Recent activity
3What happened in the last seven days
Hoshimi Shion bookmarked Inherit the Stars · 10 minutes ago
Peek commented on your review · 1 hour ago
Glass followed you · yesterday
<script setup lang="ts">
import { Activity } from '@lucide/vue'
import { Button, Panel, Stack, Text } from '@hina-ui/vue'
const events = [
{ who: 'Hoshimi Shion', what: ' bookmarked Inherit the Stars', when: '10 minutes ago' },
{ who: 'Peek', what: ' commented on your review', when: '1 hour ago' },
{ who: 'Glass', what: ' followed you', when: 'yesterday' },
]
</script>
<template>
<Panel
title="Recent activity"
description="What happened in the last seven days"
:count="events.length"
class="w-96"
>
<template #icon><Activity /></template>
<template #actions>
<Button size="sm" variant="ghost" tone="neutral">All</Button>
</template>
<Stack gap="sm">
<Text v-for="event in events" :key="event.what" size="sm">
{{ event.who }}{{ event.what }}
<Text as="span" tone="muted" size="sm">· {{ event.when }}</Text>
</Text>
</Stack>
</Panel>
</template>
Usage
import { Panel } from '@hina-ui/vue'
A panel is a Card with a standard title bar: title is the title, description a line under it, count a number next to the title, the icon slot an icon before the title, the actions slot the actions at the end of the title row, and the default slot the body. Panels on the same page therefore all look alike, with no title row to hand-write each time.
Account security
Last sign-in: today at 09:12, from Hangzhou.
<script setup lang="ts">
import { Panel, Text } from '@hina-ui/vue'
</script>
<template>
<Panel title="Account security" class="w-96">
<Text>Last sign-in: today at 09:12, from Hangzhou.</Text>
</Panel>
</template>
Examples
Actions
The actions slot usually holds one or two small buttons, or a link.
Email notifications
Send an email for new replies, followers and direct messages.
<script setup lang="ts">
import { Button, Panel, Text } from '@hina-ui/vue'
</script>
<template>
<Panel title="Email notifications" class="w-96">
<template #actions>
<Button size="sm" variant="outline" tone="neutral">Send a test</Button>
<Button size="sm">Save</Button>
</template>
<Text>Send an email for new replies, followers and direct messages.</Text>
</Panel>
</template>
Icon and count
The icon from the icon slot shows before the title and count after it; both add information to the title without changing its level.
Comments
128Newest first.
<script setup lang="ts">
import { MessageSquare } from '@lucide/vue'
import { Panel, Text } from '@hina-ui/vue'
</script>
<template>
<Panel title="Comments" :count="128" class="w-96">
<template #icon><MessageSquare /></template>
<Text>Newest first.</Text>
</Panel>
</template>
Description
description shows under the title and says in a sentence what this block is.
Public profile
This information shows on your profile page and is visible to everyone.
<script setup lang="ts">
import { Panel, Switch } from '@hina-ui/vue'
</script>
<template>
<Panel
title="Public profile"
description="This information shows on your profile page and is visible to everyone."
class="w-96"
>
<Switch>Show my bookmarks</Switch>
</Panel>
</template>
Body without padding
With padded set to false the body has no padding, so lists and tables that carry their own row spacing can reach the edge of the card.
Members
3Hoshimi Shion
Admin
Peek
Member
Glass
Member
<script setup lang="ts">
import { Avatar, Inline, Panel, Stack, Text } from '@hina-ui/vue'
const members = [
{ name: 'Hoshimi Shion', src: '/avatars/selfie.webp', role: 'Admin' },
{ name: 'Peek', src: '/avatars/peek.webp', role: 'Member' },
{ name: 'Glass', src: '/avatars/glass.webp', role: 'Member' },
]
</script>
<template>
<Panel title="Members" :count="members.length" :padded="false" class="w-96">
<Stack gap="none" align="stretch" class="divide-line divide-y">
<Inline
v-for="member in members"
:key="member.name"
gap="sm"
align="center"
class="px-(--hn-panel-p) py-2.5"
>
<Avatar :src="member.src" :name="member.name" size="sm" />
<Text size="sm">{{ member.name }}</Text>
<Text size="sm" tone="muted">{{ member.role }}</Text>
</Inline>
</Stack>
</Panel>
</template>
Heading level
level decides whether the title renders as h2, h3 or h4, h2 by default; a page has a single h1, so panel titles start at level two and step down one level when nested in another block.
Settings
Notifications
This panel sits under "Settings", so its title steps down to level three.
Privacy
Panels on the same level share the same heading level.
<script setup lang="ts">
import { Heading, Panel, Stack, Text } from '@hina-ui/vue'
</script>
<template>
<Stack gap="md" class="w-96">
<Heading :level="2" size="lg">Settings</Heading>
<Panel title="Notifications" :level="3">
<Text>This panel sits under "Settings", so its title steps down to level three.</Text>
</Panel>
<Panel title="Privacy" :level="3">
<Text>Panels on the same level share the same heading level.</Text>
</Panel>
</Stack>
</template>
Accessibility
- The title is a real heading element whose level comes from
level, so assistive technology can jump between headings. - The icon is decorative and hidden from assistive technology; the count is rendered as text.
API
Props
| Prop | Type | Default | Description |
|---|---|---|---|
title | string | — | Required. The title |
description | string | — | Line under the title |
count | number | — | Count next to the title |
level | 2 | 3 | 4 | 2 | Heading level of the title |
padded | boolean | true | Whether the body has padding |
class | string | — | Classes appended to the card |
Slots
| Slot | Description |
|---|---|
| default | The body |
icon | Icon before the title |
title | Title content |
description | Description content |
actions | Actions at the end of the title row |