<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Tarek Elzoghby | طارق الزغبي</title>
    <description>The latest articles on DEV Community by Tarek Elzoghby | طارق الزغبي (@tarek_elzoghby).</description>
    <link>https://dev.to/tarek_elzoghby</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4083487%2F0adbe41a-0092-41a4-8292-e6818065d079.png</url>
      <title>DEV Community: Tarek Elzoghby | طارق الزغبي</title>
      <link>https://dev.to/tarek_elzoghby</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tarek_elzoghby"/>
    <language>en</language>
    <item>
      <title># Tracing a URL to a Rendered Page, and the Two Stories I Almost Told at Once</title>
      <dc:creator>Tarek Elzoghby | طارق الزغبي</dc:creator>
      <pubDate>Tue, 22 Sep 2026 10:53:13 +0000</pubDate>
      <link>https://dev.to/tarek_elzoghby/-tracing-a-url-to-a-rendered-page-and-the-two-stories-i-almost-told-at-once-2n3d</link>
      <guid>https://dev.to/tarek_elzoghby/-tracing-a-url-to-a-rendered-page-and-the-two-stories-i-almost-told-at-once-2n3d</guid>
      <description>&lt;p&gt;The assignment was simple to state and harder to actually do: explain, step by step, everything that happens between typing a URL into a browser and seeing a rendered page appear on screen. Label the client and the server. Break the URL into its parts. Say what DNS does, in one sentence. Explain frontend versus backend, in one or two sentences. Then be able to say the whole thing out loud, from memory, without the page in front of me.&lt;/p&gt;

&lt;p&gt;I used a fake URL throughout so nothing here depends on a real server actually responding: &lt;a href="https://www.TeSidrah.com/projects?sort=latest" rel="noopener noreferrer"&gt;https://www.TeSidrah.com/projects?sort=latest&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Client and server&lt;/p&gt;

&lt;p&gt;Two roles show up the moment the URL is typed in and Enter is hit. The browser on my machine is the client, it sends requests. TeSidrah's remote machine is the server, it listens for requests and sends back responses.&lt;/p&gt;

&lt;p&gt;The frontend is everything that runs on the client's own machine, using the client's own resources. The backend is everything that runs on the server's machine, using the server's resources.&lt;/p&gt;

&lt;p&gt;That second sentence took longer to get right than it looks. My first attempt at defining backend split it into three separate ideas: where it runs, what its logic does, and where its database lives. It read like three facts about backend instead of one definition of what backend actually is. The database isn't what makes something backend, it's just a common piece of what backend systems tend to have. Once I dropped it and matched the shape of the frontend sentence exactly, both definitions said the same kind of thing about two different machines.&lt;/p&gt;

&lt;p&gt;Breaking down the URL&lt;/p&gt;

&lt;p&gt;The browser takes the URL apart into four pieces: the protocol (https://, the rules for communication, here adding an encryption layer), the domain (&lt;a href="http://www.TeSidrah.com" rel="noopener noreferrer"&gt;www.TeSidrah.com&lt;/a&gt;, the name of the server), the path (/projects, which resource on that server), and the query parameters (?sort=latest, extra instructions attached to the request).&lt;/p&gt;

&lt;p&gt;DNS: the lookup before the real request&lt;/p&gt;

&lt;p&gt;The browser can't send anything to a name like &lt;a href="http://www.TeSidrah.com" rel="noopener noreferrer"&gt;www.TeSidrah.com&lt;/a&gt;, it needs a numeric address. So it sends the domain to a DNS server first.&lt;/p&gt;

&lt;p&gt;This is where something shifted while I was writing it. DNS isn't a separate mechanism sitting off to the side of the real request. It's the same request/response pattern, just happening first and at a smaller scale: the browser sends something (a domain name) and gets something back (an IP address). Once that clicked, the rest of the flow stopped looking like a list of separate steps and started looking like one idea repeating at different sizes: ask, wait, get an answer back.&lt;/p&gt;

&lt;p&gt;Getting that into one sentence, the way the assignment asked, took a few tries: DNS translates a human-friendly domain name into the numeric IP address a computer actually needs to route the request. Writing a paragraph about DNS was easy. Getting it down to one correct sentence took several passes.&lt;/p&gt;

&lt;p&gt;The real request&lt;/p&gt;

&lt;p&gt;Now holding the IP address, the browser sends the actual request: the method (GET, please retrieve this for me), the path (/projects), the query string (?sort=latest), and metadata about the browser itself.&lt;/p&gt;

&lt;p&gt;The server responds&lt;/p&gt;

&lt;p&gt;This is the step where my first draft quietly told two different stories at once, without me noticing at first. I'd written the server's response as a 404, page not found, and then in the very next section described what happens if there was a page: the DOM gets built, CSS and JS and images get fetched, layout gets calculated, pixels get painted. Two different outcomes, stitched together as if they were one continuous thing.&lt;/p&gt;

&lt;p&gt;I considered a few ways to fix it. Keeping both outcomes as two branches would have split the walkthrough in half, and the whole point was to explain one continuous flow I could say out loud without qualifying it. Stopping at the 404 was tempting, it's a real and common outcome, but it would have cut the explanation short right before the most substantial part: everything the browser does once it actually has a page to work with. So I committed to the success path all the way through, and kept the 404 as a one-line aside instead of its own branch.&lt;/p&gt;

&lt;p&gt;TeSidrah's server reads the method, path, and query, runs its backend logic (queries the database, sorts by "latest," builds the page), and responds with a status code, 200 OK, and a body: the raw HTML.&lt;/p&gt;

&lt;p&gt;What the browser does with it&lt;/p&gt;

&lt;p&gt;The browser parses the HTML into the DOM, an internal tree of the page. As it parses, every reference to a stylesheet, script, or media file fires off a new request, each one its own small request/response cycle. Once everything's arrived, the browser combines the HTML with the CSS to work out layout, then paints the final pixels.&lt;/p&gt;

&lt;p&gt;One breath&lt;/p&gt;

&lt;p&gt;User types URL, browser labels client and server. URL splits into protocol, domain, path, query. DNS resolves the domain to an IP, a request/response in miniature. The browser sends the real request. The server runs its logic and replies with 200 and HTML. The browser parses, fetches what it's missing, lays out, paints. The page appears.&lt;/p&gt;

&lt;p&gt;Writing all of this down turned out to be the easier half. Being able to say the whole cycle out loud, from memory, without the page open in front of me, was a different and harder bar than being able to write it. Something about not being able to lean on the sentence I'd already typed made it obvious which parts I actually understood and which parts I'd just successfully described once.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>networking</category>
      <category>beginners</category>
      <category>learning</category>
    </item>
    <item>
      <title># The Breakpoint That Was Hiding the Real Bug</title>
      <dc:creator>Tarek Elzoghby | طارق الزغبي</dc:creator>
      <pubDate>Tue, 15 Sep 2026 11:30:35 +0000</pubDate>
      <link>https://dev.to/tarek_elzoghby/-the-breakpoint-that-was-hiding-the-real-bug-8j0</link>
      <guid>https://dev.to/tarek_elzoghby/-the-breakpoint-that-was-hiding-the-real-bug-8j0</guid>
      <description>&lt;p&gt;This was my first Frontend Mentor junior challenge, and the first time I used CSS Grid for anything real. I'd already built the mobile layout with Flexbox, a single stacked column, before I sat down to work through the desktop layout. That's where the actual learning happened.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two grids instead of one
&lt;/h2&gt;

&lt;p&gt;My first instinct was to wrap four of the five cards in a &lt;code&gt;.group&lt;/code&gt; div, turn that into its own 3-column grid, and place the fifth card (Kira) in a separate outer grid next to it.&lt;/p&gt;

&lt;p&gt;It didn't work. When I tried to explain my reasoning, I understood why: Kira needed to visually span two rows next to the other four cards, but she wasn't even inside the same grid as them. I didn't know, until this project, that a single grid item can span multiple rows in one column. Once I understood that was possible, the whole &lt;code&gt;.group&lt;/code&gt; wrapper stopped making sense. It wasn't solving a real problem. It was a workaround for something Grid could already do on its own.&lt;/p&gt;

&lt;p&gt;The fix was to delete &lt;code&gt;.group&lt;/code&gt; entirely and flatten the HTML so all five &lt;code&gt;&amp;lt;article&amp;gt;&lt;/code&gt; elements are direct children of one grid container.&lt;/p&gt;

&lt;h2&gt;
  
  
  grid-template-areas
&lt;/h2&gt;

&lt;p&gt;Instead of placing every card with column and row numbers, I used &lt;code&gt;grid-template-areas&lt;/code&gt;, writing the layout as a literal map:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;grid-template-areas&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="s1"&gt;"daniel  daniel   jonathan kira"&lt;/span&gt;
    &lt;span class="s1"&gt;"jeanette patrick patrick  kira"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This clicked immediately. The CSS looks like the layout. Each article gets one line, &lt;code&gt;grid-area: daniel;&lt;/code&gt;, and Grid figures out the rest.&lt;/p&gt;

&lt;p&gt;I'd guessed correctly that Kira would stretch to fill both rows, but I wasn't sure the two rows would end up equal in height. They don't, by default. With &lt;code&gt;grid-template-rows: auto auto&lt;/code&gt;, each row sizes independently based on its own content, they aren't forced to match. What does happen automatically: every grid item has &lt;code&gt;align-self: stretch&lt;/code&gt; by default, so an item spanning multiple rows fills the combined height of every row and gap it spans, no manual syncing required.&lt;/p&gt;

&lt;h2&gt;
  
  
  Chasing a stretch bug into a fake fix
&lt;/h2&gt;

&lt;p&gt;Once the grid was structurally working, the cards looked stretched and awkward, especially Jonathan's and Jeanette's, which had noticeable empty space below their text.&lt;/p&gt;

&lt;p&gt;The cause: &lt;code&gt;align-items: stretch&lt;/code&gt; was correctly making shorter cards match the height of taller ones in the same row. That part was expected Grid behavior. But &lt;code&gt;justify-content: center&lt;/code&gt; on my &lt;code&gt;article&lt;/code&gt; rule was then centering the shorter content vertically inside that taller box, creating dead space above and below instead of a clean top-aligned look. The fix was &lt;code&gt;justify-content: flex-start&lt;/code&gt; on &lt;code&gt;article&lt;/code&gt;, so content anchors to the top regardless of how tall the box gets.&lt;/p&gt;

&lt;p&gt;Before I found that, I tried something else first: I set &lt;code&gt;min-width&lt;/code&gt; on &lt;code&gt;.card&lt;/code&gt;, which happened to hide the problem by force-widening the container so text wrapped less. It looked fixed. It wasn't. &lt;code&gt;min-width&lt;/code&gt; sets a floor, and below that value the grid became wider than the viewport, causing horizontal scrollbars.&lt;/p&gt;

&lt;p&gt;Switching to &lt;code&gt;max-width&lt;/code&gt;, the actual correct property, let the grid shrink properly again, which immediately re-exposed the real stretch problem. At 768px, four columns didn't have enough room for comfortable text, so everything wrapped heavily and rows got tall and uneven.&lt;/p&gt;

&lt;p&gt;That's when I found the real root cause myself: the grid layout simply didn't have enough room to exist at 768px. The style guide specified mobile at 375px and desktop at 1440px, nothing about a tablet-specific layout, so I moved my breakpoint up to &lt;code&gt;80rem&lt;/code&gt; (1280px), well clear of the cramped zone, and kept Flexbox stacking everywhere below that. I also retuned the typography specifically for the desktop grid, since a layout at 1280px and up has different space needs than the same content at 375px.&lt;/p&gt;

&lt;p&gt;After that, I resized the browser slowly across every width to check it. No scrollbars, no stretch issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  Positioning the quotation mark
&lt;/h2&gt;

&lt;p&gt;I'd hidden the decorative quotation mark image with &lt;code&gt;display: none&lt;/code&gt; early on and forgot about it until it got pointed out near the end. Placing it behind Daniel's card content taught me three things I hadn't really understood before:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;position: relative&lt;/code&gt; on the parent turns it into the anchor that absolutely positioned children measure their offsets against. &lt;code&gt;position: absolute&lt;/code&gt; on the quotation image takes it out of normal document flow so it can overlap content without pushing anything around. And &lt;code&gt;z-index&lt;/code&gt; only works on positioned elements, an element left at the default &lt;code&gt;position: static&lt;/code&gt; ignores it completely, no matter the value.&lt;/p&gt;

&lt;p&gt;What surprised me more: positioned elements always paint above static ones regardless of &lt;code&gt;z-index&lt;/code&gt;, that comparison only happens between positioned elements. So my fix, adding &lt;code&gt;position: relative&lt;/code&gt; and &lt;code&gt;z-index: 1&lt;/code&gt; to the text containers, was really about moving them into the positioned category so &lt;code&gt;z-index&lt;/code&gt; could apply at all. The number itself mattered less than getting into the right category to begin with. DOM order alone might have already put the text on top in my case, so the fix mostly added intentional control rather than correcting an active failure.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I almost missed
&lt;/h2&gt;

&lt;p&gt;Working through structural problems like Grid and positioning, it's easy to lose track of visual details entirely. I nearly missed the quotation mark graphic, the box-shadow on the cards, and a leftover wrapper div around Jonathan's avatar from earlier experimentation. I'm trying to build a habit around this now: a quick detail scan of the design before styling each section, shadows, radius, spacing, hover states, as its own pass separate from the structural work.&lt;/p&gt;

&lt;p&gt;For the box-shadow itself, I landed on:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;box-shadow&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="err"&gt;5&lt;/span&gt;&lt;span class="nt"&gt;rem&lt;/span&gt; &lt;span class="err"&gt;3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="err"&gt;5&lt;/span&gt;&lt;span class="nt"&gt;rem&lt;/span&gt; &lt;span class="err"&gt;5&lt;/span&gt;&lt;span class="nt"&gt;rem&lt;/span&gt; &lt;span class="nt"&gt;-2rem&lt;/span&gt; &lt;span class="nt"&gt;hsl&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;224&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="err"&gt;10&lt;/span&gt;&lt;span class="o"&gt;%,&lt;/span&gt; &lt;span class="err"&gt;45&lt;/span&gt;&lt;span class="o"&gt;%);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Offset-x and offset-y shift the shadow horizontally and vertically. Blur-radius controls how soft the edge is, zero being a hard silhouette. Spread-radius grows or shrinks the shadow shape before blur, and a negative value here pulls it inward instead of framing the element evenly. Small thing, but tuning it by feel taught me more about how the property actually works than reading the syntax did.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I take away from this
&lt;/h2&gt;

&lt;p&gt;The real lesson wasn't Grid syntax. It was learning to trace a visual symptom back to its actual structural cause instead of patching it with another override. The &lt;code&gt;min-width&lt;/code&gt; detour looked like a fix. It wasn't one, it just moved the problem somewhere I couldn't see it yet. A working fix and a correct fix aren't the same thing if the first one is just hiding what's actually wrong underneath.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Working title:&lt;/strong&gt; The Breakpoint That Was Hiding the Real Bug&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Platform description/summary:&lt;/strong&gt; Building my first CSS Grid layout, and chasing a stretch bug through a fake fix before finding the actual root cause: the wrong breakpoint entirely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Suggested tags, Hashnode:&lt;/strong&gt; css, grid, frontend, debugging, beginners&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Suggested tags, dev.to (max 4):&lt;/strong&gt; css, grid, debugging, beginners&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cover image:&lt;/strong&gt; Worth having one. Something simple showing the grid-template-areas map itself (the literal ASCII-art layout string) would work well as a visual, since that's the moment the post pivots from confusion to clarity. Doesn't need to be more elaborate than that.&lt;/p&gt;

</description>
      <category>css</category>
      <category>grid</category>
      <category>debugging</category>
      <category>beginners</category>
    </item>
    <item>
      <title># Building for the Person Who Can't Debug It</title>
      <dc:creator>Tarek Elzoghby | طارق الزغبي</dc:creator>
      <pubDate>Tue, 08 Sep 2026 13:47:25 +0000</pubDate>
      <link>https://dev.to/tarek_elzoghby/-building-for-the-person-who-cant-debug-it-3hgd</link>
      <guid>https://dev.to/tarek_elzoghby/-building-for-the-person-who-cant-debug-it-3hgd</guid>
      <description>&lt;p&gt;The first post about this workflow covered the Webhook payload break — the moment the Form Trigger got swapped out and every downstream reference silently pointed at data that no longer existed. That part's done. This is the rest of the build: three smaller decisions that don't share a bug, but do share a reason.&lt;/p&gt;

&lt;p&gt;Marisol — the client this workflow was built for — isn't technical. She has no developer on call. If this breaks while she's using it, she's the one looking at it first. That fact didn't come up once and get filed away; it kept showing up, in places that had nothing to do with each other.&lt;/p&gt;

&lt;h2&gt;
  
  
  Flipping the IF Node
&lt;/h2&gt;

&lt;p&gt;The junk filter was supposed to do one thing: stop a submission if both phone and email were empty, since a lead with neither is useless. The logic in my head was straightforward — "if phone empty AND email empty, stop."&lt;/p&gt;

&lt;p&gt;But when I actually built the IF node, I flipped it: "if phone OR email is not empty, continue." Same condition, inverted. What changed was which branch reads as the main path. Written this way, the true branch is the branch I actually follow when I trace the workflow — the valid data moving forward. The false branch is just "nothing happens," which is exactly what it should look like: the door that quietly doesn't open.&lt;/p&gt;

&lt;p&gt;It's a small thing. The workflow does the same job either way. But if Marisol — or anyone else — ever opens this node to understand why a lead didn't show up in Sheets, the condition should read like the thing that's actually happening, not like a double negative she has to mentally invert first.&lt;/p&gt;

&lt;p&gt;Tested both directions: a submission with both phone and email empty correctly stopped, and a submission with just one filled correctly passed through.&lt;/p&gt;

&lt;h2&gt;
  
  
  Redesigning the Set Node
&lt;/h2&gt;

&lt;p&gt;Originally, the Set node had exactly one job — default the message field to "No message provided" if it came in empty. That was true right up until the trigger swap.&lt;/p&gt;

&lt;p&gt;Once the Webhook replaced the Form Trigger, the payload picked up a lot of extra weight — headers, params, webhookUrl, executionMode, all the raw HTTP metadata that comes bundled with a webhook request. None of it was useful to Sheets or Slack. All of it was now sitting in the same object as the five fields that actually mattered.&lt;/p&gt;

&lt;p&gt;I could have left it. The extra keys wouldn't have broken anything downstream — Sheets and Slack only read what I told them to read. But that's exactly the kind of thing that's invisible until someone else has to look at it. So I rebuilt the Set node to do more: explicit mappings for all five clean fields — Name, Phone, Email, Service address, Message — stripping the noise and defaulting the message in the same place.&lt;/p&gt;

&lt;p&gt;I chose explicit field mappings over n8n's "keep only set fields" toggle on purpose. The toggle does the same job with less typing, but it hides what's happening — you'd have to open the node and go find the setting to know why the extra fields disappeared. Written out explicitly, the five fields the workflow actually cares about are right there, visible, nothing to go dig for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing the Doc for Marisol
&lt;/h2&gt;

&lt;p&gt;The last piece was the plain-English explanation — the thing Marisol actually reads if something goes wrong. First time writing a doc like this, so I built it around what she'd need, not around what I found interesting to explain: what the workflow does, a step-by-step walkthrough using the real node names (Filter Out Junk Leads, Clean and Format Lead Data — the names she'd see if she ever opened it herself), and a troubleshooting section.&lt;/p&gt;

&lt;p&gt;For troubleshooting, I didn't invent hypothetical failure cases — I used one that had actually happened. During testing, the Google Sheets node failed once because the credential needed reconnecting; disconnecting and reconnecting it fixed it. Small thing, but real, and more likely to recur than something exotic I could've made up instead.&lt;/p&gt;

&lt;p&gt;The doc and the exported workflow JSON stayed as two separate files, deliberately. The JSON is the actual automation, importable straight into her n8n. The doc is a reference for a human. They don't get merged into each other — that split is what the brief asked for, and forcing them together would've made one file try to do two jobs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Same Constraint, Three Places
&lt;/h2&gt;

&lt;p&gt;None of these three decisions are related on the surface — an inverted condition, a redesigned node, a document. What ties them together is that each one got shaped by a person who wasn't going to be in the room the next time this workflow needed explaining, and who can't reason through n8n's internals if it breaks. The workflow doesn't get simpler because of that. It gets more legible.&lt;/p&gt;

</description>
      <category>automation</category>
      <category>webdev</category>
      <category>beginners</category>
      <category>nocode</category>
    </item>
    <item>
      <title>The Trigger Swap That Broke Every Downstream Reference</title>
      <dc:creator>Tarek Elzoghby | طارق الزغبي</dc:creator>
      <pubDate>Tue, 01 Sep 2026 11:30:24 +0000</pubDate>
      <link>https://dev.to/tarek_elzoghby/the-trigger-swap-that-broke-every-downstream-reference-cle</link>
      <guid>https://dev.to/tarek_elzoghby/the-trigger-swap-that-broke-every-downstream-reference-cle</guid>
      <description>&lt;p&gt;I built this workflow — a lead intake automation for a made-up client, Ferrer Window Co. — with a Form Trigger first, even though I knew from the start it needed to end up on a Webhook. That wasn't the plan drifting. It was deliberate: I'd used webhooks before and Form Trigger less, so building with Form Trigger first let me see real data coming through without also holding webhook payload structure in my head at the same time. Get the logic right, then swap the trigger once everything else was solid.&lt;/p&gt;

&lt;p&gt;And the logic did get solid. By the time I was ready to swap, I had a full chain working: an IF node filtering out junk leads (both phone and email empty), a Set node defaulting empty message fields to "No message provided," and Sheets and Slack both branching off that same cleaned data independently. Tested both edge cases — junk lead correctly stopped, empty message correctly defaulted. Everything passed.&lt;/p&gt;

&lt;p&gt;Then I deleted the Form Trigger, added a Webhook node, and sent a test payload through Postman.&lt;/p&gt;

&lt;p&gt;It broke immediately.&lt;/p&gt;

&lt;p&gt;Not the logic — the logic was fine. What broke was every single reference to the data. With Form Trigger, the payload had come through flat:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Tarek"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Phone"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"02-011-41104-582"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tarekalaaelzoghby1@gmail.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Service address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Primary school st"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"I want to request an order"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"submittedAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-08-24T03:24:50.089-04:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"formMode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With Webhook, everything I actually wanted was nested one level down, sitting alongside a pile of raw HTTP metadata I didn't need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"headers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Tarek"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Phone"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"02-011-41104-582"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"webhookUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"executionMode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every reference I'd written — the IF node's condition, the Set node's field mappings — pointed at &lt;code&gt;$json.Name&lt;/code&gt;, &lt;code&gt;$json.Phone&lt;/code&gt;, and so on. None of it was wrong, exactly. It was just pointed at a shape of data that no longer existed. The whole workflow's reasoning was intact and now unreachable.&lt;/p&gt;

&lt;p&gt;So I went back through it node by node — the IF node's conditions, the Set node's mappings — and repointed every one from &lt;code&gt;$json.X&lt;/code&gt; to &lt;code&gt;$json.body.X&lt;/code&gt;. Nothing about &lt;em&gt;what&lt;/em&gt; the workflow decided changed. Only &lt;em&gt;where it looked&lt;/em&gt; for the thing it was deciding about.&lt;/p&gt;

&lt;p&gt;Once that was done, I didn't just assume it worked because the references compiled. I re-ran both edge cases through the webhook path — both phone and email empty, and an empty message field — to confirm the filter still stopped junk leads and the default still kicked in, now that the data was arriving through a different shape entirely.&lt;/p&gt;

&lt;p&gt;The part I'd flag for anyone doing this same swap later: it's not a dramatic failure. Nothing throws a loud error that points you at the fix. The workflow just quietly stops seeing the data it expects, because the trigger node changed the shape of what "the data" even means. If you built your logic against one trigger's payload structure, swapping the trigger isn't a drop-in replacement — it's a second pass through everything downstream, checking what each node thinks it's looking at.&lt;/p&gt;

</description>
      <category>debugging</category>
      <category>automation</category>
      <category>json</category>
      <category>webhook</category>
    </item>
    <item>
      <title>Two Things I Got Wrong Building a Blog Preview Card</title>
      <dc:creator>Tarek Elzoghby | طارق الزغبي</dc:creator>
      <pubDate>Tue, 25 Aug 2026 13:58:57 +0000</pubDate>
      <link>https://dev.to/tarek_elzoghby/two-things-i-got-wrong-building-a-blog-preview-card-367k</link>
      <guid>https://dev.to/tarek_elzoghby/two-things-i-got-wrong-building-a-blog-preview-card-367k</guid>
      <description>&lt;p&gt;This one's a small build — a blog preview card, a Frontend Mentor challenge. Nothing about it is complicated on the surface: an image, a tag, a date, a title, a description, an author. But two small pieces of it didn't work the way I expected, and both times the reason why turned out to matter more than the fix itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  The span that wouldn't stay small
&lt;/h2&gt;

&lt;p&gt;The card has a tag on it — "Learning" — styled to look like a little yellow pill sitting above the title. I reached for a &lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt; for it, because a span is inline by default. Inline elements only take up as much width as their content needs. That's the whole reason to use one here: I wanted a tag-shaped box, not a bar running across the card.&lt;/p&gt;

&lt;p&gt;Except when I built it, it didn't behave like that. The tag stretched the full width of the card.&lt;/p&gt;

&lt;p&gt;The card is a flex container with &lt;code&gt;flex-direction: column&lt;/code&gt;. I knew that. What I hadn't connected was that flex changes the rules for its children — inside a flex container, children stretch to fill the cross axis by default, regardless of what they'd normally do outside of one. &lt;code&gt;display: inline&lt;/code&gt; doesn't mean anything once an element becomes a flex item. The span wasn't behaving like a span anymore; it was behaving like a flex child, and flex children stretch unless told otherwise.&lt;/p&gt;

&lt;p&gt;The fix is one line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.card__tag&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;align-self&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex-start&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;align-self&lt;/code&gt; overrides the container's default alignment for just that one element — shrink to fit your content, ignore what everyone else is doing. Once I added it, the tag went back to looking like a tag.&lt;/p&gt;

&lt;p&gt;The part worth remembering isn't the fix. It's that "I know what this element normally does" stopped being true the moment I put it inside a flex container. Context changes behavior, even for something as basic as inline vs block.&lt;/p&gt;

&lt;h2&gt;
  
  
  The heading that couldn't take a hover
&lt;/h2&gt;

&lt;p&gt;The title needed a hover effect — the text should change color when you mouse over it, since it's a link to the full article. I built it the straightforward way first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;h1&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card__title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;HTML &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; CSS foundations&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; because it's the piece's main heading. That part wasn't in question. But once I tried to add the hover behavior to it, something felt off, and it took some reasoning to figure out why.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; elements aren't interactive. They don't receive keyboard focus. A mouse user would see the color change on hover just fine — but a keyboard user tabbing through the page would get nothing, no visual signal that this thing does anything at all. A screen reader wouldn't announce it as a link either, because it isn't one. It's heading text that happens to look clickable to someone using a mouse, and that's it. Anyone not using a mouse would have no way to know.&lt;/p&gt;

&lt;p&gt;If something responds to hover, it should be something that can actually be interacted with — which meant an &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; tag, not styling applied to a heading. But I didn't want to just wrap the whole &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; in a link and call it done, because that changes where the hover boundary sits: hover the empty space around the text, not just the text, and it still triggers. I wanted the interactive zone to match the text itself.&lt;/p&gt;

&lt;p&gt;So the anchor goes inside the heading, not around it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;h1&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card__title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;HTML &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; CSS foundations&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.card__title&lt;/span&gt; &lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;inherit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;text-decoration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.card__title&lt;/span&gt; &lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--color-bg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;color: inherit&lt;/code&gt; on the anchor matters here too — browsers apply their own default link styling (blue, underlined), which would override whatever color the heading was supposed to have. Setting it back to &lt;code&gt;inherit&lt;/code&gt; cancels that out, so the link reads as the same color as the rest of the heading until you hover it.&lt;/p&gt;

&lt;p&gt;What changed my approach wasn't "add an anchor somewhere." It was realizing the hover target and the semantic structure needed to line up exactly — the thing that looks clickable, is clickable, exactly where it looks clickable, and nowhere else.&lt;/p&gt;

&lt;h2&gt;
  
  
  What both of these actually have in common
&lt;/h2&gt;

&lt;p&gt;Neither one was a hard bug. Nothing crashed, nothing broke visibly in a way that demanded urgent fixing. Both were cases where something looked fine and worked &lt;em&gt;almost&lt;/em&gt; right, and the "almost" was doing a lot of quiet work — an element not shrinking the way it should, an interaction that only half the people looking at the page would ever know existed. Small card, small build, and still two places where the obvious approach wasn't the correct one until I asked why it wasn't behaving the way I expected.&lt;/p&gt;

</description>
      <category>html</category>
      <category>css</category>
      <category>beginners</category>
      <category>a11y</category>
    </item>
    <item>
      <title>The Card That Leaned</title>
      <dc:creator>Tarek Elzoghby | طارق الزغبي</dc:creator>
      <pubDate>Tue, 18 Aug 2026 15:06:59 +0000</pubDate>
      <link>https://dev.to/tarek_elzoghby/the-card-that-leaned-169g</link>
      <guid>https://dev.to/tarek_elzoghby/the-card-that-leaned-169g</guid>
      <description>&lt;p&gt;This was my first project — a small QR code card, built from a Frontend Mentor challenge. Nothing complicated: a white card centered on a colored background, with an image and two lines of text stacked inside it. I thought I was done.&lt;/p&gt;

&lt;p&gt;Then I resized the browser to a smaller width, and the card started leaning to one side.&lt;/p&gt;

&lt;p&gt;That was confusing on its own, because nothing in my CSS should have cared about screen width in a way that would push the card sideways. I hadn't written anything asymmetric. So the first question wasn't "how do I fix this" — it was "what is even causing this."&lt;/p&gt;

&lt;p&gt;I reached for the debugging trick I'd read about: outline everything.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;outline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="no"&gt;red&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This draws a visible border around every element on the page, whether or not it normally has one. The idea is simple — if you can't tell where an element's boundaries are, make the boundaries visible.&lt;/p&gt;

&lt;p&gt;What I saw didn't make sense at first. The background color was filling the entire screen, the way it should. But the outlined box sitting on top of it was small — and it wasn't even centered. It had extra space on one side and almost none on the other.&lt;/p&gt;

&lt;p&gt;That was the part that stopped me. How can the background fill the whole screen, but the &lt;em&gt;body&lt;/em&gt; only take up a small, off-center chunk of it? The background color was set on &lt;code&gt;body&lt;/code&gt;. If the body were really that small, the color shouldn't be filling the screen at all.&lt;/p&gt;

&lt;p&gt;So my assumption was wrong. I was looking at the wrong element.&lt;/p&gt;

&lt;p&gt;It took a second to place it, because the actual culprit had no visual presence anywhere in my styles — I hadn't given it a color, a border, anything. It was &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt;. I'd added it early on for semantic structure and then more or less forgotten it existed, because it never &lt;em&gt;looked&lt;/em&gt; like anything. But it was still there in the HTML, still taking up space in the layout, and the browser's default styling for it was enough to throw the whole thing off-center.&lt;/p&gt;

&lt;p&gt;That's the part I hadn't internalized yet: every element in HTML takes up layout space, even the ones you can't see. Invisibility isn't the same as absence.&lt;/p&gt;

&lt;p&gt;The fix was one line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;main&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;display: contents&lt;/code&gt; removes an element from the layout entirely — visually, it's as if &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt;'s children became direct children of &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt;. But the element itself stays in the HTML, which matters, because screen readers and search engines still read &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt; as a landmark for page structure. So I got to keep the semantic meaning without it interfering with the layout.&lt;/p&gt;

&lt;p&gt;The card centered correctly after that, at every width.&lt;/p&gt;

&lt;p&gt;What I actually took from this wasn't the fix itself — &lt;code&gt;display: contents&lt;/code&gt; is a one-line answer I could have just been told. It was the order of operations that got me there: when something about spacing or alignment looks wrong and the CSS doesn't explain it, the problem is often not in the CSS at all. It's in an element you forgot was in the HTML.&lt;/p&gt;

</description>
      <category>css</category>
      <category>debugging</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
