A frosted glass story


It’s not an article about copy-pasting some code (which you are welcome to do). It’s a short story about the development of a simple, yet craftful piece of code and visual effect for our beloved web.

I love winter themes, and as part of an experiment in website theming, I created this Frosted Glass effect. It adds a frosted glass to any element with a subtle melting effect on hover.

The core idea is to have a drop-in component. It can be used in any website, regardless of the underlying technology. It is built using Web Components, which means it can be easily integrated into any web project without worrying about compatibility issues.

In the example above, the frosted glass component is simply added inside the button element, and it will automatically apply the frosted glass effect to the button. The first button has the default white frosted glass, while the second one has a custom color, which makes sense for a button with a white background.

To summarize, design goals are:

  • drop-in usage, and work out-of-the-box.
  • works in any framework.
  • minimal configuration.
  • small code footprint.

and here’s a working code usage example:

<button>
  Click
  <frosted-glass></frosted-glass>
</button>

Now, let’s dive into the story.

First iteration

Hunt for visual effect

The idea was to make it part of an experiment, where user just adds the library and all the magic is automatically applied. First, I started to experiment with icy glass effect.

My first instinct was to use real ice textures. I applied css mask with several images, some of them looked good. But this approach has some problems: it doesn’t scale well between different sizes and it’s hard to find a free to use fitting pattern. I’m not a graphic designer, so I didn’t try to create my own textures.

If image patterns are too complex, it’s harder to apply them. The idea is that this effect should be applicable to virtually any element, and most elements contain text which must stay readable. Thus, a simple overlay effect seems the way to go.

I still believe that some custom graphic effects would be a great way to make an original and beautiful design, but I leave this for another time.

Simple effect it must be, so I searched the depths of the internet for some examples as well as leveraging AI. At the end I settled with a simple repeating gradient pattern, with different transparency levels, a slight blur and a subtle shadow. I was aiming for an “ice like” effect, so I also added a slight “melting” animation on hover, which softens the effects and the element underneath becomes more prominent.

The effect became less “realistic”, but more suitable and recognizable as a web graphic effect. I think, it’s more acceptable and more widely applicable.

Applying the effect as a drop-in

As I mentioned in the beginning, it was a part of a bigger experiment, and the idea was to make it a drop-in component with as little configuration as possible. So the first implementation was a class that selected provided elements, created a new element inside to which the effect was applied.

Because white frosted glass on white background didn’t make sense, I experimented with blue color for an ice-like effect visible on white background.

This brings us to a natural question, how do we decide when to apply it?

I also experimented with AI is to detect the luminance of the host element and automatically apply a blue color for light backgrounds. But doing a few tests, I found that different hues respond differently to this slight blue tint, and although light, they can look better with white frosted glass. So I decided to apply it only for the white background. Plus, I’d like to avoid ship too much code for this simple effect, considering that it was meant to be a part of a bigger theme. So instead, it just detected recursively whether the host element has a white background.

For customization, advanced users would still be able to set the color manually, probably by providing the color as a configuration option to the manager class.

Changing the mindset

The final effect is quite simple, but I spent some time experimenting with images and tweaking the pattern. Now that the effect was ready I moved on and tried to make other effects, like sparkles and snow dust.

However, doing this takes some time. Such specific effects are new to me, and gauging the right parameters and fitting them all together is challenging.

If you are a developer, you probably see nothing wrong with it. Just code! But if you want to ship a product, even as small as this, you’ll notice something important:

I coded and researched, but I didn’t ship anything.

I had no idea if theming is even interesting for anyone. Thus, I decided to extract this effect and ship it as a standalone component, and at least get some real feedback on it.

Web Component

If before it was meant for non-technical users first, hence the drop-in and auto-detect and apply idea, now it has to be developer friendly. It’s the world I’m part of, and the most straightforward way to share.

Luckily, because of its simplicity, it’s easy to adapt into different forms.The obvious idea is to ship it as a Web Component. It encapsulates the styles and any necessary JavaScript and makes it easy to use in any project.

It still can be used as a drop-in piece or handled by manager class, used as-is in developer codebases or even copypasted and tweaked as necessary.

Firstly, I just created the web component class carrying over the initial functionality. Then, I created a detailed demo within a plain html file to showcase it and test.

Testing and adapting for developer use

I discovered z-index issues which I didn’t notice before since I focused on the effect and its usage. The effect overlay would capture mouse events and prevent interaction with its host element. I decided to disable <frosted-glass> pointer events and handle the hover differently, by adding event listeners to its host element.

Because <frosted-glass> had a :hover css rule that triggered a “melting effect”, this prompted me to create a custom state, the “melted” state, which is activated when mouse is over. At the time being this state can’t be triggered by user code, but it can easily be modified in the future if it makes sense.

Hover to melt:

Opacity contrast

I still experimented with the auto-detect of the background color, and iterated once again with the luminance detection, and the end I decided it doesn’t make sense at all to have this kind of functionality in a standalone component. Having the option to set a preferred color is enough, and if any kind of logic determining the color is needed, it can be implemented by the user in their codebase. So I removed this functionality and left it to the user to decide when to apply which color.

RedNeonPurpleMetalBlackYellowPinkCyan

Another important option is selecting the glass effect opacity. Simple option, except the fact that different parts of the pattern have varying opacity. Initially, I introduced it as opacity-coefficient which effectively was just a multiplier for the current values, but it was confusing, since without the upper limit it’s not clear the effect it has. For example, a value of 2 or 3 would just multiply the real opacity and it’s not clear when it becomes fully opaque.

Moreover, if we imagine this web component used, any updates to the frosted pattern opacity, would create unpredictable results when the opacity-coefficient is used. And the simple solution is to make it coherent with the standard css opacity rule. I changed the name of the attribute opacity-coefficient to opacity and made the effects base background color depending on it and the pattern parts became multiplies to retain the original color proportions. Now, if the value of opacity is 1, the effect is fully opaque, and when it’s 0 it’s fully transparent.

Opacity examples, 0.01 to 0.1 and 0.1 to 1:

0.01.02.03.04.05.06.07.08.09.1
.1.2.3.4.5.6.7.8.91

The last part I realized while testing is that it’s worth having the same configurable options for the “melted” state, so I introduced it as well.

This allows use to have a much better control over the effect and create more interesting interactions.

For example, a level change on hover:

Layer depth change

From specific to general

Already from the last part, you can see it was polished for a more general and flexible use. This brings us to a different place from when it’s started, the winter theme. Now it became a frosted glass effect, and with the ability to tweak its options at will, it can serve different purposes. Use warm colors for a cozy and soft effect, or use it for a futuristic design with bright neon colors.

Arctic Blue Autumn Leaf Neon Magenta

And here’s a more elaborate and dramatic example, where the frosted glass is used to create a dreamy and mystical atmosphere:

Dramatic Glass

What is the conclusion?

There’s no conclusion, it’s just one of thousands untold stories about development. There’s no moral, no lesson, no “best practices”. Creating is a journey, and every journey is different.

But there’s something you can do: try it, use it, and share your experience with this tiny visual effect:

npm install @mycolaos/frosted-glass

or copy the code from the

Frosted Glass Github repo

!important when you open the repo, click on the ⭐, thanks!