Architecting CSS grid in a design system

By Chen Hui Jing / @hj_chen

🇲🇾 👾 🏀 🚲 🖌️ 👟 💻 🖊️ 🎙 🐈‍⬛ 🧗 🏳️‍🌈
Chen Hui Jing
Jing
@hj_chen
Shopify

Screens, screens, screens

A range of screen sizes
Image source: Inch Calculator

Image credit: Jyotika Sofia Lindqvist

            .wrapper {
  display: -webkit-box;
  display: -webkit-flex;
  display: -ms-flexbox;
  display: flex;
}

          
MDN: Backwards Compatibility of Flexbox

Browser configuration pages

Browser configuration warning on Firefox
about:config
Experimental features configuration in Chrome
chrome:://flags

Grid release dates

Grid release dates for all major browsers in 2017

Data on support for the flexbox feature across the major browsers from caniuse.com

Data on support for the css-grid feature across the major browsers from caniuse.com

State of CSS 2021 survey

State of CSS 2021 survey Flexbox usage
Flexbox: 98.9%
State of CSS 2021 survey Grid usage
Grid: 83.5%
https://2021.stateofcss.com/en-US/

⚠️ Disclaimer ⚠️

The following theory may or may not oppose your view on the matter, and that is PERFECTLY FINE. I am not here to tell you what to think, merely here to share a theory based on my personal observations and experiences. You are absolutely free to agree, disagree or not care at all.

Dotcom bubble (1995-2002)

Timeline of Nasdaq over dotcom bubble period

Source: The History of the Dotcom Bubble

Screenshot of Michel Buffa's video games website
Source: Michel Buffa's Video Games Page in 1994
Crude timeline of internet technologies

Parent-child relationship

Diagram showing a container with 3 children elements within

Basic grid syntax

Item A

Item B

Item C

Item D

Item E

Item F

Item G

Item H

Item I

Named grid areas

Banner

Links and stuff?

Your main content

Footer, for copyright and moar links?

Placing grid items

A black cat

Not my cat

He just naps in my house sometimes. But isn't he super cute?

Container-led sizing

Boxies demonstrating how Grid ensures items in rows and columns are aware of each other

Item-led sizing

Boxies demonstrating how items in non-Grid layout models have no positional awareness of their siblings

Common example of grid system CSS

“Bootstrap’s grid system uses a series of containers, rows, and columns to layout and align content.”

.col-sm {
  flex: 1 0 0%;
}
.row-cols-sm-auto > * {
  flex: 0 0 auto;
  width: auto;
}
.row-cols-sm-1 > * {
  flex: 0 0 auto;
  width: 100%;
}
.row-cols-sm-2 > * {
  flex: 0 0 auto;
  width: 50%;
}
.row-cols-sm-3 > * {
  flex: 0 0 auto;
  width: 33.3333333333%;
}
.row-cols-sm-4 > * {
  flex: 0 0 auto;
  width: 25%;
}
.row-cols-sm-5 > * {
  flex: 0 0 auto;
  width: 20%;
}
.row-cols-sm-6 > * {
  flex: 0 0 auto;
  width: 16.6666666667%;
}
.col-sm-auto {
  flex: 0 0 auto;
  width: auto;
}
.col-sm-1 {
  flex: 0 0 auto;
  width: 8.33333333%;
}
.col-sm-2 {
  flex: 0 0 auto;
  width: 16.66666667%;
}
.col-sm-3 {
  flex: 0 0 auto;
  width: 25%;
}
.col-sm-4 {
  flex: 0 0 auto;
  width: 33.33333333%;
}
.col-sm-5 {
  flex: 0 0 auto;
  width: 41.66666667%;
}
.col-sm-6 {
  flex: 0 0 auto;
  width: 50%;
}
.col-sm-7 {
  flex: 0 0 auto;
  width: 58.33333333%;
}
.col-sm-8 {
  flex: 0 0 auto;
  width: 66.66666667%;
}
.col-sm-9 {
  flex: 0 0 auto;
  width: 75%;
}
.col-sm-10 {
  flex: 0 0 auto;
  width: 83.33333333%;
}
.col-sm-11 {
  flex: 0 0 auto;
  width: 91.66666667%;
}
.col-sm-12 {
  flex: 0 0 auto;
  width: 100%;
}
            
              
One of three columns
One of three columns
One of three columns

A pretty standard grid

Size Min Max Cols Margin Gutter
xs 320px 639px 4 16px 16px
sm 640px 899px 8 30px 16px
md 900px 1199px 12 50px 16px
lg 1200px 1599px 12 90px 24px
xl 1600px - 12 >180px 24px

Option 1: vanilla CSS (or SCSS)

Size Min Max Cols Margin Gutter
xs 320px 639px 4 16px 16px
sm 640px 899px 8 30px 16px
md 900px 1199px 12 50px 16px
lg 1200px 1599px 12 90px 24px
xl 1600px - 12 >180px 24px
              .grid {
  min-width: 320px;
  max-width: 1600px;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1em;
  margin-left: 16px;
  margin-right: 16px;
}

@media screen and (min-width: 640px) {
  .grid {
    grid-template-columns: repeat(8, 1fr);
    margin-left: 30px;
    margin-right: 30px;
  }
}

@media screen and (min-width: 900px) {
  .grid {
    grid-template-columns: repeat(12, 1fr);
    margin-left: 50px;
    margin-right: 50px;
  }
}

@media screen and (min-width: 1200px) {
  .grid {
    gap: 1.5em;
    margin-left: 90px;
    margin-right: 90px;
  }
}

@media screen and (min-width: 1600px) {
  .grid {
    margin-left: 180px;
    margin-right: 180px;
  }
}
            

Option 1: vanilla CSS (or SCSS)

            .grid__item--full,
.grid__item--half,
.grid__item--third,
.grid__item--quarter {
  grid-column: 1 / -1;
}

@media screen and (min-width: 640px) {
  .grid__item--quarter {
    grid-column: span 4;
  }
}

@media screen and (min-width: 900px) {
  .grid__item--half {
    grid-column: span 6;
  }

  .grid__item--third {
    grid-column: span 4;
  }

  .grid__item--quarter {
    grid-column: span 3;
  }
}
          

Option 1: vanilla CSS (or SCSS)

            .custom-thingy {
  grid-column: 1 / -1;
  font-size: var(--step-1);
}

@media screen and (min-width: 640px) {
  .custom-thingy {
    grid-column: 1 / 6;
    padding-top: 2em;
    padding-bottom: 1em;
  }
}

@media screen and (min-width: 900px) {
  .custom-thingy {
    grid-column: 1 / 7;
  }
}
          

Option 2: Container and Item components

            src/
└── components/
    ├── Col/
    │   ├── Col.module.css
    │   └── Col.tsx
    └── Grid/
        ├── Grid.module.css
        └── Grid.tsx
          

Grid.tsx

                import { ReactNode, createElement } from "react";
import styles from "./Grid.module.scss";

interface GridProps extends React.HTMLProps {
  className?: string;
  children: ReactNode;
  tag?: keyof JSX.IntrinsicElements;
}

export default function Grid({
  className = "",
  children,
  tag = "div",
  ...props
}: GridProps) {
  const Wrapper = tag;
  return createElement(
    Wrapper,
    {
      className: `${styles.grid} ${className}`,
      ...props
    },
    children
  );
}
              

Col.tsx

                import { ReactNode, createElement } from "react";
import cn from "classnames";
import styles from "./Col.module.scss";

interface ColProps extends React.HTMLProps {
  className?: string;
  children: ReactNode;
  colWidth?: "full" | "half" | "third" | "quarter";
  tag?: keyof JSX.IntrinsicElements;
}

export default function Col({
  className = "",
  children,
  colWidth,
  tag = "div",
  ...props
}: ColProps) {
  const Wrapper = tag;

  return createElement(
    Wrapper,
    {
      className: cn(className, { [styles[`${colWidth}`]]: colWidth }),
      ...props
    },
    children
  );
}
              

Col.module.css

            .full,
.half,
.third,
.quarter {
  grid-column: 1 / -1;
}

@media screen and (min-width: 640px) {
  .quarter {
    grid-column: span 4;
  }
}

@media screen and (min-width: 900px) {
  .half {
    grid-column: span 6;
  }

  .third {
    grid-column: span 4;
  }

  .quarter {
    grid-column: span 3;
  }
}
          

CustomThingy.module.scss

            p.customThingy {
  grid-column: 1 / -1;
  font-size: var( --step-1);
}

@media screen and (min-width: 640px) {
  p.customThingy {
    grid-column: 1 / 6;
    padding-top: 2em;
    padding-bottom: 1em;
  }
}

@media screen and (min-width: 900px) {
  p.customThingy {
    grid-column: 1 / 7;
  }
}
          

Option 3: Using Tailwind classes

⚠️ Yet Another Disclaimer ⚠️

The following opinion may or may not oppose your view on the matter, and that is PERFECTLY FINE. You are absolutely free to agree, disagree or not care at all.

tailwind.config.js

            module.exports = {
  theme: {
    screens: {
      xs: "320px",
      sm: "640px",
      md: "900px",
      lg: "1200px",
      xl: "1600px",
      maxSm: { max: "639px" },
      maxMd: { max: "899px" },
      btwSmMd: { min: "640px", max: "899px" }
    },
  },
  prefix: "tw-"
};
          

TailwindThingy.tsx

            export default function TailwindThingy {
  return (
    

Option 3: Use Tailwind classes

Well, this is spicy

FWIW, Tailwind has managed to support grid fairly well in this latest version

You will have to learn the tailwind classes to use them correctly

This basic example is able to match the previous 2 options

Tailwind comes out the box with opinionated defaults

But they do allow you to customize your own if theirs doesn't fit you

At some point, there will be bigger issues to think about

Does it make sense to abstract constantly repeated patterns into something much DRY-er?

There also remains the issue of code maintenance, which all 3 options still have

); }

But does it work?

CodeSandbox demo
Parody of Big Tech org charts from 2011-06-27 edition of Bonkers World
Source: Manu Cornet, 2011-06-27 edition of Bonkers World (modified to fit slide)

So you want to introduce Grid to your application?

  • Are there preferred technologies used within the organisation?
  • How is big is your application and how is it structured?
  • How flexible does the design system need to be?
  • Are there cases where code is contributed by new developers often?
  • What is the documentation culture like in your organisation?

So you want to introduce Grid to your application?

  • Are there preferred technologies used within the organisation?
  • How is big is your application and how is it structured?
  • How flexible does the design system need to be?
  • Are there cases where code is contributed by new developers often?
  • What is the documentation culture like in your organisation?

So you want to introduce Grid to your application?

  • Who is responsible for the maintenance and development of new components or pages on the application?
    • Is it a small team of full-time developers overseeing the entire project?
    • Is it numerous teams responsible for their own respective set of components and pages?
    • What is the overall CSS skill level of the developers contributing to the codebase?
    • Are the contributing developers very familiar with the frameworks and libraries used in the codebase?

Document the “Why”

One size does not fit all

–Frank Zappa

Thank you

Websitehttps://chenhuijing.com

Twitter@hj_chen

GitHub@huijing

Codepen@huijing

Font is Jones by indestructible type*.