Label
The visible name of a form control: 13px medium text that stays on screen while the user types. Marks a required field with an asterisk, or an optional one with "(optional)", and can add an info button with a tooltip for extra help. Inside a Field, use FieldLabel, which points at the control on its own.
Preview
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
<div className="flex w-full max-w-sm flex-col gap-xs">
<Label htmlFor="preview-label">API key</Label>
<Input id="preview-label" />
</div>Usage
When to use
- Name a form control with text that stays on screen while the user types.
- Mark a required field, or an optional one in a form where most fields are required.
When not to use
- A control with a description or an error. Use Field, which links them to the control and sets the label for you.
- Text that names no control, such as the title of a section. Use a heading.
- Replacing it with a placeholder, which disappears as soon as the user types.
Examples
Default
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
<div className="flex w-full max-w-sm flex-col gap-xs">
<Label htmlFor="project-name">Project name</Label>
<Input id="project-name" />
</div>Required
The asterisk is for sighted users; required on the input is what screen readers announce.
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
<div className="flex w-full max-w-sm flex-col gap-xs">
<Label htmlFor="workspace-name" required>Workspace name</Label>
<Input id="workspace-name" required />
</div>Optional
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
<div className="flex w-full max-w-sm flex-col gap-xs">
<Label htmlFor="company" optional>Company</Label>
<Input id="company" />
</div>Disabled
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
<div className="flex w-full max-w-sm flex-col gap-xs">
<Label htmlFor="region" disabled>Region</Label>
<Input id="region" disabled defaultValue="Europe" />
</div>With a tooltip
The info button names the field, and its tooltip is read as the button's description.
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
<div className="flex w-full max-w-sm flex-col gap-xs">
<Label htmlFor="api-key" tooltip="Find it under Settings, then API." tooltipLabel="About API keys">API key</Label>
<Input id="api-key" />
</div>Do and don't
Do
- Give every form control a visible label, above it or beside it.
- Write the name in sentence case without a colon: Email, Project name.
- Prefer Field, which wires the label, the control, its description and its error.
- Mark the few required fields when most are optional, or the few optional ones when most are required.
- Use the tooltip for help some users need, such as where to find a value; name its button with the field: About API keys.
- Next to a checkbox, use its label and description props; they lay out the row and link the ids.
Don't
- Don't use a placeholder or aria-label instead of a visible label.
- Don't mark both required and optional fields in one form.
- Don't put a label on text that names no control; use a heading or a paragraph.
- Don't hide what every user needs in the tooltip; write it in the field's description.
Props
| Name | Type | Values | Default | Description |
|---|---|---|---|---|
| htmlFor | string | - | - | The id of the control this label names. FieldLabel sets it for you. |
| required | boolean | - | false | Shows an asterisk after the text. Also set required on the control. Can't be combined with optional. |
| optional | boolean | - | false | Shows "(optional)" after the text, for a form where most fields are required. Mark the few required fields or the few optional ones, not both. |
| disabled | boolean | - | false | Dims the label to match a disabled control. Sets data-disabled. |
| tooltip | node | - | - | Extra help behind an info button after the label, such as where to find a value. A few words of plain text. Needs tooltipLabel. |
| tooltipLabel | string | - | - | Name of the info button, with the field in it: About API keys. Required with tooltip. |
Slots
| Name | Required | Description |
|---|---|---|
| label | yes | The name of the field, in sentence case and without a colon: Email, Project name. |
Anatomy
| Part | Description |
|---|---|
| root | The label element: 13px medium primary text, the marker after it with a small gap. With a tooltip, the root is a row that holds the label and the info button. |
| marker | The required asterisk, in the label's own color, hidden from screen readers. |
| optional | "(optional)" in regular weight and secondary text. |
| info | Optional info button after the label: a 20px circle around a 16px Tabler info icon in the secondary icon color, neutral hover and pressed overlays, focus ring. Opens the tooltip. |
| hitArea | Transparent ::before centered on the info button, never smaller than size-target-min (24px on mouse, 44px on touch). |
Accessibility
Role: generic
| Keys | Action |
|---|---|
| Tab | With tooltip: moves focus to the info button and opens the tooltip. |
| Enter, Space | With tooltip: opens or closes the tooltip. |
| Escape | With tooltip: closes the tooltip. |
- htmlFor must match the id of the control, so a press on the label focuses the control and screen readers read the label as its name. Inside a Field, FieldLabel sets it.
- The required asterisk is a shape, so it never relies on color, and it is hidden from screen readers. Set required on the control too: that is what screen readers announce.
- "(optional)" stays in the accessible name, since no attribute says a field is optional.
- A placeholder is not a label: it disappears as soon as the user types.
- The info button sits outside the label element, so its name never becomes part of the control's name. The type requires tooltipLabel with tooltip, and the label should include the field: About API keys.
- The tooltip opens on hover and focus, and a press toggles it, so it works on touch screens too. Its text is read as the button's description.
- Keep what the user needs to fill the field in a description under it (FieldDescription), not only in the tooltip.
Install
npx shadcn add https://werewolf-ui.vercel.app/r/label.jsonThis also adds styles/werewolf.css, the tokens every component uses, and any other component it needs. Import that file once in your root CSS, after @import "tailwindcss", with a path relative to that CSS file (from app/globals.css it is @import "../styles/werewolf.css").