Skip to content
edmi-ui

Index Row

Market table row: avatar stack, name, ticker and tags, mono price, delta and sparkline; includes a matching header row.

StackBlitz ↗StackBlitz ↗StackBlitz ↗
IndexCreatorPrice7dAUMHolders30d
ANT
Mag Four TiltMAGT
Pre-IPOClone
GbFK…ZUWS$1.00+1.12%$120.1K2
ANT
Defense & SpaceDFSP
Pre-IPO
7Ge1…kXSq$0.9791−2.09%$29K1
IndexCreatorPrice7dAUMHolders30d
ANT
Mag Four TiltMAGT
Pre-IPO
Clone
GbFK…ZUWS$1.00+1.12%$120.1K2
ANT
Defense & SpaceDFSP
Pre-IPO
7Ge1…kXSq$0.9791−2.09%$29K1
Index Creator Price 7d AUM Holders 30d
A
N
T
Mag Four Tilt MAGT
Pre-IPOClone
GbFK…ZUWS $1.00 +1.12% $120.1K 2
A
N
T
Defense & Space DFSP
Pre-IPO
7Ge1…kXSq $0.9791 −2.09% $29K 1
Terminal window
npx shadcn@latest add @edmi-ui/index-row
Terminal window
pnpm dlx shadcn@latest add @edmi-ui/index-row
Terminal window
yarn dlx shadcn@latest add @edmi-ui/index-row
Terminal window
bunx shadcn@latest add @edmi-ui/index-row

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/index-row
Terminal window
pnpm dlx shadcn-vue@latest add @edmi-ui/index-row
Terminal window
yarn dlx shadcn-vue@latest add @edmi-ui/index-row
Terminal window
bunx shadcn-vue@latest add @edmi-ui/index-row

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/index-row.json
Terminal window
pnpm dlx shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/index-row.json
Terminal window
yarn dlx shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/index-row.json
Terminal window
bunx --bun shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/index-row.json

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

import { IndexRowHeader } from "@/components/index-row/index-row";
import { Card } from "@/components/ui/card";
import { Table, TableBody, TableHeader } from "@/components/ui/table";
<Card className="w-full gap-0 py-0">
<Table>
<TableHeader>
<IndexRowHeader
labels={{
name: "Index",
owner: "Creator",
change: "7d",
value: "AUM",
count: "Holders",
trend: "30d",
}}
/>
</TableHeader>
<TableBody>
</TableBody>
</Table>
</Card>
<script setup lang="ts">
import { IndexRow, IndexRowHeader } from "@/components/ui/index-row";
import { Card } from "@/components/ui/card";
import { Table, TableBody, TableHeader } from "@/components/ui/table";
</script>
<template>
<Card class="w-full gap-0 py-0">
<Table>
<TableHeader>
<IndexRowHeader :labels="{ name: 'Index', owner: 'Creator', change: '7d', value: 'AUM', count: 'Holders', trend: '30d' }" />
</TableHeader>
<TableBody>
<IndexRow :key="r.symbol" :index="r" />
</TableBody>
</Table>
</Card>
</template>
<script lang="ts">
import { Card } from "$lib/components/ui/card";
import { IndexRow, IndexRowHeader } from "$lib/components/ui/index-row";
import { Table, TableBody, TableHeader } from "$lib/components/ui/table";
</script>
<Card class="w-full gap-0 py-0">
<Table>
<TableHeader>
<IndexRowHeader labels={{ name: "Index", owner: "Creator", change: "7d", value: "AUM", count: "Holders", trend: "30d" }} />
</TableHeader>
<TableBody>
{#each rows as r (r.symbol)}
<IndexRow index={r} />
{/each}
</TableBody>
</Table>
</Card>
registry/blocks/index-row/index-row.tsx
import { cn } from "cn";
import type * as React from "react";
import {
Avatar,
AvatarFallback,
AvatarGroup,
AvatarImage,
} from "@/registry/edmi/ui/avatar";
import { Badge } from "@/registry/edmi/ui/badge";
import { TableCell, TableHead, TableRow } from "@/registry/edmi/ui/table";
const isDown = (s: string) => /^[-−–]/.test(s.trim());
// Tiny trend line. `tone` defaults to the direction of the series (last vs first).
function Sparkline({
className,
data,
tone,
width = 88,
height = 28,
...props
}: Omit<React.ComponentProps<"svg">, "children" | "width" | "height"> & {
data: number[];
tone?: "up" | "down";
width?: number;
height?: number;
}) {
if (data.length < 2) return null;
const min = Math.min(...data);
const max = Math.max(...data);
const span = max - min || 1;
const pad = 2;
const points = data
.map((v, i) => {
const x = pad + (i / (data.length - 1)) * (width - pad * 2);
const y = pad + (1 - (v - min) / span) * (height - pad * 2);
return `${x.toFixed(1)},${y.toFixed(1)}`;
})
.join(" ");
const down = tone ? tone === "down" : (data.at(-1) ?? 0) < (data[0] ?? 0);
return (
<svg
data-slot="sparkline"
width={width}
height={height}
viewBox={`0 0 ${width} ${height}`}
fill="none"
aria-hidden="true"
className={cn(
"inline-block",
down ? "text-destructive-text" : "text-success-text",
className,
)}
{...props}
>
<polyline
points={points}
stroke="currentColor"
strokeWidth="1.6"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
}
type IndexRowData = {
name: string;
symbol: string;
/** One avatar per member (e.g. a constituent): image URL or a short label. */
avatars: { label: string; image?: string }[];
tags?: string[];
/** Text of the owner column. */
owner: string;
price: string;
/** Signed percentage, e.g. `+1.12%`. */
change: string;
/** Text of the value column (mono, right-aligned). */
value: string;
/** Text of the count column (mono, right-aligned). */
count: string | number;
/** Series for the trend sparkline. */
spark?: number[];
href?: string;
};
type IndexRowHeaderLabels = {
name?: React.ReactNode;
owner?: React.ReactNode;
price?: React.ReactNode;
change?: React.ReactNode;
value?: React.ReactNode;
count?: React.ReactNode;
trend?: React.ReactNode;
};
const defaultLabels: Required<IndexRowHeaderLabels> = {
name: "Name",
owner: "Owner",
price: "Price",
change: "Change",
value: "Value",
count: "Count",
trend: "Trend",
};
// Header row matching the IndexRow columns. Place it in `<TableHeader>`.
function IndexRowHeader({
className,
rank = false,
labels,
...props
}: React.ComponentProps<typeof TableRow> & {
/** ✦ Leading empty cell for the `rank` column of IndexRow. */
rank?: boolean;
/** Column labels (each defaults to a neutral English word). */
labels?: IndexRowHeaderLabels;
}) {
const l = { ...defaultLabels, ...labels };
return (
<TableRow className={className} {...props}>
{rank ? <TableHead className="w-10" /> : null}
<TableHead>{l.name}</TableHead>
<TableHead>{l.owner}</TableHead>
<TableHead className="text-right">{l.price}</TableHead>
<TableHead className="text-right">{l.change}</TableHead>
<TableHead className="text-right">{l.value}</TableHead>
<TableHead className="text-right">{l.count}</TableHead>
<TableHead className="text-right">{l.trend}</TableHead>
</TableRow>
);
}
// One market row: avatar stack, name + ticker + tags, mono numbers, delta, sparkline. Use inside `<TableBody>`.
function IndexRow({
className,
index,
rank,
delta = "text",
...props
}: Omit<React.ComponentProps<typeof TableRow>, "children"> & {
index: IndexRowData;
/** ✦ Leading mono rank column (pair with `<IndexRowHeader rank />`). */
rank?: number | string;
/** ✦ `text` (default) or `pill`: soft tinted pill for the 7d delta. */
delta?: "text" | "pill";
}) {
const down = isDown(index.change);
return (
<TableRow data-slot="index-row" className={className} {...props}>
{rank !== undefined ? (
<TableCell className="w-10 pr-0 font-mono text-[13px] text-muted-foreground">
{rank}
</TableCell>
) : null}
<TableCell>
<div className="flex items-center gap-3">
<AvatarGroup>
{index.avatars.map((t) => (
<Avatar key={t.label} className="size-7">
{t.image ? <AvatarImage src={t.image} alt="" /> : null}
<AvatarFallback className="text-[10px]">
{t.label}
</AvatarFallback>
</Avatar>
))}
</AvatarGroup>
<div>
<div className="flex items-center gap-2">
{index.href ? (
<a href={index.href} className="font-semibold hover:underline">
{index.name}
</a>
) : (
<span className="font-semibold">{index.name}</span>
)}
<span className="font-mono text-[11.5px] text-muted-foreground">
{index.symbol}
</span>
</div>
{index.tags?.length ? (
<div className="mt-1 flex items-center gap-1">
{index.tags.map((tag) => (
<Badge
key={tag}
variant="secondary"
className="h-5 text-[11px]"
>
{tag}
</Badge>
))}
</div>
) : null}
</div>
</div>
</TableCell>
<TableCell className="font-mono text-xs text-muted-foreground">
{index.owner}
</TableCell>
<TableCell className="text-right font-mono text-[12.5px] font-semibold">
{index.price}
</TableCell>
<TableCell
className={cn(
"text-right font-mono text-[12.5px]",
delta === "text" &&
(down ? "text-destructive-text" : "text-success-text"),
)}
>
{delta === "pill" ? (
<span
className={cn(
"inline-block rounded-md px-2 py-[3px] text-xs",
down
? "bg-destructive-soft text-destructive-text"
: "bg-brand-soft text-brand-text",
)}
>
{index.change}
</span>
) : (
index.change
)}
</TableCell>
<TableCell className="text-right font-mono text-[12.5px]">
{index.value}
</TableCell>
<TableCell className="text-right font-mono text-[12.5px]">
{index.count}
</TableCell>
<TableCell className="text-right">
{index.spark ? (
<Sparkline data={index.spark} tone={down ? "down" : "up"} />
) : null}
</TableCell>
</TableRow>
);
}
export type { IndexRowData, IndexRowHeaderLabels };
export { IndexRow, IndexRowHeader, Sparkline };
registry/ui/index-row/IndexRow.vue
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import type { IndexRowData } from '.'
import { computed } from 'vue'
import { cn } from '@/registry/edmi/lib/utils'
import { Avatar, AvatarFallback, AvatarGroup, AvatarImage } from '@/registry/edmi/ui/avatar'
import { Badge } from '@/registry/edmi/ui/badge'
import { TableCell, TableRow } from '@/registry/edmi/ui/table'
import Sparkline from './Sparkline.vue'
const props = withDefaults(defineProps<{
index: IndexRowData
/** ✦ Leading mono rank column (pair with `<IndexRowHeader rank />`). */
rank?: number | string
/** ✦ `text` (default) or `pill`: soft tinted pill for the change. */
delta?: 'text' | 'pill'
class?: HTMLAttributes['class']
}>(), {
rank: undefined,
delta: 'text',
})
const down = computed(() => /^[-−–]/.test(props.index.change.trim()))
</script>
<template>
<!-- One market row: avatar stack, name + ticker + tags, mono numbers, delta, sparkline. Use inside `<TableBody>`. -->
<TableRow data-slot="index-row" :class="props.class">
<TableCell v-if="props.rank !== undefined" class="w-10 pr-0 font-mono text-[13px] text-muted-foreground">
{{ props.rank }}
</TableCell>
<TableCell>
<div class="flex items-center gap-3">
<AvatarGroup>
<Avatar v-for="t in index.avatars" :key="t.label" class="size-7">
<AvatarImage v-if="t.image" :src="t.image" alt="" />
<AvatarFallback class="text-[10px]">
{{ t.label }}
</AvatarFallback>
</Avatar>
</AvatarGroup>
<div>
<div class="flex items-center gap-2">
<a v-if="index.href" :href="index.href" class="font-semibold hover:underline">{{ index.name }}</a>
<span v-else class="font-semibold">{{ index.name }}</span>
<span class="font-mono text-[11.5px] text-muted-foreground">{{ index.symbol }}</span>
</div>
<div v-if="index.tags?.length" class="mt-1 flex items-center gap-1">
<Badge v-for="tag in index.tags" :key="tag" variant="secondary" class="h-5 text-[11px]">
{{ tag }}
</Badge>
</div>
</div>
</div>
</TableCell>
<TableCell class="font-mono text-xs text-muted-foreground">
{{ index.owner }}
</TableCell>
<TableCell class="text-right font-mono text-[12.5px] font-semibold">
{{ index.price }}
</TableCell>
<TableCell
:class="cn('text-right font-mono text-[12.5px]', props.delta === 'text' && (down ? 'text-destructive-text' : 'text-success-text'))"
>
<span
v-if="props.delta === 'pill'"
:class="cn('inline-block rounded-md px-2 py-[3px] text-xs', down ? 'bg-destructive-soft text-destructive-text' : 'bg-brand-soft text-brand-text')"
>{{ index.change }}</span>
<template v-else>
{{ index.change }}
</template>
</TableCell>
<TableCell class="text-right font-mono text-[12.5px]">
{{ index.value }}
</TableCell>
<TableCell class="text-right font-mono text-[12.5px]">
{{ index.count }}
</TableCell>
<TableCell class="text-right">
<Sparkline v-if="index.spark" :data="index.spark" :tone="down ? 'down' : 'up'" />
</TableCell>
</TableRow>
</template>
registry/ui/index-row/IndexRowHeader.vue
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import type { IndexRowHeaderLabels } from '.'
import { computed } from 'vue'
import { TableHead, TableRow } from '@/registry/edmi/ui/table'
const props = defineProps<{
/** ✦ Leading empty cell for the `rank` column of IndexRow. */
rank?: boolean
/** Column labels (each defaults to a neutral English word). */
labels?: IndexRowHeaderLabels
class?: HTMLAttributes['class']
}>()
const l = computed(() => ({
name: 'Name',
owner: 'Owner',
price: 'Price',
change: 'Change',
value: 'Value',
count: 'Count',
trend: 'Trend',
...props.labels,
}))
</script>
<template>
<!-- Header row matching the IndexRow columns. Place it in `<TableHeader>`. -->
<TableRow :class="props.class">
<TableHead v-if="props.rank" class="w-10" />
<TableHead>{{ l.name }}</TableHead>
<TableHead>{{ l.owner }}</TableHead>
<TableHead class="text-right">
{{ l.price }}
</TableHead>
<TableHead class="text-right">
{{ l.change }}
</TableHead>
<TableHead class="text-right">
{{ l.value }}
</TableHead>
<TableHead class="text-right">
{{ l.count }}
</TableHead>
<TableHead class="text-right">
{{ l.trend }}
</TableHead>
</TableRow>
</template>
registry/ui/index-row/Sparkline.vue
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { computed } from 'vue'
import { cn } from '@/registry/edmi/lib/utils'
const props = withDefaults(defineProps<{
data: number[]
/** Defaults to the direction of the series (last vs first). */
tone?: 'up' | 'down'
width?: number
height?: number
class?: HTMLAttributes['class']
}>(), {
width: 88,
height: 28,
})
const points = computed(() => {
const { data, width, height } = props
const min = Math.min(...data)
const max = Math.max(...data)
const span = max - min || 1
const pad = 2
return data
.map((v, i) => {
const x = pad + (i / (data.length - 1)) * (width - pad * 2)
const y = pad + (1 - (v - min) / span) * (height - pad * 2)
return `${x.toFixed(1)},${y.toFixed(1)}`
})
.join(' ')
})
const down = computed(() =>
props.tone ? props.tone === 'down' : (props.data.at(-1) ?? 0) < (props.data[0] ?? 0),
)
</script>
<template>
<!-- Tiny trend line. -->
<svg
v-if="data.length >= 2"
data-slot="sparkline"
:width="width"
:height="height"
:viewBox="`0 0 ${width} ${height}`"
fill="none"
aria-hidden="true"
:class="cn('inline-block', down ? 'text-destructive-text' : 'text-success-text', props.class)"
>
<polyline
:points="points"
stroke="currentColor"
stroke-width="1.6"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</template>
registry/ui/index-row/index.ts
export type IndexRowData = {
name: string;
symbol: string;
/** One avatar per member (e.g. a constituent): image URL or a short label. */
avatars: { label: string; image?: string }[];
tags?: string[];
/** Text of the owner column. */
owner: string;
price: string;
/** Signed percentage, e.g. `+1.12%`. */
change: string;
/** Text of the value column (mono, right-aligned). */
value: string;
/** Text of the count column (mono, right-aligned). */
count: string | number;
/** Series for the trend sparkline. */
spark?: number[];
href?: string;
};
export type IndexRowHeaderLabels = {
name?: string;
owner?: string;
price?: string;
change?: string;
value?: string;
count?: string;
trend?: string;
};
export { default as IndexRow } from "./IndexRow.vue";
export { default as IndexRowHeader } from "./IndexRowHeader.vue";
export { default as Sparkline } from "./Sparkline.vue";
src/lib/registry/ui/index-row/index-row-header.svelte
<script lang="ts" module>
import type { Snippet } from "svelte";
export type IndexRowHeaderLabels = {
name?: string | Snippet;
owner?: string | Snippet;
price?: string | Snippet;
change?: string | Snippet;
value?: string | Snippet;
count?: string | Snippet;
trend?: string | Snippet;
};
</script>
<script lang="ts">
import { TableHead, TableRow } from "$lib/registry/ui/table/index.js";
import type { ComponentProps } from "svelte";
let {
ref = $bindable(null),
rank = false,
labels,
...restProps
}: Omit<ComponentProps<typeof TableRow>, "children"> & {
/** ✦ Leading empty cell for the `rank` column of IndexRow. */
rank?: boolean;
/** Column labels (each defaults to a neutral English word). */
labels?: IndexRowHeaderLabels;
} = $props();
const l = $derived({
name: "Name",
owner: "Owner",
price: "Price",
change: "Change",
value: "Value",
count: "Count",
trend: "Trend",
...labels,
});
</script>
{#snippet text(v: string | Snippet)}
{#if typeof v === "function"}{@render v()}{:else}{v}{/if}
{/snippet}
<!-- Header row matching the IndexRow columns. Place it in `<TableHeader>`. -->
<TableRow bind:ref {...restProps}>
{#if rank}<TableHead class="w-10" />{/if}
<TableHead>{@render text(l.name)}</TableHead>
<TableHead>{@render text(l.owner)}</TableHead>
<TableHead class="text-right">{@render text(l.price)}</TableHead>
<TableHead class="text-right">{@render text(l.change)}</TableHead>
<TableHead class="text-right">{@render text(l.value)}</TableHead>
<TableHead class="text-right">{@render text(l.count)}</TableHead>
<TableHead class="text-right">{@render text(l.trend)}</TableHead>
</TableRow>
src/lib/registry/ui/index-row/index-row.svelte
<script lang="ts" module>
export type IndexRowData = {
name: string;
symbol: string;
/** One avatar per member (e.g. a constituent): image URL or a short label. */
avatars: { label: string; image?: string }[];
tags?: string[];
/** Text of the owner column. */
owner: string;
price: string;
/** Signed percentage, e.g. `+1.12%`. */
change: string;
/** Text of the value column (mono, right-aligned). */
value: string;
/** Text of the count column (mono, right-aligned). */
count: string | number;
/** Series for the trend sparkline. */
spark?: number[];
href?: string;
};
</script>
<script lang="ts">
import { Avatar, AvatarFallback, AvatarGroup, AvatarImage } from "$lib/registry/ui/avatar/index.js";
import { Badge } from "$lib/registry/ui/badge/index.js";
import { TableCell, TableRow } from "$lib/registry/ui/table/index.js";
import { cn } from "$lib/utils.js";
import type { ComponentProps } from "svelte";
import Sparkline from "./sparkline.svelte";
let {
ref = $bindable(null),
class: className,
index,
rank,
delta = "text",
...restProps
}: Omit<ComponentProps<typeof TableRow>, "children"> & {
index: IndexRowData;
/** ✦ Leading mono rank column (pair with `<IndexRowHeader rank />`). */
rank?: number | string;
/** ✦ `text` (default) or `pill`: soft tinted pill for the change. */
delta?: "text" | "pill";
} = $props();
const down = $derived(/^[-−–]/.test(index.change.trim()));
</script>
<!-- One market row: avatar stack, name + ticker + tags, mono numbers, delta, sparkline. Use inside `<TableBody>`. -->
<TableRow bind:ref data-slot="index-row" class={className} {...restProps}>
{#if rank !== undefined}
<TableCell class="w-10 pr-0 font-mono text-[13px] text-muted-foreground">{rank}</TableCell>
{/if}
<TableCell>
<div class="flex items-center gap-3">
<AvatarGroup>
{#each index.avatars as t (t.label)}
<Avatar class="size-7">
{#if t.image}<AvatarImage src={t.image} alt="" />{/if}
<AvatarFallback class="text-[10px]">{t.label}</AvatarFallback>
</Avatar>
{/each}
</AvatarGroup>
<div>
<div class="flex items-center gap-2">
{#if index.href}
<a href={index.href} class="font-semibold hover:underline">{index.name}</a>
{:else}
<span class="font-semibold">{index.name}</span>
{/if}
<span class="font-mono text-[11.5px] text-muted-foreground">{index.symbol}</span>
</div>
{#if index.tags?.length}
<div class="mt-1 flex items-center gap-1">
{#each index.tags as tag (tag)}
<Badge variant="secondary" class="h-5 text-[11px]">{tag}</Badge>
{/each}
</div>
{/if}
</div>
</div>
</TableCell>
<TableCell class="font-mono text-xs text-muted-foreground">{index.owner}</TableCell>
<TableCell class="text-right font-mono text-[12.5px] font-semibold">{index.price}</TableCell>
<TableCell
class={cn("text-right font-mono text-[12.5px]", delta === "text" && (down ? "text-destructive-text" : "text-success-text"))}
>
{#if delta === "pill"}
<span
class={cn(
"inline-block rounded-md px-2 py-[3px] text-xs",
down ? "bg-destructive-soft text-destructive-text" : "bg-brand-soft text-brand-text"
)}>{index.change}</span
>
{:else}
{index.change}
{/if}
</TableCell>
<TableCell class="text-right font-mono text-[12.5px]">{index.value}</TableCell>
<TableCell class="text-right font-mono text-[12.5px]">{index.count}</TableCell>
<TableCell class="text-right">
{#if index.spark}<Sparkline data={index.spark} tone={down ? "down" : "up"} />{/if}
</TableCell>
</TableRow>
src/lib/registry/ui/index-row/index.ts
export { default as IndexRow, type IndexRowData } from "./index-row.svelte";
export {
default as IndexRowHeader,
type IndexRowHeaderLabels,
} from "./index-row-header.svelte";
export { default as Sparkline } from "./sparkline.svelte";
src/lib/registry/ui/index-row/sparkline.svelte
<script lang="ts">
import { cn } from "$lib/utils.js";
import type { SVGAttributes } from "svelte/elements";
let {
ref = $bindable(null),
class: className,
data,
tone,
width = 88,
height = 28,
...restProps
}: Omit<SVGAttributes<SVGSVGElement>, "children" | "width" | "height"> & {
ref?: SVGSVGElement | null;
data: number[];
/** Defaults to the direction of the series (last vs first). */
tone?: "up" | "down";
width?: number;
height?: number;
} = $props();
const points = $derived.by(() => {
const min = Math.min(...data);
const max = Math.max(...data);
const span = max - min || 1;
const pad = 2;
return data
.map((v, i) => {
const x = pad + (i / (data.length - 1)) * (width - pad * 2);
const y = pad + (1 - (v - min) / span) * (height - pad * 2);
return `${x.toFixed(1)},${y.toFixed(1)}`;
})
.join(" ");
});
const down = $derived(tone ? tone === "down" : (data.at(-1) ?? 0) < (data[0] ?? 0));
</script>
<!-- Tiny trend line. -->
{#if data.length >= 2}
<svg
bind:this={ref}
data-slot="sparkline"
{width}
{height}
viewBox="0 0 {width} {height}"
fill="none"
aria-hidden="true"
class={cn("inline-block", down ? "text-destructive-text" : "text-success-text", className)}
{...restProps}
>
<polyline {points} stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" />
</svg>
{/if}
Part Prop Type Default
IndexRow index { name, symbol, avatars, tags?, owner, price, change, value, count, spark?, href? } required
rank ✦ number | string none (no rank column)
delta ✦ "text" | "pill" "text"
IndexRowHeader labels { name?, owner?, price?, change?, value?, count?, trend? } column labels (Svelte values may be snippets) Name, Owner, Price, Change, Value, Count, Trend
rank ✦ boolean false
Sparkline data number[] required
tone "up" | "down" last vs first
width / height number 88 / 28

Place IndexRowHeader in TableHeader and IndexRow in TableBody; numeric columns are mono and right-aligned (§4.10).

Leaderboard style: a leading mono rank column and the change as a soft tinted pill.

StackBlitz ↗StackBlitz ↗StackBlitz ↗
IndexCreatorPrice7dAUMHolders30d
1
ANT
Mag Four TiltMAGT
Pre-IPOClone
GbFK…ZUWS$1.00+1.12%$120.1K2
2
ANT
Defense & SpaceDFSP
Pre-IPO
7Ge1…kXSq$0.9791−2.09%$29K1
IndexCreatorPrice7dAUMHolders30d
1
ANT
Mag Four TiltMAGT
Pre-IPO
Clone
GbFK…ZUWS$1.00+1.12%$120.1K2
2
ANT
Defense & SpaceDFSP
Pre-IPO
7Ge1…kXSq$0.9791−2.09%$29K1
Index Creator Price 7d AUM Holders 30d
1
A
N
T
Mag Four Tilt MAGT
Pre-IPOClone
GbFK…ZUWS $1.00 +1.12% $120.1K 2
2
A
N
T
Defense & Space DFSP
Pre-IPO
7Ge1…kXSq $0.9791 −2.09% $29K 1

Flat only: a table row has no depth. Put the table in a Card (flat, or raised for a featured table) to give it a surface.

  • Sparkline is exported on its own.