A Tailwind CSS strategy for Visual Builder grids

More findings from using an Optimizely SaaS CMS instance; setting up a CSS strategy for rendering Visual Builder grids.

This is tested with the Content Type Package in the CLI tool from the previous blog post.

For a look at how you can make use of it, this other previous blog post shows how routing and rendering is set up.

Strategy should work fine for different stacks, something JS-based as well as Razor Components (Blazor) or MVC/Razor Pages.

I prefer Tailwind CSS but in the end it's all just CSS so the strategy should be applicable for any CSS setup.

I've created an Experience component that handles rendering for the initial three-level hierarchy; grids (sections) → rows → columns — using "CSS Grid".

Sections

Each grid/section is rendered as a top-level <div> whose classes depend on a Design display setting or if a shared block is detected:

Default grid

grid grid-cols-6 gap-4 w-full - A 6-column grid with a gap-4 gutter.

Plain grid (design01 in the Content Type Package)

grid grid-cols-6 w-full - A simple 6-column-spanning grid with no padding or inter-column gaps. Used for stacking full width content without any design applied to the section - adding multiple columns in same row might not be recommended with this style set.

Typically used with "Standard Page"-like content sections (like Heading, Preamble, Rich Text Body).

Component node (for shared blocks)

grid grid-cols-1 w-full - A single-column grid used for shared blocks or non-grid sections (awaiting support for shared blocks to be used under the Column level but for now; this is the only place for shared blocks).

Rows

Rows are iterated but do not emit a wrapper element; columns flow directly into the parent 6-column grid, flattening the row level which we don't need when using "CSS Grid" (as opposed to solutions using Flex).

This means that there is no data-epi-block-id for the Row in the output but I haven't seen any downsides from this.

A row is just a grouping of columns, used to do the math for which col-span-* class to render on the column's <div>.

Columns

Each column receives a <div> with a col-span-* class determined by the number of sibling columns in its row:

Cols Classes Behavior
1 col-span-6 Full width
2 col-span-6 lg:col-span-3 Full width on mobile; half (3/6) on large screens
3+ col-span-6 lg:col-span-2 Full width on mobile; third (2/6) on large screens

With this responsive pattern: columns stack vertically on small screens and arrange side-by-side from the lg: breakpoint and up to 3 items per visual row - but we could add to code and support six components per row if the design requires it - now they just render on a new row after each three (so it's not mandatory for the editor to create more rows in the Visual Builder, more than three in a row will make all render in the smallest width).

Additional styling affecting columns

Default grid columns

bg-white border-1 border-slate-300 rounded-lg ring-1 shadow-xs ring-black/5 overflow-hidden - A card with background, border, shadow, and rounded corners.

Corner columns in the first and last rows also receive an extra-large border-radius to create a visual grouping:

  • First row, first column → lg:rounded-tl-4xl
  • First row, last column → lg:rounded-tr-4xl
  • Last row, first column → lg:rounded-bl-4xl
  • Last row, last column → lg:rounded-br-4xl
Plain grid columns

space-y-4 - Only vertical spacing between child elements.

Components (also referred to as Elements)

Inside each column, individual elements are wrapped in a <div>, currently with no other utilities set. The element's own component then takes care of other design details.

Comments?

Published and tagged with these categories: Optimizely