Svelte 5 component · fractalsvelte-ui · Blocks
PricingCard
A software subscription tier card displaying price intervals, highlighted popular badges, and comprehensive feature checklists. Renders call-to-action buttons configured for immediate checkout or plan selection.
Install
PricingCard is a Svelte 5 pricing card component in @fractaldesign/fractalsvelte-ui. It works in SvelteKit and in any Svelte 5 app.
pnpm add @fractaldesign/fractalsvelte-uiImport
import { PricingCard } from '@fractaldesign/fractalsvelte-ui';Usage
<PricingCard interval={'/month'}>Click me</PricingCard>Props
Also accepts every attribute of HTMLAttributes, spread onto
the root element.
| Name | Type | Default |
|---|---|---|
tier | string | — |
price | string | number | — |
interval | string | '/month' |
description | string | — |
features | string[] | [] |
ctaText | string | 'Get Started' |
ctaHref | string | — |
popular | boolean | false |
popularLabel | string | 'Most Popular' |
variant | Variant 'primary' | 'secondary' | 'soft' | 'outline' | 'ghost' | 'destructive' | 'surface' | 'surface' |
disabled | boolean | false |
class | string | '' |
Snippets
| Name | Type | Default |
|---|---|---|
header | Snippet | — |
cta | Snippet | — |
children | Snippet | — |
Source
Show the PricingCard.svelte source
<script lang="ts">
import type { Snippet } from 'svelte';
import type { HTMLAttributes } from 'svelte/elements';
import type { Variant } from '../../types';
interface Props extends HTMLAttributes<HTMLDivElement> {
tier: string;
price: string | number;
interval?: string;
description?: string;
features: string[];
ctaText?: string;
ctaHref?: string;
popular?: boolean;
popularLabel?: string;
variant?: Variant;
disabled?: boolean;
class?: string;
header?: Snippet;
cta?: Snippet;
children?: Snippet;
}
let {
tier,
price,
interval = '/month',
description,
features = [],
ctaText = 'Get Started',
ctaHref,
popular = false,
popularLabel = 'Most Popular',
variant = 'surface',
disabled = false,
class: className = '',
header,
cta,
children,
...restProps
}: Props = $props();
</script>
<div
class="f-pricing-card {className}"
data-variant={variant}
data-popular={popular ? 'true' : undefined}
data-disabled={disabled ? 'true' : undefined}
{...restProps}
>
{#if popular}
<div class="f-popular-badge">
{popularLabel}
</div>
{/if}
{#if header}
<div class="f-pricing-header-slot">
{@render header()}
</div>
{:else}
<div class="f-pricing-header">
<h3 class="f-tier-name">{tier}</h3>
{#if description}
<p class="f-tier-desc">{description}</p>
{/if}
<div class="f-price-row">
<span class="f-price-val">{price}</span>
{#if interval}
<span class="f-price-interval">{interval}</span>
{/if}
</div>
</div>
{/if}
<div class="f-pricing-features">
<ul class="f-features-list">
{#each features as feature}
<li class="f-feature-item">
<svg class="f-check-icon" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path
fill-rule="evenodd"
d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
clip-rule="evenodd"
/>
</svg>
<span class="f-feature-text">{feature}</span>
</li>
{/each}
</ul>
</div>
{#if children}
<div class="f-pricing-extra">
{@render children()}
</div>
{/if}
<div class="f-pricing-cta">
{#if cta}
{@render cta()}
{:else if ctaHref}
<a
href={ctaHref}
class="f-cta-btn"
data-variant={popular ? 'primary' : 'outline'}
>
{ctaText}
</a>
{:else}
<button
type="button"
class="f-cta-btn"
data-variant={popular ? 'primary' : 'outline'}
{disabled}
>
{ctaText}
</button>
{/if}
</div>
</div>
<style>
@layer fractalsvelte-ui.components {
.f-pricing-card {
position: relative;
display: flex;
flex-direction: column;
padding: var(--f-space-2xl) var(--f-space-xl);
border-radius: var(--f-radius-xl);
background: var(--f-surface);
border: 1px solid var(--f-border);
box-shadow: var(--f-shadow-sm);
font-family: var(--f-font-sans);
transition:
transform var(--f-duration-normal) var(--f-ease-spring),
box-shadow var(--f-duration-normal) var(--f-ease),
border-color var(--f-duration-normal) var(--f-ease);
}
.f-pricing-card:hover {
transform: translateY(-4px);
box-shadow: var(--f-shadow-xl);
border-color: var(--f-border-strong);
}
.f-pricing-card[data-popular='true'] {
border-color: var(--f-primary);
box-shadow: var(--f-shadow-lg), 0 0 20px var(--f-primary-subtle);
}
.f-pricing-card[data-disabled='true'] {
opacity: 0.6;
pointer-events: none;
}
.f-popular-badge {
position: absolute;
top: -0.75rem;
left: 50%;
transform: translateX(-50%);
background: var(--f-primary);
color: var(--f-primary-text);
padding: 0.25rem 0.85rem;
border-radius: var(--f-radius-full);
font-size: var(--f-text-xs);
font-weight: 600;
letter-spacing: 0.03em;
box-shadow: var(--f-shadow-sm);
}
.f-pricing-header {
margin-bottom: var(--f-space-xl);
}
.f-tier-name {
margin: 0 0 var(--f-space-xs) 0;
font-size: var(--f-text-lg);
font-weight: 700;
color: var(--f-text);
}
.f-tier-desc {
margin: 0 0 var(--f-space-md) 0;
font-size: var(--f-text-sm);
color: var(--f-text-muted);
line-height: 1.4;
}
.f-price-row {
display: flex;
align-items: baseline;
gap: 0.25rem;
}
.f-price-val {
font-size: var(--f-text-3xl);
font-weight: 800;
letter-spacing: -0.02em;
color: var(--f-text);
}
.f-price-interval {
font-size: var(--f-text-sm);
color: var(--f-text-muted);
}
.f-pricing-features {
flex: 1 1 auto;
margin-bottom: var(--f-space-2xl);
}
.f-features-list {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
gap: var(--f-space-sm);
}
.f-feature-item {
display: flex;
align-items: center;
gap: var(--f-space-sm);
font-size: var(--f-text-sm);
color: var(--f-text);
}
.f-check-icon {
width: 1.125rem;
height: 1.125rem;
color: var(--f-primary);
flex-shrink: 0;
}
.f-pricing-extra {
margin-bottom: var(--f-space-lg);
}
.f-pricing-cta {
margin-top: auto;
}
.f-cta-btn {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
padding: var(--f-space-sm) var(--f-space-lg);
border-radius: var(--f-radius-md);
font-family: inherit;
font-size: var(--f-text-sm);
font-weight: 600;
text-decoration: none;
cursor: pointer;
border: 1px solid transparent;
box-sizing: border-box;
transition: all var(--f-duration-fast) var(--f-ease);
}
.f-cta-btn[data-variant='primary'] {
background: var(--f-primary);
color: var(--f-primary-text);
}
.f-cta-btn[data-variant='primary']:hover {
background: var(--f-primary-hover);
box-shadow: var(--f-shadow-md), var(--f-shadow-glow);
}
.f-cta-btn[data-variant='outline'] {
background: transparent;
color: var(--f-text);
border-color: var(--f-border-strong);
}
.f-cta-btn[data-variant='outline']:hover {
background: var(--f-hover);
border-color: var(--f-primary);
color: var(--f-primary);
}
}
</style>Related components
TerminalBlock An authentic command-line shell container featuring macOS window buttons, active typing cursors, and…Timeline A vertical chronology stream linking sequence events along a continuous spine track.WorkflowNode A graph node card designed for visual pipeline editors and flowchart interfaces.BeforeAfter An interactive visual comparison slider that reveals before and after views through a draggable central…ChatBubble A conversation message bubble with dedicated user, assistant, and system roles.FeatureGrid A responsive grid showcase organizing product highlights into structured cards with icon containers, titles,…