# Creating Shadows with Pseudo-Elements

# Using Pseudo-Elements for Box Shadows

What if I wanted a `box-shadow` with a linear gradient? I can add a color to the `box-shadow` property, but I can't define a linear gradient on it. A would work, but not B:

**A**:

```
{
  /* offset-x | offset-y | blur-radius | spread-radius | color */
  box-shadow: 2px 2px 2px 1px rgba(0, 0, 0, 0.2);
}

```

**B**:

```
{
  /* offset-x | offset-y | blur-radius | spread-radius | color */
  box-shadow: 2px 2px 2px 1px linear-gradient(-45deg, #687bdd 0%, #221e22 50%, #e90e45 100%);
}
```

That's because the [`linear-gradient`](https://developer.mozilla.org/en-US/docs/Web/CSS/gradient/linear-gradient%28%29) property is actually a special type of *image*, not a color. Since a shadow cannot have an image, we have a problem.

There's also the additional issue of the *shape* of the shadow. Box shadows are boxes by default. We could use [`drop-shadow`](https://developer.mozilla.org/en-US/docs/Web/CSS/filter-function/drop-shadow%28%29) if we wanted, but then we wouldn't be able to define a linear gradient on that. Moreover, drop-shadows conform to the shape of the object they are shadowing. You don't get to define a custom shape on that shadow.

To solve this problem, we can use CSS pseudo-elements. In particular, I'm talking about the [`::before`](https://developer.mozilla.org/en-US/docs/Web/CSS/::before) and [`::after`](https://developer.mozilla.org/en-US/docs/Web/CSS/::after) pseudo-elements.

CSS pseudo-elements allow you to create extra content for your page without actually having to add elements to the HTML. There are [A LOT](https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-elements) of them, but `::before` and `::after` are the most used by far. In this case, we'll use these to make a custom shadow for our box with some cool effects.

Before we start, below are some important rules we need to remember about working with `::before` and `::after`:

- They MUST have a `content` property defined on them. You may make this an empty string, which is the convention, but you cannot omit it from the pseudo-element's CSS rule or it won't show up on your page.

- A pseudo-element is actually a *child* of the element it is defined on. This explains the monikers `::before` and `::after`. One appears *before* the rest of the containing element's children; the other appears *after* them.

- Most of the rules that apply to normal elements apply to pseudo-elements as well. You can position and style them, and they will typically inherit styles from their parents like good little children.

And now that we've set the ground rules, let's get to work!

## A Gradient Shadow

First, we define a simple card. Inside it, I'll add some text telling us what we're doing because why not?!

```
<div class="card">
  <h2 class="gradient-shadow">Gradient Shadow</h2>
</div>

```
That's all we need. We can now style it. We'll start by defining a height and width for the card. We'll also turn the card into a `flex` container and center everything so it looks nice. We'll `position: relative` it so that we can `position: absolute` its children and play around with them more easily. Finally, we'll give it a white background for simplicity.

```
.card {
    position: relative;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 300px;
    width: 300px;
    background: white;
}

```

And now comes the fun part: adding the shadow. For this, we'll use a `::before` pseudo-element, though we can just as easily use an `::after` element. Since we'll `position: absolute` it, we are not constrained by where it logically appears in its parent element.

Here's a short list of what we'll do with the `::before` element:

- We'll give it an empty string value for the `content` property.
- We'll `position: absolute` it. This allows us to define its offsets, and also whether it appears behind or in front of everything else.
- Its `top` offset will be `40px` and its `left` offset will be 0. Since it's a shadow, we want it slightly below the parent container (by 'slightly', I mean `40px` below the parent).
- We want the pseudo-element to be just as large as its parent. That way, it will be visible. We will therefore set the `width` and `height` properties to `100%`.

> Note: elements are automatically assigned `display: block` when you `position: absolute` them, so we can set a width and height on our `::before` pseudo-element without explicitly assigning a display property to it. Otherwise, `::before` and `::after` pseudo-elements have `display: inline` by default.

- We'll give our `::before` pseudo-element a[`linear-gradient`](https://developer.mozilla.org/en-US/docs/Web/CSS/gradient/linear-gradient%28%29) for its background. For this, I just used a gradient generator and picked something that looked good.

- To make our `::before` pseudo-element more shadow-like, we'll give it a blur. For this, we set its [`filter`](https://developer.mozilla.org/en-US/docs/Web/CSS/filter) property to, well, `blur`. You can specify any blur size you want. I chose `30px` because it looked more natural to me.
- The pseudo-element is still sitting in front of its parent. We'll set its `z-index` to `-1` so it goes behind it.
- Finally, I also decided to [`transform: scale()`](https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/scale%28%29) the pseudo-element down slightly. This will mean we see less of it bulging out of the sides of its parent. Since it's been shifted downwards, we'll see most of the 'shadow' on the bottom of the container.

The final code should look something like below:

```
.card::before {
  content: "";
  position: absolute;
  top: 40px;
  left: 0;
  width: 100%;
  height: 100%;
  background: linear-gradient(43deg, #4158D0 0%, #C850C0 46%, #FFCC70 100%);
  filter: blur(30px);
  z-index: -1;
  transform: scale(0.9);
}
```


Below is a pen of the final result. Note I added some styling for the body so everything looks good.

%[https://codepen.io/RamRam93/pen/KKyxKRK]

And we have a nice rainbow shadow!

But why stop there? Let's try and make something slightly fancier.

## Making A Skewed Shadow

Remember the ground rules we set? We said that you can do just about anything with a pseudo-element that you can do with a regular DOM element. That is an immense amount of power! Linear gradients are just the tip of the iceberg. You can shape them however you like and even go so far as to animate them. To give you a taste of this power, we'll create a skewed shadow with 2 different colors on opposite corners.

We'll start by writing the HTML. It will be the same div as before, except I won't be adding any text to it in this case. Nothing too complicated:

```
<div class="card">
<div>

```

Let's style the card:

- As usual, we'll `position: relative` our card so we have control over the positioning of its children.
- We'll set the `width` and `height` properties for our card. I chose the dimensions `220px` and `320px` respectively because I thought it looked good.
- Finally, I gave the card a background color. This was so that it would look nice next to its colored shadow later on. This part is at your discretion, really.

The final code should be as below:

```
.card {
  position: relative;
  width: 220px;
  height: 320px;
  background: #030a2e;
}
```

Now we can style the shadow.

For this, the concept is to have a shadow that juts out of 2 opposite corners of the card, with a distinct color at each corner. So, for example, if the shadow is to poke out of the top left and bottom right corners, it would have one color in the top left corner and another in the bottom right. To help it stand out, it would look slightly skewed at the corners.

- We can achieve the effect of 2 colors by defining a linear gradient on the shadow. It would start at one color in one corner and transition into another one by the time it reached the other corner. 
- We'll then place the shadow behind the card using its `z-index` property so all we can see are the colors popping out of the corners. 
- To make the corners actually pop out, we'll use the `transform: skewX() skewY()` property.
- The other stuff, like positioning and setting the `content` property will be like in the previous example.

There's something a little more complicated to talk about here (but only a little):

In the previous example, we explicitly set the width and height of our pseudo-element. Before that, we had given it a vertical (using `top`) and horizontal (using `left`) offset. We could do this because we had set its `position` property to `absolute`.

Elements with `position: absolute` have an interesting property that lets us set their width and height in other ways: if we set all 4 offsets (`top`, `right`, `bottom` and `left`), we can *stretch* the element. For example, if we set all 4 properties above to have a value of `4px`, the pseudo-element will have`8px` shorter `width`  and `height` properties (`4px` less on top, 4px less on the right, etc.) than its parent. It will be as if we had set the `width` and `height` properties explicitly.

On the other hand, if we set all the offsets to something like `-4px`, the pseudo-element would have `8px` longer `width` and `height` properties. So common is this practice of uniformly setting the offsets to size a `position: absolute` element that there is a CSS property to do it all at once: `inset`.

In this case, I want my `::before` element to be `8px` wider and taller than its container. That way, it will be easy to skew it at the corners. `8px` wider and taller is the same as `left: -4px`, `right: -4px`, `top: -4px`, and `bottom: -4px`. We can achieve that in one fell swoop with `inset: -4px`, allowing us to write less code.

> Note: the `inset` property takes a single value, so it should only be used where all 4 offsets would have the same value.

The final code for the before element will look something like what we have below:

```
.card::before {
    content: '';
    position: absolute;
    inset: -4px;
    transform: skewX(3deg) skewY(6deg);
    background: linear-gradient(-45deg, #687bdd 0%, #221e22 50%, #e90e45 100%);
    z-index: -1;
}
```

I also added some rules for the body, just so everything is well centered, and gave it the same background color as the card, because I thought it made the effect look even nicer. The shadow should appear as below:

%[https://codepen.io/RamRam93/pen/abVPyQJ]

## Afterglow

The above card looks nice, but we could make it look even more interesting without adding a single element to the HTML. In this case, we'll leverage the `::after` pseudo-element.

The idea is simple: We'll give the `::after` pseudo-element all the properties we gave the `::before` pseudo-element, and then some. We'll add a blur effect on the `::after` element using the `filter` property so it simulates a *glow*:

```
.card::after {
    content: '';
    position: absolute;
    inset: -4px;
    transform: skewX(3deg) skewY(6deg);
    background: linear-gradient(-45deg, #687bdd 0%, #221e22 50%, #e90e45 100%);
    filter: blur(50px);
    z-index: -1;
}
```

And now I present to you the end result!

%[https://codepen.io/RamRam93/pen/MWOqYWj]

As you can see, we've been able to achieve a lot with just a single HTML element, while doing most of the work in CSS, all thanks to the power of pseudo-elements.

## Caveats

There are some things to consider, and gotchas to avoid, while we use pseudo-elements in our daily work.

### Accessibility

Pseudo-elements don't appear in the DOM. That's probably why they're called 'pseudo-elements'; they're not actual HTML elements. You should therefore avoid using them to represent important information on your page as that might lead to accessibility issues. Not all screen readers can detect pseudo-elements.

### Z-index

In this exercise, we played around with the `z-index` of elements on the page to achieve the shadow effect. Note that the `z-index` property can only be applied to positioned elements. Also, whenever you define `z-index` on a suitable element, you create a new [stacking context](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context) for that element and its children. If you ever find `z-index` not working the way you expect it to, try to figure out whether `z-index` was defined on an element earlier in the CSS, and whether this is determining where your current element is in the stacking context. You can read the article I've linked on stacking contexts to understand how this works.

## Conclusion

And with that, we're done! We managed to create colorful shadows using pseudo-elements. We've only touched the tip of the iceberg here. As you can imagine, a lot more is possible with pseudo-elements. There are a lot of talented people out there who come up with new ways to utilize this CSS feature every day, sometimes making artwork that would give Picasso or Van Gogh a run for their money. Hopefully, this introduction will inspire you to create your own art too.

#### Attributions

I was inspired to write this by [this video](https://www.youtube.com/watch?v=jMbWDcc5oNc), where I learned this technique. It's a great channel with lots of gems. Be sure to check it out!

Any interesting thoughts? Let's talk in the comments!
