Label
A label for a control.
import { Div } from "@implementjs/core";
import { Checkbox } from "@/lib/components/ui/checkbox";
import { Input } from "@/lib/components/ui/input";
import { Label } from "@/lib/components/ui/label";
export default function LabelDemo() {
return Div(
{ class: "flex w-full max-w-sm flex-col gap-4" },
Div(
{ class: "flex flex-col gap-2" },
Label({ for: "email" }, "Email"),
Input({ id: "email", type: "email", placeholder: "you@example.com" }),
),
Div(
{ class: "flex items-center gap-2" },
Checkbox({ id: "remember" }),
Label({ for: "remember" }, "Remember me"),
),
);
}Installation
npx jsrepo add @implementjs/ui/label
Nothing else comes with it — this one stands alone on @implementjs/core and @implementjs/primitives.
Copy the file below to src/lib/components/ui/label.ts. It imports cn, so copy utils.ts to src/lib/utils.ts too.
import { Label as LabelElement, type Child, type ElementProps } from "@implementjs/core";
import { createComponent } from "@implementjs/primitives";
import { cn } from "@/lib/utils";
export type LabelProps = ElementProps<"label">;
/**
* A label for a control. Point it at one with `for`; the primitives render
* buttons rather than inputs, so `for` and `id` are how they pair up.
*
* It dims alongside a disabled control in two ways: `peer-disabled` for a
* sibling marked `peer`, and `group-data-[disabled=true]` for a wrapper
* marked `group` — which is what `Field` uses.
*/
export const Label = createComponent(function Label(
{ class: className, ...props }: LabelProps,
...children: Child[]
) {
return LabelElement(
{
...props,
"data-slot": "label",
class: cn(
"flex items-center gap-2 text-sm leading-none font-medium select-none",
"peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
"group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50",
className,
),
},
...children,
);
});Usage
import { Label } from "@/lib/components/ui/label";
Label({ for: "email" }, "Email");
Input({ id: "email", type: "email" });
The primitives render buttons rather than inputs, so for and id are how a label pairs with a checkbox, a switch, or a radio group item — the same as with a real input.
Disabled controls
A label dims with its control in two ways, so it works whichever way the markup is arranged:
peer-disabled:— for a control markedpeerthat is a sibling of the label.group-data-[disabled=true]:— for a wrapper markedgroup, which is what field uses.
Neither needs the label to be told anything.
Inside a field
Field has FieldLabel, which wraps this one and adds the layout for a label with a whole control nested inside it. Use Label on its own for a plain label; reach for FieldLabel once the form has hints and errors to arrange.
API Reference
Label
A label for a control. Point it at one with `for`; the primitives render buttons rather than inputs, so `for` and `id` are how they pair up. It dims with a disabled sibling marked `peer`, and with a disabled wrapper marked `group`. Renders a Label; extra props are forwarded onto it.