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
7 changes: 7 additions & 0 deletions .changepacks/changepack_log_skill_refresh.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"changes": {
"crates/devup-mcp/Cargo.toml": "Patch"
},
"note": "Re-vendored the embedded agent skill documents that moved in their source repositories: devup-ui. The copies in this binary are what `devup_skills install` writes on a machine with no network, so a copy that has fallen behind installs rules the upstream project no longer states. Opened automatically by the scheduled skill-drift job; the bytes here are exactly what scripts/refresh-skills.mjs produced.",
"date": "2026-10-05T06:24:50+00:00"
}
42 changes: 40 additions & 2 deletions crates/devup-mcp/src/server/skills/devup-ui/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`, `counterReset`/`counterIncrement`/`counterSet`, and the rest of the unitless list vanilla-extract uses. Times are milliseconds, never scaled: `transitionDuration={300}`, `transitionDelay`, `animationDuration` and `animationDelay` give `300ms`.

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, in Emotion and styled-components object styles, and in `stylex.create()`, `fontSize: 16` is `16px`. Devup UI shorthands (`p`, `bg`, ...) keep the scale everywhere.

### 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 All @@ -238,8 +257,14 @@ Available: `_groupHover`, `_groupFocus`, `_groupActive`, `_groupDisabled`.

// Conditional -> preserved
<Box bg={isActive ? "blue" : "gray"} /> // className={isActive ? "a" : "b"}

// Imported const -> static (resolved like the bundler: relative paths, tsconfig paths, packages; ESM or CommonJS)
import { PRIMARY } from "./tokens" // export const PRIMARY = "red"
<Box bg={PRIMARY} /> // className="a"; a `let`, call or package import stays a variable
```

A `.css.ts` file may import other stylesheets and modules (import cycles behave as in ES modules); an imported stylesheet exports the same names its own CSS uses. styled-components `.attrs()` (objects and functions) and `.withConfig()` compile, and `css(base, cond && { ... })` / `styled.div(base, cond ? a : b)` / `value || { ... }` / `value ?? { ... }` merge per property. A style argument that cannot be known at build time is a build error. StyleX `defineVars` / `defineConsts` / `createTheme` values imported from a `.stylex.ts` file resolve to the names that file generates, and variable/theme values may be condition objects (`{ default, [DARK]: ... }`, `@media` / `@supports` / `@container`). A `.css.ts` file that throws while evaluated is a build error carrying the exception. Build errors are reported all at once as `file:line:column: message`.

### Responsive + Pseudo Combined

```tsx
Expand All @@ -258,7 +283,10 @@ Changes the rendered HTML element or renders a custom component:
<Box as="section" bg="gray" /> // renders <section>
<Box as="a" href="/about" /> // renders <a>
<Box as={MyComponent} bg="red" /> // renders <MyComponent> with extracted styles
<Box as={motion.div} /> // renders <motion.div>
<Box as={b ? "div" : "section"} /> // conditional element type
<Box as={b ? "a" : undefined} /> // undefined/null/false -> default element (<div>)
<Box as={`h${level}`} /> // any other value is read at runtime, default when empty
```

### `props` (Pass-Through to `as` Component)
Expand Down Expand Up @@ -310,11 +338,20 @@ globalCss({ body: { margin: 0 }, "*": { boxSizing: "border-box" } });

const spin = keyframes({ from: { transform: "rotate(0)" }, to: { transform: "rotate(360deg)" } });
<Box animation={`${spin} 1s linear infinite`} />

// A const holding a keyframes name or a css() class is a build-time value
const card = css({ p: 4 });
css({ animationName: spin, selectors: { [`.${card}:hover &`]: { m: 1 } } });
```

- Only a `const` declared in the same file works this way; an imported keyframes/class name is not known at build time.
- `import * as Devup from "@devup-ui/react"` works (`Devup.css`, `Devup.keyframes`, `Devup.styled.div`, `<Devup.Box />`).
- `styled()` takes any base: tag, Devup component, `motion.div`, `forwardRef(...)`, a variable. `null`/number/boolean/`undefined` bases are build errors.
- Compiled imports are removed: a top-level alias (`const myCss = css`, `const Row = Flex`) compiles and is removed, but any other runtime read (`export const C = Box`, `[Box]`, `styled('div')` alone, an alias inside a function) is a **build error**.

### Dynamic Values with Custom Components

`css()` only accepts **static values**. For dynamic values on custom components, use `<Box as={Component}>`:
`css()`, `globalCss()`, `keyframes()` and `stylex.create()` only accept values known at build time - literals, theme tokens, imported constants and module-level `const`s (templates, arithmetic and `Math.*` calls over them fold). Object, array and enum constants read as if written in place (`css(base)`, `{ ...base, color: 'red' }`, `_hover: hover`, `space[2]`, `Size.M`, `<Box {...base} />`), a later property replacing an earlier one. The build only runs code whose result is certain: `const`s, functions and enums this file declares, computing from literals and constants with exact built-ins (`String`, `Number`, `JSON`, `Object`, `Array`, string/array methods, `Math.abs/ceil/floor/round/trunc/sign/max/min/sqrt/fround/imul/clz32` and `Math` constants) - `const double = (n) => n * 2; css({ w: double(SIZE) })` is static. Imports are only read as the literal, object or array their module declares; code of another module never runs (`darken(0.1, PRIMARY)` with an imported `darken` is not computed). Not run: other globals (`window`, `Date`, `Intl`, ...), `Math.random` and approximate `Math` functions (`sin`, `pow`, ...), `**`, `toString(radix)`, `toLocale*`/`localeCompare`/`normalize`, `this`, `new`, classes, regex, `try`, getters, async/generators, JSX, `obj[key]()`, and functions writing module-level bindings - elements and `styled()` keep such values as CSS variables, while `css()`/`globalCss()`/`keyframes()` report a build error (as do `css()`/`styled()` given a whole style object computed that way). An object, array or enum constant that visible code changes (member assignment, `delete`, `++`, `push`/`sort`, `Object.assign`, changing elements in `for...of`/`forEach`, a method using `this`, passing it to an unknown function) is not a constant: elements read its members at runtime, and `css(obj)`, `styled.div(obj)` or `{...obj}` on an element is a build error naming where it changes. JSX props (except `ref`), `export default`, `module.exports` and `Object.freeze` only read it. Never mutate objects styles read; use theme tokens or props for values that change. A value known only at runtime (a prop, state, a parameter), and styles written where the build cannot read an object (a spread of an unknown object, `_hover={x}`, a computed key), are a **build error**. For dynamic values on custom components, use `<Box as={Component}>`:

```tsx
// WRONG - css() cannot handle dynamic values
Expand Down Expand Up @@ -365,7 +402,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 Expand Up @@ -548,6 +585,7 @@ One rule explains `Dynamic Values = CSS Variables`, `$token Scope` and
|------|--------|
| `<Box color="red" />` | Static class |
| `<Box color={{ 1: "red", 2: "blue" }[v]} />` | Static class per value - **preferred** |
| `<Box color={PRIMARY} />` where `PRIMARY` is a module-level or imported `const` string/number | Static class |
| `<Box color={colors[v]} />` where `colors` is declared elsewhere | CSS variable |
| `<Box color={props.color} />` | CSS variable (genuinely dynamic - correct) |
| `const s = { a: css({ ... }) }` then `className={s[v]}` | Neither - see below |
Expand Down
10 changes: 5 additions & 5 deletions crates/devup-mcp/src/server/skills/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,16 @@
"usedFor": "The TSX devup_figma_export returns is devup-ui code. Without this the agent does not know its components are compile-time placeholders, that $token means devup.json, or that a style prop takes a responsive array.",
"repo": "dev-five-git/devup-ui",
"path": "SKILL.md",
"commit": "c7295916e43657ee357e35f2682b78900c5232c2",
"committedAt": "2026-09-21T14:05:45Z",
"commit": "cebcfb2a3f94649f297315e702dd468026474ff6",
"committedAt": "2026-09-30T02:49:17Z",
"documents": [
{
"path": "SKILL.md",
"bytes": 23101,
"sha256": "12787e610016e90ef9e483bd2b82e3c9312aec4b106fed29f07c816e5b0c7fba"
"bytes": 29525,
"sha256": "cd750ecb6b49340a2cf4c386ef7c69eeff4d495d115f58223b963430da9f87a2"
}
],
"sourceUrl": "https://github.com/dev-five-git/devup-ui/blob/c7295916e43657ee357e35f2682b78900c5232c2/SKILL.md",
"sourceUrl": "https://github.com/dev-five-git/devup-ui/blob/cebcfb2a3f94649f297315e702dd468026474ff6/SKILL.md",
"latestUrl": "https://github.com/dev-five-git/devup-ui/blob/HEAD/SKILL.md"
},
{
Expand Down
Loading