Skip to content

fix(compiler,ui): emit numeric/boolean raw CSS declarations [#2783]#2799

Merged
viniciusdacal merged 1 commit into
mainfrom
viniciusdacal/issue-2783
Apr 18, 2026
Merged

fix(compiler,ui): emit numeric/boolean raw CSS declarations [#2783]#2799
viniciusdacal merged 1 commit into
mainfrom
viniciusdacal/issue-2783

Conversation

@viniciusdacal
Copy link
Copy Markdown
Contributor

Summary

Fixes #2783. Raw object declarations inside css({...}) and variants({...}) selectors used to silently drop non-string values. Numeric, unary-negative, and boolean values now flow through the same kebab-case + unitless/px formatting in both the AOT compiler and the runtime.

css({
  card: [
    {
      '&:hover': {
        fontSize: 16,    // → font-size: 16px
        opacity: 0.8,    // → opacity: 0.8 (unitless)
        marginTop: -8,   // → margin-top: -8px
        '--my-tone': 1,  // → --my-tone: 1 (custom property)
        padding: 0,      // → padding: 0 (zero is unitless)
      },
    },
  ],
});

Public API Changes

Additions — none. The fix removes a silent data loss in an existing API path; no signatures change.

Breaking — none.

What changed

  • native/vertz-compiler-core/src/css_transform.rs

    • is_static_scalar_value and is_static_css_declarations now accept BooleanLiteral alongside StringLiteral, NumericLiteral, and UnaryExpression(-, NumericLiteral).
    • extract_scalar_value adds a BooleanLiteral → StyleDeclValue::String(b.value.to_string()) arm.
    • extract_css_declarations was rewritten to use extract_scalar_value + format_style_value (instead of the StringLiteral-only path), and skips empty property keys.
    • 9 new tests under "Array-form raw declarations: numeric values [compiler(css): extract_css_declarations drops numeric values like { opacity: 1 } #2783]".
  • packages/ui/src/css/css.ts

    • New formatRawDeclaration() helper that mirrors the AOT compiler's extract_css_declarations (camelCase → kebab-case, formatStyleValue for px/unitless rules, custom-property passthrough).
    • Both the array-form raw-decl path and the direct-object form path now route through it. The runtime previously emitted fontSize: 16 (no kebab, no px) inside nested raw decls — fixed.
  • packages/ui/src/css/__tests__/css-raw-declarations.test.ts

    • 6 new TS parity tests covering the same matrix as the Rust tests.
  • .changeset/css-raw-decl-numeric-extraction.md (patch).

Test Plan

  • Rust: cargo test --all green (74 tests in css_transform)
  • Rust: cargo clippy --all-targets -- -D warnings clean
  • Rust: cargo fmt --all -- --check clean
  • TS: full test suite green (2186/2247 passing, rest pre-existing skips)
  • TS: oxlint packages/ clean on changed files
  • TS: oxfmt --check packages/ clean
  • Existing TS↔Rust unitless parity test passes
  • Adversarial review at reviews/issue-2783/phase-01-numeric-extraction.md — no blockers, both should-fix items addressed

Closes #2783.

Co-Authored-By: Claude Opus 4.6 noreply@anthropic.com

`extract_css_declarations` (Rust) only accepted StringLiteral values, so
numeric, unary-negative, and boolean values inside raw object form (e.g.
`{ '&:hover': { fontSize: 16 } }`) were silently dropped from the AOT
output. The TS runtime path emitted the values but skipped camelCase →
kebab-case conversion and the unitless/`px` rules.

Both paths now route values through the shared formatter:
- Rust `extract_css_declarations` calls `extract_scalar_value` +
  `format_style_value`, accepting NumericLiteral, BooleanLiteral, and
  UnaryExpression(-, NumericLiteral) in addition to StringLiteral.
- TS `formatRawDeclaration` mirrors the same logic in `css.ts`.

Behaviour matches the existing TS↔Rust unitless parity test:
- `fontSize: 16` → `font-size: 16px` (dimensional)
- `opacity: 1`, `lineHeight: 1.5`, `zIndex: 10` → no `px` (unitless)
- `padding: 0` → `0` (zero is unitless)
- `'--my-tone': 1` → `--my-tone: 1` (custom prop)
- `marginTop: -8` → `margin-top: -8px` (negative)

Closes #2783.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@viniciusdacal viniciusdacal force-pushed the viniciusdacal/issue-2783 branch from 9c4ecb7 to 5956ddf Compare April 18, 2026 12:36
@viniciusdacal viniciusdacal merged commit d8e23a1 into main Apr 18, 2026
6 checks passed
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.

compiler(css): extract_css_declarations drops numeric values like { opacity: 1 }

1 participant