A class, not a style attribute
Every prop on this site is a closed typed set, and that is the feature: a value that typechecks works, and a token cannot drift. The cost is the property nobody has typed yet — mix-blend-mode, scroll-snap-type, a vendor prefix — and the usual way out, style={{ }}, gives up everything else at once: no sharing, no hover, no breakpoint, no theme, and a byte of inline CSS on every element. css is the same style object handed to the engine instead. It becomes a rule, the rule gets a class, and the class is shared by every element writing the same object.
A property with no prop
<Flex ai="center">
<Box width={24} height={24} borderRadius={12} bgColor="sky-400" />
<Box width={24} height={24} borderRadius={12} bgColor="rose-400" translateX={-8} css={{ mixBlendMode: 'multiply' }} />
</Flex>
It nests wherever a prop does
The object is a prop value like any other, so it goes inside hover, a breakpoint, a theme, a dataAttr, a pseudo-element or startingStyle — and each nesting compiles to its own rule with the same selector every typed prop would get. A blend that multiplies on a light surface has to screen on a dark one; the theme decides, not a re-render.
One blend mode per theme
<Flex ai="center">
<Box width={24} height={24} borderRadius={12} bgColor="amber-400" />
<Box
width={24}
height={24}
borderRadius={12}
bgColor="violet-500"
translateX={-8}
theme={{ light: { css: { mixBlendMode: 'multiply' } }, dark: { css: { mixBlendMode: 'screen' } } }}
/>
</Flex>
Line clamp: three declarations, one of them prefixed — and a hover that lets it go
A closed typed set is the feature: a value that typechecks works, a token cannot drift, and every element writing the same value shares one class. The cost is the property nobody has typed yet, and line-clamp is the classic — three declarations, one of them vendor-prefixed, for a paragraph that stops after two lines and picks up an ellipsis. It used to be an inline style or nothing.
<Box
maxWidth={96}
overflow="hidden"
css={{ display: '-webkit-box', WebkitBoxOrient: 'vertical', WebkitLineClamp: 2 }}
hover={{ css: { WebkitLineClamp: 'none' } }}
>
{clampedText}
</Box>
Names are typed, values are CSS
Property names are camelCase, the way React spells them, and they come from csstype — so a misspelt property is a compile error and every name autocompletes. WebkitLineClamp becomes -webkit-line-clamp, msOverflowStyle becomes -ms-overflow-style, and a name already hyphenated, or a --custom one, is written as it stands. Values are CSS written out as they stand — a number too, which is why width: 100 is a type error (that would be width:100, which is not CSS) while zIndex, opacity, flexGrow and lineHeight take theirs. The one thing resolved for you is a colour token: outlineColor: 'sky-500' is var(--sky-500) and 'rose-400/60' is the mix, the same rule a vars value follows, so the hatch stays themed.
The value grammar
<Box
css={{
outlineColor: 'sky-500',
textDecorationColor: 'rose-400/60',
zIndex: 3,
scrollSnapType: 'x mandatory',
WebkitLineClamp: 2,
'--rows': 3,
}}
/>
It is sorted last
The registry declares css after every other prop, so its rule sorts after every typed prop's at the same specificity. On one element, the hatch wins the property both name — it is the override, and the cascade says so rather than the order you wrote the props in.
The generated order
.p-4 { padding: 1rem }
.css-padding-3px_7px { padding: 3px 7px }
The three ways out, in order
A typed prop, if one exists. /tailwind-parity lists every family against the props that cover it; most of what feels missing is a spelling away.
Box.extend() for a property you will use more than once: five lines, and afterwards it is indistinguishable from a built-in — typed, nested, shared, server-rendered.
css for the one-off. It is the 5%, and it is meant to stay that: an ESLint rule that flags or forbids it per team policy is planned, and until it ships the audit is one line.
What it will not do
Write a selector. A key is a property name, never &:hover or > *; the nesting keys — hover, dataAttr, has, group, nth — are how a rule gets a selector, and they wrap css like any prop.
Let a value end the rule. A value carrying ; or a brace, or a name that is not a property name, is dropped — that entry only, not the object. A data URI's ; is written %3B.
Format a number. The dividers belong to the typed props; here 0 is the only number a length takes, and a unitless property takes what you wrote.