Layout

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-N-cols sets width to N columns/fraction wide, if inside of .cols-container also includes an inner gutter margin-left
  • Flex based grid
    • .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-N-cols on child, sets a margin-left of N columns wide
    • .mr-N-cols on child, sets a margin-right of N columns wide
    • .mx-N-cols on child, sets a margin-right and margin-left of N columns wide
    • .-ml-N-cols on child, sets a negative margin-left of N columns wide
    • .-mr-N-cols on child, sets a negative margin-right of N columns wide
    • .-mx-N-cols on child, sets a negative margin-right and a negative margin-left of N columns wide
  • Padding:
    • .pl-N-cols on child, sets a padding-left of N columns wide
    • .pr-N-cols on child, sets a padding-right of N columns wide
    • .px-N-cols on child, sets a padding-right and padding-left of N columns wide
  • Positioning:
    • .left-N-cols sets left to N columns
    • .right-N-cols sets right to N columns
    • .inset-x-N-cols sets left and right to N columns
    • .-left-N-cols sets negative left to N columns
    • .-right-N-cols sets negative right to N columns
    • .-inset-x-N-cols sets negative left and negative right to N columns
  • gutter-less spacing variants:
    • .ml-N-cols-no-gutter on child, sets a margin-leftof N columns wide minus an inner gutter
    • .mr-N-cols-no-gutter on child, sets a margin-right of N columns minus an inner gutter
    • .mx-N-cols-no-gutter on child, sets a margin-right and margin-left of N columns minus an inner gutter
    • .-ml-N-cols-no-gutter on child, sets a negative margin-left of N columns wide minus an inner gutter
    • .-mr-N-cols-no-gutter on child, sets a negative margin-right of N columns wide minus an inner gutter
    • .-mx-N-cols-no-gutter on child, sets a negative margin-right and a negative margin-left of N columns minus an inner gutter
    • .pl-N-cols-no-gutter on child, sets a padding-left of N columns minus an inner gutter
    • .pr-N-cols-no-gutter on child, sets a padding-right of N columns minus an inner gutter
    • .px-N-cols-no-gutter on child, sets a padding-right and padding-left of N columns minus an inner gutter
    • .left-N-cols-no-gutter sets left to N columns minus an inner gutter
    • .right-N-cols-no-gutter sets right to N columns minus an inner gutter
    • .inset-x-N-cols-no-gutter sets left and right to N columns minus an inner gutter
    • .-left-N-cols-no-gutter sets negative left to N columns minus an inner gutter
    • .-right-N-cols-no-gutter sets negative right to N columns minus an inner gutter
    • .-inset-x-N-cols-no-gutter sets negative left and right to N columns minus an inner gutter

For use outside of .container, or .breakout or some other width of container (added in v3.6.0)

  • viewwidth calc variants:
    • .*-vw where * is any of the above classes, eg: .w-4-cols-vw, .mr-2-cols-vw 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 at v3.0.0

v3.0.0 brings in Tailwind like naming to the width and margin classes, transitioning from .cols-2 to .w-2-cols and from .push-2 to .ml-2-cols. This is to give a better expectation of what those classes do, align positioning classes with width/push/pull classes and closer align with Tailwind.

Migrating to v3.0.0

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

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

And then perform similar searches for push, push-r, pull and pull-r and replace with ml, mr, -ml and -mr as appropriate.

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-N-cols classes:

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

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

Responsive

If you use w-10-cols 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-4-cols and lg:w-10-cols.

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

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

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

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

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

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

Nesting

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

w-4-cols inside w-6-cols inside w-8-cols
document.html
<div class="w-8-cols h-120 bg-column mt-20 py-20">
  <div class="w-6-cols h-80 bg-column-alt py-20">
    <div class="w-4-cols 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-1/2-cols inside w-1/2-cols, you'll get one 6 design column wide element wrapping a 3 design column wide element:

w-1/2-cols inside w-1/2-cols
document.html
<div class="w-1/2-cols">
  <div class="w-1/2-cols"></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-1/2-cols inside w-8-cols
document.html
<div class="w-8-cols">
  <div class="w-1/2-cols"></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-3-cols inside of w-1/2-cols, you'll end up with a 1.5 column wide child column:

w-3-cols inside w-1/2-cols
document.html
<div class="w-1/2-cols h-80">
  <div class="w-3-cols 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-8-cols">
  <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

If you want to make layout grids, using flex as opposed to CSS Grid, you may want to use .cols-container. This adds some auto gutter margins to .w-N-cols children:

document.html
<div class="cols-container">
  <div class="w-1-cols"></div>
  <div class="w-1-cols"></div>
  <div class="w-1-cols"></div>
  <div class="w-1-cols"></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-1-cols"></div>
  <div class="w-2-cols 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="cols-container">
  <div class="w-1-cols ml-2-cols"></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 mr-N-cols:

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

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

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

And, within .cols-container:

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

New in 3.10.0, margin-inline-start (MDN) and margin-inline-end (MDN) with ms-N-cols and me-N-cols:

w-4-cols ms-2-cols
document.html
<div class="flex flex-row mt-20">
  <div class="w-4-cols ms-2-cols"></div>
</div>
w-4-cols ml-auto me-2-cols
document.html
<div class="flex flex-row mt-20">
  <div class="w-4-cols ml-auto me-2-cols"></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-2-cols pr-4-cols
document.html
<div class="pl-2-cols pr-4-cols">
  <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-1/2-cols inside of pl-2-cols
document.html
<div class="pl-2-cols">
  <div class="w-1/2-cols"></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 ps-N-cols and pe-N-cols:

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

Positioning

You can also .left-N-cols, .left-N-cols-gutter, .right-N-cols and .right-N-cols-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 w-N-cols class.

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

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

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

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

start-2-cols
end-4-cols
document.html
<div class="relative h-80">
  <div class="w-2-cols absolute top-0 start-2-cols"></div>
  <div class="w-2-cols absolute top-40 end-4-cols"></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-3-cols ml-2-cols-no-gutter
document.html
<div class="cols-container">
  <div class="w-3-cols ml-2-cols-no-gutter"></div>
</div>

vw calc variants

Added in v3.6.0

Regular layout classes w-3-cols 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-3-cols type classes may not work, and you may need to use vw based alternatives.

using w-4-cols ❌ - too wide

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

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

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

w-4-cols 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-2-cols-vw, .ml-2-cols-vw etc.) will work correctly inside of .breakout type content.