<onWebFocus />

Knowledge is only real when shared.

CSS Styling Architecture

December 13, 2021

Modern ways to do spacing and layering.

Spacing

For a long time one of the best way to add spaces between the various components on a page was to add margin-bottom to each component. However, this meant that every component had to be written with the same spacing formula in mind and the last component in a row had to have no space or the wrapping elements were required to offset this space.

In the meantime CSS has adapted with the introduction of the gap property in Flexbox and CSS Grid. Thanks to growing browser support of the Flexbox gap property and CSS grid layouts it's now time to handle spacing differently. Defining the space in the wrapping elements avoids having to integrate spacing into every component and therefore reduces complexity.

<div style={{ paddingTop: 20, paddingLeft: 20, paddingRight: 20, paddingBottom: 0 }}>
  <div style={{ marginBottom: 20 }}>first</div>
  <div style={{ marginBottom: 20 }}>second</div>
  <div style={{ marginBottom: 20 }}>third</div>
</div>

This approach can now be simplified with the gap property when using Flexbox.

<div style={{ display: 'flex', flexDirection: 'column', padding: 20, gap: 20 }}>
  <div>first</div>
  <div>second</div>
  <div>third</div>
</div>

The gap property is shorthand to set both row-gap and column-gap which can also be set separately.

Using CSS grid the approach is similar:

<div style={{ display: 'grid', padding: 20, gap: 20 }}>
  <div>first</div>
  <div>second</div>
  <div>third</div>
</div>

Caution is advised as the Flexbox gap property was introduced quite some time after Flexbox itself and current browser support is at about 90%. In CSS grid it's a basic feature and supported where Grid itself is supported at about 95% in currently used browsers.

React Native which uses Flexbox as the only layout paradigm currently doesn't support the gap property. Fortunately, a Pull Request adding the functionality has been created and the feature might soon be released.

Layering with z-index

z-index is a CSS property used to define how elements stack up to one another. A higher index means the element will be displayed above elements with a lower index. On larger websites there are often many elements that need to be appropriately stacked up to one another. The index has to be defined in the element itself and so they are usually spread out all over the codebase. This can make adding a new layer that needs to be appropriately stacked in between all the existing elements quite a chore.

A way to remedy this issue is to define all the z-index variables in a single file and then import the values from there. This makes it easy to add in another layer without touching many files. Using SASS one can define these variables in a file we call layers.scss.

$layer-base: 1;
$layer-popup: 2;

/* Usage elsewhere */
.popup {
  z-index: $layer-popup;
}

When adding in a new layer value like $layer-modal that should be below the popup all the above values have to be increased.

$layer-base: 1;
$layer-modal: 2;
$layer-popup: 3;

Introducing laier for CSS-in-JS

laier is a plugin for use with CSS-in-JS libraries that aims to make this process even simpler. As in the SASS example the layers are defined in a separate file and then imported elsewhere.

import configureLayer from 'laier'

export const Layer = configureLayer(['Base', 'Popup'])

Once the layers are configured they can be imported and used anywhere to define z-index values.

import { Layer } from '../../index'

export const MyComponent = () => <div style={{ zIndex: Layer.Popup }}>Popup Content</div>

Apart from getting TypeScript support allowing only the use of defined layers it also makes adding new layers much easier as there are no numbers to be incremented by hand. Adding a modal layer is as simple as adding it to the list at the appropriate place.

export const Layer = configureLayer(['Base', 'Modal', 'Popup'])