# react.form.styles
An optional stylesheet for the `react.form` elements, as a function that
returns CSS text. The elements are fully usable without it: they render
plain, accessible HTML with stable `caps-*` class names, and a browser's
default styles work. This gives them a clean GOV.UK-like look in one line,
and every colour, the radius and the font are CSS custom properties, so an
app can restyle it with its own CSS without calling it again.
```tsx
// app/layout.tsx: a server component, so the CSS is in the HTML, not in JavaScript
import { formStyles } from "#fune/react.form.styles@^1";
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<head><style>{formStyles({})}</style></head>
<body>{children}</body>
</html>
);
}
```
Or write it to a file at build time and link it like any other stylesheet:
```ts
import { writeFileSync } from "node:fs";
writeFileSync("public/forms.css", formStyles({ accent: "#00703c", radius: "4px" }));
```
## The theme
Every field is optional; `{}` is the default, and an empty string is the same
as leaving a field out.
| Field | Custom property | Default |
|---|---|---|
| `text` | `--caps-form-text` | `#0b0c0c` |
| `muted` | `--caps-form-muted` (hints, counts) | `#505a5f` |
| `background` | `--caps-form-background` (inputs) | `#ffffff` |
| `border` | `--caps-form-border` | `#0b0c0c` |
| `accent` | `--caps-form-accent` (buttons, ticked boxes) | `#1d70b8` |
| | `--caps-form-accent-text` (text on the accent and warning buttons) | `#ffffff` |
| | `--caps-form-secondary` (secondary button, prefix and suffix) | `#f3f2f1` |
| `focus` | `--caps-form-focus` (the focus ring) | `#ffdd00` |
| | `--caps-form-focus-text` (text on focus yellow, the ring's dark edge) | `#0b0c0c` |
| `error` | `--caps-form-error` | `#d4351c` |
| `radius` | `--caps-form-radius` | `0` |
| `font` | `--caps-form-font` | the system UI font; no web font is loaded |
| `scope` | | none: rules apply to the whole page |
| `dark` | | `media` |
- **scope** is one selector, such as `.signup` or `main > form`: every rule
becomes `.signup .fune-field`, and the custom properties are set on
`.signup` instead of `:root`, so the styles stay inside that container.
- **dark** is `media` (the dark palette under
`@media (prefers-color-scheme: dark)`), `attribute` (under
`[data-theme=dark]` on any ancestor, or on the scope's element itself, for
a site with its own theme switch) or `none`. The dark palette replaces the
colours, the theme's included, because a colour picked for a light page is
rarely readable on a dark one; to theme dark mode too, set the custom
properties in your own dark rule (or use `dark: "none"` and do it all
yourself).
To theme without calling again, override the properties anywhere after the
stylesheet: `.checkout { --caps-form-accent: #00703c; --caps-form-radius: 6px }`.
Theme values are put into the CSS as they are, so anything that could end a
declaration or a rule, or close the `<style>` element, is refused rather
than escaped: `;`, `{`, `}`, `<`, `\`, `/*`, control characters, a quote left
open, and `>` except in the scope (where it is the child combinator). A scope
must be one selector, since in `.a, .b` only `.b` would be scoped. The error
names the field: `invalid theme.accent: ";" is not allowed, it could break
out of the stylesheet`.
## What it styles
Every class the `react.form` elements render: `fune-field` (and
`fune-field--error`, a red bar on the left, as GOV.UK marks a field in error),
`fune-fieldset`, `fune-label`, `fune-legend` (`--heading`),
`fune-legend-heading`, `fune-hint`, `fune-error-message`,
`fune-visually-hidden` (the standard clip pattern: hidden on screen, read by
screen readers), `fune-input` (`--error`, `--width-2`, `--width-4`),
`fune-input-wrapper`, `fune-input-prefix`, `fune-input-suffix`,
`fune-textarea` (`--error`), `fune-character-count__message` (`--over`),
`fune-select` (`--error`), `fune-checkbox`/`fune-radio` with their `-input`,
`-label` and `-hint`, `fune-checkboxes--small`, `fune-radios--small`,
`fune-radios--inline`, the `-divider`s, `fune-date-input` and
`fune-date-input-item`, `fune-password-field__toggle`,
`fune-password-checklist` and its items (`--met` gets a tick and `--unmet` a
dash, so the state is not shown by colour alone), `fune-button`
(`--secondary`, `--warning`, `--loading`, and `disabled` or
`aria-disabled="true"`) and `fune-error-summary` with its `-title`, `-list` and links.
The choices, from the GOV.UK Design System and WCAG 2.2:
- **Focus is loud**: a 3px yellow outline with a dark edge on checkboxes,
radios and the error summary, the input border thickened inside the yellow
on inputs, and a yellow button with a dark underline, so it shows on any
background, light or dark (WCAG 2.4.7 and 2.4.13).
- **Targets are at least 44px**: inputs, buttons, the password toggle and
each checkbox and radio row (its label is clickable too). Checkboxes and
radios are the native inputs, 40px, coloured with `accent-color`, so they
keep every browser's keyboard and screen reader behaviour.
- Text is 19px (1.1875rem), as GOV.UK uses for form text, and inputs inherit
it, which also stops iOS zooming into a focused input.
Sources: GOV.UK Design System, https://design-system.service.gov.uk/
(components: text input, error message, checkboxes, radios, date input,
button, error summary; "Colour" https://design-system.service.gov.uk/styles/colour/);
WCAG 2.2, 2.5.8 Target Size and 2.4.13 Focus Appearance
https://www.w3.org/TR/WCAG22/; MDN, `accent-color`
https://developer.mozilla.org/en-US/docs/Web/CSS/accent-color.