Skip to content
edmi-ui

Step Card

Numbered step card with a corner icon, title and description.

StackBlitz ↗StackBlitz ↗StackBlitz ↗
01
Create
Pick up to 10 assets and set weights.
02
Share
A link, OG image, feed card and Blink.
03
Join
Investors pay in USDC in one click.
01
Create
Pick up to 10 assets and set weights.
02
Share
A link, OG image, feed card and Blink.
03
Join
Investors pay in USDC in one click.
01
Create
Pick up to 10 assets and set weights.
02
Share
A link, OG image, feed card and Blink.
03
Join
Investors pay in USDC in one click.
Terminal window
npx shadcn@latest add @edmi-ui/step-card
Terminal window
pnpm dlx shadcn@latest add @edmi-ui/step-card
Terminal window
yarn dlx shadcn@latest add @edmi-ui/step-card
Terminal window
bunx shadcn@latest add @edmi-ui/step-card

Pick your package manager above; the choice is remembered. Add the @edmi-ui registry first, see Getting started.

Terminal window
npx shadcn-vue@latest add @edmi-ui/step-card
Terminal window
pnpm dlx shadcn-vue@latest add @edmi-ui/step-card
Terminal window
yarn dlx shadcn-vue@latest add @edmi-ui/step-card
Terminal window
bunx shadcn-vue@latest add @edmi-ui/step-card

Pick your package manager above; the choice is remembered. Add the @edmi-ui registry first, see Getting started.

Terminal window
npx shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/step-card.json
Terminal window
pnpm dlx shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/step-card.json
Terminal window
yarn dlx shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/step-card.json
Terminal window
bunx --bun shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/step-card.json

Pick your package manager above; the choice is remembered. Add the @edmi-ui registry first, see Getting started.

import { StepCard } from "@/components/step-card/step-card";
<div className="flex flex-wrap gap-3">
<StepCard
className="w-[200px]"
index="01"
title="Create"
description="Pick up to 10 assets and set weights."
/>
<StepCard
className="w-[200px]"
index="02"
title="Share"
description="A link, OG image, feed card and Blink."
/>
<StepCard
className="w-[200px]"
index="03"
title="Join"
description="Investors pay in USDC in one click."
/>
</div>
<script setup lang="ts">
import { StepCard } from "@/components/ui/step-card";
</script>
<template>
<div class="flex flex-wrap gap-3">
<StepCard class="w-[200px]" index="01" title="Create" description="Pick up to 10 assets and set weights." />
<StepCard class="w-[200px]" index="02" title="Share" description="A link, OG image, feed card and Blink." />
<StepCard class="w-[200px]" index="03" title="Join" description="Investors pay in USDC in one click." />
</div>
</template>
<script lang="ts">
import { StepCard } from "$lib/components/ui/step-card";
</script>
<div class="flex flex-wrap gap-3">
<StepCard class="w-[200px]" index="01" title="Create" description="Pick up to 10 assets and set weights." />
<StepCard class="w-[200px]" index="02" title="Share" description="A link, OG image, feed card and Blink." />
<StepCard class="w-[200px]" index="03" title="Join" description="Investors pay in USDC in one click." />
</div>
registry/blocks/step-card/step-card.tsx
import { cn } from "cn";
import type * as React from "react";
import { ArrowUpRightIcon } from "@phosphor-icons/react";
import { Card } from "@/registry/edmi/ui/card";
import type { Elevation } from "@/registry/edmi/ui/elevation";
type StepCardProps = Omit<React.ComponentProps<typeof Card>, "title"> & {
/** Mono step number, e.g. "01". */
index: React.ReactNode;
title: React.ReactNode;
description?: React.ReactNode;
/** Corner icon element; defaults to an arrow up-right. */
icon?: React.ReactNode;
/** ✦ depth of the card: sunken -1, flat 0, raised +1, floating +2. */
elevation?: Elevation;
};
const arrow = (
<ArrowUpRightIcon
className="size-3.5"
/>
);
function StepCard({
className,
index,
title,
description,
icon = arrow,
elevation,
...props
}: StepCardProps) {
return (
<Card
data-slot="step-card"
elevation={elevation}
className={cn("gap-0 p-[18px]", className)}
{...props}
>
<div className="flex items-center justify-between">
<span className="font-mono text-xs text-muted-foreground">{index}</span>
<span className="text-muted-foreground">{icon}</span>
</div>
<div className="mt-[18px] text-base font-semibold">{title}</div>
{description ? (
<div className="mt-1.5 text-[13px] text-muted-foreground">
{description}
</div>
) : null}
</Card>
);
}
export { StepCard };
registry/ui/step-card/StepCard.vue
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { ArrowUpRightIcon } from '@lucide/vue'
import { Card } from '@/registry/edmi/ui/card'
import type { Elevation } from '@/registry/edmi/ui/elevation'
import { cn } from '@/registry/edmi/lib/utils'
const props = defineProps<{
/** ✦ depth of the card: sunken -1, flat 0, raised +1, floating +2 */
elevation?: Elevation
class?: HTMLAttributes['class']
/** Mono step number, e.g. "01". */
index?: string | number
title?: string
description?: string
}>()
// Slots `index`, `title`, `description` (string props are the fallback); slot `icon` = corner icon (defaults to an arrow up-right).
</script>
<template>
<Card :elevation="elevation" data-slot="step-card" :class="cn('gap-0 p-[18px]', props.class)">
<div class="flex items-center justify-between">
<span class="font-mono text-xs text-muted-foreground"><slot name="index">{{ index }}</slot></span>
<span class="text-muted-foreground">
<slot name="icon"><ArrowUpRightIcon class="size-3.5" /></slot>
</span>
</div>
<div class="mt-[18px] text-base font-semibold"><slot name="title">{{ title }}</slot></div>
<div v-if="description || $slots.description" class="mt-1.5 text-[13px] text-muted-foreground"><slot name="description">{{ description }}</slot></div>
</Card>
</template>
registry/ui/step-card/index.ts
export { default as StepCard } from "./StepCard.vue";
src/lib/registry/ui/step-card/index.ts
import Root from "./step-card.svelte";
export {
Root,
//
Root as StepCard,
};
src/lib/registry/ui/step-card/step-card.svelte
<script lang="ts">
import { cn } from "$lib/utils.js";
import { Card } from "$lib/registry/ui/card/index.js";
import type { Elevation } from "$lib/registry/ui/elevation/index.js";
import ArrowUpRightIcon from "phosphor-svelte/lib/ArrowUpRight";
import type { ComponentProps, Snippet } from "svelte";
let {
ref = $bindable(null),
class: className,
index,
title,
description,
icon,
elevation = "auto",
...restProps
}: Omit<ComponentProps<typeof Card>, "title" | "children"> & {
/** Mono step number, e.g. "01". */
index: string | number | Snippet;
title: string | Snippet;
description?: string | Snippet;
/** Corner icon; defaults to an arrow up-right. */
icon?: Snippet;
/** ✦ depth of the card: sunken -1, flat 0, raised +1, floating +2. */
elevation?: Elevation;
} = $props();
</script>
<Card bind:ref {elevation} data-slot="step-card" class={cn("gap-0 p-[18px]", className)} {...restProps}>
<div class="flex items-center justify-between">
<span class="font-mono text-xs text-muted-foreground">{#if typeof index === "function"}{@render index()}{:else}{index}{/if}</span>
<span class="text-muted-foreground">
{#if icon}
{@render icon()}
{:else}
<ArrowUpRightIcon
class="size-3.5"
/>
{/if}
</span>
</div>
<div class="mt-[18px] text-base font-semibold">{#if typeof title === "function"}{@render title()}{:else}{title}{/if}</div>
{#if description}
<div class="mt-1.5 text-[13px] text-muted-foreground">{#if typeof description === "function"}{@render description()}{:else}{description}{/if}</div>
{/if}
</Card>

See the Elevation guide for the levels, roles and layered mode.

Flat by default. elevation is forwarded to the card: sunken (-1) a soft inset well, raised (+1) the bevel, floating (+2) the bevel plus one soft drop. In a layered ElevationProvider the card resolves to its role default (raised).

StackBlitz ↗StackBlitz ↗StackBlitz ↗

Flat (0)

01
Create
Pick up to 10 assets and set weights.
02
Share
A link, OG image, feed card and Blink.
03
Join
Investors pay in USDC in one click.

Raised (+1)

01
Create
Pick up to 10 assets and set weights.
02
Share
A link, OG image, feed card and Blink.
03
Join
Investors pay in USDC in one click.

Floating (+2)

01
Create
Pick up to 10 assets and set weights.
02
Share
A link, OG image, feed card and Blink.
03
Join
Investors pay in USDC in one click.

Flat (0)

01
Create
Pick up to 10 assets and set weights.
02
Share
A link, OG image, feed card and Blink.
03
Join
Investors pay in USDC in one click.

Raised (+1)

01
Create
Pick up to 10 assets and set weights.
02
Share
A link, OG image, feed card and Blink.
03
Join
Investors pay in USDC in one click.

Floating (+2)

01
Create
Pick up to 10 assets and set weights.
02
Share
A link, OG image, feed card and Blink.
03
Join
Investors pay in USDC in one click.

Flat (0)

01
Create
Pick up to 10 assets and set weights.
02
Share
A link, OG image, feed card and Blink.
03
Join
Investors pay in USDC in one click.

Raised (+1)

01
Create
Pick up to 10 assets and set weights.
02
Share
A link, OG image, feed card and Blink.
03
Join
Investors pay in USDC in one click.

Floating (+2)

01
Create
Pick up to 10 assets and set weights.
02
Share
A link, OG image, feed card and Blink.
03
Join
Investors pay in USDC in one click.
Part Prop Type Default
StepCard index React ReactNode; Vue slot index (prop string | number as fallback); Svelte string | number | Snippet required
title React ReactNode; Vue slot title (string prop as fallback); Svelte string | Snippet required
description React ReactNode; Vue slot description (string prop as fallback); Svelte string | Snippet none
icon React ReactNode; Vue slot icon; Svelte icon snippet arrow up-right
elevation ✦ "auto" | "sunken" | "flat" | "raised" | "floating" "auto"
  • Whole block is an Edmi addition (DESIGN §5 Patterns, board 12). Composed from: card.
  • Install: shadcn add @edmi-ui/step-card.