Foundations
This guide covers Pathogen::* components, Lookbook previews, and host-app usage. It turns the five Pathogen qualities into practical design decisions.
Ownership
Section titled “Ownership”- Pathogen owns shared components, tokens, previews, and interaction patterns.
- Host apps own brand mapping and product-specific layout.
- Previews show how components behave in product-like work, not a separate documentation style.
Reusable UI should move into Pathogen when more than one surface needs it. Host-local components should still use Pathogen tokens, keyboard behaviour, and accessibility patterns.
A Good Pathogen Surface
Section titled “A Good Pathogen Surface”A good Pathogen surface is quiet, precise, accessible, compact, and trustworthy at the same time.
Quiet means the surface does not compete with the work. Neutrals hold the room. Accent and status colour appear only when they explain action, selection, focus, navigation, or state.
Precise means the parts agree. Type follows role. Spacing follows the 4px rhythm. Corners match the scale of the object. Dividers describe real structure.
Accessible means state can be perceived and operated. Focus is visible. Names match visible labels. Contrast, target size, reduced motion, and forced-colours support are design decisions.
Compact means dense information remains usable. Smaller controls still need spacing between neighbours. Validation text, focus rings, and row states need room.
Trustworthy means the interface feels stable and honest. Clinical workflows should not feel branded for its own sake. Clarity beats polish.
Building Blocks
Section titled “Building Blocks”Start with the job an element performs, then choose the smallest visual treatment that makes that job clear.
1. Intent
Section titled “1. Intent”Name the job before choosing classes: primary action, cancel, status, navigation, metadata, destructive confirm, selected row, validation error. Intent determines tone, emphasis, colour role, and placement.
2. Colour
Section titled “2. Colour”Use semantic roles (--pvc-color-*) in shared components. Palette stops such as primary-600 and neutral-200 are implementation details unless no role exists yet.
| Family | Use |
|---|---|
| Surface / muted / raised | Canvas, grouped regions, subtle layer changes |
| Border / border-strong | Edges, dividers, table rules, framed controls |
| Text / text-muted | Body copy, labels, hints, secondary information |
| Accent / accent-strong | Links, selected state, brand action, outline emphasis |
| Accent solid / solid-hover | Filled primary action |
| Danger / danger-strong | Error text, destructive outline and ghost actions |
| Danger solid / solid-hover | Confirmed destructive action |
| Success-strong / warning-strong | Normal-sized status text |
| Success / warning | Status icons, borders, large text, outline emphasis |
| Success solid / solid-hover | Filled success surfaces |
| Warning solid / solid-hover | Filled warning surfaces |
| Focus | Keyboard focus, separate from accent |
Accent, danger, success, and warning each have a four-step ladder: foreground pair for text and outlines, solid pair for filled surfaces. Use success-strong and warning-strong for ordinary status text; base success and warning are for icons, borders, and large text. Solid success and warning fills use --pvc-color-success-solid and --pvc-color-warning-solid (and their hover steps). Do not create those fills with raw emerald-* or amber-*.
Host apps theme accent through --color-primary-*. Shared components should not hard-code IRIDA green, Pathogen blue, or any other host brand.
Current migration state: Button, CopyableValue, Typography, Link, Tabs, and Form use semantic colour roles. Tooltip still mixes semantic roles with neutral-* / primary-*. Move touched code toward --pvc-color-* when the role exists.
3. Typography
Section titled “3. Typography”Sizes are role tokens (--type-*) consumed through Tailwind arbitrary-length utilities such as
text-[length:var(--type-body)]. Components and these docs share one scale.
| Role | Token | Size | Use |
|---|---|---|---|
| Meta | --type-meta |
12px | Hints, timestamps, grid footnotes |
| Control | --type-control |
14px | Labels, buttons, dense UI, inline code |
| Body | --type-body |
16px | Paragraphs, descriptions |
| Callout | --type-callout |
18px | Emphasised lead-ins |
| Section | --type-section |
20px | Section titles, lead paragraphs |
| Title | --type-title |
24px | Sub-page headings |
| Page | --type-page |
32px | Page titles via Heading |
14px and 16px carry most product work. Larger type should mark real hierarchy, not create display moments.
- Labels for settings and rows:
text-[length:var(--type-control)] font-semibold. - Muted copy:
--pvc-color-text-mutedor typographymuted. Supportingdefaults to control scale for captions and labels. Passsize: :metafor timestamps and footnotes;HeadingGroupmetadata uses meta scale by default.- Body weight stays normal. Heading weight steps deliberately:
Headinglevels 1–6 map to extrabold (page title), bold, then semibold. Levels 4–5 share callout scale; level 6 uses body scale. Extrabold is reserved for the single page title. - Page titles live in
Pathogen::Typography::Heading; hosts should not override them. - Set type left-aligned. Following the New Typography, hierarchy comes from size, weight, and
asymmetric placement — not centring or decorative uppercase. There is no uppercase display label
component; use
Supportingfor metadata labels.
Prefer text-[length:var(--type-control)] in new code. Existing text-sm is fine where it matches the role.
Migration from pre-token typography
Section titled “Migration from pre-token typography”| Removed | Replacement |
|---|---|
Pathogen::Typography::Eyebrow / pathogen_eyebrow |
Supporting with variant: :muted and size: :meta |
HeadingGroup#with_eyebrow |
with_metadata or Supporting above the heading |
responsive: on typography components |
Removed (was a no-op); use role tokens via components |
text-base … text-5xl in the Pathogen CSS bundle |
text-[length:var(--type-*)] or host-app Tailwind scale |
4. Spacing
Section titled “4. Spacing”Base unit: 4px.
| Step | Class | Good for |
|---|---|---|
| 4px | gap-1, space-y-1 |
Tight groups, icon padding |
| 8px | gap-2, space-y-2 |
Control internals, toolbar separation |
| 16px | gap-4, space-y-4 |
Forms, fieldsets, ordinary stacks |
| 24px | gap-6 |
Sections |
| 32px | gap-8 |
Major regions |
Line up labels, columns, and action groups to the same rhythm. Avoid custom page spacing unless a shared component absorbs the pattern.
5. Radius
Section titled “5. Radius”| Token | Size | Use |
|---|---|---|
--pvc-radius-control-sm |
2px | Checkboxes, tight primitives |
--pvc-radius-control |
4px | Inputs, inline code |
--pvc-radius-action |
6px | Buttons, tabs, grouped controls |
--pvc-radius-panel |
8px | Grids, fieldsets, larger framed surfaces |
Sharper corners suit dense clinical UI. Avoid rounded-xl on everyday product surfaces. Pills (rounded-full) are for radios, switches, avatars, and true pill controls, not general panels.
6. Elevation
Section titled “6. Elevation”| Level | Treatment |
|---|---|
| Base | Border + surface colour, no shadow |
| Grouped | Muted surface or stronger border, still no shadow |
| Overlay | --pvc-shadow-overlay for tooltips, popovers, menus |
Use borders and surface steps before shadow. Product UI stays mostly flat; floating layers cast the small shadow because they need to separate from the page.
7. Motion
Section titled “7. Motion”| Token | Duration | Use |
|---|---|---|
--pvc-duration-fast |
120ms | Control colour, border, background |
--pvc-duration-default |
160ms | General feedback |
--pvc-duration-overlay |
200ms | Tooltips, popovers, menus |
Animate colour, border, background, and opacity before position. Respect prefers-reduced-motion.
Surfaces
Section titled “Surfaces”A surface is a bounded work area: a panel, grid, fieldset, dialog body, or preview frame.
Use this recipe unless the workflow needs something else:
- Background:
--pvc-color-surfaceor--pvc-color-surface-muted - Border: 1px
--pvc-color-border - Radius:
--pvc-radius-panelfor larger frames;--pvc-radius-controlfor fields - Shadow: only for overlays
DataGridComponent is the main framed surface today. Hosts can compose the same recipe from tokens until a shared Panel component exists.
Layout
Section titled “Layout”- Build on a grid before adding emphasis.
- Let dividers show real structure.
- Use asymmetry only when the workflow is asymmetric.
- Leave room for focus rings, validation text, and dense row states.
- Let label, control, help, and action order do the work before adding icons or colour blocks.
Think product utility: GitHub, Linear, and public-sector data tools are closer references than landing pages.
Buttons
Section titled “Buttons”Buttons have two decisions: tone and emphasis.
| Decision | Values |
|---|---|
tone |
neutral, primary, danger |
emphasis |
solid, outline, ghost |
Tone And Emphasis
Section titled “Tone And Emphasis”Primary moves work forward: solid for the main action, outline for a secondary brand action, ghost for a quiet brand-linked action.
Neutral supports the workflow: outline for cancel/close, ghost for tools and utilities, solid only when a strong neutral action is clearer than accent.
Danger marks destructive work: solid only after confirmation, outline when confirmation happens elsewhere, ghost for dense destructive utilities.
Placement
Section titled “Placement”- One
primary+solidper action group. - Form footers place the main action last; cancel stays secondary.
- Toolbars use
outlineorghostand named icon buttons. Usesmallonly for an explicitly compact workflow; a toolbar is not automatically compact. - Table toolbars default to full-width reflow (
variant: :table). Group related controls withPathogen::Toolbar::Groupand push end groups withToolbar::Spacer. Keep text inputs and native selects outsiderole="toolbar"and its roving focus, even when they share the same visual action band. If an arrow-key-owning control is genuinely unavoidable inside the toolbar, include only one and place it last in DOM order. - Toggle toolbar buttons set
aria-pressedand rely on Button’s visible pressed styles (accent-tinted fill + accent border). - Do not put two primary solid buttons beside each other unless they are genuinely equal choices.
Hover On Solid Buttons
Section titled “Hover On Solid Buttons”Solid fills should darken on hover, like pressing further in. Danger uses red-600 -> red-700. Primary uses primary-700 -> primary-800 through --pvc-color-accent-solid-hover.
Size And State
Section titled “Size And State”| Size | Where | Minimum target |
|---|---|---|
medium |
Default for controls and actions | 44 × 44 CSS px |
small |
Explicit compact choice for dense workflows | 24 × 24 CSS px, with 8px between neighbours |
These are activation areas, not required icon or switch-track dimensions. Follow the target-size policy for exceptions and migration status.
Use short verb labels. Icon-only buttons need aria-label, aria-labelledby, or visible text. Use disabled when unfocusable. Use aria_disabled when the control must remain focusable so validation feedback can be announced.
Use Pathogen::Link for inline navigation. Use button-styled links only inside button groups.
Pathogen::Link is for underlined inline navigation with accent on hover. Toolbar actions should be buttons.
- Native controls first.
- Labels:
text-sm font-semibold,--pvc-color-text. - Fieldsets:
gap-4. - Checkboxes:
--pvc-radius-control-sm. - Radios and switches:
rounded-full. - Prefer
PathogenFormBuilderandPathogen::Form::*.
Data Grid
Section titled “Data Grid”The data grid is dense by design. It can use neutral-950 inside the grid frame in dark mode so rows read clearly. That exception does not apply to app backgrounds, sidebars, or preview shells.
Hover, focus, selected, disabled, and active states must remain visually distinct.
Focus And Keyboard
Section titled “Focus And Keyboard”Focus must show in light, dark, high-contrast, and forced-colours modes. It also needs to remain distinct from accent colour.
Target pattern: focus-visible:outline-2 outline-[var(--pvc-color-focus)] outline-offset-2
--pvc-color-focus resolves to primary-500 in both modes so focus stays distinct from accent colour.
Button, CopyableValue, Link, Form, Tabs, and each focusable Toolbar control use the target pattern.
Do not hide focus with transparent borders. Do not rely on colour alone for state.
Accessibility
Section titled “Accessibility”Pathogen targets WCAG 2.2 Level AA, with the enhanced target-size and focus requirements below. This is a design and acceptance policy, not a claim that every component or host page already conforms. Accessibility is not a late audit pass. It is part of how quiet, precise, and trustworthy the product feels.
How to build
Section titled “How to build”- Native HTML first — use the element that already matches the job (
button,a,input,fieldset,table,dialogwhere supported). - Pathogen components next — prefer
Pathogen::Button,Pathogen::Link,Pathogen::Badge,Pathogen::Form::*,Pathogen::Tabs,Pathogen::Disclosure,Pathogen::DataGridComponent, and related primitives before inventing markup. - ARIA patterns when custom — if the UI cannot be native, follow the matching pattern in the ARIA Authoring Practices Guide (APG) for roles, keyboard behaviour, focus management, and state.
Fix accessibility in Pathogen primitives first. Host apps inherit those gains everywhere the primitive is used.
Pathogen requirements
Section titled “Pathogen requirements”- Semantic HTML — correct element, label association, and document structure.
- Contrast — WCAG AA for text and meaningful UI state (see What is WCAG? for an overview; WCAG 2.2 for success criteria).
- Visible focus —
focus-visibletreatment in light, dark, high-contrast, and forced-colours modes (Focus and keyboard). - Names and labels — visible labels,
aria-label, oraria-labelledbywhere the visible UI does not speak for itself (especially icon-only controls). - State beyond colour — use visible text, shape, icons, or position so meaning survives without hue. Expose the corresponding programmatic state separately through native HTML or ARIA. ARIA alone does not provide a visible cue.
- Keyboard — full operation without a pointer; logical tab order; no keyboard traps except in modal flows that manage escape.
- Pointer targets — default to 44 × 44 CSS px; choose compact mode explicitly under the policy below.
- Motion — honour
prefers-reduced-motionfor non-essential animation. - Forced colours — custom controls remain usable when system high-contrast themes override author styles.
Target size and density
Section titled “Target size and density”Decision: 44px defaults with explicit compact mode, accepted 2026-09-21.
- Default activation areas must be at least 44 × 44 CSS px, including icon-only actions. Keep the visual control smaller when appropriate by enlarging its associated clickable label or padding. Targets must not overlap unrelated actions.
- Compact mode is an explicit component or workflow choice for dense clinical work. Existing
size: :smallis one such choice. Require at least 24 × 24 CSS px and an 8px gap between adjacent compact controls. Do not infer compact mode from screen width or pointer type. - Inline text links and other documented exceptions follow Target Size (Enhanced), SC 2.5.5. A dense layout alone is not a WCAG exception.
- A 24px compact target can meet the AA size requirement while falling short of the enhanced 44px requirement. Record that distinction. The 8px gap is a Pathogen design rule; it is not the general spacing formula in Target Size (Minimum), SC 2.5.8.
- Demonstrate default and compact layouts with keyboard focus, long labels, and narrow viewports. Document who chooses compact mode and why.
Implementation: Button, Toolbar::Button, Disclosure, Tabs, Form::RadioButton, and Form::Switch default to size: :medium. Use size: :small explicitly for compact use. Radio labels include the control in the activation area; switch tracks retain their visual size inside a larger label target. Hosts own spacing between separately rendered compact controls. Native checkbox helpers, grid controls, host-supplied triggers, and host layouts still require their own target audit; this list is not a library-wide conformance claim.
Enhanced focus
Section titled “Enhanced focus”Require a focus indicator with at least the area of a 2 CSS px perimeter and at least 3:1 contrast between the same pixels in focused and unfocused states, following Focus Appearance, SC 2.4.13. A class name or token name is not proof: measure the rendered indicator on every supported adjacent surface.
Keep the focused component fully visible despite author-created sticky regions and overlays, following Focus Not Obscured (Enhanced), SC 2.4.12. Library previews prove component behaviour; hosts must verify their complete page layouts. Selecting these enhanced criteria does not establish full AAA conformance.
Adaptation and evidence
Section titled “Adaptation and evidence”Verify representative workflows at 320 CSS px, 200% text resizing, 400% browser zoom, and WCAG text-spacing overrides. Two-dimensional grids and tables may need their own scrolling region; that does not exempt surrounding controls or the whole page from reflow.
For each interactive component, record the tested states and distinguish automated checks, real-browser interaction, manual assistive-technology checks, and host acceptance. Record the date, revision, browser/platform, and tester for manual evidence. An unavailable screen reader means not tested, not passed. Automated scans alone do not establish conformance for complete pages or workflows.
Where to look by pattern
Section titled “Where to look by pattern”| UI | Start here |
|---|---|
| Buttons, links | Button pattern, Pathogen Button previews |
| Tabs | Tabs pattern, Pathogen::Tabs |
| Forms, switches, radios | Checkbox, Radio, Switch, Pathogen::Form::* |
| Tooltips | Tooltip pattern, Pathogen::Tooltip |
| Toolbars | Toolbar pattern, Pathogen::Toolbar |
| Static status / metadata labels | Visible text + semantic tone (not colour alone); Pathogen::Badge |
| Data grids / tables | Grid and Table, Pathogen::DataGridComponent |
| Dialogs and disclosures | Dialog modal, Disclosure, Pathogen::Disclosure (button + aria-expanded, medium 44px target, optional heading_level) |
When Pathogen and APG disagree, match Pathogen tokens and visual language while keeping APG keyboard and naming behaviour — or document a deliberate exception.
Further reading
Section titled “Further reading”- What is WCAG? — plain-language introduction.
- WCAG 2.2 — official success criteria.
- ARIA Authoring Practices (APG) — patterns for custom widgets.
- WAI tutorials — forms, menus, tables, and more.
Previews should show accessibility-relevant states when they matter: focus, disabled, validation, selected, expanded, and error. See Preview guidelines.
Local Exceptions
Section titled “Local Exceptions”Sometimes a workflow needs a different pattern. Name the exception, why the workflow needs it, and whether it should become a shared component or token. Undocumented differences become a second design system.