# Schemas

> Describing structured data once, so its fields, its checks and its type all follow from one description.

A schema describes the shape of a piece of data: what each part is, what it is
called, and what it may hold. An interface that edits structured data — a form,
an inspector, a table — is easiest to get right when it is derived from one such
description, rather than restating it in every field.

## Best practices

**Describe the data once.** The kind of each value, its label, its limits and
whether it may be absent belong in one description. The fields shown, the
checks applied and the type the program sees all follow from it, so they cannot
disagree.

**Give every value a kind.** A number, a date, text, a yes-or-no, a choice among
named options, a record of named fields, a list. The kind decides how a value is
read and shown, which control edits it, and what it means to be valid.

**Let one of several records be told apart by a tag.** Where a value may be one
kind of record or another — a card or a transfer, a heading or a paragraph —
each kind has its own fields, and the value says which it is in a field kept for
that. Only the fields of the kind it is are asked for and checked; a value that
names no kind is at fault where it names it. Until one is chosen there is no
value at all, unless the schema says which a new record starts as.

**Keep what two kinds share when one becomes the other.** Changing which kind of
record a value is keeps the fields the two have in common — the same key, and a
value of the same kind — and starts the rest as a new record does. What only the
old kind had is let go.

**Let a schema refer to a schema, itself included.** A folder holds folders; a
comment has replies. The reference is followed when a value reaches it and not
before, so a schema that contains itself can be stated, a value of any depth is
checked as far as it goes, and a new value starts with nothing where the schema
meets itself. What the reference says of its own — its label, whether it is
optional — holds in that place only.

**Say where a problem is, and what kind it is.** A check of a whole record
reports each problem with the path to the value at fault — the third tag, the
price — and its kind: missing, of the wrong kind, too small, too long. The words
for it are the interface's, in its language; the report itself is neutral.

**Distinguish absent from empty.** A value that may be absent is absent, not
zero and not an empty string; a required text that is empty has not been given.
Start a new record with nothing entered rather than with invented defaults.

**Count characters as people see them.** A limit on the length of a text counts
what people perceive as characters — a family emoji is one — not the units a
computer stores.

**Offer choices as values with names.** A choice holds a stable value and shows
a label; the label may change, or be translated, without changing the data.

**Say what a value is for, beside its kind.** A description says, in a
sentence, what is wanted — shown with the field before anything is wrong. What
a text holds — an address for mail, a telephone number, a person's name —
chooses the keyboard it is typed on and what a browser may fill in; an address
or a link must look like one.

**State a default where there is one.** A new record starts from the defaults
its schema states and from nothing elsewhere; a default is checked against its
own schema like any value.

**Mark what is only shown.** A value that is read-only is shown, checked like
any other, and not offered for change — a field, a group or a whole record.

**Change data without disturbing what did not change.** When one part of a
record changes, the rest is the same data as before, so an interface showing it
need not redraw, and an unchanged edit is no change at all.

**Fit old data to a schema that has changed.** Keep each value that is still of
its field's kind and start the rest blank; leave a broken limit to be reported
rather than discarding the value. Told what the schema was, treat a field
renamed in place as the same field.

See also [Schemas of schemas](https://ui.publictheta.com/foundations/schema-schema.md),
[Editing values](https://ui.publictheta.com/foundations/use-field.md), [Numbers](https://ui.publictheta.com/foundations/number-type.md) and
[Dates](https://ui.publictheta.com/foundations/date-type.md).

## Behaviour

Each statement is verified by a test in Chromium, Chromium (touch), Firefox, WebKit, WebKit (touch), unless it names the environments it applies to or is marked open.

### Types

- A schema gives the TypeScript type of its values, with nothing to keep in step.
- A description, a default and being read-only say nothing about the type.
- A value checked against a schema is known to have its type.
- A variant gives the type of each of its records, told apart by the tag.
- A reference to a schema stated before it gives that schema's type, optional if the reference says so.
- Where a schema refers to itself its type is unknown, unless the reference is told it.
- A schema known only as a schema has values known only as unknown.

### Validation

- A value that meets its schema has no issues.
- An issue says where in the value it is, and what kind of problem it is.
- A value that is absent is an issue unless its schema is optional.
- A required text that is empty has not been given.
- A value of the wrong kind is an issue of type, and its limits are not checked.
- A choice must be one of its options.
- A date must be a day that exists, within its range.
- The length of a text is counted in characters as people see them.
- A text said to hold an email address or a URL must look like one.
- What else a text is said to hold changes how it is entered, not what is allowed.
- A read-only value is checked like any other.
- A list is checked for its length and each of its items.
- A pattern that is not a regular expression matches nothing, and is an issue rather than an error.

### Variants

- A variant's value is one of several records, checked against the fields of the one its tag names.
- A tag that names none of the variants is an issue at the tag, and no fields are checked.
- A variant of which none is chosen is absent: an issue unless the variant is optional.
- A variant within a record reports its problems under the record's path to it.
- A blank variant is not yet chosen, unless its schema states which it starts as.
- The variant a schema starts as must be one of its variants.
- A path leads to a variant's tag as a choice of the variants, and to a field of the variant the value is.
- A variant's tag is the same choice every time it is asked for.
- A value changed to another variant keeps the fields the two share by key and kind, and starts the rest blank.
- A field of the same key and another kind is not shared, and one of the same kind is made to fit.
- Changing to the variant a value already is changes nothing, and changing to none leaves nothing chosen.
- Whatever a value held, changed to a variant it has that variant's fields and no others.

### References

- A reference is checked as the schema it refers to.
- What a reference states of its own — a label, a description, optional, read-only — stands in place of its target's.
- A reference is read when a value first reaches it, never while the schema is stated, and once.
- A schema that contains itself checks any value that ends, as deep as the value goes.
- A blank value of a schema that contains itself ends: where the schema is met within itself it starts as nothing.
- A path leads through a reference to what it refers to.
- A reference that leads only back to itself describes nothing, and is refused.
- A reference that could not be read is read again when next it is reached.

### Fitting a schema that has changed

- A value that fits its schema is left as it is: the same value, not a copy.
- A field the schema has gained starts as a new record's does, and one it has lost is dropped.
- A part that is no longer of its schema's kind starts blank; one that only breaks a limit is kept.
- An absent part stays absent where it may be, and starts blank where it may not.
- Only what had to change is copied: every part that fits is the part it was.
- Given the schema a value was made for, a field renamed in place keeps its value.
- An option or a variant renamed in place keeps the values that named it.
- A field put where another stood, while that one stays, is not taken for it.
- An option that takes the place of one removed is not that one renamed.
- Whatever a value held, once fitted it is of its schema's shape, and fitting it again changes nothing.

### Values

- A blank value has every field, with nothing entered.
- A blank value starts from a default wherever its schema states one.
- A default its own schema does not allow is found, at the part that states it.
- A path leads to the part of a value, and to that part's schema.
- Replacing a part copies only what lies along its path.
- Replacing a part with itself returns the same value, so nothing re-renders.
- Replacing a part that does not exist yet makes the way to it.
- Replacing a part past the end of a list fills the way with nothing, and leaves no holes.
- A value of the wrong shape is replaced along the path, never taken apart into it.

## Reference implementation

React: `npx shadcn@latest add https://ui.publictheta.com/r/schema.json`
