Modern Web Weekly #9

Modern Web Weekly #9
Staying up to date with the modern web
๐Ÿ‘‹
Hello there! I'm Danny and this is Modern Web Weekly, your weekly update on what the modern web is capable of, Web Components, and Progressive Web Apps (PWA). I test modern web features and write about them in plain English to make sure you stay up to date.

Exciting new PWA proposals ๐ŸŽ‰

Both Microsoft and Webkit have come up with exciting new proposals for PWAs (and regular web apps) recently.

Microsoft proposes a new Web Install API to replace the current beforeinstallprompt and enable cross-domain installation of PWAs. Currently, a PWA can only install itself (same-domain) but when web apps can also install PWAs from other domains (cross-domain), app store-like catalogs could be created where users can pick any PWA to install directly.

The explainer has more details.

While Webkit has only recently begun to support Push Notifications, the team has now come up with an interesting proposal for declarative push notifications called Declarative Web Push that would enable the OS to display notifications without requiring a Service Worker.

This would mean that displaying a push notification would become simpler, more privacy-preserving and less CPU-intensive. It would also mean that a push notification would always be shown as any errors in the Service Worker code would no longer prevent it from showing.

Check the explainer for all the details.

A native HTML accordion with <details> and <summary>

Safari Tech Preview 179 added support for the name attribute on the <details> element which enables you to group together multiple <details> elements as an accordion.

When you then click one <details> element in that group, all others will close. This makes it possible to create an accordion with only HTML, no JavaScript needed.

Here's a codepen that demonstrates this, currently only supported in Safari Tech Preview 179:

You may need to view this in the browser version:

Animating opening and closing of dialogs and popovers

Being able to animate a <dialog> or popover when it's opened and closed is a much-requested feature by developers. You would therefore expect that it would be as easy as adding a transition to the <dialog> when it has the open attribute present.

Let's say we want the <dialog> to fade in and slide in from the top when it's opened and fade out and slide up when it's closed. When the <dialog> is opened the open attribute is automatically added so it would be easy to animate that with something like this right?:

dialog {
  opacity: 0;
  transform: translate(0 -50px);
  transition: opacity .3s ease-out, transform .3s ease-out;
}

dialog[open] {
  opacity: 1;
  transform: translate(0 0);
}

Well... no.

Unfortunately, this CSS doesn't animate the <dialog> because transitions can only be started for elements that are being rendered and since <dialog> has display: none before it's opened, transitions to the open state don't work.

One way to solve this could be to add another attribute to the <dialog>, for example opened when it's opened, remove that when it's closed, and define the transition with that:

/* note the attribute is now "opened" instead of "open"
dialog[opened] {
  opacity: 1;
  transform: translate(0 0);
}

Then you could add two buttons to add and close the dialog and put the adding and removing of opened in their click handlers:

openButton.addEventListener('click', () => {
  dialog.show();
  dialog.setAttribute('opened', '');
});

closeButton.addEventListener('click', () => {
  dialog.removeAttribute('opened');

  setTimeout(() => dialog.close(), 300);
});

The buttons now open and close the <dialog> and add and remove the opened attribute. You also need to make sure that the <dialog> is closed after the animation has finished otherwise, the closing animation will not be visible. This means you need to delay the closing using a setTimeout with a delay equal to the transition duration.

Here's a codepen that does exactly this:

You may need to view this in the browser version:

But this is not a good solution. You need to remember to add and remove the opened attribute and keep the transition duration that's in CSS in sync with the delay of setTimeout that's in JavaScript.

Luckily, we can now do better.

By using the @starting-style grouping rule, we can now define styles where the transition should start from for elements that are not being rendered when the transition starts.

This means that the styles that we set on dialog to start transitioning from should now also be defined in @starting-style:

@starting-style {
  dialog[open] {
    opacity: 0;
    translate: 0 -50px;
  }
}

Note that we define the styles with the selector dialog[open] and not dialog. This is because when the transition starts, the dialog already has the open attribute.

Now we can just use the styles from the first example combined with the styles defined inside @starting-style to make the transition work, although we still need one more change.

When the dialog is closed the browser will immediately set display: none on it so the exit effect (fade-out and slide-up) won't be visible. Recall this was the reason we needed to wrap the closing of the dialog in setTimeout in the first example.

To prevent that we also need to transition the display property which is possible since Chrome 116. This will ensure that the dialog will only get display: none at the end of the transition so the fade-out and sliding up will be visible.

The same is true for the overlay property. A modal dialog is displayed in the so-called "top-layer" of the document which is a layer that is displayed above all other elements on the page. This layer ensures that a dialog or popover is always displayed on top of everything else. An element that is in the top-layer has its overlay property set to auto and when it's not in the top-layer the value of overlay is none.

overlay can only be set by the browser to add or remove an element from this top-layer so you cannot set it with CSS but you can transition/animate it. By animating the overlay property you ensure that the element stays in the top-layer while the transition or animation is running so it won't be obscured or clipped by any other elements on the page.

When animating discrete properties like display and overlay you need to set the transition-behavior to allow-discrete. When using a shorthand you need to add allow-discrete to all discrete properties that are transitioned:

transition: opacity .3s ease-out, 
            translate .3s ease-out, 
            overlay .3s ease-out allow-discrete, /* discrete property */
            display .3s ease-out allow-discrete; /* discrete property */

Congratulations! You now have an animated dialog! ๐ŸŽ‰

Here's a codepen that demonstrates animating a dialog and a popover. Note that styles for a displayed popover are defined with the :popover-open pseudo-class:

You may need to view this in the browser version:

๐Ÿ”—
Got an interesting link for Modern Web Weekly?Send me a DM on Twitter to let me know!