Skip to content
Updated May 2026Edit this page ↗

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

TYPESCRIPT
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=1

These are read once at startup from environment variables. They're plain booleans — check them anywhere in your code.

Environment variables

VariableSetsWhen to use
NO_UNICODE=1caps.unicode = falseCI environments, PuTTY, Windows cmd.exe, any terminal with incomplete Unicode support
NO_MOTION=1caps.motion = falseReduced motion preference, screen readers, recording terminal output
NO_COLOR=1caps.color = falseLog files, piped output, color-blind users, any context where ANSI colors break the output
TERMINAL
# 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 test

Writing ASCII fallbacks

When you use custom unicode characters in your own widgets or components, guard them with caps.unicode:

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

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

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

WidgetNO_UNICODENO_MOTION
SpinnerASCII frames |/-\Static char, no animation
ProgressBar# / . instead of / N/A (static)
SkeletonStatic blockStatic block, no pulse/shimmer
Gauge# / . bar charsN/A
Sparkline18 digitsN/A
StreamingText_ cursorFull text shown immediately
HeatMap. : # @ shadingN/A
LineChart* / \ - plot charsN/A
StatusMessage[OK]/[X] iconsN/A
BannerPlain border charsN/A

WCAG color contrast utilities

@termuijs/core includes utilities for checking WCAG color contrast ratios:

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

LevelNormal textLarge text (≥ 18pt bold)
AA≥ 4.5:1≥ 3:1
AAA≥ 7:1≥ 4.5:1

Use these to validate custom theme colors before shipping:

TYPESCRIPT
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)

Add this to your test script to catch unicode/motion regressions early:

TERMINAL
# 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