DEV Community

loading...

How to style margin with React

seya profile image kazuya ・4 min read

TL;DR

  • grid-gap for grid layout
  • If it's a flexbox, I want to use flex-gap (but Safari doesn't support it as of today).
  • Specify the appropriate padding
  • Stack component for multiple identical margins, Spacer component for the rest.

Child components should not know the "layout style" of the parent component

First of all, child component should not know the "layout style" of its parent component.

For example, let's say there is a component with multiple icons arranged like this.
icons

There are margins between the icons at equal intervals.
Let's say you have defined this margin in the icon component as below.

.icon {
  ...
  ... margin-right: 15px;  
}
Enter fullscreen mode Exit fullscreen mode

Now, let's say you want to use this icon component on another page, and in that page it requires 100px margin on the right side of the icon. How would you style margin?

There are many ways to solve this problem, but no matter what your choice is, you will have to worry about the margin in this icon component. And I think it's easy to imagine that as such solutions pile up, we'll have more and more dirty CSS.

This is why child components should not be styled by their parents in terms of what layout they will be used in.

It's ok to have appropriate "padding"

I think it's okay to have components to have own "padding" as long as the padding is appropriate.
For instance, you may want to make the button component larger than it appears to be in order to increase area to accept click.
Alt Text

As long as you are aware of who owns the margins and make the right decisions, you should be fine.

Who owns margin?

As I mentioned above, as long as you are aware of who owns the margins and can make the right decisions, you should be fine.
So the question is "who owns margin between elements?"

I think it's either "parent" or "nobody".

Parent

The most obvious case is the grid layout, where the parent container describes the layout style. Margins can be specified with grid-gap.

.grid {
    display: grid;
    grid-gap: 1rem;
}
Enter fullscreen mode Exit fullscreen mode

Nobody

If the parent doesn't specify any structure for the layout, the margin can be considered as an independent entity, not belonging to anyone.

This should also be same as the way designers think of margins. When the designer decide a margin between two elements, he/she doesn't think "the margin is tied to this one", but rather "these are the same concept, so the margin should be smaller, and these are completely different, so let's separate them more", and so on.

They decide margin based on the "relationship" between those elements.

Therefore, margin does not belong to either of them, but is only responsible for one thing: having margin.

How to implementation

Let's dive into how to implement it.

Parent - Grid

As I mentioned earlier, for Grid Layout, we'll use grid-gap. (If you don't add grid-, it won't work in Safari).
https://developer.mozilla.org/en/docs/Web/CSS/gap

You can specify the vertical and horizontal direction respectively.

If you want to set different widths in the same row or column, you can nest them together to make it work.

parent - flexbox

I'd like to use gap, but Safari doesn't support it at the time of this writing, so it will be difficult to adopt. I hope they will be implemented it soon!

https://developer.mozilla.org/en/docs/Web/CSS/gap
https://caniuse.com/flexbox-gap

Same margin between child elements

This is the case when using list elements such as ul and ol.

For this, use Stack component.
As an example, let's look at the Stack in Chakra UI.
https://chakra-ui.com/docs/layout/stack

If you set the spacing of the Stack component like this, the value will be applied as the margin between elements.
Alt Text

In Chakra UI, children are mapped and wrapped with a component called StackItem to apply the margin style.

If you happen to have multiple values of the same margin for this Stack, you may want to use this method. (However, since it's not conceptually correct, you may have to break it down if you want to set a different margin for new elements in the future. So in that case, I think it's better to use Spacer.)

Nobody

Lastly, in the case that it doesn't belong to anyone, let's think about making a Spacer component.

If you want to make your own, you can specify the size and axis as follows.
(Referenced from https://www.joshwcomeau.com/react/modern-spacer-gif/)

import React from 'react';

const Spacer = ({
  size,
  axis,
  style = {},
  ... .delegated,
}) => {
  const width = axis === 'vertical' ? 1 : size;
  const height = axis === 'horizontal' ? 1 : size;
  return (
    <span
      style={{
        display: 'block',
        width,
        minWidth: width,
        height,
        minHeight: height,
        ... . style,
      }}
      {... .delegated}
    />
  );
};
export default Spacer;
Enter fullscreen mode Exit fullscreen mode

The only props needed is size.

// It will be 16px x 16px
<Spacer size={16} />
Enter fullscreen mode Exit fullscreen mode

If the width of the Spacer gets in the way in directions other than the one you want, you can specify the axis direction to keep it to a minimum.

// It will be 32px x 1px.
<Spacer axis="horizontal" size={32} />
Enter fullscreen mode Exit fullscreen mode

Conclusion

Spacer may seem weird at first sight, but I personally think it makes a lot of sense, so let's make use of Stack and Spacer to get some nice margin styling💪

Reference

https://www.joshwcomeau.com/react/modern-spacer-gif/
https://mxstbr.com/thoughts/margin/

Discussion (0)

pic
Editor guide