Skip to content

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

·CODE
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
·CODE
# Run without unicode, motion, or colors (good for CI)
NO_UNICODE=1 NO_MOTION=1 NO_COLOR=1 bun app.ts

# Run your tests with the same constraints
NO_UNICODE=1 NO_MOTION=1 bun test

Writing ASCII fallbacks

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

·CODE
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:

·CODE
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:

·CODE
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:

·CODE
import { contrastRatio, wcagLevel, parseColor } from '@termuijs/core'

const fg = parseColor('#ffffff')
const bg = parseColor('#0a0a0f')

const ratio = contrastRatio(fg, bg)   // → 18.1 (a good ratio)
wcagLevel(ratio)                      // → 'AAA' (≥ 7:1 for normal text)

WCAG levels:

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

Use validateThemeContrast to check a theme's color pairs before shipping. It returns the pairs that fall below AA:

·CODE
import { validateThemeContrast } from '@termuijs/core'
import { nordTheme } from '@termuijs/tss'

const failures = validateThemeContrast(nordTheme)
console.log('Nord contrast failures:', failures)

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

·CODE
# 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