Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changepacks/changepack_log_known_limitations.json
Original file line number Diff line number Diff line change
@@ -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"
}
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
</h3>

<p align="center">
<strong>Zero Config · Zero FOUC · Zero Runtime · Complete CSS-in-JS Syntax Coverage</strong>
<strong>Zero Config · Zero FOUC · Zero Runtime · Build-Time CSS-in-JS</strong>
</p>

---
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -137,7 +137,7 @@ const example = <div className="a" style={{ '--a': colorVariable }} />
// .a { background-color: var(--a); }
```

**Complex expressions and responsive arrays — fully supported:**
**Complex expressions and responsive arrays:**

```tsx
// You write:
Expand Down
6 changes: 3 additions & 3 deletions README_ko.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
</h3>

<p align="center">
<strong>Zero Config · Zero FOUC · Zero Runtime · 모든 CSS-in-JS 문법 완벽 지원</strong>
<strong>Zero Config · Zero FOUC · Zero Runtime · 빌드 타임 CSS-in-JS</strong>
</p>

---
Expand Down Expand Up @@ -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 출력 최소화
Expand Down Expand Up @@ -137,7 +137,7 @@ const generated = <div className="a" style={{ '--a': colorVariable }} />
// .a { background-color: var(--a); }
```

**복잡한 표현식과 반응형 배열 — 완벽 지원:**
**복잡한 표현식과 반응형 배열:**

```tsx
// 개발자가 작성:
Expand Down
1 change: 1 addition & 0 deletions apps/landing/src/app/(detail)/docs/LeftMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export function LeftMenu() {
Core Concepts
</MenuItem>
<MenuItem to="/docs/features">Features</MenuItem>
<MenuItem to="/docs/limitations">Supported Syntax & Limitations</MenuItem>
<MenuItem
subMenu={[
{
Expand Down
8 changes: 4 additions & 4 deletions apps/landing/src/app/(detail)/docs/features/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ export const metadata = {

# Features

Devup UI provides complete CSS-in-JS syntax coverage with zero runtime overhead. Every pattern you know from styled-components and Emotion works seamlessly — but without the performance cost.
Devup UI compiles the CSS-in-JS patterns you know from styled-components and Emotion at build time, with zero runtime overhead.

## Complete Syntax Coverage
## Syntax Coverage

Unlike other zero-runtime solutions that limit what you can do, Devup UI handles every CSS-in-JS pattern at build time.
Everything the build can know compiles to static CSS; a runtime value on an element becomes a CSS variable, and anything else is a build error naming the file, line and code. See [Supported Syntax & Limitations](/docs/limitations) for the full list.

## How It Works

Expand Down Expand Up @@ -230,7 +230,7 @@ const cardStyles = css({
borderRadius: '8px',
})

const example = <Box {...cardStyles} />
const example = <Box className={cardStyles} />
```

`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 `<Box {...baseStyles} _hover={hoverStyles} />` 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.
Expand Down
106 changes: 106 additions & 0 deletions apps/landing/src/app/(detail)/docs/limitations/page.mdx
Original file line number Diff line number Diff line change
@@ -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, `<ClassNames>`, `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 (
<div className={className} style={style}>
{children}
</div>
)
}

const example = <Card css={{ color: 'red', padding: width }} />
```

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 `<ClassNames>`, `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 `<div>` 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.
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
const G = styled(motion.div)`
color: red;
`
const H = styled(forwardRef(Field))({ bg: 'red' })

Check warning on line 30 in apps/landing/src/app/(detail)/docs/migration/styled-components/page.mdx

View workflow job for this annotation

GitHub Actions / publish

In React 19, 'forwardRef' is no longer necessary. Pass 'ref' as a prop instead
```

Any base renders: a tag, a component, a member such as `motion.div`, or any other value, read once where the component is declared. `null`, numbers, booleans and `undefined` never render, so they are build errors, as is a `styled` left uncompiled (`styled('div')` never given styles, `styled.div` exported bare): its import is removed, and that code would throw at runtime.
Expand Down Expand Up @@ -121,6 +121,13 @@
| `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.
2 changes: 1 addition & 1 deletion apps/landing/src/app/(detail)/docs/overview/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions e2e/exported-routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
6 changes: 3 additions & 3 deletions packages/react/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,15 +103,15 @@ 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 = <Box bg="red" />

const after = <div className="d0" />
```

Variables are fully supported.
Variables become CSS variables.

```tsx
const before = <Box bg={colorVariable} />
Expand All @@ -126,7 +126,7 @@ const after = (
)
```

Various expressions and responsiveness are also fully supported.
Conditions and responsive arrays compile too.

```tsx
const before = <Box bg={['red', 'blue', a > b ? 'yellow' : variable]} />
Expand Down
Loading