Krzysztof Żuraw

Stack & Column in CSS-in-JS

  • #css-in-js
  • #layout

Today I want to write more about new emotion components that we try at Ingrid. Those components help us with spacing.

The problem

I want to implement this footer:

Footer to implement

It has a logo, text in two rows and horizontal space between them. It sounds not bad. I added a logo, then left margin and in the end two rows of text. Or I can use css grid to have the same effect.

Yet after two weeks requirements change and I don’t need to display the telephone number to the user if there is no enough data. In this case, I won’t render this component. Yet I have a problem - margin-top between contact our support & at 555-555-555. I can either refactor that to grid/flexbox with a gap instead of margin-top (the best way) or I can add conditional inside my css prop telling emotion to reset margin-top to 0 (the worst way). In the worst case, my code started to look like spaghetti, in the best case in mix flexbox or grid definitions with margins, colours & fonts making my code less readable.

Solution

What is solution for this problem? Use specialised components. Specialised component has one task - to give spacing and make sure that elements lay in the place where they should be. Below you will find example of those specialised components in action:

<Columns space="0.5rem" alignY="flex-start">
  <img src="logo.svg" />
  <Stack space="0">
    <p>Contact our support</p>
    <p>at 555-555-555</p>
  </Stack>
</Columns>

I now have Columns component to create columns from its children - in my case img and Stack. Stack is making sure that p children have spacing between them.

What are the pros? I have a nice abstraction that can be shared across a team of people. I can point my teammates into those components when they need to create other combinations of layouts. Because this is abstraction I can think about layouts as a combination of different boxes (which is HTML sense they are). I found it much easier to combine them, reason about them, or change them. I can move my focus to other pieces of frontend instead of fighting layout & spacing.

Below you can find an example implementation of Columns & Stack components in emotion + react:

import * as React from 'react';

type Props = {
  space: 'string'; // it can be typeof keyof Theme['spacing']
};

export const Stack: React.FunctionComponent<Props> = ({ children, space }) => {
  return (
    <div
      css={{
        '> * + *': {
          marginTop: space,
        },
      }}
    >
      {children}
    </div>
  );
};
import * as React from 'react';

type Props = {
  space: 'string'; // it can be typeof keyof Theme['spacing']
};

export const Columns: React.FunctionComponent<Props> = ({ children, space }) => {
  return (
    <div
      css={{
        display: 'flex',
        alignItems: 'center',
        '> * + *': {
          marginLeft: space,
        },
      }}
    >
      {children}
    </div>
  );
};

HTML examples can be found on codepen.

Summary

In this blog post, I wrote on how one can use Stack & Columns components for creating layouts.