[{"data":1,"prerenderedAt":1271},["ShallowReactive",2],{"page-\u002Fcss\u002F21-preprocessors-and-build-tools":3},{"id":4,"title":5,"body":6,"description":1264,"extension":1265,"meta":1266,"navigation":82,"path":1267,"seo":1268,"stem":1269,"__hash__":1270},"content\u002Fcss\u002F21-preprocessors-and-build-tools.md","21 — Preprocessors & Build Tools: Sass, PostCSS, Vite & CSS Modules",{"type":7,"value":8,"toc":1249},"minimark",[9,13,17,22,388,399,492,496,501,581,661,665,768,772,832,836,888,965,969,979,983,1042,1046,1141,1145,1200,1245],[10,11,5],"h1",{"id":12},"_21-preprocessors-build-tools-sass-postcss-vite-css-modules",[14,15,16],"p",{},"Sass extends CSS with compile-time variables, nesting, mixins, and loops. PostCSS transforms standard CSS with plugins. CSS Modules scope class names per file. The modern pipeline: CSS variables (runtime) + a build tool (Vite) + PostCSS (prefixes\u002Fminify). Add Sass only if you need mixins\u002Floops.",[18,19,21],"h2",{"id":20},"sass-scss-variables-nesting-mixins-loops","Sass (SCSS) — variables, nesting, mixins, loops",[23,24,26],"code-wrapper",{"language":25},"scss",[27,28,32],"pre",{"className":29,"code":30,"language":25,"meta":31,"style":31},"language-scss shiki shiki-themes github-light github-dark","$primary: #3498db;   \u002F\u002F compile-time constant → becomes #3498db in the output. Not runtime.\n$spacing: 1rem;\n\n.card {\n  padding: $spacing;\n  &__title { font-size: 1.5rem; color: $primary; }   \u002F\u002F BEM with nesting\n  &--featured { border-color: $primary; }\n}\n\n@mixin flex-center { display: flex; align-items: center; justify-content: center; }\n.hero { @include flex-center; min-height: 100vh; }\n\n@function rem($px) { @return $px \u002F 16px * 1rem; }   \u002F\u002F build-time function\n.title { font-size: rem(24px); }                   \u002F\u002F → 1.5rem\n\n@for $i from 1 through 12 { .col-#{$i} { width: ($i \u002F 12) * 100%; } }  \u002F\u002F loop → 12 classes\n","",[33,34,35,59,77,84,94,106,145,165,171,176,216,244,249,295,322,327],"code",{"__ignoreMap":31},[36,37,40,44,48,52,55],"span",{"class":38,"line":39},"line",1,[36,41,43],{"class":42},"sCrzJ","$primary",[36,45,47],{"class":46},"ssxIu",": ",[36,49,51],{"class":50},"snvgF","#3498db",[36,53,54],{"class":46},";   ",[36,56,58],{"class":57},"sdCPZ","\u002F\u002F compile-time constant → becomes #3498db in the output. Not runtime.\n",[36,60,62,65,67,70,74],{"class":38,"line":61},2,[36,63,64],{"class":42},"$spacing",[36,66,47],{"class":46},[36,68,69],{"class":50},"1",[36,71,73],{"class":72},"svdQ7","rem",[36,75,76],{"class":46},";\n",[36,78,80],{"class":38,"line":79},3,[36,81,83],{"emptyLinePlaceholder":82},true,"\n",[36,85,87,91],{"class":38,"line":86},4,[36,88,90],{"class":89},"sIsaT",".card",[36,92,93],{"class":46}," {\n",[36,95,97,100,102,104],{"class":38,"line":96},5,[36,98,99],{"class":50},"  padding",[36,101,47],{"class":46},[36,103,64],{"class":42},[36,105,76],{"class":46},[36,107,109,113,116,119,122,124,127,129,132,135,137,139,142],{"class":38,"line":108},6,[36,110,112],{"class":111},"sk71V","  &",[36,114,115],{"class":89},"__title",[36,117,118],{"class":46}," { ",[36,120,121],{"class":50},"font-size",[36,123,47],{"class":46},[36,125,126],{"class":50},"1.5",[36,128,73],{"class":72},[36,130,131],{"class":46},"; ",[36,133,134],{"class":50},"color",[36,136,47],{"class":46},[36,138,43],{"class":42},[36,140,141],{"class":46},"; }   ",[36,143,144],{"class":57},"\u002F\u002F BEM with nesting\n",[36,146,148,150,153,155,158,160,162],{"class":38,"line":147},7,[36,149,112],{"class":111},[36,151,152],{"class":42},"--featured",[36,154,118],{"class":46},[36,156,157],{"class":50},"border-color",[36,159,47],{"class":46},[36,161,43],{"class":42},[36,163,164],{"class":46},"; }\n",[36,166,168],{"class":38,"line":167},8,[36,169,170],{"class":46},"}\n",[36,172,174],{"class":38,"line":173},9,[36,175,83],{"emptyLinePlaceholder":82},[36,177,179,182,185,187,190,192,195,197,200,202,205,207,210,212,214],{"class":38,"line":178},10,[36,180,181],{"class":72},"@mixin",[36,183,184],{"class":89}," flex-center",[36,186,118],{"class":46},[36,188,189],{"class":50},"display",[36,191,47],{"class":46},[36,193,194],{"class":50},"flex",[36,196,131],{"class":46},[36,198,199],{"class":50},"align-items",[36,201,47],{"class":46},[36,203,204],{"class":50},"center",[36,206,131],{"class":46},[36,208,209],{"class":50},"justify-content",[36,211,47],{"class":46},[36,213,204],{"class":50},[36,215,164],{"class":46},[36,217,219,222,224,227,229,231,234,236,239,242],{"class":38,"line":218},11,[36,220,221],{"class":89},".hero",[36,223,118],{"class":46},[36,225,226],{"class":72},"@include",[36,228,184],{"class":89},[36,230,131],{"class":46},[36,232,233],{"class":50},"min-height",[36,235,47],{"class":46},[36,237,238],{"class":50},"100",[36,240,241],{"class":72},"vh",[36,243,164],{"class":46},[36,245,247],{"class":38,"line":246},12,[36,248,83],{"emptyLinePlaceholder":82},[36,250,252,255,258,261,264,267,270,273,276,279,282,285,288,290,292],{"class":38,"line":251},13,[36,253,254],{"class":72},"@function",[36,256,257],{"class":50}," rem",[36,259,260],{"class":46},"(",[36,262,263],{"class":42},"$px",[36,265,266],{"class":46},") { ",[36,268,269],{"class":72},"@return",[36,271,272],{"class":42}," $px",[36,274,275],{"class":72}," \u002F",[36,277,278],{"class":50}," 16",[36,280,281],{"class":72},"px",[36,283,284],{"class":72}," *",[36,286,287],{"class":50}," 1",[36,289,73],{"class":72},[36,291,141],{"class":46},[36,293,294],{"class":57},"\u002F\u002F build-time function\n",[36,296,298,301,303,305,307,309,311,314,316,319],{"class":38,"line":297},14,[36,299,300],{"class":89},".title",[36,302,118],{"class":46},[36,304,121],{"class":50},[36,306,47],{"class":46},[36,308,73],{"class":50},[36,310,260],{"class":46},[36,312,313],{"class":50},"24",[36,315,281],{"class":72},[36,317,318],{"class":46},"); }                   ",[36,320,321],{"class":57},"\u002F\u002F → 1.5rem\n",[36,323,325],{"class":38,"line":324},15,[36,326,83],{"emptyLinePlaceholder":82},[36,328,330,333,336,339,341,344,347,349,352,355,357,360,363,366,368,370,373,376,379,382,385],{"class":38,"line":329},16,[36,331,332],{"class":72},"@for",[36,334,335],{"class":42}," $i",[36,337,338],{"class":72}," from",[36,340,287],{"class":50},[36,342,343],{"class":72}," through",[36,345,346],{"class":50}," 12",[36,348,118],{"class":46},[36,350,351],{"class":89},".col-",[36,353,354],{"class":42},"#{$i}",[36,356,118],{"class":46},[36,358,359],{"class":50},"width",[36,361,362],{"class":46},": (",[36,364,365],{"class":42},"$i",[36,367,275],{"class":72},[36,369,346],{"class":50},[36,371,372],{"class":46},") ",[36,374,375],{"class":72},"*",[36,377,378],{"class":50}," 100",[36,380,381],{"class":72},"%",[36,383,384],{"class":46},"; } }  ",[36,386,387],{"class":57},"\u002F\u002F loop → 12 classes\n",[18,389,391,394,395,398],{"id":390},"use-modern-vs-import-deprecated",[33,392,393],{},"@use"," (modern) vs ",[33,396,397],{},"@import"," (deprecated)",[23,400,401],{"language":25},[27,402,404],{"className":29,"code":403,"language":25,"meta":31,"style":31},"\u002F\u002F _variables.scss\n$primary: #3498db;\n\n\u002F\u002F main.scss\n@use 'variables' as *;   \u002F\u002F imports $primary into scope. Scoped, loads once.\n\u002F\u002F or\n@use 'variables';        \u002F\u002F access as variables.$primary (namespaced)\n\n\u002F\u002F _index.scss (barrel)\n@forward 'variables';\n@forward 'mixins';\n",[33,405,406,411,421,425,430,448,453,465,469,474,483],{"__ignoreMap":31},[36,407,408],{"class":38,"line":39},[36,409,410],{"class":57},"\u002F\u002F _variables.scss\n",[36,412,413,415,417,419],{"class":38,"line":61},[36,414,43],{"class":42},[36,416,47],{"class":46},[36,418,51],{"class":50},[36,420,76],{"class":46},[36,422,423],{"class":38,"line":79},[36,424,83],{"emptyLinePlaceholder":82},[36,426,427],{"class":38,"line":86},[36,428,429],{"class":57},"\u002F\u002F main.scss\n",[36,431,432,434,438,441,443,445],{"class":38,"line":96},[36,433,393],{"class":72},[36,435,437],{"class":436},"sJ6F3"," 'variables'",[36,439,440],{"class":72}," as",[36,442,284],{"class":50},[36,444,54],{"class":46},[36,446,447],{"class":57},"\u002F\u002F imports $primary into scope. Scoped, loads once.\n",[36,449,450],{"class":38,"line":108},[36,451,452],{"class":57},"\u002F\u002F or\n",[36,454,455,457,459,462],{"class":38,"line":147},[36,456,393],{"class":72},[36,458,437],{"class":436},[36,460,461],{"class":46},";        ",[36,463,464],{"class":57},"\u002F\u002F access as variables.$primary (namespaced)\n",[36,466,467],{"class":38,"line":167},[36,468,83],{"emptyLinePlaceholder":82},[36,470,471],{"class":38,"line":173},[36,472,473],{"class":57},"\u002F\u002F _index.scss (barrel)\n",[36,475,476,479,481],{"class":38,"line":178},[36,477,478],{"class":72},"@forward",[36,480,437],{"class":436},[36,482,76],{"class":46},[36,484,485,487,490],{"class":38,"line":218},[36,486,478],{"class":72},[36,488,489],{"class":436}," 'mixins'",[36,491,76],{"class":46},[18,493,495],{"id":494},"the-sass-vs-css-variables-trap-compile-time-runtime","The Sass-vs-CSS-Variables Trap — compile-time ≠ runtime",[497,498,500],"h3",{"id":499},"anti-pattern-sass-variable-in-media-cant-change","Anti-pattern: Sass variable in @media can't change",[23,502,503],{"language":25},[27,504,506],{"className":29,"code":505,"language":25,"meta":31,"style":31},"\u002F\u002F ❌ Sass variables are COMPILE-TIME. $primary: red inside @media changes it only within that\n\u002F\u002F block's scope. By the time .button is compiled, $primary is blue (the main-scope value).\n$primary: blue;\n@media (max-width: 768px) { $primary: red; }  \u002F\u002F scoped to the @media block, doesn't persist\n.button { background: $primary; }  \u002F\u002F → background: blue; (no media query, no change)\n",[33,507,508,513,518,529,562],{"__ignoreMap":31},[36,509,510],{"class":38,"line":39},[36,511,512],{"class":57},"\u002F\u002F ❌ Sass variables are COMPILE-TIME. $primary: red inside @media changes it only within that\n",[36,514,515],{"class":38,"line":61},[36,516,517],{"class":57},"\u002F\u002F block's scope. By the time .button is compiled, $primary is blue (the main-scope value).\n",[36,519,520,522,524,527],{"class":38,"line":79},[36,521,43],{"class":42},[36,523,47],{"class":46},[36,525,526],{"class":50},"blue",[36,528,76],{"class":46},[36,530,531,534,537,540,542,545,547,549,551,553,556,559],{"class":38,"line":86},[36,532,533],{"class":72},"@media",[36,535,536],{"class":46}," (",[36,538,539],{"class":50},"max-width",[36,541,47],{"class":46},[36,543,544],{"class":50},"768",[36,546,281],{"class":72},[36,548,266],{"class":46},[36,550,43],{"class":42},[36,552,47],{"class":46},[36,554,555],{"class":50},"red",[36,557,558],{"class":46},"; }  ",[36,560,561],{"class":57},"\u002F\u002F scoped to the @media block, doesn't persist\n",[36,563,564,567,569,572,574,576,578],{"class":38,"line":96},[36,565,566],{"class":89},".button",[36,568,118],{"class":46},[36,570,571],{"class":50},"background",[36,573,47],{"class":46},[36,575,43],{"class":42},[36,577,558],{"class":46},[36,579,580],{"class":57},"\u002F\u002F → background: blue; (no media query, no change)\n",[23,582,583],{"language":25},[27,584,586],{"className":29,"code":585,"language":25,"meta":31,"style":31},"\u002F\u002F ✓ CSS variables are RUNTIME — they respond to @media at runtime.\n:root { --primary: blue; }\n@media (max-width: 768px) { :root { --primary: red; } }\n.button { background: var(--primary); }  \u002F\u002F blue desktop, red mobile — at runtime\n",[33,587,588,593,609,638],{"__ignoreMap":31},[36,589,590],{"class":38,"line":39},[36,591,592],{"class":57},"\u002F\u002F ✓ CSS variables are RUNTIME — they respond to @media at runtime.\n",[36,594,595,598,600,603,605,607],{"class":38,"line":61},[36,596,597],{"class":89},":root",[36,599,118],{"class":46},[36,601,602],{"class":42},"--primary",[36,604,47],{"class":46},[36,606,526],{"class":50},[36,608,164],{"class":46},[36,610,611,613,615,617,619,621,623,625,627,629,631,633,635],{"class":38,"line":79},[36,612,533],{"class":72},[36,614,536],{"class":46},[36,616,539],{"class":50},[36,618,47],{"class":46},[36,620,544],{"class":50},[36,622,281],{"class":72},[36,624,266],{"class":46},[36,626,597],{"class":89},[36,628,118],{"class":46},[36,630,602],{"class":42},[36,632,47],{"class":46},[36,634,555],{"class":50},[36,636,637],{"class":46},"; } }\n",[36,639,640,642,644,646,648,651,653,655,658],{"class":38,"line":86},[36,641,566],{"class":89},[36,643,118],{"class":46},[36,645,571],{"class":50},[36,647,47],{"class":46},[36,649,650],{"class":50},"var",[36,652,260],{"class":46},[36,654,602],{"class":42},[36,656,657],{"class":46},"); }  ",[36,659,660],{"class":57},"\u002F\u002F blue desktop, red mobile — at runtime\n",[18,662,664],{"id":663},"postcss-the-plugin-pipeline","PostCSS — the plugin pipeline",[23,666,668],{"language":667},"javascript",[27,669,672],{"className":670,"code":671,"language":667,"meta":31,"style":31},"language-javascript shiki shiki-themes github-light github-dark","\u002F\u002F postcss.config.js\nmodule.exports = {\n  plugins: [\n    require('autoprefixer'),                      \u002F\u002F vendor prefixes (browserslist-driven)\n    require('postcss-preset-env')({ stage: 2 }),  \u002F\u002F future CSS → today's CSS\n    require('cssnano')({ preset: 'default' }),     \u002F\u002F minify\n  ],\n};\n",[33,673,674,679,695,700,716,737,758,763],{"__ignoreMap":31},[36,675,676],{"class":38,"line":39},[36,677,678],{"class":57},"\u002F\u002F postcss.config.js\n",[36,680,681,684,687,690,693],{"class":38,"line":61},[36,682,683],{"class":50},"module",[36,685,686],{"class":46},".",[36,688,689],{"class":50},"exports",[36,691,692],{"class":72}," =",[36,694,93],{"class":46},[36,696,697],{"class":38,"line":79},[36,698,699],{"class":46},"  plugins: [\n",[36,701,702,705,707,710,713],{"class":38,"line":86},[36,703,704],{"class":89},"    require",[36,706,260],{"class":46},[36,708,709],{"class":436},"'autoprefixer'",[36,711,712],{"class":46},"),                      ",[36,714,715],{"class":57},"\u002F\u002F vendor prefixes (browserslist-driven)\n",[36,717,718,720,722,725,728,731,734],{"class":38,"line":96},[36,719,704],{"class":89},[36,721,260],{"class":46},[36,723,724],{"class":436},"'postcss-preset-env'",[36,726,727],{"class":46},")({ stage: ",[36,729,730],{"class":50},"2",[36,732,733],{"class":46}," }),  ",[36,735,736],{"class":57},"\u002F\u002F future CSS → today's CSS\n",[36,738,739,741,743,746,749,752,755],{"class":38,"line":108},[36,740,704],{"class":89},[36,742,260],{"class":46},[36,744,745],{"class":436},"'cssnano'",[36,747,748],{"class":46},")({ preset: ",[36,750,751],{"class":436},"'default'",[36,753,754],{"class":46}," }),     ",[36,756,757],{"class":57},"\u002F\u002F minify\n",[36,759,760],{"class":38,"line":147},[36,761,762],{"class":46},"  ],\n",[36,764,765],{"class":38,"line":167},[36,766,767],{"class":46},"};\n",[18,769,771],{"id":770},"vite-the-modern-build-tool","Vite — the modern build tool",[23,773,774],{"language":667},[27,775,777],{"className":670,"code":776,"language":667,"meta":31,"style":31},"\u002F\u002F vite.config.js — handles CSS\u002FSCSS\u002FPostCSS out of the box.\nexport default {\n  css: {\n    preprocessorOptions: {\n      scss: { additionalData: `@use \"variables\" as *;` },  \u002F\u002F auto-import for every SCSS file\n    },\n  },\n};\n",[33,778,779,784,794,799,804,818,823,828],{"__ignoreMap":31},[36,780,781],{"class":38,"line":39},[36,782,783],{"class":57},"\u002F\u002F vite.config.js — handles CSS\u002FSCSS\u002FPostCSS out of the box.\n",[36,785,786,789,792],{"class":38,"line":61},[36,787,788],{"class":72},"export",[36,790,791],{"class":72}," default",[36,793,93],{"class":46},[36,795,796],{"class":38,"line":79},[36,797,798],{"class":46},"  css: {\n",[36,800,801],{"class":38,"line":86},[36,802,803],{"class":46},"    preprocessorOptions: {\n",[36,805,806,809,812,815],{"class":38,"line":96},[36,807,808],{"class":46},"      scss: { additionalData: ",[36,810,811],{"class":436},"`@use \"variables\" as *;`",[36,813,814],{"class":46}," },  ",[36,816,817],{"class":57},"\u002F\u002F auto-import for every SCSS file\n",[36,819,820],{"class":38,"line":108},[36,821,822],{"class":46},"    },\n",[36,824,825],{"class":38,"line":147},[36,826,827],{"class":46},"  },\n",[36,829,830],{"class":38,"line":167},[36,831,767],{"class":46},[18,833,835],{"id":834},"css-modules-scoped-class-names","CSS Modules — scoped class names",[23,837,839],{"language":838},"css",[27,840,843],{"className":841,"code":842,"language":838,"meta":31,"style":31},"language-css shiki shiki-themes github-light github-dark","\u002F* Button.module.css — class names are scoped to this file (compiled to unique names). *\u002F\n.btn { padding: 0.5rem 1rem; }\n.primary { background: blue; }\n",[33,844,845,850,873],{"__ignoreMap":31},[36,846,847],{"class":38,"line":39},[36,848,849],{"class":57},"\u002F* Button.module.css — class names are scoped to this file (compiled to unique names). *\u002F\n",[36,851,852,855,857,860,862,865,867,869,871],{"class":38,"line":61},[36,853,854],{"class":89},".btn",[36,856,118],{"class":46},[36,858,859],{"class":50},"padding",[36,861,47],{"class":46},[36,863,864],{"class":50},"0.5",[36,866,73],{"class":72},[36,868,287],{"class":50},[36,870,73],{"class":72},[36,872,164],{"class":46},[36,874,875,878,880,882,884,886],{"class":38,"line":79},[36,876,877],{"class":89},".primary",[36,879,118],{"class":46},[36,881,571],{"class":50},[36,883,47],{"class":46},[36,885,526],{"class":50},[36,887,164],{"class":46},[23,889,890],{"language":667},[27,891,893],{"className":670,"code":892,"language":667,"meta":31,"style":31},"import styles from '.\u002FButton.module.css';\n\u003Cbutton className={`${styles.btn} ${styles.primary}`}>Click\u003C\u002Fbutton>\n\u002F\u002F Compiled: class=\"Button_btn__3a2f1 Button_primary__9b1c4\" — no collisions, no BEM needed.\n",[33,894,895,911,960],{"__ignoreMap":31},[36,896,897,900,903,906,909],{"class":38,"line":39},[36,898,899],{"class":72},"import",[36,901,902],{"class":46}," styles ",[36,904,905],{"class":72},"from",[36,907,908],{"class":436}," '.\u002FButton.module.css'",[36,910,76],{"class":46},[36,912,913,916,919,922,925,928,931,934,936,939,942,944,946,949,952,955,957],{"class":38,"line":61},[36,914,915],{"class":46},"\u003C",[36,917,918],{"class":111},"button",[36,920,921],{"class":89}," className",[36,923,924],{"class":72},"=",[36,926,927],{"class":46},"{",[36,929,930],{"class":436},"`${",[36,932,933],{"class":46},"styles",[36,935,686],{"class":436},[36,937,938],{"class":46},"btn",[36,940,941],{"class":436},"} ${",[36,943,933],{"class":46},[36,945,686],{"class":436},[36,947,948],{"class":46},"primary",[36,950,951],{"class":436},"}`",[36,953,954],{"class":46},"}>Click\u003C\u002F",[36,956,918],{"class":111},[36,958,959],{"class":46},">\n",[36,961,962],{"class":38,"line":79},[36,963,964],{"class":57},"\u002F\u002F Compiled: class=\"Button_btn__3a2f1 Button_primary__9b1c4\" — no collisions, no BEM needed.\n",[18,966,968],{"id":967},"when-to-use-what","When to Use What",[23,970,972],{"language":971},"text",[27,973,977],{"className":974,"code":976,"language":971,"meta":31},[975],"language-text","Tool             Use for\nCSS variables     Runtime theming, JS-accessible, @media-responsive\nSass variables    Build-time math, loops, mixins (compiled away)\nPostCSS           Prefixes, future CSS, minification, Tailwind\nCSS Modules       Scoped component styles (React)\nTailwind          Utility-first design system\nVite              Dev server + bundling (modern default)\n",[33,978,976],{"__ignoreMap":31},[18,980,982],{"id":981},"tips-tricks","💡 Tips & Tricks",[984,985,986,1001,1020,1025,1037],"ul",{},[987,988,989,993,994,996,997,1000],"li",{},[990,991,992],"strong",{},"Idiom",": CSS variables for theming (runtime, JS-accessible, @media-responsive) + Sass variables for build-time math\u002Fmixins — they're complementary. ",[33,995,602],{}," changes at runtime; ",[33,998,999],{},"$breakpoint"," is a compile-time constant for loops.",[987,1002,1003,47,1005,1007,1008,1010,1011,1013,1014,1016,1017,1019],{},[990,1004,992],{},[33,1006,393],{}," (not ",[33,1009,397],{},") in modern Sass — ",[33,1012,397],{}," is deprecated (pollutes global scope, loads multiple times). ",[33,1015,393],{}," is scoped and loads once. ",[33,1018,478],{}," re-exports from a barrel.",[987,1021,1022,1024],{},[990,1023,992],{},": PostCSS for prefixes + minification (Autoprefixer + cssnano) — configure once, forget. Vite\u002Fwebpack integrate it.",[987,1026,1027,1029,1030,1033,1034,686],{},[990,1028,992],{},": CSS Modules for scoped component styles in React — ",[33,1031,1032],{},"import styles from '.\u002FX.module.css'"," gives locally-scoped names (no collisions, no BEM). For Vue, use ",[33,1035,1036],{},"\u003Cstyle scoped>",[987,1038,1039,1041],{},[990,1040,992],{},": Vite for new projects — handles CSS\u002FSCSS\u002FPostCSS out of the box, native ES modules in dev (instant startup), Rollup for production. Faster and simpler than webpack.",[18,1043,1045],{"id":1044},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[984,1047,1048,1066,1081,1104,1114,1126,1135],{},[987,1049,1050,1056,1057,1059,1060,1062,1063,1065],{},[990,1051,1052,1053,1055],{},"Sass ",[33,1054,397],{}," is deprecated",": use ",[33,1058,393],{},"\u002F",[33,1061,478],{},". ",[33,1064,397],{}," pollutes the global namespace and loads files multiple times.",[987,1067,1068,47,1071,1074,1075,1077,1078,1080],{},[990,1069,1070],{},"Sass variables are compile-time",[33,1072,1073],{},"$primary: blue"," becomes ",[33,1076,526],{}," in the compiled CSS — can't change at runtime or per media query. Use CSS variables (",[33,1079,602],{},") for runtime theming.",[987,1082,1083,47,1088,1091,1092,1095,1096,1099,1100,1103],{},[990,1084,1085,1087],{},[33,1086,393],{}," namespaces by default",[33,1089,1090],{},"@use 'variables'"," → ",[33,1093,1094],{},"variables.$primary",". Use ",[33,1097,1098],{},"@use 'variables' as *"," for no namespace, or ",[33,1101,1102],{},"as v"," for a short one.",[987,1105,1106,1109,1110,1113],{},[990,1107,1108],{},"CSS Modules need a framework",": they work via the bundler (React\u002FVue\u002FNext.js). A plain ",[33,1111,1112],{},".html"," file can't use them (no scoping mechanism).",[987,1115,1116,47,1122,1125],{},[990,1117,1118,1121],{},[33,1119,1120],{},"@apply"," in Tailwind couples CSS to Tailwind",[33,1123,1124],{},"@apply mt-4;"," makes your CSS dependent on Tailwind's utilities. Use sparingly; prefer composing in JSX\u002FHTML.",[987,1127,1128,47,1131,1134],{},[990,1129,1130],{},"webpack loaders execute right-to-left",[33,1132,1133],{},"['style-loader', 'css-loader', 'postcss-loader', 'sass-loader']"," → sass-loader first (SCSS→CSS), then postcss-loader, then css-loader, then style-loader (inject).",[987,1136,1137,1140],{},[990,1138,1139],{},"Minification can break dynamic class names",": aggressive minification that removes \"unused\" CSS can misidentify JS-generated classes. Configure PurgeCSS\u002FTailwind safelist carefully.",[18,1142,1144],{"id":1143},"spot-the-bug","🧠 Spot the Bug",[23,1146,1147],{"language":25},[27,1148,1150],{"className":29,"code":1149,"language":25,"meta":31,"style":31},"$primary: blue;\n@media (max-width: 768px) { $primary: red; }\n.button { background: $primary; }\n",[33,1151,1152,1162,1186],{"__ignoreMap":31},[36,1153,1154,1156,1158,1160],{"class":38,"line":39},[36,1155,43],{"class":42},[36,1157,47],{"class":46},[36,1159,526],{"class":50},[36,1161,76],{"class":46},[36,1163,1164,1166,1168,1170,1172,1174,1176,1178,1180,1182,1184],{"class":38,"line":61},[36,1165,533],{"class":72},[36,1167,536],{"class":46},[36,1169,539],{"class":50},[36,1171,47],{"class":46},[36,1173,544],{"class":50},[36,1175,281],{"class":72},[36,1177,266],{"class":46},[36,1179,43],{"class":42},[36,1181,47],{"class":46},[36,1183,555],{"class":50},[36,1185,164],{"class":46},[36,1187,1188,1190,1192,1194,1196,1198],{"class":38,"line":79},[36,1189,566],{"class":89},[36,1191,118],{"class":46},[36,1193,571],{"class":50},[36,1195,47],{"class":46},[36,1197,43],{"class":42},[36,1199,164],{"class":46},[1201,1202,1203,1207],"details",{},[1204,1205,1206],"summary",{},"Answer",[14,1208,1209,1210,1062,1213,1216,1217,1219,1220,1223,1224,1226,1227,1229,1230,1233,1234,1237,1238,1237,1241,1244],{},"Sass variables are ",[990,1211,1212],{},"compile-time",[33,1214,1215],{},"$primary: red"," inside the ",[33,1218,533],{}," block changes the variable only within that block's scope. By the time ",[33,1221,1222],{},".button { background: $primary; }"," is compiled, ",[33,1225,43],{}," is ",[33,1228,526],{}," (the main-scope value). The compiled CSS is ",[33,1231,1232],{},"background: blue;"," with no media query — the red never applies. Sass variables can't change at runtime or per viewport. Fix: use CSS variables — ",[33,1235,1236],{},":root { --primary: blue; }"," + ",[33,1239,1240],{},"@media (max-width: 768px) { :root { --primary: red; } }",[33,1242,1243],{},"background: var(--primary)",". CSS variables are runtime and @media-responsive.",[1246,1247,1248],"style",{},"html pre.shiki code .sdCPZ, html code.shiki .sdCPZ{--shiki-default:#6A737D;--shiki-github-dark:#6A737D}html pre.shiki code .snvgF, html code.shiki .snvgF{--shiki-default:#005CC5;--shiki-github-dark:#79B8FF}html pre.shiki code .ssxIu, html code.shiki .ssxIu{--shiki-default:#24292E;--shiki-github-dark:#E1E4E8}html pre.shiki code .svdQ7, html code.shiki .svdQ7{--shiki-default:#D73A49;--shiki-github-dark:#F97583}html pre.shiki code .sIsaT, html code.shiki .sIsaT{--shiki-default:#6F42C1;--shiki-github-dark:#B392F0}html pre.shiki code .sJ6F3, html code.shiki .sJ6F3{--shiki-default:#032F62;--shiki-github-dark:#9ECBFF}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}html pre.shiki code .sCrzJ, html code.shiki .sCrzJ{--shiki-default:#E36209;--shiki-github-dark:#FFAB70}",{"title":31,"searchDepth":61,"depth":61,"links":1250},[1251,1252,1254,1257,1258,1259,1260,1261,1262,1263],{"id":20,"depth":61,"text":21},{"id":390,"depth":61,"text":1253},"@use (modern) vs @import (deprecated)",{"id":494,"depth":61,"text":495,"children":1255},[1256],{"id":499,"depth":79,"text":500},{"id":663,"depth":61,"text":664},{"id":770,"depth":61,"text":771},{"id":834,"depth":61,"text":835},{"id":967,"depth":61,"text":968},{"id":981,"depth":61,"text":982},{"id":1044,"depth":61,"text":1045},{"id":1143,"depth":61,"text":1144},"Sass @use vs deprecated @import, compile-time Sass variables vs runtime CSS variables, PostCSS plugin chains, CSS Modules scoping, and the Sass-variable-in-@media trap. Code-first reference for the modern build pipeline.","md",{},"\u002Fcss\u002F21-preprocessors-and-build-tools",{"title":5,"description":1264},"css\u002F21-preprocessors-and-build-tools","ylhStCR_PAH9VlRPNSTsejY-7grLzw2R70dCxLOlEqY",1789924647583]