CSS is a great language and has improved a lot in the past decade but it still has the same global nature as in its early days. CSS was designed to style documents, which causes issues in large scale applications. Naming conventions, like BEM, CSS Modules and later CSS in JS significantly improved the maintainability of styles, but we can go one step further. Using components to create responsive Flexbox and Grid layouts can simplify code, improve the developer experience and feels natural in React. In this talk, we’ll explore how to create any kind of page layout with just three components.
Artem Sapegin: [0:02] Hi, everyone. My name is Artem. I'm a frontend developer living in Berlin. I also created React Styleguidist, a tool to help you develop React components and share them with your team. Let's start from the very end. We're going to implement the layout of this page today.
[0:23] First, I want to ask you a question. Do you think that we can implement this layout without writing a single-line of CSS? That's exactly what we're going to do today. We're not going to write any CSS, but we will use CSS in a slightly different way.
[0:44] Let's look at the evolution of web application layout and styling. I think these are its main steps. Usually, HTML was made for documents, not for complex interactive applications.
[1:01] Tables were the only way to create layouts, and presentational things like font were the only way to style content or hardcoding fonts, sizes, and colors, which made our pages inconsistent.
[1:14] This is how the most basic layout could look like. We would use a table for layout which was not semantically correct and had accessibility issues, we would use presentational things like font, hardcoded fonts, sizes, colors, and so on.
[1:43] CSS gave us a lot of power to create amazing styles, but we didn't know how to write maintainable styles. Everything was in the same global scope, and changes in one component could break another component on another page. Code was almost impossible to remove.
[1:59] Here, we're already using CSS for layout instead of a table, which is great because it has better accessibility. Still, there are many problems like global class names, styling of particular HTML elements, which makes this code very difficult to maintain.
[2:20] These jokes about CSS how from exactly this error. BEM and similar naming conventions like OOCSS and Xmarks, some of the problem of the global scope and styles of the component affecting other components.
[2:38] We ended up writing tons of styles for each component, covering all the basic attributes like colors, fonts, whitespace, and layouts, often making our style sheets bloated and our interfaces inconsistent. Also, we had to cut paste big chunks of markup to reuse components.
[3:03] Here, we have isolated styles so each class name is scoped to its component, so styles of one component will never affect another component. Atomic CSS solved the consistency and style reuse issues. There are no custom styles in this example, only shared class names like these ones, but there is a lot of markup to write.
[3:35] This was a big problem before component-based frameworks like React. There was no easy way to reuse markup. Primitive components complete the evolution cycle. We're back to the font and center text and describing layouts using the markup. Primitive components look suspiciously close to the old HTML too.
[4:00] The main difference here is that with components, we can enforce constraint of our design system and we can easily create parameterize components that encapsulate repetitive markup, and reuse them many times.
[4:13] Here, we're still creating layout using the flexbox CSS. We're using components to describe this layout, and we don't need to write any custom styles because we can compose any layout from these primitive components.
[4:38] We've already done the shift with events in React. This code doesn't scare us anymore. We know that this approach is fast, thanks to React, and maintainable. The same idea works with styles too, and it can be implemented in a performant and maintainable way.
[5:00] I talk a lot about components and CSS and JS in this talk, but I'm not suggesting to forget the CSS knowledge you have already. I suggest to build on top of this knowledge. I suggest to write better CSS using the tools that we have now. This is great, but there are better ways to write CSS. These better ways are components.
[5:29] Now, let's talk how we can create such layout components. Usually, there are two ways of implementing layout with components. The first way is discarding CSS and the second way is embracing CSS. The first approach, discarding CSS may look like this.
[5:57] The problem of this approach is that you have to forget all the CSS knowledge you've gained in the last 15 years, and learn a completely new API forage like this FlexContainer here, that have completely different API from the CSS length box. You have to learn it for each component to be able to use this component efficiently.
[6:23] The second approach, on the other hand, embracing CSS may look like this. On the first site it's similar, but it's already looking to you familiar because it's the same CSS. Props are camelised CSS properties and the values are CSS values, so you immediately see what this markup does.
[6:52] You don't need to learn all the properties because you already know them. Such API is more convenient. Yeah, no need to forget all this CSS knowledge you already have. We can reuse it with the new tooling and approaches available to us now.
[7:23] Now, responsiveness. Probably the most common approach to responsiveness is to create separate prop for each breakpoint. The second approach is to make all props responsive.
[7:40] The first approach may look like this. We have row component, we have core component which is I guess a column, and we have three props for extra small size, for medium, and for large, and they define I guess, the width of this column. It's already hard to understand what this does, if you're not familiar with its API.
[8:08] That second approach where all props are responsive is much easier to understand, because you need to learn it once and it will work for each component. Here, we have Box component where we have responsive ways. We have 100 percent for small, we have 50 percent for medium, and we have 25 percent for large.
[8:31] The same idea works for padding. We have small padding for our small screens, medium padding for medium screens, and large padding for large screens. This works for any prop this component might accept. You learn it once and you use this forage for everything. Learn once, use everywhere.
[9:06] The last thing, with components, we can easily access project design tokens. For example, here, we're using background color for text which is described in design here, and for the background, we're using text which creates an inverted block. For padding, we use the median value, which is 1rem, 16 pixels.
[9:37] What I just described you is styled-system, all the good examples. Styled-system, not styled-components, of course. Styled-system is a tool to create components with style props. Why is it good? There are several reasons. Props are camelCased CSS properties, which is easy to remember. You already know them all.
[10:03] Each prop is responsive. You can either supply a single value or an array of values in mobile for style, and is constrained by design tokens. Each props will use the design tokens instead of the hard coded value. It also works with more CSS in JS libraries like styled components or emotion.
[10:38] Now let's use styled system to create some components that will be helpful to implement our markup. We're going to create four components box, flex grid and stack and that should be enough for our layout needs.
[10:54] The first one is box without any props. It renders div without any styles. We can control its padding margin, text and background colors, layout properties like width and height, Flexbox properties and CSS Grid properties using component props.
[11:17] We can also change the HTML element with styled components as prop. This is all the code we need to write to create this component that will support all kinds of CSS props. Each props is responsive and that's how we can use it.
[11:44] We have a card scene here that has background color that has seen border of gray color, and it has base borderRadius. It also has different horizontal and vertical padding. Then Flex. Flex is based on the box component. It defines flex display: 'flex' by default. It's a shortcut to make our markup a bit simpler because Flexbox is very common thing.
[12:26] That's how we can use it. We can define different justifyContent dependent on the screen size, 'center' for small, 'space-between' for larger screen and we defined flexWrap="wrap". We nest two other components inside. They're rendered with some space between them. They should also be responsive. No can show it, all right.
[13:02] Then the grid that is the same as the flex move based on the box component. This time we define this display: 'grid' for CSS Grid layouts. Here are how we can create a simple see is a grid layout with the grid component. We define gridGap and we define gridTemplateColumns. This is how it works.
[13:38] The last one is stack. Stack is more tricky than all the other components. We use it to create stack layouts. Stack layouts means we have equal horizontal or vertical space between components shoulder. We want to have whitespace between children but not around the component.
[14:04] Implement this we're using the lobotomized old CSS selector which targets all the children of a container except the first one meaning that we adding a marginTop to our children component that says "components except the very first one, which creates the desired layout with whitespace between children but no whitespace around the container."
[14:37] Implementation of such component will be significantly different when all browsers support the gap property on Flexbox. We could use the flex component directly without the need to separate stack component and this implementation is also simplified.
[14:56] It only supports vertical stacking but in reality, the horizontal stacking as well as switching dynamically between horizontal and vertical depending on screen size is also very convenient. The implementation will probably take four or five slides, which is not so great, but API is still similar. You can have a look at this more complete implementation here.
[15:43] This is how we can use this stack component to create a stack layout. Stack is based on the box component so we can define all properties this the box components or parts for example textAlign and we can define the gap which we implemented in the stack component to create a whitespace between these three elements.
[16:17] This is a more complex example that is using all four layout components. The container is flex with background and border. Then inside we have an image we have stack that creates whitespace between this metadata and the button.
[16:42] We also define justifyContent space between to push the button to the bottom edge of the container. Then we nest another stack to create small whitespace between the title, subtitle and the rating.
[17:03] Nested stacking is one of my favorite layout trick layout techniques. It's very convenient. Usually, in a layout, we have bigger whitespace between main page elements. Then we have smaller whitespace between page sub elements and then even smaller whitespace between inside the sub element.
[17:30] It's very easy to implement with stack and it looks very good in the markup because you have this visual nesting that is the same as the hierarchy of the page, this make the layout, easy to understand and to work with. OK, let's look at our mock-up again. Are you confident now that we can implement this layout without writing a single line of CSS? Folks could tell, " Yes, you're right."
[18:06] Actually, it's not a mock up and it never was. I lied to us since the very beginning. It's a real page with links, with the text. We can scroll it. We can do something in the forums. It's all live and working. You can also see which components are used to this page. For example, these are the block component.
[18:53] These are flex component. This is the second component, the same example that we looked at before and the slide. We looked at this example also. All layout of this page is created with primitive layout components without writing any custom CSS. It's fully responsive. You can check this demo and this URL or check the code on GitHub.
[20:19] To recap, components are the new way of writing CSS. I believe this is the future of frontend development. Also, I believe that there is no reason to forget all your CSS knowledge. We've spent a lot of time learning called the possible CSS properties, learning how to create way layouts using Flexbox, CSS grids, and so on.
[20:48] There is absolutely no need to forget this knowledge. We just need to reshape it a little bit according to do available to us today and desks that are required from us. The four components that we will implement today are enough to create almost a new layout.
[21:13] If you want to learn more about component-driven and community-driven development and design systems in React, my friend, Andrey Okonetchnikov, and I are doing workshops on these subjects. We're also launching new blogs. Go to component driven.io and subscribe to our newsletter. That's all. Thank you. Use components and follow me on Twitter.
We’re already doing it with styled-components, styled-system does a bit more work to generate styles but I’d expect that a typical app has more serious performance issues than styles.
If Vue.js has CSS in JS solution that has an API similar to styled-system, then it might work. Styled-system itself is framework-agnosting.
Great talk, Artem! Though instead of "Stack" I would use "Column" or "Row" for that component. I had the chance to work with Flutter recently and using primitives like these for composition is really great. I'm glad to see that this is also being adopted in the web.
Ariel, the Stack name makes more sense in the responsive version of the component where one could switch between row and column depending on the viewport width, and I had several cases where it worked great.
<Stack gap="medium" direction={['column', 'row']}>...</Stack>
Are there any performances issues or considerations in replacing an entire app's html with layout primitives, i.e. replacing every <div> tags with <Box> components? I'd be interested to see some metrics on how long it takes React to render <Box/> components vs <div>s with css
Also, is there option to use styled-system in Vue.js ?