Bhinish Dhiman
How to make CSS shadows and glass that stay visible and readable
Build shadows as layers on one light angle, then measure the edge: a soft shadow on a white page gives about 1.1 to 1.6:1, so a control needs a 1px ring to reach 3:1. In dark mode, lift the surface and ring it instead of raising the shadow's opacity, which measures 1.00:1. For glass, measure text at its worst pixel through the blur, and give browsers without backdrop-filter a stronger tint.
A shadow is judged on a swatch, in light mode, on a calm background. The page it ships on has none of those. A card’s edge can be invisible to someone with low vision, the same shadow does nothing on a dark page, and the text on a pane of glass can sit over the one bright patch of the image behind it. Each of those has a number you can check before the CSS leaves your editor.
Make one with the tool
- Open the box shadow generator. It starts on the Soft card preset, measured, with its dark twin beside it, so there is a working surface on screen straight away.
- Choose Shadow, Glass or Soft UI at the top of the stage and press space, or Generate, for a new surface. Lock Colours, Light or Elevation on the stage (Backdrop, Tint or Blur for glass) to keep them. What space makes passes the text check, and a control’s 3:1 edge, unless a lock rules that out.
- For a shadow, open Settings and set Level, Light angle, First offset, Softness and Layer opacity. Edit the layers by hand turns the scale into numbers you can type, and the presets are under Presets, every one measured.
- Open the Edge chip under the stage and, under This surface is, choose Content for a card or A control for an input or a toggle. The edge verdict follows the choice.
- Read the light surface’s edge beside the dark twin’s, which sits next to the figure for the same shadow with its opacity tripled. If a side falls short, press Set the ring to …%, which gives every side 3:1 with the lightest ring that does it. On a phone the stage shows one surface at a time: switch it from Light to Dark twin.
- For glass, choose a Backdrop or Use your own image in Settings, then set Blur, Tint opacity and Rim. If a line fails, open the Tint needs chip and press Raise the tint to …%.
- Press Copy CSS in the bar, or use Export for CSS, Custom properties, Tailwind v4 or Inline style, the Full rule with its dark and forced-colours versions, or all five levels as Elevation tokens.
A shadow rarely makes an edge on its own
Non-text contrast is WCAG 2.2’s requirement, in success criterion 1.4.11, that the parts of an interface you need to see to use it differ from the colours beside them by at least 3:1. It applies where the boundary is how you find the control: a text field with no label inside it, a checkbox, a toggle. A content card is not held to it, and a button with readable text does not need an outline.
A shadow changes the colour beside the edge, but only on the side it falls. Measured on a 352-pixel card, as the lowest and highest ratio between the card’s last pixel and the page beside it:
| Shadow | White card on white | Slate-800 card on slate-900 |
|---|---|---|
Tailwind shadow-sm |
1.03 – 1.23 | 1.22 – 1.24 |
Tailwind shadow-lg |
1.01 – 1.32 | 1.22 – 1.25 |
Tailwind shadow-2xl |
1.02 – 1.50 | 1.22 – 1.27 |
neumorphism.io default on #e0e0e0 |
1.07 – 1.25 | — |
None of these comes near 3:1. That is fine for a card and a failure for an input field. The tool’s default soft card, as the preview shows it, measures 1.13:1 at the top and 1.59:1 at the bottom; a 1px ring in the shadow colour at 47% opacity is the lightest that brings all four sides to 3:1. The ring goes first in the box-shadow list, so it paints on top of the softer layers.
Comparing the card’s fill with the page colour, as most checkers would, misses all of this. White on white is 1:1 with or without a ring, and a dark shadow under the bottom edge looks the same as one on every side.
Dark mode needs a different mechanism
A shadow works by darkening what is under it. On a dark page there is almost nothing left to darken, which is why the slate column in the table barely moves from the 1.22:1 the two fills already give. Raising the opacity only produces a dark smear: the default card’s shadow at triple opacity, on a surface the colour of the page, measures 1.00:1.
The dark twin swaps the mechanism. Tonal elevation is the practice of making a surface lighter the higher it sits, so height reads as light rather than shadow. The twin lifts each level by white at Material’s overlay percentage, 4.5 × ln(dp + 1) + 2, rings it with white at 30% and adds a lit top rim, keeping the shadow only as a faint residual. On the default card its weakest side measures 1.67:1. A ring has to differ from the lift: white at the same percentage as the surface is simply more surface.
Glass text has many backgrounds
Text on glass is drawn over a blurred, tinted copy of whatever is behind the pane, so it has a different background at every point. A glass panel with a 15% white tint and a 20-pixel blur leaves white text at 3.69:1 at its worst pixel over a soft mesh gradient and 1.75:1 over a page of hard light and dark bands. A snippet with a 20% white tint and a 5-pixel blur leaves white text at about 1:1 over a light page of text.
The tool reports the lowest ratio under each line, the tint opacity at which every line passes through the blur, and the higher one needed with no blur at all. That second figure matters: backdrop-filter is missing in older browsers and drops out when a reader turns on Reduce transparency, which Chrome 118 and later report to CSS. The export writes both backdrop-filter and -webkit-backdrop-filter, since Safari only dropped the prefix in version 18, and puts the lighter tint behind an @supports test.
Other ways to do it
There is no built-in operating-system method for this. You can write box-shadow by hand in any browser, and Tailwind v4 ships the six shadow sizes above as shadow-xs to shadow-2xl, plus ring utilities for the 1px outline; its dark variant can switch between them, but its default shadows are black at fixed opacities.
Check that nothing left your device
The tool runs in the page. Open your browser’s developer tools, choose the Network tab and work for a minute, including uploading a backdrop: no request carries your colours or your image, and the state lives in the address bar’s fragment, which browsers never send.
Where to go next
Open the box shadow generator and press space, or start from a preset. To choose the colours themselves, the contrast checker finds the nearest passing text colour, and dark mode contrast explains why ratios behave differently near black.