Pure CSS to Make a Button “Shine” and Gently Change Colors Over Time

Because animations and gradients in CSS are delightful.

Paige Niedringhaus
Bits and Pieces

--

Photo by Ruvim Noga on Unsplash

I love when websites go the extra mile…

As a web developer myself, I always appreciate when websites go above and beyond being purely functional, and add a touch of fun and magic. A parallax background, a cool animation or page transition, or some other cute Easter egg I wasn’t expecting to find — you know it when you see it. It’s the kind of thing that makes you smile and think: “Cool!”.

And a lot of times, it really isn’t that hard to make these things happen — or, at least it may not be — because chances are, you’re not the first person who’s wanted to do it. All it takes is a little searching, a little testing and tweaking to make it fit your use case, and a little extra effort, to make someone else’s day when they encounter it.

Recently, I’ve been building my own personal website with Gatsby. It’s a site where people can learn more about me, find all the articles I’ve written over time about web development and get in touch if they want to say hi. 👋

One of my site components is a subscribe button for an email newsletter so people who want to stay up-to-date with what I’m writing and sharing can be notified when I release new material.

And instead of a standard button, I wanted to make this button fun and special (because this is my personal site and I’m the designer and the developer! 👩‍🎨). So I gave it a blue color-shifting background gradient, and when a user mouses over it, the button grows, the gradient shifts to pink AND there’s a really cool shine effect that slides across the button. All of this (which may sound like a lot of extra effort), is what I’ll show you how to do today — and with only pure CSS, I might add.

💡 Tip: When sharing components to Bit, whether it is to add a new component to your design system or just to make a useful component available for reuse across repositories — do not limit yourself to JS components alone (React, Angular, Vue, etc.) — make sure to share your CSS/SASS components as well.

Learn more about sharing and reusing your components:

Learn about reusing CSS components:

Example: A CSS component shared on Bit.dev

The subscribe button

Below is a video I made showing the finished product: you’ll notice when the button is at rest, the background color slowly shifts from dark blue to light blue and back again. And then when the mouse hovers over the button, it grows bigger, the background gradient shifts to a pink to blue gradient and a “shine effect” slides quickly across it from left to right.

Video of the subscribe button in action: its slowly shifting gradient backgrounds, and how it grows and shines when a user hovers over it.

I’m pretty pleased with how this turned out, and it was really fun to work on to get it just right. The best part? Even though it may seem complicated at first glance, it’s really not that bad — all you need to know is CSS.

If you want to interact with this CSS before I dive into the details, here’s a CodePen I made:

Interactive CodePen demo of the subscribe button CSS

Shifting gradient backgrounds

Ok, the first thing I want to cover is how to give the button a color shifting background, both when it’s just at rest and also when a user mouses over it.

But before I cover that, let me show you the button’s base CSS.

Button base CSS

So my subscribe HTML <button /> element has a class of .subscribe-button attached to it, which is where I applied my basic styling. Please note: all of the CSS you’ll see is actually written in SCSS, a superset of CSS, but it should be simple enough to follow along with, and easy to convert to traditional CSS without much effort.

Subscribe.scss

Your basic button CSS — nothing too fancy here.

The CSS of this button is nothing out of the ordinary: font-size, font-weight, text color and text alignment inside the button, the size of the button itself, and a little stylish rounding of the button’s corners courtesy of the border-radius property. Easy enough.

Background gradient and animation CSS

Now to the fun part of the CSS: the background gradient. There’s two CSS elements at play here: the linear-gradient(), which is responsible for the background colors, and the keyframes, which handle the animation to make it appear to shift over time.

Here is the CSS I added to my button and the button’s :hover state as well to make this happen. Note the comment // button css here this is where the button’s original CSS is, I just omitted it from this screenshot for ease of seeing the additional code.

Subscribe.scss

The CSS to create the background gradients for the button and then animate the movement with the help of CSS keyframes animation.

linear-gradient() is a really cool function available in CSS, and it creates an image consisting of a progressive transition between two or more colors along a straight line. It can be rotated by degrees, with simple directions like: to left top, or with turn.some-percentage-here. You can also define at what percentage along the gradient’s length you want one color to begin or end.

There’s really so many different ways you can style these CSS gradients, I would highly encourage you to check out the MDN documentation, which does a great job explaining and showing interactive examples.

So the original background-image color is a gradient of two different medium blue hues: #8E9AC2 (my CSS variable $medBlueTint2) and #42579A ($medBlue), and angled at a 270 degree tilt. When the mouse hovers over the button, a new linear gradient is applied (because why not? 🎨), which is a bright blue (#2D8FE5) and a bright pink (#D155B8), that goes straight left now, and transitions gently from blue on the left to pink on the right.

The slow progression of the button’s background gradients from one color to another hinges on the background-size being much larger than the actual button itself (hence the reason I set it to 400% height and 400% width in the CSS with background-size: 400% 400%;). Then, the gradient (which is much larger than what you can actually see behind the button thanks to the button’s CSS property of overflow: hidden;) is animated using CSS keyframes and changing the background’s position to different points on the gradient as the animation progresses.

Inside of the keyframes CSS, the background-position is updated over the course of the animation. background-position sets the initial position for each background image: at the beginning (0%) and end (100%) of the animation, the piece of the background behind the button is the top left of side of the gradient (background-position: 1% 0%), at the middle of the animation (50%), the background will be positioned at the bottom right of the gradient (background-position: 99% 100%).

Finally, the keyframes defined are animated in the button with the CSS animation property: animation: TransitionBackground 10s ease infinite;. What this translates to is: animate the keyframe named TransitionBackground, at a rate of 10 seconds, ease it (so the animation has a slow start, then speeds up in the middle, before it slows down again at the end), and loop it infinitely.

And last, but certainly not least, I have to mention the transition: 0.6s property on the button’s CSS. As you may have noticed in the basic button’s CSS, the button’s dimensions were: height: 60px; and width: 200px;, but on hover, the button actually grows a bit to height: 75px; and width: 215px;. In order for the button to grow gradually on hover (or shrink when the mouse moves away), the transition property is needed. Otherwise, it will snap from smaller to larger and back again with no smooth growing or shrinking over the course of 0.6 seconds. Cool, huh?

Shine animation on hover

Next up is the little “shine” effect that zips quickly across the button when the user hovers over it. This little effect’s CSS looks deceptively complex, but it’s not too bad once I break down what’s happening.

Shine elements and animation CSS

The thing to understand for shine is that it employs the CSS pseudo-elements ::before and ::after.

If you’re not too familiar with pseudo-elements (I wasn’t until now), what they are is special keywords added to a selector that lets you style a specific part of the selected element(s). For example, to make the first line of every <p> tag specially styled with the ::first-line pseudo-element, you’d write the following CSS:

Pseudo-element ::first-line example

p::first-line {
color: blue;
text-transform: uppercase;
}

Note that in CSS3, pseudo-elements are defined in CSS with the ::pseudo-element, whereas traditional actions on elements like :hover, :focus, :disabled, etc. only have one colon at the beginning. There’s only 15 pseudo-elements at the time of my writing this, and some are still in the experimental stage, but I encourage you to check them out, they’re pretty cool.

Here’s the CSS for the shine effect on the button.

Subscribe.scss

The pseudo-element CSS for the button to make the shine effect and make it slide across the button on mouseover.

Although the shine effect looks like one element when you see it, it’s actually made up of two-elements: the Subscribe button’s ::before and ::after pseudo-elements.

The pseudo-element ::before creates an element that is the first child of the selected element. And likewise, the ::after pseudo-element makes an element that is the last child of the selected element. Both are often used to add cosmetic content to an element with the content property, which is how I employed them too. Note: You need to include content (even if it’s an empty string) as one of your CSS properties for these pseudo-elements or else the element won’t be visible in the browser.

If you look at the code screenshot, you’ll notice the pseudo-elements share a good bit of CSS: they both have empty content, they share the same display, position, height, top, and transform properties. Note also: both elements’ transform property actually starts them off the button 100px to the left, but because the button itself has the overflow: hidden; property, they aren’t visible to the naked eye (like the extra large gradient background too).

They also share the same background color of white, but the ::before's opacity is higher at 50% — the last element in rgba(255,255,255,0.5);, while the ::after element’s opacity is 20% (rgba(255,255,255,0.2);).

The ::before pseudo-element is slightly larger than the ::after (60px versus 30px), and its filter property is greater ,as well (blur(30px) versus blur(5px);).

Most of these decisions about opacity, size and blur were purely for aesthetics of how the shine looked to me. Feel free to tweak them to make the shine effect to your liking. I highly recommend changing various pieces to see what happens and if you like the look it gives you.

The part to really pay attention to is the CSS nested inside the &:hover state. This combination of transition and transform is what animates the shine and sends it flying from its original starting position, hidden off to the left, all the way across the button’s surface, until it’s hidden again on the right.

&::before, 
&::after {
transform: translateX(300px) skewX(-15deg);
transition: 0.7s;
}

When you combine an element’s overflow: hidden; property , then add some pseudo-elements positioned to start beyond its original borders, and throw in a simple transform to shift them across it quickly, it ends up looking like a really cool shine running along the element. 😄

So… why did I chose to use pseudo-elements instead of just making two new divs to be the shine effect, you ask? Mostly to make my life easier. Since the pseudo-elements automatically go with whatever element they’re part of, I didn’t have to worry about their height being off, their placement versus the button itself not matching, or having to deal with having them expanding at the same rate as the button size changes on hover, either. They’re the perfect solution for my particular use case.

Now let’s wrap this all up.

Putting it altogether

If you’re curious, here’s a screenshot of all my CSS in one place (plus comments), and if you’d like to see the actual code, it’s available on Github here and as an interactive CodePen demo, which I’ve linked to below.

Interactive CodePen demo of the subscribe button CSS

Here’s a screenshot of the CSS as well.

Subscribes.scss

The complete CSS for the button in one place. From basic styling, to background gradients, to shine effects.

Yes, it looks a little intimidating at first glance, but once it’s dissected and discussed piece by piece, it’s really not all that bad, is it?

Conclusion

Magic matters. When it comes to websites that stand head and shoulders above others — that make them memorable, it’s because they took the time to put in those little extra touches. A sparkle here, a wiggle there, a custom loading animation — it all adds up and makes a difference.

It might seem trivial, it might not be something you think you’d notice as a user, but someone will (even if they don’t know the time and effort that it took to get it “just right”), and they will appreciate it. I know I certainly do.

And the best part? Magic (or CSS, in this case) doesn’t have to be hard, and it doesn’t have to be done in a vacuum. One of the best things about being a web developer is the amazing community of people building fun things and sharing their source code and how they did it (like this blog, for instance!). They want us to take those initial creations and ideas, and build on them to make them our own unique thing. 😎

Check back in a few weeks — I’ll be writing more about JavaScript, React, ES6, or something else related to web development.

Thanks for reading. I hope you found this pure CSS article about shifting gradients and shines leveraging CSS pseudo-elements fun, and that you may be able to incorporate some “magic” like this into your own projects.

Build Apps with reusable components, just like Lego

Bit’s open-source tool help 250,000+ devs to build apps with components.

Turn any UI, feature, or page into a reusable component — and share it across your applications. It’s easier to collaborate and build faster.

Learn more

Split apps into components to make app development easier, and enjoy the best experience for the workflows you want:

Micro-Frontends

Design System

Code-Sharing and reuse

Monorepo

Learn more:

References & Further Resources

--

--

Staff Software Engineer at Blues, previously a digital marketer. Technical writer & speaker. Co-host of Front-end Fire & LogRocket podcasts