Patterns

Patterns

Overview

hudsonkit/patterns is an optional app-interior UI layer. It does not replace an app's visual system or shell slots. It gives apps a shared, high-quality vocabulary for dense navigation, object hierarchy, preview surfaces, and selection context.

Use it when an app wants Hudson's default rhythm. Ignore it when an app needs a fully custom surface.

Components

ComponentUse
HudRailSide navigation with optional search, grouped sections, selected rows, status badges, and footer content.
HudGroupedListSectioned lists with counts, empty states, selected rows, descriptions, badges, and trailing actions.
HudTreeRecursive disclosure tree with selected state, depth rails, badges, and trailing actions.
HudPreviewCardSingle previewable entity card with media/preview, status, metrics, footer, and actions.
HudCardGridResponsive grid for preview cards or app-rendered custom cards.
HudTilingUniversal space-filling tiler/grid for arbitrary items (terminals, chats, docs, etc.). Supports max cols/rows, fill %, gap, strategies, drag-to-reorder, for both fixed panels and canvas world spaces.
HudContextPanelRight-side selected-object summary with metadata rows and collapsible sections.

Import

import {
  HudRail,
  HudTree,
  HudPreviewCard,
  HudCardGrid,
  HudTiling,
  HudContextPanel,
} from 'hudsonkit/patterns';

Rail

<HudRail
  title="Assets"
  search={{ value: query, onChange: setQuery, placeholder: 'Search assets...' }}
  selectedId={selectedId}
  onSelect={(item) => setSelectedId(item.id)}
  sections={[
    {
      id: 'active',
      title: 'Active',
      count: activeAssets.length,
      items: activeAssets.map(asset => ({
        id: asset.id,
        title: asset.name,
        subtitle: asset.path,
        status: asset.kind,
        statusTone: 'accent',
      })),
    },
  ]}
/>

Tree

<HudTree
  nodes={assetTree}
  selectedId={selectedAssetId}
  defaultExpandedIds={['root']}
  onSelect={(node) => setSelectedAssetId(node.id)}
/>

HudTree owns disclosure state by default. Pass expandedIds and onExpandedChange when an app needs controlled expansion.

Grouped List

<HudGroupedList
  groups={[
    { id: 'working', title: 'Working', count: working.length, tone: 'success', items: working },
    { id: 'done', title: 'Done', count: done.length, items: done },
  ]}
  itemKey={(item) => item.id}
  selectedKey={selectedId}
  onSelect={setSelectedItem}
  renderTitle={(item) => item.title}
  renderDescription={(item) => item.summary}
/>

Cards

<HudCardGrid
  items={shots}
  itemKey={(shot) => shot.id}
  selectedKey={selectedShotId}
  onSelect={(shot) => setSelectedShotId(shot.id)}
  minCardWidth={220}
  getCardProps={(shot) => ({
    title: shot.name,
    subtitle: shot.description,
    media: <img src={shot.thumbnailUrl} alt="" className="h-full w-full object-cover" />,
    status: shot.status,
    metrics: [
      { label: 'Frames', value: shot.frames },
      { label: 'Assets', value: shot.assetCount },
    ],
  })}
/>

Context Panel

<HudContextPanel
  title={asset.name}
  subtitle={asset.path}
  status={asset.status}
  rows={[
    { label: 'Kind', value: asset.kind },
    { label: 'Size', value: asset.sizeLabel },
  ]}
  sections={[
    {
      id: 'metadata',
      title: 'Metadata',
      rows: metadataRows,
    },
  ]}
/>

Boundary

Patterns are deliberately optional and slot-heavy:

  • Apps own data meaning, hierarchy, and domain actions.
  • Hudson owns consistent density, selected state, disclosure behavior, focus styling, grouping rhythm, and token usage.
  • Workspace, canvas, map, and saved workspace behavior remain in their current layers. hudsonkit/patterns only covers app interiors.
For AI agents