CSS is eating JavaScript
Kevin Powell's Introduction and the Expanding Power of Modern CSS
Anna introduces Kevin Powell as a passionate CSS educator known for his massive YouTube following, prompting him to open day two of CSS Day with a celebration of how far CSS has come. Kevin traces the arc from the checkbox hack — manipulating page content without JavaScript — through scroll-driven animations, anchor positioning, and complex math functions including trigonometry. He showcases a graph theory demo by Thomania Fiff that calculates shortest paths between page elements entirely in CSS, establishing the talk's central theme: CSS is taking over functionality once reserved for JavaScript.
The Separation of Concerns Debate: Critics of CSS Complexity
Kevin shares YouTube comments from his graph theory demo video, highlighting a recurring criticism that CSS is becoming a generalized language and 'going too far.' He addresses the separation of concerns argument — HTML for content, CSS for appearance, JavaScript for everything else — and exposes its irony: many developers who hold this view still have JavaScript fetch data, process logic, and apply inline styles while CSS sits idle. Kevin illustrates this anti-pattern with the Weather Network's 14-day forecast, where precipitation bar heights are set as hardcoded pixel values by JavaScript rather than derived from CSS logic.
From Custom Properties to the Advanced ATTR Function
Kevin advocates for the current best practice of using custom properties as inline styles so CSS can own the math and presentation logic, but notes that tracking where those values come from can be confusing during debugging. He reviews the long-standing but limited ATTR function — restricted to the content property and unable to produce searchable or translatable text — before introducing the advanced ATTR function, currently in Chromium and Safari Tech Preview. Kevin demonstrates how assigning a type (such as `<number>`) to an attribute value is essential: without it, the value is treated as a quoted string and silently fails, a bug that DevTools does not currently surface as an error.
Refactoring the Weather App: Moving Styling Logic from JavaScript into CSS
Kevin live-codes a refactoring of the weather card example, stripping precipitation math out of JavaScript and instead passing raw data as a data attribute on the parent element so CSS can access it. He uses CSS math functions — `calc()` and `min()` — to reproduce the same percentage-height calculation directly in the stylesheet, including an overflow guard, and assigns a `<number>` type to the custom property. Kevin shows how this dramatically improves the information flow: the custom property's origin is now traceable through the data attribute in DevTools, and type assignment causes invalid values to appear crossed out rather than silently broken.
E-Commerce Case Studies: Walmart's Inline Style Problem and Exposing Full Card Data
Kevin examines Walmart's product tag implementation, which uses JavaScript to apply four inline styles — background, color, border-radius, and font-weight — inconsistently across pages, despite having 1,300 design system custom properties already available in the CSS. He argues the right approach is to expose all semantically meaningful card attributes — price, stock, rating, promotion — so CSS can choose which to use for styling decisions. Kevin demonstrates this philosophy with a star rating component adapted from Yuna's blog post, using ATTR with a linear gradient mask to render dynamic star fill percentages, then adding CSS `round()` to snap to half-star increments.
CSS Sorting, Reading Flow, and Dynamic View Transition Names
Kevin demonstrates using the CSS `order` property combined with ATTR-derived numeric values to sort product cards by rating and price without JavaScript DOM manipulation, using a negative multiplier to invert sort direction. He introduces the `reading-flow` property, which ensures keyboard tab order follows the visually reordered layout rather than DOM source order, preserving accessibility when CSS resequences content. Kevin also shows how the advanced ATTR function with a `custom-ident` type can assign unique view-transition names dynamically from an ID attribute, enabling smooth animated transitions when the sort order changes.
CSS if() for Conditional Property Values
Kevin introduces the CSS `if()` function — currently Chromium-only — which brings conditional logic directly into property value declarations without needing separate selector rules. He explains the syntax (condition, then value, else fallback) with examples spanning media queries, feature queries, and style queries, then live-codes a weather example where precipitation bar color switches between a snow color and a rain color based on a data attribute value. Kevin notes this approach allows CSS to make styling decisions that previously required JavaScript to evaluate a condition and apply a class, and emphasizes readable formatting to avoid errors with nested semicolons.
ATTR Security Restrictions and Container Style Queries as a Workaround
Kevin explains a spec-defined security restriction called 'attr-tainted' values, which prevents URLs from appearing in any property that depends — even indirectly — on an ATTR function result, blocking his attempt to assign background-image icons via an if() style query. He walks through a three-div debugging demo that isolates the issue and warns the audience to watch for it. As a solution, Kevin turns to container style queries — now baseline newly available across all browsers — which sidestep the restriction and can assign background images based on custom property values. He compares the two approaches: style queries can only target descendants, not the element itself, while if() can query the element directly but returns only one value at a time rather than a full declaration block.
Range Syntax and Chained Conditions for Complex Styling Logic
Kevin demonstrates range syntax within if() and container style queries to handle numeric comparisons, using e-commerce availability sorting as a practical example — products with zero stock are pushed to the end by substituting a large sentinel value via `stock > 0`. He then applies chained `and` conditions to weather icons, distinguishing heavy rain from light rain by precipitation threshold, and combining weather type with precipitation amount to produce a partly-cloudy-with-rain icon for ambiguous API data. Kevin closes by reframing the talk's thesis: CSS isn't eating JavaScript, it's simply growing into its own domain — styling logic, including conditional logic, belongs in the stylesheet where it applies.
Audience Q&A: Security, Performance, and ATTR Practical Considerations
The Q&A covers a range of practical concerns raised by the audience. Kevin addresses security risks in the advanced ATTR function, explaining that the spec's attr-tainted mechanism already guards against URL-based attacks. He fields questions on whether attribute selectors would have been simpler than if() for the rain/snow example (acknowledging they probably would), how to manage design system exposure in the browser, whether the performance cost of CSS computation outweighs JavaScript alternatives (recommending testing but noting raw data handoff is likely faster), and how the string and custom-ident types differ from the default string treatment for use cases like view transition names.
Kevin spoke at CSS Day two editions ago, my first CSS Day ever. And just as he got on stage, I overheard two young lads saying really excited to each other, it's the YouTube guy. He was so cute. I was like, oh, it's so cute. But for Ali, our first speaker is an educator who is very passionate about CSS and a highly talented one because which reflects on the massive YouTube following they have.
But he's also a writer from newsletters to articles, courses, and podcasts. And it takes a lot of talent and a lot of work to do all this. Front end friends, big a big give me a big round of applause for YouTube guy, Kevin.
Hello, my front end friends. I'm so happy to be here kicking off day two of CSS Day. It's a pleasure to be here. Happy to see everyone manage to wake up nice and early. Oh, no. Restart now. Yeah. Another time. And, yeah, I figured what a better way to kick off the day of CSS day than talk about how CSS is eating JavaScript.
Right? We're all CSS enthusiasts here. I'm sure a lot of people here play around with CSS only solutions when they can. Some of us are probably a bit more ambitious with those attempts than others. But for everybody who has done anything that's been a CSS only type of approach, you probably started with a bit of code that looked like this, with the checkbox hack, which Leroy mentioned yesterday. Right? I remember the first time I saw this bit of code. I was like, wait.
I can manipulate the content on the page based on user interactions without any JavaScript? How come nobody told me this earlier? Then, of course, I went and did a whole bunch of probably kind of irresponsible things with it and pushed it to production without knowing any better. But it was really exciting, and I enjoyed that. And you start getting into the world of that and seeing what you can do. But probably what's more amazing now and more exciting is what we can do without hacks.
We have things that used to require lots of JavaScript, like scroll driven animations that we can do with just a couple of lines of CSS now. And I'm really excited scroll triggered animations to make their way in as well. Things like anchor positioning. Jake did allude to the fact that it can be a little finicky. There's still some bugs to work out.
But so often, when we're using things like anchor positioning, know for me when I play with it, I'm like, this is kind of too easy. I feel like I'm cheating a little bit at times as well. And not only can we get rid of some tooltip dependencies that we might have needed in the past, but we can also do some other cool things with it and be a little bit more creative. And it just, again, works so easily.
And it's it's a bit of magic. Modern CSS is so great. And not only can we do things like that, though, there's some behind the scenes things as well where we're taking away from what we used to require JavaScript for with all the math functions that are coming in now. We can do all of these things that we used to have to require or have to use JavaScript for to do all the complex math and calculations.
Now we can do this directly in CSS. I won't lie. When this first happened, I was a little bit intimidated. I like making backgrounds change the color, maybe center a div somewhere. Then all of a sudden, trig shows up, and I'm like, that's not what I signed up for. But I managed to figure out how to make this slide.
This is CSS. I got everything in a circle with no magic numbers. I was very proud of myself for that. Of course, other people who actually understand how trigonometry works, they can do a little bit more with it. This is a demo by Thomania Fiff, graph theory demo, where he's measuring the distance between the different points on the page in real time with CSS and then calculating the shortest path between those points from one to the other, all without any JavaScript.
It's crazy that we can do things like that these days. And I mean, we're at CSS today. I know everybody here thinks all of these things are just amazing and incredible. Right? CSS is coming to such a wonderful place. What you might be surprised to learn is outside of these walls, there are some people who don't actually think all of this is a good idea.
So as Anna alluded to, have a YouTube channel. When you post things on YouTube, people leave comments. And I did a video. I did a video where I looked at that graph theory demo by Tammany, trying to figure it out. I never really understood it, but I sort of dug in a little bit to see what was going on.
And it got a lot of comments, and most people were just blown away with what we can do with modern CSS. But there was definitely a theme in the comments that started to emerge. And I did pick one specific one. This is the one that I I found that I thought you'd all enjoy. We're gonna skip to the last paragraph to start with where he said CSS should not need calc functions.
If you don't know what color your web page should be at generation time, what are you doing? I thought you guys would like that one. So it's a YouTube comment section, People are gonna be trolling. They're gonna, you know, whatever. It's part of the game. That's completely fine. But I did also pick this one because what he said in the second paragraph goes more with the general theme that I did see emerging, where he said, languages should specialize.
And shifting from style to programming is making CSS a generalized language now, which is silly at best. And this is a theme that I saw in that video. I've seen it in other videos too, where I'm just playing around having fun, not doing anything complex like graph theory. I've seen it in social media posts. I've seen it or I've heard it through conversations with developers where we're talking a bit about it, they're like, I think CSS is going too far now.
It's starting to do a little bit too much. It's eating into the job of CSS. Separation of concerns. Right? That's the big red flag that goes off for people. They see that CSS is starting to do things they think JavaScript should be doing, so they raise the red flags. HTML should be the content. CSS should make things look pretty.
And I guess JavaScript gets to do everything else. But the funny thing with this, and when you hear the separation of concerns, then you might go to a website that one of these developers is working on. And often, you might see some sort of flow that's like this, where the user visits the page. The JavaScript might have to go get some data from an API, pulls in that data, processes the data, does all the logic, and then they throw an inline style on there based on everything they figured out. Well, CSS just sits there in the background.
The classes are there. They're being applied. It's not really having too much fun. And this I find kind of funny because it's separation of concerns. And of course, it's an inline style. Technically, it's CSS, but JavaScript's doing all the hard work. It took me about two minutes to try and find an example to use in my slides.
This is the one that I did come across. This is from the weather network with their fourteen day forecast that shows the next six days of weather. And this is that typical flow. The user lands on the page. They have to hit the they get the location data, have to hit the API, get all the information for there, bring in the temperatures, everything else. And they have those rain bar indicators underneath.
And that's a nice little UX thing, five millimeters of rain. Is that a lot? I don't know. So let's give a little bar to let users know. But of course, they're doing that with an inline style with a height of 62 pixels. If you're going to do it with JavaScript, you might as well just magic number the hell out of it.
Right? 62 pixels on there. But this is sort of a very typical way that you see a lot of things done. Now, some of a lot of people here are very smart and we're CSS people. So you might be going, well, that's not the right way to do it. Maybe you want to do something like this. Right? Have JavaScript set a custom property instead as an inline style.
And then if you do that, then you can do some processing and some of the math and other things on there. And I think this is the right way to work. Right now, this is fantastic. It allows us to pass information to the CSS, and then we can do what we need to do. But I don't know about you, but I I've run into situations with this. It works really well.
But I find that the flow of information can be a little bit strange at times. You get stuck. So here's an example. I took the you know, recreating the weather network and what they were up to. And here we can see in my JavaScript, I have sort of the fake API data. Everything's coming in. And if I come and look in the CSS, I find my visual.
And here is the block size, which is my precipitation amount. I didn't put the math in here. I was lazy. I kept the math in the JavaScript. But if I start looking for this custom property anywhere in the CSS, I can't actually find it. And you start poking around. You get a little bit annoyed. And eventually, do an inspect element on there.
And when you do your inspect, you eventually find it. There, you can see it as an inline style with the percentage coming in or whatever it is, whatever the value is. And it's not the end of the world. It's a great way to work currently, but I think things are going to start changing in a pretty dramatic way.
And there's a way that we can make this flow of information a lot more clear going into the future using the adder function. I might call it the adder function. I might call it the attribute function. I might say ATTR. I always call it something different, but I think all the spec people call it the adder function.
So I'll try to stick with that. Show of hands who's used this already, just like the basic version of it? Cool. So we'll do the next part pretty fast. But just as a quick one, not everyone put their hand up. So basic way that it used to work because we've had this forever. It's been in CSS for a really long time.
But if I come on my card here, I have a custom property set up with a content. I can put content there, it shows up in the tag of my card. But instead of that, I can do ATTR, and I can put in my data. Whoops, promo. And it pulls that in. And it's the text or the value of the data promo is there. So if I change the content, it changes inside of there.
It's fantastic. You can do this for any attribute you want, but up until now, it only worked on the content property, so a little bit limited. You could use it for a few things, though. If I change this content or the data promo to be sale, I have a styling hook down here with an attribute selector. So then we can have some styling hooks, other things.
And that's kind of good. But again, it's been limited to the content property. Because it's limited to the content property, it also means the text can't be searched for or selected. And there's internationalization issues because it won't be translated because it's not in the DOM. That text is being generated by CSS. So there are some limitations to it, which some niche use cases, which can be really good.
But sometimes it's a deal breaker and you don't always want content being injected that way. I I had remembered there also being screen reader issues with it, but Manuel did some tests for me and a bit I did a bit of research after and I haven't I think modern screen readers do read the text without any issues.
So at least that's a good thing. The exciting thing now is we have the advanced adder function. And this can be used to provide a value for any property. We're no longer limited just to the content. This is currently in Chromium. It's also in Safari's technical preview. And it's behind a flag in Firefox. So we're right around the corner from this hopefully becoming at least newly available. And as a quick thing, this is actually the very first demo I created when I heard about this and when it was first in Chrome, where I was like, oh, I like Grid, As you might know, I always play around with it.
But one thing that can be annoying is we need lots of modifier classes for different column counts. So like, can I just do an attribute on all of these and throw that on there so I put the different attribute on each one? I have my grid template column set up looking at a custom property. So if I put three in there, obviously, it's going to be three columns for all of them.
But now I can say, a t t r, and it was my data column count. But it doesn't work. It could work. One frustrating thing though is if you go and look in your dev tools at this point because Kevin told me I could do this. Why isn't it working? So you go look in your dev tools to make sure you didn't make a typo.
Chrome people that are here, maybe you could fix this. But currently, in my dev tools, if I go take a look, I don't see any problems. I see the three coming through here. I see the three even coming here, and there's no point anywhere here this is showing up as invalid. That's a little bit weird and annoying because it is invalid.
And it's invalid because you can see it's in quotation marks because it's actually a string. And that's why we could traditionally only use it with the content property because it was bringing in a string of content. Anyone who's used registered custom properties now knows that we can actually assign types or the syntax to the different types of data we're using in CSS now.
And this allows us to do that. So I can come here. And for now, we're going to talk more about this in a few minutes. But I'm just going to assign this a type of number. And by saying it's a number, CSS now actually sees it as the number three instead of a string of three. And my column counts are all working.
And thank you. We change that. We get eight columns, whatever it is. So a nice little quick win to get rid of a whole bunch of modifier classes. You can just do things that way. Really important, you do not need to be using data attributes. It can prevent naming collisions, either current or in the future.
And it's also handy if you are using JavaScript to use data attributes, but you don't have to. I could remove the data attribute from each one of these. And you can use custom attributes as well. We just have to keep the name the same in our HTML or in our yeah. In the HTML and the CSS, and it still works.
And I did that when I was playing around with the first demo. I'm like, oh, this is really cool. And I was like, wait. Is our favorite utility framework soon going to feel like it's writing HTML like it's 1995 all over again? I don't want to worry too much about that. We'll see where that if that goes down that road.
But for me, the most important thing is I started thinking about different ways we could actually leverage this to improve the flow of information from JavaScript into CSS. And let's look here again at how we can do that. So the same demo as before. Right now, everything is happening in JavaScript.
I have a max participation here that's being used, but down here, we have the math being done for different things, and we have it is doing it as a style of my precip amount. So I'm going to take all of this out from here. So I just have my precipitation visuals, an empty div waiting to get styled. Then we can jump in.
Oh, actually, before we jump in, I need to pass that information over. So I can come up to here and say that my data, I'm going to call it I'll do this on another line actually so we can see it. We can say data precip is going to be equal to and you just call it whatever in this case, I don't call it whatever I want. I believe it is my precipitation that I didn't spell correctly.
Oh, my dollar sign's on the wrong side. Thank you. There we go. Perfect. So now I've exposed that to the CSS. I've made it available if the CSS wants that information. I could pull that directly in on this class because this is where I'm going to be using it. But I find it really valuable, and we'll see why with some other examples in a minute.
But I find it really valuable to actually come here and do it on the parent. So I'm going to do this on my weather card. It's going to have and you don't even need to make these custom properties, but I find it useful to hook into them. It just makes it a bit easier. The data attribute's kind of long.
So on here, let's just call it preset make it or I'll do preset amount actually because I already have that down below. And that would be my a t t r. And then here, I would put in my data preset. And once again, it's not working yet because I haven't assigned it a type. I said I'd mention types a little bit more.
There are a lot of types. I don't even have a slide to show you all the types because you're going to take a picture of it. You'll never look at it again, and you're going to forget which one you want anyway. There's a whole bunch of them. I would just suggest looking up which one you want. I always forget. But you have number.
You have angle. You have color, custom idents, and a few others. Just if you get lost or you're not sure, I would just suggest looking it up on the MDM. It's the easiest thing to do. But before I go into the types, you can do something else, which is just assign it a unit. I could do l h, and it's now going to be 8.2 l h and 3.6 l h high, which is kind of cool.
This works for any unit whatsoever. Rem or m, it could be a pixel value. As long as it's a number, it's just going to say, oh, I see that's a number. I see that you want it to be this specific unit. So I'm just going to assign it and make it that unit. Makes it a lot easier to work with if you know what unit you want it to be.
Pixels is kind of small. You can also do percentages. It doesn't show too much in the demo. In this case, though, I am going to keep it as a type of number. Don't forget the triangle brackets when you're assigning a type. It feels weird putting triangle brackets in CSS, but it is what it is. Then we get down here.
I can do a quick I'm going do a min function. Min, we can do our precip amount. Then, well, if I want to actually do the math that I was doing in the JavaScript previously, I had that max precipitation, which was 10. If it's a magic number related to styling, the magic number might as well live in the CSS.
So here we can do max precip of 10. So if it's 10 millimeters or above, we get a full bar. So precipitation amount divided by my var of max precip, max precip, and let's put this on a few lines so it keeps it readable. That will be times 100%.
So it becomes a percentage based on Well, you get the idea. And then I did it in a min function just because if ever we got a really rainy day, then we don't want it to overflow. So by being in a min, I can just add the comma 100% here, and it never overflows. And so we've improved the flow because now if I wanted to look at this, I mentioned it earlier about getting lost with a custom property a little bit.
It's like this hidden bit of information. You have to hunt down where it's actually coming from. Now if I have my preset amount, I can go and actually find that where I see this existing here. I see it's coming from a data attribute. Then I know it's coming from the JavaScript. And I can just make the connection a lot easier.
So for me, this is already a really big win. Next. But on top of that, we can assign the types as well. And I think assigning types is a really important thing that helps with debugging and other issues that can potentially come up. So if we go back to this example where I was looking at the column counts, the top one isn't working this time.
I go, darn, why isn't the top one working? Let me go look in my dev tools. And if I get in there and I find the right element, we will see that the number is actually getting crossed off now because I have assigned it a type. Type number, and right away it's saying, well, this one, it doesn't have a type number.
And if I go and take a look, it's because I used a string of three instead. I didn't put the number. So it goes, it's not an actual number. So this is showing up crossed off. And then the column count is actually not defined for that one. So the flow of information is a bit more clear. We can follow the path a bit easier.
And we've built type safety into this, which helps with debugging and other things, which is really, really amazing and awesome. And I'm pretty happy with that. But I think this can then be used in a lot of really good ways. This is another example that I found. This is from Walmart. You can see that Walmart puts tags on all their e commerce things. They're I don't know why, but, you can see on there that their tag has four inline styles, the background, the color, the border radius, the font weight.
And this is probably something that server side rendered in this case. So it's not necessarily about the flow of information because it had to get done by JavaScript, but it's, you know, the servers hitting up the database or a CMS or something to grab some information. It's processing things, doing some logic, and all of its things, and once again ends up applying some inline styles while the CSS just sits there not actually doing anything.
In this case of Walmart, I think it probably looked something like this, probably a React component or something. You had some setup. Those numbers that I've hard coded at the top, they're probably not hard coded. They're probably getting that from a design system somewhere. So they're going and fetching this, and then the developer's inline styling these. What's kind of confusing with their example, they also have a class on the badge that's being used for some styles but not all the styles, which is interesting.
And if you go to different pages to show that they actually have some of the pages, it's a hex. Other pages, it's an RGB value. So that's, I know, not part of this talk. That's just large companies doing things in strange ways. They also have their entire design system exposed through 1,300 custom properties. So those values are also sitting there waiting to be used, not being used, which is fun.
And they're all there. But again, that's big companies with large teams that don't communicate. Another talk maybe. But I really think there's a good argument to be made that we should just be doing something like this. And you could say, well, Kevin, do we even need it to be like this? This could just be a class. That would work. And I think so.
Like, why not just throw that in as a class? It'd probably make life a lot easier. You wouldn't get inconsistencies between pages, which is super strange. And that would all work out really well. But I think this doesn't even really cover the important part of it. I think if we're going to be exposing this information, we should be taking a step back at the entire card, like I looked a little bit at the weather card, and exposing as much information as we can be.
So if we know what the promotion is, if we know what the price is, the stock, the rating, all of this can be passed through as data attributes that CSS can then choose to use or not use if it could be useful for some of the styling. So here's a little e commerce shop that I've set up.
We have some tags and other things. And in this case, I'm not going to look at the tags. That one's fairly straightforward. But I'm gonna I stole this I don't know if, Yuna is here, but I stole this demo from her blog post. These slides are all available after, so you can get a link. I did leave it as a comment here.
It's a nice one that looks at a few different use cases of the advanced adder function. And she looked at doing a star rating. So I've already set this up to have all my data attributes coming through. And then I'm already grabbing those data attributes in my CSS. So we're not too worried. The JavaScript that we'll get to in a second.
But we're grabbing the ones that we want to be using, and then I can go in my star rating and I can just style it. So I could do calc of my whoops. We want that to be a var rating. And I'm going to say divide by five times 100%. Yeah. There we go. We get a star rating coming in.
I'm doing that just with a background linear gradient. So that's controlling the color stops. So if I need like 80 if it's an 80%, it goes up to 80% on the gradient. And there's a mask set up to give me the star shape. The only thing that's a little bit annoying with this, and you might need half ratings.
Right? Like right now, the first one is 4.2, and you get like this little piece of the star being highlighted. It's a little bit awkward. But luckily, we can just come here and say this should be round instead. And we can round that by 10%. And now it'll be half star ratings instead the whole way down. So that makes, you know, a bit more consistent and and no issues that are coming there.
But this is sort of like some basic styling we can do. I said that we should be exposing as much information as possible. And so why don't we expose or I did expose other things. We can use those as well. A little bit lower down or I have this sorry. We have a sort by that's right here.
I might as well take advantage of that. And I'll talk more about this in a second. But you can see I'm actually using a little bit of JavaScript here. You could do this CSS only if you really wanted to. And again, I think Sarah had mentioned yesterday there's issues with selects if they're not really in a form necessarily, but we're going to be doing it this way for today.
Where I am using I'm just setting a data attribute on the parent or based on the value from my select menu. And I'm doing this because I want to look at view transitions as well. Bramis inspired me yesterday. So and as I said, if you don't need the view transitions, you could even do this CSS only sort of a checkbox hack version of it if you really wanted to.
And what I could actually do here is the order of these. And if I'm doing the order, this could be don't do this in production today. But I do have the reading flow just above, so I'll show what that does in just a second. But on my order, I could get a calc. I am going to do calc of the var of the rating that we had. And I'm actually going to say times 10.
And the reason I'm saying times 10 is my ratings are like 4.2, 3.1, whatever it is. The decimal place, it won't work in the order. So times 10, it gives me twenty, thirty, five, forty two, whatever it is. And now I believe that should work. We'll see. Sometimes when I do this there we go. The only problem is we have the worst product is coming up first, which is probably the opposite.
So we just make sure that this is a negative 10, and now it will actually sort it the way around that you want. So that's a little bit better. And again, I'm doing this using the reading flow that's there, which is a new feature, not well supported. But what that means is if I'm using the order and I'm rearranging things on the page out of order from the DOM, I can actually tab through.
I should have made this a little bit more obvious. I apologize. But we can see as I'm going through, even though I've reordered it with CSS, the reading flow is actually following or my tab order is following the rearrangement of content, which is pretty neat. And there's a lot that you can look into for reading flow if you're interested in that.
I've also already set things up down here for the price sorting. And in this case, I just did it with the data attribute. I hadn't made a custom property. I mentioned you don't have to have custom properties. I just find this a little bit more difficult to look at. It makes reading things a little easier when they are.
But that means if I come high to low or low to high, it also works, which is fun. View transitions. I threw this in just because Brahmas was talking about it yesterday. And maybe some of you were inspired. You were making some demos last night playing around with what you learned. Hope you were. But you might have run into the issue with view transitions where you have to give everything a name.
That sucks. It's the worst. With this, this is a bit off topic from what I want to do, but view transition name, you can now do a data attribute. As long as you have an ID on everything that you want to do or a different something that's unique for each one of them. So often, you'd probably use an ID.
Whoops. You'd probably use an ID. And you'd be completely fine to do it this way. I actually called it the data ID just because I was putting data attributes on everything. So I have a data ID. And if you do that, you want to do the type. I said I'd mention it. It is a custom ident. Oops, wrong thing right there.
Custom ident. I wouldn't suggest it for sorting things like I'm doing here necessarily for e commerce. But if I did that correctly, which I didn't So you used that to add to the Oh, thank you. ATTR. There we go. Now, yeah, we get a nice little fun flippy transition instead.
Again, you probably don't want that on an e commerce shop, but maybe for your portfolio or something, it could be interesting. So yeah, that's a nice little win for a few transitions that I wanted to include in there. But up until now, I've just been looking at the data attribute.
And we've been doing a lot with it, passing the information, trying to make the flow of information a lot more clear. And then we've just been processing the data there, doing some math on it, which is already amazing. Like, it's super cool that we can do things like that. But it doesn't completely replace what we would be doing with JavaScript a lot of the time.
When we're using JavaScript, we're doing things where you have to decide, like in this situation, do this thing. In this other situation, do something else. And now with CSS, we have the capability or the ability to start doing that with two new features. The first feature is one two features that can help. The first one is If.
You haven't well, it's only in Chromium right now, so it is a bit limited. But this is going to be a really nice one. I'm very excited for if. I'm happy that we have it in CSS now. If you haven't seen it before, it looks something like this. You have an if statement or an if function with a condition.
And if that condition is met, it will apply a value. Usually, you won't only have one. You'll have a whole bunch of if conditions to finally get to something. And if none of those conditions are met, then you'll have your else value that comes in. Some actual situations where you might use this. If you have a one liner for a media query, you could do that directly on your flex direction where you change it.
If the media is a certain size, then you can switch from row or else it will be a column. We can do this with feature queries as well. So if supports display grid lanes, we get our grid lanes. If not, you have your fallback of a display grid. What's most relevant for what I'm talking about today would be doing it with style queries.
So if we have a certain style, give me a specific value. And we can read the values of the custom properties to get anything that we want along the way. So let's go back to our weather. You might have noticed all of a sudden, Wednesday got really cold. It's no longer raining. It's actually snowing. We're not really telling our users that it's snowing.
We have the blue bar filling up. It's not the best indication. They have to pay too much attention, so it probably makes sense to actually change the color of that. Now we shouldn't only use color, but we'll use it for now and then we'll add to that. So what I could do is on my rainfall visual, instead of just saying that the color is rain, let's get rid of that.
And we're going to say if. My suggestion to you, because every time I use if, I get confused with where things should open and close, you have nested, semicolons, and it can get a little bit funky. So do your if, then just go on a new line and put every single one of the statements on its own line, and it becomes very easy to read and much easier not to make mistakes. I want a style query, so I do a style.
We want to look for, in this case, I'm grabbing both the amount of precipitation, which I misspelled on this one. I thought I fixed it in all my slides, but I missed this demo. But I want to get my style of weather. And we can say, is the weather snow? If the weather is snow, the background color should be my color of snow. And we can see the first one changed over.
And now I can just say else because in this, we only have rain or snow. So I get to say else, We're going to do our color of rain. And the two colors come through that way. So a nice way, again, to let CSS start deciding on what should be happening instead of JavaScript making these types of decisions for us. But, this is one of those things where we shouldn't only be using color to convey information to our users.
Right? Usually on a weather app, you have icons and other things that can also help out. And I found out the hard way when prepping this demo and went into panic mode about two days before I got on the plane. Luckily, Bramis pointed me in the right direction for this one. But the adder function has some rules around URLs.
And this is directly in the spec. I'm not going to read the whole thing. It's something. But it's basically about preventing people from doing bad things, that we don't want to be passing certain types of information through adder functions. I actually knew about this when I ran into the bug. The part that I circled, though, was the part that was actually tripping me up, which says, to guard against this, the values produced by an adder function are considered adder function tainted.
And the red part, the important part, as are functions that contain an adder tainted value. Sounds kind of weird. In trying to debug and understand what was going on, I made this demo that's right here where we have three divs. The div on the top is well, we have example one, example two, and example three is the control, making sure that everything I'm doing isn't actually broken.
You can see example one, I'm putting a custom property of test one. The second one, I'm doing a custom property of test. But in this one, I'm actually getting the number one from the data attribute up here. And then three doesn't have anything. So that's why it's the control. If we take a look at the CSS on this one, we'll start with the border because the border is working as intended.
So we're saying, if test is one, give me a dashed orange red border. It's working on the first one. It's working on the second one. And our control at the bottom is doing the else statement just as we'd expect it to. So the borders are working fine, but the images aren't. There's a weird thing in the middle one, and that's coming from here where if the style test one used this background image, else set a gradient. First one's working because it just has a custom property. Third one's doing the gradient because it doesn't have anything.
The middle one, if you go and look in the dev tools, it actually says that the value of the image is none. And I was like, what am I doing wrong here? But this is part of that loop of the whole idea of an add or tainted value. The value isn't being set by the custom property or by the data attribute coming through in there.
But the end result of the whole, you know, doing the style query to then set that based on that data function is meaning that it won't accept the URL there. And I just don't want any of you to run into this situation because it can be a little bit hard to figure out. So I wanted to mention it.
Be very careful with your URLs with data attributes or with the adder function. Luckily, there's a way around this using the other feature that I wanted to talk about today, which is container style queries, which are now baseline newly available, which is awesome. All the browsers now support this. Yeah. A hand to all the developer or all the browser engineers, because this is super exciting.
There's a lot we can do with this. I could do a whole talk on it. We're just going look at a few quick things today. But we can come back to here with this to bring in our icons. And you can see I have icons that are set up here. Then if we go down a little bit further, what I ideally wanted to do is something like this with an if statement. But as we just saw, that doesn't actually work.
This would have been cool if I could do that, just set all of them on one spot. But I can't do that. As I said, I can do it with a container style query. So I should have done this as one big comment. Did each one in its own. But I can just do a style query for each one of those instead and bring in the icons that way.
And let's on break the demo. There we go. So if it's raining, our background image is rain. If it's partly cloudy, we get the partly cloudy icon and so on. There's a few important caveats to what I'm doing here and a few distinctions I just want to make between if and style queries and another thing on how I'm doing this because it may be not the best, but we'll talk about that in a second.
The differences between style queries and if that I don't really have a lot in my demos but I wanted to make sure that I talked about. Style queries can't query the element itself. So in the example that I have there, have a card setting a class of or the theme, a custom property of theme. The card itself, you can't do an at container to look for that on the card itself.
It's only going to work on the children or the descendants of that card. So really, really important. I think most people here at least would be familiar with this, doing it with any type of container query where you have that restriction. One of the things that's so great about if statements, though, is with if, they can actually see the custom property on that selector, which can be really powerful.
But there is the limitation with if that you're doing it line by line, whereas with a container query, you can do an entire declaration block, as many property values as you want. If is always returning just a single value every single time, so it can lead to a bit more verbose depending on what you're doing. Just pick and choose.
Well, right now, you'd probably pick a style query because browser support's better. But once we get there, then you pick and choose depending on the situation that you're in. The other thing some of you might have been wondering about or looking at that last demo, you might be going, Kevin's showing us this really cool thing we can do with this advanced feature that's not even well supported yet.
Couldn't we just do this instead? And in the example I was showing, you could have. I'm really good at finding these new things and going down these rabbit holes and then being like, this is the best thing. The amount of stuff I've done with has where I'm like, oh, this is amazing. And then someone looks at it and they're like, couldn't you have just done this really simple selector instead?
I'm like, oh, yeah. I could have. So don't fall down. Just because we can do it in a a not cooler way maybe, it doesn't mean that we should. Attribute selectors have been around forever. We can still pass that through with JavaScript and set things up that way, and it would work perfectly fine. That would be, if this was the end of the talk, it'd be a bit of a bummer.
But there's a really special ability that if and style queries both have now. And that special ability is the range syntax. Range syntax is amazing. So it's currently only in Chromium, including on the container style queries. So style queries are or the container style queries are supported in all browsers, but what we're about to look at would only be Chromium, so just so you know.
We'll go back to this example here because I do have time. I wasn't sure if I'd have to skip this one or not, but we have time for this one where I've set up my ecommerce shop once again, and we'll look at how we can do this with an if statement. So if I come down, we have all my products.
We've set up my sorting, and I skipped the availability sorting earlier because Because the availability sorting, we'd have to do this one a little bit different. We want to use our order once again. So say I come on the order. And in this case, I think that I did it as do we have our we have stock.
So my data stock's being put in as a custom property of stock. And then we can come down here. So I can say my order is my var of stock. And this should actually work just like that. So if I sort by availability, it works. But the problem is the ones that are sold out end up first because they have zero stock.
That sucks. We want those to be at the end. But if I'm doing it by availability, or I guess it depends how you're doing it, but you might want items that are almost sold out to be the first one that people see. So here, what I could say is, whoops, if and I guess we don't really need the range for this one, but it still works. If the stock is less, not less, if it's, let's do greater than zero. If whoops.
See, this is where you screw up with, I'm gonna delete everything and start my if statement again because I was getting myself confused. So if open close, go on a new line, bring in your style query or whatever you are querying for, feature query or media query. Style of stock is greater than zero, then we could just say that this will be my var of stock.
Else, just spam nines on there like we do with our zed index. And now the availability will always push. You know, it'd be the the things being sold out first or almost sold out. And then the further down you go and the ones all the way at the end, end up getting pushed down to the end. So people aren't worried about using those.
Maybe not the best example, but I still like it. We can also play around a little bit with our custom or sorry, the well, we're using the images here. So we're gonna be using our style queries. And in this case, my broken. And the reason the rain is broken is we've changed some of the icons. We now have icons for heavy rain or not a lot of rain. So let's go and fix that.
We'll find where it's raining. And this is another thing you can do with container style queries is we can chain different conditions together. So I can say and if the style of I think I called it precip spelled like that this time. If the precip is greater than, let's say, five millimeters, the icon will change to a heavy rain.
And then I know my layout is broken. We have overflow, but, we get the icon coming in. And we can take the same thing and do it for light rain. Copy that. Paste it in. And if it's less than or equal to, because we want to make sure there's not a missing decimal in there anywhere, keep the five there. And we could change that one to rain light.
And let's just fix the icons. Come over to here. And maybe now the first day, it's only raining 1.2. So we have less raindrops. We go back to a rainy day. We get more raindrops coming in. And you could do more than just that. We could also check for other conditions. So maybe the API, it probably will tell you if it's partly raining, partly cloudy, all this information.
But let's say our API is not very good, and it's only telling us it's partly cloudy. But we can see it's partly cloudy and raining, so we want to update that icon as well. So I come here. Whoops. Let's do the end on this line because it'd be easier to read. And style precip is greater than zero.
So as long as it rains at all, then we can do part rain and have a rain cloud coming in, a bit of rain, a bit of what we need to sort of indicate what's actually happening on that day as well. And these are fairly simple examples that I'm showing you. I just want to show you what's actually possible with these.
There's obviously a lot more that we can do with them as well. But I think in doing this, we can see that we've sort of improved the flow of what we're doing because JavaScript's still responsible for fetching the data, getting the data. But then it can take the relevant pieces that would be useful and pass those off to CSS.
Now CSS can get them. If we're doing it today, we might be getting this and applying these as custom properties. And that's going to work fantastic. It's going to work really well. But in the future, we're going be able to get them and then add type to them, which is a good thing. Having type values, as we saw, can help with debugging, help with the information flow. And then we're to do all of our styling and all of our logic over in the CSS using things like if statements and container style queries and using our math functions that some people don't even think should be in CSS to be able to do these things.
And for me, this is you know, my talk was CSS is eating JavaScript. I But don't think it has anything to do with CSS actually eating JavaScript. I think it's a JavaScript was overstepping its bounds maybe a little before through necessity because we didn't have these. This is more about CSS starting to grow into its own and starting to do what it can do.
And I think that anytime we can apply styling, whether the you know, it doesn't matter if it's logic. Logic shouldn't be living in JavaScript. Logic should be living where it's needed. If it's needed for some JavaScript stuff, it can live over there. And if it's needed for styling things, then all of that logic should live where the styling is actually happening.
Thank you very much.
Thank you. Would you join me? This one. There's our fancy camera. It's just over there.
Perfect.
I was gonna call this the round table, but then I've only just realized right now it's not round.
So I'm just
gonna stick with the table. We've got a few questions for you. That was a great talk.
Thank you
so much, and great way to start the day. So first question, we've got time for a couple. Khalil asks, are there any security issues to be considered with the advanced attribute, ATTR?
The old I as far as I know, no, because they thought of that. You know, they're I think in the old days, probably, could have been, but these days, I think the working group realizes when those security risks are there. That's why I ran into that image with the issue, which even shows how much you're thinking about it because it wasn't getting it directly from the data attribute.
It's getting it from it's seeing, like, the whole chain that's getting there to eventually assign a URL, it's even blocking that. So I think we should be pretty safe.
Nice. Right. Sid. Who is Sid? I don't know who that is. Just a random Sid. Is there an advantage for using attribute ID over the match element for the VT name, probably for cross document view transitions?
I don't know. Problems.
Fair enough. I don't know. We can
talk about later. Yeah. Sorry.
Hang out. Go hang out and chat. We've got time for a couple of more. Let me have a look. This question doesn't have a name attached to it. For the rain snow, wasn't it easier and cleaner to use cascade and an attribute selector to assign colors, like you mentioned at the end?
In that case, because it was zero oh, we're just doing the weather? Yeah. You probably could have just done an attribute selector with like, if it's snowing or if it's not, that would most probably work. That's where maybe I was having too much fun.
I hope I'm pronouncing this correctly. Homan Homan Homan Wonders. Not directly related, but you mentioned they have their design systems exposed. Any suggestions on how to hide it?
Do you need to hide it? I don't know. I'm I'm I come from the view source generation where it's like, want every I think it's cool that you can see everything that's there. I think the main concern they might have is potential performance issues because they had that wasn't just the design system parts they were going be using. I actually saw every layer of the design system, which you probably don't need as custom properties.
So maybe something like that's a little bit overboard, which would just come down to however you're engineering things. But it was like 1,600 custom props with and it even said in the name of each one. But yeah, I don't think the design system values like, even if you try and abstract it away, you're still gonna find the value somewhere in there. So I don't know if it's worth the headache.
Maybe that's just me, though. I don't know.
That's a great segue because the next question is, at what point do we need to worry about the runtime performance of all this new CSS computation?
That's a good question. I do think for a lot of it too, like, if I'm path like, say we're doing some of these calculations in JavaScript, you'd have to compare what's happening. Like, if you're looking at the weather app, especially coming in with the height there, it's going to the API, fetching that data, bringing it back, and then JavaScript's having to go, Okay, here's the information.
I have to measure the height of the div I'm filling up, figure out how tall to make that. They didn't do a percentage. I don't know why they figured it out in pixels then to so that's all happening through JavaScript. So that will you know, it has to happen anyway. I would assume that that's probably more performant if you just provide the raw data to CSS.
It would definitely be worth testing, though. I I haven't measured anything with performance, and it's also brand new. So even if it's a little bit slow now, like even attribute selectors, there was all those articles and Everything Forever saying attribute selectors are slower. But modern browsers these days, the selector performance is amazing. So it's not something you start worrying about.
But if it's something you're thinking about trying, I would suggest testing just to see if there is an impact.
Right. One last question. I believe this question was put in before you reached this that bit of the slide, but in case anyone else missed it. From CJ, is it possible to use the the type string instead of number as well as in the attribute?
Sorry. Say that again. Is
it possible to use the type string instead of number as well in the
Oh, yes. Yeah. Yeah. I think there's string. And, like, I did the custom ident at one point. The custom ident or actually, if you want it as a string, you just don't have to even assign a type. It's going to treat it as a string. I'm 90% sure on that. It just that's what it is as a default value.
And then if you'd need a different type, that's where you can do it. I use the custom ident for the view transition names just because then it comes without the quotes around it. It probably can be useful for other purposes as well. But with the view transition name, you just then don't have to treat it as a string.
You can write the words in there. So it depend it's a little bit different from a string. If you wanna know the difference between a string and a custom ident, go ask someone who works at the browsers and not me. They would have a better answer.
Yeah. They're around. Yeah. Right. That's all the questions we've got. But a few weeks ago, Kevin wrote a blog post called Reach Out and Say Thanks, which was a lovely blog post. And go ahead and read it. The human summary of it is, if someone shares educational and fun content you appreciate and makes you smile, please tell them thank you.
And I really want to encourage you to go to our speakers today during the breaks and just say a quick thanks if you enjoyed it, and I promise you'll make their day. So please do go ask Kevin everything you didn't ask online. I'm sure you'll be more than happy to chat, and don't feel discouraged to approach our speakers throughout the day and the breaks.
It'll be really lovely to see you all chat, which leads me to our break. We are going to have a break now. Thank you so much. Give a round of applause for Kevin.
Thank you.
People
- Bramus
- Manuel
- Temani Afif
- Yuna
Technologies & Tools
- ATTR function
- Attribute selectors
- Checkbox hack
- Chromium
- Container style queries
- CSS anchor positioning
- CSS custom properties
- CSS Grid
- CSS if()
- CSS math functions
- CSS min()
- CSS reading-flow
- CSS round()
- CSS trig functions
- Firefox
- Range syntax
- React
- Registered custom properties
- Safari Technical Preview
- Scroll-driven animations
- View transitions
Standards & Specs
- CSS custom-ident
- Data attributes
Concepts & Methods
- Graph theory
- Separation of concerns
Organisations & Products
- MDN
- The Weather Network
- Walmart
Advanced attr(), style queries, and if() let CSS read and interpret values
that used to require JavaScript. Kevin Powell explores how these features
shift responsibilities away from JavaScript while keeping styling and
behaviour cleanly separated.















