@tenphi/cookbook 0.20.3 → 0.20.5

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.
@@ -1,35 +1,29 @@
1
1
  ---
2
- title: Theme reference
3
- description: Build an accessible Glaze palette, customize Tasty tokens, and use the supported Astro components.
2
+ title: Theme and components
3
+ description: Choose a brand, build a semantic palette, set design tokens, and find the right customization guide.
4
4
  sidebar:
5
5
  order: 5
6
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
- For the short brand, logo, and font path, start with the
30
- [configuration recipes](./recipes.md). This reference covers semantic colors,
31
- typography, tokens, every configurable style tree, and custom components. Read
32
- the [customization rules](./customization-rules.md) before adding styles.
7
+ Cookbook resolves Glaze colors and Tasty styles during the build, then ships
8
+ static CSS. Start with a brand color; add semantic palette roles, typography,
9
+ and component overrides only where the default needs to change. The
10
+ [configuration recipes](./recipes.md) show complete, build-checked starting
11
+ points. Read the [customization rules](./customization-rules.md) before writing
12
+ new styles.
13
+
14
+ | To change | Start here |
15
+ | ------------------------------------- | ------------------------------------------------- |
16
+ | Brand, color roles, and design tokens | This page |
17
+ | Font files, families, and typography | [Fonts and typography](./fonts-and-typography.md) |
18
+ | Built-in UI surfaces and named parts | [Component styles](./component-styles.md) |
19
+ | Your own Astro or MDX components | [Custom components](./custom-components.md) |
20
+ | Complete configuration fields | [Configuration reference](./configuration.md) |
21
+
22
+ Glaze derives light, dark, and high-contrast values from the brand and palette
23
+ inputs. Tasty applies those values through semantic tokens. See the
24
+ [Glaze reference](./upstream/glaze/README.md) for color declarations and the
25
+ [Tasty style DSL](./upstream/tasty/docs/dsl.md) for state maps, tokens, units,
26
+ and sub-elements.
33
27
 
34
28
  ## Brand color
35
29
 
@@ -142,842 +136,6 @@ Borders stay intentionally quiet: Glaze derives their hue from `brand` but
142
136
  uses only one quarter of the brand saturation. Normal and high-contrast modes
143
137
  change border tone, not that restrained saturation relationship.
144
138
 
145
- The Appearance button in the desktop and mobile header opens one panel with
146
- Color scheme and Contrast sections. Color scheme offers Light, Dark, and Auto;
147
- Contrast offers Normal, High, and Auto. Each preference is independent, and
148
- selections are saved in the browser. The panel supports keyboard navigation,
149
- Escape, and outside-click dismissal. The popover fades and scales vertically
150
- from 96% to 100% over 120ms using native entry and exit transitions. Reduced
151
- motion disables the transition. Customize these states with
152
- `theme.styles.ThemeSelect.Panel` and `OpenPanel`; the root `$popover-transition`
153
- token controls the duration. Contrast can follow the system, force the
154
- normal palette, or activate the Glaze high-contrast palette.
155
- System mode responds to `prefers-contrast: more`; an explicit selection is
156
- persisted and takes precedence over that media query.
157
-
158
- Interactive controls step up exactly one surface level: a control on `surface`
159
- uses `surface-2`, while a control on `surface-2` uses `surface-3`. Hover and
160
- pressed states build on that elevated surface without changing the border.
161
- Inputs stay on their surrounding surface so their border remains the visual
162
- boundary. Primary buttons and selected navigation use the fixed-mode
163
- `accent-surface` and its paired light `accent-surface-text`. The brand fill
164
- keeps its polarity in dark mode while Glaze adjusts it to preserve label
165
- contrast.
166
-
167
- Page navigation keeps Next first in the keyboard order. It appears above
168
- Previous on narrow screens; wider layouts show Previous at the start and Next
169
- at the end.
170
-
171
- ## Design tokens
172
-
173
- Token names follow Tasty's
174
- [token and unit syntax](./upstream/tasty/docs/dsl.md#built-in-units): `$name`
175
- becomes the CSS custom property `--name`. Existing `--name` keys are still
176
- accepted for compatibility.
177
-
178
- ```ts
179
- theme: {
180
- tokens: {
181
- "$gap": "0.5rem",
182
- "$radius": "8px",
183
- "$card-radius": "16px",
184
- "$border-width": "1px",
185
- "$outline-width": "2px",
186
- "$outline-offset": "2px",
187
- "$control-height": "2.5rem",
188
- "$layout-width": "87.5rem",
189
- "$content-width": "58rem",
190
- "$sidebar-width": "17.5rem"
191
- }
192
- }
193
- ```
194
-
195
- `layout-width` caps and centers the complete documentation shell, while
196
- `content-width` limits the reading column inside it. `radius` is the control
197
- and navigation radius; `card-radius` is the larger surface radius. Keeping
198
- those roles separate makes a sharp control theme or a soft card theme possible
199
- without one-off component overrides.
200
-
201
- ## Typography presets
202
-
203
- The built-in [typography presets](./upstream/tasty/docs/styles.md#preset) are
204
- `body`, `heading`, `h1` through `h6`, `navigation`, `small`, and `code`. Onest
205
- is self-hosted and used for body and heading text by default; JetBrains Mono is
206
- self-hosted for code.
207
-
208
- ### Change font families
209
-
210
- Set `theme.fonts` to load fonts and apply them to the matching presets. A Google
211
- Fonts family needs only its name:
212
-
213
- ```ts
214
- theme: {
215
- fonts: {
216
- body: "Inter",
217
- heading: "Newsreader"
218
- }
219
- }
220
- ```
221
-
222
- The short form follows the regular and strong weights in your configured
223
- presets, including heading and custom preset references. It requests normal and
224
- italic styles, prefers a variable range, and falls back to real static weights
225
- when ranges are unavailable. A font without italics produces a build warning;
226
- choose another family or select only its supported styles. To control the
227
- request exactly, list weights or ascending variable ranges and styles. Google
228
- Fonts must support them; see the
229
- [Google Fonts CSS API](https://developers.google.com/fonts/docs/css2) for its
230
- available styles and weight syntax.
231
-
232
- ```ts
233
- theme: {
234
- fonts: {
235
- body: { google: "Inter", weights: ["400 750"], styles: ["normal", "italic"] }
236
- }
237
- }
238
- ```
239
-
240
- For a font you own, put its files in `public/fonts/` and give Cookbook their
241
- public paths. Cookbook checks that each file exists during the build and adjusts
242
- its URL for the Astro `base` path. A variable font can cover a weight range;
243
- use separate entries for regular and italic files when needed:
244
-
245
- ```ts
246
- theme: {
247
- fonts: {
248
- body: {
249
- family: "Acme Sans",
250
- files: [
251
- { src: "/fonts/acme-sans.woff2", weight: "100 900" },
252
- { src: "/fonts/acme-sans-italic.woff2", weight: "100 900", style: "italic" }
253
- ]
254
- }
255
- }
256
- }
257
- ```
258
-
259
- The `body` role also feeds navigation and small text. `heading` feeds `h1`
260
- through `h6`, while `code` covers code text. Each role has a system fallback.
261
- Cookbook emits font faces through Tasty with `font-display: swap`. Google CSS
262
- and font files are downloaded at build time and cached in
263
- `<Astro cacheDir>/cookbook-fonts`. Files are served from your own site's
264
- `_cookbook/fonts/` path by default, including the deployment base. Visitors make
265
- no Google Fonts requests. Existing local-file definitions keep working.
266
-
267
- ```ts
268
- theme: {
269
- fonts: { body: "Inter" },
270
- fontLoading: {
271
- google: "self-hosted", // or "remote" to keep Google's font CDN URLs
272
- cache: "reuse", // "refresh" to fetch again; "offline" to forbid network
273
- display: "swap" // also auto, block, fallback, optional
274
- }
275
- }
276
- ```
277
-
278
- The first build requires network access. Preserve the Astro cache between CI
279
- runs; `offline` fails with an actionable message when an entry is missing or
280
- corrupt. Cache entries are content-verified and keyed by the exact request.
281
- `refresh` deliberately updates them. For a fully checked-in font source, copy
282
- licensed files into `public/fonts/` and use the local form. Only `wght` and
283
- `ital` axes are requested; use local variable files and Tasty typography styles
284
- for other axes. Styles and weights that do not exist are rejected for explicit
285
- requests. For static fonts, browsers select their nearest real weight;
286
- intermediate weights require a variable font.
287
-
288
- Migration: name-only Google font configuration now downloads the required
289
- weights and italics and serves them locally. This increases the initial build's
290
- font downloads and removes visitors' external font dependency. Set
291
- `fontLoading.google: "remote"` to retain CDN delivery. Existing CSS-variable
292
- weights that cannot be resolved from presets require explicit `weights`.
293
-
294
- ### Adjust presets
295
-
296
- Presets merge property-by-property, so changing size, weight, or line height
297
- does not require copying the rest of the preset. `theme.presets` takes precedence
298
- over `theme.fonts` for an explicitly set `fontFamily`:
299
-
300
- ```ts
301
- theme: {
302
- presets: {
303
- body: {
304
- boldFontWeight: 680
305
- },
306
- heading: {
307
- fontWeight: 650,
308
- boldFontWeight: 740
309
- },
310
- h1: {
311
- fontSize: "3rem",
312
- letterSpacing: "-0.02em"
313
- },
314
- code: { fontSize: "0.9rem" }
315
- }
316
- }
317
- ```
318
-
319
- For a system or already loaded font, set its CSS family directly with
320
- `theme.presets.<role>.fontFamily`. This does not load a font file. Additional
321
- named presets are available to custom MDX components.
322
-
323
- Heading presets reference the `heading` family and weights; body, navigation,
324
- and controls reference `body`. Semantic `strong` and `b` elements use Tasty's
325
- `strong` modifier, so their weight comes from the active preset's
326
- `boldFontWeight` instead of a separate element-specific value. `strong` is a
327
- reserved modifier keyword, not a named preset; the modifier-only form is
328
- equivalent to `inherit / strong`. Body text uses a regular `400` weight and
329
- neutral tracking, while
330
- headings use a medium `610` weight and progressively gentle negative tracking.
331
- The separate `720` heading bold weight keeps emphasized heading text distinct.
332
- Navigation uses a lighter `450` weight with `580` for the current sidebar page,
333
- while smaller group labels establish hierarchy without oversized bold text.
334
- Inactive sidebar links use `sidebar-text`, a slightly softer blend of `text-soft`
335
- and the surface. In high-contrast mode it matches `text-soft`. Set
336
- `theme.palette['sidebar-text']` to adjust that color without changing other
337
- secondary text, or `theme.styles.Sidebar.Control` to customize the control.
338
- Top-level sidebar groups are always-visible section headings with their direct
339
- links aligned beneath them. Deeper groups expand on click or keyboard activation;
340
- the current group and its ancestors start open. Groups can have a parent-page
341
- `link`; activating an unselected linked header selects that page and keeps its
342
- children open. Activating the selected header toggles its children.
343
- The current group can stay collapsed because its header remains visible. Only
344
- the current page is highlighted; ancestors receive no extra emphasis. Explicit expansion choices are
345
- remembered for the browser tab; automatically revealing the current page does
346
- not overwrite them. Desktop scroll position is restored independently of mobile
347
- menu scrolling, and the current page's ancestors remain open after navigation.
348
- Nested groups use indentation without vertical rails. Customize section headings
349
- with `theme.styles.Sidebar.SectionHeading`, indentation with `NestedItem`, and
350
- disclosure icons with `Caret` and `ExpandedCaret`.
351
- Adjacent sidebar items have a `1bw` gap. Inline code scales to `0.875em` of its
352
- surrounding text, including smaller table text; code blocks keep the `code` preset
353
- size. Customize inline code through `theme.styles.MarkdownInlineCode`.
354
- Cookbook applies each semantic typography role through its complete Tasty
355
- `preset`, so configured fields such as `fontStyle` and `textTransform` are not
356
- silently omitted.
357
-
358
- The exported `DEFAULT_THEME_TOKENS` and `DEFAULT_TYPOGRAPHY_PRESETS` constants
359
- are useful when building a theme editor or presenting a reset action.
360
-
361
- ## Style customization
362
-
363
- Cookbook-owned interface elements are direct `tasty()` components. Supported
364
- Cookbook components and generated Markdown surfaces use Tasty style trees.
365
- Customize them by name under `theme.styles`; the configuration is resolved
366
- before CSS generation, so this is not a selector-based CSS override.
367
-
368
- > **Direct styles, without a specificity battle.** Cookbook merges your partial
369
- > style object with the component's base styles before Tasty extracts CSS. The
370
- > generated stylesheet reflects that result; changing a supported property
371
- > does not add a competing rule just to beat the original. You can customize
372
- > existing elements without long selectors or `!important`.
373
-
374
- Start with the area you want to change, then use the complete sub-element table
375
- below to find its exact anatomy:
376
-
377
- | Page area | Style names to inspect |
378
- | ----------------------------- | ------------------------------------------------------------------------------------ |
379
- | Header, title, and navigation | `HeaderFrame`, `Header`, `SiteLogo`, `HeaderLinks`, `TopNavigation` |
380
- | Sidebar and mobile menu | `Sidebar`, `MobileMenuToggle`, `MobileNavigationTabs`, `MobileMenuFooter` |
381
- | Article text and headings | `Markdown`, `MarkdownHeading`, `Heading`, `MainContent` |
382
- | Code and diagrams | `MarkdownCodeBlock`, `MarkdownInlineCode`, `SyntaxHighlight`, `CodeGroup`, `Mermaid` |
383
- | Search | `SearchButton`, `Search`, `SearchResults` |
384
- | Table of contents | `TableOfContents`, `MobileTableOfContents`, `TableOfContentsLayout` |
385
- | Cards, notes, and tabs | `Card`, `Callout`, `MarkdownAlert`, `Tabs` |
386
- | Footer and page controls | `Footer`, `Pagination`, `PageActions`, `ThemeSelect` |
387
-
388
- For example, this changes a typography role and one named element without
389
- copying a component's full style tree:
390
-
391
- ```ts
392
- theme: {
393
- presets: { h2: { fontSize: "1.75rem" } },
394
- styles: {
395
- Sidebar: {
396
- LinkLabel: {
397
- preset: "body",
398
- whiteSpace: { "": "nowrap", "@mobile": "normal" }
399
- }
400
- }
401
- }
402
- }
403
- ```
404
-
405
- The `@mobile` value sits with the default value in one Tasty state map. Shared
406
- tokens and presets keep the component aligned with the rest of the site, while
407
- Glaze resolves semantic colors across the configured schemes and contrast
408
- modes.
409
-
410
- In a generated site, run `npm run validate` (or the matching package manager's
411
- command) after editing `docs.config.ts`. It checks TypeScript theme properties,
412
- then runs Cookbook's preflight and production output checks. A misspelled
413
- preset or built-in sub-element property fails type checking.
414
-
415
- Provide only the root and named
416
- [sub-element](./upstream/tasty/docs/dsl.md#sub-element) properties you want to
417
- override. Cookbook deep-merges that partial style object into the complete
418
- base style object inside the renderer, following Tasty's
419
- [state-map merge semantics](./upstream/tasty/docs/dsl.md#extending-vs-replacing-state-maps):
420
-
421
- ```ts
422
- theme: {
423
- styles: {
424
- ThemeSelect: {
425
- Trigger: {
426
- border: "#border-strong"
427
- },
428
- Panel: {
429
- padding: "1x",
430
- shadow: "0 1rem 3rem #shadow"
431
- }
432
- },
433
- TopNavigation: {
434
- Link: { preset: "body" },
435
- CurrentLink: { color: "#accent-text" }
436
- },
437
- Sidebar: {
438
- LinkLabel: { whiteSpace: "normal" }
439
- },
440
- TableOfContents: {
441
- LinkLabel: { whiteSpace: "normal" }
442
- },
443
- MobileMenuToggle: {
444
- Page: { color: "#accent-text" }
445
- }
446
- }
447
- }
448
- ```
449
-
450
- No merge helper or base style object is required in user configuration. Style
451
- customization has one behavior: every supplied object is a partial override and
452
- is merged into the base inside Cookbook.
453
-
454
- Every configurable surface accepts styles at the root plus these named Tasty
455
- sub-elements:
456
-
457
- | Configuration name | Named sub-elements |
458
- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
459
- | `Card` | `Heading2`, `Heading3`, `Paragraph` |
460
- | `Callout` | `Title`, `Body`, `Tip`, `Caution`, `Danger` |
461
- | `CodeGroup` | `Caption`, `Pre`, `Code` |
462
- | `Tab` | `Heading`, `Hidden`, `HiddenHeading` |
463
- | `MobileTableOfContents` | `Summary`, `List`, `NestedList`, `Item`, `Link`, `HoverLink`, `Focus` |
464
- | `PageActions` | `Control`, `Hover`, `Focus`, `Pending`, `Status` |
465
- | `Footer` | `Meta`, `LoneMetaItem`, `MetaLink`, `HoverMetaLink`, `Credit`, `CreditLink`, `HoverCreditLink` |
466
- | `Hero` | `Visual`, `DarkVisual`, `LightVisual`, `Stack`, `Copy`, `Title`, `Tagline`, `Actions`, `Action`, `HoverAction`, `PrimaryAction`, `SecondaryAction`, `MinimalAction`, `ActionIcon` |
467
- | `LanguageSelect` | `Label`, `HoverLabel`, `LabelIcon`, `Select`, `Caret`, `Option` |
468
- | `Logo` | `Svg`, `Mark` |
469
- | `SiteLogo` | `Image`, `Light`, `Dark` |
470
- | `MarkdownCodeBlock` | `Pre`, `CopyButton`, `HoverCopyButton`, `CopiedButton`, `CopyIcon`, `CopiedIcon`, `Code`, `Diff`, `DiffCode`, `DiffLine`, `EmptyDiffLine`, `InsertedLine`, `DeletedLine` |
471
- | `MarkdownHeading` | `Heading`, `Heading1`, `Heading2`, `Heading3`, `Heading4`, `Heading5`, `Heading6`, `Link`, `RevealedLink`, `HoverLink`, `LinkIcon`, `CopiedLink`, `CopiedLinkIcon`, `CopiedIcon` |
472
- | `MarkdownInlineCode` | None |
473
- | `MarkdownTable` | `Table`, `Cell`, `LastBodyRowCell`, `HeaderCell`, `Scroll` |
474
- | `Mermaid` | `Diagram`, `Text`, `MonoText` |
475
- | `MobileMenuFooter` | `Social` |
476
- | `MobileMenuToggle` | `Control`, `Icon`, `Section`, `Page`, `HoverControl`, `ActiveControl` |
477
- | `MobileNavigationTabs` | `Trigger`, `Marker`, `Caret`, `ExpandedCaret`, `Label`, `List`, `Item`, `Link`, `HoverLink`, `CurrentLink` |
478
- | `MobileTableOfContents` | `Item`, `Link`, `LinkLabel`, `HoverLink`, `CurrentLink`, `CurrentIndicator` |
479
- | `PackageVersion` | None |
480
- | `VersionSwitcher` | `Trigger`, `Panel`, `OpenPanel`, `Link`, `CurrentLink` |
481
- | `Preview` | `Caption`, `Stage`, `Frame`, `Code`, `Summary`, `Pre` |
482
- | `Sidebar` | `Backdrop`, `OpenBackdrop`, `MobileHeading`, `HomeLink`, `HomeLogo`, `HomeLabel`, `Close`, `HoverClose`, `CloseIcon`, `CurrentLink`, `OpenPane`, `EnteredPane`, `Content`, `Tree`, `List`, `Item`, `TopLevelSpacing`, `GroupSpacing`, `NestedItem`, `SectionHeading`, `Control`, `Summary`, `GroupLabel`, `GroupLabelText`, `Link`, `LinkLabel`, `InteractiveControl`, `SummaryMarker`, `Caret`, `ExpandedCaret`, `LinkedSummary`, `GroupLink`, `LinkedSectionHeading`, `SectionLink`, `Badge`, `TopLevelLink` |
483
- | `SocialIcons` | `Link`, `HoverLink`, `Icon` |
484
- | `Header` | `Primary`, `TitleAndSearch`, `Title`, `LogoLink`, `Logo`, `SiteTitle`, `Search`, `SearchElement`, `Tools`, `ToolItem`, `Social`, `MobileTheme` |
485
- | `TableOfContentsLayout` | `WithMobile`, `Content` |
486
- | `SearchButton` | `PendingShortcut`, `Label`, `Shortcut`, `Hover`, `Active`, `NativeIcon`, `Icon` |
487
- | `Layout` | `Islands`, `LockedPage`, `Light`, `Auto` |
488
- | `HeaderLinks` | `Desktop`, `DesktopLink`, `Link`, `HoverLink`, `PrimaryLink`, `HoverPrimaryLink`, `Trigger`, `HoverTrigger`, `Panel`, `PanelNavigation`, `PanelLink`, `FirstPanelLink`, `Close`, `HoverClose` |
489
- | `Heading` | `Level1`, `Level2`, `Level3`, `Level4`, `Level5`, `Level6`, `PageTitle` |
490
- | `HeaderFrame` | None |
491
- | `PageFrame` | `MainFrame`, `SidebarFrame`, `Columns` |
492
- | `Steps` | `Item`, `Marker` |
493
- | `Tabs` | `List`, `Button`, `SelectedButton`, `FocusedButton` |
494
- | `TableOfContents` | `Heading`, `List`, `Item`, `Link`, `LinkLabel`, `HoverLink`, `CurrentLink` |
495
- | `ThemeSelect` | `Trigger`, `HoverTrigger`, `ActiveTrigger`, `Icon`, `Panel`, `OpenPanel`, `Section`, `SectionSpacing`, `SectionLabel`, `Option`, `HoverOption`, `CheckedOption`, `FocusedOption`, `Input`, `OptionIcon`, `Checkmark`, `SelectedCheckmark` |
496
- | `TopNavigation` | `Scrollbar`, `Link`, `HoverLink`, `CurrentLink`, `ActiveIndicator` |
497
- | `Document` | `All`, `Body`, `Control`, `Pointer`, `ResponsiveWidth`, `ResponsiveHeight`, `Hidden`, `PrintHidden`, `DesktopBlock`, `DesktopFlex`, `ScreenReaderOnly`, `Strong`, `Link`, `NarrowBlock`, `MobileBlock`, `Code`, `FocusRing`, `CurrentLink`, `SearchOpen` |
498
- | `MainPane` | `WithSidebars` |
499
- | `MainContent` | `ContentSpacing`, `Container`, `Panel`, `FirstPanel`, `BodyPanel` |
500
- | `Banner` | `Link` |
501
- | `SkipLink` | `Focus` |
502
- | `Search` | `Status`, `Dialog`, `CloseIcon`, `OpenDialog`, `Backdrop`, `Frame`, `Container`, `Close`, `HoverClose`, `ActiveClose` |
503
- | `Pagination` | `Link`, `PreviousLink`, `NextLink`, `NextIcon`, `NextLabel`, `HoverLink`, `ActiveLink`, `Title`, `LoneNextLink`, `Icon`, `PreviousIconRtl`, `NextIconRtl` |
504
- | `Markdown` | `Block`, `BlockSpacing`, `HeadingSpacing`, `List`, `CompactItem`, `ListItem`, `DefinitionTerm`, `DefinitionDescription`, `Link`, `HoverLink`, `Quote`, `Rule`, `Details`, `HoverDetails`, `Summary`, `OpenSummary`, `SummaryMarker`, `SummaryIcon`, `OpenSummaryIcon`, `Code` |
505
- | `MermaidSource` | None |
506
- | `MarkdownAlert` | `Note`, `Tip`, `Caution`, `Danger`, `Title`, `FirstContent` |
507
- | `SyntaxHighlight` | `Scroll`, `Wrap`, `Marker`, `Comment`, `Punctuation`, `Keyword`, `String`, `Token`, `Property`, `Number`, `Function`, `Value`, `Operator`, `Text`, `Bg`, `Inserted`, `Deleted`, `Italic`, `Strong`, `Underline` |
508
- | `SearchResults` | `Form`, `Input`, `Clear`, `Results`, `Result`, `ResultLink`, `SearchIcon`, `ClearIcon`, `SuppressedClear`, `Message`, `List`, `Title`, `Excerpt`, `NestedResult`, `Match`, `More`, `HoverMore` |
509
-
510
- `COOKBOOK_COMPONENT_NAMES` publishes the configuration names, and
511
- `COOKBOOK_COMPONENT_SUB_ELEMENTS` publishes the complete sub-element lists for
512
- theme editors and other tooling. The corresponding TypeScript types are
513
- `CookbookComponentName`, `CookbookComponentSubElementName`,
514
- `CookbookComponentStyles`, `ComponentStyleConfig`, and `ComponentStylesConfig`.
515
- Known configuration names provide editor suggestions for their named
516
- sub-elements. `ThemeSelect` styles the combined Appearance panel; replace old
517
- `Select` and `Picker` overrides with `Trigger` and `Panel`. The separate
518
- `ContrastSelect` surface is replaced by the same `ThemeSelect` panel and option
519
- sub-elements. `SocialIcons` styles the shared icon-button links in the header
520
- and mobile menu. On wider layouts, heading permalinks sit to the left of the
521
- heading and are revealed on heading hover or keyboard focus. On mobile they
522
- stay visible and inline so the target is not clipped by the narrower content
523
- gutter. Customize the behavior with `MarkdownHeading.Link`,
524
- `MarkdownHeading.RevealedLink`, and `MarkdownHeading.LinkIcon`. The `#` icon
525
- scales with its heading and stays centered in the link target across heading
526
- levels and screen widths. Activating a heading link copies the full page URL
527
- with that section’s fragment without scrolling or changing the current URL. A
528
- checkmark and screen-reader status confirm the copy; clipboard failures are
529
- announced. Modified clicks and links
530
- without JavaScript retain native anchor behavior. Customize the confirmation
531
- with `MarkdownHeading.CopiedLink`, `MarkdownHeading.CopiedLinkIcon` (the hidden
532
- original icon), and `MarkdownHeading.CopiedIcon` (the checkmark).
533
-
534
- Root properties customize each navigation container. For example, overriding
535
- `Sidebar.LinkLabel.whiteSpace` keeps the other base `LinkLabel` properties
536
- because the configured object is merged by default.
537
- By default, the footer credit uses the primary body text color, while its
538
- Cookbook link uses the semantic brand link color.
539
-
540
- `components.overrides` remains the structural escape hatch for replacing an
541
- Astro component. Prefer `theme.styles` when the markup and behavior remain the
542
- same.
543
-
544
- Register custom names and their partial Tasty objects in `theme.customStyles`.
545
- Built-in names belong in `theme.styles` and reject misspelled sub-elements.
546
- Custom names can also target a matching user-authored `data-tasty-anatomy` attribute.
547
- The name must match the string passed to `defineComponent()` or
548
- `resolveComponentStyles()`, or the `data-tasty-anatomy` value. A production
549
- build warns when a configured custom name has no matching component style
550
- resolver or rendered anatomy attribute.
551
-
552
- The default renderer runs Tasty in Astro extract mode. Direct components and
553
- the remaining document/vendor bridge styles are collected into shared static
554
- CSS during the build, while appearance controls add only the small client
555
- behavior needed to persist selected theme and contrast modes. The compact
556
- Cookbook logo is shown beside the project title in the default top bar, links
557
- to the localized home page, and is also the default generated favicon. Use
558
- [`site.favicon`](./configuration.md#site-icons) to generate the complete icon
559
- set from project artwork.
560
-
561
- ## Diff snippets
562
-
563
- Use a `diff` code fence to highlight changed lines. Insertions and deletions
564
- receive subtle theme-aware backgrounds while their `+` and `-` markers remain
565
- visible for readers who do not distinguish the colors.
566
-
567
- ```diff
568
- -formatOkhsl(v.h, v.s * 100, l * 100);
569
- +formatOkhsl(v.h, v.s, l);
570
-
571
- -formatOkhst(v.h, v.s * 100, v.t * 100);
572
- +formatOkhst(v.h, v.s, v.t);
573
- ```
574
-
575
- ## Astro and MDX components
576
-
577
- The facade exposes supported components from `@tenphi/cookbook/components`:
578
-
579
- ```mdx
580
- import { Card, Logo, Preview, Steps, Tabs } from "@tenphi/cookbook/components";
581
-
582
- <Card title="Package-first" href="/getting-started/">
583
- Generate a site from a locked npm artifact.
584
- </Card>
585
- ```
586
-
587
- * `Card` renders a titled article or link.
588
- * `Logo` renders the Cookbook mark using the active brand color.
589
- * `Steps` provides an ordered steps container.
590
- * `Tabs` and `Tab` provide accessible tabbed panels.
591
- * `Callout` renders a note, tip, caution, or danger block.
592
- * `CodeGroup` renders labeled source strings in tabs.
593
-
594
- See [Authoring components](./authoring.mdx) for complete examples and props.
595
-
596
- * `Preview` isolates HTML, CSS, and optional JavaScript in a sandboxed iframe.
597
- The frame never receives same-origin access to the documentation page.
598
-
599
- Use MDX only for repository content you control. Locked package content remains
600
- Markdown-only unless the consumer explicitly sets `trust: "mdx"` for that
601
- source.
602
-
603
- ## Authoring custom components
604
-
605
- Import styling tools from `@tenphi/cookbook/styling`. They use the same Tasty
606
- runtime as Cookbook, with its semantic colors, typography presets, units, and
607
- responsive states. The renderer package also exposes them from
608
- `@tenphi/renderer/styling`.
609
-
610
- | Export | Purpose |
611
- | ------------------------ | ---------------------------------------------------------------------------------- |
612
- | `defineComponent` | Create a named Tasty component with its `theme.customStyles[name]` overrides. |
613
- | `tasty` | Use Tasty directly without binding a component to `theme.customStyles`. |
614
- | `useGlobalStyles` | Collect a global Tasty style tree while rendering a page. |
615
- | `resolveComponentStyles` | Merge `theme.customStyles[name]` into a complete base style tree for global rules. |
616
- | `mergeStyles` | Compose your own base styles with Tasty's deep-merge semantics. |
617
- | `Styles` | TypeScript type for a Tasty style object. |
618
-
619
- Cookbook supplies the React renderer and extracts these styles into its static
620
- CSS. No additional Tasty dependency, Astro integration, or `client:*` directive
621
- is needed for static components. Set shared tokens, presets, and states in
622
- `docs.config.ts` before rendering. Import the styling entry point in component
623
- modules; configuration files should use the main `@tenphi/cookbook` entry point.
624
-
625
- ### A shared site logo
626
-
627
- Set `site.logo` once to use your artwork in the header and mobile drawer:
628
-
629
- ```ts
630
- site: {
631
- title: "Acme",
632
- logo: "./assets/acme.svg"
633
- }
634
- ```
635
-
636
- Paths are local files relative to the resolved project root, just like
637
- `site.favicon`. Cookbook reads the intrinsic dimensions and copies the image to
638
- a content-hashed URL under the deployment base. SVG, PNG, JPEG, WebP, AVIF, and
639
- GIF are supported. Missing or invalid files fail during setup.
640
-
641
- ```ts
642
- site: {
643
- title: "Acme",
644
- logo: {
645
- light: "./assets/acme-light.svg",
646
- dark: "./assets/acme-dark.svg",
647
- alt: "Acme documentation",
648
- href: "/",
649
- decorative: true
650
- },
651
- favicon: "./assets/acme-icon.svg"
652
- }
653
- ```
654
-
655
- Use `src` for one image, or both `light` and `dark`. Variants follow the
656
- appearance control and the system preference in Auto mode. They must share an
657
- aspect ratio to prevent layout shifts. Optional `width` and `height` describe
658
- intrinsic dimensions; Tasty controls display size. Customize
659
- `theme.styles.SiteLogo` and its complete anatomy: `Image`, `Light`, and `Dark`.
660
- Wide wordmarks retain their proportions within the header.
661
-
662
- The logo is decorative by default because the neighboring title labels the
663
- site. Set `decorative: false` and `alt` when the image itself carries meaningful
664
- text. The header logo link still has a name when the image is decorative.
665
- `href` defaults to the localized documentation home; it accepts a root-relative
666
- documentation route or HTTP(S) destination. The header title and drawer use the
667
- same destination. `site.logo: false` removes the mark from both locations.
668
-
669
- A logo does not change the favicon or touch icons. Configure `site.favicon`
670
- separately for square artwork; the default Cookbook icon set remains in place
671
- until changed. `Logo` remains available as the standalone Cookbook book mark.
672
-
673
- The built-in mark uses `theme.palette["logo-surface"]` and `theme.palette["logo-mark"]`.
674
- The default homepage hero artwork also uses `logo-surface` for its brand fill.
675
- Both default to Glaze `mode: "fixed"`: the brand background and light book keep
676
- their polarity in dark mode while respecting Glaze's tone boundaries and
677
- high-contrast settings. The mark-to-background contrast floor is 3:1 normally
678
- and 4.5:1 in high contrast. These colors are separate from interactive accents.
679
- For example, to customize the logo background while retaining brand hue:
680
-
681
- ```ts
682
- theme: {
683
- brand: { from: "#315efb" },
684
- palette: {
685
- "logo-surface": {
686
- base: "logo-mark",
687
- tone: 40,
688
- saturation: 0.8,
689
- mode: "fixed",
690
- contrast: { wcag: [3, 4.5] },
691
- },
692
- "logo-mark": { tone: 100, saturation: 0, mode: "fixed" },
693
- },
694
- }
695
- ```
696
-
697
- Custom image assets keep their authored colors; use `light`/`dark` files when
698
- you want distinct artwork. Header and drawer home links center logos with the
699
- site title regardless of their aspect ratio. Use `theme.styles.SiteLogo` for
700
- image size and `theme.styles.Header.SiteTitle` / `Sidebar.HomeLink`
701
- for typography; they do not need baseline offsets for a different preset.
702
-
703
- ### Advanced site title markup
704
-
705
- To put your own logo and title in one home link, create
706
- `docs/components/site-title.ts`:
707
-
708
- ```ts
709
- import { defineComponent } from "@tenphi/cookbook/styling";
710
-
711
- export const SiteTitleRoot = defineComponent("ProjectSiteTitle", {
712
- as: "a",
713
- styles: {
714
- display: "flex",
715
- alignItems: "center",
716
- gap: "1x",
717
- inlineSize: "min 0",
718
- color: "#text",
719
- preset: { "": "h4 / strong", "@mobile": "h5 / strong" },
720
- textDecoration: "none",
721
- Logo: {
722
- $: "> svg",
723
- display: "block",
724
- flexShrink: "0",
725
- inlineSize: { "": "2rem", "@mobile": "1.75rem" },
726
- blockSize: { "": "2rem", "@mobile": "1.75rem" },
727
- color: "#accent-text",
728
- },
729
- Label: {
730
- $: "> span",
731
- inlineSize: "min 0",
732
- overflow: "hidden",
733
- textOverflow: "ellipsis",
734
- whiteSpace: "nowrap",
735
- },
736
- },
737
- });
738
- ```
739
-
740
- Then create `docs/components/SiteTitle.astro`, using an SVG whose paths use
741
- `currentColor` to follow the semantic accent color:
742
-
743
- ```astro
744
- ---
745
- import ProjectLogo from "../../public/logo.svg";
746
- import { SiteTitleRoot } from "./site-title.js";
747
-
748
- const { siteTitle, siteTitleHref } = Astro.locals.cookbookRoute;
749
- ---
750
-
751
- <SiteTitleRoot href={siteTitleHref}>
752
- <ProjectLogo aria-hidden="true" focusable="false" />
753
- <span translate="no">{siteTitle}</span>
754
- </SiteTitleRoot>
755
- ```
756
-
757
- Register the replacement in `docs.config.ts` and disable the shared mark.
758
- A `SiteTitle` override affects the header; use `site.logo` above for ordinary
759
- branding shared with the drawer. For custom drawer markup, override `Sidebar`.
760
- The custom component supports root styles plus its complete list of named
761
- sub-elements: `Logo` and `Label`.
762
-
763
- ```ts
764
- site: { logo: false },
765
- components: {
766
- overrides: {
767
- SiteTitle: "./docs/components/SiteTitle.astro"
768
- }
769
- },
770
- theme: {
771
- customStyles: {
772
- ProjectSiteTitle: { Logo: { color: "#text" } }
773
- }
774
- }
775
- ```
776
-
777
- `defineComponent(name, options)` accepts Tasty factory options and a name for
778
- `theme.customStyles[name]`. It merges the configured partial style object before
779
- creating the component, retaining the other base properties and responsive
780
- states. Tasty options such as `elements`, `variants`, `styleProps`, `modProps`,
781
- and `tokenProps` keep their behavior and inferred types, including generated
782
- subcomponents such as `Component.Label`.
783
- Configured styles become the component's defaults; variants and styles passed
784
- at render time follow Tasty's usual precedence.
785
-
786
- Use `defineComponent` when authoring a component that should support
787
- `theme.customStyles`. Use `tasty` directly for ordinary Tasty creation or composition
788
- that does not need a configuration name. To define a named component around
789
- an existing React component that forwards `className`, pass it as `as` in the options. Use `className`
790
- when passing a class to a Tasty component from Astro.
791
-
792
- ### Global style trees
793
-
794
- For markup you do not render through a Tasty component, call
795
- `useGlobalStyles()` during rendering. For example, this Astro component
796
- supports root styles and a `Label` sub-element through `theme.customStyles.ProjectNote`:
797
-
798
- ```astro
799
- ---
800
- import {
801
- resolveComponentStyles,
802
- useGlobalStyles,
803
- } from "@tenphi/cookbook/styling";
804
-
805
- useGlobalStyles(
806
- ".project-note",
807
- resolveComponentStyles("ProjectNote", {
808
- padding: "2x",
809
- fill: "#surface-2",
810
- Label: { $: "> strong", color: "#text", preset: "strong" },
811
- }),
812
- );
813
- ---
814
-
815
- <aside class="project-note"><strong>Note</strong><slot /></aside>
816
- ```
817
-
818
- `defineComponent` and `resolveComponentStyles` read the same `theme.customStyles`
819
- configuration; user configuration contains only the properties to change. They do not require a
820
- `data-tasty-anatomy` attribute. That attribute remains available for older
821
- components using the compatibility bridge described above.
822
-
823
- ## Linting custom styles
824
-
825
- `@tenphi/cookbook/eslint-plugin` re-exports the Tasty ESLint plugin together with
826
- Cookbook's `validationConfig`. The renderer offers the same exports from
827
- `@tenphi/renderer/eslint-plugin`. Keep style definitions in `.ts` or `.tsx`
828
- modules, as in the site title example above, to lint them with either ESLint or
829
- oxlint.
830
-
831
- Create `tasty.config.ts` at your project root:
832
-
833
- ```ts
834
- export default {
835
- extends: "@tenphi/cookbook",
836
- };
837
- ```
838
-
839
- Renderer-only consumers can use `extends: "@tenphi/renderer"` instead.
840
- The preset registers both Cookbook styling import paths, its built-in tokens,
841
- units, responsive states, and typography presets. It also describes
842
- `defineComponent`, `resolveComponentStyles`, and `mergeStyles`, so their inline
843
- style objects, variants, and named sub-elements receive the same checks as
844
- `tasty()` calls. Partial overrides passed to `mergeStyles` may omit default state
845
- values and retain the plugin's safeguards against fixes that replace base styles.
846
-
847
- ### ESLint
848
-
849
- Install ESLint and a TypeScript parser:
850
-
851
- ```sh
852
- pnpm add -D eslint @typescript-eslint/parser
853
- ```
854
-
855
- Add the plugin to `eslint.config.mjs`:
856
-
857
- ```js
858
- import parser from "@typescript-eslint/parser";
859
- import tasty from "@tenphi/cookbook/eslint-plugin";
860
-
861
- export default [
862
- {
863
- ...tasty.configs.recommended,
864
- files: ["**/*.{ts,tsx}"],
865
- languageOptions: { parser },
866
- },
867
- ];
868
- ```
869
-
870
- Run `pnpm exec eslint docs/components --max-warnings 0`. Use
871
- `tasty.configs.strict` for additional checks, including custom property names and
872
- runtime style values. The named `recommended` and `strict` exports contain rule
873
- maps for configurations that register the plugin themselves.
874
-
875
- ### oxlint
876
-
877
- Install oxlint:
878
-
879
- ```sh
880
- pnpm add -D oxlint
881
- ```
882
-
883
- Create `oxlint.config.mjs` using its JavaScript plugin support:
884
-
885
- ```js
886
- import { recommended } from "@tenphi/cookbook/eslint-plugin";
887
-
888
- export default {
889
- jsPlugins: [{ name: "tasty", specifier: "@tenphi/cookbook/eslint-plugin" }],
890
- rules: recommended,
891
- };
892
- ```
893
-
894
- Run `pnpm exec oxlint --config oxlint.config.mjs docs/components --deny-warnings`.
895
- The `tasty` alias keeps rule names such as `tasty/known-property` consistent with
896
- ESLint. Import `strict` instead of `recommended` to enable the stricter rule map.
897
- Both presets are tested with oxlint 1.83.0; JavaScript plugin support is experimental.
898
-
899
- ### Custom theme names
900
-
901
- Add names introduced by your `docs.config.ts` theme to the validation config.
902
- For example, after defining `theme.tokens["$project-gap"]`, `theme.states["@project-wide"]`,
903
- and `theme.presets["project-title"]`:
904
-
905
- ```ts
906
- import type { TastyValidationConfig } from "@tenphi/cookbook/eslint-plugin";
907
-
908
- export default {
909
- extends: "@tenphi/cookbook",
910
- tokens: ["$project-gap"],
911
- states: ["@project-wide"],
912
- presets: ["project-title"],
913
- } satisfies TastyValidationConfig;
914
- ```
915
-
916
- `extends` merges and deduplicates these arrays with the inherited configuration,
917
- so list only your additions. It also inherits the styling helper signatures;
918
- entries you add to `styleFunctions` override inherited signatures by function name.
919
- The exported `validationConfig` object remains available for programmatic use.
920
- The entry point also exports the upstream `StyleFunctionConfig` and
921
- `ResolvedConfig` types for shared lint configurations. Additional imported
922
- helpers can be registered in `styleFunctions`; use `partial: true` for helpers
923
- that merge overrides into existing styles.
924
-
925
- ## Static behavior
926
-
927
- Documentation content, navigation, headings, code, and images remain readable
928
- without client JavaScript. Search, mobile navigation, copy controls, appearance
929
- persistence, and executable previews can progressively enhance the static
930
- output.
931
-
932
- ### Responsive header and navigation
933
-
934
- The header uses `#header`, generated from `theme.palette.header` (or the page
935
- surface) at 70% opacity in all four appearance modes, with a 16px backdrop blur.
936
- Customize the outer bar with `theme.styles.HeaderFrame`, its contents with
937
- `Header`, and header buttons with `HeaderLinks` and `SearchButton`.
938
-
939
- Below 50rem, page navigation moves into a header row and opens a left drawer.
940
- `MobileMenuToggle.Section` and `Page` style its breadcrumb labels. The drawer
941
- contains the complete active section, a collapsible `MobileNavigationTabs`
942
- selector, and a close button. The drawer slides in and out over 120ms and
943
- disables motion when the reader prefers reduced motion. Its mobile shadow uses
944
- the Glaze `shadow`-typed `#shadow` token. Customize the movement and shadow through
945
- `theme.styles.Sidebar`, and the underlay through `Sidebar.Backdrop` and
946
- `OpenBackdrop`. `theme.palette.overlay` supplies the underlay declaration, which defaults
947
- to black at 50% opacity. Glaze resolves it in fixed mode with tone remapping
948
- disabled, so it stays black in dark and high-contrast schemes.
949
- Icon buttons retain their plain desktop appearance on mobile. More uses
950
- left-aligned navigation links with a `1bw` gap, without primary button variants.
951
- `HeaderLinks.PanelLink` styles the menu rows; `FirstPanelLink` reserves space
952
- for the close button. The inset divider above the tabs belongs to `TopNavigation`.
953
-
954
- The desktop table of contents is hidden below 72rem. Set
955
- `tableOfContents: { mobile: true }` to show an optional compact disclosure there.
956
- On desktop, the current section is marked as the page scrolls and its link stays
957
- visible in a long contents list. Customize its appearance with
958
- `theme.styles.TableOfContents.CurrentLink`.
959
- `MobileTableOfContents` owns its summary and links; `TableOfContentsLayout`
960
- controls placement. `Layout` controls shared
961
- navigation dimensions and the drawer's scroll lock; `PageFrame` styles the
962
- page container and content columns.
963
-
964
- ### Header control shape
965
-
966
- Header buttons and the mobile drawer close button use the independent
967
- `$header-control-radius` token, which defaults to `999px` for pill and circular
968
- shapes. Set it to `8px` for the same corners as the default content controls:
969
-
970
- ```ts
971
- theme: {
972
- tokens: { "$header-control-radius": "8px" }
973
- }
974
- ```
975
-
976
- `$radius` continues to control code-copy buttons, navigation items, and other
977
- content controls. Individual `theme.styles` overrides still take precedence.
978
- The mobile header divider spans the viewport while its content keeps the
979
- configured horizontal padding.
980
-
981
139
  ### Add palette colors and reference other roles
982
140
 
983
141
  Every `theme.palette` entry participates in one Glaze color graph. Use lowercase
@@ -1021,64 +179,6 @@ explicitly for a colored label; the mix/shadow dependency graph stays intact.
1021
179
  A shadow status also uses a neutral derived surface, because Glaze does not allow
1022
180
  a shadow to be a mix target. Override the surface separately when needed.
1023
181
 
1024
- ### Register units and recipes
1025
-
1026
- Register custom units and flat recipes in `theme`; Cookbook configures Tasty
1027
- before evaluating your components:
1028
-
1029
- ```ts
1030
- theme: {
1031
- brand: { from: "#315efb" },
1032
- palette: {
1033
- "review-panel": { base: "surface", tone: "-2", saturation: 0.05 },
1034
- "review-ink": { base: "review-panel", tone: 0, contrast: { wcag: [7, 10] } },
1035
- },
1036
- units: { rh: "6px" },
1037
- recipes: {
1038
- "review-panel": {
1039
- fill: "#review-panel",
1040
- color: "#review-ink",
1041
- padding: "2rh",
1042
- radius: "3px",
1043
- },
1044
- },
1045
- customStyles: { DemoBadge: { Label: { color: "#review-ink" } } },
1046
- },
1047
- ```
1048
-
1049
- ```ts
1050
- import { defineComponent } from "@tenphi/cookbook/styling";
1051
-
1052
- export const DemoBadge = defineComponent("DemoBadge", {
1053
- as: "aside",
1054
- elements: { Label: "strong" },
1055
- styles: {
1056
- display: "block",
1057
- recipe: "review-panel",
1058
- Label: { preset: "h4" },
1059
- },
1060
- });
1061
- ```
1062
-
1063
- Use `<DemoBadge><DemoBadge.Label>Review ready</DemoBadge.Label></DemoBadge>` in
1064
- MDX. Keep these styled components server-rendered: their CSS is extracted at
1065
- build time. Add behavior with a small client script targeting the rendered
1066
- markup. This recipe produces 12px padding. Units merge by name, with the later
1067
- configuration winning. Recipes merge using Tasty `mergeStyles`, including state
1068
- maps. Recipes are flat: define named sub-elements on the owning component, and
1069
- compose recipes with `recipe: "base elevated"` rather than referencing a recipe
1070
- inside another recipe. `none` is a reserved recipe name. Cookbook's built-in
1071
- units are `x`, `r`, `cr`, and `bw`; overriding one changes its meaning globally.
1072
-
1073
- Keep editor and linter validation synchronized with your configuration:
1074
-
1075
- ```ts
1076
- // tasty.config.ts
1077
- import { createValidationConfig } from "@tenphi/cookbook/eslint-plugin";
1078
- import docs from "./docs.config";
1079
- export default createValidationConfig(docs.theme);
1080
- ```
1081
-
1082
182
  ### Configure Glaze adaptation
1083
183
 
1084
184
  `theme.glaze` accepts `lightTone`, `darkTone`, `darkDesaturation`, `autoFlip`,
@@ -1125,7 +225,7 @@ directly to Tasty `#name` tokens. `COOKBOOK_PALETTE_NAMES`, exported from
1125
225
  | Additional hues | `orange`, `green`, `blue`, `purple`, `red`, each with `-text` and `-surface` variants |
1126
226
  | Syntax | `syntax-bg`, `syntax-text`, `syntax-comment`, `syntax-punctuation`, `syntax-keyword`, `syntax-string`, `syntax-token`, `syntax-property`, `syntax-number`, `syntax-function`, `syntax-value`, `syntax-operator` |
1127
227
 
1128
- ## Contrast checks and troubleshooting
228
+ ### Contrast checks and troubleshooting
1129
229
 
1130
230
  Cookbook checks text, headings, muted reading text, brand links, filled-control
1131
231
  labels, status text on its tinted surface, focus rings on each elevated
@@ -1154,69 +254,136 @@ Keep contrast requirements when adjusting colors. Extreme custom tone windows,
1154
254
  fixed colors, or opacity can prevent a requested floor from being met. A palette
1155
255
  check does not replace keyboard, screen-reader, and rendered-page testing.
1156
256
 
1157
- ## Customize shared page surfaces
257
+ ## Design tokens
258
+
259
+ Token names follow Tasty's
260
+ [token and unit syntax](./upstream/tasty/docs/dsl.md#built-in-units): `$name`
261
+ becomes the CSS custom property `--name`. Existing `--name` keys are still
262
+ accepted for compatibility.
263
+
264
+ ```ts
265
+ theme: {
266
+ tokens: {
267
+ "$gap": "0.5rem",
268
+ "$radius": "8px",
269
+ "$card-radius": "16px",
270
+ "$border-width": "1px",
271
+ "$outline-width": "2px",
272
+ "$outline-offset": "2px",
273
+ "$control-height": "2.5rem",
274
+ "$layout-width": "87.5rem",
275
+ "$content-width": "58rem",
276
+ "$sidebar-width": "17.5rem"
277
+ }
278
+ }
279
+ ```
280
+
281
+ `layout-width` caps and centers the complete documentation shell, while
282
+ `content-width` limits the reading column inside it. `radius` is the control
283
+ and navigation radius; `card-radius` is the larger surface radius. Keeping
284
+ those roles separate makes a sharp control theme or a soft card theme possible
285
+ without one-off component overrides.
286
+
287
+ ### Register units and recipes
1158
288
 
1159
- Each bridge has a complete base style tree; supply only the properties you
1160
- want to change. Sub-elements keep their selectors and all other defaults.
1161
- For example, a compact search dialog and quieter pagination need no CSS:
289
+ Register custom units and flat recipes in `theme`; Cookbook configures Tasty
290
+ before evaluating your components:
1162
291
 
1163
292
  ```ts
1164
293
  theme: {
294
+ brand: { from: "#315efb" },
295
+ palette: {
296
+ "review-panel": { base: "surface", tone: "-2", saturation: 0.05 },
297
+ "review-ink": { base: "review-panel", tone: 0, contrast: { wcag: [7, 10] } },
298
+ },
299
+ units: { rh: "6px" },
300
+ recipes: {
301
+ "review-panel": {
302
+ fill: "#review-panel",
303
+ color: "#review-ink",
304
+ padding: "2rh",
305
+ radius: "3px",
306
+ },
307
+ },
308
+ customStyles: { DemoBadge: { Label: { color: "#review-ink" } } },
309
+ },
310
+ ```
311
+
312
+ ```ts
313
+ import { defineComponent } from "@tenphi/cookbook/styling";
314
+
315
+ export const DemoBadge = defineComponent("DemoBadge", {
316
+ as: "aside",
317
+ elements: { Label: "strong" },
1165
318
  styles: {
1166
- Search: { Dialog: { inlineSize: "max 32rem" } },
1167
- SearchResults: { ResultLink: { color: "#accent-text" } },
1168
- Pagination: { Link: { padding: "1.5x", fill: "#surface" } },
1169
- Banner: { padding: "1x 2x" },
1170
- Markdown: { Quote: { color: "#text", inlinePadding: "3x start" } },
319
+ display: "block",
320
+ recipe: "review-panel",
321
+ Label: { preset: "h4" },
1171
322
  },
1172
- }
323
+ });
1173
324
  ```
1174
325
 
1175
- `Document` owns resets, base typography, generic controls, responsive media,
1176
- and accessibility utilities. `MainPane` and `MainContent` own the content
1177
- layout. `Markdown` owns prose, lists, links, quotations, and disclosure
1178
- anatomy. `Search` owns the dialog; `SearchResults` owns the Pagefind UI.
1179
- `MarkdownAlert` styles rendered alert blockquotes; `Card`, `Callout`, and
1180
- `Steps` style Cookbook components.
326
+ Use `<DemoBadge><DemoBadge.Label>Review ready</DemoBadge.Label></DemoBadge>` in
327
+ MDX. Keep these styled components server-rendered: their CSS is extracted at
328
+ build time. Add behavior with a small client script targeting the rendered
329
+ markup. This recipe produces 12px padding. Units merge by name, with the later
330
+ configuration winning. Recipes merge using Tasty `mergeStyles`, including state
331
+ maps. Recipes are flat: define named sub-elements on the owning component, and
332
+ compose recipes with `recipe: "base elevated"` rather than referencing a recipe
333
+ inside another recipe. `none` is a reserved recipe name. Cookbook's built-in
334
+ units are `x`, `r`, `cr`, and `bw`; overriding one changes its meaning globally.
1181
335
 
1182
- Custom components created with `defineComponent()` receive `theme.customStyles`
1183
- overrides during server rendering. Units, recipes, states, and presets are
1184
- resolved there and emitted as static CSS. Cookbook ships no Tasty/Glaze browser
1185
- runtime, theme configuration, or style hydration setup. Browser scripts and
1186
- `client:*` islands must not import Cookbook styling, Tasty, or Glaze; the build
1187
- rejects these imports. Attach small interaction scripts to server-rendered markup
1188
- or pass already-styled static markup into a client island.
336
+ Keep editor and linter validation synchronized with your configuration:
1189
337
 
1190
- Code fences and `CodeGroup` use the same `MarkdownCodeBlock` style tree. Its
1191
- root `$copy-button-size` defaults to `2rem`; the copy control and vertical
1192
- padding share it, keeping equal top, right, and bottom insets on one-line
1193
- snippets. The padding follows the `code` preset's line height. Override it with
1194
- `theme.styles.MarkdownCodeBlock` when changing the control size or code layout.
338
+ ```ts
339
+ // tasty.config.ts
340
+ import { createValidationConfig } from "@tenphi/cookbook/eslint-plugin";
341
+ import docs from "./docs.config";
342
+ export default createValidationConfig(docs.theme);
343
+ ```
344
+
345
+ ## Typography presets
1195
346
 
1196
- ### Syntax highlighting and isolated previews
347
+ Cookbook supplies semantic `body`, `heading`, `h1`–`h6`, `navigation`, `small`,
348
+ and `code` presets. Change their families, sizes, and weights through
349
+ `theme.fonts` and `theme.presets`. See [Fonts and typography](./fonts-and-typography.md)
350
+ for local and Google font examples, cache behavior, and preset details.
1197
351
 
1198
- Shiki classifies code; Tasty renders every token using Glaze's semantic syntax
1199
- colors. Customize token rules with `theme.styles.SyntaxHighlight`, or their
1200
- colors with `theme.palette` (for example `syntax-keyword`). The default
1201
- `syntax-bg` stays white in light mode and takes a slight brand-hued tint, close
1202
- to the page background's saturation, in dark mode. Set
1203
- `theme.palette["syntax-bg"]` to give code its own color seed;
1204
- syntax text and token contrast resolve against that surface. Code fences, including
1205
- MDX and diff blocks, use classes without inline style attributes. Custom Shiki
1206
- transformers should emit classes; unsupported inline declarations fail with a
1207
- configuration hint.
352
+ ### Change font families
353
+
354
+ Use `theme.fonts` to load Google families by name or local font files. The
355
+ [font guide](./fonts-and-typography.md#change-font-families) explains both
356
+ paths and how to select weights and styles.
1208
357
 
1209
- The production build rejects style attributes, style blocks (including inside
1210
- shadow-root templates), and non-Tasty stylesheets throughout documentation HTML.
1211
- It parses actual markup, so escaped examples and strings in scripts are allowed.
1212
- The explicit exception is the separate document in a sandboxed `Preview` iframe;
1213
- it cannot access its parent's origin. Configure its size with
1214
- `theme.styles.Preview.Frame`. The documentation shell supports the CSP directive
1215
- `style-src-attr 'none'`. A host's CSP may also restrict the preview document; when
1216
- using that directive, authored previews must use style blocks rather than style
1217
- attributes.
358
+ ## Style customization
359
+
360
+ Use a partial Tasty style object at `theme.styles.<ComponentName>` to change a
361
+ built-in surface. Cookbook merges it with the complete base style object before
362
+ CSS extraction. The [component styles reference](./component-styles.md) gives
363
+ examples and the complete list of named sub-elements for every surface.
364
+
365
+ ## Authoring custom components
366
+
367
+ Use `defineComponent()` for a configurable component root and
368
+ `resolveComponentStyles()` for an owned global style tree. Keep styling on the
369
+ server so the browser receives extracted CSS. The
370
+ [custom component guide](./custom-components.md) covers both patterns.
371
+
372
+ ### A shared site logo
373
+
374
+ For ordinary branding, set `site.logo` once for the header and mobile drawer.
375
+ Use the [logo guide](./custom-components.md#a-shared-site-logo) for image
376
+ variants, accessibility, and advanced title markup.
377
+
378
+ ## Linting custom styles
379
+
380
+ Adopt Cookbook's Tasty validation preset and register your additional names.
381
+ See [Linting custom styles](./custom-components.md#linting-custom-styles) for
382
+ ESLint and oxlint examples.
383
+
384
+ ## Static behavior
1218
385
 
1219
- Search uses an owned, keyboard-accessible dialog with Pagefind's indexing and
1220
- results UI. Its clear-button sizing is adapted to `SearchResults.Input` styles,
1221
- so the browser does not need inline layout declarations. The adapter is checked
1222
- against the pinned Pagefind UI version during builds.
386
+ The page content remains readable without client JavaScript. Search, menus,
387
+ copy controls, and appearance persistence enhance the static HTML. See
388
+ [Component styles](./component-styles.md#static-behavior) for the boundary and
389
+ responsive UI behavior.