css-in-props
v3.14.8
Published
Utilize props as CSS methods
Readme
css-in-props
CSS properties as component props for DOMQL elements. Transforms design-system-aware props into CSS classes via the atomic CSS engine (@symbo.ls/css).
What it does
- Transforms component props (
theme,color,background,border,shadow, etc.) into resolved CSS - Resolves design system tokens (colors, spacing, typography, themes) from
@symbo.ls/scratch - Handles media queries (
@dark,@mobileS, etc.) and pseudo selectors (:hover,:focus) as prop prefixes - Generates atomic CSS classes for optimized rendering (one class per property-value pair)
Theme prop
The theme prop resolves theme definitions into CSS variables. Theme switching is handled entirely by CSS — no DOMQL re-renders needed.
const Card = {
theme: 'primary', // uses --theme-primary-* CSS vars
// themeModifier: 'dark', // optional: force a specific scheme on this component
}When globalTheme is 'auto' (default), CSS variables switch automatically via prefers-color-scheme media queries and [data-theme] selectors.
Props reference
| Category | Props |
|----------|-------|
| Theme | theme, color, background, backgroundColor, borderColor |
| Border | border, borderLeft, borderTop, borderRight, borderBottom, outline |
| Shadow | shadow, boxShadow, textShadow |
| Text | textStroke |
| Image | backgroundImage |
| Layout | outlineOffset |
Media and selector props
Props can be prefixed with media queries or selectors:
const Button = {
background: 'blue',
':hover': { background: 'darkblue' },
'@mobileS': { padding: 'A' },
'.active': { background: 'green' }
}transformersByPrefix
The prefix-to-handler registry that powers media queries, selectors, conditionals, and variables. Each key is a single-character prefix that triggers a specific transformer when found at the start of a prop key:
| Prefix | Handler | Example |
|--------|---------|---------|
| @ | Media query | @mobileS, @dark, @print |
| : | Pseudo selector | :hover, :focus, :first-child |
| [ | Attribute selector | [disabled], [data-active] |
| > | Child combinator | > .child |
| & | Self selector | &.active |
| $ | Global case (from context.cases) | $isSafari |
| . | Truthy conditional (props/state, then context.cases) | .visible |
| ! | Falsy conditional (props/state, then context.cases) | !hidden |
| - | CSS variable | --my-var |
| *, +, ~ | CSS combinators | * div, + .sibling, ~ .general |
import { transformersByPrefix } from 'css-in-props'Interaction with the define system
The $ prefix is used both by css-in-props ($isActive case conditional) and by the define system (e.g. $router). The framework resolves this by checking for define handlers before applying prefix rules. Keys with matching define handlers stay at the element root; only $-prefixed keys without define handlers are processed by css-in-props.
In v3.14, properties go directly on the element (no
props:wrapper). The CSS engine processes design token properties internally.
Global Cases
Cases are defined in symbols/cases.js and added to context.cases (not designSystem). Case functions receive the element as this and as the first argument, but must also work without element context (arrow functions).
// symbols/cases.js
export default {
isSafari: () => /Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent),
isGeorgian () { return this?.state?.root?.language === 'ka' },
isMobile: () => window.innerWidth < 768
}// symbols/context.js
import cases from './cases.js'
export default { cases, /* ...other context */ }Resolution order
$prefix: Checkscontext.cases[key]first (call if function, check truthiness if value). Falls back toelement[key]..prefix: Checkselement[key]/element.state[key]first. Falls back tocontext.cases[key].!prefix: Same as.but inverted — applies when condition is falsy.
const Button = {
padding: 'A',
'$isSafari': { padding: 'B' }, // global case
'.isActive': { background: 'blue' }, // props/state, then cases
'!isMobile': { maxWidth: '1200px' } // inverted
}CSS Variable Resolution
String values starting with -- are automatically wrapped in var() for all CSS properties:
const Box = {
padding: '--my-gap', // → var(--my-gap)
fontSize: '--base-size', // → var(--base-size)
}