[{"data":1,"prerenderedAt":1556},["ShallowReactive",2],{"page-\u002Fcss\u002F17-modern-css-features":3},{"id":4,"title":5,"body":6,"description":1549,"extension":1550,"meta":1551,"navigation":405,"path":1552,"seo":1553,"stem":1554,"__hash__":1555},"content\u002Fcss\u002F17-modern-css-features.md","17 — Modern CSS (2023+): Nesting, :has(), Layers, Style Queries & color-mix",{"type":7,"value":8,"toc":1526},"minimark",[9,18,31,40,244,250,412,420,486,494,635,639,695,701,838,848,898,902,1022,1026,1051,1082,1089,1166,1170,1243,1247,1356,1360,1405,1522],[10,11,13,14,17],"h1",{"id":12},"_17-modern-css-2023-nesting-layers-style-queries-color-mix","17 — Modern CSS (2023+): Nesting, ",[15,16],"has",{},"(), Layers, Style Queries & color-mix",[19,20,21,22,26,27,30],"p",{},"CSS post-2023 replaced JavaScript and preprocessor workarounds with native features. Nesting, ",[23,24,25],"code",{},":has()"," (the parent selector), cascade layers, container style queries, ",[23,28,29],{},"color-mix()",", and scroll-driven animations are now shipping in all major browsers. This chapter covers the features that change how you architect CSS.",[32,33,35,36,39],"h2",{"id":34},"native-nesting-is-the-parent-selector","Native Nesting — ",[23,37,38],{},"&"," is the parent selector",[41,42,44],"code-wrapper",{"language":43},"css",[45,46,50],"pre",{"className":47,"code":48,"language":43,"meta":49,"style":49},"language-css shiki shiki-themes github-light github-dark",".card {\n  padding: 1rem;\n  & .title { font-size: 1.5rem; }      \u002F* = .card .title (descendant) *\u002F\n  & p { line-height: 1.6; }            \u002F* = .card p *\u002F\n  &:hover { background: #f0f0f0; }     \u002F* = .card:hover (compound — needs &) *\u002F\n  &.active { border-color: red; }      \u002F* = .card.active *\u002F\n  @media (min-width: 768px) { & { padding: 2rem; } }  \u002F* nesting @media inside rules *\u002F\n}\n\u002F* Without &, a nested selector is a DESCENDANT: .title inside .card = .card .title.\n   Use & for compound selectors (&:hover, &.active, & + .sibling). *\u002F\n","",[23,51,52,65,85,114,139,164,187,226,232,238],{"__ignoreMap":49},[53,54,57,61],"span",{"class":55,"line":56},"line",1,[53,58,60],{"class":59},"sIsaT",".card",[53,62,64],{"class":63},"ssxIu"," {\n",[53,66,68,72,75,78,82],{"class":55,"line":67},2,[53,69,71],{"class":70},"snvgF","  padding",[53,73,74],{"class":63},": ",[53,76,77],{"class":70},"1",[53,79,81],{"class":80},"svdQ7","rem",[53,83,84],{"class":63},";\n",[53,86,88,91,94,97,100,102,105,107,110],{"class":55,"line":87},3,[53,89,90],{"class":63},"  & .",[53,92,93],{"class":70},"title",[53,95,96],{"class":63}," { ",[53,98,99],{"class":70},"font-size",[53,101,74],{"class":63},[53,103,104],{"class":70},"1.5",[53,106,81],{"class":80},[53,108,109],{"class":63},"; }      ",[53,111,113],{"class":112},"sdCPZ","\u002F* = .card .title (descendant) *\u002F\n",[53,115,117,120,123,125,128,130,133,136],{"class":55,"line":116},4,[53,118,119],{"class":63},"  & ",[53,121,19],{"class":122},"sk71V",[53,124,96],{"class":63},[53,126,127],{"class":70},"line-height",[53,129,74],{"class":63},[53,131,132],{"class":70},"1.6",[53,134,135],{"class":63},"; }            ",[53,137,138],{"class":112},"\u002F* = .card p *\u002F\n",[53,140,142,145,148,150,153,155,158,161],{"class":55,"line":141},5,[53,143,144],{"class":63},"  &",[53,146,147],{"class":59},":hover",[53,149,96],{"class":63},[53,151,152],{"class":70},"background",[53,154,74],{"class":63},[53,156,157],{"class":70},"#f0f0f0",[53,159,160],{"class":63},"; }     ",[53,162,163],{"class":112},"\u002F* = .card:hover (compound — needs &) *\u002F\n",[53,165,167,169,172,174,177,179,182,184],{"class":55,"line":166},6,[53,168,144],{"class":63},[53,170,171],{"class":59},".active",[53,173,96],{"class":63},[53,175,176],{"class":70},"border-color",[53,178,74],{"class":63},[53,180,181],{"class":70},"red",[53,183,109],{"class":63},[53,185,186],{"class":112},"\u002F* = .card.active *\u002F\n",[53,188,190,193,196,199,201,204,207,210,213,215,218,220,223],{"class":55,"line":189},7,[53,191,192],{"class":80},"  @media",[53,194,195],{"class":63}," (",[53,197,198],{"class":70},"min-width",[53,200,74],{"class":63},[53,202,203],{"class":70},"768",[53,205,206],{"class":80},"px",[53,208,209],{"class":63},") { & { ",[53,211,212],{"class":70},"padding",[53,214,74],{"class":63},[53,216,217],{"class":70},"2",[53,219,81],{"class":80},[53,221,222],{"class":63},"; } }  ",[53,224,225],{"class":112},"\u002F* nesting @media inside rules *\u002F\n",[53,227,229],{"class":55,"line":228},8,[53,230,231],{"class":63},"}\n",[53,233,235],{"class":55,"line":234},9,[53,236,237],{"class":112},"\u002F* Without &, a nested selector is a DESCENDANT: .title inside .card = .card .title.\n",[53,239,241],{"class":55,"line":240},10,[53,242,243],{"class":112},"   Use & for compound selectors (&:hover, &.active, & + .sibling). *\u002F\n",[32,245,247,249],{"id":246},"has-the-relational-pseudo-class-parent-selector",[23,248,25],{}," — the relational pseudo-class (parent selector)",[41,251,252],{"language":43},[45,253,255],{"className":47,"code":254,"language":43,"meta":49,"style":49},"\u002F* Style a card based on its DESCENDANTS — the long-requested \"parent selector\". *\u002F\n.card:has(img) { padding-block-start: 0; }           \u002F* card with an image *\u002F\n.form-section:has(.error) { border-color: red; }      \u002F* section containing an error *\u002F\nli:has(ul) { font-weight: bold; }                    \u002F* list item that has a nested list *\u002F\n.card:not(:has(img)) { padding: 2rem; }               \u002F* card WITHOUT an image *\u002F\nh2:has(+ p) { margin-block-end: 0; }                 \u002F* heading directly followed by a paragraph *\u002F\n\n\u002F* Specificity: :has() adds its ARGUMENT's specificity. .card:has(.error) = .card + .error = (0,2,0). *\u002F\n",[23,256,257,262,290,313,342,372,401,407],{"__ignoreMap":49},[53,258,259],{"class":55,"line":56},[53,260,261],{"class":112},"\u002F* Style a card based on its DESCENDANTS — the long-requested \"parent selector\". *\u002F\n",[53,263,264,267,270,273,276,279,281,284,287],{"class":55,"line":67},[53,265,266],{"class":59},".card:has",[53,268,269],{"class":63},"(",[53,271,272],{"class":122},"img",[53,274,275],{"class":63},") { ",[53,277,278],{"class":70},"padding-block-start",[53,280,74],{"class":63},[53,282,283],{"class":70},"0",[53,285,286],{"class":63},"; }           ",[53,288,289],{"class":112},"\u002F* card with an image *\u002F\n",[53,291,292,295,297,300,302,304,306,308,310],{"class":55,"line":87},[53,293,294],{"class":59},".form-section:has",[53,296,269],{"class":63},[53,298,299],{"class":59},".error",[53,301,275],{"class":63},[53,303,176],{"class":70},[53,305,74],{"class":63},[53,307,181],{"class":70},[53,309,109],{"class":63},[53,311,312],{"class":112},"\u002F* section containing an error *\u002F\n",[53,314,315,318,321,323,326,328,331,333,336,339],{"class":55,"line":116},[53,316,317],{"class":122},"li",[53,319,320],{"class":59},":has",[53,322,269],{"class":63},[53,324,325],{"class":122},"ul",[53,327,275],{"class":63},[53,329,330],{"class":70},"font-weight",[53,332,74],{"class":63},[53,334,335],{"class":70},"bold",[53,337,338],{"class":63},"; }                    ",[53,340,341],{"class":112},"\u002F* list item that has a nested list *\u002F\n",[53,343,344,347,349,351,353,355,358,360,362,364,366,369],{"class":55,"line":141},[53,345,346],{"class":59},".card:not",[53,348,269],{"class":63},[53,350,320],{"class":59},[53,352,269],{"class":63},[53,354,272],{"class":122},[53,356,357],{"class":63},")) { ",[53,359,212],{"class":70},[53,361,74],{"class":63},[53,363,217],{"class":70},[53,365,81],{"class":80},[53,367,368],{"class":63},"; }               ",[53,370,371],{"class":112},"\u002F* card WITHOUT an image *\u002F\n",[53,373,374,376,378,380,383,386,388,391,393,395,398],{"class":55,"line":166},[53,375,32],{"class":122},[53,377,320],{"class":59},[53,379,269],{"class":63},[53,381,382],{"class":80},"+",[53,384,385],{"class":122}," p",[53,387,275],{"class":63},[53,389,390],{"class":70},"margin-block-end",[53,392,74],{"class":63},[53,394,283],{"class":70},[53,396,397],{"class":63},"; }                 ",[53,399,400],{"class":112},"\u002F* heading directly followed by a paragraph *\u002F\n",[53,402,403],{"class":55,"line":189},[53,404,406],{"emptyLinePlaceholder":405},true,"\n",[53,408,409],{"class":55,"line":228},[53,410,411],{"class":112},"\u002F* Specificity: :has() adds its ARGUMENT's specificity. .card:has(.error) = .card + .error = (0,2,0). *\u002F\n",[413,414,416,417,419],"h3",{"id":415},"anti-pattern-rule-ordering","Anti-pattern: ",[15,418],{},"() rule ordering",[41,421,422],{"language":43},[45,423,425],{"className":47,"code":424,"language":43,"meta":49,"style":49},"\u002F* If the :has() rule comes BEFORE the base rule and specificity is equal, source order may bite.\n   Put the :has() override AFTER the base for clarity. *\u002F\n.form-section { border: 2px solid #ccc; }              \u002F* base FIRST *\u002F\n.form-section:has(.error) { border-color: red; }      \u002F* override AFTER — clear intent *\u002F\n",[23,426,427,432,437,465],{"__ignoreMap":49},[53,428,429],{"class":55,"line":56},[53,430,431],{"class":112},"\u002F* If the :has() rule comes BEFORE the base rule and specificity is equal, source order may bite.\n",[53,433,434],{"class":55,"line":67},[53,435,436],{"class":112},"   Put the :has() override AFTER the base for clarity. *\u002F\n",[53,438,439,442,444,447,449,451,453,456,459,462],{"class":55,"line":87},[53,440,441],{"class":59},".form-section",[53,443,96],{"class":63},[53,445,446],{"class":70},"border",[53,448,74],{"class":63},[53,450,217],{"class":70},[53,452,206],{"class":80},[53,454,455],{"class":70}," solid",[53,457,458],{"class":70}," #ccc",[53,460,461],{"class":63},"; }              ",[53,463,464],{"class":112},"\u002F* base FIRST *\u002F\n",[53,466,467,469,471,473,475,477,479,481,483],{"class":55,"line":116},[53,468,294],{"class":59},[53,470,269],{"class":63},[53,472,299],{"class":59},[53,474,275],{"class":63},[53,476,176],{"class":70},[53,478,74],{"class":63},[53,480,181],{"class":70},[53,482,109],{"class":63},[53,484,485],{"class":112},"\u002F* override AFTER — clear intent *\u002F\n",[32,487,489,490,493],{"id":488},"cascade-layers-layer-for-precedence-control","Cascade Layers — ",[23,491,492],{},"@layer"," for precedence control",[41,495,496],{"language":43},[45,497,499],{"className":47,"code":498,"language":43,"meta":49,"style":49},"@layer reset, base, components, utilities;  \u002F* order declared FIRST → fixes precedence *\u002F\n\n@layer reset { * { box-sizing: border-box; margin: 0; } }\n@layer base { body { font-size: 16px; } }\n@layer components { .btn { padding: 0.5rem 1rem; } }\n@layer utilities { .text-center { text-align: center; } }\n\n\u002F* Layer order sets precedence: utilities (last) beats components beats base, REGARDLESS of specificity.\n   A .text-center utility (0,0,1,0) beats .btn .text-center (0,0,2,0) because utilities is a later layer. *\u002F\n\u002F* Unlayered rules beat ALL layered rules — the escape hatch. *\u002F\n",[23,500,501,508,512,543,566,594,616,620,625,630],{"__ignoreMap":49},[53,502,503,505],{"class":55,"line":56},[53,504,492],{"class":80},[53,506,507],{"class":63}," reset, base, components, utilities;  \u002F* order declared FIRST → fixes precedence *\u002F\n",[53,509,510],{"class":55,"line":67},[53,511,406],{"emptyLinePlaceholder":405},[53,513,514,517,520,522,525,527,530,533,536,538,540],{"class":55,"line":87},[53,515,516],{"class":63},"@layer reset { ",[53,518,519],{"class":122},"*",[53,521,96],{"class":63},[53,523,524],{"class":70},"box-sizing",[53,526,74],{"class":63},[53,528,529],{"class":70},"border-box",[53,531,532],{"class":63},"; ",[53,534,535],{"class":70},"margin",[53,537,74],{"class":63},[53,539,283],{"class":70},[53,541,542],{"class":63},"; } }\n",[53,544,545,547,550,553,555,557,559,562,564],{"class":55,"line":116},[53,546,492],{"class":80},[53,548,549],{"class":63}," base { ",[53,551,552],{"class":122},"body",[53,554,96],{"class":63},[53,556,99],{"class":70},[53,558,74],{"class":63},[53,560,561],{"class":70},"16",[53,563,206],{"class":80},[53,565,542],{"class":63},[53,567,568,570,573,576,578,580,582,585,587,590,592],{"class":55,"line":141},[53,569,492],{"class":80},[53,571,572],{"class":63}," components { ",[53,574,575],{"class":59},".btn",[53,577,96],{"class":63},[53,579,212],{"class":70},[53,581,74],{"class":63},[53,583,584],{"class":70},"0.5",[53,586,81],{"class":80},[53,588,589],{"class":70}," 1",[53,591,81],{"class":80},[53,593,542],{"class":63},[53,595,596,598,601,604,606,609,611,614],{"class":55,"line":166},[53,597,492],{"class":80},[53,599,600],{"class":63}," utilities { ",[53,602,603],{"class":59},".text-center",[53,605,96],{"class":63},[53,607,608],{"class":70},"text-align",[53,610,74],{"class":63},[53,612,613],{"class":70},"center",[53,615,542],{"class":63},[53,617,618],{"class":55,"line":189},[53,619,406],{"emptyLinePlaceholder":405},[53,621,622],{"class":55,"line":228},[53,623,624],{"class":112},"\u002F* Layer order sets precedence: utilities (last) beats components beats base, REGARDLESS of specificity.\n",[53,626,627],{"class":55,"line":234},[53,628,629],{"class":112},"   A .text-center utility (0,0,1,0) beats .btn .text-center (0,0,2,0) because utilities is a later layer. *\u002F\n",[53,631,632],{"class":55,"line":240},[53,633,634],{"class":112},"\u002F* Unlayered rules beat ALL layered rules — the escape hatch. *\u002F\n",[32,636,638],{"id":637},"container-style-queries-2023","Container Style Queries (2023+)",[41,640,641],{"language":43},[45,642,644],{"className":47,"code":643,"language":43,"meta":49,"style":49},"\u002F* Query a container's custom PROPERTIES (not just size). A card adapts to its container's --theme. *\u002F\n@container style(--theme: dark) {\n  .card { background: #222; color: #eee; }\n}\n\u002F* You can query custom properties, not standard properties (like color). Support: Chrome 111+, Safari 17.4+. *\u002F\n",[23,645,646,651,659,686,690],{"__ignoreMap":49},[53,647,648],{"class":55,"line":56},[53,649,650],{"class":112},"\u002F* Query a container's custom PROPERTIES (not just size). A card adapts to its container's --theme. *\u002F\n",[53,652,653,656],{"class":55,"line":67},[53,654,655],{"class":80},"@container",[53,657,658],{"class":63}," style(--theme: dark) {\n",[53,660,661,664,666,668,670,673,675,678,680,683],{"class":55,"line":87},[53,662,663],{"class":59},"  .card",[53,665,96],{"class":63},[53,667,152],{"class":70},[53,669,74],{"class":63},[53,671,672],{"class":70},"#222",[53,674,532],{"class":63},[53,676,677],{"class":70},"color",[53,679,74],{"class":63},[53,681,682],{"class":70},"#eee",[53,684,685],{"class":63},"; }\n",[53,687,688],{"class":55,"line":116},[53,689,231],{"class":63},[53,691,692],{"class":55,"line":141},[53,693,694],{"class":112},"\u002F* You can query custom properties, not standard properties (like color). Support: Chrome 111+, Safari 17.4+. *\u002F\n",[32,696,698,700],{"id":697},"color-mix-derive-shades-at-runtime",[23,699,29],{}," — derive shades at runtime",[41,702,703],{"language":43},[45,704,706],{"className":47,"code":705,"language":43,"meta":49,"style":49},":root {\n  --primary: oklch(0.62 0.18 245);\n  --primary-hover: color-mix(in oklch, var(--primary) 85%, white);   \u002F* lighter shade *\u002F\n  --primary-press: color-mix(in oklch, var(--primary) 75%, black);   \u002F* darker shade *\u002F\n  \u002F* No preprocessor needed — derive tints\u002Fshades from one token, at runtime, cascading. *\u002F\n}\n",[23,707,708,715,740,789,829,834],{"__ignoreMap":49},[53,709,710,713],{"class":55,"line":56},[53,711,712],{"class":59},":root",[53,714,64],{"class":63},[53,716,717,721,723,726,728,731,734,737],{"class":55,"line":67},[53,718,720],{"class":719},"sCrzJ","  --primary",[53,722,74],{"class":63},[53,724,725],{"class":70},"oklch",[53,727,269],{"class":63},[53,729,730],{"class":70},"0.62",[53,732,733],{"class":70}," 0.18",[53,735,736],{"class":70}," 245",[53,738,739],{"class":63},");\n",[53,741,742,745,747,750,752,755,758,761,764,766,769,772,775,778,780,783,786],{"class":55,"line":87},[53,743,744],{"class":719},"  --primary-hover",[53,746,74],{"class":63},[53,748,749],{"class":70},"color-mix",[53,751,269],{"class":63},[53,753,754],{"class":719},"in",[53,756,757],{"class":719}," oklch",[53,759,760],{"class":63},", ",[53,762,763],{"class":70},"var",[53,765,269],{"class":63},[53,767,768],{"class":719},"--primary",[53,770,771],{"class":63},") ",[53,773,774],{"class":70},"85",[53,776,777],{"class":80},"%",[53,779,760],{"class":63},[53,781,782],{"class":70},"white",[53,784,785],{"class":63},");   ",[53,787,788],{"class":112},"\u002F* lighter shade *\u002F\n",[53,790,791,794,796,798,800,802,804,806,808,810,812,814,817,819,821,824,826],{"class":55,"line":116},[53,792,793],{"class":719},"  --primary-press",[53,795,74],{"class":63},[53,797,749],{"class":70},[53,799,269],{"class":63},[53,801,754],{"class":719},[53,803,757],{"class":719},[53,805,760],{"class":63},[53,807,763],{"class":70},[53,809,269],{"class":63},[53,811,768],{"class":719},[53,813,771],{"class":63},[53,815,816],{"class":70},"75",[53,818,777],{"class":80},[53,820,760],{"class":63},[53,822,823],{"class":70},"black",[53,825,785],{"class":63},[53,827,828],{"class":112},"\u002F* darker shade *\u002F\n",[53,830,831],{"class":55,"line":141},[53,832,833],{"class":112},"  \u002F* No preprocessor needed — derive tints\u002Fshades from one token, at runtime, cascading. *\u002F\n",[53,835,836],{"class":55,"line":166},[53,837,231],{"class":63},[32,839,841,844,845],{"id":840},"text-wrap-balance-pretty",[23,842,843],{},"text-wrap: balance"," \u002F ",[23,846,847],{},"pretty",[41,849,850],{"language":43},[45,851,853],{"className":47,"code":852,"language":43,"meta":49,"style":49},"h1, h2 { text-wrap: balance; }  \u002F* even line lengths for headings — no manual \u003Cbr> *\u002F\np { text-wrap: pretty; }        \u002F* avoid a single word on the last line (orphans) *\u002F\n\u002F* Free typography polish. The browser optimizes wrapping. *\u002F\n",[23,854,855,879,893],{"__ignoreMap":49},[53,856,857,859,861,863,865,868,870,873,876],{"class":55,"line":56},[53,858,10],{"class":122},[53,860,760],{"class":63},[53,862,32],{"class":122},[53,864,96],{"class":63},[53,866,867],{"class":70},"text-wrap",[53,869,74],{"class":63},[53,871,872],{"class":70},"balance",[53,874,875],{"class":63},"; }  ",[53,877,878],{"class":112},"\u002F* even line lengths for headings — no manual \u003Cbr> *\u002F\n",[53,880,881,883,885,887,890],{"class":55,"line":67},[53,882,19],{"class":122},[53,884,96],{"class":63},[53,886,867],{"class":70},[53,888,889],{"class":63},": pretty; }        ",[53,891,892],{"class":112},"\u002F* avoid a single word on the last line (orphans) *\u002F\n",[53,894,895],{"class":55,"line":87},[53,896,897],{"class":112},"\u002F* Free typography polish. The browser optimizes wrapping. *\u002F\n",[32,899,901],{"id":900},"scroll-driven-animations-2023","Scroll-Driven Animations (2023+)",[41,903,904],{"language":43},[45,905,907],{"className":47,"code":906,"language":43,"meta":49,"style":49},"@keyframes progress { from { transform: scaleX(0); } to { transform: scaleX(1); } }\n.progress-bar {\n  animation: progress linear;\n  animation-timeline: scroll(root block);  \u002F* driven by scroll position, no JS *\u002F\n  transform-origin: inline-start;\n}\n\u002F* A progress bar that fills as you scroll — pure CSS. Support: Chrome 115+, Safari 17.4+. *\u002F\n",[23,908,909,957,964,979,1001,1013,1017],{"__ignoreMap":49},[53,910,911,914,917,919,922,924,927,929,932,934,936,939,942,944,946,948,950,952,954],{"class":55,"line":56},[53,912,913],{"class":80},"@keyframes",[53,915,916],{"class":719}," progress",[53,918,96],{"class":63},[53,920,921],{"class":59},"from",[53,923,96],{"class":63},[53,925,926],{"class":70},"transform",[53,928,74],{"class":63},[53,930,931],{"class":70},"scaleX",[53,933,269],{"class":63},[53,935,283],{"class":70},[53,937,938],{"class":63},"); } ",[53,940,941],{"class":59},"to",[53,943,96],{"class":63},[53,945,926],{"class":70},[53,947,74],{"class":63},[53,949,931],{"class":70},[53,951,269],{"class":63},[53,953,77],{"class":70},[53,955,956],{"class":63},"); } }\n",[53,958,959,962],{"class":55,"line":67},[53,960,961],{"class":59},".progress-bar",[53,963,64],{"class":63},[53,965,966,969,971,974,977],{"class":55,"line":87},[53,967,968],{"class":70},"  animation",[53,970,74],{"class":63},[53,972,973],{"class":70},"progress",[53,975,976],{"class":70}," linear",[53,978,84],{"class":63},[53,980,981,984,986,989,992,995,998],{"class":55,"line":116},[53,982,983],{"class":70},"  animation-timeline",[53,985,74],{"class":63},[53,987,988],{"class":70},"scroll",[53,990,991],{"class":63},"(root ",[53,993,994],{"class":70},"block",[53,996,997],{"class":63},");  ",[53,999,1000],{"class":112},"\u002F* driven by scroll position, no JS *\u002F\n",[53,1002,1003,1006,1008,1011],{"class":55,"line":141},[53,1004,1005],{"class":70},"  transform-origin",[53,1007,74],{"class":63},[53,1009,1010],{"class":70},"inline-start",[53,1012,84],{"class":63},[53,1014,1015],{"class":55,"line":166},[53,1016,231],{"class":63},[53,1018,1019],{"class":55,"line":189},[53,1020,1021],{"class":112},"\u002F* A progress bar that fills as you scroll — pure CSS. Support: Chrome 115+, Safari 17.4+. *\u002F\n",[32,1023,1025],{"id":1024},"view-transitions-api","View Transitions API",[41,1027,1028],{"language":43},[45,1029,1031],{"className":47,"code":1030,"language":43,"meta":49,"style":49},"::view-transition-old(root), ::view-transition-new(root) { animation-duration: 0.3s; }\n",[23,1032,1033],{"__ignoreMap":49},[53,1034,1035,1038,1041,1043,1046,1049],{"class":55,"line":56},[53,1036,1037],{"class":63},"::view-transition-old(root), ::view-transition-new(root) { ",[53,1039,1040],{"class":70},"animation-duration",[53,1042,74],{"class":63},[53,1044,1045],{"class":70},"0.3",[53,1047,1048],{"class":80},"s",[53,1050,685],{"class":63},[41,1052,1054],{"language":1053},"javascript",[45,1055,1058],{"className":1056,"code":1057,"language":1053,"meta":49,"style":49},"language-javascript shiki shiki-themes github-light github-dark","document.startViewTransition(() => { \u002F* DOM update — API captures old\u002Fnew, animates between them *\u002F });\n",[23,1059,1060],{"__ignoreMap":49},[53,1061,1062,1065,1068,1071,1074,1076,1079],{"class":55,"line":56},[53,1063,1064],{"class":63},"document.",[53,1066,1067],{"class":59},"startViewTransition",[53,1069,1070],{"class":63},"(() ",[53,1072,1073],{"class":80},"=>",[53,1075,96],{"class":63},[53,1077,1078],{"class":112},"\u002F* DOM update — API captures old\u002Fnew, animates between them *\u002F",[53,1080,1081],{"class":63}," });\n",[32,1083,1085,1088],{"id":1084},"accent-color-theme-native-form-controls",[23,1086,1087],{},"accent-color"," — theme native form controls",[41,1090,1091],{"language":43},[45,1092,1094],{"className":47,"code":1093,"language":43,"meta":49,"style":49},"input[type=\"checkbox\"], input[type=\"radio\"], input[type=\"range\"] {\n  accent-color: var(--primary);  \u002F* colors the native control — no custom widget needed *\u002F\n}\n",[23,1095,1096,1144,1162],{"__ignoreMap":49},[53,1097,1098,1101,1104,1107,1110,1114,1117,1119,1121,1123,1125,1128,1130,1132,1134,1136,1138,1141],{"class":55,"line":56},[53,1099,1100],{"class":122},"input",[53,1102,1103],{"class":63},"[",[53,1105,1106],{"class":59},"type",[53,1108,1109],{"class":80},"=",[53,1111,1113],{"class":1112},"sJ6F3","\"checkbox\"",[53,1115,1116],{"class":63},"], ",[53,1118,1100],{"class":122},[53,1120,1103],{"class":63},[53,1122,1106],{"class":59},[53,1124,1109],{"class":80},[53,1126,1127],{"class":1112},"\"radio\"",[53,1129,1116],{"class":63},[53,1131,1100],{"class":122},[53,1133,1103],{"class":63},[53,1135,1106],{"class":59},[53,1137,1109],{"class":80},[53,1139,1140],{"class":1112},"\"range\"",[53,1142,1143],{"class":63},"] {\n",[53,1145,1146,1149,1151,1153,1155,1157,1159],{"class":55,"line":67},[53,1147,1148],{"class":70},"  accent-color",[53,1150,74],{"class":63},[53,1152,763],{"class":70},[53,1154,269],{"class":63},[53,1156,768],{"class":719},[53,1158,997],{"class":63},[53,1160,1161],{"class":112},"\u002F* colors the native control — no custom widget needed *\u002F\n",[53,1163,1164],{"class":55,"line":87},[53,1165,231],{"class":63},[32,1167,1169],{"id":1168},"tips-tricks","💡 Tips & Tricks",[325,1171,1172,1185,1200,1221,1229],{},[317,1173,1174,74,1178,1180,1181,1184],{},[1175,1176,1177],"strong",{},"Idiom",[23,1179,25],{}," for state-based styling without JS — ",[23,1182,1183],{},".card:has(.error) { border-color: red; }"," styles a card based on its contents, no JS class toggling. The \"parent selector\" finally exists.",[317,1186,1187,74,1189,1191,1192,1195,1196,1199],{},[1175,1188,1177],{},[23,1190,492],{}," to control precedence in large codebases — ",[23,1193,1194],{},"@layer reset, base, components, utilities;"," makes utilities reliably beat components regardless of specificity. The end of ",[23,1197,1198],{},"!important"," wars.",[317,1201,1202,1204,1205,1207,1208,1210,1211,760,1214,1217,1218,1220],{},[1175,1203,1177],{},": native nesting (",[23,1206,38],{},") for component-scoped styles — reduces repetition, keeps related styles together. Use ",[23,1209,38],{}," for compound selectors (",[23,1212,1213],{},"&:hover",[23,1215,1216],{},"&.active","); without ",[23,1219,38],{}," it's a descendant.",[317,1222,1223,74,1225,1228],{},[1175,1224,1177],{},[23,1226,1227],{},"color-mix(in oklch, var(--c) 80%, white)"," to derive tints\u002Fshades from one token — no preprocessor, cascades, runtime.",[317,1230,1231,74,1233,1235,1236,1238,1239,1242],{},[1175,1232,1177],{},[23,1234,843],{}," for headings, ",[23,1237,847],{}," for paragraphs — free typography polish, no JS or manual ",[23,1240,1241],{},"\u003Cbr>",".",[32,1244,1246],{"id":1245},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[325,1248,1249,1267,1278,1295,1304,1326,1339,1350],{},[317,1250,1251,74,1256,1259,1260,1263,1264,1266],{},[1175,1252,1253,1255],{},[23,1254,25],{}," specificity = its argument's specificity",[23,1257,1258],{},".card:has(.error)"," = (0,2,0), ",[23,1261,1262],{},".card:has(#x)"," = (0,1,1,0). A ",[23,1265,25],{}," with an ID inside is ID-strength.",[317,1268,1269,74,1274,1277],{},[1175,1270,1271,1273],{},[23,1272,25],{}," can't be nested in itself in all engines",[23,1275,1276],{},":has(:has(...))"," has limited support. Avoid deep nesting.",[317,1279,1280,74,1283,1286,1287,1290,1291,1294],{},[1175,1281,1282],{},"Cascade layer order is set by the FIRST declaration",[23,1284,1285],{},"@layer reset, base;"," fixes the order. Later ",[23,1288,1289],{},"@layer base { }"," appends to the existing ",[23,1292,1293],{},"base"," in its position.",[317,1296,1297,1300,1301,1303],{},[1175,1298,1299],{},"Unlayered rules beat layered rules",": a rule outside any ",[23,1302,492],{}," wins over all layered rules regardless of specificity. Layer everything or nothing for predictable precedence.",[317,1305,1306,74,1312,1315,1316,1319,1320,1315,1322,1325],{},[1175,1307,1308,1309,1311],{},"Nesting ",[23,1310,38],{}," specificity is the combined selector's",[23,1313,1314],{},"& .title"," = ",[23,1317,1318],{},".card .title"," (0,2,0). ",[23,1321,1216],{},[23,1323,1324],{},".card.active"," (0,2,0).",[317,1327,1328,74,1331,1334,1335,1338],{},[1175,1329,1330],{},"Container style queries query custom properties only",[23,1332,1333],{},"@container style(--theme: dark)"," works; ",[23,1336,1337],{},"@container style(color: red)"," doesn't.",[317,1340,1341,1346,1347,1349],{},[1175,1342,1343,1345],{},[23,1344,843],{}," is for short text"," (headings): on long paragraphs, it can look worse. Use ",[23,1348,847],{}," for paragraphs.",[317,1351,1352,1355],{},[1175,1353,1354],{},"Scroll-driven animations and view transitions are newer"," (2023+): provide a JS fallback or accept no animation on older browsers.",[32,1357,1359],{"id":1358},"spot-the-bug","🧠 Spot the Bug",[41,1361,1362],{"language":43},[45,1363,1365],{"className":47,"code":1364,"language":43,"meta":49,"style":49},".form-section:has(.error) { border-color: red; }\n.form-section { border: 2px solid #ccc; }\n",[23,1366,1367,1385],{"__ignoreMap":49},[53,1368,1369,1371,1373,1375,1377,1379,1381,1383],{"class":55,"line":56},[53,1370,294],{"class":59},[53,1372,269],{"class":63},[53,1374,299],{"class":59},[53,1376,275],{"class":63},[53,1378,176],{"class":70},[53,1380,74],{"class":63},[53,1382,181],{"class":70},[53,1384,685],{"class":63},[53,1386,1387,1389,1391,1393,1395,1397,1399,1401,1403],{"class":55,"line":67},[53,1388,441],{"class":59},[53,1390,96],{"class":63},[53,1392,446],{"class":70},[53,1394,74],{"class":63},[53,1396,217],{"class":70},[53,1398,206],{"class":80},[53,1400,455],{"class":70},[53,1402,458],{"class":70},[53,1404,685],{"class":63},[1406,1407,1408,1412,1461,1511],"details",{},[1409,1410,1411],"summary",{},"Answer",[19,1413,1414,1415,1417,1418,1422,1423,1426,1427,1429,1430,1433,1434,1436,1437,1440,1441,1443,1444,1447,1448,1450,1451,1453,1454,1457,1458,1460],{},"The ",[23,1416,25],{}," rule comes ",[1419,1420,1421],"em",{},"before"," the base rule. ",[23,1424,1425],{},".form-section:has(.error)"," has specificity (0,2,0) — it beats ",[23,1428,441],{}," (0,1,0) by specificity, so it ",[1419,1431,1432],{},"should"," win regardless of order. But the base rule uses the ",[23,1435,446],{}," ",[1419,1438,1439],{},"shorthand",", which resets ",[23,1442,176],{}," to ",[23,1445,1446],{},"#ccc",". If the ",[23,1449,25],{}," rule only sets ",[23,1452,176],{}," (not the full shorthand), and the base comes after, the shorthand's ",[23,1455,1456],{},"border-color: #ccc"," could interfere at equal or higher specificity. The clearer fix: put the base rule first, the ",[23,1459,25],{}," override after — source order expresses intent even when specificity would win:",[45,1462,1464],{"className":47,"code":1463,"language":43,"meta":49,"style":49},".form-section { border: 2px solid #ccc; }           \u002F* base first *\u002F\n.form-section:has(.error) { border-color: red; }   \u002F* override after *\u002F\n",[23,1465,1466,1489],{"__ignoreMap":49},[53,1467,1468,1470,1472,1474,1476,1478,1480,1482,1484,1486],{"class":55,"line":56},[53,1469,441],{"class":59},[53,1471,96],{"class":63},[53,1473,446],{"class":70},[53,1475,74],{"class":63},[53,1477,217],{"class":70},[53,1479,206],{"class":80},[53,1481,455],{"class":70},[53,1483,458],{"class":70},[53,1485,286],{"class":63},[53,1487,1488],{"class":112},"\u002F* base first *\u002F\n",[53,1490,1491,1493,1495,1497,1499,1501,1503,1505,1508],{"class":55,"line":67},[53,1492,294],{"class":59},[53,1494,269],{"class":63},[53,1496,299],{"class":59},[53,1498,275],{"class":63},[53,1500,176],{"class":70},[53,1502,74],{"class":63},[53,1504,181],{"class":70},[53,1506,1507],{"class":63},"; }   ",[53,1509,1510],{"class":112},"\u002F* override after *\u002F\n",[19,1512,1513,1514,1516,1517,1519,1520,1242],{},"Also verify ",[23,1515,25],{}," browser support (Chrome 105+, Safari 15.4+, Firefox 121+) and that ",[23,1518,299],{}," is actually a descendant of ",[23,1521,441],{},[1523,1524,1525],"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 .snvgF, html code.shiki .snvgF{--shiki-default:#005CC5;--shiki-github-dark:#79B8FF}html pre.shiki code .svdQ7, html code.shiki .svdQ7{--shiki-default:#D73A49;--shiki-github-dark:#F97583}html pre.shiki code .sdCPZ, html code.shiki .sdCPZ{--shiki-default:#6A737D;--shiki-github-dark:#6A737D}html pre.shiki code .sk71V, html code.shiki .sk71V{--shiki-default:#22863A;--shiki-github-dark:#85E89D}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 .sCrzJ, html code.shiki .sCrzJ{--shiki-default:#E36209;--shiki-github-dark:#FFAB70}html pre.shiki code .sJ6F3, html code.shiki .sJ6F3{--shiki-default:#032F62;--shiki-github-dark:#9ECBFF}",{"title":49,"searchDepth":67,"depth":67,"links":1527},[1528,1530,1535,1537,1538,1540,1542,1543,1544,1546,1547,1548],{"id":34,"depth":67,"text":1529},"Native Nesting — & is the parent selector",{"id":246,"depth":67,"text":1531,"children":1532},":has() — the relational pseudo-class (parent selector)",[1533],{"id":415,"depth":87,"text":1534},"Anti-pattern: () rule ordering",{"id":488,"depth":67,"text":1536},"Cascade Layers — @layer for precedence control",{"id":637,"depth":67,"text":638},{"id":697,"depth":67,"text":1539},"color-mix() — derive shades at runtime",{"id":840,"depth":67,"text":1541},"text-wrap: balance \u002F pretty",{"id":900,"depth":67,"text":901},{"id":1024,"depth":67,"text":1025},{"id":1084,"depth":67,"text":1545},"accent-color — theme native form controls",{"id":1168,"depth":67,"text":1169},{"id":1245,"depth":67,"text":1246},{"id":1358,"depth":67,"text":1359},"Native nesting with &, :has() the parent selector, cascade layer precedence, container style queries, color-mix() for derived palettes, text-wrap balance\u002Fpretty, scroll-driven animations, and view transitions. Code-first reference for post-2023 CSS.","md",{},"\u002Fcss\u002F17-modern-css-features",{"title":5,"description":1549},"css\u002F17-modern-css-features","_RledjwXWsQtwrWtyhvqsxED9JzYg_oXrK4We9zHASE",1789924647509]