Empty
Thymeleaf source
<sl:empty> <sl:empty-header> <sl:empty-media variant="icon"><sl:icon name="folder-code"/></sl:empty-media> <sl:empty-title level="3">No projects yet</sl:empty-title> <sl:empty-description>You haven't created any projects yet. Get started by creating your first project.</sl:empty-description> </sl:empty-header> <sl:empty-content> <sl:button as="a" href="/projects/new">Create project</sl:button> </sl:empty-content> <sl:button as="a" href="/docs/projects" variant="link" size="sm"> Learn more <sl:slot name="icon-end"><sl:icon name="arrow-up-right"/></sl:slot> </sl:button></sl:empty>Rendered HTML
<div class="empty"> <div class="empty-header"> <div class="empty-media" data-variant="icon"> <svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"> <path d="M10 10.5 8 13l2 2.5"></path> <path d="m14 10.5 2 2.5-2 2.5"></path> <path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2z"></path> </svg> </div> <div class="empty-title" role="heading" aria-level="3">No projects yet</div> <div class="empty-description">You haven't created any projects yet. Get started by creating your first project.</div> </div> <div class="empty-content"> <a class="button" href="/projects/new">Create project</a> </div> <a class="button" data-variant="link" data-size="sm" href="/docs/projects"> Learn more <span class="button-icon" data-icon="inline-end"> <svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"> <path d="M7 7h10v10"></path> <path d="M7 17 17 7"></path> </svg> </span> </a></div>Built from parts
Section titled “Built from parts”An empty state is <sl:empty> plus the parts you need: <sl:empty-header> (holding
<sl:empty-media>, <sl:empty-title> and <sl:empty-description>) and <sl:empty-content> for what to do next, such
as a button, a search field or a link. Everything is centred, and the empty state grows to fill a flex container, so
it sits in the middle of the space it is given.
<sl:empty-media variant="icon"> puts an icon on a muted square. The default media shows its content as it is, for an
avatar or an image.
Thymeleaf source
<sl:empty> <sl:empty-header> <sl:empty-media> <sl:avatar size="lg" label="Ada Lovelace"> <sl:avatar-image th:src="@{/users/{id}/photo(id=${user.id})}"/> <sl:avatar-fallback>AL</sl:avatar-fallback> </sl:avatar> </sl:empty-media> <sl:empty-title level="3">User offline</sl:empty-title> <sl:empty-description>This user is currently offline. You can leave a message to notify them or try again later.</sl:empty-description> </sl:empty-header> <sl:empty-content> <sl:button size="sm">Leave message</sl:button> </sl:empty-content></sl:empty>Rendered HTML
<div class="empty"> <div class="empty-header"> <div class="empty-media"> <span class="avatar" data-size="lg" role="img" aria-label="Ada Lovelace"> <img class="avatar-image" alt="" src="data:image/jpeg;base64,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"> <span class="avatar-fallback">AL</span> </span> </div> <div class="empty-title" role="heading" aria-level="3">User offline</div> <div class="empty-description">This user is currently offline. You can leave a message to notify them or try again later.</div> </div> <div class="empty-content"> <button class="button" type="button" data-size="sm">Leave message</button> </div></div>A title that is a heading
Section titled “A title that is a heading”<sl:empty-title> is plain text unless you give it a level. With one, it is announced as a heading of that level.
Pick the level that fits the page: an empty state under an <h2> gets level="3".
Outline
Section titled “Outline”border draws a dashed line around the empty state, to mark out the area. It works without utility classes, so also
with the standalone stylesheet.
Thymeleaf source
<sl:empty border> <sl:empty-header> <sl:empty-media variant="icon"><sl:icon name="cloud"/></sl:empty-media> <sl:empty-title level="3">Cloud storage is empty</sl:empty-title> <sl:empty-description>Upload files to your cloud storage to access them anywhere.</sl:empty-description> </sl:empty-header> <sl:empty-content> <sl:button variant="outline" size="sm">Upload files</sl:button> </sl:empty-content></sl:empty>Rendered HTML
<div class="empty" data-border="dashed"> <div class="empty-header"> <div class="empty-media" data-variant="icon"> <svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"> <path d="M17.5 19H9a7 7 0 1 1 6.71-9h1.79a4.5 4.5 0 1 1 0 9Z"></path> </svg> </div> <div class="empty-title" role="heading" aria-level="3">Cloud storage is empty</div> <div class="empty-description">Upload files to your cloud storage to access them anywhere.</div> </div> <div class="empty-content"> <button class="button" type="button" data-variant="outline" data-size="sm">Upload files</button> </div></div>In a card
Section titled “In a card”Thymeleaf source
<sl:card> <sl:card-header> <sl:card-title level="3">Projects</sl:card-title> <sl:card-description>Everything your team is working on</sl:card-description> </sl:card-header> <sl:card-content> <sl:empty> <sl:empty-header> <sl:empty-media variant="icon"><sl:icon name="folder"/></sl:empty-media> <sl:empty-title level="4">No projects yet</sl:empty-title> <sl:empty-description>Create a project to start working with your team.</sl:empty-description> </sl:empty-header> <sl:empty-content> <sl:button as="a" href="/projects/new">Create project</sl:button> </sl:empty-content> </sl:empty> </sl:card-content></sl:card>Rendered HTML
<div class="card"> <div class="card-header"> <div class="card-title" role="heading" aria-level="3">Projects</div> <div class="card-description">Everything your team is working on</div> </div> <div class="card-content"> <div class="empty"> <div class="empty-header"> <div class="empty-media" data-variant="icon"> <svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"> <path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"></path> </svg> </div> <div class="empty-title" role="heading" aria-level="4">No projects yet</div> <div class="empty-description">Create a project to start working with your team.</div> </div> <div class="empty-content"> <a class="button" href="/projects/new">Create project</a> </div> </div> </div></div>In an empty table
Section titled “In an empty table”Put the empty state in <sl:table-empty>, which spans every column: it replaces the row’s
“No results.” text.
Thymeleaf source
<sl:table> <thead> <tr> <th>Name</th> <th>Email</th> <th>Role</th> </tr> </thead> <tbody> <tr th:each="person : ${people}"> <td th:text="${person.name}">Ada Lovelace</td> <td th:text="${person.email}">ada@example.com</td> <td th:text="${person.role}">Owner</td> </tr> <sl:table-empty colspan="3" th:if="${people.empty}"> <sl:empty> <sl:empty-header> <sl:empty-media variant="icon"><sl:icon name="users"/></sl:empty-media> <sl:empty-title level="3">No people yet</sl:empty-title> <sl:empty-description>Invite your team to collaborate on this project.</sl:empty-description> </sl:empty-header> <sl:empty-content> <sl:button as="a" href="/people/invite" size="sm"> <sl:slot name="icon-start"><sl:icon name="plus"/></sl:slot> Invite people </sl:button> </sl:empty-content> </sl:empty> </sl:table-empty> </tbody></sl:table>Rendered HTML
<div class="table-container"> <table class="table"> <thead> <tr> <th>Name</th> <th>Email</th> <th>Role</th> </tr> </thead> <tbody> <tr class="table-empty"> <td colspan="3"> <div class="empty"> <div class="empty-header"> <div class="empty-media" data-variant="icon"> <svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"> <path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"></path> <path d="M16 3.128a4 4 0 0 1 0 7.744"></path> <path d="M22 21v-2a4 4 0 0 0-3-3.87"></path> <circle cx="9" cy="7" r="4"></circle> </svg> </div> <div class="empty-title" role="heading" aria-level="3">No people yet</div> <div class="empty-description">Invite your team to collaborate on this project.</div> </div> <div class="empty-content"> <a class="button" data-size="sm" href="/people/invite"> <span class="button-icon" data-icon="inline-start"> <svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"> <path d="M5 12h14"></path> <path d="M12 5v14"></path> </svg> </span> Invite people </a> </div> </div> </td> </tr> </tbody> </table></div>With a search field
Section titled “With a search field”A link in a description is underlined. An <sl:empty-description> can also go in the content, below the field.
Thymeleaf source
<sl:empty> <sl:empty-header> <sl:empty-title level="3">404 - Not found</sl:empty-title> <sl:empty-description>The page you're looking for doesn't exist. Try searching for what you need below.</sl:empty-description> </sl:empty-header> <sl:empty-content> <form action="/search" role="search" aria-label="Pages"> <sl:input-group> <sl:input-group-input type="search" name="pageQuery" placeholder="Try searching for pages..." aria-label="Search pages"/> <sl:input-group-addon><sl:icon name="search"/></sl:input-group-addon> </sl:input-group> </form> <sl:empty-description>Need help? <a href="/support">Contact support</a></sl:empty-description> </sl:empty-content></sl:empty>Rendered HTML
<div class="empty"> <div class="empty-header"> <div class="empty-title" role="heading" aria-level="3">404 - Not found</div> <div class="empty-description">The page you're looking for doesn't exist. Try searching for what you need below.</div> </div> <div class="empty-content"> <form action="/search" role="search" aria-label="Pages"> <div class="input-group" role="group"> <input class="input-group-input" type="search" name="pageQuery" placeholder="Try searching for pages..." aria-label="Search pages"> <div class="input-group-addon" role="group"> <svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"> <path d="m21 21-4.34-4.34"></path> <circle cx="11" cy="11" r="8"></circle> </svg> </div> </div> </form> <div class="empty-description"> Need help? <a href="/support">Contact support</a> </div> </div></div>While it loads
Section titled “While it loads”An <sl:spinner> takes the icon’s place in the media while something loads.
Thymeleaf source
<sl:empty> <sl:empty-header> <sl:empty-media variant="icon"><sl:spinner/></sl:empty-media> <sl:empty-title level="3">Processing your request</sl:empty-title> <sl:empty-description>Please wait while we process your request. Do not refresh the page.</sl:empty-description> </sl:empty-header> <sl:empty-content> <sl:button variant="outline" size="sm">Cancel</sl:button> </sl:empty-content></sl:empty>Rendered HTML
<div class="empty"> <div class="empty-header"> <div class="empty-media" data-variant="icon"> <svg xmlns="http://www.w3.org/2000/svg" class="icon spinner" viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" role="status" aria-label="Loading"> <path d="M21 12a9 9 0 1 1-6.219-8.56"></path> </svg> </div> <div class="empty-title" role="heading" aria-level="3">Processing your request</div> <div class="empty-description">Please wait while we process your request. Do not refresh the page.</div> </div> <div class="empty-content"> <button class="button" type="button" data-variant="outline" data-size="sm">Cancel</button> </div></div>Attributes
Section titled “Attributes”sl:empty
Section titled “sl:empty”| Attribute | Values | Default | Description |
|---|---|---|---|
border | boolean | — | A dashed line around the empty state, to mark out the area. |
Every prop can also be set from an expression: th:border="${...}". Any other attribute passes through to the rendered element, and class is appended.
sl:empty-header
Section titled “sl:empty-header”sl:empty-header declares no props; every attribute passes through.
sl:empty-media
Section titled “sl:empty-media”| Attribute | Values | Default | Description |
|---|---|---|---|
variant | default icon | default | icon puts an icon on a muted square; default shows the content as it is, e.g. an avatar. |
Every prop can also be set from an expression: th:variant="${...}". Any other attribute passes through to the rendered element, and class is appended.
sl:empty-title
Section titled “sl:empty-title”| Attribute | Values | Default | Description |
|---|---|---|---|
level | 1 2 3 4 5 6 | — | Heading level. With it the title is a heading (role="heading" and aria-level) that screen reader users can jump to; without it, plain text. Pick the level that fits the page's outline. |
Every prop can also be set from an expression: th:level="${...}". Any other attribute passes through to the rendered element, and class is appended.
sl:empty-description
Section titled “sl:empty-description”sl:empty-description declares no props; every attribute passes through.
sl:empty-content
Section titled “sl:empty-content”sl:empty-content declares no props; every attribute passes through.