Components · Data Display
List
A vertical list of uniform rows — avatar + title/description meta and trailing actions, with size, bordered and split options.
Examples
Size
Bordered
Split
List · bordered · avatar + title/description meta · trailing actions · row hover
<!-- Paste-and-run: save as .html and open in a browser. No build step.
<aha-list> is the SAME shared custom element React and Vue consume — here in its
native form. Theming comes only from the --aha-* tokens in tokens.css. -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/ahaslides-product/ahaslides-design@master/lib/tokens.css">
<script type="module">
import 'https://cdn.jsdelivr.net/gh/ahaslides-product/ahaslides-design@master/lib/icons.js'; // <aha-icon>
import 'https://cdn.jsdelivr.net/gh/ahaslides-product/ahaslides-design@master/lib/aha-list.js'; // <aha-list>
</script>
<!-- Structured rows via `items`: avatar + title/description meta + trailing actions.
size = small | default | large · bordered = outer border · split = row dividers (default on). -->
<aha-list bordered list-title="Recent decks" footer="3 of 24 decks" style="max-width:360px"
items='[
{"title":"Team offsite 2026","description":"12 slides · edited 2h ago","avatar":"system-user-circle",
"actions":[{"key":"edit","icon":"system-pencil-simple","label":"Edit"},{"key":"delete","icon":"system-trash","danger":true,"label":"Delete"}]},
{"title":"Q3 product review","description":"28 slides · edited yesterday","avatar":"system-user-circle",
"actions":[{"key":"edit","icon":"system-pencil-simple","label":"Edit"},{"key":"delete","icon":"system-trash","danger":true,"label":"Delete"}]}
]'></aha-list>
<!-- …or the free-form escape hatch: any light-DOM child becomes one plain row. -->
<aha-list bordered list-title="Simple rows" style="max-width:360px;margin-top:16px">
<div>Polls</div>
<div>Quizzes</div>
<div>Word cloud</div>
</aha-list>
import '@ahaslides-product/design/aha-list'; // registers <aha-list>
import '@ahaslides-product/design/icons'; // registers <aha-icon>
// Structured rows: pass `items` (JSON) for avatar + title/description + trailing actions.
// size = small | default | large · bordered = outer border · split = row dividers (default on).
function DeckList({ title, decks }) {
return (
<aha-list
bordered
list-title={title}
footer={`${decks.length} decks`}
items={JSON.stringify(
decks.map((d) => ({
title: d.name,
description: d.meta,
avatar: 'system-user-circle',
actions: [
{ key: 'edit', icon: 'system-pencil-simple', label: 'Edit' },
{ key: 'delete', icon: 'system-trash', danger: true, label: 'Delete' },
],
}))
)}
ref={(el) => el && (el.onaction = (e) => console.log(e.detail))} // { key, index }
/>
);
}
// usage
<DeckList title="Recent decks" decks={[{ name: 'Team offsite 2026', meta: '12 slides · edited 2h ago' }]} />
// main.ts — register the elements + mark aha-* as custom elements
import '@ahaslides-product/design/aha-list';
import '@ahaslides-product/design/icons'; // registers <aha-icon>
app.config.compilerOptions.isCustomElement = (tag) => tag.startsWith('aha-');
// Component.vue
<script setup>
import { ref, computed } from 'vue';
// size = small | default | large · bordered = outer border · split = row dividers (default on).
const decks = ref([
{ name: 'Team offsite 2026', meta: '12 slides · edited 2h ago' },
{ name: 'Q3 product review', meta: '28 slides · edited yesterday' },
]);
const items = computed(() => JSON.stringify(decks.value.map((d) => ({
title: d.name, description: d.meta, avatar: 'system-user-circle',
actions: [
{ key: 'edit', icon: 'system-pencil-simple', label: 'Edit' },
{ key: 'delete', icon: 'system-trash', danger: true, label: 'Delete' },
],
}))));
const onAction = (e) => console.log(e.detail); // { key, index }
</script>
<template>
<aha-list bordered list-title="Recent decks" :footer="`${decks.length} decks`"
:items="items" @action="onAction" />
</template>
API
| Prop | Type | Default | Notes |
|---|---|---|---|
items | JSON array | [] | Structured rows: { title, description?, avatar? (icon name), actions?:[{ key, icon, danger?, label? }] } |
(children) | slot | — | Free-form fallback — each light-DOM child renders as one plain row (used when `items` is empty) |
size | 'small' | 'default' | 'large' | default | Row vertical padding scale (8 · 12 · 16) |
bordered | boolean | false | Draw the outer container border |
split | boolean | true | Row dividers; set split="false" to remove them |
list-title | string | — | Renders an optional header row |
footer | string | — | Renders an optional footer row |
loading | boolean | false | Show shimmer skeleton rows in place of content |
action | CustomEvent | — | Composed event on a trailing action click, detail { key, index } |
Install
# .npmrc — once: point the @ahaslides-product scope at GitHub Packages
@ahaslides-product:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN} # a GitHub token with read:packages
npm i @ahaslides-product/design
import '@ahaslides-product/design/tokens.css'; // once, at the app root
import '@ahaslides-product/design/aha-list'; // registers <aha-list>
Agent feed for this component (absolute, fetchable anywhere): list.agent.json · list.md · list.llms.txt
When to use
When to use
- List — many uniform rows of the same shape (recent items, participants, feeds)
- Table — multi-column records that need sorting or alignment
- Card — one titled group of mixed content
Keep rows to one shape — a title, optionally a description and a small trailing actions cluster. Reserve `bordered` for a standalone list; drop the border when the list sits inside a card. For dense multi-column data reach for the Table instead.
Surfaces
editor dashboard settings audience
Spec
Container radius 12px · bg #FFFFFF · border 1px #E3E3E3 only when `bordered` · Header padding 14/20 · 15/22 SemiBold #1A1A1A · divider #F1F1F1 · Row padding 12/20 (small 8, large 16) · 14/22 #4A4A4A · avatar 36 + meta + trailing actions · Avatar 36px pill, bg-accent #F9F5FF fill, color-primary #6A1EBB glyph · Meta title 600 #1A1A1A + description 13/20 #8A8A8A, both ellipsised · Action 28px icon button, text-tertiary #8A8A8A; danger hover error-tinted · Split row dividers #F1F1F1, on by default; split=false drops them · Row hover interactive rows → bg #F7F7F7, motion-fast ease-out (persistent node) · Footer padding 12/20 · 13/20 #8A8A8A · top divider