CSS Shadows: Box Shadow and Text Shadow Made Simple

Shadows add depth and polish to a page. Learn box-shadow and text-shadow inside out — every value explained, inset shadows, layered shadows, glows, and the patterns behind real cards, buttons, and elevation.

Published August 12, 202611 min readBy ACY Partner Indonesia
CSS Shadows — box-shadow and text-shadow for depth
300 × 250Ad Space AvailablePlace your ad here

Look at almost any interface you admire — a card that seems to float just above the page, a button that lifts when you hover it, a modal that clearly sits on top of everything else. What gives those elements their sense of depth? Almost always, it’s a shadow. Shadows are the single most effective trick for making a flat screen feel layered, and CSS gives you full control over them with two properties.

Those two are box-shadow, which casts a shadow around any element’s box, and text-shadow, which does the same for text. They share the same basic idea — an offset, a blur, a color — so once one clicks, the other comes for free. By the end of this guide you’ll read any shadow value at a glance and write your own with confidence. Let’s get into it.

How box-shadow works

box-shadow paints a shadow around the rectangular box of an element — following its border-radius, so rounded corners get rounded shadows. The full syntax takes a handful of values in a specific order:

.card {
  box-shadow: 0 4px 12px 0 rgba(0, 0, 0, 0.15);
}

That one line covers the most common case: a soft shadow sitting just below a card. But to bend shadows to your will, you need to know what each of those numbers is doing. The order is horizontal offset, vertical offset, blur radius, spread radius, color:

Value What it controls
0 Horizontal offset — pushes the shadow left (negative) or right (positive)
4px Vertical offset — pushes it up (negative) or down (positive)
12px Blur radius — how soft and spread out the edge is (bigger = softer)
0 Spread radius — grows (positive) or shrinks (negative) the whole shadow
rgba(0,0,0,0.15) Color — usually a semi-transparent black

Let’s walk through each one, because understanding them individually is what lets you design a shadow instead of copying one off the internet and hoping.

The offsets: where the shadow falls

The first two numbers decide which direction the shadow is cast. Think of them as the position of a light source. A positive vertical offset pushes the shadow downward, which reads as light coming from above — exactly how light usually behaves in the real world, and why down-shadows look natural:

/* shadow directly below — light from straight above */
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.2);

/* shadow down and to the right — light from top-left */
box-shadow: 4px 4px 12px rgba(0, 0, 0, 0.2);

Most UI shadows use a horizontal offset of 0 and a positive vertical offset, giving that clean “lit from above” look. Offsetting horizontally too is fine for a more dramatic, angled light — just keep it consistent across your design so everything looks lit by the same sun.

Blur radius: soft or sharp

The third number is the blur. At 0, the shadow has a hard, crisp edge like a solid shape. The larger it gets, the softer and more diffuse the edge becomes:

box-shadow: 0 4px 0 rgba(0, 0, 0, 0.3);    /* hard edge, no blur */
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); /* soft, gentle glow */

Blur is the value that most controls how realistic a shadow feels. Real shadows in the world are almost never razor-sharp; they fade out at the edges. So for natural-looking depth, you’ll usually want a generous blur — often larger than the offset itself. A tiny offset with a big blur produces that soft, premium “floating” look you see on well-designed cards.

Spread radius: bigger or smaller

The fourth number, the spread, grows or shrinks the entire shadow before the blur is applied. Positive values make it larger than the element; negative values pull it in tighter:

/* shadow slightly larger than the box */
box-shadow: 0 4px 12px 4px rgba(0, 0, 0, 0.15);

/* shadow pulled in, so it peeks out only at the bottom */
box-shadow: 0 8px 12px -6px rgba(0, 0, 0, 0.3);

Spread is the value people forget exists, but it’s quietly useful. A negative spread is a favourite trick: it shrinks the shadow so it only shows where the offset pushes it out, giving a tight, contained shadow that hugs the element instead of bleeding out on all sides.

Spread is optional — and so is half of this

You only need to supply the values you care about. box-shadow: 0 4px 12px black (offsets, blur, color) is perfectly valid — the spread defaults to 0. The absolute minimum is two offsets and a color: box-shadow: 2px 2px black. Everything beyond that is fine-tuning. So don’t feel you must memorize a four-number incantation; start with offset and blur, and add the rest only when a design needs it.

Color: keep it subtle

The last value is the shadow’s color. The single most common mistake beginners make is using solid black (#000), which looks heavy and fake. Real shadows are transparent — they darken what’s behind them rather than painting a solid grey blob. So almost always reach for rgba() with a low alpha:

box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);  /* tasteful */
box-shadow: 0 4px 12px #000000;              /* heavy, avoid */

An alpha somewhere between 0.1 and 0.25 covers most cases. On dark backgrounds you can go a little stronger; on light backgrounds, a little softer. If you’re shaky on rgba() and color formats, our guide to colors and backgrounds walks through them.

Inset shadows: shadows on the inside

Everything so far casts the shadow outside the element. Add the keyword inset and the shadow flips to the inside, making the element look pressed in or carved out instead of raised up:

.input {
  box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1);
}

This is the secret behind that subtle “sunken” look on form inputs, pressed buttons, and toggle switches. The inner shadow makes the surface feel recessed, like a real physical slot. It’s the same property and the same values — just inset flips which side the shadow lands on.

/* a button that looks pressed down while clicked */
.button:active {
  box-shadow: inset 0 3px 6px rgba(0, 0, 0, 0.2);
}

Combine an outer shadow on the resting state with an inset shadow on :active and you get a button that genuinely feels like it depresses when clicked. Pair that with a quick transition and the effect is wonderfully tactile.

Layering multiple shadows

Here’s where shadows get powerful: you can apply several at once, separated by commas, and they stack from front to back. This is the single biggest upgrade you can make to your shadows, because real-world depth is never one flat shadow — it’s several soft ones layered together:

.card {
  box-shadow:
    0 1px 2px rgba(0, 0, 0, 0.08),
    0 4px 8px rgba(0, 0, 0, 0.08),
    0 12px 24px rgba(0, 0, 0, 0.08);
}

That card has three shadows: a tight close one, a medium one, and a wide diffuse one. Together they mimic how light actually scatters, producing a far richer, more believable sense of elevation than any single shadow could. This “layered shadow” technique is the difference between a shadow that looks designed and one that looks bolted on.

A simple elevation system

Designers think of shadows as elevation — how high an element floats above the page. A clean trick is to define a few levels and reuse them. A resting card might sit at a low elevation, and lift to a higher one on hover:

.card {
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  transition: box-shadow 0.2s ease;
}
.card:hover {
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
}

The card visibly “rises” toward you on hover. Define three or four such levels once and your whole interface gains a consistent, professional sense of depth.

Glows and colored shadows

Nothing says a shadow has to be grey. Use a colored, transparent value and the shadow becomes a glow — perfect for focus rings, neon effects, and drawing attention to an active element:

/* a soft cyan glow around a focused input */
.input:focus {
  box-shadow: 0 0 0 4px rgba(0, 184, 230, 0.4);
  outline: none;
}

Notice both offsets are 0 and the blur is 0 here — with only a spread, the shadow becomes an even ring all the way around the element, which is exactly how you build a custom focus highlight. Bump the blur up and that ring softens into a real glow:

.button {
  box-shadow: 0 0 20px rgba(0, 184, 230, 0.6);
}

Don't remove focus outlines without replacing them

It’s tempting to write outline: none to kill the default focus ring browsers draw. But that ring is what lets keyboard users see where they are on the page — removing it and leaving nothing breaks accessibility badly. If you set outline: none, you must provide your own visible focus indicator, and a box-shadow ring like the one above is a great way to do it. Never strip the focus style and leave the element with no visible focus at all.

text-shadow: the same idea, for text

Once box-shadow makes sense, text-shadow is almost no extra learning. It casts a shadow behind the text itself, and its syntax is the simpler cousin — horizontal offset, vertical offset, blur, color:

.heading {
  text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.4);
}

There’s no spread and no inset — just the two offsets, a blur, and a color. That single shadow lifts the text slightly off the background, which can help readability when text sits over a busy image. Used with restraint, it adds a touch of polish:

/* subtle lift for a hero heading over a photo */
.hero-title {
  color: #ffffff;
  text-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
}

And just like box-shadow, you can layer multiple text shadows with commas to build effects — an outline by stacking shadows in every direction, or a glowing neon look:

/* a glowing neon-style heading */
.neon {
  color: #ffffff;
  text-shadow:
    0 0 8px rgba(0, 184, 230, 0.9),
    0 0 20px rgba(0, 184, 230, 0.6);
}

Keep text shadows light

Text shadows are easy to overdo. A heavy, dark shadow behind body text makes it harder to read, not easier — the blur smears the letterforms. Reserve text-shadow for headings, hero text over images, or deliberate decorative effects, and keep the blur and opacity gentle. For plain paragraphs on a solid background, no text shadow at all is almost always the right call.

A real example: a polished card

Let’s pull box-shadow, layering, and a hover transition together into a card you’d genuinely ship:

.card {
  background: #ffffff;
  border-radius: 12px;
  padding: 24px;
  box-shadow:
    0 1px 3px rgba(0, 0, 0, 0.06),
    0 6px 16px rgba(0, 0, 0, 0.08);
  transition: box-shadow 0.25s ease, transform 0.25s ease;
}

.card:hover {
  transform: translateY(-4px);
  box-shadow:
    0 2px 6px rgba(0, 0, 0, 0.08),
    0 16px 32px rgba(0, 0, 0, 0.12);
}
<div class="card">
  <h3>ACY Partner Indonesia</h3>
  <p>A floating card with layered shadows that lifts on hover.</p>
</div>

At rest, the card has a gentle two-layer shadow that makes it float just above the page. On hover, it nudges up four pixels and its shadow grows deeper and wider — the classic “lifting toward you” effect. The transition smooths the whole thing out. If you want to dig into that smooth motion, our guide to CSS transitions covers it in full.

Shadows can cost performance if you animate them carelessly

Animating box-shadow directly on every frame can be expensive, because the browser has to re-render the blur each time. For hover lifts, animating transform (as above) is cheap and smooth; the shadow change is small and infrequent enough that it’s usually fine. But if you build something that animates a heavy blurred shadow continuously, watch your frame rate — a common pro trick is to fade a separate pre-rendered shadow layer with opacity instead.

Wrapping up

Shadows are the fastest way to add depth and polish to an interface, and you’ve now got both shadow properties down cold:

  • box-shadow casts a shadow around an element: horizontal offset, vertical offset, blur, spread, color.
  • Most UI shadows use a 0 horizontal offset, a positive vertical offset, a generous blur, and a transparent rgba() color — never solid black.
  • Spread grows or shrinks the shadow; a negative spread keeps it tight and contained.
  • inset flips the shadow inside the element for a pressed or sunken look.
  • Layer multiple shadows with commas to mimic real, scattered light and build an elevation system.
  • A colored, transparent shadow becomes a glow — great for focus rings, but never remove a focus outline without replacing it.
  • text-shadow is the same idea for text — offset, blur, color, no spread — and works best kept subtle.

With shadows you can make any element float, sink, or glow. Next up is a property that paints with color in a whole different way — blending one color smoothly into another across a surface: CSS gradients, which take your backgrounds from flat to vivid.

Tags:cssfrontendbox-shadowtext-shadowintermediate
728 × 90Ad Space AvailablePlace your ad here

Related Articles

See All Articles

You Might Also Like

Frontend best practices overview cover
Frontend / Fundamentals

Frontend Best Practices: An Overview

A friendly map of what good frontend really means — semantic HTML, clean CSS, unobtrusive JavaScript, responsive layouts, performance, and progressive enhancement, with pointers to go deeper.

Sep 20, 20268 min read
A Frontend Developer Roadmap cover with the path HTML to CSS to JavaScript
Frontend / Fundamentals

A Frontend Developer Roadmap: What to Learn, and in What Order

A calm, step-by-step learning path for beginners: HTML, CSS, JavaScript, developer tools, responsive and accessible design, a framework, TypeScript, and deployment, with the reasoning behind each step.

Sep 20, 202611 min read
Title card reading Styling and Interactivity over a dark blue ACY Partner background
Frontend / Fundamentals

How Styling and Interactivity Work

A beginner-friendly look at how CSS turns plain HTML into a designed layout, and how JavaScript adds behavior — the structure, style, behavior mental model for building web pages.

Sep 20, 20269 min read
Three front-end layers combining on one web page
Frontend / Fundamentals

How HTML, CSS, and JavaScript Work Together

A hands-on walkthrough for beginners: build one small button, give it structure with HTML, looks with CSS, and behavior with JavaScript, and watch the three layers cooperate on a real page.

Sep 20, 20268 min read
The Frontend Developer Toolkit cover with editor, browser, and CLI motifs
Frontend / Fundamentals

The Frontend Developer Toolkit

A friendly tour of the everyday tools a frontend developer relies on — code editor, browser and DevTools, terminal, version control, package managers, and a dev server — and what each one is actually for.

Sep 20, 202610 min read
Cover illustration for What Frontend Developers Do
Frontend / Fundamentals

What Frontend Developers Do: A Beginner's Guide to the Role

A clear, beginner-friendly look at what frontend developers actually do every day: turning designs into working interfaces, building reusable UI, and caring about responsiveness, accessibility, and speed.

Sep 20, 202610 min read