"""Build web/static/prism.css from the design mockup's pier.css (2026-10-16): the Prism tokens (:root) plus the component rules (card, strip, banner, badge, button, field, table, key-value, list, scroll, chip ...) with every selector scoped under `.prism`, so the new stage-B screens use Prism while the old screens keep their look until stage D. Skipped from the mockup: global resets (body, a, headings), the app shell (.app/.top/.rail/.main/...), the modal scrim. usage: python tools/make_prism_css.py""" import re from pathlib import Path APP = Path(__file__).resolve().parents[1] SRC = APP.parents[1] / "DOC" / "01_기획" / "웹앱디자인시안" / "시안" / "pier.css" OUT = APP / "web" / "static" / "prism.css" START = "/* scope, card, strip, banner */" SKIP_PREFIX = (".scrim", ".modal") # app-shell overlays are not used in the web app def scope(selector): parts = [p.strip() for p in selector.split(",")] return ", ".join(f".prism {p}" if not p.startswith("@") else p for p in parts) css = SRC.read_text(encoding="utf-8") root = css[:css.index("* { box-sizing")] comp = css[css.index(".eyebrow {"):css.index(START)] + css[css.index(START):] # layout helpers (.row .muted .grid3 ...) + components comp = comp.replace("@media (prefers-reduced-motion: reduce) { * { animation: none !important; transition: none !important; } }", "") rules = re.findall(r"([^{}/]+)\{([^{}]*)\}", re.sub(r"/\*.*?\*/", "", comp, flags=re.S)) out = ["/* generated by tools/make_prism_css.py from DOC/01_기획/웹앱디자인시안/시안/pier.css — do not edit by hand */", '@font-face { font-family: "Pretendard"; src: url("fonts/PretendardVariable.woff2") format("woff2-variations"); font-weight: 45 920; font-display: swap; }', root.strip(), ".prism { font-family: var(--prism-font); font-size: var(--prism-type-body-sm); line-height: var(--prism-lh-body); color: var(--prism-text-body);" " font-variant-numeric: tabular-nums; margin: var(--prism-space-200) 0; }", # the page itself is the canvas (prism-app.css) ".prism * { box-sizing: border-box; }", ".prism a { color: var(--prism-text-heading); }", ".prism h1, .prism h2, .prism h3, .prism p, .prism dl, .prism dd, .prism ol, .prism ul { margin: 0; padding: 0; }", ".prism h2 { font-size: var(--prism-type-title-sm); line-height: var(--prism-lh-title); font-weight: 500; color: var(--prism-text-heading); }", ".prism h3 { font-size: var(--prism-type-body-lg); font-weight: 700; color: var(--prism-text-heading); }", ".prism textarea { height: auto; min-height: 5rem; width: 100%; font-family: var(--prism-font); }", # base.html sets a 480 px textarea for the JSON editor ".prism :focus-visible { outline: var(--prism-focus-ring) solid var(--prism-comp-line-strong); outline-offset: var(--prism-focus-offset); }", ] for sel, body in rules: sel = sel.strip() if not sel or sel.startswith(SKIP_PREFIX) or sel.startswith("@"): continue out.append(f"{scope(sel)} {{{body.strip()}}}") OUT.write_text("\n".join(out) + "\n", encoding="utf-8") print("written", OUT, len(rules), "rules read")