Repository navigation
[전수조사/E] CSS 생성 정확성: 순서, layer, theme, 유효하지 않은 CSS #685
Copy link
Copy link
Open
Labels
bugSomething isn't workingSomething isn't working
Description
빌드는 통과하는데 CSS가 틀리거나 무효인 문제들입니다. 확인 예: globalCss({ body: { color: "$text" } }) → body{color:$text}, @layer 안 selector 소실, styled 템플릿의 > p가 전역 규칙으로 출력, content:hi 따옴표 소실.
전체 계획과 결정 사항은 추적 이슈(#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.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… - 수정 방향: 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.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. - 수정 방향: 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,$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. - 수정 방향: 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.
- 근거: In
- CSS-07 (P0, WRONG) Documented typography fields are silently ignored
- 근거:
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. - 수정 방향: 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$tokenCSS- 근거:
globalCss({body:{color:'$text'}})emitsbody{color:$text}inglobal-isolated.tsx - 수정 방향: apply token-to-
var(--token)conversion in global selector extraction, with color/length/shadow and responsive token tests
- 근거:
- FN-07 (P0, WRONG) Top-level
@layerdestroys nested selectors- 근거:
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
@layeras 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.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. - 수정 방향: 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+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). - 수정 방향: Only assign dark color-scheme from explicit theme metadata or the conventional
darkvariant. For arbitrary two-theme variants, emit ordinary per-theme variable overrides rather than encoding selection throughlight-dark().
- 근거: A two-theme config named
- CSS-05 (P1, WRONG) Flattened color token names collide silently
- 근거:
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… - 수정 방향: 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.
- 근거:
- CSS-06 (P1, WRONG) Invalid token names generate declarations browsers discard
- 근거: Keys
${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. - 수정 방향: Validate token/theme names during registration and fail with the offending path, or CSS-escape names consistently in both declaration and
var()references.
- 근거: Keys
- FN-05 (P1, WRONG)
globalCss({imports:[{url,query}]})silently drops object imports- 근거:
global-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 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.
- 근거:
- JSX-05 (P1, WRONG) Typed selector string values disappear
- 근거:
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
contentstrings lose quotes and become invalid CSS- 근거:
39-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. - 수정 방향: Preserve/serialize CSS string tokens for
contenteven when they contain no whitespace; add ASCII, Unicode, escaped quote, empty-string, and attr/counter regression cases.
- 근거:
- TOOL-06 (P1, WRONG) Selector escape hatch accepts invalid selectors and emits invalid CSS
- 근거: TypeScript accepts
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. - 수정 방향: Require
&${string}(plus documented shorthands) in types and add lint validation for literal selector keys; build should error on invalid keys.
- 근거: TypeScript accepts
권장 (1)
- CSS-08 (P1, WRONG) Legacy flex numeric props are converted to invalid lengths
- 근거:
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. - 수정 방향: Add legacy
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)
- JSX-07 (P2, WRONG) Nested responsive arrays flatten into conflicting breakpoint classes
- 근거:
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. - 수정 방향: detect an array value nested inside a responsive array and emit a precise build error instead of recursively flattening it.
- 근거:
- JSX-08 (P2, WRONG)
selectorsaccepts arbitrary strings but rewrites missing-&keys as invalid pseudos- 근거:
06-raw-selectors.tsx: keybodyemits.class:body{color:pink};:hoveris 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.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. - 수정 방향: generate the inventory from the supported
csstypesurface; remove nonexistent names and clearly quarantine deprecated compatibility names.
- 근거:
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
bugSomething isn't workingSomething isn't working
Activity
owjs3901 commentedon Sep 30, 2026
theme 출력 작업(#698) 중 조사 목록에 없던 버그를 발견해 함께 고쳤습니다.
$text-primary처럼 대시가 들어간 token이var(--text)-primary로 깨졌습니다.VAR_RE가\$[\w.]+여서 대시를 받지 않았기 때문입니다.\$\w[\w.-]*로 수정했습니다(fix(sheet): define variant-only colors, pick color schemes by name and check token names #698).--len.only로 선언되는데 참조는var(--len-only)여서 어긋났습니다. 색과 같이 점을 대시로 바꿔 선언하도록 수정했습니다(fix(sheet): define variant-only colors, pick color schemes by name and check token names #698).후속 과제로 남긴 것입니다.
card와 shadowcard는 둘 다--card가 되어 CSS 순서로 승패가 갈립니다. 한 theme 안의 같은 종류 충돌(a-b/a.b)은 #698에서 오류로 처리했고, 종류 간 충돌은 Theme 전체를 검증하는 단계가 따로 필요합니다.fix(sheet): define variant-only colors, pick color schemes by name an…
fix(extractor): read theme tokens, import objects and @layer blocks i…
test: cover global order paths on every tarpaulin engine
test: cover the lines CI coverage formatting maps to new code
test(css): cover the descendant separator directly
test(extractor): cover composition and styled branches reported by Linux
fix(extractor): prove module effects before exact stylesheet evaluation
fix(extractor): reject recoverable source and semantic parse errors
fix(extractor): reject synthetic reflection during static evaluation
test(extractor): cover evaluation setup and naming seams
chore(wasm): record located diagnostics patch changepack
test(extractor): cover stylesheet precompute errors and import proof …
test(extractor): cover declaration and call-origin boundaries
46 remaining items
chore: merge announced dormant counter extractor milestone
test(extractor): cover unrelated late Counter naming demand
chore: merge announced partial dormant counter consumer milestone
chore: merge announced dormant counter test proof milestone
chore: merge announced dormant counter L2-L6 test milestones
chore: merge main into css output edges
fix(sheet): merge main and preserve authored global CSS order
merge: integrate styled captures with global layer order
merge: update PR #757 with PR #750
test(extractor): batch deep heap fixture construction
test(extractor): trim deep fixture loop bookkeeping
test(extractor): cover captured stylesheet contracts
test(extractor): cover bound namespace tag inlining
merge: update PR #757 with settled main
ci: allow 180 seconds per coverage test binary