DEV Community

Cover image for Day 19: Styling a popup like clouds
Masa Kudamatsu
Masa Kudamatsu

Posted on • Updated on • Originally published at Medium

Day 19: Styling a popup like clouds

TL;DR

For a popup in web apps to appear like clouds, use the frosted glass look (aka. glassmorphism) with translucent white box shadows around (as seen in the top image). And write the CSS code as in the CodeSandbox demo for this article.

Introduction

My Ideal Map, a web app I'm buidling, aims to improve the user experience of Google Maps. For one thing, it allows users to see their saved places on Google Maps and nothing else. 

Naturally, the app needs to embed Google Maps full-screen by default.

Given this default user interface, how should I design and code popups to show menus, a search box, error messages, etc.?

My answer is to make popups appear like the eye sight of being in the midst of clouds, with the CSS technique for glassmorphism. 

In this article, I describe the rationale behind this design decision and the CSS code for realizing this UI design.

Design considerations

Whenever I need to make a decision on designing a web app component, I go back to the design concept for the whole app.

The design concept of My Ideal Map is “Dye me in your hue from the sky”. Likening Google Maps to the view of city streets from the sky, the app “dyes” the city view with colorful placemarks as the user’s favorite places. (For more detail, see Day 2 of this blog series.)

Consequently, I design the app’s user interface, featuring Google Maps full-screen, by mocking the view of a city from the sky. Which is why I have designed the buttons to open the menu and to open the search box as if they were the clouds floating in the sky. (For more detail, see Day 7.)

Now I need to design popups that opens up after pressing these cloud-shaped buttons. The popup will also display information on searched or saved places or error messages.

The city view, that is, the embedded Google Maps, will be overlaid with a popup. Imagine you’re flying high in the sky like a bird, looking down the streets of a city. What can the bird-eye’s view of a city be overlaid with?

Yes, clouds.
An aerial photo of central London amid clouds Aerial view of central London amid clouds (image source: Wirestock via Freepik)

Given that the user presses the cloud-shaped buttons to open the popups for the menu and the search box, popups should give an impression that the user moves into the midst of clouds, with their eye sight blurred due to the myriads of tiny water droplets in the clouds.


Now the question is what makes the popup background appear like being the midst of clouds.

What makes the popup look like clouds?

First attempt: solid white

Clouds are white. I can make the popup look like clouds by making its background purely white:

.popup {
  background-color: rgb(255, 255, 255);
}
Enter fullscreen mode Exit fullscreen mode

Solid white rectangle shown over a street map

Well, that doesn’t work. A solid white background reminds us of white paper. Many web apps use this analogy, including Google Maps. With the pure white background, I cannot distinguish My Ideal Map from Google Maps. That's not a good branding strategy because the app is not a copycat of Google Maps. Instead it aims to improve the user experience of Google Maps.

Second attempt: translucent white

A better idea is a translucent white background with the opacity of, say, 93%:

.popup {
  background-color: rgba(255, 255, 255, 0.93);
}
Enter fullscreen mode Exit fullscreen mode

Translucent white rectangle shown over a street map

A cloud is not a solid. It’s made of myriads of tiny water droplets. What’s behind can be seen through clouds as long as clouds are not too thick.

But I wasn’t satisfied with this solution, without exactly knowing why.

Third attempt: Glassmorphism

I kept searching for inspiration, and I happened to learn about the UI style known as “glassmorphism” (Malewicz 2020). It is the frosty-glass look introduced by Microsoft’s Fluent Design System and later popularized by MacOS BigSur.

As the name suggests, it imitates the look of a glass sheet with uneven surface. Refraction of light in varoius directions (due to the unevenness of the glass surface) blurs the appearance of what’s behind the glass.

I realize that’s what happens when we are in the midst of clouds. Clouds are made of tiny water droplets, refracting light in various directions. As a result, the view of what’s behind clouds gets blurred.

Yes, I can use the “glassmorphism” look to make My Ideal Map's popups appear like clouds.

And I want to achieve something like this:

A frosted glass-looking rectangle over a street map

CSS for glassmorphism look

The basic technique to create the glassmorphism look is to use the CSS declaration of background-filter: blur().

Until very recently (July 22, 2022), however, Firefox didn’t support the background-filter property. As no one clearly explained how to allow Firefox users to see the glassmorphism look, I wrote an article entitled “Glassmorphism for Firefox” (Kudamatsu 2021), which now comes at the top of Google search for “glassmorphism Firefox”.

Using the technique descirbed in that article, I can make the popup’s background look like clouds as follows.

Supporting legacy browsers

First, provide the fallback for legacy browsers

.popup {
  --popup-background-color-fallback: rgba(255, 255, 255, 0.93);
  background-color: var(--popup-background-color-fallback);
}
Enter fullscreen mode Exit fullscreen mode

A translucent white does the best job to imitate clouds as a fallback. The opacity value of 0.93 is chosen because it is used for cloud-shaped buttons as well (see Section 6.1 of Day 7).

For modern browsers

Then, for all the modern browsers (including the versions of Firefox released in July 2022 or later), overwrite the background-color property as follows:

.popup {
/* ADDED FROM HERE */
  --blur-radius: 8px;
  --popup-background-color: rgba(255, 255, 255, 0.75);
  /* ADDED UNTIL HERE */

  --popup-background-color-fallback: rgba(255, 255, 255, 0.93);
  background-color: var(--popup-background-color-fallback);
}

/* ADDED FROM HERE */
@supports (backdrop-filter: blur(var(--blur-radius))) or (-webkit-backdrop-filter: blur(var(--blur-radius))) {
  .popup {
    background-color: var(--popup-background-color);
    -webkit-backdrop-filter: blur(var(--blur-radius));
    backdrop-filter: blur(var(--blur-radius));
  }
}
/* ADDED UNTIL HERE */
Enter fullscreen mode Exit fullscreen mode

First, the blur radius value of 8px for backdrop-filter and the opacity value of 0.75 for background-color were chosen after trial and error to make the popup background evoke a sense of clouds.

Second, the @supports at-rule is necessary to prevent legacy browsers from overwriting the background-color property value.

Finally, the -webkit-backdrop-filter property is necessary to support Safari, which ignores backdrop-filter without the prefix.

For legacy Firefox and Kai OS

Finally, for the versions of Firefox released before July 2022, which doesn’t support background-filter, I use the -moz-element() CSS function to achieve the same glassmorphism look. This Firefox-only function, which I wish other browsers would also support, copies another HTML element as an image. If we set

background-attachment: fixed;
background-image: -moz-element(#map);
Enter fullscreen mode Exit fullscreen mode

then the background image is exactly the same as what is rendered as an HTML element with id="map". Since I embed Google Maps in <div id="map"> by following the Google Maps Platform documentation, the above CSS declaration uses the entire Google Maps as the background image(!). Plus, thanks to background-attachment: fixed, the positioning of an image of Google Maps is exactly the same as the original one.

Using this technique, here’s the entire CSS code to achieve the glassmorphism background:

.popup {
  --blur-radius: 8px; 
  --popup-background-color: rgba(255, 255, 255, 0.75); 
  --popup-background-color-fallback: rgba(255, 255, 255, 0.93);
  background-color: var(--popup-background-color-fallback);
}
@supports (backdrop-filter: blur(var(--blur-radius))) or (-webkit-backdrop-filter: blur(var(--blur-radius))) {
  .popup {
    background-color: var(--popup-background-color);
    -webkit-backdrop-filter: blur(var(--blur-radius));
    backdrop-filter: blur(var(--blur-radius));
  }
}

/* ADDED FROM HERE */
@supports (background-image: -moz-element(#map)) and (not (backdrop-filter: blur(var(--blur-radius)))) {
  .popup {
    background-color: transparent;
  }
  /* Blurring the map beneath */
  .popup::before {
    background-attachment: fixed;
    background-image: -moz-element(#map);
    content: "";
    filter: blur(var(--blur-radius));
    position: absolute;
    left: 0; right: 0; top: 0; bottom: 0;
    z-index: -2;
  }
  /* Applying translucent white on top */
  .popup::after {
    background-color: var(--popup-background-color);
    content: "";
    position: absolute;
    left: 0; right: 0; top: 0; bottom: 0;
    z-index: -1;
  }
}
/* ADDED UNTIL HERE */
Enter fullscreen mode Exit fullscreen mode

First, the @supports at-rule includes (not (backdrop-filter: blur(var(--blur-radius)))) so that the latest versions of Firefox will ignore the rule.

Second, the popup element itself has a transparent background, because the other two pseudo elements, rendered beneath, create the glassmorphism look.

Third, the ::before pseudo element’s filter: blur() applies the same blurring effect to the background as the background-filter: blur() does.

Finally, the blurred map image is overlaid with the ::after pseudo element whose background color is --popup-background-color (i.e., the translucent white).

For more detail, please see Section 2 of Kudamatsu (2021).

It turns out that Kai OS, a Chinese mobile phone OS, also supports the -moz-element() CSS function, but not backdrop-filter (the links take you to Can I Use?). So the fallback for legacy Firefox also works for Kai OS.

Edge treatment

If the popup is full-screen, the above CSS code is enough to create an impression of clouds. But on a large screen, the popup needs to occupy only a part of the screen. Since the edge of an HTML element is straight by default, it kills an illusion of clouds as a popup.

My solution is to use box shadow as follows:

box-shadow: 0 0 var(--blur-radius) var(--blur-radius) var(--popup-background-color);
Enter fullscreen mode Exit fullscreen mode

where --blur-radius and --popup-background-color are defined as 8px and rgba(255, 255, 255, 0.75), respectively, as in the previous section.

The first two values of box-shadow locate the shadow beneath the element. With 0 0, the shadow is positioned directly beneath the element, as if the light hits the element directly from the above. This way, all the four sides have shadows.

The third and fourth values of box-shadow are the blur radius and the spread radius, respectively. The former specifies the degree to which the shadow color blends with the image beneath while the latter sets the extent to which the shadow expands around the original rectangle (see my article, Kudamatsu 2020, for more detail).

It seems that the definition of blur radius differs between the box-shadow property and the blur() function (see Baron 2011). After trial and error, however, I find using 8px, the value for blur(), for both blur and spread radius values of box-shadow makes the popup's straight edge least noticeable.

And the fifth value of box-shadow is the shadow color. I use the same translucent white as the one used for the popup background. This changes whether we target legacy browsers or modern browsers. So the entire CSS code is now as follows:

.popup {
  --blur-radius: 8px; 
  --popup-background-color: rgba(255, 255, 255, 0.75); 
  --popup-background-color-fallback: rgba(255, 255, 255, 0.93);
  /* Legacy browsers */
  background-color: var(--popup-background-color-fallback);
  box-shadow: 0 0 var(--blur-radius) var(--blur-radius) var(--popup-background-color-fallback); /* ADDED */
}
@supports (backdrop-filter: blur(var(--blur-radius))) or (-webkit-backdrop-filter: blur(var(--blur-radius))) {
  .popup {
    background-color: var(--popup-background-color);
    -webkit-backdrop-filter: blur(var(--blur-radius));
    backdrop-filter: blur(var(--blur-radius));
    box-shadow: 0 0 var(--blur-radius) var(--blur-radius) var(--popup-background-color); / * ADDED */
  }
}
/* Legacy Firefox */
@supports (background-image: -moz-element(#map)) and (not (backdrop-filter: blur(var(--blur-radius)))) {
  .popup {
    background-color: transparent;
    box-shadow: none; /* ADDED */
  }
  /* Blurring the map beneath */
  .popup::before {
    background-attachment: fixed;
    background-image: -moz-element(#map);
    content: "";
    filter: blur(var(--blur-radius));
    position: absolute;
    left: 0; right: 0; top: 0; bottom: 0;
    z-index: -2;
  }
  /* Applying translucent white on top */
  .popup::after {
    background-color: var(--popup-background-color);
    box-shadow: 0 0 var(--blur-radius) var(--blur-radius) var(--popup-background-color); /* ADDED */
    content: "";
    position: absolute;
    left: 0; right: 0; top: 0; bottom: 0;
    z-index: -1;
  }
}
Enter fullscreen mode Exit fullscreen mode

For legacy Firefox, the box-shadow is best to be applied to the ::after pseudo element to conceal the straight edges of a popup element. (Again it is the conclusion from trial and error; I don’t know the reason behind.) This is why I need to remove the box-shadow of the popup element itself: otherwise, the box shadow for legacy browsers will also be rendered.

Other styling

There are a couple of other CSS declarations necessary to render a popup over the map.

Placing over the map

The HTML code for the map and the popup in My Ideal Map will be something like this:

<body>
  <main>
    <div id="map">
      <!-- JavaScript embeds Google Maps here-->
    </div>
  </main>
  <form role="search">
    <div class="popup">
      <button aria-label="Close popup" type="button">
        <!-- SVG code for the cross (x) icon (omitted) -->
      </button>
    </div>
  </form>
</body>
Enter fullscreen mode Exit fullscreen mode

where <form role="search"> can be other landmark role elements such as <nav> for the navigation menu.

To place the popup over the map, therefore, we need to apply position: absolute to the popup. If the popup is full screen, the following CSS code does the job:

.popup {
  bottom: 0;
  left: 0;
  position: absolute;
  right: 0;
  top: 0;
}
Enter fullscreen mode Exit fullscreen mode

To make the popup smaller than the full-screen size, we can adjust the values of bottom, left, right, and top. In the demo for this article, I set these four property values to be 10% so that the popup size is 80% of the full screen size (and center-aligned).

Positioning the close button

To position the close button around the top right corner of the popup, I use the following CSS code:

.popup {
  --popup-margin: 8px;
}
.popup button[aria-label="Close popup"] {
  position: absolute;
  right: var(--popup-margin);
  top: var(--popup-margin);
}
Enter fullscreen mode Exit fullscreen mode

The value of 8px is chosen as the minimum spacing between interactive elements:

Touch targets should also be spaced about 8 pixels apart, both horizontally and vertically, so that a user's finger pressing on one tap target does not inadvertently touch another tap target. — (Gash et al. 2020).

The top-right corner of the popup, if not full-screen, can be adjacent to a tappable element on Google Maps. To make sure the user will be able to tap the close button rather than something else on Google Maps, I want the tappable area of the close button 8px away from the top corner both vertically and horizontally.

Also, I use the attribute selector button[aria-label="Close popup"] to style the close button. A popup is very likely to have other buttons as its child elements. To target the close button only, we can use a class selector like .close-button. As I use Styled Components to style the close button (see Day 18), however, I need an alternative selector that does not require any change in the HTML code. In such a situation, the attribute selector is a great solution.

Demo

Here’s the demo of the entire code described in this article. It uses React to reuse the CloseButton component created on Day 18. But I use CSS stylesheet, rather than Styled Components, in line with what this article describes.

References

Baron, David (2011) “What does a blur radius mean?”, David Baron’s Weblog, Feb 25, 2011.

Gash, Dave, Meggin Kearney, Rachel Andrew, Rob Dodson, and Patrick H. Lauke (2020) “Accessible tap targets”, web.dev, Mar 31, 2020.

Kudamatsu, Masa (2020) “CSS box shadow is not to create a shadow...”, Web Dev Survey from Kyoto, Jan 3, 2020.

Kudamatsu, Masa (2021) “Glassmorphism for Firefox)”, Web Dev Surveys from Kyoto, Dec 3, 2021.

Malewicz, Michal (2020) “Glassmorphism in user interfaces”, UX Collective, Nov 23, 2020.

Top comments (0)