Designing a Component Library

Adam Wathan discusses designing a flexible component library using Tailwind, sharing lessons learned from building components over five years.

Transcript

00:00:00 amazing to be back at laracon I feel like I missed one laracon and somehow I haven't been here with this group of people in half a decade now that's pretty crazy I had a lot of fomo last year in Nashville um so really excited to to be back here today so um I started working on Tailwind full-time about five years ago I think it was more than five years ago I guess it was beginning of 2019 and in that time I have built an ungodly amount of components and there's a lot that I've learned about doing that

00:00:37 in a way that makes them both really sort of opinionated and easy to work with but also hyper flexible and not limiting or frustrating to use at the same time so today I'm going to share a bunch of those Lessons Learned some opinions some ideas some tips and tricks and probably a few horrible crazy awful things that are going to make you regret ever taking advice from me in the first place at all but that's what makes it fun so let's get started so say you needed to build like an interface like this with these types

00:01:14 of like form components and stuff like that labels and inputs and selects and descriptions and text areas and checkboxes buttons that sort of thing um intuitively uh for a lot of folks myself included I've built things this way in the past um it's easy to think that maybe the best way to approach this is with apis like this where you take sort of the label and the component and the description you bundle it up into sort of one opinionated thing that handles all the layout kind of makes everything

00:01:43 work together all the knowledge is in one place about what should be happening so your code can make all the decisions um that it needs to make but every time I've tried to build something this way I've it's just bit me and I'm going to walk through a couple examples of how so here's what it might look like we've got this input with a label and a description this is maybe the top level API for working with it uh say we wanted to constrain the width of the input though in in some way intuitively you might think okay well

00:02:14 why don't we add like a size prop um that specifies like how wide we want the control to be but then you realize ah you know what I kind of want this to be full width on mobile but I want it to be constrained on desktop because the layout's changed in some way so you can't just do it with a prop because a prop can't really change responsively so what do you do you know you try to do something like okay let's set the max width of this thing at a certain break point um and you stick that right on the input and then next

00:02:42 thing you know my description wrapped I didn't want that to happen so then you start adding a props like okay well let's add an input class name prop so we can add some stuff just to the input control itself but not affect anything else okay that works but what if I want to move that description to the top of the control and instead of the bottom of the control I have no way to sort of control that from this API so maybe you add a description placement prop and you specify where it should go um next okay maybe this this is

00:03:12 subtle did everyone catch that we don't want people to have to type that dollar sign in this currency field so we're going to add a little icon to represent it so everyone knows that that's already handled for them and they can just type the numbers so how do we add this icon okay well maybe we'll add an icon prop we'll pass any the icon that we want to use and the component will take care of everything for us but now we want like another icon on the other side with a tool tip and we've already used the icon

00:03:37 prop so so what do we do well maybe we rename that to Icon left and then we add an icon right prop and you know things just sort of start spiraling out of control what if we want to have a different layout we want to put them next to each other for one specific part in the app well our component didn't really handle that so now we end up with like a layout horizontal prop and you can see it's just prop City you know prop after prop after prop after prop after prop it I don't know it it just feels like you

00:04:02 have to go and edit this thing over and over and over again and you don't have sort of a composable system that you can work with so instead of apis like this what I found is the most sort of flexible and Powerful way to build component systems like this is with apis that look more like this it's a lot more verbose it looks a lot more just like plain HTML but in practice I found I could do a lot more with this so that's the end of the slides that's probably too many slides for a talk that I'm going to give anyways

00:04:31 um so let's just do some programming and I'll kind of show you how I would build this sort of thing and walk through a bunch of different examples okay so I probably need to bump up the font size here yeah how's that is that okay okay so here's a little oh dude when you plug these things into like anything your cursor like stops changing on hover in places where you want it to change you have no feedback but if clicking in the right place we will make a work though okay so all I've got here is a

00:05:03 couple components just pre-built that are just Styles you know it might look a little complicated I'm using react here but I promise nothing I actually talk about in this presentation is specific to react at all we're actually going to solve all these interesting problems with just Tailwind interestingly enough so all this stuff is going to work anywhere um so I've got just a label an input and a description and all they're doing is applying some default Styles so one thing you'll notice is um I'm just

00:05:30 stacking them up down here the spacing around them isn't quite right everything's a little bit too tight so if I wanted to space things out you know one way I could do that is adding like a space Y 2 or something right and that looks pretty good to me uh but now if I move the description up to me that doesn't really look good anymore I feel like the gap between the label and the description is actually way too much um so how can we actually handle this we don't want the same space between everything we kind of want more

00:06:03 fine grained control so I'm going to head over to the actual field implementation and uh we'll work on some classes there actually you know what let's just do it uh yeah we'll do it there we'll do it properly you know okay so wrap this guy up and wrap that in a template tag here template lital we'll just start dropping some classes in there so really we want to do here is um style each thing on its own so instead of using the space stuff you can do some crazy stuff in Tailwind if we just want to Target

00:06:40 specific child elements using what are called arbitrary variants so uh let's go back to how things were before and say we wanted to add that margin above that input control again so one way to do that is basically to use square brackets here Ampersand angle bracket input and just say we want the input that's a child of this element to have like a top margin of two okay and that's going to give us our top margin and then we can do the same thing for the description now this is where things get a little tricky because we

00:07:11 don't even like know what element that is you know I know under the hood that it's a P tag um so I guess that works it feels a little weird to me but it gets even weirder if say instead of this input I wanted to use like the select control that I have built now we actually like lose that margin and to make that work I'd have to make this feel component know about all the different possible children that could go inside of it so the first pattern that I want to talk about is something we stumbled upon when uh building

00:07:38 Catalyst which is using data attributes to sort of Mark things as a sort of particular type of element um from the perspective of the parent so in this case the one that I use for something like an input is I'll go up here and I'll basically do something like data slot um and I'll call it control so this input is going to fill the control slot of anything that is inside of it and similarly label I'm I'm good with calling that one just label and then description I'm good with calling that one just description control I think

00:08:16 needs to be a little bit more generic because there's multiple components that we could be using right so now instead of input I'm just going to say data slot equals control and then instead of P I'm going to say data slot equals description and now we have the same thing but if I replace this with the select you know we have the same uh spacing we didn't lose the spacing so now let's move the description above again switch us back to the input and uh figure out what's going on so now we want to sort of

00:08:49 remove that spacing above the description contextually so the way to do that is rather than try and undo it in this specific case that's only at it if the description directly follows the control CU that's actually the only situation we need to add it so things are going to get a whole lot more fun and Wy here um so if there is a control followed by our description then we want to add that space otherwise we don't want any space and I actually think like that amount of spacing looks pretty good to

00:09:24 me um yeah so now we can sort of move that stuff around and that's pretty cool and now we wanted to solve that problem of the constrained width on the input what I would do is and you can see up here I've already kind of set this up but this is a controversial thing by the way a lot of people think exposing class name on components is kind of like a bad practice because there's a lot of classes that you can add that will conflict with the classes that are built into the component personally I think of

00:09:52 it as just a very sharp knife and as long as you sort of know what type of things are safe to use then I think it can very powerful for me the sort of things that I consider to be like allowed categories of classes to add to a component like this are things like margins because I never bake margins into components I always handle those contextually and then things like Max width which is layout related so stuff that's sort of contextual based on where it's going to be used so now uh I can just add you know

00:10:22 like a Max WID 32 like we were talking about in uh the slide portion and if I only want that to happen at a certain break point I can set that to only happen at a certain break point and if I've given myself enough space here oh I'm not getting the cursor hint there we go you know you can see it only gets constrained there and you can see like the description doesn't wrap because we're able to do that um directly on the input right so that slot pattern I found to be um a real major unlock for us when building uh this sort

00:10:53 of thing because uh of the reasons I just mentioned especially the thing with the select you know it's not hard coding this around inputs it's just this is what the spacing around the control should be and I'm going to go through another demo now that is kind of going to show why that's powerful as well so the other thing that we were talking about was the sort of pattern where you have like an icon on the left and then maybe an icon on the right and stuff like that um the way that I would would do that is by creating a separate

00:11:19 component I've kind of got the boiler plate for it down here I'm going to call this an input group and let's wrap this input with our input group we'll get rid of the max width on that for now and then I'm just going to slap in the dollar icon which I've already got imported so this isn't looking that great um I would say right but it's a starting point so I'm going to open the icons file for a second one thing I'd like to point out is just like for all of the other controls um icons I always give

00:11:57 them this data slot equals icon so I know know I can reliably Target any icon again you could just Target the SVG directly but for consistency reasons I like just giving everything a slot and targeting the slot so let's style this guy I'm going to do this with multiple lines of classes in an array because it's going to be a lot um so first let's um kind of just set things up in general Okay so the way that I've found to be the most effective way to build these things is using a CSS grid and a cool property of

00:12:37 CSS grid which is that multiple things can reside in the same column so I'm going to make this element a grid and for like the grid columns I'm going to send it to uh spacing 10 so fun note here I'm using Tailwind 4 the alpha of Tailwind 4 for this my favorite thing about Tailwind 4 is that all the things in your theme are automatically made available as CSS variables so you have access to them when you're doing stuff like this which is kind of neat and then we're going to do one frame in the middle and then we're going

00:13:11 to do another space on the other side because inevitably we're going to try and add that that other icon right okay so we've got a grid it's got three columns in it you can see the dollar sign is taking up the First Column the input's taking up the second column and there's an empty column over here so the first thing that I'm going to do is start kind of just putting things in place um so let's target the uh data icon slot and we're going to say just to be explicit so everything works the way it

00:13:49 needs to work we're going to put that in the First Column and we're going to put the control uh and it starting in the First Column but spanning all three columns and we also need to specify the row because by default grid is going to be like wait wait wait wait wait are you are you telling me that you're trying to uh put two things on top of each other you definitely don't want to do that right you must be wanting to put it in the next row down and then the icon is going to be like wait wait wait you put

00:14:27 that other thing in the first row no no no no no you don't want two things in the same row right so let's uh let's be real clear and tell the browser no trust me dude we want to put these things on top of each other and it's like okay but like you understand that that's hidden by the input now right and that's when we say Ah that's what the index is for man like we know what we're doing here like don't worry about it okay so that's kind of in the right spot now right um so let's go ahead and actually like style um the

00:14:57 icon so I'm going to say Place self Center to make sure that that like goes in the center of that column that was really subtle it would have been more clear what was happening if the icon was smaller but uh let's give the icon a size too maybe make it like four and let's give it like a fill of like neutral 500 or something like that okay so so there we go and now if I click into the input uh you can see I'm like typing on top of the icon that's a little strange right so how do we sort of account for

00:15:30 that for that space so this is when we start getting into that crazy like sibling selector stuff again if you were around for Caleb's talk this morning he kind of hinted at a lot of these types of techniques when he was introducing flux so I think it's kind of fun to sort of expose how all that stuff kind of works under the hood and all the sort of uh crazy things you have to do to make these things as easy to work with from the from the outside as uh as you kind of want them to be so in this case we

00:15:55 could just do something like okay well if there's a data slot icon follow by a data slot control then maybe we want to add like way more padding to the input and that like seems pretty good now right so let's try and do the same thing with another icon on the other side I'll throw in this like help icon and we're going to go and import that guy and I only got 30 minutes so I'm not going to make it a real tool tip or anything like that we're just going to pretend that that works but we can see that's already like not working and

00:16:27 that's because we're always putting icons um in the First Column actually we only want to put an icon in the in the First Column if it's the first thing so let's say if that icon is the first child we'll put it there um but if it's the last child and where did my uh dude when you have to zoom in like this and even an individual class name wraps oh man it is fun times so the last one should start in the third column now the is I think we're going to be able to type on top of that one too right which is no good so this is where

00:17:04 things get sort of really interesting so in CSS it's easy to Target an element based on its previous sibling historically it's been very hard to Target an element um based on a following sibling so we need to change the padding of the input if an icon comes after it right so buckle up uh I'm going to show you some really modern CSS stuff uh that'll make this possible so basically we're going to check so has has anyone used has in CSS the has pseudo class pseudo selector a few people not too many which is good

00:17:42 otherwise this would be pretty uninteresting so has basically let you say style this element if something about the children is something you know um so we're going to say if this element has a data slot equals control um followed by a data slot equals icon then we want to Target the control and give that some right padding now let's pray that I got this right but in theory there we go so now it doesn't override that right [Applause] okay so one thing you might have noticed uh since we started working on this is

00:18:34 we lost all that nice spacing that we had in the field control um because of that whole slot abstraction we can solve that entire thing by just going and saying okay this input group this is also a control from the perspective of a field and now we automatically get that spacing so an input group can sort of stand in for an input and uh you know everything kind of works so that is how I kind of like to handle this sort of um form stuff okay let's go back to another screen and by back I mean go to it for

00:19:10 the first time and here I've got that little asking price thing that we built um it's not the exact same thing I have it built in a separate file this one still has that Max width applied and we got rid of the icon but I want to show you a bug that we've introduced accidentally here Watch What Happens pay close attention mention as I start scrolling this thing what how many times have you seen something show on top of your sticky nav bar has anyone experienced the polluted sticky nav bar bar before

00:19:41 yeah so what's the solution to this problem well most people are going to jump into that navbar component and they're going to be like well I guess we just got to do like I don't know this is that the yeah it's problem solved right no no that is not the solution not the solution you want to see the real solution nobody knows about this a few of you know about this I would bet thousands of dollars that less than 25 people in the audience know about the CSS property okay um I'm going to jump over to the

00:20:19 other implementation of this the one that I built up for um behind the scenes here watch what happens if I just type isolate what it goes behind it and I didn't have to add any Z index wait a the Navar the Navar has the same Z index as the icon it's earlier in the Dom and somehow it's on top and uh the isolate class uses the CSS isolation property and what that does is basically create like this sandboxed area where any zindex that you specify is sort of sandboxed to that element so the element that has isolate

00:20:58 on on it behaves as if it has Z Auto basically and the Z10 is sort of like a relative Z10 inside of it so this isolate thing I swear to God this solves like almost every zindex problem you've ever run into in your entire life I almost never need to think about zindex other than using like Z1 and Z1 and z0 and then just wrap things in isolate and let Dom order sort of take care of everything else lifechanging thing and you might think it's new it's been around since Safari 8 there are people in this room who are newer than Safari

00:21:32 8 okay all right what else do I got to show let's check out the notes here okay another cool one um in this Navar I've got this like drop down right let's hear for drop downs right okay cool thing um that I'd like to do with this drop down if if you go back to the nav bar you can see the way the drop Down's built same same sort of pattern right it's this big compound component with all these child components one thing I don't love about this drop down right now is if I want to have no icon for one of these elements

00:22:08 the text like moves over this is horrible this is a crime you should be able to be arrested for this so one way to solve this right is to build this in a way where you're always leaving space for the icon and maybe position the icon absolutely when it's present so there's always a gap there but wouldn't it suck if you did that and then none of them had an icon and then instead of this where the text is aligned all the way to the left you'd have this big weird Gap that you had put there um assuming at least one thing

00:22:38 would have had an icon so I'm going to show you some cool CSS stuff um for solving this problem too so let's head over to this drop down menu so if you take a look at the drop down item most of this stuff isn't really important for what we're talking about the parts that are important is this Flex box stuff so we just built each thing as a flex container it's got an icon and some text you know no big deal um first step to getting this working is let's convert this to use grid so instead of flex I'm going to say

00:23:11 grid and then for the columns the First Column is going to be an autosized column so it's going to be sized based on its content and the other one is just going to expand to take up the rest of the space if I did this right I don't think anything should actually change here it should just look the same and if you go back to the nav bar like none of this Behavior should change right you'll still see like things move over and stuff like that but Watch What Happens now if I hoist this grid up to the parent instead

00:23:42 of defining it on each item because because what we need to do is design things in a way where everything is somehow related and there's some sort of common base layout where each thing is influencing other things right so I'm going to hoist this up to the parent I'm going to copy this I'm going to go to the drop down menu itself which is the thing that's holding all of those and instead of making that Flex Flex call we're just going to do the same thing grid grid calls Auto okay it's looking pretty bad I know this isn't right let's

00:24:11 jump back down here and we'll say call spin uh full on each one of these guys so it fills up the entire space you probably can't see this but there's like a little separator at the bottom there um we're going to want to do the same thing there to make sure that fills the full thing and again okay nothing has changed in terms of the behavior here if we go back and comment out an icon same same sort of deal but Watch What Happens now has anybody used subgrid in CSS before a handful of people so the the roote

00:24:47 problem here is we can't just use like a big grid for this because each one of these elements is wrapped in a container and we need that for the whole thing to be like a link to have a hover State and stuff right like that's what this dropped down item is the user icon the drop down label they're not grid items in the drop down menu parent they're grid items in the drop down item grid but using subgrid we can basically make drop down item almost like a transparent wrapper that inherits and influences the parent grid and it sounds

00:25:16 kind of confusing but if I show it to you I think you'll sort of understand what I mean so instead of explicitly saying that this should be using um Auto and 1fr I'm just going to say subgrid and nothing's going to change yet but now if I go back to the nav bar comment on icon we should see a slightly different Behavior okay so now the text is sitting in the same column as the icon you know and this is kind of interesting because to me this says Ah we can make one small tweak and make this whole thing

00:25:49 work let's jump back to the dropdown and take the drop down label which is where the text is and let's just say this should always start in the second column okay so now we're reserving space for that icon and if you go back to the nav bar and comment out all the icons the space disappears so as soon as we introduce one icon the whole menu sort of adapts to it and keeps everything aligned sort of like you see in like your Mac OS menu bar or other nicely designed like thoughtfully designed drop time

00:26:22 menus so that was a really um a cool trick I discovered when we were working on a component Library uh last year subgrid I feel like is one of those CSS features that until you run into a spot where you need it it's impossible to understand like what it could possibly ever do for you and then as soon as you do need it it's unlocks something in your brain you're like wait a minute there's like a whole class of problems that I think this this can solve um so it's pretty neat that we're able to create these sort of relationships like

00:26:48 using pure CSS none of these elements are coming from some individual parent component that's just taking in a bunch of data and spitting out a layout they're all just composed together but they can still talk to each other using CSS features like subgrid that can sort of create uh these relationships okay another one right now this drop- down button is you might not be able to see that clearly but that is 32 pixels by 32 pixels it's nice and big if you're using a mouse it's not very big if you're

00:27:23 using your finger okay so I think it's the Apple human interface guidelines recommends like 4 4X 44 is the minimum size for touch Targets on mobile and this is sort of a tricky problem to solve in a way that doesn't just totally suck um one way that you could solve this right is to just add some padding around the button go to the parent nav bar sort of try and compensate for that padding um stuff like that or use like some negative margin around the button to sort of eat into the parent space and still take up

00:27:52 a bunch of space I'll show a solution that we came up with that um feels a little less intrusive to me that I I really like so what I'm going to do is I'm going to add a span inside of um the drop down button and I'm going to make this absolute positioned and let's just make it like pink 500 so we can actually see where it is and what it's doing can't see it yet I'm going to put this halfway from the top and halfway from the left and just so we can find out where it even is on the screen let's give it like a size

00:28:29 so that's totally in the wrong spot right that's not relative to the button at all let's add relative to the actual button okay so now you can see the middle of this square is in the middle of that button uh I'm going to use negative translate 12 and that'll move it so it's perfectly centered and we could just go ahead and say okay we want this to be 44 pixels and now we basically have like a touch Target you know I can click on the edges even though the Avatar itself isn't there and and this still works the thing

00:29:00 that sucks is if the icon is wider um the these Dimensions just kind of feel like so arbitrary I a technique that I kind of like is to use the CSS Max function and basically say I want the size of this thing to be the bigger of 100% or 44 pixels so now if the icon itself um is bigger and maybe if we go to like the nav bar I can I can demo this if we make this like 12 you know now this is going to be the same size so 48x 48 that is the size 12 but if I set this to like size one now it's going to it's going to be

00:29:45 clamped down to like 44x 44 which is kind of cool so let's undo that back to what whatever it was before eight and let's get rid of um the pink background so it's just like in visible and now you can see I can click around the edges to open it now that's nice on mobile I kind of find it to be a little bit weird um on desktop it's unintuitive to me that when I have a very fine cursor like this that clicking outside of the button would actually open it so you might think okay well why don't we just like only show this on large

00:30:20 screens so maybe we'll like hide it um on mobile screens um or sorry well other way around right SM hidden so we'll only show it on on large screens but the size of the screen isn't really what we're talking about here we're talking about whether you have like a fine grained cursor or not and there's a way to do that in CSS to um using this media query we really need to add this to tail when it's like a first class thing but we can do it with an arbitrary media query so we can say if there's a fine pointer

00:30:50 which is like a mouse then this should be hidden and you'll see if I hover this element it just like doesn't show up but now if I go up here and I set this to be like touch and then I hover over that element oh where is it it's right right here now it's visible so that extra area is only going to be present in the Dom if someone is on a touch device um and we're able to just like comment out this element and all that behavior goes away we didn't actually have to like account for it with the sizing in the button or

00:31:19 the sizing in the Avatar it's like a discret concern that just handles this problem in a composable way which I find to be like I don't know just really fun and cool so I don't know hopefully that's kind of an interesting fun one too okay so last one I'm going to scroll down to the bottom here and check out these two buttons okay they're just basic buttons the same type of buttons you've seen anywhere what I want to kind of talk about is creating sort of component variants and how to sort of customize

00:31:54 components from the outside in different situations that you need to do that so if you open up the button component I'm doing sort of your classic color variant thing here right where you can pass in a color prop if I want the gray button we grab the classes for the gray button and throw them in there if you grab the black button we throw the classes for the black button in there no big deal I think this is a great use case for using a prop to sort of customize a component one that I think that's a

00:32:22 little bit more controversial that I want to kind of convince you on is this situation so here we have an avatar and you can see I have like a a size prop on the Avatar so we have like a constrained set of values we have small avatars medium avatars large avatars and this works fine until you need to change the size of the Avatar at a different break point because there's no way to just change the value of a prop at a different break point so this is actually a situation where I probably would not use a size prop if I ever put

00:32:55 it this way if I ever ran into a problem in my app where I needed to change the size of an avatar responsibly and I had a size prop like this I would immediately delete that prop uh and just refactor everything to use like custom sizes um using a class name because using a class name you know we can say all right I want this to be size eight and then on small screens oh sizes not defined yeah yeah yeah yeah that's fine uh and then on small screens I want it to be you know size insane I don't know

00:33:26 uh like that big no one would ever do that but we can do it and that's what matters is that we have the freedom to do it you know um so that's a simple example this is where the talk gets dangerous risky scary terrifying um where you'll start to doubt and question any advice I've ever shared and any talk and any blog post and any video um but I think it's pretty it's pretty fun so we're going to do some stunt programming but it's also really powerful and interesting and useful and I think you'll take something

00:34:06 interesting away from it okay so we got a table here right this table is horizontally scrollable I find this to be like a nice simple pattern for handling tables that have an unknown number of rows where you just need it to work on mobile you just need all the data available right but you'll notice that the text gets like truncated over on the side here um which I think is kind of a bummer and it gets truncated on the left over here too right so a pattern that I've used historically in the past for solving

00:34:34 this type of thing is to add some like negative margin uh to the table and do a bunch of crazy stuff with a bunch of rappers and God knows what sort of voodoo to make it scrollable the way I wanted to make it this this would be a full day conference talk if I wanted to show you how to build that from scratch but at the end of the day you can do something like specify the gutter size that you're trying to basically account for and we have like a table that can handle that so it doesn't get truncated

00:35:01 anywhere right and this works like pretty pretty nicely where it breaks down though is if the gutter size on the page like the page padding changes at different break points for the same reason I was talking about before if we bump this up to the next breakpoint I've increased the size of the page padding and now you can see this gets truncated again which sucks I could bump this up to 10 to account for that on this and now it works again but when I go back down to this break point we have a way worse problem this is a

00:35:30 huge crime which is that when we're done scrolling we can scroll a little bit more we no one feels good when they see this happen on their website when there's that six pixels of horizontal scrolling that is I don't know that's maybe the worst feeling I've ever had as a web developer in my entire career so how do we solve this problem let's take a look at the implementation of this table so you see I've got got this gutter prop and to sort of keep things simple and remove some duplication I'm setting a custom

00:36:04 property inside the table component um to whatever the value is that comes in and then I'm setting a negative margin to that Gutter and I'm also setting some padding to that gutter to sort of account for it and do all this bookkeeping so things like scroll properly similarly to what I was talking about with the Avatar um if we just like get rid of this get rid of this prop and handle that whole gutter thing from the outside ourselves we can just do something like set the gutter variable to VAR spacing six and then on small

00:36:42 screens well uh let's not spoil it let's go step by step so that's working right but now I have that same problem on bigger screens where it's truncated but now because I'm just using a CSS custom property instead of just a regular prop I can just change this to match um the page padding which is 10 at that screen size and now we have no truncated stuff and we have no horrible extra horizontal scrolling so I really find this to be a fascinating pattern where you can basically take a prop represented as a

00:37:13 CSS variable and now you have a responsive prop instead of just a regular prop which I don't know I think that's pretty cool that's not the scary horrible horrific thing that I was going to show you that's what we're going to talk about now so please cross your fingers that I can remember this because it involves a lot of text and a lot of math and a lot of it's going to be it's going to be fun what if we wanted to be able to make this table full width sometimes okay um I'm going to add a custom

00:37:48 property here called bleed to say we want this table to bleed all the way to the edge I'm going to set it to one which is how we're going to have to represent like a Boolean true prop with this problem and now we're going to open this table component and we're going to pray that I can remember all of the Cal stuff necessary uh to make this actually work so first what I'm going to do is see how we've got this like horizontal padding that's like ACC counting for the gutter if we want the table to be full

00:38:22 width we don't want that to be there right so basically if bleed is zero we don't don't want to apply this gutter so we're going to use math to do that so we're going to wrap this in a cal call we're going to multiply it by the bleed value but we don't want to multiply it by just the bleed value because we actually want the want it to go away when bleed is one so we need to multiply it by sort of the inverse of the bleed value so we're going to multiply that by one minus the bleed value okay so now we've got like an edge

00:38:57 Edge table but obviously like that's not what we actually want right we want this text to line up with that heading text this is where it gets really interesting so to keep this readable we could do what I'm about to do in a arbitrary uh value in Tailwind I don't think it would be good for me to do that and have it exist on the internet because it would reflect extremely poorly on this tool that I've been working on for the last several years so I'm going to create a uh CSS variable here called um Edge padding or something

00:39:34 I don't know it doesn't really matter it's kind of a private internal thing but we're going to do the math to figure out what should the padding be on on each cell here so let's go down to the bottom and check so each cell um already has it set to Edge padding I guess so this is what happens when you're like preparing a talk and practicing things and forgetting to undo things um but what this was set to was uh one okay so there's uh what did I do now oh yeah this don't worry about it it's just react I know you guys don't like react

00:40:09 but inertia to and react I'm telling you it's a fun stack okay so there's a tiny bit of padding on the edge right it's like a quarter Ram it's like four pixels so I'm just going to undo what I just did basically let's replace this with that edge padding thing and just move that value up to the Declaration here so it's going to be VAR spacing one and that should be like the same thing okay so nothing's changed but if bleed is true we don't want this to be a quarter RM we want it to be equal to the

00:40:45 gutter size right math time so let's Cal and what we want to basically do is because we can't conditionally not apply the space one we need to add the difference between the gutter and the spacing that's already there so let's read the bleed variable we'll default to zero in case the person hasn't actually set it and if it's set to one then we want to basically apply um some additional padding and that additional padding if I if I'm getting this right if I'm think about this right should be the

00:41:31 gutter minus the spacing that's always there and that's how we're going to get like the difference right oh did I actually do that on the first try unbelievable okay so so we got a full width table that's scrollable right and if I go back to here not only can I just like make it not full width like anytime I want and now it's just like a regular table that's scrollable with the the gutters sort of matching the page gutters I can make it full width on mobile but then on larger screens I could say no I don't want it to be full

00:42:06 width so I'll set bleed back to zero so this is a full width table now everything goes edge to edge looks kind of cool and then when we get bigger now it like respects the page padding and again this is like a really interesting technique because this was would have been impossible to do with regular props because we need this value um to change responsibly you could do with regular props if you're doing like resize observers and doing all this crazy JavaScript stuff but like none of the stuff that we did today needed

00:42:30 JavaScript at all it was all just pure CSS so anyways that's my last uh crazy weird um designing component Library spacing layout fun weird mad science experiment uh demo hopefully you learned something interesting from some of these demos some techniques that maybe you'll take away and apply uh you know when you get back to work and start building stuff maybe you can simplify some code I think the coolest thing about this technique is before we implemented this we had all this crazy react context

00:42:58 stuff and drilling variables through everything and even when we did that it wasn't as powerful as this because that still wasn't responsive so I don't know this I thought was a really cool technique that unlocked a lot of things um for me again hopefully you learned something uh cool here uh thanks so much for coming to larom thanks so much for coming and checking out my talk and if you use Tailwind thank you so much for using Tailwind I've got one I've got one weird ask I was debating whether or not I

00:43:26 should do this but I think could' be kind of fun so this is the first talk that I've ever given where my daughter has seen me speak my daughter Parker is in the front row right now she's 6 years old tomorrow is her birthday and she turned seven would it be too much to ask the laran audience to sing Happy Birthday to my daughter with me you got to lead us you got to lead us all right you ready 1 2 3 happy birthday to you happy birthday to you happy birthday dear Parker happy birthday to you thank you so much I really

00:44:21 appreciate that if anyone wants to chat about any of this stuff come find me after the talk happy to happy to chat as always happy birthday well done um so I have just one question for you um having observed you for a long time you have both a very low pain tolerance you don't you don't tolerate any paper cuts y but you have the ability to eat glass for a year making it right which is a very high pain tolerance how do you square how do you square those two things the second one is born out of necessity because of the first one I

00:44:54 think Taylor would tell you the same thing that's something we talk about a lot I think um having that low sort of developer experience pain tolerance is a required ingredient for having like the grit to sort of push through and make things the best they can be at any cost you know yes so yeah well I think you and Taylor share that y'all give it up for Adam Wavin


Highlights

πŸŽ‰ Excitement about returning to Laracon after a long break.
πŸ“¦ Importance of building opinionated yet flexible components.
βš™οΈ Challenges with complex APIs and the need for simplification.
🌟 Using data attributes to enhance component flexibility.
πŸ’‘ CSS features like subgrid unlock new design possibilities.
πŸŽ‚ Celebrating his daughter’s birthday with the audience.
πŸ’¬ Open invitation for further discussion on component design.

Key Insights

πŸ”„ Flexibility in Component Design: Wathan emphasizes that components should be designed to be both opinionated and flexible, allowing developers to customize them without compromising usability. This approach encourages a better developer experience.
πŸ“ API Complexity: He warns against overly complex APIs that require excessive props, which can lead to frustration. Simplifying these APIs can result in a more intuitive and maintainable codebase.
🏷️ Data Attributes: Leveraging data attributes helps organize component functionality, making it easier to manage spacing and layout without tightly coupling the API to specific components.
πŸ“Š Responsive Design with CSS: Wathan highlights CSS properties like max() and subgrid that facilitate responsive designs without relying on JavaScript, showcasing the power of modern CSS.
πŸ› οΈ Custom Properties for Responsiveness: Using CSS custom properties allows for responsive adjustments to component layouts, enabling developers to create adaptable designs that respond to varying screen sizes.
πŸŽ‰ Community Engagement: The talk emphasizes the importance of community, as Wathan invites interaction and celebrates personal milestones, fostering a supportive environment among developers.
🧩 Continuous Improvement Mindset: His low tolerance for developer pain drives a commitment to refine tools and components, highlighting how personal experiences shape professional growth and innovation.