@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 +91 -6
- package/dist/hypercms.min.js +169 -84
- package/package.json +5 -3
- package/src/apply-loop.js +3 -2
- package/src/catalogs/colors.js +293 -0
- package/src/catalogs/fonts.js +126 -0
- package/src/catalogs/index.js +3 -0
- package/src/catalogs/variant-icons.js +24 -0
- package/src/color-control.js +196 -0
- package/src/events.js +25 -2
- package/src/font-control.js +142 -0
- package/src/fonts-link.js +83 -0
- package/src/form-builder.js +85 -3
- package/src/form-rules.js +48 -3
- package/src/hypercms.js +111 -21
- package/src/page-root.js +28 -0
- package/src/refresh.js +4 -1
- package/src/session.js +51 -3
- package/src/targets.js +55 -13
- package/src/templates.js +100 -6
- package/src/theme/pixel-quiet.overrides.css +91 -0
- package/src/theme.generated.css +91 -0
- package/src/toggle.js +80 -296
- package/src/variant-control.js +46 -0
- package/src/views/inline-layer.js +71 -15
- package/src/views/inline.js +87 -17
package/src/templates.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { ruleAttrIndex } from 'hyper-html-api/engine'
|
|
2
2
|
import { getRuleAtPath } from './path.js'
|
|
3
3
|
|
|
4
|
+
// --font-* names that hold something other than a family (Tailwind 4 uses
|
|
5
|
+
// --font-weight-*, many pages use --font-size-*), so they never get the font menu.
|
|
6
|
+
const NOT_A_FAMILY = /^--font-(size|weight|style|stretch|variant|feature-settings|variation-settings|optical-sizing|kerning|synthesis|smoothing|palette|size-adjust|display)(-|$)/
|
|
7
|
+
|
|
4
8
|
export function humanize(key) {
|
|
5
9
|
return key
|
|
6
10
|
.replace(/([a-z])([A-Z])/g, '$1 $2')
|
|
@@ -194,6 +198,59 @@ const DEFAULT_TEMPLATES = {
|
|
|
194
198
|
<div class="hcms-error" hidden></div>
|
|
195
199
|
</label>
|
|
196
200
|
`,
|
|
201
|
+
// @color: the hidden input is the field (the form commits it like any
|
|
202
|
+
// scalar); swatches, the current chip and the custom inputs are built and
|
|
203
|
+
// painted by color-control.js.
|
|
204
|
+
'@color': `
|
|
205
|
+
<div class="hcms-field hcms-color" data-hcms-shape="scalar">
|
|
206
|
+
<span class="hcms-label" data-hcms-label></span>
|
|
207
|
+
<input type="hidden" data-hcms-field />
|
|
208
|
+
<div class="hcms-color-row">
|
|
209
|
+
<button type="button" class="hcms-color-current" data-hcms-action="color-panel" aria-expanded="false">
|
|
210
|
+
<span class="hcms-color-chip"></span>
|
|
211
|
+
<span class="hcms-color-name"></span>
|
|
212
|
+
</button>
|
|
213
|
+
<button type="button" class="hcms-color-clear" data-hcms-action="color-clear" aria-label="Clear color">${CLOSE_ICON}</button>
|
|
214
|
+
</div>
|
|
215
|
+
<div class="hcms-color-panel" hidden>
|
|
216
|
+
<div class="hcms-color-grid"></div>
|
|
217
|
+
<div class="hcms-color-custom">
|
|
218
|
+
<input type="color" class="hcms-color-native" data-hcms-color-input="native" aria-label="Pick a custom color" />
|
|
219
|
+
<input class="mirk-input hcms-color-text" data-hcms-color-input="text" spellcheck="false" autocomplete="off" placeholder="Any CSS color" aria-label="Custom color" />
|
|
220
|
+
</div>
|
|
221
|
+
</div>
|
|
222
|
+
<div class="hcms-error" hidden></div>
|
|
223
|
+
</div>
|
|
224
|
+
`,
|
|
225
|
+
'@font': `
|
|
226
|
+
<div class="hcms-field hcms-font" data-hcms-shape="scalar">
|
|
227
|
+
<span class="hcms-label" data-hcms-label></span>
|
|
228
|
+
<input type="hidden" data-hcms-field />
|
|
229
|
+
<button type="button" class="hcms-font-current" data-hcms-action="font-panel" aria-haspopup="listbox" aria-expanded="false">
|
|
230
|
+
<span class="hcms-font-name"></span>
|
|
231
|
+
<span aria-hidden="true" class="hcms-font-chevron">›</span>
|
|
232
|
+
</button>
|
|
233
|
+
<div class="hcms-font-panel" hidden>
|
|
234
|
+
<div class="hcms-font-list" role="listbox"></div>
|
|
235
|
+
</div>
|
|
236
|
+
<div class="hcms-error" hidden></div>
|
|
237
|
+
</div>
|
|
238
|
+
`,
|
|
239
|
+
// @variant: a radio group of layout choices, each an icon plus a label; the
|
|
240
|
+
// page's own CSS keys on the attribute the choice writes.
|
|
241
|
+
'@variant': `
|
|
242
|
+
<div class="hcms-field hcms-variant" data-hcms-shape="scalar">
|
|
243
|
+
<span class="hcms-label" data-hcms-label></span>
|
|
244
|
+
<div class="hcms-variant-row" role="radiogroup">
|
|
245
|
+
<label class="hcms-variant-option">
|
|
246
|
+
<input type="radio" class="mirk-sr-only" data-hcms-field />
|
|
247
|
+
<span class="hcms-variant-icon"></span>
|
|
248
|
+
<span class="hcms-variant-label"></span>
|
|
249
|
+
</label>
|
|
250
|
+
</div>
|
|
251
|
+
<div class="hcms-error" hidden></div>
|
|
252
|
+
</div>
|
|
253
|
+
`,
|
|
197
254
|
'@chips': `
|
|
198
255
|
<div class="hcms-field hcms-chips" data-hcms-shape="scalar-array">
|
|
199
256
|
<span class="hcms-label" data-hcms-label></span>
|
|
@@ -247,7 +304,9 @@ export function injectComponentTemplate(doc, key) {
|
|
|
247
304
|
// "true" helps nobody). @href is NOT inferred: editing a link/URL is far more
|
|
248
305
|
// common than uploading a file, so an a@href rule stays a plain URL field and
|
|
249
306
|
// the file upload is opt-in. Suffix is split like the engine (ruleAttrIndex,
|
|
250
|
-
// see engine/extract.js).
|
|
307
|
+
// see engine/extract.js). A custom property named --color or --color-* infers
|
|
308
|
+
// the color picker; --font / --font-* infers the font menu. Returns a
|
|
309
|
+
// DEFAULT_TEMPLATES key.
|
|
251
310
|
const PROP_TO_COMPONENT = { src: '@image', checked: '@checkbox', innerHTML: '@richtext' }
|
|
252
311
|
// data-hcms-component values for scalar rules. "chips" is array-only (below).
|
|
253
312
|
const SCALAR_COMPONENT_BY_NAME = {
|
|
@@ -260,6 +319,9 @@ const SCALAR_COMPONENT_BY_NAME = {
|
|
|
260
319
|
textarea: '@textarea',
|
|
261
320
|
number: '@number',
|
|
262
321
|
richtext: '@richtext',
|
|
322
|
+
color: '@color',
|
|
323
|
+
font: '@font',
|
|
324
|
+
variant: '@variant',
|
|
263
325
|
}
|
|
264
326
|
// Every opt-in component injects on demand; only the 6 shape templates are
|
|
265
327
|
// always present.
|
|
@@ -273,7 +335,7 @@ export function componentForScalarRule(rule, doc, pathArr, pageRules) {
|
|
|
273
335
|
if (typeof rule !== 'string') return '@scalar'
|
|
274
336
|
const at = ruleAttrIndex(rule)
|
|
275
337
|
const host = hostSelectorOf(rule, at, pathArr, pageRules)
|
|
276
|
-
const override =
|
|
338
|
+
const override = readRuleAttr(rule, at, pathArr, pageRules, doc, 'data-hcms-component')
|
|
277
339
|
if (override && SCALAR_COMPONENT_BY_NAME[override]) {
|
|
278
340
|
const key = SCALAR_COMPONENT_BY_NAME[override]
|
|
279
341
|
// Value guards check exactly the elements the engine binds: under an array
|
|
@@ -296,6 +358,8 @@ export function componentForScalarRule(rule, doc, pathArr, pageRules) {
|
|
|
296
358
|
if (at >= 0) {
|
|
297
359
|
const prop = rule.slice(at + 1)
|
|
298
360
|
if (PROP_TO_COMPONENT[prop]) return PROP_TO_COMPONENT[prop]
|
|
361
|
+
if (/^--color(-|$)/.test(prop) && prop !== '--color-scheme') return '@color'
|
|
362
|
+
if (/^--font(-|$)/.test(prop) && !NOT_A_FAMILY.test(prop)) return '@font'
|
|
299
363
|
}
|
|
300
364
|
return '@scalar'
|
|
301
365
|
}
|
|
@@ -307,10 +371,24 @@ export function componentForScalarRule(rule, doc, pathArr, pageRules) {
|
|
|
307
371
|
export function declaredComponentKey(rule, doc, pathArr, pageRules) {
|
|
308
372
|
if (typeof rule !== 'string') return null
|
|
309
373
|
const at = ruleAttrIndex(rule)
|
|
310
|
-
const name =
|
|
374
|
+
const name = readRuleAttr(rule, at, pathArr, pageRules, doc, 'data-hcms-component')
|
|
311
375
|
return (name && SCALAR_COMPONENT_BY_NAME[name]) || null
|
|
312
376
|
}
|
|
313
377
|
|
|
378
|
+
// Style fields (color, font, variant) are grouped apart from content. One bound
|
|
379
|
+
// to html, body or :root outside any list styles the whole page ('page'); any
|
|
380
|
+
// other styles its own element ('section'). Content fields return null.
|
|
381
|
+
const STYLE_COMPONENTS = new Set(['@color', '@font', '@variant'])
|
|
382
|
+
const PAGE_HOSTS = new Set(['html', 'body', ':root'])
|
|
383
|
+
|
|
384
|
+
export function styleRole(rule, doc, pathArr, pageRules) {
|
|
385
|
+
if (typeof rule !== 'string' || rule.endsWith('[]')) return null
|
|
386
|
+
if (!STYLE_COMPONENTS.has(componentForScalarRule(rule, doc, pathArr, pageRules))) return null
|
|
387
|
+
const inList = Array.isArray(pathArr) && pathArr.some((s) => s === '*' || typeof s === 'number')
|
|
388
|
+
const selector = scalarSelectorOf(rule, ruleAttrIndex(rule)).trim()
|
|
389
|
+
return !inList && PAGE_HOSTS.has(selector) ? 'page' : 'section'
|
|
390
|
+
}
|
|
391
|
+
|
|
314
392
|
// A number input physically rejects any value that isn't a plain number — it
|
|
315
393
|
// reads back '' — and the whole-form commit would then blank the page value on
|
|
316
394
|
// the next edit of ANY field. When any bound value isn't number-shaped, fall
|
|
@@ -416,7 +494,7 @@ export function winningScalarArrayComponent(rule, pathArr, doc) {
|
|
|
416
494
|
export function readOptionsOverride(rule, doc, pathArr, pageRules) {
|
|
417
495
|
if (typeof rule !== 'string') return null
|
|
418
496
|
const at = ruleAttrIndex(rule)
|
|
419
|
-
const raw =
|
|
497
|
+
const raw = readRuleAttr(rule, at, pathArr, pageRules, doc, 'data-hcms-options')
|
|
420
498
|
if (raw == null) return null
|
|
421
499
|
const tokens = raw.trim().split(/\s+/).filter(Boolean)
|
|
422
500
|
return tokens.length ? tokens : null
|
|
@@ -428,7 +506,7 @@ export function readOptionsOverride(rule, doc, pathArr, pageRules) {
|
|
|
428
506
|
export function readCropOverride(rule, doc, pathArr, pageRules) {
|
|
429
507
|
if (typeof rule !== 'string') return null
|
|
430
508
|
const at = ruleAttrIndex(rule)
|
|
431
|
-
return
|
|
509
|
+
return readRuleAttr(rule, at, pathArr, pageRules, doc, 'data-hcms-crop')
|
|
432
510
|
}
|
|
433
511
|
|
|
434
512
|
function scalarSelectorOf(rule, at) {
|
|
@@ -493,6 +571,22 @@ function readElementAttr(selector, doc, attrName) {
|
|
|
493
571
|
return el && el.getAttribute ? el.getAttribute(attrName) : null
|
|
494
572
|
}
|
|
495
573
|
|
|
574
|
+
// The authoring attribute for ONE rule. An element can carry several rules
|
|
575
|
+
// (<html> holds every theme property), so data-hcms-component:--color-accent
|
|
576
|
+
// names the control for the @--color-accent rule alone and wins over the plain
|
|
577
|
+
// attribute. The plain attribute never applies to a @--* rule: it describes the
|
|
578
|
+
// element's own value, not a CSS variable stored on it.
|
|
579
|
+
function readRuleAttr(rule, at, pathArr, pageRules, doc, attrName) {
|
|
580
|
+
const host = hostSelectorOf(rule, at, pathArr, pageRules)
|
|
581
|
+
const prop = at >= 0 ? rule.slice(at + 1) : null
|
|
582
|
+
if (prop) {
|
|
583
|
+
const scoped = readElementAttr(host, doc, `${attrName}:${prop}`)
|
|
584
|
+
if (scoped != null) return scoped
|
|
585
|
+
if (prop.startsWith('--')) return null
|
|
586
|
+
}
|
|
587
|
+
return readElementAttr(host, doc, attrName)
|
|
588
|
+
}
|
|
589
|
+
|
|
496
590
|
// Pre-inject the component templates this page's rules actually select, so
|
|
497
591
|
// both deriveFormRules (selectors) and buildForm (DOM) resolve them. Walks the
|
|
498
592
|
// same rule shapes as buildTemplateMap; idempotent and author-template-safe.
|
|
@@ -604,7 +698,7 @@ export function buildTemplateMap(pageRules, doc) {
|
|
|
604
698
|
const winningComp = shape === 'scalar-array' ? winningScalarArrayComponent(rule, pathArr, doc) : null
|
|
605
699
|
const defaultKey =
|
|
606
700
|
shape === 'scalar'
|
|
607
|
-
? componentForScalarRule(rule, doc, pathArr)
|
|
701
|
+
? componentForScalarRule(rule, doc, pathArr, pageRules)
|
|
608
702
|
: (arrayComp && arrayComp.array) || SHAPE_TO_DEFAULT_KEY[shape] || '@scalar'
|
|
609
703
|
|
|
610
704
|
let tpl =
|
|
@@ -458,6 +458,94 @@ body.hcms-open.hcms-overlay { overflow: hidden; --hcms-toggle-display: none; }
|
|
|
458
458
|
color: var(--mirk-placeholder-color);
|
|
459
459
|
}
|
|
460
460
|
|
|
461
|
+
/* ---------- COLOR (@color) ----------
|
|
462
|
+
Swatch colors arrive as --hcms-swatch on each element's style, so a value
|
|
463
|
+
never becomes a selector or a class. The full palette is an 11-column grid
|
|
464
|
+
(one row per hue); a narrowed set wraps. */
|
|
465
|
+
.hcms-color-row { display: flex; align-items: center; gap: 8px; }
|
|
466
|
+
.hcms-color-current {
|
|
467
|
+
display: inline-flex; align-items: center; gap: 8px;
|
|
468
|
+
padding: 4px 12px 4px 4px; border: 1px solid var(--mirk-input-border); border-radius: 999px;
|
|
469
|
+
background: var(--mirk-input-bg); color: var(--mirk-fg); font: inherit; cursor: pointer;
|
|
470
|
+
}
|
|
471
|
+
.hcms-color--inline .hcms-color-current { cursor: default; }
|
|
472
|
+
.hcms-color-chip, .hcms-color-swatch { background: var(--hcms-swatch, transparent); }
|
|
473
|
+
.hcms-color-chip {
|
|
474
|
+
width: 20px; height: 20px; border-radius: 50%;
|
|
475
|
+
box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--mirk-fg) 20%, transparent);
|
|
476
|
+
}
|
|
477
|
+
.hcms-color[data-hcms-color-empty] .hcms-color-chip {
|
|
478
|
+
background: repeating-conic-gradient(var(--mirk-input-border) 0 25%, transparent 0 50%) 50% / 8px 8px;
|
|
479
|
+
}
|
|
480
|
+
.hcms-color-clear {
|
|
481
|
+
display: inline-flex; padding: 2px; border: 0; background: transparent;
|
|
482
|
+
color: var(--mirk-placeholder-color); cursor: pointer;
|
|
483
|
+
}
|
|
484
|
+
.hcms-color-clear:hover { color: var(--mirk-fg); }
|
|
485
|
+
.hcms-color[data-hcms-color-empty] .hcms-color-clear { display: none; }
|
|
486
|
+
.hcms-color-panel { margin-top: 8px; }
|
|
487
|
+
.hcms-color-panel[hidden] { display: none; }
|
|
488
|
+
.hcms-color-grid { display: flex; flex-wrap: wrap; gap: 4px; }
|
|
489
|
+
.hcms-color-grid--full { display: grid; grid-template-columns: repeat(11, minmax(0, 1fr)); gap: 3px; }
|
|
490
|
+
.hcms-color-swatch {
|
|
491
|
+
width: 22px; height: 22px; padding: 0; border: 0; border-radius: 4px; cursor: pointer;
|
|
492
|
+
box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--mirk-fg) 15%, transparent);
|
|
493
|
+
}
|
|
494
|
+
.hcms-color-grid--full .hcms-color-swatch { width: auto; height: auto; aspect-ratio: 1; }
|
|
495
|
+
.hcms-color-swatch[aria-pressed="true"] { box-shadow: 0 0 0 2px var(--mirk-bg), 0 0 0 4px var(--mirk-fg); }
|
|
496
|
+
.hcms-color-swatch:focus-visible { outline: 2px solid var(--mirk-focus-color); outline-offset: 2px; }
|
|
497
|
+
.hcms-color-custom { display: flex; align-items: center; gap: 8px; margin-top: 8px; }
|
|
498
|
+
.hcms-color-native {
|
|
499
|
+
width: 32px; height: 28px; padding: 0; border: 1px solid var(--mirk-input-border);
|
|
500
|
+
border-radius: 4px; background: none; cursor: pointer;
|
|
501
|
+
}
|
|
502
|
+
.hcms-color-text { flex: 1; min-width: 0; }
|
|
503
|
+
|
|
504
|
+
/* ---------- FONT (@font) ----------
|
|
505
|
+
Each option sets its own font-family inline, so the list previews every
|
|
506
|
+
family in its face once the editor-only preview stylesheet arrives. */
|
|
507
|
+
.hcms-font-current {
|
|
508
|
+
display: flex; align-items: center; justify-content: space-between; gap: 8px; width: 100%;
|
|
509
|
+
padding: 6px 10px; border: 1px solid var(--mirk-input-border); border-radius: 4px;
|
|
510
|
+
background: var(--mirk-input-bg); color: var(--mirk-fg); font: inherit; cursor: pointer; text-align: left;
|
|
511
|
+
}
|
|
512
|
+
.hcms-font-name { font-size: 15px; }
|
|
513
|
+
.hcms-font-chevron { transform: rotate(90deg); color: var(--mirk-placeholder-color); }
|
|
514
|
+
.hcms-font-panel { margin-top: 6px; }
|
|
515
|
+
.hcms-font-panel[hidden] { display: none; }
|
|
516
|
+
.hcms-font-list {
|
|
517
|
+
display: flex; flex-direction: column; max-height: 320px; overflow-y: auto;
|
|
518
|
+
border: 1px solid var(--mirk-input-border); border-radius: 4px; background: var(--mirk-input-bg);
|
|
519
|
+
}
|
|
520
|
+
.hcms-font-group {
|
|
521
|
+
padding: 8px 10px 4px; font-size: 11px; letter-spacing: 0.04em; text-transform: uppercase;
|
|
522
|
+
color: var(--mirk-placeholder-color);
|
|
523
|
+
}
|
|
524
|
+
.hcms-font-option {
|
|
525
|
+
padding: 6px 10px; border: 0; background: transparent; color: var(--mirk-fg);
|
|
526
|
+
font-size: 16px; text-align: left; cursor: pointer;
|
|
527
|
+
}
|
|
528
|
+
.hcms-font-option:hover { background: color-mix(in oklab, var(--mirk-fg) 8%, transparent); }
|
|
529
|
+
.hcms-font-option[aria-selected="true"] { background: color-mix(in oklab, var(--mirk-fg) 14%, transparent); }
|
|
530
|
+
.hcms-font-option:focus-visible { outline: 2px solid var(--mirk-focus-color); outline-offset: -2px; }
|
|
531
|
+
|
|
532
|
+
/* ---------- VARIANT (@variant) ----------
|
|
533
|
+
Native radios behind icon tiles: arrow keys move the choice, the tile with
|
|
534
|
+
the checked radio is the selected one. */
|
|
535
|
+
.hcms-variant-row { display: flex; flex-wrap: wrap; gap: 8px; }
|
|
536
|
+
.hcms-variant-option {
|
|
537
|
+
position: relative; display: inline-flex; flex-direction: column; align-items: center; gap: 4px;
|
|
538
|
+
min-width: 64px; padding: 8px 10px; border: 1px solid var(--mirk-input-border); border-radius: 6px;
|
|
539
|
+
background: var(--mirk-input-bg); color: var(--mirk-placeholder-color); font-size: 12px; cursor: pointer;
|
|
540
|
+
}
|
|
541
|
+
.hcms-variant-option:hover { color: var(--mirk-fg); }
|
|
542
|
+
.hcms-variant-option:has(input:checked) {
|
|
543
|
+
color: var(--mirk-fg); border-color: var(--mirk-fg);
|
|
544
|
+
box-shadow: inset 0 0 0 1px var(--mirk-fg);
|
|
545
|
+
}
|
|
546
|
+
.hcms-variant-option:has(input:focus-visible) { outline: 2px solid var(--mirk-focus-color); outline-offset: 2px; }
|
|
547
|
+
.hcms-variant-icon { display: inline-flex; }
|
|
548
|
+
|
|
461
549
|
/* ---------- UPLOADING (spec §9) ----------
|
|
462
550
|
One attribute, [data-hcms-uploading], set on the field for as long as the host
|
|
463
551
|
has the bytes, plus --hcms-upload-progress carrying the percent. No markup of
|
|
@@ -826,6 +914,9 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
|
|
|
826
914
|
.hcms-inline .hcms-form [data-hcms-path] { display: none; }
|
|
827
915
|
.hcms-inline .hcms-form [data-hcms-path].is-hcms-inline-onpath,
|
|
828
916
|
.hcms-inline .hcms-form [data-hcms-path].is-hcms-inline-active { display: block; }
|
|
917
|
+
/* The Page style group has no data path of its own: hide it like a path until
|
|
918
|
+
a reveal marks it on-path. */
|
|
919
|
+
.hcms-inline .hcms-form [data-hcms-page-style]:not(.is-hcms-inline-onpath) { display: none; }
|
|
829
920
|
|
|
830
921
|
.hcms-inline .hcms-form .is-hcms-inline-onpath,
|
|
831
922
|
.hcms-inline .hcms-form {
|
package/src/theme.generated.css
CHANGED
|
@@ -1469,6 +1469,94 @@ body.hcms-open.hcms-overlay { overflow: hidden; --hcms-toggle-display: none; }
|
|
|
1469
1469
|
color: var(--mirk-placeholder-color);
|
|
1470
1470
|
}
|
|
1471
1471
|
|
|
1472
|
+
/* ---------- COLOR (@color) ----------
|
|
1473
|
+
Swatch colors arrive as --hcms-swatch on each element's style, so a value
|
|
1474
|
+
never becomes a selector or a class. The full palette is an 11-column grid
|
|
1475
|
+
(one row per hue); a narrowed set wraps. */
|
|
1476
|
+
.hcms-color-row { display: flex; align-items: center; gap: 8px; }
|
|
1477
|
+
.hcms-color-current {
|
|
1478
|
+
display: inline-flex; align-items: center; gap: 8px;
|
|
1479
|
+
padding: 4px 12px 4px 4px; border: 1px solid var(--mirk-input-border); border-radius: 999px;
|
|
1480
|
+
background: var(--mirk-input-bg); color: var(--mirk-fg); font: inherit; cursor: pointer;
|
|
1481
|
+
}
|
|
1482
|
+
.hcms-color--inline .hcms-color-current { cursor: default; }
|
|
1483
|
+
.hcms-color-chip, .hcms-color-swatch { background: var(--hcms-swatch, transparent); }
|
|
1484
|
+
.hcms-color-chip {
|
|
1485
|
+
width: 20px; height: 20px; border-radius: 50%;
|
|
1486
|
+
box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--mirk-fg) 20%, transparent);
|
|
1487
|
+
}
|
|
1488
|
+
.hcms-color[data-hcms-color-empty] .hcms-color-chip {
|
|
1489
|
+
background: repeating-conic-gradient(var(--mirk-input-border) 0 25%, transparent 0 50%) 50% / 8px 8px;
|
|
1490
|
+
}
|
|
1491
|
+
.hcms-color-clear {
|
|
1492
|
+
display: inline-flex; padding: 2px; border: 0; background: transparent;
|
|
1493
|
+
color: var(--mirk-placeholder-color); cursor: pointer;
|
|
1494
|
+
}
|
|
1495
|
+
.hcms-color-clear:hover { color: var(--mirk-fg); }
|
|
1496
|
+
.hcms-color[data-hcms-color-empty] .hcms-color-clear { display: none; }
|
|
1497
|
+
.hcms-color-panel { margin-top: 8px; }
|
|
1498
|
+
.hcms-color-panel[hidden] { display: none; }
|
|
1499
|
+
.hcms-color-grid { display: flex; flex-wrap: wrap; gap: 4px; }
|
|
1500
|
+
.hcms-color-grid--full { display: grid; grid-template-columns: repeat(11, minmax(0, 1fr)); gap: 3px; }
|
|
1501
|
+
.hcms-color-swatch {
|
|
1502
|
+
width: 22px; height: 22px; padding: 0; border: 0; border-radius: 4px; cursor: pointer;
|
|
1503
|
+
box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--mirk-fg) 15%, transparent);
|
|
1504
|
+
}
|
|
1505
|
+
.hcms-color-grid--full .hcms-color-swatch { width: auto; height: auto; aspect-ratio: 1; }
|
|
1506
|
+
.hcms-color-swatch[aria-pressed="true"] { box-shadow: 0 0 0 2px var(--mirk-bg), 0 0 0 4px var(--mirk-fg); }
|
|
1507
|
+
.hcms-color-swatch:focus-visible { outline: 2px solid var(--mirk-focus-color); outline-offset: 2px; }
|
|
1508
|
+
.hcms-color-custom { display: flex; align-items: center; gap: 8px; margin-top: 8px; }
|
|
1509
|
+
.hcms-color-native {
|
|
1510
|
+
width: 32px; height: 28px; padding: 0; border: 1px solid var(--mirk-input-border);
|
|
1511
|
+
border-radius: 4px; background: none; cursor: pointer;
|
|
1512
|
+
}
|
|
1513
|
+
.hcms-color-text { flex: 1; min-width: 0; }
|
|
1514
|
+
|
|
1515
|
+
/* ---------- FONT (@font) ----------
|
|
1516
|
+
Each option sets its own font-family inline, so the list previews every
|
|
1517
|
+
family in its face once the editor-only preview stylesheet arrives. */
|
|
1518
|
+
.hcms-font-current {
|
|
1519
|
+
display: flex; align-items: center; justify-content: space-between; gap: 8px; width: 100%;
|
|
1520
|
+
padding: 6px 10px; border: 1px solid var(--mirk-input-border); border-radius: 4px;
|
|
1521
|
+
background: var(--mirk-input-bg); color: var(--mirk-fg); font: inherit; cursor: pointer; text-align: left;
|
|
1522
|
+
}
|
|
1523
|
+
.hcms-font-name { font-size: 15px; }
|
|
1524
|
+
.hcms-font-chevron { transform: rotate(90deg); color: var(--mirk-placeholder-color); }
|
|
1525
|
+
.hcms-font-panel { margin-top: 6px; }
|
|
1526
|
+
.hcms-font-panel[hidden] { display: none; }
|
|
1527
|
+
.hcms-font-list {
|
|
1528
|
+
display: flex; flex-direction: column; max-height: 320px; overflow-y: auto;
|
|
1529
|
+
border: 1px solid var(--mirk-input-border); border-radius: 4px; background: var(--mirk-input-bg);
|
|
1530
|
+
}
|
|
1531
|
+
.hcms-font-group {
|
|
1532
|
+
padding: 8px 10px 4px; font-size: 11px; letter-spacing: 0.04em; text-transform: uppercase;
|
|
1533
|
+
color: var(--mirk-placeholder-color);
|
|
1534
|
+
}
|
|
1535
|
+
.hcms-font-option {
|
|
1536
|
+
padding: 6px 10px; border: 0; background: transparent; color: var(--mirk-fg);
|
|
1537
|
+
font-size: 16px; text-align: left; cursor: pointer;
|
|
1538
|
+
}
|
|
1539
|
+
.hcms-font-option:hover { background: color-mix(in oklab, var(--mirk-fg) 8%, transparent); }
|
|
1540
|
+
.hcms-font-option[aria-selected="true"] { background: color-mix(in oklab, var(--mirk-fg) 14%, transparent); }
|
|
1541
|
+
.hcms-font-option:focus-visible { outline: 2px solid var(--mirk-focus-color); outline-offset: -2px; }
|
|
1542
|
+
|
|
1543
|
+
/* ---------- VARIANT (@variant) ----------
|
|
1544
|
+
Native radios behind icon tiles: arrow keys move the choice, the tile with
|
|
1545
|
+
the checked radio is the selected one. */
|
|
1546
|
+
.hcms-variant-row { display: flex; flex-wrap: wrap; gap: 8px; }
|
|
1547
|
+
.hcms-variant-option {
|
|
1548
|
+
position: relative; display: inline-flex; flex-direction: column; align-items: center; gap: 4px;
|
|
1549
|
+
min-width: 64px; padding: 8px 10px; border: 1px solid var(--mirk-input-border); border-radius: 6px;
|
|
1550
|
+
background: var(--mirk-input-bg); color: var(--mirk-placeholder-color); font-size: 12px; cursor: pointer;
|
|
1551
|
+
}
|
|
1552
|
+
.hcms-variant-option:hover { color: var(--mirk-fg); }
|
|
1553
|
+
.hcms-variant-option:has(input:checked) {
|
|
1554
|
+
color: var(--mirk-fg); border-color: var(--mirk-fg);
|
|
1555
|
+
box-shadow: inset 0 0 0 1px var(--mirk-fg);
|
|
1556
|
+
}
|
|
1557
|
+
.hcms-variant-option:has(input:focus-visible) { outline: 2px solid var(--mirk-focus-color); outline-offset: 2px; }
|
|
1558
|
+
.hcms-variant-icon { display: inline-flex; }
|
|
1559
|
+
|
|
1472
1560
|
/* ---------- UPLOADING (spec §9) ----------
|
|
1473
1561
|
One attribute, [data-hcms-uploading], set on the field for as long as the host
|
|
1474
1562
|
has the bytes, plus --hcms-upload-progress carrying the percent. No markup of
|
|
@@ -1837,6 +1925,9 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
|
|
|
1837
1925
|
.hcms-inline .hcms-form [data-hcms-path] { display: none; }
|
|
1838
1926
|
.hcms-inline .hcms-form [data-hcms-path].is-hcms-inline-onpath,
|
|
1839
1927
|
.hcms-inline .hcms-form [data-hcms-path].is-hcms-inline-active { display: block; }
|
|
1928
|
+
/* The Page style group has no data path of its own: hide it like a path until
|
|
1929
|
+
a reveal marks it on-path. */
|
|
1930
|
+
.hcms-inline .hcms-form [data-hcms-page-style]:not(.is-hcms-inline-onpath) { display: none; }
|
|
1840
1931
|
|
|
1841
1932
|
.hcms-inline .hcms-form .is-hcms-inline-onpath,
|
|
1842
1933
|
.hcms-inline .hcms-form {
|