@panphora/hyper-cms 0.11.6 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -56,7 +56,7 @@ cms.open({ rules: { title: '.title' } }) // a literal rules object, passed by h
56
56
 
57
57
  ## Auto-open with `?cms=true`
58
58
 
59
- Load any hypercms page with `?cms=true` in the URL and the CMS opens by itself once the DOM is ready — no trigger, no `cms.open()` call. The auto-open uses the host defaults (`document.body` as both `pageRoot` and `mountTo`), and it never double-mounts: if your page also calls `cms.open()`, the second open is a no-op.
59
+ Load any hypercms page with `?cms=true` in the URL and the CMS opens by itself once the DOM is ready — no trigger, no `cms.open()` call. The auto-open uses the host defaults (the whole `document` as `pageRoot`, `document.body` as `mountTo`), and it never double-mounts: if your page also calls `cms.open()`, the second open is a no-op.
60
60
 
61
61
  When the user closes the CMS, hypercms rewrites the param to `cms=false` via `history.replaceState` (the param is kept, no reload), so refreshing or sharing the post-close URL does not auto-reopen:
62
62
 
@@ -96,12 +96,25 @@ The full grammar lives in the [hyper-html-api docs](https://github.com/panphora/
96
96
  | Object array | `"products": [".product", { "name": ".name" }]` | list of cards |
97
97
  | Nested | object arrays inside object arrays | recurses |
98
98
 
99
+ ## Choosing the view
100
+
101
+ A page declares which editor it opens in, on its rules tag:
102
+
103
+ ```html
104
+ <script data-rules-name="cms" data-rules-version="1" data-cms-view="inline" type="application/json">…</script>
105
+ ```
106
+
107
+ `inline` edits text where it sits on the page and opens small popovers for everything else.
108
+ `sidebar` (also what you get with no attribute) opens the form in a side panel. The floating
109
+ toggle opens and closes whichever view the page declares; it does not switch between them.
110
+ `cms.open({ view: 'inline' })` still overrides the declaration for one session.
111
+
99
112
  ## Field controls
100
113
 
101
114
  How the sidebar picks a control for each leaf, in order:
102
115
 
103
- 1. If the page element the rule points at carries `data-hcms-component`, that control wins. Explicit beats inferred. Values: `checkbox`, `toggle`, `select`, `radio`, `textarea`, `number`, `image`, `file` on scalar fields, `chips` on list containers.
104
- 2. Otherwise a rule ending in `@src` gets the **image upload** control, and a rule ending in `@checked` gets a **checkbox**.
116
+ 1. If the page element the rule points at carries `data-hcms-component`, that control wins. Explicit beats inferred. Values: `checkbox`, `toggle`, `select`, `radio`, `textarea`, `number`, `image`, `file`, `color`, `font`, `variant` on scalar fields, `chips` on list containers.
117
+ 2. Otherwise a rule ending in `@src` gets the **image upload** control, a rule ending in `@checked` gets a **checkbox**, a rule ending in `@--color` or `@--color-<name>` gets the **color** control, and a rule ending in `@--font` or `@--font-<name>` gets the **font** control.
105
118
  3. Otherwise the rule's shape picks one of the defaults below, unless a [custom template](#custom-form-templates) overrides it.
106
119
 
107
120
  A custom `<template data-hcms-tpl>` matching the field's path always outranks a named component (the author's template is the stronger signal); when that happens the sidebar logs a `console.info` naming the shadowed component.
@@ -194,6 +207,76 @@ A "Choose" button uploads the file, writes the URL into the `a href`, and shows
194
207
 
195
208
  Use it for: a resume PDF, a downloadable spec or whitepaper, a press kit — links whose target the owner should replace by picking a file.
196
209
 
210
+ ### Page style: colors, fonts and layouts (`color`, `font`, `variant`)
211
+
212
+ These three controls change how a page looks rather than what it says. They write CSS custom
213
+ properties or attributes, and the page's own CSS decides what those do.
214
+
215
+ **The `@--name` rule form** reads and writes one CSS custom property in an element's `style`
216
+ attribute, leaving every other declaration alone:
217
+
218
+ ```html
219
+ <html style="--color-accent: #0284c7; --font-body: 'Inter', ui-sans-serif, system-ui, sans-serif">
220
+ <style>
221
+ a, .button { color: var(--color-accent); }
222
+ body { font-family: var(--font-body); }
223
+ </style>
224
+ ```
225
+
226
+ ```json
227
+ { "theme": { "accent": "html@--color-accent", "body": "html@--font-body" } }
228
+ ```
229
+
230
+ Writes through the guarded paths (`writeDocument`, `applyData`, the JSON API) accept a custom
231
+ property only when its value is plain: at most 200 characters, no `;`, braces, angle brackets or
232
+ backslash, and no `url()`, `image()` or similar functions.
233
+
234
+ **Several controls on one element.** Attributes scoped to a property name apply to that rule
235
+ only, so one element can carry a layout choice and a background color:
236
+
237
+ ```html
238
+ <section id="features" data-layout="3" style="--color-section-bg: #f8fafc"
239
+ data-hcms-component:data-layout="variant"
240
+ data-hcms-options:data-layout="1:cols-1 2:cols-2 3:cols-3"
241
+ data-hcms-options:--color-section-bg="slate-50 slate-100 sky-50 custom">
242
+ ```
243
+
244
+ `data-hcms-component:<prop>`, `data-hcms-options:<prop>` and `data-hcms-crop:<prop>` win over the
245
+ plain attribute for the rule ending in `@<prop>`. A plain attribute never applies to an `@--name`
246
+ rule.
247
+
248
+ **Color** (`color`, inferred for `@--color` and `@--color-*`). A swatch grid of the full
249
+ Tailwind palette (26 hues by 11 shades) plus a custom picker. `data-hcms-options` narrows it:
250
+ color ids (`sky-600`), whole hues (`sky`), whole shades (`500`), plain CSS colors, and `custom`
251
+ to keep the custom picker, which a narrowed list hides unless you add it. A clear button empties
252
+ the property so the page falls back to its own CSS.
253
+
254
+ **Font** (`font`, inferred for `@--font` and `@--font-*`). A list of 40 Google Fonts, each shown
255
+ in its own face, plus the system stacks. `data-hcms-options` narrows it: font slugs (`inter`),
256
+ groups (`sans`, `serif`, `display`, `mono`), and `system`. The CMS keeps one
257
+ `<link data-hcms-fonts>` in the head that loads exactly the families the page's font fields hold,
258
+ and removes it when none do.
259
+
260
+ **Variant** (`variant`, never inferred). A row of icon tiles, one per option, backed by native
261
+ radios. Each `data-hcms-options` token is `value:icon` or a bare value; the icons are `cols-1`,
262
+ `cols-2`, `cols-3`, `cols-4`, `media-left`, `media-right`, `media-top`, `list`, `cards`,
263
+ `centered`, `card-flat` and `card-raised`. Choosing one writes only the attribute, so the layout
264
+ lives in your CSS:
265
+
266
+ ```css
267
+ #features { display: grid; gap: 1.5rem; }
268
+ #features[data-layout="2"] { grid-template-columns: repeat(2, 1fr); }
269
+ #features[data-layout="3"] { grid-template-columns: repeat(3, 1fr); }
270
+ ```
271
+
272
+ **Where they show up.** In the sidebar, style fields bound to `html`, `body` or `:root` gather
273
+ in a **Page style** group at the top, and inside every other group and card the style fields
274
+ come first. In the inline view they are never hover targets: a section's style fields open from
275
+ a **Style** chip in its top-left corner, a list row's from **Style** in its gear menu, and the
276
+ page's from the **Page style** button in the bar.
277
+
278
+ Living example: `demo/theme.html`.
279
+
197
280
  ### Everything else: custom templates
198
281
 
199
282
  Anything past the built-ins — multi-select, sliders, color pickers, bespoke layouts, option labels that differ from their values — is [custom template](#custom-form-templates) territory over the same machinery. Living examples: `demo/index.html` (the `quantity` template, a wildcard-path override) and `demo/pixel-quiet.html` (the `bio` template).
@@ -287,7 +370,7 @@ cms.api.removeItem(itemPath) // drop an item by index
287
370
 
288
371
  | Option | Default | Effect |
289
372
  |---|---|---|
290
- | `pageRoot` | `document.body` | Where extract/apply run. |
373
+ | `pageRoot` | `document` | Where extract/apply run. The whole document by default, so a rule can bind `html` or `body` themselves. Events still report `document.body` as `pageRoot`. |
291
374
  | `mountTo` | `document.body` | Where the shell DOM mounts. Can be a descendant of pageRoot. |
292
375
  | `side` | `'right'` | `'right'` or `'left'`. |
293
376
  | `overlay` | `false` | If true, sidebar covers the page; if false, page content gets a padding offset. |
@@ -377,8 +460,10 @@ In hyperclayjs apps, drag works out of the box (hypercms's object-array template
377
460
 
378
461
  ## The floating toggle
379
462
 
380
- In edit mode, on a page that has a cms rules tag, hypercms injects a floating
381
- "Edit content" button in the bottom-right corner. It is runtime-only chrome: it
463
+ On a page that has a cms rules tag, hypercms injects a floating "Edit content"
464
+ button in the bottom-right corner: in edit mode, and on a ClayJS page whenever
465
+ ClayJS lets the visitor edit (in view too), where pressing it switches to edit
466
+ and then opens the CMS. It is runtime-only chrome: it
382
467
  carries `no-save`, `snapshot-remove` and `save-ignore`, so it never reaches the
383
468
  saved file and never marks the page dirty.
384
469