[{"data":1,"prerenderedAt":270},["ShallowReactive",2],{"post-progressive-enhancement-still-pays-off":3,"blog-posts":14},{"_path":4,"title":5,"description":6,"date":7,"tags":8,"readingTime":12,"body":13},"\u002Fblog\u002Fprogressive-enhancement-still-pays-off\u002F","Progressive Enhancement Still Pays Off","Build the core experience in HTML first, then layer CSS and JavaScript so your site stays useful when networks, scripts, or devices misbehave.","2024-11-05",[9,10,11],"html","frontend","architecture",5,"# Progressive Enhancement Still Pays Off\n\nFrameworks make it easy to ship a blank shell that only works after JavaScript wakes up. Progressive enhancement flips the default: start with a usable document, then improve it. The result is sturdier sites and fewer “white screen of waiting” moments.\n\n## Begin with a document that already works\n\nBefore choosing a UI library, ask what the page must accomplish with markup alone. A blog should expose titles, dates, and article text. A contact form should post somewhere sensible. A tool landing page should explain what the tool does and link to how it runs.\n\nThat baseline is not nostalgia—it is a contract. Search engines, feed readers, text browsers, aggressive content blockers, and flaky mobile networks all benefit when meaning lives in HTML. If your primary content only appears after a client fetch, you have turned reading into an app bootstrap problem.\n\nPractical habits:\n\n- Put real headings and paragraphs in the initial HTML for content pages\n- Use native controls (`a`, `button`, `form`, `input`) for critical actions\n- Prefer server-rendered or statically generated HTML for marketing and blog surfaces\n- Treat JSON-driven UI as an enhancement for interactivity, not the only source of truth\n\nYou can still use Vue or Nuxt. The point is that the generated output should already contain the story, not an empty `#app` waiting for hydration to invent it. Static generation and server rendering are progressive enhancement at build time: the browser receives a document first, and interactivity arrives as a second chapter—not the only chapter.\n\n## Layer CSS for clarity, not as a gate\n\nCSS enhancement means the page remains understandable unstyled, then becomes clearer and more pleasant with styles applied. It does not mean “hide everything until fonts and animations load.”\n\nKeep structure independent of decoration. If a navigation only exists as absolutely positioned overlays with no in-flow links, a CSS failure becomes a navigation failure. If hierarchy depends solely on color, contrast bugs become comprehension bugs.\n\nUseful checks:\n\n- Disable CSS briefly—can you still find the main content and primary links?\n- Confirm focus styles are visible without relying on box-shadow alone\n- Avoid making essential content `display: none` until a script runs\n- Use progressive CSS features (`@supports`, newer selectors) as bonuses, not requirements for basic layout\n\nMotion and theme flourishes belong in this layer too—but they should never be the only way to understand state. A loading indicator can be animated; the underlying status text should still exist.\n\n## Add JavaScript where it earns its keep\n\nJavaScript is where progressive enhancement is most often abandoned. Client-side routing, optimistic UI, and rich editors are valuable—when they improve an already workable path.\n\nGood enhancement targets:\n\n- Instant client navigations after the first HTML landing\n- Live validation that supplements, rather than replaces, server or constraint validation\n- Keyboard shortcuts layered on top of visible controls\n- Local file processing for privacy tools after the page explains the flow in static copy\n\nRisky patterns:\n\n- Rendering the only copy of article text exclusively on the client\n- Buttons that do nothing without listeners and have no `form` or `href` fallback\n- “Single page” tools that cannot even show instructions if the bundle fails\n\nWhen you need interactivity, prefer progressive APIs: start with a form POST or a static result page, then upgrade to in-place updates. Feature-detect before assuming `localStorage`, clipboard, or Web Crypto are available, and degrade with a clear message instead of a silent no-op.\n\nTeam process matters here. If design mockups only show the JavaScript-complete state, engineers will implement only that state. Ask for a “no-JS” or “HTML-first” frame in reviews for content pages. It is a small ritual that prevents enhancement from becoming a euphemism for exclusive dependence.\n\n## Test the broken paths on purpose\n\nEnhancement only proves itself when you break things. Add a short resilience checklist to your release habit:\n\n1. Load the page with JavaScript disabled—content and primary links still work?\n2. Throttle to Slow 3G—is the first meaningful text visible before heavy scripts finish?\n3. Block third-party scripts—does the core product still function?\n4. Tab through with a keyboard—can you reach actions without a mouse?\n5. Read the raw HTML output—does it contain the words you claim to publish?\n\nPersonal and portfolio sites are perfect places to practice this discipline. You control the stack, the content is mostly documents, and the cost of a JS-only homepage is reputational: visitors bounce before they meet you.\n\nDocument intentional exceptions. A canvas game or a WebGL demo may legitimately require JavaScript. Say so early, and still provide surrounding HTML context—what it is, what to expect, what browser features it needs—so the page is not a mystery while assets load.\n\n## Wrap-up\n\nProgressive enhancement is not anti-JavaScript; it is pro-resilience. Ship a working document, clarify it with CSS, and improve it with script only where the upgrade is real. When networks stutter or scripts fail, your site should still introduce you—not apologize with a blank screen.",[15,23,33,42,49,57,64,72,79,86,92,100,107,114,121,129,135,143,152,160,166,173,179,187,195,202,208,214,221,227,233,239,246,253,260,268],{"_path":16,"title":17,"description":18,"date":19,"tags":20,"readingTime":12},"\u002Fblog\u002Fclient-side-tools-that-earn-trust\u002F","Client-Side Tools That Earn Trust","How to design browser tools that feel private by default—local processing, clear data boundaries, and UX that never asks people to guess where their files go.","2026-07-20",[21,10,22],"privacy","tools",{"_path":24,"title":25,"description":26,"date":27,"tags":28,"readingTime":32},"\u002Fblog\u002Fusb-c-cables-that-actually-matter\u002F","USB-C Cables That Actually Matter","How to buy USB-C cables by wattage and data speed so you stop guessing which cord charges slowly or fails a file transfer.","2026-07-14",[29,30,31],"gadgets","hardware","productivity",4,{"_path":34,"title":35,"description":36,"date":37,"tags":38,"readingTime":12},"\u002Fblog\u002Fcolor-contrast-people-can-read\u002F","Color Contrast People Can Actually Read","Quick checks for text and UI colors that stay legible in light mode, dark mode, and on mediocre displays.","2026-07-07",[39,40,41],"accessibility","css","design",{"_path":43,"title":44,"description":45,"date":46,"tags":47,"readingTime":12},"\u002Fblog\u002Fportable-ssds-worth-carrying\u002F","Portable SSDs Worth Carrying","When a pocket SSD beats cloud sync for travel, backups, and large media—and how to pick one that stays fast and durable.","2026-06-26",[29,48,31],"storage",{"_path":50,"title":51,"description":52,"date":53,"tags":54,"readingTime":12},"\u002Fblog\u002Fwireless-earbuds-that-last-the-day\u002F","Wireless Earbuds That Last the Day","Simple charging and fit habits that stretch earbud battery life without babysitting the case every few hours.","2026-06-18",[29,55,56],"audio","habits",{"_path":58,"title":59,"description":60,"date":61,"tags":62,"readingTime":32},"\u002Fblog\u002Fwhen-a-second-monitor-helps\u002F","When a Second Monitor Actually Helps","A practical take on dual screens for coding, writing, and research—plus when a single good display is still the better desk.","2026-06-10",[29,31,63],"workspace",{"_path":65,"title":66,"description":67,"date":68,"tags":69,"readingTime":32},"\u002Fblog\u002Fshort-session-game-picks\u002F","Short-Session Games That Respect Your Evening","Genres and design patterns that fit a 30-minute window without the “one more hour” trap.","2026-06-04",[70,56,71],"gaming","indie",{"_path":73,"title":74,"description":75,"date":76,"tags":77,"readingTime":12},"\u002Fblog\u002Fphone-charging-habits-that-age-better\u002F","Phone Charging Habits That Age Better","Practical charging routines that keep a phone useful longer—without obsessing over the perfect battery percentage.","2026-05-23",[29,78,56],"mobile",{"_path":80,"title":81,"description":82,"date":83,"tags":84,"readingTime":12},"\u002Fblog\u002Ffinishing-side-projects-without-burning-out\u002F","Finishing Side Projects Without Burning Out","A practical playbook for shrinking scope, shipping thin vertical slices, and keeping personal projects fun long enough to actually launch.","2026-05-14",[31,85,56],"side-projects",{"_path":87,"title":88,"description":89,"date":90,"tags":91,"readingTime":12},"\u002Fblog\u002Faccessible-forms-people-finish\u002F","Accessible Forms That People Actually Finish","Practical accessibility checks for labels, errors, and focus so your forms work for more users.","2026-05-07",[39,9,10],{"_path":93,"title":94,"description":95,"date":96,"tags":97,"readingTime":12},"\u002Fblog\u002Fvue-composition-api-patterns\u002F","Practical Vue Composition API Patterns","Reusable patterns for composables, shared state, and cleaner component logic in Vue 3.","2026-04-27",[98,99,10],"vue","javascript",{"_path":101,"title":102,"description":103,"date":104,"tags":105,"readingTime":12},"\u002Fblog\u002Fdesigning-empty-states-that-teach\u002F","Designing Empty States That Teach the Product","Empty screens are not placeholders—they are onboarding. How to write, layout, and sequence first-run UI so people know what to do next.","2026-04-19",[106,10,41],"ux",{"_path":108,"title":109,"description":110,"date":111,"tags":112,"readingTime":12},"\u002Fblog\u002Fwhy-indie-games-keep-winning\u002F","Why Indie Games Keep Winning Attention","How small teams punch above their weight with focused design, personality, and smart scope.","2026-04-13",[70,71,113],"industry",{"_path":115,"title":116,"description":117,"date":118,"tags":119,"readingTime":12},"\u002Fblog\u002Fkeeping-component-props-simple\u002F","Keeping Component Props Simple","When to split a component, when to use slots, and how to avoid prop objects that grow forever.","2026-04-03",[98,10,120],"best-practices",{"_path":122,"title":123,"description":124,"date":125,"tags":126,"readingTime":12},"\u002Fblog\u002Fcalm-frontend-performance\u002F","A Calm Approach to Frontend Performance","Performance work that sticks: measure what users feel, fix the critical path first, and avoid optimization theater on personal and portfolio sites.","2026-03-26",[127,10,128],"performance","web",{"_path":130,"title":131,"description":132,"date":133,"tags":134,"readingTime":12},"\u002Fblog\u002Fcss-layout-without-the-hacks\u002F","CSS Layout Without the Hacks","Modern Flexbox and Grid techniques that replace brittle floats, magic numbers, and overflow tricks.","2026-03-17",[40,10,41],{"_path":136,"title":137,"description":138,"date":139,"tags":140,"readingTime":12},"\u002Fblog\u002Fcloud-gaming-what-gets-in-the-way\u002F","Cloud Gaming: What Still Gets in the Way","Latency, input feel, and library lock-in—why streaming games is impressive and still uneven.","2026-03-10",[70,141,142],"cloud-gaming","tech",{"_path":144,"title":145,"description":146,"date":147,"tags":148,"readingTime":12},"\u002Fblog\u002Fshipping-static-sites-with-nuxt\u002F","Shipping Static Sites with Nuxt","How I build and deploy a Nuxt site to Cloudflare Pages with predictable routes and content.","2026-03-02",[149,150,151],"nuxt","ssg","devops",{"_path":153,"title":154,"description":155,"date":156,"tags":157,"readingTime":12},"\u002Fblog\u002Fdebugging-faster-in-the-browser\u002F","Debugging Faster in the Browser","A short toolkit of DevTools habits—breakpoints, network filters, and DOM inspection—that cut debug time.","2026-02-24",[99,158,159],"devtools","debugging",{"_path":161,"title":162,"description":163,"date":164,"tags":165,"readingTime":12},"\u002Fblog\u002Fsmall-javascript-habits-that-scale\u002F","Small JavaScript Habits That Scale","Everyday habits—naming, early returns, and clearer async flow—that keep front-end codebases maintainable.","2026-02-18",[99,120,10],{"_path":167,"title":168,"description":169,"date":170,"tags":171,"readingTime":32},"\u002Fblog\u002Fhealthier-gaming-routine\u002F","Building a Healthier Gaming Routine","Simple habits for sessions that stay fun—breaks, goals, and knowing when to stop.","2026-02-02",[70,56,172],"wellness",{"_path":174,"title":175,"description":176,"date":177,"tags":178,"readingTime":12},"\u002Fblog\u002Fresponsive-images-without-layout-shift\u002F","Responsive Images Without Layout Shift","Width, height, srcset, and lazy loading patterns that keep pages stable while images load.","2026-01-22",[9,127,10],{"_path":180,"title":181,"description":182,"date":183,"tags":184,"readingTime":12},"\u002Fblog\u002Fgit-habits-for-small-teams\u002F","Git Habits for Solo and Small Teams","Branch naming, commit messages, and PR hygiene that keep history useful without slowing you down.","2026-01-09",[185,186,31],"git","workflow",{"_path":188,"title":189,"description":190,"date":191,"tags":192,"readingTime":12},"\u002Fblog\u002Fwhat-makes-a-game-stream-worth-watching\u002F","What Makes a Game Stream Worth Watching","Commentary, pacing, and community—why some streams click even when the gameplay is messy.","2025-12-14",[70,193,194],"streaming","content",{"_path":196,"title":197,"description":198,"date":199,"tags":200,"readingTime":12},"\u002Fblog\u002Fwhen-to-reach-for-typescript-vue\u002F","When to Reach for TypeScript on a Vue App","A pragmatic take on where TypeScript pays off in Vue—and where plain JS is still fine.","2025-11-30",[201,98,10],"typescript",{"_path":203,"title":204,"description":205,"date":206,"tags":207,"readingTime":12},"\u002Fblog\u002Fenvironment-variables-for-static-sites\u002F","Environment Variables for Static Sites","How to use build-time env vars in Nuxt and other static generators without leaking secrets or breaking CI.","2025-10-05",[149,151,10],{"_path":209,"title":210,"description":211,"date":212,"tags":213,"readingTime":12},"\u002Fblog\u002Fpicking-a-side-project-stack\u002F","Picking a Side Project Stack Without Overthinking It","How to choose tools for a personal project when the goal is shipping, not building the perfect architecture.","2025-09-18",[31,10,120],{"_path":215,"title":216,"description":217,"date":218,"tags":219,"readingTime":32},"\u002Fblog\u002Fco-op-games-for-busy-friends\u002F","Co-op Games That Work When Everyone Is Busy","Async-friendly and drop-in co-op picks for friend groups that rarely share the same free evening.","2025-08-22",[70,220,56],"co-op",{"_path":222,"title":223,"description":224,"date":225,"tags":226,"readingTime":12},"\u002Fblog\u002Fsemantic-html-still-matters\u002F","Semantic HTML Still Matters","Landmarks, headings, and native elements that make pages easier to use, style, and maintain.","2025-07-06",[9,39,10],{"_path":228,"title":229,"description":230,"date":231,"tags":232,"readingTime":12},"\u002Fblog\u002Fgit-without-the-jargon\u002F","Learning Git Without the Jargon","A plain-language mental model for commits, branches, and merges when tutorials assume you already know Git.","2025-05-14",[185,186,31],{"_path":234,"title":235,"description":236,"date":237,"tags":238,"readingTime":12},"\u002Fblog\u002Fwhy-loading-states-matter\u002F","Why Loading States Matter More Than Animations","Skeletons, disabled buttons, and honest feedback beat flashy spinners when data takes time to arrive.","2025-03-28",[106,10,41],{"_path":240,"title":241,"description":242,"date":243,"tags":244,"readingTime":12},"\u002Fblog\u002Fnaming-things-so-future-you-survives\u002F","Naming Things So Future You Survives","Practical rules for naming variables, components, and files so a codebase stays readable months after the clever jokes fade.","2025-03-10",[99,120,245],"maintainability",{"_path":247,"title":248,"description":249,"date":250,"tags":251,"readingTime":12},"\u002Fblog\u002Fsave-systems-that-respect-time\u002F","Save Systems That Respect the Player’s Time","Checkpoints, autosaves, and manual slots are design choices. How thoughtful saving reduces rage-quits without deleting tension from the game.","2025-02-28",[70,252,106],"game-design",{"_path":254,"title":255,"description":256,"date":257,"tags":258,"readingTime":12},"\u002Fblog\u002Fmicrocopy-that-makes-interfaces-human\u002F","Microcopy That Makes Interfaces Feel Human","Button labels, error lines, and helper text are product design. How to write UI words that reduce hesitation without sounding like a chatbot.","2025-01-22",[106,259,41],"writing",{"_path":261,"title":262,"description":263,"date":264,"tags":265,"readingTime":12},"\u002Fblog\u002Freadmes-that-get-projects-running\u002F","READMEs That Get Projects Running","How to write a README that helps someone—including future you—install, run, and understand a project without opening a support chat.","2024-12-18",[266,267,56],"documentation","open-source",{"_path":4,"title":5,"description":6,"date":7,"tags":269,"readingTime":12},[9,10,11],1784620505265]