Brand system, built to be read by everyone.
The same calm Orchard identity as the static guide, now interactive: click any swatch to copy it, flip the whole system to dark, and run any color pair through a live WCAG checker. Tap a core color to jump to the audit.
This guide dogfoods its own rules: body text is slate 600, links are green 800, and the focus ring is green 700 — every one an AA-or-better pairing verified in the Accessibility tab.
Palette
Click any swatch to copy its hex. Each shows its own contrast against white and against ink so you can see, at a glance, which steps are safe for text.
Contrast audit & fixes
Every ratio on this page is computed live from the tokens using the WCAG relative-luminance formula, so it stays honest if a color changes. Thresholds: normal text needs 4.5:1 (AA) or 7:1 (AAA); large text 3:1; icons, borders and focus rings that carry meaning need 3:1 (1.4.11).
System audit
Every color pairing the design system actually uses, graded against its real job.
| Use | Pair | Ratio | Verdict | Fix |
|---|
Priority fixes
The pairings that fall short, and the exact token change that resolves each.
Link & brand text was under-set Was failing
The old text.link = green.700 lands at 4.14:1 on white — below the 4.5 needed for normal text. Bump the alias to green.800 (#2F6E2B) for 6.20:1. Green 700 stays valid for large text (18px+ bold or 24px+).
Warning solid never takes a white label 1.80:1
White on warning.500 (#F4B740) is 1.80:1 — effectively invisible. Warning fills use an ink label (9.59:1). Set status.warning.on = ink, matching how the brand orange accent already behaves.
Solid success / danger labels need the darker step Large-only
White on success.500 (3.09) and danger.500 (3.91) only clear large text. For normal-size button labels, fill with the 600–700 step: danger.600 (5.06), info.600 (5.76), or use an ink label on success.
Focus ring must be green 700, not green 500 1.4.11
A green.500 ring on white is 2.15:1, under the 3:1 that non-text indicators require. Draw the focus outline in green.700 (4.14) or ink, and keep the soft green glow as a secondary cue. This guide already does.
Outlined controls carry their edge with slate 500 1.4.11
Where a field's border is the only thing marking it, slate.200 (1.36) and even slate.400 (2.98) fall short of 3:1. Give inputs a slate.500 resting border, or a filled slate.50 background so the edge reads by fill. Slate 100–200 stay fine for decorative dividers, which are exempt.
Placeholder text isn't a place for meaning 2.98:1
Faint slate.400 is 2.98:1 — acceptable for disabled or purely decorative text, but never for a hint a user needs. Put essential guidance in a visible label (slate 600, 8.11) and keep placeholders illustrative.
Fraunces & Instrument Sans
Fraunces carries the warmth of the wordmark for display; Instrument Sans runs the interface and keeps money aligned with tabular figures. Both open-license, both on web and Expo.
Scale (px): 12·13·14·16·18·22·26·34·44·52. Currency, units and rates always use tabular figures.
Core UI, WCAG-clean
Built from the corrected tokens. Every label pairing here passes AA at its size.
Buttons
Primary & warning take ink labels; secondary & danger take white. No white on green, orange or amber.
Pills & field
Input border is slate 500 (3:1+); focus is a green 700 outline plus glow.
Holdings
Compliance guardrail
Orchard distributes; it does not advise. Returns, performance or yield wording shown next to a live listing goes through compliance review first, and no copy or AI output is framed as a recommendation.
Design tokens
The four token files from the static guide still apply. Below are the accessibility deltas to fold into the semantic aliases so components inherit the fixes.
orchard-tokens.json · sourceorchard-tokens.css · Laravel + Vueorchard-tokens.ts · React Native / Expotailwind.orchard.js · webAccessibility deltas
status.warning.on: white → ink
status.success.solid: white on 500 → ink, or white on 700
status.info.solid: info.500 → info.600
border.focus (ring): green.500 → green.700
border.control (outlined): slate.200 → slate.500