Step Card
Numbered step card with a corner icon, title and description.
import { StepCard } from "@/components/step-card/step-card";
export default function Demo() { return ( <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>Installation
Section titled “Installation”npx shadcn@latest add @edmi-ui/step-cardpnpm dlx shadcn@latest add @edmi-ui/step-cardyarn dlx shadcn@latest add @edmi-ui/step-cardbunx shadcn@latest add @edmi-ui/step-cardPick your package manager above; the choice is remembered. Add the @edmi-ui registry first, see Getting started.
Add the Edmi components this one builds on:
card,elevation.Copy the code from Component source into your project and update the import paths to match your aliases.
npx shadcn-vue@latest add @edmi-ui/step-cardpnpm dlx shadcn-vue@latest add @edmi-ui/step-cardyarn dlx shadcn-vue@latest add @edmi-ui/step-cardbunx shadcn-vue@latest add @edmi-ui/step-cardPick your package manager above; the choice is remembered. Add the @edmi-ui registry first, see Getting started.
Install the dependencies:
Terminal window npm install @lucide/vueTerminal window pnpm add @lucide/vueTerminal window yarn add @lucide/vueTerminal window bun add @lucide/vueAdd the Edmi components this one builds on:
card,elevation.Copy the code from Component source into your project and update the import paths to match your aliases.
npx shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/step-card.jsonpnpm dlx shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/step-card.jsonyarn dlx shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/step-card.jsonbunx --bun shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/step-card.jsonPick your package manager above; the choice is remembered. Add the @edmi-ui registry first, see Getting started.
Add the Edmi components this one builds on:
card,elevation.Copy the code from Component source into your project and update the import paths to match your aliases.
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>Component source
Section titled “Component source”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 };<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>export { default as StepCard } from "./StepCard.vue";import Root from "./step-card.svelte";
export { Root, // Root as StepCard,};<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>Elevation ✦
Section titled “Elevation ✦”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).
Flat (0)
Raised (+1)
Floating (+2)
Flat (0)
Raised (+1)
Floating (+2)
Flat (0)
Raised (+1)
Floating (+2)
import { StepCard } from "@/components/step-card/step-card";import type { Elevation } from "@/components/ui/elevation";
function Sample({ elevation }: { elevation: Elevation }) { return ( <div className="flex flex-wrap gap-3"> <StepCard elevation={elevation} className="w-[200px]" index="01" title="Create" description="Pick up to 10 assets and set weights." /> <StepCard elevation={elevation} className="w-[200px]" index="02" title="Share" description="A link, OG image, feed card and Blink." /> <StepCard elevation={elevation} className="w-[200px]" index="03" title="Join" description="Investors pay in USDC in one click." /> </div> );}
const levels = [ { value: "flat", label: "Flat (0)" }, { value: "raised", label: "Raised (+1)" }, { value: "floating", label: "Floating (+2)" },] as const;
export default function Demo() { return ( <div className="flex flex-col gap-6"> {levels.map(({ value, label }) => ( <div key={value} className="flex flex-col gap-2"> <p className="text-xs font-medium text-muted-foreground">{label}</p> <Sample elevation={value} /> </div> ))} </div> );}<script setup lang="ts">import { StepCard } from "@/components/ui/step-card";
const levels = [ { value: "flat", label: "Flat (0)" } as const, { value: "raised", label: "Raised (+1)" } as const, { value: "floating", label: "Floating (+2)" } as const,];</script>
<template> <div class="flex flex-col gap-6"> <div v-for="l in levels" :key="l.value" class="flex flex-col gap-2"> <p class="text-xs font-medium text-muted-foreground">{{ l.label }}</p> <div class="flex flex-wrap gap-3"> <StepCard :elevation="l.value" class="w-[200px]" index="01" title="Create" description="Pick up to 10 assets and set weights." /> <StepCard :elevation="l.value" class="w-[200px]" index="02" title="Share" description="A link, OG image, feed card and Blink." /> <StepCard :elevation="l.value" class="w-[200px]" index="03" title="Join" description="Investors pay in USDC in one click." /> </div> </div> </div></template><script lang="ts"> import { StepCard } from "$lib/components/ui/step-card";
const levels = [ { value: "flat", label: "Flat (0)" } as const, { value: "raised", label: "Raised (+1)" } as const, { value: "floating", label: "Floating (+2)" } as const, ];</script>
<div class="flex flex-col gap-6"> {#each levels as l (l.value)} <div class="flex flex-col gap-2"> <p class="text-xs font-medium text-muted-foreground">{l.label}</p><div class="flex flex-wrap gap-3"> <StepCard elevation={l.value} class="w-[200px]" index="01" title="Create" description="Pick up to 10 assets and set weights." /> <StepCard elevation={l.value} class="w-[200px]" index="02" title="Share" description="A link, OG image, feed card and Blink." /> <StepCard elevation={l.value} class="w-[200px]" index="03" title="Join" description="Investors pay in USDC in one click." /></div> </div> {/each}</div>| 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" |
✦ Edmi additions
Section titled “✦ Edmi additions”- Whole block is an Edmi addition (DESIGN §5 Patterns, board 12). Composed from: card.
- Install:
shadcn add @edmi-ui/step-card.