A Love Letter to CSS Variables
Introduction to Daniel Banks
Nathan Curtis introduces Daniel Banks, highlighting his role at AWS Amplify UI and his significant contributions to design tokens and the CSS community.
Daniel's Love for CSS
Daniel shares his deep affection for CSS, tracing back to its early influence on his career and personal life.
Early Experiences with CSS
Reflecting on his initial experiences with CSS, Daniel recalls the impact of CSS Zen Garden and its role in his understanding of CSS's potential.
The Evolution of CSS with Sass
Daniel discusses the evolution of CSS with the introduction of Sass, emphasizing its capabilities in clean and maintainable style writing.
Impact of Twitter Bootstrap
He highlights the significant role of Twitter Bootstrap in the development of UI systems and libraries, and its influence on CSS as an API.
Emergence of CSS Variables
Daniel explores the first draft specification for CSS variables and their gradual adoption in the industry.
Deep Dive into CSS Variables
An in-depth look at the implementation details of CSS variables, focusing on inheritance, resolution, and their practical applications.
Understanding CSS Variables as Runtime Macros
Exploring CSS variables as runtime macros, Daniel discusses their flexibility and limitations in various CSS contexts.
CSS as an API Contract
Daniel stresses the importance of treating CSS as an API contract, ensuring reliable and customizable designs for end users.
Integrating CSS Variables in Development
Discussing the integration of CSS variables in development, Daniel highlights their role in creating a more dynamic and accessible user experience.
Reflections and Acknowledgements
Daniel concludes with reflections on the evolution of CSS and acknowledges the contributions of various individuals in the field.
Q&A Session with Nathan Curtis
A Q&A session where Daniel addresses questions about the adoption of CSS variables and their future in design systems.
Nathan Curtis: All right, everybody, welcome back.
I hope you're ready for a Clarity afternoon of day one.
Our first speaker.
Yes!
Let's feel it.
Let's get the energy up.
Love it.
So we've got a return speaker, who may need no introduction for many of you, but Danny Banks spoke at the 2020, Clarity about an introduction to multi platform design and he is actually back today.
Danny leads the AWS Amplify UI team, helping front end and full stack developers build cloud connected UI.
His love for design tokens is apparent in Style Dictionary, which I would assert is the fairly industry standard of how we're translating our design tokens.
It's an open source framework, and he also participates in the W3C Design Tokens Community Group, along with some other colleagues of mine.
I know his contributions have been very significant there.
Also, he has two fluffy cats.
As well as two human children.
Please welcome Danny Banks.
Daniel Banks: Can you guys hear me?
Okay.
Cool.
All right.
this is something that's been brewing in my mind for a while, and I really wanted to get it out into a talk or a blog post or something like that, so I feel like this was the perfect opportunity, for me to get my thoughts out into the world about CSS variables and just really my love for CSS and I love CSS.
It's my favorite programming language.
It's beautiful.
It's elegant.
And, my partner is not in the audience.
CSS is actually my first love.
Long before I met my partner and we started dating and we had cats and kids, even before my first, serious, long term relationship.
There was CSS.
And I want to go back, I want to go back to when I first fell in love with CSS.
And this is very bright now, sorry.
I'm sure some of you older people probably remember CSS Zen Garden.
does anyone remember when CSS Zen Garden first came out?
You.
2007?
Close.
Close.
ding, ding, 2003.
CSS ZenGuardian is 20 years old.
That's crazy.
And now I feel really old that I've been writing CSS for 20 years.
but this is where I first really fell in love with CSS.
Before this, I was, making PSDs and chopping them up and just writing terrible HTML and CSS.
And CSS Zen Garden really taught me about the cascade and the expressiveness of CSS and properly structuring your HTML content.
And that you can create beautiful designs with semantic markup and semantic CSS.
And the journey doesn't stop there.
Next comes Sass.
Sass, again, I love Sass.
Sass came out in 2006, I believe, so it's very old as well.
I still use Sass, in my day job, Amplify UI uses it, we use SCSS, I don't know if anyone actually still uses this original Sass syntax anymore, but Sass really changed the game.
It unlocked the potential to write clean and maintainable styles with variables and mixins and nesting.
This stuff all came out way before the CSS specification for those exact things.
and it really unlocked you to make broad changes and unlock your creativity to really create much more expressive web designs, and it was just, it was revolutionary.
Next comes Twitter Bootstrap.
Now I know some people may love or hate Twitter Bootstrap, but love it or hate it, this Bootstrap was really what kind of, I would say launched a lot of.
UI systems and UI libraries and component libraries, it might not have been the first, I think there was, some other ones before it, but really, this was the one that kind of set a lot of things in motion.
It came out in 2012?
Or 2013?
and what this really taught me about CSS is that CSS can be an API.
It can be an API for your components, because the API for Twitter Bootstrap was these class names.
And it was also Less and Sass variables as well for customization, but the CSS class names were the API that you would use as well as the Sass or Less variables.
And we're coming full circle now.
There's actually some UI kits, UI frameworks that are basically bootstrap using semantic class names, but built on top of Tailwind.
which is interesting, so it's we're going back to Bootstrap from Tailwind.
it's, I, find it funny.
So the first draft specification for CSS variables came out in 2012.
I didn't fully read this specification, but I did a brief glance over it.
It does not look like what we use today.
but a lot of hard work went into coming up with this specification and, getting browsers on board and, browser support for, CSS variables or custom properties, technically as they're known, really picked up in around 2017, that's when pretty widespread browser support happened.
so we've had CSS variables for about five ish years now.
And when I started writing CSS variables, I just wrote them like this.
you put some CSS variables on the root, whatever the root is, and then you use them in your components or whatever, and this is fine.
but really, all this is doing is changing a build time paradigm, like in Sass, to a runtime one, but it wasn't really leveraging any of the benefits of that.
It was just changing one syntax to another.
So this doesn't really get us to a much better place than what we already had with Sass.
to truly love someone or something, you have to know it inside and out.
You have to truly appreciate all of the flaws and imperfections, and so I want to go into a little bit of the implementation details of CSS variables so that we can really truly understand and know CSS variables.
And the first thing I want to talk about is inheritance and resolution.
So CSS, custom properties or variables, they are inherited properties.
descendant DOM nodes will be, will get inherited those custom properties that you define higher up.
That's why a lot of times people defining CSS variables at, with colon root, because root basically just means the root element of the document, the HTML element.
And so that way they're global variables.
You can use them in any DOM node, because they just inherit all the way down.
Um, another thing you can do with this is you can define CSS variables at any DOM node and they will, any descendant DOM nodes will inherit those properties.
and you can even define them on, pseudo states like hover and active.
And CSS variables can reference other CSS variables.
And to see how these two things, like interact I have a pretty contrived example, but this is something that kind of tripped me up in the past, so I want to really call it out.
So given this HTML structure, and given this CSS, at first I would have thought, okay, my button should be green, right?
at the root div in this example, I'm defining color brand, which is a teal, and color background, which references color brand, and then in my button, I use button background, but then in between those two DOM nodes, I set the color brand to green.
And I would assume that button background would actually become green, because button background references color brand.
But that's not actually how it works, so references get resolved at the DOM node that they are defined in.
So at that root div, button background is now just teal.
It's no longer a reference.
And so when you get to the div class success, if you set color brand, that's great, but button background is already teal, because it's already been resolved, it's already been replaced.
And there are a few ways you can fix this.
One easy way is you can just put, in this example, just put data theme also on that div class success, because now that DOM node has both the CSS declarations up top and the ones down below, so it can resolve color brand, or button background can resolve color brand as green.
And I do have a CodePen, example for this.
I realized with this giant screen, it's going to be very hard to do live coding.
So I'll just skip that for now and move on.
But what I really tend to think of CSS variables as is runtime macros.
And macros are just a computer science word for find and replace.
So you see some string and you replace it with some other string.
And that's really powerful for CSS, but it's also a double edged sword because you can use CSS variables in a lot of places.
You can use them as separate channels in a color.
You can use them as, the percentage of a linear gradient.
You can use them as very specific parts.
But there's actually two places you can't really use CSS variables, and I'll just briefly go over them, just cause it's actually interesting.
You can't use them inside of a media query declaration, and you can't use them inside of a container style query body.
Now, this makes it a little bit confusing, but you can't do at media, min width, and then some CSS variable.
Because at that time, that CSS variable could change, and so the browser doesn't know, if that actually is a specific constant value.
but, you can set them inside of the body of a media query, and generally that's how you do, things like dark mode, is you would set, your colors to be inverted if it's in dark mode or something.
And recently, so there's container queries, which most people know of, as you can do, min width, max width on containers.
You can also do container style queries, and this is saying, if I have a container called my container, and there is this custom property, variation is new, in that DOM element, if it's defined as that exact thing in that DOM element, perform these style updates.
but one thing you can't do, is you can't define that, define other CSS custom properties inside the body.
so that's just one of the things that kind of tripped me up as I was, like, learning about this, and but to really show the, this is showing the double edged sword, or the bad part about this, but I think the good part is you can separate things like hue, lightness, and chroma, I think that's what LCH stands for, into separate parts and then change them independently, so with CSS variables and something like OKLCH, you can create really easy, completely visually uniform palettes by just setting L and C values for, like zero to ten colors, and then just changing the hue.
And they will be visually, perceptually the same lightness, which is different than doing things with HSL, which certain colors, like yellows, are visually brighter even though they're the same saturation and lightness, technically.
and I, haven't used OKLCH much yet, but I really think it's, powerful and I definitely want to start using it.
and another thing that's tripped me up, but I learned how to get around it is you can't transition a variable or a CSS custom property with an asterisk.
Because CSS custom properties by default are just macros, they're just find and replace.
When you see this thing, replace it with this other thing.
the browser doesn't know that, if some variable is 40%, the browser doesn't know that is supposed to be a percentage or that some hex value is supposed to be a color.
So it doesn't know how to interpolate or transition a value from one to the other unless you explicitly tell it to.
And that is where you can use this @ property syntax.
So this lets you type your CSS custom properties or variables and say, hey, this thing is a percentage.
And you can also say that it shouldn't be inherited to ancestor DOM nodes, and that its initial value is 40%, taking over the fallback of CSS variables.
So now CSS knows that this thing is a percentage and now it can interpolate or transition from 40 percent to 100 percent in like a hover effect or something.
so this is super powerful, and something that you should definitely look into if you're gonna be going down using CSS variables.
And what all of this is all these learnings that I've had over using CSS variables for a while.
Really got me to think about our CSS, at least for Amplify UI's case, as an API contract.
We define and use CSS variables as an API contract so that if we change the name or, change the class name of certain components, those are breaking changes for us because we want to make sure that customers can customize and theme confidently and make sure that, they're not breaking down the dumb structure and, and overriding certain things and then we change something and everything breaks.
So we, we think of our, CSS as a contract for our users, our customers.
And you might ask, okay, so how, like, how many of your styles actually use CSS variables?
And I'll say all of them, like literally.
99.
9 percent of all of our CSS declarations, like padding, background color, they're all using CSS custom properties.
It's, whoa, that's very big.
It's all variables.
It's all high, or whatever this meme was.
And I'll show you just really quickly, one of the ways that we're using them and why it's really helped us.
create a nice style API or CSS API for our customers.
so this is a pretty big example, and I could honestly probably talk about this just one piece of code for a while, but I will spare you all of that and just mention a couple things.
So one, we're still using Sass with the ampersand thing.
We're using BEM class names, so we're old school in that way.
But what we're doing here, so we have this tabs component, and you can theme it to have thicker borders.
There's like a border on the bottom, and each tab item has an indicator that's also a border.
And we want the borders to overlap as well.
So we create these internal CSS variables, margin start, margin end, and border width, that can then be, triggered in different variations of the components.
So you can also set the indicator position to top or bottom.
And so when we do that, we need to change the margin start and the margin end and also the border width of our tab list and our tab items themselves.
So this allows us to keep our CSS very maintainable and clean while also giving a nice style API to our customers.
They just need to say, tab width is 5 pixels.
And then if they have a tab where the indicator's on top, they don't need to worry about that, it just happens.
And, so really what this allows us to do is to hide the implementation details, from our customers, but still give them a nice, clean API contract that they can use to style and customize.
And, CSS doesn't need to be the only interface that you give customers to do that.
For better or worse, there are people in the industry that hate CSS, or think that CSS is not real programming, or that CSS is too complex or hard or, too difficult to understand.
And people have gone so far out of their way to not write CSS, they literally just recreate a styling engine in JavaScript.
And, maybe that's not the best thing in the world, but they do have some points on the developer experience, CSS is not a strongly typed language, at least in terms of knowing which variables you have access to and stuff like that.
It's very dynamic.
so what we do is we try to give that same developer experience of using CSS in your JavaScript but just using CSS variables under the hood.
So you can still define like your theme in this object like structure, this is all typed, this all has IntelliSense and stuff like that.
You can, define your tokens, define overrides for dark mode.
But really all this is doing is turning this object into CSS variables, and that's it.
and, but that has a really nice developer experience for people that don't want to write CSS.
And then you can also use these, CSS variables or tokens in your React code as well, again, these are just references to CSS variables, so if their values change based on light mode or dark mode or something, this doesn't get re rendered at least as far as like React can tell, but the browser re renders it or it re paints it because those CSS variables have changed.
And so, this allows developers to start using CSS variables without even really knowing it, and they still get type safety and IntelliSense.
And but really the main benefit is that nothing is faster than the platform, no matter how hard you try to do JavaScript optimizations or make the virtual DOM diffing better, nothing is going to outperform just using the actual browser, or using the actual browser standards.
Changing a CSS variable versus changing an entire DOM structure or DOM node.
using the platform and using the standards are the most performant thing that you can do for your application.
And, really what I want you to take away from this is that CSS shouldn't be an implementation detail, or shouldn't be thought of as an implementation detail, but rather think about it as an API that you're giving to your users.
it's your API not only to the users of your system, like other developers or designers, but also their users, the end users as well.
Using CSS variables, you're betting on the open standards of the web, which, Kaelig talked about earlier today, and the web being more open, we all win.
So you can inspect things and play with them, having CSS variables in your application allows your users to open up DevTools and change a couple values and get a personalized experience, versus if you're using, obfuscated class names and all this type of stuff, they don't know, there's no way to know, like, how can I style something or how can I hack something myself.
and by betting on open standards like CSS specification, frameworks, tools, they come and go, maybe one day React won't be the most popular framework, but CSS will always be there.
It'll always have your back.
And when writing this talk, I kept on coming back to this one quote, and it does echo I think what, Kaelig was talking about earlier today as well, and this is a quote from Marshall McLuhan.
And, we shape our tools, and therefore our tools shape us.
So CSS has shaped the way that I think about how I design websites and web applications.
And that thinking has changed over the years.
CSS Zen Garden taught me that CSS is an expressive language and to really think about semantic markup and semantic CSS.
Saas taught me how to write clean and maintainable styles and reusable styles that were easy to understand and update.
Bootstrap taught me that CSS can be an API, and CSS variables have taught me that the structure of that style API for my components and CSS is not just an implementation detail.
And you can also see, as an industry, in these different eras, how web design, or at least the kind of, the zeitgeist of web design has changed over the years.
in CSS 1, before, CSS Zen Garden, it was, a bunch of text and then some animated GIFs for flavor.
And then, CSS Zen Garden came out, and then we started to get these really beautiful, really extravagant skeuomorphic designs, at least based on semantic HTML and markup.
And then, And then after, once we got CSS3 with like border radius and box shadow, we toned it down a little bit.
And now I like to think that we're in our CSS variables era, building much more interactive, accessible, and personalized experiences.
So we are evolving and maturing as an industry, and I think CSS variables is one of those key pieces to that.
Um, and I've only really just scratched the surface, like I could talk for hours about this and go through code examples and stuff like that, but I only have 30 minutes, and before I let you go, these are some amazing people that produce a lot of excellent content about CSS and new techniques and new features of the CSS specification, and so most of what I've learned have been from these three people.
and also I just generally want to give my deepest gratification to all the people that work on the CSS specification, all the people that work on Sass, these are the things that have really changed our industry and changed how we all think about designing and building interfaces for our users.
With that, I want to say thank you for listening to me ramble about CSS, because I love it.
Nathan Curtis: Thanks, Danny.
Yep.
You opened with examples from past eras, and you ended by talking about almost naming some of those eras and saying we're in the custom variables era.
Are there people, I don't sense all the teams I work with are using custom variables, like when they're writing CSS.
In fact, many of them aren't.
Are we in different simultaneous eras and some people are using them and when do you think this era is going to transition into something new?
Daniel Banks: Yeah, I mean I think we're like right at the kind of cusp of it.
So it's, I think we're just starting to gain really widespread adoption of CSS custom properties and the whole industry obviously hasn't made that change yet, similarly, a lot of teams still don't use like design tokens in their design system, but I think it's definitely starting to shift, if you open up most, GitHub or Asana or, Slack in the web browser, all of those use CSS custom properties like throughout.
So I think we're just on the like beginning part of this era and I think, we'll start to see a lot more interesting and, experimental ways of using, CSS custom properties and defining entire color palettes with, a couple variables.
Nathan Curtis: One of the things I saw that I was surprised by was you had, one or two examples of a code editor where every single rule has a variable.
And oftentimes the advice I find myself giving people drifting into tokens and, essentially describing the choices they have is describe the choices in tokens that you need to and don't prematurely optimize to tokenize everything.
And first, was that really an example where you're trying to achieve complete themability and is that why everything's described?
And are there instances where you're only creating variables to associate them when you need them?
Daniel Banks: Yeah, that, that's a great question.
I think, yeah, for our case, we want themability to be the number one thing, because we're an open source UI library that's meant to be used to build anything versus a design system for a specific company.
And I think you're right, if you're building a design system for a company, being explicit in where you want those, where you want that API basically, because, you should treat that as an API, you can change or reference these things.
And really thinking about how you want that API to work.
For us, it was, we need people to have all of those knobs and levers to be able to really fine tune every single thing.
And there are some instances, a flex component where it displays flex, come on, you don't need to have a CSS variable for that.
But other than that, it's everything that we do, we've really tried to think about the style API so that people can really make everything their own.
Nathan Curtis: You contributed to an era we're all in when you made Style Dictionary, which is a tool that helps us all translate our design tokens across a range of formats.
And you showed an example where you're creating another translation tool so the developer experience can be easier and familiar for the way they wanted it, or the way they're most comfortable.
How do you make a choice around when it's worth it to make and maintain and deal with the consequences having that translation tool out there in your ecosystem, like what will influence you to say it's time to make this tool versus it's not worth it?
Daniel Banks: Yeah, that's actually, yeah, so in, in actually our code base, we use the internal pieces of style dictionary without like explicitly saying Hey, you're using Style Dictionary.
because like we made a conscious decision that like, we don't want to, we want to be very, so, really un opinionated in how you build your application, so you could use style dictionary to define your tokens and then pass them to this, create theme function or whatever, if you wanted to.
But we didn't want to, force everyone into that decision, because, we didn't think that was, like, the right thing to do because, people may not want that.
and so really we wanted to give people the power of choice.
You can also completely not use that either and just use all CSS variables and just define them all in the root or something and that is completely valid and completely works.
So I think it's really like what choice did we want to give people and how much control did we want to give them and for our case, it's a lot.
for other people, it might be not a lot at all, and you might want to rein in that control.
Nathan Curtis: Has there ever been an instance when you've created a tool like that, and you've regretted it?
It hasn't been worth it, or actually led to more confusion than clarity?
Daniel Banks: yes.
Nathan Curtis: Can you talk about it?
Daniel Banks: Yes.
so I created another open source library called Sketch Constructor.
And this was when Sketch kind of first came out with like their open, AP, or their open kind of like JSON specification.
And so I created this library and you could create Sketch files just in JavaScript.
And it would create, the underlying JSON and you can just, build Sketch files programmatically.
Which was cool and awesome, but it was a lot of work.
I honestly haven't checked in any code for that in multiple years.
and so it's just slowly gathered dust.
I think the one thing that's really difficult is, funding for open source projects.
Because, so technically, so technically Amazon owns Style Dictionary.
Or at least, if I leave Amazon, I don't necessarily keep my maintainership, but also I'm not paid, or that's not part of my job description to maintain style dictionary, so it's this kind of weird, thing where it's not really funded, but I still want to make it and maintain it for the community, and it's Amazon doesn't really get any benefit from it, they're not making money from it, so why would they pay me to do it?
So it's, I don't know, open source funding is very difficult, I would say, and that's the thing that I've realized is.
If there's not like a business model tied to it, it's really difficult and, people's lives get in the way, I get busy, luckily, for Style Dictionary's sake, people from the Token Studio, reached out and they are now co maintainers and working on the next major version of Style Dictionary, with my guidance and stuff, but they're writing most of the code, Luckily there's really great communities that really help, but it is a very difficult problem is open source funding and finding the time.
Nathan Curtis: thank you for solving it for us.
How do you document the API for things like this?
To the models you create and so on, is it just you have good code editing and IntelliSense and so on, or do you do more to communicate and document how it works?
Daniel Banks: We do a lot of auto generated API documentation, so like, all of our, class names and stuff like that are all defined in, a JavaScript or TypeScript file that then we generate documentation for and same, we actually, create our theme, type shapes and, default theme and stuff like that so that, we can just, for this component, here are all, here's the shape of the theme and also here's all of the CSS custom properties like that are based on that, because it is really just a very simple translation of tabs, border width, is just the CSS variable, tabs, border width.
And we consciously made that decision to make it, easily create, easily, allow it to easily create programmatic API documentation.
So that in our docs we say, here's all the CSS variables, here's all the CSS classes.
And these are coming from, our code, and if we change them, it's gonna be a breaking change.
Nathan Curtis: The last question I have is, zooming out a bit, if you were to give advice to design system teams that haven't yet embraced CSS variables, custom properties, what would be your main pitch to get them to convert, and what would be a condition where you'd say, yeah, it's not worth it?
Daniel Banks: I think the main thing, from, at least from my side, is, having much more maintainable and readable CSS code, especially if you are writing, CSS, and not just, CSS in JS, JavaScript stuff.
I would say probably if you have, if you don't necessarily have a need for really dynamic theming capabilities, it's not, your end users might not have as much benefit.
So it's more of an internal, like engineering excellence type thing.
So I'd say if you have, if you want more dynamic and expressive experiences, then it's definitely time to go in on CSS variables.
But if you don't have that requirement, it is more of a, internal excellence type thing and less of an outward, benefit to your end customers.
Nathan Curtis: Awesome.
Thank you so much.
Everybody please join me in giving a hand to Danny Banks.