Stat Tile
KPI tile with a mono value and delta badge, an optional segmented meter, and a stat strip for grouped numbers.
import { StatStrip, StatStripItem, StatTile,} from "@/components/stat-tile/stat-tile";
export default function Demo() { return ( <div className="flex flex-col gap-4"> <div className="flex flex-wrap gap-4"> <StatTile label="AUM" value="$49,182" delta="+37%" deltaLabel="vs last week" /> <StatTile label="Daily active holders" value="10,291" meter={{ value: 23 / 30, zones: [ { upTo: 14 / 30, color: "var(--chart-1)" }, { upTo: 24 / 30, color: "var(--chart-3)" }, { upTo: 1, color: "var(--chart-5)" }, ], }} delta="+8%" deltaLabel="vs last week" /> <StatTile label="Net flow" value="−$1,204" delta="−4.2%" deltaLabel="vs last week" /> </div> <StatStrip> <StatStripItem value="412" label="holders" /> <StatStripItem value="$49.2K" label="AUM" /> <StatStripItem value="63%" label="activity off-hours" /> <StatStripItem value="14" label="indexes" /> </StatStrip> </div> );}<script setup lang="ts">import { StatStrip, StatStripItem, StatTile } from "@/components/ui/stat-tile";
const meter = { value: 23 / 30, zones: [ { upTo: 14 / 30, color: "var(--chart-1)" }, { upTo: 24 / 30, color: "var(--chart-3)" }, { upTo: 1, color: "var(--chart-5)" }, ],};</script>
<template> <div class="flex flex-col gap-4"> <div class="flex flex-wrap gap-4"> <StatTile label="AUM" value="$49,182" delta="+37%" delta-label="vs last week" /> <StatTile label="Daily active holders" value="10,291" :meter="meter" delta="+8%" delta-label="vs last week" /> <StatTile label="Net flow" value="−$1,204" delta="−4.2%" delta-label="vs last week" /> </div> <StatStrip> <StatStripItem value="412" label="holders" /> <StatStripItem value="$49.2K" label="AUM" /> <StatStripItem value="63%" label="activity off-hours" /> <StatStripItem value="14" label="indexes" /> </StatStrip> </div></template><script lang="ts"> import { StatStrip, StatStripItem, StatTile } from "$lib/components/ui/stat-tile";
const meter = { value: 23 / 30, zones: [ { upTo: 14 / 30, color: "var(--chart-1)" }, { upTo: 24 / 30, color: "var(--chart-3)" }, { upTo: 1, color: "var(--chart-5)" }, ], };</script>
<div class="flex flex-col gap-4"> <div class="flex flex-wrap gap-4"> <StatTile label="AUM" value="$49,182" delta="+37%" deltaLabel="vs last week" /> <StatTile label="Daily active holders" value="10,291" {meter} delta="+8%" deltaLabel="vs last week" /> <StatTile label="Net flow" value="−$1,204" delta="−4.2%" deltaLabel="vs last week" /> </div> <StatStrip> <StatStripItem value="412" label="holders" /> <StatStripItem value="$49.2K" label="AUM" /> <StatStripItem value="63%" label="activity off-hours" /> <StatStripItem value="14" label="indexes" /> </StatStrip></div>Installation
Section titled “Installation”npx shadcn@latest add @edmi-ui/stat-tilepnpm dlx shadcn@latest add @edmi-ui/stat-tileyarn dlx shadcn@latest add @edmi-ui/stat-tilebunx shadcn@latest add @edmi-ui/stat-tilePick 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,badge,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/stat-tilepnpm dlx shadcn-vue@latest add @edmi-ui/stat-tileyarn dlx shadcn-vue@latest add @edmi-ui/stat-tilebunx shadcn-vue@latest add @edmi-ui/stat-tilePick 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,badge,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/stat-tile.jsonpnpm dlx shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/stat-tile.jsonyarn dlx shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/stat-tile.jsonbunx --bun shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/stat-tile.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,badge,elevation.Copy the code from Component source into your project and update the import paths to match your aliases.
import { StatTile } from "@/components/stat-tile/stat-tile";
<div className="flex flex-col gap-4"> <div className="flex flex-wrap gap-4"> <StatTile label="AUM" value="$49,182" delta="+37%" deltaLabel="vs last week" /> <StatTile label="Daily active holders" value="10,291" meter={{ value: 23 / 30, zones: [ { upTo: 14 / 30, color: "var(--chart-1)" }, { upTo: 24 / 30, color: "var(--chart-3)" }, { upTo: 1, color: "var(--chart-5)" }, ], }} delta="+8%" deltaLabel="vs last week" /> {/* … */} </div> {/* … */}</div><script setup lang="ts">import { StatStrip, StatStripItem, StatTile } from "@/components/ui/stat-tile";</script>
<template> <div class="flex flex-col gap-4"> <div class="flex flex-wrap gap-4"> <StatTile label="AUM" value="$49,182" delta="+37%" delta-label="vs last week" /> <StatTile label="Daily active holders" value="10,291" delta="+8%" delta-label="vs last week" /> <StatTile label="Net flow" value="−$1,204" delta="−4.2%" delta-label="vs last week" /> </div> <StatStrip> <StatStripItem value="412" label="holders" /> <StatStripItem value="$49.2K" label="AUM" /> <StatStripItem value="63%" label="activity off-hours" /> <StatStripItem value="14" label="indexes" /> </StatStrip> </div></template><script lang="ts"> import { StatStrip, StatStripItem, StatTile, } from "$lib/components/ui/stat-tile";</script>
<div class="flex flex-col gap-4"> <div class="flex flex-wrap gap-4"> <StatTile label="AUM" value="$49,182" delta="+37%" deltaLabel="vs last week" /> <StatTile label="Daily active holders" value="10,291" delta="+8%" deltaLabel="vs last week" /> <StatTile label="Net flow" value="−$1,204" delta="−4.2%" deltaLabel="vs last week" /> </div> <StatStrip> <StatStripItem value="412" label="holders" /> <StatStripItem value="$49.2K" label="AUM" /> <StatStripItem value="63%" label="activity off-hours" /> <StatStripItem value="14" label="indexes" /> </StatStrip></div>Component source
Section titled “Component source”import { cn } from "cn";import type * as React from "react";import { Badge } from "@/registry/edmi/ui/badge";import { Card } from "@/registry/edmi/ui/card";import type { Elevation } from "@/registry/edmi/ui/elevation";
const isDown = (s: string) => /^[-−–]/.test(s.trim());
type MeterZone = { /** Upper bound of the zone, 0..1. */ upTo: number; /** Any CSS color, e.g. `var(--chart-1)`. */ color: string;};
const defaultZones: MeterZone[] = [ { upTo: 0.5, color: "var(--chart-1)" }, { upTo: 0.8, color: "var(--chart-3)" }, { upTo: 1, color: "var(--chart-5)" },];
// Segmented meter: `steps` flat bars, filled up to `value` (0..1), the rest dimmed.function StatMeter({ className, value, steps = 30, zones = defaultZones, ...props}: Omit<React.ComponentProps<"div">, "children"> & { value: number; steps?: number; zones?: MeterZone[];}) { return ( // biome-ignore lint/a11y/useSemanticElements: <meter> cannot be styled as segments <div data-slot="stat-meter" role="meter" aria-valuemin={0} aria-valuemax={1} aria-valuenow={value} className={cn("flex gap-0.5", className)} {...props} > {Array.from({ length: steps }, (_, i) => { const at = (i + 1) / steps; const zone = zones.find((z) => at <= z.upTo) ?? zones[zones.length - 1]; return ( <span // biome-ignore lint/suspicious/noArrayIndexKey: fixed-length static list key={i} className={cn("h-[7px] flex-1", at > value && "opacity-30")} style={{ background: zone?.color }} /> ); })} </div> );}
// KPI tile: label, mono value, delta badge (+ optional meter). Delta direction comes from its sign.function StatTile({ className, label, value, delta, deltaLabel, trend, meter, elevation, children, ...props}: Omit<React.ComponentProps<typeof Card>, "children" | "size"> & { label: React.ReactNode; value: React.ReactNode; delta?: string; deltaLabel?: React.ReactNode; /** Overrides the sign-based direction of `delta`. */ trend?: "up" | "down"; /** Segmented meter under the value. */ meter?: React.ComponentProps<typeof StatMeter>; /** ✦ depth of the tile (forwarded to Card). */ elevation?: Elevation; children?: React.ReactNode;}) { const down = trend ? trend === "down" : delta ? isDown(delta) : false; return ( <Card data-slot="stat-tile" elevation={elevation} className={cn("w-60 gap-0 px-5 py-[18px]", className)} {...props} > <div className="text-[13px] text-muted-foreground">{label}</div> <div className="mt-1.5 font-mono text-[28px] leading-tight tracking-[-0.5px]"> {value} </div> {meter ? ( <StatMeter {...meter} className={cn("mt-2.5", meter.className)} /> ) : null} {delta ? ( <div className="mt-2.5 flex items-center gap-2"> <Badge variant={down ? "destructive" : "success"} shape="number" className="px-[7px]" > {delta} </Badge> {deltaLabel ? ( <span className="text-xs text-muted-foreground">{deltaLabel}</span> ) : null} </div> ) : null} {children} </Card> );}
// Row of numbers in one card; cells are divided by 1px borders.function StatStrip({ className, elevation, ...props}: React.ComponentProps<typeof Card> & { /** ✦ depth of the strip (forwarded to Card). */ elevation?: Elevation;}) { return ( <Card data-slot="stat-strip" elevation={elevation} className={cn("flex-row gap-0 overflow-x-auto p-0", className)} {...props} /> );}
function StatStripItem({ className, value, label, ...props}: Omit<React.ComponentProps<"div">, "children"> & { value: React.ReactNode; label: React.ReactNode;}) { return ( <div data-slot="stat-strip-item" className={cn( "min-w-[140px] flex-1 border-border px-[22px] py-4 not-first:border-l", className, )} {...props} > <div className="font-mono text-2xl">{value}</div> <div className="mt-1 text-xs text-muted-foreground">{label}</div> </div> );}
export type { MeterZone };export { StatMeter, StatStrip, StatStripItem, StatTile };<script setup lang="ts">import type { HTMLAttributes } from 'vue'import type { MeterZone } from '.'import { computed } from 'vue'import { cn } from '@/registry/edmi/lib/utils'
const props = withDefaults(defineProps<{ value: number steps?: number zones?: MeterZone[] class?: HTMLAttributes['class']}>(), { steps: 30, zones: () => [ { upTo: 0.5, color: 'var(--chart-1)' }, { upTo: 0.8, color: 'var(--chart-3)' }, { upTo: 1, color: 'var(--chart-5)' }, ],})
// Segmented meter: `steps` flat bars, filled up to `value` (0..1), the rest dimmed.const bars = computed(() => Array.from({ length: props.steps }, (_, i) => { const at = (i + 1) / props.steps const zone = props.zones.find(z => at <= z.upTo) ?? props.zones[props.zones.length - 1] return { dim: at > props.value, color: zone?.color } }),)</script>
<template> <div data-slot="stat-meter" role="meter" aria-valuemin="0" aria-valuemax="1" :aria-valuenow="value" :class="cn('flex gap-0.5', props.class)" > <span v-for="(b, i) in bars" :key="i" :class="cn('h-[7px] flex-1', b.dim && 'opacity-30')" :style="{ background: b.color }" /> </div></template><script setup lang="ts">import type { HTMLAttributes } from 'vue'import { cn } from '@/registry/edmi/lib/utils'import { Card } from '@/registry/edmi/ui/card'import type { Elevation } from '@/registry/edmi/ui/elevation'
const props = defineProps<{ /** ✦ depth of the card: sunken -1, flat 0, raised +1, floating +2 */ elevation?: Elevation class?: HTMLAttributes['class']}>()</script>
<template> <!-- Row of numbers in one card; cells are divided by 1px borders. --> <Card :elevation="elevation" data-slot="stat-strip" :class="cn('flex-row gap-0 overflow-x-auto p-0', props.class)"> <slot /> </Card></template><script setup lang="ts">import type { HTMLAttributes } from 'vue'import { cn } from '@/registry/edmi/lib/utils'
const props = defineProps<{ value?: string | number label?: string class?: HTMLAttributes['class']}>()</script>
<template> <div data-slot="stat-strip-item" :class="cn('min-w-[140px] flex-1 border-border px-[22px] py-4 not-first:border-l', props.class)" > <div class="font-mono text-2xl"> <slot name="value">{{ value }}</slot> </div> <div class="mt-1 text-xs text-muted-foreground"> <slot name="label">{{ label }}</slot> </div> </div></template><script setup lang="ts">import type { HTMLAttributes } from 'vue'import type { MeterZone } from '.'import { computed } from 'vue'import { cn } from '@/registry/edmi/lib/utils'import { Badge } from '@/registry/edmi/ui/badge'import { Card } from '@/registry/edmi/ui/card'import type { Elevation } from '@/registry/edmi/ui/elevation'import StatMeter from './StatMeter.vue'
const props = defineProps<{ /** ✦ depth of the card: sunken -1, flat 0, raised +1, floating +2 */ elevation?: Elevation label?: string value?: string | number delta?: string /** Muted text next to the delta badge (or use the `delta-label` slot). */ deltaLabel?: string /** Overrides the sign-based direction of `delta`. */ trend?: 'up' | 'down' /** Segmented meter under the value. */ meter?: { value: number, steps?: number, zones?: MeterZone[], class?: HTMLAttributes['class'] } class?: HTMLAttributes['class']}>()
const down = computed(() => props.trend ? props.trend === 'down' : props.delta ? /^[-−–]/.test(props.delta.trim()) : false,)</script>
<template> <!-- KPI tile: label, mono value, delta badge (+ optional meter). Delta direction comes from its sign. --> <Card :elevation="elevation" data-slot="stat-tile" :class="cn('w-60 gap-0 px-5 py-[18px]', props.class)"> <div class="text-[13px] text-muted-foreground"> <slot name="label">{{ label }}</slot> </div> <div class="mt-1.5 font-mono text-[28px] leading-tight tracking-[-0.5px]"> <slot name="value">{{ value }}</slot> </div> <StatMeter v-if="meter" v-bind="meter" :class="cn('mt-2.5', meter.class)" /> <div v-if="delta" class="mt-2.5 flex items-center gap-2"> <Badge :variant="down ? 'destructive' : 'success'" shape="number" class="px-[7px]"> {{ delta }} </Badge> <span v-if="deltaLabel || $slots['delta-label']" class="text-xs text-muted-foreground"><slot name="delta-label">{{ deltaLabel }}</slot></span> </div> <slot /> </Card></template>export type MeterZone = { /** Upper bound of the zone, 0..1. */ upTo: number; /** Any CSS color, e.g. `var(--chart-1)`. */ color: string;};
export { default as StatMeter } from "./StatMeter.vue";export { default as StatStrip } from "./StatStrip.vue";export { default as StatStripItem } from "./StatStripItem.vue";export { default as StatTile } from "./StatTile.vue";export { default as StatMeter, type MeterZone } from "./stat-meter.svelte";export { default as StatStrip } from "./stat-strip.svelte";export { default as StatStripItem } from "./stat-strip-item.svelte";export { default as StatTile } from "./stat-tile.svelte";<script lang="ts" module> export type MeterZone = { /** Upper bound of the zone, 0..1. */ upTo: number; /** Any CSS color, e.g. `var(--chart-1)`. */ color: string; };
const defaultZones: MeterZone[] = [ { upTo: 0.5, color: "var(--chart-1)" }, { upTo: 0.8, color: "var(--chart-3)" }, { upTo: 1, color: "var(--chart-5)" }, ];</script>
<script lang="ts"> import { cn, type WithElementRef } from "$lib/utils.js"; import type { HTMLAttributes } from "svelte/elements";
let { ref = $bindable(null), class: className, value, steps = 30, zones = defaultZones, ...restProps }: WithElementRef<Omit<HTMLAttributes<HTMLDivElement>, "children">> & { value: number; steps?: number; zones?: MeterZone[]; } = $props();
// Segmented meter: `steps` flat bars, filled up to `value` (0..1), the rest dimmed. const bars = $derived( Array.from({ length: steps }, (_, i) => { const at = (i + 1) / steps; const zone = zones.find((z) => at <= z.upTo) ?? zones[zones.length - 1]; return { dim: at > value, color: zone?.color }; }) );</script>
<div bind:this={ref} data-slot="stat-meter" role="meter" aria-valuemin={0} aria-valuemax={1} aria-valuenow={value} class={cn("flex gap-0.5", className)} {...restProps}> {#each bars as b, i (i)} <span class={cn("h-[7px] flex-1", b.dim && "opacity-30")} style:background={b.color}></span> {/each}</div><script lang="ts"> import { cn, type WithElementRef } from "$lib/utils.js"; import type { HTMLAttributes } from "svelte/elements"; import type { Snippet } from "svelte";
let { ref = $bindable(null), class: className, value, label, ...restProps }: WithElementRef<Omit<HTMLAttributes<HTMLDivElement>, "children">> & { value: string | number | Snippet; label: string | Snippet; } = $props();</script>
<div bind:this={ref} data-slot="stat-strip-item" class={cn("min-w-[140px] flex-1 border-border px-[22px] py-4 not-first:border-l", className)} {...restProps}> <div class="font-mono text-2xl"> {#if typeof value === "function"}{@render value()}{:else}{value}{/if} </div> <div class="mt-1 text-xs text-muted-foreground"> {#if typeof label === "function"}{@render label()}{:else}{label}{/if} </div></div><script lang="ts"> import { Card } from "$lib/registry/ui/card/index.js"; import { cn } from "$lib/utils.js"; import type { Elevation } from "$lib/registry/ui/elevation/index.js"; import type { ComponentProps } from "svelte";
let { ref = $bindable(null), class: className, elevation = "auto", children, ...restProps }: Omit<ComponentProps<typeof Card>, "size"> & { /** ✦ depth of the strip (forwarded to Card). */ elevation?: Elevation; } = $props();</script>
<!-- Row of numbers in one card; cells are divided by 1px borders. --><Card bind:ref data-slot="stat-strip" {elevation} class={cn("flex-row gap-0 overflow-x-auto p-0", className)} {...restProps}> {@render children?.()}</Card><script lang="ts"> import { Badge } from "$lib/registry/ui/badge/index.js"; import { Card } from "$lib/registry/ui/card/index.js"; import { cn } from "$lib/utils.js"; import type { Elevation } from "$lib/registry/ui/elevation/index.js"; import type { ComponentProps, Snippet } from "svelte"; import StatMeter from "./stat-meter.svelte";
let { ref = $bindable(null), class: className, label, value, delta, deltaLabel, trend, meter, elevation = "auto", children, ...restProps }: Omit<ComponentProps<typeof Card>, "children" | "size"> & { label: Snippet | string; value: Snippet | string | number; delta?: string; deltaLabel?: string | Snippet; /** Overrides the sign-based direction of `delta`. */ trend?: "up" | "down"; /** Segmented meter under the value. */ meter?: ComponentProps<typeof StatMeter>; /** ✦ depth of the tile (forwarded to Card). */ elevation?: Elevation; children?: Snippet; } = $props();
const down = $derived(trend ? trend === "down" : delta ? /^[-−–]/.test(delta.trim()) : false);</script>
<!-- KPI tile: label, mono value, delta badge (+ optional meter). Delta direction comes from its sign. --><Card bind:ref data-slot="stat-tile" {elevation} class={cn("w-60 gap-0 px-5 py-[18px]", className)} {...restProps}> <div class="text-[13px] text-muted-foreground"> {#if typeof label === "function"}{@render label()}{:else}{label}{/if} </div> <div class="mt-1.5 font-mono text-[28px] leading-tight tracking-[-0.5px]"> {#if typeof value === "function"}{@render value()}{:else}{value}{/if} </div> {#if meter} <StatMeter {...meter} class={cn("mt-2.5", meter.class)} /> {/if} {#if delta} <div class="mt-2.5 flex items-center gap-2"> <Badge variant={down ? "destructive" : "success"} shape="number" class="px-[7px]">{delta}</Badge> {#if deltaLabel} <span class="text-xs text-muted-foreground"> {#if typeof deltaLabel === "function"}{@render deltaLabel()}{:else}{deltaLabel}{/if} </span> {/if} </div> {/if} {@render children?.()}</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 { StatStrip, StatStripItem, StatTile,} from "@/components/stat-tile/stat-tile";import type { Elevation } from "@/components/ui/elevation";
function Sample({ elevation }: { elevation: Elevation }) { return ( <div className="flex flex-col gap-4"> <div className="flex flex-wrap gap-4"> <StatTile elevation={elevation} label="AUM" value="$49,182" delta="+37%" deltaLabel="vs last week" /> <StatTile elevation={elevation} label="Daily active holders" value="10,291" meter={{ value: 23 / 30, zones: [ { upTo: 14 / 30, color: "var(--chart-1)" }, { upTo: 24 / 30, color: "var(--chart-3)" }, { upTo: 1, color: "var(--chart-5)" }, ], }} delta="+8%" deltaLabel="vs last week" /> <StatTile elevation={elevation} label="Net flow" value="−$1,204" delta="−4.2%" deltaLabel="vs last week" /> </div> <StatStrip elevation={elevation}> <StatStripItem value="412" label="holders" /> <StatStripItem value="$49.2K" label="AUM" /> <StatStripItem value="63%" label="activity off-hours" /> <StatStripItem value="14" label="indexes" /> </StatStrip> </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 { StatStrip, StatStripItem, StatTile } from "@/components/ui/stat-tile";
const meter = { value: 23 / 30, zones: [ { upTo: 14 / 30, color: "var(--chart-1)" }, { upTo: 24 / 30, color: "var(--chart-3)" }, { upTo: 1, color: "var(--chart-5)" }, ],};
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-col gap-4"> <div class="flex flex-wrap gap-4"> <StatTile :elevation="l.value" label="AUM" value="$49,182" delta="+37%" delta-label="vs last week" /> <StatTile :elevation="l.value" label="Daily active holders" value="10,291" :meter="meter" delta="+8%" delta-label="vs last week" /> <StatTile :elevation="l.value" label="Net flow" value="−$1,204" delta="−4.2%" delta-label="vs last week" /> </div> <StatStrip> <StatStripItem value="412" label="holders" /> <StatStripItem value="$49.2K" label="AUM" /> <StatStripItem value="63%" label="activity off-hours" /> <StatStripItem value="14" label="indexes" /> </StatStrip> </div> </div> </div></template><script lang="ts"> import { StatStrip, StatStripItem, StatTile } from "$lib/components/ui/stat-tile";
const meter = { value: 23 / 30, zones: [ { upTo: 14 / 30, color: "var(--chart-1)" }, { upTo: 24 / 30, color: "var(--chart-3)" }, { upTo: 1, color: "var(--chart-5)" }, ], };
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-col gap-4"> <div class="flex flex-wrap gap-4"> <StatTile elevation={l.value} label="AUM" value="$49,182" delta="+37%" deltaLabel="vs last week" /> <StatTile elevation={l.value} label="Daily active holders" value="10,291" {meter} delta="+8%" deltaLabel="vs last week" /> <StatTile elevation={l.value} label="Net flow" value="−$1,204" delta="−4.2%" deltaLabel="vs last week" /> </div> <StatStrip elevation={l.value}> <StatStripItem value="412" label="holders" /> <StatStripItem value="$49.2K" label="AUM" /> <StatStripItem value="63%" label="activity off-hours" /> <StatStripItem value="14" label="indexes" /> </StatStrip></div> </div> {/each}</div>| Part | Prop | Type | Default |
|---|---|---|---|
StatTile |
label / value |
ReactNode (Vue slots label / value, Svelte string | Snippet) |
required |
delta |
string (sign sets success/destructive badge) |
none | |
deltaLabel |
ReactNode (Vue delta-label slot, Svelte string | Snippet) |
none | |
trend |
"up" | "down" (overrides the sign) |
auto | |
meter |
StatMeter props |
none | |
elevation |
"auto" | "sunken" | "flat" | "raised" | "floating" |
"auto" |
|
StatStrip |
elevation |
forwarded to the card | "auto" |
StatStripItem |
label / value |
ReactNode (Vue slots label / value, Svelte string | number | Snippet) |
required |
StatMeter |
value |
number 0..1 |
required |
steps |
number |
30 |
|
zones |
{ upTo: number; color: string }[] |
green / amber / red |
✦ Edmi additions
Section titled “✦ Edmi additions”StatMeterandStatStrip/StatStripItemare separate exports of the same block.