@viglet/viglet-design-system 2026.3.4 → 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.
@@ -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 six consumers take it: Turing, Shio, Dumont and the Cloud Home.
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;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,MAAM,CAsEzE"}
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
+ }