@viglet/viglet-design-system 2026.3.5 → 2026.3.8
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.
- package/README.md +33 -13
- package/dist/components/login/login.d.ts.map +1 -1
- package/dist/exports.json +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.es.js +1 -1
- package/dist/preset.css +38 -13
- package/dist/viglet-design-system.css +1 -1
- package/dist/vite/boot-loader.d.ts +4 -2
- package/dist/vite/boot-loader.d.ts.map +1 -1
- package/docs/BENTO-AUTHORING.md +225 -0
- package/docs/BENTO-BOUNDARY.md +114 -0
- package/docs/reference/README.md +46 -0
- package/docs/reference/canvas.json +108 -0
- package/docs/reference/detail.dc.html +167 -0
- package/docs/reference/form-hero.dc.html +135 -0
- package/docs/reference/grounds.dc.html +140 -0
- package/docs/reference/list.dc.html +147 -0
- package/docs/reference/page-anatomy.dc.html +173 -0
- package/docs/reference/panel.dc.html +119 -0
- package/docs/reference/primary.dc.html +132 -0
- package/docs/reference/reading-column.dc.html +116 -0
- package/docs/reference/shell.dc.html +133 -0
- package/package.json +9 -4
- package/scripts/page-reference.mjs +298 -0
|
@@ -65,10 +65,12 @@ export interface VigletBootLoaderOptions {
|
|
|
65
65
|
* build/dev time, so the loader visuals stay identical across the ecosystem and
|
|
66
66
|
* there is a single place to evolve the design.
|
|
67
67
|
*
|
|
68
|
-
* Four of the
|
|
68
|
+
* Four of the seven consumers take it: Turing, Shio, Dumont and the Cloud Home.
|
|
69
69
|
* The Cloud Console and Schools are Next applications and have no Vite build to
|
|
70
70
|
* plug into — they render their own first paint, which is what a server-rendered
|
|
71
|
-
* page does instead of a boot loader.
|
|
71
|
+
* page does instead of a boot loader. The Roadkeep GUI has a Vite build and
|
|
72
|
+
* still does not take it: it ships as a desktop application, so its bundle is on
|
|
73
|
+
* disk and nothing is waiting on a fetch to paint.
|
|
72
74
|
*
|
|
73
75
|
* What the plugin injects:
|
|
74
76
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"boot-loader.d.ts","sourceRoot":"","sources":["../../src/vite/boot-loader.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA4B,MAAM,EAAE,MAAM,MAAM,CAAC;AAE7D;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,uBAAuB;IACtC,oEAAoE;IACpE,KAAK,EAAE,MAAM,CAAC;IACd,mFAAmF;IACnF,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;;;;OAOG;IACH,KAAK,EAAE,MAAM,CAAC;IACd,0FAA0F;IAC1F,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sFAAsF;IACtF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED
|
|
1
|
+
{"version":3,"file":"boot-loader.d.ts","sourceRoot":"","sources":["../../src/vite/boot-loader.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA4B,MAAM,EAAE,MAAM,MAAM,CAAC;AAE7D;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,uBAAuB;IACtC,oEAAoE;IACpE,KAAK,EAAE,MAAM,CAAC;IACd,mFAAmF;IACnF,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;;;;OAOG;IACH,KAAK,EAAE,MAAM,CAAC;IACd,0FAA0F;IAC1F,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sFAAsF;IACtF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,MAAM,CAsEzE"}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
# Authoring a bento page
|
|
2
|
+
|
|
3
|
+
The rules that keep every bento screen looking like the same product. They held
|
|
4
|
+
across a hundred-odd pages in one console because one team read one file; this
|
|
5
|
+
is that file, addressed to anyone importing
|
|
6
|
+
`@viglet/viglet-design-system/bento`.
|
|
7
|
+
|
|
8
|
+
The first mistake is a page that looks bento inside a console that does not.
|
|
9
|
+
Adopt the shell before the pages.
|
|
10
|
+
|
|
11
|
+
## 1. The shell, and the page's regions
|
|
12
|
+
|
|
13
|
+
The shell is these regions, and every one of them is owned once — by the shell, by a page,
|
|
14
|
+
or by the product passing a prop. Almost all of the divergence between two products built
|
|
15
|
+
from this layer is an argument about that ownership rather than about a component.
|
|
16
|
+
[docs/reference/page-anatomy.dc.html](reference/page-anatomy.dc.html) draws it.
|
|
17
|
+
|
|
18
|
+
**The nav is the rail** — fixed, one width, desktop only, with its gutter reserved by
|
|
19
|
+
`bento-rail-gutter` on whatever wraps the routed page (§7).
|
|
20
|
+
|
|
21
|
+
**The header carries a set, in this order:** the mark and wordmark; a back control where
|
|
22
|
+
the route has a parent; the palette trigger, with the platform's own keyboard hint. On the
|
|
23
|
+
trailing edge: the locale where a second one ships, the ground, and the signed-in user.
|
|
24
|
+
`BentoBackToTop` sits at the corner.
|
|
25
|
+
|
|
26
|
+
Two things the header is not. It is not a second always-visible nav — the rail is the nav
|
|
27
|
+
and the palette is the mobile one, and a second eats the width the content needs. And it is
|
|
28
|
+
not a home for one surface's controls: a switcher or a pending count belongs to the surface
|
|
29
|
+
that owns it, not to every page that renders beneath it.
|
|
30
|
+
|
|
31
|
+
**The shell owns the reading column.** `main` sets the max width, the gutters and the
|
|
32
|
+
vertical rhythm once, so every page begins and ends on the same line; a page sets none of
|
|
33
|
+
the three. A narrower column for a single-question form is a variant the shell offers by
|
|
34
|
+
name, not a class each page repeats. The moment pages set their own they disagree, and the
|
|
35
|
+
defect exists only *between* screens — which is why nobody reviewing one of them sees it.
|
|
36
|
+
|
|
37
|
+
**A page may own an aside**, inside that column and scrolling with it: filters, a contents
|
|
38
|
+
list, a conversation. That is not the console era's sidebar, which collapses, remembers its
|
|
39
|
+
width and pushes content — the reason that era needs a provider and this one does not.
|
|
40
|
+
|
|
41
|
+
**The footer is the one region this chrome has not settled.** `AppFooter` is exported — a
|
|
42
|
+
hairline, then the product name, its version and a few links — and it is written `mt-auto`,
|
|
43
|
+
so it expects a shell that is a flex column with the main growing. No bento shell mounts
|
|
44
|
+
one. Either the shell carries a footer for every page or the chrome has none; what must not
|
|
45
|
+
happen is one page growing its own.
|
|
46
|
+
|
|
47
|
+
**The signed-in user is two routes**, and `BentoUserMenu` has a default for neither (§3).
|
|
48
|
+
Anything past them — a tenant, a review count — belongs to the surface that owns it.
|
|
49
|
+
|
|
50
|
+
## 2. Structure — thin config, not bespoke pages
|
|
51
|
+
|
|
52
|
+
There are three page shapes. Almost every screen is one of them plus data.
|
|
53
|
+
|
|
54
|
+
**A detail screen** is `BentoEntityShell` wrapping a form. The shell owns the
|
|
55
|
+
identity hero, the save-bar morph and the delete flow; the form is a render prop
|
|
56
|
+
receiving `{ staged, onStateChange }` and groups its fields in
|
|
57
|
+
`BentoFormSection`s. Wire the query hooks in the page and put no shell mechanics
|
|
58
|
+
there.
|
|
59
|
+
|
|
60
|
+
**A list screen** is one `BentoListPage` call with a `renderTile`. Use
|
|
61
|
+
`BentoEntityTile` for the common icon-chip + status-pill + title + meta shape;
|
|
62
|
+
hand-roll a tile only when the entity genuinely needs a different layout.
|
|
63
|
+
|
|
64
|
+
**A form screen with its own hero** is `BentoFormHero` as the first child inside
|
|
65
|
+
the `<form>` it submits. It renders both halves of the morph itself.
|
|
66
|
+
|
|
67
|
+
**A frosted box with arbitrary content** — a stats strip, a toolbar, a listing,
|
|
68
|
+
a message — is `BentoPanel`. It is the one container in this layer with **no
|
|
69
|
+
heading**, which is the point rather than an omission: those surfaces sit under a
|
|
70
|
+
hero that already names the page, and a heading on them is noise that also puts a
|
|
71
|
+
section in the document outline the page does not have. Two class slots,
|
|
72
|
+
`className` on the frosted container and `contentClassName` on the inner wrapper,
|
|
73
|
+
and no padding of its own — a table wants `p-0` and a toolbar wants `py-2`.
|
|
74
|
+
Never hand-roll `bento-glass rounded-2xl border`: the moment two call sites pick
|
|
75
|
+
different radii the product is inconsistent for a reason no diff shows.
|
|
76
|
+
|
|
77
|
+
Two rules that outrank convenience:
|
|
78
|
+
|
|
79
|
+
- **Identity lives in the hero, never in the form.** Title, description, icon
|
|
80
|
+
and enabled state are the shell's; a form field for any of them is a second
|
|
81
|
+
place to edit the same thing.
|
|
82
|
+
- **The save bar is always the morph.** Controls start in the hero and a fixed
|
|
83
|
+
bar fades in as the hero scrolls away, so nothing is duplicated on screen and
|
|
84
|
+
the bar appears exactly when the title leaves. Never a hand-rolled
|
|
85
|
+
`<div className="sticky …">`, and never a permanently visible bar.
|
|
86
|
+
- `BentoEntityShell` gives you this for free.
|
|
87
|
+
- Every own-hero form uses `BentoFormHero`. Do not compose a fade-out control
|
|
88
|
+
group and a `BentoScrollSaveBar` yourself: they are two halves that drift.
|
|
89
|
+
- A page that saves **imperatively** passes its own buttons through
|
|
90
|
+
`BentoFormHero`'s `actions`, which replaces the default pair in *both*
|
|
91
|
+
copies and keeps the morph.
|
|
92
|
+
- Keep the **destructive** action in the hero's `trailing` only. A controlled
|
|
93
|
+
dialog rendered twice opens two modals at once, and an action inside the
|
|
94
|
+
fade-out group vanishes as the reader scrolls.
|
|
95
|
+
|
|
96
|
+
## 3. What the package will not hold
|
|
97
|
+
|
|
98
|
+
The layer is chrome; the map of your product is yours. Four things arrive as
|
|
99
|
+
props, and the package has no default for any of them:
|
|
100
|
+
|
|
101
|
+
| You supply | To |
|
|
102
|
+
|---|---|
|
|
103
|
+
| The nav array, already filtered by privilege or licence | `BentoNavRail`, `BentoCommandPalette` |
|
|
104
|
+
| Routes for account, sign-out and any tenancy surfaces | `BentoUserMenu` |
|
|
105
|
+
| A resolved layout and callbacks to persist it | `BentoListPage` |
|
|
106
|
+
| A keyword suggester, if you want one | `IconPickerDialog` |
|
|
107
|
+
|
|
108
|
+
`BentoNavItem` is the render contract, deliberately narrower than what you will
|
|
109
|
+
keep: carry `privilege`, licence flags and anything else on your own type, and
|
|
110
|
+
pass the filtered result.
|
|
111
|
+
|
|
112
|
+
An entry appears when its route does. That is how you say a reader may see
|
|
113
|
+
something — the package cannot read your feature model, and should not try.
|
|
114
|
+
|
|
115
|
+
## 4. Colour — a tone is a token
|
|
116
|
+
|
|
117
|
+
No component in the layer names a colour. A tone is
|
|
118
|
+
`--vg-bento-tone-<name>-from` / `-to` in the preset, and the chip reads them, so
|
|
119
|
+
you re-key the palette by redefining variables rather than forking a component.
|
|
120
|
+
The status intents (`on`, `warn`, `error`, `danger`) work the same way.
|
|
121
|
+
|
|
122
|
+
Accents — a focus border, the rail's active marker, a hover ring — take your
|
|
123
|
+
`--primary`. A shared component marking "you are here" in a colour of its own is
|
|
124
|
+
the clearest way to make one product look wrong.
|
|
125
|
+
|
|
126
|
+
**Claim `--primary`, or four surfaces stay neutral.** The rail's active marker is
|
|
127
|
+
not the only thing reading it: `.bento-tile:hover` takes its glow from
|
|
128
|
+
`--primary`, `.bento-editing` and `.bento-new-tile` take their borders, and the
|
|
129
|
+
default `Button` variant is `bg-primary text-primary-foreground`. The preset's
|
|
130
|
+
value is a neutral rather than a brand, so keying only the accent leaves all four
|
|
131
|
+
near-black on light and near-white on dark — which is what "you are here" looks
|
|
132
|
+
like in a product that thinks it has re-keyed.
|
|
133
|
+
|
|
134
|
+
Claim it at `:root`, through the inputs, the way the accent is claimed:
|
|
135
|
+
|
|
136
|
+
```css
|
|
137
|
+
:root {
|
|
138
|
+
--vg-primary-base: …; /* the mark, on light */
|
|
139
|
+
--vg-primary-base-dark: …; /* and on dark */
|
|
140
|
+
--vg-primary-foreground-base: …; /* what the solid fill carries */
|
|
141
|
+
--vg-primary-foreground-base-dark: …;
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**Setting `--vg-primary` itself is the mistake the inputs exist to prevent.** Your
|
|
146
|
+
stylesheet imports this package and then declares its own `:root`, so it lands
|
|
147
|
+
after the preset's dark block at the same specificity and in no layer. One value
|
|
148
|
+
set there wins on *both* grounds, and the dark ground silently gets the light
|
|
149
|
+
value. The inputs are read per ground, so you never write a dark block.
|
|
150
|
+
|
|
151
|
+
The solid fill carries text, so that pair holds 4.5:1 on both grounds (§6). An
|
|
152
|
+
accent stop at full chroma usually does not, so this value is often a deeper step
|
|
153
|
+
than the one the chip is drawn with.
|
|
154
|
+
|
|
155
|
+
If you need a one-off tint, set `--bento-tone-from` / `--bento-tone-to` on a
|
|
156
|
+
subtree instead of touching the tokens.
|
|
157
|
+
|
|
158
|
+
## 5. i18n
|
|
159
|
+
|
|
160
|
+
- Reuse the keys you already have. Do not mint a parallel namespace for a
|
|
161
|
+
surface that already has one: the same entity rendered in two chromes is the
|
|
162
|
+
same entity.
|
|
163
|
+
- Always pass a default in the **object** form: `t("key", { defaultValue: "…" })`.
|
|
164
|
+
The string form is ambiguous and test mocks generally do not honour it.
|
|
165
|
+
- Add every new key to every locale you ship.
|
|
166
|
+
|
|
167
|
+
## 6. Accessibility
|
|
168
|
+
|
|
169
|
+
This is a gate, not advice — the catalogue's stories run under axe on every
|
|
170
|
+
push, and a violation fails the build.
|
|
171
|
+
|
|
172
|
+
- **Icon-only controls** get an `aria-label`. Decorative glyphs get
|
|
173
|
+
`aria-hidden` and convey their meaning through an adjacent `sr-only` span:
|
|
174
|
+
screen readers do not reliably announce `aria-label` on a bare `<svg>`, and
|
|
175
|
+
Testing Library's `getByLabelText` will not match it either.
|
|
176
|
+
- **Keyboard**: inline edit is reachable and committable by keyboard, the
|
|
177
|
+
palette is arrow-key and Enter navigable, and active rows carry
|
|
178
|
+
`aria-current="page"` (rail) or `aria-selected` (palette).
|
|
179
|
+
- **ARIA values are string literals** — `aria-expanded="true"`,
|
|
180
|
+
`aria-selected={active ? "true" : "false"}`. A bare attribute or a
|
|
181
|
+
`{boolean}` expression is a lint failure.
|
|
182
|
+
- **Headings increase by one.** The hero is `h1`, so a section under it is `h2`
|
|
183
|
+
— which is what `BentoFormSection` renders by default. A repeated label, like
|
|
184
|
+
the sticky bar's title, is not a heading at all.
|
|
185
|
+
- **Contrast holds at 4.5:1**, including text on a tinted surface. Tinted pills
|
|
186
|
+
need the darker text token in light mode.
|
|
187
|
+
- **Reduced motion**: every animation in `bento.css` is turned off under
|
|
188
|
+
`@media (prefers-reduced-motion: reduce)`, and a test asserts that every
|
|
189
|
+
animating selector is named in that guard. A new keyframe that is not is a
|
|
190
|
+
failing build, not a review comment.
|
|
191
|
+
|
|
192
|
+
## 7. Responsive
|
|
193
|
+
|
|
194
|
+
- The grid is `grid-cols-2 md:grid-cols-4 lg:grid-cols-6` with `col-span-2`
|
|
195
|
+
tiles and `row-span-2` for a featured one. **Keep spans in multiples of two**
|
|
196
|
+
so tiles reflow cleanly at every breakpoint — this is what keeps two consoles'
|
|
197
|
+
grids aligned.
|
|
198
|
+
- The nav rail is desktop-only. Reserve its gutter with `bento-rail-gutter` on
|
|
199
|
+
whatever wraps the routed page. On mobile, navigation is the header's command
|
|
200
|
+
trigger and the global shortcut; do not add a second always-visible nav that
|
|
201
|
+
eats mobile width.
|
|
202
|
+
- There is **no sidebar provider**, and no context between the shell's pieces.
|
|
203
|
+
The console era needs one because its sidebar collapses, remembers and pushes
|
|
204
|
+
content; the rail is fixed, one width, and hidden below `md`. That is about the
|
|
205
|
+
shell — a page's own aside is §1, and it needs no provider either.
|
|
206
|
+
|
|
207
|
+
## 8. Tests
|
|
208
|
+
|
|
209
|
+
- Every shared component here carries vitest and Testing Library coverage, so a
|
|
210
|
+
regression surfaces once rather than in each console.
|
|
211
|
+
- Router-dependent components render inside a `MemoryRouter`.
|
|
212
|
+
- Assert on the readable `defaultValue` strings rather than on i18n keys, so a
|
|
213
|
+
test reads like the screen does.
|
|
214
|
+
- jsdom implements neither `matchMedia`, `ResizeObserver` nor `scrollIntoView`.
|
|
215
|
+
This package's own setup polyfills all three; a consumer testing these
|
|
216
|
+
components will need the same.
|
|
217
|
+
|
|
218
|
+
## What this does not cover
|
|
219
|
+
|
|
220
|
+
Which components are the shared layer and which stay in a product is
|
|
221
|
+
[docs/BENTO-BOUNDARY.md](BENTO-BOUNDARY.md).
|
|
222
|
+
|
|
223
|
+
Every rule above, drawn rather than stated, is [docs/reference/](reference/) — eight
|
|
224
|
+
artboards, one per decision. Look there when a sentence here is clear and you still cannot
|
|
225
|
+
picture the page it describes.
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# The bento boundary
|
|
2
|
+
|
|
3
|
+
Which of Turing's `components/bento` exports become a layer of this package, and
|
|
4
|
+
which stay in the product. Decided once, before any file moves, because the
|
|
5
|
+
alternative is deciding it twenty-seven times under time pressure and shipping a
|
|
6
|
+
package the second product has to import around.
|
|
7
|
+
|
|
8
|
+
## The rule
|
|
9
|
+
|
|
10
|
+
A component is **shared** when a second console would render the same thing.
|
|
11
|
+
A component is **product** when what makes it useful is one product's data, one
|
|
12
|
+
product's offer, or one product's brand.
|
|
13
|
+
|
|
14
|
+
Two consequences worth stating, because they are where the rule gets bent:
|
|
15
|
+
|
|
16
|
+
- **Generic mechanism plus product data is shared, and the data is a prop.** The
|
|
17
|
+
nav rail is not Turing's because it renders Turing's routes; it is shared, and
|
|
18
|
+
the routes are an argument. This is the package's existing non-goal — no
|
|
19
|
+
product data here — applied to chrome.
|
|
20
|
+
- **Generic mechanism plus product content is product**, when the mechanism on
|
|
21
|
+
its own is thin enough that this package already offers it. A stepped dialog is
|
|
22
|
+
`Dialog` and `Stepper`, both already exported.
|
|
23
|
+
|
|
24
|
+
## Inventory
|
|
25
|
+
|
|
26
|
+
Counted from the barrel rather than from the file listing, because several files
|
|
27
|
+
export more than one component: **28 components, 4 hooks and 15 other bindings**
|
|
28
|
+
(tone maps, layout maths, the nav arrays and their helpers).
|
|
29
|
+
|
|
30
|
+
The roadmap line that opened this work said twenty-three components. That number
|
|
31
|
+
was taken per file, and misses `BentoTileGrid`, `BentoEntityTile` and
|
|
32
|
+
`BentoBackLink`, each of which lives inside a file named after a sibling. The
|
|
33
|
+
split below is 23 shared and 5 product — the twenty-three is a coincidence of
|
|
34
|
+
arithmetic, not the same twenty-three.
|
|
35
|
+
|
|
36
|
+
### Shared — the layer (23 components, 1 hook)
|
|
37
|
+
|
|
38
|
+
| Export | Coupling to lift before it moves |
|
|
39
|
+
|---|---|
|
|
40
|
+
| `BentoTile` | — |
|
|
41
|
+
| `BentoCountTile` | — |
|
|
42
|
+
| `BentoSection` | — |
|
|
43
|
+
| `BentoHero`, `BentoBackLink` | — |
|
|
44
|
+
| `BentoEmptyState` | — |
|
|
45
|
+
| `BentoFormSection` | — |
|
|
46
|
+
| `BentoSaveBar` | — |
|
|
47
|
+
| `BentoScrollSaveBar` | — |
|
|
48
|
+
| `BentoFormHero` | — |
|
|
49
|
+
| `useBentoScrollFade` | — |
|
|
50
|
+
| `BentoStatusMarker` | — |
|
|
51
|
+
| `BentoHeroIconPicker` | — |
|
|
52
|
+
| `BentoInlineEdit` | — |
|
|
53
|
+
| `BentoActionsMenu` | — |
|
|
54
|
+
| `BentoBackToTop` | — |
|
|
55
|
+
| `BentoEntityShell` | — |
|
|
56
|
+
| `BentoTileGrid` | — |
|
|
57
|
+
| `BentoEntityTile` | — |
|
|
58
|
+
| `BentoListPage` | Persists per-surface layout through `@/api/queries/bento-layout.queries`. The mosaic is generic; where the layout is stored is the product's. Lift to props: a resolved layout in, a save callback out. |
|
|
59
|
+
| `BentoNavRail` | Imports `ROUTES`. Takes the nav array as a prop. |
|
|
60
|
+
| `BentoUserMenu` | Imports `ROUTES` and `useFeatures`. Routes and feature flags become props. |
|
|
61
|
+
| `BentoCommandPalette` | Reads the nav config. Takes the same array the rail does — this is what VDS17 separates. |
|
|
62
|
+
| `BentoShortcutsDialog` | — |
|
|
63
|
+
|
|
64
|
+
### Product — stays in Turing (5 components, 1 hook, the nav data)
|
|
65
|
+
|
|
66
|
+
| Export | Why |
|
|
67
|
+
|---|---|
|
|
68
|
+
| `BentoActivationCard` | Turing's commercial offer rendered as a card. |
|
|
69
|
+
| `BentoQuotaBanner`, `useQuotaRefusal` | Quota is the shape of the licence, not of the chrome. |
|
|
70
|
+
| `BentoNoLlmState` | An empty state whose whole content is one product's capability. |
|
|
71
|
+
| `BentoVigletActivationDialog` | Activation flow — the offer again, as a modal. |
|
|
72
|
+
| `BentoFirstRunTour`, `BENTO_TOUR_SEEN_KEY`, `hasSeenBentoTour` | Decided below. |
|
|
73
|
+
| `BENTO_NAV_ITEMS`, `BENTO_NAV_SECTIONS`, `bentoNavTarget`, `bentoSectionByAreaRoute`, `bentoSectionAreaRoute`, `useVisibleBentoNav`, `useVisibleBentoSections` (`bento-nav.config.ts`) | Routes, privileges and entity ids, and the two hooks that filter them by privilege. This is the product data the package must never hold. The *schema* of an entry moves with the rail and the palette; the array does not. The span constants in the same file do not belong to it — see below. |
|
|
74
|
+
|
|
75
|
+
The first four are the ones the roadmap names as commercial, and they are the
|
|
76
|
+
reason this document exists: moving them as chrome would put one product's
|
|
77
|
+
business model into every product's console.
|
|
78
|
+
|
|
79
|
+
### Non-component modules
|
|
80
|
+
|
|
81
|
+
| Module | Verdict |
|
|
82
|
+
|---|---|
|
|
83
|
+
| `bento-tones.ts` (`BENTO_TONE_GRADIENTS`, `BentoTone`) | Shared, but re-keyed as CSS custom properties rather than moved as Tailwind class strings — a shared component that names its own colours makes one of two products look wrong. |
|
|
84
|
+
| `bento-layout.ts` — `BENTO_EMPHASIS_SPAN`, `BENTO_EMPHASIS_NEXT`, `resolveBentoLayout`, `toBentoLayoutEntries` | Shared. Pure functions and maps over a layout response; no product data in them. |
|
|
85
|
+
| `BENTO_SPAN_FEATURED`, `BENTO_SPAN_WIDE`, `BENTO_SPAN_SQUARE` | Shared, and currently in the wrong file — they are grid spans, defined in `bento-nav.config.ts` because the nav array is what sizes tiles with them. Moving the layer means splitting them out to sit beside `bento-layout.ts`; the array that uses them stays behind. |
|
|
86
|
+
| `bento-scroll-fade.ts` | Shared — already listed above as `useBentoScrollFade`. |
|
|
87
|
+
| `bento-nav.config.ts` | Product, as above. |
|
|
88
|
+
| `bento.styles.css` | Shared. The frosted surface and the hover-lift are what make a page read as bento. |
|
|
89
|
+
|
|
90
|
+
## The first-run tour
|
|
91
|
+
|
|
92
|
+
**Product.** It was the one genuinely open question, and the argument runs both
|
|
93
|
+
ways: the overlay, the step index and the seen-flag are generic, while the four
|
|
94
|
+
steps are Turing's.
|
|
95
|
+
|
|
96
|
+
It lands on product because of what is left after the content is removed. The
|
|
97
|
+
mechanism is a `Dialog` holding a step index and a `localStorage` flag — perhaps
|
|
98
|
+
sixty lines, over primitives this package already exports. Against that, every
|
|
99
|
+
distinctive thing in the file is Turing's: the `TurLogo` it renders, the four
|
|
100
|
+
steps, and a first step whose entire subject is turning the product's AI on.
|
|
101
|
+
Extracting a tour engine to serve one product's onboarding is generality bought
|
|
102
|
+
before there is a second buyer. If a second console wants a tour, that is the
|
|
103
|
+
moment the shape of a shared one is actually known.
|
|
104
|
+
|
|
105
|
+
The mechanism is cheap to rebuild and the content is not portable, which is the
|
|
106
|
+
test this rule uses everywhere else.
|
|
107
|
+
|
|
108
|
+
## What this does not decide
|
|
109
|
+
|
|
110
|
+
- **How** a component moves. That is VDS11 and the lines after it.
|
|
111
|
+
- Whether a moved component keeps its props. It does — a move whose diff also
|
|
112
|
+
changes behaviour cannot be reviewed against the pages that depend on it.
|
|
113
|
+
- Where the authoring contract lives. Turing's `CONVENTIONS.md` is addressed to
|
|
114
|
+
Turing; VDS20 rewrites it product-neutral and brings it here.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# The page reference
|
|
2
|
+
|
|
3
|
+
Nine artboards, one per decision that makes two products built from this package stop
|
|
4
|
+
looking like one. They are the composition half of the catalogue: a story proves a component
|
|
5
|
+
in isolation, and these prove the arrangement.
|
|
6
|
+
|
|
7
|
+
Read them beside [BENTO-AUTHORING.md](../BENTO-AUTHORING.md), which states the same rules in
|
|
8
|
+
prose. Where the two disagree, the contract is right and an artboard is stale.
|
|
9
|
+
|
|
10
|
+
## What is here
|
|
11
|
+
|
|
12
|
+
| Page | Artboard | The decision |
|
|
13
|
+
| ---------- | ----------------------- | ------------------------------------------------------------------------- |
|
|
14
|
+
| The shell | `page-anatomy.dc.html` | every region of a page and who owns it, the signed-in user included |
|
|
15
|
+
| The shell | `shell.dc.html` | what the header carries, in what order, and what it is not |
|
|
16
|
+
| The shell | `reading-column.dc.html`| the shell sets the measure once, or every page repeats one and they differ |
|
|
17
|
+
| The shell | `primary.dc.html` | the four surfaces `--primary` reaches, unclaimed beside claimed |
|
|
18
|
+
| The shapes | `list.dc.html` | a list screen, and the discriminator between tiles and rows |
|
|
19
|
+
| The shapes | `detail.dc.html` | a detail screen, and the save-bar morph at both ends of the scroll |
|
|
20
|
+
| The shapes | `form-hero.dc.html` | a form with no entity behind it yet |
|
|
21
|
+
| The shapes | `panel.dc.html` | the frosted box with no heading, and why it has none |
|
|
22
|
+
| The shapes | `grounds.dc.html` | the same components on both grounds, with the ratios |
|
|
23
|
+
|
|
24
|
+
`canvas.json` places them and names the two pages.
|
|
25
|
+
|
|
26
|
+
## Two rules about the content
|
|
27
|
+
|
|
28
|
+
**No product data.** Subjects are role labels — "Entity name", "Eyebrow", "Meta". A
|
|
29
|
+
reference carrying somebody's routes and entity names is a screenshot of their console, and
|
|
30
|
+
the package's non-goals forbid it for the same reason they forbid a nav array.
|
|
31
|
+
|
|
32
|
+
**The values are the resolved tokens**, not an impression of them. Colour, type, radius,
|
|
33
|
+
the rail's width and the header's height come from `src/styles/preset.css`, so an artboard
|
|
34
|
+
is checkable against the preset. The accent shown is the one this package ships, because a
|
|
35
|
+
product's own is a re-key and `primary.dc.html` is where that is drawn.
|
|
36
|
+
|
|
37
|
+
## Editing them
|
|
38
|
+
|
|
39
|
+
Each `.dc.html` is one artboard: a standalone file whose root is a fixed-size element
|
|
40
|
+
matching the `w`/`h` this directory's `canvas.json` declares for it. Keep the two in step —
|
|
41
|
+
a root larger than its frame is clipped, not scaled.
|
|
42
|
+
|
|
43
|
+
These files are also vendored into consuming repositories, where they are refreshed from
|
|
44
|
+
here on every install. So an artboard is corrected in this directory and nowhere else: a
|
|
45
|
+
consumer that edits its copy loses the edit on the next run, the same way a forked component
|
|
46
|
+
loses it on the next upgrade.
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
{
|
|
2
|
+
"pages": [
|
|
3
|
+
{ "id": "vds-shell", "name": "The shell" },
|
|
4
|
+
{ "id": "vds-shapes", "name": "The shapes" }
|
|
5
|
+
],
|
|
6
|
+
"artboards": [
|
|
7
|
+
{
|
|
8
|
+
"file": "page-anatomy.dc.html",
|
|
9
|
+
"x": 0,
|
|
10
|
+
"y": 0,
|
|
11
|
+
"w": 1400,
|
|
12
|
+
"h": 980,
|
|
13
|
+
"title": "The page's regions",
|
|
14
|
+
"page": "vds-shell"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"file": "shell.dc.html",
|
|
18
|
+
"x": 1520,
|
|
19
|
+
"y": 0,
|
|
20
|
+
"w": 1280,
|
|
21
|
+
"h": 760,
|
|
22
|
+
"title": "The shell",
|
|
23
|
+
"page": "vds-shell"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"file": "reading-column.dc.html",
|
|
27
|
+
"x": 2920,
|
|
28
|
+
"y": 0,
|
|
29
|
+
"w": 1280,
|
|
30
|
+
"h": 720,
|
|
31
|
+
"title": "Who owns the reading column",
|
|
32
|
+
"page": "vds-shell"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"file": "primary.dc.html",
|
|
36
|
+
"x": 4320,
|
|
37
|
+
"y": 0,
|
|
38
|
+
"w": 1080,
|
|
39
|
+
"h": 640,
|
|
40
|
+
"title": "What --primary reaches",
|
|
41
|
+
"page": "vds-shell"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"file": "list.dc.html",
|
|
45
|
+
"x": 0,
|
|
46
|
+
"y": 0,
|
|
47
|
+
"w": 1280,
|
|
48
|
+
"h": 720,
|
|
49
|
+
"title": "A list screen",
|
|
50
|
+
"page": "vds-shapes"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"file": "detail.dc.html",
|
|
54
|
+
"x": 1400,
|
|
55
|
+
"y": 0,
|
|
56
|
+
"w": 1280,
|
|
57
|
+
"h": 760,
|
|
58
|
+
"title": "A detail screen",
|
|
59
|
+
"page": "vds-shapes"
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"file": "form-hero.dc.html",
|
|
63
|
+
"x": 2800,
|
|
64
|
+
"y": 0,
|
|
65
|
+
"w": 1080,
|
|
66
|
+
"h": 680,
|
|
67
|
+
"title": "A form with its own hero",
|
|
68
|
+
"page": "vds-shapes"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"file": "panel.dc.html",
|
|
72
|
+
"x": 0,
|
|
73
|
+
"y": 900,
|
|
74
|
+
"w": 1080,
|
|
75
|
+
"h": 680,
|
|
76
|
+
"title": "The panel",
|
|
77
|
+
"page": "vds-shapes"
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"file": "grounds.dc.html",
|
|
81
|
+
"x": 1200,
|
|
82
|
+
"y": 900,
|
|
83
|
+
"w": 1180,
|
|
84
|
+
"h": 700,
|
|
85
|
+
"title": "Both grounds",
|
|
86
|
+
"page": "vds-shapes"
|
|
87
|
+
}
|
|
88
|
+
],
|
|
89
|
+
"annotations": [
|
|
90
|
+
{
|
|
91
|
+
"id": "vds-why-shell",
|
|
92
|
+
"page": "vds-shell",
|
|
93
|
+
"x": 0,
|
|
94
|
+
"y": -190,
|
|
95
|
+
"w": 700,
|
|
96
|
+
"text": "Three decisions live on this page, and none of them is a component defect -- which is why no component change closes them.\nThe contract says adopt the shell before the pages. These artboards say what the shell is: what the header carries and in what order, who owns the reading column, and which surfaces --primary reaches.\nThe content is role-labelled on purpose. Routes and entity names belong to a product, never to this package."
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"id": "vds-why-shapes",
|
|
100
|
+
"page": "vds-shapes",
|
|
101
|
+
"x": 0,
|
|
102
|
+
"y": -190,
|
|
103
|
+
"w": 700,
|
|
104
|
+
"text": "Almost every screen is one of three shapes plus data: a detail is BentoEntityShell wrapping a form, a list is one BentoListPage call with a renderTile, a form with no entity behind it yet is BentoFormHero as the first child of its form.\nThe panel is the fourth thing and not a shape: a frosted box with no heading, for whatever a hero already named.\nThe last artboard is the same components on both grounds, because one look means one look twice."
|
|
105
|
+
}
|
|
106
|
+
],
|
|
107
|
+
"launch": { "view": "canvas", "page": "vds-shell" }
|
|
108
|
+
}
|