tai-ui TradeAIAlgo UI library
Theme LightDarkSystem

Web Components · WCAG 2.2 AA

Accessible components for dense financial interfaces

tai-ui is a small, framework-free library of custom elements and design tokens built for TradeAIAlgo, an AI-assisted options trading platform. Every component is used in the real app, and every colour pair is contrast-tested — including on glass.

Glass that passes

Sample index
24,180.35
Change today
▲ 112.40 (0.47%)
Bank sample
▼ 86.15 (0.16%)

Text and up/down colours on this translucent card are checked against the worst backdrop behind it. Illustrative numbers.

Components

Principles

  • Semantic HTML first; ARIA only to fill gaps. A native <dialog>, a real <table>, form participation through ElementInternals. ARIA is added where HTML has no element for the job (tabs, radio groups that look like buttons).
  • Keyboard parity. Everything a mouse can do, a keyboard can — following the ARIA Authoring Practices keyboard models, tested in a real browser.
  • Never colour alone. Up/down, selected, at-the-money: each also has text, shape or an icon.
  • Tokens that are tested, not hoped. Contrast is computed against real backdrops; a failing pair fails the build.
  • Respect the user's settings. Reduced motion, increased contrast, reduced transparency and Windows forced colours each change behaviour.
  • Framework-free, React-friendly. Plain custom elements. React 19 uses them directly — properties, events and all.

Design tokens

Colour, type, spacing, radius, motion and focus tokens are CSS custom properties (--tai-*) generated from one source file. The app switches theme with <html data-theme>; without it, tokens follow the operating system. Each chip shows light (top-left) and dark (bottom-right).

  • --tai-color-fg#0E1A2B · #E7EBF3
  • --tai-color-fg-muted#475569 · #C3CAD7
  • --tai-color-fg-subtle#566174 · #9AA4B6
  • --tai-color-canvas#F3F6FB · #0F1218
  • --tai-color-surface#FFFFFF · #171B23
  • --tai-color-border-control#6F7B8F · #6B768A
  • --tai-color-accent#1D4ED8 · #2563EB
  • --tai-color-focus#1D4ED8 · #8AB4FF
  • --tai-color-up#05694C · #34D399
  • --tai-color-down#B42318 · #F87171
  • --tai-color-danger#B42318 · #DC2626
  • --tai-color-highlight#FDE68A · #4A3B0A

Contrast, measured

“Glass” means the translucent surface composited over each reference backdrop — white, the top and the deepest blue of the homepage hero (light), or the graphite canvas, surface and panel (dark). The ratio shown is the worst case. The unit tests run the same calculation.

Used forThemeForegroundWorst backgroundRatioNeedsResult
Body text Light fg surface 17.48:1 4.5:1 Pass
Secondary text Light fg-muted surface 7.57:1 4.5:1 Pass
Captions Light fg-subtle surface 6.25:1 4.5:1 Pass
Text on the page Light fg canvas 16.13:1 4.5:1 Pass
Captions on the page Light fg-subtle canvas 5.77:1 4.5:1 Pass
Captions on sunken panels Light fg-subtle surface-sunken 5.70:1 4.5:1 Pass
Text on glass Light fg glass on hero deep 15.84:1 4.5:1 Pass
Secondary text on glass Light fg-muted glass on hero deep 6.86:1 4.5:1 Pass
Captions on glass Light fg-subtle glass on hero deep 5.66:1 4.5:1 Pass
Price up Light up surface 6.70:1 4.5:1 Pass
Price down Light down surface 6.57:1 4.5:1 Pass
Price up on glass Light up glass on hero deep 6.07:1 4.5:1 Pass
Price down on glass Light down glass on hero deep 5.96:1 4.5:1 Pass
Price up on the hero Light up hero deep 4.61:1 4.5:1 Pass
Price down on the hero Light down hero deep 4.52:1 4.5:1 Pass
Links Light accent-fg surface 6.70:1 4.5:1 Pass
Primary button text Light on-accent accent 6.70:1 4.5:1 Pass
Primary button (hover) Light on-accent accent-hover 8.72:1 4.5:1 Pass
Destructive button text Light on-danger danger 6.57:1 4.5:1 Pass
Error text Light danger-fg surface 6.57:1 4.5:1 Pass
Error banner text Light danger-fg danger-soft 5.74:1 4.5:1 Pass
Selected chip text Light accent-soft-fg accent-soft 8.91:1 4.5:1 Pass
Warning text Light warning-fg warning-soft 6.10:1 4.5:1 Pass
Highlighted row text Light on-highlight highlight 14.03:1 4.5:1 Pass
Control borders Light border-control surface 4.28:1 3:1 Pass
Control borders on the page Light border-control canvas 3.95:1 3:1 Pass
Focus ring Light focus surface 6.70:1 3:1 Pass
Focus ring on the page Light focus canvas 6.18:1 3:1 Pass
Focus ring on glass Light focus glass on hero deep 6.07:1 3:1 Pass
Selected state fill Light accent surface 6.70:1 3:1 Pass
Body text Dark fg surface 14.43:1 4.5:1 Pass
Secondary text Dark fg-muted surface 10.47:1 4.5:1 Pass
Captions Dark fg-subtle surface 6.86:1 4.5:1 Pass
Text on the page Dark fg canvas 15.69:1 4.5:1 Pass
Captions on the page Dark fg-subtle canvas 7.46:1 4.5:1 Pass
Captions on sunken panels Dark fg-subtle surface-sunken 6.42:1 4.5:1 Pass
Text on glass Dark fg glass on panel 13.50:1 4.5:1 Pass
Secondary text on glass Dark fg-muted glass on panel 9.79:1 4.5:1 Pass
Captions on glass Dark fg-subtle glass on panel 6.42:1 4.5:1 Pass
Price up Dark up surface 8.97:1 4.5:1 Pass
Price down Dark down surface 6.23:1 4.5:1 Pass
Price up on glass Dark up glass on panel 8.39:1 4.5:1 Pass
Price down on glass Dark down glass on panel 5.83:1 4.5:1 Pass
Price up on the hero Dark up panel 6.58:1 4.5:1 Pass
Price down on the hero Dark down panel 4.57:1 4.5:1 Pass
Links Dark accent-fg surface 8.25:1 4.5:1 Pass
Primary button text Dark on-accent accent 5.16:1 4.5:1 Pass
Primary button (hover) Dark on-accent accent-hover 6.70:1 4.5:1 Pass
Destructive button text Dark on-danger danger 4.82:1 4.5:1 Pass
Error text Dark danger-fg surface 6.23:1 4.5:1 Pass
Error banner text Dark danger-fg danger-soft 5.64:1 4.5:1 Pass
Selected chip text Dark accent-soft-fg accent-soft 10.02:1 4.5:1 Pass
Warning text Dark warning-fg warning-soft 8.14:1 4.5:1 Pass
Highlighted row text Dark on-highlight highlight 8.77:1 4.5:1 Pass
Control borders Dark border-control surface 3.76:1 3:1 Pass
Control borders on the page Dark border-control canvas 4.09:1 3:1 Pass
Focus ring Dark focus surface 8.25:1 3:1 Pass
Focus ring on the page Dark focus canvas 8.97:1 3:1 Pass
Focus ring on glass Dark focus glass on panel 7.72:1 3:1 Pass
Selected state fill Dark accent surface 3.33:1 3:1 Pass

When transparency isn't wanted

Under prefers-reduced-transparency or prefers-contrast: more, glass becomes the solid surface colour and the blur is removed. Increased contrast also strengthens borders, darkens caption text and thickens focus rings to 3px.

Using it in an app

JS

// Once, before your UI renders (in Next.js: a small 'use client' module)

import '@tradeaialgo/tai-ui'
import '@tradeaialgo/tai-ui/tokens.css'

React 19 sets known properties as properties and turns ontai-change-style props into event listeners, so there are no wrapper components. Types for JSX are in @tradeaialgo/tai-ui/react.

Server rendering (Next.js)

  • The library imports safely on the server — elements are only defined in the browser.
  • Elements add their ARIA (role, aria-checked, roving tabindex) when they upgrade, which can happen before React hydrates. That's intended, so mark server-rendered tai-* elements with suppressHydrationWarning.
  • Pass arrays and functions (columns, rows) only from client components; they're set as properties in the browser.
  • Page-wide resets like Tailwind's preflight can't break the components: box styles live inside the shadow root, not on :host.

Contributing a component

  1. Start from a pattern. Find the closest native element first, then the ARIA Authoring Practices pattern. Write the keyboard table before any code.
  2. Create the folder src/components/tai-name/ with the element, styles built only from --tai-* tokens, and JSDoc for every attribute, property, event, slot and part.
  3. Decide shadow or light DOM — shadow for encapsulated widgets; light when native semantics must stay in the document (tables, form labels).
  4. Test three ways: unit tests (Vitest + happy-dom), keyboard tests that follow the APG model (Playwright, real Chrome), and axe-core with zero violations in every documented state.
  5. Document it with the same sections as the other pages: live example, API, keyboard, accessibility notes with the decisions you made, do/don't, and a React snippet.

Review checklist

  • Every interactive part is reachable and operable by keyboard, with a visible focus ring (3:1).
  • Name, role and state are correct in the accessibility tree.
  • No information is carried by colour alone.
  • Works in light, dark, increased contrast and forced colours; animation stops under reduced motion.
  • Events bubble and are composed; nothing is announced more often than a person can follow.
  • Docs examples use made-up data.