@tenphi/cookbook 0.15.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +25 -0
  2. package/dist/config.d.ts +2 -2
  3. package/dist/index.d.ts +2 -2
  4. package/docs/ai-agents.md +61 -0
  5. package/docs/architecture.md +91 -0
  6. package/docs/authoring.mdx +86 -0
  7. package/docs/cli.md +88 -0
  8. package/docs/configuration.md +587 -0
  9. package/docs/content-sources.md +178 -0
  10. package/docs/customization-rules.md +154 -0
  11. package/docs/deployment.md +95 -0
  12. package/docs/examples.md +101 -0
  13. package/docs/getting-started.md +134 -0
  14. package/docs/index.md +74 -0
  15. package/docs/migration.md +36 -0
  16. package/docs/starlight-comparison.md +124 -0
  17. package/docs/theme-and-components.md +745 -0
  18. package/docs/upstream/glaze/LICENSE +21 -0
  19. package/docs/upstream/glaze/README.md +156 -0
  20. package/docs/upstream/glaze/docs/api.md +2021 -0
  21. package/docs/upstream/glaze/docs/methodology.md +686 -0
  22. package/docs/upstream/glaze/docs/migration.md +351 -0
  23. package/docs/upstream/glaze/docs/okhst.md +207 -0
  24. package/docs/upstream/manifest.json +18 -0
  25. package/docs/upstream/tasty/LICENSE +21 -0
  26. package/docs/upstream/tasty/README.md +685 -0
  27. package/docs/upstream/tasty/docs/README.md +39 -0
  28. package/docs/upstream/tasty/docs/adoption.md +323 -0
  29. package/docs/upstream/tasty/docs/ai-agents.md +229 -0
  30. package/docs/upstream/tasty/docs/comparison.md +448 -0
  31. package/docs/upstream/tasty/docs/configuration.md +823 -0
  32. package/docs/upstream/tasty/docs/debug.md +335 -0
  33. package/docs/upstream/tasty/docs/design-system.md +455 -0
  34. package/docs/upstream/tasty/docs/dsl.md +1021 -0
  35. package/docs/upstream/tasty/docs/getting-started.md +238 -0
  36. package/docs/upstream/tasty/docs/injector.md +634 -0
  37. package/docs/upstream/tasty/docs/methodology.md +648 -0
  38. package/docs/upstream/tasty/docs/migration-v3.md +285 -0
  39. package/docs/upstream/tasty/docs/pipeline.md +749 -0
  40. package/docs/upstream/tasty/docs/plugins.md +353 -0
  41. package/docs/upstream/tasty/docs/react-api.md +681 -0
  42. package/docs/upstream/tasty/docs/runtime-benchmarks.md +351 -0
  43. package/docs/upstream/tasty/docs/ssr.md +610 -0
  44. package/docs/upstream/tasty/docs/styles.md +677 -0
  45. package/docs/upstream/tasty/docs/tasty-static.md +582 -0
  46. package/package.json +12 -7
@@ -0,0 +1,745 @@
1
+ ---
2
+ title: Theme and components
3
+ description: Build an accessible Glaze palette, customize Tasty tokens, and use the supported Astro components.
4
+ sidebar:
5
+ order: 5
6
+ ---
7
+ Cookbook uses Glaze to derive light, dark, and high-contrast values from one
8
+ brand input, then exposes the result through Tasty tokens and component
9
+ anatomy.
10
+
11
+ Start with the [customization rules](./customization-rules.md) for the consumer
12
+ contract, local package references, and guidance for coding agents.
13
+
14
+ Use the [Glaze documentation](./upstream/glaze/README.md) to learn how palette
15
+ inputs, color modes, and contrast targets are resolved. These Tasty references
16
+ cover the configuration language accepted by `theme.styles`:
17
+
18
+ * [Style DSL](./upstream/tasty/docs/dsl.md) for state maps, tokens, units,
19
+ conditional selectors, and merge behavior.
20
+ * [Style properties](./upstream/tasty/docs/styles.md) for every enhanced CSS
21
+ property and its accepted syntax.
22
+ * [Configuration](./upstream/tasty/docs/configuration.md) for shared states,
23
+ tokens, units, recipes, and typography presets.
24
+ * [Sub-element styling](./upstream/tasty/docs/react-api.md#sub-element-styling)
25
+ for the component anatomy model used by Cookbook's named style trees.
26
+ * [Tasty methodology](./upstream/tasty/docs/methodology.md) for the design-system
27
+ patterns behind roots, sub-elements, and controlled overrides.
28
+
29
+ ## Brand color
30
+
31
+ The default brand is a calm blue with 68% OKHSL saturation. Set a brand color
32
+ with Glaze's `from` declaration:
33
+
34
+ ```ts
35
+ theme: {
36
+ brand: {
37
+ from: "#2f5bff";
38
+ }
39
+ }
40
+ ```
41
+
42
+ Add a contrast target when the brand appears as text or focus color:
43
+
44
+ ```ts
45
+ theme: {
46
+ brand: {
47
+ from: "#2f5bff",
48
+ contrast: { apca: [45, 60] }
49
+ }
50
+ }
51
+ ```
52
+
53
+ The authored color remains exact when it already satisfies the required
54
+ contrast against the page surface. Otherwise Glaze moves it only as far as the
55
+ floor requires. Dark and high-contrast schemes resolve independently.
56
+
57
+ Cookbook rejects a normal APCA target below 45 unless
58
+ `unsafeContrast: true` is present. That escape hatch is intentionally visible
59
+ in configuration reviews. Literal color shorthand and structured
60
+ `hue`/`saturation`/`tone` input also remain supported for the brand.
61
+
62
+ ## Semantic palette
63
+
64
+ `brand` controls accent text, fills, and focus. The optional palette roles
65
+ control the reading surface, text, and callout colors. Declare color
66
+ relationships with Glaze's `tone`, `base`, and `contrast` properties:
67
+
68
+ ```ts
69
+ theme: {
70
+ brand: { from: "#2f5bff" },
71
+ palette: {
72
+ surface: { tone: 98, saturation: 0.05 },
73
+ text: {
74
+ base: "surface",
75
+ tone: "-10",
76
+ saturation: 0,
77
+ contrast: { wcag: [7, 10] }
78
+ },
79
+ textSoft: {
80
+ base: "surface",
81
+ tone: "-10",
82
+ saturation: 0.05,
83
+ contrast: { wcag: [4.5, 7] }
84
+ }
85
+ },
86
+ contrastLevel: "auto"
87
+ }
88
+ ```
89
+
90
+ Glaze resolves each declaration for light, dark, normal, and high-contrast
91
+ modes. The first contrast value applies to normal mode; the second applies to
92
+ high contrast. Palette declarations inherit the brand hue and saturation:
93
+ `saturation` is a 0–1 factor of that seed, and `tone` is 0–100. They can set
94
+ an absolute tone or a tone relative to `base`. Use `from` on a palette role
95
+ when it needs its own color seed; otherwise relative declarations keep its
96
+ relationship to the brand and surrounding surface as the scheme or contrast
97
+ changes. `info`, `success`, `warning`, and `danger` also accept Glaze
98
+ declarations and expose border, `-text`, and `-surface` semantic tokens.
99
+ Cookbook keeps literal surface seeds desaturated in the dark scheme so a nearly
100
+ white tint does not become vivid dark chrome when its tone is inverted.
101
+
102
+ Components consume semantic colors consistently: `surface`, `header`, `surface-2`,
103
+ `surface-3`, `text`, `text-soft`, `border`, `border-strong`, `accent-text`,
104
+ `accent-surface`, `accent-surface-text`, and `focus`. Tasty components can use
105
+ these as `#surface`, `#text`, `#border`, and so on; the Astro shell consumes the
106
+ same resolved values. Glaze also generates hover and pressed states, subtle
107
+ accent fills, overlays, shadows, and the orange, green, blue, purple, and red
108
+ roles used by Starlight content components. No browser color mixes or
109
+ Starlight fallback palette values participate in the rendered theme. Surface
110
+ elevation uses Glaze's contrast-uniform tone axis: `surface-2` advances two tone
111
+ steps and `surface-3` advances four from the base surface, with proportionally
112
+ wider steps in high-contrast mode. Their saturation also steps down to 75% and
113
+ 65% of the authored surface seed so tinted surfaces remain restrained as they
114
+ move farther from the base tone.
115
+
116
+ Borders stay intentionally quiet: Glaze derives their hue from `brand` but
117
+ uses only one quarter of the brand saturation. Normal and high-contrast modes
118
+ change border tone, not that restrained saturation relationship.
119
+
120
+ The Appearance button in the desktop and mobile header opens one panel with
121
+ Color scheme and Contrast sections. Color scheme offers Light, Dark, and Auto;
122
+ Contrast offers Normal, High, and Auto. Each preference is independent, and
123
+ existing saved choices are preserved. The panel supports keyboard navigation,
124
+ Escape, and outside-click dismissal. The popover fades and scales vertically
125
+ from 96% to 100% over 120ms using native entry and exit transitions. Reduced
126
+ motion disables the transition. Customize these states with
127
+ `theme.styles.ThemeSelect.Panel` and `OpenPanel`; the root `$popover-transition`
128
+ token controls the duration. Contrast can follow the system, force the
129
+ normal palette, or activate the Glaze high-contrast palette.
130
+ System mode responds to `prefers-contrast: more`; an explicit selection is
131
+ persisted and takes precedence over that media query.
132
+
133
+ Interactive controls step up exactly one surface level: a control on `surface`
134
+ uses `surface-2`, while a control on `surface-2` uses `surface-3`. Hover and
135
+ pressed states build on that elevated surface without changing the border.
136
+ Inputs stay on their surrounding surface so their border remains the visual
137
+ boundary. Selected navigation uses the fixed-mode `accent-surface` and its
138
+ paired `accent-surface-text`, so the brand fill does not drift toward the
139
+ adaptive link color in dark mode.
140
+
141
+ ## Design tokens
142
+
143
+ Token names follow Tasty's
144
+ [token and unit syntax](./upstream/tasty/docs/dsl.md#built-in-units): `$name`
145
+ becomes the CSS custom property `--name`. Existing `--name` keys are still
146
+ accepted for compatibility.
147
+
148
+ ```ts
149
+ theme: {
150
+ tokens: {
151
+ "$gap": "0.5rem",
152
+ "$radius": "8px",
153
+ "$card-radius": "16px",
154
+ "$border-width": "1px",
155
+ "$outline-width": "2px",
156
+ "$outline-offset": "2px",
157
+ "$control-height": "2.5rem",
158
+ "$layout-width": "87.5rem",
159
+ "$content-width": "58rem",
160
+ "$sidebar-width": "17.5rem"
161
+ }
162
+ }
163
+ ```
164
+
165
+ `layout-width` caps and centers the complete documentation shell, while
166
+ `content-width` limits the reading column inside it. `radius` is the control
167
+ and navigation radius; `card-radius` is the larger surface radius. Keeping
168
+ those roles separate makes a sharp control theme or a soft card theme possible
169
+ without one-off component overrides.
170
+
171
+ ## Typography presets
172
+
173
+ The built-in [typography presets](./upstream/tasty/docs/styles.md#preset) are
174
+ `body`, `heading`, `h1` through `h6`, `navigation`, `small`, and `code`. Onest
175
+ is self-hosted and used for body and heading text by default; JetBrains Mono is
176
+ self-hosted for code.
177
+
178
+ ### Change font families
179
+
180
+ Set `theme.fonts` to load fonts and apply them to the matching presets. A Google
181
+ Fonts family needs only its name:
182
+
183
+ ```ts
184
+ theme: {
185
+ fonts: {
186
+ body: "Inter",
187
+ heading: "Newsreader"
188
+ }
189
+ }
190
+ ```
191
+
192
+ The short form requests the family's default style. To use specific weights,
193
+ list them explicitly. Google Fonts must support the requested weights; see the
194
+ [Google Fonts CSS API](https://developers.google.com/fonts/docs/css2) for its
195
+ available styles and weight syntax.
196
+
197
+ ```ts
198
+ theme: {
199
+ fonts: {
200
+ body: { google: "Inter", weights: [400, 600, 700] }
201
+ }
202
+ }
203
+ ```
204
+
205
+ For a font you own, put its files in `public/fonts/` and give Cookbook their
206
+ public paths. Cookbook checks that each file exists during the build and adjusts
207
+ its URL for the Astro `base` path. A variable font can cover a weight range;
208
+ use separate entries for regular and italic files when needed:
209
+
210
+ ```ts
211
+ theme: {
212
+ fonts: {
213
+ body: {
214
+ family: "Acme Sans",
215
+ files: [
216
+ { src: "/fonts/acme-sans.woff2", weight: "100 900" },
217
+ { src: "/fonts/acme-sans-italic.woff2", weight: "100 900", style: "italic" }
218
+ ]
219
+ }
220
+ }
221
+ }
222
+ ```
223
+
224
+ The `body` role also feeds navigation and small text. `heading` feeds `h1`
225
+ through `h6`, while `code` covers code text. Each role has a system fallback.
226
+ Cookbook emits the font faces through Tasty. Google font names are resolved at
227
+ build time, so those builds need network access; the generated site has no
228
+ Google CSS stylesheet. Font files are requested from Google's font CDN when
229
+ visitors open the site. Use local files if you need a self-hosted site.
230
+
231
+ ### Adjust presets
232
+
233
+ Presets merge property-by-property, so changing size, weight, or line height
234
+ does not require copying the rest of the preset. `theme.presets` takes precedence
235
+ over `theme.fonts` for an explicitly set `fontFamily`:
236
+
237
+ ```ts
238
+ theme: {
239
+ presets: {
240
+ body: {
241
+ boldFontWeight: 680
242
+ },
243
+ heading: {
244
+ fontWeight: 650,
245
+ boldFontWeight: 740
246
+ },
247
+ h1: {
248
+ fontSize: "3rem",
249
+ letterSpacing: "-0.02em"
250
+ },
251
+ code: { fontSize: "0.9rem" }
252
+ }
253
+ }
254
+ ```
255
+
256
+ For a system or already loaded font, set its CSS family directly with
257
+ `theme.presets.<role>.fontFamily`. This does not load a font file. Additional
258
+ named presets are available to custom MDX components.
259
+
260
+ Heading presets reference the `heading` family and weights; body, navigation,
261
+ and controls reference `body`. Semantic `strong` and `b` elements use Tasty's
262
+ `strong` modifier, so their weight comes from the active preset's
263
+ `boldFontWeight` instead of a separate element-specific value. `strong` is a
264
+ reserved modifier keyword, not a named preset; the modifier-only form is
265
+ equivalent to `inherit / strong`. Body text uses a regular `400` weight and
266
+ neutral tracking, while
267
+ headings use a medium `610` weight and progressively gentle negative tracking.
268
+ The separate `720` heading bold weight keeps emphasized heading text distinct.
269
+ Navigation uses a lighter `450` weight with `580` for the current sidebar page,
270
+ while smaller group labels establish hierarchy without oversized bold text.
271
+ Top-level sidebar groups are always-visible section headings with their direct
272
+ links aligned beneath them. Deeper groups expand on click or keyboard activation;
273
+ the current group and its ancestors start open. Groups can have a parent-page
274
+ `link`; activating an unselected linked header selects that page and keeps its
275
+ children open. Activating the selected header toggles its children.
276
+ The current group can stay collapsed because its header remains visible. Only
277
+ the current page is highlighted; ancestors receive no extra emphasis. Explicit expansion choices are
278
+ remembered for the browser tab; automatically revealing the current page does
279
+ not overwrite them. Desktop scroll position is restored independently of mobile
280
+ menu scrolling, and the current page's ancestors remain open after navigation.
281
+ Nested groups use indentation without vertical rails. Customize section headings
282
+ with `theme.styles.Sidebar.SectionHeading`, indentation with `NestedItem`, and
283
+ disclosure icons with `Caret` and `ExpandedCaret`.
284
+ Adjacent sidebar items have a `1bw` gap. Inline code scales to `0.875em` of its
285
+ surrounding text, including smaller table text; code blocks keep the `code` preset
286
+ size. Customize inline code through `theme.styles.MarkdownInlineCode`.
287
+ Cookbook applies each semantic typography role through its complete Tasty
288
+ `preset`, so configured fields such as `fontStyle` and `textTransform` are not
289
+ silently omitted.
290
+
291
+ The exported `DEFAULT_THEME_TOKENS` and `DEFAULT_TYPOGRAPHY_PRESETS` constants
292
+ are useful when building a theme editor or presenting a reset action.
293
+
294
+ ## Style customization
295
+
296
+ Cookbook-owned interface elements are direct `tasty()` components. Supported
297
+ Starlight-rendered surfaces use the same Tasty style trees through the global
298
+ bridge. Customize either kind by name under `theme.styles`; the configuration
299
+ is resolved before CSS generation, so this is not a selector-based CSS
300
+ override.
301
+
302
+ Provide only the root and named
303
+ [sub-element](./upstream/tasty/docs/dsl.md#sub-element) properties you want to
304
+ override. Cookbook deep-merges that partial style object into the complete
305
+ base style object inside the renderer, following Tasty's
306
+ [state-map merge semantics](./upstream/tasty/docs/dsl.md#extending-vs-replacing-state-maps):
307
+
308
+ ```ts
309
+ theme: {
310
+ styles: {
311
+ ThemeSelect: {
312
+ Trigger: {
313
+ border: "#border-strong"
314
+ },
315
+ Panel: {
316
+ padding: "1x",
317
+ shadow: "0 1rem 3rem #shadow"
318
+ }
319
+ },
320
+ TopNavigation: {
321
+ Link: { preset: "body" },
322
+ CurrentLink: { color: "#accent-text" }
323
+ },
324
+ Sidebar: {
325
+ LinkLabel: { whiteSpace: "normal" }
326
+ },
327
+ TableOfContents: {
328
+ LinkLabel: { whiteSpace: "normal" }
329
+ },
330
+ MobileMenuToggle: {
331
+ Page: { color: "#accent-text" }
332
+ }
333
+ }
334
+ }
335
+ ```
336
+
337
+ No merge helper or base style object is required in user configuration. Style
338
+ customization has one behavior: every supplied object is a partial override and
339
+ is merged into the base inside Cookbook.
340
+
341
+ Every configurable surface accepts styles at the root plus these named Tasty
342
+ sub-elements:
343
+
344
+ | Configuration name | Named sub-elements |
345
+ | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
346
+ | `Card` | `Heading2`, `Heading3`, `Paragraph` |
347
+ | `Callout` | `Title`, `Body`, `Tip`, `Caution`, `Danger` |
348
+ | `CodeGroup` | `Caption`, `Pre`, `Code` |
349
+ | `Tab` | `Heading`, `Hidden`, `HiddenHeading` |
350
+ | `Footer` | `Meta`, `LoneMetaItem`, `MetaLink`, `HoverMetaLink`, `Credit`, `CreditLink`, `HoverCreditLink` |
351
+ | `Hero` | `Visual`, `Stack`, `Copy`, `Title`, `Tagline`, `Actions`, `Action`, `HoverAction`, `PrimaryAction`, `SecondaryAction`, `MinimalAction`, `ActionIcon` |
352
+ | `LanguageSelect` | `Label`, `HoverLabel`, `LabelIcon`, `Select`, `Caret`, `Option` |
353
+ | `Logo` | `Svg`, `Mark` |
354
+ | `MarkdownCodeBlock` | `Pre`, `CopyButton`, `HoverCopyButton`, `CopiedButton`, `CopyIcon`, `CopiedIcon` |
355
+ | `MarkdownHeading` | `Heading`, `Heading1`, `Heading2`, `Heading3`, `Heading4`, `Heading5`, `Heading6`, `Link`, `RevealedLink`, `HoverLink`, `LinkIcon`, `CopiedLink`, `CopiedLinkIcon`, `CopiedIcon` |
356
+ | `MarkdownInlineCode` | None |
357
+ | `MarkdownTable` | `Table`, `Cell`, `LastBodyRowCell`, `HeaderCell` |
358
+ | `Mermaid` | `Diagram`, `Text`, `MonoText` |
359
+ | `MobileMenuFooter` | `Social` |
360
+ | `MobileMenuToggle` | `Control`, `Icon`, `Section`, `Page`, `HoverControl`, `ActiveControl` |
361
+ | `MobileNavigationTabs` | `Trigger`, `Marker`, `Caret`, `ExpandedCaret`, `Label`, `List`, `Item`, `Link`, `HoverLink`, `CurrentLink` |
362
+ | `MobileTableOfContents` | `Item`, `Link`, `LinkLabel`, `HoverLink`, `CurrentLink`, `CurrentIndicator` |
363
+ | `PackageVersion` | None |
364
+ | `VersionSwitcher` | `Trigger`, `Panel`, `OpenPanel`, `Link`, `CurrentLink` |
365
+ | `Preview` | `Caption`, `Stage`, `Frame`, `Code`, `Summary`, `Pre` |
366
+ | `Sidebar` | `Backdrop`, `OpenBackdrop`, `MobileHeading`, `HomeLink`, `HomeLogo`, `HomeLabel`, `Close`, `HoverClose`, `CloseIcon`, `CurrentLink`, `OpenPane`, `Content`, `Tree`, `List`, `Item`, `TopLevelSpacing`, `GroupSpacing`, `NestedItem`, `SectionHeading`, `Control`, `Summary`, `GroupLabel`, `GroupLabelText`, `Link`, `LinkLabel`, `InteractiveControl`, `SummaryMarker`, `Caret`, `ExpandedCaret`, `LinkedSummary`, `GroupLink`, `LinkedSectionHeading`, `SectionLink`, `Badge`, `TopLevelLink` |
367
+ | `SocialIcons` | `Link`, `HoverLink`, `Icon` |
368
+ | `StarlightHeader` | `Primary`, `TitleAndSearch`, `Title`, `LogoLink`, `Logo`, `SiteTitle`, `Search`, `SearchElement`, `Tools`, `ToolItem`, `Social`, `MobileTheme` |
369
+ | `TableOfContentsLayout` | `Content` |
370
+ | `SearchButton` | `Label`, `Shortcut`, `Hover`, `Active`, `NativeIcon`, `Icon` |
371
+ | `Layout` | `LockedPage` |
372
+ | `HeaderLinks` | `Desktop`, `Link`, `HoverLink`, `PrimaryLink`, `HoverPrimaryLink`, `Trigger`, `HoverTrigger`, `Panel`, `PanelNavigation`, `PanelLink`, `FirstPanelLink`, `Close`, `HoverClose` |
373
+ | `HeaderFrame` | None |
374
+ | `PageFrame` | `MainFrame`, `SidebarFrame`, `Columns` |
375
+ | `Steps` | `Item`, `Marker` |
376
+ | `Tabs` | `List`, `Button`, `SelectedButton`, `FocusedButton` |
377
+ | `TableOfContents` | `Heading`, `List`, `Item`, `Link`, `LinkLabel`, `HoverLink`, `CurrentLink` |
378
+ | `ThemeSelect` | `Trigger`, `HoverTrigger`, `ActiveTrigger`, `Icon`, `Panel`, `OpenPanel`, `Section`, `SectionSpacing`, `SectionLabel`, `Option`, `HoverOption`, `CheckedOption`, `FocusedOption`, `Input`, `OptionIcon`, `Checkmark`, `SelectedCheckmark` |
379
+ | `TopNavigation` | `Scrollbar`, `Link`, `HoverLink`, `CurrentLink`, `ActiveIndicator` |
380
+
381
+ `COOKBOOK_COMPONENT_NAMES` publishes the configuration names, and
382
+ `COOKBOOK_COMPONENT_SUB_ELEMENTS` publishes the complete sub-element lists for
383
+ theme editors and other tooling. The corresponding TypeScript types are
384
+ `CookbookComponentName`, `CookbookComponentSubElementName`,
385
+ `CookbookComponentStyles`, `ComponentStyleConfig`, and `ComponentStylesConfig`.
386
+ Known configuration names provide editor suggestions for their named
387
+ sub-elements. `ThemeSelect` styles the combined Appearance panel; replace old
388
+ `Select` and `Picker` overrides with `Trigger` and `Panel`. The separate
389
+ `ContrastSelect` surface is replaced by the same `ThemeSelect` panel and option
390
+ sub-elements. `SocialIcons` styles the shared icon-button links in the header
391
+ and mobile menu. On wider layouts, heading permalinks sit to the left of the
392
+ heading and are revealed on heading hover or keyboard focus. On mobile they
393
+ stay visible and inline so the target is not clipped by the narrower content
394
+ gutter. Customize the behavior with `MarkdownHeading.Link`,
395
+ `MarkdownHeading.RevealedLink`, and `MarkdownHeading.LinkIcon`. Activating a
396
+ heading link copies the full page URL with that section’s fragment, without
397
+ scrolling or changing the current URL. A checkmark and screen-reader status
398
+ confirm the copy; clipboard failures are announced. Modified clicks and links
399
+ without JavaScript retain native anchor behavior. Customize the confirmation
400
+ with `MarkdownHeading.CopiedLink`, `MarkdownHeading.CopiedLinkIcon` (the hidden
401
+ original icon), and `MarkdownHeading.CopiedIcon` (the checkmark).
402
+
403
+ Root properties customize each navigation container. For example, overriding
404
+ `Sidebar.LinkLabel.whiteSpace` keeps the other base `LinkLabel` properties
405
+ because the configured object is merged by default.
406
+ By default, the footer credit uses the primary body text color, while its
407
+ Cookbook link uses the semantic brand link color.
408
+
409
+ `components.overrides` remains the structural escape hatch for replacing an
410
+ Astro component. Prefer `theme.styles` when the markup and behavior remain the
411
+ same.
412
+
413
+ Register custom names and their partial Tasty objects in `theme.customStyles`.
414
+ Built-in names belong in `theme.styles` and reject misspelled sub-elements.
415
+ Custom names can also target a matching user-authored `data-tasty-anatomy` attribute.
416
+
417
+ The default renderer runs Tasty in Astro extract mode. Direct components and
418
+ the remaining document/vendor bridge styles are collected into shared static
419
+ CSS during the build, while appearance controls add only the small client
420
+ behavior needed to persist selected theme and contrast modes. The compact
421
+ Cookbook logo is shown beside the project title in the default top bar, links
422
+ to the localized home page, and is also the default generated favicon. Use
423
+ [`site.favicon`](./configuration.md#site-icons) to generate the complete icon
424
+ set from project artwork.
425
+
426
+ ## Diff snippets
427
+
428
+ Use a `diff` code fence to highlight changed lines. Insertions and deletions
429
+ receive subtle theme-aware backgrounds while their `+` and `-` markers remain
430
+ visible for readers who do not distinguish the colors.
431
+
432
+ ```diff
433
+ -formatOkhsl(v.h, v.s * 100, l * 100);
434
+ +formatOkhsl(v.h, v.s, l);
435
+
436
+ -formatOkhst(v.h, v.s * 100, v.t * 100);
437
+ +formatOkhst(v.h, v.s, v.t);
438
+ ```
439
+
440
+ ## Astro and MDX components
441
+
442
+ The facade exposes supported components from `@tenphi/cookbook/components`:
443
+
444
+ ```mdx
445
+ import { Card, Logo, Preview, Steps, Tabs } from "@tenphi/cookbook/components";
446
+
447
+ <Card title="Package-first" href="/getting-started/">
448
+ Generate a site from a locked npm artifact.
449
+ </Card>
450
+ ```
451
+
452
+ * `Card` renders a titled article or link.
453
+ * `Logo` renders the Cookbook mark using the active brand color.
454
+ * `Steps` provides an ordered steps container.
455
+ * `Tabs` and `Tab` provide accessible tabbed panels.
456
+ * `Callout` renders a note, tip, caution, or danger block.
457
+ * `CodeGroup` renders labeled source strings in tabs.
458
+
459
+ See [Authoring components](./authoring.mdx) for complete examples and props.
460
+
461
+ * `Preview` isolates HTML and CSS with declarative Shadow DOM, or JavaScript
462
+ in a sandboxed iframe.
463
+
464
+ Use MDX only for repository content you control. Locked package content remains
465
+ Markdown-only unless the consumer explicitly sets `trust: "mdx"` for that
466
+ source.
467
+
468
+ ## Authoring custom components
469
+
470
+ Import styling tools from `@tenphi/cookbook/styling`. They use the same Tasty
471
+ runtime as Cookbook, with its semantic colors, typography presets, units, and
472
+ responsive states. The renderer package also exposes them from
473
+ `@tenphi/starlight/styling`.
474
+
475
+ | Export | Purpose |
476
+ | ------------------------ | ---------------------------------------------------------------------------------- |
477
+ | `defineComponent` | Create a named Tasty component with its `theme.customStyles[name]` overrides. |
478
+ | `tasty` | Use Tasty directly without binding a component to `theme.customStyles`. |
479
+ | `useGlobalStyles` | Collect a global Tasty style tree while rendering a page. |
480
+ | `resolveComponentStyles` | Merge `theme.customStyles[name]` into a complete base style tree for global rules. |
481
+ | `mergeStyles` | Compose your own base styles with Tasty's deep-merge semantics. |
482
+ | `Styles` | TypeScript type for a Tasty style object. |
483
+
484
+ Cookbook supplies the React renderer and extracts these styles into its static
485
+ CSS. No additional Tasty dependency, Astro integration, or `client:*` directive
486
+ is needed for static components. Set shared tokens, presets, and states in
487
+ `docs.config.ts` before rendering. Import the styling entry point in component
488
+ modules; configuration files should use the main `@tenphi/cookbook` entry point.
489
+
490
+ ### A custom logo and site title
491
+
492
+ To put your own logo and title in one home link, create
493
+ `docs/components/site-title.ts`:
494
+
495
+ ```ts
496
+ import { defineComponent } from "@tenphi/cookbook/styling";
497
+
498
+ export const SiteTitleRoot = defineComponent("ProjectSiteTitle", {
499
+ as: "a",
500
+ styles: {
501
+ display: "flex",
502
+ alignItems: "center",
503
+ gap: "1x",
504
+ inlineSize: "min 0",
505
+ color: "#text",
506
+ preset: { "": "h4 / strong", "@mobile": "h5 / strong" },
507
+ textDecoration: "none",
508
+ Logo: {
509
+ $: "> svg",
510
+ display: "block",
511
+ flexShrink: "0",
512
+ inlineSize: { "": "2rem", "@mobile": "1.75rem" },
513
+ blockSize: { "": "2rem", "@mobile": "1.75rem" },
514
+ color: "#accent-text",
515
+ },
516
+ Label: {
517
+ $: "> span",
518
+ inlineSize: "min 0",
519
+ overflow: "hidden",
520
+ textOverflow: "ellipsis",
521
+ whiteSpace: "nowrap",
522
+ },
523
+ },
524
+ });
525
+ ```
526
+
527
+ Then create `docs/components/SiteTitle.astro`, using an SVG whose paths use
528
+ `currentColor` to follow the semantic accent color:
529
+
530
+ ```astro
531
+ ---
532
+ import ProjectLogo from "../../public/logo.svg";
533
+ import { SiteTitleRoot } from "./site-title.js";
534
+
535
+ const { siteTitle, siteTitleHref } = Astro.locals.starlightRoute;
536
+ ---
537
+
538
+ <SiteTitleRoot href={siteTitleHref}>
539
+ <ProjectLogo aria-hidden="true" focusable="false" />
540
+ <span translate="no">{siteTitle}</span>
541
+ </SiteTitleRoot>
542
+ ```
543
+
544
+ Register the replacement in `docs.config.ts` and hide the default header mark.
545
+ The custom component supports root styles plus its complete list of named
546
+ sub-elements: `Logo` and `Label`.
547
+
548
+ ```ts
549
+ components: {
550
+ overrides: {
551
+ SiteTitle: "./docs/components/SiteTitle.astro"
552
+ }
553
+ },
554
+ theme: {
555
+ styles: {
556
+ StarlightHeader: { LogoLink: { hide: true } }
557
+ },
558
+ customStyles: {
559
+ ProjectSiteTitle: { Logo: { color: "#text" } }
560
+ }
561
+ }
562
+ ```
563
+
564
+ `defineComponent(name, options)` accepts Tasty factory options and a name for
565
+ `theme.customStyles[name]`. It merges the configured partial style object before
566
+ creating the component, retaining the other base properties and responsive
567
+ states. Tasty options such as `elements`, `variants`, `styleProps`, `modProps`,
568
+ and `tokenProps` keep their behavior and inferred types, including generated
569
+ subcomponents such as `Component.Label`.
570
+ Configured styles become the component's defaults; variants and styles passed
571
+ at render time follow Tasty's usual precedence.
572
+
573
+ Use `defineComponent` when authoring a component that should support
574
+ `theme.customStyles`. Use `tasty` directly for ordinary Tasty creation or composition
575
+ that does not need a configuration name. To define a named component around
576
+ an existing React component that forwards `className`, pass it as `as` in the options. Use `className`
577
+ when passing a class to a Tasty component from Astro.
578
+
579
+ ### Global style trees
580
+
581
+ For markup you do not render through a Tasty component, call
582
+ `useGlobalStyles()` during rendering. For example, this Astro component
583
+ supports root styles and a `Label` sub-element through `theme.customStyles.ProjectNote`:
584
+
585
+ ```astro
586
+ ---
587
+ import {
588
+ resolveComponentStyles,
589
+ useGlobalStyles,
590
+ } from "@tenphi/cookbook/styling";
591
+
592
+ useGlobalStyles(
593
+ ".project-note",
594
+ resolveComponentStyles("ProjectNote", {
595
+ padding: "2x",
596
+ fill: "#surface-2",
597
+ Label: { $: "> strong", color: "#text", preset: "strong" },
598
+ }),
599
+ );
600
+ ---
601
+
602
+ <aside class="project-note"><strong>Note</strong><slot /></aside>
603
+ ```
604
+
605
+ `defineComponent` and `resolveComponentStyles` read the same `theme.customStyles`
606
+ configuration; user configuration contains only the properties to change. They do not require a
607
+ `data-tasty-anatomy` attribute. That attribute remains available for older
608
+ components using the compatibility bridge described above.
609
+
610
+ ## Linting custom styles
611
+
612
+ `@tenphi/cookbook/eslint-plugin` re-exports the Tasty ESLint plugin together with
613
+ Cookbook's `validationConfig`. The renderer offers the same exports from
614
+ `@tenphi/starlight/eslint-plugin`. Keep style definitions in `.ts` or `.tsx`
615
+ modules, as in the site title example above, to lint them with either ESLint or
616
+ oxlint.
617
+
618
+ Create `tasty.config.ts` at your project root:
619
+
620
+ ```ts
621
+ export default {
622
+ extends: "@tenphi/cookbook",
623
+ };
624
+ ```
625
+
626
+ Renderer-only consumers can use `extends: "@tenphi/starlight"` instead.
627
+ The preset registers both Cookbook styling import paths, its built-in tokens,
628
+ units, responsive states, and typography presets. It also describes
629
+ `defineComponent`, `resolveComponentStyles`, and `mergeStyles`, so their inline
630
+ style objects, variants, and named sub-elements receive the same checks as
631
+ `tasty()` calls. Partial overrides passed to `mergeStyles` may omit default state
632
+ values and retain the plugin's safeguards against fixes that replace base styles.
633
+
634
+ ### ESLint
635
+
636
+ Install ESLint and a TypeScript parser:
637
+
638
+ ```sh
639
+ pnpm add -D eslint @typescript-eslint/parser
640
+ ```
641
+
642
+ Add the plugin to `eslint.config.mjs`:
643
+
644
+ ```js
645
+ import parser from "@typescript-eslint/parser";
646
+ import tasty from "@tenphi/cookbook/eslint-plugin";
647
+
648
+ export default [
649
+ {
650
+ ...tasty.configs.recommended,
651
+ files: ["**/*.{ts,tsx}"],
652
+ languageOptions: { parser },
653
+ },
654
+ ];
655
+ ```
656
+
657
+ Run `pnpm exec eslint docs/components --max-warnings 0`. Use
658
+ `tasty.configs.strict` for additional checks, including custom property names and
659
+ runtime style values. The named `recommended` and `strict` exports contain rule
660
+ maps for configurations that register the plugin themselves.
661
+
662
+ ### oxlint
663
+
664
+ Install oxlint:
665
+
666
+ ```sh
667
+ pnpm add -D oxlint
668
+ ```
669
+
670
+ Create `oxlint.config.mjs` using its JavaScript plugin support:
671
+
672
+ ```js
673
+ import { recommended } from "@tenphi/cookbook/eslint-plugin";
674
+
675
+ export default {
676
+ jsPlugins: [{ name: "tasty", specifier: "@tenphi/cookbook/eslint-plugin" }],
677
+ rules: recommended,
678
+ };
679
+ ```
680
+
681
+ Run `pnpm exec oxlint --config oxlint.config.mjs docs/components --deny-warnings`.
682
+ The `tasty` alias keeps rule names such as `tasty/known-property` consistent with
683
+ ESLint. Import `strict` instead of `recommended` to enable the stricter rule map.
684
+ Both presets are tested with oxlint 1.83.0; JavaScript plugin support is experimental.
685
+
686
+ ### Custom theme names
687
+
688
+ Add names introduced by your `docs.config.ts` theme to the validation config.
689
+ For example, after defining `theme.tokens["$project-gap"]`, `theme.states["@project-wide"]`,
690
+ and `theme.presets["project-title"]`:
691
+
692
+ ```ts
693
+ import type { TastyValidationConfig } from "@tenphi/cookbook/eslint-plugin";
694
+
695
+ export default {
696
+ extends: "@tenphi/cookbook",
697
+ tokens: ["$project-gap"],
698
+ states: ["@project-wide"],
699
+ presets: ["project-title"],
700
+ } satisfies TastyValidationConfig;
701
+ ```
702
+
703
+ `extends` merges and deduplicates these arrays with the inherited configuration,
704
+ so list only your additions. It also inherits the styling helper signatures;
705
+ entries you add to `styleFunctions` override inherited signatures by function name.
706
+ The exported `validationConfig` object remains available for programmatic use.
707
+ The entry point also exports the upstream `StyleFunctionConfig` and
708
+ `ResolvedConfig` types for shared lint configurations. Additional imported
709
+ helpers can be registered in `styleFunctions`; use `partial: true` for helpers
710
+ that merge overrides into existing styles.
711
+
712
+ ## Static behavior
713
+
714
+ Documentation content, navigation, headings, code, and images remain readable
715
+ without client JavaScript. Search, mobile navigation, copy controls, appearance
716
+ persistence, and executable previews can progressively enhance the static
717
+ output.
718
+
719
+ ### Responsive header and navigation
720
+
721
+ The header uses `#header`, generated from `theme.palette.header` (or the page
722
+ surface) at 70% opacity in all four appearance modes, with a 16px backdrop blur.
723
+ Customize the outer bar with `theme.styles.HeaderFrame`, its contents with
724
+ `StarlightHeader`, and header buttons with `HeaderLinks` and `SearchButton`.
725
+
726
+ Below 50rem, page navigation moves into a header row and opens a left drawer.
727
+ `MobileMenuToggle.Section` and `Page` style its breadcrumb labels. The drawer
728
+ contains the complete active section, a collapsible `MobileNavigationTabs`
729
+ selector, and a close button. The drawer slides in and out over 120ms and
730
+ disables motion when the reader prefers reduced motion. Its mobile shadow uses
731
+ the Glaze `shadow`-typed `#shadow` token. Customize the movement and shadow through
732
+ `theme.styles.Sidebar`, and the underlay through `Sidebar.Backdrop` and
733
+ `OpenBackdrop`. `theme.palette.overlay` supplies the underlay declaration, which defaults
734
+ to black at 50% opacity. Glaze resolves it in fixed mode with tone remapping
735
+ disabled, so it stays black in dark and high-contrast schemes.
736
+ Icon buttons retain their plain desktop appearance on mobile. More uses
737
+ left-aligned navigation links with a `1bw` gap, without primary button variants.
738
+ `HeaderLinks.PanelLink` styles the menu rows; `FirstPanelLink` reserves space
739
+ for the close button. The inset divider above the tabs belongs to `TopNavigation`.
740
+
741
+ The table of contents is hidden below 72rem through `TableOfContentsLayout`.
742
+ `MobileTableOfContents` remains a legacy style name for custom overrides; the
743
+ default renderer no longer renders that component. `Layout` controls shared
744
+ navigation dimensions and the drawer's scroll lock; `PageFrame` styles the
745
+ page container and content columns.