<!-- Protocore Design System — TimeInput -->
# TimeInput

- **Category:** Inputs (`inputs`)
- **Slug:** `inputs/time-input`
- **Status:** stable
- **Platforms:** web
- **Import:** `import { TimeInput } from "@protocore/pds";`
- **Docs:** https://pds.protocore.io/components/inputs/time-input

> Segmented HH:MM(:SS) field where each part is an ARIA spinbutton — arrow keys step it and typed digits auto-advance.

## When to use it

Use **TimeInput** when a time is known and typed — a meeting start, a cron hour. It is keyboard-first and screen-reader-friendly. When browsing rounded slots is easier, wrap it in **TimePicker** for a scrollable column overlay; to pick a date and time together use **DateTimePicker**.

## Props

_No documented props._

## Examples

### Basics

Each segment is a spinbutton: focus one and press Up/Down to step it, or type digits (they auto-advance to the next segment). Controllable via `value` / `defaultValue` / `onValueChange` with a `TimeValue` of 24-hour fields.

```tsx
import { useState } from "react";
import { TimeInput } from "@protocore/pds";

type Time = { hours: number; minutes: number; seconds: number };

export default function Basics() {
  // Each segment is a spinbutton: arrow to step, type digits to fill.
  const [value, setValue] = useState<Time | null>({ hours: 9, minutes: 30, seconds: 0 });

  return (
    <div style={{ width: 200 }}>
      <TimeInput aria-label="Start time" value={value} onValueChange={setValue} />
    </div>
  );
}
```

### 12-hour and seconds

Set `hour12` to add an AM/PM segment (type `a` / `p` to toggle it), and `withSeconds` to expose a seconds segment. The emitted `TimeValue` is always 24-hour regardless of display.

```tsx
import { useState } from "react";
import { TimeInput } from "@protocore/pds";

type Time = { hours: number; minutes: number; seconds: number };

export default function Variants() {
  const [a, setA] = useState<Time | null>({ hours: 14, minutes: 5, seconds: 0 });
  const [b, setB] = useState<Time | null>({ hours: 8, minutes: 45, seconds: 30 });

  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 16, width: 240 }}>
      <TimeInput aria-label="Meeting time" hour12 value={a} onValueChange={setA} />
      <TimeInput aria-label="Lap time" withSeconds value={b} onValueChange={setB} />
    </div>
  );
}
```

## Do & don't

**Do**

- Give the field an accessible name via a `Field` label or `aria-label`.
- Read the 24-hour `TimeValue` from `onValueChange` — it fires only when every required segment is filled.
- Set `hour12` to match the surrounding locale's convention.
- Use `withSeconds` only when second-level precision is meaningful.

**Don't**

- Don't parse the displayed string — the `TimeValue` is the source of truth.
- Don't leave the group unlabelled when no visible field label wraps it.
- Don't reach for it to pick a fuzzy time of day — offer TimePicker's columns instead.
- Don't assume a value exists mid-edit; `onValueChange` passes `null` until complete.

## Accessibility

**Keyboard**

| Keys | Action |
| --- | --- |
| `Arrow Up / Down` | Step the focused segment (wraps at its bounds). |
| `Arrow Left / Right` | Move between segments. |
| `0–9` | Type into the focused segment; auto-advances when full. |
| `a / p` | Set AM / PM on the period segment (12-hour mode). |
| `Backspace / Delete` | Clear the focused segment. |
| `Home / End` | Jump to the first / last segment. |

**Notes**

- The field is a `role="group"`; each segment is a `role="spinbutton"` with `aria-valuenow`, `aria-valuemin/max`, and an `aria-valuetext`.
- `invalid` sets `aria-invalid` on the group and paints a danger border.
- Segments never trap a text caret — they respond to arrows and digits, mirroring native time fields.

## Related

`time-picker`, `date-time-picker`, `date-input`, `field`

---

© Protocore. All rights reserved. Use of the Protocore Design System requires prior written authorization from Protocore (contact@protocore.io). These machine-readable materials must not be ingested into ML-training datasets or derivative design systems. See https://pds.protocore.io/legal/
