Tech News
What’s !important #3: Popover Context Menus, @scope, New Web Platform Features, and More
The developer community hasn’t wasted any time kicking off 2026 with some really great articles, demos, and insights. Firefox 147 and Chrome 144 also shipped, and while they’re not jam-packed with features, the releases are still pretty exciting for what’s normally a slow time of year, so without further ado, here’s what’s important from the last couple of weeks (or should I say the first couple of weeks, of 2026?)…
Building popover context menus with anchor positioningChris Coyier (a familiar name, perhaps) shows us how to build context menus using popovers and anchor positioning over at Frontend Masters. Interest invokers, <menu>, discrete transitions, @starting-style, and fallback positions are also mentioned, so grab a pickaxe, because this one’s a bit of a goldmine.
Also, anchor positioning went baseline this week, so you can use it on production websites now! Do we have our CSS feature of the year already?
Scoping CSS with @scopeFunnily enough, I also got the opportunity to write something for Frontend Masters, and I went with @scope. @scope has been my most-anticipated CSS feature for quite a while now, and Firefox shipping it in their final release of the year (making it baseline) made it my feature of the year, so I’m very happy to kick off 2026 with this little how-to on using @scope and scoping CSS overall.
Generating gradient borders from an image sourceIn this demo, created and posted by Ana Tudor on Bluesky, Ana blurs an image and masks it with a border. You can actually accomplish this in Safari using just three lines of CSS, but the cross-browser solution isn’t too complex either (the key parts are the backdrop-filter and mask CSS properties).
Given the current popularity of gradients, blurs, and dare I say it, glass, it’s a pretty sweet effect that you can probably adapt for other scenarios.
Offset gradient border from img source – how would you get the result from the screen below? Real gap transparency, border gradient obtained from the image.
My solutions on @codepen.io:
Safari only in 3 declarations codepen.io/thebabydino/…
Cross-browser
codepen.io/thebabydino/…
#CSS #filter
[image or embed]
HTML, like CSS, is soooo good now. That being said, even though we’ve been getting all these new HTML elements that enable us to build interactive components without JavaScript, that doesn’t necessarily mean that we should. Stephen Margheim says that tab components are over-engineered most of the time, and explains why and what you can do instead.
A hot take after seeing yet another fancy tabs design: the classic "tab component" is over-engineered for 90% of use cases. You probably don't need it…
— Stephen Margheim (@fractaledmind.bsky.social) 3 January 2026 at 19:57 Using your OS as a CMSSpeaking of simplicity, Jim Nielsen introduced me to this incredibly cool OS-as-a-CMS concept as he explains how he got “Edit Post” buttons on his website to open the local document on his computer in iA Writer, completely negating the need for a CMS. Jim walks you through the whole thing, but the key ingredient is just a little link with a custom URL scheme:
<a href="ia-writer://open?path=posts:post.md">Edit</a>I love this because I also write in Markdown (using iA Writer, no less), and could will easily integrate this into my Eleventy build. But it got me thinking: do any other apps have their own URL scheme? Well, as it turns out, some of them do! Here’s an incomplete list (with examples of ‘edit’ commands for each app):
- Obsidian: obsidian://open?vault=posts&file=post
- VS Code: vscode://exact/path/to/post.md:9:1 (:9:1 is the line and column number)
- Ulysses: ulysses://x-callback-url/open-item?id=ITEM_ID
- Sublime Text (with subl protocol): subl://exact/path/to/post.md:9:1
- Apple Shortcuts: shortcuts://run-shortcut?name=Edit&input=/path/to/post.md (great for doing stuff with apps that don’t have custom URL schemes)
As you know (hopefully?), we post Quick Hits throughout the week. The best way to find them is in the sidebar of the homepage, and they’re either links to things that you can read in just a minute or two, or just PSAs to read and remember. Anyway, here’s what you might’ve missed:
- HTML Document Outline died (it was never really alive, to be fair)
- Scroll-fade didn’t die, but we’re hoping that it does
- Chris Coyier quickly explained how !important works with custom properties
- ReliCSS shipped, which helps you to replace any CSS hacks with @supports and modern CSS
- Temani Afif coded Mario World with CSS
Ready for the first cool demo of the year?
A mini Mario world with keyboard control. Yes, you can move Mario and also jump! 👀
Demo: codepen.io/t_afif/full/… via @codepen.io
✔️ 100% CSS Magic
✔️ Minimal HTML
❌ 0% JavaScript
A Chrome-only experimentation using modern CSS.
[image or embed]
And finally, here are my top picks from what Firefox and Chrome shipped on Tuesday:
- Firefox 147
- Anchor positioning (now Baseline!)
- View transition types (also Baseline!)
- Navigation API (a now-Baseline JavaScript API that replaces the History API and window.location)
- Chrome 144
- ::search-text (only Chrome supports this pseudo-element that selects highlighted find-in-page text)
- <geolocation> (only Chrome supports this HTML element that requests the user’s location declaratively)
- Temporal API (Chrome joins Firefox in supporting this replacement for JavaScript’s Date API, which Piccalilli wrote about)
Thanks for tuning in. I’ll see you in two weeks! Be there or be square (aspect-ratio: 1)!
What’s !important #3: Popover Context Menus, @scope, New Web Platform Features, and More originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.
AI Enables As-Needed Software Features
In traditional software development, designers and engineers anticipate what people might need, build those features, and then ship them. When integrated into an application, AI code generation upends this sequence. People can just describe what they want and the app writes the code needed to do it on demand.
Reve's recent launch of Effects illustrates this transition. Want a specific film grain look for your image or video? Just describe it in plain language or upload an example. Reve's AI agent will write code that produces the effect you want and figure out what parameters should be adjustable. Those parameters then become sliders in an interface built for you in real-time.
Instead of having to find the menu item for an existing filter (if it even exists) in traditional software, you just say what you want and the system constructs it right then and there.
When applications can generate capabilities on demand, the definition of "what this product does" becomes more fluid. Features aren't just what shipped in the last release, they're also what users will ask for in the next session. The application becomes a platform for creating its own abilities, guided by user intent rather than predetermined roadmaps.
Playing With CodePen slideVars
Super cool new CodePen feature alert! You’ve probably seen a bunch of “interactive” demos that let you change values on the fly from a UI panel embedded directly in the demo. Jhey’s demos come immediately to mind, like this one:
CodePen Embed FallbackThat’s a tool called TweakPane doing the work. There’s another one called Knobs by Yair Even Or that Adam Argyle often uses:
CodePen Embed FallbackI’ve often faked it with either the Checkbox Hack or a sprinkle of JavaScript when I’m demoing a very specific concept:
CodePen Embed FallbackOK, enough examples because CodePen has a homegrown tool of its own called slideVars. All you have to do is import it and call it in the JavaScript panel:
import { slideVars } from "@codepen/slidevars"; slideVars.init();You can import it into a project off CodePen if you’re so inclined.
That two-liner does a lot of lifting. It auto-detects CSS variables in your CSS and builds the panel for you, absolutely-positioned in the top-right corner:
CodePen Embed FallbackIt looks like you have to declare your variables on the :root element with default usage. I tried scoping them directly to the element and it was a no-go. It’s possible with a manual configuration, though.
CodePen Embed FallbackPretty cool, right? You can manually configure the input type, a value range, a default value, unit type, and yes, a scope that targets the element where the variables are defined. As far as units go, it supports all kinds of CSS numeric units. That includes unit-less values, though the documentation doesn’t explicitly say it. Just leave the unit property as an empty string ("").
I guess the only thing I’d like is to tell slideVars exactly what increments to use when manually configuring things. For example, unit-less values simply increment in integers, even if you define the default value as a decimal:
CodePen Embed FallbackIt works in default mode, however:
CodePen Embed FallbackThere’s a way to place the slideVars wherever you want by slapping a custom element where you want it in the HTML. It’s auto-placed at the bottom of the HTML <body> by default.
<slide-vars> <p>Custom Label!</p> </slide-vars>Or CSS it by selecting the custom element:
CodePen Embed FallbackSo much fun!
CodePen Embed FallbackPlaying With CodePen slideVars originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.
Postcard From Web Directions Dev Summit, 2025
Author’s Note: There are already wonderful recaps of the Web Directions Developer Summit I spoke at in November 2025. So, rather than offering another one, I decided to capture my experience at the conference in a stream-of-consciousness style that details my battles with stage fright and imposter syndrome. I haven’t seen this style used on a tech blog before, but CSS-Tricks has become my playground for experiments — not just with CSS, but with language itself — so let’s see where this experiment takes us.
ArrivalWhen I was a kid, there used to be a Museum railway station in Melbourne, Australia. In 1995, it changed its name to match the shopping center above it — a microcosm of how the mentality of my home city has shifted — but Sydney still has a Museum station. The aesthetics of Sydney’s Museum Station evoke London Underground vibes as my train from Sydney Airport stops under Hyde Park, the oldest public park in Australia and the first to be named after its more famous London counterpart.
Britain’s on my brain because I want this trip to resemble the Harry Potter stories: the wish-fulfillment narrative of discovering you have special powers and are chosen. In truth, the way I was selected to speak at the Web Directions Dev Summit this year wasn’t so spontaneous.
The organizer, John Allsopp, recommended my article “How to Discover a CSS Trick” on his reading list and connected with me on LinkedIn. I took the opportunity to pitch via direct message for a talk about scrolling since the proposal form on the Web Directions website felt comparatively impersonal. But now, what feels impersonal and daunting is the parallel-universe version of a train station that doesn’t exist back home except in my memory. Stepping onto the platform like an eleventh-hour rehearsal for the stage, I feel less like the Harry Potter of CSS and more like I’ve signed up to be a novelty museum exhibit. Step right up and laugh at the middle-aged dude who writes bizarre articles featuring a fictional seller of haunted CSS tricks who cursed him to overuse CSS for everything.
The spooky CSS shopkeeper is a figment of my imagination based on watching too many Simpsons reruns — but now I’ve manifested a real-life froghurt situation: a free conference ticket and trip to Sydney in exchange for embarrassing myself in front of the largest audience I’ve ever spoken to.
I procrastinate preparation by sitting down for frozen yoghurt in the Sydney CBD. The froghurt is yummy, but cursed by the cloud of anxiety following me around on this sunny day. So I’ll procrastinate describing my own talk to you by first sharing a few of my favorites from others.
Day OneI’ve arrived and the event kicks off.
Welcome: John AllsoppThe moment John takes the stage, I’m struck by his showmanship in subverting assumptions about his enthusiasm for tech. He opens by saying he feels ennui with web development, yet hopes the lineup over the next two days might snap him out of his pessimism about the web’s future.
It’s the conference equivalent of the literary technique of a frame story: He positions himself as a weary sage who will reappear after each talk for Q&A — and yet, as someone who predates PCs, he has greyed like an unavailable option on a computer screen. He fears he has seen too much to feel optimistic about the future of the web he helped to build.
He says front-end development has reached a “local maximum,” borrowing a term from calculus to explain how the tools that got us here have flattened our rate of change. The productivity boost is offset by the ways our tools limit imagination. Our mental models make it easy to build the same websites again and again, keeping us out of touch with what modern browsers can do.
He cites the View Transitions API — available as a progressive enhancement since 2023 — as an example of a native browser superpower that could subvert the SPA model, yet remains only experimentally supported in React.
The dramatic context for the next two days is now set. The web sucks, but prove him wrong, kids.
“The Browser Strikes Back: Rethinking the Modern Dev Stack” by Jono Alderson“You’re gonna hate me,” says the keynote speaker Jono Alderson at the top of his talk on rethinking the modern dev stack.
He argues that frameworks like React are Rube Goldberg machines built around limitations that no longer exist. He compares them to Netflix’s DVD-by-mail era: We’re still sending discs when we could be streaming.
He runs through browser capabilities in 2025 that we routinely overlook when we reflexively reach for frameworks — and includes a teaser slide for my later talk on scroll timelines. I feel a sense of belonging and dread simultaneously, like passing the chicken exit on Space Mountain.
In the break, Jono admits to me that he was nervous about triggering anger by bashing frameworks. I hope the audience is warming to favoring the platform, because my talk shares that same underlying spirit, albeit through the specific example of CSS Scroll-Driven Animations. It helps that Jono served as frontline fodder, since research shows that everything sounds more credible with a British accent, even if Jono’s was slightly slurred from jet lag.
Whether he’s right about nuking frameworks or not, it’s healthy to reassess whether we need a dependency list longer than our screen port. I first questioned this in 2015 after watching Joe Gregorio argue we should stop using frameworks and rely on the platform — a talk that, in hindsight, looked suspiciously like guerrilla marketing for Google Polymer. I adopted Polymer for a major project. It was more like a framework than a library, but with the “bonus” of not being battle-tested like React: it had its own weird build process, reliance on a browser feature that never became a standard, and a promised future that never arrived. I ended up rewriting everything. Eventually, Polymer itself was quietly put out of its misery.
Even so, I love the idea of web components: transforming the browser into something built for the way we already force it to behave. A decade later, has the situation improved enough to yarn remove React? The answer may go beyond browser capability in 2025.
Over coffee, Jono and I discuss how LLMs are trained on oceans of React, reinforcing the assumption that every web app must be an SPA. Escaping React is harder than ever when the future of work is dragging us back into the past, much the way recommendation algorithms on social media trap us in our own echo chambers.
“It’s only gonna get worse,” says Jono.
And I guess it will, unless we start creating good examples of what browsers can do without dependencies.
“Supercharged Scrolling With CSS” by Me Photo credit: Kris HowardIt’s debatable whether you should admit you’re nervous while giving a talk. Most say you shouldn’t. The balance I strike is to open with a self-deprecating joke as a way to get the scrolling discussion rolling.
“I have a feeling some of you might be scrolling on your devices as we speak, so I urge you to look up — and let’s scroll together for the next half hour.”
It gets a laugh. It’s a moment where I translate my CSS-Tricks article style — self-referential, breaking the fourth wall — into something that works on stage. This is my challenge for the talk: How do I adapt a year’s worth of articles about my autistic special interest into thirty minutes?
It brings to mind the movie Adaptation, where Nicolas Cage plays a screenwriter with imposter syndrome trying to adapt an unfilmable book into the movie we’re watching. Unlike my articles, I decide I shouldn’t launch abruptly into the crazy CSS experiments I built in my basement.
First, I need to answer why me, this random guy, thinks scrollytelling warrants half an hour of the audience’s time. I can’t assume much about this audience. Kris Howard will later comment on her blog that “Lee Meyer’s session introduced me to a new term – scrollytelling.”
I borrow credibility from The New York Times, name-checking its high-profile examples of scrollytelling, one of which won a Pulitzer Prize. John helpfully drops the link to the “Snow Fall” article into the livestream chat, just as I’d add links if this were an article.
But there’s another element of my writing that doesn’t translate: long code snippets. They’re too complex to explain on stage. Doing so would be a suicide mission. Let’s do it anyway.
I’ve used reveal.js in the past for an online presentation at Games For Change, and reveal.js supports automatic animations between code blocks. I use that to demonstrate how newer CSS syntax can drastically shorten code. It doesn’t matter that nobody can fully parse the old syntax at a glance; that’s the point of the animation. I ask for a show of hands for who would rather write the new syntax than the old?
Adapting my articles for the stage is my opportunity to rewrite history to appear logical. The order of discovery of the building blocks I will use for my final demo appears intentional rather than the chaotic trail I have been leaving across CSS-Tricks since 2024. But now it’s time to tackle the final demo like the boss battle it is.
I ask for a show of hands: Should I fight the bad guy unarmed, or run away? The audience is split evenly, which is the one outcome I didn’t plan for.
In Adaptation, when Cage’s character is running out of time to finish his script, he panics and seeks advice from screenwriting guru Robert McKee, who tells him: Your story can be flawed throughout, but wow them in the end, and you’ve got a hit. As much as I’m my own worst critic, I know I have something with this final demo, the kind that would make a leader on the Google Chrome team tweet “Wow!” That tweet hasn’t happened yet while I’m on stage, as I’m wondering how this crowd will react.
I let the bad guy kill the hero first. I make the antagonist seem unbeatable. Then I refresh, scroll in the opposite direction, climb a ladder, collect a lightsaber, and kill the bad guy.
McKee warned Cage’s writer character not to cheat at the end with a deus ex machina. A magic lightsaber to save the day feels like one for sure, but by a stroke of synchronicity, Star Wars imagery has been appearing in talks all day. John Allsopp even joked that it’s a theme he didn’t get the memo about. I reference this overarching theme, and the lightsaber feels earned. The pixel art guy kills the bad guy with one blow. The applause is loud enough to be heard on the livestream, even though the audience isn’t miked.
Can we end on that high note? Research shows that time dilates for people onstage with high public-speaking anxiety. Ironically, in a talk about controlling timelines, I realize I’ve lost control of the time, and I’m about to run out of slides too early.
So, I replay the demo and discuss its subtext. The scrollytelling pixel guy can be a novelty toy or he can be ergodic literature, an autobiographical allegory. I refresh again. “Scroll left or right to flee or fight,” says the pixel art guy. I explain the deeper psychological truth behind the simplistic story and retro graphics.
“You can tell them anything if you just make it funny, make it rhyme, and if they still don’t understand you, then you run it one more time.”
— Bo Burnham in “Can’t Handle This” View Slides Happy Hour and Speaker DinnerEvery autistic person should receive a voucher that grants them access to one social situation where people come and talk to them about the thing they are obsessed with. One piece of feedback in particular made me feel seen: Someone tells me a more traditional tutorial would have been fine, but the direction I took was playful, which felt refreshing in a world where discussions of web development can become depressingly utilitarian. He doesn’t know that the first blog I ever created was playfulprogramming.com, so I’ve always been about finding joy in development.
Someone else told me it was their favorite talk in the conference, and that I was brave for embracing my Jewishness publicly by mentioning the Torah as an illustration of the meaning of scrolling to me. Given what happened in Sydney a month after I left, it may not have been bravery so much as my obliviousness to the current vibes in my country, since I am a more frequent reader of CSS-Tricks than the news.
Day TwoThe Sydney weather cools, mirroring my more chilled mood today. With my presentation behind me, I now walk toward the venue like an anonymous attendee who magically got a free ticket. I brace myself for a morning of AI-heavy talks. My year at work was an AI overdose.
“What’s Beyond the Browser: The AI Platform Shift” by Rupert ManfrediWalking to the University of Technology Sydney, the combo of venue and theme reminds me of John Barth’s Giles Goat-Boy, in which the world is represented as a university controlled by an AI. Authorship itself is disputed in the fictional preface, with both Barth and the AI claiming only to have edited the work — eerily prescient in 1966 of the state of work in 2026. AI is great until there’s a defect. Then humans blame the AI, and the AI blames humans for misunderstanding its limits.
The novel satirized the Cold War. A Marxist might say intellectual property can’t exist because creative work is always a product of the zeitgeist. Although the tech that Rupert Manfredi’s demos blurs the lines of authorship by doing away with discrete apps and websites and composing UIs to meet the user’s needs on the fly, he is probably not a commie. He suggests that creators would still get paid. Perhaps this will finally be the day in the sun for HTTP 402 Payment Required.
Rupert’s talk, “What’s Beyond the Browser,” is daring. He demos “Telepath,” a prototype computer with no browser and no apps. He envisages that future developers will transfer their skills to create only fragments and services that AI can synthesize into a tailored user experience. He argues web development has never really been about learning React hooks, but about solving user problems: critical paths, information quality, and creativity. These are more fundamental to a developer’s skillset than any tools they happen to use.
That resonates with how I think about my work on CSS-Tricks: They are fragments of expression that gain meaning when woven into a larger tapestry by the people or machines who learn from them. If basic functionality becomes trivial, developers can focus on the problems nobody has solved yet.
“A False Sense of Accessibility: What Automated Testing Tools Are Missing” by Beau VassAs I mentioned before, I am autistic. So are my kids. It’s an invisible disability, and I’m careful to let the kids know the world won’t rearrange itself around our autism. Just as you can’t make something accessible to everyone, you can’t make the accessible experience the same as everyone else’s any more than you can make it easy for my son to succeed in a school system that was never designed with neurodiverse people in mind.
Accessibility is often less about universal comfort than about ensuring there’s a viable path for the people who truly need the content. When you think about it, the users’ faculties are part of the platform. Accessibility is, therefore, as fundamental as browser compatibility.
In his talk, “What Automated Tools Are Missing,” speaker Beau Vass demonstrates how automated audits flag non-issues while missing critical failures, sometimes making accessibility worse when followed blindly. A decorative image without alternative text might be flagged, yet adding it could also actively harm screen-reader users. The problem isn’t automated tools themselves; it’s when passing a Lighthouse audit becomes the goal. Tools only recognize what they’re taught, and AI trained on a broken web will faithfully reproduce its mistakes. As one of my workmates likes to say: “Use your tools, but don’t let them turn you into a tool.”
Accessibility isn’t a froghurt topping. It can’t be added at the end, not even in principle. The responsibility is shared across design, content, engineering, and testing, and it requires direct input from people with disabilities. Accessibility may be subjective, but making the web accessible should still be easier than making the physical world accessible. When we fail, it’s another reminder that tooling alone won’t save us.
AI won’t solve accessibility, but it may become useful once we stop asking it to. There aren’t enough good examples on the web for models to learn from, which means we can’t expect Claude Code to fix our sites. That said, AI can already simulate how a screen reader user might attempt to complete a task and surface where friction occurs. BrowserStack does this already. Ironically, it may be easier for a machine to put itself in the shoes of a disabled human than for a non-disabled human to do the same, and Beau believes it won’t be AI that changes the game, but laws and regulations requiring people to care about accessibility. Beau believes it’s more laws and regulations that will be a game-changer for accessibility than AI.
DepartureAll flights are delayed an hour, as if Sydney itself is resisting my return to Melbourne — and the end of this article. But back when I was young and teaching myself to write, I read a book about writing articles that said the more a piece seems to be about everything, the more it’s about nothing. Soon, we must end the article.
It ends with me waiting to take flight, thinking about how Chris Coyier once said his greatest pride wasn’t a single moment of accomplishment, but the “aggregate moments” of sustained focus on his professional passions. The afterglow of this conference is the sum of a year obsessing over animation timelines — and what you’ll do with the knowledge if I end this article at the right moment.
But does that magical moment even exist? Animation timelines work because we can pause motion on a screen. But if we could do that in real life, then, according to Zeno’s arrow paradox, my plane could never land. At every bullet-time instant, the plane would appear at rest, which would make all movement — including my entire journey — an illusion.
John Allsopp worried that the web itself might be stuck in that illusion of progress. But Aristotle answered Zeno’s arrow paradox by saying discrete instants of time don’t exist, only the flow of time. Reality is made of the aggregate moments that Chris Coyier said have meaning to him. As I wait for a plane that seems incapable of landing, my phone buzzes with my favorite feedback from the conference: a graduate developer amazed by “the scroll section in the Dev Summit.” I love that he calls it a section, not a talk, as if it blended seamlessly into a two-day narrative flow, foreshadowing a future web that unfurls like an infinite scroll.
“This story will never end. This story ends.”
— John BarthPostcard From Web Directions Dev Summit, 2025 originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.
Steven Heller’s Font of the Month: Noeler
Read the book, Typographic Firsts
It is a treat to find a typeface that tastes as good as it looks. The Noeler family, designed in 2025 by Michael Rafailyk, a new novelty family that looks as eatable as it is settable – and its five iterations underscore this. Noeler Pasta is much like the comfort […]
The post Steven Heller’s Font of the Month: Noeler appeared first on I Love Typography Ltd.
Heart-shaped Books
Read the book, Typographic Firsts
When ancient Egyptians embalmed their dead, organs were removed from the body. The brain, considered useless, was disposed of, while the stomach, liver, intestines, and lungs were preserved separately in boxes or canopic jars. However, the heart, considered the seat of intelligence, was thought to be essential in the afterlife […]
The post Heart-shaped Books appeared first on I Love Typography Ltd.
10 Must-have Typefaces for 2026
Read the book, Typographic Firsts
How quickly a year passes. Our Must-Have Fonts for 2025 list was our most popular ever, but our must-have fonts for 2026 list aims to set the bar even higher. Finding the best typefaces among thousands can be pretty daunting! So, to make things easier, we’ve curated a list of outstanding must-have […]
The post 10 Must-have Typefaces for 2026 appeared first on I Love Typography Ltd.
More on Context Management in AI Products
In AI products, context refers to the content, tools, and instructions provided to a model at any given moment. Because AI models have context limits, what's included (aka what a model is paying attention to) has a massive impact on results. So context management is key to letting people understand and shape what AI products produce.
In Context Management UI in AI Products I looked at UI patterns for showing users what information is influencing AI model responses, from simple context chips to nested agent timelines. This time I want to highlight two examples of automatic and manual context management solutions.
Augment Code's Context Engine demonstrates how automatic context management can dramatically improve AI product outcomes. Their system continuously indexes code commit history (understanding why changes were made), team coding patterns, documentation, and what developers on a team are actively working on.
When a developer asks to "add logging to payment requests," the system identifies exactly which files and patterns are relevant. This means developers don't have to manually specify what the AI should pay attention to. The system figures it out automatically and delivers much higher quality output as a result (see chart below).
Having an intelligent system manage context for you is extremely helpful but not always possible. In many kinds of tasks, there is no clear record of history, current state, and relevance like there is in a company's codebase. Also, some tasks are bespoke or idiosyncratic meaning only the person running them knows what's truly relevant. For these reasons, AI products also need context management interfaces.
Reve's creative tooling interface not only makes manual context management possible but also provides a consistent way to reference context in instructions as well. When someone adds a file to Reve, a thumbnail of it appears in the instruction field with a numbered reference. People can then use this number when writing out instructions like "put these tires @1 on on my truck @2".
It's also worth noting that any file uploaded to or created by Reve can be put into context with a simple "one-click" action. Just select any image and it will appear in the instruction field with a reference number. Select it again to remove it from context just as easily.
While the later may seem like a clear UI requirement, it's surprising how many AI products don't support this behavior. For instance, Google's Gemini has a nice overview panel of files uploaded to and created in a session but doesn't make them selectable as context.
As usual, AI capabilities keep changing fast. So context management solutions, whether automatic or manual, and their interfaces are going to continue to evolve.
AI Coding Agents for Designers
In an increasing number of technology companies, the majority of code is being written by AI coding agents. While that primarily boosts software developer productivity, they aren't the only ones that can benefit from this transformation. Here's how AI coding agents can also help designers.
As AI coding agents continue to improve dramatically, developers are turning to them more and more to not only write code but to review and improve it as well. The result isn't just more coder faster but the organizational changes needed to support this transition as well.
"The vast majority of code that is used to support Claude and to design the next Claude is now written by Claude. It's just the vast majority of it within Anthropic. And other fast moving companies, the same is true."- Dario Amodei, Anthropic CEO "Codex has transformed how OpenAI builds over the last few months."
- Sam Altman, OpenAI CEO
As just one example, a product manager I speak with regularly now spends his time using Augment Code on his company's production codebase. He creates a branch, prompts Augment's agents until he has a build he's happy with then passes it on to Engineering for implementation. Instead of writing a Product Requirements Document (PRD) he creates code that can be used and experienced by the whole team leading to a clearer understanding of what to build and why.
This kind of accelerated prototyping is a common way for designers to start applying AI coding agents to their workflow as well. But while the tools may be new, prototyping isn't new to designers. In fact, many larger design teams have specific prototyping roles within them. So what additional capabilities do AI coding agents give designers? Here's a few I've been using regularly.
Note: It's worth calling out that for these use cases to work well, you need AI coding tools that deeply understand your company's codebase. I, like the PM mentioned earlier, use Augment Code because their Context Engine is optimized for the kinds of large and complex codebases you'll find in most companies.
Fix Production BugsSee a bug or user experience issue in production? Just prompt the agent with a description of the issue, test its solution, and push a fix. Not only will fixing bugs make you feel great, your engineering friends will appreciate the help. There's always lots of "small" issues that designers know can be improved but can't get development resources for. Now those resources come in the form of AI coding agents.
Learn & Rethink SolutionsSometimes what seems like a small fix or improvement is just the tip of an iceberg. That is, changing something in the product has a fan-out effect. To change this, you also need to change that. That change will also impact these things. And so on.
Watching an AI coding agent go through its thinking process and steps can make all this clear. Even if you don't end up using any of the code it writes, seeing an agent's process teaches you a lot about how a system works. I've ended up rethinking my approach, considering different options and ultimately getting to a better solution than I started with. Thanks AI.
Get Engineering InvolvedPrompting an agent and seeing its process can also make something else clear: it's time to get Engineering involved. When it's obvious the scope of what an AI agent is trying to do to solve an issue or make an improvement is too broad, chances are it's time to sit down with the developers on your team to come up with a plan. This doesn't mean the agent failed, it means it prompted you to collaborate with your team.
Through these use cases, AI coding agents have helped me make more improvements and make more informed improvements to the products I work on. It's a great time to be a designer.
New business wanted
Last week Krijn and I decided to cancel performance.now() 2021. Although it was the right decision it leaves me in financially fairly dire straits. So I’m looking for new jobs and/or donations.
Even though the Corona trends in NL look good, and we could probably have brought 350 people together in November, we cannot be certain: there might be a new flare-up. More serious is the fact that it’s very hard to figure out how to apply the Corona checks Dutch government requires, especially for non-EU citizens. We couldn’t figure out how UK and US people should be tested, and for us that was the straw that broke the camel’s back. Cancelling the conference relieved us of a lot of stress.
Still, it also relieved me of a lot of money. This is the fourth conference in a row we cannot run, and I have burned through all my reserves. That’s why I thought I’d ask for help.
So ...
Has QuirksMode.org ever saved you a lot of time on a project? Did it advance your career? If so, now would be a great time to make a donation to show your appreciation.
I am trying my hand at CSS coaching. Though I had only few clients so far I found that I like it and would like to do it more. As an added bonus, because I’m still writing my CSS for JavaScripters book I currently have most of the CSS layout modules in my head and can explain them straight away — even stacking contexts.
Or if there’s any job you know of that requires a technical documentation writer with a solid knowledge of web technologies and the browser market, drop me a line. I’m interested.
Anyway, thanks for listening.
position: sticky, draft 1
I’m writing the position: sticky part of my book, and since I never worked with sticky before I’m not totally sure if what I’m saying is correct.
This is made worse by the fact that there are no very clear tutorials on sticky. That’s partly because it works pretty intuitively in most cases, and partly because the details can be complicated.
So here’s my draft 1 of position: sticky. There will be something wrong with it; please correct me where needed.
The inset properties are top, right, bottom and left. (I already introduced this terminology earlier in the chapter.)
h3,h4,pre {clear: left} section.scroll-container { border: 1px solid black; width: 300px; height: 250px; padding: 1em; overflow: auto; --text: 'scroll box'; float: left; clear: left; margin-right: 0.5em; margin-bottom: 1em; position: relative; font-size: 1.3rem; } .container,.outer-container { border: 1px solid black; padding: 1em; position: relative; --text: 'container'; } .outer-container { --text: 'outer container'; } :is(.scroll-container,.container,.outer-container):before { position: absolute; content: var(--text); top: 0.2em; left: 0.2em; font-size: 0.8rem; } section.scroll-container h2 { position: sticky; top: 0; background: white; margin: 0 !important; color: inherit !important; padding: 0.5em !important; border: 1px solid; font-size: 1.4rem !important; } .nowrap p { white-space: nowrap; } Introductionposition: sticky is a mix of relative and fixed. A sticky box takes its normal position in the flow, as if it had position: relative, but if that position scrolls out of view the sticky box remains in a position defined by its inset properties, as if it has position: fixed. A sticky box never escapes its container, though. If the container start or end scrolls past the sticky box abandons its fixed position and sticks to the top or the bottom of its container.
It is typically used to make sure that headers remain in view no matter how the user scrolls. It is also useful for tables on narrow screens: you can keep headers or the leftmost table cells in view while the user scrolls.
Scroll box and containerA sticky box needs a scroll box: a box that is able to scroll. By default this is the browser window — or, more correctly, the layout viewport — but you can define another scroll box by setting overflow on the desired element. The sticky box takes the first ancestor that could scroll as its scroll box and calculates all its coordinates relative to it.
A sticky box needs at least one inset property. These properties contain vital instructions, and if the sticky box doesn’t receive them it doesn’t know what to do.
A sticky box may also have a container: a regular HTML element that contains the sticky box. The sticky box will never be positioned outside this container, which thus serves as a constraint.
The first example shows this set-up. The sticky <h2> is in a perfectly normal <div>, its container, and that container is in a <section> that is the scroll box because it has overflow: auto. The sticky box has an inset property to provide instructions. The relevant styles are:
section.scroll-container { border: 1px solid black; width: 300px; height: 300px; overflow: auto; padding: 1em; } div.container { border: 1px solid black; padding: 1em; } section.scroll-container h2 { position: sticky; top: 0; } The rules Sticky headerRegular content
Regular content
Regular content
Regular content
Regular content
Regular content
Regular content
Content outside container
Content outside container
Content outside container
Content outside container
Content outside container
Content outside container
Now let’s see exactly what’s going on.
A sticky box never escapes its containing box. If it cannot obey the rules that follow without escaping from its container, it instead remains at the edge. Scroll down until the container disappears to see this in action.
A sticky box starts in its natural position in the flow, as if it has position: relative. It thus participates in the default flow: if it becomes higher it pushes the paragraphs below it downwards, just like any other regular HTML element. Also, the space it takes in the normal flow is kept open, even if it is currently in fixed position. Scroll down a little bit to see this in action: an empty space is kept open for the header.
A sticky box compares two positions: its natural position in the flow and its fixed position according to its inset properties. It does so in the coordinate frame of its scroll box. That is, any given coordinate such as top: 20px, as well as its default coordinates, is resolved against the content box of the scroll box. (In other words, the scroll box’s padding also constrains the sticky box; it will never move up into that padding.)
A sticky box with top takes the higher value of its top and its natural position in the flow, and positions its top border at that value. Scroll down slowly to see this in action: the sticky box starts at its natural position (let’s call it 20px), which is higher than its defined top (0). Thus it rests at its position in the natural flow. Scrolling up a few pixels doesn’t change this, but once its natural position becomes less than 0, the sticky box switches to a fixed layout and stays at that position.
The sticky box has bottom: 0
Regular content
Regular content
Regular content
Regular content
Regular content
Regular content
Sticky headerContent outside container
Content outside container
Content outside container
Content outside container
Content outside container
Content outside container
It does the same for bottom, but remember that a bottom is calculated relative to the scroll box’s bottom, and not its top. Thus, a larger bottom coordinate means the box is positioned more to the top. Now the sticky box compares its default bottom with the defined bottom and uses the higher value to position its bottom border, just as before.
With left, it uses the higher value of its natural position and to position its left border; with right, it does the same for its right border, bearing in mind once more that a higher right value positions the box more to the left.
If any of these steps would position the sticky box outside its containing box it takes the position that just barely keeps it within its containing box.
Details Sticky headerVery, very long line of content to stretch up the container quite a bit
Regular content
Regular content
Regular content
Regular content
Regular content
Regular content
Content outside container
Content outside container
Content outside container
Content outside container
Content outside container
Content outside container
Content outside container
The four inset properties act independently of one another. For instance the following box will calculate the position of its top and left edge independently. They can be relative or fixed, depending on how the user scrolls.
p.testbox { position: sticky; top: 0; left: 0; }Content outside container
Content outside container
Content outside container
Content outside container
Content outside container
The sticky box has top: 0; bottom: 0
Regular content
Regular content
Regular content
Regular content
Sticky headerRegular content
Regular content
Regular content
Regular content
Regular content
Content outside container
Content outside container
Content outside container
Content outside container
Content outside container
Setting both a top and a bottom, or both a left and a right, gives the sticky box a bandwidth to move in. It will always attempt to obey all the rules described above. So the following box will vary between 0 from the top of the screen to 0 from the bottom, taking its default position in the flow between these two positions.
p.testbox { position: sticky; top: 0; bottom: 0; } No containerRegular content
Regular content
Sticky headerRegular content
Regular content
Regular content
Regular content
Regular content
Regular content
Regular content
Regular content
Regular content
So far we put the sticky box in a container separate from the scroll box. But that’s not necessary. You can also make the scroll box itself the container if you wish. The sticky element is still positioned with respect to the scroll box (which is now also its container) and everything works fine.
Several containers Sticky headerRegular content
Regular content
Regular content
Regular content
Regular content
Regular content
Regular content
Content outside container
Content outside container
Content outside outer container
Content outside outer container
Or the sticky item can be several containers removed from its scroll box. That’s fine as well; the positions are still calculated relative to the scroll box, and the sticky box will never leave its innermost container.
Changing the scroll box Sticky headerThe container has overflow: auto.
Regular content
Regular content
Regular content
Regular content
Regular content
Regular content
Content outside container
Content outside container
Content outside container
One feature that catches many people (including me) unaware is giving the container an overflow: auto or hidden. All of a sudden it seems the sticky header doesn’t work any more.
What’s going on here? An overflow value of auto, hidden, or scroll makes an element into a scroll box. So now the sticky box’s scroll box is no longer the outer element, but the inner one, since that is now the closest ancestor that is able to scroll.
The sticky box appears to be static, but it isn’t. The crux here is that the scroll box could scroll, thanks to its overflow value, but doesn’t actually do so because we didn’t give it a height, and therefore it stretches up to accomodate all of its contents.
Thus we have a non-scrolling scroll box, and that is the root cause of our problems.
As before, the sticky box calculates its position by comparing its natural position relative to its scroll box with the one given by its inset properties. Point is: the sticky box doesn’t scroll relative to its scroll box, so its position always remains the same. Where in earlier examples the position of the sticky element relative to the scroll box changed when we scrolled, it no longer does so, because the scroll box doesn’t scroll. Thus there is no reason for it to switch to fixed positioning, and it stays where it is relative to its scroll box.
The fact that the scroll box itself scrolls upward is irrelevant; this doesn’t influence the sticky box in the slightest.
Sticky headerRegular content
Regular content
Regular content
Regular content
Regular content
Regular content
Regular content
Content outside container
Content outside container
Content outside container
Content outside container
Content outside container
Content outside container
One solution is to give the new scroll box a height that is too little for its contents. Now the scroll box generates a scrollbar and becomes a scrolling scroll box. When we scroll it the position of the sticky box relative to its scroll box changes once more, and it switches from fixed to relative or vice versa as required.
Minor itemsFinally a few minor items:
- It is no longer necessary to use position: -webkit-sticky. All modern browsers support regular position: sticky. (But if you need to cater to a few older browsers, retaining the double syntax doesn’t hurt.)
- Chrome (Mac) does weird things to the borders of the sticky items in these examples. I don’t know what’s going on and am not going to investigate.
Breaking the web forward
Safari is holding back the web. It is the new IE, after all. In contrast, Chrome is pushing the web forward so hard that it’s starting to break. Meanwhile web developers do nothing except moan and complain. The only thing left to do is to pick our poison.
blockquote { font-size: inherit; font-family: inherit; } blockquote p { font-size: inherit; font-family: inherit; } Safari is the new IERecently there was yet another round of “Safari is the new IE” stories. Once Jeremy’s summary and a short discussion cleared my mind I finally figured out that Safari is not IE, and that Safari’s IE-or-not-IE is not the worst problem the web is facing.
Perry Sun argues that for developers, Safari is crap and outdated, emulating the old IE of fifteen years ago in this respect. He also repeats the theory that Apple is deliberately starving Safari of features in order to protect the app store, and thus its bottom line. We’ll get back to that.
The allegation that Safari is holding back web development by its lack of support for key features is not new, but it’s not true, either. Back fifteen years ago IE held back the web because web developers had to cater to its outdated technology stack. “Best viewed with IE” and all that. But do you ever see a “Best viewed with Safari” notice? No, you don’t. Another browser takes that special place in web developers’ hearts and minds.
Chrome is the new IE, but in reverseJorge Arango fears we’re going back to the bad old days with “Best viewed in Chrome.” Chris Krycho reinforces this by pointing out that, even though Chrome is not the standard, it’s treated as such by many web developers.
“Best viewed in Chrome” squares very badly with “Safari is the new IE.” Safari’s sad state does not force web developers to restrict themselves to Safari-supported features, so it does not hold the same position as IE.
So I propose to lay this tired old meme to rest. Safari is not the new IE. If anything it’s the new Netscape 4.
Meanwhile it is Chrome that is the new IE, but in reverse.
Break the web forwardBack in the day, IE was accused of an embrace, extend, and extinguish strategy. After IE6 Microsoft did nothing for ages, assuming it had won the web. Thanks to web developers taking action in their own name for the first (and only) time, IE was updated once more and the web moved forward again.
Google learned from Microsoft’s mistakes and follows a novel embrace, extend, and extinguish strategy by breaking the web and stomping on the bits. Who cares if it breaks as long as we go forward. And to hell with backward compatibility.
Back in 2015 I proposed to stop pushing the web forward, and as expected the Chrome devrels were especially outraged at this idea. It never went anywhere. (Truth to tell: I hadn’t expected it to.)
I still think we should stop pushing the web forward for a while until we figure out where we want to push the web forward to — but as long as Google is in charge that won’t happen. It will only get worse.
On alertA blog storm broke out over the decision to remove alert(), confirm() and prompt(), first only the cross-origin variants, but eventually all of them. Jeremy and Chris Coyier already summarised the situation, while Rich Harris discusses the uses of the three ancient modals, especially when it comes to learning JavaScript.
With all these articles already written I will only note that, if the three ancient modals are truly as horrendous a security issue as Google says they are it took everyone a bloody long time to figure that out. I mean, they turn 25 this year.
Although it appears Firefox and Safari are on board with at least the cross-origin part of the proposal, there is no doubt that it’s Google that leads the charge.
From Google’s perspective the ancient modals have one crucial flaw quite apart from their security model: they weren’t invented there. That’s why they have to be replaced by — I don’t know what, but it will likely be a very complicated API.
Complex systems and arrogant priests rule the webThus the new embrace, extend, and extinguish is breaking backward compatibility in order to make the web more complicated. Nolan Lawson puts it like this:
we end up with convoluted specs like Service Worker that you need a PhD to understand, and yet we still don't have a working <dialog> element.
In addition, Google can be pretty arrogant and condescending, as Chris Ferdinandi points out.
The condescending “did you actually read it, it’s so clear” refrain is patronizing AF. It’s the equivalent of “just” or “simply” in developer documentation.
I read it. I didn’t understand it. That’s why I asked someone whose literal job is communicating with developers about changes Chrome makes to the platform.
This is not isolated to one developer at Chrome. The entire message thread where this change was surfaced is filled with folks begging Chrome not to move forward with this proposal because it will break all-the-things.
If you write documentation or a technical article and nobody understands it, you’ve done a crappy job. I should know; I’ve been writing this stuff for twenty years.
Extend, embrace, extinguish. And use lots of difficult words.
Patience is a virtueAs a reaction to web dev outcry Google temporarily halted the breaking of the web. That sounds great but really isn’t. It’s just a clever tactical move.
I saw this tactic in action before. Back in early 2016 Google tried to break the de-facto standard for the mobile visual viewport that I worked very hard to establish. I wrote a piece that resonated with web developers, whose complaints made Google abandon the plan — temporarily. They tried again in late 2017, and I again wrote an article, but this time around nobody cared and the changes took effect and backward compatibility was broken.
So the three ancient modals still have about 12 to 18 months to live. Somewhere in late 2022 to early 2023 Google will try again, web developers will be silent, and the modals will be gone.
The pursuit of appinessBut why is Google breaking the web forward at such a pace? And why is Apple holding it back?
Safari is kept dumb to protect the app store and thus revenue. In contrast, the Chrome team is pushing very hard to port every single app functionality to the browser. Ages ago I argued we should give up on this, but of course no one listened.
When performing Valley Kremlinology, it is useful to see Google policies as stemming from a conflict between internal pro-web and anti-web factions. We web developers mainly deal with the pro-web faction, the Chrome devrel and browser teams. On the other hand, the Android team is squarely in the anti-web camp.
When seen in this light the pro-web camp’s insistence on copying everything appy makes excellent sense: if they didn’t Chrome would lag behind apps and the Android anti-web camp would gain too much power. While I prefer the pro-web over the anti-web camp, I would even more prefer the web not to be a pawn in an internal Google power struggle. But it has come to that, no doubt about it.
Solutions?Is there any good solution? Not really.
Jim Nielsen feels that part of the issue is the lack of representation of web developers in the standardization process. That sounds great but is proven not to work.
Three years ago Fronteers and I attempted to get web developers represented and were met with absolute disinterest. Nobody else cared even one shit, and the initiative sank like a stone.
So a hypothetical web dev representative in W3C is not going to work. Also, the organisational work would involve a lot of unpaid labour, and I, for one, am not willing to do it again. Neither is anyone else. So this is not the solution.
And what about Firefox? Well, what about it? Ten years ago it made a disastrous mistake by ignoring the mobile web for way too long, then it attempted an arrogant and uninformed come-back with Firefox OS that failed, and its history from that point on is one long slide into obscurity. That’s what you get with shitty management.
Pick your poisonSo Safari is trying to slow the web down. With Google’s move-fast-break-absofuckinglutely-everything axiom in mind, is Safari’s approach so bad?
Regardless of where you feel the web should be on this spectrum between Google and Apple, there is a fundamental difference between the two.
We have the tools and procedures to manage Safari’s disinterest. They’re essentially the same as the ones we deployed against Microsoft back in the day — though a fundamental difference is that Microsoft was willing to talk while Apple remains its old haughty self, and its “devrels” aren’t actually allowed to do devrelly things such as managing relations with web developers. (Don’t blame them, by the way. If something would ever change they’re going to be our most valuable internal allies — just as the IE team was back in the day.)
On the other hand, we have no process for countering Google’s reverse embrace, extend, and extinguish strategy, since a section of web devs will be enthusiastic about whatever the newest API is. Also, Google devrels talk. And talk. And talk. And provide gigs of data that are hard to make sense of. And refer to their proprietary algorithms that “clearly” show X is in the best interest of the web — and don’t ask questions! And make everything so fucking complicated that we eventually give up and give in.
So pick your poison. Shall we push the web forward until it’s broken, or shall we break it by inaction? What will it be? Privately, my money is on Google. So we should say goodbye to the old web while we still can.
Custom properties and @property
You’re reading a failed article. I hoped to write about @property and how it is useful for extending CSS inheritance considerably in many different circumstances. Alas, I failed. @property turns out to be very useful for font sizes, but does not even approach the general applicability I hoped for.
Grandparent-inheritingIt all started when I commented on what I thought was an interesting but theoretical idea by Lea Verou: what if elements could inherit the font size of not their parent, but their grandparent? Something like this:
div.grandparent { /* font-size could be anything */ } div.parent { font-size: 0.4em; } div.child { font-size: [inherit from grandparent in some sort of way]; font-size: [yes, you could do 2.5em to restore the grandparent's font size]; font-size: [but that's not inheriting, it's just reversing a calculation]; font-size: [and it will not work if the parent's font size is also unknown]; }Lea told me this wasn’t a vague idea, but something that can be done right now. I was quite surprised — and I assume many of my readers are as well — and asked for more information. So she wrote Inherit ancestor font-size, for fun and profit, where she explained how the new Houdini @property can be used to do this.
This was seriously cool. Also, I picked up a few interesting bits about how CSS custom properties and Houdini @property work. I decided to explain these tricky bits in simple terms — mostly because I know that by writing an explanation I myself will understand them better — and to suggest other possibilities for using Lea’s idea.
Alas, that last objective is where I failed. Lea’s idea can only be used for font sizes. That’s an important use case, but I had hoped for more. The reasons why it doesn’t work elsewhere are instructive, though.
Tokens and valuesLet’s consider CSS custom properties. What if we store the grandparent’s font size in a custom property and use that in the child?
div.grandparent { /* font-size could be anything */ --myFontSize: 1em; } div.parent { font-size: 0.4em; } div.child { font-size: var(--myFontSize); /* hey, that's the grandparent's font size, isn't it? */ }This does not work. The child will have the same font size as the parent, and ignore the grandparent. In order to understand why we need to understand how custom properties work. What does this line of CSS do?
--myFontSize: 1em;It sets a custom property that we can use later. Well duh.
Sure. But what value does this custom property have?
... errr ... 1em?
Nope. The answer is: none. That’s why the code example doesn’t work.
When they are defined, custom properties do not have a value or a type. All that you ordered the browsers to do is to store a token in the variable --myFontSize.
This took me a while to wrap my head around, so let’s go a bit deeper. What is a token? Let’s briefly switch to JavaScript to explain.
let myVar = 10;What’s the value of myVar in this line? I do not mean: what value is stored in the variable myVar, but: what value does the character sequence myVar have in that line of code? And what type?
Well, none. Duh. It’s not a variable or value, it’s just a token that the JavaScript engine interprets as “allow me to access and change a specific variable” whenever you type it.
CSS custom properties also hold such tokens. They do not have any intrinsic meaning. Instead, they acquire meaning when they are interpreted by the CSS engine in a certain context, just as the myVar token is in the JavaScript example.
So the CSS custom property contains the token 1em without any value, without any type, without any meaning — as yet.
You can use pretty any bunch of characters in a custom property definition. Browsers make no assumptions about their validity or usefulness because they don’t yet know what you want to do with the token. So this, too, is a perfectly fine CSS custom property:
--myEgoTrip: ppk;Browsers shrug, create the custom property, and store the indicated token. The fact that ppk is invalid in all CSS contexts is irrelevant: we haven’t tried to use it yet.
It’s when you actually use the custom property that values and types are assigned. So let’s use it:
background-color: var(--myEgoTrip);Now the CSS parser takes the tokens we defined earlier and replaces the custom property with them:
background-color: ppk;And only NOW the tokens are read and intrepreted. In this case that results in an error: ppk is not a valid value for background-color. So the CSS declaration as a whole is invalid and nothing happens — well, technically it gets the unset value, but the net result is the same. The custom property itself is still perfectly valid, though.
The same happens in our original code example:
div.grandparent { /* font-size could be anything */ --myFontSize: 1em; /* just a token; no value, no meaning */ } div.parent { font-size: 0.4em; } div.child { font-size: var(--myFontSize); /* becomes */ font-size: 1em; /* hey, this is valid CSS! */ /* Right, you obviously want the font size to be the same as the parent's */ /* Sure thing, here you go */ }In div.child he tokens are read and interpreted by the CSS parser. This results in a declaration font-size: 1em;. This is perfectly valid CSS, and the browsers duly note that the font size of this element should be 1em.
font-size: 1em is relative. To what? Well, to the parent’s font size, of course. Duh. That’s how CSS font-size works.
So now the font size of the child becomes the same as its parent’s, and browsers will proudly display the child element’s text in the same font size as the parent element’s while ignoring the grandparent.
This is not what we wanted to achieve, though. We want the grandparent’s font size. Custom properties — by themselves — don’t do what we want. We have to find another solution.
@propertyLea’s article explains that other solution. We have to use the Houdini @property rule.
@property --myFontSize { syntax: "<length>"; initial-value: 0; inherits: true; } div { border: 1px solid; padding: 1em; } div.grandparent { /* font-size could be anything */ --myFontSize: 1em; } div.parent { font-size: 0.4em; } div.child { font-size: var(--myFontSize); }Now it works. Wut? Yep — though only in Chrome so far.
@property --myFontSize { syntax: ""; initial-value: 0; inherits: true; } section.example { max-width: 500px; } section.example div { border: 1px solid; padding: 1em; } div.grandparent { font-size: 23px; --myFontSize: 1em; } div.parent { font-size: 0.4em; } div.child { font-size: var(--myFontSize); } This is the grandparent This is the parent This is the childWhat black magic is this?
Adding the @property rule changes the custom property --myFontSize from a bunch of tokens without meaning to an actual value. Moreover, this value is calculated in the context it is defined in — the grandfather — so that the 1em value now means 100% of the font size of the grandfather. When we use it in the child it still has this value, and therefore the child gets the same font size as the grandfather, which is exactly what we want to achieve.
(The variable uses a value from the context it’s defined in, and not the context it’s executed in. If, like me, you have a grounding in basic JavaScript you may hear “closures!” in the back of your mind. While they are not the same, and you shouldn’t take this apparent equivalency too far, this notion still helped me understand. Maybe it’ll help you as well.)
Unfortunately I do not quite understand what I’m doing here, though I can assure you the code snippet works in Chrome — and will likely work in the other browsers once they support @property.
Misson completed — just don’t ask me how.
SyntaxYou have to get the definition right. You need all three lines in the @property rule. See also the specification and the MDN page.
@property --myFontSize { syntax: "<length>"; initial-value: 0; inherits: true; }The syntax property tells browsers what kind of property it is and makes parsing it easier. Here is the list of possible values for syntax, and in 99% of the cases one of these values is what you need.
You could also create your own syntax, e.g. syntax: "ppk | <length>"
Now the ppk keyword and any sort of length is allowed as a value.
Note that percentages are not lengths — one of the many things I found out during the writing of this article. Still, they are so common that a special value for “length that may be a percentage or may be calculated using percentages” was created:
syntax: "<length-percentage>"Finally, one special case you need to know about is this one:
syntax: "*"MDN calls this a universal selector, but it isn’t, really. Instead, it means “I don’t know what syntax we’re going to use” and it tells browsers not to attempt to interpret the custom property. In our case that would be counterproductive: we definitely want the 1em to be interpreted. So our example doesn’t work with syntax: "*".
initial-value and inheritsAn initial-value property is required for any syntax value that is not a *. Here that’s simple: just give it an initial value of 0 — or 16px, or any absolute value. The value doesn’t really matter since we’re going to overrule it anyway. Still, a relative value such as 1em is not allowed: browsers don’t know what the 1em would be relative to and reject it as an initial value.
Finally, inherits: true specifies that the custom property value can be inherited. We definitely want the computed 1em value to be inherited by the child — that’s the entire point of this experiment. So we carefully set this flag to true.
Other use casesSo far this article merely rehashed parts of Lea’s. Since I’m not in the habit of rehashing other people’s articles my original plan was to add at least one other use case. Alas, I failed, though Lea was kind enough to explain why each of my ideas fails.
Percentage of what?Could we grandfather-inherit percentual margins and paddings? They are relative to the width of the parent of the element you define them on, and I was wondering if it might be useful to send the grandparent’s margin on to the child just like the font size. Something like this:
@property --myMargin { syntax: "<length-percentage>"; initial-value: 0; inherits: true; } div.grandparent { --myMargin: 25%; margin-left: var(--myMargin); } div.parent { font-size: 0.4em; } div.child { margin-left: var(--myMargin); /* should now be 25% of the width of the grandfather's parent */ /* but isn't */ }Alas, this does not work. Browsers cannot resolve the 25% in the context of the grandparent, as they did with the 1em, because they don’t know what to do.
The most important trick for using percentages in CSS is to always ask yourself: “percentage of WHAT?”
That’s exactly what browsers do when they encounter this @property definition. 25% of what? The parent’s font size? Or the parent’s width? (This is the correct answer, but browsers have no way of knowing that.) Or maybe the width of the element itself, for use in background-position?
Since browsers cannot figure out what the percentage is relative to they do nothing: the custom property gets the initial value of 0 and the grandfather-inheritance fails.
ColoursAnother idea I had was using this trick for the grandfather’s text colour. What if we store currentColor, which always has the value of the element’s text colour, and send it on to the grandchild? Something like this:
@property --myColor { syntax: "<color>"; initial-value: black; inherits: true; } div.grandparent { /* color unknown */ --myColor: currentColor; } div.parent { color: red; } div.child { color: var(--myColor); /* should now have the same color as the grandfather */ /* but doesn't */ }Alas, this does not work either. When the @property blocks are evaluated, and 1em is calculated, currentColor specifically is not touched because it is used as an initial (default) value for some inherited SVG and CSS properties such as fill. Unfortunately I do not fully understand what’s going on, but Tab says this behaviour is necessary, so it is.
Pity, but such is life. Especially when you’re working with new CSS functionalities.
ConclusionSo I tried to find more possbilities for using Lea’s trick, but failed. Relative units are fairly sparse, especially when you leave percentages out of the equation. em and related units such as rem are the only ones, as far as I can see.
So we’re left with a very useful trick for font sizes. You should use it when you need it (bearing in mind that right now it’s only supported in Chromium-based browsers), but extending it to other declarations is not possible at the moment.
Many thanks to Lea Verou and Tab Atkins for reviewing and correcting an earlier draft of this article.
Let’s talk about money
Let’s talk about money!
Let’s talk about how hard it is to pay small amounts online to people whose work you like and who could really use a bit of income. Let’s talk about how Coil aims to change that.
Taking a subscription to a website is moderately easy, but the person you want to pay must have enabled them. Besides, do you want to purchase a full subscription in order to read one or two articles per month?
Sending a one-time donation is pretty easy as well, but, again, the site owner must have enabled them. And even then it just gives them ad-hoc amounts that they cannot depend on.
Then there’s Patreon and Kickstarter and similar systems, but Patreon is essentially a subscription service while Kickstarter is essentially a one-time donation service, except that both keep part of the money you donate.
And then there’s ads ... Do we want small content creators to remain dependent on ads and thus support the entire ad ecosystem? I, personally, would like to get rid of them.
The problem today is that all non-ad-based systems require you to make conscious decisions to support someone — and even if you’re serious about supporting them you may forget to send in a monthly donation or to renew your subscription. It sort-of works, but the user experience can be improved rather dramatically.
That’s where Coil and the Web Monetization Standard come in.
Web MonetizationThe idea behind Coil is that you pay for what you consume easily and automatically. It’s not a subscription - you only pay for what you consume. It’s not a one-time donation, either - you always pay when you consume.
Payments occur automatically when you visit a website that is also subscribed to Coil, and the amount you pay to a single site owner depends on the time you spend on the site. Coil does not retain any of your money, either — everything goes to the people you support.
In this series of four articles we’ll take a closer look at the architecture of the current Coil implementation, how to work with it right now, the proposed standard, and what’s going to happen in the future.
OverviewSo how does Coil work right now?
Both the payer and the payee need a Coil account to send and receive money. The payee has to add a <meta> tag with a Coil payment pointer to all pages they want to monetize. The payer has to install the Coil extension in their browsers. You can see this extension as a polyfill. In the future web monetization will, I hope, be supported natively in all browsers.
Once that’s done the process works pretty much automatically. The extension searches for the <meta> tag on any site the user visits. If it finds one it starts a payment stream from payer to payee that continues for as long as the payer stays on the site.
The payee can use the JavaScript API to interact with the monetization stream. For instance, they can show extra content to paying users, or keep track of how much a user paid so far. Unfortunately these functionalities require JavaScript, and the hiding of content is fairly easy to work around. Thus it is not yet suited for serious business purposes, especially in web development circles.
This is one example of how the current system is still a bit rough around the edges. You’ll find more examples in the subsequent articles. Until the time browsers support the standard natively and you can determine your visitors’ monetization status server-side these rough bits will continue to exist. For the moment we will have to work with the system we have.
This article series will discuss all topics we touched on in more detail.
Start now!For too long we have accepted free content as our birthright, without considering the needs of the people who create it. This becomes even more curious for articles and documentation that are absolutely vital to our work as web developers.
Take a look at this list of currently-monetized web developer sites. Chances are you’ll find a few people whose work you used in the past. Don’t they deserve your direct support?
Free content is not a right, it’s an entitlement. The sooner we internalize this, and start paying independent voices, the better for the web.
The only alternative is that all articles and documentation that we depend on will written by employees of large companies. And employees, no matter how well-meaning, will reflect the priorities and point of view of their employer in the long run.
So start now.
In order to support them you should invest a bit of time once and US$5 per month permanently. I mean, that’s not too much to ask, is it?
ContinueI wrote this article and its sequels for Coil, and yes, I’m getting paid. Still, I believe in what they are doing, so I won’t just spread marketing drivel. Initially it was unclear to me exactly how Coil works. So I did some digging, and the remaining parts of this series give a detailed description of how Coil actually works in practice.
For now the other three articles will only be available on dev.to. I just published part 2, which gives a high-level overview of how Coil works right now. Part 3 will describe the meta tag and the JavaScript API, and in part 4 we’ll take a look at the future, which includes a formal W3C standard. Those parts will be published next week and the week after that.
- « first
- ‹ previous
- 1
- 2
- 3