HUDShell
HUDShell is the full-screen frame of a JARVIS app. It draws the complete backdrop and gives you slots for the top bar, the orb, your panels and the voice dock, layered in the right order. Start every app with it.
import { HUDShell } from 'jarvis-react-ui';
Starting an app
Wrap the app in JarvisProvider for sound and toasts, then render HUDShell as its root element and fill the slots.
import 'jarvis-react-ui/style.css';
import { createRoot } from 'react-dom/client';
import { JarvisProvider } from 'jarvis-react-ui';
import { App } from './App'; // renders <HUDShell ...> like the demo below
createRoot(document.getElementById('root') as HTMLElement).render(
<JarvisProvider>
<App />
</JarvisProvider>,
);
The demo shows a typical App: a TopBar with a
StatusBadge, a CssOrb, two panels and a
StatusDock that share one AppOrbState. Click the microphone to switch
between idle and listening. The demo shrinks the orb with scale-[0.6] to fit the frame; drop
that class in a full-screen app.
import { useState } from 'react';
import {
BrandMark,
CssOrb,
HUDShell,
Metric,
Panel,
StatusBadge,
StatusDock,
TopBar,
} from 'jarvis-react-ui';
import type { AppOrbState } from 'jarvis-react-ui';
export default function AppShell() {
const [state, setState] = useState<AppOrbState>('idle');
return (
<HUDShell
topbar={<TopBar left={<BrandMark />} right={<StatusBadge />} />}
orb={<CssOrb state={state} particles={false} className="scale-[0.6]" />}
dock={
<StatusDock
state={state}
onPTT={() => setState((s) => (s === 'idle' ? 'listening' : 'idle'))}
/>
}
>
<div className="flex justify-between px-5 pt-16">
<Panel title="Vitals" className="w-[180px]">
<Metric value={42} unit="%" />
</Panel>
<Panel title="Agenda" className="w-[180px]">
<span className="text-text-secondary">10:30 · Briefing</span>
</Panel>
</div>
</HUDShell>
);
}
Layers
HUDShell is a position: fixed; inset: 0 element. From back to front it renders:
| Layer | Rendered | Toggle |
|---|---|---|
| Scene background | always | scene (grid, stars, scanlines) |
| Reactor glow | by default | reactor={false} |
| ViewportCorners | by default | viewportCorners={false} |
orb slot, full-screen and click-through (z 0) |
when set | — |
children in a relative layer (z 10) |
when set | — |
dock slot, rendered as is (StatusDock is z 25) |
when set | — |
topbar slot (z 30) |
when set | — |
The children layer starts at the top of the screen, spans its width and is only as tall as its
content. Lay out your panels inside it, or use WindowManager for draggable windows.
import { HUDShell, Panel } from 'jarvis-react-ui';
export default function Basic() {
return (
<HUDShell>
<div className="p-6">
<Panel title="Diagnostics" className="h-[120px] w-[280px]">
<span className="text-text-secondary">All subsystems nominal.</span>
</Panel>
</div>
</HUDShell>
);
}
scene switches off single Scene layers; the dark background, vignette, grain and horizon glow
always stay. reactor and viewportCorners remove the glow and the corner marks.
import { HUDShell, Panel } from 'jarvis-react-ui';
export default function Layers() {
return (
<HUDShell
scene={{ stars: false, scanlines: false }}
reactor={false}
viewportCorners={false}
>
<div className="p-6">
<Panel title="Minimal" className="h-[120px] w-[280px]">
<span className="text-text-secondary">Grid only, no reactor or corners.</span>
</Panel>
</div>
</HUDShell>
);
}
Idle mode
idle dims and blurs every Panel inside children and fades the top bar, so
only the orb remains in focus. The children layer becomes inert while idle: it is
click-through, its controls leave the tab order and it is hidden from assistive technology. Entering
and leaving idle both crossfade (instantly under prefers-reduced-motion). Use it when the
assistant has been inactive for a while. The top bar stays interactive, which is where this demo
puts the toggle.
import { useState } from 'react';
import { BrandMark, Button, HUDShell, Panel, TopBar } from 'jarvis-react-ui';
export default function Idle() {
const [idle, setIdle] = useState(false);
return (
<HUDShell
idle={idle}
topbar={
<TopBar
left={<BrandMark />}
right={
<Button size="sm" variant="ghost" onClick={() => setIdle((v) => !v)}>
{idle ? 'Wake' : 'Idle'}
</Button>
}
/>
}
>
<div className="flex justify-center px-5 pt-20">
<Panel title="Transcript" className="h-[140px] w-[300px]">
<span className="text-text-secondary">
{idle ? 'Panels are dimmed and click-through.' : 'Awaiting input.'}
</span>
</Panel>
</div>
</HUDShell>
);
}
Working state
Set working while a tool call runs. HUDShell adds the is-working class to its root, which draws
an accent light trace sweeping along the top edge and a soft accent vignette breathing at the
screen edges. Under prefers-reduced-motion both stay still: the trace becomes a static hairline
across the top. The amber working look of the orb still comes from passing state="working" to
the orb itself; you can target .hud-shell.is-working for further styles of your own.
import { useState } from 'react';
import { BrandMark, Button, HUDShell, Panel, TopBar } from 'jarvis-react-ui';
export default function Working() {
const [working, setWorking] = useState(true);
return (
<HUDShell
working={working}
topbar={
<TopBar
left={<BrandMark />}
right={
<Button size="sm" variant="ghost" onClick={() => setWorking((v) => !v)}>
{working ? 'Done' : 'Run tool'}
</Button>
}
/>
}
>
<div className="flex justify-center px-5 pt-20">
<Panel title="Tool call" className="h-[140px] w-[300px]">
<span className="text-text-secondary">
{working ? 'Fetching weather data...' : 'Idle.'}
</span>
</Panel>
</div>
</HUDShell>
);
}
Accessibility
- HUDShell renders plain
<div>s without landmark roles. Give your content its own structure, for example a<main>around the panels and a<nav>in the top bar. - The Scene, Reactor and ViewportCorners layers are
aria-hiddenand ignore pointer events; the orb layer ignores pointer events as well. - Idle mode sets
inerton thechildrenlayer, so the dimmed panels cannot be clicked or focused and are hidden from assistive technology untilidleisfalseagain. The top bar, orb and dock slots stay interactive. - The working effect is decorative pseudo-element chrome and is not announced; pair it with a visible or live status (for example StatusDock).
API
| Prop | Type | Default | Description |
|---|---|---|---|
children |
ReactNode |
Panels and windows; rendered in a relative layer above the orb (z-index 10). | |
className |
string |
Additional class names for the fixed, full-screen root <div>. |
|
dock |
ReactNode |
Dock slot, typically a StatusDock; rendered as is, so it keeps its own fixed position. | |
idle |
boolean |
false |
Idle mode: dims and blurs the panels in children, makes them inert (click-through and removed from the tab order and accessibility tree) and fades the top bar. Entering and leaving idle both crossfade. |
orb |
ReactNode |
Orb slot, typically a CssOrb or ThreeOrb; rendered in a full-screen, click-through layer behind the panels. | |
reactor |
boolean |
true |
Renders the Reactor glow at the bottom edge. |
scene |
{ grid?: boolean; stars?: boolean; scanlines?: boolean | undefined; } | undefined |
Optional layers of the background Scene; each one is on unless set to false. |
|
style |
CSSProperties |
Inline styles for the root <div>; use it to inject CSS custom properties. |
|
topbar |
ReactNode |
Top bar slot, typically a TopBar; rendered above everything else (z-index 30). | |
viewportCorners |
boolean |
true |
Renders the ViewportCorners marks in the four corners. |
working |
boolean |
false |
Working mode for tool-call activity: adds the is-working class to the root, which draws an accent light trace sweeping along the top edge and a soft breathing accent vignette at the screen edges (static under prefers-reduced-motion). |