AnglinAI Color System (v2)
The v2 color system lives in @anglinai/ui and provides a WCAG AAA-compliant palette, semantic CSS custom properties, Tailwind utilities, and a dark-mode ThemeProvider. All AnglinAI projects should use it.
Approved Palette
Every production token meets WCAG AAA (7:1 minimum) contrast ratio on the stated background.
| Token | Hex | On | Ratio | Level |
|---|---|---|---|---|
| Primary (deep blue) | #054fb9 | white | 7.3:1 | β AAA |
| Primary hover | #0057b0 | white | 7.1:1 | β AAA |
| Accent / sky blue | #8babf1 | black | 9.2:1 | β AAA |
| Secondary (dark orange) | #8b3200 | white | 7.2:1 | β AAA |
| Vivid orange | #f57600 | black | 7.4:1 | β AAA |
Aspirational tokens (AA only on white β stored as references, not for direct use in UI):
| Token | Hex | Ratio | Note |
|---|---|---|---|
#0073e6 | mid blue | 4.6:1 AA | Design reference only |
#c44601 | orange | 4.8:1 AA | Design reference only |
Never use teal or green in any AnglinAI UI β see global
CLAUDE.md.
CSS Custom Properties
After importing @anglinai/ui/theme.css, these variables are available everywhere.
Semantic tokens (use these in components)
/* Light mode values shown. Dark mode inverts automatically via .dark class. */
/* Primary */--anglin-color-primary /* #054fb9 light / #8babf1 dark */--anglin-color-primary-hover /* #0057b0 light / #a5bff5 dark */--anglin-color-primary-active /* #03368a light / #c0d3f9 dark */--anglin-color-primary-subtle /* #dde8fb light / #1a2d5a dark */--anglin-color-on-primary /* #ffffff light / #000000 dark */
/* Secondary */--anglin-color-secondary /* #8b3200 light / #f57600 dark */--anglin-color-secondary-hover /* #7a2b00 light / #f78c1f dark */--anglin-color-secondary-subtle /* #fdebd6 light / #3d1e00 dark */--anglin-color-on-secondary /* #ffffff light / #000000 dark */
/* Accent */--anglin-color-accent /* #8babf1 light / #0057b0 dark */--anglin-color-on-accent /* #000000 light / #ffffff dark */
/* Vivid */--anglin-color-vivid /* #f57600 both modes */--anglin-color-on-vivid /* #000000 both modes */
/* Surfaces */--anglin-color-surface /* #ffffff light / #0f172a dark */--anglin-color-surface-subtle /* #f8fafc light / #1e293b dark */--anglin-color-surface-muted /* #eff6ff light / #162044 dark */--anglin-color-on-surface /* #0f172a light / #f1f5f9 dark */--anglin-color-on-surface-muted /* #475569 light / #94a3b8 dark */
/* Borders & focus */--anglin-color-border /* #e2e8f0 light / #334155 dark */--anglin-color-focus-ring /* #054fb9 light / #8babf1 dark */Palette reference tokens (raw values β not for components)
--anglin-palette-blue-deep /* #054fb9 */--anglin-palette-blue-hover-aaa /* #0057b0 */--anglin-palette-blue-sky /* #8babf1 */--anglin-palette-orange-dark-aaa /* #8b3200 */--anglin-palette-orange-vivid /* #f57600 *//* Aspirational (AA only) */--anglin-palette-blue-mid /* #0073e6 */--anglin-palette-orange-dark /* #c44601 */Usage in plain CSS
.my-button { background-color: var(--anglin-color-primary); color: var(--anglin-color-on-primary);}
.my-button:hover { background-color: var(--anglin-color-primary-hover);}Tailwind Utilities
After adding @anglinai/ui as a Tailwind preset, these classes are available.
Setting up the preset
module.exports = { darkMode: 'class', presets: [require('@anglinai/ui/tailwind-preset')], content: [ './src/**/*.{ts,tsx}', './node_modules/@anglinai/ui/dist/**/*.{js,mjs}', ],};Available classes
All anglin-* utilities support Tailwind opacity modifiers (e.g. bg-anglin-primary/80).
bg-anglin-primary text-anglin-primary border-anglin-primarybg-anglin-primary-hover hover:bg-anglin-primary-hoverbg-anglin-primary-subtle text-anglin-primary-subtlebg-anglin-on-primary text-anglin-on-primary
bg-anglin-secondary text-anglin-secondarybg-anglin-secondary-hover hover:bg-anglin-secondary-hoverbg-anglin-secondary-subtlebg-anglin-on-secondary text-anglin-on-secondary
bg-anglin-accent text-anglin-accentbg-anglin-on-accent text-anglin-on-accent
bg-anglin-vivid text-anglin-vividbg-anglin-on-vivid text-anglin-on-vivid
bg-anglin-surface bg-anglin-surface-subtle bg-anglin-surface-mutedtext-anglin-on-surface text-anglin-on-surface-muted
border-anglin-borderStatic hex classes (for SVG/canvas where CSS vars donβt work)
bg-anglin-static-blue-deep text-anglin-static-blue-deepbg-anglin-static-blue-hoverbg-anglin-static-blue-sky text-anglin-static-blue-skybg-anglin-static-orange-darkbg-anglin-static-orange-vivid text-anglin-static-orange-vividExample component
<button className="bg-anglin-primary text-anglin-on-primary hover:bg-anglin-primary-hover px-4 py-2 rounded-lg"> Save changes</button>
<div className="bg-anglin-surface-subtle border border-anglin-border rounded-xl p-6"> <p className="text-anglin-on-surface">Content here</p> <p className="text-anglin-on-surface-muted">Supporting text</p></div>Layout Modes (Shared Theme)
Layout modes let a single codebase switch between two complete color schemes by setting a data-layout attribute. Both apps (accessible-pdf-converter and accessible-org-chart) share the same canonical theme definitions, which live in @anglinai/ui/layouts.css.
Available modes
data-layout value | Scheme | Use case |
|---|---|---|
"standard" | AnglinAI v2 royal blue (#054fb9, WCAG AAA) | Default app pages |
"control-center" | Warm cream + copper accent (#C2703E) | Admin / power-user shells |
Each mode defines both light and dark variants. The .dark class on <html> (managed by ThemeProvider) activates the dark variant automatically.
Activating a mode
Apply data-layout to any wrapper element β the tokens cascade to all descendants:
// Entire page in standard blue mode<div data-layout="standard"> <p className="text-cc-text-primary">β¦</p></div>
// Entire page in copper control-center mode<div data-layout="control-center" className="bg-cc-base"> <p className="text-cc-text-primary">β¦</p></div>Both apps also apply the attribute to <body> at runtime so the mode affects the full viewport. In accessible-pdf-converter, this is managed by LayoutProvider (src/lib/layout-context.tsx):
'classic' layout β data-layout="standard" on <body>'control-center' β data-layout="control-center" on <body>CSS variables provided by each mode
Layout modes define two sets of variables:
--cc-* variables β used directly by components inside the shell:
--cc-bg-base --cc-bg-surface --cc-bg-card --cc-bg-card-hover--cc-border --cc-border-strong--cc-text-primary --cc-text-secondary --cc-text-muted--cc-accent --cc-accent-hover --cc-accent-glow--cc-success --cc-warning --cc-danger--anglin-color-* remaps β the mode overrides the shared semantic tokens from theme.css so that generic text-anglin-primary, bg-anglin-surface, etc. classes automatically reflect the active palette without any code changes to the components themselves.
Importing in a project
@import '@anglinai/ui/theme.css'; /* base v2 semantic tokens */@import '@anglinai/ui/layouts.css'; /* layout-mode overrides */
@tailwind base;@tailwind components;@tailwind utilities;Do not define your own [data-layout="control-center"] or [data-layout="standard"] blocks locally β inherit from the package. App-specific overrides (e.g. component-level adjustments for a dropzone inside the CC shell) belong in the appβs own CSS file after the import.
How to update a layout theme
All layout-mode color definitions live in one file:
anglinai-monorepo/packages/ui/src/layouts.cssTo change a color value in a mode:
- Open
packages/ui/src/layouts.css - Find the relevant
[data-layout="β¦"]block (light) or.dark [data-layout="β¦"]block - Update the hex value(s)
- Also update the matching
--anglin-rgb-*channel variable (space-separated R G B, used for Tailwind opacity modifiers like/50) - Bump the patch version in
packages/ui/package.json - Publish the package (see below)
Example β changing the copper accent from #C2703E to #B8611A:
/* Before */--cc-accent: #C2703E;--anglin-color-primary: #C2703E;--anglin-rgb-primary: 194 112 62;
/* After */--cc-accent: #B8611A;--anglin-color-primary: #B8611A;--anglin-rgb-primary: 184 97 26;Always verify WCAG contrast after changing any interactive color. The accent must meet 4.5:1 on its background (AA minimum for text). Use https://webaim.org/resources/contrastchecker/.
To add a new layout mode:
- Add a new
[data-layout="my-mode"]block and.dark [data-layout="my-mode"]block inlayouts.css, following the same variable structure as the existing modes - Add the
cccolor token mappings totailwind.config.jsin any consuming app (thecckey already maps all--cc-*vars β no change needed if the variable names match) - Apply
data-layout="my-mode"to a wrapper element or<body>
Publishing after a change
After editing layouts.css, publish a new version so consuming apps can pick up the change:
cd anglinai-monorepo/packages/uinpm version patch # or minor/major as appropriatenpm run buildnpm publishThen in each consuming app:
npm update @anglinai/uiThe apps will pick up the new layouts.css on the next build with no code changes required.
Dark Mode Setup
1. Import theme.css
In your root globals.css, import before @tailwind directives:
@import '@anglinai/ui/theme.css';
@tailwind base;@tailwind components;@tailwind utilities;2. Add ThemeScript to prevent FOUC
In your root layout.tsx (Next.js App Router), place ThemeScript inside <head> before the first paint:
import { ThemeScript, ThemeProvider } from '@anglinai/ui';
export default function RootLayout({ children }) { return ( <html lang="en" suppressHydrationWarning> <head> <ThemeScript /> </head> <body> <ThemeProvider defaultTheme="system"> {children} </ThemeProvider> </body> </html> );}
suppressHydrationWarningon<html>is required becauseThemeScriptmodifies the class list before React hydrates.
3. Use the useTheme hook
'use client';import { useTheme } from '@anglinai/ui';
export function ThemeToggle() { const { theme, resolvedTheme, setTheme } = useTheme();
return ( <button onClick={() => setTheme(resolvedTheme === 'dark' ? 'light' : 'dark')}> {resolvedTheme === 'dark' ? 'βοΈ Light' : 'π Dark'} </button> );}useTheme returns
| Field | Type | Description |
|---|---|---|
theme | 'light' | 'dark' | 'system' | The stored user preference |
resolvedTheme | 'light' | 'dark' | What is actually rendered right now |
setTheme | (t: Theme) => void | Persists to localStorage and applies immediately |
The default localStorage key is anglin-theme. Override it via <ThemeScript storageKey="my-key" /> and <ThemeProvider storageKey="my-key">.
How dark mode works
ThemeScriptruns as a blocking inline script before paint β readslocalStorage, falls back toprefers-color-scheme, setsclass="dark"anddata-theme="dark"on<html>.- Tailwindβs
darkMode: 'class'activates the.darkvariant for alldark:*utilities. - All
--anglin-*CSS variables defined intheme.cssswap to their dark-mode values inside.dark {}. ThemeProvidermanages React state and re-syncs on system preference changes whentheme === 'system'.
TypeScript: Color Palette Constants
Import raw palette values for non-CSS contexts (e.g. chart libraries, canvas, email templates):
import { COLOR_PALETTE, CONTRAST_TABLE } from '@anglinai/ui';
// Use in a chart configconst chartColors = { primary: COLOR_PALETTE.blueDeep, // '#054fb9' secondary: COLOR_PALETTE.orangeVivid, // '#f57600'};
// Print contrast auditCONTRAST_TABLE.forEach(({ token, ratio, wcag }) => console.log(`${token}: ${ratio} ${wcag}`));COLOR_PALETTE keys
| Key | Value | WCAG |
|---|---|---|
blueDeep | #054fb9 | AAA |
blueHoverAaa | #0057b0 | AAA |
blueSky | #8babf1 | AAA |
orangeDarkAaa | #8b3200 | AAA |
orangeVivid | #f57600 | AAA |
blueMid | #0073e6 | AA (reference) |
orangeDark | #c44601 | AA (reference) |
Installation in a New Project
# Add .npmrc if not presentecho "@anglinai:registry=https://npm.pkg.github.com" >> .npmrcecho "//npm.pkg.github.com/:_authToken=\${NODE_AUTH_TOKEN}" >> .npmrc
NODE_AUTH_TOKEN=<your-github-pat> npm install @anglinai/uiThen follow the Dark Mode Setup section above.
Migration: Replacing Old Colors
These search patterns find old primary blues and orange accents to migrate to v2 utilities:
# Old primary bluesgrep -rn "#2563eb\|#1d4ed8\|#1e40af\|#0284c7\|#0369a1\|#0ea5e9" src/
# Old orange/accent valuesgrep -rn "#f97316\|#ea580c\|#1E40AF\|#3B82F6" src/
# Old Tailwind primary-scale classesgrep -rn "bg-primary-\|text-primary-\|border-primary-\|ring-primary-" src/Known hardcoded values to migrate later
accessible-org-chart:
src/utils/versionCheck.ts:33β'color: #2563eb'β'color: #054fb9'src/components/templates/org-chart-radial/themes.tsβ SVG theme colors (useanglin-static-*values)src/app/preview/page.tsx:45β'#1e40af'in color picker preset
Replacement map
| Old value | New CSS var | New Tailwind class |
|---|---|---|
#2563eb | var(--anglin-color-primary) | text-anglin-primary |
#1d4ed8 | var(--anglin-color-primary-hover) | bg-anglin-primary-hover |
#1e40af | var(--anglin-color-primary) | bg-anglin-primary |
#0284c7 | var(--anglin-color-primary) | bg-anglin-primary |
#eff6ff | var(--anglin-color-surface-muted) | bg-anglin-surface-muted |
#f8fafc | var(--anglin-color-surface-subtle) | bg-anglin-surface-subtle |
#f97316 | var(--anglin-color-vivid) | bg-anglin-vivid |
Files Reference
| File | Package path | Purpose |
|---|---|---|
src/theme.css | @anglinai/ui/theme.css | Base v2 semantic CSS vars β safe to import anywhere, no @tailwind directives |
src/layouts.css | @anglinai/ui/layouts.css | Layout-mode overrides (control-center, standard) β import after theme.css |
src/globals.css | @anglinai/ui/globals.css | CSS vars + @tailwind directives β used in the UI package itself |
tailwind.config.cjs | @anglinai/ui/tailwind-preset | Tailwind preset with all anglin-* and cc-* utilities |
src/tokens/palette.ts | @anglinai/ui (named export) | COLOR_PALETTE, CONTRAST_TABLE, PaletteColor |
src/components/theme-provider.tsx | @anglinai/ui (named export) | ThemeProvider, useTheme, Theme type |
src/components/theme-script.tsx | @anglinai/ui (named export) | ThemeScript β FOUC prevention |
Consuming app globals.css pattern
@import '@anglinai/ui/theme.css'; /* 1. base semantic tokens */@import '@anglinai/ui/layouts.css'; /* 2. layout-mode overrides */
@tailwind base;@tailwind components;@tailwind utilities;
/* App-specific component classes only below here */Changelog
| Version | Date | Changes |
|---|---|---|
| v0.2.5 | 2026-03-02 | Added layouts.css β canonical shared layout-mode themes (copper CC + standard blue). Both apps now inherit from the package instead of duplicating the token blocks locally. accessible-pdf-converter primary palette aligned to v2 #054fb9. |
| v0.2.0 | 2026-02-24 | v2 color system β WCAG AAA palette, CSS vars, Tailwind preset, ThemeProvider, ThemeScript |
| v0.1.1 | 2025 | Initial release with legacy anglin.deep-blue utilities |