Skip to content

Foundations

This guide covers Pathogen::* components, Lookbook previews, and host-app usage. It turns the five Pathogen qualities into practical design decisions.

  • 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 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.

Start with the job an element performs, then choose the smallest visual treatment that makes that job clear.

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.

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.

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-muted or typography muted.
  • Supporting defaults to control scale for captions and labels. Pass size: :meta for timestamps and footnotes; HeadingGroup metadata uses meta scale by default.
  • Body weight stays normal. Heading weight steps deliberately: Heading levels 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 Supporting for metadata labels.

Prefer text-[length:var(--type-control)] in new code. Existing text-sm is fine where it matches the role.

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

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.

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.

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.

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.

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-surface or --pvc-color-surface-muted
  • Border: 1px --pvc-color-border
  • Radius: --pvc-radius-panel for larger frames; --pvc-radius-control for 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.

  • 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 have two decisions: tone and emphasis.

Decision Values
tone neutral, primary, danger
emphasis solid, outline, ghost

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.

  • One primary + solid per action group.
  • Form footers place the main action last; cancel stays secondary.
  • Toolbars use outline or ghost and named icon buttons. Use small only for an explicitly compact workflow; a toolbar is not automatically compact.
  • Table toolbars default to full-width reflow (variant: :table). Group related controls with Pathogen::Toolbar::Group and push end groups with Toolbar::Spacer. Keep text inputs and native selects outside role="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-pressed and 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.

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 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 PathogenFormBuilder and Pathogen::Form::*.

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 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.

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.

  1. Native HTML first — use the element that already matches the job (button, a, input, fieldset, table, dialog where supported).
  2. Pathogen components next — prefer Pathogen::Button, Pathogen::Link, Pathogen::Badge, Pathogen::Form::*, Pathogen::Tabs, Pathogen::Disclosure, Pathogen::DataGridComponent, and related primitives before inventing markup.
  3. 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.

  • 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-visible treatment in light, dark, high-contrast, and forced-colours modes (Focus and keyboard).
  • Names and labels — visible labels, aria-label, or aria-labelledby where 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-motion for non-essential animation.
  • Forced colours — custom controls remain usable when system high-contrast themes override author styles.

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: :small is 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.

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.

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.

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.

Previews should show accessibility-relevant states when they matter: focus, disabled, validation, selected, expanded, and error. See Preview guidelines.

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.