Layout

v5.x.x

Description

This plugin creates classes to handle column layouts:

Where N can be a number of columns, up to the largest amount of columns defined or a fraction (1/2, 1/3, 1/4, 2/3 or 3/4)

For use inside .container or a descendant of .container

  • Element width:
    • .w-cols-N sets width to N columns/fraction wide, if inside of .cols-container also includes an inner gutter margin-left
  • Flex based grid
    🚨 deprecated in v5.0.0
    • .cols-container makes a container for columns, flex: row wrap with a negative inner gutter margin-left
    • .ml-0 on child, resets margin-left to 0, adds specificity to the class
  • Margins:
    • .ml-cols-N on child, sets a margin-left of N columns wide
    • .mr-cols-N on child, sets a margin-right of N columns wide
    • .mx-cols-N on child, sets a margin-right and margin-left of N columns wide
    • .ml-cols-N on child, sets a negative margin-left of N columns wide
    • .mr-cols-N on child, sets a negative margin-right of N columns wide
    • .mx-cols-N on child, sets a negative margin-right and a negative margin-left of N columns wide
    • .ms-cols-N on child, sets a margin-inline-start of N columns wide
    • .me-cols-N on child, sets a margin-inline-end of N columns wide
    • .ms-cols-N on child, sets a negative margin-inline-start of N columns wide
    • .me-cols-N on child, sets a negative margin-inline-end of N columns wide
  • Padding:
    • .pl-cols-N on child, sets a padding-left of N columns wide
    • .pr-cols-N on child, sets a padding-right of N columns wide
    • .px-cols-N on child, sets a padding-right and padding-left of N columns wide
    • .ps-cols-N on child, sets a padding-inline-start of N columns wide
    • .pe-cols-N on child, sets a padding-inline-end of N columns wide
  • Positioning:
    • .left-cols-N sets left to N columns
    • .right-cols-N sets right to N columns
    • .inset.x-cols-N sets left and right to N columns
    • .left-cols-N sets negative left to N columns
    • .right-cols-N sets negative right to N columns
    • .-inset.x-cols-N sets negative left and negative right to N columns
    • .start-cols-N sets inset-inline-start to N columns
    • .end-cols-N sets inset-inline-end to N columns
    • .start-cols-N sets negative inset-inline-start to N columns
    • .end-cols-N sets negative inset-inline-end to N columns
  • gutter-less spacing variants:
    • .ml-cols-no-gutter-N on child, sets a margin-leftof N columns wide minus an inner gutter
    • .mr-cols-no-gutter-N on child, sets a margin-right of N columns minus an inner gutter
    • .mx-cols-no-gutter-N on child, sets a margin-right and margin-left of N columns minus an inner gutter
    • .ml-cols-no-gutter-N on child, sets a negative margin-left of N columns wide minus an inner gutter
    • .mr-cols-no-gutter-N on child, sets a negative margin-right of N columns wide minus an inner gutter
    • .mx-cols-no-gutter-N on child, sets a negative margin-right and a negative margin-left of N columns minus an inner gutter
    • .ms-cols-no-gutter-N on child, sets a margin-inline-startof N columns wide minus an inner gutter
    • .me-cols-no-gutter-N on child, sets a margin-inline-end of N columns minus an inner gutter
    • .ms-cols-no-gutter-N on child, sets a negative margin-inline-startt of N columns wide minus an inner gutter
    • .me-cols-no-gutter-N on child, sets a negative margin-inline-end of N columns wide minus an inner gutter
    • .pl-cols-no-gutter-N on child, sets a padding-left of N columns minus an inner gutter
    • .pr-cols-no-gutter-N on child, sets a padding-right of N columns minus an inner gutter
    • .px-cols-no-gutter-N on child, sets a padding-right and padding-left of N columns minus an inner gutter
    • .ps-cols-no-gutter-N on child, sets a padding-inline-start of N columns minus an inner gutter
    • .pe-cols-no-gutter-N on child, sets a padding-inline-end of N columns minus an inner gutter
    • .left-cols-no-gutter-N sets left to N columns minus an inner gutter
    • .right-cols-no-gutter-N sets right to N columns minus an inner gutter
    • .inset.x-cols-no-gutter-N sets left and right to N columns minus an inner gutter
    • .left-cols-no-gutter-N sets negative left to N columns minus an inner gutter
    • .right-cols-no-gutter-N sets negative right to N columns minus an inner gutter
    • .-inset.x-cols-no-gutter-N sets negative left and right to N columns minus an inner gutter
    • .start-cols-no-gutter-N sets inset-inline-start to N columns minus an inner gutter
    • .end-cols-no-gutter-N sets inset-inline-end to N columns minus an inner gutter
    • .start-cols-no-gutter-N sets negative inset-inline-start to N columns minus an inner gutter
    • .end-cols-no-gutter-N sets negative inset-inline-end to N columns minus an inner gutter

For use outside of .container, or .breakout or some other width of container

  • viewwidth calc variants:
    • .*-vw where * is any of the above classes, eg: .w-cols-vw-4, .mr-cols-vw-2 etc. useful if your element's container is not

Each of these have tailwind responsive classes and all settings are breakpoint+, read about responsive usage.

Nesting of elements is also possible, read about nesting usage.

Breaking changes in v5.0.0

The format of the class names has been updated and .cols-container was deprecated in v5.0.0

Before v5.0.0 class names came in the format .w-N-cols - CSS attr, number of columns then -cols/-cols-vw/-cols-no-gutter.

From v5.0.0 onwards, class names will come in an updated format of .w-cols-N - CSS attr, -cols/-cols-vw/-cols-no-gutter and lastly the number of columns. This change was necessary as Tailwind v4.0.0 changes the undocumented rules on how Plugins are generated and our old method was no longer compatible.

:troll perhaps we should have stuck with our pre v3.0.0 names...

Migrating to v5.0.0 from v4.x.x/v3.x.x

If you're ok with doing some regex, its possible to transition to v5.0.0 by:

  1. Search for (\w*)-(\d\/\d|\d*)-cols(-no-gutter|-vw)?
  2. Replace with $1-cols$3-$2

And for any fraction class usage:

  1. Search for (\w*)-(\d\/\d)-cols
  2. Replace with $1-cols-$2

Setup

tailwind.config.js
const { Setup, Layout } = require('@area17/a17-tailwind-plugins');

module.exports = {
  ...
  plugins: [Setup, Layout],
  theme: {
    screens: {
      xs: "0",
      sm: "544px",
      md: "650px",
      lg: "990px",
      xl: "1300px",
      xxl: "1520px"
    },
    mainColWidths: {
      xs: "auto",
      sm: "auto",
      md: "auto",
      lg: "auto",
      xl: "auto",
      xxl: "1440px"
    },
    innerGutters: {
      xs: "10px",
      sm: "15px",
      md: "20px",
      lg: "30px",
      xl: "40px",
      xxl: "40px"
    },
    outerGutters: {
      xs: "20px",
      sm: "30px",
      md: "40px",
      lg: "40px",
      xl: "40px",
      xxl: "0px"
    },
    columnCount: {
      xs: "4",
      sm: "4",
      md: "8",
      lg: "12",
      xl: "12",
      xxl: "12"
    },
  }
  ...
};

Requires Setup plugin with theme.screens, theme.mainColWidths, theme.innerGutters, theme.outerGutters and theme.columnCount configured.

Demo

Using number of desired columns

Basic usage of the .w-cols-N classes:

w-cols-1
w-cols-2
w-cols-3
w-cols-4
w-cols-5
w-cols-6
w-cols-7
w-cols-8
w-cols-9
w-cols-10
w-cols-11
w-cols-12
document.html
<div class="w-cols-1"></div>
<div class="w-cols-2"></div>
<div class="w-cols-3"></div>
<div class="w-cols-4"></div>
<div class="w-cols-5"></div>
<div class="w-cols-6"></div>
<div class="w-cols-7"></div>
<div class="w-cols-8"></div>
<div class="w-cols-9"></div>
<div class="w-cols-10"></div>
<div class="w-cols-11"></div>
<div class="w-cols-12"></div>

As the plugin reads the config, it works out the maximum amount of columns it needs - so if your smallest breakpoint has 4 design columns and the largest has 12 - then it will create classes .w-cols-1 through .w-cols-12.

Using a fraction

Sometimes you want a simpler layout scheme, using %ages of the container width rather than column numbers, but still taking into account the inner gutters. For this you can use fractions.

Fractions can be any of 1/2, 1/3, 1/4, 2/3 or 3/4.

Note Fractions are consistent across all breakpoints. So, 1/2 will be 50% minus some inner gutter on all breakpoints set, unlike setting via columns whose apparent width will change if the total number of design columns at a breakpoint changes.

w-cols-1/4
w-cols-1/3
w-cols-1/2
w-cols-2/3
w-cols-3/4
document.html
<div class="w-cols-1/4 h-40 bg-column mt-20"></div>
<div class="w-cols-1/3 h-40 bg-column mt-20"></div>
<div class="w-cols-1/2 h-40 bg-column mt-20"></div>
<div class="w-cols-2/3 h-40 bg-column mt-20"></div>
<div class="w-cols-3/4 h-40 bg-column mt-20"></div>

Responsive

If you use w-cols-10 and your smallest breakpoint only has 4 design columns then you may also want to use max-w-full to stop a container being wider than 100%. Or better, use w-cols-4 and lg:w-cols-10.

Which will be 4 design columns wide until the lg breakpoint, when it will become 10 columns wide:

w-cols-4 lg:w-cols-10
document.html
<div class="w-cols-4 lg:w-cols-10"></div>

And of course, can assign different sizes at all of your breakpoints:

w-cols-4 sm:w-cols-3 md:w-cols-6 lg:w-cols-10 xl:w-cols-8 xxl:w-cols-6
document.html
<div class="w-cols-4 sm:w-cols-3 md:w-cols-6 lg:w-cols-10 xl:w-cols-8 xxl:w-cols-6"></div>

As previously mentioned, fractions will be consistent on all breakpoints. They can of course be changed per breakpoint:

w-cols-3/4 md:w-cols-1/2 lg:w-cols-2/3 xl:w-cols-1/2
document.html
<div class="w-cols-3/4 md:w-cols-1/2 lg:w-cols-2/3 xl:w-cols-1/2 h-40 bg-column mt-20"></div>

Nesting

When using a N number of columns, you can nest elements:

w-cols-4 inside w-cols-6 inside w-cols-8
document.html
<div class="w-cols-10 xl:w-cols-8 h-120 bg-column mt-20 py-20">
  <div class="w-cols-6 h-80 bg-column-alt py-20">
    <div class="w-cols-4 h-40 bg-column">8</div>
  </div>
</div>

Warning - Nesting Fractional Columns

Nesting fractions inside fractions will work, but instead halving the breakpoint's total columns, you'll the containers total columns. And so, if you have 12 design columns and you insert w-cols-1/2 inside w-cols-1/2, you'll get one 6 design column wide element wrapping a 3 design column wide element:

w-cols-1/2 inside w-cols-1/2
document.html
<div class="w-cols-1/2">
  <div class="w-cols-1/2"></div>
</div>

Warning - Mixing and matching fractional and N cols widths

Mixing and matching N cols widths and fractional widths when nesting may give you unexpected results. A fraction as a child of a N cols width will work as expected:

w-cols-1/2 inside w-cols-8
document.html
<div class="w-cols-8">
  <div class="w-cols-1/2"></div>
</div>

But, placing a N cols widths inside a fractional width column will not work as fractional widths work cross breakpoint regardless of column count and so don't set/reset the columns total inside themselves. In this scenario, the N cols width assumes the fractional width column container has total columns at that breakpoint. So, if you have 12 design columns and you set a w-cols-3 inside of w-cols-1/2, you'll end up with a 1.5 column wide child column:

w-cols-3 inside w-cols-1/2
document.html
<div class="w-cols-1/2 h-80">
  <div class="w-cols-3 h-40"></div>
</div>

You end up with a 1.5 wide column in that example because 3/12 columns is 1/4 of the total columns, the fractional 1/2 shrinks the 12 columns to fit in a 6 col wide contaner, 1/4 of 6 is 1.5.


Compatibility with GridLayout plugin

As both plugins use the same set of :root variables, they can be nested inside of each other:

document.html
<div class="w-cols-8">
  <div class="grid-layout">
    <div class="grid-col-span-2"></div>
    <div class="grid-col-span-3"></div>
    <div class="grid-col-span-1"></div>
  </div>
</div>

Inside .cols-container

🚨 Note: deprecated in v5.0.0.

Previously we used .cols-container, which added a left gutter and some auto margins because support for gap with flex layouts was poor. But this isn't the case in 2024 and so we no longer recommend using .cols-container.

If you want to make layout grids, using flex as opposed to CSS Grid, use flex gap-gutter:

document.html
<div class="flex gap-gutter">
  <div class="w-cols-1"></div>
  <div class="w-cols-1"></div>
  <div class="w-cols-1"></div>
  <div class="w-cols-1"></div>
</div>

For legacy usage of .cols-container:

document.html
<div class="cols-container">
  <div class="w-cols-1"></div>
  <div class="w-cols-1"></div>
  <div class="w-cols-1"></div>
  <div class="w-cols-1"></div>
</div>

.cols-container has a negative gutter margin left.
Fractions, responsive and nesting works within .cols-container.

If you need to zero out the margin left for some reason, you can use ml-0:

document.html
<div class="cols-container">
  <div class="w-cols-1"></div>
  <div class="w-cols-2 ml-0"></div>
</div>

(ml-0 is essentially Tailwind's ml-0 but with a child relationship selector to add weight to the selector)


Margins

Classes for .ml-, .mr- and .mx-, and their negative equivalents (.-ml-, .-mr-, .-mx-) are also generated.

From 3.10.0, ms- and me- classes and their negative equivalents -ms- and -me- are also generated (using CSS margin-inline-start and margin-inline-end).

document.html
<div class="flex gap-gutter">
  <div class="w-cols-1 ml-cols-2"></div>
</div>

If your content is aligned right, say with justify-end and you want to push items away from the right, you can use :

document.html
<div class="flex flex-row justify-end">
  <div class="w-cols-1 mr-cols-1 h-40 bg-column"></div>
</div>

You can also push by fractions, which by their design, account for gutters:

w-cols-1/3 ml-cols-2/3
document.html
<div class="flex flex-row">
  <div class="w-cols-1/3 ml-cols-2/3"></div>
</div>

And, within .cols-container:

w-cols-1/2
w-cols-1/4
document.html
<div class="cols-container">
  <div class="w-cols-1/2"></div>
</div>
  <div class="w-cols-1/4"></div>
</div>

New in 3.10.0, margin-inline-start (MDN) and margin-inline-end (MDN) with and :

w-cols-4 ms-cols-2
document.html
<div class="flex flex-row mt-20">
  <div class="w-cols-4 ms-cols-2"></div>
</div>
w-cols-4 ml-auto me-cols-2
document.html
<div class="flex flex-row mt-20">
  <div class="w-cols-4 ml-auto me-cols-2"></div>
</div>

Padding

Much like margins, padding classes are also generated: .pl-, .pr- and .px-

From 3.10.0, ps- and pe- classes are also generated (using CSS padding-inline-start and padding-inline-end).

100% wide inside of pl-cols-2 pr-cols-4
document.html
<div class="pl-cols-2 pr-cols-4">
  <div></div>
</div>

Padding and nesting do not mix very well, as the padded amount isn't accounted for in the width calc. Fractional widths will still align to the grid:

w-cols-1/2 inside of pl-cols-2
document.html
<div class="pl-cols-2">
  <div class="w-cols-1/2"></div>
</div>

In our 12 design columm grid, padding the left by 2 columns leaves 10 columns, 1/2 of 10 is 5 and so the child div in the example above is 5 design columns wide.

New in 3.10.0, padding-inline-start (MDN) and padding-inline-end (MDN) with and :

100% wide inside of ps-cols-2 pe-cols-4
document.html
<div class="ps-cols-2 pe-cols-4">
  <div></div>
</div>

Positioning

You can also .left-cols-N, .left-cols-N-gutter, .right-cols-N and .right-cols-N-gutter for position: absolute; type positioning (or maybe with position: relative; depending on your use case).

From 3.10.0, start- and end- classes and their negative equivalents -start- and -end- are also generated (using CSS inset-inline-start and inset-inline-end).

Fractions and responsive work as expected and nesting will work, if you apply a class.

w-cols-3 left-cols-1
w-cols-1/3 left-cols-1/3
document.html
<div class="relative h-96 mt-20">
  <div class="w-cols-3 h-40 bg-column absolute top-0 left-cols-1">w-cols-3 left-cols-1</div>
  <div class="w-cols-1/3 h-40 bg-column absolute top-56 left-cols-1/3">w-cols-1/3 left-cols-1/3</div>
</div>

Push and pull classes, with or without gutters, will also work:

left-0 ml-cols-2
right-0 mr-cols-2
document.html
<div class="relative h-80">
  <div class="w-cols-2 h-40 bg-column absolute top-0 left-0 ml-cols-2"></div>
  <div class="w-cols-2 h-40 bg-column absolute top-40 right-0 mr-cols-2"></div>
</div>

New in 3.10.0, inset-inline-start (MDN) and inset-inline-end (MDN) with and :

start-cols-2
end-cols-4
document.html
<div class="relative h-80">
  <div class="w-cols-2 absolute top-0 start-cols-2"></div>
  <div class="w-cols-2 absolute top-40 end-cols-4"></div>
</div>

Gutter-less classes

Margin, padding and positioning classes also have gutter-less versions. That is, you can position things to the edge of the preceding column rather than the following column:

w-cols-3 ml-cols-no-gutter-2
document.html
<div class="flex gap-gutter">
  <div class="w-cols-3 ml-cols-no-gutter-2"></div>
</div>

vw calc variants

Regular layout classes w-cols-3 use a CSS calc() based on 100% container width and a --grid-columns variable to split the container width as required.

Sometimes, you might need to set an element on the grid, where you container is some other width, such as inside .breakout's or perhaps inside a carousel. In these case the standard w-cols-3 type classes may not work, and you may need to use vw based alternatives.

using w-cols-4 ❌ - too wide

using w-cols-vw-4 ✅ - on grid

document.html
<div class="breakout">
  <div class="w-cols-4 ml-outer-gutter">
    <p>using <code>w-cols-4</code> ❌ - too wide</p>
  </div>

  <div class="w-cols-vw-4 ml-outer-gutter">
    <p>using <code>w-cols-vw-4</code> ✅ - on grid</p>
  </div>
</div>

w-cols-4 draws the div too wide, as the parent container is wider than .container. The suffix of -vw alters the calc to base the calcs off of 100vw.

But, to make this work, we need to account for the scroll bar width.

Additional set up for working with gutters inside .breakout

The .*-vw type classes uses 100vw as its base, which, frustratingly is likely wider than the document as the document can take up 100vw minus the scroll bar width. So, to use .*-vw type classes, we need to account for the scrollbar width:

application.js
const scrollbox = document.createElement('div');
scrollbox.style.overflow = 'scroll';
document.body.appendChild(scrollbox);

// Compare inner and out widths of the box to determine scroll bar width
const scrollBarWidth = scrollbox.offsetWidth - scrollbox.clientWidth;

document.body.removeChild(scrollbox);
document.documentElement.style.setProperty('--scrollbar-width', `${ scrollBarWidth }px`);

// test is scroll bar is visible
function setScrollBarVisible() {
  const scrollBarVisible = document.documentElement.scrollHeight > document.documentElement.clientHeight;
  const overflowYSet = window.getComputedStyle(document.documentElement, null).getPropertyValue('overflow-y') === 'scroll';
  document.documentElement.style.setProperty('--scrollbar-visible-width', `${ scrollBarVisible || overflowYSet ? scrollBarWidth : 0 }px`);
}

window.addEventListener('load', setScrollBarVisible, false);
window.addEventListener('resized', setScrollBarVisible, false);
setScrollBarVisible();

This also makes sure the .p?-outer-gutter type Breakout classes (.w-cols-vw-2, .ml-cols-vw-2 etc.) will work correctly inside of .breakout type content.