Card
A container with a header, content and footer.
Plain HTML
Login to your account
Enter your email below to login.
Small card
Tighter spacing, and square corners set from outside.
Content goes here.
<script lang="ts">
import { Button } from '#lib/components/ui/button/index.js';
import * as Card from '#lib/components/ui/card/index.js';
import { Input } from '#lib/components/ui/input/index.js';
import { Label } from '#lib/components/ui/label/index.js';
</script>
<div class="cards">
<Card.Root>
<Card.Header>
<Card.Title>Login to your account</Card.Title>
<Card.Description>Enter your email below to login.</Card.Description>
<Card.Action><Button variant="link">Sign up</Button></Card.Action>
</Card.Header>
<Card.Content>
<div class="field">
<Label for="card-email">Email</Label>
<Input id="card-email" type="email" placeholder="you@example.com" />
</div>
</Card.Content>
<Card.Footer>
<Button>Login</Button>
<Button variant="outline">Cancel</Button>
</Card.Footer>
</Card.Root>
<Card.Root size="sm" style="--dui-card-radius: 0">
<Card.Header>
<Card.Title>Small card</Card.Title>
<Card.Description>Tighter spacing, and square corners set from outside.</Card.Description>
</Card.Header>
<Card.Content>Content goes here.</Card.Content>
</Card.Root>
</div>
<style>
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
align-items: start;
gap: var(--dui-space-4);
width: min(100%, 34rem);
}
.field {
display: grid;
gap: var(--dui-space-2);
}
</style>
Installation
npx distill-ui add card- Set up the tokens once, if you haven't: see manual installation.
- Copy these files into your project:
<script lang="ts">
import type { HTMLAttributes } from 'svelte/elements';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: HTMLAttributes<HTMLDivElement> & { ref?: HTMLDivElement | null } = $props();
</script>
<div {...restProps} bind:this={ref} class={['action', className]}>
{@render children?.()}
</div>
<style>
.action {
grid-column: 2;
grid-row: 1 / span 2;
align-self: start;
justify-self: end;
margin-inline-start: var(--dui-space-4);
}
</style>
<script lang="ts">
import type { HTMLAttributes } from 'svelte/elements';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: HTMLAttributes<HTMLDivElement> & { ref?: HTMLDivElement | null } = $props();
</script>
<div {...restProps} bind:this={ref} class={['content', className]}>
{@render children?.()}
</div>
<style>
.content {
padding-inline: var(--card-spacing);
}
</style>
<script lang="ts">
import type { HTMLAttributes } from 'svelte/elements';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: HTMLAttributes<HTMLParagraphElement> & { ref?: HTMLParagraphElement | null } = $props();
</script>
<p {...restProps} bind:this={ref} class={['description', className]}>
{@render children?.()}
</p>
<style>
.description {
grid-column: 1;
margin: 0;
color: var(--dui-color-muted-foreground);
font-size: var(--dui-text-sm);
}
</style>
<script lang="ts">
import type { HTMLAttributes } from 'svelte/elements';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: HTMLAttributes<HTMLDivElement> & { ref?: HTMLDivElement | null } = $props();
</script>
<div {...restProps} bind:this={ref} class={['footer', className]}>
{@render children?.()}
</div>
<style>
.footer {
display: flex;
align-items: center;
gap: var(--dui-space-2);
padding-inline: var(--card-spacing);
}
</style>
<script lang="ts">
import type { HTMLAttributes } from 'svelte/elements';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: HTMLAttributes<HTMLDivElement> & { ref?: HTMLDivElement | null } = $props();
</script>
<div {...restProps} bind:this={ref} class={['header', className]}>
{@render children?.()}
</div>
<style>
.header {
/* Two columns so Card.Action can sit top-right beside the title and description. */
display: grid;
grid-template-columns: 1fr auto;
align-items: start;
row-gap: var(--dui-space-1);
padding-inline: var(--card-spacing);
}
</style>
<script lang="ts">
import type { HTMLAttributes } from 'svelte/elements';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: HTMLAttributes<HTMLDivElement> & { ref?: HTMLDivElement | null } = $props();
</script>
<div {...restProps} bind:this={ref} class={['title', className]}>
{@render children?.()}
</div>
<style>
.title {
grid-column: 1;
font-size: var(--card-title-size);
line-height: 1.5;
font-weight: var(--dui-font-weight-medium);
}
</style>
<script lang="ts">
import type { HTMLAttributes } from 'svelte/elements';
let {
ref = $bindable(null),
size = 'md',
class: className,
children,
...restProps
}: HTMLAttributes<HTMLDivElement> & {
ref?: HTMLDivElement | null;
size?: 'sm' | 'md';
} = $props();
</script>
<div {...restProps} bind:this={ref} class={['card', className]} data-size={size}>
{@render children?.()}
</div>
<!--
Options you can set from a parent or on the card itself:
--dui-card-spacing, --dui-card-radius, --dui-card-bg, --dui-card-fg, --dui-card-border
-->
<style>
.card {
/* Inherited by the card's parts (header, content, footer) for their padding. */
--card-spacing: var(--dui-card-spacing, var(--dui-space-6));
--card-title-size: var(--dui-text-base);
display: flex;
flex-direction: column;
gap: var(--card-spacing);
padding-block: var(--card-spacing);
overflow: hidden;
border-radius: var(--dui-card-radius, var(--dui-radius-xl));
background: var(--dui-card-bg, var(--dui-color-card));
color: var(--dui-card-fg, var(--dui-color-card-foreground));
font-size: var(--dui-text-sm);
box-shadow:
0 0 0 1px
var(--dui-card-border, color-mix(in oklch, var(--dui-color-foreground) 10%, transparent)),
var(--dui-shadow-xs);
}
.card[data-size='sm'] {
--card-spacing: var(--dui-card-spacing, var(--dui-space-4));
--card-title-size: var(--dui-text-sm);
}
</style>
import Action from './card-action.svelte';
import Content from './card-content.svelte';
import Description from './card-description.svelte';
import Footer from './card-footer.svelte';
import Header from './card-header.svelte';
import Title from './card-title.svelte';
import Root from './card.svelte';
export { Root, Header, Title, Description, Action, Content, Footer };
Customizing
Set any of these on the component or on a parent element. See Customizing for how they work.
| Custom property | Default |
|---|---|
--dui-card-spacing | var(--dui-space-6) |
--dui-card-radius | var(--dui-radius-xl) |
--dui-card-bg | var(--dui-color-card) |
--dui-card-fg | var(--dui-color-card-foreground) |
--dui-card-border | color-mix(in oklch, var(--dui-color-foreground) 10%, transparent) |