What else do we want or need CSS to do? It’s like being out late at night someplace you shouldn’t be and a stranger in a trenchcoat walks up and whispers in your ear.
“Psst. You wanna buy some async @imports? I’ve got the specificity you want.”
You know you shouldn’t entertain the idea but you do it anyway. All your friends doing Cascade Layers. What are you, a square?
I keep thinking of how amazing it is to write CSS today. There was an email exchange just this morning where I was discussing a bunch of ideas for a persistent set of controls in the UI that would have sounded bonkers even one year ago if it wasn’t for new features, like anchor positioning, scroll timelines, auto-height transitions, and popovers. We’re still in the early days of all these things — among many, many more — and have yet to see all the awesome possibilities come to fruition. Exciting times!
Chris kept a CSS wishlist, going back as far as 2013 and following up on it in 2019. We all have things we’d like to see CSS do and we always will no matter how many sparkly new features we get. Let’s revisit the ones from 2013:
✅ “I’d like to be able to select an element based on if it contains another particular selector.” Hello, :has()!
❌ “I’d like to be able to select an element based on the content it contains.”
❌ “I’d like multiple pseudo-elements.”
✅ “I’d like to be able to animate/transition something to height: auto;” Yep, we got that!
🟠 “I’d like things from Sass, like @extend, @mixin, and nesting.” We got the nesting part down with some progress on mixins.
❌ “I’d like ::nth-letter, ::nth-word, etc.”
✅ “I’d like all the major browsers to auto-update.” This one was already fulfilled.
So, about a score of 3.5 out of 7. It could very well be that some of these things fell out of favor at some point (haven’t heard any crying for a new pseudo-element since the first wishlist). Chris re-articulated the list this way:
Parent queries. As in, selecting an element any-which-way, then selecting the parent of that element. We have some proof it’s possible with :focus-within.
Container queries. Select a particular element when the element itself is under certain conditions.
But what else is on your CSS wishlist? Ironically enough, Adam Argyle went through this exercise just this morning and I love the way he’s broken things down into a user-facing wishlist and a developer-facing wishlist. I mean, geez, a CSS carousel? Yes, please! I love his list and all lists like it.
We’ll round things up and put a list together — so let us know!
What else do we want or need CSS to do? It’s like being out late at night someplace you shouldn’t be and a stranger in a trenchcoat walks up and whispers in your ear.
“Psst. You wanna buy some async @imports? I’ve got the specificity you want.”
You know you shouldn’t entertain the idea but you do it anyway. All your friends doing Cascade Layers. What are you, a square?
I keep thinking of how amazing it is to write CSS today. There was an email exchange just this morning where I was discussing a bunch of ideas for a persistent set of controls in the UI that would have sounded bonkers even one year ago if it wasn’t for new features, like anchor positioning, scroll timelines, auto-height transitions, and popovers. We’re still in the early days of all these things — among many, many more — and have yet to see all the awesome possibilities come to fruition. Exciting times!
Chris kept a CSS wishlist, going back as far as 2013 and following up on it in 2019. We all have things we’d like to see CSS do and we always will no matter how many sparkly new features we get. Let’s revisit the ones from 2013:
✅ “I’d like to be able to select an element based on if it contains another particular selector.” Hello, :has()!
❌ “I’d like to be able to select an element based on the content it contains.”
❌ “I’d like multiple pseudo-elements.”
✅ “I’d like to be able to animate/transition something to height: auto;” Yep, we got that!
🟠 “I’d like things from Sass, like @extend, @mixin, and nesting.” We got the nesting part down with some progress on mixins.
❌ “I’d like ::nth-letter, ::nth-word, etc.”
✅ “I’d like all the major browsers to auto-update.” This one was already fulfilled.
So, about a score of 3.5 out of 7. It could very well be that some of these things fell out of favor at some point (haven’t heard any crying for a new pseudo-element since the first wishlist). Chris re-articulated the list this way:
Parent queries. As in, selecting an element any-which-way, then selecting the parent of that element. We have some proof it’s possible with :focus-within.
Container queries. Select a particular element when the element itself is under certain conditions.
But what else is on your CSS wishlist? Ironically enough, Adam Argyle went through this exercise just this morning and I love the way he’s broken things down into a user-facing wishlist and a developer-facing wishlist. I mean, geez, a CSS carousel? Yes, please! I love his list and all lists like it.
We’ll round things up and put a list together — so let us know!
Superscripts and subscripts are essential elements in academic and scientific content — from citation references to chemical formulas and mathematical expressions. Yet browsers handle these elements with a static approach that can create significant problems: elements become either too small on mobile devices or disproportionately large on desktop displays.
After years of wrestling with superscript and subscript scaling in CSS, I’m proposing a modern solution using fluid calculations. In this article, I’ll show you why the static approach falls short and how we can provide better typography across all viewports while maintaining accessibility. Best of all, this solution requires nothing but clean, pure CSS.
The problem with static scaling
The scaling issue is particularly evident when comparing professional typography with browser defaults. Take this example (adapted from Wikipedia), where the first “2” is professionally designed and included in the glyph set, while the second uses <sub> (top) and <sup> (bottom) elements:
Browsers have historically used font-size: smaller for <sup> and <sub> elements, which translates to roughly 0.83x scaling. While this made sense in the early days of CSS for simple documents, it can create problems in modern responsive designs where font sizes can vary dramatically. This is especially true when using fluid typography, where text sizes can scale smoothly between extremes.
Fluid scaling: A better solution
I’ve developed a solution that scales more naturally across different sizes by combining fixed and proportional units. This approach ensures legibility at small sizes while maintaining proper proportions at larger sizes, eliminating the need for context-specific adjustments.
Natural scaling: The degressive formula ensures that superscripts and subscripts remain proportional at all sizes
Baseline alignment: By using vertical-align: baseline and relative positioning, we prevent the elements from affecting line height and it gives us better control over the offset to match your specific needs. You’re probably also wondering where the heck these values come from — I’ll explain in the following.
Breaking down the math
Let’s look at how this works, piece by piece:
Calculating the font size (px)
At small sizes, the fixed 4px component has more impact. At large sizes, the 0.5em proportion becomes dominant. The result is more natural scaling across all sizes.
sup, sub {
font-size: calc(0.5em + 4px);
/* ... */
}
sub {
/* ... */
}
Calculating the parent font size (em)
Within the <sup> and <sub> elements, we can calculate the parent’s font-size:
The fluid font size is defined as calc(0.5em + 4px). To compensate for the 0.5em, we first need to solve 0.5em * x = 1em which gives us x = 2. The 1em here represents the font size of the <sup> and <sub> elements themselves. We subtract the 4px fixed component from our current em value before multiplying.
The vertical offset
For the vertical offset, we start with default CSS positioning values and adjust them to work with our fluid scaling:
The formula is carefully calibrated to match standard browser positioning:
0.5em (super) and 0.25em (sub) are the default vertical offset values (e.g. used in frameworks like Tailwind CSS and Bootstrap).
We multiply by 0.83 to account for the browser’s font-size: smaller scaling factor, which is used per default for superscript and subscript.
This approach ensures that our superscripts and subscripts maintain familiar vertical positions while benefiting from improved fluid scaling. The result matches what users expect from traditional browser rendering but scales more naturally across different font sizes.
Helpful tips
The exact scaling factor font-size: (0.5em + 4px) is based on my analysis of superscript Unicode characters in common fonts. Feel free to adjust these values to match your specific design needs. Here are a few ways how you might want to customize this approach:
I built this small interactive demo to show different fluid scaling options, compare them to the browser’s static scaling, and fine-tune the vertical positioning to see what works best for your use case:
I strongly believe Anchor Positioning will go down as one of the greatest additions to CSS. It may not be as game-changing as Flexbox or Grid, but it does fill a positioning gap that has been missing for decades. As awesome as I think it is, CSS Anchor Positioning has a lot of quirks, some of which are the product of its novelty and others due to its unique way of working. Today, I want to bring you yet another Anchor Positioning quirk that has bugged me since I first saw it.
The inception
It all started a month ago when I was reading about what other people have made using Anchor Positioning, specifically this post by Temani Afif about “Anchor Positioning & Scroll-Driven Animations.” I strongly encourage you to read it and find out what caught my eye there. Combining Anchor Positioning and Scroll-Driven Animation, he makes a range slider that changes colors while it progresses.
CodePen Embed Fallback
Amazing by itself, but it’s interesting that he is using two target elements with the same anchor name, each attached to its corresponding anchor, just like magic. If this doesn’t seem as interesting as it looks, we should then briefly recap how Anchor Positioning works.
CSS Anchor Positioning and the anchor-scope property
Anchor Positioning brings two new concepts to CSS, an anchor element and a target element. The anchor is the element used as a reference for positioning other elements, hence the anchor name. While the target is an absolutely-positioned element placed relative to one or more anchors.
An anchor and a target can be almost every element, so you can think of them as just two div sitting next to each other:
To start, we first have to register the anchor element in CSS using the anchor-name property:
.anchor {
anchor-name: --my-anchor;
}
And the position-anchor property on an absolutely-positioned element attaches it to an anchor of the same name. However, to move the target around the anchor we need the position-area property.
.target {
position: absolute;
position-anchor: --my-anchor;
position-area: top right;
}
CodePen Embed Fallback
This works great, but things get complicated if we change our markup to include more anchors and targets:
Instead of each target attaching to its closest anchor, they all pile up at the last registered anchor in the DOM.
CodePen Embed Fallback
The anchor-scope property was introduced in Chrome 131 as an answer to this issue. It limits the scope of anchors to a subtree so that each target attaches correctly. However, I don’t want to focus on this property, because what initially caught my attention was that Temani didn’t use it. For some reason, they all attached correctly, again, like magic.
What’s happening?
Targets usually attach to the last anchor on the DOM instead of their closest anchor, but in our first example, we saw two anchors with the same anchor-name and their corresponding targets attached. All this without the anchor-scope property. What’s happening?
Two words: Containing Block.
Something to know about Anchor Positioning is that it relies a lot on how an element’s containing block is built. This isn’t something inherently from Anchor Positioning but from absolute positioning. Absolute elements are positioned relative to their containing block, and inset properties like top: 0px, left: 30px or inset: 1rem are just moving an element around its containing block boundaries, creating what’s called the inset-modified containing block.
A target attached to an anchor isn’t any different, and what the position-area property does under the table is change the target’s inset-modified containing block so it is right next to the anchor.
Usually, the containing block of an absolutely-positioned element is the whole viewport, but it can be changed by any ancestor with a position other than static (usually relative). Temani takes advantage of this fact and creates a new containing block for each slider, so they can only be attached to their corresponding anchors. If you snoop around the code, you can find it at the beginning:
label {
position: relative;
/* No, It's not useless so don't remove it (or remove it and see what happens) */
}
If we use this tactic on our previous examples, suddenly they are all correctly attached!
CodePen Embed Fallback
Yet another quirk
We didn’t need to use the anchor-scope property to attach each anchor to its respective target, but instead took advantage of how the containing block of absolute elements is computed. However, there is yet another approach, one that doesn’t need any extra bits of code.
This occurred to me when I was also experimenting with Scroll-Driven Animations and Anchor Positioning and trying to attach text-bubble footnotes on the side of a post, like the following:
Logically, each footnote would be a target, but the choice of an anchor is a little more tricky. I initially thought that each paragraph would work as an anchor, but that would mean having more than one anchor with the same anchor-name. The result: all the targets would pile up at the last anchor:
CodePen Embed Fallback
This could be solved using our prior approach of creating a new containing block for each note. However, there is another route we can take, what I call the reductionist method. The problem comes when there is more than one anchor with the same anchor-name, so we will reduce the number of anchors to one, using an element that could work as the common anchor for all targets.
In this case, we just want to position each target on the sides of the post so we can use the entire body of the post as an anchor, and since each target is naturally aligned on the vertical axis, what’s left is to move them along the horizontal axis:
CodePen Embed Fallback
You can better check how it was done on the original post!
Conclusion
The anchor-scope may be the most recent CSS property to be shipped to a browser (so far, just in Chrome 131+), so we can’t expect its support to be something out of this world. And while I would love to use it every now and there, it will remain bound to short demos for a while. This isn’t a reason to limit the use of other Anchor Positioning properties, which are supported in Chrome 125 onwards (and let’s hope in other browsers in the near future), so I hope these little quirks can help you to keep using Anchor Positioning without any fear.
Join the Chrome DevRel team and a skateboarding Chrome Dino on a journey through the latest CSS launched for Chrome and the web platform in 2024, highlighting 17 new features
The CSS Working Group (CSSWG) meets weekly (or close to it) to discuss and quickly resolve issues from their GitHub that would otherwise be lost in the back-and-forth of forum conversation. While each meeting brings interesting conversation, this past Wednesday (December 4th) was special. The CSSWG met to try and finally squash a debate that has been going on for five years: whether Masonry should be a part of Grid or a separate system.
In 2017, it was frequently asked whether Grid could handle masonry layouts; layouts where the columns (or the rows) could hold unevenly sized items without gaps in between. While this is just one of several possibilities with masonry, you can think about the layout popularized by Pinterest:
In 2020, Firefox released a prototype in which masonry was integrated into the CSS Grid layout module. The main voice against it was Rachel Andrew, arguing that it should be its own, separate thing. Since then, the debate has escalated with two proposals from Apple and Google, arguing for and against a grid-integrated syntax, respectively.
There were some technical worries against a grid-masonry implementation that were since resolved. What you have to know is this: right now, it’s a matter of syntax. To be specific, which syntax is
a. is easier to learn for authors and
b. how might this decision impact possible future developments in one or both models (or CSS in general).
In the middle, the W3C Technical Architecture Group (TAG) was asked for input on the issue which has prompted an effort to unify the two proposals. Both sides have brought strong arguments to the table over a series of posts, and in the following meeting, they were asked to lay those arguments once again in a presentation, with the hope of reaching a consensus.
Remember that you can subscribe and read the full minutes on W3C.org
The Battle of PowerPoints
Alison Maher representing Google and an advocate of implementing Masonry as a new display value, opened the meeting with a presentation. The main points were:
Several properties behave differently between masonry and grid.
There was an argument against display: masonry since fallbacks would be more lengthy to implement, whereas in a grid-integrated the fallback to grid is already there. Alison Maher refutes this since “needing one is a temporary problem, so [we] should focus on the future,” and that “authors should make explicit fallback, to avoid surprises.”
“Positioning in masonry is simpler than grid, it’s only placed in 1 axis instead of 2.”
Shorthands are also better: “Grid shorthand is complicated, hard to use. Masonry shorthand is easier because don’t need to remember the order.”
“Placement works differently in grid vs masonry” and “alignment is also very different”
There will be “other changes for submasonry/subgrid that will lead to divergences.”
“Integrating masonry into grid will lead to spec bloat, will be harder to teach, and lead to developer confusion.”
alisonmaher: “Conclusion: masonry should be a separate display type”
Jen Simmons, representing the WebKit team and advocate of the “Just Use Grid” approach followed with another presentation. On this side, the main points were:
Author learning could be skewed since “a new layout type creates a separate tool with separate syntax that’s similar but not the same as what exists […]. They’re familiar but not quite the same”
The Chrome proposal would add around 10 new properties. “We don’t believe there’s a compelling argument to add so many new properties to CSS.”
“Chromium argues that their new syntax is more understandable. We disagree, just use grid-auto-flow“
“When you layout rows in grid, template syntax is a bit different — you stack the template names to physically diagram the names for the rows. Just Use Grid re-uses this syntax exactly; but new masonry layout uses the column syntax for rows”
“Other difference is the auto-flow — grid’s indicates the primary fill direction, Chrome believes this doesn’t make sense and changed it to match the orientation of lines”
“Chrome argues that new display type allows better defaults — but the defaults propose aren’t good […] it doesn’t quite work as easily as claimed [see article] requires deep understanding of autosizing”
“Easier to switch, e.g. at breakpoints or progressive enhancement”
“Follows CSS design principles to re-use what already exists”
The TAG review
After two presentations with compelling arguments, Lea Verou (also a member of the TAG) followed with their input.
lea: We did a TAG review on this. My opinion is fully reflected there. I think the arguments WebKit team makes are compelling. We thought not only should masonry be part of grid, but should go further. A lot of arguments for integrating is that “grid is too hard”. In that case we should make grid things easier. Complex things are possible, but simple things are not so easy.
Big part of Google’s argument is defaults, but we could just have smarter defaults — there is precedent for this in CSS if we decided that would help ergonomics We agree that switching between grid vs. masonry is common. Grid might be a slightly better fallback than nothing, but minor argument because people can use @supports. Introducing all these new properties increasing the API surfaces that authors need to learn. Less they can port over. Even if we say we will be disciplined, experience shows that we won’t. Even if not intentional, accidental. DRY – don’t have multiple sources of truth
One of arguments against masonry in grid is that grids are 2D, but actually in graphic design grids were often 1D. I agree that most masonry use cases need simpler grids than general grid use cases, but that means we should make those grids easier to define for both grid and masonry. The more we looked into this, we realize there are 3 different layout modes that give you 2D arrangement of children. We recommended not just make masonry part of grid, but find ways of integrating what we already have better could we come up with a shorthand that sets grid-auto-flow and flex-direction, and promote that for layout direction in general? Then authors only need to learn one control for it.
The debate
All was laid out onto the table, it was only left what other members had to say.
oriol: Problem with Jen Simmons’s reasoning. She said the proposed masonry-direction property would be new syntax that doesn’t match grid-auto-flow property, but this property matches flex-direction property so instead of trying to be close to grid, tries to be close to flexbox. Closer to grid is a choice, could be consistent with different things.
astearns: One question I asked is, has anyone changed their mind on which proposal they support? I personally have. I thought that separate display property made a lot more sense, in terms of designing the feature and I was very daunted by the idea that we’d have to consider both grid and masonry for any new development in either seemed sticky to me but the TAG argument convinced me that we should do the work of integrating these things.
TabAtkins: Thanks for setting that up for me, because I’m going to refute the TAG argument! I think they’re wrong in this case. You can draw a lot of surface-level connections between Grid and Masonry, and Flexbox, and other hypothetical layouts but when you actually look at details of how they work, behaviors each one is capable of, they’re pretty distinct if you try to combine together, it would be an unholy mess of conflicting constraints — e.g. flexing in items of masonry or grid or you’d have a weird mish-mash of, “the 2D layout.
But if you call it a flex you get access to these properties, call it grid, access to these other properties concrete example, “pillar” example mentioned in webKit blog post, that wasn’t compatible with the base concepts in masonry and flex because it wants a shared block formatting context grid etc have different formatting contexts, can’t use floats.
lea: actually, the TAG argument was that layout seems to actually be a continuum, and syntax should accommodate that rather than forcing one of two extremes (current flex vs current grid).
The debate kept back and forth until there was an attempt to set a general north star to follow.
jyasskin: Wanted to emphasize a couple aspects of TAG review. It seems really nice to keep the property from Chrome proposal that you don’t have to learn both, can just learn to do masonry without learning all of Grid even if that’s in a unified system perhaps still define masonry shorthand, and have it set grid properties
jensimmons: To create a simple masonry-style layout in Grid, you just need 3 lines of code (4 with a gap). It’s quite simple.
jyasskin: Most consensus part of TAG feedback was to share properties whenever possible. Not necessary to share the same ‘display’ values; could define different ‘display’ values but share the properties. One thing we didn’t like about unified proposal was grid-auto-flow in the unified proposal, where some values were ignored. Yeah, this is the usability point I’m pounding on
Another Split Decision
Despite all, it looked like nobody was giving away, and the debate seemed stuck once again:
astearns: I’m not hearing a way forward yet. At some point, one of the camps is going to have to concede in order to move this forward.
lea: What if we do a straw poll. Not to decide, but to figure out how far are we from consensus?
The votes were cast and the results were… split.
florian: though we could still not reach consensus, I want to thank both sides for presenting clear arguments, densely packed, well delivered. I will go back to the presentations, and revisit some points, it really was informative to present the way it was.
That’s all folks, a split decision! There isn’t a preference for either of the two proposals and implementing something with such mixed opinions is something nobody would approve. After a little over five years of debate, I think this meeting is yet another good sign that a new proposal addressing the concerns of both sides should be considered, but that’s just a personal opinion. To me, masonry (or whatever name it may be) is an important step in CSS layout that may shape future layouts, it shouldn’t be rushed so until then, I am more than happy to wait for a proposal that satisfies both sides.
What? A front-ender interviewing really smart people about their processes for user research, documenting requirements, and scaling teams around usability? I was a product designer once upon a time and even though it’s been a long time since I’ve flexed that muscle, it was a hoot learning from the guests, which included: Chris Kolb, Kevin Hawkins, and Vicky Carmichael.
The videos are barred from embedding, so I’ll simply link ’em up directly to YouTube:
Small teams have the luxury of being in greater, more intimate contact with customers. Vicky explained how their relatively small size (~11 employees) means that everyone interfaces with customers and that customer issues and requests are handled more immediately.
Large teams have to be mindful of teams forming into individual silos. A silo mentality typically happens when teams scale up in size, resulting in less frequent communication and collaboration. Team dashboards help, as do artifacts from meetings in multiple formats, such as AI-flavored summaries, video recordings, and documented decisions.
Customers may appear to be dumb, but what looks like dumbness is often what happens when humans are faced with a lack of time and context. Solving “dumb” user problems often means coming at the problem in the same bewildered context rather than simply assuming the customer “just doesn’t get it.”
Alt text is one of those things in my muscle memory that pops up anytime I’m working with an image element. The attribute almost writes itself.
<img src="image.jpg" alt="">
Or if you use Emmet, that’s autocompleted for you. Don’t forget the alt text! Use it even if there’s no need for it, as an empty string is simply skipped by screen readers. That’s called “nulling” the alternative text and many screen readers simply announce the image file name. Just be sure it’s truly an empty string because even a space gets picked up by some assistive tech, which causes a screen reader to completely skip the image:
Probably is doing a lot of lifting there because not all images are equal when it comes to content and context. Emma Cionca and Tanner Kohler have a fresh study on those situations where you probably don’t need alt. It’s a well-written and researched piece and I’m rounding up some nuggets from it.
What Users Need from Alt Text
It’s the same as what anyone else would need from an image: an easy path to accomplish basic tasks. A product image is a good example of that. Providing a visual smooths the path to purchasing because it’s context about what the item looks like and what to expect when you get it. Not providing an image almost adds friction to the experience if you have to stop and ask customer support basic questions about the size and color of that shirt you want.
So, yes. Describe that image in alt! But maybe “describe” isn’t the best wording because the article moves on to make the next point…
Quit Describing What Images Look Like
The article gets into a common trap that I’m all too guilty of, which is describing an image in a way that I find helpful. Or, as the article says, it’s a lot like I’m telling myself, “I’ll describe it in the alt text so screen-reader users can imagine what they aren’t seeing.”
That’s the wrong way of going about it. Getting back to the example of a product image, the article outlines how a screen reader might approach it:
For example, here’s how a screen-reader user might approach a product page:
Jump between the page headers to get a sense of the page structure.
Explore the details of a specific section with the heading label Product Description.
Encounter an image and wonder “What information that I might have missed elsewhere does this image communicate about the product?”
Interesting! Where I might encounter an image and evaluate it based on the text around it, a screen reader is already questioning what content has been missed around it. This passage is one I need to reflect on (emphasis mine):
Most of the time, screen-reader users don’t wonder what images look like. Instead, they want to know their purpose. (Exceptions to this rule might include websites presenting images, such as artwork, purely for visual enjoyment, or users who could previously see and have lost their sight.)
OK, so how in the heck do we know when an image needs describing? It feels so awkward making what’s ultimately a subjective decision. Even so, the article presents three questions to pose to ourselves to determine the best route.
Is the image repetitive? Is the task-related information in the image also found elsewhere on the page?
Is the image referential? Does the page copy directly reference the image?
Is the image efficient? Could alt text help users more efficiently complete a task?
This is the meat of the article, so I’m gonna break those out.
Is the image repetitive?
Repetitive in the sense that the content around it is already doing a bang-up job painting a picture. If the image is already aptly “described” by content, then perhaps it’s possible to get away with nulling the alt attribute.
This is the figure the article uses to make the point (and, yes, I’m alt-ing it):
The caption for this image describes exactly what the image communicates. Therefore, any alt text for the image will be redundant and a waste of time for screen-reader users. In this case, the actual alt text was the same as the caption. Coming across the same information twice in a row feels even more confusing and unnecessary.
The happy path:
<img src="image.jpg" alt="">
But check this out this image about informal/semi-formal table setting showing how it is not described by the text around it (and, no, I’m not alt-ing it):
If I was to describe this image, I might get carried away describing the diagram and all the points outlined in the legend. If I can read all of that, then a screen reader should, too, right? Not exactly. I really appreciate the slew of examples provided in the article. A sampling:
Bread plate and butter knife, located in the top left corner.
Dessert fork, placed horizontally at the top center.
Dessert spoon, placed horizontally at the top center, below the dessert fork.
The second image I dropped in that last section is a good example of a referential image because I directly referenced it in the content preceding it. I nulled the alt attribute because of that. But what I messed up is not making the image recognizable to screen readers. If the alt attribute is null, then the screen reader skips it. But the screen reader should still know it’s there even if it’s aptly described.
The happy path:
<img src="image.jpg" alt="">
Remember that a screen reader may announce the image’s file name. So maybe use that as an opportunity to both call out the image and briefly describe it. Again, we want the screen reader to announce the image if we make mention of it in the content around it. Simply skipping it may cause more confusion than clarity.
Is the image efficient?
My mind always goes to performance when I see the word efficient pop up in reference to images. But in this context the article means whether or not the image can help visitors efficiently complete a task.
If the image helps complete a task, say purchasing a product, then yes, the image needs alt text. But if the content surrounding it already does the job then we can leave it null (alt="") or skip it (alt=" ") if there’s no mention of it.
Wrapping up
I put a little demo together with some testing results from a few different screen readers to see how all of that shakes out.