Skip to content
  1. Keyline UI
  2. Panel
Keyline UIComponents
  • Get started
    • Installation
    • Theming
    • Modes
    • Configuration
    • Composition
  • Actions
    • Button
    • Button group
    • Split button
    • Toggle
    • Toggle group
    • Toolbar
    • Menu
    • Menubar
    • Context menu
  • Inputs
    • Field and form
    • Checkbox
    • Checkbox group
    • Radio group
    • Switch
    • Slider
    • Segmented
    • Select
    • Native select
    • Combobox
    • Multiselect
    • Number field
    • OTP field
    • Time field
    • Date picker and calendar
    • Date range
  • Data
    • Table
    • Chart
    • Stat and meter
    • Status and badge
    • Progress
    • Skeleton
    • Empty
    • Avatar
    • Kbd
    • Marker
  • Navigation
    • Tabs
    • Breadcrumb
    • Pagination
    • Nav
    • Navigation menu
    • Command menu
  • Overlays
    • Dialog
    • Alert dialog
    • Drawer
    • Sheet
    • Popover
    • Hover card
    • Tooltip
    • Toast
    • Notice
  • Layout
    • App shell
    • Sidebar
    • Panel
    • Resizable
    • Scroll area
    • Accordion
    • Collapsible
    • Carousel
    • Inspector
    • Item
    • Direction
  • Conversation
    • Message
    • Bubble
    • Message scroller
    • Attachment
    • Questionnaire
  • Blocks
    • Shipments

Panel

A region of a screen in a 3px frame. Its header, body and footer are cells stacked on shared keylines, and the footer's actions are cells too. Standalone panels sit one gutter apart.

Loading the live example…

"use client"

import { useState } from "react"
import { Button } from "@/components/keyline/ui/button"
import { Meter } from "@/components/keyline/ui/stat"
import { Panel, PanelBody, PanelFooter, PanelHeader } from "@/components/keyline/ui/panel"

const REPS = [
  { name: "Dwight Schrute", reams: 1_680, target: 1_500 },
  { name: "Jim Halpert", reams: 1_240, target: 1_500 },
  { name: "Phyllis Vance", reams: 1_120, target: 1_200 },
  { name: "Stanley Hudson", reams: 910, target: 1_200 },
]

export function SalesTargets() {
  const [sent, setSent] = useState(false)
  return (
    <Panel>
      <PanelHeader code="Q4" title="Sales targets" description="Reams sold against each rep's target, this quarter." />
      <PanelBody className="grid gap-4">
        {REPS.map((r) => (
          <Meter key={r.name} label={r.name} value={r.reams} max={r.target} />
        ))}
      </PanelBody>
      <PanelFooter>
        <span className="flex flex-1 items-center px-3 text-[13px] text-kl-ink-2" role="status">
          {sent ? "Sent to Jan." : "Updated at 9:00"}
        </span>
        <Button layout="cell" variant="primary" disabled={sent} onClick={() => setSent(true)}>
          Send to corporate
        </Button>
      </PanelFooter>
    </Panel>
  )
}

Installation

npx shadcn@latest add @keyline-ui/panel

Run it where your components.json is. The first time, map the @keyline-ui registry: one command, in Installation.

Usage

import { Panel, PanelBody, PanelFooter, PanelGrid, PanelHeader } from "@/components/keyline/ui/panel"
<Panel>
  <PanelHeader code="Q4" title="Sales targets" />
  <PanelBody>…</PanelBody>
  <PanelFooter>
    <Button layout="cell" variant="primary">Send to corporate</Button>
  </PanelFooter>
</Panel>

Examples

One frame for a board

Inside PanelGrid, panels with layout="cell" become cells of one frame, so a dashboard reads as one instrument. The seams stay right however the grid wraps; min sets the narrowest cell.

Loading the live example…

"use client"

import { Panel, PanelBody, PanelGrid, PanelHeader } from "@/components/keyline/ui/panel"
import { Status } from "@/components/keyline/ui/status"

export function BranchBoard() {
  return (
    <PanelGrid min={200}>
      <Panel layout="cell">
        <PanelHeader headingLevel={3} title="Sales this week" />
        <PanelBody>
          <p className="m-0 text-[28px] leading-none font-semibold tabular-nums">1,810 reams</p>
        </PanelBody>
      </Panel>
      <Panel layout="cell">
        <PanelHeader headingLevel={3} title="Deliveries" />
        <PanelBody className="grid gap-2 text-[14px]">
          <Status kind="done">14 delivered</Status>
          <Status kind="attention">1 held at Vance Refrigeration</Status>
        </PanelBody>
      </Panel>
      <Panel layout="cell">
        <PanelHeader headingLevel={3} title="Paper stock" />
        <PanelBody>
          <p className="m-0 text-[14px]">30 cases of letter. Reorder by Friday.</p>
        </PanelBody>
      </Panel>
    </PanelGrid>
  )
}

Props

PropTypeDefaultDescription
layout"control" | "cell"Default "control"On Panel: cell drops its frame to sit in a PanelGrid or another frame.
title · code · description · actionsReactNodeDefault —On PanelHeader: code sits before the title; actions are cells at the end.
headingLevel2 | 3 | 4Default 2On PanelHeader: the title's heading level.
flushbooleanDefault falseOn PanelBody: no padding, for lists and tables that bring their own keylines.
minnumberDefault 240On PanelGrid: the narrowest a cell gets before the grid wraps, in pixels.

Resizable, Scroll area and Item pages put their content in a Panel body with flush.

Sheet 59 of 73PreviousSidebarNextResizable
On this page