Pick any color in a design system and you rarely use just that color. You need it lighter for backgrounds, darker for hover states, lighter still for disabled elements, darker again for pressed states. The systematic way to produce those variants is through tints and shades, and understanding the difference between them — and how to generate them predictably — is one of those foundational skills that makes everything downstream easier.
The Tint & Shade Generator on DevToolShack produces a full range of tints and shades from any hex color you provide, with HEX, RGB, and HSL output for each step. Here is what you need to know to use them well.
Tints vs Shades: The Core Distinction
The terms are often used interchangeably in conversation, but they mean different things:
- A tint is produced by mixing a color with white. It raises the lightness while reducing saturation slightly. Tints are lighter than the base color.
- A shade is produced by mixing a color with black. It lowers the lightness and can also deepen the apparent saturation. Shades are darker than the base color.
- A tone (less commonly discussed in UI contexts) is produced by mixing a color with gray, reducing saturation without strongly changing lightness.
In CSS and most design tools, tints and shades are generated by adjusting the lightness value in HSL. Starting with a base color like hsl(220, 70%, 50%), a tint moves the lightness toward 100% and a shade moves it toward 0%.
Why You Need a Range, Not Just One Variant
A complete color scale gives you a set of consistently spaced values from very light to very dark, all sharing the same hue. Tailwind CSS made this approach mainstream with its numbered scales (100 through 900), and most modern design systems follow a similar pattern.
A 9-step scale from a single base color might look like this:
--blue-100: hsl(220, 70%, 95%); /* near-white background */
--blue-200: hsl(220, 70%, 87%); /* subtle background, borders */
--blue-300: hsl(220, 70%, 76%); /* disabled state */
--blue-400: hsl(220, 70%, 64%); /* lighter interactive */
--blue-500: hsl(220, 70%, 50%); /* base brand color */
--blue-600: hsl(220, 70%, 40%); /* hover state */
--blue-700: hsl(220, 70%, 32%); /* active/pressed state */
--blue-800: hsl(220, 70%, 22%); /* deep accent */
--blue-900: hsl(220, 70%, 13%); /* near-black text on light bg */
With this scale in place, every interactive state, background layer, and text color has a semantically named value to pull from. Button hover goes from 500 to 600. A subtle panel background uses 100. An active state drops to 700. The consistency this creates is difficult to achieve when picking colors by eye each time.
Generating Tints and Shades in CSS
If you're working with CSS custom properties, HSL makes tint and shade generation particularly clean because you can separate the hue from the lightness:
:root {
--hue: 220;
--sat: 70%;
--blue-100: hsl(var(--hue), var(--sat), 95%);
--blue-500: hsl(var(--hue), var(--sat), 50%);
--blue-900: hsl(var(--hue), var(--sat), 13%);
}
Changing --hue re-generates the entire scale in a different color. This is useful for theming or for white-labeling a UI where clients need different brand colors.
CSS also supports the newer color-mix() function, which lets you mix colors directly in CSS without pre-computing values:
/* A 20% tint: 80% base color, 20% white */
background: color-mix(in srgb, var(--blue-500) 80%, white);
/* A 30% shade: 70% base color, 30% black */
background: color-mix(in srgb, var(--blue-500) 70%, black);
Browser support for color-mix() is solid in modern browsers as of 2024, but check your support requirements before using it without a fallback.
The Perceptual Lightness Problem
One limitation of simple HSL-based tint and shade generation is that HSL lightness is not perceptually uniform. A yellow at 50% HSL lightness looks much lighter to the human eye than a blue or purple at 50% lightness. This means mechanically generated scales can feel inconsistent across different hues.
More sophisticated design tools use perceptually uniform color spaces like OKLCH or LCH for scale generation, where equal steps in lightness produce visually equal steps in perceived brightness. Tailwind CSS v3 moved to OKLCH for its color generation for exactly this reason.
For most developer-facing UI work, HSL-based scales are more than adequate. For design systems that need to feel polished across many different brand colors, OKLCH is worth exploring.
Tints and Shades for Specific UI Patterns
A few patterns where tints and shades do most of the work:
Button states. Base color for default, one shade darker for hover, two shades darker for active/pressed, two tints lighter with reduced opacity for disabled. This creates a consistent, predictable interaction model.
Layered backgrounds. Page background at the lightest tint (100), card backgrounds at the next step (50 or white), nested panels one step darker. The depth reads clearly without large color jumps.
Text on colored backgrounds. When placing text on a colored background, use shades (not tints) for the text. Shades read better because they have higher contrast against the mid-tone background than tints do. Always verify with a contrast checker before shipping.
Focus rings and borders. A border one shade darker than the background it sits on, with a focus ring two shades darker still, creates visible but unobtrusive affordances.
Using the Tint & Shade Generator
The Tint & Shade Generator on DevToolShack takes a hex color as input and returns a full set of tints and shades as HEX, RGB, and HSL values. You can copy individual values or the full set for use in your CSS variables, design tokens, or design tool color styles.
It works well paired with the Image Color Extractor and Color Palette Generator: extract a dominant color from an image, then run it through the tint and shade generator to build the full scale you need for a UI. The guide to generating palettes from images covers that workflow in detail. For converting the output between HEX, RGB, and HSL formats, the Color Picker and Converter handles all the translations.