Hey, we’re back with weekly updates about the browser landscape from Šime Vidas.
In this week’s update, the CSS :not
pseudo class can accept complex selectors, how to disable smooth scrolling when using “Find on page…” in Chrome, Safari’s support for there media
attribute on <video>
elements, and the long-awaited debut of the path()
function for the CSS clip-path
property.
Let’s jump into the news…
The enhanced :not() pseudo-class enables new kinds of powerful selectors
After a years-long wait, the enhanced :not()
pseudo-class has finally shipped in Chrome and Firefox, and is now supported in all major browser engines. This new version of :not()
accepts complex selectors and even entire selector lists.
For example, you can now select all <p>
elements that are not contained within an <article>
element.
/* select all <p>s that are descendants of <article> */
article p {
}
/* NEW! */
/* select all <p>s that are not descendants of <article> */
p:not(article *) {
}
In another example, you may want to select the first list item that does not have the hidden
attribute (or any other attribute, for that matter). The best selector for this task would be :nth-child(1 of :not([hidden]))
, but the of
notation is still only supported in Safari. Luckily, this unsupported selector can now be re-written using only the enhanced :not()
pseudo-class.
/* select all non-hidden elements that are not preceded by a non-hidden sibling (i.e., select the first non-hidden child */
:not([hidden]):not(:not([hidden]) ~ :not([hidden])) {
}
Refresh
header can be an accessibility issue
The HTTP The HTTP Refresh
header (and equivalent HTML <meta>
tag) is a very old and widely supported non-standard feature that instructs the browser to automatically and periodically reload the page after a given amount of time.
<!-- refresh page after 60 seconds -->
<meta http-equiv="refresh" content="60">
According to Google’s data, the <meta http-equiv="refresh">
tag is used by a whopping 2.8% of page loads in Chrome (down from 4% a year ago). All these websites risk failing several success criteria of the Web Content Accessibility Guidelines (WCAG):
If the time interval is too short, and there is no way to turn auto-refresh off, people who are blind will not have enough time to make their screen readers read the page before the page refreshes unexpectedly and causes the screen reader to begin reading at the top.
However, WCAG does allow using the <meta http-equiv="refresh">
tag specifically with the value 0
to perform a client-side redirect in the case that the author does not control the server and hence cannot perform a proper HTTP redirect.
How to disable smooth scrolling for the “Find on page…” feature in Chrome
CSS scroll-behavior: smooth
is supported in Chrome and Firefox. When this declaration is set on the <html>
element, the browser scrolls the page “in a smooth fashion.” This applies to navigations, the standard scrolling APIs (e.g., window.scrollTo({ top: 0 })
), and scroll snapping operations (CSS Scroll Snap).
Unfortunately, Chrome erroneously keeps smooth scrolling enabled even when the user performs a text search on the page (“Find on page…” feature). Some people find this annoying. Until that is fixed, you can use Christian Schaefer’s clever CSS workaround that effectively disables smooth scrolling for the “Find on page…” feature only.
@keyframes smoothscroll1 {
from,
to {
scroll-behavior: smooth;
}
}
@keyframes smoothscroll2 {
from,
to {
scroll-behavior: smooth;
}
}
html {
animation: smoothscroll1 1s;
}
html:focus-within {
animation-name: smoothscroll2;
scroll-behavior: smooth;
}
In the following demo, notice how clicking the links scrolls the page smoothly while searching for the words “top” and “bottom” scrolls the page instantly.
media
attribute on video sources
Safari still supports the With the HTML <video>
element, it is possible to declare multiple video sources of different MIME types and encodings. This allows websites to use more modern and efficient video formats in supporting browsers, while providing a fallback for other browsers.
<video>
<source src="/flower.webm" type="video/webm">
<source src="/flower.mp4" type="video/mp4">
</video>
In the past, browsers also supported the media
attribute on video sources. For example, a web page could load a higher-resolution video if the user’s viewport exceeded a certain size.
<video>
<source media="(min-width: 1200px)" src="/large.mp4" type="video/mp4">
<source src="/small.mp4" type="video/mp4">
</video>
The above syntax is in fact still supported in Safari today, but it was removed from other browsers around 2014 because it was not considered a good feature:
It is not appropriate for choosing between low resolution and high resolution because the environment can change (e.g., the user might fullscreen the video after it has begun loading and want high resolution). Also, bandwidth is not available in media queries, but even if it was, the user agent is in a better position to determine what is appropriate than the author.
Scott Jehl (Filament Group) argues that the removal of this feature was a mistake and that websites should be able to deliver responsive video sources using <video>
alone.
For every video we embed in HTML, we’re stuck with the choice of serving source files that are potentially too large or small for many users’ devices … or resorting to more complicated server-side or scripted or third-party solutions to deliver a correct size.
Scott has written a proposal for the reintroduction of media
in video <source>
elements and is welcoming feedback.
clip-path: path()
function ships in Chrome
The CSS It wasn’t mentioned in the latest “New in Chrome 88” article, but Chrome just shipped the path()
function for the CSS clip-path
property, which means that this feature is now supported in all three major browser engines (Safari, Firefox, and Chrome).
The path()
function is defined in the CSS Shapes module, and it accepts an SVG path string. Chris calls this the ultimate syntax for the clip-path
property because it can clip an element with “literally any shape.” For example, here’s a photo clipped with a heart shape:
Nice news! A few notes. First one regarding example using the
:not
pseudo class. Couldn’t we rewrite this example as:That is without first
:not([hidden])
selector?Regarding better Smooth Scroll example, again I’m a bit confused why do we need 2 animations, Until I’m missing something, I think we could rewrite it in the following way:
That is, we only use single animation, and then when the focus is within
html
we remove this animation.If I understand the selector correctly,
:not(:not([hidden]) ~ :not([hidden]))
does select the item we want (the first not hidden one), but it also selects all the hidden items.So in order to only select the first non-hidden item and not the hidden items, we need to further qualify the selector with an extra
:not([hidden])
.I must admit, I do not understand how the workaround works, but I’ve tested your code (CodePen) and the scrolling is no longer smooth in Firefox for me.
Thank you Šime, for your response.
This double negation is still quite confusing, to say the least.
For instance, if this selector
ul > :not(:not([hidden]) ~ :not([hidden]))
means that the list items that preceded by another list item which has
hidden
attribute defined on it and there are other list items, with thehidden
attribute, that follows,then why first list item on the list, which doesn’t have a preceding sibling with the
hidden
attribute on it, gets selected?As for the
smooth-scroll
trick. After the first click onto bottom
link, consequent clicks won’t do anything, that is, the page doesn’t scroll or even jump to the bottom of the page.It might be some bug in the FF.
I’ve checked on Windows 10, FF 85
Yup, it’s kind of like regex, powerful but not easy to read.
I think a good way to work out what this selector does is to look at the inner part first. What does
:not([hidden]) ~ :not([hidden])
select? It selects all non-hidden elements that are preceded by at least one non-hidden element. So in my demo that would be the 5., 6., 8., and 9. item.The outer
:not()
inverts this, which means that:not(:not([hidden]) ~ :not([hidden]))
selects all other items: the 1., 2., 3., 4., and 7. item.We want to select only the 2. item, the only non-hidden one in that group, so we add another
:not([hidden])
to the front of the selector to exclude the hidden items.Re
smooth-scroll
, I suspect the issue is caused by CodePen’s wrapper. When I open the demo on a clean page*, the issue does not occur.*There’s an “Open frame in new tab” option in Firefox
how would a selector list look like for the :not() selector…like this:
?
Yup, a comma-separated list of selectors.
The specificity of the selector is determined by the most specific argument, so if you write
.foo:not(.bar, #baz)
, the specificity of the full selector will be 1 ID (#baz
) + 1 class (.foo
), which is quite high.Also, if one of the arguments in the list is not supported in the browser, the entire selector is ignored. For example,
.foo:not(.bar, :focus-visible)
is ignored in browsers that don’t support:focus-visible
.