Editor’s note: This article was last updated by Rahul Chhodde on 4 November 2024 to add recent CSS developments that impact the use of the box-shadow
property, including the View Transitions API, native CSS nesting, and the CSS @layer
rule.
The box-shadow
CSS property allows you to add a shadow around an element on a webpage. Shadows give us an idea of an object’s size and depth, and box-shadow
brings this realism into our online experience. The property can tell us if an element like a button, navigation item, or text card is interactive. When styled properly, box-shadow
s can also improve the aesthetics of the webpage.
In this article, we’ll look at the box-shadow
CSS property and how you can style it in three different ways: layered shadows, neon shadows, and neumorphic shadows.
Let’s look at how a typical box-shadow
in declared in CSS:
.selector { box-shadow: <x-offset> <y-offset> <blur-radius> <spread-radius> <shadow-color>; }
The first five values are:
x-offset
, which represents the horizontal shadow positiony-offset
, which represents the shadow’s position verticallyAll of these, except the blur radius, can be negative values. For instance, the positive values will place box-shadow
on the bottom of the element but if you add negative values like the code below, the shadow will be on top:
.card-1 { box-shadow: 0px 5px 10px 0px rgba(0, 0, 0, 0.5); } .card-2 { box-shadow: 0px -5px 10px 0px rgba(0, 0, 0, 0.5); }
The spread value set at 0px
will make the shadow the same size as the box; a positive value will increase its size and a negative value will shrink it.
The next value is color. We’ll be using rgba()
colors because of their alpha value. With this, we can specify an opacity, which is an important aspect to consider when styling realistic shadows. Shadows in well-lit spaces aren’t black or completely opaque — you can see the color of the area the shadow is cast on.
When styling the box-shadow
property, remember that transparent shadows are the best because they look good on multicolored backgrounds. Look around and observe how shadows behave in relation to their light sources — you’ll want to keep this in mind when styling with CSS:
The area closest to the object has the darkest shadows, then it spreads and blurs outwards gradually. Opaque or completely black shadows would be distracting, ugly, and imply a complete blockage of light, which isn’t what we’re after.
drop-shadow()
functionThe drop-shadow()
function adds a drop shadow around an image, following the shape of the entire element’s content. This means it can include any transparent areas. It differs from the box-shadow
property, which applies shadows to the rectangle of the image’s box:
The code snippet below shows the distinction between these filters:
.box-shadow { box-shadow: 5px 5px 5px 0px rgba(0,0,0,0.3); } .shadow-filter { filter: drop-shadow(5px 5px 5px rgba(0,0,0,0.3)); }
Using the drop-shadow
filter can negatively impact the performance and lacks features like spread, inset, or stacked shadows. If your goal is to add shadows to rectangular elements, I recommend you stick to using box-shadow
.
box-shadow
To get started with box-shadow
, first create a simple box container with HTML:
<div class="box"> ... </div>
Next, add the CSS:
.box { height: 150px; width: 150px; background: #fff; border-radius: 20px; box-shadow: 0px 5px 10px 0px rgba(0, 0, 0, 0.5); }
This will be the output:
box-shadow
with the :hover
pseudo-class and transform
propertybox-shadow
can also be affected by the :hover
pseudo-class. You could add a shadow to a component that didn’t previously have one, or make changes to an existing shadow. In this example, the transform
property modifies our shadow:
.box:hover { box-shadow: 0px 10px 20px 5px rgba(0, 0, 0, 0.5); transform: translateY(-5px); }
The transform
property helps the illusion that the box is being lifted further off the page. In this example, the translate()
function is used to resize the box.
The inset
keyword can be used to put the shadow inside the frame of the box or element. The box will appear to have sunk into the page:
.box2 { box-shadow: inset 0px 5px 10px 0px rgba(0, 0, 0, 0.5); } .box2:hover { transform: translateY(5px); box-shadow: inset 0px 10px 20px 2px rgba(0, 0, 0, 0.25); }
You can play with the values until you get what you like. This is what the final shadows will look like using these examples:
See the Pen
Simple Box-shadow by Oscar-Jite (@oscar-jite)
on CodePen.
An alternative to the translate()
function is scale()
. While translate()
changes the shadow position along the x- and y-axes, scale()
increases the size of the box along the x- and y-axes.
Let’s demonstrate this. On the second box, add the scale()
value on :hover
:
.box2:hover { transform: scale(1.1); box-shadow: 0px 10px 20px 2px rgba(0, 0, 0, 0.25); }
This will increase the size of the box by 1.1x the original size:
See the Pen
Translate and scale by Oscar-Jite (@oscar-jite)
on CodePen.
You don’t want ugly, botched, or boring shadows that look amateurish on a webpage. There are many ways to improve the look of box-shadow
and use it nicely and effectively — all of which I’ll explain in the following sections.
box-shadow
property with text-shadow
Similar to box-shadow
, by specifying the shadow’s blur radius, color, and offset in both the horizontal and vertical planes, you can add shadows to text using the text-shadow
CSS property.
The text-shadow
property gives you precise control over the shadow of the text, so you can make different kinds of visual effects. Here’s a basic syntax:
.selector { text-shadow: <horizontal-offset> <vertical-offset> <blur-radius> <color>; }
While text-shadow
is only applicable to text elements, it can be combined with the box-shadow
property to create complex designs that not only make your text stand out but also add depth and dimension to the surrounding elements on your webpage.
You can apply box and text shadows to an element simultaneously to increase the element’s visual impact. Here’s an example of how you can combine these two CSS properties:
<div class="site-container"> <div class="card">...</p> </div>
We added the box-shadow
and text-shadow
properties to the .card
class. The box-shadow
applies a shadow effect to the card. The two shadow layers with different colors and offsets create a sense of depth and a visually pleasing neumorphic effect.
Then, text-shadow
creates a text shadow effect by applying two shadows with different offsets and opacity values, giving the text a shadow effect:
.card { padding: 2rem; border-radius: 0.5rem; background: linear-gradient(145deg, #cacaca, #f0f0f0); color: #764abc; text-shadow: -6px 6px 15px rgba(0, 0, 0, 0.5), 6px -6px 15px rgba(255, 255, 255, 0.8); box-shadow: 20px 20px 60px #bebebe, -20px -20px 60px white; }
Here’s the result:
See the Pen
Combining text and box shadow: Revised by Rahul (@_rahul)
on CodePen.
You can stack multiple shadows on top of each other by separating their values with commas. This technique can be used to create interesting shadows that blend smoothly into the page.
Let’s demonstrate with CSS:
.stacked-shadows { box-shadow: 0px 1px 2px rgba(0,0,0,0.1), 0px 2px 4px rgba(0,0,0,0.1), 0px 4px 8px rgba(0,0,0,0.1), 0px 8px 16px rgba(0,0,0,0.1); }
Notice that the spread value isn’t added — it’s not necessary in this case, but ultimately, it’s up to you to decide how your box-shadow
looks.
If we set the offset and blur radius to 0px
and add a spread value to one shadow, we can add a border to the box:
.bordered-stacked-shadows { box-shadow: 0px 0px 0px 2px rgba(0,0,0,0.5), 0px 2px 4px rgba(0,0,0,0.1), 0px 4px 8px rgba(0,0,0,0.1), 0px 8px 16px rgba(0,0,0,0.1); }
This border is technically a shadow because no extra space is taken up by the box in the parent element. This is what the output will look like using these two techniques:
Notice the smooth subtle shadow on the left box. The box on the right shows the shadow border.
Now, let’s look at the box-shadow
in a practical scenario. This property can be used on almost any element on a webpage, but the more common ones include the navbar, text cards, and images. It can also be added to input fields and buttons:
See the Pen
Layered shadow Page by Oscar-Jite (@oscar-jite)
on CodePen.
Build a simple webpage like the one shown in the demo, and try styling the box-shadow
yourself!
Real-life shadows are usually black or gray, with varying amounts of opacity. But what if shadows had colors?
In the real world, you get colored shadows by changing the color of the light source. There’s no “real” light source equivalent to change on a website, so you get this neon effect by changing the color value on box-shadow
.
Let’s change the color on our first example:
.box{ box-shadow: 0px 5px 10px 0px rgba(0, 0, 0, 0.7); } .box2{ box-shadow: inset 0px 5px 10px 0px rgba(0, 0, 0, 0.7); }
This is the output:
See the Pen
Neon Shadows by Oscar-Jite (@oscar-jite)
on CodePen.
You can get a more vibrant glow by layering the shadows:
box-shadow: 0px 1px 2px 0px rgba(0,255,255,0.7), 1px 2px 4px 0px rgba(0,255,255,0.7), 2px 4px 8px 0px rgba(0,255,255,0.7), 2px 4px 16px 0px rgba(0,255,255,0.7);
The best way to showcase colored shadows, especially neon ones, is on a dark-themed webpage. Dark themes are very popular and this shadow effect can complement it nicely if you use the right colors.
All the heavy lifting has been done in the previous examples, so let’s darken the earlier site demo, play with colors and shadows, and see the result:
See the Pen
Neon Shadow Demo: Revised by Rahul (@_rahul)
on CodePen.
It’s best to use colors that contrast well, as we’ve done in this demo. The blue box-shadow
stands out well against the dark background. To make it brighter, you can increase the opacity.
This effect is unique and visually pleasing. It came from skeuomorphism, which tries to replicate objects exactly as they would appear in real life.
The first two effects we need to create deal with flat web components that seem to float above the page and cast shadows on the background. This effect makes these components look like they extrude from the page:
.neumorphic-shadow { box-shadow: -10px -10px 15px rgba(255,255,255,0.5), 10px 10px 15px rgba(70,70,70,0.12); }
We can also place them on the inside of our object:
.neumorphic-shadow { box-shadow: inset -10px -10px 15px rgba(255, 255, 255, 0.5), inset 10px 10px 15px rgba(70, 70, 70, 0.12); }
There are two box-shadow
s working opposite each other in the above example. The white box-shadow
shows the direction of the light source and serves as a highlight. It’s similar to what we see in real life:
See the Pen
Neumorphic Shadows: Revised by Rahul (@_rahul)
on CodePen.
Neumorphic design mimics real-life objects. It doesn’t entirely replicate things, but it looks real enough that you could reach out and touch it. Now let’s create something cool: a push switch using a checkbox:
To get started, create a checkbox input:
<input type="checkbox" class="neumorphic-checkbox" />
Now, it’s time for the CSS:
.neumorphic-switch { display: flex; align-items: center; justify-content: center; height: 200px; width: 200px; border-radius: 50%; box-shadow: -10px -10px 15px rgba(255, 255, 255, 0.5), 10px 10px 15px rgba(70, 70, 70, 0.12); border: 20px solid #ececec; outline: none; cursor: pointer; -webkit-appearance: none; }
Next, add the icon. I got this particular icon from Font Awesome. Link the CDN and copy the icon’s Unicode:
.neumorphic-switch::after { font-family: FontAwesome; content: "\f011"; /*ON/OFF icon Unicode*/ color: #7a7a7a; font-size: 70px; }
Set the properties for when the button is clicked. We’re adding box-shadow
inside the circle, so that means creating two inset layers:
.neumorphic-switch:checked{ box-shadow: -10px -10px 15px rgba(255, 255, 255, 0.5), 10px 10px 15px rgba(70, 70, 70, 0.12), inset -10px -10px 15px rgba(255, 255, 255, 0.5), inset 10px 10px 15px rgba(70, 70, 70, 0.12); }
Finally, set the color of the icon after the click:
.neumorphic-switch:checked::after{ color: #15e38a; }
Here’s the result:
See the Pen
Switch by Rahul (@_rahul)
on CodePen.
box-shadow
with the View Transitions APIWith the freshly introduced View Transitions API, you can apply box-shadow
styles dynamically for page or element transitions.
Let’s understand it with a simple example focusing on element transitions (or same-document transitions) using the View Transitions API. Starting by setting up a base, let’s define some CSS styles for a card component we want to expand and collapse upon clicking:
.card { ... box-shadow: 0 0 0.25rem 0.5rem rgba(0, 0, 0, .15); } .card--collapsible { height: 120px; overflow: hidden; view-transition-name: card; } .card--expanded { height: auto; box-shadow: 0 1rem 2rem rgba(0, 0, 0, .35); }
We can easily establish a logic to click-toggle the CSS class carrying box-shadow
styles for our highlighted view with JavaScript’s classList
API:
const card = document.querySelector(".card"); card?.addEventListener("click", () => { card.classList.toggle("card--elevated"); });
We can then use this logic as a callback inside the startViewTransitions
method of the View Transitions API, which should result in something as shown in the following CodePen:
See the Pen
VTBS by Rahul (@_rahul)
on CodePen.
As you can see in the demo above, we used a CSS class to dynamically handle styles — particularly box-shadow
— for the new view state, which also transitions flawlessly without needing a transition property definition.
The User-Agent styles apply a default cross-fading animation to every view transition, which takes care of the transitioning between the views, giving an impression that the shadows are also getting transitioned.
If you prefer managing your styles using JavaScript, consider using the boxShadow
property of the style
object or go with a CSS-in-JS library of your choice:
const targetElement = document.querySelector(".my-element"); targetElement?.style.boxShadow = "0 0 3px 4px hsl(25deg 50% 50% / 20%)"
Before implementing this in production, make sure to check the browser support for View Transtions, which is currently just above 74%.
box-shadow
with native CSS nestingWhen writing complex box-shadow utilities, the native CSS nesting can provide a better, less redundant way to organize declarations.
For instance, say you have a card component with different box shadows for hover, active, and focused states. Then you also have some variations of the card component with different box-shadow
implementations.
Traditionally, you’d write rules for each of these states one-by-one in CSS, or utilize a CSS preprocessor like Sass to take advantage of nesting. With the native CSS nesting, you won’t have to rely on the preprocessor for nesting because the browser will deal with it automatically:
/* Simple card */ .card { /* Card styles */ box-shadow: ...; &:hover { box-shadow: ...; } &:active { box-shadow: ...; } } /* Elevated card variation */ .card--elevated { /* Elevated card styles */ box-shadow: ...; &:hover { ... } &:active { ... } }
This structure is easier to manage and maintain compared to separate rule sets. We can take this one step further with cascade layers, another important modern CSS feature covered in the next section.
box-shadow
in @layer
blocksThe @layer
rule is designed to efficiently deal with specificity issues in CSS. In our case, it can help us control the order of specificity and scope for our box-shadow
components and utilities.
In the below code, the specificity order for the layers is defined in the first line where the utilities layer takes precedence over the components layer, and then the cascade layer blocks are declared:
@layer base, components, utilities; @layer base { :root { --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.1); --shadow-md: 0 1px 3px rgba(0, 0, 0, 0.15); --shadow-lg: 0 4px 6px rgba(0, 0, 0, 0.2); @media (prefers-color-scheme: dark) { --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.2); --shadow-md: 0 1px 3px rgba(0, 0, 0, 0.3); --shadow-lg: 0 4px 6px rgba(0, 0, 0, 0.4); } } } @layer components { .card { box-shadow: var(--shadow-md); &:hover { box-shadow: var(--shadow-lg) } &:active { box-shadow: var(--shadow-sm) } } } @layer utilities { .shadow-sm { box-shadow: var(--shadow-sm) } .shadow-md { box-shadow: var(--shadow-md) } .shadow-lg { box-shadow: var(--shadow-lg) } }
Using the above cascade layer strategy, our utility styles will always override the component styles as they are declared later in the specificity order.
This strategy not only powers better maintenance and organization but also provides style encapsulation and makes framework integration a lot less painful.
box-shadow
generatorsCSS box-shadow
generators are online tools that aid with the creation and virtualization of CSS box-shadow
effects. They can be useful to both beginners and experienced developers as they can help save time and effort when designing shadow effects.
There are many CSS box-shadow
generators out there but here are a few popular options:
box-shadow
effects. It provides a wide range of features, including the ability to add multiple shadows, each with its own set of independent controls. Users can choose from a variety of shadow types, such as inset and outer, and fine-tune the offset, blur, spread, color, and opacity of the shadowsbox-shadow
generation, design tools such as Adobe XD or Figma include extensive web design features such as shadow effects. Designers frequently choose these tools to generate and customize not only box shadows but also other design components in a unified environmentbox-shadow
addition in Tailwind CSS projects. Additionally, it provides ready-made vanilla CSS box-shadow
code that can be seamlessly integrated into your projectbox-shadow
best practicesThe box-shadow
property is a nice way to make your website look good, but it could easily ruin your project if not used properly. Here are some tips to consider when styling shadows:
box-shadow
. Plus, this property already looks great on its own, so there’s really no need for animation. Animations can be a simple :hover
to transition
box-shadow
layers to your object, which saves time as you don’t have to enter multiple values to get the perfect shadow manually. Plus, you can get more intricate shadow values a bit more quicklyIn this article, we looked at how to layer shadows and create neon shadows using the box-shadow
CSS property. We then touched on implementing box-shadow
with the View Transitions API, native CSS nesting, and cascade layers. We also introduced a few CSS box-shadow
generators that can be useful to both beginners and experienced developers by saving you time and effort when designing shadow effects.
Finally, we looked at how we can combine the box-shadow
property with the text-shadow
property to create well-rounded shadow effects to add depth to our webpage.
You’re well on your way to becoming a shadow expert! Practice makes perfect, so try styling some shadows yourself. See how many box-shadow
layers you can add to an element. Try combining colors and see what works. Remember to test on as many devices as possible for optimal performance.
As web frontends get increasingly complex, resource-greedy features demand more and more from the browser. If you’re interested in monitoring and tracking client-side CPU usage, memory usage, and more for all of your users in production, try LogRocket.
LogRocket is like a DVR for web and mobile apps, recording everything that happens in your web app, mobile app, or website. Instead of guessing why problems happen, you can aggregate and report on key frontend performance metrics, replay user sessions along with application state, log network requests, and automatically surface all errors.
Modernize how you debug web and mobile apps — start monitoring for free.
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.
Handle frontend data discrepancies with eventual consistency using WebSockets, Docker Compose, and practical code examples.
Efficient initializing is crucial to smooth-running websites. One way to optimize that process is through lazy initialization in Rust 1.80.
One Reply to "Styling with the CSS box-shadow property"
Muito bom suas dicas, obrigado