@caltho/site-kit 0.2.9 → 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.
- package/README.md +63 -8
- package/dist/cli/check.js +31 -1
- package/dist/content/index.d.ts +318 -23
- package/dist/content/index.js +148 -15
- package/dist/content/look.d.ts +260 -9
- package/dist/content/look.js +386 -22
- package/dist/finish.js +9 -1
- package/dist/fonts/families/dm-sans/LICENSE +93 -0
- package/dist/fonts/families/dm-sans/dm-sans-latin-ext-wght-italic.woff2 +0 -0
- package/dist/fonts/families/dm-sans/dm-sans-latin-ext-wght-normal.woff2 +0 -0
- package/dist/fonts/families/dm-sans/dm-sans-latin-wght-italic.woff2 +0 -0
- package/dist/fonts/families/dm-sans/dm-sans-latin-wght-normal.woff2 +0 -0
- package/dist/fonts/families/fraunces/LICENSE +93 -0
- package/dist/fonts/families/fraunces/fraunces-latin-ext-wght-italic.woff2 +0 -0
- package/dist/fonts/families/fraunces/fraunces-latin-ext-wght-normal.woff2 +0 -0
- package/dist/fonts/families/fraunces/fraunces-latin-wght-italic.woff2 +0 -0
- package/dist/fonts/families/fraunces/fraunces-latin-wght-normal.woff2 +0 -0
- package/dist/fonts/families/inter/LICENSE +93 -0
- package/dist/fonts/families/inter/inter-latin-ext-wght-italic.woff2 +0 -0
- package/dist/fonts/families/inter/inter-latin-ext-wght-normal.woff2 +0 -0
- package/dist/fonts/families/inter/inter-latin-wght-italic.woff2 +0 -0
- package/dist/fonts/families/inter/inter-latin-wght-normal.woff2 +0 -0
- package/dist/fonts/families/lora/LICENSE +93 -0
- package/dist/fonts/families/lora/lora-latin-ext-wght-italic.woff2 +0 -0
- package/dist/fonts/families/lora/lora-latin-ext-wght-normal.woff2 +0 -0
- package/dist/fonts/families/lora/lora-latin-wght-italic.woff2 +0 -0
- package/dist/fonts/families/lora/lora-latin-wght-normal.woff2 +0 -0
- package/dist/fonts/families/manrope/LICENSE +93 -0
- package/dist/fonts/families/manrope/manrope-latin-ext-wght-normal.woff2 +0 -0
- package/dist/fonts/families/manrope/manrope-latin-wght-normal.woff2 +0 -0
- package/dist/fonts/families/newsreader/LICENSE +93 -0
- package/dist/fonts/families/newsreader/newsreader-latin-ext-wght-italic.woff2 +0 -0
- package/dist/fonts/families/newsreader/newsreader-latin-ext-wght-normal.woff2 +0 -0
- package/dist/fonts/families/newsreader/newsreader-latin-wght-italic.woff2 +0 -0
- package/dist/fonts/families/newsreader/newsreader-latin-wght-normal.woff2 +0 -0
- package/dist/fonts/families/outfit/LICENSE +93 -0
- package/dist/fonts/families/outfit/outfit-latin-ext-wght-normal.woff2 +0 -0
- package/dist/fonts/families/outfit/outfit-latin-wght-normal.woff2 +0 -0
- package/dist/fonts/families/playfair-display/LICENSE +93 -0
- package/dist/fonts/families/playfair-display/playfair-display-latin-ext-wght-italic.woff2 +0 -0
- package/dist/fonts/families/playfair-display/playfair-display-latin-ext-wght-normal.woff2 +0 -0
- package/dist/fonts/families/playfair-display/playfair-display-latin-wght-italic.woff2 +0 -0
- package/dist/fonts/families/playfair-display/playfair-display-latin-wght-normal.woff2 +0 -0
- package/dist/fonts/families/source-serif-4/LICENSE +93 -0
- package/dist/fonts/families/source-serif-4/source-serif-4-latin-ext-wght-italic.woff2 +0 -0
- package/dist/fonts/families/source-serif-4/source-serif-4-latin-ext-wght-normal.woff2 +0 -0
- package/dist/fonts/families/source-serif-4/source-serif-4-latin-wght-italic.woff2 +0 -0
- package/dist/fonts/families/source-serif-4/source-serif-4-latin-wght-normal.woff2 +0 -0
- package/dist/fonts/families/work-sans/LICENSE +93 -0
- package/dist/fonts/families/work-sans/work-sans-latin-ext-wght-italic.woff2 +0 -0
- package/dist/fonts/families/work-sans/work-sans-latin-ext-wght-normal.woff2 +0 -0
- package/dist/fonts/families/work-sans/work-sans-latin-wght-italic.woff2 +0 -0
- package/dist/fonts/families/work-sans/work-sans-latin-wght-normal.woff2 +0 -0
- package/dist/fonts/google.json +1 -0
- package/dist/fonts-google.d.ts +35 -0
- package/dist/fonts-google.js +70 -0
- package/dist/fonts.css +365 -0
- package/dist/frame.test.d.ts +1 -0
- package/dist/frame.test.js +33 -0
- package/dist/migrate.d.ts +45 -0
- package/dist/migrate.js +284 -0
- package/dist/next/block-mark.d.ts +7 -0
- package/dist/next/block-mark.js +16 -0
- package/dist/next/blocks.d.ts +11 -0
- package/dist/next/blocks.js +35 -0
- package/dist/next/data.d.ts +4 -0
- package/dist/next/data.js +4 -0
- package/dist/next/frame.d.ts +34 -0
- package/dist/next/frame.js +70 -0
- package/dist/next/index.d.ts +6 -2
- package/dist/next/index.js +7 -3
- package/dist/next/menu-group.d.ts +11 -0
- package/dist/next/menu-group.js +33 -0
- package/dist/next/parts/LookStyle.js +3 -1
- package/dist/next/preview.js +282 -25
- package/dist/next/sections.d.ts +21 -6
- package/dist/next/sections.js +167 -6
- package/dist/prepare.d.ts +5 -1
- package/dist/prepare.js +59 -1
- package/dist/preview-match.d.ts +8 -0
- package/dist/preview-match.js +33 -1
- package/dist/schema.d.ts +10 -0
- package/dist/schema.js +26 -0
- package/dist/sections/icons.d.ts +47 -0
- package/dist/sections/icons.js +51 -0
- package/dist/sections/index.d.ts +284 -2
- package/dist/sections/index.js +156 -2
- package/dist/sections/presets.d.ts +13 -0
- package/dist/sections/presets.js +300 -0
- package/dist/sections.css +1393 -9
- package/dist/sections.test.js +310 -8
- package/package.json +8 -3
package/dist/content/index.js
CHANGED
|
@@ -10,9 +10,11 @@
|
|
|
10
10
|
// them with no further work.
|
|
11
11
|
import { z } from "zod";
|
|
12
12
|
import { checkStructure, describePath } from "../schema.js";
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
-
export {
|
|
13
|
+
import { presets } from "../sections/presets.js";
|
|
14
|
+
import { buttonStyleName, COLOUR_ROLES, colourOf, isDark, lookProblems, lookSchema, swatchSlug } from "./look.js";
|
|
15
|
+
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, } from "./look.js";
|
|
16
|
+
export { fontFaceCss, fontFiles, googleFamily, googleFont, googleFonts, isFontName, servedAt } from "../fonts-google.js";
|
|
17
|
+
export { blockLabel, checkStructure, describePath } from "../schema.js";
|
|
16
18
|
export { z };
|
|
17
19
|
// Fields carry an "x-ui" hint in the JSON Schema, so CalOS draws the right control: a text area, a
|
|
18
20
|
// Markdown box, a picture picker.
|
|
@@ -33,16 +35,26 @@ export const href = z
|
|
|
33
35
|
.string()
|
|
34
36
|
.regex(/^(\/[\w\-./#?=&%]*|#[\w-]+|https?:\/\/\S+|tel:\+?[\d\s()-]+|mailto:\S+@\S+)$/, { error: HREF_HELP })
|
|
35
37
|
.meta({ "x-error": HREF_HELP });
|
|
36
|
-
export const link = z.object({
|
|
38
|
+
export const link = z.object({
|
|
39
|
+
label: text,
|
|
40
|
+
href,
|
|
41
|
+
// Main (the brand colour), quiet (an outline), or the accent colour: a button that stands out on a photo or a dark band.
|
|
42
|
+
style: z.enum(["primary", "secondary", "accent"]).optional().meta({ title: "Style", "x-labels": { primary: "Main", secondary: "Quiet", accent: "Accent colour" } }),
|
|
43
|
+
});
|
|
37
44
|
export const time = z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/, { error: "A time like 08:00 or 17:30" }).meta({ "x-error": "A time like 08:00 or 17:30" });
|
|
38
45
|
export const DAYS = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"];
|
|
39
46
|
/** What every block has: a place on the page, for links like /#contact. */
|
|
40
47
|
const blockBase = { id: z.string().regex(/^[a-z][\w-]*$/).optional() };
|
|
41
48
|
/**
|
|
42
|
-
* How a section sits on the page,
|
|
43
|
-
* width, spacing
|
|
44
|
-
* so a choice here stays sensible on a phone.
|
|
45
|
-
* `layout`, a site's choice of how it's drawn,
|
|
49
|
+
* How a section sits on the page, offered on every section (defineSite's `placement`, on unless a site
|
|
50
|
+
* turns it off): its alignment, width, spacing, the screens it shows on, and what's behind it. The
|
|
51
|
+
* site's design says what each means at every width, so a choice here stays sensible on a phone.
|
|
52
|
+
* CalOS shows it as the section's Layout. (A block's own `layout`, a site's choice of how it's drawn,
|
|
53
|
+
* is a different thing and keeps its name.)
|
|
54
|
+
*
|
|
55
|
+
* Its background is drawn by KitBlocks (../next/blocks.tsx) as a frame around the section: a colour
|
|
56
|
+
* (one of the page's, by its role, or one of the site's own, by its slug) and a photo, shaded so the
|
|
57
|
+
* words over it read.
|
|
46
58
|
*/
|
|
47
59
|
export const placement = z
|
|
48
60
|
.object({
|
|
@@ -50,9 +62,24 @@ export const placement = z
|
|
|
50
62
|
width: z.enum(["narrow", "normal", "wide"]).default("normal").meta({ title: "Width" }),
|
|
51
63
|
spacing: z.enum(["compact", "normal", "roomy"]).default("normal").meta({ title: "Spacing" }),
|
|
52
64
|
show: z.enum(["all", "desktop", "phone"]).default("all").meta({ title: "Show on" }),
|
|
65
|
+
// A line across the page's full width above it, below it or both (a strip between two rules), quiet (the Look's line colour) or strong (its text colour).
|
|
66
|
+
lines: z.enum(["none", "top", "bottom", "both"]).optional().meta({ title: "Lines", "x-labels": { none: "None", top: "Above", bottom: "Below", both: "Above and below" } }),
|
|
67
|
+
lineColour: z.enum(["quiet", "strong"]).optional().meta({ title: "Line colour", "x-labels": { quiet: "Quiet", strong: "Strong" } }),
|
|
68
|
+
background: z
|
|
69
|
+
.object({
|
|
70
|
+
color: z
|
|
71
|
+
.string()
|
|
72
|
+
.regex(/^[a-z0-9]+(-[a-z0-9]+)*$/, { error: "One of the site's colours" })
|
|
73
|
+
.optional()
|
|
74
|
+
.meta({ title: "Colour", "x-ui": "swatch", "x-error": "One of the site's colours" }),
|
|
75
|
+
photo: image.optional().meta({ title: "Photo" }),
|
|
76
|
+
overlay: z.enum(["none", "light", "dark"]).default("dark").meta({ title: "Shade over the photo" }),
|
|
77
|
+
})
|
|
78
|
+
.optional()
|
|
79
|
+
.meta({ title: "Background" }),
|
|
53
80
|
})
|
|
54
81
|
.meta({ title: "Layout" });
|
|
55
|
-
/** A section's placement as class names, only what differs from the usual: "place-align-center place-width-narrow", or "". */
|
|
82
|
+
/** 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). */
|
|
56
83
|
export function placementClass(block) {
|
|
57
84
|
const l = block.placement ?? {};
|
|
58
85
|
const usual = { align: "left", width: "normal", spacing: "normal", show: "all" };
|
|
@@ -61,6 +88,63 @@ export function placementClass(block) {
|
|
|
61
88
|
.map((k) => `place-${k}-${l[k]}`)
|
|
62
89
|
.join(" ");
|
|
63
90
|
}
|
|
91
|
+
/** The classes of the lines across a section's full width (KitBlocks wraps it in them), or "" for none. */
|
|
92
|
+
export function linesClass(block) {
|
|
93
|
+
const l = block.placement?.lines;
|
|
94
|
+
if (!l || l === "none")
|
|
95
|
+
return "";
|
|
96
|
+
return ["kit-lines", `kit-lines-${l}`, block.placement?.lineColour === "strong" ? "kit-lines-strong" : ""].filter(Boolean).join(" ");
|
|
97
|
+
}
|
|
98
|
+
/** Whether a section has something behind it (a colour or a photo), and so a frame. */
|
|
99
|
+
export function hasBackground(block) {
|
|
100
|
+
const b = block.placement?.background;
|
|
101
|
+
return Boolean(b && (b.color || b.photo?.src));
|
|
102
|
+
}
|
|
103
|
+
/** What a page's colours are when a site has no Look: a light page, its other colours dark. */
|
|
104
|
+
const NO_LOOK = { "--look-page": "#ffffff", "--look-soft": "#f3f3f3", "--look-brand": "#111111", "--look-accent": "#111111", "--look-ink": "#111111" };
|
|
105
|
+
/**
|
|
106
|
+
* Whether a section's background is dark, so its words go light: under a photo, by its shade (dark
|
|
107
|
+
* or none: light words; light: dark words); else by its colour's value in the Look (`vars`, from
|
|
108
|
+
* lookVars, at build and in the preview alike). A colour it can't find counts as light.
|
|
109
|
+
*/
|
|
110
|
+
export function backgroundIsDark(block, vars = {}) {
|
|
111
|
+
const b = block.placement?.background;
|
|
112
|
+
if (!b)
|
|
113
|
+
return false;
|
|
114
|
+
if (b.photo?.src)
|
|
115
|
+
return (b.overlay ?? "dark") !== "light";
|
|
116
|
+
if (!b.color)
|
|
117
|
+
return false;
|
|
118
|
+
const value = colourOf(b.color, { ...NO_LOOK, ...vars });
|
|
119
|
+
return value ? isDark(value) : false;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* The classes of the frame KitBlocks draws around a section with a background, or "" for one
|
|
123
|
+
* without: `kit-frame`, its colour (`kit-bg-soft`, or `kit-bg-swatch-2` for the site's second own
|
|
124
|
+
* colour, found in `vars`), `kit-frame-photo kit-overlay-dark` under a photo, and `kit-frame-dark`
|
|
125
|
+
* when its words go light (backgroundIsDark). sections.css draws them from the Look's variables.
|
|
126
|
+
*/
|
|
127
|
+
export function frameClass(block, vars = {}) {
|
|
128
|
+
if (!hasBackground(block))
|
|
129
|
+
return "";
|
|
130
|
+
const b = block.placement.background;
|
|
131
|
+
const out = ["kit-frame"];
|
|
132
|
+
if (b.color) {
|
|
133
|
+
if (COLOUR_ROLES.includes(b.color))
|
|
134
|
+
out.push(`kit-bg-${b.color}`);
|
|
135
|
+
else {
|
|
136
|
+
const value = vars[`--look-swatch-${b.color}`];
|
|
137
|
+
const n = value ? Array.from({ length: 12 }, (_, i) => i + 1).find((i) => vars[`--look-swatch-${i}`] === value) : undefined;
|
|
138
|
+
if (n)
|
|
139
|
+
out.push(`kit-bg-swatch-${n}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (b.photo?.src)
|
|
143
|
+
out.push("kit-frame-photo", `kit-overlay-${b.overlay ?? "dark"}`);
|
|
144
|
+
if (backgroundIsDark(block, vars))
|
|
145
|
+
out.push("kit-frame-dark");
|
|
146
|
+
return out.join(" ");
|
|
147
|
+
}
|
|
64
148
|
const address = z.object({
|
|
65
149
|
street: text,
|
|
66
150
|
locality: text,
|
|
@@ -111,7 +195,7 @@ const pageBase = (trailingSlash) => ({
|
|
|
111
195
|
});
|
|
112
196
|
/**
|
|
113
197
|
* The site's menu, as its header draws it: the one edited in CalOS (its links in order, one picked out,
|
|
114
|
-
* and the header's button), else the pages that have a menu label, in page order (and no button: the
|
|
198
|
+
* 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
|
|
115
199
|
* site's own default).
|
|
116
200
|
*/
|
|
117
201
|
export function menuOf(site) {
|
|
@@ -133,10 +217,11 @@ export function defineSite(blocks, o = {}) {
|
|
|
133
217
|
const names = Object.keys(blocks);
|
|
134
218
|
if (!names.length)
|
|
135
219
|
throw new Error("A site needs at least one kind of block");
|
|
220
|
+
const placed = o.placement ?? true;
|
|
136
221
|
const union = z.discriminatedUnion("type",
|
|
137
222
|
// Each kind keeps what the site said of it (its title, for CalOS to call it by).
|
|
138
223
|
names.map((type) => {
|
|
139
|
-
const kind = z.object({ type: z.literal(type), ...blockBase, ...(
|
|
224
|
+
const kind = z.object({ type: z.literal(type), ...blockBase, ...(placed ? { placement: placement.optional() } : {}) }).extend(blocks[type].shape);
|
|
140
225
|
const meta = blocks[type].meta();
|
|
141
226
|
return meta ? kind.meta(meta) : kind;
|
|
142
227
|
}));
|
|
@@ -153,8 +238,23 @@ export function defineSite(blocks, o = {}) {
|
|
|
153
238
|
/** The header's links in order, and its button: edited as one list in CalOS. Left out, the pages' menu labels are used. */
|
|
154
239
|
menu: z
|
|
155
240
|
.object({
|
|
156
|
-
links: z
|
|
157
|
-
|
|
241
|
+
links: z
|
|
242
|
+
.array(z.object({
|
|
243
|
+
label: text,
|
|
244
|
+
href,
|
|
245
|
+
emphasis: z.boolean().default(false).meta({ title: "Emphasize" }),
|
|
246
|
+
children: z.array(z.object({ label: text, href })).max(8).optional().meta({ title: "Links under it" }),
|
|
247
|
+
}))
|
|
248
|
+
.max(10),
|
|
249
|
+
button: z
|
|
250
|
+
.object({
|
|
251
|
+
label: text,
|
|
252
|
+
href,
|
|
253
|
+
// Drawn in the theme's Main (the usual), Accent or Quiet, or one of the business's own button styles.
|
|
254
|
+
style: buttonStyleName.optional().meta({ title: "Style", "x-ui": "button-style" }),
|
|
255
|
+
})
|
|
256
|
+
.optional()
|
|
257
|
+
.meta({ title: "Button" }),
|
|
158
258
|
})
|
|
159
259
|
.optional()
|
|
160
260
|
.meta({ title: "Menu" }),
|
|
@@ -172,6 +272,12 @@ export function defineSite(blocks, o = {}) {
|
|
|
172
272
|
...(o.look ? { look: lookSchema(o.look) } : {}),
|
|
173
273
|
...o.extend,
|
|
174
274
|
pages: z.array(page).min(1),
|
|
275
|
+
/** Sections the business put together and named, to add again anywhere ("Package card"): CalOS offers them beside the site's own kinds. */
|
|
276
|
+
saved: z
|
|
277
|
+
.array(z.object({ name: text.meta({ title: "Name" }), block: union }))
|
|
278
|
+
.max(30)
|
|
279
|
+
.default([])
|
|
280
|
+
.meta({ title: "Saved sections" }),
|
|
175
281
|
})
|
|
176
282
|
.superRefine((site, ctx) => {
|
|
177
283
|
const s = site;
|
|
@@ -180,6 +286,9 @@ export function defineSite(blocks, o = {}) {
|
|
|
180
286
|
if (o.look)
|
|
181
287
|
for (const i of lookProblems(s.look, o.look))
|
|
182
288
|
ctx.addIssue({ code: "custom", path: i.path, message: i.message });
|
|
289
|
+
if (placed)
|
|
290
|
+
for (const i of backgroundProblems(s, o.look))
|
|
291
|
+
ctx.addIssue({ code: "custom", path: i.path, message: i.message });
|
|
183
292
|
o.refine?.(s, ctx);
|
|
184
293
|
});
|
|
185
294
|
return {
|
|
@@ -195,9 +304,33 @@ export function defineSite(blocks, o = {}) {
|
|
|
195
304
|
csp: o.csp ?? {},
|
|
196
305
|
share: o.share,
|
|
197
306
|
look: o.look,
|
|
198
|
-
/**
|
|
199
|
-
|
|
307
|
+
/**
|
|
308
|
+
* The declaration CalOS draws its editing forms from. A site with sections of the business's own
|
|
309
|
+
* (`own`) carries the ready-made ones it can start from, each one this site's schema takes, as
|
|
310
|
+
* `x-presets`: [{ key, name, description, block }].
|
|
311
|
+
*/
|
|
312
|
+
jsonSchema: () => {
|
|
313
|
+
const json = z.toJSONSchema(schema, { io: "input", unrepresentable: "any" });
|
|
314
|
+
if (names.includes("own"))
|
|
315
|
+
json["x-presets"] = presets.filter((p) => union.safeParse(p.block).success);
|
|
316
|
+
return json;
|
|
317
|
+
},
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
/** A section's background naming a colour the site hasn't: one of the page's roles, or one of the Look's own colours by its slug. */
|
|
321
|
+
function backgroundProblems(site, look) {
|
|
322
|
+
const palette = site.look?.palette ?? look?.palette ?? [];
|
|
323
|
+
const known = new Set([...COLOUR_ROLES, ...palette.map((s) => swatchSlug(s.name))]);
|
|
324
|
+
const out = [];
|
|
325
|
+
const check = (b, at) => {
|
|
326
|
+
const color = b.placement?.background?.color;
|
|
327
|
+
if (color && !known.has(color))
|
|
328
|
+
out.push({ path: [...at, "placement", "background", "color"], message: `"${color}" isn't one of the site's colours: choose another` });
|
|
200
329
|
};
|
|
330
|
+
site.pages.forEach((p, i) => p.blocks.forEach((b, j) => check(b, ["pages", i, "blocks", j])));
|
|
331
|
+
const saved = site.saved ?? [];
|
|
332
|
+
saved.forEach((s, i) => s?.block && check(s.block, ["saved", i, "block"]));
|
|
333
|
+
return out;
|
|
201
334
|
}
|
|
202
335
|
/** Search results cut a title at about 60 characters and a description at about 160: said, not refused, since the page still works. */
|
|
203
336
|
function lengthWarnings(site) {
|
package/dist/content/look.d.ts
CHANGED
|
@@ -1,4 +1,18 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
export declare const TYPE_ROLES: readonly ["display", "h1", "h2", "h3", "body", "small"];
|
|
3
|
+
export type TypeRole = (typeof TYPE_ROLES)[number];
|
|
4
|
+
export type TypeStyle = {
|
|
5
|
+
size?: number;
|
|
6
|
+
weight?: 300 | 400 | 500 | 600 | 700;
|
|
7
|
+
tracking?: number;
|
|
8
|
+
leading?: number;
|
|
9
|
+
};
|
|
10
|
+
export type TypeScale = Partial<Record<TypeRole, TypeStyle>>;
|
|
11
|
+
export type Swatch = {
|
|
12
|
+
name: string;
|
|
13
|
+
color: string;
|
|
14
|
+
};
|
|
15
|
+
export type Space = "compact" | "normal" | "roomy";
|
|
2
16
|
export type LookOptions = {
|
|
3
17
|
/** The fonts the site carries, by the name CalOS shows → the font-family it's drawn with. */
|
|
4
18
|
fonts?: Record<string, string>;
|
|
@@ -8,19 +22,183 @@ export type LookOptions = {
|
|
|
8
22
|
/** The site's own colours, used until others are chosen. */
|
|
9
23
|
brand: string;
|
|
10
24
|
accent?: string;
|
|
11
|
-
/** The page's background, which a brand colour must read against. */
|
|
25
|
+
/** The page's background, which a brand colour must read against (`background` is the older name). */
|
|
26
|
+
page?: string;
|
|
12
27
|
background?: string;
|
|
28
|
+
/** The site's main text colour (a dark section's background). */
|
|
29
|
+
ink?: string;
|
|
30
|
+
/** Quieter text, lines and borders, a quiet section's background: worked out from page and ink when not given. */
|
|
31
|
+
muted?: string;
|
|
32
|
+
line?: string;
|
|
33
|
+
soft?: string;
|
|
34
|
+
/** Colours of the site's own, by name. */
|
|
35
|
+
palette?: Swatch[];
|
|
36
|
+
/** The site's type scale: px sizes, weights, tracking (em) and line height, by role. */
|
|
37
|
+
type?: TypeScale;
|
|
38
|
+
/** The content's width in px (800–1600). */
|
|
39
|
+
width?: number;
|
|
40
|
+
/** The space each side of the content, in px, on a wide screen (8–96; 40 unless said): half of it on a phone, growing with the screen between. A site's own sections and the kit's line up when its CSS reads --look-gutter. */
|
|
41
|
+
gutter?: number;
|
|
42
|
+
/** The space above and below each section. */
|
|
43
|
+
space?: Space;
|
|
13
44
|
corners?: Corners;
|
|
14
45
|
buttons?: Buttons;
|
|
46
|
+
/** The site's header, until the business chooses otherwise (headerOf). */
|
|
47
|
+
header?: HeaderLook;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* The header: whether it stays at the top as the page scrolls, how it's set apart from the page
|
|
51
|
+
* (a line under it, nothing, the brand colour, or a bar floating over the page), and where the
|
|
52
|
+
* name and menu sit (name left and menu right, all centred, or name and menu together on the left).
|
|
53
|
+
*/
|
|
54
|
+
export type HeaderLook = {
|
|
55
|
+
sticky?: boolean;
|
|
56
|
+
style?: HeaderStyle;
|
|
57
|
+
layout?: HeaderLayout;
|
|
58
|
+
menuShows?: MenuShows;
|
|
59
|
+
menuStyle?: string;
|
|
60
|
+
};
|
|
61
|
+
/** The phone's menu button: what it shows (its icon, the word, both); its look is a button style (menuStyle). */
|
|
62
|
+
export declare const MENU_SHOWS: readonly ["both", "icon", "word"];
|
|
63
|
+
export type MenuShows = (typeof MENU_SHOWS)[number];
|
|
64
|
+
/** A button style of the business's own: its colours by the theme's names, border, corners, size and lettering. */
|
|
65
|
+
export type ButtonStyle = {
|
|
66
|
+
name: string;
|
|
67
|
+
/** Its fill: a role, a palette colour's slug, or "none" (an outline). */
|
|
68
|
+
fill?: string;
|
|
69
|
+
/** Its words' colour: a role or palette slug; else whichever of the page and text reads on the fill. */
|
|
70
|
+
text?: string;
|
|
71
|
+
border?: {
|
|
72
|
+
width?: number;
|
|
73
|
+
color?: string;
|
|
74
|
+
};
|
|
75
|
+
/** Its corners in px, or 999 for round ends. */
|
|
76
|
+
radius?: number;
|
|
77
|
+
size?: "small" | "medium" | "large";
|
|
78
|
+
weight?: number;
|
|
79
|
+
caps?: boolean;
|
|
80
|
+
};
|
|
81
|
+
export declare const BUTTON_SIZES: readonly ["small", "medium", "large"];
|
|
82
|
+
/** How many styles of its own a site can have (the classes sections.css draws, kit-btn-1 to -8). */
|
|
83
|
+
export declare const MAX_BUTTON_STYLES = 8;
|
|
84
|
+
/** The theme's own, always there: a style of the business's can't take their names. */
|
|
85
|
+
export declare const THEME_BUTTONS: readonly ["main", "accent", "quiet"];
|
|
86
|
+
export declare const HEADER_STYLES: readonly ["line", "plain", "brand", "floating"];
|
|
87
|
+
export declare const HEADER_LAYOUTS: readonly ["spread", "centered", "left"];
|
|
88
|
+
export type HeaderStyle = (typeof HEADER_STYLES)[number];
|
|
89
|
+
export type HeaderLayout = (typeof HEADER_LAYOUTS)[number];
|
|
90
|
+
/** The header as it's drawn: what the business chose, else the site's own, else the kit's. */
|
|
91
|
+
export declare function headerOf(look: Look | undefined, o: Pick<LookOptions, "header">): Required<Omit<HeaderLook, "menuStyle">> & {
|
|
92
|
+
menuStyle?: string;
|
|
15
93
|
};
|
|
16
94
|
declare const CORNERS: readonly ["square", "soft", "round"];
|
|
17
95
|
declare const BUTTONS: readonly ["solid", "outline"];
|
|
18
96
|
type Corners = (typeof CORNERS)[number];
|
|
19
97
|
type Buttons = (typeof BUTTONS)[number];
|
|
98
|
+
/** The colours of the page any section's background may be, beside the site's own (by their slug). */
|
|
99
|
+
/**
|
|
100
|
+
* The kit's fonts (fonts.css, imported once in the site's layout): variable, every weight, by the
|
|
101
|
+
* name CalOS shows and the family it's drawn with. A site's look.ts lists them as its fonts (with
|
|
102
|
+
* any of its own), so the business picks any of them for its headings and text.
|
|
103
|
+
*/
|
|
104
|
+
export declare const KIT_FONTS: Record<string, string>;
|
|
105
|
+
export declare const COLOUR_ROLES: readonly ["page", "soft", "brand", "accent", "ink"];
|
|
106
|
+
export type ColourRole = (typeof COLOUR_ROLES)[number];
|
|
107
|
+
/** The kit's type scale, for what a site doesn't say. */
|
|
108
|
+
export declare const KIT_TYPE: Record<TypeRole, Required<TypeStyle>>;
|
|
109
|
+
/** A colour of the site's own as a CSS name: "Dark sand" → "dark-sand" (runs of anything else one dash, none at the ends). */
|
|
110
|
+
export declare function swatchSlug(name: string): string;
|
|
20
111
|
/** The Look's fields, for the site's content. */
|
|
21
112
|
export declare function lookSchema(o: LookOptions): z.ZodOptional<z.ZodObject<{
|
|
113
|
+
uploadedFonts?: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
114
|
+
family: z.ZodString;
|
|
115
|
+
files: z.ZodArray<z.ZodObject<{
|
|
116
|
+
src: z.ZodString;
|
|
117
|
+
weight: z.ZodString;
|
|
118
|
+
style: z.ZodEnum<{
|
|
119
|
+
normal: "normal";
|
|
120
|
+
italic: "italic";
|
|
121
|
+
}>;
|
|
122
|
+
}, z.core.$strip>>;
|
|
123
|
+
}, z.core.$strip>>> | undefined;
|
|
124
|
+
footer?: z.ZodDefault<z.ZodObject<{
|
|
125
|
+
background: z.ZodDefault<z.ZodString>;
|
|
126
|
+
}, z.core.$strip>> | undefined;
|
|
127
|
+
header?: z.ZodDefault<z.ZodObject<{
|
|
128
|
+
sticky: z.ZodDefault<z.ZodBoolean>;
|
|
129
|
+
style: z.ZodDefault<z.ZodEnum<{
|
|
130
|
+
line: "line";
|
|
131
|
+
plain: "plain";
|
|
132
|
+
brand: "brand";
|
|
133
|
+
floating: "floating";
|
|
134
|
+
}>>;
|
|
135
|
+
layout: z.ZodDefault<z.ZodEnum<{
|
|
136
|
+
spread: "spread";
|
|
137
|
+
centered: "centered";
|
|
138
|
+
left: "left";
|
|
139
|
+
}>>;
|
|
140
|
+
menuShows: z.ZodDefault<z.ZodEnum<{
|
|
141
|
+
both: "both";
|
|
142
|
+
icon: "icon";
|
|
143
|
+
word: "word";
|
|
144
|
+
}>>;
|
|
145
|
+
menuStyle: z.ZodOptional<z.ZodString>;
|
|
146
|
+
}, z.core.$strip>> | undefined;
|
|
22
147
|
brand: z.ZodDefault<z.ZodString>;
|
|
23
148
|
accent: z.ZodDefault<z.ZodString>;
|
|
149
|
+
page: z.ZodDefault<z.ZodString>;
|
|
150
|
+
ink: z.ZodDefault<z.ZodString>;
|
|
151
|
+
muted: z.ZodDefault<z.ZodString>;
|
|
152
|
+
line: z.ZodDefault<z.ZodString>;
|
|
153
|
+
soft: z.ZodDefault<z.ZodString>;
|
|
154
|
+
palette: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
155
|
+
name: z.ZodString;
|
|
156
|
+
color: z.ZodString;
|
|
157
|
+
}, z.core.$strip>>>;
|
|
158
|
+
type: z.ZodDefault<z.ZodObject<{
|
|
159
|
+
display: z.ZodOptional<z.ZodObject<{
|
|
160
|
+
size: z.ZodOptional<z.ZodNumber>;
|
|
161
|
+
weight: z.ZodOptional<z.ZodLiteral<400 | 500 | 600 | 700 | 300>>;
|
|
162
|
+
tracking: z.ZodOptional<z.ZodNumber>;
|
|
163
|
+
leading: z.ZodOptional<z.ZodNumber>;
|
|
164
|
+
}, z.core.$strip>>;
|
|
165
|
+
h1: z.ZodOptional<z.ZodObject<{
|
|
166
|
+
size: z.ZodOptional<z.ZodNumber>;
|
|
167
|
+
weight: z.ZodOptional<z.ZodLiteral<400 | 500 | 600 | 700 | 300>>;
|
|
168
|
+
tracking: z.ZodOptional<z.ZodNumber>;
|
|
169
|
+
leading: z.ZodOptional<z.ZodNumber>;
|
|
170
|
+
}, z.core.$strip>>;
|
|
171
|
+
h2: z.ZodOptional<z.ZodObject<{
|
|
172
|
+
size: z.ZodOptional<z.ZodNumber>;
|
|
173
|
+
weight: z.ZodOptional<z.ZodLiteral<400 | 500 | 600 | 700 | 300>>;
|
|
174
|
+
tracking: z.ZodOptional<z.ZodNumber>;
|
|
175
|
+
leading: z.ZodOptional<z.ZodNumber>;
|
|
176
|
+
}, z.core.$strip>>;
|
|
177
|
+
h3: z.ZodOptional<z.ZodObject<{
|
|
178
|
+
size: z.ZodOptional<z.ZodNumber>;
|
|
179
|
+
weight: z.ZodOptional<z.ZodLiteral<400 | 500 | 600 | 700 | 300>>;
|
|
180
|
+
tracking: z.ZodOptional<z.ZodNumber>;
|
|
181
|
+
leading: z.ZodOptional<z.ZodNumber>;
|
|
182
|
+
}, z.core.$strip>>;
|
|
183
|
+
body: z.ZodOptional<z.ZodObject<{
|
|
184
|
+
size: z.ZodOptional<z.ZodNumber>;
|
|
185
|
+
weight: z.ZodOptional<z.ZodLiteral<400 | 500 | 600 | 700 | 300>>;
|
|
186
|
+
tracking: z.ZodOptional<z.ZodNumber>;
|
|
187
|
+
leading: z.ZodOptional<z.ZodNumber>;
|
|
188
|
+
}, z.core.$strip>>;
|
|
189
|
+
small: z.ZodOptional<z.ZodObject<{
|
|
190
|
+
size: z.ZodOptional<z.ZodNumber>;
|
|
191
|
+
weight: z.ZodOptional<z.ZodLiteral<400 | 500 | 600 | 700 | 300>>;
|
|
192
|
+
tracking: z.ZodOptional<z.ZodNumber>;
|
|
193
|
+
leading: z.ZodOptional<z.ZodNumber>;
|
|
194
|
+
}, z.core.$strip>>;
|
|
195
|
+
}, z.core.$strip>>;
|
|
196
|
+
width: z.ZodDefault<z.ZodNumber>;
|
|
197
|
+
space: z.ZodDefault<z.ZodEnum<{
|
|
198
|
+
normal: "normal";
|
|
199
|
+
compact: "compact";
|
|
200
|
+
roomy: "roomy";
|
|
201
|
+
}>>;
|
|
24
202
|
corners: z.ZodDefault<z.ZodEnum<{
|
|
25
203
|
square: "square";
|
|
26
204
|
soft: "soft";
|
|
@@ -30,30 +208,103 @@ export declare function lookSchema(o: LookOptions): z.ZodOptional<z.ZodObject<{
|
|
|
30
208
|
solid: "solid";
|
|
31
209
|
outline: "outline";
|
|
32
210
|
}>>;
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
211
|
+
radius: z.ZodOptional<z.ZodNumber>;
|
|
212
|
+
buttonRadius: z.ZodOptional<z.ZodNumber>;
|
|
213
|
+
buttonStyles: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
214
|
+
name: z.ZodString;
|
|
215
|
+
fill: z.ZodOptional<z.ZodString>;
|
|
216
|
+
text: z.ZodOptional<z.ZodString>;
|
|
217
|
+
border: z.ZodOptional<z.ZodObject<{
|
|
218
|
+
width: z.ZodOptional<z.ZodNumber>;
|
|
219
|
+
color: z.ZodOptional<z.ZodString>;
|
|
220
|
+
}, z.core.$strip>>;
|
|
221
|
+
radius: z.ZodOptional<z.ZodNumber>;
|
|
222
|
+
size: z.ZodOptional<z.ZodEnum<{
|
|
223
|
+
small: "small";
|
|
224
|
+
medium: "medium";
|
|
225
|
+
large: "large";
|
|
226
|
+
}>>;
|
|
227
|
+
weight: z.ZodOptional<z.ZodNumber>;
|
|
228
|
+
caps: z.ZodOptional<z.ZodBoolean>;
|
|
229
|
+
}, z.core.$strip>>>;
|
|
230
|
+
buttonBorder: z.ZodOptional<z.ZodObject<{
|
|
231
|
+
width: z.ZodNumber;
|
|
232
|
+
color: z.ZodOptional<z.ZodString>;
|
|
233
|
+
}, z.core.$strip>>;
|
|
234
|
+
headingFont?: z.ZodDefault<z.ZodString> | undefined;
|
|
235
|
+
bodyFont?: z.ZodDefault<z.ZodString> | undefined;
|
|
236
|
+
}, z.core.$strip>>;
|
|
237
|
+
/** A button style's name, by its slug where a button names it ("main", "accent", "quiet", or one of the business's). */
|
|
238
|
+
export declare const buttonStyleName: z.ZodString;
|
|
239
|
+
declare const uploadedFonts: z.ZodArray<z.ZodObject<{
|
|
240
|
+
family: z.ZodString;
|
|
241
|
+
files: z.ZodArray<z.ZodObject<{
|
|
242
|
+
src: z.ZodString;
|
|
243
|
+
weight: z.ZodString;
|
|
244
|
+
style: z.ZodEnum<{
|
|
245
|
+
normal: "normal";
|
|
246
|
+
italic: "italic";
|
|
247
|
+
}>;
|
|
248
|
+
}, z.core.$strip>>;
|
|
39
249
|
}, z.core.$strip>>;
|
|
250
|
+
export type UploadedFont = z.infer<typeof uploadedFonts>[number];
|
|
251
|
+
/** The @font-face rules for an uploaded font, each file at the address `at` gives its path. */
|
|
252
|
+
export declare function uploadedFontCss(f: UploadedFont, at: (src: string) => string): string;
|
|
40
253
|
export type Look = {
|
|
41
254
|
headingFont?: string;
|
|
42
255
|
bodyFont?: string;
|
|
43
256
|
brand?: string;
|
|
44
257
|
accent?: string;
|
|
258
|
+
page?: string;
|
|
259
|
+
ink?: string;
|
|
260
|
+
muted?: string;
|
|
261
|
+
line?: string;
|
|
262
|
+
soft?: string;
|
|
263
|
+
palette?: Swatch[];
|
|
264
|
+
type?: TypeScale;
|
|
265
|
+
width?: number;
|
|
266
|
+
space?: Space;
|
|
45
267
|
corners?: Corners;
|
|
46
268
|
buttons?: Buttons;
|
|
269
|
+
/** Exact corners in px, past the presets: cards and pictures, and buttons (999: a pill). */
|
|
270
|
+
radius?: number;
|
|
271
|
+
buttonRadius?: number;
|
|
272
|
+
/** A border round buttons: px wide, in a role's or a palette colour's colour (else the button's own). */
|
|
273
|
+
buttonBorder?: {
|
|
274
|
+
width?: number;
|
|
275
|
+
color?: string;
|
|
276
|
+
};
|
|
277
|
+
/** Button styles of the business's own, past the theme's Main, Accent and Quiet: each button picks one by its name's slug. */
|
|
278
|
+
buttonStyles?: ButtonStyle[];
|
|
279
|
+
header?: HeaderLook;
|
|
280
|
+
/** The kit footer's background: "dark" (the site's own), a colour role, or a palette colour's slug. */
|
|
281
|
+
footer?: {
|
|
282
|
+
background?: string;
|
|
283
|
+
};
|
|
284
|
+
/** Fonts the business uploaded, each family's files (under fonts/ among the site's pictures). */
|
|
285
|
+
uploadedFonts?: UploadedFont[];
|
|
47
286
|
};
|
|
48
287
|
/** How readable one colour is on another (WCAG): 1 to 21. */
|
|
49
288
|
export declare function contrast(a: string, b: string): number;
|
|
50
|
-
/**
|
|
289
|
+
/** Whether a colour is dark: white words read better on it than near-black ones. */
|
|
290
|
+
export declare function isDark(color: string): boolean;
|
|
291
|
+
/** The colours chosen that won't read: the brand colour is used for text and buttons on the page, so it must stand out from it; text must read on it; the site's own colours need names of their own. */
|
|
51
292
|
export declare function lookProblems(look: Look | undefined, o: LookOptions): {
|
|
52
|
-
path: string[];
|
|
293
|
+
path: (string | number)[];
|
|
53
294
|
message: string;
|
|
54
295
|
}[];
|
|
55
296
|
/** The Look as CSS variables: what's chosen, else the site's own. */
|
|
56
297
|
export declare function lookVars(look: Look | undefined, o: LookOptions): Record<string, string>;
|
|
57
298
|
/** The Look as a stylesheet for the page's head: `:root { … }`. */
|
|
58
299
|
export declare function lookCss(look: Look | undefined, o: LookOptions): string;
|
|
300
|
+
/**
|
|
301
|
+
* The colour a section's background names (a role, or one of the site's own by its slug), from the
|
|
302
|
+
* Look's variables; undefined when it names none.
|
|
303
|
+
*/
|
|
304
|
+
export declare function colourOf(name: string, vars: Record<string, string>): string | undefined;
|
|
305
|
+
/**
|
|
306
|
+
* The class that draws a button in one of the business's own styles (kit-btn-3), from the Look's
|
|
307
|
+
* values (lookVars): undefined for the theme's own (main, accent, quiet) or a style it doesn't have.
|
|
308
|
+
*/
|
|
309
|
+
export declare function buttonStyleClass(style: string | undefined, vars: Record<string, string>): string | undefined;
|
|
59
310
|
export {};
|