Design Systems: Walking the Line Between Flexibility and Consistency

Bookmark

Design systems aim to bring consistency to a brand's design and make the UI development productive. Component libraries with well-thought API can make this a breeze. But, sometimes an API choice can accidentally overstep and slow the team down! There's a balance there... somewhere. Let's explore some of the problems and possible creative solutions.



Transcription


Hey, I'm Sid. I work in the design system team at GitHub, and I want to talk about walking the fine line between flexibility and consistency. So let's start directly with code. I have this alert that I've been looking at other design systems for inspiration, for api ideas, all of that. And I was looking at this alert and trying to build it with shopify's Polaris and Material UI's component library. So this is kind of how they differ. So with shopify, they call it a banner. So you render a banner, and then it takes a status. In this case, status is critical, and it takes a title. So it takes these props as config, right? So the title doesn't go in the child. It goes on the component as a prop. And then you have the action, and you only say what is the action and what is the content that go inside the action. You don't really get to pick the action per se. And the component takes care of where it's rendered, how it's rendered, what color, the border, all of that. So this is an example of a config api, which is very tightly controlled. And in fact, if you try, I played around with this, but I tried to give a style tag. I tried to give it different class names. It doesn't accept any. This is what you get. And then when I tried to build the same thing with Material UI, they had a severity as well. They call it error not critical, which is all the same. But you could give it any icon you want. So I was able to import an icon from the Material icon and pass it. I could also give it different styles. So I could customize the border and for the contents inside, I had to take the alert title, which they export, and take a button and try to make the button look exactly the way I wanted. Plus the space in between, I had to do it with the margin in between the components. So that's just a tiny example of how the flexibility can be so different between two components. If you compare the api of these, the shopify one is super tightly controlled versus the Material UI one is you can customize basically anything you want. And that kind of brings us to this line or the spectrum of flexibility where Polaris is probably somewhere here. So it's very open united. It's built for shopify. It's not built for everyone. It has a very strict api and that leads to predictable and consistent outcomes. When you use that banner, you know exactly what's going to come out. But then it could become rigid and restricting for experimentation or different use cases. On the other hand, the Material UI one is probably somewhere on the right edge, maybe even all the way there. And that's because it's open source. It has an extremely flexible api because it has no context of where it will be used. So it needs to be flexible so that you can fit it in the product that you're building. And then of course, with so much customization, it can become messy and fragmented because well, the component library doesn't have a lot of design decisions baked in. All of those lie in the product side of things. So taking this, it's kind of like what is right for you? Where should your component library fall into the spectrum of flexibility? And the answer is that it kind of depends, doesn't it? Always. But this is what it depends on. It depends on, is your component library built for a brand or is it built for open source, which is like everyone? What's the design culture in the brand that you work for? Is it consolidated with a small group of designers or is it a bunch of independent teams that operate pretty much on their own? And even what's the maturity of the products that you're working on? Do you have established patterns or is it still early and it's more experimental, still playing around to find those patterns? So the answer then basically is it depends, right? Thank you for coming to my talk. Bye bye. But no, I try to use these and try to apply them to this component. This is an action list component inside an overlay and you'll see this spread across GitHub. There's a lot of variations of this, but this is a pretty involved component. So it has a bunch of variations and this is some of them. So you see this on the repo page. Just to the side, you see this as options in a dropdown. You see these as in the releases panel. This is the releases one. You also see them as trying to select assignees or reviewers. So there's a bunch of, there's a lot of range in this component and I was trying to figure out what should the api look like, how controlled or flexible should it be? And let me take you through that journey so that I can quantify what does it depends means. So let's do this api exercise from the start. I'm building this action list. This is the issues page and quick, quick warning for you. None of these designs are actually production design. These are all from an exploration. So if it doesn't exactly match the aesthetics of what you see on GitHub.com, that's actually intentional. This is all an exploration. Okay. That out of the way, this is the menu that you see and it has some styles. I think it has some keyboard interactions. Yep, there you go. So to make this menu, let's see, the simplest menu over here could be, you have an action list and you just pass it items and the items are these options that you're going to do. Right. So, and then on select, you do something with it. So you can call your own action depending on the string, but it feels a little weird to do these action based on just like the string content. Like if you say quote reply, this is what you get in return. So it feels weird to just act on a string. So maybe something like this is better where it's a config, it's an object and then you can pass in your own select. So depending on the copy of that, you can also pass it what the selection should be and that feels decent. So we move on to the next use case where not all of these have the same actions. So some of these are based on action to, you know, like taking it outside. Some of these are about the content itself. So edit and delete, and this is third, which is, you know, it's a report, it's a moderation thing. So we want to separate them with dividers and now what should the divider api look like? So I thought about it and I was thinking, we already have text. Should it be like a secret text, like a very specific text that you give? Like if you give me underscore divider, then I know how to put a divider or maybe it shouldn't do that at all. Maybe it should be a type, right? And all the other ones are type row and this is type divider. But then of course, you know, like is it small case divider, upper case divider, all of that. So maybe you should import it from the action list and then it's just a value on the object. So it's action list or divider type. And if you're already doing this, then maybe this is action list or divider where, you know, action list divider is an object that is an internal detail that knows how to render the divider. So this feels like a good balance where we're hiding the implementation detail of divider. Nobody needs to know there's a type divider. All you do is put an action list divider there. Feels good. Okay, moving on. Next use case. Some of these are destructive. So delete comment is a destructive thing. We wanted to have a different, scarier style. Could be as simple as adding a variant. So everything is variant default or variant subtle. And this one is variant danger. Seems to fit well. Let's move on. Now, on the right side of the issue, you'll see all of these options. You have labels, you have assignees, you have reviewers. Let's start with labels. So if you look at labels, it kind of is similar. It's an action list with options that you can click. But the difference here is that it has a circle, the label color. So to begin with, I'd say, you know, let's just put all the items here. There's a text, there's an on select and there's a label color. So because I only have text here, I need something to qualify that there should be a circle. So I'm putting this special smart key here, which is called label color. And if you pass it a hex value, it knows that it has to render a color, render a circle with that color. And it takes care of what should be the margin, what should be the gap between the color and the text. Seems smart. And you know, so you do it with all of them. And realistically, if this is being used in a product, it would look something like this. You would loop through repo.labels or like a config of labels and pull the label color out of it. Seems smart. Okay. But when we talk about assignees, assignees has doesn't have labels, it has avatars. So do we just do repo.collaborators, map through them and then you have text and on select, which is the same. But instead of label color, it becomes avatar and it knows it has to render a circle, how much gap should there be. And you just pass the user.avatar URL. So seems smart enough. So we have, it could be a label color or it could be an avatar, right? And the action list component knows how to render both of these. Seems okay. But then you have other things like milestones, which has this icon. You have projects, which has this icon. In fact, if I show you all of these samples, there's a bunch of these. Some of them have icons, some of them don't. Some of them has this indentation. But overall, it looks like most of these are either avatars or they're icons. Some of the are colored icons, but they're still icons. So maybe we just need to know if it's a label or avatar or an icon and that probably just works, doesn't it? So for an icon use case, maybe we just import an icon from our icon library and pass it on. So milestones know how to render an icon. But what happens if you give it an avatar and an icon both? I'm not saying people are actively trying to do this. They're trying to break something. But it's more like if they're trying to achieve something else, like a selection and they want to put a check mark, does this feel like a feasible solution? So you import the check icon and you want to show check mark next to the person it's assigned to. And you will end up with something like this in this case, where there's not enough space for both of them. So they just kind of shift everything and then you're thinking about, okay, how do I create that margin? How do I get more space? Maybe I can override some margin with css. And this api looks like that is a valid direction, even though we don't want you to do that with the component. So this is one of those things where if the api pushes you in a certain direction, it should be the direction that you want. You don't want people to make these decisions, fall into these traps, because they're not trying to break the api. They're just trying to create the selection feature. So coming from that, realize there should only be one prefix. There should only be one visual here and it can be an avatar. It could be a label. It could be the selection icon. And you import that and you pass that as a prefix element. So in this case, you only get one. You get an avatar. But of course, if you get a prefix element, then you also need to pass it a prop. So you get something like maybe prefix props where I have to pass the avatar URL. And if you think about it, this is like if you have an element and props, this is a component. So we might as well just call it a component. So this is prefix, which accepts any component that you want and it tries to put it here. Now, definitely more flexible, definitely more in sync with what we needed to do. But the trade off here is that you could put any component that you want. So this opens up the api more than just three allowed config to put whatever you want. But if you want an icon, then you can. You can import the icon and put the check icon is what I wanted. You could do something like that. So we don't call it the prefix. We call it leading visual. So that's in sync with Figma. Tiny implementation details shouldn't matter. And it kind of works. You can pass it the avatar. You can pass it the label color. You could pass it the milestone icon. So it fits all of these use cases without really breaking the api. So I like it. Let's move on and make it more complicated, of course. So looking at this, so you see labels, actually assignees. We show the name of the assignee right next to their handle. And it has a different text style. And it shows up in line versus label over here. You can put a lot more text into it and it shows in the next line. I think this pattern is also there in milestones, in the next line, projects. It shows the repo or the org in the next line. So I'm just calling it description, right? This is another key. I don't want people to change the text color or anything like that. So I'm just accepting a text field here, which is user.name. And, you know, also works for labels because it could be what's the label description. And then if you want it in the next line, then you give it a variant of block. So it could be inline, it could be block. And I call it description variant. So this is still pretty locked up. Like a config api seems to be, there's like one escape hatch and there's like the element and element variant ugliness, but overall it seems to be doing well. So let's keep going with this. So now let's talk about reviewers because that's a slightly more complicated component. So this is what reviewers look like. And you'd see that in reviewers, you have suggestions at the top and you have everyone else at the bottom. So you have two variations of this. You get, you kind of have groups of folks, which is not something you have in assignees. In assignees, it's just one flat list, but in reviewers it's grouped. So how do we deal with groups here? Maybe we just pass the group. So, you know, if like the user has recently edited, this is a function, could be anything. Then it goes into suggestions. Otherwise it goes into everyone, right? So everyone with that title gets grouped and we know we have to render the suggestions before the first one. Kind of icky, but could work. Okay, but how do we decide what comes first? Like the suggestions come first for everyone. This doesn't really, this isn't reliable, right? Because if the first one is everyone, then does that come first? That doesn't make sense. So that kind of leads us to a slightly different api where we want group metadata to be on the top so that, you know, you can declare which comes first. And then now that you have these, we don't have to rely on strings anymore. We can make this ID, right? So if it's recently edited, then it should be the zero element, which is suggestions. So you have the group ID here instead of group. And if it's otherwise, it's like the other case, which is one, which is of this element, everyone. Seems all right. I also did something else, which is the, this is filled up instead of being plain. So let me go back to the previous one. This was inline, inline or subtle, but the next one is actually filled up and different areas in GitHub use different styles for this. So I think in the reviewers, we definitely use a filled one, but there are other places where we don't, where we use a subtle one. So in this case, we'll kind of have to also say which variant we want. So we want a filled variant for reviewers, not the subtle one. And finally, you know, we want this header, which is, it says request up to a hundred viewers. So I put the header, I put title because that's what group also has. And these are similar APIs. And this is variant subtle and not, this is not filled up. This is always subtle. So it takes the same props. Looks fine. Not super happy with the group metadata and group ID. It's like, you know, you have to create, this is kind of like a side effect of config based api where you have this group ID, but then you have to create another object because you can't fit that in items. It's not items, it's a different hierarchy, but it's killed. So, and finally we have this, which is, we were only dealing with one sort of description. It was either inline or it was block. But here we have both because we actually want to say what's the reason they have been suggested. So we have inline and block both. And so how do you accommodate that? I guess you do this where instead of description, you have descriptions, plural, and you can give an array where you have two texts and then you have two variants. One is inline, one is block. And I don't know if you give three, I guess, then it has to fall into one of these and then the last one wins or something. So you can see, I'm forcing this api to kind of do too much and it is struggling a bit. This api isn't the most intuitive anymore. It started off strong, but not so much anymore. All right. And finally, if you don't want to do this, if you want to do something, there is a point where you say, okay, this component has had enough. And now we don't want to take care of this in the component library. This feels like a job for the product, right? You have to defer responsibility and say, this is like inversion of responsibility and say, this was very product specific. This is not something happens across GitHub. This only happens on this one screen. So this should lie in the product and we won't put it in the one that's common for everyone. So for that, you need to enable some sort of way that they can do this. And I imagine it kind of looks something like this, where the item list accepts something like a render item function, right? This is like a render prop function. And then you get all the internal props and these are props that are useful for all the behavior and hover styles and keyboard navigation. We have a lot of props for this. So we want all of those to get passed down. But then inside it, as long as you pass all the internal props, you can decide however you want to render this. So as long as you use the right primitives, where you use the same avatar that we use and the same text color and text size for this, everything would work out. And this is an approach that a lot of component libraries take where they have an escape hatch, where if things get too crazy, you are allowed to fork out of it and everything's fine. I'm not a super big fan of this approach because you're kind of just deferring the responsibility. And I'm a bit scared of what happens inside this internal props if it's applied correctly. And you can end up breaking responsive behavior or accessible behavior if not done correctly, if not composed correctly. So you can end up building components that don't follow the guidelines, even though you've taken all the building blocks of the guidelines. So all that to say that I don't think deferring the responsibility on the product is always the best idea. It's already that's like the last resort, but I think we can do better here. So here's the other api style. I think we've clearly seen here that this api needs to be a lot more flexible. So it probably doesn't, like the config based api doesn't really suit it well. We need a more composable api. So on the spectrum, we need to be a slightly more towards the right side, maybe not all the way to open source style library, but somewhere in between those two. So this is what it would look like with a different api where I have this action list. And then to render all the items, we used to do this, where we had this array of objects. Instead, you get a different item inside the action list, which is just a component. So because this is all javascript, what you can do is create a component action list, create another component called item, and then attach it, because it's all objects, to the action list. So and then you only export one, which is the parent. So you end up doing things like action list dot item, and it maps to the internal item, which is perfect. So you get action list dot item, you can put the text inside, you don't need to say text prop anymore, because react already has a way of putting content inside an element, it's called children. So you can use the children, you just put it inside and pass the on select. Seems easy, seems to be pretty similar, not that different, seems to work well. Alright, moving to dividers then. This is the next use case. This is how we did it earlier, where we had a few ways of cryptically telling what is the divider. In this case, I think it can just be a component, right? Because you render the action list divider, and just like item, it's a component, and you can put it wherever you want, and that creates the divider right there. Seems the same, not very creative. And then you know, you have this variant, I think I'm going to keep that behavior, variant as a prop seems to make sense, the default is inline or subtle, and you can pass the variant in. Alright, now this is where it starts getting interesting. So for the labels, whoops, selected. For labels, what we were doing was, we had this leading visual, right? And so this is what it would look like by default. You would, inside the action list, you would map through labels, and then use the action list item, give it a key because now it's a loop, we're mapping through it, and then have the on select, and then you know, the children is where the label name goes. So we could just put something before this, like we could put the label color component, but then we would be responsible for you know, what is the margin towards the right and stuff. Instead, what you could do is have action list.leading visual as a component, right? So you see what we're doing here, we're creating all of the child components that can be composed together to create this style. And the children know where they should go, like the styles are already inside the component, so they always end up at the right position. So in this case, leading visual is this area on the left, it knows how much margin to put, it knows that it has to center vertically all the content inside, and then we could put label color. If it's the collaborators list, if it's this list, then we know that, you know, we have to put the avatars inside. And if we have, let's see. Okay, so if we have the milestones and projects, it kind of looks very similar. So moving on to description, because that seems like the very next. Again, does the description go here? Like do I say user.name? But then I also know it has to have a different style. So maybe I'd use action list.description. In the previous api, it was just a variable here. In this, it's a component, and you say action list.description. And you see, as we're laying these down, they all know their place in the row of this component. So they all take up that space, which is already decided. And you could use a stack, you could use a grid for this in the action list, and each one could have its own grid position. So it's basically a layout component at this point. And you could put anything inside description, which is, you know, which is a bit scary, because in the previous api, you could say this is only a text, it cannot be anything else. Over here, you have children. So can children be something else? Not necessary, you can actually... Oh, I skipped the slide. Okay. Let me let me come back to that point. But labels, remember, they have to be block variants. Well, we already have a description. So we have a place where we can put that prop, which would be variant block. So that's done. Now, coming back to the thing I was saying about type, you can actually make make description or make children be typed, right? There's a way to do that, which is you you say, using typescript in this case, but you could do it with prop types as well, where the description type, the variant can be inline or block. But the children have to be a string, they don't have to be children as a node, right, which is the default with react, it could be anything you want, you could have a number if you wanted. So in this case, we just typed children as string. And if you try to give it something else, you get a warning that, you know, please don't do that. It's not allowed. And to show you how this component actually works, how do things fall exactly into their spaces? What I have in this place, or at least this one that's rendered on the left, is have something of like a slot based api. So I have these slots where I have this is the leading visual, this is the text, this is the space for inline description, and then I have space for block description. And what I'm doing is, if you use the children from action list, if you use the components that we ship with it, we look at the child.type. So I'm looping through all of these children, looking at the child.type. And if there are things that we already identify, then I fill the slot. So for example, the leading visual, if the component type is action list dot leading visual, which we ship, it goes into the slot of leading visual. So if you try to pass multiple leading visuals, well, I guess the last one wins. And that's totally fine. It kind of shows that it only accepts one. And we have the same thing for description. If it's description and it's a variant block, it goes into block description, otherwise into inline. And everything else goes into the text, right? So the text part over here is something I have an array. So you could put multiple, multiple text blocks over there if you wanted. And that's the only free form here. Everything else is typed and has a place. But the middle area is something that you could put anything. So that's the one we keep free. And then I have some selection state as well, which, you know, I don't need to show you just yet. But other than that, it's your good old component. It renders a slot. If there is nothing in the slot, it renders nothing. It just goes empty. But I'm using Flexbox. You can use Grid. It's a layout thing. So slots as an api choice. Really cool. I like it. Let's see if the milestone and project things. So we have the leading visuals, we have the name, we have the description. And you can see that I can switch what goes in the middle, what goes inside the slot. And I can get milestones, projects, labels, all of them fit in the slot. So I'm only changing the slot. The rest of the structure remains the same. It's perfect. All right. Now let's talk about this review. So in reviewers, remember we had the metadata thing, which I didn't like. In this case, we're kind of flipping the paradigm. Inside the action list, we want to put two groups of elements. So I just created action list.group, same style as before. And it takes a title, it takes a variant. And honestly, this title is not a text because I want to fill this group with all the items. So the group has children and the children are actually action list items. So I can map through suggested folks and put them here. And I can map through everyone else and put them in the second group. So then there's no mapping, there's no metadata. It's all you think of one group at a time and then you fill what the folks that go inside it. And inside that, you have the same thing that we just saw. You have a leading visual, which has an avatar. You have the text, you have the description, nothing too wild. It kind of just works. It composes very nicely. We don't have to learn anything new. There were no extra props on the item. The only new api was for group and it's all self-contained. So you see that we're taking different parts and we're composing them with each other and they seem to fit really well from an api standpoint. Finally, remember we had these two descriptions. One is inline, one is block. How do we fit these? Well, we just put two descriptions, I guess. So you have action list description variant inline and we have action list description variant block. And both of these just sit next to each other. And it doesn't really matter what order you give them in because we have the slots and the slots have the specific space to go in. So yeah, you could just put two descriptions if you wanted. It works pretty nicely. All right. Finally, I made a header component which takes the title and the variant. Again, I wanted to put the title inside because react has a way of putting children, but so that it stays consistent with the group api, I kind of followed title and variant instead of putting inside. Tiny, tiny choice though. All right. So now if you compare, this is what it looked like in the group metadata in the previous config api where you had the group metadata, you had to match it with the group ID. We had to kind of do this escape hatch render for the descriptions. And over here, it's definitely, you know, it looks a lot longer, but it's also more composable. So we've been able to take all the parts without breaking the api and keep composing them. And it kind of really works nicely. All right. So moving on from this, let's see if the other things work. So milestone is a leading visual, but it also has this cool effect where you can select something, you know, and this we didn't even talk about in the, actually, you know what, there you go. So we didn't even talk about this in the config api because, well, I kind of didn't really have space for it anymore. But in this case, there are a bunch of things you could do. You could probably just show the check mark as a check icon, you know, say if this milestone is selected, then put a check icon here. But we also want this space to be opened up, right? Because we want these to be aligned. We don't want these two to be left aligned and then have no gap. So this kind of doesn't work. You need to know that one of the slots is filled. So maybe do this, what we've been doing till now, you have action list dot check, and then you pass checks. So if it's check true, then, you know, it renders this. If it's checked false, then it knows it has to put an empty space here. Kind of works. In the case of labels, actually, let's look at assignees. In the case of assignees though, you'll notice that it's not a check anymore. It's a check box because it's multiple selection, not just one. So, you know, we don't want to close this with a tick. We actually want to keep this open, put all the check marks. So maybe instead of check, we just do check box and then it ships too. Kind of, but I think there's a simpler way because to be honest, based on if it's a single selection or a multi selection, we want to change the behavior, which is, does it close instantly or not? And we want to change what the selection looks like. So I'm going to replace this with action list dot selection. And the action list knows which selection to put based on the selection variant. Or maybe a different name for this tag, you know, single select, multi select. I've chosen selection variant where you can put single and multiple. And based on this, the action select knows which selection style to render. So then it becomes slightly less of an api. You don't have to pick between a check box or a check mark, a check icon. You just have to say, I have selection in this. So you put that component against slots based api. It knows, what did I do? Oh, I clicked. I went into her profile. Give me a second. I need to go back. Go back. No, there we go. So, yeah, because of slots, it knows exactly where to render it and it picks based on the selection variant. So the api is scaling well. And finally, we haven't really talked about how this gets rendered in the menu. Right. So till now, we've been looking at this, the contents inside. But what about the action menu, the overlay, the, you know, what, what you click to get this. So that's not action list. That's a different component called action menu. Names are similar because we want to couple them a lot of times. So let me see. OK, this is not the example. So with the action menu, what you do get is an action menu dot anchor. So for example, these three dots are an anchor for that case. But in this case, this whole thing is an anchor. So you have assignees and you have this gear and the whole row is an anchor. So you can put basically put anything you want. I have assignees and gear and they're aligned and whatnot. But other than that, anything else that I put here is, is rendered inside the overlay. So the action menu essentially knows again, slots, knows what an anchor is, knows everything else is up to you. So you could put a menu, you could put an emoji picker. I should have the example. You should put the emoji picker here or you can put a selection with action list with a selection variant and that goes into the overlay. So what about, what about this pattern that you see sometimes where you can have a search? So, you know, I can search for Mike and this list filters, right? So we want to put something else here. Now, in a config based api, you would kind of have to declare this, that user filter or, you know, filter true, and then it would render the input. You would have to give this placeholder, find the user. You would have to say what happens when it's on chain. So you're looking at a bunch of config variables, a bunch of list of new props that would be added to the top level api. In this case, I think what we can get away with is render a text input, you know, render it. It will, if you render it here in the open area, it will go inside the overlay, right? To go inside the body of it and just before action list and you can add an action menu divider on an action, this divider, they're kind of the same to get this behavior. And because we want some focus styles, like check this out. If I am selecting, I can move my keyboard around, but my selection state would still remain here so that I can keep typing. This behavior is very specific. So to do that, we actually ship action menu dot text input. And this is like, it renders the text and it renders the divider. It renders all the behavior, but then you basically can give it whatever placeholder you want. Oh, see, it's still locked on that. You can give it whatever placeholder you want, like this one. This one says, find a user. And then in the on chain, you say filter users, and then, you know, you're responsible for filtering. So you control all the state in the product, which makes sense. And the component controls the layout and the behavior, but not the state. Makes perfect sense. So this is just an example of how you can use composable APIs to kind of, you know, compose components from the product and compose components from the component library, put them all together. And it kind of still works because we've thought of all those use cases where we don't really know what people will put here, but we know that they want to put things in the overlay. So we give them that slot, which is kind of an open slot and you can fill that with the things you want. Okay. Let's see. So to summarize, experiment to find out your spot on that flexibility consistency spectrum, right? Like somebody else can't really tell you that. You have to know your company culture. You kind of have to know the kind of use cases that you serve. In our case, I thought we were more on the consistent side, but we're actually in a lot of cases, we have to be, GitHub is big enough to be, you know, like there are a lot of use cases. So we probably slightly more towards the composable size, more flexible. But you can still use composition to allow flexibility without ejecting. Right. And which brings me to the last point, like design systems are for people, component libraries are for people. Brilliant quote, love this. This is by Gina. And the summary there is that people using our components are just trying to build the products, right? They're trying to build products for their customers. They're not so much interested in what the api is, or they're not super interested in, you know, the correctness of the api. It's nice when the api feels good. It's nice when the api is consistent. All of those things are important, of course. But that's not the main thing. The main thing is what can you build with the component and so that you can build something for the customer. So and this is where like, I really like the idea of composition, allowing that flexibility, because like the folks have to get the job done. If your component library doesn't do it, they'll do it with something else. They'll build their own custom components, right? Like they still have to, there are jobs to be done. So that's the idea. Experiment to know where you are on the spectrum, use composition instead of ejecting out of systems. And finally, remember that you're building for people. Thank you. That's it. If you want to look at Primer, if you want to look at the design system, it's right here. It's on github.com slash Primer. And that's my Twitter. If you if you like what you heard, if you want more of that, that's kind of it. So, I'm doing well. How are you? Good, good. I love the talk. I was really enjoying that. And I'm going to go back and finish it off later. But I've got some questions that have come in and I'm just going to pose them to you. One of the first one is in a nutshell, is there a thumb rule to decide if a component should be part of the design system? How many times do you need to use it? Is there a number of usages enough for you to make that decision? That's a very good question. So, I guess the thumb rule is something that you hinted at, which is how many times is it used, especially across pages and products? So, the moment you see something is a pattern that repeats a lot. But of course, each product or each team would develop it for their own use case. That's a sign that across teams and across products, it could be just a tiny bit inconsistent or it could be accessible in one but not in the other. So, that's usually a sure shot sign that this could be put in the system, polished, made consistent, and then released back to the teams to use that one true version. That's awesome. Thank you. And one thing I really loved as you were going through the talk was you were slowly building it and we watched this design system get more and more complex. And there's a relevant question to this where someone asked how do you decide whether a component should be split up into multiple components to support different use cases without that api becoming too complex? Yeah, that's a good question again. I don't know if there's an easy answer for it other than you're trying to build a few examples with it. And you can, like the thing I tried to do in the talk, you can start telling when it starts becoming icky to use. When it starts becoming a little too, you have to do too much guesswork or copy from the docs exactly. So, when it's not predictable anymore, you use the api, you can't really tell what's going to happen. That's usually a sign that this component is doing too much and you might benefit from either a specialized component or the api is just going in the wrong direction and you have one to split it. So, the second half you see, I take a more composable approach. So, it's the same amount of components, just one with a bunch of children, but it's a more composable api. So, you can actually do more by keeping the api smaller. Thank you. And another one as well, because there will always be design exceptions. When you're trying to deal with those, is composite patterns the only way to be flexible enough but still stay abstract? At least in my experience, that's the way that I like the most. Some folks also have nested styles. You basically pass it to the root and you can reference each child and customize them. So, maybe in the top level list I can say all of the images, they should actually have this extra border because I choose them. But I like to co-locate them as close as I can to them. So, I usually prefer composite, but it's css. You can actually start from the top and cascade it down. Nice. And another thing I want to get your opinion on, what's your view on creating your own design system versus using a components library? I know, like you said, it depends, but how do you usually make that decision? I actually don't have a good answer. So, I'm going to give the answer that I personally do, which is you can start off with a library, which is hopefully an unstyled one. There are a few now. So that you can at least control the styling. But eventually, I always hit a point where I want to customize it more and have to go inside the internal. So, the moment you find yourself customizing the behavior and not just the aesthetics of it, that's a sign that you have to pull this component out and build it from scratch, make it your own. But honestly, if you look at a component library and you're like, we are happy with the behavior that this component gives us, especially if you're like early stage, you don't have to get everything exactly the way you want and you're slightly more flexible. Shipping is more important than it's mine open united thing. Then sure, use a component library. Try to find an unstyled one because you at least want it to feel like your own company. You don't want it to feel like somebody else's. And we've run out of time, but I want to put one more question for you. Did you deliberately color coordinate the books you've got in the background? Yeah, yeah, it's true. All of these are color coordinated. Some of these are not even mine. I haven't read them, but I borrowed them just so that I can show the green. Orange book is there because it makes the transition smoother. That's nice. Thank you so much. Everyone give a round of applause.
47 min
22 Oct, 2021

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career

Workshops on related topic