accessibility
Technical notes on web development, DevOps, and AI integration.
14 articles
- 17:46frontend
Taking 242 Client Names Out of an Image
242 client names used to live inside one image. Now they are indexed text, readable by screen readers, with a total computed from the data.
TL;DR: Client names were locked in a JPEG, invisible to screen readers and Google. The fix moved 242 names into a typed TypeScript module grouped by sector and rendered them as real searchable HTML with Next.js. The image stays for visual proof under WCAG's logo exception, while live text keeps the list accessible, indexed, and always in sync.
#nextjs#seo#accessibility - 09:16frontend
A Font Experiment That Ended in 22 Minutes
Reverting a heading font took one line because every heading reads a single token. Taste experiments can be cheap.
TL;DR: A font experiment on this project lasted twenty-two minutes: one commit swapped the heading token to Source Serif 4, the next reverted it. Because every heading reads a single CSS variable instead of hardcoded font names, the whole trial cost one line each way. The failed taste experiment left behind cheap reversibility and an untouched, durable type scale.
#css#tailwind#accessibility - 09:13frontend
Four Commits Chasing One Vanishing Border
A card border vanished on a muted background because two grays were near-twins. Tokens, contrast, and backdrop assumptions.
TL;DR: The missing borders were never broken code; the border token was nearly identical to the muted background, so the line was invisible. The fix used Tailwind's opacity modifier for a subtle primary-colored border plus a card tint, which also satisfies WCAG 1.4.11 contrast. Per-card icons and left alignment followed; now every component gets tested on every surface.
#css#tailwind#accessibility - 06:55frontend
One Card Component, Two Grids
Copying Tailwind classes from the corporate cards was not enough: the list-item identity and its keyboard handlers had to move too.
TL;DR: Copying Tailwind classes between cards failed because the divider depended on the li-in-ul structure, not styling alone. Converting the office card to a list item meant adding the button role, keyboard handlers, and proper ul wrappers. Extracting an OfficeCard component with two props made the 2+3 layout trivial, though a leftover ul rendered the list twice until deleted.
#react#tailwindcss#accessibility - 02:34frontend
Animating a Static Map with One SVG Overlay, No Map Library
Glowing office dots and flowing lines on a static map with one SVG overlay: viewBox 0-100, preserveAspectRatio none, reduced-motion-safe CSS animation.
TL;DR: Instead of Mapbox or hardcoded pixels, the author stacked an SVG with viewBox="0 0 100 100" over a static image, so office positions in percentage coordinates stretch perfectly with the viewport. Flowing dashed lines come from animating stroke-dashoffset, layered with pulsing dots, all pure CSS. The overlay stays accessible via pointer-events-none and aria-hidden, wrapped in prefers-reduced-motion.
#svg#css#animation - 01:46frontend
Minimal Cards, Decorative Icons, and Deleting Metadata
Unverified metadata is gone; what remains is name, sector, description, plus three technical details that keep cards accessible.
TL;DR: The author stripped unverified metadata from company cards, keeping only name, sector, and description to protect content integrity. CSS pseudo-elements create dividers without DOM bloat, while aria-hidden icons and motion-reduce utilities keep the component accessible. Explicit array slicing handles grid rows predictably, avoiding awkward orphaned cards.
#accessibility#css#react - 00:44frontend
I Deleted a Photo and Shipped a Gradient
The hero photo needed an 85-90 percent overlay just to stay legible. One token-driven CSS motif replaced both, drift included, reduced-motion safe.
TL;DR: Replacing a hero photo with a tiled CSS gradient exposed a simple truth: the old JPEG was drowned under an 85-90 percent overlay, so the overlay color was doing the real branding work. The new pattern uses color-mix, a design token, and a transform-only drift animation gated by prefers-reduced-motion. Result: one asset deleted, zero downloads, better accessibility.
#css#nextjs#accessibility - 23:18frontend
Turning 113 Client Logos into a Pure CSS Marquee
A 113-logo client grid pushed everything below the fold. I rebuilt it as a pure CSS two-row marquee with an exact -50 percent loop.
TL;DR: A 113-logo static grid buried the homepage content, so I replaced it with a pure CSS marquee instead of pulling in a JS library. Two duplicated tracks animate translate3d from 0 to -50%, with flex gap folded into padding so the loop seams perfectly. Transform-only animation, edge fades via mask-image, aria-hidden tiles, and prefers-reduced-motion keep it fast and accessible.
#css#nextjs#accessibility - 13:49frontend
Why I Stopped Patching HTML lang Client-Side in Next.js
Moving the root layout into the [locale] segment makes html lang server-rendered, deletes the client-side language bootstrap, and passes the structural audit.
TL;DR: We replaced a client-side script that guessed the reader's language with Next.js's recommended pattern: nesting the root layout under a dynamic locale segment so the lang attribute renders correctly on the server. This brought free 404s for unknown locales, static prerendering, and correct screen-reader pronunciation, even on 404 pages. Detection now lives in an Accept-Language redirect instead.
#nextjs#i18n#html