Skip to page content

Layout

Since September 2023, all “evergreen” browsers (Chrome, Edge, Firefox, Safari) have capability for the new CSS Container Queries (see Can I use: Container Queries). So, Baselayer now has container query powered layouts to control flex, grid, and hidden utility classes.

Container query contexts

Since Baselayer v.3.4.0, The <body> tag now has container query context applied by container-type: inline-size.

There are no @media queries in Baseayer. Responsive flexbox, grid, and invisibility classes are all set using @container queries. Since the <body> tag now has container query context by default, these will all behave in much the same way as media-query controlled grid systems in other CSS libraries.

The Baselayer container class only adds another container query context wherever you need it. For x-axis centering and spacing similar to e.g. the Bootstrap container, you need Baselayer wrappers.

Decoration spacing variables --s-3 and --s-4 involve a clamp() that has its middle value set using cqi (container query inline (width) percentile unit). These spacing variables are used on margin, padding, gap, and border radius utilities.

Text alignment utility classes also have responsive container query variants, as does the block utility class (see below).

Blocks

  • block — make an inline element behave as a block element. Also available: responsive blocks
  • inline-block — to enable block-like settings on an inline element (width, height, margins, paddings)
  • inline-flex, flex — see flex layouts
  • grid — see grid layouts

Dimensions

Wrappers

Baselayer’s wrapper classes add a constrained layout width, inline margin (x-axis) auto centering, and side edge whitespace when the viewport width is at or narrower then the wrapper width.

Baselayer’s wrapper classes were formerly known as container classes, copying the name from other CSS frameworks such as Bootstrap. But after switching from @media queries to @container queries, a context-container was required for parent elements of container query controlled layout systems. Therefore, the Baselayer container class is now exclusively used for that purpose, and container is reserved for setting container query contexts. See container query powered layouts.

The centered layout wrapper is set up as follows:

/* In root.css */

:root {
  ...
  --s-4: clamp(2rem, 1rem + 2.5cqi, 3rem);
  ...
  --w-xxl: 1792px;
}

/* In layout.css */

.wrapper,
[class*="wrapper-"] {
  --w-max: var(--w-xxl);
  width: min(100% - var(--s-4), var(--w-max));
  margin-inline: auto;
}

Wrapper side spacing is provided by --s-4 when the viewport width is less than --w-max. This adds a some negative space (commonly known as whitespace, though it is not always white) right and left of the wrapper, to prevent text being difficult to read when up against the sides of the viewport.

There are several wrapper utilities, with maximum widths same as the width utilities (see below).

Widths

256px intervals. w- classes will have widths 100% until their max width. wrapper- classes will have widths (100% minus side space) until their max width.

The difference between width utilities and wrapper utilities is that wrappers have x-axis side padding and x-axis centering (see above).

  • w-xxs — maximum width 256px
  • wrapper-xs / w-xs — maximum width 512px
  • wrapper-sm / w-sm — maximum width 768px
  • wrapper-content / w-content — maximum width 66ch
  • wrapper-md / w-md — maximum width 1024px
  • wrapper-lg / w-lg — maximum width 1280px
  • wrapper-xl / w-xl — maximum width 1536px
  • wrapper / wrapper-xxl / w-xxl — maximum width 1792px

Four more:

  • w-100% — width expands to 100% of available space
  • w-100vw — width expands to 100vw (viewport width)
  • w-max-100vw — width is constrained to 100vw (viewport width); use this to prevent horizontal scrolling
  • w-fit-content — width is constrained to the max-width of its content; use this for “shrink wrapping”

Heights

  • h-100% — height expands to 100%, e.g. for making cards equal to the height of their wrapper
  • h-100dvh — height expands to 100dvh, e.g. for making “full cover” panels
  • h-max-100dvh — height is constrained to 100dvh, e.g. for tall sidebars (use this in conjunction with overflow-y)

Baselayer uses 100dvh (dynamic viewport height) that gives a different viewport height for some devices — i.e. it compensates for the retracting interface toolbars on iOS Safari.

Box

  • box — expands an inner element using position: absolute; inset: 0 to fill the size of its wrapper (use in conjunction with relative positioning on the parent element of a box). This is useful for setting up a panel (e.g. hero or card) background image with text overlay.

Positions

  • relative
  • absolute
  • sticky
  • top — top: 0
  • right — right: 0
  • bottom — bottom: 0
  • left — left: 0
  • place-center — place-self: center (since Baselayer 3.7.0)
  • z-1 — z-index: 1 — e.g. use low value z-indexes for layering in hero components
  • z-2 — z-index: 2
  • z-3 — z-index: 3
  • z-997 — z-index: 997 — e.g. use high value z-indexed for layering in modals and fixed or sticky menubars
  • z-998 — z-index: 998
  • z-999 — z-index: 999

Example:

Top
Right
Bottom
Left
Centered
and middled
<div class="relative">

  <div class="absolute top">
    Top
  </div>

  <div class="absolute right">
    Right
  </div>

  <div class="absolute bottom">
    Bottom
  </div>

  <div class="absolute left">
    Left
  </div>

  <div class="box place-center">
    Centered and middled
  </div>

</div>

Container query powered layouts

Since Baselayer 3.4.0, the <body> tag provides a container query context, using container-type: inline-size. Additionally you can set another (inner) container query context ising the container class.

The container class does not constrain the outer element’s width in any way. You may also need to control the widths of your content with e.g. wrapper or width utilities, or by placing the container within a grid cell, etc.

There are four @container variants for flex, grid, and hidden classes corresponding to prefix widths:

  • xs:* (512px) — new in Baselayer 3.7.0
  • sm:* (768px)
  • md:* (1024px)
  • lg:* (1280px)

Classes with container query variants are:

  1. t-left, t-center, t-righttext alignment utilities
  2. block, inline-block — block wrapper utilities
  3. flex, inline-flex, and all flex- modifiers — flexbox utilities
  4. grid, and col- and row- modifiers — grid system utilities (using CSS Grid)
  5. hidden — invisibility utilities

Note: content-grid is not controlled by a container query.

There are some demos of container query layouts in these docs, each set within their own container context — and they have an x-axis resizer, so that you can play with them (if your device enables you to do so) and see how they work. The resizer is indicated by a dashed border and a “resizer” symbol in the bottom right corner.

Here is an example resizer with no demo inside:

Resizer symbol ↘

Responsive blocks and inline-blocks

block and inline-block have (base), xs:, sm:, md: and lg: variants.

Use one of the container-responsive breakpoint width variants as an override when you only need some element to behave as a flex or a grid within a smaller width but not at or above a larger width.

Example: in the template for this documentation, at body-container width 1024px and up (i.e. same as viewport width), the Baselayer logo and title in the used md:block to switch off the flex that’s required to push these to the left of the menubar. This push would break the layout when the menubar is a sidebar from md up, so md:block to stop that heppening.

Flex layouts

Flexbox utilities for simple layout, menubars, pagination lists, cards, etc.

All of the following have (base), xs:, sm:, md: and lg: variants:

  • flex — flexbox at all viewport widths
    • flex puts flexbox behaviour on flex items, while the flexbox wrapper continues to behave as a block element. And the flex items will behave as blocks.
  • inline-flex — inline flexbox at all viewport widths
    • inline-flex makes the flexbox wrapper itself behave as an inline item (similar to inline-block), as well as putting flexbox behavior on flex items (immediate child elements). And the flex items will behave as inline-blocks.
  • X-axis: flex-start / flex-center / flex-end
  • Y-axis: flex-top / flex-middle / flex-bottom
  • flex-wrap — gives you flex-wrap: wrap
  • flex-column — gives you flex-direction: column
  • flex-row — gives you flex-direction: row (default behavior)
  • flex-space-between — gives you justify-content: space-between
  • flex-grow-equal — makes grid item expand so that they occupy an equal fraction of the total width (or height, if used with flex-column)
  • flex-grow-auto — makes grid item expand so that they occupy an unequal fraction of the total width (or height, if used with flex-column). Each flex item will expand as required by its respective content.

Examples:

One
Two
Three
Four
<div class="flex">
  <div>One</div>
  <div>Two</div>
  <div>Three</div>
  <div>Four</div>
</div>
One
Two
Three
One
Two
Three
One
Two
Three
<div class="my-3 inline-flex">
  <div class="b-thin p-cell">One</div>
  <div class="b-thin p-cell">Two</div>
  <div class="b-thin p-cell">Three</div>
</div>

<div class="my-3 inline-flex">
  <div class="b-thin p-cell">One</div>
  <div class="b-thin p-cell">Two</div>
  <div class="b-thin p-cell">Three</div>
</div>

<div class="my-3 inline-flex">
  <div class="b-thin p-cell">One</div>
  <div class="b-thin p-cell">Two</div>
  <div class="b-thin p-cell">Three</div>
</div>

Using flex modifiers

flex flex-grow-equal:

One
Two
Three
Four
<div class="flex flex-grow-equal">
  <div>One</div>
  <div>Two</div>
  <div>Three</div>
  <div>Four</div>
</div>

flex flex-space-between:

One
Two
Three
Four
<div class="flex flex-space-between">
  <div>One</div>
  <div>Two</div>
  <div>Three</div>
  <div>Four</div>
</div>

flex flex-column transforms flexbox to operate on the vertical axis instead of horizontal:

One Two Three Four
<div class="flex flex-column">
  <span>One</span>
  <span>Two</span>
  <span>Three</span>
  <span>Four</span>
</div>

Flex-item grow

  • grow — gives you flex-grow: 1
(no grow)
grow
<div class="flex">
  <div></div>
  <div class="grow"></div>
</div>

Flex gaps

gap-* — adds a horizontal and vertical gap (same as for grid layouts):

  • gap-tiny — using --s-tiny
  • gap-1 — using --s-1
  • gap-2 — using --s-2
  • gap-3 — using --s-3
  • gap-4 — using --s-4

flex gap-2:

One
Two
Three
Four
<div class="flex gap-2">
  <div>One</div>
  <div>Two</div>
  <div>Three</div>
  <div>Four</div>
</div>

Note: flex and grid these gaps have the same spacing CSS variables as margins and paddings.

Flex and container queries

Baselayer has @contaner controlled flex and all flex modifier classes (including the gap-* classes) that will take effect at the following container widths up — use one of these instead of simply flex on your outer block element:

  • xs:flex, and modifiers sm:flex-start etc. — flex at container width 512px and up
  • sm:flex, and modifiers sm:flex-start etc. — flex at container width 768px and up
  • md:flex, and modifiers md:flex-start etc. — flex at container width 1024px and up
  • lg:flex, and modifiers lg:flex-start etc. — flex at container width 1280px and up

Example using a container outer with flex flex-column sm:flex-row sm:flex-end gap-2:

This sets up a classic switch from phones menu to tablets-up menubar. (Buttons have an inline-block built in.)

<nav class="flex flex-column sm:flex-row sm:flex-end gap-2">
  <a class="btn flex-start" href="#/">Home</a>
  <a class="btn flex-start" href="#/">About</a>
  <a class="btn flex-start" href="#/">Blog</a>
  <a class="btn flex-start" href="#/">Contact</a>
</nav>

Grid layouts

Setting up a grid

Controlling tracks at grid wrapper level:

  • Grid — the grid class initializes the CSS grid. It only adds display: grid — it doesn’t provide information about how many columns you want, or what their widths will be. To control the number of columns, you either:
    • Use the explicit grid by adding equal- classes on the grid wrapper (see below); or
    • Use the implicit grid by controlling the position of grid items.
  • Gap (optional) — adds vertical and horizontal whitespace (a.k.a. gutters) along internal grid tracks. See grid gaps.
  • Equal width grid cell control (optional)equal-*-cols etc. specifies how many columns your layout has (2, 3, or 4), where each column width is equalized.
  • Dense packing (optional)grid-dense can be used as a quick way to automatically reorder grid items: packing later items into earlier empty cells if there’s enough space for them. There is a dense packing example below, after where we have described per-item control.

Having 2, 3, or 4 explicit CSS grid columns covers most use cases for the traditional 12 column grid system in webpage design (12 is used because it is divisible by 2, 3, or 4 without remainder). The Baselayer grid can do all that, and so much more.

1
2
3
<div class="grid equal-3-cols">
  <div></div>
  <div></div>
  <div></div>
</div>

If you do only this to set up a Baselayer grid, then each grid item will automatically occupy the next available grid cell and span one grid cell. If you have more grid items than set columns, the surplus will wrap onto new row(s).

1
2
3
4
5
<div class="grid equal-3-cols">
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
</div>

Grid gaps

  • gap-* — adds a horizontal and vertical gap between grid cells, using the same spacing variables as for margins and paddings:
    • gap-tiny1 — using spaing variable --s-tiny
    • gap-1 — using spaing variable --s-1
    • gap-2 — using spaing variable --s-2
    • gap-3 — using spaing variable --s-3
    • gap-4 — using spaing variable --s-4

These same gap-* classes are used for flex layouts.

With gap-tiny

gap-tiny is good for button groups and form-button combos, where you want a gap to be approx 1 charcter width.


With gap-1

With gap-2

With gap-3

With gap-4
<div class="grid equal-3-cols gap-2">
  <div>...</div>
  <div>...</div>
  <div>...</div>
</div>

Controlling grid items

If you want your grid items to simply be placed in adjacent cells and to span one cell each, and you wanted your columns to be equal width, then there's nothing else you need to do.

For example, each of the following grid examples has 4 grid cells.

grid equal-2-cols:

1
2
3
4

grid equal-3-cols:

1
2
3
4

grid equal-4-cols:

1
2
3
4
<div class="grid equal-4-cols">
  <div>...</div>
  <div>...</div>
  <div>...</div>
  <div>...</div>
</div>

Controlling positioning and spanning at per-grid-item level:

  • Positioning (optional)col-* and row-* etc. – for positioning each grid item over non-adjacent grid cells.
  • Spanning (optional)col-span-* and row-span-*etc. – spanning 2, 3, or 4 columns or rows.

CSS grid positions grid items automatically on the available grid cells — so an item will be placed on the next available cell until required to begin again on the next (i.e. new) row. You can use this automatic positioning to your advantage, allowing CSS grid to presume where you want your next item to be. As in the following simple example:

1
columns 2 to 3
<div class="grid equal-3-cols gap-2">
  <div>...</div>
  <div class="col-span-2">...</div>
</div>

The col-span- and row-span- spanning classes can be used to make grid items to span up to 4 columns and/or 4 rows.

The col- and row- positioning classes are used to instruct the grid which grid cell you want your grid item to be placed on (up to 4 columns and/or 4 rows).

Pairing spanning with positioning gives yo even more control. In the following example, we specified that item 3 should go on row-2 and the CSS grid automatically figured out that item 4 should start in the next available grid column (i.e. column 3):

1
2
columns 1 to 2, row 2
columns 3 to 4, rows 1 to 2
<div class="grid equal-4-cols gap-1">
  <div>1</div>
  <div>2</div>
  <div class="col-span-2 row-2">columns 1 to 2, row 2</div>
  <div class="col-span-2 row-span-2">columns 3 to 4, rows 1 to 2</div>
</div>

Grid items and dense packing

When you control the positioning of grid items, you cam sometimes leave leave spaces of unoccupied cells. This is because CSS grid automatially tries to place the next item in the next available cell — it does not automatically back-fill any empty cells that you have left:

Item 1
Item 2
Item 3
Item 4
Item 5
<div class="grid gap-1 equal-4-cols">
  <div>Item 1</div>
  <div class="col-1 col-span-2">Item 2</div>
  <div class="col-2 col-span-3">Item 3</div>
  <div class="col-4">Item 4</div>
  <div class="col-3">Item 5</div>
</div>

With the grid-dense modifier you can back-fill some or all of these unoccupied cells, by CSS grid reordering (rearranging) your grid items to fill in the spaces. Here’s the example above again, but with grid grid-dense:

1
2
3
4
5
<div class="grid gap-1 equal-4-cols grid-dense">
  <div>Item 1</div>
  <div class="col-1 col-span-2">2</div>
  <div class="col-2 col-span-3">3</div>
  <div class="col-4">4</div>
  <div class="col-3">5</div>
</div>

Grid and container queries

The Baselayer grid system has three tiers of container query breakpoint widths, for creating different grid layouts for different sized containers: all widths, sm:, md:, and lg:.

  • Tier 0: grid layout effective at all container widths (including below 512px)
  • Tier 1: xs:grid grid layout effective at container widths 512px and up
  • Tier 2: sm:grid grid layout effective at container widths 768px and up
  • Tier 3: md:grid grid layout effective at container widths 1024px and up
  • Tier 4: lg:grid grid layout effective at container widths 1280px and up

These three breakpoint prefixes can also be added to grid wrapper modifier equal- classes (to specify that you want 2, 3, or 4 equalized columns at those container widths). And they can also be added to the per-item positioning and spanning classes.

The tier 0 grid system has no container query prefixes — it takes effect at all widths. This makes the tier 1 grid ideal for creating small icon galleries, or for making small media objects that you don’t want to “stack collapse” in narrow columns or on phones (e.g. social messaging or comment cards).

Responsive grid layout tiers can be combined

Therefore you can create up to four different layouts on the same grid.

Simple example: With just adding a container around your grid, and then container query width prefixes on equal- classes, this is all you need for setting up equal width items such as in image galleries, or sets of cards:

1
2
3
4
5
6
<div class="container">
  <div class="grid xs:equal-2-cols md:equal-3-cols gap-2">
    <div>1</div>
    <div>2</div>
    <div>3</div>
    <div>4</div>
    <div>5</div>
    <div>6</div>
  </div>
</div>

Another example:

Media object

Lorem ipsum dolor, sit amet consectetur adipisicing elit. Magni rem animi quaerat accusantium illum architecto, nemo, ex harum voluptatum adipisci eum blanditiis dolorum.

A few more examples can be found in examples, where you can see how container query breakpoint width tiers can be used on grid items for positioning and spanning.

Invisibility (hidden) classes

There may be situation where you require some element(s) to be displayed on smaller or larger container widths, but hidden otherwise. Baselayer has:

  • xs:hidden — hides elements in containers with width 512px and up
  • xs:hidden-below — hides elements in containers with width below 512px
  • sm:hidden — hides elements in containers with width 768px and up
  • sm:hidden-below — hides elements in containers with width below 768px
  • md:hidden — hides elements in containers with width 1024px and up
  • md:hidden-below — hides elements in containers with width below 1024px
  • lg:hidden — hides elements in containers with width 1280px and up
  • lg:hidden-below — hides elements in containers with width below 1280px
Invisibility: ✓ = displayed; ✗ = hidden
Class Example ≤512px 512px–767px 768px–1023px 1024px–1279px ≥1280px
xs:hidden-below
Example
sm:hidden-below
Example
md:hidden-below
Example
lg:hidden-below
Example
xs:hidden
Example
sm:hidden
Example
md:hidden
Example
lg:hidden
Example

Content-grid

content-grid is intended for long-read (a.k.a. long-form) prose such as blog losts, news articles, and academic papers. Using a 7 column CSS Grid layout, the default behavior of content-grid will place your content in the middle (column 4), where it will have maximum width --w-content: 66ch.

content-grid expects your content typographic blocks (headings, paragraphs, lists, tables, etc.), and the panel blocks below, to be its immediate children.

Note: content-grid is not a container query powered layout — it doesn’t need to be.

Popout panels

Use the popout utility class on an immediate child of content-grid to make an element span the middle 3 columns (3 to 5) instead of only column 4. Columns 3 and 5 have width --s-4.

Example information panel using popout:

☆ Information panel
<div aria-label="Note" class="popout my-4 bt-heavy b-blue b-300 dark:bg-700 rad p-3 bg-gray bg-100 dark:bg-900">
  &star; Information panel
</div>

Expanded panels

Sometimes you need to expand a panel more than as is done in the popout above. You can do this using the expand class, that makes an immediate child of content-grid to span the middle 5 columns (2 to 6). The expanded area has max-width equivalent to Baselayer var(--w-md) (1024px default).

Example “poster” infographic panel using expand (and showing how Baselayer’s aspect ratio utilities work):

This is a lot of example text that may or may not distort the aspect ratio (16×9) of this expand component.

See what it does on a small viewport width (e.g. phone).

A z-index positioning layer (e.g. z-1) is required to make the text overlay the image layer. (Alternatively, you can add another relative context.)

<div class="expand mb-2 aspect-ratio-16x9 flex flex-center flex-middle relative">
  <svg>...</svg>
  <div class="z-1 w-sm aspect-ratio-16x9 p-3">
    <div class="flow">
      <p class="h1 t-bold">This is a lot of example text that may or may not distort the aspect ratio (16×9) of this <code>expand</code> component.</p>
      <p class="h1 t-bold">See what it does on a small viewport width (e.g. phone).</p>
    </div>
  </div>
</div>

For information on the flow utility class, see typographic block elements.

Full-bleed panels

Use the full-bleed utility class to make an element span all 7 columns of a content-grid.

Columns 1 and 7 (the first and last column) have a minimum width of --s-2 — providing the middle columns with inline (x-axis) side whitespace.

If your layout has no sidebars, side spacing (margin or padding), or other object that takes up some of the viewport width, then your content-grid full-bleed will expand to the full width of the viewport. But if it can’t get to the full viewport width, then it will expand to the available width (as seen in the docs example below).

Example colored stripe using full-bleed:

full-bleed — expands to the full width of the avilable space. If there are no sidebars, it will reach the sides of the viewport.
<div class="full-bleed">
 Full bleed panel content...
</div>

Aspect ratios

Common aspect ratio constraints for images, video, and hero blocks.

aspect-ratio-1x1
aspect-ratio-4x3
aspect-ratio-16x9
aspect-ratio-21x9

Overflows

Using auto to add scrollling when the content of a block exceeds its constrained height or width.

  • overflow-x — e.g. for wrapping tables with a lots of columns, that would break a template layout in small viewports
  • overflow-y — e.g. for sidebar menus loaded with content

Overflow clip

  • overflow-clip — for hiding content that overflows your set dimensions on an element, and for preventing images to show in the corners of elements that have rounded corners.

Floats

This is an example of
float-right

Lorem, ipsum dolor sit amet consectetur adipisicing elit. Corporis dolore quis iste fuga molestias necessitatibus. Autem quidem, consequuntur dicta illo rem nobis ratione vel? Cupiditate beatae similique nobis temporibus sequi rerum mollitia, saepe architecto ad, necessitatibus placeat repudiandae commodi laboriosam quos molestiae sed modi dignissimos nisi magni adipisci eligendi.

This is an example of
float-left

Lorem, ipsum dolor sit amet consectetur adipisicing elit. Corporis dolore quis iste fuga molestias necessitatibus. Autem quidem, consequuntur dicta illo rem nobis ratione vel? Cupiditate beatae similique nobis temporibus sequi rerum mollitia, saepe architecto ad, necessitatibus placeat repudiandae commodi laboriosam quos molestiae sed modi dignissimos nisi magni adipisci eligendi.

There's also a clearfix class, if you need it.