[{"data":1,"prerenderedAt":1028},["ShallowReactive",2],{"blog-\u002Fblog\u002Fbuild-once-at-build-time":3},{"id":4,"title":5,"body":6,"canonical":1018,"date":1019,"description":1020,"draft":1021,"duration":1018,"extension":1022,"lang":1018,"meta":1023,"navigation":132,"path":1024,"place":1018,"placeLink":1018,"readingTime":1018,"seo":1025,"stem":1026,"__hash__":1027},"blog\u002Fblog\u002Fbuild-once-at-build-time.md","Build once, at build time",{"type":7,"value":8,"toc":1004},"minimark",[9,19,31,37,40,43,46,53,85,92,303,308,315,332,339,346,367,373,377,384,417,432,435,447,451,471,477,486,490,501,504,507,572,575,582,597,606,666,680,683,687,690,800,803,807,813,822,828,844,848,857,860,965,969,975,990,993,1000],[10,11,13,14,18],"h3",{"id":12},"what-panda-does-with-your-css-call-before-the-browser-ever-sees-it","What Panda does with your ",[15,16,17],"code",{},"css()"," call before the browser ever sees it.",[20,21,22],"p",{},[23,24],"img",{"alt":25,"fetchPriority":26,"loading":27,"src":28,"width":29,"height":30},"Build once, at build time, on a warm cream background with yellow and coral glows, surrounded by the Panda and Chakra logos, a css() sticker and a pill that reads 3.8 MB to 2 rules.","high","eager","\u002Fimages\u002Fblog\u002Fbuild-once-at-build-time\u002Fcover.svg",1200,630,[20,32,33],{},[34,35,36],"em",{},"Panda internals, part 1.",[20,38,39],{},"Earlier this year, before I worked on Panda, I tried to get Chakra to ship static CSS instead of running Emotion in the browser. My first attempt was a generator that wrote a CSS rule for every token, every utility and every condition, ahead of time. It produced 62,656 lines of CSS. 3.8 MB. Almost none of it would ever be used.",[20,41,42],{},"So, technically, build time. Just the dumb version of it. 😅",[20,44,45],{},"Panda does its work at build time too, but it starts somewhere else. It reads your code first, and only generates what you actually wrote. It reads your files, finds your Panda calls, works out their values without running anything, turns them into CSS, and leaves the browser almost nothing to do.",[20,47,48,49,52],{},"We'll follow one component through all of it. A token in ",[15,50,51],{},"tokens.ts",":",[54,55,60],"pre",{"className":56,"code":57,"language":58,"meta":59,"style":59},"language-ts shiki shiki-themes github-light github-dark","export const brand = 'red.300'\n","ts","",[15,61,62],{"__ignoreMap":59},[63,64,67,71,74,78,81],"span",{"class":65,"line":66},"line",1,[63,68,70],{"class":69},"szBVR","export",[63,72,73],{"class":69}," const",[63,75,77],{"class":76},"sj4cs"," brand",[63,79,80],{"class":69}," =",[63,82,84],{"class":83},"sZZnC"," 'red.300'\n",[20,86,87,88,91],{},"And a badge in ",[15,89,90],{},"badge.tsx"," that uses it:",[54,93,97],{"className":94,"code":95,"language":96,"meta":59,"style":59},"language-tsx shiki shiki-themes github-light github-dark","import { css } from '..\u002Fstyled-system\u002Fcss'\nimport { brand } from '.\u002Ftokens'\n\nconst size = 'lg'\n\nexport function Badge({ active }: { active: boolean }) {\n  return (\n    \u003Cspan\n      className={css({\n        color: brand,\n        padding: size === 'lg' ? '3' : '2',\n        fontWeight: active ? 'bold' : 'normal',\n      })}\n    \u002F>\n  )\n}\n","tsx",[15,98,99,114,127,134,148,153,190,199,209,227,233,260,279,285,291,297],{"__ignoreMap":59},[63,100,101,104,108,111],{"class":65,"line":66},[63,102,103],{"class":69},"import",[63,105,107],{"class":106},"sVt8B"," { css } ",[63,109,110],{"class":69},"from",[63,112,113],{"class":83}," '..\u002Fstyled-system\u002Fcss'\n",[63,115,117,119,122,124],{"class":65,"line":116},2,[63,118,103],{"class":69},[63,120,121],{"class":106}," { brand } ",[63,123,110],{"class":69},[63,125,126],{"class":83}," '.\u002Ftokens'\n",[63,128,130],{"class":65,"line":129},3,[63,131,133],{"emptyLinePlaceholder":132},true,"\n",[63,135,137,140,143,145],{"class":65,"line":136},4,[63,138,139],{"class":69},"const",[63,141,142],{"class":76}," size",[63,144,80],{"class":69},[63,146,147],{"class":83}," 'lg'\n",[63,149,151],{"class":65,"line":150},5,[63,152,133],{"emptyLinePlaceholder":132},[63,154,156,158,161,165,168,172,175,177,180,182,184,187],{"class":65,"line":155},6,[63,157,70],{"class":69},[63,159,160],{"class":69}," function",[63,162,164],{"class":163},"sScJk"," Badge",[63,166,167],{"class":106},"({ ",[63,169,171],{"class":170},"s4XuR","active",[63,173,174],{"class":106}," }",[63,176,52],{"class":69},[63,178,179],{"class":106}," { ",[63,181,171],{"class":170},[63,183,52],{"class":69},[63,185,186],{"class":76}," boolean",[63,188,189],{"class":106}," }) {\n",[63,191,193,196],{"class":65,"line":192},7,[63,194,195],{"class":69},"  return",[63,197,198],{"class":106}," (\n",[63,200,202,205],{"class":65,"line":201},8,[63,203,204],{"class":106},"    \u003C",[63,206,208],{"class":207},"s9eBZ","span\n",[63,210,212,215,218,221,224],{"class":65,"line":211},9,[63,213,214],{"class":163},"      className",[63,216,217],{"class":69},"=",[63,219,220],{"class":106},"{",[63,222,223],{"class":163},"css",[63,225,226],{"class":106},"({\n",[63,228,230],{"class":65,"line":229},10,[63,231,232],{"class":106},"        color: brand,\n",[63,234,236,239,242,245,248,251,254,257],{"class":65,"line":235},11,[63,237,238],{"class":106},"        padding: size ",[63,240,241],{"class":69},"===",[63,243,244],{"class":83}," 'lg'",[63,246,247],{"class":69}," ?",[63,249,250],{"class":83}," '3'",[63,252,253],{"class":69}," :",[63,255,256],{"class":83}," '2'",[63,258,259],{"class":106},",\n",[63,261,263,266,269,272,274,277],{"class":65,"line":262},12,[63,264,265],{"class":106},"        fontWeight: active ",[63,267,268],{"class":69},"?",[63,270,271],{"class":83}," 'bold'",[63,273,253],{"class":69},[63,275,276],{"class":83}," 'normal'",[63,278,259],{"class":106},[63,280,282],{"class":65,"line":281},13,[63,283,284],{"class":106},"      })}\n",[63,286,288],{"class":65,"line":287},14,[63,289,290],{"class":106},"    \u002F>\n",[63,292,294],{"class":65,"line":293},15,[63,295,296],{"class":106},"  )\n",[63,298,300],{"class":65,"line":299},16,[63,301,302],{"class":106},"}\n",[304,305,307],"h2",{"id":306},"why-not-just-do-it-in-the-browser","Why not just do it in the browser",[20,309,310,311,314],{},"Runtime CSS-in-JS makes your user's browser pay the rent. On every render, something has to turn style objects into CSS, hash a class name and inject a ",[15,312,313],{},"\u003Cstyle>"," tag, on the user's device.",[20,316,317,318,325,326,331],{},"That cost is well measured by now. Sam Magura, one of Emotion's maintainers, ",[319,320,324],"a",{"href":321,"rel":322},"https:\u002F\u002Fdev.to\u002Fsrmagura\u002Fwhy-were-breaking-up-wiht-css-in-js-4g9b",[323],"nofollow","cut render time from 54.3ms to 27.7ms"," by moving off it. Last month, GitHub ",[319,327,330],{"href":328,"rel":329},"https:\u002F\u002Fgithub.blog\u002Fengineering\u002Farchitecture-optimization\u002Fimproving-site-performance-by-shipping-more-css\u002F",[323],"wrote up"," moving Primer onto CSS Modules, and server rendering took 55% less time. It also doesn't fit React Server Components, which can't inject styles at render time. A plain CSS file has no such problem.",[20,333,334,335,338],{},"My Chakra experiment showed the trade honestly. The render timer went ",[34,336,337],{},"up",", from about 1ms to 2.5ms, because the browser now parses a stylesheet up front. But Total Blocking Time went from 160ms to 10ms. A little CSS parsing, in exchange for no style work during render.",[304,340,342,343],{"id":341},"before-any-of-this-styled-system","Before any of this: ",[15,344,345],{},"styled-system",[20,347,348,349,352,353,356,357,359,360,362,363,366],{},"That ",[15,350,351],{},"import { css } from '..\u002Fstyled-system\u002Fcss'"," has to come from somewhere. When you run ",[15,354,355],{},"panda codegen",", Panda writes a ",[15,358,345],{}," folder into your project, generated from your config. It holds ",[15,361,223],{},", ",[15,364,365],{},"cva",", patterns, recipes, the JSX helpers, and types for every token you defined. That's where your autocomplete comes from.",[20,368,369,370,372],{},"Keep that in mind. The ",[15,371,223],{}," you import is a small generated function, and it never creates CSS in the browser.",[304,374,376],{"id":375},"_1-read","1. Read",[20,378,379,380,383],{},"Panda only looks at the files you point it at, through ",[15,381,382],{},"include"," in your config:",[54,385,387],{"className":56,"code":386,"language":58,"meta":59,"style":59},"export default defineConfig({\n  include: ['.\u002Fsrc\u002F**\u002F*.{ts,tsx}'],\n})\n",[15,388,389,401,412],{"__ignoreMap":59},[63,390,391,393,396,399],{"class":65,"line":66},[63,392,70],{"class":69},[63,394,395],{"class":69}," default",[63,397,398],{"class":163}," defineConfig",[63,400,226],{"class":106},[63,402,403,406,409],{"class":65,"line":116},[63,404,405],{"class":106},"  include: [",[63,407,408],{"class":83},"'.\u002Fsrc\u002F**\u002F*.{ts,tsx}'",[63,410,411],{"class":106},"],\n",[63,413,414],{"class":65,"line":129},[63,415,416],{"class":106},"})\n",[20,418,419,420,425,426,431],{},"Each of those files is parsed once into a syntax tree. In v2 the parser is ",[319,421,424],{"href":422,"rel":423},"https:\u002F\u002Foxc.rs",[323],"Oxc",", written in Rust, and it's fast. In the ",[319,427,430],{"href":428,"rel":429},"https:\u002F\u002Fpanda-css.com\u002Fblog\u002Fpanda-css-v2",[323],"v2 release",", extracting 100 files went from 187ms to 7.5ms, and re-parsing a single file in watch mode dropped from about 652µs to under 2µs.",[20,433,434],{},"In most setups, Panda also looks at a file's imports before doing anything else. No Panda imports, and it can usually move on.",[20,436,437,439,440,442,443,446],{},[15,438,90],{}," imports ",[15,441,223],{}," from ",[15,444,445],{},"styled-system\u002Fcss",", so Panda keeps going.",[304,448,450],{"id":449},"_2-find","2. Find",[20,452,453,454,362,456,458,459,462,463,466,467,470],{},"Panda checks what you imported and from where: ",[15,455,223],{},[15,457,365],{},", patterns like ",[15,460,461],{},"stack",", your recipes, and JSX like ",[15,464,465],{},"styled.div"," or ",[15,468,469],{},"\u003CBox>",". Then it finds every place you call them.",[20,472,473,474,476],{},"It also checks scope. If you declare your own ",[15,475,223],{}," that shadows the import, Panda knows that one isn't Panda's and leaves it alone.",[20,478,479,480,482,483,485],{},"In ",[15,481,90],{},", it finds one ",[15,484,17],{}," call with three properties in it.",[304,487,489],{"id":488},"_3-work-out-the-values","3. Work out the values",[20,491,492,493,496,497,500],{},"This is the interesting part. Panda has the call, but ",[15,494,495],{},"color: brand"," and ",[15,498,499],{},"padding: size === 'lg' ? '3' : '2'"," aren't values yet. They're expressions.",[20,502,503],{},"Panda never runs your code. It works the values out from the syntax alone. That's a real difference from tools like Linaria, which fall back to running your code in Node when they can't work a value out.",[20,505,506],{},"Here's what happens to our three properties:",[508,509,510,531,556],"ul",{},[511,512,513,519,520,523,524,526,527,530],"li",{},[514,515,516,518],"strong",{},[15,517,495],{},"."," ",[15,521,522],{},"brand"," is imported from ",[15,525,51],{},", so Panda follows the import, sees ",[15,528,529],{},"'red.300'",", and uses that.",[511,532,533,519,538,541,542,544,545,548,549,552,553,518],{},[514,534,535,518],{},[15,536,537],{},"padding",[15,539,540],{},"size"," is a ",[15,543,139],{}," with a literal value, so Panda knows it's ",[15,546,547],{},"'lg'",". Now ",[15,550,551],{},"size === 'lg'"," can be decided, and the ternary resolves to ",[15,554,555],{},"'3'",[511,557,558,519,563,565,566,496,569,518],{},[514,559,560,518],{},[15,561,562],{},"fontWeight",[15,564,171],{}," is a prop. Panda can't know it at build time. But both branches are plain values, so it keeps both: ",[15,567,568],{},"'bold'",[15,570,571],{},"'normal'",[20,573,574],{},"That last rule has a catch. Both branches have to be workable. If either side can't be worked out, the whole ternary is dropped, not half of it.",[20,576,577,578,581],{},"Template literals, simple math and TypeScript casts like ",[15,579,580],{},"as const"," get worked out too. What doesn't:",[508,583,584,591,594],{},[511,585,586,587,590],{},"A ",[15,588,589],{},"let"," that gets reassigned somewhere.",[511,592,593],{},"A function parameter, unless its type spells out the literal values.",[511,595,596],{},"Anything that only exists at runtime. Props, state, an API response.",[20,598,599,600,605],{},"Those get skipped. Panda would rather generate nothing than guess. The Panda docs have ",[319,601,604],{"href":602,"rel":603},"https:\u002F\u002Fpanda-css.com\u002Fdocs\u002Fstyling\u002Fdynamic-styling#runtime-reference-on-known-objects",[323],"a good example"," of how that can catch you out:",[54,607,609],{"className":94,"code":608,"language":96,"meta":59,"style":59},"const colorByType = {\n  primary: 'red.300',\n  secondary: 'blue.300',\n}\n\ncss({ color: colorByType[type] ?? 'gray.100' })\n",[15,610,611,623,632,642,646,650],{"__ignoreMap":59},[63,612,613,615,618,620],{"class":65,"line":66},[63,614,139],{"class":69},[63,616,617],{"class":76}," colorByType",[63,619,80],{"class":69},[63,621,622],{"class":106}," {\n",[63,624,625,628,630],{"class":65,"line":116},[63,626,627],{"class":106},"  primary: ",[63,629,529],{"class":83},[63,631,259],{"class":106},[63,633,634,637,640],{"class":65,"line":129},[63,635,636],{"class":106},"  secondary: ",[63,638,639],{"class":83},"'blue.300'",[63,641,259],{"class":106},[63,643,644],{"class":65,"line":136},[63,645,302],{"class":106},[63,647,648],{"class":65,"line":150},[63,649,133],{"emptyLinePlaceholder":132},[63,651,652,654,657,660,663],{"class":65,"line":155},[63,653,223],{"class":163},[63,655,656],{"class":106},"({ color: colorByType[type] ",[63,658,659],{"class":69},"??",[63,661,662],{"class":83}," 'gray.100'",[63,664,665],{"class":106}," })\n",[20,667,668,671,672,675,676,679],{},[15,669,670],{},"colorByType"," is static, but ",[15,673,674],{},"type"," isn't. So the only thing Panda can extract here is the fallback, ",[15,677,678],{},"gray.100",". The other colors never make it into the CSS.",[20,681,682],{},"v1 did all of this with a JavaScript evaluator. v2 works the expressions out directly in Rust, with no interpreter involved.",[304,684,686],{"id":685},"_4-turn-it-into-css","4. Turn it into CSS",[20,688,689],{},"Now Panda has plain values. Each property, value and condition becomes an atom, each atom is kept once, and they're sorted into a fixed order and written out:",[54,691,694],{"className":692,"code":693,"language":223,"meta":59,"style":59},"language-css shiki shiki-themes github-light github-dark","@layer utilities {\n  .c_red\\.300 { color: var(--colors-red-300) }\n  .p_3 { padding: var(--spacing-3) }\n  .fw_bold { font-weight: var(--font-weights-bold) }\n  .fw_normal { font-weight: var(--font-weights-normal) }\n}\n",[15,695,696,704,735,755,776,796],{"__ignoreMap":59},[63,697,698,701],{"class":65,"line":66},[63,699,700],{"class":69},"@layer",[63,702,703],{"class":106}," utilities {\n",[63,705,706,709,712,715,717,720,723,726,729,732],{"class":65,"line":116},[63,707,708],{"class":163},"  .c_red",[63,710,711],{"class":76},"\\.",[63,713,714],{"class":163},"300",[63,716,179],{"class":106},[63,718,719],{"class":76},"color",[63,721,722],{"class":106},": ",[63,724,725],{"class":76},"var",[63,727,728],{"class":106},"(",[63,730,731],{"class":170},"--colors-red-300",[63,733,734],{"class":106},") }\n",[63,736,737,740,742,744,746,748,750,753],{"class":65,"line":129},[63,738,739],{"class":163},"  .p_3",[63,741,179],{"class":106},[63,743,537],{"class":76},[63,745,722],{"class":106},[63,747,725],{"class":76},[63,749,728],{"class":106},[63,751,752],{"class":170},"--spacing-3",[63,754,734],{"class":106},[63,756,757,760,762,765,767,769,771,774],{"class":65,"line":136},[63,758,759],{"class":163},"  .fw_bold",[63,761,179],{"class":106},[63,763,764],{"class":76},"font-weight",[63,766,722],{"class":106},[63,768,725],{"class":76},[63,770,728],{"class":106},[63,772,773],{"class":170},"--font-weights-bold",[63,775,734],{"class":106},[63,777,778,781,783,785,787,789,791,794],{"class":65,"line":150},[63,779,780],{"class":163},"  .fw_normal",[63,782,179],{"class":106},[63,784,764],{"class":76},[63,786,722],{"class":106},[63,788,725],{"class":76},[63,790,728],{"class":106},[63,792,793],{"class":170},"--font-weights-normal",[63,795,734],{"class":106},[63,797,798],{"class":65,"line":155},[63,799,302],{"class":106},[20,801,802],{},"Four rules. Both font weights are there, because Panda doesn't know which one you'll need. Nothing else is, which is the whole difference from my 62,656 lines.",[304,804,806],{"id":805},"_5-whats-left-for-the-browser","5. What's left for the browser",[20,808,809,810,812],{},"Remember the generated ",[15,811,223],{}," function? In the browser, it takes your object and returns class names. The same names the build already wrote into the stylesheet:",[54,814,816],{"className":56,"code":815,"language":58,"meta":59,"style":59},"'c_red.300 p_3 fw_bold'\n",[15,817,818],{"__ignoreMap":59},[63,819,820],{"class":65,"line":66},[63,821,815],{"class":83},[20,823,824,825,827],{},"No CSS created, no ",[15,826,313],{}," tag injected. Just a string.",[20,829,830,831,836,837,840,841,843],{},"In v2, even that can go. Turn on ",[319,832,835],{"href":833,"rel":834},"https:\u002F\u002Fpanda-css.com\u002Fdocs\u002Fstyling\u002Fsource-transforms",[323],"source transforms"," with ",[15,838,839],{},"transform: true"," on the Vite, webpack, Rollup or Bun plugin, and the bundler replaces the call with the strings Panda already worked out. The ",[15,842,171],{}," check stays, because that's still a runtime decision. It's opt-in, it doesn't work with Turbopack yet, and any call Panda couldn't resolve stays as a normal runtime call.",[304,845,847],{"id":846},"what-build-time-cant-see","What build time can't see",[20,849,850,851,856],{},"All of this only works on what's in your source. When Sage ",[319,852,855],{"href":853,"rel":854},"https:\u002F\u002Fwww.adebayosegun.com\u002Fblog\u002Fchakra-panda-and-ark-whats-the-plan",[323],"wrote about Panda"," back in 2023, it was described as \"constrained to styles that can be statically analyzed.\" That's the deal.",[20,858,859],{},"When a value really does depend on runtime, you've got a few options:",[508,861,862,887,947,959],{},[511,863,864,867,868,872,873,875,876,883,884,886],{},[514,865,866],{},"Recipes."," This is the one ",[319,869,871],{"href":602,"rel":870},[323],"the Panda docs push hardest",". For the ",[15,874,670],{}," case above, a ",[319,877,880,882],{"href":878,"rel":879},"https:\u002F\u002Fpanda-css.com\u002Fdocs\u002Frecipes\u002Fatomic-recipe",[323],[15,881,365],{}," recipe"," with a ",[15,885,674],{}," variant puts every color in the source, so all of them get extracted.",[511,888,889,892,893],{},[514,890,891],{},"CSS variables."," Set the variable at runtime, reference it in your styles:",[54,894,896],{"className":94,"code":895,"language":96,"meta":59,"style":59},"\u003Cdiv\n  className={css({ color: 'var(--accent)' })}\n  style={{ '--accent': accent }}\n\u002F>\n",[15,897,898,906,926,942],{"__ignoreMap":59},[63,899,900,903],{"class":65,"line":66},[63,901,902],{"class":106},"\u003C",[63,904,905],{"class":207},"div\n",[63,907,908,911,913,915,917,920,923],{"class":65,"line":116},[63,909,910],{"class":163},"  className",[63,912,217],{"class":69},[63,914,220],{"class":106},[63,916,223],{"class":163},[63,918,919],{"class":106},"({ color: ",[63,921,922],{"class":83},"'var(--accent)'",[63,924,925],{"class":106}," })}\n",[63,927,928,931,933,936,939],{"class":65,"line":129},[63,929,930],{"class":163},"  style",[63,932,217],{"class":69},[63,934,935],{"class":106},"{{ ",[63,937,938],{"class":83},"'--accent'",[63,940,941],{"class":106},": accent }}\n",[63,943,944],{"class":65,"line":136},[63,945,946],{"class":106},"\u002F>\n",[511,948,949,958],{},[514,950,951,518],{},[319,952,955],{"href":953,"rel":954},"https:\u002F\u002Fpanda-css.com\u002Fdocs\u002Fstyling\u002Fstatic",[323],[15,956,957],{},"staticCss"," Tell Panda in your config which styles to generate anyway.",[511,960,961,964],{},[514,962,963],{},"Tokens."," If a value can be a token, make it one. A finite set of tokens means a finite stylesheet.",[304,966,968],{"id":967},"the-whole-trip","The whole trip",[20,970,971,972,974],{},"So here's what happened to that one ",[15,973,17],{}," call.",[20,976,977,978,980,981,983,984,986,987,989],{},"It started as an object full of expressions in ",[15,979,90],{},". Panda read the file, found the call, followed ",[15,982,522],{}," into another file, decided ",[15,985,551],{},", kept both sides of ",[15,988,171],{},", and wrote four CSS rules. In the browser, all that's left is a function that hands back a string. With source transforms, not even that.",[20,991,992],{},"The browser gets a stylesheet that's already done.",[20,994,995,996,518],{},"Step 4 skipped over how atoms actually work: how Panda decides what counts as one, keeps each exactly once, and puts them in order. That's ",[319,997,999],{"href":998},"\u002Fblog\u002Fevery-style-is-an-atom","part 2",[1001,1002,1003],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-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 .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 .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}",{"title":59,"searchDepth":116,"depth":116,"links":1005},[1006,1008,1009,1011,1012,1013,1014,1015,1016,1017],{"id":12,"depth":129,"text":1007},"What Panda does with your css() call before the browser ever sees it.",{"id":306,"depth":116,"text":307},{"id":341,"depth":116,"text":1010},"Before any of this: styled-system",{"id":375,"depth":116,"text":376},{"id":449,"depth":116,"text":450},{"id":488,"depth":116,"text":489},{"id":685,"depth":116,"text":686},{"id":805,"depth":116,"text":806},{"id":846,"depth":116,"text":847},{"id":967,"depth":116,"text":968},null,"2026-10-16","What Panda actually does with your css() call at build time, step by step, and why doing that work before the browser matters.",false,"md",{},"\u002Fblog\u002Fbuild-once-at-build-time",{"title":5,"description":1020},"blog\u002Fbuild-once-at-build-time","X1yN3GNYMuehZ8VnIE0jY1rP3aJkpwWdmzDZLfixsSo",1792585585792]