You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
전체 계획과 결정 사항은 추적 이슈(#682)에 있습니다. 조사 기준 커밋: 77daad74 (현재 main d0b84255).
결정에 따른 작업
(필수) theme이 여러 개면 theme별 변수로 출력(light-dark()는 두 개만 표현 가능)
(필수) 이름이 dark인 theme만 다크 color-scheme, 다른 이름은 devup.json에서 지정(CSS-04)
발견 항목
심각도: P0 = 흔한 사용에서 조용히 틀리거나 크래시, P1 = 흔한 패턴이 명시적 에러로 막히거나 드물게 조용히 틀림, P2 = 드문 경우·도구/문서 불일치, P3 = 있으면 좋음.
필수 (15)
CSS-01 (P0, WRONG) Same-file global CSS and imports lose author source order
근거: global-order.tsx registers imports z.css, a.css, then template globals body{color:red}, body{color:blue}. Whole-sheet output is @import "a.css";@import "z.css";...body{color:blue}body{color:red}. Thus red wins although blue is the later author declaration. StyleSheet.css stores per-file globals in a BTreeSet (libs/sheet/src/lib.rs:262), and create_css iterat…
수정 방향: Store ordered records with a stable source ordinal and deduplicate without sorting by CSS text. Preserve first occurrence for duplicate imports/font faces while retaining relative order.
CSS-02 (P0, WRONG) Named cascade-layer order is sorted instead of preserved
근거: layers.tsx declares reset before base, but output starts @layer base,reset;. Chromium showed that prelude makes a later reset rule win over base (rgb(255,0,0)), the opposite of the author-declared normal-layer precedence.
수정 방향: Track layer names in insertion order (with a membership set for deduplication) and emit the prelude in that order, including nested/user layers.
CSS-03 (P0, WRONG) Theme-specific color tokens are typed but never defined
근거: In theme-two.json, $extraOnly exists only in theme blue. getThemeInterface exposes $extraOnly, and extraction emits background:var(--extraOnly), but theme CSS defines only --primary-100 and --shared; no --extraOnly exists under either root. theme-three.json likewise drops darkOnly and solarOnly entirely.
수정 방향: Build the union of color keys. Emit default values when present and per-theme declarations for added/changed keys; do not restrict emission to keys shared with the default theme.
CSS-07 (P0, WRONG) Documented typography fields are silently ignored
근거: theme-two.json sets fontStyle:"italic" and textTransform:"uppercase". .typo-heading emits family, size, weight, line-height, and letter-spacing only. Registration succeeds without warning. Both omitted fields are explicitly listed as supported in apps/landing/src/app/(detail)/docs/devup/typography/page.mdx:69-145.
수정 방향: Add both fields to typography deserialization and declaration emission at every responsive level, with tests; preferably reject unknown typography keys rather than silently dropping them.
FN-06 (P0, WRONG) Theme tokens in globalCss() become invalid literal $token CSS
근거: globalCss({body:{color:'$text'}}) emits body{color:$text} in global-isolated.tsx
수정 방향: apply token-to-var(--token) conversion in global selector extraction, with color/length/shadow and responsive token tests
근거: globalCss({'@layer utilities':{'.x':{color:'blue'},'.y':{color:'red'}}}) emits @layer utilities{color:blue;color:red}—both selectors are absent (global-isolated.tsx)
수정 방향: treat named @layer as an at-rule wrapping a selector map, not as a selector whose value is a style object; preserve child selectors and nested ordering
SC-08 (P0, WRONG) Child selectors are emitted globally instead of scoped
근거: 70-child-selector.tsx emits .child{color:red}> p{color:blue}, with neither selector prefixed by the component's generated classes a b. 37-nested-selectors.tsx similarly emits global > p and > p:hover. These rules can style unrelated nodes globally; leading-combinator rules are also invalid/ineffective at top level.
수정 방향: Carry the owning atomic class into nested selector generation and prefix child selectors (.<owner> .child, .<owner> > p), preserving nested & relative to that selector.
CSS-04 (P1, WRONG) Non-dark named themes force dark native UI
근거: A two-theme config named default + blue emits :root[data-theme=blue]{color-scheme:dark}. With three themes, both dark and solarized emit color-scheme:dark. The docs explicitly support arbitrary variants such as blue and green, not only light/dark (apps/landing/src/app/(detail)/docs/devup/colors/page.mdx:78-103).
수정 방향: Only assign dark color-scheme from explicit theme metadata or the conventional dark variant. For arbitrary two-theme variants, emit ordinary per-theme variable overrides rather than encoding selection through light-dark().
CSS-05 (P1, WRONG) Flattened color token names collide silently
근거: theme-weird.json defines both flat "a-b":"#111" and nested "a":{"b":"#222"}. Both normalize to CSS key a-b; output contains one --a-b:#222, and the interface contains only $a.b. ColorTheme indexes entries only by flattened css_key and inserts during deserialization (libs/sheet/src/theme.rs:23-28,85-100). Hash-map iteration makes which logical token survives…
수정 방향: Detect duplicate normalized CSS keys while flattening and return a descriptive configuration error, or adopt an escaping scheme that distinguishes literal dashes/dots from nesting separators.
근거: Keys ${bad}, len.only, and shadow.only emit --${bad}, --len.only, and --shadow.only. Chromium CSSOM retained only valid --a-b; it discarded the dollar/braces and dotted declarations and any var() use, leaving the consuming declarations unset. The generated interface nevertheless advertises all keys.
수정 방향: Validate token/theme names during registration and fail with the offending path, or CSS-escape names consistently in both declaration and var() references.
근거: global-isolated.tsx emits only @import "b.css"; {url:'a.css',query:'screen'} disappears. In extract_global_style_from_expression.rs:107-120, object keys are read through o.key.as_expression(), which misses static identifier keys
수정 방향: read keys with get_string_by_property_key/get_str_by_property_key; add object/string mixed-array and query snapshots
JSX-04 (P1, WRONG) Parameterized group selectors emit syntactically invalid selectors
근거: 28-advanced-selectors.tsx: _groupNthChild={{params:['2n'],color:'orange'}} emits :is([role=group],[data-group]):nth-child .class(2n){color:orange}. Parameters attach to the generated class rather than :nth-child.
수정 방향: apply advanced-selector params before inserting the descendant marker/class for group selectors; add snapshots for several parameterized group pseudos.
근거: 30-selector-values.tsx: <Box _hover="external-class"/> and <Box _hover={['...', ...]}/> lose every string arm; the first output is <div/>, and only object arms survive responsive arrays.
수정 방향: define and implement string semantics consistently (class reference or CSS text); if string support is unintended, remove it from public types/tests and emit a build diagnostic rather than dropping it.
SC-09 (P1, WRONG) Some content strings lose quotes and become invalid CSS
근거: 39-before-content.tsx emits .b::before{content:"hello world"} but .a::after{content:→} for source content: "→"; the latter is not a valid CSS string value and is dropped by the browser.
수정 방향: Preserve/serialize CSS string tokens for content even when they contain no whitespace; add ASCII, Unicode, escaped quote, empty-string, and attr/counter regression cases.
근거: TypeScript accepts selectors={{definitelyNotASelector:{...}}} because Selectors contains (string & {}) (selector/index.ts:61-68); build emits .a:definitely-not-a-selector{color:red}. Unknown _notASelector is correctly rejected by types but lint has no selector rule.
수정 방향: Require &${string} (plus documented shorthands) in types and add lint validation for literal selector keys; build should error on invalid keys.
권장 (1)
CSS-08 (P1, WRONG) Legacy flex numeric props are converted to invalid lengths
근거: values.tsx turns flexOrder={2} into flex-order:8px and msFlex={2} into -ms-flex:8px. Chromium drops both declarations and reports CSS.supports(...,'8px') === false; modern order={2} correctly remains unitless. stroke-dasharray:8px remains valid, so this is property classification rather than generic numeric failure.
수정 방향: Add legacy flex-order to the unitless integer set and -ms-flex to the flex-number/shorthand handling used by flex, then add a table-driven numeric-property test.
근거: 03-responsive.tsx: color={[['red','blue'],'green']} produces color-0-red, color-1-blue, and color-1-green; two level-1 classes rely on CSS generation order for the winner.
수정 방향: detect an array value nested inside a responsive array and emit a precise build error instead of recursively flattening it.
JSX-08 (P2, WRONG) selectors accepts arbitrary strings but rewrites missing-& keys as invalid pseudos
근거: 06-raw-selectors.tsx: key body emits .class:body{color:pink}; :hover is silently normalized to &:hover.
수정 방향: validate selector keys, require & for raw nesting, and report malformed keys; alternatively implement standards-based nesting rather than unconditional pseudo-prefixing.
JSX-10 (P3, MISMATCH) CSS property inventory includes invalid and obsolete names
근거: 35-property-mismatch.tsx generates stroke-color:red, box-align:center, ime-mode:active, and scroll-snap-coordinate:0 0; stroke-color is not a CSS/SVG property, and the others are obsolete/nonstandard. Inventory source: libs/css/src/is_special_property.rs:448-958.
수정 방향: generate the inventory from the supported csstype surface; remove nonexistent names and clearly quarantine deprecated compatibility names.
빌드는 통과하는데 CSS가 틀리거나 무효인 문제들입니다. 확인 예:
globalCss({ body: { color: "$text" } })→body{color:$text},@layer안 selector 소실, styled 템플릿의> p가 전역 규칙으로 출력,content:hi따옴표 소실.전체 계획과 결정 사항은 추적 이슈(#682)에 있습니다. 조사 기준 커밋:
77daad74(현재 maind0b84255).결정에 따른 작업
light-dark()는 두 개만 표현 가능)dark인 theme만 다크 color-scheme, 다른 이름은 devup.json에서 지정(CSS-04)발견 항목
심각도: P0 = 흔한 사용에서 조용히 틀리거나 크래시, P1 = 흔한 패턴이 명시적 에러로 막히거나 드물게 조용히 틀림, P2 = 드문 경우·도구/문서 불일치, P3 = 있으면 좋음.
필수 (15)
global-order.tsxregisters importsz.css,a.css, then template globalsbody{color:red},body{color:blue}. Whole-sheet output is@import "a.css";@import "z.css";...body{color:blue}body{color:red}. Thus red wins although blue is the later author declaration.StyleSheet.cssstores per-file globals in aBTreeSet(libs/sheet/src/lib.rs:262), andcreate_cssiterat…layers.tsxdeclaresresetbeforebase, but output starts@layer base,reset;. Chromium showed that prelude makes a laterresetrule win overbase(rgb(255,0,0)), the opposite of the author-declared normal-layer precedence.theme-two.json,$extraOnlyexists only in themeblue.getThemeInterfaceexposes$extraOnly, and extraction emitsbackground:var(--extraOnly), but theme CSS defines only--primary-100and--shared; no--extraOnlyexists under either root.theme-three.jsonlikewise dropsdarkOnlyandsolarOnlyentirely.theme-two.jsonsetsfontStyle:"italic"andtextTransform:"uppercase"..typo-headingemits family, size, weight, line-height, and letter-spacing only. Registration succeeds without warning. Both omitted fields are explicitly listed as supported inapps/landing/src/app/(detail)/docs/devup/typography/page.mdx:69-145.globalCss()become invalid literal$tokenCSSglobalCss({body:{color:'$text'}})emitsbody{color:$text}inglobal-isolated.tsxvar(--token)conversion in global selector extraction, with color/length/shadow and responsive token tests@layerdestroys nested selectorsglobalCss({'@layer utilities':{'.x':{color:'blue'},'.y':{color:'red'}}})emits@layer utilities{color:blue;color:red}—both selectors are absent (global-isolated.tsx)@layeras an at-rule wrapping a selector map, not as a selector whose value is a style object; preserve child selectors and nested ordering70-child-selector.tsxemits.child{color:red}> p{color:blue}, with neither selector prefixed by the component's generated classesa b.37-nested-selectors.tsxsimilarly emits global> pand> p:hover. These rules can style unrelated nodes globally; leading-combinator rules are also invalid/ineffective at top level..<owner> .child,.<owner> > p), preserving nested&relative to that selector.default+blueemits:root[data-theme=blue]{color-scheme:dark}. With three themes, bothdarkandsolarizedemitcolor-scheme:dark. The docs explicitly support arbitrary variants such asblueandgreen, not only light/dark (apps/landing/src/app/(detail)/docs/devup/colors/page.mdx:78-103).darkvariant. For arbitrary two-theme variants, emit ordinary per-theme variable overrides rather than encoding selection throughlight-dark().theme-weird.jsondefines both flat"a-b":"#111"and nested"a":{"b":"#222"}. Both normalize to CSS keya-b; output contains one--a-b:#222, and the interface contains only$a.b.ColorThemeindexes entries only by flattenedcss_keyand inserts during deserialization (libs/sheet/src/theme.rs:23-28,85-100). Hash-map iteration makes which logical token survives…${bad},len.only, andshadow.onlyemit--${bad},--len.only, and--shadow.only. Chromium CSSOM retained only valid--a-b; it discarded the dollar/braces and dotted declarations and anyvar()use, leaving the consuming declarations unset. The generated interface nevertheless advertises all keys.var()references.globalCss({imports:[{url,query}]})silently drops object importsglobal-isolated.tsxemits only@import "b.css";{url:'a.css',query:'screen'}disappears. Inextract_global_style_from_expression.rs:107-120, object keys are read througho.key.as_expression(), which misses static identifier keysget_string_by_property_key/get_str_by_property_key; add object/string mixed-array and query snapshots28-advanced-selectors.tsx:_groupNthChild={{params:['2n'],color:'orange'}}emits:is([role=group],[data-group]):nth-child .class(2n){color:orange}. Parameters attach to the generated class rather than:nth-child.30-selector-values.tsx:<Box _hover="external-class"/>and<Box _hover={['...', ...]}/>lose every string arm; the first output is<div/>, and only object arms survive responsive arrays.contentstrings lose quotes and become invalid CSS39-before-content.tsxemits.b::before{content:"hello world"}but.a::after{content:→}for sourcecontent: "→"; the latter is not a valid CSS string value and is dropped by the browser.contenteven when they contain no whitespace; add ASCII, Unicode, escaped quote, empty-string, and attr/counter regression cases.selectors={{definitelyNotASelector:{...}}}becauseSelectorscontains(string & {})(selector/index.ts:61-68); build emits.a:definitely-not-a-selector{color:red}. Unknown_notASelectoris correctly rejected by types but lint has no selector rule.&${string}(plus documented shorthands) in types and add lint validation for literal selector keys; build should error on invalid keys.권장 (1)
values.tsxturnsflexOrder={2}intoflex-order:8pxandmsFlex={2}into-ms-flex:8px. Chromium drops both declarations and reportsCSS.supports(...,'8px') === false; modernorder={2}correctly remains unitless.stroke-dasharray:8pxremains valid, so this is property classification rather than generic numeric failure.flex-orderto the unitless integer set and-ms-flexto the flex-number/shorthand handling used byflex, then add a table-driven numeric-property test.선택 (3)
03-responsive.tsx:color={[['red','blue'],'green']}producescolor-0-red,color-1-blue, andcolor-1-green; two level-1 classes rely on CSS generation order for the winner.selectorsaccepts arbitrary strings but rewrites missing-&keys as invalid pseudos06-raw-selectors.tsx: keybodyemits.class:body{color:pink};:hoveris silently normalized to&:hover.&for raw nesting, and report malformed keys; alternatively implement standards-based nesting rather than unconditional pseudo-prefixing.35-property-mismatch.tsxgeneratesstroke-color:red,box-align:center,ime-mode:active, andscroll-snap-coordinate:0 0;stroke-coloris not a CSS/SVG property, and the others are obsolete/nonstandard. Inventory source:libs/css/src/is_special_property.rs:448-958.csstypesurface; remove nonexistent names and clearly quarantine deprecated compatibility names.