diff --git a/.changeset/linear-collection-views.md b/.changeset/linear-collection-views.md
new file mode 100644
index 0000000..4124c04
--- /dev/null
+++ b/.changeset/linear-collection-views.md
@@ -0,0 +1,6 @@
+---
+"@karnstack/dowel": minor
+---
+
+Add collection view controls, advanced DataTable state and interactions, a
+TanStack Form adapter, and a darker dark-mode badge surface.
diff --git a/.gitignore b/.gitignore
index cf06758..8719d87 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,6 +6,7 @@ dist/
# TanStack Start's scratch directory: the SSR bundle it builds only to render
# the prerendered HTML, plus the route generator's temp files.
.tanstack/
+.wrangler/
test-results/
playwright-report/
blob-report/
diff --git a/apps/docs/package.json b/apps/docs/package.json
index 15e1cea..ca99eae 100644
--- a/apps/docs/package.json
+++ b/apps/docs/package.json
@@ -4,7 +4,10 @@
"private": true,
"type": "module",
"scripts": {
+ "agent-docs": "node scripts/generate-agent-docs.mjs",
+ "predev": "pnpm agent-docs",
"dev": "vite dev",
+ "prebuild": "pnpm agent-docs",
"build": "vite build",
"typecheck": "tsc --noEmit",
"test": "echo \"browser accessibility tests run with pnpm test:a11y\" && exit 0",
diff --git a/apps/docs/public/components/accordion.md b/apps/docs/public/components/accordion.md
new file mode 100644
index 0000000..0d406de
--- /dev/null
+++ b/apps/docs/public/components/accordion.md
@@ -0,0 +1,9 @@
+# Accordion
+
+> A keyboard-accessible group of animated disclosure panels.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/accordion)
+- [Markdown documentation](https://dowel.sh/components/accordion.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/components/alert-dialog.md b/apps/docs/public/components/alert-dialog.md
new file mode 100644
index 0000000..ff286bc
--- /dev/null
+++ b/apps/docs/public/components/alert-dialog.md
@@ -0,0 +1,9 @@
+# Alert Dialog
+
+> A focused confirmation modal for consequential actions.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/alert-dialog)
+- [Markdown documentation](https://dowel.sh/components/alert-dialog.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/components/avatar.md b/apps/docs/public/components/avatar.md
new file mode 100644
index 0000000..eb6df24
--- /dev/null
+++ b/apps/docs/public/components/avatar.md
@@ -0,0 +1,9 @@
+# Avatar
+
+> An image or initials fallback with shape, size, and presence.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/avatar)
+- [Markdown documentation](https://dowel.sh/components/avatar.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/components/badge.md b/apps/docs/public/components/badge.md
new file mode 100644
index 0000000..b3828ee
--- /dev/null
+++ b/apps/docs/public/components/badge.md
@@ -0,0 +1,9 @@
+# Badge
+
+> A compact status or metadata pill in five tones.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/badge)
+- [Markdown documentation](https://dowel.sh/components/badge.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/components/breadcrumbs.md b/apps/docs/public/components/breadcrumbs.md
new file mode 100644
index 0000000..22c42e9
--- /dev/null
+++ b/apps/docs/public/components/breadcrumbs.md
@@ -0,0 +1,9 @@
+# Breadcrumbs
+
+> A compact location trail with automatic middle-item elision.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/breadcrumbs)
+- [Markdown documentation](https://dowel.sh/components/breadcrumbs.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/components/button.md b/apps/docs/public/components/button.md
new file mode 100644
index 0000000..f1ef950
--- /dev/null
+++ b/apps/docs/public/components/button.md
@@ -0,0 +1,9 @@
+# Button
+
+> The default action control. Five hierarchy levels, two sizes.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/button)
+- [Markdown documentation](https://dowel.sh/components/button.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/components/calendar.md b/apps/docs/public/components/calendar.md
new file mode 100644
index 0000000..d5fc11a
--- /dev/null
+++ b/apps/docs/public/components/calendar.md
@@ -0,0 +1,9 @@
+# Calendar & Date Picker
+
+> An internationalized month grid and segmented date field.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/calendar)
+- [Markdown documentation](https://dowel.sh/components/calendar.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/components/callout.md b/apps/docs/public/components/callout.md
new file mode 100644
index 0000000..bb9df69
--- /dev/null
+++ b/apps/docs/public/components/callout.md
@@ -0,0 +1,9 @@
+# Callout
+
+> Persistent contextual guidance with optional actions and tone.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/callout)
+- [Markdown documentation](https://dowel.sh/components/callout.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/components/checkbox.md b/apps/docs/public/components/checkbox.md
new file mode 100644
index 0000000..a8484a2
--- /dev/null
+++ b/apps/docs/public/components/checkbox.md
@@ -0,0 +1,9 @@
+# Checkbox
+
+> Checked, unchecked, and mixed selection with native form values.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/checkbox)
+- [Markdown documentation](https://dowel.sh/components/checkbox.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/components/collapsible.md b/apps/docs/public/components/collapsible.md
new file mode 100644
index 0000000..eb28932
--- /dev/null
+++ b/apps/docs/public/components/collapsible.md
@@ -0,0 +1,9 @@
+# Collapsible
+
+> An animated disclosure for one optional content region.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/collapsible)
+- [Markdown documentation](https://dowel.sh/components/collapsible.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/components/collection-controls.md b/apps/docs/public/components/collection-controls.md
new file mode 100644
index 0000000..b75cca1
--- /dev/null
+++ b/apps/docs/public/components/collection-controls.md
@@ -0,0 +1,9 @@
+# Collection Controls
+
+> Filters, view options, row actions, and bulk selection controls.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/collection-controls)
+- [Markdown documentation](https://dowel.sh/components/collection-controls.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/components/combobox.md b/apps/docs/public/components/combobox.md
new file mode 100644
index 0000000..41af427
--- /dev/null
+++ b/apps/docs/public/components/combobox.md
@@ -0,0 +1,9 @@
+# Combobox
+
+> A filterable single-choice field for larger collections.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/combobox)
+- [Markdown documentation](https://dowel.sh/components/combobox.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/components/command-menu.md b/apps/docs/public/components/command-menu.md
new file mode 100644
index 0000000..086b1b1
--- /dev/null
+++ b/apps/docs/public/components/command-menu.md
@@ -0,0 +1,9 @@
+# Command Menu
+
+> A modal search surface for grouped application commands.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/command-menu)
+- [Markdown documentation](https://dowel.sh/components/command-menu.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/components/composer.md b/apps/docs/public/components/composer.md
new file mode 100644
index 0000000..d7e657d
--- /dev/null
+++ b/apps/docs/public/components/composer.md
@@ -0,0 +1,9 @@
+# Composer
+
+> A borderless creation shell with metadata and action regions.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/composer)
+- [Markdown documentation](https://dowel.sh/components/composer.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/components/context-menu.md b/apps/docs/public/components/context-menu.md
new file mode 100644
index 0000000..8c70357
--- /dev/null
+++ b/apps/docs/public/components/context-menu.md
@@ -0,0 +1,9 @@
+# Context Menu
+
+> A pointer-positioned menu for actions on a target surface.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/context-menu)
+- [Markdown documentation](https://dowel.sh/components/context-menu.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/components/data-table.md b/apps/docs/public/components/data-table.md
new file mode 100644
index 0000000..2f23590
--- /dev/null
+++ b/apps/docs/public/components/data-table.md
@@ -0,0 +1,9 @@
+# Data Table
+
+> A borderless, sortable, selectable, and resizable data table.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/data-table)
+- [Markdown documentation](https://dowel.sh/components/data-table.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/components/dialog.md b/apps/docs/public/components/dialog.md
new file mode 100644
index 0000000..850f3dd
--- /dev/null
+++ b/apps/docs/public/components/dialog.md
@@ -0,0 +1,9 @@
+# Dialog
+
+> A modal on the modal elevation tier, labelled by its title.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/dialog)
+- [Markdown documentation](https://dowel.sh/components/dialog.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/components/drawer.md b/apps/docs/public/components/drawer.md
new file mode 100644
index 0000000..28aa8d2
--- /dev/null
+++ b/apps/docs/public/components/drawer.md
@@ -0,0 +1,9 @@
+# Drawer
+
+> A swipe-dismissable edge panel for navigation and detail.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/drawer)
+- [Markdown documentation](https://dowel.sh/components/drawer.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/components/empty-state.md b/apps/docs/public/components/empty-state.md
new file mode 100644
index 0000000..a041176
--- /dev/null
+++ b/apps/docs/public/components/empty-state.md
@@ -0,0 +1,9 @@
+# Empty State
+
+> Structured guidance and actions for a surface without content.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/empty-state)
+- [Markdown documentation](https://dowel.sh/components/empty-state.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/components/file-upload.md b/apps/docs/public/components/file-upload.md
new file mode 100644
index 0000000..b873239
--- /dev/null
+++ b/apps/docs/public/components/file-upload.md
@@ -0,0 +1,9 @@
+# File Upload
+
+> A native file picker and dropzone with built-in validation.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/file-upload)
+- [Markdown documentation](https://dowel.sh/components/file-upload.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/components/form.md b/apps/docs/public/components/form.md
new file mode 100644
index 0000000..8d75b05
--- /dev/null
+++ b/apps/docs/public/components/form.md
@@ -0,0 +1,9 @@
+# Form
+
+> TanStack Form with Dowel fields and actions pre-bound.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/form)
+- [Markdown documentation](https://dowel.sh/components/form.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/components/icon-button.md b/apps/docs/public/components/icon-button.md
new file mode 100644
index 0000000..dd4a333
--- /dev/null
+++ b/apps/docs/public/components/icon-button.md
@@ -0,0 +1,9 @@
+# Icon Button
+
+> A square control for a single icon, with a required label.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/icon-button)
+- [Markdown documentation](https://dowel.sh/components/icon-button.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/components/index.md b/apps/docs/public/components/index.md
new file mode 100644
index 0000000..d68bbaa
--- /dev/null
+++ b/apps/docs/public/components/index.md
@@ -0,0 +1,50 @@
+# Dowel components
+
+> The complete Dowel component catalog.
+
+- [Accordion](https://dowel.sh/components/accordion.md): A keyboard-accessible group of animated disclosure panels.
+- [Alert Dialog](https://dowel.sh/components/alert-dialog.md): A focused confirmation modal for consequential actions.
+- [Avatar](https://dowel.sh/components/avatar.md): An image or initials fallback with shape, size, and presence.
+- [Badge](https://dowel.sh/components/badge.md): A compact status or metadata pill in five tones.
+- [Breadcrumbs](https://dowel.sh/components/breadcrumbs.md): A compact location trail with automatic middle-item elision.
+- [Button](https://dowel.sh/components/button.md): The default action control. Five hierarchy levels, two sizes.
+- [Calendar & Date Picker](https://dowel.sh/components/calendar.md): An internationalized month grid and segmented date field.
+- [Callout](https://dowel.sh/components/callout.md): Persistent contextual guidance with optional actions and tone.
+- [Checkbox](https://dowel.sh/components/checkbox.md): Checked, unchecked, and mixed selection with native form values.
+- [Collapsible](https://dowel.sh/components/collapsible.md): An animated disclosure for one optional content region.
+- [Combobox](https://dowel.sh/components/combobox.md): A filterable single-choice field for larger collections.
+- [Command Menu](https://dowel.sh/components/command-menu.md): A modal search surface for grouped application commands.
+- [Collection Controls](https://dowel.sh/components/collection-controls.md): Filters, view options, row actions, and bulk selection controls.
+- [Composer](https://dowel.sh/components/composer.md): A borderless creation shell with metadata and action regions.
+- [Context Menu](https://dowel.sh/components/context-menu.md): A pointer-positioned menu for actions on a target surface.
+- [Data Table](https://dowel.sh/components/data-table.md): A borderless, sortable, selectable, and resizable data table.
+- [Dialog](https://dowel.sh/components/dialog.md): A modal on the modal elevation tier, labelled by its title.
+- [Drawer](https://dowel.sh/components/drawer.md): A swipe-dismissable edge panel for navigation and detail.
+- [Empty State](https://dowel.sh/components/empty-state.md): Structured guidance and actions for a surface without content.
+- [File Upload](https://dowel.sh/components/file-upload.md): A native file picker and dropzone with built-in validation.
+- [Form](https://dowel.sh/components/form.md): TanStack Form with Dowel fields and actions pre-bound.
+- [Icon Button](https://dowel.sh/components/icon-button.md): A square control for a single icon, with a required label.
+- [Input](https://dowel.sh/components/input.md): Surface and borderless fields for settings and rich composers.
+- [Kbd](https://dowel.sh/components/kbd.md): A keyboard shortcut rendered one key per cap.
+- [List](https://dowel.sh/components/list.md): Semantic grouped rows with cells, density, and selection.
+- [Menu](https://dowel.sh/components/menu.md): A dropdown with keyboard navigation and typeahead.
+- [Native Select](https://dowel.sh/components/native-select.md): A resilient native choice control with Dowel field styling.
+- [Pagination](https://dowel.sh/components/pagination.md): Controlled page navigation with compact number elision.
+- [Popover](https://dowel.sh/components/popover.md): An anchored surface for interactive controls and rich details.
+- [Property Picker](https://dowel.sh/components/property-picker.md): Searchable grouped metadata selection with a compact pill trigger.
+- [Progress](https://dowel.sh/components/progress.md): Determinate and indeterminate progress for long tasks.
+- [Radio Group](https://dowel.sh/components/radio-group.md): A single-choice group with native forms and arrow-key selection.
+- [Search Field](https://dowel.sh/components/search-field.md): A search input with icon, clear action, and shortcut hint.
+- [Separator](https://dowel.sh/components/separator.md): A quiet horizontal or vertical content divider.
+- [Select](https://dowel.sh/components/select.md): A grouped single-choice popup with native form submission.
+- [Sidebar](https://dowel.sh/components/sidebar.md): A responsive application rail with pointer and keyboard resize.
+- [Slider](https://dowel.sh/components/slider.md): A pointer and keyboard control for a numeric range.
+- [Skeleton](https://dowel.sh/components/skeleton.md): Reduced-motion-aware placeholders for pending content.
+- [Spinner](https://dowel.sh/components/spinner.md): Accessible indeterminate progress in three compact sizes.
+- [Status](https://dowel.sh/components/status.md): A lightweight state icon and label in semantic tones.
+- [Switch](https://dowel.sh/components/switch.md): A compact immediate setting for binary on and off state.
+- [Tabs](https://dowel.sh/components/tabs.md): Compact pill and line view switching with arrow-key activation.
+- [Toast](https://dowel.sh/components/toast.md): A global notification queue with actions and promises.
+- [Toggle Group](https://dowel.sh/components/toggle-group.md): A connected set of independently pressed view controls.
+- [Tooltip](https://dowel.sh/components/tooltip.md): A hover and focus label on the popover elevation tier.
+- [Tree View](https://dowel.sh/components/tree-view.md): A nested hierarchy with selection and keyboard navigation.
diff --git a/apps/docs/public/components/input.md b/apps/docs/public/components/input.md
new file mode 100644
index 0000000..cb4c836
--- /dev/null
+++ b/apps/docs/public/components/input.md
@@ -0,0 +1,9 @@
+# Input
+
+> Surface and borderless fields for settings and rich composers.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/input)
+- [Markdown documentation](https://dowel.sh/components/input.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/components/kbd.md b/apps/docs/public/components/kbd.md
new file mode 100644
index 0000000..2930898
--- /dev/null
+++ b/apps/docs/public/components/kbd.md
@@ -0,0 +1,9 @@
+# Kbd
+
+> A keyboard shortcut rendered one key per cap.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/kbd)
+- [Markdown documentation](https://dowel.sh/components/kbd.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/components/list.md b/apps/docs/public/components/list.md
new file mode 100644
index 0000000..f163972
--- /dev/null
+++ b/apps/docs/public/components/list.md
@@ -0,0 +1,9 @@
+# List
+
+> Semantic grouped rows with cells, density, and selection.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/list)
+- [Markdown documentation](https://dowel.sh/components/list.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/components/menu.md b/apps/docs/public/components/menu.md
new file mode 100644
index 0000000..a163b87
--- /dev/null
+++ b/apps/docs/public/components/menu.md
@@ -0,0 +1,9 @@
+# Menu
+
+> A dropdown with keyboard navigation and typeahead.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/menu)
+- [Markdown documentation](https://dowel.sh/components/menu.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/components/native-select.md b/apps/docs/public/components/native-select.md
new file mode 100644
index 0000000..e6f1824
--- /dev/null
+++ b/apps/docs/public/components/native-select.md
@@ -0,0 +1,9 @@
+# Native Select
+
+> A resilient native choice control with Dowel field styling.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/native-select)
+- [Markdown documentation](https://dowel.sh/components/native-select.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/components/pagination.md b/apps/docs/public/components/pagination.md
new file mode 100644
index 0000000..723e01b
--- /dev/null
+++ b/apps/docs/public/components/pagination.md
@@ -0,0 +1,9 @@
+# Pagination
+
+> Controlled page navigation with compact number elision.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/pagination)
+- [Markdown documentation](https://dowel.sh/components/pagination.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/components/popover.md b/apps/docs/public/components/popover.md
new file mode 100644
index 0000000..0a7d2fc
--- /dev/null
+++ b/apps/docs/public/components/popover.md
@@ -0,0 +1,9 @@
+# Popover
+
+> An anchored surface for interactive controls and rich details.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/popover)
+- [Markdown documentation](https://dowel.sh/components/popover.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/components/progress.md b/apps/docs/public/components/progress.md
new file mode 100644
index 0000000..7a6c490
--- /dev/null
+++ b/apps/docs/public/components/progress.md
@@ -0,0 +1,9 @@
+# Progress
+
+> Determinate and indeterminate progress for long tasks.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/progress)
+- [Markdown documentation](https://dowel.sh/components/progress.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/components/property-picker.md b/apps/docs/public/components/property-picker.md
new file mode 100644
index 0000000..d8e8d6c
--- /dev/null
+++ b/apps/docs/public/components/property-picker.md
@@ -0,0 +1,9 @@
+# Property Picker
+
+> Searchable grouped metadata selection with a compact pill trigger.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/property-picker)
+- [Markdown documentation](https://dowel.sh/components/property-picker.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/components/radio-group.md b/apps/docs/public/components/radio-group.md
new file mode 100644
index 0000000..f203891
--- /dev/null
+++ b/apps/docs/public/components/radio-group.md
@@ -0,0 +1,9 @@
+# Radio Group
+
+> A single-choice group with native forms and arrow-key selection.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/radio-group)
+- [Markdown documentation](https://dowel.sh/components/radio-group.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/components/search-field.md b/apps/docs/public/components/search-field.md
new file mode 100644
index 0000000..a380ea4
--- /dev/null
+++ b/apps/docs/public/components/search-field.md
@@ -0,0 +1,9 @@
+# Search Field
+
+> A search input with icon, clear action, and shortcut hint.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/search-field)
+- [Markdown documentation](https://dowel.sh/components/search-field.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/components/select.md b/apps/docs/public/components/select.md
new file mode 100644
index 0000000..f8132fd
--- /dev/null
+++ b/apps/docs/public/components/select.md
@@ -0,0 +1,9 @@
+# Select
+
+> A grouped single-choice popup with native form submission.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/select)
+- [Markdown documentation](https://dowel.sh/components/select.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/components/separator.md b/apps/docs/public/components/separator.md
new file mode 100644
index 0000000..487fe98
--- /dev/null
+++ b/apps/docs/public/components/separator.md
@@ -0,0 +1,9 @@
+# Separator
+
+> A quiet horizontal or vertical content divider.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/separator)
+- [Markdown documentation](https://dowel.sh/components/separator.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/components/sidebar.md b/apps/docs/public/components/sidebar.md
new file mode 100644
index 0000000..a9a563b
--- /dev/null
+++ b/apps/docs/public/components/sidebar.md
@@ -0,0 +1,9 @@
+# Sidebar
+
+> A responsive application rail with pointer and keyboard resize.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/sidebar)
+- [Markdown documentation](https://dowel.sh/components/sidebar.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/components/skeleton.md b/apps/docs/public/components/skeleton.md
new file mode 100644
index 0000000..f5b72c2
--- /dev/null
+++ b/apps/docs/public/components/skeleton.md
@@ -0,0 +1,9 @@
+# Skeleton
+
+> Reduced-motion-aware placeholders for pending content.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/skeleton)
+- [Markdown documentation](https://dowel.sh/components/skeleton.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/components/slider.md b/apps/docs/public/components/slider.md
new file mode 100644
index 0000000..8f32458
--- /dev/null
+++ b/apps/docs/public/components/slider.md
@@ -0,0 +1,9 @@
+# Slider
+
+> A pointer and keyboard control for a numeric range.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/slider)
+- [Markdown documentation](https://dowel.sh/components/slider.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/components/spinner.md b/apps/docs/public/components/spinner.md
new file mode 100644
index 0000000..78a0846
--- /dev/null
+++ b/apps/docs/public/components/spinner.md
@@ -0,0 +1,9 @@
+# Spinner
+
+> Accessible indeterminate progress in three compact sizes.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/spinner)
+- [Markdown documentation](https://dowel.sh/components/spinner.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/components/status.md b/apps/docs/public/components/status.md
new file mode 100644
index 0000000..c2f6832
--- /dev/null
+++ b/apps/docs/public/components/status.md
@@ -0,0 +1,9 @@
+# Status
+
+> A lightweight state icon and label in semantic tones.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/status)
+- [Markdown documentation](https://dowel.sh/components/status.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/components/switch.md b/apps/docs/public/components/switch.md
new file mode 100644
index 0000000..9f9ccf6
--- /dev/null
+++ b/apps/docs/public/components/switch.md
@@ -0,0 +1,9 @@
+# Switch
+
+> A compact immediate setting for binary on and off state.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/switch)
+- [Markdown documentation](https://dowel.sh/components/switch.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/components/tabs.md b/apps/docs/public/components/tabs.md
new file mode 100644
index 0000000..8ef0140
--- /dev/null
+++ b/apps/docs/public/components/tabs.md
@@ -0,0 +1,9 @@
+# Tabs
+
+> Compact pill and line view switching with arrow-key activation.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/tabs)
+- [Markdown documentation](https://dowel.sh/components/tabs.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/components/toast.md b/apps/docs/public/components/toast.md
new file mode 100644
index 0000000..7afe492
--- /dev/null
+++ b/apps/docs/public/components/toast.md
@@ -0,0 +1,9 @@
+# Toast
+
+> A global notification queue with actions and promises.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/toast)
+- [Markdown documentation](https://dowel.sh/components/toast.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/components/toggle-group.md b/apps/docs/public/components/toggle-group.md
new file mode 100644
index 0000000..626262b
--- /dev/null
+++ b/apps/docs/public/components/toggle-group.md
@@ -0,0 +1,9 @@
+# Toggle Group
+
+> A connected set of independently pressed view controls.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/toggle-group)
+- [Markdown documentation](https://dowel.sh/components/toggle-group.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/components/tooltip.md b/apps/docs/public/components/tooltip.md
new file mode 100644
index 0000000..036272f
--- /dev/null
+++ b/apps/docs/public/components/tooltip.md
@@ -0,0 +1,9 @@
+# Tooltip
+
+> A hover and focus label on the popover elevation tier.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/tooltip)
+- [Markdown documentation](https://dowel.sh/components/tooltip.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/components/tree-view.md b/apps/docs/public/components/tree-view.md
new file mode 100644
index 0000000..6af75a2
--- /dev/null
+++ b/apps/docs/public/components/tree-view.md
@@ -0,0 +1,9 @@
+# Tree View
+
+> A nested hierarchy with selection and keyboard navigation.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/tree-view)
+- [Markdown documentation](https://dowel.sh/components/tree-view.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/dependencies.md b/apps/docs/public/dependencies.md
new file mode 100644
index 0000000..e68a1fa
--- /dev/null
+++ b/apps/docs/public/dependencies.md
@@ -0,0 +1,18 @@
+# Dowel dependencies
+
+> Runtime packages installed with Dowel and peer runtimes owned by the consuming application.
+
+## Runtime dependencies
+
+- `@base-ui/react`: `^1.7.0`
+- `@heroicons/react`: `2.2.0`
+- `@internationalized/date`: `^3.12.3`
+- `@stylexjs/stylex`: `0.19.0`
+- `@tanstack/react-form`: `^1.33.5`
+- `@tanstack/react-table`: `^9.1.2`
+- `react-aria-components`: `^1.20.0`
+
+## Peer dependencies
+
+- `react`: `^19.0.0`
+- `react-dom`: `^19.0.0`
diff --git a/apps/docs/public/index.md b/apps/docs/public/index.md
new file mode 100644
index 0000000..a92514b
--- /dev/null
+++ b/apps/docs/public/index.md
@@ -0,0 +1,8 @@
+# Dowel
+
+> Compact, accessible React components for product interfaces.
+
+- [Agent documentation index](https://dowel.sh/llms.txt)
+- [Complete agent documentation](https://dowel.sh/llms-full.txt)
+- [Component catalog](https://dowel.sh/components/index.md)
+- [HTML documentation](https://dowel.sh/components)
diff --git a/apps/docs/public/llms-full.txt b/apps/docs/public/llms-full.txt
new file mode 100644
index 0000000..0181d31
--- /dev/null
+++ b/apps/docs/public/llms-full.txt
@@ -0,0 +1,570 @@
+# Dowel complete documentation
+
+> Dowel 0.2.0 is an opinionated React component library for compact, accessible product interfaces.
+
+## Installation
+
+```sh
+pnpm add @karnstack/dowel
+```
+
+```tsx
+import "@karnstack/dowel/dowel.css";
+```
+
+## Conventions
+
+- Import public components and types from `@karnstack/dowel`.
+- Wrap the application in `ThemeProvider` when selecting light, dark, or system themes explicitly.
+- Dowel controls own their appearance. Prefer documented props over consumer class or style overrides.
+- Icon-only controls require an accessible `label`.
+
+## Accordion
+
+> A keyboard-accessible group of animated disclosure panels.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/accordion)
+- [Markdown documentation](https://dowel.sh/components/accordion.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
+
+---
+
+## Alert Dialog
+
+> A focused confirmation modal for consequential actions.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/alert-dialog)
+- [Markdown documentation](https://dowel.sh/components/alert-dialog.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
+
+---
+
+## Avatar
+
+> An image or initials fallback with shape, size, and presence.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/avatar)
+- [Markdown documentation](https://dowel.sh/components/avatar.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
+
+---
+
+## Badge
+
+> A compact status or metadata pill in five tones.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/badge)
+- [Markdown documentation](https://dowel.sh/components/badge.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
+
+---
+
+## Breadcrumbs
+
+> A compact location trail with automatic middle-item elision.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/breadcrumbs)
+- [Markdown documentation](https://dowel.sh/components/breadcrumbs.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
+
+---
+
+## Button
+
+> The default action control. Five hierarchy levels, two sizes.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/button)
+- [Markdown documentation](https://dowel.sh/components/button.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
+
+---
+
+## Calendar & Date Picker
+
+> An internationalized month grid and segmented date field.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/calendar)
+- [Markdown documentation](https://dowel.sh/components/calendar.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
+
+---
+
+## Callout
+
+> Persistent contextual guidance with optional actions and tone.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/callout)
+- [Markdown documentation](https://dowel.sh/components/callout.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
+
+---
+
+## Checkbox
+
+> Checked, unchecked, and mixed selection with native form values.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/checkbox)
+- [Markdown documentation](https://dowel.sh/components/checkbox.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
+
+---
+
+## Collapsible
+
+> An animated disclosure for one optional content region.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/collapsible)
+- [Markdown documentation](https://dowel.sh/components/collapsible.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
+
+---
+
+## Combobox
+
+> A filterable single-choice field for larger collections.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/combobox)
+- [Markdown documentation](https://dowel.sh/components/combobox.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
+
+---
+
+## Command Menu
+
+> A modal search surface for grouped application commands.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/command-menu)
+- [Markdown documentation](https://dowel.sh/components/command-menu.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
+
+---
+
+## Collection Controls
+
+> Filters, view options, row actions, and bulk selection controls.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/collection-controls)
+- [Markdown documentation](https://dowel.sh/components/collection-controls.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
+
+---
+
+## Composer
+
+> A borderless creation shell with metadata and action regions.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/composer)
+- [Markdown documentation](https://dowel.sh/components/composer.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
+
+---
+
+## Context Menu
+
+> A pointer-positioned menu for actions on a target surface.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/context-menu)
+- [Markdown documentation](https://dowel.sh/components/context-menu.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
+
+---
+
+## Data Table
+
+> A borderless, sortable, selectable, and resizable data table.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/data-table)
+- [Markdown documentation](https://dowel.sh/components/data-table.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
+
+---
+
+## Dialog
+
+> A modal on the modal elevation tier, labelled by its title.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/dialog)
+- [Markdown documentation](https://dowel.sh/components/dialog.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
+
+---
+
+## Drawer
+
+> A swipe-dismissable edge panel for navigation and detail.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/drawer)
+- [Markdown documentation](https://dowel.sh/components/drawer.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
+
+---
+
+## Empty State
+
+> Structured guidance and actions for a surface without content.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/empty-state)
+- [Markdown documentation](https://dowel.sh/components/empty-state.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
+
+---
+
+## File Upload
+
+> A native file picker and dropzone with built-in validation.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/file-upload)
+- [Markdown documentation](https://dowel.sh/components/file-upload.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
+
+---
+
+## Form
+
+> TanStack Form with Dowel fields and actions pre-bound.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/form)
+- [Markdown documentation](https://dowel.sh/components/form.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
+
+---
+
+## Icon Button
+
+> A square control for a single icon, with a required label.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/icon-button)
+- [Markdown documentation](https://dowel.sh/components/icon-button.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
+
+---
+
+## Input
+
+> Surface and borderless fields for settings and rich composers.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/input)
+- [Markdown documentation](https://dowel.sh/components/input.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
+
+---
+
+## Kbd
+
+> A keyboard shortcut rendered one key per cap.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/kbd)
+- [Markdown documentation](https://dowel.sh/components/kbd.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
+
+---
+
+## List
+
+> Semantic grouped rows with cells, density, and selection.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/list)
+- [Markdown documentation](https://dowel.sh/components/list.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
+
+---
+
+## Menu
+
+> A dropdown with keyboard navigation and typeahead.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/menu)
+- [Markdown documentation](https://dowel.sh/components/menu.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
+
+---
+
+## Native Select
+
+> A resilient native choice control with Dowel field styling.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/native-select)
+- [Markdown documentation](https://dowel.sh/components/native-select.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
+
+---
+
+## Pagination
+
+> Controlled page navigation with compact number elision.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/pagination)
+- [Markdown documentation](https://dowel.sh/components/pagination.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
+
+---
+
+## Popover
+
+> An anchored surface for interactive controls and rich details.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/popover)
+- [Markdown documentation](https://dowel.sh/components/popover.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
+
+---
+
+## Property Picker
+
+> Searchable grouped metadata selection with a compact pill trigger.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/property-picker)
+- [Markdown documentation](https://dowel.sh/components/property-picker.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
+
+---
+
+## Progress
+
+> Determinate and indeterminate progress for long tasks.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/progress)
+- [Markdown documentation](https://dowel.sh/components/progress.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
+
+---
+
+## Radio Group
+
+> A single-choice group with native forms and arrow-key selection.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/radio-group)
+- [Markdown documentation](https://dowel.sh/components/radio-group.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
+
+---
+
+## Search Field
+
+> A search input with icon, clear action, and shortcut hint.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/search-field)
+- [Markdown documentation](https://dowel.sh/components/search-field.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
+
+---
+
+## Separator
+
+> A quiet horizontal or vertical content divider.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/separator)
+- [Markdown documentation](https://dowel.sh/components/separator.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
+
+---
+
+## Select
+
+> A grouped single-choice popup with native form submission.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/select)
+- [Markdown documentation](https://dowel.sh/components/select.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
+
+---
+
+## Sidebar
+
+> A responsive application rail with pointer and keyboard resize.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/sidebar)
+- [Markdown documentation](https://dowel.sh/components/sidebar.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
+
+---
+
+## Slider
+
+> A pointer and keyboard control for a numeric range.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/slider)
+- [Markdown documentation](https://dowel.sh/components/slider.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
+
+---
+
+## Skeleton
+
+> Reduced-motion-aware placeholders for pending content.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/skeleton)
+- [Markdown documentation](https://dowel.sh/components/skeleton.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
+
+---
+
+## Spinner
+
+> Accessible indeterminate progress in three compact sizes.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/spinner)
+- [Markdown documentation](https://dowel.sh/components/spinner.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
+
+---
+
+## Status
+
+> A lightweight state icon and label in semantic tones.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/status)
+- [Markdown documentation](https://dowel.sh/components/status.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
+
+---
+
+## Switch
+
+> A compact immediate setting for binary on and off state.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/switch)
+- [Markdown documentation](https://dowel.sh/components/switch.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
+
+---
+
+## Tabs
+
+> Compact pill and line view switching with arrow-key activation.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/tabs)
+- [Markdown documentation](https://dowel.sh/components/tabs.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
+
+---
+
+## Toast
+
+> A global notification queue with actions and promises.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/toast)
+- [Markdown documentation](https://dowel.sh/components/toast.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
+
+---
+
+## Toggle Group
+
+> A connected set of independently pressed view controls.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/toggle-group)
+- [Markdown documentation](https://dowel.sh/components/toggle-group.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
+
+---
+
+## Tooltip
+
+> A hover and focus label on the popover elevation tier.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/tooltip)
+- [Markdown documentation](https://dowel.sh/components/tooltip.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
+
+---
+
+## Tree View
+
+> A nested hierarchy with selection and keyboard navigation.
+
+- Package: `@karnstack/dowel`
+- [HTML documentation](https://dowel.sh/components/tree-view)
+- [Markdown documentation](https://dowel.sh/components/tree-view.md)
+
+Import public components and types from the package root. Import `@karnstack/dowel/dowel.css` once at the application root.
diff --git a/apps/docs/public/llms.txt b/apps/docs/public/llms.txt
new file mode 100644
index 0000000..4a76e4c
--- /dev/null
+++ b/apps/docs/public/llms.txt
@@ -0,0 +1,61 @@
+# Dowel
+
+> Dowel is an opinionated React component library for compact, accessible product interfaces.
+
+Install `@karnstack/dowel`, import `@karnstack/dowel/dowel.css` once, and import components from the package root. Dowel targets React 19.
+
+## Components
+
+- [Accordion](https://dowel.sh/components/accordion.md): A keyboard-accessible group of animated disclosure panels.
+- [Alert Dialog](https://dowel.sh/components/alert-dialog.md): A focused confirmation modal for consequential actions.
+- [Avatar](https://dowel.sh/components/avatar.md): An image or initials fallback with shape, size, and presence.
+- [Badge](https://dowel.sh/components/badge.md): A compact status or metadata pill in five tones.
+- [Breadcrumbs](https://dowel.sh/components/breadcrumbs.md): A compact location trail with automatic middle-item elision.
+- [Button](https://dowel.sh/components/button.md): The default action control. Five hierarchy levels, two sizes.
+- [Calendar & Date Picker](https://dowel.sh/components/calendar.md): An internationalized month grid and segmented date field.
+- [Callout](https://dowel.sh/components/callout.md): Persistent contextual guidance with optional actions and tone.
+- [Checkbox](https://dowel.sh/components/checkbox.md): Checked, unchecked, and mixed selection with native form values.
+- [Collapsible](https://dowel.sh/components/collapsible.md): An animated disclosure for one optional content region.
+- [Combobox](https://dowel.sh/components/combobox.md): A filterable single-choice field for larger collections.
+- [Command Menu](https://dowel.sh/components/command-menu.md): A modal search surface for grouped application commands.
+- [Collection Controls](https://dowel.sh/components/collection-controls.md): Filters, view options, row actions, and bulk selection controls.
+- [Composer](https://dowel.sh/components/composer.md): A borderless creation shell with metadata and action regions.
+- [Context Menu](https://dowel.sh/components/context-menu.md): A pointer-positioned menu for actions on a target surface.
+- [Data Table](https://dowel.sh/components/data-table.md): A borderless, sortable, selectable, and resizable data table.
+- [Dialog](https://dowel.sh/components/dialog.md): A modal on the modal elevation tier, labelled by its title.
+- [Drawer](https://dowel.sh/components/drawer.md): A swipe-dismissable edge panel for navigation and detail.
+- [Empty State](https://dowel.sh/components/empty-state.md): Structured guidance and actions for a surface without content.
+- [File Upload](https://dowel.sh/components/file-upload.md): A native file picker and dropzone with built-in validation.
+- [Form](https://dowel.sh/components/form.md): TanStack Form with Dowel fields and actions pre-bound.
+- [Icon Button](https://dowel.sh/components/icon-button.md): A square control for a single icon, with a required label.
+- [Input](https://dowel.sh/components/input.md): Surface and borderless fields for settings and rich composers.
+- [Kbd](https://dowel.sh/components/kbd.md): A keyboard shortcut rendered one key per cap.
+- [List](https://dowel.sh/components/list.md): Semantic grouped rows with cells, density, and selection.
+- [Menu](https://dowel.sh/components/menu.md): A dropdown with keyboard navigation and typeahead.
+- [Native Select](https://dowel.sh/components/native-select.md): A resilient native choice control with Dowel field styling.
+- [Pagination](https://dowel.sh/components/pagination.md): Controlled page navigation with compact number elision.
+- [Popover](https://dowel.sh/components/popover.md): An anchored surface for interactive controls and rich details.
+- [Property Picker](https://dowel.sh/components/property-picker.md): Searchable grouped metadata selection with a compact pill trigger.
+- [Progress](https://dowel.sh/components/progress.md): Determinate and indeterminate progress for long tasks.
+- [Radio Group](https://dowel.sh/components/radio-group.md): A single-choice group with native forms and arrow-key selection.
+- [Search Field](https://dowel.sh/components/search-field.md): A search input with icon, clear action, and shortcut hint.
+- [Separator](https://dowel.sh/components/separator.md): A quiet horizontal or vertical content divider.
+- [Select](https://dowel.sh/components/select.md): A grouped single-choice popup with native form submission.
+- [Sidebar](https://dowel.sh/components/sidebar.md): A responsive application rail with pointer and keyboard resize.
+- [Slider](https://dowel.sh/components/slider.md): A pointer and keyboard control for a numeric range.
+- [Skeleton](https://dowel.sh/components/skeleton.md): Reduced-motion-aware placeholders for pending content.
+- [Spinner](https://dowel.sh/components/spinner.md): Accessible indeterminate progress in three compact sizes.
+- [Status](https://dowel.sh/components/status.md): A lightweight state icon and label in semantic tones.
+- [Switch](https://dowel.sh/components/switch.md): A compact immediate setting for binary on and off state.
+- [Tabs](https://dowel.sh/components/tabs.md): Compact pill and line view switching with arrow-key activation.
+- [Toast](https://dowel.sh/components/toast.md): A global notification queue with actions and promises.
+- [Toggle Group](https://dowel.sh/components/toggle-group.md): A connected set of independently pressed view controls.
+- [Tooltip](https://dowel.sh/components/tooltip.md): A hover and focus label on the popover elevation tier.
+- [Tree View](https://dowel.sh/components/tree-view.md): A nested hierarchy with selection and keyboard navigation.
+
+## Library
+
+- [Complete documentation](https://dowel.sh/llms-full.txt): All component summaries in one Markdown document.
+- [Component index](https://dowel.sh/components/index.md): Compact component catalog.
+- [Dependencies](https://dowel.sh/dependencies.md): Runtime and peer dependency overview.
+- [GitHub](https://github.com/karnstack/dowel): Source, issues, and releases.
diff --git a/apps/docs/scripts/generate-agent-docs.mjs b/apps/docs/scripts/generate-agent-docs.mjs
new file mode 100644
index 0000000..0a8d204
--- /dev/null
+++ b/apps/docs/scripts/generate-agent-docs.mjs
@@ -0,0 +1,189 @@
+import { mkdir, readFile, writeFile } from "node:fs/promises";
+
+import { componentNav } from "../src/lib/nav.ts";
+
+const docsOrigin = "https://dowel.sh";
+const publicRoot = new URL("../public/", import.meta.url);
+const packageRoot = new URL("../../../packages/dowel/", import.meta.url);
+const tick = String.fromCharCode(96);
+
+function absolute(path) {
+ return new URL(path, docsOrigin).href;
+}
+
+function componentSlug(item) {
+ return String(item.to).replace("/components/", "");
+}
+
+function componentMarkdown(item, heading = "#") {
+ const docsUrl = absolute(String(item.to));
+ const markdownUrl = absolute(String(item.to) + ".md");
+ return [
+ heading + " " + item.title,
+ "",
+ "> " + item.summary,
+ "",
+ "- Package: " + tick + "@karnstack/dowel" + tick,
+ "- [HTML documentation](" + docsUrl + ")",
+ "- [Markdown documentation](" + markdownUrl + ")",
+ "",
+ "Import public components and types from the package root. Import " +
+ tick +
+ "@karnstack/dowel/dowel.css" +
+ tick +
+ " once at the application root.",
+ ].join("\n");
+}
+
+await mkdir(new URL("components/", publicRoot), { recursive: true });
+
+for (const item of componentNav) {
+ await writeFile(
+ new URL("components/" + componentSlug(item) + ".md", publicRoot),
+ componentMarkdown(item) + "\n",
+ );
+}
+
+const packageMetadata = JSON.parse(
+ await readFile(new URL("package.json", packageRoot), "utf8"),
+);
+const componentLinks = componentNav
+ .map(
+ (item) =>
+ "- [" +
+ item.title +
+ "](" +
+ absolute(String(item.to) + ".md") +
+ "): " +
+ item.summary,
+ )
+ .join("\n");
+
+const llms = [
+ "# Dowel",
+ "",
+ "> Dowel is an opinionated React component library for compact, accessible product interfaces.",
+ "",
+ "Install " +
+ tick +
+ "@karnstack/dowel" +
+ tick +
+ ", import " +
+ tick +
+ "@karnstack/dowel/dowel.css" +
+ tick +
+ " once, and import components from the package root. Dowel targets React 19.",
+ "",
+ "## Components",
+ "",
+ componentLinks,
+ "",
+ "## Library",
+ "",
+ "- [Complete documentation](" +
+ absolute("/llms-full.txt") +
+ "): All component summaries in one Markdown document.",
+ "- [Component index](" +
+ absolute("/components/index.md") +
+ "): Compact component catalog.",
+ "- [Dependencies](" +
+ absolute("/dependencies.md") +
+ "): Runtime and peer dependency overview.",
+ "- [GitHub](https://github.com/karnstack/dowel): Source, issues, and releases.",
+ "",
+].join("\n");
+
+const componentDocs = componentNav
+ .map((item) => componentMarkdown(item, "##"))
+ .join("\n\n---\n\n");
+const llmsFull = [
+ "# Dowel complete documentation",
+ "",
+ "> Dowel " +
+ packageMetadata.version +
+ " is an opinionated React component library for compact, accessible product interfaces.",
+ "",
+ "## Installation",
+ "",
+ tick.repeat(3) + "sh",
+ "pnpm add @karnstack/dowel",
+ tick.repeat(3),
+ "",
+ tick.repeat(3) + "tsx",
+ 'import "@karnstack/dowel/dowel.css";',
+ tick.repeat(3),
+ "",
+ "## Conventions",
+ "",
+ "- Import public components and types from " +
+ tick +
+ "@karnstack/dowel" +
+ tick +
+ ".",
+ "- Wrap the application in " +
+ tick +
+ "ThemeProvider" +
+ tick +
+ " when selecting light, dark, or system themes explicitly.",
+ "- Dowel controls own their appearance. Prefer documented props over consumer class or style overrides.",
+ "- Icon-only controls require an accessible " + tick + "label" + tick + ".",
+ "",
+ componentDocs,
+ "",
+].join("\n");
+
+const componentIndex = [
+ "# Dowel components",
+ "",
+ "> The complete Dowel component catalog.",
+ "",
+ componentLinks,
+ "",
+].join("\n");
+
+const dependencyLines = Object.entries(packageMetadata.dependencies)
+ .map(
+ ([name, version]) =>
+ "- " + tick + name + tick + ": " + tick + version + tick,
+ )
+ .join("\n");
+const peerLines = Object.entries(packageMetadata.peerDependencies)
+ .map(
+ ([name, version]) =>
+ "- " + tick + name + tick + ": " + tick + version + tick,
+ )
+ .join("\n");
+const dependencies = [
+ "# Dowel dependencies",
+ "",
+ "> Runtime packages installed with Dowel and peer runtimes owned by the consuming application.",
+ "",
+ "## Runtime dependencies",
+ "",
+ dependencyLines,
+ "",
+ "## Peer dependencies",
+ "",
+ peerLines,
+ "",
+].join("\n");
+
+const index = [
+ "# Dowel",
+ "",
+ "> Compact, accessible React components for product interfaces.",
+ "",
+ "- [Agent documentation index](" + absolute("/llms.txt") + ")",
+ "- [Complete agent documentation](" + absolute("/llms-full.txt") + ")",
+ "- [Component catalog](" + absolute("/components/index.md") + ")",
+ "- [HTML documentation](" + absolute("/components") + ")",
+ "",
+].join("\n");
+
+await Promise.all([
+ writeFile(new URL("llms.txt", publicRoot), llms),
+ writeFile(new URL("llms-full.txt", publicRoot), llmsFull),
+ writeFile(new URL("components/index.md", publicRoot), componentIndex),
+ writeFile(new URL("dependencies.md", publicRoot), dependencies),
+ writeFile(new URL("index.md", publicRoot), index),
+]);
diff --git a/apps/docs/src/docs.css b/apps/docs/src/docs.css
index 8040ac1..6664ccf 100644
--- a/apps/docs/src/docs.css
+++ b/apps/docs/src/docs.css
@@ -1709,24 +1709,103 @@ body {
border: 0;
}
-.docs-data-table-header-demo {
- margin-block-start: var(--dowel-space-8);
+.docs-collection-view {
+ position: relative;
+ inline-size: 100%;
+ min-inline-size: 0;
+}
+
+.docs-collection-controls {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: var(--dowel-space-3);
+ inline-size: 100%;
+ min-inline-size: 0;
+ padding-block-end: var(--dowel-space-3);
+}
+
+.docs-collection-controls [data-dowel-component="filter-bar"] {
+ flex: 1;
+ min-inline-size: 0;
+}
+
+.docs-collection-actions {
+ display: flex;
+ flex: none;
+ align-items: center;
+ gap: var(--dowel-space-2);
+}
+
+.docs-selection-demo {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ gap: var(--dowel-space-8);
+ inline-size: 100%;
+ min-block-size: 5rem;
+}
+
+.docs-form-demo {
+ inline-size: min(100%, 30rem);
+}
+
+.docs-form-result {
+ margin: var(--dowel-space-4) 0 0;
+ color: var(--dowel-text-2);
+ font-size: var(--dowel-font-size-sm);
}
.docs-data-table-states {
display: grid;
grid-template-columns: minmax(0, 1fr);
- gap: var(--dowel-space-6);
+ gap: var(--dowel-space-4);
margin-block-start: var(--dowel-space-8);
}
-.docs-data-table-states > [data-dowel-component="data-table"] {
- padding: var(--dowel-space-3);
+.docs-data-table-state,
+.docs-data-table-mode {
+ overflow: hidden;
border: 1px solid var(--dowel-border-1);
border-radius: var(--dowel-radius-lg);
background-color: var(--dowel-bg-1);
}
+.docs-data-table-state {
+ margin: 0;
+}
+
+.docs-data-table-state figcaption,
+.docs-data-table-mode-heading {
+ display: flex;
+ align-items: center;
+ min-block-size: 2.25rem;
+ padding-inline: var(--dowel-space-5);
+ border-block-end: 1px solid var(--dowel-border-1);
+ color: var(--dowel-text-2);
+ font-size: var(--dowel-fs-mini);
+ font-weight: 550;
+}
+
+.docs-data-table-state > [data-dowel-component="data-table"],
+.docs-data-table-mode > [data-dowel-component="data-table"] {
+ padding: var(--dowel-space-2);
+}
+
+.docs-data-table-mode {
+ margin-block-start: var(--dowel-space-8);
+}
+
+.docs-data-table-mode-heading {
+ justify-content: space-between;
+ gap: var(--dowel-space-4);
+}
+
+.docs-data-table-mode-heading > :last-child {
+ color: var(--dowel-text-4);
+ font-weight: 450;
+}
+
.docs-issue-title {
display: flex;
align-items: center;
diff --git a/apps/docs/src/lib/nav.ts b/apps/docs/src/lib/nav.ts
index 374b325..9ad698a 100644
--- a/apps/docs/src/lib/nav.ts
+++ b/apps/docs/src/lib/nav.ts
@@ -80,6 +80,11 @@ export const componentNav: NavItem[] = [
to: "/components/command-menu",
summary: "A modal search surface for grouped application commands.",
},
+ {
+ title: "Collection Controls",
+ to: "/components/collection-controls",
+ summary: "Filters, view options, row actions, and bulk selection controls.",
+ },
{
title: "Composer",
to: "/components/composer",
@@ -115,6 +120,11 @@ export const componentNav: NavItem[] = [
to: "/components/file-upload",
summary: "A native file picker and dropzone with built-in validation.",
},
+ {
+ title: "Form",
+ to: "/components/form",
+ summary: "TanStack Form with Dowel fields and actions pre-bound.",
+ },
{
title: "Icon Button",
to: "/components/icon-button",
@@ -267,6 +277,7 @@ export const componentSections: NavSection[] = [
"/components/calendar",
"/components/combobox",
"/components/file-upload",
+ "/components/form",
"/components/input",
"/components/native-select",
"/components/property-picker",
@@ -327,7 +338,10 @@ export const componentSections: NavSection[] = [
},
{
title: "Composites",
- items: components("/components/composer"),
+ items: components(
+ "/components/collection-controls",
+ "/components/composer",
+ ),
},
];
diff --git a/apps/docs/src/routeTree.gen.ts b/apps/docs/src/routeTree.gen.ts
index a68130f..a357c82 100644
--- a/apps/docs/src/routeTree.gen.ts
+++ b/apps/docs/src/routeTree.gen.ts
@@ -23,6 +23,7 @@ import { Route as ComponentsCalendarRouteImport } from './routes/components/cale
import { Route as ComponentsCalloutRouteImport } from './routes/components/callout'
import { Route as ComponentsCheckboxRouteImport } from './routes/components/checkbox'
import { Route as ComponentsCollapsibleRouteImport } from './routes/components/collapsible'
+import { Route as ComponentsCollectionControlsRouteImport } from './routes/components/collection-controls'
import { Route as ComponentsComboboxRouteImport } from './routes/components/combobox'
import { Route as ComponentsCommandMenuRouteImport } from './routes/components/command-menu'
import { Route as ComponentsComposerRouteImport } from './routes/components/composer'
@@ -32,6 +33,7 @@ import { Route as ComponentsDialogRouteImport } from './routes/components/dialog
import { Route as ComponentsDrawerRouteImport } from './routes/components/drawer'
import { Route as ComponentsEmptyStateRouteImport } from './routes/components/empty-state'
import { Route as ComponentsFileUploadRouteImport } from './routes/components/file-upload'
+import { Route as ComponentsFormRouteImport } from './routes/components/form'
import { Route as ComponentsIconButtonRouteImport } from './routes/components/icon-button'
import { Route as ComponentsInputRouteImport } from './routes/components/input'
import { Route as ComponentsKbdRouteImport } from './routes/components/kbd'
@@ -128,6 +130,12 @@ const ComponentsCollapsibleRoute = ComponentsCollapsibleRouteImport.update({
path: '/collapsible',
getParentRoute: () => ComponentsRouteRoute,
} as any)
+const ComponentsCollectionControlsRoute =
+ ComponentsCollectionControlsRouteImport.update({
+ id: '/collection-controls',
+ path: '/collection-controls',
+ getParentRoute: () => ComponentsRouteRoute,
+ } as any)
const ComponentsComboboxRoute = ComponentsComboboxRouteImport.update({
id: '/combobox',
path: '/combobox',
@@ -173,6 +181,11 @@ const ComponentsFileUploadRoute = ComponentsFileUploadRouteImport.update({
path: '/file-upload',
getParentRoute: () => ComponentsRouteRoute,
} as any)
+const ComponentsFormRoute = ComponentsFormRouteImport.update({
+ id: '/form',
+ path: '/form',
+ getParentRoute: () => ComponentsRouteRoute,
+} as any)
const ComponentsIconButtonRoute = ComponentsIconButtonRouteImport.update({
id: '/icon-button',
path: '/icon-button',
@@ -314,6 +327,7 @@ export interface FileRoutesByFullPath {
'/components/callout': typeof ComponentsCalloutRoute
'/components/checkbox': typeof ComponentsCheckboxRoute
'/components/collapsible': typeof ComponentsCollapsibleRoute
+ '/components/collection-controls': typeof ComponentsCollectionControlsRoute
'/components/combobox': typeof ComponentsComboboxRoute
'/components/command-menu': typeof ComponentsCommandMenuRoute
'/components/composer': typeof ComponentsComposerRoute
@@ -323,6 +337,7 @@ export interface FileRoutesByFullPath {
'/components/drawer': typeof ComponentsDrawerRoute
'/components/empty-state': typeof ComponentsEmptyStateRoute
'/components/file-upload': typeof ComponentsFileUploadRoute
+ '/components/form': typeof ComponentsFormRoute
'/components/icon-button': typeof ComponentsIconButtonRoute
'/components/input': typeof ComponentsInputRoute
'/components/kbd': typeof ComponentsKbdRoute
@@ -363,6 +378,7 @@ export interface FileRoutesByTo {
'/components/callout': typeof ComponentsCalloutRoute
'/components/checkbox': typeof ComponentsCheckboxRoute
'/components/collapsible': typeof ComponentsCollapsibleRoute
+ '/components/collection-controls': typeof ComponentsCollectionControlsRoute
'/components/combobox': typeof ComponentsComboboxRoute
'/components/command-menu': typeof ComponentsCommandMenuRoute
'/components/composer': typeof ComponentsComposerRoute
@@ -372,6 +388,7 @@ export interface FileRoutesByTo {
'/components/drawer': typeof ComponentsDrawerRoute
'/components/empty-state': typeof ComponentsEmptyStateRoute
'/components/file-upload': typeof ComponentsFileUploadRoute
+ '/components/form': typeof ComponentsFormRoute
'/components/icon-button': typeof ComponentsIconButtonRoute
'/components/input': typeof ComponentsInputRoute
'/components/kbd': typeof ComponentsKbdRoute
@@ -414,6 +431,7 @@ export interface FileRoutesById {
'/components/callout': typeof ComponentsCalloutRoute
'/components/checkbox': typeof ComponentsCheckboxRoute
'/components/collapsible': typeof ComponentsCollapsibleRoute
+ '/components/collection-controls': typeof ComponentsCollectionControlsRoute
'/components/combobox': typeof ComponentsComboboxRoute
'/components/command-menu': typeof ComponentsCommandMenuRoute
'/components/composer': typeof ComponentsComposerRoute
@@ -423,6 +441,7 @@ export interface FileRoutesById {
'/components/drawer': typeof ComponentsDrawerRoute
'/components/empty-state': typeof ComponentsEmptyStateRoute
'/components/file-upload': typeof ComponentsFileUploadRoute
+ '/components/form': typeof ComponentsFormRoute
'/components/icon-button': typeof ComponentsIconButtonRoute
'/components/input': typeof ComponentsInputRoute
'/components/kbd': typeof ComponentsKbdRoute
@@ -466,6 +485,7 @@ export interface FileRouteTypes {
| '/components/callout'
| '/components/checkbox'
| '/components/collapsible'
+ | '/components/collection-controls'
| '/components/combobox'
| '/components/command-menu'
| '/components/composer'
@@ -475,6 +495,7 @@ export interface FileRouteTypes {
| '/components/drawer'
| '/components/empty-state'
| '/components/file-upload'
+ | '/components/form'
| '/components/icon-button'
| '/components/input'
| '/components/kbd'
@@ -515,6 +536,7 @@ export interface FileRouteTypes {
| '/components/callout'
| '/components/checkbox'
| '/components/collapsible'
+ | '/components/collection-controls'
| '/components/combobox'
| '/components/command-menu'
| '/components/composer'
@@ -524,6 +546,7 @@ export interface FileRouteTypes {
| '/components/drawer'
| '/components/empty-state'
| '/components/file-upload'
+ | '/components/form'
| '/components/icon-button'
| '/components/input'
| '/components/kbd'
@@ -565,6 +588,7 @@ export interface FileRouteTypes {
| '/components/callout'
| '/components/checkbox'
| '/components/collapsible'
+ | '/components/collection-controls'
| '/components/combobox'
| '/components/command-menu'
| '/components/composer'
@@ -574,6 +598,7 @@ export interface FileRouteTypes {
| '/components/drawer'
| '/components/empty-state'
| '/components/file-upload'
+ | '/components/form'
| '/components/icon-button'
| '/components/input'
| '/components/kbd'
@@ -708,6 +733,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof ComponentsCollapsibleRouteImport
parentRoute: typeof ComponentsRouteRoute
}
+ '/components/collection-controls': {
+ id: '/components/collection-controls'
+ path: '/collection-controls'
+ fullPath: '/components/collection-controls'
+ preLoaderRoute: typeof ComponentsCollectionControlsRouteImport
+ parentRoute: typeof ComponentsRouteRoute
+ }
'/components/combobox': {
id: '/components/combobox'
path: '/combobox'
@@ -771,6 +803,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof ComponentsFileUploadRouteImport
parentRoute: typeof ComponentsRouteRoute
}
+ '/components/form': {
+ id: '/components/form'
+ path: '/form'
+ fullPath: '/components/form'
+ preLoaderRoute: typeof ComponentsFormRouteImport
+ parentRoute: typeof ComponentsRouteRoute
+ }
'/components/icon-button': {
id: '/components/icon-button'
path: '/icon-button'
@@ -960,6 +999,7 @@ interface ComponentsRouteRouteChildren {
ComponentsCalloutRoute: typeof ComponentsCalloutRoute
ComponentsCheckboxRoute: typeof ComponentsCheckboxRoute
ComponentsCollapsibleRoute: typeof ComponentsCollapsibleRoute
+ ComponentsCollectionControlsRoute: typeof ComponentsCollectionControlsRoute
ComponentsComboboxRoute: typeof ComponentsComboboxRoute
ComponentsCommandMenuRoute: typeof ComponentsCommandMenuRoute
ComponentsComposerRoute: typeof ComponentsComposerRoute
@@ -969,6 +1009,7 @@ interface ComponentsRouteRouteChildren {
ComponentsDrawerRoute: typeof ComponentsDrawerRoute
ComponentsEmptyStateRoute: typeof ComponentsEmptyStateRoute
ComponentsFileUploadRoute: typeof ComponentsFileUploadRoute
+ ComponentsFormRoute: typeof ComponentsFormRoute
ComponentsIconButtonRoute: typeof ComponentsIconButtonRoute
ComponentsInputRoute: typeof ComponentsInputRoute
ComponentsKbdRoute: typeof ComponentsKbdRoute
@@ -1008,6 +1049,7 @@ const ComponentsRouteRouteChildren: ComponentsRouteRouteChildren = {
ComponentsCalloutRoute: ComponentsCalloutRoute,
ComponentsCheckboxRoute: ComponentsCheckboxRoute,
ComponentsCollapsibleRoute: ComponentsCollapsibleRoute,
+ ComponentsCollectionControlsRoute: ComponentsCollectionControlsRoute,
ComponentsComboboxRoute: ComponentsComboboxRoute,
ComponentsCommandMenuRoute: ComponentsCommandMenuRoute,
ComponentsComposerRoute: ComponentsComposerRoute,
@@ -1017,6 +1059,7 @@ const ComponentsRouteRouteChildren: ComponentsRouteRouteChildren = {
ComponentsDrawerRoute: ComponentsDrawerRoute,
ComponentsEmptyStateRoute: ComponentsEmptyStateRoute,
ComponentsFileUploadRoute: ComponentsFileUploadRoute,
+ ComponentsFormRoute: ComponentsFormRoute,
ComponentsIconButtonRoute: ComponentsIconButtonRoute,
ComponentsInputRoute: ComponentsInputRoute,
ComponentsKbdRoute: ComponentsKbdRoute,
diff --git a/apps/docs/src/routes/__root.tsx b/apps/docs/src/routes/__root.tsx
index f7b664e..fb6aab8 100644
--- a/apps/docs/src/routes/__root.tsx
+++ b/apps/docs/src/routes/__root.tsx
@@ -43,7 +43,14 @@ export const Route = createRootRoute({
content: "Compact, accessible React components for product interfaces.",
},
],
- links: faviconLinks,
+ links: [
+ ...faviconLinks,
+ {
+ rel: "describedby",
+ href: "/llms.txt",
+ type: "text/markdown",
+ },
+ ],
}),
component: RootDocument,
});
diff --git a/apps/docs/src/routes/components/collection-controls.tsx b/apps/docs/src/routes/components/collection-controls.tsx
new file mode 100644
index 0000000..a7afe03
--- /dev/null
+++ b/apps/docs/src/routes/components/collection-controls.tsx
@@ -0,0 +1,194 @@
+import {
+ ArchiveBoxIcon,
+ EllipsisHorizontalIcon,
+} from "@heroicons/react/16/solid";
+import { createFileRoute } from "@tanstack/react-router";
+import {
+ Button,
+ FilterBar,
+ FilterPicker,
+ HoverActions,
+ IconButton,
+ MultiSelectToolbar,
+ ViewOptions,
+} from "@karnstack/dowel";
+import type {
+ DataTableDensity,
+ FilterBarFilter,
+ FilterPickerProperty,
+} from "@karnstack/dowel";
+import { useState } from "react";
+
+import { Demo } from "../../components/demo";
+import { DocsPage, Section } from "../../components/docs-page";
+
+export const Route = createFileRoute("/components/collection-controls")({
+ component: CollectionControlsDocs,
+});
+
+const toc = [
+ { id: "filters", title: "Filters and views" },
+ { id: "selection", title: "Bulk selection" },
+ { id: "row-actions", title: "Row actions" },
+];
+
+const initialFilters: FilterBarFilter[] = [
+ { id: "status", label: "Status", value: "Active" },
+ { id: "assignee", label: "Assignee", value: "Me" },
+];
+
+const filterProperties: FilterPickerProperty[] = [
+ {
+ id: "status",
+ label: "Status",
+ values: [
+ { value: "active", label: "Active" },
+ { value: "done", label: "Completed" },
+ ],
+ },
+ {
+ id: "assignee",
+ label: "Assignee",
+ values: [
+ { value: "me", label: "Me" },
+ { value: "unassigned", label: "Unassigned" },
+ ],
+ },
+];
+
+function CollectionControlsDocs() {
+ const [filters, setFilters] = useState(initialFilters);
+ const [density, setDensity] = useState
+ Keep active constraints visible and place display preferences at the
+ collection edge. Both components are controlled, so saved views can
+ own the same state.
+
+ The toolbar appears only while items are selected. Keep its actions
+ specific to the selected set and always provide a way back to the
+ unselected state.
+
+ Hover actions reserve no visual attention until the row is hovered or
+ focus moves inside it. Pass
- Rows sit directly on the page without a boxed grid. Identity stays on
- the left, the title absorbs available space, and metadata remains
- quiet on the right.
+ Compose the table with filters, display settings, and bulk actions to
+ build a complete issue view. The collection state remains controlled,
+ so it can be stored in a URL, workspace preference, or saved view.
visible when a parent row
+ tracks pointer state.
+
- Show the header to expose sorting and column resizing. Sort controls, - selection boxes, rows, and resize handles all carry visible keyboard - focus. Resize with the pointer or Left and Right Arrow, then double - click the handle to restore its original width. + Show the header to expose sorting and column resizing. Rows support + Arrow, Home, End, Space, Enter, and Shift-range selection. Resize with + the pointer or Arrow keys, then double click the handle to restore its + original width.
-+ Created “{savedTitle}” +
+ ) : null} +
+ useDowelForm keeps TanStack Form's typed state model
+ while removing the repetitive wiring between fields and Dowel
+ controls.
+
+ The adapter currently includes TextField and{" "}
+ TextareaField. Both bind value, blur, name, touched
+ validation, descriptions, and accessible error output. Use{" "}
+ withDowelForm and withDowelFieldGroup to
+ compose reusable forms without losing inferred value types.
+
- Dowel does not install a router, form library, or data-fetching
+ Dowel does not install a router, schema validator, or data-fetching
client, and it does not prescribe an application-wide icon system.
Consumer-facing icon slots accept ordinary React content, so
applications keep control of their visual language.
diff --git a/apps/docs/worker.mjs b/apps/docs/worker.mjs
new file mode 100644
index 0000000..257de15
--- /dev/null
+++ b/apps/docs/worker.mjs
@@ -0,0 +1,46 @@
+const markdownType = "text/markdown; charset=utf-8";
+const llmsLink = '; rel="describedby"; type="text/markdown"';
+
+function wantsMarkdown(request) {
+ return request.headers
+ .get("Accept")
+ ?.toLowerCase()
+ .split(",")
+ .some((value) => value.trim().startsWith("text/markdown"));
+}
+
+function markdownPath(pathname) {
+ const path = pathname.replace(/\/+$/, "") || "/";
+ if (path === "/") return "/index.md";
+ if (path === "/components") return "/components/index.md";
+ return path + ".md";
+}
+
+function withDiscoveryHeaders(response, markdown = false) {
+ const headers = new Headers(response.headers);
+ headers.set("Link", llmsLink);
+ headers.append("Vary", "Accept");
+ if (markdown) headers.set("Content-Type", markdownType);
+ return new Response(response.body, {
+ status: response.status,
+ statusText: response.statusText,
+ headers,
+ });
+}
+
+export default {
+ async fetch(request, env) {
+ const url = new URL(request.url);
+ if (request.method === "GET" && wantsMarkdown(request)) {
+ const markdownUrl = new URL(markdownPath(url.pathname), url);
+ const markdownResponse = await env.ASSETS.fetch(
+ new Request(markdownUrl, request),
+ );
+ if (markdownResponse.ok) {
+ return withDiscoveryHeaders(markdownResponse, true);
+ }
+ }
+
+ return withDiscoveryHeaders(await env.ASSETS.fetch(request));
+ },
+};
diff --git a/apps/docs/wrangler.jsonc b/apps/docs/wrangler.jsonc
index 901b59a..8fc1e51 100644
--- a/apps/docs/wrangler.jsonc
+++ b/apps/docs/wrangler.jsonc
@@ -1,13 +1,15 @@
-// dowel.sh — the docs site, served as Cloudflare Worker static assets.
-// Assets-only: no worker script, because the site is fully prerendered.
+// dowel.sh — prerendered docs with a small content-negotiation Worker.
//
// www.dowel.sh cannot be redirected from an assets-only worker; that is a
// Cloudflare dashboard Redirect Rule (manual follow-up).
{
"name": "dowel-sh",
- "compatibility_date": "2026-08-09",
+ "main": "./worker.mjs",
+ "compatibility_date": "2026-08-08",
"assets": {
"directory": "./dist",
+ "binding": "ASSETS",
+ "run_worker_first": true,
// The build emits sub-pages as directories (components/button/index.html),
// so /components/button must resolve to that file. auto-trailing-slash
// serves /components/button/ directly and 307s /components/button to it.
diff --git a/packages/dowel/package.json b/packages/dowel/package.json
index 311d97a..b963a3f 100644
--- a/packages/dowel/package.json
+++ b/packages/dowel/package.json
@@ -55,6 +55,7 @@
"@heroicons/react": "2.2.0",
"@internationalized/date": "^3.12.3",
"@stylexjs/stylex": "0.19.0",
+ "@tanstack/react-form": "^1.33.5",
"@tanstack/react-table": "^9.1.2",
"react-aria-components": "^1.20.0"
},
diff --git a/packages/dowel/src/components/badge/badge.stylex.ts b/packages/dowel/src/components/badge/badge.stylex.ts
index 7b9d733..a6a82b2 100644
--- a/packages/dowel/src/components/badge/badge.stylex.ts
+++ b/packages/dowel/src/components/badge/badge.stylex.ts
@@ -5,7 +5,7 @@ import { tokens } from "../../theme/tokens.stylex";
export const badge = stylex.create({
root: {
alignItems: "center",
- backgroundColor: tokens["--dowel-bg-surface-2"],
+ backgroundColor: tokens["--dowel-bg-badge"],
borderColor: tokens["--dowel-border-default"],
borderRadius: tokens["--dowel-radius-pill"],
borderStyle: "solid",
diff --git a/packages/dowel/src/components/data-table/data-table.stylex.ts b/packages/dowel/src/components/data-table/data-table.stylex.ts
index 1847387..39e677a 100644
--- a/packages/dowel/src/components/data-table/data-table.stylex.ts
+++ b/packages/dowel/src/components/data-table/data-table.stylex.ts
@@ -27,6 +27,12 @@ export const parts = stylex.create({
headerRow: {
backgroundColor: "transparent",
},
+ stickyHeader: {
+ backgroundColor: tokens["--dowel-bg-surface-1"],
+ insetBlockStart: 0,
+ position: "sticky",
+ zIndex: 4,
+ },
headerCell: {
boxSizing: "border-box",
color: tokens["--dowel-text-tertiary"],
@@ -41,11 +47,16 @@ export const parts = stylex.create({
verticalAlign: "middle",
whiteSpace: "nowrap",
},
+ pinnedHeaderCell: {
+ backgroundColor: tokens["--dowel-bg-surface-1"],
+ position: "sticky",
+ zIndex: 5,
+ },
headerButton: {
alignItems: "center",
appearance: "none",
backgroundColor: "transparent",
- border: 0,
+ borderStyle: "none",
borderRadius: tokens["--dowel-radius-sm"],
color: "inherit",
cursor: "pointer",
@@ -95,7 +106,7 @@ export const parts = stylex.create({
},
resizeHandle: {
backgroundColor: "transparent",
- border: 0,
+ borderStyle: "none",
cursor: "col-resize",
height: "1.25rem",
outline: "none",
@@ -144,10 +155,10 @@ export const parts = stylex.create({
transitionProperty: "background-color",
transitionTimingFunction: tokens["--dowel-ease-out"],
":hover": {
- backgroundColor: tokens["--dowel-bg-hover"],
+ backgroundColor: tokens["--dowel-bg-row-hover"],
},
":focus-visible": {
- backgroundColor: tokens["--dowel-bg-hover"],
+ backgroundColor: tokens["--dowel-bg-row-hover"],
boxShadow: `inset 0 0 0 1px ${tokens["--dowel-focus-ring"]}`,
},
"[data-selected]": {
@@ -162,11 +173,11 @@ export const parts = stylex.create({
boxSizing: "border-box",
color: tokens["--dowel-text-primary"],
fontSize: "0.8125rem",
- fontWeight: 450,
- letterSpacing: "-0.0075rem",
- lineHeight: 1.35,
+ fontWeight: 500,
+ letterSpacing: "-0.005rem",
+ lineHeight: 1.2,
paddingBlock: 0,
- paddingInline: "0.5rem",
+ paddingInline: "0.375rem",
textAlign: "left",
verticalAlign: "middle",
":first-child": {
@@ -178,8 +189,22 @@ export const parts = stylex.create({
borderTopRightRadius: tokens["--dowel-radius-md"],
},
},
+ pinnedCell: {
+ position: "sticky",
+ zIndex: 2,
+ },
+ pinnedStartEdge: {
+ borderInlineEndColor: tokens["--dowel-border-default"],
+ borderInlineEndStyle: "solid",
+ borderInlineEndWidth: tokens["--dowel-hairline"],
+ },
+ pinnedEndEdge: {
+ borderInlineStartColor: tokens["--dowel-border-default"],
+ borderInlineStartStyle: "solid",
+ borderInlineStartWidth: tokens["--dowel-hairline"],
+ },
compactCell: {
- height: tokens["--dowel-row-md"],
+ height: "2.5rem",
},
comfortableCell: {
height: "3.25rem",
@@ -198,6 +223,70 @@ export const parts = stylex.create({
selectionCell: {
paddingInline: "0.375rem",
},
+ groupRow: {
+ backgroundColor: "transparent",
+ },
+ groupCell: {
+ backgroundColor: tokens["--dowel-bg-surface-1"],
+ borderRadius: tokens["--dowel-radius-md"],
+ boxSizing: "border-box",
+ boxShadow: `inset 0 0 0 ${tokens["--dowel-hairline"]} ${tokens["--dowel-border-subtle"]}`,
+ padding: "0.125rem 0.375rem",
+ },
+ groupButton: {
+ alignItems: "center",
+ appearance: "none",
+ backgroundColor: "transparent",
+ borderStyle: "none",
+ borderRadius: tokens["--dowel-radius-sm"],
+ color: tokens["--dowel-text-secondary"],
+ cursor: "pointer",
+ display: "inline-flex",
+ fontFamily: tokens["--dowel-font-sans"],
+ fontSize: "0.75rem",
+ fontWeight: 550,
+ gap: "0.25rem",
+ height: "1.75rem",
+ maxWidth: "100%",
+ outline: "none",
+ paddingInline: "0.25rem 0.5rem",
+ ":hover": {
+ backgroundColor: tokens["--dowel-bg-hover"],
+ color: tokens["--dowel-text-primary"],
+ },
+ ":focus-visible": {
+ boxShadow: `inset 0 0 0 1px ${tokens["--dowel-focus-ring"]}`,
+ color: tokens["--dowel-text-primary"],
+ },
+ },
+ groupChevron: {
+ alignItems: "center",
+ display: "inline-flex",
+ flexShrink: 0,
+ height: "0.75rem",
+ justifyContent: "center",
+ width: "0.75rem",
+ },
+ groupChevronExpanded: {
+ transform: "rotate(90deg)",
+ },
+ groupChevronGlyph: {
+ backgroundColor: "currentColor",
+ clipPath: "polygon(15% 10%, 85% 50%, 15% 90%)",
+ display: "block",
+ height: "0.5rem",
+ width: "0.4375rem",
+ },
+ groupLabel: {
+ overflow: "hidden",
+ textOverflow: "ellipsis",
+ whiteSpace: "nowrap",
+ },
+ groupCount: {
+ color: tokens["--dowel-text-tertiary"],
+ fontSize: "0.6875rem",
+ fontWeight: 450,
+ },
alignCenter: {
justifyContent: "center",
textAlign: "center",
@@ -264,7 +353,7 @@ export const parts = stylex.create({
},
emptyCell: {
color: tokens["--dowel-text-secondary"],
- padding: "3.5rem 1rem",
+ padding: "2.5rem 1rem",
textAlign: "center",
},
emptyTitle: {
@@ -287,6 +376,7 @@ export const parts = stylex.create({
animationTimingFunction: "ease-in-out",
backgroundColor: tokens["--dowel-bg-surface-3"],
borderRadius: tokens["--dowel-radius-pill"],
+ display: "block",
height: "0.375rem",
maxWidth: "12rem",
width: "68%",
diff --git a/packages/dowel/src/components/data-table/data-table.test.tsx b/packages/dowel/src/components/data-table/data-table.test.tsx
index 48b5fc8..006d353 100644
--- a/packages/dowel/src/components/data-table/data-table.test.tsx
+++ b/packages/dowel/src/components/data-table/data-table.test.tsx
@@ -70,6 +70,13 @@ describe("DataTable", () => {
).toBe("ascending");
});
+ it("sorts text columns with the registered automatic comparator", async () => {
+ render( No matches No matches {
+ if (node) rowRefs.current.set(row.id, node);
+ else rowRefs.current.delete(row.id);
+ }}
+ {...stylex.props(styles.parts.row)}
+ data-row-id={row.id}
+ data-selected={row.getIsSelected() ? "" : undefined}
+ data-actionable={onRowActivate ? "" : undefined}
+ tabIndex={
+ rowIsFocusable ? (activeRowId === row.id ? 0 : -1) : undefined
+ }
+ onBlurCapture={handleRowBlur}
+ onClick={(event) => triggerRow(event, row.original, onRowActivate)}
+ onFocusCapture={() => {
+ setActiveRowId(row.id);
+ setActiveActionsRowId(row.id);
+ }}
+ onKeyDown={(event) =>
+ keyboardNavigation
+ ? handleRowKeyDown(event, row)
+ : triggerRow(event, row.original, onRowActivate)
+ }
+ onMouseEnter={() => setActiveActionsRowId(row.id)}
+ onMouseLeave={() =>
+ setActiveActionsRowId((current) =>
+ current === row.id ? null : current,
+ )
+ }
+ >
+ {row.getVisibleCells().map((cell) => {
+ const meta = cell.column.columnDef.meta;
+ const pinned = cell.column.getIsPinned();
+ return (
+
+ );
+ }
+
return (
+
+ );
+ })}
+ (
))}
{showHeader ? (
-
+
{table.getHeaderGroups().map((headerGroup) => (
(
alignmentStyle(meta?.align),
header.column.id === "__dowel_selection" &&
styles.parts.selectionCell,
+ header.column.getIsPinned() &&
+ styles.parts.pinnedHeaderCell,
+ header.column.getIsPinned() === "start" &&
+ header.column.getIsLastColumn("start") &&
+ styles.parts.pinnedStartEdge,
+ header.column.getIsPinned() === "end" &&
+ header.column.getIsFirstColumn("end") &&
+ styles.parts.pinnedEndEdge,
)}
+ style={pinnedPositionStyle(header.column)}
scope="col"
aria-sort={
sorted === "asc"
@@ -499,55 +932,65 @@ export const DataTable = forwardRef(function DataTable
),
)
- : rows.map((row) => (
-
- triggerRow(event, row.original, onRowActivate)
- }
- onKeyDown={(event) =>
- triggerRow(event, row.original, onRowActivate)
- }
- >
- {row.getVisibleCells().map((cell) => {
- const meta = cell.column.columnDef.meta;
- return (
+ : groupedRows.map((entry, index) => {
+ if (!entry.group) {
+ return (
+
+ {collapsed ? null : entry.rows.map(renderDataRow)}
+
+ );
+ })}
{!loading && rows.length === 0 ? (
- ))}
+
-
- );
- })}
-
{
+ it("edits, removes, and clears filters", async () => {
+ const onFilterClick = vi.fn();
+ const onRemove = vi.fn();
+ const onClear = vi.fn();
+ const { container } = render(
+