If you squint at most websites, you’ll start to see the same repeated patterns. For example, a common product or service homepage might have the following structure:
Navigation is usually logo to the left and navigation items to the right. Heroes are a big, impactful statement to the left of something visual or centered and surrounded by oodles of weird shapes. The tops and bottoms of websites don’t change often, but every page probably has a unique middle. How you enable editors to work in that middle can make or break your Wagtail CMS.
A simple block pattern for Wagtail sites
Most websites consist of huge, stacked “blocks” of content (let’s call them “Container Blocks”), in what is known as composable design. Stacking blocks can be useful if you want flexibility in your page designs. In composable design, you should create well-defined, structured blocks like ProductOfferingBlock
and PricingCardBlock
, but sometimes you just need to stick some content into a grid and make it presentable. Our simple block pattern provides a method to do just this.
In HTML and Tailwind CSS, a simple two-column content block might look like this:
(In fact, a block just like this is found on AppPack.io, our solution to no-stress AWS deployment)
You may find it easy to imagine this HTML repeated on the page with slight variations. With this simple structure, we have some essentials:
- Content can be grouped visually via the padding provided via
py-10 lg:py-16
and the background effect (in this case,bg-gray-100
). - Additionally, since content separation happens on the outermost
<div>
, this is a great place to define a block-level theme, e.g..theme-high-contrast
. - Visual relationships between content can be established via the grid. Grid ratios can be adjusted to emphasize some content of others. Pre-defined grid options can be defined, e.g. 2 equal columns, 1 / 2 columns, 3 equal columns, etc. More complex grids are also possible.
- Some design constraints are defined: vertical rhythm (spacing) is consistent (
py-10
) and we know all grids havegap-8
on small displays,gap-16
on large displays. These constraints could also be configurable, with few options for variation. - We have clear columns to fill with content, and most designs rarely care about the height or length of columns. If something is off, it will look off.
In Wagtail CMS, you can define StreamFields to allow mixed content, from paragraphs via blocks.RichTextBlock to full fledge StructBlocks (combinations of child blocks). This fits our pattern perfectly. Porting this to Wagtail CMS, you may expect the following:
In this basic example, ContainerBlock
only accepts a set of colors (possibly for the background), and a single StreamBlock, Column
, which looks something like this:
The list of types that could be added to a Column
varies by website, but in general, they are elements and components: buttons, headings, text, embedded forms, cards, etc. If you are familiar with Atomic Design Methodolgy, Column
probably contains Atoms and Molecules, with each ContentBlock
being an Organism.
Building with ContainerBlocks
Our simple ContainerBlock
will look like this when added to a StreamField:
Editors should immediately recognize that they need to begin adding columns or content. This method is preferred for simpler grids over more explicit variations (e.g., 2ColContainerBlock
, 3ColContainerBlock
), as you gain the ability to reorder and resize content without the need to delete and recreate it in Wagtail CMS. It also allows editors to start thinking about the grid or the content first, whichever works best for their workflow.
Our example is limited to a maximum of four columns, which we have found to be flexible enough for most content and friendly to small (1 column), medium (2 columns), and large (4 columns) responsive displays. Consider this more advanced grid if you want a general, flexible starting point:
Our example is intentionally simple. Here are a few things to consider when adopting this pattern:
- ContainerBlocks could be customized in many ways: vertical padding, coloring, grid variations. Group these options, give them safe defaults, and collapse them so the editor immediately sees the ability to begin adding content. Editors will often think content first, followed by styling second.
- If you are repeating a specific
ContainerBlock
+Column
structure, such as Text + Image, consider refactoring that into a simpler StructBlock that lives as a sibling toContainerBlock
. TheContainerBlock
pattern is meant for flexibility, and has little semantic meaning. - Additionally, avoid making a
Column
a junk drawer for every component in your design system. For example, a photo carousel may fit in a single or two-columnContainerBlock
, but it has unique semantic meaning and specific display requirements. Just because it can fit does not mean it should. - Navigating the UI for deeply nested blocks can be challenging. It is easy to get lost on long pages*. Aim for shallow nesting for usability. Blocks added to
Column
should only allow ~one more level, e.g.Column
>CheckList
>ListItem
.
* All the more reason to carefully plan your Page types.
A well-tested pattern
The ContainerBlock
pattern is one we have been using since 2014, and the idea of “blocks of content” is inherent in many modern content management systems. As a tried and tested pattern, we reach for it first and refactor from it based on editor usage. When starting a new Wagtail CMS project, it allows us to quickly demo content while we flesh out Page types and other more structured StructBlock types (e.g., TextImageBlock
, CardGridBlock
, etc). Column
forces us to think about the smallest parts of a webpage, and how they will be represented to editors.
Lastly, it serves as an excellent fallback. While there may be more exact layout options, editors know they can always get something presentable with this pattern. After all, it’s all over the web.
(Featured photo by Magda Ehlers: https://www.pexels.com/photo/orange-cube-1340185/)