← ui

label

ui

what a field is called: a real label that focuses its field when pressed

npx shadcn add @ag/label
Already installed? Preview an update
npx shadcn add @ag/label --dry-run

Your installed source stays unchanged until you update it. How updates work

Latest changes · 2026-10-10
  • New: what a field is called, a real <label>, dimmed beside or around a disabled field; on glass its lettering catches the rim's light.
Updated 2026-10-10 · release notes

shelf controls

uses material

used by components, field

the demo above, verbatim

label.demo.tsx
"use client"

import { Sample } from "@/app/samples"
import { Label } from "@/registry/base-nova/ui/label"
import { MaterialProvider } from "@/registry/base-nova/ui/material"
import { Textarea } from "@/registry/base-nova/ui/textarea"

// #region field
function Field({ id }: { id: string }) {
  return (
    <div className="flex w-72 flex-col gap-2">
      <Label htmlFor={id}>note</Label>
      <Textarea id={id} placeholder="anything we should know" rows={2} />
    </div>
  )
}
// #endregion

export default function LabelDemo() {
  return (
    <div className="space-y-8">
      <Sample
        name="materials"
        title="plain and glass"
        description="a press on the label focuses its field."
        with="field"
      >
        <div className="grid gap-4 sm:grid-cols-2">
          <Field id="note-plain" />
          <MaterialProvider material="glass">
            <div className="rounded-xl bg-[url(/lightbox/field.jpg)] bg-cover bg-center p-6">
              <Field id="note-glass" />
            </div>
          </MaterialProvider>
        </div>
      </Sample>
      <Sample name="note" glance title="a label over its field" with="field">
        <Field id="note-alone" />
      </Sample>
    </div>
  )
}

the source, every file an install writes

label.tsx
"use client"

import type * as React from "react"
import { cn } from "@/lib/utils"
import { type Material, useMaterial } from "@/registry/base-nova/ui/material"

// What a field is called: a real <label>, so a press on it focuses its field (or
// wrap the field in it). The field's own voice, one step quieter than what is typed,
// at the field's size; dimmed with a disabled field beside it (`peer`) or inside it.
// `material="glass"`: lettering on glass, catching the rim's light along its foot.

export type LabelProps = React.ComponentProps<"label"> & {
  material?: Material
}

export function Label({ material, className, ...props }: LabelProps) {
  const m = useMaterial(material)
  return (
    // biome-ignore lint/a11y/noLabelWithoutControl: the consumer names its control (htmlFor) or wraps it
    <label
      data-slot="label"
      data-material={m}
      className={cn(
        "inline-flex items-center gap-2 font-medium text-[13px] leading-none select-none has-[:disabled]:opacity-50 peer-disabled:opacity-50",
        m === "glass"
          ? "text-current/90 [text-shadow:0_0.5px_0_color-mix(in_oklab,var(--glass-rim-ink,white)_40%,transparent)]"
          : "text-foreground/80",
        className,
      )}
      {...props}
    />
  )
}