@poodle64/ui 2026.10.17 → 2026.10.18

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 (57) hide show
  1. package/README.md +94 -11
  2. package/dist/components/feedback/report-widget.svelte +4 -1
  3. package/dist/components/feedback/report-widget.svelte.d.ts +1 -0
  4. package/dist/components/ui/app-identity/app-identity.svelte +7 -2
  5. package/dist/components/ui/app-shell/texture.d.ts +5 -5
  6. package/dist/components/ui/app-shell/texture.js +3 -3
  7. package/dist/components/ui/breadcrumbs/breadcrumbs.svelte +70 -0
  8. package/dist/components/ui/breadcrumbs/breadcrumbs.svelte.d.ts +32 -0
  9. package/dist/components/ui/breadcrumbs/index.d.ts +2 -0
  10. package/dist/components/ui/breadcrumbs/index.js +2 -0
  11. package/dist/components/ui/content-skeleton/content-skeleton.svelte +94 -0
  12. package/dist/components/ui/content-skeleton/content-skeleton.svelte.d.ts +25 -0
  13. package/dist/components/ui/content-skeleton/index.d.ts +2 -0
  14. package/dist/components/ui/content-skeleton/index.js +2 -0
  15. package/dist/components/ui/document-reader/document-reader.svelte +336 -0
  16. package/dist/components/ui/document-reader/document-reader.svelte.d.ts +65 -0
  17. package/dist/components/ui/document-reader/index.d.ts +2 -0
  18. package/dist/components/ui/document-reader/index.js +2 -0
  19. package/dist/components/ui/page-container/index.d.ts +2 -0
  20. package/dist/components/ui/page-container/index.js +2 -0
  21. package/dist/components/ui/page-container/page-container.svelte +39 -0
  22. package/dist/components/ui/page-container/page-container.svelte.d.ts +24 -0
  23. package/dist/components/ui/page-header/page-header.svelte +12 -7
  24. package/dist/components/ui/page-header/page-header.svelte.d.ts +3 -2
  25. package/dist/components/ui/page-section/index.d.ts +2 -0
  26. package/dist/components/ui/page-section/index.js +2 -0
  27. package/dist/components/ui/page-section/page-section.svelte +92 -0
  28. package/dist/components/ui/page-section/page-section.svelte.d.ts +36 -0
  29. package/dist/components/ui/sheet/index.d.ts +13 -0
  30. package/dist/components/ui/sheet/index.js +15 -0
  31. package/dist/components/ui/sheet/sheet-body.svelte +21 -0
  32. package/dist/components/ui/sheet/sheet-body.svelte.d.ts +5 -0
  33. package/dist/components/ui/sheet/sheet-close.svelte +11 -0
  34. package/dist/components/ui/sheet/sheet-close.svelte.d.ts +4 -0
  35. package/dist/components/ui/sheet/sheet-content.svelte +70 -0
  36. package/dist/components/ui/sheet/sheet-content.svelte.d.ts +37 -0
  37. package/dist/components/ui/sheet/sheet-description.svelte +20 -0
  38. package/dist/components/ui/sheet/sheet-description.svelte.d.ts +4 -0
  39. package/dist/components/ui/sheet/sheet-footer.svelte +23 -0
  40. package/dist/components/ui/sheet/sheet-footer.svelte.d.ts +5 -0
  41. package/dist/components/ui/sheet/sheet-header.svelte +21 -0
  42. package/dist/components/ui/sheet/sheet-header.svelte.d.ts +5 -0
  43. package/dist/components/ui/sheet/sheet-overlay.svelte +20 -0
  44. package/dist/components/ui/sheet/sheet-overlay.svelte.d.ts +4 -0
  45. package/dist/components/ui/sheet/sheet-portal.svelte +7 -0
  46. package/dist/components/ui/sheet/sheet-portal.svelte.d.ts +3 -0
  47. package/dist/components/ui/sheet/sheet-title.svelte +21 -0
  48. package/dist/components/ui/sheet/sheet-title.svelte.d.ts +4 -0
  49. package/dist/components/ui/sheet/sheet-trigger.svelte +11 -0
  50. package/dist/components/ui/sheet/sheet-trigger.svelte.d.ts +4 -0
  51. package/dist/components/ui/sheet/sheet.svelte +7 -0
  52. package/dist/components/ui/sheet/sheet.svelte.d.ts +3 -0
  53. package/dist/components/ui/skeleton/skeleton.svelte +1 -1
  54. package/dist/styles.css +8 -0
  55. package/package.json +1 -1
  56. package/registry/component-map.json +43 -4
  57. package/registry/component-map.md +9 -4
@@ -0,0 +1,36 @@
1
+ /**
2
+ * A section of a page, under its PageHeader: a heading, an optional line of
3
+ * explanation, trailing actions, and the section's content at one distance
4
+ * below them.
5
+ *
6
+ * One app had five recipes for this one role, from an 11px uppercase kicker
7
+ * to a 20px display heading, and each page spaced its content from the
8
+ * heading differently. The heading and the gap under it are this
9
+ * component's, because those are the halves that drift.
10
+ *
11
+ * Level 2 is a section, in the display face one step under the page title.
12
+ * Level 3 is a subsection, in the body face at a Panel title's size, so a
13
+ * titled card and a subsection read as the same rank. There is no uppercase
14
+ * kicker form: a small caps label above content is the column head of a
15
+ * table or a nav group, never a heading.
16
+ */
17
+ import type { Snippet } from 'svelte';
18
+ import type { HTMLAttributes } from 'svelte/elements';
19
+ import { type WithElementRef } from '../../../utils.js';
20
+ type $$ComponentProps = Omit<WithElementRef<HTMLAttributes<HTMLElement>>, 'title'> & {
21
+ title: string;
22
+ /** 2, a section of the page; 3, a section within one. */
23
+ level?: 2 | 3;
24
+ /** A sentence or two under the heading. It wraps. */
25
+ description?: string;
26
+ /** How many things the section holds, set beside the heading. */
27
+ count?: number;
28
+ /** Controls for the section, trailing the heading. */
29
+ actions?: Snippet;
30
+ children?: Snippet;
31
+ /** The heading's id, for a link to the section. One is generated otherwise. */
32
+ id?: string;
33
+ };
34
+ declare const PageSection: import("svelte").Component<$$ComponentProps, {}, "ref">;
35
+ type PageSection = ReturnType<typeof PageSection>;
36
+ export default PageSection;
@@ -0,0 +1,13 @@
1
+ import Root from './sheet.svelte';
2
+ import Trigger from './sheet-trigger.svelte';
3
+ import Close from './sheet-close.svelte';
4
+ import Portal from './sheet-portal.svelte';
5
+ import Overlay from './sheet-overlay.svelte';
6
+ import Content from './sheet-content.svelte';
7
+ import Header from './sheet-header.svelte';
8
+ import Body from './sheet-body.svelte';
9
+ import Footer from './sheet-footer.svelte';
10
+ import Title from './sheet-title.svelte';
11
+ import Description from './sheet-description.svelte';
12
+ export { sheetVariants, type SheetSide } from './sheet-content.svelte';
13
+ export { Root, Trigger, Close, Portal, Overlay, Content, Header, Body, Footer, Title, Description, Root as Sheet, Trigger as SheetTrigger, Close as SheetClose, Portal as SheetPortal, Overlay as SheetOverlay, Content as SheetContent, Header as SheetHeader, Body as SheetBody, Footer as SheetFooter, Title as SheetTitle, Description as SheetDescription };
@@ -0,0 +1,15 @@
1
+ import Root from './sheet.svelte';
2
+ import Trigger from './sheet-trigger.svelte';
3
+ import Close from './sheet-close.svelte';
4
+ import Portal from './sheet-portal.svelte';
5
+ import Overlay from './sheet-overlay.svelte';
6
+ import Content from './sheet-content.svelte';
7
+ import Header from './sheet-header.svelte';
8
+ import Body from './sheet-body.svelte';
9
+ import Footer from './sheet-footer.svelte';
10
+ import Title from './sheet-title.svelte';
11
+ import Description from './sheet-description.svelte';
12
+ export { sheetVariants } from './sheet-content.svelte';
13
+ export { Root, Trigger, Close, Portal, Overlay, Content, Header, Body, Footer, Title, Description,
14
+ //
15
+ Root as Sheet, Trigger as SheetTrigger, Close as SheetClose, Portal as SheetPortal, Overlay as SheetOverlay, Content as SheetContent, Header as SheetHeader, Body as SheetBody, Footer as SheetFooter, Title as SheetTitle, Description as SheetDescription };
@@ -0,0 +1,21 @@
1
+ <script lang="ts">
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+ import { cn, type WithElementRef } from '../../../utils.js';
4
+
5
+ let {
6
+ ref = $bindable(null),
7
+ class: className,
8
+ children,
9
+ ...restProps
10
+ }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
11
+ </script>
12
+
13
+ <!-- The part that scrolls, so the header and footer stay put. -->
14
+ <div
15
+ bind:this={ref}
16
+ data-slot="sheet-body"
17
+ class={cn('min-h-0 flex-1 overflow-y-auto p-4', className)}
18
+ {...restProps}
19
+ >
20
+ {@render children?.()}
21
+ </div>
@@ -0,0 +1,5 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+ import { type WithElementRef } from '../../../utils.js';
3
+ declare const SheetBody: import("svelte").Component<WithElementRef<HTMLAttributes<HTMLDivElement>>, {}, "ref">;
4
+ type SheetBody = ReturnType<typeof SheetBody>;
5
+ export default SheetBody;
@@ -0,0 +1,11 @@
1
+ <script lang="ts">
2
+ import { Dialog as DialogPrimitive } from 'bits-ui';
3
+
4
+ let {
5
+ ref = $bindable(null),
6
+ type = 'button',
7
+ ...restProps
8
+ }: DialogPrimitive.CloseProps = $props();
9
+ </script>
10
+
11
+ <DialogPrimitive.Close bind:ref data-slot="sheet-close" {type} {...restProps} />
@@ -0,0 +1,4 @@
1
+ import { Dialog as DialogPrimitive } from 'bits-ui';
2
+ declare const SheetClose: import("svelte").Component<DialogPrimitive.TriggerProps, {}, "ref">;
3
+ type SheetClose = ReturnType<typeof SheetClose>;
4
+ export default SheetClose;
@@ -0,0 +1,70 @@
1
+ <script lang="ts" module>
2
+ import { tv, type VariantProps } from 'tailwind-variants';
3
+
4
+ // A sheet stands on the hairline-and-ladder depth every other kit surface
5
+ // uses (README §Depth): an edge border against the dimmed page, no shadow.
6
+ // A side sheet takes a phone's whole width: a sliver of dimmed page beside
7
+ // it is too narrow to read and too narrow to aim at.
8
+ export const sheetVariants = tv({
9
+ base: 'bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out border-border fixed z-50 flex flex-col text-sm outline-none duration-200 ease-out data-closed:duration-150',
10
+ variants: {
11
+ side: {
12
+ right: 'data-closed:slide-out-to-right data-open:slide-in-from-right inset-y-0 right-0 h-full w-full border-l sm:max-w-sm',
13
+ left: 'data-closed:slide-out-to-left data-open:slide-in-from-left inset-y-0 left-0 h-full w-full border-r sm:max-w-sm',
14
+ bottom: 'data-closed:slide-out-to-bottom data-open:slide-in-from-bottom inset-x-0 bottom-0 max-h-[85%] rounded-t-xl border-t',
15
+ top: 'data-closed:slide-out-to-top data-open:slide-in-from-top inset-x-0 top-0 max-h-[85%] rounded-b-xl border-b'
16
+ }
17
+ },
18
+ defaultVariants: { side: 'right' }
19
+ });
20
+
21
+ export type SheetSide = VariantProps<typeof sheetVariants>['side'];
22
+ </script>
23
+
24
+ <script lang="ts">
25
+ import { Dialog as SheetPrimitive } from 'bits-ui';
26
+ import type { ComponentProps, Snippet } from 'svelte';
27
+ import XIcon from '@lucide/svelte/icons/x';
28
+ import { cn, type WithoutChildrenOrChild } from '../../../utils.js';
29
+ import { Button } from '../button/index.js';
30
+ import SheetOverlay from './sheet-overlay.svelte';
31
+ import SheetPortal from './sheet-portal.svelte';
32
+
33
+ let {
34
+ ref = $bindable(null),
35
+ class: className,
36
+ side = 'right',
37
+ portalProps,
38
+ showCloseButton = true,
39
+ children,
40
+ ...restProps
41
+ }: WithoutChildrenOrChild<SheetPrimitive.ContentProps> & {
42
+ side?: SheetSide;
43
+ portalProps?: WithoutChildrenOrChild<ComponentProps<typeof SheetPortal>>;
44
+ showCloseButton?: boolean;
45
+ children: Snippet;
46
+ } = $props();
47
+ </script>
48
+
49
+ <SheetPortal {...portalProps}>
50
+ <SheetOverlay />
51
+ <SheetPrimitive.Content
52
+ bind:ref
53
+ data-slot="sheet-content"
54
+ data-side={side}
55
+ class={cn(sheetVariants({ side }), className)}
56
+ {...restProps}
57
+ >
58
+ {@render children?.()}
59
+ {#if showCloseButton}
60
+ <SheetPrimitive.Close data-slot="sheet-close">
61
+ {#snippet child({ props })}
62
+ <Button variant="ghost" class="absolute top-3 right-3" size="icon-sm" {...props}>
63
+ <XIcon />
64
+ <span class="sr-only">Close</span>
65
+ </Button>
66
+ {/snippet}
67
+ </SheetPrimitive.Close>
68
+ {/if}
69
+ </SheetPrimitive.Content>
70
+ </SheetPortal>
@@ -0,0 +1,37 @@
1
+ import { type VariantProps } from 'tailwind-variants';
2
+ export declare const sheetVariants: import("tailwind-variants").TVReturnType<{
3
+ side: {
4
+ right: string;
5
+ left: string;
6
+ bottom: string;
7
+ top: string;
8
+ };
9
+ }, undefined, "bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out border-border fixed z-50 flex flex-col text-sm outline-none duration-200 ease-out data-closed:duration-150", {
10
+ side: {
11
+ right: string;
12
+ left: string;
13
+ bottom: string;
14
+ top: string;
15
+ };
16
+ }, undefined, import("tailwind-variants").TVReturnType<{
17
+ side: {
18
+ right: string;
19
+ left: string;
20
+ bottom: string;
21
+ top: string;
22
+ };
23
+ }, undefined, "bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out border-border fixed z-50 flex flex-col text-sm outline-none duration-200 ease-out data-closed:duration-150", unknown, unknown, undefined>>;
24
+ export type SheetSide = VariantProps<typeof sheetVariants>['side'];
25
+ import { Dialog as SheetPrimitive } from 'bits-ui';
26
+ import type { ComponentProps, Snippet } from 'svelte';
27
+ import { type WithoutChildrenOrChild } from '../../../utils.js';
28
+ import SheetPortal from './sheet-portal.svelte';
29
+ type $$ComponentProps = WithoutChildrenOrChild<SheetPrimitive.ContentProps> & {
30
+ side?: SheetSide;
31
+ portalProps?: WithoutChildrenOrChild<ComponentProps<typeof SheetPortal>>;
32
+ showCloseButton?: boolean;
33
+ children: Snippet;
34
+ };
35
+ declare const SheetContent: import("svelte").Component<$$ComponentProps, {}, "ref">;
36
+ type SheetContent = ReturnType<typeof SheetContent>;
37
+ export default SheetContent;
@@ -0,0 +1,20 @@
1
+ <script lang="ts">
2
+ import { Dialog as DialogPrimitive } from 'bits-ui';
3
+ import { cn } from '../../../utils.js';
4
+
5
+ let {
6
+ ref = $bindable(null),
7
+ class: className,
8
+ ...restProps
9
+ }: DialogPrimitive.DescriptionProps = $props();
10
+ </script>
11
+
12
+ <DialogPrimitive.Description
13
+ bind:ref
14
+ data-slot="sheet-description"
15
+ class={cn(
16
+ 'text-muted-foreground *:[a]:hover:text-foreground text-sm *:[a]:underline *:[a]:underline-offset-3',
17
+ className
18
+ )}
19
+ {...restProps}
20
+ />
@@ -0,0 +1,4 @@
1
+ import { Dialog as DialogPrimitive } from 'bits-ui';
2
+ declare const SheetDescription: import("svelte").Component<DialogPrimitive.DescriptionProps, {}, "ref">;
3
+ type SheetDescription = ReturnType<typeof SheetDescription>;
4
+ export default SheetDescription;
@@ -0,0 +1,23 @@
1
+ <script lang="ts">
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+ import { cn, type WithElementRef } from '../../../utils.js';
4
+
5
+ let {
6
+ ref = $bindable(null),
7
+ class: className,
8
+ children,
9
+ ...restProps
10
+ }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
11
+ </script>
12
+
13
+ <div
14
+ bind:this={ref}
15
+ data-slot="sheet-footer"
16
+ class={cn(
17
+ 'bg-muted/50 mt-auto flex shrink-0 flex-col-reverse gap-2 border-t p-4 sm:flex-row sm:justify-end',
18
+ className
19
+ )}
20
+ {...restProps}
21
+ >
22
+ {@render children?.()}
23
+ </div>
@@ -0,0 +1,5 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+ import { type WithElementRef } from '../../../utils.js';
3
+ declare const SheetFooter: import("svelte").Component<WithElementRef<HTMLAttributes<HTMLDivElement>>, {}, "ref">;
4
+ type SheetFooter = ReturnType<typeof SheetFooter>;
5
+ export default SheetFooter;
@@ -0,0 +1,21 @@
1
+ <script lang="ts">
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+ import { cn, type WithElementRef } from '../../../utils.js';
4
+
5
+ let {
6
+ ref = $bindable(null),
7
+ class: className,
8
+ children,
9
+ ...restProps
10
+ }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
11
+ </script>
12
+
13
+ <!-- pr-12 keeps the title clear of the content's close button. -->
14
+ <div
15
+ bind:this={ref}
16
+ data-slot="sheet-header"
17
+ class={cn('flex shrink-0 flex-col gap-1.5 border-b p-4 pr-12', className)}
18
+ {...restProps}
19
+ >
20
+ {@render children?.()}
21
+ </div>
@@ -0,0 +1,5 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+ import { type WithElementRef } from '../../../utils.js';
3
+ declare const SheetHeader: import("svelte").Component<WithElementRef<HTMLAttributes<HTMLDivElement>>, {}, "ref">;
4
+ type SheetHeader = ReturnType<typeof SheetHeader>;
5
+ export default SheetHeader;
@@ -0,0 +1,20 @@
1
+ <script lang="ts">
2
+ import { Dialog as DialogPrimitive } from 'bits-ui';
3
+ import { cn } from '../../../utils.js';
4
+
5
+ let {
6
+ ref = $bindable(null),
7
+ class: className,
8
+ ...restProps
9
+ }: DialogPrimitive.OverlayProps = $props();
10
+ </script>
11
+
12
+ <DialogPrimitive.Overlay
13
+ bind:ref
14
+ data-slot="sheet-overlay"
15
+ class={cn(
16
+ 'data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 fixed inset-0 isolate z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs',
17
+ className
18
+ )}
19
+ {...restProps}
20
+ />
@@ -0,0 +1,4 @@
1
+ import { Dialog as DialogPrimitive } from 'bits-ui';
2
+ declare const SheetOverlay: import("svelte").Component<DialogPrimitive.OverlayProps, {}, "ref">;
3
+ type SheetOverlay = ReturnType<typeof SheetOverlay>;
4
+ export default SheetOverlay;
@@ -0,0 +1,7 @@
1
+ <script lang="ts">
2
+ import { Dialog as DialogPrimitive } from 'bits-ui';
3
+
4
+ let { ...restProps }: DialogPrimitive.PortalProps = $props();
5
+ </script>
6
+
7
+ <DialogPrimitive.Portal {...restProps} />
@@ -0,0 +1,3 @@
1
+ declare const SheetPortal: import("svelte").Component<import("bits-ui").PortalProps, {}, "">;
2
+ type SheetPortal = ReturnType<typeof SheetPortal>;
3
+ export default SheetPortal;
@@ -0,0 +1,21 @@
1
+ <script lang="ts">
2
+ import { Dialog as SheetPrimitive } from 'bits-ui';
3
+ import { cn } from '../../../utils.js';
4
+
5
+ let {
6
+ ref = $bindable(null),
7
+ class: className,
8
+ ...restProps
9
+ }: SheetPrimitive.TitleProps = $props();
10
+ </script>
11
+
12
+ <!-- AppDialog's title treatment, so a sheet and a dialogue name themselves alike. -->
13
+ <SheetPrimitive.Title
14
+ bind:ref
15
+ data-slot="sheet-title"
16
+ class={cn(
17
+ 'font-display text-lg leading-tight font-semibold tracking-[var(--ds-font-display-tracking,-0.025em)]',
18
+ className
19
+ )}
20
+ {...restProps}
21
+ />
@@ -0,0 +1,4 @@
1
+ import { Dialog as SheetPrimitive } from 'bits-ui';
2
+ declare const SheetTitle: import("svelte").Component<SheetPrimitive.TitleProps, {}, "ref">;
3
+ type SheetTitle = ReturnType<typeof SheetTitle>;
4
+ export default SheetTitle;
@@ -0,0 +1,11 @@
1
+ <script lang="ts">
2
+ import { Dialog as DialogPrimitive } from 'bits-ui';
3
+
4
+ let {
5
+ ref = $bindable(null),
6
+ type = 'button',
7
+ ...restProps
8
+ }: DialogPrimitive.TriggerProps = $props();
9
+ </script>
10
+
11
+ <DialogPrimitive.Trigger bind:ref data-slot="sheet-trigger" {type} {...restProps} />
@@ -0,0 +1,4 @@
1
+ import { Dialog as DialogPrimitive } from 'bits-ui';
2
+ declare const SheetTrigger: import("svelte").Component<DialogPrimitive.TriggerProps, {}, "ref">;
3
+ type SheetTrigger = ReturnType<typeof SheetTrigger>;
4
+ export default SheetTrigger;
@@ -0,0 +1,7 @@
1
+ <script lang="ts">
2
+ import { Dialog as DialogPrimitive } from 'bits-ui';
3
+
4
+ let { open = $bindable(false), ...restProps }: DialogPrimitive.RootProps = $props();
5
+ </script>
6
+
7
+ <DialogPrimitive.Root bind:open {...restProps} />
@@ -0,0 +1,3 @@
1
+ declare const Sheet: import("svelte").Component<import("bits-ui").AlertDialogRootPropsWithoutHTML, {}, "open">;
2
+ type Sheet = ReturnType<typeof Sheet>;
3
+ export default Sheet;
@@ -12,6 +12,6 @@
12
12
  <div
13
13
  bind:this={ref}
14
14
  data-slot="skeleton"
15
- class={cn('bg-muted animate-pulse rounded-md', className)}
15
+ class={cn('bg-muted animate-pulse rounded-md motion-reduce:animate-none', className)}
16
16
  {...restProps}
17
17
  ></div>
package/dist/styles.css CHANGED
@@ -296,11 +296,19 @@
296
296
  /* Eyebrow / column-head tracking — pairs with text-2xs + uppercase. */
297
297
  --tracking-eyebrow: 0.06em;
298
298
 
299
+ /* The type scale, by role (README §The type scale). An app sets none of
300
+ these; a role with no key here is set in the component that owns it. */
301
+
299
302
  /* Page-title and large-readout size (PageHeader, StatCard). */
300
303
  --text-display: 1.75rem;
301
304
  --text-display--line-height: 2.125rem;
302
305
  --text-display--letter-spacing: -0.02em;
303
306
 
307
+ /* A section of a page, under its title (PageSection). */
308
+ --text-section: 1.25rem;
309
+ --text-section--line-height: 1.75rem;
310
+ --text-section--letter-spacing: -0.015em;
311
+
304
312
  /* Panel/card heading size — one step above the sm body default. */
305
313
  --text-body: 0.9375rem;
306
314
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poodle64/ui",
3
- "version": "2026.10.17",
3
+ "version": "2026.10.18",
4
4
  "description": "Household shared component layer: shadcn-svelte primitives (bits-ui) plus the composed page chrome every app builds its routes from, restyled by each app's @poodle64/design-tokens alias layer. One fix reaches every app.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "meta": {
3
3
  "package": "@poodle64/ui",
4
- "version": "2026.10.17",
4
+ "version": "2026.10.18",
5
5
  "generatedBy": "scripts/generate-registry.mjs",
6
6
  "source": "scripts/situations.json + package source (DO NOT EDIT the outputs by hand)",
7
- "componentCount": 70,
7
+ "componentCount": 76,
8
8
  "situationCount": 20
9
9
  },
10
10
  "situations": [
@@ -291,7 +291,7 @@
291
291
  {
292
292
  "key": "library-data",
293
293
  "title": "Library data the app fetched",
294
- "description": "The estate's shared document-library surfaces — a faceted catalogue, one collection's detail, one document's detail, a ranked search answer — rendered from plain props the app maps its own API responses into. The package never fetches and never routes: the app owns the query, the offset and every link. Configuration is the schema-driven half (SchemaForm); library data is fixed components because its shape is stable.",
294
+ "description": "The estate's shared document-library surfaces — a faceted catalogue, one collection's detail, one document's detail, a document to read with its contents, a ranked search answer — rendered from plain props the app maps its own API responses into. The package never fetches and never routes: the app owns the query, the offset and every link. Configuration is the schema-driven half (SchemaForm); library data is fixed components because its shape is stable.",
295
295
  "components": [
296
296
  {
297
297
  "name": "LibraryBrowse",
@@ -314,6 +314,13 @@
314
314
  "props": "document, loading?, error?, collectionHref?, onOpenCollection?, actions?, children?",
315
315
  "insteadOf": "a bespoke document page: identity dl, locations, tags and memberships hand-laid-out per app"
316
316
  },
317
+ {
318
+ "name": "DocumentReader",
319
+ "dir": "document-reader",
320
+ "import": "@poodle64/ui/document-reader",
321
+ "props": "title?, subtitle?, breadcrumbs?, meta?, actions?, html?, children?, contents?, contentsLabel?, aside?, footer?, loading?, error?, onRetry?",
322
+ "insteadOf": "a hand-built markdown page: its own h1, back row, table of contents, scroll-spy and max-w-4xl; or a document body in a box under DocumentDetail"
323
+ },
317
324
  {
318
325
  "name": "SearchResults",
319
326
  "dir": "search-results",
@@ -361,7 +368,7 @@
361
368
  {
362
369
  "key": "page-frame",
363
370
  "title": "The frame every route shares",
364
- "description": "The standing chrome — the app shell and its nav, the one page-title treatment, the persistent right-hand context column, a foldable working list beside the page's own content.",
371
+ "description": "The standing chrome — the app shell and its nav, the one page-title treatment and its trail, the page's own container and sections, the persistent right-hand context column, a foldable working list beside the page's own content.",
365
372
  "components": [
366
373
  {
367
374
  "name": "AppShell",
@@ -391,6 +398,27 @@
391
398
  "props": "title?, eyebrow?, breadcrumbs?, icon?, subtitle?, info?, meta?, actions?",
392
399
  "insteadOf": "a hand-written <h1> and title bar (the drift gate fails this)"
393
400
  },
401
+ {
402
+ "name": "Breadcrumbs",
403
+ "dir": "breadcrumbs",
404
+ "import": "@poodle64/ui/breadcrumbs",
405
+ "props": "items, label?",
406
+ "insteadOf": "a hand-written chevron trail in PageHeader's breadcrumbs snippet; PageHeader takes the same items as `breadcrumbs={[…]}`"
407
+ },
408
+ {
409
+ "name": "PageContainer",
410
+ "dir": "page-container",
411
+ "import": "@poodle64/ui/page-container",
412
+ "props": "children",
413
+ "insteadOf": "a per-page wrapper picking its own vertical rhythm (space-y-6, gap-10, …)"
414
+ },
415
+ {
416
+ "name": "PageSection",
417
+ "dir": "page-section",
418
+ "import": "@poodle64/ui/page-section",
419
+ "props": "title, level?, description?, count?, actions?, children?, id?",
420
+ "insteadOf": "a hand-styled section heading (an uppercase kicker, text-lg font-semibold, a display h2) and its own gap to the content under it"
421
+ },
394
422
  {
395
423
  "name": "RecordSwitcher",
396
424
  "dir": "record-switcher",
@@ -503,6 +531,13 @@
503
531
  "props": "message?",
504
532
  "insteadOf": "a bare spinner or 'Loading…' you place by hand"
505
533
  },
534
+ {
535
+ "name": "ContentSkeleton",
536
+ "dir": "content-skeleton",
537
+ "import": "@poodle64/ui/content-skeleton",
538
+ "props": "shape?, count?, label?",
539
+ "insteadOf": "bare Skeleton boxes sized by hand (h-44 rounded-xl, h-24) standing in for a list, tiles or a document body while the page's header is already drawn"
540
+ },
506
541
  {
507
542
  "name": "ErrorState",
508
543
  "dir": "error-state",
@@ -590,6 +625,10 @@
590
625
  "dir": "separator",
591
626
  "import": "@poodle64/ui/separator"
592
627
  },
628
+ {
629
+ "dir": "sheet",
630
+ "import": "@poodle64/ui/sheet"
631
+ },
593
632
  {
594
633
  "dir": "skeleton",
595
634
  "import": "@poodle64/ui/skeleton"
@@ -1,7 +1,7 @@
1
1
  # @poodle64/ui — situation → component map
2
2
 
3
3
  <!-- GENERATED by scripts/generate-registry.mjs from scripts/situations.json + package source. DO NOT EDIT. -->
4
- Generated from `@poodle64/ui@2026.10.17`. 70 components, 20 situations.
4
+ Generated from `@poodle64/ui@2026.10.18`. 76 components, 20 situations.
5
5
 
6
6
  **Read this before writing a `<div>`.** Find the SITUATION you are in below, then compose the component named for it — do not hand-build it from raw `Card` or utility classes. Import is `import { Name } from '<import path>'`. Props marked `?` are optional. This map is the retrieval step the [`frontend-design` skill] makes mandatory; the [CHI 2026 study] measured composing-from-a-registry at 95% design-system compliance against 71% for writing the CSS from a prose style guide.
7
7
 
@@ -128,13 +128,14 @@ _A config object whose shape arrives at runtime as a JSON Schema plus a JSON For
128
128
 
129
129
  ## Library data the app fetched
130
130
 
131
- _The estate's shared document-library surfaces — a faceted catalogue, one collection's detail, one document's detail, a ranked search answer — rendered from plain props the app maps its own API responses into. The package never fetches and never routes: the app owns the query, the offset and every link. Configuration is the schema-driven half (SchemaForm); library data is fixed components because its shape is stable._
131
+ _The estate's shared document-library surfaces — a faceted catalogue, one collection's detail, one document's detail, a document to read with its contents, a ranked search answer — rendered from plain props the app maps its own API responses into. The package never fetches and never routes: the app owns the query, the offset and every link. Configuration is the schema-driven half (SchemaForm); library data is fixed components because its shape is stable._
132
132
 
133
133
  | Component | Import | Key props | Reach for it instead of |
134
134
  | --- | --- | --- | --- |
135
135
  | `LibraryBrowse` | `@poodle64/ui/library-browse` | `documents, facets?, query?, total?, offset?, limit?, loading?, error?, onQueryChange?, onFacetChange?, onPageChange?, documentHref?, onOpenDocument?, toolbar?` | a hand-built catalogue page: search box, facet rail, filter chips, results table and pager re-derived per app |
136
136
  | `CollectionDetail` | `@poodle64/ui/collection-detail` | `collection, stats?, documents?, documentsTotal?, loading?, error?, documentsLoading?, documentsError?, onPageChange?, documentHref?, onOpenDocument?, actions?, children?` | a bespoke collection page assembled from raw Card, a hand-built table and its own stat rows |
137
137
  | `DocumentDetail` | `@poodle64/ui/document-detail` | `document, loading?, error?, collectionHref?, onOpenCollection?, actions?, children?` | a bespoke document page: identity dl, locations, tags and memberships hand-laid-out per app |
138
+ | `DocumentReader` | `@poodle64/ui/document-reader` | `title?, subtitle?, breadcrumbs?, meta?, actions?, html?, children?, contents?, contentsLabel?, aside?, footer?, loading?, error?, onRetry?` | a hand-built markdown page: its own h1, back row, table of contents, scroll-spy and max-w-4xl; or a document body in a box under DocumentDetail |
138
139
  | `SearchResults` | `@poodle64/ui/search-results` | `results, query?, total?, loading?, error?, resultHref?, onOpen?, onRetry?` | a hand-built ranked hit list minting its own highlight and relevance styling |
139
140
 
140
141
  ## A document a machine read, with evidence outlined
@@ -156,7 +157,7 @@ _People, organisations, sources or any entities as nodes and their relations as
156
157
 
157
158
  ## The frame every route shares
158
159
 
159
- _The standing chrome — the app shell and its nav, the one page-title treatment, the persistent right-hand context column, a foldable working list beside the page's own content._
160
+ _The standing chrome — the app shell and its nav, the one page-title treatment and its trail, the page's own container and sections, the persistent right-hand context column, a foldable working list beside the page's own content._
160
161
 
161
162
  | Component | Import | Key props | Reach for it instead of |
162
163
  | --- | --- | --- | --- |
@@ -164,6 +165,9 @@ _The standing chrome — the app shell and its nav, the one page-title treatment
164
165
  | `ShellControls` | `@poodle64/ui/app-shell` | `children` | a scope row (entity, financial year, chart/table) hand-placed above the page body; the shell's top bar carries it |
165
166
  | `Segmented` | `@poodle64/ui/segmented` | `value, options, label, size?` | a hand-built row of aria-pressed buttons for a small fixed choice |
166
167
  | `PageHeader` | `@poodle64/ui/page-header` | `title?, eyebrow?, breadcrumbs?, icon?, subtitle?, info?, meta?, actions?` | a hand-written <h1> and title bar (the drift gate fails this) |
168
+ | `Breadcrumbs` | `@poodle64/ui/breadcrumbs` | `items, label?` | a hand-written chevron trail in PageHeader's breadcrumbs snippet; PageHeader takes the same items as `breadcrumbs={[…]}` |
169
+ | `PageContainer` | `@poodle64/ui/page-container` | `children` | a per-page wrapper picking its own vertical rhythm (space-y-6, gap-10, …) |
170
+ | `PageSection` | `@poodle64/ui/page-section` | `title, level?, description?, count?, actions?, children?, id?` | a hand-styled section heading (an uppercase kicker, text-lg font-semibold, a display h2) and its own gap to the content under it |
167
171
  | `RecordSwitcher` | `@poodle64/ui/record-switcher` | `name, groups, current?, noun?, placeholder?, icon?, all?, pages?, separator?` | a bare record name beside a small chevrons button in the top bar, and a dropdown of names with no search, groups or thumbnails; the record's own page tabs come with it |
168
172
  | `ContextColumn` | `@poodle64/ui/context-column` | `stats?, statsTitle?, statsInfo?, detail?, title?, subtitle?, onClose?, children?, documentPane?, ariaLabel?` | a hand-built right-hand <aside> with a stat card and a detail pane; on a record page, the one opened item (title, close, an inner scroll) and its DocumentPane, in the column's place or beside it once the page's row has room |
169
173
  | `ListShell` | `@poodle64/ui/list-shell` | `title, children, label?, open?, actions?` | a module hand-rolling its own foldable second column beside the rail — a queue, an inbox, a working list |
@@ -198,11 +202,12 @@ _A surface with nothing to show yet, still loading, or that failed to load — t
198
202
  | --- | --- | --- | --- |
199
203
  | `EmptyState` | `@poodle64/ui/empty-state` | `title, icon?, description?, action?` | a hand-centred 'Nothing here' message |
200
204
  | `LoadingState` | `@poodle64/ui/loading-state` | `message?` | a bare spinner or 'Loading…' you place by hand |
205
+ | `ContentSkeleton` | `@poodle64/ui/content-skeleton` | `shape?, count?, label?` | bare Skeleton boxes sized by hand (h-44 rounded-xl, h-24) standing in for a list, tiles or a document body while the page's header is already drawn |
201
206
  | `ErrorState` | `@poodle64/ui/error-state` | `message, action?` | a hand-built error message with a retry button |
202
207
 
203
208
  ## Primitives you already know
204
209
 
205
210
  _Standard shadcn-svelte / bits-ui primitives — reach for them the same way; no situation lookup needed. Import each from its path below._
206
211
 
207
- `@poodle64/ui/alert` · `@poodle64/ui/alert-dialog` · `@poodle64/ui/avatar` · `@poodle64/ui/badge` · `@poodle64/ui/button` · `@poodle64/ui/card` · `@poodle64/ui/checkbox` · `@poodle64/ui/command` · `@poodle64/ui/data-table` · `@poodle64/ui/dialog` · `@poodle64/ui/dropdown-menu` · `@poodle64/ui/input` · `@poodle64/ui/input-group` · `@poodle64/ui/label` · `@poodle64/ui/password-input` · `@poodle64/ui/popover` · `@poodle64/ui/progress` · `@poodle64/ui/select` · `@poodle64/ui/separator` · `@poodle64/ui/skeleton` · `@poodle64/ui/sonner` · `@poodle64/ui/switch` · `@poodle64/ui/table` · `@poodle64/ui/tabs` · `@poodle64/ui/textarea` · `@poodle64/ui/tooltip`
212
+ `@poodle64/ui/alert` · `@poodle64/ui/alert-dialog` · `@poodle64/ui/avatar` · `@poodle64/ui/badge` · `@poodle64/ui/button` · `@poodle64/ui/card` · `@poodle64/ui/checkbox` · `@poodle64/ui/command` · `@poodle64/ui/data-table` · `@poodle64/ui/dialog` · `@poodle64/ui/dropdown-menu` · `@poodle64/ui/input` · `@poodle64/ui/input-group` · `@poodle64/ui/label` · `@poodle64/ui/password-input` · `@poodle64/ui/popover` · `@poodle64/ui/progress` · `@poodle64/ui/select` · `@poodle64/ui/separator` · `@poodle64/ui/sheet` · `@poodle64/ui/skeleton` · `@poodle64/ui/sonner` · `@poodle64/ui/switch` · `@poodle64/ui/table` · `@poodle64/ui/tabs` · `@poodle64/ui/textarea` · `@poodle64/ui/tooltip`
208
213