entasis 0.9.2 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/dist/components/AppShell/appShell.theme.js +3 -3
  2. package/dist/components/ButtonGroup/ButtonGroup.svelte +10 -4
  3. package/dist/components/ButtonGroup/buttonGroup.mcp.d.ts +1 -1
  4. package/dist/components/ButtonGroup/buttonGroup.mcp.js +23 -3
  5. package/dist/components/ButtonGroup/buttonGroup.props.d.ts +19 -5
  6. package/dist/components/Chart/Chart.svelte +1 -1
  7. package/dist/components/Chart/chart.cartesian.js +3 -2
  8. package/dist/components/Chart/chart.fit.d.ts +53 -0
  9. package/dist/components/Chart/chart.fit.js +81 -0
  10. package/dist/components/Chart/chart.mcp.d.ts +1 -1
  11. package/dist/components/Chart/chart.mcp.js +2 -1
  12. package/dist/components/Chart/chart.polar.js +137 -18
  13. package/dist/components/Chart/chart.polar.props.d.ts +5 -0
  14. package/dist/components/Chart/chart.proportion.js +12 -6
  15. package/dist/components/Chart/chart.relation.network.js +32 -4
  16. package/dist/components/Chart/chart.relation.sankey.js +53 -6
  17. package/dist/components/Chart/chart.relation.tree.js +84 -24
  18. package/dist/components/Chart/chart.series.props.d.ts +6 -0
  19. package/dist/components/Chart/chart.state.svelte.d.ts +1 -0
  20. package/dist/components/Chart/chart.state.svelte.js +9 -6
  21. package/dist/components/Chart/chart.viewport.svelte.d.ts +1 -0
  22. package/dist/components/Chart/chart.viewport.svelte.js +25 -7
  23. package/dist/components/DataTable/DataTable.svelte +25 -8
  24. package/dist/components/DataTable/DataTableRow.svelte +33 -10
  25. package/dist/components/DataTable/dataTable.mcp.d.ts +1 -1
  26. package/dist/components/DataTable/dataTable.mcp.js +12 -1
  27. package/dist/components/DataTable/dataTable.model.svelte.js +4 -3
  28. package/dist/components/DataTable/dataTable.props.d.ts +17 -0
  29. package/dist/components/DataTable/dataTable.theme.d.ts +12 -0
  30. package/dist/components/DataTable/dataTable.theme.js +3 -2
  31. package/dist/components/DataTable/index.d.ts +1 -1
  32. package/dist/components/Dialog/Dialog.svelte +12 -7
  33. package/dist/components/Dialog/dialog.state.svelte.d.ts +2 -1
  34. package/dist/components/Dialog/dialog.state.svelte.js +7 -7
  35. package/dist/components/Dialog/dialog.theme.js +1 -1
  36. package/dist/components/FloatingWindow/floatingWindow.theme.js +1 -1
  37. package/dist/components/Form/Select/Select.svelte +3 -1
  38. package/dist/components/PageShell/pageShell.state.svelte.d.ts +1 -1
  39. package/dist/components/PageShell/pageShell.state.svelte.js +6 -13
  40. package/dist/components/Popover/Popover.svelte +5 -5
  41. package/dist/components/Popover/index.d.ts +1 -1
  42. package/dist/components/Popover/popover.mcp.d.ts +1 -1
  43. package/dist/components/Popover/popover.mcp.js +33 -6
  44. package/dist/components/Popover/popover.state.svelte.d.ts +9 -1
  45. package/dist/components/Popover/popover.state.svelte.js +61 -8
  46. package/dist/components/Popover/popover.theme.js +1 -1
  47. package/dist/components/Sidebar/Sidebar.svelte +1 -1
  48. package/dist/components/Sidebar/SidebarDesktopShell.svelte +26 -6
  49. package/dist/components/Sidebar/sidebar-layout.js +7 -6
  50. package/dist/components/Sidebar/sidebar.mcp.d.ts +1 -1
  51. package/dist/components/Sidebar/sidebar.mcp.js +2 -2
  52. package/dist/components/Sidebar/sidebar.theme.js +36 -20
  53. package/dist/components/Theme/index.d.ts +1 -1
  54. package/dist/components/Theme/index.js +1 -1
  55. package/dist/components/Theme/theme.mcp.d.ts +1 -1
  56. package/dist/components/Theme/theme.mcp.js +3 -0
  57. package/dist/components/Theme/theme.state.svelte.d.ts +4 -0
  58. package/dist/components/Theme/theme.state.svelte.js +4 -0
  59. package/dist/components/Tooltip/Tooltip.svelte +2 -4
  60. package/dist/components/Tooltip/tooltip.attachment.svelte.js +5 -0
  61. package/dist/components/Tooltip/tooltip.mcp.d.ts +1 -1
  62. package/dist/components/Tooltip/tooltip.mcp.js +4 -2
  63. package/dist/generated/componentAliases.d.ts +1 -0
  64. package/dist/generated/componentAliases.js +1 -0
  65. package/dist/generated/componentContract.d.ts +13 -3
  66. package/dist/generated/componentContract.js +15 -1
  67. package/dist/generated/componentMcpRegistry.d.ts +8 -7
  68. package/dist/generated/componentMcpRegistry.js +2 -0
  69. package/dist/i18n/ar.js +1 -1
  70. package/dist/i18n/de.js +1 -1
  71. package/dist/i18n/en.js +1 -1
  72. package/dist/i18n/es.js +1 -1
  73. package/dist/i18n/fr.js +1 -1
  74. package/dist/i18n/pt.js +1 -1
  75. package/dist/i18n/zh.js +1 -1
  76. package/dist/tailwind/colors.d.ts +10 -3
  77. package/dist/tailwind/colors.js +6 -0
  78. package/dist/tailwind/palette.d.ts +5 -0
  79. package/dist/tailwind/palette.js +4 -0
  80. package/dist/tailwind/palette.mcp.d.ts +1 -0
  81. package/dist/tailwind/palette.mcp.js +34 -0
  82. package/dist/utils/layers.svelte.js +9 -8
  83. package/dist/utils/registry.svelte.d.ts +21 -0
  84. package/dist/utils/registry.svelte.js +49 -0
  85. package/package.json +8 -1
@@ -25,9 +25,9 @@ export declare const componentMcpRegistry: {
25
25
  readonly stack: "\n# Stack\n\nStack arranges arbitrary content along one flex axis. Use the `orientation` prop to switch\nbetween horizontal and vertical layout, and `align` / `justify` for cross-axis and main-axis\nalignment.\n\n## Import\n\n```svelte\n<script lang=\"ts\">\n import { Stack } from '../components/Stack/index.ts';\n</script>\n```\n\n## Usage\n\n```svelte\n<Stack gap=\"xl\" padding=\"xl\">\n <h2>Account</h2>\n <Stack orientation=\"horizontal\" align=\"center\" gap=\"md\" wrap=\"wrap\">\n <span>Profile</span>\n <span>Security</span>\n </Stack>\n</Stack>\n```\n\n## Responsive props\n\n`orientation`, `gap`, `align`, `justify` and `wrap` each take a plain value or a\nper-breakpoint record:\n\n```svelte\n<Stack orientation={{ md: 'horizontal' }} gap={{ xs: 'sm', lg: 'xl' }} align=\"center\">\n <span>Filters</span>\n <span>Results</span>\n</Stack>\n```\n\nThe breakpoints measure the stack's OWN width — `xs` base, `sm` 36rem, `md` 42rem,\n`lg` 56rem, `xl` 72rem — not the viewport's, so the same stack is `xs` in a narrow sidebar\nand `lg` full-bleed on the same page. The nearest defined key at or below the stack's width\nwins, and below the narrowest key the prop's default applies: `{ md: 'horizontal' }` is\nvertical at `xs` and `sm`. A prop falls back to its default only when it is `undefined`\nor `null`. A function form must be deterministic in its argument — it is called once per\nbreakpoint. There is no measurement and no JS: the five values ship as custom\nproperties and container queries pick one, so server-rendered markup is already laid out.\n\n## Props\n\n- `orientation`: `'horizontal' | 'vertical'` — flex direction (default: `'vertical'`).\n- `align`: cross-axis alignment — `start | center | end | stretch` (default: `'stretch'`).\n- `justify`: main-axis alignment — `start | center | end | between | around | evenly` (default: `'start'`).\n- `gap`, `padding`, `paddingInline`, and `paddingBlock` accept\n `none | xs | sm | md | lg | xl`.\n- `paddingInline` and `paddingBlock` override `padding` on their axis.\n- `width`, `height`, `maxWidth`, and `minHeight` accept CSS strings or pixel numbers.\n- `wrap` accepts `nowrap | wrap | wrap-reverse`.\n- `scrollable` enables native `overflow: auto`.\n- `as` changes the semantic HTML element without changing layout behavior:\n `div | span | section | article | aside | main | nav | header | footer | form | fieldset`.\n Lists are not among them — the root always wraps its children in one layout `<div>`, which\n `<ul>` and `<ol>` do not admit. Write the list yourself and put a stack inside an `<li>`.\n\n## Structure\n\nStack renders two elements: the root (`data-slot=\"stack\"`) is the box the host sizes and the\ncontainer the breakpoints are measured against — it takes `as`, `class`, `style`, the size\nprops, the padding and `scrollable` — and a single layout child (`data-slot=\"stack-layout\"`)\ncarries the flex line. The `inner` theme slot styles that child.\n\nStack forwards common semantic HTML attributes and Svelte attachments to the root element.\nPrefer parent-owned `gap` over child margins. The internal `micro` and `layout-*` tokens are\nreserved for component recipes and must not be used in generated interfaces.\n";
26
26
  readonly 'app-shell': "\n# AppShell Component\n\nConvenience wrapper for the common application layout: Sidebar owns navigation,\nresponsive drawer behavior, the application wall, and variant surfaces, while PageShell\nowns the page header, document-flow content, footer, and route-level injection. AppShell\nforwards one shared variant to Sidebar and composes PageShell inside it.\nAppShell establishes a dynamic viewport-height minimum while letting the document own\nvertical scrolling. The desktop sidebar remains sticky independently of page content.\n\nUse AppShell when every route follows the same sidebar + page shell structure. Use\nSidebar and PageShell directly when the frame needs custom composition.\n\n## Basic Usage\n\n```svelte\n<script lang=\"ts\">\n\timport { AppShell, type AppShellSidebarProps } from 'entasis/app-shell';\n\timport { houseIcon } from 'entasis/icons/house';\n\n\tconst sidebar: AppShellSidebarProps = {\n\t\tcollapsible: 'icon',\n\t\trail: true,\n\t\titems: [\n\t\t\t{\n\t\t\t\tlabel: 'Workspace',\n\t\t\t\titems: [{ label: 'Home', href: '/', icon: houseIcon, isActive: true }]\n\t\t\t}\n\t\t]\n\t};\n</script>\n\n<AppShell variant=\"framed\" {sidebar} title=\"Dashboard\" subtitle=\"Operational overview\">\n\t{#snippet children({ sidebar })}\n\t\t<button type=\"button\" onclick={sidebar.toggle}>Toggle sidebar</button>\n\t{/snippet}\n</AppShell>\n```\n\n## Route-Level Injection\n\nAppShell renders PageShell internally, so child pages can use the PageShell context API:\n\n```svelte\n<script lang=\"ts\">\n\timport { setPageShell } from 'entasis/page-shell';\n\n\tsetPageShell({\n\t\ttitle: 'Insights',\n\t\tsubtitle: 'Revenue and retention',\n\t\tfooter: pageFooter\n\t});\n</script>\n\n{#snippet pageFooter()}\n\t<span>Synced just now</span>\n{/snippet}\n```\n\n## Props\n\n- **variant**: 'admin' | 'floating' | 'inset' | 'split' | 'framed' - Shared shell treatment forwarded to Sidebar and PageShell chrome. Admin chrome uses the Sidebar canvas surface; floating chrome uses detached raised, rounded surfaces. `framed` draws one rounded card (`rounded-xl` + `raised-1`, so the border and elevation come from the elevation engine) around both the sidebar and the page; the Sidebar's own `framed` variant paints its navigation well as `surface-recessed`, an inset of that card, and the page header sits on the page surface.\n- **sidebar**: AppShellSidebarProps - Sidebar props except `children`, `mode`, `frame`, and `variant`.\n Configure Sidebar `size` and `density` independently inside this object.\n- **eyebrow**: string | Snippet<[PageShellApi]> - Small metadata above the PageShell title.\n- **breadcrumbs**: BreadcrumbItem[] | Snippet<[AppShellApi]> - PageShell breadcrumbs.\n- **breadcrumbsMaxItems**: number - Maximum visible breadcrumb items before ellipsis. Defaults to 4.\n- **back**: PageShellAction | Snippet<[AppShellApi]> - Back affordance before breadcrumbs or eyebrow.\n- **title**: string | Snippet<[PageShellApi]> - Default PageShell title.\n- **subtitle**: string | Snippet<[PageShellApi]> - Default PageShell subtitle.\n- **header**: Snippet<[PageShellApi]> - Custom PageShell header.\n- **headerActions**: Snippet<[AppShellApi]> | PageShellAction[] - Actions in the default PageShell header. Use an array for standard Button props, or a snippet when the action needs sidebar/page-shell API access.\n- **footer**: Snippet<[PageShellApi]> - PageShell footer.\n- **footerActions**: Snippet<[AppShellApi]> | PageShellAction[] - PageShell footer actions.\n- **children**: Snippet<[AppShellApi]> - Main content, with `pageShell` and `sidebar` APIs.\n- **contentPadding**: 'none' | 'small' | 'normal' | 'large' - PageShell content padding preset.\n- **contentWidth**: 'full' | 'narrow' | 'normal' | 'wide' | 'prose' - PageShell content width preset.\n- **pageShellTheme**: PageShellThemeProps - PageShell theme overrides.\n- **theme**: AppShellThemeProps - AppShell `root`, `frame` and `page` surface-token overrides. Sidebar owns the wall and shell geometry.\n\n## Accessibility\n\nAppShell delegates navigation semantics to Sidebar and page landmarks to PageShell.\nUse string `title` for the default `h1`, or preserve heading semantics when replacing\nthe PageShell header with a custom snippet.\n";
27
27
  readonly 'page-shell': "\n# PageShell Component\n\nContent shell for pages rendered inside an application frame. PageShell provides a\nsticky header and footer, document-flow content, title/subtitle props, and a context\nAPI for child routes to inject shell content. Scrolling stays on the document by default,\nso browser navigation and scroll restoration keep their native behavior.\n\nUse PageShell inside `Sidebar.children` when Sidebar owns navigation and responsive\ndrawer behavior.\n\n## Basic Usage\n\n```svelte\n<script lang=\"ts\">\n\timport { PageShell, type PageShellAction } from 'entasis/page-shell';\n\timport { downloadSimpleIcon } from 'entasis/icons/downloadSimple';\n\n\tconst headerActions = [\n\t\t{\n\t\t\tcontent: 'Export',\n\t\t\tcolor: 'primary',\n\t\t\tprefix: downloadSimpleIcon\n\t\t}\n\t] satisfies PageShellAction[];\n</script>\n\n<PageShell title=\"Insights\" subtitle=\"Live account health\" {headerActions}>\n\t{#snippet footer()}\n\t\t<span>Updated just now</span>\n\t{/snippet}\n\n\t{#snippet children()}\n\t\t<section class=\"p-6\">Page content</section>\n\t{/snippet}\n</PageShell>\n```\n\n## Route-Level Injection\n\nChild pages can set header and footer content through context. Use `setPageShell`\nduring component initialization for automatic cleanup.\n\n```svelte\n<script lang=\"ts\">\n\timport { setPageShell } from 'entasis/page-shell';\n\n\tsetPageShell({\n\t\ttitle: 'Revenue',\n\t\tsubtitle: 'Segment breakdown',\n\t\theaderActions: revenueActions,\n\t\tfooter: revenueFooter\n\t});\n</script>\n\n{#snippet revenueActions()}\n\t<button type=\"button\">Refresh</button>\n{/snippet}\n\n{#snippet revenueFooter()}\n\t<span>Synced 2 minutes ago</span>\n{/snippet}\n```\n\n## Props\n\n- **eyebrow**: string | Snippet<[PageShellApi]> - Small metadata above the title. Ignored when breadcrumbs are set.\n- **breadcrumbs**: BreadcrumbItem[] | Snippet<[PageShellApi]> - Default-header breadcrumbs.\n- **breadcrumbsMaxItems**: number - Maximum visible breadcrumb items before ellipsis. Defaults to 4.\n- **back**: PageShellAction | Snippet<[PageShellApi]> - Back affordance before breadcrumbs or eyebrow.\n- **title**: string | Snippet<[PageShellApi]> - Default header title.\n- **subtitle**: string | Snippet<[PageShellApi]> - Default header subtitle.\n- **header**: Snippet<[PageShellApi]> - Custom sticky header content.\n- **headerActions**: Snippet<[PageShellApi]> | PageShellAction[] - Actions on the right side of the default header. Use an array for standard Button props, or a snippet when the action needs shell API access.\n- **footer**: Snippet<[PageShellApi]> - Custom sticky footer content.\n- **footerActions**: Snippet<[PageShellApi]> | PageShellAction[] - Actions on the right side of the sticky footer.\n\t- **children**: Snippet<[PageShellApi]> - Page content rendered in normal document flow.\n\t- **contentPadding**: 'none' | 'small' | 'normal' | 'large' - Padding applied to the content inner wrapper.\n\t- **contentWidth**: 'full' | 'narrow' | 'normal' | 'wide' | 'prose' - Max-width preset for the content inner wrapper.\n\t- **actionOverflow**: 'auto' | 'never' - Mobile overflow behavior for action arrays.\n\t- **mobileActionCount**: 0 | 1 | 2 - Number of action-array buttons kept inline on mobile.\n\t- **label**: string - Accessible name for the page's `main` landmark, applied as aria-label.\n\t- **theme**: PageShellThemeProps - Per-instance theme overrides.\n\n## API\n\n- **usePageShell()** returns the current PageShell API and throws when no PageShell exists.\n- **setPageShell(config)** registers a scoped config override and removes it on component destroy.\n- **api.set(config)** pushes a manual override and returns a cleanup function.\n- **api.setFooterActions(actions)** pushes scoped page footer actions.\n- **api.reset()** clears all scoped overrides.\n\n## Header Action Arrays\n\n```svelte\n<script lang=\"ts\">\n\timport { PageShell, type PageShellAction } from 'entasis/page-shell';\n\timport { arrowClockwiseIcon } from 'entasis/icons/arrowClockwise';\n\n\tconst headerActions = [\n\t\t{\n\t\t\tlabel: 'Refresh',\n\t\t\tsquared: true,\n\t\t\tvariant: 'outline',\n\t\t\tprefix: arrowClockwiseIcon\n\t\t},\n\t\t{\n\t\t\tcontent: 'Create report',\n\t\t\tcolor: 'primary'\n\t\t}\n\t] satisfies PageShellAction[];\n</script>\n\n<PageShell title=\"Reports\" {headerActions}>\n\t{#snippet children()}\n\t\tPage content\n\t{/snippet}\n</PageShell>\n```\n\n## Content Presets And Footer Actions\n\n```svelte\n<PageShell\n\teyebrow=\"Settings\"\n\ttitle=\"Billing profile\"\n\tcontentPadding=\"normal\"\n\tcontentWidth=\"narrow\"\n\tfooterActions={[\n\t\t{ content: 'Cancel', variant: 'outline' },\n\t\t{ content: 'Save changes', color: 'primary' }\n\t]}\n>\n\t{#snippet footer()}\n\t\t<span>2 unsaved changes</span>\n\t{/snippet}\n\n\t{#snippet children()}\n\t\t<form>...</form>\n\t{/snippet}\n</PageShell>\n```\n\n## Accessibility\n\nPageShell renders semantic `header`, `main`, and `footer` regions. The title is an\n`h1` when provided as a string. Custom snippets are responsible for preserving\nequivalent semantics when replacing the default header.\n";
28
- readonly sidebar: "\n# Sidebar Component\n\nSidebar navigation with data-driven groups, icon collapse, mobile drawer behavior,\nrecursive tree groups, header/footer rows, search, actions, and snippet escape hatches.\n\n## Import\n\n```svelte\n<script lang=\"ts\">\n\timport { Sidebar, type SidebarGroup } from 'entasis/sidebar';\n</script>\n```\n\n## Basic Usage\n\n```svelte\n<script lang=\"ts\">\n\timport { Sidebar, type SidebarGroup } from 'entasis/sidebar';\n\timport { houseIcon } from 'entasis/icons/house';\n\timport { gearIcon } from 'entasis/icons/gear';\n\n\tconst items: SidebarGroup[] = [\n\t\t{\n\t\t\tlabel: 'Workspace',\n\t\t\titems: [\n\t\t\t\t{ label: 'Dashboard', href: '/', icon: houseIcon, isActive: true },\n\t\t\t\t{ label: 'Settings', href: '/settings', icon: gearIcon }\n\t\t\t]\n\t\t}\n\t];\n</script>\n\n<Sidebar items={items}>\n\t{#snippet children({ toggle })}\n\t\t<header>\n\t\t\t<button type=\"button\" onclick={toggle}>Toggle</button>\n\t\t</header>\n\t\t<main>Page content</main>\n\t{/snippet}\n</Sidebar>\n```\n\n## AI-Safe Usage Contract\n\n1. Use `items` for normal navigation. Use `content` only when data-driven rows cannot express the layout.\n2. Use local Entasis icon snippets such as `houseIcon`, not Lucide component constructors.\n3. Use `MenuItem[]` from `entasis/menu` for `menu` and action dropdowns.\n4. Do not combine `menu` with `href` or `onclick` on the same row; use `action` for a trailing row menu.\n5. Keep `children`, `header`, `content`, `footer`, `banner`, and action snippets pure; they receive `SidebarApi`.\n6. Use `collapsible=\"icon\"` for icon rail behavior, `collapsible=\"offcanvas\"` for hidden desktop panels, and `collapsible=\"none\"` for fixed sidebars. Icon collapse automatically falls back to offcanvas when any data-driven row lacks an icon.\n7. Offcanvas sidebars reveal over the content from the screen edge by default when hidden; set `edgeReveal={false}` to disable that. A revealed hidden sidebar keeps its resize handle and dismisses through a small rectangular pointer tolerance.\n8. Set `keyboardShortcut={false}` when embedding Sidebar inside another shortcut-heavy surface.\n9. Sidebar owns navigation, resize mechanics, the lower application wall, and variant surface geometry. AppShell forwards its variant and composes PageShell inside that surface.\n10. Use `size` for typography, icon scale, and item height. Use `density` independently for section padding, gaps, and submenu spacing.\n11. Use `activityBar` for a persistent icon rail outside the panel (section switching, workspaces). Every item needs an `icon` and a `label`; the label is the accessible name and the tooltip. It is layout mode only: `mode=\"panel\"` renders the navigation panel alone. The rail does not change the panel by itself: keep the selected rail item in state from `onSelect`, mark it `isActive`, and either derive the Sidebar's `items` from it or, for an animated switch, give each rail item a view in `views` and set `view` from it.\n12. Use `expandOnHover` only with `collapsible=\"icon\"`. It is a temporary peek, not a toggle: the persisted collapsed state never changes, while the peeked panel renders with expanded semantics.\n13. Use `views` when the panel's contents change as the user moves through the app: sections switched from the activity bar or the route, and nested menus that open inside the panel. Key each view, set `parent` on nested ones, open them with a row's `view`, and drive `view` from state or the URL. Do not hand-animate `items` swaps.\n\n## Data Model\n\n### SidebarGroup\n- **label**: string - Group label, hidden in icon-collapsed mode.\n- **items**: SidebarMenuEntry[] - Menu rows.\n- **tree**: SidebarTreeNode[] - Recursive tree rows instead of menu items.\n- **action**: SidebarMenuActionDescriptor | SidebarMenuActionDescriptor[] | Snippet<[SidebarApi]> - Top-right group actions. Pass an array to pin several affordances (a `+` and a drag handle) to one group header; each renders as its own icon-only ghost button.\n- **collapsible**: boolean - Makes the group label a toggle.\n- **defaultOpen**: boolean - Initial collapsible group state.\n- **separator**: boolean - Divider before the group.\n\n### SidebarMenuEntry\n- **label**: string - Visible row label.\n- **icon**: SidebarIcon - Entasis icon snippet or string.\n- **iconColor**: Colors - Role tint for the leading icon, applied through `data-color`.\n- **iconVariant**: 'bare' | 'tile' - Leading icon treatment. `tile` paints a rounded square (`bg-color-muted text-color-muted-readable`) around the glyph, so per-project colour chips come from the role scale instead of hand-built markup.\n- **href**: string - Render as an anchor. Mutually exclusive with menu.\n- **onclick**: (event: MouseEvent) => void - Native click handler for button or anchor rows. Mutually exclusive with menu.\n- **isActive**: boolean - Adds active styling and `aria-current=\"page\"`.\n- **disabled**: boolean - Disables button rows and marks anchor rows disabled.\n- **badge**: string | number - Trailing count/status, hidden in icon mode.\n- **tooltip**: string - Entasis Tooltip content in icon mode. Defaults to label.\n- **items**: SidebarMenuSubEntry[] - Inline nested menu.\n- **collapsible**: boolean - Set false for an always-open submenu.\n- **defaultOpen**: boolean - Initial nested menu state.\n- **menu**: MenuItem[] - Popup menu opened from the full row. Mutually exclusive with href/onclick.\n- **view**: string - Key of the `views` entry the row opens, sliding it in; the row shows a trailing chevron. Mutually exclusive with href, menu and items.\n- **action**: SidebarMenuActionDescriptor | Snippet<[SidebarApi]> - Hover/focus trailing action.\n\n### SidebarView\nOne named panel content in `views`.\n- **label**: string - The view's name, shown on the back row of the views nested under it.\n- **parent**: string - Key of the view this one is nested under. A nested view opens with a back row to its parent (named \"Back, <parent label>\"), and on mobile a swipe toward the inline end goes back. A view without `parent` is a top-level section.\n- **items**: SidebarGroup[] / **content**: Snippet<[SidebarApi]> - The view's body.\n- **headerButton**, **search**, **headerMenu**, **header**, **footerButton**, **footerMenu**, **footer** - Header and footer props for this view. Each one left undefined comes from the parent view, then from the Sidebar's own prop; `null` removes an inherited one.\n\nChanging the view slides the two views side by side, like pages: a deeper view comes in from the inline end while the old one leaves to the start, a shallower one slides back the other way, and between views at the same depth (sections) the later one in `views` counts as forward. When both views get every header and footer prop from the same place, the header and footer stay still and only the menu slides; when a view changes any of them, the whole panel slides as one page. `api.view` reads the current view and `api.setView(key)` changes it from a snippet.\n\n### SidebarActivityBar\nIcon rail pinned to the outer edge of the sidebar, visible in every display state.\n- **items**: SidebarActivityBarItem[] - Items rendered from the top.\n- **footerItems**: SidebarActivityBarItem[] - Items pinned to the end of the column.\n- **header** / **footer**: Snippet - Custom content before the first item and after the pinned ones.\n- **width**: string (default '3rem') - Column thickness, published as `--sidebar-width-activity`.\n- **label**: string - Accessible name for the column landmark. Set it whenever the panel also renders navigation.\n- **onSelect**: ({ item, index }) => void - Fires after an item is activated. `index` counts `items` then `footerItems`.\n\n### SidebarActivityBarItem\n- **icon**: SidebarIcon (required) - Icon rendered in the square.\n- **label**: string (required) - Accessible name and default tooltip; the square shows no text.\n- **id**: string - Stable render key.\n- **href** / **target** / **rel** - Render an anchor instead of a button.\n- **onclick**: (event: MouseEvent) => void - Native click handler.\n- **isActive**: boolean - Adds active styling and `aria-current=\"page\"`.\n- **badge**: string | number | Snippet - Corner badge pinned to the outer top corner. An empty string renders a bare dot. A string or number badge joins the accessible name (`\"Alerts, 3\"`); a dot and a Snippet badge are decorative, so put their meaning in `label`.\n- **disabled**: boolean - Blocks activation and skips the item during keyboard navigation.\n- **tooltip**: string | false - Tooltip override; `false` suppresses it.\n\n### SidebarMenuButtonItem\nUse for `headerButton`, `footerButton`, or direct `<SidebarMenuButton />` rows.\n- **icon**: SidebarIcon - Leading logo/icon.\n- **avatar**: { src?: string; alt?: string; fallback?: string } - Leading avatar.\n- **variant**: 'default' | 'brand' | 'compact'.\n- **title**: string - Primary text.\n- **subtitle**: string - Secondary text.\n- **trailing**: SidebarIcon | false | SidebarMenuActionDescriptor - Trailing content. An icon is decorative; an action descriptor (`{ icon, label, onclick }`, the same shape as a group action) renders its own icon-only ghost button beside the row, so a workspace card can carry its own collapse control without a custom `header` snippet. Descriptor handlers are `onclick(event, api)`, so `api.toggle()` is reachable.\n- **href** / **onclick** / **menu** - Choose link, button, or popup behavior. `onclick(event, api)` receives the SidebarApi beside the event, so `api.toggle()` is reachable from the row itself.\n- **menuIconClass**: string - Class override for option icons inside the popup menu.\n\n## Props\n\n### State\n- **open**: boolean (bindable, default true) - Desktop expanded state.\n- **defaultOpen**: boolean (default true) - Initial desktop state when `open` is omitted.\n- **onOpenChange**: (open: boolean) => void - Called once for a library-originated desktop state change. Repeated requests and parent prop updates stay silent.\n- **onDisplayStateChange**: (state: SidebarDisplayState) => void - Called once for a library-originated semantic display-state change.\n- **view**: string (bindable) - Key of the view on screen when `views` is set. Defaults to `defaultView`, then the first view.\n- **defaultView**: string - Initial view when `view` is omitted.\n- **onViewChange**: (view: string) => void - Called once for a library-originated view change: a view row, a back row, a swipe. Parent prop updates stay silent; with a route-driven `view`, navigate here.\n- **api.displayState**: 'expanded' | 'collapsed' | 'hidden' - Semantic desktop state; hidden means closed offcanvas. A hover peek does not change it.\n- **api.isPeeking**: boolean - True while a hover peek renders the collapsed panel at full width. Read it alongside `displayState` when a snippet hides content in icon mode.\n- **keyboardShortcut**: string | false (default 'b') - Ctrl/Cmd shortcut key.\n\n### Layout\n- **side**: 'left' | 'right' - Desktop and mobile side.\n- **variant**: 'admin' | 'floating' | 'inset' | 'split' | 'framed' - Sidebar geometry. `framed` is the admin geometry for a sidebar hosted inside a raised card (AppShell variant framed), with a `surface-recessed` well. `admin` renders the conventional full-height navigation column; `inset` integrates navigation into the lower wall with an inset content surface; `split` renders detached sidebar and content surfaces.\n- **size**: 'small' | 'normal' | 'large' (default 'normal') - Typography, icon, avatar, badge, leading-media, item-height, and search-height scale.\n- **iconSize**: 'small' | 'normal' | 'large' - Icon and leading-media scale inside the panel on its own; defaults to `size`. Menu rows, sub rows, group labels and the header button follow it; the activity bar keeps `size`.\n- **activeVariant**: 'soft' | 'outline' | 'solid' (default 'soft') - How active rows are painted. `soft` is the shared selected recipe (`selectedSoft`: `bg-selected-muted text-selected-muted-readable`), `solid` its loud counterpart (`selectedSolid`: `bg-selected text-selected-contrast`), `outline` a bordered surface card (`bg-surface border border-neutral-muted text-neutral`) that reads as a raised card on a tinted well. Each row carries the choice as `data-active-variant`, so no descendant selector is needed to restyle selection.\n- **density**: 'compact' | 'normal' | 'comfortable' (default 'normal') - Section padding, group padding, gaps, horizontal inset, and submenu spacing.\n- **collapsible**: 'offcanvas' | 'icon' | 'none' - Collapse behavior. Icon mode requires icons on every data-driven row and otherwise resolves to offcanvas.\n- **collapseIcon**: DisclosureIndicator — 'chevron' | 'plus-minus' | 'none' (default 'chevron') - Disclosure indicator drawn on collapsible menu rows. 'none' renders no indicator.\n- **mode**: 'layout' | 'panel' - Full resizing layout or only the visible navigation panel.\n- **frame**: 'viewport' | 'contained' - Standalone Sidebar positioning. Viewport mode uses Theme's dynamic window-height token; contained mode fills a positioned parent.\n- **width**: string - Expanded width.\n- **widthIcon**: string - Icon-collapsed width.\n- **widthMobile**: string - Mobile drawer width.\n- **rail**: boolean | 'line' | 'thumb' - Edge toggle rail. `true` keeps the thin line style; `thumb` renders a short visible handle with the same full-height hitbox. The appearance is preserved when the rail shares the resize control.\n- **activityBar**: SidebarActivityBar - Icon rail pinned outside the panel, in layout mode only (`mode=\"panel\"` renders the panel alone and ignores it). It never slides off screen: only the panel takes the offcanvas offset, and the reserved layout column is the panel width plus the rail width. It wears the surface of the variant's panel: a hairline column on the canvas for `admin`, the recessed well for `framed`, borderless on the canvas for `inset`, and a card of its own (the panel's radius, edge and gutter) for `floating` and `split`. With `inset` and `split`, hiding the panel normally lets the page drop its frame and fill the edge; with an activity bar the rail is still beside the page, so the page keeps its framed form (padding, rounding, AppShell's border and shadow), and a `split` page takes the gutter the panel used to supply. The wrapper carries `data-page-flush` and the `main` part a `flush` variant only when the page does fill the edge. On mobile it renders as a horizontal row at the top of the drawer.\n- **expandOnHover**: boolean (default false) - With `collapsible=\"icon\"`, hovering or focusing into the collapsed panel expands it to `width` over the page (`data-peek=\"true\"`) while the reserved column stays at `widthIcon`, so page content does not reflow. The persisted collapsed state is untouched, and the peeked panel renders exactly like an expanded one: group headers, badges, search, inline submenus, and inline tree branches all come back, and the rail or resize handle travels to its inner edge.\n- **edgeReveal**: boolean (default true) - Pointer/focus edge preview for hidden offcanvas sidebars. Hover reveal overlays content, remains resizable when configured, and re-hides after the pointer leaves its small rectangular tolerance. Dragging the sidebar closed suppresses immediate hover reopening until the pointer leaves the edge trigger; toggle/click opens persistently.\n- **resizable**: boolean | SidebarResizableOptions - Enables pointer and keyboard resizing while expanded, icon-collapsed, or temporarily edge-revealed. By default, collapse requires dragging 75% of `minWidth` beyond the minimum; override `collapseThreshold` for a custom boundary. Use `storageKey` to restore and persist the expanded width across sessions.\n - `onWidthChange({ width, isUserInteraction })` reports every expanded-width change with one named payload: continuously while the user resizes (`isUserInteraction: true`) and once when a stored width is restored (`isUserInteraction: false`).\n\n### Content\n- **items**: SidebarGroup[] - Data-driven body navigation.\n- **views**: Record<string, SidebarView> - Named panel contents, one on screen at a time, replacing `items` and `content`. See SidebarView.\n- **headerButton** / **footerButton**: SidebarMenuButtonItem - Sticky large rows.\n- **search**: SidebarSearch - Header search input; use its native `oninput` handler.\n- **headerMenu** / **footerMenu**: SidebarMenuEntry[] - Sticky quick menus.\n- Menu entries and nested entries accept `size: 'small' | 'normal' | 'large'` for row geometry.\n- **header**, **content**, **footer**, **children**, **banner**: Snippet<[SidebarApi]> - Escape hatches. The `header` snippet renders **first** in the header region, above `headerButton`, `search` and `headerMenu`.\n\n### Styling\n- **class**: string - Classes applied to the Sidebar root.\n- **theme**: SidebarThemeProps - Semantic part overrides such as `panel`, `header`, `nav`, `footer`, menu, search, rail, and mobile drawer parts. Every part, with the element it lands on, its variants and their default classes, is listed in the entasis skill's `theme-parts/sidebar.md`; the registry key is `sidebar`.\n\n## Motion\n\n- **motion** theme slot: the y-axis slide shared by collapsible groups, inline submenus, and\n tree branches. Takes `in` / `out` slide params plus a `duration` / `easing` motion token.\n- Ladder: `<Theme components={{ sidebar: { motion } }}>` → `setSidebarTheme({ motion })` →\n `theme.motion`. Reduced motion collapses it to 0.\n- The `view` variant (`part: 'view'`) is the pager between views: `in.x` / `out.x` is how far a view travels (default `'100%'`: the two views slide side by side like pages) while it fades between `opacity` (default 0) and 1, at the `slow` duration on the `enter` easing. The back swipe moves the same layers the view change would, with the same travel and opacity. Reduced motion makes the switch instant while a swipe still follows the finger.\n\n## Restyle recipes\n\nThe five asks that come up first, as the override to copy. Each one is rendered and asserted by\n`sidebar-recipes.svelte.test.ts`, so it cannot drift from the component. One rule behind them: a\ndefault written under a variant prefix (`data-[active-variant=solid]:data-active:bg-selected`,\n`data-[side=left]:border-r`) is only replaced by an override carrying the same prefixes; an\nunprefixed class coexists with it and loses on specificity. `theme-parts/sidebar.md` shows every\ndefault verbatim, prefixes included.\n\n- **Dark panel.** `dark` scopes the dark theme's variables to the panel, so every neutral ink inside\n flips with it (every theme also emits its variables on `.<name>`, so the class scopes the dark theme to one subtree); needs a theme named `dark`, which the documented setup declares.\n `theme={{ panel: { base: 'dark bg-slate-900' }, mobilePanel: { base: 'dark bg-slate-900' } }}`\n- **Active row in your colour.** `activeVariant=\"solid\"` plus the same prefix chain as the default:\n `theme={{ menuButton: { base: 'rounded-full', activeVariant: { solid: 'data-[active-variant=solid]:data-active:bg-indigo-600 data-[active-variant=solid]:data-active:text-white' } } }}`\n- **No hover change.** The overlay is a `::before` and the default also brightens the ink on hover:\n `theme={{ menuButton: { base: 'state-layer-none hover:text-inherit' }, subButton: { base: 'state-layer-none hover:text-neutral/70' } }}`\n- **Flat panel.** The edge is a prefixed `border-r` / `border-l` on the admin variant, the shadow a\n `raised-*` on the floating ones:\n `theme={{ panel: { base: 'raised-none', variant: { admin: 'data-[side=left]:border-r-0 data-[side=right]:border-l-0' } } }}`\n- **Row height.** A plain height beats the compound `h-control-*`: `theme={{ menuButton: { base: 'h-11' } }}`\n- **Bigger icons.** A prop, not a theme: `iconSize=\"large\"`.\n\n## Accessibility\n\n- The body navigation renders inside a named `<nav>` landmark (`data-sidebar=\"nav\"`), so assistive tech can jump straight to it and tell it apart from the activity bar's own landmark.\n- Active links set `aria-current=\"page\"`.\n- Collapsible rows and groups set `aria-expanded`.\n- Disabled buttons use `disabled`; disabled links omit `href`, use `aria-disabled` and `tabindex=-1`, and block activation.\n- Mobile drawer includes a backdrop button labelled \"Close Sidebar\".\n- Icon-collapsed rows keep their labels mounted and visually fade them, preserving accessible names and stable icon geometry.\n- Search, group controls, actions, and nested rows become inert before collapse can remove or hide them; focus returns to the owning visible row.\n- Nested groups, tree branches, and inline submenus use reversible height transitions for open, close, and sidebar-collapse changes.\n- Tree roots use menu-row styling and nested tree nodes use submenu-row styling. In desktop icon mode, root folders open a PopupMenu and descendants remain navigable through recursive Menu submenu popovers; root leaves retain direct navigation and tooltips.\n- When `rail` and `resizable` are both enabled, one edge control owns click-to-toggle, drag resize, and keyboard resize without overlapping hitboxes.\n- Hidden offcanvas sidebars keep that combined edge control while temporarily revealed. Resizing does not pin the sidebar open; leaving the panel, trigger, and handle tolerance re-hides it without discarding the configured width.\n- Both peeks (edge reveal and `expandOnHover`) stay open while focus is inside the panel or while an overlay opened from inside it is open, including nested submenus. They release about 120ms after the pointer, focus, and every such overlay are gone.\n- The activity bar is its own `<nav>` landmark with a `<ul>` of items, a roving tabindex, and ArrowUp/ArrowDown/Home/End navigation that loops and skips disabled items. Tab lands on the `isActive` item. Each square takes its accessible name from `label`, with a string or number `badge` appended to it, and shows `label` as a tooltip on hover and focus.\n- A hidden offcanvas panel is `inert`, so Tab never lands in a panel parked off screen; a peek makes it interactive again.\n- Views: when focus sat in the view being replaced, it lands on the back row after going deeper and on the row that opened the view after coming back. A view change from outside the panel (a route) leaves focus where it is. The back swipe is a shortcut; the back row stays the accessible path.\n\n## Notes\n\n- Dropdown menus use Entasis `PopupMenu` and `MenuItem[]`.\n- The component uses semantic Entasis tokens. Do not add shadcn `sidebar-*` color tokens.\n- Snippet icons from `entasis/icons/*` are the preferred icon format.\n";
28
+ readonly sidebar: "\n# Sidebar Component\n\nSidebar navigation with data-driven groups, icon collapse, mobile drawer behavior,\nrecursive tree groups, header/footer rows, search, actions, and snippet escape hatches.\n\n## Import\n\n```svelte\n<script lang=\"ts\">\n\timport { Sidebar, type SidebarGroup } from 'entasis/sidebar';\n</script>\n```\n\n## Basic Usage\n\n```svelte\n<script lang=\"ts\">\n\timport { Sidebar, type SidebarGroup } from 'entasis/sidebar';\n\timport { houseIcon } from 'entasis/icons/house';\n\timport { gearIcon } from 'entasis/icons/gear';\n\n\tconst items: SidebarGroup[] = [\n\t\t{\n\t\t\tlabel: 'Workspace',\n\t\t\titems: [\n\t\t\t\t{ label: 'Dashboard', href: '/', icon: houseIcon, isActive: true },\n\t\t\t\t{ label: 'Settings', href: '/settings', icon: gearIcon }\n\t\t\t]\n\t\t}\n\t];\n</script>\n\n<Sidebar items={items}>\n\t{#snippet children({ toggle })}\n\t\t<header>\n\t\t\t<button type=\"button\" onclick={toggle}>Toggle</button>\n\t\t</header>\n\t\t<main>Page content</main>\n\t{/snippet}\n</Sidebar>\n```\n\n## AI-Safe Usage Contract\n\n1. Use `items` for normal navigation. Use `content` only when data-driven rows cannot express the layout.\n2. Use local Entasis icon snippets such as `houseIcon`, not Lucide component constructors.\n3. Use `MenuItem[]` from `entasis/menu` for `menu` and action dropdowns.\n4. Do not combine `menu` with `href` or `onclick` on the same row; use `action` for a trailing row menu.\n5. Keep `children`, `header`, `content`, `footer`, `banner`, and action snippets pure; they receive `SidebarApi`.\n6. Use `collapsible=\"icon\"` for icon rail behavior, `collapsible=\"offcanvas\"` for hidden desktop panels, and `collapsible=\"none\"` for fixed sidebars. Icon collapse automatically falls back to offcanvas when any data-driven row lacks an icon.\n7. Offcanvas sidebars reveal over the content from the screen edge by default when hidden; set `edgeReveal={false}` to disable that. A revealed hidden sidebar keeps its resize handle and dismisses through a small rectangular pointer tolerance.\n8. Set `keyboardShortcut={false}` when embedding Sidebar inside another shortcut-heavy surface.\n9. Sidebar owns navigation, resize mechanics, the lower application wall, and variant surface geometry. AppShell forwards its variant and composes PageShell inside that surface.\n10. Use `size` for typography, icon scale, and item height. Use `density` independently for section padding, gaps, and submenu spacing.\n11. Use `activityBar` for a persistent icon rail outside the panel (section switching, workspaces). Every item needs an `icon` and a `label`; the label is the accessible name and the tooltip. It is layout mode only: `mode=\"panel\"` renders the navigation panel alone. The rail does not change the panel by itself: keep the selected rail item in state from `onSelect`, mark it `isActive`, and either derive the Sidebar's `items` from it or, for an animated switch, give each rail item a view in `views` and set `view` from it.\n12. Use `expandOnHover` only with `collapsible=\"icon\"`. It is a temporary peek, not a toggle: the persisted collapsed state never changes, while the peeked panel renders with expanded semantics.\n13. Use `views` when the panel's contents change as the user moves through the app: sections switched from the activity bar or the route, and nested menus that open inside the panel. Key each view, set `parent` on nested ones, open them with a row's `view`, and drive `view` from state or the URL. Do not hand-animate `items` swaps.\n\n## Data Model\n\n### SidebarGroup\n- **label**: string - Group label, hidden in icon-collapsed mode.\n- **items**: SidebarMenuEntry[] - Menu rows.\n- **tree**: SidebarTreeNode[] - Recursive tree rows instead of menu items.\n- **action**: SidebarMenuActionDescriptor | SidebarMenuActionDescriptor[] | Snippet<[SidebarApi]> - Top-right group actions. Pass an array to pin several affordances (a `+` and a drag handle) to one group header; each renders as its own icon-only ghost button.\n- **collapsible**: boolean - Makes the group label a toggle.\n- **defaultOpen**: boolean - Initial collapsible group state.\n- **separator**: boolean - Divider before the group.\n\n### SidebarMenuEntry\n- **label**: string - Visible row label.\n- **icon**: SidebarIcon - Entasis icon snippet or string.\n- **iconColor**: Colors - Role tint for the leading icon, applied through `data-color`.\n- **iconVariant**: 'bare' | 'tile' - Leading icon treatment. `tile` paints a rounded square (`bg-color-muted text-color-muted-readable`) around the glyph, so per-project colour chips come from the role scale instead of hand-built markup.\n- **href**: string - Render as an anchor. Mutually exclusive with menu.\n- **onclick**: (event: MouseEvent) => void - Native click handler for button or anchor rows. Mutually exclusive with menu.\n- **isActive**: boolean - Adds active styling and `aria-current=\"page\"`.\n- **disabled**: boolean - Disables button rows and marks anchor rows disabled.\n- **badge**: string | number - Trailing count/status, hidden in icon mode.\n- **tooltip**: string - Entasis Tooltip content in icon mode. Defaults to label.\n- **items**: SidebarMenuSubEntry[] - Inline nested menu.\n- **collapsible**: boolean - Set false for an always-open submenu.\n- **defaultOpen**: boolean - Initial nested menu state.\n- **menu**: MenuItem[] - Popup menu opened from the full row. Mutually exclusive with href/onclick.\n- **view**: string - Key of the `views` entry the row opens, sliding it in; the row shows a trailing chevron. Mutually exclusive with href, menu and items.\n- **action**: SidebarMenuActionDescriptor | Snippet<[SidebarApi]> - Hover/focus trailing action.\n\n### SidebarView\nOne named panel content in `views`.\n- **label**: string - The view's name, shown on the back row of the views nested under it.\n- **parent**: string - Key of the view this one is nested under. A nested view opens with a back row to its parent (named \"Back, <parent label>\"), and on mobile a swipe toward the inline end goes back. A view without `parent` is a top-level section.\n- **items**: SidebarGroup[] / **content**: Snippet<[SidebarApi]> - The view's body.\n- **headerButton**, **search**, **headerMenu**, **header**, **footerButton**, **footerMenu**, **footer** - Header and footer props for this view. Each one left undefined comes from the parent view, then from the Sidebar's own prop; `null` removes an inherited one.\n\nChanging the view slides the two views side by side, like pages: a deeper view comes in from the inline end while the old one leaves to the start, a shallower one slides back the other way, and between views at the same depth (sections) the later one in `views` counts as forward. When both views get every header and footer prop from the same place, the header and footer stay still and only the menu slides; when a view changes any of them, the whole panel slides as one page. `api.view` reads the current view and `api.setView(key)` changes it from a snippet.\n\n### SidebarActivityBar\nIcon rail pinned to the outer edge of the sidebar, visible in every display state.\n- **items**: SidebarActivityBarItem[] - Items rendered from the top.\n- **footerItems**: SidebarActivityBarItem[] - Items pinned to the end of the column.\n- **header** / **footer**: Snippet - Custom content before the first item and after the pinned ones.\n- **width**: string (default '3rem') - Column thickness, published as `--sidebar-width-activity`.\n- **label**: string - Accessible name for the column landmark. Set it whenever the panel also renders navigation.\n- **onSelect**: ({ item, index }) => void - Fires after an item is activated. `index` counts `items` then `footerItems`.\n\n### SidebarActivityBarItem\n- **icon**: SidebarIcon (required) - Icon rendered in the square.\n- **label**: string (required) - Accessible name and default tooltip; the square shows no text.\n- **id**: string - Stable render key.\n- **href** / **target** / **rel** - Render an anchor instead of a button.\n- **onclick**: (event: MouseEvent) => void - Native click handler.\n- **isActive**: boolean - Adds active styling and `aria-current=\"page\"`.\n- **badge**: string | number | Snippet - Corner badge pinned to the outer top corner. An empty string renders a bare dot. A string or number badge joins the accessible name (`\"Alerts, 3\"`); a dot and a Snippet badge are decorative, so put their meaning in `label`.\n- **disabled**: boolean - Blocks activation and skips the item during keyboard navigation.\n- **tooltip**: string | false - Tooltip override; `false` suppresses it.\n\n### SidebarMenuButtonItem\nUse for `headerButton`, `footerButton`, or direct `<SidebarMenuButton />` rows.\n- **icon**: SidebarIcon - Leading logo/icon.\n- **avatar**: { src?: string; alt?: string; fallback?: string } - Leading avatar.\n- **variant**: 'default' | 'brand' | 'compact'.\n- **title**: string - Primary text.\n- **subtitle**: string - Secondary text.\n- **trailing**: SidebarIcon | false | SidebarMenuActionDescriptor - Trailing content. An icon is decorative; an action descriptor (`{ icon, label, onclick }`, the same shape as a group action) renders its own icon-only ghost button beside the row, so a workspace card can carry its own collapse control without a custom `header` snippet. Descriptor handlers are `onclick(event, api)`, so `api.toggle()` is reachable.\n- **href** / **onclick** / **menu** - Choose link, button, or popup behavior. `onclick(event, api)` receives the SidebarApi beside the event, so `api.toggle()` is reachable from the row itself.\n- **menuIconClass**: string - Class override for option icons inside the popup menu.\n\n## Props\n\n### State\n- **open**: boolean (bindable, default true) - Desktop expanded state.\n- **defaultOpen**: boolean (default true) - Initial desktop state when `open` is omitted.\n- **onOpenChange**: (open: boolean) => void - Called once for a library-originated desktop state change. Repeated requests and parent prop updates stay silent.\n- **onDisplayStateChange**: (state: SidebarDisplayState) => void - Called once for a library-originated semantic display-state change.\n- **view**: string (bindable) - Key of the view on screen when `views` is set. Defaults to `defaultView`, then the first view.\n- **defaultView**: string - Initial view when `view` is omitted.\n- **onViewChange**: (view: string) => void - Called once for a library-originated view change: a view row, a back row, a swipe. Parent prop updates stay silent; with a route-driven `view`, navigate here.\n- **api.displayState**: 'expanded' | 'collapsed' | 'hidden' - Semantic desktop state; hidden means closed offcanvas. A hover peek does not change it.\n- **api.isPeeking**: boolean - True while a hover peek renders the collapsed panel at full width. Read it alongside `displayState` when a snippet hides content in icon mode.\n- **keyboardShortcut**: string | false (default 'b') - Ctrl/Cmd shortcut key.\n\n### Layout\n- **side**: 'left' | 'right' - Desktop and mobile side.\n- **variant**: 'admin' | 'floating' | 'inset' | 'split' | 'framed' - Sidebar geometry. `framed` is the admin geometry for a sidebar hosted inside a raised card (AppShell variant framed), with a `surface-recessed` well. `admin` renders the conventional full-height navigation column; `inset` integrates navigation into the lower wall with an inset content surface; `split` renders detached sidebar and content surfaces.\n- **size**: 'small' | 'normal' | 'large' (default 'normal') - Typography, icon, avatar, badge, leading-media, item-height, and search-height scale.\n- **iconSize**: 'small' | 'normal' | 'large' - Icon and leading-media scale inside the panel on its own; defaults to `size`. Menu rows, sub rows, group labels and the header button follow it; the activity bar keeps `size`.\n- **activeVariant**: 'soft' | 'outline' | 'solid' (default 'soft') - How active rows are painted. `soft` is the shared selected recipe (`selectedSoft`: `bg-selected-muted text-selected-muted-readable`), `solid` its loud counterpart (`selectedSolid`: `bg-selected text-selected-contrast`), `outline` a bordered surface card (`bg-surface border border-neutral-muted text-neutral`) that reads as a raised card on a tinted well. Each row carries the choice as `data-active-variant`, so no descendant selector is needed to restyle selection.\n- **density**: 'compact' | 'normal' | 'comfortable' (default 'normal') - Section padding, group padding, gaps, horizontal inset, and submenu spacing.\n- **collapsible**: 'offcanvas' | 'icon' | 'none' - Collapse behavior. Icon mode requires icons on every data-driven row and otherwise resolves to offcanvas.\n- **collapseIcon**: DisclosureIndicator — 'chevron' | 'plus-minus' | 'none' (default 'chevron') - Disclosure indicator drawn on collapsible menu rows. 'none' renders no indicator.\n- **mode**: 'layout' | 'panel' - Full resizing layout or only the visible navigation panel.\n- **frame**: 'viewport' | 'contained' - Standalone Sidebar positioning. Viewport mode uses Theme's dynamic window-height token; contained mode fills a positioned parent.\n- **width**: string - Expanded width.\n- **widthIcon**: string - Icon-collapsed width.\n- **widthMobile**: string - Mobile drawer width.\n- **rail**: boolean | 'line' | 'thumb' - Edge toggle rail. `true` keeps the thin line style; `thumb` renders a short visible handle with the same full-height hitbox. The appearance is preserved when the rail shares the resize control.\n- **activityBar**: SidebarActivityBar - Icon rail pinned outside the panel, in layout mode only (`mode=\"panel\"` renders the panel alone and ignores it). It never slides off screen: only the panel takes the offcanvas offset, and the reserved layout column is the panel width plus the rail width. It wears the surface of the variant's panel: a hairline column on the canvas for `admin`, the recessed well for `framed`, borderless on the canvas for `inset`, and a card of its own (the panel's radius, edge and gutter) for `floating` and `split`. Every gutter between the activity bar, the panel and the page, and between them and the frame edge, is `--space-md`, so it scales with the theme's spacing. With `inset` and `split`, hiding the panel normally lets the page drop its frame and fill the edge; with an activity bar the rail is still beside the page, so the page keeps its framed form (padding, rounding, AppShell's border and shadow), and a `split` page takes the gutter the panel used to supply. The wrapper carries `data-page-flush` and the `main` part a `flush` variant only when the page does fill the edge. On mobile it renders as a horizontal row at the top of the drawer.\n- **expandOnHover**: boolean (default false) - With `collapsible=\"icon\"`, hovering or focusing into the collapsed panel expands it to `width` over the page (`data-peek=\"true\"`) while the reserved column stays at `widthIcon`, so page content does not reflow. The persisted collapsed state is untouched, and the peeked panel renders exactly like an expanded one: group headers, badges, search, inline submenus, and inline tree branches all come back, and the rail or resize handle travels to its inner edge.\n- **edgeReveal**: boolean (default true) - Pointer/focus edge preview for hidden offcanvas sidebars. Hover reveal overlays content, remains resizable when configured, and re-hides after the pointer leaves its small rectangular tolerance. Dragging the sidebar closed suppresses immediate hover reopening until the pointer leaves the edge trigger; toggle/click opens persistently.\n- **resizable**: boolean | SidebarResizableOptions - Enables pointer and keyboard resizing while expanded, icon-collapsed, or temporarily edge-revealed. `minWidth` and `maxWidth` (a CSS length or a number of px; defaults `12rem` and `32rem`) bound both the drag and the keyboard resize; a `maxWidth` below `minWidth` throws. By default, collapse requires dragging 75% of `minWidth` beyond the minimum; override `collapseThreshold` for a custom boundary. Use `storageKey` to restore and persist the expanded width across sessions.\n - `onWidthChange({ width, isUserInteraction })` reports every expanded-width change with one named payload: continuously while the user resizes (`isUserInteraction: true`) and once when a stored width is restored (`isUserInteraction: false`).\n\n### Content\n- **items**: SidebarGroup[] - Data-driven body navigation.\n- **views**: Record<string, SidebarView> - Named panel contents, one on screen at a time, replacing `items` and `content`. See SidebarView.\n- **headerButton** / **footerButton**: SidebarMenuButtonItem - Sticky large rows.\n- **search**: SidebarSearch - Header search input; use its native `oninput` handler.\n- **headerMenu** / **footerMenu**: SidebarMenuEntry[] - Sticky quick menus.\n- Menu entries and nested entries accept `size: 'small' | 'normal' | 'large'` for row geometry.\n- **header**, **content**, **footer**, **children**, **banner**: Snippet<[SidebarApi]> - Escape hatches. The `header` snippet renders **first** in the header region, above `headerButton`, `search` and `headerMenu`.\n\n### Styling\n- **class**: string - Classes applied to the Sidebar root.\n- **theme**: SidebarThemeProps - Semantic part overrides such as `panel`, `header`, `nav`, `footer`, menu, search, rail, and mobile drawer parts. Every part, with the element it lands on, its variants and their default classes, is listed in the entasis skill's `theme-parts/sidebar.md`; the registry key is `sidebar`.\n\n## Motion\n\n- **motion** theme slot: the y-axis slide shared by collapsible groups, inline submenus, and\n tree branches. Takes `in` / `out` slide params plus a `duration` / `easing` motion token.\n- Ladder: `<Theme components={{ sidebar: { motion } }}>` → `setSidebarTheme({ motion })` →\n `theme.motion`. Reduced motion collapses it to 0.\n- The `view` variant (`part: 'view'`) is the pager between views: `in.x` / `out.x` is how far a view travels (default `'100%'`: the two views slide side by side like pages) while it fades between `opacity` (default 0) and 1, at the `slow` duration on the `enter` easing. The back swipe moves the same layers the view change would, with the same travel and opacity. Reduced motion makes the switch instant while a swipe still follows the finger.\n\n## Restyle recipes\n\nThe five asks that come up first, as the override to copy. Each one is rendered and asserted by\n`sidebar-recipes.svelte.test.ts`, so it cannot drift from the component. One rule behind them: a\ndefault written under a variant prefix (`data-[active-variant=solid]:data-active:bg-selected`,\n`data-[side=left]:border-r`) is only replaced by an override carrying the same prefixes; an\nunprefixed class coexists with it and loses on specificity. `theme-parts/sidebar.md` shows every\ndefault verbatim, prefixes included.\n\n- **Dark panel.** `dark` scopes the dark theme's variables to the panel, so every neutral ink inside\n flips with it (every theme also emits its variables on `.<name>`, so the class scopes the dark theme to one subtree); needs a theme named `dark`, which the documented setup declares.\n `theme={{ panel: { base: 'dark bg-slate-900' }, mobilePanel: { base: 'dark bg-slate-900' } }}`\n- **Active row in your colour.** `activeVariant=\"solid\"` plus the same prefix chain as the default:\n `theme={{ menuButton: { base: 'rounded-full', activeVariant: { solid: 'data-[active-variant=solid]:data-active:bg-indigo-600 data-[active-variant=solid]:data-active:text-white' } } }}`\n- **No hover change.** The overlay is a `::before` and the default also brightens the ink on hover:\n `theme={{ menuButton: { base: 'state-layer-none hover:text-inherit' }, subButton: { base: 'state-layer-none hover:text-neutral/70' } }}`\n- **Flat panel.** The edge is a prefixed `border-r` / `border-l` on the admin variant, the shadow a\n `raised-*` on the floating ones:\n `theme={{ panel: { base: 'raised-none', variant: { admin: 'data-[side=left]:border-r-0 data-[side=right]:border-l-0' } } }}`\n- **Row height.** A plain height beats the compound `h-control-*`: `theme={{ menuButton: { base: 'h-11' } }}`\n- **Bigger icons.** A prop, not a theme: `iconSize=\"large\"`.\n\n## Accessibility\n\n- The body navigation renders inside a named `<nav>` landmark (`data-sidebar=\"nav\"`), so assistive tech can jump straight to it and tell it apart from the activity bar's own landmark.\n- Active links set `aria-current=\"page\"`.\n- Collapsible rows and groups set `aria-expanded`.\n- Disabled buttons use `disabled`; disabled links omit `href`, use `aria-disabled` and `tabindex=-1`, and block activation.\n- Mobile drawer includes a backdrop button labelled \"Close Sidebar\".\n- Icon-collapsed rows keep their labels mounted and visually fade them, preserving accessible names and stable icon geometry.\n- Search, group controls, actions, and nested rows become inert before collapse can remove or hide them; focus returns to the owning visible row.\n- Nested groups, tree branches, and inline submenus use reversible height transitions for open, close, and sidebar-collapse changes.\n- Tree roots use menu-row styling and nested tree nodes use submenu-row styling. In desktop icon mode, root folders open a PopupMenu and descendants remain navigable through recursive Menu submenu popovers; root leaves retain direct navigation and tooltips.\n- When `rail` and `resizable` are both enabled, one edge control owns click-to-toggle, drag resize, and keyboard resize without overlapping hitboxes.\n- Hidden offcanvas sidebars keep that combined edge control while temporarily revealed. Resizing does not pin the sidebar open; leaving the panel, trigger, and handle tolerance re-hides it without discarding the configured width.\n- Both peeks (edge reveal and `expandOnHover`) stay open while focus is inside the panel or while an overlay opened from inside it is open, including nested submenus. They release about 120ms after the pointer, focus, and every such overlay are gone.\n- The activity bar is its own `<nav>` landmark with a `<ul>` of items, a roving tabindex, and ArrowUp/ArrowDown/Home/End navigation that loops and skips disabled items. Tab lands on the `isActive` item. Each square takes its accessible name from `label`, with a string or number `badge` appended to it, and shows `label` as a tooltip on hover and focus.\n- A hidden offcanvas panel is `inert`, so Tab never lands in a panel parked off screen; a peek makes it interactive again.\n- Views: when focus sat in the view being replaced, it lands on the back row after going deeper and on the row that opened the view after coming back. A view change from outside the panel (a route) leaves focus where it is. The back swipe is a shortcut; the back row stays the accessible path.\n\n## Notes\n\n- Dropdown menus use Entasis `PopupMenu` and `MenuItem[]`.\n- The component uses semantic Entasis tokens. Do not add shadcn `sidebar-*` color tokens.\n- Snippet icons from `entasis/icons/*` are the preferred icon format.\n";
29
29
  readonly button: "\n# Button Component\n\nThe Button component is a flexible and customizable button element that supports various variants, sizes, colors, and interactive states.\n\n## Basic Usage\n\n```svelte\n<Button>Click me</Button>\n<Button variant=\"outline\">Outline Button</Button>\n<Button color=\"primary\" size=\"large\">Large Primary Button</Button>\n```\n\n## Props\n\n### Core Props\n- **variant**: 'solid' | 'outline' | 'soft' | 'ghost' | 'link' (default: 'solid')\n - solid: Filled background with color\n - outline: Transparent background with colored border\n - soft: Muted color background\n - ghost: Transparent background with a transient state layer on hover and press\n - link: Text-only styling with underline on hover\n\n- **color**: 'primary' | 'secondary' | 'neutral' | 'danger' | 'success' | 'warning' | 'info' (default: 'neutral')\n - Determines the color scheme of the button\n\n- **size**: 'small' | 'normal' | 'large' (default: 'normal')\n - small: 28px height, smaller padding and text\n - normal: 32px height, standard padding\n - large: 36px height, larger padding and text\n\n### Layout Props\n- **fullWidth**: boolean (default: false) - Makes button take full width of container\n- **squared**: boolean - Makes button square (aspect-ratio 1:1), auto-determined if only prefix/suffix is provided\n- **disabled**: boolean (default: false) - Disables button interaction\n- **loading**: boolean (default: false) - Shows the Theme-configured loading spinner and disables interaction\n\n### State Props\nDescribe the meaning; the Button writes the ARIA. Never pass an aria-* attribute to a Button.\n- **pressed**: boolean - Toggle state of a button that stays on or off (a bold button in a toolbar, a \"show password\" eye). Rendered as aria-pressed\n- **selected**: boolean - Chosen state of a button acting as one option among several (a tab, a listbox option). Rendered as aria-selected\n- **expanded**: boolean - Whether the surface this button opens is showing. Rendered as aria-expanded. A entasis surface (Popover, PopupMenu, Select, Combobox) sets this on its own trigger, so pass it only for a surface you open yourself\n- **haspopup**: 'dialog' | 'menu' | 'listbox' | 'tree' | 'grid' | true - What the surface this button opens contains. Rendered as aria-haspopup, and likewise set by a entasis surface on its own trigger\n\n### Link Props\n- **href**: string - Makes button render as anchor tag\n- **target**: string - Link target (e.g., \"_blank\")\n- **rel**: string - Link relationship\n\n### Event Props\n- **onclick**: (event: MouseEvent) => void - Native click event handler\n- **onpointerenter**: (event: PointerEvent) => void - Native pointer enter event handler\n- **onpointerleave**: (event: PointerEvent) => void - Native pointer leave event handler\n\n### Content Props (Slots)\n- **children**: Snippet - Main button content\n- **prefix**: Snippet - Content before main text (typically icons)\n- **suffix**: Snippet - Content after main text (typically icons)\n\n### Advanced Props\n- **label**: string - Accessible label applied as aria-label on the root element (required for icon-only buttons)\n- **ref**: HTMLElement - Reference to the button element\n- **class**: string - Additional CSS classes\n- **theme**: ComponentTheme - Custom theme overrides\n\n## Structure\n\nThe button follows this DOM structure:\n```\n<Button>\n\t<Prefix /> <!-- Optional prefix content -->\n\t<Children /> <!-- Main button content -->\n\t<Suffix /> <!-- Optional suffix content -->\n</Button>\n```\n\n## Examples\n\n### Basic Buttons\n```svelte\n<Button>Default Button</Button>\n<Button variant=\"outline\" color=\"primary\">Primary Outline</Button>\n<Button variant=\"soft\" color=\"danger\">Soft Danger</Button>\n<Button variant=\"ghost\">Ghost Button</Button>\n<Button variant=\"link\">Link Button</Button>\n```\n\n### With Icons\n```svelte\n<Button>\n\t{#snippet prefix()}\n\t\t{@render icon()}\n\t{/snippet}\n\tAdd Item\n</Button>\n\n<Button squared>\n\t{#snippet prefix()}\n\t\t{@render icon()}\n\t{/snippet}\n</Button>\n```\n\n### Interactive States\n```svelte\n<Button loading>Loading...</Button>\n<Button disabled>Disabled</Button>\n<Button fullWidth>Full Width Button</Button>\n```\n\n### As Link\n```svelte\n<Button href=\"/dashboard\" target=\"_blank\">Go to Dashboard</Button>\n```\n\n### With Event Handlers\n```svelte\n<script lang=\"ts\">\n\tfunction handleClick(event: MouseEvent) {\n\t\tconsole.log('Clicked:', event.currentTarget);\n\t}\n</script>\n\n<Button onclick={handleClick}>\n\tClick\n</Button>\n```\n\n### Custom Styling\n```svelte\n<Button class=\"lift-4 border-2\" color=\"primary\" variant=\"outline\">\n\tCustom Styled\n</Button>\n```\n\n## Accessibility\n\n- Automatically sets appropriate ARIA roles (button/link)\n- Supports keyboard navigation\n- Disabled state prevents interaction\n- Loading state provides visual feedback\n\n## Notes\n\n- When `href` is provided, renders as `<a>` tag, otherwise `<button>`\n- `squared` is automatically determined when only prefix or suffix is provided without children\n- All event handlers respect disabled state\n- Icon sizing is automatically adjusted based on button size\n\n## Theme Customization\n\nThe Button component uses a theme object that can be customized using the `theme` prop or by setting a global theme.\n\n### Theme Structure\n\nThe theme object contains the following parts:\n- **root**: Main button container styles\n- **prefix**: Styles for prefix content (icons before text)\n- **suffix**: Styles for suffix content (icons after text)\n\n### Theme Type Definition\n\n```typescript\nimport type { ButtonThemeProps } from 'entasis/button';\n\n// Example theme customization\nconst customTheme: ButtonThemeProps = {\n root: {\n base: 'custom-base-classes',\n size: {\n small: 'custom-small-classes',\n normal: 'custom-normal-classes',\n large: 'custom-large-classes'\n },\n color: {\n primary: 'bg-blue-500 text-white',\n danger: 'bg-red-500 text-white'\n },\n variant: {\n solid: 'bg-color text-color-contrast',\n outline: 'border-2 border-color'\n }\n },\n prefix: {\n size: {\n small: 'w-3 h-3',\n normal: 'w-4 h-4',\n large: 'w-5 h-5'\n }\n }\n};\n```\n\n### Available Variants\n\n**root**:\n- base: Base classes applied to all buttons\n- Variants:\n - size: 'small' | 'normal' | 'large' - Controls padding, text size, and height\n - color: 'primary' | 'secondary' | 'neutral' | 'danger' | 'success' | 'warning' | 'info' - Color scheme\n - variant: 'solid' | 'outline' | 'soft' | 'ghost' | 'link' - Visual style variant\n - loading: boolean - Loading state styling\n - disabled: boolean - Disabled state styling\n - squared: boolean - Square button styling\n - fullWidth: boolean - Full width styling\n\n**prefix**:\n- base: Base classes for prefix content\n- Variants:\n - size: 'small' | 'normal' | 'large' - Icon size based on button size\n\n**suffix**:\n- base: Base classes for suffix content\n- Variants:\n - size: 'small' | 'normal' | 'large' - Icon size based on button size\n\n### Usage Examples\n\n**Basic Theme Override**:\n```svelte\n<Button \n theme={{\n root: {\n base: 'rounded-full lift-4',\n size: {\n large: 'px-8 py-4 text-xl'\n }\n }\n }}\n>\n Custom Styled Button\n</Button>\n```\n\n**Color Variant Customization**:\n```svelte\n<Button \n color=\"primary\"\n theme={{\n root: {\n color: {\n primary: 'state-layer bg-linear-to-r from-blue-500 to-purple-500'\n }\n }\n }}\n>\n Gradient Button\n</Button>\n```\n\n**Global Theme Setting**:\n```svelte\n<script>\n import { setButtonTheme } from '../components/Button/index.ts';\n \n setButtonTheme({\n root: {\n variant: {\n solid: 'state-layer bg-color text-color-contrast lift-3 hover:lift-4 transition-shadow',\n outline: 'state-layer border-2 border-color'\n }\n },\n prefix: {\n size: {\n normal: 'w-5 h-5'\n }\n }\n });\n</script>\n```\n";
30
- readonly 'button-group': "\n# ButtonGroup Component\n\nThe ButtonGroup component displays a collection of related buttons as a cohesive group with shared styling properties.\n\n## Basic Usage\n\n```svelte\n<ButtonGroup \n\titems={[\n\t\t{ children: 'First' },\n\t\t{ children: 'Second' },\n\t\t{ children: 'Third' }\n\t]}\n/>\n```\n\n## Props\n\n### Core Props\n- **items**: Array<ButtonProps> (required) - Array of button configurations\n - Each button can have all standard Button component props\n\n### Shared Button Props\n- **size**: 'small' | 'normal' | 'large' - Applied to all buttons in the group\n- **color**: 'primary' | 'secondary' | 'neutral' | 'danger' | 'success' | 'warning' | 'info' - Shared color for all buttons\n- **variant**: 'solid' | 'outline' | 'soft' | 'ghost' | 'link' - Shared variant for all buttons\n- **disabled**: boolean - Disables all buttons in the group\n\n### Styling Props\n- **class**: string - Additional CSS classes for the group container\n- **theme**: ComponentTheme - Custom theme overrides\n\n## Structure\n\n```\n<ButtonGroup>\n\t<Button />\n\t<Button />\n\t<Button />\n</ButtonGroup>\n```\n\n## Examples\n\n### Basic Button Group\n```svelte\n<ButtonGroup \n\titems={[\n\t\t{ children: 'Left' },\n\t\t{ children: 'Center' },\n\t\t{ children: 'Right' }\n\t]}\n/>\n```\n\n### With Shared Styling\n```svelte\n<ButtonGroup \n\tsize=\"large\"\n\tcolor=\"primary\"\n\tvariant=\"outline\"\n\titems={[\n\t\t{ children: 'Option 1' },\n\t\t{ children: 'Option 2' },\n\t\t{ children: 'Option 3' }\n\t]}\n/>\n```\n\n### With Icons\n```svelte\n<script lang=\"ts\">\n\timport { ButtonGroup } from 'entasis/button-group';\n\timport { textAlignLeftIcon } from 'entasis/icons/textAlignLeft';\n\timport { textAlignCenterIcon } from 'entasis/icons/textAlignCenter';\n\timport { textAlignRightIcon } from 'entasis/icons/textAlignRight';\n</script>\n\n<ButtonGroup \n\titems={[\n\t\t{ \n\t\t\tprefix: textAlignLeftIcon,\n\t\t\tchildren: 'Left' \n\t\t},\n\t\t{ \n\t\t\tprefix: textAlignCenterIcon,\n\t\t\tchildren: 'Center' \n\t\t},\n\t\t{ \n\t\t\tprefix: textAlignRightIcon,\n\t\t\tchildren: 'Right' \n\t\t}\n\t]}\n/>\n```\n\n### With Individual Click Handlers\n```svelte\n<script>\n\tfunction handleOption(option) {\n\t\tconsole.log(`Selected: ${option}`);\n\t}\n</script>\n\n<ButtonGroup \n\titems={[\n\t\t{ \n\t\t\tchildren: 'Save',\n\t\t\tonclick: () => handleOption('save')\n\t\t},\n\t\t{ \n\t\t\tchildren: 'Cancel',\n\t\t\tonclick: () => handleOption('cancel')\n\t\t}\n\t]}\n/>\n```\n\n### Disabled Group\n```svelte\n<ButtonGroup \n\tdisabled\n\titems={[\n\t\t{ children: 'Option 1' },\n\t\t{ children: 'Option 2' }\n\t]}\n/>\n```\n\n### Icon Only Buttons\n```svelte\n<script lang=\"ts\">\n\timport { ButtonGroup } from 'entasis/button-group';\n\timport { textBIcon } from 'entasis/icons/textB';\n\timport { textItalicIcon } from 'entasis/icons/textItalic';\n\timport { textUnderlineIcon } from 'entasis/icons/textUnderline';\n</script>\n\n<ButtonGroup \n\titems={[\n\t\t{ \n\t\t\tsquared: true,\n\t\t\tprefix: textBIcon\n\t\t},\n\t\t{ \n\t\t\tsquared: true,\n\t\t\tprefix: textItalicIcon\n\t\t},\n\t\t{ \n\t\t\tsquared: true,\n\t\t\tprefix: textUnderlineIcon\n\t\t}\n\t]}\n/>\n```\n\n### Mixed Button States\n```svelte\n<ButtonGroup \n\tvariant=\"outline\"\n\titems={[\n\t\t{ children: 'Active', color: 'primary' },\n\t\t{ children: 'Default', color: 'neutral' },\n\t\t{ children: 'Disabled', disabled: true }\n\t]}\n/>\n```\n\n### Segmented Control\n```svelte\n<script>\n\tlet selected = $state('week');\n</script>\n\n<ButtonGroup \n\titems={[\n\t\t{ \n\t\t\tchildren: 'Day',\n\t\t\tvariant: selected === 'day' ? 'solid' : 'ghost',\n\t\t\tonclick: () => selected = 'day'\n\t\t},\n\t\t{ \n\t\t\tchildren: 'Week',\n\t\t\tvariant: selected === 'week' ? 'solid' : 'ghost',\n\t\t\tonclick: () => selected = 'week'\n\t\t},\n\t\t{ \n\t\t\tchildren: 'Month',\n\t\t\tvariant: selected === 'month' ? 'solid' : 'ghost',\n\t\t\tonclick: () => selected = 'month'\n\t\t}\n\t]}\n/>\n```\n\n## Styling\n\nButtonGroup automatically:\n- Removes border-radius from middle buttons\n- Adjusts borders to prevent double borders\n- Creates a cohesive, connected appearance\n- Maintains consistent spacing\n\n## Accessibility\n\n- Each button maintains full keyboard accessibility\n- Focus styles are preserved\n- Disabled state cascades properly\n- Screen readers announce each button individually\n\n## Notes\n\n- Individual button props override shared props\n- Buttons are rendered in the order provided\n- The group container can be styled with the `class` prop\n- All Button component features are supported for individual buttons\n\n## Theme Customization\n\nThe ButtonGroup component uses a theme object that can be customized using the `theme` prop or by setting a global theme.\n\n### Theme Structure\n\nThe theme object contains the following parts:\n- **root**: Main button group container styles\n\n### Available Variants\n\n**root**:\n- base: Base classes for button group container (handles border radius and border connections between buttons)\n\n### Usage Examples\n\n**Basic Theme Override**:\n```svelte\n<ButtonGroup \n items={buttons}\n theme={{\n root: {\n base: 'flex items-center rounded-lg overflow-hidden'\n }\n }}\n/>\n```\n\n**Custom Group Styling**:\n```svelte\n<ButtonGroup \n items={buttons}\n theme={{\n root: {\n base: 'flex items-center gap-0 border-2 border-primary rounded-lg overflow-hidden'\n }\n }}\n/>\n```\n\n**Global Theme Setting**:\n```svelte\n<script>\n import { setButtonGroupTheme } from '../components/ButtonGroup/index.ts';\n \n setButtonGroupTheme({\n root: {\n base: 'flex items-center first-child:rounded-r-none last-child:rounded-l-none'\n }\n });\n</script>\n```\n";
30
+ readonly 'button-group': "\n# ButtonGroup Component\n\nThe ButtonGroup component displays a collection of related buttons as a cohesive group with shared styling properties.\n\n## Basic Usage\n\n```svelte\n<ButtonGroup \n\titems={[\n\t\t{ children: 'First' },\n\t\t{ children: 'Second' },\n\t\t{ children: 'Third' }\n\t]}\n/>\n```\n\n## Props\n\n### Core Props\nPass either `items` or `children`.\n- **items**: Array<ButtonProps> - Array of button configurations\n - Each button can have all standard Button component props\n- **children**: Snippet - Buttons composed directly, for content an item object cannot describe\n (a Tooltip or Popover trigger, a Button with a custom body). Each direct child is joined to its\n neighbours; set size, color and variant on each Button, since the shared props below apply to\n `items` only.\n- **label**: string - Accessible name of the group (the root has `role=\"group\"`)\n\n### Shared Button Props (items)\n- **size**: 'small' | 'normal' | 'large' - Applied to all buttons in the group\n- **color**: 'primary' | 'secondary' | 'neutral' | 'danger' | 'success' | 'warning' | 'info' - Shared color for all buttons\n- **variant**: 'solid' | 'outline' | 'soft' | 'ghost' | 'link' - Shared variant for all buttons\n- **disabled**: boolean - Disables all buttons in the group, even one whose item sets `disabled: false`\n\n### Styling Props\n- **class**: string - Additional CSS classes for the group container\n- **theme**: ComponentTheme - Custom theme overrides\n\n## Structure\n\n```\n<ButtonGroup>\n\t<Button />\n\t<Button />\n\t<Button />\n</ButtonGroup>\n```\n\n## Examples\n\n### Composed Children\n```svelte\n<script lang=\"ts\">\n\timport { Button } from 'entasis/button';\n\timport { ButtonGroup } from 'entasis/button-group';\n\timport { Tooltip } from 'entasis/tooltip';\n</script>\n\n<ButtonGroup label=\"History\">\n\t<Button variant=\"outline\">Undo</Button>\n\t<Tooltip content=\"Redo the last change\" trigger={{ content: 'Redo', variant: 'outline' }} />\n</ButtonGroup>\n```\n\n### Basic Button Group\n```svelte\n<ButtonGroup \n\titems={[\n\t\t{ children: 'Left' },\n\t\t{ children: 'Center' },\n\t\t{ children: 'Right' }\n\t]}\n/>\n```\n\n### With Shared Styling\n```svelte\n<ButtonGroup \n\tsize=\"large\"\n\tcolor=\"primary\"\n\tvariant=\"outline\"\n\titems={[\n\t\t{ children: 'Option 1' },\n\t\t{ children: 'Option 2' },\n\t\t{ children: 'Option 3' }\n\t]}\n/>\n```\n\n### With Icons\n```svelte\n<script lang=\"ts\">\n\timport { ButtonGroup } from 'entasis/button-group';\n\timport { textAlignLeftIcon } from 'entasis/icons/textAlignLeft';\n\timport { textAlignCenterIcon } from 'entasis/icons/textAlignCenter';\n\timport { textAlignRightIcon } from 'entasis/icons/textAlignRight';\n</script>\n\n<ButtonGroup \n\titems={[\n\t\t{ \n\t\t\tprefix: textAlignLeftIcon,\n\t\t\tchildren: 'Left' \n\t\t},\n\t\t{ \n\t\t\tprefix: textAlignCenterIcon,\n\t\t\tchildren: 'Center' \n\t\t},\n\t\t{ \n\t\t\tprefix: textAlignRightIcon,\n\t\t\tchildren: 'Right' \n\t\t}\n\t]}\n/>\n```\n\n### With Individual Click Handlers\n```svelte\n<script>\n\tfunction handleOption(option) {\n\t\tconsole.log(`Selected: ${option}`);\n\t}\n</script>\n\n<ButtonGroup \n\titems={[\n\t\t{ \n\t\t\tchildren: 'Save',\n\t\t\tonclick: () => handleOption('save')\n\t\t},\n\t\t{ \n\t\t\tchildren: 'Cancel',\n\t\t\tonclick: () => handleOption('cancel')\n\t\t}\n\t]}\n/>\n```\n\n### Disabled Group\n```svelte\n<ButtonGroup \n\tdisabled\n\titems={[\n\t\t{ children: 'Option 1' },\n\t\t{ children: 'Option 2' }\n\t]}\n/>\n```\n\n### Icon Only Buttons\n```svelte\n<script lang=\"ts\">\n\timport { ButtonGroup } from 'entasis/button-group';\n\timport { textBIcon } from 'entasis/icons/textB';\n\timport { textItalicIcon } from 'entasis/icons/textItalic';\n\timport { textUnderlineIcon } from 'entasis/icons/textUnderline';\n</script>\n\n<ButtonGroup \n\titems={[\n\t\t{ \n\t\t\tsquared: true,\n\t\t\tprefix: textBIcon\n\t\t},\n\t\t{ \n\t\t\tsquared: true,\n\t\t\tprefix: textItalicIcon\n\t\t},\n\t\t{ \n\t\t\tsquared: true,\n\t\t\tprefix: textUnderlineIcon\n\t\t}\n\t]}\n/>\n```\n\n### Mixed Button States\n```svelte\n<ButtonGroup \n\tvariant=\"outline\"\n\titems={[\n\t\t{ children: 'Active', color: 'primary' },\n\t\t{ children: 'Default', color: 'neutral' },\n\t\t{ children: 'Disabled', disabled: true }\n\t]}\n/>\n```\n\n### Segmented Control\n```svelte\n<script>\n\tlet selected = $state('week');\n</script>\n\n<ButtonGroup \n\titems={[\n\t\t{ \n\t\t\tchildren: 'Day',\n\t\t\tvariant: selected === 'day' ? 'solid' : 'ghost',\n\t\t\tonclick: () => selected = 'day'\n\t\t},\n\t\t{ \n\t\t\tchildren: 'Week',\n\t\t\tvariant: selected === 'week' ? 'solid' : 'ghost',\n\t\t\tonclick: () => selected = 'week'\n\t\t},\n\t\t{ \n\t\t\tchildren: 'Month',\n\t\t\tvariant: selected === 'month' ? 'solid' : 'ghost',\n\t\t\tonclick: () => selected = 'month'\n\t\t}\n\t]}\n/>\n```\n\n## Styling\n\nButtonGroup automatically:\n- Removes border-radius from middle buttons\n- Adjusts borders to prevent double borders\n- Creates a cohesive, connected appearance\n- Maintains consistent spacing\n\n## Accessibility\n\n- Each button maintains full keyboard accessibility\n- Focus styles are preserved\n- Disabled state cascades properly\n- Screen readers announce each button individually\n\n## Notes\n\n- Individual button props override shared props\n- Buttons are rendered in the order provided\n- The group container can be styled with the `class` prop\n- All Button component features are supported for individual buttons\n\n## Theme Customization\n\nThe ButtonGroup component uses a theme object that can be customized using the `theme` prop or by setting a global theme.\n\n### Theme Structure\n\nThe theme object contains the following parts:\n- **root**: Main button group container styles\n\n### Available Variants\n\n**root**:\n- base: Base classes for button group container (handles border radius and border connections between buttons)\n\n### Usage Examples\n\n**Basic Theme Override**:\n```svelte\n<ButtonGroup \n items={buttons}\n theme={{\n root: {\n base: 'flex items-center rounded-lg overflow-hidden'\n }\n }}\n/>\n```\n\n**Custom Group Styling**:\n```svelte\n<ButtonGroup \n items={buttons}\n theme={{\n root: {\n base: 'flex items-center gap-0 border-2 border-primary rounded-lg overflow-hidden'\n }\n }}\n/>\n```\n\n**Global Theme Setting**:\n```svelte\n<script>\n import { setButtonGroupTheme } from '../components/ButtonGroup/index.ts';\n \n setButtonGroupTheme({\n root: {\n base: 'flex items-center first-child:rounded-r-none last-child:rounded-l-none'\n }\n });\n</script>\n```\n";
31
31
  readonly 'segmented-control': "\n# SegmentedControl\n\nA compact single-selection input for switching between a small set of mutually exclusive modes. It uses radiogroup/radio semantics and an animated indicator that slides and resizes to the selected item.\n\n## Basic usage\n\n```svelte\n<script lang=\"ts\">\n import { SegmentedControl } from '../components/SegmentedControl/index.ts';\n import { gridFourIcon } from 'entasis/icons/gridFour';\n import { listIcon } from 'entasis/icons/list';\n\n const items = [\n { value: 'grid', label: 'Grid', icon: gridFourIcon },\n { value: 'list', label: 'List', icon: listIcon }\n ] as const;\n\n let value = $state<(typeof items)[number]['value']>('grid');\n</script>\n\n<SegmentedControl {items} bind:value />\n```\n\n## Props\n\n- **items**: `readonly SegmentedControlItem[]` — required options with unique `value` fields.\n- **value**: `string` — bindable selected value; defaults to the first enabled item.\n- **defaultValue**: `string` — initial selected value when value is omitted.\n- **onValueChange**: `(value: string) => void` — called once after user interaction changes the value.\n- **item**: `Snippet<[SegmentedControlItem]>` — replaces the default item renderer.\n- **size**: `'small' | 'normal' | 'large'` — defaults to `'normal'`.\n- **color**: semantic color — controls the selected pill and focus ring; defaults to `'neutral'`.\n- **variant**: `'normal' | 'pill'` — controls corner radius; defaults to the moderately rounded `'normal'` shape.\n- **disabled**: `boolean` — disables the full control.\n- **label**: `string` — accessible name for the radiogroup; defaults to the catalog's \"Segmented control\".\n- **class**: additional root classes.\n- **theme**: component theme overrides.\n\n## Item shape\n\n```ts\ntype SegmentedControlItem<Value extends string = string> = {\n value: Value;\n label?: string | Snippet;\n icon?: string | Snippet;\n disabled?: boolean;\n};\n```\n\nThe default renderer displays `icon`, then `label`. `label` is also the segment's accessible name: a string is painted and spoken, a snippet names the segment through its own content, and an icon-only segment (no `label`) falls back to its `value`.\n\n## Custom item renderer\n\n```svelte\n<SegmentedControl {items} bind:value>\n {#snippet item(option)}\n <span>{option.label}</span>\n <span>{option.count}</span>\n {/snippet}\n</SegmentedControl>\n```\n\nExtra fields on item objects remain available to the snippet through generic inference.\n\n## Shapes\n\n`variant=\"normal\"` uses a moderately rounded track and segments. Use `variant=\"pill\"` for the fully rounded stadium shape.\n\n```svelte\n<SegmentedControl {items} bind:value />\n<SegmentedControl {items} bind:value variant=\"pill\" />\n```\n\n## Keyboard behavior\n\n- Tab enters on the selected item, or the first enabled item when no value matches.\n- Arrow Left/Right moves and selects, wrapping at the ends.\n- Home/End selects the first/last enabled item.\n- Disabled items are skipped.\n\nEach item has an invisible, size-aware pointer target that extends beyond the visual track: 36px for small, 44px for normal, and 48px for large. Adjacent targets meet at the midpoint of the configured gap instead of overlapping.\n\nUse SegmentedControl for mode or value selection. Use Tabbar when changing navigational views with tab semantics.\n";
32
32
  readonly 'toggle-button': "\n# ToggleButton Component\n\nThe ToggleButton component is a two-state button that can be toggled on and off, useful for binary settings and selections.\n\n## Basic Usage\n\n```svelte\n<script>\n\tlet isActive = $state(false);\n</script>\n\n<ToggleButton bind:value={isActive}>\n\tToggle Me\n</ToggleButton>\n```\n\n## Props\n\n### Core Props\n- **value**: boolean (bindable) - Toggle state\n- **defaultValue**: boolean (default: false) - Initial state when value is omitted\n- **label**: string - Accessible name for icon-only buttons\n- **type**: 'button' | 'submit' | 'reset' (default: 'button') - Native button type. The default prevents accidental form submission.\n- **role**: 'radio' - Exposes the pressed state as `aria-checked` instead of `aria-pressed`. Set by ToggleButtonGroup in `type=\"single\"` mode; rarely needed on a standalone button\n\n### Visual Props\n- **color**: 'primary' | 'secondary' | 'neutral' | 'danger' | 'success' | 'warning' | 'info' (default: 'neutral')\n- **variant**: 'outline' | 'ghost' (default: 'ghost') — a Button subset. Resting outline uses the same border and surface recipe as Button; the pressed state adds a muted fill.\n- **size**: 'small' | 'normal' | 'large' (default: 'normal')\n\n### State Props\n- **disabled**: boolean (default: false) - Disables interaction\n\n### Event Props\n- **onValueChange**: (value: boolean) => void - Called once when toggle state changes\n\n### Content Slots\n- **children**: Snippet - Button content\n- **prefix**: Snippet - Content before text (typically icons)\n- **suffix**: Snippet - Content after text (typically icons)\n\n### Styling Props\n- **class**: string - Additional CSS classes\n- **theme**: ComponentTheme - Custom theme overrides\n\n## Examples\n\n### Basic Toggle\n```svelte\n<script>\n\tlet value = $state(false);\n</script>\n\n<ToggleButton bind:value>\n\t{value ? 'On' : 'Off'}\n</ToggleButton>\n```\n\n### With Icon\n```svelte\n<script lang=\"ts\">\n\timport { textBIcon } from 'entasis/icons/textB';\n\n\tlet isBold = $state(false);\n</script>\n\n<ToggleButton bind:value={isBold}>\n\t{#snippet prefix()}\n\t\t{@render textBIcon()}\n\t{/snippet}\n\tBold\n</ToggleButton>\n```\n\n### Different Variants\n```svelte\n<ToggleButton variant=\"ghost\" bind:value>Ghost</ToggleButton>\n<ToggleButton variant=\"outline\" bind:value>Outline</ToggleButton>\n```\n\n### Different Colors\n```svelte\n<ToggleButton color=\"primary\" bind:value>Primary</ToggleButton>\n<ToggleButton color=\"danger\" bind:value>Danger</ToggleButton>\n<ToggleButton color=\"success\" bind:value>Success</ToggleButton>\n```\n\n### Toolbar Buttons\n```svelte\n<script lang=\"ts\">\n\timport { textBIcon } from 'entasis/icons/textB';\n\timport { textItalicIcon } from 'entasis/icons/textItalic';\n\timport { textUnderlineIcon } from 'entasis/icons/textUnderline';\n\n\tlet format = $state({ bold: false, italic: false, underline: false });\n</script>\n\n<div class=\"flex gap-1\">\n\t<ToggleButton bind:value={format.bold}>\n\t\t{#snippet prefix()}\n\t\t\t{@render textBIcon()}\n\t\t{/snippet}\n\t</ToggleButton>\n\t<ToggleButton bind:value={format.italic}>\n\t\t{#snippet prefix()}\n\t\t\t{@render textItalicIcon()}\n\t\t{/snippet}\n\t</ToggleButton>\n\t<ToggleButton bind:value={format.underline}>\n\t\t{#snippet prefix()}\n\t\t\t{@render textUnderlineIcon()}\n\t\t{/snippet}\n\t</ToggleButton>\n</div>\n```\n\n### With Change Handler\n```svelte\n<script>\n\tfunction handleChange(value) {\n\t\tconsole.log('Toggled:', value);\n\t}\n</script>\n\n<ToggleButton onValueChange={handleChange}>\n\tNotify Me\n</ToggleButton>\n```\n\n### Disabled State\n```svelte\n<ToggleButton disabled value>Disabled On</ToggleButton>\n<ToggleButton disabled>Disabled Off</ToggleButton>\n```\n\n## Accessibility\n\n- Exposes the checked state through `aria-pressed` (or `aria-checked` when `role=\"radio\"`)\n- Renders `type=\"button\"` unless explicitly overridden\n- Keyboard accessible (Space/Enter to toggle)\n- Focus states for keyboard navigation\n- Screen reader friendly\n\n## Notes\n\n- Maintains toggle state between interactions\n- Visual feedback for checked/unchecked states\n- Can be used standalone or in ToggleButtonGroup\n- Supports all standard button features\n\n## Theme Customization\n\nThe ToggleButton component uses a theme object that can be customized using the `theme` prop or by setting a global theme.\n\n### Theme Structure\n\nThe theme object contains the following parts:\n- **root**: Main button element styles\n- **prefix**: Prefix icon/content styles\n- **suffix**: Suffix icon/content styles\n\n### Available Variants\n\n**root**:\n- base: Base classes for button element\n- Variants:\n - checked: boolean - Checked/toggled state styling\n - disabled: boolean - Disabled state styling\n - color: Color variants\n - variant: 'outline' | 'ghost' - Button variant\n - squared: boolean - Square button styling\n - size: 'small' | 'normal' | 'large' - Button size\n\n**prefix**:\n- base: Base classes for prefix content\n- Variants:\n - size: 'small' | 'normal' | 'large' - Icon size\n - checked: boolean - Checked state styling\n\n**suffix**:\n- base: Base classes for suffix content\n- Variants:\n - size: 'small' | 'normal' | 'large' - Icon size\n - checked: boolean - Checked state styling\n\n### Usage Examples\n\n**Basic Theme Override**:\n```svelte\n<ToggleButton \n bind:value\n theme={{\n root: {\n base: 'rounded-md transition-all',\n checked: {\n true: 'bg-primary text-white',\n false: 'bg-gray-200'\n },\n size: {\n normal: 'px-4 py-2'\n }\n }\n }}\n>\n Toggle\n</ToggleButton>\n```\n\n**Custom Checked State**:\n```svelte\n<script lang=\"ts\">\n import { checkIcon } from 'entasis/icons/check';\n</script>\n\n<ToggleButton \n bind:value\n variant=\"outline\"\n theme={{\n root: {\n variant: {\n outline: 'border-2'\n },\n checked: {\n true: 'border-primary bg-primary/10 text-primary',\n false: 'border-gray-300'\n }\n },\n prefix: {\n checked: {\n true: 'text-primary'\n }\n }\n }}\n>\n {#snippet prefix()}\n {@render checkIcon()}\n {/snippet}\n Toggle\n</ToggleButton>\n```\n\n**Global Theme Setting**:\n```svelte\n<script>\n import { setToggleButtonTheme } from '../components/ToggleButton/index.ts';\n \n setToggleButtonTheme({\n root: {\n base: 'transition-all duration-100',\n checked: {\n true: 'bg-primary text-white',\n false: 'bg-gray-200'\n },\n size: {\n normal: 'px-4 py-2 h-8'\n }\n },\n prefix: {\n size: {\n normal: 'max-w-6 max-h-6'\n }\n }\n });\n</script>\n```\n";
33
33
  readonly 'toggle-button-group': "\n# ToggleButtonGroup Component\n\nToggleButtonGroup renders an ordered list of ToggleButton items and exposes the pressed values as one bindable value.\n\n## Basic Usage\n\n```svelte\n<script lang=\"ts\">\n\tlet formatting = $state(['bold']);\n</script>\n\n<ToggleButtonGroup\n\tbind:value={formatting}\n\tlabel=\"Text formatting\"\n\tcolor=\"neutral\"\n\titems={[\n\t\t{ value: 'bold', children: 'Bold' },\n\t\t{ value: 'italic', children: 'Italic' },\n\t\t{ value: 'underline', children: 'Underline' }\n\t]}\n/>\n```\n\n## Props\n\n- **items**: ToggleButtonGroupItem[] (required) - Ordered button configurations. Each item carries a unique `value` plus ToggleButton props (no `variant`, `color`, or `size`; pressed state lives on the group value).\n- **label**: string (required) - Accessible name for the group.\n- **value**: string[] | string | undefined (bindable) - The only pressed-state source. `type=\"multiple\"` holds every pressed value as an array; `type=\"single\"` holds the checked value.\n- **defaultValue**: same shape as value - Initial pressed state when value is omitted.\n- **onValueChange**: (value) => void - Called once with the updated group value after a toggle.\n- **size**: 'small' | 'normal' | 'large' - Applied to every item.\n- **color**: Colors - Applied to every item.\n- **variant**: 'outline' | 'ghost' - Applied to every item. Defaults to 'ghost'.\n- **disabled**: boolean - Disables every item.\n- **joined**: boolean (default: false) - Renders the buttons as contiguous segments.\n- **type**: 'single' | 'multiple' (default: 'multiple') - `'multiple'` lets any number of buttons be pressed (`role=\"group\"`, `aria-pressed`) and its value is a `string[]`. `'single'` makes the selection exclusive: the value is a single string, the root becomes `role=\"radiogroup\"`, each button `role=\"radio\"` with `aria-checked`, pressing one clears the others, and the checked radio cannot be unpressed.\n- **class**: string - Additional CSS classes for the root.\n- **theme**: ToggleButtonGroupThemeProps - Theme overrides.\n\n## Examples\n\n### Icon Toolbar\n\n```svelte\n<ToggleButtonGroup\n\tbind:value={formatting}\n\tlabel=\"Text formatting\"\n\titems={[\n\t\t{ value: 'bold', prefix: textBIcon, label: 'Bold' },\n\t\t{ value: 'italic', prefix: textItalicIcon, label: 'Italic' },\n\t\t{ value: 'underline', prefix: textUnderlineIcon, label: 'Underline' }\n\t]}\n/>\n```\n\n### Joined Segments\n\n```svelte\n<ToggleButtonGroup\n\tjoined\n\tvariant=\"outline\"\n\tcolor=\"neutral\"\n\tlabel=\"Text formatting\"\n\tvalue={['bold']}\n\titems={[\n\t\t{ value: 'bold', prefix: textBIcon, label: 'Bold' },\n\t\t{ value: 'italic', prefix: textItalicIcon, label: 'Italic' },\n\t\t{ value: 'underline', prefix: textUnderlineIcon, label: 'Underline' }\n\t]}\n/>\n```\n\n### Single Selection (radio group)\n\n```svelte\n<script lang=\"ts\">\n\tlet alignment = $state('left');\n</script>\n\n<ToggleButtonGroup\n\ttype=\"single\"\n\tlabel=\"Alignment\"\n\tbind:value={alignment}\n\titems={[\n\t\t{ value: 'left', children: 'Left' },\n\t\t{ value: 'center', children: 'Center' },\n\t\t{ value: 'right', children: 'Right' }\n\t]}\n/>\n```\n\n### Change Handler\n\n```svelte\n<ToggleButtonGroup\n\tlabel=\"Density options\"\n\titems={[\n\t\t{ value: 'compact', children: 'Compact' },\n\t\t{ value: 'comfortable', children: 'Comfortable' }\n\t]}\n\tonValueChange={(value) => {\n\t\tconsole.log(value);\n\t}}\n/>\n```\n\n## Theme\n\n- **root**: Main button group container styles.\n\n## Accessibility\n\n- `type=\"multiple\"`: the root is `role=\"group\"` named by `label`; each ToggleButton exposes its independent state through `aria-pressed`.\n- `type=\"single\"`: the root is `role=\"radiogroup\"`; buttons are `role=\"radio\"` with `aria-checked`.\n- The group is a single tab stop (roving tabindex): Tab enters it, ArrowLeft / ArrowRight move between buttons (looping, mirrored in RTL), Home / End jump to the first / last, Space or Enter toggles the focused button.\n- Prefer SegmentedControl for a visually segmented exclusive choice; use `type=\"single\"` when you want toggle-button styling with exclusive semantics.\n\n## Theme example\n\n```svelte\n<ToggleButtonGroup\n\tlabel=\"Options\"\n\titems={items}\n\ttheme={{\n\t\troot: {\n\t\t\tbase: 'flex items-center gap-1'\n\t\t}\n\t}}\n/>\n```\n";
@@ -65,7 +65,7 @@ export declare const componentMcpRegistry: {
65
65
  readonly 'voice-input': "\n# VoiceInput Component\n\nVoiceInput records microphone audio into a bindable Blob, renders live microphone feedback, and previews the finalized recording with a seekable waveform. It can stay full width, expand leftward during capture, or remain mic-only with level-responsive rings.\n\n## Import\n\n```svelte\n<script lang=\"ts\">\n\timport { VoiceInput } from 'entasis/voice-input';\n</script>\n```\n\n## Basic usage\n\n```svelte\n<script lang=\"ts\">\n\timport { VoiceInput } from 'entasis/voice-input';\n\n\tlet recording = $state<Blob | null>(null);\n\tlet duration = $state(0);\n</script>\n\n<VoiceInput\n\tlabel=\"Voice note\"\n\tbind:value={recording}\n\tbind:duration\n\tminDuration={1}\n\tmaxDuration={60}\n/>\n```\n\n## Props\n\n- **value**: `Blob | null` (bindable, default: `null`) - Finalized microphone recording. Starting a successful new recording or using Clear removes the previous value.\n- **defaultValue**: `Blob | null` (default: `null`) - Initial recording used only when `value` is omitted.\n- **duration**: `number` (bindable, default: `0`) - Current or finalized duration in seconds.\n- **minDuration**: `number` (default: `0`) - Minimum accepted finalized duration in seconds. Shorter recordings surface a Field validation error.\n- **maxDuration**: `number | undefined` - Maximum duration in seconds. Recording stops automatically at this limit.\n- **color**: `Colors` (default: `'primary'`) - Focus and active recording color.\n- **variant**: `'default' | 'expandable' | 'compact'` (default: `'default'`) - Keeps the waveform full width, expands it only while recording or previewing, or renders a mic-only level indicator.\n- **size**: `'small' | 'normal' | 'large'` (default: `'normal'`) - Control height, waveform height, timer size, and action size.\n- **disabled**: `boolean` (default: `false`) - Prevents microphone, playback, seeking, and clear interactions.\n- **required**: `boolean` (default: `false`) - Requires a recorded Blob during Field/Form validation.\n- **startLabel**: `string` (default: `'Start voice recording'`) - Start-button accessible label and tooltip.\n- **stopLabel**: `string` (default: `'Stop recording'`) - Stop-button accessible label and tooltip.\n- **playLabel**: `string` (default: `'Play recording'`) - Playback-button label and tooltip while paused.\n- **pauseLabel**: `string` (default: `'Pause recording'`) - Playback-button label and tooltip while playing.\n- **seekLabel**: `string` (default: `'Seek recording'`) - Accessible label for the finalized waveform seek control.\n- **clearLabel**: `string` (default: `'Clear recording'`) - Clear-button accessible label and tooltip.\n- **onValueChange**: `(value: Blob | null) => void` - Runs when the field value changes, including Clear.\n- **onStart**: `() => void` - Runs after microphone capture starts.\n- **onStop**: `({ blob, duration }: VoiceInputResult) => void` - Runs after MediaRecorder finalizes the recording.\n- **onError**: `(error: Error) => void` - Runs when permission, recording, or playback fails.\n- **theme**: `VoiceInputThemeProps & FieldThemeProps` - Overrides voice-input and inherited Field theme parts.\n\nVoiceInput also accepts the standard Field props and slots, including `label`, `description`, `helper`, errors, and attachments.\n\n## Form configuration\n\n```svelte\n<Form\n\tinputs={{\n\t\tmessage: {\n\t\t\ttype: 'voice',\n\t\t\tlabel: 'Voice message',\n\t\t\trequired: true,\n\t\t\tminDuration: 1,\n\t\t\tmaxDuration: 60\n\t\t}\n\t}}\n\tonSubmit={(value) => uploadVoiceMessage(value.message)}\n/>\n```\n\n## Behavior\n\n- The waveform is drawn on a responsive canvas and maps live microphone RMS through a logarithmic decibel scale so quiet speech remains visible.\n- New samples enter from the right and push older samples left.\n- `maxDuration` schedules a hard automatic stop and caps the published duration at the configured limit.\n- `minDuration` participates in normal Field validation after MediaRecorder publishes the final Blob.\n- The `expandable` variant has no hidden full-width footprint while collapsed, then anchors the action on the right and animates the waveform surface toward the left while recording, finalizing, or previewing saved audio.\n- The `compact` variant omits the waveform and timer, uses a solid active surface while recording, and scales two concentric rings with the same logarithmic microphone level.\n- Finalized recordings expose play/pause and clear actions. The saved waveform becomes a native range-based playback timeline with elapsed progress, pointer scrubbing, and keyboard seeking.\n- Clear pauses playback, revokes the Blob URL, resets duration and waveform state, and publishes `null` through the normal field value contract.\n- Playback creates its Blob URL lazily and revokes it when the value changes or the component unmounts.\n- Stopping waits for MediaRecorder's final `dataavailable` event before publishing the Blob.\n- Starting another recording replaces the previous value only once microphone setup succeeds.\n- Permission denial, missing microphones, and unsupported browser APIs are surfaced in the control and through `onError`.\n- Microphone tracks, cutoff timers, animation frames, audio graph nodes, observers, and the AudioContext are released on stop, failure, or unmount.\n\n## Accessibility\n\n- The visible field label targets the current microphone/stop button.\n- Microphone, stop, play, pause, and clear actions have accessible labels and tooltips.\n- Recording and playback state changes are announced through a polite live region without announcing the timer every second.\n- Operational errors use `role=\"alert\"`; duration requirements use the inherited Field error rendering.\n- The canvas is decorative and hidden from assistive technology. A transparent native range input exposes the finalized waveform as a labelled slider with elapsed and total time.\n- Compact rings are decorative pseudo-elements and add no accessibility-tree content.\n\n## Browser requirements\n\nMicrophone capture requires a secure context (HTTPS, with localhost allowed by browsers), user permission, MediaRecorder, and Web Audio API support.\n\n## Theme parts\n\n- `root` - Field root and variant width behavior.\n- `inputContainer` - Input-like recording surface.\n- `content` - Waveform, operational error, and timer region.\n- `action` - Microphone/stop action, including compact level rings.\n- `playbackAction` - Play/pause action shown for a finalized recording.\n- `clearAction` - Clear action shown for a finalized recording.\n- `waveformContainer` - Relative live/playback waveform surface and focus treatment.\n- `waveform` - Responsive visual canvas.\n- `waveformInput` - Native range hitbox layered over a finalized waveform.\n- `timer` - Stable tabular duration.\n- `error` - Operational error message.\n\n## State contract\n\n- **value**: current bindable editable value.\n- **defaultValue**: initial value used only when `value` is omitted.\n- **onValueChange**: called with the new value when the component changes it.\n\n";
66
66
  readonly avatar: "\n# Avatar Component\n\nThe Avatar component displays a user's profile picture with fallback initials. It supports a bindable loading flag, various sizes, and badges (prefix/suffix). The AvatarGroup component displays multiple avatars with overlap.\n\n## Basic Usage\n\n```svelte\n<Avatar name=\"John Doe\" src=\"https://example.com/avatar.png\" />\n```\n\n## Props\n\n### Core Props\n- **name**: string (required) - Display name; its initials are the fallback when no image renders\n- **src**: string - Image URL; omit it, or let it fail, to render the initials instead\n- **alt**: string - Alternative text for the image (defaults to `name`)\n- **size**: 'small' | 'normal' | 'large' (default: 'normal')\n - small: 24px (1.5rem)\n - normal: 32px (2rem)\n - large: 40px (2.5rem)\n\n### Loading Props\n- **delay**: number (default: 0) - Delay in milliseconds before showing avatar\n- **loading**: boolean (default: false, bindable) - True while the image request is in flight; false with no `src`, and false once the image has loaded or failed. A failed image is reported by rendering the initials.\n\n### Content Slots\n- **prefix**: Slot - Badge/icon positioned at bottom-left\n- **suffix**: Slot - Badge/icon positioned at bottom-right\n\n### Styling Props\n- **class**: string - Additional CSS classes\n- **theme**: ComponentTheme - Custom theme overrides\n\n## Avatar Structure\n\n```\n<Avatar>\n\t<AvatarImage /> <!-- Profile picture -->\n\t<AvatarPrefix /> <!-- Behind the image (bottom-left) -->\n\t<AvatarSuffix /> <!-- Absolute position (bottom-right) -->\n\t<AvatarInitials /> <!-- Fallback initials -->\n</Avatar>\n```\n\n## AvatarGroup Props\n\n### Core Props\n- **items**: Array<{ src?: string; alt?: string; name: string } & T> (required) - Avatar items\n- **max**: number - Maximum number of avatars to show before \"+N\" indicator\n- **size**: 'small' | 'normal' | 'large' (default: 'normal')\n\n### Content Slots\n- **avatar**: Snippet<{ item: T; index: number; avatarProps }> - Custom avatar rendering\n- **remainingCount**: Snippet<{ items: T[]; remaining: number }> - Custom \"+N\" counter rendering\n\n### Styling Props\n- **class**: string - Additional CSS classes\n- **theme**: ComponentTheme - Custom theme overrides\n\n## AvatarGroup Structure\n\n```\n<AvatarGroup>\n\t<Avatar />\n\t<Avatar />\n\t<Avatar />\n\t<AvatarGroupCount /> <!-- \"+N\" indicator -->\n</AvatarGroup>\n```\n\n## Examples\n\n### Basic Avatar\n```svelte\n<Avatar name=\"Jane Smith\" src=\"/images/jane.jpg\" />\n```\n\n### Without Image (Initials)\n```svelte\n<Avatar name=\"John Doe\" />\n<!-- Displays \"JD\" -->\n```\n\n### Different Sizes\n```svelte\n<Avatar size=\"small\" name=\"Small User\" />\n<Avatar size=\"normal\" name=\"Normal User\" />\n<Avatar size=\"large\" name=\"Large User\" />\n```\n\n### With Status Badge (Prefix)\n```svelte\n<Avatar name=\"John Doe\">\n\t{#snippet prefix()}\n\t\t<div class=\"w-2 h-2 rounded-full bg-success\"></div>\n\t{/snippet}\n</Avatar>\n```\n\n### With Icon Badge (Suffix)\n```svelte\n<script lang=\"ts\">\n\timport { Avatar } from 'entasis/avatar';\n\timport { checkIcon } from 'entasis/icons/check';\n</script>\n\n<Avatar name=\"Jane Smith\">\n\t{#snippet suffix()}\n\t\t{@render checkIcon({ class: 'text-success' })}\n\t{/snippet}\n</Avatar>\n```\n\n### With Loading State\n```svelte\n<script lang=\"ts\">\n\tlet loading = $state(false);\n</script>\n\n<Avatar name=\"John Doe\" src=\"/avatar.jpg\" bind:loading />\n{loading}\n```\n\n### Avatar Group\n```svelte\n<script>\n\tlet items = [\n\t\t{ name: 'John Doe', src: '/john.jpg' },\n\t\t{ name: 'Jane Smith', src: '/jane.jpg' },\n\t\t{ name: 'Bob Johnson', src: '/bob.jpg' }\n\t];\n</script>\n\n<AvatarGroup {items} />\n```\n\n### Avatar Group with Max Limit\n```svelte\n<AvatarGroup \n\titems={[\n\t\t{ name: 'User 1' },\n\t\t{ name: 'User 2' },\n\t\t{ name: 'User 3' },\n\t\t{ name: 'User 4' },\n\t\t{ name: 'User 5' }\n\t]}\n\tmax={3}\n/>\n<!-- Shows 3 avatars + \"+2\" indicator -->\n```\n\n### Custom Avatar in Group\n```svelte\n<script lang=\"ts\">\n\timport { Avatar, AvatarGroup } from 'entasis/avatar';\n\n\tlet items = [{ name: 'John Doe' }, { name: 'Jane Smith' }];\n</script>\n\n<AvatarGroup {items}>\n\t{#snippet avatar({ item, index, avatarProps })}\n\t\t<Avatar {...avatarProps} {...item}>\n\t\t\t{#snippet suffix()}\n\t\t\t\t<span class=\"text-xs\">{index + 1}</span>\n\t\t\t{/snippet}\n\t\t</Avatar>\n\t{/snippet}\n</AvatarGroup>\n```\n\n### Custom Remaining Count\n```svelte\n<script lang=\"ts\">\n\timport { AvatarGroup } from 'entasis/avatar';\n\n\tlet items = [{ name: 'User 1' }, { name: 'User 2' }, { name: 'User 3' }, { name: 'User 4' }];\n</script>\n\n<AvatarGroup {items} max={3}>\n\t{#snippet remainingCount({ remaining })}\n\t\t<div class=\"avatar-count\">\n\t\t\t+{remaining} more\n\t\t</div>\n\t{/snippet}\n</AvatarGroup>\n```\n\n### Status Indicators\n```svelte\n<Avatar name=\"Online User\">\n\t{#snippet suffix()}\n\t\t<div class=\"w-3 h-3 rounded-full bg-success border-2 border-surface\"></div>\n\t{/snippet}\n</Avatar>\n\n<Avatar name=\"Away User\">\n\t{#snippet suffix()}\n\t\t<div class=\"w-3 h-3 rounded-full bg-warning border-2 border-surface\"></div>\n\t{/snippet}\n</Avatar>\n```\n\n## Accessibility\n\n- `alt` defaults to `name`, so the image always carries alt text\n- Fallback to initials when image fails to load\n- Proper foreground for initials display\n- The image loading flag is bindable\n\n## Notes\n\n- Initials are automatically extracted from the name (first letter of first two words)\n- Avatar image uses object-cover to maintain aspect ratio\n- Prefix badge is positioned at bottom-left, behind the image\n- Suffix badge is positioned at bottom-right, absolute positioning\n- Avatar group creates overlapping effect with negative margins\n- `loading` is true only while the image request is in flight; a failed image falls back to the initials\n- Each new `src` is loaded from scratch: `loading` goes true again and a working URL recovers the picture after a broken one\n\n## Theme Customization\n\nThe Avatar component uses a theme object that can be customized using the `theme` prop or by setting a global theme.\n\n### Theme Structure\n\nThe theme object contains the following parts:\n- **root**: Main avatar container styles\n- **avatarImage**: Avatar image element styles\n- **avatarPrefix**: Prefix badge styles (bottom-left)\n- **avatarSuffix**: Suffix badge styles (bottom-right)\n- **avatarInitials**: Fallback initials display styles\n\n### Theme Type Definition\n\n```typescript\nimport type { AvatarThemeProps } from 'entasis/avatar';\n\n// Example theme customization\nconst customTheme: AvatarThemeProps = {\n root: {\n base: 'custom-base-classes',\n size: {\n small: 'size-6',\n normal: 'size-8',\n large: 'size-10'\n }\n },\n avatarImage: {\n size: {\n small: '',\n normal: '',\n large: ''\n }\n },\n avatarPrefix: {\n size: {\n small: 'size-5 right-[-0.25rem] bottom-[-0.25rem]',\n normal: 'size-4 right-[-0.3rem] bottom-[-0.2rem]',\n large: 'size-5 left-[-0.4rem] bottom-[-0.4rem]'\n }\n },\n avatarSuffix: {\n size: {\n small: 'size-3.5 left-[-0.25rem] bottom-[-0.25rem]',\n normal: 'size-4 right-[-0.3rem] bottom-[-0.3rem]',\n large: 'size-3.5 right-[-0.4rem] bottom-[-0.4rem]'\n }\n },\n avatarInitials: {\n size: {\n small: 'text-xs',\n normal: 'text-sm',\n large: 'text-base'\n }\n }\n};\n```\n\n### Available Variants\n\n**root**:\n- base: Base classes applied to all avatars\n- Variants:\n - size: 'small' | 'normal' | 'large' - Controls avatar dimensions (6/8/10)\n\n**avatarImage**:\n- base: Base classes for avatar image\n- Variants:\n - size: 'small' | 'normal' | 'large' - Inherited from avatar size\n\n**avatarPrefix**:\n- base: Base classes for prefix badge\n- Variants:\n - size: 'small' | 'normal' | 'large' - Badge size and positioning based on avatar size\n\n**avatarSuffix**:\n- base: Base classes for suffix badge\n- Variants:\n - size: 'small' | 'normal' | 'large' - Badge size and positioning based on avatar size\n\n**avatarInitials**:\n- base: Base classes for initials fallback\n- Variants:\n - size: 'small' | 'normal' | 'large' - Text size based on avatar size\n\n### Usage Examples\n\n**Basic Theme Override**:\n```svelte\n<Avatar \n name=\"John Doe\"\n theme={{\n root: {\n base: 'ring-2 ring-primary',\n size: {\n large: 'size-12'\n }\n },\n avatarInitials: {\n size: {\n large: 'text-lg'\n }\n }\n }}\n/>\n```\n\n**Custom Badge Styling**:\n```svelte\n<Avatar\n name=\"Jane Smith\"\n theme={{\n avatarSuffix: {\n size: {\n normal: 'size-5 ring-2 ring-white'\n }\n }\n }}\n>\n {#snippet suffix()}\n <div class=\"w-3 h-3 bg-success rounded-full\"></div>\n {/snippet}\n</Avatar>\n```\n\n**Global Theme Setting**:\n```svelte\n<script>\n import { setAvatarTheme } from '../components/Avatar/index.ts';\n \n setAvatarTheme({\n root: {\n base: 'ring-2 ring-neutral-muted transition-all',\n size: {\n normal: 'size-10'\n }\n },\n avatarInitials: {\n base: 'font-bold'\n }\n });\n</script>\n```\n";
67
67
  readonly 'avatar-group': "\n# Avatar Component\n\nThe Avatar component displays a user's profile picture with fallback initials. It supports a bindable loading flag, various sizes, and badges (prefix/suffix). The AvatarGroup component displays multiple avatars with overlap.\n\n## Basic Usage\n\n```svelte\n<Avatar name=\"John Doe\" src=\"https://example.com/avatar.png\" />\n```\n\n## Props\n\n### Core Props\n- **name**: string (required) - Display name; its initials are the fallback when no image renders\n- **src**: string - Image URL; omit it, or let it fail, to render the initials instead\n- **alt**: string - Alternative text for the image (defaults to `name`)\n- **size**: 'small' | 'normal' | 'large' (default: 'normal')\n - small: 24px (1.5rem)\n - normal: 32px (2rem)\n - large: 40px (2.5rem)\n\n### Loading Props\n- **delay**: number (default: 0) - Delay in milliseconds before showing avatar\n- **loading**: boolean (default: false, bindable) - True while the image request is in flight; false with no `src`, and false once the image has loaded or failed. A failed image is reported by rendering the initials.\n\n### Content Slots\n- **prefix**: Slot - Badge/icon positioned at bottom-left\n- **suffix**: Slot - Badge/icon positioned at bottom-right\n\n### Styling Props\n- **class**: string - Additional CSS classes\n- **theme**: ComponentTheme - Custom theme overrides\n\n## Avatar Structure\n\n```\n<Avatar>\n\t<AvatarImage /> <!-- Profile picture -->\n\t<AvatarPrefix /> <!-- Behind the image (bottom-left) -->\n\t<AvatarSuffix /> <!-- Absolute position (bottom-right) -->\n\t<AvatarInitials /> <!-- Fallback initials -->\n</Avatar>\n```\n\n## AvatarGroup Props\n\n### Core Props\n- **items**: Array<{ src?: string; alt?: string; name: string } & T> (required) - Avatar items\n- **max**: number - Maximum number of avatars to show before \"+N\" indicator\n- **size**: 'small' | 'normal' | 'large' (default: 'normal')\n\n### Content Slots\n- **avatar**: Snippet<{ item: T; index: number; avatarProps }> - Custom avatar rendering\n- **remainingCount**: Snippet<{ items: T[]; remaining: number }> - Custom \"+N\" counter rendering\n\n### Styling Props\n- **class**: string - Additional CSS classes\n- **theme**: ComponentTheme - Custom theme overrides\n\n## AvatarGroup Structure\n\n```\n<AvatarGroup>\n\t<Avatar />\n\t<Avatar />\n\t<Avatar />\n\t<AvatarGroupCount /> <!-- \"+N\" indicator -->\n</AvatarGroup>\n```\n\n## Examples\n\n### Basic Avatar\n```svelte\n<Avatar name=\"Jane Smith\" src=\"/images/jane.jpg\" />\n```\n\n### Without Image (Initials)\n```svelte\n<Avatar name=\"John Doe\" />\n<!-- Displays \"JD\" -->\n```\n\n### Different Sizes\n```svelte\n<Avatar size=\"small\" name=\"Small User\" />\n<Avatar size=\"normal\" name=\"Normal User\" />\n<Avatar size=\"large\" name=\"Large User\" />\n```\n\n### With Status Badge (Prefix)\n```svelte\n<Avatar name=\"John Doe\">\n\t{#snippet prefix()}\n\t\t<div class=\"w-2 h-2 rounded-full bg-success\"></div>\n\t{/snippet}\n</Avatar>\n```\n\n### With Icon Badge (Suffix)\n```svelte\n<script lang=\"ts\">\n\timport { Avatar } from 'entasis/avatar';\n\timport { checkIcon } from 'entasis/icons/check';\n</script>\n\n<Avatar name=\"Jane Smith\">\n\t{#snippet suffix()}\n\t\t{@render checkIcon({ class: 'text-success' })}\n\t{/snippet}\n</Avatar>\n```\n\n### With Loading State\n```svelte\n<script lang=\"ts\">\n\tlet loading = $state(false);\n</script>\n\n<Avatar name=\"John Doe\" src=\"/avatar.jpg\" bind:loading />\n{loading}\n```\n\n### Avatar Group\n```svelte\n<script>\n\tlet items = [\n\t\t{ name: 'John Doe', src: '/john.jpg' },\n\t\t{ name: 'Jane Smith', src: '/jane.jpg' },\n\t\t{ name: 'Bob Johnson', src: '/bob.jpg' }\n\t];\n</script>\n\n<AvatarGroup {items} />\n```\n\n### Avatar Group with Max Limit\n```svelte\n<AvatarGroup \n\titems={[\n\t\t{ name: 'User 1' },\n\t\t{ name: 'User 2' },\n\t\t{ name: 'User 3' },\n\t\t{ name: 'User 4' },\n\t\t{ name: 'User 5' }\n\t]}\n\tmax={3}\n/>\n<!-- Shows 3 avatars + \"+2\" indicator -->\n```\n\n### Custom Avatar in Group\n```svelte\n<script lang=\"ts\">\n\timport { Avatar, AvatarGroup } from 'entasis/avatar';\n\n\tlet items = [{ name: 'John Doe' }, { name: 'Jane Smith' }];\n</script>\n\n<AvatarGroup {items}>\n\t{#snippet avatar({ item, index, avatarProps })}\n\t\t<Avatar {...avatarProps} {...item}>\n\t\t\t{#snippet suffix()}\n\t\t\t\t<span class=\"text-xs\">{index + 1}</span>\n\t\t\t{/snippet}\n\t\t</Avatar>\n\t{/snippet}\n</AvatarGroup>\n```\n\n### Custom Remaining Count\n```svelte\n<script lang=\"ts\">\n\timport { AvatarGroup } from 'entasis/avatar';\n\n\tlet items = [{ name: 'User 1' }, { name: 'User 2' }, { name: 'User 3' }, { name: 'User 4' }];\n</script>\n\n<AvatarGroup {items} max={3}>\n\t{#snippet remainingCount({ remaining })}\n\t\t<div class=\"avatar-count\">\n\t\t\t+{remaining} more\n\t\t</div>\n\t{/snippet}\n</AvatarGroup>\n```\n\n### Status Indicators\n```svelte\n<Avatar name=\"Online User\">\n\t{#snippet suffix()}\n\t\t<div class=\"w-3 h-3 rounded-full bg-success border-2 border-surface\"></div>\n\t{/snippet}\n</Avatar>\n\n<Avatar name=\"Away User\">\n\t{#snippet suffix()}\n\t\t<div class=\"w-3 h-3 rounded-full bg-warning border-2 border-surface\"></div>\n\t{/snippet}\n</Avatar>\n```\n\n## Accessibility\n\n- `alt` defaults to `name`, so the image always carries alt text\n- Fallback to initials when image fails to load\n- Proper foreground for initials display\n- The image loading flag is bindable\n\n## Notes\n\n- Initials are automatically extracted from the name (first letter of first two words)\n- Avatar image uses object-cover to maintain aspect ratio\n- Prefix badge is positioned at bottom-left, behind the image\n- Suffix badge is positioned at bottom-right, absolute positioning\n- Avatar group creates overlapping effect with negative margins\n- `loading` is true only while the image request is in flight; a failed image falls back to the initials\n- Each new `src` is loaded from scratch: `loading` goes true again and a working URL recovers the picture after a broken one\n\n## Theme Customization\n\nThe Avatar component uses a theme object that can be customized using the `theme` prop or by setting a global theme.\n\n### Theme Structure\n\nThe theme object contains the following parts:\n- **root**: Main avatar container styles\n- **avatarImage**: Avatar image element styles\n- **avatarPrefix**: Prefix badge styles (bottom-left)\n- **avatarSuffix**: Suffix badge styles (bottom-right)\n- **avatarInitials**: Fallback initials display styles\n\n### Theme Type Definition\n\n```typescript\nimport type { AvatarThemeProps } from 'entasis/avatar';\n\n// Example theme customization\nconst customTheme: AvatarThemeProps = {\n root: {\n base: 'custom-base-classes',\n size: {\n small: 'size-6',\n normal: 'size-8',\n large: 'size-10'\n }\n },\n avatarImage: {\n size: {\n small: '',\n normal: '',\n large: ''\n }\n },\n avatarPrefix: {\n size: {\n small: 'size-5 right-[-0.25rem] bottom-[-0.25rem]',\n normal: 'size-4 right-[-0.3rem] bottom-[-0.2rem]',\n large: 'size-5 left-[-0.4rem] bottom-[-0.4rem]'\n }\n },\n avatarSuffix: {\n size: {\n small: 'size-3.5 left-[-0.25rem] bottom-[-0.25rem]',\n normal: 'size-4 right-[-0.3rem] bottom-[-0.3rem]',\n large: 'size-3.5 right-[-0.4rem] bottom-[-0.4rem]'\n }\n },\n avatarInitials: {\n size: {\n small: 'text-xs',\n normal: 'text-sm',\n large: 'text-base'\n }\n }\n};\n```\n\n### Available Variants\n\n**root**:\n- base: Base classes applied to all avatars\n- Variants:\n - size: 'small' | 'normal' | 'large' - Controls avatar dimensions (6/8/10)\n\n**avatarImage**:\n- base: Base classes for avatar image\n- Variants:\n - size: 'small' | 'normal' | 'large' - Inherited from avatar size\n\n**avatarPrefix**:\n- base: Base classes for prefix badge\n- Variants:\n - size: 'small' | 'normal' | 'large' - Badge size and positioning based on avatar size\n\n**avatarSuffix**:\n- base: Base classes for suffix badge\n- Variants:\n - size: 'small' | 'normal' | 'large' - Badge size and positioning based on avatar size\n\n**avatarInitials**:\n- base: Base classes for initials fallback\n- Variants:\n - size: 'small' | 'normal' | 'large' - Text size based on avatar size\n\n### Usage Examples\n\n**Basic Theme Override**:\n```svelte\n<Avatar \n name=\"John Doe\"\n theme={{\n root: {\n base: 'ring-2 ring-primary',\n size: {\n large: 'size-12'\n }\n },\n avatarInitials: {\n size: {\n large: 'text-lg'\n }\n }\n }}\n/>\n```\n\n**Custom Badge Styling**:\n```svelte\n<Avatar\n name=\"Jane Smith\"\n theme={{\n avatarSuffix: {\n size: {\n normal: 'size-5 ring-2 ring-white'\n }\n }\n }}\n>\n {#snippet suffix()}\n <div class=\"w-3 h-3 bg-success rounded-full\"></div>\n {/snippet}\n</Avatar>\n```\n\n**Global Theme Setting**:\n```svelte\n<script>\n import { setAvatarTheme } from '../components/Avatar/index.ts';\n \n setAvatarTheme({\n root: {\n base: 'ring-2 ring-neutral-muted transition-all',\n size: {\n normal: 'size-10'\n }\n },\n avatarInitials: {\n base: 'font-bold'\n }\n });\n</script>\n```\n";
68
- readonly chart: "\n# Chart Component\n\nChart renders layered cartesian, polar, relation, or faceted marks from one typed data array. Consumers import only from `entasis/chart`; TanStack Charts and D3 stay private implementation details of the component, but they must be installed as optional peer dependencies.\n\n## Requires\n\nChart renders through TanStack Charts and D3. Those packages are optional peer dependencies of entasis, so install them alongside it:\n\n`pnpm add @tanstack/charts d3-array d3-force d3-hierarchy d3-sankey d3-scale d3-shape`\n\nOne public type (`curve`) is D3's `CurveFactory`, so TypeScript users add its typings: `pnpm add -D @types/d3-shape`.\n\n## Basic usage\n\n```svelte\n<script lang=\"ts\">\n import { Chart } from '../components/Chart/index.ts'\n\n type Row = { month: Date; actual: number; forecast: number; low: number; high: number }\n const x = { scale: { type: 'utc' }, axis: { label: 'Month' } } as const\n const y = { scale: { type: 'linear' }, grid: true } as const\n const marks = [\n {\n type: 'series',\n x: 'month',\n y: 'forecast',\n interval: { lower: 'low', upper: 'high', fill: 'secondary' },\n analysis: [\n { type: 'reference', statistic: 'median' },\n { type: 'rolling', statistic: 'mean', window: 3 }\n ]\n },\n { type: 'series', x: 'month', y: 'actual', stroke: 'primary', points: true }\n ] as const\n</script>\n\n<Chart\n data={rows}\n {x}\n {y}\n {marks}\n tooltip\n viewport\n label=\"Monthly revenue\"\n height={320}\n/>\n```\n\n## Contract\n\n- `data` is one immutable array shared by every mark.\n- `marks` is a required non-empty discriminated union; array order is paint order.\n- A `series` mark renders a line by default. Set `area`, `points`, or `line` with booleans or local option objects to compose its visible layers.\n- A series `interval` adds a non-interactive band behind the same line. Its required `lower` and `upper` numeric channels represent explicit bounds such as confidence, prediction, credible, or min/max intervals. `interval` and `area` are mutually exclusive because both own the filled surface.\n- `analysis` is a non-empty list of derived statistical layers owned by a series, scatter, bar, or distribution mark. Reference analysis supports mean, median, quantile, and standard deviation. Series and scatter support linear regression with optional confidence or prediction intervals. Series also supports rolling mean and rolling median. Analysis can use the complete plot or each series independently and does not add tooltip points. Stacked layouts reject analysis because their displayed values differ from the source channels.\n- A `scatter` mark renders independent observations with the default `points` variant. A numeric `size` is a constant pixel radius. A `size` data channel uses `sqrt` by default; `sizeScale` accepts `linear`, `sqrt`, `log`, `exp`, or an object with `type`, `domain`, `range`, and an optional `base` for logarithmic or exponential scales. The `hexbin` variant accepts numeric `x` and `y` channels and aggregates dense observations into responsive pixel-space hexagons; `radius` controls the bin size.\n- A `bar` mark is simple by default. Its optional `variant` is `group` or `stack`. Use `offset: 'normalize'` on a stacked bar to compare proportions with a 0–1 value axis. A stacked bar also accepts `gap` (pixels of surface between consecutive segments of one stack).\n- Wide data: a stacked bar, and an area series with `layout: { type: 'stack' }`, accept a list of numeric fields as their value channel (`y: ['completed', 'inProgress', 'pending']`, or `x` when horizontal). The rows are melted internally into one series per field, the field name becoming the series key, in the order the fields are listed. A wide mark owns its series identity, so it takes no `series` or `colorBy`, and rejects `annotations` and `analysis`. Long format with a `series` channel keeps working unchanged.\n- A `matrix` mark uses the default `grid` variant with categorical `x` and `y` channels. Use `colorBy` for a categorical matrix, or use a numeric `value` with `color` for a heatmap. The `calendar` variant replaces `x` and `y` with a `date` channel and derives week and weekday axes automatically. Its optional `colorScale` accepts a `quantize` domain and an ordered color range.\n- A data mark can own `arrow`, `label`, `rule`, `band`, and `marker` annotations. Targets resolve against the parent mark's final rendered coordinates. A band annotation uses `thickness` for its cross-axis size.\n- Position scales use local string discriminants such as `linear`, `utc`, and `band`.\n- `tooltip: true` groups cartesian marks on the categorical or x axis, renders native color rows, and stays inside the chart surface without adding chart focus states.\n- `palette` is either an ordered list consumed in series-discovery order, or a record keyed by series key (`{ completed: 'success', pending: 'danger' }`). Record entries win by key; a series without an entry falls back to the default palette in discovery order.\n- `ChartColor` accepts the seven semantic roles and the surface family (`surface`, `surface-recessed`, `surface-canvas`, `surface-raised`, `surface-floating`); anything else is passed through as a CSS color.\n- `legend: true` shows a categorical color key, or a numeric heatmap/hexbin color ramp. Use `legend: { interactive: true }` to hide and show series, bars, scatter points, and empirical distributions whose series and color identities match. Other layouts use static legends; facets keep their own labels. Visibility preserves axes, colors, and stack totals. The object accepts `placement: 'top' | 'bottom'`, `label`, controlled `value` and `onValueChange`, or an initial `defaultValue`; omitted visibility shows every series.\n- `legend.format: (key) => string` sets the display text of a series independently of its key. The same formatter labels the series in the tooltip.\n- A categorical legend is drawn by the library, not by the rendering engine: the interactive one is a `ToggleButtonGroup` of small ghost toggles, one per series, each carrying a color swatch of the resolved series color and the `legend.format` label, pressed when the series is visible; the static one is the same swatch and label as plain items. It takes its own row above or below the plot and the plot shrinks by that row. A numeric legend stays a color ramp drawn inside the plot. Style the row with the `legend`, `legendItem`, and `legendSwatch` theme parts.\n- Legend layout accepts `align: 'left' | 'center' | 'right'` and `orientation: 'horizontal' | 'vertical'`. Vertical stacks categorical entries in one column; numeric color ramps stay horizontal. Both static and interactive legends support alignment and top/bottom placement.\n- Histogram and rolling preparation use native transforms. Regression uses native Student-t confidence bounds; prediction intervals add observation uncertainty to the fitted-mean bounds. Small samples therefore have wider intervals than a fixed normal approximation.\n- Tooltip objects can override grouping with `groupBy: 'x'`, `groupBy: 'y'`, or `groupBy: false`.\n- A tooltip can be pinned: `tooltip.value` / `tooltip.defaultValue` take the row key of the pinned datum (the mark's `key` channel when it has one, its x value otherwise; `null` pins nothing). A pinned row shows its tooltip on mount, hover moves the tooltip normally, pointer leave restores the pinned row, and clicking a datum pins it (clicking it again unpins) through `tooltip.onValueChange`. Pinning is disabled while `viewport` owns the press gesture.\n- `viewport: true` enables native x-axis brush zoom with pointer and touch input, an accessible reset control, and a reduced-motion-aware transition. Numeric and date axes select continuous windows; categorical axes snap to values and also expose keyboard handles. The object form accepts `reset` and `transition` options. Native 2D brushing is not supported.\n- A `distribution` mark reads one categorical `group` channel and one numeric `value` channel. Its summary variants are `violin`, `box`, and `error-bar`. Its raw-sample variants are `histogram`, `density`, and `ecdf`. Histogram accepts `bins`; density accepts `bandwidth` and `samples`; `direction` can be `vertical` or `horizontal`.\n- Distribution tooltips use a built-in statistical summary. They do not accept custom `tooltip.fields`.\n- A `proportion` mark reads one categorical `category` channel and one non-negative numeric `value` channel. Its `variant` is `pie`, `donut`, or `waffle`; consumers do not calculate angles or cells.\n- A `polar` mark reads `angle` and `radius` channels. The `circular` and `radar` variants compose boolean `area`, `line`, and `points` layers. The `radial-bar` and `rose` variants render wedges and expose only bar options.\n- A `relation` mark owns a complete topology layout. Its `tree` variant reads `nodeId` and `parent`; its `network` and `sankey` variants read `nodeId` and an outgoing `relations` channel. Relation marks cannot use axes, sibling marks, facets, or custom tooltip fields.\n- A `facet` mark owns nested `marks` with the same public union and inherits the plot positions.\n- Proportion tooltips show the category value and its share. They do not accept custom `tooltip.fields` or grouped axes.\n- `label` is required and `ariaDescription` is optional.\n- Sizing has one input: `height` (pixels) or `aspectRatio`. Either one sizes the plot and the server-rendered SVG (laid out at 800px wide), and they cannot be combined. With neither, the root class owns the height (320px by default, replaced by a height class on `class`) and SSR emits a stable empty host whose SVG mounts only in the browser.\n- Replace `data`, `marks`, or another configuration prop to update a mounted chart. In-place mutation is not an update contract.\n- Configuration errors throw a prefixed `TypeError`; dependency and accessor errors propagate.\n\n## Motion\n\n- **motion** theme slot: one preset (no variants) whose `duration` / `easing` are the default\n timing of the viewport zoom settle; `viewport.transition` still overrides per chart.\n- Ladder: `<Theme components={{ chart: { motion } }}>` → `setChartTheme({ motion })` →\n `theme.motion`. Reduced motion collapses the duration to 0 and the chart snaps.\n";
68
+ readonly chart: "\n# Chart Component\n\nChart renders layered cartesian, polar, relation, or faceted marks from one typed data array. Consumers import only from `entasis/chart`; TanStack Charts and D3 stay private implementation details of the component, but they must be installed as optional peer dependencies.\n\n## Requires\n\nChart renders through TanStack Charts and D3. Those packages are optional peer dependencies of entasis, so install them alongside it:\n\n`pnpm add @tanstack/charts d3-array d3-force d3-hierarchy d3-sankey d3-scale d3-shape`\n\nOne public type (`curve`) is D3's `CurveFactory`, so TypeScript users add its typings: `pnpm add -D @types/d3-shape`.\n\n## Basic usage\n\n```svelte\n<script lang=\"ts\">\n import { Chart } from '../components/Chart/index.ts'\n\n type Row = { month: Date; actual: number; forecast: number; low: number; high: number }\n const x = { scale: { type: 'utc' }, axis: { label: 'Month' } } as const\n const y = { scale: { type: 'linear' }, grid: true } as const\n const marks = [\n {\n type: 'series',\n x: 'month',\n y: 'forecast',\n interval: { lower: 'low', upper: 'high', fill: 'secondary' },\n analysis: [\n { type: 'reference', statistic: 'median' },\n { type: 'rolling', statistic: 'mean', window: 3 }\n ]\n },\n { type: 'series', x: 'month', y: 'actual', stroke: 'primary', points: true }\n ] as const\n</script>\n\n<Chart\n data={rows}\n {x}\n {y}\n {marks}\n tooltip\n viewport\n label=\"Monthly revenue\"\n height={320}\n/>\n```\n\n## Contract\n\n- `data` is one immutable array shared by every mark.\n- `marks` is a required non-empty discriminated union; array order is paint order.\n- A `series` mark renders a line by default. Set `area`, `points`, or `line` with booleans or local option objects to compose its visible layers.\n- An area is filled with a gradient of the series colour along its value axis. `areaFill: 'solid'` paints one flat fill at `fillOpacity` (20% by default) instead.\n- A series `interval` adds a non-interactive band behind the same line. Its required `lower` and `upper` numeric channels represent explicit bounds such as confidence, prediction, credible, or min/max intervals. `interval` and `area` are mutually exclusive because both own the filled surface.\n- `analysis` is a non-empty list of derived statistical layers owned by a series, scatter, bar, or distribution mark. Reference analysis supports mean, median, quantile, and standard deviation. Series and scatter support linear regression with optional confidence or prediction intervals. Series also supports rolling mean and rolling median. Analysis can use the complete plot or each series independently and does not add tooltip points. Stacked layouts reject analysis because their displayed values differ from the source channels.\n- A `scatter` mark renders independent observations with the default `points` variant. A numeric `size` is a constant pixel radius. A `size` data channel uses `sqrt` by default; `sizeScale` accepts `linear`, `sqrt`, `log`, `exp`, or an object with `type`, `domain`, `range`, and an optional `base` for logarithmic or exponential scales. The `hexbin` variant accepts numeric `x` and `y` channels and aggregates dense observations into responsive pixel-space hexagons; `radius` controls the bin size.\n- A `bar` mark is simple by default. Its optional `variant` is `group` or `stack`. Use `offset: 'normalize'` on a stacked bar to compare proportions with a 0–1 value axis. A stacked bar also accepts `gap` (pixels of surface between consecutive segments of one stack).\n- Wide data: a stacked bar, and an area series with `layout: { type: 'stack' }`, accept a list of numeric fields as their value channel (`y: ['completed', 'inProgress', 'pending']`, or `x` when horizontal). The rows are melted internally into one series per field, the field name becoming the series key, in the order the fields are listed. A wide mark owns its series identity, so it takes no `series` or `colorBy`, and rejects `annotations` and `analysis`. Long format with a `series` channel keeps working unchanged.\n- A `matrix` mark uses the default `grid` variant with categorical `x` and `y` channels. Use `colorBy` for a categorical matrix, or use a numeric `value` with `color` for a heatmap. The `calendar` variant replaces `x` and `y` with a `date` channel and derives week and weekday axes automatically. Its optional `colorScale` accepts a `quantize` domain and an ordered color range.\n- A data mark can own `arrow`, `label`, `rule`, `band`, and `marker` annotations. Targets resolve against the parent mark's final rendered coordinates. A band annotation uses `thickness` for its cross-axis size.\n- Position scales use local string discriminants such as `linear`, `utc`, and `band`.\n- `tooltip: true` groups cartesian marks on the categorical or x axis, renders native color rows, and stays inside the chart surface without adding chart focus states.\n- `palette` is either an ordered list consumed in series-discovery order, or a record keyed by series key (`{ completed: 'success', pending: 'danger' }`). Record entries win by key; a series without an entry falls back to the default palette in discovery order.\n- `ChartColor` accepts the seven semantic roles and the surface family (`surface`, `surface-recessed`, `surface-canvas`, `surface-raised`, `surface-floating`); anything else is passed through as a CSS color.\n- `legend: true` shows a categorical color key, or a numeric heatmap/hexbin color ramp. Use `legend: { interactive: true }` to hide and show series, bars, scatter points, and empirical distributions whose series and color identities match. Other layouts use static legends; facets keep their own labels. Visibility preserves axes, colors, and stack totals. The object accepts `placement: 'top' | 'bottom'`, `label`, controlled `value` and `onValueChange`, or an initial `defaultValue`; omitted visibility shows every series.\n- `legend.format: (key) => string` sets the display text of a series independently of its key. The same formatter labels the series in the tooltip.\n- A categorical legend is drawn by the library, not by the rendering engine: the interactive one is a `ToggleButtonGroup` of small ghost toggles, one per series, each carrying a color swatch of the resolved series color and the `legend.format` label, pressed when the series is visible; the static one is the same swatch and label as plain items. It takes its own row above or below the plot and the plot shrinks by that row. A numeric legend stays a color ramp drawn inside the plot. Style the row with the `legend`, `legendItem`, and `legendSwatch` theme parts.\n- Legend layout accepts `align: 'left' | 'center' | 'right'` and `orientation: 'horizontal' | 'vertical'`. Vertical stacks categorical entries in one column; numeric color ramps stay horizontal. Both static and interactive legends support alignment and top/bottom placement.\n- Histogram and rolling preparation use native transforms. Regression uses native Student-t confidence bounds; prediction intervals add observation uncertainty to the fitted-mean bounds. Small samples therefore have wider intervals than a fixed normal approximation.\n- Tooltip objects can override grouping with `groupBy: 'x'`, `groupBy: 'y'`, or `groupBy: false`.\n- A tooltip can be pinned: `tooltip.value` / `tooltip.defaultValue` take the row key of the pinned datum (the mark's `key` channel when it has one, its x value otherwise; `null` pins nothing). A pinned row shows its tooltip on mount, hover moves the tooltip normally, pointer leave restores the pinned row, and clicking a datum pins it (clicking it again unpins) through `tooltip.onValueChange`. Pinning is disabled while `viewport` owns the press gesture.\n- `viewport: true` enables native x-axis brush zoom with pointer and touch input, an accessible reset control, and a reduced-motion-aware transition. Numeric and date axes select continuous windows; categorical axes snap to values and also expose keyboard handles. The object form accepts `reset` and `transition` options. Native 2D brushing is not supported.\n- A `distribution` mark reads one categorical `group` channel and one numeric `value` channel. Its summary variants are `violin`, `box`, and `error-bar`. Its raw-sample variants are `histogram`, `density`, and `ecdf`. Histogram accepts `bins`; density accepts `bandwidth` and `samples`; `direction` can be `vertical` or `horizontal`.\n- Distribution tooltips use a built-in statistical summary. They do not accept custom `tooltip.fields`.\n- A `proportion` mark reads one categorical `category` channel and one non-negative numeric `value` channel. Its `variant` is `pie`, `donut`, or `waffle`; consumers do not calculate angles or cells.\n- A `polar` mark reads `angle` and `radius` channels. The `circular` and `radar` variants compose boolean `area`, `line`, and `points` layers. The `radial-bar` and `rose` variants render wedges and expose only bar options.\n- A `relation` mark owns a complete topology layout. Its `tree` variant reads `nodeId` and `parent`; its `network` and `sankey` variants read `nodeId` and an outgoing `relations` channel. Relation marks cannot use axes, sibling marks, facets, or custom tooltip fields.\n- A `facet` mark owns nested `marks` with the same public union and inherits the plot positions.\n- Proportion tooltips show the category value and its share. They do not accept custom `tooltip.fields` or grouped axes.\n- `label` is required and `ariaDescription` is optional.\n- Sizing has one input: `height` (pixels) or `aspectRatio`. Either one sizes the plot and the server-rendered SVG (laid out at 800px wide), and they cannot be combined. A legend row sits outside the plot and adds to the chart's height. With neither, the root class owns the height (320px by default, replaced by a height class on `class`) and SSR emits a stable empty host whose SVG mounts only in the browser.\n- Replace `data`, `marks`, or another configuration prop to update a mounted chart. In-place mutation is not an update contract.\n- Configuration errors throw a prefixed `TypeError`; dependency and accessor errors propagate.\n\n## Motion\n\n- **motion** theme slot: one preset (no variants) whose `duration` / `easing` are the default\n timing of the viewport zoom settle; `viewport.transition` still overrides per chart.\n- Ladder: `<Theme components={{ chart: { motion } }}>` → `setChartTheme({ motion })` →\n `theme.motion`. Reduced motion collapses the duration to 0 and the chart snaps.\n";
69
69
  readonly chip: "\n# Chip Component\n\nThe Chip component is a compact element for displaying tags, labels, categories, filters, or positioned indicators. It supports colors, variants, sizes, links, buttons, and optional corner placement.\n\n## Basic Usage\n\n```svelte\n<Chip>Tag</Chip>\n<Chip color=\"primary\">Primary Tag</Chip>\n<Chip variant=\"outline\">Outline Tag</Chip>\n```\n\n## Props\n\n### Core Props\n- **color**: 'primary' | 'secondary' | 'neutral' | 'danger' | 'success' | 'warning' | 'info' (default: 'primary')\n - Determines the color scheme\n\n- **variant**: 'solid' | 'outline' | 'soft' (default: 'solid')\n - solid: Filled background with color\n - outline: Transparent background with colored border\n - soft: Semi-transparent background\n\n- **selected**: boolean (default: false)\n - Sets `data-selected` and paints the shared soft selected fill on top of `variant`\n - Also the chip's accessible state: `aria-pressed` on a chip that resolved to a button, `aria-current` on one that resolved to a link. Never pass either attribute yourself\n - A chip with neither `onclick` nor `href` has no interactive role, so there `selected` is paint-only\n - Use it for chip lists that mark their chosen entries (filters, tag pickers)\n\n- **size**: 'small' | 'normal' | 'large' (default: 'normal')\n - small: Compact size for dense layouts\n - normal: Standard size\n - large: Larger for emphasis\n\n- **position**: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' (optional)\n - Turns the Chip into an absolutely positioned overlay anchored to the selected corner\n - Requires a containing element with a positioning context such as `position: relative`\n\n### Interactive Props\n- **onclick**: (event: MouseEvent) => void - Native click handler (makes chip a button)\n- **onpointerenter**: (event: PointerEvent) => void - Native pointer enter handler\n- **onpointerleave**: (event: PointerEvent) => void - Native pointer leave handler\n\n### Link Props\n- **href**: string - Makes chip render as anchor tag\n- **target**: string - Link target (e.g., \"_blank\")\n- **rel**: string - Link relationship\n\n### Content Slots\n- **children**: Snippet - Main chip content\n- **prefix**: Snippet - Content before main text (typically icons)\n- **suffix**: Snippet - Content after main text (typically close buttons or icons)\n\n### Styling Props\n- **class**: string - Additional CSS classes\n- **theme**: ComponentTheme - Custom theme overrides\n\n## Structure\n\n```\n<Chip>\n\t<Prefix /> <!-- Optional prefix -->\n\t<Children /> <!-- Main content -->\n\t<Suffix /> <!-- Optional suffix -->\n</Chip>\n```\n\n## Examples\n\n### Basic Chips\n```svelte\n<Chip>Default</Chip>\n<Chip variant=\"outline\">Outline</Chip>\n<Chip variant=\"soft\">Soft</Chip>\n```\n\n### Color Variants\n```svelte\n<Chip color=\"primary\">Primary</Chip>\n<Chip color=\"secondary\">Secondary</Chip>\n<Chip color=\"danger\">Danger</Chip>\n<Chip color=\"success\">Success</Chip>\n<Chip color=\"warning\">Warning</Chip>\n<Chip color=\"info\">Info</Chip>\n```\n\n### Different Sizes\n```svelte\n<Chip size=\"small\">Small</Chip>\n<Chip size=\"normal\">Normal</Chip>\n<Chip size=\"large\">Large</Chip>\n```\n\n### Positioned Indicator\n\n```svelte\n<div class=\"relative\">\n\t<Button>Notifications</Button>\n\t<Chip position=\"top-right\" color=\"danger\">3</Chip>\n</div>\n```\n\n### With Icons\n```svelte\n<script lang=\"ts\">\n\timport { Chip } from 'entasis/chip';\n\timport { tagIcon } from 'entasis/icons/tag';\n\timport { xIcon } from 'entasis/icons/x';\n</script>\n\n<Chip>\n\t{#snippet prefix()}\n\t\t{@render tagIcon()}\n\t{/snippet}\n\tTagged\n</Chip>\n\n<Chip>\n\tCategory\n\t{#snippet suffix()}\n\t\t{@render xIcon()}\n\t{/snippet}\n</Chip>\n```\n\n### Interactive Chip (Button)\n```svelte\n<script>\n\tfunction handleClick() {\n\t\tconsole.log('Chip clicked');\n\t}\n</script>\n\n<Chip onclick={handleClick}>\n\tClickable\n</Chip>\n```\n\n### Removable Chip\n```svelte\n<script lang=\"ts\">\n\timport { Chip } from 'entasis/chip';\n\timport { xIcon } from 'entasis/icons/x';\n\n\tlet tags = $state(['React', 'Vue', 'Svelte']);\n\n\tfunction removeTag(tag: string) {\n\t\ttags = tags.filter((t) => t !== tag);\n\t}\n</script>\n\n{#each tags as tag}\n\t<Chip color=\"primary\">\n\t\t{tag}\n\t\t{#snippet suffix()}\n\t\t\t<button onclick={() => removeTag(tag)}>\n\t\t\t\t{@render xIcon({ size: 12 })}\n\t\t\t</button>\n\t\t{/snippet}\n\t</Chip>\n{/each}\n```\n\n### As Link\n```svelte\n<Chip href=\"/tags/svelte\" target=\"_blank\">\n\tSvelte Tag\n</Chip>\n```\n\n### Status Chips\n```svelte\n<Chip color=\"success\" variant=\"soft\">Active</Chip>\n<Chip color=\"warning\" variant=\"soft\">Pending</Chip>\n<Chip color=\"danger\" variant=\"soft\">Inactive</Chip>\n```\n\n### With Custom Styling\n```svelte\n<Chip class=\"lift-3 hover:lift-4 transition-shadow\">\n\tCustom Style\n</Chip>\n```\n\n### Filter Chips\n```svelte\n<script>\n\tlet filters = $state(['All', 'Active', 'Completed', 'Archived']);\n\tlet selected = $state('All');\n</script>\n\n<div class=\"flex gap-2\">\n\t{#each filters as filter}\n\t\t<Chip\n\t\t\tselected={selected === filter}\n\t\t\tvariant={selected === filter ? 'soft' : 'outline'}\n\t\t\tonclick={() => selected = filter}\n\t\t>\n\t\t\t{filter}\n\t\t</Chip>\n\t{/each}\n</div>\n```\n\n### Category Chips\n```svelte\n<div class=\"flex flex-wrap gap-2\">\n\t<Chip color=\"primary\" variant=\"soft\">JavaScript</Chip>\n\t<Chip color=\"secondary\" variant=\"soft\">TypeScript</Chip>\n\t<Chip color=\"info\" variant=\"soft\">CSS</Chip>\n\t<Chip color=\"success\" variant=\"soft\">HTML</Chip>\n</div>\n```\n\n## Rendering Behavior\n\n- Renders as `<button>` when `onclick`, `onpointerenter`, or `onpointerleave` is provided\n- Renders as `<a>` when `href` is provided\n- Renders as `<div>` otherwise\n\n- Becomes absolutely positioned only when `position` is set\n\n## Accessibility\n\n- Automatically sets appropriate ARIA roles\n- Button chips support keyboard interaction\n- Link chips support standard anchor behavior\n- Proper focus states for interactive chips\n\n## Notes\n\n- The chip automatically determines its element type based on props\n- Interactive chips have hover and focus states\n- Suffix is commonly used for close/remove actions\n- Prefix is typically used for icons or status indicators\n- Positioned Chips replace the need for a separate Badge component\n\n## Theme Customization\n\nThe Chip component uses a theme object that can be customized using the `theme` prop or by setting a global theme.\n\n### Theme Structure\n\nThe theme object contains the following parts:\n- **root**: Main chip container styles\n- **prefix**: Prefix icon/content styles\n- **suffix**: Suffix icon/content styles\n\n### Theme Type Definition\n\n```typescript\nimport type { ChipThemeProps } from 'entasis/chip';\n\n// Example theme customization\nconst customTheme: ChipThemeProps = {\n root: {\n base: 'custom-base-classes',\n size: {\n small: 'px-1.5 py-0.5 min-h-4 text-sm gap-1',\n normal: 'px-2 py-0.5 min-h-5 text-base gap-1',\n large: 'px-2.5 py-0.5 min-h-6 text-md gap-1.5'\n },\n color: {\n primary: 'bg-primary text-primary-contrast',\n danger: 'bg-danger text-danger-contrast'\n },\n variant: {\n solid: 'text-color-contrast bg-color',\n outline: 'bg-opacity-0 text-color border-color border',\n soft: 'bg-color-muted text-color'\n\t},\n\tposition: {\n\t\t'top-right': 'absolute top-0 right-0 translate-x-1/2 -translate-y-1/2',\n\t\t'top-left': 'absolute top-0 left-0 -translate-x-1/2 -translate-y-1/2',\n\t\t'bottom-right': 'absolute right-0 bottom-0 translate-x-1/2 translate-y-1/2',\n\t\t'bottom-left': 'absolute bottom-0 left-0 -translate-x-1/2 translate-y-1/2'\n }\n },\n prefix: {\n size: {\n small: 'w-2 h-2',\n normal: 'w-4 h-4',\n large: 'w-5 h-5'\n }\n },\n suffix: {\n size: {\n small: 'w-2 h-2',\n normal: 'w-4 h-4',\n large: 'w-5 h-5'\n }\n }\n};\n```\n\n### Available Variants\n\n**root**:\n- base: Base classes applied to all chips\n- Variants:\n - size: 'small' | 'normal' | 'large' - Controls padding, height, text size, and gap\n - color: 'primary' | 'secondary' | 'neutral' | 'danger' | 'success' | 'warning' | 'info' - Color scheme\n - variant: 'solid' | 'outline' | 'soft' - Visual style variant\n - position: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' - Optional absolute corner placement\n\n**prefix**:\n- base: Base classes for prefix content\n- Variants:\n - size: 'small' | 'normal' | 'large' - Icon size based on chip size\n\n**suffix**:\n- base: Base classes for suffix content\n- Variants:\n - size: 'small' | 'normal' | 'large' - Icon size based on chip size\n\n### Usage Examples\n\n**Basic Theme Override**:\n```svelte\n<Chip \n theme={{\n root: {\n base: 'rounded-full lift-3',\n size: {\n large: 'px-4 py-2 min-h-8'\n }\n }\n }}\n>\n Custom Chip\n</Chip>\n```\n\n**Color and Variant Customization**:\n```svelte\n<Chip \n color=\"danger\"\n variant=\"outline\"\n theme={{\n root: {\n variant: {\n outline: 'border-2 border-red-500 bg-red-50 text-red-700'\n }\n }\n }}\n>\n Danger Chip\n</Chip>\n```\n\n**Global Theme Setting**:\n```svelte\n<script>\n import { setChipTheme } from '../components/Chip/index.ts';\n \n setChipTheme({\n root: {\n base: 'transition-all hover:scale-105',\n variant: {\n solid: 'lift-1 hover:lift-3',\n outline: 'state-layer border-2'\n }\n },\n prefix: {\n size: {\n normal: 'w-5 h-5'\n }\n }\n });\n</script>\n```\n";
70
70
  readonly 'event-calendar': "\n# EventCalendar\n\nA typed scheduling calendar for Svelte 5. It renders month, week, day, configurable N-day, agenda, and resource-day views from one occurrence model. The consumer owns loading and persistence; EventCalendar owns range derivation, recurrence expansion, layout, selection, accessible interaction, validation, clipboard operations, bounded history, and immutable mutation transactions.\n\n## Import\n\n~~~svelte\n<script lang=\"ts\">\n\timport { EventCalendar, type EventCalendarItem } from 'entasis/event-calendar';\n\n\tlet date = $state(new Date('2026-07-15T10:00:00.000Z'));\n\tlet items = $state<EventCalendarItem[]>([]);\n</script>\n\n<EventCalendar bind:date bind:items timeZone=\"Europe/Paris\" class=\"h-[42rem]\" />\n~~~\n\n'date' and 'timeZone' are required. Contained scrolling also requires a definite height through 'class'. The display-zone name must be 'UTC' or an IANA zone; EventCalendar never infers a server/browser system zone.\n\n## Data model\n\n- Every range is half-open: [start, end). Timed boundaries are Date instants.\n- All-day starts and exclusive ends are canonical YYYY-MM-DD strings. Valid civil dates span 0001-01-01 through 9999-12-31, but 9999-12-30 is the last selectable/renderable day because a rendered day needs an exclusive end. An all-day end may be 9999-12-31.\n- Items need stable unique ids. Invalid items, resources, views, zones, recurrence, and stale transactions throw EventCalendarError with a public EventCalendarErrorCode.\n- 'items' and 'resources' are immutable controlled collections: allocate a fresh outer array for every consumer update and replace changed definitions. EventCalendar never mutates consumer objects.\n- EventCalendarItem<TItemFields> includes optional 'description' and 'color' fields and preserves custom fields through props, snippets, callbacks, and API methods. Item colors accept Entasis semantic tokens or CSS colors; omitted item colors render as neutral. Custom fields cannot redeclare calendar-owned keys.\n- Foreground items can be timed, all-day, multi-day, or recurring. 'display: background' items are display-only and never interactive.\n- Assign one leaf with 'resourceId' or several leaves with an ordered unique 'resourceIds' array. The two fields are mutually exclusive.\n- Timed recurrence requires an explicit 'recurrenceTimeZone'. All-day recurrence remains floating civil dates.\n\n## State and view props\n\n- 'items': EventCalendarItem<TItemFields>[] = [] (bindable)\n- 'view': 'month' | 'week' | 'day' | 'days' | 'agenda' | 'resource' = 'month' (bindable)\n- 'views': ordered, unique enabled-view list = all six views. Resource is available only with at least one leaf resource.\n- 'date': Date (required, bindable anchor instant)\n- 'dayCount': positive integer = 3 (bindable; used by 'days')\n- 'selection': EventCalendarSelection = empty (bindable)\n- 'resources': EventCalendarResource<TResourceFields>[] = []\n- 'loading': boolean = false. Marks content busy and blocks content interaction, not navigation.\n- 'disabled': boolean = false. Blocks navigation, selection, and mutation.\n\nThe component never changes 'view' because its container becomes narrow. Previous/next/today, header controls, and the imperative API reassign bindable state and call matching change callbacks. External assignments do not echo the same callback. Navigation beyond the supported civil domain is a no-op; direct invalid anchors and jumps throw.\n\n## Date, locale, and range props\n\n- 'timeZone': required IANA name or 'UTC'\n- 'locale': BCP-47 string; defaults to the active Entasis catalog locale\n- 'i18n': per-instance Partial<Messages>\n- 'dir': 'ltr' | 'rtl'; defaults to ambient direction\n- 'weekStartsOn': 0..6; otherwise locale-derived\n- 'validRange': half-open Date range\n- 'onRangeChange(payload)': receives view, anchor date, time zone, current/render/active/fetch ranges, and exact visibleDays\n\n'fetchRange' is the clipped active rendering/interaction envelope. The consumer must return overlapping non-recurring definitions, recurring sources that expand into the range, moved exception definitions selected by current or reconstructed original occurrence overlap, and every returned exception's source. EventCalendar performs no requests, caching, retries, or error substitution.\n\n## View settings\n\n- 'month': fixedWeeks=true, showOutsideDays=true, showWeekNumbers=false, and maxItemsPerCell=\"auto\".\n- Weekend columns remain controlled by 'showWeekends=true' and 'weekendDays=[0,6]' because the weekday classification is shared across views.\n- 'timeGrid': startHour=0, endHour=24, labelIntervalMinutes=60, slotClickDurationMinutes=30, snapDurationMinutes=15, scrollToHour=7, and nowIndicatorRefreshMs=30000.\n- 'allDayConversion': preserveDuration=false, timedDurationMinutes=60, and allDayDurationDays=1.\n- Agenda: 'agendaDayCount=30'.\n- 'availability': offDays=false, businessHours=[], and constrainMutations=false.\n- 'nowIndicator' renders the built-in current-time line when omitted, accepts a replacement snippet, and disables the line when false.\n- Scrolling/chrome: 'scrollMode=\"contained\"' uses ScrollArea; 'stickyHeader=false' and 'showDatePicker=false'. 'header' and 'itemTooltip' use their built-ins when omitted, accept replacement snippets, and disable their regions when false.\n\nNumeric and time settings are validated and never silently clamped. Hidden weekdays are removed from 'visibleDays'; day and agenda counts count rendered days.\n\n## Interaction and persistence\n\nMove, resize-start, resize-end, API updates, and keyboard mode share one proposal/validation/commit pipeline. Empty-slot drag creation and two-click selection produce a selected range; EventCalendar never fabricates a domain item.\n\n'externalEvent(createItem)' is an attachment for draggable elements outside the calendar. 'createItem' runs once at drag start and should return a structurally valid item with a fresh unique id. A valid drop uses the same placement preview, resource assignment, constraints, resolveItemUpdate callback, immutable add transaction, guarded revert, and history pipeline as internal interaction. The resulting EventCalendarChange has kind 'add' and source 'external-drop'. Invalid or cancelled drops do not change 'items'.\n\n~~~svelte\n<script lang=\"ts\">\n\timport { externalEvent, type EventCalendarItem } from 'entasis/event-calendar';\n\n\tlet sequence = 0;\n\tconst createExternalItem = (): EventCalendarItem => ({\n\t\tid: 'external-' + ++sequence,\n\t\ttitle: 'Focus block',\n\t\tstart: new Date('2026-07-15T08:00:00.000Z'),\n\t\tend: new Date('2026-07-15T09:00:00.000Z')\n\t});\n</script>\n\n<div {@attach externalEvent(createExternalItem)}>Focus block</div>\n~~~\n\nA recurring external source can move inside its original timed or all-day domain. Cross-domain conversion is rejected because timed and all-day recurrence identities have different contracts.\n\n- 'interactions': partial policy. Drag, resize, slot selection, keyboard controls, two-click range selection, and clipboard default on. interactions.createActivation controls drag-create and defaults to distancePx 5, touchDelayMs 300, touchTolerancePx 8.\n- 'allowOverlap': boolean or predicate = true.\n- 'validateItemUpdate(payload)': synchronous live item validation.\n- 'resolveItemUpdate(payload)': accept, reject with false, or adjust placement/resource. Adjustments are fully revalidated.\n- 'validateSlotSelection(slot)': synchronous slot validation.\n- Validation order is structural/editability/range, business hours, overlap, then custom policy.\n\n'onItemsChange({ items, change })' runs after one accepted immutable reassignment. Persist 'items' at the application boundary. On failure, call 'change.revert()' and then surface the original error. Revert is guarded and one-shot: it throws 'stale-transaction' rather than overwrite a newer calendar or consumer update.\n\n'interactions.clipboard=true' enables internal occurrence copy/paste through the API and Mod+C/Mod+V. Paste creates a standalone item, targets a selected compatible slot when present, and never mutates the copied recurrence series. 'historyLimit=50' bounds immutable undo entries; 0 disables history. Mod+Z undoes, Mod+Shift+Z and Mod+Y redo. History refuses stale controlled collections instead of overwriting consumer state.\n\nItem callbacks are 'onItemClick({ occurrence, event })', 'onItemDoubleClick({ occurrence, event })', 'onMoreClick({ day, occurrences, event })', and 'onInteractionBlocked'. Slot callbacks are 'onSlotClick({ slot, event })' and 'onSelect({ slot, info })'; info.source is 'drag-create', 'keyboard', or 'single-pointer'. Bound-state callbacks are 'onViewChange', 'onDateChange', 'onDayCountChange', and 'onSelectionChange'.\n\nSelection-callback naming: 'onSelect({ slot, info })' is the pick event -- the user picked this slot -- and 'onSelectionChange(payload)' is the state change of the selection model, completed by the bindable 'selection' prop and its 'defaultSelection' initial value. Those two names are the only selection callbacks on the component.\n\n## Recurrence\n\n'recurrence.editScope' is 'occurrence' by default, creating or updating a persisted exception with recurringItemId plus immutable originalStart. 'disabled' blocks recurring mutations. 'series' transforms the source and all bound exceptions atomically and is an assertion that the current 'items' collection contains the complete exception set for that editable series. A windowed consumer that cannot guarantee completeness must use 'occurrence' or 'disabled'.\n\nStructured recurrence supports daily, weekly, monthly, and yearly rules with interval, count, inclusive until, weekdays including ordinals, month days, months, week start, exclusions, and additions. Supported raw RRULE strings are accepted with explicit restrictions on series transformations. Expansion is finite and capped; unsupported rules and cap exhaustion throw. 'recurrence.expand' is the synchronous bounded escape hatch, and 'recurrence.getExceptionId' overrides exception identity generation.\n\n## Resources\n\n'resources' is a typed flat collection. 'parentId' creates groups; only leaves become resource-day columns and accept assignment. Unknown parents, cycles, and duplicate IDs throw. A leaf can define local 'businessHours' and 'readOnly'; those constraints feed the same proposal validator used by every view. Foreground/background occurrences without a resolvable leaf appear in the localized Unassigned projection. Multi-assigned events render once for every resolved leaf. Resource moves replace only the dragged projection's assignment. Parent and leaf input order is preserved.\n\n## Snippet composition\n\nSnippets replace content inside component-owned semantic and interactive wrappers:\n\n- 'header': snapshot plus ready-made previous, today, next, title, viewSwitcher, datePicker, and actions snippets; false removes the header\n- 'actions': calendar snapshot and API\n- 'item': occurrence, segment, active view (including agenda), states, defaultContent, markerContent, titleContent, and timeContent\n- 'itemTooltip': customizes the item HoverCard with occurrence, segment, view, defaultAccessibleLabel, and defaultContent; false removes it\n- 'monthCell': day/state/segments/overflow and defaultContent\n- 'dayHeader', 'timeGutter' with defaultContent, and 'allDay'\n- 'overflow' and 'overflowContent'; both expose defaultContent, and the latter preserves the built-in interactive item list when rendered\n- 'agendaDetails'\n- 'resourceHeader'\n- 'nowIndicator', 'dragPreview', 'empty', and 'loadingContent'; replacement state snippets expose defaultContent, and nowIndicator=false disables the line\n\nRender 'defaultContent' or the ready-made header snippets when wrapping the built-ins. Snippet content cannot remove item focusability, labels, selection state, drag/resize wiring, disclosures, or live announcements.\n\nEventCalendar does not own create/edit dialogs. Compose 'onSlotClick'/'onSelect'/'onItemClick' with Entasis Dialog, Form, DateInput, TimeInput, Select, and Switch, then publish a fresh 'items' array.\n\n## Imperative API\n\n'bind:this' exposes EventCalendarApi<TItemFields>:\n\n- navigation: next, previous, today, goTo, setView\n- scrolling/ranges: scrollToTime, getVisibleRange, getActiveRange, getVisibleDays\n- queries: getOccurrence, getOccurrences, getOccurrencesForDay\n- immutable mutations: addItem, updateItem, updateOccurrence, removeItem\n- clipboard/history: copySelection, paste, undo, redo, canUndo, canRedo\n- selection/interaction: select, clearSelection, cancelInteraction\n\nUnknown IDs/keys and invalid operations throw EventCalendarError. API mutations use the same validation and transaction callbacks as pointer and keyboard input.\n\n## Theme and accessibility\n\n'density' defaults to 'normal'; 'theme' accepts EventCalendarThemeProps. Import 'eventCalendarTheme', 'setEventCalendarTheme', and 'useEventCalendarTheme' from 'entasis/event-calendar'. Stable parts cover chrome, month, time grid, items/interactions, agenda, and resources. CSS metrics include --event-calendar-slot-height, --event-calendar-time-gutter-width, --event-calendar-day-min-width, --event-calendar-resource-min-width, --event-calendar-item-min-height, and --event-calendar-sticky-offset.\n\nThe active view exposes grids/groups/buttons/disclosures, roving focus, keyboard move/resize, Escape cancellation, two-click range selection, 24px interaction targets, polite announcements, reduced-motion behavior, narrow-container wrapping/scrolling, and RTL-aware physical movement. Keyboard mutation starts from a focused item, uses arrows to propose, Enter to commit, and Escape to cancel.\n";
71
71
  readonly 'gantt-chart': "\n# GanttChart\n\nGanttChart is Entasis's typed project-scheduling surface for Svelte 5. It combines one hierarchical, virtualized tree grid with a synchronized, horizontally windowed time pane. It is a controlled component: task, dependency, and assignment definitions remain application-owned, while resolved WBS, summary spans/progress, durations, slack, critical state, constraint violations, and workload are exposed separately.\n\n## Import\n\n~~~svelte\n<script lang=\"ts\">\n\timport {\n\t\tGanttChart,\n\t\ttype GanttDependency,\n\t\ttype GanttDependencyCreationRequest,\n\t\ttype GanttTask\n\t} from 'entasis/gantt-chart';\n\n\tlet tasks = $state<GanttTask[]>([\n\t\t{ id: 'plan', title: 'Plan', type: 'summary' },\n\t\t{\n\t\t\tid: 'design',\n\t\t\tparentId: 'plan',\n\t\t\ttitle: 'Design',\n\t\t\tstart: new Date('2026-07-27T07:00:00.000Z'),\n\t\t\tend: new Date('2026-07-30T15:00:00.000Z'),\n\t\t\tprogress: 0.4\n\t\t}\n\t]);\n\tlet dependencies = $state<GanttDependency[]>([]);\n\tlet nextDependencyId = 0;\n\n\tfunction createDependency(request: GanttDependencyCreationRequest): GanttDependency {\n\t\tnextDependencyId += 1;\n\t\treturn {\n\t\t\tid: `dependency-${nextDependencyId}`,\n\t\t\tfromTaskId: request.fromTaskId,\n\t\t\ttoTaskId: request.toTaskId,\n\t\t\ttype: request.type\n\t\t};\n\t}\n</script>\n\n<GanttChart\n\tbind:tasks\n\tbind:dependencies\n\ttimeZone=\"Europe/Paris\"\n\tinteractions={{ dependencyCreation: { create: createDependency } }}\n\tclass=\"h-[36rem]\"\n/>\n~~~\n\nThe required timeZone must be UTC or an explicit IANA zone. Contained scrolling needs a definite height. GanttChart never infers a browser or server zone. Locale and SSR reading direction come from ambient Entasis I18n; without that provider, client rendering reconciles the root's computed DOM direction after mount. i18n remains available for per-instance message overrides.\n\n## Domain and controlled state\n\n- GanttTask<TTaskFields> preserves consumer fields while reserving owned keys. A normal task is scheduled with start < end or unscheduled with both endpoints absent. A milestone requires equal endpoints. A summary forbids consumer dates and derives its visible descendant span and weighted progress. Optional segments are ordered, non-overlapping intervals inside one logical task span.\n- GanttDependency<TDependencyFields> supports finish-start, start-start, finish-finish, and start-finish links plus signed minute/hour/day/week lag. Duplicate ids, missing endpoints, self-links, duplicate semantic links, and cycles throw GanttChartError.\n- GanttResource<TResourceFields>, GanttAssignment<TAssignmentFields>, and GanttCalendar are explicit typed inputs. Assignment units are 0 through 1. Calendars use IANA zones, weekday intervals, and civil-date exceptions.\n- tasks, dependencies, assignments, expandedTaskIds, selection, and zoom are bindable. resources and calendars are immutable inputs. Every accepted mutation publishes a fresh outer array and fresh changed objects.\n- Selection-callback naming: events.onSelect(payload) is the pick event -- the range a user picked on an empty row -- and events.onSelectionChange(payload) is the state change of the selection model, completed by the bindable selection prop and its defaultSelection initial value.\n- Selection is empty, a task, a dependency, or a tree cell. Query resolved nodes for WBS, derived summary values, elapsed/working duration, earliest/latest dates, total/free slack, critical state, and violations; those values never mutate definitions.\n\nConsumer field generics appear in props, layout columns, render snippets, proposals, mutation policies, events, change records, resolved nodes, and API queries/mutations. A custom field cannot shadow an owned key, and index-signature custom objects are rejected by the type surface.\n\n## Scheduling\n\nPass calendars with schedule.calendarId to use working-time arithmetic; task calendarId overrides the project calendar. schedule.propagation is manual, move-successors, or auto. Auto propagation forwards accepted changes through successors while preserving working duration. move-successors carries successors during a task move without enabling the scheduling engine. Constraints support as-soon-as-possible, start-no-earlier-than, start-no-later-than, finish-no-earlier-than, finish-no-later-than, must-start-on, and must-finish-on.\n\nA leaf with progress === 1 remains in dependency and critical-path analysis and its actual dates constrain successors, but forward scheduling never moves it. Conflicts are exposed as typed violations. Resource calendars affect diagnostic workload only; GanttChart never performs automatic resource leveling.\n\nUse timeline.display to toggle criticalPath, baselines, deadlines, constraints, nonWorkingTime, and workload. Resolved analysis and getScheduleAnalysis expose critical task/link ids and violations. getWorkload returns capacity-aware buckets for all resources or an explicit range.\n\n## Layout and presentation\n\n- Built-in zoom levels are hour, day, week, month, quarter, and year. timeline.scales is one ordered list of enabled built-in ids and typed custom scale definitions.\n- timeline owns today/weekend/holiday presentation and optional snapDuration. Move, resize, and range snapping otherwise follows the active scale: 1 minute at hour, 15 minutes at day, 1 hour at week, 4 hours at month, 1 day at quarter, and 3 days at year. The initial view anchors to scheduled project content; use the API for exceptional navigation.\n- schedule.validRange owns the half-open project boundary. layout owns rowHeight, contained/page scroll mode, and the optional tree grid. gridWidth remains bindable, while safe splitter bounds, virtualization overscan, and sticky behavior are internal. GanttChart always uses the Entasis scrollbar owner.\n- size changes typography, header controls, and task geometry. density changes row heights, padding, gaps, and indentation. Both are independent of zoom and of each other. The default WBS, title, start, end, duration, progress, and resources columns live under layout.grid.columns and accept typed visibility, size, alignment, sort, filter, value, compare, and edit behavior. Custom editable columns provide applyEdit.\n- Tasks render leaf/summary/milestone shapes, progress and expected progress, segments, baseline, deadline, labels, continuations, tooltips, non-working shade, project/today lines, constraints, critical state, and SVG dependency connectors.\n- timeline.resourceView filters or groups rows by resource and sets the compact workload height. render.resourceAssignments and render.workloadCell receive typed custom fields and over-allocation state.\n\n## Interactions and immutable transactions\n\nThe interactions policy independently enables task move, start/end resize, progress resize, row reorder, indent/outdent, empty-range creation, keyboard, and touch. dependencyCreation, clipboard, and history each accept false or their required configuration. Touch activation thresholds are internal. loading blocks content mutation but preserves safe navigation; disabled blocks both.\n\nEvery UI or API mutation constructs a proposal, validates it, calls mutations.task/dependency/assignment.validate, calls the corresponding synchronous resolve hook, revalidates any adjusted record, then publishes immutable collections. The `onTasksChange`, `onDependenciesChange`, and `onAssignmentsChange` callbacks each receive one transaction payload with prior/current collections, affected ids, source/kind, and a guarded one-shot revert. Do not swallow persistence errors: call change.revert() only if desired, then handle or rethrow the error at the application boundary.\n\nPointer-created links require interactions.dependencyCreation.create(request), because GanttChart cannot fabricate ids or required custom fields. Clipboard paste can use interactions.clipboard.getId(request). It copies one selected standalone task subtree, remaps internal dependencies and assignments, and explicitly omits external dependency links.\n\n## Keyboard and accessibility\n\nTree cells, task controls, and dependency controls use roving focus across virtual rows. Arrow keys navigate hierarchy and chronology; RTL mirrors physical movement without reversing chronological intent. M, S, E, and P enter task move, start-resize, end-resize, and progress modes. D or Shift+D starts dependency creation; R starts empty-range creation. Enter commits and Escape cancels. Alt+Shift+Arrow keys reorder or change hierarchy. Delete/Backspace requests deletion. Mod+C/Mod+V copy and paste; Mod+Z, Mod+Shift+Z, and Mod+Y use bounded stale-safe history.\n\nTreegrid/timeline roles, expanded/selected/grabbed state, focus restoration, 24px handle hit targets, high contrast, reduced motion, touch long-press/tolerance, and live announcements remain component-owned. Snippets cannot replace these semantic or interaction owners.\n\nThe task grid is a `treegrid` whose direct children are two `rowgroup`s — the sticky header band and the scrolling body — so every `row` stays inside a valid parent. A `gridHeader` snippet renders decoratively inside the header row and must not introduce roles of its own.\n\n## Render snippets\n\nThe render object provides typed Svelte 5 snippets for header, actions, gridHeader, columnHeader, treeCell, taskRow, timeHeader, task, taskLabel, taskTooltip, dependencyTooltip, progress, baseline, deadline, nonWorkingTime, resourceAssignments, workloadCell, dragPreview, empty, and loadingContent. The task payload identifies leaf, summary, and milestone nodes. The timeHeader payload identifies its upper or lower level. Payloads include ready-made defaultContent where meaningful. Header payloads expose owned today, fit-project, zoom, and action snippets; render.header can be false.\n\n## Imperative API\n\nBind the component instance as GanttChartApi. Real methods include:\n\n- Navigation: fitProject, zoomIn, zoomOut, setZoom, scrollToDate, scrollToTask, getVisibleRange.\n- Queries: getTask, getResolvedTask, getVisibleTasks, getDependency, getAssignment, getResources, getScheduleAnalysis, getWorkload.\n- Hierarchy/selection: expandTask, collapseTask, toggleTask, expandAll, collapseAll, select, clearSelection.\n- Mutations: add/update/remove task, dependency, and assignment.\n- Workflow: copySelection, paste, undo, redo, canUndo, canRedo, cancelInteraction.\n\n## Application-owned editors and non-goals\n\nGanttChart does not own task creation/edit dialogs. Compose events.onTaskDoubleClick or events.onSelect with Entasis Dialog and Form controls, validate the definition, then publish a fresh controlled array or call the API. Network fetching, persistence, retries, collaboration, recurrence, automatic resource leveling, proprietary import/export, and deployment are outside this package.\n";
@@ -77,7 +77,7 @@ export declare const componentMcpRegistry: {
77
77
  readonly 'sortable-list': "\n# SortableList Component\n\nA vertical list whose items are reordered by drag and drop. Its default live preview places the dragged row at the prospective slot while neighboring rows move with FLIP; `indicator` keeps rows fixed and draws an insertion line instead. Built on the `useDndList` attachment utility (Pragmatic drag and drop), the same engine as Kanban. Generic over the item type `T`.\n\n## Basic Usage\n\n```svelte\n<script lang=\"ts\">\n\timport { SortableList } from 'entasis/sortable-list';\n\tlet items = $state([\n\t\t{ id: '1', label: 'Write the brief' },\n\t\t{ id: '2', label: 'Design the mockups' },\n\t\t{ id: '3', label: 'Ship it' }\n\t]);\n</script>\n\n<SortableList bind:items />\n```\n\nItems need a stable, unique `id` property (primitive items are matched by value).\n\n## Props\n\n### Core Props\n- **items**: `T[]` (required, bindable)\n - The list items in display order. The bound array is reassigned once, at drop — the mid-drag motion is a render-only preview.\n- **handle**: `boolean | Snippet<[{ item, index, isDragging }]>` (default: `false`)\n - Drag mode and handle content in one prop.\n - `false`: the whole row initiates the drag (grab cursor).\n - `true`: only a grip handle drags. A default grip icon renders.\n - snippet/string: turns on handle mode and customizes what renders inside the handle.\n\n### Style Props\n- **size**: `'small' | 'normal' | 'large'` (default: `'normal'`)\n - Row padding, gaps and typography.\n- **disabled**: `boolean` (default: `false`)\n - Disables all dragging. Rows still render but cannot be reordered.\n- **indicator**: `boolean` (default: `false`)\n - Keeps rows in place while dragging and draws the shared insertion line. When false, the dragged row renders as a dimmed live placeholder and neighboring rows part around it.\n- **orientation**: `'vertical' | 'horizontal' | 'grid'` (default: `'vertical'`)\n - Layout and drag axis. `horizontal` lays rows in a line, `grid` wraps them; both resolve before/after on the horizontal axis (RTL-aware), and dragging across wrapped grid lines follows the logical order. For a real CSS grid, override the root theme part — the drag math is identical.\n\n### Cross-list Props\n- **group**: `string` (optional) - Lists sharing the same group accept each other's rows: dragging into another list of the group inserts the row there, with the live preview showing the exact slot. Both bound `items` arrays update automatically at drop. Lists in a group should share the item shape. Captured at mount.\n- **name**: `string` (optional) - Identifies this list within its group; reported as `from`/`to` in the cross-list callbacks. Defaults to an internal unique id.\n- **accepts**: `({ item, from }) => boolean` (optional) - Cross-list accept policy on top of the group match; return false to reject (no preview, drop ignored). `from` is the source list's name. Enables receive-only or type-gated lists.\n\n```svelte\n<SortableList bind:items={today} group=\"planner\" name=\"today\" />\n<SortableList bind:items={tomorrow} group=\"planner\" name=\"tomorrow\" />\n```\n\n### Event Props\n- **onReorder**: `({ items, from, to, item }: SortableListReorderPayload<T>) => void`\n - Fired once when a drag ends and the order actually changed, with the reordered array and the move details. A cancelled drag or a no-op drop does not fire this.\n- **onReceive**: `(payload: { item: T; index: number; from: { list: string; index: number } }) => void` - A row arrived from another list of the group (`items` already updated). `from.index` is the row's index in the source list at drag start — enough to persist the full move.\n- **onRemove**: `(payload: { item: T; index: number; to: { list: string } }) => void` - One of this list's rows left for another list of the group (`items` already updated); `index` is its index here at the moment of drop.\n- **onDragStart**: `(payload: { item: T; index: number }) => void` - A drag of one of this list's rows started.\n- **onDragEnd**: `(payload: { item: T; dropped: boolean }) => void` - The drag ended (drop or cancel), after state updates; `dropped` is true when it landed on an accepting list.\n\n### Localization\n- **i18n**: `Partial<Messages>` - Per-instance i18n overrides merged over the global catalog. Supplies the default handle's aria-label (`dragToReorder`).\n\n### Content Props (Slots)\n- **item**: `Snippet<[{ item: T, index: number, isDragging: boolean }]>` (optional)\n - Row content. When omitted, each row renders a plain label: `String(item)` for primitives, otherwise `item.label ?? item.title ?? item.id`. `index` is the rendered position; `isDragging` identifies the dragged row (the dimmed placeholder in the default preview mode).\n\n```svelte\n<SortableList bind:items>\n\t{#snippet item({ item, isDragging })}\n\t\t<div class=\"flex flex-col\">\n\t\t\t<span class=\"font-medium\">{item.title}</span>\n\t\t\t<span class=\"text-neutral/70 text-sm\">{item.description}</span>\n\t\t</div>\n\t{/snippet}\n</SortableList>\n```\n\n- **empty**: `Slot` (snippet or string, optional) - Rendered inside the list when it has no rows. Grouped lists should provide it: it gives an empty list a visible, hittable drop area (dashed min-h row via the `empty` theme part). Nothing renders by default.\n\n- **handle** (via the `handle` prop as a snippet): `Snippet<[{ item, index, isDragging }]>`\n - Customizes what renders inside the grip handle. The component owns the handle wrapper (with the `data-dnd-handle` marker, aria-label and grab cursor); the snippet only fills its contents.\n\n```svelte\n<script lang=\"ts\">\n\timport { dotsSixVerticalIcon } from 'entasis/icons/dotsSixVertical';\n</script>\n\n<SortableList bind:items>\n\t{#snippet handle()}\n\t\t{@render dotsSixVerticalIcon()}\n\t{/snippet}\n</SortableList>\n```\n\n### Advanced Props\n- **ref**: `HTMLElement | null` (bindable) - Reference to the root `<ul>` element.\n- **class**: `string` - Class for the root `<ul>`.\n- **theme**: `SortableListThemeProps` - Theme overrides.\n\n## Structure\n\nA `<ul>` (`root`) holding one `<li>` (`item`) per entry. In full-row mode the `<li>` is the drag activator. In handle mode the `<li>` also contains a grip `<button>` (`handle`, marked `data-dnd-handle`) that is the only drag activator, followed by the `content` region. By default the dragged row renders dimmed at its prospective slot (the `dragging` theme variant) and the others flip around it; `indicator` keeps the DOM order stable until drop.\n\n## Accessibility\n\n- Renders a real `<ul>` / `<li>` list; the default grip handle is a real `<button>` with a localized `aria-label`.\n- Drag and drop is pointer-driven (native HTML5 drag); there is no keyboard sorting. Offer explicit move buttons or an alternate flow where keyboard reordering is a requirement.\n\n## Notes\n- State changes and `onReorder` fire once, at drop. Both feedback modes resolve from the same hover state as the final drop.\n- If the list sits in a scroll container (or is one), dragging near its edges auto-scrolls it.\n- Animations respect `prefers-reduced-motion`.\n- Ids must be stable and unique; using an array index as the id breaks reordering.\n- Theme parts: `root` (variants: `size`, `orientation`), `item` (variants: `size`, `handle`, `dragging`, `disabled`), `content`, `handle` (variant: `size`), `empty`.\n";
78
78
  readonly stat: "\n# Stat Component\n\nStat renders a compact metric card. Every region — label, value (with an optional unit), indicator, separator, trend, description — is a named snippet, and the `order` prop decides which of them render and in what sequence. A decorative indicator and an interactive action share the top-right column.\n\n## Basic Usage\n\n```svelte\n<Stat label=\"Revenue\" value=\"$45,231\" trend=\"+20.1%\" trendDirection=\"up\">\n\t{#snippet indicator()}\n\t\t{@render trendUpIcon()}\n\t{/snippet}\n</Stat>\n```\n\n## Region Order\n\n`order` lists the regions to render. A region left out of the list is not rendered at all, so the separator is opt-in: add `'separator'` where the hairline belongs.\n\n```svelte\n<!-- default: ['label', 'value', 'indicator', 'trend', 'description'] -->\n<Stat label=\"Tasks\" value=\"147\" trend=\"+12%\" trendDirection=\"up\" description=\"This sprint\" />\n\n<!-- description above the hairline, trend last -->\n<Stat\n\torder={['label', 'value', 'description', 'separator', 'trend']}\n\tlabel=\"Tasks\"\n\tvalue=\"147\"\n\tdescription=\"Across every project\"\n\ttrend=\"8 completed today\"\n\ttrendDirection=\"up\"\n/>\n```\n\n`indicator` always renders in column 2 whatever its position in `order`; the first two listed regions sit beside it, the rest span the full card width.\n\n## Unit\n\n`unit` renders inline after `value`, one type step smaller and baseline-aligned.\n\n```svelte\n<Stat label=\"Tasks\" value=\"147\" unit=\"task\" />\n```\n\n## Trend\n\n`trendIcon` is a leading, always-neutral slot. `trend` is the text, and the component appends its own direction arrow from `trendDirection` ('up' → up-right, 'down' → down-right, 'neutral' → none). Only the text and the arrow take the trend colour.\n\n```svelte\n<Stat label=\"Throughput\" value=\"147\" trend=\"12 more than last week\" trendDirection=\"up\">\n\t{#snippet trendIcon()}\n\t\t{@render lightningIcon()}\n\t{/snippet}\n</Stat>\n```\n\n## Action\n\n`action` renders a ghost icon button in the top-right corner. `actionLabel` is the accessible name and is required whenever the action is icon-only. When an indicator is present too, the action sits above it in the same column.\n\n```svelte\n<Stat\n\tlabel=\"Tasks\"\n\tvalue=\"147\"\n\tunit=\"task\"\n\tindicatorVariant=\"badge\"\n\tindicatorColor=\"success\"\n\tindicator=\"Live\"\n\tactionLabel=\"More actions for Tasks\"\n\tonAction={() => openMenu()}\n>\n\t{#snippet action()}\n\t\t{@render dotsThreeVerticalIcon()}\n\t{/snippet}\n</Stat>\n```\n\n## Sizes and Density\n\n```svelte\n<!-- size scales the typography and icons -->\n<Stat size=\"small\" label=\"Small type\" value=\"1,204\" />\n<Stat size=\"normal\" label=\"Normal type (default)\" value=\"1,204\" />\n<Stat size=\"large\" label=\"Large type\" value=\"1,204\" />\n\n<!-- density scales the padding and gaps -->\n<Stat density=\"compact\" label=\"Dense dashboard stat\" value=\"1,204\" />\n<Stat density=\"normal\" label=\"Everyday stat (default)\" value=\"1,204\" />\n<Stat density=\"comfortable\" label=\"Roomy detail stat\" value=\"1,204\" />\n```\n\n## Props\n\n### Core Props\n- **ref**: HTMLElement | null - Bindable reference to the root element.\n- **class**: string - Additional classes for the root element.\n- **color**: Colors (default: 'neutral') - Semantic color token for the stat surface.\n- **variant**: 'solid' | 'outline' | 'soft' | 'ghost' (default: 'solid') - Surface treatment.\n- **size**: 'small' | 'normal' | 'large' (default: 'normal') - Scales typography and icons only. Combine with density to control spacing independently.\n- **density**: 'compact' | 'normal' | 'comfortable' (default: 'normal') - Controls the surface padding and gaps between regions.\n- **order**: readonly StatPart[] (default: ['label', 'value', 'indicator', 'trend', 'description']) - Regions to render, in order. StatPart is 'label' | 'value' | 'indicator' | 'separator' | 'trend' | 'description'.\n- **theme**: StatThemeProps - Theme overrides for root and stat parts.\n\n### Content Props\n- **label**: Slot - Label content.\n- **value**: Slot - Primary metric content.\n- **unit**: Slot - Unit rendered inline after the value at a reduced size.\n- **indicator**: Slot - Decorative indicator rendered in the top-right column.\n- **action**: Slot - Icon button rendered above the indicator in the top-right column.\n- **trendIcon**: Slot - Leading trend icon, always neutral ink.\n- **trend**: Slot - Trend text; the direction arrow is appended by the component.\n- **description**: Slot - Supporting description content.\n- **children**: Slot - Additional custom content rendered after the named regions.\n\n### Indicator Props\n- **indicatorVariant**: 'default' | 'icon' | 'badge' (default: 'default') - Indicator presentation. The indicator is purely decorative.\n- **indicatorColor**: Colors (default: 'neutral') - Semantic color for the indicator.\n\n### Action Props\n- **onAction**: (event: MouseEvent) => void - Click handler for the action button.\n- **actionLabel**: string - Accessible name for the action button; required when it is icon-only.\n- **actionDisabled**: boolean - Disabled state for the action button.\n\n### Trend Props\n- **trendDirection**: 'up' | 'down' | 'neutral' (default: 'neutral') - Tone for the trend text and the appended arrow.\n\n## Structure\n\n```\n<Stat>\n\t<!-- flow column, in the sequence given by order -->\n\t<label slot />\n\t<value slot><unit slot /></value>\n\t<separator />\n\t<trend slot><trendIcon slot /><text + direction arrow /></trend>\n\t<description slot />\n\t<!-- column 2, top-aligned -->\n\t<aside>\n\t\t<action slot />\n\t\t<indicator slot />\n\t</aside>\n\t<children slot />\n</Stat>\n```\n\n## Theme Parts\n\n`root`, `region` (column placement for the flow regions), `label`, `value`, `unit`, `aside`, `action`, `indicator`, `trend`, `trendIcon`, `trendText`, `description`, `separator`.\n\n## Accessibility\n\n- The root is a non-interactive `div`; use surrounding landmarks/headings to provide page structure.\n- `action` is the only interactive region and renders a native `button`; give it `actionLabel` when it holds an icon only.\n- The indicator is decorative and never focusable.\n- The separator is decorative.\n\n## Notes\n\n- `Stat` is the only public component; compose through props and named snippets.\n- Trend colors use Entasis semantic tokens: success for up, danger for down, muted current color for neutral.\n- Use string props for compact markup and named snippets when a region needs icon or richer content.\n";
79
79
  readonly table: "\n# Table Component\n\nThe Table component provides a flexible way to display tabular data with support for headers, footers, and customizable rows. It's designed with a configuration-over-markup approach, allowing you to define table structure through props.\n\n## Basic Usage\n\n```svelte\n<script>\n\timport { Table } from 'entasis/table';\n\t\n\t// Header can use strings directly (simplified syntax)\n\tconst header = {\n\t\tname: 'Name',\n\t\temail: 'Email',\n\t\trole: 'Role'\n\t};\n\t\n\t// Rows can also use strings directly\n\tconst rows = [\n\t\t{\n\t\t\tcells: {\n\t\t\t\tname: 'John Doe',\n\t\t\t\temail: 'john@example.com',\n\t\t\t\trole: 'Admin'\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\tcells: {\n\t\t\t\tname: 'Jane Smith',\n\t\t\t\temail: 'jane@example.com',\n\t\t\t\trole: 'User'\n\t\t\t}\n\t\t}\n\t];\n</script>\n\n<Table {header} items={rows} />\n```\n\n### Alternative Syntax\n\nYou can also use the full object syntax when you need additional properties:\n\n```svelte\n<script>\n\timport { Table } from 'entasis/table';\n\t\n\t// Full object syntax for cells with classes or spans\n\tconst header = {\n\t\tname: { content: 'Name', class: 'w-32' },\n\t\temail: { content: 'Email' },\n\t\trole: { content: 'Role' }\n\t};\n\t\n\tconst rows = [\n\t\t{\n\t\t\tcells: {\n\t\t\t\tname: { content: 'John Doe' },\n\t\t\t\temail: { content: 'john@example.com' },\n\t\t\t\trole: { content: 'Admin', class: 'text-success' }\n\t\t\t}\n\t\t}\n\t];\n</script>\n\n<Table {header} items={rows} />\n```\n\n## Props\n\n### Core Props\n- **items**: TableRow[] (required) - Array of rows to display in the table body\n - Each row can have either `cells` (object keyed by column name) or `content` (Slot) for flexible rendering\n - Cells in rows are rendered in the same order as the header keys (if header is provided)\n- **header**: Record<string, TableCellValue> (optional) - Object of cells for the table header, keyed by column name\n - Each cell can be a `TableCell` object, a string, or a Snippet\n- **footer**: Record<string, TableCellValue> (optional) - Object of cells for the table footer, keyed by column name\n - Each cell can be a `TableCell` object, a string, or a Snippet\n\n### Content Props (Slots)\n- **prefix**: Slot (optional) - Content rendered above the table\n - Useful for search/filter controls that will be added later\n- **suffix**: Slot (optional) - Content rendered below the table\n - Useful for pagination controls that will be added later\n- **caption**: Slot (optional) - Table caption rendered as a <caption> element\n\n### Styling Props\n- **density**: 'compact' | 'normal' | 'comfortable' (optional, default: 'normal') - Spacing density controlling cell paddings and row heights only\n - 'small' for dense data grids, 'normal' is the everyday scale, 'large' for roomy detail surfaces\n - Cascades from the root to header cells, body cells, rows, and caption; the wrapper exposes it as a `data-density` attribute\n- **class**: string (optional) - Additional CSS classes applied to the table wrapper\n- **theme**: TableThemeProps (optional) - Theme overrides for custom styling\n\n## TableCell Type\n\n`TableCell` represents a single cell in header, footer, or row:\n\n```typescript\ntype TableCell = {\n\tcontent?: Slot; // String or Snippet\n\tclass?: string; // Optional CSS classes\n\trowSpan?: number; // Number of rows the cell spans\n\tcolSpan?: number; // Number of columns the cell spans\n};\n```\n\n## TableCellValue Type\n\n`TableCellValue` is a union type that allows flexible cell definitions:\n\n```typescript\ntype TableCellValue = TableCell | Slot;\n```\n\nThis means you can define cells in three ways:\n1. **Full object**: `{ content: 'Name', class: 'w-32' }` - When you need classes or spans\n2. **String**: `'Name'` - Simplified syntax for simple text content\n3. **Snippet**: `mySnippet` - Direct snippet for custom rendering\n\n## TableRow Type\n\n`TableRow` represents a row in the table body. It supports two rendering modes:\n\n```typescript\ntype TableRow = {\n\tcells?: Record<string, TableCellValue>; // Object of cells keyed by column name (structured mode)\n\tcontent?: Slot; // Direct content slot (flexible mode)\n\tclass?: string; // Optional CSS classes for the row\n\tselected?: boolean; // Drives the row theme's selected variant\n};\n```\n\n**Note**: \n- If both `cells` and `content` are provided, `content` takes precedence.\n- When `header` is provided, cells in rows are rendered in the same order as the header keys.\n- If no header is provided, cells are rendered in the order of their keys.\n- Cells can be defined as strings, snippets, or full TableCell objects for maximum flexibility.\n\n## Structure\n\n```\n<div data-slot=\"table-wrapper\" data-density=\"compact | normal | comfortable\">\n\t{#if prefix}\n\t\t<div data-slot=\"table-prefix\">\n\t\t\t<Prefix />\n\t\t</div>\n\t{/if}\n\t\n\t<div data-slot=\"table-container\">\n\t\t<table data-slot=\"table\">\n\t\t\t{#if caption}\n\t\t\t\t<caption data-slot=\"table-caption\">\n\t\t\t\t\t<Caption />\n\t\t\t\t</caption>\n\t\t\t{/if}\n\t\t\t\n\t\t\t{#if header}\n\t\t\t\t<thead data-slot=\"table-header\">\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<th>...</th>\n\t\t\t\t\t</tr>\n\t\t\t\t</thead>\n\t\t\t{/if}\n\t\t\t\n\t\t\t<tbody data-slot=\"table-body\">\n\t\t\t\t<tr>\n\t\t\t\t\t<td>...</td>\n\t\t\t\t</tr>\n\t\t\t</tbody>\n\t\t\t\n\t\t\t{#if footer}\n\t\t\t\t<tfoot data-slot=\"table-footer\">\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<td>...</td>\n\t\t\t\t\t</tr>\n\t\t\t\t</tfoot>\n\t\t\t{/if}\n\t\t</table>\n\t</div>\n\t\n\t{#if suffix}\n\t\t<div data-slot=\"table-suffix\">\n\t\t\t<Suffix />\n\t\t</div>\n\t{/if}\n</div>\n```\n\n## Examples\n\n### Basic Table with Header and Rows\n\n```svelte\n<script>\n\timport { Table } from 'entasis/table';\n\t\n\t// Simplified syntax with strings\n\tconst header = {\n\t\tproduct: 'Product',\n\t\tprice: 'Price',\n\t\tstock: 'Stock'\n\t};\n\t\n\tconst rows = [\n\t\t{\n\t\t\tcells: {\n\t\t\t\tproduct: 'Laptop',\n\t\t\t\tprice: '$999',\n\t\t\t\tstock: '15'\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\tcells: {\n\t\t\t\tproduct: 'Mouse',\n\t\t\t\tprice: '$25',\n\t\t\t\tstock: '42'\n\t\t\t}\n\t\t}\n\t];\n</script>\n\n<Table {header} items={rows} />\n```\n\n### Table with Mixed Cell Formats\n\n```svelte\n<script>\n\timport { Table } from 'entasis/table';\n</script>\n\n{#snippet customStatus()}\n\t<span class=\"text-success\">✓ Active</span>\n{/snippet}\n\n<Table\n\theader={{\n\t\tname: 'Name',\n\t\tstatus: { content: customStatus },\n\t\tdate: { content: 'Date', class: 'w-32' }\n\t}}\n\titems={[\n\t\t{ cells: { name: 'John Doe', status: customStatus, date: '2024-01-15' } }\n\t]}\n/>\n```\n\n### Table with Footer\n\n```svelte\n<script>\n\timport { Table } from 'entasis/table';\n\t\n\tconst header = {\n\t\titem: { content: 'Item' },\n\t\tquantity: { content: 'Quantity' },\n\t\ttotal: { content: 'Total' }\n\t};\n\t\n\tconst rows = [\n\t\t{\n\t\t\tcells: {\n\t\t\t\titem: { content: 'Product A' },\n\t\t\t\tquantity: { content: '2' },\n\t\t\t\ttotal: { content: '$200' }\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\tcells: {\n\t\t\t\titem: { content: 'Product B' },\n\t\t\t\tquantity: { content: '1' },\n\t\t\t\ttotal: { content: '$150' }\n\t\t\t}\n\t\t}\n\t];\n\t\n\tconst footer = {\n\t\titem: { content: 'Total' },\n\t\tquantity: { content: '3' },\n\t\ttotal: { content: '$350' }\n\t};\n</script>\n\n<Table {header} items={rows} {footer} />\n```\n\n### Table with Custom Row Content (Slot)\n\n```svelte\n<script>\n\timport { Table } from 'entasis/table';\n</script>\n\n{#snippet customRow()}\n\t<td>John Doe</td>\n\t<td>\n\t\t<button>Edit</button>\n\t\t<button>Delete</button>\n\t</td>\n{/snippet}\n\n<Table\n\theader={{ name: 'Name', actions: 'Actions' }}\n\titems={[{ content: customRow }]}\n/>\n```\n\n### Table with Prefix and Suffix (Future Features)\n\n```svelte\n<script>\n\timport { Table } from 'entasis/table';\n\timport { TextInput } from 'entasis/text-input';\n\timport { Button } from 'entasis/button';\n\t\n\tconst header = {\n\t\tname: { content: 'Name' },\n\t\temail: { content: 'Email' }\n\t};\n\t\n\tconst rows = [\n\t\t{\n\t\t\tcells: {\n\t\t\t\tname: { content: 'John Doe' },\n\t\t\t\temail: { content: 'john@example.com' }\n\t\t\t}\n\t\t}\n\t];\n</script>\n\n<Table {header} items={rows}>\n\t{#snippet prefix()}\n\t\t<div class=\"mb-4\">\n\t\t\t<TextInput placeholder=\"Search...\" />\n\t\t</div>\n\t{/snippet}\n\t\n\t{#snippet suffix()}\n\t\t<div class=\"mt-4 flex justify-between\">\n\t\t\t<span>Showing 1-10 of 50</span>\n\t\t\t<div class=\"flex gap-2\">\n\t\t\t\t<Button>Previous</Button>\n\t\t\t\t<Button>Next</Button>\n\t\t\t</div>\n\t\t</div>\n\t{/snippet}\n</Table>\n```\n\n### Table with Caption\n\n```svelte\n<script>\n\timport { Table } from 'entasis/table';\n\t\n\tconst header = {\n\t\tmonth: { content: 'Month' },\n\t\tsales: { content: 'Sales' }\n\t};\n\t\n\tconst rows = [\n\t\t{\n\t\t\tcells: {\n\t\t\t\tmonth: { content: 'January' },\n\t\t\t\tsales: { content: '$5000' }\n\t\t\t}\n\t\t}\n\t];\n</script>\n\n<Table {header} items={rows}>\n\t{#snippet caption()}\n\t\tMonthly Sales Report\n\t{/snippet}\n</Table>\n```\n\n### Table with Custom Cell Classes\n\n```svelte\n<script>\n\timport { Table } from 'entasis/table';\n\t\n\tconst header = {\n\t\tstatus: { content: 'Status', class: 'w-24' },\n\t\tdescription: { content: 'Description' }\n\t};\n\t\n\tconst rows = [\n\t\t{\n\t\t\tcells: {\n\t\t\t\tstatus: { content: 'Active', class: 'text-success' },\n\t\t\t\tdescription: { content: 'System is running normally' }\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\tcells: {\n\t\t\t\tstatus: { content: 'Warning', class: 'text-warning' },\n\t\t\t\tdescription: { content: 'High CPU usage detected' }\n\t\t\t},\n\t\t\tclass: 'bg-warning/10'\n\t\t}\n\t];\n</script>\n\n<Table {header} items={rows} />\n```\n\n### Table Density\n\n```svelte\n<script>\n\timport { Table } from 'entasis/table';\n\n\tconst header = { name: 'Name', email: 'Email' };\n\tconst rows = [\n\t\t{ cells: { name: 'John Doe', email: 'john@example.com' } }\n\t];\n</script>\n\n<!-- Dense data grid -->\n<Table {header} items={rows} density=\"compact\" />\n\n<!-- Default everyday scale -->\n<Table {header} items={rows} density=\"normal\" />\n\n<!-- Roomy detail surface -->\n<Table {header} items={rows} density=\"comfortable\" />\n```\n\n### Table with RowSpan and ColSpan\n\n```svelte\n<script lang=\"ts\">\n\timport { Table, type TableRow } from 'entasis/table';\n\n\tconst header = {\n\t\tname: { content: 'Name' },\n\t\tdetails: { content: 'Details' },\n\t\tstatus: { content: 'Status' }\n\t};\n\n\tconst rows: TableRow[] = [\n\t\t{\n\t\t\tcells: {\n\t\t\t\tname: { content: 'John Doe', rowSpan: 2 },\n\t\t\t\tdetails: { content: 'Email: john@example.com' },\n\t\t\t\tstatus: { content: 'Active' }\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\tcells: {\n\t\t\t\tdetails: { content: 'Phone: +1234567890' },\n\t\t\t\tstatus: { content: 'Active' }\n\t\t\t}\n\t\t}\n\t];\n</script>\n\n<Table {header} items={rows} />\n```\n\n## Accessibility\n\n- Uses semantic HTML elements (`<table>`, `<thead>`, `<tbody>`, `<tfoot>`, `<th>`, `<td>`)\n- Supports `<caption>` for table descriptions\n- Proper heading structure with `<th>` elements in header\n- Hover states for better interactivity feedback\n\n## Notes\n\n- The component uses a configuration-over-markup approach, making it easy to generate tables from data\n- Header, footer, and row cells are defined as objects keyed by column name (`Record<string, TableCell>`)\n- Cells in rows are automatically rendered in the same order as the header keys (if header is provided)\n- Rows can use either structured `cells` objects or flexible `content` slots\n- The `prefix` and `suffix` slots are designed for future features like search and pagination\n- All parts of the table can be styled via the theme system\n- The table container includes horizontal scroll for responsive design\n- Rows have a subtle hover effect with `bg-neutral-muted/40`\n- All borders use `border-neutral-muted` for consistency\n- Row/cell spacing follows the `density` prop; the default 'normal' keeps rows at `py-0.5` with `p-2` cells\n\n## Theme Customization\n\nThe Table component uses a theme object that can be customized using the `theme` prop or by setting a global theme.\n\n### Theme Structure\n\nThe theme object contains the following parts:\n- **root**: Table wrapper container styles\n- **table**: Main table element styles\n- **thead**: Table header section styles\n- **tbody**: Table body section styles\n- **tfoot**: Table footer section styles\n- **row**: Table row styles\n- **head**: Header cell styles\n- **cell**: Data cell styles\n- **caption**: Table caption styles\n- **prefix**: Prefix slot container styles\n- **suffix**: Suffix slot container styles\n\n### Available Variants\n\n**root**:\n- base: Base classes for scrollable container\n\n**table**:\n- base: Base classes for table element\n\n**thead**:\n- base: Base classes for header section\n\n**tbody**:\n- base: Base classes for body section\n\n**tfoot**:\n- base: Base classes for footer section\n\n**row**:\n- base: Base classes for table rows\n- Variants:\n - density: 'compact' | 'normal' | 'comfortable' - Vertical row padding (py-0 / py-0.5 / py-1)\n - selected: boolean - Selected row styling, driven by the row's `selected` flag (which also sets `data-state=\"selected\"`)\n\n**head**:\n- base: Base classes for header cells\n- Variants:\n - density: 'compact' | 'normal' | 'comfortable' - Header cell height and horizontal padding (h-8 px-1.5 / h-10 px-2 / h-12 px-3)\n\n**cell**:\n- base: Base classes for data cells\n- Variants:\n - density: 'compact' | 'normal' | 'comfortable' - Cell padding (px-1.5 py-1 / p-2 / p-3)\n\n**caption**:\n- base: Base classes for table caption\n- Variants:\n - density: 'compact' | 'normal' | 'comfortable' - Caption top margin (mt-3 / mt-4 / mt-6)\n\n**prefix**:\n- base: Base classes for prefix slot\n\n**suffix**:\n- base: Base classes for suffix slot\n\n### Usage Examples\n\n**Basic Theme Override**:\n```svelte\n<Table \n {header}\n items={rows}\n theme={{\n table: {\n base: 'border-collapse border-2'\n },\n row: {\n base: 'state-layer transition-colors'\n },\n head: {\n base: 'bg-gray-100 font-semibold'\n }\n }}\n/>\n```\n\n**Custom Row Styling**:\n```svelte\n<Table \n {header}\n items={rows}\n theme={{\n row: {\n base: 'border-b border-gray-200',\n selected: {\n true: 'bg-blue-50'\n }\n },\n cell: {\n base: 'px-4 py-2'\n },\n head: {\n base: 'px-4 py-3 text-left font-bold'\n }\n }}\n/>\n```\n\n**Global Theme Setting**:\n```svelte\n<script>\n import { setTableTheme } from '../components/Table/index.ts';\n \n setTableTheme({\n table: {\n base: 'w-full border-collapse'\n },\n row: {\n base: 'state-layer transition-colors',\n selected: {\n true: 'bg-primary/10'\n }\n },\n head: {\n base: 'bg-gray-100 font-semibold'\n }\n });\n</script>\n```\n";
80
- readonly 'data-table': "\n# DataTable\n\nDataTable is the typed, interactive, virtualized table for application data. Its public API is\nEntasis-native; TanStack Table Core remains private, but it must be installed as an optional peer\ndependency. Use Table for static tabular content.\n\n## Requires\n\nDataTable builds its row model with TanStack Table Core, which ships with entasis.\n\n## Basic usage\n\n```svelte\n<script lang=\"ts\">\n import {\n createDataTableColumnHelper,\n DataTable\n } from '../components/DataTable/index.ts';\n\n type Person = { id: string; name: string; role: string; active: boolean };\n const column = createDataTableColumnHelper<Person>();\n const columns = [\n column.accessor('name', {\n id: 'name',\n header: 'Name',\n sortable: true,\n filter: { type: 'text' }\n }),\n column.accessor('role', { id: 'role', header: 'Role' }),\n column.accessor('active', {\n id: 'active',\n header: 'Active',\n filter: { type: 'boolean' }\n })\n ];\n</script>\n\n<DataTable\n {items}\n {columns}\n getRowId={(person) => person.id}\n height={420}\n search\n pagination={{ pageSize: 25, pageSizes: [25, 50, 100] }}\n/>\n```\n\n`items`, `columns`, and `getRowId` are required. Without `height`, DataTable fills a\nparent with a definite height so the virtual viewport remains bounded. Pass `virtualize={false}`\ninstead to drop the table into normal document flow at its natural height:\n\n```svelte\n<DataTable {items} {columns} {getRowId} virtualize={false} />\n```\n\n## Core props\n\n- **items**: `readonly TData[]`.\n- **columns**: `readonly DataTableColumn<TData>[]`.\n- **getRowId**: stable row identity used by selection, expansion, virtualization, and focus.\n- **height**: optional `string | number`; otherwise fills a definite-height parent. Only read\n while virtualizing.\n- **virtualize**: default true. False renders every row in normal flow, so the table needs no\n height and no definite-height parent.\n- **density**: `compact | normal | comfortable`, default `normal`.\n- **interactionMode**: `table | grid`, default `table`.\n- **processingMode**: `client | manual`, default `client`.\n- **selectionMode**: `none | single | multiple`, default `none`.\n- **pagination**: `false | DataTablePaginationConfig`. False disables pagination processing.\n `showControls: false` keeps processing active while hiding the built-in footer.\n- **search**: false, true, or placeholder/debounce configuration.\n- **showColumnVisibilityControl**: default false.\n- **stickyHeader**, **overscan**, **estimatedRowHeight**, and **animateRows** control rendering.\n- **disabled** blocks sorting controls, selection paths, editing, and column manipulation.\n- **loading** and **error** drive table states and the NetworkIndicator.\n- **api**: bindable narrow external-control instance handle (`DataTableApi<TData>`).\n- **cell** and **header**: table-level renderer snippets with `renderDefault` delegates.\n- **class**, **theme**, **ref**, and Svelte attachments follow Entasis conventions.\n\n## Column definitions\n\n`DataTableColumn<TData, TValue>` supports `id`, `accessor`, required `header`, optional\n`cell` and `aggregatedCell`, sorting, filtering, grouping, aggregation, editing, visibility,\nresizing, reordering, pinning, alignment, `width` / `minWidth` / `maxWidth`, and column\nclasses.\n\nUse `createDataTableColumnHelper<TData>()` to infer accessor values inside cell and editor\nconfiguration. Raw DataTableColumn definitions remain available as an escape hatch.\n\n## Rendering\n\nColumn-level renderers are part of the default rendering chain. A table-level `cell` renderer\nwraps every public ordinary, grouped, aggregated, and placeholder cell. It does not wrap selection\ncells, action cells, detail rows, or active editors. A table-level `header` renderer wraps only\nconfigured public header content; sorting, menus, dragging, resizing, focus, and ARIA remain owned\nby DataTable.\n\n`DataTableCellRenderPayload<TData>` contains the normal cell payload plus `column`,\n`placeholder`, and `renderDefault`. `DataTableHeaderRenderPayload<TData>` contains the normal\nheader payload plus `renderDefault`.\n\n```svelte\n{#snippet cell({ columnId, value, renderDefault })}\n {#if columnId === 'status'}\n <Chip>{value}</Chip>\n {:else}\n {@render renderDefault()}\n {/if}\n{/snippet}\n```\n\nPrecedence is structural rendering or an active editor, then the table-level renderer, then\n`renderDefault`, which resolves the column renderer before the built-in fallback.\n\n## Filters\n\nBuilt-in filters are text, number range, select, multi-select, date range, and boolean.\nA custom filter is `{ type: 'custom', render, predicate }`. Its renderer receives the column,\ncurrent value, active state, `setValue(value)`, and `clear()`; `predicate(row, value,\ncolumnValue)` owns client matching. Filtering, searching, sorting, and grouping reset page one.\n\n## State and external composition\n\nUse `createDataTableState(columns, initialState?, pageSize?)` for controlled state. The bindable\nstate contains sorting, globalFilter, columnFilters, one-based pagination, rowSelection,\ncolumnVisibility, columnOrder, columnPinning, columnSizing, grouping, and expanded.\n\nDataTable updates replace slices immutably and call `onStateChange(nextState)`. Parent-originated\nmutations through `bind:state` are observed without calling that callback again.\n\n`bind:api` exposes a stable `DataTableApi<TData>` with reactive state, totals, visible\nrows, selected loaded rows, save status, and commands for global/column filters, selection clearing,\npage, and page size. Commands use the same guards and reset rules as built-in controls.\n\n```svelte\n<DataTable\n bind:state\n bind:api\n pagination={{ pageSize: 25, showControls: false }}\n {...props}\n/>\n```\n\n## Client and manual processing\n\nClient mode runs filtering, sorting, grouping, aggregation, expansion, and pagination locally.\n\nManual mode delegates filtering, sorting, and pagination together. It requires `rowCount`, and\n`items` must contain the already processed current page. Grouping and aggregation are client-only\nin this release: grouping commands are hidden and unsupported manual grouping state is normalized\naway. Abort stale requests when state changes. In manual mode, `dataTable.selectedRows` includes\nonly loaded row objects; all selected IDs remain in `state.rowSelection`.\n\n## Editing\n\nBuilt-in editors are text, number, select, date, and switch. A custom editor snippet receives row\nmetadata, draft, pending/error state, `setDraft`, `commit`, and `cancel`. DataTable never\nmutates rows; changed values call `onCellCommit` and render optimistically while the\nNetworkIndicator tracks the request. Unchanged drafts close without a commit. Rejection rolls back,\nrestores the editor and draft, and exposes the error. Only one asynchronous transaction can be\nactive or recoverable. Columns with editors do not enter edit mode without `onCellCommit` and\nissue a development warning.\n\nEnter and outside clicks commit, Escape cancels, and Tab commits without leaving the edited cell.\nCalendar selection synchronizes its draft before commit. Arrow keys remain owned by editor controls.\n\n## Slots\n\n- **caption**, **toolbarPrefix**, **toolbarSuffix**, **bulkActions**, **rowActions**,\n **expandedContent**, **loadingContent**, **empty**, **noResults**, and **errorContent**.\n- Toolbar slots receive state, selected loaded rows, visible rows, clearFilters, and clearSelection.\n- Row slots receive row identity, selection/expansion state, depth, and guarded toggle actions.\n\n## Accessibility and virtualization\n\nTable mode preserves native table semantics. Grid mode uses one roving cell tab stop with arrows,\nTab/Shift+Tab, Home/End, Ctrl+Home/Ctrl+End, and PageUp/PageDown. Focus is reconciled by row and\ncolumn identity after data, pagination, filtering, grouping, and visibility changes. State rows use\ncomplete column spans and keep recovery controls keyboard reachable. Logical row indices derive\nfrom the final grouped/expanded row model.\n\nRows are virtualized by default; `virtualize={false}` renders all of them in normal flow at\ntheir natural height, keeping the same selection, keyboard navigation, and editing paths. Use it for\nsmall datasets that belong in document flow, not for large ones. Semantic table mode keeps all\ncolumns mounted; grid mode additionally virtualizes center columns while pinned columns stay\nmounted. Resize handles support pointer and\nkeyboard resizing. Reorder handles support left/right keyboard movement and pointer movement within\ntheir current pin region. Logical inset positioning preserves RTL pinning.\n";
80
+ readonly 'data-table': "\n# DataTable\n\nDataTable is the typed, interactive, virtualized table for application data. Its public API is\nEntasis-native; TanStack Table Core remains private, but it must be installed as an optional peer\ndependency. Use Table for static tabular content.\n\n## Requires\n\nDataTable builds its row model with TanStack Table Core, which ships with entasis.\n\n## Basic usage\n\n```svelte\n<script lang=\"ts\">\n import {\n createDataTableColumnHelper,\n DataTable\n } from '../components/DataTable/index.ts';\n\n type Person = { id: string; name: string; role: string; active: boolean };\n const column = createDataTableColumnHelper<Person>();\n const columns = [\n column.accessor('name', {\n id: 'name',\n header: 'Name',\n sortable: true,\n filter: { type: 'text' }\n }),\n column.accessor('role', { id: 'role', header: 'Role' }),\n column.accessor('active', {\n id: 'active',\n header: 'Active',\n filter: { type: 'boolean' }\n })\n ];\n</script>\n\n<DataTable\n {items}\n {columns}\n getRowId={(person) => person.id}\n height={420}\n search\n pagination={{ pageSize: 25, pageSizes: [25, 50, 100] }}\n/>\n```\n\n`items`, `columns`, and `getRowId` are required. Without `height`, DataTable fills a\nparent with a definite height so the virtual viewport remains bounded. Pass `virtualize={false}`\ninstead to drop the table into normal document flow at its natural height:\n\n```svelte\n<DataTable {items} {columns} {getRowId} virtualize={false} />\n```\n\n## Core props\n\n- **items**: `readonly TData[]`.\n- **columns**: `readonly DataTableColumn<TData>[]`.\n- **getRowId**: stable row identity used by selection, expansion, virtualization, and focus.\n- **height**: optional `string | number`; otherwise fills a definite-height parent. Only read\n while virtualizing.\n- **virtualize**: default true. False renders every row in normal flow, so the table needs no\n height and no definite-height parent.\n- **density**: `compact | normal | comfortable`, default `normal`.\n- **interactionMode**: `table | grid`, default `table`.\n- **processingMode**: `client | manual`, default `client`.\n- **selectionMode**: `none | single | multiple`, default `none`.\n- **pagination**: `false | DataTablePaginationConfig`. False disables pagination processing.\n `showControls: false` keeps processing active while hiding the built-in footer. The size select\n always offers the current `pageSize`, even when `pageSizes` leaves it out.\n- **search**: false, true, or placeholder/debounce configuration.\n- **showColumnVisibilityControl**: default false.\n- **stickyHeader**, **overscan**, **estimatedRowHeight**, and **animateRows** control rendering.\n- **disabled** blocks sorting controls, selection paths, editing, and column manipulation.\n- **loading** and **error** drive table states and the NetworkIndicator.\n- **api**: bindable narrow external-control instance handle (`DataTableApi<TData>`).\n- **cell** and **header**: table-level renderer snippets with `renderDefault` delegates.\n- **class**, **theme**, **ref**, and Svelte attachments follow Entasis conventions.\n\n## Column definitions\n\n`DataTableColumn<TData, TValue>` supports `id`, `accessor`, required `header`, optional\n`cell` and `aggregatedCell`, sorting, filtering, grouping, aggregation, editing, visibility,\nresizing, reordering, pinning, alignment, `width` / `minWidth` / `maxWidth`, and column\nclasses.\n\nUse `createDataTableColumnHelper<TData>()` to infer accessor values inside cell and editor\nconfiguration. Raw DataTableColumn definitions remain available as an escape hatch.\n\n## Rendering\n\nColumn-level renderers are part of the default rendering chain. A table-level `cell` renderer\nwraps every public ordinary, grouped, aggregated, and placeholder cell. It does not wrap selection\ncells, action cells, detail rows, or active editors. A table-level `header` renderer wraps only\nconfigured public header content; sorting, menus, dragging, resizing, focus, and ARIA remain owned\nby DataTable.\n\n`DataTableCellRenderPayload<TData>` contains the normal cell payload plus `column`,\n`placeholder`, and `renderDefault`. `DataTableHeaderRenderPayload<TData>` contains the normal\nheader payload plus `renderDefault`.\n\n```svelte\n{#snippet cell({ columnId, value, renderDefault })}\n {#if columnId === 'status'}\n <Chip>{value}</Chip>\n {:else}\n {@render renderDefault()}\n {/if}\n{/snippet}\n```\n\nPrecedence is structural rendering or an active editor, then the table-level renderer, then\n`renderDefault`, which resolves the column renderer before the built-in fallback.\n\n## Filters\n\nBuilt-in filters are text, number range, select, multi-select, date range, and boolean.\nA custom filter is `{ type: 'custom', render, predicate }`. Its renderer receives the column,\ncurrent value, active state, `setValue(value)`, and `clear()`; `predicate(row, value,\ncolumnValue)` owns client matching. Filtering, searching, sorting, and grouping reset page one.\n\n## State and external composition\n\nUse `createDataTableState(columns, initialState?, pageSize?)` for controlled state. The bindable\nstate contains sorting, globalFilter, columnFilters, one-based pagination, rowSelection,\ncolumnVisibility, columnOrder, columnPinning, columnSizing, grouping, and expanded.\n\nDataTable updates replace slices immutably and call `onStateChange(nextState)`. Parent-originated\nmutations through `bind:state` are observed without calling that callback again.\n\n`bind:api` exposes a stable `DataTableApi<TData>` with reactive state, totals, visible\nrows, selected loaded rows, save status, and commands for global/column filters, selection clearing,\npage, and page size. Commands use the same guards and reset rules as built-in controls.\n\n```svelte\n<DataTable\n bind:state\n bind:api\n pagination={{ pageSize: 25, showControls: false }}\n {...props}\n/>\n```\n\n## Client and manual processing\n\nClient mode runs filtering, sorting, grouping, aggregation, expansion, and pagination locally.\n\nManual mode delegates filtering, sorting, and pagination together. It requires `rowCount`, and\n`items` must contain the already processed current page. Grouping and aggregation are client-only\nin this release: grouping commands are hidden and unsupported manual grouping state is normalized\naway. Abort stale requests when state changes. In manual mode, `dataTable.selectedRows` includes\nonly loaded row objects; all selected IDs remain in `state.rowSelection`.\n\n## Editing\n\nBuilt-in editors are text, number, select, date, and switch. A custom editor snippet receives row\nmetadata, draft, pending/error state, `setDraft`, `commit`, and `cancel`. DataTable never\nmutates rows; changed values call `onCellCommit` and render optimistically while the\nNetworkIndicator tracks the request. Unchanged drafts close without a commit. Rejection rolls back,\nrestores the editor and draft, and exposes the error. Only one asynchronous transaction can be\nactive or recoverable. Columns with editors do not enter edit mode without `onCellCommit` and\nissue a development warning.\n\nEnter and outside clicks commit, Escape cancels, and Tab commits without leaving the edited cell.\nCalendar selection synchronizes its draft before commit. Arrow keys remain owned by editor controls.\n\n## Slots\n\n- **caption**, **toolbarPrefix**, **toolbarSuffix**, **bulkActions**, **rowActions**,\n **expandedContent**, **loadingContent**, **empty**, **noResults**, and **errorContent**.\n- Toolbar slots receive state, selected loaded rows, visible rows, clearFilters, and clearSelection.\n- Row slots receive row identity, selection/expansion state, depth, and guarded toggle actions.\n- The row actions column is 36px, one icon button; set `rowActionsWidth` (pixels) for text\n buttons or several actions.\n\n## Row activation\n\n`onRowActivate(payload)` receives the row payload plus the native `event`, for modifier keys.\nIt fires on a click anywhere in the row except on a control (link, button, input, label, focusable\nelement), the selection or actions column, a group row, or a click that ends a text selection.\nIn grid mode Enter activates a cell that has no editor and no control. Rows show a pointer while it\nis set. Table-mode rows are not focusable, so keep a link or row action for keyboard users.\n\n## Accessibility and virtualization\n\nTable mode preserves native table semantics. Grid mode uses one roving cell tab stop with arrows,\nTab/Shift+Tab, Home/End, Ctrl+Home/Ctrl+End, and PageUp/PageDown. Focus is reconciled by row and\ncolumn identity after data, pagination, filtering, grouping, and visibility changes. State rows use\ncomplete column spans and keep recovery controls keyboard reachable. Logical row indices derive\nfrom the final grouped/expanded row model.\n\nRows are virtualized by default; `virtualize={false}` renders all of them in normal flow at\ntheir natural height, keeping the same selection, keyboard navigation, and editing paths. Use it for\nsmall datasets that belong in document flow, not for large ones. Semantic table mode keeps all\ncolumns mounted; grid mode additionally virtualizes center columns while pinned columns stay\nmounted. Resize handles support pointer and\nkeyboard resizing. Reorder handles support left/right keyboard movement and pointer movement within\ntheir current pin region. Logical inset positioning preserves RTL pinning.\n";
81
81
  readonly timeline: "\n# Timeline\n\nRender an ordered sequence of dated or descriptive events on a vertical or horizontal axis. Timeline owns the semantic list, item surfaces, axis anchors, markers, connectors, responsive alternate collapse, and horizontal overflow behavior. Input order is always render order.\n\n## Basic usage\n\n```svelte\n<script lang=\"ts\">\n import { Timeline, type TimelineItem } from '../components/Timeline/index.ts';\n\n const items: TimelineItem[] = [\n {\n id: 'placed',\n date: 'Mar 15, 2024',\n datetime: '2024-03-15',\n title: 'Order placed',\n description: 'Your order has been received.'\n },\n {\n id: 'payment',\n date: 'Mar 16, 2024',\n datetime: '2024-03-16',\n title: 'Payment confirmed',\n color: 'success'\n }\n ];\n</script>\n\n<Timeline {items} />\n```\n\nThe default configuration is a vertical timeline with all content on the logical end side of the axis.\n\n## Props\n\n### Data and layout\n\n- **items**: `readonly Item[]` (required) — entries in display order. `Item` must extend `TimelineItem`.\n- **orientation**: `'vertical' | 'horizontal'` (default: `'vertical'`) — direction of the sequence axis.\n- **placement**: `'start' | 'end' | 'alternate'` (default: `'end'`) — logical side used for item surfaces. Alternate placement starts on `end`, then alternates by array index.\n- **variant**: `'ghost' | 'card' | 'outline' | 'soft'` (default: `'ghost'`) — global surface treatment for all entries.\n- **size**: `'small' | 'normal' | 'large'` (default: `'normal'`) — title and description typography, marker and icon scale, and loading Spinner scale.\n- **density**: `'compact' | 'normal' | 'comfortable'` (default: `'normal'`) — item gaps, surface padding, connector spacing, and horizontal item minimum width.\n- **color**: Entasis semantic color (default: `'neutral'`) — default marker and outline or soft surface accent.\n- **connectorColor**: Entasis semantic color (default: `'neutral'`) — default outgoing connector color.\n- **showConnectors**: `boolean` (default: `true`) — shows connector segments between markers.\n- **scrollFade**: `boolean` (default: `true`) — applies the shared logical horizontal scroll fade only while a horizontal timeline actually overflows.\n- **i18n**: `Partial<Messages>` — per-instance translations merged over the global i18n catalog.\n\n### Custom rendering\n\n- **item**: `Snippet<[TimelineItemPayload<Item>]>` — replaces the content inside each Timeline-owned variant surface.\n- **marker**: `Snippet<[TimelineItemPayload<Item>]>` — replaces the marker visual inside the Timeline-owned axis anchor.\n- **opposite**: `Snippet<[TimelineItemPayload<Item>]>` — replaces the opposite track in alternate placement. Its default renderer is the item date. TypeScript rejects this prop with fixed `start` or `end` placement.\n\n### Root element\n\n- **ref**: `HTMLOListElement | null` (bindable) — reference to the root `<ol>`.\n- **class**: `string` — additional root classes.\n- **theme**: `TimelineThemeProps` — per-instance overrides for Timeline theme parts.\n- Native ordered-list attributes and Svelte attachments are forwarded to the root.\n\n## Item shape\n\n```ts\ntype TimelineItem = {\n id?: string | number;\n title: Slot;\n date?: Slot;\n datetime?: string;\n description?: Slot;\n icon?: Slot;\n loading?: boolean;\n color?: Colors;\n connectorColor?: Colors;\n side?: 'start' | 'end';\n};\n```\n\n- `title` is required. `title`, `date`, `description`, and `icon` accept a string or snippet.\n- `id` is optional. Use a stable explicit ID when entries can reorder; otherwise Timeline uses the array index as identity.\n- `datetime` adds the machine-readable value to a default `<time>` element when `date` is present.\n- `icon` replaces the default dot inside the default marker.\n- `loading` replaces the default marker visual with a color- and size-aware Spinner.\n- `color` overrides the root color for this marker and its outline or soft surface.\n- `connectorColor` overrides the root color for this item's outgoing connector. It has no effect on the final item.\n- `side` overrides alternate parity for one item. It is not valid with fixed placement.\n\n## Placement and dates\n\nFixed `start` and `end` placement keeps every surface on one logical side. The default content renders the date before the title inside each surface.\n\n`placement=\"alternate\"` creates an opposite track and starts the first item on `end`. Its default opposite renderer displays the date. Set `item.side` to place a specific alternate item on `start` or `end` without changing the array order.\n\n```svelte\n<Timeline items={milestones} placement=\"alternate\" variant=\"card\" />\n```\n\nTimeline treats dates as display content. It does not parse, format, compare, or sort them. It also has no completed, current, or pending status model. Put status meaning in the title or description and use semantic color or a custom marker only as supporting presentation.\n\n## Generic snippets and defaults\n\nApplication item types can extend `TimelineItem`. The extended type remains available as `payload.item` in all three renderer snippets.\n\n```svelte\n<script lang=\"ts\">\n import { Timeline, type TimelineItem } from '../components/Timeline/index.ts';\n\n type Release = TimelineItem & {\n version: string;\n href: string;\n };\n\n const releases: Release[] = [\n { id: 'v2', title: 'Version 2 released', version: '2.0.0', href: '/releases/v2' }\n ];\n</script>\n\n<Timeline items={releases}>\n {#snippet item({ item, defaultContent })}\n <a href={item.href} class=\"block\">\n {@render defaultContent()}\n <span class=\"text-neutral/70 text-xs\">{item.version}</span>\n </a>\n {/snippet}\n</Timeline>\n```\n\nEvery snippet receives:\n\n```ts\ntype TimelineItemPayload<Item extends TimelineItem> = Readonly<{\n item: Item;\n index: number;\n side: 'start' | 'end';\n orientation: 'vertical' | 'horizontal';\n color: Colors;\n connectorColor: Colors;\n isFirst: boolean;\n isLast: boolean;\n defaultContent: Snippet;\n defaultMarker: Snippet;\n defaultOpposite: Snippet;\n}>;\n```\n\nCall `defaultContent()`, `defaultMarker()`, or `defaultOpposite()` to wrap or extend the matching default renderer. A custom `marker` snippet that does not call `defaultMarker()` owns its complete marker visual, including loading presentation. Timeline still owns the `<ol>`, each `<li>`, axis and connector geometry, marker anchor, opposite track, and variant surface.\n\n## Responsive alternate layout\n\nA vertical alternate timeline uses its own inline-size container. Below `40rem`, it moves the axis to the logical start edge, places every main surface on the end side, and places opposite content before the main surface. DOM and input order do not change. Per-item `side` values remain in the payload, but their visual side effect is suspended while the layout is collapsed.\n\n## Horizontal overflow\n\nHorizontal entries stay on one axis and use density-controlled minimum widths. They do not wrap or compress; the root uses native horizontal scrolling. Timeline measures real overflow with `ResizeObserver`, applies `scroll-fade-x` only when needed, and makes the ordered list keyboard-focusable only while it overflows. The fade and native scrolling use logical inline direction and work in LTR and RTL layouts.\n\n## Variants\n\n- `ghost`: no surface chrome.\n- `card`: neutral raised surface with a quiet ring and shadow.\n- `outline`: transparent surface with a ring in the resolved item color.\n- `soft`: muted surface in the resolved item color.\n\nThe variant is global. Timeline does not support per-item variant overrides.\n\n## Runtime validation\n\nTimeline throws a descriptive `TypeError` when:\n\n- two items have the same explicit `id`;\n- an item supplies `side` while placement is fixed to `start` or `end`.\n\nThe ID check preserves number and string identity, so `1` and `'1'` are different IDs. TypeScript separately rejects `opposite` with fixed placement.\n\n## Accessibility\n\n- The root is an ordered list and every event is a list item.\n- Axis anchors, markers, and connectors are decorative and hidden from assistive technology. Do not use marker color or shape as the only status signal.\n- A default loading marker has a localized polite status announcement outside the decorative axis.\n- A default date with `datetime` renders as a semantic `<time>` element.\n- An overflowing horizontal timeline becomes a native keyboard scroll region; a non-overflowing timeline adds no tab stop.\n\n## Theme parts\n\n`root`, `item`, `opposite`, `axis`, `connector`, `marker`, `content`, `date`, `titleRow`, `title`, `description`, and `loading`.\n\nTimeline is display-only. It has no selection, navigation, event callbacks, animation state, bindable controller, date processing, or built-in progress status.\n";
82
82
  readonly tree: "\n# Tree Component\n\nVirtualized file tree powered by @pierre/trees. The wrapper provides Svelte props,\nSSR pre-rendering, bindable access to the FileTree instance, tokenized styling,\nGit status, search, snippets, context menus, drag-and-drop, renaming, and mutation\nevents.\n\n## Basic Usage\n\n```svelte\n<script lang=\"ts\">\n\timport { Tree } from 'entasis/tree';\n\n\tconst paths = [\n\t\t'src/lib/components/Tree/Tree.svelte',\n\t\t'src/lib/components/Tree/tree.props.ts',\n\t\t'src/routes/components/tree/+page.svelte'\n\t];\n</script>\n\n<Tree paths={paths} height=\"320px\" initialExpansion=\"open\" search />\n```\n\n## Large Trees\n\nUse `prepareFileTreeInput` from `entasis/tree` and pass `preparedInput` for large\nor SSR-heavy trees. Keep the input preparation outside component render work.\n\n```svelte\n<script lang=\"ts\">\n\timport { Tree, prepareFileTreeInput } from 'entasis/tree';\n\n\tconst preparedInput = prepareFileTreeInput(paths, { sort: 'default' });\n</script>\n\n<Tree {preparedInput} height={420} initialVisibleRowCount={18} overscan={8} />\n```\n\n## AI-Safe Usage Contract\n\nPrefer the narrowest public API:\n\n1. Use `paths` for small and medium static trees.\n2. Use `preparedInput` for large trees, server-rendered trees, or pre-sorted data.\n3. Use promoted props such as `search`, `gitStatus`, `icons`, `renaming`,\n `dragAndDrop`, `density`, and `searchTopInset` before using raw `options`.\n4. Use `bind:api` only when you need imperative behavior such as custom search,\n focus, mutation, or scroll control.\n5. Passing `onRename` enables inline rename with the default policy unless\n `renaming={false}`. Passing `onDropComplete` enables drag-and-drop with the\n default policy unless `dragAndDrop={false}`.\n6. Use `TreeContextMenuSurface` for normal context menus instead of rebuilding menu\n positioning from scratch.\n7. Use `unsafeCSS` only for unsupported shadow-DOM styling. Prefer `searchTopInset`\n for search spacing.\n\nNever pass both `paths` and `preparedInput`. Virtualized trees need a bounded\n`height` or an outer layout that gives the wrapper a real height.\n\n## Recipes\n\n### Controlled Search\n\n```svelte\n<script lang=\"ts\">\n\timport { Tree, type FileTree } from 'entasis/tree';\n\n\tlet fileTree: FileTree | undefined = $state();\n\tlet query = $state('');\n\n\tfunction setQuery(value: string) {\n\t\tquery = value;\n\t\tfileTree?.setSearch(value.length === 0 ? null : value);\n\t}\n</script>\n\n<input value={query} oninput={(event) => setQuery(event.currentTarget.value)} />\n<Tree bind:api paths={paths} height={360} search initialSearchQuery={query} />\n```\n\n### Git Status\n\n```svelte\n<script lang=\"ts\">\n\timport { Tree, type GitStatusEntry } from 'entasis/tree';\n\n\tconst gitStatus: GitStatusEntry[] = [\n\t\t{ path: 'src/lib/Tree.svelte', status: 'modified' }\n\t];\n</script>\n\n<Tree paths={paths} {gitStatus} showGitStatus height={360} />\n```\n\n### Context Menu\n\n```svelte\n<script lang=\"ts\">\n\timport { Tree, TreeContextMenuSurface } from 'entasis/tree';\n</script>\n\n<Tree\n\tpaths={paths}\n\theight={360}\n\tcomposition={{ contextMenu: { enabled: true, triggerMode: 'both' } }}\n>\n\t{#snippet contextMenu(data)}\n\t\t<TreeContextMenuSurface\n\t\t\t{data}\n\t\t\titems={[{ type: 'option', title: `Copy ${data.name}`, onclick: () => copy(data.path) }]}\n\t\t/>\n\t{/snippet}\n</Tree>\n```\n\n## Props\n\n### Data\n- **paths**: readonly string[] - Canonical file and directory paths.\n- **preparedInput**: FileTreePreparedInput - Pre-shaped input from @pierre/trees.\n- **options**: TreeOptions - Lower-level FileTree options.\n- **api**: FileTree - Bindable instance handle for imperative calls.\n\n### Rendering\n- **height**: number | string - Bounded wrapper height. Virtualized trees need one.\n- **hostClass**: string - Classes applied to the inner file-tree-container.\n- **theme**: TreeThemeProps - Theme overrides for root, viewport, host, and error.\n- **class**: string - Classes applied to the outer wrapper.\n- **ref**: HTMLDivElement - Bindable wrapper element.\n\n### Tree Options\n- **id**: string - Stable tree id.\n- **initialExpansion**: 'closed' | 'open' | number - Initial expansion policy.\n- **initialExpandedPaths**: readonly string[] - Paths expanded initially or on reset.\n- **initialSelectedPaths**: readonly string[] - Paths selected initially.\n- **flattenEmptyDirectories**: boolean - Collapse single-child directory chains.\n- **presorted**: boolean - Treat input paths as already sorted.\n- **sort**: 'default' | FileTreeSortComparator - Sort policy.\n- **density**: 'compact' | 'normal' | 'comfortable' - Semantic row density. Lower-level numeric tuning belongs in options.density.\n- **itemHeight**: number - Virtualized row height.\n- **overscan**: number - Extra rows above and below the viewport.\n- **initialVisibleRowCount**: number - SSR and virtualization first-pass row count.\n- **icons**: FileTreeIcons - Built-in icon set and overrides.\n- **gitStatus**: GitStatusEntry[] - Status by path.\n- **showGitStatus**: boolean - Set false to hide status styling.\n- **renderRowDecoration**: FileTreeRowDecorationRenderer - Row label/icon decoration.\n- **search**: boolean - Enables built-in search UI.\n- **initialSearchQuery**: string | null - Initial search value.\n- **fileTreeSearchMode**: FileTreeSearchMode - Search filtering strategy.\n- **searchBlurBehavior**: FileTreeSearchBlurBehavior - Search close/retain behavior.\n- **searchFakeFocus**: boolean - Preserve visual tree focus during search.\n- **searchTopInset**: number | string - Top spacing above the built-in search input.\n- **stickyFolders**: boolean - Keep ancestor folders sticky while scrolling.\n- **unsafeCSS**: string - CSS injected into the tree shadow root.\n- **dragAndDrop**: boolean | FileTreeDragAndDropConfig - DnD behavior and policies.\n- **renaming**: boolean | FileTreeRenamingConfig - Inline rename behavior and policies.\n- **composition**: FileTreeCompositionOptions - Raw Pierre composition hooks.\n\n### Snippets\n- **header**: Snippet - Custom header mounted into the tree composition header slot.\n- **contextMenu**: Snippet<[TreeContextMenuSnippetData]> - Custom row context menu.\n- **TreeContextMenuSurface**: exported helper component for standard entasis Menu styling\n and Pierre context-menu positioning.\n\n### Events\n- **onReady**: (payload: FileTree) => void - After render or hydration.\n- **onFocusChange**: (path: string | null) => void - Focused row changed.\n- **onMutation**: (event: FileTreeMutationEvent) => void - Add/remove/move/reset event.\n- **onSelectionChange**: (paths: readonly string[]) => void - Selection changed. Its payload is the\n whole selected-path set, so it belongs to the `onSelectionChange` state family rather than the\n `onSelect` pick event. @pierre/trees owns the selection and exposes no setter, so\n `initialSelectedPaths` is the only default and there is no controlled `selection` prop.\n- **onSearchChange**: (value: string | null) => void - Search changed.\n- **onRename**: FileTreeRenamingConfig['onRename'] - Rename completed.\n- **onRenameError**: FileTreeRenamingConfig['onError'] - Rename failed.\n- **onDropComplete**: FileTreeDragAndDropConfig['onDropComplete'] - Drop completed.\n- **onDropError**: receives one { error, event } object when a drop fails.\n\n## Accessibility\n\n@pierre/trees owns the ARIA tree semantics, keyboard navigation, selection, search,\nrenaming, drag interactions, and virtualized focus handling. This wrapper preserves\nthe server-rendered tree host during hydration and surfaces rendering errors through\na role=alert error region.\n\n## Notes\n\n- Pass exactly one data source: `paths` or `preparedInput`.\n- Virtualized trees need a bounded height via `height` or an outer layout class.\n- The wrapper maps Pierre CSS variables to entasis `--color-*` tokens, so light and\ndark themes are inherited automatically.\n- Import common Pierre helpers and types from `entasis/tree` first. Reach for\n`@pierre/trees` directly only when this wrapper does not re-export a type or helper.\n";
83
83
  readonly alert: "\n# Alert Component\n\nThe Alert component displays important messages and notifications to users. It supports icons, titles, descriptions, and multiple color variants to convey different types of information (success, warning, error, info).\n\n## Basic Usage\n\n```svelte\n<Alert>\n\t{#snippet title()}\n\t\tAlert Title\n\t{/snippet}\n\t{#snippet description()}\n\t\tThis is an important message for the user.\n\t{/snippet}\n</Alert>\n```\n\n## Props\n\n### Core Props\n- **color**: 'primary' | 'secondary' | 'neutral' | 'danger' | 'success' | 'warning' | 'info' (default: 'neutral')\n - Determines the color scheme of the alert\n - danger: For error messages\n - success: For success messages\n - warning: For warning messages\n - info: For informational messages\n\n- **variant**: 'solid' | 'outline' | 'soft' (default: 'outline')\n - solid: Filled background with color\n - outline: Transparent background with colored border\n - soft: The tinted \"toast\" look — a muted-color surface with a colored border and a legible on-tint accent (readable in light and dark). For the status colors (success/info/warning/danger) a matching filled icon is shown automatically when no `prefix` is provided.\n\n- **size**: 'small' | 'normal' | 'large' (default: 'normal')\n - small: Reduced padding and smaller text\n - normal: Standard padding and text size\n - large: Increased padding and larger text\n\n### Layout Props\n- **disabled**: boolean (default: false)\n - Disables interactions and applies opacity styling\n- **dismissible**: boolean (default: false)\n - Renders a close button; clicking it calls `onDismiss`. The alert's visibility is owned by the caller.\n- **onDismiss**: () => void\n - Called when the close button is clicked (hide the alert in this handler).\n\n### Content Props (Slots)\n- **prefix**: Snippet - Icon or content before the alert text (typically an icon)\n - When provided, the grid layout automatically adjusts to accommodate the icon\n - Suggested icons by color:\n - danger: AlertCircle or X icon\n - warning: AlertTriangle icon\n - success: CheckCircle icon\n - info: Info icon\n - default: Info icon\n- **title**: Snippet - Alert title text (bold, appears first)\n- **description**: Snippet - Alert description text (muted, appears below title)\n- **children**: Snippet - Default content (rendered inside description section if description is not provided)\n\n### Styling Props\n- **class**: string - Additional CSS classes for the alert container\n- **ref**: HTMLElement | null - Reference to the alert element\n- **theme**: AlertThemeProps - Custom theme overrides\n\n## Structure\n\nThe Alert component uses a CSS Grid layout that automatically adapts based on icon presence:\n\n```\n<Alert>\n\t<!-- Icon (optional) -->\n\t{#snippet prefix()}\n\t\t{@render warningCircleIcon()}\n\t{/snippet}\n\t\n\t<!-- Title (optional) -->\n\t{#snippet title()}\n\t\tAlert Title\n\t{/snippet}\n\t\n\t<!-- Description or children -->\n\t{#snippet description()}\n\t\tAlert description text\n\t{/snippet}\n\t\n\t<!-- Or use children -->\n\t{#snippet children()}\n\t\tAlert content\n\t{/snippet}\n</Alert>\n```\n\nThe grid layout:\n- When icon is present: `grid-cols-[calc(var(--spacing)*4)_1fr]` with gap-x-3\n- When no icon: `grid-cols-[0_1fr]`\n- Icon uses col-start-1, title/description use col-start-2\n\n## Examples\n\n### Basic Alert\n```svelte\n<Alert>\n\t{#snippet title()}\n\t\tHeads up!\n\t{/snippet}\n\t{#snippet description()}\n\t\tThis is a basic alert message.\n\t{/snippet}\n</Alert>\n```\n\n### Alert with Icon\n```svelte\n<script lang=\"ts\">\n\timport { Alert } from 'entasis/alert';\n\timport { infoIcon } from 'entasis/icons/info';\n</script>\n\n<Alert color=\"info\">\n\t{#snippet prefix()}\n\t\t{@render infoIcon()}\n\t{/snippet}\n\t{#snippet title()}\n\t\tInformation\n\t{/snippet}\n\t{#snippet description()}\n\t\tThis alert includes an icon.\n\t{/snippet}\n</Alert>\n```\n\n### Success Alert\n```svelte\n<script lang=\"ts\">\n\timport { Alert } from 'entasis/alert';\n\timport { checkCircleIcon } from 'entasis/icons/checkCircle';\n</script>\n\n<Alert color=\"success\">\n\t{#snippet prefix()}\n\t\t{@render checkCircleIcon()}\n\t{/snippet}\n\t{#snippet title()}\n\t\tSuccess!\n\t{/snippet}\n\t{#snippet description()}\n\t\tYour changes have been saved successfully.\n\t{/snippet}\n</Alert>\n```\n\n### Warning Alert\n```svelte\n<script lang=\"ts\">\n\timport { Alert } from 'entasis/alert';\n\timport { warningIcon } from 'entasis/icons/warning';\n</script>\n\n<Alert color=\"warning\" variant=\"outline\">\n\t{#snippet prefix()}\n\t\t{@render warningIcon()}\n\t{/snippet}\n\t{#snippet title()}\n\t\tWarning\n\t{/snippet}\n\t{#snippet description()}\n\t\tPlease review your input before proceeding.\n\t{/snippet}\n</Alert>\n```\n\n### Danger Alert\n```svelte\n<script lang=\"ts\">\n\timport { Alert } from 'entasis/alert';\n\timport { warningCircleIcon } from 'entasis/icons/warningCircle';\n</script>\n\n<Alert color=\"danger\">\n\t{#snippet prefix()}\n\t\t{@render warningCircleIcon()}\n\t{/snippet}\n\t{#snippet title()}\n\t\tError\n\t{/snippet}\n\t{#snippet description()}\n\t\tSomething went wrong. Please try again.\n\t{/snippet}\n</Alert>\n```\n\n### Alert Variants\n```svelte\n<!-- Solid (default) -->\n<Alert variant=\"solid\" color=\"primary\">\n\t{#snippet title()}\n\t\tSolid Alert\n\t{/snippet}\n\t{#snippet description()}\n\t\tFilled background with color\n\t{/snippet}\n</Alert>\n\n<!-- Outline -->\n<Alert variant=\"outline\" color=\"primary\">\n\t{#snippet title()}\n\t\tOutline Alert\n\t{/snippet}\n\t{#snippet description()}\n\t\tTransparent background with border\n\t{/snippet}\n</Alert>\n\n<!-- Soft -->\n<Alert variant=\"soft\" color=\"primary\">\n\t{#snippet title()}\n\t\tSoft Alert\n\t{/snippet}\n\t{#snippet description()}\n\t\tMuted background\n\t{/snippet}\n</Alert>\n```\n\n### Alert Sizes\n```svelte\n<Alert size=\"small\" color=\"info\">\n\t{#snippet title()}\n\t\tSmall Alert\n\t{/snippet}\n\t{#snippet description()}\n\t\tCompact size\n\t{/snippet}\n</Alert>\n\n<Alert size=\"normal\" color=\"info\">\n\t{#snippet title()}\n\t\tNormal Alert\n\t{/snippet}\n\t{#snippet description()}\n\t\tStandard size (default)\n\t{/snippet}\n</Alert>\n\n<Alert size=\"large\" color=\"info\">\n\t{#snippet title()}\n\t\tLarge Alert\n\t{/snippet}\n\t{#snippet description()}\n\t\tLarger padding and text\n\t{/snippet}\n</Alert>\n```\n\n### Alert with Children Only\n```svelte\n<Alert color=\"info\">\n\t{#snippet children()}\n\t\tSimple alert message without title or description slots.\n\t{/snippet}\n</Alert>\n```\n\n### Alert without Icon\n```svelte\n<Alert color=\"neutral\" variant=\"soft\">\n\t{#snippet title()}\n\t\tNo Icon Alert\n\t{/snippet}\n\t{#snippet description()}\n\t\tThe grid layout automatically adjusts when no icon is provided.\n\t{/snippet}\n</Alert>\n```\n\n### Disabled Alert\n```svelte\n<Alert disabled={true} color=\"info\">\n\t{#snippet title()}\n\t\tDisabled Alert\n\t{/snippet}\n\t{#snippet description()}\n\t\tThis alert is disabled and non-interactive.\n\t{/snippet}\n</Alert>\n```\n\n## Accessibility\n\n- The alert container has `role=\"alert\"` attribute for screen readers\n- Icons inherit text color via `text-current` for proper foreground\n- Description text uses muted foreground color for visual hierarchy\n- The component uses semantic HTML structure with data attributes for styling hooks\n\n## Notes\n\n- The grid layout automatically adjusts based on icon presence (hasIcon variant)\n- Icons are sized based on the alert size (small: size-3, normal: size-4, large: size-5)\n- Title and description stack vertically in the second column\n- When description is not provided, children slot content is rendered in its place\n- The component supports all standard colors from the design system\n- Compound variants handle special color+variant combinations (e.g., danger+soft for muted error styling)\n\n## Theme Customization\n\nThe Alert component uses a theme object that can be customized using the `theme` prop or by setting a global theme.\n\n### Theme Structure\n\nThe theme object contains the following parts:\n- **root**: Main alert container styles\n- **prefix**: Icon/prefix content styles\n- **content**: Content wrapper styles\n- **title**: Alert title text styles\n- **description**: Alert description text styles\n\n### Theme Type Definition\n\n```typescript\nimport type { AlertThemeProps } from 'entasis/alert';\n\n// Example theme customization\nconst customTheme: AlertThemeProps = {\n root: {\n base: 'custom-base-classes',\n hasIcon: {\n true: '',\n false: ''\n },\n color: {\n primary: 'bg-primary text-primary-contrast border-primary',\n danger: 'bg-danger text-danger-contrast border-danger'\n },\n variant: {\n solid: 'bg-color text-color-contrast border-color',\n outline: 'bg-transparent border-color text-color',\n soft: 'bg-color-muted text-color border-transparent'\n },\n size: {\n small: 'px-3 py-2 text-xs',\n normal: 'px-4 py-3 text-sm',\n large: 'px-5 py-4 text-base'\n },\n disabled: {\n true: 'opacity-50 cursor-not-allowed',\n false: null\n },\n hasDescription: {\n true: '',\n false: ''\n },\n hasTitle: {\n true: '',\n false: ''\n }\n },\n prefix: {\n size: {\n small: '[&>svg]:size-4',\n normal: '[&>svg]:size-5',\n large: '[&>svg]:size-6'\n }\n },\n title: {\n size: {\n small: 'text-sm',\n normal: 'text-base',\n large: 'text-md'\n }\n },\n description: {\n size: {\n small: 'text-xs',\n normal: 'text-sm',\n large: 'text-base'\n }\n }\n};\n```\n\n### Available Variants\n\n**root**:\n- base: Base classes applied to all alerts\n- Variants:\n - hasIcon: boolean - Grid layout adjustment when icon is present\n - color: 'primary' | 'secondary' | 'neutral' | 'danger' | 'success' | 'warning' | 'info' - Color scheme\n - variant: 'solid' | 'outline' | 'soft' - Visual style variant\n - size: 'small' | 'normal' | 'large' - Controls padding and text size\n - disabled: boolean - Disabled state styling\n - hasDescription: boolean - Layout adjustment when description is present\n - hasTitle: boolean - Layout adjustment when title is present\n\n**prefix**:\n- base: Base classes for icon/prefix content\n- Variants:\n - size: 'small' | 'normal' | 'large' - Icon size based on alert size\n\n**content**:\n- base: Base classes for content wrapper (flex container)\n\n**title**:\n- base: Base classes for title text\n- Variants:\n - size: 'small' | 'normal' | 'large' - Text size\n\n**description**:\n- base: Base classes for description text\n- Variants:\n - size: 'small' | 'normal' | 'large' - Text size\n\n### Usage Examples\n\n**Basic Theme Override**:\n```svelte\n<script lang=\"ts\">\n import { Alert } from '../components/Alert/index.ts';\n import { warningCircleIcon } from 'entasis/icons/warningCircle';\n</script>\n\n<Alert \n theme={{\n root: {\n base: 'rounded-xl raised-4',\n variant: {\n solid: 'border-2'\n }\n },\n title: {\n size: {\n large: 'text-xl font-bold'\n }\n }\n }}\n>\n {#snippet title()}\n Custom Styled Alert\n {/snippet}\n</Alert>\n```\n\n**Color and Variant Customization**:\n```svelte\n<Alert \n color=\"danger\"\n variant=\"soft\"\n theme={{\n root: {\n variant: {\n soft: 'bg-red-50 border-red-200 text-red-800'\n }\n },\n prefix: {\n size: {\n normal: '[&>svg]:size-6 text-red-600'\n }\n }\n }}\n>\n {#snippet prefix()}\n {@render warningCircleIcon()}\n {/snippet}\n {#snippet title()}\n Error Alert\n {/snippet}\n</Alert>\n```\n\n**Global Theme Setting**:\n```svelte\n<script>\n import { setAlertTheme } from '../components/Alert/index.ts';\n \n setAlertTheme({\n root: {\n base: 'rounded-lg transition-all',\n variant: {\n solid: 'lift-3',\n outline: 'border-2',\n soft: 'bg-opacity-20'\n }\n },\n prefix: {\n size: {\n normal: '[&>svg]:size-6'\n }\n }\n });\n</script>\n```\n";
@@ -109,8 +109,8 @@ export declare const componentMcpRegistry: {
109
109
  readonly 'hover-card': "\n# HoverCard Component\n\nHoverCard previews supplemental content when a trigger is hovered or focused. It composes Popover for positioning and dismissal with Card for the visible content surface.\n\n## Basic Usage\n\n```svelte\n<script lang=\"ts\">\n\timport { HoverCard } from 'entasis/hover-card';\n</script>\n\n<HoverCard\n\ttrigger={{ content: 'Hover @entasis', variant: 'link' }}\n\ttitle=\"@entasis\"\n\tdescription=\"Composable Svelte UI components.\"\n>\n\t<p>Preview content shown on hover or focus.</p>\n</HoverCard>\n```\n\n## Props\n\n### Core Props\n- **id**: string - Stable id for the underlying popover root.\n- **open**: boolean - Bindable open state.\n- **defaultOpen**: boolean (default: false) - Initial state when open is not provided.\n- **trigger**: string | Snippet<[HoverCardPayload]> | ButtonProps - Trigger content. ButtonProps render a Entasis Button.\n- **children**: string | Snippet<[HoverCardPayload]> - Main card content.\n- **title**: string | Snippet<[HoverCardPayload]> - Card title slot.\n- **description**: string | Snippet<[HoverCardPayload]> - Card description slot.\n- **footer**: string | Snippet<[HoverCardPayload]> - Card footer slot.\n\n### Behavior Props\n- **position**: Placement (default: 'top') - Preferred placement relative to the trigger.\n- **offset**: number (default: 8) - Gap between trigger and card.\n- **delay**: number (default: 150) - Delay before opening on hover or focus.\n- **closeDelay**: number (default: 100) - Delay before closing after pointer/focus leaves.\n- **openOnFocus**: boolean (default: true) - Opens when focus enters the trigger or card.\n- **openOnClick**: boolean (default: false) - Toggles on trigger click, useful for touch fallbacks.\n- **disabled**: boolean (default: false) - Prevents opening and disables Button triggers.\n\n### Dismissal and Transition Props\n- **closeOnEscape**: boolean (default: true) - Escape closes the hover card.\n- **closeOnClickOutside**: boolean (default: true) - Outside clicks close the hover card.\n- **directedTransition**: boolean (default: true) - Transition direction follows placement.\n- **transition**: ResponsiveProps<FSOProps> - Popover transition override.\n\n### Styling Props\n- **size**: 'small' | 'normal' | 'large' (default: 'normal') - Controls Popover panel and Card sizing.\n- **density**: 'compact' | 'normal' | 'comfortable' (default: 'normal') - Controls inner Card padding and spacing.\n- **class**: string - Extra classes on the inner Card.\n- **triggerClass**: string - Extra classes on the trigger wrapper.\n- **popover**: Props forwarded to the transparent Popover panel as one object - `{ class, theme }`.\n- **card**: Props forwarded to the inner Card as one object - `{ color, variant, theme }` (defaults: color 'neutral', variant 'solid').\n- **showBorders**: boolean (default: false) - Card section borders.\n- **theme**: HoverCardThemeProps - Theme overrides for HoverCard wrapper parts.\n\n### Callbacks\n- **onOpenChange**: (open: boolean) => void - Called once for each library-requested state change.\n- **onAfterOpen**: (payload: HoverCardPayload) => void - Called after the open transition finishes.\n- **onAfterClose**: (payload: HoverCardPayload) => void - Called after the close transition finishes.\n\n## Examples\n\n### Delays\n```svelte\n<HoverCard delay={300} closeDelay={200} trigger=\"Hover\">\n\tContent\n</HoverCard>\n```\n\n### Custom Trigger\n```svelte\n<HoverCard position=\"right\">\n\t{#snippet trigger(hoverCard)}\n\t\t<button aria-expanded={hoverCard.isOpen}>Preview</button>\n\t{/snippet}\n\n\tPreview content\n</HoverCard>\n```\n\n## Accessibility\n\n- Opens on pointer hover and keyboard focus by default.\n- Escape and outside click dismissal are delegated to Popover.\n- Button triggers receive aria-haspopup, aria-expanded, and aria-controls.\n- HoverCard is best for supplemental previews; primary content should remain reachable without hover.\n\n## Motion\n\n- **motion** theme slot: one preset (no variants) — a small lift plus scale on `fast` / `enter`.\n- Resolved by HoverCard and handed to the underlying Popover, replacing the popover preset.\n- Ladder: `<Theme components={{ 'hover-card': { motion } }}>` → `setHoverCardTheme({ motion })`\n → `theme.motion` → the `transition` prop. Reduced motion collapses it to 0.\n";
110
110
  readonly 'link-preview': "\n# LinkPreview Component\n\nLinkPreview renders an anchor trigger with a HoverCard preview that loads link metadata asynchronously. It shows Skeleton placeholders while loading and displays title, description, site name, Open Graph image, and favicon when available.\n\n## Import\n\n```svelte\n<script>\n\timport { LinkPreview } from 'entasis/link-preview';\n</script>\n```\n\n## Basic Usage\n\n```svelte\n<LinkPreview href=\"https://svelte.dev\">Svelte</LinkPreview>\n```\n\nBy default, LinkPreview requests `/api/link-metadata?url=<href>` when the card opens. Browser-only fetching of arbitrary links is not reliable because most sites block cross-origin HTML reads, so applications should provide a server endpoint or a custom `fetchMetadata` function.\n\n## With Preloaded Metadata\n\n```svelte\n<LinkPreview\n\thref=\"https://entasis.dev\"\n\tmetadata={{\n\t\ttitle: 'Entasis',\n\t\tdescription: 'Configuration-first Svelte components.',\n\t\tsiteName: 'Entasis',\n\t\tfavicon: '/favicon.png'\n\t}}\n>\n\tEntasis\n</LinkPreview>\n```\n\n## Custom Fetcher\n\n```svelte\n<script>\n\tconst fetchMetadata = async (href, signal) => {\n\t\tconst response = await fetch(`/api/preview?href=${encodeURIComponent(href)}`, { signal });\n\t\tif (!response.ok) throw new Error('Preview unavailable');\n\t\treturn response.json();\n\t};\n</script>\n\n<LinkPreview href=\"https://example.com\" {fetchMetadata}>Example</LinkPreview>\n```\n\n## Props\n\n- **href**: string - URL opened by the trigger link and requested by the metadata loader.\n- **id**: string - Stable DOM id for the underlying HoverCard; falls back to a generated id.\n- **open**: boolean - Bindable open state.\n- **defaultOpen**: boolean (default: false) - Initial state when open is not provided.\n- **children**: string | Snippet<[LinkPreviewPayload]> - Trigger anchor content.\n- **metadata**: LinkPreviewMetadata - Preloaded metadata; skips network loading.\n- **fetchMetadata**: (href, signal) => Promise<LinkPreviewMetadata> - Custom async loader.\n- **metadataEndpoint**: string | (href) => string - Endpoint used when fetchMetadata is not provided. String endpoints receive ?url=<href>.\n- **prefetch**: boolean - Load metadata on mount instead of waiting for open.\n- **target**: string - Trigger anchor target.\n- **rel**: string - Trigger anchor rel. Defaults to noopener noreferrer for target=\"_blank\".\n- **fallbackTitle**: string - Title shown when metadata has no title.\n- **imageAlt**: string - Alt text for the preview image.\n- **showUrl**: boolean - Whether to show the URL line.\n- **loadingLabel**: string - Accessible label for the loading region.\n- **errorLabel**: string - Heading shown when metadata loading fails.\n- **position**: Popover placement - Preferred card placement.\n- **offset**: number - Gap between trigger and card.\n- **delay**: number - Open delay in milliseconds.\n- **closeDelay**: number - Close delay in milliseconds.\n- **openOnFocus**: boolean - Open when focus enters trigger or card.\n- **openOnClick**: boolean - Toggle card on click before navigation.\n- **closeOnEscape**: boolean - Close on Escape.\n- **closeOnClickOutside**: boolean - Close when clicking outside.\n- **directedTransition**: boolean - Use placement-aware transitions.\n- **transition**: object - Popover transition overrides.\n- **size**: 'small' | 'normal' | 'large' - Preview card size.\n- **disabled**: boolean - Disable opening and link navigation.\n- **class**: string - Trigger anchor classes.\n- **card**: Props forwarded to the inner Card as one object - `{ class, color, variant, theme }` (defaults: color 'neutral', variant 'solid').\n- **popover**: Props forwarded to the Popover panel as one object - `{ class, theme }`.\n- **showBorders**: boolean - Show Card section borders.\n- **onOpenChange**: (open: boolean) => void - Called once for each library-requested state change.\n- **onAfterOpen**: (payload) => void - Called after open transition.\n- **onAfterClose**: (payload) => void - Called after close transition.\n- **onLoad**: (payload) => void - Called after metadata loads.\n- **onError**: (error) => void - Called after metadata loading fails.\n- **theme**: LinkPreviewThemeProps - LinkPreview theme overrides.\n- **hoverCardTheme**: HoverCardThemeProps - HoverCard wrapper theme overrides.\n\n## Metadata Shape\n\n```ts\ntype LinkPreviewMetadata = {\n\turl?: string;\n\ttitle?: string;\n\tdescription?: string;\n\tsiteName?: string;\n\timage?: string;\n\tfavicon?: string;\n};\n```\n\n## Endpoint Contract\n\nThe default endpoint should return JSON matching LinkPreviewMetadata. Non-2xx responses should return a JSON object with a `message` string when possible.\n\n## Accessibility\n\n- The trigger remains a real anchor, so the destination is reachable without hover.\n- Loading and error states use role=\"status\".\n- A disabled LinkPreview removes the anchor href and prevents hover opening.\n\n## Theme Parts\n\n- **trigger** - Anchor trigger.\n- **card** - HoverCard surface classes.\n- **content** - Preview content wrapper.\n- **media** - Image container.\n- **image** - Preview image.\n- **body** - Metadata text stack.\n- **header** - Favicon and site row.\n- **favicon** - Favicon image.\n- **site** - Site name text.\n- **title** - Preview title.\n- **description** - Preview description.\n- **url** - URL display line.\n- **loading** - Skeleton stack.\n- **error** - Error state container.\n\n## Motion\n\n- LinkPreview has no preset of its own: it forwards `transition` (now a plain `FSOProps`,\n responsive) to HoverCard, whose **motion** slot owns the preset.\n- Retune it with `<Theme components={{ 'hover-card': { motion } }}>` or\n `setHoverCardTheme({ motion })`; the `transition` prop still wins per instance.\n";
111
111
  readonly overlay: "\n# Overlay Component\n\nOverlay layers concise content and actions over bounded media or another visual surface. Place it as the direct first child of a container; the component automatically establishes that parent as its positioning context, so no attachment or wrapper component is required.\n\n## Basic Usage\n\n```svelte\n<script>\n\timport { Overlay } from 'entasis/overlay';\n</script>\n\n<div class=\"aspect-video overflow-hidden rounded-lg\">\n\t<Overlay\n\t\ttitle=\"Design system foundations\"\n\t\tdescription=\"A practical tour of tokens, primitives, and composition.\"\n\t\tactions={[{ content: 'Open gallery', color: 'neutral', variant: 'soft' }]}\n\t/>\n\t<img src=\"/cover.jpg\" alt=\"Coastal landscape\" class=\"size-full object-cover\" />\n</div>\n```\n\n## Props\n\n- **position**: 'fill' | 'top' | 'bottom' (default: 'fill') - Places the content vertically. Fill uses a surface-wide dark scrim; top and bottom use content-sized black-to-transparent gradients.\n- **align**: 'start' | 'center' | 'end' (default: 'center') - Horizontal content and text alignment.\n- **showOn**: 'always' | 'hover' | 'focus' (default: 'always') - Reveal condition. Hover also reveals for focus-within so actions remain keyboard accessible.\n- **open**: boolean (default: true) - Enables or hides the overlay while preserving its reveal transition.\n- **defaultOpen**: boolean (default: true) - Initial state when open is not provided.\n- **onOpenChange**: (open: boolean) => void - Reserved for library-requested state changes.\n- **onAfterOpen**: () => void - Called after the open transition finishes.\n- **onAfterClose**: () => void - Called after the close transition finishes.\n- **scrim**: boolean (default: true) - Toggles the dark fill or directional gradient.\n- **size**: 'small' | 'normal' | 'large' (default: 'normal') - Scales padding, gaps, and typography.\n- **actions**: OverlayAction[] - Button props plus a content string, rendered as a wrapping action row.\n- **ref**: HTMLDivElement | null - Bindable root reference.\n- **class**: string - Additional root classes.\n- **theme**: OverlayThemeProps - Theme overrides.\n\n## Slots\n\n- **title**: Main overlay heading.\n- **description**: Supporting text.\n- **content**: Additional body content between the description and actions.\n- **children**: Fully custom composition replacing title, description, content, and actions.\n\nAll named content slots accept a string or snippet.\n\n## Placement\n\nThe overlay must be the direct first child of the surface it covers:\n\n```svelte\n<div class=\"overflow-hidden rounded-lg\">\n\t<Overlay position=\"bottom\" title=\"Golden hour\" />\n\t<img src=\"/photo.jpg\" alt=\"Golden hour over a valley\" />\n</div>\n```\n\nThe parent receives a zero-specificity relative positioning context and isolated stacking context through CSS `:has()`. An explicit parent positioning utility or inline style still takes precedence.\n\nTop and bottom content enters from its corresponding edge while the scrim fades in place. Fill content only fades. Motion is disabled when the user requests reduced motion.\n\n## Reveal on Hover or Focus\n\n```svelte\n<div class=\"aspect-video overflow-hidden rounded-lg\">\n\t<Overlay\n\t\tshowOn=\"hover\"\n\t\tposition=\"bottom\"\n\t\ttitle=\"Mountain archive\"\n\t\tactions={[{ content: 'View collection', color: 'neutral', variant: 'soft' }]}\n\t/>\n\t<img src=\"/mountain.jpg\" alt=\"Snow-covered mountain\" />\n</div>\n```\n\n## Accessibility\n\n- The overlay is semantically neutral; supplied buttons and links keep their native semantics.\n- Hover-revealed content also appears when focus enters the surface or its actions.\n- Setting open to false makes the overlay inert and hides it from assistive technology.\n- Images and video beneath the overlay still require their own accessible labels or alternatives.\n\n## Theme Parts\n\n- **root**: Absolute overlay, reveal state, vertical placement, and clipping.\n- **scrim**: Fill or directional gradient.\n- **content**: Padding and horizontal alignment.\n- **header**: Title and description stack.\n- **title**: Heading typography.\n- **description**: Supporting text.\n- **body**: Additional content slot.\n- **actions**: Button row.\n";
112
- readonly popover: "\n# Popover Component\n\nThe Popover component displays floating content positioned relative to a trigger element. It's ideal for tooltips, dropdown menus, and contextual information.\n\n## Basic Usage\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n\t\t\n</script>\n// By default Popover comes with a button that triggers them, no need to define a callback and a $state\n<Popover trigger={{ content: \"Toggle Popover\" }}>\n\tPopover content here\n</Popover>\n```\n\n## Props\n\n### Core Props\n- **open**: boolean (bindable) - Controls popover visibility (optional when using trigger prop)\n- **defaultOpen**: boolean (default: false) - Initial state when open is not provided\n- **ref**: HTMLElement | null - Reference element to position popover against (optional when using trigger prop)\n- **id**: string - Unique identifier\n\n### Layout Props\n- **position**: 'top' | 'bottom' | 'left' | 'right' | 'top-start' | 'top-end' | 'bottom-start' | 'bottom-end' | 'left-start' | 'left-end' | 'right-start' | 'right-end' (default: 'bottom')\n - Determines where popover appears relative to trigger\n- **offset**: number - Distance in pixels from the reference element\n- **fitTrigger**: boolean (default: false) - Whether popover should match the width of the trigger element\n- **positionPanel**: ({ panel, reference }) => { x: number; y: number; minWidth?: number } | null - Place the panel yourself instead of floating-ui: return its viewport coordinates (the panel is `position: fixed`) and optionally a `minWidth` in px that replaces `fitTrigger`'s, or `null` to fall back to `position`. Called when the panel mounts and whenever floating-ui would reposition it. Select uses it to open over its trigger\n- **mobileSheet**: boolean (default: false) - On mobile viewports (<768px), render as a bottom sheet instead of an anchored floating panel\n- **inline**: boolean (default: false) - Render the panel in normal document flow where the component sits instead of portaling to the viewport-fixed layer: no floating-ui positioning, no scroll lock, no outside-press dismissal (Escape still closes it). Same panel classes and motion, and the trigger still toggles it. Wins over `mobileSheet`\n- **mobileSheetSizeTransition**: boolean (default: true) - Whether mobile-sheet panels animate intrinsic size changes\n\n### Event Props\n- **onOpenChange**: (open: boolean) => void - Called once for each library-requested state change\n- **onAfterOpen**: (payload: PopoverState) => void - Called after the open transition finishes\n- **onAfterClose**: (payload: PopoverState) => void - Called after the close transition finishes\n\n### Slot Props\n- **children**: Snippet<[PopoverState]> - Popover content\n- **trigger**: Snippet<[PopoverState]> | (ButtonProps & { content?: string }) | false - Trigger element\n - Pass a snippet function for custom trigger: `{#snippet trigger(popover)}...</snippet>`\n - Pass button props object for default button: `trigger={{ content: \"Click Me\", color: \"primary\" }}`\n - Pass `false` to disable trigger (use with external ref)\n\n### Interaction Props\n- **openOnHover**: boolean (default: false) - Open on mouse hover\n- **openOnClick**: boolean (default: true) - Open on click\n- **delay**: number (default: 100) - Delay in ms before opening on hover\n- **closeOnEscape**: boolean (default: true) - Close on Escape. Only the topmost open layer closes, so Escape inside a nested popover leaves its parent open\n- **closeOnClickOutside**: boolean (default: true) - Close on an outside press. A press dismisses this popover and every layer stacked above it, but never the layer that was pressed\n- **closeOnMouseLeave**: boolean (default: false) - Close when the pointer leaves the hover safe area. The safe area is the trigger, the panel, and a prediction cone toward the panel, so a diagonal move to the panel keeps it open.\n- **debugSafeArea**: boolean (default: false) - Show hover safe-area overlays. Trigger/panel rectangles render in blue; the prediction cone toward the panel renders in orange.\n\n### Focus & ARIA Props\n- **focusOnOpen**: 'first' | 'container' | false (default: false) - Where focus goes when the panel opens: `'first'` moves it to an `[autofocus]` / `[data-autofocus]` target or the first tabbable control, `'container'` focuses the panel itself, `false` keeps it on the trigger. Focus always returns to the trigger when the popover closes (Escape or outside press)\n- **haspopup**: 'dialog' | 'menu' | 'listbox' | 'tree' | 'grid' | true (default: 'dialog') - Value of `aria-haspopup` on the trigger, describing what the panel contains. `aria-expanded` and `aria-controls` are managed automatically alongside it, for the built-in Button trigger and for a snippet trigger using `{@attach popover.reference}`\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n</script>\n\n<Popover focusOnOpen=\"first\" haspopup=\"listbox\" trigger={{ content: 'Pick one' }}>\n\t<ul role=\"listbox\"><li role=\"option\" tabindex=\"0\">First</li></ul>\n</Popover>\n```\n\n### Visual Props\n- **size**: 'small' | 'normal' | 'large' (default: 'normal')\n - small: Compact popover size\n - normal: Standard popover size\n - large: Larger popover size\n- **transition**: TransitionConfig - Custom transition animation\n- **directedTransition**: boolean (default: true) - Transition direction follows position\n\n### Behavior Props\n- **lockScroll**: boolean (default: true) - Lock body scroll when open\n\n### Styling Props\n- **class**: string - Additional CSS classes\n- **theme**: ComponentTheme - Custom theme overrides\n\n## Structure\n\n```\n<PopoverTrigger>\n\t<Trigger />\n</PopoverTrigger>\n\n<PopoverContent>\n\t<Children />\n</PopoverContent>\n```\n\n## Examples\n\n### More Examples\n\n### With a custom trigger snippet\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n\timport { Button } from 'entasis/button';\n</script>\n\n<!-- {@attach popover.reference} anchors the panel to the element and keeps\n aria-haspopup / aria-expanded / aria-controls in sync on it -->\n<Popover>\n\t{#snippet trigger(popover)}\n\t\t<Button onclick={() => popover.toggle()} {@attach popover.reference}>Open</Button>\n\t{/snippet}\n\t\n\t<p>This is a popover!</p>\n</Popover>\n```\n\n### With button props\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n</script>\n\n<Popover \n\ttrigger={{\n\t\tcontent: \"Click Me\",\n\t\tcolor: \"secondary\",\n\t\tsize: \"small\"\n\t}}\n>\n\t<p>This is a popover!</p>\n</Popover>\n```\n\n### Different Positions\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n</script>\n\n<!-- Top -->\n<Popover position=\"top\" trigger={{ content: \"Top\" }}>\n\tTop popover\n</Popover>\n\n<!-- Bottom -->\n<Popover position=\"bottom\" trigger={{ content: \"Bottom\" }}>\n\tBottom popover\n</Popover>\n\n<!-- Left -->\n<Popover position=\"left\" trigger={{ content: \"Left\" }}>\n\tLeft popover\n</Popover>\n\n<!-- Right -->\n<Popover position=\"right\" trigger={{ content: \"Right\" }}>\n\tRight popover\n</Popover>\n```\n\n### Advanced Example\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n\timport { Button } from 'entasis/button';\n</script>\n\n<Popover position=\"bottom-start\" trigger={{ content: \"Menu\" }}>\n\t<div class=\"flex flex-col gap-1\">\n\t\t<Button variant=\"ghost\" fullWidth>Profile</Button>\n\t\t<Button variant=\"ghost\" fullWidth>Settings</Button>\n\t\t<Button variant=\"ghost\" fullWidth>Logout</Button>\n\t</div>\n</Popover>\n```\n\n### Open on Hover\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n</script>\n\n<Popover \n\ttrigger={{ content: \"Hover Me\" }}\n\topenOnHover\n\topenOnClick={false}\n\tdelay={200}\n>\n\tHover content\n</Popover>\n```\n\n### With Custom Offset\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n</script>\n\n<Popover \n\ttrigger={{ content: \"Trigger\" }}\n\tposition=\"bottom\"\n\toffset={20}\n>\n\t20px away from trigger\n</Popover>\n```\n\n### Fit Trigger Width\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n</script>\n\n<Popover \n\ttrigger={{ content: \"Click Me\" }}\n\tfitTrigger\n>\n\tPopover matches trigger width\n</Popover>\n```\n\n### Inline (static, in flow)\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n</script>\n\n<!-- Open in place, no portal: useful for docs, visual tests, or an always-visible panel -->\n<Popover inline open trigger={false}>\n\t<p>Rendered where the component sits.</p>\n</Popover>\n\n<!-- The trigger still toggles an inline panel -->\n<Popover inline trigger={{ content: 'Toggle' }}>\n\t<p>Expands below the trigger, in the flow.</p>\n</Popover>\n```\n\n### Mobile Bottom Sheet\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n</script>\n\n<Popover\n\ttrigger={{ content: \"Open filters\" }}\n\tposition=\"bottom\"\n\tmobileSheet\n>\n\tFilters content\n</Popover>\n```\n\n### Different Sizes\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n</script>\n\n<!-- Small -->\n<Popover size=\"small\" trigger={{ content: \"Small\" }}>\n\tSmall popover\n</Popover>\n\n<!-- Large -->\n<Popover size=\"large\" trigger={{ content: \"Large\" }}>\n\tLarge popover with more content\n</Popover>\n```\n\n### Close on Mouse Leave\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n</script>\n\n<Popover \n\ttrigger={{ content: \"Hover Me\" }}\n\topenOnHover\n\tcloseOnMouseLeave\n>\n\tCloses when you move outside the rectangle tolerance\n</Popover>\n```\n\n### With Lifecycle Hooks\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n</script>\n\n<Popover \n\ttrigger={{ content: \"Trigger\" }}\n\tonAfterOpen={(payload) => console.log('Popover opened', payload)}\n\tonAfterClose={(payload) => console.log('Popover closed', payload)}\n>\n\tWatch the console\n</Popover>\n```\n\n### User Card Popover with External Ref\n\n```svelte\n<script lang=\"ts\">\n\timport { Popover } from 'entasis/popover';\n\timport { Button } from 'entasis/button';\n\timport { Avatar } from 'entasis/avatar';\n\n\tlet avatarRef = $state<HTMLElement | null>(null);\n\tlet open = $state(false);\n\tconst user = { name: 'John Doe', email: 'john@example.com' };\n</script>\n\n<button type=\"button\" bind:this={avatarRef} onclick={() => (open = !open)}>\n\t<Avatar name={user.name} />\n</button>\n\n<Popover bind:open ref={avatarRef} position=\"bottom\">\n\t<div class=\"p-4\">\n\t\t<h3>{user.name}</h3>\n\t\t<p>{user.email}</p>\n\t\t<Button fullWidth>View Profile</Button>\n\t</div>\n</Popover>\n```\n\n## State Management\n\nThe Popover component uses a `PopoverState` instance that is passed to all slot snippets. This state object provides:\n\n- **id**: string - Popover identifier\n- **size**: Size - Current popover size\n- **position**: Placement - Current popover position\n- **offset**: number - Current offset value\n- **open()**: () => void - Method to open the popover\n- **close()**: () => void - Method to close the popover\n- **toggle()**: () => void - Method to toggle the popover\n- **reference**: attachment for a custom trigger element (`{@attach popover.reference}`); anchors the panel to it and keeps its `aria-haspopup`, `aria-expanded`, and `aria-controls` in sync\n\n## Accessibility\n\n- The trigger carries `aria-haspopup` (from `haspopup`), `aria-expanded`, and `aria-controls` — automatically for the built-in Button and for a snippet trigger using `{@attach popover.reference}`\n- `focusOnOpen` decides where focus lands on open; focus returns to the trigger on close, whether by Escape or an outside press\n- Non-modal: Tab is not contained and the page is not made inert (use Dialog for that)\n- Escape closes only the topmost open layer; an outside press dismisses every layer stacked above the one pressed. Popovers, menus, and dialogs share one layer stack\n- Keyboard navigation support\n\n## Notes\n\n- Popover is positioned using floating-ui, except with `inline`, where the document lays the panel out\n- Automatically adjusts position to stay in viewport\n- Multiple popovers can be stacked\n- Scroll locking prevents background scroll (when enabled)\n- Transitions animate based on position direction\n\n## Theme Customization\n\nThe Popover component uses a theme object that can be customized using the `theme` prop or by setting a global theme.\n\n### Theme Structure\n\nThe theme object contains the following parts:\n- **motion**: Open/close transition preset, keyed by `mode` (a floating panel scales, the mobile\n sheet slides up). Takes `in` / `out` FSO params plus a `duration` / `easing` motion token;\n the `transition` prop wins over it\n- **popover**: Main popover container styles\n\n### Theme Type Definition\n\n```typescript\nimport type { PopoverThemeProps } from 'entasis/popover';\n\n// Example theme customization\nconst customTheme: PopoverThemeProps = {\n popover: {\n base: 'z-[+50] fixed bg-surface-floating text-neutral w-fit rounded-xl raised isolate h-fit',\n size: {\n small: 'max-w-3xs w-full p-2',\n normal: 'max-w-xs w-full p-3',\n large: 'max-w-sm w-full p-4'\n }\n }\n};\n```\n\n### Available Variants\n\n**popover**:\n- base: Base classes applied to all popovers\n- Variants:\n - size: 'small' | 'normal' | 'large' - Controls max-width, width, and padding\n - mode: 'floating' | 'inline' | 'mobileSheet' - Set from `inline` / `mobileSheet`; `root` positions the wrapper (fixed, in flow, or full-screen sheet)\n\n### Usage Examples\n\n**Basic Theme Override**:\n```svelte\n<Popover \n trigger={{ content: \"Click Me\" }}\n theme={{\n popover: {\n base: 'rounded-xl lift-5 border-2 border-primary',\n size: {\n normal: 'max-w-md p-4'\n }\n }\n }}\n>\n Custom styled popover content\n</Popover>\n```\n\n**Size Customization**:\n```svelte\n<Popover \n size=\"large\"\n trigger={{ content: \"Large Popover\" }}\n theme={{\n popover: {\n size: {\n large: 'max-w-lg p-6'\n }\n }\n }}\n>\n Large popover with more padding\n</Popover>\n```\n\n**Global Theme Setting**:\n```svelte\n<script>\n import { setPopoverTheme } from '../components/Popover/index.ts';\n \n setPopoverTheme({\n popover: {\n base: 'rounded-lg lift-5 backdrop-blur-sm bg-white/95',\n size: {\n normal: 'max-w-sm p-4'\n }\n }\n });\n</script>\n```\n";
113
- readonly tooltip: "\n# Tooltip\n\nContextual information shown on hover or keyboard focus. Two forms share one surface:\n\n- `<Tooltip>` — a component with a `trigger` prop, like every other overlay.\n- `tooltip()` — the underlying attachment, for elements you already render yourself.\n\nBoth are rendered by the single tooltip surface that `<Theme>` mounts (`TooltipHost`).\n\n## Basic Usage\n\n```svelte\n<script lang=\"ts\">\n\timport { Tooltip } from 'entasis/tooltip';\n</script>\n\n<Tooltip content=\"Click to submit\" trigger={{ content: 'Submit', variant: 'outline' }} />\n```\n\n## Props\n\n- **content**: string | Snippet (required) - Tooltip body\n- **trigger**: Snippet<[Attachment<HTMLElement>]> | ButtonProps & { content?: string } (required) -\n A snippet receives the tooltip attachment and spreads it on its own element; Button props render\n a Button carrying it\n- **open**: boolean (default: false) - Shows the tooltip without hover or focus; bindable\n- **defaultOpen**: boolean (default: false) - Initial open state when `open` is not provided\n- **onOpenChange**: (open: boolean) => void - Called whenever the tooltip becomes visible or hidden,\n hover and focus included\n- **position**: Placement (default: 'top') - Tooltip position relative to the trigger\n - Options: 'top' | 'bottom' | 'left' | 'right' | 'top-start' | 'top-end' | 'bottom-start' | 'bottom-end' | 'left-start' | 'left-end' | 'right-start' | 'right-end'\n- **size**: 'small' | 'normal' | 'large' (default: 'normal') - Visual size\n- **color**: Colors (default: 'neutral') - Color theme\n- **variant**: 'solid' | 'outline' | 'soft' (default: 'solid') - Visual style matching Chip\n- **delay**: number (default: 400) - Delay in ms before showing tooltip; zero shows immediately\n- **offset**: number - Distance from the trigger in pixels\n- **class**: string - Additional CSS classes\n- **transition**: FSOProps - Custom transition configuration\n- **theme**: TooltipThemeProps - Per-instance theme overrides\n- **onAfterOpen**: () => void - Callback after the opening transition completes\n- **onAfterClose**: () => void - Callback after the closing transition completes\n\nEvery prop except `trigger`, `open`, `defaultOpen` and `onOpenChange` is also an option of the\n`tooltip()` attachment.\n\n## Examples\n\n### Button Trigger\n```svelte\n<script lang=\"ts\">\n\timport { Tooltip } from 'entasis/tooltip';\n</script>\n\n<Tooltip\n\tcontent=\"This is helpful information\"\n\ttrigger={{ content: 'Hover me', variant: 'outline', color: 'neutral' }}\n/>\n```\n\n### Snippet Trigger\n```svelte\n<script lang=\"ts\">\n\timport type { Attachment } from 'svelte/attachments';\n\timport { Tooltip } from 'entasis/tooltip';\n</script>\n\n{#snippet helpTrigger(attach: Attachment<HTMLElement>)}\n\t<span class=\"underline\" {@attach attach}>What is this?</span>\n{/snippet}\n\n<Tooltip content=\"Anchored to any element you like\" trigger={helpTrigger} />\n```\n\n### Forced Open\n```svelte\n<script lang=\"ts\">\n\timport { Tooltip } from 'entasis/tooltip';\n</script>\n\n<!-- Useful for docs, screenshots and visual tests -->\n<Tooltip open content=\"Always visible\" trigger={{ content: 'Anchor', variant: 'outline' }} />\n```\n\n### Different Positions\n```svelte\n<script lang=\"ts\">\n\timport { Tooltip } from 'entasis/tooltip';\n</script>\n\n<Tooltip content=\"Top tooltip\" position=\"top\" trigger={{ content: 'Top' }} />\n<Tooltip content=\"Bottom tooltip\" position=\"bottom\" trigger={{ content: 'Bottom' }} />\n<Tooltip content=\"Left tooltip\" position=\"left\" trigger={{ content: 'Left' }} />\n<Tooltip content=\"Right tooltip\" position=\"right\" trigger={{ content: 'Right' }} />\n```\n\n### Different Colors\n```svelte\n<script lang=\"ts\">\n\timport { Tooltip } from 'entasis/tooltip';\n</script>\n\n<Tooltip content=\"Success!\" color=\"success\" trigger={{ content: 'Success' }} />\n<Tooltip content=\"Warning!\" color=\"warning\" trigger={{ content: 'Warning' }} />\n<Tooltip content=\"Error!\" color=\"danger\" trigger={{ content: 'Error' }} />\n```\n\n### Custom Delay\n```svelte\n<script lang=\"ts\">\n\timport { Tooltip } from 'entasis/tooltip';\n</script>\n\n<Tooltip content=\"Quick tooltip\" delay={100} trigger={{ content: 'Quick (100ms)' }} />\n<Tooltip content=\"Slow tooltip\" delay={1000} trigger={{ content: 'Slow (1000ms)' }} />\n```\n\n### Different Sizes and Variants\n```svelte\n<script lang=\"ts\">\n\timport { Tooltip } from 'entasis/tooltip';\n</script>\n\n<Tooltip content=\"Small tooltip\" size=\"small\" trigger={{ content: 'Small' }} />\n<Tooltip content=\"Large tooltip\" size=\"large\" trigger={{ content: 'Large' }} />\n<Tooltip content=\"Outlined tooltip\" variant=\"outline\" trigger={{ content: 'Outline' }} />\n<Tooltip content=\"Soft tooltip\" variant=\"soft\" trigger={{ content: 'Soft' }} />\n```\n\n### With Snippet Content\n```svelte\n<script lang=\"ts\">\n\timport { Tooltip } from 'entasis/tooltip';\n</script>\n\n{#snippet richContent()}\n\t<div class=\"p-2\">\n\t\t<strong>Pro Tip</strong>\n\t\t<p class=\"text-sm\">Use Ctrl+S to save</p>\n\t</div>\n{/snippet}\n\n<Tooltip content={richContent} trigger={{ content: 'Keyboard Shortcuts' }} />\n```\n\n### With Callbacks\n```svelte\n<script lang=\"ts\">\n\timport { Tooltip } from 'entasis/tooltip';\n</script>\n\n<Tooltip\n\tcontent=\"Tracked tooltip\"\n\tonAfterOpen={() => console.log('Tooltip opened')}\n\tonAfterClose={() => console.log('Tooltip closed')}\n\ttrigger={{ content: 'Track me' }}\n/>\n```\n\n## The tooltip() attachment\n\nUse the attachment when the element already exists in your markup — icons, table cells, list rows,\ndisabled wrappers — or inside another component's internals.\n\n```svelte\n<script lang=\"ts\">\n\timport { tooltip } from 'entasis/tooltip';\n</script>\n\n<button {@attach tooltip({ content: 'Click to submit' })}> Submit </button>\n\n<!-- On icons or any non-interactive element -->\n<span {@attach tooltip({ content: 'More information', position: 'right' })}> ⓘ </span>\n\n<!-- Disabled elements do not fire events, so wrap them -->\n<span {@attach tooltip({ content: 'Feature coming soon' })}>\n\t<button disabled>Disabled Button</button>\n</span>\n```\n\nThe `<Tooltip>` component hands this same attachment to a snippet trigger, so the two forms are\ninterchangeable.\n\n## Accessibility\n\n- Shows on pointer hover and on keyboard focus (`focusin` / `focusout` on the trigger element), so\n attach it to focusable elements for keyboard users\n- Dismissed on mouse leave or blur\n- Non-interactive (cannot be clicked)\n- Renders with `role=\"tooltip\"` and sets `aria-describedby` on the trigger while visible (any previous value is restored on hide)\n- Does not block content behind it\n\n## Notes\n\n- Only one tooltip shows at a time; an `open` tooltip hands the surface over when another tooltip is\n hovered and reports that through `onOpenChange`\n- Automatically positions to stay in viewport using Floating UI\n- Uses smart delay: subsequent tooltips show instantly if within 400ms of previous\n- Brief content only (use Popover for interactive content)\n- The surface is a singleton (`TooltipHost`) rendered by `<Theme>`\n- Does not lock scroll or trap focus\n\n## Theme Customization\n\nThe Tooltip uses a theme object that can be customized using the `theme` prop or by setting a global theme.\n\n### Theme Structure\n\nThe theme object contains the following parts:\n- **root**: Main tooltip container styles\n\n### Theme Type Definition\n\n```typescript\nimport type { TooltipThemeProps } from 'entasis/tooltip';\n\n// Example theme customization\nconst customTheme: TooltipThemeProps = {\n root: {\n base: 'inline-flex w-fit items-center rounded-full border font-medium',\n size: {\n small: 'h-5 px-2 text-xs',\n normal: 'h-6 px-2.5 text-xs',\n large: 'h-7 px-3 text-sm'\n },\n\tcolor: {\n\t neutral: 'bg-neutral text-neutral-contrast',\n primary: 'bg-primary text-primary-contrast',\n danger: 'bg-danger text-danger-contrast',\n success: 'bg-success text-success-contrast',\n warning: 'bg-warning text-warning-contrast',\n\t info: 'bg-info text-info-contrast'\n\t},\n\tvariant: {\n\t solid: 'bg-color text-color-contrast',\n\t outline: 'border-color bg-transparent text-color-readable',\n\t soft: 'bg-color-muted text-color-muted-readable'\n }\n }\n};\n```\n\n### Available Variants\n\n**root**:\n- base: Base classes applied to all tooltips\n- Variants:\n - size: 'small' | 'normal' | 'large' - Controls text size and padding\n - color: 'primary' | 'secondary' | 'neutral' | 'danger' | 'success' | 'warning' | 'info' - Color scheme\n - variant: 'solid' | 'outline' | 'soft' - Matches Chip's visual variants\n\n### Usage Examples\n\n**Basic Theme Override**:\n```svelte\n<script lang=\"ts\">\n\timport { Tooltip } from 'entasis/tooltip';\n</script>\n\n<Tooltip\n\tcontent=\"Custom tooltip\"\n\ttheme={{ root: { base: 'rounded-lg lift-4 border-2', size: { normal: 'px-3 py-2 text-sm' } } }}\n\ttrigger={{ content: 'Hover me' }}\n/>\n```\n\n**Color Customization**:\n```svelte\n<script lang=\"ts\">\n\timport { tooltip } from 'entasis/tooltip';\n</script>\n\n<button\n\t{@attach tooltip({\n\t\tcontent: 'Success!',\n\t\tcolor: 'success',\n\t\ttheme: { root: { color: { success: 'bg-green-500 text-white lift-3' } } }\n\t})}\n>\n\tSuccess Tooltip\n</button>\n```\n\n**Global Theme Setting**:\n```svelte\n<script lang=\"ts\">\n\timport { setTooltipTheme } from 'entasis/tooltip';\n\n\tsetTooltipTheme({\n\t\troot: {\n\t\t\tbase: 'rounded-md lift-4 backdrop-blur-sm',\n\t\t\tsize: { normal: 'px-3 py-1.5 text-sm' },\n\t\t\tcolor: { neutral: 'bg-gray-900 text-white', primary: 'bg-blue-500 text-white' }\n\t\t}\n\t});\n</script>\n```\n\n## Motion\n\n- **motion** theme slot: one preset (no variants) — a short fade-and-rise, `duration: 'fast'`.\n- Resolved by the tooltip surface and handed to the underlying Popover, so it replaces the\n popover preset.\n- Ladder: `<Theme components={{ tooltip: { motion } }}>` → `setTooltipTheme({ motion })` →\n `theme.motion` → the tooltip's `transition` option. Reduced motion collapses it to 0.\n- The tooltip surface is a singleton rendered by `<Theme>`, so a `setTooltipTheme` call\n made *below* `<Theme>` never reaches it. Call it at or above the `<Theme>` boundary, or\n use the `<Theme components={{ tooltip }}>` registry, which always applies.\n";
112
+ readonly popover: "\n# Popover Component\n\nThe Popover component displays floating content positioned relative to a trigger element. It's ideal for tooltips, dropdown menus, and contextual information.\n\n## Basic Usage\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n\t\t\n</script>\n// By default Popover comes with a button that triggers them, no need to define a callback and a $state\n<Popover trigger={{ content: \"Toggle Popover\" }}>\n\tPopover content here\n</Popover>\n```\n\n## Props\n\n### Core Props\n- **open**: boolean (bindable) - Controls popover visibility (optional when using trigger prop)\n- **defaultOpen**: boolean (default: false) - Initial state when open is not provided\n- **ref**: HTMLElement | null - Reference element to position popover against (optional when using trigger prop)\n- **id**: string - Unique identifier\n\n### Layout Props\n- **position**: 'top' | 'bottom' | 'left' | 'right' | 'top-start' | 'top-end' | 'bottom-start' | 'bottom-end' | 'left-start' | 'left-end' | 'right-start' | 'right-end' (default: 'bottom')\n - Determines where popover appears relative to trigger\n- **offset**: number - Distance in pixels from the reference element\n- **fitTrigger**: boolean (default: false) - Whether popover should match the width of the trigger element\n- **positionPanel**: ({ panel, reference }) => { x: number; y: number; minWidth?: number } | null - Place the panel yourself instead of floating-ui: return its viewport coordinates (the panel is `position: fixed`) and optionally a `minWidth` in px that replaces `fitTrigger`'s, or `null` to fall back to `position`. Called when the panel mounts and whenever floating-ui would reposition it. Select uses it to open over its trigger\n- **mobileSheet**: boolean (default: false) - On mobile viewports (<768px), render as a bottom sheet instead of an anchored floating panel\n- **inline**: boolean (default: false) - Render the panel in normal document flow where the component sits instead of portaling to the viewport-fixed layer: no floating-ui positioning, no scroll lock, no outside-press dismissal (Escape still closes it). Same panel classes and motion, and the trigger still toggles it. Wins over `mobileSheet`\n- **mobileSheetSizeTransition**: boolean (default: true) - Whether mobile-sheet panels animate intrinsic size changes\n\n### Event Props\n- **onOpenChange**: (open: boolean) => void - Called once for each library-requested state change\n- **onAfterOpen**: (payload: PopoverState) => void - Called after the open transition finishes\n- **onAfterClose**: (payload: PopoverState) => void - Called after the close transition finishes\n\n### Slot Props\n- **children**: Snippet<[PopoverState]> - Popover content\n- **trigger**: Snippet<[PopoverState]> | (ButtonProps & { content?: string }) | false - Trigger element\n - Pass a snippet function for custom trigger: `{#snippet trigger(popover)}...</snippet>`. Put\n `{@attach popover.trigger}` on any element (a card, an avatar, a Button) and it opens the\n panel on click and from the keyboard, with the ARIA state kept in sync. Type the parameter\n with `PopoverState` from `entasis/popover`.\n - Pass button props object for default button: `trigger={{ content: \"Click Me\", color: \"primary\" }}`.\n `children` (a string or a snippet) replaces `content` for a richer body; `prefix` and\n `suffix` work as on Button.\n - Pass `false` to disable trigger (use with external ref)\n\n### Interaction Props\n- **openOnHover**: boolean (default: false) - Open on mouse hover\n- **openOnClick**: boolean (default: true) - Open on click\n- **delay**: number (default: 100) - Delay in ms before opening on hover\n- **closeOnEscape**: boolean (default: true) - Close on Escape. Only the topmost open layer closes, so Escape inside a nested popover leaves its parent open\n- **closeOnClickOutside**: boolean (default: true) - Close on an outside press. A press dismisses this popover and every layer stacked above it, but never the layer that was pressed\n- **closeOnMouseLeave**: boolean (default: false) - Close when the pointer leaves the hover safe area. The safe area is the trigger, the panel, and a prediction cone toward the panel, so a diagonal move to the panel keeps it open.\n- **debugSafeArea**: boolean (default: false) - Show hover safe-area overlays. Trigger/panel rectangles render in blue; the prediction cone toward the panel renders in orange.\n\n### Focus & ARIA Props\n- **focusOnOpen**: 'first' | 'container' | false (default: false) - Where focus goes when the panel opens: `'first'` moves it to an `[autofocus]` / `[data-autofocus]` target or the first tabbable control, `'container'` focuses the panel itself, `false` keeps it on the trigger. Focus always returns to the trigger when the popover closes (Escape or outside press)\n- **haspopup**: 'dialog' | 'menu' | 'listbox' | 'tree' | 'grid' | true (default: 'dialog') - Value of `aria-haspopup` on the trigger, describing what the panel contains. `aria-expanded` and `aria-controls` are managed automatically alongside it, for the built-in Button trigger and for a snippet trigger using `{@attach popover.reference}`\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n</script>\n\n<Popover focusOnOpen=\"first\" haspopup=\"listbox\" trigger={{ content: 'Pick one' }}>\n\t<ul role=\"listbox\"><li role=\"option\" tabindex=\"0\">First</li></ul>\n</Popover>\n```\n\n### Visual Props\n- **size**: 'small' | 'normal' | 'large' (default: 'normal')\n - small: Compact popover size\n - normal: Standard popover size\n - large: Larger popover size\n- **transition**: TransitionConfig - Custom transition animation\n- **directedTransition**: boolean (default: true) - Transition direction follows position\n\n### Behavior Props\n- **lockScroll**: boolean (default: true) - Lock body scroll when open\n\n### Styling Props\n- **class**: string - Additional CSS classes\n- **theme**: ComponentTheme - Custom theme overrides\n\n## Structure\n\n```\n<PopoverTrigger>\n\t<Trigger />\n</PopoverTrigger>\n\n<PopoverContent>\n\t<Children />\n</PopoverContent>\n```\n\n## Examples\n\n### More Examples\n\n### With a custom trigger snippet\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n\timport { Button } from 'entasis/button';\n</script>\n\n<!-- {@attach popover.trigger} anchors the panel to the element, toggles it on click and keeps\n aria-haspopup / aria-expanded / aria-controls in sync on it -->\n<Popover>\n\t{#snippet trigger(popover)}\n\t\t<Button {@attach popover.trigger}>Open</Button>\n\t{/snippet}\n\t\n\t<p>This is a popover!</p>\n</Popover>\n```\n\nAny element works. One that is not a control gets `role=\"button\"`, `tabindex=\"0\"` and Enter/Space\nactivation:\n\n```svelte\n<script lang=\"ts\">\n\timport { Popover, type PopoverState } from 'entasis/popover';\n\timport { Avatar } from 'entasis/avatar';\n</script>\n\n<Popover>\n\t{#snippet trigger(popover: PopoverState)}\n\t\t<div class=\"flex items-center gap-sm\" {@attach popover.trigger}>\n\t\t\t<Avatar name=\"Ada Lovelace\" />\n\t\t\t<span>Ada Lovelace</span>\n\t\t</div>\n\t{/snippet}\n\n\t<p>Profile details</p>\n</Popover>\n```\n\n### With button props\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n</script>\n\n<Popover \n\ttrigger={{\n\t\tcontent: \"Click Me\",\n\t\tcolor: \"secondary\",\n\t\tsize: \"small\"\n\t}}\n>\n\t<p>This is a popover!</p>\n</Popover>\n```\n\n### Different Positions\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n</script>\n\n<!-- Top -->\n<Popover position=\"top\" trigger={{ content: \"Top\" }}>\n\tTop popover\n</Popover>\n\n<!-- Bottom -->\n<Popover position=\"bottom\" trigger={{ content: \"Bottom\" }}>\n\tBottom popover\n</Popover>\n\n<!-- Left -->\n<Popover position=\"left\" trigger={{ content: \"Left\" }}>\n\tLeft popover\n</Popover>\n\n<!-- Right -->\n<Popover position=\"right\" trigger={{ content: \"Right\" }}>\n\tRight popover\n</Popover>\n```\n\n### Advanced Example\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n\timport { Button } from 'entasis/button';\n</script>\n\n<Popover position=\"bottom-start\" trigger={{ content: \"Menu\" }}>\n\t<div class=\"flex flex-col gap-1\">\n\t\t<Button variant=\"ghost\" fullWidth>Profile</Button>\n\t\t<Button variant=\"ghost\" fullWidth>Settings</Button>\n\t\t<Button variant=\"ghost\" fullWidth>Logout</Button>\n\t</div>\n</Popover>\n```\n\n### Open on Hover\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n</script>\n\n<Popover \n\ttrigger={{ content: \"Hover Me\" }}\n\topenOnHover\n\topenOnClick={false}\n\tdelay={200}\n>\n\tHover content\n</Popover>\n```\n\n### With Custom Offset\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n</script>\n\n<Popover \n\ttrigger={{ content: \"Trigger\" }}\n\tposition=\"bottom\"\n\toffset={20}\n>\n\t20px away from trigger\n</Popover>\n```\n\n### Fit Trigger Width\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n</script>\n\n<Popover \n\ttrigger={{ content: \"Click Me\" }}\n\tfitTrigger\n>\n\tPopover matches trigger width\n</Popover>\n```\n\n### Inline (static, in flow)\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n</script>\n\n<!-- Open in place, no portal: useful for docs, visual tests, or an always-visible panel -->\n<Popover inline open trigger={false}>\n\t<p>Rendered where the component sits.</p>\n</Popover>\n\n<!-- The trigger still toggles an inline panel -->\n<Popover inline trigger={{ content: 'Toggle' }}>\n\t<p>Expands below the trigger, in the flow.</p>\n</Popover>\n```\n\n### Mobile Bottom Sheet\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n</script>\n\n<Popover\n\ttrigger={{ content: \"Open filters\" }}\n\tposition=\"bottom\"\n\tmobileSheet\n>\n\tFilters content\n</Popover>\n```\n\n### Different Sizes\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n</script>\n\n<!-- Small -->\n<Popover size=\"small\" trigger={{ content: \"Small\" }}>\n\tSmall popover\n</Popover>\n\n<!-- Large -->\n<Popover size=\"large\" trigger={{ content: \"Large\" }}>\n\tLarge popover with more content\n</Popover>\n```\n\n### Close on Mouse Leave\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n</script>\n\n<Popover \n\ttrigger={{ content: \"Hover Me\" }}\n\topenOnHover\n\tcloseOnMouseLeave\n>\n\tCloses when you move outside the rectangle tolerance\n</Popover>\n```\n\n### With Lifecycle Hooks\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n</script>\n\n<Popover \n\ttrigger={{ content: \"Trigger\" }}\n\tonAfterOpen={(payload) => console.log('Popover opened', payload)}\n\tonAfterClose={(payload) => console.log('Popover closed', payload)}\n>\n\tWatch the console\n</Popover>\n```\n\n### User Card Popover with External Ref\n\n```svelte\n<script lang=\"ts\">\n\timport { Popover } from 'entasis/popover';\n\timport { Button } from 'entasis/button';\n\timport { Avatar } from 'entasis/avatar';\n\n\tlet avatarRef = $state<HTMLElement | null>(null);\n\tlet open = $state(false);\n\tconst user = { name: 'John Doe', email: 'john@example.com' };\n</script>\n\n<button type=\"button\" bind:this={avatarRef} onclick={() => (open = !open)}>\n\t<Avatar name={user.name} />\n</button>\n\n<Popover bind:open ref={avatarRef} position=\"bottom\">\n\t<div class=\"p-4\">\n\t\t<h3>{user.name}</h3>\n\t\t<p>{user.email}</p>\n\t\t<Button fullWidth>View Profile</Button>\n\t</div>\n</Popover>\n```\n\n## State Management\n\nThe Popover component uses a `PopoverState` instance that is passed to all slot snippets. This state object provides:\n\n- **id**: string - Popover identifier\n- **size**: Size - Current popover size\n- **position**: Placement - Current popover position\n- **offset**: number - Current offset value\n- **open()**: () => void - Method to open the popover\n- **close()**: () => void - Method to close the popover\n- **toggle()**: () => void - Method to toggle the popover\n- **trigger**: attachment that makes any element the trigger (`{@attach popover.trigger}`): `reference` plus a click toggle (when `openOnClick`), and on a non-control `role=\"button\"`, `tabindex=\"0\"` and Enter/Space activation. The element's own handler wins: when it calls `open`, `close`, `toggle` or `setOpen` during the click, the default toggle stands down, so a kept `onclick={popover.toggle}` does not toggle twice\n- **reference**: attachment for a custom trigger element (`{@attach popover.reference}`); anchors the panel to it and keeps its `aria-haspopup`, `aria-expanded`, and `aria-controls` in sync, leaving clicks and keyboard to the element\n\n## Accessibility\n\n- The trigger carries `aria-haspopup` (from `haspopup`), `aria-expanded`, and `aria-controls` — automatically for the built-in Button and for a snippet trigger using `{@attach popover.trigger}` or `{@attach popover.reference}`\n- `focusOnOpen` decides where focus lands on open; focus returns to the trigger on close, whether by Escape or an outside press\n- Non-modal: Tab is not contained and the page is not made inert (use Dialog for that)\n- Escape closes only the topmost open layer; an outside press dismisses every layer stacked above the one pressed. Popovers, menus, and dialogs share one layer stack\n- Keyboard navigation support\n\n## Notes\n\n- Popover is positioned using floating-ui, except with `inline`, where the document lays the panel out\n- Automatically adjusts position to stay in viewport\n- Multiple popovers can be stacked\n- Scroll locking prevents background scroll (when enabled)\n- Transitions animate based on position direction\n\n## Theme Customization\n\nThe Popover component uses a theme object that can be customized using the `theme` prop or by setting a global theme.\n\n### Theme Structure\n\nThe theme object contains the following parts:\n- **motion**: Open/close transition preset, keyed by `mode` (a floating panel scales, the mobile\n sheet slides up). Takes `in` / `out` FSO params plus a `duration` / `easing` motion token;\n the `transition` prop wins over it\n- **popover**: Main popover container styles\n\n### Theme Type Definition\n\n```typescript\nimport type { PopoverThemeProps } from 'entasis/popover';\n\n// Example theme customization\nconst customTheme: PopoverThemeProps = {\n popover: {\n base: 'z-[+50] fixed bg-surface-floating text-neutral w-fit rounded-xl raised isolate h-fit',\n size: {\n small: 'max-w-3xs w-full p-2',\n normal: 'max-w-xs w-full p-3',\n large: 'max-w-sm w-full p-4'\n }\n }\n};\n```\n\n### Available Variants\n\n**popover**:\n- base: Base classes applied to all popovers\n- Variants:\n - size: 'small' | 'normal' | 'large' - Controls max-width, width, and padding\n - mode: 'floating' | 'inline' | 'mobileSheet' - Set from `inline` / `mobileSheet`; `root` positions the wrapper (fixed, in flow, or full-screen sheet)\n\n### Usage Examples\n\n**Basic Theme Override**:\n```svelte\n<Popover \n trigger={{ content: \"Click Me\" }}\n theme={{\n popover: {\n base: 'rounded-xl lift-5 border-2 border-primary',\n size: {\n normal: 'max-w-md p-4'\n }\n }\n }}\n>\n Custom styled popover content\n</Popover>\n```\n\n**Size Customization**:\n```svelte\n<Popover \n size=\"large\"\n trigger={{ content: \"Large Popover\" }}\n theme={{\n popover: {\n size: {\n large: 'max-w-lg p-6'\n }\n }\n }}\n>\n Large popover with more padding\n</Popover>\n```\n\n**Global Theme Setting**:\n```svelte\n<script>\n import { setPopoverTheme } from '../components/Popover/index.ts';\n \n setPopoverTheme({\n popover: {\n base: 'rounded-lg lift-5 backdrop-blur-sm bg-white/95',\n size: {\n normal: 'max-w-sm p-4'\n }\n }\n });\n</script>\n```\n";
113
+ readonly tooltip: "\n# Tooltip\n\nContextual information shown on hover or keyboard focus. Two forms share one surface:\n\n- `<Tooltip>` — a component with a `trigger` prop, like every other overlay.\n- `tooltip()` — the underlying attachment, for elements you already render yourself.\n\nBoth are rendered by the single tooltip surface that `<Theme>` mounts (`TooltipHost`).\n\n## Basic Usage\n\n```svelte\n<script lang=\"ts\">\n\timport { Tooltip } from 'entasis/tooltip';\n</script>\n\n<Tooltip content=\"Click to submit\" trigger={{ content: 'Submit', variant: 'outline' }} />\n```\n\n## Props\n\n- **content**: string | Snippet (required) - Tooltip body\n- **trigger**: Snippet<[Attachment<HTMLElement>]> | ButtonProps & { content?: string } (required) -\n A snippet receives the tooltip attachment and spreads it on its own element; an element the\n browser cannot focus (a span, an icon) gets `tabindex=\"0\"` so keyboard focus shows the tooltip.\n Button props render a Button carrying it; `children` (a string or a snippet) replaces\n `content` for a richer body\n- **open**: boolean (default: false) - Shows the tooltip without hover or focus; bindable\n- **defaultOpen**: boolean (default: false) - Initial open state when `open` is not provided\n- **onOpenChange**: (open: boolean) => void - Called whenever the tooltip becomes visible or hidden,\n hover and focus included\n- **position**: Placement (default: 'top') - Tooltip position relative to the trigger\n - Options: 'top' | 'bottom' | 'left' | 'right' | 'top-start' | 'top-end' | 'bottom-start' | 'bottom-end' | 'left-start' | 'left-end' | 'right-start' | 'right-end'\n- **size**: 'small' | 'normal' | 'large' (default: 'normal') - Visual size\n- **color**: Colors (default: 'neutral') - Color theme\n- **variant**: 'solid' | 'outline' | 'soft' (default: 'solid') - Visual style matching Chip\n- **delay**: number (default: 400) - Delay in ms before showing tooltip; zero shows immediately\n- **offset**: number - Distance from the trigger in pixels\n- **class**: string - Additional CSS classes\n- **transition**: FSOProps - Custom transition configuration\n- **theme**: TooltipThemeProps - Per-instance theme overrides\n- **onAfterOpen**: () => void - Callback after the opening transition completes\n- **onAfterClose**: () => void - Callback after the closing transition completes\n\nEvery prop except `trigger`, `open`, `defaultOpen` and `onOpenChange` is also an option of the\n`tooltip()` attachment.\n\n## Examples\n\n### Button Trigger\n```svelte\n<script lang=\"ts\">\n\timport { Tooltip } from 'entasis/tooltip';\n</script>\n\n<Tooltip\n\tcontent=\"This is helpful information\"\n\ttrigger={{ content: 'Hover me', variant: 'outline', color: 'neutral' }}\n/>\n```\n\n### Snippet Trigger\n```svelte\n<script lang=\"ts\">\n\timport type { Attachment } from 'svelte/attachments';\n\timport { Tooltip } from 'entasis/tooltip';\n</script>\n\n{#snippet helpTrigger(attach: Attachment<HTMLElement>)}\n\t<span class=\"underline\" {@attach attach}>What is this?</span>\n{/snippet}\n\n<Tooltip content=\"Anchored to any element you like\" trigger={helpTrigger} />\n```\n\n### Forced Open\n```svelte\n<script lang=\"ts\">\n\timport { Tooltip } from 'entasis/tooltip';\n</script>\n\n<!-- Useful for docs, screenshots and visual tests -->\n<Tooltip open content=\"Always visible\" trigger={{ content: 'Anchor', variant: 'outline' }} />\n```\n\n### Different Positions\n```svelte\n<script lang=\"ts\">\n\timport { Tooltip } from 'entasis/tooltip';\n</script>\n\n<Tooltip content=\"Top tooltip\" position=\"top\" trigger={{ content: 'Top' }} />\n<Tooltip content=\"Bottom tooltip\" position=\"bottom\" trigger={{ content: 'Bottom' }} />\n<Tooltip content=\"Left tooltip\" position=\"left\" trigger={{ content: 'Left' }} />\n<Tooltip content=\"Right tooltip\" position=\"right\" trigger={{ content: 'Right' }} />\n```\n\n### Different Colors\n```svelte\n<script lang=\"ts\">\n\timport { Tooltip } from 'entasis/tooltip';\n</script>\n\n<Tooltip content=\"Success!\" color=\"success\" trigger={{ content: 'Success' }} />\n<Tooltip content=\"Warning!\" color=\"warning\" trigger={{ content: 'Warning' }} />\n<Tooltip content=\"Error!\" color=\"danger\" trigger={{ content: 'Error' }} />\n```\n\n### Custom Delay\n```svelte\n<script lang=\"ts\">\n\timport { Tooltip } from 'entasis/tooltip';\n</script>\n\n<Tooltip content=\"Quick tooltip\" delay={100} trigger={{ content: 'Quick (100ms)' }} />\n<Tooltip content=\"Slow tooltip\" delay={1000} trigger={{ content: 'Slow (1000ms)' }} />\n```\n\n### Different Sizes and Variants\n```svelte\n<script lang=\"ts\">\n\timport { Tooltip } from 'entasis/tooltip';\n</script>\n\n<Tooltip content=\"Small tooltip\" size=\"small\" trigger={{ content: 'Small' }} />\n<Tooltip content=\"Large tooltip\" size=\"large\" trigger={{ content: 'Large' }} />\n<Tooltip content=\"Outlined tooltip\" variant=\"outline\" trigger={{ content: 'Outline' }} />\n<Tooltip content=\"Soft tooltip\" variant=\"soft\" trigger={{ content: 'Soft' }} />\n```\n\n### With Snippet Content\n```svelte\n<script lang=\"ts\">\n\timport { Tooltip } from 'entasis/tooltip';\n</script>\n\n{#snippet richContent()}\n\t<div class=\"p-2\">\n\t\t<strong>Pro Tip</strong>\n\t\t<p class=\"text-sm\">Use Ctrl+S to save</p>\n\t</div>\n{/snippet}\n\n<Tooltip content={richContent} trigger={{ content: 'Keyboard Shortcuts' }} />\n```\n\n### With Callbacks\n```svelte\n<script lang=\"ts\">\n\timport { Tooltip } from 'entasis/tooltip';\n</script>\n\n<Tooltip\n\tcontent=\"Tracked tooltip\"\n\tonAfterOpen={() => console.log('Tooltip opened')}\n\tonAfterClose={() => console.log('Tooltip closed')}\n\ttrigger={{ content: 'Track me' }}\n/>\n```\n\n## The tooltip() attachment\n\nUse the attachment when the element already exists in your markup — icons, table cells, list rows,\ndisabled wrappers — or inside another component's internals.\n\n```svelte\n<script lang=\"ts\">\n\timport { tooltip } from 'entasis/tooltip';\n</script>\n\n<button {@attach tooltip({ content: 'Click to submit' })}> Submit </button>\n\n<!-- On icons or any non-interactive element -->\n<span {@attach tooltip({ content: 'More information', position: 'right' })}> ⓘ </span>\n\n<!-- Disabled elements do not fire events, so wrap them -->\n<span {@attach tooltip({ content: 'Feature coming soon' })}>\n\t<button disabled>Disabled Button</button>\n</span>\n```\n\nThe `<Tooltip>` component hands this same attachment to a snippet trigger, so the two forms are\ninterchangeable.\n\n## Accessibility\n\n- Shows on pointer hover and on keyboard focus (`focusin` / `focusout` on the trigger element), so\n attach it to focusable elements for keyboard users\n- Dismissed on mouse leave or blur\n- Non-interactive (cannot be clicked)\n- Renders with `role=\"tooltip\"` and sets `aria-describedby` on the trigger while visible (any previous value is restored on hide)\n- Does not block content behind it\n\n## Notes\n\n- Only one tooltip shows at a time; an `open` tooltip hands the surface over when another tooltip is\n hovered and reports that through `onOpenChange`\n- Automatically positions to stay in viewport using Floating UI\n- Uses smart delay: subsequent tooltips show instantly if within 400ms of previous\n- Brief content only (use Popover for interactive content)\n- The surface is a singleton (`TooltipHost`) rendered by `<Theme>`\n- Does not lock scroll or trap focus\n\n## Theme Customization\n\nThe Tooltip uses a theme object that can be customized using the `theme` prop or by setting a global theme.\n\n### Theme Structure\n\nThe theme object contains the following parts:\n- **root**: Main tooltip container styles\n\n### Theme Type Definition\n\n```typescript\nimport type { TooltipThemeProps } from 'entasis/tooltip';\n\n// Example theme customization\nconst customTheme: TooltipThemeProps = {\n root: {\n base: 'inline-flex w-fit items-center rounded-full border font-medium',\n size: {\n small: 'h-5 px-2 text-xs',\n normal: 'h-6 px-2.5 text-xs',\n large: 'h-7 px-3 text-sm'\n },\n\tcolor: {\n\t neutral: 'bg-neutral text-neutral-contrast',\n primary: 'bg-primary text-primary-contrast',\n danger: 'bg-danger text-danger-contrast',\n success: 'bg-success text-success-contrast',\n warning: 'bg-warning text-warning-contrast',\n\t info: 'bg-info text-info-contrast'\n\t},\n\tvariant: {\n\t solid: 'bg-color text-color-contrast',\n\t outline: 'border-color bg-transparent text-color-readable',\n\t soft: 'bg-color-muted text-color-muted-readable'\n }\n }\n};\n```\n\n### Available Variants\n\n**root**:\n- base: Base classes applied to all tooltips\n- Variants:\n - size: 'small' | 'normal' | 'large' - Controls text size and padding\n - color: 'primary' | 'secondary' | 'neutral' | 'danger' | 'success' | 'warning' | 'info' - Color scheme\n - variant: 'solid' | 'outline' | 'soft' - Matches Chip's visual variants\n\n### Usage Examples\n\n**Basic Theme Override**:\n```svelte\n<script lang=\"ts\">\n\timport { Tooltip } from 'entasis/tooltip';\n</script>\n\n<Tooltip\n\tcontent=\"Custom tooltip\"\n\ttheme={{ root: { base: 'rounded-lg lift-4 border-2', size: { normal: 'px-3 py-2 text-sm' } } }}\n\ttrigger={{ content: 'Hover me' }}\n/>\n```\n\n**Color Customization**:\n```svelte\n<script lang=\"ts\">\n\timport { tooltip } from 'entasis/tooltip';\n</script>\n\n<button\n\t{@attach tooltip({\n\t\tcontent: 'Success!',\n\t\tcolor: 'success',\n\t\ttheme: { root: { color: { success: 'bg-green-500 text-white lift-3' } } }\n\t})}\n>\n\tSuccess Tooltip\n</button>\n```\n\n**Global Theme Setting**:\n```svelte\n<script lang=\"ts\">\n\timport { setTooltipTheme } from 'entasis/tooltip';\n\n\tsetTooltipTheme({\n\t\troot: {\n\t\t\tbase: 'rounded-md lift-4 backdrop-blur-sm',\n\t\t\tsize: { normal: 'px-3 py-1.5 text-sm' },\n\t\t\tcolor: { neutral: 'bg-gray-900 text-white', primary: 'bg-blue-500 text-white' }\n\t\t}\n\t});\n</script>\n```\n\n## Motion\n\n- **motion** theme slot: one preset (no variants) — a short fade-and-rise, `duration: 'fast'`.\n- Resolved by the tooltip surface and handed to the underlying Popover, so it replaces the\n popover preset.\n- Ladder: `<Theme components={{ tooltip: { motion } }}>` → `setTooltipTheme({ motion })` →\n `theme.motion` → the tooltip's `transition` option. Reduced motion collapses it to 0.\n- The tooltip surface is a singleton rendered by `<Theme>`, so a `setTooltipTheme` call\n made *below* `<Theme>` never reaches it. Call it at or above the `<Theme>` boundary, or\n use the `<Theme components={{ tooltip }}>` registry, which always applies.\n";
114
114
  readonly 'audio-player': "\n# AudioPlayer Component\n\nAudioPlayer is a native HTML5 audio player with Entasis chrome. It can render a\nwaveform or track seek/progress surface and composes controls from Button, Tooltip,\nPopover, and the shared Slider primitive.\n\n## Import\n\n```ts\nimport { AudioPlayer } from 'entasis/audio-player';\n```\n\n## Core Props\n\n- **src**: string - Single audio source URL.\n- **sources**: AudioPlayerSource[] - Multiple native source candidates.\n- **title**: string - Track title.\n- **label**: string - Accessible player label; falls back to `title`.\n- **artist**: string - Secondary metadata line.\n- **artwork**: string | false - Optional artwork image URL. Omitted artwork renders no fallback.\n- **variant**: 'waveform' | 'track' - Primary progress surface.\n- **layout**: 'block' | 'inline' - Controls/progress arrangement. Block stacks the seek surface below the header; inline places controls and seek on the same row when space allows.\n- **color**: Colors - Theme color for controls and progress fill.\n- **waveform**: number[] - Amplitude samples from 0 to 1. When omitted, samples are generated from the selected audio source when possible.\n- **waveformVariant**: 'centered' | 'histogram' - Waveform visual mode.\n- **waveformBars**: number - Number of bars rendered after resampling.\n- **controls**: AudioPlayerControl[] - Toggle play, seek, time, volume, loop, download.\n- **header**: Snippet<[AudioPlayerState]> - Replaces the full default header row.\n- **controlsSlot**: Snippet<[AudioPlayerState]> - Replaces the default controls area.\n- **leading**: Snippet<[AudioPlayerState]> - Renders before default metadata.\n- **trailing**: Snippet<[AudioPlayerState]> - Renders after default controls.\n- **seek**: Snippet<[AudioPlayerState]> - Replaces the waveform or track seek surface.\n- **download**: boolean | string - true uses the selected source, string uses that href.\n- **theme**: AudioPlayerThemeProps - Per-instance theme overrides.\n\n## Notes\n\nThe waveform and track surfaces are both seek inputs with an invisible range hitbox.\nWhen no waveform samples are provided, the component fetches and decodes the selected\naudio source in the browser to generate peak samples. While generation is pending, or\nif fetch/decode is unavailable, it uses a deterministic fallback waveform from source\nand metadata. Generation failures are reported through onError.\n";
115
115
  readonly carousel: "\n# Carousel Component\n\nCarousel renders a scrollable collection from items. Its public data contract matches Stepper and Tabs: pass items, then render one generated slide with children({ carousel, item, index }).\n\nThe component owns the direct slide wrappers. Do not put a consumer-owned repeated slide loop directly inside Carousel.\n\nThe chrome is a FOOTER ROW under the slides, never an overlay: pagination fills the leading side, the prev/next pair sits at the trailing end — including with pagination={false}, where the arrows are the row's only child and still sit trailing. By default the pagination is a progress line whose fill is the fraction of the scrollable range already scrolled.\n\n## Basic Usage\n\n<script lang=\"ts\">\n\timport { Carousel } from 'entasis/carousel';\n\n\tconst items = [\n\t\t{ title: 'Signal', description: 'Collect the first insight.' },\n\t\t{ title: 'Orbit', description: 'Review the second item.' },\n\t\t{ title: 'Focus', description: 'Finish with the third item.' }\n\t];\n</script>\n\n<Carousel items={items} navigationButton={{ color: 'primary' }} pagination={{ color: 'primary' }}>\n\t{#snippet children({ item, index, carousel })}\n\t\t<article>\n\t\t\t<p>Slide {index + 1}</p>\n\t\t\t<h3>{item.title}</h3>\n\t\t\t<p>{item.description}</p>\n\t\t\t<button onclick={() => carousel.next()}>Next</button>\n\t\t</article>\n\t{/snippet}\n</Carousel>\n\n## Core Props\n\n- items: Item[] - the collection used to generate one slide per item.\n- children: Snippet<[CarouselRenderPayload<Item>]> - renders content inside each generated slide wrapper.\n- layout: ResponsiveProps<number> - number of slides visible. Default: 1.\n- gaps: ResponsiveProps<number> - gap in pixels between generated slides. Default: 20.\n- partialDelta: ResponsiveProps<number> - pixels to reveal from the adjacent slide. Default: 0.\n- dragFree: boolean - disables strict snap behavior when true.\n- navigationButton: object, snippet or false - built-in prev/next controls, custom controls, or no buttons. The object takes color and size: 'small' | 'normal' | 'large' (default 'normal'). Default: { color: 'neutral' }.\n- pagination: object, snippet or false - built-in pagination, custom pagination, or none. The object takes variant: 'line' | 'dots', color and size. Default: { variant: 'line' }. 'line' is a presentational progress bar (role=\"progressbar\", not clickable); 'dots' renders one clickable dot per page.\n\nWhen pagination and navigationButton are both false no footer is rendered and the track is scrolled by drag, wheel and the keyboard.\n- class: string - classes for the root container.\n- theme: CarouselThemeProps - theme overrides for public parts.\n\nThose three props take the shared ResponsiveProps shape: a plain number used at every width (gaps={16}) or a record keyed by breakpoint (layout={{ xs: 1, md: 2 }}). In the record form xs is the base — there is no default key — and the nearest defined key at or below the active width wins, so { xs: 1, md: 2 } shows two slides from md up. A key you leave unset below the narrowest one falls back to the prop default.\n\nThe xs / sm / md / lg / xl keys are the CAROUSEL's own width, not the viewport's: sm from 36rem, md from 42rem, lg from 56rem, xl from 72rem of carousel width, with xs below that. These are the shared container breakpoints exported from entasis/theme, so sm means the same box width in Carousel, Grid and Stack. A 360px carousel in a sidebar of a wide page is xs; the same carousel run full-bleed is xl. Give the carousel a width that fills its host (the default root is w-full) so it can measure itself.\n\n## Render Payload\n\nchildren receives:\n\n- carousel: CarouselState - state and navigation helpers.\n- item: Item - the current item, preserving the caller's item shape.\n- index: number - zero-based generated slide index.\n\nUse item fields directly. For example, if items contains { title, image }, item.title and item.image are typed inside the snippet.\n\n## Responsive Example\n\n<Carousel\n\titems={items}\n\tlayout={{ xs: 1, sm: 2, lg: 3 }}\n\tgaps={{ xs: 16, lg: 24 }}\n\tpartialDelta={48}\n\tnavigationButton={{ color: 'primary' }}\n\tpagination={{ color: 'primary' }}\n>\n\t{#snippet children({ item })}\n\t\t<div class=\"rounded bg-surface p-6\">\n\t\t\t<h3>{item.title}</h3>\n\t\t\t<p>{item.description}</p>\n\t\t</div>\n\t{/snippet}\n</Carousel>\n\n## Custom Navigation\n\nThe snippet is rendered once per direction inside the footer's trailing slot, so it needs no positioning of its own.\n\n<Carousel items={items} pagination={{ color: 'primary' }}>\n\t{#snippet navigationButton(carousel, attributes, direction)}\n\t\t<button\n\t\t\t{...attributes}\n\t\t\tonclick={() => direction === 'prev' ? carousel.prev() : carousel.next()}\n\t\t>\n\t\t\t{direction === 'prev' ? 'Previous' : 'Next'}\n\t\t</button>\n\t{/snippet}\n\n\t{#snippet children({ item })}\n\t\t<div>{item.title}</div>\n\t{/snippet}\n</Carousel>\n\n## Custom Pagination\n\n<Carousel items={items} navigationButton={{ color: 'primary' }}>\n\t{#snippet pagination(carousel, dotItems)}\n\t\t<div class=\"flex justify-center gap-2\">\n\t\t\t{#each dotItems as dot, index}\n\t\t\t\t<button {...dot.attributes}>\n\t\t\t\t\t<span class=\"sr-only\">Slide {index + 1}</span>\n\t\t\t\t</button>\n\t\t\t{/each}\n\t\t</div>\n\t{/snippet}\n\n\t{#snippet children({ item })}\n\t\t<div>{item.title}</div>\n\t{/snippet}\n</Carousel>\n\n## CarouselState\n\n- currentSlide - currently visible slide.\n- lastSlideInView - last visible slide in the viewport.\n- canScrollNext - whether next navigation is possible.\n- canScrollPrev - whether previous navigation is possible.\n- sortedSlides - generated slide records in DOM order.\n- dots - pagination dot records with active state and attributes.\n- progress - 0..1 fraction of the scrollable range already scrolled; the progress line's fill width. 0 on the server.\n- scrollRange - the track's scrollable distance (scrollWidth - clientWidth).\n- breakpoint - active breakpoint, resolved from the carousel's own measured width.\n- resolvedLayout - active slides-per-view value.\n- resolvedGaps - active gap value.\n- next(count?) - move forward by count slides, defaulting to the active layout size.\n- prev(count?) - move backward by count slides, defaulting to the active layout size.\n- nextButton and prevButton - attributes for custom button composition.\n\n## Keyboard and Accessibility\n\n- The slider track is a focusable landmark: role=\"region\", aria-roledescription=\"carousel\", tabindex=\"0\"; each slide is labelled \"Slide n of m\".\n- With the track focused, ArrowRight / ArrowLeft move to the next / previous slide (mirrored in RTL), Home and End jump to the first and last slide.\n- Built-in dots mark the active slide with aria-current (not aria-selected); custom dots receive the same attributes through dot.attributes.\n- The progress line is presentational: role=\"progressbar\" with aria-valuenow/min/max and an i18n accessible name. It is not focusable and not clickable.\n\n## Theme Parts\n\n- root - outer carousel container.\n- slider - scrollable track.\n- slide - generated direct slide wrapper.\n- footer - the chrome row under the slider.\n- progress - the progress line's recessed track.\n- progressFill - the progress line's coloured fill.\n- navigation - the prev/next pair's container.\n- navigationButton - built-in previous and next buttons.\n- dots - built-in dots container.\n- dot - built-in dot button.\n\nUse the slide theme part to style every generated wrapper consistently:\n\n<Carousel\n\titems={items}\n\ttheme={{ slide: { base: 'rounded-lg bg-surface p-4' } }}\n>\n\t{#snippet children({ item })}\n\t\t<h3>{item.title}</h3>\n\t{/snippet}\n</Carousel>\n";
116
116
  readonly 'image-gallery': "\n# ImageGallery\n\nImageGallery enhances descendant images inside arbitrary HTML. It discovers matching `img`\nelements, makes them keyboard reachable, and initializes LightGallery against those real elements.\nLightGallery provides origin zoom, swipe navigation, thumbnails, pinch zoom, and image panning.\n\n## Usage\n\n```svelte\n<script>\n\timport { ImageGallery } from 'entasis/image-gallery';\n</script>\n\n<ImageGallery>\n\t<article>\n\t\t<img src=\"/photos/one.jpg\" alt=\"Mountain lake\" title=\"Morning at the lake\" />\n\t\t<p>Any HTML can live here.</p>\n\t\t<img src=\"/photos/two.jpg\" alt=\"Forest trail\" />\n\t</article>\n</ImageGallery>\n```\n\n## Image Discovery\n\nImages are discovered from the DOM with `imageSelector`, defaulting to `img`.\n\n- `src` comes from `img.currentSrc || img.src`.\n- `alt` comes from `img.alt`.\n- The default caption comes from `img.title || img.alt`.\n- The root attachment initializes LightGallery synchronously with the mounted wrapper and owns teardown.\n- There is no `items` prop and no high-resolution data attribute contract in v1.\n\n## Props\n\n- **open**: boolean (bindable, default: false) - Controls the zoomed gallery.\n- **defaultOpen**: boolean (default: false) - Initial state when open is not provided.\n- **activeIndex**: number (bindable, default: 0) - Controls the active discovered image.\n- **imageSelector**: string (default: \"img\") - Selector used inside the wrapper.\n- **disabled**: boolean (default: false) - Prevents image enhancement and opening.\n- **zoomMargin**: number (default: 32) - Minimum viewport margin around the zoomed image.\n- Zoom animation duration and easing now come from the `motion` theme slot (default tokens `normal` / `enter`); see Motion below.\n- **closeOnClickOutside**: boolean (default: true) - Closes from the backdrop.\n- **closeOnEscape**: boolean (default: true) - Closes on Escape.\n- **lockScroll**: boolean (default: true) - Locks page scroll while open.\n- **buttonLabel**: string (default: \"Open image gallery\") - Accessible label prefix for enhanced images.\n- **closeLabel**: string (default: \"Close image gallery\") - Accessible close button label.\n- **previousLabel**: string (default: \"Previous image\") - Previous control label.\n- **nextLabel**: string (default: \"Next image\") - Next control label.\n- **licenseKey**: string (default: LightGallery evaluation key) - LightGallery license key. A production key is required unless the consuming project is GPLv3-compatible.\n- **class**: string - Additional root classes.\n- **theme**: ImageGalleryThemeProps - Per-instance theme overrides.\n- **onOpenChange**: (open: boolean) => void - Fired once when the library requests a new open state.\n- **onIndexChange**: ({ index, gallery }) => void - Fired when navigation changes the active image.\n- **onAfterOpen**: (payload) => void - Fired after the open animation completes.\n- **onAfterClose**: (payload) => void - Fired after the close animation completes.\n\n## Slots\n\n- **children**: arbitrary HTML to render and enhance.\n- **caption**: custom caption content. Receives `ImageGalleryPayload`.\n\n## Accessibility\n\n- Discovered images receive `role=\"button\"`, `tabindex=\"0\"`, and an accessible label while mounted.\n- Previous attributes are restored when ImageGallery is destroyed or disabled.\n- Enter and Space open the gallery from a focused image.\n- The zoom layer uses `role=\"dialog\"` and `aria-modal=\"true\"`.\n- Focus moves into the LightGallery dialog after opening and returns to the source image after closing.\n- Escape closes the gallery by default.\n- LightGallery handles horizontal swipe and drag navigation.\n- Wheel, double-click, and two-finger pinch zoom the active image; drag pans while zoomed.\n- Static selector mode keeps LightGallery's `zoomFromOrigin` animation attached to the exact source image.\n\n## Examples\n\n### Controlled\n\n```svelte\n<script>\n\tlet open = $state(false);\n\tlet activeIndex = $state(0);\n</script>\n\n<button onclick={() => { activeIndex = 1; open = true; }}>\n\tOpen second image\n</button>\n\n<ImageGallery bind:open bind:activeIndex>\n\t<img src=\"/one.jpg\" alt=\"First image\" />\n\t<img src=\"/two.jpg\" alt=\"Second image\" />\n</ImageGallery>\n```\n\n### Custom caption\n\n```svelte\n<ImageGallery>\n\t<img src=\"/one.jpg\" alt=\"First image\" title=\"Editorial caption\" />\n\n\t{#snippet caption({ activeImage })}\n\t\t<p>{activeImage?.caption}</p>\n\t{/snippet}\n</ImageGallery>\n```\n\n## Motion\n\n- **motion** theme slot: one preset (no variants). Its resolved `duration` / `easing` become\n the lightbox's animation duration and CSS easing (default tokens `normal` / `enter`).\n- Ladder: `<Theme components={{ 'image-gallery': { motion } }}>` →\n `setImageGalleryTheme({ motion })` → `theme.motion`. Reduced motion collapses it to 0.\n";
@@ -128,10 +128,11 @@ export declare const componentMcpRegistry: {
128
128
  readonly 'qr-code': "\n# QRCode Component\n\nThe QRCode component renders a customizable QR code as an SVG. It supports theme sizes and colors, gradients, custom shapes for data modules and finder patterns, an embedded center image, and downloading as SVG, PNG or JPEG. Ported from react-qr-code (https://github.com/LGLabGreg/react-qr-code).\n\n## Basic Usage\n\n```svelte\n<QRCode value=\"https://example.com\" />\n<QRCode value=\"https://example.com\" size=\"large\" color=\"primary\" />\n```\n\n## Props\n\n### Core Props\n- **value**: string | string[] (required) - The value to encode. An array of strings represents multiple segments to further optimize the QR Code.\n- **size**: 'small' | 'normal' | 'large' (default: 'normal')\n - small: 96px (size-24)\n - normal: 128px (size-32)\n - large: 192px (size-48)\n- **color**: Colors (default: 'neutral') - Theme color of the modules and finder patterns. Applied through `currentColor`, so it adapts to the active theme.\n- **level**: 'L' | 'M' | 'Q' | 'H' (default: 'M') - The Error Correction Level.\n- **minVersion**: number (default: 1) - Minimum QR version (1-40) used as the lower bound when encoding.\n- **boostLevel**: boolean (default: true) - Allow raising the Error Correction Level when it does not increase the version.\n- **marginSize**: number (default: 4) - Number of modules used as margin (quiet zone). The QR specification requires 4.\n\n### Styling Props\n- **background**: string | GradientSettings - Background color or gradient. Transparent when not provided.\n- **gradient**: GradientSettings - Gradient applied to data modules and finder patterns. Overrides `color` and the settings colors.\n- **dataModulesSettings**: { color?, style?, randomSize?, scale?, lineWidth? } - Data module rendering.\n - style: 'square' | 'square-sm' | 'pinched-square' | 'rounded' | 'leaf' | 'vertical-line' | 'horizontal-line' | 'circuit-board' | 'circle' | 'diamond' | 'star' | 'heart' | 'hashtag'\n- **finderPatternOuterSettings**: { color?, style? } - Outer finder pattern rendering.\n - style: 'square' | 'pinched-square' | 'rounded-sm' | 'rounded' | 'rounded-lg' | 'circle' | 'inpoint-sm' | 'inpoint' | 'inpoint-lg' | 'outpoint-sm' | 'outpoint' | 'outpoint-lg' | 'leaf-sm' | 'leaf' | 'leaf-lg'\n- **finderPatternInnerSettings**: { color?, style? } - Inner finder pattern rendering.\n - style: same as outer, plus 'diamond' | 'star' | 'heart' | 'hashtag' | 'microchip'\n- **imageSettings**: { src, width, height, excavate?, x?, y?, opacity?, crossOrigin? } - Embedded center image. `excavate` clears the modules behind the image. Pixel values are relative to the nominal size of the QR code.\n- **class**: string - Additional CSS classes on the SVG element.\n- **theme**: QRCodeTheme - Theme overrides.\n\n### Accessibility Props\n- **label**: string (default: 'QR Code') - Accessible label of the SVG.\n\n### Advanced Props\n- **ref**: SVGSVGElement | null (bindable) - The rendered SVG element.\n\n## Methods\n\nBind the component instance to access:\n\n- **download(options?)**: Downloads the QR code.\n - options.name: string (default: 'qr-code') - File name without extension.\n - options.format: 'svg' | 'png' | 'jpeg' (default: 'svg')\n - options.dimension: number (default: 500) - Exported file width and height in pixels.\n\n```svelte\n<script>\n\tlet qr;\n</script>\n\n<QRCode bind:this={qr} value=\"https://example.com\" />\n<Button onclick={() => qr.download({ format: 'png' })}>Download</Button>\n```\n\n## Examples\n\n### Gradient with custom shapes\n```svelte\n<QRCode\n\tvalue=\"https://example.com\"\n\tgradient={{\n\t\ttype: 'linear',\n\t\trotation: 45,\n\t\tstops: [\n\t\t\t{ offset: '0%', color: '#6d78d5' },\n\t\t\t{ offset: '100%', color: '#d56d6d' }\n\t\t]\n\t}}\n\tdataModulesSettings={{ style: 'circle' }}\n\tfinderPatternOuterSettings={{ style: 'rounded' }}\n\tfinderPatternInnerSettings={{ style: 'circle' }}\n/>\n```\n\n### Embedded image\n```svelte\n<QRCode\n\tvalue=\"https://example.com\"\n\tlevel=\"H\"\n\timageSettings={{ src: '/logo.png', width: 24, height: 24, excavate: true }}\n/>\n```\n\n## Accessibility\n\n- The SVG has `role=\"img\"` and an `aria-label` (customizable via the `label` prop).\n\n## Notes\n\n- Colors default to `currentColor`, driven by the `color` prop theme classes; downloads resolve the computed color so exports match the on-screen theme.\n- Keep enough contrast between the modules and the surface behind the QR code, and prefer `level=\"H\"` when embedding an image, otherwise the code may not scan.\n- `randomSize` and low `scale`/`lineWidth` values in `dataModulesSettings` may degrade scannability.\n";
129
129
  readonly hitbox: "\n# Hitbox Component\n\nHitbox enlarges the pointer target of an existing interactive element without changing its visible dimensions or semantics. It renders one transparent, aria-hidden span centered over its positioned parent; pointer events bubble to the parent button or link.\n\n## Usage\n\n```svelte\n<script>\n import { Hitbox } from '../components/Hitbox/index.ts';\n</script>\n\n<button type=\"button\" aria-label=\"Select page\" class=\"relative size-2 rounded-full bg-primary\">\n <Hitbox size=\"normal\" />\n</button>\n```\n\nThe interactive parent must establish a positioning context and allow overflow. Adjacent controls should reserve enough layout space for their hitboxes so targets do not overlap.\n\n## Props\n\n- **size**: 'small' | 'normal' | 'large' (default: 'normal') - Target dimensions: 20px, 24px, or 28px.\n- **ref**: HTMLSpanElement | null - Bindable reference to the transparent span.\n- **class**: string - Classes applied to the span.\n- **theme**: HitboxThemeProps - Theme overrides for the root part.\n\n## Accessibility\n\nHitbox is aria-hidden and does not create another focusable element. The parent remains responsible for its accessible name, keyboard behavior, disabled state, and focus indication.\n\n## Theme\n\n- **root**: Transparent centered target surface and size variants.\n";
130
130
  readonly slot: "\n# Slot Component\n\nThe Slot component is a utility for rendering dynamic content - it can render snippets, strings, numbers, or other components with proper handling and props passing.\n\n## Basic Usage\n\n```svelte\n<Slot render={content} />\n```\n\n## Props\n\n### Core Props\n- **render**: Slot - Content to render (can be string, number, Snippet, or component)\n- **class**: string - CSS class to apply to the wrapper\n\n## Slot Type\n\nThe Slot type accepts:\n- **string** - Rendered as text\n- **number** - Rendered as text\n- **Snippet** - Rendered as a Svelte snippet with props\n- **Component** - Rendered as a Svelte component\n\n## Examples\n\n### Render String\n```svelte\n<Slot render=\"Hello World\" />\n```\n\n### Render Number\n```svelte\n<Slot render={42} />\n```\n\n### Render Snippet\n```svelte\n{#snippet content()}\n\t<strong>Bold Text</strong>\n{/snippet}\n\n<Slot render={content} />\n```\n\n### Render Snippet\n```svelte\n{#snippet greeting()}\n\t<h1>Hello World!</h1>\n{/snippet}\n\n<Slot render={greeting} />\n```\n\n### With CSS Class\n```svelte\n<Slot \n\trender={content}\n\tclass=\"text-primary font-bold\"\n/>\n```\n\n### Conditional Rendering\n```svelte\n<script>\n\tlet content = condition ? 'Yes' : 'No';\n</script>\n\n<Slot render={content} />\n```\n\n### In Component Props\n```svelte\n<script lang=\"ts\">\n\timport { Slot, type SlotContent } from 'entasis/slot';\n\n\tlet { title, description }: { title: SlotContent; description: SlotContent } = $props();\n</script>\n\n<div class=\"card\">\n\t<Slot render={title} class=\"card-title\" />\n\t<Slot render={description} class=\"card-description\" />\n</div>\n```\n\n### Dynamic Icon\n```svelte\n<script>\n\tlet icon = condition ? checkIcon : xIcon;\n</script>\n\n<Slot render={icon} class=\"icon\" />\n```\n\n## Use Cases\n\n### 1. Flexible Component Props\nAllow component users to pass either static content or dynamic snippets:\n\n```svelte\n<Button>\n\t<Slot render={label} />\n</Button>\n```\n\n### 2. Conditional Content\nRender different content types based on runtime conditions:\n\n```svelte\n<Slot render={isLoading ? 'Loading...' : data} />\n```\n\n### 3. List Rendering\nRender items with flexible content:\n\n```svelte\n{#each items as item}\n\t<Slot render={item.label} />\n{/each}\n```\n\n## Notes\n\n- Automatically handles different content types\n- Safely renders null/undefined as empty\n- Class is applied to the wrapper element\n- Useful for building flexible, reusable components\n";
131
- readonly theme: "\n# Theme\n\n`Theme` owns global theme selection, runtime design tokens, shared overlay state, and theme\ntransitions. Wrap the application once and use the `ThemeState` received by the children snippet.\n\n## Runtime design tokens\n\n```svelte\n<script lang=\"ts\">\n\timport { Theme, type ThemeDesignTokenMap } from 'entasis/theme';\n\n\tlet spacing = $state<'small' | 'normal' | 'large'>('normal');\n\tconst designTokens = $derived({\n\t\tlight: {\n\t\t\tspacing,\n\t\t\tspacingScale: { xs: 1, sm: 1.5, md: 2, lg: 3, xl: 4 },\n\t\t\tradius: 'normal',\n\t\t\ttypeScale: 'default',\n\t\t\traisedWithBorder: true,\n\t\t\tdefaultColor: 'neutral'\n\t\t},\n\t\tdark: {\n\t\t\tspacing,\n\t\t\tspacingScale: { xs: 1, sm: 1.5, md: 2, lg: 3, xl: 4 },\n\t\t\tradius: 'small',\n\t\t\ttypeScale: 'compact',\n\t\t\traisedWithBorder: false,\n\t\t\tdefaultColor: 'neutral'\n\t\t}\n\t} satisfies ThemeDesignTokenMap<readonly ['light', 'dark']>);\n</script>\n\n<Theme {designTokens} transition=\"radial-top-right\">\n\t{#snippet children(theme)}\n\t\t<button onclick={() => (spacing = spacing === 'small' ? 'large' : 'small')}>\n\t\t\tChange density\n\t\t</button>\n\t\t<button onclick={() => (theme.theme = theme.resolvedTheme === 'dark' ? 'light' : 'dark')}>\n\t\t\tToggle color scheme\n\t\t</button>\n\t{/snippet}\n</Theme>\n```\n\n`designTokens` is keyed by logical theme name and respects the `attribute` and `value` props.\nEleven presets ship as `themePresets` (`dense`, `compact`, `balanced`, `comfortable`, `spacious`,\n`sharp`, `rounded`, `display`, `editorial`, `glass`, `terminal`): `designTokens={{ light: themePresets.glass.tokens, dark: themePresets.glass.tokens }}`.\nChanging the controlled object updates already-rendered Tailwind utilities without rebuilding CSS.\n\n### ThemeDesignTokens\n\n- `spacing`: `'small' | 'normal' | 'large' | number`. Globally scales density.\n- `spacingScale`: partial overrides for the strictly increasing `xs`, `sm`, `md`, `lg`,\n and `xl` spacing multipliers. Defaults to 1/1.5/2/3/4.\n- `radius`: `'none' | 'subtile' | 'small' | 'normal' | 'large' | 'round' | number`.\n Controls take the full multiplier; surface steps (`lg` and up) stop at `large` (1.5×).\n- `typeScale`: `'compact' | 'default' | 'comfortable' | 'large' | TypeScaleOptions`.\n- `raisedWithBorder`: toggles the border used by `raised-*` utilities.\n- `defaultColor`: `Colors` role kit chrome inherits when a control omits `color`.\n Defaults to `neutral`. Set `primary` to restore an accent-colored kit. Compiles the\n current-color family (`--color`, `--color-readable`, muted/contrast/light/dark variants)\n and `--default-color` onto the theme selector. Do not set `data-color` on `html`.\n- `focusColor`, `selectedColor`, `hoverColor`, `pressedColor`: the four `Colors` **state\n roles**. They pin, for the whole theme, what a focus ring, a persistent selection and the\n transient hover/pressed layer look like, independently of the role of the control the state\n lands on. They compile `--color-focus`, `--color-selected` (plus its `-contrast`,\n `-readable` and `-muted-readable` companions), `--color-hover` and\n `--color-pressed` onto the theme selector. There is no `--color-selected-muted`: the soft\n fill is a translucent tint of `--color-selected` at `--state-selected-opacity`, so it reads\n on any surface. None is declared at `:root`: every use site\n falls back to the matching current role (`ring-focus` is\n `var(--color-focus, var(--color))`, `bg-selected-muted` tints\n `var(--color-selected, var(--color))`, the state layer is\n `var(--color-hover, currentColor)` and on `:active`\n `var(--color-pressed, var(--color-hover, currentColor))`), so leaving them unset changes\n nothing and `data-color` keeps moving the states with `--color`. Theme-level only: there is\n no per-component override. An unknown role throws.\n\nComponent-level density remains a local variant. It selects utility classes whose values inherit\nthe active global spacing token.\n\nGenerated interfaces should use the public `xs | sm | md | lg | xl` vocabulary through component\nprops and named gap/padding utilities. `micro` and `layout-*` are internal recipe tokens. Prefer\nparent-owned gaps over child margins; do not emit arbitrary spacing or unsupported radius values.\n\n## Theme selection\n\nThe selection props wrap `svelte-themes`: `themes`, `defaultTheme`, `forcedTheme`,\n`systemTheme`, `syncColorScheme`, `transitionOnChange`, `storageKey`, `attribute`,\n`value`, and `colorScheme`. The default themes are light and dark, with system selection\nenabled. `systemTheme`, `syncColorScheme`, and `transitionOnChange` all default to\n`true`; they map onto the library's `enableSystem`, `enableColorScheme`, and\n`disableTransitionOnChange` options.\n\n`ThemeState` exposes `theme`, `resolvedTheme`, `themes`, and `systemTheme`. Assign\n`theme.theme` to switch themes. The optional `transition` prop applies a named view transition;\nunsupported browsers and reduced-motion users switch instantly.\n\n`spinnerVariant` sets the global default spinner animation. The children snippet is required.\n\n## Motion tokens\n\nMotion is a token scale like spacing and radius: five duration steps and four easing roles.\n`motion` retunes them app-wide; an omitted token keeps its default.\n\n| Duration | Default | | Easing role | Default |\n| ---------- | ------- | --- | ------------ | ------------- |\n| `instant` | 0ms | | `standard` | `cubicInOut` |\n| `fast` | 100ms | | `enter` | `cubicOut` |\n| `normal` | 200ms | | `exit` | `cubicIn` |\n| `slow` | 300ms | | `emphasized` | `backOut` |\n| `slower` | 500ms | | | |\n\n```svelte\n<script lang=\"ts\">\n\timport { Theme } from 'entasis/theme';\n\n\tlet { children } = $props();\n</script>\n\n<Theme motion={{ duration: { normal: 150, slow: 260 }, easing: { standard: 'quintOut' } }}>\n\t{@render children()}\n</Theme>\n```\n\nThe Tailwind plugin emits the same scale as CSS variables on `html` (`--duration-normal`,\n`--ease-standard`, ...) plus the matching `duration-*` / `ease-*` utilities, so CSS transitions\nand Svelte transitions read one set of numbers. The `motion` prop rewrites those variables on\n`html` at runtime and `designTokens.motion` rewrites them again per theme, layered over the prop;\n`ThemeState.motion` resolves through the same two rungs, so the utilities and the presets never\ndisagree. `ThemeState.transition` is a deprecated alias for its `normal` duration and `standard`\neasing. Reduced motion resolves every duration to 0 and collapses the `--duration-*` variables via\nthe `data-entasis-reduce-motion` attribute on `html`.\n\nComponents keep their own transition in a reserved `motion` slot on their theme, so the `theme`\nprop covers motion as well as classes:\n\n```svelte\n<script lang=\"ts\">\n\timport { Dialog } from 'entasis/dialog';\n</script>\n\n<Dialog theme={{ motion: { duration: 'fast', easing: 'emphasized' } }} title=\"Quick\">Body</Dialog>\n```\n\n## Component theme registry\n\n`components` sets app-wide component theme defaults without a wrapper component per component:\nit is keyed by theme name (`dialog`, `button`, ...) and each entry takes the same slots as that\ncomponent's `theme` prop, the `motion` slot included. A `set<Component>Theme` call in a subtree\nbeats the registry, and an instance `theme` prop beats both — per slot: each rung layers on the one\nbelow it, so a subtree that restyles one slot keeps the registry's others.\n\n```svelte\n<script lang=\"ts\">\n\timport { Theme } from 'entasis/theme';\n\n\tlet { children } = $props();\n</script>\n\n<Theme\n\tcomponents={{\n\t\tdialog: { motion: { duration: 'fast' }, content: { base: 'rounded-2xl' } },\n\t\tbutton: { root: { base: 'tracking-wide' } }\n\t}}\n>\n\t{@render children()}\n</Theme>\n```\n\n## Reduced motion\n\n`reduceMotion` forces reduced motion on (`true`) or off (`false`) for every entasis animation,\noverriding the OS `prefers-reduced-motion` setting; omit it to follow the OS. The live result is\nexposed as `ThemeState.preferReducesMotion` (reactive, so it updates when the OS setting changes)\nand mirrored as a `data-entasis-reduce-motion` attribute on `html` for CSS-only animations.\n\n```svelte\n<script>\n\timport { Theme } from 'entasis/theme';\n\n\tlet { children } = $props();\n</script>\n\n<Theme reduceMotion>{@render children()}</Theme>\n```\n\n## Build-time boundary\n\nThe Tailwind plugin still generates color palettes and registers utility names, variants,\nkeyframes, and spinner CSS. Spacing, radius, typography scale, raised borders,\n`defaultColor` and the four state roles (`focusColor`, `selectedColor`, `hoverColor`,\n`pressedColor`) belong to `Theme.designTokens`; colors remain CSS variables and can be\noverridden directly. `ThemeState.defaultColor` exposes the active role. Kit chrome should\nresolve omitted `color` props with `useDefaultColor`.\n";
131
+ readonly theme: "\n# Theme\n\n`Theme` owns global theme selection, runtime design tokens, shared overlay state, and theme\ntransitions. Wrap the application once and use the `ThemeState` received by the children snippet.\nComponents below it read the same state with `useTheme()` from `entasis/theme`, called during\ncomponent initialisation; it throws when no `Theme` is above. For a palette computed at runtime,\nsee `generateColorPalette` in `entasis/color-palette`.\n\n## Runtime design tokens\n\n```svelte\n<script lang=\"ts\">\n\timport { Theme, type ThemeDesignTokenMap } from 'entasis/theme';\n\n\tlet spacing = $state<'small' | 'normal' | 'large'>('normal');\n\tconst designTokens = $derived({\n\t\tlight: {\n\t\t\tspacing,\n\t\t\tspacingScale: { xs: 1, sm: 1.5, md: 2, lg: 3, xl: 4 },\n\t\t\tradius: 'normal',\n\t\t\ttypeScale: 'default',\n\t\t\traisedWithBorder: true,\n\t\t\tdefaultColor: 'neutral'\n\t\t},\n\t\tdark: {\n\t\t\tspacing,\n\t\t\tspacingScale: { xs: 1, sm: 1.5, md: 2, lg: 3, xl: 4 },\n\t\t\tradius: 'small',\n\t\t\ttypeScale: 'compact',\n\t\t\traisedWithBorder: false,\n\t\t\tdefaultColor: 'neutral'\n\t\t}\n\t} satisfies ThemeDesignTokenMap<readonly ['light', 'dark']>);\n</script>\n\n<Theme {designTokens} transition=\"radial-top-right\">\n\t{#snippet children(theme)}\n\t\t<button onclick={() => (spacing = spacing === 'small' ? 'large' : 'small')}>\n\t\t\tChange density\n\t\t</button>\n\t\t<button onclick={() => (theme.theme = theme.resolvedTheme === 'dark' ? 'light' : 'dark')}>\n\t\t\tToggle color scheme\n\t\t</button>\n\t{/snippet}\n</Theme>\n```\n\n`designTokens` is keyed by logical theme name and respects the `attribute` and `value` props.\nEleven presets ship as `themePresets` (`dense`, `compact`, `balanced`, `comfortable`, `spacious`,\n`sharp`, `rounded`, `display`, `editorial`, `glass`, `terminal`): `designTokens={{ light: themePresets.glass.tokens, dark: themePresets.glass.tokens }}`.\nChanging the controlled object updates already-rendered Tailwind utilities without rebuilding CSS.\n\n### ThemeDesignTokens\n\n- `spacing`: `'small' | 'normal' | 'large' | number`. Globally scales density.\n- `spacingScale`: partial overrides for the strictly increasing `xs`, `sm`, `md`, `lg`,\n and `xl` spacing multipliers. Defaults to 1/1.5/2/3/4.\n- `radius`: `'none' | 'subtile' | 'small' | 'normal' | 'large' | 'round' | number`.\n Controls take the full multiplier; surface steps (`lg` and up) stop at `large` (1.5×).\n- `typeScale`: `'compact' | 'default' | 'comfortable' | 'large' | TypeScaleOptions`.\n- `raisedWithBorder`: toggles the border used by `raised-*` utilities.\n- `defaultColor`: `Colors` role kit chrome inherits when a control omits `color`.\n Defaults to `neutral`. Set `primary` to restore an accent-colored kit. Compiles the\n current-color family (`--color`, `--color-readable`, muted/contrast/light/dark variants)\n and `--default-color` onto the theme selector. Do not set `data-color` on `html`.\n- `focusColor`, `selectedColor`, `hoverColor`, `pressedColor`: the four `Colors` **state\n roles**. They pin, for the whole theme, what a focus ring, a persistent selection and the\n transient hover/pressed layer look like, independently of the role of the control the state\n lands on. They compile `--color-focus`, `--color-selected` (plus its `-contrast`,\n `-readable` and `-muted-readable` companions), `--color-hover` and\n `--color-pressed` onto the theme selector. There is no `--color-selected-muted`: the soft\n fill is a translucent tint of `--color-selected` at `--state-selected-opacity`, so it reads\n on any surface. None is declared at `:root`: every use site\n falls back to the matching current role (`ring-focus` is\n `var(--color-focus, var(--color))`, `bg-selected-muted` tints\n `var(--color-selected, var(--color))`, the state layer is\n `var(--color-hover, currentColor)` and on `:active`\n `var(--color-pressed, var(--color-hover, currentColor))`), so leaving them unset changes\n nothing and `data-color` keeps moving the states with `--color`. Theme-level only: there is\n no per-component override. An unknown role throws.\n\nComponent-level density remains a local variant. It selects utility classes whose values inherit\nthe active global spacing token.\n\nGenerated interfaces should use the public `xs | sm | md | lg | xl` vocabulary through component\nprops and named gap/padding utilities. `micro` and `layout-*` are internal recipe tokens. Prefer\nparent-owned gaps over child margins; do not emit arbitrary spacing or unsupported radius values.\n\n## Theme selection\n\nThe selection props wrap `svelte-themes`: `themes`, `defaultTheme`, `forcedTheme`,\n`systemTheme`, `syncColorScheme`, `transitionOnChange`, `storageKey`, `attribute`,\n`value`, and `colorScheme`. The default themes are light and dark, with system selection\nenabled. `systemTheme`, `syncColorScheme`, and `transitionOnChange` all default to\n`true`; they map onto the library's `enableSystem`, `enableColorScheme`, and\n`disableTransitionOnChange` options.\n\n`ThemeState` exposes `theme`, `resolvedTheme`, `themes`, and `systemTheme`. Assign\n`theme.theme` to switch themes. The optional `transition` prop applies a named view transition;\nunsupported browsers and reduced-motion users switch instantly.\n\n`spinnerVariant` sets the global default spinner animation. The children snippet is required.\n\n## Motion tokens\n\nMotion is a token scale like spacing and radius: five duration steps and four easing roles.\n`motion` retunes them app-wide; an omitted token keeps its default.\n\n| Duration | Default | | Easing role | Default |\n| ---------- | ------- | --- | ------------ | ------------- |\n| `instant` | 0ms | | `standard` | `cubicInOut` |\n| `fast` | 100ms | | `enter` | `cubicOut` |\n| `normal` | 200ms | | `exit` | `cubicIn` |\n| `slow` | 300ms | | `emphasized` | `backOut` |\n| `slower` | 500ms | | | |\n\n```svelte\n<script lang=\"ts\">\n\timport { Theme } from 'entasis/theme';\n\n\tlet { children } = $props();\n</script>\n\n<Theme motion={{ duration: { normal: 150, slow: 260 }, easing: { standard: 'quintOut' } }}>\n\t{@render children()}\n</Theme>\n```\n\nThe Tailwind plugin emits the same scale as CSS variables on `html` (`--duration-normal`,\n`--ease-standard`, ...) plus the matching `duration-*` / `ease-*` utilities, so CSS transitions\nand Svelte transitions read one set of numbers. The `motion` prop rewrites those variables on\n`html` at runtime and `designTokens.motion` rewrites them again per theme, layered over the prop;\n`ThemeState.motion` resolves through the same two rungs, so the utilities and the presets never\ndisagree. `ThemeState.transition` is a deprecated alias for its `normal` duration and `standard`\neasing. Reduced motion resolves every duration to 0 and collapses the `--duration-*` variables via\nthe `data-entasis-reduce-motion` attribute on `html`.\n\nComponents keep their own transition in a reserved `motion` slot on their theme, so the `theme`\nprop covers motion as well as classes:\n\n```svelte\n<script lang=\"ts\">\n\timport { Dialog } from 'entasis/dialog';\n</script>\n\n<Dialog theme={{ motion: { duration: 'fast', easing: 'emphasized' } }} title=\"Quick\">Body</Dialog>\n```\n\n## Component theme registry\n\n`components` sets app-wide component theme defaults without a wrapper component per component:\nit is keyed by theme name (`dialog`, `button`, ...) and each entry takes the same slots as that\ncomponent's `theme` prop, the `motion` slot included. A `set<Component>Theme` call in a subtree\nbeats the registry, and an instance `theme` prop beats both — per slot: each rung layers on the one\nbelow it, so a subtree that restyles one slot keeps the registry's others.\n\n```svelte\n<script lang=\"ts\">\n\timport { Theme } from 'entasis/theme';\n\n\tlet { children } = $props();\n</script>\n\n<Theme\n\tcomponents={{\n\t\tdialog: { motion: { duration: 'fast' }, content: { base: 'rounded-2xl' } },\n\t\tbutton: { root: { base: 'tracking-wide' } }\n\t}}\n>\n\t{@render children()}\n</Theme>\n```\n\n## Reduced motion\n\n`reduceMotion` forces reduced motion on (`true`) or off (`false`) for every entasis animation,\noverriding the OS `prefers-reduced-motion` setting; omit it to follow the OS. The live result is\nexposed as `ThemeState.preferReducesMotion` (reactive, so it updates when the OS setting changes)\nand mirrored as a `data-entasis-reduce-motion` attribute on `html` for CSS-only animations.\n\n```svelte\n<script>\n\timport { Theme } from 'entasis/theme';\n\n\tlet { children } = $props();\n</script>\n\n<Theme reduceMotion>{@render children()}</Theme>\n```\n\n## Build-time boundary\n\nThe Tailwind plugin still generates color palettes and registers utility names, variants,\nkeyframes, and spinner CSS. Spacing, radius, typography scale, raised borders,\n`defaultColor` and the four state roles (`focusColor`, `selectedColor`, `hoverColor`,\n`pressedColor`) belong to `Theme.designTokens`; colors remain CSS variables and can be\noverridden directly. `ThemeState.defaultColor` exposes the active role. Kit chrome should\nresolve omitted `color` props with `useDefaultColor`.\n";
132
132
  readonly i18n: "\n# Internationalization\n\nImport from `entasis/i18n`.\n\nI18n provides locale messages to child components. setI18n and useI18n set and read the component context. en is the English message set; Messages and I18nInput describe translation inputs. locales and localeList expose the supported locale inventory with LocaleCode and LocaleMeta types.\n";
133
133
  readonly 'tailwind-plugin': "\n# Main Tailwind plugin\n\n`@plugin 'entasis/tailwind-plugin'`\n\nThis palette-agnostic plugin registers shared utilities, variants, keyframes, and spinner CSS.\nUse it when colors are defined separately instead of through the theme plugin.\n\n## Configuration\n\n`spinner`\n- **Type**: `Spinner` object\n- **Default**: Auto-generated\n- **Description**: Custom spinner configuration for the `.ui-spinner` class\n\n## Shared utilities\n\n### `.state-layer`\n- Composites `currentColor` at `--state-hover-opacity` on hover and `data-highlighted=\"true\"`\n- Composites `currentColor` at `--state-pressed-opacity` on `:active`\n- Does not activate for disabled, `data-disabled`, or `aria-disabled=\"true\"` elements\n- Theme plugin defaults the opacities to 5% and 10% in light themes, and 16% and 32% in dark themes\n\n### `bg-selected-muted` / `rounded-<step>-concentric`\n- `bg-selected-muted` composites `var(--color-selected, var(--color))` at\n `--state-selected-opacity` (0.07 light, 0.10 dark), so a persistent selection reads the same on\n `surface`, `surface-raised` and `surface-floating`. `bg-color-muted` stays opaque.\n- `rounded-<step>-concentric` (also `rounded-t-<step>-concentric` /\n `rounded-b-<step>-concentric`, with `<step>` one of `xs sm md lg xl 2xl 3xl 4xl`) keeps the\n child on its own design step but caps it at what concentricity allows inside a rounded padded\n container:\n `min(var(--radius-<step>), var(--radius-parent) - max(--pad-parent-x, --pad-parent-y))`. The\n container declares nothing: every `rounded-<step>` publishes `--radius-parent` to its\n children and `p` / `px` / `py` publish `--pad-parent-x/-y`, so the two boxes stay\n concentric at every `radius` preset. Outside any rounded container the parent radius is\n infinite, so the child is exactly its step; a negative difference clamps to 0, a square corner.\n Put it on a child that sits flush against the padding box; a floating child (an avatar, a\n Button, a Chip) keeps its own radius.\n The padding half reads the other way: `px|py-<step>-concentric` is\n `max(space-<step>, min(radius-parent / 2, space-<step> * 3))`, so a flush bar's content clears a\n large container corner (a very round theme over a compact title bar) and stays the plain step\n otherwise.\n\nConfigure spacing, radius, typography scale, and raised borders at runtime through\n`Theme.designTokens`.\n\nSemantic spacing utilities use the active theme's `xs`, `sm`, `md`, `lg`, and `xl`\nscale for gaps, padding, margins, and physical insets. For example, `gap-lg`, `p-lg`,\n`top-lg`, and `left-lg` use the same spacing value. Inset utilities support `top`,\n`right`, `bottom`, and `left`, including responsive variants.\n";
134
134
  readonly 'theme-tailwind-plugin': "\n# Theme Tailwind plugin\n\n`@plugin 'entasis/tailwind-plugin/theme'` generates color variables for each named theme. The declaration\nmarked `default: true` also registers the shared utility vocabulary, variants, spinner styles,\nand keyframes.\n\n```css\n@import 'tailwindcss';\n\n@plugin 'entasis/tailwind-plugin/theme' {\n\tname: light;\n\tdefault: true;\n\tcolorscheme: light;\n\tprimary: #5f62ef;\n\tsecondary: #e4e4e7;\n\tsurface: #fafafa;\n\tneutral: #18181b;\n}\n\n@plugin 'entasis/tailwind-plugin/theme' {\n\tname: dark;\n\tcolorscheme: dark;\n\tprimary: #5f62ef;\n\tsecondary: #27272a;\n\tsurface: #09090b;\n\tneutral: #fafafa;\n}\n```\n\n## Identity\n\n- `name: string` scopes variables to `html[data-theme=\"<name>\"]` and `.<name>`.\n- `default: boolean` also applies the palette to bare `html` and installs the shared engine.\n- `colorscheme: 'light' | 'dark'` controls mode-aware color defaults.\n- `prefersDark: boolean` also emits the palette under the dark system media query.\n\n## Palette inputs\n\nBase semantic colors are `primary`, `secondary`, `danger`, `success`, `warning`, `info`,\nand `neutral`. `surface` seeds the elevation ladder: `surface-recessed`,\n`surface-canvas`, `surface`, `surface-raised`, and `surface-floating`.\n\nEach semantic color supports explicit `-light`, `-lighter`, `-dark`, `-muted`,\n`-contrast`, `-readable`, and `-muted-readable` overrides. Missing variants are generated.\n`luminance` and `saturation` adjust the generated palette.\n\n`state-hover-opacity` and `state-pressed-opacity` calibrate the CSS variables consumed by\n`.state-layer`. They default to 0.05/0.10 in light mode and 0.16/0.32 in dark mode. `state-layer-none` switches that overlay off on one element.\n`state-selected-opacity` (0.07 light, 0.10 dark) is the alpha `bg-selected-muted` composites the\nselected role at: the soft selection fill is a translucent tint, not an opaque colour, so it reads\nthe same on `surface`, `surface-raised` and `surface-floating`.\n\n## Runtime boundary\n\nSpacing, radius, typography scale, raised borders, `defaultColor` and the four state roles\n(`focusColor`, `selectedColor`, `hoverColor`, `pressedColor`) are not plugin options.\nConfigure them with the `designTokens` prop on `Theme`. Tailwind still discovers and compiles\nthe finite utility names; runtime theming changes the CSS variables those utilities consume.\n\nThe public spacing vocabulary is `xs | sm | md | lg | xl`, available through named gap, padding,\nand margin utilities such as `gap-md` and `px-lg`. The `micro` and `layout-*` values are\ninternal component-recipe tokens. Generated interfaces should prefer Stack/Grid gaps and must not\nemit arbitrary spacing or unsupported radius utilities.\n\nColor variables can also be overridden directly at runtime:\n\n```css\nhtml[data-theme='light'] {\n\t--color-primary: oklab(0.21 0.01 -0.03);\n}\n```\n";
135
+ readonly 'color-palette': "\n# Color palette\n\n`generateColorPalette` from `entasis/color-palette` computes, at runtime, the palette the theme\nplugin writes for one theme. It takes the same inputs as a `@plugin 'entasis/tailwind-plugin/theme'`\ndeclaration (`primary`, `secondary`, `danger`, `success`, `warning`, `info`, `neutral`,\n`surface`, their `-light`/`-muted`/… overrides, `colorscheme`, `luminance`, `saturation`\nand the `state-*-opacity` values) and does not import Tailwind.\n\n```svelte\n<script lang=\"ts\">\n\timport { generateColorPalette } from 'entasis/color-palette';\n\n\tlet brand = $state('#0f766e');\n\tconst palette = $derived(\n\t\tgenerateColorPalette({ colorscheme: 'light', primary: brand, surface: '#fafafa', neutral: '#18181b' })\n\t);\n\tconst style = $derived(\n\t\tObject.entries(palette.cssVariables)\n\t\t\t.map(([name, value]) => `${name}: ${value}`)\n\t\t\t.join('; ')\n\t);\n</script>\n\n<div {style}>\n\t<!-- Components in here use the generated palette. -->\n</div>\n```\n\n- `cssVariables` maps `--color-*` and `--state-*` custom properties to values. Setting them on an\n element themes its subtree, since every utility reads those variables.\n- `colorsPalette` holds the resolved hex colours for each role and variant.\n- Spacing, radius, typography and the state roles stay on `Theme`'s `designTokens`.\n";
135
136
  readonly types: "\n# Shared theme types\n\nImport from `entasis/types`.\n\nColors names semantic palette roles. Sizes selects small, normal, or large component geometry. Density uses a separate compact/normal/comfortable scale for internal whitespace, so a density value can never be passed where a size is expected. The module also exports theme color paths, typography paths, transition easing, style types, and deepMerge for composing nested theme values.\n";
136
137
  readonly cva: "\n# Component variants\n\nImport from `entasis/cva`.\n\ncva defines class variants and defaults. cx joins class values; compose composes variants. setComponentTheme and useComponentTheme connect component theme definitions to the Svelte theme context. A resolver takes an optional second argument, the shared variant values, and then returns every class slot already bound to them, so a template calls `slots.root()` instead of passing the same props to each slot. VariantProps and InferComponentTheme derive the corresponding public types. Keep component CVA definitions beside their owner in a .theme.ts file.\n";
137
138
  readonly scheduling: "\n# Scheduling layout\n\nImport from `entasis/scheduling`.\n\npackSchedulingLanes and packSchedulingOverlaps compute placements for scheduling intervals. SchedulingInterval, SchedulingLaneInterval, the lane placement and layout types, and the overlap placement and layout types describe their inputs and results. EventCalendar and GanttChart share these layout algorithms.\n";