@cf/ui

@cf/label

Label

A styled <label> for naming a form control when you're not using Field.

Clicking the first label toggles its switch. The second switch is disabled, and its label doesn't dim yet: Base UI renders the switch root as a span, which never matches :disabled, so peer-disabled: has nothing to catch.

A plain <label> with type styles. It exists mostly because FieldLabel is built on it, and I reach for FieldLabel in almost every case. Nothing on the site imports Label directly.

The peer-disabled: classes only work for a control that is an earlier sibling marked peer. That rules out both of the controls people reach for: Input isn't marked peer, and Switch renders a span, which never matches :disabled. Dim the Label yourself when its control is disabled.

import { Label } from "@/components/ui/label";
 
<Label htmlFor="name">Name</Label>
<input id="name" />

Rules

The same rules ship in DESIGN.md and the MCP server, for the agents building with this.

  • must

    Set htmlFor to the control's id, or wrap the control inside the Label; a Label with neither names nothing.

    The component just forwards htmlFor, so the association is entirely the caller's job.

    associate-with-control

  • should

    Inside a Field, use FieldLabel instead of Label, so the label picks up the field's disabled and checked states.

    prefer-field-label

  • may

    Rely on the built-in peer-disabled dimming only when the control comes before the Label in the DOM and is a native input carrying the peer class; :disabled never matches Base UI's Switch, which renders a span.

    peer-disabled-needs-order

Install

bunx shadcn@latest add @cf/label

Needs the @cf registry in your components.json once. The theme and any sibling components come along automatically.

Source

label.tsxShow
"use client";
 
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "cn";
import * as React from "react";
 
const labelVariants = cva(
  "font-medium text-sm leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
);
 
type LabelProps = React.ComponentProps<"label"> &
  VariantProps<typeof labelVariants>;
 
const Label = React.forwardRef<HTMLLabelElement, LabelProps>(
  ({ className, htmlFor, ...props }, ref) => (
    // biome-ignore lint/a11y/noLabelWithoutControl: This reusable wrapper forwards the consumer-provided control association.
    <label
      className={cn(labelVariants(), className)}
      htmlFor={htmlFor}
      ref={ref}
      {...props}
    />
  )
);
Label.displayName = "Label";
 
export { Label, type LabelProps, labelVariants };