Skip to content
Merged
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
9 changes: 9 additions & 0 deletions .changepacks/changepack_log_emotion_css.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"changes": {
"packages/plugin-utils/package.json": "Patch",
"packages/react/package.json": "Patch",
"bindings/devup-ui-wasm/package.json": "Patch"
},
"note": "@emotion/css is aliased by default: css and keyframes compile like Devup UI's own (numbers in px, as Emotion reads them), injectGlobal declares global CSS. cx takes class names, as in Emotion: strings and runtime values are classes kept as written (falsy values are skipped), an object is a { name: condition } class map, arrays nest, and inline css() calls and css() classes compose with the rules css(a, b) uses. merge(className) splits what the build can read (string tokens, template literal parts) and composes the css() classes in it, keeping a runtime string it cannot split as it is. Namespace members, constant-string bracket access, const destructuring and const alias chains compile through these same named APIs with scope-aware binding resolution; numeric values retain Emotion units after constant inlining and build-time evaluation. The Emotion import disappears when no runtime-only member is used. Ambient declarations type the styling imports (cx(...args: ClassNamesArg[]), merge(className: string)). New located build errors: cx() cannot use a spread, a getter, a setter or a method in a class map, or a template literal call; merge() takes exactly one class string, not a spread or a template literal call; the root module has no default export, namespace member keys must be constant strings naming supported exports, and styling functions cannot be passed, mutated or re-exported. Optional styling reads/calls, namespace rest destructuring, styling defaults or nested patterns, non-const aliases, exported styling bindings and reads whose initialization order cannot be established are build errors instead of runtime dependencies. Styling loaded through require, import-equals or dynamic import must use static ES module imports; an unawaited dynamic namespace that the build cannot follow is also a located error. Its runtime-only APIs (cache, flush, hydrate, sheet, getRegisteredStyles) stay on @emotion/css, including their nested/default destructuring, optional reads and recognized runtime-loader access",
"date": "2026-10-01T00:00:00.000Z"
}
7 changes: 3 additions & 4 deletions apps/landing/src/app/(detail)/docs/build-errors/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -1902,15 +1902,14 @@ 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.
**Fix:** Call the imported css directly instead of creating a local runtime alias.

{/* probe: mutated-const-alias after */}

```tsx
import { css } from '@devup-ui/react'
export function f() {
const c = css
return c({ color: 'red' })
return css({ color: 'red' })
}
```

Expand Down Expand Up @@ -3926,7 +3925,7 @@ The `devup/no-runtime-read` rule reports compile-time bindings read outside supp
`<binding>` is read at runtime, where it does not exist: the build compiles it only where it is called or rendered.
```

```tsx
```tsx-error
import { css } from '@devup-ui/react'
consume(css)
```
Expand Down
7 changes: 4 additions & 3 deletions e2e/tailwind-hover-order.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ for (const singleCss of [true, false]) {
for (const fixture of cases) {
for (const [index, width] of [640, 1024].entries()) {
const label = 'props' in fixture ? 'explicit Devup guard: ' : ''
const devupColorIndex = 'props' in fixture ? 1 : 0
test(`${label}${fixture.classes}, ${width}px, singleCss=${singleCss}`, async ({
page,
}, testInfo) => {
Expand Down Expand Up @@ -191,7 +192,7 @@ for (const singleCss of [true, false]) {
const observed = []
for (const [name, classes, css, color] of [
['Tailwind', fixture.classes, oracleCss, expected[0]],
['Devup', matched?.[1], devupCss, expected[1]],
['Devup', matched?.[1], devupCss, expected[devupColorIndex]],
]) {
await page.setContent(
`<!doctype html><html data-theme="dark"><head><style>${css}</style></head><body><button id="target" class="${classes}" ${disabled ? 'disabled' : ''} style="width:160px;height:80px">probe</button></body></html>`,
Expand All @@ -207,14 +208,14 @@ for (const singleCss of [true, false]) {
} else if (!disabled) {
await expect(target).toHaveCSS(
'background-color',
colors.red[name === 'Tailwind' ? 0 : 1],
colors.red[name === 'Tailwind' ? 0 : devupColorIndex],
)
}
if (fixture.classes.includes('focus:')) {
await target.focus()
await expect(target).toHaveCSS(
'background-color',
colors.green[name === 'Tailwind' ? 0 : 1],
colors.green[name === 'Tailwind' ? 0 : devupColorIndex],
)
}
await page.mouse.down()
Expand Down
Loading
Loading