@uniweb/theming 0.1.10 → 0.1.12
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/package.json +1 -1
- package/src/css-generator.js +14 -2
- package/src/processor.js +67 -3
package/package.json
CHANGED
package/src/css-generator.js
CHANGED
|
@@ -26,7 +26,12 @@ const DEFAULT_CONTEXT_TOKENS = {
|
|
|
26
26
|
'ring': 'var(--primary-500)',
|
|
27
27
|
'link': 'var(--primary-600)',
|
|
28
28
|
'link-hover': 'var(--primary-700)',
|
|
29
|
-
'
|
|
29
|
+
// Shade 500 is the site's colour exactly as authored (the palette anchors
|
|
30
|
+
// the input there). `link` and `primary` sit at 600 because they carry the
|
|
31
|
+
// strict contrast burden — body-size links, and white text on a fill. An
|
|
32
|
+
// accent is decorative emphasis, usually at display size, so it keeps the
|
|
33
|
+
// brand colour unmuddied instead of paying for contrast it doesn't need.
|
|
34
|
+
'accent': 'var(--accent-500)',
|
|
30
35
|
'primary': 'var(--primary-600)',
|
|
31
36
|
'primary-foreground': 'white',
|
|
32
37
|
'primary-hover': 'var(--primary-700)',
|
|
@@ -55,7 +60,7 @@ const DEFAULT_CONTEXT_TOKENS = {
|
|
|
55
60
|
'ring': 'var(--primary-500)',
|
|
56
61
|
'link': 'var(--primary-600)',
|
|
57
62
|
'link-hover': 'var(--primary-700)',
|
|
58
|
-
'accent': 'var(--accent-
|
|
63
|
+
'accent': 'var(--accent-500)', // exact brand — see the light context
|
|
59
64
|
'primary': 'var(--primary-600)',
|
|
60
65
|
'primary-foreground': 'white',
|
|
61
66
|
'primary-hover': 'var(--primary-700)',
|
|
@@ -84,6 +89,11 @@ const DEFAULT_CONTEXT_TOKENS = {
|
|
|
84
89
|
'ring': 'var(--primary-500)',
|
|
85
90
|
'link': 'var(--primary-400)',
|
|
86
91
|
'link-hover': 'var(--primary-300)',
|
|
92
|
+
// Stays lightened, unlike the light/medium contexts which use the exact
|
|
93
|
+
// brand at 500. Every text token here is lightened (body, subtle, link)
|
|
94
|
+
// because a mid-brand hue on a near-black surface often isn't legible —
|
|
95
|
+
// a navy or forest-green accent at 500 would effectively disappear. A site
|
|
96
|
+
// whose brand does read on dark can set `contexts.dark.accent` to it.
|
|
87
97
|
'accent': 'var(--accent-400)',
|
|
88
98
|
'primary': 'var(--primary-500)',
|
|
89
99
|
'primary-foreground': 'white',
|
|
@@ -108,6 +118,8 @@ const DEFAULT_CONTEXT_TOKENS = {
|
|
|
108
118
|
const DEFAULT_COLORS = {
|
|
109
119
|
primary: '#3b82f6', // Blue
|
|
110
120
|
secondary: '#64748b', // Slate
|
|
121
|
+
// Last resort only. A site that names no accent inherits its primary (see
|
|
122
|
+
// processTheme) — this literal is reached solely when the primary is unusable.
|
|
111
123
|
accent: '#8b5cf6', // Purple
|
|
112
124
|
neutral: '#78716c', // Stone
|
|
113
125
|
}
|
package/src/processor.js
CHANGED
|
@@ -26,10 +26,19 @@ const NEUTRAL_PRESETS = {
|
|
|
26
26
|
/**
|
|
27
27
|
* Default inline text styles (content-author markdown: [text]{accent})
|
|
28
28
|
* These reference semantic tokens so they adapt to context automatically
|
|
29
|
+
*
|
|
30
|
+
* `accent` resolves to --accent, not --link. It used to be --link, which
|
|
31
|
+
* conflated two roles that have genuinely different constraints: --link means
|
|
32
|
+
* "navigable text" and must stay readable at body size (4.5:1), while an accent
|
|
33
|
+
* is decorative emphasis, usually at display size (3:1 suffices). Welding them
|
|
34
|
+
* together meant a site that responsibly darkened its link colour also drained
|
|
35
|
+
* the brand colour out of its headlines, where the darker value buys nothing.
|
|
36
|
+
* It also left the `accent` colour family nearly unreachable from markdown, and
|
|
37
|
+
* contradicted `callout` right below, which already used --accent.
|
|
29
38
|
*/
|
|
30
39
|
const DEFAULT_INLINE = {
|
|
31
40
|
accent: {
|
|
32
|
-
color: 'var(--
|
|
41
|
+
color: 'var(--accent)',
|
|
33
42
|
'font-weight': '600',
|
|
34
43
|
},
|
|
35
44
|
callout: {
|
|
@@ -369,6 +378,49 @@ export function validateThemeConfig(config) {
|
|
|
369
378
|
}
|
|
370
379
|
}
|
|
371
380
|
|
|
381
|
+
/**
|
|
382
|
+
* Merge inline text styles over the framework defaults, property by property.
|
|
383
|
+
*
|
|
384
|
+
* Everything else in theme.yml merges at the granularity of the leaf value —
|
|
385
|
+
* `contexts[name]` merges your tokens into the default context object, `colors`
|
|
386
|
+
* and `appearance` merge per key. This does the same one level down, so a site
|
|
387
|
+
* declares only the deltas:
|
|
388
|
+
*
|
|
389
|
+
* inline:
|
|
390
|
+
* accent:
|
|
391
|
+
* font-weight: inherit # keeps the default colour
|
|
392
|
+
*
|
|
393
|
+
* A shallow merge replaced the whole style object instead, so the example above
|
|
394
|
+
* silently produced `span[accent] { font-weight: inherit }` with no colour at
|
|
395
|
+
* all, and accented text rendered in the surrounding heading colour. The failure
|
|
396
|
+
* was subtractive and invisible in the config — you had to know what the default
|
|
397
|
+
* contained to notice it was gone. Three of the shipped templates carried a
|
|
398
|
+
* verbatim restatement of the default `accent` style purely to work around this.
|
|
399
|
+
*
|
|
400
|
+
* To *drop* a default property rather than change it, give it a neutral CSS
|
|
401
|
+
* value — `font-weight: inherit` to follow the surrounding text, or `initial` /
|
|
402
|
+
* `unset` / `revert`. Setting a whole style to null still removes it entirely
|
|
403
|
+
* (the CSS generator skips non-object entries).
|
|
404
|
+
*
|
|
405
|
+
* @param {Object} defaults - DEFAULT_INLINE
|
|
406
|
+
* @param {Object} [overrides] - rawConfig.inline
|
|
407
|
+
* @returns {Object} merged style map
|
|
408
|
+
*/
|
|
409
|
+
function mergeInlineStyles(defaults, overrides) {
|
|
410
|
+
const merged = { ...defaults }
|
|
411
|
+
|
|
412
|
+
for (const [name, style] of Object.entries(overrides || {})) {
|
|
413
|
+
// Non-objects (null/false) pass through so a site can delete a default
|
|
414
|
+
// style outright; anything else layers over the default of the same name.
|
|
415
|
+
merged[name] =
|
|
416
|
+
style && typeof style === 'object' && !Array.isArray(style)
|
|
417
|
+
? { ...(defaults[name] || {}), ...style }
|
|
418
|
+
: style
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
return merged
|
|
422
|
+
}
|
|
423
|
+
|
|
372
424
|
/**
|
|
373
425
|
* Normalize appearance configuration
|
|
374
426
|
*
|
|
@@ -486,6 +538,19 @@ export function processTheme(rawConfig = {}, options = {}) {
|
|
|
486
538
|
rawColors.neutral = NEUTRAL_PRESETS[rawColors.neutral]
|
|
487
539
|
}
|
|
488
540
|
|
|
541
|
+
// A site that declares no accent colour almost always means "my brand
|
|
542
|
+
// colour" — an accent is an optional second voice, not a required one. The
|
|
543
|
+
// alternative is a fixed hue nobody chose showing up wherever markdown says
|
|
544
|
+
// [text]{accent} or {callout}. Falling back to primary also makes the
|
|
545
|
+
// --accent token safe to use as a default elsewhere: it is always *a* brand
|
|
546
|
+
// colour rather than a stock purple. The literal in DEFAULT_COLORS survives
|
|
547
|
+
// only as a last resort for a site whose primary is unusable.
|
|
548
|
+
const brandColor = rawColors.primary ?? defaultColors.primary
|
|
549
|
+
const isUsableColor = (v) => (typeof v === 'object' && v !== null) || isValidColor(v)
|
|
550
|
+
if (rawColors.accent === undefined && isUsableColor(brandColor)) {
|
|
551
|
+
rawColors.accent = brandColor
|
|
552
|
+
}
|
|
553
|
+
|
|
489
554
|
// Filter to only valid colors (skip invalid ones in non-strict mode)
|
|
490
555
|
const validColors = {}
|
|
491
556
|
for (const [name, value] of Object.entries({ ...defaultColors, ...rawColors })) {
|
|
@@ -637,8 +702,7 @@ export function processTheme(rawConfig = {}, options = {}) {
|
|
|
637
702
|
const background = rawConfig.background || null
|
|
638
703
|
|
|
639
704
|
// Inline text styles (semantic names → CSS declarations)
|
|
640
|
-
|
|
641
|
-
const inline = { ...DEFAULT_INLINE, ...(rawConfig.inline || {}) }
|
|
705
|
+
const inline = mergeInlineStyles(DEFAULT_INLINE, rawConfig.inline)
|
|
642
706
|
|
|
643
707
|
const config = {
|
|
644
708
|
colors, // Raw colors for CSS generator
|