[{"data":1,"prerenderedAt":270},["ShallowReactive",2],{"post-calm-frontend-performance":3,"blog-posts":14},{"_path":4,"title":5,"description":6,"date":7,"tags":8,"readingTime":12,"body":13},"\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",[9,10,11],"performance","frontend","web",5,"# A Calm Approach to Frontend Performance\n\nFrontend performance gets loud fast—new metrics, new tooling, new guilt. A calmer approach works better on real projects: measure what people feel, fix the critical path, and ignore optimizations that do not move the needle.\n\n## Measure experience, not vanity scores\n\nLab scores are useful. They are not the product. A green lighthouse category can still hide a page that feels slow because fonts block text, images shove layout around, or a hydration storm freezes the first click.\n\nStart with a short list of questions tied to your site:\n\n- How fast is meaningful text visible on a mid-range phone?\n- Does the layout jump while images and ads (if any) load?\n- Can someone tap the primary nav or CTA without waiting on JS?\n- Are blog posts readable if scripts fail?\n\nAnswer those with a mix of field intuition and light tooling. Chrome performance panel, Web Vitals extensions, and a throttled network profile are enough for most personal sites. You do not need a full RUM pipeline before you compress an oversized hero image.\n\nWhen you do look at metrics, prefer ones with a story:\n\n- **LCP** — is the main content late because of image weight or late discovery?\n- **CLS** — are dimensions missing on media or injected banners?\n- **INP** — is the main thread busy when people try to click?\n\nTreat the score as a clue, then verify with your eyes on a real device. Optimization that only flatters the lab is theater.\n\n## Fix the critical path before micro-tuning\n\nThe critical path is whatever blocks the first useful paint and the first useful interaction. On a content site that is usually HTML, critical CSS, fonts, and the hero or title region. On a tool page it may be the form shell before any analysis runs.\n\nOrder of attack that rarely wastes time:\n\n1. **Shrink and correctly size images** — modern formats, explicit width\u002Fheight, responsive `srcset` where it matters\n2. **Stop layout shift** — reserve space; avoid inserting content above existing content after load\n3. **Subset and load fonts deliberately** — `font-display`, limited weights, no surprise FOIT if you can avoid it\n4. **Defer non-critical JS** — analytics, widgets, and “nice to have” enhancers should not gate reading\n5. **Cache static assets** — sensible headers or host defaults for hashed bundles\n\nOnly after those should you obsess over shaving 3 KB from a dependency or rewriting a loop. Premature micro-tuning feels productive and often rearranges deck chairs while a 2 MB PNG still sits in the hero.\n\nFor SPAs and static generators, also watch the JS you ship for pages that are mostly prose. A blog post does not need the same client bundle as an interactive app. Route-level code splitting and lean markdown rendering keep reading cheap.\n\n## Budget complexity like you budget kilobytes\n\nEvery dependency is a performance decision. Adding a chart library, animation framework, or CMS runtime for a portfolio flourish has a cost that compounds with every page load.\n\nCreate a lightweight budget in plain language:\n\n- Home and blog index should stay readable without waiting on large client graphs\n- Third-party scripts require a written reason and an owner (even if the owner is you)\n- New features must declare whether they load globally or only on their route\n\nBudgets fail when they are absolute numbers nobody remembers. Prefer rules of thumb you can enforce in review: “no new global script,” “images over X KB need a reason,” “fonts limited to two weights.” When you break a rule, break it consciously and note why.\n\nBe especially wary of “just one more” patterns: another icon pack, another font for one heading, another client-side markdown parser when the build already emits HTML. Personal sites rot through accumulation more often than through one dramatic mistake.\n\nA useful habit is a quarterly dependency walk: open the lockfile, ask which packages still earn their weight, and remove anything you no longer touch. Dead code is not free—bundlers help, but unused CSS, leftover client plugins, and forgotten widgets still tax attention and risk. Performance culture is as much subtraction as compression.\n\n## Optimize for maintainability so wins survive\n\nA performance pass that only you understand will regress the week you add a feature. Prefer fixes that are structural:\n\n- Image pipeline or clear folder conventions instead of one-off compressed files with no source\n- Layout components that always require dimensions for media\n- Font loading centralized in one place\n- Analytics loaded behind consent or after idle, in one module\n\nDocument the non-obvious wins in the README: “We avoid X because it hurt LCP,” “Hero images go through Y.” Future you—or a collaborator—needs the constraint, not just the current numbers.\n\nRe-measure after each meaningful change. Performance work without a before\u002Fafter is vibes. Keep a simple note: date, change, what improved. That habit prevents thrashing and makes it easier to stop when the site already feels fine.\n\n## Wrap-up\n\nCalm performance work starts with user-visible questions, attacks the critical path first, budgets complexity as carefully as bytes, and leaves maintainable defaults behind. Chase fewer metrics with more honesty, and your site will feel fast without turning every commit into an optimization spiral.",[15,23,33,42,49,57,64,72,79,86,93,101,108,115,122,124,130,138,147,155,161,168,174,182,190,197,203,209,216,222,228,234,241,248,255,263],{"_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,92,10],"html",{"_path":94,"title":95,"description":96,"date":97,"tags":98,"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",[99,100,10],"vue","javascript",{"_path":102,"title":103,"description":104,"date":105,"tags":106,"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",[107,10,41],"ux",{"_path":109,"title":110,"description":111,"date":112,"tags":113,"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,114],"industry",{"_path":116,"title":117,"description":118,"date":119,"tags":120,"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",[99,10,121],"best-practices",{"_path":4,"title":5,"description":6,"date":7,"tags":123,"readingTime":12},[9,10,11],{"_path":125,"title":126,"description":127,"date":128,"tags":129,"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":131,"title":132,"description":133,"date":134,"tags":135,"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,136,137],"cloud-gaming","tech",{"_path":139,"title":140,"description":141,"date":142,"tags":143,"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",[144,145,146],"nuxt","ssg","devops",{"_path":148,"title":149,"description":150,"date":151,"tags":152,"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",[100,153,154],"devtools","debugging",{"_path":156,"title":157,"description":158,"date":159,"tags":160,"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",[100,121,10],{"_path":162,"title":163,"description":164,"date":165,"tags":166,"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,167],"wellness",{"_path":169,"title":170,"description":171,"date":172,"tags":173,"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",[92,9,10],{"_path":175,"title":176,"description":177,"date":178,"tags":179,"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",[180,181,31],"git","workflow",{"_path":183,"title":184,"description":185,"date":186,"tags":187,"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,188,189],"streaming","content",{"_path":191,"title":192,"description":193,"date":194,"tags":195,"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",[196,99,10],"typescript",{"_path":198,"title":199,"description":200,"date":201,"tags":202,"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",[144,146,10],{"_path":204,"title":205,"description":206,"date":207,"tags":208,"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,121],{"_path":210,"title":211,"description":212,"date":213,"tags":214,"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,215,56],"co-op",{"_path":217,"title":218,"description":219,"date":220,"tags":221,"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",[92,39,10],{"_path":223,"title":224,"description":225,"date":226,"tags":227,"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",[180,181,31],{"_path":229,"title":230,"description":231,"date":232,"tags":233,"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",[107,10,41],{"_path":235,"title":236,"description":237,"date":238,"tags":239,"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",[100,121,240],"maintainability",{"_path":242,"title":243,"description":244,"date":245,"tags":246,"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,247,107],"game-design",{"_path":249,"title":250,"description":251,"date":252,"tags":253,"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",[107,254,41],"writing",{"_path":256,"title":257,"description":258,"date":259,"tags":260,"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",[261,262,56],"documentation","open-source",{"_path":264,"title":265,"description":266,"date":267,"tags":268,"readingTime":12},"\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",[92,10,269],"architecture",1784620504908]