Design system#
Where the tokens live#
Every color, font stack, corner radius and transition used by this
site is defined in exactly one place: the two token blocks at the
top of docs/_static/theme.css. The first :root block holds the
light palette; the html.dark block directly beneath it holds the
dark palette (Shibuya switches modes by setting the dark class on
the root element). Everything below those blocks only references
the tokens; no other CSS file, template or directive may contain a
color literal.
To change the accent, edit --rot-accent and --rot-accent-hover
in both blocks, and set the ACCENT_COLOR tunable in
docs/conf.py to the nearest radix family name so the surfaces
Shibuya owns stay in sync. Nothing else needs to change.
The palette#
One accent color, used for links, active navigation, focus rings and card hover borders. Nothing else gets a brand color; admonition colors are semantic and stay functional. All pairs pass WCAG AA:
The values themselves live only in theme.css; this page cites
tokens by name so the numbers cannot go stale. Contrast ratios from
the last audit, measured against --rot-bg in each mode:
Token |
Light mode |
Dark mode |
|---|---|---|
|
4.95:1 |
7.08:1 |
|
16.14:1 |
14.73:1 |
|
5.80:1 |
7.18:1 |
Run a contrast check on both modes whenever a token changes; the
accent must keep at least 4.5:1 against --rot-bg and
--rot-surface.
Radius, typography, motion#
One corner radius scale:
--rot-radius(6px), applied to cards, dropdowns, images, tables and code blocks. Do not introduce a second radius.Two font stacks, both system-first, each behind one variable:
--rot-font-sansfor body and headings,--rot-font-monofor code. No Google Fonts. A webfont added later must be self-hosted in_static/fontswithfont-display: swap.Prose is capped near 72 characters. Tables and code blocks may use the full content column but scroll inside their own containers; the page never scrolls sideways.
Motion is limited to hover and focus transitions under 150ms (
--rot-transition), with one exception: collapsing a sidebar is a movement rather than a state change, so it eases its width over 240ms (--rot-transition-panel). Both honourprefers-reduced-motion, which disables them.
Which component for which job#
Job |
Component |
|---|---|
A set of links to related pages, each with a one-line pitch |
|
The same task shown against different backends or languages |
|
Advanced or rarely used options that would bloat the main flow |
|
A dataset the reader will want to sort or search |
|
A short warning or side note |
A standard admonition ( |
Page skeleton for a new module section#
Copy the structure of the data access section, the worked example:
An intent paragraph: what the subpackage is for and what design idea unifies it.
A card grid, one card per submodule, each with an
{iconify}`tabler:...`icon, the resource name, and one line of description, linking to the generated API page.A quickstart as tabs, one minimal working snippet per data source or task.
Optional: a sortable table for enumerable facts, dropdowns for tuning knobs.
Common patterns: three to five realistic end-to-end snippets.
The generated API pages come for free: add the module to
docs/api/index.rst and autosummary builds the reference page the
cards link to. Icons must be present in
_static/vendor/iconify-preload.js to render offline; extend that
file when you use a new icon (see the URL pattern in the file
header of conf.py’s iconify section).