Say you’ve got a <Card />
component. It’s highly likely it shouldn’t be butted right up against any other components with no spacing around it. That’s true for… pretty much every component. So, how do you handle component spacing in a design system?
Do you apply spacing using margin directly on the <Card />
? Perhaps margin-block-end: 1rem; margin-inline-end: 1rem;
so it pushes away from the two sides where more content natural flows? That’s a little presumptuous. Perhaps the cards are children inside a <Grid />
component and the grid applies a gap: 1rem
. That’s awkward, as now the <Card />
component spacing is going to conflict with the <Grid />
component spacing, which is very likely not what you want, not to mention the amount of space is hard coded.
Different perspectives on component spacing
Eric Bailey got into this recently and looked at some options:
- You could bake spacing into every component and try to be as clever as you can about it. (But that’s pretty limiting.)
- You could pass in component spacing, like
<Card space="xxl" />
. (That can be a good approach, likely needs more than one prop, maybe even one for each direction, which is quite verbose.) - You could use no component spacing and create something like a
<Spacer />
or<Layout />
component specifically for spacing between components. (It breaks up the job of components nicely, but can also be verbose and add unnecessary DOM weight.)
This conversation has a wide spectrum of viewpoints, some as extreme as Max Stoiber saying just never use margin
ever at all. That’s a little dogmatic for me, but I like that it’s trying to rethink things. I do like the idea of taking the job of spacing and layout away from components themselves — like, for example, those content components should completely not care where they are used and let layout happen a level up from them.
Adam Argyle predicted a few years back that the use of margin
in CSS would decline as the use of gap
rises. He’s probably going to end up right about this, especially now that flexbox has gap
and that developers have an appetite these days to use CSS Flexbox and Grid on nearly everything at both a macro and micro level.
It’s all about compositional layout for me.
Let the skeletal layout handle the sizing and spacing, then it doesn’t matter what component goes where.
Especially for a card grid I still use flex diplay as grid will not fullfill all my needs.
Years ago I wrote my own less code to minus margin the left ones according to left minus values don’t force scrollbars. It has also a simple fallback for IE9 n older (if still needed)
Within this I have a variable for the gap and I set it.
This is one of my favorite things about Vue. You can just place a class on the component from the context it’s used in and it’ll land on the component’s root element. No need to add a new
prop
to all of your components for such a common need.(This changed a little bit in Vue 3 with the introduction of multi-root components, but they can still explicitly bind it just using the native
class
attribute.)Oh wow, thanks for the link! A few other things I’d like to express here:
gap
as his preferred approach. I’m a big fan ofgap
and am hoping to eventually use it on our design system once our IE support concerns fade away. I also really like the idea of utilizing a Grid component to create semantic, intentional overall page/view layouts.At HalalBooking we use
:not(:last-child)
approach. So margins are applied only to one side of a component and children have no effect on the parent’s margin.E.g.
We apply it in 2 ways:
– directly to a component (good if you have like 50+ instances of a component on a page)
– as an extra CSS class/component, e.g.
.vertical-section--xxl
. This one made our life much easier.I definitely go for a separation between the component itself and the structure it’s put into.
The component unless specifically needed has no size (takes the whole space naturally) and no outside spacing either (no margin).
the structure can then handle spacing. Whether it’s a grid with gaps set up, or flexbox that can have definitions that apply margins to the element inside the flex box environment.
Allows authors to build pages with the very same component in different environments and contexts, and still get proper spacing baed on the design system.
Right now I’m doing something similar in our design/component system to what Ben writes above.
The spacing of a component is defined by the context it’s placed into. So the component itself doesn’t do anything with its own margins, but the parent (another component or page) does and contains all the logic of spacing its own children. So no components have any outside margins by themselves, but these are primarily set by their parents.