|

350 stars
28 forks
Rust
31 views

SKILL.md


name: devup-ui description: | Zero-runtime CSS-in-JS preprocessor for React. Transforms JSX styles to static CSS at build time.

TRIGGER WHEN:

  • Writing/modifying Devup UI components (Box, Flex, Grid, Text, Button, etc.)
  • Using styling APIs: css(), styled(), globalCss(), keyframes()
  • Configuring devup.json theme (colors, typography)
  • Setting up build plugins (Vite, Next.js, Webpack, Rsbuild, Bun)
  • Debugging "Cannot run on the runtime" errors
  • Working with responsive arrays or pseudo-selectors (_hover, _dark, etc.)

Devup UI

Build-time CSS extraction. No runtime JS for styling.

Critical: Components Are Compile-Time Only

All @devup-ui/react components (Box, Flex, Text, etc.) throw Error('Cannot run on the runtime'). They are placeholders that build plugins transform to <div className="...">.

// BEFORE BUILD (what you write):
<Box bg="red" p={4} _hover={{ bg: "blue" }} />

// AFTER BUILD (what runs in browser):
<div className="a b c" />  // + CSS: .a{background:red} .b{padding:16px} .c:hover{background:blue}

Style Prop Syntax

Shorthands (ALWAYS use these)

Short Full Short Full
bg background m, mt, mr, mb, ml, mx, my margin-*
p, pt, pr, pb, pl, px, py padding-* w, h width, height
minW, maxW, minH, maxH min/max width/height boxSize width + height (same value)
gap gap

Spacing Scale (× 4 = px)

<Box p={1} />    // padding: 4px
<Box p={4} />    // padding: 16px
<Box p="4" />    // padding: 16px (unitless string also × 4)
<Box p="20px" /> // padding: 20px (with unit = exact value)

Responsive Arrays (5 breakpoints)

// [mobile, mid, tablet, mid, PC] - 5 levels
// Use indices 0, 2, 4 most frequently. Use null to skip.

<Box bg={["red", null, "blue", null, "yellow"]} />  // mobile=red, tablet=blue, PC=yellow
<Box p={[2, null, 4, null, 6]} />                   // mobile=8px, tablet=16px, PC=24px
<Box w={["100%", null, "50%"]} />                   // mobile=100%, tablet+=50%

Pseudo-Selectors (underscore prefix)

<Box
  _hover={{ bg: "blue" }}
  _focus={{ outline: "2px solid blue" }}
  _active={{ bg: "darkblue" }}
  _dark={{ bg: "gray.800" }}   // theme variant
  _before={{ content: '""' }}
  _firstChild={{ mt: 0 }}
/>

Dynamic Values = CSS Variables

// Static value -> class
<Box bg="red" />  // className="a" + .a{background:red}

// Dynamic value -> CSS variable
<Box bg={props.color} />  // className="a" style={{"--a":props.color}} + .a{background:var(--a)}

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

Dynamic Values with Custom Components

css() only accepts static values (extracted at build time). For dynamic values on custom components, use <Box as={Component}>:

// WRONG - css() cannot handle dynamic values
const MyComponent = ({ width }) => (
  <CustomComponent className={css({ w: width })} />  // ERROR: width is dynamic!
);

// CORRECT - use Box with `as` prop for dynamic values
const MyComponent = ({ width }) => (
  <Box as={CustomComponent} w={width} />  // Works: generates CSS variable
);

Styling APIs

css() Returns className String (NOT object)

import { css, styled, globalCss, keyframes } from "@devup-ui/react";
import clsx from "clsx";

// css() returns a className STRING - use with className prop
const cardStyle = css({ bg: "white", p: 4, borderRadius: "8px" });
<Box className={cardStyle} />  // CORRECT

// WRONG - css() is NOT an object to spread
// <Box {...cardStyle} />  // ERROR!

// Combine multiple styles with clsx
const baseStyle = css({ p: 4, borderRadius: "8px" });
const activeStyle = css({ bg: "$primary", color: "white" });
<Box className={clsx(baseStyle, isActive && activeStyle)} styleOrder={1} />

// styleOrder={1} REQUIRED when mixing className with direct props
<Box className={cardStyle} bg="$background" styleOrder={1} />

styled() API

// Styled component (familiar styled-components/Emotion API)
const Card = styled("div", { bg: "white", p: 4, _hover: { shadow: "lg" } });

globalCss() and keyframes()

// Global styles
globalCss({ body: { margin: 0 }, "*": { boxSizing: "border-box" } });

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

Theme (devup.json)

{
  "theme": {
    "colors": {
      "default": { "primary": "#0070f3", "text": "#000" },
      "dark": { "primary": "#3291ff", "text": "#fff" }
    },
    "typography": {
      "heading": { "fontFamily": "Pretendard", "fontSize": "24px", "fontWeight": 700 }
    }
  }
}

Use colors with $ prefix: <Box color="$primary" /> Use typography without prefix: <Box typography="heading" />

Theme API:

import { useTheme, setTheme, getTheme, initTheme, ThemeScript } from "@devup-ui/react";
setTheme("dark");        // switch theme
const theme = useTheme(); // hook for current theme
<ThemeScript />          // SSR hydration (add to <head>)

Build Plugin Setup

// vite.config.ts
import DevupUI from "@devup-ui/vite-plugin";
export default defineConfig({ plugins: [react(), DevupUI()] });

// next.config.ts
import { DevupUI } from "@devup-ui/next-plugin";
export default DevupUI({
  // Next.js config here
});

// rsbuild.config.ts
import DevupUI from "@devup-ui/rsbuild-plugin";
export default defineConfig({ plugins: [DevupUI()] });

Options:

  • singleCss: true - single CSS file (recommended for Turbopack)
  • include: ["@devup/hello"] - process external libraries that use @devup-ui internally
// When using external library that uses @devup-ui (e.g. @devup/hello)
DevupUI({ include: ["@devup/hello"] })  // required to extract and merge their styles

$color Token Scope

$color tokens only work in JSX props. Use var(--color) in external objects.

// CORRECT - $color in JSX prop
<Box bg="$primary" />
<Box bg={{ active: '$primary', inactive: '$gray' }[status]} />  // inline object OK

// WRONG - $color in external object (won't be transformed)
const colors = { active: '$primary' }  // '$primary' stays as string literal
<Box bg={colors.active} />  // broken!

// CORRECT - var(--color) in external object
const colors = { active: 'var(--primary)' }
<Box bg={colors.active} />  // works

Inline Variant Pattern (Preferred)

Use inline object indexing instead of external config objects:

// PREFERRED - inline object indexing
<Box
  h={{ lg: '48px', md: '40px', sm: '32px' }[size]}
  bg={{ primary: '$primary', secondary: '$gray100' }[variant]}
/>

// AVOID - external config object
const sizeStyles = { lg: { h: '48px' }, md: { h: '40px' } }
<Box h={sizeStyles[size].h} />  // unnecessary indirection

Anti-Patterns (NEVER do)

Wrong Right Why
<Box style={{ color: "red" }}> <Box color="red"> style prop bypasses extraction
<Box {...css({...})} /> <Box className={css({...})} /> css() returns string, not object
css({ bg: variable }) <Box bg={variable}> or <Box as={Comp} bg={variable}> css()/globalCss() only accept static values
$color in external object var(--color) in external object $color only transformed in JSX props
No build plugin configured Configure plugin first Components throw at runtime without transformation
as any on style props Fix types properly Type errors indicate real issues

README

Devup UI logo

The Future of CSS-in-JS — Zero Runtime, Full Power

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



English | 한국어

Why Devup UI?

Devup UI isn't just another CSS-in-JS library — it's the next evolution.

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
  • 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
  • Fastest Build Times: Rust + WebAssembly delivers unmatched preprocessing speed

Install

npm install @devup-ui/react

# on next.js
npm install @devup-ui/next-plugin

# on vite
npm install @devup-ui/vite-plugin

# on rsbuild
npm install @devup-ui/rsbuild-plugin

# on webpack
npm install @devup-ui/webpack-plugin

Features

  • Preprocessor — All CSS extraction happens at build time
  • Zero Config — Works out of the box with sensible defaults
  • Zero FOUC — No flash of unstyled content, no Provider required
  • Zero Runtime — No client-side JavaScript for styling
  • RSC Support — Full React Server Components compatibility
  • Library Mode — Build component libraries with extracted styles
  • Dynamic Themes — Zero-cost theme switching via CSS variables
  • Type-Safe Themes — Full TypeScript support for theme tokens
  • Smallest & Fastest — Proven by benchmarks

Comparison Benchmarks

Next.js Build Time and Build Size (github action - ubuntu-latest)

Library Version Build Time Build Size
tailwindcss 4.1.13 19.31s 59,521,539 bytes
styleX 0.15.4 41.78s 86,869,452 bytes
vanilla-extract 1.17.4 19.50s 61,494,033 bytes
kuma-ui 1.5.9 20.93s 69,924,179 bytes
panda-css 1.3.1 20.64s 64,573,260 bytes
chakra-ui 3.27.0 28.81s 222,435,802 bytes
mui 7.3.2 20.86s 97,964,458 bytes
devup-ui(per-file css) 1.0.18 16.90s 59,540,459 bytes
devup-ui(single css) 1.0.18 17.05s 59,520,196 bytes
tailwindcss(turbopack) 4.1.13 6.72s 5,355,082 bytes
devup-ui(single css+turbopack) 1.0.18 10.34s 4,772,050 bytes

How it works

Devup UI transforms your components at build time. Class names are generated using a compact base-37 encoding for minimal CSS size.

Basic transformation:

// You write:
const variable = <Box _hover={{ bg: 'blue' }} bg="red" p={4} />

// Devup UI generates:
const variable = <div className="a b c" />

// With CSS:
// .a { background-color: red; }
// .b { padding: 1rem; }
// .c:hover { background-color: blue; }

Dynamic values become CSS variables:

// You write:
const example = <Box bg={colorVariable} />

// Devup UI generates:
const example = <div className="a" style={{ '--a': colorVariable }} />

// With CSS:
// .a { background-color: var(--a); }

Complex expressions and responsive arrays — fully supported:

// You write:
const example = <Box bg={['red', 'blue', isActive ? 'green' : dynamicColor]} />

// Devup UI generates:
const example = (
  <div
    className={`a b ${isActive ? 'c' : 'd'}`}
    style={{ '--d': dynamicColor }}
  />
)

// With responsive CSS for each breakpoint

Type-safe theming:

devup.json

{
  "theme": {
    "colors": {
      "default": {
        "primary": "#0070f3",
        "text": "#000"
      },
      "dark": {
        "primary": "#3291ff",
        "text": "#fff"
      }
    },
    "typography": {
      "heading": {
        "fontFamily": "Pretendard",
        "fontSize": "24px",
        "fontWeight": 700,
        "lineHeight": 1.3
      }
    }
  }
}
// Type-safe theme tokens
const textExample = <Text color="$primary" />
const boxExample = <Box typography="$heading" />

Responsive + Pseudo selectors together:

// Responsive with pseudo selector
const example = <Box _hover={{ bg: ['red', 'blue'] }} />

// Equivalent syntax
const example2 = <Box _hover={[{ bg: 'red' }, { bg: 'blue' }]} />

styled-components / Emotion compatible styled() API:

import { styled } from '@devup-ui/react'

// Familiar syntax for styled-components and Emotion users
const Card = styled('div', {
  bg: 'white',
  p: 4, // 4 * 4 = 16px
  borderRadius: '8px',
  boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)',
  _hover: {
    boxShadow: '0 10px 15px rgba(0, 0, 0, 0.1)',
  },
})

const Button = styled('button', {
  px: 4, // 4 * 4 = 16px
  py: 2, // 2 * 4 = 8px
  borderRadius: '4px',
  cursor: 'pointer',
})

// Usage
const cardExample = <Card>Content</Card>
const buttonExample = <Button>Click me</Button>

Inspirations

  • Styled System
  • Chakra UI
  • Theme UI
  • Vanilla Extract
  • Rainbow Sprinkles
  • Kuma UI

How to Contribute

Requirements

Development Setup

To set up the development environment, install the following packages:

bun install
bun run build
cargo install cargo-tarpaulin
cargo install wasm-pack

After installation, run bun run test to ensure everything works correctly.