@dogsbay/format-astro 0.2.0-beta.121 → 0.2.0-beta.123

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 (87) hide show
  1. package/dist/api-markdown.d.ts +0 -1
  2. package/dist/api-markdown.js +0 -1
  3. package/dist/base-path.d.ts +0 -1
  4. package/dist/base-path.js +0 -1
  5. package/dist/blog.d.ts +0 -1
  6. package/dist/blog.js +0 -1
  7. package/dist/browser.d.ts +0 -1
  8. package/dist/browser.js +0 -1
  9. package/dist/canonical.d.ts +0 -1
  10. package/dist/canonical.js +0 -1
  11. package/dist/capabilities.d.ts +28 -0
  12. package/dist/capabilities.js +105 -0
  13. package/dist/cli.d.ts +0 -1
  14. package/dist/cli.js +2 -1
  15. package/dist/diff-decoration.d.ts +0 -1
  16. package/dist/diff-decoration.js +0 -1
  17. package/dist/granularity.d.ts +0 -1
  18. package/dist/granularity.js +0 -1
  19. package/dist/head-metadata.d.ts +0 -1
  20. package/dist/head-metadata.js +0 -1
  21. package/dist/index.d.ts +2 -1
  22. package/dist/index.js +4 -1
  23. package/dist/lead.d.ts +0 -1
  24. package/dist/lead.js +0 -1
  25. package/dist/llms-txt.d.ts +0 -1
  26. package/dist/llms-txt.js +0 -1
  27. package/dist/plugins.d.ts +0 -1
  28. package/dist/plugins.js +0 -1
  29. package/dist/project.d.ts +0 -1
  30. package/dist/project.js +2 -1
  31. package/dist/redirects.d.ts +0 -1
  32. package/dist/redirects.js +0 -1
  33. package/dist/serialize.d.ts +0 -1
  34. package/dist/serialize.js +59 -22
  35. package/dist/sitemap.d.ts +0 -1
  36. package/dist/sitemap.js +0 -1
  37. package/dist/taxonomy.d.ts +0 -1
  38. package/dist/taxonomy.js +0 -1
  39. package/dist/theme-css.d.ts +8 -1
  40. package/dist/theme-css.js +33 -5
  41. package/dist/tone-classes.d.ts +39 -1
  42. package/dist/tone-classes.js +55 -1
  43. package/dist/write-if-changed.d.ts +0 -1
  44. package/dist/write-if-changed.js +0 -1
  45. package/package.json +7 -7
  46. package/dist/api-markdown.d.ts.map +0 -1
  47. package/dist/api-markdown.js.map +0 -1
  48. package/dist/base-path.d.ts.map +0 -1
  49. package/dist/base-path.js.map +0 -1
  50. package/dist/blog.d.ts.map +0 -1
  51. package/dist/blog.js.map +0 -1
  52. package/dist/browser.d.ts.map +0 -1
  53. package/dist/browser.js.map +0 -1
  54. package/dist/canonical.d.ts.map +0 -1
  55. package/dist/canonical.js.map +0 -1
  56. package/dist/cli.d.ts.map +0 -1
  57. package/dist/cli.js.map +0 -1
  58. package/dist/diff-decoration.d.ts.map +0 -1
  59. package/dist/diff-decoration.js.map +0 -1
  60. package/dist/granularity.d.ts.map +0 -1
  61. package/dist/granularity.js.map +0 -1
  62. package/dist/head-metadata.d.ts.map +0 -1
  63. package/dist/head-metadata.js.map +0 -1
  64. package/dist/index.d.ts.map +0 -1
  65. package/dist/index.js.map +0 -1
  66. package/dist/lead.d.ts.map +0 -1
  67. package/dist/lead.js.map +0 -1
  68. package/dist/llms-txt.d.ts.map +0 -1
  69. package/dist/llms-txt.js.map +0 -1
  70. package/dist/plugins.d.ts.map +0 -1
  71. package/dist/plugins.js.map +0 -1
  72. package/dist/project.d.ts.map +0 -1
  73. package/dist/project.js.map +0 -1
  74. package/dist/redirects.d.ts.map +0 -1
  75. package/dist/redirects.js.map +0 -1
  76. package/dist/serialize.d.ts.map +0 -1
  77. package/dist/serialize.js.map +0 -1
  78. package/dist/sitemap.d.ts.map +0 -1
  79. package/dist/sitemap.js.map +0 -1
  80. package/dist/taxonomy.d.ts.map +0 -1
  81. package/dist/taxonomy.js.map +0 -1
  82. package/dist/theme-css.d.ts.map +0 -1
  83. package/dist/theme-css.js.map +0 -1
  84. package/dist/tone-classes.d.ts.map +0 -1
  85. package/dist/tone-classes.js.map +0 -1
  86. package/dist/write-if-changed.d.ts.map +0 -1
  87. package/dist/write-if-changed.js.map +0 -1
@@ -5,4 +5,3 @@ export declare function apiInlineHtml(text: string): string;
5
5
  /** A schema property list with every `description` rendered inline,
6
6
  * through nested `properties` / `items`. A copy; the node is untouched. */
7
7
  export declare function apiPropertiesHtml(list: unknown): unknown;
8
- //# sourceMappingURL=api-markdown.d.ts.map
@@ -47,4 +47,3 @@ export function apiPropertiesHtml(list) {
47
47
  return o;
48
48
  });
49
49
  }
50
- //# sourceMappingURL=api-markdown.js.map
@@ -188,4 +188,3 @@ export declare function resolvePrefixes(siteUrl: string | undefined, basePath: s
188
188
  * its nav entry (`/base`), so prev/next silently falls back to the first item.
189
189
  */
190
190
  export declare function buildCurrentPath(basePath: string, section: string | undefined, slug: string): string;
191
- //# sourceMappingURL=base-path.d.ts.map
package/dist/base-path.js CHANGED
@@ -246,4 +246,3 @@ export function buildCurrentPath(basePath, section, slug) {
246
246
  parts.push(slug);
247
247
  return parts.length === 0 ? "/" : `/${parts.join("/")}`;
248
248
  }
249
- //# sourceMappingURL=base-path.js.map
package/dist/blog.d.ts CHANGED
@@ -131,4 +131,3 @@ export declare function emitBlogFiles(outputDir: string, data: BlogData, config:
131
131
  skipped: string[];
132
132
  pruned: string[];
133
133
  };
134
- //# sourceMappingURL=blog.d.ts.map
package/dist/blog.js CHANGED
@@ -321,4 +321,3 @@ const title = pageNo === 1 ? siteConfig.siteName : \`\${siteConfig.siteName} —
321
321
  </DocsLayout>
322
322
  `;
323
323
  }
324
- //# sourceMappingURL=blog.js.map
package/dist/browser.d.ts CHANGED
@@ -11,4 +11,3 @@ export * from "./llms-txt.js";
11
11
  export * from "./sitemap.js";
12
12
  export * from "./redirects.js";
13
13
  export * from "./base-path.js";
14
- //# sourceMappingURL=browser.d.ts.map
package/dist/browser.js CHANGED
@@ -11,4 +11,3 @@ export * from "./llms-txt.js";
11
11
  export * from "./sitemap.js";
12
12
  export * from "./redirects.js";
13
13
  export * from "./base-path.js";
14
- //# sourceMappingURL=browser.js.map
@@ -31,4 +31,3 @@ export interface PageCanonical {
31
31
  }
32
32
  /** Per page slug: its canonical, noindex and sitemap decision. */
33
33
  export declare function canonicalPlan(pages: ExportPage[], o: CanonicalPlanOptions): Map<string, PageCanonical>;
34
- //# sourceMappingURL=canonical.d.ts.map
package/dist/canonical.js CHANGED
@@ -104,4 +104,3 @@ export function canonicalPlan(pages, o) {
104
104
  }
105
105
  return plan;
106
106
  }
107
- //# sourceMappingURL=canonical.js.map
@@ -0,0 +1,28 @@
1
+ /**
2
+ * What the Astro exporter can and cannot represent.
3
+ *
4
+ * This is a CONTRACT, not commentary (the format-docusaurus pattern):
5
+ * `validateCapabilities` rejects any non-full entry without a note, a test
6
+ * asserts every `MATRIX_ROWS` type has exactly one declaration here, and the
7
+ * published component × format matrix (docs/component-format-matrix.md) is
8
+ * generated from it.
9
+ *
10
+ * Declared for the page output `site build` / `convert --to astro` emits:
11
+ * `treeToAstro` in TEMPLATE mode (src/serialize.ts), where TreeNodes become
12
+ * real `@dogsbay/ui` components. The hybrid `set:html` mode is not what a
13
+ * site build ships and is not described here.
14
+ *
15
+ * Measured by running `treeToAstro` on parsed dogsbay-md and hand-built
16
+ * nodes (2026-10-13), not inferred from the case list: several rows have a
17
+ * case and still lose content. format-astro has NO export ledger — the
18
+ * inline walker's `report` is never wired (serialize-core inline.ts defaults
19
+ * it to a no-op) — so every `partial` below is a loss nothing records at
20
+ * export time. The note is the only place it is stated.
21
+ *
22
+ * Support levels:
23
+ * full — renders faithfully as the matching component / element
24
+ * partial — survives with reduced fidelity; the note says what is lost
25
+ * none — dropped or flattened
26
+ */
27
+ import type { CapabilityDeclaration } from "@dogsbay/serialize-core";
28
+ export declare const ASTRO_CAPABILITIES: CapabilityDeclaration[];
@@ -0,0 +1,105 @@
1
+ export const ASTRO_CAPABILITIES = [
2
+ // ── Blocks ───────────────────────────────────────────────
3
+ { feature: "heading", support: "full", rendersAs: "`<hN id>` with a hover anchor" },
4
+ { feature: "paragraph", support: "full", rendersAs: "`<p>`; a lone image becomes `<BlockImage>`" },
5
+ { feature: "blockquote", support: "full", rendersAs: "styled `<blockquote>`" },
6
+ { feature: "unordered-list", support: "full", rendersAs: "`<ul>`" },
7
+ { feature: "ordered-list", support: "full", rendersAs: "`<ol start>`" },
8
+ { feature: "hr", support: "full", rendersAs: "`<hr>`" },
9
+ { feature: "table", support: "full", rendersAs: "`<Table>` (block content in cells; caption prop or slot)" },
10
+ {
11
+ feature: "code",
12
+ support: "partial",
13
+ rendersAs: "`<CodeBlock>`, or `<CodeRich>` with line numbers / highlights / diff / `[!code]` notation",
14
+ note: "Title, language, highlights, line numbers, diff marks and copyText survive. The `collapse` fence option is consumed and not emitted: the block renders expanded, unrecorded.",
15
+ },
16
+ { feature: "math-block", support: "full", rendersAs: "`<MathKatex display>` (KaTeX at build time)" },
17
+ { feature: "deflist", support: "full", rendersAs: "`<dl><dt><dd>`" },
18
+ { feature: "figure", support: "full", rendersAs: "`<figure>` + `<figcaption>`; a captioned code block puts the caption in its header" },
19
+ { feature: "footnote-list", support: "full", rendersAs: "GitHub-style footnotes `<section>` with backrefs" },
20
+ { feature: "html", support: "full", rendersAs: "raw HTML via `set:html`; a marked callout div becomes `<Alert>`" },
21
+ {
22
+ feature: "html-container",
23
+ support: "partial",
24
+ rendersAs: "its HTML as authored, or a bare `<div>` around its children (card-grid / mdx-columns classes are recognised)",
25
+ note: "A children-only container from an importer loses its source tag and attributes (class, id, style); the content is kept.",
26
+ },
27
+ { feature: "diagram", support: "full", rendersAs: "`<Diagram>` (source + pre-rendered SVG)" },
28
+ { feature: "youtube", support: "full", rendersAs: "`<YouTube>`" },
29
+ // ── Components ───────────────────────────────────────────
30
+ { feature: "callout", support: "full", rendersAs: "`<Alert variant>` with the authored type kept as `data-type`" },
31
+ { feature: "details", support: "full", rendersAs: "`<CollapsibleAlert variant title open icon>` (`title`, or its alias `summary`)" },
32
+ { feature: "details-sheet", support: "full", rendersAs: "`<DetailsSheet>` around the CollapsibleAlert: `<db-details-sheet>` opens it as a right-hand native `<dialog>`; without the script it stays a collapsible block" },
33
+ { feature: "theme", support: "full", rendersAs: "a `<div class=\"dark …\" data-theme>`: the theme re-declares its tokens on `.dark`, so the subtree turns dark" },
34
+ { feature: "tabs-variant", support: "full", rendersAs: "TabsList/TabsTrigger given the variant's classes (TAB_VARIANTS), merged over their base" },
35
+ { feature: "tone", support: "full", rendersAs: "each card's border and tint from the tone (CARD_TONE_CLASSES); a group tone applies to every card without one" },
36
+ { feature: "tabs", support: "full", rendersAs: "`<Tabs>` / `<TabsTrigger>` / `<TabsContent>` (`<db-tabs>`), sync keys kept" },
37
+ {
38
+ feature: "steps",
39
+ support: "partial",
40
+ rendersAs: "`<Steps>` / `<Step>` / `<SubSteps>`",
41
+ note: "A step's body renders; a `title` PROP on a step is not emitted (titles carried in the step's own content, as dogsbay-md writes them, survive).",
42
+ },
43
+ {
44
+ feature: "accordion",
45
+ support: "full",
46
+ rendersAs: "`<Accordion type defaultValue>` / `<AccordionItem>`; a child that is not an item stays in reading order around the group",
47
+ },
48
+ {
49
+ feature: "cards",
50
+ support: "partial",
51
+ rendersAs: "`<Grid>` of `<Card>`",
52
+ note: "The grid is always `cols={[1, 2]}`: a `cols` prop is ignored. When the group has `card` children, any non-card child is dropped; in the MkDocs list form, non-list children are dropped and a list item whose content is inline (a tight list) renders an EMPTY card.",
53
+ },
54
+ {
55
+ feature: "card",
56
+ support: "partial",
57
+ rendersAs: "`<Card>` with title, description, body and optional link",
58
+ note: "Outside a card group, an icon that is an image PATH is passed to `<Icon name>`, which renders nothing for it — the logo disappears (inside a group it becomes an `<img>`).",
59
+ },
60
+ { feature: "link-card", support: "full", rendersAs: "`<LinkCard>`; with body content, a linked `<Card>`" },
61
+ { feature: "files", support: "full", rendersAs: "`<FileTree items>`, folders from nesting or a trailing `/`" },
62
+ { feature: "grid", support: "full", rendersAs: "`<Grid cols gap rows>` / `<GridItem>`" },
63
+ { feature: "link-button", support: "full", rendersAs: "`<Button href variant>`" },
64
+ { feature: "related", support: "full", rendersAs: "`<section class=\"related-resources\">` with a caption title" },
65
+ { feature: "example", support: "full", rendersAs: "`<MarkdownExample source>` (source + live render)" },
66
+ {
67
+ feature: "container",
68
+ support: "partial",
69
+ rendersAs: "`<div data-directive=\"name\">` with the title as a bold paragraph",
70
+ note: "An unknown directive has no component: its content and title survive in a marked div with no styling or behaviour.",
71
+ },
72
+ {
73
+ feature: "annotation-list",
74
+ support: "partial",
75
+ rendersAs: "a tinted box after the code block",
76
+ note: "The annotations render as a list beside the code, not as markers/tooltips attached to their lines.",
77
+ },
78
+ // ── API reference ────────────────────────────────────────
79
+ { feature: "endpoint", support: "full", rendersAs: "`<ApiLayout>` + `<EndpointCard>` + `<CodeSamples>` / `<ExampleBlock>`" },
80
+ { feature: "api-doc", support: "full", rendersAs: "`<ApiDoc html>`" },
81
+ { feature: "api-class", support: "full", rendersAs: "`<ApiSymbol kind level>`" },
82
+ { feature: "api-member", support: "full", rendersAs: "`<ApiSymbol kind level>`" },
83
+ { feature: "api-params", support: "full", rendersAs: "`<ApiParams>`" },
84
+ { feature: "api-source", support: "full", rendersAs: "`<ApiSource file line>`" },
85
+ // ── Inline ───────────────────────────────────────────────
86
+ {
87
+ feature: "link",
88
+ support: "full",
89
+ rendersAs: "`<a>`; an inline `:button` becomes `<Button size=\"sm\">`",
90
+ note: "A link whose scheme could run script keeps its text and loses the href — by design, but unrecorded (no ledger).",
91
+ },
92
+ { feature: "image", support: "full", rendersAs: "`<img loading=\"lazy\">`; alone in a paragraph, `<BlockImage>`" },
93
+ { feature: "kbd", support: "full", rendersAs: "`<Kbd>` per key, joined by `+`" },
94
+ {
95
+ feature: "math",
96
+ support: "partial",
97
+ rendersAs: "`<code class=\"math-inline\">` holding the LaTeX source",
98
+ note: "Inline math is NOT typeset (display math is, via MathKatex): the reader sees raw LaTeX in a code span.",
99
+ },
100
+ { feature: "highlight", support: "full", rendersAs: "styled `<mark>`" },
101
+ { feature: "badge", support: "full", rendersAs: "`<Badge variant>`" },
102
+ { feature: "icon", support: "full", rendersAs: "`<Icon name=\"pack:name\">`" },
103
+ { feature: "footnote-ref", support: "full", rendersAs: "`<sup><a href=\"#fn-…\">`" },
104
+ { feature: "html-inline", support: "full", rendersAs: "raw HTML via `<Fragment set:html>`" },
105
+ ];
package/dist/cli.d.ts CHANGED
@@ -19,4 +19,3 @@ import type { FormatPlugin } from "@dogsbay/types";
19
19
  * applied by `dogsbay site build`.
20
20
  */
21
21
  export declare const plugin: FormatPlugin;
22
- //# sourceMappingURL=cli.d.ts.map
package/dist/cli.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { emitAstroPages } from "./project.js";
2
+ import { ASTRO_CAPABILITIES } from "./capabilities.js";
2
3
  /**
3
4
  * `dogsbay convert --to astro` plugin.
4
5
  *
@@ -22,6 +23,7 @@ export const plugin = {
22
23
  name: "astro",
23
24
  canImport: false,
24
25
  canExport: true,
26
+ capabilities: ASTRO_CAPABILITIES,
25
27
  detectSource: () => false,
26
28
  exportOptions: [
27
29
  { flags: "--local", description: "Use file: references to local monorepo packages (for development)" },
@@ -51,4 +53,3 @@ export const plugin = {
51
53
  });
52
54
  },
53
55
  };
54
- //# sourceMappingURL=cli.js.map
@@ -76,4 +76,3 @@ export declare function decorateDiff(node: TreeNode, rendered: string, renderPee
76
76
  * both color modes, no hardcoded palette (key-decisions doctrine).
77
77
  */
78
78
  export declare const DIFF_CSS = "/* tree-diff decorations \u2014 one DiffTree, rendered by the normal components.\n Mark styling is GUARDED by html:not([data-diff-hidden]) rather than\n force-reset under hidden mode: a reset like \"border-left: none\n !important\" also clobbers a component's OWN border (an added warning\n callout lost its left edge \u2014 user find, adapters/mysql). Guarded\n rules simply stop applying, and component styling survives. */\n[data-diff] { position: relative; }\nhtml:not([data-diff-hidden]) [data-diff=\"added\"] {\n background: color-mix(in oklch, var(--color-success, oklch(0.72 0.19 150)) 12%, transparent);\n border-left: 3px solid var(--color-success, oklch(0.72 0.19 150));\n padding-left: 0.75rem;\n}\nhtml:not([data-diff-hidden]) [data-diff=\"removed\"] {\n background: color-mix(in oklch, var(--color-destructive, oklch(0.64 0.21 25)) 10%, transparent);\n border-left: 3px solid var(--color-destructive, oklch(0.64 0.21 25));\n padding-left: 0.75rem;\n opacity: 0.75;\n}\nhtml:not([data-diff-hidden]) [data-diff=\"removed\"] :is(p, li, code, pre) { text-decoration: line-through; text-decoration-thickness: 1px; }\nhtml:not([data-diff-hidden]) [data-diff=\"changed\"] {\n border-left: 3px solid var(--color-warning, oklch(0.8 0.16 85));\n padding-left: 0.75rem;\n}\nhtml:not([data-diff-hidden]) [data-diff=\"moved\"] {\n border-left: 3px dashed var(--color-info, oklch(0.7 0.14 240));\n padding-left: 0.75rem;\n}\nhtml:not([data-diff-hidden]) [data-diff=\"moved\"][data-diff-from]::after {\n content: \"moved from \" attr(data-diff-from);\n display: block;\n font-size: 0.75rem;\n color: var(--color-muted-foreground, inherit);\n opacity: 0.8;\n}\n\n/* Explicit \"added\"/\"removed\" label pills straddling the block's top\n edge. Components with their own chrome (callouts, code blocks)\n camouflage a tint-only mark \u2014 a green-tinted warning callout reads\n as a callout variant, not as new content (user feedback,\n better-auth adapters/mysql). Table rows are excluded (a pill can't\n straddle a tr); nested marks defer to the outermost label. */\nhtml:not([data-diff-hidden]) [data-diff=\"added\"]:not(tr, th, td)::before,\nhtml:not([data-diff-hidden]) [data-diff=\"removed\"]:not(tr, th, td)::before {\n position: absolute;\n top: 0;\n right: 0.6rem;\n transform: translateY(-55%);\n font-size: 0.65rem;\n font-weight: 600;\n letter-spacing: 0.04em;\n line-height: 1.4;\n padding: 0 0.5rem;\n border-radius: 99px;\n z-index: 1;\n text-decoration: none;\n}\nhtml:not([data-diff-hidden]) [data-diff=\"added\"]:not(tr, th, td)::before {\n content: \"added\";\n background: color-mix(in oklch, var(--color-success, oklch(0.72 0.19 150)) 25%, var(--color-background, Canvas));\n border: 1px solid color-mix(in oklch, var(--color-success, oklch(0.72 0.19 150)) 45%, transparent);\n}\nhtml:not([data-diff-hidden]) [data-diff=\"removed\"]:not(tr, th, td)::before {\n content: \"removed\";\n background: color-mix(in oklch, var(--color-destructive, oklch(0.64 0.21 25)) 20%, var(--color-background, Canvas));\n border: 1px solid color-mix(in oklch, var(--color-destructive, oklch(0.64 0.21 25)) 45%, transparent);\n}\n[data-diff] [data-diff]::before { content: none !important; }\n\n/* Nav change markers (the sidebar is client-rendered from nav.json, so\n the styles ship here rather than with the component). Same contract as\n the tab markers: a GLYPH carries the meaning, colour reinforces it,\n visually-hidden text announces it. */\n.db-nav-mark {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n width: 1rem;\n height: 1rem;\n margin-left: auto;\n border-radius: 99px;\n font-size: 0.75rem;\n font-weight: 700;\n line-height: 1;\n color: var(--color-muted-foreground, currentColor);\n}\n.db-nav-mark[data-nav-mark=\"added\"] { color: var(--color-success, oklch(0.72 0.19 150)); }\n.db-nav-mark[data-nav-mark=\"changed\"] { color: var(--color-warning, oklch(0.8 0.16 85)); }\n.db-nav-mark[data-nav-mark=\"removed\"] { color: var(--color-destructive, oklch(0.64 0.21 25)); }\n.db-nav-mark[data-nav-mark=\"moved\"] { color: var(--color-info, oklch(0.7 0.14 240)); }\n/* a subtree marker is a hint about descendants, not a claim about this row */\n.db-nav-mark[data-nav-mark-subtree] { opacity: 0.55; }\n/* removed pages get a SECOND non-colour signal */\n/* Belt-and-braces: a subtree mark must never strike through its own row. */\n[data-nav-mark-row=\"removed\"]:not([data-nav-mark-subtree]) { text-decoration: line-through; opacity: 0.7; }\n@media (forced-colors: active) {\n .db-nav-mark { color: CanvasText; }\n}\n\n/* Which tab changed? A marked panel is invisible until opened, so its\n trigger carries a marker (the group's bar says \"in here\", the marker\n says \"this one\"). The marker is a GLYPH, not a colored dot: colour\n alone fails WCAG 1.4.1 and background-tinted dots vanish entirely in\n forced-colors mode. Colour reinforces; shape carries the meaning.\n Visually-hidden text rides along (emitted by the serializer). */\n.db-tab-mark {\n display: inline-block;\n margin-left: 0.35rem;\n font-size: 0.75rem;\n font-weight: 700;\n line-height: 1;\n vertical-align: middle;\n color: var(--color-muted-foreground, currentColor);\n}\n[data-diff-hidden] .db-tab-mark { display: none; }\n.db-tab-mark[data-diff-tab=\"changed\"] { color: var(--color-warning, oklch(0.8 0.16 85)); }\n.db-tab-mark[data-diff-tab=\"added\"] { color: var(--color-success, oklch(0.72 0.19 150)); }\n.db-tab-mark[data-diff-tab=\"removed\"] { color: var(--color-destructive, oklch(0.64 0.21 25)); }\n.db-tab-mark[data-diff-tab=\"moved\"] { color: var(--color-info, oklch(0.7 0.14 240)); }\n@media (forced-colors: active) {\n .db-tab-mark { color: CanvasText; }\n}\nins.db-diff-ins {\n text-decoration: none;\n background: color-mix(in oklch, var(--color-success, oklch(0.72 0.19 150)) 22%, transparent);\n border-radius: 2px;\n padding: 0 1px;\n}\ndel.db-diff-del {\n background: color-mix(in oklch, var(--color-destructive, oklch(0.64 0.21 25)) 18%, transparent);\n border-radius: 2px;\n padding: 0 1px;\n}\n.db-diff-pair { display: grid; gap: 0.25rem; margin-block: 1rem; }\n.db-diff-pair-label {\n font-size: 0.7rem;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--color-muted-foreground, inherit);\n opacity: 0.8;\n}\ntr[data-diff] { border-left-width: 3px; }\n\n/* Folded unchanged sections are CHROME, not content callouts:\n dashed, dimmed, compact \u2014 visually \"collapsed context\". */\n.db-diff-fold {\n border: 1px dashed color-mix(in oklch, currentColor 25%, transparent) !important;\n border-left-width: 1px !important;\n background: transparent !important;\n opacity: 0.7;\n margin-block: 0.5rem !important;\n}\n.db-diff-fold:hover, .db-diff-fold[open] { opacity: 1; }\n.db-diff-fold summary, .db-diff-fold [data-slot=\"trigger\"] {\n font-size: 0.85rem;\n}\n\n/* The comparison banner at the top of each reader page. */\n.db-diff-banner-row { display: flex; flex-wrap: wrap; gap: 0.4rem 0.8rem; align-items: center; width: 100%; }\n.db-diff-banner {\n border: 1px solid color-mix(in oklch, currentColor 15%, transparent);\n border-radius: 8px;\n padding: 0.75rem 1rem;\n margin-block-end: 1.5rem;\n font-size: 0.85rem;\n display: flex;\n flex-wrap: wrap;\n gap: 0.4rem 1rem;\n align-items: center;\n}\n.db-diff-banner .db-chip {\n display: inline-block;\n border-radius: 99px;\n padding: 0.05rem 0.6rem;\n font-size: 0.75rem;\n border: 1px solid transparent;\n}\n.db-chip-added { background: color-mix(in oklch, var(--color-success, oklch(0.72 0.19 150)) 15%, transparent); }\n.db-chip-removed { background: color-mix(in oklch, var(--color-destructive, oklch(0.64 0.21 25)) 12%, transparent); }\n.db-chip-changed { background: color-mix(in oklch, var(--color-warning, oklch(0.8 0.16 85)) 15%, transparent); }\n.db-chip-moved { background: color-mix(in oklch, var(--color-info, oklch(0.7 0.14 240)) 12%, transparent); }\n.db-chip-muted { border-color: color-mix(in oklch, currentColor 25%, transparent); opacity: 0.8; }\n\n/* The persistent view-switcher bar (Reader / Docs-ops / Agent):\n perspectives on ONE comparison \u2014 tabs are links, verification\n lives in the chrome, the Monitor CTA sits next to the value. */\n.db-cmp-bar {\n position: sticky;\n /* BELOW the docs header, not under it: both were sticky at top:0 and\n the header's higher z-index simply swallowed this bar on scroll \u2014\n the chrome carrying the trust contract was the chrome that vanished.\n --db-header-height is declared by DocsLayout (the header sizes itself\n from the same variable, so they cannot drift). */\n top: var(--db-header-height, 3.25rem);\n z-index: 30;\n display: flex;\n flex-wrap: wrap;\n gap: 0.4rem 0.9rem;\n align-items: center;\n padding: 0.5rem 0.9rem;\n margin-block-end: 1rem;\n border: 1px solid color-mix(in oklch, currentColor 15%, transparent);\n border-radius: 8px;\n background: var(--color-background, Canvas);\n font-size: 0.8rem;\n}\n.db-cmp-bar .db-chip {\n display: inline-block;\n border-radius: 99px;\n padding: 0.05rem 0.6rem;\n font-size: 0.75rem;\n border: 1px solid transparent;\n}\n.db-cmp-bar button.db-chip { cursor: pointer; background: transparent; font: inherit; font-size: 0.75rem; color: inherit; }\n.db-cmp-tabs {\n display: inline-flex;\n gap: 0.15rem;\n border: 1px solid color-mix(in oklch, currentColor 20%, transparent);\n border-radius: 99px;\n padding: 0.1rem;\n}\n.db-cmp-tab {\n padding: 0.1rem 0.7rem;\n border-radius: 99px;\n text-decoration: none;\n color: inherit;\n}\n.db-cmp-tab[aria-current=\"page\"] {\n background: color-mix(in oklch, currentColor 12%, transparent);\n font-weight: 600;\n}\n.db-cmp-pairs { position: relative; display: inline-block; }\n.db-cmp-pairs summary { cursor: pointer; }\n.db-cmp-pairs ul {\n position: absolute;\n top: 100%;\n left: 0;\n margin: 0.25rem 0 0;\n padding: 0.35rem 0.75rem;\n list-style: none;\n background: var(--color-background, Canvas);\n border: 1px solid color-mix(in oklch, currentColor 20%, transparent);\n border-radius: 8px;\n white-space: nowrap;\n z-index: 40;\n}\n.db-cmp-monitor { text-decoration: none; border-color: color-mix(in oklch, currentColor 30%, transparent) !important; }\n.db-cmp-monitor:hover { background: color-mix(in oklch, currentColor 10%, transparent); }\n\n/* A wholly added/removed PAGE carries one mark around all its content.\n The per-block tint would be overwhelming at page scale, so page level\n gets a stronger edge and a much lighter wash \u2014 legible at a glance,\n still readable as prose. The \"added\"/\"removed\" pill (the ::before on\n [data-diff]) rides on this wrapper, which is exactly right: it labels\n the page, once. */\nhtml:not([data-diff-hidden]) [data-diff].db-diff-page {\n border-left-width: 5px;\n padding-left: 1rem;\n padding-block: 0.5rem;\n border-radius: 0 6px 6px 0;\n}\nhtml:not([data-diff-hidden]) [data-diff=\"added\"].db-diff-page {\n background: color-mix(in oklch, var(--color-success, oklch(0.72 0.19 150)) 5%, transparent);\n}\nhtml:not([data-diff-hidden]) [data-diff=\"removed\"].db-diff-page {\n background: color-mix(in oklch, var(--color-destructive, oklch(0.64 0.21 25)) 5%, transparent);\n opacity: 1; /* the page is READABLE \u2014 the mark says removed, not the dimming */\n}\n/* \u2026and a removed PAGE is not struck through \u2014 that is for a deleted line,\n not for a document the reader still needs to read. */\n[data-diff=\"removed\"].db-diff-page :is(p, li, code, pre) { text-decoration: none; }\n\n/* Changed raw HTML: the faithful new render, plus the previous version\n one click away. Without it a deletion inside opaque markup (a dropped\n table column) is simply invisible \u2014 the reader is told \"something\n changed\" and shown no evidence. */\n.db-diff-html-before {\n margin-block-start: 0.5rem;\n font-size: 0.8rem;\n border: 1px dashed color-mix(in oklch, currentColor 25%, transparent);\n border-radius: 6px;\n padding: 0.25rem 0.6rem;\n}\n/* Full-strength text: the summary is a CONTROL \u2014 dimming it (opacity or\n muted-foreground) drops it below 4.5:1 and axe rightly fails it. */\n.db-diff-html-before summary { cursor: pointer; color: inherit; }\n.db-diff-html-before[open] summary { margin-block-end: 0.5rem; }\n[data-diff-hidden] .db-diff-html-before { display: none; }\n\n/* Merged code diffs carry a hidden clean twin (the post-change\n render); Hide highlights swaps them. */\n.db-diff-code { position: relative; }\n.db-diff-code-clean { display: none; }\n[data-diff-hidden] .db-diff-code-clean { display: block; }\n[data-diff-hidden] .db-diff-code-merged { display: none; }\n\n/* Whitespace-only code edits are still marked (indentation is\n semantic in YAML/Python/markdown) \u2014 but NAMED, so the reader\n is not hunting an invisible 3-space shift. */\nhtml:not([data-diff-hidden]) .db-diff-code-ws .db-diff-code-merged::before {\n content: \"whitespace only\";\n position: absolute;\n top: 0;\n right: 0.6rem;\n transform: translateY(-55%);\n z-index: 1;\n font-size: 0.65rem;\n font-weight: 600;\n letter-spacing: 0.04em;\n line-height: 1.4;\n padding: 0 0.5rem;\n border-radius: 99px;\n background: color-mix(in oklch, var(--color-warning, oklch(0.8 0.16 85)) 22%, var(--color-background, Canvas));\n border: 1px solid color-mix(in oklch, var(--color-warning, oklch(0.8 0.16 85)) 45%, transparent);\n}\n\n/* Hide highlights \u2014 read the target version clean. One attribute on\n <html>: the guarded mark rules above stop applying by themselves;\n only content that does not exist in the target version (removed\n blocks, del runs, the \"before\" code side) needs hiding, plus the\n ins tint. */\n[data-diff-hidden] :is([data-diff=\"removed\"], del.db-diff-del, .db-diff-pair-label) { display: none !important; }\n[data-diff-hidden] ins.db-diff-ins { background: transparent; padding: 0; }\n";
79
- //# sourceMappingURL=diff-decoration.d.ts.map
@@ -538,4 +538,3 @@ html:not([data-diff-hidden]) .db-diff-code-ws .db-diff-code-merged::before {
538
538
  [data-diff-hidden] :is([data-diff="removed"], del.db-diff-del, .db-diff-pair-label) { display: none !important; }
539
539
  [data-diff-hidden] ins.db-diff-ins { background: transparent; padding: 0; }
540
540
  `;
541
- //# sourceMappingURL=diff-decoration.js.map
@@ -80,4 +80,3 @@ export declare function bookSlugFrom(slugs: string[], fallback: string): string;
80
80
  /** Strip the route prefix + leading slash + anchor/query from a nav href → slug. */
81
81
  export declare function slugFromHref(href: string | undefined, hrefPrefix: string): string;
82
82
  export declare function slugify(text: string): string;
83
- //# sourceMappingURL=granularity.d.ts.map
@@ -244,4 +244,3 @@ export function slugify(text) {
244
244
  .replace(/^-+|-+$/g, "")
245
245
  .trim() || "section");
246
246
  }
247
- //# sourceMappingURL=granularity.js.map
@@ -23,4 +23,3 @@ export interface HeadMetadataOptions {
23
23
  indexable?: (p: ExportPage) => boolean;
24
24
  }
25
25
  export declare function headMetadata(pages: ExportPage[], nav: NavItem[], o: HeadMetadataOptions): Map<string, HeadMetadata>;
26
- //# sourceMappingURL=head-metadata.d.ts.map
@@ -53,4 +53,3 @@ export function headMetadata(pages, nav, o) {
53
53
  }
54
54
  return plan;
55
55
  }
56
- //# sourceMappingURL=head-metadata.js.map
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export { treeToAstro, escapeExpr, escapeAttr, escapeTemplate } from "./serialize.js";
2
2
  export { writeFileSync, copyFileSync } from "./write-if-changed.js";
3
+ export { ASTRO_CAPABILITIES } from "./capabilities.js";
3
4
  export { detectLeadingNodes, deriveDescription } from "./lead.js";
4
5
  export type { LeadingNodeInfo } from "./lead.js";
5
6
  export type { AstroGenResult, AstroGenOptions, AstroRenderMode } from "./serialize.js";
@@ -31,4 +32,4 @@ export type { DiffMarks } from "./diff-decoration.js";
31
32
  export { BOOK_SEGMENT, assembleBook, bookHeading, bookSlugFrom, estimateTreeBytes, openAsPageNode, rewriteIntraBookLinks, shiftHeadings, slugFromHref, } from "./granularity.js";
32
33
  export type { AssembleBookOptions, BookView } from "./granularity.js";
33
34
  export { pageUnitPrefix, inPageUnit, normalizeTarget } from "./redirects.js";
34
- //# sourceMappingURL=index.d.ts.map
35
+ export { TONES, CARD_TONE_CLASSES, TAB_VARIANTS, THEME_SCOPES } from "./tone-classes.js";
package/dist/index.js CHANGED
@@ -1,6 +1,8 @@
1
1
  // Astro serializer (TreeNode[] → Astro component markup)
2
2
  export { treeToAstro, escapeExpr, escapeAttr, escapeTemplate } from "./serialize.js";
3
3
  export { writeFileSync, copyFileSync } from "./write-if-changed.js";
4
+ // Component × format matrix: what the page output represents, per TreeNode type.
5
+ export { ASTRO_CAPABILITIES } from "./capabilities.js";
4
6
  // Auto-lede detection — figures out whether a page tree already
5
7
  // has a leading H1 / paragraph so format-astro can decide whether
6
8
  // to inject frontmatter title + description at the top of <main>.
@@ -58,4 +60,5 @@ export { DIFF_CSS, decorateDiff, diffMarksOf, hasDiffMarks, injectAttrs, inlineS
58
60
  */
59
61
  export { BOOK_SEGMENT, assembleBook, bookHeading, bookSlugFrom, estimateTreeBytes, openAsPageNode, rewriteIntraBookLinks, shiftHeadings, slugFromHref, } from "./granularity.js";
60
62
  export { pageUnitPrefix, inPageUnit, normalizeTarget } from "./redirects.js";
61
- //# sourceMappingURL=index.js.map
63
+ // Variants, tones and scoped themes: the class tables both renderers draw from
64
+ export { TONES, CARD_TONE_CLASSES, TAB_VARIANTS, THEME_SCOPES } from "./tone-classes.js";
package/dist/lead.d.ts CHANGED
@@ -1,2 +1 @@
1
1
  export { detectLeadingNodes, deriveDescription, type LeadingNodeInfo } from "@dogsbay/serialize-core";
2
- //# sourceMappingURL=lead.d.ts.map
package/dist/lead.js CHANGED
@@ -1,4 +1,3 @@
1
1
  // Moved to @dogsbay/serialize-core (src/lead.ts), so @dogsbay/site-check can use
2
2
  // these without depending on format-astro, which brings @dogsbay/ui and astro.
3
3
  export { detectLeadingNodes, deriveDescription } from "@dogsbay/serialize-core";
4
- //# sourceMappingURL=lead.js.map
@@ -120,4 +120,3 @@ export declare function buildSectionLlmsTxt(siteConfig: Pick<SiteConfig, "siteNa
120
120
  * line, then either the full body (default) or just a description.
121
121
  */
122
122
  export declare function buildLlmsFullTxt(siteConfig: Pick<SiteConfig, "siteName" | "description" | "siteUrl">, nav: NavItem[], pages: ExportPage[], options?: BuildLlmsFullTxtOptions): string;
123
- //# sourceMappingURL=llms-txt.d.ts.map
package/dist/llms-txt.js CHANGED
@@ -148,4 +148,3 @@ function getPageDescription(page, limit) {
148
148
  const firstPara = extractFirstParagraph(page.tree);
149
149
  return firstPara ? truncate(firstPara, limit) : "";
150
150
  }
151
- //# sourceMappingURL=llms-txt.js.map
package/dist/plugins.d.ts CHANGED
@@ -37,4 +37,3 @@ export interface EmitPluginRuntimeOptions {
37
37
  export declare function emitPluginRuntime(options: EmitPluginRuntimeOptions): void;
38
38
  /** Test-only: clear emitted plugin runtime files. */
39
39
  export declare function _clearPluginRuntime(outputDir: string): void;
40
- //# sourceMappingURL=plugins.d.ts.map
package/dist/plugins.js CHANGED
@@ -338,4 +338,3 @@ export function _clearPluginRuntime(outputDir) {
338
338
  rmSync(join(outputDir, PLUGIN_CONFIG_DIR_REL), { recursive: true, force: true });
339
339
  rmSync(join(outputDir, PLUGIN_STYLES_DIR_REL), { recursive: true, force: true });
340
340
  }
341
- //# sourceMappingURL=plugins.js.map
package/dist/project.d.ts CHANGED
@@ -682,4 +682,3 @@ export declare function rootVersionOf(options: Pick<AstroProjectOptions, "versio
682
682
  * introduced in a release (Kbd, FileTree) reaches sites scaffolded earlier.
683
683
  */
684
684
  export declare function copyComponents(outputDir: string): string[];
685
- //# sourceMappingURL=project.d.ts.map
package/dist/project.js CHANGED
@@ -3336,6 +3336,8 @@ export function copyComponents(outputDir) {
3336
3336
  "icon",
3337
3337
  // Inline :kbd and the :::files tree.
3338
3338
  "kbd", "files",
3339
+ // :::details{sheet} / DITA db-details sheet
3340
+ "details-sheet",
3339
3341
  ];
3340
3342
  const added = [];
3341
3343
  for (const name of needed) {
@@ -3534,4 +3536,3 @@ function resolveComponentsSource() {
3534
3536
  return demo;
3535
3537
  return null;
3536
3538
  }
3537
- //# sourceMappingURL=project.js.map
@@ -113,4 +113,3 @@ export declare function renderMetaRefreshPage(rule: RedirectRule, siteUrl?: stri
113
113
  * lands at `<mount>/<mount>/old/`.
114
114
  */
115
115
  export declare function stubPathForRule(rule: RedirectRule, prefix: string): string;
116
- //# sourceMappingURL=redirects.d.ts.map
package/dist/redirects.js CHANGED
@@ -221,4 +221,3 @@ export function stubPathForRule(rule, prefix) {
221
221
  }
222
222
  return p ? `${p}/index.html` : "index.html";
223
223
  }
224
- //# sourceMappingURL=redirects.js.map
@@ -41,4 +41,3 @@ export declare function componentAttr(name: string, value: string): string;
41
41
  /** Escape text for use directly in an Astro template (not inside an expression). */
42
42
  export declare function escapeTemplate(s: string): string;
43
43
  export declare function treeToAstro(nodes: TreeNode[], options?: AstroGenOptions): AstroGenResult;
44
- //# sourceMappingURL=serialize.d.ts.map
package/dist/serialize.js CHANGED
@@ -1,7 +1,7 @@
1
- import { walkInline, applyTextFlags, renderLeaf, indent, isSafeHref, apiPageLayout, apiSymbolLevels, } from "@dogsbay/serialize-core";
1
+ import { walkInline, applyTextFlags, renderLeaf, indent, isSafeHref, apiPageLayout, apiSymbolLevels, renderAroundGroup, isDetailsSheet, } from "@dogsbay/serialize-core";
2
2
  import { parseInline } from "@dogsbay/format-dogsbay-md";
3
3
  import { decorateDiff, hasDiffMarks } from "./diff-decoration.js";
4
- import { TONE_CLASSES } from "./tone-classes.js";
4
+ import { TONE_CLASSES, CARD_TONE_CLASSES, TAB_VARIANTS, THEME_SCOPES, lookup } from "./tone-classes.js";
5
5
  import { apiBlockHtml, apiPropertiesHtml } from "./api-markdown.js";
6
6
  export { TONE_CLASSES };
7
7
  const COMPONENT_IMPORTS = {
@@ -16,6 +16,9 @@ const COMPONENT_IMPORTS = {
16
16
  details: [
17
17
  'import CollapsibleAlert from "@ui/alert/CollapsibleAlert.astro";',
18
18
  ],
19
+ "details-sheet": [
20
+ 'import DetailsSheet from "@ui/details-sheet/DetailsSheet.astro";',
21
+ ],
19
22
  code: [
20
23
  'import CodeBlock from "@ui/code-block/CodeBlock.astro";',
21
24
  ],
@@ -245,8 +248,21 @@ function nodeToAstroBase(node, ctx) {
245
248
  return genericContainerToAstro(node, ctx);
246
249
  case "details":
247
250
  return detailsToAstro(node, ctx);
251
+ case "theme": {
252
+ // A scoped theme: `.dark` re-declares the tokens for its subtree, so a
253
+ // plain wrapper is enough (THEME_SCOPES); an unknown name keeps the content
254
+ const name = node.props?.name;
255
+ const scope = lookup(THEME_SCOPES, name);
256
+ const inner = childrenToAstro(node, ctx);
257
+ if (!scope)
258
+ return inner;
259
+ const passthrough = renderPassthroughAttrs(node.props, ["name", "class", "data-theme"]);
260
+ // The default (expression) form: an entity-escaped `[&amp;>…]` is not a
261
+ // class Tailwind's scanner recognises, so the rule was never generated
262
+ return `<div${mergeClassAttr(scope, node.props?.class)} data-theme="${escapeAttr(name)}"${passthrough}>\n${indentStr(inner, 2)}\n</div>`;
263
+ }
248
264
  case "tabs":
249
- return tabsToAstro(node, ctx);
265
+ return renderAroundGroup(node, "tab", (n) => tabsToAstro(n, ctx), (c) => nodeToAstro(c, ctx), "\n");
250
266
  case "ordered-list":
251
267
  return wrapBlock("ol", "", node, childrenToAstro(node, ctx), ["start"])
252
268
  .replace(/^<ol(\s[^>]*)?>/, (m) => {
@@ -370,7 +386,7 @@ function nodeToAstroBase(node, ctx) {
370
386
  case "link-button":
371
387
  return linkButtonToAstro(node, ctx);
372
388
  case "accordion":
373
- return accordionToAstro(node, ctx);
389
+ return renderAroundGroup(node, "accordion-item", (n) => accordionToAstro(n, ctx), (c) => nodeToAstro(c, ctx), "\n");
374
390
  case "accordion-item":
375
391
  // Bare accordion-item outside an :::accordion wrapper. Wrap it in an
376
392
  // Accordion shell so the output is still a valid widget.
@@ -414,7 +430,7 @@ ${childrenToAstro(node, ctx)}
414
430
  // controls the bg/text combo (see TONE_CLASSES). Authors can also pass
415
431
  // their own `class` to override.
416
432
  if (label !== undefined) {
417
- const toneClasses = TONE_CLASSES[tone] ?? TONE_CLASSES["primary-light"];
433
+ const toneClasses = lookup(TONE_CLASSES, tone) ?? TONE_CLASSES["primary-light"];
418
434
  return `<GridItem ${attrParts.join(" ")}${classAttr}${passthrough}>
419
435
  <div class="flex h-full min-h-16 items-center justify-center rounded-lg font-mono text-sm font-medium ${toneClasses}">${escapeTemplate(label)}</div>
420
436
  </GridItem>`;
@@ -1000,7 +1016,8 @@ function detailsToAstro(node, ctx) {
1000
1016
  ctx.imports.add("details");
1001
1017
  // `variant` as documented; `type` as dogsbay-ssg (and callouts) read it.
1002
1018
  const variant = node.props?.variant ?? node.props?.type ?? "note";
1003
- const title = node.props?.title ?? "Details";
1019
+ // `summary` is dogsbay-md's alias for the title (`:::details{summary="…"}`)
1020
+ const title = node.props?.title ?? node.props?.summary ?? "Details";
1004
1021
  const open = node.props?.open;
1005
1022
  const icon = node.props?.icon;
1006
1023
  const inner = childrenToAstro(node, ctx);
@@ -1011,11 +1028,16 @@ function detailsToAstro(node, ctx) {
1011
1028
  attrs.push(`icon="${escapeAttr(icon)}"`);
1012
1029
  const classAttr = mergeClassAttr("my-4", node.props?.class);
1013
1030
  const passthrough = renderPassthroughAttrs(node.props, [
1014
- "variant", "type", "title", "open", "icon", "class", "summary",
1031
+ "variant", "type", "title", "open", "icon", "class", "summary", "sheet",
1015
1032
  ]);
1016
- return `<CollapsibleAlert ${attrs.join(" ")}${classAttr}${passthrough}>
1033
+ const alert = `<CollapsibleAlert ${attrs.join(" ")}${classAttr}${passthrough}>
1017
1034
  ${indentStr(inner, 2)}
1018
1035
  </CollapsibleAlert>`;
1036
+ // `sheet`: the same details, opened as a side sheet by <db-details-sheet>
1037
+ if (!isDetailsSheet(node.props))
1038
+ return alert;
1039
+ ctx.imports.add("details-sheet");
1040
+ return `<DetailsSheet>\n${indentStr(alert, 2)}\n</DetailsSheet>`;
1019
1041
  }
1020
1042
  function tabsToAstro(node, ctx) {
1021
1043
  ctx.imports.add("tabs");
@@ -1052,11 +1074,15 @@ function tabsToAstro(node, ctx) {
1052
1074
  removed: "−",
1053
1075
  moved: "→",
1054
1076
  };
1077
+ // A variant's class strings are complete (TAB_VARIANTS): TabsList and
1078
+ // TabsTrigger merge a `class` over their base with tailwind-variants
1079
+ const variant = lookup(TAB_VARIANTS, node.props?.variant);
1080
+ const triggerClass = variant ? ` class="${escapeAttr(variant.trigger)}"` : "";
1055
1081
  const triggers = tabItems
1056
1082
  .map((t) => {
1057
1083
  const signal = tabDiffSignal(t.node);
1058
1084
  if (!signal) {
1059
- return ` <TabsTrigger${componentAttr("value", t.value)}>${escapeTemplate(t.label)}</TabsTrigger>`;
1085
+ return ` <TabsTrigger${componentAttr("value", t.value)}${triggerClass}>${escapeTemplate(t.label)}</TabsTrigger>`;
1060
1086
  }
1061
1087
  const glyph = TAB_GLYPH[signal] ?? "•";
1062
1088
  // The glyph and its announced text are ONE element: "Hide
@@ -1072,7 +1098,7 @@ function tabsToAstro(node, ctx) {
1072
1098
  // a title containing `&` was emitted `&amp;` here and `&` there, so the
1073
1099
  // trigger and its panel no longer paired and the tab rendered empty.
1074
1100
  // `data-diff-tab` is a plain data attribute — entity escaping is right.
1075
- return ` <TabsTrigger${componentAttr("value", t.value)} data-diff-tab="${escapeAttr(signal)}">${escapeTemplate(t.label)}<Fragment set:html={${escapeExpr(marker)}} /></TabsTrigger>`;
1101
+ return ` <TabsTrigger${componentAttr("value", t.value)}${triggerClass} data-diff-tab="${escapeAttr(signal)}">${escapeTemplate(t.label)}<Fragment set:html={${escapeExpr(marker)}} /></TabsTrigger>`;
1076
1102
  })
1077
1103
  .join("\n");
1078
1104
  const contents = tabItems
@@ -1088,7 +1114,7 @@ function tabsToAstro(node, ctx) {
1088
1114
  const syncAttr = syncKey ? componentAttr("syncKey", syncKey) : "";
1089
1115
  // Same encoding as the trigger/content `value` it selects — see above.
1090
1116
  return `<Tabs${componentAttr("defaultValue", defaultValue)}${syncAttr}${classAttr}${passthrough}>
1091
- <TabsList>
1117
+ <TabsList${variant ? ` class="${escapeAttr(variant.list)}"` : ""}>
1092
1118
  ${triggers}
1093
1119
  </TabsList>
1094
1120
  ${contents}
@@ -1355,7 +1381,7 @@ function cardsToAstro(node, ctx) {
1355
1381
  // Grid-level attributes on the :::cards container
1356
1382
  const outerClass = mergeClassAttr("", node.props?.class);
1357
1383
  const outerPass = renderPassthroughAttrs(node.props, [
1358
- "cols", "gap", "rows", "class",
1384
+ "cols", "gap", "rows", "class", "tone",
1359
1385
  ]);
1360
1386
  // MDX pattern: children are card nodes with title/href/icon props
1361
1387
  const cardNodes = (node.children ?? []).filter((c) => c.type === "card");
@@ -1389,12 +1415,15 @@ function cardsToAstro(node, ctx) {
1389
1415
  const desc = lead + (inner
1390
1416
  ? `<${descTag} class="text-sm text-muted-foreground mt-1 [text-decoration:none]">${inner}</${descTag}>`
1391
1417
  : "");
1418
+ // A tone (TONES; the card's own, else the group's) sets the card's
1419
+ // border and tint: Card's tailwind-variants merge replaces bg-card
1420
+ const toned = lookup(CARD_TONE_CLASSES, card.props?.tone ?? node.props?.tone);
1392
1421
  const cardDefaultClass = href
1393
- ? "p-4 hover:border-foreground/20 transition-colors"
1394
- : "p-4";
1422
+ ? toned ? `p-4 ${toned} hover:shadow-md transition-colors` : "p-4 hover:border-foreground/20 transition-colors"
1423
+ : toned ? `p-4 ${toned}` : "p-4";
1395
1424
  const cardClassAttr = mergeClassAttr(cardDefaultClass, card.props?.class);
1396
1425
  const cardPass = renderPassthroughAttrs(card.props, [
1397
- "title", "href", "description", "icon", "variant", "class",
1426
+ "title", "href", "description", "icon", "variant", "class", "tone",
1398
1427
  ]);
1399
1428
  if (href) {
1400
1429
  return `<Card${cardClassAttr}${cardPass}>\n<CardContent class="p-0">\n<a href="${escapeAttr(href)}" class="[text-decoration:none] text-foreground">\n${iconHtml}\n${titleHtml}\n${desc}\n</a>\n</CardContent>\n</Card>`;
@@ -1452,12 +1481,14 @@ function standaloneCardToAstro(node, ctx) {
1452
1481
  const desc = lead + (inner
1453
1482
  ? `<${descTag} class="text-sm text-muted-foreground mt-1 [text-decoration:none]">${inner}</${descTag}>`
1454
1483
  : "");
1484
+ // A tone (TONES), as on a card in a group (cardsToAstro)
1485
+ const toned = lookup(CARD_TONE_CLASSES, node.props?.tone);
1455
1486
  const cardDefaultClass = href
1456
- ? "p-4 hover:border-foreground/20 transition-colors"
1457
- : "p-4";
1487
+ ? toned ? `p-4 ${toned} hover:shadow-md transition-colors` : "p-4 hover:border-foreground/20 transition-colors"
1488
+ : toned ? `p-4 ${toned}` : "p-4";
1458
1489
  const classAttr = mergeClassAttr(cardDefaultClass, node.props?.class);
1459
1490
  const passthrough = renderPassthroughAttrs(node.props, [
1460
- "title", "href", "description", "icon", "variant", "class",
1491
+ "title", "href", "description", "icon", "variant", "class", "tone",
1461
1492
  ]);
1462
1493
  if (href) {
1463
1494
  return `<Card${classAttr}${passthrough}>\n<CardContent class="p-0">\n<a href="${escapeAttr(href)}" class="[text-decoration:none] text-foreground">\n${iconHtml}\n${titleHtml}\n${desc}\n</a>\n</CardContent>\n</Card>`;
@@ -1571,13 +1602,16 @@ function accordionToAstro(node, ctx) {
1571
1602
  ]);
1572
1603
  const items = (node.children ?? [])
1573
1604
  .filter((c) => c.type === "accordion-item")
1574
- .map((item) => accordionItemToAstro(item, ctx, /*standalone*/ false))
1605
+ .map((item, i) => accordionItemToAstro(item, ctx, /*standalone*/ false, i + 1))
1575
1606
  .join("\n");
1576
1607
  return `<Accordion${typeAttr}${defaultAttr}${classAttr}${passthrough}>\n${items}\n</Accordion>`;
1577
1608
  }
1578
- function accordionItemToAstro(node, ctx, standalone) {
1609
+ function accordionItemToAstro(node, ctx, standalone,
1610
+ /** 1-based position: an item with no `value` must not share one with its siblings */
1611
+ index = 1) {
1579
1612
  ctx.imports.add("accordion");
1580
- const value = node.props?.value ?? "item-1";
1613
+ // Items are matched by value: a shared fallback opened and closed them together
1614
+ const value = node.props?.value ?? `item-${index}`;
1581
1615
  const label = node.props?.label ?? node.props?.title ?? "Item";
1582
1616
  const valueAttr = componentAttr("value", value);
1583
1617
  const classAttr = mergeClassAttr("", node.props?.class);
@@ -1644,6 +1678,10 @@ function htmlContainerToAstro(node, ctx) {
1644
1678
  const inner = childrenToAstro(node, ctx);
1645
1679
  return `<div class="columns-1 sm:columns-2 gap-4 [&_ul]:m-0">\n${inner}\n</div>`;
1646
1680
  }
1681
+ // A container parsed from raw HTML (a one-line `<details>` body) carries
1682
+ // its content in `html`, not children — rendering only children dropped it
1683
+ if (!node.children?.length && node.html)
1684
+ return htmlToAstro(node.html, ctx);
1647
1685
  const inner = childrenToAstro(node, ctx);
1648
1686
  return `<div>\n${inner}\n</div>`;
1649
1687
  }
@@ -1706,4 +1744,3 @@ const indentStr = indent;
1706
1744
  function escapeHtml(s) {
1707
1745
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
1708
1746
  }
1709
- //# sourceMappingURL=serialize.js.map
package/dist/sitemap.d.ts CHANGED
@@ -105,4 +105,3 @@ export declare function buildSitemapIndex(options: BuildSitemapOptions): string;
105
105
  export declare function pageHref(slug: string | undefined, prefix: string): string;
106
106
  export declare function normalizePrefix(basePath: string | undefined): string;
107
107
  export declare function originOf(siteUrl: string): string;
108
- //# sourceMappingURL=sitemap.d.ts.map
package/dist/sitemap.js CHANGED
@@ -124,4 +124,3 @@ export function originOf(siteUrl) {
124
124
  function stripTrailingSlash(s) {
125
125
  return s.replace(/\/+$/, "");
126
126
  }
127
- //# sourceMappingURL=sitemap.js.map