Synapse devlog

A small, accessible design system

Synapse has a design system, and it is a very small one. There is no theme engine or config objects or variants API with thousands of props per button. What we have is actually a handful of components, a file of design tokens and a base stylesheet. That restraint is precisely the point and it is what is keeping the thing accessible and maintainable.

Tokens first, components second

The foundation is two CSS files. One is the tokens: colours, the type scale, spacing steps… The other is a base reset and the primitive element styles (.btn, form fields, panels [anything shared between components]). Everything visual refers back to those tokens.

.btn {
  border: var(--bw) solid var(--line);
  border-radius: var(--radius);
  color: var(--ink);
  padding: var(--space-sm) var(--space-md);
}

The base identity lives in plain CSS variables, so it’s portable. This very blog is a separate project, and it reuses the same tokens and base styles directly.

Keeping the component count low

Every component you add is a thing to document, test, keep accessible and keep consistent. This makes the bar for adding one is high: a component earns its place only when the same markup and behaviour show up in three or four spots. A modal, a menu, a button, a field, a few more. When something is needed once, it stays a one-off.

The payoff is that the whole system fits in your head and you are never hunting through variants to find the button you want, because there are only a couple and they are meant to compose.

Accessible because of the platform, not on top of it

The cheapest way to be accessible is to use the elements the browser already made accessible (and then not break them :D). The modal is a native <dialog>. That choice hands you the focus trap, the focus restoration when it closes, Escape to dismiss, and making the rest of the page inert, none of which I had to write.

The same logic runs through the rest. Buttons are buttons and links are links, so keyboard and screen-reader behaviour comes for free. Forms are real <form> elements, so pressing Enter submits, as people expect. Icons that carry meaning get a label; icons that are decoration are hidden from assistive tech. None of this is special, it’s a boring use of what already exists and it is mostly the discipline of not reaching for a <div> with a click handler when a real element exists.

Tests select elements by their role (getByRole('button', { name: ... })), the way an assistive tool would, so a component that is inaccessible is also untestable and gets caught. And an automated accessibility scan runs over the key screens, checking against WCAG A and AA, so a regression shows up in the suite rather than in someone’s way. Automated scans (I’m using axe) are not a silver bullet but they help catch a few things. For actual accessibility testing one need to do an actual audit with accessibility specialists (which - as a disclaimer - I have not done yet). If you are an accessibilty agency and you want to audit a small FOSS project, Synapse needs you :)

Why Svelte suits this

A small design system wants a small runtime and as little ceremony as possible between the markup you write and the DOM that ships, whic is why I chose Svelte.

Components are close to plain HTML and CSS. A button component is a <button> with a class and a slot for its label, and its styles sit right next to it, scoped by default so they cannot leak. There is no styling library layered on top, className soup, runtime theme provider (you name it). The tokens are CSS variables and the components are HTML, which is exactly the level a design system should live at.

Each component owns its CSS without a naming convention to enforce or a bundler plugin to configure. Svelte gives it to you with no extra setup.

And all that disappears at build time:there is no framework runtime shipped to render a button, which keeps the components light. For a lightweight system whose idea is to be small and to gets out of the way, a compiler that also gets out of the way felt like the right foundation.

Take-aways from this

Start with the tokens and the base stylesheet, and resist adding components until the repetition forces your hand. Lean on real HTML elements for behaviour and accessibility, and test by role and semantic markup. Keep the whole thing small enough to hold in your head. Then, “clean”, “lean” and “accessible” become the same shared goal.

← All writing