6.7 KiB
Label
(no submodules)
- label.variants — `labelVariants` is the cva (class-…-
label.variants —
labelVariantsis the cva (class-variance-authority) configuration that powers every<Label>render. It is split fro… -
label.variants —
labelVariantsis the cva (class-… -
label.variants —
labelVariantsis the cva (class-variance-authority) configuration that powers every<Label>render. It is split fro… -
label.variants —
labelVariantsis the cva (class-… -
label.variants —
labelVariantsis the cva (class-variance-authority) configuration that powers every<Label>render. It is split fro… -
label.variants —
labelVariantsis the cva (class-… -
label.variants —
labelVariantsis the cva (class-variance-authority) configuration that powers every<Label>render. It is split fro… -
label.variants —
labelVariantsis the cva (class-variance-authority) configuration that powers every<Label>render. It is split fro…
Purpose
<Label> is the chassis form-label primitive for @suaveplan/react-ui. It wraps @radix-ui/react-label so consumers inherit the Radix labelling contract (a click on the label focuses the matched input via htmlFor, and synthetic click events originating inside the label are not double-fired on the input) while threading variant resolution through useThemedVariants("Label") and surfacing a data-disabled="true" mirror so visual disabled state can be represented without needing a sibling form control.
The label children are deliberately NOT translated by the chassis. Labels routinely interleave dynamic inline elements (<strong>, <span>, <Trans>, count badges, asterisks for required fields) and forcing the children through a single string-key lookup would prevent that composition. Consumers compose translation calls in their own JSX, e.g. <Label htmlFor="email">{t('app.email.label')}</Label> or <Label htmlFor="email"><Trans i18nKey="app.email.label" /></Label>.
Features
- Wraps
@radix-ui/react-labelfor full Radix labelling semantics (focus management on click, no double-fire of synthetic events). - Forwards refs to the underlying
<label>element. - All native
LabelHTMLAttributesare forwarded as-is via the Radix primitive's props passthrough. disabledboolean prop writesdata-disabled="true"on the rendered label so the cva config'sdata-[disabled=true]:cursor-not-allowed data-[disabled=true]:opacity-70rules activate.peer-disabled:Tailwind variant baked into the cva base so a label adjacent to apeer-classed input picks up the disabled styling automatically when the input is disabled.- Theme variant overrides via
useThemedVariants("Label", labelVariants). AThemePlugin.variantOverrides.Labelmay add brand-new variants (e.g.variant: "section-heading"). - No translation lookup on children — composition is consumer-driven.
Usage: Basic Example
import { Label, Input } from "@suaveplan/react-ui";
export function EmailField() {
return (
<div className="flex flex-col gap-1">
<Label htmlFor="email">Email</Label>
<Input id="email" type="email" />
</div>
);
}
Usage: Advanced Example
import { Label, Input } from "@suaveplan/react-ui";
import { useTranslation } from "@suaveplan/react-i18n";
export function PasswordField({ disabled }: { disabled: boolean }) {
const { t } = useTranslation();
return (
<div className="flex flex-col gap-1">
<Label htmlFor="password" disabled={disabled}>
{t("app.password.label")}{" "}
<span aria-hidden="true" className="text-destructive">
*
</span>
</Label>
<Input
id="password"
type="password"
disabled={disabled}
className="peer"
autoComplete="current-password"
/>
</div>
);
}
API Reference
<Label>
forwardRef<HTMLLabelElement, LabelProps> — renders a Radix <Label.Root> element (which itself renders a native <label> with the labelling enhancements applied).
LabelProps
| Prop | Type | Default | Description |
|---|---|---|---|
htmlFor |
string |
none | Required. The id of the form control this label is bound to. |
variant |
"default" |
"default" |
Visual variant; the chassis ships only default so themes carry the variation. |
size |
"default" |
"default" |
Sizing variant; the chassis ships only default so themes carry the variation. |
disabled |
boolean |
undefined |
When true, writes data-disabled="true" so the cva opacity decay activates. |
all ComponentPropsWithoutRef<typeof LabelPrimitive.Root> |
native | native | Forwarded as-is to Radix. |
labelVariants
The bare cva resolver. Exported so external consumers (@suaveplan/react-ui-theme-*, ejected source under npx shadcn@latest add @suaveplan/label) can re-use the variants directly without going through useThemedVariants.
Errors
The Label component does NOT call useTranslation() so it does not interact with the i18n provider at all. Consumers that wrap <Trans> or useTranslation themselves inherit the upstream provider's no-throw-in-production contract.
Implementation Notes
- The chassis re-exports Radix's labelling semantics rather than re-implementing them: the click-on-label-focuses-input + click-event-bubble-suppression behaviour is non-trivial to get right, and Radix is the upstream-of-record.
data-disabled="true"is written when the prop istrue, omitted whenfalse/undefined. The CVA selector convention isdata-[disabled=true]:…, so an explicitdata-disabled="false"would not match but it would clutter the DOM.- The
peer-disabled:cursor-not-allowed peer-disabled:opacity-70rules in the cva base let consumers wire a label adjacent to apeer-classed input — when the input is disabled, the label picks up the visual decay automatically without requiring the consumer to thread thedisabledprop through both elements. - The variants module is intentionally separate (
label.variants.ts) so an ejected source copy keeps the cva config in a dedicated file, mirroring the upstream shadcn convention. useThemedVariants("Label", labelVariants)is called unconditionally so the hook order is stable across renders. The hook's memoisation key is(themeName, "Label")so a re-render with the same theme returns the same resolver reference.- Refs forward to the underlying
<label>(RadixLabel.Rootis implemented as a<label>element, not a custom-element wrapper).