Contextualism

Conference Opening and Adam Argyle's Introduction

The emcee introduces Adam Argyle as a CSS champion, CSS Working Group member, and host of GUI Challenges. Adam takes the stage, praises the conference, acknowledges fellow speakers including PPK, and leads the audience in a call-and-response CSS chant—a callback to a memorable and slightly awkward moment from his previous year's appearance that he wants to put to rest properly.

Contextualism: Why Design Systems Are Just Fancy Lego

Adam introduces the central thesis of his talk: most design systems, including the one he works on at Shopify, are 'barely modern Bootstraps' that produce static Lego blocks rather than truly adaptive components. Drawing from the architectural concept of contextualism, he argues that the web platform is uniquely capable of building components that morph to their environment—like a house that becomes a cabin in the woods, a lake house by water, or an igloo in Antarctica—and that developers should go back to their teams and demand more time to build this intelligence in.

Act One: Everything a Component Can Know About the User

Adam opens the first act by cataloguing what a component can know about the user and their device: physical size, orientation (noting landscape is routinely forgotten), pointer type (coarse vs. fine), hover capability, color capabilities, and user preferences including color scheme, reduced motion, reduced transparency, forced colors, and prefers-reduced-data. He emphasizes logical properties as a zero-effort way to handle all reading directions and writing modes simultaneously, comparing it to asking someone to shake with their 'good hand' rather than a specific one.

Knowing the Surroundings: Container Queries, Quantity, and Style

Adam covers the full range of ways a component can know about its immediate environment, opening with design tokens as a starting point that most teams mistake for the finish line. Named container queries let a card know whether it lives in a sidebar or a feed. Size queries reveal exactly how much allocated space a component has. Quantity queries allow components to adapt layout based on sibling count—from zero to 'too many.' Adam also covers style queries for paint-level intelligence, proximity styling, and at-scope's donut scoping to contain selector spread.

Reading the Room: Cascade Awareness, Intrinsic Sizing, and Relative Color

Adam explains how components can derive intelligence from inherited context using light-dark() and currentColor to avoid hard-coding values, framing 'not caring' as a feature of well-designed CSS. He champions intrinsic web design concepts including calc-size() for animating to auto dimensions, CSS shape wrapping for elegant text flow, and the relative color syntax—which he calls more expressive than JavaScript destructuring—for deriving entire palette variations from a single brand token rather than managing hundreds of explicit color variables. He also touches on font-palette and override-colors for adapting color fonts to theme contexts.

Live Color Demo: Open Props v2 Dynamic Palette System

Adam demonstrates an Open Props v2 color system built on OKLCH that generates a full 16-color palette from just three custom properties, showing live hue rotation, chroma toggling for grayscale, and real-time WCAG contrast scoring across all palette combinations. He explains why he intentionally avoids palette inversion in dark mode, instead remapping which section of the palette is used. The segment closes with three strategies for nested light/dark themes—at-scope, color-scheme inheritance, and style queries—demonstrated on his blog nerdy.dev, directly inspired by a talk earlier in the conference.

Adaptive Frosted Glass, Typography Tricks, and Viewport Mastery

Adam demos adaptive frosted glass that automatically adjusts opacity and blur to light and dark contexts via light-dark(), then explains the irradiation illusion fix: using font-variation-settings GRAD on variable fonts to visually thin text in dark mode without changing glyph box dimensions, avoiding cumulative layout shift. He then walks through viewport units (DVH, LVH, SVH), notch handling for full-bleed native-app appearance, fluid typography strategies, conditional border-radius using clamp with scientific notation, and concentric border-radius patterns for visually polished nested elements.

Interaction States, Form Intelligence, and Scroll-Driven Behaviors

Adam catalogues the full range of interactive states components can leverage—focus-visible, focus-within for whole-form visual engagement, user-valid and user-invalid (not colon-invalid, which yells at users before they've typed anything), video playback and picture-in-picture states, popover open/closed, full-screen, placeholder-shown, browser autofill styling, and indeterminate checkboxes. He then covers scroll-driven behaviors: view-transition names applied only to in-viewport cards for performance, natural stagger animations via scroll, stuck scroll-state queries for sticky headers that signal their stuck state, and branded highlight and target-text pseudo-elements.

Component Self-Knowledge: Sibling Identity, Web Components, and PWA Awareness

Adam covers ways a component can introspect its own identity: sibling-index and sibling-count for stagger and arc layout effects, the :defined pseudo-class for web component skeleton states before upgrade, :host selectors, and @supports for graceful capability-based degradation. He addresses print styles owned by the component itself rather than dumped in a separate stylesheet. The segment closes with PWA-specific intelligence—detecting installed state, bleeding into the title bar for a native-app appearance—and content-visibility with an intrinsic size hint for performant off-screen rendering of large lists.

Anchor Positioning, Component Relationships, and Trigonometric Layout

Adam introduces anchor positioning as one of the most transformative recent additions to CSS and demonstrates 'anchor interpolated morph'—a technique using anchor's live x, y, and width knowledge to create FLIP-style transition animations between intrinsic states with no JavaScript and full interruptibility. He also covers matching element widths via anchor, CSS counter nesting, subgrid adoption from a parent container, using a distant scroll container as an animation timeline, cascade layer rollback with revert-layer, and trigonometric angle calculation with atan2 to point elements toward their anchor targets.

What CSS Can't Do Yet—and the Prop-for-That Library

Adam lists everything CSS currently lacks: scroll velocity, global and element-local pointer position, slider and form field values, dirty/touched/pristine form states, select and color input values, and device sensors like battery, geolocation, and frame rate. He then introduces his JavaScript library 'prop-for-that,' which bridges these gaps by writing live custom properties onto opted-in elements. Live demos show pointer-driven gradients, parallax, trigonometric pointer tracking, a ticking clock, scroll momentum, low-battery mode simulation, form dirty/touched state detection, and per-frame video color extraction—all consumed purely in CSS, so developers can stay in CSS while the library handles the JavaScript.

Our final speaker of the day. I cannot believe how fast the day has gone. It's been a wonderful and I it's literally the best person to end the day. And, yeah, I did have when you are an emcee, you have to prepare for the unexpected. And, Adam was here last year, and he got the audience electric with their contagious energy.

The walk the walk the talk is something I think of when I think of Adam. Regardless of job title or employer, Adam is a CSS champion who is also on the CSS working group, host of GUI challenges, and cohost of bad at CSS podcast. I again, please give a very, very warm applause for Adam, who is going to call who is going to be the closing talk of the day. And again, I couldn't think of a better person to do it. Welcome to the stage, Adam.

Thank

you so much. Oh my goodness. I go to a lot of conferences. I've been to so many. This is by far the best fucking one. It's so good. And it's not just the people, it's the content, the depth of which we are learning. I'm learning, and I try to study this shit all day. So I gotta get a couple things out of the way.

First one, PPK, you rock, and you need claps right now. The second one goes out to all the speakers. All the speakers have been kicking, but let's give it up for them again really quick. The sound guy hasn't made just kidding. It's working great.

And then, yeah, we gotta get something off our chest, is I did this weird it got weird last time we chanted CSS together. Like, I I did I think I threw my hands in the air, and it was all like, oh, it's weird. But I think we I think people want to oh, did my computer fall asleep? It did.

We need to do it again. And this time, we're gonna do it just three times. That way it doesn't get weird. I'm gonna count to three, and we're just gonna go CSS CSS all together, and it's gonna be, camaraderie, and it'd be awesome. K. You ready? One, two, three. CSS CSS. Oh, I can't do that. CSS. Yeah. Thank you so much.

I was like, we just gotta get it off our chest. Everyone asked me about it. It's almost like a celebrity who's walking down the street, and they're like, hey. And they quote something stupid that they did in a movie. Like, hey, you guys. And the guy's like, that was twenty years ago. Don't chant that at me. CSS does that to me, but I love it.

Alright. So here's the provocative version of my, title of my, talk if this oh, it was working before it fell asleep. It's off and on the remote. I'm gonna unplug and plug back in this remote because I'd like to be walking around still. That's been fun. Do what? Click the browser? Yeah. Alright. Here's the provocative title if I was a YouTuber, which I'm not, and it would be like, our design systems suck.

I'm serious. I'm kinda getting I'm frustrated at the one I work on at Shopify. I'm frustrated at all the base UI, the shad c n's. They all tick me off. They look the same. They're basically barely modern bootstraps, and it's nothing to say about y'all skills. Like, it's nothing to say about the skills of the team that I'm on. They just kinda suck, and Yuna was showing us how good it could be.

She laid it up. All of these talks are laying up the amazing things that we have in our toolkit. So this particular talk is called contextualism, and I'm going a mega list of every single thing that a component or your page can respond to that proves that this is the best platform to be building on because of this list doesn't exist in any design tools.

This list doesn't exist on iOS. It only exists on the web, and we just need to teach everybody about it. So this is a this is for designers. This is for developers. You need to hand this to the people on your team and say, our components aren't done. Every time I go through one of these slides, you need to think about, is my component leveraging this or not?

It's valuable for the user, for you as a developer, usually the user. And one of the things that's kinda messing me up is we're we're all we make design systems. I'm like, no. You don't. You make a dinky Lego. And then everyone's like, well, I make I make Legos, and then we we orchestrate them all together. Or I make Lego what's the plural?

Is it? It's Lego. So I'm American. Say Legos, and y'all are like, stop doing that. Anyway, we make Legos. It's a terrible thing. That's not what we build anymore. It worked really good at first, and I used to sell Legos too. Was like, you make Legos, you put them together, it works really great. But the ways that we can adapt and the features that we have in the browser are far beyond Legos.

If I go back really quick just to what contextualism is, I saw that from architecture. This is a term I didn't make up. This has been around for a long time. It's anytime you build something that's adaptive to the space it needs to exist in. You think about, like, a cabin. A cabin is made of wood. It's in the woods.

It blends in. It feels appropriate. Everything's right. But what we can do on the web is we can build a cab we can build a house that when it's in the woods, it turns into a cabin. And that house that moves over into next to a lake and becomes a lake house. You move it into the, you know, Antarctica and it becomes an igloo. That's not what Legos do.

Legos are just little blocks, and you're like, but the blue one's next to the other blue one. I don't care anymore. It's boring, and our platform can do way better. And it's ticking me off looking at all the boring stuff that keeps getting built. So I'm hoping that this particular talk gets you pumped about where you're not done yet, where you're gonna go back to your design system and your component library, and you're gonna tell your boss, it's like, we're not done.

There's way more for us to do here. There's a lot more intelligence that I can build into this component. I need more time. And I empathize very much with not usually being given much time on the front end. That also ticks me off. That's a different talk. This talk is an extension of a blog post I wrote about six or eight months ago, something like that.

I was like, components can know. And I wanted to build the mega list, of all the things that components can know about the page. And it is absolutely incredible, and that's why this talk's about actually, all the talks so far have perfectly led up to this exact moment. Yuna's talk led up to this moment. All the talks led up to this moment of all these different things. And so we're gonna see them one by one, and I want you for each slide, ask yourself, am I using this in my design system? Do my components use this to make a better, smarter decision? If I move my card from the footer to the sidebar, does it adapt?

Or do I have to go in and reach in and be like, you're now in the sidebar, and you have to do all this dorky stuff where you have to tell it from the outside in. I want your components to just know and behave. You said you built a system, and I haven't seen systems yet. I've seen blocks that are acting like Legos, and I want you to stop building Legos.

And the other thing is I want you to think about how to combine these things. So first, they're gonna roll up easy. You're gonna have media queries. You'll be like, yeah. Media queries I got prefers color scheme. I'm responding to the user's preference, and I'm like, sweet. Combine that with other things. Unis showed a lot of combinations.

Look at this combination. You put these two things together, the scroll direction plus this, you do an animation, meaningful adaptive results that are owned by the component. That component now is truly able to move. That's another thing that bugs me. Everyone's like, I build component architecture, because it can move other places and maintain its behavior. And I'm like, yeah.

But it's the same dork over there. You build two buttons in a form, and the buttons are like, hey. I'm a button. Next to the other one, hey. I'm a button. And they don't even act like they know each other. There's no relationship there. The form isn't showing that there's buttons that they're everything can know what's inside of it now.

Everything can know what's around it now. Everything knows everything about the user's preferences. And at the end, I'm gonna tell you all the things CSS doesn't know, and then I'm gonna give you a surprise that teaches it to do it. I like having a clicker. This is gonna be really fun. I might lose my voice. I'm loving it. Okay.

Act one. This is where we're gonna talk about the user. The most important part of what's going on here. It's not about us. We like building this is why it's also one of my favorite cons conferences. We build for the user. We like all our tools, but our tools are a means to an end to deliver something beautiful and meaningful.

And so let's go through all the different ways that we can, listen to our users and let I like to think of it like a car. Okay. You get into a car and imagine you sat on okay. Right now, we sit in a car and we get, like we can adjust the seat and and, like, the steering wheel and then the temperature. And that's, like, all we get.

CSS, we wanna get in a CSS car. It gets in. It's like the color changes. It's like Batman. I'm all pimped out and back. It's like we could really form fit the experience to people way beyond the things in the physical world can do like Legos or cars. And I want us to do that. That's because our platform is amazing that way.

So if we look at the device, we've got, hey. How big is the device? Right? A physical size. The device is physical, and that's that's a whole other talk. Anyway, so we got the device size, the orientation. Often, landscape is already forgotten. We're on page one. Are you using landscape? Probably not. You're like, oh, they flip sideways and now it's all squishy and they just gotta deal with it.

You can be there and be thoughtful about it. You designers that are watching this video, hope you're watch think about landscape more. Just like Eunice calling out, don't just build for desktop. There's ways here. The pointer coarse or the pointer fine. You know if it's a sausage trying to tap, like me and my shaky fingers, like, they don't like to tap things, because it's coarse. I like to use a mouse.

Mouse is fine. It stays there and it's rested. It doesn't have tremors like I do. I'm not talking about like stage tremors. I have like little literal tremors. And then media hover is lovely. I wish it was actually reliable. We need a better way to know if someone is on a mobile device or not because the hover query is close.

You can combine the hover query with the width query and an orientation query try to figure it out. It's not that good. And you can know what the color capabilities are. And color is so fun. Isn't it, Leah? Color's fun. Alright. So then we got other preferences. What color scheme do they prefer? Let's adapt it. They get in the car.

Cool. Make it dark. You know, get in the car. Make it light. What's their preference? Reduce the motion if they need it. Reduce the transparency if they need it. Prefers contrast. More forced colors. Prefers reduced data. That's not on there. It should be. And I'm gonna fix that later, but we'll see them. Sneak peek into what I might be delivering.

But these are all really important things. And how many of these are you using in your system? You know, is your LEGO changing to these? It should be. A lot of these are just minor additions. The transparency one, I'll show a demo later. You could do adaptive frosted glass, And it doesn't have to be no no transparency. It can be reduced transparency.

You don't have to remove animation. It can be reduced animation. It's really important that you don't just throw your hands up at these things. These are micro moments where you get to shine. This is you get to shine because you know these exist. This whole talk is you know this exists. You have these tools. Apply them. I'm I desperately wanna see new component libraries.

I'm so I already said that. Okay. Anyway, reading and writing. If you just use logical properties, you get it all for free. You don't have to care. Like, I like to think about like you go to shake someone's hand. And if you say, hey, shake me with your right hand, they might not have a right hand, and it might not be their good hand. So why why don't you just say, shake me with your good hand?

Then you're just you're removing any potential for you to screw it up. You're taking yourself and your preferences out of the thing, and you're just shaking someone's good hand. When you use logical properties, you won't be screwing up a right to left language. You won't be screwing up a top to bottom right to left language. You don't have to care what language, and you write it once instead of four times.

And so many dorks are writing it four times. I don't get it. So use logical properties. And so let's talk about the surroundings, the things around your component, the things that this component you truly say a component is portable and can go everywhere. Let's talk about the things that it can respond to. One of the first things is you have it can get design tokens. These things are very common to start at the top.

They come into your component, and now it can kind of look like the rest of them. This is something most of the design systems think, they're done in. They're like, we're all using the same border radius. We're all using the same amount of padding of values so you see visual consistency. That is important. I'm very happy that we have tokens.

However, it's just a fraction of the opportunity. Where am I? So in this particular case, this is one of the ones that really set me off about knowing a a component owning its own presence and appearance anywhere inside of the page. You have container queries now that are just named queries. So in this case, the sidebar says, I'm the sidebar.

And the feed says, I'm the feed. You could also have the class, but it's not the same when you have to use has to query the parent to go all the way back down. And also named container queries compares to Shadow DOM. So if you're using container, if you're using web components, it can know that it's in a sidebar way beyond the roots that it's generally trapped inside of.

And it's also brilliant in that it's owned by the component itself. So this card, this, some sort of callout can adapt to all the different places. I'm talking about that house that knew it was in the forest, that knew it's in Antarctica. That's a named container query. You give your zones of your application names in a macro level, your components can own how they look there.

And that way when someone uses your component library and they go, oh, I wonder what happens when I put it here. And it changes and it looks beautiful. And they're like, someone thought of that, and that makes me happy. I wanna see more of that. I'm really tired that this stuff is not happening more often. Give some things room to breathe.

This is how you know how much space do I have allocated. So, right, it's similar to a name query except it's not a media query. It's not how big is the device. It's how big is the space I'm literally being placed into. How big is my grid cell that I'm placed inside? How big is the sidebar that I'm placed inside?

I am going to adapt myself. I don't need my parents to tell me what to do here. I'm a component. I'm intelligent, and I'm smart. Someone thought about these details, and they knew that the capability was there. They build it in the component. Do it. Quantity queries. How many items are in there? This is so cool. You can be very smart.

If there's only one, make it big. If it's a big list so I really like to think about micro experiences as none is a state that you need to design for. One is a state that you can design for that's even better. Three, five, these can start to adapt and look like a little bento grid. You get into nine, ten, and then you get into too many. And these are all states that you need to think about. Anytime you're rendering a list that's dynamic coming from something, you have to think about quantity queries.

What does zero look like? What does one look like? What does three? What does five? What does nine? What does too many look like? And this should be an elegant thing that this component owns that you don't have to go use JavaScript to do because CSS can do the work. What's inside? Do I have a video? Am I a card with a video?

Look different. If I if I if I've got a card with a, like, a aspect ratio image, look different. Do I have checkboxes that are checked inside of me? Forms are one of the other things that really tick me off that are so lame. They can know so much about what's going on. The field set element can know so much about what it contains, and it never adapts.

The legend can look different when you're focused within something. The legend can look different when it's valid or invalid. The form can be counting with CSS counters. How many valid items are in there and how many invalid items are in there? It's not that hard, and it's really meaningful to the user. And that form now doesn't need as much JavaScript.

It doesn't need as much logic because you've built it in. You literally built a system. You didn't just build a block. You built a system. Oh, man. This is so much fun. Okay. Cool. So style queries. These are, now based this has been a hot topic this whole conference. I'm really happy to see it. I've got a again, at the end of this talk, I'm gonna be giving you something that's gonna use a lot of style queries.

Be very excited. But, again, it's like, what's the value of a variable? We had Kevin Powell talking about putting variables on things and the power that can come from that, from meaningful icons, Adaptive intelligent components that don't need so much logic when it's just a paint issue, when it's just a meaningful visual feedback that you wanna give someone. Style queries are one of those tools for you.

We've got proximity styling. So this one's kinda interesting about where am I going to prefer my style to come from? Like, what's my proximity to another class? We have at scope, which allows also donut scoping, which is really nice so that your selectors can have an end and they don't kind of infect the entire tree all the way down.

But you've also got this ability of proximity. How close is my component to another component of another class? And if the proximity is close, you now know you're near something else, and you can be intelligent about that. So, right, we've got named container queries. We've got quantity queries. We have all of these ways for your components to be intelligent, and this list is not done.

It's a mega list of, 80 slides, y'all. Yeah. You're like, dang. My brain hurts already. And already, I've only used two of them in my design system. Crap. He sees me. I see my components also need to work on this. This is a call out for myself. That's why I said our design systems suck, and I'm not calling you out. I'm calling me out too.

And it listing these things out. It's just important for us all to hear all at once. Okay. Here's another one. Read the room. So this one's local context for the inherited cascade. You need to know what's coming in, and you can be intelligent about it. And the light dark function is really cool about that. Current color is cool about that.

You don't have to care. Not caring is a really fun way to work with CSS because CSS the opposite of CSS is overcaring, and that's controlling. And controlling gets you into all sorts of trouble. You do, however, want to build something that's smart and intelligent and is sniffing and, oh, I smell something near me. I should probably adapt.

Or hey, how about you just give me a color and I'll just be the color? I'll use contrast color later and be like, give me a color and I'll contrast against it. You know, a system, a real system. And then you can size to the inner content. Jen Simmons had a really amazing talk about intrinsic web web design.

Right? We went from, like, responsive web design, which I still feel is what we're generally stuck in. Even though Eunice called for the new responsive, we have all these names and don't have anything yet. I don't even think contextualism is a good enough name because contextualism does not encapsulate this ability to morph to more places than one.

It's always been singular, and we need to do something better. But this allows you to look inside your content and say, like, what does my content need? I like to think about this too about people. All of us here are different size, different length. We have different linked arms and everything. And, the things that we get to buy are all getting to, we can adapt to that.

They adapt to us, or we can go buy something that adapts to us. The same thing happens with here. Like, my arm can only bend in a certain way. I have a min content of the way that my arm can bend. That's a really important thing to be considerate about when I'm trying to put a cast on it or something like that.

When you're putting a box around content, ask it. What do you need? What's your max content? What's your min content? I can fit the content. Calc size was the one thing from your slide that I was like, hell yeah, man. The fact that you can put that in there and combine it with something else is very powerful and meaningful, and I'm really excited to see that progress more on the web.

And then you can animate to auto. So you can take the intrinsic values of things and go from one to the next and make it look good. You know that the min value is this, and you're going to some sort of grown value or an an or grown intrinsic value. Animate those things. Make it look intentional. Bring continuity. Let the user see the feedback that growth is happening.

We see these which is summary details, but there's way more use cases for calc size. Find Bramis' article on calc size. It will make you inspired for just all sorts of things. You hover on right. You've got a button. Bramis' pretty much key demo. You hover on an icon, and it slides out a label. The label is form fit to the label's content.

You don't have to define the width of that button. The button fits its content. It's for any language, any writing mode, and reading mode. This is what I'm saying. I'm writing less and reaching more users than you, and I want you to do it too because all of these tools are so incredible. Flow around me. So this is you can say, I've got a shape, and you can watch text go, hey, I'm aware of the shape of this thing, and it looks all elegant around there.

Looks intentional. You're doing something that's form fitting and adapting to the content around it. You can look at the thing before you, and you can look at the stuff after you. We'll see that in a second here. You know, you find you find the state of your proceeding. This is also like a premise of a lot of checkbox hacks and stuff like that where you're looking at, I'm a label.

I've got a checkbox. There's an association here. Generally, you have to put one before the other so that you get the state. But still, you can know if something next to you is done. So this is again, be aware of what's the how many things are inside of me, what kind of things are inside of this component, what's near me, what's the proximity of other classes, what's the zone I'm in based on the named container query.

I hope you're still thinking to yourself, is this in my system? Do my components use this? And how can I combine all these together? Because that is the name of our game. This is how we get inventive. This is how we win, is that we start combining these things altogether. Not that it's a competition, but it is a competition.

A little bit. You can derive things from context. This is the relative color syntax. I love this syntax. JavaScript wishes it had a syntax this powerful, because it's basically destructuring micro variables out of an object, performing operations on them, and sticking them right back in in a nice, really succinct function. So here I'm taking the color and I'm just is that oh, yeah. So the lightness, I'm doing a calc on it.

So I'm like, hey. I need you to to be just a little bit less light than whatever the brand is. And then the chrome and hue or chrome and hue, whatever. Don't care. You can derive things from color. You don't have to be so explicit. You know another thing? I wanna get I am really upset, in a lot of ways.

But the colors that I see into like you see, 13 which who spoke and said there was 1,300 colors or 1,300 variables on the face? That drives me nuts. That's just such an explicit way to go define all these colors when you could be deriving them and have less code that does more themes than those big old blocks of color.

And Open Props was guilty of this. I delivered all sorts of color themes for you, but not in version two, and I'll just give you a little taste of that here in a little bit. Yuna had this in her talk. I don't know if anybody noticed, but the color font that she was using to manage the syntax highlighting was a color font.

She tapped into and changed the color of glyphs inside of that font using, font palette values and override colors. It's a really cool way for a font that exposes these things to CSS For CSS then to adapt those levers and those features of that font for a context that's meaningful to match the theme. You can continue to build intelligence all the way into a font.

And you can grow the palette. So here's one of our first demos. This is what I was talking about. Open props has a bunch of like new color stuff going on, which also names. Why have I stuck a name to? I just hung out with you at lunch. Manuel. Thank you. Jeez. I need I need a second brain like permanent permanently.

Anyway, he showed he had a color palette where he had these little levers and he was like, it's just three props and then you can change the whole theme of my site. And I'm like, hell yeah. That's what I've got going on here too. Three props, a bunch of fun. Here's the palette that gets made by it and you can just change the hue and you get a whole entirely new color palette.

And I'm not inverting colors. I intentionally do 16 colors because I don't think you should invert a palette. You need to use this section of the palette or this section of the palette. And then the way that this uses OKLCH, and it's using lightness not mathematically, I'd say 99% of the combinations of colors that you can create here always have proper contrast.

And I'll show you that in the bottom really quick. But the this little random button is super fun. You can also take the chroma out and make a grayscale, bring the chroma back in. You can pump it up too high at which point you do start clipping, but that's on you if you really wanna pump it up. And a little bit of rotation in the hue can go a long way y'all.

Like if you go in here and you pick a color like, let's go here and let's reverse our hue rotation the other way. Look at that. It's boring when it's monochrome and it oh, that's a very cool package by Modai. His name is oh, god. David Arnie, a phenomenal color expert, who has a package that is, on MPM that you give it a color and it will give you a name for it.

And they're so fun. Yeah. Do you see these? Seashell, Pico Ivory, Toronia, palette orange, tangerine cream. I'm like, I wanna wear these or eat some of them. Great. Dwarf fortress, he sees me. I love it. And so then down here is where I show you the the contrast. So these are live contrast scores for the palettes. And the way that I define light is like this.

I just take the all those 16 colors and you define them. And as long as you have a decent healthy enough spread between the colors, you get the contrast that you need any hue. You don't care. Instead of hand managing and micromanaging a whole bunch of colors, you can make something far more dynamic that taps into the color preferences and abilities of the user's browser, giving them the maximum amount of color potential that they can.

It's really fun. So and then here's dark. So you can see I inverted the variables. I didn't invert the palette. And I think that's important to do. And so That's in open props v two. It's been in prototype book beta for about two years. There's a bunch of people that use it anyway. And this site is really fun to use.

Okay. So that was just a little rant on palettes. We wanna be readable on anything. We always wanna make something readable. The whole point of someone coming to your website is they wanna read your shit. And sometimes, you don't make it that easy, and it is really annoying to them. And it's sometimes not your own fault, but contrast color is here to make that a little bit easier.

Pick white or black. Leave it up to the browser. Pick the best thing. I want this readable. So that's a really quick and easy way to be adaptive to any color, any scenario, any tag, any user generated color, any it's very cool stuff. And then at the end of the thing, I have a, I have a package I'll be sharing with you.

But anyway, it extracts colors out of images. So if you wanna overlay a little caption that's themed with the image, and then you always wanna have text on top of it that is also excessively read. Boom. Contrast color. Okay. And we've also have this one here. So this was interesting. I added this slide after Sarah's talk. Thank you so much, Sarah.

Every single one of the talks, was so inspired. And I think I threw in slides for every single person except you know you were too I had to go right after you. That's too risky. But here's what, like, what the user wants versus what you wanna render. And I know you've seen this where you've got this happens in the Shopify admin. The whole theme is generally white right now, but at the top, we have a dark bar.

We need to know that you want a light theme preference. However, we still need to have dark areas. The power and the contrast can be really powerful and meaningful. So you now you have to balance with your component the difference between a global setting coming from the operating system, a setting coming from the switch, and a setting coming from a localized set of color scheme. Color scheme being set on the component itself.

And at that point, light dark is your best friend because you don't have to care. You're like, I told you what to do when you're in light or dark. Someone can assign me this value at any time and I can be dark here, light over here. I could be moved into the side panel, is a dark side panel, which inherits color scheme dark and now I'm in light mode because I need to pop on the call out.

This is what I mean. You wanna build a system, build a system. If you wanna build Legos, go build Legos like they did ten years ago. I haven't even had any beers yet. I'm gonna get real wicked later y'all. Oh, man. Okay. So now we have like page first component. Actually, think that's a little bit of a doubled one.

Yeah. Oh, but I this one oh, you know what? I added this because of your talk and didn't realize it was in my slides already. But here's the demo that it made me think of. Let's just navigate to that real quick. Right. So I have on this article I wrote on my blog, nerdy.dev. I have three different strategies on how to achieve nested kind of light dark themes.

This And is something that Tailwind touted at one point. It's really easy since we use classes to have a dark area inside of a light area, inside of a dark area. And I was like, I've got three ways to do that without you. It's a cool story. And you can go follow you can go check these out.

It's like using Atscope. What is it? It's Atscope, light dark color scheme and style queries. So three really powerful ways you pick the one that fits your system best. What is your system trying to optimize for? Cool. And I talked earlier about adapter frosted glasses. Just look at it. Right? This one uses light dark to adapt to it so that way you don't have to care.

And I think the the way that it looks and the way that it's meaningful is really nice. I think this one also does prefers reduced contrast because I just like to be completionist like that. I hope it does or otherwise, it just talks shit. I think it just talks shit. Okay. Apparently, I don't like to do that.

Oh, and, yeah. Arc hijacked the command shift c so that I can't do that. And then, yeah, Sarah showed everybody the little paintbrush. You and I had to fight really hard to get this paintbrush in here y'all. It needs to be easier to find that. And now I have dark adaptive frosted glass. Light. It's not just colors sometimes.

You can go way beyond this. You have to be thinking about an an adaptive system. My frosted glass is adapting intelligently, and that's cool stuff. Cool. Cool. Alright. Let's get on. How are we doing on time? We're doing alright. Hey. The irradi irradiation illusion. This is something that I've seen people talk about twice today, but they were using font weight to adjust when they're in a dark text. So light text on dark does look a little bit thicker and sometimes that's undesirable.

However, when you use font weight bold, you can cause layout shift. I'm looking at Barry. Because you literally can. If you set all the text to bold, it all, gets a little bit wider. However, if you use font variation settings grad, if you have a variable font that you're using in your design system, you can just change the gradation of it, which makes it visually look thinner, and it doesn't change the glyph box, which doesn't cause the cumulative layout shift.

Hell yeah. Alright. Let's talk about the viewport. Hey, man. Let's and that's a hot tip. You can even tweak more stuff in there. Like, have a whole blog post. Anyway, I got a blog post for a lot of stuff. You can know when you're in view or not. And this becomes really important. This is about mixing stuff too.

On my personal blog, I have view transitions that go from one blog post to the next. However, only when the card is in view does it have a view transition name. Otherwise, I would have 400 view transition names. And when you click a box, it would take 400 images because the browser's kinda done that way right now.

I try to get them to change it. So instead, took matters into my own hand. And I use scroll driven animations. And when you're in view, you have a view transition name. And now I've isolated and reduced the amount of images that need to be taken when you click, and it's much faster it's and much better. And combining adaptive behaviors and being able to know when you're in view is so cool.

And we saw Eunice showing a natural stagger. At least that's what I called it when we were playing with all these tools. You have a list, a bunch of bullets, and as they scroll into view, as they enter the bottom 25%, they fade themselves in. And each one gets an individual amount of key frames for their own entry.

And it looks staggered, but you don't have to use anything about delays. It's a very cool feature. And my stuck scroll state query, it's really cool when your UI shows that it's stuck. Jake showed my select element, and I knew that he copied a lot of my code, and I'm happy you did. Because when he scrolled up in his demo, the legend that stuck to the top changed colors and highlighted, and it indicated that it knew it was stuck.

That stuck element showed me and showed the user that's cool stuff. And it can be used in so many different ways. You can do it with toolbars that know they're stuck. We've seen, other people oh, man. Another name I'm gonna forget. Roland has a sticky header. When you scroll up and you scroll down, it collapses and expands.

We've seen a lot of really cool demos like that. Use these tools to do meaningful a system that looks intentional and does meaningful cool cool stuff. Here's a cool one. Am I filling the viewport or not? As a component, there's really meaningful moments like border radius. Border radius when you have it and it's touching the edges of the bottom looks stupid.

It does. Looks stupid. And you can very easily know, hey, am I in a container that's fitting the whole full width? Then make my border radius zero, and now I'm appropriately edge to edge full bleed. That's a really cool trick. You can also do it with clamp though. There's been a lot of different versions of this particular way to write this clamp. This is the way I like to use it, which is with the scientific notation for a really big ass number.

That means one followed by five zeros, and I just love that. You can also do it nines and all sorts of So scientific note notation is like 20 years old, and it's badass. So anyway, this just says like, hey, you're either zero pixels or the radius three depending on if you at the 100% size are less than the viewport width.

And so it makes it the clamp in the middle makes you choose one or the other, and you either have a radius or if you're full width, you don't conditional radius. It's cool. Also following that with concentric radius, look intentional. This is another big design mistake that people do. It's just kinda lazy actually, or your eye isn't trained yet.

A lot of this stuff is can't unsee. Right? You saw it, now you can't unsee it. Concentric border radius is one of those things. You'll see a little nubbin next to a really nice rounded corner and you're like, that doesn't look good. And the way that you can do it pretty easily is you take the padding in the border radius.

You make the inner one have a little bit of a calc and it's done. I do have an open draft in the CSS working group for making this automatic with a keyword. And it says like match parent. My border radius match my parent. I wanna look concentric and I don't wanna do the math. And I don't wanna have to pass props and padding just to do that. Fitting and changing viewport.

You've got all sorts of ways to fit the viewport. You need to be intelligent about these. Pick the right one. Pick a 100 d v h for the dynamic viewport height. Usually, that one's not a good choice actually because it can cause wiggles and jiggles. Yeah. Everyone's like, does this? I'm like, yeah. Good call. But you got the LVH, the low large viewport height, the small viewport height. And I'm not gonna lie.

These actually drive me nuts that we have all these options. However, it's nice to have the options because we can do something intelligent with each of these. Knowing what's in the viewport, is that status bar there or not? Where's the notch? This is you adapting to the notch. Being full bleed is a sign of an app a native application.

Are you using this? Does your application know if it's landscape and there's a notch or not? Are you filling that space, or do you have dumbass bars sitting on the side of your application? Wasted space is probably 20% of the space plus all the dorky other stuff that's in there, and now you're down to 30 viewport trying to use your sausage to scroll the screen looking like a dork. Anyway, check out the notches.

Scale with me. This is way for fluid typography. There's so many strategies here. We had a really cool one from Manuel. Now I remember your name. Tall or wide. Sorry, man. I don't know my neighbor's name, so don't take it personal. I wish I did. Tall or wide. What's the orientation? You can do orientation on the container query also.

It's not shown here. That's a really powerful thing. Your container knows if it's in a landscape or portrait mode. That's an opportunity for you to change your bento box. It's an opportunity for you to look adaptive like you were intelligent and you thought of that. You knew that that existed and you built it into your system. We've got interactions. So, it's something being interacted with.

We probably all have these in the design system, so I'm not gonna go over these. Focus visible though is cool. You should check that one out if you haven't heard it before. Focus within, another one that I feel so slept on. It's always it's there, and you it's so powerful for anything to know that your focus is inside of it, and the whole contain and multiple layers of parent elements can look like they know you're there and make it focus you.

And because you're focused there, obviously, your focus is there, so you can make the whole UI make it look like you're focused there. Your UI doesn't look just intentional with like a little focus ring. The whole form is giving you visual focus. Reduce the noise. Nice. That's very catchy. Are things valid or invalid? These are really important too.

Use user invalid or user valid, though, because they don't yell at the user before they've interacted with it. Like, if you use colon invalid, the page loads and it's like, red, you have fucked up. And and I'm like, I haven't even done anything yet. Why are you so mad at me, UI? If you use user invalid, it'll wait for you to touch it and then screw up, and then it'll go red once you blur it. And then it's like, hey.

We saw you were there, and, you did it wrong. You know? Anyway, use those. You can check to see if a video is playing or not. You can also check to see if a video is playing or not inside of you or inside of the component. Yeah. It's not I'm not a Teletubby. I didn't even think about that.

That was funny. Yeah. I know. Got a video playing inside of me. But these are cool. Like, is it paused? Change the UI. Is it playing? Reduce noise. Let them focus. Something's playing. Be cool about it. Picture in picture, a little bit more obscure, but still good to know that if something is in picture in picture, you can do something cool with your UI.

Am I open? Is a popover open? Is a popover closed? There's a lot of cool things that you can do just knowing and being aware that things are open or not. Going big is something full screen. You can do nice and appreciative things to the background of your UI knowing that they're in a full screen mode. Is your design system doing this?

Probably not. But also maybe your design system doesn't have things to go full screen. So you get a get a jail free card on that one. Alright. I'll I'll give that to you. Still empty. This is placeholder shown. This is a really cool one for inputs again. Is the input currently showing a placeholder? If it is, then that means it's empty.

It's a really strong indicator, and you can do cool things knowing that it's empty. You can call attention to it. Maybe it's the first input of the form that has placeholder shown. Meaning, you should start here, your UI can guide your eye, guide the user to this particular moment and thing you want them to do. You can check to see if something is checked or not.

We've been using that one for years. I'll skip over. But indeterminate is cool. You know these things, and you know these things about the components inside of your components, and you know this about the components outside your components. This is what all the checkbox hacks were. We were playing games by just managing checkboxes, knowing that their presence was on or off.

It's really cool. Did the browser autofill it? You can style the browser autofill. I think that's meaningful because it's usually covered in piss yellow. Who made it piss yellow? So gross. Like, I have a beautiful app here, and you wanna put yellow on these? Get out of here. Style it. Make it look like you're intentional. Own your design system.

Can somebody edit something? Change the cursor. If they hover on it, they put focus on it, show them meaningful feedback. Don't just have it be read write in a mystery. That's annoying. Drive things by scroll. Oh, yeah. Hey. This was cool. We it was like a theme of this. And I was like, I got one that does that too.

And here, let me change this to light. Oh, snap. Hold on. Turn up my brightness. Oh, there we go. Where's color scheme? Color scheme, I think you're on the HTML element. Yeah. Here we go. Light mode since better on the screen. This is a sticky element, and sticky elements are observing the scroll position. And look, I get like this natural inertia.

It's very satisfying. Anyway, scrolling. You can do this for all sorts of really cool effects. Knowing and introducing things, keeping track of the scroll, just like view, the scroll one is super cool. Yeah. You cannot save that. That's great. Did I snap? This one's really important too. The one item that snapped it's been a mystery until scroll state queries. Or you had to I did a whole talk on scroll snap here a few years ago, and there was an ugly ugly hack that we did to try to figure it out.

Does anyone remember what it was? It was a we we put a humongous gap on the grid layout or on the flex layout so that all the items that weren't snapped just weren't in the scroll port anymore. And then we looked to see with JavaScript who was still in the scroll port. And we're like, it must be snapped because the gap is a 100 VW, so anything remaining in here obviously snapped.

That was what we had to do. It's ridiculous. Now it's just a query. And you can make that element look promoted. The scroll, did it last year with, remaking the switch home screen. The switch home screen had a really intelligent way of showing the blue border that was shiny just like it does on the actual switch. Could do that with a scroll state query.

What's highlighted? This is another thing. You can change this, and you can use contrast color with that. So you can use your brand, bring in an always accessible contrasting text color on top of be intended. You said you built a system, and most of you didn't do this. This was in the HTML five boilerplate, and it was hot pink. Who remembers that?

Dang. That's not as many hands as I thought. It's all good though. That was a minute ago. So was it Paul Irish that put that in there? Yeah. That was a good choice. He's like, no one's gonna even know this exists. Watch those high everyone knows when they used it because you'd to a site and highlight text about they used the h o five boilerplate. Oh, so funny.

Target text. When someone highlights text, they share it to a friend and it gets clicked to and scrolled down to, you can make it match your theme. It doesn't have to be the browser defaults. You jump to me just like tar so just like the search text, you've got target. Did the browser just jump to me? This one's also been a really old school one, and there's lots of really cool ways to do this.

And in this particular one, I'm using margin block start to make sure that the thing that got scrolled to isn't touching the top of the viewport. Give it some space. Be intentional about your system. Use the tools. Build something badass. Continuity. We saw a whole big talk about how to make your animations look like users can follow something to somewhere else.

The continuity is really important. This helps you follow the differences and change. It's not just a flash of change. Our world things don't flash like that. They always have some motion, and so having the motion helps people feel connected to it. And, of course, like in the beginning, listen to the user preferences for all this stuff so that when they get in the car, it doesn't make them sick.

They get in the car and they say this car is fucking rad. Okay. Now we got to self. Some of these are a little bit more obscure, but, this one's like, hey. Which one am I? So this is using sibling count and sibling index. Where am I amongst my children? And that can make you, that can help you make a lot of really cool effects, staggers, arcs.

I think we had, Patrick Brose had an arc. That's a really cool use case for using the sibling index. Am I upgraded? It was his web component. Have I been defined? Am I not defined? I can make a skeleton state. I can do all sorts of meaningful things because I'm currently empty. I haven't been hydrated. You have a hook for that.

Is your design system using that? Am I the host? You can know if I'm the host of a whole bunch of other elements, or am I the host at the am I at the top layer of this particular thing? It's a cool hook for you to know that you what you can do. Who's rendering me? So this one's kinda funny.

Like, which browser am I in? Does it have the capability? So this is again like knowing enough information about where your component's rendering to make an intelligent decision, build it into the component. You have components now that anybody can use and they always look rad. You know, is that in your system? Are you using, ad supports to do that kind of work?

Am I being printed? I still think this one's fun. People print stuff all the time and you can do lots of meaningful user experience enhancements to something being printed. Don't sleep on that. Does your design system care about thing? And the component can own that. A lot of times people have like a print style sheet and they go in there and they just stuff all the selectors in there and put importance on.

But now your component can own it. If it's doing something naughty that doesn't need to be printed, it can own it and get rid of it. This one is cool because we have nth child but allows you to do what, how many it's like nth child given a filter. And that can be really nice for, again, for Kevin's demo where we had certain elements that were on sale or rare or had a rating and a few other things.

You can specifically count those and look to see where am I amongst that. How many of them are there? I can count every three of just the featured items with this particular query. That's cool stuff. Your components can know so much information about what's going on. Who comes after me? So that was like we looked earlier. You could see who's before me, and you can use has to see who's coming after me.

So again, being hyper aware of your surroundings and the state of the other components around you. So that that's that's where you fix the dumb button scenario. Hey, button. Hey, button. And that's it. That's all they know. But you could be like, oh, snap. I'm next to a reset button. I'm next to a submit button. Hey. There's four buttons here.

I should probably push the reset button all the way to the edge and tuck these other ones over here to the right. Do something intelligent looking at the content that's inside of there. Here's a p w a one. Am I installed as an app? You can just ask the media, what am I? And do something intelligent. Look intentional. Your app now looks promoted.

You do the notches. You do the installed PWA stuff. You look intentional. Your design system looks cool. You can also bleed your way into the title bar. So sometimes we install PWAs. They look like PWAs because they get the, you know, big ugly bar that's at the top. You don't have to deal with that. You can actually say, I wanna account for that.

Give me the environment variables. I'll push away. Make it transparent. And as people scroll, I look like a full bleed native application, and people won't know. The And more you spend time thinking about these things, the more time you spend making it look intentional, the more that they think they're using a native app, and the more that you get success.

Skip me if I'm off off screen. So content visibility. Is this is this particular element not even in the view viewport can be ignored by the browser renderer, and then you give it a placeholder size that says, hey. While I'm off screen, just assume I'm about 600 pixels. And you can do that for humongous lists of content, saving your initial paint, saving your users' batteries, and making it so the browser does the work to render those things as they come into the view. It's very much like a recycler view, but it's quick and easy. It's not devoid of its own little, quirks, but still it can be a very powerful way to have an intelligent list view.

Promising the browser, YFx will stay inside me. So you can say that, like, hey. I have an inch I have a width and a height. I'm like a map. Everything inside of me, you don't have to treat it. If if if things inside of me change, the rest of the page doesn't have to rerender. You can trust me that I contain all of my own work.

And these give hints to the browser that are much like will change, but they're like, I'm gonna manage my own render tree. It's a really powerful way if you're finding that you need to isolate something from the render. We've got relationships. I'm gonna burn through some of these because I wanna get to the, end of the talk here.

But this is we're talking about Anchor, a lot of people are using JavaScript to do this. Anchor, Jake said it, is one of the most powerful things that has entered into our tool belt in a long time, and he is not wrong. And we're still learning what it can do, and I wanna show you this trick that I, came up with called anchor interpolated morph. Who hears heard a flip first, last, and verb play? It's a very powerful way to do dynamic animations that go from two intrinsic states of something, and then morphs between them.

And so I found out that while I was doing git bounding client rect that actually anchor knows all the same shit. It knows the x and the y of anything that has an anchor name, and it knows the width of anything that has an anchor name, allowing me to do something like this. This is just like Yuna's demo with an interest.

The interest, of hover invokes a popover. The popover uses anchor to look at the measured width and position of the target thing being hovered from, and they both work in harmony to appear to morph from it. What's cool and not shown in the demo is that you can also morph to somewhere else. Anything with an anchor name, your element can transition it.

This is also interruptible. There's no JavaScript, and I can hover, not hover, and just cut it off in the middle of it, and it goes back perfectly. There's no key frames. Transitions, by the way, are a much more powerful thing than, transitions are much more powerful than keyframes, and I try to use transitions as often as I can.

And this technique is very powerful. It does use a wild combination of things, though. Uses at starting style. It's using all the pop over things. It's using anchor in a way that you've never seen anyone use before. It's very cool stuff. And I wrote an article explaining how it all works so that you can do it too.

So let me close that out. Yeah. Okay. Matching sizes. So this is again anchor. You can say how, what's your width? I'll just be that width. So your tool tip can match it. You can do again, anchor, so powerful. Light or dark. We kinda beat this one up. Let's just skip it. Counters are super powerful. People sleep on them.

You can nest them. They go all sorts of cool directions. You can adopt a grid. My parent defined some Collins and some Rose. I would like to just use those also for myself. Being aware of the parent grid layout. You can pass that down as many times as you want, and it makes for really meaningful alignment of content.

A distant driver with scroll container or with animation timeline, you can actually observe somebody else's intersection with the viewport. This element is going to spin based off the section and its intersection. So as this goes in and out, that's a timeline. The spin of this is off of that timeline. You can hoist up the intersection of one element into a parent container and have another container observe it.

Really cool stuff. So again, intelligence and meaningful stuff going on here. You can roll back based on cascade layers, revert to something prior, be smart about what you're doing here. Maybe you have like, this could be a blog. This could be something that intentionally needs to revert to another layer. Find my angle. This is where using trig to see, Tamani does this all the time when we saw a demo where it's like, hey. The target thing's over there.

I can use trig to know that it's that way, and then I can use anchor to know how far the distance. I've got x y, and I can calculate the triangulation. And you can get all this stuff with CSS. There's a a tan two trick right there too, which we've seen today already. Sticky can cling to the scroller, so again, being aware of the scroller in its state.

And here's a list of all the stuff that you don't have access to. So if that wasn't big enough, you also don't currently know what the window size is or the visual viewport dimensions is pixels. Jane Ori has a trick using tan and a tan two that extracts the pixel values out of the viewport so that you can use them as pixels and you don't have to use the viewport width and height.

That shit is cool. Scroll bar width. We have thin and we have thick scroll bars that change between every single operating system, and they can make you have if you use a 100 VW prior and it's getting fixed. But still, knowing what the scroll bar width is can make you have a more elegant scroll scenario. We now we don't know what the scroll velocity is.

We don't know the global position of the pointer or the mouse. We don't know the element local pointer position. If I'm hovering on a card, I wanna know where that mouse is. I don't know the element geometry. I only know relative units. I don't know element visibility. I mean, I kinda do. I have intersection observers, and I have, you know, view, with the scroll driven animations and stuff like that.

But I also don't know my elements ratio. I know if it's portrait or landscape, and I can query it, but I wanna be told what it is. I wanna be told what it is and a percentage because then I can do something even more intelligent. My form controls don't tell me the stinking ranger slider value. How many demos here were like, a little bit of JavaScript so that I could oh my god.

That's driving me nuts. Give me the value of the inputs. Tell me the slider value. I wanna fill it with goo. The generic field value. What's the value of the field? I've got meaningful places I can stick that. I wanna know the minute I know the minute and the max give it to me. I wanna know how many characters have been typed.

I can do cool shit with that. What's the we only have active and invalid and, like, a few of these things about inputs, and that's why Angular fifteen years ago came up with dirty states and pristine states and all these more meaningful ways to get has the user even touched this? And we're only just getting user invalid.

That pisses me off, and I want more. The form states. I wanna know more about the form state value in my CSS. I want my select to know what the I want my CSS to know what the select value is. I wanna know what the color input value is. If someone changes the color input, give it to me without JS.

It's driving me nuts. It's color. Device sensors. Is someone moving it? What's the geolocation? I can do cool stuff with the geo. What's the hardware? What's the battery? What's the CPU level? What's the frame rate? What's all this? JavaScript knows this stuff, y'all, but CSS doesn't. It obviously is making me kinda mad. So okay. That's why I made prop for that.

So it's a JavaScript library. You install it and it just you decorate an element and you say, I want props for the pointer. I want props for the pristine and the field values. I want props for all that shit I just listed is basically handled for you in this library so that you don't have to write the JavaScript. You get to be the CSS developer.

Some other dork, me, wrote the JavaScript for you. And I'm gonna show you a quick demo on that, and then we're, gonna wrap this up. Here, pointer position. CSS knows where it is. That's the global one. You can see it up here. These are live. I can even pause the library, and it stops live updating all the custom properties.

So it's a custom JavaScript just giving over the things that it knows that CSS doesn't straight to CSS' custom property for you to do something cool with. Look at as I hover over here on the edges. I'm using the pointer location to drive a gradient. I know what the frames per second are of the page. Hey. That's funny. When I'm not plugged into the monitor, I have a 120 frames per second, but when I'm plugged into this, I have 60. I can scroll and see the scroll velocity.

As I hover this, I can know where it is on the thing and change the hue of the whole page. Gee. I wonder how I changed the hue of the whole page and maintained contrast. Probably something I told you. So here it is. You import it like this. You put data props for size. Hey. I want props for size on this element.

And the props come in as live w, live h, and it's all plug in based. So it starts out basically giving you nothing. You opt in to every single thing you want to be observed and you tell it which component it's gonna put them on. And let me just show you a few more of these demos. Attract the pointer. Parallax?

Easy. Right? How many times have you wanted parallax because it looks sick, but you're like, that's j s for that. It's just a pain in my booty. And, you know, and I have to do all the trig, but it's once you have the variables, you can do it, and it's not so much of a big deal. And this one actually has it on the outer here.

There's a gradient here. There's a border gradient here. It's all in there. And I only wrote CSS. This whole entire page only imports this library. I literally don't write another single line of JavaScript. It's all in my CSS. I have scroll triggered, observers here for you because I don't wanna wait for the scroll triggered spec to have land in two or three years for me to get, to use it.

It has two props. Has it entered and has it gone out again? So you live live visibility in and out binary, and the other one is has it entered at all. Once it comes in, it stays that way forever so it doesn't have to keep animating. You can keep things kinda minimal on the user. We've got slider values.

Doy. Doy. And look at all the cool ways I can respond to that. We've got discrete values. So using style queries, you go up and down, and you can do cool things with color and values, project them as content, all sorts of cool stuff. Live length as you type. Yeah. Satisfying. Trigonometry. Here.

Right? They're all just looking at the pointer, and they're using a little bit of trig to point to it. We're just talking about that stuff. A clock ticking. Who had the clock in their demo again? Eric. Annules. Yeah. There were two clocks. And I was like, I got a clock to show you. It's gonna be really fun.

And it's even got a little pointer listener into it, like it's got some little gooey liquid in But I'm writing to you the seconds to hours and the because JavaScript knows it. Why doesn't CSS know it? You can do cool shit with it. Why should you have to write that JavaScript every time? This one's Chrome only most of the time.

Yeah. It says right here, but I can know if you're online. If you're on offline, what's your battery status? What's the frame rate? Scrolling with momentum. We saw that one already. A card with the shape. I'm gonna skip that one for now. This one is a live video playing, and I'm watching the progress of it because I will give you the progress value so that you can update any part of your UI that knows how far or how not far someone is into the video.

Plus, you see the color changing? Let's oh, I wonder how he did that. Well, here, I'll show you in a sec. Here's simulate low battery mode. So this one will write to you if someone's in a low battery, and then you can go take out your animations and your gradients. CSS working group probably will never give us this.

CSS working group will probably never give you most of these, which is why I took matters into my own hand. And I'm not trying to not trying to say anything mean. They just don't wanna increase the amount of private fingerprinting vectors that people can track on you. However, JavaScript is there doing it the whole time, so is the server. So why can't CSS have it?

Anyway, this one was just a clever use of using the pointers. So once you've hit active, you can see the style there. Once it's active, I track the pointer and then you get this kind of simulated drag effect, which is just kinda cool. Here's all the form states. Dirty, touched, changed. If I go back to ADA, it's dirty but not changed. It's touched.

Right? These are really valuable. What's the value of the currently selected element? How many see? What's the color? Just tell me the stinking color and I will do something cool with it. Now you don't have to. This thing will do it for you. Images. Any image, it will extract all the colors for you. It'll do it to, gradients as well, almost kinda giving you free little cool color palettes out of a gradient.

You can have a video. An off screen canvas will render the video constantly getting the colors out of it and write them back for you so that you can use them to make a cool glow around the edges. You can use either the dominant color or the accent color. And here's every single property that this bra this library supports for you so that not just 80 different things that you can respond to with your components, There's about 25 more here for you.

And I want you to be building the best components in the world and using CSS to do it because it's the one that's most capable ever. There's no design tool as powerful. There's no rendering engine that's as powerful as what we have. This is dynamic and meaningful And to the user, go out and build badass shit, y'all.

Thank you, Adam. That was aggressively smooth.

Contextualism

Adam Argyle

.css-day {
	date: 'June 11 & 12 2026';
	location: 'Amsterdam'
}

#identifier-sponsors {
	Google
	AG Grid
}

selector-sponsors {
	9ELEMENTS
	Polypane
}
A headshot of Adam Argyle, a smiling man with short hair and a beard. A stylized logo for 'CSS-DAY' composed of interlocking rectangular blocks. Logos for Google, AG Grid, 9ELEMENTS, and Polypane.

LAY DAY

.now {

16:15 | Una Kravets

.now {

17:10 | Adam Argyle

.now {

17:10 | Adam A

Google

CSS!

17:10 | Adam Argyle

.now {

Google AG Grid

A stylized 'CSS DAY' logo is displayed in the top right corner.

CSS!

CSS!

The word "CSS!" is displayed in large, pink, brush-stroke lettering on a white background.

CSS!

CSS!

The text 'CSS!' is displayed in a thick, pink, brush-stroke style.

CSS!

CSS!

The word "CSS!" is displayed in large, energetic, pink brush-stroke letters on a white background.

CSS!

CSS!

A stylized, brushstroke rendering of the word 'CSS!' in pink.

OUR DESIGN SYSTEMS SUCKS

Image of a man with a surprised expression, hands on his cheeks, next to the title text.

OUR DESIGN SYSTEMS SUCKS

An illustration of a man with a shocked expression, hands on his face, wide-eyed.

OUR DESIGN SYSTEMS SUCKS

An illustration of a man with short hair and tattoos on his arms, looking shocked with his hands on his cheeks and mouth wide open, complementing the text.

CSS DAY 2026

CONTEXTUALISM

MEGA LIST of the ways CSS can enable highly adaptive, contextually relevant, components

css-day-2026.netlify.app/

CONTEXTUALISM

CSS DAY 2026

CONTEXTUALISM

MEGA LIST of the ways CSS can enable highly adaptive, contextually relevant, components

css-day-2026.netlify.app

SYSTEMS

Not kits

ADAPTIVE ORCHESTRATION

Not just Lego's; but a highly resilient design system

ADAPTIVE ORCHESTRATION

Not just Lego's, but a highly resilient design system

CSS DAY 2026

CONTEXTUALISM

MEGA LIST of the ways CSS can enable highly adaptive, contextually relevant, components

css-day-2026.netlify.app/

CONTEXTUALISM

CSS DAY 2026

CONTEXTUALISM

MEGA LIST of the ways CSS can enable highly adaptive, contextually relevant, components

css-day-2026.netlify.app

ADAPTIVE ORCHESTRATION

Not just Lego's, but a highly resilient design system

This talk is an extension of this blog post

nerdy.dev/components-can-know

FOR EACH SLIDE

Ask yourself

  • Are my components leveraging this CSS feature?
  • How can I meaningfully combine these features for better UX?

ACT I

THE USER

what they prefer, how they read & write, and the device in their hands

THE DEVICE

the user's device size, orientation, and capabilities

@media (width >= 768px) { ... }
@media (orientation: landscape) { ... }
@media (pointer: coarse) { ... }
@media (hover) { ... }
@media (color-gamut: p3) { ... }
Screenshot of a blank application window with a macOS menu bar.

PREFERENCES

the users preferences

@media (prefers-color-scheme: dark) { - }
@media (prefers-reduced-motion: reduce) { - }
@media (prefers-reduced-transparency: reduce) { - }
@media (prefers-contrast: more) { - }
@media (forced-colors: active) { - }

prefers-color-scheme media query

Baseline Widely available

A code block displaying CSS @media queries for user preferences.

PREFERENCES

the users preferences

@media (prefers-color-scheme: dark) { _ }
@media (prefers-reduced-motion: reduce) { _ }
@media (prefers-reduced-transparency: reduce) { _ }
@media (prefers-contrast: more) { _ }
@media (forced-colors: active) { _ }

prefers-color-scheme media query

Baseline Widely available

PREFERENCES

the users preferences

@media (prefers-color-scheme: dark) { - }
@media (prefers-reduced-motion: reduce) { - }
@media (prefers-reduced-transparency: reduce) { - }
@media (prefers-contrast: more) { - }
@media (forced-colors: active) { - }

prefers-color-scheme media query

Baseline Widely available

PREFERENCES

the users preferences

@media (prefers-color-scheme: dark) { - }
@media (prefers-reduced-motion: reduce) { - }
@media (prefers-reduced-transparency: reduce) { - }
@media (prefers-contrast: more) { - }
@media (forced-colors: active) { - }

prefers color scheme media query

✓ Baseline Widely available

PREFERENCES

the users preferences

@media (prefers-color-scheme: dark) { ... }
@media (prefers-reduced-motion: reduce) { ... }
@media (prefers-reduced-transparency: reduce) { ... }
@media (prefers-contrast: more) { ... }
@media (forced-colors: active) { ... }

prefers-color-scheme media query

Baseline Widely available

A row of small circular browser logos including Chrome, Firefox, Edge, and Safari, indicating wide availability.

PREFERENCES

the users preferences

@media (prefers-color-scheme: dark) { - }
@media (prefers-reduced-motion: reduce) { - }
@media (prefers-reduced-transparency: reduce) { - }
@media (prefers-contrast: more) { - }
@media (forced-colors: active) { - }

prefers-color-scheme media query

Baseline Widely available.

PREFERENCES

the users preferences

@media (prefers-color-scheme: dark) { _ }
@media (prefers-reduced-motion: reduce) { _ }
@media (prefers-reduced-transparency: reduce) { _ }
@media (prefers-contrast: more) { _ }
@media (forced-colors: active) { _ }

prefers-color-scheme media query

Baseline Widely available

PREFERENCES

the users preferences

@media (prefers-color-scheme: dark) { - }
@media (prefers-reduced-motion: reduce) { - }
@media (prefers-reduced-transparency: reduce) { - }
@media (prefers-contrast: more) { - }
@media (forced-colors: active) { - }

prefers-color-scheme media query

✓ Baseline Widely available

Browser compatibility icons for Chrome, Firefox, Safari, and Edge indicating wide availability.

PREFERENCES

the users preferences

@media (prefers-color-scheme: dark) { _ }
@media (prefers-reduced-motion: reduce) { _ }
@media (prefers-reduced-transparency: reduce) { _ }
@media (prefers-contrast: more) { _ }
@media (forced-colors: active) { _ }

prefers-color-scheme media query

Baseline Widely available

READING & WRITING

you should be using logical properties

.card {
	padding-inline: 1rem; /* not left / right */
	margin-block: 2rem; /* not top / bottom */
	border-inline-start: 2px solid;
}
:dir(rtl) .chevron { rotate: .5turn; }

Logical properties

Baseline Widely available

READING & WRITING

you should be using logical properties

.card {
	padding-inline: 1rem;	/* not left / right */
	margin-block: 2rem;	/* not top / bottom */
	border-inline-start: 2px solid;
}

:dir(rtl) .chevron { rotate: .5turn; }

Logical properties

Baseline Widely available

Icons representing browser support for CSS logical properties.

READING & WRITING

you should be using logical properties

.card {
	padding-inline: 1rem;    /* not left / right */
	margin-block: 2rem;      /* not top / bottom */
	border-inline-start: 2px solid;
}

:dir(rtl) .chevron { rotate: .5turn; }
.card {
  padding-inline: 1rem; /* not left / right */
  margin-block: 2rem; /* not top / bottom */
  border-inline-start: 2px solid;
}

:dir(rtl) .chevron { rotate: .5turn; }

READING & WRITING

you should be using logical properties

.card {
	padding-inline: 1rem; /* not left / right */
	margin-block: 2rem; /* not top / bottom */
	border-inline-start: 2px solid;
}

:dir(rtl) .chevron { rotate: .5turn; }

Logical properties

Baseline Widely available

READING & WRITING

you should be using logical properties

.card {
	padding-inline: 1rem;	/* not left / right */
	margin-block: 2rem;	/* not top / bottom */
	border-inline-start: 2px solid;
}

:dir(rtl) .chevron { rotate: .5turn; }

Logical properties

READING & WRITING

you should be using logical properties

.card {
  padding-inline: 1rem;   /* not left / right */
  margin-block: 2rem;     /* not top / bottom */
  border-inline-start: 2px solid;
}

:dir(rtl) .chevron { rotate: .5turn; }

Logical properties

Baseline Widely available

READING & WRITING

you should be using logical properties


.card {
	padding-inline: 1rem;	/* not left / right */
	margin-block: 2rem;		/* not top / bottom */
	border-inline-start: 2px solid;
}
:dir(rtl) .chevron { rotate: .5turn; }

Logical properties

READING & WRITING

you should be using logical properties

.card {
	padding-inline: 1rem;    /* not left / right */
	margin-block: 2rem;      /* not top / bottom */
	border-inline-start: 2px solid;
}

:dir(rtl) .chevron { rotate: .5turn; }

Logical properties

✓ Baseline Widely available

A row of browser logos (Chrome, Firefox, Safari, Edge) indicating wide support for logical properties.

ACT II

THE SURROUNDINGS

its design system, where it lives, how much room it has, and what's inside

ACT II: THE SURROUNDINGS

ACT II

THE SURROUNDINGS

its design system, where it lives, how much room it has, and what's inside

DESIGN TOKENS

the app's design system

.card {	padding: var(--space);	border-radius: var(--radius);}

Custom properties

Baseline Widely available

A presentation slide titled 'DESIGN TOKENS' with stylized brushstroke text. Below the title is a code editor window displaying CSS code for a `.card` class using CSS custom properties for padding and border-radius. At the bottom, a line indicates 'Baseline Widely available' with a checkbox and several browser logos.

DESIGN TOKENS

the app's design system

.card {
	padding: var(--space);
	border-radius: var(--radius);
}

Custom properties

Baseline Widely available

A code editor displays CSS for a `.card` class, setting padding and border-radius using CSS variables. Below the code, a row of browser icons indicates wide baseline availability for custom properties.

WHERE AM I?

where they are in the app

.sidebar { container: sidebar / inline-size; }
.feed    { container: feed / inline-size; }

@container sidebar {
    .card { /* I'm in the sidebar - go compact */ }
A code snippet showing CSS container queries with a dark theme.

WHERE AM I?

where they are in the app

.sidebar { container: sidebar / inline-size; }
.feed { container: feed / inline-size; }

@container sidebar {
	.card { /* I'm in the sidebar - go compact */ }
}

Container queries

Baseline Widely available

Screenshot of a code editor displaying CSS code for defining and using container queries. Below the code, a row of browser compatibility icons (Chrome, Edge, Firefox, Safari, Opera) is shown, indicating wide availability of the feature.

WHERE AM I?

where they are in the app

.sidebar { container: sidebar / inline-size; }
.feed { container: feed / inline-size; }

@container sidebar {
    .card { /* I'm in the sidebar - go compact */ }
}

Container queries

Baseline Widely available

A code editor-like interface displays CSS code defining container queries for a sidebar and feed, with an example of a card component adapting when inside the sidebar. A checkmark icon is displayed next to the text 'Baseline Widely available', followed by several browser icons.

WHERE AM I?

where they are in the app

.sidebar { container: sidebar / inline-size; }
.feed { container: feed / inline-size; }

@container sidebar {
	.card { /* I'm in the sidebar - go compact */ }
}

Container queries

✔ Baseline Widely available (supported across major browsers)

ROOM TO BREATHE

how much space they have

.gallery { container-type: inline-size; }

@container (width >= 40ch) {
  .card { grid-template-columns: 1fr 2fr; }
}

Container queries

Baseline Widely available

ROOM TO BREATHE

how much space they have

.gallery { container-type: inline-size; }

@container (width >= 40ch) {
  .card { grid-template-columns: 1fr 2fr; }
}

The slide displays large, colorful, brush-stroke text that reads "ROOM TO BREATHE". Below this, a dark-themed code editor window shows CSS code demonstrating container queries.

.gallery { container-type: inline-size; }

@container (width >= 40ch) {
  .card { grid-template-columns: 1fr 2fr; }
}

Container queries

Baseline Widely available

A screenshot of a code editor displaying CSS code for container queries. Below the code, icons for Chrome, Edge, Firefox, and Safari are shown with green checkmarks, indicating that container queries are widely available across these browsers.

QUANTITY QUERIES

how many items they contain

/* 5 or more children? go compact */
.list:has(> :nth-child(5)) {
	--density: compact;
}

.list:has(> :only-child) { /* the lone one */ }

QUANTITY QUERIES

how many items they contain

/* 5 or more children? go compact */
.list:has(> :nth-child(5)) {
    --density: compact;
}

.list:has(> :only-child) { /* the lone one */ }
A screenshot of a code editor displaying CSS code demonstrating quantity queries using the `:has()` selector.

QUANTITY QUERIES

how many items they contain

/* 5 or more children? go compact */
.list:has(> :nth-child(5)) {
	--density: compact;
}

.list:has(> :only-child) { /* the lone one */ }
A screenshot of a code editor displaying CSS code examples.

WHAT'S INSIDE

what kind of items they contain

/* a figure holding a video */
figure:has(video) { aspect-ratio: 16 / 9; }
/* a label wrapping a checked box */
label:has(:checked) { font-weight: bold; }

has()

✓ Baseline 2023 ADOPTLY AVAILABLE

WHAT'S INSIDE

what kind of items they contain

/* a figure holding a video */
figure:has(video) { aspect-ratio: 16 / 9; }

/* a label wrapping a checked box */
label:has(:checked) { font-weight: bold; }

:has()

Baseline 2023 NEWLY AVAILABLE

A code block displays CSS examples using the :has() pseudo-class for styling a figure containing a video and a label wrapping a checked checkbox. Below the code, icons for Chrome, Edge, Firefox, and Safari are shown, indicating browser support for the :has() selector.

STYLE QUERIES

what the value of a variable for them is

@container style(--variant: danger) {
  .callout { border-color: red; }
}

Container style queries

Baseline 2026 NEWLY AVAILABLE

STYLE QUERIES

what the value of a variable for them is

@container style(--variant: danger) {
	.callout { border-color: red; }
}

Container style queries

Baseline 2023 NEWLY AVAILABLE

STYLE QUERIES

what the value of a variable for them is

@container style(--variant: danger) {
	.callout { border-color: red; }
}

Container style queries
Baseline 2026 NEWLY AVAILABLE

@scope (.card) to (.card .content) {
	/* nearest .card wins – proximity over specificity */
	a {
		color: var(--brand);
	}
}

READ THE ROOM

local context from the inherited cascade

.panel {
	color: light-dark(#222, #eee);
}

.panel .icon {
	/* adopt whatever the panel inherited */
	fill: currentColor;
}

READ THE ROOM

local context from the inherited cascade

.panel {
	color: light-dark(#222, #eee);
}

.panel .icon {
	/* adopt whatever the panel inherited */
	fill: currentColor;
}

light-dark()

READ THE ROOM

local context from the inherited cascade

.panel {
	color: light-dark(#222, #eee);
}

.panel .icon {
	/* adopt whatever the panel inherited */
	fill: currentColor;
}

Code annotation: The function light-dark() is highlighted.

Feature status: Supported in Baseline 2024, newly available across major browsers.

READ THE ROOM

local context from the inherited cascade

.panel {
  color: light-dark(#222, #eee);
}

.panel .icon {
  /* adopt whatever the panel inherited */
  fill: currentColor;
}

light-dark()

A screenshot of a code editor window displaying CSS code.

READ THE ROOM

local context from the inherited cascade

.panel {
	color: light-dark(#222, #eee);
}

.panel .icon {
	/* adopt whatever the panel inherited */
	fill: currentColor;
}

light-dark()

Baseline 2024 NEWLY AVAILABLE

READ THE ROOM

local context from the inherited cascade

.panel {
  color: light-dark(#222, #eee);
}

.panel .icon {
  /* adopt whatever the panel inherited */
  fill: currentColor;
}

light-dark()

Baseline 2024 FINALLY AVAILABLE

READ THE ROOM

local context from the inherited cascade

.panel {
  color: light-dark(#222, #eee);
}

.panel .icon {
  /* adopt whatever the panel inherited */
  fill: currentColor;
}

light-dark()

SIZE TO CONTENT

intrinsic sizing from what's inside

.tag    { inline-size: min-content; } /* hug, wrap hard */
.toast  { inline-size: max-content; } /* never wrap */
.dialog { inline-size: fit-content; } /* grow till it fits */
A code snippet displayed in a gray box, resembling a terminal or editor window, demonstrating CSS properties for intrinsic sizing.

SIZE TO CONTENT

intrinsic sizing from what's inside

.tag { inline-size: min-content; } /* hug, wrap hard */
.toast { inline-size: max-content; } /* never wrap */
.dialog { inline-size: fit-content; } /* grow til it fits */

SIZE TO CONTENT

intrinsic sizing from what's inside

.tag    { inline-size: min-content; } /* hug, wrap hard */
.toast  { inline-size: max-content; } /* never wrap */
.dialog { inline-size: fit-content; } /* grow 'til it fits */

SIZE TO CONTENT

intrinsic sizing from what's inside

.tag    { inline-size: min-content; } /* hug, wrap hard */
.toast  { inline-size: max-content; } /* never wrap */
.dialog { inline-size: fit-content; } /* grow til it fits */

SIZE TO CONTENT

intrinsic sizing from what's inside

.tag    { inline-size: min-content; } /* hug, wrap hard */
.toast  { inline-size: max-content; } /* never wrap */
.dialog { inline-size: fit-content; } /* grow til it fits */
A stylized code editor window displays CSS properties.

SIZE TO CONTENT

intrinsic sizing from what's inside

.tag    { inline-size: min-content; } /* hug, wrap hard */
.toast  { inline-size: max-content; } /* never wrap */
.dialog { inline-size: fit-content; } /* grow til it fits */

ANIMATE TO AUTO

interpolating to & from intrinsic keywords

:root { interpolate-size: allow-keywords; }

.accordion {
  block-size: 0;
  transition: block-size .3s;
}

.accordion[open] { block-size: max-content; }

interpolate-size

Limited availability

Below the code block, there is an icon representing an experimental feature, followed by the text "Limited availability", and then icons for browser compatibility (Chrome, Edge, Firefox, Safari).

ANIMATE TO AUTO

interpolating to & from intrinsic keywords

:root { interpolate-size: allow-keywords; }

.accordion {
	block-size: 0;
	transition: block-size .3s;
}
.accordion[open] { block-size: max-content; }

interpolate-size

Limited availability

A dark-themed code editor window displays CSS code for an accordion component, demonstrating the `interpolate-size` property. Below the code, browser compatibility icons indicate limited availability for `interpolate-size` across different browsers.

ANIMATE TO AUTO

interpolating to & from intrinsic keywords

:root { interpolate-size: allow-keywords; }

.accordion {
  block-size: 0;
  transition: block-size .3s;
}

ANIMATE TO AUTO

interpolating to & from intrinsic keywords

:root { interpolate-size: allow-keywords; }

.accordion {
  block-size: 0;
  transition: block-size .3s;
}
.accordion[open] { block-size: max-content; }

interpolate-size

Limited availability

FLOW AROUND ME

physical geometry adaptations for content

.avatar {
	float: inline-start;
	shape-outside: circle(50%);
	shape-margin: 1rem;
}
/* text hugs the circle, not the box */

shapes

Baseline Widely available

FLOW AROUND ME

physical geometry adaptations for content

.avatar {
    float: inline-start;
    shape-outside: circle(50%);
    shape-margin: 1rem;
}
/* text hugs the circle, not the box */

A screenshot of a code editor displaying CSS code. The code defines an .avatar class with properties for float, shape-outside, and shape-margin, along with a comment explaining that text hugs the circle, not the box.

FLOW AROUND ME

physical geometry adaptations for content

.avatar {
  float: inline-start;
  shape-outside: circle(50%);
  shape-margin: 1rem;
}
/* text hugs the circle, not the box */

shapes

Baseline Widely available

A code editor window displays CSS demonstrating `shape-outside` for text to flow around a circular avatar. Browser compatibility icons are shown at the bottom.

THE ONE BEFORE ME

adapting to the preceding element's input state

/* float the label once its input has a value */
input:placeholder-shown + label { translate: 0 1.4em; }

/* light up everything after a checked toggle */
input:checked ~ .panel { display: grid; }
A code editor interface displaying CSS code examples.

THE ONE BEFORE ME

adapting to the preceding element's input state

/* float the label once its input has a value */
input:placeholder-shown + label { translate: 0 1.4em; }
/* light up everything after a checked toggle */
input:checked ~ .panel { display: grid; }

DERIVE FROM CONTEXT

spinning new colors out of an existing one

.btn {
  background: var(--brand);
  border-color: oklch(from var(--brand) calc(l - .2) c h);
}

&:hover {
  background: oklch(from var(--brand) calc(l - .1) c h);
}

Relative colors

DERIVE FROM CONTEXT

spinning new colors out of an existing one

.btn {
  background: var(--brand);
  border-color: oklch(from var(--brand) calc(l - .2) c h);
  &:hover {
    background: oklch(from var(--brand) calc(l - .1) c h);
  }
}

Relative colors

alc(1 - .2) c h);
alc(1 - .1) c h);

DERIVE FROM CONTEXT

spinning new colors out of an existing one

.btn {
  background: var(--brand);
  border-color: oklch(from var(--brand) calc(1 - .2) c h);
}

&:hover {
  background: oklch(from var(--brand) calc(1 - .1) c h);
}

Relative colors

Screenshot of a code editor displaying CSS/SCSS code that uses `oklch` and `calc` with CSS variables for dynamic color adjustments on a button's background and border, including a hover state.

DERIVE FROM CONTEXT

spinning new colors out of an existing one

.btn {
	background: var(--brand);
	border-color: oklch(from var(--brand) calc(1 - .2) c h);
}

&:hover {
	background: oklch(from var(--brand) calc(1 - .1) c h);
}

Relative colors

Baseline 2024 NEWLY AVAILABLE

A code editor screenshot showing CSS rules for a button, utilizing `oklch` for relative colors derived from a `--brand` variable for `border-color` and `background` on hover.

DERIVE FROM CONTEXT

spinning new colors out of an existing one

.btn {
  background: var(--brand);
  border-color: oklch(from var(--brand) calc(l - .2) c h);
  &:hover {
    background: oklch(from var(--brand) calc(l - .1) c h);
  }
}

Relative colors

DERIVE FROM CONTEXT

spinning new colors out of an existing one

.btn {
  background: var(--brand);
  border-color: oklch(from var(--brand) calc(l - .2) c h);
}
&:hover {
  background: oklch(from var(--brand) calc(l - .1) c h);
}

Relative colors

Baseline 2024 NOT YET AVAILABLE

REPAINT THE GLYPHS

adapting a color font's palette to the use case

@font-palette-values --brandish {
  font-family: "Nabla";
  override-colors: 0 var(--brand), 1 var(--accent);
}

.display { font-palette: --brandish; }

font palette

✓ Baseline Widely available

font-palette

@font-palette-values --brandish {
	font-family: "Nabla";
	override-colors: 0 var(--brand), 1 var(--accent);
}

.display { font-palette: --brandish; }

Baseline Widely available

A code example demonstrating CSS `@font-palette-values` and `font-palette` properties. Below the code, there's a browser compatibility indicator showing wide availability across Chrome, Edge, Firefox, and Safari.

16 dynamic color props

Open Props has 16 supercharged oklch variables for making palettes of any hue (can i use oklch). This enables 360 hues and all sorts of chromas to be a palette, not some limited set of indigo, pink, cyan, etc sets like before.

tldr;

Open Props v1 has 19 color palettes, each with 12 color variables.

Open Props v2 has 360*1000 color palette,s in just 19 variables.

In this new workflow you import the palette props. This gives you access to --color--{1-16}. Customize the palette with 1-3 palette props:

  • --palette-hue
  • --palette-hue-rotate-by
  • --palette-chroma

Tint with any hue, add hue rotation along the palette for intrigue, and choose the vibrance. Here's an example of the CSS you'll write to customize the palette, as well as handy levers and a fun shuffle button.

@import "open-props/palette.css";
A horizontal color palette displays a gradient from light pink and purple hues to darker purples and blues.

16 dynamic color props

Open Props has 16 supercharged v12k variables for making palettes of any hue (calc(var(--_l) * 1deg)) This enables 360 hues and all sorts of chromas to be a palette, not some limited set of indigo, pink, cyan, etc sets like before.

tldr;
Open Props v1 has 19 color palettes, each with 12 color variables.
Open Props v2 has 360*1000 color palettes in just 19 variables.

In this new workflow you import the palette props. This gives you access to ~@palette-(1)-(X). Customize the palette with 1-3 palette props.

  • --palette-hue
  • --palette-hue-rotatability
  • --palette-chroma

Tint with any hue, add hue rotation along the palette for intrigue, and choose the vibrance. Here's an example of the CSS you'll write to customize the palette, as well as handy levers and a fun shuffle button.

@import 'open-props/palette.css';

A horizontal gradient color palette displaying various shades of purple.

16 dynamic color props

Screenshot of the Open Props documentation website, showing the 'Color' section. A horizontal color palette displaying shades of purple is at the bottom.

Open Props: Color Customization

Tint with any hue, add hue rotation along the palette for intrigue, and choose the vibrance. Here's an example of the CSS you'll write to customize the palette, as well as handy levers and a fun shuffle button.

@import "open-props/palette.css";

.palette-scope {
  --palette-hue: 358;
  --palette-hue-rotate-by: -5;
  --palette-chroma: 0.9;
}

Your Palette

Open Props

@import "open-props/palette.css";

.palette-scope {
  --palette-hue: 82;
  --palette-hue-rotate-by: -5;
  --palette-chroma: 0.9;
}

Your Palette

Sweet Frosting var(--color-1) oklch(0.98 0.027 82)

Xâkestari White var(--color-2) oklch(0.97 0.054 77)

Cookie var(--color-3) oklch(0.93 0.09 72)

Rich Honey var(--color-4) oklch(0.84 0.108 67)

Pumpkin Vapour var(--color-5) oklch(0.8 0.144 62)

Screenshot of the Open Props web application demonstrating color palette customization. The screen displays CSS code for a `.palette-scope` containing variables `--palette-hue`, `--palette-hue-rotate-by`, and `--palette-chroma`, each with an interactive slider. Below the code is a section titled "Your Palette" showing a horizontal color gradient ranging from light yellow to dark brown, and several individual color swatches with descriptive names and oklch color values.

Open Props

Screenshot of the Open Props website, showing a color palette customization interface. It features sliders to adjust `--palette-hue`, `--palette-hue-rotate-by`, and `--palette-chroma` using CSS custom properties. Below these controls, a generated color palette is displayed with a gradient row and individual color swatches, each labeled with a name (e.g., Sweet Frosting, Xâkestari White, Cookie, Rich Honey, Pumpkin Vapour) and its `oklch` color value.

Open Props

Screenshot of the Open Props application interface, showing controls for adjusting color palette properties like hue and chroma, and displaying the resulting color palette with named color swatches such as "Sweet Frosting", "Xäkestari White", "Cookie", "Rich Honey", and "Pumpkin Vapour".

Open Props

A screenshot of the Open Props web application demonstrating color palette generation and adjustment, showing sliders for hue, hue rotation, and chroma, and displaying a palette of warm, earthy color swatches with names like Sweet Frosting, Xikestari White, Cookie, Rich Honey, and Pumpkin Vapour, along with their CSS variable references and OKLCH values.

Open Props

A screenshot of the 'Open Props' web application, demonstrating a color palette generator. The interface shows navigation on the left, controls with sliders for adjusting `palette-hue`, `palette-hue-rotate`, and `palette-chroma`, and a display of a generated color palette titled "Your Palette". Below the main palette, there are five individual color swatches, each labeled with a name such as "Sweet Frosting", "Xäkesteri White", "Cookie", "Rich Honey", and "Pumpkin Vabour", along with their respective CSS variable names and OKLCH values.

Open Props

Screenshot of the Open Props web application, displaying its color palette generator interface. The interface includes a navigation sidebar, a search bar, a code editor-like section with sliders to adjust CSS variables for hue, hue rotation, and chroma, a horizontal gradient color bar, and a grid of individual color swatches with names and OKLCH color values.

Open Props

Screenshot of the Open Props web application, displaying a color palette generator. The interface shows CSS variables for hue, hue rotation, and chroma, with interactive sliders to adjust these values. Below, a generated color palette features various shades of green, each labeled with a name and its corresponding OKLCH color value.

Open Props

Screenshot of the Open Props web application showing a color palette generator. It features controls for hue, hue rotation, and chroma, and displays a generated palette of various green shades with specific named colors and their OKLCH values.
<section class='slide-text'> <h3>Open Props</h3> <nav aria-label="Application navigation"> <ul> <li>Playground</li> <li>Examples <span>5</span></li> <li>Guides <ul> <li>Get Started</li> <li><strong>Color</strong></li> <li>Customizing</li> <li>Tooling</li> </ul> </li> <li>Shop Props</li> <li>Prop Packs <ul> <li>Index <span>v1</span></li> <li>Index <span>v2</span></li> <li>Animate</li> <li>Forms</li> <li>Normalize <span>v1</span></li> <li>Normalize <span>v2</span></li> <li>Utilities</li> </ul> </li> </ul>

Open Props

Screenshot of the Open Props web application, displaying a color palette feature with various named color swatches, their CSS variable names, and Oklch values, alongside a navigation sidebar.

Open Props

Screenshot of the Open Props application displaying a color palette with named color swatches and their oklch values, alongside a navigation sidebar.
<section class='slide-text'> <h3>Open Props</h3> <ul> <li>Color</li> </ul> <p>Light / Dark Tabs</p> <h4>Light Theme Color Palette</h4> <ul> <li>Surface 3 (AA 19.4)</li> <li>AA 9.5</li> <li>AA 16.5</li> <li>Well 1 (AA 6.1)</li> <li>AA 10.6</li> <li>Well 2 (AA 4.4)</li> <li>AA 7.6</li> </ul> <h4>Dark Theme Color Palette</h4> <ul> <li>Surface 3 (AA 11.2)</li> <li>AA

Accessibility contrast ratios for two color palettes: AA 7.6 (score 2) and AA 19.4 (score 2).

Theme selection: Light (selected) and Dark.

[data-theme="light"], .light {
    --surface-1: white;
    --surface-2: var(--color-1);
    --surface-3: var(--color-3);
    --surface-document: var(--color-4);
    --well-1: var(--color-5);
    --well-2: var(--color-6);
    --text-1: var(--color-16);
    --text-2: var(--color-12);
}

Tip

A screenshot of a web page interface demonstrating color theme definition in CSS. It features two color blocks displaying accessibility contrast scores: one showing AA 7.6 and the other AA 19.4, both associated with the number 2. Below these, there are two tabs for theme selection, 'Light' and 'Dark', with 'Light' currently active. The main content area displays CSS code defining custom properties for a light theme, such as `--surface-1: white;` and various `var(--color-N)` assignments for surfaces, wells, and text. A 'Tip' section with a rocket icon is partially visible at the bottom.

Open Props: Color Theming

This section demonstrates Light and Dark themes, illustrating contrast accessibility ratings (AA values) for various UI elements like surfaces and wells.

[data-theme="dark"], .dark {
	--surface-1: var(--color-11);
	--surface-2: var(--color-12);
	--surface-3: var(--color-13);
	--surface-document: var(--color-14);
	--well-1: var(--color-15);
	--well-2: var(--color-16);
	--text-1: var(--color-1);
	--text-2: var(--color-5);
}

Tip: You can also build a dark theme with an entirely...

Screenshot of the Open Props web application, displaying the "Color" section. The main content area shows side-by-side examples of "Light" and "Dark" themes, each presenting various color swatches labeled as "Surface" and "Well" with associated AA contrast accessibility ratings. Below the theme examples, a code editor displays CSS variables used to define the dark theme.

Open Props: Your Palette

Screenshot of the Open Props web application, displaying a color palette with named color swatches and their oklch values.

Open Props

Open Props has 16 supercharged <color/> variables for making palettes of any hue (sRGB colorspace). This enables 360 hues and all sorts of chromas to be a palette, not some limited set of indigo, pink, cyan, etc. sets like before.

🚧 tldr;
Open Props v1 has 19 color palettes, each with 12 color variables.
Open Props v2 has 360*1000 color palette.s in just 19 variables.

In this new workflow you import the palette props. This gives you access to --color-l--(--x)--. Customize the palette with 1-3 palette props.

  • --palette-hue
  • --palette-hue-rotate-by
  • --palette-chroma

Tint with any hue, add hue rotation along the palette for intrigue, and choose the vibrance. Here's an example of the CSS you'll write to customize the palette, as well as handy levers and a fun shuffle button.

@import "open-props/palette.css";

palette-scope {
  --palette-hue: 47;
}

A color palette showing a gradient of warm, orange-toned colors, with a slider control for adjusting the hue.

GROW THE PALETTE

one brand color, a whole adaptive scale

:root {
  --palette-hue: 358;
  --palette-hue-rotate-by: -5;
  --palette-chroma: 0.9;
}

opv2-beta.netlify.app/color

Relative colors

Baseline 2024 NOW AVAILABLE

A screenshot of a web application demonstrating CSS variable usage for creating adaptive color palettes, with a URL to the tool.

READABLE ON ANYTHING

let the browser choose the ink

.chip {
	background: var(--bg);
	color: contrast-color(var(--bg));
}

contrast-color()

Baseline 2026 NEWLY AVAILABLE

A code editor-like window displaying CSS code for a `.chip` class.

contrast-color()

.chip {
  background: var(--bg);
  color: contrast-color(var(--bg));
}

Baseline 2026 NEWLY AVAILABLE

A dark-themed code editor window displays CSS code defining a `.chip` class. The code sets the background to a CSS variable `--bg` and the text color using the `contrast-color()` function with the same `--bg` variable. Below the code, a row of browser icons, including Chrome, Firefox, Safari, and Edge, indicates compatibility for the `contrast-color()` function.

PREFER VS OPT-IN

what the user wants, vs what you render


/* the user's request */
@media (prefers-color-scheme: dark) {
	:root { color-scheme: light dark; }
	.invoice { color-scheme: light; }
}

PREFER VS OPT-IN

what the user wants, vs what you render

/* the user's request */
@media (prefers-color-scheme: dark) { ... }
:root { color-scheme: light dark; }
.invoice { color-scheme: light; }

PREFER VS OPT-IN

what the user wants, vs what you render


/* the user's request */
@media (prefers-color-scheme: dark) {
  :root { color-scheme: light dark; }
  .invoice { color-scheme: light; }
}
A code snippet displayed within a dark, stylized block, resembling a code editor window.

PREFER VS OPT-IN

what the user wants, vs what you render

/* the user's request */
@media (prefers-color-scheme: dark) { }
:root { color-scheme: light dark; }
.invoice { color-scheme: light; }

CSS Code for Color Scheme Preferences

/* the user's request */
@media (prefers-color-scheme: dark) { ... }

:root { color-scheme: light dark; }
.invoice { color-scheme: light; }
Screenshot of a code editor displaying CSS code for managing color schemes, including a media query for dark mode preference and color-scheme properties for root and invoice elements.

PAGE VS COMPONENT

three strategies for who owns light & dark

:root { color-scheme: light dark; }
.sidebar { color-scheme: dark; }

.card {
	color-scheme: light dark;
	background: light-dark(white, #222);
}

nerdy.dev/page-and-component-light-dark-strategies

Component Light/Dark

A light-themed UI component, represented by a rounded rectangle, is displayed against a dark background.

Component Light/Dark

A visual representation of a component, possibly demonstrating light and dark mode, with a light gray outer container and a dark gray inner rectangle.

Component light/Dark

Dark version of the site

light and dark elements,

sometimes you want more

be tricky.

Table of contents

  • Today
  • Color-scheme and light-dark()
  • Future
  • @scope
  • Style Queries
A dark-themed user interface or web page is shown, with a darker rectangular header-like element in the top left. A scrollbar is visible on the right side of the main content area.

Table of contents

  • Today
    • Color-scheme and light-dark()
  • Future
    • @scope
    • Style Queries
Screenshot of a UI demonstration or development environment showing 'Dark' and 'Light' theme previews, with UI components featuring icons and dropdowns.

Table of contents

  • Today
  • Color-scheme and light-dark()
  • Future
  • @scope
  • Style Queries
A CodePen embed is visible on the left side of the slide, demonstrating light and dark themes. It features a dark grey section labeled 'Dark' and a light grey section labeled 'Light'. Above the demo, the text 'EDIT ON CODEPEN' is visible, and below it are controls like '0.25x' and 'Rerun'. Further down, two rectangular UI elements with multiple small circular icons are displayed.

Table of contents

Screenshot of a web interface, likely a CodePen demo, demonstrating interactive controls for selecting different color themes. The left side shows three horizontal options, with one highlighted in a dark background, and partial text labels indicating theme selections like "Dark" and "Light". On the right, a "Table of contents" is displayed, listing items such as "Color-scheme and light-dark()" and "@scope".
background: light-dark(#ddd, #222);
color: light-dark(#222, #ddd);
}

Table of contents

  • Today
  • Color-scheme and light-dark()
  • Future
  • @scope
  • Style Queries

Can I Use?

Screenshot of a code editor displaying a CSS snippet using the light-dark() function. Below the code, a visual demonstration shows two columns of nested light and dark theme boxes. The left column has a dark box, then a light box, then a dark box. The right column has a light box, then a dark box, then a light box.

Table of contents

  • Today
  • Color-scheme and light-dark()
  • Future
  • @scope
  • Style Queries

Can I Use?

@scope

Baseline 2025 NEWLY AVAILABLE

We don't get to query or do anything "just in time", but we are able to change colors and any styles we want within a light or dark context.

Style Queries

A CodePen-like interface is displayed, featuring HTML, CSS, and Result panels. The Result panel demonstrates nested light and dark themes with boxes labeled 'Light' and 'Dark'. A 'Run Pen' button is visible. Below, a compatibility section for @scope shows 'Baseline 2025 NEWLY AVAILABLE' with icons indicating support from Chrome, Edge, Firefox, Safari, Opera, Samsung Internet, and WebKit.

PAGE VS COMPONENT

three strategies for who owns light & dark

:root { color-scheme: light dark; }
.sidebar { color-scheme: dark; }

.card {
  color-scheme: light dark;
  background: light-dark(white, #222);
}

nerdy.dev/page-and-component-light-dark-strategies

A code editor window displaying CSS rules for managing color schemes for a root element, sidebar, and card, including the use of the light-dark() function for background.

ADAPTIVE FROSTED GLASS

a glass tint that adapts to light & dark

.glass {
	background: light-dark(
		oklch(100% 0 0 / 40%),
		oklch(20% 0 0 / 40%)
	);
}

codepen.io/argyleink/pen/wvQNpgm

ADAPTIVE FROSTED GLASS

a glass tint that adapts to light & dark

.glass {
  background: light-dark(
    oklch(100% 0 0 / 40%),
    oklch(20% 0 0 / 40%)
  );
}

codepen.io/argyleink/pen/wvQNPgm

A screenshot of a code editor showing a CSS code block defining adaptive frosted glass styles.

HTML

<figure class="card">
    <picture>
      <img
        src="https://picsum.photos/720/480"
        width="720" height="480" alt=""
      >
    </picture>
    <figcaption>
      <h2>Top 10 most beautiful
      image</h2>
      <p>12K 👍</p>
    </figcaption>
  </figure>

CSS

.card > figcaption {
    color: light-dark(#000, #fff);
    border-image: fill 0 conic-
      gradient(light-dark(#fff8, #0008) 0
    0);
  }

  @layer demo.support {
    * {
      box-sizing: border-box;
    }
  }

JS (Babel)

Screenshot of a web development environment, likely CodePen, showing three code editor panes: HTML, CSS, and an empty JavaScript pane. A large, blank white preview pane is visible on the right. The HTML code defines a figure element for a card with an image and a caption. The CSS code styles the card's caption, utilizing `light-dark()` for color and a `conic-gradient()` for the border image, alongside a `@layer` rule for `box-sizing`.
<figure class="card">
  <picture>
    <img src="https://picsum.photos/720/480" width="720" height="480" alt=""/>
  </picture>
  <figcaption>
    <h2>Top 10 most beautiful
    image</h2>
    <p>12K &#128077;</p>
  </figcaption>
</figure>
.card > figcaption {
  color: light-dark(#000, #fff);
  border-image: fill 0 conic-
    gradient(light-dark(#fff8, #0008) 0
  );
}

@layer demo.support {
  * {
    box-sizing: border-box;
  }
}
A screenshot of a web development environment, displaying HTML, CSS, and an empty JavaScript panel in a code editor on the left. On the right, a live preview shows a web component card. The card features an image of a silver laptop on a wooden desk with a tiled wall in the background. A semi-transparent overlay at the bottom of the image displays the title 'Top 10 most beautiful image' and '12K' with a thumbs-up emoji.

Adaptive scrims

<figure class="card">
  <picture>
    <img
      src="https://picsum.photos/720/480"
      width="720" height="480" alt="">
  </picture>
  <figcaption>
    <h2>Top 10 most beautiful
    image</h2>
    <p>12K &#128077;</p>
  </figcaption>
</figure>
display: grid;
place-content: center;
padding: 8vmin;
}

img {
  max-inline-size: 100%;
  block-size: auto;
}

h2 {
  text-wrap: balance;
}

.card {
  display: grid;
  align-items: end;
  border-radius: 20px;
  overflow: clip;
}
A screenshot of a web development environment, displaying HTML and CSS code panels on the left and a live preview of the rendered component on the right. The live preview shows a rounded rectangular card featuring an image of a silver laptop with an Apple logo on a wooden table. Below the image, the text "Top 10 most beautiful image" is displayed, followed by "12K" and a thumbs-up emoji.

HTML

<figure class="card">
  <picture>
    <img
      src="https://picsum.photos/720/480"
      width="720" height="480" alt="">
  </picture>
  <figcaption>
    <h2>Top 10 most beautiful
    image</h2>
    <p>12K 👍</p>
  </figcaption>
</figure>

CSS

  backdrop-filter: blur(5px);
}
...
  gap: 1em;
}
...
  padding: 1rem;
}
  backdrop-filter: blur(5px);
}
...
A screenshot of a code editor on the left showing HTML and CSS code, and on the right, the rendered output. The output is a card component with an image of a laptop on a table. Overlaid on the bottom of the image is a semi-transparent, blurred text area containing the title "Top 10 most beautiful image" and "12K 👍".

HTML

<figure class="card">
	<picture>
		<img
		src="https://picsum.photos/720/480"
		width="720" height="480" alt=""/>
	</picture>
	<figcaption>
		<h2>Top 10 most beautiful
		image</h2>
		<p>12K ✨</p>
	</figcaption>
</figure>

CSS

.card > figcaption {
	color: light-dark(#000, #fff);
	border-image: fill 0 conic-
	gradient(light-dark(#fff8, #0008) 0
	0);
}

@layer demo.support {
	* {
		box-sizing: border-box;
		margin: 0;
	}
}

html {
	block-size: 100%;
	color-scheme: dark light;
}

body {
	min-block-size: 100%;
}
Screenshot of a web development environment showing HTML and CSS code on the left, and the rendered output on the right. The output displays an image of a laptop on a wooden table, with a title and like count overlaid on a translucent background at the bottom.
<section class='slide-text'><h3>Adaptive scrims</h3><p>HTML code:</p><pre><code data-lang='html'>&lt;figure class=&quot;card&quot;&gt; &lt;picture&gt; &lt;img src=&quot;https://picsum.photos/720/480&quot; width=&quot;720&quot; height=&quot;480&quot; alt=&quot;&quot;&gt; &lt;/picture&gt; &lt;figcaption&gt; &lt;h2&gt;Top 10 most beautiful image&lt;/h2&gt; &lt;p&gt;12K &#

Code Demonstration of Adaptive Styling

<figure class="card">
  <picture>
    <img
      src="https://picsum.photos/720/480"
      width="720" height="480" alt="">
  </picture>
  <figcaption>
    <h2>Top 10 most beautiful
      image</h2>
    <p>12K &#x1f44d;</p>
  </figcaption>
</figure>
.card > figcaption {
  color: light-dark(#000, #fff);
  border-image: fill 0 conic-
    gradient(light-dark(#fff8, #0008) 0
    0);
}

@layer demo.support {
  * {
    box-sizing: border-box;
    margin: 0;
  }
}

html {
  block-size: 100%;
  color-scheme: dark light;
}

body {
  min-block-size: 100%;
}

Chrome DevTools Styles Panel:

Current element selector: body.room-editor.editor.s

element.style { }

/* From twilight-17_591.css:188 */
body.editor {
  background: #1d1e22;
}

/* From editor-28e5_5f.css:3077 */
body {
  display: -webkit-box;
  display: -webkit-flex;
  display: flex;
  /* ... */
}
Screenshot of a web development environment, showing an HTML editor on the left, a CSS editor below it, and a live browser preview on the right. The browser preview displays a card with an image and the title 'Top 10 most beautiful image'. To the far right, Chrome DevTools is open, displaying the 'Styles' tab with various CSS rules for the page. The browser used appears to be Arc.
<figure class="card">
  <picture>
    <img
      src="https://picsum.photos/720/480"
      width="720" height="480" alt=""
    />
  </picture>
  <figcaption>
    <h2>Top 10 most beautiful
    image</h2>
    <p>12K 👍</p>
  </figcaption>
</figure>
.card > figcaption {
  color: light-dark(#000, #fff);
  border-image: fill 0 conic-
    gradient(light-dark(#fff8, #0008) 0
  );
}

@layer demo.support {
  * {
    box-sizing: border-box;
    margin: 0;
  }
}

html {
  block-size: 100%;
  color-scheme: dark light;
}

body {
  min-block-size: 100%;
}
Screenshot of a web development environment, likely an online IDE. The left side displays an HTML editor with code for a `figure` element containing an image and a `figcaption`. Below it, a CSS editor shows rules for the card, including `light-dark()` functions and `color-scheme: dark light;`. The middle-right panel displays a live preview of the rendered HTML, showing an image of a laptop on a desk with a title "Top 10 most beautiful image" and "12K 👍" overlay. To the far right, browser developer tools are open, displaying the HTML structure and CSS styles.

Top 10 most beautiful image

HTML Code

<figure class="card">
  <picture>
    <img
    src="https://picsum.photos/720/480"
    width="720" height="480" alt=""/>
  </picture>
  <figcaption>
    <h2>Top 10 most beautiful
    image</h2>
    <p>12K &#128077;</p>
  </figcaption>
</figure>

CSS Code

.card > figcaption {
  color: light-dark(#000, #fff);
  border-image: fill 0 conic-
    gradient(light-dark(#fff8, #0008) 0
    0);
}

@layer demo.support {
  * {
    box-sizing: border-box;
    margin: 0;
  }
}
html {
  block-size: 100%;
  color-scheme: dark light;
}
body {
  min-block-size: 100%;
}
Screenshot of a web development environment previewing a webpage. The webpage shows an image of a silver laptop with an Apple logo, viewed from behind, resting on a wooden table with a smartphone nearby. A caption below the image reads 'Top 10 most beautiful image' and '12K likes'.

Adaptive scrims

Adam Argyle

<figure class="card">
  <picture>
    <img
      src="https://picsum.photos/720/400"
      width="720" height="400" alt="">
  </picture>
  <figcaption>
    <h2>Top 10 most beautiful
      image</h2>
    <p>12K 👍</p>
  </figcaption>
</figure>
.card > figcaption {
  color: light-dark(#000, #fff);
  border-image: fill 4 conic-
    gradient(light-dark(#fff, #000) 0 0);
}

@layer demo.support {
  * {
    box-sizing: border-box;
    margin: 0;
  }
  html {
    block-size: 100%;
    color-scheme: dark light;
  }
  body {
    min-block-size: 100%;
  }
}
Screenshot of a web development IDE displaying HTML and CSS code on the left panels, and a live preview of the resulting webpage on the right. The webpage preview shows a laptop on a table with the text "Top 10 most beautiful image". A browser DevTools panel is also visible on the far right of the screenshot.

ADAPTIVE FROSTED GLASS

a glass tint that adapts to light & dark

.glass {
	background: light-dark(
		oklch(100% 0 0 / 40%),
		oklch(20% 0 0 / 40%)
	);
}

codepen.io/argyleink/pen/wvQNpgm

IRRADIATION ILLUSION

Light on dark looks bolder

@media (prefers-color-scheme: dark) {
	.adjusted {
		font-variation-settings: "GRAD" -50;
	}
}

font-variation-settings

✓ Baseline Widely available

IRRADIATION ILLUSION

Light on dark looks bolder

@media (prefers-color-scheme: dark) {
	.adjusted {
		font-variation-settings: "GRAD" -50;
	}
}

A code editor window displays CSS code that adjusts font variation settings for dark color schemes.

Light on dark looks bolder.

@media (prefers-color-scheme: dark) {
  .adjusted {
    font-variation-settings: "GRAD" -50;
  }
}
Screenshot of a code editor displaying CSS code.

IRRADIATION ILLUSION

Light on dark looks bolder


@media (prefers-color-scheme: dark) {
  .adjusted {
    font-variation-settings: "GRAD" -50;
  }
}
  

font-variation-settings

✔ Baseline Widely available

A code editor-like window displays a CSS code block. Small icons representing various web browsers (Chrome, Edge, Firefox, Safari, Opera) are shown next to the text indicating feature availability.

IRRADIATION ILLUSION

Light on dark looks bolder

@media (prefers-color-scheme: dark) {
  .adjusted {
    font-variation-settings: "GRAD" -50;
  }
}

font-variation-settings

Baseline Widely available

A code block showing CSS for adjusting font variation settings in dark mode. Below the code, a section titled 'font-variation-settings' indicates 'Baseline Widely available' with a checkmark and icons representing Chrome, Edge, Firefox, and Safari, signifying broad browser support for the property.

IRRADIATION ILLUSION

Light on dark looks bolder

@media (prefers-color-scheme: dark) {
	.adjusted {
		font-variation-settings: "GRAD" -50;
	}
}

font-variation-settings

Baseline Widely available

A screenshot of a code editor displaying CSS code within a light-themed interface.

IRRADIATION ILLUSION

Light on dark looks bolder

@media (prefers-color-scheme: dark) {
  .adjusted {
    font-variation-settings: "GRAD" -50;
  }
}

font-variation-settings

Baseline Widely available

A dark-themed code block showing CSS for adjusting font-variation-settings based on preferred color scheme. Below the code, a compatibility bar with a checkmark and browser icons (Chrome, Edge, Firefox, Safari) indicates wide availability for 'font-variation-settings'.

ACT III

THE VIEWPORT

what's on screen, and whether the page is moving

IN VIEW

whats in or out of view

.reveal {
  animation: fade-in linear both;
  animation-timeline: view();
  animation-range: entry 0% cover 30%;
}

Scroll-driven animations

Limited availability

A code editor snippet displaying CSS rules for a reveal animation. Below the code, icons representing various web browsers indicate limited availability for scroll-driven animations.

IN VIEW

whats in or out of view

.reveal {
	animation: fade-in linear both;
	animation-timeline: view();
	animation-range: entry 0% cover 30%;
}

Scroll-driven animations

Limited availability

The slide features a CSS code example for scroll-driven animations, using properties like animation-timeline and animation-range. Below the code, icons for Chrome, Edge, Safari, and Firefox indicate browser compatibility.

IN VIEW

whats in or out of view

.reveal {
    animation: fade-in linear both;
    animation-timeline: view();
    animation-range: entry 0% cover 30%;
}

IN VIEW

whats in or out of view

.reveal {
  animation: fade-in linear both;
  animation-timeline: view();
  animation-range: entry 0% cover 30%;
}

Scroll-driven animations

Limited availability

The slide features a code block in a dark grey box, displaying CSS code. At the bottom, next to "Limited availability", there are five small circular icons, three of which are filled and two are outlined.

IN VIEW

whats in or out of view

.reveal {
  animation: fade-in linear both;
  animation-timeline: view();
  animation-range: entry 0% cover 30%;
}

Scroll-driven animations

Limited availability for Chrome, Edge, Firefox, and Safari browsers.

IN VIEW

whats in or out of view

.reveal {
	animation: fade-in linear both;
	animation-timeline: view();
	animation-range: entry 0% cover 30%;
}

Scroll-driven animations

Limited availability: Chrome, Edge, Firefox, Safari, Opera, Brave, Vivaldi browser icons

A dark-themed code block displaying CSS for a '.reveal' class. Below that, several browser compatibility icons (Chrome, Edge, Firefox, Safari, Opera, Brave, Vivaldi) are shown.

IN VIEW

whats in or out of view

.reveal {
  animation: fade-in linear both;
  animation-timeline: view();
  animation-range: entry 0% cover 30%;
}

Scroll-driven animations

Limited availability

A screenshot of a CSS code block demonstrating scroll-driven animations, followed by browser icons indicating limited availability.

AMI STUCK?

whether its scrolling or not

.toolbar { container-type: scroll-state; }

@container scroll-state(stuck: top) {
	.toolbar__shadow { opacity: 1; }
}

Container scroll-state queries

Limited availability

AMI STUCK?

whether its scrolling or not

.toolbar { container-type: scroll-state; }

@container scroll-state(stuck: top) {
  .toolbar__shadow { opacity: 1; }
}

Container scroll-state queries

Limited availability

A code block is displayed within a UI element resembling a window. Below the code, there are several small browser compatibility icons.

AMI STUCK?

whether its scrolling or not

.toolbar { container-type: scroll-state; }

@container scroll-state(stuck: top) {
  .toolbar__shadow { opacity: 1; }
}

Container scroll-state queries

Limited availability

A dark-themed code block displays CSS code demonstrating scroll-state container queries. Below the code, a row of browser icons (Chrome, Edge, Firefox, Safari, and other experimental browsers) indicates current compatibility for the feature.

FILLING SCREEN

Am I filling the viewport width or not?

@container (inline-size = 100vw) {
  * {
    border-radius: 0;
  }
}
A stylized code block displays CSS for a container query, setting border-radius to 0 for all child elements when the container's inline-size is 100vw.

FILLING SCREEN

Am I filling the viewport width or not?

@container (inline-size = 100vw) {
  * {
    border-radius: 0;
  }
}

FILLING SCREEN V2

Don't ask, bake it into the token

.conditional-radius {
  border-radius: clamp(
    0px,
    calc(100vw - 100%) * 1e5,
    var(--radius-3)
  );
}

Screenshot of a code editor showing CSS code for a conditional border-radius property.

FILLING SCREEN V2

Don't ask, bake it into the token

.conditional-radius {
  border-radius: clamp(
    0px,
    calc(100vw - 100%) * 1e5,
    var(--radius-3)
  );
}

FILLING SCREEN V2

Don't ask, bake it into the token

.conditional-radius {
  border-radius: clamp(
    0px,
    calc(100vw - 100%) * 1e5,
    var(--radius-3)
  );
}
Code editor snippet displaying CSS code for a conditional border-radius property using `clamp`, `calc`, and CSS variables.

CONCENTRIC CORNERS

Adaptive inner radius derived from the outer

.card {
    --radius: var(--radius-4);
    --pad: var(--size-3);
    border-radius: var(--radius);
    padding: var(--pad);

    & > .inner {
        border-radius: calc(var(--radius) - var(--pad));
    }
}
A code editor displays CSS rules for creating concentric corners, defining custom properties for radius and padding, and calculating an inner border-radius based on the outer radius and padding.

CONCENTRIC CORNERS

Adaptive inner radius derived from the outer

.card {
	--radius: var(--radius-4);
	--pad: var(--size-3);
	border-radius: var(--radius);
	padding: var(--pad);

	& > .inner {
		border-radius: calc(var(--radius) - var(--pad));
	}
}
A code block demonstrating CSS rules for creating concentric corners by calculating the inner border-radius from the outer border-radius and padding variables.

CONCENTRIC CORNERS

Adaptive inner radius derived from the outer

.card {
	--radius: var(--radius-4);
	--pad: var(--size-3);
	border-radius: var(--radius);
	padding: var(--pad);

	& > .inner {
		border-radius: calc(var(--radius) - var(--pad));
		/* ... code continues ... */
Screenshot of a code editor showing CSS code for `concentric corners` using CSS variables and `calc()` for `border-radius`.

CONCENTRIC CORNERS

Adaptive inner radius derived from the outer

.card {
	--radius: var(--radius-4);
	--pad: var(--size-3);
	border-radius: var(--radius);
	padding: var(--pad);

	& > .inner {
		border-radius: calc(var(--radius) - var(--pad));
	}
}

CONCENTRIC CORNERS

Adaptive inner radius derived from the outer

.card {
	--radius: var(--radius-4);
	--pad: var(--size-3);
	border-radius: var(--radius);
	padding: var(--pad);

	> .inner {
		border-radius: calc(var(--radius) - var(--pad));
	}
}

CONCENTRIC CORNERS

Adaptive inner radius derived from the outer

.card {
	--radius: var(--radius-4);
	--pad: var(--size-3);
	border-radius: var(--radius);
	padding: var(--pad);
}

> .inner {
	border-radius: calc(var(--radius) - var(--pad));
}
A screenshot of a code editor or presentation tool displaying CSS code for concentric corners, utilizing CSS custom properties and `calc()` for border-radius calculations.

FITTING A CHANGING VIEWPORT

dynamic, large & small viewport units to the rescue

.hero    { block-size: 100dvh; }
.cover   { block-size: 100lvh; }
.compact { block-size: 100svh; }

Small, large, and dynamic viewport units

✓ Baseline Widely available

Code example displayed within a stylized window, demonstrating CSS properties using dynamic, large, and small viewport height units.

FITTING A CHANGING VIEWPORT

dynamic, large & small viewport units to the rescue

.hero     { block-size: 100dvh; }
.cover    { block-size: 100lvh; }
.compact  { block-size: 100svh; }

Small, large, and dynamic viewport units

Baseline Widely available.

A code editor interface displaying CSS code examples for viewport units.

Icons representing Chrome, Firefox, Safari, and Edge browsers, indicating wide availability.

FITTING A CHANGING VIEWPORT

dynamic, large & small viewport units to the rescue

.hero { block-size: 100dvh; }
.cover { block-size: 100lvh; }
.compact { block-size: 100svh; }

Small, large, and dynamic viewport units

Baseline Widely available

A dark-themed code editor box displays CSS code examples for .hero, .cover, and .compact classes setting block-size to 100dvh, 100lvh, and 100svh respectively. Below this, a checkmark icon is followed by logos for Chrome, Firefox, Safari, Edge, Opera, and Samsung Internet, indicating wide browser support.

FITTING A CHANGING VIEWPORT

dynamic, large & small viewport units to the rescue

.hero { block-size: 100dvh; }
.cover { block-size: 100lvh; }
.compact { block-size: 100svh; }

Small, large, and dynamic viewport units

Baseline Widely available

A code editor window displays CSS code defining `block-size` using `dvh`, `lvh`, and `svh` units for `.hero`, `.cover`, and `.compact` classes. Below the code, a checkmark and icons for Chrome, Firefox, Edge, Safari, and Opera indicate browser compatibility.

FITTING A CHANGING VIEWPORT

dynamic, large & small viewport units to the rescue

.hero   { block-size: 100dvh; }
.cover  { block-size: 100lvh; }
.compact { block-size: 100svh; }

Small, large, and dynamic viewport units

Baseline Widely available

WHERE'S THE NOTCH?

the hardware viewport & its safe area insets

.app {
	padding-block-start: env(safe-area-inset-top);
	padding-block-end: env(safe-area-inset-bottom);
	padding-inline: env(safe-area-inset-left)
		env(safe-area-inset-right);
}

Safe area inset environment variables

Baseline Widely available

A code snippet showing CSS properties for handling safe area insets, displayed within a generic code editor window. Below the code, there's text indicating "Baseline Widely available" accompanied by icons representing popular web browsers (Chrome, Firefox, Safari, Edge), signifying broad browser support for these features.

WHERE'S THE NOTCH?

the hardware viewport & its safe area insets

.app {
  padding-block-start: env(safe-area-inset-top);
  padding-block-end: env(safe-area-inset-bottom);
  padding-inline: env(safe-area-inset-left)
  env(safe-area-inset-right);
}

Safe area inset environment variables

Baseline Widely available

A screenshot of a code editor displaying CSS code for an app class, using `env()` with `safe-area-inset` properties. Below the code, a compatibility bar shows browser logos with checkmarks, indicating wide availability of these features.

SCALE WITH ME

fluid typography that tracks the viewport

h1 {
	font-size: clamp(1rem, 2vw + 1rem, 3rem);
}

min(), max(), and clamp()

Media queries

Baseline Widely available

An abstract vertical graphic showing a design adapting or stretching, with pink/purple hues at the top and gray/black at the bottom, likely illustrating responsiveness or orientation changes. Below the text "Baseline Widely available", a series of circular icons represent various web browsers (Chrome, Firefox, Safari, Edge, Opera), indicating widespread compatibility.

TALL OR WIDE?

whether the viewport is portrait or landscape

@media (orientation: portrait) {
  .gallery { grid-auto-flow: row; }
}

@media (orientation: landscape) {
  .gallery { grid-auto-flow: column; }
}

Media queries

✅ Baseline Widely available

A code example demonstrates CSS media queries for `orientation: portrait` and `orientation: landscape` to control `grid-auto-flow`. Below the code, a graphic with several browser icons indicates wide browser support for media queries.

BEING TOUCHED

is the user hovering, focusing or activating elements

.btn:hover { background: var(--brand); }
.btn:focus-visible { outline: 2px solid; }
.btn:active { scale: .98; }

focus-visible

Baseline Widely available

BEING TOUCHED

is the user hovering, focusing or activating elements

.btn:hover { background: var(--brand); }
.btn:focus-visible { outline: 2px solid; }
.btn:active { scale: .98; }

Regarding :focus-visible, it is Baseline Widely available.

A row of browser logos for Chrome, Edge, Firefox, and Safari indicates wide compatibility for the displayed CSS features.

BEING TOUCHED

is the user hovering, focusing or activating elements

.btn:hover { background: var(--brand); }
.btn:focus-visible { outline: 2px solid; }
.btn:active { scale: .98; }

FOCUS WITHIN

whether there's focus within or not

.field:focus-within {
	--label-opacity: 1;
	border-color: var(--brand);
}

:focus-within

Baseline Widely available

A screenshot of a code editor displaying a CSS code block that uses the `:focus-within` pseudo-class. Below the code, a row of browser compatibility icons is shown.

VALID?

if there's valid in invalid form inputs

input:user-valid { border-color: green; }
input:user-invalid { border-color: red; }

.form:has(:invalid) button { opacity: .5; }

user-valid and user-invalid

✓ Baseline Widely available

:user-valid and :user-invalid

input:user-valid { border-color: green; }
input:user-invalid { border-color: red; }

.form:has(:invalid) button { opacity: .5; }

Baseline Widely available

A code editor style block displays CSS rules for styling valid and invalid user inputs and a form with invalid elements. Below the code, browser logos with checkmarks indicate that the described CSS features are widely available.

NOW PLAYING

if media is playing or not

video:playing { } /* it's playing */
video:paused { } /* it's paused */
.player:has(video:playing) .play-icon {
	display: none;
}

Media element pseudo-classes

A slide features two CSS code examples related to detecting media playback states. The first example shows the :playing and :paused pseudo-classes for the video element. The second example demonstrates using the :has(video:playing) selector to hide a play icon when a video is active. Below the code, an infinity symbol is displayed alongside several circular browser compatibility icons, indicating 'Limited availability'.

NOW PLAYING

if media is playing or not

video:playing { ... } /* it's playing */
video:paused  { ... } /* it's paused */

.player:has(video:playing) .play-icon {
	display: none;
}

NOW PLAYING

if media is playing or not

video:playing { ... } /* it's playing */
video:paused { ... } /* it's paused */

.player:has(video:playing) .play-icon {
	display: none;
}

Media element pseudo-classes

Limited availability

A code editor screenshot displays CSS rules for video pseudo-classes like `:playing` and `:paused`, and using `:has()` with `.play-icon` to hide it when a video is playing. Below the code, browser compatibility icons for Chrome, Edge, Firefox, and Safari are shown.

NOW PLAYING

if media is playing or not

video:playing { ... } /* it's playing */
video:paused { ... } /* it's paused */

.player:has(video:playing) .play-icon {
	display: none;
}

Media element pseudo-classes

Limited availability

A dark-themed code editor window displays CSS code. Below, icons representing various web browsers (Chrome, Edge, Firefox, Safari, and others) are shown with checkmarks, indicating compatibility for the described feature.

PICTURE IN PICTURE

whether a video has popped out into picture-in-picture

video:picture-in-picture {
	outline: 3px solid var(--brand);
}

:root:has(video:picture-in-picture) .mini-player {
	display: flex;
}

Picture-in-picture (video)

Limited availability

Browser compatibility icons are shown for Chrome, Edge, Firefox, Safari, and Opera.

AMI OPEN?

whether a popover, dialog or details is open

.menu:popover-open { animation: pop-in .2s; }

details[open] .chevron {
  rotate: 90deg;
}

Popover

✔ Baseline 2025 NEWLY AVAILABLE Chrome, Firefox, Safari, Edge, Opera, Samsung Internet, WebKit

GOING BIG

whether the element is presented fullscreen

.player:fullscreen {
  --controls-size: 1.5rem;
  background: black;
}

Fullscreen API

Limited availability

A code editor showing CSS rules for a fullscreen player. Browser logos for Chrome, Edge, Firefox, and Safari are displayed below, indicating limited availability for the Fullscreen API.

GOING BIG

whether the element is presented fullscreen

.player:fullscreen {
    --controls-size: 1.5rem;
    background: black;
}

Fullscreen API

Limited availability

GOING BIG

whether the element is presented fullscreen

.player:fullscreen {
	--controls-size: 1.5rem;
	background: black;
}

Fullscreen API

Limited availability

Icons indicating browser compatibility for the Fullscreen API.

STILL EMPTY?

whether an input is showing its placeholder

input:placeholder-shown {
  border-style: dashed;
}

input:not(:placeholder-shown) {
  border-color: var(--brand);
}

:placeholder-shown

Baseline Widely available

Browser compatibility icons indicating widespread support for the :placeholder-shown pseudo-class across multiple browsers.

STILL EMPTY?

whether an input is showing its placeholder

input:placeholder-shown {
  border-style: dashed;
}

input:not(:placeholder-shown) {
  border-color: var(--brand);
}

:placeholder-shown

Baseline Widely available

A screenshot of a code editor showing CSS rules for the `:placeholder-shown` pseudo-class. Below the code, a row of browser icons (Chrome, Edge, Firefox, Safari, Opera, Samsung Internet) with checkmarks indicates widespread support.

ON, OFF, OR MAYBE

whether a control is checked or indeterminate

input:checked + label {
	font-weight: 600;
}

input:indeterminate + label {
	color: var(--text-2);
}
A code block displays CSS rules for `input:checked + label` and `input:indeterminate + label` in a dark editor-like theme.

input:checked + label {
  font-weight: 600;
}

input:indeterminate + label {
  color: var(--text-2);
}
  
A screenshot of a code editor displaying CSS code.

THE BROWSER FILLED IT

whether a field was autofilled by the browser

input:autofill {
  box-shadow: 0 0 0 100px var(--surface-2) inset;
  -webkit-text-fill-color: var(--text-1);
}

:autofill

Limited availability

A dark-themed code editor window displays CSS code for the `input:autofill` pseudo-class. Below the code, icons for Chrome, Firefox, Safari, Edge, and Opera indicate browser compatibility for the feature.

Whether a field was autofilled by the browser.

input:autofill {
  box-shadow: 0 0 0 100px var(--surface-2) inset;
  -webkit-text-fill-color: var(--text-1);
}

:autofill

Limited availability

A code editor showing CSS rules to style the input:autofill pseudo-class, including `box-shadow` and `-webkit-text-fill-color`. Below the code, a row of browser icons (Chrome, Firefox, Safari, Edge, Opera) with checkmarks or crosses indicates the compatibility of the :autofill pseudo-class across different browsers.

CAN I EDIT?

whether a field is editable or locked

input:read-write { background: var(--surface-1); }

input:read-only {
  background: none;
  border-style: dashed;
}
A mouse cursor icon hovers above the title. A dark-themed code block displays CSS rules for styling read-write and read-only input fields.

CAN I EDIT?

whether a field is editable or locked

input:read-write { background: var(--surface-1); }

input:read-only {
  background: none;
  border-style: dashed;
}

DRIVEN BY SCROLL

reacting to how far the scroller has moved

.progress-bar {
    animation: grow linear;
    animation-timeline: scroll();
}

@keyframes grow { to { scale: 1 1; } }
https://codepen.io/argyleink/pen/yyayVZb

Scroll-driven animations

Limited availability

Screenshot of a code editor displaying CSS code for scroll-driven animations. Browser compatibility icons for Chrome, Edge, Firefox, and Safari are shown.
Screenshot of the Arc web browser interface showing a blank white content area.

Scroll Driven Orbital System

<main class="space-runway">

  <div class="scroll-prompt">Orbit
    Scroll</div>

  <div class="galaxy">

    <div class="orbit-ring ring1"></div>
    <div class="orbit-ring ring2"></div>
/* The magic formula:
  Translate by -50% to center the
  element on its origin,
  then add the Cosine (X) and Sine
  (Y) of the animated angle,
  multiplied by the desired radius.
*/
transform: translate(
  calc(50% + var(--radius)) *
  cos(var(--angle))),
  calc(50% + var(--radius)) *
  sin(var(--angle)));

/* Link the animation to the root
  scrollbar */
animation: orbit linear both;
animation-timeline: scroll(root);
Screenshot of a web development environment showing HTML and CSS code on the left, and a live preview of a scroll-driven orbital system on the right. The system features a central yellow sun, with three smaller planets (light blue, red, and green) orbiting it along dashed concentric paths. The text "Orbit Scroll" is also visible in the preview.

Scroll Driven Orbital System

<main class="space-runway">
  <div class="scroll-prompt">Orbit Scroll</div>
  <div class="galaxy">
    <div class="orbit-ring ring1"></div>
    <div class="orbit-ring ring2"></div>
  </div>
html {
  block-size: 100%;
  color-scheme: light;
  scrollbar-color: color-mix(in
  srgb, CanvasText, #000 50%) #000;
}

body {
  min-block-size: 100%;
  font-family: system-ui, sans-
  serif;
  display: grid;
  place-content: center;
}

.scroll-prompt {
  position: fixed;
  top: 30px;
}
Screenshot of a web development environment (CodePen or similar) showing HTML and CSS code on the left, and a live preview of the web page on the right. The live preview displays a central glowing yellow sphere, with a smaller blue oval and a red oval orbiting around it. The text "Orbit Scroll" is visible at the top right of the preview area.

Scroll Driven Orbital System

<main class="space-runway">
  <div class="scroll-prompt">Orbit
    Scroll</div>
  <div class="galaxy">
    <div class="orbit-ring ring1"></div>
    <div class="orbit-ring ring2"></div>
html {
  block-size: 100%;
  color-scheme: light;
  scrollbar-color: color-mix(
    srgb, CanvasText, #0000 50%) #0000;
}

body {
  min-block-size: 100%;
  font-family: system-ui, sans-
    serif;
  display: grid;
  place-content: center;
}

.scroll-prompt {
  position: fixed;
  top: 30px;
}

Preview Content:

Orbit Scroll

Screenshot of a web development environment, likely CodePen, showing HTML and CSS code panels on the left. On the right, a live preview displays a white background with the text 'Orbit Scroll' in the top right, a large central glowing yellow sphere, and three smaller orbiting spheres (blue, green, red).

DRIVEN BY SCROLL

reacting to how far the scroller has moved

.progress-bar {
    animation: grow linear;
    animation-timeline: scroll();
}

@keyframes grow { to { scale: 1 1; } }

https://codepen.io/argyleink/pen/yyyavZb

Scroll-driven animations

Limited availability

Screenshot of a browser window displaying the code for scroll-driven animations in CSS, with icons indicating browser compatibility for Chrome, Firefox, Safari, and Edge.

DID I SNAP?

snapping, and styling the element that snapped

.slides {
	scroll-snap-type: inline mandatory;
	container-type: scroll-state;
}

@container scroll-state(snapped: inline) {
	.slide { scale: 1.05; }
}

Container scroll-state queries

Limited availability

DID I SNAP?

snapping, and styling the element that snapped

.slides {
	scroll-snap-type: inline mandatory;
	container-type: scroll-state;
}

@container scroll-state(snapped: inline) {
	.slide { scale: 1.05; }
}

Container scroll-state queries

Limited availability

A code editor-like window displays CSS code. Below it, browser icons (Chrome, Edge, Firefox, Safari) show compatibility status for the feature.

DID I SNAP?

snapping, and styling the element that snapped

.slides {
	scroll-snap-type: inline mandatory;
	container-type: scroll-state;
}

@container scroll-state(snapped: inline) {
	.slide { scale: 1.05; }
}

DID I SNAP?

snapping, and styling the element that snapped

.slides {
	scroll-snap-type: inline mandatory;
	container-type: scroll-state;
}
@container scroll-state(snapped: inline) {
	.slide { scale: 1.05; }
}

Container scroll-state queries

Limited availability

A row of browser icons (including Chrome, Edge, Firefox, Safari, and others) indicates the compatibility status of 'Container scroll-state queries', with some showing full support and others partial or no support.

WHAT'S HIGHLIGHTED

styling the text the user has selected

::selection {
	background: var(--brand);
	color: white;
}

::selection

Limited availability

Browser compatibility icons are displayed below the code, showing support for Chrome, Firefox, Safari, and Edge.

styling the text the user has selected

::selection {
  background: var(--brand);
  color: white;
}

::selection Limited availability

A dark-themed code editor displaying CSS code. Below the code, a row of browser compatibility icons with green checkmarks indicates support for the feature.

LINKED RIGHT HERE

styling text arrived at via a scroll-to-text link

/* #::text=... fragment landed on this run of text */
::target-text {
	background: var(--brand);
	color: white;
}

target-text

Baseline 2024 NEWLY AVAILABLE

LINKED RIGHT HERE

styling text arrived at via a scroll-to-text-link

/* #::text=... fragment landed on this run of text */
::target-text {
	background: var(--brand);
	color: white;
}

target-text

LINKED RIGHT HERE

styling text arrived at via a scroll-to-text link

/* #::-text=... fragment landed on this run of text */
::target-text {
  background: var(--brand);
  color: white;
}

target-text

Baseline 2024 AVAILABLE

LINKED RIGHT HERE

styling text arrived at via a scroll-to-text link

/* #::-text=... Fragment landed on this run of text */
::target-text {
    background: var(--brand);
    color: white;
}

target-text

Baseline 2024: Feature support is available, indicated by icons for major browsers (Chrome, Edge, Firefox, Safari, Opera).

YOU JUMPED TO ME

whether the element is the URL's fragment target

/* :target — the #id the page was linked to */
.section:target {
    scroll-margin-block-start: 2rem;
    outline: 2px solid var(--brand);
}

:target

Baseline Widely available

A code snippet styled to resemble a dark-themed editor window, displaying CSS rules for the `:target` pseudo-class.

Below the code, browser compatibility icons for Chrome, Edge, Firefox, Safari, and Opera are visible.

view-transition-name: var(--);
}

View transitions

Baseline 2025 NEWLY AVAILABLE

A checkmark icon appears next to the 'Baseline 2025' text. Below it are icons for Chrome, Edge, Firefox, and Safari, each displaying a checkmark to indicate support for the feature.

CONTINUITY

morph between states

@view-transition { navigation: auto; }

.card {
  view-transition-name: var(--id);
}

View transitions

✓ Baseline 2025 NEWLY AVAILABLE

ACT V

THE SELF

its siblings, its frame, its definition, its very nature

WHICH ONE AM I?

position within siblings

li {
	--i: sibling-index();
	--n: sibling-count();
	transition-delay: calc(var(--i) * 50ms);
}

sibling-count() and sibling-index()

Limited availability

position within siblings

li {
  --i: sibling-index();
  --n: sibling-count();
  transition-delay: calc(var(--i) * 50ms);
}

sibling-count() and sibling-index()

Limited availability

Screenshot of a code editor displaying CSS. Below the code, browser compatibility icons show checkmarks for Chrome, Edge, and Safari, and an X for Opera, indicating limited availability for the described functions.

AMI UPGRADED?

is defined or not

my-widget:not(:defined) { visibility: hidden; }
my-widget:defined { animation: pop-in .3s; }

Autonomous custom elements

Baseline Widely available

AMI UPGRADED?

is defined or not

my-widget:not(:defined) { visibility: hidden; }
my-widget:defined { animation: pop-in .3s; }

Autonomous custom elements

Baseline Widely available

A row of browser icons (Chrome, Firefox, Safari, Edge, Opera) indicating wide support.

I AM THE HOST

is in a web component

:host { display: block; }
:host([open]) { ... }
:host-context(.dark) { color-scheme: dark; }

Shadow DOM

Baseline Widely available

Browser compatibility status showing a green checkmark indicating "Baseline Widely available" for several browsers, represented by their logos: Chrome, Edge, Firefox, and Safari.

WHO'S RENDERING ME?

which browser it's in

/* ask about features, not names */
@supports (animation-timeline: scroll()) {
  /* ... */
}

@supports not (selector(:has(*))) {
  /* graceful fallback */
}

@supports

Baseline Widely available

A dark-themed code editor window displays CSS code examples using the `@supports` at-rule. Below this, a green checkmark icon is displayed next to the text "Baseline Widely available", followed by a series of browser logos including Chrome, Firefox, Safari, Edge, and Opera, indicating broad compatibility.

WHO'S RENDERING ME?

which browser it's in

/* ask about features, not names */
@supports (animation-timeline: scroll()) { ... }

@supports not (selector(:has(*))) {
  /* graceful fallback */
}

@supports

Baseline Widely available

A code editor window displaying CSS code with `@supports` rules. Below the code, a row of browser icons (Chrome, Firefox, Edge, Safari, Opera, Samsung Internet, WebKit) each with a green checkmark, indicating wide browser support for the `@supports` feature.

WHO'S RENDERING ME?

which browser it's in

/* ask about features, not names */
@supports (animation-timeline: scroll()) { - }

@supports not (selector(:has(*))) {
    /* graceful fallback */
}

@supports

Baseline Widely available

ON PAPER

is being printed and/or fragmented across print pages

@media print {
  nav, .ads { display: none; }
}

.chapter { break-before: page; }
tr { break-inside: avoid; }
A code example showing CSS rules for print media, specifically hiding navigation and ads, forcing page breaks before chapters, and preventing breaks inside table rows.

WHICH OF MY KIND?

my index among matching siblings, not all siblings

/* every 3rd .featured card – ignores non-matching siblings */
.card:nth-child(3n of .featured) {
    grid-column: span 2;
}

:nth-child() of <selector>

✓ Baseline Widely available

A code snippet demonstrating the CSS :nth-child() pseudo-class with a selector. Below the code, a "Baseline Widely available" label is accompanied by checkmarks and logos of various browsers (Chrome, Edge, Firefox, Safari desktop, and Safari iOS) indicating broad support for the feature.

WHICH OF MY KIND?

my index among matching siblings, not all siblings

/* every 3rd .featured card - ignores non-matching siblings */
.card:nth-child(3n of .featured) {
  grid-column: span 2;
}

Feature: :nth-child() of <selector>

Baseline Widely available

Browser compatibility icons for Chrome, Firefox, Edge, Safari, Opera, Samsung Internet, and various Android browsers.

WHO COMES AFTER ME?

adapting to the siblings that follow, via :has()

.label:has(+ input:required)::after {
  content: " *";
}
.row:has(~ .row.error) { border-color: var(--warn); }

:has()

Baseline 2023 NEWLY AVAILABLE

A screenshot of a code editor displaying two CSS code examples using the `:has()` pseudo-class. Below the code, browser logos indicate compatibility.

INSTALLED AS AN APP?

whether I'm running as a PWA, and how

@media (display-mode: standalone) { ... }
@media (display-mode: minimal-ui) { ... }
@media (display-mode: window-controls-overlay) { ... }

display-mode media query

Limited availability

A set of browser icons indicating compatibility for the display-mode media query, showing varying levels of support across Chrome, Edge, Firefox, Safari, and Opera.

INTO THE TITLEBAR

laying out within the window-controls overlay

.titlebar {
	position: fixed;
	inset-block-start: env(titlebar-area-y);
	inset-inline-start: env(titlebar-area-x);
	inline-size: env(titlebar-area-width);
	block-size: env(titlebar-area-height);
}

Window Controls Overlay

Limited availability

Browser compatibility icons indicating support for various browsers like Chrome, Edge, Firefox, and Safari, with checks for some and crosses for others.

INTO THE TITLEBAR

laying out within the window-controls overlay

.titlebar {
  position: fixed;
  inset-block-start: env(titlebar-area-y);
  inset-inline-start: env(titlebar-area-x);
  inline-size: env(titlebar-area-width);
  block-size: env(titlebar-area-height);
}

Window Controls Overlay

Limited availability

SKIP ME IF OFFSCREEN

letting the browser skip rendering work until needed

.section {
    content-visibility: auto;
    contain-intrinsic-size: auto 600px;
}

content-visibility: Baseline 2025, NEWLY AVAILABLE

A code editor showing a CSS snippet for `content-visibility` and `contain-intrinsic-size`. Below the code, icons representing Chrome, Edge, Firefox, and Safari indicate browser compatibility.

SKIP ME IF OFFSCREEN

letting the browser skip rendering work until needed

.section {
	content-visibility: auto;
	contain-intrinsic-size: auto 600px;
}

content-visibility

✓ Baseline 2025 APPLY AVAILABLE

MIND MY OWN BOX

promising the browser my effects stay inside

.widget { contain: content; }
.card { contain: layout style; }
.offscreen { contain: strict; }

containment

Unknown availability

A code snippet demonstrating CSS `contain` properties. Below it, browser compatibility icons for Chrome, Firefox, Safari, Edge, and Opera are displayed next to the text "Unknown availability".

MIND MY OWN BOX

promising the browser my effects stay inside

.widget   { contain: content; }
.card     { contain: layout style; }
.offscreen{ contain: strict; }

containment

MIND MY OWN BOX

promising the browser my effects stay inside

.widget { contain: content; }
.card { contain: layout style; }
.offscreen { contain: strict; }

containment

Unknown availability:

MIND MY OWN BOX

promising the browser my effects stay inside

.widget { contain: content; }
.card { contain: layout style; }
.offscreen { contain: strict; }

containment

A dark rectangular code editor window displays CSS code.

ANCHORED

where other elements are and their size

.tooltip {
  position: absolute;
  position-anchor: --trigger;
  inset-block-start: anchor(bottom);
}
nerdy.dev/anchor-interpolated-morphing

Anchor positioning
∞ Limited availability

ANCHORED

where other elements are and their size

.tooltip {
	position: absolute;
	position-anchor: --trigger;
	inset-block-start: anchor(bottom);
}

nerdy.dev/anchor-interpolated-morphing

Anchor positioning

Limited availability

Browser compatibility icons are shown, with some fully colored (Chrome, Edge, Firefox, Safari Technology Preview) and others partially colored or greyed out, indicating limited availability for Anchor positioning.

ANCHORED

where other elements are and their size

.tooltip {
    position: absolute;
    position-anchor: --trigger;
    inset-block-start: anchor(bottom);
}

nerdy.dev/anchor-interpolated-morphing

Anchor positioning

Limited availability

A code editor displaying CSS code. Icons indicating browser compatibility.

Anchor Interpolated Morph

What if Anchor knew how to do FLIP?

::marker {
  animation: marker-flip var(--transition-in-ms) ease-in-out;
}

@keyframes marker-flip {
  from {
    transform: translate(var(--dx), var(--dy)) rotate(var(--dr));
    font-size: var(--ds));
  }
}

.item {
  position: absolute;
  inset: 0;
  width: var(--size);
  height: var(--size);
  background: hsl(var(--hue) 50% 50%);
  border-radius: 50%;
  offset-anchor: top right; /* <--- */
}

The magical property is offset-anchor

Which is also interpolatable

Anchor Interpolated Morph (AIM)

7 min read

CSS

January 23, 2026

7,898 views • 14 active

Illustration of the Pokémon Ditto holding a rope tied to a large anchor on a sandy beach, with palm trees and the ocean in the background.

Author avatar featuring a white skull wearing a purple baseball cap.

Anchor Interpolated Morph (AIM)

7 min read
css
January 23, 2026
2,898 views - 34 active

Illustration featuring a beach scene with a large anchor, a rope, and a purple, blob-like creature (resembling Ditto from Pokémon) on the sand. Palm trees are in the background. A small skull icon is visible in the bottom left of the image.

Anchor Interpolated Morph (AIM)

7 min read

CSS

January 23, 2026

7,898 views • 14 active

An illustration of a purple Ditto Pokémon on a sandy beach, pulling a thick rope connected to a large anchor partially submerged in shallow water. Palm trees are visible in the background. A circular icon shows a white skull wearing a purple baseball cap on a dark background.

It's more natural when animations start near the trigger

point, this CSS technique makes it easy. AIM animates from any element, and to any element.

Thanks to anchor(), anchor-size(), @starting-style, and interpolate-size.

Anchor positioning

131

Limited availability

interpolate-size

26

Limited availability

@starting-style

Baseline 2024 NEWLY AVAILABLE

Table of contents

  • FLIP
  • AIM
  • How it works
  • View Transitions
  • Spatial Continuity
  • Comments 9

All together they can create an anchor interpolated morph: an interruptible contextual transition from where a user invoked it.

The slide shows a documentation or article page about CSS animation. Key features like 'Anchor positioning', 'interpolate-size', and '@starting-style' are listed with their browser compatibility indicated by icons for Chrome, Edge, Firefox, Safari, Opera, and Samsung Internet. Some features are marked 'Limited availability' or 'NEWLY AVAILABLE' and 'Baseline 2024'. A 'Table of contents' sidebar on the right lists sections such as FLIP, AIM, and View Transitions.

All together they can create an anchor interpolated morph: an interruptible contextual transition from where a user invoked it.

That's a popover=hint + interestfor , no JS CSS transition. Try it

This is a very powerful and capable feature combination.

Similar results are visually possible with child elements using their parent as

Table of contents

  • FLIP
  • AIM
  • How it works
  • View Transitions
  • Spatial Continuity
  • Comments 9

An embedded video player demonstrating a web feature where a contextual popover appears over text with a smooth transition.

Showed some "interest" did we?

Pretty cool web feature, interest and command invokers.

I could see myself using these for a lot more interaction, especially combined with AIM.

Showed some "interest!" did we?

Pretty cool web feature, interest and command invokers.

A white rounded rectangle, resembling a speech bubble or popover, contains the text.
A subtle, rounded rectangular user interface element or card is visible at the bottom center of the slide.

Hover Me

A rounded rectangular button with the text 'Hover Me' and a downward-pointing icon.
7
A dark grey rounded rectangle displays the keyboard shortcut Shift Command 7.

"Hover Me" UI Element

Screenshot of a minimalist user interface showing a rounded rectangular button labeled "Hover Me" with an anchor icon.

Showed some "interest" did we?

Pretty cool web feature, interest and command invokers.

I could see myself using these for a lot more interaction, especially combined with AIM.

Hover Me

A white, rounded rectangular button featuring an anchor icon and the text 'Hover Me'.

Showed some "interest" did we?

Pretty cool web feature, interest and command invokers

I could see myself using these for a lot more interaction, especially combined with AIM.

A white rounded rectangle contains the text content. Below the text, a mouse cursor icon is visible.

Showed some "interest" did we?

Pretty cool web feature, interest and command invokers.

I could see myself using these for a lot more interaction, especially combined with AIM.

All together they can create an `anchor interpolated.morph`: an interruptible contextual transition from where a user invoked it.

Showed some "interest" did we?

Pretty cool web feature, interest and command invokers

I could see myself using these for a lot more interaction, especially combined with AIM

That's a popover-hint + interest for, no JS CSS transition. Try it

This is a very powerful and capable feature combination.

Similar results are visually possible with child elements using their parent as

Table of contents

  • FLIP
  • AIM
  • How it works
  • View Transitions
  • Spatial Continuity
  • Comments ⟡
A presentation slide showing a web browser interface with a main content area and a sidebar. The main content area demonstrates a pop-up hint over some text. The sidebar displays a 'Table of contents'.

ANCHORED

where other elements are and their size

.tooltip {
    position: absolute;
    position-anchor: --trigger;
    inset-block-start: anchor(bottom);
}

nerdy.dev/anchor-interpolated-morphing

Anchor positioning

A presentation slide displaying a CSS code example for anchor positioning, titled 'ANCHORED'.

MATCHING SIZES

distance from other elements

.menu {
  position-anchor: --btn;
  min-width: anchor-size(width); /* match the button */
}

Anchor positioning

Limited availability

MATCHING SIZES

distance from other elements

.menu {
  position-anchor: --btn;
  min-width: anchor-size(width); /* match the button */
}

Anchor positioning

Limited availability

Browser compatibility icons indicating limited availability for a feature.

LIGHT OR DARK

color scheme

:root { color-scheme: light dark; }

.card {
  background: light-dark(white, black);
  color:      light-dark(black, white);
}

light-dark()

Baseline 2024 NEWLY AVAILABLE

A dark-themed code editor window displays CSS code demonstrating the `color-scheme` property for the root element and the `light-dark()` function for background and text colors within a card class. Below the code, a set of browser icons (Chrome, Edge, Firefox, Safari) indicates compatibility.

COUNTING ITSELF

counting:itself – flat, then by nesting depth

ol { counter-reset: section; }
li { counter-increment: section; }

/* counter() – just my own number */
li::marker { content: counter(section) ". "; }

/* counters() – one level per ancestor: 1, 1.1, 1.1.1 */
li li::marker { content: counters(section, ".") " "; }

Counters (CSS)

Baseline Widely available

A stylized code block showing CSS rules for `counter-reset`, `counter-increment`, `counter()`, and `counters()`. Below the code, there is a green checkmark next to "Baseline Widely available", followed by icons representing Chrome, Firefox, Safari, Edge, and Opera browsers.

COUNTING ITSELF

counting itself — flat, then by nesting depth

ol { counter-reset: section; }
li { counter-increment: section; }

/* counter() – just my own number */
li::marker { content: counter(section) " "; }

/* counters() – one level per ancestor: 1, 1.1, 1.1.1 */
li li::marker { content: counters(section, ".") " "; }

Counters (CSS)

Baseline Widely available

A code snippet demonstrating CSS counters with `counter-reset`, `counter-increment`, `counter()`, and `counters()` properties, displayed within a dark-themed code editor interface.

ADOPT A GRID

aligning my children to my ancestor's tracks

.cards {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
}
.card {
	display: grid;
	grid-template-co...
	grid-colum span...
}

Subgrid

✓ Baseline Widely available ⦿ ⦿ ⦿ ⦿ ⦿

A DISTANT DRIVER

reacting to a timeline declared elsewhere in the DOM

.page { timeline-scope: --gallery; }
.scroller { view-timeline: --gallery inline; }

.progress {
	animation-timeline: --gallery;
}

https://codepen.io/argyleink/pen/gQyoBLJ

Scroll-driven animations

Limited availability

A screenshot of a code editor or browser window displaying CSS code for scroll-driven animations, a Codepen URL, and several small circular icons indicating feature availability.

A DISTANT DRIVER

reacting to a timeline declared elsewhere in the DOM

.page { timeline-scope: --gallery; }
.scroller { view-timeline: --gallery inline; }

.progress {
  animation-timeline: --gallery;
}
https://codepen.io/argyleink/pen/gOyoBLj

Scroll-driven animations

Limited availability

A code editor interface displaying CSS code. Browser logos indicate support for the described feature.

Scrollytelling - CSS scroll driven animations

<header>
  <div class="site-main">
    <div class="mock-logo"></div>
    <nav>
      <a href="https://developer.chrome.com/docs/css-ui/scroll-driven-animations">Overview</a>
      <a href="https://developer.chrome.com/doc..."></a>
    </nav>
  </div>
</header>
@layer support, demo;

@import "https://unpkg.com/open-props" layer(support.design-system);

@keyframes fade {
  0% {
    opacity: 0;
    transform: perspective(1e3px)
    rotateY(90deg) rotateX(-5deg)
    ...
  }
}

Enhance scrolling with Scrollytelling.

@keyframes fade-in {
  to { opacity: 1 }
}

@keyframes shadow-in {
  to { box-shadow: 0 10px 10px #0003 }
}

nav {
  animation:
    fade-in linear both,
    shadow-in linear both
  ;
  animation-timeline: scroll();
}
Screenshot of a web development environment (likely CodePen) showing HTML and CSS code editors on the left and a live preview on the right. The preview displays the text "Enhance scrolling with Scrollytelling", where a mouse cursor hovers over "Scrollytelling", and a dark card containing CSS code for scroll-driven animations.

ROLL ME BACK

deferring to the styles a previous layer defined

@layer base, theme;

.btn {
  color: revert-layer;
  margin: revert;
  border: unset;
}

Cascade layers

Baseline Widely available

A code editor screenshot showing CSS code defining a button style with 'revert-layer', 'revert', and 'unset' properties within a layer.

ROLL ME BACK

deferring to the styles a previous layer defined

@layer base, theme;

.btn {
	color: revert-layer;
	margin: revert;
	border: unset;
}

Cascade layers

Baseline Widely available

A screenshot of a code editor displays a CSS code snippet within a dark interface. The code defines an `@layer` for `base` and `theme`, and then a style for `.btn` that uses `revert-layer`, `revert`, and `unset` for color, margin, and border properties respectively. Below this, a checkmark icon is displayed next to the text "Baseline Widely available", followed by icons representing various web browsers (Chrome, Firefox, Safari, Edge, Opera, Samsung Internet) indicating broad support for the feature.

{ }

Cascade layers

Baseline Widely available

A green checkmark icon appears before the text 'Baseline Widely available'. Below this, logos for Chrome, Edge, Firefox, and Safari are displayed, each accompanied by a green checkmark, indicating wide browser support for the feature.

Trigonometric functions (CSS)

Baseline Widely available

A green checkmark icon appears next to the text "Baseline Widely available". To the right, logos for Chrome, Edge, Firefox, and Safari are shown, each with a green checkmark indicating support.

FIND MY ANGLE

resolving coordinates & rotations natively

.petal {
    --a: calc(var(--i) * 1turn / var(--n));
    translate: calc(cos(var(--a)) * 8rem)
               calc(sin(var(--a)) * 8rem);
    rotate: atan2(var(--dy), var(--dx));
}

Trigonometric functions (CSS)

Baseline Widely available

A dark-themed code block displays CSS code using trigonometric functions for rotation and translation. Below the code, a compatibility bar shows a checkmark next to "Baseline Widely available" and various browser logos, indicating broad support for these features.

CLING TO MY SCROLLER

holding position relative to my scroll container

.section-heading {
  position: sticky;
  inset-block-start: 0;
  background: var(--surface-1);
}

Sticky positioning

Baseline Widely available

A dark, rounded rectangle displays CSS code. Below it, a green checkmark and text "Baseline Widely available" are shown next to small icons representing various web browsers, indicating compatibility.

holding position relative to my scroll container

.section-heading {
	position: sticky;
	inset-block-start: 0;
	background: var(--surface-1);
}

Sticky positioning

Baseline Widely available

Screenshot of a code editor window displaying CSS code. Below the code, icons for Chrome, Edge, Firefox, and Safari browsers are displayed.

And, here's a few things CSS doesn't know...

  • element-local pointer position
  • aspect-ratio / ratio
  • generic field value, field state
  • form value, color-input value
  • Device sensors: device motion, geolocation
  • Hardware & system: battery, CPU pressure, FPS, plus head-time constants (CPU cores, device memory, DPR)
  • Network & connectivity: online/offline, connection info (effective type/downlink/RTT), navigation type
  • Page lifecycle: page focused, page visible
  • Time: clock
  • Color extraction: image colors, video colors

and, here's a few things CSS doesn't know...

  • Viewport: window/visual-viewport dimensions as px, scrollbar width
  • Scrolling: scroll velocity
  • Pointer: global pointer position, element-local pointer position
  • Element geometry: size as px
  • Element visibility: intersection visibility / ratio
  • Form controls: range/slider value, generic field value, field state (focus/valid/dirty), form state, select value, color-input value
  • Device sensors: device motion, geolocation
  • Hardware & system: battery, CPU pressure, FPS, plus head-time constants (CPU cores, device memory, DPR)
  • Network & connectivity: online/offline, connection info (effective type/downlink/RTT), navigation type
  • Page lifecycle: page focused, page visible
  • Time: clock
  • Color extraction: image colors, video colors

and, here's a few things CSS doesn't know...

  • Viewport: window/visual-viewport
    • dimensions as px, scrollbar width
  • Scrolling: scroll velocity
  • Pointer: global pointer position, element-local pointer position
  • Element geometry: size as px
  • Element visibility: intersection visibility / ratio
  • Form controls: range/slider value, generic field value, field state (focus/valid/dirty), form state, select value, color-input value
  • Device sensors: device motion, geolocation
  • Hardware & system: battery, CPU pressure, FPS, plus
    • head-time constants (CPU cores, device memory, DPR)
  • Network & connectivity: online/offline, connection
    • info (effective type/downlink(RTT), navigation type
  • Page lifecycle: page focused, page visible
  • Time: clock
  • Color extraction: image colors, video colors

and, here's a few things CSS doesn't know...

  • Viewport: window/visual-viewport dimensions as px, scrollbar width
  • Scrolling: scroll velocity
  • Pointer: global pointer position, element-local pointer position
  • Element geometry: size as px
  • Element visibility: intersection visibility / ratio
  • Form controls: range/slider value, generic field value, field state (focus/valid/dirty), form state, select value, color-input value
  • Device sensors: device motion, geolocation
  • Hardware & system: battery, CPU pressure, FPS, plus head-time constants (CPU cores, device memory, DPR)
  • Network & connectivity: online/offline, connection info (effective type/downlink/RTT), navigation type
  • Page lifecycle: page focused, page visible
  • Time: clock
  • Color extraction: image colors, video colors

and, here's a few things `CSS` doesn't know...

  • Viewport: `window`/`visual-viewport`
  • dimensions as `px`, `scrollbar` width
  • Scrolling: `scroll velocity`
  • Pointer: `global pointer position`, `element-local pointer position`
  • Element geometry: `size as px`
  • Element visibility: `intersection visibility` / `ratio`
  • Form controls: `range`/`slider value`, `generic field value`, `field state` (`(focus`/`valid`/`dirty)`)`, `form state`, `select value`, `color-input value`
  • Device sensors: `device motion`, `geolocation`
  • Hardware & system: `battery`, `CPU pressure`, `FPS`, plus
  • `head-time constants` (`CPU cores`, `device memory`, `DPR`)
  • Network & connectivity: `online`/`offline`, `connection`
  • `info` (`(effective type/downlink/RTT)`)`, `navigation type`
  • Page lifecycle: `page focused`, `page visible`
  • Time: `clock`
  • Color extraction: `image colors`, `video colors`

and, here's a few things CSS doesn't know...

  • Viewport: window/visual-viewport dimensions as px, scrollbar width
  • Scrolling: scroll velocity
  • Pointer: global pointer position, element-local pointer position
  • Element geometry: size as px
  • Element visibility: intersection visibility / ratio
  • Form controls: range/slider value, generic field value, field state (focus/valid/dirty), form state, select value, color-input value
  • Device sensors: device motion, geolocation
  • Hardware & system: battery, CPU pressure, FPS, plus head-time constants (CPU cores, device memory, DPR)
  • Network & connectivity: online/offline, connection info (effective type/downlink/RTT), navigation type
  • Page lifecycle: page focused, page visible
  • Time: clock
  • Color extraction: image colors, video colors

and, here's a few things CSS doesn't know...

  • Viewport: window/visual-viewport dimensions as px, scrollbar width
  • Scrolling: scroll velocity
  • Pointer: global pointer position, element-local pointer position
  • Element geometry: size as px
  • Element visibility: intersection visibility / ratio
  • Form controls: range/slider value, generic field value, field state (focus/valid/dirty), form state, select value, color-input value
  • Device sensors: device motion, geolocation
  • Hardware & system: battery, CPU pressure, FPS, plus head-time constants (CPU cores, device memory, DPR)
  • Network & connectivity: online/offline, connection info (effective type/downlink(RTT)), navigation type
  • Page lifecycle: page focused, page visible
  • Time: clock
  • Color extraction: image colors, video colors

and, here's a few things CSS doesn't know...

  • Viewport: window/visual-viewport dimensions as px, scrollbar width
  • Scrolling: scroll velocity
  • Pointer: global pointer position, element-local pointer position
  • Element geometry: size as px
  • Element visibility: intersection visibility / ratio
  • Form controls: range/slider value, generic field value, field state (focus/valid/dirty), form state, select value, color-input value
  • Device sensors: device motion, geolocation
  • Hardware & system: battery, CPU pressure, FPS, plus head-time constants (CPU cores, device memory, DPR)
  • Network & connectivity: online/offline, connection info (effective type/downlink(RTT)), navigation type
  • Page lifecycle: page focused, page visible
  • Time: clock
  • Color extraction: image colors, video colors

and, here's a few things CSS doesn't know...

  • Viewport: window/visual-viewport dimensions as px, scrollbar width
  • Scrolling: scroll velocity
  • Pointer: global pointer position, element-local pointer position
  • Element geometry: size as px
  • Element visibility: intersection visibility / ratio
  • Form controls: range/slider value, generic field value, field state (focus/valid/dirty), form state, select value, color-input value
  • Device sensors: device motion, geolocation
  • Hardware & system: battery, CPU pressure, FPS, plus head-time constants (CPU cores, device memory, DPR)
  • Network & connectivity: online/offline, connection info (effective type/downlink/RTT), navigation type
  • Page lifecycle: page focused, page visible
  • Time: clock
  • Color extraction: image colors, video colors

and, here's a few things CSS doesn't know...

  • Viewport: window/visual-viewport dimensions as px, scrollbar width
  • Scrolling: scroll velocity
  • Pointer: global pointer position, element-local pointer position
  • Element geometry: size as px
  • Element visibility: intersection visibility / ratio
  • Form controls: range/slider value, generic field value, field state (focus/valid/dirty), form state, select value, color-input value
  • Device sensors: device motion, geolocation
  • Hardware & system: battery, CPU pressure, FPS, plus head-time constants (CPU cores, device memory, DPR)
  • Network & connectivity: online/offline, connection info (effective type/downlink(RTT)), navigation type
  • Page lifecycle: page focused, page visible
  • Time: clock
  • Color extraction: image colors, video colors

and now CSS knows because there's a...

PROP FOR THAT

a tiny JS library that sets a custom property for many missing signals for CSS

prop-for-that.netlify.app

npm i prop-for-that

What JS knows, now CSS knows.

Runtime state for CSS. Now there's a Prop For That.

View on GitHub Read the docs

LIVE

pointer.x: 59%

pointer.y: 26%

CLOCK

18:00:02

FPS

60 fps

VIEWPORT

1580 x 850

SCROLL VELOCITY

0 px

ACCENT HUE

h 289

Screenshot of a web page demonstrating the 'Prop For That' library. The main title states 'What JS knows, now CSS knows.' The page features an installation command `npm i prop-for-that`, 'View on GitHub' and 'Read the docs' buttons. Various live performance and state metrics are displayed in separate panels: mouse pointer coordinates (x and y), a clock, frames per second (with a progress bar), viewport dimensions, scroll velocity (with a progress bar), and an 'Accent Hue' slider with a value display. The 'LIVE' data panel also includes a pause/play button. A subtle grid pattern is visible in the background.

What JS knows, now CSS knows.

npm i prop-for-that

Runtime state for CSS. Now there's a Prop For That.

View on GitHub Read the docs

Live pointer position: X 63%, Y 45%

Clock: 18:00:03

FPS: 60 fps

Viewport: 1580 x 850

Scroll Velocity: 0 px

Accent Hue slider with value h 289.

A web page demonstrating real-time CSS variables, featuring a mouse pointer with a purple trail, and interactive UI elements displaying live data for clock, frames per second (FPS), viewport dimensions, scroll velocity, and an accent hue color picker.

npm i prop-for-that

What JS knows, now CSS knows.

Runtime state for CSS. Now there's a Prop For That.

View on GitHub Read the docs

Live Data:

  • Clock: 18:00:04
  • FPS: 60 fps
  • Viewport: 1580 x 850
  • Scroll Velocity: 0 px
  • Accent Hue: Scrub me (with color slider)
  • Live Pointer Position: X 47%, Y 36% (currently paused)
Screenshot of a web page interface demonstrating the "Prop For That" library. It shows several UI cards displaying live runtime data: a clock, FPS counter, viewport dimensions, scroll velocity, and an interactive accent hue color slider. In the top right, a panel shows the live pointer X and Y coordinates.

What JS knows, now CSS knows.

npm i prop-for-that

Runtime state for CSS. Now there's a Prop For That.

View on GitHub Read the docs

LIVE
pointer.x 59%
pointer.y 38%

CLOCK 18:00:05

FPS 60 fps

VIEWPORT 1580 x 850

SCROLL VELOCITY 0 px

ACCENT HUE • SCRUB ME h 289

Screenshot of a web page demonstrating the 'Prop For That' library. The page displays real-time metrics including mouse pointer coordinates (x and y), a clock, frames per second (FPS) with a progress bar, viewport dimensions, scroll velocity with a progress bar, and an accent hue slider. Buttons to "View on GitHub" and "Read the docs" are also present.

What JS knows, now CSS knows.

Runtime state for CSS.
Now there's a Prop For That.

npm i prop-for-that

CLOCK

18:00:07

FPS

60 fps

VIEWPORT

1580 x 850

SCROLL VELOCITY

0 px

ACCENT HUE • SCRUB ME

h 289

PAUSED

pointer.x 91%

pointer.y 5%

Screenshot of a web application interface for 'Prop For That', displaying real-time metrics for clock, FPS, viewport dimensions, and scroll velocity. It also features a color hue slider and a 'PAUSED' indicator showing static pointer coordinates.

What JS knows, now CSS knows.

Runtime state for CSS.
Now there's a Prop For That.

npm i prop-for-that

View on GitHub Read the docs

CLOCK

18:00:12

FPS

60 fps

VIEWPORT

1580 x 850

SCROLL VELOCITY

0 px

ACCENT HUE - SCRUB ME

h 289

LIVE

pointer.x 62%

pointer.y 25%

A web application interface demonstrating real-time browser metrics and controls, featuring a pointer position tracker, a color accent hue slider, and displays for clock time, frames per second, viewport dimensions, and scroll velocity.
npm i prop-for-that

What JS knows, now CSS knows.

Runtime state for CSS. Now there's a Prop For That.

View on GitHub

Read the docs

LIVE

pointer.x 17%

pointer.y 71%

CLOCK 18:00:23

FPS 60 fps

VIEWPORT 1580 x 850

SCROLL VELOCITY 0 px

ACCENT HUE • SCRUB ME h 289

A screenshot of a web page demonstrating "Prop For That", a tool that makes JavaScript runtime state available to CSS. The page displays various live metrics within cards, including: pointer X and Y coordinates (17% and 71% respectively), a clock showing 18:00:23, 60 frames per second (FPS), viewport dimensions of 1580 x 850, and scroll velocity of 0 pixels. An accent hue slider is also shown, currently at h 289. Buttons labelled "View on GitHub" and "Read the docs" are visible below the main heading.

npm i prop-for-that

What JS knows, now CSS knows.

Runtime state for CSS. Now there's a Prop For That.

View on GitHub Read the docs

LIVE

pointer.x: 33%

pointer.y: 71%

CLOCK: 18:00:24

FPS: 60 fps

VIEWPORT: 1580 x 850

SCROLL VELOCITY: 0 px

ACCENT HUE: SCRUB ME h 289

Screenshot of a web application demonstrating "Prop For That," a tool for connecting JavaScript runtime state with CSS. The interface displays real-time metrics including pointer coordinates, frames per second, viewport dimensions, scroll velocity, current time, and an accent hue slider.

What JS knows, now CSS knows.

Runtime state for CSS. Now there's a Prop For That.

npm i prop-for-that

View on GitHub Read the docs

LIVE
pointer.x: 44%
pointer.y: 76%

CLOCK
18:00:31

FPS
60 fps

VIEWPORT
1580 x 850

SCROLL VELOCITY
0 px

ACCENT HUE
h 289

Screenshot of a web application demonstrating 'Prop For That', displaying live data for pointer coordinates, clock, frames per second (FPS), viewport dimensions, scroll velocity, and an interactive accent hue slider.

Prop For That

A screenshot of a web page demonstrating the "Prop For That" library. The page features the prominent tagline "What JS knows, now CSS knows." and displays various real-time web metrics. These metrics include a clock (18:00:32), frames per second (58 fps), viewport dimensions (1580 x 850), scroll velocity (-5 px), and live mouse pointer coordinates (x 44%, y 76%). Below these, there is an "Accent Hue" slider to adjust color. The page also shows "View on GitHub" and "Read the docs" buttons.
npm i prop-for-that

What JS knows, now CSS knows.

Runtime state for CSS. Now there's a Prop For That.

View on GitHub Read the docs

  • LIVE

    pointer.x 39%

    pointer.y 81%

  • CLOCK

    18:00:34

  • FPS

    53 fps

  • VIEWPORT

    1580 × 850

  • SCROLL VELOCITY

    0 px

  • ACCENT HUE - SCRUB ME

    h 128

Screenshot of a web page demonstrating "Prop For That", a tool that exposes JavaScript runtime state to CSS. The page displays live metrics for clock time, frames per second, viewport dimensions, scroll velocity, and an interactive accent hue slider. It also shows live pointer X and Y coordinates.

What JS knows, now CSS knows.

Runtime state for CSS.

Now there's a Prop For That.

npm i prop-for-that

Live Data:

  • Pointer X: 51%
  • Pointer Y: 82%
  • Clock: 18:00:35
  • FPS: 57 fps
  • Viewport: 1580 × 850
  • Scroll Velocity: 0 px
  • Accent Hue: h 177 (Scrub me)
Screenshot of a web page demonstrating "Prop For That", showing live metrics in distinct cards for pointer position, clock, frames per second, viewport dimensions, scroll velocity, and an interactive accent hue slider.

What JS knows, now CSS knows.

npm i prop-for-that

Runtime state for CSS. Now there's a Prop For That.

Buttons: View on GitHub, Read the docs

Live data displays:

  • Pointer coordinates: X 58%, Y 81%
  • Clock: 18:00:37
  • FPS: 60 fps (with progress bar)
  • Viewport: 1580 × 850
  • Scroll Velocity: 0 px (with progress bar)
  • Accent Hue: h 222 (with interactive slider)
Screenshot of a web application interface for "Prop For That", featuring several interactive cards that display live runtime metrics such as pointer coordinates, current time, frames per second, viewport dimensions, scroll velocity, and an adjustable accent hue color slider.

What JS knows, now CSS knows.

Screenshot of a web page demonstrating a tool called 'Prop For That', which allows CSS to access runtime state. The page displays various live metrics including current time, frames per second, viewport dimensions, scroll velocity, pointer coordinates (X and Y), and an interactive slider to change the accent hue.

import 'prop-for-that/auto' once, then tag any element with data-props-for and list the props you want.

JS

import 'prop-for-that/auto'

HTML

<div data-props-for="size" style="resize: both"></div>

CSS

.box::after {
  counter-reset: w calc(var(--live-w)) h calc(var(--live-h));
  content: counter(w) ' x ' counter(h);
}

II LIVE

  • pointer.x 58%
  • pointer.y 81%

A screenshot demonstrating a web development tool. On the left, a resizable rectangular element is displayed, showing its current dimensions as "288 x 160" and labeled with "data-props-for='size'".

Quick start: just an attribute

Import 'prop-for-that/auto' once, then tag any element with data-props-for and list the props you want.

data-props-for="size"

288 × 160

JS

import 'prop-for-that/auto'

HTML

<div data-props-for="size" style="resize: both"></div>

CSS

.box::after {
  counter-reset: w calc(var(--live-w)) h calc(var(--live-h));
  content: counter(w) ' × ' counter(h);
}

LIVE

  • pointer.x 51%
  • pointer.y 76%
A screenshot demonstrating live properties of elements. A central rectangular box is labeled with the text "data-props-for='size'" and displays its current dimensions "288 × 160". In the top right corner, a smaller box is labeled "LIVE" and shows real-time pointer coordinates: "pointer.x 51%" and "pointer.y 76%".

import 'prop-for-that/auto' once, then tag any element with data-props-for and list the props you want.

JS

import 'prop-for-that/auto'

HTML

<div data-props-for="size" style="resize: both"></div>

CSS

.box::after {
	counter-reset: w calc(var(--live-w)) h calc(var(--live-h));
	content: counter(w) 'x' counter(h);
}

LIVE

  • pointer.x 91%
  • pointer.y 74%
A white box representing a UI element displays its current dimensions: 288 x 160. Text "data-props-for="size"" is visible above the dimensions. A "LIVE" section shows real-time pointer coordinates: pointer.x 91% and pointer.y 74%.

Import prop-for-that/auto once, then tag any element with data-props-for and list the props you want.

JS

import "prop-for-that/auto"

HTML

<div data-props-for='size' style='--size-w:h;'></div>

CSS

:host::after {
	counter-reset: w calc((--live-w) * 1px);
	content: counter(x) " " counter(h);
}
An illustration of a rectangular element labeled 'data-props-for='size'' displaying dynamic dimensions '288 x 160'.

Track the pointer

Bind pointer-local to an element and it gives its own --live-local-pointer-x-ratio / --live-local-pointer-y-ratio props

<div data-props="prop:pointer-local">
    ...
</div>
.tilt-card {
    --rx: calc((5 * var(--live-local-pointer-y-ratio)) + 3deg);
    --ry: calc((5 * var(--live-local-pointer-x-ratio) - 1) * 3deg);
    transform: rotateX(var(--rx)) rotateY(var(--ry));
}

Text shown on the demo panel:

  • LOCAL
  • move your pointer
  • the panel follows, then rests

A visual demo showing a black rectangular card element with white text that tilts and rotates in response to pointer movement.

Track the pointer

  • Bind `pointer-local` to an element and it gets
  • its own `--live-local-pointer-x-ratio` /
  • `--live-local-pointer-y-ratio` props
HTML
<div data-live='local' data-props='pointer-local-x,y'></div>
CSS
:root {
    --vx: calc(1 * var(--live-local-pointer-x-ratio) * 10deg);
    --vy: calc(1 * var(--live-local-pointer-y-ratio) * 10deg);
    transform: rotateX(var(--vy)) rotateY(var(--vx));
}
An illustration of a dark rectangular card with the text "LOCAL" and a mouse pointer icon at the top, and "move your pointer" at the bottom, demonstrating the pointer tracking effect.

Track the pointer

Bind pointer-local to an element and it gets its own --live-local-pointer-x-ratio / --live-local-pointer-y-ratio props.

HTML

<div id="card" data-props-for="pointer-local"></div>

CSS

.tilt-card {
	--rx: calc((.5 - var(--live-local-pointer-y-ratio)) * 16deg);
	--ry: calc((var(--live-local-pointer-x-ratio) - .5) * 16deg);
	transform: rotateX(var(--rx)) rotateY(var(--ry));
}
LIVE
  • pointer.x 37%
  • pointer.y 78%
LOCAL

move your pointer

the panel follows, then rests

A web demonstration showing a dark rectangular card with rounded corners, labeled 'LOCAL' and "move your pointer", which visually tilts in response to the user's pointer. To its right, code snippets for HTML and CSS are displayed, along with a live readout showing pointer X and Y coordinates as percentages.

ws, then rests

Screenshot of a dark UI component.

Track the pointer

Bind pointer-local to an element and it gets its own --live-local-pointer-x-ratio / --live-local-pointer-y-ratio props.

HTML

<div id="card" data-props-for="pointer-local"></div>

CSS

.tilt-card {
	--rx: calc((.5 - var(--live-local-pointer-y-ratio)) * 16deg);
	--ry: calc((var(--live-local-pointer-x-ratio) - .5) * 16deg);
	transform: rotateX(var(--rx)) rotateY(var(--ry));
}

On the interactive demo card, the text reads: LOCAL, move your pointer, the panel follows, then rests.

LIVE pointer values are displayed:

  • pointer.x: 70%
  • pointer.y: 63%
A demonstration area features a dark blue, rounded-rectangle card labeled "LOCAL" with large white text "move your pointer" and smaller text "the panel follows, then rests." The card is shown in a slightly tilted perspective, illustrating a reactive tilt effect driven by pointer movement. A small "LIVE" panel in the top-right corner displays the current `pointer.x` as 70% and `pointer.y` as 63%, which are variables that would influence the card's tilt.

Track the pointer

Bind pointer-local to an an element and it gets its own --live-local-pointer-x-ratio / --live-local-pointer-y-ratio props.

HTML

<div id="card" data-props-for="pointer-local"></div>

CSS

.tilt-card {
  --rx: calc((.5 - var(--live-local-pointer-y-ratio)) * 16deg);
  --ry: calc((var(--live-local-pointer-x-ratio) - .5) * 16deg);
  transform: rotateX(var(--rx)) rotateY(var(--ry));
}

Within the demonstration, a dark card displays "LOCAL" at the top and "move your pointer the panel follows, then rests" at the bottom.

Live pointer values: pointer.x 33%, pointer.y 63%.

A demonstration of an interactive web element: a dark, rectangular card that tilts as a mouse pointer hovers over it, illustrating a parallax effect. The slide shows the HTML and CSS code snippets used to implement this effect, along with live readouts of the pointer's x and y coordinates.

Track the pointer

Bind pointer-local to an element and it gets its own --live-local-pointer-x-ratio / --live-local-pointer-y-ratio props.

HTML

<div id="card" data-props-for="pointer-local"></div>

CSS

.tilt-card {
  --rx: calc((.5 - var(--live-local-pointer-y-ratio)) * 16deg);
  --ry: calc((var(--live-local-pointer-x-ratio) - .5) * 16deg);
  transform: rotateX(var(--rx)) rotateY(var(--ry));
}

LIVE

  • pointer.x 27%
  • pointer.y 65%
A demonstration shows a dark, rectangular card labeled 'LOCAL' with the text 'move your pointer' and 'the panel follows, then rests'. It simulates a 3D tilt effect as a mouse pointer hovers over it. The current mouse cursor is visible over the card. A small panel labeled 'LIVE' displays real-time pointer coordinates: pointer.x 27% and pointer.y 65%.

Reveal once, then stay

Triggered, not linked: const-has-entered latches once a panel is fully in view, so each reveals once and stays. Scroll back up: a view() timeline would reverse, this won't.

HTML

<artticle class="reveal" data-props-for="...

CSS

:reveal {
  opacity: calc(.25 + var(--const-has-e...
  translate: 0 calc(t1 - var(--const-has-e...
  transition: .6s;
}

Each step develops as it enters = lift, unblur, fade = then holds.

scroll the steps!

A blue circular icon with an 'i' inside marks a point in a vertical timeline.

02 / SCROLL

Reveal once, then stay

Triggered, not linked. --const-has-entered latches once a panel is fully in view, so each reveals once and stays. Scroll back up: a view() timeline would reverse, this won't.

HTML

<article class="reveal" data-props-for='...'>

CSS

.reveal {
  opacity: calc(.25 + var(--const-has-er
  translate: 0 calc(1 - var(--const-has
  transition: .5s;
}

Each step develops in as it enters — lift, unblur, fade — then holds.

LIVE

  • pointer.x 53%
  • pointer.y 59%
A large, faint "02" is visible in the upper right background. A circular annotation marker with the letter 'I' is shown with lines connecting to the HTML and CSS code blocks and the explanatory text.

02 / SCROLL

Reveal once, then stay

Triggered, not linked. —has-entered latches once a panel is fully in view, so each reveals once and stays. Scroll back up a view() timeline would reverse. this won't.

HTML
<article class='reveal' data-props='foo'></article>
CSS
reveal {
	opacity: calc(0.3 + var(--scroll-has-entered));
	translate: 0 calc(3 * var(--scroll-has-entered) * 1em);
	transition: 1s;
}

Each step develops in as it enters — lift, unblur, fade — then holds.

02 / SCROLL

Reveal once, then stay

Triggered, not linked. --has-entered latches once a panel is fully in view, so each reveals once and stays. Scroll back up a view() timeline would reverse. This won't.

HTML

<article class='reveal' data-props='foo'>

CSS

reveal {
  modify: calc(0 + var(--reveal-has-e...));
  translate: 0 calc(1 * var(--reveal-has...));
  transition: .8s;
}
  1. Each step develops in as it enters — lift, unblur, fade — then holds

02 / SCROLL

Reveal once, then stay

Triggered, not linked. --cont-has-entered latches once a panel is fully in view, so each reveals once and stays. Scroll back up & view() timeline would reverse, this won't.

HTML

<article class='reveal' data-props='{...}'

CSS

reveal {
    	opacity: calc(.35 + var(--cont-has-ex...));
    	translate: 0 calc(1 - var(--cont-has-...));
    	transition: .6s;
    }

Each step develops in as it enters — lift, unblur, fade — then holds

A large number "02" is displayed in the upper right, indicating the slide or section number. A circled number "1" appears next to the description of how each step develops.

a panel is fully in view, so each reveals once and stays. Scroll back up: a view() timeline would reverse, this won't.

HTML

<article class="reveal" data-props-for='...

CSS

.reveal {
	opacity: calc(.25 + var(--const-has-er...
	translate: 0 calc( (1 - var(--const-has-er...
	transition: .5s;
}

scroll the steps ↓

Each step develops in as it enters — lift, unblur, fade — then holds.

An interactive circular element containing the letter 'I' is positioned on a vertical timeline. A 'LIVE' status box in the top right corner displays 'pointer.x: 53%' and 'pointer.y: 59%'. Blue progress bars are visible beneath the HTML and CSS code blocks.

HTML

<article class="reveal" data-props-for='...'>

CSS

.reveal {
	opacity: calc(.25 + var(--const-has-er...));
	translate: 0 calc((1 - var(--const-ha...)));
	transition: .5s;
}

scroll the steps ↓

I Each step develops in as it enters — lift, unblur, fade — then holds.

II --live-visible lights this card only while it's wholly on screen.

LIVE

  • pointer.x 53%
  • pointer.y 59%
A diagram showing a vertical timeline with two marked steps, I and II, each with an explanatory text. On the left are HTML and CSS code snippets illustrating the `.reveal` class. A small white box in the top right shows live pointer coordinates.

HTML

<article class="reveal" data-props-for='

CSS

.reveal {
  opacity: calc(.25 + var(--const-has-er...
  translate: 0 calc((1 - var(--const-ha...
  transition: .5s;
}

scroll the steps ↓

Each step develops in as it enters — lift, unblur, fade — then holds.

--live-visible lights this card only while it's wholly on screen.

II LIVE

  • pointer.x: 51%
  • pointer.y: 57%

A diagram illustrating steps with a vertical timeline. Circle 'I' is linked to the text "Each step develops in as it enters — lift, unblur, fade — then holds." Circle 'II' is linked to the text "--live-visible lights this card only while it's wholly on screen."

</div>

CSS

.gauge__num::after {
  counter-reset: v calc(var(--live-value));
  content: counter(v);
}

.gauge__arc {
  background: conic-gradient(var(--accent)
  calc(var(--live-value-pct) * 360deg), var(--line) 0);
}

DISCRETE STATE: A STYLE QUERY, NOT VAR()

@container style(--live-value: 100) {
  .gauge__num { color: var(--max-tint); }
  .gauge__flag::after { content: 'max'; }
}
A circular gauge displays the number '46' in its center, with a blue arc indicating a partial fill. Below the code, a horizontal slider with an orange thumb and track is set to approximately 42. A small, grey cursor icon with three right-pointing arrows is positioned next to the CSS heading.

Discrete values

  • Turn 3 into the word "high"
  • container style() can:
  • zero JS branches.

HTML

A large number "04" is prominently displayed on the slide.

HTML

<div id="level" data-props-for="range">
  <input type="range" min="0" max="4" step="1" value="2">
</div>

CSS

@container style(--live-value: 0) {
  .level__word::after { content: 'Off'; }
}
@container style(--live-value: 4) {
  .level__word::after { content: 'Max'; }
  .level__core { --tier-h: 25; }
}

CSS

@container style(--live-value: 0) {
  .level__word::after { content: 'Off'; }
}

@container style(--live-value: 4) {
  .level__word::after { content: 'Max'; }
  .level__core { --tier-h: 25; }
}

MIN 12 · MAX 120

MAX REACHED

HTML

<div id="note" class="field" data-props-for="field" style="--min: 12;">
  <textarea minlength="12" maxlength="120"></textarea>
  </div>

CSS

.field_meter i {
  	inline-size: calc(var(--live-length) / var(--max) * 100%);
  }
  @container style(--live-length: 120) {
  	.field_word::after { content: 'max reached'; }
  }
A screenshot of a user interface element featuring a text area filled with placeholder text. Below the text area, a red progress bar is fully extended, and the text "MAX REACHED" is displayed. Above the text area, a label indicates "MIN 12 · MAX 120". To the right, two code blocks are shown, labeled "HTML" and "CSS", providing the source code for this component.

an metry

D , then rides nomnetry is all angle.

HTML

<div class="eye" data-props-for="pointer-local">...</div>
A black mouse pointer icon is visible on the left side. A partial circular graphic, possibly an eye, is visible at the bottom left.
<div class="eye" data-props-for="pointer-local">...</div>

CSS

.eye {
  --a: atan2(
    calc(var(--live-local-pointer-y-ratio) - .5),
    calc(var(--live-local-pointer-x-ratio) - .5));
}
.eye__pupil {
  translate:
    calc(cos(var(--a)) * 1.1rem)
    calc(sin(var(--a)) * 1.1rem);
}
An illustration of a stylized white eye with a large black pupil, positioned to the left of the code example.
<div class="eye" data-props-for="pointer-local">...</div>

CSS

.eye {
  --a: atan2(
    calc(var(--live-local-pointer-y-ratio) - .5),
    calc(var(--live-local-pointer-x-ratio) - .5));
}
.eye__pupil {
  translate:
    calc(cos(var(--a)) * 1.1rem)
    calc(sin(var(--a)) * 1.1rem);
}
An illustration of a stylized eye with a black pupil, looking towards three small grey mouse pointers. The eye is white with a subtle shadow, giving it a 3D effect.

cking S

  • once a second;
  • ...dout is a CSS
  • ...Interval

HTML

<div class="clock" data-props-for="clock">...</div>

CSS

A partial image of a clock face with hour and minute markers is visible in the bottom left corner.

HTML

<div class="clock" data-props-for="clock">...</div>

CSS

.clock__hand--sec {
	rotate: calc(var(--live-seconds) * 6deg);
}
.clock__hand--hour {
	rotate: calc(var(--live-hours) * 30deg
	+ var(--live-minutes) * .5deg);
}
A partial image of a clock face is visible on the left side of the slide. A mouse cursor is shown on the right, pointing between the HTML and CSS code sections.

A clock, ticking in pure CSS

The clock source ticks --live-seconds-- once a second, the hands rotate by `calc()`, and the readout is a CSS counter. The only JavaScript is one `setInterval`.

HTML

<div class='clock' data-props='{ "src":"clock"}'></div>

CSS

.clock__hand--sec {
	rotate: calc(var(--live-seconds) * 360deg);
}
.clock__hand--hour {
	rotate: calc(var(--live-hours) * 30deg
	+ var(--live-minutes) * .5deg);
}

An animated analog clock face with a digital time readout below it, showing 18:02:31.

A clock, ticking in pure CSS

The clock source ticks calc(--live-seconds) once a second, the hands rotate by calc() and the readout is a CSS counter. The only JavaScript is one setInterval

HTML

<div class='clock' data-props-for='clock'></div>

CSS

clock_hand-sec {
	rotate: calc(var(--live-seconds) * 1deg);
}

clock_hand-hour {
	rotate: calc(var(--live-hours) * 1deg);
	var(--live-minutes) * 1deg);
}

An analog clock face with a digital time readout below it showing 18:02:33.

A clock, ticking in pure CSS

The clock source ticks --live-seconds once a second; the hands rotate by calc() and the readout is a CSS counter. The only JavaScript is one setInterval.

18:02:40

HTML

<div class="clock" data-props-for="clock"></div>

CSS

.clock__hand--sec {
	rotate: calc(var(--live-seconds) * 6deg);
}
.clock__hand--hour {
	rotate: calc(var(--live-hours) * 30deg
		+ var(--live-minutes) * .5deg);
}
An analog clock face with hour, minute, and second hands, visually representing the time 18:02:40. Below the clock, a digital readout also displays 18:02:40. Adjacent to these, two code blocks are shown: one for HTML with a simple div, and one for CSS demonstrating how clock hands are rotated using CSS variables and calc() functions.

device state

The quiet globals: online, link speed, battery, frame rate. Each writes on :root for CSS to read. Toggle offline in DevTools to watch it flip. (Battery and Network are Chromium-only.)

CONNECTION

online

navigator.online

EFFECTIVE TYPE

4g

9 Mbps · 100 ms rtt

BATTERY

64%

FRAME RATE

60 fps

HTML

<div class="tiles" data-props-for="online network battery fps">...</div>

CSS

@container style(--live-online: 0) {
  .tile--online { color: var(--limit); }
  .tile--online::after { content: 'offline'; }
}
.tile__gauge i {
  inline-size: calc(var(--live-battery-level) * 100%);
}

LIVE

pointer.x 30%

pointer.y 20%

Screenshot of a web page interface displaying real-time device state information. It shows four cards for "Connection" (online), "Effective Type" (4G, 9 Mbps, 100 ms rtt), "Battery" (64%), and "Frame Rate" (60 fps). Below these, HTML and CSS code snippets are shown. A small "LIVE" widget in the top right corner displays pointer X and Y coordinates (30% and 20%).

from playback

A conic-gradient ring fills from --live-progress as the clip plays. Typed as an @property, the value interpolates, so the ring sweeps smoothly between updates.

HTML

<div id="player" data-props-for="media">
  <video src="clip.mp4" muted loop autoplay></video>
</div>

THE SETUP: TYPED PROPS BECOME INTERPOLATABLE

configure({ typed: true }) // register --live-* as @property

CSS

.player { transition: --live-progress .25s linear; }

.player_ring {
  background: conic-gradient(var(--accent)
    calc(var(--live-progress) * 360deg), var(--line) 0);
}

II LIVE

pointer.x 50%

pointer.y 59%

A circular video player shows a close-up of a plant bud, with a circular progress ring filling around its edge. Below the video, "0s / 5s" is displayed. A small panel labeled "II LIVE" in the top right corner displays "pointer.x 50%" and "pointer.y 59%".

from playback

A conic-gradient ring fills from --live-progress as the clip plays. Typed as an @property, the value interpolates, so the ring sweeps smoothly between updates.

HTML

<div id="player" data-props-for="media">
  <video src="clip.mp4" muted loop autoplay></video>
</div>

THE SETUP: TYPED PROPS BECOME INTERPOLATABLE

configure({ typed: true }) // register --live-* as @property

CSS

.player { transition: --live-progress .25s linear; }

.player__ring {
  background: conic-gradient(var(--accent)
    calc(var(--live-progress) * 360deg), var(--line) 0);
}

3s / 5s

II LIVE
  • pointer.x 47%
  • pointer.y 52%
A screenshot of a web browser interface demonstrating a video playback progress indicator. On the left, a circular progress bar frames an image of a red flower, with a red arc indicating 3 seconds out of a total of 5 seconds. On the right, separate code panels display HTML, JavaScript configuration, and CSS code snippets related to the progress bar functionality. In the top right corner, a small panel titled "II LIVE" shows live pointer coordinates: "pointer.x 47%" and "pointer.y 52%".

from playback

A conic-gradient ring fills from --live-progress as the clip plays. Typed as an @property, the value interpolates, so the ring sweeps smoothly between updates.

|| LIVE
pointer.x 47%
pointer.y 52%

HTML

<div id="player" data-props-for="media">
  <video src="clip.mp4" muted loop autoplay></video>
</div>

THE SETUP: TYPED PROPS BECOME INTERPOLATABLE

configure({ typed: true }) // register --live-* as @property

CSS

.player { transition: --live-progress .25s linear; }

.player__ring {
  background: conic-gradient(var(--accent)
    calc(var(--live-progress) * 360deg), var(--line) 0);
}

3s / 5s

A circular image of a red flower bud with green leaves is shown, with a red ring partially encircling it, indicating progress. Below the circle, the text "3s / 5s" is displayed, suggesting 3 seconds out of 5 seconds of playback. On the top right, a small box labeled "|| LIVE" shows "pointer.x 47%" and "pointer.y 52%".

from playback

A conic-gradient ring fills from --live-progress as the clip plays. Typed as an @property, the value interpolates, so the ring sweeps smoothly between updates.

HTML

<div id="player" data-props-for="media">
    <video src="clip.mp4" muted loop autoplay></video>
</div>

THE SETUP: TYPED PROPS BECOME INTERPOLATABLE

configure({ typed: true }) // register --live-* as @property

CSS

.player { transition: --live-progress .25s linear; }

.player__ring {
    background: conic-gradient(var(--accent)
        calc(var(--live-progress) * 360deg), var(--line) 0);
}

LIVE

  • pointer.x 35%
  • pointer.y 48%
A circular video player displaying a close-up of a red flower bud, surrounded by a progress ring partially filled to indicate video playback. Video controls and the current time of '1s / 5s' are visible below the video.

UI that saves your battery

Below 20% battery, a @container style() strips the animation and heavy paint to save power. Flip the switch to simulate it. (Battery Status is Chromium-only.)

simulate low battery

now playing
full power · effects on

CSS

.ambient {
  --low: clamp(0, round(up, calc(.2 - var(--live-battery-level, 1))), 1);
}
@container style(--low: 1) {
  .ambient__card { animation: none; }
  .ambient__state::after { content: 'low battery'; }
}
A screenshot of a web UI demonstrating a "low battery" mode. It features a toggle switch labeled "simulate low battery" which is currently off. Below it, a card displays "now playing" with "full power · effects on". To the right, a code editor shows CSS rules for an `.ambient` class that adjusts `--low` variable based on live battery level, and then uses `@container style(--low: 1)` to remove animation and change content to 'low battery' when in low power state. A small "LIVE" widget in the top right shows "pointer.x 21%" and "pointer.y 44%".
	transition: none; /* follow while held */
}
Screenshot of a code editor showing a CSS snippet.

CSS can't see

field-state tracks the interaction history pseudo-classes can't

  • --live-dirty, --live-touched, --live-changed, --live-submitted per input. form-state aggregates the whole form.
  • --live-valid, --live-all-valid, --live-valid-count, --live-completion to drive the progress bar and gate Submit. Type then delete back.
  • --live-dirty is always, --live-changed once different, --live-touched once interacted, --live-submitted once the gate closes. The required plan <select> is there.
  • value-num joins that gate, and the colour picker (color-input--live-color) re-themes the whole form — live, in CSS.
  • --live-color re-themes the whole form — live, in CSS.

Form Example

3 / 4 valid

3 / 4 valid

3 / 4 valid

INCOMPLETE

INCOMPLETE

INCOMPLETE

@dirty @touched @changed @submitted

@dirty @touched @changed @submitted

HTML

<form data-props-for="form-state">
  <input name="name" required data-props-for="field-state">
  <select name="plan" required data-props-for="field-state">
    <option value="none">...</option>
  </select>
  <input type="color" data-props-for="color-input">
</form>

CSS

.form {
  --accent: var(--live-color); /* the picker re-skins it */
}
form:is(--accent, var(--live-color)); /* the picker re-skins it */
.seats::after {
  content: counter(--live-value-num) ' seats';
}
form:is(
  [data-props-for="field-state"] [data-live-all-valid],
  [data-props-for="field-state"] [data-live-completion]
) [data-submit] {
  opacity: 1; /* the gate opens */
}

LIVE

pointer.x: 52%

pointer.y: 33%

A screenshot showing a web application interface with a form, live interaction states, and code examples. A small widget displays live pointer coordinates.

Form State Management and Styling Example

3 / 4 valid INCOMPLETE

DISPLAY NAME

Value: ada,

Status: dirty, touched, changed, submitted

EMAIL

Value: ada@example.com

Status: dirty, touched, changed, submitted

TEAM PLAN

Selected: Choose a plan...

BRAND COLOUR

Selected: Purple

Submit Reset

HTML

<form data-props-for="form-state">
  <input name="name" required data-props-for="field-state">
  <select name="plan" required data-props-for="select"></select>
  <input type="color" data-props-for="color-input">
</form>

CSS

.form { --accent: var(--live-color); } /* the picker re-skins it */
.seats::after { content: counter(--live-value-num) ' seats'; }
@container style(--live-all-valid: 1) {
  .submit { opacity: 1; } /* the gate opens */
}

LIVE

  • pointer.x 36%
  • pointer.y 37%

Screenshot of a web form interface demonstrating form input states and live styling. The form includes fields for Display Name, Email, Team Plan, and Brand Colour. Below the Display Name field, the states dirty, touched, and changed are active. Below the Email field, no states are active. HTML code shows the form structure with custom data attributes. CSS code demonstrates styling that dynamically updates based on form state and a live color picker. A "LIVE" panel in the top right displays pointer coordinates (x and y).

Status: 3 / 4 valid (INCOMPLETE)

Form Demonstration

Display Name: ada,

Current states: dirty, touched, changed

Email: ada@example.com

Current states: dirty, touched

Team Plan: Team (options include Solo, Team, Org)

Brand Colour: Purple

HTML Code

<form data-props-for="form-state">
  <input name="name" required data-props-for="field-state">
  <select name="plan" required data-props-for="select">...</select>
  <input type="color" data-props-for="color-input">
  </form>

CSS Code

.form { --accent: var(--live-color); } /* the picker re-skins it */
.seats::after { content: counter(--live-value-num) ' seats'; }
@container style(--live-all-valid: 1) {
  .submit { opacity: 1; } /* the gate opens */
}

LIVE Data:

  • pointer.x: 28%
  • pointer.y: 59%

Screenshot of a web development tool interface, displaying a form with input fields for display name, email, team plan (dropdown), and brand color (color picker). The form fields show their current state indicators: dirty, touched, changed. The right side of the interface shows corresponding HTML and CSS code snippets, with live data feedback for pointer coordinates.

Interactive Form Demonstration and Live Code

The slide displays a split view of a web application. On the left, an interactive form demonstrates real-time field states and data binding. On the right, corresponding HTML and CSS code snippets are shown with live variable values.

Form Interface

  • DISPLAY NAME: An input field pre-filled with "ada,". Below the field are status indicators: dirty, touched, changed, submitted.
  • EMAIL: An input field pre-filled with "ada@example.com". Below the field are status indicators: dirty, touched, changed, submitted.
  • TEAM PLAN: A dropdown selection box currently showing "Team", with "5 seats included" text below it.
  • BRAND COLOUR: A color input picker displaying a purple color.

At the bottom of the form are "Submit" and "Reset" buttons.

Code Snippets

HTML:

<form data-props-for="form-state">
  <input name="name" required data-props-for="field-state">
  <select name="plan" required data-props-for="select">...</select>
  <input type="color" data-props-for="color-input">
</form>
  
CSS:

.form { --accent: var(--live-color); } /* the picker re-skins it */
.seats::after { content: counter(--live-value-num) ' seats'; }
@container style(--live-all-valid: 1) {
  .submit { opacity: 1; } /* the gate opens */
}
  

Live Data Display

  • pointer.x 29%
  • pointer.y 66%

Screenshot of a web development interface, split vertically. The left panel displays a live interactive web form, and the right panel displays corresponding HTML and CSS code, along with live pointer coordinates.

Live Color Extraction and Dynamic Shadow Effect

HTML

<figure data-props-for="img-color">
  <img src="art.jpg">
</figure>

CSS

.sun-card { /* shadow: image colour, cast off the cursor */
  box-shadow:
    calc((.5 - var(--live-local-pointer-x-ratio)) * 8rem)
    calc((.5 - var(--live-local-pointer-y-ratio)) * 8rem)
    3rem
    oklch(from var(--live-img) l c h / .6);
}

Extracted Colors

  • DOMINANT
  • ACCENT
  • DARK
  • LIGHT
  • AVG

Live Pointer Data

pointer.x: 35%

pointer.y: 80%

A web development demonstration interface showing a large card with a purple gradient, whose shadow dynamically responds to pointer movement. Below the card are five small square color swatches labeled DOMINANT, ACCENT, DARK, LIGHT, and AVG, displaying colors extracted from the image. Further below is a row of circular thumbnails, including images and solid colors, with a purple gradient thumbnail currently selected. On the right, HTML and CSS code snippets are displayed, detailing the structure and styling for the card's dynamic shadow based on image color and live pointer coordinates. A "LIVE" panel shows the current pointer x and y percentages.
<section class='slide-text'> <h3>Component Properties Reference</h3> <ul> <li> <h4><code>nav-type</code> <span class="tag">GLOBAL</span></h4> <ul> <li><code>--const-nav-type</code>: <code>navigate</code></li> </ul> <p>How you reached this page, set once: navigate / reload / back / prerender.</p> </li> <li> <h4><code>pointer-local</code> <span class="tag">ELEMENT</span></h4> <ul> <li><code>--live-local-pointer-x-ratio</code></li> <li><code>--live-local-pointer-y-ratio</code></li> <li><code>--live-local-pointer-inside</code></li> </ul>

Fully in view this a glitch once entered.

  • field

    • .is-value-static
    • .is-type-oriented
    • .is-pattern-oriented
    • .has-top-input
    • .has-bottom-input
  • field-text-area

    • .has-resize-handle
    • .is-resizable-both
    • .is-resizable-vertical
    • .is-resizable-horizontal
    • .can-snap-resize
  • field-select

    • .has-focus-root
    • .has-select-all
    • .allows-opt-groups
    • .can-filter-options
    • .can-multi-select

A slider or number input's value

  • field

    • .is-for-short
    • .is-for-long
    • .has-range-indicator
    • .is-range-specific
    • .is-range-flexible
  • field-number

    • .is-discrete
    • .is-continuous
    • .is-float-limited
    • .can-step-up
    • .can-step-down
  • field-radio

    • .has-label-group
    • .can-toggle
    • .is-group-vertical
    • .is-group-horizontal
    • .is-group-flexible

Text field length, validity, and remaining character output

  • field-state

    • .is-valid
    • .is-invalid
    • .is-hidden
    • .is-disabled
    • .is-focused
    • .is-hovered
    • .is-pressed
    • .is-dragged
  • field-text-output

    • .is-pre-filled
    • .is-char-count
    • .is-char-limit
    • .is-remaining
    • .is-maxed-out
  • field-input-type

    • .is-single-line
    • .is-multi-line
    • .is-email
    • .is-password
    • .is-tel
    • .is-url
    • .is-number
    • .is-search

Source: prive-for-that.netlify.app/docs/hooks/form/field

Screenshot of a web browser window displaying documentation for a UI component library, presenting various properties and states for form fields in a multi-column layout.
<section class='slide-text'><h3>CSS Custom Properties for Element States</h3><h4>Field Flags</h4><ul><li><code>--live-bad-input</code></li><li><code>--live-custom-error</code></li></ul><p>Which constraint failed: missing, wrong type, pattern, bad input, custom.</p><h4>Length, Range & Step Bounds</h4><ul><li><code>--live-range-overflow</code></li><li><code>--live-step-mismatch</code></li><li><code>--live-underflow</code></li></ul><p>Too long/short, under/over range, step mismatch.</p><h4>Form Interaction History</h4><ul><li><code>--live-unchanged</code></li><li><code>--live-edited</code></li><li><code>--live-submitted</code></li></ul><p>Pseudo-classes can't see

THANKS!

Components can know. Now go let them.

What did I miss? Let me know!

The slide shows colorful confetti showering across a white background.

THANKS!

Components can know. Now go let them.

What did I miss? Let me know!

LESS DAY

Stylized text forming the words 'LESS DAY' using black rectangular blocks.

People

  • Bramus Van Damme
  • Jane Ori
  • Jen Simmons
  • Kevin Powell
  • Paul Irish
  • Temani Afif

Technologies & Tools

  • Angular
  • HTML5 Boilerplate
  • Open Props
  • PWA
  • shadcn/ui
  • Shadow DOM

Standards & Specs

  • :has()
  • @scope
  • @starting-style
  • calc-size
  • Cascade layers
  • Color fonts
  • Container queries
  • Content visibility
  • contrast-color
  • CSS Anchor positioning
  • font-palette
  • light-dark()
  • Logical properties
  • Media queries
  • OKLCH
  • Popover API
  • prefers-color-scheme
  • prefers-reduced-motion
  • Quantity queries
  • Relative color syntax
  • Scroll state queries
  • Scroll-driven animations
  • Style queries
  • Variable fonts
  • View transitions

Concepts & Methods

  • Design tokens
  • FLIP animation
  • Intrinsic web design

Organisations & Products

  • CSS Working Group
  • Shopify