[{"data":1,"prerenderedAt":277},["ShallowReactive",2],{"post-page-transitions-that-dont-fight-your-data":3,"blog-posts":14},{"_path":4,"title":5,"description":6,"date":7,"tags":8,"readingTime":12,"body":13},"\u002Fblog\u002Fpage-transitions-that-dont-fight-your-data\u002F","Page Transitions That Dont Fight Your Data","Route animations feel polished when they respect loading time, preserve context, and get out of the way for people who prefer less motion.","2026-07-30",[9,10,11],"frontend","vue","ux",6,"# Page Transitions That Don't Fight Your Data\n\nA smooth page transition promises continuity. A slow fetch with a flashy fade promises confusion. The goal is not motion for its own sake—it is helping people understand that navigation started, that the app is still working, and that the new view belongs to the same place.\n\n## Treat navigation as a state change, not a magic wipe\n\nIn a multi-page site, the browser handles the handoff: old document out, new document in. In an SPA, you own that handoff. If you animate the entire viewport away before new content exists, users stare at an empty stage.\n\nStart by naming what changes on navigation:\n\n- **Shell** — header, nav, layout chrome (usually stays)\n- **Page region** — the part that swaps per route\n- **Data** — lists, detail records, form defaults (often arrives after the route)\n\nAnimate the page region, not the whole app. Keep the shell stable so orientation survives the transition. If your layout uses a shared `\u003Cmain>` wrapper, that is the boundary—not `body` or `#__nuxt`.\n\nIn Nuxt 3, a minimal pattern is a keyed outlet inside the layout:\n\n```vue\n\u003C!-- layouts\u002Fdefault.vue -->\n\u003Ctemplate>\n  \u003CAppHeader \u002F>\n  \u003Cmain id=\"main-content\">\n    \u003CNuxtPage :transition=\"{ name: 'page', mode: 'out-in' }\" \u002F>\n  \u003C\u002Fmain>\n  \u003CAppFooter \u002F>\n\u003C\u002Ftemplate>\n```\n\n`mode: 'out-in'` runs leave-then-enter, which is readable when both views are ready. It is painful when the incoming page still needs a fetch—more on that below. The habit to build first: decide what is transitioning and what is not, then design loading around that boundary.\n\n## Keep the outgoing page until the next one is ready\n\nThe most common transition mistake is animating away content that still represents the truth. Someone clicks “Posts,” the blog list vanishes in 200ms, and a spinner sits in a fading container for two seconds. That feels slower than no animation at all.\n\nBetter defaults:\n\n- **Prefetch on intent** — hover or focus on nav links when routes are cheap to warm (`NuxtLink` prefetches by default in many setups; use it deliberately for heavy routes)\n- **Hold the old view** — delay the leave transition until critical data for the next route resolves, or keep the previous page visible with a lightweight progress indicator at the top\n- **Skeleton inside the incoming slot** — if you must enter early, show a layout-shaped placeholder in the page region, not a blank fade\n\nFor data that must load after navigation, coordinate transition timing with your fetch:\n\n```vue\n\u003Cscript setup>\nconst route = useRoute();\nconst { data, pending } = await useAsyncData(\n  () => `post-${route.params.slug}`,\n  () => $fetch(`\u002Fapi\u002Fposts\u002F${route.params.slug}`),\n  { watch: [() => route.params.slug] }\n);\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Carticle v-if=\"!pending && data\">\n    \u003Ch1>{{ data.title }}\u003C\u002Fh1>\n    \u003C!-- … -->\n  \u003C\u002Farticle>\n  \u003CPostSkeleton v-else \u002F>\n\u003C\u002Ftemplate>\n```\n\nPair this with CSS that does not animate opacity on an empty node. The transition should fire when there is something meaningful to show—or when a skeleton clearly marks “loading here.” Same lesson as honest loading states: the animation answers *where am I going?* not *please wait in a void*.\n\nFor shared elements (a card thumbnail expanding into a hero), reach for view transitions only when the DOM path is predictable and the benefit beats the maintenance cost. Most apps ship faster with a simple fade on the page region plus good skeletons.\n\n## Match motion to the wait you are asking for\n\nShort, subtle transitions work when navigation is mostly synchronous—static prerendered pages, cached data, or instant client-side filters. Longer or more elaborate motion belongs to moments where content is already present.\n\nRough pairing:\n\n| Situation | Transition approach |\n|-----------|---------------------|\n| Static \u002F prerendered route | Short fade or slide (150–250ms) on the page region |\n| Client fetch under ~500ms | Same, plus inline pending UI in the destination |\n| Slower or uncertain fetch | Top progress bar or skeleton; defer leave animation |\n| Modal \u002F drawer | Focus trap + enter\u002Fexit on the overlay, not the whole page |\n\nRespect `prefers-reduced-motion`. Wrap decorative keyframes in a media query and keep instant swaps as the fallback:\n\n```css\n@media (prefers-reduced-motion: no-preference) {\n  .page-enter-active,\n  .page-leave-active {\n    transition: opacity 0.2s ease;\n  }\n  .page-enter-from,\n  .page-leave-to {\n    opacity: 0;\n  }\n}\n```\n\nVue’s `\u003CTransition>` and Nuxt’s page transitions both honor this if you gate the CSS. Do not rely on motion alone to communicate state—screen readers and motion-sensitive users still need text or structure that says navigation happened.\n\nAlso watch scroll position. A beautiful fade loses trust if the new page loads scrolled to the middle of a long article because the previous scroll position leaked. Reset scroll on route change for full-page swaps (`scrollBehavior` in Vue Router, or `window.scrollTo` on `afterEach` when appropriate). Keep scroll restoration for back\u002Fforward when you can—continuity should work both ways.\n\n## What to skip when transitions cause more harm\n\nNot every route deserves animation. Skip or simplify transitions when:\n\n- **Forms are mid-edit** — confirm before leaving; do not slide away half-filled fields without warning\n- **Lists are being filtered** — updating query params in place often needs no page transition at all\n- **Errors are likely** — a dramatic exit into an error boundary feels punitive; keep the shell calm\n- **Performance is tight** — low-end phones pay for blur, scale, and staggered children; one opacity tween beats five\n\nAnti-patterns worth deleting:\n\n- Global transitions on every nested child route (nested outlets fighting each other)\n- `out-in` on the full layout when only a paragraph changed\n- Animations longer than the fetch they are masking—users learn to distrust the polish\n\nIf you are unsure, ship without a route transition first. Add motion when you can point at a specific confusion it fixes (“people did not realize the settings page changed”). Measurement beats taste: if time-to-interactive regresses, cut the effect.\n\n## Wrap-up\n\nPage transitions earn their place when they respect data timing and human perception. Keep the shell steady, animate the page region—not the whole world—hold or skeleton the outgoing view until the next one has something to show, scale motion to the wait, and fall back cleanly for reduced motion and slow devices. Continuity is the product; the easing curve is optional garnish.",[15,17,26,36,45,52,60,67,75,82,89,96,103,109,116,123,131,137,145,154,162,168,175,181,189,197,204,210,216,223,229,235,241,248,255,262,270],{"_path":4,"title":5,"description":6,"date":7,"tags":16,"readingTime":12},[9,10,11],{"_path":18,"title":19,"description":20,"date":21,"tags":22,"readingTime":25},"\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",[23,9,24],"privacy","tools",5,{"_path":27,"title":28,"description":29,"date":30,"tags":31,"readingTime":35},"\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",[32,33,34],"gadgets","hardware","productivity",4,{"_path":37,"title":38,"description":39,"date":40,"tags":41,"readingTime":25},"\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",[42,43,44],"accessibility","css","design",{"_path":46,"title":47,"description":48,"date":49,"tags":50,"readingTime":25},"\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",[32,51,34],"storage",{"_path":53,"title":54,"description":55,"date":56,"tags":57,"readingTime":25},"\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",[32,58,59],"audio","habits",{"_path":61,"title":62,"description":63,"date":64,"tags":65,"readingTime":35},"\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",[32,34,66],"workspace",{"_path":68,"title":69,"description":70,"date":71,"tags":72,"readingTime":35},"\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",[73,59,74],"gaming","indie",{"_path":76,"title":77,"description":78,"date":79,"tags":80,"readingTime":25},"\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",[32,81,59],"mobile",{"_path":83,"title":84,"description":85,"date":86,"tags":87,"readingTime":25},"\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",[34,88,59],"side-projects",{"_path":90,"title":91,"description":92,"date":93,"tags":94,"readingTime":25},"\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",[42,95,9],"html",{"_path":97,"title":98,"description":99,"date":100,"tags":101,"readingTime":25},"\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",[10,102,9],"javascript",{"_path":104,"title":105,"description":106,"date":107,"tags":108,"readingTime":25},"\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",[11,9,44],{"_path":110,"title":111,"description":112,"date":113,"tags":114,"readingTime":25},"\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",[73,74,115],"industry",{"_path":117,"title":118,"description":119,"date":120,"tags":121,"readingTime":25},"\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",[10,9,122],"best-practices",{"_path":124,"title":125,"description":126,"date":127,"tags":128,"readingTime":25},"\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",[129,9,130],"performance","web",{"_path":132,"title":133,"description":134,"date":135,"tags":136,"readingTime":25},"\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",[43,9,44],{"_path":138,"title":139,"description":140,"date":141,"tags":142,"readingTime":25},"\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",[73,143,144],"cloud-gaming","tech",{"_path":146,"title":147,"description":148,"date":149,"tags":150,"readingTime":25},"\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",[151,152,153],"nuxt","ssg","devops",{"_path":155,"title":156,"description":157,"date":158,"tags":159,"readingTime":25},"\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",[102,160,161],"devtools","debugging",{"_path":163,"title":164,"description":165,"date":166,"tags":167,"readingTime":25},"\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",[102,122,9],{"_path":169,"title":170,"description":171,"date":172,"tags":173,"readingTime":35},"\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",[73,59,174],"wellness",{"_path":176,"title":177,"description":178,"date":179,"tags":180,"readingTime":25},"\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",[95,129,9],{"_path":182,"title":183,"description":184,"date":185,"tags":186,"readingTime":25},"\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",[187,188,34],"git","workflow",{"_path":190,"title":191,"description":192,"date":193,"tags":194,"readingTime":25},"\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",[73,195,196],"streaming","content",{"_path":198,"title":199,"description":200,"date":201,"tags":202,"readingTime":25},"\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",[203,10,9],"typescript",{"_path":205,"title":206,"description":207,"date":208,"tags":209,"readingTime":25},"\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",[151,153,9],{"_path":211,"title":212,"description":213,"date":214,"tags":215,"readingTime":25},"\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",[34,9,122],{"_path":217,"title":218,"description":219,"date":220,"tags":221,"readingTime":35},"\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",[73,222,59],"co-op",{"_path":224,"title":225,"description":226,"date":227,"tags":228,"readingTime":25},"\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",[95,42,9],{"_path":230,"title":231,"description":232,"date":233,"tags":234,"readingTime":25},"\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",[187,188,34],{"_path":236,"title":237,"description":238,"date":239,"tags":240,"readingTime":25},"\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",[11,9,44],{"_path":242,"title":243,"description":244,"date":245,"tags":246,"readingTime":25},"\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",[102,122,247],"maintainability",{"_path":249,"title":250,"description":251,"date":252,"tags":253,"readingTime":25},"\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",[73,254,11],"game-design",{"_path":256,"title":257,"description":258,"date":259,"tags":260,"readingTime":25},"\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",[11,261,44],"writing",{"_path":263,"title":264,"description":265,"date":266,"tags":267,"readingTime":25},"\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",[268,269,59],"documentation","open-source",{"_path":271,"title":272,"description":273,"date":274,"tags":275,"readingTime":25},"\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",[95,9,276],"architecture",1785363949981]