A Panda pattern is a function that turns a few typed props into a style object. You import it from @pindoba/styled-system/patterns and call it directly (translucent({ blur: "md" }) returns a class), or spread pattern.raw({...}) into css(), css.raw() or an sva() slot. The pindoba preset defines eight:
| Pattern | For |
|---|---|
translucent | Frosted-glass backdrop blur |
interactiveStates | Hover, active, focus and disabled colors in one call |
sizing | Control typography, padding and radius from the size scale |
controlRow | Height, gap and per-side padding from the Control Var Contract |
panelNest | Make a plain element one level of the concentric radius cascade |
rhythm | Measure a prose slot in baseline lines inside a [data-rhythm] scope |
container | Centered max-width page box (pindoba override of the base preset) |
grid | CSS grid with a token-scale default gap (pindoba override) |
Panda’s other base patterns (stack, flex, hstack, center, …) are still available unchanged.
Use the pattern, not a plain helper. Panda’s extractor ignores spreads of ordinary imported objects. A class name is generated, but the published bundle has no CSS for it. Patterns are a special case: their transform runs at codegen. That’s why controlRow and rhythm are patterns and not functions in a styles package.
A frosted-glass surface: a backdrop blur, with saturation boosted so colors behind the glass stay vivid. You can get it two ways.
css({ translucent: "…" })translucent({ blur })---
import { css } from "@pindoba/styled-system/css";
import { translucent } from "@pindoba/styled-system/patterns";
// Something busy behind the glass, so the blur has edges to soften.
const backdrop = css({
position: "relative",
display: "grid",
placeItems: "center",
minHeight: "120px",
borderRadius: "md",
overflow: "hidden",
padding: "sm",
backgroundColor: "neutral.surface.base",
backgroundImage:
// Soft color fields plus one crisp disc: an edge for the blur to soften,
// without high-contrast stripes that strobe behind the glass.
"radial-gradient(circle at 25% 35%, {colors.warning.accent.surface.base} 0 18%, transparent 19%), radial-gradient(circle at 70% 60%, {colors.primary.accent.surface.hill}, transparent 45%)",
});
const glass = css.raw({
width: "full",
paddingBlock: "sm",
paddingInline: "md",
borderRadius: "sm",
borderWidth: "1px",
borderStyle: "solid",
borderColor: "neutral.border.muted",
color: "neutral.text.bold",
fontFamily: "mono",
fontSize: "xs",
textAlign: "center",
});
const grid = css({
display: "grid",
gridTemplateColumns: "repeat(auto-fit, minmax(140px, 1fr))",
gap: "sm",
width: "full",
});
// Utility: one string value, `none`–`xl`. Each class is a static call so
// Panda extracts every step.
const utilitySteps = [
{
value: "none",
cls: css(glass, { translucent: "none", bg: "neutral.surface.peak/60" }),
},
{
value: "2xs",
cls: css(glass, { translucent: "2xs", bg: "neutral.surface.peak/60" }),
},
{
value: "xs",
cls: css(glass, { translucent: "xs", bg: "neutral.surface.peak/60" }),
},
{
value: "sm",
cls: css(glass, { translucent: "sm", bg: "neutral.surface.peak/60" }),
},
{
value: "md",
cls: css(glass, { translucent: "md", bg: "neutral.surface.peak/60" }),
},
{
value: "lg",
cls: css(glass, { translucent: "lg", bg: "neutral.surface.peak/60" }),
},
{
value: "xl",
cls: css(glass, { translucent: "xl", bg: "neutral.surface.peak/60" }),
},
];
// Pattern: any `blurs` token, saturate 200%. Pass the fill explicitly.
const patternSteps = [
{
label: 'translucent({ blur: "md" })',
cls: css(
glass,
translucent.raw({
blur: "md",
backgroundColor: "neutral.surface.peak/60",
}),
),
},
{
label: 'translucent({ blur: "3xl" })',
cls: css(
glass,
translucent.raw({
blur: "3xl",
backgroundColor: "neutral.surface.peak/60",
}),
),
},
];
const heading = css({
fontSize: "sm",
fontWeight: "medium",
color: "neutral.text.bold",
});
const stack = css({ display: "flex", flexDirection: "column", gap: "sm" });
---
<div class={css({ display: "flex", flexDirection: "column", gap: "lg" })}>
<section class={stack}>
<h4 class={heading}>
Utility: <code>css({ translucent: "…" })</code>
</h4>
<div class={grid}>
{utilitySteps.map((step) => (
<div class={backdrop}>
<div class={step.cls}>{step.value}</div>
</div>
))}
</div>
</section>
<section class={stack}>
<h4 class={heading}>
Pattern: <code>translucent({ blur })</code>
</h4>
<div class={grid}>
{patternSteps.map((step) => (
<div class={backdrop}>
<div class={step.cls}>{step.label}</div>
</div>
))}
</div>
</section>
</div>The translucent utility is a css() property that takes one string, from none and 2xs to xl. It emits backdrop-filter: blur(<token>) saturate(150%) brightness(100%) and nothing else, so set the fill yourself:
import { css } from "@pindoba/styled-system/css";
<div className={css({ translucent: "md", bg: "neutral.surface.peak/40" })}>
Glass card
</div>;
The translucent pattern takes blur (any blurs token, none to 5xl) and emits backdropBlur with backdropSaturate: 200% and backdropBrightness: 100%:
import { translucent } from "@pindoba/styled-system/patterns";
<div
className={translucent({
blur: "lg",
backgroundColor: "neutral.surface.peak/40",
})}
>
Glass card
</div>;
// in a recipe slot
root: css.raw({ ...translucent.raw({ blur: "md" }) });
Always pass backgroundColor to the pattern. Its built-in default, neutral.surface/80, refers to a token that doesn’t exist, so without an explicit fill the background is dropped. Don’t use the pattern with blur: "none": it then sets saturate and brightness to 0, which turns the backdrop black. Use the none utility value, or leave the pattern off.
For a full glass surface with a border, radius and theme-aware fill, use <Panel translucent> (or any panel-based component). It mixes the background step at 35% opacity and adds an lg blur.
interactiveStates combines a resting color pair with _hover, _active, optional _focusVisibleWithin and _disabled blocks. Start from a preset, override individual states, or pass everything yourself:
import { interactiveStates } from "@pindoba/styled-system/patterns";
// preset: colorPalette fill with contrast text
<button className={interactiveStates({ preset: "primary" })}>Save</button>
// preset + one override
<a className={interactiveStates({ preset: "secondary", bgHover: "neutral.surface.hill" })}>Open</a>
// fully custom; any color prop takes a { base, _light, _dark } object
<div
className={interactiveStates({
bgBase: "neutral.surface.peak",
bgHover: "neutral.surface.base",
bgActive: "neutral.surface.ground",
colorBase: "neutral.text.bold",
withFocusOutline: true,
})}
/>
| Prop | Effect |
|---|---|
preset | primary (colorPalette fill), secondary (neutral surfaces) or ghost (secondary on a transparent base) |
bg*: Base, Hover, Active, Disabled | Background per state; overrides the preset |
color*: Base, Hover, Active, Disabled | Text color per state; overrides the preset |
withFocusOutline | Focus ring on _focusVisibleWithin (default false) |
withDisabledFilter | Grayscale + disabled opacity when disabled (default true) |
sizing maps a size (xs to xl) to the control typography (the control.<size> text style), inline and block padding (control.padX/padY.<size> spacing tokens) and the corner radius (control.<size> radii token). All of these tokens are generated from SIZE_PRESETS, so the pattern holds no table of its own. Any of fontSize, lineHeight, px, py, minHeight and borderRadius can be overridden.
import { sizing } from "@pindoba/styled-system/patterns";
css({ ...sizing.raw({ size: "md" }) });
css({ ...sizing.raw({ size: "md", px: "sm" }) }); // tighter inline padding
| size | text style | padding | radius · font |
|---|---|---|---|
| xs | control.xs | px xs NaNpx · py 3xs NaNpx | radius sm NaNpx · font xs |
| sm | control.sm | px sm NaNpx · py 2xs NaNpx | radius sm NaNpx · font xs |
| md | control.md | px md NaNpx · py xs NaNpx | radius md NaNpx · font sm |
| lg | control.lg | px xl NaNpx · py sm NaNpx | radius lg NaNpx · font md |
| xl | control.xl | px 3xl NaNpx · py md NaNpx | radius xl NaNpx · font lg |
A fixed-height control row passes inlinePad: false and spreads controlRow next to it, so the contract owns inline padding and height. See Sizing & Controls.
controlRow takes no props. It reads the Control Var Contract variables that the preset publishes on [data-control-size]: height: var(--control-h), gap: var(--control-gap), and padding-inline-start/end from --control-pad-start/end, which change per side when an <Affix> is present.
import { sizing, controlRow } from "@pindoba/styled-system/patterns";
size: {
md: {
root: css.raw({
...sizing.raw({ size: "md", inlinePad: false }),
...controlRow.raw({}),
}),
},
},
The connect function must put data-control-size="md" on the same root. Retune the input variables (--control-pad-x-base, --control-gap, …), never --control-pad-start/end. The full model, with a live explorer, is on Sizing & Controls.
radius="inner" subtracts a panel’s padding to keep nested corners concentric. Padding from a plain <div>, <ul> or grid cell isn’t counted. panelNest makes such an element a real nesting level. It needs the data-panel-nest attribute as well, because the cascade rules are keyed on it.
<div data-panel-nest class={panelNest({ padding: "md" })}>
<Panel radius="inner">…</Panel>
<!-- corner = parent − md -->
</div>
Props: padding (spacing token, also emitted as padding unless setPadding: false) and radius (this element’s own corner; if omitted, it inherits the chain). Prefer a real <Panel radius="inner" background="transparent" border="none"> when you control the markup.
rhythm measures a recipe slot in baseline lines, but only inside a [data-rhythm] scope. Outside one it emits nothing. Counts are lines, not tokens, and 0.5 resolves to a half line.
import { rhythm } from "@pindoba/styled-system/patterns";
root: css.raw({ ...rhythm.raw({ padBlock: 1, padInline: 1, borderW: "1px" }) }),
title: css.raw({ ...rhythm.raw({ text: true }) }), // quantise leading
header: css.raw({ ...rhythm.raw({ lines: 1 }) }), // fixed one-line row
Props: text, textLines, lines, minLines, spaceBefore, padBlock, padInline, gap (block axis only), borderW (subtracted so the border box lands on the raster) and panelPad (republishes the padding to the radius cascade; on by default). Use it on prose surfaces such as panels, cards, banners and alerts, never on a control row: the row already gets its height from --control-h. See Raster.
The base preset’s container pattern with values that work against pindoba’s tokens: maxWidth: breakpoint-2xl and paddingInline: { base: "md", md: "xl" }. Upstream’s values (px: 4/6/8, 8xl) don’t resolve with pindoba’s t-shirt spacing or its 10px root. For page layout, prefer <Container> from the grid packages, which also publishes the raster margins.
import { container } from "@pindoba/styled-system/patterns";
<main className={container()}>…</main>;
The base preset’s grid pattern (columns, minChildWidth, gap, columnGap, rowGap), except that the default gap is the md token instead of a raw 8px. For layouts on the modular grid, use <Grid> from the grid packages.
import { grid } from "@pindoba/styled-system/patterns";
<ul className={grid({ minChildWidth: "xs" })}>…</ul>;
<div className={grid({ columns: 3, gap: "lg" })}>…</div>;
translucent sparingly. backdrop-filter is GPU-intensive, so check it on low-end devices.<Panel translucent>, size) over spreading a pattern by hand in app code. Patterns are mainly building blocks for recipes..raw() inside css.raw()/sva(). Never spread a plain helper object, because the extractor drops it.sizing emits token names instead of reading SIZE_PRESETS.