# Date picker: Validation

> Show a failed check on the slotsmith DatePicker: the invalid prop, an error message tied to the combobox, and the --sdp-danger token.

The date picker takes `invalid` for a value that failed your check. It marks the combobox with `aria-invalid="true"` and draws the trigger's border in the danger colour. The error message and the label's colour are your app's markup, tied to the combobox with `aria-describedby`.

```tsx
import { useId, useState, type FormEvent } from "react";
import { DatePicker, type ISODate } from "slotsmith/date-picker";

export default function Validation() {
  const [start, setStart] = useState<ISODate | null>(null);
  const [submitted, setSubmitted] = useState(false);
  const [sent, setSent] = useState(false);
  const fieldId = useId();
  const labelId = useId();
  const errorId = useId();

  // Check once the form has been submitted, then again on every change.
  const invalid = submitted && start === null;

  function submit(event: FormEvent) {
    event.preventDefault();
    setSubmitted(true);
    setSent(start !== null);
    // `id` lands on the combobox, so focus can go straight back to the field that failed.
    if (start === null) document.getElementById(fieldId)?.focus();
  }

  return (
    <form onSubmit={submit} noValidate style={{ display: "grid", gap: "1rem", maxWidth: 360 }}>
      <div style={{ display: "grid", gap: "0.375rem" }}>
        {/* The label and the message are the app's markup: the component only marks itself invalid. */}
        <span id={labelId} style={{ fontWeight: 500, color: invalid ? "var(--ss-danger, #dc2626)" : undefined }}>
          Start date
        </span>
        <DatePicker
          id={fieldId}
          value={start}
          onChange={(next) => {
            setStart(next);
            setSent(false);
          }}
          // aria-invalid on the combobox, data-invalid on the root and trigger.
          invalid={invalid}
          aria-labelledby={labelId}
          aria-required="true"
          aria-describedby={invalid ? errorId : undefined}
        />
        {invalid && (
          <p id={errorId} style={{ margin: 0, color: "var(--ss-danger, #dc2626)" }}>
            The start date is required.
          </p>
        )}
      </div>
      <div>
        <button type="submit" style={{ background: "transparent" }}>
          Continue
        </button>
      </div>
      <p role="status" style={{ margin: 0 }}>
        {sent ? `Sent: ${start}` : ""}
      </p>
    </form>
  );
}
```

Press Continue with no date picked: the field turns red, the message appears and focus returns to the picker. Pick a day and the error clears.

## What `invalid` does

[`invalid`](https://slotsmith.dev/components/date-picker/api/#prop-invalid) (default `false`) sets:

- `aria-invalid="true"` on the trigger, the element with `role="combobox"`.
- `data-invalid` on the root and on the trigger, for your own styles.
- In the default stylesheet, the trigger's border and focus ring in the danger colour, read from `--sdp-danger`, then the shared `--ss-danger` (`#dc2626` light, `#f87171` dark). See [Theming](https://slotsmith.dev/theming/).

In a plain HTML form, give the picker a [`name`](https://slotsmith.dev/components/date-picker/api/#props): its root holds a hidden input of that name per picked day, written as [`valueFormat`](https://slotsmith.dev/components/date-picker/guides/value-format-and-time-zone/) says, so the form submits the value rather than the text in the field. A range submits its start, then its end.

The picker does not validate and does not render a message. A rule such as "the end must be after the start" belongs in your form code; the picker shows the result.

## Tying the message to the field

The naming and validation props you pass to `DatePicker` go to the combobox. Give the message an `id` and pass it as `aria-describedby`, which every major screen reader reads after the field's name. `aria-errormessage` is meant for this too, but support is uneven, so keep `aria-describedby` beside it. Add `aria-required="true"` for a required date.

The `id` you pass also lands on the combobox, so after a failed submit `document.getElementById(id).focus()` moves focus to the field, as the demo does.

The demo colours its label and message with `var(--ss-danger, #dc2626)`, to match the border in both themes; the message text carries the meaning, so colour is not the only signal. The [ready-made adapters](https://slotsmith.dev/components/date-picker/adapters/) show each library's own error style on the trigger. The [autocomplete's validation guide](https://slotsmith.dev/components/autocomplete/guides/validation/) covers the same pattern for a picker.
