Skip to content

[전수조사/E] CSS 생성 정확성: 순서, layer, theme, 유효하지 않은 CSS #685

Description

@owjs3901

빌드는 통과하는데 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.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
  • FN-07 (P0, WRONG) Top-level @layer destroys 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 @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.
  • CSS-06 (P1, WRONG) Invalid token names generate declarations browsers discard
    • 근거: 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.
  • FN-05 (P1, WRONG) globalCss({imports:[{url,query}]}) silently drops object imports
    • 근거: 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.
  • 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 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.
  • TOOL-06 (P1, WRONG) Selector escape hatch accepts invalid selectors and emits invalid CSS
    • 근거: 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.

선택 (3)

  • JSX-07 (P2, WRONG) Nested responsive arrays flatten into conflicting breakpoint classes
    • 근거: 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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions