From 117b8d93d6f69082c4800b3b52b434eae270e962 Mon Sep 17 00:00:00 2001 From: owjs3901 Date: Sat, 3 Oct 2026 13:50:29 +0900 Subject: [PATCH 01/13] docs: document supported syntax, override rules and limitations Refs #693 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- .../changepack_log_known_limitations.json | 7 ++ README.md | 6 +- README_ko.md | 6 +- .../src/app/(detail)/docs/LeftMenu.tsx | 1 + .../src/app/(detail)/docs/features/page.mdx | 8 +- .../app/(detail)/docs/limitations/page.mdx | 106 ++++++++++++++++++ .../docs/migration/styled-components/page.mdx | 9 +- .../src/app/(detail)/docs/overview/page.mdx | 2 +- e2e/exported-routes.ts | 1 + packages/react/README.md | 6 +- 10 files changed, 137 insertions(+), 15 deletions(-) create mode 100644 .changepacks/changepack_log_known_limitations.json create mode 100644 apps/landing/src/app/(detail)/docs/limitations/page.mdx diff --git a/.changepacks/changepack_log_known_limitations.json b/.changepacks/changepack_log_known_limitations.json new file mode 100644 index 000000000..1878c32ec --- /dev/null +++ b/.changepacks/changepack_log_known_limitations.json @@ -0,0 +1,7 @@ +{ + "changes": { + "packages/react/package.json": "Patch" + }, + "note": "The README no longer claims every CSS-in-JS pattern compiles: it describes what the build compiles and that anything it cannot know is a CSS variable or a located build error, and links the new Supported Syntax & Limitations page, which lists what compiles, the style override rules (where a later part wins and where the CSS cascade decides), Baseline 2024 browser support, that a component taking the css prop must pass className and style on, the runtime-only APIs and how each is handled, and the known limitations", + "date": "2026-10-01T00:00:00.000Z" +} diff --git a/README.md b/README.md index 354d19968..1ec2a05d3 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@

- Zero Config · Zero FOUC · Zero Runtime · Complete CSS-in-JS Syntax Coverage + Zero Config · Zero FOUC · Zero Runtime · Build-Time CSS-in-JS

--- @@ -41,7 +41,7 @@ English | [한국어](README_ko.md) Traditional CSS-in-JS solutions force you to choose between developer experience and performance. Devup UI eliminates this trade-off entirely by processing all styles at build time using a Rust-powered preprocessor. -- **Complete Syntax Coverage**: Every CSS-in-JS pattern you know — variables, conditionals, responsive arrays, pseudo-selectors — all fully supported +- **Broad Syntax Coverage**: Variables, conditionals, responsive arrays, pseudo-selectors, `styled()`, Emotion's `css` prop and more compile at build time; anything the build cannot know is a located build error, never a silent miss ([supported syntax & limitations](https://devup-ui.com/docs/limitations)) - **Familiar API**: `styled()` API compatible with styled-components and Emotion patterns - **True Zero Runtime**: No JavaScript execution for styling at runtime. Period. - **Smallest Bundle Size**: Optimized class names (`a`, `b`, ... `aa`, `ab`) minimize CSS output @@ -137,7 +137,7 @@ const example =
// .a { background-color: var(--a); } ``` -**Complex expressions and responsive arrays — fully supported:** +**Complex expressions and responsive arrays:** ```tsx // You write: diff --git a/README_ko.md b/README_ko.md index 11c0caf7c..37b283632 100644 --- a/README_ko.md +++ b/README_ko.md @@ -7,7 +7,7 @@

- Zero Config · Zero FOUC · Zero Runtime · 모든 CSS-in-JS 문법 완벽 지원 + Zero Config · Zero FOUC · Zero Runtime · 빌드 타임 CSS-in-JS

--- @@ -41,7 +41,7 @@ 기존 CSS-in-JS 솔루션들은 개발자 경험과 성능 사이에서 타협을 강요했습니다. Devup UI는 Rust 기반 전처리기를 통해 모든 스타일을 빌드 타임에 처리함으로써 이 트레이드오프를 완전히 제거합니다. -- **완전한 문법 지원**: 변수, 조건문, 반응형 배열, 가상 선택자 등 모든 CSS-in-JS 패턴을 완벽하게 지원 +- **폭넓은 문법 지원**: 변수, 조건문, 반응형 배열, 가상 선택자, `styled()`, Emotion `css` prop 등을 빌드 타임에 컴파일합니다. 빌드가 알 수 없는 것은 조용히 빠지지 않고 위치가 표시된 빌드 오류가 됩니다 ([지원 문법과 한계](https://devup-ui.com/docs/limitations)) - **익숙한 API**: styled-components, Emotion과 호환되는 `styled()` API 제공 - **진정한 제로 런타임**: 런타임에서 스타일링을 위한 JavaScript 실행이 전혀 없습니다 - **가장 작은 번들 크기**: 최적화된 클래스명(`a`, `b`, ... `aa`, `ab`)으로 CSS 출력 최소화 @@ -137,7 +137,7 @@ const generated =
// .a { background-color: var(--a); } ``` -**복잡한 표현식과 반응형 배열 — 완벽 지원:** +**복잡한 표현식과 반응형 배열:** ```tsx // 개발자가 작성: diff --git a/apps/landing/src/app/(detail)/docs/LeftMenu.tsx b/apps/landing/src/app/(detail)/docs/LeftMenu.tsx index 85250e4ff..344434066 100644 --- a/apps/landing/src/app/(detail)/docs/LeftMenu.tsx +++ b/apps/landing/src/app/(detail)/docs/LeftMenu.tsx @@ -36,6 +36,7 @@ export function LeftMenu() { Core Concepts Features + Supported Syntax & Limitations +const example = ``` `css()`, `globalCss()` and `keyframes()` have no element to set a CSS variable on, so their values must be known at build time — literals, theme tokens or constants. Constants may be objects, arrays and TypeScript enums, declared in the file or imported: `css(baseStyles)`, `{ ...baseStyles, color: 'red' }`, `_hover: hoverStyles`, `space[2]`, `Size.M`, and `` all read as if written in place, a later property replacing an earlier one as in JavaScript. `Math` calls over them (`Math.max(SIZE, 20)`, `Math.round(x)`, `Math.PI`) fold like arithmetic. diff --git a/apps/landing/src/app/(detail)/docs/limitations/page.mdx b/apps/landing/src/app/(detail)/docs/limitations/page.mdx new file mode 100644 index 000000000..24939bd4a --- /dev/null +++ b/apps/landing/src/app/(detail)/docs/limitations/page.mdx @@ -0,0 +1,106 @@ +export const metadata = { + title: 'Supported Syntax & Limitations', + alternates: { + canonical: '/docs/limitations', + }, +} + +# Supported Syntax & Limitations + +Devup UI compiles styles at build time and ships no styling runtime. Whatever the build can know is compiled; whatever it cannot know is either kept as a CSS variable on an element or reported as a build error with the file, line, column, the code and what it needs instead. Nothing is silently dropped, and the output is the same on every build. + +This page lists what compiles, what stays as written, and where the limits are. + +## Supported at build time + +| Pattern | Result | +| --------------------------------------------------------------- | ------------------------------------------------------------------------ | +| Style props with literals, theme tokens and constants | Static classes | +| Style props with runtime values (props, state) | A class reading a CSS variable set in `style` | +| Responsive arrays, `_hover` and other selectors, both combined | Classes per breakpoint and selector | +| Conditions (`a ? b : c`, `a && b`, `a \|\| b`, `a ?? b`) | One class per branch, chosen at runtime | +| Constants declared with `const`, in the file or imported | Read as written, including objects, arrays, enums and computed constants | +| `css()`, `globalCss()`, `keyframes()` | Static CSS; values must be known at build time | +| `styled()` in every spelling, `.attrs()`, `as`, `withComponent` | A component rendering its tag with static classes | +| styled-components / Emotion theme reads (`p.theme.a.b`) | `var(--a-b)`, set by `ThemeProvider` | +| Emotion `css` prop, ``, `jsx`, JSX runtime pragmas | Compiled to `className` and `style` | +| Component selectors (`${Child} { ... }`, `[Child]`) | A short marker class on the selected component | +| vanilla-extract `style`, `globalStyle`, `keyframes` | Static CSS | +| StyleX `create`, `props`, `attrs`, `defineVars`, `createTheme` | Static CSS, merged key by key | +| Tailwind v4 classes in `className` | Static classes; unknown classes stay as written | + +## Style override rules + +When styles the build knows are composed, a later part replaces an earlier one for the same property, selector, breakpoint and layer, as it would at runtime in the original library. This holds for: + +- `css(a, b)` and Emotion's `cx(a, b)` with classes the file binds to `css()` +- `styled(Base)` extending a styled component the file defines, and `.attrs()` +- vanilla-extract `style([a, b])`, StyleX `props(a, b)` +- JSX spreads followed by explicit props, and the Emotion `css` prop with a `className` + +Responsive values are always written after non-responsive ones, so a breakpoint value overrides the base value regardless of where the classes land in the stylesheet. + +### Where the cascade decides + +Some composition only happens at runtime, so the build cannot merge it. There the result follows CSS specificity and stylesheet order, not the order the code wrote the parts in: + +- a `className` passed in through props, an external class (CSS Modules, a design system) or a class string built at runtime, joined with Devup UI classes +- a `styled()` base the build cannot read (a `let`, a function result, a component from another package), which keeps wrapping it +- `withComponent(make())` and other targets only the runtime knows +- unknown spread props, which pass through as written +- StyleX arguments the build cannot read key by key (namespaces from other modules, `include()`) + +When the winner matters, pass the variation as a prop, a variant (`cond ? a : b`) or a `styled()` extension the file defines, so the build sees both sides. + +## Browser support + +The generated CSS targets [Baseline 2024](https://web.dev/baseline): it uses `light-dark()` for color themes, `@layer` for cascade layers and `:is()` for group selectors. Older browsers that lack these features are not supported. + +## The `css` prop on your own components + +The Emotion `css` prop compiles to `className` and `style`. A component of your own that takes the prop receives those two props instead, so it must pass both on to the element it renders: + +```tsx +function Card({ className, style, children }) { + return ( +
+ {children} +
+ ) +} + +const example = +``` + +A dynamic value such as `width` becomes a CSS variable set in `style`, so a component that drops `style` loses it. + +## Runtime-only APIs + +| API | Behavior | +| --------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | +| `ServerStyleSheet`, `StyleSheetManager`, `CacheProvider` | Accepted and inert; Devup UI writes a real stylesheet at build time. `CacheProvider` ignores `value` | +| stylis plugins (`stylisPlugins`) | Not applied; there is no stylis pass at build time | +| `@emotion/css` `cache`, `flush`, `hydrate`, `merge`, `sheet` | Stay on `@emotion/css`; they work on styles inserted at runtime | +| `ThemeProvider` | Renders a `display: contents` wrapper that sets the theme's CSS variables | +| `jest-styled-components`, `@emotion/jest`, React Native targets | Not supported | +| vanilla-extract `recipes`, `sprinkles` | A build error while `@vanilla-extract/css` is aliased; use `css()` or the vanilla-extract plugin | + +## Known limitations + +- A class, `keyframes` name or styled component defined in another module is not read as a build-time value: its name is only known once that file is built. Declare it in the file that uses it, or read it from a style prop. +- `css()`, `globalCss()` and `keyframes()` have no element to set a CSS variable on, so a runtime value there is a build error. Move it to a style prop. +- A styled-components or Emotion style function may read the theme only as `theme.a.b` (or `theme.a[2]`). Calls, whole-theme reads and runtime keys are build errors. +- Inside ``, `cx(getClass())` is a build error; put the result in a variable first, which then joins as a class. +- The plugins read `jsxImportSource` from the `tsconfig.json` (or `jsconfig.json`) in the working directory. Opt out with `importAliases: { '@emotion/react/jsx-runtime': false }`. +- Code that only the runtime can compute — other globals, `Date`, `Math.random`, `new`, classes, async code — is never run by the build; on an element it stays a CSS variable, elsewhere it is a build error. + +## Build errors + +Every build error names where it is and what the build needs: + +``` +src/App.tsx:6:18: `css()` cannot use `width` at build time: its values must be literals, theme tokens or constants, or be computed from them +src/Card.tsx:3:36: `css` on `
` cannot use `getStyles()` at build time: it must be a style object, CSS text, a class `css()` gives, or a function of the theme giving one, or an array or condition of them +``` + +A file with several problems reports them all at once, in source order. diff --git a/apps/landing/src/app/(detail)/docs/migration/styled-components/page.mdx b/apps/landing/src/app/(detail)/docs/migration/styled-components/page.mdx index 0cb08360b..ffa68a944 100644 --- a/apps/landing/src/app/(detail)/docs/migration/styled-components/page.mdx +++ b/apps/landing/src/app/(detail)/docs/migration/styled-components/page.mdx @@ -121,6 +121,13 @@ Emotion's `` behaves the same way: the `styles` prop is e | `ThemeProvider`, `useTheme`, `withTheme` | `@devup-ui/react/compat`, CSS-variable backed | | `createGlobalStyle`, `Global` | extracted, render nothing | | `ServerStyleSheet`, `StyleSheetManager` | inert — Devup UI already emits a real stylesheet, so there is nothing to collect | +| `CacheProvider` | renders its children; the cache it configures has nothing to hold | | `isStyledComponent` | always `false` | -| `ClassNames`, `CacheProvider` | no equivalent; stays on its own package with a build warning | +| `css` prop, `jsx`, JSX runtime pragmas | compiled to `className` and `style` | +| `ClassNames` | replaced by what its child renders, each `css` / `cx` call compiled to classes | +| Component selectors (`${Child}`) | a marker class on the selected component | | `.attrs()`, `.withConfig()` | attrs merged over props; `withConfig` dropped | + +## Limitations + +Composition only the runtime sees — a `className` from props, an external class, a `styled()` base the build cannot read — follows CSS specificity and stylesheet order rather than the order you wrote. Styled components and classes defined in another module are not read as build-time values, stylis plugins are not applied, and test utilities and React Native targets are not supported. A component of your own that takes the `css` prop must pass on both `className` and `style`. See [Supported Syntax & Limitations](/docs/limitations) for the full list. diff --git a/apps/landing/src/app/(detail)/docs/overview/page.mdx b/apps/landing/src/app/(detail)/docs/overview/page.mdx index b186af1b8..a6d49fd90 100644 --- a/apps/landing/src/app/(detail)/docs/overview/page.mdx +++ b/apps/landing/src/app/(detail)/docs/overview/page.mdx @@ -31,7 +31,7 @@ Libraries like styled-components and Emotion offer great DX but execute JavaScri ### The Devup UI Solution -Devup UI eliminates this trade-off entirely. Our Rust-powered preprocessor analyzes your code at build time and handles every CSS-in-JS pattern: +Devup UI eliminates this trade-off entirely. Our Rust-powered preprocessor analyzes your code at build time; what it cannot know is a CSS variable or a located build error ([supported syntax & limitations](/docs/limitations)): - **Variables** — Dynamic values become CSS custom properties - **Conditionals** — Ternary expressions are statically analyzed diff --git a/e2e/exported-routes.ts b/e2e/exported-routes.ts index 5c8edd2bc..e3e6cbff1 100644 --- a/e2e/exported-routes.ts +++ b/e2e/exported-routes.ts @@ -80,6 +80,7 @@ export const EXPECTED_EXPORTED_ROUTES = [ '/docs/figma-and-theme-integration/devup-figma-plugin', '/docs/figma-and-theme-integration/devup-json', '/docs/installation', + '/docs/limitations', '/docs/migration/overview', '/docs/migration/styled-components', '/docs/migration/stylex', diff --git a/packages/react/README.md b/packages/react/README.md index ede5459d6..fec687dd8 100644 --- a/packages/react/README.md +++ b/packages/react/README.md @@ -103,7 +103,7 @@ The Turbopack ranges overlap, so the direct-API result is effectively parity wit Devup UI is a CSS in JS preprocessor that does not require runtime. Devup UI eliminates the performance degradation of the browser through the CSS in JS preprocessor. -We develop a preprocessor that considers all grammatical cases. +What the build cannot know is kept as a CSS variable or reported as a located build error; see [supported syntax & limitations](https://devup-ui.com/docs/limitations). ```tsx const before = @@ -111,7 +111,7 @@ const before = const after =
``` -Variables are fully supported. +Variables become CSS variables. ```tsx const before = @@ -126,7 +126,7 @@ const after = ( ) ``` -Various expressions and responsiveness are also fully supported. +Conditions and responsive arrays compile too. ```tsx const before = b ? 'yellow' : variable]} /> From 80fb7c33d5aeec6ab84a668e8fcf8274be1ff15f Mon Sep 17 00:00:00 2001 From: owjs3901 Date: Sat, 3 Oct 2026 14:25:23 +0900 Subject: [PATCH 02/13] chore: satisfy clippy assert_is_empty lint on Rust 1.99 Refs #693 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- bindings/devup-ui-wasm/src/lib.rs | 8 ++++---- libs/css/src/theme_tokens.rs | 2 +- libs/extractor/src/lib.rs | 8 ++++---- libs/extractor/src/tailwind.rs | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/bindings/devup-ui-wasm/src/lib.rs b/bindings/devup-ui-wasm/src/lib.rs index 4560410f3..0a2347ac2 100644 --- a/bindings/devup-ui-wasm/src/lib.rs +++ b/bindings/devup-ui-wasm/src/lib.rs @@ -1551,7 +1551,7 @@ mod tests { ); // Test getters - assert!(!output.code().is_empty()); + assert_ne!(output.code(), ""); assert_eq!(output.css_file(), Some("devup-ui-0.css".to_string())); assert_eq!(output.map(), Some("//# sourceMappingURL=test".to_string())); assert!(output.css().is_some()); @@ -1951,7 +1951,7 @@ mod tests { assert!(result.is_ok()); let output = result.unwrap(); - assert!(!output.code().is_empty()); + assert_ne!(output.code(), ""); assert!(output.map().is_some()); } @@ -1975,7 +1975,7 @@ mod tests { assert!(result.is_ok()); let output = result.unwrap(); - assert!(!output.code().is_empty()); + assert_ne!(output.code(), ""); assert!(output.map().is_none()); } @@ -2000,7 +2000,7 @@ mod tests { assert!(result.is_err()); if let Err(error) = result { - assert!(!error.is_empty()); + assert_ne!(error, ""); } } diff --git a/libs/css/src/theme_tokens.rs b/libs/css/src/theme_tokens.rs index d8d8fb021..3b2346aed 100644 --- a/libs/css/src/theme_tokens.rs +++ b/libs/css/src/theme_tokens.rs @@ -119,7 +119,7 @@ mod tests { set_typography_keys(vec!["body".to_string(), "title".to_string()]); assert_eq!(get_typography_keys(), vec!["body", "title"]); set_typography_keys(vec![]); - assert!(get_typography_keys().is_empty()); + assert_eq!(get_typography_keys(), Vec::::new()); } #[test] diff --git a/libs/extractor/src/lib.rs b/libs/extractor/src/lib.rs index 68981f36c..96c6ca430 100644 --- a/libs/extractor/src/lib.rs +++ b/libs/extractor/src/lib.rs @@ -836,8 +836,8 @@ mod tests { alternate: None, }; - assert!(empty.extract().is_empty()); - assert!(empty.into_extract().is_empty()); + assert_eq!(empty.extract(), vec![]); + assert_eq!(empty.into_extract(), vec![]); } #[test] @@ -13598,7 +13598,7 @@ globalCss({ ); assert!(result.is_ok()); let output = result.unwrap(); - assert!(!output.code.is_empty()); + assert_ne!(output.code, ""); } #[test] @@ -18778,7 +18778,7 @@ export const k = styled('div')({ color: SIZE });", &memory_resolver(CONSTANT_MODULES), ) .unwrap(); - assert!(without_imports.dependencies.is_empty()); + assert_eq!(without_imports.dependencies.len(), 0); let without_constants = extract_with_modules( "/src/Handler.tsx", "import { Box } from '@devup-ui/react';\nimport { handler } from './handler';\nexport const a = ;", diff --git a/libs/extractor/src/tailwind.rs b/libs/extractor/src/tailwind.rs index 87e9f5178..70643aa7c 100644 --- a/libs/extractor/src/tailwind.rs +++ b/libs/extractor/src/tailwind.rs @@ -284,7 +284,7 @@ pub struct TailwindClass { /// non-overlapping) but mutates the existing buffer instead of allocating a new /// `String`. `needle` must be non-empty. fn remove_all_substr(haystack: &mut String, needle: &str) { - debug_assert!(!needle.is_empty()); + debug_assert_ne!(needle, ""); let mut search_from = 0; while let Some(rel) = haystack[search_from..].find(needle) { let at = search_from + rel; @@ -3934,7 +3934,7 @@ mod tests { #[test] fn test_empty_string() { let styles = parse_tailwind_to_styles(""); - assert!(styles.is_empty()); + assert_eq!(styles, vec![]); } #[test] From 5162032e701d7fb169ec602e792df5ab3ec565fb Mon Sep 17 00:00:00 2001 From: owjs3901 Date: Sun, 4 Oct 2026 13:24:27 +0900 Subject: [PATCH 03/13] docs: link the build-error reference from READMEs Refs #693 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- README.md | 2 +- README_ko.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1ec2a05d3..c07fac795 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ English | [한국어](README_ko.md) Traditional CSS-in-JS solutions force you to choose between developer experience and performance. Devup UI eliminates this trade-off entirely by processing all styles at build time using a Rust-powered preprocessor. -- **Broad Syntax Coverage**: Variables, conditionals, responsive arrays, pseudo-selectors, `styled()`, Emotion's `css` prop and more compile at build time; anything the build cannot know is a located build error, never a silent miss ([supported syntax & limitations](https://devup-ui.com/docs/limitations)) +- **Broad Syntax Coverage**: Variables, conditionals, responsive arrays, pseudo-selectors, `styled()`, Emotion's `css` prop and more compile at build time; anything the build cannot know is a [located build error](https://devup-ui.com/docs/build-errors), never a silent miss ([supported syntax & limitations](https://devup-ui.com/docs/limitations)) - **Familiar API**: `styled()` API compatible with styled-components and Emotion patterns - **True Zero Runtime**: No JavaScript execution for styling at runtime. Period. - **Smallest Bundle Size**: Optimized class names (`a`, `b`, ... `aa`, `ab`) minimize CSS output diff --git a/README_ko.md b/README_ko.md index 37b283632..eaee1981a 100644 --- a/README_ko.md +++ b/README_ko.md @@ -41,7 +41,7 @@ 기존 CSS-in-JS 솔루션들은 개발자 경험과 성능 사이에서 타협을 강요했습니다. Devup UI는 Rust 기반 전처리기를 통해 모든 스타일을 빌드 타임에 처리함으로써 이 트레이드오프를 완전히 제거합니다. -- **폭넓은 문법 지원**: 변수, 조건문, 반응형 배열, 가상 선택자, `styled()`, Emotion `css` prop 등을 빌드 타임에 컴파일합니다. 빌드가 알 수 없는 것은 조용히 빠지지 않고 위치가 표시된 빌드 오류가 됩니다 ([지원 문법과 한계](https://devup-ui.com/docs/limitations)) +- **폭넓은 문법 지원**: 변수, 조건문, 반응형 배열, 가상 선택자, `styled()`, Emotion `css` prop 등을 빌드 타임에 컴파일합니다. 빌드가 알 수 없는 것은 조용히 빠지지 않고 [위치가 표시된 빌드 오류](https://devup-ui.com/docs/build-errors)가 됩니다 ([지원 문법과 한계](https://devup-ui.com/docs/limitations)) - **익숙한 API**: styled-components, Emotion과 호환되는 `styled()` API 제공 - **진정한 제로 런타임**: 런타임에서 스타일링을 위한 JavaScript 실행이 전혀 없습니다 - **가장 작은 번들 크기**: 최적화된 클래스명(`a`, `b`, ... `aa`, `ab`)으로 CSS 출력 최소화 From 617fce2289440b21f7e27f941c2fb754cfd78d07 Mon Sep 17 00:00:00 2001 From: owjs3901 Date: Sun, 4 Oct 2026 18:05:54 +0900 Subject: [PATCH 04/13] docs: add the build-error reference page Refs #693 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- .../app/(detail)/docs/build-errors/page.mdx | 3964 +++++++++++++++++ apps/landing/src/app/(detail)/docs/layout.tsx | 1 + e2e/build-errors.spec.ts | 64 + e2e/exported-routes.ts | 1 + 4 files changed, 4030 insertions(+) create mode 100644 apps/landing/src/app/(detail)/docs/build-errors/page.mdx create mode 100644 e2e/build-errors.spec.ts diff --git a/apps/landing/src/app/(detail)/docs/build-errors/page.mdx b/apps/landing/src/app/(detail)/docs/build-errors/page.mdx new file mode 100644 index 000000000..753b69bbc --- /dev/null +++ b/apps/landing/src/app/(detail)/docs/build-errors/page.mdx @@ -0,0 +1,3964 @@ +export const metadata = { + title: 'Build Errors', + alternates: { canonical: '/docs/build-errors' }, +} + +# Build Errors + +Devup UI builds styles into CSS rather than shipping a styling runtime. This reference groups the diagnostics emitted by the reviewed extractor and plugin sources, with failing inputs and accepted repairs. See [Supported Syntax & Limitations](/docs/limitations) for the broader support and composition rules. + +A located style diagnostic has this shape: + +```text +::: cannot use `` at build time: +``` + +The placeholders vary with your source. The requirement text below is the real emitted wording, not a paraphrase. Diagnostics can also report `Cannot compose`, `Cannot place`, a removed runtime binding, or an invalid shape. Style diagnostics identify the original source location. Internal loader/setup messages are listed separately. + +Each example is a separate compiler input. Names such as `width`, `rules`, `value`, or `getStyles` denote values only the runtime supplies unless the snippet declares them. Dynamic values in known element declarations can become CSS variables; unknown rule keys or whole opaque style objects cannot. + +Message families shared by multiple spellings are reused by those APIs. The shown before/after pairs were verified on source-specific WASM builds; class names and generated variable names are not stable public identifiers. + +## Style props + +### Unknown selector style object + +**API:** Style props. **Message pattern:** + +```text +`` cannot use `` at build time: its styles must be an object literal or a constant object, or be computed from constants +``` + +**Trigger:** + +{/* probe: selector-object before */} + +```tsx-error +import { Box } from '@devup-ui/react'; +export const App = ({ rules }) => ; +``` + +**Why:** A selector needs a known set of properties; an opaque runtime object gives the build no declarations to extract. + +**Fix:** Write the object structure explicitly. Its individual values can remain dynamic and become CSS variables. + +{/* probe: selector-object after */} + +```tsx +import { Box } from '@devup-ui/react' +export const App = ({ color }) => +``` + +### Invalid selector spelling + +**API:** Style props. **Message pattern:** + +```text +`` cannot use `` 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 &` +``` + +**Trigger:** + +{/* probe: selector-name before */} + +```tsx-error +import { Box } from '@devup-ui/react'; +export const App = () => ; +``` + +**Why:** An unknown pseudo-selector name is not a CSS selector the build can emit. + +**Fix:** Use a supported pseudo-selector or a full selector containing &, such as &:hover. + +{/* probe: selector-name after */} + +```tsx +import { Box } from '@devup-ui/react' +export const App = () => +``` + +### A class string is not selector CSS text + +**API:** Style props. **Message pattern:** + +```text +`` cannot use `` at build time: a selector takes styles, an object such as `{ color: 'red' }` or CSS text such as `color: red` +``` + +**Trigger:** + +{/* probe: selector-css-text before */} + +```tsx-error +import { Box } from '@devup-ui/react'; +export const App = () => ; +``` + +**Why:** A selector takes declarations, not an external class name. + +**Fix:** Supply a style object or valid CSS declaration text such as color: red. + +{/* probe: selector-css-text after */} + +```tsx +import { Box } from '@devup-ui/react' +export const App = () => +``` + +## css / globalCss / keyframes + +### Runtime value in css() + +**API:** css / globalCss / keyframes. **Message pattern:** + +```text +`css()` cannot use `` at build time: its values must be literals, theme tokens or constants, or be computed from them +``` + +**Trigger:** + +{/* probe: static-css before */} + +```tsx-error +import { css } from '@devup-ui/react'; +export const card = css({ width }); +``` + +**Why:** This API emits stylesheet rules and has no element on which to set a runtime CSS variable. + +**Fix:** Use a literal, theme token, constant, or deterministic computation from constants. For a runtime value, move the declaration to a style prop, for example <Box w=\{width\} />. + +{/* probe: static-css after */} + +```tsx +import { css } from '@devup-ui/react' +export const card = css({ width: '20px' }) +``` + +### Unknown global rule object + +**API:** css / globalCss / keyframes. **Message pattern:** + +```text +`globalCss()` cannot use `` at build time: its values must be literals, theme tokens or constants, or be computed from them +``` + +**Trigger:** + +{/* probe: global-object before */} + +```tsx-error +import { globalCss } from '@devup-ui/react'; +globalCss(rules); +``` + +**Why:** A global stylesheet needs a known selector-to-rules structure. + +**Fix:** Pass a literal object or a constant object/computation the build can evaluate. + +{/* probe: global-object after */} + +```tsx +import { globalCss } from '@devup-ui/react' +globalCss({ body: { color: 'red' } }) +``` + +### Unknown keyframe spread + +**API:** css / globalCss / keyframes. **Message pattern:** + +```text +`keyframes()` cannot use `` at build time: its values must be literals, theme tokens or constants, or be computed from them +``` + +**Trigger:** + +{/* probe: keyframes-spread before */} + +```tsx-error +import { keyframes } from '@devup-ui/react'; +export const fade = keyframes({ ...frames }); +``` + +**Why:** The build must know the names and declarations of every frame. + +**Fix:** Write the frames explicitly or resolve them from a constant object. + +{/* probe: keyframes-spread after */} + +```tsx +import { keyframes } from '@devup-ui/react' +export const fade = keyframes({ from: { opacity: 0 }, to: { opacity: 1 } }) +``` + +### Uncompilable composition argument + +**API:** css / globalCss / keyframes. **Message pattern:** + +```text +Cannot compose `` at build time: each style must be a rule object, a class, or a condition choosing between them +``` + +**Trigger:** + +{/* probe: css-compose before */} + +```tsx-error +import { css } from '@devup-ui/react'; +export const card = css(getStyles()); +``` + +**Why:** The build cannot discover the keys of an unknown function result. + +**Fix:** Compose known rule objects, classes, or conditions choosing between those forms. A helper that is deterministic and build-readable is allowed. + +{/* probe: css-compose after */} + +```tsx +import { css } from '@devup-ui/react' +export const card = css({ color: 'red' }) +``` + +### Runtime selector or property-name interpolation + +**API:** css / globalCss / keyframes. **Message pattern:** + +```text +Cannot place `` at build time: an interpolation in a selector or a property name must be a literal or a constant +``` + +**Trigger:** + +{/* probe: template-selector before */} + +```tsx-error +import { css } from '@devup-ui/react'; +export const card = css`& ${selector} { color: red; }`; +``` + +**Why:** The build cannot put a dynamic expression into selector or property-name syntax. + +**Fix:** Use literal text or a build-readable constant. This also applies to styled templates. + +{/* probe: template-selector after */} + +```tsx +import { css } from '@devup-ui/react' +export const card = css` + & > p { + color: red; + } +` +``` + +### Style object changed by other code + +**API:** css / globalCss / keyframes. **Message pattern:** + +```text +`css()` cannot use `` at build time: its styles must be an object literal or a constant object, or be computed from constants +`` is changed here, so the build cannot read it as a constant +``` + +**Trigger:** + +{/* probe: changed-object before */} + +```tsx-error +import { css } from '@devup-ui/react'; +const rules = { color: 'red' }; +rules.color = color; +export const card = css(rules); +``` + +**Why:** A const binding does not make a mutated object a fixed stylesheet value; the error may include a note locating the mutation. + +**Fix:** Do not mutate the style object. Put the dynamic value on an element or build a new constant object from static values. + +{/* probe: changed-object after */} + +```tsx +import { css } from '@devup-ui/react' +const rules = { color: 'red' } +export const card = css(rules) +``` + +### Runtime styles object in Global + +**API:** css / globalCss / keyframes. **Message pattern:** + +```text +`` cannot use `` at build time: its values must be literals, theme tokens or constants +``` + +**Trigger:** + +{/* probe: global-element-object before */} + +```tsx-error +import { Global } from '@emotion/react'; +export const App = () => ; +``` + +**Why:** Global cannot discover selectors or declarations in an opaque runtime object. + +**Fix:** Write a known rule object or resolve it from a module-level constant. + +{/* probe: global-element-object after */} + +```tsx +import { Global } from '@emotion/react' +export const App = () => +``` + +### Invalid selector in css() + +**API:** css / globalCss / keyframes. **Message pattern:** + +```text +`css()` cannot use `` at build time: a selector takes styles, an object such as `{ color: 'red' }` or CSS text such as `color: red` +``` + +**Trigger:** + +{/* probe: selector-css-api before */} + +```tsx-error +import { css } from '@devup-ui/react'; +export const card = css({ _focus: 'red' }); +``` + +**Why:** A selector value must contain declarations, not an arbitrary string. + +**Fix:** Use a style object or valid CSS declaration text; the same selector checks apply inside css, styled, and globalCss. + +{/* probe: selector-css-api after */} + +```tsx +import { css } from '@devup-ui/react' +export const card = css({ _focus: { color: 'red' } }) +``` + +### Invalid global selector spelling + +**API:** css / globalCss / keyframes. **Message pattern:** + +```text +`globalCss()` cannot use `` 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 &` +``` + +**Trigger:** + +{/* probe: selector-global-api before */} + +```tsx-error +import { globalCss } from '@devup-ui/react'; +globalCss({ _nope: { color: 'red' } }); +``` + +**Why:** An unknown pseudo name cannot be emitted as a useful global selector. + +**Fix:** Use a valid global selector, or a supported pseudo selector in the appropriate rule object. + +{/* probe: selector-global-api after */} + +```tsx +import { globalCss } from '@devup-ui/react' +globalCss({ body: { color: 'red' } }) +``` + +## styled + +### Invalid styled factory shape + +**API:** styled. **Message pattern:** + +```text +`styled()` cannot use `` at build time: it renders a tag, a component or a value naming one, with rule objects or CSS text +``` + +**Trigger:** + +{/* probe: styled-factory before */} + +```tsx-error +import { styled } from '@devup-ui/react'; +export const Card = styled(123)({ color: 'red' }); +``` + +**Why:** The styled factory must know what tag or component it renders and which arguments are rules. + +**Fix:** Provide a tag/component first, then rule objects or CSS text. + +{/* probe: styled-factory after */} + +```tsx +import { styled } from '@devup-ui/react' +export const Card = styled('div')({ color: 'red' }) +``` + +### Runtime whole rule object + +**API:** styled. **Message pattern:** + +```text +`styled()` cannot use `` at build time: its styles must be an object literal or a constant object, or be computed from constants +``` + +**Trigger:** + +{/* probe: styled-object before */} + +```tsx-error +import { styled } from '@devup-ui/react'; +export const Card = styled.div({ _hover: rules }); +``` + +**Why:** A whole runtime rule object has unknown keys, unlike a runtime value of a known property. + +**Fix:** Keep the rule structure explicit. For runtime values, use a style prop such as <Box \_hover=\{\{ color \}\} />. + +{/* probe: styled-object after */} + +```tsx +import { styled } from '@devup-ui/react' +export const Card = styled.div({ _hover: { color: 'red' } }) +``` + +### Uncompilable styled composition + +**API:** styled. **Message pattern:** + +```text +Cannot compose `` at build time: each style must be a rule object, a class, or a condition choosing between them +``` + +**Trigger:** + +{/* probe: styled-compose before */} + +```tsx-error +import { styled } from '@devup-ui/react'; +export const Card = styled.div({ color: 'red' }, getStyles()); +``` + +**Why:** An unknown function result cannot be split into stylesheet declarations. + +**Fix:** Compose known objects/classes or conditions selecting them; express runtime differences as props. + +{/* probe: styled-compose after */} + +```tsx +import { styled } from '@devup-ui/react' +export const Card = styled.div({ color: 'red' }, { padding: '4px' }) +``` + +### Unsupported shouldForwardProp predicate + +**API:** styled. **Message pattern:** + +```text +`styled()` cannot use `` at build time: `shouldForwardProp` must be a function of the prop name that compares it with strings, `[...].includes(prop)`, `prop.startsWith(...)` or `isPropValid(prop)`, joined by `!`, `&&` and `||` +``` + +**Trigger:** + +{/* probe: styled-forward before */} + +```tsx-error +import { styled } from '@devup-ui/react'; +export const Card = styled.div.withConfig({ shouldForwardProp: name => check(name) })({ color: 'red' }); +``` + +**Why:** The build evaluates the filter, so arbitrary runtime predicates cannot decide which props pass to the element. + +**Fix:** Compare with strings, use includes, startsWith, or isPropValid, and combine them with !, &&, or ||. + +{/* probe: styled-forward after */} + +```tsx +import { styled } from '@devup-ui/react' +export const Card = styled.div.withConfig({ + shouldForwardProp: (name) => name !== 'tone', +})({ color: 'red' }) +``` + +### Invalid selector in styled() + +**API:** styled. **Message pattern:** + +```text +`styled()` cannot use `` 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 &` +``` + +**Trigger:** + +{/* probe: selector-styled-api before */} + +```tsx-error +import { styled } from '@devup-ui/react'; +export const Card = styled.div({ selectors: { nope: { color: 'red' } } }); +``` + +**Why:** The compiler cannot emit an unknown shorthand selector name. + +**Fix:** Use a supported pseudo name or a full selector containing &. + +{/* probe: selector-styled-api after */} + +```tsx +import { styled } from '@devup-ui/react' +export const Card = styled.div({ selectors: { '&:hover': { color: 'red' } } }) +``` + +## Emotion css prop and ClassNames + +### Unsupported css prop value + +**API:** Emotion css prop and ClassNames. **Message pattern:** + +```text +`css` on `
` cannot use `` at build time: it must be a style object, CSS text, a class `css()` gives, or a function of the theme giving one, or an array or condition of them +``` + +**Trigger:** + +{/* probe: css-prop-value before */} + +```tsx-error +import { css } from '@emotion/react'; +export const App = () =>
; +``` + +**Why:** The css prop needs a known rule structure, CSS text, or class, not an opaque runtime computation. + +**Fix:** Use a style object, CSS text, a class from css(), a theme function giving one, or an array/condition of these. + +{/* probe: css-prop-value after */} + +```tsx +export const App = () =>
+``` + +### Local style object hidden behind a binding + +**API:** Emotion css prop and ClassNames. **Message pattern:** + +```text +`css` on `
` cannot use `` at build time: a style object it composes must be written in it, or declared with `const` at the top level of the module, where the build reads it +``` + +**Trigger:** + +{/* probe: css-prop-local before */} + +```tsx-error +import { css } from '@emotion/react'; +export function App() { const rules = { color: 'red' }; return
; } +``` + +**Why:** The composition path reads module-level const objects; a local object cannot be mistaken for a class string. + +**Fix:** Write the object directly in css, or declare it with const at the module top level. + +{/* probe: css-prop-local after */} + +```tsx +const rules = { color: 'red' } +export function App() { + return
+} +``` + +### css prop overrides an opaque styled component + +**API:** Emotion css prop and ClassNames. **Message pattern:** + +```text +`css` on `` overrides styles `Card` sets, which the build orders only for a styled component rendering a tag with no attrs or props read, given no spread, `as` or `forwardedAs`: move these styles into `styled(Card)(...)` +``` + +**Trigger:** + +{/* probe: css-prop-override before */} + +```tsx-error +import { css } from '@emotion/react'; +import styled from '@emotion/styled'; +const Card = styled.div.attrs({ title: 'card' })({ color: 'red' }); +export const App = () => ; +``` + +**Why:** Ordering is known only for a static styled tag with no attrs, props reads, spread, as, or forwardedAs. + +**Fix:** Move the override into styled(Card)(...). A non-overlapping css prop does not need this ordering. + +{/* probe: css-prop-override after */} + +```tsx +import '@emotion/react' + +import styled from '@emotion/styled' +const Card = styled.div.attrs({ title: 'card' })({ color: 'red' }) +const BlueCard = styled(Card)({ color: 'blue' }) +export const App = () => +``` + +### ClassNames child cannot be compiled + +**API:** Emotion css prop and ClassNames. **Message pattern:** + +```text +`` cannot use `` at build time: it takes only a child function of `{ css, cx, theme }` giving what it renders at once +``` + +**Trigger:** + +{/* probe: class-child before */} + +```tsx-error +import { ClassNames } from '@emotion/react'; +export const App = () => {render}; +``` + +**Why:** ClassNames is erased by compiling a single immediate child function; arbitrary children, attributes, async callbacks, and statement blocks cannot be erased that way. + +**Fix:** Use one child function taking only \{ css, cx, theme \} and returning what it renders immediately; remove ClassNames attributes. + +{/* probe: class-child after */} + +```tsx +import { ClassNames } from '@emotion/react' +export const App = () => ( + + {({ css }) =>
} + +) +``` + +### ClassNames helper escapes its call + +**API:** Emotion css prop and ClassNames. **Message pattern:** + +```text +`` cannot use `` at build time: the `css` and `cx` its child function takes can only be called, as the build compiles each call +``` + +**Trigger:** + +{/* probe: class-read before */} + +```tsx-error +import { css, ClassNames } from '@emotion/react'; +export const App = () => {({ css }) =>
}; +``` + +**Why:** The child helpers exist only while the build compiles their direct calls. + +**Fix:** Call css/cx directly inside the child rather than passing, storing, or returning either helper. + +{/* probe: class-read after */} + +```tsx +import { ClassNames } from '@emotion/react' +export const App = () => ( + + {({ css }) =>
} + +) +``` + +### ClassNames composes an unknown call + +**API:** Emotion css prop and ClassNames. **Message pattern:** + +```text +`` cannot use `` at build time: `css` and `cx` compose only style objects, CSS text, classes, calls of them, or arrays or conditions of these +``` + +**Trigger:** + +{/* probe: class-part before */} + +```tsx-error +import { ClassNames } from '@emotion/react'; +export const App = () => {({ cx }) =>
}; +``` + +**Why:** The ClassNames composition reader cannot classify an arbitrary call as a class or style object. + +**Fix:** Compute the class outside the child and pass its binding, or pass known objects/text/classes and direct css/cx calls. + +{/* probe: class-part after */} + +```tsx +import { ClassNames } from '@emotion/react' +export const App = () => ( + {({ cx }) =>
} +) +``` + +### ClassNames class map is not a data map + +**API:** Emotion css prop and ClassNames. **Message pattern:** + +```text +`` cannot use `` at build time: an object `cx` takes must give each class a condition, as `{ name: condition }` +``` + +**Trigger:** + +{/* probe: class-map before */} + +```tsx-error +import { ClassNames } from '@emotion/react'; +export const App = ({ classes }) => {({ cx }) =>
}; +``` + +**Why:** A ClassNames class map must expose every class and its condition; opaque spreads/getters/methods hide those entries. + +**Fix:** Use ordinary \{ name: condition \} entries. Computed class keys are allowed when written explicitly. + +{/* probe: class-map after */} + +```tsx +import { ClassNames } from '@emotion/react' +export const App = ({ active }) => ( + {({ cx }) =>
} +) +``` + +### CSS interpolation is neither value nor mixin + +**API:** Emotion css prop and ClassNames. **Message pattern:** + +```text +`css` on `
` cannot use `` at build time: an interpolation in CSS text must be a value, or a mixin standing where a declaration would +``` + +**Trigger:** + +{/* probe: mixin-placement before */} + +```tsx-error +import { css } from '@emotion/react'; +export const App = ({ selector }) =>
; +``` + +**Why:** An expression in selector/property syntax cannot become a declaration value or a separable mixin. + +**Fix:** Keep selectors and property names literal or constant; place dynamic values after a property colon. + +{/* probe: mixin-placement after */} + +```tsx +export const App = () => ( +
p { + color: red; + } + `} + /> +) +``` + +### Mixin inside a nested rule + +**API:** Emotion css prop and ClassNames. **Message pattern:** + +```text +`css` on `
` cannot use `` at build time: a mixin must stand outside nested rules, where the parts it composes with can be split +``` + +**Trigger:** + +{/* probe: nested-mixin before */} + +```tsx-error +import { css } from '@emotion/react'; +export const App = ({ rules }) =>
; +``` + +**Why:** The compiler splits mixins into composition parts only outside nested rules. + +**Fix:** Write the nested rule structure explicitly, or move a known mixin to the top-level declaration position. + +{/* probe: nested-mixin after */} + +```tsx +export const App = ({ color }) =>
+``` + +### Call cx instead of tagging a template + +**API:** Emotion css prop and ClassNames. **Message pattern:** + +```text +`cx()` cannot use `` at build time: call it with class names, as in `cx('a', 'b')` +``` + +**Trigger:** + +{/* probe: cx-template before */} + +```tsx-error +import * as E from '@emotion/css'; +export const a = E.cx`a b`; +``` + +**Why:** cx takes class-name arguments, not tagged CSS or class templates. + +**Fix:** Use cx with explicit class string arguments. + +{/* probe: cx-template after */} + +```tsx +import * as E from '@emotion/css' +export const a = E.cx('a', 'b') +``` + +### Call merge instead of tagging a template + +**API:** Emotion css prop and ClassNames. **Message pattern:** + +```text +`merge()` cannot use `` at build time: call it with class names, as in `cx('a', 'b')` +``` + +**Trigger:** + +{/* probe: merge-template before */} + +```tsx-error +import * as E from '@emotion/css'; +export const a = E.merge`a b`; +``` + +**Why:** merge takes one class string, not a tagged template. + +**Fix:** Pass the template contents as one ordinary string argument. + +{/* probe: merge-template after */} + +```tsx +import * as E from '@emotion/css' +export const a = E.merge('a b') +``` + +### Replace class-map setters with condition properties + +**API:** Emotion css prop and ClassNames. **Message pattern:** + +```text +`cx()` cannot use `` at build time: a class map entry must be written `name: condition`, not as a getter, setter or method +``` + +**Trigger:** + +{/* probe: map-setter before */} + +```tsx-error +import {cx} from '@emotion/css'; +export const a = () => cx({set b(v){}}); +``` + +**Why:** A cx class map describes class-name conditions; accessors and methods are not condition entries. + +**Fix:** Provide an explicit b: on condition instead of a setter. + +{/* probe: map-setter after */} + +```tsx +import { cx } from '@emotion/css' +export const a = (on) => cx({ b: on }) +``` + +### Do not spread unresolved cx arguments + +**API:** Emotion css prop and ClassNames. **Message pattern:** + +```text +`cx()` cannot use `` at build time: write every entry out, as its keys must be known +``` + +**Trigger:** + +{/* probe: cx-spread before */} + +```tsx-error +import {cx} from '@emotion/css'; +export const a = list => cx(...list); +``` + +**Why:** The build cannot enumerate entries of an unresolved runtime spread. + +**Fix:** For the illustrated fixed two-class shape, declare and pass each class argument explicitly; arbitrary runtime lists need a runtime class-string join. + +{/* probe: cx-spread after */} + +```tsx +import { cx } from '@emotion/css' +export const a = (first, second) => cx(first, second) +``` + +### Join runtime lists before calling merge + +**API:** Emotion css prop and ClassNames. **Message pattern:** + +```text +`merge()` cannot use `` at build time: write every entry out, as its keys must be known +``` + +**Trigger:** + +{/* probe: merge-spread before */} + +```tsx-error +import {merge} from '@emotion/css'; +export const a = list => merge(...list); +``` + +**Why:** The build cannot enumerate entries of an unresolved runtime spread. + +**Fix:** Join the list into one class string, then pass that single string to merge. + +{/* probe: merge-spread after */} + +```tsx +import { merge } from '@emotion/css' +export const a = (list) => merge(list.join(' ')) +``` + +### Give merge exactly one argument, even for no classes + +**API:** Emotion css prop and ClassNames. **Message pattern:** + +```text +`merge()` cannot use `` at build time: it takes one class string +``` + +**Trigger:** + +{/* probe: merge-zero before */} + +```tsx-error +import {merge} from '@emotion/css'; +export const a = merge(); +``` + +**Why:** merge has a one-class-string argument contract. + +**Fix:** Pass an empty string for an intentionally empty result. + +{/* probe: merge-zero after */} + +```tsx +import { merge } from '@emotion/css' +export const a = merge('') +``` + +### Use build-time values in css rules + +**API:** Emotion css prop and ClassNames. **Message pattern:** + +```text +`css()` cannot use `` at build time: its values must be literals, theme tokens or constants, or be computed from them +``` + +**Trigger:** + +{/* probe: css-runtime-value before */} + +```tsx-error +import * as E from '@emotion/css'; +export const a = E.css({color:unknown}); +``` + +**Why:** There is no rendered element on which this compile-time API can place a dynamic CSS variable. + +**Fix:** Replace the unresolved value with a literal, theme token or constant. + +{/* probe: css-runtime-value after */} + +```tsx +import * as E from '@emotion/css' +export const a = E.css({ color: 'red' }) +``` + +### Use build-time values in keyframe rules + +**API:** Emotion css prop and ClassNames. **Message pattern:** + +```text +`keyframes()` cannot use `` at build time: its values must be literals, theme tokens or constants, or be computed from them +``` + +**Trigger:** + +{/* probe: keyframes-runtime-value before */} + +```tsx-error +import * as E from '@emotion/css'; +export const a = E.keyframes({from:{opacity:unknown}}); +``` + +**Why:** There is no rendered element on which this compile-time API can place a dynamic CSS variable. + +**Fix:** Use a literal opacity value in the keyframe. + +{/* probe: keyframes-runtime-value after */} + +```tsx +import * as E from '@emotion/css' +export const a = E.keyframes({ from: { opacity: 0 } }) +``` + +### Use build-time values in injected global rules + +**API:** Emotion css prop and ClassNames. **Message pattern:** + +```text +`globalCss()` cannot use `` at build time: its values must be literals, theme tokens or constants, or be computed from them +``` + +**Trigger:** + +{/* probe: global-runtime-value before */} + +```tsx-error +import * as E from '@emotion/css'; +E.injectGlobal({body:{color:unknown}}); +``` + +**Why:** There is no rendered element on which this compile-time API can place a dynamic CSS variable. + +**Fix:** Use a literal global color value. + +{/* probe: global-runtime-value after */} + +```tsx +import * as E from '@emotion/css' +E.injectGlobal({ body: { color: 'red' } }) +``` + +### Do not pass mutated rule objects to css + +**API:** Emotion css prop and ClassNames. **Message pattern:** + +```text +`css()` cannot use `` at build time: its styles must be an object literal or a constant object, or be computed from constants +`` is changed here, so the build cannot read it as a constant +``` + +**Trigger:** + +{/* probe: css-mutated-object before */} + +```tsx-error +import * as E from '@emotion/css'; +const rules = {color:'red'}; +rules.color = 'blue'; +export const a = E.css(rules); +``` + +**Why:** A rule object changed by running code is not a build-time constant. + +**Fix:** Construct the final rule object without mutations before the css call. + +{/* probe: css-mutated-object after */} + +```tsx +import * as E from '@emotion/css' +const rules = { color: 'blue' } +export const a = E.css(rules) +``` + +### Compose known rule objects instead of spreading runtime styles + +**API:** Emotion css prop and ClassNames. **Message pattern:** + +```text +Cannot compose `` at build time: each style must be a rule object, a class, or a condition choosing between them +``` + +**Trigger:** + +{/* probe: css-composition before */} + +```tsx-error +import * as E from '@emotion/css'; +export const a = styles => E.css(...styles); +``` + +**Why:** Each composed style must have a build-time-readable rule or class shape. + +**Fix:** For a known composition, write each rule object out explicitly. + +{/* probe: css-composition after */} + +```tsx +import * as E from '@emotion/css' +export const a = E.css({ color: 'red' }, { padding: 8 }) +``` + +### Keep template selector interpolations constant + +**API:** Emotion css prop and ClassNames. **Message pattern:** + +```text +Cannot place `` at build time: an interpolation in a selector or a property name must be a literal or a constant +``` + +**Trigger:** + +{/* probe: css-selector-interpolation before */} + +```tsx-error +import * as E from '@emotion/css'; +export const a = E.css`.${unknown}{color:red}`; +``` + +**Why:** A runtime selector interpolation cannot determine the CSS rule to emit. + +**Fix:** Write the selector as literal text, or interpolate a build-time constant. + +{/* probe: css-selector-interpolation after */} + +```tsx +import * as E from '@emotion/css' +export const a = E.css`.selected{color:red}` +``` + +## Theme reads + +### Whole-theme or computed theme read in styled + +**API:** Theme reads. **Message pattern:** + +```text +`styled()` cannot use `` at build time: a theme read must be a path of names or literal keys, such as `p => p.theme.colors.brand` or `({ theme }) => theme.space[2]`, which reads the CSS variable `ThemeProvider` declares; compute other values outside the style +``` + +**Trigger:** + +{/* probe: styled-theme-old before */} + +```tsx-error +import styled from 'styled-components'; +export const Card = styled.div`color: ${props => props.theme};`; +``` + +**Why:** The compatibility theme is represented by CSS variables, not a runtime theme object available to style functions. + +**Fix:** Read a path of property names or literal keys, such as props.theme.colors.brand or theme.space[2]. Compute other values outside the style. + +{/* probe: styled-theme-old after */} + +```tsx +import styled from 'styled-components' +export const Card = styled.div` + color: ${(props) => props.theme.colors.brand}; +` +``` + +### Theme function does not immediately return rules + +**API:** Theme reads. **Message pattern:** + +```text +`css` on `
` cannot use `` at build time: a function of the theme must give its rules at once, as `theme => ({ ... })` +``` + +**Trigger:** + +{/* probe: theme-function before */} + +```tsx-error +import { css } from '@emotion/react'; +export const App = () =>
{ const rules = { color: theme.color }; return rules; }} />; +``` + +**Why:** The build rewrites the function result and needs one immediate expression or a single return statement. + +**Fix:** Use theme => (\{ ... \}) or a non-async function with a single return. Keep other computations outside. + +{/* probe: theme-function after */} + +```tsx +export const App = () =>
({ color: theme.color })} /> +``` + +### Theme read outside a declaration value + +**API:** Theme reads. **Message pattern:** + +```text +`css` on `
` cannot use `` at build time: it may read the theme only as `theme.a.b` in a value, which becomes the CSS variable `var(--a-b)` the `ThemeProvider` sets +``` + +**Trigger:** + +{/* probe: theme-read before */} + +```tsx-error +import { css } from '@emotion/react'; +export const App = () =>
({ color: theme.pick() })} />; +``` + +**Why:** Only a static path in a declaration value maps to a ThemeProvider CSS variable. + +**Fix:** Read theme.a.b or a literal index/key. Do not read the whole object, call it, use runtime keys, or use theme values as selector names. + +{/* probe: theme-read after */} + +```tsx +export const App = () => ( +
({ color: theme.colors.brand })} /> +) +``` + +### ClassNames theme read outside CSS + +**API:** Theme reads. **Message pattern:** + +```text +`` cannot use `` at build time: it may read the theme only as `theme.a.b` in a value, which becomes the CSS variable `var(--a-b)` the `ThemeProvider` sets +``` + +**Trigger:** + +{/* probe: class-theme-read before */} + +```tsx-error +import { ClassNames } from '@emotion/react'; +export const App = () => {({ theme }) =>
}; +``` + +**Why:** The ClassNames theme binding only becomes a CSS variable when used as a style value, not as an ordinary HTML attribute. + +**Fix:** Use a static theme path inside css(), or pass non-style data from a real runtime binding outside ClassNames. + +{/* probe: class-theme-read after */} + +```tsx +import { ClassNames } from '@emotion/react' +export const App = () => ( + + {({ css, theme }) =>
} + +) +``` + +## Imports and barrels + +### Reading an API imported through a barrel as a runtime value + +**API:** Imports and barrels. **Message pattern:** + +```text +`` is read at runtime, where it does not exist: the build compiles it only where it is called or rendered +``` + +**Trigger:** + +{/* probe: runtime-barrel-api before */} + +```tsx-error +import { css } from './ui'; +export const a = [css]; +``` + +**Companion-module fixture:** The imports above are resolved from these additional source modules. Before/after keys, where present, identify the failing and repaired resolver inputs. + +```json +{ + "ui.ts": "export { css } from '@devup-ui/react';\n" +} +``` + +**Why:** Following a barrel does not make a compile-time API into a runtime value. + +**Fix:** Call the resolved API; the barrel import itself is supported. + +{/* probe: runtime-barrel-api after */} + +```tsx +import { css } from './ui' +export const a = css({ color: 'red' }) +``` + +### Use a namespace instead of a root default import + +**API:** Imports and barrels. **Message pattern:** + +```text +`@emotion/css()` cannot use `` at build time: the root module has no default export; use named or namespace imports +``` + +**Trigger:** + +{/* probe: default-import before */} + +```tsx-error +import E from '@emotion/css'; +``` + +**Why:** The @emotion/css root module has no default export. + +**Fix:** Import the namespace and call its css member directly. + +{/* probe: default-import after */} + +```tsx +import * as E from '@emotion/css' +export const a = E.css({ color: 'red' }) +``` + +### Do not import the named default export + +**API:** Imports and barrels. **Message pattern:** + +```text +`@emotion/css()` cannot use `` at build time: the root module has no default export +``` + +**Trigger:** + +{/* probe: named-default before */} + +```tsx-error +import {default as E} from '@emotion/css'; +``` + +**Why:** The root has no export named default either. + +**Fix:** Import the named css API and export its compiled result. + +{/* probe: named-default after */} + +```tsx +import { css } from '@emotion/css' +export const a = css({ color: 'red' }) +``` + +### Do not forward every Emotion export + +**API:** Imports and barrels. **Message pattern:** + +```text +`@emotion/css()` cannot use `` at build time: a namespace containing styling functions cannot be re-exported +``` + +**Trigger:** + +{/* probe: star-reexport before */} + +```tsx-error +export * from '@emotion/css'; +``` + +**Why:** Styling functions are compiled away and cannot be provided as runtime exports. + +**Fix:** Explicitly re-export only runtime members such as flush and cache. + +{/* probe: star-reexport after */} + +```tsx +export { cache, flush } from '@emotion/css' +``` + +### Export a compiled class, not the css function + +**API:** Imports and barrels. **Message pattern:** + +```text +`@emotion/css()` cannot use `` at build time: styling functions cannot be re-exported +``` + +**Trigger:** + +{/* probe: style-reexport before */} + +```tsx-error +export {css} from '@emotion/css'; +``` + +**Why:** Styling functions are compiled away and cannot be provided as runtime exports. + +**Fix:** Import css locally, call it, and export the resulting class string. + +{/* probe: style-reexport after */} + +```tsx +import { css } from '@emotion/css' +export const red = css({ color: 'red' }) +``` + +### Do not export a require import-equals namespace + +**API:** Imports and barrels. **Message pattern:** + +```text +`@emotion/css()` cannot use `` at build time: a namespace containing styling functions cannot be exported +``` + +**Trigger:** + +{/* probe: export-import-equals before */} + +```tsx-error +export import E = require('@emotion/css'); +``` + +**Why:** The namespace contains compile-time styling functions, not a general-purpose runtime object. + +**Fix:** Replace the namespace export with explicit runtime-only ES re-exports. + +{/* probe: export-import-equals after */} + +```tsx +export { cache, flush } from '@emotion/css' +``` + +### Keep namespace aliases local + +**API:** Imports and barrels. **Message pattern:** + +```text +`@emotion/css()` cannot use `` at build time: namespace and styling function bindings cannot be exported +``` + +**Trigger:** + +{/* probe: export-namespace-alias before */} + +```tsx-error +import * as E from '@emotion/css'; +export const N = E; +``` + +**Why:** The namespace contains compile-time styling functions, not a general-purpose runtime object. + +**Fix:** Declare the namespace alias locally and export only a compiled class. + +{/* probe: export-namespace-alias after */} + +```tsx +import * as E from '@emotion/css' +const N = E +export const red = N.css({ color: 'red' }) +``` + +### Do not export an imported namespace binding + +**API:** Imports and barrels. **Message pattern:** + +```text +`@emotion/css()` cannot use `` at build time: a namespace may only be read through statically known members or const aliases +``` + +**Trigger:** + +{/* probe: export-namespace before */} + +```tsx-error +import * as E from '@emotion/css'; +export {E}; +``` + +**Why:** The namespace contains compile-time styling functions, not a general-purpose runtime object. + +**Fix:** Call a static member and export its compiled result. + +{/* probe: export-namespace after */} + +```tsx +import * as E from '@emotion/css' +export const red = E.css({ color: 'red' }) +``` + +### Call styling functions without optional invocation + +**API:** Imports and barrels. **Message pattern:** + +```text +`@emotion/css()` cannot use `` at build time: styling functions must be called directly, not mutated, exported or passed as values +``` + +**Trigger:** + +{/* probe: optional-call before */} + +```tsx-error +import * as E from '@emotion/css'; +E.css?.({}); +``` + +**Why:** An optional call is not a direct compile-time invocation. + +**Fix:** Remove optional invocation and call the API directly. + +{/* probe: optional-call after */} + +```tsx +import * as E from '@emotion/css' +export const red = E.css({ color: 'red' }) +``` + +### Use const for namespace aliases + +**API:** Imports and barrels. **Message pattern:** + +```text +`@emotion/css()` cannot use `` at build time: namespace and styling aliases must be simple const bindings +`@emotion/css()` cannot use `` at build time: a namespace may only be read through statically known members or const aliases +``` + +**Trigger:** + +{/* probe: let-namespace before */} + +```tsx-error +import * as E from '@emotion/css'; +let N = E; +``` + +**Why:** The build follows and eliminates only simple const namespace or styling aliases. + +**Fix:** Replace let with const and access a known member. + +{/* probe: let-namespace after */} + +```tsx +import * as E from '@emotion/css' +const N = E +export const red = N.css({ color: 'red' }) +``` + +### Use const for styling aliases + +**API:** Imports and barrels. **Message pattern:** + +```text +`@emotion/css()` cannot use `` at build time: namespace and styling aliases must be simple const bindings +`@emotion/css()` cannot use `` at build time: styling functions must be called directly, not mutated, exported or passed as values +``` + +**Trigger:** + +{/* probe: let-style before */} + +```tsx-error +import * as E from '@emotion/css'; +let c = E.css; +``` + +**Why:** The build follows and eliminates only simple const namespace or styling aliases. + +**Fix:** Bind the styling member with const and call it directly. + +{/* probe: let-style after */} + +```tsx +import * as E from '@emotion/css' +const c = E.css +export const red = c({ color: 'red' }) +``` + +### Name namespace members instead of using rest + +**API:** Imports and barrels. **Message pattern:** + +```text +`@emotion/css()` cannot use `` at build time: namespace rest destructuring can expose styling functions +``` + +**Trigger:** + +{/* probe: rest-destructure before */} + +```tsx-error +import * as E from '@emotion/css'; +const {...rest} = E; +``` + +**Why:** Namespace rest can expose styling functions to runtime code. + +**Fix:** List only the required runtime-only members explicitly. + +{/* probe: rest-destructure after */} + +```tsx +import * as E from '@emotion/css' +const { flush, cache } = E +flush() +console.info(cache) +``` + +### Use constant strings for destructuring keys + +**API:** Imports and barrels. **Message pattern:** + +```text +`@emotion/css()` cannot use `` at build time: destructuring keys must be constant strings +``` + +**Trigger:** + +{/* probe: computed-destructure before */} + +```tsx-error +import * as E from '@emotion/css'; +const {[key]: c} = E; +``` + +**Why:** The normalizer must determine which API a destructuring property names. + +**Fix:** Declare the string key as a const before the destructuring. + +{/* probe: computed-destructure after */} + +```tsx +import * as E from '@emotion/css' +const key = 'css' +const { [key]: c } = E +export const red = c({ color: 'red' }) +``` + +### Do not default a destructured styling function + +**API:** Imports and barrels. **Message pattern:** + +```text +`@emotion/css()` cannot use `` at build time: use a plain binding without defaults or nested patterns +``` + +**Trigger:** + +{/* probe: default-destructure before */} + +```tsx-error +import * as E from '@emotion/css'; +const {css = other} = E; +``` + +**Why:** A fallback would give an eliminated macro binding a runtime meaning. + +**Fix:** Use a plain binding without a default, then call it directly. + +{/* probe: default-destructure after */} + +```tsx +import * as E from '@emotion/css' +const { css } = E +export const red = css({ color: 'red' }) +``` + +### Read only recognized Emotion namespace members + +**API:** Imports and barrels. **Message pattern:** + +```text +`@emotion/css()` cannot use `` at build time: use a supported styling API or a runtime-only Emotion export +``` + +**Trigger:** + +{/* probe: unknown-member before */} + +```tsx-error +import * as E from '@emotion/css'; +E.unknown({}); +``` + +**Why:** The build recognizes css, cx, merge, keyframes, injectGlobal and the runtime members cache, flush, hydrate, sheet and getRegisteredStyles. + +**Fix:** Use a supported styling member such as css. + +{/* probe: unknown-member after */} + +```tsx +import * as E from '@emotion/css' +export const red = E.css({ color: 'red' }) +``` + +### Initialize namespace aliases in dependency order + +**API:** Imports and barrels. **Message pattern:** + +```text +`@emotion/css()` cannot use `` at build time: an alias must be initialized before any read that can run first; declare it above the earliest code that can reach this read +``` + +**Trigger:** + +{/* probe: early-namespace before */} + +```tsx-error +import * as E from '@emotion/css'; +const N = A; +const A = E; +``` + +**Why:** Eliminating an alias must not hide a read that can occur in its temporal dead zone. + +**Fix:** Declare the source namespace alias before aliases derived from it. + +{/* probe: early-namespace after */} + +```tsx +import * as E from '@emotion/css' +const A = E +const N = A +export const red = N.css({ color: 'red' }) +``` + +### Use a statically known namespace API key + +**API:** Imports and barrels. **Message pattern:** + +```text +`@emotion/css()` cannot use `` at build time: namespace member keys must be constant strings +``` + +**Trigger:** + +{/* probe: computed-member before */} + +```tsx-error +import * as E from '@emotion/css'; +E[key]({color:'red'}); +``` + +**Why:** A runtime key cannot determine which styling import must be generated. + +**Fix:** Use a const string key initialized before the read. + +{/* probe: computed-member after */} + +```tsx +import * as E from '@emotion/css' +const key = 'css' +export const red = E[key]({ color: 'red' }) +``` + +### Do not optional-chain a namespace styling read + +**API:** Imports and barrels. **Message pattern:** + +```text +`@emotion/css()` cannot use `` at build time: optional namespace reads cannot be compiled +``` + +**Trigger:** + +{/* probe: optional-member before */} + +```tsx-error +import * as E from '@emotion/css'; +E?.css({}); +``` + +**Why:** Optional namespace styling reads cannot be lowered to unconditional generated imports. + +**Fix:** Use a normal static member call. + +{/* probe: optional-member after */} + +```tsx +import * as E from '@emotion/css' +export const red = E.css({ color: 'red' }) +``` + +### Do not load styling APIs through module.require + +**API:** Imports and barrels. **Message pattern:** + +```text +`@emotion/css()` cannot use `` at build time: styling APIs read through require(), import-equals or dynamic import cannot be compiled; use `import * as ns from '@emotion/css'` +``` + +**Trigger:** + +{/* probe: module-require before */} + +```tsx-error +const E = module.require('@emotion/css'); +E.cx('a'); +``` + +**Why:** A runtime loader does not provide the static ES imports needed for build-time styling extraction. + +**Fix:** Use a static namespace import before calling cx. + +{/* probe: module-require after */} + +```tsx +import * as E from '@emotion/css' +export const a = E.cx('a') +``` + +### Await dynamic imports directly for runtime-only use + +**API:** Imports and barrels. **Message pattern:** + +```text +`@emotion/css()` cannot use `` at build time: a dynamic import hands out a runtime namespace; `await` it directly for runtime-only members, or use `import * as ns from '@emotion/css'` +``` + +**Trigger:** + +{/* probe: unawaited-runtime before */} + +```tsx-error +const p = import('@emotion/css'); +``` + +**Why:** A stored import promise is rejected even if no styling member is visibly read. + +**Fix:** Await the import directly and read only a runtime-only member. + +{/* probe: unawaited-runtime after */} + +```tsx +const E = await import('@emotion/css') +E.flush() +``` + +### Repair malformed source when diagnostic restoration fails + +**API:** Imports and barrels. **Message pattern:** + +```text +`@emotion/css()` cannot use `` at build time: use a supported styling API or a runtime-only Emotion export +Emotion diagnostic restoration failed: Diagnostics([OxcDiagnostic { inner: OxcDiagnosticInner { message: "Missing initializer in const declaration", labels: [LabeledSpan { label: None, span: Span { start: 61, end: 67 }, primary: false }], help: Some("Add an initializer (e.g. ` = undefined`) here"), note: None, severity: Error, code: OxcCode { scope: None, number: None }, url: None } }]) +``` + +**Trigger:** + +{/* probe: restoration-suffix before */} + +```tsx-error +import * as E from '@emotion/css'; +E.__emotion_user(); const broken; +``` + +**Why:** An error expression containing the generated-name prefix invokes original-source diagnostic restoration; malformed source prevents that restoration. + +**Fix:** Correct the malformed declaration and use a supported namespace member. + +{/* probe: restoration-suffix after */} + +```tsx +import * as E from '@emotion/css' +export const red = E.css({ color: 'red' }) +``` + +### Array destructuring a namespace + +**API:** Imports and barrels. **Message pattern:** + +```text +`Devup` cannot use `` at build time: read its members by name, as `Devup.css`, `Devup['css']` or `const { css } = Devup`, or import them by name +``` + +**Trigger:** + +{/* probe: array-destructure before */} + +```tsx-error +import * as Devup from '@devup-ui/react'; +const [a] = Devup; +``` + +**Why:** The namespace rewrite requires an exact member or a simple, unmixed object destructure; it cannot rewrite this shape. + +**Fix:** Read the desired API by name, rather than destructuring the namespace as an array. + +{/* probe: array-destructure after */} + +```tsx +import * as Devup from '@devup-ui/react' +export const a = Devup.css({ color: 'red' }) +``` + +### Writing a followed const API alias in a function + +**API:** Imports and barrels. **Message pattern:** + +```text +`c` cannot use `` at build time: read its members by name, as `c.css`, `c['css']` or `const { css } = c`, or import them by name +``` + +**Trigger:** + +{/* probe: mutated-const-alias before */} + +```tsx-error +import { css } from '@devup-ui/react'; +export function f() { const c = css; c = () => 1; return c({ color: "red" }); } +``` + +**Why:** A followed alias cannot be reassigned after the rewrite removes its declaration. + +**Fix:** Keep the const alias unchanged and call it. + +{/* probe: mutated-const-alias after */} + +```tsx +import { css } from '@devup-ui/react' +export function f() { + const c = css + return c({ color: 'red' }) +} +``` + +### Writing a member through a const namespace alias + +**API:** Imports and barrels. **Message pattern:** + +```text +`D` cannot use `` at build time: read its members by name, as `D.css`, `D['css']` or `const { css } = D`, or import them by name +``` + +**Trigger:** + +{/* probe: namespace-alias-write before */} + +```tsx-error +import * as Devup from '@devup-ui/react'; +const D = Devup; +D.css = () => 1; +``` + +**Why:** Const namespace aliases are followed, so member writes use the same unreadable-namespace diagnostic. + +**Fix:** Keep the namespace alias immutable and only read exact members. + +{/* probe: namespace-alias-write after */} + +```tsx +import * as Devup from '@devup-ui/react' +const D = Devup +export const a = D.css({ color: 'red' }) +``` + +### Reading a css result before its declaration executes + +**API:** Imports and barrels. **Message pattern:** + +```text +`css()` cannot use `` at build time: it is read before `const k = css(…)` runs, so move that declaration above where it is first read +``` + +**Trigger:** + +{/* probe: early-css-result before */} + +```tsx-error +import { css } from '@devup-ui/react'; +export const a = k; +const k = css({ color: 'red' }); +``` + +**Why:** An eager top-level read precedes the const initializer; only deferred function reads of literal style constants may be hoisted. + +**Fix:** Move the initializer above the first eager read. + +{/* probe: early-css-result after */} + +```tsx +import { css } from '@devup-ui/react' +const k = css({ color: 'red' }) +export const a = k +``` + +### Reading a keyframes result before its declaration executes + +**API:** Imports and barrels. **Message pattern:** + +```text +`keyframes()` cannot use `` at build time: it is read before `const k = keyframes(…)` runs, so move that declaration above where it is first read +``` + +**Trigger:** + +{/* probe: early-keyframes-result before */} + +```tsx-error +import { keyframes } from '@devup-ui/react'; +export const a = k; +const k = keyframes({ from: { opacity: 0 } }); +``` + +**Why:** An eager top-level read precedes the const initializer; only deferred function reads of literal style constants may be hoisted. + +**Fix:** Move the initializer above the first eager read. + +{/* probe: early-keyframes-result after */} + +```tsx +import { keyframes } from '@devup-ui/react' +const k = keyframes({ from: { opacity: 0 } }) +export const a = k +``` + +### Unresolved star re-export in a barrel that reaches Devup UI + +**API:** Imports and barrels. **Message pattern:** + +```text +`Foo` cannot use `` at build time: the build cannot follow its re-export of Devup UI (`./missing` cannot be read); import it from the package instead +``` + +**Trigger:** + +{/* probe: unreadable-star-barrel before */} + +```tsx-error +import { Foo } from './ui'; +export const a = ; +``` + +**Companion-module fixture:** The imports above are resolved from these additional source modules. Before/after keys, where present, identify the failing and repaired resolver inputs. + +```json +{ + "ui.ts": "export * from './missing';\nexport { Box } from '@devup-ui/react';\n" +} +``` + +**Why:** The barrel mentions the package but the missing re-export cannot be read by the resolver. + +**Fix:** Import the desired compile-time member directly from the package, or make the re-export resolvable. + +{/* probe: unreadable-star-barrel after */} + +```tsx +import { Box } from '@devup-ui/react' +export const a = +``` + +### Unresolved default re-export reports every value use + +**API:** Imports and barrels. **Message pattern:** + +```text +`Page` cannot use `` at build time: the build cannot follow its re-export of Devup UI (`./missing` cannot be read); import it from the package instead +`Page` cannot use `` at build time: the build cannot follow its re-export of Devup UI (`./missing` cannot be read); import it from the package instead +``` + +**Trigger:** + +{/* probe: unreadable-default-barrel before */} + +```tsx-error +import Page from './ui'; +export const a = [Page, Page]; +``` + +**Companion-module fixture:** The imports above are resolved from these additional source modules. Before/after keys, where present, identify the failing and repaired resolver inputs. + +```json +{ + "ui.ts": "export { default } from './missing';\nexport { Box } from '@devup-ui/react';\n" +} +``` + +**Why:** Each value reference of the opaque default import has its own source location. + +**Fix:** Replace the opaque default import with a known named package import. + +{/* probe: unreadable-default-barrel after */} + +```tsx +import { Box } from '@devup-ui/react' +export const a = +``` + +### Unresolved member of a barrel namespace + +**API:** Imports and barrels. **Message pattern:** + +```text +`UI.nothing` cannot use `` at build time: the build cannot follow its re-export of Devup UI (`./missing` cannot be read); import it from the package instead +``` + +**Trigger:** + +{/* probe: unreadable-barrel-member before */} + +```tsx-error +import * as UI from './ui'; +export const a = UI.nothing; +``` + +**Companion-module fixture:** The imports above are resolved from these additional source modules. Before/after keys, where present, identify the failing and repaired resolver inputs. + +```json +{ + "ui.ts": "export * from './missing';\nexport { Box } from '@devup-ui/react';\n" +} +``` + +**Why:** The namespace member leads through an unreadable star re-export. + +**Fix:** Read a known package export directly rather than an unresolved barrel member. + +{/* probe: unreadable-barrel-member after */} + +```tsx +import { Box } from '@devup-ui/react' +export const a = +``` + +### Destructuring an unresolved barrel member + +**API:** Imports and barrels. **Message pattern:** + +```text +`{ nothing } = UI` cannot use `` at build time: the build cannot follow its re-export of Devup UI (`./missing` cannot be read); import it from the package instead +``` + +**Trigger:** + +{/* probe: unreadable-barrel-destructure before */} + +```tsx-error +import * as UI from './ui'; +const { nothing } = UI; +export const a = nothing; +``` + +**Companion-module fixture:** The imports above are resolved from these additional source modules. Before/after keys, where present, identify the failing and repaired resolver inputs. + +```json +{ + "ui.ts": "export * from './missing';\nexport { Box } from '@devup-ui/react';\n" +} +``` + +**Why:** Destructuring must resolve each compiled member to its package origin. + +**Fix:** Import a known member from the package or repair the barrel chain. + +{/* probe: unreadable-barrel-destructure after */} + +```tsx +import { Box } from '@devup-ui/react' +export const a = +``` + +### Namespace re-export of a module that itself re-exports Devup UI + +**API:** Imports and barrels. **Message pattern:** + +```text +`inner` cannot use `` at build time: the build cannot follow its re-export of Devup UI (`./inner` is a namespace of modules re-exporting `@devup-ui/react`); import it from the package instead +``` + +**Trigger:** + +{/* probe: nested-barrel-namespace before */} + +```tsx-error +import { inner } from './ui'; +export const a = ; +``` + +**Companion-module fixture:** The imports above are resolved from these additional source modules. Before/after keys, where present, identify the failing and repaired resolver inputs. + +```json +{ + "ui.ts": "export * as inner from './inner';\n", + "inner.ts": "export { Box } from '@devup-ui/react';\n" +} +``` + +**Why:** A module namespace whose contents re-export the package is not a single exact package namespace. + +**Fix:** Import the component from the package instead of passing through a module namespace. + +{/* probe: nested-barrel-namespace after */} + +```tsx +import { Box } from '@devup-ui/react' +export const a = +``` + +### Nested module namespace read through an outer namespace + +**API:** Imports and barrels. **Message pattern:** + +```text +`UI.inner` cannot use `` at build time: the build cannot follow its re-export of Devup UI (`./inner` is a namespace of modules re-exporting `@devup-ui/react`); import it from the package instead +``` + +**Trigger:** + +{/* probe: nested-barrel-member before */} + +```tsx-error +import * as UI from './ui'; +export const a = UI.inner; +``` + +**Companion-module fixture:** The imports above are resolved from these additional source modules. Before/after keys, where present, identify the failing and repaired resolver inputs. + +```json +{ + "ui.ts": "export * as inner from './inner';\n", + "inner.ts": "export { Box } from '@devup-ui/react';\n" +} +``` + +**Why:** The outer namespace member names a module namespace re-exporting the package. + +**Fix:** Import the exact package member directly. + +{/* probe: nested-barrel-member after */} + +```tsx +import { Box } from '@devup-ui/react' +export const a = +``` + +### Destructuring a nested module namespace + +**API:** Imports and barrels. **Message pattern:** + +```text +`{ inner } = UI` cannot use `` at build time: the build cannot follow its re-export of Devup UI (`./inner` is a namespace of modules re-exporting `@devup-ui/react`); import it from the package instead +``` + +**Trigger:** + +{/* probe: nested-barrel-destructure before */} + +```tsx-error +import * as UI from './ui'; +const { inner } = UI; +export const a = inner; +``` + +**Companion-module fixture:** The imports above are resolved from these additional source modules. Before/after keys, where present, identify the failing and repaired resolver inputs. + +```json +{ + "ui.ts": "export * as inner from './inner';\n", + "inner.ts": "export { Box } from '@devup-ui/react';\n" +} +``` + +**Why:** The destructured member is a namespace of modules, rather than a supported exact package origin. + +**Fix:** Import the exact package component directly. + +{/* probe: nested-barrel-destructure after */} + +```tsx +import { Box } from '@devup-ui/react' +export const a = +``` + +### Mixed compiled and locally declared barrel members + +**API:** Imports and barrels. **Message pattern:** + +```text +`UI` cannot use `` at build time: read its members by name, as `UI.css`, `UI['css']` or `const { css } = UI`, or import them by name +``` + +**Trigger:** + +{/* probe: mixed-barrel-destructure before */} + +```tsx-error +import * as UI from './ui'; +const { Box, own } = UI; +export const a = {own}; +``` + +**Companion-module fixture:** The imports above are resolved from these additional source modules. Before/after keys, where present, identify the failing and repaired resolver inputs. + +```json +{ + "ui.ts": "export { Box } from '@devup-ui/react';\nexport const own = 1;\n" +} +``` + +**Why:** A destructuring declaration containing both compiled imports and retained barrel values cannot be removed as a whole. + +**Fix:** Keep local/runtime members in their own destructure and read compiled members by name. + +{/* probe: mixed-barrel-destructure after */} + +```tsx +import * as UI from './ui' +const { own } = UI +export const a = {own} +``` + +## vanilla-extract + +### Recipes value import + +**API:** vanilla-extract. **Message pattern:** + +```text +`@vanilla-extract/recipes` generates CSS that Devup UI does not compile, so with the `@vanilla-extract/css` alias its rules would never reach the stylesheet. Fix: write the styles with `css()`, or set `importAliases: { '@vanilla-extract/css': false }` and build with the vanilla-extract plugin +``` + +**Trigger:** + +{/* probe: recipes before */} + +```tsx-error +import { recipe } from '@vanilla-extract/recipes'; +export const button = recipe({ base: { color: 'red' }, variants: { size: { small: { padding: 4 }, large: { padding: 8 } } } }); +``` + +**Why:** On the companion branch, a non-type recipes import while the vanilla CSS alias is active is rejected before stylesheet execution. The authored message includes original filename:line:column. This replaces silent pass-through on the scoped snapshot. + +**Fix:** Replace the companion-generated styles with css(); this example covers one selected variant, not recipe runtime behavior. + +{/* probe: recipes after */} + +```tsx +import { css } from '@devup-ui/react' +export const button = css({ color: 'red', padding: '4px' }) +``` + +### Sprinkles value import + +**API:** vanilla-extract. **Message pattern:** + +```text +`@vanilla-extract/sprinkles` generates CSS that Devup UI does not compile, so with the `@vanilla-extract/css` alias its rules would never reach the stylesheet. Fix: write the styles with `css()`, or set `importAliases: { '@vanilla-extract/css': false }` and build with the vanilla-extract plugin +``` + +**Trigger:** + +{/* probe: sprinkles before */} + +```tsx-error +import { defineProperties, createSprinkles } from '@vanilla-extract/sprinkles'; +const properties = defineProperties({ properties: { color: ['red', 'blue'] } }); +export const sprinkles = createSprinkles(properties); +export const red = sprinkles({ color: 'red' }); +``` + +**Why:** On the companion branch, a non-type sprinkles import while the vanilla CSS alias is active is rejected before stylesheet execution. The authored message includes original filename:line:column. This replaces silent pass-through on the scoped snapshot. + +**Fix:** Replace the selected style with css(); this is not a full sprinkles runtime API replacement. + +{/* probe: sprinkles after */} + +```tsx +import { css } from '@devup-ui/react' +export const red = css({ color: 'red' }) +``` + +## StyleX + +Static namespaces need known declarations. Dynamic namespaces expose direct parameters as CSS variables; compute a derived value before passing it to that namespace. `props()` and `attrs()` compose the extracted classes. Helpers are interpreted only in their supported contexts. + +### create(): array argument shape + +**API:** StyleX. **Message pattern:** + +```text +`stylex.create()` cannot use `` at build time: it takes one object literal +``` + +**Trigger:** + +{/* probe: stylex-create-argument-array before */} + +```tsx-error +import * as stylex from '@stylexjs/stylex'; +export function example() { + const result = stylex.create([]); + return result; +} +``` + +**Why:** The scoped extractor lowers this API only when its argument list has the exact object-literal shape. Runtime inputs cannot become an extracted declaration or namespace map. + +**Fix:** Pass the object literal directly with the required number of arguments, rather than a runtime value or argument spread. + +{/* probe: stylex-create-argument-array after */} + +```tsx +import * as stylex from '@stylexjs/stylex' +export function example() { + const result = stylex.create({ base: { color: 'red' } }) + return result +} +``` + +### defineVars(): array argument shape + +**API:** StyleX. **Message pattern:** + +```text +`stylex.defineVars()` cannot use `` at build time: it takes one object literal +``` + +**Trigger:** + +{/* probe: stylex-defineVars-argument-array before */} + +```tsx-error +import * as stylex from '@stylexjs/stylex'; +export function example() { + const result = stylex.defineVars([]); + return result; +} +``` + +**Why:** The scoped extractor lowers this API only when its argument list has the exact object-literal shape. Runtime inputs cannot become an extracted declaration or namespace map. + +**Fix:** Pass the object literal directly with the required number of arguments, rather than a runtime value or argument spread. + +{/* probe: stylex-defineVars-argument-array after */} + +```tsx +import * as stylex from '@stylexjs/stylex' +export function example() { + const result = stylex.defineVars({ color: 'red' }) + return result +} +``` + +### defineConsts(): array argument shape + +**API:** StyleX. **Message pattern:** + +```text +`stylex.defineConsts()` cannot use `` at build time: it takes one object literal +``` + +**Trigger:** + +{/* probe: stylex-defineConsts-argument-array before */} + +```tsx-error +import * as stylex from '@stylexjs/stylex'; +export function example() { + const result = stylex.defineConsts([]); + return result; +} +``` + +**Why:** The scoped extractor lowers this API only when its argument list has the exact object-literal shape. Runtime inputs cannot become an extracted declaration or namespace map. + +**Fix:** Pass the object literal directly with the required number of arguments, rather than a runtime value or argument spread. + +{/* probe: stylex-defineConsts-argument-array after */} + +```tsx +import * as stylex from '@stylexjs/stylex' +export function example() { + const result = stylex.defineConsts({ color: 'red' }) + return result +} +``` + +### createThemeContract(): array argument shape + +**API:** StyleX. **Message pattern:** + +```text +`stylex.createThemeContract()` cannot use `` at build time: it takes one object literal +``` + +**Trigger:** + +{/* probe: stylex-createThemeContract-argument-array before */} + +```tsx-error +import * as stylex from '@stylexjs/stylex'; +export function example() { + const result = stylex.createThemeContract([]); + return result; +} +``` + +**Why:** The scoped extractor lowers this API only when its argument list has the exact object-literal shape. Runtime inputs cannot become an extracted declaration or namespace map. + +**Fix:** Pass the object literal directly with the required number of arguments, rather than a runtime value or argument spread. + +{/* probe: stylex-createThemeContract-argument-array after */} + +```tsx +import * as stylex from '@stylexjs/stylex' +export function example() { + const result = stylex.createThemeContract({ color: null }) + return result +} +``` + +### positionTry(): array argument shape + +**API:** StyleX. **Message pattern:** + +```text +`stylex.positionTry()` cannot use `` at build time: it takes one object literal +``` + +**Trigger:** + +{/* probe: stylex-positionTry-argument-array before */} + +```tsx-error +import * as stylex from '@stylexjs/stylex'; +export function example() { + const result = stylex.positionTry([]); + return result; +} +``` + +**Why:** The scoped extractor lowers this API only when its argument list has the exact object-literal shape. Runtime inputs cannot become an extracted declaration or namespace map. + +**Fix:** Pass the object literal directly with the required number of arguments, rather than a runtime value or argument spread. + +{/* probe: stylex-positionTry-argument-array after */} + +```tsx +import * as stylex from '@stylexjs/stylex' +export function example() { + const result = stylex.positionTry({ top: 0 }) + return result +} +``` + +### viewTransitionClass(): array argument shape + +**API:** StyleX. **Message pattern:** + +```text +`stylex.viewTransitionClass()` cannot use `` at build time: it takes one object literal +``` + +**Trigger:** + +{/* probe: stylex-viewTransitionClass-argument-array before */} + +```tsx-error +import * as stylex from '@stylexjs/stylex'; +export function example() { + const result = stylex.viewTransitionClass([]); + return result; +} +``` + +**Why:** The scoped extractor lowers this API only when its argument list has the exact object-literal shape. Runtime inputs cannot become an extracted declaration or namespace map. + +**Fix:** Pass the object literal directly with the required number of arguments, rather than a runtime value or argument spread. + +{/* probe: stylex-viewTransitionClass-argument-array after */} + +```tsx +import * as stylex from '@stylexjs/stylex' +export function example() { + const result = stylex.viewTransitionClass({ color: 'red' }) + return result +} +``` + +### keyframes(): array argument shape + +**API:** StyleX. **Message pattern:** + +```text +`stylex.keyframes()` cannot use `` at build time: it takes one object literal +``` + +**Trigger:** + +{/* probe: stylex-keyframes-argument-array before */} + +```tsx-error +import * as stylex from '@stylexjs/stylex'; +export function example() { + const result = stylex.keyframes([]); + return result; +} +``` + +**Why:** The scoped extractor lowers this API only when its argument list has the exact object-literal shape. Runtime inputs cannot become an extracted declaration or namespace map. + +**Fix:** Pass the object literal directly with the required number of arguments, rather than a runtime value or argument spread. + +{/* probe: stylex-keyframes-argument-array after */} + +```tsx +import * as stylex from '@stylexjs/stylex' +export function example() { + const result = stylex.keyframes({ from: { opacity: 0 }, to: { opacity: 1 } }) + return result +} +``` + +### createTheme(): spread-argument argument shape + +**API:** StyleX. **Message pattern:** + +```text +`stylex.createTheme()` cannot use `` at build time: it takes a `defineVars()` group, of this file or imported, and an object literal +``` + +**Trigger:** + +{/* probe: stylex-createTheme-argument-spread-argument before */} + +```tsx-error +import * as stylex from '@stylexjs/stylex'; +const vars = stylex.defineVars({ color: 'red' }); +export function example(args) { + const result = stylex.createTheme(...args); + return result; +} +``` + +**Why:** createTheme needs an identifier bound to a defineVars/createThemeContract group known by the visitor, followed by an object literal of overrides. An arbitrary object or runtime reference is not a registered contract. + +**Fix:** Define the contract first, bind it to one identifier, and pass that identifier with one override object. + +{/* probe: stylex-createTheme-argument-spread-argument after */} + +```tsx +import * as stylex from '@stylexjs/stylex' +const vars = stylex.defineVars({ color: 'red' }) +export function example() { + const result = stylex.createTheme(vars, { color: 'blue' }) + return result +} +``` + +### Array destructuring cannot bind create() namespaces + +**API:** StyleX. **Message pattern:** + +```text +`stylex.create()` cannot be destructured at build time: assign it to one variable, as `const styles = stylex.create({ ... })` +``` + +**Trigger:** + +{/* probe: stylex-create-destructure-array before */} + +```tsx-error +import * as stylex from '@stylexjs/stylex'; +export const [base] = stylex.create({ base: { color: 'red' } }); +``` + +**Why:** The extractor associates a namespace map with one variable binding. Destructuring the create result prevents that map from being registered for props/attrs and includes. + +**Fix:** Assign create() to styles and read styles.base instead of destructuring the declaration. + +{/* probe: stylex-create-destructure-array after */} + +```tsx +import * as stylex from '@stylexjs/stylex' +export const styles = stylex.create({ base: { color: 'red' } }) +``` + +### create(): scalar namespace + +**API:** StyleX. **Message pattern:** + +```text +`stylex.create()` cannot use `` at build time: a namespace is an object of styles or an arrow function returning one +``` + +**Trigger:** + +{/* probe: stylex-namespace-scalar before */} + +```tsx-error +import * as stylex from '@stylexjs/stylex'; +export function example() { + const result = stylex.create({ base: 'red' }); + return result; +} +``` + +**Why:** Every create() entry is a namespace, not an individual CSS value. The supported namespace forms are an object of styles, an object-returning arrow, or null (empty namespace). + +**Fix:** Put CSS declarations inside a namespace object, or use a supported dynamic arrow. + +{/* probe: stylex-namespace-scalar after */} + +```tsx +import * as stylex from '@stylexjs/stylex' +export function example() { + const result = stylex.create({ base: { color: 'red' } }) + return result +} +``` + +### Dynamic arrow: scalar-return + +**API:** StyleX. **Message pattern:** + +```text +`stylex.create()` cannot use `` at build time: a dynamic style is an arrow function with plain parameters returning an object literal +``` + +**Trigger:** + +{/* probe: stylex-dynamic-scalar-return before */} + +```tsx-error +import * as stylex from '@stylexjs/stylex'; +export function example() { + const result = stylex.create({ base: (color) => color }); + return result; +} +``` + +**Why:** Dynamic namespace extraction reads plain parameter identifiers and an arrow expression whose body is an object literal. It does not execute a function body or destructuring pattern. + +**Fix:** Use (value) => (\{ color: value \}) with a plain identifier parameter and expression body. + +{/* probe: stylex-dynamic-scalar-return after */} + +```tsx +import * as stylex from '@stylexjs/stylex' +export function example() { + const result = stylex.create({ base: (color) => ({ color }) }) + return result +} +``` + +### Dynamic namespace value: runtime + +**API:** StyleX. **Message pattern:** + +```text +`stylex.create()` cannot use `` at build time: a dynamic style's value is one of its parameters or a static value; compute it before passing it +``` + +**Trigger:** + +{/* probe: stylex-dynamic-value-runtime before */} + +```tsx-error +import * as stylex from '@stylexjs/stylex'; +export function example(value) { + const result = stylex.create({ base: (width) => ({ width: value }) }); + return result; +} +``` + +**Why:** A dynamic namespace value can directly name one of its parameters or be static. Arithmetic, member reads, conditions and helper calls inside that arrow are not evaluated as dynamic CSS expressions. + +**Fix:** Compute the desired value at the call site and pass it to a plain parameter referenced directly by the style property. + +{/* probe: stylex-dynamic-value-runtime after */} + +```tsx +import * as stylex from '@stylexjs/stylex' +export function example() { + const result = stylex.create({ base: (width) => ({ width }) }) + return result +} +``` + +### Static style value: identifier + +**API:** StyleX. **Message pattern:** + +```text +`stylex.create()` cannot use `` at build time: its values must be literals, theme tokens or constants, or be computed from them +``` + +**Trigger:** + +{/* probe: stylex-static-value-identifier before */} + +```tsx-error +import * as stylex from '@stylexjs/stylex'; +export function example(value) { + const result = stylex.create({ base: { color: value } }); + return result; +} +``` + +**Why:** A static namespace has no runtime CSS variable assignment. Its leaf values must resolve at build time: literals, constants/computations the evaluator can fold, registered keyframe names or defineVars/defineConsts member references. + +**Fix:** Use a literal or resolvable constant; for genuinely runtime input, use a dynamic namespace parameter. + +{/* probe: stylex-static-value-identifier after */} + +```tsx +import * as stylex from '@stylexjs/stylex' +export function example() { + const result = stylex.create({ base: { color: 'red' } }) + return result +} +``` + +### Condition key: ordinary + +**API:** StyleX. **Message pattern:** + +```text +`stylex.create()` cannot use `` at build time: a condition is `default`, a pseudo-class, a pseudo-element or an `@media`, `@supports` or `@container` rule +``` + +**Trigger:** + +{/* probe: stylex-condition-ordinary before */} + +```tsx-error +import * as stylex from '@stylexjs/stylex'; +export function example() { + const result = stylex.create({ base: { color: { 'hover': 'red' } } }); + return result; +} +``` + +**Why:** A value-level object is interpreted as conditions, not an arbitrary nested value. Its keys must be default, a colon-prefixed pseudo-class/element, or an @media/@supports/@container rule. + +**Fix:** Replace the invalid condition with default or a supported pseudo/at-rule condition. + +{/* probe: stylex-condition-ordinary after */} + +```tsx +import * as stylex from '@stylexjs/stylex' +export function example() { + const result = stylex.create({ + base: { color: { default: 'red', ':hover': 'blue' } }, + }) + return result +} +``` + +### Pseudo selector: element-array + +**API:** StyleX. **Message pattern:** + +```text +`stylex.create()` cannot use `` at build time: a pseudo-class or pseudo-element key takes an object of styles +``` + +**Trigger:** + +{/* probe: stylex-pseudo-element-array before */} + +```tsx-error +import * as stylex from '@stylexjs/stylex'; +export function example() { + const result = stylex.create({ base: { '::before': [] } }); + return result; +} +``` + +**Why:** A top-level colon-prefixed namespace key denotes a pseudo selector whose value must be an object of CSS declarations. A scalar belongs under a property-level condition instead. + +**Fix:** Use \{ ":hover": \{ color: "red" \} \} or \{ color: \{ ":hover": "red" \} \}. + +{/* probe: stylex-pseudo-element-array after */} + +```tsx +import * as stylex from '@stylexjs/stylex' +export function example() { + const result = stylex.create({ base: { '::before': { color: 'red' } } }) + return result +} +``` + +### create(): spread in namespace-map + +**API:** StyleX. **Message pattern:** + +```text +`stylex.create()` cannot use `` at build time: write every entry out, as its keys must be known +``` + +**Trigger:** + +{/* probe: stylex-create-spread-namespace-map before */} + +```tsx-error +import * as stylex from '@stylexjs/stylex'; +export function example(extra) { + const result = stylex.create({ ...extra }); + return result; +} +``` + +**Why:** This StyleX object layer does not accept unresolved object spreads: the build needs each key. The sole namespace-level exception is a recognized ...include(styles.base) reference to an earlier local create namespace. + +**Fix:** Write the entries explicitly; for namespace composition, spread include() with an earlier same-file namespace. + +{/* probe: stylex-create-spread-namespace-map after */} + +```tsx +import * as stylex from '@stylexjs/stylex' +export function example() { + const result = stylex.create({ base: { color: 'red' } }) + return result +} +``` + +### create(): computed key in namespace-map + +**API:** StyleX. **Message pattern:** + +```text +`stylex.create()` cannot use `` at build time: its keys must be known +``` + +**Trigger:** + +{/* probe: stylex-create-key-namespace-map before */} + +```tsx-error +import * as stylex from '@stylexjs/stylex'; +export function example(key) { + const result = stylex.create({ [key]: {} }); + return result; +} +``` + +**Why:** The object-key reader accepts static identifiers and literal keys. A key computed from a runtime input has no statically extractable CSS property, namespace, condition or variable name. + +**Fix:** Use an explicit literal key (or a constant expression that the evaluator actually resolves). + +{/* probe: stylex-create-key-namespace-map after */} + +```tsx +import * as stylex from '@stylexjs/stylex' +export function example() { + const result = stylex.create({ base: { color: 'red' } }) + return result +} +``` + +### defineVars(): object spread + +**API:** StyleX. **Message pattern:** + +```text +`stylex.defineVars()` cannot use `` at build time: write every entry out, as its keys must be known +``` + +**Trigger:** + +{/* probe: stylex-defineVars-spread before */} + +```tsx-error +import * as stylex from '@stylexjs/stylex'; +export function example(extra) { + const result = stylex.defineVars({ ...extra }); + return result; +} +``` + +**Why:** This StyleX object layer does not accept unresolved object spreads: the build needs each key. The sole namespace-level exception is a recognized ...include(styles.base) reference to an earlier local create namespace. + +**Fix:** Write the entries explicitly; for namespace composition, spread include() with an earlier same-file namespace. + +{/* probe: stylex-defineVars-spread after */} + +```tsx +import * as stylex from '@stylexjs/stylex' +export function example() { + const result = stylex.defineVars({ color: 'red' }) + return result +} +``` + +### defineVars(): computed key + +**API:** StyleX. **Message pattern:** + +```text +`stylex.defineVars()` cannot use `` at build time: its keys must be known +``` + +**Trigger:** + +{/* probe: stylex-defineVars-key before */} + +```tsx-error +import * as stylex from '@stylexjs/stylex'; +export function example(key) { + const result = stylex.defineVars({ [key]: 'red' }); + return result; +} +``` + +**Why:** The object-key reader accepts static identifiers and literal keys. A key computed from a runtime input has no statically extractable CSS property, namespace, condition or variable name. + +**Fix:** Use an explicit literal key (or a constant expression that the evaluator actually resolves). + +{/* probe: stylex-defineVars-key after */} + +```tsx +import * as stylex from '@stylexjs/stylex' +export function example() { + const result = stylex.defineVars({ color: 'red' }) + return result +} +``` + +### defineConsts(): object spread + +**API:** StyleX. **Message pattern:** + +```text +`stylex.defineConsts()` cannot use `` at build time: write every entry out, as its keys must be known +``` + +**Trigger:** + +{/* probe: stylex-defineConsts-spread before */} + +```tsx-error +import * as stylex from '@stylexjs/stylex'; +export function example(extra) { + const result = stylex.defineConsts({ ...extra }); + return result; +} +``` + +**Why:** This StyleX object layer does not accept unresolved object spreads: the build needs each key. The sole namespace-level exception is a recognized ...include(styles.base) reference to an earlier local create namespace. + +**Fix:** Write the entries explicitly; for namespace composition, spread include() with an earlier same-file namespace. + +{/* probe: stylex-defineConsts-spread after */} + +```tsx +import * as stylex from '@stylexjs/stylex' +export function example() { + const result = stylex.defineConsts({ color: 'red' }) + return result +} +``` + +### defineConsts(): computed key + +**API:** StyleX. **Message pattern:** + +```text +`stylex.defineConsts()` cannot use `` at build time: its keys must be known +``` + +**Trigger:** + +{/* probe: stylex-defineConsts-key before */} + +```tsx-error +import * as stylex from '@stylexjs/stylex'; +export function example(key) { + const result = stylex.defineConsts({ [key]: 'red' }); + return result; +} +``` + +**Why:** The object-key reader accepts static identifiers and literal keys. A key computed from a runtime input has no statically extractable CSS property, namespace, condition or variable name. + +**Fix:** Use an explicit literal key (or a constant expression that the evaluator actually resolves). + +{/* probe: stylex-defineConsts-key after */} + +```tsx +import * as stylex from '@stylexjs/stylex' +export function example() { + const result = stylex.defineConsts({ color: 'red' }) + return result +} +``` + +### createThemeContract(): object spread + +**API:** StyleX. **Message pattern:** + +```text +`stylex.createThemeContract()` cannot use `` at build time: write every entry out, as its keys must be known +``` + +**Trigger:** + +{/* probe: stylex-createThemeContract-spread before */} + +```tsx-error +import * as stylex from '@stylexjs/stylex'; +export function example(extra) { + const result = stylex.createThemeContract({ ...extra }); + return result; +} +``` + +**Why:** This StyleX object layer does not accept unresolved object spreads: the build needs each key. The sole namespace-level exception is a recognized ...include(styles.base) reference to an earlier local create namespace. + +**Fix:** Write the entries explicitly; for namespace composition, spread include() with an earlier same-file namespace. + +{/* probe: stylex-createThemeContract-spread after */} + +```tsx +import * as stylex from '@stylexjs/stylex' +export function example() { + const result = stylex.createThemeContract({ color: 'red' }) + return result +} +``` + +### createThemeContract(): computed key + +**API:** StyleX. **Message pattern:** + +```text +`stylex.createThemeContract()` cannot use `` at build time: its keys must be known +``` + +**Trigger:** + +{/* probe: stylex-createThemeContract-key before */} + +```tsx-error +import * as stylex from '@stylexjs/stylex'; +export function example(key) { + const result = stylex.createThemeContract({ [key]: 'red' }); + return result; +} +``` + +**Why:** The object-key reader accepts static identifiers and literal keys. A key computed from a runtime input has no statically extractable CSS property, namespace, condition or variable name. + +**Fix:** Use an explicit literal key (or a constant expression that the evaluator actually resolves). + +{/* probe: stylex-createThemeContract-key after */} + +```tsx +import * as stylex from '@stylexjs/stylex' +export function example() { + const result = stylex.createThemeContract({ color: 'red' }) + return result +} +``` + +### positionTry(): object spread + +**API:** StyleX. **Message pattern:** + +```text +`stylex.positionTry()` cannot use `` at build time: write every entry out, as its keys must be known +``` + +**Trigger:** + +{/* probe: stylex-positionTry-spread before */} + +```tsx-error +import * as stylex from '@stylexjs/stylex'; +export function example(extra) { + const result = stylex.positionTry({ ...extra }); + return result; +} +``` + +**Why:** This StyleX object layer does not accept unresolved object spreads: the build needs each key. The sole namespace-level exception is a recognized ...include(styles.base) reference to an earlier local create namespace. + +**Fix:** Write the entries explicitly; for namespace composition, spread include() with an earlier same-file namespace. + +{/* probe: stylex-positionTry-spread after */} + +```tsx +import * as stylex from '@stylexjs/stylex' +export function example() { + const result = stylex.positionTry({ color: 'red' }) + return result +} +``` + +### positionTry(): computed key + +**API:** StyleX. **Message pattern:** + +```text +`stylex.positionTry()` cannot use `` at build time: its keys must be known +``` + +**Trigger:** + +{/* probe: stylex-positionTry-key before */} + +```tsx-error +import * as stylex from '@stylexjs/stylex'; +export function example(key) { + const result = stylex.positionTry({ [key]: 'red' }); + return result; +} +``` + +**Why:** The object-key reader accepts static identifiers and literal keys. A key computed from a runtime input has no statically extractable CSS property, namespace, condition or variable name. + +**Fix:** Use an explicit literal key (or a constant expression that the evaluator actually resolves). + +{/* probe: stylex-positionTry-key after */} + +```tsx +import * as stylex from '@stylexjs/stylex' +export function example() { + const result = stylex.positionTry({ color: 'red' }) + return result +} +``` + +### viewTransitionClass(): object spread + +**API:** StyleX. **Message pattern:** + +```text +`stylex.viewTransitionClass()` cannot use `` at build time: write every entry out, as its keys must be known +``` + +**Trigger:** + +{/* probe: stylex-viewTransitionClass-spread before */} + +```tsx-error +import * as stylex from '@stylexjs/stylex'; +export function example(extra) { + const result = stylex.viewTransitionClass({ ...extra }); + return result; +} +``` + +**Why:** This StyleX object layer does not accept unresolved object spreads: the build needs each key. The sole namespace-level exception is a recognized ...include(styles.base) reference to an earlier local create namespace. + +**Fix:** Write the entries explicitly; for namespace composition, spread include() with an earlier same-file namespace. + +{/* probe: stylex-viewTransitionClass-spread after */} + +```tsx +import * as stylex from '@stylexjs/stylex' +export function example() { + const result = stylex.viewTransitionClass({ color: 'red' }) + return result +} +``` + +### viewTransitionClass(): computed key + +**API:** StyleX. **Message pattern:** + +```text +`stylex.viewTransitionClass()` cannot use `` at build time: its keys must be known +``` + +**Trigger:** + +{/* probe: stylex-viewTransitionClass-key before */} + +```tsx-error +import * as stylex from '@stylexjs/stylex'; +export function example(key) { + const result = stylex.viewTransitionClass({ [key]: 'red' }); + return result; +} +``` + +**Why:** The object-key reader accepts static identifiers and literal keys. A key computed from a runtime input has no statically extractable CSS property, namespace, condition or variable name. + +**Fix:** Use an explicit literal key (or a constant expression that the evaluator actually resolves). + +{/* probe: stylex-viewTransitionClass-key after */} + +```tsx +import * as stylex from '@stylexjs/stylex' +export function example() { + const result = stylex.viewTransitionClass({ color: 'red' }) + return result +} +``` + +### createTheme(): override spread + +**API:** StyleX. **Message pattern:** + +```text +`stylex.createTheme()` cannot use `` at build time: write every entry out, as its keys must be known +``` + +**Trigger:** + +{/* probe: stylex-createTheme-spread before */} + +```tsx-error +import * as stylex from '@stylexjs/stylex'; +const vars = stylex.defineVars({ color: 'red' }); +export function example(extra) { + const result = stylex.createTheme(vars, { ...extra }); + return result; +} +``` + +**Why:** This StyleX object layer does not accept unresolved object spreads: the build needs each key. The sole namespace-level exception is a recognized ...include(styles.base) reference to an earlier local create namespace. + +**Fix:** Write the entries explicitly; for namespace composition, spread include() with an earlier same-file namespace. + +{/* probe: stylex-createTheme-spread after */} + +```tsx +import * as stylex from '@stylexjs/stylex' +const vars = stylex.defineVars({ color: 'red' }) +export function example() { + const result = stylex.createTheme(vars, { color: 'blue' }) + return result +} +``` + +### createTheme(): computed override key + +**API:** StyleX. **Message pattern:** + +```text +`stylex.createTheme()` cannot use `` at build time: its keys must be known +``` + +**Trigger:** + +{/* probe: stylex-createTheme-key before */} + +```tsx-error +import * as stylex from '@stylexjs/stylex'; +const vars = stylex.defineVars({ color: 'red' }); +export function example(key) { + const result = stylex.createTheme(vars, { [key]: 'blue' }); + return result; +} +``` + +**Why:** The object-key reader accepts static identifiers and literal keys. A key computed from a runtime input has no statically extractable CSS property, namespace, condition or variable name. + +**Fix:** Use an explicit literal key (or a constant expression that the evaluator actually resolves). + +{/* probe: stylex-createTheme-key after */} + +```tsx +import * as stylex from '@stylexjs/stylex' +const vars = stylex.defineVars({ color: 'red' }) +export function example() { + const result = stylex.createTheme(vars, { color: 'blue' }) + return result +} +``` + +### include(): malformed bare reference + +**API:** StyleX. **Message pattern:** + +```text +`stylex.include()` cannot use `` at build time: it takes a namespace such as `styles.base` +``` + +**Trigger:** + +{/* probe: stylex-include-malformed-bare before */} + +```tsx-error +import * as stylex from '@stylexjs/stylex'; +const styles = stylex.create({ base: { color: 'red' } }); +export function example() { + const result = stylex.create({ derived: { ...stylex.include(styles) } }); + return result; +} +``` + +**Why:** A recognized include spread reads one static member on an identifier, such as styles.base. A bare identifier, nested member path, computed member or missing argument does not have that shape. + +**Fix:** Declare a local create() map and spread stylex.include(styles.base) inside a static namespace. + +{/* probe: stylex-include-malformed-bare after */} + +```tsx +import * as stylex from '@stylexjs/stylex' +const styles = stylex.create({ base: { color: 'red' } }) +export function example() { + const result = stylex.create({ derived: { ...stylex.include(styles.base) } }) + return result +} +``` + +### include(): same-call + +**API:** StyleX. **Message pattern:** + +```text +`stylex.include()` cannot use `` at build time: it takes a namespace `stylex.create()` defines earlier in this file +``` + +**Trigger:** + +{/* probe: stylex-include-same-call before */} + +```tsx-error +import * as stylex from '@stylexjs/stylex'; +export const styles = stylex.create({ base: { color: 'red' }, derived: { ...stylex.include(styles.base) } }); +``` + +**Why:** Having the syntax styles.base is insufficient: the visible binding and member must have been registered by an earlier create() in this file. Unknown, forward, imported or missing namespace references cannot be composed here. + +**Fix:** Define the namespace earlier in this file and reference an existing member from that binding. + +{/* probe: stylex-include-same-call after */} + +```tsx +import * as stylex from '@stylexjs/stylex' +const styles = stylex.create({ base: { color: 'red' } }) +export function example() { + const result = stylex.create({ derived: { ...stylex.include(styles.base) } }) + return result +} +``` + +### defineVars(): runtime variable value + +**API:** StyleX. **Message pattern:** + +```text +`stylex.defineVars()` cannot use `` at build time: its values must be literals, theme tokens or constants, or be computed from them +``` + +**Trigger:** + +{/* probe: stylex-defineVars-value-runtime before */} + +```tsx-error +import * as stylex from '@stylexjs/stylex'; +export function example(value) { + const result = stylex.defineVars({ color: value }); + return result; +} +``` + +**Why:** defineVars/createTheme variable values are literal leaves or nested default/@media/@supports/@container condition objects, optionally wrapped by types.\*(). Null contributes no assignment. Runtime leaves, arrays and pseudo conditions are not variable values. + +**Fix:** Use a literal variable value, or a supported default/at-rule condition object with static leaves. + +{/* probe: stylex-defineVars-value-runtime after */} + +```tsx +import * as stylex from '@stylexjs/stylex' +export function example() { + const result = stylex.defineVars({ color: 'red' }) + return result +} +``` + +### createTheme(): runtime variable value + +**API:** StyleX. **Message pattern:** + +```text +`stylex.createTheme()` cannot use `` at build time: its values must be literals, theme tokens or constants, or be computed from them +``` + +**Trigger:** + +{/* probe: stylex-createTheme-value-runtime before */} + +```tsx-error +import * as stylex from '@stylexjs/stylex'; +const vars = stylex.defineVars({ color: 'red' }); +export function example(value) { + const result = stylex.createTheme(vars, { color: value }); + return result; +} +``` + +**Why:** defineVars/createTheme variable values are literal leaves or nested default/@media/@supports/@container condition objects, optionally wrapped by types.\*(). Null contributes no assignment. Runtime leaves, arrays and pseudo conditions are not variable values. + +**Fix:** Use a literal variable value, or a supported default/at-rule condition object with static leaves. + +{/* probe: stylex-createTheme-value-runtime after */} + +```tsx +import * as stylex from '@stylexjs/stylex' +const vars = stylex.defineVars({ color: 'red' }) +export function example() { + const result = stylex.createTheme(vars, { color: 'blue' }) + return result +} +``` + +### Override only keys present in the theme contract + +**API:** StyleX. **Message pattern:** + +```text +`stylex.createTheme()` cannot use `` at build time: `vars` has no such variable +``` + +**Trigger:** + +{/* probe: stylex-createTheme-unknown-key before */} + +```tsx-error +import * as stylex from '@stylexjs/stylex'; +const vars = stylex.defineVars({ color: 'red' }); +export function example() { + const result = stylex.createTheme(vars, { missing: 'blue' }); + return result; +} +``` + +**Why:** createTheme maps override keys to custom properties registered by its contract. A key absent from that contract has no CSS variable to assign. + +**Fix:** Use an existing contract key or add the intended key to the contract declaration first. + +{/* probe: stylex-createTheme-unknown-key after */} + +```tsx +import * as stylex from '@stylexjs/stylex' +const vars = stylex.defineVars({ color: 'red' }) +export function example() { + const result = stylex.createTheme(vars, { color: 'blue' }) + return result +} +``` + +### defineConsts(): null value is not a literal + +**API:** StyleX. **Message pattern:** + +```text +`stylex.defineConsts()` cannot use `` at build time: its values must be literals, theme tokens or constants, or be computed from them +``` + +**Trigger:** + +{/* probe: stylex-defineConsts-value-null before */} + +```tsx-error +import * as stylex from '@stylexjs/stylex'; +export function example() { + const result = stylex.defineConsts({ color: null }); + return result; +} +``` + +**Why:** defineConsts uses the literal reader directly: it neither reads condition objects nor unwraps StyleX helper calls, and null is not a literal value that this reader publishes. + +**Fix:** Replace the value with a literal such as red. + +{/* probe: stylex-defineConsts-value-null after */} + +```tsx +import * as stylex from '@stylexjs/stylex' +export function example() { + const result = stylex.defineConsts({ color: 'red' }) + return result +} +``` + +### positionTry(): null declaration value + +**API:** StyleX. **Message pattern:** + +```text +`stylex.positionTry()` cannot use `` at build time: its values must be literals, theme tokens or constants, or be computed from them +``` + +**Trigger:** + +{/* probe: stylex-positionTry-value-null before */} + +```tsx-error +import * as stylex from '@stylexjs/stylex'; +export function example() { + const result = stylex.positionTry({ color: null }); + return result; +} +``` + +**Why:** This named declaration-block API accepts literal CSS values, not runtime leaves, null, arrays or value-level condition objects. Its declaration reader normalizes property names and numeric units without dynamic variables. + +**Fix:** Supply a static literal CSS value for the declaration. + +{/* probe: stylex-positionTry-value-null after */} + +```tsx +import * as stylex from '@stylexjs/stylex' +export function example() { + const result = stylex.positionTry({ color: 'red' }) + return result +} +``` + +### positionTry(): types is outside its enclosing API context + +**API:** StyleX. **Message pattern:** + +```text +`stylex.positionTry()` cannot use `` at build time: its values must be literals; `firstThatWorks()`, `include()` and `types` are read only inside `stylex.create()` and `stylex.defineVars()` +``` + +**Trigger:** + +{/* probe: stylex-positionTry-helper-types before */} + +```tsx-error +import * as stylex from '@stylexjs/stylex'; +export function example() { + const result = stylex.positionTry({ color: stylex.types.color('red') }); + return result; +} +``` + +**Why:** The declaration reader recognizes genuine StyleX helper calls but does not read them here. Its dedicated error explains that values are literals and helpers are read only by create()/defineVars(); each helper still has its own supported subcontext there. + +**Fix:** Replace the helper with a literal here. Use firstThatWorks in a static create property, include as a static namespace spread, and types as a supported value wrapper. + +{/* probe: stylex-positionTry-helper-types after */} + +```tsx +import * as stylex from '@stylexjs/stylex' +export function example() { + const result = stylex.positionTry({ color: 'red' }) + return result +} +``` + +### viewTransitionClass(): null declaration value + +**API:** StyleX. **Message pattern:** + +```text +`stylex.viewTransitionClass()` cannot use `` at build time: its values must be literals, theme tokens or constants, or be computed from them +``` + +**Trigger:** + +{/* probe: stylex-viewTransitionClass-value-null before */} + +```tsx-error +import * as stylex from '@stylexjs/stylex'; +export function example() { + const result = stylex.viewTransitionClass({ color: null }); + return result; +} +``` + +**Why:** This named declaration-block API accepts literal CSS values, not runtime leaves, null, arrays or value-level condition objects. Its declaration reader normalizes property names and numeric units without dynamic variables. + +**Fix:** Supply a static literal CSS value for the declaration. + +{/* probe: stylex-viewTransitionClass-value-null after */} + +```tsx +import * as stylex from '@stylexjs/stylex' +export function example() { + const result = stylex.viewTransitionClass({ color: 'red' }) + return result +} +``` + +### viewTransitionClass(): types is outside its enclosing API context + +**API:** StyleX. **Message pattern:** + +```text +`stylex.viewTransitionClass()` cannot use `` at build time: its values must be literals; `firstThatWorks()`, `include()` and `types` are read only inside `stylex.create()` and `stylex.defineVars()` +``` + +**Trigger:** + +{/* probe: stylex-viewTransitionClass-helper-types before */} + +```tsx-error +import * as stylex from '@stylexjs/stylex'; +export function example() { + const result = stylex.viewTransitionClass({ color: stylex.types.color('red') }); + return result; +} +``` + +**Why:** The declaration reader recognizes genuine StyleX helper calls but does not read them here. Its dedicated error explains that values are literals and helpers are read only by create()/defineVars(); each helper still has its own supported subcontext there. + +**Fix:** Replace the helper with a literal here. Use firstThatWorks in a static create property, include as a static namespace spread, and types as a supported value wrapper. + +{/* probe: stylex-viewTransitionClass-helper-types after */} + +```tsx +import * as stylex from '@stylexjs/stylex' +export function example() { + const result = stylex.viewTransitionClass({ color: 'red' }) + return result +} +``` + +### firstThatWorks(): null fallback value + +**API:** StyleX. **Message pattern:** + +```text +`stylex.firstThatWorks()` cannot use `` at build time: its values must be literals, theme tokens or constants, or be computed from them +``` + +**Trigger:** + +{/* probe: stylex-firstThatWorks-value-null before */} + +```tsx-error +import * as stylex from '@stylexjs/stylex'; +export function example() { + const result = stylex.create({ base: { color: stylex.firstThatWorks('red', null) } }); + return result; +} +``` + +**Why:** firstThatWorks is read inside a static create() property and each fallback argument must independently resolve to a static leaf. A spread, null or condition object is not one fallback leaf. + +**Fix:** Pass individual static fallback values; retain condition objects outside the fallback helper if needed. + +{/* probe: stylex-firstThatWorks-value-null after */} + +```tsx +import * as stylex from '@stylexjs/stylex' +export function example() { + const result = stylex.create({ + base: { color: stylex.firstThatWorks('red', 'blue') }, + }) + return result +} +``` + +### keyframes(): frame-spread + +**API:** StyleX. **Message pattern:** + +```text +`stylex.keyframes()` cannot use `` at build time: its values must be literals, theme tokens or constants, or be computed from them +``` + +**Trigger:** + +{/* probe: stylex-keyframes-frame-spread before */} + +```tsx-error +import * as stylex from '@stylexjs/stylex'; +export function example(extra) { + const result = stylex.keyframes({ ...extra }); + return result; +} +``` + +**Why:** Keyframes have no element on which to place a runtime value or conditional class. Frame keys and declarations must resolve to a fixed set of static CSS declarations; unreadable keys/spreads and runtime branches are reported through the keyframes value requirement. + +**Fix:** Write explicit frames and CSS declarations with literal/foldable values. Move runtime selection outside the keyframes definition. + +{/* probe: stylex-keyframes-frame-spread after */} + +```tsx +import * as stylex from '@stylexjs/stylex' +export function example() { + const result = stylex.keyframes({ from: { opacity: 0 }, to: { opacity: 1 } }) + return result +} +``` + +## Plugins and config + +These operational setup messages are distinct from style-syntax diagnostics. The build plugins forward extractor diagnostics; native filesystem or transport failures depend on the platform and underlying error. + +### Next coordinator and loader failures + +The Next plugin manages its coordinator and port-file options. The following internal setup/HTTP fixtures describe the failure contract, not options to hand-author in an application. Restart the Next build with a matching plugin/coordinator configuration and inspect its originating error when the transport fails. + +#### Coordinator port file not found + +**Message pattern:** + +```text +Coordinator port file not found +``` + +**Trigger (internal protocol fixture):** + +```json +{ + "loader": "loader", + "portFileExists": false +} +``` + +**Why:** Real port file is absent for all 20 loader retries (50ms each); loader reports the authored callback error. Both loaders share this error family. + +**Fix:** Create the configured port file containing the listening coordinator port before extraction. + +```json +{ + "loader": "loader", + "portFileExists": true, + "httpResponse": { + "status": 200, + "body": "{\"code\":\"export const answer = 42;\",\"dependencies\":[]}" + } +} +``` + +#### Coordinator error + +**Message pattern:** + +```text +Coordinator error +``` + +**Trigger (internal protocol fixture):** + +```json +{ + "loader": "loader", + "portFileExists": true, + "httpResponse": { + "status": 500, + "body": "{}" + } +} +``` + +**Why:** HTTP status is non-200 and parsed JSON has no string error field; loader uses its authored fallback. + +**Fix:** Have POST /extract return HTTP 200 with a string code field and dependencies array. + +```json +{ + "loader": "loader", + "portFileExists": true, + "httpResponse": { + "status": 200, + "body": "{\"code\":\"export const answer = 42;\",\"dependencies\":[]}" + } +} +``` + +#### Coordinator response missing code + +**Message pattern:** + +```text +Coordinator response missing code +``` + +**Trigger (internal protocol fixture):** + +```json +{ + "loader": "loader", + "portFileExists": true, + "httpResponse": { + "status": 200, + "body": "{\"dependencies\":[]}" + } +} +``` + +**Why:** HTTP 200 parsed JSON has no string code field; loader rejects the response contract. + +**Fix:** Include a string code field in the successful POST /extract JSON response. + +```json +{ + "loader": "loader", + "portFileExists": true, + "httpResponse": { + "status": 200, + "body": "{\"code\":\"export const answer = 42;\",\"dependencies\":[]}" + } +} +``` + +#### Coordinator CSS error: 503 + +**Message pattern:** + +```text +Coordinator CSS error: +``` + +**Trigger (internal protocol fixture):** + +```json +{ + "loader": "css-loader", + "portFileExists": true, + "httpResponse": { + "status": 503, + "body": "unavailable" + } +} +``` + +**Why:** CSS loader rejects any non-200 status and inserts the actual numeric HTTP status into its authored error. + +**Fix:** Have GET /css return HTTP 200 and the CSS text, not a failure status. + +```json +{ + "loader": "css-loader", + "portFileExists": true, + "httpResponse": { + "status": 200, + "body": ".a{color:red}" + } +} +``` + +### A build plugin is missing + +`Cannot run on the runtime` is a placeholder exception, not an extractor build diagnostic. It means a compile-time component or helper executed without being transformed. Configure the appropriate Devup UI build plugin and ensure the file is included in extraction. Do not fix it by adding a styling runtime. + +### Catch removed runtime reads with ESLint + +The `devup/no-runtime-read` rule reports compile-time bindings read outside supported calls/renders before the build. The lint message has a final period; the extractor message does not. The same direct call is the repair in both cases. + +```text +`` is read at runtime, where it does not exist: the build compiles it only where it is called or rendered. +``` + +```tsx +import { css } from '@devup-ui/react' +consume(css) +``` + +```tsx +import { css } from '@devup-ui/react' +const card = css({ color: 'red' }) +``` + +Other ESLint diagnostics, alias-retention warnings, and StyleX shorthand warnings are not extractor build errors. Tailwind classes the build does not recognise are preserved; they do not produce a new finite build-error family. + +## Verification sources + +The examples were checked against these source snapshots. The reference uses separately built branch binaries, not a claim that every open implementation PR has already merged or that their combined final binary was tested. + +- `origin/main` at `a935315c` +- `origin/fix/selectors` at `d94ef3c6` +- `origin/fix/compat-imports` at `4a6ee3a2` +- `origin/fix/styled-props` at `e2cfa67b` +- `origin/fix/theme-reads` at `53bdbc20` +- `origin/fix/emotion-css` at `9b2a04a3` +- `origin/fix/emotion-fallback-arrays` at `bb86b80d` +- `origin/fix/vanilla-extract-companions` at `7866e4f3` +- `origin/fix/nested-css-results` at `001a9816` +- `origin/fix/emotion-component-selectors` at `d44950f7` +- `origin/fix/imported-styled-definitions` at `7df41c06` +- `origin/fix/barrel-and-namespace-imports` at `10ec6113` +- `origin/fix/imports-follow-aliases` at `4830601d` +- `origin/fix/emotion-rule-numbers-and-mixins` at `050c6862` +- `origin/fix/local-style-objects` at `734fd16f` +- `origin/fix/scoped-compiled-names` at `29812f37` +- `origin/fix/tailwind-classes` at `c4a62b68` +- `origin/feat/tailwind-v4-complete` at `8104fdec` +- `origin/fix/types-match-build` at `d0028a89` +- `origin/fix/eslint-alias-rules` at `32c30074` diff --git a/apps/landing/src/app/(detail)/docs/layout.tsx b/apps/landing/src/app/(detail)/docs/layout.tsx index 20c5945db..be86bb3e4 100644 --- a/apps/landing/src/app/(detail)/docs/layout.tsx +++ b/apps/landing/src/app/(detail)/docs/layout.tsx @@ -18,6 +18,7 @@ export default function DetailLayout({ { + // Read the exported SSR HTML without vinext's client router, at a width + // where the docs sidebar is shown. + test.use({ + javaScriptEnabled: false, + viewport: { width: 1440, height: 900 }, + }) + + test('serves the page heading and canonical URL', async ({ page }) => { + const response = await page.goto('/docs/build-errors') + + expect(response?.status()).toBe(200) + await expect(page.locator('.markdown-body h1')).toHaveText('Build Errors') + await expect(page.locator('link[rel="canonical"]')).toHaveAttribute( + 'href', + /^(?:https:\/\/devup-ui\.com)?\/docs\/build-errors$/, + ) + }) + + test('has a section for each API, in order', async ({ page }) => { + await page.goto('/docs/build-errors') + + const headings = await page.locator('.markdown-body h2').allTextContents() + + expect( + headings.filter((heading) => API_SECTIONS.includes(heading)), + `h2 headings: ${JSON.stringify(headings)}`, + ).toEqual(API_SECTIONS) + }) + + test('docs sidebar links the page right after the limitations page', async ({ + page, + }) => { + await page.goto('/docs/build-errors') + + const sidebarLink = page.locator( + 'a[href="/docs/limitations"] + a[href="/docs/build-errors"]', + ) + await expect(sidebarLink).toBeVisible() + await expect(sidebarLink).toHaveText('Build Errors') + }) + + test('limitations Build errors section links the page', async ({ page }) => { + await page.goto('/docs/limitations') + + await expect( + page.locator('h2#build-errors ~ p a[href="/docs/build-errors"]'), + ).toHaveText('Build Errors') + }) +}) diff --git a/e2e/exported-routes.ts b/e2e/exported-routes.ts index e3e6cbff1..31993a757 100644 --- a/e2e/exported-routes.ts +++ b/e2e/exported-routes.ts @@ -63,6 +63,7 @@ export const EXPECTED_EXPORTED_ROUTES = [ '/docs/api/style-props', '/docs/api/text', '/docs/api/v-stack', + '/docs/build-errors', '/docs/core-concepts/nm-base', '/docs/core-concepts/no-dependencies', '/docs/core-concepts/optimize-css', From bb9db7416d114492395fc2384524ecab773330c2 Mon Sep 17 00:00:00 2001 From: owjs3901 Date: Sun, 4 Oct 2026 18:13:38 +0900 Subject: [PATCH 05/13] docs: list the build-error reference in the docs menu Refs #693 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- apps/landing/src/app/(detail)/docs/LeftMenu.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/landing/src/app/(detail)/docs/LeftMenu.tsx b/apps/landing/src/app/(detail)/docs/LeftMenu.tsx index 344434066..240e614f7 100644 --- a/apps/landing/src/app/(detail)/docs/LeftMenu.tsx +++ b/apps/landing/src/app/(detail)/docs/LeftMenu.tsx @@ -37,6 +37,7 @@ export function LeftMenu() { Features Supported Syntax & Limitations + Build Errors Date: Sun, 4 Oct 2026 18:21:54 +0900 Subject: [PATCH 06/13] docs: link the build-error reference from the limitations page Refs #693 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- apps/landing/src/app/(detail)/docs/limitations/page.mdx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/landing/src/app/(detail)/docs/limitations/page.mdx b/apps/landing/src/app/(detail)/docs/limitations/page.mdx index 24939bd4a..c92635565 100644 --- a/apps/landing/src/app/(detail)/docs/limitations/page.mdx +++ b/apps/landing/src/app/(detail)/docs/limitations/page.mdx @@ -104,3 +104,5 @@ src/Card.tsx:3:36: `css` on `
` cannot use `getStyles()` at build time: it m ``` A file with several problems reports them all at once, in source order. + +See [Build Errors](/docs/build-errors) for the build errors, grouped by API. From 2b6c03ec332c08f00d57e0e1ee5b820a82cb759a Mon Sep 17 00:00:00 2001 From: owjs3901 Date: Sun, 4 Oct 2026 18:31:30 +0900 Subject: [PATCH 07/13] docs: link the build-error reference from package READMEs Refs #693 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- .changepacks/changepack_log_build_error_docs.json | 8 ++++++++ .../src/rules/css-utils-literal-only/README.md | 2 +- packages/react/README.md | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 .changepacks/changepack_log_build_error_docs.json diff --git a/.changepacks/changepack_log_build_error_docs.json b/.changepacks/changepack_log_build_error_docs.json new file mode 100644 index 000000000..9016dbe60 --- /dev/null +++ b/.changepacks/changepack_log_build_error_docs.json @@ -0,0 +1,8 @@ +{ + "changes": { + "packages/eslint-plugin/package.json": "Patch", + "packages/react/package.json": "Patch" + }, + "note": "The @devup-ui/react README and the css-utils-literal-only rule docs link their mention of build errors to the new Build Errors reference (https://devup-ui.com/docs/build-errors), which lists the build errors grouped by API; the docs sidebar lists the page after Supported Syntax & Limitations, whose Build errors section links to it. Documentation only: no new build errors", + "date": "2026-10-01T00:00:00.000Z" +} diff --git a/packages/eslint-plugin/src/rules/css-utils-literal-only/README.md b/packages/eslint-plugin/src/rules/css-utils-literal-only/README.md index c9416e32c..ba8f84b5c 100644 --- a/packages/eslint-plugin/src/rules/css-utils-literal-only/README.md +++ b/packages/eslint-plugin/src/rules/css-utils-literal-only/README.md @@ -4,7 +4,7 @@ Enforce that CSS utility functions only use values known at build time in devup- ## Rule Details -This rule ensures that CSS utility functions (`css`, `globalCss`, `keyframes`, `createGlobalStyle`) from devup-ui, and the StyleX functions devup-ui compiles (`create`, `keyframes`, `defineVars`, `defineConsts`, `createTheme`, `createThemeContract`, `positionTry`, `viewTransitionClass`), only receive values the build knows. They have no element to set a CSS variable on, so a value known only at runtime is a build error. +This rule ensures that CSS utility functions (`css`, `globalCss`, `keyframes`, `createGlobalStyle`) from devup-ui, and the StyleX functions devup-ui compiles (`create`, `keyframes`, `defineVars`, `defineConsts`, `createTheme`, `createThemeContract`, `positionTry`, `viewTransitionClass`), only receive values the build knows. They have no element to set a CSS variable on, so a value known only at runtime is a [build error](https://devup-ui.com/docs/build-errors). It checks the values of every rule object they take, in any argument, and the interpolations of CSS text, written as a template argument or a tagged template (`` css`color: ${color};` ``). A part `css()` composes as a class (`css(base, { m: 1 })`), and a condition choosing between parts, are read at runtime and not checked. `styled()` sets a CSS variable on the element it renders, so its values are not checked either. diff --git a/packages/react/README.md b/packages/react/README.md index fec687dd8..9a7fbf7fa 100644 --- a/packages/react/README.md +++ b/packages/react/README.md @@ -103,7 +103,7 @@ The Turbopack ranges overlap, so the direct-API result is effectively parity wit Devup UI is a CSS in JS preprocessor that does not require runtime. Devup UI eliminates the performance degradation of the browser through the CSS in JS preprocessor. -What the build cannot know is kept as a CSS variable or reported as a located build error; see [supported syntax & limitations](https://devup-ui.com/docs/limitations). +What the build cannot know is kept as a CSS variable or reported as a [located build error](https://devup-ui.com/docs/build-errors); see [supported syntax & limitations](https://devup-ui.com/docs/limitations). ```tsx const before = From 575884aea22ab445b388cf7e0e14c8caa3934bdc Mon Sep 17 00:00:00 2001 From: owjs3901 Date: Mon, 5 Oct 2026 10:17:12 +0900 Subject: [PATCH 08/13] docs: correct Button and optimization output examples Refs #693 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- .../changepack_log_docs_accuracy.json | 7 + .../src/app/(detail)/docs/api/button/page.mdx | 26 +- .../docs/core-concepts/optimize-css/page.mdx | 264 ++++++++---------- 3 files changed, 138 insertions(+), 159 deletions(-) create mode 100644 .changepacks/changepack_log_docs_accuracy.json diff --git a/.changepacks/changepack_log_docs_accuracy.json b/.changepacks/changepack_log_docs_accuracy.json new file mode 100644 index 000000000..47222273b --- /dev/null +++ b/.changepacks/changepack_log_docs_accuracy.json @@ -0,0 +1,7 @@ +{ + "changes": { + "apps/landing/package.json": "Patch" + }, + "note": "Documentation examples now preserve Button event handlers and show the CSS actually emitted for values, colors, keyframes, fonts and atomic rule reuse. Documentation only: no new build errors.", + "date": "2026-10-01T00:00:00.000Z" +} diff --git a/apps/landing/src/app/(detail)/docs/api/button/page.mdx b/apps/landing/src/app/(detail)/docs/api/button/page.mdx index a79e01c5f..1eb82336b 100644 --- a/apps/landing/src/app/(detail)/docs/api/button/page.mdx +++ b/apps/landing/src/app/(detail)/docs/api/button/page.mdx @@ -24,28 +24,22 @@ function App() { } ``` -The Button component defined above will render like this: +With the build plugin configured, the Button component is transformed into a native +`button`. The React event handler remains a function expression, not a string +attribute. This is the extracted code and CSS from an isolated run with an empty +theme and the default prefix; generated names depend on extraction state and prefix. -```tsx +```text +import "@devup-ui/react/devup-ui.css"; function App() { - return ( - - ) + ; } ``` -```css -.a { - background: red; -} -.b { - width: 100px; -} -.c { - height: 100px; -} +```text +/*! devup-ui v1.0.82, | Apache License 2.0 | https://devup-ui.com */.a{background:red}.b{height:100px}.c{width:100px} ``` If you pass a number without a unit to a style property, it will be automatically scaled by 4. diff --git a/apps/landing/src/app/(detail)/docs/core-concepts/optimize-css/page.mdx b/apps/landing/src/app/(detail)/docs/core-concepts/optimize-css/page.mdx index 736e94c3f..ba438b31d 100644 --- a/apps/landing/src/app/(detail)/docs/core-concepts/optimize-css/page.mdx +++ b/apps/landing/src/app/(detail)/docs/core-concepts/optimize-css/page.mdx @@ -7,199 +7,177 @@ export const metadata = { # Optimize CSS -Devup UI automatically optimizes CSS output through micro-unit optimization, duplicate removal, and intelligent value conversion to minimize bundle size and improve performance. This document explains the various CSS optimization techniques used by Devup UI, including micro-unit optimization, color optimization, and global CSS handling. +Devup UI extracts styles at build time, normalizes supported values, and reuses +identical atomic rules. It is not a general-purpose CSS minifier: do not assume +that every decimal or shorthand will be reduced. -## Micro-unit Optimization +Each input below is a standalone source file. With the build plugin configured, +the shown CSS was emitted by an isolated extraction with an empty theme (`{}`) +and the default prefix. Generated class, keyframe, and layer names depend on +extraction state and prefix; they are not stable identifiers to reference manually. -### **Value Optimization** - -Devup UI optimizes CSS values by: - -- **Removing unnecessary zeros**: `0px` becomes `0` -- **Shortening decimals**: `0.5000` becomes `0.5` -- **Optimizing units**: `0px` becomes `0` when appropriate -- **Converting to alternative units**: `0px` is converted to `0%` in CSS functions where unit-less zero is not allowed -- **Removing redundant spaces**: `margin: 0 0 0 0` becomes `margin:0` - -``` -// Before optimization - - -// After optimization - -``` - -### **Aspect Ratio Optimization** - -Aspect ratios are simplified to their smallest equivalent ratio using the greatest common divisor (GCD). -For example, 500/100 becomes 5/1, while 16/9 remains unchanged since GCD(16, 9) = 1. +## Value and Unit Optimization ```tsx -// Before: aspect-ratio: 500/100 - - -// After: aspect-ratio: 5/1 (optimized) -// The system calculates GCD(500, 100) = 100, simplifying the ratio to 5/1 +import { Box } from '@devup-ui/react' + +const values = +const ratios = ( + <> + + + +) +const units = ( + <> + + + + +) ``` -## Color Optimization - -### **RGB/RGBA to Hex Conversion** - -All RGB and RGBA color values are normalized and converted into compact hexadecimal form. +Generated CSS: +```text +/*! devup-ui v1.0.82, | Apache License 2.0 | https://devup-ui.com */.e{aspect-ratio:16/9}.d{aspect-ratio:5/1}.a{margin:0 0 0 0}.b{padding:16.0000px}.c{width:0}.h{width:100%}.g{width:16rem}.f{width:64px} ``` -// Input - - -// Output - // #ff0000 shortened to #f00 - // #ff000080 shortened to #f000 -``` +- Zero lengths lose their units here, but the four margin values remain + `0 0 0 0`; they are not collapsed into one value. +- The explicit decimal string `16.0000px` is preserved, not shortened to `16px`. +- The integer aspect ratio `500/100` reduces to `5/1`, while `16/9` is unchanged. +- Numeric width uses Devup UI's scale: `16` becomes `64px`. Explicit `rem` and + percentage units are preserved. Unitless properties such as `opacity` and + `aspectRatio` do not use this four-times scale. -### **Hex Shortening** +## Color Optimization -Hex shortening converts long hex codes into their shortest possible form. -Six- or eight-digit values are reduced to three or four digits when safely equivalent. +The following RGB, RGBA, and hex inputs show color normalization, hex shortening, +and opaque-alpha removal. These observations do not promise conversion of every +CSS color syntax. ```tsx -// 6-digit hex to 3-digit -// #ffffff → #fff -// #000000 → #000 -// #ff0000 → #f00 - -// 8-digit hex to 4-digit (when alpha is FF) -// #ffffffff → #ffff -// #000000ff → #000f +import { Box } from '@devup-ui/react' + +const colors = ( + <> + + + + + + + + + + +) ``` -### **Alpha Channel Optimization** - -Alpha channel optimization removes unnecessary opacity information. -Fully opaque colors (FF) are shortened, while partial transparency values are preserved for accuracy. +Generated CSS: -```tsx -// Full opacity is removed and shortened to 3-digit hex -// #ff0000ff → #f00 - -// Partial opacity is preserved and shortened to 4-digit hex -// #ff000080 → #f000 +```text +/*! devup-ui v1.0.82, | Apache License 2.0 | https://devup-ui.com */.d{background:#000}.a{background:#F00}.b{background:#FF000080}.c{background:#FFF} ``` -## Global CSS Optimization +RGB red, six-digit red, and fully opaque eight-digit red share `#F00`. White and +black likewise shorten to `#FFF` and `#000`, including their opaque-alpha forms. +The RGBA input with alpha `0.5` emits `#FF000080`, as does the equivalent hex +input in this fixture. Its alpha byte `80` cannot be shortened to a repeated +single hex digit. In particular, `#f000` would mean fully transparent red, not +half-transparent red. -### **CSS Block Optimization** +## Global CSS Extraction -Global CSS blocks are optimized by: +Use `globalCss()` to extract global rules. This example emits a compact rule +inside a generated cascade layer; it does not demonstrate minification of an +arbitrary CSS text block. -- **Minifying whitespace**: Multiple spaces become single spaces -- **Removing semicolons**: Last property in a block doesn't need semicolon +```tsx +import { globalCss } from '@devup-ui/react' -```css -/* Before optimization */ -div { - /* comment */ - background-color: red; - /* color: blue; */ -} +globalCss({ div: { backgroundColor: 'red' } }) +``` -/* After optimization */ -div { - background-color: red; -} +Generated CSS: + +```text +/*! devup-ui v1.0.82, | Apache License 2.0 | https://devup-ui.com */@layer b;@layer b{div{background-color:red}} ``` -### **Keyframes Optimization** +## Keyframes Extraction -Keyframes are optimized for minimal output: +`keyframes()` returns the generated animation name. It is not derived from the +JavaScript variable name `fadeIn`. ```tsx -// Input +import { Box, keyframes } from '@devup-ui/react' + const fadeIn = keyframes({ from: { opacity: 0 }, to: { opacity: 1 }, }) - -// Output (optimized) -// @keyframes k-fadeIn{from{opacity:0}to{opacity:1}} +const animated = ``` -### **Font Face Optimization** +Generated CSS: -Font faces are automatically added and optimized: - -```tsx -// Input - - -// Output (optimized) -// @font-face{font-family:Inter;src:url(./fonts/Inter.woff2)} +```text +/*! devup-ui v1.0.82, | Apache License 2.0 | https://devup-ui.com */@keyframes a{from{opacity:0}to{opacity:1}}.b{animation:a 1s linear} ``` -## Value Conversion - -### **Unit Conversion** +## Font Family Extraction -Values are converted to their most appropriate units: -Numeric values are automatically interpreted as pixel units, while string-based values with explicit units (e.g., rem, %) are preserved for accurate rendering. +Setting `fontFamily` emits a font-family declaration. It does not discover a +font file or automatically create an `@font-face` rule. Load the font separately. -``` -// Numbers without units are treated as pixels (multiplied by 4) - // width: 64px +```tsx +import { Box } from '@devup-ui/react' -// Strings with units are preserved - // width: 16rem - // width: 100% +const text = ``` -### **Zero Value Optimization** - -Zero values are optimized: - -``` -// Before - +Generated CSS: -// After - +```text +/*! devup-ui v1.0.82, | Apache License 2.0 | https://devup-ui.com */.a{font-family:Inter} ``` -## Duplicate Removal +## Style Deduplication -### **Style Deduplication** - -Identical style rules across multiple components are automatically merged into a single CSS rule, reducing redundancy and overall bundle size. +Repeated identical declarations reuse atomic classes. Background and text color +remain separate rules, not a fused `.red-white` rule. ```tsx -// Multiple components with same styles -
- - - -
- -// Output: Only one CSS rule generated -// .red-white{background:red;color:white} +import { Box } from '@devup-ui/react' + +const repeated = ( +
+ + + +
+) ``` -### **Advantages** +Extracted code: + +```text +import "@devup-ui/react/devup-ui.css"; +const repeated =
+
+
+
+
; +``` -Devup UI offers complete flexibility in how you implement and optimize styles — while maintaining measurable performance gains. +Generated CSS: -- **Flexible syntax support** – Use CSS utility objects, template literals, or prop-based expressions seamlessly in one system -- **Smaller bundle size** – Reduced CSS output through color, value, and duplicate optimization -- **Faster parsing** – Lightweight CSS loads and parses quickly in browsers -- **Better caching** – Compact, consistent values improve cache efficiency -- **Lower memory usage** – Reduced CSS footprint minimizes runtime memory consumption +```text +/*! devup-ui v1.0.82, | Apache License 2.0 | https://devup-ui.com */.a{background:red}.b{color:white} +``` -This unified approach ensures both **maximum developer freedom** and **optimized performance**, -allowing teams to write CSS their way without compromising efficiency. +These three elements share two rules. Reuse reduces repeated CSS without adding +JavaScript for styling at runtime; the examples above describe observed output, +not a guarantee of the shortest possible representation for every value. From 2a1548f67e066ab7d3c8365899a468f885dc3212 Mon Sep 17 00:00:00 2001 From: owjs3901 Date: Mon, 5 Oct 2026 10:30:42 +0900 Subject: [PATCH 09/13] docs: update legacy theme schema and declaration paths Refs #693 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- .../devup-json/page.mdx | 42 +++++++++++++++++-- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/apps/landing/src/app/(detail)/docs/figma-and-theme-integration/devup-json/page.mdx b/apps/landing/src/app/(detail)/docs/figma-and-theme-integration/devup-json/page.mdx index 92038e9fa..5bf10cbc0 100644 --- a/apps/landing/src/app/(detail)/docs/figma-and-theme-integration/devup-json/page.mdx +++ b/apps/landing/src/app/(detail)/docs/figma-and-theme-integration/devup-json/page.mdx @@ -15,7 +15,9 @@ The `devup.json` file is the central configuration file for Devup UI that allows { "theme": { "colors": { - "primary": "#5A44FF" + "default": { + "primary": "#5A44FF" + } }, "typography": { "h1": { @@ -28,6 +30,10 @@ The `devup.json` file is the central configuration file for Devup UI that allows ## Colors Configuration +`theme.colors` maps variant names (`default`, `light`, `dark`, or your own names) +to token maps. A flat `colors.primary` value is not the theme schema; place it +under a variant such as `colors.default.primary` instead. + ### **Basic Color Setup** Define colors for different themes. @@ -169,12 +175,41 @@ Use arrays to define styles for each breakpoint. Access colors using the `$` prefix. ```tsx - + Content ``` -- To enable type autocompletion for theme values, add `"df/*.ts"` to the `include` array in your `tsconfig.json` file. +Use only tokens defined in your selected variant. The basic example defines +`$primary`; add `text` to that variant before using `$text`. + +### Generated Theme Types + +The build plugin generates `theme.d.ts` inside its `distDir`. The default +`distDir` is `df`, so the default declaration path is `df/theme.d.ts`. To enable +type autocompletion, include the generated declarations in `tsconfig.json`: + +```json +{ + "include": ["src", "df/*.d.ts"] +} +``` + +`distDir` is a build-plugin option, not a field in `devup.json`. For example, a +Vite configuration can change the generated directory: + +```ts +import DevupUI from '@devup-ui/vite-plugin' +import { defineConfig } from 'vite' + +export default defineConfig({ + plugins: [DevupUI({ distDir: 'generated/devup' })], +}) +``` + +That configuration writes `generated/devup/theme.d.ts`; include +`generated/devup/*.d.ts` instead of `df/*.d.ts`. Run the build plugin after +changing the theme to regenerate the declarations. ### **Typography Usage** @@ -183,6 +218,5 @@ Apply typography styles using the `typography` prop. ```tsx <> Heading 1 - Body text ``` From 783f1c8f88d90a04996f78a5e170a076d6a3f394 Mon Sep 17 00:00:00 2001 From: owjs3901 Date: Mon, 5 Oct 2026 10:37:55 +0900 Subject: [PATCH 10/13] docs: clarify vanilla-extract ordinary-module support Refs #693 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- .../docs/migration/vanilla-extract/page.mdx | 103 ++++++++++++++++-- 1 file changed, 92 insertions(+), 11 deletions(-) diff --git a/apps/landing/src/app/(detail)/docs/migration/vanilla-extract/page.mdx b/apps/landing/src/app/(detail)/docs/migration/vanilla-extract/page.mdx index df42ee57e..40894861d 100644 --- a/apps/landing/src/app/(detail)/docs/migration/vanilla-extract/page.mdx +++ b/apps/landing/src/app/(detail)/docs/migration/vanilla-extract/page.mdx @@ -40,7 +40,9 @@ A stylesheet that throws while it is evaluated — an import that does not resol ## Ordinary modules -`style`, `globalStyle` and `keyframes` also resolve in `.ts` / `.tsx`, mapped onto their Devup UI counterparts, and `style([base, { ... }])` keeps composing `base`: +Named imports of `style`, `globalStyle` and `keyframes` also resolve in `.ts` / +`.tsx`, mapped onto their Devup UI counterparts. Literal rule objects compile +there without evaluating the whole module: ```tsx import { globalStyle, style } from '@vanilla-extract/css' @@ -49,28 +51,107 @@ export const a = style({ color: 'red' }) globalStyle('body', { margin: 0 }) ``` -```tsx -// output — no import left -export const a = 'a' +```text +import "@devup-ui/react/devup-ui.css"; +export const a = "a"; +; +``` + +With an empty theme, the same probe emits: + +```text +/*! devup-ui v1.0.82, | Apache License 2.0 | https://devup-ui.com */@layer b;@layer b{body{margin:0}}.a{color:red} ``` +Generated names depend on the class prefix and extraction state. The +vanilla-extract API import is removed, but the generated CSS import remains. + `globalStyle(selector, rules)` keeps vanilla-extract's two-argument shape; the extractor folds the selector back into the object `globalCss` takes. -The remaining APIs stay on `@vanilla-extract/css` outside a stylesheet file, because evaluating a module that also contains React components is not possible. A build warning names them: +When the build knows the composed styles, `style([base, { ... }])` keeps +non-conflicting declarations and lets the later part win per property, +selector, breakpoint, and layer, as described in +[Supported Syntax & Limitations](/docs/limitations). For example: +```ts +import { style } from '@vanilla-extract/css' + +const base = style({ color: 'red', padding: 8 }) +export const button = style([base, { color: 'blue' }]) ``` -[devup-ui] WARNING: '@vanilla-extract/css' keeps styleVariants, createVar because -devup-ui has no equivalent export, so the package stays a runtime dependency. + +Extracted code: + +```text +import "@devup-ui/react/devup-ui.css"; +const base = "a b"; +export const button = "b c"; ``` -Moving those calls into a `.css.ts` file — where vanilla-extract wants them anyway — removes the dependency. +Generated CSS: -## Namespace imports +```text +/*! devup-ui v1.0.82, | Apache License 2.0 | https://devup-ui.com */.c{color:blue}.a{color:red}.b{padding:8px} +``` -A namespace stands for many named exports whose Devup UI counterparts are renamed (`style` → `css`), which a namespace access cannot express, so it is left alone: +`button` does not include the conflicting red class. Unknown external classes +still follow the CSS cascade; class-name string order is not an override rule. + +Ordinary-module extraction does **not** execute a vanilla-extract theme contract +to discover its generated custom properties. Keep contracts and their consuming +styles together in `.css.ts` / `.css.js`. For example, the stylesheet example +at the top of this page is not valid if saved as an ordinary `theme.ts` module: + +```ts +// theme.ts (not a stylesheet file) +import { createTheme, createThemeContract, style } from '@vanilla-extract/css' + +const vars = createThemeContract({ colors: { bg: null } }) +export const light = createTheme(vars, { colors: { bg: 'white' } }) +export const box = style({ background: vars.colors.bg }) +``` + +The probe locates the call at line 6, column 20 and reports this message: + +```text +`css()` cannot use `vars.colors.bg` at build time: its values must be literals, theme tokens or constants, or be computed from them +``` + +The two-file stylesheet example above likewise does not promise that the same +imports work in an ordinary `.ts` module: ```ts -// unchanged +// button.ts (not a stylesheet file) +import { style } from '@vanilla-extract/css' + +import { base, vars } from './theme.css' +import { PRIMARY } from './tokens' + +export const button = style([base, { color: PRIMARY, margin: vars.space }]) ``` +Even when `theme.css.ts` and `tokens.ts` resolve, the probe locates the +theme-variable read at line 7, column 23 and reports: + +```text +`css()` cannot use `vars.space` at build time: its values must be literals, theme tokens or constants, or be computed from them +``` + +Move that consuming module to `button.css.ts` and export the styles and theme +values from valid stylesheet modules. Ordinary modules can read resolvable +literal constants, but that does not make a generated theme object an ordinary +literal constant. A build-time-known composition must not be confused with a +theme-variable read the ordinary-module path cannot resolve. + +The remaining APIs stay imported from `@vanilla-extract/css` outside a +stylesheet file. For example, `createVar()` and `styleVariants()` remain calls +to that package even when a `style()` call in the same module is transformed. +They are not evaluated by the ordinary-module extraction path. + +Moving those calls into a `.css.ts` file — where vanilla-extract wants them anyway — removes the dependency. + +## Namespace imports + +A namespace stands for many named exports whose Devup UI counterparts are renamed (`style` → `css`), which a namespace access cannot express, so it is left alone: + Use named imports to get the rewrite. From cef0c83188785c220134cf7c813514cf41a7a261 Mon Sep 17 00:00:00 2001 From: owjs3901 Date: Mon, 5 Oct 2026 10:50:18 +0900 Subject: [PATCH 11/13] docs: refresh extractor guide with actual probe output Refs #693 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- .../changepack_log_extractor_docs.json | 7 ++++ libs/extractor/README.md | 40 ++++++++++++------- 2 files changed, 33 insertions(+), 14 deletions(-) create mode 100644 .changepacks/changepack_log_extractor_docs.json diff --git a/.changepacks/changepack_log_extractor_docs.json b/.changepacks/changepack_log_extractor_docs.json new file mode 100644 index 000000000..660877013 --- /dev/null +++ b/.changepacks/changepack_log_extractor_docs.json @@ -0,0 +1,7 @@ +{ + "changes": { + "bindings/devup-ui-wasm/package.json": "Patch" + }, + "note": "The extractor guide shows reproducible native-element output, generated CSS-variable bindings, and actual atomic CSS rather than obsolete class names. Documentation only: no new build errors.", + "date": "2026-10-01T00:00:00.000Z" +} diff --git a/libs/extractor/README.md b/libs/extractor/README.md index 9f43037ea..706dcfd13 100644 --- a/libs/extractor/README.md +++ b/libs/extractor/README.md @@ -1,28 +1,40 @@ ## Extractor -jsx to css extractor +Build-time JSX to CSS extractor. Static styles become atomic classes; dynamic +style values are passed through CSS variables on the native element. ### Example -Before +Standalone input (with the build plugin configured): ```tsx - - Hello World - +import { Box } from '@devup-ui/react' + +function Example({ variable }: { variable: 'left' | 'right' }) { + return + Hello World + +} ``` -After +Extracted code from an isolated run with an empty theme (`{}`) and the default +prefix. Generated class and variable names depend on extraction state and prefix. ```tsx - - Hello World - +import "@devup-ui/react/devup-ui.css"; +function Example({ variable }: { + variable: "left" | "right"; +}) { + return
+ Hello World +
; +} +``` + +Generated CSS: + +```css +/*! devup-ui v1.0.82, | Apache License 2.0 | https://devup-ui.com */.a{background:red}.b{color:white}.c{margin:8px}.d{padding:8px}.e{text-align:var(--f)} ``` ```mermaid From b11bb0e9b8ab91f7be6a9340107cec04370cdfd5 Mon Sep 17 00:00:00 2001 From: owjs3901 Date: Mon, 5 Oct 2026 12:33:06 +0900 Subject: [PATCH 12/13] docs: lock breakpoint tables and complete verified stylesheet examples Refs #693 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- .../changepack_log_docs_breakpoints.json | 8 + .../(detail)/docs/devup/typography/page.mdx | 4 +- .../docs/migration/vanilla-extract/page.mdx | 13 +- libs/sheet/tests/docs_breakpoints.rs | 137 ++++++++++++++++++ 4 files changed, 158 insertions(+), 4 deletions(-) create mode 100644 .changepacks/changepack_log_docs_breakpoints.json create mode 100644 libs/sheet/tests/docs_breakpoints.rs diff --git a/.changepacks/changepack_log_docs_breakpoints.json b/.changepacks/changepack_log_docs_breakpoints.json new file mode 100644 index 000000000..2f71eb081 --- /dev/null +++ b/.changepacks/changepack_log_docs_breakpoints.json @@ -0,0 +1,8 @@ +{ + "changes": { + "bindings/devup-ui-wasm/package.json": "Patch", + "apps/landing/package.json": "Patch" + }, + "note": "Typography documents the emitted default breakpoints, and source-backed integration tests keep the public breakpoint lists and range bounds synchronized with the production defaults. No runtime changes or new build errors.", + "date": "2026-10-01T00:00:00.000Z" +} diff --git a/apps/landing/src/app/(detail)/docs/devup/typography/page.mdx b/apps/landing/src/app/(detail)/docs/devup/typography/page.mdx index 376f818a6..965106674 100644 --- a/apps/landing/src/app/(detail)/docs/devup/typography/page.mdx +++ b/apps/landing/src/app/(detail)/docs/devup/typography/page.mdx @@ -180,9 +180,9 @@ The breakpoints are: - Index 0: `0px` (mobile) - Index 1: `480px` - Index 2: `768px` -- Index 3: `1024px` +- Index 3: `992px` - Index 4: `1280px` (desktop) -- Index 5: `1640px` +- Index 5: `1600px` Use `null` to skip a breakpoint and inherit from the previous value. diff --git a/apps/landing/src/app/(detail)/docs/migration/vanilla-extract/page.mdx b/apps/landing/src/app/(detail)/docs/migration/vanilla-extract/page.mdx index 40894861d..23d720a21 100644 --- a/apps/landing/src/app/(detail)/docs/migration/vanilla-extract/page.mdx +++ b/apps/landing/src/app/(detail)/docs/migration/vanilla-extract/page.mdx @@ -15,15 +15,24 @@ Inside `.css.ts` / `.css.js` — the only place vanilla-extract allows its APIs // theme.css.ts import { createTheme, createThemeContract, style } from '@vanilla-extract/css' -const vars = createThemeContract({ colors: { bg: null } }) -export const light = createTheme(vars, { colors: { bg: 'white' } }) +export const vars = createThemeContract({ colors: { bg: null }, space: null }) +export const light = createTheme(vars, { + colors: { bg: 'white' }, + space: '8px', +}) export const box = style({ background: vars.colors.bg }) +export const base = style({ padding: 8 }) ``` Numbers keep vanilla-extract's meaning, here and in ordinary modules: `padding: 8` is `8px`, and unitless properties such as `lineHeight` or `zIndex` stay as written. As in vanilla-extract, a number for a time gets `px` too (`transitionDuration: 300` is `300px`, which browsers ignore), so write times with their unit: `transitionDuration: '300ms'`. A stylesheet can import other stylesheets and ordinary modules — ES modules or CommonJS — resolved like the bundler resolves them (relative paths, `tsconfig` `paths` and packages). An imported stylesheet is extracted the way the bundler extracts it, so the class names and custom properties it exports are the ones its own CSS uses, and the importer keeps importing it so that CSS is loaded: +```ts +// tokens.ts +export const PRIMARY = 'blue' +``` + ```ts // button.css.ts import { style } from '@vanilla-extract/css' diff --git a/libs/sheet/tests/docs_breakpoints.rs b/libs/sheet/tests/docs_breakpoints.rs new file mode 100644 index 000000000..a1d0650fc --- /dev/null +++ b/libs/sheet/tests/docs_breakpoints.rs @@ -0,0 +1,137 @@ +use sheet::theme::Theme; +use std::{ + error::Error, + io::{Error as IoError, ErrorKind}, +}; + +#[test] +fn typography_breakpoint_indices_match_default_theme() -> Result<(), Box> { + let docs = + include_str!("../../../apps/landing/src/app/(detail)/docs/devup/typography/page.mdx"); + let defaults = Theme::default().breakpoints; + + let documented: Vec<(usize, u16)> = docs + .lines() + .filter_map(|line| line.trim().strip_prefix("- Index ")) + .map(|entry| -> Result<_, Box> { + let (index, value) = entry.split_once(':').ok_or_else(|| { + IoError::new( + ErrorKind::InvalidData, + "typography breakpoint entry must contain an index and value", + ) + })?; + let pixels = value + .trim() + .strip_prefix('`') + .and_then(|value| value.split_once("px`").map(|(pixels, _)| pixels)) + .ok_or_else(|| { + IoError::new( + ErrorKind::InvalidData, + "typography breakpoint value must be pixels in inline code", + ) + })?; + Ok((index.parse()?, pixels.parse()?)) + }) + .collect::>()?; + + assert_eq!( + documented, + defaults.into_iter().enumerate().collect::>(), + "public typography breakpoint indices must match Theme::default()" + ); + Ok(()) +} + +#[test] +fn documented_breakpoint_ranges_match_default_theme() -> Result<(), Box> { + let docs = + include_str!("../../../apps/landing/src/app/(detail)/docs/devup/breakpoints/page.mdx"); + let defaults = Theme::default().breakpoints; + let table = docs + .split_once("") + .and_then(|(_, body)| body.split_once("")) + .map(|(body, _)| body) + .ok_or_else(|| { + IoError::new( + ErrorKind::InvalidData, + "breakpoints page must contain a ranges table body", + ) + })?; + + let documented: Vec<(usize, u16, Option)> = table + .split("") + .skip(1) + .map(|row| -> Result<_, Box> { + let cells = row + .split("") + .skip(1) + .map(|cell| { + cell.split_once("") + .map(|(value, _)| value.trim()) + .ok_or_else(|| { + IoError::new( + ErrorKind::InvalidData, + "breakpoint table cell must have a closing tag", + ) + }) + }) + .collect::, _>>()?; + let mut cells = cells.into_iter(); + let index = cells + .next() + .ok_or_else(|| { + IoError::new( + ErrorKind::InvalidData, + "breakpoint row must contain an index", + ) + })? + .parse()?; + let range = cells.nth(1).ok_or_else(|| { + IoError::new( + ErrorKind::InvalidData, + "breakpoint row must contain a range", + ) + })?; + let (start, end) = if let Some(start) = range.strip_suffix("px+") { + (start, None) + } else { + let (start, end) = range.split_once("px - ").ok_or_else(|| { + IoError::new( + ErrorKind::InvalidData, + "breakpoint range must have pixel bounds", + ) + })?; + let end = end.strip_suffix("px").ok_or_else(|| { + IoError::new(ErrorKind::InvalidData, "range end must use pixels") + })?; + (start, Some(end.parse()?)) + }; + Ok((index, start.parse()?, end)) + }) + .collect::>()?; + + let expected: Vec<_> = defaults + .iter() + .copied() + .enumerate() + .map(|(index, start)| { + let end = defaults + .get(index + 1) + .map(|next| { + next.checked_sub(1).ok_or_else(|| { + IoError::new( + ErrorKind::InvalidData, + "default breakpoint range must have a positive upper boundary", + ) + }) + }) + .transpose()?; + Ok::<_, IoError>((index, start, end)) + }) + .collect::>()?; + assert_eq!( + documented, expected, + "public breakpoint range indices and bounds must match Theme::default()" + ); + Ok(()) +} From d1dd51d388dbc5ab761fb57bdc23ce2ef7de0f3c Mon Sep 17 00:00:00 2001 From: owjs3901 Date: Mon, 5 Oct 2026 13:26:56 +0900 Subject: [PATCH 13/13] docs: synchronize root README examples with actual output Refs #693 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- README.md | 70 +++++++++++++++++++++++++++++++++++----------------- README_ko.md | 68 ++++++++++++++++++++++++++++++++++---------------- 2 files changed, 95 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index c07fac795..556be5e8e 100644 --- a/README.md +++ b/README.md @@ -111,47 +111,73 @@ Devup UI transforms your components at build time. Class names are generated usi **Basic transformation:** +Generated class and CSS variable names may change between versions, build state, and prefix settings; do not manually reuse them. + +You write: + ```tsx -// You write: -const variable = +import { Box } from '@devup-ui/react' + +const example = +``` -// Devup UI generates: -const variable =
+Devup UI generates: -// With CSS: -// .a { background-color: red; } -// .b { padding: 1rem; } -// .c:hover { background-color: blue; } +```tsx +import "@devup-ui/react/devup-ui.css"; +const example =
; +``` + +With CSS: + +```css +/*! devup-ui v1.0.82, | Apache License 2.0 | https://devup-ui.com */.b{background:red}.c{padding:16px}.a:hover{background:blue} ``` **Dynamic values become CSS variables:** +You write: + ```tsx -// You write: +import { Box } from '@devup-ui/react' + const example = +``` -// Devup UI generates: -const example =
+Devup UI generates: -// With CSS: -// .a { background-color: var(--a); } +```tsx +import "@devup-ui/react/devup-ui.css"; +const example =
; +``` + +With CSS: + +```css +/*! devup-ui v1.0.82, | Apache License 2.0 | https://devup-ui.com */.a{background:var(--b)} ``` **Complex expressions and responsive arrays:** +You write: + ```tsx -// You write: +import { Box } from '@devup-ui/react' + const example = +``` + +Devup UI generates: + +```tsx +import "@devup-ui/react/devup-ui.css"; +const example =
; +``` -// Devup UI generates: -const example = ( -
-) +With responsive CSS for each breakpoint: -// With responsive CSS for each breakpoint +```css +/*! devup-ui v1.0.82, | Apache License 2.0 | https://devup-ui.com */.a{background:red}@media(min-width:480px){.b{background:blue}}@media(min-width:768px){.c{background:green}.d{background:var(--e)}} ``` **Type-safe theming:** diff --git a/README_ko.md b/README_ko.md index eaee1981a..9dadc6606 100644 --- a/README_ko.md +++ b/README_ko.md @@ -111,47 +111,73 @@ Devup UI는 빌드 타임에 컴포넌트를 변환합니다. 클래스명은 CS **기본 변환:** +생성된 클래스명과 CSS 변수명은 버전, 빌드 상태, 접두사 설정에 따라 달라질 수 있으므로 직접 재사용하지 마세요. + +개발자가 작성: + ```tsx -// 개발자가 작성: +import { Box } from '@devup-ui/react' + const example = +``` -// Devup UI가 생성: -const generated =
+Devup UI가 생성: -// CSS: -// .a { background-color: red; } -// .b { padding: 1rem; } -// .c:hover { background-color: blue; } +```tsx +import "@devup-ui/react/devup-ui.css"; +const example =
; +``` + +CSS: + +```css +/*! devup-ui v1.0.82, | Apache License 2.0 | https://devup-ui.com */.b{background:red}.c{padding:16px}.a:hover{background:blue} ``` **동적 값은 CSS 변수로 변환:** +개발자가 작성: + ```tsx -// 개발자가 작성: +import { Box } from '@devup-ui/react' + const example = +``` -// Devup UI가 생성: -const generated =
+Devup UI가 생성: -// CSS: -// .a { background-color: var(--a); } +```tsx +import "@devup-ui/react/devup-ui.css"; +const example =
; +``` + +CSS: + +```css +/*! devup-ui v1.0.82, | Apache License 2.0 | https://devup-ui.com */.a{background:var(--b)} ``` **복잡한 표현식과 반응형 배열:** +개발자가 작성: + ```tsx -// 개발자가 작성: +import { Box } from '@devup-ui/react' + const example = +``` + +Devup UI가 생성: + +```tsx +import "@devup-ui/react/devup-ui.css"; +const example =
; +``` -// Devup UI가 생성: -const generated = ( -
-) +각 브레이크포인트에 대한 반응형 CSS: -// 각 브레이크포인트에 대한 반응형 CSS 생성 +```css +/*! devup-ui v1.0.82, | Apache License 2.0 | https://devup-ui.com */.a{background:red}@media(min-width:480px){.b{background:blue}}@media(min-width:768px){.c{background:green}.d{background:var(--e)}} ``` **타입 세이프 테마:**