[{"data":1,"prerenderedAt":1274},["ShallowReactive",2],{"page-\u002Fcss\u002F04-colors-and-units":3},{"id":4,"title":5,"body":6,"description":1267,"extension":1268,"meta":1269,"navigation":697,"path":1270,"seo":1271,"stem":1272,"__hash__":1273},"content\u002Fcss\u002F04-colors-and-units.md","04 — Color Spaces, Units & the Fluid Math of CSS",{"type":7,"value":8,"toc":1248},"minimark",[9,13,26,31,227,232,265,330,341,413,417,427,431,513,574,590,860,864,930,937,1005,1009,1079,1083,1187,1191,1218,1244],[10,11,5],"h1",{"id":12},"_04-color-spaces-units-the-fluid-math-of-css",[14,15,16,17,21,22,25],"p",{},"CSS has a unit system and a color system. Most bugs in both come from not knowing ",[18,19,20],"em",{},"what the unit is relative to"," (rem→root, em→parent, %→parent's same-axis dimension, vw→viewport including scrollbar) and not knowing ",[18,23,24],{},"that HSL lightness is a lie"," (50% lightness varies in perceived brightness across hues). This chapter engineers both.",[27,28,30],"h2",{"id":29},"color-the-perceptually-uniform-truth","Color — the perceptually uniform truth",[32,33,35],"code-wrapper",{"language":34},"css",[36,37,41],"pre",{"className":38,"code":39,"language":34,"meta":40,"style":40},"language-css shiki shiki-themes github-light github-dark",":root {\n  --c-primary: oklch(0.62 0.18 245);       \u002F* L=0-1, C=chroma 0-0.37, H=0-360. Perceptually uniform. *\u002F\n  --c-primary-hover: color-mix(in oklch, var(--c-primary) 85%, white 15%);  \u002F* derive tint at runtime *\u002F\n  --c-danger: oklch(0.62 0.22 25);         \u002F* same L as primary → SAME perceived brightness. HSL can't do this. *\u002F\n  --c-on-primary: oklch(0.98 0 0);         \u002F* near-white, zero chroma → grayscale *\u002F\n}\n\u002F* Why OKLCH: hsl(60,100%,50%) yellow looks WAY brighter than hsl(240,100%,50%) blue\n   at the \"same\" 50% lightness. OKLCH's L axis is perceptually uniform — 0.62 looks\n   equally bright whether the hue is blue, red, or green. Deriving palettes by\n   tweaking L is now mathematically sound. *\u002F\n","",[42,43,44,57,90,146,172,197,203,209,215,221],"code",{"__ignoreMap":40},[45,46,49,53],"span",{"class":47,"line":48},"line",1,[45,50,52],{"class":51},"sIsaT",":root",[45,54,56],{"class":55},"ssxIu"," {\n",[45,58,60,64,67,71,74,77,80,83,86],{"class":47,"line":59},2,[45,61,63],{"class":62},"sCrzJ","  --c-primary",[45,65,66],{"class":55},": ",[45,68,70],{"class":69},"snvgF","oklch",[45,72,73],{"class":55},"(",[45,75,76],{"class":69},"0.62",[45,78,79],{"class":69}," 0.18",[45,81,82],{"class":69}," 245",[45,84,85],{"class":55},");       ",[45,87,89],{"class":88},"sdCPZ","\u002F* L=0-1, C=chroma 0-0.37, H=0-360. Perceptually uniform. *\u002F\n",[45,91,93,96,98,101,103,106,109,112,115,117,120,123,126,130,132,135,138,140,143],{"class":47,"line":92},3,[45,94,95],{"class":62},"  --c-primary-hover",[45,97,66],{"class":55},[45,99,100],{"class":69},"color-mix",[45,102,73],{"class":55},[45,104,105],{"class":62},"in",[45,107,108],{"class":62}," oklch",[45,110,111],{"class":55},", ",[45,113,114],{"class":69},"var",[45,116,73],{"class":55},[45,118,119],{"class":62},"--c-primary",[45,121,122],{"class":55},") ",[45,124,125],{"class":69},"85",[45,127,129],{"class":128},"svdQ7","%",[45,131,111],{"class":55},[45,133,134],{"class":69},"white",[45,136,137],{"class":69}," 15",[45,139,129],{"class":128},[45,141,142],{"class":55},");  ",[45,144,145],{"class":88},"\u002F* derive tint at runtime *\u002F\n",[45,147,149,152,154,156,158,160,163,166,169],{"class":47,"line":148},4,[45,150,151],{"class":62},"  --c-danger",[45,153,66],{"class":55},[45,155,70],{"class":69},[45,157,73],{"class":55},[45,159,76],{"class":69},[45,161,162],{"class":69}," 0.22",[45,164,165],{"class":69}," 25",[45,167,168],{"class":55},");         ",[45,170,171],{"class":88},"\u002F* same L as primary → SAME perceived brightness. HSL can't do this. *\u002F\n",[45,173,175,178,180,182,184,187,190,192,194],{"class":47,"line":174},5,[45,176,177],{"class":62},"  --c-on-primary",[45,179,66],{"class":55},[45,181,70],{"class":69},[45,183,73],{"class":55},[45,185,186],{"class":69},"0.98",[45,188,189],{"class":69}," 0",[45,191,189],{"class":69},[45,193,168],{"class":55},[45,195,196],{"class":88},"\u002F* near-white, zero chroma → grayscale *\u002F\n",[45,198,200],{"class":47,"line":199},6,[45,201,202],{"class":55},"}\n",[45,204,206],{"class":47,"line":205},7,[45,207,208],{"class":88},"\u002F* Why OKLCH: hsl(60,100%,50%) yellow looks WAY brighter than hsl(240,100%,50%) blue\n",[45,210,212],{"class":47,"line":211},8,[45,213,214],{"class":88},"   at the \"same\" 50% lightness. OKLCH's L axis is perceptually uniform — 0.62 looks\n",[45,216,218],{"class":47,"line":217},9,[45,219,220],{"class":88},"   equally bright whether the hue is blue, red, or green. Deriving palettes by\n",[45,222,224],{"class":47,"line":223},10,[45,225,226],{"class":88},"   tweaking L is now mathematically sound. *\u002F\n",[228,229,231],"h3",{"id":230},"anti-pattern-hsl-for-derived-palettes","Anti-pattern: HSL for derived palettes",[32,233,234],{"language":34},[36,235,237],{"className":38,"code":236,"language":34,"meta":40,"style":40},"\u002F* ❌ HSL lightness is NOT perceptual. These two \"50% lightness\" colors look wildly different: *\u002F\n--yellow: hsl(60, 100%, 50%);   \u002F* appears near-white-bright *\u002F\n--blue:   hsl(240, 100%, 50%);  \u002F* appears mid-dark *\u002F\n\u002F* Deriving a \"lighter\" shade by bumping lightness 10% gives inconsistent results per hue. *\u002F\n",[42,238,239,244,252,260],{"__ignoreMap":40},[45,240,241],{"class":47,"line":48},[45,242,243],{"class":88},"\u002F* ❌ HSL lightness is NOT perceptual. These two \"50% lightness\" colors look wildly different: *\u002F\n",[45,245,246,249],{"class":47,"line":59},[45,247,248],{"class":55},"--yellow: hsl(60, 100%, 50%);   ",[45,250,251],{"class":88},"\u002F* appears near-white-bright *\u002F\n",[45,253,254,257],{"class":47,"line":92},[45,255,256],{"class":55},"--blue:   hsl(240, 100%, 50%);  ",[45,258,259],{"class":88},"\u002F* appears mid-dark *\u002F\n",[45,261,262],{"class":47,"line":148},[45,263,264],{"class":88},"\u002F* Deriving a \"lighter\" shade by bumping lightness 10% gives inconsistent results per hue. *\u002F\n",[32,266,267],{"language":34},[36,268,270],{"className":38,"code":269,"language":34,"meta":40,"style":40},"\u002F* ✓ OKLCH: same L = same perceived brightness. Tint\u002Fshade derivation is uniform. *\u002F\n--base:  oklch(0.62 0.18 245);\n--tint:  oklch(0.72 0.14 245);   \u002F* +0.10 L → consistent perceptual lightening *\u002F\n--shade: oklch(0.52 0.16 245);   \u002F* -0.10 L → consistent perceptual darkening *\u002F\n",[42,271,272,277,293,312],{"__ignoreMap":40},[45,273,274],{"class":47,"line":48},[45,275,276],{"class":88},"\u002F* ✓ OKLCH: same L = same perceived brightness. Tint\u002Fshade derivation is uniform. *\u002F\n",[45,278,279,282,285,287,290],{"class":47,"line":59},[45,280,281],{"class":55},"--base:  oklch(0",[45,283,284],{"class":51},".62",[45,286,189],{"class":55},[45,288,289],{"class":51},".18",[45,291,292],{"class":55}," 245);\n",[45,294,295,298,301,303,306,309],{"class":47,"line":92},[45,296,297],{"class":55},"--tint:  oklch(0",[45,299,300],{"class":51},".72",[45,302,189],{"class":55},[45,304,305],{"class":51},".14",[45,307,308],{"class":55}," 245);   ",[45,310,311],{"class":88},"\u002F* +0.10 L → consistent perceptual lightening *\u002F\n",[45,313,314,317,320,322,325,327],{"class":47,"line":148},[45,315,316],{"class":55},"--shade: oklch(0",[45,318,319],{"class":51},".52",[45,321,189],{"class":55},[45,323,324],{"class":51},".16",[45,326,308],{"class":55},[45,328,329],{"class":88},"\u002F* -0.10 L → consistent perceptual darkening *\u002F\n",[27,331,333,336,337,340],{"id":332},"opacity-vs-rgba-what-gets-transparent",[42,334,335],{},"opacity"," vs ",[42,338,339],{},"rgba"," — what gets transparent",[32,342,343],{"language":34},[36,344,346],{"className":38,"code":345,"language":34,"meta":40,"style":40},".card { opacity: 0.5; }               \u002F* ENTIRE element + ALL children → 50% transparent. Text too. *\u002F\n.card { background: rgb(0 0 0 \u002F 0.5); } \u002F* only the background color is 50% transparent. Children opaque. *\u002F\n\u002F* Use rgb()\u002Foklch() with alpha for semi-transparent surfaces. Use opacity only when you\n   mean the whole subtree (including text) should fade. opacity \u003C 1 also creates a stacking context. *\u002F\n",[42,347,348,369,403,408],{"__ignoreMap":40},[45,349,350,353,356,358,360,363,366],{"class":47,"line":48},[45,351,352],{"class":51},".card",[45,354,355],{"class":55}," { ",[45,357,335],{"class":69},[45,359,66],{"class":55},[45,361,362],{"class":69},"0.5",[45,364,365],{"class":55},"; }               ",[45,367,368],{"class":88},"\u002F* ENTIRE element + ALL children → 50% transparent. Text too. *\u002F\n",[45,370,371,373,375,378,380,383,385,388,390,392,395,397,400],{"class":47,"line":59},[45,372,352],{"class":51},[45,374,355],{"class":55},[45,376,377],{"class":69},"background",[45,379,66],{"class":55},[45,381,382],{"class":69},"rgb",[45,384,73],{"class":55},[45,386,387],{"class":69},"0",[45,389,189],{"class":69},[45,391,189],{"class":69},[45,393,394],{"class":55}," \u002F ",[45,396,362],{"class":69},[45,398,399],{"class":55},"); } ",[45,401,402],{"class":88},"\u002F* only the background color is 50% transparent. Children opaque. *\u002F\n",[45,404,405],{"class":47,"line":92},[45,406,407],{"class":88},"\u002F* Use rgb()\u002Foklch() with alpha for semi-transparent surfaces. Use opacity only when you\n",[45,409,410],{"class":47,"line":148},[45,411,412],{"class":88},"   mean the whole subtree (including text) should fade. opacity \u003C 1 also creates a stacking context. *\u002F\n",[27,414,416],{"id":415},"units-what-each-is-relative-to","Units — what each is relative to",[32,418,420],{"language":419},"text",[36,421,425],{"className":422,"code":424,"language":419,"meta":40},[423],"language-text","px    → CSS pixels (1\u002F96 inch). Absolute. Ignores user font-size setting (a11y issue for text).\nem    → parent's font-size. COMPOUNDS: 1.2em inside 1.2em = 1.44× root. Trap for nested components.\nrem   → root (html) font-size. Does NOT compound. Consistent. Use for font-size and spacing.\n%     → parent's SAME-AXIS dimension. width:% → parent width. height:% → parent height (needs defined parent height).\nvw\u002Fvh → 1% of viewport. ⚠️ 100vw INCLUDES scrollbar on desktop → horizontal overflow.\ndvh\u002Fdvw → dynamic viewport (adjusts when mobile address bar shows\u002Fhides). Use instead of vh\u002Fvw.\nsvh   → smallest viewport (all browser chrome visible). Stable, no jump.\nlvh   → largest viewport (all chrome hidden). = old vh behavior.\nch    → width of \"0\" glyph in current font. Use for max-width:60ch (reading measure).\ncqw\u002Fcqi → 1% of query container's inline size. Container-relative fluid type.\n",[42,426,424],{"__ignoreMap":40},[228,428,430],{"id":429},"anti-pattern-em-for-font-size-in-nested-components","Anti-pattern: em for font-size in nested components",[32,432,433],{"language":34},[36,434,436],{"className":38,"code":435,"language":34,"meta":40,"style":40},"\u002F* ❌ em compounds through nesting → 1.2 × 1.2 × 1.2 = 1.728× in a deeply nested structure *\u002F\n.card { font-size: 1.2em; }       \u002F* 1.2× parent *\u002F\n.card .body { font-size: 1.2em; } \u002F* 1.2× .card → 1.44× root *\u002F\n.card .body p { font-size: 1.2em; } \u002F* 1.728× root — unintended escalation *\u002F\n",[42,437,438,443,465,488],{"__ignoreMap":40},[45,439,440],{"class":47,"line":48},[45,441,442],{"class":88},"\u002F* ❌ em compounds through nesting → 1.2 × 1.2 × 1.2 = 1.728× in a deeply nested structure *\u002F\n",[45,444,445,447,449,452,454,457,459,462],{"class":47,"line":59},[45,446,352],{"class":51},[45,448,355],{"class":55},[45,450,451],{"class":69},"font-size",[45,453,66],{"class":55},[45,455,456],{"class":69},"1.2",[45,458,18],{"class":128},[45,460,461],{"class":55},"; }       ",[45,463,464],{"class":88},"\u002F* 1.2× parent *\u002F\n",[45,466,467,469,472,474,476,478,480,482,485],{"class":47,"line":92},[45,468,352],{"class":51},[45,470,471],{"class":51}," .body",[45,473,355],{"class":55},[45,475,451],{"class":69},[45,477,66],{"class":55},[45,479,456],{"class":69},[45,481,18],{"class":128},[45,483,484],{"class":55},"; } ",[45,486,487],{"class":88},"\u002F* 1.2× .card → 1.44× root *\u002F\n",[45,489,490,492,494,498,500,502,504,506,508,510],{"class":47,"line":148},[45,491,352],{"class":51},[45,493,471],{"class":51},[45,495,497],{"class":496},"sk71V"," p",[45,499,355],{"class":55},[45,501,451],{"class":69},[45,503,66],{"class":55},[45,505,456],{"class":69},[45,507,18],{"class":128},[45,509,484],{"class":55},[45,511,512],{"class":88},"\u002F* 1.728× root — unintended escalation *\u002F\n",[32,514,515],{"language":34},[36,516,518],{"className":38,"code":517,"language":34,"meta":40,"style":40},"\u002F* ✓ rem is root-relative → never compounds regardless of nesting depth *\u002F\n.card { font-size: 1.125rem; }       \u002F* 18px (at 16px root) *\u002F\n.card .body { font-size: 1rem; }     \u002F* 16px — stays at root regardless of nesting *\u002F\n\u002F* Use em ONLY for component-internal spacing that should scale with the element's own font-size. *\u002F\n",[42,519,520,525,546,569],{"__ignoreMap":40},[45,521,522],{"class":47,"line":48},[45,523,524],{"class":88},"\u002F* ✓ rem is root-relative → never compounds regardless of nesting depth *\u002F\n",[45,526,527,529,531,533,535,538,541,543],{"class":47,"line":59},[45,528,352],{"class":51},[45,530,355],{"class":55},[45,532,451],{"class":69},[45,534,66],{"class":55},[45,536,537],{"class":69},"1.125",[45,539,540],{"class":128},"rem",[45,542,461],{"class":55},[45,544,545],{"class":88},"\u002F* 18px (at 16px root) *\u002F\n",[45,547,548,550,552,554,556,558,561,563,566],{"class":47,"line":92},[45,549,352],{"class":51},[45,551,471],{"class":51},[45,553,355],{"class":55},[45,555,451],{"class":69},[45,557,66],{"class":55},[45,559,560],{"class":69},"1",[45,562,540],{"class":128},[45,564,565],{"class":55},"; }     ",[45,567,568],{"class":88},"\u002F* 16px — stays at root regardless of nesting *\u002F\n",[45,570,571],{"class":47,"line":148},[45,572,573],{"class":88},"\u002F* Use em ONLY for component-internal spacing that should scale with the element's own font-size. *\u002F\n",[27,575,577,111,580,111,583,111,586,589],{"id":576},"calc-min-max-clamp-the-responsive-math-engine",[42,578,579],{},"calc()",[42,581,582],{},"min()",[42,584,585],{},"max()",[42,587,588],{},"clamp()"," — the responsive math engine",[32,591,592],{"language":34},[36,593,595],{"className":38,"code":594,"language":34,"meta":40,"style":40},":root {\n  \u002F* Fluid type: clamp(min, preferred, max). The preferred formula scales with viewport.\n     No media queries. Bounds ensure a11y (never below 1rem = respects user setting). *\u002F\n  --font-body: clamp(1rem, 0.9rem + 0.5vw, 1.25rem);\n  --fluid-space: clamp(1rem, 2vw + 1rem, 3rem);\n\n  \u002F* calc mixes units. ⚠️ spaces required around + and - or it's invalid. *\u002F\n  --sidebar-offset: calc(100% - var(--sidebar-w) - var(--gap));\n  --neg-calc: calc(var(--n) * 1px);  \u002F* multiply unitless var by 1px to add a unit *\u002F\n}\n.layout {\n  \u002F* min(): picks the smaller → caps at 600px but shrinks on narrow screens *\u002F\n  width: min(100%, 600px);\n  \u002F* max(): picks the larger → floor at 1rem, grows with viewport *\u002F\n  font-size: max(1rem, 2vw);\n}\n",[42,596,597,603,608,613,655,693,699,704,747,780,784,792,798,824,830,855],{"__ignoreMap":40},[45,598,599,601],{"class":47,"line":48},[45,600,52],{"class":51},[45,602,56],{"class":55},[45,604,605],{"class":47,"line":59},[45,606,607],{"class":88},"  \u002F* Fluid type: clamp(min, preferred, max). The preferred formula scales with viewport.\n",[45,609,610],{"class":47,"line":92},[45,611,612],{"class":88},"     No media queries. Bounds ensure a11y (never below 1rem = respects user setting). *\u002F\n",[45,614,615,618,620,623,625,627,629,631,634,636,639,642,645,647,650,652],{"class":47,"line":148},[45,616,617],{"class":62},"  --font-body",[45,619,66],{"class":55},[45,621,622],{"class":69},"clamp",[45,624,73],{"class":55},[45,626,560],{"class":69},[45,628,540],{"class":128},[45,630,111],{"class":55},[45,632,633],{"class":69},"0.9",[45,635,540],{"class":128},[45,637,638],{"class":62}," +",[45,640,641],{"class":69}," 0.5",[45,643,644],{"class":128},"vw",[45,646,111],{"class":55},[45,648,649],{"class":69},"1.25",[45,651,540],{"class":128},[45,653,654],{"class":55},");\n",[45,656,657,660,662,664,666,668,670,672,675,677,679,682,684,686,689,691],{"class":47,"line":174},[45,658,659],{"class":62},"  --fluid-space",[45,661,66],{"class":55},[45,663,622],{"class":69},[45,665,73],{"class":55},[45,667,560],{"class":69},[45,669,540],{"class":128},[45,671,111],{"class":55},[45,673,674],{"class":69},"2",[45,676,644],{"class":128},[45,678,638],{"class":62},[45,680,681],{"class":69}," 1",[45,683,540],{"class":128},[45,685,111],{"class":55},[45,687,688],{"class":69},"3",[45,690,540],{"class":128},[45,692,654],{"class":55},[45,694,695],{"class":47,"line":199},[45,696,698],{"emptyLinePlaceholder":697},true,"\n",[45,700,701],{"class":47,"line":205},[45,702,703],{"class":88},"  \u002F* calc mixes units. ⚠️ spaces required around + and - or it's invalid. *\u002F\n",[45,705,706,709,711,714,716,719,721,724,727,729,732,734,737,739,741,744],{"class":47,"line":211},[45,707,708],{"class":62},"  --sidebar-offset",[45,710,66],{"class":55},[45,712,713],{"class":69},"calc",[45,715,73],{"class":55},[45,717,718],{"class":69},"100",[45,720,129],{"class":128},[45,722,723],{"class":128}," -",[45,725,726],{"class":69}," var",[45,728,73],{"class":55},[45,730,731],{"class":62},"--sidebar-w",[45,733,122],{"class":55},[45,735,736],{"class":128},"-",[45,738,726],{"class":69},[45,740,73],{"class":55},[45,742,743],{"class":62},"--gap",[45,745,746],{"class":55},"));\n",[45,748,749,752,754,756,758,760,762,765,767,770,772,775,777],{"class":47,"line":217},[45,750,751],{"class":62},"  --neg-calc",[45,753,66],{"class":55},[45,755,713],{"class":69},[45,757,73],{"class":55},[45,759,114],{"class":69},[45,761,73],{"class":55},[45,763,764],{"class":62},"--n",[45,766,122],{"class":55},[45,768,769],{"class":128},"*",[45,771,681],{"class":69},[45,773,774],{"class":128},"px",[45,776,142],{"class":55},[45,778,779],{"class":88},"\u002F* multiply unitless var by 1px to add a unit *\u002F\n",[45,781,782],{"class":47,"line":223},[45,783,202],{"class":55},[45,785,787,790],{"class":47,"line":786},11,[45,788,789],{"class":51},".layout",[45,791,56],{"class":55},[45,793,795],{"class":47,"line":794},12,[45,796,797],{"class":88},"  \u002F* min(): picks the smaller → caps at 600px but shrinks on narrow screens *\u002F\n",[45,799,801,804,806,809,811,813,815,817,820,822],{"class":47,"line":800},13,[45,802,803],{"class":69},"  width",[45,805,66],{"class":55},[45,807,808],{"class":69},"min",[45,810,73],{"class":55},[45,812,718],{"class":69},[45,814,129],{"class":128},[45,816,111],{"class":55},[45,818,819],{"class":69},"600",[45,821,774],{"class":128},[45,823,654],{"class":55},[45,825,827],{"class":47,"line":826},14,[45,828,829],{"class":88},"  \u002F* max(): picks the larger → floor at 1rem, grows with viewport *\u002F\n",[45,831,833,836,838,841,843,845,847,849,851,853],{"class":47,"line":832},15,[45,834,835],{"class":69},"  font-size",[45,837,66],{"class":55},[45,839,840],{"class":69},"max",[45,842,73],{"class":55},[45,844,560],{"class":69},[45,846,540],{"class":128},[45,848,111],{"class":55},[45,850,674],{"class":69},[45,852,644],{"class":128},[45,854,654],{"class":55},[45,856,858],{"class":47,"line":857},16,[45,859,202],{"class":55},[228,861,863],{"id":862},"the-fluid-container-pattern-no-breakpoints","The fluid container pattern (no breakpoints)",[32,865,866],{"language":34},[36,867,869],{"className":38,"code":868,"language":34,"meta":40,"style":40},".container {\n  \u002F* fill viewport, cap at 1200px, center. The only responsive container you need. *\u002F\n  width: min(100% - 2rem, 1200px);\n  margin-inline: auto;\n}\n",[42,870,871,878,883,913,926],{"__ignoreMap":40},[45,872,873,876],{"class":47,"line":48},[45,874,875],{"class":51},".container",[45,877,56],{"class":55},[45,879,880],{"class":47,"line":59},[45,881,882],{"class":88},"  \u002F* fill viewport, cap at 1200px, center. The only responsive container you need. *\u002F\n",[45,884,885,887,889,891,893,895,897,899,902,904,906,909,911],{"class":47,"line":92},[45,886,803],{"class":69},[45,888,66],{"class":55},[45,890,808],{"class":69},[45,892,73],{"class":55},[45,894,718],{"class":69},[45,896,129],{"class":128},[45,898,723],{"class":62},[45,900,901],{"class":69}," 2",[45,903,540],{"class":128},[45,905,111],{"class":55},[45,907,908],{"class":69},"1200",[45,910,774],{"class":128},[45,912,654],{"class":55},[45,914,915,918,920,923],{"class":47,"line":148},[45,916,917],{"class":69},"  margin-inline",[45,919,66],{"class":55},[45,921,922],{"class":69},"auto",[45,924,925],{"class":55},";\n",[45,927,928],{"class":47,"line":174},[45,929,202],{"class":55},[27,931,933,936],{"id":932},"currentcolor-the-inheritance-bridge",[42,934,935],{},"currentColor"," — the inheritance bridge",[32,938,939],{"language":34},[36,940,942],{"className":38,"code":941,"language":34,"meta":40,"style":40},".icon { fill: currentColor; }  \u002F* SVG fill tracks the element's computed `color` → recolor via color *\u002F\n.link { color: var(--c-primary); }\n.link .icon { fill: currentColor; }  \u002F* icon matches link color automatically. No separate --icon-color. *\u002F\n",[42,943,944,964,985],{"__ignoreMap":40},[45,945,946,949,951,954,956,958,961],{"class":47,"line":48},[45,947,948],{"class":51},".icon",[45,950,355],{"class":55},[45,952,953],{"class":69},"fill",[45,955,66],{"class":55},[45,957,935],{"class":69},[45,959,960],{"class":55},"; }  ",[45,962,963],{"class":88},"\u002F* SVG fill tracks the element's computed `color` → recolor via color *\u002F\n",[45,965,966,969,971,974,976,978,980,982],{"class":47,"line":59},[45,967,968],{"class":51},".link",[45,970,355],{"class":55},[45,972,973],{"class":69},"color",[45,975,66],{"class":55},[45,977,114],{"class":69},[45,979,73],{"class":55},[45,981,119],{"class":62},[45,983,984],{"class":55},"); }\n",[45,986,987,989,992,994,996,998,1000,1002],{"class":47,"line":92},[45,988,968],{"class":51},[45,990,991],{"class":51}," .icon",[45,993,355],{"class":55},[45,995,953],{"class":69},[45,997,66],{"class":55},[45,999,935],{"class":69},[45,1001,960],{"class":55},[45,1003,1004],{"class":88},"\u002F* icon matches link color automatically. No separate --icon-color. *\u002F\n",[27,1006,1008],{"id":1007},"tips-tricks","💡 Tips & Tricks",[1010,1011,1012,1035,1047,1067],"ul",{},[1013,1014,1015,66,1019,1022,1023,1026,1027,1030,1031,1034],"li",{},[1016,1017,1018],"strong",{},"Idiom",[42,1020,1021],{},"oklch()"," + ",[42,1024,1025],{},"color-mix()"," replaces every preprocessor color function — ",[42,1028,1029],{},"color-mix(in oklch, var(--c) 80%, white)"," derives a tint at runtime, cascades, and is JS-accessible. No ",[42,1032,1033],{},"lighten()"," mixin needed.",[1013,1036,1037,66,1039,1042,1043,1046],{},[1016,1038,1018],{},[42,1040,1041],{},"clamp(1rem, 0.5rem + 2vw, 3rem)"," for fluid type — the ",[42,1044,1045],{},"0.5rem + 2vw"," preferred value gives a linear ramp; the rem-based bounds respect the user's browser font-size setting (px bounds don't).",[1013,1048,1049,66,1051,1054,1055,1058,1059,1062,1063,1066],{},[1016,1050,1018],{},[42,1052,1053],{},"dvh"," for mobile full-height sections — ",[42,1056,1057],{},"100vh"," on mobile includes the address bar's potential space → content hidden behind chrome. ",[42,1060,1061],{},"100dvh"," adjusts dynamically. ",[42,1064,1065],{},"100svh"," for the stable smallest case.",[1013,1068,1069,66,1071,1074,1075,1078],{},[1016,1070,1018],{},[42,1072,1073],{},"ch"," for text measure — ",[42,1076,1077],{},"max-width: 65ch"," gives ~65 characters per line, the optimal reading width, regardless of font size. Better than a fixed px max-width.",[27,1080,1082],{"id":1081},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[1010,1084,1085,1102,1128,1145,1165],{},[1013,1086,1087,1093,1094,1097,1098,1101],{},[1016,1088,1089,1092],{},[42,1090,1091],{},"100vw"," includes the desktop scrollbar"," → horizontal overflow. Use ",[42,1095,1096],{},"100%"," of a full-width parent or ",[42,1099,1100],{},"dvw",".",[1013,1103,1104,66,1115,1118,1119,1122,1123,1112,1125,1127],{},[1016,1105,1106,1108,1109,1112,1113],{},[42,1107,579],{}," requires spaces around ",[42,1110,1111],{},"+","\u002F",[42,1114,736],{},[42,1116,1117],{},"calc(100%-20px)"," is invalid (parsed as a negative percentage). ",[42,1120,1121],{},"calc(100% - 20px)"," works. ",[42,1124,769],{},[42,1126,1112],{}," don't need spaces.",[1013,1129,1130,66,1133,1136,1137,1140,1141,1144],{},[1016,1131,1132],{},"Hex alpha is the last 2 digits",[42,1134,1135],{},"#ff000080"," is 50% red (",[42,1138,1139],{},"80"," hex = 128\u002F255 ≈ 50%). ",[42,1142,1143],{},"#ff0000ff"," is opaque.",[1013,1146,1147,1158,1159,1161,1162,1164],{},[1016,1148,1149,1151,1152,1155,1156],{},[42,1150,935],{}," is the ",[18,1153,1154],{},"computed"," ",[42,1157,973],{},": it changes with the element's ",[42,1160,973],{}," property — it's not a frozen reference. It also creates no stacking context (unlike ",[42,1163,335],{},").",[1013,1166,1167,66,1172,1175,1176,1179,1180,1182,1183,1186],{},[1016,1168,1169,1171],{},[42,1170,129],{}," height needs a parent with a concrete height",[42,1173,1174],{},"height: 100%"," of a ",[42,1177,1178],{},"height: auto"," parent resolves to 0. Use ",[42,1181,1061],{},", flex ",[42,1184,1185],{},"align-items: stretch",", or set the parent's height.",[27,1188,1190],{"id":1189},"spot-the-bug","🧠 Spot the Bug",[32,1192,1193],{"language":34},[36,1194,1196],{"className":38,"code":1195,"language":34,"meta":40,"style":40},".hero { height: 100vh; }\n",[42,1197,1198],{"__ignoreMap":40},[45,1199,1200,1203,1205,1208,1210,1212,1215],{"class":47,"line":48},[45,1201,1202],{"class":51},".hero",[45,1204,355],{"class":55},[45,1206,1207],{"class":69},"height",[45,1209,66],{"class":55},[45,1211,718],{"class":69},[45,1213,1214],{"class":128},"vh",[45,1216,1217],{"class":55},"; }\n",[1219,1220,1221,1225],"details",{},[1222,1223,1224],"summary",{},"Answer",[14,1226,1227,1229,1230,1233,1234,1237,1238,1240,1241,1243],{},[42,1228,1057],{}," on mobile browsers is the ",[18,1231,1232],{},"large"," viewport — it includes space the address bar ",[18,1235,1236],{},"could"," occupy. When the address bar is visible (the common case), the hero is taller than the visible area → the bottom is behind the address bar \u002F cut off. Fix: ",[42,1239,1061],{}," (dynamic — resizes when chrome shows\u002Fhides) or ",[42,1242,1065],{}," (smallest — stable, always fits).",[1245,1246,1247],"style",{},"html pre.shiki code .sIsaT, html code.shiki .sIsaT{--shiki-default:#6F42C1;--shiki-github-dark:#B392F0}html pre.shiki code .ssxIu, html code.shiki .ssxIu{--shiki-default:#24292E;--shiki-github-dark:#E1E4E8}html pre.shiki code .sCrzJ, html code.shiki .sCrzJ{--shiki-default:#E36209;--shiki-github-dark:#FFAB70}html pre.shiki code .snvgF, html code.shiki .snvgF{--shiki-default:#005CC5;--shiki-github-dark:#79B8FF}html pre.shiki code .sdCPZ, html code.shiki .sdCPZ{--shiki-default:#6A737D;--shiki-github-dark:#6A737D}html pre.shiki code .svdQ7, html code.shiki .svdQ7{--shiki-default:#D73A49;--shiki-github-dark:#F97583}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 .github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html.github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html pre.shiki code .sk71V, html code.shiki .sk71V{--shiki-default:#22863A;--shiki-github-dark:#85E89D}",{"title":40,"searchDepth":59,"depth":59,"links":1249},[1250,1253,1255,1258,1262,1264,1265,1266],{"id":29,"depth":59,"text":30,"children":1251},[1252],{"id":230,"depth":92,"text":231},{"id":332,"depth":59,"text":1254},"opacity vs rgba — what gets transparent",{"id":415,"depth":59,"text":416,"children":1256},[1257],{"id":429,"depth":92,"text":430},{"id":576,"depth":59,"text":1259,"children":1260},"calc(), min(), max(), clamp() — the responsive math engine",[1261],{"id":862,"depth":92,"text":863},{"id":932,"depth":59,"text":1263},"currentColor — the inheritance bridge",{"id":1007,"depth":59,"text":1008},{"id":1081,"depth":59,"text":1082},{"id":1189,"depth":59,"text":1190},"OKLCH perceptual uniformity, em\u002Frem compounding, viewport units vs dynamic dvh, and calc\u002Fmin\u002Fmax\u002Fclamp as the responsive math engine. Code-first reference with color-mix and fluid-type production patterns.","md",{},"\u002Fcss\u002F04-colors-and-units",{"title":5,"description":1267},"css\u002F04-colors-and-units","mYRLlyuFmZOwx-hr8u-jAdTbS32dqX5M8vRRG4HOLQM",1789924644819]