Skip to content

Engineering

Common Document Semantics mistakes that still ship

Shipping with div-only layouts, heading soup, and "Lighthouse was green once" folklore still happens—document semantics is about structural signals assistive technology can use on the live page.

· by Sam Ortega

Common Document Semantics mistakes that still ship

When a client says the site "looks fine but our accessibility vendor is unhappy," people guess. They tweak color contrast. They add alt to one image. They ignore the flat div tree that never exposed a landmark. Document semantics is fixable without that theater if you treat the glossary line as something you can verify: it "Reviews structural signals that help assistive technology."

I have sat on enough launch-week calls to prefer boring checklists over heroic CSS.

Outcome if you catch these early

You ship primary templates with a usable structural map after render—landmarks, headings, native semantics—with findings tied to URLs, and with Accessible names and Accessibility audit tracked as siblings. Performance and accessibility stays a route-linked report instead of a junk drawer of vibes.

Boundaries

Read-only observation. Permissioned scans. Not a pentest. Not a silent code edit. Document semantics lives under Accessibility with Accessibility audit, Accessible names, and Viewport configuration. Structure ≠ labels ≠ viewport ≠ full WCAG with users. The Performance and accessibility product runs those audits after a real-browser render of discovered pages.

Loading diagram.

Mistake 1: visual hierarchy without DOM hierarchy

Designers ship a type scale. Engineers style divs to match. Heading navigation finds nothing. Dig into the rendered tree, not the Figma export.

html
<!-- Mistake -->
<div class="h1-style">Plans</div>
<div class="h2-style">For startups</div>

<!-- Fix direction -->
<h1>Plans</h1>
<h2>For startups</h2>

Mistake 2: no main, or five competing mains

Primary content should be findable as main. Zero landmarks force linear trawling. Duplicate main regions confuse the map. One clear main beats clever layout wrappers.

Mistake 3: nav that is only a row of links in a div

Sighted users see a nav bar. Assistive tech sees generic links. Wrap primary navigation in nav (and name it if you have several). Product language is structural signals that help—not decoration roles on every wrapper.

text
mistake: nav_is_a_div
symptom: "header looks fine in screenshots"
fix: use nav/header/main/footer appropriately; rescan the same URL

Mistake 4: collapse Document semantics into Accessible names

Accessible names "Finds controls and content that lack usable accessible labels." Labeling a hamburger does not create a heading outline. Separate tickets when both fire. What embarrasses you on a call is claiming "we fixed a11y" when you only patched one slice.

Mistake 5: treat one local Lighthouse run as the handoff

Accessibility audit "Runs automated accessibility checks in a rendered browser page." That sibling still is not a license to skip Document semantics. Run the checks on the URLs you are actually handing over, after the final shell ships.

Mistake 6: SPA empty states only

You scan /app before login and congratulate yourselves. The public marketing templates—the ones in the SOW—still ship flat heroes. Scope the URL list to what the client will click without a privileged session. Public read-only scanning will not invent authenticated chrome.

Mistake 7: ARIA spam as a substitute for native structure

Sprinkling role="region" everywhere without names creates noise. Prefer native elements first. Structural signals should clarify the document, not bury it.

How I verify without folklore

I dump the scan's Document semantics findings by URL, open the same routes in a real browser, and check landmarks plus headings. Then I open Accessible names if controls look anonymous.

bash
# Permissioned: keep the URL list explicit
cat <<'EOF'
https://client.example.com/
https://client.example.com/pricing
EOF
text
ask: structural signals that help assistive technology on rendered pages?
compare: main/nav/header/footer + heading outline vs design intent
follow_up: accessible-names, accessibility-audit, viewport-configuration

No invented scores. No invented prices. Just the definition and the mismatches.

Mistake 8: export one PDF and never re-check

Templates change after handoff—new heroes, new cookie banners, new app shells. Teams export one accessibility PDF at launch and never re-render. The check's value is that you can observe again on the same URLs. Monitoring and follow-up scans exist so page-quality findings stay beside new issues instead of rotting in final_a11y.pdf.

What I put in the engineering note

I keep a short, scannable block the next person can paste into the portal without rewriting history:

text
document_semantics_note:
  urls: [/ , /pricing, /contact]
  landmarks: main=yes nav=yes header=yes footer=yes
  headings: h1_count=1 outline=ok|skippy
  siblings: accessible-names|accessibility-audit|viewport
  rescan: scheduled|done

If the outline is skippy, I name the template file or CMS block responsible. Vague "fix structure" tickets bounce. Specific "hero on /pricing uses div.title instead of h1" tickets close.

Ownership after you leave

Somebody has to own the design system primitives that emit headings and landmarks. If every landing page forks its own hero markup, the same mistake returns on the next campaign. I write the component owner into the handoff packet the same way I write the registrar owner for DNS work. Ambiguity here recreates flat pages six months later when a contractor ships a new promo template under deadline.

Related next step

Keep the language concrete: structural signals, page URL, sibling check. Soft links: Document semantics, Accessible names, Performance and accessibility. Soft close: a permissioned SlaySlop pass ties structure findings to routes better than a folder of unlabeled screenshots.