Skip to content

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.

TokenHexOnRatioLevel
Primary (deep blue)#054fb9white7.3:1βœ… AAA
Primary hover#0057b0white7.1:1βœ… AAA
Accent / sky blue#8babf1black9.2:1βœ… AAA
Secondary (dark orange)#8b3200white7.2:1βœ… AAA
Vivid orange#f57600black7.4:1βœ… AAA

Aspirational tokens (AA only on white β€” stored as references, not for direct use in UI):

TokenHexRatioNote
#0073e6mid blue4.6:1 AADesign reference only
#c44601orange4.8:1 AADesign 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

tailwind.config.js
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-primary
bg-anglin-primary-hover hover:bg-anglin-primary-hover
bg-anglin-primary-subtle text-anglin-primary-subtle
bg-anglin-on-primary text-anglin-on-primary
bg-anglin-secondary text-anglin-secondary
bg-anglin-secondary-hover hover:bg-anglin-secondary-hover
bg-anglin-secondary-subtle
bg-anglin-on-secondary text-anglin-on-secondary
bg-anglin-accent text-anglin-accent
bg-anglin-on-accent text-anglin-on-accent
bg-anglin-vivid text-anglin-vivid
bg-anglin-on-vivid text-anglin-on-vivid
bg-anglin-surface bg-anglin-surface-subtle bg-anglin-surface-muted
text-anglin-on-surface text-anglin-on-surface-muted
border-anglin-border

Static hex classes (for SVG/canvas where CSS vars don’t work)

bg-anglin-static-blue-deep text-anglin-static-blue-deep
bg-anglin-static-blue-hover
bg-anglin-static-blue-sky text-anglin-static-blue-sky
bg-anglin-static-orange-dark
bg-anglin-static-orange-vivid text-anglin-static-orange-vivid

Example 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 valueSchemeUse 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

globals.css
@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.css

To change a color value in a mode:

  1. Open packages/ui/src/layouts.css
  2. Find the relevant [data-layout="…"] block (light) or .dark [data-layout="…"] block
  3. Update the hex value(s)
  4. Also update the matching --anglin-rgb-* channel variable (space-separated R G B, used for Tailwind opacity modifiers like /50)
  5. Bump the patch version in packages/ui/package.json
  6. 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:

  1. Add a new [data-layout="my-mode"] block and .dark [data-layout="my-mode"] block in layouts.css, following the same variable structure as the existing modes
  2. Add the cc color token mappings to tailwind.config.js in any consuming app (the cc key already maps all --cc-* vars β€” no change needed if the variable names match)
  3. 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:

Terminal window
cd anglinai-monorepo/packages/ui
npm version patch # or minor/major as appropriate
npm run build
npm publish

Then in each consuming app:

Terminal window
npm update @anglinai/ui

The 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>
);
}

suppressHydrationWarning on <html> is required because ThemeScript modifies 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

FieldTypeDescription
theme'light' | 'dark' | 'system'The stored user preference
resolvedTheme'light' | 'dark'What is actually rendered right now
setTheme(t: Theme) => voidPersists 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

  1. ThemeScript runs as a blocking inline script before paint β€” reads localStorage, falls back to prefers-color-scheme, sets class="dark" and data-theme="dark" on <html>.
  2. Tailwind’s darkMode: 'class' activates the .dark variant for all dark:* utilities.
  3. All --anglin-* CSS variables defined in theme.css swap to their dark-mode values inside .dark {}.
  4. ThemeProvider manages React state and re-syncs on system preference changes when theme === '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 config
const chartColors = {
primary: COLOR_PALETTE.blueDeep, // '#054fb9'
secondary: COLOR_PALETTE.orangeVivid, // '#f57600'
};
// Print contrast audit
CONTRAST_TABLE.forEach(({ token, ratio, wcag }) =>
console.log(`${token}: ${ratio} ${wcag}`)
);

COLOR_PALETTE keys

KeyValueWCAG
blueDeep#054fb9AAA
blueHoverAaa#0057b0AAA
blueSky#8babf1AAA
orangeDarkAaa#8b3200AAA
orangeVivid#f57600AAA
blueMid#0073e6AA (reference)
orangeDark#c44601AA (reference)

Installation in a New Project

Terminal window
# Add .npmrc if not present
echo "@anglinai:registry=https://npm.pkg.github.com" >> .npmrc
echo "//npm.pkg.github.com/:_authToken=\${NODE_AUTH_TOKEN}" >> .npmrc
NODE_AUTH_TOKEN=<your-github-pat> npm install @anglinai/ui

Then 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:

Terminal window
# Old primary blues
grep -rn "#2563eb\|#1d4ed8\|#1e40af\|#0284c7\|#0369a1\|#0ea5e9" src/
# Old orange/accent values
grep -rn "#f97316\|#ea580c\|#1E40AF\|#3B82F6" src/
# Old Tailwind primary-scale classes
grep -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 (use anglin-static-* values)
  • src/app/preview/page.tsx:45 β€” '#1e40af' in color picker preset

Replacement map

Old valueNew CSS varNew Tailwind class
#2563ebvar(--anglin-color-primary)text-anglin-primary
#1d4ed8var(--anglin-color-primary-hover)bg-anglin-primary-hover
#1e40afvar(--anglin-color-primary)bg-anglin-primary
#0284c7var(--anglin-color-primary)bg-anglin-primary
#eff6ffvar(--anglin-color-surface-muted)bg-anglin-surface-muted
#f8fafcvar(--anglin-color-surface-subtle)bg-anglin-surface-subtle
#f97316var(--anglin-color-vivid)bg-anglin-vivid

Files Reference

FilePackage pathPurpose
src/theme.css@anglinai/ui/theme.cssBase v2 semantic CSS vars β€” safe to import anywhere, no @tailwind directives
src/layouts.css@anglinai/ui/layouts.cssLayout-mode overrides (control-center, standard) β€” import after theme.css
src/globals.css@anglinai/ui/globals.cssCSS vars + @tailwind directives β€” used in the UI package itself
tailwind.config.cjs@anglinai/ui/tailwind-presetTailwind 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

VersionDateChanges
v0.2.52026-03-02Added 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.02026-02-24v2 color system β€” WCAG AAA palette, CSS vars, Tailwind preset, ThemeProvider, ThemeScript
v0.1.12025Initial release with legacy anglin.deep-blue utilities