# Sizing

> The control-sizing contract — a single set of `--wr-control-*` tokens that every form control reads, so button, input, select and friends line up pixel-for-pixel at each size and track density together. Plus the radius scale that frames controls, containers and overlays.

Source: https://ngwr.dev/guides/tokens/sizing  
Kind: Reference, Tokens

## The control-sizing contract

Three sizes — `sm` (22px), `md` (30px, the default) and `lg` (36px). A control's height is never hard-coded. It is the sum of its line-height, twice its vertical padding and twice its 1px border. Keeping the parts as separate tokens means padding-y can stay density-aware while line-height holds the text box steady.

```scss
:root {
  /* sm → 22px · md → 30px · lg → 36px */
  --wr-control-padding-y-sm: 0.125rem;  /* 2px  → 16 + 2×2 + 2×1 = 22 */
  --wr-control-padding-y-md: 0.25rem;   /* 4px  → 20 + 2×4 + 2×1 = 30 */
  --wr-control-padding-y-lg: 0.3125rem; /* 5px  → 24 + 2×5 + 2×1 = 36 */

  --wr-control-padding-x-sm: 0.5rem;    /* 8px  */
  --wr-control-padding-x-md: 0.75rem;   /* 12px */
  --wr-control-padding-x-lg: 1rem;      /* 16px */

  --wr-control-line-height-sm: 1rem;    /* 16px */
  --wr-control-line-height-md: 1.25rem; /* 20px */
  --wr-control-line-height-lg: 1.5rem;  /* 24px */

  --wr-control-font-size-sm: var(--wr-text-xs);   /* 12px */
  --wr-control-font-size-md: var(--wr-text-sm);   /* 14px */
  --wr-control-font-size-lg: var(--wr-text-base); /* 16px */

  --wr-control-radius-sm: 5px;
  --wr-control-radius-md: 6px;
  --wr-control-radius-lg: 7px;
}
```

## The size ladder

Each box below is a bare `<div>` styled only with the contract tokens — no fixed heights. The rendered height equals `line-height + 2×padding-y + 2×1px border`, i.e. 22 / 30 / 36px.

## How a control derives its height

A control points its own `--wr-<name>-*` knobs at the contract, then renders padding and line-height from them. The `sm` / `lg` modifiers simply re-point the same knobs at the matching tier — there is no separate height rule to keep in sync. Because only padding-y is multiplied by `--wr-density-y`, switching density keeps the whole catalogue aligned.

```scss
/* A control never hard-codes its height. It sums the three contract
   parts, and multiplies padding-y by the density factor so the whole
   catalogue tracks density together. From the button styles: */
.wr-btn {
  --wr-btn-padding-y: var(--wr-control-padding-y-md);
  --wr-btn-line-height: var(--wr-control-line-height-md);
  --wr-btn-font-size: var(--wr-control-font-size-md);
  --wr-btn-radius: var(--wr-control-radius-md);

  line-height: var(--wr-btn-line-height);
  font-size: var(--wr-btn-font-size);
  /* height = line-height + 2×padding-y(×density) + 2×1px border */
  padding: calc(var(--wr-btn-padding-y) * var(--wr-density-y, 1))
    calc(var(--wr-btn-padding-x) * var(--wr-density-x, 1));
}

/* The 'sm' / 'lg' modifiers just re-point the same knobs at the
   matching tier of the contract — so button, input and select line
   up pixel-for-pixel in a row at every size. */
.wr-btn--sm {
  --wr-btn-padding-y: var(--wr-control-padding-y-sm);
  --wr-btn-line-height: var(--wr-control-line-height-sm);
  --wr-btn-font-size: var(--wr-control-font-size-sm);
  --wr-btn-radius: var(--wr-control-radius-sm);
}
```

## The contract tokens

| Name | Description | Type | Default |
| --- | --- | --- | --- |
| `--wr-control-padding-y-{sm,md,lg}` | Vertical padding per size. Multiplied by `--wr-density-y` at consume time, so it stays the only density-aware part of the height. | `length` | `2px · 4px · 5px` |
| `--wr-control-padding-x-{sm,md,lg}` | Horizontal padding per size. Multiplied by `--wr-density-x` at consume time. | `length` | `8px · 12px · 16px` |
| `--wr-control-line-height-{sm,md,lg}` | The text box height — the fixed core the padding is added around. | `length` | `16px · 20px · 24px` |
| `--wr-control-font-size-{sm,md,lg}` | Label size per tier — 12 / 14 / 16px. Aliases the type scale so it stays in step with typography. | `length` | ```--wr-text-xs` · `--wr-text-sm` · `--wr-text-base``` |
| `--wr-control-radius-{sm,md,lg}` | Corner radius per control size. Climbs gently so larger controls read a touch rounder. | `length` | `5px · 6px · 7px` |

## The shared size API

Every form control takes the same `size` input — `sm`, `md` (default) or `lg`. One mental model across the catalogue: pick a size once and button, input and select agree on it.

```angular-html
<!-- One size scale everywhere — sm · md (default) · lg. The attribute
     name differs on the input directive: it is `size`, so it cannot clash
     with the native `<input size>`. -->
<button wr-btn size="sm">Small</button>
<button wr-btn size="md">Medium</button>
<button wr-btn size="lg">Large</button>

<input wrInput size="sm" placeholder="Small" />
<wr-select size="lg" placeholder="Large">…</wr-select>
```

```angular-ts
import { input } from '@angular/core';

// Each control re-exports its own size alias, but they share one shape.
export type WrButtonSize = 'sm' | 'md' | 'lg';
export type WrInputSize = 'sm' | 'md' | 'lg';
export type WrSelectSize = 'sm' | 'md' | 'lg';

// In the component the input defaults to 'md' and only emits a class
// when it differs — so the base styles cover the common case.
readonly size = input<WrButtonSize>('md');
// host: const size = this.size();
//       if (size !== 'md') parts.push(`wr-btn--${size}`);
```

## The radius scale

Two families. Controls use the tight `--wr-control-radius-{sm,md,lg}` ramp (5 / 6 / 7px) so they read crisp at small sizes. Containers and overlays use `--wr-border-radius-*`, anchored on a 10px `base` for panels — cards, dropdowns, dialogs, toasts — with `sm` (6px) for nested rows, `lg` (16px) for large surfaces and `pill` (50rem) for fully-rounded ends. Circles (avatars, radio dots, FABs) use `border-radius: 50%` directly — there is no token for them.

```scss
:root {
  /* Controls — the squircle radii that sit on buttons / inputs / selects. */
  --wr-control-radius-sm: 5px;
  --wr-control-radius-md: 6px;
  --wr-control-radius-lg: 7px;

  /* Containers & overlays — cards, dropdowns, dialogs, toasts… */
  --wr-border-radius-sm: 0.375rem;  /* 6px  — nested items (e.g. dropdown rows) */
  --wr-border-radius-base: 0.625rem; /* 10px — the default panel radius */
  --wr-border-radius-lg: 1rem;       /* 16px — large surfaces */
  --wr-border-radius-pill: 50rem;    /* fully-rounded ends (pill buttons, tags) */
}

/* Circles are not a token — avatars, radio dots and FABs use 50% directly. */
.wr-avatar { border-radius: 50%; }
```

## Disabled

Part of the control contract too — a control that will not respond has one fade strength, not seven.

| Name | Description | Type | Default |
| --- | --- | --- | --- |
| `--wr-disabled-opacity` | How far a control fades when it will not respond. One number, because the catalog had **seven** — 0.6 in eighteen places, then 0.5, 0.7, 0.55, 0.4, 0.35 and 0.65 across 29 entry points, with `date-picker` and `calendar` each disagreeing with themselves three ways inside one file. Override it in a component block, not per declaration: `wr-calendar` does, because a day from the adjacent month is still navigable at 0.55, so a disabled day has to be fainter than that or the two swap meanings. | `0.6` | — |

## The radius tokens

| Name | Description | Type | Default |
| --- | --- | --- | --- |
| `--wr-control-radius-sm` | Small control corners (`size="sm"` button / input / select). | `length` | `5px` |
| `--wr-control-radius-md` | Default control corners. | `length` | `6px` |
| `--wr-control-radius-lg` | Large control corners. | `length` | `7px` |
| `--wr-border-radius-sm` | Nested surfaces inside a panel — e.g. dropdown / context-menu rows. | `length` | `0.375rem (6px)` |
| `--wr-border-radius-base` | The default container / overlay radius — cards, dropdowns, dialogs, toasts, popovers. | `length` | `0.625rem (10px)` |
| `--wr-border-radius-lg` | Large surfaces that want a softer frame. | `length` | `1rem (16px)` |
| `--wr-border-radius-pill` | Fully-rounded ends — pill buttons, tags, the `pill` shape modifier. | `length` | `50rem` |

## See also

- [WrDensity](https://ngwr.dev/reference/services/density) — The `--wr-density-*` multipliers that scale padding-y at runtime.
- [Theming](https://ngwr.dev/guides/theming) — Overriding any of these tokens on `:root`.
