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