Skip to content

fix: correct CSS output edges and declaration diagnostics - #759

Open
owjs3901 wants to merge 6 commits into
mainfrom
fix/css-output-edges
Open

owjs3901 wants to merge 6 commits into
mainfrom
fix/css-output-edges

Conversation

@owjs3901

@owjs3901 owjs3901 commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

Refs #685 (CSS-08, JSX-07, JSX-08, JSX-10). #702 위에 쌓은 브랜치이며 #702가 먼저 병합되어야 합니다. PR의 base는 main입니다.

요약

  • 레거시 vendor flex 숫자를 길이로 변환하지 않고 단위 없이 유지합니다.
  • 중첩 반응형 배열이 같은 브레이크포인트에 충돌하는 클래스를 만드는 대신 위치를 포함한 빌드 오류를 냅니다.
  • JSX-08은 #702의 수정으로 이미 해결됨을 원본 WASM 출력으로 확인하고 회귀 테스트를 추가합니다.
  • 알 수 없는 최신/실험적 CSS 이름은 계속 통과시킵니다. 현재 Chrome/Edge/Firefox/Safari 어디에서도 지원하지 않는 닫힌 목록만 오류로 처리하며, vendor 접두사와 문서화된 shorthand는 유지합니다.
  • Rust 1.99에서 필요한 비교 assertion 수정과 Patch changepack을 포함합니다.

동작

수정 전 기준은 #702의 d94ef3c66946e2c0e4df3b8c11e4a84c4d07dfc7입니다. 이 기준을 직접 WASM으로 빌드한 뒤 각 항목을 프로브했습니다.

항목 기준 브랜치에서 확인한 결과 변경 정책
CSS-08 flex={1}/flexGrow={2}는 이미 정상, msFlex={2}/msFlexOrder={2}/WebkitBoxFlex={2}는 8px vendor 숫자 속성은 단위 없이 유지
JSX-07 color={[["red","blue"],"green"]}는 level-1 blue와 green 클래스를 동시에 출력 중첩 배열을 위치가 있는 빌드 오류로 처리
JSX-08 selectors={{body:{color:'pink'}}}는 이미 오류, :hover와 & > p는 정상 추가 변경 없이 회귀 테스트
JSX-10 box-align:center, scroll-snap-coordinate:0 0처럼 무효 선언을 출력 확인된 dead name만 오류

브라우저 근거

2026-10-04에 MDN browser-compat-data를 조회했습니다. 표의 대상은 접두사가 없는 이름이며 -webkit-, -moz-, -ms- 등의 호환 이름은 차단하지 않습니다.

이름 현재 Chrome / Edge / Firefox / Safari 대체 속성 근거
box-align 모두 접두사 없이 지원하지 않음 align-items BCD
box-pack 모두 접두사 없이 지원하지 않음 justify-content BCD
box-flex 모두 접두사 없이 지원하지 않음 flex-grow BCD
box-flex-group 모두 접두사 없이 지원하지 않음 현대 Flexbox로 작성 BCD
box-orient 모두 접두사 없이 지원하지 않음 flex-direction, 필요한 경우 WebkitBoxOrient BCD
box-ordinal-group 모두 접두사 없이 지원하지 않음 order BCD
box-direction 모두 접두사 없이 지원하지 않음 flex-direction BCD
box-lines 모두 접두사 없이 지원하지 않음 flex-wrap BCD
flex-order BCD에 현재 속성 없음, 현대 속성은 order order BCD order
flex-positive BCD는 IE10의 -ms-flex-positive만 옛 대체 이름으로 기록 flex-grow BCD flex-grow
flex-negative 현대 속성은 flex-shrink, 옛 이름은 접두사 필요 flex-shrink BCD flex-shrink
flex-preferred-size 현대 속성은 flex-basis, 옛 이름은 접두사 필요 flex-basis BCD flex-basis
scroll-snap-coordinate Chrome/Edge/Safari 미지원, Firefox 68에서 제거 scroll-snap-align BCD 제거 PR
scroll-snap-destination Chrome/Edge/Safari 미지원, Firefox 68에서 제거 scroll-padding, scroll-snap-type BCD 제거 PR
scroll-snap-points-x 현재 네 브라우저 미지원, Firefox 68에서 제거 scroll-snap-type, scroll-snap-align BCD 제거 PR
scroll-snap-points-y 현재 네 브라우저 미지원, Firefox 68에서 제거 scroll-snap-type, scroll-snap-align BCD 제거 PR
scroll-snap-type-x 현재 네 브라우저 미지원, Firefox 68에서 제거 scroll-snap-type BCD 제거 PR
scroll-snap-type-y 현재 네 브라우저 미지원, Firefox 68에서 제거 scroll-snap-type BCD 제거 PR

원래 조사에서 무효로 분류한 stroke-color는 Safari 11.1부터 지원되므로 계속 통과합니다. ime-mode도 Firefox 3부터 지원되므로 유지합니다. 새로운 별칭은 만들지 않습니다.

새로 생기는 오류

  • 반응형 배열의 한 브레이크포인트 값이 다시 배열인 경우.
  • 위 18개 dead name을 JSX 스타일 속성, css()/styled() 객체, CSS 텍스트, globalCss() 또는 keyframes() 선언 이름으로 작성한 경우.
  • 모든 오류에는 파일:줄:열, 문제가 된 코드, 이유와 대체 방법이 포함됩니다. 직접 작성한 JSX 속성, 객체 리터럴, CSS 텍스트는 정확한 키 위치를 사용합니다. 평가·계산·import를 통해 만들어진 선언은 그 선언을 만든 원본 style/globalStyle/keyframes/styleVariants 호출이 작성된 파일의 호출 위치와 키 경로를 사용하며, 생성된 코드의 위치는 사용하지 않습니다. JSX-08의 body 오류는 #702에서 이미 생긴 오류이며 이 PR이 새로 추가하는 오류는 아닙니다.

남는 한계

  • CSS 전체를 allowlist로 검증하지 않습니다. 지원되는 실험적 속성, 알 수 없는 draft 이름, vendor 호환 속성은 그대로 출력됩니다.
  • TypeScript 타입 수정은 #738의 소유 범위로 남깁니다. 이 브랜치에서는 타입을 변경하지 않습니다.

숫자 처리에서 바뀐 대표 이름은 -ms-flex, -ms-flex-order, -ms-flex-positive, -ms-flex-negative, -webkit-box-flex, -webkit-box-ordinal-group, -ms-grid-column-span, -ms-grid-row-span, -moz-tab-size입니다. -webkit-line-clamp도 단위 없는 값이 유지되는지 검사합니다. webkit/moz/ms/o 접두사의 animation-duration, animation-delay, transition-duration, transition-delay는 모두 ms로 처리합니다. OAnimationDuration 같은 입력의 출력 이름에 빠졌던 -o-의 첫 -도 수정합니다. vendor 이름 목록 전체를 unprefixed 숫자 형식과 비교하는 테이블 테스트와 실제 WASM CSS 테스트를 함께 둡니다.

검증

  • cargo fmt --all, cargo check --workspace, Rust 1.99 strict clippy, cargo test --workspace 통과: Rust 테스트 2,932개 통과.
  • WASM 재빌드와 루트 패키지 빌드 통과. bun test: 5,474개 통과, 0 실패, 함수/라인 커버리지 100%.
  • bun lint: 오류 없음, 기준 브랜치에 있던 경고 2개만 유지.
  • commit hook의 lint/tarpaulin/Bun 테스트 통과. Windows tarpaulin과 별도로 Linux CI의 100% 기준을 확인했습니다.
  • 실제 public WASM 경로로 프로브를 실행해 아래 CSS와 위치가 포함된 오류를 확인했습니다.

수정 후 WASM CSS 발췌:

.-ms-flex-0-2--255{-ms-flex:2}
.-ms-flex-order-0-2--255{-ms-flex-order:2}
.-webkit-box-flex-0-2--255{-webkit-box-flex:2}
.-webkit-box-ordinal-group-0-2--255{-webkit-box-ordinal-group:2}
.flex-0-1--255{flex:1}
.flex-grow-0-2--255{flex-grow:2}
.padding-0-8px--255{padding:8px}
.-webkit-animation-duration-0-2ms--255{-webkit-animation-duration:2ms}
.-webkit-transition-delay-0-3ms--255{-webkit-transition-delay:3ms}
.-o-animation-duration-0-2ms--255{-o-animation-duration:2ms}
.-o-transition-delay-0-3ms--255{-o-transition-delay:3ms}
.stroke-color-0-red--255{stroke-color:red}
.ime-mode-0-active--255{ime-mode:active}
nested.tsx:2:36: `<Box>` cannot use `["red", "blue"]` at build time: responsive arrays must be flat; each entry supplies one breakpoint value or selector style object, not another array
dead-template.tsx:4:3: `styled()` cannot use `box-align` at build time: unprefixed box-align has no browser implementation; use display: flex and align-items
dead-ve.css.ts:3:24: `style()` cannot use `boxAlign` at build time: unprefixed box-align has no browser implementation; use display: flex and align-items
stylex-dead.tsx:2:45: `stylex.create()` cannot use `box-align` at build time: unprefixed box-align has no browser implementation; use display: flex and align-items
nested-evaluated.css.ts:5:20: `style()` cannot use `p -> 0` at build time: responsive arrays must be flat; each entry supplies one breakpoint value or selector style object, not another array
nested-direct.css.ts:4:32: `style()` cannot use `[1, 2]` at build time: responsive arrays must be flat; each entry supplies one breakpoint value or selector style object, not another array

수정 전 WASM 프로브의 실제 CSS 발췌:

.-ms-flex-0-8px--255{-ms-flex:8px}
.-ms-flex-order-0-8px--255{-ms-flex-order:8px}
.-webkit-box-flex-0-8px--255{-webkit-box-flex:8px}
.flex-0-1--255{flex:1}
.flex-grow-0-2--255{flex-grow:2}
.padding-0-8px--255{padding:8px}
.color-0-red--255{color:red}
@media(min-width:480px){.color-1-blue--255{color:blue}.color-1-green--255{color:green}}
.box-align-0-center--255{box-align:center}
.scroll-snap-coordinate-0-0_s_0--255{scroll-snap-coordinate:0 0}

기준 브랜치의 selector 프로브:

selectors.tsx:2:42: `<Box>` cannot use `body` at build time: a selector key names a pseudo-class or pseudo-element, as `_hover` or `hover`, or is a selector, as `&:hover`, `& > p` or `.parent &`
.color-0-red-_a__c_hover-255:hover{color:red}
.color-0-blue-_a__s__gt__s_p-255 > p{color:blue}

최종 CI 결과

최종 head: 402109f97e94714fc316351761b931d6554d799d.

CI run 37245278743에서 코드/빌드/테스트/커버리지/E2E 검증은 모두 통과했습니다.

  • Linux Rust 커버리지: 100.00% (10,278 / 10,278 라인). 임계값이나 제외 설정을 변경하지 않았습니다.
  • Bun 테스트와 커버리지: 100%.
  • benchmark, landing-next-e2e, vinext-rsc-css-e2e job: 성공.
  • publish job의 lint, Rust/Bun 테스트, landing per-file/singleCss 빌드 및 E2E 검증: 성공.
  • 전체 run의 유일한 실패 원인은 Upload to codecov.io 단계의 외부 Codecov CLI 다운로드 장애입니다. cli.codecov.io가 만료된 TLS 인증서를 제공하여 curl: (60) SSL certificate problem: certificate has expired가 발생했고, 다운로드되지 않은 GPG 서명 파일의 검증도 이어서 실패했습니다.

CSS 출력 PR에서 공유 workflow를 변경하거나 TLS 검증을 우회하지 않습니다. 인증서가 복구되면 coordinator가 실패 job을 한 번 재실행하여 최종 상태를 확인합니다.

owjs3901 and others added 6 commits October 1, 2026 01:02
…ct selectors that select nothing

A nested rule without & in a template became a global rule, content lost the quotes of its strings, params of a group selector went after the class, and a selector key or string value that selects nothing gave dead CSS or vanished. Nested rules are now relative to their rule as in styled-components and Emotion, content keeps its strings, params go on the pseudo-class that takes them, and unknown selector names and strings that are not CSS text are build errors.

Refs #685

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Bare selectors keys no longer reach SelectorSeparator::Space through StyleSelector::from, so test it where it is defined.

Refs #685

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #685

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #685

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #685

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #685

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
@github-actions

github-actions Bot commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

Changepacks

@devup-ui/wasm@1.0.83 → 1.0.84 - bindings/devup-ui-wasm/package.json

Patch

  • Preserve unitless legacy vendor numbers, classify vendor time values as milliseconds and emit the Opera vendor prefix correctly; reject nested responsive arrays and known dead unprefixed CSS property names with source-located build errors, while preserving supported experimental CSS, unknown properties and documented shorthands.
  • Selectors: a nested rule without & in a styled or css template is scoped to the component instead of becoming a global rule (.child {} and > p {} select descendants, :hover {} and ::before {} the component), and a rule nested in a selector list applies under each of its selectors; content keeps the quotes of its strings (content: 'x', content: ""); params go on the pseudo-class that takes them, so _groupNthChild gives :is(...):nth-child(2n) .a also when nested; a bare selectors key is a pseudo-class name, an attribute of the element or a descendant selector ('div p', '> p'), under a parent too, and keeps the case of class names; ::part(), ::slotted(), ::highlight(), ::picker() and the view-transition pseudo-elements get two colons. New build errors: a _name prop or selector key that names nothing (_notASelector, selectors={{ div: ... }}), and a string under a selector that is not CSS text (_hover="external-class")

@devup-ui/bun-plugin@1.0.22 → 1.0.23 - packages/bun-plugin/package.json

Patch

  • Auto-update: depends on '@devup-ui/wasm' via a local workspace dependency

@devup-ui/components@0.1.60 → 0.1.61 - packages/components/package.json

Patch

  • Auto-update: depends on '@devup-ui/vite-plugin' via a local workspace dependency

@devup-ui/next-plugin@1.0.90 → 1.0.91 - packages/next-plugin/package.json

Patch

  • Auto-update: depends on '@devup-ui/wasm' via a local workspace dependency

@devup-ui/rsbuild-plugin@1.0.67 → 1.0.68 - packages/rsbuild-plugin/package.json

Patch

  • Auto-update: depends on '@devup-ui/wasm' via a local workspace dependency

@devup-ui/vite-plugin@1.0.73 → 1.0.74 - packages/vite-plugin/package.json

Patch

  • Auto-update: depends on '@devup-ui/wasm' via a local workspace dependency

@devup-ui/webpack-plugin@1.0.71 → 1.0.72 - packages/webpack-plugin/package.json

Patch

  • Auto-update: depends on '@devup-ui/wasm' via a local workspace dependency

@codecov

codecov Bot commented Oct 5, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Files with missing lines Coverage Δ
bindings/devup-ui-wasm/src/lib.rs 100.00% <ø> (ø)
libs/css/src/constant.rs 100.00% <ø> (ø)
libs/css/src/is_special_property.rs 100.00% <ø> (ø)
libs/css/src/lib.rs 100.00% <100.00%> (ø)
libs/css/src/optimize_multi_css_value.rs 100.00% <ø> (ø)
libs/css/src/selector_separator.rs 100.00% <ø> (ø)
libs/css/src/style_selector.rs 100.00% <100.00%> (ø)
libs/css/src/theme_tokens.rs 100.00% <ø> (ø)
libs/extractor/src/css_utils.rs 100.00% <100.00%> (ø)
libs/extractor/src/dead_properties.rs 100.00% <100.00%> (ø)
... and 20 more

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant