mrkeyoor.com_
Tue 22 Sept 22:31 UTC
npmWeb Frontendupdated 22 Sept 2026

@ctrl/tinycolor review

Our @ctrl/tinycolor 4.2.0 browser build came to 17.3 KB minified and 6.4 KB gzipped. The package parses forgiving color input, converts among hex, RGB, HSL, HSV, and CMYK, changes alpha or brightness, builds color combinations, and calculates WCAG 2.0 contrast. It is a TypeScript fork of tinycolor2 with a named TinyColor class and standalone helpers such as readability, fromRatio, and random. Version 4.2.0 changed the release process to publish with npm provenance; the latest user-facing addition is CMYK input from 4.1.0.

Verdict

@ctrl/tinycolor 4.2.0 installed in 1.5 seconds and added 6.4 KB gzipped in our browser build, with bundled types and no audit findings. Install it for forgiving UI color entry and familiar manipulation methods; skip it when strict validation, immutable values, or OKLCH-class color work sets the requirements.

We installed it

Lab card: what happened when we installed @ctrl/tinycolorScreenshot of @ctrl/tinycolor documentation
Install✓ · 1.5s1 package on disk · 1 MB
ImportESM import works · require() works · CommonJS package
Browser6.4 KBgzipped (17.3 KB minified), bundled with esbuild
TypesTypeScript types bundled
Known vulns00 critical · 0 high · 0 moderate · 0 low (npm audit)

Answers from our run

Does @ctrl/tinycolor install cleanly?

Yes. In a fresh container with an empty cache, npm install @ctrl/tinycolor finished in 2 seconds, leaving 1 package and 1 MB on disk. npm audit reported no known vulnerabilities.

How much does @ctrl/tinycolor add to a browser bundle?

6.4 KB gzipped (17.3 KB minified) when the whole package is bundled for the browser with esbuild. Importing only part of it is usually smaller.

Does @ctrl/tinycolor work with both ESM and CommonJS?

Yes. Both import '@ctrl/tinycolor' and require('@ctrl/tinycolor') worked in Node 22 in our run. The package is published as CommonJS.

Does @ctrl/tinycolor include TypeScript types?

Yes, type declarations ship inside the package, so no @types install is needed.

@ctrl/tinycolor or culori: which should you use?

culori: Choose it when modern perceptual color spaces, gamut mapping, or individually imported functions matter more than tinycolor-style chaining. @ctrl/tinycolor 4.2.0 installed in 1.5 seconds and added 6.4 KB gzipped in our browser build, with bundled types and no audit findings.

When should you not use @ctrl/tinycolor?

Unrecognized input must stop immediately. The README says parsing is permissive, and an invalid instance behaves like black unless code checks the isValid property.

API stability4/5Version 4.2.0 retains the TinyColor class, named exports, conversion methods, mutation behavior, palette helpers, and accessibility functions documented across the current major. The fork clearly lists its deliberate differences from tinycolor2, including the missing default export and relocated helpers. A future major could still revisit packaging, and consumers migrating from tinycolor2 must change imports and property access before the API feels stable.
Docs5/5The project documents accepted hex, RGB, HSL, HSV, CMYK, object, name, and numeric inputs with runnable examples. It also spells out invalid-color behavior, every output format, mutating adjustments, palettes, seeded random generation, alpha compositing, WCAG 2.0 contrast options, and the tinycolor2 migration differences. The https://tinycolor.vercel.app/ documentation endpoint returned HTTP 200 during this rewrite.
Maintenance3/5Release 4.2.0 shipped on September 16, 2025 with npm provenance, and the repository was last pushed on September 18, 2025. GitHub shows 10 open issues and pull requests and does not mark the project archived. That is reasonable for a settled utility, but nearly a year without a push makes the signal weaker than its 4,538,898 weekly downloads might suggest.
Ecosystem4/5The npm endpoint counted 4,538,898 downloads from August 18 through August 24, 2026, while GitHub reports 614 stars. The API carries familiar tinycolor2 operations into TypeScript and works through both require() and ESM import in our test. It has 0 runtime dependencies, yet the JavaScript color ecosystem has moved further into perceptual spaces through packages such as culori and chroma-js.

Use it if

  • A color input field must accept several familiar CSS-like forms and return one normalized representation.
  • UI code needs tints, shades, complements, alpha compositing, or small palettes without a larger visualization toolkit.
  • You need typed WCAG 2.0 contrast calculations and can supply the correct text-size threshold yourself.
  • A tinycolor2 migration can accommodate named imports, standalone helpers, and the TinyColor class name.
Skip it if

Setup reality

We installed @ctrl/tinycolor 4.2.0 in a clean Node 22 Bookworm container. npm completed in 1.5 seconds, left 1 package, and used 1 MB on disk. The package itself was 348 KB unpacked, with 0 direct dependencies and 0 peers. npm audit found 0 known vulnerabilities. It bundles TypeScript declarations. require() and ESM import both worked even though the package is CommonJS and has no exports map. Our esbuild check produced 17.3 KB minified and 6.4 KB gzipped for a namespace import.

There is no config file, native compilation, credential, or runtime setup. Node 14 is the declared minimum. Import TinyColor by name because version 4 has no default export. Helpers that tinycolor2 users may remember as class methods have moved: readability and fromRatio are standalone exports, while the old random behavior is exposed separately from the newer random() helper. isValid and format are properties, so calling either as a function is an API mistake.

Input parsing deliberately accepts loose punctuation and several numeric forms. Bad input does not throw; it creates an invalid object whose other methods produce black-like results. Check color.isValid at the boundary before saving a value. HSL object output uses fractions for saturation and lightness, while formatted HSL strings use percentages. Alpha can also change the chosen string form, such as rgb() becoming rgba().

Most adjustment methods mutate and return the same object for chaining. Call clone() before creating variants from a shared base. For translucent colors, use onBackground() before readability() or isReadable(). The accessibility helpers calculate WCAG 2.0 ratios, but your code must choose AA or AAA and small or large text. Pass an integer seed to random() when server rendering or snapshots need the same color on every run.

Patterns

Reject a bad color at the boundary parse-and-reject-invalid

import { TinyColor } from '@ctrl/tinycolor';

const color = new TinyColor(userInput);
if (!color.isValid) throw new Error('Invalid color');
console.log(color.toHexString());

An invalid TinyColor does not throw and behaves like black in later methods. Check the isValid property first.

Normalize mixed input to hex normalize-to-hex

import { TinyColor } from '@ctrl/tinycolor';

function normalizeHex(input: string) {
  const color = new TinyColor(input);
  return color.isValid ? color.toHexString() : null;
}

console.log(normalizeHex('rgb(54, 153, 204)'));

toHexString() drops alpha. Use toHex8String() when the stored value must preserve transparency.

Convert RGBA to eight-digit hex preserve-alpha-channel

import { TinyColor } from '@ctrl/tinycolor';

const color = new TinyColor('rgba(255, 0, 0, 0.5)');
console.log(color.toHex8String()); // #ff000080

Eight-digit hex puts alpha in the final byte; rounding maps an alpha of 0.5 to 0x80.

Read HSL components and text convert-to-hsl

const color = new TinyColor('#ff8000');
console.log(color.toHsl());
console.log(color.toHslString());

toHsl() returns saturation and lightness as 0-to-1 fractions, while toHslString() writes percentages.

Lighten without changing the source derive-with-clone

const base = new TinyColor('#2563eb');
const lighter = base.clone().lighten(15);

console.log(base.toHexString());
console.log(lighter.toHexString());

lighten() mutates its receiver. clone() keeps the original TinyColor unchanged.

Blend two colors by percentage mix-two-colors

const mixed = new TinyColor('#ff00ff')
  .mix('#00ff00', 50)
  .toHexString();

console.log(mixed);

mix() changes the first TinyColor instance, and its amount is a percentage between 0 and 100.

Generate four wheel positions build-tetrad-palette

const palette = new TinyColor('#ef4444')
  .tetrad()
  .map((entry) => entry.toHexString());

Palette methods return TinyColor objects. Convert every entry to the output form your UI or database expects.

Measure foreground contrast calculate-contrast-ratio

import { readability } from '@ctrl/tinycolor';

const ratio = readability('#111827', '#ffffff');
console.log(ratio);

readability() returns the numeric WCAG 2.0 ratio; passing criteria still depend on text size and target level.

Test an AA small-text pair check-aa-small-text

import { isReadable } from '@ctrl/tinycolor';

const passes = isReadable('#111827', '#ffffff', {
  level: 'AA',
  size: 'small',
});

The package cannot inspect font size or weight. Your code must choose small or large correctly.

Pick black or white text choose-readable-foreground

import { mostReadable } from '@ctrl/tinycolor';

const foreground = mostReadable('#7c3aed', ['#000', '#fff'], {
  level: 'AA',
  size: 'small',
});
console.log(foreground.toHexString());

mostReadable() returns a TinyColor object. With includeFallbackColors enabled, it may return black or white even if neither was supplied.

Resolve alpha before contrast composite-transparent-color

import { TinyColor, readability } from '@ctrl/tinycolor';

const background = '#ffffff';
const displayed = new TinyColor('rgba(37, 99, 235, 0.5)')
  .onBackground(background);
console.log(readability(displayed, background));

A translucent color has no single displayed contrast until it is composited over a known background.

Seed random color output generate-repeatable-random-color

import { random } from '@ctrl/tinycolor';

const color = random({
  seed: 42,
  hue: 'blue',
  luminosity: 'light',
});
console.log(color.toHexString());

An integer seed makes random() repeatable for snapshots and server-rendered markup.

Alternatives

PackageRegistryPick it when
culorinpmChoose it when modern perceptual color spaces, gamut mapping, or individually imported functions matter more than tinycolor-style chaining.
colornpmChoose it when an immutable chainable object is preferable and its supported CSS conversion set covers the application.
chroma-jsnpmChoose it for data-visualization scales, interpolation modes, and numeric color analysis beyond UI color adjustments.

More web frontend guides

postcss · react · react-dom · tailwindcss · htmlparser2 · tailwind-merge · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.