UNPKG

@loke/ui

Version:
154 lines (112 loc) 4.44 kB
--- name: label type: core domain: forms requires: [loke-ui] description: > Label primitive wrapping Primitive.label. Prevents text selection on double-click via mousedown prevention when target is not a nested form control. Associate with Checkbox, Switch, RadioGroupItem via htmlFor or wrapping. Do not use plain HTML label. --- # Label `@loke/ui/label` — thin wrapper over `Primitive.label` that prevents accidental text selection on double-click, while still forwarding all standard label props and `ref`. **Exports:** `Label` ## Setup ```tsx import { Checkbox, CheckboxIndicator } from "@loke/ui/checkbox"; import { Label } from "@loke/ui/label"; function TermsField() { return ( <div style={{ display: "flex", alignItems: "center", gap: 8 }}> <Checkbox id="terms" name="terms"> <CheckboxIndicator></CheckboxIndicator> </Checkbox> <Label htmlFor="terms">I accept the terms and conditions</Label> </div> ); } ``` ## Core Patterns ### htmlFor association Use `htmlFor` when the Label does not wrap the control. Clicking the label activates the associated control by `id`. ```tsx <Label htmlFor="email">Email address</Label> <input id="email" type="email" /> ``` ### Wrapping a Switch When wrapping, `htmlFor` is optional — the label activates the first focusable descendant. ```tsx import { Switch, SwitchThumb } from "@loke/ui/switch"; import { Label } from "@loke/ui/label"; <Label> <Switch name="notifications"> <SwitchThumb /> </Switch> Push notifications </Label> ``` ### Labelling a RadioGroup item Each `RadioGroupItem` needs its own `Label`. Use `htmlFor` pointing to the item's `id`: ```tsx import { RadioGroup, RadioGroupItem, RadioGroupIndicator } from "@loke/ui/radio-group"; import { Label } from "@loke/ui/label"; <RadioGroup name="plan"> <div style={{ display: "flex", gap: 8 }}> <RadioGroupItem id="plan-free" value="free"> <RadioGroupIndicator /> </RadioGroupItem> <Label htmlFor="plan-free">Free</Label> </div> <div style={{ display: "flex", gap: 8 }}> <RadioGroupItem id="plan-pro" value="pro"> <RadioGroupIndicator /> </RadioGroupItem> <Label htmlFor="plan-pro">Pro</Label> </div> </RadioGroup> ``` ## Common Mistakes ### 1. Using a plain HTML `<label>` instead of the Label primitive **Wrong:** ```tsx <label htmlFor="agree">Accept terms</label> ``` **Correct:** ```tsx import { Label } from "@loke/ui/label"; <Label htmlFor="agree">Accept terms</Label> ``` A plain `<label>` does not prevent double-click text selection. The Label primitive intercepts `mousedown` and calls `preventDefault` when `event.detail > 1` and the click target is not a form control — eliminating the flash of selected text users see when double-clicking label text. Source: `src/components/label/label.tsx` — mousedown handler ### 2. Forgetting htmlFor when label does not wrap the control **Wrong:** ```tsx <Label>Accept terms</Label> <Checkbox id="agree" name="agree"> <CheckboxIndicator></CheckboxIndicator> </Checkbox> ``` The label text is visible but clicking it does not activate the checkbox. There is no ARIA association either, so screen readers do not link the two. **Correct:** ```tsx <Label htmlFor="agree">Accept terms</Label> <Checkbox id="agree" name="agree"> <CheckboxIndicator></CheckboxIndicator> </Checkbox> ``` Source: `src/components/label/label.tsx` — standard HTML label `for` attribute behavior ### 3. Wrapping non-form elements with Label The mousedown prevention logic checks whether the click target is `button, input, select, textarea`. Wrapping arbitrary interactive elements (e.g., custom divs, anchors) results in unexpected `mousedown` prevention. **Wrong:** ```tsx <Label> <div role="button" onClick={handleClick}>Custom button</div> Section label </Label> ``` **Correct:** Only use `Label` to wrap or reference native form controls (`<input>`, `<button>`, `<select>`, `<textarea>`) or the `@loke/ui` form primitives (`Checkbox`, `Switch`, `RadioGroupItem`) which render as `<button>` elements. Source: `src/components/label/label.tsx` — `target.closest("button, input, select, textarea")` ## Cross-references - **Checkbox** (`@loke/ui/checkbox`) — primary use case for Label - **Switch** (`@loke/ui/switch`) — use Label for accessible switch naming - **Radio Group** (`@loke/ui/radio-group`) — label each RadioGroupItem individually