Theming Overview0:00
The way theming is set up in chat. C and UI is actually pretty simple, but it's extremely powerful. Let's take a look Just to be super clear and make sure we all on the same page. What I mean by theming is the ability to change the way the UI looks without anything changing in the markup. So the class names remain the same.
anything changing in the markup. So the class names remain the same. And here you have an example of theming in chat and UI and you can see that you can choose a different color. Of course, this is sort of the basis of theming, but you can also change the border radius. So if I go 0, everything will have hard edges. You can see the button here, the button here, and so on. And then I can go the opposite way with very rounded UIs. Typically tailwind classes are named
Semantic vs Presentational Classes0:41
And then I can go the opposite way with very rounded UIs. Typically tailwind classes are named after what they look like. And so that's what's called presentational class names, and it's not really teamable by default. So to achieve this change the style without changing classes, we need to switch to what's called semantic class name, where the classes are named after their semantic meaning rather
where the classes are named after their semantic meaning rather than what they look like. Let's take a look at the app.css file, which remember was mostly generated by ui. And you will find here what you can think of as design tokens for border-radius. So you can see small, medium, large and extra large. And then these color names that as I was mentioning are using semantic naming.
And then these color names that as I was mentioning are using semantic naming. So we have colorBackground or colorForeground, which shows the intent of coloring well, the background or the foreground. And then colorCard, cardForeground, primary, secondary, muted, accent and so on. These are semantic color names. In contrast with something like blue600 or indigo400, they allow to create a separation.
In contrast with something like blue 600 or indigo 400, they allow to create a separation between the color intent and the color visuals, which is critical and necessary to be able to tothe these colors without it becoming super confusing. Because if you had a red 500 colors, which turned out to be green or blue in a different theme, it would be super, super weird. Alright, so you can think of these color name spaces.
CSS Variables and Scopes1:59
it would be super, super weird. Alright, so you can think of these color name spaces inside the theme block as your design tokens. These are the values that we want to make available to the system. So all of these, and then the themes come later. So this is where in the root scope. So as a default baseline, we set values to all of these for the border-radius and then all the semantic colors that we've seen,
for the border radius and then all the semantic colors that we've seen, these are the default colors in the default state. And then if I scroll down, you can see that there is a dark scope here. And in the dark scope, all the colors are redefined to handle dark mode that way. And that's basically all there is to theming. You have a root scope with a set of color definition, and then these colors are redefined within a narrower scope.
You have a root scope with a set of color definitions, and then these colors are redefined within a narrower scope. And this is your theme scope. So here we have the default and the dark mode, but then we can create new themes like the Swiss theme where everything is red and white. And this would be a scope determined by a selector. So something like data-theme="Swiss" or a Swiss class. We basically need a way to opt in a theme for a specific part of DOM tree.
Dark Mode Example3:05
We basically need a way to opt in a theme for a specific part of dorm tree and then it'll trickle down all the way to the lymph nodes because of the way CSS variables are redefined in the scope. Okay, enough with the theory, let's take a look at a concrete example. And so I'm going to replace what we had in the previous lesson with a bit of markup that I will comment on. So I'm importing the Button component from SHA N ui.
that I will comment on. So I'm importing the Button component from ShanUI. This is not my button, but the one from Shan. And then I have two identical blocks of markup that show two buttons. Very simple stuff. And that's what it looks like in the browser. Now note that these two blocks of markup have the bg background class. If we look in app.css,
of markup have the bg-background class. If we look in app.css, the bg-background at the root scope is set to be this white color. And so when we use bg-background, it's going to use this background color. Now let's take a look inside the dark scope here. What happens? And you can see that background is really fine to something that looks almost completely black. And because if I go to the top of the file,
to something that looks almost completely black. And because if I go to the top of the file, there is this dark variance that check if there is a dark class before the element I can on the second block of markup, remember it's exactly the same, add the class dark. And by just changing this check what happens. Aha. Now a second element here has the BG background, which is almost black. And our buttons have taken the dark variants of those.
which is almost black. And our buttons have taken the dark variants of those and you probably guessed it. But the reason these buttons look different in the second section is because they also use semantic color names for the background and text colors. Let's look at the button components. Alright, so I will go inspect the Button component here. And if we look in the CVA style variance, we can see
Alright, so I will go inspect the Button component here. And if we look in the CVA style variance, we can see that the default variant has a BG of primary text, primary foreground, and hover BG primary 90. So just for kicks in app.css, let's go find the primary background color. And there it is. And let's change this to tomato, which is a CSS color that looks like this. So now have a think what is the default button going
that looks like this. So now have a think what is the default button going to look like in the browser, both for the default and the dark variance? There you go. Well done. I know you've got this right? The default button looks tomato on the default version, but in the dark mode it still has its custom dark color because we have changed the value for the default or light theme, but the primary color for dark mode is still set to
Updating Custom Button5:28
or light theme, but the primary color for dark mode is still set to what it's overridden to by default. Okay, we're talking about dark mode here, but the way you support multiple color themes is the exact same. So what I suggest we do is we take our, my Button component we've built in the previous lesson and then we try to make it support this dark mode first and then try to add a new theme to it.
and then we try to make it support this dark mode first and then try to add a new theme to it. Okay, back in, welcome the TSX. After the two chat and UI buttons, I will use my button and I'll duplicate this and place the other one in the dark section. Alright, and I keep forgetting that we had this compound variance to have uppercase text. I might remove that now, but before I do, let's add another button.
I might remove that now, but before I do, let's add another button. And what variance did we have? I'm not even sure we had it called look, I think. Yep. And let's go look secondary. And I will have that here as well. My secondary button, I need to add the word secondary as well here. Alright, so why don't we go in the my button component and make it support thinkable semantic colors instead.
Alright, so why don't we go in the MyButton component and make it support thinkable semantic colors instead of the sort of hardcoded ones. So myButton, and I believe we had set multiple style variants. Yep, there they are. And let me quickly remove these compound variants. So we stop having the uppercase text. There we go. And so here in the look, I'll start with the secondary one. So this is where instead of bg-slate-200,
And so here in the look, I'll start with the secondary one. So this is where instead of bg-slate-200, you would go bg-secondary, I think. Yep, it is. And I guess the text would be text-secondary. That's the one. And for the hover and active state, what does the chat and UI button do here? It's just using opacity variants of the colors. So yeah, let's do that for now. So hover:bg-secondary/90%
So yeah, let's do that for now. So hover bg-secondary/90% and active bg-secondary/80%. And there you go. By doing this, the secondary button is now honoring the dark mode. And so it's changing the text and background color accordingly. Now for the primary button we could go bg-primary, which is going to make it look tomato on the light mode. Yep, like this. But let's say we wanted a completely custom
which is going to make it look tomato on the light mode. Yep, like this. But let's say we wanted a completely custom new themeable color. So bgMyPrimary, and then we'd do the same on hover bgMyPrimary90 and for active, myPrimary80. But now we've completely lost the background color because myPrimary doesn't exist. No worries. Let's go add it in app.css. So in our theme, we will define a new primary,
No worries. Let's go add it in app. That's CSS. So in our theme, we will define a new primary, myPrimary color. So maybe after the primary here, --my-primary and it's going to use var(--my-primary). So this --my-primary doesn't exist yet, but we can go in the root scope and after the primary colors, we can define them here. --my-primary.
after the primary colors, we can define them here. Dash, dash my primary. And I'm not very good at working out KLCH colors by hand, but let's ask copilot to have a vibrant purple. And let's see if it gives us an KLCH color. It looks like it does. And yeah, that looks pretty nice. And maybe we can have a, my primary foreground, a super light purple that goes well on my primary. You can work it out. Okay, let's accept that
that goes well on my primary. You can work it out. Okay, let's accept that and yep, I'm pretty happy with that. So now that we have those defined, our primary button here should take these colors and let's take a look at what it looks like. Well that's pretty nice. So obviously it looks the same in dark because we haven't redefined the CSS variables. So let's do that now I will copy these two colors
because we haven't redefined the CSS variables. So let's do that now I will copy these two colors and then scroll down in the dark section and define there. And I think I'm gonna give another task to co-pilot. Give me a very light purple. And here for the text we'll have a dark purple foreground. Al yellow accepted. Looks great. Whoops. It looks like we are not using the foreground text and yep, my bad, it's using text-white. So here we can use text-my-primary-foreground,
and yep, my bad, it's using text-white. So here we can use text-my-primary-foreground, which doesn't exist, my bad. Again, because we have only defined a my-primary-color here, but we need to do a my-primary-foreground as well, which uses the my-primary-foreground-color. And this time I will be able to select text-my-primary-foreground, and we've got the dark version of the button.
Creating Swiss Theme9:58
my primary foreground, and we've got the dark version of the button. Cool. Alright. And that's essentially how themeing works in chat and ui. So what we're going to do is create a new Swiss theme. We are definitely not going to define all the colors for the whole chat n UI suite. We are just going to create a couple of colors just to understand how it works and override the border radius too.
to understand how it works and override the border radius too. And then we are going to have a scope for that theme and have a third row in our UI that uses the Swiss team. All right, so we don't need a variant here because we don't want to target specific classes, we just want to do some theming. So what we're going to do is create a new CSS variable scope. There is the root scope, there is the dark scope.
CSS variable scope. There is the root scope, there is the dark scope. And what we want to do is have a Swiss scope. So we could have a class like this, but I like to be a bit more specific that this is a theme and go data-theme equals Swiss. And here we'll keep it super simple. We'll override the primaryColor to be red and --primary-background is going to be white and we'll do the same for the custom, myPrimary.
and dash dash primary four ground is going to be white and we'll do the same for the custom, my primary and my primary foreground colors. And since everything is straight and square in Switzerland, I'll also change the border radius, which is here, dash, dash radius. And we'll set that to zero. So whenever we find ourselves inside a block of HTML that has a data-theme Swiss attached to it,
So whenever we find ourselves inside a block of HTML that has a data-theme Swis attached to it, these should be the color overrides. So I will triplicate this element here, but I'll remove the dark class. So now we have the same one more time. And here I will add the data-theme equals Swiss data attribute and check what happens. It's working the Swiss, my primary and primary buttons have a red background with white text.
It's working the Swiss, my primary and primary buttons have a red background with white text and hard straight edges. And the really cool thing is you can even scope a specific element by just added the data-theme to it. So let's say we wanted this my button here in the first segment to have the Swiss theme. I could go to the top one, this one here, and I could give that the data-theme of Swiss. And now just this button within the non Swiss section has
and I could give that the data theme of Swiss. And now just this button within the non Swiss section has the Swiss team because it is part of a scope where there is the data theme equals Swiss. Alright? And that's the essence of theming in Shaea N ui. So if you look through the components generated by Shaea N ui, you will see these semantic thimble colors all over the place. So we've seen the button component with these primary text,
colors all over the place. So we've seen the button component with these primary text, primary colors, but you can see also the ring color of destructive, border destructive. They are all these semantic colors. And if I look at the tabs, I am sure that there are also some semantic colors. So it's using the bg background text, muted foreground, and so on. And really the app that CSS theme colors tell the story.
muted foreground, and so on. And really the app that CSS theme colors tell the story. You can see that the cards will have a foreground and background color, so will the popovers. Then you have your more traditional semantic colors like muted accent, destructive, uh, and the charts. We'll have specific colors and so on. So like I said at the beginning, I think that this is pretty simple stuff. This is just a bunch of CSS variables.
that this is pretty simple stuff. This is just a bunch of CSS variables that are really defined at different scopes and that lets you handle theming that way. I hope you enjoyed it. See you in the next one.
