Here at Woovi, one of our core philosophies around the front-end is ensure that our entire UI will be resilient around any future change. In the startup scenario, we need to iterate faster in our front-end and produce a high-quality code even in this scenario.
One of the principles that serves as a guarantee of a good code in the UI is related to how we handle the spacing between children elements. In general, we will find two different approaches: top-down and bottom-up.
Bottom-up
A bottom-up approach implies in the children elements causing a side-effect in the next (or close) elements. Rules like margin
, when used to determine the spacing between two or more elements, is a bottom-up.
This can be called a bad practice. But why? In the example above, each child has their own margin
rule applying 8px
to the next element.
In this scenario, you're creating more entropy around your codebase. You give the power to these children to determine how they will behave in the macro UI, causing an undesirable side-effect around other elements. If one of these elements has a different spacing value, like 12px
to margin-bottom
, will be harder to debug and identify in which of these elements is the issue.
The CSS logic around this approach would be more harder to handle too. How do you ensure that the last element won't apply another margin-bottom
? You have some ways to ensure it, but it's the kind of code that is useless because someone already solved to you.
If someone wants to change the spacing between elements from 8px
to 12px
, instead of changing only one element, you'll need to deep dive around the children finding where we're putting these margins and modify it, then you modify 3 (three) elements instead of only 1 (one).
Top-down
This is an example of top-down spacing. With this pattern, the parent element applies the spacing around their children. With a flexbox, you put a gap
rule in the parent element and add the spacing around the children elements.
You're reducing the entropy around this code. You apply this rule only for the children elements, not having an external side-effect around other elements that aren't related to this scope. Putting the rule inside only one element is a better practice too, it's easier to debug and find around if necessary.
When margin
rule can be applied?
A margin rule is unnecessary in almost every case. But can be acceptable given some context, for example, a spacing between a heading, and the rest of their siblings. MaterialUI provide to us an example of it, exposing a gutterBottom
prop that adds a margin-bottom
of 8px
in the Typography element. In cases like that, where you want to add some spacing between the heading element and the "rest of the body", a margin is acceptable.
Another scenario are insets, like the Inset exposed by the Radix UI. Is a good practice around design stuffs to give a better centralization around some icons and other related stuffs, a good way to do it is using margin. It gives to you an easier way to centralize an element with a more predictable way around a specific parent.
About us
Woovi is a startup that enables shoppers to pay as they like. To make this possible, Woovi provides instant payment solutions for merchants to accept orders.
We are hiring!
Top comments (6)
Thanks for sharing, I am very much into the spacing topic and always eager to read about others opinions and solutions. Currently I use
margin-top
for almost all spacing and it works really well so far. I wrote a post about this quite a while ago: Why I fell in love with margin top.In our design system we do have a stack component as well and it basically looks like this:
The reason why I did not go with
gap
for the stack was mainly due to my relatively strictmargin-top
policy. (in some some other scenarios we do usegap
though).The other thing I like about the
margin-top
stack is that the CSS stays very explicit and clean even when some elements in the stack need another spacing.Imagine an example a stack that has a gap of 16px and 10 elements but:
element2 needs to have 24px (at the top)
element5 just 12px (at the top)
Solution 1: You don’t use a stack for this but individual spacing.
Solution 2: You use multiple stacks (I am not a huge fan of this solution)
Solution 3: You solve it with individual margin like so:
gap
stack:element2 needs
margin-top: 8px
element5 needs
margin-top: -4px
or better you can expose the gap size in a CSS var and write it more explicit:
element2
margin-top: calc(24px - var(--gap))
element5
margin-top: calc(12px - var(--gap))
margin-top
stack:element2:
margin-top: 24px
element5:
margin-top: -12px
Happy to hear your thoughts. As you can tell I am very much into my
margin-top
stuff, but because it works so well for me since years. If you somewhen need someone to discuss spacing stuff I am all in.I once worked in a Drupal skin project and to change the spacing between 2 elements I had to change multiple paddings and collapsing margins, this is the Hell 😂
Is it appropriate to handle it with wrapper's padding instead of margin?
If you have a background-image on a wrapper and use padding, the spacing between the image and the content above/below will be halved.
Padding's for the inner space, margin's for the outer space [insert astronaut picture for the meme] 😁
I would say that is a bad practice to use padding as a spacing ruler. Even more because of facts like
border-box
, etc.I always would prefer to use a gap or a margin (as a polyfill for old browsers) in an abstraction, like the Stack exposed by Material UI. Or other similar solution.
I would say you would be adding padding to containers in most cases, but thats besides the point of having to manage the space between container child elements either with margins on the children or flexbox gap on the container.
Excellent topic and writeup! Such a simple concept which I find myself struggling with more than I should. Your MaterialUI real world example is invaluable in driving this approach home.