Design system
These are the foundations, controls and composition rules used to build this desktop. Explore the examples, then open their usage notes for the exact implementation.
Using this reference
This page and its reference data are the canonical design-system contract. Explicit direction from Sylvain takes precedence. Production components and tokens implement the contract; discrepancies must be reconciled rather than copied as new conventions.
Shared components are reusable implementations. Shared CSS requires the documented native markup. Compositions describe relationships rather than universal components. Demonstrations explain behaviour and do not establish a new site-wide control. Journal experiments are not production conventions unless explicitly adopted here.
- Find the matching entry and inspect its production source before building. Reuse shared components and CSS; preserve the documented HTML and accessibility behaviour.
- Use the listed component aliases. Theme files change tokens, not component selectors. Keep surface and text colours paired; never substitute approximate local colours.
- Keep bespoke compositions specific to their feature. Reuse an established pattern before extracting a new abstraction; there is no universal card.
- When a recurring convention changes, update the production implementation, this reference and its checks together. A code change alone does not approve a new design convention.
- Verify direct routes, browser history, keyboard focus, reduced motion, narrow screens and contrasting themes. Review screenshots against the approved design; do not automatically accept new visual baselines.
Reference data (JSON) is generated from the same records as these notes.
Find a component or pattern
- Theme behaviour · Foundation
- Colour and contrast · Foundation
- Typography · Foundation
- Nested surfaces · Composition
- Split surfaces · Composition
- Icons · Foundation
- Motion and focus · Shared CSS
- Panel actions · Shared component
- Study fields and selects · Shared component
- Selection controls · Shared component
- Range inputs · Shared CSS
- Panel navigation · Shared component
- Toolbar and collection · Demonstration
- Overflow and edge fades · Shared component
Existing page compositions
Use the owning feature when extending a page. These compositions retain their individual layouts.
- Desktop and route panels
Use the shell's panel slot and existing activePanel values. Preserve direct routes and the existing Astro navigation lifecycle; do not add another router.
src/components/DesktopShell.astro- Launcher tiles
Each tile in src/components/launcher owns its composition. Shared tokens connect them; a universal card would erase their differences.
src/components/launcher/WorkLauncher.astro- Journal articles
Use the article layout, journal-posts data and the relevant study component. Experimental specimens do not become production conventions automatically.
src/components/JournalPostLayout.astro- Work entries
Use work-items data and its existing presentation choices. Tool pages retain their dedicated components and direct routes.
src/pages/work/[slug].astro- Context notes
Reuse the existing contextual-note structure and theme-note aliases when the same inline disclosure pattern is needed.
src/components/ContextNote.astro- Media collections
Use the appropriate existing media feature: ArtefactCarousel, TypeArchive or the 3D Work implementation. Their behaviour is feature-specific.
src/components/ArtefactCarousel.astro
Themes
Usage and source · Foundation
One token system powers the entire interface. The preview can follow the site or explore a local variation.
- Reuse ThemeControls for the theme panel and its existing saved-state behaviour. Do not build another preference store.
- The specimen presets and its buttons affect only the embedded preview. Its field layout and Reset fields button are demonstration UI, not general-purpose production components.
- System preferences apply until explicitly overridden. Reset restores the authored preference behaviour. Theme changes must preserve reduced motion.
- Source
tokens.csssrc/styles/base-theme.csssrc/data/theme-controls.tssrc/components/launcher/ThemeControls.astrosrc/scripts/theme-controls.ts- Token hooks
--theme-hue--theme-lightness--theme-chroma--surface-distance--contrast-low--contrast-high- States to preserve
- Site · Local preview · Light and dark · High contrast · Reset
- In use
- Theme controls
Foundations
Colour and contrast
Primary
Supporting
Primary
Supporting
Usage and source · Foundation
Derive surfaces from the colour axis and use the ink generated for each surface.
- Component CSS consumes its own aliases from tokens.css, not source colour tokens or literal colours.
- Use high ink for primary content and supporting ink for small readable labels. Low ink is available for lower-emphasis decoration; it is not a substitute for supporting text.
- Keep borders, focus and selection token-controlled. Do not use the accent as a text-bearing fill without a verified matching foreground.
- Source
tokens.csssrc/styles/base-theme.csssrc/scripts/color-contrast-fallback.ts- Token hooks
--color-surface-base--color-surface-sunken--color-text-base-high--color-text-base-supporting--color-text-sunken-high--contrast-supporting- States to preserve
- Base · Sunken · Dark · Chromatic · High contrast
- In use
- Desktop
Typography
Room to think.
A clear starting point
A little space makes the important things easier to see.
Use familiar words, a comfortable measure and a consistent rhythm.
Details that help without competing.
Usage and source · Foundation
42dot Sans and a shared modular scale give each text role a consistent size.
- Map feature aliases to semantic type roles; do not invent local font sizes for an existing role.
- Heading levels describe document structure, not visual size. Keep one page h1 and a meaningful heading order.
- Keep rem-based sizing and allow text to wrap when the visitor increases their text size. The scale slider here is a local demonstration.
- Source
tokens.csssrc/lib/type-scale.tssrc/scripts/type-scale-fallback.ts- Token hooks
--font-sans--type-base--type-ratio--type-rounding--font-size-body--font-size-supporting- States to preserve
- Display · Panel title · Reading · Body · Supporting · Larger text
- In use
- Profile
Radius and padding
Usage and source · Composition
A nested curve follows the outside curve, its padding and the border between them.
- For inset shapes, subtract padding and border from the outside radius and clamp at zero.
- Keep corner shape and radius controlled by the owning component aliases. The geometry sliders illustrate the relationship; their values are not saved site settings.
- Source
tokens.csssrc/styles/segmented-control.css- Token hooks
--segment-radius--segment-option-radius--segment-padding--segment-border-width- States to preserve
- Square · Rounded · Visible border
- In use
- Type archive
Radius and gap
Usage and source · Composition
A header and body share an outside silhouette with smaller corners where they meet.
- Use the existing cap geometry: joining corners follow half the outside radius, bounded by the shared maximum. Preserve the independent gap.
- Apply the feature's surface and edge aliases. Do not turn Work, Journal and the other bespoke compositions into one configurable card.
- Source
tokens.csssrc/styles/surface-edge.csssrc/components/launcher/WorkLauncher.astrosrc/styles/work-panels.css- Token hooks
--launcher-cap-radius--launcher-cap-gap--surface-cap-radius-max--surface-cap-radius-factor- States to preserve
- Small and large radii · Visible border · Narrow screen
Iconography
Usage and source · Foundation
Use the site's prepared Lucide SVGs and the owning control's dimensions.
- Import prepared SVGs from src/icons. Decorative icons are hidden from assistive technology; icon-only actions require an accessible name.
- Use currentColor and component size aliases. Do not restore the archived custom-icon generator or introduce an icon runtime.
- Source
src/icons/README.mdsrc/icons/SOURCE.jsonsrc/components/PanelAction.astro- Token hooks
--panel-action-icon-size- States to preserve
- Labelled · Icon-only · Keyboard focus
- In use
- Work
Motion
Selection
Hover and focus
Reveal
Collection details
12 items · Updated today
Usage and source · Shared CSS
Motion explains selection and position; the same state remains clear without animation.
- Reuse SegmentedControl for moving selection and data-hover-list with the existing helper for list highlights.
- Keep focus visible independently of hover. Respect prefers-reduced-motion before starting scripted animation and when it changes.
- The disclosure example and usage notes share native details/summary and the same CSS content reveal and chevron motion. Opening and closing animate where supported, and become immediate with reduced motion; the collection content is illustrative.
- Source
src/styles/segmented-control.csssrc/styles/hover-list.csssrc/scripts/hover-list.tssrc/scripts/desktop-shell.tssrc/styles/design-system.css- Token hooks
--segment-duration--hover-list-duration--hover-list-ease--ds-motion-duration--ds-motion-ease- States to preserve
- Hover · Keyboard focus · Selected · Reduced motion
- In use
- Journal
Example inside DesktopShell. Adjust import paths to the calling file.
<ul data-hover-list=".collection-link">
<li><a class="collection-link" href="/work">Work</a></li>
</ul>Components
Actions and links
Examples stay on this page.
Usage and source · Shared component
Reuse PanelAction for Back, Close and Open controls. Navigation uses links; local actions use buttons.
- The default is a link and requires href. Use as="button" only for an action, never to imitate navigation.
- Supply text for the Back treatment, or label for an icon-only control. The icon choices are close and arrow.
- The buttons here provide local feedback so the examples keep you on this page. Production navigation still uses real destinations. This is not a generic submit-button component.
- Source
src/components/PanelAction.astrosrc/styles/panel-action.css- Token hooks
--panel-action-background--panel-action-color--panel-action-radius--panel-action-focus-color- States to preserve
- Back · Close · Open · Disabled button · Keyboard focus
Example inside DesktopShell. Adjust import paths to the calling file.
---
import PanelAction from "../components/PanelAction.astro";
---
<PanelAction href="/work" text="Work" />
<PanelAction href="/" label="Close panel" />
<PanelAction as="button" icon="arrow" label="Next item" />Study fields and selects
Usage and source · Shared component
StudyFrame supplies the real field and button treatment used in Journal studies. Native selects share their own CSS.
- Use StudyFrame around a Journal study; as="div" is available when figure semantics do not fit. Label every input and connect feedback with aria-describedby.
- Use native select elements with class native-select. StudyFrame maps the closed select to the same background and foreground as its other fields; the open menu keeps its own surface pair. Keep feature mappings to --select-*; native browser fallback remains available.
- Contact and tool fields have their own feature contracts. Do not copy the reference page's ds__field or ds__button helpers into a feature, or apply study styling to unrelated forms.
- Source
src/components/StudyFrame.astrosrc/styles/study-frame.csssrc/styles/native-select.css- Token hooks
--study-control-background--study-card-color--study-control-radius--select-field-background--select-field-color- States to preserve
- Editable · Disabled · Invalid · Reset · Keyboard focus
- In use
- Native controls study
Example inside DesktopShell. Adjust import paths to the calling file.
---
import StudyFrame from "../components/StudyFrame.astro";
---
<StudyFrame>
<label for="view"><span>View</span>
<select id="view" class="native-select"><option>List</option><option>Grid</option></select>
</label>
</StudyFrame>Selection
Usage and source · Shared component
SegmentedControl renders native radios. Checkboxes and the theme switch use established native markup and shared CSS.
- Give each radio group a unique name, a label, two to eight uniquely valued options and a matching selected value. Use disabled for an unavailable group. Named slots can provide option icons.
- Keep native radio keyboard behaviour. The selected value must remain distinguishable with motion disabled.
- Checkboxes use a primitive-study ancestor and data-study-checkbox. Theme switches use the ThemeControls markup and theme-toggle classes, following the range input: off slightly darkens its unfilled track, on uses its filled track, and the solid thumb shares the range thumb fill and dimensions. High contrast uses the range track directly. Keep the thumb flush, without an inset ring. Thumb borders are reserved for high contrast; keyboard focus remains visible in every mode. Neither requires a custom JavaScript state library.
- Source
src/components/SegmentedControl.astrosrc/styles/segmented-control.csssrc/styles/primitive-study.csssrc/styles/theme-controls.csssrc/components/launcher/ThemeControls.astro- Token hooks
--segment-background--segment-selected-background--segment-radius--study-checkbox-background--theme-control-switch-background--theme-control-switch-thumb-background--theme-control-switch-thumb-inset- States to preserve
- Checked and unchecked · Selected · Disabled · Keyboard arrows · Reduced motion
Example inside DesktopShell. Adjust import paths to the calling file.
---
import SegmentedControl from "../components/SegmentedControl.astro";
---
<SegmentedControl name="collection-view" label="Collection view" value="list"
options={[{ value: "list", label: "List" }, { value: "grid", label: "Grid" }]} />Ranges
Usage and source · Shared CSS
Native range inputs share the site's track, thumb, focus and progress treatment.
- Use a labelled input[type=range] with explicit bounds and an associated output. Keep arrow-key operation native.
- The shell installs range enhancement. Call syncRangeInput when feature code sets a value programmatically.
- The amount meter is an illustrative output, not a reusable progress component.
- Source
src/styles/range-input.csssrc/scripts/range-input.tssrc/scripts/desktop-shell.ts- Token hooks
--range-track-background--range-thumb-background--range-focus-color- States to preserve
- Minimum · Maximum · Disabled · Keyboard adjustment
- In use
- Theme
Example inside DesktopShell. Adjust import paths to the calling file.
<label for="amount">Amount <output for="amount">50%</output></label>
<input id="amount" type="range" min="0" max="100" value="50" />Patterns
Toolbar and collection
- Notes12 entries
- Studies6 experiments
- Projects4 projects
- Archive28 items
Usage and source · Demonstration
This composition demonstrates a shared selector with a local list/grid arrangement.
- Reuse the selector, not the demo collection as a universal layout. Production collections retain their own feature source.
- The ds-collection classes and sample data belong to this demonstration. List/grid changes must retain readable order, keyboard control and a reduced-motion state.
- Source
src/components/design-system/Patterns.astrosrc/components/SegmentedControl.astrosrc/styles/design-system.css- Token hooks
--ds-collection-duration--ds-collection-ease- States to preserve
- List · Grid · Narrow screen · Reduced motion
- In use
- Production 3D collection
Overflow and edge fades
Scroll to reveal more. A soft blur and fade mark remaining content and clear at each end.
Usage and source · Shared component
ScrollFade marks remaining content and clears each edge when there is nothing more to scroll.
- Place data-scroll-fade on the direct scrollable child of ScrollFade. The shell initialises the helper and handles teardown.
- Provide a keyboard-focusable, labelled scroll region when its content needs keyboard scrolling. The feature owns its height and overflow styling.
- Use normal end padding; do not add empty clearance for the fade. Keep content available without JavaScript and respect reduced motion.
- Source
src/components/ScrollFade.astrosrc/styles/scroll-fade.csssrc/scripts/scroll-fade.tssrc/scripts/desktop-shell.ts- Token hooks
--scroll-fade-start-distance- States to preserve
- Start · Middle · End · No overflow · Keyboard scrolling
Example inside DesktopShell. Adjust import paths to the calling file.
---
import ScrollFade from "../components/ScrollFade.astro";
---
<ScrollFade>
<div class="collection-scroll" data-scroll-fade tabindex="0" role="region" aria-label="Collection">
<!-- Feature content; collection-scroll owns height and overflow-y: auto. -->
</div>
</ScrollFade>