Components
Checkbox
An independent yes or no, on its own or in a list where several can be true.
Use it wherever options do not exclude each other.
Watch out. If exactly one option can be chosen, that is a Radio.
Sub-brandTheme
Liverendered from the shipped recipe
Size
s default
m
States
unchecked
hover
checked
error
disabled
Code
The HTML for the live example above is on the page - use your browser's inspector, or copy the CSS here and the markup from the preview. Load post-ui.css once per site and this component works anywhere.
/* ============================================================================
POST UI System — components
HAND-WRITTEN. Not generated: this file encodes the RULES the components follow,
which is not the same thing as whatever the Figma canvas currently looks like.
The contract it implements (variant axes + reasoning) is synced in components.json.
Import order: tokens.css -> type.css -> components.css
NAMING. Every class is `post-` prefixed. This ships into Webflow projects where
`.button`, `.field`, `.label` or `.text-body` are exactly the classes a site
already has, and an unprefixed collision would silently restyle real content.
Block `post-x` · modifier `post-x--y` · state `is-*`.
THE FOUR RULES EVERYTHING BELOW OBEYS
1. Tokens are named by scale, never by consumer — space/m, stroke/m, icon-size/m
and text/md all mean "the middle step".
2. Strokes are altitude, not component — s detail · m component · l section.
3. Optical padding — outer padding is uniform, the LABEL carries space/xs left and
right, and the gap is 0. Text ends up inset by outer+xs while an icon sits at
outer, which is correct: a glyph carries internal whitespace and text does not.
One recipe covers icon-left, icon-right, both, or neither.
4. Control height comes from the ICON TIER, never from text. Every control label
carries min-height = its icon-size token. Without it the icon becomes the
tallest child and toggling an optional icon grows the control — a chip jumped
28 -> 32px exactly this way. With it, toggling an icon changes width only.
currentColor is load-bearing. Outline/ghost controls bind their label fill AND
their stroke to the same `action` token, so the border is written as
`border-color: currentColor` and one colour change moves both.
============================================================================ */
/* ---- checkbox ---- */
/* A SHARP square — this is where the brand's sharp-corner rule pays off, because a
square checkbox is the conventional form anyway. Size m is the form-primary one,
sized to sit beside Input fields. */
.post-check { display: inline-flex; align-items: flex-start; gap: var(--space-s); cursor: pointer; }
.post-check__box {
flex: 0 0 auto;
display: inline-flex;
align-items: center;
justify-content: center;
width: var(--icon-size-s);
height: var(--icon-size-s);
border: var(--stroke-m) solid var(--border);
background: var(--surface);
color: transparent;
}
.post-check__box svg { width: 100%; height: 100%; fill: currentColor; }
.post-check__label {
font-family: var(--font-post);
font-size: var(--text-sm);
font-weight: 500;
line-height: var(--icon-size-s);
color: var(--text);
}
.post-check--m .post-check__box { width: var(--icon-size-m); height: var(--icon-size-m); }
.post-check--m .post-check__label { font-size: var(--text-md); line-height: var(--icon-size-m); }
.post-check:hover .post-check__box,
.post-check.is-hover .post-check__box { background: var(--hover-surface); border-color: var(--hover-ink); }
.post-check.is-checked .post-check__box {
background: var(--action);
border-color: var(--action);
color: var(--on-action);
}
.post-check.is-error .post-check__box { border-color: var(--feedback-error-text); background: var(--feedback-error-surface); }
.post-check.is-disabled { opacity: 0.4; cursor: not-allowed; }
.post-check input { position: absolute; opacity: 0; width: 0; height: 0; }
.post-check input:focus-visible + .post-check__box { outline: 2px solid var(--action); outline-offset: 2px; }