Integration · Setup
Tailwind theme An accessible starter, built and checked
The token studio’s Tailwind v4 export, made into a starter: seven components that use only its role colors, a setup pinned to the versions it was built with, and a checklist for your own pages. Every component was built with Tailwind and checked in both themes.
- Version
- Tailwind 4.3.3
- Needs
- Node.js and npm; Tailwind 4.3.3
- Cost
- Free, nothing to sign up for
- Checked
Before you start
What you need
- A project with Node.js and npm. The starter was built on Node.js 24.11.0.
- Templates where you can add classes: HTML, or the components of any framework.
- Nothing from this site to install: the theme is one CSS file.
Setup
Step by step
-
Download the theme
theme.cssis the token studio’s Tailwind v4 export of its default system: the token variables for both themes, then an@theme inlineblock that makes each role a Tailwind color, the four status colors included.@import "tailwindcss"; /* … the token variables for both themes … */ @theme inline { --color-page: var(--color-canvas); --color-panel: var(--color-surface); --color-raised: var(--color-surface-raised); --color-ink: var(--color-text); --color-muted: var(--color-text-muted); --color-action: var(--color-primary); --color-action-hover: var(--color-primary-hover); --color-on-action: var(--color-on-primary); --color-focus: var(--color-ring); --color-boundary: var(--color-border); --color-success: var(--color-success-text); --color-success-soft: var(--color-success-surface); --color-warning: var(--color-warning-text); --color-warning-soft: var(--color-warning-surface); --color-danger: var(--color-danger-text); --color-danger-soft: var(--color-danger-surface); --color-info: var(--color-info-text); --color-info-soft: var(--color-info-surface); /* … */ } -
Install the tested versions
These are the exact versions the starter was built and checked with. A newer 4.x release should work; these are the ones that were run.
npm install [email protected] @tailwindcss/[email protected] -
Build
Build from the theme. Tailwind finds the classes in your templates and writes only those.
npx @tailwindcss/cli -i ./theme.css -o ./dist/styles.css -
Copy the components
Paste the components below into your templates and change the words. Keep the focus, error and status markup: the checklist depends on it. Set
data-theme="dark"on a wrapper for the dark theme.You should see: the built example below: seven components in each theme, with no axe violations in either.
Components
Seven components, each with its states
Each uses Tailwind’s own utilities and the theme’s role colors only, so it follows the theme. The text of each is a placeholder; the structure is the part that matters.
The built example
theme.css. The example is in English, as the code is. Open the built example in its own tabButtons
Primary and secondary, 44 pixels tall. Focus draws a 2-pixel outline in the theme’s focus color. The primary button keeps a transparent border, so forced colors can show its edge.
<div class="flex flex-wrap gap-3">
<button type="button" class="inline-flex min-h-11 items-center rounded-lg border-2 border-transparent bg-action px-4 font-medium text-on-action motion-safe:transition-colors hover:bg-action-hover focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus">
Save changes
</button>
<button type="button" class="inline-flex min-h-11 items-center rounded-lg border-2 border-boundary bg-panel px-4 font-medium text-ink motion-safe:transition-colors hover:border-ink focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus">
Cancel
</button>
</div>
A link in text
Underlined, so it is told apart by more than color. Hover thickens the line.
<p class="text-ink">
Read the <a href="#setup" class="font-medium text-action underline underline-offset-4 hover:decoration-2 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus">setup guide</a> before you start.
</p>
A field with an error
A real label. The hint and the error are tied to the field with aria-describedby, and aria-invalid marks it. The message has an icon and words, not a red border alone.
<div class="grid max-w-sm gap-2">
<label for="email" class="font-medium text-ink">Email</label>
<p id="email-hint" class="text-sm text-muted">We send the receipt here.</p>
<input id="email" type="email" autocomplete="email" value="sam.mail" aria-invalid="true" aria-describedby="email-hint email-error" class="min-h-11 w-full rounded-lg border-2 border-danger bg-panel px-3 text-ink focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus">
<p id="email-error" class="flex gap-2 text-sm font-medium text-danger">
<svg class="mt-0.5 size-5 shrink-0" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"><path d="M10 2a8 8 0 1 0 0 16 8 8 0 0 0 0-16Zm3.3 10.2-1.1 1.1L10 11.1l-2.2 2.2-1.1-1.1L8.9 10 6.7 7.8l1.1-1.1L10 8.9l2.2-2.2 1.1 1.1L11.1 10l2.2 2.2Z"/></svg>
Enter an email address with an @ sign.
</p>
</div>
Status messages
Success, note, warning and error, each with an icon, a word that names it and a live-region role. Use role="alert" only for a message that appears because of something the reader did.
<div class="grid gap-3">
<div role="status" class="flex gap-3 rounded-lg border border-success bg-success-soft p-4 text-success">
<svg class="mt-0.5 size-5 shrink-0" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"><path d="M10 2a8 8 0 1 0 0 16 8 8 0 0 0 0-16Zm3.7 6.2-4.5 4.6a.8.8 0 0 1-1.1 0L6.3 11a.8.8 0 1 1 1.1-1.1l1.3 1.2 3.9-4a.8.8 0 1 1 1.1 1.1Z"/></svg>
<p><strong class="font-semibold">Saved.</strong> Your changes are live.</p>
</div>
<div role="status" class="flex gap-3 rounded-lg border border-info bg-info-soft p-4 text-info">
<svg class="mt-0.5 size-5 shrink-0" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"><path d="M10 2a8 8 0 1 0 0 16 8 8 0 0 0 0-16Zm.9 12H9.1V9h1.8v5Zm0-6.5H9.1V6h1.8v1.5Z"/></svg>
<p><strong class="font-semibold">Note:</strong> Reports update every hour.</p>
</div>
<div role="status" class="flex gap-3 rounded-lg border border-warning bg-warning-soft p-4 text-warning">
<svg class="mt-0.5 size-5 shrink-0" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"><path d="M10.9 2.6a1 1 0 0 0-1.8 0L1.6 16a1 1 0 0 0 .9 1.5h15a1 1 0 0 0 .9-1.5L10.9 2.6Zm-.1 12.4H9.2v-1.6h1.6V15Zm0-3H9.2V7h1.6v5Z"/></svg>
<p><strong class="font-semibold">Warning:</strong> Your plan renews in three days.</p>
</div>
<div role="alert" class="flex gap-3 rounded-lg border border-danger bg-danger-soft p-4 text-danger">
<svg class="mt-0.5 size-5 shrink-0" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"><path d="M10 2a8 8 0 1 0 0 16 8 8 0 0 0 0-16Zm3.3 10.2-1.1 1.1L10 11.1l-2.2 2.2-1.1-1.1L8.9 10 6.7 7.8l1.1-1.1L10 8.9l2.2-2.2 1.1 1.1L11.1 10l2.2 2.2Z"/></svg>
<p><strong class="font-semibold">Error:</strong> The payment did not go through.</p>
</div>
</div>
Badges
Short labels on a soft fill. The words carry the meaning; the color only supports it.
<p class="flex flex-wrap gap-2">
<span class="inline-flex items-center rounded-full bg-info-soft px-3 py-1 text-sm font-medium text-info">Beta</span>
<span class="inline-flex items-center rounded-full bg-success-soft px-3 py-1 text-sm font-medium text-success">Paid</span>
<span class="inline-flex items-center rounded-full bg-warning-soft px-3 py-1 text-sm font-medium text-warning">Due soon</span>
</p>
A card
A raised surface with a heading, muted text and a link that is a 44-pixel target.
<article class="max-w-sm rounded-xl border border-boundary bg-raised p-6 shadow-sm">
<h3 class="text-lg font-semibold text-ink">October report</h3>
<p class="mt-2 text-muted">Twelve pages, updated on 1 October.</p>
<a href="#card" class="mt-2 inline-flex min-h-11 items-center font-medium text-action underline underline-offset-4 hover:decoration-2 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus">Open the report</a>
</article>
Verification
A checklist for your own pages
Run these on your build, in every theme, after you change anything. The last two columns are what each check found on the built example.
| Check | How to check it | Light theme | Dark theme |
|---|---|---|---|
| Text contrast | Every text color on its background reaches 4.5:1, or 3:1 for large text. Measure each pair in each theme, error and hover states included. | 6.51:1 at its lowest Passes | 7.66:1 at its lowest Passes |
| Boundaries | The field’s border, the secondary button’s border, the current tab’s bar and the card’s edge reach 3:1 against what is next to them. | 3.32:1 at its lowest Passes | 5.78:1 at its lowest Passes |
| Keyboard focus | Tab through the page. Every control shows an outline at least 2 pixels wide, at 3:1 or more against the color behind it. | 8 of 8 controls; outline 7.25:1 Passes | 8 of 8 controls; outline 7.66:1 Passes |
| Target size | Buttons, fields and navigation links are at least 24 by 24 pixels. These are taller, at 44. | 43 px at the smallest Passes | 43 px at the smallest Passes |
| Forced colors | With Windows high contrast on (forced colors), every button and field keeps a visible border. | 3 of 3 keep a border Passes | 3 of 3 keep a border Passes |
| Automated rules | Run axe or Lighthouse on each theme. They catch missing labels, names and roles, not whether the words make sense. | 0 violations Passes | 0 violations Passes |
| Not color alone | Each message carries an icon and a word that names it, and each badge a text label. | 4 of 4 messages Passes | 4 of 4 messages Passes |
| Errors tied to the field | The field in error has aria-invalid, and aria-describedby names its hint and its error, so a screen reader reads both. | 3 of 3 references found Passes | 3 of 3 references found Passes |
| Reduced motion | Transitions run only under motion-safe:, so a reader who asks for reduced motion gets none. | 2 of 2 transitions Passes | 2 of 2 transitions Passes |
Limits
What it does not do
- The checks cover the default token system. If you change the seed or a role in the studio, export again and run the checklist on your own build.
- Automated checks cannot judge meaning. Whether a message is clear, or the reading order works, needs a person; the starter has not yet been tested with a screen reader.
- The starter is for Tailwind v4. The studio also exports a Tailwind v3 configuration, which the starter does not use.
- Layout, the type scale and responsive behavior are yours: the starter covers color, focus and states.
Go further
The full reference
- The design tokens guide: plain CSS and Figma
- Open the token studio
- Accessible UI patterns
- Modern color in CSS, explained
Other integrations
- Browser extensionCheck and fix contrast on the page in front of you.
- VS CodeUnderline low contrast in your stylesheets and fix it in place.
- REST APIConvert colors and check pairs from a server or a script.
- Design tokensExport one token system to Tailwind, CSS or Figma.
- Contrast embedPut a contrast checker on your own page.
- React tokensTheme a React app from the token export.
- Figma variablesTurn Figma variables into CSS tokens.
- Contrast in CICheck your color pairs on every pull request.
Checked against
Where these facts come from
Versions, requirements and examples on this page were checked on . The starter was built with Tailwind 4.3.3 and its CLI 4.3.3 on Node.js 24.11.0 from the token studio’s export, then checked in Chromium with axe 4.12.1 in both themes; contrast, focus, target size and forced colors were measured on the built page.
- Tailwind CSS: Installing the Tailwind CLItailwindcss.com
- Tailwind CSS: Theme variablestailwindcss.com
- Tailwind CSS: Hover, focus, and other statestailwindcss.com
- Understanding WCAG 2.2: Non-text Contrastwww.w3.org
- Understanding WCAG 2.2: Focus Visiblewww.w3.org
- Understanding WCAG 2.2: Target Size (Minimum)www.w3.org
- axe-coregithub.com
Found a mistake? Write to us and we will correct it.