Day 19: Styling a popup like clouds
Tags: webdev, css, glassmorphism
Published: 2022-12-16 04:38:04

## 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](https://codesandbox.io/s/my-ideal-maps-popup-mb9mpl?file=/src/styles.css).

## 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**](https://dev.to/masakudamatsu/day-2-finding-design-concept-to-drive-ui-design-process-49g3).)

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**](https://dev.to/masakudamatsu/day-7-making-buttons-look-like-clouds-for-embedded-google-maps-33po).)

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zj2plhotz862750mudyh.jpg) **Aerial view of central London amid clouds** (image source: [Wirestock via Freepik](https://www.freepik.com/free-photo/aerial-view-central-london-through-clouds_10499932.htm))

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:
```css
.popup {
  background-color: rgb(255, 255, 255);
}
```
![Solid white rectangle shown over a street map](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/okorivnvgxd042gh3nxz.png)

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%:
```css
.popup {
  background-color: rgba(255, 255, 255, 0.93);
}
```

![Translucent white rectangle shown over a street map](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9lhq11zwdrost5gysy9r.png)

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](https://uxdesign.cc/glassmorphism-in-user-interfaces-1f39bb1308c9)). It is the frosty-glass look introduced by [Microsoft’s Fluent Design System](https://docs.microsoft.com/en-us/windows/apps/design/style/acrylic) 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s1tshoypy9gewl58vw6j.png)

## 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](https://medium.com/web-dev-survey-from-kyoto/glassmorphism-for-firefox-2113dff03747)), 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
```css
.popup {
  --popup-background-color-fallback: rgba(255, 255, 255, 0.93);
  background-color: var(--popup-background-color-fallback);
}
```
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**](https://dev.to/masakudamatsu/day-7-making-buttons-look-like-clouds-for-embedded-google-maps-33po)).

### 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:
```css
.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 */
```
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
```css
background-attachment: fixed;
background-image: -moz-element(#map);
```
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](https://developers.google.com/maps/documentation/javascript/overview#maps_map_simple-html), 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:
```css
.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 */
```
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)](https://dev.to/masakudamatsu/glassmorphism-for-firefox-3285).

It turns out that [Kai OS](https://en.wikipedia.org/wiki/KaiOS), a Chinese mobile phone OS, also [supports the `-moz-element()` CSS function](https://caniuse.com/css-element-function), but [not `backdrop-filter`](https://caniuse.com/css-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:
```css
box-shadow: 0 0 var(--blur-radius) var(--blur-radius) var(--popup-background-color);
```
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](https://medium.com/web-dev-survey-from-kyoto/css-box-shadow-is-not-to-create-a-shadow-but-to-place-the-duplicate-copy-beneath-11e849d000c0), for more detail).

It seems that the definition of _blur radius_ differs between the `box-shadow` property and the `blur()` function (see [Baron 2011](https://dbaron.org/log/20110225-blur-radius)). 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:
```css
.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;
  }
}
```
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:
```html
<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>
```
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:
```css
.popup {
  bottom: 0;
  left: 0;
  position: absolute;
  right: 0;
  top: 0;
}
```
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](https://codesandbox.io/s/my-ideal-maps-popup-mb9mpl?file=/src/styles.css), 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:
```css
.popup {
  --popup-margin: 8px;
}
.popup button[aria-label="Close popup"] {
  position: absolute;
  right: var(--popup-margin);
  top: var(--popup-margin);
}
```
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](https://web.dev/accessible-tap-targets/)).

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](
https://codesandbox.io/s/my-ideal-maps-popup-mb9mpl?file=/src/styles.css). It uses React to reuse the CloseButton component created on [Day 18](https://dev.to/masakudamatsu/day-18-building-a-close-button-with-ripple-effect-as-a-react-component-1n62). 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?](https://dbaron.org/log/20110225-blur-radius)”, _David Baron’s Weblog_, Feb 25, 2011.

Gash, Dave, Meggin Kearney, Rachel Andrew, Rob Dodson, and Patrick H. Lauke (2020) “[Accessible tap targets](https://web.dev/accessible-tap-targets/)”, _web.dev_, Mar 31, 2020.

Kudamatsu, Masa (2020) “[CSS box shadow is not to create a shadow...](https://medium.com/web-dev-survey-from-kyoto/css-box-shadow-is-not-to-create-a-shadow-but-to-place-the-duplicate-copy-beneath-11e849d000c0)”, _Web Dev Survey from Kyoto_, Jan 3, 2020.

Kudamatsu, Masa (2021) “[Glassmorphism for Firefox](https://medium.com/web-dev-survey-from-kyoto/glassmorphism-for-firefox-2113dff03747))”, _Web Dev Surveys from Kyoto_, Dec 3, 2021.

Malewicz, Michal (2020) “[Glassmorphism in user interfaces](https://uxdesign.cc/glassmorphism-in-user-interfaces-1f39bb1308c9)”, _UX Collective_, Nov 23, 2020.