Web Platform News

Subscribe to the RSS feed

If the page URL has a fragment (e.g., ht​tps://example.com/#posts), then the CSS :target pseudo-class1 matches the element with that ID (e.g., <div id="posts">). However, browsers only update :target on page load (before DOMContentLoaded2) and during fragment navigation3. If the page uses JavaScript to dynamically add a fragment to the URL or insert an element with an ID to the DOM, resulting in a match between URL fragment and element ID, the :target selector will not start matching the element. This is a problem for websites with dynamic content. The CSS Working Group is currently discussing whether :target’s behavior should be changed4.

  1. drafts.csswg.org/selectors-4/#the-target-pseudo
  2. twitter.com/domenic/status/1480903837967863810
  3. github.com/WICG/navigation-api/issues/162#issuecomment-961475923
  4. github.com/w3c/csswg-drafts/issues/6942

Use the CSS :disabled pseudo-class1 instead of the [disabled] attribute selector to match disabled form controls. The [disabled] selector matches form controls that have the disabled attribute set (this includes dynamically disabled form controls, since the .disabled property reflects the disabled attribute2). However, a form control is also in the disabled state if it’s inside a disabled <fieldset> element3. In other words, the form control can be disabled even when it doesn’t have the disabled attribute set. The :disabled selector matches the disabled form control in both cases.

  1. html.spec.whatwg.org/multipage/semantics-other.html#selector-disabled
  2. html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-fe-disabled
  3. html.spec.whatwg.org/multipage/form-control-infrastructure.html#concept-fe-disabled

The global innerWidth and innerHeight properties return the width and height of the layout viewport1 (including the size of the classic scrollbar if present2). These properties are not interoperable between browsers due to a bug in WebKit3. In Safari on macOS and all iOS browsers, when the user zooms in on a part of the page via a pinch-to-zoom gesture, innerWidth and innerHeight incorrectly return the size of the the visual viewport4 instead of the layout viewport.

  1. drafts.csswg.org/cssom-view/#dom-window-innerwidth
  2. mastodon.social/@simevidas/111209023632113357
  3. bugs.webkit.org/show_bug.cgi?id=174362
  4. drafts.csswg.org/cssom-view/#example-vvanimation

The CSS Font Loading module defines a check method1, which can be used to check if a web font has finished loading and is ready to render some text without causing a font swap2. Specifically, check returns false for unloaded3 web fonts, and true in all other cases. Chromium’s implementation of document.fonts.check() incorrectly also checks if the font is available locally and returns false if it isn’t4. This bug has lead some developers to think that this API can be used to check if a font is available on the user’s device. For that purpose, Cr-based desktop browsers support the (non-standard5) Local Font Access API, which requires permission from the user6.

  1. drafts.csswg.org/css-font-loading/#font-face-set-check
  2. mastodon.social/@simevidas/110555005990155071
  3. mastodon.social/@simevidas/110571366141696765
  4. bugs.chromium.org/p/chromium/issues/detail?id=1416842
  5. groups.google.com/a/chromium.org/g/blink-dev/c/xTUSN930_gM/m/ED3pHltrEgAJ
  6. developer.chrome.com/articles/local-fonts/#enumerating-local-fonts

Šime’s newsletter #5
  1. In the HTML Standard, a new type of diagram for visualizing navigable documents and their session history entries is named after Jake Archibald: Jake diagrams.
  2. GOV․UK upgraded to Google Analytics 4 (implemented using Google Tag Manager), and the total JavaScript size went up from around 20 KB to 100 KB (“nearly 5 times the size”). Initial tests revealed that this “had little to no impact” on perceived performance and interaction time.
  3. Wikimedia Tech Blog writes that they “still see edge cases where HTTP/2 is sometimes not able to re-negotiate priorities quick enough, causing CSS to needlessly be held back by HTML chunks that have already filled up the network pipes for that connection”. For now, Wikipedia’s website continues to host content images on a separate site (wikimedia.org).
  4. When a company doesn’t consider accessibility during the initial development of a product, and as a result the beta version has fundamental accessibility issues, are you a “buzzkill” for pointing that out?
  5. Trivia: If the CSS animation-name property is briefly set to a different value and then back to the original value (e.g., animation-name: bounce ⇒ animation-name: none ⇒ animation-name: bounce), that will cause the CSS animation to re-run.
  6. Wikipedia’s start page uses spaces as thousands separators (e.g., “6 668 000+ articles”). I noticed that this causes VoiceOver (and probably other screen readers as well) to mispronounce the number (“six six hundred and sixty-eight zero zero zero”). I reported the issue to Wikipedia. They should probably use the Intl.NumberFormat API for language-sensitive number formatting.
  7. The CSS Selectors module added two new general-purpose pseudo-classes, :open and :closed, which represent the “open” and “closed” states of elements such as <details>, <select>, and <dialog>. In addition to that, the HTML Standard defines a :popover-open pseudo-class specifically for popovers (elements with popover attribute). The :open pseudo-class cannot be used for popovers because elements that match :closed (e.g., a closed <dialog>) can have an open popover at the same time.
  8. Some HTML validation errors are more important than others, and some HTML errors don’t seem to matter at all. For example, errors that cause accessibility issues should be fixed at a high priority, but other errors that don’t seem to cause any damage can probably be safely ignored. It’s unfortunate that W3C’s HTML validation tool does not categorize HTML errors by importance.
  9. A reminder for websites that store data in IndexedDB: Safari automatically deletes IndexedDB storage after some time, unless the user actively visits the website (about at least once per week). The website can ask for permission to make its storage persistent (I haven’t tested if that prevents auto-deletion in Safari). In related news, Mozilla plans to start allowing IndexedDB in private windows in Firefox (Chrome allows it, Safari doesn’t).
  10. Desktop Safari has a “Zoom In” feature like every other browser (I call this “layout zoom”), but also a “Make Text Bigger” feature, which only increases the text sizes on the page without affecting layout (decreasing viewport width). The keyboard command for this feature is [option] [command] [+]. I’m not entirely sure how this feature works, but I quite like it. The problem with “Zoom In” is that it often causes the page to switch to a mobile (single-column) layout, which I usually want to avoid when increasing text sizes on web pages in my desktop browser.
  11. When the user presses a “Copy to clipboard” button, screen readers should announce that the text was copied. I’ve checked on a few popular websites if this happens: On Google Fonts and GitHub, nothing is announced. Christian suggests using an <output> element to trigger the announcement. On a related note, could the “Copy to clipboard” functionality be added to HTML?
  12. Some websites set CSS overflow-x: hidden on <html> or <body>, probably to guard against unintended horizontal overflow caused by some element on the page. This may not be a good practice (let me know if you disagree). It’s probably better to fix this type of issue by stopping the element from causing horizontal overflow to begin with.
  13. It is still not possible for a <table> to have sticky headers (position: sticky) and be horizontally scrollable (overflow-x: auto on table wrapper) at the same time. In the demo, notice how you must choose between sticky headers and horizontal scrolling. The CSS Working Group is debating how to enable sticky headers in this scenario (by either updating the meaning of overflow: clip or adding a new keyword).
  14. Web browsers support videos with transparent parts (which are the “cheapest alternative to WebGL”), but you’ll need to create two video sources with different codecs for full browser support: HEVC for Safari, and VP9 for the other browsers.
  15. You can use the <picture> element to prevent an image from loading on mobile (at narrow viewport widths). First, define the image source in a <source> element with a media min-width condition, and then leave out the src attribute on the <img> element. This is invalid HTML (<img> without src), but it works in all major browsers.
  16. It looks like <input type=number> is a problem for people with dyscalculia. GOV․UK recommendsletting people include spaces when entering numbers”. <input type=number> does not allow that (Chrome doesn’t allow it at all, and in Safari and Firefox, such a value is  invalid).

The CSS overscroll‑behavior property1 can be used to disable default browser gestures such as pull-to-refresh2 and swipe-between-pages3, as well as prevent scroll chaining (e.g., when the page starts scrolling after the user scrolls to the end of a modal dialog). This CSS feature is currently limited in browsers in two ways: (1) The swipe-between-pages gesture cannot be disabled in Safari4. (2) Scroll chaining can only be prevented if the first scroll container (e.g., modal dialog) has scrollable overflow5; this limitation is considered a browser bug6 (all browsers are affected).

  1. drafts.csswg.org/css-overscroll-1/#overscroll-behavior-properties
  2. mastodon.social/@simevidas/110469529895106763
  3. mastodon.social/@simevidas/110509240433746957
  4. bugs.webkit.org/show_bug.cgi?id=240183
  5. mastodon.social/@simevidas/110487583172745276
  6. github.com/w3c/csswg-drafts/issues/3349#issuecomment-492721871

In Shadow DOM, an element’s id attribute is locally scoped within the shadow root1. This allows multiple instances of a custom element to use the same id attribute values2, but it also prevents attributes such as aria‑describedby (attributes that refer to other elements by ID) from working across shadow boundaries3. As an exception, URL-fragment links (e.g., <a href="#heading">) refer to the id in the outer page, even if that id also exists in the shadow tree4.

  1. dom.spec.whatwg.org/#shadow-trees
  2. twitter.com/simevidas/status/1620359541153210368
  3. nolanlawson.com/2022/11/28/shadow-dom-and-accessibility-the-trouble-with-aria/
  4. mastodon.social/@rniwa/109928261352974271

The CSS font‑size‑adjust property1 is supported in Firefox and Safari 16.4 (March 2023). It’s also available as an SVG presentation attribute2. This feature is useful for aligning the x-heights of different fonts3, and preserving the x-height when font fallback occurs in order to keep the text readable. Firefox also supports the new two-value syntax4, which allows adjusting the font size based on other typographic metrics such as cap-height.

  1. drafts.csswg.org/css-fonts/#font-size-adjust-prop
  2. developer.mozilla.org/docs/Web/SVG/Attribute/font-size-adjust
  3. mastodon.social/@simevidas/110390520213899641
  4. groups.google.com/a/chromium.org/g/blink-dev/c/2FufB-ckycw/m/mj1I3iUECAAJ

The HTML maxlength attribute defines the “maximum allowed value length”1 of an <input> or <textarea> element. Browsers do not allow users to enter any more characters after the maximum length has been reached. If the user pastes or drag-and-drops a longer string, it is automatically truncated2 (this can damage the meaning of the text3). Unfortunately, screen readers don’t announce when the user reaches the maxlength character limit4. Also, Safari’s maxlength implementation treats all emoji characters as length 1, regardless of their string length (this will be “fixed” to match other browsers in a future update5).

  1. html.spec.whatwg.org/multipage/input.html#the-maxlength-and-minlength-attributes
  2. mastodon.social/@simevidas/109919980697679274
  3. github.com/whatwg/html/issues/7861#issuecomment-1111094236
  4. twitter.com/aardrian/status/1574474853767315458
  5. bugs.webkit.org/show_bug.cgi?id=252900

Browsers provide form validation by default. Unfortunately, the default error messages have a number of accessibility and usability issues: (1) the error message disappears after a few seconds in some browsers1; (2) if the form contains multiple errors, only one error message is shown; (3) the user is unable to increase the text size of error messages; (4) most of the time, the error message does not clearly explain the problem and how to fix it. It is recommended to avoid the default error messages2 and implement custom error messages instead (make sure to avoid premature validation3).

  1. bugs.chromium.org/p/chromium/issues/detail?id=1322670
  2. adrianroselli.com/2019/02/avoid-default-field-validation.html
  3. mastodon.social/@tomw/109467536437235323

When a web page is requested, the web server may sometimes take a bit longer to construct the HTML response. An early hint1 is a preliminary response with HTTP status code 103 that allows the web server to send preload links for critical page resources (stylesheets, scripts, images, fonts) before the final response is ready. The browser can then request these resources early, while still waiting for the HTML document. “Before Early Hints, no work could be started until the browser received the first byte of the response.”2

  1. html.spec.whatwg.org/multipage/semantics.html#early-hints
  2. blog.cloudflare.com/early-hints-on-cloudflare-pages/

Chrome supports a new type of full prerendering (a replacement1 for <link rel=prerender>) for “instant page loads”. Websites can declare a list of URLs to prerender (called “speculation rules”) in JSON format. When the user types something into the URL bar, Chrome will prerender the predicted URL if its “confidence” is greater than 80%2. During prerendering, the page is loaded in the background, the DOM tree is fully constructed, and scripts are executed, but a number of web API calls are delayed until the page is activated3. A web page can detect if it’s being prerendered by checking if document.prerendering returns true. Web servers can opt out of prerendering by returning a non-success status code for HTTP requests with a Sec‑Purpose: prefetch;prerender header.

  1. wicg.github.io/nav-speculation/prerendering.html
  2. developer.chrome.com/en/blog/prerender-pages/#viewing-chromes-address-bar-predictions
  3. wicg.github.io/nav-speculation/prerendering.html#delay-async-apis

People with motor disabilities use speech recognition software (e.g, Dragon, iOS Voice Control) to browse the web and interact with websites. Links, buttons, and other interactive controls should have visible text labels (e.g., the user will speak “Click products” to activate <button>Products</button>). Users can also speak keyboard commands (“Tab”, “Press enter”, etc.), so that’s another reason for the website to be keyboard accessible (e.g., modal dialogs should trap focus). Sticky headers should be avoided because they “can overlap and obscure focusable content as the page scrolls down”.


A web page’s JavaScript code can cause long tasks (50 milliseconds or more on the main thread). If the user attempts to interact with the page during that time, the page may appear frozen and unresponsive. To mitigate this problem, long tasks can be broken up into smaller tasks via setTimeout (this can be “promisified” for better ergonomics). Additionally, Cr-based browsers support isInputPending, which is useful for deciding whether to yield to the main thread early and continue work in a subsequent task, and postTask, which allows scheduling tasks at three different levels of priority.


Šime’s newsletter #4
  1. PWA as Windows application: On Windows, a PWA can already work well as a standalone application (e.g., music player) that is installed through Chrome/Edge or Microsoft Store. I made an overview of the relevant web app manifest members.
  2. Firefox experimentally supports the CSS font-variant-emoji property. With the font-variant-emoji: emoji declaration, all emoji characters are automatically rendered in the colorful emoji style (instead of the black-and-white text style). The same can be achieved by making sure that every emoji character contains Variation Selector-16.
  3. It looks like some websites have added dummy service workers with no-op fetch handlers (onfetch = () => {}) for the sole purpose of meeting Chrome’s PWA installation criteria, and now Chrome has decided to ignore such service workers in order to speed up navigation to the page.
  4. When the user replaces their Android device, the user’s installed PWAs are not migrated to the new device. Google plans to introduce a solution for this, and it will be based on Chrome Sync.
  5. You have a grid layout with two columns. The height of the grid should be based on the content in the left column, and the right column should be as tall as the left column. If the content in the right column doesn’t fit, it should scroll. How do you do this? … The solution is to set height: 0; min-height: 100%; on the right column.
  6. The Guardian (British newspaper) created a version of their website that blocks all scripts (they achieve this by setting the HTTP Content-Security-Policy header to script-src 'none'). This no-JS website is only available over the Tor network. One of the difficulties they encountered when developing the website was that they couldn’t rely on the <noscript> element, since that element activates only when JavaScript is disabled, but not when scripts are blocked. (Editor’s note: I have a similar problem as a user.)
  7. CSS: body { color: blue }. HTML: <body><div>Hello</div>. Result: “Hello” is colored blue. Question: Does color: blue ”apply” to the <div>? … The answer seems to be No. Only declarations whose selectors match the element apply to it. Declarations that are assigned to the element through inheritance don’t apply to it.
  8. Smaller Cr-based browsers (Brave, Vivaldi, etc.) use Chrome’s exact User-Agent string for compatibility with websites. In theory, websites should be able to identify the browser via the navigator.userAgentData.brands property. Unfortunately, some websites have started blocking browsers based on this API (e.g., only allowing the brands "Google Chrome" and "Microsoft Edge"). This may ultimately force smaller Cr-based browsers to include the brand "Google Chrome" for compatibility, in other words, pretend to be Chrome. “Only the major vendors are able to tell the truth”, writes Vivaldi.
  9. Firefox devtools has a useful little feature that makes debugging CSS quicker. When a CSS declaration is overridden by another declaration, Firefox shows a Filter button next to it. Click the button to highlight all instances of that property, including shorthands that contain this property.
  10. It is not possible to force-open a <details> element only under certain conditions (e.g., large viewport width) with HTML and CSS alone. This could be enabled by adding a media attribute to <details>.
  11. Trivia: Some CSS selectors are case-insensitive. This includes element type selectors (e.g., INPUT) and certain attribute selectors (e.g., [TYPE=TEXT]).
  12. Firefox added support for the Web MIDI API, but the feature is “add-on-gated”. When a website requests access to MIDI devices on the user’s system (and at least one MIDI device is connected), the user is prompted to install a “site permission add-on” that enables the Web MIDI API for that website. Mozilla says that this approach “bridges the gap between casual, risk-free browsing and new powerful device APIs”. It might be justified.
  13. The Media Stream Recording API for recording media streams (webcam, mic) and media elements (<video>, <audio>) is widely supported in browsers, but it seems that browsers can only record media in the WebM file format. “Many video editing softwares do not handle .webm videos; therefore it’s a struggle to do any sort of post-processing or editing on the generated video files.”
  14. WebKit Blog writes that people who have the latest iPad Pro M2 can activate CSS :hover states with the Apple Pencil in web browsers.
  15. Some web developers were annoyed that “Mozilla is investing in AI instead of Firefox”. However, Mozilla’s AI startup most likely won’t impact Firefox development. Mozilla.ai was launched by the nonprofit Mozilla Foundation, while Firefox is made by the for-profit Mozilla Corporation. The Foundation owns the Corporation, but both entities generally operate separately and don’t share resources.
  16. Google should probably hire or pay a web accessibility expert to review guides before they are published on web.dev. For example, the “Building a tooltip component” article provides code that violates WCAG and should therefore not be used. Adrian Roselli has been posting about this issue repeatedly in October, November, and December of last year.
  17. The name of the web development conference ffconf is pronounced “F F conf”, but screen readers announce it as ”fconf”. This may seem like an issue that needs to be fixed. However, websites should not use hidden text, aria-label, etc. to affect how screen readers speak stylized words. There is currently no good way to do that, and it’s probably not even necessary.

The HTML <template> element1 is a unique kind of element2. The HTML content of a <template> is placed into a separate document fragment that is not rendered, and unlike <div hidden>, not active (scripts don’t run, images don’t load, etc.) and isolated from DOM APIs such as document.querySelector. This inactive (not to be confused with inert3) HTML content can then be dynamically added to the page via JavaScript (e.g.,a menu button as a progressive enhancement).

  1. developer.mozilla.org/docs/Web/HTML/Element/template
  2. html.spec.whatwg.org/multipage/syntax.html#elements-2
  3. mastodon.social/@simevidas/110135347953456292

The CSS text‑transform property1 can be used to change the letter case2 of text rendered on the screen (e.g., text‑transform: uppercase renders “text” as “TEXT”). In 2018, the CSS Working Group decided that such text transforms are purely a visual effect that should not apply when the user copies text to the clipboard3. Only Firefox implements this behavior. In all other browsers, when CSS-transformed text is copied, it is sent to the clipboard with transformations applied (“TEXT” instead of “text”).

  1. w3c.github.io/csswg-drafts/css-text-4/#text-transform-property
  2. en.wikipedia.org/wiki/Letter_case
  3. github.com/w3c/csswg-drafts/issues/627#issuecomment-380425497

The <audio> element has been supported in all major browsers since 2013. If the element includes the <audio loop> attribute1, the sound will play on repeat endlessly. Unfortunately, most browsers still have trouble looping audio in a seamless manner2 (without a noticeable pause between iterations). The WebKit and Chromium bugs3,4 for this issue have been filed in 2021. As a workaround, the Web Audio API5 can be used to play audio that loops seamlessly in all browsers6.

  1. html.spec.whatwg.org/multipage/media.html#attr-media-loop
  2. jsbin.com/wamifol/edit?html,output
  3. bugs.webkit.org/show_bug.cgi?id=230553
  4. bugs.chromium.org/p/chromium/issues/detail?id=1251904
  5. developer.mozilla.org/docs/Web/API/Web_Audio_API
  6. jsbin.com/bejogak/edit?js,output

In the Internationalization API1, the Intl.NumberFormat constructor2 for language-sensitive number formatting provides a compact option (notation: 'compact') with two display modes (compactDisplay: 'short' and 'long'). For example, formatting the number 1_234_567 produces the strings '1.2M' (short) or '1.2 million' (long) in English ('en'). Additionally, the length of the output can be adjusted via the maximumSignificantDigits option (e.g., setting it to 3 changes the string to '1.23M'). Test different numbers in the interactive demo3.

  1. tc39.es/ecma402/
  2. developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat
  3. codepen.io/simevidas/pen/xxaQoRp?editors=0010

The events index1 in the HTML Standard does not list a number of events that are defined in other specifications. This includes the cut, copy, and paste events, which are defined in Clipboard API and events2, mouse and keyboard events (incl. contextmenu), which are defined in UI Events3, the resize, scroll, and scrollend events, which are defined in CSSOM View4, and the slotchange event, which is defined in the DOM Standard5.

  1. html.spec.whatwg.org/multipage/indices.html#events-2
  2. w3c.github.io/clipboard-apis/#clipboard-event-definitions
  3. w3c.github.io/uievents/
  4. drafts.csswg.org/cssom-view/#event-summary
  5. dom.spec.whatwg.org/#eventdef-htmlslotelement-slotchange

The CSS Fonts module contains a list of around 40 font features (e.g., slashed zero1) for which it recommends authors to use the high-level font‑variant‑* and font‑kerning properties instead the the low-level font‑feature‑settings property. However, some font‑variant‑* properties and values are still not widely supported in browsers2. For example, font‑variant‑position is not supported in Chromium3 (the sups and subs font features can make the lh unit more reliable4), while font‑variant‑alternates (e.g., styleset()5) shipped in Chrome and Safari only very recently6,7.

  1. twitter.com/mgechev/status/1501430097873715204
  2. caniuse.com/?search=font-variant-
  3. groups.google.com/a/chromium.org/g/blink-dev/c/wQ_8T-AXLFA/m/IbL4wlAMBAAJ
  4. twitter.com/simevidas/status/1637860665124610050
  5. toot.cafe/@simevidas@mastodon.social/110056822352094891
  6. groups.google.com/a/chromium.org/g/blink-dev/c/IDXErld2g04/m/tJRD0Wu7CQAJ
  7. front-end.social/@jensimmons/109514586045636753

When a PWA is installed on desktop1, it launches in a standalone window. The PWA can define the color of the window’s title bar via the web app manifest "theme_color" member2. Additionally, the manifest can include "display_override": [ "window‑controls‑overlay" ]3, which enables the user to hide the title bar. In that case, the window’s controls (close, minimize, etc.) are still displayed in the corner of the window. Also, whenever the user navigates within the PWA, the website’s domain is briefly shown next to the window controls4. The PWA can position elements in the remaining title bar area, which has a dynamic width, via CSS titlebar‑area‑* environment variables.

  1. mastodon.social/@simevidas/109977164853966826
  2. w3c.github.io/manifest/#theme_color-member
  3. groups.google.com/a/chromium.org/g/blink-dev/c/guI1QCPJTAA/m/Lly_wIYpBQAJ
  4. twitter.com/simevidas/status/1629519140242223105

Certain user actions, such as clicks and key presses, trigger user activation, and certain web APIs, such as the Clipboard API writeText method, can only be used in response to user activation1. In Cr-based browsers and Firefox, any such event (e.g., clicking an empty space on the page) triggers user activation for about 5 seconds2. In Safari, user activation is available only when handling the event (e.g., inside an addEventListener callback). Some of the other user actions that don’t trigger user activation in Safari are gamepad button presses3 and voice commands4.

  1. developer.mozilla.org/docs/Web/Security/User_activation
  2. mastodon.social/@simevidas/109907811147988648
  3. twitter.com/AshleyGullen/status/1625987814272778241
  4. twitter.com/sepia_fw/status/1627402933485637634

Šime’s newsletter #3
  1. The Web Almanac uses a clever approach to progressively enhance the website’s hamburger menu. Initially, the hamburger icon is a link (<a>) to the menu in the website’s footer. Once the JavaScript loads, the link is changed to a button (<button>) that opens the menu in a popup. By initially rendering the menu in the footer instead of the header, the website avoids a layout shift during page load.
  2. A heading with an id attribute (e.g., <h2 id="about">) can be linked to by adding a matching fragment to the URL (#about). GitHub supports linking to headings in Markdown documents via URL fragments. However, these headings actually don’t have id attributes. Instead, GitHub scrolls linked headings into view using JavaScript. If we consider that browsers create global properties for elements with id attributes in the DOM, GitHub probably wanted to avoid polluting the global object with such properties in this case.
  3. GitHub uses <blockquote> elements for notes and warnings in Markdown documents. This doesn’t make a whole lot of sense if you consider that <blockquote> represents “a section that is quoted from another source”. The announcement even refers to this as an ”accessible” solution. I’m not sure what they mean by that. MDN Web Docs offers a very similar option in Markdown documents, but the notes and warnings are correctly wrapped in a <div> instead of <blockquote>.
  4. Todd Libby: “There is a lot more to accessibility testing than Lighthouse”. Getting a 100 score in the accessibility category does not mean that the job is done. Manual testing with keyboard and screen reader should be viewed as a requirement (Lighthouse “encourages” it). I half-jokingly suggested that Lighthouse’s accessibility score should only go up to 33, to send a strong signal that most accessibility issues cannot be automatically detected.
  5. If you use Nu Html Checker (probably the best HTML validator), keep in mind that when you check by address (the default option), Nu fetches only the initial HTML document, which means that it can’t detect issues caused by DOM manipulation via JavaScript. Luckily, there’s a bookmarklet that can serialize your page’s DOM and send it to Nu.
  6. I think it makes sense to set CSS user-select: none on <label> and <summary> to prevent unintended text selection when clicking these elements repeatedly. Ideally, only click-based selection would be disabled, but that’s not possible with the user-select property alone. The none value disables all methods of text selection (click, drag, keyboard command, etc.).
  7. LastPass added spellcheck=false to <input type=password> to prevent Chrome’s enhanced spell check (an optional feature) from sending their users’ passwords to Google’s servers. Chrome’s behavior has been dubbed “spell-jacking”.
  8. I started automatically replacing hyphens with non-breaking hyphens in <code> elements on my website. I do this because most browsers wrap <code> too aggressively for my liking. For example, the CSS custom property --foo is broken into - at the end of the first line and -foo at the beginning of the next line. Also, when a code word like font-size is broken into font- and size, it is not entirely clear if the hyphen is inserted by the browser, or if it’s part of the word. I avoid both of these problems by using non-breaking hyphens.
  9. In Firefox on Windows, if the user clicks a number input (<input type=number>) to enter a value and then attempts to scroll the page while the mouse cursor is still hovering the input, the mouse wheel will alter the input’s value instead of scrolling the page. Mozilla is debating whether to remove this functionality from Firefox.
  10. The Popover API is ready to ship in Chromium. This feature consists of the HTML popover and related attributes, the CSS :open pseudo-class, and a JavaScript API for advanced uses (showPopover method, popovershow event, etc.). This feature can be used to display transient elements in the top layer, such as notifications, toasts, nested menus, media popups, rich tooltips, navigation drawers, and more.
  11. Cr-based browsers support the fetchpriority and blocking=render attributes for scripts. If I understand correctly, <script async fetchpriority=high blocking=render> loads a script at a high priority, while not blocking the parser, but blocking rendering.
  12. CSS prefers-reduced-data still hasn’t shipped in a single browser (Blink supports it behind flag since 2020). If browsers shipped this feature, websites like Twitter could use it to automatically (this is key) turn on their data saver modes for users with (prefers-reduced-data: reduce). I asked Mozilla to add an “Ask websites to reduce data” checkbox to Firefox’s settings that would be mapped to the prefers-reduced-data media feature in CSS.
  13. I realized that animated GIFs are foremost an accessibility issue because there’s no simple way to pause them in browsers.
  14. I have trouble reading CSS min()/max() values, for example, max(300px, 50vw). My best attempt: First, identify the absolute value (300px). That value is the limit. Next, the CSS function defines the type of limit (max() ⇒ lower limit). Finally, apply the limit to the other, non-absolute value: “The value of max(300px, 50vw) is 50vw, except that it stops shrinking at 300px”.
  15. Native Android apps can use the System WebView component to render web pages, but that comes with a couple of serious limitations, according to Meta. First, the system webview is not always up to date (Meta says that this affects “many Android users”), and updating it causes apps to crash (this is unpreventable). Second, in the system webview, the compositor does not run in a separate GPU process, which affects rendering performance. Facebook’s in-app browser is now powered by Meta’s own custom webview. Facebook users can opt out of this browser in Settings > Profile settings > Media and contacts > Links open externally.

If the user enters (or pastes) an invalid value into a number input (<input type=number>), the input’s value property will return an empty string ('') instead of the actual input. In Firefox, a single space before or after the number is enough to invalidate the input1 (e.g., if the user enters “123 ”, the value property will return ''). A good rule of thumb is to use <input type=number> only if it would make sense for the user to increment and decrement the value2.

  1. mastodon.social/@simevidas/109891416986175755
  2. kilianvalkhof.com/2022/css-html/are-you-sure-thats-a-number-input/

The aria‑label attribute is an invisible text label for screen reader users. Appropriate uses of aria‑label include icon buttons1 (e.g., <button aria‑label="Close">×</button>), and labeled landmarks. Labeling a landmark makes sense when the page contains more than one landmark of the same type1 (e.g., <nav aria‑label="Primary">), and when you want to highlight an important part of the page2 (e.g., <section aria‑label="All posts">). aria‑label doesn’t have great support in services that translate web pages into different languages, so you may instead want to provide the label as hidden text and reference it via aria‑labelledby3,4.

  1. www.htmhell.dev/adventcalendar/2022/7/
  2. www.scottohara.me/blog/2021/07/16/section.html
  3. twitter.com/SaraSoueidan/status/1627674905121640448
  4. adrianroselli.com/2019/11/aria-label-does-not-translate.html

Five years later1, Safari is still the only major browser that supports MP4 videos in <img> elements (<img src="video.mp4">). Such a video auto-plays without sound and loops automatically, which is the same behavior as <video autoplay muted loop>. One problem with this feature is that Safari’s “Never auto-play” preference doesn’t apply to videos in <img>2,3, and there’s no way to pause them manually either because <img> doesn’t have video controls. As an alternative, Cr-based browsers (with the exception of Microsoft Edge4) support animated AVIF (<img src="animated.avif">), but it would still make sense to also add support for MP4 in <img>5.

  1. bugs.chromium.org/p/chromium/issues/detail?id=791658
  2. bugs.webkit.org/show_bug.cgi?id=245232
  3. twitter.com/simevidas/status/1625128346429403136
  4. front-end.social/@AmeliaBR/109899775082771064
  5. github.com/whatwg/html/issues/7141

In 2017, the CSS Values and Units module added two new font-relative length units, cap and lh. The cap unit is equal to the font’s cap-height (height of a capital letter). This unit is useful for vertically centering icons1. The lh unit is equal to the computed value of the line‑height property (which doesn’t necessarily match the actual line height2). This unit is useful for sizing <textarea> elements3. The cap unit is supported in Firefox, and the lh unit is supported in Cr-based browsers and experimentally in Safari Preview since 2020 (it’s still buggy4,5). Both units also have their root variants, rcap and rlh.

  1. mastodon.social/@simevidas/109841865574850560
  2. twitter.com/simevidas/status/1624348272558047233
  3. groups.google.com/a/chromium.org/g/blink-dev/c/E3Q7qOCk7A4/m/kMtu9VlqAQAJ
  4. bugs.webkit.org/show_bug.cgi?id=252075
  5. bugs.webkit.org/show_bug.cgi?id=252108

Find-in-page can find hidden text in closed <details> elements (which are auto-expanded1) in Cr-based browsers, but not yet in Safari and Firefox2. Chromium also supports the HTML hidden=until‑found attribute extension3, which makes text that was hidden via the hidden attribute searchable in the same manner (the browser automatically removes the hidden attribute when a match is actively highlighted within the element4). Wikipedia’s mobile website uses until‑found on its (JavaScript-powered) collapsed sections5.

  1. groups.google.com/a/chromium.org/g/blink-dev/c/a6iO__pqI_E/m/Asj1sUABBAAJ
  2. bugzilla.mozilla.org/show_bug.cgi?id=1724299
  3. html.spec.whatwg.org/multipage/interaction.html#the-hidden-attribute
  4. groups.google.com/a/chromium.org/g/blink-dev/c/e8n8Z5dzMs0/m/SWTK7yL1AAAJ
  5. twitter.com/JosephArhar/status/1567279577160126464

The Async Clipboard API1 only supports three mandatory data types (plain text, HTML, and PNG), and browsers may optionally sanitize the clipboard data (e.g., remove <script> elements). To get around these limitations, Cr-based browsers support “web custom formats”, which allow websites to paste arbitrary, unsanitized data to the clipboard by using a custom MIME type with a “web ” prefix (e.g., 'web text/html'). This feature is not supported in Safari and Firefox2, and hasn’t been added to the Clipboard API specification yet3.

  1. w3c.github.io/clipboard-apis/
  2. wpt.fyi/results/clipboard-apis?label=master&label=experimental&aligned&view=subtest&q=async-custom-formats
  3. github.com/w3c/clipboard-apis/issues/165

Šime’s newsletter #2
  1. SVG elements often use ID attribute references (e.g., <g clip-path="url(#clip1)"> references <clipPath id="clip1">). If a web page contains multiple inline SVGs that use the same ID attribute names, the SVGs won’t render correctly because subsequent elements with the same ID will be ignored (IDs must be unique in the DOM tree). As a workaround, you can use Shadow DOM to encapsulate the individual <svg> elements and scope their ID references.
  2. Apple’s system font (San Francisco) is a variable font with a full range of 1000 weights (font-weight 1 to 1000), but Chrome only supports 9 of the 1000 weights. As a workaround, you can declare weights via font-variation-settings instead, but be aware that this low-level property disables font-weight declarations on nested elements.
  3. If the main content of your website consists of multiple distinct sections, you may want to make it easier for screen reader users to jump to an important section within the main content by turning it into a landmark via a labeled <section> element (e.g., <section aria-label="All posts">).
  4. CSS scroll snap reduces scroll momentum on iOS. The WebKit bug links to a test page where you can confirm this issue in an iOS browser. Basically, when scroll snapping is turned on, the user is unable to scroll very far with a single fast swipe because the scroll operation snaps very quickly. Android browsers do not have this issue. I decided against adding scroll snapping to my website because of this issue.
  5. On the WICG forum, I suggested that browsers should mark required form fields by default. WCAG states that required fields must be labeled as such. Most websites do this by appending an asterisk (*) to the field’s label. I think it would be better for both users and websites, if browsers took responsibility for marking required fields.
  6. Firefox is the only browser that supports alternative stylesheets (<link rel="alternate stylesheet">) that can be selected via the View > Page Style menu on desktop. It’s basically a native color theme switcher for websites (video demo). Unfortunately, the user’s selection is not persisted on return visits. This is a shame because this feature could potentially serve as a basis for allowing users to turn on dark mode on a per-site basis (the website provides dark mode via an alternative stylesheet, the user selects it via the Page Style menu, and Firefox remembers the user’s selection).
  7. In desktop Safari, you can individually enable experimental web platform features via the Develop > Experimental Features menu. Unfortunately, it appears that your preferences are reset every time Safari is updated.
  8. Complex conditional width: In CSS, it is possible to size a website’s sidebar so that its width is a fraction of the viewport width (e.g., one third), but no smaller than a fixed minimum width, and if it grows to 50% at smaller viewport widths (due to its minimum width), the entire page layout switches to a single column (video demo).
  9. In SEO, there is a dark pattern where the website uses markup like <span data-href> (plus JavaScript) instead of <a href> to hide a link from search engines. Such a fake <span> link is not accessible to keyboard and screen reader users.
  10. Slack announced improvements to keyboard navigation. “Pressing the F6 key moves focus between different sections in Slack.” I think this functionality would be useful in desktop browsers in general. Navigation via the Tab key is relatively slow. Another keyboard command for navigating to the page’s landmarks (<nav>, <main>, etc.) would be useful.
  11. The <input size> attribute is not very consistent across browsers. According to the HTML Standard, this attribute defines how many characters should be visible in the <input>. I’ve filed a Firefox bug. Until this attribute becomes more interoperable, it’s probably better to size inputs with CSS (e.g., width: 1em).

One last thing: I published the biggest update to my collection of 1000+ web dev feeds in years. A few months ago when I switched from Feedly to Feedbro, I discovered dozens of outdated, broken feed URLs in the collection, and I’ve fixed them all in the latest version. See the README for more information.


Contrary to what its name might suggest, the pageshow event fires even if the web page loads in a background tab where it’s not visible. This event has nothing to do with the page being visibly shown to the user. Rather, it’s a page transition event that fires “when the page’s session history entry becomes the active entry”. In practice, pageshow fires shortly after the load event, which can be very late on a slow connection1. The event’s persisted property is useful for detecting if the page was restored from the browser’s back/forward cache (bfcache)2. If you need to run some JavaScript code when the page is actually shown to the user for the first time, check the document’s visibilityState3, and if this property returns 'hidden', listen for the the next visibilitychange event.

  1. youtu.be/_iq1fPjeqMQ?t=427
  2. web.dev/bfcache/#apis-to-observe-bfcache
  3. developer.mozilla.org/docs/Web/API/Document/visibilityState

The HTML <details> element1 cannot become a flexbox or grid container (display: flex and display: grid are ignored). This restriction is specified in the HTML Standard (“The <details> element is expected to render as a block box”2) and implemented in all browsers. It is possible to work around this limitation with display: contents3 (on <details>4). Browsers also allow turning <details> into an inline-level element (display: inline)5.

  1. developer.mozilla.org/docs/Web/HTML/Element/details
  2. html.spec.whatwg.org/multipage/rendering.html#the-details-and-summary-elements
  3. mastodon.social/@simevidas/109538578812286095
  4. mastodon.social/@simevidas/109547778317262203
  5. mastodon.social/@simevidas/109541423675816213

The default overlay scrollbars1 in iOS and macOS browsers are either light or dark, depending on the web page’s background color. The problem with this system is that the scrollbar is almost invisible on pages with contrasting headers (e.g., WebKit Blog). Limiting the page’s supported color schemes via the CSS color‑scheme property2 (e.g., color‑scheme: light to get a dark scrollbar) does not work in this case. Websites can avoid this issue by coloring the scrollbar via the CSS scrollbar‑color property3, but this is currently only supported in Firefox on macOS.

  1. drafts.csswg.org/css-overflow-3/#overlay-scrollbars
  2. drafts.csswg.org/css-color-adjust-1/#color-scheme-prop
  3. drafts.csswg.org/css-scrollbars-1/#scrollbar-color

Standard modal dialogs (<dialog> elements opened via the showModal method1) are always rendered on top of all other elements on the page (e.g., sticky headers), regardless of their stacking contexts and z‑index values. Modal dialogs also have a backdrop that prevents users from interacting with the rest of the page while the dialog is open (clicks, taps, and keyboard tabs won’t go through). However, the user can still scroll the underlying page2. The <dialog> element is not supported on iOS 14 and older versions (3% of all mobile requests3).

  1. developer.mozilla.org/docs/Web/API/HTMLDialogElement/showModal
  2. github.com/whatwg/html/issues/7732
  3. analytics.wikimedia.org/dashboards/browsers/#mobile-site-by-os/os-family-and-major-hierarchical-view

All major browsers support the CSS prefers‑contrast media feature1. The (prefers‑contrast: more) media query matches when the user turns on “Increase contrast” on Apple’s devices, “High contrast” (or one of the new “Contrast themes”) on Windows, etc. Websites should use this media query to increase the text contrast ratio to 7:1 or more2. Firefox’s Settings page does this3.

  1. caniuse.com/mdn-css_at-rules_media_prefers-contrast
  2. w3c.github.io/wcag/21/guidelines/#contrast-enhanced
  3. twitter.com/simevidas/status/1564935790160842752

A common way to add a drop cap to a paragraph is to apply float: left (and a larger font‑size) to the ::first‑letter pseudo-element. Firefox has a long-standing bug1 that causes floated drop caps to be vertically misaligned, compared to other browsers. This bug will become less of an issue once Firefox adds support for the CSS initial‑letter property2, which enables creating drop caps without float (e.g., initial‑letter: 3 creates a three-line drop cap). This property is already supported in Safari and Chrome 1103.

  1. bugzilla.mozilla.org/show_bug.cgi?id=415506
  2. developer.chrome.com/blog/control-your-drop-caps-with-css-initial-letter/
  3. mastodon.social/@simevidas/109453728737882679

The tel URL scheme allows websites to link to telephone numbers1. Smartphone users can more quickly initiate calls by tapping tel links (<a href="tel:">). On desktop, this feature may not work for some users. The GOV.UK Design System recommends disabling tel links “on devices that cannot make calls”2, but there is no way for a website to detect if the user can make calls via tel links on desktop.

  1. www.rfc-editor.org/rfc/rfc3966#section-8
  2. design-system.service.gov.uk/patterns/telephone-numbers/#do-not-display-telephone-numbers-as-links-on-devices-that-cannot-make-calls

Since 2016, the CSS Generated Content module states that ::before and ::after text should be “searchable, selectable, and available to assistive technologies”. CSS text can be used to enhance the user experience1 (e.g., marking links to PDF files with “(PDF)”). It is correctly announced by modern screen readers, but it remains unselectable in browsers (and it’s searchable only in Firefox, with some limitations2). The current behavior in browsers (which is arguably user hostile3) can be a problem for users4.

  1. adrianroselli.com/2019/02/f87-css-generated-content-and-wcag-conformance.html#Examples
  2. bugzilla.mozilla.org/show_bug.cgi?id=1804202
  3. bugzilla.mozilla.org/show_bug.cgi?id=1627643#c5
  4. mastodon.social/@simevidas/109465434126597779

When the user types into an <input type=search> field, all major browsers except Firefox show a clear button (right-aligned ⊗ icon). This button can be hidden by applying display: none to the (non-standard) CSS ::‑webkit‑search‑cancel‑button pseudo-element1. Firefox shows such a clear button on time and date inputs, but there exists no matching CSS pseudo-element for this button. This is a problem for component libraries that provide their own custom clear buttons2.

  1. developer.mozilla.org/docs/Web/CSS/::-webkit-search-cancel-button
  2. github.com/shoelace-style/shoelace/issues/791

W3C Wiki has a page on SVG security (last updated in 2014) that lists the restrictions applied to SVG images. An SVG loaded with an <img> element cannot fetch any external resources (including fonts) and cannot execute scripts. These restrictions don’t apply to inline SVG (<svg> element in HTML document).


When a hyperlink contains both an icon and text (e.g., “← Back”), wrap both parts in a single <a> element. Avoid using two redundant <a> elements for the icon and text. Redundant links are a problem for keyboard users (they have to press Tab twice) and screen reader users (the link is announced twice).


The CSS ::marker pseudo-element1 for styling list item markers (bullets, numbers, etc.) only accepts a small set of text-related properties2, such as font‑size and color, and the content property, which means that it’s currently not possible to increase the spacing between the marker and the text via padding or margin properties3. As a workaround, you can add spaces (e.g., content: "• "), hide the marker and create a custom marker using the list item’s ::before element, or add padding to the list item in some cases4.

  1. developer.mozilla.org/docs/Web/CSS/::marker
  2. drafts.csswg.org/css-lists-3/#marker-properties
  3. github.com/w3c/csswg-drafts/issues/4571
  4. twitter.com/simevidas/status/1591208191983255553

An event’s target property1 references the node to which the event was dispatched (event’s origin), whereas the event’s currentTarget property2 references the node to which the event listener was added (object of addEventListener call). You can use CSS pointer‑events: none3 to ensure that click and other pointer events4 aren’t dispatched to an element’s descendants (useful for buttons).

  1. developer.mozilla.org/docs/Web/API/Event/target
  2. developer.mozilla.org/docs/Web/API/Event/currentTarget
  3. developer.mozilla.org/docs/Web/CSS/pointer-events
  4. svgwg.org/svg2-draft/interact.html#PointerEvents

When the user focuses an initially empty, required form field (<input> with required attribute), some screen readers will announce this field as invalid (“invalid data” in Firefox/VoiceOver, “invalid entry” in Chrome and Firefox/NVDA, etc.). This information is misleading and can confuse screen reader users. Website authors can avoid this issue by using aria‑required="true"1 instead of required, and implementing client-side validation (error message on attempted from submit) with JavaScript.

  1. developer.mozilla.org/docs/Web/Accessibility/ARIA/Attributes/aria-required

Facebook, Gmail, GitHub, and other popular websites allow users to navigate the site and perform actions using single-character keyboard shortcuts (e.g., pressing the C key on Gmail’s website opens the “New message” dialog). This feature can be frustrating for speech input and keyboard users1, so websites should allow users to remap or disable single-character shortcuts (e.g., Facebook has an option for this2).

  1. w3c.github.io/wcag/understanding/character-key-shortcuts.html
  2. twitter.com/simevidas/status/1573747791448399872

Automatic hyphenation (CSS hyphens: auto) is widely supported in modern browsers1. However, the CSS hyphenate‑limit‑chars property2, which would allow websites to set the minimum number of characters before/after the hyphenation break, is still not supported. Browsers default to 2 characters, but that may be too low for some people who would like to turn on automatic hyphenation. You can voice your support for this CSS property in Chromium issue 9240693 and Mozilla bug 15217234.

  1. caniuse.com/css-hyphens
  2. drafts.csswg.org/css-text-4/#hyphenate-char-limits
  3. bugs.chromium.org/p/chromium/issues/detail?id=924069
  4. bugzilla.mozilla.org/show_bug.cgi?id=1521723

When using the CSS calc() function to add (or subtract) a variable to a length or percentage, e.g., calc(100% ‑ var(‑‑margin)), be aware that the calc() value will become invalid if the custom property is set to a unitless zero (‑‑margin: 0). To avoid this error, set the custom property to 0px instead. Note that your CSS linter may automatically strip the unit from this value (turn 0px into 0).


Chrome shipped (inline) import maps for JavaScript modules (<script type="importmap">) in 20211. This feature can be used to improve the cacheability of versioned module dependencies: The mutable hashed file names are only declared in the import map, while the modules use “bare import specifiers” (e.g., import moment from "moment"). Safari has not implemented import maps yet because this feature currently does not integrate CSP subresource integrity2.

  1. groups.google.com/a/chromium.org/g/blink-dev/c/rVX_dJAJ-eI/m/17r-6-eiAgAJ
  2. lists.webkit.org/pipermail/webkit-dev/2020-October/031556.html

Šime’s newsletter #1

Hello, everyone. I posted 23 news items on webplatform.news in the past month. The best way to subscribe to that content is via the website’s RSS feed.

In this newsletter, I’d like to share other information that I found interesting. Here’s the first batch of content:

  1. I checked which older browser versions (more than one year old) still have a significant usage share on Wikimedia’s analytics. The one that stands out is the previous (major) version of Safari (Safari 14). For this reason, I think it makes sense to focus on this browser version in particular when implementing fallbacks (e.g., for :focus-visible).
  2. Jeremy Keith asked why so many websites avoid the <button> element. This could be (in part) due to the <button> element’s layout limitations. For example, it’s difficult to make buttons flexible (like <div> elements).
  3. :focus, :focus-visible, and :focus:not(:focus-visible). How do these three CSS selectors relate to each other? I was confused about this, so I made a diagram. My main takeaway is that :focus-visible is a proper subset of :focus (an element that is :focus-visible is also :focus).
  4. I somehow never learned that href="/page" is a path-absolute URL string. For comparison, href="page" (which is the same as href="./page") is a path-relative URL string. See my example.
  5. I agree with Chris Heilmann (and Bramus, and probably many others) that light/dark mode switches should be a browser feature instead of cluttering the web.
  6. The CSS Working Group added the scrollbar-width property (in 2018), and Firefox shipped this feature shortly after. The scrollbar-width: thin value makes the scrollbar thinner, which can make sense ”for certain small or cramped elements”. I suggested a thin scrollbar for the table of contents sidebar in Wikipedia’s new design, but there is a concern that thin scrollbars may be difficult to use for people with mobility impairments.
  7. The two ways in CSS to truncate a single line of text with an ellipsis are text-overflow: ellipsis and -webkit-line-clamp: 1, but they behave differently, and -webkit-line-clamp: 1 is the safer method because text-overflow: ellipsis splits words, which can have disastrous results.

Until next time,
Šime


Browsers allow websites to write text to the clipboard (Async Clipboard writeText method1) without requesting permission from the user. In Safari and Firefox, this operation requires a user gesture2 (mouse click, key press, etc.), but in Chromium-based browsers, a web page can freely write text to the clipboard without any interaction from the user3 ('clipboard‑write' access is auto-granted4). Attempting to write to the clipboard without user interaction now triggers a permission prompt in Chrome and Edge5.

  1. developer.mozilla.org/docs/Web/API/Clipboard/writeText
  2. github.com/w3c/clipboard-apis/issues/182
  3. twitter.com/simevidas/status/1565813868475080704
  4. groups.google.com/a/chromium.org/g/blink-dev/c/epeaao7l13M/m/b8ewAH5uBwAJ
  5. github.com/w3c/clipboard-apis/issues/182#issuecomment-1336764282

If an image on the page has a caption (<figcaption> element), it should also have a text alternative (alt attribute). Repeating the same text in <figcaption> and alt is incorrect. Marking an image that has a caption as decorative (alt="") is also incorrect. The Guardian1 and The Verge2 are some of the websites that get this wrong.

  1. twitter.com/simevidas/status/1558682035484590080
  2. twitter.com/simevidas/status/1564959853076008961

A better name for third-party cookies is cross-site cookies1 (“third party” is ambiguous2). E.g., if a web page includes an image from a different website, then the cookie that comes with that image is a cross-site cookie. The web page cannot access this cookie. If multiple websites load this image, then the image’s web server can track the user across these sites via that cookie. Some browsers protect the user’s privacy by blocking or partitioning cross-site cookies3.

  1. httpwg.org/http-extensions/draft-ietf-httpbis-rfc6265bis.html#name-third-party-cookies
  2. tess.oconnor.cx/2020/10/parties
  3. educatedguesswork.org/posts/private-browsing/#cookies

Two nested requestAnimationFrame1 function calls (“double rAF”) is the generic way to run code after the browser has painted the next frame. Waiting for the next frame is necessary when applying a CSS transition to an element that has just been added to the DOM. If there isn’t a frame between the initial and final states, the browser won’t animate the transition and just render the final state2.

  1. developer.mozilla.org/docs/Web/API/window/requestAnimationFrame
  2. twitter.com/simevidas/status/1563222845546586114

During page load, if you need to run a script as soon as the initial HTML document has finished parsing, use a <script defer> element (type=module scripts are defer by default). The problem with the DOMContentLoaded event is that it fires after all deferred scripts have loaded and executed1. If the initial HTML document contains lots of content, and you need to run a script before the entire document has finished parsing, use a preloaded <script async> element2.

  1. youtu.be/_iq1fPjeqMQ?t=378
  2. youtu.be/_iq1fPjeqMQ?t=732

To ensure that an emoticon1 is correctly and consistently announced by screen readers, wrap it in a <span role=img> element with an aria‑label attribute, e.g., <span role=img aria‑label="tears of happiness emoticon">:’‑)</span>. This solution can also be used for emoji2.

  1. en.wikipedia.org/wiki/List_of_emoticons
  2. tink.uk/accessible-emoji/

Firefox shipped two CSS properties that were already supported in other browsers: (1) Apple introduced the backdrop‑filter property in 20151 to enable the frosted glass effect on the web. The filter effect is applied to the area behind the element, so the element’s background needs to be (semi-)transparent for the effect to be visible. (2) When scroll‑snap‑stop: always is added to a scroll snap container2, each scroll operation (e.g., swipe gesture) always stops at the next element (snap position) and cannot scroll past it3.

  1. webkit.org/blog/3632/introducing-backdrop-filters/
  2. twitter.com/argyleink/status/1456979682344128517
  3. groups.google.com/a/chromium.org/g/blink-dev/c/bkUwigYHJDM/m/Bzvm8tkHAgAJ

The CSS :focus‑visible pseudo-class enables websites to change the appearance of the browser’s default focus outline, which is shown on text inputs and elements focused via the keyboard (see “The :focus‑visible heuristic”1). You may need to do this if the default outline isn’t visible against your design’s background2 (WCAG requires a minimum contrast of 3:1 between outline and background3). If your focus indicator is based on background instead of outline, add a transparent outline as a fallback for Windows High Contrast Mode4. Also consider falling back to :focus (via @supports selector()) for last year’s version of Safari5.

  1. blog.chromium.org/2020/09/giving-users-and-developers-more.html
  2. twitter.com/simevidas/status/1560223916999720961
  3. www.tpgi.com/new-success-criteria-in-wcag22/#focus-appearance
  4. www.matuzo.at/blog/2022/focus-outline/
  5. twitter.com/simevidas/status/1560611832473145345

For legacy reasons, the CSS url() function does not accept CSS variables, e.g., background‑image: url(var(‑‑foo)) is invalid. The CSS Working Group discussed1 this limitation and (in 2020) decided to propose a src() function2 as a modern alternative to url() that does accept variables.

  1. github.com/w3c/csswg-drafts/issues/541
  2. drafts.csswg.org/css-values-4/#urls

You can use a subclass of EventTarget to dispatch and handle custom (synthetic) events independently of the DOM. When dispatching a custom event via the dispatchEvent method1, execution moves synchronously to event listeners that have been registered for events of that type: “When you call dispatchEvent(), all registered listeners are invoked before the next line of your code runs.” Event handlers that are async functions won’t block other event handlers.

  1. developer.mozilla.org/docs/Web/API/EventTarget/dispatchEvent

When using the scrollIntoView method1 to scroll an element on the page into view, browsers will by default scroll the element to the top of the viewport. This will happen even if the element was already fully visible in the viewport. If you don’t want the page to scroll in this case, add the block: 'nearest' option to the method call, e.g., input.scrollIntoView({ block: 'nearest' }). This option behaves similarly to the non-standard scrollIntoViewIfNeeded method2, which isn’t supported in Firefox.

  1. developer.mozilla.org/docs/Web/API/Element/scrollIntoView
  2. developer.mozilla.org/docs/Web/API/Element/scrollIntoViewIfNeeded

The HTML Standard has added a focusVisible option1 for the focus method, e.g., button.focus({ focusVisible: true }). This new option will enable websites to forcibly show the browser’s focus indicator (focus ring) when programmatically focusing an element (e.g., to show a focus ring on the default button when opening a dialog2). This proposed feature is supported by Gecko and Chromium3.

  1. html.spec.whatwg.org/multipage/interaction.html#focus-management-apis
  2. github.com/WICG/focus-visible/issues/268
  3. github.com/whatwg/html/pull/8087

In today’s browsers, the CSS :empty selector1 matches <p></p> and <p><!‑‑ comment ‑‑></p> but not <p> </p>. This behavior is not consistent with the CSS Selectors Level 4 module, which (since 20182) states that :empty should also match elements that contain only white space (spaces, newlines, etc.). Whether browsers will implement this change remains an open question.

  1. www.w3.org/TR/selectors-4/#the-empty-pseudo
  2. github.com/w3c/csswg-drafts/issues/1967#issuecomment-424788796

Unlike with images, defining the aspect ratio of a video via the width and height attributes will not prevent a <video> layout shift in browsers. This is the result of an oversight in the HTML Standard1. As a temporary workaround, you can directly set the CSS aspect‑ratio property on the <video> element to avoid the layout shift2.

  1. github.com/w3c/csswg-drafts/issues/7524
  2. twitter.com/simevidas/status/1557648172549390336

A good way to implement a mutually exclusive button group is via toggle buttons. A toggle button is a <button> element with an aria‑pressed state1. JavaScript is necessary to update the aria‑pressed attributes when the user clicks the buttons (aria‑pressed="true" on the clicked button and aria‑pressed="false" on all other buttons). You can use the CSS [aria‑pressed="true"] selector to style the pressed button.

  1. w3c.github.io/aria/#aria-pressed

Accurate alternative text for images (<img alt> attribute) is necessary for people with vision disabilities, but it also makes it easier to find the image on Google Search1. In general, alternative text is the text that you would have written if you hadn’t been able to include the image2.

  1. twitter.com/simevidas/status/1556877948942155777
  2. html.spec.whatwg.org/multipage/images.html#alt

As of today, a total of 29 policy-controlled features have been standardized1. Websites can use the HTTP Permissions‑Policy (Chromium) and Feature‑Policy (Safari and Firefox) headers to, among other things, prevent its cross-origin iframes from accessing certain features2, such as geolocation and camera.

  1. github.com/w3c/webappsec-permissions-policy/blob/main/features.md
  2. developer.chrome.com/docs/privacy-sandbox/permissions-policy/

If some text on your website is in a different language, and your primary font doesn’t fully support that language’s diacritics (e.g., the popular Lato font doesn’t include the letter Đ1, which is used in some South Slavic languages), you can switch to a more suitable font by marking the text with the HTML lang attribute (e.g., lang="hr") and then using the CSS :lang() selector (e.g., :lang(hr)) to match it and apply a different font‑family.

  1. twitter.com/simevidas/status/1556228230893473793

Since 2020, Firefox has an implementation of masonry layout, which can be enabled on the about:config page (search for “masonry”). This feature is defined in the CSS Grid Layout 3 spec. Mozilla is now waiting for other implementations before masonry layout can be shipped in Firefox1. To switch a grid to masonry layout, add grid‑template‑rows: masonry to the container, and the items will “effectively move up to plug any gaps”2.

  1. bugzilla.mozilla.org/show_bug.cgi?id=1757446#c0
  2. twitter.com/simevidas/status/1555034351456423936

If you use an image’s error event to set src to a fallback image (e.g., <img onerror="this.src='fallback.png'">), and that fallback image for whatever reason also triggers an error, this will result in an infinite recursion in browsers.


You can use CSS Grid to create a full-width component that breaks out of a central wrapper. The grid layout consists of a central column for components, and two flexible columns on each side (e.g., grid‑template‑columns: 1fr minmax(0, 70rem) 1fr). The full-width component then simply spans all three columns.


The web standards community is retiring the term “browser intervention” because spec changes cannot be cleanly categorized into interventions and non-interventions. One of the biggest interventions that shipped in browsers was improving scrolling performance by moving scrolling to the compositor thread, where it can’t be interrupted by JavaScript.


You can use the standard <hgroup> element1 to group a heading and a subheading for styling purposes, but this element doesn’t have any special semantics2 and is not included in the browser’s accessibility tree, which means that <hgroup> is no more accessible than a <div>.

  1. html.spec.whatwg.org/multipage/sections.html#the-hgroup-element
  2. twitter.com/aardrian/status/1543952473693605888