# tints-and-shades: a color tool that matches what Chrome DevTools actually computes

> The Tint and Shade Generator produces tints and shades from a hex color in 5, 10, and 20 percent steps using the same per-channel arithmetic as Chrome DevTools, and ships the same engine as an npm package for your own projects. The interesting part is the rounding rule and the four related palette schemes built on hue rotation.

**edelstone/tints-and-shades** — 🌈 Deterministic tints and shades from a given hex color.

- Repository: https://github.com/edelstone/tints-and-shades
- Website: https://maketintsandshades.com
- Stars: 588 · Forks: 64
- Language: JavaScript
- License: MIT
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/edelstone-tints-and-shades

## The premise is that most tint tools get the arithmetic wrong

The Tint and Shade Generator is described as a precision color tool for producing tints, which are lighter variants, and shades, which are darker variants, from a given hex color in 5%, 10%, or 20% increments. The argument for its existence is in the Why section, and it is a claim about correctness rather than about features. The author writes that in their experience similar tools get the calculations incorrect due to rounding errors, creator preferences, or other inconsistencies, and says the tool takes the math seriously. The README also claims that testing shows the output matches Chrome DevTools' calculation method as well as some established and popular methods to derive tints and shades via Sass. That framing explains the whole design. If a tool exists to be right where others are loose, then the algorithm and its rounding behaviour are the product, and everything else, the web app, the Figma plugin, the npm package, is distribution for that algorithm.

## Tints move toward white, shades scale toward black, channel by channel

The calculation method section is precise about what happens. The given hex color is first converted to RGB, and each RGB component is then calculated independently. For tints the new value is the current value plus the remaining distance to 255 times the tint factor, and for shades the new value is the current value multiplied by the shade factor. The tint factor is described as the percentage of the remaining distance each channel moves toward white, and the shade factor as the percentage of the original color retained as each channel is scaled toward black. The new value is rounded to the nearest whole number, with values ending in .5 rounding up, and then converted back to hex for display. That last clause is the detail that separate tools get wrong, because a naive implementation with a different rounding mode produces visibly different hex values for the same input, which is exactly the inconsistency the project set out to remove.

## A worked Rebecca Purple example shows the rounding in the open

The README walks through both directions with Rebecca Purple, `#663399`, which converts to RGB `(102, 51, 153)`. For a 10% tint, red is `102 + ((255 - 102) x 0.1) = 117.3` rounded to 117, green is `51 + ((255 - 51) x 0.1) = 71.4` rounded to 71, and blue is `153 + ((255 - 153) x 0.1) = 163.2` rounded to 163, and RGB `(117, 71, 163)` becomes `#7547a3`. For a 10% shade, red is `102 x 0.9 = 91.8` rounded to 92, green is `51 x 0.9 = 45.9` rounded to 46, and blue is `153 x 0.9 = 137.7` rounded to 138, and RGB `(92, 46, 138)` becomes `#5c2e8a`. Publishing the arithmetic is a deliberate act. A user can now check the numbers by hand, and a user who disagrees with the rounding rule can see exactly where the disagreement is, which is a better starting point than a colour picker that simply looks different from the one in DevTools.

## Four hue-rotation palettes sit on top of the same engine

Beyond tints and shades, the tool derives related palettes based on common color-wheel relationships, and these shift the hue while preserving the original saturation and lightness, which the README says works well for most color systems. The complementary scheme adds one palette using the hue directly opposite the base color at 180 degrees, producing the strongest contrast and clear visual separation. The split complementary adds two palettes at 30 degrees on either side of the complementary color, keeping contrast high but less extreme than a direct complementary pairing. The analogous scheme adds two palettes at 30 degrees on either side of the base color, giving low contrast, cohesive combinations suited to subtle variation. The triadic scheme adds two palettes evenly spaced at 120 degrees around the color wheel, creating medium-high contrast with energetic, distinct relationships. The key property is that saturation and lightness are untouched, so a complementary palette derived from a desaturated base stays desaturated, which is the behaviour you want when the base color is part of a brand system.

## The engine ships as an npm package alongside the web app

The README states that the Tint and Shade Generator exposes its color engine as a standalone API, that it generates tints and shades, derives related palettes, and performs common color normalization and conversion, and that it can also be used independently in your projects. The package is published as `@edelstone/tints-and-shades` on npm, with the source in `packages/tints-and-shades` in the repository. The web app itself is built with Astro, and the root `package.json` is a private workspace with `packages/*` as its members, so the app and the library are built from the same tree. The `build` script shows the order of operations, building the API package first, then the README, then the Astro site:

```json
  "scripts": {
    "watch:readme": "nodemon --watch src/shared/about.html --watch build-readme.js --exec \"npm run build:readme\"",
    "build:readme": "node build-readme.js",
    "dev": "npm run build:readme && astro dev",
    "build": "npm run build:api && npm run build:readme && astro build",
    "start": "concurrently \"npm run watch:readme\" \"npm run dev\"",
  },
```

That is a real monorepo layout with a library, a site, and a shared readme build, and it explains why the README and the npm package cannot drift apart in their claims about the calculation.

## Running it locally is four steps and one port

The local development section lists Node.js 22.12 or newer as the prerequisite, then four steps: clone the project, install dependencies with `npm install`, start the server with `npm start`, and open `localhost:4321`. The port is not a guess, since the root `package.json` sets the engines field to `node >=22.12.0` and the `start` script runs the readme watcher and the dev server together through `concurrently`, which is why the readme is rebuilt on change rather than written by hand. There is a `build-readme.js` script and a `src/shared/about.html` source, and the `turndown` dependency in devDependencies suggests the readme is generated by converting HTML into Markdown, which is how a single source of truth can be both a web page and the repository readme. Tests are split too, with `test:api` delegating to the workspace package and `test:app` running `node --test test/*.test.mjs`.

## A Figma plugin and a stated design system

There is a Figma plugin so you can generate the same tints and shades without leaving the canvas, and the README notes it can automatically create local color styles too. It is published through the Figma Community listing. The design of the site itself is specified in the readme as well, with Work Sans for typography by Wei Huang, Tabler Icons for the iconography, and a palette of named colors including `#000000`, `#1a1a1a`, `#e6e6e6`, `#ffffff`, and `#e96443`, each linked to the generator with its own color parameter in the URL. That last detail is worth noting for anyone who wants the tool to be shareable, because the state lives in the link. The site also offers sharing palettes via link or image, and exporting colors for design tokens, CSS, or JSON, which are the three destinations a developer actually needs. The credits name Michael Edelstone as the designer and organizer with major assistance from Nick Wing on the color calculations, and the project builds on Astro, the Color Names package, and Coloris.

## Conclusion

tints-and-shades fits a designer or developer who needs a tint and shade scale that agrees with the browser, whether they are generating UI states, building a palette, or exporting design tokens, and who would rather call a small package than reimplement the arithmetic. It is the wrong choice if you need perceptual uniformity, since the method is per-channel linear scaling in sRGB and will not produce the even lightness ramp of a CIELAB-based tool. Before you depend on the npm package, compare its output for one brand color against the Sass or Less tint function you already use, since the README claims a match with established methods rather than a formal specification, and note that the repository has no GitHub releases, so the version in the package manifest is what you track.

## FAQ

### How does the tints-and-shades generator calculate a tint?

The hex color is converted to RGB and each channel is computed independently as the current value plus the distance to 255 times the tint factor, for example `102 + ((255 - 102) x 0.1)`. The result is rounded to the nearest whole number with values ending in .5 rounding up, then converted back to hex.

### How does the tints-and-shades generator calculate a shade?

Each RGB channel is multiplied by the shade factor, so a 10% shade of Rebecca Purple gives `102 x 0.9 = 91.8` rounded to 92 for red. The rounded RGB triple is then converted back to its hex equivalent.

### Can I use the tints-and-shades color engine in my own project?

Yes. The README says the generator exposes its color engine as a standalone API that generates tints and shades, derives related palettes, and performs color normalization and conversion. The package is published on npm as `@edelstone/tints-and-shades`.

### What related palettes does the tints-and-shades tool generate?

Complementary at 180 degrees, split complementary at 30 degrees either side of the complementary, analogous at 30 degrees either side of the base color, and triadic at 120 degree intervals. These shift the hue while preserving the original saturation and lightness.

### How do I run the tints-and-shades app locally?

You need Node.js 22.12 or newer. Clone the project, run `npm install`, then `npm start`, and open `localhost:4321`. The start script runs the readme watcher and the Astro dev server together.

## Sources

- [edelstone/tints-and-shades on GitHub](https://github.com/edelstone/tints-and-shades)
- [Issues](https://github.com/edelstone/tints-and-shades/issues)
- [License: MIT](https://github.com/edelstone/tints-and-shades/blob/main/LICENSE)
- [Project website](https://maketintsandshades.com)
- [README](https://github.com/edelstone/tints-and-shades/blob/main/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/edelstone-tints-and-shades
