frontend
Technical notes on web development, DevOps, and AI integration.
103 articles
- 10:26frontend
Four Commits, One Flag: When the Image Optimizer Is the Bug
Flat-color artwork turned muddy by the default WebP quality-75 pipeline: a single unoptimized flag served the original file untouched.
TL;DR: The Next.js Image component's default lossy WebP pipeline at quality 75 wrecked flat-color mascot artwork, adding fat edges and banding the pristine PNG never had. Instead of touching global config, the fix was one unoptimized prop on that single hero slot; the 170 KB file is small enough that skipping optimization costs little. Photos stay optimized.
#nextjs#next-image#webp - 10:18frontend
A Sticker on the Wrong Wall: One Brand Mark, Two Files
A dark pill was a patch, not a fix: a brand mark needs two assets the moment a site ships a real, user-controlled dark mode.
TL;DR: A brand wordmark can't work on both themes as one asset, so two Next.js Image components share one slot, toggled with dark:hidden and dark:block—zero JavaScript. The Tailwind v4 gotcha: dark defaults to prefers-color-scheme, not a class, so the override must respect the manual toggle. Filters and single-file hacks fail; two files keep theme state in one place.
#css#tailwind#dark-mode - 10:06frontend
Ditching Manual Height Math at the Bottom of the Page
The taller brand column needed no height math and no JavaScript at all: CSS grid stretch plus one auto margin did the entire job.
TL;DR: While refactoring a site footer, the author nearly wrote JavaScript to measure column heights before realizing CSS grid's default stretch already equalized them. Pinning the company line to the bottom took only flex column plus mt-auto. The lesson: inspect defaults first, since two class names beat any runtime measuring script.
#css#flexbox#tailwind - 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 - 08:16frontend
Icon Cards from a Union Type, Guarded by the Compiler
A closed icon registry plus a required union key lets the TypeScript compiler keep card icons honest.
TL;DR: Icons hardcoded in JSX drift out of sync across typed content and locale files, silently breaking the UI. Moving icon choices into content data as a closed union type, backed by a small registry, makes the compiler reject mismatches at build time. Three files changed together, zero runtime cost, no silent failures.
#typescript#react#lucide - 07:27frontend
Three Passes, Less and Less
Three restyling commits, one direction: drop the ring, drop the icon wrapper, delete the duplicated certification strip. Negative diff.
TL;DR: After three restyling passes, the author realized less is more. Removing a decorative span wrapper around Lucide icons let them breathe with thinner strokes and larger sizes, while deleting a duplicated certification strip cut 34 lines. The takeaway: successful restyling is reduction, so run a deletion sweep and ask who would miss each decoration.
#css#tailwindcss#refactoring - 07:27frontend
The Card That Stopped Decorating Itself
Matching a card style turned out to be about deleting decoration and borrowing one shared divider class, not stacking more utilities.
TL;DR: Chasing a calmer card style, the author almost copied utility classes by hand until discovering the clean card used one shared class, reference-divider. The fix was mostly deleting borders, shadows, decorations, and an index prop, keeping only focus-visible and motion-reduce for accessibility. Lesson: prefer shared styles and divide-x/divide-y over duplicated decoration.
#css#tailwindcss#refactoring - 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