Front End Web Development

CSS Container Queries

Css Tricks - Mon, 06/10/2024 - 6:12am

Container queries are often considered a modern approach to responsive web design where traditional media queries have long been the gold standard — the reason being that we can create layouts made with elements that respond to, say, the width of their containers rather than the width of the viewport.

.parent { container-name: hero-banner; container-type: inline-size; /* or container: hero-banner / inline-size; */ } } .child { display: flex; flex-direction: column; } /* When the container is greater than 60 characters... */ @container hero-banner (width > 60ch) { /* Change the flex direction of the .child element. */ .child { flex-direction: row; } } Why care about CSS Container Queries?
  1. When using a container query, we give elements the ability to change based on their container’s size, not the viewport.
  1. They allow us to define all of the styles for a particular element in a more predictable way.
  1. They are more reusable than media queries in that they behave the same no matter where they are used. So, if you were to create a component that includes a container query, you could easily drop it into another project and it will still behave in the same predictable fashion.
  1. They introduce new types of CSS length units that can be used to size elements by their container’s size.
Table of Contents Registering Elements as Containers .cards { container-name: card-grid; container-type: inline-size; /* Shorthand */ container: card-grid / inline-size; }

This example registers a new container named card-grid that can be queried by its inline-size, which is a fancy way of saying its “width” when we’re working in a horizontal writing mode. It’s a logical property. Otherwise, “inline” would refer to the container’s “height” in a vertical writing mode.

  • The container-name property is used to register an element as a container that applies styles to other elements based on the container’s size and styles.
  • The container-type property is used to register an element as a container that can apply styles to other elements when it meets certain conditions.
  • The container property is a shorthand that combines the container-name and container-type properties into a single declaration.
Some Possible Gotchas Querying a Container @container my-container (width > 60ch) { article { flex-direction: row; } }
  • The @container at-rule property informs the browser that we are working with a container query rather than, say, a media query (i.e., @media).
  • The my-container part in there refers to the container’s name, as declared in the container’s container-name property.
  • The article element represents an item in the container, whether it’s a direct child of the container or a further ancestor. Either way, the element must be in the container and it will get styles applied to it when the queried condition is matched.
Some Possible Gotchas Container Queries Properties & Values Container Queries Properties & Values container-name container-name: none | <custom-ident>+; Value Descriptions
  • none: The element does not have a container name. This is true by default, so you will likely never use this value, as its purpose is purely to set the property’s default behavior.
  • <custom-ident>: This is the name of the container, which can be anything, except for words that are reserved for other functions, including default, none, at, no, and or. Note that the names are not wrapped in quotes.
Open in Almanac
  • Initial value: none
  • Applies to: All elements
  • Inherited: No
  • Percentages: N/A
  • Computed value: none or an ordered list of identifiers
  • Canonical order: Per grammar
  • Animation: Not animatable
container-type container-type: normal | size | inline-size; Value Descriptions
  • normal: This indicates that the element is a container that can be queried by its styles rather than size. All elements are technically containers by default, so we don’t even need to explicitly assign a container-type to define a style container.
  • size: This is if we want to query a container by its size, whether we’re talking about the inline or block direction.
  • inline-size: This allows us to query a container by its inline size, which is equivalent to width in a standard horizontal writing mode. This is perhaps the most commonly used value, as we can establish responsive designs based on element size rather than the size of the viewport as we would normally do with media queries.
Open in Almanac
  • Initial value: normal
  • Applies to: All elements
  • Inherited: No
  • Percentages: N/A
  • Computed value: As specified by keyword
  • Canonical order: Per grammar
  • Animation: Not animatable
container container: <'container-name'> [ / <'container-type'> ]? Value Definitons

If <'container-type'> is omitted, it is reset to its initial value of normalwhich defines a style container instead of a size container. In other words, all elements are style containers by default, unless we explicitly set the container-type property value to either size or inline-size which allows us to query a container’s size dimensions.

Open in Almanac
  • Initial value: none / normal
  • Applies to: All elements
  • Inherited: No
  • Percentages: N/A
  • Computed value: As specified
  • Canonical order: Per grammar
  • Animation: Not animatable
Container Length Units Container Width & Height Units UnitNameEquivalent to…cqwContainer query width1% of the queried container’s widthcqhContainer query height1% of the queried container’s height Container Logical Directions UnitNameEquivalent to…cqiContainer query inline size1% of the queried container’s inline size, which is its width in a horizontal writing mode.cqbContainer query block size1% of the queried container’s inline size, which is its height in a horizontal writing mode. Container Minimum & Maximum Lengths UnitNameEquivalent to…cqminContainer query minimum sizeThe value of cqi or cqb, whichever is smaller.cqmaxContainer query maximum sizeThe value of cqi or cqb, whichever is larger. Container Style Queries

Container Style Queries is another piece of the CSS Container Queries puzzle. Instead of querying a container by its size or inline-size, we can query a container’s CSS styles. And when the container’s styles meet the queried condition, we can apply styles to other elements. This is the sort of “conditional” styling we’ve wanted on the web for a long time: If these styles match over here, then apply these other styles over there.

CSS Container Style Queries are only available as an experimental feature in modern web browsers at the time of this writing, and even then, style queries are only capable of evaluating CSS custom properties (i.e., variables).

Browser Support

The feature is still considered experimental at the time of this writing and is not supported by any browser, unless enabled through feature flags.

This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.

DesktopChromeFirefoxIEEdgeSafari129NoNo125TPMobile / TabletAndroid ChromeAndroid FirefoxAndroidiOS Safari125No12518.0 Registering a Style Container article { container-name: card; }

That’s really it! Actually, we don’t even need the container-name property unless we need to target it specifically. Otherwise, we can skip registering a container altogether.

And if you’re wondering why there’s no container-type declaration, that’s because all elements are already considered containers. It’s a lot like how all elements are position: relative by default; there’s no need to declare it. The only reason we would declare a container-type is if we want a CSS Container Size Query instead of a CSS Container Style Query.

So, really, there is no need to register a container style query because all elements are already style containers right out of the box! The only reason we’d declare container-name, then, is simply to help select a specific container by name when writing a style query.

Using a Style Container Query @container style(--bg-color: #000) { p { color: #fff; } }

In this example, we’re querying any matching container (because all elements are style containers by default).

Notice how the syntax it’s a lot like a traditional media query? The biggest difference is that we are writing @container instead of @media. The other difference is that we’re calling a style() function that holds the matching style condition. This way, a style query is differentiated from a size query, although there is no corresponding size() function.

In this instance, we’re checking if a certain custom property named --bg-color is set to black (#000). If the variable’s value matches that condition, then we’re setting paragraph (p) text color to white (#fff).

Custom Properties & Variables .card-wrapper { --bg-color: #000; } .card { @container style(--bg-color: #000) { /* Custom CSS */ } } Nesting Style Queries @container style(--featured: true) { article { grid-column: 1 / -1; } @container style(--theme: dark) { article { --bg-color: #000; --text: #fff; } } } Specification

CSS Container Queries are defined in the CSS Containment Module Level 3 specification, which is currently in Editor’s Draft status at the time of this writing.

Browser Support

Browser support for CSS Container Size Queries is great. It’s just style queries that are lacking support at the time of this writing.

  • Chrome 105 shipped on August 30, 2022, with support.
  • Safari 16 shipped on September 12, 2022, with support.
  • Firefox 110 shipped on February 14, 2023, with support.

This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.

DesktopChromeFirefoxIEEdgeSafari106110No10616.0Mobile / TabletAndroid ChromeAndroid FirefoxAndroidiOS Safari12512612516.0 Demos!

Many, many examples on the web demonstrate how container queries work. The following examples are not unique in that regard in that they illustrate the general concept of applying styles when a container element meets a certain condition.

You will find plenty more examples listed in the References at the end of this guide, but check out Ahmad Shadeed’s Container Queries Lab for the most complete set of examples because it also serves as a collection of clever container query use cases.

Card Component

In this example, a “card” component changes its layout based on the amount of available space in its container.

CodePen Embed Fallback Call to Action Panel

This example is a lot like those little panels for signing up for an email newsletter. Notice how the layout changes three times according to how much available space is in the container. This is what makes CSS Container Queries so powerful: you can quite literally drop this panel into any project and the layout will respond as it should, as it’s based on the space it is in rather than the size of the browser’s viewport.

CodePen Embed Fallback Stepper Component

This component displays a series of “steps” much like a timeline. In wider containers, the stepper displays steps horizontally. But if the container becomes small enough, the stepper shifts things around so that the steps are vertically stacked.

CodePen Embed Fallback Icon Button

Sometimes we like to decorate buttons with an icon to accentuate the button’s label with a little more meaning and context. And sometimes we don’t know just how wide that button will be in any given context, which makes it tough to know when exactly to hide the icon or re-arrange the button’s styles when space becomes limited. In this example, an icon is displayed to the right edge of the button as long as there’s room to fit it beside the button label. If room runs out, the button becomes a square tile that stacks the icons above the label. Notice how the border-radius is set in container query units, 4cqi, which is equal to 4% of the container’s inline-size (i.e. width) and results in rounder edges as the button grows in size.

CodePen Embed Fallback Pagination

Pagination is a great example of a component that benefits from CSS Container Queries because, depending on the amount of space we have, we can choose to display links to individual pages, or hide them in favor of only two buttons, one to paginate to older content and one to paginate to newer content.

CodePen Embed Fallback Articles & Tutorials General Information Article on Oct 4, 2022 Say Hello to CSS Container Queries Robin Rendle Article on Dec 16, 2019 The Origin Story of Container Queries Robin Rendle Article on Jun 11, 2021 A Cornucopia of Container Queries Geoff Graham Article on Apr 6, 2017 Container Query Discussion Chris Coyier Article on Jul 1, 2015 Container Queries: Once More Unto the Breach Chris Coyier Article on Aug 29, 2022 Next Gen CSS: @container Una Kravets Article on May 17, 2021 251: Container Queries are the Future Geoff Graham Article on Oct 9, 2019 Let’s Not Forget About Container Queries Chris Coyier Article on Dec 2, 2020 Minimal Takes on Faking Container Queries Chris Coyier Article on Nov 12, 2020 The Raven Technique: One Step Closer to Container Queries Mathias Hülsbusch

Container Size Query Tutorials Article on Jun 15, 2021 Media Queries in Times of @container Chris Coyier Article on Sep 21, 2022 Can We Create a “Resize Hack” With Container Queries? Jhey Tompkins Article on Dec 13, 2022 A Few Times Container Size Queries Would Have Helped Me Out Dan Christofi Article on Jan 19, 2022 A New Container Query Polyfill That Just Works Chris Coyier Article on Jun 21, 2021 256: When to use @container queries Geoff Graham Article on Sep 1, 2022 iShadeed’s Container Queries Lab Geoff Graham Article on Dec 2, 2020 Minimal Takes on Faking Container Queries Chris Coyier Article on May 4, 2020 Playing With (Fake) Container Queries With watched-box & resizeasaurus Chris Coyier

Container Style Queries Article on Nov 7, 2022 Early Days of Container Style Queries Geoff Graham Article on May 22, 2024 Digging Deeper Into Container Style Queries Geoff Graham

Almanac References Almanac on May 22, 2024 container .element { container: cards-grid / inline-size; } Geoff Graham Almanac on May 22, 2024 container-name .element { container-name: cards-grid; } Geoff Graham Almanac on May 22, 2024 container-type .element { container-type: inline-size; } Geoff Graham

Related Guides Article on May 1, 2024 A Complete Guide to CSS Media Queries Andrés Galante Article on May 23, 2024 A Complete Guide to Custom Properties Chris Coyier

References

CSS Container Queries originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Steven Heller’s Font of the Month: Moron

Typography - Wed, 06/05/2024 - 6:24pm

Read the book, Typographic Firsts

Steven Heller takes a closer look at the Moron font family from Barnbrook Fonts.

The post Steven Heller’s Font of the Month: Moron appeared first on I Love Typography.

Typography Matters

Typography - Wed, 05/22/2024 - 7:42pm

Read the book, Typographic Firsts

A review of Elliot Jay Stock's Universal Principles of Typography.

The post Typography Matters appeared first on I Love Typography.

Steven Heller’s Font of the Month: Sandhouse

Typography - Sun, 05/05/2024 - 7:00pm

Read the book, Typographic Firsts

Steven Heller takes a closer look at the Sandhouse font family from Tipastype.

The post Steven Heller’s Font of the Month: Sandhouse appeared first on I Love Typography.

Demystifying Screen Readers: Accessible Forms & Best Practices

Css Tricks - Fri, 04/19/2024 - 4:26am

This is the 3rd post in a small series we did on form accessibility. If you missed the second post, check out “Managing User Focus with :focus-visible“. In this post we are going to look at using a screen reader when navigating a form, and also some best practices.

Editor’s Note: Edits were made throughout in regard to some of the best practices and code sample additions. If you have ideas and feedback to build on this post, please let us know!

What is a Screen Reader?

You may have heard the term “screen reader” as you have been moving around the web. You might even be using a screen reader at this moment to run manual accessibility tests on the experiences you are building. A screen reader is a type of AT or assistive technology.

A screen reader converts digital text into synthesized speech or Braille output, commonly seen with a Braille reader.

https://do.co/3vQTmoW

In this example, I will be using Mac VO. Mac VO (VoiceOver) is built-in to all Mac devices; iOS, iPadOS, and macOS systems. Depending on the type of device you are running macOS on, opening VO could differ. The Macbook Pro that is running VO I am writing this on doesn’t have the touch bar, so I will be using the shortcut keys according to the hardware.

Spinning Up VO on macOS

If you are using an updated Macbook Pro, the keyboard on your machine will look something like the image below.

You will start by holding down the cmd key and then pressing the Touch ID three times quickly.

If you are on a MBP (MacBook Pro) with a TouchBar, you will use the shortcut cmd+fn+f5 to turn on VO. If you are using a traditional keyboard with your desktop or laptop, the keys should be the same or you will have to toggle VO on in the Accessibility settings.. Once VO is turned on, you will be greeted with this dialog along with a vocalized introduction to VO.

If you click the “Use VoiceOver” button you are well on your way to using VO to test your websites and apps. One thing to keep in mind is that VO is optimized for use with Safari. That being said, make sure when you are running your screen reader test that Safari is the browser you are using. That goes for the iPhone and iPad as well.

There are two main ways you can use VO from the start. The way I personally use it is by navigating to a website and using a combination of the tab, control, option, shift and arrow keys, I can navigate through the experience efficiently with these keys alone.

Another common way to navigate the experience is by using the VoiceOver Rotor. The Rotor is a feature designed to navigate directly to where you want to be in the experience. By using the Rotor, you eliminate having to traverse through the whole site, think of it as a “Choose Your Own Adventure”.

Modifier Keys

Modifier keys are the way you use the different features in VO. The default modifier key or VO is control + option but you can change it to caps lock or choose both options to use interchangeably.

Using the Rotor

In order to use the Rotor you have to use a combination of your modifier key(s) and the letter “U”. For me, my modifier key is caps lock. I press caps lock + U and the Rotor spins up for me. Once the Rotor comes up I can navigate to any part of the experience that I want using the left and right arrows.

Using the Rotor in VoiceOver Navigating By Heading Level

Another neat way to navigate the experience is by heading level. If you use the combination of your modifier keys + cmd + H you can traverse the document structure based on heading levels. You can also move back up the document by pressing shift in the sequence like so, modifier keys + shift + cmd + H.

Using the Heading Level Shortcut with VoiceOver History & Best Practices

Forms are one of the most powerful native elements we have in HTML. Whether you are searching for something on a page, submitting a form to purchase something or submit a survey. Forms are a cornerstone of the web, and were a catalyst that introduced interactivity to our experiences.

The history of the web form dates back to September 1995 when it was introduced in the HTML 2.0 spec. Some say the good ole days of the web, at least I say that. Stephanie Stimac wrote an awesome article on Smashing Magazine titled, “Standardizing Select And Beyond: The Past, Present And Future Of Native HTML Form Controls”.

In my opinion, the following are some best practices to follow when building an accessible form for the web.

  1. Labelling implicitly is 100% ok to do. Check out the https://www.w3.org/WAI/tutorials/forms/labels/ article. If the form author is unaware of the id, it can be labeled implicitly. I personally prefer the explicit way.
<!-- Implicit Label --> <label> First Name: <input type="text" name="firstname"> </label> <!-- Explicit Label --> <label for="name">Name:</label> <input type="text" id="name" name="name" required/>
  1. If a field is required in order for the form to be complete, use the required attribute. Be sure to have a visual indication that a field is required, too, because non-screenreader users need to know a field is required as well.
<input type="text" id="name" name="name" required />
  1. A button is used to invoke an action, like submitting a form. Use it! Don’t create buttons using div’s. A div is a container without semantic meaning by itself.
Demo Navigating a Web Form with VoiceOver

If you want to check out the code, navigate to the VoiceOver Demo GitHub repo. If you want to try out the demo above with your screen reader of choice, check out Navigating a Web Form with VoiceOver.

Screen Reader Software

Below is a list of various types of screen reader software you can use on your given operating system. If a Mac is not your machine of choice, there are options out there for Windows and Linux, as well as for Android devices.

NVDA

NVDA is a screen reader from NV Access. It is currently only supported on PC’s running Microsoft Windows 7 SP1 and later. For more access, check out the NVDA version 2024.1 download page on the NV Access website!

JAWS

“We need a better screen reader”

— Anonymous

If you understood the reference above, you are in good company. According to the JAWS website, this is what it is in a nutshell:

“JAWS, Job Access With Speech, is the world’s most popular screen reader, developed for computer users whose vision loss prevents them from seeing screen content or navigating with a mouse. JAWS provides speech and Braille output for the most popular computer applications on your PC. You will be able to navigate the Internet, write a document, read an email and create presentations from your office, remote desktop, or from home.”

JAWS website

Check out JAWS for yourself and if that solution fits your needs, definitely give it a shot!

Narrator

Narrator is a built-in screen reader solution that ships with WIndows 11. If you choose to use this as your screen reader of choice, the link below is for support documentation on its usage.

Complete guide to Narrator

Orca

Orca is a screen reader that can be used on different Linux distributions running GNOME.

“Orca is a free, open source, flexible, and extensible screen reader that provides access to the graphical desktop via speech and refreshable braille.

Orca works with applications and toolkits that support the Assistive Technology Service Provider Interface (AT-SPI), which is the primary assistive technology infrastructure for Linux and Solaris. Applications and toolkits supporting the AT-SPI include the GNOME Gtk+ toolkit, the Java platform’s Swing toolkit, LibreOffice, Gecko, and WebKitGtk. AT-SPI support for the KDE Qt toolkit is being pursued.”

Orca Website TalkBack

Google TalkBack is the screen reader that is used on Android devices. For more information on turning it on and using it, check out this article on the Android Accessibility Support Site.

Browser Support

If you are looking for actual browser support for HTML elements and ARIA (Accessible Rich Internet Application) attributes, I suggest caniuse.com for HTML and Accessibility Support for ARIA to get the latest 4-1-1 on browser support. Remember, if the browser doesn’t support the tech, chances are the screen reader won’t either.

DigitalA11Y can help summarize browser and screen reader info with their article,  Screen Readers and Browsers! Which is the Best Combination for Accessibility Testing?

Thanks!

Thanks to Adrian Roselli, Karl Groves, Todd Libby, Scott O’Hara, Kev Bonnett, and others for clarifications and feedback!

Links

Demystifying Screen Readers: Accessible Forms & Best Practices originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

The Music of Type Design

Typography - Mon, 04/15/2024 - 6:19pm

Read the book, Typographic Firsts

A typeface is a series of conversations happening simultaneously between different characters. For example, in the Latin script, the lowercase b talks to the d, talks to the p, talks to the q, and they respond. So there is this ongoing conversation between the b, d, p, and q, and then there is this other […]

The post The Music of Type Design appeared first on I Love Typography.

Steven Heller’s Font of the Month: Atol

Typography - Thu, 04/11/2024 - 4:00pm

Read the book, Typographic Firsts

Steven Heller takes a closer look at the Atol font family.

The post Steven Heller’s Font of the Month: Atol appeared first on I Love Typography.

Managing User Focus with :focus-visible

Css Tricks - Fri, 04/05/2024 - 12:13pm

This is going to be the 2nd post in a small series we are doing on form accessibility. If you missed the first post, check out Accessible Forms with Pseudo Classes. In this post we are going to look at :focus-visible and how to use it in your web sites!

Focus Touchpoint

Before we move forward with :focus-visible, let’s revisit how :focus works in your CSS. Focus is the visual indicator that an element is being interacted with via keyboard, mouse, trackpad, or assistive technology. Certain elements are naturally interactive, like links, buttons, and form elements. We want to make sure that our users know where they are and the interactions they are making.

Remember don’t do this in your CSS!

:focus { outline: 0; } /*** OR ***/ :focus { outline: none; }

When you remove focus, you remove it for EVERYONE! We want to make sure that we are preserving the focus.

If for any reason you do need to remove the focus, make sure there is also fallback :focus styles for your users. That fallback can match your branding colors, but make sure those colors are also accessible. If marketing, design, or branding doesn’t like the default focus ring styles, then it is time to start having conversations and collaborate with them on the best way of adding it back in.

What is focus-visible?

The pseudo class, :focus-visible, is just like our default :focus pseudo class. It gives the user an indicator that something is being focused on the page. The way you write :focus-visible is cut and dry:

:focus-visible { /* ... */ }

When using :focus-visible with a specific element, the syntax looks something like this:

.your-element:focus-visible { /*...*/ }

The great thing about using :focus-visible is you can make your element stand out, bright and bold! No need to worry about it showing if the element is clicked/tapped. If you choose not to implement the class, the default will be the user agent focus ring which to some is undesirable.

Backstory of focus-visible

Before we had the :focus-visible, the user agent styling would apply :focus to most elements on the page; buttons, links, etc. It would apply an outline or “focus ring” to the focusable element. This was deemed to be ugly, most didn’t like the default focus ring the browser provided. As a result of the focus ring being unfavorable to look at, most authors removed it… without a fallback. Remember, when you remove :focus, it decreases usability and makes the experience inaccessible for keyboard users.

In the current state of the web, the browser no longer visibly indicates focus around various elements when they have focus. The browser instead uses varying heuristics to determine when it would help the user, providing a focus ring in return. According to Khan Academy, a heuristic is, “a technique that guides an algorithm to find good choices.”

What this means is that the browser can detect whether or not the user is interacting with the experience from a keyboard, mouse, or trackpad and based on that input type, it adds or removes the focus ring. The example in this post highlights the input interaction.

In the early days of :focus-visible we were using a polyfill to handle the focus ring created by Alice Boxhall and Brian Kardell, Mozilla also came out with their own pseudo class, :moz-focusring, before the official specification. If you want to learn more about the early days of the focus-ring, check out A11y Casts with Rob Dodson.

Focus Importance

There are plenty of reasons why focus is important in your application. For one, like I stated above, we as ambassadors of the web have to make sure we are providing the best, accessible experience we can. We don’t want any of our users guessing where they are while they are navigation through the experience.

One example that always comes to mind is the Two Blind Brothers website. If you go to the website and click/tap (this works on mobile), the closed eye in the bottom left corner, you will see the eye open and a simulation begins. Both the brothers, Bradford and Bryan Manning, were diagnosed at a young age with Stargardt’s Disease. Stargardt’s disease is a form of macular degeneration of the eye. Over time both brothers will be completely blind. Visit the site and click the eye to see how they see.

If you were in their shoes and you had to navigate through a page, you would want to make sure you knew exactly where you were throughout the whole experience. A focus ring gives you that power.

Demo

The demo below shows how :focus-visible works when added to your CSS. The first part of the video shows the experience when navigating through with a mouse the second shows navigating through with just my keyboard. I recorded myself as well to show that I did switch from using my mouse, to my keyboard.

Video showing how the heuristics of the browser works based on input and triggering the focus visible pseudo class.

The browser is predicting what to do with the focus ring based on my input (keyboard/mouse), and then adding a focus ring to those elements. In this case, when I am navigating through this example with the keyboard, everything receives focus. When using the mouse, only the input gets focus and the buttons don’t. If you remove :focus-visible, the browser will apply the default focus ring.

The code below is applying :focus-visible to the focusable elements.

:focus-visible { outline-color: black; font-size: 1.2em; font-family: serif; font-weight: bold; }

If you want to specify the label or the button to receive :focus-visible just prepend the class with input or button respectively.

button:focus-visible { outline-color: black; font-size: 1.2em; font-family: serif; font-weight: bold; } /*** OR ***/ input:focus-visible { outline-color: black; font-size: 1.2em; font-family: serif; font-weight: bold; } Support

If the browser does not support :focus-visible you can have a fall back in place to handle the interaction. The code below is from the MDN Playground. You can use the @supports at-rule or “feature query” to check support. One thing to keep in mind, the rule should be placed at the top of the code or nested inside another group at-rule.

<button class="button with-fallback" type="button">Button with fallback</button> <button class="button without-fallback" type="button">Button without fallback</button> .button { margin: 10px; border: 2px solid darkgray; border-radius: 4px; } .button:focus-visible { /* Draw the focus when :focus-visible is supported */ outline: 3px solid deepskyblue; outline-offset: 3px; } @supports not selector(:focus-visible) { .button.with-fallback:focus { /* Fallback for browsers without :focus-visible support */ outline: 3px solid deepskyblue; outline-offset: 3px; } } Further Accessibility Concerns

Accessibility concerns to keep in mind when building out your experience:

  • Make sure the colors you choose for your focus indicator, if at all, are still accessible according to the information documented in the WCAG 2.2 Non-text Contrast (Level AA)
  • Cognitive overload can cause a user distress. Make sure to keep styles on varying interactive elements consistent
Browser Support

This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.

DesktopChromeFirefoxIEEdgeSafari864*No8615.4Mobile / TabletAndroid ChromeAndroid FirefoxAndroidiOS Safari12512612515.4 Links

Managing User Focus with :focus-visible originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

The Power of :has() in CSS

Css Tricks - Fri, 03/29/2024 - 4:07pm

Hey all you wonderful developers out there! In this post we are going to explore the use of :has() in your next web project. :has() is relatively newish but has gained popularity in the front end community by delivering control over various elements in your UI. Let’s take a look at what the pseudo class is and how we can utilize it.

Syntax

The :has() CSS pseudo-class helps style an element if any of the things we’re searching for inside it are found and accounted for. It’s like saying, “If there’s something specific inside this box, then style the box this way AND only this way.”

:has(<direct-selector>) { /* ... */ }

“The functional :has() CSS pseudo-class represents an element if any of the relative selectors that are passed as an argument match at least one element when anchored against this element. This pseudo-class presents a way of selecting a parent element or a previous sibling element with respect to a reference element by taking a relative selector list as an argument.”

For a more robust explanation, MDN does it perfectly The Styling Problem

In years past we had no way of styling a parent element based on a direct child of that parent with CSS or an element based on another element. In the chance we had to do that, we would need to use some JavaScript and toggle classes on/off based on the structure of the HTML. :has() solved that problem.


Let’s say that you have a heading level 1 element (h1) that is the title of a post or something of that nature on a blog list page, and then you have a heading level 2 (h2) that directly follows it. This h2 could be a sub-heading for the post. If that h2 is present, important, and directly after the h1, you might want to make that h1 stand out. Before you would have had to write a JS function.

Old School Way – JavaScript const h1Elements = document.querySelectorAll('h1'); h1Elements.forEach((h1) => { const h2Sibling = h1.nextElementSibling; if (h2Sibling && h2Sibling.tagName.toLowerCase() === 'h2') { h1.classList.add('highlight-content'); } });

This JS function is looking for all the h1’s that have a h2 proceeding it, and applying a class of highlight-content to make the h1 stand out as an important article.

New and improved with modern day CSS coming in hot! The capabilities of what we can do in the browser have come a long way. We now can take advantage of CSS to do things that we traditionally would have to do with JavaScript, not everything, but some things.

New School Way – CSS h1:has(+ h2) { color: blue; } Throw Some :has() On It!

Now you can use :has() to achieve the same thing that the JS function did. This CSS is checking for any h1 and using the sibling combinator checking for an h2 that immediately follows it, and adds the color of blue to the text. Below are a couple use cases of when :has() can come in handy.

:has Selector Example 1 HTML <h1>Lorem, ipsum dolor.</h1> <h2>Lorem ipsum dolor sit amet.</h2> <p>Lorem, ipsum dolor sit amet consectetur adipisicing elit. Eius, odio voluptatibus est vero iste ad?</p> <!-- WITHOUT HAS BELOW --> <h1>This is a test</h1> <p>Lorem, ipsum dolor sit amet consectetur adipisicing elit. Eius, odio voluptatibus est vero iste ad?</p> CSS h1:has(+ h2) { color: blue; } :has Selector Example 2

A lot of times we as workers on the web are manipulating or working with images. We could be using tools that Cloudinary provides to make use of various transformations on our images, but usually we want to add drop shadows, border-radii, and captions (not to be confused with alternative text in an alt attribute).


The example below is using :has() to see if a figure or image has a figcaption element and if it does, it applies some background and a border radius to make the image stand out.

HTML <section> <figure> <img src="https://placedog.net/500/280" alt="My aunt sally's dog is a golden retreiver." /> <figcaption>My Aunt Sally's Doggo</figcaption> </figure> </section> CSS figure:has(figcaption) { background: #c3baba; padding: 0.6rem; max-width: 50%; border-radius: 5px; } Can I :has() that?

You can see that :has() has great support across modern browsers.

This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.

DesktopChromeFirefoxIEEdgeSafari105121No10515.4Mobile / TabletAndroid ChromeAndroid FirefoxAndroidiOS Safari12512612515.4 :has() in the Community!

I reached out to my network on Twitter to see how my peers were using :has() in their day-to-day work and this is what they had to say about it.

“One example I have is styling a specific SVG from a 3rd party package in @saucedopen because I couldn’t style it directly.”

This is what Nick Taylor from OpenSauced had to say about using :has(). svg:has(> #Mail) { stroke-width: 1; }

Lol the last time I used it I was building keyboard functionality into a tree view, so I needed to detect states and classes of sibling elements, but it wasn’t in Firefox yet so I had to find another solution. &#x1fae0;

Abbey Perini from Nexcor Food Safety Technologies, Inc.

It is great to see how community members are using modern CSS to solve real world problems, and also a shout out to Abbey using it for accessibility reasons!

Things to Keep in Mind

There are a few key points to keep in mind when using :has() Bullet points referenced from MDN.

  • The pseudo-class takes on specificity of the most specific selector in its argument
  • If the :has() pseudo-class itself is not supported in a browser, the entire selector block will fail unless :has() is in a forgiving selector list, such as in :is() and :where()
  • The :has() pseudo-class cannot be nested within another :has() 
  • Pseudo-elements are also not valid selectors within :has() and pseudo-elements are not valid anchors for :has()
Conclusion

Harnessing the power of CSS, including advanced features like the :has() pseudo-class, empowers us to craft exceptional web experiences. CSS’s strengths lie in its cascade and specificity…the best part, allowing us to leverage its full potential. By embracing the capabilities of CSS, we can drive web design and development forward, unlocking new possibilities and creating groundbreaking user interfaces.

Links:

The Power of :has() in CSS originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Accessible Forms with Pseudo Classes

Css Tricks - Fri, 03/22/2024 - 8:52am

Hey all you wonderful developers out there! In this post, I am going to take you through creating a simple contact form using semantic HTML and an awesome CSS pseudo class known as :focus-within. The :focus-within class allows for great control over focus and letting your user know this is exactly where they are in the experience. Before we jump in, let’s get to the core of what web accessibility is.


Form Accessibility?


You have most likely heard the term “accessibility” everywhere or the numeronym, a11y. What does it mean? That is a great question with so many answers. When we look at the physical world, accessibility means things like having sharps containers in your bathrooms at your business, making sure there are ramps for wheel assisted people, and having peripherals like large print keyboards on hand for anyone that needs it.

The gamut of accessibility doesn’t stop there, we have digital accessibility that we need to be cognizant of as well, not just for external users, but internal colleagues as well. Color contrast is a low hanging fruit that we should be able to nip in the bud. At our workplaces, making sure that if any employee needs assistive tech like a screen reader, we have that installed and available. There are a lot of things that need to be kept into consideration. This article will focus on web accessibility by keeping the WCAG (web content accessibility guidelines) in mind.

MDN (Mozilla Developer Network)

The :focus-within CSS pseudo-class matches an element if the element or any of its descendants are focused. In other words, it represents an element that is itself matched by the :focus pseudo-class or has a descendant that is matched by :focus. (This includes descendants in shadow trees.)

This pseudo class is really great when you want to emphasize that the user is in fact interacting with the element. You can change the background color of the whole form, for example. Or, if focus is moved into an input, you can make the label bold and larger of an input element when focus is moved into that input. What is happening below in the code snippets and examples is what is making the form accessible. :focus-within is just one way we can use CSS to our advantage.

How To Focus


Focus, in regards to accessibility and the web experience, is the visual indicator that something is being interacted with on the page, in the UI, or within a component. CSS can tell when an interactive element is focused.

“The :focus CSS pseudo-class represents an element (such as a form input) that has received focus. It is generally triggered when the user clicks or taps on an element or selects it with the keyboard’s Tab key.”

MDN (Mozilla Developer Network)

Always make sure that the focus indicator or the ring around focusable elements maintains the proper color contrast through the experience.

Focus is written like this and can be styled to match your branding if you choose to style it.

:focus { * / INSERT STYLES HERE /* }

Whatever you do, never set your outline to 0 or none. Doing so will remove a visible focus indicator for everyone across the whole experience. If you need to remove focus, you can, but make sure to add that back in later. When you remove focus from your CSS or set the outline to 0 or none, it removes the focus ring for all your users. This is seen a lot when using a CSS reset. A CSS reset will reset the styles to a blank canvas. This way you are in charge of the empty canvas to style as you wish. If you wish to use a CSS reset, check out Josh Comeau’s reset.

*DO NOT DO what is below!

:focus { outline: 0; } :focus { outline: none; }
Look Within!


One of the coolest ways to style focus using CSS is what this article is all about. If you haven’t checked out the :focus-within pseudo class, definitely give that a look! There are a lot of hidden gems when it comes to using semantic markup and CSS, and this is one of them. A lot of things that are overlooked are accessible by default, for instance, semantic markup is by default accessible and should be used over div’s at all times.

<header> <h1>Semantic Markup</h1> <nav> <ul> <li><a href="/">Home</a></li> <li><a href="/about">About</a></li> </ul> </nav> </header> <section><!-- Code goes here --></section> <section><!-- Code goes here --></section> <aside><!-- Code goes here --></aside> <footer><!-- Code goes here --></footer>

The header, nav, main, section, aside, and footer are all semantic elements. The h1 and ul are also semantic and accessible.

Unless there is a custom component that needs to be created, then a div is fine to use, paired with ARIA (Accessible Rich Internet Applications). We can do a deep dive into ARIA in a later post. For now let’s focus…see what I did there…on this CSS pseudo class.

The :focus-within pseudo class allows you to select an element when any descendent element it contains has focus.


:focus-within in Action!
HTML <form> <div> <label for="firstName">First Name</label><input id="firstName" type="text"> </div> <div> <label for="lastName">Last Name</label><input id="lastName" type="text"> </div> <div> <label for="phone">Phone Number</label><input id="phone" type="text"> </div> <div> <label for="message">Message</label><textarea id="message"></textarea> </div> </form> CSS form:focus-within { background: #ff7300; color: black; padding: 10px; }

The example code above will add a background color of orange, add some padding, and change the color of the labels to black.

The final product looks something like below. Of course the possibilities are endless to change up the styling, but this should get you on a good track to make the web more accessible for everyone!

Another use case for using :focus-within would be turning the labels bold, a different color, or enlarging them for users with low vision. The example code for that would look something like below.

HTML <form> <h1>:focus-within part 2!</h1> <label for="firstName">First Name: <input name="firstName" type="text" /></label> <label for="lastName">Last Name: <input name="lastName" type="text" /></label> <label for="phone">Phone number: <input type="tel" id="phone" /></label> <label for="message">Message: <textarea name="message" id="message"/></textarea></label> </form> CSS label { display: block; margin-right: 10px; padding-bottom: 15px; } label:focus-within { font-weight: bold; color: red; font-size: 1.6em; }

:focus-within also has great browser support across the board according to Can I use.

Conclusion

Creating amazing, accessible user experience should always be a top priority when shipping software, not just externally but internally as well. We as developers, all the way up to senior leadership need to be cognizant of the challenges others face and how we can be ambassadors for the web platform to make it a better place.

Using technology like semantic markup and CSS to create inclusive spaces is a crucial part in making the web a better place, let’s continue moving forward and changing lives.

Check out another great resource here on CSS-Tricks on using :focus-within.

Accessible Forms with Pseudo Classes originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

New business wanted

QuirksBlog - Thu, 09/30/2021 - 12:22am

Last week Krijn and I decided to cancel performance.now() 2021. Although it was the right decision it leaves me in financially fairly dire straits. So I’m looking for new jobs and/or donations.

Even though the Corona trends in NL look good, and we could probably have brought 350 people together in November, we cannot be certain: there might be a new flare-up. More serious is the fact that it’s very hard to figure out how to apply the Corona checks Dutch government requires, especially for non-EU citizens. We couldn’t figure out how UK and US people should be tested, and for us that was the straw that broke the camel’s back. Cancelling the conference relieved us of a lot of stress.

Still, it also relieved me of a lot of money. This is the fourth conference in a row we cannot run, and I have burned through all my reserves. That’s why I thought I’d ask for help.

So ...

Has QuirksMode.org ever saved you a lot of time on a project? Did it advance your career? If so, now would be a great time to make a donation to show your appreciation.

I am trying my hand at CSS coaching. Though I had only few clients so far I found that I like it and would like to do it more. As an added bonus, because I’m still writing my CSS for JavaScripters book I currently have most of the CSS layout modules in my head and can explain them straight away — even stacking contexts.

Or if there’s any job you know of that requires a technical documentation writer with a solid knowledge of web technologies and the browser market, drop me a line. I’m interested.

Anyway, thanks for listening.

position: sticky, draft 1

QuirksBlog - Wed, 09/08/2021 - 7:44am

I’m writing the position: sticky part of my book, and since I never worked with sticky before I’m not totally sure if what I’m saying is correct.

This is made worse by the fact that there are no very clear tutorials on sticky. That’s partly because it works pretty intuitively in most cases, and partly because the details can be complicated.

So here’s my draft 1 of position: sticky. There will be something wrong with it; please correct me where needed.

The inset properties are top, right, bottom and left. (I already introduced this terminology earlier in the chapter.)

h3,h4,pre {clear: left} section.scroll-container { border: 1px solid black; width: 300px; height: 250px; padding: 1em; overflow: auto; --text: 'scroll box'; float: left; clear: left; margin-right: 0.5em; margin-bottom: 1em; position: relative; font-size: 1.3rem; } .container,.outer-container { border: 1px solid black; padding: 1em; position: relative; --text: 'container'; } .outer-container { --text: 'outer container'; } :is(.scroll-container,.container,.outer-container):before { position: absolute; content: var(--text); top: 0.2em; left: 0.2em; font-size: 0.8rem; } section.scroll-container h2 { position: sticky; top: 0; background: white; margin: 0 !important; color: inherit !important; padding: 0.5em !important; border: 1px solid; font-size: 1.4rem !important; } .nowrap p { white-space: nowrap; } Introduction

position: sticky is a mix of relative and fixed. A sticky box takes its normal position in the flow, as if it had position: relative, but if that position scrolls out of view the sticky box remains in a position defined by its inset properties, as if it has position: fixed. A sticky box never escapes its container, though. If the container start or end scrolls past the sticky box abandons its fixed position and sticks to the top or the bottom of its container.

It is typically used to make sure that headers remain in view no matter how the user scrolls. It is also useful for tables on narrow screens: you can keep headers or the leftmost table cells in view while the user scrolls.

Scroll box and container

A sticky box needs a scroll box: a box that is able to scroll. By default this is the browser window — or, more correctly, the layout viewport — but you can define another scroll box by setting overflow on the desired element. The sticky box takes the first ancestor that could scroll as its scroll box and calculates all its coordinates relative to it.

A sticky box needs at least one inset property. These properties contain vital instructions, and if the sticky box doesn’t receive them it doesn’t know what to do.

A sticky box may also have a container: a regular HTML element that contains the sticky box. The sticky box will never be positioned outside this container, which thus serves as a constraint.

The first example shows this set-up. The sticky <h2> is in a perfectly normal <div>, its container, and that container is in a <section> that is the scroll box because it has overflow: auto. The sticky box has an inset property to provide instructions. The relevant styles are:

section.scroll-container { border: 1px solid black; width: 300px; height: 300px; overflow: auto; padding: 1em; } div.container { border: 1px solid black; padding: 1em; } section.scroll-container h2 { position: sticky; top: 0; } The rules Sticky header

Regular content

Regular content

Regular content

Regular content

Regular content

Regular content

Regular content

Content outside container

Content outside container

Content outside container

Content outside container

Content outside container

Content outside container

Now let’s see exactly what’s going on.

A sticky box never escapes its containing box. If it cannot obey the rules that follow without escaping from its container, it instead remains at the edge. Scroll down until the container disappears to see this in action.

A sticky box starts in its natural position in the flow, as if it has position: relative. It thus participates in the default flow: if it becomes higher it pushes the paragraphs below it downwards, just like any other regular HTML element. Also, the space it takes in the normal flow is kept open, even if it is currently in fixed position. Scroll down a little bit to see this in action: an empty space is kept open for the header.

A sticky box compares two positions: its natural position in the flow and its fixed position according to its inset properties. It does so in the coordinate frame of its scroll box. That is, any given coordinate such as top: 20px, as well as its default coordinates, is resolved against the content box of the scroll box. (In other words, the scroll box’s padding also constrains the sticky box; it will never move up into that padding.)

A sticky box with top takes the higher value of its top and its natural position in the flow, and positions its top border at that value. Scroll down slowly to see this in action: the sticky box starts at its natural position (let’s call it 20px), which is higher than its defined top (0). Thus it rests at its position in the natural flow. Scrolling up a few pixels doesn’t change this, but once its natural position becomes less than 0, the sticky box switches to a fixed layout and stays at that position.

The sticky box has bottom: 0

Regular content

Regular content

Regular content

Regular content

Regular content

Regular content

Sticky header

Content outside container

Content outside container

Content outside container

Content outside container

Content outside container

Content outside container

It does the same for bottom, but remember that a bottom is calculated relative to the scroll box’s bottom, and not its top. Thus, a larger bottom coordinate means the box is positioned more to the top. Now the sticky box compares its default bottom with the defined bottom and uses the higher value to position its bottom border, just as before.

With left, it uses the higher value of its natural position and to position its left border; with right, it does the same for its right border, bearing in mind once more that a higher right value positions the box more to the left.

If any of these steps would position the sticky box outside its containing box it takes the position that just barely keeps it within its containing box.

Details Sticky header

Very, very long line of content to stretch up the container quite a bit

Regular content

Regular content

Regular content

Regular content

Regular content

Regular content

Content outside container

Content outside container

Content outside container

Content outside container

Content outside container

Content outside container

Content outside container

The four inset properties act independently of one another. For instance the following box will calculate the position of its top and left edge independently. They can be relative or fixed, depending on how the user scrolls.

p.testbox { position: sticky; top: 0; left: 0; }

Content outside container

Content outside container

Content outside container

Content outside container

Content outside container

The sticky box has top: 0; bottom: 0

Regular content

Regular content

Regular content

Regular content

Sticky header

Regular content

Regular content

Regular content

Regular content

Regular content

Content outside container

Content outside container

Content outside container

Content outside container

Content outside container

Setting both a top and a bottom, or both a left and a right, gives the sticky box a bandwidth to move in. It will always attempt to obey all the rules described above. So the following box will vary between 0 from the top of the screen to 0 from the bottom, taking its default position in the flow between these two positions.

p.testbox { position: sticky; top: 0; bottom: 0; } No container

Regular content

Regular content

Sticky header

Regular content

Regular content

Regular content

Regular content

Regular content

Regular content

Regular content

Regular content

Regular content

So far we put the sticky box in a container separate from the scroll box. But that’s not necessary. You can also make the scroll box itself the container if you wish. The sticky element is still positioned with respect to the scroll box (which is now also its container) and everything works fine.

Several containers Sticky header

Regular content

Regular content

Regular content

Regular content

Regular content

Regular content

Regular content

Content outside container

Content outside container

Content outside outer container

Content outside outer container

Or the sticky item can be several containers removed from its scroll box. That’s fine as well; the positions are still calculated relative to the scroll box, and the sticky box will never leave its innermost container.

Changing the scroll box Sticky header

The container has overflow: auto.

Regular content

Regular content

Regular content

Regular content

Regular content

Regular content

Content outside container

Content outside container

Content outside container

One feature that catches many people (including me) unaware is giving the container an overflow: auto or hidden. All of a sudden it seems the sticky header doesn’t work any more.

What’s going on here? An overflow value of auto, hidden, or scroll makes an element into a scroll box. So now the sticky box’s scroll box is no longer the outer element, but the inner one, since that is now the closest ancestor that is able to scroll.

The sticky box appears to be static, but it isn’t. The crux here is that the scroll box could scroll, thanks to its overflow value, but doesn’t actually do so because we didn’t give it a height, and therefore it stretches up to accomodate all of its contents.

Thus we have a non-scrolling scroll box, and that is the root cause of our problems.

As before, the sticky box calculates its position by comparing its natural position relative to its scroll box with the one given by its inset properties. Point is: the sticky box doesn’t scroll relative to its scroll box, so its position always remains the same. Where in earlier examples the position of the sticky element relative to the scroll box changed when we scrolled, it no longer does so, because the scroll box doesn’t scroll. Thus there is no reason for it to switch to fixed positioning, and it stays where it is relative to its scroll box.

The fact that the scroll box itself scrolls upward is irrelevant; this doesn’t influence the sticky box in the slightest.

Sticky header

Regular content

Regular content

Regular content

Regular content

Regular content

Regular content

Regular content

Content outside container

Content outside container

Content outside container

Content outside container

Content outside container

Content outside container

One solution is to give the new scroll box a height that is too little for its contents. Now the scroll box generates a scrollbar and becomes a scrolling scroll box. When we scroll it the position of the sticky box relative to its scroll box changes once more, and it switches from fixed to relative or vice versa as required.

Minor items

Finally a few minor items:

  • It is no longer necessary to use position: -webkit-sticky. All modern browsers support regular position: sticky. (But if you need to cater to a few older browsers, retaining the double syntax doesn’t hurt.)
  • Chrome (Mac) does weird things to the borders of the sticky items in these examples. I don’t know what’s going on and am not going to investigate.

Breaking the web forward

QuirksBlog - Thu, 08/12/2021 - 5:19am

Safari is holding back the web. It is the new IE, after all. In contrast, Chrome is pushing the web forward so hard that it’s starting to break. Meanwhile web developers do nothing except moan and complain. The only thing left to do is to pick our poison.

blockquote { font-size: inherit; font-family: inherit; } blockquote p { font-size: inherit; font-family: inherit; } Safari is the new IE

Recently there was yet another round of “Safari is the new IE” stories. Once Jeremy’s summary and a short discussion cleared my mind I finally figured out that Safari is not IE, and that Safari’s IE-or-not-IE is not the worst problem the web is facing.

Perry Sun argues that for developers, Safari is crap and outdated, emulating the old IE of fifteen years ago in this respect. He also repeats the theory that Apple is deliberately starving Safari of features in order to protect the app store, and thus its bottom line. We’ll get back to that.

The allegation that Safari is holding back web development by its lack of support for key features is not new, but it’s not true, either. Back fifteen years ago IE held back the web because web developers had to cater to its outdated technology stack. “Best viewed with IE” and all that. But do you ever see a “Best viewed with Safari” notice? No, you don’t. Another browser takes that special place in web developers’ hearts and minds.

Chrome is the new IE, but in reverse

Jorge Arango fears we’re going back to the bad old days with “Best viewed in Chrome.” Chris Krycho reinforces this by pointing out that, even though Chrome is not the standard, it’s treated as such by many web developers.

“Best viewed in Chrome” squares very badly with “Safari is the new IE.” Safari’s sad state does not force web developers to restrict themselves to Safari-supported features, so it does not hold the same position as IE.

So I propose to lay this tired old meme to rest. Safari is not the new IE. If anything it’s the new Netscape 4.

Meanwhile it is Chrome that is the new IE, but in reverse.

Break the web forward

Back in the day, IE was accused of an embrace, extend, and extinguish strategy. After IE6 Microsoft did nothing for ages, assuming it had won the web. Thanks to web developers taking action in their own name for the first (and only) time, IE was updated once more and the web moved forward again.

Google learned from Microsoft’s mistakes and follows a novel embrace, extend, and extinguish strategy by breaking the web and stomping on the bits. Who cares if it breaks as long as we go forward. And to hell with backward compatibility.

Back in 2015 I proposed to stop pushing the web forward, and as expected the Chrome devrels were especially outraged at this idea. It never went anywhere. (Truth to tell: I hadn’t expected it to.)

I still think we should stop pushing the web forward for a while until we figure out where we want to push the web forward to — but as long as Google is in charge that won’t happen. It will only get worse.

On alert

A blog storm broke out over the decision to remove alert(), confirm() and prompt(), first only the cross-origin variants, but eventually all of them. Jeremy and Chris Coyier already summarised the situation, while Rich Harris discusses the uses of the three ancient modals, especially when it comes to learning JavaScript.

With all these articles already written I will only note that, if the three ancient modals are truly as horrendous a security issue as Google says they are it took everyone a bloody long time to figure that out. I mean, they turn 25 this year.

Although it appears Firefox and Safari are on board with at least the cross-origin part of the proposal, there is no doubt that it’s Google that leads the charge.

From Google’s perspective the ancient modals have one crucial flaw quite apart from their security model: they weren’t invented there. That’s why they have to be replaced by — I don’t know what, but it will likely be a very complicated API.

Complex systems and arrogant priests rule the web

Thus the new embrace, extend, and extinguish is breaking backward compatibility in order to make the web more complicated. Nolan Lawson puts it like this:

we end up with convoluted specs like Service Worker that you need a PhD to understand, and yet we still don't have a working <dialog> element.

In addition, Google can be pretty arrogant and condescending, as Chris Ferdinandi points out.

The condescending “did you actually read it, it’s so clear” refrain is patronizing AF. It’s the equivalent of “just” or “simply” in developer documentation.

I read it. I didn’t understand it. That’s why I asked someone whose literal job is communicating with developers about changes Chrome makes to the platform.

This is not isolated to one developer at Chrome. The entire message thread where this change was surfaced is filled with folks begging Chrome not to move forward with this proposal because it will break all-the-things.

If you write documentation or a technical article and nobody understands it, you’ve done a crappy job. I should know; I’ve been writing this stuff for twenty years.

Extend, embrace, extinguish. And use lots of difficult words.

Patience is a virtue

As a reaction to web dev outcry Google temporarily halted the breaking of the web. That sounds great but really isn’t. It’s just a clever tactical move.

I saw this tactic in action before. Back in early 2016 Google tried to break the de-facto standard for the mobile visual viewport that I worked very hard to establish. I wrote a piece that resonated with web developers, whose complaints made Google abandon the plan — temporarily. They tried again in late 2017, and I again wrote an article, but this time around nobody cared and the changes took effect and backward compatibility was broken.

So the three ancient modals still have about 12 to 18 months to live. Somewhere in late 2022 to early 2023 Google will try again, web developers will be silent, and the modals will be gone.

The pursuit of appiness

But why is Google breaking the web forward at such a pace? And why is Apple holding it back?

Safari is kept dumb to protect the app store and thus revenue. In contrast, the Chrome team is pushing very hard to port every single app functionality to the browser. Ages ago I argued we should give up on this, but of course no one listened.

When performing Valley Kremlinology, it is useful to see Google policies as stemming from a conflict between internal pro-web and anti-web factions. We web developers mainly deal with the pro-web faction, the Chrome devrel and browser teams. On the other hand, the Android team is squarely in the anti-web camp.

When seen in this light the pro-web camp’s insistence on copying everything appy makes excellent sense: if they didn’t Chrome would lag behind apps and the Android anti-web camp would gain too much power. While I prefer the pro-web over the anti-web camp, I would even more prefer the web not to be a pawn in an internal Google power struggle. But it has come to that, no doubt about it.

Solutions?

Is there any good solution? Not really.

Jim Nielsen feels that part of the issue is the lack of representation of web developers in the standardization process. That sounds great but is proven not to work.

Three years ago Fronteers and I attempted to get web developers represented and were met with absolute disinterest. Nobody else cared even one shit, and the initiative sank like a stone.

So a hypothetical web dev representative in W3C is not going to work. Also, the organisational work would involve a lot of unpaid labour, and I, for one, am not willing to do it again. Neither is anyone else. So this is not the solution.

And what about Firefox? Well, what about it? Ten years ago it made a disastrous mistake by ignoring the mobile web for way too long, then it attempted an arrogant and uninformed come-back with Firefox OS that failed, and its history from that point on is one long slide into obscurity. That’s what you get with shitty management.

Pick your poison

So Safari is trying to slow the web down. With Google’s move-fast-break-absofuckinglutely-everything axiom in mind, is Safari’s approach so bad?

Regardless of where you feel the web should be on this spectrum between Google and Apple, there is a fundamental difference between the two.

We have the tools and procedures to manage Safari’s disinterest. They’re essentially the same as the ones we deployed against Microsoft back in the day — though a fundamental difference is that Microsoft was willing to talk while Apple remains its old haughty self, and its “devrels” aren’t actually allowed to do devrelly things such as managing relations with web developers. (Don’t blame them, by the way. If something would ever change they’re going to be our most valuable internal allies — just as the IE team was back in the day.)

On the other hand, we have no process for countering Google’s reverse embrace, extend, and extinguish strategy, since a section of web devs will be enthusiastic about whatever the newest API is. Also, Google devrels talk. And talk. And talk. And provide gigs of data that are hard to make sense of. And refer to their proprietary algorithms that “clearly” show X is in the best interest of the web — and don’t ask questions! And make everything so fucking complicated that we eventually give up and give in.

So pick your poison. Shall we push the web forward until it’s broken, or shall we break it by inaction? What will it be? Privately, my money is on Google. So we should say goodbye to the old web while we still can.

Custom properties and @property

QuirksBlog - Wed, 07/21/2021 - 3:18am

You’re reading a failed article. I hoped to write about @property and how it is useful for extending CSS inheritance considerably in many different circumstances. Alas, I failed. @property turns out to be very useful for font sizes, but does not even approach the general applicability I hoped for.

Grandparent-inheriting

It all started when I commented on what I thought was an interesting but theoretical idea by Lea Verou: what if elements could inherit the font size of not their parent, but their grandparent? Something like this:

div.grandparent { /* font-size could be anything */ } div.parent { font-size: 0.4em; } div.child { font-size: [inherit from grandparent in some sort of way]; font-size: [yes, you could do 2.5em to restore the grandparent's font size]; font-size: [but that's not inheriting, it's just reversing a calculation]; font-size: [and it will not work if the parent's font size is also unknown]; }

Lea told me this wasn’t a vague idea, but something that can be done right now. I was quite surprised — and I assume many of my readers are as well — and asked for more information. So she wrote Inherit ancestor font-size, for fun and profit, where she explained how the new Houdini @property can be used to do this.

This was seriously cool. Also, I picked up a few interesting bits about how CSS custom properties and Houdini @property work. I decided to explain these tricky bits in simple terms — mostly because I know that by writing an explanation I myself will understand them better — and to suggest other possibilities for using Lea’s idea.

Alas, that last objective is where I failed. Lea’s idea can only be used for font sizes. That’s an important use case, but I had hoped for more. The reasons why it doesn’t work elsewhere are instructive, though.

Tokens and values

Let’s consider CSS custom properties. What if we store the grandparent’s font size in a custom property and use that in the child?

div.grandparent { /* font-size could be anything */ --myFontSize: 1em; } div.parent { font-size: 0.4em; } div.child { font-size: var(--myFontSize); /* hey, that's the grandparent's font size, isn't it? */ }

This does not work. The child will have the same font size as the parent, and ignore the grandparent. In order to understand why we need to understand how custom properties work. What does this line of CSS do?

--myFontSize: 1em;

It sets a custom property that we can use later. Well duh.

Sure. But what value does this custom property have?

... errr ... 1em?

Nope. The answer is: none. That’s why the code example doesn’t work.

When they are defined, custom properties do not have a value or a type. All that you ordered the browsers to do is to store a token in the variable --myFontSize.

This took me a while to wrap my head around, so let’s go a bit deeper. What is a token? Let’s briefly switch to JavaScript to explain.

let myVar = 10;

What’s the value of myVar in this line? I do not mean: what value is stored in the variable myVar, but: what value does the character sequence myVar have in that line of code? And what type?

Well, none. Duh. It’s not a variable or value, it’s just a token that the JavaScript engine interprets as “allow me to access and change a specific variable” whenever you type it.

CSS custom properties also hold such tokens. They do not have any intrinsic meaning. Instead, they acquire meaning when they are interpreted by the CSS engine in a certain context, just as the myVar token is in the JavaScript example.

So the CSS custom property contains the token 1em without any value, without any type, without any meaning — as yet.

You can use pretty any bunch of characters in a custom property definition. Browsers make no assumptions about their validity or usefulness because they don’t yet know what you want to do with the token. So this, too, is a perfectly fine CSS custom property:

--myEgoTrip: ppk;

Browsers shrug, create the custom property, and store the indicated token. The fact that ppk is invalid in all CSS contexts is irrelevant: we haven’t tried to use it yet.

It’s when you actually use the custom property that values and types are assigned. So let’s use it:

background-color: var(--myEgoTrip);

Now the CSS parser takes the tokens we defined earlier and replaces the custom property with them:

background-color: ppk;

And only NOW the tokens are read and intrepreted. In this case that results in an error: ppk is not a valid value for background-color. So the CSS declaration as a whole is invalid and nothing happens — well, technically it gets the unset value, but the net result is the same. The custom property itself is still perfectly valid, though.

The same happens in our original code example:

div.grandparent { /* font-size could be anything */ --myFontSize: 1em; /* just a token; no value, no meaning */ } div.parent { font-size: 0.4em; } div.child { font-size: var(--myFontSize); /* becomes */ font-size: 1em; /* hey, this is valid CSS! */ /* Right, you obviously want the font size to be the same as the parent's */ /* Sure thing, here you go */ }

In div.child he tokens are read and interpreted by the CSS parser. This results in a declaration font-size: 1em;. This is perfectly valid CSS, and the browsers duly note that the font size of this element should be 1em.

font-size: 1em is relative. To what? Well, to the parent’s font size, of course. Duh. That’s how CSS font-size works.

So now the font size of the child becomes the same as its parent’s, and browsers will proudly display the child element’s text in the same font size as the parent element’s while ignoring the grandparent.

This is not what we wanted to achieve, though. We want the grandparent’s font size. Custom properties — by themselves — don’t do what we want. We have to find another solution.

@property

Lea’s article explains that other solution. We have to use the Houdini @property rule.

@property --myFontSize { syntax: "<length>"; initial-value: 0; inherits: true; } div { border: 1px solid; padding: 1em; } div.grandparent { /* font-size could be anything */ --myFontSize: 1em; } div.parent { font-size: 0.4em; } div.child { font-size: var(--myFontSize); }

Now it works. Wut? Yep — though only in Chrome so far.

@property --myFontSize { syntax: ""; initial-value: 0; inherits: true; } section.example { max-width: 500px; } section.example div { border: 1px solid; padding: 1em; } div.grandparent { font-size: 23px; --myFontSize: 1em; } div.parent { font-size: 0.4em; } div.child { font-size: var(--myFontSize); } This is the grandparent This is the parent This is the child

What black magic is this?

Adding the @property rule changes the custom property --myFontSize from a bunch of tokens without meaning to an actual value. Moreover, this value is calculated in the context it is defined in — the grandfather — so that the 1em value now means 100% of the font size of the grandfather. When we use it in the child it still has this value, and therefore the child gets the same font size as the grandfather, which is exactly what we want to achieve.

(The variable uses a value from the context it’s defined in, and not the context it’s executed in. If, like me, you have a grounding in basic JavaScript you may hear “closures!” in the back of your mind. While they are not the same, and you shouldn’t take this apparent equivalency too far, this notion still helped me understand. Maybe it’ll help you as well.)

Unfortunately I do not quite understand what I’m doing here, though I can assure you the code snippet works in Chrome — and will likely work in the other browsers once they support @property.

Misson completed — just don’t ask me how.

Syntax

You have to get the definition right. You need all three lines in the @property rule. See also the specification and the MDN page.

@property --myFontSize { syntax: "<length>"; initial-value: 0; inherits: true; }

The syntax property tells browsers what kind of property it is and makes parsing it easier. Here is the list of possible values for syntax, and in 99% of the cases one of these values is what you need.

You could also create your own syntax, e.g. syntax: "ppk | <length>"

Now the ppk keyword and any sort of length is allowed as a value.

Note that percentages are not lengths — one of the many things I found out during the writing of this article. Still, they are so common that a special value for “length that may be a percentage or may be calculated using percentages” was created:

syntax: "<length-percentage>"

Finally, one special case you need to know about is this one:

syntax: "*"

MDN calls this a universal selector, but it isn’t, really. Instead, it means “I don’t know what syntax we’re going to use” and it tells browsers not to attempt to interpret the custom property. In our case that would be counterproductive: we definitely want the 1em to be interpreted. So our example doesn’t work with syntax: "*".

initial-value and inherits

An initial-value property is required for any syntax value that is not a *. Here that’s simple: just give it an initial value of 0 — or 16px, or any absolute value. The value doesn’t really matter since we’re going to overrule it anyway. Still, a relative value such as 1em is not allowed: browsers don’t know what the 1em would be relative to and reject it as an initial value.

Finally, inherits: true specifies that the custom property value can be inherited. We definitely want the computed 1em value to be inherited by the child — that’s the entire point of this experiment. So we carefully set this flag to true.

Other use cases

So far this article merely rehashed parts of Lea’s. Since I’m not in the habit of rehashing other people’s articles my original plan was to add at least one other use case. Alas, I failed, though Lea was kind enough to explain why each of my ideas fails.

Percentage of what?

Could we grandfather-inherit percentual margins and paddings? They are relative to the width of the parent of the element you define them on, and I was wondering if it might be useful to send the grandparent’s margin on to the child just like the font size. Something like this:

@property --myMargin { syntax: "<length-percentage>"; initial-value: 0; inherits: true; } div.grandparent { --myMargin: 25%; margin-left: var(--myMargin); } div.parent { font-size: 0.4em; } div.child { margin-left: var(--myMargin); /* should now be 25% of the width of the grandfather's parent */ /* but isn't */ }

Alas, this does not work. Browsers cannot resolve the 25% in the context of the grandparent, as they did with the 1em, because they don’t know what to do.

The most important trick for using percentages in CSS is to always ask yourself: “percentage of WHAT?”

That’s exactly what browsers do when they encounter this @property definition. 25% of what? The parent’s font size? Or the parent’s width? (This is the correct answer, but browsers have no way of knowing that.) Or maybe the width of the element itself, for use in background-position?

Since browsers cannot figure out what the percentage is relative to they do nothing: the custom property gets the initial value of 0 and the grandfather-inheritance fails.

Colours

Another idea I had was using this trick for the grandfather’s text colour. What if we store currentColor, which always has the value of the element’s text colour, and send it on to the grandchild? Something like this:

@property --myColor { syntax: "<color>"; initial-value: black; inherits: true; } div.grandparent { /* color unknown */ --myColor: currentColor; } div.parent { color: red; } div.child { color: var(--myColor); /* should now have the same color as the grandfather */ /* but doesn't */ }

Alas, this does not work either. When the @property blocks are evaluated, and 1em is calculated, currentColor specifically is not touched because it is used as an initial (default) value for some inherited SVG and CSS properties such as fill. Unfortunately I do not fully understand what’s going on, but Tab says this behaviour is necessary, so it is.

Pity, but such is life. Especially when you’re working with new CSS functionalities.

Conclusion

So I tried to find more possbilities for using Lea’s trick, but failed. Relative units are fairly sparse, especially when you leave percentages out of the equation. em and related units such as rem are the only ones, as far as I can see.

So we’re left with a very useful trick for font sizes. You should use it when you need it (bearing in mind that right now it’s only supported in Chromium-based browsers), but extending it to other declarations is not possible at the moment.

Many thanks to Lea Verou and Tab Atkins for reviewing and correcting an earlier draft of this article.

Let&#8217;s talk about money

QuirksBlog - Tue, 06/29/2021 - 1:23am

Let’s talk about money!

Let’s talk about how hard it is to pay small amounts online to people whose work you like and who could really use a bit of income. Let’s talk about how Coil aims to change that.

Taking a subscription to a website is moderately easy, but the person you want to pay must have enabled them. Besides, do you want to purchase a full subscription in order to read one or two articles per month?

Sending a one-time donation is pretty easy as well, but, again, the site owner must have enabled them. And even then it just gives them ad-hoc amounts that they cannot depend on.

Then there’s Patreon and Kickstarter and similar systems, but Patreon is essentially a subscription service while Kickstarter is essentially a one-time donation service, except that both keep part of the money you donate.

And then there’s ads ... Do we want small content creators to remain dependent on ads and thus support the entire ad ecosystem? I, personally, would like to get rid of them.

The problem today is that all non-ad-based systems require you to make conscious decisions to support someone — and even if you’re serious about supporting them you may forget to send in a monthly donation or to renew your subscription. It sort-of works, but the user experience can be improved rather dramatically.

That’s where Coil and the Web Monetization Standard come in.

Web Monetization

The idea behind Coil is that you pay for what you consume easily and automatically. It’s not a subscription - you only pay for what you consume. It’s not a one-time donation, either - you always pay when you consume.

Payments occur automatically when you visit a website that is also subscribed to Coil, and the amount you pay to a single site owner depends on the time you spend on the site. Coil does not retain any of your money, either — everything goes to the people you support.

In this series of four articles we’ll take a closer look at the architecture of the current Coil implementation, how to work with it right now, the proposed standard, and what’s going to happen in the future.

Overview

So how does Coil work right now?

Both the payer and the payee need a Coil account to send and receive money. The payee has to add a <meta> tag with a Coil payment pointer to all pages they want to monetize. The payer has to install the Coil extension in their browsers. You can see this extension as a polyfill. In the future web monetization will, I hope, be supported natively in all browsers.

Once that’s done the process works pretty much automatically. The extension searches for the <meta> tag on any site the user visits. If it finds one it starts a payment stream from payer to payee that continues for as long as the payer stays on the site.

The payee can use the JavaScript API to interact with the monetization stream. For instance, they can show extra content to paying users, or keep track of how much a user paid so far. Unfortunately these functionalities require JavaScript, and the hiding of content is fairly easy to work around. Thus it is not yet suited for serious business purposes, especially in web development circles.

This is one example of how the current system is still a bit rough around the edges. You’ll find more examples in the subsequent articles. Until the time browsers support the standard natively and you can determine your visitors’ monetization status server-side these rough bits will continue to exist. For the moment we will have to work with the system we have.

This article series will discuss all topics we touched on in more detail.

Start now!

For too long we have accepted free content as our birthright, without considering the needs of the people who create it. This becomes even more curious for articles and documentation that are absolutely vital to our work as web developers.

Take a look at this list of currently-monetized web developer sites. Chances are you’ll find a few people whose work you used in the past. Don’t they deserve your direct support?

Free content is not a right, it’s an entitlement. The sooner we internalize this, and start paying independent voices, the better for the web.

The only alternative is that all articles and documentation that we depend on will written by employees of large companies. And employees, no matter how well-meaning, will reflect the priorities and point of view of their employer in the long run.

So start now.

In order to support them you should invest a bit of time once and US$5 per month permanently. I mean, that’s not too much to ask, is it?

Continue

I wrote this article and its sequels for Coil, and yes, I’m getting paid. Still, I believe in what they are doing, so I won’t just spread marketing drivel. Initially it was unclear to me exactly how Coil works. So I did some digging, and the remaining parts of this series give a detailed description of how Coil actually works in practice.

For now the other three articles will only be available on dev.to. I just published part 2, which gives a high-level overview of how Coil works right now. Part 3 will describe the meta tag and the JavaScript API, and in part 4 we’ll take a look at the future, which includes a formal W3C standard. Those parts will be published next week and the week after that.

Wed, 12/31/1969 - 2:00pm
Syndicate content
©2003 - Present Akamai Design & Development.