How to use this component
<i80-flex> lays a set of items out in a row or a column with one of the standard gaps between them, so you don't have to write flexbox CSS or know all its intricacies. <i80-flex-item> is the optional companion for a single child whose own sizing has to differ from the rest.
Basic usage
The simplest flexbox layout is an <i80-flex> with some content in it.
<i80-flex>
<div>Some content</div>
<div>Some other content</div>
<div>More content</div>
</i80-flex>
Every direct child of the flexbox container is treated, by the browser's layout engine, as a flex item (for details about what this means, refer to the MDN guide).
Here is an example with four equally sized generic content placeholders:
<i80-flex>
<div class="doc-placeholder" style="width: 100%; height: 40px; background: #e5ffd2">Some content</div>
<div class="doc-placeholder" style="width: 100%; height: 40px; background: #e4c5f3">Other content</div>
<div class="doc-placeholder" style="width: 100%; height: 40px; background: #d2f4ff">More content</div>
<div class="doc-placeholder" style="width: 100%; height: 40px; background: #fff8d2">Extra content</div>
</i80-flex>
For a child that needs its own basis, grow or shrink (see below), wrap that one child — and only that one — in an <i80-flex-item>:
<i80-flex gap="16">
<div class="doc-placeholder" style="height: 40px; background: #e5ffd2">Some content</div>
<i80-flex-item grow="0">
<div class="doc-placeholder" style="height: 40px; background: #e4c5f3">Some other content</div>
</i80-flex-item>
<div class="doc-placeholder" style="height: 40px; background: #d2f4ff">More content</div>
</i80-flex>
Putting the layout on another element
When the box has to be a list, a <nav> or a <span> inside a sentence, name that element with the tag attribute. <i80-flex> renders it around the children and steps out of the layout, so the elements you write yourself become the flex items.
<i80-flex tag="ul" gap="16" style="margin: 0; padding: 0; list-style: none">
<li class="doc-placeholder" style="height: 40px; background: #e5ffd2">Some content</li>
<li class="doc-placeholder" style="height: 40px; background: #e4c5f3">Some other content</li>
<li class="doc-placeholder" style="height: 40px; background: #d2f4ff">More content</li>
</i80-flex>
Row or column
To specify in which direction the flex items are laid out, use the direction attribute.
<i80-flex tag="ul" direction="column" style="margin: 0; padding: 0; list-style: none">
<li class="doc-placeholder" style="height: 40px; background: #e5ffd2">Some content</li>
<li class="doc-placeholder" style="height: 40px; background: #e4c5f3">Other content</li>
<li class="doc-placeholder" style="height: 40px; background: #d2f4ff">More content</li>
<li class="doc-placeholder" style="height: 40px; background: #fff8d2">Extra content</li>
</i80-flex>
Note: we don't expose the reverse directions because they come with intrinsic accessibility limitations by changing the visual order of elements on the page from the DOM order.
Space between items
To control the spacing between flex items, use the gap attribute.
Below is an example of multiple flex items with different relative sizes but an equal gap of 24px between them:
<i80-flex gap="24">
<div class="doc-placeholder" style="width: 100%; height: 40px; background: #e5ffd2">Some content</div>
<div class="doc-placeholder" style="width: 80%; height: 40px; background: #e4c5f3">Other content</div>
<div class="doc-placeholder" style="width: 60%; height: 40px; background: #d2f4ff">More content</div>
<div class="doc-placeholder" style="width: 120%; height: 40px; background: #fff8d2">Extra content</div>
</i80-flex>
To differentiate the vertical and horizontal spacing between items when they wrap on multiple rows, give gap two values separated by a space, as in CSS.
This is an example of multiple flex items with fixed sizes, appearing on multiple rows, with a vertical gap of 12px and a horizontal one of 24px:
<i80-flex wrap gap="12 24">
<div class="doc-placeholder" style="width: 450px; height: 40px; background: #e5ffd2">Some content</div>
<div class="doc-placeholder" style="width: 250px; height: 40px; background: #e4c5f3">Other content</div>
<div class="doc-placeholder" style="width: 180px; height: 40px; background: #d2f4ff">More content</div>
<div class="doc-placeholder" style="width: 200px; height: 40px; background: #fff8d2">Extra content</div>
<div class="doc-placeholder" style="width: 240px; height: 40px; background: #f3d9c5">Even more content</div>
</i80-flex>
The first value refers to the vertical gap between "rows" of items (row-gap in CSS), the second one to the horizontal spacing between "columns" of items (column-gap in CSS).
gap accepts only pre-defined values, it can't be used to provide custom spacing values. Refer to the Attributes section for details on which values are accepted.
If you need to provide custom spacing values, see below how you can use a special escape hatch for this.
Other gap sizes
If you absolutely have to use non-standard spacing value(s) for the flex gap, you can set the internal --i80-layout-flex-row-gap and --i80-layout-flex-column-gap CSS variables to custom values (e.g., via a local CSS class or an inline style).
<i80-flex style="--i80-layout-flex-column-gap: 13px">
<div class="doc-placeholder" style="height: 40px; background: #e5ffd2">Some content</div>
<div class="doc-placeholder" style="height: 40px; background: #e4c5f3">Other content</div>
<div class="doc-placeholder" style="height: 40px; background: #d2f4ff">More content</div>
</i80-flex>
If the flex items are wrapping to multiple lines, you have to overwrite both the "row" and "column" gap values:
<i80-flex wrap style="--i80-layout-flex-row-gap: 10px; --i80-layout-flex-column-gap: 0.625rem">
<div class="doc-placeholder" style="width: 450px; height: 40px; background: #e5ffd2">Some content</div>
<div class="doc-placeholder" style="width: 250px; height: 40px; background: #e4c5f3">Other content</div>
<div class="doc-placeholder" style="width: 400px; height: 40px; background: #d2f4ff">More content</div>
</i80-flex>
Alignment
Using flexbox is one of the simplest ways to control how sets of items are aligned and spaced, and it's done via a set of justify-* and align-* properties.
On <i80-flex> this is achieved using the justify and align attributes.
<i80-flex justify="space-between" align="center" gap="16">
<div class="doc-placeholder" style="height: 40px; background: #e5ffd2">Some content</div>
<div class="doc-placeholder" style="height: 80px; background: #e4c5f3">Other content</div>
<div class="doc-placeholder" style="height: 40px; background: #d2f4ff">More content</div>
</i80-flex>
These correspond to the justify-content and align-items flexbox properties in CSS.
When the direction is set to row, the first controls the horizontal alignment and the second controls the vertical alignment. When the direction is set to column, the behavior is reversed.
Note: we only expose the most commonly used values for justify-content/align-items. Refer to the Attributes section for details on which values are accepted.
Evenly distributed items
This is an example of how to implement a layout where a set of items are evenly spaced:
<i80-flex justify="space-between" gap="16" class="doc-flex-outlined-container">
<div class="doc-placeholder" style="height: 40px; background: #e5ffd2">Some content</div>
<div class="doc-placeholder" style="height: 40px; background: #e4c5f3">Other content</div>
<div class="doc-placeholder" style="height: 40px; background: #d2f4ff">More content</div>
<div class="doc-placeholder" style="width: 200px; height: 80px; background: #fff8d2">Some content that is wider and taller</div>
</i80-flex>
In this case, the justify="space-between" applied to the flex container forces the flex layout model to distribute the space evenly between the items; the space between the items remains the same, even if their size is different.
Right aligned items
This is an example of how to obtain a layout where one of the items is flushed to the right side of the container, while all the others are flushed to the left:
<i80-flex gap="16" class="doc-flex-outlined-container">
<div class="doc-placeholder" style="height: 40px; background: #d2f4ff">Some content</div>
<div class="doc-placeholder" style="height: 40px; background: #d2f4ff">Other content</div>
<div class="doc-placeholder" style="height: 40px; background: #fff8d2; margin-left: auto">Content flushed on the right</div>
</i80-flex>
In this case, by applying a margin-left: auto to the last flex item, the flexbox layout engine pushes it completely to the right (because implicitly it has margin-right: 0).
The same technique can also be applied when there are multiple elements that need to be aligned to the right:
<i80-flex gap="16" class="doc-flex-outlined-container">
<div class="doc-placeholder" style="height: 40px; background: #d2f4ff">Some content</div>
<div class="doc-placeholder" style="height: 40px; background: #d2f4ff">Other content</div>
<div class="doc-placeholder" style="height: 40px; background: #fff8d2; margin-left: auto">More content</div>
<div class="doc-placeholder" style="height: 40px; background: #e4c5f3">Extra content</div>
</i80-flex>
In this case, the margin-left: auto is applied only to the first item that is flushed to the right; all the other items are automatically pushed to the right by the first flushed right item.
Vertical centering
A classic CSS problem: how to vertically center two or more items? A reliable solution is to use flexbox alignment.
<i80-flex align="center" gap="8">
<div class="doc-placeholder" style="width: 100px; height: 100px; background: #d2f4ff">Some content</div>
<div class="doc-placeholder" style="width: 200px; height: 40px; background: #fff8d2">Other content</div>
<div class="doc-placeholder" style="width: 300px; height: 20px; background: #e4c5f3">More content</div>
</i80-flex>
In this case, the align="center" applied to the flex container forces the flex items to vertically align their centers along the "cross axis".
Both horizontal and vertical centering
Another classic CSS problem: how to center content both horizontally and vertically inside a larger container? Again, the solution is to use flexbox alignment.
<i80-flex justify="center" align="center" class="doc-flex-outlined-container" style="height: 320px">
<div class="doc-placeholder" style="width: 240px; height: 180px; background: #d2f4ff">Some content</div>
</i80-flex>
In this case, the justify="center" and align="stretch" attributes applied to the flex container force the flex item to be centered both horizontally and vertically.
Note: We have applied an outline and a fixed height to the flex container to demonstrate the behavior.
Wrapping onto more rows
One useful function of the flexbox layout model is that it allows items to wrap along multiple rows when there is not enough space in the container to fit them all in a single line.
To allow the flex items to wrap along multiple rows if needed, add the wrap attribute.
<i80-flex wrap gap="8 24" class="doc-flex-outlined-container">
<div class="doc-placeholder" style="width: 220px; height: 40px; background: #e5ffd2">Some content</div>
<div class="doc-placeholder" style="width: 250px; height: 40px; background: #e4c5f3">Other content</div>
<div class="doc-placeholder" style="width: 200px; height: 40px; background: #d2f4ff">More content</div>
<div class="doc-placeholder" style="width: 300px; height: 40px; background: #fff8d2">Extra content</div>
<div class="doc-placeholder" style="width: 240px; height: 40px; background: #f3d9c5">Even more content</div>
</i80-flex>
In this case wrap applied to the flex container allows the flex items to wrap on multiple lines. The gap="8 24" applied to the flex container defines the gap between the items along the two "directions" (the first value is the space between "rows", the second between "columns").
How the items are sized
In flexbox, it's possible to control the relative sizing of flex items within a flex container using the basis, grow and shrink properties.
These are the attributes of the same name on <i80-flex-item>, which you add around the one child that needs them.
<i80-flex gap="16">
<div class="doc-placeholder" style="height: 40px; background: #d2f4ff">Some content</div>
<i80-flex-item basis="200px" grow="1" shrink="0">
<div class="doc-placeholder" style="height: 40px; background: #e5ffd2">The flex item content here</div>
</i80-flex-item>
<div class="doc-placeholder" style="height: 40px; background: #d2f4ff">Extra content</div>
</i80-flex>
How these three properties impact the (relative) size of a flex item is not trivial. We suggest consulting the MDN guide.
Force items to use all the available space
By default, flex items use only the space necessary to fit their content. If there is extra space in the flex container, the items will not automatically expand to fill it.
<i80-flex gap="16" class="doc-flex-outlined-container">
<div class="doc-placeholder" style="height: 40px; background: #d2f4ff">Some content</div>
<i80-flex-item>
<div class="doc-placeholder" style="height: 40px; background: #e5ffd2">This content occupies the minimum space possible</div>
</i80-flex-item>
<div class="doc-placeholder" style="height: 40px; background: #d2f4ff">Extra content</div>
</i80-flex>
If we want an item to grow and occupy the available space, we have to add grow to it.
<i80-flex gap="16" class="doc-flex-outlined-container">
<div class="doc-placeholder" style="height: 40px; background: #d2f4ff">Some content</div>
<i80-flex-item grow>
<div class="doc-placeholder" style="height: 40px; background: #e5ffd2">This content occupies as much space as possible</div>
</i80-flex-item>
<div class="doc-placeholder" style="height: 40px; background: #d2f4ff">Extra content</div>
</i80-flex>
In this case we have added grow to the wrapping <i80-flex-item>, forcing it to use all the available space inside the flexbox container. A number above 1 makes it a share of the free space rather than all of it.
Prevent collapsing of content
In the example below, you can see how, if one of the flex items has an intrinsic size larger than the flex container, it may squeeze the rest of the flex items (specifically the button element in this case).
<i80-flex gap="16" class="doc-flex-outlined-container">
<div class="doc-placeholder" style="width: 100%; background: #d2f4ff">Some content that wants to occupy all the available space</div>
<button type="button">A simple button</button>
</i80-flex>
To avoid this, one has to tell the item not to shrink beyond its own size:
<i80-flex gap="16" class="doc-flex-outlined-container">
<div class="doc-placeholder" style="width: 100%; background: #d2f4ff">Some content that wants to occupy all the available space</div>
<i80-flex-item shrink="0">
<button type="button">A simple button</button>
</i80-flex-item>
</i80-flex>
In this case, we have used a wrapping <i80-flex-item> and applied shrink="0" to it, to prevent it from shrinking.
Note: the opposite case — text that should be allowed to shrink below its own width so it can ellipsize — is what the collapse attribute of <i80-flex-item> is for.
Prevent stretching of content
If one of the flex items has an intrinsic height larger than the other flex items, those items may be stretched vertically so they all have the same height:
<i80-flex gap="16" class="doc-flex-outlined-container">
<div class="doc-placeholder" style="height: 80px; background: #d2f4ff">Some content that is taller than the sibling item</div>
<button type="button" style="margin-left: auto">A simple button</button>
</i80-flex>
This behavior is due to the default behavior of the flexbox layout model, and depends on the CSS display of those items.
To avoid this, one can choose a non-stretching alignment for the items.
<i80-flex align="start" gap="16" class="doc-flex-outlined-container">
<div class="doc-placeholder" style="height: 80px; background: #d2f4ff">Some content that is taller than the sibling item</div>
<button type="button" style="margin-left: auto">A simple button</button>
</i80-flex>
In this case, the align="start" applied to the flex container forces the flex items to vertically align at the start (top) along what is called the "cross axis".
An alternative solution is to wrap the element that shouldn't stretch in an <i80-flex-item>, with no attributes at all, like in this example:
<i80-flex direction="column" gap="16">
<i80-badge color="critical">Stretched badge</i80-badge>
<i80-flex-item>
<i80-badge color="success">Non-stretched badge</i80-badge>
</i80-flex-item>
</i80-flex>
In a line of text
To change the display from flex to inline-flex, so the container sits in a line of text, add the inline attribute.
It is going to be a
<p>It is going to be a
<i80-flex tag="span" inline gap="4">
<span class="doc-placeholder" style="width: 16px; height: 16px; background: #e4c5f3">😎</span>
<span class="doc-placeholder" style="height: 16px; background: #fff8d2">Sunny day</span>
</i80-flex>
this Sunday.</p>
Nesting
It's possible to nest flexbox containers to achieve more complex layouts.
<i80-flex direction="column" class="doc-flex-outlined-container">
<div class="doc-placeholder" style="height: 50px; background: #e5ffd2">Top content</div>
<i80-flex direction="row">
<div class="doc-placeholder" style="width: 150px; background: #e4c5f3">Side content</div>
<i80-flex-item grow>
<div class="doc-placeholder" style="height: 250px; background: #d2f4ff">Main content</div>
</i80-flex-item>
</i80-flex>
<div class="doc-placeholder" style="height: 50px; background: #fff8d2">Bottom content</div>
</i80-flex>
In this case, the outer container is a column-based flexbox layout, with three child items, while the second item is a row-based flexbox layout, used to create two side-by-side content blocks. Of these two, the first has a fixed width, while the second fills the remaining area thanks to grow.
Common layout patterns
Below are some examples of common layouts that can be achieved with <i80-flex> in combination with other I80 components.
Media + Text/Content
This is a classic layout, where a "media" element (it could be an image, an icon, an avatar, etc) is laid out next to a block of text (but could be more complex content). The two elements are either visually centered along their horizontal axis, or aligned to their top.
<i80-flex align="center" gap="8">
<div class="doc-placeholder doc-flex-media-avatar" style="width: 50px; height: 50px; background: #e4c5f3">avatar</div>
<i80-text tag="p" size="200">Lorem ipsum dolor sit amet</i80-text>
</i80-flex>
<i80-flex align="center" gap="8">
<i80-icon name="info" size="24"></i80-icon>
<i80-text tag="p" size="200">Lorem ipsum dolor sit amet</i80-text>
</i80-flex>
<i80-flex align="start" gap="16">
<i80-icon-tile icon="shield" color="coral"></i80-icon-tile>
<i80-flex direction="column" gap="4">
<i80-text tag="h3" variant="display" size="400">Lorem ipsum dolor</i80-text>
<i80-text tag="p" size="200">Lorem ipsum dolor sit amet consectetur adipiscing elit</i80-text>
<i80-link-standalone href="#documentation" icon="external-link" icon-position="trailing">Documentation</i80-link-standalone>
</i80-flex>
</i80-flex>
Group of cards
Using the automatic layout offered by flexbox, you can create a group of identically sized, evenly spaced cards, like in this example:
<i80-flex align="stretch" gap="48">
<i80-flex-item basis="0" grow>
<i80-card level="mid" border>
<div style="padding: 18px 12px; height: 100%; text-align: center">
<i80-text tag="p" size="200">This is some generic content inside a card</i80-text>
</div>
</i80-card>
</i80-flex-item>
<i80-flex-item basis="0" grow>
<i80-card level="mid" border>
<div style="padding: 18px 12px; height: 100%; text-align: center">
<i80-text tag="p" size="200">This is some generic content inside a card</i80-text>
</div>
</i80-card>
</i80-flex-item>
<i80-flex-item basis="0" grow>
<i80-card level="mid" border>
<div style="padding: 18px 12px; height: 100%; text-align: center">
<i80-text tag="p" size="200">This is also some generic content inside a card, but the text is longer and makes the card grow vertically</i80-text>
</div>
</i80-card>
</i80-flex-item>
</i80-flex>
Similarly, it's easy to implement a vertical stack of cards:
<i80-flex direction="column" gap="16">
<i80-card level="mid" border>
<div style="padding: 18px 12px; text-align: center">
<i80-text tag="p" size="200">This is some generic content inside a card</i80-text>
</div>
</i80-card>
<i80-card level="mid" border>
<div style="padding: 18px 12px; text-align: center">
<i80-text tag="p" size="200">This is some other generic content inside a card</i80-text>
</div>
</i80-card>
<i80-card level="mid" border>
<div style="padding: 18px 12px; text-align: center">
<i80-text tag="p" size="200">This is also some generic content inside a card, but the text is longer and makes the card grow vertically</i80-text>
</div>
</i80-card>
</i80-flex>
Empty state
Using the horizontal/vertical alignment offered by flexbox, it is also possible to implement a standard empty state in a page. As a consumer, you will just have to take care of the height of the container in relation to the available vertical space in the page.
The item you were looking for was not found.
<i80-flex justify="center" align="center" class="doc-flex-outlined-container" style="height: 320px">
<i80-application-state title="Empty state title text" title-tag="h3" icon="alert-circle">
<p>The item you were looking for was not found.</p>
</i80-application-state>
</i80-flex>
Right aligned actions
Using the margin-left: auto trick described above, one can achieve the following layout, where one of the flex items is flushed to the right:
<i80-flex align="start" gap="16" class="doc-flex-outlined-container">
<i80-icon-tile icon="shield" color="coral"></i80-icon-tile>
<i80-text tag="h3" variant="display" size="500">Lorem ipsum dolor</i80-text>
<i80-flex-item style="margin-left: auto">
<i80-dropdown text="Main actions">
<i80-dropdown-item href="#one">Action One</i80-dropdown-item>
<i80-dropdown-item href="#two">Action Two</i80-dropdown-item>
<i80-dropdown-item href="#three">Action Three</i80-dropdown-item>
</i80-dropdown>
</i80-flex-item>
</i80-flex>
If needed, multiple items can be flushed to the right:
<i80-flex align="start" gap="16" class="doc-flex-outlined-container">
<i80-icon-tile icon="shield" color="coral"></i80-icon-tile>
<i80-text tag="h3" variant="display" size="500">Lorem ipsum dolor</i80-text>
<i80-flex-item style="margin-left: auto">
<i80-button icon="edit">Main action</i80-button>
</i80-flex-item>
<i80-dropdown text="More actions" color="secondary">
<i80-dropdown-item href="#one">Action One</i80-dropdown-item>
<i80-dropdown-item href="#two">Action Two</i80-dropdown-item>
<i80-dropdown-item href="#three">Action Three</i80-dropdown-item>
</i80-dropdown>
</i80-flex>
Attributes
<i80-flex> renders this component. Everything it understands:
| Attribute | Values | Default | Notes |
|---|---|---|---|
direction |
row · column | row |
Which way the items are laid out. The reverse directions are left out on purpose: they move the visual order away from the DOM order. |
justify |
start · center · end · space-between · space-around · space-evenly | Along the direction (justify-content). | |
align |
start · center · end · stretch | Across the direction (align-items). | |
gap |
0 · 4 · 8 · 12 · 16 · 24 · 32 · 48 | 0 |
One value for both gaps, or two — "8 24" is a row gap of 8 and a column gap of 24, as in CSS. For anything else set --i80-layout-flex-row-gap and --i80-layout-flex-column-gap in a style attribute. |
wrap |
boolean | Lets the items run onto more rows when they don't fit. | |
inline |
boolean | inline-flex: the container sits in a line of text. | |
tag |
element name | Renders that element around the children and puts the layout on it, for when the box has to be a list, a <nav> or a <span>. Its items are then the elements you write yourself. | |
i80-flex-item basis |
CSS length · 0 | flex-basis: the size to start from. | |
i80-flex-item grow |
boolean · number | Whether the item takes the free space (flex-grow). A number above 1 is a share of it. | |
i80-flex-item shrink |
boolean · number | Whether the item gives up space when there isn't enough (flex-shrink). shrink="0" keeps a button at its size. | |
i80-flex-item collapse |
boolean | Lets the item shrink below its content's own width (min-width: 0) — for text that should ellipsize. | |
i80-flex-item tag |
element name | As above, for the item. |
The tag keeps any native element you write inside it, so a server-rendered form, link or button works as it is. See Web Components for loading i80.js and the reference for these examples running.
0.1.0
Første i80-version (fork af upstream, se core/UPSTREAM.md).