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'scx(a, b)with classes the file binds tocss()styled(Base)extending a styled component the file defines, and.attrs()- vanilla-extract
style([a, b]), StyleXprops(a, b) - JSX spreads followed by explicit props, and the Emotion
cssprop with aclassName
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
classNamepassed 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 (alet, 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: 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:
1function Card({ className, style, children }) { 2 return ( 3 <div className={className} style={style}> 4 {children} 5 </div> 6 ) 7} 8 9const example = <Card css={{ color: 'red', padding: width }} />1function Card({ className, style, children }) { 2 return ( 3 <div className={className} style={style}> 4 {children} 5 </div> 6 ) 7} 8 9const 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,
keyframesname 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()andkeyframes()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(ortheme.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
jsxImportSourcefrom thetsconfig.json(orjsconfig.json) in the working directory. Opt out withimportAliases: { '@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.
See Build Errors for the build errors, grouped by API.