Styling in Folio 2.0
Folio 2.0 applies styles directly to elements in the template or to PHP node builders. The style system is intentionally small and explicit: every value is resolved before layout, so the output is predictable.
Style attributes
Most elements accept these attributes:
| Attribute | Type | Effect |
|---|---|---|
width | length or % | Element width. 100% fills the parent container. |
height | length or % | Element height. |
padding | number | Inner spacing on all sides (points). |
gap | number | Space between children in a column or row. |
background | color hex | Solid background fill. |
color | color hex | Text color. |
fontSize | number | Text size in points. |
fontWeight | bold / normal | Currently maps to Helvetica Bold or normal. |
align | left / right / center / justify | Text alignment. |
grow | number | Flex grow factor for column/row children. |
colspan | integer | Table cell column span. |
Lengths are in points (1/72 inch) unless you append a %, which makes them relative to the available width.
Design tokens and themes
Load a theme with @theme "name". A theme is a JSON file that groups values into categories such as colors, fontSizes, space and radii.
{
"tokens": {
"colors": {
"brand": "#1e3a8a",
"surface": "#f8fafc"
},
"fontSizes": {
"2xl": 20
},
"space": {
"4": 12
}
},
"styles": {
"brand": {
"color": "{colors.brand}",
"fontSize": "{fontSizes.2xl}"
}
}
}Reference tokens in attributes or style blocks with {category.name}:
@theme "modern"
page {
text(class="brand") "Branded heading"
}@style blocks
Add CSS-like rules that apply by class or element type. These are scoped to the document that contains them.
@style {
.hero {
background: {colors.surface};
padding: 24;
color: {colors.brand};
}
heading {
fontWeight: bold;
}
}
page {
column {
heading(class="hero") "Title"
}
}Colors
Folio 2.0 accepts 6-digit hex colors:
text(color="#0f172a") "Dark slate"
text(color="#ef4444") "Red alert"A short palette used across the examples:
#0f172a— dark navy (headers)#334155— slate (sub-headings)#64748b— muted slate (secondary text)#94a3b8— light slate (subtle labels)#f8fafc— near white (card backgrounds)#ffffff— white
Flex layout
column and row are the two flex containers.
columnstacks children vertically.rowplaces children horizontally.gapsets the spacing between children.alignon the container controls cross-axis alignment.growon a child makes it expand to fill leftover space.
row(gap=24) {
column(grow=1) { text "Left side" }
column(grow=1) { text "Right side" }
}Text styling
Text inherits the current color and font size from its parent. You can also set it explicitly:
column(padding=24, gap=8) {
heading(fontSize=20, color="#0f172a") "Title"
text(fontSize=11, color="#64748b") "Body copy in a muted color."
text(fontSize=11, fontWeight="bold") "Emphasised inline text."
}Table styling
Tables accept padding, background and width. Rows accept background, fontSize and fontWeight. Cells accept align, background and colspan.
table(padding=12, background="#f8fafc", width="100%") {
header(background="#0f172a", color="#ffffff", fontSize=10, fontWeight="bold") {
th "Description"
th(align="right") "Qty"
}
tr(background="#ffffff", fontSize=10) {
td "Premium service"
td(align="right") "2"
}
}Style inheritance means fontWeight="bold" on the header row makes each th inside bold.
Complete theme example
A real theme keeps every color, spacing and font-size decision in one JSON file:
{
"name": "pro",
"tokens": {
"colors": {
"brand": "#1e3a8a",
"surface": "#f8fafc",
"muted": "#64748b",
"ink": "#0f172a",
"paper": "#ffffff"
},
"fontSizes": {
"2xl": 26,
"xl": 20,
"lg": 14,
"base": 11,
"sm": 9
},
"space": {
"4": 12,
"8": 24,
"12": 48
},
"radii": {
"md": 4,
"lg": 8
}
},
"styles": {
"brand": {
"color": "{colors.brand}",
"fontSize": "{fontSizes.xl}",
"fontWeight": "bold"
},
"muted": {
"color": "{colors.muted}",
"fontSize": "{fontSizes.sm}"
}
}
}Use it in a template:
@theme "pro"
@style {
.card {
background: {colors.surface};
padding: {space.8};
radius: {radii.lg};
}
.total {
color: {colors.ink};
fontSize: {fontSizes.xl};
fontWeight: bold;
}
}
page(background="{colors.paper}") {
column(width="100%", padding="{space.12}", gap="{space.8}") {
heading(class="brand") "Invoice"
column(class="card") { ... }
text(class="total") total
}
}Style cascade
Folio resolves styles in this order, with later steps overriding earlier ones:
- Inherited values from the parent element.
- Theme named styles for each
classon the element. - Utility classes such as
p-4,rounded-mdandtext-brand. - Styles from the document's
@styleblock that match the element type or class. - Inline attributes on the element (
color="#0f172a",fontSize=12).
This means you can set a default with a class or @style rule and override it with an inline attribute when necessary.
What is not yet supported
PandaCSS-style recipes and slot recipes, gradients, and filters are not yet implemented.