<?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: RapidBizz Team</title>
    <description>The latest articles on DEV Community by RapidBizz Team (@rapidbizz).</description>
    <link>https://dev.to/rapidbizz</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%2F4178927%2F76a2d4b6-bc1c-4617-9303-dfea195b9318.png</url>
      <title>DEV Community: RapidBizz Team</title>
      <link>https://dev.to/rapidbizz</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rapidbizz"/>
    <language>en</language>
    <item>
      <title>We Mapped Every Keyword to One Page on a 61-Page Site — Then Fixed 20 Gaps in a Day</title>
      <dc:creator>RapidBizz Team</dc:creator>
      <pubDate>Wed, 14 Oct 2026 13:40:07 +0000</pubDate>
      <link>https://dev.to/rapidbizzteam/we-mapped-every-keyword-to-one-page-on-a-61-page-site-then-fixed-20-gaps-in-a-day-1l43</link>
      <guid>https://dev.to/rapidbizzteam/we-mapped-every-keyword-to-one-page-on-a-61-page-site-then-fixed-20-gaps-in-a-day-1l43</guid>
      <description>&lt;p&gt;Most sites don't have a keyword problem. They have an ownership problem: five pages vaguely "about" the same topic, none of them clearly owning the search term, and Google left to guess which one to rank.&lt;/p&gt;

&lt;p&gt;We run a hand-built static HTML site (61 pages, no CMS, no framework). This week we did the unglamorous work properly:&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Build the map before touching a page
&lt;/h2&gt;

&lt;p&gt;We collected 253 candidate keywords from real sources — published search volumes where they exist, competitor pages, and the questions customers actually ask — and grouped them into 24 clusters. Then every one of the 61 URLs got exactly &lt;strong&gt;one primary keyword&lt;/strong&gt; and a set of secondaries.&lt;/p&gt;

&lt;p&gt;Nine overlaps (cannibalization flags) showed up immediately. Example: "ecommerce SEO" was targeted by both our marketing page and our ecommerce solution page. We assigned it to one owner — the service page — and kept the other page on development intent only.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Measure phrases the way search engines read them
&lt;/h2&gt;

&lt;p&gt;Exact-string matching lies. Pages say "e-commerce" with a hyphen, or put a stop word inside the phrase ("website design packages &lt;strong&gt;in the&lt;/strong&gt; UAE"). We counted coverage with stop-word-tolerant, punctuation-insensitive matching across visible body copy only.&lt;/p&gt;

&lt;p&gt;Result across 45 mapped pages: 8 strong, 17 okay, &lt;strong&gt;20 with a real gap&lt;/strong&gt; — the page's primary phrase appeared zero times in its body copy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Fix with sentences, not stuffing
&lt;/h2&gt;

&lt;p&gt;Each gap got &lt;strong&gt;one natural sentence&lt;/strong&gt; in the hero or intro copy carrying the primary phrase. No titles changed. No H1s changed. No keyword repeated five times in a footer.&lt;/p&gt;

&lt;p&gt;Twenty gaps, sixteen fixed and verified live the same day. The other four were already owned by bigger builds in our queue (a new service page, a platform section, an audit section).&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Strengthen the pages that are already indexed
&lt;/h2&gt;

&lt;p&gt;Internal links are the cheapest authority you own. We audited the contextual link mesh between our 16 verified-indexed pages and found our logo-design landing page had &lt;strong&gt;zero&lt;/strong&gt; contextual inbound links. Two honest, relevant links later (a case study and a cost guide), it has a fighting chance.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rule we ended with
&lt;/h2&gt;

&lt;p&gt;One page owns one term. Every fix is verified on the live page, byte for byte, before it counts as done. And nothing gets reported until it can be clicked.&lt;/p&gt;

&lt;p&gt;If you want to see the site this was done on: &lt;a href="https://rapidbizz.com/services/websites/" rel="noopener noreferrer"&gt;RapidBizz&lt;/a&gt; — and our write-up of the AI-search side of the same system: &lt;a href="https://rapidbizz.com/blog/geo-ai-search-guide-uae/" rel="noopener noreferrer"&gt;GEO &amp;amp; AI search guide&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>seo</category>
      <category>webdev</category>
      <category>marketing</category>
    </item>
    <item>
      <title>How we took CLS to 0.000 on a hand-built static site (no framework, no CDN)</title>
      <dc:creator>RapidBizz Team</dc:creator>
      <pubDate>Wed, 14 Oct 2026 07:21:50 +0000</pubDate>
      <link>https://dev.to/rapidbizzteam/how-we-took-cls-to-0000-on-a-hand-built-static-site-no-framework-no-cdn-45f3</link>
      <guid>https://dev.to/rapidbizzteam/how-we-took-cls-to-0000-on-a-hand-built-static-site-no-framework-no-cdn-45f3</guid>
      <description>&lt;p&gt;Our production site is deliberately boring technology: hand-written HTML, one compiled CSS file per page, no framework, no build step, no CDN, shared hosting. Boring, but not slow — an earlier pass that removed the Tailwind Play CDN and self-hosted our fonts took the homepage from 57 to 94 in the official mobile PageSpeed batch. What it didn't fix was layout shift. Mobile CLS medians across our 11 key pages sat between 0.028 and 0.061, with worst runs striking 0.351; on desktop, the homepage median was 0.485. Here's where the shift actually lived (not images), the two CSS fixes that killed it, and the three "obvious" fixes we measured, rejected, and reverted.&lt;/p&gt;

&lt;h2&gt;
  
  
  The shift wasn't images — it was icons
&lt;/h2&gt;

&lt;p&gt;We diagnosed with Lighthouse's layout-shift attribution plus rect-timeline probes that sample element geometry over time. Every page told the same story: the header and topbar moved after first paint, at the moment the icon systems landed.&lt;/p&gt;

&lt;p&gt;The site uses Lucide icons. In the HTML they're placeholder elements; a script later swaps each one for a real SVG. An inline placeholder has no intrinsic size, so it collapses to roughly zero until the swap. Measured effects: the mobile menu button grew 16×16 → 48×48 px (header +16 px), topbar text clusters widened ~43 px, the theme-toggle button grew 8×8 → 24×24 px. The Font Awesome brand icons in the social cluster had the mirror-image problem — no width until the FA webfont arrived, so the cluster widened 72 → 100 px mid-load. None of this shows up if you only audit images and ad slots, which is where most CLS advice (&lt;a href="https://web.dev/articles/cls" rel="noopener noreferrer"&gt;https://web.dev/articles/cls&lt;/a&gt;) quite reasonably points first.&lt;/p&gt;

&lt;h2&gt;
  
  
  The second, subtler shift
&lt;/h2&gt;

&lt;p&gt;Sizing the placeholders removed most of it, but a residual survived — and it took the rect probes to see why. The placeholder sits on the text baseline, inheriting font-descent space below it. The swapped-in SVG computes vertical-align: middle instead. So the theme button rendered 28 px tall before the swap and 24 px after, and main jumped −4 px.&lt;/p&gt;

&lt;p&gt;Four pixels sounds like nothing. But CLS weights the area that moves: on pages with a near-full-viewport decorative layer, that 4 px jump of a huge visible region scored ~0.19–0.21 CLS by itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix: reserve the icon's exact final box
&lt;/h2&gt;

&lt;p&gt;Everything shipped as one appended style block. The core of it:&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;i&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-lucide&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.hidden&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.md&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nd"&gt;:hidden&lt;/span&gt;&lt;span class="o"&gt;)&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;inline-block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nl"&gt;vertical-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;middle&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;i&lt;/span&gt;&lt;span class="nc"&gt;.fab.fa-linkedin-in&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;i&lt;/span&gt;&lt;span class="nc"&gt;.fab.fa-instagram&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;i&lt;/span&gt;&lt;span class="nc"&gt;.fab.fa-facebook-f&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;inline-block&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;i&lt;/span&gt;&lt;span class="nc"&gt;.fab.fa-linkedin-in&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;i&lt;/span&gt;&lt;span class="nc"&gt;.fab.fa-instagram&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;.875em&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;i&lt;/span&gt;&lt;span class="nc"&gt;.fab.fa-facebook-f&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;.625em&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three things matter here. display:inline-block lets the placeholder take the width/height from its own size utility classes, so it reserves exactly the box the SVG will occupy. vertical-align:middle matches the SVG's computed alignment, killing the baseline-descent effect above. And the :not() guards are load-bearing: the stylesheet must never override the hidden state of the 111 sun/moon theme-toggle icons and responsive arrows whose visibility is conditional. The FA widths are the fonts' exact glyph advances, so the settled rendering is pixel-identical to before — this is a geometry reservation, not a design change. On the contact page, mobile CLS went from 0.061 in 3 of 3 baseline runs to 0.000.&lt;/p&gt;

&lt;h2&gt;
  
  
  The decorative layer that moved with the text
&lt;/h2&gt;

&lt;p&gt;The second fix was a different mechanism entirely. Our hero glow was centred against its section:&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;.hero-glow&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;50%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;translate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-50%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;-50%&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
&lt;span class="nc"&gt;.hero-glow&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;56px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;translate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-50%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;639px&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;&lt;span class="nc"&gt;.hero-glow&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;64px&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the webfont landed and the hero text re-wrapped, the section's height changed — and a top:50% element physically moved with it. We measured the settled glow position on each of the 19 affected pages (fonts loaded, animations frozen, desktop and mobile) and replaced the centring with that fixed anchor. The glow can now never move when the section does. Because this touched something visual, it was parity-gated: settled-state captures before and after showed computed styles and rects of 15 key elements per page identical (except the glow's own values, and 0.5 px rounding on some captures), with zero strong pixel diffs on 45 of 48 captures. The 3 exceptions were our JS typed headline caught at different typing phases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;Mobile CLS after the pass: 0.000 — median and worst run — on all 11 pages. Desktop medians fell everywhere: homepage 0.485 → 0.002, websites 0.189 → 0.038, the long-form blog page 0.336 → 0.128. In the official PSI batch we ran after the pass, CLS read 0 or near-0 on 8 of 9 runs, and desktop LCP was ≤ 1.0 s on every desktop run.&lt;/p&gt;

&lt;h2&gt;
  
  
  LCP had no lever to pull
&lt;/h2&gt;

&lt;p&gt;The LCP breakdown (&lt;a href="https://web.dev/articles/lcp" rel="noopener noreferrer"&gt;https://web.dev/articles/lcp&lt;/a&gt;) on all 11 pages contained exactly two subparts: time to first byte, and element render delay. Resource load delay and duration were absent in every run — because the LCP element is hero text on every page (on one page, a decorative code-text layer). There is no image-LCP page in the set at all, so the standard playbook — preload the hero image, add fetchpriority — had nothing to attach to. What's left is TTFB (an origin property; our local rig reads 0.77–1.27 s through its proxy path versus 0.2–0.4 s of true origin response measured separately) and the intrinsic cost of painting text once its font lands. In the lab, desktop LCP was ≤ 1.21 s on all 11 pages both before and after — the composition simply isn't page-side fixable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three fixes we measured and reverted
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;fetchpriority="high" on the font preloads. Home desktop CLS over 5 runs: median 0.466 with the change, 0.468 without. Zero effect on the font-race re-wrap. Reverted.&lt;/li&gt;
&lt;li&gt;CSS containment on the decorative code layer (contain: layout paint plus will-change). Render delay moved inside run-to-run noise. Reverted.&lt;/li&gt;
&lt;li&gt;Preloading that layer's mono font. No improvement beyond noise. Reverted.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(A critical-CSS pattern from an earlier phase had already failed its measurement gate and wasn't retried.)&lt;/p&gt;

&lt;p&gt;The discipline behind those reverts: on a noisy shared origin, single-run deltas are fiction. We take medians of 3 runs per page per stage, 5-run checks on pages prone to strikes, and we distrust any batch whose conditions changed mid-flight — our final batch ran under visibly heavier machine contention, and its LCP deltas tracked the contention, not the code. Field data would arbitrate better, but CrUX still reports "No Data" for this origin, so lab medians plus official PSI batches are the evidence we have.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's still broken, honestly
&lt;/h2&gt;

&lt;p&gt;Two font-metric re-wraps remain, both attributed by Lighthouse to "Web font loaded": the homepage's desktop H1 (0.466 when it strikes; median 0.002 in the final batch) and one blog intro paragraph (0.128). The real cures — fixed text-block heights, different typefaces, or font-display behaviour that never swaps a late font in — are all design changes. So they're written down as decisions for the site's owner, not silent fixes. We shipped what was measurable and provably design-neutral, and documented the rest.&lt;/p&gt;

&lt;p&gt;We build sites like this at RapidBizz (&lt;a href="https://rapidbizz.com" rel="noopener noreferrer"&gt;https://rapidbizz.com&lt;/a&gt;) — hand-built, measured in batches, and reverted when the numbers say so.&lt;/p&gt;

</description>
      <category>webperf</category>
      <category>css</category>
      <category>javascript</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
