[{"data":1,"prerenderedAt":2841},["ShallowReactive",2],{"page-\u002Fcss-only-micro-interactions-animations\u002Fcolor-and-theme-transitions":3,"content-all-pages":2275,"live-demo:color-status-badges":2835},{"id":4,"title":5,"body":6,"description":2255,"extension":2256,"meta":2257,"navigation":1347,"path":2271,"seo":2272,"stem":2273,"__hash__":2274},"content\u002Fcss-only-micro-interactions-animations\u002Fcolor-and-theme-transitions\u002Findex.md","Color & Theme Transitions: Colour as Part of the Motion System",{"type":7,"value":8,"toc":2231},"minimark",[9,13,28,34,67,70,75,111,125,237,260,265,268,324,344,347,350,352,356,524,526,530,534,642,648,835,839,1007,1014,1018,1070,1074,1117,1120,1122,1126,1129,1133,1670,1676,1678,1682,1691,1700,1706,1712,1725,1728,1732,1739,1746,1756,1760,1772,1782,1788,1803,1805,1809,1821,1827,1833,1839,1845,1847,1851,1904,1906,1910,2015,2023,2025,2029,2123,2125,2129,2160,2166,2172,2178,2187,2191,2227],[10,11,5],"h1",{"id":12},"color-theme-transitions-colour-as-part-of-the-motion-system",[14,15,16,17,21,22,27],"p",{},"Colour changes are the most frequent animations in any interface. Every hover, focus, press, selection, validation state and theme toggle changes a colour, often many times a minute. They are also the animations most often handled ad hoc: a hard-coded darker hex here, a ",[18,19,20],"code",{},"transition: all"," there, a dark theme maintained as a second copy of every token. This guide, part of ",[23,24,26],"a",{"href":25},"\u002Fcss-only-micro-interactions-animations\u002F","CSS-Only Micro-Interactions & Animations",", treats colour as a system: states derived from tokens instead of listed, themes expressed as pairs instead of copies, gradients animated through their typed inputs, and theme switches that feel deliberate without costing frames.",[14,29,30],{},[31,32,33],"strong",{},"Prerequisites — this guide assumes you can already:",[35,36,37,46,49,64],"ul",{},[38,39,40,41,45],"li",{},"Define and use custom properties as design tokens (see ",[23,42,44],{"href":43},"\u002Fcss-only-micro-interactions-animations\u002Fcss-custom-properties-architecture\u002F","CSS Custom Properties Architecture",").",[38,47,48],{},"Write transitions and understand per-state timing.",[38,50,51,52,55,56,59,60,63],{},"Read colour notations beyond hex: ",[18,53,54],{},"rgb()"," with alpha, ",[18,57,58],{},"hsl()",", and ideally ",[18,61,62],{},"oklch()",".",[38,65,66],{},"Check contrast ratios against WCAG 1.4.3 and 1.4.11.",[68,69],"hr",{},[71,72,74],"h2",{"id":73},"the-core-concept-colours-are-values-images-are-not","The Core Concept: Colours Are Values, Images Are Not",[14,76,77,78,81,82,81,85,81,88,81,91,81,94,97,98,101,102,106,107,110],{},"The CSS animation engine interpolates any value it can describe numerically. A colour is three channels plus alpha, so any property that takes a single colour transitions smoothly: ",[18,79,80],{},"color",", ",[18,83,84],{},"background-color",[18,86,87],{},"border-color",[18,89,90],{},"outline-color",[18,92,93],{},"fill",[18,95,96],{},"stroke",", the colour in a ",[18,99,100],{},"box-shadow",". A gradient, by contrast, is an ",[103,104,105],"em",{},"image",", and ",[18,108,109],{},"background-image"," is discrete — it flips rather than blends.",[14,112,113,114,117,118,121,122,63],{},"Two further facts shape everything in this guide. First, colour interpolation happens in a colour space, and the space matters: blending blue to yellow in sRGB passes through grey, while OKLab stays saturated. Current CSS interpolates colour transitions in OKLab by default, and functions such as ",[18,115,116],{},"color-mix()"," and gradients let you choose the space explicitly. Second, custom properties are untyped unless registered, so a token holding a colour cannot itself interpolate unless it is declared with ",[18,119,120],{},"@property"," and ",[18,123,124],{},"syntax: \"\u003Ccolor>\"",[126,127,133,134,133,138,133,142,133,150,133,160,133,166,133,170,133,174,133,178,133,183,133,188,133,192,133,195,133,197,133,200,133,203,133,206,133,213,133,217,133,220,133,224,133,228,133,232],"svg",{"viewBox":128,"role":129,"ariaLabel":130,"xmlns":131,"style":132},"0 0 720 330","img","Map of what can be transitioned directly, what needs a registered property, and which colour space the interpolation uses","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;height:auto;max-width:720px;margin:2rem 0","\n  ",[135,136,137],"title",{},"What interpolates, and how",[139,140,141],"desc",{},"Single-colour properties transition directly and interpolate in OKLab by default. Gradients and background-image do not interpolate; their inputs must be registered custom properties. Unregistered colour tokens flip; registered ones interpolate.",[143,144,149],"text",{"textAnchor":145,"x":146,"y":147,"style":148},"middle","360","26","text-anchor:middle;fill:currentColor;font:700 17px sans-serif","Three kinds of colour value, three behaviours",[151,152],"rect",{"x":153,"y":154,"width":155,"height":156,"rx":157,"fill":158,"opacity":159},"20","52","220","210","10","#7aa2ff","0.3",[143,161,165],{"textAnchor":145,"x":162,"y":163,"style":164},"130","80","text-anchor:middle;fill:currentColor;font:700 13px sans-serif","Single colours",[143,167,84],{"textAnchor":145,"x":162,"y":168,"style":169},"108","text-anchor:middle;fill:currentColor;font:12px ui-monospace,monospace",[143,171,173],{"textAnchor":145,"x":162,"y":172,"style":169},"128","color, border-color",[143,175,177],{"textAnchor":145,"x":162,"y":176,"style":169},"148","fill, stroke",[143,179,182],{"textAnchor":145,"x":162,"y":180,"style":181},"200","text-anchor:middle;fill:currentColor;font:600 12px sans-serif","transition directly",[143,184,187],{"textAnchor":145,"x":162,"y":185,"style":186},"222","text-anchor:middle;fill:currentColor;font:12px sans-serif","OKLab midpoints",[151,189],{"x":190,"y":154,"width":155,"height":156,"rx":157,"fill":158,"opacity":191},"250","0.14",[143,193,194],{"textAnchor":145,"x":146,"y":163,"style":164},"Gradients",[143,196,109],{"textAnchor":145,"x":146,"y":168,"style":169},[143,198,199],{"textAnchor":145,"x":146,"y":172,"style":169},"mask-image",[143,201,202],{"textAnchor":145,"x":146,"y":180,"style":181},"flip, never blend",[143,204,205],{"textAnchor":145,"x":146,"y":185,"style":186},"animate inputs instead",[151,207],{"x":208,"y":154,"width":155,"height":156,"rx":157,"fill":209,"stroke":210,"strokeWidth":211,"opacity":212},"480","none","currentColor","1.5","0.6",[143,214,216],{"textAnchor":145,"x":215,"y":163,"style":164},"590","Colour tokens",[143,218,219],{"textAnchor":145,"x":215,"y":168,"style":169},"--brand: #4f46e5",[143,221,223],{"textAnchor":145,"x":215,"y":222,"style":186},"150","unregistered: flips",[143,225,227],{"textAnchor":145,"x":215,"y":226,"style":186},"172","registered \u003Ccolor>:",[143,229,231],{"textAnchor":145,"x":215,"y":230,"style":186},"192","interpolates",[143,233,236],{"textAnchor":145,"x":146,"y":234,"style":235},"304","text-anchor:middle;fill:currentColor;font:12px sans-serif;opacity:0.8","Most colour work animates the first column; the other two need a deliberate technique.",[14,238,239,240,244,245,249,250,254,255,259],{},"The four guides in this section each take one part of the system. ",[23,241,243],{"href":242},"\u002Fcss-only-micro-interactions-animations\u002Fcolor-and-theme-transitions\u002Fcolor-mix-hover-states\u002F","Hover and Active States With color-mix()"," derives interaction colours from a single token. ",[23,246,248],{"href":247},"\u002Fcss-only-micro-interactions-animations\u002Fcolor-and-theme-transitions\u002Flight-dark-function-for-themes\u002F","Light and Dark Themes With light-dark()"," pairs theme colours in one declaration. ",[23,251,253],{"href":252},"\u002Fcss-only-micro-interactions-animations\u002Fcolor-and-theme-transitions\u002Fanimating-gradients\u002F","Animating CSS Gradients Smoothly"," makes the non-interpolable interpolate. ",[23,256,258],{"href":257},"\u002Fcss-only-micro-interactions-animations\u002Fcolor-and-theme-transitions\u002Fsmooth-theme-switching-transitions\u002F","Smooth Light\u002FDark Theme Switch Transitions"," animates the moment the whole palette changes.",[261,262,264],"h3",{"id":263},"a-three-layer-colour-architecture","A three-layer colour architecture",[14,266,267],{},"Colour systems that stay maintainable tend to have three layers, and each technique in this section lives in one of them.",[126,269,133,272,133,275,133,278,133,281,133,286,133,290,133,294,133,297,133,301,133,305,133,309,133,313,133,317,133,321],{"viewBox":270,"role":129,"ariaLabel":271,"xmlns":131,"style":132},"0 0 720 300","Three layers: palette values, theme role tokens using light-dark, and component state tokens using color-mix, each referencing the layer below",[135,273,274],{},"Palette, roles and states",[139,276,277],{},"The bottom layer holds raw palette values. The middle layer maps roles such as surface and text to palette pairs with light-dark. The top layer derives component states such as hover and active with color-mix from role tokens. Components reference only the top two layers.",[143,279,280],{"textAnchor":145,"x":146,"y":147,"style":148},"Each layer references only the one below",[151,282],{"x":283,"y":156,"width":284,"height":285,"rx":157,"fill":209,"stroke":210,"strokeWidth":211,"opacity":212},"60","600","56",[143,287,289],{"textAnchor":145,"x":146,"y":288,"style":164},"236","Palette",[143,291,293],{"textAnchor":145,"x":146,"y":292,"style":169},"256","--indigo-600: #4f46e5 · --slate-900: #0f172a",[151,295],{"x":283,"y":162,"width":284,"height":285,"rx":157,"fill":158,"opacity":296},"0.18",[143,298,300],{"textAnchor":145,"x":146,"y":299,"style":164},"156","Theme roles",[143,302,304],{"textAnchor":145,"x":146,"y":303,"style":169},"176","--brand: light-dark(var(--indigo-600), var(--indigo-300))",[151,306],{"x":283,"y":307,"width":284,"height":285,"rx":157,"fill":158,"opacity":308},"50","0.34",[143,310,312],{"textAnchor":145,"x":146,"y":311,"style":164},"76","Component states",[143,314,316],{"textAnchor":145,"x":146,"y":315,"style":169},"96","--brand-hover: color-mix(in oklch, var(--brand), black 12%)",[318,319],"line",{"x1":146,"y1":320,"x2":146,"y2":162,"stroke":210,"strokeWidth":211,"opacity":212},"106",[318,322],{"x1":146,"y1":323,"x2":146,"y2":156,"stroke":210,"strokeWidth":211,"opacity":212},"186",[14,325,326,327,330,331,333,334,337,338,340,341,343],{},"The ",[31,328,329],{},"palette"," is raw values with no meaning — named hues and steps. ",[31,332,300],{}," give meaning and pair light and dark values with ",[18,335,336],{},"light-dark()",". ",[31,339,312],{}," derive hover, active, disabled and focus shades from roles with ",[18,342,116],{},". Components reference roles and states, never the palette directly, so no component needs to know which theme is active. With that structure, a rebrand edits the palette, a theme tweak edits one role, and a new component variant sets one role — and every derived state follows.",[14,345,346],{},"The layering also answers a question that otherwise causes endless debate: where does a new colour go? A colour with no meaning yet is a palette entry. A colour that means something across the product — \"the colour of destructive actions\" — is a role. A colour that exists only as a variation of another in a particular interaction — \"the danger button while pressed\" — is a derived state, and should be computed rather than stored. Teams that apply this test consistently find their token count stays small even as the product grows, because most apparent new colours turn out to be derivations of existing roles.",[14,348,349],{},"It also keeps animation predictable. Transitions always run between two computed colours, so whether a hover transition starts from a palette value, a role or a mix makes no difference to the browser. What changes is how many places must be edited to adjust it — ideally exactly one.",[68,351],{},[71,353,355],{"id":354},"syntax-and-parameters","Syntax and Parameters",[357,358,359,375],"table",{},[360,361,362],"thead",{},[363,364,365,369,372],"tr",{},[366,367,368],"th",{},"Feature",[366,370,371],{},"Syntax",[366,373,374],{},"Notes",[376,377,378,406,423,447,463,480,494,509],"tbody",{},[363,379,380,385,390],{},[381,382,383],"td",{},[18,384,116],{},[381,386,387],{},[18,388,389],{},"color-mix(in \u003Cspace>, \u003Ccolor> \u003Cpct>?, \u003Ccolor> \u003Cpct>?)",[381,391,392,393,81,396,81,399,81,402,405],{},"Spaces include ",[18,394,395],{},"srgb",[18,397,398],{},"oklab",[18,400,401],{},"oklch",[18,403,404],{},"hsl","; percentages default to 50%",[363,407,408,412,417],{},[381,409,410],{},[18,411,336],{},[381,413,414],{},[18,415,416],{},"light-dark(\u003Clight-color>, \u003Cdark-color>)",[381,418,419,420],{},"Chosen by the element's used ",[18,421,422],{},"color-scheme",[363,424,425,429,444],{},[381,426,427],{},[18,428,422],{},[381,430,431,434,435,434,438,434,441],{},[18,432,433],{},"normal"," | ",[18,436,437],{},"light",[18,439,440],{},"dark",[18,442,443],{},"light dark",[381,445,446],{},"Inherited; also themes form controls and scrollbars",[363,448,449,452,457],{},[381,450,451],{},"Gradient interpolation",[381,453,454],{},[18,455,456],{},"linear-gradient(\u003Cdir> in \u003Cspace> \u003Chue-method>?, …)",[381,458,459,460],{},"e.g. ",[18,461,462],{},"in oklch longer hue",[363,464,465,470,474],{},[381,466,467,469],{},[18,468,120],{}," for colours",[381,471,472],{},[18,473,124],{},[381,475,476,477,479],{},"Makes a token interpolable; freezes ",[18,478,336],{}," at declaration",[363,481,482,486,491],{},[381,483,484],{},[18,485,62],{},[381,487,488],{},[18,489,490],{},"oklch(L C H \u002F A)",[381,492,493],{},"Perceptual lightness, chroma, hue",[363,495,496,501,506],{},[381,497,498],{},[18,499,500],{},"forced-colors",[381,502,503],{},[18,504,505],{},"@media (forced-colors: active)",[381,507,508],{},"System palette overrides author colours",[363,510,511,516,521],{},[381,512,513],{},[18,514,515],{},"prefers-contrast",[381,517,518],{},[18,519,520],{},"@media (prefers-contrast: more)",[381,522,523],{},"Stronger tokens for users who need them",[68,525],{},[71,527,529],{"id":528},"step-by-step-implementation-a-themed-stateful-component-library","Step-by-Step Implementation: A Themed, Stateful Component Library",[261,531,533],{"id":532},"step-1-palette-values","Step 1: palette values",[535,536,541],"pre",{"className":537,"code":538,"language":539,"meta":540,"style":540},"language-css shiki shiki-themes github-light-high-contrast github-dark-high-contrast",":root {\n  --indigo-300: #a5b4fc;\n  --indigo-600: #4f46e5;\n  --slate-50: #f8fafc;\n  --slate-900: #0f172a;\n  --slate-200: #e2e8f0;\n  --slate-700: #334155;\n}\n","css","",[18,542,543,555,571,584,597,610,623,636],{"__ignoreMap":540},[544,545,547,551],"span",{"class":318,"line":546},1,[544,548,550],{"class":549},"s-5SL",":root",[544,552,554],{"class":553},"suds8"," {\n",[544,556,558,562,565,568],{"class":318,"line":557},2,[544,559,561],{"class":560},"soyes","  --indigo-300",[544,563,564],{"class":553},": ",[544,566,567],{"class":549},"#a5b4fc",[544,569,570],{"class":553},";\n",[544,572,574,577,579,582],{"class":318,"line":573},3,[544,575,576],{"class":560},"  --indigo-600",[544,578,564],{"class":553},[544,580,581],{"class":549},"#4f46e5",[544,583,570],{"class":553},[544,585,587,590,592,595],{"class":318,"line":586},4,[544,588,589],{"class":560},"  --slate-50",[544,591,564],{"class":553},[544,593,594],{"class":549},"#f8fafc",[544,596,570],{"class":553},[544,598,600,603,605,608],{"class":318,"line":599},5,[544,601,602],{"class":560},"  --slate-900",[544,604,564],{"class":553},[544,606,607],{"class":549},"#0f172a",[544,609,570],{"class":553},[544,611,613,616,618,621],{"class":318,"line":612},6,[544,614,615],{"class":560},"  --slate-200",[544,617,564],{"class":553},[544,619,620],{"class":549},"#e2e8f0",[544,622,570],{"class":553},[544,624,626,629,631,634],{"class":318,"line":625},7,[544,627,628],{"class":560},"  --slate-700",[544,630,564],{"class":553},[544,632,633],{"class":549},"#334155",[544,635,570],{"class":553},[544,637,639],{"class":318,"line":638},8,[544,640,641],{"class":553},"}\n",[261,643,645,646],{"id":644},"step-2-theme-roles-with-light-dark","Step 2: theme roles with ",[18,647,336],{},[535,649,651],{"className":537,"code":650,"language":539,"meta":540,"style":540},":root {\n  color-scheme: light dark;\n  --surface: light-dark(var(--slate-50), var(--slate-900));\n  --text: light-dark(var(--slate-900), var(--slate-200));\n  --border: light-dark(var(--slate-200), var(--slate-700));\n  --brand: light-dark(var(--indigo-600), var(--indigo-300));\n}\n:root[data-theme=\"light\"] { color-scheme: light; }\n:root[data-theme=\"dark\"]  { color-scheme: dark; }\n",[18,652,653,659,673,703,727,751,776,780,810],{"__ignoreMap":540},[544,654,655,657],{"class":318,"line":546},[544,656,550],{"class":549},[544,658,554],{"class":553},[544,660,661,664,666,668,671],{"class":318,"line":557},[544,662,663],{"class":549},"  color-scheme",[544,665,564],{"class":553},[544,667,437],{"class":549},[544,669,670],{"class":549}," dark",[544,672,570],{"class":553},[544,674,675,678,681,684,687,690,693,695,697,700],{"class":318,"line":573},[544,676,677],{"class":560},"  --surface",[544,679,680],{"class":553},": light-dark(",[544,682,683],{"class":549},"var",[544,685,686],{"class":553},"(",[544,688,689],{"class":560},"--slate-50",[544,691,692],{"class":553},"), ",[544,694,683],{"class":549},[544,696,686],{"class":553},[544,698,699],{"class":560},"--slate-900",[544,701,702],{"class":553},"));\n",[544,704,705,708,710,712,714,716,718,720,722,725],{"class":318,"line":586},[544,706,707],{"class":560},"  --text",[544,709,680],{"class":553},[544,711,683],{"class":549},[544,713,686],{"class":553},[544,715,699],{"class":560},[544,717,692],{"class":553},[544,719,683],{"class":549},[544,721,686],{"class":553},[544,723,724],{"class":560},"--slate-200",[544,726,702],{"class":553},[544,728,729,732,734,736,738,740,742,744,746,749],{"class":318,"line":599},[544,730,731],{"class":560},"  --border",[544,733,680],{"class":553},[544,735,683],{"class":549},[544,737,686],{"class":553},[544,739,724],{"class":560},[544,741,692],{"class":553},[544,743,683],{"class":549},[544,745,686],{"class":553},[544,747,748],{"class":560},"--slate-700",[544,750,702],{"class":553},[544,752,753,756,758,760,762,765,767,769,771,774],{"class":318,"line":612},[544,754,755],{"class":560},"  --brand",[544,757,680],{"class":553},[544,759,683],{"class":549},[544,761,686],{"class":553},[544,763,764],{"class":560},"--indigo-600",[544,766,692],{"class":553},[544,768,683],{"class":549},[544,770,686],{"class":553},[544,772,773],{"class":560},"--indigo-300",[544,775,702],{"class":553},[544,777,778],{"class":318,"line":625},[544,779,641],{"class":553},[544,781,782,784,787,790,794,798,801,803,805,807],{"class":318,"line":638},[544,783,550],{"class":549},[544,785,786],{"class":553},"[",[544,788,789],{"class":549},"data-theme",[544,791,793],{"class":792},"sHUrx","=",[544,795,797],{"class":796},"sT6z2","\"light\"",[544,799,800],{"class":553},"] { ",[544,802,422],{"class":549},[544,804,564],{"class":553},[544,806,437],{"class":549},[544,808,809],{"class":553},"; }\n",[544,811,813,815,817,819,821,824,827,829,831,833],{"class":318,"line":812},9,[544,814,550],{"class":549},[544,816,786],{"class":553},[544,818,789],{"class":549},[544,820,793],{"class":792},[544,822,823],{"class":796},"\"dark\"",[544,825,826],{"class":553},"]  { ",[544,828,422],{"class":549},[544,830,564],{"class":553},[544,832,440],{"class":549},[544,834,809],{"class":553},[261,836,838],{"id":837},"step-3-derived-states-per-component","Step 3: derived states per component",[535,840,842],{"className":537,"code":841,"language":539,"meta":540,"style":540},".btn {\n  --btn-bg: var(--brand);\n  --btn-bg-hover: color-mix(in oklch, var(--btn-bg), var(--text) 12%);\n  --btn-fg: var(--surface);\n  background-color: var(--btn-bg);\n  color: var(--btn-fg);\n  transition: background-color 140ms ease;\n}\n.btn:hover { background-color: var(--btn-bg-hover); }\n",[18,843,844,851,868,915,931,946,962,981,985],{"__ignoreMap":540},[544,845,846,849],{"class":318,"line":546},[544,847,848],{"class":549},".btn",[544,850,554],{"class":553},[544,852,853,856,858,860,862,865],{"class":318,"line":557},[544,854,855],{"class":560},"  --btn-bg",[544,857,564],{"class":553},[544,859,683],{"class":549},[544,861,686],{"class":553},[544,863,864],{"class":560},"--brand",[544,866,867],{"class":553},");\n",[544,869,870,873,875,878,880,883,886,888,890,892,895,897,899,901,904,907,910,913],{"class":318,"line":573},[544,871,872],{"class":560},"  --btn-bg-hover",[544,874,564],{"class":553},[544,876,877],{"class":549},"color-mix",[544,879,686],{"class":553},[544,881,882],{"class":560},"in",[544,884,885],{"class":560}," oklch",[544,887,81],{"class":553},[544,889,683],{"class":549},[544,891,686],{"class":553},[544,893,894],{"class":560},"--btn-bg",[544,896,692],{"class":553},[544,898,683],{"class":549},[544,900,686],{"class":553},[544,902,903],{"class":560},"--text",[544,905,906],{"class":553},") ",[544,908,909],{"class":549},"12",[544,911,912],{"class":792},"%",[544,914,867],{"class":553},[544,916,917,920,922,924,926,929],{"class":318,"line":586},[544,918,919],{"class":560},"  --btn-fg",[544,921,564],{"class":553},[544,923,683],{"class":549},[544,925,686],{"class":553},[544,927,928],{"class":560},"--surface",[544,930,867],{"class":553},[544,932,933,936,938,940,942,944],{"class":318,"line":599},[544,934,935],{"class":549},"  background-color",[544,937,564],{"class":553},[544,939,683],{"class":549},[544,941,686],{"class":553},[544,943,894],{"class":560},[544,945,867],{"class":553},[544,947,948,951,953,955,957,960],{"class":318,"line":612},[544,949,950],{"class":549},"  color",[544,952,564],{"class":553},[544,954,683],{"class":549},[544,956,686],{"class":553},[544,958,959],{"class":560},"--btn-fg",[544,961,867],{"class":553},[544,963,964,967,970,973,976,979],{"class":318,"line":625},[544,965,966],{"class":549},"  transition",[544,968,969],{"class":553},": background-color ",[544,971,972],{"class":549},"140",[544,974,975],{"class":792},"ms",[544,977,978],{"class":549}," ease",[544,980,570],{"class":553},[544,982,983],{"class":318,"line":638},[544,984,641],{"class":553},[544,986,987,990,993,995,997,999,1001,1004],{"class":318,"line":812},[544,988,989],{"class":549},".btn:hover",[544,991,992],{"class":553}," { ",[544,994,84],{"class":549},[544,996,564],{"class":553},[544,998,683],{"class":549},[544,1000,686],{"class":553},[544,1002,1003],{"class":560},"--btn-bg-hover",[544,1005,1006],{"class":553},"); }\n",[14,1008,1009,1010,1013],{},"Mixing toward ",[18,1011,1012],{},"var(--text)"," instead of plain black makes the hover darken in light mode and lighten in dark mode automatically, because the text role flips with the theme.",[261,1015,1017],{"id":1016},"step-4-stronger-tokens-on-request","Step 4: stronger tokens on request",[535,1019,1021],{"className":537,"code":1020,"language":539,"meta":540,"style":540},"@media (prefers-contrast: more) {\n  :root {\n    --border: light-dark(var(--slate-700), var(--slate-200));\n  }\n}\n",[18,1022,1023,1031,1038,1061,1066],{"__ignoreMap":540},[544,1024,1025,1028],{"class":318,"line":546},[544,1026,1027],{"class":792},"@media",[544,1029,1030],{"class":553}," (prefers-contrast: more) {\n",[544,1032,1033,1036],{"class":318,"line":557},[544,1034,1035],{"class":549},"  :root",[544,1037,554],{"class":553},[544,1039,1040,1043,1045,1047,1049,1051,1053,1055,1057,1059],{"class":318,"line":573},[544,1041,1042],{"class":560},"    --border",[544,1044,680],{"class":553},[544,1046,683],{"class":549},[544,1048,686],{"class":553},[544,1050,748],{"class":560},[544,1052,692],{"class":553},[544,1054,683],{"class":549},[544,1056,686],{"class":553},[544,1058,724],{"class":560},[544,1060,702],{"class":553},[544,1062,1063],{"class":318,"line":586},[544,1064,1065],{"class":553},"  }\n",[544,1067,1068],{"class":318,"line":599},[544,1069,641],{"class":553},[261,1071,1073],{"id":1072},"step-5-a-safety-net-for-forced-colours","Step 5: a safety net for forced colours",[535,1075,1077],{"className":537,"code":1076,"language":539,"meta":540,"style":540},"@media (forced-colors: active) {\n  .btn { border: 1px solid ButtonText; }\n}\n",[18,1078,1079,1086,1113],{"__ignoreMap":540},[544,1080,1081,1083],{"class":318,"line":546},[544,1082,1027],{"class":792},[544,1084,1085],{"class":553}," (forced-colors: active) {\n",[544,1087,1088,1091,1093,1096,1098,1101,1104,1107,1111],{"class":318,"line":557},[544,1089,1090],{"class":549},"  .btn",[544,1092,992],{"class":553},[544,1094,1095],{"class":549},"border",[544,1097,564],{"class":553},[544,1099,1100],{"class":549},"1",[544,1102,1103],{"class":792},"px",[544,1105,1106],{"class":549}," solid",[544,1108,1110],{"class":1109},"sh5d0"," ButtonText",[544,1112,809],{"class":553},[544,1114,1115],{"class":318,"line":573},[544,1116,641],{"class":553},[14,1118,1119],{},"In forced-colours mode the background colours are replaced, so a visible border keeps the button's shape recognisable.",[68,1121],{},[71,1123,1125],{"id":1124},"annotated-production-example-a-status-badge-set","Annotated Production Example: A Status Badge Set",[14,1127,1128],{},"Status badges — success, warning, error, info — are a small system of their own: each needs a background, text and border that work in both themes and meet contrast. With the three-layer approach, each variant sets a single hue.",[1130,1131],"live-demo",{"name":1132},"color-status-badges",[535,1134,1136],{"className":537,"code":1135,"language":539,"meta":540,"style":540},".badge {\n  --hue: 250;                                            \u002F* the only variant input *\u002F\n  --badge-base: oklch(55% 0.16 var(--hue));\n  --badge-bg: light-dark(\n    color-mix(in oklch, var(--badge-base) 14%, white),\n    color-mix(in oklch, var(--badge-base) 30%, black)\n  );\n  --badge-fg: light-dark(\n    color-mix(in oklch, var(--badge-base), black 35%),\n    color-mix(in oklch, var(--badge-base), white 55%)\n  );\n\n  display: inline-flex;\n  align-items: center;\n  gap: 0.35rem;\n  padding: 0.2rem 0.6rem;\n  border: 1px solid color-mix(in oklch, var(--badge-fg) 35%, transparent);\n  border-radius: 999px;\n  background: var(--badge-bg);\n  color: var(--badge-fg);\n  font-size: 0.85rem;\n  transition: background-color 200ms ease, color 200ms ease;\n}\n\n.badge--success { --hue: 150; }\n.badge--warning { --hue: 75; }\n.badge--error   { --hue: 25; }\n.badge--info    { --hue: 250; }\n\n\u002F* A non-colour cue: every badge carries an icon or text label, so the\n   status is readable in forced-colours mode and by colour-blind users. *\u002F\n.badge::before { content: \"●\"; font-size: 0.6em; }\n",[18,1137,1138,1145,1161,1190,1198,1233,1266,1271,1278,1307,1337,1342,1349,1362,1375,1391,1411,1457,1472,1489,1504,1519,1545,1550,1555,1571,1588,1606,1623,1628,1634,1640],{"__ignoreMap":540},[544,1139,1140,1143],{"class":318,"line":546},[544,1141,1142],{"class":549},".badge",[544,1144,554],{"class":553},[544,1146,1147,1150,1152,1154,1157],{"class":318,"line":557},[544,1148,1149],{"class":560},"  --hue",[544,1151,564],{"class":553},[544,1153,190],{"class":549},[544,1155,1156],{"class":553},";                                            ",[544,1158,1160],{"class":1159},"sLBg1","\u002F* the only variant input *\u002F\n",[544,1162,1163,1166,1168,1170,1172,1175,1177,1180,1183,1185,1188],{"class":318,"line":573},[544,1164,1165],{"class":560},"  --badge-base",[544,1167,564],{"class":553},[544,1169,401],{"class":549},[544,1171,686],{"class":553},[544,1173,1174],{"class":549},"55",[544,1176,912],{"class":792},[544,1178,1179],{"class":549}," 0.16",[544,1181,1182],{"class":549}," var",[544,1184,686],{"class":553},[544,1186,1187],{"class":560},"--hue",[544,1189,702],{"class":553},[544,1191,1192,1195],{"class":318,"line":586},[544,1193,1194],{"class":560},"  --badge-bg",[544,1196,1197],{"class":553},": light-dark(\n",[544,1199,1200,1203,1205,1207,1209,1211,1213,1215,1218,1220,1223,1225,1227,1230],{"class":318,"line":599},[544,1201,1202],{"class":549},"    color-mix",[544,1204,686],{"class":553},[544,1206,882],{"class":560},[544,1208,885],{"class":560},[544,1210,81],{"class":553},[544,1212,683],{"class":549},[544,1214,686],{"class":553},[544,1216,1217],{"class":560},"--badge-base",[544,1219,906],{"class":553},[544,1221,1222],{"class":549},"14",[544,1224,912],{"class":792},[544,1226,81],{"class":553},[544,1228,1229],{"class":549},"white",[544,1231,1232],{"class":553},"),\n",[544,1234,1235,1237,1239,1241,1243,1245,1247,1249,1251,1253,1256,1258,1260,1263],{"class":318,"line":612},[544,1236,1202],{"class":549},[544,1238,686],{"class":553},[544,1240,882],{"class":560},[544,1242,885],{"class":560},[544,1244,81],{"class":553},[544,1246,683],{"class":549},[544,1248,686],{"class":553},[544,1250,1217],{"class":560},[544,1252,906],{"class":553},[544,1254,1255],{"class":549},"30",[544,1257,912],{"class":792},[544,1259,81],{"class":553},[544,1261,1262],{"class":549},"black",[544,1264,1265],{"class":553},")\n",[544,1267,1268],{"class":318,"line":625},[544,1269,1270],{"class":553},"  );\n",[544,1272,1273,1276],{"class":318,"line":638},[544,1274,1275],{"class":560},"  --badge-fg",[544,1277,1197],{"class":553},[544,1279,1280,1282,1284,1286,1288,1290,1292,1294,1296,1298,1300,1303,1305],{"class":318,"line":812},[544,1281,1202],{"class":549},[544,1283,686],{"class":553},[544,1285,882],{"class":560},[544,1287,885],{"class":560},[544,1289,81],{"class":553},[544,1291,683],{"class":549},[544,1293,686],{"class":553},[544,1295,1217],{"class":560},[544,1297,692],{"class":553},[544,1299,1262],{"class":549},[544,1301,1302],{"class":549}," 35",[544,1304,912],{"class":792},[544,1306,1232],{"class":553},[544,1308,1310,1312,1314,1316,1318,1320,1322,1324,1326,1328,1330,1333,1335],{"class":318,"line":1309},10,[544,1311,1202],{"class":549},[544,1313,686],{"class":553},[544,1315,882],{"class":560},[544,1317,885],{"class":560},[544,1319,81],{"class":553},[544,1321,683],{"class":549},[544,1323,686],{"class":553},[544,1325,1217],{"class":560},[544,1327,692],{"class":553},[544,1329,1229],{"class":549},[544,1331,1332],{"class":549}," 55",[544,1334,912],{"class":792},[544,1336,1265],{"class":553},[544,1338,1340],{"class":318,"line":1339},11,[544,1341,1270],{"class":553},[544,1343,1345],{"class":318,"line":1344},12,[544,1346,1348],{"emptyLinePlaceholder":1347},true,"\n",[544,1350,1352,1355,1357,1360],{"class":318,"line":1351},13,[544,1353,1354],{"class":549},"  display",[544,1356,564],{"class":553},[544,1358,1359],{"class":549},"inline-flex",[544,1361,570],{"class":553},[544,1363,1365,1368,1370,1373],{"class":318,"line":1364},14,[544,1366,1367],{"class":549},"  align-items",[544,1369,564],{"class":553},[544,1371,1372],{"class":549},"center",[544,1374,570],{"class":553},[544,1376,1378,1381,1383,1386,1389],{"class":318,"line":1377},15,[544,1379,1380],{"class":549},"  gap",[544,1382,564],{"class":553},[544,1384,1385],{"class":549},"0.35",[544,1387,1388],{"class":792},"rem",[544,1390,570],{"class":553},[544,1392,1394,1397,1399,1402,1404,1407,1409],{"class":318,"line":1393},16,[544,1395,1396],{"class":549},"  padding",[544,1398,564],{"class":553},[544,1400,1401],{"class":549},"0.2",[544,1403,1388],{"class":792},[544,1405,1406],{"class":549}," 0.6",[544,1408,1388],{"class":792},[544,1410,570],{"class":553},[544,1412,1414,1417,1419,1421,1423,1425,1428,1430,1432,1434,1436,1438,1440,1443,1445,1448,1450,1452,1455],{"class":318,"line":1413},17,[544,1415,1416],{"class":549},"  border",[544,1418,564],{"class":553},[544,1420,1100],{"class":549},[544,1422,1103],{"class":792},[544,1424,1106],{"class":549},[544,1426,1427],{"class":549}," color-mix",[544,1429,686],{"class":553},[544,1431,882],{"class":560},[544,1433,885],{"class":560},[544,1435,81],{"class":553},[544,1437,683],{"class":549},[544,1439,686],{"class":553},[544,1441,1442],{"class":560},"--badge-fg",[544,1444,906],{"class":553},[544,1446,1447],{"class":549},"35",[544,1449,912],{"class":792},[544,1451,81],{"class":553},[544,1453,1454],{"class":549},"transparent",[544,1456,867],{"class":553},[544,1458,1460,1463,1465,1468,1470],{"class":318,"line":1459},18,[544,1461,1462],{"class":549},"  border-radius",[544,1464,564],{"class":553},[544,1466,1467],{"class":549},"999",[544,1469,1103],{"class":792},[544,1471,570],{"class":553},[544,1473,1475,1478,1480,1482,1484,1487],{"class":318,"line":1474},19,[544,1476,1477],{"class":549},"  background",[544,1479,564],{"class":553},[544,1481,683],{"class":549},[544,1483,686],{"class":553},[544,1485,1486],{"class":560},"--badge-bg",[544,1488,867],{"class":553},[544,1490,1492,1494,1496,1498,1500,1502],{"class":318,"line":1491},20,[544,1493,950],{"class":549},[544,1495,564],{"class":553},[544,1497,683],{"class":549},[544,1499,686],{"class":553},[544,1501,1442],{"class":560},[544,1503,867],{"class":553},[544,1505,1507,1510,1512,1515,1517],{"class":318,"line":1506},21,[544,1508,1509],{"class":549},"  font-size",[544,1511,564],{"class":553},[544,1513,1514],{"class":549},"0.85",[544,1516,1388],{"class":792},[544,1518,570],{"class":553},[544,1520,1522,1524,1526,1528,1530,1532,1534,1536,1539,1541,1543],{"class":318,"line":1521},22,[544,1523,966],{"class":549},[544,1525,969],{"class":553},[544,1527,180],{"class":549},[544,1529,975],{"class":792},[544,1531,978],{"class":549},[544,1533,81],{"class":553},[544,1535,80],{"class":549},[544,1537,1538],{"class":549}," 200",[544,1540,975],{"class":792},[544,1542,978],{"class":549},[544,1544,570],{"class":553},[544,1546,1548],{"class":318,"line":1547},23,[544,1549,641],{"class":553},[544,1551,1553],{"class":318,"line":1552},24,[544,1554,1348],{"emptyLinePlaceholder":1347},[544,1556,1558,1561,1563,1565,1567,1569],{"class":318,"line":1557},25,[544,1559,1560],{"class":549},".badge--success",[544,1562,992],{"class":553},[544,1564,1187],{"class":560},[544,1566,564],{"class":553},[544,1568,222],{"class":549},[544,1570,809],{"class":553},[544,1572,1574,1577,1579,1581,1583,1586],{"class":318,"line":1573},26,[544,1575,1576],{"class":549},".badge--warning",[544,1578,992],{"class":553},[544,1580,1187],{"class":560},[544,1582,564],{"class":553},[544,1584,1585],{"class":549},"75",[544,1587,809],{"class":553},[544,1589,1591,1594,1597,1599,1601,1604],{"class":318,"line":1590},27,[544,1592,1593],{"class":549},".badge--error",[544,1595,1596],{"class":553},"   { ",[544,1598,1187],{"class":560},[544,1600,564],{"class":553},[544,1602,1603],{"class":549},"25",[544,1605,809],{"class":553},[544,1607,1609,1612,1615,1617,1619,1621],{"class":318,"line":1608},28,[544,1610,1611],{"class":549},".badge--info",[544,1613,1614],{"class":553},"    { ",[544,1616,1187],{"class":560},[544,1618,564],{"class":553},[544,1620,190],{"class":549},[544,1622,809],{"class":553},[544,1624,1626],{"class":318,"line":1625},29,[544,1627,1348],{"emptyLinePlaceholder":1347},[544,1629,1631],{"class":318,"line":1630},30,[544,1632,1633],{"class":1159},"\u002F* A non-colour cue: every badge carries an icon or text label, so the\n",[544,1635,1637],{"class":318,"line":1636},31,[544,1638,1639],{"class":1159},"   status is readable in forced-colours mode and by colour-blind users. *\u002F\n",[544,1641,1643,1646,1648,1651,1653,1656,1659,1662,1664,1666,1668],{"class":318,"line":1642},32,[544,1644,1645],{"class":549},".badge::before",[544,1647,992],{"class":553},[544,1649,1650],{"class":549},"content",[544,1652,564],{"class":553},[544,1654,1655],{"class":796},"\"●\"",[544,1657,1658],{"class":553},"; ",[544,1660,1661],{"class":549},"font-size",[544,1663,564],{"class":553},[544,1665,212],{"class":549},[544,1667,103],{"class":792},[544,1669,809],{"class":553},[14,1671,1672,1673,1675],{},"Each variant is one number, and each derived colour is computed in OKLCH, so the four badges have matching lightness and saturation — a success badge is not accidentally brighter than an error badge. The ",[18,1674,336],{}," wrappers produce a pale tint with dark text in light mode and a deep tint with light text in dark mode. The badge text itself carries the status word, which satisfies WCAG 1.4.1 Use of Color: colour reinforces the status but never carries it alone.",[68,1677],{},[71,1679,1681],{"id":1680},"choosing-a-colour-space-for-ui-work","Choosing a Colour Space for UI Work",[14,1683,1684,1685,1687,1688,1690],{},"Colour spaces used to be a specialist topic. They now matter to every front-end developer, because ",[18,1686,116],{},", gradients and colour transitions all interpolate ",[103,1689,882],{}," a space, and the choice changes what users see.",[14,1692,1693,1696,1697,1699],{},[31,1694,1695],{},"sRGB"," is the space of hex codes and ",[18,1698,54],{},". It is device-oriented: equal numeric steps do not look equal. Mixing or interpolating saturated colours through sRGB tends to produce dull, greyish midpoints, and darkening by a fixed amount affects yellows far more than blues. It remains fine for mixing with transparency, where only alpha changes.",[14,1701,1702,1705],{},[31,1703,1704],{},"HSL"," looks friendly — hue, saturation, lightness — but its lightness is not perceptual. A yellow and a blue at the same HSL lightness look nothing alike in brightness, so a palette built by varying HSL hue at fixed lightness has wildly uneven contrast.",[14,1707,1708,1711],{},[31,1709,1710],{},"OKLab and OKLCH"," are designed so equal steps look equal. OKLCH expresses the same space as lightness, chroma and hue, which makes it the most practical for design tokens: keep lightness fixed and vary hue to get a family of colours with matching visual weight, as the badge example does. For mixing and interpolation, OKLab and OKLCH both avoid muddy midpoints; OKLCH's hue channel additionally allows choosing the direction round the colour wheel.",[14,1713,1714,1715,1717,1718,1720,1721,1724],{},"The recommendation for new work is simple: author palette values in ",[18,1716,62],{},", mix in ",[18,1719,401],{},", and write gradients ",[18,1722,1723],{},"in oklch",". Existing hex palettes can stay as they are, since the interpolation space is independent of how the endpoint colours are written.",[14,1726,1727],{},"One caution: OKLCH can describe colours outside the display's gamut, particularly at high chroma. Browsers map such colours back into gamut, which can shift their appearance slightly. Keep chroma moderate for UI colours — roughly 0.1 to 0.2 — and check results on the devices you support.",[71,1729,1731],{"id":1730},"state-colours-in-dark-mode","State Colours in Dark Mode",[14,1733,1734,1735,1738],{},"Interaction states are where naïve dark themes most often fail. In a light theme, darkening a button on hover makes it more prominent against a pale page. In a dark theme, the same darkening makes the button ",[103,1736,1737],{},"less"," prominent, sinking it into the dark background. The state still changes, but its meaning inverts.",[14,1740,1741,1742,1745],{},"The fix is to derive states relative to the theme rather than to black or white. Mixing toward the text role, as in the step-by-step example, darkens in light mode and lightens in dark mode, because the text colour flips with the theme. Elevation works the same way: in dark themes, raised surfaces conventionally get ",[103,1743,1744],{},"lighter"," rather than casting darker shadows, so a hovered card might mix toward white a little instead of deepening its shadow. Expressing these as derived tokens means the component CSS stays identical across themes while its behaviour adapts.",[14,1747,1748,1749,1751,1752,63],{},"Focus rings deserve special care. A ring colour chosen for contrast against white may disappear against a dark surface. A two-tone ring — a light outline with a dark ",[18,1750,100],{}," halo, or the reverse — stays visible on any background, which is the approach recommended in ",[23,1753,1755],{"href":1754},"\u002Fcss-only-micro-interactions-animations\u002Faccessibility-in-css-animations\u002Fcreating-accessible-focus-indicators\u002F","Creating Accessible Focus Indicators",[71,1757,1759],{"id":1758},"integration-with-adjacent-css","Integration With Adjacent CSS",[14,1761,1762,1765,1766,1768,1769,1771],{},[31,1763,1764],{},"Style queries."," A section can publish a theme token that descendants react to with a container style query — useful for \"inverted\" bands on a page. Combined with ",[18,1767,422],{}," on the section, ",[18,1770,336],{}," tokens inside it resolve to the opposite theme automatically.",[14,1773,1774,1777,1778,1781],{},[31,1775,1776],{},"Cascade layers."," Palette and role tokens belong in an early ",[18,1779,1780],{},"tokens"," layer, derived states alongside components, and forced-colours or high-contrast overrides in a late layer so they reliably win.",[14,1783,1784,1787],{},[31,1785,1786],{},"View transitions."," A theme switch animated with a view transition animates snapshots rather than individual colours, which is the cheapest way to make a whole-page palette change feel smooth.",[14,1789,1790,1795,1796,1798,1799,1802],{},[31,1791,1792,1793,63],{},"SVG and ",[18,1794,210],{}," Inline icons and diagrams that use ",[18,1797,210],{}," inherit text colour and therefore follow the theme and any colour transition on their parent, with no extra rules. Diagrams that need a second colour can read a role token through ",[18,1800,1801],{},"fill: var(--brand)"," in a style attribute or stylesheet, which keeps illustrations on the same palette as the interface around them.",[68,1804],{},[71,1806,1808],{"id":1807},"performance-and-accessibility-notes","Performance and Accessibility Notes",[14,1810,1811,1814,1815,1817,1818,1820],{},[31,1812,1813],{},"Colour changes repaint, not relayout."," Transitioning ",[18,1816,84],{}," or ",[18,1819,80],{}," costs a repaint per frame for the affected elements, which is cheap for individual components. Avoid universal colour transitions, which turn every hover and every theme switch into hundreds of simultaneous repaints.",[14,1822,1823,1826],{},[31,1824,1825],{},"Registered properties repaint too."," Gradient and mix-amount animations that rebuild an image each frame are more expensive than plain colour transitions. Keep them short and on small elements.",[14,1828,1829,1832],{},[31,1830,1831],{},"Contrast must hold in every state and theme."," Derived shades are computed, so verify them once per brand colour and theme. Disabled states are exempt from contrast requirements but should remain legible.",[14,1834,1835,1838],{},[31,1836,1837],{},"Colour is never the only signal."," Status, validity and selection must also be conveyed by text, icons or shape, both for colour-blind users and for forced-colours mode, where author colours disappear.",[14,1840,1841,1844],{},[31,1842,1843],{},"Theme switches honour reduced motion."," Sweeping reveals become short fades; crossfades may stay if they are brief. The inline no-flash script matters for light-sensitive users.",[68,1846],{},[71,1848,1850],{"id":1849},"devtools-debugging-workflow","DevTools Debugging Workflow",[1852,1853,1854,1865,1879,1885,1891],"ol",{},[38,1855,1856,1859,1860,121,1862,1864],{},[31,1857,1858],{},"Inspect computed colours."," The Computed pane shows the resolved value of ",[18,1861,116],{},[18,1863,336],{}," expressions, which confirms what a derived token actually produced.",[38,1866,1867,1870,1871,81,1874,121,1876,1878],{},[31,1868,1869],{},"Emulate schemes."," Chromium's Rendering drawer and Firefox's inspector can emulate ",[18,1872,1873],{},"prefers-color-scheme",[18,1875,515],{},[18,1877,500],{},", so each theme and mode can be checked without changing system settings.",[38,1880,1881,1884],{},[31,1882,1883],{},"Check contrast in place."," Chromium's colour picker shows the contrast ratio against the background beneath the element, with AA and AAA markers; use it on every derived state.",[38,1886,1887,1890],{},[31,1888,1889],{},"Watch for flips."," If a colour transition jumps instead of blending, check whether the animated value is an unregistered custom property or a gradient.",[38,1892,1893,1896,1897,1900,1901,1903],{},[31,1894,1895],{},"Verify nested schemes."," Select an element inside a ",[18,1898,1899],{},"color-scheme: dark"," region and confirm ",[18,1902,336],{}," tokens resolve to their dark values there.",[68,1905],{},[71,1907,1909],{"id":1908},"browser-compatibility","Browser Compatibility",[357,1911,1912,1927],{},[360,1913,1914],{},[363,1915,1916,1918,1921,1924],{},[366,1917,368],{},[366,1919,1920],{},"Chrome \u002F Edge",[366,1922,1923],{},"Firefox",[366,1925,1926],{},"Safari",[376,1928,1929,1944,1957,1972,1985,2000],{},[363,1930,1931,1935,1938,1941],{},[381,1932,1933],{},[18,1934,116],{},[381,1936,1937],{},"111+",[381,1939,1940],{},"113+",[381,1942,1943],{},"16.2+",[363,1945,1946,1950,1953,1955],{},[381,1947,1948],{},[18,1949,336],{},[381,1951,1952],{},"Supported in current versions",[381,1954,1952],{},[381,1956,1952],{},[363,1958,1959,1963,1966,1969],{},[381,1960,1961],{},[18,1962,120],{},[381,1964,1965],{},"85+",[381,1967,1968],{},"128+",[381,1970,1971],{},"16.4+",[363,1973,1974,1977,1979,1982],{},[381,1975,1976],{},"View transitions (theme reveal)",[381,1978,1937],{},[381,1980,1981],{},"144+",[381,1983,1984],{},"18+",[363,1986,1987,1991,1994,1997],{},[381,1988,1989],{},[18,1990,515],{},[381,1992,1993],{},"96+",[381,1995,1996],{},"101+",[381,1998,1999],{},"14.1+",[363,2001,2002,2006,2009,2012],{},[381,2003,2004],{},[18,2005,500],{},[381,2007,2008],{},"89+ \u002F 79+",[381,2010,2011],{},"89+",[381,2013,2014],{},"16+",[14,2016,2017,2018,121,2020,2022],{},"Plain colour transitions are universal. Declare hex fallbacks before ",[18,2019,116],{},[18,2021,336],{}," declarations for older engines.",[68,2024],{},[71,2026,2028],{"id":2027},"common-pitfalls","Common Pitfalls",[357,2030,2031,2044],{},[360,2032,2033],{},[363,2034,2035,2038,2041],{},[366,2036,2037],{},"Pitfall",[366,2039,2040],{},"Cause",[366,2042,2043],{},"Resolution",[376,2045,2046,2059,2072,2091,2108],{},[363,2047,2048,2051,2056],{},[381,2049,2050],{},"Gradient snaps on hover",[381,2052,2053,2055],{},[18,2054,109],{}," is discrete",[381,2057,2058],{},"Animate registered colour or position inputs",[363,2060,2061,2064,2069],{},[381,2062,2063],{},"Hover shade wrong on variants",[381,2065,2066,2067],{},"Derived tokens declared on ",[18,2068,550],{},[381,2070,2071],{},"Declare derived tokens on the component",[363,2073,2074,2079,2085],{},[381,2075,2076,2078],{},[18,2077,336],{}," always light",[381,2080,2081,2082,2084],{},"No ",[18,2083,422],{}," including dark",[381,2086,2087,2090],{},[18,2088,2089],{},"color-scheme: light dark"," on the root",[363,2092,2093,2096,2102],{},[381,2094,2095],{},"Nested dark region ignored",[381,2097,2098,2099],{},"Token registered as ",[18,2100,2101],{},"\u003Ccolor>",[381,2103,2104,2105,2107],{},"Keep ",[18,2106,336],{}," tokens unregistered",[363,2109,2110,2113,2120],{},[381,2111,2112],{},"Theme switch stutters",[381,2114,2115,2116,2119],{},"Universal ",[18,2117,2118],{},"transition"," on colours",[381,2121,2122],{},"Scope to large surfaces or use a view transition",[68,2124],{},[71,2126,2128],{"id":2127},"faq","FAQ",[14,2130,2131,2134,2135,81,2137,81,2139,81,2141,81,2143,81,2146,81,2148,81,2150,2153,2154,2156,2157,2159],{},[31,2132,2133],{},"Which colour properties can be transitioned?","\nAny property whose value is a single colour: ",[18,2136,80],{},[18,2138,84],{},[18,2140,87],{},[18,2142,90],{},[18,2144,2145],{},"text-decoration-color",[18,2147,93],{},[18,2149,96],{},[18,2151,2152],{},"accent-color"," and the colour inside ",[18,2155,100],{},". Gradients and ",[18,2158,109],{}," cannot, unless the gradient is built from registered custom properties.",[14,2161,2162,2165],{},[31,2163,2164],{},"What colour space do colour transitions use?","\nColour transitions and animations interpolate in OKLab by default in current CSS, which keeps midpoints between saturated colours vivid rather than grey. Older implementations interpolated in sRGB, which is why some colour transitions used to look muddy halfway through.",[14,2167,2168,2171],{},[31,2169,2170],{},"Are colour transitions expensive?","\nA colour change requires a repaint of the element, but not layout. Transitioning a few elements' colours is cheap. Transitioning hundreds at once, as a universal theme transition does, can cost frames, so scope colour transitions to the elements that need them.",[14,2173,2174,2177],{},[31,2175,2176],{},"How do colour transitions interact with forced-colors mode?","\nIn forced-colors mode the browser replaces author colours with a small system palette, so colour transitions largely disappear. That is expected. Make sure no state is communicated by colour alone, so the interface still works when colours are forced.",[14,2179,2180,2183,2184,2186],{},[31,2181,2182],{},"Should the theme follow the operating system or a toggle?","\nBoth. Default to the system preference with ",[18,2185,2089],{},", and offer a toggle that overrides it and persists the choice. Apply the stored choice before first paint to avoid a flash of the wrong theme.",[71,2188,2190],{"id":2189},"related","Related",[35,2192,2193,2198,2203,2208,2213,2220],{},[38,2194,2195,2197],{},[23,2196,243],{"href":242}," — derived interaction colours.",[38,2199,2200,2202],{},[23,2201,248],{"href":247}," — paired theme tokens.",[38,2204,2205,2207],{},[23,2206,253],{"href":252}," — animating what does not interpolate.",[38,2209,2210,2212],{},[23,2211,258],{"href":257}," — the switch itself.",[38,2214,2215,2219],{},[23,2216,2218],{"href":2217},"\u002Fcss-only-micro-interactions-animations\u002Fhover-focus-state-design\u002F","Hover & Focus State Design"," — where most colour transitions happen.",[38,2221,2222,2226],{},[23,2223,2225],{"href":2224},"\u002Fmastering-container-queries-responsive-layouts\u002Fstyle-queries-and-container-state\u002Fcontainer-style-query-theming\u002F","Container Style Query Theming"," — themes scoped to sections.",[2228,2229,2230],"style",{},"html pre.shiki code .s-5SL, html code.shiki .s-5SL{--shiki-default:#023B95;--shiki-dark:#91CBFF}html pre.shiki code .suds8, html code.shiki .suds8{--shiki-default:#0E1116;--shiki-dark:#F0F3F6}html pre.shiki code .soyes, html code.shiki .soyes{--shiki-default:#702C00;--shiki-dark:#FFB757}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHUrx, html code.shiki .sHUrx{--shiki-default:#A0111F;--shiki-dark:#FF9492}html pre.shiki code .sT6z2, html code.shiki .sT6z2{--shiki-default:#032563;--shiki-dark:#ADDCFF}html pre.shiki code .sh5d0, html code.shiki .sh5d0{--shiki-default:#6E011A;--shiki-default-font-style:italic;--shiki-dark:#FFB1AF;--shiki-dark-font-style:italic}html pre.shiki code .sLBg1, html code.shiki .sLBg1{--shiki-default:#66707B;--shiki-dark:#BDC4CC}",{"title":540,"searchDepth":557,"depth":557,"links":2232},[2233,2236,2237,2245,2246,2247,2248,2249,2250,2251,2252,2253,2254],{"id":73,"depth":557,"text":74,"children":2234},[2235],{"id":263,"depth":573,"text":264},{"id":354,"depth":557,"text":355},{"id":528,"depth":557,"text":529,"children":2238},[2239,2240,2242,2243,2244],{"id":532,"depth":573,"text":533},{"id":644,"depth":573,"text":2241},"Step 2: theme roles with light-dark()",{"id":837,"depth":573,"text":838},{"id":1016,"depth":573,"text":1017},{"id":1072,"depth":573,"text":1073},{"id":1124,"depth":557,"text":1125},{"id":1680,"depth":557,"text":1681},{"id":1730,"depth":557,"text":1731},{"id":1758,"depth":557,"text":1759},{"id":1807,"depth":557,"text":1808},{"id":1849,"depth":557,"text":1850},{"id":1908,"depth":557,"text":1909},{"id":2027,"depth":557,"text":2028},{"id":2127,"depth":557,"text":2128},{"id":2189,"depth":557,"text":2190},"Colour as a motion system: derived states with color-mix(), light and dark tokens with light-dark(), animatable gradients and accessible theme switching.","md",{"pageTitle":2258,"datePublished":2259,"dateModified":2259,"faq":2260},"Color and Theme Transitions in CSS","2026-09-18",[2261,2263,2265,2267,2269],{"q":2133,"a":2262},"Any property whose value is a single colour: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, accent-color and the colour inside box-shadow. Gradients and background-image cannot, unless the gradient is built from registered custom properties.",{"q":2164,"a":2264},"Colour transitions and animations interpolate in OKLab by default in current CSS, which keeps midpoints between saturated colours vivid rather than grey. Older implementations interpolated in sRGB, which is why some colour transitions used to look muddy halfway through.",{"q":2170,"a":2266},"A colour change requires a repaint of the element, but not layout. Transitioning a few elements' colours is cheap. Transitioning hundreds at once, as a universal theme transition does, can cost frames, so scope colour transitions to the elements that need them.",{"q":2176,"a":2268},"In forced-colors mode the browser replaces author colours with a small system palette, so colour transitions largely disappear. That is expected. Make sure no state is communicated by colour alone, so the interface still works when colours are forced.",{"q":2182,"a":2270},"Both. Default to the system preference with color-scheme: light dark, and offer a toggle that overrides it and persists the choice. Apply the stored choice before first paint to avoid a flash of the wrong theme.","\u002Fcss-only-micro-interactions-animations\u002Fcolor-and-theme-transitions",{"title":5,"description":2255},"css-only-micro-interactions-animations\u002Fcolor-and-theme-transitions\u002Findex","pImXNANb0hOQw9l7FVGCOH4eDqRMq_jxcUnIO4zKDiI",[2276,2279,2282,2285,2288,2291,2294,2297,2300,2303,2306,2309,2312,2315,2318,2321,2324,2327,2328,2331,2334,2337,2340,2343,2346,2349,2352,2355,2358,2361,2364,2367,2370,2373,2376,2379,2382,2385,2388,2391,2394,2397,2400,2403,2406,2409,2412,2415,2418,2421,2424,2427,2430,2433,2436,2439,2442,2445,2448,2451,2454,2457,2460,2463,2466,2469,2472,2475,2478,2481,2484,2487,2490,2493,2496,2499,2502,2505,2508,2511,2514,2517,2520,2523,2526,2529,2532,2535,2538,2541,2544,2547,2550,2553,2556,2559,2562,2565,2568,2571,2574,2577,2580,2583,2586,2589,2592,2595,2598,2601,2604,2607,2610,2613,2616,2619,2622,2625,2628,2631,2634,2637,2640,2643,2646,2649,2652,2655,2658,2661,2664,2667,2670,2673,2676,2679,2682,2685,2688,2691,2694,2697,2700,2703,2706,2709,2712,2715,2718,2721,2724,2727,2730,2733,2736,2739,2742,2745,2748,2751,2754,2757,2760,2763,2766,2769,2772,2775,2778,2781,2784,2787,2790,2793,2796,2799,2802,2805,2808,2811,2814,2817,2820,2823,2826,2829,2832],{"path":2277,"title":2278},"\u002Fcss-only-micro-interactions-animations\u002Faccessibility-in-css-animations\u002Fcreating-accessible-focus-indicators","Creating Accessible Focus Indicators: Building a Ring That Survives Any Background",{"path":2280,"title":2281},"\u002Fcss-only-micro-interactions-animations\u002Faccessibility-in-css-animations\u002Fflashing-content-and-seizure-thresholds","Flashing Content and Seizure Thresholds",{"path":2283,"title":2284},"\u002Fcss-only-micro-interactions-animations\u002Faccessibility-in-css-animations\u002Fforced-colors-mode-and-animations","Forced Colors Mode and Animations",{"path":2286,"title":2287},"\u002Fcss-only-micro-interactions-animations\u002Faccessibility-in-css-animations","Accessibility in CSS Animations: Patterns, Specs & Best Practices",{"path":2289,"title":2290},"\u002Fcss-only-micro-interactions-animations\u002Faccessibility-in-css-animations\u002Fpause-controls-for-looping-animations","Pause Controls for Looping CSS Animations: Meeting WCAG 2.2.2",{"path":2292,"title":2293},"\u002Fcss-only-micro-interactions-animations\u002Faccessibility-in-css-animations\u002Fprefers-reduced-motion-recipes","prefers-reduced-motion Recipes: A Reduction for Every Common Effect",{"path":2295,"title":2296},"\u002Fcss-only-micro-interactions-animations\u002Faccessibility-in-css-animations\u002Freducing-motion-preferences-in-css","Reducing Motion Preferences in CSS: How the Setting Becomes a Media Feature",{"path":2298,"title":2299},"\u002Fcss-only-micro-interactions-animations\u002Faccessibility-in-css-animations\u002Ftarget-size-and-pointer-accessibility","Target Size and Pointer Accessibility: Meeting 2.5.8 Without Redesigning",{"path":2301,"title":2302},"\u002Fcss-only-micro-interactions-animations\u002Faccessibility-in-css-animations\u002Fvestibular-safe-animation-patterns","Vestibular-Safe Animation Patterns: Why Some Motion Makes People Sick",{"path":2304,"title":2305},"\u002Fcss-only-micro-interactions-animations\u002Faccessibility-in-css-animations\u002Fwcag-motion-success-criteria","WCAG Motion Success Criteria: What Each One Actually Requires",{"path":2307,"title":2308},"\u002Fcss-only-micro-interactions-animations\u002Fclip-path-and-mask-animations\u002Fanimated-gradient-borders-with-property","Animated Gradient Borders With @property: A Spinning Highlight Without Extra Elements",{"path":2310,"title":2311},"\u002Fcss-only-micro-interactions-animations\u002Fclip-path-and-mask-animations\u002Fclip-path-reveal-animations","Clip-Path Reveal Animations: Wipes, Irises and Corners Without Extra Markup",{"path":2313,"title":2314},"\u002Fcss-only-micro-interactions-animations\u002Fclip-path-and-mask-animations","Clip-Path & Mask Animations: Animating Visibility Instead of Position",{"path":2316,"title":2317},"\u002Fcss-only-micro-interactions-animations\u002Fclip-path-and-mask-animations\u002Fmask-image-fade-edges","Fading Edges With mask-image: Soft Boundaries That Work on Any Background",{"path":2319,"title":2320},"\u002Fcss-only-micro-interactions-animations\u002Fclip-path-and-mask-animations\u002Fmorphing-shapes-with-clip-path-polygon","Morphing Shapes With clip-path: polygon(): Point-Matched Transitions",{"path":2322,"title":2323},"\u002Fcss-only-micro-interactions-animations\u002Fcolor-and-theme-transitions\u002Fanimating-gradients","Animating CSS Gradients: Smooth Colour Motion Without Jumps",{"path":2325,"title":2326},"\u002Fcss-only-micro-interactions-animations\u002Fcolor-and-theme-transitions\u002Fcolor-mix-hover-states","Hover and Active States With color-mix(): One Token, Every Shade",{"path":2271,"title":5},{"path":2329,"title":2330},"\u002Fcss-only-micro-interactions-animations\u002Fcolor-and-theme-transitions\u002Flight-dark-function-for-themes","Light and Dark Themes With light-dark(): One Declaration per Colour",{"path":2332,"title":2333},"\u002Fcss-only-micro-interactions-animations\u002Fcolor-and-theme-transitions\u002Fsmooth-theme-switching-transitions","Smooth Theme Switching: Crossfades, Circular Reveals and No Flash",{"path":2335,"title":2336},"\u002Fcss-only-micro-interactions-animations\u002Fcontainer-aware-motion\u002Fcontainer-query-hover-affordances","Container Query Hover Affordances: When Narrow Means Persistent, Not Hidden",{"path":2338,"title":2339},"\u002Fcss-only-micro-interactions-animations\u002Fcontainer-aware-motion","Container-Aware Motion: Animation Sized by the Space a Component Actually Has",{"path":2341,"title":2342},"\u002Fcss-only-micro-interactions-animations\u002Fcontainer-aware-motion\u002Flayout-change-animations-at-container-breakpoints","Animating Changes at Container Breakpoints",{"path":2344,"title":2345},"\u002Fcss-only-micro-interactions-animations\u002Fcontainer-aware-motion\u002Fmotion-driven-by-container-style-queries","Motion Driven by Container Style Queries",{"path":2347,"title":2348},"\u002Fcss-only-micro-interactions-animations\u002Fcontainer-aware-motion\u002Fmotion-that-scales-with-container-size","Motion That Scales with Container Size: cqi, cqb, and Derived Durations",{"path":2350,"title":2351},"\u002Fcss-only-micro-interactions-animations\u002Fcontainer-aware-motion\u002Fsuppressing-motion-in-small-containers","Suppressing Motion in Small Containers: Deliberately Animating Nothing",{"path":2353,"title":2354},"\u002Fcss-only-micro-interactions-animations\u002Fcss-animation-vs-web-animations-api\u002Fcontrolling-css-animations-from-javascript","Controlling CSS Animations From JavaScript",{"path":2356,"title":2357},"\u002Fcss-only-micro-interactions-animations\u002Fcss-animation-vs-web-animations-api","CSS Animation vs the Web Animations API: A Decision Guide",{"path":2359,"title":2360},"\u002Fcss-only-micro-interactions-animations\u002Fcss-animation-vs-web-animations-api\u002Fwaapi-vs-css-for-interruptible-ui","Interruptible UI: CSS Transitions vs WAAPI",{"path":2362,"title":2363},"\u002Fcss-only-micro-interactions-animations\u002Fcss-animation-vs-web-animations-api\u002Fwhen-to-use-javascript-animation","When to Use JavaScript Instead of CSS for Animation",{"path":2365,"title":2366},"\u002Fcss-only-micro-interactions-animations\u002Fcss-custom-properties-architecture\u002Fanimating-custom-properties-with-at-property","Animating Custom Properties with @property: Typed Variables That Interpolate",{"path":2368,"title":2369},"\u002Fcss-only-micro-interactions-animations\u002Fcss-custom-properties-architecture\u002Fcustom-property-fallbacks-and-invalid-values","Custom Property Fallbacks and Invalid Values",{"path":2371,"title":2372},"\u002Fcss-only-micro-interactions-animations\u002Fcss-custom-properties-architecture\u002Ffluid-spacing-tokens-driving-transition-durations","Fluid Spacing Tokens That Drive Transition Durations",{"path":2374,"title":2375},"\u002Fcss-only-micro-interactions-animations\u002Fcss-custom-properties-architecture","CSS Custom Properties Architecture: Scalable Design Systems & Dynamic UI",{"path":2377,"title":2378},"\u002Fcss-only-micro-interactions-animations\u002Fcss-custom-properties-architecture\u002Fregistered-properties-and-type-safety","Registered Properties and Type Safety: @property as a Contract for Design Tokens",{"path":2380,"title":2381},"\u002Fcss-only-micro-interactions-animations\u002Fcss-custom-properties-architecture\u002Fscoping-custom-properties-to-components","Scoping Custom Properties to Components",{"path":2383,"title":2384},"\u002Fcss-only-micro-interactions-animations\u002Fcss-transition-fundamentals\u002Fcss-transition-timing-functions","CSS Transition Timing Functions: ease, cubic-bezier(), steps() and linear()",{"path":2386,"title":2387},"\u002Fcss-only-micro-interactions-animations\u002Fcss-transition-fundamentals","CSS Transition Fundamentals: Architecture, Performance & Patterns",{"path":2389,"title":2390},"\u002Fcss-only-micro-interactions-animations\u002Fcss-transition-fundamentals\u002Finterrupted-and-reversing-transitions","Interrupted and Reversing Transitions: What Happens Mid-Flight",{"path":2392,"title":2393},"\u002Fcss-only-micro-interactions-animations\u002Fcss-transition-fundamentals\u002Fstarting-style-entry-animations","@starting-style: Animating an Element's Very First Style Change",{"path":2395,"title":2396},"\u002Fcss-only-micro-interactions-animations\u002Fcss-transition-fundamentals\u002Ftransition-delay-choreography","Choreographing Motion With transition-delay",{"path":2398,"title":2399},"\u002Fcss-only-micro-interactions-animations\u002Fcss-transition-fundamentals\u002Ftransition-shorthand-and-multiple-properties","The transition Shorthand and Multiple Properties",{"path":2401,"title":2402},"\u002Fcss-only-micro-interactions-animations\u002Fcss-transition-fundamentals\u002Ftransitioning-display-with-allow-discrete","Transitioning display with transition-behavior: allow-discrete and @starting-style",{"path":2404,"title":2405},"\u002Fcss-only-micro-interactions-animations\u002Fdialog-and-popover-animations\u002Fanimating-dialog-open-and-close","Animating \u003Cdialog> Open and Close: Both Directions, No Library",{"path":2407,"title":2408},"\u002Fcss-only-micro-interactions-animations\u002Fdialog-and-popover-animations\u002Fbackdrop-animations","Animating ::backdrop: Fades and Blurs Behind Dialogs and Popovers",{"path":2410,"title":2411},"\u002Fcss-only-micro-interactions-animations\u002Fdialog-and-popover-animations","Dialog & Popover Animations: Motion for the Top Layer",{"path":2413,"title":2414},"\u002Fcss-only-micro-interactions-animations\u002Fdialog-and-popover-animations\u002Finterpolate-size-for-height-auto-animation","Animating to height: auto With interpolate-size and calc-size()",{"path":2416,"title":2417},"\u002Fcss-only-micro-interactions-animations\u002Fdialog-and-popover-animations\u002Fpopover-entry-and-exit-transitions","Popover Menus That Animate From Their Anchor",{"path":2419,"title":2420},"\u002Fcss-only-micro-interactions-animations\u002Feasing-and-motion-design\u002Fchoosing-animation-durations","Choosing Animation Durations: How Long Motion Should Last",{"path":2422,"title":2423},"\u002Fcss-only-micro-interactions-animations\u002Feasing-and-motion-design\u002Fcubic-bezier-overshoot-and-anticipation","Overshoot and Anticipation With cubic-bezier(): Motion With Character",{"path":2425,"title":2426},"\u002Fcss-only-micro-interactions-animations\u002Feasing-and-motion-design\u002Fentry-vs-exit-easing","Entry vs Exit Easing: Different Curves for Arriving and Leaving",{"path":2428,"title":2429},"\u002Fcss-only-micro-interactions-animations\u002Feasing-and-motion-design","Easing & Motion Design: Making CSS Motion Feel Intentional",{"path":2431,"title":2432},"\u002Fcss-only-micro-interactions-animations\u002Feasing-and-motion-design\u002Flinear-easing-function-for-springs","Spring and Bounce Easing With linear(): Physical Motion Without JavaScript",{"path":2434,"title":2435},"\u002Fcss-only-micro-interactions-animations\u002Feasing-and-motion-design\u002Fsteps-easing-for-sprite-animations","steps() Easing: Sprite Sheets, Typewriters and Other Frame-by-Frame Effects",{"path":2437,"title":2438},"\u002Fcss-only-micro-interactions-animations\u002Fhover-focus-state-design\u002Faccessible-css-only-tooltips","Accessible CSS-Only Tooltips and Hover Cards With  and ",{"path":2440,"title":2441},"\u002Fcss-only-micro-interactions-animations\u002Fhover-focus-state-design\u002Fbutton-press-and-active-states","Button Press and  States",{"path":2443,"title":2444},"\u002Fcss-only-micro-interactions-animations\u002Fhover-focus-state-design\u002Ffocus-visible-vs-focus-polyfill-alternatives"," vs :focus: Why It Replaced the Focus-Ring Polyfill",{"path":2446,"title":2447},"\u002Fcss-only-micro-interactions-animations\u002Fhover-focus-state-design\u002Ffocus-within-form-patterns"," Form Patterns: Styling a Group From the Field Inside It",{"path":2449,"title":2450},"\u002Fcss-only-micro-interactions-animations\u002Fhover-focus-state-design\u002Fhover-intent-delays-with-transition-delay","Hover Intent Delays With transition-delay: Menus That Don't Flicker",{"path":2452,"title":2453},"\u002Fcss-only-micro-interactions-animations\u002Fhover-focus-state-design","Hover & Focus State Design: Spec-Compliant Patterns for Modern UIs",{"path":2455,"title":2456},"\u002Fcss-only-micro-interactions-animations\u002Fhover-focus-state-design\u002Fpointer-and-hover-media-queries","Pointer and Hover Media Queries: Gating Affordances by Input Capability",{"path":2458,"title":2459},"\u002Fcss-only-micro-interactions-animations\u002Fhover-focus-state-design\u002Fsmooth-hover-effects-without-javascript","Smooth Hover Effects Without JavaScript: CSS-Only Patterns for Modern UI",{"path":2461,"title":2462},"\u002Fcss-only-micro-interactions-animations","CSS-Only Micro-Interactions & Animations: Architecture, Performance & Implementation",{"path":2464,"title":2465},"\u002Fcss-only-micro-interactions-animations\u002Fkeyframe-animation-patterns\u002Fanimation-composition-and-stacking","Stacking Animations With animation-composition",{"path":2467,"title":2468},"\u002Fcss-only-micro-interactions-animations\u002Fkeyframe-animation-patterns\u002Fanimation-fill-mode-explained","animation-fill-mode: What an Animation Applies Before and After It Runs",{"path":2470,"title":2471},"\u002Fcss-only-micro-interactions-animations\u002Fkeyframe-animation-patterns\u002Fcontainer-query-triggered-keyframe-animations","Container-Query-Triggered Keyframe Animations: Animate Differently per Available Space",{"path":2473,"title":2474},"\u002Fcss-only-micro-interactions-animations\u002Fkeyframe-animation-patterns\u002Fcss-only-accordions-and-disclosure","CSS-Only Accordions and Disclosure Widgets with details and summary",{"path":2476,"title":2477},"\u002Fcss-only-micro-interactions-animations\u002Fkeyframe-animation-patterns\u002Fcss-only-loading-spinners-and-skeletons","CSS-Only Loading Spinners and Skeleton Shimmer Screens with @keyframes",{"path":2479,"title":2480},"\u002Fcss-only-micro-interactions-animations\u002Fkeyframe-animation-patterns\u002Fcss-only-toggle-switches-and-checkboxes","CSS-Only Toggle Switches and Checkboxes That Keep the Native Input",{"path":2482,"title":2483},"\u002Fcss-only-micro-interactions-animations\u002Fkeyframe-animation-patterns","Keyframe Animation Patterns: Spec-Compliant Architectures for Modern UIs",{"path":2485,"title":2486},"\u002Fcss-only-micro-interactions-animations\u002Fkeyframe-animation-patterns\u002Fmulti-step-keyframes-and-per-keyframe-easing","Multi-Step Keyframes and Per-Keyframe Easing",{"path":2488,"title":2489},"\u002Fcss-only-micro-interactions-animations\u002Fkeyframe-animation-patterns\u002Fstaggered-list-animations-with-custom-properties","Staggered List Animations Using --i Index Custom Properties",{"path":2491,"title":2492},"\u002Fcss-only-micro-interactions-animations\u002Fperformance-gpu-acceleration\u002Fanimating-box-shadow-and-filters-cheaply","Animating box-shadow and Filters Cheaply",{"path":2494,"title":2495},"\u002Fcss-only-micro-interactions-animations\u002Fperformance-gpu-acceleration\u002Fcontain-property-for-animation-performance","The contain Property for Animation Performance",{"path":2497,"title":2498},"\u002Fcss-only-micro-interactions-animations\u002Fperformance-gpu-acceleration\u002Fcss-triggers-layout-paint-composite","Layout, Paint and Composite: What Triggers What",{"path":2500,"title":2501},"\u002Fcss-only-micro-interactions-animations\u002Fperformance-gpu-acceleration","Performance & GPU Acceleration in CSS: A Developer’s Blueprint",{"path":2503,"title":2504},"\u002Fcss-only-micro-interactions-animations\u002Fperformance-gpu-acceleration\u002Foptimizing-css-animations-for-60fps","Optimizing CSS Animations for 60fps: Budgeting the Frame",{"path":2506,"title":2507},"\u002Fcss-only-micro-interactions-animations\u002Fperformance-gpu-acceleration\u002Fprofiling-animations-in-devtools","Profiling CSS Animations in DevTools: Finding the Frame That Costs Too Much",{"path":2509,"title":2510},"\u002Fcss-only-micro-interactions-animations\u002Fperformance-gpu-acceleration\u002Fwill-change-and-the-compositor-thread","will-change and the Compositor Thread: Layer Promotion Without the Footguns",{"path":2512,"title":2513},"\u002Fcss-only-micro-interactions-animations\u002Fscroll-driven-animations","Scroll-Driven Animations: Binding Motion to Scroll Position in Pure CSS",{"path":2515,"title":2516},"\u002Fcss-only-micro-interactions-animations\u002Fscroll-driven-animations\u002Fscroll-driven-animation-fallbacks","Shipping Scroll-Driven Animations Without Breaking Anything",{"path":2518,"title":2519},"\u002Fcss-only-micro-interactions-animations\u002Fscroll-driven-animations\u002Fscroll-driven-parallax-effects","Scroll-Driven Parallax Effects in CSS",{"path":2521,"title":2522},"\u002Fcss-only-micro-interactions-animations\u002Fscroll-driven-animations\u002Fscroll-progress-bar-without-javascript","A Reading Progress Bar Driven by scroll(root)",{"path":2524,"title":2525},"\u002Fcss-only-micro-interactions-animations\u002Fscroll-driven-animations\u002Fscroll-triggered-reveal-animations","Reveal-on-Scroll with view() and animation-range",{"path":2527,"title":2528},"\u002Fcss-only-micro-interactions-animations\u002Fscroll-driven-animations\u002Ftimeline-scope-for-cross-element-animations","timeline-scope for Cross-Element Animations",{"path":2530,"title":2531},"\u002Fcss-only-micro-interactions-animations\u002Fscroll-driven-animations\u002Fview-timeline-and-animation-range","View Timelines and animation-range",{"path":2533,"title":2534},"\u002Fcss-only-micro-interactions-animations\u002Fview-transitions-for-css-developers\u002Fcross-document-view-transitions","Cross-Document View Transitions",{"path":2536,"title":2537},"\u002Fcss-only-micro-interactions-animations\u002Fview-transitions-for-css-developers","View Transitions for CSS Developers: the Pseudo-Element Tree, Snapshots, and Named Elements",{"path":2539,"title":2540},"\u002Fcss-only-micro-interactions-animations\u002Fview-transitions-for-css-developers\u002Fsame-document-view-transitions","Writing a Same-Document View Transition: the CSS You Actually Author",{"path":2542,"title":2543},"\u002Fcss-only-micro-interactions-animations\u002Fview-transitions-for-css-developers\u002Fview-transition-names-shared-elements","view-transition-name and Shared Elements: Morphing a Thumbnail into a Hero",{"path":2545,"title":2546},"\u002Fcss-only-micro-interactions-animations\u002Fview-transitions-for-css-developers\u002Fview-transition-types-and-classes","View Transition Types and Classes",{"path":2548,"title":2549},"\u002Fcss-only-micro-interactions-animations\u002Fview-transitions-for-css-developers\u002Fview-transitions-for-list-reordering","View Transitions for List Reordering",{"path":2551,"title":2552},"\u002Fcss-only-micro-interactions-animations\u002Fview-transitions-for-css-developers\u002Fview-transitions-with-reduced-motion","Honouring prefers-reduced-motion in View Transitions",{"path":2554,"title":2555},"\u002F","Modern CSS Layouts & Micro-Interactions",{"path":2557,"title":2558},"\u002Fmastering-container-queries-responsive-layouts\u002Fanchor-positioning-and-overlays\u002Fanchor-positioned-tooltips","Anchor-Positioned Tooltips: Tethering, Flipping, and Getting the Semantics Right",{"path":2560,"title":2561},"\u002Fmastering-container-queries-responsive-layouts\u002Fanchor-positioning-and-overlays\u002Fanchor-positioning-fallbacks","Anchor Positioning Fallbacks: A Graceful Floor for Older Browser Versions",{"path":2563,"title":2564},"\u002Fmastering-container-queries-responsive-layouts\u002Fanchor-positioning-and-overlays\u002Fanchor-size-for-matching-widths","anchor-size(): Overlays That Match Their Trigger's Size",{"path":2566,"title":2567},"\u002Fmastering-container-queries-responsive-layouts\u002Fanchor-positioning-and-overlays","CSS Anchor Positioning and Overlays: Tethering Elements Without JavaScript",{"path":2569,"title":2570},"\u002Fmastering-container-queries-responsive-layouts\u002Fanchor-positioning-and-overlays\u002Fpopover-attribute-and-css-styling","The popover Attribute: Top-Layer Overlays With No JavaScript",{"path":2572,"title":2573},"\u002Fmastering-container-queries-responsive-layouts\u002Fanchor-positioning-and-overlays\u002Fpopover-light-dismiss-and-stacking","Popover Light Dismiss, Nesting and Stacking",{"path":2575,"title":2576},"\u002Fmastering-container-queries-responsive-layouts\u002Fanchor-positioning-and-overlays\u002Fposition-area-grid-explained","position-area: The Anchor Placement Grid",{"path":2578,"title":2579},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-fallbacks\u002Fcontainer-query-polyfill-tradeoffs","Container Query Polyfills: What They Cost and When to Skip Them",{"path":2581,"title":2582},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-fallbacks\u002Ffeature-detection-with-supports","Feature Detection with @supports: Syntax, Semantics, and the Traps",{"path":2584,"title":2585},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-fallbacks\u002Fhandling-container-query-fallbacks-for-older-browsers","Handling Container Query Fallbacks for Older Browsers",{"path":2587,"title":2588},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-fallbacks","Container Query Fallbacks: Spec-Compliant CSS Strategies for Legacy Browsers",{"path":2590,"title":2591},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-fallbacks\u002Fintrinsic-layout-fallbacks-without-queries","Intrinsic Layout Fallbacks: Baselines That Adapt Without Any Query",{"path":2593,"title":2594},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-fallbacks\u002Ftesting-fallbacks-without-old-browsers","Testing CSS Fallbacks Without an Old Browser",{"path":2596,"title":2597},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics\u002Fcontainer-queries-inside-css-nesting","Container Queries Inside CSS Nesting: Keeping Responses Next to the Styles They Change",{"path":2599,"title":2600},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics\u002Fcontainer-query-range-and-logic-operators","Container Query Range Syntax and Logic Operators",{"path":2602,"title":2603},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics\u002Fcontainer-query-units-cqi-cqb-explained","Container Query Units Explained: cqi, cqb, cqw, cqh, cqmin and cqmax",{"path":2605,"title":2606},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics\u002Fcontainer-type-size-vs-inline-size","Choosing container-type: inline-size, size, or normal",{"path":2608,"title":2609},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics\u002Fcontainer-vs-media-queries-comparison","Container vs Media Queries: Choosing the Right Reference Box",{"path":2611,"title":2612},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics\u002Fdebugging-container-queries-in-devtools","Debugging Container Queries in DevTools: Why a Rule Doesn't Match",{"path":2614,"title":2615},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics\u002Fhow-to-use-container-queries-in-production","How to Use Container Queries in Production Without a Rewrite",{"path":2617,"title":2618},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics","Container Query Syntax Basics",{"path":2620,"title":2621},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics\u002Fnesting-and-naming-container-queries","Nesting and Naming Container Queries: Targeting the Right Ancestor",{"path":2623,"title":2624},"\u002Fmastering-container-queries-responsive-layouts\u002Fcss-grid-and-subgrid-layouts\u002Fauto-fit-minmax-responsive-grids","Responsive Grids with repeat(auto-fit, minmax())",{"path":2626,"title":2627},"\u002Fmastering-container-queries-responsive-layouts\u002Fcss-grid-and-subgrid-layouts\u002Fbuilding-a-dashboard-with-subgrid","Building a Responsive Dashboard Where Cards Align with Subgrid",{"path":2629,"title":2630},"\u002Fmastering-container-queries-responsive-layouts\u002Fcss-grid-and-subgrid-layouts\u002Fgrid-auto-flow-dense-packing","grid-auto-flow: dense for Packed Layouts",{"path":2632,"title":2633},"\u002Fmastering-container-queries-responsive-layouts\u002Fcss-grid-and-subgrid-layouts\u002Fgrid-template-areas-responsive-layouts","Responsive Layouts With grid-template-areas: Rearranging Regions by Name",{"path":2635,"title":2636},"\u002Fmastering-container-queries-responsive-layouts\u002Fcss-grid-and-subgrid-layouts\u002Fholy-grail-layout-with-grid","The Holy-Grail Layout with CSS Grid and Container Queries",{"path":2638,"title":2639},"\u002Fmastering-container-queries-responsive-layouts\u002Fcss-grid-and-subgrid-layouts","CSS Grid and Subgrid Layouts for Responsive Interfaces",{"path":2641,"title":2642},"\u002Fmastering-container-queries-responsive-layouts\u002Fcss-grid-and-subgrid-layouts\u002Fmasonry-style-layouts-with-grid","Masonry-Style Layouts With the CSS That Ships Today",{"path":2644,"title":2645},"\u002Fmastering-container-queries-responsive-layouts\u002Fcss-grid-and-subgrid-layouts\u002Foverlapping-elements-with-grid-stacking","Overlapping Elements With Grid Stacking: Layers Without position: absolute",{"path":2647,"title":2648},"\u002Fmastering-container-queries-responsive-layouts\u002Fcss-grid-and-subgrid-layouts\u002Fsubgrid-vs-nested-grid","Subgrid vs an Independently Nested Grid",{"path":2650,"title":2651},"\u002Fmastering-container-queries-responsive-layouts\u002Fflexbox-layout-patterns\u002Fcentering-in-css-every-method","Centering in CSS: Every Modern Method and When Each One Breaks",{"path":2653,"title":2654},"\u002Fmastering-container-queries-responsive-layouts\u002Fflexbox-layout-patterns\u002Fflex-basis-vs-width","flex-basis vs width: Which Size a Flex Item Actually Starts From",{"path":2656,"title":2657},"\u002Fmastering-container-queries-responsive-layouts\u002Fflexbox-layout-patterns\u002Fflex-grow-and-flex-shrink-explained","flex-grow and flex-shrink Explained",{"path":2659,"title":2660},"\u002Fmastering-container-queries-responsive-layouts\u002Fflexbox-layout-patterns\u002Fflex-wrap-intrinsic-responsive-rows","Breakpoint-Free Responsive Rows With flex-wrap and flex-basis",{"path":2662,"title":2663},"\u002Fmastering-container-queries-responsive-layouts\u002Fflexbox-layout-patterns\u002Fflexbox-gap-and-spacing","Flexbox gap vs Margins: Spacing Items Without Edge Hacks",{"path":2665,"title":2666},"\u002Fmastering-container-queries-responsive-layouts\u002Fflexbox-layout-patterns","Flexbox Layout Patterns: One-Dimensional Layout for Real Components",{"path":2668,"title":2669},"\u002Fmastering-container-queries-responsive-layouts\u002Fflexbox-layout-patterns\u002Fsticky-footer-with-flexbox-and-grid","Sticky Footer With Flexbox or Grid: Pinning the Footer on Short Pages",{"path":2671,"title":2672},"\u002Fmastering-container-queries-responsive-layouts\u002Ffluid-typography-with-clamp\u002Fclamp-vs-media-query-typography","clamp() Fluid Type vs Media-Query Step Typography: Tradeoffs and Anatomy",{"path":2674,"title":2675},"\u002Fmastering-container-queries-responsive-layouts\u002Ffluid-typography-with-clamp\u002Fdebugging-clamp-values","Debugging clamp(): Values That Are Ignored, Stuck or Never Fluid",{"path":2677,"title":2678},"\u002Fmastering-container-queries-responsive-layouts\u002Ffluid-typography-with-clamp\u002Ffluid-line-height-and-measure","Fluid Line Height and Measure: Keeping Text Readable as It Scales",{"path":2680,"title":2681},"\u002Fmastering-container-queries-responsive-layouts\u002Ffluid-typography-with-clamp\u002Ffluid-space-scale-with-clamp","Building a Fluid Spacing Scale with clamp() and Container Units",{"path":2683,"title":2684},"\u002Fmastering-container-queries-responsive-layouts\u002Ffluid-typography-with-clamp\u002Ffluid-type-accessibility-and-zoom","Fluid Type, Accessibility and Zoom: The vw-Only Trap",{"path":2686,"title":2687},"\u002Fmastering-container-queries-responsive-layouts\u002Ffluid-typography-with-clamp\u002Ffluid-typography-without-javascript","Fluid Typography Without JavaScript: A Precision CSS Reference",{"path":2689,"title":2690},"\u002Fmastering-container-queries-responsive-layouts\u002Ffluid-typography-with-clamp","Fluid Typography with clamp(): A Practical Guide for Modern CSS",{"path":2692,"title":2693},"\u002Fmastering-container-queries-responsive-layouts\u002Ffluid-typography-with-clamp\u002Ftext-wrap-balance-and-pretty","text-wrap: balance and pretty: Better Line Breaks Without Manual Breaks",{"path":2695,"title":2696},"\u002Fmastering-container-queries-responsive-layouts","Mastering Container Queries & Responsive Layouts",{"path":2698,"title":2699},"\u002Fmastering-container-queries-responsive-layouts\u002Fintrinsic-sizing-techniques\u002Faspect-ratio-for-responsive-media","Using aspect-ratio for Responsive Media and Preventing Layout Shift",{"path":2701,"title":2702},"\u002Fmastering-container-queries-responsive-layouts\u002Fintrinsic-sizing-techniques\u002Fcontent-visibility-and-contain-intrinsic-size","content-visibility and contain-intrinsic-size: Rendering Only What Is Near the Screen",{"path":2704,"title":2705},"\u002Fmastering-container-queries-responsive-layouts\u002Fintrinsic-sizing-techniques","Intrinsic Sizing Techniques: Modern CSS Layouts for Responsive UI",{"path":2707,"title":2708},"\u002Fmastering-container-queries-responsive-layouts\u002Fintrinsic-sizing-techniques\u002Flayout-widths-with-min-max-clamp","Layout Widths With min(), max() and clamp(): Sizing Without Breakpoints",{"path":2710,"title":2711},"\u002Fmastering-container-queries-responsive-layouts\u002Fintrinsic-sizing-techniques\u002Fmin-max-fit-content-explained","min-content, max-content, and fit-content() Explained",{"path":2713,"title":2714},"\u002Fmastering-container-queries-responsive-layouts\u002Fintrinsic-sizing-techniques\u002Fobject-fit-and-object-position","object-fit and object-position: Images That Fit Any Box",{"path":2716,"title":2717},"\u002Fmastering-container-queries-responsive-layouts\u002Fintrinsic-sizing-techniques\u002Fpreventing-flex-and-grid-overflow","The min-width: auto Trap in Flex and Grid Layouts",{"path":2719,"title":2720},"\u002Fmastering-container-queries-responsive-layouts\u002Fmodern-css-reset-strategies\u002Fcascade-layers-for-reset-and-tokens","Cascade Layers for Reset and Design Tokens",{"path":2722,"title":2723},"\u002Fmastering-container-queries-responsive-layouts\u002Fmodern-css-reset-strategies\u002Fcascade-layers-vs-specificity-hacks","Cascade Layers vs Specificity Hacks: Ordering Instead of Escalating",{"path":2725,"title":2726},"\u002Fmastering-container-queries-responsive-layouts\u002Fmodern-css-reset-strategies\u002Fcss-scope-for-component-styles","@scope for Component Styles: Short Selectors That Cannot Leak",{"path":2728,"title":2729},"\u002Fmastering-container-queries-responsive-layouts\u002Fmodern-css-reset-strategies","Modern CSS Reset Strategies: A Spec-Compliant Foundation",{"path":2731,"title":2732},"\u002Fmastering-container-queries-responsive-layouts\u002Fmodern-css-reset-strategies\u002Frevert-layer-and-revert-keywords","initial, inherit, unset, revert and revert-layer: Undoing Styles Correctly",{"path":2734,"title":2735},"\u002Fmastering-container-queries-responsive-layouts\u002Fmodern-css-reset-strategies\u002Fwriting-a-minimal-modern-reset","Writing a Minimal Modern CSS Reset: Every Line Justified",{"path":2737,"title":2738},"\u002Fmastering-container-queries-responsive-layouts\u002Fparent-aware-layouts-with-has\u002Fcontent-aware-card-layouts-with-has","Content-Aware Card Layouts With :has(): One Component, Many Shapes",{"path":2740,"title":2741},"\u002Fmastering-container-queries-responsive-layouts\u002Fparent-aware-layouts-with-has\u002Fempty-and-loading-states-with-has","Empty and Loading States With ()",{"path":2743,"title":2744},"\u002Fmastering-container-queries-responsive-layouts\u002Fparent-aware-layouts-with-has\u002Fform-validation-layouts-with-has","Form Validation Layouts With :has(): Styling Groups From Field State",{"path":2746,"title":2747},"\u002Fmastering-container-queries-responsive-layouts\u002Fparent-aware-layouts-with-has\u002Fhas-selector-performance",":has() Selector Performance: What Is Cheap, What Is Not, and How to Measure",{"path":2749,"title":2750},"\u002Fmastering-container-queries-responsive-layouts\u002Fparent-aware-layouts-with-has\u002Fhas-vs-container-style-queries",":has() vs Container Style Queries: Discovered State and Published State",{"path":2752,"title":2753},"\u002Fmastering-container-queries-responsive-layouts\u002Fparent-aware-layouts-with-has","Parent-Aware Layouts With :has(): Letting Containers React to Their Contents",{"path":2755,"title":2756},"\u002Fmastering-container-queries-responsive-layouts\u002Fparent-aware-layouts-with-has\u002Fquantity-queries-with-has","Quantity Queries With :has(): Layouts That Respond to How Many Items There Are",{"path":2758,"title":2759},"\u002Fmastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns\u002Fbuilding-responsive-cards-with-container-queries","Building Responsive Cards with Container Queries: A Production-Ready Guide",{"path":2761,"title":2762},"\u002Fmastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns\u002Fcontainer-query-data-tables","Container Query Data Tables: Reflowing Tables to Cards",{"path":2764,"title":2765},"\u002Fmastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns\u002Fcontainer-query-sidebar-layouts","Container Query Sidebar Layouts That Travel Between Contexts",{"path":2767,"title":2768},"\u002Fmastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns","Responsive Component Patterns: Architecture & Implementation",{"path":2770,"title":2771},"\u002Fmastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns\u002Fresponsive-forms-with-container-queries","Responsive Forms with Container Queries: Reflow Without Losing the Wiring",{"path":2773,"title":2774},"\u002Fmastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns\u002Fresponsive-hero-sections-with-container-queries","Responsive Hero Sections With Container Queries",{"path":2776,"title":2777},"\u002Fmastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns\u002Fresponsive-navigation-without-media-queries","Responsive Navigation Without Media Queries Using Container Queries",{"path":2779,"title":2780},"\u002Fmastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns\u002Fresponsive-pricing-tables","Responsive Pricing Tables: Aligned Features, a Clear Recommendation, Any Width",{"path":2782,"title":2783},"\u002Fmastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns\u002Ftabs-that-become-accordions","Tabs That Become an Accordion: One Component, Two Layouts",{"path":2785,"title":2786},"\u002Fmastering-container-queries-responsive-layouts\u002Fscroll-snap-and-overflow-layouts\u002Fcss-only-carousel-with-scroll-snap","A CSS-Only Carousel With Scroll Snap",{"path":2788,"title":2789},"\u002Fmastering-container-queries-responsive-layouts\u002Fscroll-snap-and-overflow-layouts\u002Fhorizontal-scrolling-card-rows","Horizontal Scrolling Card Rows: Swipeable Lists That Stay in Their Lane",{"path":2791,"title":2792},"\u002Fmastering-container-queries-responsive-layouts\u002Fscroll-snap-and-overflow-layouts","Scroll Snap & Overflow Layouts: Designing Components That Scroll on Purpose",{"path":2794,"title":2795},"\u002Fmastering-container-queries-responsive-layouts\u002Fscroll-snap-and-overflow-layouts\u002Foverscroll-behavior-for-nested-scrolling","overscroll-behavior: Taming Nested Scrolling, Pull-to-Refresh and Back-Swipe",{"path":2797,"title":2798},"\u002Fmastering-container-queries-responsive-layouts\u002Fscroll-snap-and-overflow-layouts\u002Fscroll-margin-and-scroll-padding-for-sticky-headers","scroll-margin and scroll-padding: Keeping Targets Clear of Sticky Headers",{"path":2800,"title":2801},"\u002Fmastering-container-queries-responsive-layouts\u002Fstyle-queries-and-container-state\u002Fcombining-size-and-style-queries","Combining Size and Style Container Queries",{"path":2803,"title":2804},"\u002Fmastering-container-queries-responsive-layouts\u002Fstyle-queries-and-container-state\u002Fcontainer-style-query-theming","Container Style Query Theming: One Token Instead of a Variant Class Matrix",{"path":2806,"title":2807},"\u002Fmastering-container-queries-responsive-layouts\u002Fstyle-queries-and-container-state","Style Queries and Container State: Token-Driven Component Variants",{"path":2809,"title":2810},"\u002Fmastering-container-queries-responsive-layouts\u002Fstyle-queries-and-container-state\u002Fstyle-queries-with-custom-properties","Style Queries With Custom Properties: Declaring, Inheriting, and Matching Tokens",{"path":2812,"title":2813},"\u002Fmastering-container-queries-responsive-layouts\u002Fstyle-queries-and-container-state\u002Fstyle-query-component-variants","Component Variants With Style Queries: One Token, Many Looks",{"path":2815,"title":2816},"\u002Fmastering-container-queries-responsive-layouts\u002Fstyle-queries-and-container-state\u002Fstyle-query-fallbacks-and-support","Style Query Fallbacks and Support: Designing for Older Browser Versions",{"path":2818,"title":2819},"\u002Fmastering-container-queries-responsive-layouts\u002Fviewport-units-and-mobile-layout\u002Fdvh-svh-lvh-mobile-viewport-units","dvh, svh and lvh: Choosing the Right Mobile Viewport Unit",{"path":2821,"title":2822},"\u002Fmastering-container-queries-responsive-layouts\u002Fviewport-units-and-mobile-layout\u002Ffull-bleed-sections-in-constrained-layouts","Full-Bleed Sections in a Constrained Layout: Breaking Out Without Breaking the Page",{"path":2824,"title":2825},"\u002Fmastering-container-queries-responsive-layouts\u002Fviewport-units-and-mobile-layout","Viewport Units & Mobile Layout: Designing for the Screen You Actually Get",{"path":2827,"title":2828},"\u002Fmastering-container-queries-responsive-layouts\u002Fviewport-units-and-mobile-layout\u002Fsafe-area-insets-for-notched-screens","Safe Area Insets: Designing Around Notches, Corners and Home Indicators",{"path":2830,"title":2831},"\u002Fmastering-container-queries-responsive-layouts\u002Fviewport-units-and-mobile-layout\u002Fviewport-units-vs-container-units","Viewport Units vs Container Units: Choosing the Right Reference Box",{"path":2833,"title":2834},"\u002Fmastering-container-queries-responsive-layouts\u002Fviewport-units-and-mobile-layout\u002Fvirtual-keyboard-and-interactive-widget","Layouts That Survive the Virtual Keyboard",{"title":2836,"caption":2837,"height":2838,"html":2839,"css":2840},"Status badges derived from one hue each","Four variants set only --hue; background, text and border are mixed in OKLCH and follow the frame's light or dark scheme.",150,"\n\u003Cdiv class=\"row\">\n  \u003Cspan class=\"badge badge--success\">Deployed\u003C\u002Fspan>\n  \u003Cspan class=\"badge badge--warning\">Degraded\u003C\u002Fspan>\n  \u003Cspan class=\"badge badge--error\">Failed\u003C\u002Fspan>\n  \u003Cspan class=\"badge badge--info\">Queued\u003C\u002Fspan>\n\u003C\u002Fdiv>","\n.row { display: flex; flex-wrap: wrap; gap: 0.6rem; justify-content: center; }\n.badge {\n  --hue: 250;\n  --badge-base: oklch(55% 0.16 var(--hue));\n  --badge-bg: light-dark(color-mix(in oklch, var(--badge-base) 14%, white), color-mix(in oklch, var(--badge-base) 30%, black));\n  --badge-fg: light-dark(color-mix(in oklch, var(--badge-base), black 35%), color-mix(in oklch, var(--badge-base), white 55%));\n  display: inline-flex;\n  align-items: center;\n  gap: 0.35rem;\n  padding: 0.2rem 0.6rem;\n  border: 1px solid color-mix(in oklch, var(--badge-fg) 35%, transparent);\n  border-radius: 999px;\n  background: var(--badge-bg);\n  color: var(--badge-fg);\n  font-size: 0.85rem;\n  transition: background-color 200ms ease, color 200ms ease;\n}\n.badge--success { --hue: 150; }\n.badge--warning { --hue: 75; }\n.badge--error { --hue: 25; }\n.badge--info { --hue: 250; }\n.badge::before { content: \"●\"; font-size: 0.6em; }",1789717745536]