Terminal
ProA command line for your app: a title bar, a monospace scrollback and a prompt with ghost completion and a block cursor. Presentational parts on the theme's tokens; your app runs the commands, so it fits a dev console, a search console or a REPL alike.
Installation#
Usage#
The parts paint; the loop is yours. A working shell is a list of lines, the text being typed, and a key handler that turns Enter into output.
The preview is yours to explore. Its code comes with a Pro license, paid once and used in every project.
Have a license? Unlock it here
Anatomy#
import {
Terminal,
TerminalBar,
TerminalCaption,
TerminalCaptionButton,
TerminalControls,
TerminalField,
TerminalIndicator,
TerminalInputLine,
TerminalLine,
TerminalPrompt,
TerminalScreen,
TerminalTab,
TerminalTitle,
TerminalTitleTag,
TerminalTray,
TerminalWatermark,
} from "@/components/ui/terminal"
<Terminal>
{/* A faint stamp behind the scrollback */}
<TerminalWatermark />
{/* The title bar, and the drag handle */}
<TerminalBar>
{/* A tab, or the indicator and title straight in the bar */}
<TerminalTab>
<TerminalIndicator />
<TerminalTitle>
<TerminalTitleTag />
</TerminalTitle>
</TerminalTab>
<TerminalControls />
{/* Minimize, maximize, close: flush in the corner */}
<TerminalCaption>
<TerminalCaptionButton action="minimize" />
</TerminalCaption>
</TerminalBar>
{/* The scrollback, a ScrollArea */}
<TerminalScreen>
<TerminalLine />
<TerminalInputLine>
<TerminalPrompt />
<TerminalField />
</TerminalInputLine>
</TerminalScreen>
{/* Live results, suggestions, a status line */}
<TerminalTray />
</Terminal>Line tones#
tone sets a line's ink. in is a command that was run, in the foreground ink; out (the default) and raw are output in the reading ink; dim is a comment in the meta grey; err and ok take the status hues' text step, which still reads on the light themes.
Clickable lines#
Give a line onClick and it becomes a button: focusable, run by a click, Enter or Space, with a highlight that keeps the text on its column. For search results, a file listing, or a #N the user would otherwise have to type.
Prompt, completion and cursor#
TerminalPrompt takes a prefix and a mark ("> " by default). TerminalField paints what is typed and a block cursor at the caret: it follows the arrow keys and sits over the character it is on. ghost is the grey completion after it; accepting it is your key handler's job (Tab or → here, at the end of the line). The cursor is a solid block while the field has focus and a hollow cell when it doesn't; drive cursorVisible from a timer to make it blink.
A blink that restarts lit on every keystroke, and holds still under reduced motion:
The preview is yours to explore. Its code comes with a Pro license, paid once and used in every project.
Have a license? Unlock it here
Tray#
TerminalTray is the band under the scrollback, on a quieter tint: live results while the user types, suggestions, a status line. It holds Koala components (a Badge, a Kbd, a Command list) or clickable TerminalLines, as here.
Window chrome#
TerminalBar carries the TerminalIndicator, a TerminalTitle (with an optional TerminalTitleTag), TerminalControls for your own 32px buttons, and TerminalCaption: Windows' minimize, maximize and close, 46px wide and flush in the corner, close going red on hover. What they do is yours: here they minimize, widen and close the window, and a double-click on the bar widens it too. movable gives the bar a grab cursor and dragging holds the grabbing one while your pointer handlers move the window. TerminalWatermark lays a faint stamp behind the scrollback.
API reference#
Terminal
The surface, a div: a Windows-style window (8px corners, popover fill, the lg shadow) every part reads its styles from. Every part is optional but the root. Forwards every div prop; every part merges className last. terminalVariants is the recipe.
| Prop | Type | Default | Description |
|---|---|---|---|
| movable | boolean | false | The bar shows a grab cursor. Moving the window is your pointer handler on TerminalBar. |
| dragging | boolean | false | The grabbing cursor, and no text selection while it lasts. |
| Data attribute | Description |
|---|---|
| data-slot | "terminal". |
| CSS variable | Description |
|---|---|
| --surface | Set to var(--popover), so controls nested inside paint the panel's ground. |
TerminalBar
The title bar, a div, and the drag handle: hang onPointerDown on it. Drop it for a console embedded in a page.
| Data attribute | Description |
|---|---|
| data-slot | "terminal-bar". |
TerminalTab
A tab in the title bar, a div, the way Windows Terminal draws one: put a TerminalIndicator and a TerminalTitle inside. The open tab merges into the screen below it.
| Prop | Type | Default | Description |
|---|---|---|---|
| active | boolean | true | The open tab: the screen's fill and a hairline frame. Inactive tabs sit back in the strip in muted ink. |
| Data attribute | Description |
|---|---|
| data-slot | "terminal-tab". |
| data-active | The active prop. |
TerminalIndicator
The live dot before the title, a decorative span (aria-hidden).
| Data attribute | Description |
|---|---|
| data-slot | "terminal-indicator". |
TerminalTitle
The title, a span that ends a long title in an ellipsis.
| Data attribute | Description |
|---|---|
| data-slot | "terminal-title". |
TerminalTitleTag
The title's secondary label, a span after a middle dot. Goes inside TerminalTitle.
| Data attribute | Description |
|---|---|
| data-slot | "terminal-title-tag". |
TerminalControls
The window buttons at the end of the bar, a div. Controls inside default to the sm tier (32px); an explicit size still wins.
| Data attribute | Description |
|---|---|
| data-slot | "terminal-controls". |
| data-control-size | "sm". |
TerminalCaption
The window buttons, a div flush in the bar's top-right corner: the window's corner clips the last button's fill. Goes last in TerminalBar, after TerminalControls if there are any.
| Data attribute | Description |
|---|---|
| data-slot | "terminal-caption". |
TerminalCaptionButton
A 46 by 40 window button, a button with the Windows hairline glyph and an aria-label from its action (override either). Wire onClick yourself: the window geometry is your app's.
| Prop | Type | Default | Description |
|---|---|---|---|
| action* | "minimize" | "maximize" | "restore" | "close" | - | Which button: its glyph and its label. close fills red on hover. The type is exported as TerminalCaptionAction. |
| children | React.ReactNode | - | Replaces the glyph. |
| Data attribute | Description |
|---|---|
| data-slot | "terminal-caption-button". |
| data-action | The action prop. |
TerminalScreen
The scrollback, on ScrollArea: takes its props but the axis (orientation and draggable are left out). Its content is a role="log", so assistive tech announces new lines.
| Prop | Type | Default | Description |
|---|---|---|---|
| fade | boolean | false | Fades the edge that still has content beyond it. |
| viewportRef | React.Ref<HTMLDivElement> | - | The scrolling element itself: use it to keep the scrollback at the bottom. |
| type | "auto" | "always" | "scroll" | "hover" | "hover" | When the scrollbars show, as on ScrollArea. |
| Data attribute | Description |
|---|---|
| data-slot | "terminal-screen", on the log inside the scroller. |
TerminalWatermark
A faint stamp behind the scrollback (a logo, a seal), a decorative img (aria-hidden, not draggable). Set its opacity with style. Forwards every img prop.
| Prop | Type | Default | Description |
|---|---|---|---|
| alt | string | "" | Empty by default: the image is decorative. |
| Data attribute | Description |
|---|---|
| data-slot | "terminal-watermark". |
TerminalLine
One line of the scrollback, a div that wraps long text. It needs no Terminal around it. terminalLineVariants({ tone, interactive, className }) returns a line's classes for your own markup.
| Prop | Type | Default | Description |
|---|---|---|---|
| tone | "in" | "out" | "err" | "ok" | "dim" | "raw" | "out" | The line's ink. The type is exported as TerminalTone. |
| onClick | (e: React.MouseEvent<HTMLDivElement> | React.KeyboardEvent<HTMLDivElement>) => void | - | Makes the line a role="button" in the tab order, run by click, Enter or Space. |
| Data attribute | Description |
|---|---|
| data-slot | "terminal-line". |
| data-tone | The line's tone. |
TerminalInputLine
The prompt row, a div: the prompt and the field on one baseline. Keep it last in the screen so the prompt scrolls with the output, the way a shell does.
| Data attribute | Description |
|---|---|
| data-slot | "terminal-input-line". |
TerminalPrompt
The prompt, a span: the prefix in full-weight ink, then the mark.
| Prop | Type | Default | Description |
|---|---|---|---|
| prefix* | React.ReactNode | - | What comes before the mark: the system, the host, the unit. |
| mark | React.ReactNode | "> " | The prompt mark. Keep its trailing space. |
| Data attribute | Description |
|---|---|
| data-slot | "terminal-prompt", with terminal-prompt-prefix and terminal-prompt-mark inside. |
TerminalField
The prompt's field: painted text with a block cursor at the caret, under a transparent <input> that takes the keyboard. Every input prop goes to that input, className and ref included; spellcheck, autocomplete, autocapitalize and autocorrect are off unless you turn them on.
| Prop | Type | Default | Description |
|---|---|---|---|
| value* | string | - | The controlled text. |
| ghost | string | - | The grey completion painted after the caret when it is at the end. Your key handler accepts it, usually on Tab. |
| cursorVisible | boolean | true | The blink beat while focused: drive it from a timer to blink, leave it true for a steady cursor. Out of focus the cursor is a still, hollow cell. |
| placeholder | string | - | Painted in the ghost's grey while the field is empty. |
| Data attribute | Description |
|---|---|
| data-slot | "terminal-field" on the wrapper, "terminal-input" on the input, "terminal-cursor" on the cursor. |
| data-visible | On the cursor: the cursorVisible beat. |
TerminalTray
The band under the scrollback, a div: live results, suggestions, a status line. Drop it when nothing runs live.
| Data attribute | Description |
|---|---|
| data-slot | "terminal-tray". |
