@nanisoft/prism-llms 0.3.0 → 0.5.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 (175) hide show
  1. package/README.md +88 -0
  2. package/THIRD-PARTY-NOTICES.md +48 -0
  3. package/dist/data.json +3998 -375
  4. package/dist/demo-graph.d.ts +21 -12
  5. package/dist/demo-graph.js +37 -21
  6. package/dist/extractor.d.ts +24 -17
  7. package/dist/extractor.js +269 -153
  8. package/dist/index.d.ts +12 -7
  9. package/dist/index.js +9 -7
  10. package/dist/llms-full.txt +7988 -1989
  11. package/dist/llms.txt +97 -91
  12. package/dist/markdown.d.ts +69 -23
  13. package/dist/markdown.js +131 -72
  14. package/dist/md/blocks/app-shell-01.md +135 -0
  15. package/dist/md/blocks/auth-form-01.md +135 -0
  16. package/dist/md/blocks/cta-01.md +88 -0
  17. package/dist/md/blocks/data-table-01.md +196 -0
  18. package/dist/md/blocks/feature-grid-01.md +92 -0
  19. package/dist/md/blocks/hero-01.md +90 -0
  20. package/dist/md/blocks/instrument-panel-01.md +109 -0
  21. package/dist/md/blocks/logo-strip-01.md +97 -0
  22. package/dist/md/blocks/note-grid-01.md +102 -0
  23. package/dist/md/blocks/page-header-01.md +97 -0
  24. package/dist/md/blocks/pricing-01.md +91 -0
  25. package/dist/md/blocks/process-rail-01.md +106 -0
  26. package/dist/md/blocks/product-grid-01.md +113 -0
  27. package/dist/md/blocks/settings-panel-01.md +148 -0
  28. package/dist/md/blocks/site-footer.md +111 -0
  29. package/dist/md/blocks/site-header.md +119 -0
  30. package/dist/md/blocks/stack-grid-01.md +108 -0
  31. package/dist/md/blocks/stats-01.md +89 -0
  32. package/dist/md/blocks/status-ledger-01.md +121 -0
  33. package/dist/md/changelogs/prism-llms.md +206 -0
  34. package/dist/md/changelogs/prism-mcp-server.md +88 -0
  35. package/dist/md/changelogs/prism-tokens.md +126 -0
  36. package/dist/md/changelogs/prism-ui.md +429 -0
  37. package/dist/md/components/accordion.md +108 -0
  38. package/dist/md/components/alert.md +90 -7
  39. package/dist/md/components/avatar.md +89 -7
  40. package/dist/md/components/badge.md +74 -7
  41. package/dist/md/components/breadcrumb.md +84 -7
  42. package/dist/md/components/button.md +90 -7
  43. package/dist/md/components/card.md +90 -7
  44. package/dist/md/components/checkbox.md +101 -7
  45. package/dist/md/components/cta-link.md +98 -0
  46. package/dist/md/components/diagram.md +124 -0
  47. package/dist/md/components/dialog.md +128 -0
  48. package/dist/md/components/dropdown-menu.md +125 -0
  49. package/dist/md/components/fact-list.md +85 -0
  50. package/dist/md/components/field.md +116 -0
  51. package/dist/md/components/input.md +86 -7
  52. package/dist/md/components/kbd.md +77 -0
  53. package/dist/md/components/pagination.md +95 -7
  54. package/dist/md/components/popover.md +95 -7
  55. package/dist/md/components/product-mark.md +123 -0
  56. package/dist/md/components/product-switcher.md +88 -0
  57. package/dist/md/components/progress.md +88 -7
  58. package/dist/md/components/prose.md +91 -0
  59. package/dist/md/components/radio-group.md +111 -0
  60. package/dist/md/components/section.md +91 -0
  61. package/dist/md/components/select.md +143 -7
  62. package/dist/md/components/separator.md +77 -0
  63. package/dist/md/components/skeleton.md +71 -7
  64. package/dist/md/components/slider.md +90 -7
  65. package/dist/md/components/switch.md +104 -7
  66. package/dist/md/components/table.md +104 -7
  67. package/dist/md/components/tabs.md +105 -7
  68. package/dist/md/components/textarea.md +101 -0
  69. package/dist/md/components/tooltip.md +95 -7
  70. package/dist/md/components/typography.md +82 -7
  71. package/dist/md/content/about-content.md +36 -0
  72. package/dist/md/content/audience.md +43 -0
  73. package/dist/md/content/glossary.md +69 -0
  74. package/dist/md/content/product-vocabulary.md +53 -0
  75. package/dist/md/content/voice.md +52 -0
  76. package/dist/md/content/writing-mechanics.md +58 -0
  77. package/dist/md/foundation/colors.md +10 -0
  78. package/dist/md/foundation/iconography.md +14 -0
  79. package/dist/md/foundation/motion.md +10 -0
  80. package/dist/md/foundation/radii.md +9 -0
  81. package/dist/md/foundation/shadows.md +9 -0
  82. package/dist/md/foundation/spacings.md +8 -0
  83. package/dist/md/foundation/typography.md +9 -0
  84. package/dist/md/foundation/variables.md +9 -0
  85. package/dist/md/overview/architecture.md +76 -0
  86. package/dist/md/overview/brand.md +41 -0
  87. package/dist/md/overview/composition.md +77 -0
  88. package/dist/md/overview/quickstart.md +97 -0
  89. package/dist/md/overview/theming.md +89 -0
  90. package/dist/md/overview/using-llms.md +83 -0
  91. package/dist/md/pages/auth-page.md +149 -0
  92. package/dist/md/pages/blog-post-page.md +136 -0
  93. package/dist/md/pages/dashboard-page.md +241 -0
  94. package/dist/md/pages/marketing-page.md +199 -0
  95. package/dist/md/pages/not-found-page.md +105 -0
  96. package/dist/md/pages/settings-page.md +206 -0
  97. package/dist/prism-skill.md +50 -0
  98. package/dist/store.d.ts +203 -0
  99. package/dist/store.js +317 -0
  100. package/package.json +29 -26
  101. package/dist/demo-graph.d.ts.map +0 -1
  102. package/dist/demo-graph.js.map +0 -1
  103. package/dist/extractor.d.ts.map +0 -1
  104. package/dist/extractor.js.map +0 -1
  105. package/dist/index.d.ts.map +0 -1
  106. package/dist/index.js.map +0 -1
  107. package/dist/markdown.d.ts.map +0 -1
  108. package/dist/markdown.js.map +0 -1
  109. package/dist/md/blocks/component-demo.md +0 -19
  110. package/dist/md/blocks/page-header.md +0 -19
  111. package/dist/md/components/affix.md +0 -14
  112. package/dist/md/components/anchor.md +0 -14
  113. package/dist/md/components/auto-complete.md +0 -14
  114. package/dist/md/components/back-top.md +0 -14
  115. package/dist/md/components/border-beam.md +0 -14
  116. package/dist/md/components/calendar.md +0 -14
  117. package/dist/md/components/carousel.md +0 -14
  118. package/dist/md/components/cascader.md +0 -14
  119. package/dist/md/components/col.md +0 -14
  120. package/dist/md/components/collapse.md +0 -14
  121. package/dist/md/components/color-picker.md +0 -14
  122. package/dist/md/components/date-picker.md +0 -14
  123. package/dist/md/components/descriptions.md +0 -14
  124. package/dist/md/components/display-title.md +0 -20
  125. package/dist/md/components/divider.md +0 -14
  126. package/dist/md/components/drawer.md +0 -14
  127. package/dist/md/components/dropdown.md +0 -14
  128. package/dist/md/components/empty.md +0 -14
  129. package/dist/md/components/flex.md +0 -14
  130. package/dist/md/components/float-button.md +0 -14
  131. package/dist/md/components/form.md +0 -14
  132. package/dist/md/components/grid.md +0 -14
  133. package/dist/md/components/image.md +0 -14
  134. package/dist/md/components/input-number.md +0 -14
  135. package/dist/md/components/layout.md +0 -14
  136. package/dist/md/components/list.md +0 -14
  137. package/dist/md/components/listy.md +0 -14
  138. package/dist/md/components/masonry.md +0 -14
  139. package/dist/md/components/mentions.md +0 -14
  140. package/dist/md/components/menu.md +0 -14
  141. package/dist/md/components/modal.md +0 -14
  142. package/dist/md/components/popconfirm.md +0 -14
  143. package/dist/md/components/qrcode.md +0 -14
  144. package/dist/md/components/radio.md +0 -14
  145. package/dist/md/components/rate.md +0 -14
  146. package/dist/md/components/result.md +0 -14
  147. package/dist/md/components/row.md +0 -14
  148. package/dist/md/components/segmented.md +0 -14
  149. package/dist/md/components/space.md +0 -14
  150. package/dist/md/components/spin.md +0 -14
  151. package/dist/md/components/splitter.md +0 -14
  152. package/dist/md/components/statistic.md +0 -14
  153. package/dist/md/components/steps.md +0 -14
  154. package/dist/md/components/tag.md +0 -14
  155. package/dist/md/components/time-picker.md +0 -14
  156. package/dist/md/components/timeline.md +0 -14
  157. package/dist/md/components/tour.md +0 -14
  158. package/dist/md/components/transfer.md +0 -14
  159. package/dist/md/components/tree-select.md +0 -14
  160. package/dist/md/components/tree.md +0 -14
  161. package/dist/md/components/upload.md +0 -14
  162. package/dist/md/components/watermark.md +0 -14
  163. package/dist/md/docs/brand.md +0 -41
  164. package/dist/md/pages/blog-layout.md +0 -19
  165. package/dist/md/pages/docs-shell.md +0 -23
  166. package/dist/md/theme/blue-dark.md +0 -127
  167. package/dist/md/theme/blue-light.md +0 -129
  168. package/dist/md/theme/green-dark.md +0 -127
  169. package/dist/md/theme/green-light.md +0 -129
  170. package/dist/md/theme/lavender-dark.md +0 -127
  171. package/dist/md/theme/lavender-light.md +0 -129
  172. package/dist/md/theme/peach-dark.md +0 -127
  173. package/dist/md/theme/peach-light.md +0 -129
  174. package/dist/md/theme/rose-dark.md +0 -127
  175. package/dist/md/theme/rose-light.md +0 -129
@@ -0,0 +1,106 @@
1
+ # ProcessRail01
2
+
3
+ A process drawn as a rail: two, three or four steps on one line, each with an ordinal, a name and one caption.
4
+
5
+ ```tsx
6
+ import { ProcessRail01 } from '@nanisoft/prism-ui/blocks/process-rail-01'
7
+ ```
8
+
9
+ ## Overview
10
+
11
+ ProcessRail01 draws a process as a rail: the steps of a pipeline, in order, on
12
+ one line, each with an ordinal, a name and one caption.
13
+
14
+ All four NaniSoft sites compose this section, and each one drew it by hand with
15
+ the same three parts: an ordinal per step, a name, and one caption per step,
16
+ separated by hairlines with a label on the last one. This is that section once,
17
+ with the ordinal and the separation owned here rather than by four sets of site
18
+ CSS.
19
+
20
+ **The rail admits four steps and refuses five, in the type.** `steps` is a tuple
21
+ union rather than an array, so a fifth element is a compile error and not a fifth
22
+ column. The reason is that a rail is a claim about a sequence, and a rail that
23
+ quietly dropped a step to fit a width would be a diagram of a process that is not
24
+ the process. Four is the number every one of the four sites actually states; a
25
+ longer process is a different shape and earns its own Block.
26
+
27
+ ## Guidelines
28
+
29
+ ### When to use
30
+
31
+ Use ProcessRail01 for a process with two to four steps where each step is one
32
+ name and one line: a data path, a build pipeline, a review loop.
33
+
34
+ Use it when the order is the point. A set of independent features is a
35
+ `FeatureGrid01`, and a set of stages that do not run in sequence is a set.
36
+
37
+ ### When not to use
38
+
39
+ Do not use it for a process of five or more steps. It will not compile. That is
40
+ the design: a rail this wide stops reading as a line, and a longer process wants a
41
+ numbered list, which is a `Prose` and not a rail.
42
+
43
+ Do not use it for two steps that are really a before and an after. A rail with
44
+ two columns is two columns, and `NoteGrid01` says the same thing with less
45
+ ceremony.
46
+
47
+ ### Best practices
48
+
49
+ - Keep each caption to about twelve words. A caption that wraps under its step and
50
+ the row stops reading as a line; the detail belongs above the rail in
51
+ `description`.
52
+ - Pass `finalLabel` only when the rail genuinely ends in a state worth naming.
53
+ "serving", "merged" and "verified" are claims; a rail that has not finished
54
+ should say nothing.
55
+ - Leave `title` out when the rail is composed under a heading the page already
56
+ owns.
57
+
58
+ ### Content guidelines
59
+
60
+ - Name a step the way you would name it in conversation: a verb or a short noun
61
+ phrase, not a sentence.
62
+ - Write the caption as what happens, not as why it matters. The why belongs in
63
+ `description`.
64
+ - Do not number the steps yourself. The ordinal is rendered from the position, so
65
+ a list that carries its own numbers can disagree with its own order after a
66
+ sort.
67
+
68
+ **Four steps on one line, with the label on the last one.**
69
+
70
+ ```tsx
71
+ import { ProcessRail01 } from '@nanisoft/prism-ui/blocks/process-rail-01'
72
+
73
+ /** Four steps on one line, with the label on the last one. */
74
+ export default function ProcessRail01Demo() {
75
+ return (
76
+ <ProcessRail01
77
+ headingLevel="h3"
78
+ eyebrow="Preview"
79
+ title="The data path"
80
+ description="One line about the whole path, above the rail rather than inside it."
81
+ finalLabel="serving"
82
+ steps={[
83
+ { name: 'Collect', description: 'The whole surface, every minute.' },
84
+ { name: 'Conform', description: 'One timestamp, one symbol form.' },
85
+ { name: 'Verify', description: 'The column list held by test.' },
86
+ { name: 'Serve', description: 'One queryable view.' },
87
+ ]}
88
+ />
89
+ )
90
+ }
91
+ ```
92
+
93
+ ## Props
94
+
95
+ **`eyebrow`** `string` · optional · default: — — Optional label above the section title. See the No-Default-Eyebrow Rule.
96
+ **`title`** `string` · optional · default: — — The section title. Omit it for a rail that is composed under its own heading.
97
+ **`description`** `string` · optional · default: — — One or two sentences under the title.
98
+ **`finalLabel`** `string` · optional · default: — — The label on the last step, for a rail that ends in a state worth naming: "serving", "merged", "verified". Omit it when the last step needs no label, which is the honest state for a rail that has not finished.
99
+ **`steps`** `ProcessRail01Steps` · required · default: — — The steps, in the order the process runs. Two, three or four; a fifth is a type error. See `ProcessRail01Steps`.
100
+ **`headingLevel`** `HeadingLevel` · optional · default: — — Heading level for the section title. See `HeadingLevel`.
101
+
102
+ ## Composition
103
+
104
+ **`exports`** `ProcessRail01` — the public runtime names this Block promises
105
+ **`source`** `packages/ui/src/blocks/process-rail-01/index.tsx` — the file this Block is defined in
106
+ **`dependencies`** `Section` — the public Prism exports this item composes
@@ -0,0 +1,113 @@
1
+ # ProductGrid01
2
+
3
+ A set of products as hairline rows: a two-part ProductMark, a name, one tagline and a destination per row.
4
+
5
+ ```tsx
6
+ import { ProductGrid01 } from '@nanisoft/prism-ui/blocks/product-grid-01'
7
+ ```
8
+
9
+ ## Overview
10
+
11
+ ProductGrid01 is a set of products as a stack of hairline rows: a two-part mark, a
12
+ name, one tagline and a destination per row.
13
+
14
+ All four NaniSoft sites publish this section, and each one drew it by hand with
15
+ the same three parts in the same order: a colour mark for the product, the
16
+ product's name, and one line about what it is, on a hairline row with the whole
17
+ row a link. Each site also resolved the mark's ink in JavaScript from the live
18
+ mode, which is the part that is a second source of truth.
19
+
20
+ **The mark is two parts, and it is `ProductMark`.** The ring is `brand-ink` and
21
+ the core is the product's own `primary`, with the name in the brand ink beside it.
22
+ That split is settled by a measurement rather than by taste: a pastel brand value
23
+ is a fill and never text, so the core may be `primary` and the name may not, and a
24
+ *filled* mark on a light card measures under the threshold a mark has to clear, so
25
+ the mark is a hairline ring around the fill rather than a disc of it.
26
+
27
+ ## Guidelines
28
+
29
+ ### When to use
30
+
31
+ Use ProductGrid01 where the reader's question is "which of these should I open".
32
+ That is a family of products, a set of integrations, a set of sibling sites.
33
+
34
+ Use it when each member is one line long. A member that needs a paragraph is a
35
+ `FeatureGrid01` card, and a paragraph in a row makes the row a column of
36
+ paragraphs.
37
+
38
+ ### When not to use
39
+
40
+ Do not use it for a set of things that are not navigable. A grid of capabilities
41
+ is a `FeatureGrid01`; there is nothing to click, and a row that pretends to be a
42
+ link teaches a reader that everything on a page is.
43
+
44
+ Do not use it for two members. Two products side by side is a hero with two
45
+ things in it, and a hairline list of two is a list.
46
+
47
+ ### Best practices
48
+
49
+ - Order the rows by how likely a reader is to want each one. Order is a claim and
50
+ it is yours.
51
+ - Pass `newTab` per row rather than globally. Prism does not decide what counts as
52
+ external, and a same-site link that opens a tab is a reader who has lost the
53
+ page.
54
+ - Leave `pack` off the one member that has no pack of its own. That member wears
55
+ the spectrum.
56
+ - Use one mark size across the grid. The grid passes one size to every mark, so a
57
+ mixed set is not reachable from here.
58
+
59
+ ### Content guidelines
60
+
61
+ - Keep a tagline to one line. It is what a reader scans.
62
+ - Write the tagline as what the product is, not as what it does for the reader.
63
+ - Use the product's own name, in the case it uses everywhere else.
64
+
65
+ **Two members, one wearing a pack and one wearing the spectrum.**
66
+
67
+ ```tsx
68
+ import { ProductGrid01 } from '@nanisoft/prism-ui/blocks/product-grid-01'
69
+
70
+ /** Two members, one wearing a pack and one wearing the spectrum. */
71
+ export default function ProductGrid01Demo() {
72
+ return (
73
+ <ProductGrid01
74
+ headingLevel="h3"
75
+ eyebrow="Preview"
76
+ title="Built on one platform"
77
+ description="One line about the set, above the rows rather than inside one."
78
+ caption="The company root has no pack of its own, so its mark wears the spectrum."
79
+ products={[
80
+ {
81
+ id: 'nexus',
82
+ name: 'Nexus',
83
+ pack: 'lavender',
84
+ tagline: 'The agent factory: an issue in, a reviewed change out.',
85
+ href: '#nexus',
86
+ },
87
+ { id: 'www', name: 'NaniSoft', tagline: 'The company behind the platform.', href: '#www' },
88
+ ]}
89
+ />
90
+ )
91
+ }
92
+ ```
93
+
94
+ ## Props
95
+
96
+ **`eyebrow`** `string` · optional · default: — — Optional label above the section title. See the No-Default-Eyebrow Rule.
97
+ **`title`** `string` · optional · default: — — The section title. Omit it for a grid composed under its own heading.
98
+ **`description`** `string` · optional · default: — — One or two sentences under the title.
99
+ **`products`** `readonly ProductGrid01Product[]` · required · default: — — The products, in the order a reader should meet them. Order is the caller's because it is a claim about which product matters first.
100
+ **`caption`** `string` · optional · default: — — The line under the grid, for the sentence that qualifies it: what the set does not include, or where to read more.
101
+ **`headingLevel`** `HeadingLevel` · optional · default: — — Heading level for the section title. See `HeadingLevel`.
102
+ **`id`** `string` · required · default: — — The product's key within the set. Carried on the markup as `data-product` through `ProductMark`, so a test can name one product rather than the first one, and two products in one document are individually addressable.
103
+ **`name`** `string` · required · default: — — The product's own name, which is the second part of its mark. It is the anchor of the row and the first thing a reader reads.
104
+ **`pack`** `ProductMarkProps['pack']` · optional · default: — — The product's pack, which is the hue of the mark's core. Omit it, or pass `null`, for the one member of a set that has no pack of its own: that member wears the full spectrum rather than a colourless mark.
105
+ **`tagline`** `string` · required · default: — — One line about what the product is.
106
+ **`href`** `string` · required · default: — — Where the row goes. Rendered as a native anchor's `href`, so the row is a link with the browser's own affordances rather than a click handler.
107
+ **`newTab`** `boolean` · optional · default: — — Opens the destination in a new browsing context, which defaults the link relationship to `noopener noreferrer`. Prism does not decide what counts as external, so the caller declares it.
108
+
109
+ ## Composition
110
+
111
+ **`exports`** `ProductGrid01` — the public runtime names this Block promises
112
+ **`source`** `packages/ui/src/blocks/product-grid-01/index.tsx` — the file this Block is defined in
113
+ **`dependencies`** `ProductMark, Section` — the public Prism exports this item composes
@@ -0,0 +1,148 @@
1
+ # SettingsPanel01
2
+
3
+ A settings form grouped into sections of text, select and switch fields.
4
+
5
+ ```tsx
6
+ import { SettingsPanel01 } from '@nanisoft/prism-ui/blocks/settings-panel-01'
7
+ ```
8
+
9
+ ## Overview
10
+
11
+ SettingsPanel01 is a settings form grouped into named sections. The card holds
12
+ the title and description, each section holds a heading and its fields, and the
13
+ footer holds the save and cancel controls, with a rule between sections.
14
+
15
+ The block composes these components:
16
+
17
+ | Part | Component | Role |
18
+ | --- | --- | --- |
19
+ | Surface | Card, Separator | the panel and the rule between sections |
20
+ | Fields | Field, Input, Textarea | labelled text controls |
21
+ | Choices | Select, Switch | one-of-many and on-or-off fields |
22
+ | Footer | Button | the save and secondary actions |
23
+
24
+ A field is a small object that describes one control: its `kind`, its label, an
25
+ optional description and its value handlers. The block owns no values and no
26
+ copy. Omitting `submitLabel` renders no footer, the right shape for a read-only
27
+ view.
28
+
29
+ ## Guidelines
30
+
31
+ ### When to use
32
+
33
+ Use SettingsPanel01 when a screen changes several settings that share one save.
34
+ It works when the groups are stable and a reader scans them top to bottom.
35
+
36
+ ### When not to use
37
+
38
+ Do not use it for a single setting. One field in a card is a heavier frame than
39
+ the choice deserves; put the control in the page.
40
+
41
+ Do not use it when each change should apply immediately with no save. A switch
42
+ inside a saved form reads as applied; use standalone switches with their own
43
+ handler instead.
44
+
45
+ ### Best practices
46
+
47
+ - Give every field a stable `id`, so the label is linked to the control.
48
+ - Keep one save per panel. A mixed model, where some fields save and others do
49
+ not, makes the footer's promise unclear.
50
+ - Put the consequential choice first in its section, so a reader does not hunt
51
+ for the setting that matters most.
52
+ - Disable the submit control while a save is in flight.
53
+
54
+ ### Content guidelines
55
+
56
+ - A section title is one or two words in sentence case: "General",
57
+ "Notifications".
58
+ - A field label is a short noun phrase and does not end in a colon.
59
+
60
+ **A grouped settings form with text, select and switch fields.**
61
+
62
+ ```tsx
63
+ 'use client'
64
+
65
+ import { SettingsPanel01 } from '@nanisoft/prism-ui/blocks/settings-panel-01'
66
+
67
+ /** A grouped settings form with text, select and switch fields. */
68
+ export default function SettingsPanel01Demo() {
69
+ return (
70
+ <SettingsPanel01
71
+ title="Workspace settings"
72
+ description="Manage how this workspace behaves for everyone on the team."
73
+ submitLabel="Save changes"
74
+ secondaryAction={{ label: 'Cancel' }}
75
+ sections={[
76
+ {
77
+ id: 'general',
78
+ title: 'General',
79
+ description: 'Shown to everyone you collaborate with.',
80
+ fields: [
81
+ {
82
+ kind: 'text',
83
+ id: 'workspace-name',
84
+ label: 'Workspace name',
85
+ defaultValue: 'Northwind',
86
+ },
87
+ {
88
+ kind: 'textarea',
89
+ id: 'workspace-description',
90
+ label: 'Description',
91
+ placeholder: 'What does this workspace do?',
92
+ rows: 3,
93
+ },
94
+ {
95
+ kind: 'select',
96
+ id: 'workspace-region',
97
+ label: 'Data region',
98
+ placeholder: 'Choose a region',
99
+ defaultValue: 'eu-west',
100
+ options: [
101
+ { label: 'Europe West', value: 'eu-west' },
102
+ { label: 'US East', value: 'us-east' },
103
+ ],
104
+ },
105
+ ],
106
+ },
107
+ {
108
+ id: 'notifications',
109
+ title: 'Notifications',
110
+ fields: [
111
+ {
112
+ kind: 'switch',
113
+ id: 'weekly-digest',
114
+ label: 'Weekly digest',
115
+ description: 'A summary of activity every Monday.',
116
+ defaultChecked: true,
117
+ },
118
+ {
119
+ kind: 'switch',
120
+ id: 'mention-alerts',
121
+ label: 'Mention alerts',
122
+ description: 'Notify me when someone mentions my name.',
123
+ },
124
+ ],
125
+ },
126
+ ]}
127
+ />
128
+ )
129
+ }
130
+ ```
131
+
132
+ ## Props
133
+
134
+ **`title`** `string` · required · default: —
135
+ **`description`** `string` · optional · default: —
136
+ **`sections`** `SettingsPanelSection[]` · required · default: — — The settings groups, each rendered with a heading and its own fields.
137
+ **`submitLabel`** `string` · optional · default: — — The submit button label. Omit to render no footer.
138
+ **`onSubmit`** `(event: FormEvent<HTMLFormElement>) => void` · optional · default: —
139
+ **`secondaryAction`** `{ label: string; onClick?: () => void; }` · optional · default: — — A secondary control, such as Cancel.
140
+ **`footer`** `ReactNode` · optional · default: — — Extra content in the footer, e.g. a status note.
141
+ **`label`** `string` · required · default: —
142
+ **`onClick`** `() => void` · optional · default: —
143
+
144
+ ## Composition
145
+
146
+ **`exports`** `SettingsPanel01` — the public runtime names this Block promises
147
+ **`source`** `packages/ui/src/blocks/settings-panel-01/index.tsx` — the file this Block is defined in
148
+ **`dependencies`** `Button, Card, Field, Input, Select, Separator, Switch, Textarea` — the public Prism exports this item composes
@@ -0,0 +1,111 @@
1
+ # SiteFooter
2
+
3
+ The block at the foot of a product site: a brand lockup, grouped destinations, the links that leave the site and a slot for the legal line.
4
+
5
+ ```tsx
6
+ import { SiteFooter } from '@nanisoft/prism-ui/blocks/site-footer'
7
+ ```
8
+
9
+ ## Overview
10
+
11
+ SiteFooter is the block at the foot of a product site: a brand lockup, grouped
12
+ destinations, the links that leave the site, and an optional slot for the legal
13
+ line.
14
+
15
+ All four NaniSoft sites compose this exact footer in their root layout, and each
16
+ one wrote its own column model, its own mark and its own current-page treatment.
17
+
18
+ Every string is a prop and the block ships none. There is no site name, no
19
+ copyright, no year, no "built with" line and no default column: a footer that
20
+ hardcoded any of them would hand every consumer a claim about a product that is
21
+ not theirs and a year that was not this one.
22
+
23
+ ## Guidelines
24
+
25
+ ### When to use
26
+
27
+ Use SiteFooter as the chrome at the foot of a whole product site, in the root
28
+ layout, below the main region.
29
+
30
+ Use it wherever there are destinations a reader might otherwise not find: the
31
+ documentation, the blog, the repository, the company site.
32
+
33
+ ### When not to use
34
+
35
+ Do not use it inside an application screen. `AppShell01` owns a signed-in shell
36
+ and its own foot.
37
+
38
+ Do not use it for a single link. A footer with one destination is a link with a
39
+ large margin around it.
40
+
41
+ ### Best practices
42
+
43
+ - Group by what the links are, and name each group. Each column is its own
44
+ navigation region named by its title, so a reader navigating by landmark reaches
45
+ "Documentation" rather than one anonymous list of links.
46
+ - The columns collapse to one column on a narrow viewport, and a footer read as
47
+ three unreadable columns on a phone is three columns nobody reads. Group what
48
+ belongs together before you split it into columns.
49
+ - **Links that leave the site open in a new tab by default**, because a link that
50
+ leaves and comes back is a reader who has lost the page they were on. Pass
51
+ `newTab={false}` on one link to override it: Prism does not decide which
52
+ destinations are external, so the per-link prop wins.
53
+ - Put a social icon in `icon`. It is `aria-hidden`, so the label is announced once
54
+ rather than twice.
55
+ - Use `legal` for the copyright, the licence or the version. It is a slot because
56
+ every one of those is a fact about a specific product and about the year it was
57
+ published, and a block that held a year would be a block that went stale.
58
+
59
+ ### Content guidelines
60
+
61
+ - Keep a link label to one or two words. A footer is scanned.
62
+ - Name a column after what the links in it are, not after where they live.
63
+ - Keep the social labels the same words a reader would use for the destination.
64
+
65
+ **The site chrome: a brand lockup, grouped destinations, a social link and a legal slot.**
66
+
67
+ ```tsx
68
+ import { SiteFooter } from '@nanisoft/prism-ui/blocks/site-footer'
69
+
70
+ /** The site chrome: a brand lockup, grouped destinations, a social link and a legal slot. */
71
+ export default function SiteFooterDemo() {
72
+ return (
73
+ <SiteFooter
74
+ product={{ id: 'nexus', name: 'Nexus', pack: 'lavender' }}
75
+ columns={[
76
+ {
77
+ title: 'Site',
78
+ links: [
79
+ { label: 'Landing', href: '#landing' },
80
+ { label: 'Docs', href: '#docs' },
81
+ { label: 'Blog', href: '#blog' },
82
+ ],
83
+ },
84
+ {
85
+ title: 'Elsewhere',
86
+ links: [
87
+ { label: 'GitHub', href: '#github' },
88
+ { label: 'nanisoft.com', href: '#company', newTab: true },
89
+ ],
90
+ },
91
+ ]}
92
+ social={[{ label: 'GitHub', href: '#github' }]}
93
+ legal={<span>2026 NaniSoft. One design language, five sites.</span>}
94
+ />
95
+ )
96
+ }
97
+ ```
98
+
99
+ ## Props
100
+
101
+ **`product`** `Pick<ProductMarkProps, 'id' | 'name' | 'pack'>` · required · default: — — The product this footer belongs to: the id, name and pack its `ProductMark` is drawn from. The footer renders the mark rather than taking a slot, because the brand lockup is the one part of a footer that is never product-specific and every consumer would otherwise reimplement it.
102
+ **`columns`** `readonly SiteFooterColumn[]` · optional · default: — — The groups of destinations: the sections of this site, its documentation, and anywhere else the site sends a reader. Omit it for a footer that carries only the brand lockup.
103
+ **`social`** `readonly SiteFooterSocial[]` · optional · default: — — The links that leave the site. A `ReactNode` icon is drawn beside each label and hidden from assistive technology, so a screen reader announces the label once.
104
+ **`legal`** `ReactNode` · optional · default: — — A slot for the line at the foot of the footer: a copyright, a licence, a version, a legal link. A slot rather than a prop, because every one of those is a fact about a specific product and about the year it was published, and a Block that held a year would be a Block that went stale.
105
+ **`className`** `string` · optional · default: — — Layout only, exactly as on every Component. Changing a Prism-owned visual property from here is prohibited.
106
+
107
+ ## Composition
108
+
109
+ **`exports`** `SiteFooter` — the public runtime names this Block promises
110
+ **`source`** `packages/ui/src/blocks/site-footer/index.tsx` — the file this Block is defined in
111
+ **`dependencies`** `ProductMark` — the public Prism exports this item composes
@@ -0,0 +1,119 @@
1
+ # SiteHeader
2
+
3
+ The bar at the top of a product site: a brand lockup, an optional product switcher, the site navigation and a slot for application controls.
4
+
5
+ ```tsx
6
+ import { SiteHeader } from '@nanisoft/prism-ui/blocks/site-header'
7
+ ```
8
+
9
+ ## Overview
10
+
11
+ SiteHeader is the bar at the top of a product site: a brand lockup, an optional
12
+ product switcher, the site's own navigation, and a slot for the controls your
13
+ product owns.
14
+
15
+ All four NaniSoft sites compose this exact bar in their root layout, and each one
16
+ wrote its own: the mark in one place, the switcher in a popover in another, the
17
+ navigation in a third, and each wrote the focus and current-page wiring separately.
18
+
19
+ It is a Block and not a Component because it is a region of a page rather than a
20
+ control: it composes a `ProductMark`, a `ProductSwitcher` and a set of links, and
21
+ it owns the bar they sit in.
22
+
23
+ **There is no product directory in this package.** The product is passed as the
24
+ three facts its mark is drawn from rather than as an id this package would have to
25
+ resolve. That is the same answer `ProductMark` gives, and for the same reason: a
26
+ consumer that composes a set of its own products composes the header too.
27
+
28
+ ## Guidelines
29
+
30
+ ### When to use
31
+
32
+ Use SiteHeader as the chrome around a whole product site. One header per site, in
33
+ the root layout, above the main region.
34
+
35
+ Use it on the landing page and on every documentation and blog page alike. A
36
+ header that changes between sections of one site is two sites' worth of chrome.
37
+
38
+ ### When not to use
39
+
40
+ Do not use it inside an application screen. `AppShell01` is that: a rail, a top
41
+ bar and a mobile sheet, for signed-in state rather than for a public site.
42
+
43
+ Do not use it for a product site's in-page navigation. That is a `Tabs` or a
44
+ `Breadcrumb`.
45
+
46
+ ### Best practices
47
+
48
+ - Pass `product`, and pass `products` only when the site is one of several. A set
49
+ of one renders one mark, so pass nothing rather than a switcher with a single
50
+ member.
51
+ - Mark the current page with `current` on the one link whose destination is the
52
+ current route. The header marks it with `aria-current="page"`; it does not colour
53
+ it, because a colour is not a state a screen reader can read.
54
+ - `navLabel` and `productsLabel` are required, because they are words a reader
55
+ hears. A Block that ships no copy ships no reader-facing copy either, so the name
56
+ of your own landmarks is yours to pass.
57
+ - Put interactive controls in `actions`. It is a slot rather than a prop, because
58
+ an account menu, a sign-in link and a mode toggle are all application state and
59
+ none of them is a fact a header can be told.
60
+ - Use `sticky` only where the page scrolls. A sticky header on a page with no
61
+ scroll costs the reader a strip of viewport for nothing.
62
+ - The bar is one flex row that wraps. The switcher and the navigation wrap onto a
63
+ second line rather than overflowing, so a narrow viewport gets a taller header
64
+ and never a clipped one.
65
+
66
+ ### Content guidelines
67
+
68
+ - Keep a nav label to one word. It is a bar.
69
+ - Use each product's own name in the lockup, in the case it uses everywhere else.
70
+ - Order the nav by how often a reader goes there, not alphabetically.
71
+
72
+ **The site chrome: a brand lockup, the product set, the site nav and a slot.**
73
+
74
+ ```tsx
75
+ import { SiteHeader } from '@nanisoft/prism-ui/blocks/site-header'
76
+
77
+ /** The site chrome: a brand lockup, the product set, the site nav and a slot. */
78
+ export default function SiteHeaderDemo() {
79
+ return (
80
+ <SiteHeader
81
+ product={{ id: 'nexus', name: 'Nexus', pack: 'lavender' }}
82
+ navLabel="Site"
83
+ productsLabel="Products"
84
+ products={[
85
+ { id: 'nexus', name: 'Nexus', pack: 'lavender', href: '#nexus' },
86
+ { id: 'atlas', name: 'Atlas', pack: 'mint', href: '#atlas' },
87
+ { id: 'alphalens', name: 'AlphaLens', pack: 'blush', href: '#alphalens' },
88
+ ]}
89
+ nav={[
90
+ { label: 'Docs', href: '#docs' },
91
+ { label: 'Blog', href: '#blog' },
92
+ { label: 'About', href: '#about', current: true },
93
+ ]}
94
+ actions={<span className="text-muted-foreground text-xs">A control you own</span>}
95
+ />
96
+ )
97
+ }
98
+ ```
99
+
100
+ ## Props
101
+
102
+ **`product`** `Pick<ProductMarkProps, 'id' | 'name' | 'pack'>` · required · default: — — The product this header belongs to: the id, name and pack its `ProductMark` is drawn from. The header renders the mark itself rather than a slot, because a header whose brand lockup is a slot is a header every consumer has to reimplement, and that is the one part of a site header that is never product-specific.
103
+ **`products`** `readonly SwitcherProduct[]` · optional · default: — — The set of products to move between, rendered as a `ProductSwitcher` between the mark and the navigation. Omit it on a site that is the only product, and pass it on a site that is one of several. A set of one renders one mark, so pass nothing rather than a switcher with a single member.
104
+ **`nav`** `readonly SiteHeaderLink[]` · optional · default: — — The header's own destinations: the sections of this site. Omit it for a header that carries only the brand lockup.
105
+ **`navLabel`** `string` · required · default: — — The accessible name of the site navigation, which is also the visible word a screen reader announces before the list. It is required rather than defaulted because it is a word a reader hears, and a Block that ships no copy ships no reader-facing copy either. A header that defaulted this would put its own name on every consumer's site navigation, and "Site" is right for a company site and wrong for a product that has a docs section it wants called something else.
106
+ **`productsLabel`** `string` · optional · default: — — The accessible name of the product switcher. Required whenever `products` is passed, for the same reason `navLabel` is: it is a word a reader hears, and a Block that ships no copy ships none of those either. Omit it and the switcher takes the site navigation's name, which is right when the two are the same set of places and wrong when they are not.
107
+ **`actions`** `ReactNode` · optional · default: — — A slot for the controls a product owns on the right: an account menu, a sign in link, a mode toggle, a theme picker. A slot rather than a prop, because every one of those is application state and none of them is a fact a header can be told.
108
+ **`sticky`** `boolean` · optional · default: — — Keeps the header at the top of the viewport as the page scrolls. Off by default, because a sticky header is a decision about a page rather than about a header, and a header that is sticky on a page with no scroll costs the reader a strip of their viewport.
109
+ **`className`** `string` · optional · default: — — Layout only, exactly as on every Component. Changing a Prism-owned visual property from here is prohibited.
110
+ **`label`** `string` · required · default: — — The text of the link.
111
+ **`href`** `string` · required · default: — — Where the link goes.
112
+ **`current`** `boolean` · optional · default: — — Marks this link as the page the reader is on. Set it on the one link whose destination is the current route; leave it off every other link.
113
+ **`newTab`** `boolean` · optional · default: — — Opens the destination in a new browsing context, which defaults the link relationship to `noopener noreferrer`. Prism does not decide what counts as external: a cross-origin destination is not by itself a reason to open a tab.
114
+
115
+ ## Composition
116
+
117
+ **`exports`** `SiteHeader` — the public runtime names this Block promises
118
+ **`source`** `packages/ui/src/blocks/site-header/index.tsx` — the file this Block is defined in
119
+ **`dependencies`** `ProductMark, ProductSwitcher` — the public Prism exports this item composes