Panel
Panels are the framed surfaces of the HUD: a translucent glass body with corner brackets, side rails, a light trace and a header strip. They hold the content of a dashboard tile or a window.
import { Panel } from 'jarvis-react-ui';
Basic
A panel is as wide as its container and, without a set height, grows with its content. Size it
with className (a class from your own CSS, or Tailwind utilities if your project uses Tailwind)
or with style. With a fixed height the body scrolls when its content overflows. The body has 12px
padding, so content such as Tabs needs no extra spacing.
import { Panel } from 'jarvis-react-ui';
export default function Basic() {
return (
<Panel title="Diagnostics" className="w-[280px] h-[120px]">
<span className="text-text-secondary">All subsystems nominal.</span>
</Panel>
);
}
Header
The header shows an optional index glyph (ix), the title, three status dots, optional actions
and an optional badge, in that order. Use IconButton for actions.
import { RefreshCw, Settings } from 'lucide-react';
import { IconButton, Metric, Panel } from 'jarvis-react-ui';
export default function Header() {
return (
<Panel
ix="◈"
title="System Vitals"
badge="LIVE"
actions={
<>
<IconButton icon={RefreshCw} label="Refresh" size="sm" />
<IconButton icon={Settings} label="Settings" size="sm" />
</>
}
className="w-[316px] h-[150px]"
>
<div className="grid grid-cols-2 gap-3">
<Metric value={42} unit="%" />
<Metric value={68} unit="°C" warn />
</div>
</Panel>
);
}
Focus
focused switches the border to the accent color and turns on an animated bloom. The panel does not
track focus itself: onFocus fires on mouse down anywhere inside it, so keep the focused id in state
and pass it back.
import { useState } from 'react';
import { Panel } from 'jarvis-react-ui';
const PANELS = ['Transcript', 'Agenda', 'Now Playing'];
export default function Focus() {
const [focused, setFocused] = useState('Transcript');
return (
<>
{PANELS.map((title) => (
<Panel
key={title}
title={title}
focused={focused === title}
onFocus={() => setFocused(title)}
className="w-[200px] h-[110px]"
>
<span className="text-text-secondary">
{focused === title ? 'Focused' : 'Click to focus'}
</span>
</Panel>
))}
</>
);
}
Note: Building windows
For draggable, slot-docked panels use WindowManager. Its windows are Panels wired through
headerRef,onHeaderPointerDownandonHeaderClickfor dragging and double-click docking. You rarely need these three props yourself.
Sound
Hovering a panel plays hover_panel through the nearest SfxProvider. Without a provider the panel
stays silent. See Sound effects.
Accessibility
- The panel is a presentational
<div>without a landmark role. Wrap it in a<section>with anaria-labelwhen the panel is a distinct region of the page. focusedis purely visual and does not move keyboard focus.- Header
actionsshould be real buttons with accessible names, such asIconButtonwithlabel.
API
| Prop | Type | Default | Description |
|---|---|---|---|
actions |
ReactNode |
Controls rendered in the header between the dots and the badge, e.g. icon buttons. | |
badge |
ReactNode |
Tag shown at the right end of the header, e.g. LIVE. |
|
children |
ReactNode |
Panel body content. | |
className |
string |
Additional class names for the root element, e.g. width and height utilities. | |
focused |
boolean |
false |
Highlights the panel: accent border and a stronger, animated bloom. |
headerRef |
Ref<HTMLDivElement> |
Ref to the header element, e.g. to measure it or attach a drag handle. | |
ix |
ReactNode |
Index glyph shown before the title in the header, e.g. ◈. |
|
onFocus |
(() => void) |
Called on mouse down anywhere in the panel; use it to set focused. |
|
onHeaderClick |
((e: MouseEvent<HTMLDivElement, MouseEvent>) => void) |
Click handler on the header; used for double-click detection by Window. |
|
onHeaderPointerDown |
PointerEventHandler<HTMLDivElement> |
Pointer-down handler on the header; used as a drag handle by Window. |
|
style |
CSSProperties |
Inline style for the root element, e.g. { width: 320, height: 220 }. |
|
title |
ReactNode |
Header title. |