Component Library
Polished. Accessible. Keyboard-navigable. Conversation UI, agentic primitives, sidebars, dialogs — all included, all composable.
The problem
Custom components reinvent forms, dialogs, accessibility. Every time.
Every team writes their own select, dialog, dropdown, and tooltip — badly. Focus management is hard. Keyboard navigation is hard. Screen-reader semantics are hard. Most custom components fail accessibility audits and feel janky to power users.
The teams that use a real component library save the time. The teams that don't spend the year and ship something worse.
How it works
Radix UI primitives + Tailwind + composable agentic UI components.
Sage ships with Radix UI primitives for the hard parts — focus traps, ARIA roles, keyboard event handling, scroll containment. Tailwind handles styling with utility classes mapped to the brand design tokens.
On top of Radix, Sage ships agentic UI primitives: a chat thread component with streaming, a message bubble that opens its trace in Grafana on click, a composer with attachment + voice support, a tool-call card for visualizing agent actions, and a contextual AI sidebar pattern that fits every page.
- Radix UI for focus management, ARIA, keyboard nav — the parts that are hardest to get right
- Tailwind for styling — utility-first, design tokens from the brand system
- Pre-built agentic primitives: ChatThread, MessageBubble, ComposerInput, ToolCallCard
- Sidebar + contextual AI panel patterns — keyboard shortcuts, persistent state
- Theming hooks for tenant-specific brand voice + colors (WorldviewPack-compatible)
- Storybook + visual regression tests scaffold-installed; no design-system rot
// web/components/conversation/ChatThread.tsx — example agentic UI primitive
import * as ScrollArea from "@radix-ui/react-scroll-area";
import { useConversation } from "@/hooks/use-conversation";
import { MessageBubble } from "./MessageBubble";
import { ComposerInput } from "./ComposerInput";
export function ChatThread({ sessionId }: { sessionId: string }) {
const { messages, send, streaming } = useConversation(sessionId);
return (
<div className="flex h-full flex-col">
<ScrollArea.Root className="flex-1 overflow-hidden">
<ScrollArea.Viewport className="h-full w-full">
{messages.map((m) => (
<MessageBubble
key={m.id}
role={m.role}
content={m.content}
traceId={m.traceId} // click to open trace in Grafana
/>
))}
</ScrollArea.Viewport>
<ScrollArea.Scrollbar orientation="vertical" />
</ScrollArea.Root>
<ComposerInput onSubmit={send} disabled={streaming} />
</div>
);
}
// All components are keyboard-navigable, screen-reader friendly,
// and use Radix primitives for focus management + ARIA semantics. Get Started
Stop reinventing forms and dialogs.
Radix + Tailwind + agentic UI primitives, all keyboard-navigable and screen-reader friendly. The polish that takes a year to get right — already done.