Svelte 5 component · fractalsvelte-ui · Layout
Card
A versatile container with structured slots for media, header, content body, and footer actions. Supports multiple surface variants, elevation shadows, corner radii, and padding tiers.
Install
Card is a Svelte 5 card component in @fractaldesign/fractalsvelte-ui. It works in SvelteKit and in any Svelte 5 app.
pnpm add @fractaldesign/fractalsvelte-uiImport
import { Card } from '@fractaldesign/fractalsvelte-ui';Usage
<Card variant={'surface'} elevation={'sm'} padding={'lg'}>Click me</Card>Props
Also accepts every attribute of HTMLAttributes, spread onto
the root element.
| Name | Type | Default |
|---|---|---|
variant | CardVariant 'surface' | 'outline' | 'sunken' | 'raised' | 'ghost' Visual surface style variant | 'surface' |
elevation | CardElevation 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' Shadow elevation depth | 'sm' |
padding | Space 'none' | '3xs' | '2xs' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' Inner content padding token | 'lg' |
radius | CardRadius 'none' | 'sm' | 'md' | 'lg' | 'xl' Corner radius scale token | 'lg' |
title | string Optional card title rendered in header | — |
description | string Optional card description subtitle | — |
class | string Additional CSS classes | '' |
Snippets
| Name | Type | Default |
|---|---|---|
media | Snippet Top media or image banner slot | — |
header | Snippet Custom full header slot | — |
headerExtra | Snippet Auxiliary element placed on the right side of header | — |
footer | Snippet Card footer action or metadata slot | — |
children | Snippet Primary content body slot | — |
Source
Show the Card.svelte source
<script lang="ts">
import type { Snippet } from 'svelte';
import type { HTMLAttributes } from 'svelte/elements';
import type { Space } from '../../types';
export type CardVariant = 'surface' | 'outline' | 'sunken' | 'raised' | 'ghost';
export type CardElevation = 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl';
export type CardRadius = 'none' | 'sm' | 'md' | 'lg' | 'xl';
interface Props extends HTMLAttributes<HTMLDivElement> {
/** Visual surface style variant */
variant?: CardVariant;
/** Shadow elevation depth */
elevation?: CardElevation;
/** Inner content padding token */
padding?: Space;
/** Corner radius scale token */
radius?: CardRadius;
/** Optional card title rendered in header */
title?: string;
/** Optional card description subtitle */
description?: string;
/** Additional CSS classes */
class?: string;
/** Top media or image banner slot */
media?: Snippet;
/** Custom full header slot */
header?: Snippet;
/** Auxiliary element placed on the right side of header */
headerExtra?: Snippet;
/** Card footer action or metadata slot */
footer?: Snippet;
/** Primary content body slot */
children?: Snippet;
}
let {
variant = 'surface',
elevation = 'sm',
padding = 'lg',
radius = 'lg',
title,
description,
class: className = '',
media,
header,
headerExtra,
footer,
children,
...restProps
}: Props = $props();
const hasHeader = $derived(Boolean(header || title || description || headerExtra));
</script>
<div
class="f-card {className}"
data-variant={variant}
data-elevation={elevation}
data-padding={padding}
data-radius={radius}
{...restProps}
>
{#if media}
<div class="f-card-media">
{@render media()}
</div>
{/if}
{#if hasHeader}
<div class="f-card-header">
{#if header}
{@render header()}
{:else}
<div class="f-card-header-content">
{#if title}
<h3 class="f-card-title">{title}</h3>
{/if}
{#if description}
<p class="f-card-desc">{description}</p>
{/if}
</div>
{#if headerExtra}
<div class="f-card-header-extra">
{@render headerExtra()}
</div>
{/if}
{/if}
</div>
{/if}
{#if children}
<div class="f-card-body">
{@render children()}
</div>
{/if}
{#if footer}
<div class="f-card-footer">
{@render footer()}
</div>
{/if}
</div>
<style>
@layer fractalsvelte-ui.components {
.f-card {
position: relative;
display: flex;
flex-direction: column;
background: var(--f-surface);
color: var(--f-text);
border: 1px solid var(--f-border);
box-sizing: border-box;
overflow: hidden;
transition: border-color var(--f-duration-fast) var(--f-ease),
box-shadow var(--f-duration-fast) var(--f-ease),
background var(--f-duration-fast) var(--f-ease);
}
/* Variants */
.f-card[data-variant='surface'] {
background: var(--f-surface);
border-color: var(--f-border);
}
.f-card[data-variant='outline'] {
background: transparent;
border-color: var(--f-border-strong);
}
.f-card[data-variant='sunken'] {
background: var(--f-sunken);
border-color: var(--f-border-subtle);
}
.f-card[data-variant='raised'] {
background: var(--f-raised);
border-color: var(--f-border);
}
.f-card[data-variant='ghost'] {
background: transparent;
border-color: transparent;
}
/* Elevations */
.f-card[data-elevation='none'] {
box-shadow: none;
}
.f-card[data-elevation='xs'] {
box-shadow: var(--f-shadow-xs);
}
.f-card[data-elevation='sm'] {
box-shadow: var(--f-shadow-sm);
}
.f-card[data-elevation='md'] {
box-shadow: var(--f-shadow-md);
}
.f-card[data-elevation='lg'] {
box-shadow: var(--f-shadow-lg);
}
.f-card[data-elevation='xl'] {
box-shadow: var(--f-shadow-xl);
}
/* Radii */
.f-card[data-radius='none'] {
border-radius: 0;
}
.f-card[data-radius='sm'] {
border-radius: var(--f-radius-sm);
}
.f-card[data-radius='md'] {
border-radius: var(--f-radius-md);
}
.f-card[data-radius='lg'] {
border-radius: var(--f-radius-lg);
}
.f-card[data-radius='xl'] {
border-radius: var(--f-radius-xl);
}
/* Padding scale applied to header, body, footer */
.f-card[data-padding='none'] .f-card-header,
.f-card[data-padding='none'] .f-card-body,
.f-card[data-padding='none'] .f-card-footer {
padding: 0;
}
.f-card[data-padding='3xs'] .f-card-header,
.f-card[data-padding='3xs'] .f-card-body,
.f-card[data-padding='3xs'] .f-card-footer {
padding: var(--f-space-3xs);
}
.f-card[data-padding='2xs'] .f-card-header,
.f-card[data-padding='2xs'] .f-card-body,
.f-card[data-padding='2xs'] .f-card-footer {
padding: var(--f-space-2xs);
}
.f-card[data-padding='xs'] .f-card-header,
.f-card[data-padding='xs'] .f-card-body,
.f-card[data-padding='xs'] .f-card-footer {
padding: var(--f-space-xs);
}
.f-card[data-padding='sm'] .f-card-header,
.f-card[data-padding='sm'] .f-card-body,
.f-card[data-padding='sm'] .f-card-footer {
padding: var(--f-space-sm);
}
.f-card[data-padding='md'] .f-card-header,
.f-card[data-padding='md'] .f-card-body,
.f-card[data-padding='md'] .f-card-footer {
padding: var(--f-space-md);
}
.f-card[data-padding='lg'] .f-card-header,
.f-card[data-padding='lg'] .f-card-body,
.f-card[data-padding='lg'] .f-card-footer {
padding: var(--f-space-lg);
}
.f-card[data-padding='xl'] .f-card-header,
.f-card[data-padding='xl'] .f-card-body,
.f-card[data-padding='xl'] .f-card-footer {
padding: var(--f-space-xl);
}
.f-card[data-padding='2xl'] .f-card-header,
.f-card[data-padding='2xl'] .f-card-body,
.f-card[data-padding='2xl'] .f-card-footer {
padding: var(--f-space-2xl);
}
/* Card sections */
.f-card-media {
width: 100%;
overflow: hidden;
line-height: 0;
}
.f-card-header {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: var(--f-space-md);
border-bottom: 1px solid var(--f-border-subtle);
}
.f-card-header-content {
display: flex;
flex-direction: column;
gap: var(--f-space-3xs);
flex: 1;
min-width: 0;
}
.f-card-title {
margin: 0;
font-size: var(--f-text-lg);
font-weight: 600;
color: var(--f-text);
line-height: 1.3;
}
.f-card-desc {
margin: 0;
font-size: var(--f-text-sm);
color: var(--f-text-muted);
line-height: 1.45;
}
.f-card-header-extra {
display: inline-flex;
align-items: center;
flex-shrink: 0;
}
.f-card-body {
flex: 1;
font-size: var(--f-text-base);
color: var(--f-text);
line-height: 1.5;
}
.f-card-footer {
display: flex;
align-items: center;
justify-content: flex-end;
gap: var(--f-space-sm);
border-top: 1px solid var(--f-border-subtle);
background: var(--f-sunken);
}
}
</style>Related components
ClickableCard An interactive card that functions as a button or anchor link with elevation hover lift and active press…Container A responsive centered container that constrains content width to standardized breakpoints or fluid dimensions.DataTable A dense, responsive tabular data presentation component supporting sortable columns, zebra striping,…Divider A separator line for partitioning adjacent content blocks horizontally or vertically.Flex A flexible flexbox layout utility providing granular control over direction, wrapping, and distribution.Grid A CSS grid layout container featuring track templates for fixed column counts, auto-fit, and auto-fill.