[{"data":1,"prerenderedAt":247},["ShallowReactive",2],{"sanity-LevEgKvCUg3Ul3GKyhTsfZIfaGswLQ-qsmdjo9-lWe4":3,"sanity-sPAMqIrhiiDxHrRUyDNjyFmprG9geqGr9de4KmoEOgU":42},{"data":4},{"_createdAt":5,"_id":6,"_rev":7,"_type":6,"_updatedAt":8,"acceptAllLink":9,"acceptNecessaryLink":18,"bannerColorSource":23,"bannerDescription":24,"bannerTitle":25,"categories":26,"declineAllLink":27,"managePreferencesLink":31,"modalColorSource":23,"modalTitle":35,"preferencesIconColorSource":23,"provider":36,"savePreferencesLink":37,"thirdPartyEmbedCode":26,"title":41},"2026-08-29T18:46:18Z","cookieSettings","C1Syut0iGaGX58H2hZvChS","2026-08-29T18:46:23Z",{"_type":10,"style":11,"title":16,"type":17},"link",{"_type":12,"stylePreset":13},"link.style",{"_ref":14,"_type":15},"link-style-primary","reference","Accept all","button",{"_type":10,"style":19,"title":22,"type":17},{"_type":12,"stylePreset":20},{"_ref":21,"_type":15},"link-style-tertiary-outline","Accept necessary","inherit","We use cookies to enhance your browsing experience and analyse our traffic. Please choose your cookie preferences below.","We use cookies",null,{"_type":10,"style":28,"title":30,"type":17},{"_type":12,"stylePreset":29},{"_ref":21,"_type":15},"Decline all",{"_type":10,"style":32,"title":34,"type":17},{"_type":12,"stylePreset":33},{"_ref":21,"_type":15},"Manage preferences","Cookie Preferences","custom",{"_type":10,"style":38,"title":40,"type":17},{"_type":12,"stylePreset":39},{"_ref":14,"_type":15},"Save preferences","Cookie Settings",{"data":43,"sourceMap":-1},[44],{"_createdAt":5,"_id":45,"_rev":7,"_type":46,"_updatedAt":8,"authorProfile":47,"authorRef":59,"body":61,"categories":158,"excerpt":169,"footer":26,"layout":170,"mainImage":174,"navigation":26,"publishedAt":178,"readTime":179,"relatedPosts":180,"routeOptions":237,"seo":26,"showOnWebsite":239,"slug":242,"tags":243,"title":246},"post-dark-mode-axis","post",{"avatar":48,"bio":53,"name":54,"role":55,"sameAs":56},{"_type":49,"alt":50,"asset":51},"image","Portrait of Priya Raman",{"_ref":52,"_type":15},"image-cfc7cc28fea565890fb3a5986f7d299c7704a12a-512x512-jpg","Priya works on the seam between design tokens and the components that consume them, and is unreasonably interested in how colour behaves when someone else picks it.","Priya Raman","Design Systems Engineer",[57,58],"https:\u002F\u002Fexample.com\u002Fpriya-raman","https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fexample-priya-raman",{"_ref":60,"_type":15},"author-priya-raman",[62,73,82,104,112,117,125,133,142,150],{"_key":63,"_type":64,"children":65,"markDefs":71,"style":72},"b0109","block",[66],{"_key":67,"_type":68,"marks":69,"text":70},"s0110","span",[],"A prerendered page is written before anyone has asked for it. It has no request, no headers, and no visitor — so it cannot know whether that visitor's device prefers a dark interface.",[],"normal",{"_key":74,"_type":64,"children":75,"markDefs":80,"style":81},"b0111",[76],{"_key":77,"_type":68,"marks":78,"text":79},"s0112",[],"The invariant that makes it work",[],"h2",{"_key":83,"_type":64,"children":84,"markDefs":99,"style":72},"b0116",[85,89,95],{"_key":86,"_type":68,"marks":87,"text":88},"s0113",[],"There is exactly one rule, and everything else follows from it: ",{"_key":90,"_type":68,"marks":91,"text":94},"s0114",[92,93],"strong","ann0108","JavaScript picks the variable name, CSS picks the value",{"_key":96,"_type":68,"marks":97,"text":98},"s0115",[],". The name depends only on page data, which the server knows. The value depends on the scheme, which only the browser knows.",[100],{"_key":93,"_type":101,"color":102},"highlight",{"_ref":103,"_type":15},"color-primary",{"_key":105,"_type":64,"children":106,"markDefs":111,"style":72},"b0117",[107],{"_key":108,"_type":68,"marks":109,"text":110},"s0118",[],"Which means nothing scheme-dependent may appear in rendered markup — no hex, no class, no attribute, no inline style. Get that wrong and the page paints correctly, then flips the moment it hydrates.",[],{"_key":113,"_type":49,"alt":114,"asset":115,"markDefs":26},"img0119","Two colour ramps, light and dark, sharing one set of variable names",{"_ref":116,"_type":15},"image-7e494283a767857841bff28399fa67e54c766791-1200x675-jpg",{"_key":118,"_type":64,"children":119,"markDefs":124,"style":81},"b0120",[120],{"_key":121,"_type":68,"marks":122,"text":123},"s0121",[],"Derive the colours once, at build time",[],{"_key":126,"_type":64,"children":127,"markDefs":132,"style":72},"b0122",[128],{"_key":129,"_type":68,"marks":130,"text":131},"s0123",[],"A theme is a background, a text colour and an accent — plus optional dark counterparts. Everything else (hover states, borders, surfaces, gradient stops, the readable ink on top of an accent) is arithmetic, and arithmetic done during render is arithmetic done on every page view for a result that never changes.",[],{"_key":134,"_type":64,"children":135,"markDefs":140,"style":141},"b0124",[136],{"_key":137,"_type":68,"marks":138,"text":139},"s0125",[],"An empty dark slot reuses its light colour, so a theme with no dark colours looks identical in both schemes — and every \"does this theme have an accent?\" branch stays scheme-independent.",[],"blockquote",{"_key":143,"_type":64,"children":144,"markDefs":149,"style":81},"b0126",[145],{"_key":146,"_type":68,"marks":147,"text":148},"s0127",[],"Three states, not two",[],{"_key":151,"_type":64,"children":152,"markDefs":157,"style":72},"b0128",[153],{"_key":154,"_type":68,"marks":155,"text":156},"s0129",[],"A toggle that flips between light and dark can never get back to \"follow my device\". The visitor's standing choice and the currently painted scheme are two different questions, and a control that offers \"revert to system\" has to be able to tell them apart.",[],[159,165],{"_createdAt":5,"_id":160,"_rev":161,"_type":162,"_updatedAt":5,"slug":163,"title":164},"category-design-systems","2d20vp0kUSy7uZjqDieLmb","category","design-systems","Design Systems",{"_createdAt":5,"_id":166,"_rev":161,"_type":162,"_updatedAt":5,"slug":167,"title":168},"category-performance","performance","Performance","The server cannot know which colour scheme a visitor prefers. Anything you render that depends on it will hydrate differently than it painted.",{"_type":171,"layout":172,"pageDesign":173},"post.layout.options","default","design1",{"_type":49,"alt":175,"asset":176},"Abstract indigo gradient split between light and dark",{"_ref":177,"_type":15},"image-96db9eded5277118c54a29e63593a6d24a382797-1600x900-jpg","2026-07-09T09:00:00.000Z",1,[181,203,221],{"_id":182,"authorProfile":183,"categories":190,"excerpt":195,"mainImage":196,"publishedAt":200,"readTime":179,"slug":201,"title":202},"post-design-tokens",{"avatar":184,"name":188,"role":189},{"_type":49,"alt":185,"asset":186},"Portrait of Avery Lin",{"_ref":187,"_type":15},"image-7d9d32f0d8cbaa44274ac31144c4acd3b6256c53-512x512-jpg","Avery Lin","Content Architect",[191,192],{"slug":163,"title":164},{"slug":193,"title":194},"content-modelling","Content Modelling","When an editor picks a brand colour, half a dozen derived values have to stay legible. Here is how to derive them once instead of hard-coding them everywhere.",{"_type":49,"alt":197,"asset":198},"Abstract magenta gradient representing a derived colour palette",{"_ref":199,"_type":15},"image-c96efd6b55a465dcb615ad80af90714207b25dbc-1600x900-jpg","2026-06-21T09:00:00.000Z","theme-colours-belong-to-the-content","Theme colours belong to the content, not the stylesheet",{"_id":204,"authorProfile":205,"categories":208,"excerpt":213,"mainImage":214,"publishedAt":218,"readTime":179,"slug":219,"title":220},"post-scaffolding",{"avatar":206,"name":54,"role":55},{"_type":49,"alt":50,"asset":207},{"_ref":52,"_type":15},[209,212],{"slug":210,"title":211},"developer-experience","Developer Experience",{"slug":163,"title":164},"Any component that needs six files in five directories will be built wrong by hand eventually. Generating it is cheaper than writing the guide twice.",{"_type":49,"alt":215,"asset":216},"Abstract green gradient suggesting generated structure",{"_ref":217,"_type":15},"image-d52abf1b9836ea87c4978039efb609149c6c90de-1600x900-jpg","2026-04-08T09:00:00.000Z","a-scaffolder-is-documentation-that-runs","A scaffolder is documentation that runs",{"_id":222,"authorProfile":223,"categories":226,"excerpt":229,"mainImage":230,"publishedAt":234,"readTime":179,"slug":235,"title":236},"post-type-scale",{"avatar":224,"name":54,"role":55},{"_type":49,"alt":50,"asset":225},{"_ref":52,"_type":15},[227,228],{"slug":163,"title":164},{"slug":193,"title":194},"Every scale looks balanced against three words of placeholder. The test is a heading an editor actually wrote, on a phone, in two languages.",{"_type":49,"alt":231,"asset":232},"Abstract amber gradient suggesting stepped type sizes",{"_ref":233,"_type":15},"image-aa3a1dd55ed9709d36bafe8f5715c90b8906166f-1600x900-jpg","2026-02-24T09:00:00.000Z","a-type-scale-has-to-survive-real-content","A type scale has to survive real content",{"_type":238,"includeInSitemap":239,"slug":240},"page.routing.options",true,{"_type":241,"current":242},"slug","dark-mode-is-a-second-axis",[164,244,245],"Theming","Hydration","Dark mode is a second axis, not a second stylesheet",1788029817766]