Create consistent designs in React using Tailwind CSS
Learn how to create a “mini design system” for your projects of all sizes using Tailwind CSS. Bree Hall teaches us.
Links & Resources
- https://x.com/bytesofbree
- https://www.bytesofbree.com/
- https://storybook.js.org/docs/writing-stories/args#args-object
- https://tailwindcss.com/
- https://www.youtube.com/tailwindlabs
- https://storybook.js.org/
- https://www.learnwithjason.dev/schedule/
- https://www.sanity.io/events/hackathon-nextjsconf
- https://githubuniverse.com/
Full Transcript
Click to toggle the visibility of the transcript
Captions provided by White Coat Captioning (https://whitecoatcaptioning.com/). Communication Access Realtime Translation (CART) is provided in order to facilitate communication accessibility and may not be a totally verbatim record of the proceedings.
JASON LENGSTORF: Hello, everyone, and welcome to another episode of Learn With Jason. Today, on the show, we're going to dig into something that I have struggled with. Um, and you know, it's one of those things that we all have our preferences, we all have our pet projects we love working with and today, we're going to challenge me to use one that I have historically not loved, but I'll tell you what I do love is our guest, who's going to be teaching us. I'm so excited to have her on the show. Please welcome Bree Hall. Bree, how you doing?
BREE HALL: I'm good. How are you doing today?
JASON LENGSTORF: I'm superexcited to have you today. I've been following you for a long time. I you give a background on what you do?
BREE HALL: Absolutely. You might have seen me as Bytes of Bree. My career started out as a full stack engineer in 2018. I very quickly realized I did not like backend development at all and I felt like the frontend is where I could be a lot more creative and I felt like there was an endless world I could explore. In doing so, and in learning about myself and learning about coding and how I learn best, I started to share what my journey was like on social media because I didn't see a lot of people that I could relate to, at the time, who may be had struggled in getting a career in software engineering or getting a career in coding. I guess my presence is to show people there isn't one right way to do it. Sometimes there's going to be ups, sometimes there's going to be down. If you have a will, there's definitely a way for you to get there.
JASON LENGSTORF: I love that. It echoes a lot of my feelings on the subject. There are a lot of paths through and, you know, it's not about a destination, it's about finding a way that fits so I really do love that.
Okay. So, let's talk a little bit about what we actually want to cover today. I am very interested in just finding ways to make building and scaling things, on the frontend, a little more sustainable because I am definitely� you know, I love my bespoke, handcrafted CSS. It is my Achilles Heel. So, for me, what I find is I really enjoy writing CSS when I start. I really enjoy getting into the design. The bespokeness makes it difficult to onboard maintainers, it makes it hard to remember what I was doing when I worked on the thing.
I've started reaching for the scoped singlefile components. Astro is my current preferred way of doing it. Got a little bit of Markup, a little bit of CSS and they're tightlycoupled so I don't have to remember this section relates to this tiny, little file over here. But even with that, I think there's rough edges and some things I would like to do better. This is where you're an expert. Let's talk about how do you make consistent things in frontend projects, that can survive maintenance, that can survive age?
BREE HALL: Absolutely. I think the first piece of creating something that is consistent, but also still malleable and flexible is not going into something with the mindset that whenever you create it, at a certain day or a certain time, that's how it's always going to be and that's how it's going to stand. I think a lot of times, I start something, just like you, I'm very excited, I want things to look a certain way. For me, once I start to get into it and see the components together� I'll be the first person to say, I do not scope everything out in Figma. I don't do a lot of designing. If I try to do it, I'm not going to do the development at all. Sometimes I tend to just dive in.
For me, it's usually having an open mind of, okay, I kind of know what I want this to look like in my head, working out it. If I don't like it, it's easier for me to change a component that's being reused multiple times versus going into individual places, almost like you said with Astro, with the templates, and having to change it in multiple different places.
JASON LENGSTORF: Okay. Great. I mean, that� that sounds� in the abstract, that all makes a lot of sense. When we get down to brass tacks.
BREE HALL: I'm an Astro girly. Astro and templates makes development so much easier. In Tailwind, I also have been recently using, I previously didn't use Tailwind because I was not happy about the superlong class names. I really didn't like the thought of, oh, I can just, like, do this with CSS if I my own CSS file, like, that's really easy. Why would I use this over that? But what ended up happening was, I realized, as I started to use some of the utilities, it was a lot easier, as I got to know them more, and got to realize what kind of, like, flexibility and scalability, almost Tailwind has, because some properties have multiple different CSS properties involved and it kind of takes away the, I'm not a designer, I don't know if this has the right spacing.
JASON LENGSTORF: Yeah. Okay. All right. That makes sense, I'm with you. I will agree, I'm coming from the same standpoint of, like, a lot of things about Tailwind kind of put me off, right. So I have not made the switch and so I'm really excited to see how you've bridged that gap. So, I guess, were there specific things that you did, that made Tailwind make sense for you? Was it more of a, through prolonged usage, it started to feel more familiar and that get rid of the hesitation? I guess, how did you shape Tailwind into something that you wanted to use and reach for as a default?
BREE HALL: Yeah, absolutely. I worked on design system and working on a design system, number one, easily one of the most difficult jobs that I've had being, like, a maintainer. But it taught me a lot and it taught me a lot about having good project structure, good standards and good ways of scoping and creating things so that they do end up scaling with the project.
One time I Tweeted out, Tailwind, really long class names, I don't really love it. One of my coworkers said, I use it as a way to leverage my own component library or design system. I was like, what do you mean by that? And, we thought about it in the realm of a component library where there's a lot of, um, a lot of props, a lot of state, a lot of things going on and I knew that my projects were not at that scale and I didn't know if I will ever need a component library at that scale.
But what it did do was, it allowed me to mold and take out the little pieces of Tailwind I wanted to use and shape them in a way or componentize them� in one, nice package where, okay, I want to create a heading, for instance, which is one of the things we're going to create today. I don't need to create that 20 different times. Just being able to create one, use the exact properties that I need from Tailwind, and then just exporting that and not having to worry about it again, sort of reframed how I was thinking about it.
Going from thinking about it as, we're putting the class names directly on every single element we need to be styled, to I'm creating a small package of just one thing I'm trying to achieve kind of helped me reframe it.
JASON LENGSTORF: We'll probably� as we get into the code, I'll have a couple more pointed questions because there's some things that always come to me when we're inlining the styles via class names. I always worry about, like, okay, what happens when we decide we're going to change the border radius? Do I have to find and change the border radius class? Then I'm kind of like, why aren't we� anyways, lots of things that come to my mind so I'm very excited to dig into this a little bit and learn more.
So, let's see...what else do we need to know? We're going to be using� you said Next.js today. We're going to be using Tailwind. How are you organizing these systems? What are you using to sort of keep track of it and make sure that other people know how to use it?
BREE HALL: Absolutely. One tool is Storybook. It's great for a lot of things, especially visual testing. We're going to be using that, today, to visually see what we're building in isolation. We're going to be building the components in isolation from pages, just so we can see what the building process is like for them. Originally, I was thinking about building something, but I think it kind of takes away from the understanding of, we're building a component that can live by itself, regardless of what page it's on or how it's being used, so by developing in isolation that way, that's what we're going to be doing.
But, yeah, usually Storybook. I also use a package called Class Names. It's a tool that can class names for you and it allows you to conditionally render different classes.
JASON LENGSTORF: Cool. Very, very cool. So I don't think I have any other questions that wouldn't be easier to answer with code so why don't we switch into the pair programming view. I'm going to pop up this banner and this banner, here, and first and foremost, I'm going to make a shoutout to our captioner, we've got Vanessa here with us, doing live, human captioning. Thanks, Vanessa. That is through White Coat Captioning. That is made possible by Netlify.
We are going to be using Tuple for the pair programming. You'll see a little bit how that works as Bree and I go through the project today. If you work with other people, it's dope.
We're talking to Bree. You should follow Bree in all the places. Let me drop a link in here. This is where you can find Bree, all over the internet. I love a Link Tree. And then� and then, we� let's see, we're talking about Storybook. We're talking about, um� what else are we talking about today? Tailwind. Tailwind CSS. Let's get into this one...
Any other links I should throw up?
BREE HALL: We might put them up as we go. I think those are good ones for now.
JASON LENGSTORF: Excellent. I think the next piece is to ask you what I do next. [Laughter].
BREE HALL: Feel free to open your favorite code editor and we are actually going to spin up a new Next.js project.
JASON LENGSTORF: Okay. I'm in a code editor. I will move into� let's see, let's go into this one and this one and I'm ready.
BREE HALL: Perfect. Is it okay if I paste in a little command?
JASON LENGSTORF: Absolutely.
BREE HALL: Awesome. Perfect. Yep. So, we're going to do "npx create next app." This is going to create our Next application. I like Astro, but it doesn't work super, super well with Storybook just yet so we are going to do TypeScript, we are going to do ESLint. We are going to use a source directory.
JASON LENGSTORF: Do you want to use app router?
BREE HALL: You can if you want. We're not actually using Next.js.
JASON LENGSTORF: I hit the button before I asked. [Laughter].
BREE HALL: It's all good. So that's going to install everything that we need and it's� that's so lovely that it just does it all for you, in one go.
JASON LENGSTORF: It's really nice. This put us in Tailwind components and then I will open that...
BREE HALL: And the next thing we're going to do, I want to open the tailwind.config.ts file.
JASON LENGSTORF: Let me "init" this project so it stops being grayed out on us.
BREE HALL: Okay. Perfect.
JASON LENGSTORF: Wait, where am I? Why isn't that showing� hold on one second. Let me open the window.
BREE HALL: So the thing I'm looking for is to make sure, in our content array, that Source and Components is included so Tailwind can find and use classes in our folder.
JASON LENGSTORF: Got it. Great.
BREE HALL: It looks good. The next thing we're going to do is install Class Names. That's a really easy "npm install." Exactly.
And the last thing we're going to need to install is Storybook. That is "npx sb init."
JASON LENGSTORF: All right.
BREE HALL: It is going to turn our server on immediately, as soon as it's installed�
JASON LENGSTORF: Okay. So, this is adding Storybook 835, installing dependencies. It's all good. And now it is� it determined it was a Next app so it's autoinstalling things.
BREE HALL: It has all kinds of addons you can do. The configuration for Next, you used to have different addons. They made the developer experience better because it's allinone.
JASON LENGSTORF: This is our Storybook app.
BREE HALL: Yes.
JASON LENGSTORF: And looking down here, we've got� it's already installed. It's running� so, it ran "npm run Storybook" for us.
BREE HALL: Uhhuh.
JASON LENGSTORF: Didn't build the preview, but then I think it got past that. Broken build, fix the error above, you may need to refresh the browser. We'll send through crash reports, that's fine. Let's� so, wait, this is down, then? So let's try one more time. "Npm run Storybook."
BREE HALL: "Npx" has to be in it. That should be fine.
JASON LENGSTORF: It's broken again. Cannot find the module ag� what the heck is this? Oh, Storybook. Which thing is broken?
[Laughter].
This is, like, a brandnew package so nothing that we've done is� is what is broken. So something went weird.
BREE HALL: Awry.
JASON LENGSTORF: Let me do a quick Google on this error and see if somebody's found this.
BREE HALL: I just did the most recent version as well. It was able to find that we were using Next.js so I don't think it's a problem of our Next build being broken or anything.
JASON LENGSTORF: Um...these are older errors. Everybody's getting this issue. This isn't Storybook, this is another project. But it looks like maybe we need to...it did something weird.
Okay. Let's try one more time and see if that maybe does it...and it crashed again. Oh, no, this is� this is not great. Um...let me just try, uh, the Nuclear approach, where we delete the Node modules and try again. Remove Node modules, remove the package lock. What else is in here? Is that Storybook?
BREE HALL: Uhhuh.
JASON LENGSTORF: That's all fine. And let's see if it works this time.
BREE HALL: Fingers crossed...
JASON LENGSTORF: Web Dev Challenge is coming October 30th.
Let's see here. What's up, everybody. Thanks for hanging out. There's our build. Oh, no.
Okay. So, this is not ideal. Try installing AJV and see if maybe installing it manually works? Huh. Yeah, they're just missing� weird. Storybook's missing a dep right now.
I'm coming down here. We've got our site running.
BREE HALL: Perfect. The lovely onboarding, that will rhyme quite a few times. [Laughter].
JASON LENGSTORF: Thank you, Antonio, for the idea there, that unblocked us.
BREE HALL: Thank you. Love it. So now you should be able to see, click on the buttons on everything around there. This interface is basically what we're going to be using to test different permutations of our app we development.
JASON LENGSTORF: I guess for folks who haven't seen Storybook before, do you want to give, like, the onesentence pitch for what this is and what it's doing.
BREE HALL: Documentation, testing and visualization of components, all in one place without having to build your own testing playgrounds our testing website.
JASON LENGSTORF: We have docs for it. We have the primary version that we were looking at. We have the secondary version. Large and small. So, you can kind of see each of these is the button component and then I'm assuming, these are all being handled through probably somewhere in here, as a way to get the code?
BREE HALL: Yeah. So, these are actually created by Storybook Stories and we're going to build some of those, but what allows you to change, um, like, the permutations, for instance, is at the bottom, where it says, "name and control," those are different props available for the button. If you set Primary to "false," it with change.
JASON LENGSTORF: Got it. Okay. And then we� yeah, so we have a lot of control here.
BREE HALL: Yes.
JASON LENGSTORF: Great.
BREE HALL: Absolutely. Perfect. What we need to do is actually configure Storybook and tell it exactly where our styles are. So we can head back over to the code editor and we can open, inside of the�.story fold, it's going to be preview.ts. What we're going to do is import the global styles. So it's going to be Import and the global styles are inside of our source folders, which is @/globals.css.
JASON LENGSTORF: Do we have that?
BREE HALL: We should. It should be in�
JASON LENGSTORF: I just wanted� globals.css.
BREE HALL: Perfect. Now we should have just about everything that we need, all set up and all configured so what we're going to do is create a very basic, rough component, just to make sure that everything is all good before we get too much farther in.
So, the basic structure of our components folder is going to be our source and then we're going to add a folder called Components.
JASON LENGSTORF: Okay.
BREE HALL: And then inside of it, we're going to add another folder for all of our different components that we do. So the first one is going to be a heading.
JASON LENGSTORF: Okay.
BREE HALL: And then each, um, folder for the components is going to have two pieces. One is going to be the component.tsx. So, in this instance, it's going to be heading.tsx. And then the other one is going to be heading.stories.ts.
JASON LENGSTORF: Ts. Okay.
BREE HALL: Perfect. So, let's start with building a very, very simple, um, heading component and we're going to head into heading.tsx. Yep. And you're already on the right path. We're just going to import React and create a function component called Heading and right�
JASON LENGSTORF: Does it matter, in Storybook, if it is a named or default export, like, if I do "export const heading" and, you know, like, something like this...
BREE HALL: I think it still should be fine. I use default exports, but I don't think it really matters in this case.
JASON LENGSTORF: I'm curious to see. I don't know what Storybook does with exports.
BREE HALL: I've seen it both ways. That's a great start, but we're also going to� right next to our H1, we're going to add the class name attribute� oh, I probably� do you have extensions available on this IDE? Because the Tailwind IntelliSense would be very handy.
JASON LENGSTORF: Tailwind� this one?
BREE HALL: Yes.
JASON LENGSTORF: Apparently, I already have this installed. [Laughter]. I don't know what's going on in this IDE. [Laughter].
BREE HALL: So, for class name, you should be able to start typing "text" and it should give you some options. "Textpurple," which is my favorite color.
JASON LENGSTORF: All right. And what shade do you want?
BREE HALL: Let's do 500, a nice middleofthe ground color. IntelliSense is popping up and letting us know we can add Tailwind classes to this.
Next, what we're going to do is head over to our heading.stories.ts.
JASON LENGSTORF: Okay.
BREE HALL: And, I actually� this part can get a little bit repetitive so I have a template� is it okay if I just paste it in?
JASON LENGSTORF: Yeah, please do.
BREE HALL: Please do.
JASON LENGSTORF: This is such a nice thing, since I started using Tuple because before, you would have had to copypaste that into a gist.
BREE HALL: It's so handy. The basic rundown of a Storybook story is the first thing we need to do is update our imports to make sure they are the actual components that we would like. So, in this case, we don't have any props for our heading just yet so we can leave that. We have two parts of this. We have the meta, which is going to tell us everything about our component and let us define the arguments that we saw in Storybook, where we can change the different prop permutations and then we have the actual story, itself, and in this case, we really don't have a lot going on here because we're really just using it as a playground.
So for the meta, we are going to use "type of heading," because we want the meta to describe our heading component and it's going to be the same on Line 14, inside of the story object, as well. Perfect.
Now when we save this, if we head back to Storybook, we should see that we have a new example for our heading and we do. And we have our component there, as well. So what we've done so far is created a super, super basic example of a component. We applied our Tailwind class and we have made it so that we can see that component inside of Storybook.
JASON LENGSTORF: Excellent. And so, this� and what I like about this, too, is that from the standpoint of, like, building, I haven't had to change anything. Because sometimes there are tools that I've used where they're like, we'll add all that functionality, all you have to do is change the way you write your components. So the idea that there's some boilerplate here, but it's not heavy and I like that. You know, it's kind of nice that we don't have to deal with the� I would say, like, the chaos of all this. We are, instead, able to just, like� we mark up a component however we want and then we have control over, like, how much work we put into Storybook. Yeah, that's nice. I like that.
BREE HALL: Exactly. I will say, sometimes, Storybook does get verbose. It tries really hard to figure out what the controls should be. Maybe there are different ways that you would prefer to have those props displayed for users, for documentation purposes.
JASON LENGSTORF: Absolutely.
BREE HALL: So the next thing we're going to do� I will shorten this, just in case we end up not having enough time. I basically would like to run through creating atomic components. By atomic components, I mean these are the most building blocks of a component library or of a design system that you could think about. So, some of these are going to be our heading, which we've already worked on; a simple text component; a button, which is one of the mostused components on any website; an input form element; a badge and an avatar, if we get to those. And then, from those components, we're going to create two different composed components, I'll call them. These could be thought of as different pages or different elements you might find on a website.
JASON LENGSTORF: I tried to keep up with you and I definitely didn't. So, you said a heading, text input and what were the others?
BREE HALL: Heading, text, input, button.
JASON LENGSTORF: Okay.
BREE HALL: You're quick because I would still be on, like, text.
JASON LENGSTORF: Text, heading, input, button. And then you said?
BREE HALL: Badge.
JASON LENGSTORF: Badge.
BREE HALL: Yes.
JASON LENGSTORF: Okay.
BREE HALL: And the last one, right now, is avatar.
JASON LENGSTORF: Okay. There's probably faster ways to do this. The Vim users are like, how are you so slow? [Laughter].
BREE HALL: I just recently got a Stream Deck and I'm trying to figure out ways to create shortcuts. I can never remember keyboard shortcuts, so, yeah.
JASON LENGSTORF: I'm getting slowly better, but has taken years.
Okay. So, I have my heading, here. And why don't I just take each of these and we'll do a very, very basic, kind of� mark it up. We'll just say...to do avatar. And we'll change this name to Avatar.
BREE HALL: Sounds good.
JASON LENGSTORF: Okay. Then we can save that. We can head over here. We can do the same thing, change this to Badge. Button. Input. And, Text. Okay. And then, do you want me to do the story thing, too, like, copy�
BREE HALL: Either you can copy it or I can paste the boilerplate. Either one, whatever one is going to be the easiest.
JASON LENGSTORF: Let's see, if I change each of these to be its main thing� and I do one of those�
BREE HALL: I would have done that in, like, 50 seconds. A simple Find and Replace. [Laughter].
JASON LENGSTORF: Okay. Then we go here, Text. And, then we do� was that it?
BREE HALL: Yeah. I think so.
JASON LENGSTORF: All right. So� did I get the badge? No, not yet. Okay. So if I did that right� something went wrong. View error. Unable to index files.
BREE HALL: Try refreshing.
JASON LENGSTORF: One of these. One of these. One of those. One of them and one of these. So that should be all of them.
BREE HALL: You might have to stop the server and start it again. I noticed even in development, sometimes when you're adding, um, stories, or, like, the component.stories.whatever, it takes a second to�
JASON LENGSTORF: Avatar, stories. Missing default export out� I screwed up something in avatar. Oh, because we didn't do the avatar because it was off my screen.
BREE HALL: It was hiding. [Laughter].
JASON LENGSTORF: Okay. Hey, how about now? Heyyyyy. All of our very simple pieces. They all are showing did I screw this up?
BREE HALL: No. Oh, they should be� there you go.
JASON LENGSTORF: There we go. There we go.
BREE HALL: Little refresh. Always does it.
JASON LENGSTORF: Here's our heading, input, text. Components.
BREE HALL: We love it. That's half the battle, honestly.
[Laughter].
I know everybody tends to do component development a little bit differently. I tend to start with the props and then work my way down from there. I do already have the props up and running, just in case we do need, like, a backup copy and paste also so if it gets to be too much or maybe it would be easier to copy and paste, always happy to do so.
So, in thinking about ways� because a heading is always my first thing that I go to whenever I'm starting a new project because we always need a heading for a landing page, if it's a blog, if it's a directory, you always need some version of a heading so typically that's the first one try to do. How can I use it, how can I componentize to fit my uses? The first thing I think about is semantics. I don't want H1, H2, H3. I like to add a level. We can have a type export, as well, whichever one is most comfortable.
JASON LENGSTORF: It looks like the Storybook convention is to do something like that?
BREE HALL: Yes.
JASON LENGSTORF: So we have a level.
BREE HALL: It's just going to be our H1, H2, H3, all the way through H6. This is to tell us we can keep up with semantics and have those proper heading levels.
JASON LENGSTORF: Got it. Whoa, what button was that?
[Laughter].
It's because the six is over here.
[Laughter].
All right. So there's that. Let me make this a little bit bigger so we can see what we're doing.
BREE HALL: The next thing I think about is distinguishing size from level. Just because we have an H1 doesn't mean we want that to be the biggest thing on the page. I try to use Tailwind naming conventions. Is it okay if I paste the name while we talk about it?
JASON LENGSTORF: Please do.
BREE HALL: Tailwind used the twoletter, "sm, lg," until you get to the larger sizes. I wish they used "medium" instead of "base." We have a specific level, we want it to be whatever size the developer states.
The next thing I think about is alignment, which is typical for any version of text so the next prop we have is "align" or "alignment." We go with "left, center and right."
JASON LENGSTORF: Got it.
BREE HALL: And then the last one that we need is going to be children. And this is going to be a React Node and what this is going to do is say anything that's included inside of our heading is what we want to display. Now, we mostly want this to be a string. But, yeah, we're going to go ahead and pass that right along.
JASON LENGSTORF: React's in the global namespace so that will work. And then I do "props." "Heading props."
BREE HALL: Yep. Or we can define them individually, as well. We also want to define a couple of defaults.
JASON LENGSTORF: Gotcha.
BREE HALL: For level, we want to specific what level it is. For size, we're going to make the base size the default in case they don't pass anything in.
JASON LENGSTORF: There we go.
BREE HALL: And then for alignment, we're going to default to left alignment. Perfect. So now what we need to do� now that we have our props and everything, is we need to create a heading tag that's based on a level that is provided. And we can do that by creating a const and we're just going to call it Heading Tag and we're going to set it to be whatever level H1 is. We're going to replace our H1 to be that heading tag. Yep. So that way it's dynamic based on whatever was input. And we also need to go back up to the top for just one second, to import class names because
JASON LENGSTORF: Let's see if my thingy's going to work here. If I start typing class names, it should...theoreticallyspeaking�
BREE HALL: Fingers crossed.
JASON LENGSTORF: Class names from class names, a-ha!
BREE HALL: Perfect. We love it. We're going to set this as Heading Class Names, as a const. That's usually my naming convention, whenever I am styling class names. And then we're going to, first, define any common, um, class names that we need. And we actually don't need to put them in an array. We can actually just use a string and then, like, it'll concatenate them all together.
Tailwind's naming convention for font and font styling, it gets confusing. We're going to do "fontbold." And then the next thing that we want to do, outside of that string, so we can add a comma at the end, is we want to start conditionally assigning, um, the sizing based on whatever side the person has input is. So, we're going to actually use the brackets.
JASON LENGSTORF: Square boys?
BREE HALL: Curly boys. Yes. For instance, it allows to determine the sizing. Let's start a string as the key and we're going to say that we want to apply text4xl, which is the Tailwind sizing of 4x. I don't really know what that is in the actual pixels or m sizing. But then we're going to do a colon to give it a value and say "if size is equal to 4xl, then we're basically just applying those styles." So we can do that for the remainder of them and I have them all queued up if you want me to copy and paste them.
JASON LENGSTORF: Yeah, please do. I'm not familiar with class names. Could we also do string interpolation where we did a text size?
BREE HALL: Yes. You could do it with class names but the problem, in this case, is actually going to be Tailwind. So, these classes are generated at build time and string interpolation doesn't happen at the same time so what happens is, the class name is built but Tailwind did not generate the class so�
JASON LENGSTORF: Because it didn't see this or whatever?
BREE HALL: Yeah. That was a big hurdle to get over because this, in theory, could be so much cleaner by just saying the size and then just, you know, use string interpolation to throw in what the actual size it. That is one, slight drawback. But the last thing we want to do, after we have conditionally done our sizes, is our text. Using the same method: Textleft, for our key inside of the curly brackets because we're using a new conditional.
JASON LENGSTORF: Got it. So, text level.
BREE HALL: And if our alignment is left.
JASON LENGSTORF: We need a few of those.
BREE HALL: Uhhuh.
JASON LENGSTORF: Center.
BREE HALL: Spelling is hard. [Laughter].
JASON LENGSTORF: It's the worst. Okay. So that's left, center, right. What else do we have? Just our children, right?
BREE HALL: Yes. We can throw children right in the center.
JASON LENGSTORF: Okay. And then do I just apply this as a variable here or do I have to doing anything with it to use the class names?
BREE HALL: No. You apply it directly to the element.
JASON LENGSTORF: What are you mad about? That makes sense. We've got our class names here. I will collapse these down so we can see what's going on in a full file. All right. This makes sense and I can see kind of where this is going, where, um, you want to intentionally choose what people are doing rather than saying, you can use any CSS you want. You're sort of limiting the scope a little bit.
BREE HALL: Exactly.
JASON LENGSTORF: You could maybe say, our headings, we don't have these ones. You, as the designer, can say, this is what we allow in our style guide. If you want something different, you need to make a case for why you're not using our style guide.
BREE HALL: 100%. You can overwrite these. Maybe I hire a designer and they say, hey, your sizing is way too small but maybe I don't want to go change all of the sizing within, like, my components and my app and everything like that. I can actually use the Tailwind config to change them all in one place and that's it and it's done.
JASON LENGSTORF: Cool. Very cool.
BREE HALL: Yeah. So let's actually see what this looks like in action and we're going to open our stories that are available for this heading component.
JASON LENGSTORF: Okay. So we're going to go out here. Heading. Here's our playground.
BREE HALL: Oh, in the code. I'm sorry.
JASON LENGSTORF: Oh, in the code. Now we don't have our type. I need my heading props now. Do I replace this type of heading with the heading props?
BREE HALL: Yes.
JASON LENGSTORF: Okay. Anything else we need to fix here?
BREE HALL: In our arg types, what we're going to do is make a few amendments. So, the arg types, on line 8, is what tells us what controls we can have inside of the UI and because this gets a little bit monotonous, I can copy and paste this in for you. We are telling the arg types what child we want to update or what prop we want to update. Did I mess that up really bad?
JASON LENGSTORF: No, I think we might have a double "a" here.
BREE HALL: We want to make sure our children is updated to text so that we can see what we're trying in. We can't input a React Node inside of Storybook. If we want to see the changes happen, as we're in Storybook, we're just going to change it to text and then for level, size and alignment, what we're doing is changing the type to be select. I want to say, it would probably end up being a, like, a text box if we didn't have the select there. That just gives us a nice, graphical UI dropdown so we can easily just say, okay, I want this, whatever, whatever, supereasy to select.
Then at the bottom, what we're going to do for our args� because args and arg types are different� is we're going to define what we want our default props in this component to be. For instance, let's say we want to start this story at a level of H1, that isn't going to change anything visually. It's just going to be semantic inside of the DOM. Is it okay if I type it out?
JASON LENGSTORF: Please.
BREE HALL: Apparently, I like brackets. So we're going to set the level to be an H1 and I love that the IntelliSense tells me what size is and what is available.
JASON LENGSTORF: Extremely nice.
BREE HALL: It is supernice. For children, it's just a string so we can say whatever we want it to, within reason. And then, we'll have it be leftaligned. No?
JASON LENGSTORF: It's very temperamental. If you don't take its first suggestion, it's like, okay, fine, I'm not helping.
BREE HALL: Exactly. Once we save that, we should be able to head over to the Storybook UI now. Level's not going to do too much in the UI itself, but if we were to doing something, like open the console, we should be able to see that those changes are made. And I love that we can see the changes, um, in the size, as well. And then if we were to change text or do anything like that, we should be able to see that.
So, yeah.
[Laughter].
JASON LENGSTORF: Yeah, I mean, this is great. This is a sandbox where I can see what the component is doing by itself and what we've made here is a selfcontained component. And this, from an architecture standpoint, from a design standpoint, you know, I remember the first time that I saw one of these, this is a lot of boilerplate when I could put some CSS in. If I'm working with a team of five people, the likelihood that those five people will write CSS the same way is not there. When I joined IBM, there were teams, zero chance that they were going to consistently apply arbitrary things. We had a whole design systems team that was building out these kinds of prescriptive things. They were like, trust me, if it wasn't there, y'all would make a huge mess. [Laughter].
BREE HALL: The one thing I learned about working on a design system, none of the components were small. None of them were supertiny, supercute, easy components. They were so verbose. It took me a really long time to understand a lot of the components just because there was so much going on behind the scenes that we had tried to account for to make the developer experience and the developer's life easier so they didn't have to apply extra styles, so they didn't have to take certain things into account, so that it's always semantic and accessible. Accessibility is a big thing and is not something that everybody takes into account. If we could do it in our design system, then we did do it.
JASON LENGSTORF: Yeah. And I think that really does kind of� it starts to highlight the value of a design systems team. If I want to get the ticket that I've been assigned for the project my team's working on and I want to build it as quickly as possible.
BREE HALL: Yes.
JASON LENGSTORF: And my incentive is not to do extensive accessibility testing. It's not to do extensive design system adherence testing and making sure what I do looks exactly the same as everybody else does across the system. You can feel the outcome of that, where each page of your dashboard is just a little different and it feels a little weird and so you know, it's very much that, like, the design system is there to do the work that we will� if we were left on our own, we would cut those corners. It's the foundation of making something that feels good to use, even if I'm the laziest version of myself when I use those components and I think when it's done well, where the magic really comes in is that you end up with components that are easier for me to use, as a developer, who wants to work fast, and all of the right outcomes, it's accessible, it's cohesive with the rest of the design, it's maintainable, it's using the best practices of the company. If you make the change on the design system, the looks right. It is difficult to communicate this sometimes but it so hard to overstate the value. Even if it's just you� [Laughter].
BREE HALL: No, what you said about being lazy, I am a lazy engineer, especially when it's� I've worked all day and I'm trying to figure out how to fit my side project in. I this idea and I want it to come out but I don't have the full capacity to do a design, I don't have the full capacity to make it look how I want it to look right now. I have an idea and I need to get it out. I will be lazy about the styling, about creating the components and getting something at least looking halfway decent. If I can focus on what's actually the hard part for me, which is sometimes figuring out how to integrate certain functionality. This part, we can offload some of the duty a little bit. Especially what you did about inviting people in, if you want to go open source, it is easier to be like, hey, build this page, I already have the components built for you. If you find something you need that's not there, let's update the design system or the components part of the library app.
JASON LENGSTORF: Absolutely. Excellent. This is great. Okay. What do we want to do next? What's our next step here?
BREE HALL: Perfect. I'm looking at the time to make sure we're on time. Text is sort of the same as heading. I'm going to skip over it for right now. Let's go ahead and take a look at Button. Buttons� I just saw somebody put in the chat. Buttons are one of those things where you have 18 different types of buttons and somehow they all look slightly different but it's different enough for you to tell that something's different. [Laughter].
JASON LENGSTORF: Right.
BREE HALL: Let's say in this design system or this project, we want three main buttons, which is typical. We want to have different variants and we're going to call them "primary, secondary and empty." "Empty" doesn't have a link or a border. We want a couple different sizes. We'll make it easy and say we want a small variant and we'll do base. And for small, we actually just want it to be "sm" because� sorry, I should have said that.
[Laughter].
And then, we want to also have children for the same purpose that we had our children in our heading and that's also going to be a React Node. We're also going to have an onclick function that is going to be optional just in case you want to have, I guess, a button that looks nice.
[Laughter].
And it can return "void."
And "disabled" as an optional one, as well.
JASON LENGSTORF: Got it. And, we make things optional in TypeScript like this.
BREE HALL: We do. Yes.
JASON LENGSTORF: So then down here, we're going to have our button props.
BREE HALL: Uhhuh.
JASON LENGSTORF: Sorry, is this valid TypeScript?
BREE HALL: I do believe it is. I have mine as void. We're not adding an onclick to our actual button today. But in theory, if you wanted to do it that way, you could.
JASON LENGSTORF: Do we want to default that to something?
BREE HALL: Yeah, let's default it to primary.
JASON LENGSTORF: Our size, I'm assuming we default to base?
BREE HALL: Yes.
JASON LENGSTORF: Our children won't be defaulted. The onclick will default to that function and "disabled," we'd default to "false."
BREE HALL: Yes. Your onclick is giving you a terrible time, it looks like?
JASON LENGSTORF: How dare you. "Void" is not assigning.
BREE HALL: To an anonymous function.
JASON LENGSTORF: So then we have a button. We have our onclick. That equals onclick. Then we have our children.
BREE HALL: And we have "disabled," as well, that needs to be a prop.
JASON LENGSTORF: Oh, you're right. And "disabled" is going to be disabled. Okay. And then we got to do our class names, right?
BREE HALL: We do. Awesome. So what we can do is start with just general classes that are going to be on the buttons. I like rounded buttons so the first one we're going to have� as soon as we do class names� is going to be rounded. And this changes the border radius for, um, any elements.
Perfect. And then we're going to open some curly brackets and defining the different style variations. So before we actually head into the style variations, I'm going to have you open the tailwind.config.ts file. So, this is almost like a onestop shop, a onestop place where you can both extend and override any values inside of TypeScript. So, what we're going to do, in this case, is extend our colors so right under "foreground," we're going to add a new line and add a primary color so we're just going to call it "primary." And then we're actually going to use a hex value. You also can use CSS variables, as well. But it's going to be "b388eb." And that's just a lovely lavender color.
JASON LENGSTORF: Do we want a secondary, as well, while we're here?
BREE HALL: No. Because our secondary button is actually going to be a button that uses the primary color, but with an outline and white on the inside.
We can head to our button.tsx. For primary, we want to set the background to be our primary color. So, we're going to use "bgprimary" inside of a string.
JASON LENGSTORF: Um, sorry, you're going to have to walk me through this because I haven't used the class names. These are the class names we want to apply if variant equals primary, right?
BREE HALL: Right.
JASON LENGSTORF: This is "bgprimary."
BREE HALL: Yes, exactly. We can string a lot of them together for a lot of the styles we need. Inside of that string, we're going to continue and say that we want the text to be why, so that's just going to be "textwhite." That is going to complete our primary button. It's a button that has rounded edges, has a purple background and white text. That's it, really simple.
So our next variant is going to be secondary and "bgwhite" because we want the background of the button to be white. We want the text to appear in the purple color, so "textprimary." And apply a border to this one. So the border is going to be a few different styles that we need. The first one is "border," by itself. The next one is "border2. "This gives us a border with, I want to say a twopixel width. And then the next one is going to be "borderprimary" and this is going to set the border color to be that lavender color and that completes our secondary variation.
And then we can move down to one more, which is going to be the "empty" button and what we're going to do is have this almost look like a link so we really only care about the text and its color. "Textprimary." And then what we want to do is underline it as well, so "underline."
So that is the different variations for the buttons that we have. So now we can define a little bit of padding and sizing for the buttons, as well. So we can come out of that little curly section and add another one. This is going to be a really, really small one. But if our size is equal to base...then what we want to do is apply a padding of 4. So, p4.
JASON LENGSTORF: P4.
BREE HALL: Uhhuh. The second one, for our small, is going to be p2.
JASON LENGSTORF: P2 for small and those were our only sizes, right?
BREE HALL: Yes. We only had two of them there.
JASON LENGSTORF: What are you mad about, bro?
[Laughter].
Okay. So then if I add these class names, we should be in good shape...
BREE HALL: Yes.
JASON LENGSTORF: Button class names. Okay. So, now I need to go into the Stories.
BREE HALL: Yes.
JASON LENGSTORF: And then we'll get the type of button props.
BREE HALL: Uhhuh. And just like that time, I can copy those arg and arg types in for you.
JASON LENGSTORF: Yes, go ahead. All right. And then for these ones, is there anything worth calling out here?
BREE HALL: The difference is for 6, it says, we have a disabled boolean. We'll get to that in a really quick second. This should be enough for us to see what the buttons look like inside of Storybook. So if we save this and head over to the browser, we should be able to see our�
JASON LENGSTORF: Heyyyy. We can do secondary, we can do empty.
BREE HALL: Uhhuh. And change the sizing...
JASON LENGSTORF: Yeah, this is� this is looking good.
BREE HALL: Perfect. I do have a couple more styles. It's a little bit tricky to save them so I will copy them and paste them in so that you can see them.
JASON LENGSTORF: Okay.
BREE HALL: And, can we open button.tsx?
[Laughter].
JASON LENGSTORF: Apparently not. [Laughter].
BREE HALL: Perfect. So...I'm going to scroll down, just a little bit, right above our class names. And I'm going to add these almost at the top. The thing that I really like about doing it this way, you can see that this easily gets to be a lot of class names when you think about all of the different button states there are and even if we have just, like, one button where we weren't using class names or putting them directly on to the button, between ternaries and what's being used, even though it looks to be a bit more verbose, I like doing it this way because I'm being explicit about what I'm doing and what's actually contained in the classes.
JASON LENGSTORF: This is addressing one of the key problems I see with Tailwind is that, as you add more layers, the layers that you're adding, you're not always clear on, like, which ones are which so it's just sort of this wall of classes and it's not clear why they're all there so breaking them out like this is helpful because it� you can now see, ah, this� everything in here is related to the thing being disabled. Everything here is related to hover states and I can confidently know I'm not breaking another layer.
BREE HALL: Absolutely. Sometimes there's a weird feeling about separation of concerns, especially when you get into CSS and JS. I like CSS and JS, too. I like all kinds of styling.
JASON LENGSTORF: You're specifically out here to get my riled up, is what I'm hearing. [Laughter].
BREE HALL: No, I know that is a hot take. I know a lot of people do not like CSS and JS.
JASON LENGSTORF: This one� let me do a little bit of inference here. My hunch is, the hover class we just want to put in because Tailwind knows how hover works and so that's not something that we have to do. But this one, my hunch is we have to enable only if the disabled is set to true? Tailwind knows when disabled is there and will do?
BREE HALL: Exactly. Tailwind already knows when it is there and it's looking at the DOM to see if it is disabled. We can add both of these classes, as they are, to the class names and they'll just work.
JASON LENGSTORF: Okay. So then these come in here and go up here, go to primary. There's some hover. And then if I make it disabled, it goes disabled.
BREE HALL: Yes. One thing I do want to call out, which is something I learned very recently, is that enabled keyword that is on the hover classes back in button.tsx, so I was having a little bit of trouble with the button doing the hover effects when it was disabled. This makes sure it only applies these styles if there is no disabled element or disabled attribute on the element itself.
JASON LENGSTORF: If you're in CSS and not applying disabled styles.
BREE HALL: Perfect. So how are we feeling about that?
JASON LENGSTORF: It does the job and again, I think it's nice that these are, like, classified together. So, we know what the things are and then when you look down here, you can see, you know, what we've done. You could even take that a step further and say, these would be call, I don't know, the variant styles and these could be called size styles so you really have a clear delineation of what's going on.
There's probably limits of reasonableness in these, you can always overabstract something and it's even harder to maintain because you're like, why is everything in 15 different places? There is a happy medium to be struck here and I feel like these types of decisions are the right� the right call, in my opinion, because you don't have to, like, figure out that this and this, concatenate into one string. That's really nice.
BREE HALL: Absolutely. I really like what you said about overabstraction. When I first learned how to use TypeScript, everything was a type. Not everything needs to be separate. Not everything needs to have its own type. I probably would have made the variants their own types because I was convinced I was going to use them share else. Now that is a pattern that I got from working on a largerscale system and it's something I absolutely do not need in a personal blog or a personal project like this.
JASON LENGSTORF: Well, you know, one of the things I've learned from TypeScript is if I find myself writing a type and I'm not working on a library, I've probably overcomplicated my code because the vast majority of TypeScript that I write, if I write the code in a simplified way, TypeScript can infer all of my types and then I don't have to write custom types and so that� that has been a big� it's encouraged me to not be clever in my code and to keep things simple so that the parsers can infer. Some of the things people post on Twitter, how do you even do this with types? I'm like, I would never. [Laughter].
BREE HALL: Yeah. I think going� doing the jump from JavaScript to TypeScript was� I actually started with Java. So everything was superstrongly typed. It forces you to really understand what JavaScript is doing and keeping track of handling your data and TypeScript extracts that. It's easy to get into the mode of, I just want to add safety around it.
JASON LENGSTORF: Okay. So, we have about� call it 15ish minutes of coding left. Did you want to� we can probably get one more of these?
BREE HALL: Would you like to do avatar or badge?
JASON LENGSTORF: My hunch is badge isn't going to be terribly different from the button. It's going to be a box with padding and borders.
BREE HALL: The input is interesting, too.
JASON LENGSTORF: Chat, you got an opinion? You've got 10 seconds decide.
BREE HALL: Input, badge and avatar.
JASON LENGSTORF: One vote for avatar.
BREE HALL: There we go. We're going with that one.
JASON LENGSTORF: I want images, let's do it.
BREE HALL: So we're going to open avatar.tsx. Perfect. And our avatar props are actually going to be pretty simple. The first thing that we need is a source to tell us where the image is coming from. And, it's going to be a string. And then we have an alt image� not an alt image, an alt tag. We're going to give this a small base or a large size.
And it's going to be "sm."
JASON LENGSTORF: Small.
BREE HALL: "Large" is going to be "lg." Those are all the props we need. For avatars, specifically, most of the classes are shared. So, we can destructure, if we would like. We don't have any defaults in this one because we always want them to tell us what the source is. But I guess we can provide a default size.
JASON LENGSTORF: Yeah. We can default the size. So, there we go.
BREE HALL: Our return, for this one, is going to be an image. And we are going to directly add that alt on top of it.
JASON LENGSTORF: Got the alt and source. Our class names are going to end up being the avatar class names that we'll define up here.
BREE HALL: Yes.
JASON LENGSTORF: Avatar class names. And that's going to be class names.
BREE HALL: Uhhuh. And we might eventually want to close our class names. What we want it to be is, think about when you're commenting on something. It's a circle that has a picture in it or maybe it just has your initial. We're going with the picture version right now. In order to get that rounded, circle image, we're going to do "rounded full." It does pixels. We also want to add Object Cover, which is going to add the Object Fit property on it.
We're going to open some curly brackets and work on defining the sizing of our images. So, sizing is one of those things, like I said, that I definitely could see myself defining and then coming back to being like, oohh, that wasn't a good idea. What we're going to do, because our image is going to be fullyround, we need to set a width and a height on each one. So if the size is large, then we're going to set it at width of 24. And that's going to be "w24."
JASON LENGSTORF: 24. And then is, like, H24, as well?
BREE HALL: Yes.
JASON LENGSTORF: Got it. Is Tailwind planning on adopting the inline and block syntax, as well? Because they�
BREE HALL: The logical?
JASON LENGSTORF: Now when you do text directions and stuff, if you go left to right or top to bottom, if you use inline and block, it still flows if you switch to Japanese. The whereas the width and height doesn't. So, yeah, that'll be a fun� a fun thing. It's already implemented.
BREE HALL: It's already implemented. I love it. I'll have to check it out.
JASON LENGSTORF: We've got our large, we've got our small, we've got our base. All right. And what sizes do you want for the, um, for the base?
BREE HALL: For base, we can do 20.
JASON LENGSTORF: 20. And for small, 16?
BREE HALL: Yes. How did you know? It's funny because one of the� the trickiest things is not every number is a size in Tailwind. Sometimes I was using 1818, those aren't sizes.
JASON LENGSTORF: It's like a base 4?
BREE HALL: For smaller numbers, you might have one and as it gets larger, the increments get bigger. It wasn't intuitive at first. But the IntelliSense really helps out there.
That is our entire avatar component. I used to repeat and code multiple places so even something as simple as this makes it a little bit easier.
We can go over to our Stories for avatar and I'll throw in some arg and arg types.
JASON LENGSTORF: Avatar props.
BREE HALL: I think we forgot to export it, the avatar props.
JASON LENGSTORF: Let's go to our avatar again. Oh, yep, sure did. We've got our avatar props. Then you had� I'll let you take over...
BREE HALL: Yeah...
JASON LENGSTORF: Okay. So, we're doing the select. You've got some defaults in there, which is great.
BREE HALL: And, for the arg types, it's actually nice we didn't have to do many changes here because Storybook correctly inferred what I needed.
JASON LENGSTORF: Here's our avatar and we have a little kitten. We can start messing with it. I think I have my shot in here so we can, like� yeah, so you can swap out for one of those, make it a different size. So, this is really fun. Like, I love� I love that you can do that.
Now one thing that I noticed, that I� I� am I completely derailing you if I ask a couple Storybook questions? Something cool that I saw, when we were looking at the button, for example, is that I started kind of switching stuff up and I was going to say, let's do a small, secondary button and the text is going to be secondary small and then it gives me this new button down here. If I click this, does it just generate the file so that we get another option in the bottomleft here?
BREE HALL: Let's see. I actually don't know.
JASON LENGSTORF: We're going to call this "secondary small." Wowwwww!
BREE HALL: I see it there, but did it actually add it to our� okay.
JASON LENGSTORF: How does that work?
BREE HALL: Is it in the Stories folder. They created a Stories folder when we installed it.
JASON LENGSTORF: Sample stories, example button, primary, secondary. That's their examples. Where has it gone?
BREE HALL: And they didn't add any�
JASON LENGSTORF: Where did you go?
BREE HALL: Can you humor me for one moment, if you stopped and started the server, did it persist?
JASON LENGSTORF: I was about to start hunting.
BREE HALL: It did.
JASON LENGSTORF: So where did you go? Okay. So, I'm� I'm both impressed and confused. [Laughter]. You didn't go in here? Primary. Secondary. Theoretically, it would have exported from here, is what I would have guessed. Okay. Let's find in files and look for "secondary small."
BREE HALL: Nothing.
JASON LENGSTORF: How, on earth, do you exist?
BREE HALL: Let me...
JASON LENGSTORF: Any Tailwind experts know?
BREE HALL: Let's see. There's a blog post. Is there anything that I can� oh, let's� that's misleading.
JASON LENGSTORF: Local storage DB. That's a good question. Maybe it's in here. Let's see, that's not it. Local storage. Storybook. Manager store. That doesn't look like it.
Um...index DB. Nothing I see in there. Oh, here it is, lastviewed story ID. Let's see if this got stored anyway? Rough ID, Storybook internal. Okay. So it's not generating files. Is it giving us anything in here that's like, hey, we created a thing that you should probably save?
BREE HALL: You can probably try saving another one instead of scrolling up through the mumbo jumbo.
JASON LENGSTORF: Let's create a third one, empty. Let's create a new one. "Add a new story to your existing stories file." It says it's doing something.
BREE HALL: But it's not actually adding it.
JASON LENGSTORF: Where do you live? Button props empty. I am�
BREE HALL: Or is it in the stories this time?
JASON LENGSTORF: I don't know.
BREE HALL: Nothing, still at the bottom. I'm actually a little bit confused about that. There are technically two button stories. If it's accurately picking up the rest of our props, I don't know.
JASON LENGSTORF: It's all working. Something is happening� oh, you know what? I know how to test this, for real. Let's "get add everything." "Get commit." And say, "testing." And then we're going to run it again and we're going to see if anything changes in our "get setup" when we do this.
I'm going to go to Empty and we're going to say "base." "Empty base disabled."
BREE HALL: Someone mentioned we have a file� timed out.
JASON LENGSTORF: Try refreshing. I think I did something weird there. "Empty base disabled." Okay. So that says it did something. Then we can go to Source Control.
BREE HALL: Uhhuh.
JASON LENGSTORF: Where's the actual view. There we go. Um...secondary�
BREE HALL: Oh, it did add it.
JASON LENGSTORF: This one deleted it, looks like?
BREE HALL: Oh. From button.stories. No, it's in components, too.
JASON LENGSTORF: Fascinating what it's doing. So, yeah...how� how� how does this work?
[Laughter].
I'm so� I'm so confused because it�
BREE HALL: I don't think I know how it works, either. [Laughter].
JASON LENGSTORF: Okay.
BREE HALL: I think I may also not know how it works.
JASON LENGSTORF: This is fascinating because it's literally showing us the stuff working, but it's not there. Amazing. Anyways�
BREE HALL: I love it.
JASON LENGSTORF: Storybook, maybe somebody can tell us how this actually functions, but I would say, short of me not understanding Storybook, this was fascinating. There's a lot of to learn in here. There's a lot of really useful stuff that we can take and move into our projects in the future. So, we have, let's see, about six minutes before I got to shut us down.
Where should people go if they want to go deeper?
BREE HALL: The Tailwind YouTube channel is particularly helpful. They have a lot of videos on there. I think something that's hard to get a gauge on sometimes are patterns and patterns that work for you. I know there are some people who, this isn't what they would necessarily reach for. I think it's okay to find what's your jam, what works for you, especially for personal projects, if it helps you to build, helps you to build faster, do it. See how it scales. See how many problems you have or how many times you have to adjust. I always find my everything else on Twitter. [Laughter]. One thing I also do want to mention that we didn't get to take a look at because I'm bad with time and said, okay, we'll build eight components in an hour. Tailwind really does allow you to be flexible and customize in a lot of ways so one thing that I had queued up and ready was showing one login form using CSS variables and defining a couple really quick things in our Tailwind Configure, we can create multiple different themes. You don't have to start one place and say this is exactly where I want to be. Development is an iterative task. You're going to come across things that change. You're going to come across a lot of things you want to tweak or make modifications to. Componentizing your thinking really allows you to build small things at a time, adjust. You don't have to implement those straight at production time. You can build, deploy what you like, scrap what you don't.
JASON LENGSTORF: Got it. Yeah. I mean, this is great. I feel like I� maybe my heart is little more open to Tailwind after today. Still don't know if I'm going to use it, but, you know, I can� I can talk about it now. [Laughter].
BREE HALL: You know what, clearly you have used it before because you had the IntelliSense. You were basically a Tailwind developer.
JASON LENGSTORF: Basically a Tailwind developer. [Laughter]. Don't tell anybody who actually writes Tailwind. [Laughter].
BREE HALL: Yeah. It's okay.
JASON LENGSTORF: So, this is great. Let me� let me send one more round of links to you and your stuff, here. So, make sure you go and follow Bree on all the places. We have been talking about Storybook today, so we'll do a Storybook.
BREE HALL: Yes. And if this is something you would be interested in seeing more of, I've been toying with the idea of doing a YouTube series about this thinking and I would love to do it in more longform.
JASON LENGSTORF: 1,0000%, yes. Definitely do it. Yeah. It's coming in already. Make that happen. That's a great idea.
BREE HALL: Perfect.
JASON LENGSTORF: All right. So, let me do one more round of shoutouts to our captioner, we've had Vanessa from White Coat Captioning doing real, live human captioning and that is made possible through the support of our sponsor, Netlify. Let the sponsors know you appreciate them making this show better for everybody.
We've been using Tuple. You saw Bree take over and copy and paste. We can even do fun stuff, like draw on the screen, like, what up? It's great.
[Laughter].
All that is due to Tuple. Thank you, Tuple, for sponsoring the show. I really do appreciate it. It makes it possible to do this every Thursday.
Going into the end of the year, it's going to get a little bit weird. I have� I'm going to be in San Francisco next week. We're going to do a live recording of Web Dev Challenge at the Sanity opening party for Next.js Conf. I'm going to be an advisor. We're going to have some folks for Sanity.
After that, I'm at GitHub Universe. Bree, are you going to be there?
BREE HALL: No, not this year.
JASON LENGSTORF: Bummer. Hopefully� hopefully, we'll be able to hang out, inperson, somewhere soon.
And then, then I'm going to Houston and we're doing another inperson Web Dev Challenge. This time with Intuit and the Render team. It's going to be a little sparse schedule of Learn With Jason. You can find things wherever you like to subscribe to things, Discord, email, calendar, et cetera, et cetera, et cetera. If you have been enjoying this, hit the "like" button. Subscribe.
Bree, thanks for being a part of this.
BREE HALL: Thank you for having me. This has been so much fun and fun fact, I was actually� you� words. You have taught me so much on so many different platforms. I was very intimidated to come on because I was like, what am I going to teach Jason what he doesn't already know?
JASON LENGSTORF: You taught me a lot and I'm very glad you came in. Chat, thank you, as always, for hanging out. We're going to call this one done. We will see you all next time.
BREE HALL: Yes. Thank you so much.
Learn With Jason is made possible by our sponsors: