The Spacing Object

I recently posted a collection of CSS objects enhanced with CSS custom properties. The post has all the CSS custom properties namespaced with the object name to avoid collisions but something occurred to me afterwards: there was a better object that could serve as the base for most of those objects – the spacing object. It looks like this:

.o-spacing {
    --spacing: 0px;
    --spacing-horizontal: var(--spacing);
    --spacing-vertical: var(--spacing);
    --spacing-bottom: var(--spacing-vertical);
    --spacing-left: var(--spacing-horizontal);
    --spacing-right: var(--spacing-horizontal);
    --spacing-top: var(--spacing-vertical);
    --spacing-values:
        var(--spacing-top)
        var(--spacing-right)
        var(--spacing-bottom)
        var(--spacing-left);
}

This object defines a hierarchy of CSS custom properties:

  • --spacing-left and --spacing-right and defined by the value of --spacing-horizontal.
  • --spacing-top and --spacing-bottom and defined by the value of --spacing-vertical.
  • --spacing-horizontal and --spacing-vertical are defined by the value of --spacing.
                  -----------
                  | spacing |
                  -----------
                  /         \
    --------------           ------------
    |  spacing   |           | spacing  |
    | horizontal |           | vertical |
    --------------           ------------
     /          \             /        \
----------- ----------- ----------- -----------
| spacing | | spacing | | spacing | | spacing |
|  left   | |  right  | |   top   | | bottom  |
----------- ----------- ----------- -----------

We can set all those values at once by simply setting --spacing, both the vertical spacings can be simply set with --spacing-vertical and we can set the individual spacings if we want to. The spacing object also has a helper CSS custom property: --spacing-values – a quick way to use all the final spacing parts of the hierarchy.

The --spacing CSS custom property is set to 0px by default. The unit stops any use of calc() failing and value prevents parent objects accidentally defining the spacing on children. The value can be set to whatever you prefer and if you want to use the same spacing as the parent, you can use the value inherit.

The spacing object is incredibly easy to extend and manipulate. Since it used CSS custom properties, the child elements can use the values however they see fit. Fore example, here’s the box object from the previous article re-built with the spacing object.

.o-spacing,
.o-box {
    /* ... */
}

.o-box {
    padding: var(--spacing-values);
}

.o-box > :first-child {
    margin-top: 0;
}

.o-box > :last-child {
    margin-bottom: 0;
}

.o-box__lip {
    margin-left: calc(var(--spacing-left) * -1);
    margin-right: calc(var(--spacing-right) * -1);
    padding-left: var(--spacing-left);
    padding-right: var(--spacing-right);
}

Now we have a generic box object with all the flexibility of the spacing object added in. Components that we create can set the spacing as they need by setting the necessary CSS custom properties.

.c-equal-spacing {
    --spacing: 1em;
}

.c-taller-vertical-spacing {
    --spacing-horizontal: 1em;
    --spacing-vertical: 1.5em;
}

.c-larger-left-spacing {
    --spacing: 1em;
    --spacing-left: 2em;
}

We can also create other objects using the same base which will make it easier for developers to know how to adjust our objects.

.o-spacing,
.o-media {
    /* ... */
}

.o-media {

    --side-alignment: flex-start;
    --body-alignment: center;

    display: flex;
    flex-direction: row;
    width: 100%;

}

.o-media__left {
    align-self: var(--side-alignment);
    margin-right: var(--spacing-left);
}

.o-media__right {
    align-self: var(--side-alignment);
    margin-left: var(--spacing-right);
}

.o-media__body {
    align-self: var(--body-alignment);
    flex-grow: 1;
}

The spacing might refer to different things depending on the object. One of the best places that I’ve found to use the spacing object isn’t another object but a generic style:

.o-spacing,
table {
    /* ... */
}

th,
td {
    padding: var(--spacing-values);
}

I firmly believe that CSS works best when we limit selector strength as much as possible. Limiting the selector strength to a single class means we don’t have the filtering process as the CSS engine attempts to work out which elements are affected by the selector and overriding the styles are easy. With careful coding, it should mean that style cascade is based almost exclusively on the position of the style in the stylesheet and following rules set out in SMACSS or ITCSS will keep that logical and sensible. Having said that, the biggest stumbling block of my belief has always been tables. Styling table cells required classes to be added to every table cell but with the spacing object, that’s no longer the case.

.c-tabular-data {
    --spacing-horizontal: 1em;
    --spacing-vertical: 0.5em;
}

Of course, zebra striping rows in the body still requires a selector like .c-tablular-data > tbody > tr:nth-child(even) but finding a solution for that is for another article.

You can see the spacing object in action in this example fiddle:

I’ve been extending the selector in this article (and just applying the class in the fiddle) but I think the spacing object may make more sense as a SASS placeholder or “silent class”. I’m not sure that the spacing object is useful on its own but it’s a great base for a lot of other CSS layout objects.

%spacing {
    /* ... */
}

table {
    @extend %spacing;
    /* ... */
}

.o-box {
    @extend %spacing;
    /* ... */
}

Finding the spacing object has made me wonder what other new OOCSS-style classes can now be created with CSS custom properties.

Leave a Reply

Your email address will not be published. Required fields are marked *