Panel

A card with a standard title bar, with places for a title, a count and actions.

Recent activity

3

What 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>
vue

Usage

import { Panel } from '@hina-ui/vue'
ts

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>
vue

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>
vue

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

128

Newest 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>
vue

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>
vue

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

3
Hoshimi Shion

Hoshimi Shion

Admin

Peek

Peek

Member

Glass

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>
vue

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>
vue

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