Skip to content

Card

Card
Thymeleaf source
<sl:card>
<sl:card-header>
<sl:card-title level="3">Order #1042</sl:card-title>
<sl:card-description>Placed on 12 September</sl:card-description>
<sl:card-action><sl:badge variant="secondary">Shipped</sl:badge></sl:card-action>
</sl:card-header>
<sl:card-content>
<p>Three items, delivered to the Antwerp warehouse.</p>
</sl:card-content>
<sl:card-footer>
<sl:button as="a" href="/orders/1042" variant="outline">View order</sl:button>
</sl:card-footer>
</sl:card>
Rendered HTML
<div class="card">
<div class="card-header">
<div class="card-title" role="heading" aria-level="3">Order #1042</div>
<div class="card-description">Placed on 12 September</div>
<div class="card-action">
<span class="badge" data-variant="secondary">Shipped</span>
</div>
</div>
<div class="card-content">
<p>Three items, delivered to the Antwerp warehouse.</p>
</div>
<div class="card-footer">
<a class="button" data-variant="outline" href="/orders/1042">View order</a>
</div>
</div>

A card is <sl:card> plus the parts you need, as in shadcn/ui: <sl:card-header> (holding <sl:card-title>, <sl:card-description> and <sl:card-action>), <sl:card-content> and <sl:card-footer>. Use any of them, in any order. Each part is its own component, so it has its own attributes and IDE completion, and passes every other attribute through.

A card takes the width of its container; set it from your layout.

Content only
Thymeleaf source
<sl:card>
<sl:card-content><p>A card needs none of its parts but one.</p></sl:card-content>
</sl:card>
Rendered HTML
<div class="card">
<div class="card-content">
<p>A card needs none of its parts but one.</p>
</div>
</div>

<sl:card-title> is plain text unless you give it a level. With one, it is announced as a heading of that level, so screen reader users can jump between cards. Pick the level that fits the page: a card under an <h2> gets level="3".

size="sm" tightens the spacing inside the card and between its parts.

Small
Thymeleaf source
<sl:card size="sm">
<sl:card-header>
<sl:card-title level="3">Late orders</sl:card-title>
<sl:card-description>Past their delivery date</sl:card-description>
</sl:card-header>
<sl:card-content><p>3</p></sl:card-content>
</sl:card>
Rendered HTML
<div class="card" data-size="sm">
<div class="card-header">
<div class="card-title" role="heading" aria-level="3">Late orders</div>
<div class="card-description">Past their delivery date</div>
</div>
<div class="card-content">
<p>3</p>
</div>
</div>

border on the header or the footer draws a line between it and the content, with matching spacing. It works without utility classes, so also with the standalone stylesheet.

Dividing lines
Thymeleaf source
<sl:card>
<sl:card-header border>
<sl:card-title level="3">Notifications</sl:card-title>
<sl:card-description>What we email you about</sl:card-description>
</sl:card-header>
<sl:card-content><p>Order updates and weekly summaries.</p></sl:card-content>
<sl:card-footer border>
<sl:button>Save</sl:button>
<sl:button variant="ghost">Cancel</sl:button>
</sl:card-footer>
</sl:card>
Rendered HTML
<div class="card">
<div class="card-header" data-border="bottom">
<div class="card-title" role="heading" aria-level="3">Notifications</div>
<div class="card-description">What we email you about</div>
</div>
<div class="card-content">
<p>Order updates and weekly summaries.</p>
</div>
<div class="card-footer" data-border="top">
<button class="button" type="button">Save</button>
<button class="button" type="button" data-variant="ghost">Cancel</button>
</div>
</div>
AttributeValuesDefaultDescription
sizedefault smdefaultSpacing inside the card and between its parts.

Every prop can also be set from an expression: th:size="${...}". Any other attribute passes through to the rendered element, and class is appended.

AttributeValuesDefaultDescription
borderboolean—A dividing line below the header, with matching spacing.

Every prop can also be set from an expression: th:border="${...}". Any other attribute passes through to the rendered element, and class is appended.

AttributeValuesDefaultDescription
level1 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:card-description declares no props; every attribute passes through.

sl:card-action declares no props; every attribute passes through.

sl:card-content declares no props; every attribute passes through.

AttributeValuesDefaultDescription
borderboolean—A dividing line above the footer, with matching spacing.

Every prop can also be set from an expression: th:border="${...}". Any other attribute passes through to the rendered element, and class is appended.