# Label

A lightweight text label for section titles, metadata, and explicit control
associations when a full Field layout is not needed.

For complete form fields, prefer `Field`. It wires the label, control,
description, error message, required/invalid/disabled/read-only state, and ARIA
ids for you. Use standalone `Label` when you only need the label primitive.

## Usage

```tsx
import { Label } from '@takeoff-ui/react-spar';
```

```tsx
<Label as="span">Payment Method</Label>
```

For native controls, point `htmlFor` at the control id:

```tsx
<Label htmlFor="pnr">PNR</Label>
<input id="pnr" />
```

For Takeoff `Input`, manual association is possible but `Field` is usually the
better choice. If you do wire it manually, set the base id on `Input` and point
`htmlFor` at the field id generated by Spar (`${id}-field`):

```tsx
<Label id="passenger-name-label" htmlFor="passenger-name-field">
  Passenger name
</Label>
<Input id="passenger-name">
  <Input.Field />
</Input>
```

## Playground

```tsx
function PlaygroundDemo() {
  return (
    <div className="grid w-full max-w-120 gap-3">
      <div className="rounded-xl border border-neutral-200 p-4">
        <Label
          as="h4"
          style={{
            fontFamily: 'var(--desktop-title-h4-font)',
            fontSize: 'var(--desktop-title-h4-size)',
            fontWeight: 'var(--desktop-title-h4-line-weight)',
            lineHeight: 'var(--desktop-title-h4-line-height)',
          }}
        >
          Payment Method
        </Label>
        <p className="m-0 text-sm text-neutral-600">
          All transactions are secure and encrypted.
        </p>
      </div>

      <div className="grid gap-2">
        <Label htmlFor="booking-reference">Booking reference</Label>
        <input
          id="booking-reference"
          className="rounded-lg border border-neutral-200 px-3 py-2 text-sm"
          placeholder="TK-1928"
        />
      </div>

      <div className="grid gap-2 rounded-xl border border-neutral-200 p-4">
        <Label id="passenger-name-label" htmlFor="passenger-name-field">Passenger name</Label>
        <Input id="passenger-name">
          <Input.Field placeholder="Ada Lovelace" />
        </Input>
      </div>
    </div>
  );
}

render(<PlaygroundDemo />);
```

## States

`required`, `optional`, `disabled`, `readOnly`, and `invalid` are emitted as
`data-*` attributes on the label for styling. They do not set `required`,
`disabled`, `readOnly`, or `aria-invalid` on the related control. Set the same
state on the control or use `Field` when the label and control should stay
synchronized. Required labels render a decorative asterisk after the text.

```tsx
function StatesDemo() {
  return (
    <div className="grid w-full max-w-90 gap-3">
      <div className="grid gap-1">
        <Label htmlFor="required-native" required>Required label</Label>
        <input id="required-native" required className="rounded-lg border border-neutral-300 px-3 py-2 text-sm" placeholder="Required field" />
      </div>

      <div className="grid gap-1">
        <Label htmlFor="optional-native" optional>Optional label</Label>
        <input id="optional-native" className="rounded-lg border border-neutral-300 px-3 py-2 text-sm" placeholder="Optional field" />
      </div>

      <div className="grid gap-1">
        <Label htmlFor="disabled-native" disabled>Disabled label</Label>
        <input id="disabled-native" disabled className="rounded-lg border border-neutral-300 px-3 py-2 text-sm" placeholder="Disabled field" />
      </div>

      <div className="grid gap-1">
        <Label htmlFor="readonly-native" readOnly>Read-only label</Label>
        <input id="readonly-native" readOnly className="rounded-lg border border-neutral-300 px-3 py-2 text-sm" defaultValue="Read-only value" />
      </div>

      <div className="grid gap-1">
        <Label htmlFor="invalid-native" invalid>Invalid label</Label>
        <input id="invalid-native" aria-invalid="true" className="rounded-lg border border-danger-base px-3 py-2 text-sm" defaultValue="Invalid value" />
      </div>
    </div>
  );
}

render(<StatesDemo />);
```

## Label vs Field

`Label` is intentionally small. If you need helper text, error text, invalid
styling, or shared disabled/read-only/required state, use `Field.Label` inside
`Field`. That is the default path for form controls.

```tsx
function FieldComparisonDemo() {
  return (
    <Field className="w-full max-w-90" required>
      <Field.Label>Passenger email</Field.Label>
      <Input>
        <Input.Field type="email" placeholder="you@example.com" />
      </Input>
      <Field.Description>
        Use Field when the label, helper text, invalid state, and control should be wired together.
      </Field.Description>
    </Field>
  );
}

render(<FieldComparisonDemo />);
```

## API Reference

### Label {#label}

#### Props {#label-props}

| Name                            | Type                                                                                      | Default | Description                                                                |
| ------------------------------- | ----------------------------------------------------------------------------------------- | ------- | -------------------------------------------------------------------------- |
| children   | `React.ReactNode`                                                              | -       | Label content.                                                             |
| classNames | `Partial<Record<"root", string>>`                                  | -       | Per-slot extra classes.                                                    |
| slotProps  | `Partial<Record<"root", React.HTMLAttributes<HTMLElement>>>` | -       | Per-slot HTML-attribute overrides.                                         |
| disabled   | `boolean`                                                                      | false   | Marks label for a disabled field (exposed via data attribute for styling)  |
| required   | `boolean`                                                                      | false   | Marks label for a required field (exposed via data attribute for styling)  |
| readOnly   | `boolean`                                                                      | false   | Marks label for a read-only field (exposed via data attribute for styling) |
| invalid    | `boolean`                                                                      | false   | Marks label for an invalid field (exposed via data attribute for styling)  |
| optional   | `boolean`                                                                      | false   | Marks label for an optional field (exposed via data attribute for styling) |
| className  | `string`                                                                       | -       | Appends custom classes to the root slot.                                   |

#### Data attributes {#label-data-attributes}

| Attribute                                       | Applied when        | Purpose                                               |
| ----------------------------------------------- | ------------------- | ----------------------------------------------------- |
| data-slot="root" | Always              | Stable selector for wrapper styling on the root slot. |
| data-required              | `required` is true. | Styling hook for required labels.                     |
| data-optional              | `optional` is true. | Styling hook for optional labels.                     |
| data-disabled              | `disabled` is true. | Styling hook for disabled labels.                     |
| data-readonly              | `readOnly` is true. | Styling hook for read-only labels.                    |
| data-invalid               | `invalid` is true.  | Styling hook for invalid labels.                      |
