Using components

A push that carries a format 0.3 component writes two kinds of file you can use in a page as they are: components.css, the CSS for every 0.3 component, and one HTML snippet per component. This page says how to load them and what to expect from them.

What you get

File What it is
tokens/tokens.css Your tokens as CSS custom properties, one block per mode.
tokens/components.css Every 0.3 component's CSS. It names your tokens with var() and nothing else.
tokens/components/<name>.html The component's markup, and one example per variant.

tokens is your project's token folder. All three files are generated and overwritten by every push. Edit the project in Kampa, or the component documents, never these files.

Because components.css holds only var() references, it is the same for every mode: switching data-theme switches the variables, and the components follow. A token change in Kampa never needs new component CSS.

Loading them

Load tokens.css, then components.css, before your framework's CSS:

<link rel="stylesheet" href="/tokens/tokens.css">
<link rel="stylesheet" href="/tokens/components.css">
<link rel="stylesheet" href="/your-app.css">

components.css starts with @layer kampa.base, kampa.components; and puts every rule in the kampa.components layer. Two things follow:

  • Your own unlayered CSS always wins over Kampa's, whatever its order or specificity. A single class of yours overrides a Kampa rule.
  • Layers are ordered by first appearance. Loaded first, kampa is the lowest layer, and a framework's layers (Tailwind's theme, base, components, utilities) sit above it. Loaded after a framework, Kampa's rules would beat that framework's utilities. Kampa cannot see your import order, so this is the one rule to keep.

To place Kampa against your own layers, name kampa: @layer kampa, mine; puts yours above it, @layer mine, kampa; below.

Every rule is wrapped in :where(), so it has no specificity of its own (pseudo-element rules, such as a checkbox's check, have the specificity of one element). Order decides between Kampa's own rules, the same order the Kampa check grades.

Markup

Copy the element from the snippet. Each component is one element with one class, kampa-<component>, plus one class per variant that is not the default:

<button type="button" class="kampa-button kampa-button--secondary">Save</button>
<label><input type="checkbox" class="kampa-checkbox" checked> Remember me</label>
<label><input type="checkbox" role="switch" class="kampa-switch"> Notifications</label>
<input type="text" class="kampa-input" aria-label="Email" aria-invalid="true">

States are never classes. The browser shows them from the element itself:

State How the element gets it
hover, active, focus-visible The pointer and the keyboard.
checked The checked attribute, or the user.
disabled The disabled attribute. A link has none: remove its href and set role="link" and aria-disabled="true".
invalid aria-invalid="true", which you set when the value is wrong. Kampa does not style :invalid, so an empty required field is not shown as an error before anyone touches it, and an error is never only visual.
open The browser, when a select is open.

No JavaScript is needed for any of them.

The snippets give every form control a name, with a <label> or aria-label. Keep one when you change the markup: the snippet's text is a sample, the name is not optional.

Focus

Kampa's CSS removes the browser's default focus outline and draws the ring the component declares, outside the control with a gap. Every focusable component declares one, and the check grades it at 3:1 against your general surfaces. If you restyle a component, keep a visible ring.

Forced colours

Under Windows High Contrast and other forced-colours modes the browser replaces colours with the user's own. components.css carries a small block for that mode that redraws the checkbox check, the radio dot and the switch thumb with system colours, so checked and unchecked still look different. Those colours are the user's and are not graded.

The starters

A new project starts with eleven components in format 0.3, each with every state its native element has:

Component Element Variants Sizes
Button button primary, secondary, ghost, destructive sm, md, lg
Link a default, subtle
Badge span info, success, warning, error
Input input type="text" sm, md, lg
Textarea textarea sm, md, lg
Select select sm, md, lg
Checkbox input type="checkbox"
Radio input type="radio"
Switch input type="checkbox" role="switch"
Separator hr
Avatar span sm, md, lg

A size is a class like a variant, kampa-button--lg; md is the default and has none. Card and Alert are still format 0.2: they have no snippet and are not in components.css.

They name these tokens, which every template and the generator add:

Token What it is
size.control.xs to size.control.lg Control heights: 20, 32, 40 and 48px. A project generated at the compact density gets 16, 28, 32 and 40px.
opacity.disabled 0.5. A disabled control is shown at this opacity.
color.border.focus The ring: your brand colour, or the next step along its ramp that reaches 3:1 on every general surface, or your text colour.
borderWidth.medium The ring's width and its gap from the control: 2px in every template and every generated project.

A few choices follow from what the check grades:

  • A control's edge identifies it, so it is graded at 3:1 on the page in every state. The starters use color.foreground.muted for it.
  • A link's text is graded on the page as text, at 4.5:1. The starters use color.brand.emphasis for it.
  • A link paints color.background.default behind its text: format 0.3 grades the text of every component on its own background.
  • Destructive buttons keep their colours on hover and press, because no token means "error, hovered". The pointer and the ring still change.

A project made before format 0.3 has Button, Input, Checkbox and Badge in format 0.2. In Kampa, Update replaces them with the 0.3 starters and adds the size.control, opacity.disabled and color.border.focus tokens the project does not have. It never changes a token you already have, borderWidth.medium included. A starter you customised is replaced too, and Kampa asks first. After your next push, the repository has components.css and the snippets.

Starter markup

Each starter's snippet, as generated. A push writes the same markup, with one example per variant, to tokens/components/<name>.html.

<!-- Avatar -->
<span class="kampa-avatar">AB</span>
<!-- Badge -->
<span class="kampa-badge">Badge</span>
<!-- Button -->
<button type="button" class="kampa-button">Button</button>
<!-- Checkbox -->
<label><input type="checkbox" class="kampa-checkbox"> Checkbox</label>
<!-- Input -->
<input type="text" class="kampa-input" aria-label="Input" placeholder="Placeholder">
<!-- Link -->
<a class="kampa-link" href="#">Link</a>
<!-- Radio -->
<label><input type="radio" class="kampa-radio"> Radio</label>
<!-- Select -->
<select class="kampa-select" aria-label="Select"><option>Option 1</option><option>Option 2</option></select>
<!-- Separator -->
<hr class="kampa-separator">
<!-- Switch -->
<label><input type="checkbox" role="switch" class="kampa-switch"> Switch</label>
<!-- Textarea -->
<textarea class="kampa-textarea" aria-label="Textarea" placeholder="Placeholder"></textarea>

The text inside and the placeholder are samples. Replace them, and keep a name on every form control.

A select draws its chevron in its inline-end padding, so components.css keeps 28px there whatever padding-inline a size declares. The text never runs under the chevron.

What is not graded

  • The open list of a select is drawn by the browser. The closed control is graded; the list is not.
  • Surfaces other than your general ones. A border or a focus ring is graded against color.background and color.surface (default, subtle, muted). A component placed on a brand, inverted or card surface is not graded against it.
  • Your own overrides. A rule of yours that changes a colour is not something the Kampa check can see.