@viglet/viglet-design-system 2026.3.2 → 2026.3.4

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 (184) hide show
  1. package/README.md +334 -8
  2. package/dist/assets.cjs +1 -0
  3. package/dist/assets.es.js +2 -0
  4. package/dist/badge-locale-BxI9TwkE.js +579 -0
  5. package/dist/badge-locale-sIsBdP_e.cjs +7 -0
  6. package/dist/bento/bento-actions-menu.d.ts +42 -0
  7. package/dist/bento/bento-actions-menu.d.ts.map +1 -0
  8. package/dist/bento/bento-back-to-top.d.ts +11 -0
  9. package/dist/bento/bento-back-to-top.d.ts.map +1 -0
  10. package/dist/bento/bento-command-palette.d.ts +28 -0
  11. package/dist/bento/bento-command-palette.d.ts.map +1 -0
  12. package/dist/bento/bento-count-tile.d.ts +18 -0
  13. package/dist/bento/bento-count-tile.d.ts.map +1 -0
  14. package/dist/bento/bento-empty-state.d.ts +35 -0
  15. package/dist/bento/bento-empty-state.d.ts.map +1 -0
  16. package/dist/bento/bento-entity-shell.d.ts +123 -0
  17. package/dist/bento/bento-entity-shell.d.ts.map +1 -0
  18. package/dist/bento/bento-entity-tile.d.ts +40 -0
  19. package/dist/bento/bento-entity-tile.d.ts.map +1 -0
  20. package/dist/bento/bento-form-hero.d.ts +91 -0
  21. package/dist/bento/bento-form-hero.d.ts.map +1 -0
  22. package/dist/bento/bento-form-section.d.ts +31 -0
  23. package/dist/bento/bento-form-section.d.ts.map +1 -0
  24. package/dist/bento/bento-hero-icon-picker.d.ts +35 -0
  25. package/dist/bento/bento-hero-icon-picker.d.ts.map +1 -0
  26. package/dist/bento/bento-hero.d.ts +62 -0
  27. package/dist/bento/bento-hero.d.ts.map +1 -0
  28. package/dist/bento/bento-inline-edit.d.ts +52 -0
  29. package/dist/bento/bento-inline-edit.d.ts.map +1 -0
  30. package/dist/bento/bento-layout.d.ts +52 -0
  31. package/dist/bento/bento-layout.d.ts.map +1 -0
  32. package/dist/bento/bento-list-page.d.ts +124 -0
  33. package/dist/bento/bento-list-page.d.ts.map +1 -0
  34. package/dist/bento/bento-nav-rail.d.ts +32 -0
  35. package/dist/bento/bento-nav-rail.d.ts.map +1 -0
  36. package/dist/bento/bento-nav.d.ts +69 -0
  37. package/dist/bento/bento-nav.d.ts.map +1 -0
  38. package/dist/bento/bento-save-bar.d.ts +36 -0
  39. package/dist/bento/bento-save-bar.d.ts.map +1 -0
  40. package/dist/bento/bento-scroll-fade.d.ts +20 -0
  41. package/dist/bento/bento-scroll-fade.d.ts.map +1 -0
  42. package/dist/bento/bento-scroll-save-bar.d.ts +22 -0
  43. package/dist/bento/bento-scroll-save-bar.d.ts.map +1 -0
  44. package/dist/bento/bento-section-chrome.d.ts +60 -0
  45. package/dist/bento/bento-section-chrome.d.ts.map +1 -0
  46. package/dist/bento/bento-section.d.ts +27 -0
  47. package/dist/bento/bento-section.d.ts.map +1 -0
  48. package/dist/bento/bento-shortcuts-dialog.d.ts +15 -0
  49. package/dist/bento/bento-shortcuts-dialog.d.ts.map +1 -0
  50. package/dist/bento/bento-status-marker.d.ts +26 -0
  51. package/dist/bento/bento-status-marker.d.ts.map +1 -0
  52. package/dist/bento/bento-tile.d.ts +42 -0
  53. package/dist/bento/bento-tile.d.ts.map +1 -0
  54. package/dist/bento/bento-tones.d.ts +33 -0
  55. package/dist/bento/bento-tones.d.ts.map +1 -0
  56. package/dist/bento/bento-user-menu.d.ts +31 -0
  57. package/dist/bento/bento-user-menu.d.ts.map +1 -0
  58. package/dist/bento/index.d.ts +59 -0
  59. package/dist/bento/index.d.ts.map +1 -0
  60. package/dist/bento.cjs +1 -0
  61. package/dist/bento.css +442 -0
  62. package/dist/bento.es.js +1502 -0
  63. package/dist/breadcrumb.context-CGqjI0-U.js +914 -0
  64. package/dist/breadcrumb.context-DYeZEC65.cjs +1 -0
  65. package/dist/components/app-switcher.d.ts +2 -0
  66. package/dist/components/app-switcher.d.ts.map +1 -1
  67. package/dist/components/badge-locale.d.ts.map +1 -1
  68. package/dist/components/error-boundary.d.ts.map +1 -1
  69. package/dist/components/language-select.d.ts.map +1 -1
  70. package/dist/components/router/blank-slate.d.ts +10 -0
  71. package/dist/components/router/blank-slate.d.ts.map +1 -1
  72. package/dist/components/router/grid.list.d.ts +13 -0
  73. package/dist/components/router/grid.list.d.ts.map +1 -1
  74. package/dist/components/router/internal.sidebar.d.ts +39 -3
  75. package/dist/components/router/internal.sidebar.d.ts.map +1 -1
  76. package/dist/components/router/loading-provider.d.ts +6 -1
  77. package/dist/components/router/loading-provider.d.ts.map +1 -1
  78. package/dist/components/router/nav-main.d.ts +10 -0
  79. package/dist/components/router/nav-main.d.ts.map +1 -1
  80. package/dist/components/router/nav-user.d.ts +13 -0
  81. package/dist/components/router/nav-user.d.ts.map +1 -1
  82. package/dist/components/router/page-content.d.ts +10 -0
  83. package/dist/components/router/page-content.d.ts.map +1 -1
  84. package/dist/components/router/page-header.d.ts +13 -0
  85. package/dist/components/router/page-header.d.ts.map +1 -1
  86. package/dist/components/router/page.d.ts +13 -0
  87. package/dist/components/router/page.d.ts.map +1 -1
  88. package/dist/components/router/sticky.page.header.d.ts +14 -0
  89. package/dist/components/router/sticky.page.header.d.ts.map +1 -1
  90. package/dist/components/router/sub.page.d.ts +31 -11
  91. package/dist/components/router/sub.page.d.ts.map +1 -1
  92. package/dist/components/router/sub.page.header.d.ts +13 -0
  93. package/dist/components/router/sub.page.header.d.ts.map +1 -1
  94. package/dist/components/startup-first/startup-first.d.ts.map +1 -1
  95. package/dist/components/theme-provider.d.ts +37 -2
  96. package/dist/components/theme-provider.d.ts.map +1 -1
  97. package/dist/components/ui/floating-formulas-bg.d.ts +8 -1
  98. package/dist/components/ui/floating-formulas-bg.d.ts.map +1 -1
  99. package/dist/components/ui/form.d.ts +2 -2
  100. package/dist/components/ui/form.d.ts.map +1 -1
  101. package/dist/components/ui/gradient-button.d.ts.map +1 -1
  102. package/dist/components/ui/gradient-switch.d.ts.map +1 -1
  103. package/dist/components/ui/icon-picker-dialog.d.ts +35 -0
  104. package/dist/components/ui/icon-picker-dialog.d.ts.map +1 -0
  105. package/dist/components/ui/index.d.ts +1 -0
  106. package/dist/components/ui/index.d.ts.map +1 -1
  107. package/dist/components/ui/section-card.d.ts +3 -3
  108. package/dist/components/ui/section-card.d.ts.map +1 -1
  109. package/dist/components/ui/sidebar.d.ts.map +1 -1
  110. package/dist/components/ui/slider.d.ts +1 -1
  111. package/dist/components/ui/slider.d.ts.map +1 -1
  112. package/dist/components/viglet-app-switcher.d.ts +3 -0
  113. package/dist/components/viglet-app-switcher.d.ts.map +1 -1
  114. package/dist/contexts/user.context.d.ts +24 -0
  115. package/dist/contexts/user.context.d.ts.map +1 -1
  116. package/dist/exports.json +404 -0
  117. package/dist/floating-formulas-bg.cjs +1 -0
  118. package/dist/floating-formulas-bg.css +21 -0
  119. package/dist/floating-formulas-bg.es.js +84 -56
  120. package/dist/fonts/inter-cyrillic-ext-wght-normal.woff2 +0 -0
  121. package/dist/fonts/inter-cyrillic-wght-normal.woff2 +0 -0
  122. package/dist/fonts/inter-greek-ext-wght-normal.woff2 +0 -0
  123. package/dist/fonts/inter-greek-wght-normal.woff2 +0 -0
  124. package/dist/fonts/inter-latin-ext-wght-normal.woff2 +0 -0
  125. package/dist/fonts/inter-latin-wght-normal.woff2 +0 -0
  126. package/dist/fonts/inter-vietnamese-wght-normal.woff2 +0 -0
  127. package/dist/fonts/plus-jakarta-sans-cyrillic-ext-wght-normal.woff2 +0 -0
  128. package/dist/fonts/plus-jakarta-sans-latin-ext-wght-normal.woff2 +0 -0
  129. package/dist/fonts/plus-jakarta-sans-latin-wght-normal.woff2 +0 -0
  130. package/dist/fonts/plus-jakarta-sans-vietnamese-wght-normal.woff2 +0 -0
  131. package/dist/fonts.css +126 -0
  132. package/dist/hooks/use-grid-adapter.d.ts.map +1 -1
  133. package/dist/hooks/use-mobile.d.ts +10 -0
  134. package/dist/hooks/use-mobile.d.ts.map +1 -1
  135. package/dist/hooks/use-sub-page-breadcrumb.d.ts.map +1 -1
  136. package/dist/i18n/index.d.ts +136 -0
  137. package/dist/i18n/index.d.ts.map +1 -1
  138. package/dist/i18n/locales/en/bento.json.d.ts +51 -0
  139. package/dist/i18n/locales/en/common.json.d.ts +7 -1
  140. package/dist/i18n/locales/en/forms.json.d.ts +15 -1
  141. package/dist/i18n/locales/en/nav.json.d.ts +3 -1
  142. package/dist/i18n/locales/pt/bento.json.d.ts +51 -0
  143. package/dist/i18n/locales/pt/common.json.d.ts +7 -1
  144. package/dist/i18n/locales/pt/forms.json.d.ts +15 -1
  145. package/dist/i18n/locales/pt/nav.json.d.ts +3 -1
  146. package/dist/i18n-DoNdMLEd.cjs +1 -0
  147. package/dist/{i18n-C94rDRaM.js → i18n-DuNITbWa.js} +155 -27
  148. package/dist/i18n.cjs +1 -0
  149. package/dist/i18n.es.js +2 -1
  150. package/dist/index.cjs +1 -0
  151. package/dist/index.d.ts +0 -1
  152. package/dist/index.d.ts.map +1 -1
  153. package/dist/index.es.js +493 -750
  154. package/dist/lib/axios.d.ts.map +1 -1
  155. package/dist/lib/export-xlsx.d.ts +7 -1
  156. package/dist/lib/export-xlsx.d.ts.map +1 -1
  157. package/dist/lib/react-markers.d.ts +17 -0
  158. package/dist/lib/react-markers.d.ts.map +1 -0
  159. package/dist/lib/utils.d.ts +22 -0
  160. package/dist/lib/utils.d.ts.map +1 -1
  161. package/dist/loading-provider-1mgM91iY.cjs +1 -0
  162. package/dist/loading-provider-CMnOuvdR.js +224 -0
  163. package/dist/preset.css +103 -3
  164. package/dist/products-DL_YYXkV.cjs +1 -0
  165. package/dist/products-Du9KB6Xl.js +9 -0
  166. package/dist/{rolldown-runtime-CMqjfN_6.cjs → rolldown-runtime-BocRIvOZ.cjs} +1 -1
  167. package/dist/router.cjs +1 -0
  168. package/dist/router.es.js +334 -504
  169. package/dist/user.context-kPAqKN4o.js +512 -0
  170. package/dist/user.context-r50nSWER.cjs +1 -0
  171. package/dist/viglet-design-system.css +1 -1
  172. package/dist/vite/boot-loader.d.ts +21 -7
  173. package/dist/vite/boot-loader.d.ts.map +1 -1
  174. package/dist/{vite.cjs.js → vite.cjs} +8 -8
  175. package/dist/vite.es.js +29 -16
  176. package/package.json +66 -22
  177. package/scripts/check-duplicates.mjs +198 -0
  178. package/dist/breadcrumb.context-C7VCfh49.js +0 -1501
  179. package/dist/breadcrumb.context-Td6Tjc_z.cjs +0 -7
  180. package/dist/floating-formulas-bg.cjs.js +0 -1
  181. package/dist/i18n-DN_2YzjB.cjs +0 -1
  182. package/dist/i18n.cjs.js +0 -1
  183. package/dist/index.cjs.js +0 -1
  184. package/dist/router.cjs.js +0 -1
package/README.md CHANGED
@@ -1,13 +1,102 @@
1
1
  # Viglet Design System
2
2
 
3
- Shared component library, design tokens, hooks, utilities, and i18n for Viglet products (Turing, Dumont, Shio).
3
+ Shared component library, design tokens, hooks, utilities, and i18n for the six
4
+ applications that install it: the Turing, Dumont and Shio consoles,
5
+ the Cloud Console, the Cloud Home, and Schools. `consumers.json` is the list,
6
+ and it is what the guards read.
7
+
8
+ **[Browse the component catalogue](https://openviglet.github.io/viglet-design-system/)** — every
9
+ component, its variants and its props, rebuilt on each commit to `2026.3`. Look
10
+ there before writing a component: the catalogue is the answer to "does this
11
+ already exist".
12
+
13
+ The six applications that install this package are the consumers it holds itself
14
+ to, declared in [`consumers.json`](consumers.json) rather than remembered: each
15
+ entry names its framework, its chrome, the accent it renders with and the
16
+ subpaths it takes, and CI refuses prose that names a subset as though it were
17
+ the whole set. A seventh is one entry there, and the checks widen with it.
18
+
19
+ Three are Vite consoles (Turing, Shio, Dumont), one is a Vite platform home
20
+ (the Cloud Home), and two are Next applications (the Cloud Console and Schools).
21
+ The split matters: a Next consumer takes no `./router` and no `./vite`.
4
22
 
5
23
  ## Installation
6
24
 
7
25
  ```bash
8
- npm install @viglet/viglet-design-system
26
+ pnpm add @viglet/viglet-design-system
9
27
  ```
10
28
 
29
+ ## Trying a change in a product before publishing
30
+
31
+ A change here is a change to shared chrome, so the question is always what it
32
+ does to Shio, Turing, Dumont, the Cloud Console, the Cloud Home and Schools —
33
+ and the answer should not require a publish. `use:local` walks a products root,
34
+ so it reaches the first three; the other three are checked out elsewhere and
35
+ `npm pack` into them is the loop until that changes (VDS73). From this checkout:
36
+
37
+ ```bash
38
+ pnpm use:local # build, then push dist into every 2026.3 product on disk
39
+ pnpm use:local --list # show what it would write to, and stop
40
+ ```
41
+
42
+ It finds the consumers by reading their `package.json`, and finds where each one
43
+ keeps the installed copy by following the link its own package manager made, so
44
+ neither a version bump nor a switch of package manager breaks the loop. Nothing
45
+ in the product's manifest or lockfile changes: `pnpm install` in the product puts
46
+ the published build back.
47
+
48
+ Pass directories to narrow it, and `--no-build` to reuse the `dist` already on
49
+ disk:
50
+
51
+ ```bash
52
+ pnpm use:local ../shio/2026.3/shio-react --no-build
53
+ ```
54
+
55
+ One thing it refuses to do: push over a tree whose dependencies are behind. It
56
+ writes files, and files cannot re-resolve a dependency — so if a range moved
57
+ here since the product last installed, the manifest it copies would ask for a
58
+ version the directory beside it does not have, and the product would fail to
59
+ build on symbols that version does not export. It names those packages and
60
+ stops. That is a real limit rather than a check to switch off: a range that
61
+ moved reaches a product through a release, not through this. `--skip-dep-check`
62
+ pushes anyway when you know why you want it.
63
+
64
+ ## Catching a duplicate before it drifts
65
+
66
+ The package ships `dist/exports.json` — every name it exports, per entry point —
67
+ and a CLI that reads it. Run it in the product's own CI:
68
+
69
+ ```bash
70
+ viglet-ds-check-duplicates src
71
+ ```
72
+
73
+ It fails, naming the import that replaces each local copy:
74
+
75
+ ```
76
+ src/components/page-header.tsx:18 declares PageHeader
77
+ replace it with: import { PageHeader } from "@viglet/viglet-design-system/router"
78
+ ```
79
+
80
+ One-line re-export shims are the sanctioned pattern and are skipped. When a
81
+ collision is deliberate — a product component that renders that product's own
82
+ data and merely shares a name — keep it by writing the reason in the file:
83
+
84
+ ```ts
85
+ // viglet-ds-allow-duplicate AppFooter -- renders Shio's build version
86
+ ```
87
+
88
+ In a product's workflow, it is one step beside the lint:
89
+
90
+ ```yaml
91
+ - name: No local copy of a shared component
92
+ run: pnpm exec viglet-ds-check-duplicates src
93
+ ```
94
+
95
+ Add `--warn` to report without failing while the existing collisions are being
96
+ worked through, and drop it once the count is zero. `--json` prints the findings
97
+ for a bot to read; `--manifest <path>` checks against an export list other than
98
+ the installed one.
99
+
11
100
  ## Setup
12
101
 
13
102
  ### 1. Import styles
@@ -24,6 +113,37 @@ Or in your entry TypeScript/JavaScript:
24
113
  import "@viglet/viglet-design-system/styles";
25
114
  ```
26
115
 
116
+ The brand faces are a **separate import**, and one you probably want:
117
+
118
+ ```css
119
+ @import "@viglet/viglet-design-system/styles";
120
+ @import "@viglet/viglet-design-system/fonts";
121
+ ```
122
+
123
+ They used to be inside `./styles`, where they were 741 KB of its 964 KB —
124
+ Vite's library mode inlines every asset regardless of `assetsInlineLimit`, so
125
+ every subset of Inter and Plus Jakarta Sans was base64 in the CSS, twice over
126
+ because the preset imported them as well. Base64 of an already-compressed woff2
127
+ does not compress again, which is why 2,276 rules gzipped to 594 KB. Splitting
128
+ them takes `./styles` to 31 KB gzipped.
129
+
130
+ `./fonts` is 5 KB of rules pointing at 11 `.woff2` files shipped beside it in
131
+ `dist/fonts/`. Nothing in it resolves through your `node_modules`, and your CDN
132
+ and browser cache the faces apart from the rules — so a change to any rule no
133
+ longer re-downloads every font, and a product already serving Inter can drop
134
+ this copy.
135
+
136
+ **A page fetches about 77 KB of that, not 272 KB.** Each face carries a
137
+ `unicode-range`, so the browser asks only for the subsets your text needs; an
138
+ English or Portuguese page takes `latin` and nothing else, the accented
139
+ characters Portuguese uses being Latin-1 rather than `latin-ext`.
140
+ `size-budget.json` records both numbers, and CI fails if either moves — or if
141
+ the faces stop being subsetted at all.
142
+
143
+ **Upgrading:** add the second line, or the type falls back through `--font-sans`
144
+ to `system-ui`. That is a real look rather than a broken one, but it is not the
145
+ one the catalogue shows.
146
+
27
147
  ### 2. Initialize i18n
28
148
 
29
149
  ```ts
@@ -69,6 +189,42 @@ function App() {
69
189
  }
70
190
  ```
71
191
 
192
+ `ThemeProvider` is a thin wrapper over [`next-themes`](https://github.com/pacocoursey/next-themes),
193
+ which is a peer dependency. Three consequences worth knowing:
194
+
195
+ - **It renders on a server.** No `localStorage` is read during render, so a
196
+ server-rendered or React Server Components consumer can mount it.
197
+ - **There is one theme source.** This package's `Toaster` reads `next-themes`
198
+ directly, so mounting both used to run two theme systems writing the class
199
+ from two storage keys. It no longer does.
200
+ - **You can skip it.** A consumer that mounts `next-themes`'s own provider gets
201
+ the same result, and `useTheme` from here reads that one — useful when a
202
+ framework already sets the theme up for you.
203
+
204
+ `useTheme()` returns `{ theme, setTheme }`, where `theme` is the *setting*
205
+ (`"light" | "dark" | "system"`) rather than the resolved colour.
206
+
207
+ ### React Server Components
208
+
209
+ The published entries carry a `"use client"` directive where they need one, so a
210
+ Next App Router server component can import this package without adding a
211
+ directive of its own:
212
+
213
+ | Entry | |
214
+ |---|---|
215
+ | `.`, `./bento`, `./router`, `./floating-formulas-bg`, `./i18n` | client — they reach React state, context or a browser API |
216
+ | `./assets`, `./vite` | server-renderable — logo data, and a build-time Vite plugin |
217
+
218
+ `./i18n` is the one worth pointing at: it looks pure, and it pulls
219
+ `i18next-browser-languagedetector`. `./assets` staying unmarked is deliberate —
220
+ a server component can read a product logo without a client boundary.
221
+
222
+ **The directive is not the whole story for a Next consumer.** `./router` and
223
+ `./bento` import `react-router-dom` (see the peer note further down), which an
224
+ App Router application does not use. Being marked client makes them *compile*
225
+ under RSC; it does not make them route. Today a Next consumer takes the root
226
+ entry, `./assets`, `./floating-formulas-bg` and the styles.
227
+
72
228
  ### 4. Setup Axios CSRF protection
73
229
 
74
230
  ```ts
@@ -107,19 +263,40 @@ import {
107
263
 
108
264
  ```tsx
109
265
  import {
110
- PageHeader,
111
- SubPageHeader,
112
- GridList,
113
266
  DialogDelete,
114
- BlankSlate,
115
267
  LoadProvider,
116
- NavUser,
268
+ GradientButtonLink,
117
269
  AppFooter,
118
270
  LanguageSelect,
119
271
  ModeToggle,
120
272
  } from "@viglet/viglet-design-system";
121
273
  ```
122
274
 
275
+ A page's chrome — its header, its list, its sidebar — comes from the
276
+ [bento layer](#the-bento-layer) rather than from here. Read the next section
277
+ before reaching for `PageHeader`, `GridList` or `InternalSidebar`.
278
+
279
+ ### Two eras, and which one a new page should use
280
+
281
+ This package exports two complete page vocabularies. The **console era** is what the consoles ship on today; the **[bento layer](#the-bento-layer)** is the current one, and Turing has cut over to it. Every console-era export is marked `@deprecated` in its own types, so an editor strikes it through and offers the swap.
282
+
283
+ Nothing is being removed. No product has started cutting over, and a removal will get its own roadmap line rather than arriving as a side effect of this notice — so existing pages keep working and need no rush.
284
+
285
+ | Console era | Use instead | |
286
+ |---|---|---|
287
+ | `PageHeader` | `BentoHero` | Same title/subtitle/icon, plus a tone — the chip follows the product accent. |
288
+ | `SubPageHeader` | `BentoHero` | With `BentoBackLink` for the back target and `BentoActionsMenu` for the menu. |
289
+ | `StickyPageHeader` | `useBentoScrollFade` | The scroll state is separated from the chrome, so it also drives `BentoScrollSaveBar`. |
290
+ | `GridList` | `BentoListPage` | `BentoTileGrid` is the mosaic alone, for a page with its own chrome. |
291
+ | `BlankSlate` | `BentoEmptyState` | |
292
+ | `InternalSidebar` | `BentoNavRail` | The rail takes its nav array as a prop instead of importing routes. |
293
+ | `NavMain` | `BentoNavRail` | |
294
+ | `NavUser` | `BentoUserMenu` | Routes and feature flags arrive as props. |
295
+ | `SubPage` | `BentoEntityShell` | Renders through a render prop, so the shell never learns what an entity is. |
296
+ | `Page`, `PageContent` | — | No single replacement, deliberately: the shell is where a product is itself. Compose `BentoNavRail`, `BentoUserMenu` and `BentoCommandPalette`. |
297
+
298
+ `DialogDelete`, `LoadProvider` and `GradientButtonLink` are **not** console-era — the bento layer uses them itself, and they are not deprecated.
299
+
123
300
  ### Hooks
124
301
 
125
302
  ```tsx
@@ -146,6 +323,27 @@ import {
146
323
  } from "@viglet/viglet-design-system";
147
324
  ```
148
325
 
326
+ `exportToXlsx(data, headers, filename, sheetName?)` takes an optional sheet name; without one the sheet is named after `filename` (it used to be `Logging`, whatever you exported). The header row is written from `headers`, so an export with no rows still arrives with its column titles rather than as an empty file.
327
+
328
+ ### Product logos
329
+
330
+ ```tsx
331
+ import { productLogos, type ProductId } from "@viglet/viglet-design-system/assets";
332
+ ```
333
+
334
+ Their own subpath, not the root barrel. Vite's library mode inlines every asset
335
+ regardless of `assetsInlineLimit`, so anything the root barrel could reach was
336
+ base64 inside the root entry — `productLogos` names all four logos, and one of
337
+ them is 1.24 MB, which put 1.90 MB of PNG in front of every consumer. Nothing
338
+ renders that one: `VigletAppSwitcher` draws the Viglet Cloud entry with an icon.
339
+ Moving the map here left the root entry 56% lighter, and the size gate now
340
+ refuses any single inlined asset over 256 KB.
341
+
342
+ `VigletAppSwitcher` still imports the three logos it draws, so it needs no
343
+ change — and those three are now 256px rather than 1024–2375px, which is nine
344
+ times the 28 pixels the switcher renders them at. A test holds that ceiling,
345
+ because an asset this package can reach is an asset it ships.
346
+
149
347
  ### Models
150
348
 
151
349
  ```tsx
@@ -164,20 +362,47 @@ Accordion, Avatar, Badge, Breadcrumb, Button, Card, Checkbox, Dialog, Drawer, Dr
164
362
 
165
363
  ### App Components (23 components)
166
364
 
167
- AppFooter, BadgeColorful, BadgeLocale, BlankSlate, DialogDelete, GridList, InternalSidebar, LanguageSelect, LoadProvider, ModeToggle, NavMain, NavSecondary, NavUser, Page, PageContent, PageHeader, SubPage, SubPageHeader, ThemeProvider
365
+ AppFooter, BadgeColorful, BadgeLocale, DialogDelete, GradientButtonLink, LanguageSelect, LoadProvider, ModeToggle, NavSecondary, ThemeProvider
366
+
367
+ Console-era, `@deprecated`, still exported — see [the swap table](#two-eras-and-which-one-a-new-page-should-use): BlankSlate, GridList, InternalSidebar, NavMain, NavUser, Page, PageContent, PageHeader, StickyPageHeader, SubPage, SubPageHeader
168
368
 
169
369
  ### Hooks
170
370
 
171
371
  `useIsMobile`, `useDateLocale`, `useGridAdapter`, `useSubPageBreadcrumb`
172
372
 
373
+ `useGridAdapter(data, config)` tracks each extractor in `config`, not the object holding them, so a `url` builder that closes over a route param or a locale re-maps the rows when it changes. Pass the config inline if you like — keep the extractors themselves stable (a field name, a module-level function, a `useCallback`) and the memo holds.
374
+
173
375
  ### Contexts
174
376
 
175
377
  `BreadcrumbProvider` / `useBreadcrumb`, `UserProvider` / `useCurrentUser`, `ThemeProvider` / `useTheme`
176
378
 
379
+ `useCurrentUser()` returns `status` (`"loading" | "ready" | "failed"`) and `error` alongside `user` and `refreshUser`. Read `status` rather than inferring one from an empty `user` — the same empty object means a request that has not settled, a session that expired, and an account with no username. A rejecting `fetchUser` sets `status: "failed"` and is not rethrown.
380
+
177
381
  ### Design Tokens
178
382
 
179
383
  OKLCH-based color system with light/dark mode, CSS variables for theming, consistent radius scale, chart palette, sidebar theme, animations.
180
384
 
385
+ **The brand accent is one token, set once.** The tinted icon chip in a page header, the ring around it, an accented label and the solid primary fill all derive from two gradient stops, so a product picks its colour at the root rather than in every component:
386
+
387
+ ```css
388
+ :root {
389
+ --vg-accent-from: oklch(70.5% 0.213 47.604);
390
+ --vg-accent-to: oklch(64.6% 0.222 41.116);
391
+ --vg-accent-text: oklch(55.3% 0.195 38.402); /* readable on light */
392
+ --vg-accent-text-dark: oklch(75% 0.183 55.934); /* readable on dark */
393
+ }
394
+ ```
395
+
396
+ The tint, the strong tint and the hairline (`--vg-accent-surface`, `--vg-accent-surface-strong`, `--vg-accent-line`) are `color-mix` over `--vg-accent-from`, so they follow automatically; only the readable foreground is stated, because contrast is not a mix away. Use `--vg-accent-fg` in a component — it is already resolved for the current theme, so no `dark:` twin is needed.
397
+
398
+ **Declare them on `:root`, not on a wrapper.** A custom property substitutes its `var()` references where it is *declared*, so the derived tokens above are mixed against the `--vg-accent-from` declared in the preset's `:root`. Re-keying a subtree moves what a utility class mixes on the element — the chip, the solid fill — and leaves the tint, the hairline and the button fill at the root's value. The result looks like a component that half-ignores the theme.
399
+
400
+ Three utility classes cover the common shapes: `vg-accent-chip` (the tinted gradient chip — pair it with Tailwind's `ring-1`), `vg-accent-text` (an accented label or icon), and `vg-accent-solid` (a solid gradient fill).
401
+
402
+ `GradientButton` and `GradientSwitch` follow the accent in their primary variants (`default`, `outline`, `ghost`), so a re-key reaches the buttons too; their `secondary`, `destructive` and `success` variants keep fixed semantic hues, because "destructive" does not change colour with the brand.
403
+
404
+ This is separate from a component's **colour palette**. `SectionCard` (`blue | violet | emerald | amber | rose | cyan`) and `StickySaveBar` (`gray | blue | orange | green`) are keyed by hue — the caller picked that colour deliberately — so re-keying the accent leaves them alone, the same way it leaves the [bento tones](#the-bento-layer) alone.
405
+
181
406
  ### i18n
182
407
 
183
408
  Base translations (EN/PT) for common UI strings: buttons, form labels, dialog text, navigation, theme.
@@ -195,6 +420,107 @@ Base translations (EN/PT) for common UI strings: buttons, form labels, dialog te
195
420
  - Sonner for toast notifications
196
421
  - Lucide + Tabler icons
197
422
 
423
+ ## The bento layer
424
+
425
+ Turing grew a second era of chrome — the frosted tile, the airy hero, the
426
+ mosaic — inside one product while this package sat next to it exporting console
427
+ chrome. It lives here now: the components, their stylesheet, their strings and
428
+ their suites. Turing consumes them through one-line re-exports at the paths its
429
+ pages already imported, so a product adopting the layer writes the imports above
430
+ rather than a second copy.
431
+
432
+ What stayed behind is the product's, deliberately: commercial chrome (activation,
433
+ quota, the first-run tour) and navigation data. `BentoListPage` takes a `layout`
434
+ object rather than fetching one, because where a layout is stored is the
435
+ product's business — its API, its cache, its mutation library.
436
+
437
+ It is a separate entry point, so a console still on the first era carries none
438
+ of it:
439
+
440
+ ```ts
441
+ import "@viglet/viglet-design-system/bento.css";
442
+ import {
443
+ BentoEntityShell,
444
+ BentoListPage,
445
+ BentoFormHero,
446
+ BentoNavRail,
447
+ BentoCommandPalette,
448
+ } from "@viglet/viglet-design-system/bento";
449
+ ```
450
+
451
+ The stylesheet is a separate import from the components, so a consumer taking
452
+ only the layout maths does not pull CSS it never renders. It reads the preset's
453
+ tokens, so import the preset too.
454
+
455
+ "Carries none of it" is measured rather than claimed. `pnpm run build` ends by
456
+ bundling three fixtures through this package's own `exports` map, and fails if a
457
+ bento module, a bento class name or a run of `bento.css`'s selectors reaches a
458
+ consumer that imported only the root entry. `size-budget.json` records what each
459
+ entry costs: today the whole root entry is 87 KB gzipped and `./bento` 22 KB,
460
+ measured with everything the build externalises left out, so the numbers
461
+ describe this package rather than its dependencies. The preset's
462
+ `--vg-bento-tone-*` tokens are deliberately not counted as the layer: they ship
463
+ with every other token and are about a kilobyte of custom properties.
464
+
465
+ The test is whether a root consumer carries the subpath's **code**, not whether
466
+ the subpath exists. `./floating-formulas-bg` has its own entry *and*
467
+ `FloatingFormulasBg` is exported from the root barrel, where `Login` and
468
+ `StartupFirst` render it — so its rules are in `./styles` too, and that is
469
+ correct rather than a leak. The subpath is for a consumer who wants only that
470
+ background.
471
+
472
+ Every subpath in the table above is also **imported, required and type-checked
473
+ as you would use it** on each build: one fixture pulls all fifteen through the
474
+ real `exports` map, a CommonJS probe requires the seven that offer it, and a
475
+ generated TypeScript probe imports a value from each typed entry and uses it —
476
+ so a `types` field resolving to the wrong declarations fails here rather than
477
+ turning into `any` in your editor.
478
+
479
+ That last check found a real one. `"type": "module"` makes Node read any `.js`
480
+ as ESM, and the CommonJS entries were named `<entry>.cjs.js`, so every
481
+ `require()` of this package failed. They are `<entry>.cjs` now. No product met
482
+ it, because every consumer — the Vite ones through their bundler, the Next ones
483
+ through the App Router — takes the `import` condition.
484
+
485
+ **`./bento` requires `react-router-dom`.** The package declares that peer
486
+ optional because the root entry does not need it — only `./router` and `./bento`
487
+ do, and npm cannot mark a peer required for one entry point and optional for
488
+ another. `pnpm run build` fails if any other entry starts importing it, so the
489
+ split above stays true rather than becoming folklore.
490
+
491
+ [docs/BENTO-AUTHORING.md](docs/BENTO-AUTHORING.md) is the contract for writing a
492
+ bento page: the three page shapes, what the package will not hold for you, and
493
+ the colour, i18n, accessibility and layout rules that keep two consoles looking
494
+ like the same product. Read it before the first screen, not after the fifth.
495
+
496
+ [docs/BENTO-BOUNDARY.md](docs/BENTO-BOUNDARY.md) says which exports become part
497
+ of this package and which stay in the product, and why. Four of them are one
498
+ product's commercial offer rendered as cards, and shipping those as chrome would
499
+ put that offer in every console.
500
+
501
+ ## Contributing
502
+
503
+ The package manager here is pnpm, matching the Shio, Turing, Dumont, Cloud Home
504
+ and Schools workspaces. The Cloud Console installs with npm, which is a fact
505
+ about that repository and not about this one.
506
+
507
+ ```bash
508
+ pnpm install
509
+ pnpm lint # eslint
510
+ pnpm typecheck # tsc -b --force
511
+ pnpm test # vitest
512
+ pnpm build # tsc, vite, and the dist checks
513
+ pnpm storybook # the component catalogue, on :6006
514
+ ```
515
+
516
+ CI runs all of those on every push and pull request, and the publish workflow
517
+ re-runs them before it releases.
518
+
519
+ `pnpm build` ends in `scripts/check-dist.mjs`, which fails the build when dist
520
+ would be broken for consumers in a way nothing here can see — a declaration
521
+ importing through a `node_modules` path, a test artefact in the published tree,
522
+ or an `exports` entry the build did not produce.
523
+
198
524
  ## License
199
525
 
200
526
  Apache-2.0
@@ -0,0 +1 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./products-DL_YYXkV.cjs");exports.dumontLogoUrl=e.n,exports.productLogos=e.t,exports.shioLogoUrl=e.r,exports.turingLogoUrl=e.i,exports.vigletLogoUrl=e.a;
@@ -0,0 +1,2 @@
1
+ import { a as e, i as t, n, r, t as i } from "./products-Du9KB6Xl.js";
2
+ export { n as dumontLogoUrl, i as productLogos, r as shioLogoUrl, t as turingLogoUrl, e as vigletLogoUrl };