Barva 1.3.0 — truecolor, level detection, and proper types
Barva 1.3.0 brings 24-bit colour, 256-palette support, automatic downgrade, a richer modifier set, and fully typed chaining — while keeping the tagged-template API tiny and tree-shakable.
Barva — the tiny tagged-template terminal colour library — just shipped 1.3.0. It’s still zero-dependency and tree-shakable, but it now does a lot more out of the box.
Truecolor and the 256 palette
Up until 1.2 you had the 16 basic ANSI colours and their bright variants. As of 1.3, you get the full range:
1import { rgb, hex, ansi256, bgHex, bold } from 'barva';
2
3console.log(rgb(255, 128, 0)`orange`);
4console.log(hex('#ff80c0')`pink`);
5console.log(ansi256(196)`bright red`);
6
7// All chainable with modifiers and basic colours
8console.log(bold.rgb(0, 200, 180).bgHex('#101820')`teal on navy`);
Crucially, if the terminal doesn’t advertise truecolor support, the sequence is automatically downgraded to the best available palette — 256 or classic 16 — using the standard cube and grayscale-ramp math. Your code doesn’t have to care about terminal capabilities.
Custom colour aliases
Because every factory call (rgb, hex, ansi256, …) and every chain
access (red.bold, bold.bgYellow, …) returns a reusable colorizer, you
can define your own palette with nothing more than a variable:
1import { rgb, hex, red, bold, ansi256 } from 'barva';
2
3// Colour aliases
4const orange = rgb(255, 128, 0);
5const brandPink = hex('#ff80c0');
6const highlight = ansi256(196);
7
8// Style aliases — full chains work the same way
9const error = red.bold.underline;
10const warn = hex('#ffa500').bold;
11const success = bold.rgb(0, 200, 120);
12
13console.log(orange`this is orange`);
14console.log(error`something went wrong`);
15console.log(warn`heads up — ${42} retries left`);
16console.log(success`done`);
Two bonuses come from the internal cache: calling rgb(255, 128, 0) twice
from different modules returns the same instance, so scattering these
around your codebase is free; and any alias is still a full colorizer, so
you can keep chaining it (orange.bold\…``).
Levels instead of on/off
The old setEnabled() boolean is still there, but there’s now a proper
colour-level concept:
1import { getLevel, setLevel } from 'barva';
2
3getLevel(); // 0 | 1 | 2 | 3
4setLevel(3); // force truecolor output
5setLevel(undefined); // re-run environment detection
Detection is thorough: NO_COLOR, FORCE_COLOR, TERM, COLORTERM,
WT_SESSION, VS Code’s terminal, and a long list of CI providers
(GitHub Actions / Gitea / CircleCI at truecolor; GitLab, Travis, AppVeyor,
Buildkite, Drone, Codeship, Azure Pipelines, TeamCity, AWS CodeBuild,
Bitbucket Pipelines, Vercel, Netlify, Semaphore, Cirrus, Heroku, Woodpecker
at basic). Results are memoised, so repeated checks are free.
More modifiers
blink, doubleUnderline, framed, encircled, overline, superscript,
and subscript round out the SGR set. A bare reset colorizer is exported
too, for the odd case where you’re writing raw bytes to stdout and need to
cancel styling manually.
Utilities you probably already wanted
1import { strip, ansiRegex } from 'barva';
2
3strip(red.bold`hi ${blue`there`}!`); // => "hi there!"
4"…".replace(ansiRegex(), ''); // use the pattern directly
Type-safe chaining
In 1.2, TypeScript couldn’t follow chains like red.bold.underline — you
either got @ts-expect-error or had to cast. BarvaColorizer is now
recursively typed, so chains, plus the new .rgb()/.hex()/.ansi256()
methods, are fully type-checked. The helper types (BarvaColorizer,
ColorLevel, ModifierName, ForegroundName, BackgroundName) are all
exported.
Under the hood
A few changes worth noting:
- Symbol brand: colorizers are now identified by a module-private
Symbol, not a duck-typed_codesproperty — so plain objects with that field can’t be mistaken for a colorizer. - Stable ordering: chained codes always render in a deterministic order (modifiers first, then colours), regardless of how you chained them.
- 100 tests, 99.6% line coverage with an enforced 95% threshold. The
yarn test:coveragescript produces HTML, LCOV, and JSON summaries. - Every devDependency bumped to latest (ESLint 10,
@types/node25,globals17, esbuild 0.28, tsup 8.5, Jest 30.3, ts-jest 29.4.9, and friends).
Bundle size
The new features cost some bytes — but barva is still firmly in lightweight-library territory:
| Build | Minified | Gzipped |
|---|---|---|
| ESM | 8.1 KB | 3.4 KB |
| CJS | 9.3 KB | 3.7 KB |
Tree-shaking still works, so if you only import red and bold, the rest
won’t ship.
On the roadmap
One thing still deferred to a future release: a browser entry point that
emits console.log’s %c formatters instead of ANSI codes, so barva can
drop into isomorphic code without a second library. Tracked in TODO.md in
the repo.
Getting it
1yarn add barva
2# or
3npm install barva
4# or
5pnpm add barva
Source, changelog, and issues: https://github.com/magikMaker/barva .
