---
title: Prop Naming Conventions
description: The four semantic-modifier prop families — intent, surface, variant, tone — and how to tell which one a component means.
visibility: guest
---

# Prop Naming Conventions

WireKit components carry several prop names for what looks at first glance like the same concept — "which color, which shape, how much emphasis?". This page is the contract behind each name, so that a developer reading API tables across the catalog can tell them apart, and so that the answer to "which prop do I reach for here?" is a lookup rather than a guess.

One thing to know before the table: **`variant` does not mean the same thing on every component.** On most it picks a visual treatment. On a handful it is an older spelling of the color axis and is kept working on purpose. The [alias matrix](#the-alias-matrix) below names every component in that second group.

## The four families

| Prop | Semantic | Values | Examples |
| --- | --- | --- | --- |
| `intent` | **Semantic color identity.** What is this thing FOR — is it the primary action, a warning, a success? | A shared vocabulary, applied per component — see [below](#the-shared-vocabulary-is-a-vocabulary-not-one-fixed-enum) | `button`, `badge`, `alert`, `callout`, `stat` |
| `surface` | **How that color is applied.** Same intent, different weight of ink: solid fill, outline, soft tint, ghost. | `filled`, `outline`, `soft`, `ghost`, `link` | `button`, `badge`, `theme-controller` |
| `variant` | **Visual mode.** Same content, different surface treatment or layout register. | Family-specific (`outlined` / `elevated` / `flat` on `card`; `default` / `dark` / `accent` / `muted` on marketing surfaces) | `card`, `hero`, `cta`, `link`, `tabs` |
| `tone` | **Qualitative emphasis.** How loud should this read? | Per component | `feature` |

The Examples column is a handful of representative components, not the catalog — many more carry each prop. The API table on a component's own page is the authority for that component.

`intent` and `surface` are the two axes that compose: `intent` picks the color family, `surface` picks how much of it lands on the element. `<x-wirekit::button intent="danger" surface="outline">` is a destructive action rendered as an outline rather than a solid fill — two independent choices, not seven pre-blended ones.

## The alias matrix

On these components `variant` is a **back-compat alias of `intent`**. Both set the same axis, both validate against the same value set, and `intent` wins when a caller supplies both:

| Component | `variant` sets | Prefer |
| --- | --- | --- |
| `alert` | the color axis | `intent` |
| `callout` | the color axis | `intent` |
| `text` | the color axis | `intent` |
| `progress` | the color axis | `intent` |
| `progress.circle` | the color axis | `intent` |
| `reading-progress` | the color axis | `intent` |
| `timeline.item` | the color axis | `intent` |

On every other component, `variant` is the visual-mode family from the table above and is **not** an alias of anything.

Why the alias exists: `intent` is what `button` and `badge` call this axis, so it is what a developer moving between components reaches for. Adding it to these components without breaking the code already written meant accepting both. Nothing about it is deprecated in v2 — see [Where this is heading](#where-this-is-heading) for the v3 position.

## The shared vocabulary is a vocabulary, not one fixed enum

`intent` draws from a shared set of role names — `primary`, `neutral`, `info`, `success`, `warning`, `danger`, plus `accent` on the components whose design calls for it. **Which of those a given component accepts is that component's own decision, and the values are validated per component:**

| Component | Accepts |
| --- | --- |
| `button` | `primary`, `neutral`, `info`, `success`, `warning`, `danger` |
| `badge`, `stat` | the same, plus `accent` |
| `alert`, `callout` | `primary`, `neutral`, `info`, `success`, `warning`, `danger` |
| `text` | `default`, `muted`, `subtle`, `accent`, `success`, `warning`, `danger` |

`text` is the instructive one. Its color axis is a typographic scale rather than a severity set, so it has no `primary` — and an `intent` spelling it does not carry fails validation rather than quietly rendering the default. Loud beats a silent fallback: a heading that came out in the wrong color with no error is the failure nobody notices.

The practical consequence: copying `intent="primary"` from a button onto a `text` is not a safe move, and neither is a blanket find-and-replace across a codebase. Check the component's own API table.

## When you are authoring a component

1. **Reach for `intent`** when the modifier maps to a semantic state (warn / success / danger). Draw from the shared role names above rather than inventing synonyms.
2. **Reach for `surface`** when the modifier picks how heavily that color is applied — fill, outline, soft, ghost.
3. **Reach for `variant`** when the modifier picks a visual mode that is neither of those: a card that is outlined instead of elevated, a hero that renders dark instead of default.
4. **Do not reach for `tone`.** It exists on one component and is not the direction the vocabulary is going.
5. **Validate with `WireKit::validateProp()`** so an unknown value raises in debug and falls back to the first allowed value in production. See [Authoring Custom Components → Prop validation](/extending/authoring-custom-components) for the call shape.

## Where this is heading

::: info The short answer for planning
`variant` and `tone` are supported for the whole of v2 and will not be renamed in any v2 release. The unification lands in v3.0.0, which has **no fixed date**. If you are writing new code today, write `intent` and `surface` — those two names survive the transition unchanged.
:::

v3.0.0 reduces the modifier vocabulary to **two** canonical axes: `intent` for the color role, `surface` for the surface treatment. `variant` and `tone` are the names that go.

The mapping is settled per component, and the two kinds of change are worth separating, because only one of them is mechanical:

| Component | Today | v3.0.0 | Kind of change |
| --- | --- | --- | --- |
| every component in the [alias matrix](#the-alias-matrix) | `variant="…"` | `intent="…"` | prop rename, value unchanged |
| `card` | `variant="outlined"` | `surface="outline"` | prop rename **and** value rename |
| `card` | `variant="elevated"` / `"flat"` | `surface="elevated"` / `"flat"` | prop rename, value unchanged |
| `feature` | `tone="accent"` | `intent="primary"` | prop rename **and** value rename |
| `feature` | `tone="soft"` | `intent="info"` | prop rename **and** value rename |
| `feature` | `tone="success"` / `"warning"` / `"danger"` | `intent="…"` | prop rename, value unchanged |

Every row in the [alias matrix](#the-alias-matrix) is in the first group: the value you already wrote is the value `intent` takes, because both spellings already resolve into one validated set. Renaming the prop on those components changes nothing about what renders.

The rows that also rename a value are the ones a find-and-replace gets wrong, and they are named above so the rewrite can be done per component rather than in one sweep.

Preparing early costs nothing and is safe today: `intent` is already accepted on every component in the alias matrix, and `surface` is already accepted on the components that will grow it. Moving a call site from `variant` to `intent` right now is a no-op at runtime.
