Tags
November 23, 2023
by
Alexander VerbruggenRead more about this author

Layout

Building any application involves two major components:

  • content: titles, tables, paragraphs of text, images,... anything meant to convey information of some kind to the user
  • layout: dictating exactly where and how this content should appear

In this article we'll focus on layout and specifically how it is done in page builder.

There is one rule that applies to pretty much everything regarding layout in page builder: the parent dictates the layout of the children. There are a select number of ways that a child can force a particular layout for itself, but these should be used sparingly.

Grid

The primary tool used for layout in page builder is the grid: an infinitely nestable set of rows and cells. Even though infinite nesting is possible, we advise using as few levels as necessary to achieve the layout you want.

Rows are primarily used for positioning cells while cells can contain either content or more rows (not both) which in turn can contain cells.

Example layout.

The row has a quick button that allows you to rotate it from the default horizontal to vertical. This can also be toggled in the styling section:

Direction dimension

The cell has a quick button to "wrap" it. Suppose you have a header and belatedly realize you want to position a button right next to it:

Wrap cell

If you were to hit the chevron button on the top-most H1 cell, it would generate the structured you see below it, allowing you to easily add additional items right next to it.

In the default rendering mode, page builder uses flex to render the grid correctly, for those interested in the nitty gritty details of flex, this site has a good summation of it.

Alignment

If we take a simple example where you add some content cells to a row, alignment can be used on the row to dictate where the cells should go.

If we configure no particular alignment, the default will be top left while the cells are stretched to fill the row:

Default alignment

In the default setup, there are a number of alignment options available to the row:

Alignment options

We won't go over them all in detail, but it is important to understand the concept of main and cross as they determine a lot of the options. Cross and main are relative to the overall content direction of the container.

A row has a default content orientation of horizontal (so cells are rendered from left to right) while columns have a default vertical orientation. You can change these default orientations which will affect what cross and main mean.

For a standard row this means the main direction influences horizontal layout while the cross direction influences the secondary direction: vertical.

To visualize this, let's enable cross-center and main-end, in a default row this means the content should come horizontally at the end (which in a left-to-right is on the right) and in the vertical direction we want it centered:

Cross center main end aligment

If we change main-end with space-between, the row will lay out the children with as much (evenly distributed) space between them as possible:

Space-between

If we set it back to main-end but now reverse the direction from right to left, "end" actually means on the left:

Reverse direction

That one button

There is a recurring design pattern where you want to render a menu with buttons on the left and then one particular button on the right:

Button right

There are a number of ways to achieve this. We could for instance create two separate containers at the row level and use space-between to separate them as far as possible. One container contains the buttons on the left, the other contains the button (or buttons) on the right:

Alignment solution

This is the "pure alignment" option where we still use aligment to solve our problem. There is however a different way to do this:

Position solution

We have all the buttons in the same row which means there is no combination of alignment options that will achieve the visual effect we are looking for.

But this is one of those cases where a child can position itself in the parent. We can toggle "right" in the position part of the styling for the special button:

Position options

Where alignment is to lay out ones children, position is used to position oneself in the parent.

Positioning

When you are positioning yourself and thus forcing a particular layout that your parent did not foresee, it becomes important to note at which level we must apply this.

If we check the styling section of our button we actually see two stylable components:

Styling levels

The cell is the container that has the button while the button is of course the component itself. If we were to toggle the "right" position on our button, it would simply position itself on the right side of the cell.

However, the cell is usually only exactly as big as the content it contains which means visually nothing will happen.

Instead you want to position the cell relative to the parent row, so we must set the position setting on the cell.

Spacing

Another important part of layout is spacing: the amount of whitespace in between content.

This can be a consequence of alignment (e.g. space-between) but in most cases you will want to set spacing explicitly to guarantee a certain amount of whitespace. In a very general sense there are concepts for spacing that are usually used in css:

  • padding: spacing on the content of your component
  • margin: spacing around your component, so relative to siblings, parents,...

Margin is applied to an element to affect not so much itself but its relation to other elements. This goes against the core design rule where parents dictate the layout of its children.

So aris (the styling framework in page builder) does not use margin at all. Instead it relies on the flex gap concept which we apply to the parent and which is enforced on its children.

This has some benefits over margin:

  • we don't need to apply it to each element, instead the parent container has a singular value
  • margin becomes hard to use with content wrapping where you might not be sure how many siblings are on the same line or are wrapped to the next line. Gaps are calculated correctly when content is wrapped.

Because there are so many possible combinations of spacing, it is by far the largest dimension in the standard aris toolbox. If we look at the horizontal options:

Horizontal spacing

You can see there are a lot of them. There is a granularity to each option though, for example medium actually encapsulates both horizontal-medium and vertical-medium.

While horizontal-medium actually encapsulates the left and the right side and also the horizontal gap between content.

We could also just set the horizontal-sides-* which no longer sets the gap but just left and right or we could just set horizontal-left-* and horizontal-right-*.

You can also combine these options where the general rule is: the amount of dashes indicates the specificity of the rule.

This means we can combine medium with horizontal-right-none which means gaps in all direction, top, left and bottom will all be medium while specifically the right side has no spacing.

It also means we can't predictably combine horizontal-sides-medium and horizontal-right-none because they have the same specificity. However because sides only covers left and right we can just set horizontal-left-medium and horizontal-right-none to get the desired effect.

Where to apply it

As always the general rule applies: layout is done by the parent. Take this example:

It

They might look the same but they are configured differently. The left example has spacing configured on all 3 content cells, effectively using padding to provide the whitespace.

The right example simply sets spacing large on the parent adding padding to the parent and a gap setting for the content to match. The difference becomes very visible once we add some color:

Colored spacing

If we set spacing on each invidual cell, we need to think how the spacing of one cell affects the other. We don't want double spacing to happen because both elements apply it independently. If you add a new cell, you will have once again set the correct spacing. If you change your mind about the amount of spacing you will have to go over all the cells to change it.

By setting spacing on the parent we sidestep all these issues.