Style Properties Reference
Every node (except slot) accepts a style object that defines its visual appearance. Styles are written in JSON using camelCase CSS property names and are compiled to scoped CSS classes during the static build.
Style Object Format
The style object uses responsive keys. Only base is required. The tablet and mobile keys override specific properties at their respective breakpoints.
{
"style": {
"base": {
"padding": "24px",
"backgroundColor": "var(--background)",
"fontSize": "16px",
"display": "flex",
"flexDirection": "column",
"gap": "12px"
},
"tablet": {
"padding": "16px",
"fontSize": "14px"
},
"mobile": {
"padding": "12px",
"fontSize": "13px",
"flexDirection": "column"
}
}
}Breakpoint names and widths are defined in project.config.json under breakpoints. The default configuration is:
Breakpoint | Max Width |
|---|---|
| 1024px |
| 540px |
Properties not overridden in tablet or mobile inherit from base.
CSS Property Names
All CSS property names use camelCase. Common properties:
JSON Key | CSS Property |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Values are always strings (even numeric values like zIndex):
{
"style": {
"base": {
"zIndex": "10",
"opacity": "0.8",
"flexGrow": "1"
}
}
}Colors
Always use CSS custom properties from colors.json with the var() function. Raw hex values do not integrate with the editor's color system.
{
"style": {
"base": {
"color": "var(--text-primary)",
"backgroundColor": "var(--surface)",
"borderColor": "var(--border)"
}
}
}Responsive Scales
When responsiveScales is enabled in project.config.json, certain property types are automatically scaled down at smaller breakpoints. This means you only need to define base values for many properties.
Scaled property types and their default multipliers:
Property Type | Tablet | Mobile |
|---|---|---|
| 0.8x | 0.7x |
| 0.75x | 0.5x |
| 0.7x | 0.45x |
| 0.65x | 0.4x |
Explicit tablet or mobile overrides take precedence over auto-scaling.
Style Mappings
Style mappings let prop values control CSS properties. Use _mapping objects in place of static values.
{
"style": {
"base": {
"padding": "16px",
"backgroundColor": {
"_mapping": true,
"prop": "variant",
"values": {
"primary": "var(--primary)",
"secondary": "var(--secondary)",
"ghost": "transparent"
}
},
"fontSize": {
"_mapping": true,
"prop": "size",
"values": {
"small": "14px",
"medium": "16px",
"large": "20px"
}
}
}
}
}Mapping Object Properties
Field | Type | Description | |
|---|---|---|---|
|
| Required marker | |
|
| Interface prop name to read the value from | |
| `Record<string, string \ | number>` | Map from prop values to CSS values |
The rendered CSS uses the value from values that matches the current prop value. If no match is found, the property is omitted.
Interactive Styles
Interactive styles define CSS rules for pseudo-states (:hover, :focus, :active) and conditional class selectors. They are an array of rule objects on the interactiveStyles property.
{
"type": "node",
"tag": "button",
"style": {
"base": { "backgroundColor": "var(--primary)", "color": "var(--on-primary)" }
},
"interactiveStyles": [
{
"name": "onHover",
"postfix": ":hover",
"style": { "base": { "opacity": "0.8", "transform": "translateY(-1px)" } }
},
{
"name": "onFocus",
"postfix": ":focus-visible",
"style": { "base": { "outline": "2px solid var(--focus-ring)", "outlineOffset": "2px" } }
},
{
"name": "onActive",
"postfix": ":active",
"style": { "base": { "transform": "translateY(0)" } }
}
]
}Interactive Style Rule Properties
Field | Type | Required | Description |
|---|---|---|---|
|
| No | Friendly name for the rule (displayed in editor) |
|
| No | CSS selector prefix before the element class |
|
| No | CSS selector postfix after the element class |
|
| Yes | Responsive style object applied when selector matches |
|
| No | Boolean prop name that toggles preview in the editor |
Selector Pattern
The generated CSS follows the pattern: {prefix}.element-class{postfix} { ... }
Examples of prefix/postfix combinations:
prefix | postfix | Generated Selector | Use Case |
|---|---|---|---|
(none) |
|
| Hover state |
(none) |
|
| Keyboard focus |
(none) |
|
| Active class toggle |
|
|
| Dark mode hover |
|
|
| Dark mode active |
Note the trailing space in prefix values like ".dark " -- this creates a descendant selector.
Conditional Rendering (if)
The if property controls whether a node is rendered. It supports three formats.
Static Boolean
{
"type": "node",
"tag": "div",
"if": false,
"children": "This node will not render."
}Prop-Based Mapping
{
"type": "node",
"tag": "div",
"if": {
"_mapping": true,
"prop": "showBanner",
"values": { "true": true, "false": false }
},
"children": "Conditionally visible based on showBanner prop."
}Template Expression
{
"type": "node",
"tag": "div",
"if": "{{showPromo}}",
"children": "Rendered when showPromo is truthy."
}In list contexts, item fields can be used:
{
"type": "node",
"tag": "span",
"if": "{{item.featured}}",
"children": "Featured"
}generateElementClass
Set generateElementClass: true to output a scoped class name on the element without attaching any styles. This is useful when you need a stable class target for custom CSS or JavaScript.
{
"type": "node",
"tag": "div",
"generateElementClass": true,
"children": "Has a class but no inline styles."
}Complete Example
A card component structure demonstrating all style features:
{
"type": "node",
"tag": "article",
"style": {
"base": {
"padding": "24px",
"borderRadius": "12px",
"backgroundColor": "var(--surface)",
"boxShadow": {
"_mapping": true,
"prop": "elevated",
"values": {
"true": "0 4px 12px rgba(0,0,0,0.1)",
"false": "none"
}
},
"border": "1px solid var(--border)"
},
"tablet": {
"padding": "16px",
"borderRadius": "8px"
},
"mobile": {
"padding": "12px"
}
},
"interactiveStyles": [
{
"name": "hover",
"postfix": ":hover",
"style": {
"base": {
"borderColor": "var(--primary)",
"transform": "translateY(-2px)",
"boxShadow": "0 8px 24px rgba(0,0,0,0.12)"
}
}
}
],
"children": [
{
"type": "node",
"tag": "h3",
"style": { "base": { "fontSize": "20px", "fontWeight": "600", "margin": "0 0 8px 0" } },
"children": "{{title}}"
},
{
"type": "node",
"tag": "p",
"if": {
"_mapping": true,
"prop": "showDescription",
"values": { "true": true, "false": false }
},
"style": { "base": { "color": "var(--text-secondary)", "fontSize": "14px" } },
"children": "{{description}}"
}
]
}Next Steps
Node Types -- which node types support styles
Component Props -- defining props that feed into style mappings
Project Config -- breakpoints and responsive scale configuration
Template Expressions -- using expressions in if conditions