All tools run in your browser — your files never leave your device.
All tools154

Design & SEO

Gradients, shadows and palettes that do not look generated

The defaults for all three of these produce something recognisably machine-made. The fixes are small and each has a physical reason behind it.

The short answer. A two-stop gradient bands because interpolation through sRGB passes through desaturated middle tones — add a midpoint stop or interpolate in OKLCH. A single box-shadow looks flat because real shadows have a tight contact shadow and a wide soft one, so use at least two layers.

Why two-stop gradients look muddy

A gradient from blue to yellow passes through grey, not green. That is not a bug in the browser — it is what interpolating between two RGB values does.

Blue is roughly (0,0,255) and yellow (255,255,0). The midpoint is (128,128,128), which is grey. Any two colours on opposite sides of the wheel produce a desaturated band in the middle.

  • Add a midpoint stop in the hue you want to pass through. Three stops fixes most of it.
  • Or interpolate in a better space: linear-gradient(in oklch, blue, yellow) keeps saturation through the transition. Browser support is now broad.
  • Keep hues close. The most convincing gradients move 30–60 degrees of hue, not 180.
  • Vary lightness as well as hue. A gradient that only changes hue at constant lightness reads as flat.

The other giveaway is banding on large areas — visible steps in a smooth gradient, worst on dark colours across a wide element. Adding a very subtle noise overlay hides it, which is what most design tools do behind the scenes.

Shadows have two parts

A single box-shadow with a large blur is the default and looks like a sticker with a grey halo. Real shadows are two things at once.

There is a contact shadow — tight, dark, close to the object, from light blocked immediately at the join. And an ambient shadow — wide, very soft, much lighter, from diffuse light in the room.

Layering both is the whole trick: box-shadow: 0 1px 2px rgba(0,0,0,.10), 0 8px 24px -8px rgba(0,0,0,.18). The first keeps the element attached to the surface; the second gives it height. A single shadow can do one or the other and never both.

Shadow rules that hold

Shadow rules that hold
RuleWhy
Blur roughly twice the y-offsetMatches how light actually falls off
Negative spread on the wide layerStops it pooling around the edges
Never pure blackReal shadows carry the surface hue
One light source per pageMixed directions read as a mistake
Bigger elevation, softer and largerA modal casts more than a button
Reduce opacity in dark modeA black shadow on dark is invisible

That last row catches people converting a light design. Shadows do almost no work on a dark background — depth there comes from a lighter surface rather than a darker shadow, which is a different mental model rather than an inverted one.

Building a palette from one colour

The common approach — take a hue and rotate it 30 degrees for each new colour — produces a set that looks related and unbalanced, because equal hue steps are not equal perceptual steps.

It is also the reason a palette built by holding HSL lightness constant does not look evenly weighted: yellow at 50% lightness is dramatically brighter than blue at 50%. HSL lightness is mathematical, not perceptual.

  1. Start with one brand hue and build a lightness ramp from it — five to nine steps from very light to very dark. That single ramp does most of the work in any interface.
  2. Add a neutral ramp tinted very slightly toward the brand hue. Pure grey next to a warm brand colour looks dead.
  3. Add semantic colours for success, warning and error, adjusted to sit at similar perceived lightness to each other.
  4. Use OKLCH if you can. Equal lightness values genuinely look equally light, which is the thing HSL cannot give you.
  5. Check every text pair for contrast as you go, not at the end.

That last step is not optional and it is where palettes fail late. A ramp that looks balanced can put body text at 3.8:1, and you will not see it by eye — the reasoning is in WCAG contrast requirements.

Glass effects, cheaply

Frosted glass is backdrop-filter: blur() plus a translucent background, and it is expensive — the browser composites and blurs everything behind the element on every frame.

On a large area, or on several elements at once, it is one of the most reliable ways to make a page feel slow on a mid-range phone.

  • Keep the blurred area small. A header bar or a card, not a full-page overlay.
  • Blur radius 8–12px is plenty. Larger costs more and looks no better.
  • Always set a background colour too. backdrop-filter is unsupported in some contexts and the element then has no ground at all.
  • Add a hairline border at low opacity. Real glass has an edge, and without it the panel looks like a smudge.
  • Test the text contrast against the busiest thing that can scroll under it, not against the calm part of the page.

That final point is the accessibility trap in the whole effect: contrast is measured against a background that moves. A design checked against one screenshot can fail badly two scroll positions later.

Frequently asked questions

Why does my gradient look grey in the middle?

Because interpolating between two RGB values passes through their arithmetic midpoint, and for colours on opposite sides of the wheel that is grey. Add a midpoint stop in the hue you want, or use linear-gradient(in oklch, …) which keeps saturation through the transition.

Why does my box-shadow look fake?

It is one layer. Real shadows have a tight dark contact shadow plus a wide soft ambient one. Layer both — a small tight shadow keeps the element attached to the surface, a larger soft one gives it height. One shadow can do either but not both.

Should shadows be pure black?

No. Real shadows carry the hue of the surface they fall on, so a slightly tinted dark colour at low opacity looks right where pure black looks like a hole. In dark mode, reduce opacity heavily — shadows do almost no work there.

How do I build a colour palette from one brand colour?

Build a lightness ramp of five to nine steps from that hue, add a neutral ramp tinted slightly toward it, then semantic colours at similar perceived lightness. Rotating hue in equal steps produces something that looks related and unbalanced.

Why do my colours look uneven at the same lightness?

HSL lightness is mathematical rather than perceptual — yellow at 50% is far brighter than blue at 50%. OKLCH fixes this: equal lightness values genuinely look equally light, which is what makes cross-hue palettes work.

Is backdrop-filter bad for performance?

It is expensive — the browser composites and blurs everything behind the element every frame. Keep the area small, use a blur radius of 8-12px, and always set a fallback background colour, since the property is unsupported in some contexts.

Stop reading, start doing

Every tool in this guide is free.

154 browser-based utilities. No account, no upload, and no file size limit — your files are processed on your own device and never sent anywhere.

Browse all 154 tools