ComponentsForm
In-field label
A small label fixed inside the top of its field, above the value.
Import
import { InFieldLabel } from "@booleanpress/ui/in-field-label"Usage
InFieldLabel wraps one field and its <label>. The label is 10 px, inside the top of the field at its start padding; the field grows to 47 px to make room above the value.
import { InFieldLabel } from "@booleanpress/ui/in-field-label"
import { Input } from "@booleanpress/ui/input"
import { Label } from "@booleanpress/ui/label"
export function FromName() {
return (
<InFieldLabel>
<Input id="from-name" />
<Label htmlFor="from-name">From name</Label>
</InFieldLabel>
)
}The field is an Input, an InputNumber, an InputMask, a Textarea, a PasswordInput, an InputGroup, a NativeSelect or a Select; the label is a Label (or a label) whose htmlFor is the field's id, a direct child of the wrapper. The label does not move, so a placeholder can show under it. An icon in an InputGroup addon lines up with the value, below the label. For a label that moves out of an empty field, use FloatLabel.
Examples
Basic
The label sits in the top of the field; an icon lines up with the value below it.
import { MailIcon } from "lucide-react"
import { InFieldLabel } from "@booleanpress/ui/in-field-label"
import { Input } from "@booleanpress/ui/input"
import { InputGroup, InputGroupAddon, InputGroupInput } from "@booleanpress/ui/input-group"
import { Label } from "@booleanpress/ui/label"
export default function InFieldLabelBasic() {
return (
<div className="flex w-full max-w-sm flex-col gap-4">
<InFieldLabel>
<Input id="ifl-from-name" defaultValue="Acme Support" />
<Label htmlFor="ifl-from-name">From name</Label>
</InFieldLabel>
<InFieldLabel>
<InputGroup>
<InputGroupInput id="ifl-from-email" placeholder="support@example.com" />
<InputGroupAddon>
<MailIcon />
</InputGroupAddon>
</InputGroup>
<Label htmlFor="ifl-from-email">From email</Label>
</InFieldLabel>
</div>
)
}Invalid
aria-invalid on the field colours the label --destructive-strong, focused or not.
import { InFieldLabel } from "@booleanpress/ui/in-field-label"
import { Input } from "@booleanpress/ui/input"
import { Label } from "@booleanpress/ui/label"
export default function InFieldLabelInvalid() {
return (
<div className="flex w-full max-w-sm flex-col gap-1.5">
<InFieldLabel>
<Input id="ifl-api-key" defaultValue="key_test" aria-invalid aria-describedby="ifl-api-key-error" />
<Label htmlFor="ifl-api-key">API key</Label>
</InFieldLabel>
<p id="ifl-api-key-error" className="text-xs text-destructive-strong">
A live API key starts with key_live_.
</p>
</div>
)
}With a select
A Select takes the label too; its chevron stays centred in the field.
import { InFieldLabel } from "@booleanpress/ui/in-field-label"
import { Label } from "@booleanpress/ui/label"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@booleanpress/ui/select"
export default function InFieldLabelWithSelect() {
return (
<InFieldLabel className="w-full max-w-sm">
<Select defaultValue="weekly">
<SelectTrigger id="ifl-digest" className="w-full">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="daily">Every day</SelectItem>
<SelectItem value="weekly">Every week</SelectItem>
<SelectItem value="monthly">Every month</SelectItem>
</SelectContent>
</Select>
<Label htmlFor="ifl-digest">Delivery report</Label>
</InFieldLabel>
)
}With a number and a mask
An InputNumber, its suffix moving down with the number, and an InputMask.
import { InFieldLabel } from "@booleanpress/ui/in-field-label"
import { InputMask } from "@booleanpress/ui/input-mask"
import { InputNumber } from "@booleanpress/ui/input-number"
import { Label } from "@booleanpress/ui/label"
export default function InFieldLabelWithNumberAndMask() {
return (
<div className="flex w-full max-w-sm flex-col gap-4">
<InFieldLabel>
<InputNumber id="ifl-monthly-quota" fluid defaultValue={25000} suffix="emails" />
<Label htmlFor="ifl-monthly-quota">Monthly quota</Label>
</InFieldLabel>
<InFieldLabel>
<InputMask id="ifl-vat-number" mask="aa999999999" placeholder="GB123456789" />
<Label htmlFor="ifl-vat-number">VAT number</Label>
</InFieldLabel>
</div>
)
}Accessibility
- Semantics
- A
divround the field and a nativelabel. The wrapper adds no role. - Labels
- The label names the field through
htmlForand the field'sid: it is the field's visible and accessible name. - Focus
- The wrapper takes no focus. The label ignores the pointer, so a click on it reaches the field under it. Focus inside the wrapper colours the label
--secondary-foreground. - Known limits
- The 10 px label is small; keep it short.
Keyboard
| Key | Behaviour |
|---|
API
InFieldLabel
Renders a div and passes it every other prop.
Every part takes className, merged with its defaults by cn(), and ref, which reaches the element it renders.
Data attributes: data-slot="in-field-label" (InFieldLabel).
Theming
The label is --muted-foreground, --secondary-foreground while the field has focus and --destructive-strong while the field is invalid.
The tokens its classes read. Change their values in your theme, and it follows.
| Token | Used for |
|---|---|
--destructive-strong | text |
--muted-foreground | text |
--secondary-foreground | text |