Getting started
The system ships as plain CSS custom properties plus a compiled React bundle. Everything below runs in a browser as-is — open an HTML file and it works.
If you only need the brand — colours, type, spacing, the icon font — link styles.css and stop there. If you want the React components too, add the bundle.
Install
Two tags. Paths are relative to your page.
<!-- tokens + fonts + the official icon font -->
<link rel="stylesheet" href="styles.css">
<!-- React components (optional) -->
<script src="_ds_bundle.js"></script>
Components come off one global:
const { Button, Card, Stat, Icon, Input, Alert, Tabs } =
window.PattersonCompaniesDesignSystem_3534f9;
The bundle expects React 18 already on the page. For a prototype, load React, ReactDOM and Babel first, then the bundle, then your script.
ds-base.js does all of this for you — it resolves the stylesheet and bundle from one base constant. Copying a template out means editing that one line.What's in the box
| Path | What it is |
|---|---|
| styles.css | The single entry point. Imports every token file — link this, nothing else. |
| tokens/ | fonts · icons · colors · typography · spacing · effects · base |
| components/ | 13 React components in core / forms / feedback |
| templates/ | 11 starting points — surfaces, decks, app shells |
| guidelines/ | Specimen cards for every foundation |
| integrations/ | Tailwind (v3 + v4), UnoCSS, shadcn/ui, Theme UI |
| theme.json | Machine-readable theme; the adapters are generated from it |
| assets/ | Lockups, favicons, photography, fonts, the icon font |
Tokens
211 custom properties. Every one is available the moment styles.css is linked — there is no theme provider and no JavaScript involved.
Two scales live side by side
Type and spacing each carry production-verified values alongside this system's own conventions, because the two genuinely differ. Production values describe the live Patterson sites; system values are deliberate additions that make the system more consistent and more accessible.
- Production — the
--pt-*type scale,--radius-btn(5px),--radius-field(2px),--download-shadow,--transition-prod. - System — the fluid
--fs-*scale,--radius-pill, navy-tinted--shadow-*,--ring-focus, the tracking tokens, the 44px control floor.
Add class="pat-production" to any subtree to render the exact live scale instead of the system one.
Production name aliases
The live theme's own property names are aliased in, so markup lifted straight from production works unchanged:
--color-primary-dark-blue → --pat-navy
--color-primary-light-blue → --pat-sky
--color-primary-cool-grey → --pat-gray
--primary-hover → #315D83
Colour
Navy #003767 carries headings, chrome and emphasis surfaces. Sky #00A8E1 is the accent — focus rings, statistics, active underlines, card stripes. White does most of the work; navy bands punctuate.
Rules that matter
- Every heading level is navy. Production sets
color: var(--color-primary-dark-blue)on h1 through h4, the eyebrow and the caption alike. Headings are never grey or near-black. - The tertiary set is for data only. Green
#7BC24D, teal#00817Dand purple#522E91belong in charts and infographics — never as page chrome. - Sky is accent-scale, navy is surface-scale. Don't fill a large band with sky.
#AFA593is a warm taupe and it is deliberate — the brand's one non-cool grey, used for input borders. Don't "correct" it.
Type
One family does everything: Proxima Nova, with Figtree as the loaded open fallback and Arial as the PowerPoint-safe substitute. There is no display/body split — hierarchy comes from size and weight.
800 on h1/h2, 600 on buttons and 500 on eyebrows, so the live sites render browser-synthesized faux bold at those weights. Treat 700 as the real bold ceiling.The production scale
| Token | Mobile | Desktop | Weight |
|---|---|---|---|
| h1 | 2.75rem / 3rem | 4rem / 4.25rem | 800 |
| h2 | 2.25rem / 2.5rem | 2.75rem / 3rem | 800 |
| h3 | 1.5rem / 1.75rem | 2.25rem / 2.5rem | 400 |
| h4 | 1.5rem / 1.75rem | — | 400 |
| body | 1.125rem / 1.5rem | — | 400 |
| eyebrow | 0.875rem / 1.375rem | — | 500 |
| button | 0.9375rem / 1.25rem | — | 600 |
Note the hard drop: 800 at h1 and h2, then 400 from h3 down. Hierarchy is a size step plus a weight step, not a continuous bold ramp.
Two things people add that don't belong
- Letter-spacing. Production declares none anywhere — not negative on display sizes, not positive on uppercase labels.
- Uppercase sky eyebrows. The production eyebrow is navy, 14px, weight 500, sentence case. "Trusted expertise" and "Unrivaled support" appear verbatim as section eyebrows on the live sites.
.pat-eyebrow--accentkeeps the uppercase-sky treatment as a system option.
Shape & depth
Production is restrained. Radii are small, shadows are soft and low-alpha, and there are no gradients anywhere in the stylesheet.
| Token | Value | Use |
|---|---|---|
| --radius-field | 2px | Input, select, textarea |
| --radius-btn | 5px | Buttons |
| --radius-btn-elevated | 6px | Download / external buttons |
| --radius-lg | 10px | Cards — the signature container radius |
| --radius-pill | 999px | System convention only — no pill exists in production |
Elevation runs five navy-tinted steps, --shadow-xs through --shadow-xl. Cards rest at --shadow-sm and lift 3px to --shadow-lg when interactive. Focus is a 3px sky ring, --ring-focus, and it is never removed.
Motion is 120–320ms on colour, border-colour, transform and height. No bounces, no springs, no parallax, no infinite loops.
Icons
The official Patterson icon font ships with the system — 93 glyphs lifted from the production storefront pattern library, at private-use codepoints from U+E900. It replaces Lucide and every other third-party set.
<!-- plain HTML -->
<i class="pat-i pat-i-cart" aria-hidden="true"></i>
// React
<Icon name="chevron-right" size="sm" />
Because it is a font, glyphs inherit color and scale with font-size — there is no stroke to set. Production renders inline glyphs at 14px (--icon-size-sm).
The set covers navigation, commerce, documents, account and social, plus specifics no generic library has: pedigree, prescription, formulary-list, hazmat, shiponice, previouslypurchased.
aria-label — private-use codepoints read as nothing to a screen reader. Browse all 93 →Spot illustrations are a separate tier
The navy-and-sky two-tone line illustrations used to head a capability card are not in the font. They live in assets/brand at 64–120px with heavy rounded strokes.
Components
Thirteen React components on one namespace. Each ships typed props, a specimen card and usage notes for agents.
| Group | Components |
|---|---|
| core | Button · IconButton · Icon · Badge · Stat · Card |
| forms | Input · Select · Checkbox · Radio · Switch |
| feedback | Alert · Tabs |
The interaction contract
- Buttons are 5px with 15px/30px padding.
primaryis navy hovering to a lighter navy#315D83.secondaryis a filled light blue#6DCFF6with a navy label. Both are production-verified;sky,outline,ghostandonDarkare system additions. - Fields are 2px radius with the warm taupe border, and gain the sky ring on focus.
- Cards rest at 10px with a hairline and soft shadow; interactive cards lift 3px.
- Stat is the signature device — an oversized sky numeral over a grey caption. Numbers are how this brand proves things.
Templates
Ten starting points in templates/. Each folder is self-contained — open its entry file, replace the content, ship.
| Kind | Templates |
|---|---|
| Product surfaces | corporate-website · storefront · docs-site |
| Decks | deck · executive-deck · purpose-vision-values |
| Shells & pages | canvas · patterson-docs · file-manager · corporate-page |
Templates are not compiled into the bundle and their JSX is loaded directly by the page, so anything in a template can call ReactDOM.createRoot at module scope safely.
To copy one into another project, change a single line:
// templates/<slug>/ds-base.js
const base = '../..'; // → point at the bound _ds/<folder>
Framework adapters
The brand is framework-agnostic. Four adapters are generated from theme.json, so colours are the exact brand hexes rather than re-derived values.
| File | Stack |
|---|---|
| tailwind.css | Tailwind v4 CSS-first @theme, plus .pat-btn-* component utilities |
| tailwind.config.js | Tailwind v3 / JS preset |
| uno.config.js | UnoCSS theme + brand shortcuts |
| shadcn-theme.css | shadcn/ui semantic variables, light + navy-ground dark |
| theme-ui.js | Theme UI theme with variants |
tokens/*.css and theme.json together — the adapters are generated from the JSON, and drift between them is the one way this system goes wrong quietly.Brand rules
Do
- Anchor pages on white. White plus space is the brand; navy and sky are punctuation.
- Set every heading navy, with no letter-spacing.
- Keep primary-button hover inside navy —
#003767→#315D83. - Use small radii on controls: 5px buttons, 2px fields, 10px cards.
- Show the 3px sky focus ring on everything focusable.
- Render statistics at full size — proof, not decoration.
- Use warm, authentic photography of practitioners, animals and operations.
Don't
- Don't introduce a second typeface.
- Don't use gradients, textures or pattern fills — the production stylesheet contains zero gradient declarations. The wave watermark is the only decorative mark.
- Don't build glassmorphic panels or blurred backdrops beyond a sticky header.
- Don't use emoji or unicode glyphs, ever.
- Don't scrim photography with black — the scrim is navy at ~55%.
- Don't add bounce, spring, parallax or decorative animation.
- Don't crowd corporate or marketing surfaces. Density is permitted on storefront catalogues and app shells only.
Voice
"We" for Patterson, "you / your" for the customer, partnership-framed. Verb-first CTAs — "Explore solutions", "Create my account" — never "Click here". Numbers as proof. Title Case only for product and programme brands.
Questions are allowed as headings; the live sites lead with "Need technical service or equipment repair?" Keep them short and answer them immediately.
Gotchas
Twelve claims that look right and aren't. Each was checked against the production theme stylesheet.
| Assumption | Reality |
|---|---|
| Everything is a pill | Buttons 5px, fields 2px — no pill radius exists in production |
| Primary hovers to sky | #315D83, a lighter navy |
| Secondary is a navy outline | A filled #6DCFF6 with a navy label |
| Headlines are 700 throughout | 800 at h1/h2, then 400 from h3 down |
| Body is 16px at 1.6 | 18px at 24px line-height (1.33) |
| Display is tracked tight | No letter-spacing in the stylesheet at all |
| Eyebrows are uppercase sky | Navy, 14px, weight 500, sentence case |
| All greys are cool | #AFA593 is a warm taupe, used on field borders |
| Shadows are navy-tinted | Production uses neutral black at 4–12% alpha |
| Buttons are 44px tall | ≈50px — 15px/30px padding on a 20px line-box |
| Icons are single-colour strokes | Spot illustrations are navy+sky two-tone; UI glyphs come from a font |
| Weights 300–900 are available | Only 400 and 700 are licensed faces |
The system keeps a handful of deliberate divergences from production — the pill token, navy-tinted shadows, the sky focus ring, the 44px floor. They are labelled S throughout DESIGN.md, and the focus ring in particular is a real accessibility gain over production, which ships none.
Contributing
Adding a token
- Add it to the right file in tokens/.
- Mirror it in theme.json.
- If the compiler can't infer its kind, annotate:
--my-token: 4px; /* @kind spacing */
Adding a component
- Write
Name.jsxandName.d.tsside by side in a group folder — the pair is what the compiler looks for. - Add it to that group's
*.card.htmlspecimen. - Inherit the contract: 5px radius, the sky focus ring, a 120–320ms transition, and a 44px minimum target.
Adding a template
Create templates/<slug>/ with a <!-- @template name="…" description="…" --> comment as the first line of the entry file, and a sibling ds-base.js.
Run the design-system check after any edit; fix what it reports and run it again until clean.