Components and component design

Interfaces are built from components, and how a component is designed decides most of what a person can do with the interface it appears in. Components stand on their own as a subject: what they are, what designing one involves, what each one can honestly promise, and what happens when they are put together. Design systems come after that, as the answer to the last of those questions.

What a component is

A component is a bounded piece of interface that can be used more than once: a button, a field with its label, a tab set, a card, a navigation bar, a whole search results region. It bundles four things behind a single name: markup and the semantics that markup carries, presentation, behaviour, and whatever state it keeps. It also exposes a small interface through which the rest of the application configures it, usually some combination of properties, slots for content, and events it emits back.

The idea is older than the frameworks that made it fashionable. An HTML select element is a component: it has semantics, presentation, keyboard behaviour, an open and closed state, and a small configuration surface, and it was written once and reused by everybody. What frameworks added was the ability for anybody to define new ones, which is both the reason component libraries exist and the reason accessibility became something each team has to re-establish rather than inherit.

Components nest, and that is the point of them. A field is a label and an input and a message; a form is fields and a submit button; a page is regions of forms and lists and navigation. So a component is always simultaneously a whole, from the inside, and a part, from the outside, and the two views disagree about what it is responsible for. Almost everything in this section follows from that disagreement, and the levels at which it plays out are set out in the assembly hierarchy.

What component design is

Component design is the practice of deciding those boundaries and behaviours. Not drawing the button: deciding what the button is, what it does when operated by a keyboard, a pointer, a screen reader, a voice, or a switch, what it does when its label is four words long in English and eleven in German, what it does when it has no room, what it does while it waits, and what it does when the thing it triggers fails.

Put concretely, designing a component means settling at least the following, and settling them as decisions rather than as whatever the implementation happened to do.

  • Its boundary. What is inside it, what is passed into it, and what is deliberately left outside.
  • Its semantics. What it is, in terms a machine can read: which element or role, what name, what relationships to the things around it.
  • Its states. Every state it can be in (empty, loading, partial, invalid, disabled, stale, selected, expanded), and how each of those states is expressed to a person who cannot see it.
  • Its interaction. Which keys do what, where focus goes on each transition, what is announced and when, and what the touch and pointer behaviour is.
  • Its adaptation. How it behaves as space, text size, spacing, language, colour scheme and motion preference change, since those are user decisions and not device categories.
  • Its variants. Which alternative forms of the same thing exist, and what selects between them.
  • Its obligations. What the component guarantees, what it explicitly does not, and what it requires the author or the surrounding page to supply.

That last item is the one most often left implicit, and it is the one that makes the rest usable by somebody else. A component that guarantees the association between a label and a field, and states plainly that it cannot guarantee the label says anything meaningful, has told the truth. A component described only as accessible has not: it has omitted the conditions under which the description holds. What a well-made declaration contains is worked through in the framework landscape, which is also where the survey of what current libraries do and do not publish sits.

Component design is also where accessibility is cheapest. A decision taken once inside a component is inherited by every place it is used, and a decision not taken becomes a defect repeated at the same rate. This is why remediating pages is such poor value: the page is the symptom and the component is the cause.

Good components are not enough

Here is the finding that motivates the rest of these pages, and it is a negative one. Accessibility is not a property that survives being added together. A combobox that is correct on its own and a modal dialog that is correct on its own do not produce a correct dialog-with-a-combobox, because both of them want the Escape key and neither of them knows the other exists. Nothing in either component is broken. The composition is broken, and it is broken in a way that no test of either component can see.

This is not a fringe observation. The GOV.UK Design System states the limitation directly: using its styles, components and patterns does not remove the need for further research, design, development and testing at service level, and its contribution criteria require components to be tested inside realistic pages, with representative content, against real assistive technology combinations, with newly discovered problems written up as accessibility acceptance criteria.

WCAG makes the same point from the other end of the scale. Where several pages are needed to complete an activity, the complete process is the unit of conformance, and every page in that process must conform. The conformance boundary is therefore neither the component nor even the single page.

Between those two facts sits everything a component library does not tell you: what may be assembled with what, who owns which accessibility responsibility, which guarantees survive assembly, and what has to be evidenced again at each boundary. The failures themselves are enumerated in how composition fails and shown at work in worked examples.

Managing rendering and interaction between components

If the gap is between components rather than inside them, it cannot be closed by better components. It needs somewhere to record the rules that govern how a set of components render and interact together: which of them may be used at all, which may contain which, which variant is selected for whom, which one owns focus in a given state, how the keyboard grammar resolves when two of them want the same key, which one is allowed to speak when several have something to announce, and how they reflow when there is not enough room for all of them.

That record is a design system. It is worth being precise about the distinction, because the two words are used interchangeably and they are not the same thing. A component library is a set of parts. A design system is the set of decisions about which parts are used here, in what form, in what combinations, with which responsibilities assigned where, and, if it is honest, with the evidence for each decision and the record of what is still unknown attached to it.

So the dependency runs in one direction only. Component design does not require a design system: teams design components, and assemble them, whether or not anybody has written the rules down. A design system does require components, because components are what it is making decisions about. That is why this section sits before the design system pages rather than inside them, and why those pages refer back here instead of restating the argument.

I have kept them apart for a second reason. If the assembly problem only appeared inside a discussion of my own design system it would read as an argument for that design system, which invites the reader to discount it. The failures are observable without reference to any particular system, and that is how they are set out here. What follows from them for a design system, including the rule that guarantees do not union, is in what this means for a design system.

What this section holds

  • The framework landscape: the three-layer shape most frameworks share, the split between styled and headless libraries, and the one thing almost none of them publish.
  • The assembly hierarchy: six levels from primitive to complete process, and why accessibility responsibility changes character at each one.
  • How composition fails: eight classes of failure that are invisible in isolation and appear only on assembly.
  • Worked examples: six ordinary compositions, taken apart. A combobox in a dialog, a clickable card, a filter panel, a grid in a tabpanel, a multi-step form, and responsive navigation.
  • What this means for a design system: composition as a contract, the rule that guarantees do not union, and what the relationship does to component use in the other direction.
  • State and its propagation: five classes of state, eight propagation rules, a worked contract, and the predicates a validator could enforce.
  • Testing across the hierarchy: evidence matched to the level of the claim, a method for testing flows, and the hard limit of automation.
  • What is still open: the questions this work has not answered, and what I am carrying into the specification.

Where this connects

The accessibility-focused design system I am building on top of all of this is described in the section that follows. Its component contracts are the mechanism by which a guarantee carries its preconditions, its evidence and uncertainty records are how a claim states the configuration it was tested in, and its open questions include the ones this section leaves unresolved.

The research note behind these pages is research/COMPONENT-FRAMEWORKS.md in the project repository, which carries the full source list.