Theme
On this page (8)
Component Skill — components/label/component-skill.md

Native basis

<label> element. Browser provides built-in click-to-focus association with form controls.

Web Platform APIs

<label>:has() selectorforced-colors

Classes

.label.input[data-disabled].label-hint

Accessibility

• One value, written twice: the label’s for and the control’s id must be the exact same string. A mismatch fails silently - the label renders and styles normally but no longer focuses the field or names it for assistive technology.

• Clicking the label focuses the associated input - this is native <label> behavior.

• The required indicator * uses aria-hidden="true" since the required attribute on the input already conveys the requirement to assistive technology.

• Do not use <label> without a for attribute or a nested input.

• In forced-colors mode, label text maps to system LinkText color.

Notes

• The .label class is intentionally minimal - it styles the label text with appropriate font size, weight, and color.

• Labels auto-detect disabled state from adjacent controls via :has(+ :disabled) - the explicit data-disabled attribute is also supported.

• For inline use with checkboxes, switches, or radios, add style="margin:0;" to remove the default bottom margin.

• Labels compose with Input, Textarea, Select, Checkbox, Radio, Switch, and all other form controls.

§Default

Basic label paired with an input. Click the label to focus the input - for='demo-email' and id='demo-email' must always match; changing one without the other breaks the link.

§Required

The red asterisk renders automatically from :has(+ input[required]) - no markup to add or forget.

§Optional hint

Hint text indicating the field is optional.

§Disabled

Dimmed label for disabled fields. The label auto-detects the disabled input via :has() - no data-disabled attribute needed.

§With Checkbox

Inline label paired with a checkbox control.

§With Switch

Inline label paired with a switch control.

§Form Field

Label composed with an input and description text, as used in forms.

§CSS view file

/* -- Label component -------------------------------------------- */
@layer components {
  .label {
    font-size: 0.875rem;
    font-weight: 500;
    font-family: var(--font-sans);
    line-height: 1;
    color: var(--foreground);
    display: block;
    margin-bottom: 0.375rem;
    cursor: default;
    /* Required - the adjacent control carries `required`: render the star.
       Pure CSS (no span to remember, nothing to desync); decorative only —
       the native attribute already conveys the requirement to AT. */
    &:has(+ input[required])::after,
    &:has(+ textarea[required])::after {
      content: ' \00a0*';
      color: var(--destructive);
      font-weight: 600;
    }
    /* Disabled - explicit attribute */
    &[data-disabled] {
      opacity: 0.7;
      cursor: not-allowed;
    }
    /* Disabled - auto-detect via adjacent disabled control */
    &:has(+ :disabled),
    &:has(+ [data-disabled]) {
      opacity: 0.7;
      cursor: not-allowed;
    }
  }
  /* -- Hint text (optional, required indicators) ----------- */
  .label-hint {
    font-weight: 400;
    font-size: 0.8125rem;
    color: var(--muted-foreground);
  }
  /* -- Accessibility ---------------------------------------- */
  @media (prefers-contrast: more) {
    .label[data-disabled],
    .label:has(+ :disabled),
    .label:has(+ [data-disabled]) {
      opacity: 0.85;
    }
  }
  @media (forced-colors: active) {
    .label {
      color: LinkText;
    }
    .label[data-disabled],
    .label:has(+ :disabled),
    .label:has(+ [data-disabled]) {
      opacity: 1;
      color: GrayText;
    }
  }
}

Comments, ideas or improvements? Edit this page's source on GitHub