Button
Thymeleaf source
<sl:button>Save</sl:button>Rendered HTML
<button class="button" type="button">Save</button>A button never submits by accident
Section titled “A button never submits by accident”<sl:button> renders type="button", not HTML’s default submit. A button inside a form therefore does nothing
unless you say so: write type="submit" on the one that should submit.
Thymeleaf source
<form method="post" th:action="@{/orders}"> <sl:button type="submit">Save</sl:button> <sl:button variant="outline">Preview</sl:button></form>Rendered HTML
<form method="post" action="/orders"> <button class="button" type="submit">Save</button> <button class="button" type="button" data-variant="outline">Preview</button></form>Variants
Section titled “Variants”Thymeleaf source
<sl:button>Default</sl:button><sl:button variant="outline">Outline</sl:button><sl:button variant="ghost">Ghost</sl:button><sl:button variant="destructive">Destructive</sl:button><sl:button variant="secondary">Secondary</sl:button><sl:button variant="link">Link</sl:button>Rendered HTML
<button class="button" type="button">Default</button><button class="button" type="button" data-variant="outline">Outline</button><button class="button" type="button" data-variant="ghost">Ghost</button><button class="button" type="button" data-variant="destructive">Destructive</button><button class="button" type="button" data-variant="secondary">Secondary</button><button class="button" type="button" data-variant="link">Link</button>Thymeleaf source
<sl:button size="xs">Extra small</sl:button><sl:button size="sm">Small</sl:button><sl:button>Default</sl:button><sl:button size="lg">Large</sl:button>Rendered HTML
<button class="button" type="button" data-size="xs">Extra small</button><button class="button" type="button" data-size="sm">Small</button><button class="button" type="button">Default</button><button class="button" type="button" data-size="lg">Large</button>The icon sizes are square, for a button holding only an icon. Such a button has no text for a screen reader, so it
must have an aria-label (or aria-labelledby); rendering fails without one.
Thymeleaf source
<sl:button size="icon-xs" variant="outline" aria-label="Add"><sl:icon name="plus"/></sl:button><sl:button size="icon-sm" variant="outline" aria-label="Add"><sl:icon name="plus"/></sl:button><sl:button size="icon" variant="outline" aria-label="Add"><sl:icon name="plus"/></sl:button><sl:button size="icon-lg" variant="outline" aria-label="Add"><sl:icon name="plus"/></sl:button>Rendered HTML
<button class="button" type="button" data-variant="outline" data-size="icon-xs" aria-label="Add"> <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></button><button class="button" type="button" data-variant="outline" data-size="icon-sm" aria-label="Add"> <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></button><button class="button" type="button" data-variant="outline" data-size="icon" aria-label="Add"> <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></button><button class="button" type="button" data-variant="outline" data-size="icon-lg" aria-label="Add"> <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></button>With an icon
Section titled “With an icon”Put an <sl:icon> in the icon-start or icon-end slot. It takes the button’s colour and a 16 pixel size.
Thymeleaf source
<sl:button variant="destructive"> <sl:slot name="icon-start"><sl:icon name="trash"/></sl:slot> Delete order</sl:button><sl:button variant="outline"> Next <sl:slot name="icon-end"><sl:icon name="arrow-right"/></sl:slot></sl:button>Rendered HTML
<button class="button" type="button" data-variant="destructive"> <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="M10 11v6"></path> <path d="M14 11v6"></path> <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"></path> <path d="M3 6h18"></path> <path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path> </svg> </span> Delete order</button><button class="button" type="button" data-variant="outline"> Next <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="M5 12h14"></path> <path d="m12 5 7 7-7 7"></path> </svg> </span></button>As a link
Section titled “As a link”as="a" renders an anchor that looks the same, for navigation.
Thymeleaf source
<sl:button as="a" href="/orders" variant="outline">View orders</sl:button><sl:button as="a" href="/orders" variant="link">View orders</sl:button>Rendered HTML
<a class="button" data-variant="outline" href="/orders">View orders</a><a class="button" data-variant="link" href="/orders">View orders</a>Disabled and loading
Section titled “Disabled and loading”A disabled button gets the native disabled attribute. A disabled link loses its href, leaves the tab order and is
marked aria-disabled, so it cannot be followed. loading shows a spinner in place of the start icon, announces the
busy state with aria-busy and a visually hidden “Loading” label, and disables the button.
Thymeleaf source
<sl:button disabled>Save</sl:button><sl:button as="a" href="/orders" variant="outline" disabled>View orders</sl:button>Rendered HTML
<button class="button" type="button" disabled>Save</button><a class="button" data-variant="outline" aria-disabled="true" role="link" tabindex="-1">View orders</a>Thymeleaf source
<sl:button loading>Saving</sl:button><sl:button variant="outline" loading> <sl:slot name="icon-start"><sl:icon name="save"/></sl:slot> Save</sl:button><sl:button size="icon" variant="outline" aria-label="Refresh" loading><sl:icon name="refresh-cw"/></sl:button>Rendered HTML
<button class="button" type="button" aria-busy="true" disabled> <span class="button-icon" data-icon="inline-start"> <svg xmlns="http://www.w3.org/2000/svg" class="icon button-spinner" 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 12a9 9 0 1 1-6.219-8.56"></path> </svg> </span> <span class="sl-sr-only">Loading</span> Saving</button><button class="button" type="button" data-variant="outline" aria-busy="true" disabled> <span class="button-icon" data-icon="inline-start"> <svg xmlns="http://www.w3.org/2000/svg" class="icon button-spinner" 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 12a9 9 0 1 1-6.219-8.56"></path> </svg> </span> <span class="sl-sr-only">Loading</span> Save</button><button class="button" type="button" data-variant="outline" data-size="icon" aria-busy="true" aria-label="Refresh" disabled> <span class="button-icon" data-icon="inline-start"> <svg xmlns="http://www.w3.org/2000/svg" class="icon button-spinner" 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 12a9 9 0 1 1-6.219-8.56"></path> </svg> </span> <span class="sl-sr-only">Loading</span></button>The “Loading” label comes from the message sl.button.loading; define it in your own messages.properties to
translate or reword it.
Props from expressions
Section titled “Props from expressions”Every prop also has a th: form that takes a Thymeleaf expression.
Thymeleaf source
<sl:button th:variant="${order.cancellable ? 'destructive' : 'secondary'}" th:text="#{orders.cancel}">Cancel order</sl:button>Rendered HTML
<button class="button" type="button" data-variant="destructive">Cancel order</button>Everything else passes through
Section titled “Everything else passes through”Attributes that are not props, such as hx-*, x-*, aria-* and data-*, land on the rendered element unchanged,
so the button works with htmx and Alpine. class is appended to the component’s own class.
Thymeleaf source
<sl:button variant="outline" class="app-wide" hx-post="/orders/42/archive" hx-target="#orders" data-testid="archive">Archive</sl:button>Rendered HTML
<button class="button app-wide" type="button" data-variant="outline" hx-post="/orders/42/archive" hx-target="#orders" data-testid="archive">Archive</button>Attributes
Section titled “Attributes”| Attribute | Values | Default | Description |
|---|---|---|---|
variant | default outline ghost destructive secondary link | default | Visual style of the button. |
size | xs sm default lg icon icon-xs icon-sm icon-lg | default | Size of the button. The icon sizes are square, for a button that holds only an icon, and need an aria-label. |
as | button a | button | Element to render: a button, or an anchor (a) for navigation. |
type | button submit reset | button | Submit behaviour. Defaults to button, unlike HTML, so a button in a form never submits by accident. |
disabled | boolean | — | Disable the button. A disabled anchor loses its href and leaves the tab order. |
loading | boolean | — | Show a spinner, announce the busy state and disable the button. |
Every prop can also be set from an expression: th:variant="${...}". Any other attribute passes through to the rendered element, and class is appended. An aria-label or aria-labelledby is required when size is icon, icon-xs, icon-sm or icon-lg; rendering fails without one.