@caltho/site-kit 0.2.10 → 0.2.11

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 (92) hide show
  1. package/README.md +61 -12
  2. package/dist/cli/check.js +31 -1
  3. package/dist/content/index.d.ts +310 -23
  4. package/dist/content/index.js +142 -15
  5. package/dist/content/look.d.ts +260 -9
  6. package/dist/content/look.js +386 -22
  7. package/dist/finish.js +9 -1
  8. package/dist/fonts/families/dm-sans/LICENSE +93 -0
  9. package/dist/fonts/families/dm-sans/dm-sans-latin-ext-wght-italic.woff2 +0 -0
  10. package/dist/fonts/families/dm-sans/dm-sans-latin-ext-wght-normal.woff2 +0 -0
  11. package/dist/fonts/families/dm-sans/dm-sans-latin-wght-italic.woff2 +0 -0
  12. package/dist/fonts/families/dm-sans/dm-sans-latin-wght-normal.woff2 +0 -0
  13. package/dist/fonts/families/fraunces/LICENSE +93 -0
  14. package/dist/fonts/families/fraunces/fraunces-latin-ext-wght-italic.woff2 +0 -0
  15. package/dist/fonts/families/fraunces/fraunces-latin-ext-wght-normal.woff2 +0 -0
  16. package/dist/fonts/families/fraunces/fraunces-latin-wght-italic.woff2 +0 -0
  17. package/dist/fonts/families/fraunces/fraunces-latin-wght-normal.woff2 +0 -0
  18. package/dist/fonts/families/inter/LICENSE +93 -0
  19. package/dist/fonts/families/inter/inter-latin-ext-wght-italic.woff2 +0 -0
  20. package/dist/fonts/families/inter/inter-latin-ext-wght-normal.woff2 +0 -0
  21. package/dist/fonts/families/inter/inter-latin-wght-italic.woff2 +0 -0
  22. package/dist/fonts/families/inter/inter-latin-wght-normal.woff2 +0 -0
  23. package/dist/fonts/families/lora/LICENSE +93 -0
  24. package/dist/fonts/families/lora/lora-latin-ext-wght-italic.woff2 +0 -0
  25. package/dist/fonts/families/lora/lora-latin-ext-wght-normal.woff2 +0 -0
  26. package/dist/fonts/families/lora/lora-latin-wght-italic.woff2 +0 -0
  27. package/dist/fonts/families/lora/lora-latin-wght-normal.woff2 +0 -0
  28. package/dist/fonts/families/manrope/LICENSE +93 -0
  29. package/dist/fonts/families/manrope/manrope-latin-ext-wght-normal.woff2 +0 -0
  30. package/dist/fonts/families/manrope/manrope-latin-wght-normal.woff2 +0 -0
  31. package/dist/fonts/families/newsreader/LICENSE +93 -0
  32. package/dist/fonts/families/newsreader/newsreader-latin-ext-wght-italic.woff2 +0 -0
  33. package/dist/fonts/families/newsreader/newsreader-latin-ext-wght-normal.woff2 +0 -0
  34. package/dist/fonts/families/newsreader/newsreader-latin-wght-italic.woff2 +0 -0
  35. package/dist/fonts/families/newsreader/newsreader-latin-wght-normal.woff2 +0 -0
  36. package/dist/fonts/families/outfit/LICENSE +93 -0
  37. package/dist/fonts/families/outfit/outfit-latin-ext-wght-normal.woff2 +0 -0
  38. package/dist/fonts/families/outfit/outfit-latin-wght-normal.woff2 +0 -0
  39. package/dist/fonts/families/playfair-display/LICENSE +93 -0
  40. package/dist/fonts/families/playfair-display/playfair-display-latin-ext-wght-italic.woff2 +0 -0
  41. package/dist/fonts/families/playfair-display/playfair-display-latin-ext-wght-normal.woff2 +0 -0
  42. package/dist/fonts/families/playfair-display/playfair-display-latin-wght-italic.woff2 +0 -0
  43. package/dist/fonts/families/playfair-display/playfair-display-latin-wght-normal.woff2 +0 -0
  44. package/dist/fonts/families/source-serif-4/LICENSE +93 -0
  45. package/dist/fonts/families/source-serif-4/source-serif-4-latin-ext-wght-italic.woff2 +0 -0
  46. package/dist/fonts/families/source-serif-4/source-serif-4-latin-ext-wght-normal.woff2 +0 -0
  47. package/dist/fonts/families/source-serif-4/source-serif-4-latin-wght-italic.woff2 +0 -0
  48. package/dist/fonts/families/source-serif-4/source-serif-4-latin-wght-normal.woff2 +0 -0
  49. package/dist/fonts/families/work-sans/LICENSE +93 -0
  50. package/dist/fonts/families/work-sans/work-sans-latin-ext-wght-italic.woff2 +0 -0
  51. package/dist/fonts/families/work-sans/work-sans-latin-ext-wght-normal.woff2 +0 -0
  52. package/dist/fonts/families/work-sans/work-sans-latin-wght-italic.woff2 +0 -0
  53. package/dist/fonts/families/work-sans/work-sans-latin-wght-normal.woff2 +0 -0
  54. package/dist/fonts/google.json +1 -0
  55. package/dist/fonts-google.d.ts +35 -0
  56. package/dist/fonts-google.js +70 -0
  57. package/dist/fonts.css +365 -0
  58. package/dist/frame.test.d.ts +1 -0
  59. package/dist/frame.test.js +33 -0
  60. package/dist/migrate.d.ts +45 -0
  61. package/dist/migrate.js +284 -0
  62. package/dist/next/block-mark.d.ts +7 -0
  63. package/dist/next/block-mark.js +16 -0
  64. package/dist/next/blocks.d.ts +11 -0
  65. package/dist/next/blocks.js +35 -0
  66. package/dist/next/data.d.ts +4 -0
  67. package/dist/next/data.js +4 -0
  68. package/dist/next/frame.d.ts +34 -0
  69. package/dist/next/frame.js +70 -0
  70. package/dist/next/index.d.ts +6 -2
  71. package/dist/next/index.js +7 -3
  72. package/dist/next/menu-group.d.ts +11 -0
  73. package/dist/next/menu-group.js +33 -0
  74. package/dist/next/parts/LookStyle.js +3 -1
  75. package/dist/next/preview.js +282 -25
  76. package/dist/next/sections.d.ts +19 -10
  77. package/dist/next/sections.js +129 -17
  78. package/dist/prepare.d.ts +5 -1
  79. package/dist/prepare.js +59 -1
  80. package/dist/preview-match.d.ts +8 -0
  81. package/dist/preview-match.js +33 -1
  82. package/dist/schema.d.ts +10 -0
  83. package/dist/schema.js +26 -0
  84. package/dist/sections/icons.d.ts +47 -0
  85. package/dist/sections/icons.js +51 -0
  86. package/dist/sections/index.d.ts +165 -11
  87. package/dist/sections/index.js +129 -8
  88. package/dist/sections/presets.d.ts +13 -0
  89. package/dist/sections/presets.js +300 -0
  90. package/dist/sections.css +1260 -41
  91. package/dist/sections.test.js +276 -14
  92. package/package.json +8 -3
package/README.md CHANGED
@@ -45,7 +45,11 @@ export default function CalosPreview() { return <Preview origins={previewOrigins
45
45
 
46
46
  Components read content only through the kit (`site()`, `image()`), so the same components draw the
47
47
  built pages (from `.site/prepared.json`) and the draft (from CalOS). A click on the preview is matched
48
- back to the field it meant with nothing added to the components. Only the business's own CalOS
48
+ back to the field it meant with nothing added to the components. Which block a click landed in is
49
+ exact where the page puts `<BlockMark at={i} />` (from `@caltho/site-kit/next`) before each block it
50
+ draws: a block may then draw nothing (a list with nothing in it yet) or several elements. It draws
51
+ nothing on the live site. Without marks, blocks are found among `main`'s children, by their words
52
+ when there isn't one for each. Only the business's own CalOS
49
53
  addresses, which the build is given with the content, may frame the page or send it anything.
50
54
 
51
55
  Forms are separate and optional. A block can post to any address (a CalOS form, another service, or none);
@@ -62,7 +66,7 @@ the kit only checks it's a URL.
62
66
  | **Search** | Titles that fit, canonical addresses, the business as schema.org data (hours, areas, address), FAQ and breadcrumb data, robots.txt, the sitemap. |
63
67
  | **Policies** | Privacy, accessibility and terms, written from the business's facts and what the site does. Optional: a site calls `policy()` where it wants them. |
64
68
  | **Helpers for components** (`@caltho/site-kit/lib`) | The pieces that touch no files: `md` (Markdown that can't run HTML), `plain`, `policy`, the search data, `imagesIn`, `clock`, `dayRange`, `telHref`. |
65
- | **Next.js helpers** (`@caltho/site-kit/next`) | The prepared content, a page by its path, page metadata, `Img` (the prepared widths), `Md` (Markdown that can't run HTML), `JsonLd`, `previewOrigins`. The content is a store: on the server it reads `.site/prepared.json`; in the preview, what CalOS sends. |
69
+ | **Next.js helpers** (`@caltho/site-kit/next`) | The prepared content, a page by its path, page metadata, `Img` (the prepared widths), `Md` (Markdown that can't run HTML), `JsonLd`, `BlockMark` (where each block starts, for the preview), `previewOrigins`. The content is a store: on the server it reads `.site/prepared.json`; in the preview, what CalOS sends. |
66
70
  | **Preview** (`@caltho/site-kit/next/preview`) | `SitePreview`: CalOS's draft drawn with the site's own components, a click matched to its field, outlines with each section's name. |
67
71
  | **Schema** (`@caltho/site-kit/schema`) | What CalOS needs of a declaration with no site code: validate content against the JSON Schema with messages a person can act on, make a blank block or entry, list the kinds of block, and the checks across pages. |
68
72
 
@@ -74,10 +78,21 @@ them in its editor like the site's own, and Cal can add one. Today: **gallery**
74
78
  each with an optional caption, opening its largest copy; 2 to 4 across, square, landscape, portrait
75
79
  or as taken) and **slideshow** (photos one at a time, fading from one to the next, with captions).
76
80
 
77
- **Your own section** (`own`) is built in CalOS rather than in code: one to four columns (or a wide one
78
- beside a narrow one), each a list of parts (heading, text, photo, button, list, quote, facts in a row,
79
- price, photos in a grid, slideshow, space), left or centred, plain or in the brand colour. Its parts
80
- wear the site's own classes, so what's built looks like the site. One that's wanted again is saved by
81
+ **Your own section** (`own`) is built in CalOS rather than in code: a heading and intro over one to
82
+ four columns (or a wide one beside a narrow one), each a list of parts (heading, text, photo, button,
83
+ list, quote, facts in a row, price, photos in a grid, slideshow, space, table, icon, logos, video),
84
+ left or centred, plain or in the brand colour. A column can be drawn as a card (`card: true`). Its
85
+ parts wear the site's own classes, so what's built looks like the site.
86
+
87
+ - **table**: 2 to 5 columns (the first over the rows' labels, its heading may be ""), 1 to 20 rows,
88
+ a column picked out (`highlight`, counting from 1 after the labels). A cell "yes" or "no" is a tick
89
+ or a dash, read out as the word. A real table from 640px up; below, each row a small card.
90
+ - **icon**: one of the kit's line icons by name (`ICON_NAMES`: check, calendar, clock, users, …),
91
+ drawn inline in the brand colour, small, medium or large.
92
+ - **logos**: 2 to 12, in a row in grey, with a line above if it has one.
93
+ - **video**: a YouTube or Vimeo address and what the video is (its title, for a screen reader), shown
94
+ in YouTube's no-cookie player or Vimeo's with tracking off. `finish` lets the player in
95
+ (`frame-src`) only on a page that shows one, and in the preview. One that's wanted again is saved by
81
96
  name in the content's `saved` list, and CalOS offers it beside the site's own kinds.
82
97
 
83
98
  `Slideshow` (from `@caltho/site-kit/next`) is also a building block a site's own sections use: SCV's
@@ -92,17 +107,51 @@ A site takes them in once, three lines:
92
107
  import { sections } from "@caltho/site-kit/sections";
93
108
  export const blocks = { ...sections, hero, about /* … */ };
94
109
 
95
- // the site's block renderer: whatever it hasn't a component for
96
- default:
97
- return <KitSection s={block} />;
110
+ // the site's page: every block after its mark, framed when it has a background, the kit drawing
111
+ // whatever kind the site's own `render` leaves (returns null for)
112
+ <KitBlocks blocks={page.blocks} render={(block, i) => <Block s={block} />} level={(i) => (i === 0 ? 1 : 2)} />
98
113
 
99
114
  // app/layout.tsx
100
115
  import "@caltho/site-kit/sections.css";
101
116
  ```
102
117
 
103
- From then on each section the kit adds reaches the site with `cal sites kit update`. They're themed by
104
- the site's Look (`--look-*`); a site with a design of its own gives its own classes so they sit in
105
- it like its own: `<KitSection s={block} classes={{ section: "", wrap: "mx-auto max-w-5xl px-4 py-12", heading: "font-display text-3xl", text: "…", button: "…", buttonQuiet: "…" }} />`.
118
+ A section of the business's own has a background (`tone`: plain, soft, brand or dark, from the
119
+ Look's `--look-*`, with `--look-page` and `--look-ink` from the Look's `background` and `ink`), space
120
+ above and below (`spacing`: tight, normal, roomy) and can have a photo behind it (`background`, under
121
+ a scrim). On soft, brand or dark it's a card inset within the content's width in the theme's corners
122
+ (`shape: "card"`, the default) or a band across the page (`shape: "band"`). On brand, dark or a photo the kit keeps its words and buttons readable over the site's own
123
+ colour classes. `presets` (from `@caltho/site-kit/sections`) are ready-made ones (an opening, words
124
+ over a photo, three reasons, a client's words, prices, a call to get in touch, contact, common
125
+ questions, a photo and words, facts in a row, a comparison, features with icons, a logo strip, a
126
+ video), each an `own` block to add and edit like any other.
127
+
128
+ From then on each section the kit adds reaches the site with `cal sites kit update`. They're drawn
129
+ from the site's Look (`--look-*`: its type scale, colours, buttons, width and spacing) and need no
130
+ classes of the site's; a site may still give its own for any part, in place of the kit's:
131
+ `<KitBlocks … classes={{ headingLarge: "font-display text-3xl" }} />`.
132
+
133
+ ## The theme
134
+
135
+ The Look is the site's theme. Beside its fonts, brand and accent colours, corners and buttons, it has
136
+ the page's colours by role (`page`, `ink`, `muted`, `line`, `soft`), colours of the site's own by name
137
+ (`palette`), a type scale (`type`: display, h1, h2, h3, body and small, each a size in px, a weight,
138
+ tracking in em and a line height), the content's `width` and the `space` between sections. A site's
139
+ `look` options are its design as it is (the defaults CalOS shows); its CSS reads the variables
140
+ (`--look-page`, `--look-swatch-sand`, `--look-type-h2-size`, `--look-width`, `--look-section-pad`, the
141
+ full list in src/content/look.ts) so a change in CalOS reaches every page. `--look-gutter` is the
142
+ space each side of the content (the site's `gutter` option in px on a wide screen, 40 unless said,
143
+ half on a phone): a site's own wrap reads it so its sections and the kit's line up.
144
+
145
+ The menu edited in CalOS can have links under a link (`children`, up to 8, one level): `menuOf`
146
+ returns them, and the site's header draws them (the starter: a dropdown on a wide screen, indented
147
+ in the phone's menu). `blockLabel(block, kindTitle)` (`@caltho/site-kit/schema`) is what a block is
148
+ called in a list: its heading, an `own` section's first heading part, else its kind.
149
+
150
+ Every section's Layout (`placement`, on unless a site sets `placement: false`) has a **Background**: a
151
+ colour (a role, or one of the site's own by its slug) and a photo under a shade. KitBlocks draws it as
152
+ a frame around the section (`frameClass`), its words light when the colour or photo is dark
153
+ (`backgroundIsDark`, from the Look's values at build and in the preview). The schema a site writes
154
+ carries the ready-made sections its `own` kind takes, as `x-presets` at its root.
106
155
 
107
156
  ## Share cards
108
157
 
package/dist/cli/check.js CHANGED
@@ -39,7 +39,12 @@ export async function check(dir, o = {}) {
39
39
  for (const [screen, viewport] of [
40
40
  ["computer", { width: 1280, height: 800 }],
41
41
  ["phone", { width: 390, height: 844 }],
42
+ // Sizes in between, looked at for what runs past the screen's edge only.
43
+ ["tablet", { width: 768, height: 1024 }],
44
+ ["small laptop", { width: 1024, height: 768 }],
45
+ ["wide screen", { width: 1600, height: 900 }],
42
46
  ]) {
47
+ const fullCheck = screen === "computer" || screen === "phone";
43
48
  const context = await browser.newContext({ viewport });
44
49
  const page = await context.newPage();
45
50
  let where = "";
@@ -54,6 +59,31 @@ export async function check(dir, o = {}) {
54
59
  for (const path of paths) {
55
60
  where = path;
56
61
  await page.goto(base + path, { waitUntil: "networkidle" });
62
+ // Anything drawn past the screen's edge (words out of a card, a row too wide), unless a box
63
+ // around it clips what it holds on purpose (a slideshow, a scrolling row).
64
+ const over = await page.evaluate(() => {
65
+ const edge = document.documentElement.clientWidth;
66
+ const clipped = (el) => {
67
+ for (let at = el.parentElement; at && at !== document.body; at = at.parentElement) {
68
+ const c = getComputedStyle(at);
69
+ if (c.overflowX !== "visible")
70
+ return true;
71
+ }
72
+ return false;
73
+ };
74
+ return [...document.querySelectorAll("body *")]
75
+ .filter((el) => {
76
+ const r = el.getBoundingClientRect();
77
+ // Across the edge, not wholly off the screen (a field kept out of sight on purpose).
78
+ return r.width > 0 && r.height > 0 && ((r.right > edge + 1 && r.left < edge) || (r.left < -1 && r.right > 0)) && !clipped(el);
79
+ })
80
+ .slice(0, 3)
81
+ .map((el) => `${el.tagName.toLowerCase()}${el.className && typeof el.className === "string" ? `.${el.className.trim().split(/\s+/)[0]}` : ""} "${(el.textContent ?? "").trim().slice(0, 40)}"`);
82
+ });
83
+ if (over.length)
84
+ problems.push(`${path} (${screen}, ${viewport.width}px): runs past the screen's edge: ${over.join(", ")}`);
85
+ if (!fullCheck)
86
+ continue;
57
87
  // Through the browser's devtools channel: the page's own policy (rightly) blocks an injected <script>.
58
88
  await page.evaluate(axeSource);
59
89
  const result = (await page.evaluate(() =>
@@ -75,7 +105,7 @@ export async function check(dir, o = {}) {
75
105
  if (problems.length)
76
106
  log(`✗ ${dir}: ${problems.length} problem${problems.length === 1 ? "" : "s"}${list(problems)}`);
77
107
  else
78
- log(`✓ ${dir}: ${paths.length} pages, on a computer and a phone: nothing blocked or broken`);
108
+ log(`✓ ${dir}: ${paths.length} pages, on a computer and a phone: nothing blocked or broken, nothing past the screen's edge from a phone to a wide screen`);
79
109
  if (found.length)
80
110
  log(`${o.strict ? "✗" : "•"} ${dir}: accessibility, ${found.length} thing${found.length === 1 ? "" : "s"} to look at${o.strict ? "" : " (the site's to weigh; --strict fails on them)"}${list(found)}`);
81
111
  else
@@ -2,8 +2,9 @@ import type { ShareOptions } from "../share.ts";
2
2
  export type { CardNode, ShareCard, ShareFont, ShareOptions } from "../share.ts";
3
3
  import { z } from "zod";
4
4
  import { type Look, type LookOptions } from "./look.ts";
5
- export { contrast, lookCss, lookProblems, lookVars, type Look, type LookOptions } from "./look.ts";
6
- export { checkStructure, describePath, type StructureIssue } from "../schema.ts";
5
+ export { COLOUR_ROLES, colourOf, contrast, HEADER_LAYOUTS, HEADER_STYLES, headerOf, isDark, KIT_FONTS, KIT_TYPE, lookCss, lookProblems, lookVars, swatchSlug, TYPE_ROLES, uploadedFontCss, buttonStyleClass, BUTTON_SIZES, MAX_BUTTON_STYLES, THEME_BUTTONS, type ButtonStyle, type ColourRole, type HeaderLayout, type HeaderLook, type HeaderStyle, type Look, type LookOptions, type Space, type Swatch, type TypeRole, type TypeScale, type TypeStyle, type UploadedFont, } from "./look.ts";
6
+ export { fontFaceCss, fontFiles, googleFamily, googleFont, googleFonts, isFontName, servedAt, type FontFile, type FontKind, type GoogleFont } from "../fonts-google.ts";
7
+ export { blockLabel, checkStructure, describePath, type StructureIssue } from "../schema.ts";
7
8
  export { z };
8
9
  export declare const text: z.ZodString;
9
10
  /** Longer plain text: drawn as a text area. */
@@ -22,6 +23,7 @@ export declare const link: z.ZodObject<{
22
23
  label: z.ZodString;
23
24
  href: z.ZodString;
24
25
  style: z.ZodOptional<z.ZodEnum<{
26
+ accent: "accent";
25
27
  primary: "primary";
26
28
  secondary: "secondary";
27
29
  }>>;
@@ -29,10 +31,15 @@ export declare const link: z.ZodObject<{
29
31
  export declare const time: z.ZodString;
30
32
  export declare const DAYS: readonly ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"];
31
33
  /**
32
- * How a section sits on the page, for a site that offers it (defineSite's `placement`): its alignment,
33
- * width, spacing and the screens it shows on. The site's design says what each means at every width,
34
- * so a choice here stays sensible on a phone. CalOS shows it as the section's Layout. (A block's own
35
- * `layout`, a site's choice of how it's drawn, is a different thing and keeps its name.)
34
+ * How a section sits on the page, offered on every section (defineSite's `placement`, on unless a site
35
+ * turns it off): its alignment, width, spacing, the screens it shows on, and what's behind it. The
36
+ * site's design says what each means at every width, so a choice here stays sensible on a phone.
37
+ * CalOS shows it as the section's Layout. (A block's own `layout`, a site's choice of how it's drawn,
38
+ * is a different thing and keeps its name.)
39
+ *
40
+ * Its background is drawn by KitBlocks (../next/blocks.tsx) as a frame around the section: a colour
41
+ * (one of the page's, by its role, or one of the site's own, by its slug) and a photo, shaded so the
42
+ * words over it read.
36
43
  */
37
44
  export declare const placement: z.ZodObject<{
38
45
  align: z.ZodDefault<z.ZodEnum<{
@@ -41,8 +48,8 @@ export declare const placement: z.ZodObject<{
41
48
  }>>;
42
49
  width: z.ZodDefault<z.ZodEnum<{
43
50
  normal: "normal";
44
- narrow: "narrow";
45
51
  wide: "wide";
52
+ narrow: "narrow";
46
53
  }>>;
47
54
  spacing: z.ZodDefault<z.ZodEnum<{
48
55
  normal: "normal";
@@ -54,12 +61,60 @@ export declare const placement: z.ZodObject<{
54
61
  all: "all";
55
62
  desktop: "desktop";
56
63
  }>>;
64
+ lines: z.ZodOptional<z.ZodEnum<{
65
+ both: "both";
66
+ none: "none";
67
+ top: "top";
68
+ bottom: "bottom";
69
+ }>>;
70
+ lineColour: z.ZodOptional<z.ZodEnum<{
71
+ quiet: "quiet";
72
+ strong: "strong";
73
+ }>>;
74
+ background: z.ZodOptional<z.ZodObject<{
75
+ color: z.ZodOptional<z.ZodString>;
76
+ photo: z.ZodOptional<z.ZodObject<{
77
+ src: z.ZodString;
78
+ alt: z.ZodString;
79
+ }, z.core.$strip>>;
80
+ overlay: z.ZodDefault<z.ZodEnum<{
81
+ dark: "dark";
82
+ light: "light";
83
+ none: "none";
84
+ }>>;
85
+ }, z.core.$strip>>;
57
86
  }, z.core.$strip>;
58
87
  export type Placement = z.output<typeof placement>;
59
- /** A section's placement as class names, only what differs from the usual: "place-align-center place-width-narrow", or "". */
88
+ export type Background = NonNullable<Placement["background"]>;
89
+ /** A section's placement as class names, only what differs from the usual: "place-align-center place-width-narrow", or "". Its background is the frame's (frameClass). */
60
90
  export declare function placementClass(block: {
61
91
  placement?: Partial<Placement>;
62
92
  }): string;
93
+ /** The classes of the lines across a section's full width (KitBlocks wraps it in them), or "" for none. */
94
+ export declare function linesClass(block: {
95
+ placement?: Partial<Placement>;
96
+ }): string;
97
+ /** Whether a section has something behind it (a colour or a photo), and so a frame. */
98
+ export declare function hasBackground(block: {
99
+ placement?: Partial<Placement>;
100
+ }): boolean;
101
+ /**
102
+ * Whether a section's background is dark, so its words go light: under a photo, by its shade (dark
103
+ * or none: light words; light: dark words); else by its colour's value in the Look (`vars`, from
104
+ * lookVars, at build and in the preview alike). A colour it can't find counts as light.
105
+ */
106
+ export declare function backgroundIsDark(block: {
107
+ placement?: Partial<Placement>;
108
+ }, vars?: Record<string, string>): boolean;
109
+ /**
110
+ * The classes of the frame KitBlocks draws around a section with a background, or "" for one
111
+ * without: `kit-frame`, its colour (`kit-bg-soft`, or `kit-bg-swatch-2` for the site's second own
112
+ * colour, found in `vars`), `kit-frame-photo kit-overlay-dark` under a photo, and `kit-frame-dark`
113
+ * when its words go light (backgroundIsDark). sections.css draws them from the Look's variables.
114
+ */
115
+ export declare function frameClass(block: {
116
+ placement?: Partial<Placement>;
117
+ }, vars?: Record<string, string>): string;
63
118
  /** The business's facts: the header, footer, hours, contact details and search data all come from here. */
64
119
  export declare const business: z.ZodObject<{
65
120
  name: z.ZodString;
@@ -137,16 +192,22 @@ export type Redirect = {
137
192
  status: 301 | 302;
138
193
  };
139
194
  /** What every site has, whatever its blocks. */
195
+ /** A link in the menu; `children` (up to 8, one level) open under it: a dropdown on a wide screen, indented in a phone's menu. */
140
196
  export type MenuLink = {
141
197
  label: string;
142
198
  href: string;
143
199
  emphasis?: boolean;
200
+ children?: {
201
+ label: string;
202
+ href: string;
203
+ }[];
144
204
  };
145
205
  export type Menu = {
146
206
  links: MenuLink[];
147
207
  button?: {
148
208
  label: string;
149
209
  href: string;
210
+ style?: string;
150
211
  };
151
212
  };
152
213
  export type FooterLink = {
@@ -175,7 +236,7 @@ export type SiteBase = {
175
236
  };
176
237
  /**
177
238
  * The site's menu, as its header draws it: the one edited in CalOS (its links in order, one picked out,
178
- * and the header's button), else the pages that have a menu label, in page order (and no button: the
239
+ * each with the links under it if it has any, and the header's button), else the pages that have a menu label, in page order (and no button: the
179
240
  * site's own default).
180
241
  */
181
242
  export declare function menuOf(site: Pick<Site, "pages" | "menu">): Menu;
@@ -217,7 +278,7 @@ export type DefineOptions = {
217
278
  refine?: (site: Site, ctx: z.RefinementCtx) => void;
218
279
  /** How each page's share card looks (colours, fonts, a logo, or a card of the site's own); `false` for none (../share.ts). */
219
280
  share?: ShareOptions;
220
- /** Every section gets a Layout (placement: alignment, width, spacing, screens), drawn by the site through placementClass. */
281
+ /** Every section gets a Layout (placement: alignment, width, spacing, screens, background), drawn by the site through placementClass and KitBlocks. On unless `false`. */
221
282
  placement?: boolean;
222
283
  /** The site's Look, chosen in CalOS (fonts from the ones it carries, colours, corners, buttons), drawn through CSS variables (./look.ts). */
223
284
  look?: LookOptions;
@@ -247,8 +308,95 @@ export declare function defineSite<const B extends Record<string, z.ZodObject>>(
247
308
  block: z.ZodDiscriminatedUnion<[z.ZodObject<z.core.$ZodLooseShape, z.core.$strip>, ...z.ZodObject<z.core.$ZodLooseShape, z.core.$strip>[]], "type">;
248
309
  }, z.core.$strip>>>;
249
310
  look?: z.ZodOptional<z.ZodObject<{
311
+ uploadedFonts?: z.ZodOptional<z.ZodArray<z.ZodObject<{
312
+ family: z.ZodString;
313
+ files: z.ZodArray<z.ZodObject<{
314
+ src: z.ZodString;
315
+ weight: z.ZodString;
316
+ style: z.ZodEnum<{
317
+ normal: "normal";
318
+ italic: "italic";
319
+ }>;
320
+ }, z.core.$strip>>;
321
+ }, z.core.$strip>>> | undefined;
322
+ footer?: z.ZodDefault<z.ZodObject<{
323
+ background: z.ZodDefault<z.ZodString>;
324
+ }, z.core.$strip>> | undefined;
325
+ header?: z.ZodDefault<z.ZodObject<{
326
+ sticky: z.ZodDefault<z.ZodBoolean>;
327
+ style: z.ZodDefault<z.ZodEnum<{
328
+ line: "line";
329
+ plain: "plain";
330
+ brand: "brand";
331
+ floating: "floating";
332
+ }>>;
333
+ layout: z.ZodDefault<z.ZodEnum<{
334
+ spread: "spread";
335
+ centered: "centered";
336
+ left: "left";
337
+ }>>;
338
+ menuShows: z.ZodDefault<z.ZodEnum<{
339
+ both: "both";
340
+ icon: "icon";
341
+ word: "word";
342
+ }>>;
343
+ menuStyle: z.ZodOptional<z.ZodString>;
344
+ }, z.core.$strip>> | undefined;
250
345
  brand: z.ZodDefault<z.ZodString>;
251
346
  accent: z.ZodDefault<z.ZodString>;
347
+ page: z.ZodDefault<z.ZodString>;
348
+ ink: z.ZodDefault<z.ZodString>;
349
+ muted: z.ZodDefault<z.ZodString>;
350
+ line: z.ZodDefault<z.ZodString>;
351
+ soft: z.ZodDefault<z.ZodString>;
352
+ palette: z.ZodDefault<z.ZodArray<z.ZodObject<{
353
+ name: z.ZodString;
354
+ color: z.ZodString;
355
+ }, z.core.$strip>>>;
356
+ type: z.ZodDefault<z.ZodObject<{
357
+ display: z.ZodOptional<z.ZodObject<{
358
+ size: z.ZodOptional<z.ZodNumber>;
359
+ weight: z.ZodOptional<z.ZodLiteral<400 | 500 | 600 | 700 | 300>>;
360
+ tracking: z.ZodOptional<z.ZodNumber>;
361
+ leading: z.ZodOptional<z.ZodNumber>;
362
+ }, z.core.$strip>>;
363
+ h1: z.ZodOptional<z.ZodObject<{
364
+ size: z.ZodOptional<z.ZodNumber>;
365
+ weight: z.ZodOptional<z.ZodLiteral<400 | 500 | 600 | 700 | 300>>;
366
+ tracking: z.ZodOptional<z.ZodNumber>;
367
+ leading: z.ZodOptional<z.ZodNumber>;
368
+ }, z.core.$strip>>;
369
+ h2: z.ZodOptional<z.ZodObject<{
370
+ size: z.ZodOptional<z.ZodNumber>;
371
+ weight: z.ZodOptional<z.ZodLiteral<400 | 500 | 600 | 700 | 300>>;
372
+ tracking: z.ZodOptional<z.ZodNumber>;
373
+ leading: z.ZodOptional<z.ZodNumber>;
374
+ }, z.core.$strip>>;
375
+ h3: z.ZodOptional<z.ZodObject<{
376
+ size: z.ZodOptional<z.ZodNumber>;
377
+ weight: z.ZodOptional<z.ZodLiteral<400 | 500 | 600 | 700 | 300>>;
378
+ tracking: z.ZodOptional<z.ZodNumber>;
379
+ leading: z.ZodOptional<z.ZodNumber>;
380
+ }, z.core.$strip>>;
381
+ body: z.ZodOptional<z.ZodObject<{
382
+ size: z.ZodOptional<z.ZodNumber>;
383
+ weight: z.ZodOptional<z.ZodLiteral<400 | 500 | 600 | 700 | 300>>;
384
+ tracking: z.ZodOptional<z.ZodNumber>;
385
+ leading: z.ZodOptional<z.ZodNumber>;
386
+ }, z.core.$strip>>;
387
+ small: z.ZodOptional<z.ZodObject<{
388
+ size: z.ZodOptional<z.ZodNumber>;
389
+ weight: z.ZodOptional<z.ZodLiteral<400 | 500 | 600 | 700 | 300>>;
390
+ tracking: z.ZodOptional<z.ZodNumber>;
391
+ leading: z.ZodOptional<z.ZodNumber>;
392
+ }, z.core.$strip>>;
393
+ }, z.core.$strip>>;
394
+ width: z.ZodDefault<z.ZodNumber>;
395
+ space: z.ZodDefault<z.ZodEnum<{
396
+ normal: "normal";
397
+ compact: "compact";
398
+ roomy: "roomy";
399
+ }>>;
252
400
  corners: z.ZodDefault<z.ZodEnum<{
253
401
  square: "square";
254
402
  soft: "soft";
@@ -258,12 +406,31 @@ export declare function defineSite<const B extends Record<string, z.ZodObject>>(
258
406
  solid: "solid";
259
407
  outline: "outline";
260
408
  }>>;
261
- headingFont?: z.ZodDefault<z.ZodEnum<{
262
- [x: string]: string;
263
- }>> | undefined;
264
- bodyFont?: z.ZodDefault<z.ZodEnum<{
265
- [x: string]: string;
266
- }>> | undefined;
409
+ radius: z.ZodOptional<z.ZodNumber>;
410
+ buttonRadius: z.ZodOptional<z.ZodNumber>;
411
+ buttonStyles: z.ZodOptional<z.ZodArray<z.ZodObject<{
412
+ name: z.ZodString;
413
+ fill: z.ZodOptional<z.ZodString>;
414
+ text: z.ZodOptional<z.ZodString>;
415
+ border: z.ZodOptional<z.ZodObject<{
416
+ width: z.ZodOptional<z.ZodNumber>;
417
+ color: z.ZodOptional<z.ZodString>;
418
+ }, z.core.$strip>>;
419
+ radius: z.ZodOptional<z.ZodNumber>;
420
+ size: z.ZodOptional<z.ZodEnum<{
421
+ small: "small";
422
+ medium: "medium";
423
+ large: "large";
424
+ }>>;
425
+ weight: z.ZodOptional<z.ZodNumber>;
426
+ caps: z.ZodOptional<z.ZodBoolean>;
427
+ }, z.core.$strip>>>;
428
+ buttonBorder: z.ZodOptional<z.ZodObject<{
429
+ width: z.ZodNumber;
430
+ color: z.ZodOptional<z.ZodString>;
431
+ }, z.core.$strip>>;
432
+ headingFont?: z.ZodDefault<z.ZodString> | undefined;
433
+ bodyFont?: z.ZodDefault<z.ZodString> | undefined;
267
434
  }, z.core.$strip>> | undefined;
268
435
  url: z.ZodURL;
269
436
  lang: z.ZodDefault<z.ZodString>;
@@ -320,10 +487,15 @@ export declare function defineSite<const B extends Record<string, z.ZodObject>>(
320
487
  label: z.ZodString;
321
488
  href: z.ZodString;
322
489
  emphasis: z.ZodDefault<z.ZodBoolean>;
490
+ children: z.ZodOptional<z.ZodArray<z.ZodObject<{
491
+ label: z.ZodString;
492
+ href: z.ZodString;
493
+ }, z.core.$strip>>>;
323
494
  }, z.core.$strip>>;
324
495
  button: z.ZodOptional<z.ZodObject<{
325
496
  label: z.ZodString;
326
497
  href: z.ZodString;
498
+ style: z.ZodOptional<z.ZodString>;
327
499
  }, z.core.$strip>>;
328
500
  }, z.core.$strip>>;
329
501
  footer: z.ZodOptional<z.ZodObject<{
@@ -368,8 +540,95 @@ export declare function defineSite<const B extends Record<string, z.ZodObject>>(
368
540
  block: z.ZodDiscriminatedUnion<[z.ZodObject<z.core.$ZodLooseShape, z.core.$strip>, ...z.ZodObject<z.core.$ZodLooseShape, z.core.$strip>[]], "type">;
369
541
  }, z.core.$strip>>>;
370
542
  look?: z.ZodOptional<z.ZodObject<{
543
+ uploadedFonts?: z.ZodOptional<z.ZodArray<z.ZodObject<{
544
+ family: z.ZodString;
545
+ files: z.ZodArray<z.ZodObject<{
546
+ src: z.ZodString;
547
+ weight: z.ZodString;
548
+ style: z.ZodEnum<{
549
+ normal: "normal";
550
+ italic: "italic";
551
+ }>;
552
+ }, z.core.$strip>>;
553
+ }, z.core.$strip>>> | undefined;
554
+ footer?: z.ZodDefault<z.ZodObject<{
555
+ background: z.ZodDefault<z.ZodString>;
556
+ }, z.core.$strip>> | undefined;
557
+ header?: z.ZodDefault<z.ZodObject<{
558
+ sticky: z.ZodDefault<z.ZodBoolean>;
559
+ style: z.ZodDefault<z.ZodEnum<{
560
+ line: "line";
561
+ plain: "plain";
562
+ brand: "brand";
563
+ floating: "floating";
564
+ }>>;
565
+ layout: z.ZodDefault<z.ZodEnum<{
566
+ spread: "spread";
567
+ centered: "centered";
568
+ left: "left";
569
+ }>>;
570
+ menuShows: z.ZodDefault<z.ZodEnum<{
571
+ both: "both";
572
+ icon: "icon";
573
+ word: "word";
574
+ }>>;
575
+ menuStyle: z.ZodOptional<z.ZodString>;
576
+ }, z.core.$strip>> | undefined;
371
577
  brand: z.ZodDefault<z.ZodString>;
372
578
  accent: z.ZodDefault<z.ZodString>;
579
+ page: z.ZodDefault<z.ZodString>;
580
+ ink: z.ZodDefault<z.ZodString>;
581
+ muted: z.ZodDefault<z.ZodString>;
582
+ line: z.ZodDefault<z.ZodString>;
583
+ soft: z.ZodDefault<z.ZodString>;
584
+ palette: z.ZodDefault<z.ZodArray<z.ZodObject<{
585
+ name: z.ZodString;
586
+ color: z.ZodString;
587
+ }, z.core.$strip>>>;
588
+ type: z.ZodDefault<z.ZodObject<{
589
+ display: z.ZodOptional<z.ZodObject<{
590
+ size: z.ZodOptional<z.ZodNumber>;
591
+ weight: z.ZodOptional<z.ZodLiteral<400 | 500 | 600 | 700 | 300>>;
592
+ tracking: z.ZodOptional<z.ZodNumber>;
593
+ leading: z.ZodOptional<z.ZodNumber>;
594
+ }, z.core.$strip>>;
595
+ h1: z.ZodOptional<z.ZodObject<{
596
+ size: z.ZodOptional<z.ZodNumber>;
597
+ weight: z.ZodOptional<z.ZodLiteral<400 | 500 | 600 | 700 | 300>>;
598
+ tracking: z.ZodOptional<z.ZodNumber>;
599
+ leading: z.ZodOptional<z.ZodNumber>;
600
+ }, z.core.$strip>>;
601
+ h2: z.ZodOptional<z.ZodObject<{
602
+ size: z.ZodOptional<z.ZodNumber>;
603
+ weight: z.ZodOptional<z.ZodLiteral<400 | 500 | 600 | 700 | 300>>;
604
+ tracking: z.ZodOptional<z.ZodNumber>;
605
+ leading: z.ZodOptional<z.ZodNumber>;
606
+ }, z.core.$strip>>;
607
+ h3: z.ZodOptional<z.ZodObject<{
608
+ size: z.ZodOptional<z.ZodNumber>;
609
+ weight: z.ZodOptional<z.ZodLiteral<400 | 500 | 600 | 700 | 300>>;
610
+ tracking: z.ZodOptional<z.ZodNumber>;
611
+ leading: z.ZodOptional<z.ZodNumber>;
612
+ }, z.core.$strip>>;
613
+ body: z.ZodOptional<z.ZodObject<{
614
+ size: z.ZodOptional<z.ZodNumber>;
615
+ weight: z.ZodOptional<z.ZodLiteral<400 | 500 | 600 | 700 | 300>>;
616
+ tracking: z.ZodOptional<z.ZodNumber>;
617
+ leading: z.ZodOptional<z.ZodNumber>;
618
+ }, z.core.$strip>>;
619
+ small: z.ZodOptional<z.ZodObject<{
620
+ size: z.ZodOptional<z.ZodNumber>;
621
+ weight: z.ZodOptional<z.ZodLiteral<400 | 500 | 600 | 700 | 300>>;
622
+ tracking: z.ZodOptional<z.ZodNumber>;
623
+ leading: z.ZodOptional<z.ZodNumber>;
624
+ }, z.core.$strip>>;
625
+ }, z.core.$strip>>;
626
+ width: z.ZodDefault<z.ZodNumber>;
627
+ space: z.ZodDefault<z.ZodEnum<{
628
+ normal: "normal";
629
+ compact: "compact";
630
+ roomy: "roomy";
631
+ }>>;
373
632
  corners: z.ZodDefault<z.ZodEnum<{
374
633
  square: "square";
375
634
  soft: "soft";
@@ -379,12 +638,31 @@ export declare function defineSite<const B extends Record<string, z.ZodObject>>(
379
638
  solid: "solid";
380
639
  outline: "outline";
381
640
  }>>;
382
- headingFont?: z.ZodDefault<z.ZodEnum<{
383
- [x: string]: string;
384
- }>> | undefined;
385
- bodyFont?: z.ZodDefault<z.ZodEnum<{
386
- [x: string]: string;
387
- }>> | undefined;
641
+ radius: z.ZodOptional<z.ZodNumber>;
642
+ buttonRadius: z.ZodOptional<z.ZodNumber>;
643
+ buttonStyles: z.ZodOptional<z.ZodArray<z.ZodObject<{
644
+ name: z.ZodString;
645
+ fill: z.ZodOptional<z.ZodString>;
646
+ text: z.ZodOptional<z.ZodString>;
647
+ border: z.ZodOptional<z.ZodObject<{
648
+ width: z.ZodOptional<z.ZodNumber>;
649
+ color: z.ZodOptional<z.ZodString>;
650
+ }, z.core.$strip>>;
651
+ radius: z.ZodOptional<z.ZodNumber>;
652
+ size: z.ZodOptional<z.ZodEnum<{
653
+ small: "small";
654
+ medium: "medium";
655
+ large: "large";
656
+ }>>;
657
+ weight: z.ZodOptional<z.ZodNumber>;
658
+ caps: z.ZodOptional<z.ZodBoolean>;
659
+ }, z.core.$strip>>>;
660
+ buttonBorder: z.ZodOptional<z.ZodObject<{
661
+ width: z.ZodNumber;
662
+ color: z.ZodOptional<z.ZodString>;
663
+ }, z.core.$strip>>;
664
+ headingFont?: z.ZodDefault<z.ZodString> | undefined;
665
+ bodyFont?: z.ZodDefault<z.ZodString> | undefined;
388
666
  }, z.core.$strip>> | undefined;
389
667
  url: z.ZodURL;
390
668
  lang: z.ZodDefault<z.ZodString>;
@@ -441,10 +719,15 @@ export declare function defineSite<const B extends Record<string, z.ZodObject>>(
441
719
  label: z.ZodString;
442
720
  href: z.ZodString;
443
721
  emphasis: z.ZodDefault<z.ZodBoolean>;
722
+ children: z.ZodOptional<z.ZodArray<z.ZodObject<{
723
+ label: z.ZodString;
724
+ href: z.ZodString;
725
+ }, z.core.$strip>>>;
444
726
  }, z.core.$strip>>;
445
727
  button: z.ZodOptional<z.ZodObject<{
446
728
  label: z.ZodString;
447
729
  href: z.ZodString;
730
+ style: z.ZodOptional<z.ZodString>;
448
731
  }, z.core.$strip>>;
449
732
  }, z.core.$strip>>;
450
733
  footer: z.ZodOptional<z.ZodObject<{
@@ -480,7 +763,11 @@ export declare function defineSite<const B extends Record<string, z.ZodObject>>(
480
763
  csp: Record<string, string[]>;
481
764
  share: ShareOptions | undefined;
482
765
  look: LookOptions | undefined;
483
- /** The declaration CalOS draws its editing forms from. */
766
+ /**
767
+ * The declaration CalOS draws its editing forms from. A site with sections of the business's own
768
+ * (`own`) carries the ready-made ones it can start from, each one this site's schema takes, as
769
+ * `x-presets`: [{ key, name, description, block }].
770
+ */
484
771
  jsonSchema: () => Record<string, unknown>;
485
772
  };
486
773
  export type SiteDefinition = ReturnType<typeof defineSite>;