Liquid Glass Spikes
Here are a set of demos used to experiment with how Safari 26 with Liquid
Glass works with various CSS.
In particular, we noticed that modals behave in unexpected ways with the
new floating UI design for the Safari toolbar.
Table of contents
Summary of findings
The conditions for a fullscreen modal overlay element to
cover the entire screen with a position: fixed;
seems to be:
- The background must be semi-transparent
- Solid colours, linear-gradients, etc. don't work
- The container must be empty
This also means the standard <Dialog>
and
::backdrop
don't seem to work.
The conditions for a bottom sheet to cover the entire
screen, including the area around the Safari toolbar seems to be:
-
The element must be positioned within 3px from the bottom of the
viewport
- The height must be within a certain threshold
Issues in production
Here are some examples of sites that break with Safari 26.
Link
Opening one of the modals causes the overlay to appear, but as the
overlay can not reach the bottom of the viewport, you can still see some
content from the underlying page around teh afari liquid glass toolbar.
This can include words, images and in some cases playing videos.
Google image search
Link
Start by doing a search, then click on one of the search results. This
opens a full screen modal showing related images, but as the overlay can
not reach the bottom of the viewport, the initial search results are
visible around the Safari liquid glass toolbar.
Update: This seems to behave differently now (first
screnshot is before, second is after), not sure if Google or Safari
changed.
Demonstration of specific issues
Here are some specific demos that were made while trying to work out how
to control what is displayed around the liquid glass toolbar.
Native <Dialog>
element
Demo
This example has minimal implementation logic and relies on Safari's own
implementation of the <Dialog>
component, which
includes a subtle backdrop color for the overlay.
However, the overlay does not extend to the top and bottom of the
viewport as one would expect and instead stops short at the top and
bottom of the "safe area".
Overlay color
Demo
This is a small, custom implementation of a modal dialog and bottom
sheet with various colors set for the overlay.
The color choice seems to affect whether or not the modal reaches the
ends of the viewport. A solid color or linear gradient does not go to
the far edges of the viewport, while a semi-transparent color does.
The other thing to note with this example is that when the bottom sheet
is displayed, it will fill in the bottom area of the viewport around the
Safari toolbar, but this isn't the case for a lot of websites, including
our "sticky bottom" on realestate.com.au
safe-area-inset-bottom
Demo
Chrome on Android handle a similar thing with the operating systems
gesture bar covering the content at the bottom of the viewport. There is
a
blog post
explaining how to use env(safe-area-inset-bottom)
variable
to cater for this, along with the use of the
viewport-fit=cover
option in the viewport meta tag to control this behaviour.
While we couldn't find the equivalent documentation for Safari,
according to
caniuse.com, Safari do support most of the same features as Chrome, however they
do not seem to take effect for the Safari UI.
In this demo, there is supposed to be a red square with the height and
width of the env(safe-area-inset-bottom)
variable and while
it's visible on chrome when there is content overlapping with floating
UI from Android, it is not visible on Safari which would indicate that
variable is not populated with a value in this case.
vh
units
Demo
Another way of controlling the height of elements is with the various
viewport units. While
vh
has historically caused lots of issues on mobile
browsers, the introduction of dvh
, svh
and
lvh
have helped to address some of these issues.
However, even when using viewport units that are supposed to extend past
any dynamic toolbars, they still do not go into the area around the
Safari toolbar and act the same as the demo with the overlay colors.
outerHeight vs innerHeight
Demo
Another way of controlling the height of elements is with JavaScript,
which has properties on the window object that provide the height of the
viewport both within and outside of the browser UI via
window.innerHeight
and window.outerHeight
.
This demo shows a few variations of each of these properties, the first
is with a coloured <div>
within the flow of the
document, which demostrates that outerHeight
is the height
of the entire viewport and innerHeight
is the "safe area"
in between the browser toolbars.
As the "safe area" between the browser UI elements is dynamic based on
if the Safari toolbar is expanded or not, you can click the
divs
to refresh the values they are displaying but even
though the values are changing, the behaviour of the modal overlays are
the same.
The second thing this shows is using those same values except as a
position: fixed
overlay with various background colors set.
While the value of window.innerHeight
and
window.outerHeight
seem to be set correctly as shown by the
colored divs on the page, the overlay doesn't seem to be able to take
advantage of them as the color of the background seems to have more of
an impact on the actual height.
Parent-child vs sibling relationships
Demo
The "Overlay color" demo showed that by having a semi-transparent
overlay, Safari would fill the rest of the viewport with it, but not if
it was a solid color or a gradient. However, the Construct Kit modal
also uses a semi-transparent color but was not filling around the rest
of the viewport.
The difference between them is how the elements are structured. The demo
above puts the overlay and the dialog on the page as siblings while
Construct Kit uses the React Modal library, which puts the dialog (or
any provided content) within the overlay element as a child, rather than
as a sibling.
So this means that if the overlay has any content, whether it be a white
colored div
or just some text, it will not fill the rest of
the viewport.
Bottom sheet height
Demo
The bottom sheet will fill the bottom of the viewport when it's height
is below a certain threshold and it's positioned "close enough" (usually
within around 3px or so. The actual height threshold doesn't seem to be
a fixed number and I haven't worked out exactly what the conditions are
here yet.