Accessibility & caps Flags
TermUI apps run in diverse environments — CI pipelines, remote SSH sessions, accessibility tools, and terminals that don't support unicode or colors. The caps object tells your app what the current environment supports.
The caps object
import { caps } from '@termuijs/core'
caps.unicode // boolean — false when NO_UNICODE=1
caps.motion // boolean — false when NO_MOTION=1
caps.color // boolean — false when NO_COLOR=1These are read once at startup from environment variables. They're plain booleans — check them anywhere in your code.
Environment variables
| Variable | Sets | When to use |
|---|---|---|
NO_UNICODE=1 | caps.unicode = false | CI environments, PuTTY, Windows cmd.exe, any terminal with incomplete Unicode support |
NO_MOTION=1 | caps.motion = false | Reduced motion preference, screen readers, recording terminal output |
NO_COLOR=1 | caps.color = false | Log files, piped output, color-blind users, any context where ANSI colors break the output |
# Run without unicode, motion, or colors (good for CI)
NO_UNICODE=1 NO_MOTION=1 NO_COLOR=1 node app.js
# Run your tests with the same constraints
NO_UNICODE=1 NO_MOTION=1 pnpm testWriting ASCII fallbacks
When you use custom unicode characters in your own widgets or components, guard them with caps.unicode:
import { caps } from '@termuijs/core'
// Common fallback pairs
const check = caps.unicode ? '✓' : '[OK]'
const cross = caps.unicode ? '✗' : '[X]'
const warn = caps.unicode ? '⚠' : '[!]'
const info = caps.unicode ? 'ℹ' : '[i]'
const bullet = caps.unicode ? '●' : '*'
const arrow = caps.unicode ? '▶' : '>'
const bar = caps.unicode ? '█' : '#'
const empty = caps.unicode ? '░' : '.'The same pattern applies to box-drawing characters used in custom borders or dividers.
Writing motion fallbacks
For animations built with setInterval or timerPoolSubscribe:
import { caps, timerPoolSubscribe } from '@termuijs/core'
function startAnimation() {
if (!caps.motion) {
// Render final state immediately
setFrame(FINAL_FRAME)
return () => {} // no-op cleanup
}
const unsub = timerPoolSubscribe(100, tick)
return unsub
}Alternatively, use the useMotion hook in JSX components:
import { useMotion } from '@termuijs/jsx'
function Indicator() {
const { prefersReducedMotion } = useMotion()
useEffect(() => {
if (prefersReducedMotion) return
const unsub = timerPoolSubscribe(500, blink)
return unsub
}, [prefersReducedMotion])
return <Text>●</Text>
}Built-in widget support
All built-in TermUI widgets respect the caps flags automatically — you don't need to add guards when using them:
| Widget | NO_UNICODE | NO_MOTION |
|---|---|---|
| Spinner | ASCII frames |/-\ | Static char, no animation |
| ProgressBar | # / . instead of █ / ░ | N/A (static) |
| Skeleton | Static block | Static block, no pulse/shimmer |
| Gauge | # / . bar chars | N/A |
| Sparkline | 1–8 digits | N/A |
| StreamingText | _ cursor | Full text shown immediately |
| HeatMap | . : # @ shading | N/A |
| LineChart | * / \ - plot chars | N/A |
| StatusMessage | [OK]/[X] icons | N/A |
| Banner | Plain border chars | N/A |
WCAG color contrast utilities
@termuijs/core includes utilities for checking WCAG color contrast ratios:
import { contrastRatio, meetsAA, meetsAAA } from '@termuijs/core'
const fg = '#ffffff'
const bg = '#0a0a0f'
contrastRatio(fg, bg) // → 18.1 (a good ratio)
meetsAA(fg, bg) // → true (requires ≥ 4.5:1 for normal text)
meetsAAA(fg, bg) // → true (requires ≥ 7:1)WCAG levels:
| Level | Normal text | Large text (≥ 18pt bold) |
|---|---|---|
| AA | ≥ 4.5:1 | ≥ 3:1 |
| AAA | ≥ 7:1 | ≥ 4.5:1 |
Use these to validate custom theme colors before shipping:
import { contrastRatio, meetsAA } from '@termuijs/core'
import { nordTheme } from '@termuijs/tss'
const ok = meetsAA(nordTheme['--text'], nordTheme['--bg'])
console.log('Nord text contrast passes AA:', ok)Recommended CI configuration
Add this to your test script to catch unicode/motion regressions early:
# package.json
{
"scripts": {
"test:a11y": "NO_UNICODE=1 NO_MOTION=1 vitest run"
}
}This runs your full test suite with the most restrictive caps settings — the same environment a developer might use over SSH or in a bare Linux container.
See also
- useMotion — hook for guarding custom animations
- Core: String Utilities —
capsobject reference and string width helpers