Skip to content
Closed
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
8 changes: 8 additions & 0 deletions .changepacks/changepack_log_at_rule_composition.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"changes": {
"bindings/devup-ui-wasm/package.json": "Patch",
"packages/react/package.json": "Patch"
},
"note": "Fix at-rule composition and cascade: `_print`/`_media`/`@media …` nest correctly with selectors in both directions and inside `globalCss`, nested media queries merge (`print and (…)`), breakpoints put media types first, distribute query lists, and nest `not …` queries, and at-rules are emitted after all breakpoint values while pseudo selectors keep `SELECTOR_ORDER` across breakpoints. Add `_motionReduce`, `_motionSafe`, `_portrait`, `_landscape`, `_contrastMore`, `_contrastLess` and `_forcedColors`, remove the never-matching `_speech`, normalize query whitespace, support Emotion-style `'@media …'`/`'&:hover'` object keys, fix template literals losing declarations before `@media` or selectors around nested at-rules, apply `typography` conditionally under selectors, and drop stale global at-rules on re-extraction",
"date": "2026-09-26T00:00:00.000Z"
}
8 changes: 8 additions & 0 deletions .changepacks/changepack_log_css_output_fixes.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"changes": {
"bindings/devup-ui-wasm/package.json": "Patch",
"packages/eslint-plugin/package.json": "Patch"
},
"note": "Fix the full WASM build panicking on every vanilla-extract `.css.ts` file (Boa read the clock without its `js` feature), resolve vanilla-extract references to other styles, keyframes, fonts and themes in selectors, values and `globalStyle`, keep `globalCss` pseudo selectors in `SELECTOR_ORDER` across breakpoints, merge adjacent rules with the same selector, and let declarations written next to a conditional `typography` preset win over it. Add the `prefer-media-shorthand` and `no-typography-token-prefix` ESLint rules",
"date": "2026-09-27T00:00:00.000Z"
}
8 changes: 8 additions & 0 deletions .changepacks/changepack_log_library_numbers_full_engine.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"changes": {
"bindings/devup-ui-wasm/package.json": "Patch",
"packages/next-plugin/package.json": "Patch"
},
"note": "Numbers in vanilla-extract and StyleX styles keep their library's meaning instead of Devup UI's spacing scale: vanilla-extract styles add `px` in `.css.ts` files and in `style()`, `globalStyle()` and `keyframes()` calls inside ordinary modules, StyleX adds `px`/`ms` to static values and to numbers passed to dynamic styles, and unitless properties such as `columnCount`, `strokeWidth`, `orphans` or `zoom` keep a bare number everywhere. The Next.js plugin now runs every `.css.ts` module on the full engine instead of a separate static fast path, so development and production builds produce the same output",
"date": "2026-09-27T00:00:00.000Z"
}
14 changes: 14 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ Turbopack values are medians of six cold builds in alternating order:
| **devup-ui (direct APIs, single CSS)** | 1.0.40 | **5.46s** | **36,507,782 bytes** |
| **devup-ui (static `.css.ts`)** | 1.0.40 | **5.41s** | 36,586,883 bytes |

The Turbopack ranges overlap, so the direct-API median is 0.06s (1.1%) ahead but still effectively parity with Tailwind on this fixture. The six cold samples were Tailwind `5.42, 5.56, 5.70, 5.53, 5.41, 5.52s`, direct Devup UI `5.64, 5.40, 5.42, 5.46, 5.54, 5.46s`, and static `.css.ts` `5.42, 5.38, 5.42, 5.47, 5.40, 5.38s`. The fixtures have comparable app shapes, not pixel-identical styling: Tailwind styles the leading paragraph and button more heavily, while Devup UI exercises typed component/style props. Treat these as build-pipeline results rather than a per-rule microbenchmark. The static `.css.ts` row uses the `lite` WASM fast path; dynamic `.css.ts` modules use the full Boa evaluator and are not represented by that median.
The Turbopack ranges overlap, so the direct-API median is 0.06s (1.1%) ahead but still effectively parity with Tailwind on this fixture. The six cold samples were Tailwind `5.42, 5.56, 5.70, 5.53, 5.41, 5.52s`, direct Devup UI `5.64, 5.40, 5.42, 5.46, 5.54, 5.46s`, and static `.css.ts` `5.42, 5.38, 5.42, 5.47, 5.40, 5.38s`. The fixtures have comparable app shapes, not pixel-identical styling: Tailwind styles the leading paragraph and button more heavily, while Devup UI exercises typed component/style props. Treat these as build-pipeline results rather than a per-rule microbenchmark. The static `.css.ts` row was measured with the former `lite` fast path for static modules; every `.css.ts` module now runs on the full Boa evaluator, which adds about 20 ms for the first module and under 1 ms for each further one.

## How it works

Expand Down Expand Up @@ -186,7 +186,7 @@ const example = (
```tsx
// Type-safe theme tokens
const textExample = <Text color="$primary" />
const boxExample = <Box typography="$heading" />
const boxExample = <Box typography="heading" />
```

**Custom shorthands:**
Expand Down
4 changes: 2 additions & 2 deletions README_ko.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ Turbopack 값은 실행 순서를 번갈아 측정한 cold build 6회의 중앙
| **devup-ui (direct APIs, single CSS)** | 1.0.40 | **6.54s** | **36,519,234 bytes** |
| **devup-ui (static `.css.ts`)** | 1.0.40 | **6.47s** | 36,550,197 bytes |

Turbopack 측정 범위는 서로 겹치므로 direct API 결과는 이 fixture에서 Tailwind와 사실상 동률입니다. static `.css.ts` 행은 `lite` WASM 빠른 경로를 사용하며, full Boa evaluator가 필요한 dynamic `.css.ts`는 해당 중앙값에 포함되지 않습니다.
Turbopack 측정 범위는 서로 겹치므로 direct API 결과는 이 fixture에서 Tailwind와 사실상 동률입니다. static `.css.ts` 행은 정적 모듈용 `lite` 빠른 경로가 있던 때에 측정한 값입니다. 지금은 모든 `.css.ts`를 full Boa evaluator로 처리하며, 첫 모듈에 약 20ms, 이후 모듈마다 1ms 미만이 더 듭니다.

## 작동 원리

Expand Down Expand Up @@ -186,7 +186,7 @@ const generated = (
```tsx
// 타입 세이프 테마 토큰
const textExample = <Text color="$primary" />
const boxExample = <Box typography="$heading" />
const boxExample = <Box typography="heading" />
```

**사용자 정의 shorthand:**
Expand Down
21 changes: 20 additions & 1 deletion SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,10 @@ All standard CSS properties from `csstype` are also accepted directly (e.g., `di
<Box p="20px" /> // padding: 20px (with unit = exact value)
```

Unitless CSS properties keep the number as written: `lineHeight`, `zIndex`, `opacity`, `fontWeight`, `flexGrow`, `order`, `aspectRatio`, `columnCount`, `strokeWidth`, `zoom`, and the rest of the unitless list vanilla-extract uses.

Styles from other libraries keep that library's number meaning instead of the scale: in `.css.ts` files, in vanilla-extract `style()`, `globalStyle()` and `keyframes()` calls inside ordinary modules, and in `stylex.create()`, `fontSize: 16` is `16px`.

### Responsive Arrays (5 breakpoints)

```tsx
Expand Down Expand Up @@ -215,8 +219,23 @@ Available: `_groupHover`, `_groupFocus`, `_groupActive`, `_groupDisabled`.

// @ prefix syntax (equivalent)
<Box {...{ "@media": { "(min-width: 768px)": { w: "50%" } } }} />

// Query in the key (Emotion style), also inside `selectors` and css()/styled()
<Box selectors={{ "@media print": { display: "none" } }} />
```

Media shorthands wrap styles in a fixed query and nest in either direction with selectors:
`_print`, `_screen`, `_all`, `_portrait`, `_landscape`, `_motionReduce` (`prefers-reduced-motion: reduce`), `_motionSafe` (`no-preference`), `_contrastMore`, `_contrastLess`, `_forcedColors`.

```tsx
<Box transition={["opacity .2s", null, "all .3s"]} _motionReduce={{ transition: "none" }} />
<Box _motionSafe={{ _hover: { transform: "scale(1.05)" } }} />
globalCss({ _motionReduce: { "*, *::before, *::after": { transition: "none" } } })
```

- Conditions beat breakpoint values: at-rule styles are emitted after every responsive rule.
- Nested `@media` rules merge into one query (`print and (prefers-reduced-motion:reduce)`); ones that can never match together (`_print` inside `_screen`) are dropped.

### Custom Selectors

```tsx
Expand Down Expand Up @@ -365,7 +384,7 @@ const spin = keyframes({ from: { transform: "rotate(0)" }, to: { transform: "rot
```

- **Colors**: Use with `$` prefix in JSX props: `<Box color="$primary" />`
- **Typography**: Use with `$` prefix: `<Text typography="$heading" />`
- **Typography**: Use the preset name without `$`: `<Text typography="heading" />`. Under selectors or at-rules (`_hover={{ typography: "heading" }}`) the preset applies only under that condition.
- **Length**: Responsive length tokens: `<Box px="$containerX" />`, `<Flex gap="$gutter" />`
- **Shadow**: Responsive shadow tokens: `<Box boxShadow="$card" />`
- **extends**: Inherit from base config files (deep merge, last wins)
Expand Down
6 changes: 6 additions & 0 deletions apps/landing/src/app/(detail)/docs/api/box/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ However, the following properties are exceptions and **are not multiplied by 4**
- `grid-row`, `grid-row-start`, `grid-row-end`
- `animation-iteration-count`
- `tab-size`, `moz-tab-size`, `-webkit-line-clamp`
- `grid-area`
- `column-count`, `columns`, `orphans`, `widows`, `zoom`
- `border-image`, `border-image-outset`, `border-image-slice`, `border-image-width`
- `mask-border`, `mask-border-outset`, `mask-border-slice`, `mask-border-width`
- `box-flex`, `box-flex-group`, `box-ordinal-group`, `initial-letter`, `max-lines`, `shape-image-threshold`
- `fill-opacity`, `flood-opacity`, `stop-opacity`, `stroke-opacity`, `stroke-width`, `stroke-dashoffset`, `stroke-miterlimit`

### Rendering as Another Element

Expand Down
6 changes: 6 additions & 0 deletions apps/landing/src/app/(detail)/docs/api/button/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,9 @@ However, the following properties are exceptions and **are not multiplied by 4**
- `grid-row`, `grid-row-start`, `grid-row-end`
- `animation-iteration-count`
- `tab-size`, `moz-tab-size`, `-webkit-line-clamp`
- `grid-area`
- `column-count`, `columns`, `orphans`, `widows`, `zoom`
- `border-image`, `border-image-outset`, `border-image-slice`, `border-image-width`
- `mask-border`, `mask-border-outset`, `mask-border-slice`, `mask-border-width`
- `box-flex`, `box-flex-group`, `box-ordinal-group`, `initial-letter`, `max-lines`, `shape-image-threshold`
- `fill-opacity`, `flood-opacity`, `stop-opacity`, `stroke-opacity`, `stroke-width`, `stroke-dashoffset`, `stroke-miterlimit`
6 changes: 6 additions & 0 deletions apps/landing/src/app/(detail)/docs/api/center/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ However, the following properties are exceptions and **are not multiplied by 4**
- `grid-row`, `grid-row-start`, `grid-row-end`
- `animation-iteration-count`
- `tab-size`, `moz-tab-size`, `-webkit-line-clamp`
- `grid-area`
- `column-count`, `columns`, `orphans`, `widows`, `zoom`
- `border-image`, `border-image-outset`, `border-image-slice`, `border-image-width`
- `mask-border`, `mask-border-outset`, `mask-border-slice`, `mask-border-width`
- `box-flex`, `box-flex-group`, `box-ordinal-group`, `initial-letter`, `max-lines`, `shape-image-threshold`
- `fill-opacity`, `flood-opacity`, `stop-opacity`, `stroke-opacity`, `stroke-width`, `stroke-dashoffset`, `stroke-miterlimit`

<br />

Expand Down
6 changes: 6 additions & 0 deletions apps/landing/src/app/(detail)/docs/api/grid/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@ However, the following properties are exceptions and **are not multiplied by 4**
- `grid-row`, `grid-row-start`, `grid-row-end`
- `animation-iteration-count`
- `tab-size`, `moz-tab-size`, `-webkit-line-clamp`
- `grid-area`
- `column-count`, `columns`, `orphans`, `widows`, `zoom`
- `border-image`, `border-image-outset`, `border-image-slice`, `border-image-width`
- `mask-border`, `mask-border-outset`, `mask-border-slice`, `mask-border-width`
- `box-flex`, `box-flex-group`, `box-ordinal-group`, `initial-letter`, `max-lines`, `shape-image-threshold`
- `fill-opacity`, `flood-opacity`, `stop-opacity`, `stroke-opacity`, `stroke-width`, `stroke-dashoffset`, `stroke-miterlimit`

<br />

Expand Down
126 changes: 126 additions & 0 deletions apps/landing/src/app/(detail)/docs/api/selector/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -182,3 +182,129 @@ The attributes defined in Pseudo-Class correspond to those defined in Selector,
</tr>
</tbody>
</table>

## Media Shorthands

These props wrap their styles in a fixed media query. They accept responsive arrays and nest in either direction with selectors and other at-rules.

<br />

<table>
<thead>
<tr>
<th width="300px">Selector</th>
<th width="300px">Media Query</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<code>_print</code>
</td>
<td>
<code>print</code>
</td>
</tr>
<tr>
<td>
<code>_screen</code>
</td>
<td>
<code>screen</code>
</td>
</tr>
<tr>
<td>
<code>_all</code>
</td>
<td>
<code>all</code>
</td>
</tr>
<tr>
<td>
<code>_portrait</code>
</td>
<td>
<code>(orientation:portrait)</code>
</td>
</tr>
<tr>
<td>
<code>_landscape</code>
</td>
<td>
<code>(orientation:landscape)</code>
</td>
</tr>
<tr>
<td>
<code>_motionReduce</code>
</td>
<td>
<code>(prefers-reduced-motion:reduce)</code>
</td>
</tr>
<tr>
<td>
<code>_motionSafe</code>
</td>
<td>
<code>(prefers-reduced-motion:no-preference)</code>
</td>
</tr>
<tr>
<td>
<code>_contrastMore</code>
</td>
<td>
<code>(prefers-contrast:more)</code>
</td>
</tr>
<tr>
<td>
<code>_contrastLess</code>
</td>
<td>
<code>(prefers-contrast:less)</code>
</td>
</tr>
<tr>
<td>
<code>_forcedColors</code>
</td>
<td>
<code>(forced-colors:active)</code>
</td>
</tr>
</tbody>
</table>

```tsx
const example = (
<Box
_motionReduce={{ transition: 'none' }}
_motionSafe={{ _hover: { transform: 'scale(1.05)' } }}
transition={['opacity .2s', null, 'all .3s']}
/>
)
```

## At-Rules

`_media`, `_supports` and `_container` take a record of queries to styles. A query can also sit in the key itself, as in Emotion, and `globalCss` accepts at-rules around whole selector maps.

```tsx
const example = (
<Box
_media={{ '(min-width: 500px)': { p: 2 } }}
selectors={{ '@media print': { display: 'none' } }}
/>
)

globalCss({
_motionReduce: { '*, *::before, *::after': { transition: 'none' } },
})
```

Conditions always win over breakpoint values: at-rule styles come after every responsive rule. Among themselves they are ordered by condition, with width queries mobile-first (`min-width` ascending, then `max-width` descending), then media types, other features, and user preferences such as `prefers-*` last.
6 changes: 6 additions & 0 deletions apps/landing/src/app/(detail)/docs/api/v-stack/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,12 @@ However, the following properties are exceptions and **are not multiplied by 4**
- `grid-row`, `grid-row-start`, `grid-row-end`
- `animation-iteration-count`
- `tab-size`, `moz-tab-size`, `-webkit-line-clamp`
- `grid-area`
- `column-count`, `columns`, `orphans`, `widows`, `zoom`
- `border-image`, `border-image-outset`, `border-image-slice`, `border-image-width`
- `mask-border`, `mask-border-outset`, `mask-border-slice`, `mask-border-width`
- `box-flex`, `box-flex-group`, `box-ordinal-group`, `initial-letter`, `max-lines`, `shape-image-threshold`
- `fill-opacity`, `flood-opacity`, `stop-opacity`, `stroke-opacity`, `stroke-width`, `stroke-dashoffset`, `stroke-miterlimit`

<br />

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ Or use responsive typography from `devup.json`:
```

```tsx
<Text typography="$h1">Responsive Heading</Text>
<Text typography="h1">Responsive Heading</Text>
```

## Responsive with Pseudo Selectors
Expand Down
4 changes: 2 additions & 2 deletions apps/landing/src/app/(detail)/docs/devup/devup-json/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -223,8 +223,8 @@ See [Shadow](/docs/devup/shadow) for more details.
### Typography

```tsx
const headingExample = <Text typography="$h1">Heading</Text>
const bodyExample = <Text typography="$body">Body text</Text>
const headingExample = <Text typography="h1">Heading</Text>
const bodyExample = <Text typography="body">Body text</Text>
```

### Length
Expand Down
Loading
Loading