Atomic, presentational, reusable, functional components: UI architecture in the age of design systems
(electronic music) (audience applauding) - Thanks.
Okay, design systems seem to be pretty popular nowadays.
So my talk is about how these popularitive design systems influences the process of building user interfaces.
But the first thing I'm going to do, because design systems mean different things to different people, is I'm going to give you my definition. So within the context of this talk, this is what design systems means.
So it's a cross between a style guide and a pattern library. Now style guide is essentially a bunch of rules around your brand identity and how to deploy it in products and marketing materials and so forth, whereas a pattern library is basically a bunch of designs of reusable designs may or may not come with snippets of code attached to them to show you how to implement those designs. And so I'm going to kind of walk you through a part of this UI building process through the lenses of a couple of tools. Not built tools, nothing of the sort, but tools for thinking about how process and architecture can work out in this context.
Now the first of these tools is a very design related tool that's Atomic Design.
It was created by a US designer called Brad Frost, and it's become very popular with this sort of design system explosion.
The basic premise of it is that complex designs can be broken down into small components and these in turn can be broken down into very basic elements, so you can build your whole UI out of this very sort of modular set of bits and pieces.
And he takes chemistry as a model so you have very very basic components, atoms, and then several atoms together will compose a molecule, and then molecules can be composed into organisms, and then it kind of breaks out of chemistry and you go to what your actual finished product is going to be, which is a set of templates which are basically the abstract layouts for the pages that are going to have the actual content in them. So the page is sort of the finished output. To give you a more practical example...
So this is an image gallery, so you can think of an atom as maybe that image in there. It's a component all by itself, you can't divide it any further, whereas the image together with the caption would form a molecule cause they're both related, but they're two actual separate elements.
So atoms could be sort of like HTML elements, and then the whole thing all together is an organism. You might have several different molecules in here. So for instance, if you can see the back and forward navigation buttons, well those might form a molecule too. And then you have that counter at the top right hand side. Well that might be part of the navigation molecule because it's kind of related to navigation. It tells you where you are.
But then you have a problem because you may want to reuse that navigation molecule down here.
You see this little thumbnail strip? It's also got navigation.
So are we going to reuse the same molecule? They've got slightly different sizes, and well, one of them has that little atom at the top the counter thing and the other one doesn't, so how are we going to do this? And well, further to that we have the actual thumbnail strip itself.
Now, if that thumbnail strip includes a navigation molecule, is it still a molecule, or is it an organism? So, here we have a problem.
We're either going to nest molecules, or we're going to nest organisms, or we have to create a whole other level of complexity to be able to deal with this thing.
And at this point, probably you either throw out the whole model or no one in the team is talking to anyone else because everyone is very opinionated on these matters.
No, you have to do it like this, no no no.
Okay, so scratch that.
Let's start again.
So you start very simple with atoms.
They're very basic components, and then you compose these into molecules and then your molecules are composed into organisms, and well, you see where this is going.
It's kind of a very linear progression that looks lovely because humans love linearity, right? It's easy, sort of flows through, but that's not actually how the design process works. It's more like this.
It's sort of all together in a page, and you start with the big thing, and you start breaking that up into small things, and then you look at the small things and they don't quite fit with the big thing and you go back and forth and refactor...
Kind of like the process of coding really, because also with code, you don't generally start with small snippets and build up.
You write a chunk of code and then you go, ah, I'm repeating myself, so maybe I can refactor this, and I can take out that function and reuse it somewhere else.
So it goes sort of from small to big, and big to small. And with design systems, it's easy to fall into the same narrative, where you can start with the design system and have all your bits and pieces laid out and then you can use these bits and pieces to compose your user interface, or you can start with your messy user interface and try to abstract stuff from there and put it into the design system.
And either of these ways of doing these things is legitimate, but in the end you're building products, and things need to work in their context.
So, more often than not, you'll be kind of trying to build both things at once, and you might not be one of those lucky people in companies that have a whole team dedicated to building a design system.
They call it Design Ops and it's really trendy, but for most of us it's like two overworked people from the product team that are trying to document their stuff as they go along, so they're kind of stealing time from their product tickets to put stuff into the design system.
And you have to be really organised about this. You have to be really strategic, because if you don't find a very efficient way of documenting your product in the design system so sort of transferring your components across, you end up just doing nothing and creating tech debt tickets, and you all know what happens to tech debt right? It goes to a better place in the sky and then you never see it again.
So it's sad.
Now, all is not lost.
The second tool I want to talk about kind of helps us to organise our code in a way to make it easier to do the design systems bit. So, this is an idea that comes originally from the React community, although it can be used in other frameworks. But it's very popular with React, cause a few years ago, Dan Abramov, this big guy in React, he built Redux, and he blogged about this pattern. And it exploded because everything Dan Abramov says is like, oh, yeah we need to do this immediately. So basic premise is you structure your app into two different kinds of components, so everything in your app is components, but your components need to be separated by what they address.
So you have containing components dealing with data fetching and manipulation and state management and all the sort of back end things. And then you have presentational components that deal with the more front end things like the HTML and the CSS and what your UI is actually going to look like and this task is made especially easy in React because you have, essentially, two different ways of building components.
You can build them using functions or classes. So the class based components allow you to manage states inside them, and so they're more eminently suited to be container components, whereas your functional components are smaller and they're just sort of supposed output markup and CSS. which is what your presentational components do. So that's cool, except the names of these components are very sort of long winded.
So I've taken to just calling them containers and UI Components, which is short as in presentational, and has the additional advantage of being a bit more inclusive too.
So presentational, being composed of HTML and CSS, well Mandy just told us why we should probably not be thinking of HTML as strictly presentational, because it's a lot more things besides that. So when your UI components and your HTML you have a bunch of stuff including state management. HTML has state.
You have things like inputs that can be checked or invalid or required and you have a bunch of interactive states like hovering and focus and visited.
And this is all the stuff that actually fits into your HTML so this...
Actually, Dan Abramov talks about it in his article. He calls this UI state.
I guess this is what he means by UI state, we state anywhere, it just happens in the HTML. But that's fine.
That can still fit into your UI components. But then you have other sort of more complex things that are also UI related, but might not, strictly speaking, fit into functional components.
So, for instance, I have a button here and I click that button...
Now if I'm navigating with the keyboard I'm probably going to use enter to press the button, open a dropdown, and when that happens, I expect focus to be transferred to the first focusable element in the dropdown.
Now, to do that in React you have to use refs, which are references to actual dom elements, so you create a reference to that element over there and if your button isn't inside the same component as the dropdown, things get complicated.
You have to use class based components, probably, to create those refs.
The other thing you need to make sure you do is that dropdown has to close when you click outside of it, and maybe when you open another dropdown somewhere else, that needs to know that the other dropdown is closed. So in the end you're sort of sharing a state between a bunch of components because they all need to know what each other looks like when something happens inside them.
And this is all UI interaction right? This is not data fetching, it's not any of that crazy stuff.
It's pure, front end work.
So at this point things start getting a bit messy, cause now either have a bunch of UI functionality inside your container components, or else you're creating class based components for your presentational components, and everything is a mess and you're questioning your life choices, and then you go onto Twitter, and apparently Dan Abramov is very unhappy with the way that people are taking presentational container components far too seriously.
It wasn't meant to be that at all.
It was just something he noticed was happening and blogged about.
So yeah, that's all good.
The code is not a mess, it's fine.
And actually this kind of division can be very useful for design system purposes, it can.
And the thing is, that what you want to take into your design system is basically the UI side of the components, but it's the UI with the more simple HTML-based state management, because all those complex interactions between components that open and close and transfer and focus and stuff like that, that's not really going to be documented in the design system at all.
So that's fine.
You can still separate your concerns and have functions and class based.
And then within your UI components, you can use the atomic design structure to separate out your components into atoms and molecules and organisms, and it works really well when you put all of those into a design system.
Now, how you're probably going to do this is design. This, you know, in an ideal world you'd have designers and developers sitting together and working out their components and stuff. What normally happens is that design creates a bunch of design files and hands them over to the devs, and the devs look at them and go okay, I need to build this.
Let's get started.
And at this point, with all the atomic design things, you might run into problems.
Because sometimes things that look like components in the atomic design shouldn't actually be components in your code.
Now let's look at an example.
This is the button.
The button, funnily enough, is the most used example in any sort of component modularity or design system related talk.
Everyone always comes up with the button.
It's the perfect component.
It does stuff too.
But now I'm going to use React examples because it's what I am using now and it's probably the most popular framework out there, so apologies if it's not your favourite.
So this is more or less what a button would look like in React, its functional component, its got a couple of props that will set the value of the button and a click handler, and that's pretty much it.
It doesn't really do much besides.
And this is the whole of the file with that component. So in addition to the component itself, you need to have a bunch of imports and props and stuff like that.
And that's 26 lines of code.
And I can tell you that in the fairly large code base that I extracted this example from, it's been used all of once.
So, it's not very reusable and to explain why, I can show you another example of a button from the same code base.
So here we have a button that is not reusable, it's not meant to be, and it's got a bunch of properties.
Apart from the click handler it has mouse over, touch start, it's got a few aria attributes for sensibility purposes, and then it even has an alternative state so if whatever you want to do is not possible, so if comments aren't active then the button will show as disabled, because you're not supposed to be able to do anything with it and well, there you go. This is not reusable, but it's only a button. The button is only a single HTML element, it's one line of code.
There's no reason to go writing 26 lines.
If we wanted to pick out earlier examples of the reusable button and make it be able to do all this sort of stuff conditionally, we'd be looking at a hundred lines of code. I mean, for Christ's sakes, it's just a button. So, you might not want to make all your atomic components into actual components.
And with buttons, I guess you want to be able to reuse to a certain extent, consistent styling for them. So you might argue yeah it's cool to have this component that's a button, because then I can pop all the CSS in there, but you don't have to actually create a component to do that.
You can have a bunch of reusable CSS.
So here I am using Sass which is a very useful tool it's a preprocessor, and allows you to do a bunch of stuff that plain vanilla CSS doesn't.
So, that kind of solves the problem now.
It's great to be modular it's actually good to strive to create modular and reusable code.
But sometimes these solutions...
And both atomic design and that presentational and container components sort of structure are great tools to think about things and to solve certain problems that we have, but you can't really take them as rules.
They're sort of more like guidelines.
So if what you're building doesn't really fit completely into those patterns, don't tie yourself in knots about it, it's not the end of the world.
The really important things to think of are... You write your code as cleanly as you can, you write tests for it.
Tests are great.
Pretty much everyone should be writing more tests, and you document the shit out of everything. All those decisions that seem obvious at the time, especially the ones that seem obvious, because you'll forget.
You will forget, and other people will come onto the project and go why the fuck did this person do this? It's not obvious to everyone.
So that's the basic principle.
And in conclusion, it was a very short talk actually. Rule Number One, always try to leave things better than you found them.
Thank you! (audience applause) (electronic music)