🧰 ToolPicoAll Tools →
HomeBlog › Design-to-code handoff

Design-to-Code Handoff: Building a Shade Scale and Gradient Without Guesswork

You've been handed a single brand HEX value and asked to build out an entire component library around it — hover states, disabled states, a hero gradient, and a UI that still works for colorblind users. Here's a practical path through that, without hand-picking ten shades by eye.

In this guide

Turning one color into a full shade scale

Quick answerKeep the hue and saturation of your base color fixed and step only the lightness, from a very light value (numbered 50) down to a very dark one (numbered 950) — the same convention Tailwind CSS uses. Your original color slots into the closest matching step, and the rest of the scale gives you built-in hover, active, border, and disabled tones.

A common handoff problem: a designer gives you exactly one purple (say #7C3AED) for "the primary color," and you're expected to produce a hover state, a pressed state, a disabled state, a subtle background tint, and a border color that all still read as "the same purple family." Picking each of those by eye in a color picker tends to drift — the hover state ends up a slightly different hue than the button, and nobody notices until a designer flags it in review.

Why lightness, not hue: Changing only the L value in HSL keeps the color feeling like "the same purple, lighter or darker," which is why numbered scales (50–950) step lightness while holding hue and saturation constant. Changing hue instead tends to produce a color that reads as a different accent entirely.

Once you have the full scale, two export shapes cover most frontend workflows: a block of CSS custom properties (--color-500: #...;) you can drop into a stylesheet, or a ready-to-paste Tailwind config colors object if the project already uses Tailwind's theme system. Either way, the goal is the same — every shade in your UI traces back to one source value instead of being redefined ad hoc in each component file.

Building a gradient that doesn't go muddy

Quick answerA flat linear-gradient() between two far-apart hues (for example a saturated blue and a saturated orange) often passes through a dull, grayish-brown midpoint, because CSS interpolates each RGB channel independently in a straight line. Choosing stops closer in hue, or adding a middle stop, usually fixes it.

Suppose you're asked to build a hero gradient from the brand's primary purple to a complementary teal for a marketing page. The two colors look great side by side as flat swatches, but the gradient between them can dip through a flat, brownish gray around the 50% mark — a well-known side effect of interpolating RGB channels linearly instead of interpolating through a perceptual color space. It's not a bug in your CSS; it's just how linear-gradient() blends by default.

As an illustrative example only: a gradient from a deep blue to a warm orange can pass through a flat gray-brown band around its midpoint, while a gradient from that same blue to a nearby violet stays vivid the whole way through — because the hues are closer together on the wheel. Your actual result depends on the specific two colors you pick.

Two practical fixes: pick a second stop that's within roughly 90–120° of hue from the first (an analog or near-triadic relationship rather than a near-complementary one), or add a third stop partway through using a color that bridges the two hues. Adjusting the angle changes the direction of the gradient but won't fix a muddy midpoint — that's a color-choice problem, not a geometry one.

Stress-testing a palette for color blindness

Quick answerRather than testing every screen individually, run your handful of core UI colors (primary, success, warning, error) through protanopia, deuteranopia, tritanopia, and achromatopsia simulations once, side by side. If two colors that must stay distinguishable start to look alike under any simulation, that's your signal to add a shape or label, not just rely on hue.

This matters most for status colors. A green "success" badge and a red "error" badge are easy to tell apart for most users, but under a red-green color blindness simulation (protanopia or deuteranopia — together affecting a meaningful share of men, per general color-vision research) they can become much closer in appearance. The fix generally isn't to keep hunting for a "safer" green and red; it's to stop relying on color alone. Add a checkmark icon to success states and an exclamation icon to error states, and the distinction survives even if hue doesn't.

A fast pre-launch check: pick each core brand and status color one at a time, view its simulated appearance across all four color-blindness types, and specifically compare pairs of colors that appear together in the UI (like adjacent chart bars, or a badge against its background). This takes a few minutes and catches issues that are otherwise easy to miss until a user reports them.

Reverse-engineering a palette from a screenshot

Quick answerAn image-based palette extractor reads an uploaded image's pixels locally (typically via the browser's Canvas API) and clusters the most frequent colors into a short list of representative HEX values — useful when you have a screenshot or photo but no source design file.

A common scenario: someone shares a screenshot of a competitor's landing page, or a photo of a printed brochure, and asks "can we get something in these colors?" Instead of eyeballing pixels with a system color picker one at a time, uploading the image to an extractor gives you a working starting palette in one step — which you can then run back through a shade-scale generator to build out a full set the same way you would from a single brand HEX value.

Keep the caveats in mind: a photo's colors are affected by lighting and camera white balance, and a screenshot may include JPEG compression artifacts that shift pixel values slightly. Treat an extracted palette as a starting point to refine, not a final brand-accurate source.

Generate a full shade scale, a gradient, and a colorblind simulation from one HEX value — instantly, entirely in your browser.

Try the free Color Converter →

Frequently asked questions

How do I turn one brand color into a full 50–950 shade scale?
Keep the hue and saturation of your base color fixed and step the lightness from very light (numbered 50) down to very dark (numbered 950), the same numbering convention used by Tailwind CSS. Your original color is mapped to whichever step is closest to it, so the rest of the UI (hover states, disabled states, borders) can reference the scale instead of hand-picked one-off colors.
Why does my gradient look muddy in the middle instead of smooth?
This usually happens when the two gradient stops are far apart in hue (for example a saturated blue to a saturated orange) with nothing in between — CSS interpolates each RGB channel in a straight line, which can pass through a dull, grayish-brown midpoint. Picking stops closer in hue, or adding a third middle stop that bridges the two, usually fixes it.
How do I extract a color palette from a screenshot or exported image?
An image-based palette tool reads the image's pixels locally (typically via the Canvas API, entirely in your browser) and clusters the most common colors, returning a short list of representative HEX values. This is useful when you've received a screenshot, a competitor's page, or a photo and need to reverse-engineer an approximate color scheme without opening a design app.
What's an efficient way to check a UI for color blindness issues?
Rather than testing every screen, check your core palette once: run each key color (primary, success, warning, error) through a protanopia/deuteranopia/tritanopia simulation and compare them side by side. If two status colors that must be told apart (like a green "success" badge and a red "error" badge) start to look similar under any simulation, that's a signal to add a shape, icon, or label rather than relying on hue alone.
Should I hand-write Swift, Android, or Flutter color code from a HEX value?
It's easy to get wrong by hand because each platform expects a different shape: Swift's UIColor wants three floats (0–1) plus alpha, Android/Kotlin wants a single 0xAARRGGBB integer, and Flutter wraps that same integer in a Color() constructor. Generating all three from the same source HEX value avoids channel-order or normalization mistakes that only show up visually after a build.

Related guides

A note on this guide: The shade-scale and gradient behavior described here follows standard HSL math and CSS's default linear-gradient interpolation; any specific numeric or visual example is illustrative for that color pair, not a universal rule. Color-blindness prevalence figures are described in general terms only, not as a precise cited statistic. This content is informational, not a substitute for a full accessibility or brand-design review.