Appearance
UI Components (shadcn)
Purpose
Katalyst’s shared UI primitives live in src/shared/ui/primitives/ and follow a shadcn-style approach:
- Source-controlled components (not a black-box library)
- Tailwind-based styling with consistent tokens
- Built on proven foundations (Radix UI, cmdk, etc.)
These primitives are the building blocks for higher-level shared components like ConfirmDialog and CommandPalette.
When to use
Use shadcn primitives when:
- You need a consistent baseline UI (buttons, dialogs, inputs, menus)
- You want accessible behavior without re-implementing it
- You need to customize styling locally while keeping a shared foundation
Example
tsx
import { Button } from '@/shared/ui/primitives/button'
import { Dialog, DialogContent, DialogTrigger } from '@/shared/ui/primitives/dialog'
export function ExampleDialog() {
return (
<Dialog>
<DialogTrigger asChild>
<Button type="button">Open</Button>
</DialogTrigger>
<DialogContent>
<p>Dialog content</p>
</DialogContent>
</Dialog>
)
}Props (concise)
This section is intentionally high-level:
- Most primitives accept the underlying Radix/cmdk component props (plus
className). - Many primitives expose “variants” using
class-variance-authority(e.g.,buttonVariants). - Styling composition uses
cn(...)fromsrc/lib/utils.ts(Tailwind merge + conditional classes).
Notes / Gotchas
- Don’t treat them as vendor code: these primitives are part of your codebase; prefer consistent local improvements over duplicating “one-off” UI.
- Use
cn(...)for class composition: it avoids Tailwind class conflicts and keeps variants predictable. - Prefer variants over ad hoc styling: when a style needs to be reused, add a variant (or a wrapper component) rather than sprinkling custom class strings.
- Extend safely:
- Add new primitives in
src/shared/ui/primitives/following existing patterns (data-slotattributes,cn, Radix wrappers). - Build feature-specific UI as wrappers/components on top of primitives, instead of forking primitives in feature folders.
- Add new primitives in
- Keep UI separate from logic: primitives/components should stay thin; domain behavior should live in hooks/services.