@pie-players/pie-theme 0.3.67 → 0.3.68
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 +111 -0
- package/dist/apply-color-scheme.d.ts +25 -0
- package/dist/apply-color-scheme.js +65 -0
- package/dist/color-schemes.js +9 -1
- package/dist/daisyui-mapping.d.ts +11 -10
- package/dist/daisyui-mapping.js +11 -10
- package/dist/daisyui-theme-catalog.d.ts +8 -0
- package/dist/daisyui-theme-catalog.js +43 -0
- package/dist/font-sizes.css +57 -104
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/theme-definitions.d.ts +17 -0
- package/dist/theme-definitions.js +36 -0
- package/dist/theme-element.d.ts +16 -0
- package/dist/theme-element.js +80 -5
- package/dist/theme-types.d.ts +5 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
Shared PIE theming primitives and the `pie-theme` custom element.
|
|
4
4
|
|
|
5
|
+
This file is the reference. For how the pieces fit — why a host stylesheet
|
|
6
|
+
cannot override a mounted element, what the provider choice actually decides, and
|
|
7
|
+
how a host carries an accommodation into its own chrome — see
|
|
8
|
+
[`docs/theming/how-theming-works.md`](../../docs/theming/how-theming-works.md).
|
|
9
|
+
|
|
5
10
|
`pie-theme` resolves PIE theme tokens (`--pie-*`) with this precedence:
|
|
6
11
|
|
|
7
12
|
1. Base PIE theme (`theme=light|dark|auto`)
|
|
@@ -177,6 +182,59 @@ cascade requirements are intentional. Such a selector follows the normal
|
|
|
177
182
|
cascade in a stylesheet-only integration; when it competes with a mounted
|
|
178
183
|
`<pie-theme>`'s inline tokens, it needs `!important`.
|
|
179
184
|
|
|
185
|
+
## SchoolCity scheme parity
|
|
186
|
+
|
|
187
|
+
SchoolCity offers 15 schemes. Four are built in here — Black on White, White on
|
|
188
|
+
Black, Black on Rose, Yellow on Blue — and the other eleven are host palettes a
|
|
189
|
+
programme registers itself, from these values:
|
|
190
|
+
|
|
191
|
+
| SchoolCity token | Value |
|
|
192
|
+
| --- | --- |
|
|
193
|
+
| blue | `#0028a1` |
|
|
194
|
+
| red | `#bf0d00` |
|
|
195
|
+
| green | `#008272` |
|
|
196
|
+
| yellow | `#ffe072` |
|
|
197
|
+
| light gray | `#c0c3cf` |
|
|
198
|
+
| dark gray | `#9297a6` |
|
|
199
|
+
| rose | `#f8d1ce` |
|
|
200
|
+
|
|
201
|
+
All 15 clear 4.5:1 for ordinary text; Green on White is the tightest at 4.73:1.
|
|
202
|
+
Ordinary text is not the whole cost, though, because the light base chose every
|
|
203
|
+
semantic colour against white. What an overlay has to carry scales with how far
|
|
204
|
+
its background sits from white:
|
|
205
|
+
|
|
206
|
+
| Background | Tokens | Why |
|
|
207
|
+
| --- | --- | --- |
|
|
208
|
+
| white | 2 | ink and page; every base colour already holds |
|
|
209
|
+
| white, with a mid-tone ink | 4 | the ink misses the tinted recessed surfaces |
|
|
210
|
+
| `#000000` | 10 | inverted page; borrow the dark base theme's inks |
|
|
211
|
+
| mid-tone (blue, red, green, dark gray) | ~18 | neither light nor dark inks hold, so icons, boundaries and focus rings are re-chosen too |
|
|
212
|
+
|
|
213
|
+
```ts
|
|
214
|
+
registerPieColorSchemes([
|
|
215
|
+
{
|
|
216
|
+
id: "sc-blue-on-white",
|
|
217
|
+
name: "Blue on White",
|
|
218
|
+
variables: { "--pie-text": "#0028a1", "--pie-background": "#ffffff" },
|
|
219
|
+
},
|
|
220
|
+
]);
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
`packages/theme/tests/schoolcity-scheme-registration.test.ts` carries a validated
|
|
224
|
+
palette for one scheme of each cost class and is the place to copy from.
|
|
225
|
+
|
|
226
|
+
Read the receipt. Contrast diagnostics are warnings, not errors, because the
|
|
227
|
+
palette is host-owned — a two-token White on Blue registers successfully and
|
|
228
|
+
returns fourteen warnings, and a host that filters on `severity === "error"`
|
|
229
|
+
ships cyan links on a mid-blue page. `registerPieColorSchemes` checks only the
|
|
230
|
+
relationships whose tokens the overlay touches, so covering a flagged token is
|
|
231
|
+
what clears its relationship.
|
|
232
|
+
|
|
233
|
+
These are host palettes rather than built-ins on purpose. A built-in is a full
|
|
234
|
+
48-token palette, since a two-colour scheme is a promise the whole surface has to
|
|
235
|
+
keep, and which schemes a programme wants is still open on
|
|
236
|
+
[PIE-472](https://illuminate.atlassian.net/browse/PIE-472).
|
|
237
|
+
|
|
180
238
|
## Fixed hues
|
|
181
239
|
|
|
182
240
|
Some components paint a hue the palette does not own: a data encoding, like the
|
|
@@ -201,6 +259,59 @@ accessible name.
|
|
|
201
259
|
- If DaisyUI tokens are present on the target scope, `pie-theme` uses the built-in `daisyui` provider adapter.
|
|
202
260
|
- Override precedence is: base PIE -> provider output -> scheme -> `variables`.
|
|
203
261
|
- `provider="none"` (`PIE_THEME_PROVIDER_NONE`) resolves no provider at all, leaving this package's shipped defaults. It is how a host reproduces the palette it had before adopting a provider, which is the first thing to check when colours differ between two environments.
|
|
262
|
+
- The adapter is the whole integration. `DAISYUI_PIE_TOKEN_MAP` is its sole source, and it corrects a slot that would land illegible because it reads resolved colours — which a stylesheet cannot do. The separate `pie-theme-daisyui` package that shipped the same table as static CSS was removed: `<pie-theme scope="document">` writes `--pie-*` as inline styles, so a stylesheet declaring them lost to it and the import did nothing.
|
|
263
|
+
- A host on some other token vocabulary aliases its own names to `--pie-*` in its own stylesheet, which is what a non-DaisyUI design system needs anyway. Do that under `[data-color-scheme]` to make an accommodation reach host chrome, since `<pie-theme>` never writes `--color-*` or any other host prefix.
|
|
264
|
+
|
|
265
|
+
## Font size scaling
|
|
266
|
+
|
|
267
|
+
`font-sizes.css` carries four presets on `--pie-font-scale` — `normal` (1),
|
|
268
|
+
`large` (1.25), `xlarge` (1.5), `xxlarge` (1.75). They are Learnosity's steps,
|
|
269
|
+
which K-12 accommodation profiles are already written against, so the numbers are
|
|
270
|
+
a contract: changing one changes what `large` means for every learner assigned it.
|
|
271
|
+
|
|
272
|
+
A host selects a preset with `data-font-size` on any ancestor of the player, or
|
|
273
|
+
on a player element itself:
|
|
274
|
+
|
|
275
|
+
```html
|
|
276
|
+
<html data-font-size="large">
|
|
277
|
+
<div data-font-size="xlarge"><pie-section-player></pie-section-player></div>
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
A host already driving `<pie-theme>` sets the token instead and needs nothing
|
|
281
|
+
from the stylesheet:
|
|
282
|
+
|
|
283
|
+
```html
|
|
284
|
+
<pie-theme variables='{"--pie-font-scale":"1.25"}'>
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
The content path scales completely. The rules set `font-size` on the content
|
|
288
|
+
hosts — `pie-item-shell`, `pie-passage-shell`, `pie-item-player` and the
|
|
289
|
+
externally loaded `pie-player` wrapper — and `font-size` inheritance crosses
|
|
290
|
+
shadow boundaries, so text that inherits its size follows. Every font size in
|
|
291
|
+
`components.css`, which styles item content, is relative (`em`, `%`,
|
|
292
|
+
`larger`/`smaller`) and follows too. The learner-facing text that declares its
|
|
293
|
+
own size and therefore cannot inherit the scale — the item and passage card
|
|
294
|
+
titles, the formative status line, the tabbed layout's labels, the item player's
|
|
295
|
+
build warning — reads `--pie-font-scale` directly, because a card wraps the shell
|
|
296
|
+
that gets scaled rather than sitting inside it.
|
|
297
|
+
|
|
298
|
+
Tool and debug chrome does not scale, deliberately: an accommodation applies to
|
|
299
|
+
what the learner reads, and a calculator keypad growing with the passage is a
|
|
300
|
+
layout problem rather than an accommodation.
|
|
301
|
+
|
|
302
|
+
A rule elsewhere that sizes text in `rem` or `px` still will not follow — `rem`
|
|
303
|
+
resolves against the document root and `px` against nothing, and no rule inside a
|
|
304
|
+
subtree can change what either means — so a host adding its own content chrome
|
|
305
|
+
either sizes it relatively or reads the token as these rules do. Browser zoom is
|
|
306
|
+
unaffected throughout, so WCAG 2.2 1.4.4 does not depend on this feature.
|
|
307
|
+
|
|
308
|
+
The scale is applied as `calc(1rem * var(--pie-font-scale))` rather than an `em`
|
|
309
|
+
factor because the content hosts nest — an `em` factor would compound, turning a
|
|
310
|
+
requested 1.25 into 1.56 wherever an item shell sits inside a themed region.
|
|
311
|
+
|
|
312
|
+
There is no student-facing control here. The picker is host chrome, which in the
|
|
313
|
+
Renaissance context is Quiz Engine's surface; this package owns the token, the
|
|
314
|
+
presets, and the rules that consume them.
|
|
204
315
|
|
|
205
316
|
## Token registry
|
|
206
317
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Find the nearest `<pie-theme>` that owns `from`'s scheme, walking out
|
|
3
|
+
* across shadow-root boundaries. `Element.closest()` alone stops at a
|
|
4
|
+
* shadow root: a caller mounted inside its own `shadow: "open"` root would
|
|
5
|
+
* never find a `<pie-theme>` ancestor that lives in the light DOM outside
|
|
6
|
+
* it, so this repeats the search from each shadow root's host until it
|
|
7
|
+
* either finds one or reaches the document.
|
|
8
|
+
*
|
|
9
|
+
* Falls back to a document-scoped `<pie-theme>` anywhere in the page, then
|
|
10
|
+
* any `<pie-theme>` at all, matching how a page with exactly one theme host
|
|
11
|
+
* is normally set up.
|
|
12
|
+
*/
|
|
13
|
+
export declare function resolvePieThemeHost(from?: Node | null): HTMLElement | null;
|
|
14
|
+
export interface ApplyPieColorSchemeOptions {
|
|
15
|
+
/** Search from this node outward for the nearest `<pie-theme>` ancestor. */
|
|
16
|
+
from?: Node | null;
|
|
17
|
+
/** localStorage key to persist the requested scheme under. Pass `null` to skip persistence. */
|
|
18
|
+
persistenceKey?: string | null;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Apply a requested color scheme and persist it, the one canonical way —
|
|
22
|
+
* shared by every caller that offers scheme selection outside `<pie-theme>`
|
|
23
|
+
* itself (a picker tool, a demo host's own theme controls).
|
|
24
|
+
*/
|
|
25
|
+
export declare function applyPieColorScheme(schemeId: string, options?: ApplyPieColorSchemeOptions): void;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
const DEFAULT_COLOR_SCHEME_STORAGE_KEY = "pie-color-scheme";
|
|
2
|
+
/**
|
|
3
|
+
* Find the nearest `<pie-theme>` that owns `from`'s scheme, walking out
|
|
4
|
+
* across shadow-root boundaries. `Element.closest()` alone stops at a
|
|
5
|
+
* shadow root: a caller mounted inside its own `shadow: "open"` root would
|
|
6
|
+
* never find a `<pie-theme>` ancestor that lives in the light DOM outside
|
|
7
|
+
* it, so this repeats the search from each shadow root's host until it
|
|
8
|
+
* either finds one or reaches the document.
|
|
9
|
+
*
|
|
10
|
+
* Falls back to a document-scoped `<pie-theme>` anywhere in the page, then
|
|
11
|
+
* any `<pie-theme>` at all, matching how a page with exactly one theme host
|
|
12
|
+
* is normally set up.
|
|
13
|
+
*/
|
|
14
|
+
export function resolvePieThemeHost(from) {
|
|
15
|
+
if (typeof document === "undefined")
|
|
16
|
+
return null;
|
|
17
|
+
let current = from instanceof Element ? from : null;
|
|
18
|
+
while (current) {
|
|
19
|
+
const found = current.closest("pie-theme");
|
|
20
|
+
if (found)
|
|
21
|
+
return found;
|
|
22
|
+
const root = current.getRootNode();
|
|
23
|
+
current = root instanceof ShadowRoot ? root.host : null;
|
|
24
|
+
}
|
|
25
|
+
return (document.querySelector('pie-theme[scope="document"]') ||
|
|
26
|
+
document.querySelector("pie-theme"));
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Apply a requested color scheme and persist it, the one canonical way —
|
|
30
|
+
* shared by every caller that offers scheme selection outside `<pie-theme>`
|
|
31
|
+
* itself (a picker tool, a demo host's own theme controls).
|
|
32
|
+
*/
|
|
33
|
+
export function applyPieColorScheme(schemeId, options = {}) {
|
|
34
|
+
if (typeof document === "undefined")
|
|
35
|
+
return;
|
|
36
|
+
const nextScheme = (schemeId || "default").trim() || "default";
|
|
37
|
+
const themeHost = resolvePieThemeHost(options.from);
|
|
38
|
+
if (themeHost) {
|
|
39
|
+
if (themeHost.getAttribute("scheme") !== nextScheme) {
|
|
40
|
+
themeHost.setAttribute("scheme", nextScheme);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
// No <pie-theme> owns `data-color-scheme` on this document, so this is
|
|
45
|
+
// the only way the shared color-scheme CSS can still pick up the
|
|
46
|
+
// requested scheme. A scope="document" <pie-theme> that mounts later
|
|
47
|
+
// captures whatever is already on `documentElement` as its restore
|
|
48
|
+
// baseline rather than silently discarding this write.
|
|
49
|
+
const root = document.documentElement;
|
|
50
|
+
if (nextScheme === "default")
|
|
51
|
+
root.removeAttribute("data-color-scheme");
|
|
52
|
+
else
|
|
53
|
+
root.setAttribute("data-color-scheme", nextScheme);
|
|
54
|
+
}
|
|
55
|
+
if (options.persistenceKey === null)
|
|
56
|
+
return;
|
|
57
|
+
try {
|
|
58
|
+
if (typeof localStorage === "undefined")
|
|
59
|
+
return;
|
|
60
|
+
localStorage.setItem(options.persistenceKey ?? DEFAULT_COLOR_SCHEME_STORAGE_KEY, nextScheme);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// Storage may be unavailable (privacy mode, quota); the scheme still applies this session.
|
|
64
|
+
}
|
|
65
|
+
}
|
package/dist/color-schemes.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createBuiltInColorSchemeDescriptor, createPieColorSchemePreview, diagnoseThemeContrast, getBaseThemeVariables, getBuiltInColorSchemeDefinition, getDefaultColorSchemeDescriptor, getSchemeParticipation, listBuiltInColorSchemeDefinitions, } from "./theme-definitions.js";
|
|
1
|
+
import { createBuiltInColorSchemeDescriptor, createPieColorSchemePreview, diagnoseThemeContrast, getBaseThemeVariables, getBuiltInColorSchemeDefinition, getDefaultColorSchemeDescriptor, getSchemeParticipation, listBuiltInColorSchemeDefinitions, resolvePaletteColorScheme, } from "./theme-definitions.js";
|
|
2
2
|
import { normalizePieThemeVariables, } from "./theme-types.js";
|
|
3
3
|
const customSchemes = new Map();
|
|
4
4
|
const observers = new Set();
|
|
@@ -455,12 +455,20 @@ export function resolvePieTheme(input = {}) {
|
|
|
455
455
|
}
|
|
456
456
|
for (const item of diagnostics)
|
|
457
457
|
warnDiagnostic(item);
|
|
458
|
+
// Only a resolved scheme decides polarity. Without one -- including a requested
|
|
459
|
+
// scheme that turned out unavailable -- the host's theme still owns
|
|
460
|
+
// `color-scheme`, and deciding it from the base palette would take that
|
|
461
|
+
// ownership away from every host that never asked for an accommodation.
|
|
462
|
+
const colorScheme = status === "built-in" || status === "custom"
|
|
463
|
+
? resolvePaletteColorScheme(variables)
|
|
464
|
+
: null;
|
|
458
465
|
return Object.freeze({
|
|
459
466
|
baseTheme,
|
|
460
467
|
requestedScheme,
|
|
461
468
|
resolvedScheme,
|
|
462
469
|
status,
|
|
463
470
|
variables: freezeVariables(variables),
|
|
471
|
+
colorScheme,
|
|
464
472
|
diagnostics: freezeDiagnostics(diagnostics),
|
|
465
473
|
});
|
|
466
474
|
}
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The one table that says which DaisyUI slot each `--pie-*` token comes from,
|
|
3
|
-
* and one renderer that turns it into variables.
|
|
3
|
+
* and one renderer that turns it into variables. Sole source: the `daisyui`
|
|
4
|
+
* provider adapter is the only thing that reads it, and a host reaches it by
|
|
5
|
+
* mounting `<pie-theme provider="daisyui">` or leaving `provider` on `auto`.
|
|
4
6
|
*
|
|
5
|
-
* It
|
|
6
|
-
*
|
|
7
|
-
* that package's `bridge.css` — and copies drift. Two defects lived in the
|
|
8
|
-
* `--pie-missing` was corrected to `--color-warning` in one copy while three
|
|
9
|
-
* it on `--color-error`, and the parity test
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* this one by `tests/daisyui-mapping-parity.test.mjs` instead.
|
|
7
|
+
* It was consolidated here after the same table had been written out four
|
|
8
|
+
* times — this adapter, two mappers in a separate `pie-theme-daisyui` package, and
|
|
9
|
+
* that package's static `bridge.css` — and copies drift. Two defects lived in the
|
|
10
|
+
* drift: `--pie-missing` was corrected to `--color-warning` in one copy while three
|
|
11
|
+
* kept it on `--color-error`, and the parity test meant to catch that compared only
|
|
12
|
+
* the token names, never the slot each one derived from. The copies are gone; the
|
|
13
|
+
* package was removed once its CSS turned out to be outranked by the inline
|
|
14
|
+
* variables `<pie-theme>` writes, leaving it inert wherever it was imported.
|
|
14
15
|
*/
|
|
15
16
|
import { type ColorMeasure } from "./contrast.js";
|
|
16
17
|
/** The DaisyUI slots this mapping reads. Not all of DaisyUI's palette. */
|
package/dist/daisyui-mapping.js
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The one table that says which DaisyUI slot each `--pie-*` token comes from,
|
|
3
|
-
* and one renderer that turns it into variables.
|
|
3
|
+
* and one renderer that turns it into variables. Sole source: the `daisyui`
|
|
4
|
+
* provider adapter is the only thing that reads it, and a host reaches it by
|
|
5
|
+
* mounting `<pie-theme provider="daisyui">` or leaving `provider` on `auto`.
|
|
4
6
|
*
|
|
5
|
-
* It
|
|
6
|
-
*
|
|
7
|
-
* that package's `bridge.css` — and copies drift. Two defects lived in the
|
|
8
|
-
* `--pie-missing` was corrected to `--color-warning` in one copy while three
|
|
9
|
-
* it on `--color-error`, and the parity test
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* this one by `tests/daisyui-mapping-parity.test.mjs` instead.
|
|
7
|
+
* It was consolidated here after the same table had been written out four
|
|
8
|
+
* times — this adapter, two mappers in a separate `pie-theme-daisyui` package, and
|
|
9
|
+
* that package's static `bridge.css` — and copies drift. Two defects lived in the
|
|
10
|
+
* drift: `--pie-missing` was corrected to `--color-warning` in one copy while three
|
|
11
|
+
* kept it on `--color-error`, and the parity test meant to catch that compared only
|
|
12
|
+
* the token names, never the slot each one derived from. The copies are gone; the
|
|
13
|
+
* package was removed once its CSS turned out to be outranked by the inline
|
|
14
|
+
* variables `<pie-theme>` writes, leaving it inert wherever it was imported.
|
|
14
15
|
*/
|
|
15
16
|
import { LEGIBLE_NON_TEXT_MINIMUM, LEGIBLE_TEXT_MINIMUM, UNMEASURED_HUE_WEIGHT, UNMEASURED_NON_TEXT_HUE_WEIGHT, legibleColorAgainst, } from "./contrast.js";
|
|
16
17
|
export const DAISY_SLOT_CSS_VARIABLES = {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The full set of built-in DaisyUI theme ids. `<pie-theme>`'s DaisyUI
|
|
3
|
+
* provider adapter maps any of these onto the `--pie-*` token contract, so
|
|
4
|
+
* every demo host offering a DaisyUI theme picker draws from this one list
|
|
5
|
+
* instead of each hand-typing its own copy.
|
|
6
|
+
*/
|
|
7
|
+
export declare const DAISYUI_THEME_CATALOG: readonly ["light", "dark", "cupcake", "bumblebee", "emerald", "corporate", "synthwave", "retro", "cyberpunk", "valentine", "halloween", "garden", "forest", "aqua", "lofi", "pastel", "fantasy", "wireframe", "black", "luxury", "dracula", "cmyk", "autumn", "business", "acid", "lemonade", "night", "coffee", "winter", "dim", "nord", "sunset", "caramellatte", "abyss", "silk"];
|
|
8
|
+
export type DaisyUIThemeId = (typeof DAISYUI_THEME_CATALOG)[number];
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The full set of built-in DaisyUI theme ids. `<pie-theme>`'s DaisyUI
|
|
3
|
+
* provider adapter maps any of these onto the `--pie-*` token contract, so
|
|
4
|
+
* every demo host offering a DaisyUI theme picker draws from this one list
|
|
5
|
+
* instead of each hand-typing its own copy.
|
|
6
|
+
*/
|
|
7
|
+
export const DAISYUI_THEME_CATALOG = [
|
|
8
|
+
"light",
|
|
9
|
+
"dark",
|
|
10
|
+
"cupcake",
|
|
11
|
+
"bumblebee",
|
|
12
|
+
"emerald",
|
|
13
|
+
"corporate",
|
|
14
|
+
"synthwave",
|
|
15
|
+
"retro",
|
|
16
|
+
"cyberpunk",
|
|
17
|
+
"valentine",
|
|
18
|
+
"halloween",
|
|
19
|
+
"garden",
|
|
20
|
+
"forest",
|
|
21
|
+
"aqua",
|
|
22
|
+
"lofi",
|
|
23
|
+
"pastel",
|
|
24
|
+
"fantasy",
|
|
25
|
+
"wireframe",
|
|
26
|
+
"black",
|
|
27
|
+
"luxury",
|
|
28
|
+
"dracula",
|
|
29
|
+
"cmyk",
|
|
30
|
+
"autumn",
|
|
31
|
+
"business",
|
|
32
|
+
"acid",
|
|
33
|
+
"lemonade",
|
|
34
|
+
"night",
|
|
35
|
+
"coffee",
|
|
36
|
+
"winter",
|
|
37
|
+
"dim",
|
|
38
|
+
"nord",
|
|
39
|
+
"sunset",
|
|
40
|
+
"caramellatte",
|
|
41
|
+
"abyss",
|
|
42
|
+
"silk",
|
|
43
|
+
];
|
package/dist/font-sizes.css
CHANGED
|
@@ -1,141 +1,94 @@
|
|
|
1
|
-
/* PIE
|
|
1
|
+
/* PIE font size scaling.
|
|
2
2
|
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* - Extra Large (150% - 1.5x scale)
|
|
7
|
-
* - Extra Extra Large (175% - 1.75x scale)
|
|
3
|
+
* Four presets on `--pie-font-scale`, selected by `data-font-size` on any
|
|
4
|
+
* ancestor of the player — matching Learnosity's font-size accommodation
|
|
5
|
+
* (100/125/150/175%), which is the scale K-12 programs already specify against.
|
|
8
6
|
*
|
|
9
|
-
*
|
|
10
|
-
* -
|
|
11
|
-
* - Text remains readable and functional at all scale levels
|
|
12
|
-
* - No horizontal scrolling required in scaled states
|
|
7
|
+
* <html data-font-size="large"> host owns the whole page
|
|
8
|
+
* <div data-font-size="large"> host scopes it to a region
|
|
13
9
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
10
|
+
* A host that drives theming through `<pie-theme>` sets the token directly
|
|
11
|
+
* instead, and needs nothing from this file:
|
|
16
12
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
13
|
+
* <pie-theme variables='{"--pie-font-scale":"1.25"}'>
|
|
14
|
+
*
|
|
15
|
+
* WHAT SCALES. Text that inherits its size scales, because the rules below set
|
|
16
|
+
* `font-size` on the content hosts and inheritance crosses shadow boundaries.
|
|
17
|
+
* Text whose own rule names `rem` or `px` does not: `rem` resolves against the
|
|
18
|
+
* document root and `px` against nothing, and no rule inside a subtree can
|
|
19
|
+
* change what either means. A host that needs those to follow as well has to
|
|
20
|
+
* scale the root font size itself, which is a page-wide decision this package
|
|
21
|
+
* does not make on its behalf. WCAG 2.2 1.4.4 is satisfied either way — browser
|
|
22
|
+
* zoom remains available and unaffected — so this is a comfort accommodation
|
|
23
|
+
* layered over that, not a substitute for it.
|
|
24
|
+
*
|
|
25
|
+
* `calc(1rem * var(--pie-font-scale))` is deliberately root-relative rather than
|
|
26
|
+
* `em`-relative: the hosts nest (an item shell inside a section player inside a
|
|
27
|
+
* themed region), and an `em` factor would compound at each level and multiply
|
|
28
|
+
* 1.25 into 1.95.
|
|
19
29
|
*/
|
|
20
30
|
|
|
21
|
-
/*
|
|
31
|
+
/* The unset default. Also declared by the base theme, so the token resolves
|
|
32
|
+
* whether a host loads this file, `tokens.css`, or drives `<pie-theme>`. */
|
|
22
33
|
:root {
|
|
23
34
|
--pie-font-scale: 1;
|
|
24
35
|
}
|
|
25
36
|
|
|
26
|
-
/* Normal (100% - explicit setting) */
|
|
27
37
|
[data-font-size="normal"] {
|
|
28
38
|
--pie-font-scale: 1;
|
|
29
39
|
}
|
|
30
40
|
|
|
31
|
-
/* Large (125%) - Learnosity "large" preset */
|
|
32
41
|
[data-font-size="large"] {
|
|
33
42
|
--pie-font-scale: 1.25;
|
|
34
43
|
}
|
|
35
44
|
|
|
36
|
-
/* Extra Large (150%) - Learnosity "xlarge" preset */
|
|
37
45
|
[data-font-size="xlarge"] {
|
|
38
46
|
--pie-font-scale: 1.5;
|
|
39
47
|
}
|
|
40
48
|
|
|
41
|
-
/* Extra Extra Large (175%) - Learnosity "xxlarge" preset */
|
|
42
49
|
[data-font-size="xxlarge"] {
|
|
43
50
|
--pie-font-scale: 1.75;
|
|
44
51
|
}
|
|
45
52
|
|
|
46
|
-
/*
|
|
47
|
-
* Target PIE player elements and assessment content
|
|
48
|
-
* Exclude tool UIs (calculator, graph, etc.) which should remain at normal size
|
|
53
|
+
/* Content hosts.
|
|
49
54
|
*
|
|
50
|
-
*
|
|
55
|
+
* `pie-item-shell` and `pie-passage-shell` are this repo's section-player
|
|
56
|
+
* content hosts and `pie-item-player` its standalone one; each renders PIE
|
|
57
|
+
* elements directly into its own shadow tree. `pie-player` is the externally
|
|
58
|
+
* loaded item wrapper some hosts render items through instead, so both delivery
|
|
59
|
+
* paths are covered — targeting only the wrapper, as this file previously did,
|
|
60
|
+
* missed every item this repo renders itself.
|
|
61
|
+
*
|
|
62
|
+
* Scoped under `[data-font-size]` so the declaration only exists once a host has
|
|
63
|
+
* opted in, and so a host's own rules keep the upper hand at equal specificity.
|
|
51
64
|
*/
|
|
52
|
-
|
|
53
|
-
|
|
65
|
+
[data-font-size] pie-item-shell,
|
|
66
|
+
[data-font-size] pie-passage-shell,
|
|
67
|
+
[data-font-size] pie-item-player,
|
|
68
|
+
[data-font-size] pie-player,
|
|
69
|
+
pie-item-shell[data-font-size],
|
|
70
|
+
pie-passage-shell[data-font-size],
|
|
71
|
+
pie-item-player[data-font-size],
|
|
54
72
|
pie-player[data-font-size] {
|
|
55
|
-
font-size: calc(1rem * var(--pie-font-scale))
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/* Apply to all text elements within pie-player - using inherit to cascade from root */
|
|
59
|
-
pie-player[data-font-size] *,
|
|
60
|
-
pie-player[data-font-size] p,
|
|
61
|
-
pie-player[data-font-size] span,
|
|
62
|
-
pie-player[data-font-size] div,
|
|
63
|
-
pie-player[data-font-size] li,
|
|
64
|
-
pie-player[data-font-size] label,
|
|
65
|
-
pie-player[data-font-size] input,
|
|
66
|
-
pie-player[data-font-size] textarea,
|
|
67
|
-
pie-player[data-font-size] select,
|
|
68
|
-
pie-player[data-font-size] button,
|
|
69
|
-
pie-player[data-font-size] h1,
|
|
70
|
-
pie-player[data-font-size] h2,
|
|
71
|
-
pie-player[data-font-size] h3,
|
|
72
|
-
pie-player[data-font-size] h4,
|
|
73
|
-
pie-player[data-font-size] h5,
|
|
74
|
-
pie-player[data-font-size] h6,
|
|
75
|
-
pie-player[data-font-size] td,
|
|
76
|
-
pie-player[data-font-size] th,
|
|
77
|
-
pie-player[data-font-size] option {
|
|
78
|
-
font-size: inherit !important;
|
|
79
|
-
}
|
|
73
|
+
font-size: calc(1rem * var(--pie-font-scale, 1));
|
|
80
74
|
|
|
81
|
-
/*
|
|
82
|
-
|
|
83
|
-
font-size: calc(1rem * var(--pie-font-scale)) !important;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
[data-font-size] pie-player *,
|
|
87
|
-
[data-font-size] pie-player p,
|
|
88
|
-
[data-font-size] pie-player span,
|
|
89
|
-
[data-font-size] pie-player div,
|
|
90
|
-
[data-font-size] pie-player li,
|
|
91
|
-
[data-font-size] pie-player label,
|
|
92
|
-
[data-font-size] pie-player input,
|
|
93
|
-
[data-font-size] pie-player textarea,
|
|
94
|
-
[data-font-size] pie-player select,
|
|
95
|
-
[data-font-size] pie-player button,
|
|
96
|
-
[data-font-size] pie-player h1,
|
|
97
|
-
[data-font-size] pie-player h2,
|
|
98
|
-
[data-font-size] pie-player h3,
|
|
99
|
-
[data-font-size] pie-player h4,
|
|
100
|
-
[data-font-size] pie-player h5,
|
|
101
|
-
[data-font-size] pie-player h6,
|
|
102
|
-
[data-font-size] pie-player td,
|
|
103
|
-
[data-font-size] pie-player th,
|
|
104
|
-
[data-font-size] pie-player option {
|
|
105
|
-
font-size: inherit !important;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/* Preserve normal size for tool UIs and controls */
|
|
109
|
-
[data-font-size] .pie-tool-toolbar,
|
|
110
|
-
[data-font-size] .pie-tool-toolbar *,
|
|
111
|
-
[data-font-size] .pie-tool-calculator,
|
|
112
|
-
[data-font-size] .pie-tool-calculator *,
|
|
113
|
-
[data-font-size] .tool-settings-panel,
|
|
114
|
-
[data-font-size] .tool-settings-panel *,
|
|
115
|
-
[data-font-size] [class*="pie-tool-"],
|
|
116
|
-
[data-font-size] [class*="pie-tool-"] *,
|
|
117
|
-
[data-font-size] header,
|
|
118
|
-
[data-font-size] header *,
|
|
119
|
-
[data-font-size] footer,
|
|
120
|
-
[data-font-size] footer *,
|
|
121
|
-
[data-font-size] nav,
|
|
122
|
-
[data-font-size] nav *,
|
|
123
|
-
[data-font-size] button:not(.pie-button),
|
|
124
|
-
[data-font-size] .btn,
|
|
125
|
-
[data-font-size] .btn * {
|
|
126
|
-
font-size: 1rem !important;
|
|
75
|
+
/* Long words at 175% overflow a column that fits them at 100%. */
|
|
76
|
+
overflow-wrap: break-word;
|
|
127
77
|
}
|
|
128
78
|
|
|
129
|
-
/*
|
|
79
|
+
/* Line height has to open up with the type or the block tightens as it grows.
|
|
80
|
+
* A ratio, so a host that sets its own line-height still wins. */
|
|
81
|
+
[data-font-size="large"] pie-item-shell,
|
|
82
|
+
[data-font-size="large"] pie-passage-shell,
|
|
83
|
+
[data-font-size="large"] pie-item-player,
|
|
130
84
|
[data-font-size="large"] pie-player,
|
|
85
|
+
[data-font-size="xlarge"] pie-item-shell,
|
|
86
|
+
[data-font-size="xlarge"] pie-passage-shell,
|
|
87
|
+
[data-font-size="xlarge"] pie-item-player,
|
|
131
88
|
[data-font-size="xlarge"] pie-player,
|
|
89
|
+
[data-font-size="xxlarge"] pie-item-shell,
|
|
90
|
+
[data-font-size="xxlarge"] pie-passage-shell,
|
|
91
|
+
[data-font-size="xxlarge"] pie-item-player,
|
|
132
92
|
[data-font-size="xxlarge"] pie-player {
|
|
133
93
|
line-height: 1.6;
|
|
134
94
|
}
|
|
135
|
-
|
|
136
|
-
/* Prevent layout issues with scaled text */
|
|
137
|
-
[data-font-size] pie-player {
|
|
138
|
-
overflow-wrap: break-word;
|
|
139
|
-
word-wrap: break-word;
|
|
140
|
-
hyphens: auto;
|
|
141
|
-
}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,9 @@ import { PieThemeElement, definePieTheme } from "./theme-element.js";
|
|
|
2
2
|
export { PieThemeElement, definePieTheme };
|
|
3
3
|
export { DAISYUI_THEME_PROVIDER_ADAPTER, getPieThemeProvider, PIE_THEME_PROVIDER_NONE, listPieThemeProviders, registerPieThemeProvider, resolveProviderVariables, unregisterPieThemeProvider, type ThemeProviderAdapter, } from "./providers.js";
|
|
4
4
|
export { DAISY_SLOT_CSS_VARIABLES, DAISYUI_PIE_TOKEN_MAP, resolveDaisyPieVariables, type DaisyMappingEntry, type DaisySlot, } from "./daisyui-mapping.js";
|
|
5
|
+
export { DAISYUI_THEME_CATALOG, type DaisyUIThemeId, } from "./daisyui-theme-catalog.js";
|
|
5
6
|
export { createCanvasColorMeasure, type ColorMeasure, } from "./contrast.js";
|
|
6
7
|
export { isThemeMode, isThemeScope, normalizePieThemeVariables, type ColorSchemeSnapshot, type PieColorSchemeDescriptor, type PieColorSchemePreview, type PieThemeDiagnostic, type PieThemeDiagnosticCode, type PieThemeObserver, type PieThemeResolutionStatus, type RegisteredPieColorScheme, type RegistrationReceipt, type ResolvePieThemeInput, type ThemeResolution, type ThemeMode, type ThemeScope, type ThemeTokenName, type ThemeVariables, type Unsubscribe, } from "./theme-types.js";
|
|
7
8
|
export type { PieThemeSchemeParticipation, PieThemeTokenRegistry, PieThemeTokenRegistryEntry, PieThemeTokenScope, PieThemeTokenStatus, } from "./token-registry-types.js";
|
|
8
9
|
export { listPieColorSchemes, observePieColorSchemes, registerPieColorSchemes, resolvePieTheme, } from "./color-schemes.js";
|
|
10
|
+
export { applyPieColorScheme, resolvePieThemeHost, type ApplyPieColorSchemeOptions, } from "./apply-color-scheme.js";
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,9 @@ import { PieThemeElement, definePieTheme } from "./theme-element.js";
|
|
|
2
2
|
export { PieThemeElement, definePieTheme };
|
|
3
3
|
export { DAISYUI_THEME_PROVIDER_ADAPTER, getPieThemeProvider, PIE_THEME_PROVIDER_NONE, listPieThemeProviders, registerPieThemeProvider, resolveProviderVariables, unregisterPieThemeProvider, } from "./providers.js";
|
|
4
4
|
export { DAISY_SLOT_CSS_VARIABLES, DAISYUI_PIE_TOKEN_MAP, resolveDaisyPieVariables, } from "./daisyui-mapping.js";
|
|
5
|
+
export { DAISYUI_THEME_CATALOG, } from "./daisyui-theme-catalog.js";
|
|
5
6
|
export { createCanvasColorMeasure, } from "./contrast.js";
|
|
6
7
|
export { isThemeMode, isThemeScope, normalizePieThemeVariables, } from "./theme-types.js";
|
|
7
8
|
export { listPieColorSchemes, observePieColorSchemes, registerPieColorSchemes, resolvePieTheme, } from "./color-schemes.js";
|
|
9
|
+
export { applyPieColorScheme, resolvePieThemeHost, } from "./apply-color-scheme.js";
|
|
8
10
|
definePieTheme();
|
|
@@ -6,6 +6,23 @@ type BuiltInColorSchemeDefinition = Readonly<{
|
|
|
6
6
|
description?: string;
|
|
7
7
|
variables: Readonly<ThemeVariables>;
|
|
8
8
|
}>;
|
|
9
|
+
/**
|
|
10
|
+
* The CSS `color-scheme` keyword a palette implies, or `null` when the token
|
|
11
|
+
* values cannot decide it.
|
|
12
|
+
*
|
|
13
|
+
* A scheme replaces every colour, but nothing in CSS infers polarity from custom
|
|
14
|
+
* properties, so UA-styled controls -- `input`, `select`, scrollbars, native form
|
|
15
|
+
* widgets -- keep whatever polarity the host's theme declared. A dark
|
|
16
|
+
* accommodation on a light host therefore renders their text in the light-mode
|
|
17
|
+
* system colour: measured at roughly 1.1:1 against `yellow-on-blue`.
|
|
18
|
+
*
|
|
19
|
+
* Decided by whether black or white contrasts better against the resolved
|
|
20
|
+
* background, which is the same test that picks a legible foreground. `null` for
|
|
21
|
+
* a background `parseOpaqueColor` rejects -- a translucent value, a `var()`
|
|
22
|
+
* reference out to a host property, the transparent light base -- because
|
|
23
|
+
* polarity then depends on the host's backdrop and is the host's to declare.
|
|
24
|
+
*/
|
|
25
|
+
export declare function resolvePaletteColorScheme(variables: Readonly<ThemeVariables>): "light" | "dark" | null;
|
|
9
26
|
export declare function diagnoseThemeContrast(variables: Readonly<ThemeVariables>, schemeId?: string, relevantTokens?: ReadonlySet<string>): readonly PieThemeDiagnostic[];
|
|
10
27
|
export declare function getBaseThemeVariables(baseTheme: "light" | "dark"): Readonly<ThemeVariables>;
|
|
11
28
|
export declare function listBuiltInColorSchemeDefinitions(): readonly BuiltInColorSchemeDefinition[];
|
|
@@ -807,6 +807,17 @@ const PIE_THEME_CONTRAST_RELATIONSHIPS = deepFreeze([
|
|
|
807
807
|
minimum: 3,
|
|
808
808
|
role: "incorrect feedback icon",
|
|
809
809
|
},
|
|
810
|
+
{
|
|
811
|
+
// The error-surface pair. `--pie-incorrect-secondary` is a tint of the
|
|
812
|
+
// page, about 1.1:1 against it in most schemes, so a banner painted with
|
|
813
|
+
// it reads as a banner only by its `--pie-incorrect` edge -- and the ink
|
|
814
|
+
// on it is the page's own, not the error hue: `--pie-incorrect` against
|
|
815
|
+
// this tint falls to 4.14:1 under Black on White.
|
|
816
|
+
foreground: "--pie-text",
|
|
817
|
+
background: "--pie-incorrect-secondary",
|
|
818
|
+
minimum: 4.5,
|
|
819
|
+
role: "incorrect feedback surface text",
|
|
820
|
+
},
|
|
810
821
|
{
|
|
811
822
|
foreground: "--pie-missing-icon",
|
|
812
823
|
background: "--pie-background",
|
|
@@ -958,6 +969,31 @@ function resolveColor(variables, token, seen = new Set()) {
|
|
|
958
969
|
? resolveColor(variables, reference[1], seen)
|
|
959
970
|
: value;
|
|
960
971
|
}
|
|
972
|
+
/**
|
|
973
|
+
* The CSS `color-scheme` keyword a palette implies, or `null` when the token
|
|
974
|
+
* values cannot decide it.
|
|
975
|
+
*
|
|
976
|
+
* A scheme replaces every colour, but nothing in CSS infers polarity from custom
|
|
977
|
+
* properties, so UA-styled controls -- `input`, `select`, scrollbars, native form
|
|
978
|
+
* widgets -- keep whatever polarity the host's theme declared. A dark
|
|
979
|
+
* accommodation on a light host therefore renders their text in the light-mode
|
|
980
|
+
* system colour: measured at roughly 1.1:1 against `yellow-on-blue`.
|
|
981
|
+
*
|
|
982
|
+
* Decided by whether black or white contrasts better against the resolved
|
|
983
|
+
* background, which is the same test that picks a legible foreground. `null` for
|
|
984
|
+
* a background `parseOpaqueColor` rejects -- a translucent value, a `var()`
|
|
985
|
+
* reference out to a host property, the transparent light base -- because
|
|
986
|
+
* polarity then depends on the host's backdrop and is the host's to declare.
|
|
987
|
+
*/
|
|
988
|
+
export function resolvePaletteColorScheme(variables) {
|
|
989
|
+
const value = resolveColor(variables, "--pie-background");
|
|
990
|
+
const background = value ? parseOpaqueColor(value) : null;
|
|
991
|
+
if (!background)
|
|
992
|
+
return null;
|
|
993
|
+
const againstWhite = contrastRatio({ r: 255, g: 255, b: 255 }, background);
|
|
994
|
+
const againstBlack = contrastRatio({ r: 0, g: 0, b: 0 }, background);
|
|
995
|
+
return againstWhite > againstBlack ? "dark" : "light";
|
|
996
|
+
}
|
|
961
997
|
export function diagnoseThemeContrast(variables, schemeId, relevantTokens) {
|
|
962
998
|
const diagnostics = [];
|
|
963
999
|
for (const relationship of PIE_THEME_CONTRAST_RELATIONSHIPS) {
|
package/dist/theme-element.d.ts
CHANGED
|
@@ -28,6 +28,22 @@ export declare class PieThemeElement extends HTMLElementBase {
|
|
|
28
28
|
attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
|
|
29
29
|
protected getTarget(): HTMLElement;
|
|
30
30
|
protected applyTheme(): void;
|
|
31
|
+
/**
|
|
32
|
+
* Provider adapters resolve by reading custom properties off the target, and
|
|
33
|
+
* those are selected by the target's own `data-theme` -- daisyUI's `--color-*`
|
|
34
|
+
* are the case that matters. So the target has to be carrying the incoming
|
|
35
|
+
* theme before the read: resolving first resolves the palette of the theme
|
|
36
|
+
* being replaced and leaves every `--pie-*` value one selection behind, with
|
|
37
|
+
* nothing to re-resolve it until some other attribute changes.
|
|
38
|
+
*
|
|
39
|
+
* The attributes are restored rather than left in place. For
|
|
40
|
+
* `scope="document"` the ownership arbitration in `applyTheme` decides whether
|
|
41
|
+
* this element may stamp them at all, and `ensureDocumentThemeBaseline` has to
|
|
42
|
+
* see the host's pre-existing value to have something to restore on
|
|
43
|
+
* disconnect. Stamp and restore happen in one synchronous pass, so the
|
|
44
|
+
* transient state is never painted.
|
|
45
|
+
*/
|
|
46
|
+
private resolveProviderVariablesForState;
|
|
31
47
|
private resolveThemeState;
|
|
32
48
|
private clearPreviousTarget;
|
|
33
49
|
private clearPreviousKeys;
|
package/dist/theme-element.js
CHANGED
|
@@ -48,6 +48,31 @@ function parseVariableOverrides(value) {
|
|
|
48
48
|
}
|
|
49
49
|
return output;
|
|
50
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* `color-scheme` decides how UA-styled controls paint -- `input` and `select`
|
|
53
|
+
* text, scrollbars, native form widgets -- and no amount of custom properties
|
|
54
|
+
* reaches them. A scheme that replaces the palette without it leaves those
|
|
55
|
+
* controls on the host theme's polarity, which under a dark accommodation on a
|
|
56
|
+
* light host means dark-on-dark.
|
|
57
|
+
*
|
|
58
|
+
* `null` restores rather than removes. Clearing a scheme has to undo this
|
|
59
|
+
* element's keyword, and the thing to return to is whatever the host declared --
|
|
60
|
+
* removing outright would take a host's own `color-scheme` away the first time a
|
|
61
|
+
* document-scoped element resolved without a scheme, which is most of the time.
|
|
62
|
+
* A self-scoped target has no host declaration to preserve, so its caller passes
|
|
63
|
+
* no baseline and `null` does remove.
|
|
64
|
+
*/
|
|
65
|
+
function applyColorScheme(target, colorScheme, baseline) {
|
|
66
|
+
if (colorScheme) {
|
|
67
|
+
target.style.setProperty("color-scheme", colorScheme);
|
|
68
|
+
}
|
|
69
|
+
else if (baseline) {
|
|
70
|
+
target.style.setProperty("color-scheme", baseline.value, baseline.priority);
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
target.style.removeProperty("color-scheme");
|
|
74
|
+
}
|
|
75
|
+
}
|
|
51
76
|
const documentThemeOwners = new Map();
|
|
52
77
|
let documentThemeBaseline = null;
|
|
53
78
|
let documentThemeAppliedKeys = new Set();
|
|
@@ -55,6 +80,12 @@ function ensureDocumentThemeBaseline(target, tokens) {
|
|
|
55
80
|
documentThemeBaseline ??= {
|
|
56
81
|
dataTheme: target.getAttribute("data-theme"),
|
|
57
82
|
dataColorScheme: target.getAttribute("data-color-scheme"),
|
|
83
|
+
colorScheme: target.style.getPropertyValue("color-scheme")
|
|
84
|
+
? {
|
|
85
|
+
value: target.style.getPropertyValue("color-scheme"),
|
|
86
|
+
priority: target.style.getPropertyPriority("color-scheme"),
|
|
87
|
+
}
|
|
88
|
+
: null,
|
|
58
89
|
variables: new Map(),
|
|
59
90
|
};
|
|
60
91
|
for (const token of tokens) {
|
|
@@ -92,6 +123,7 @@ function applyDocumentThemeState(target, state) {
|
|
|
92
123
|
else {
|
|
93
124
|
target.removeAttribute("data-color-scheme");
|
|
94
125
|
}
|
|
126
|
+
applyColorScheme(target, state.colorScheme, documentThemeBaseline?.colorScheme ?? null);
|
|
95
127
|
for (const [token, value] of Object.entries(state.variables)) {
|
|
96
128
|
target.style.setProperty(token, value);
|
|
97
129
|
}
|
|
@@ -111,6 +143,12 @@ function restoreDocumentThemeBaseline(target) {
|
|
|
111
143
|
else {
|
|
112
144
|
target.setAttribute("data-color-scheme", baseline.dataColorScheme);
|
|
113
145
|
}
|
|
146
|
+
if (baseline.colorScheme) {
|
|
147
|
+
target.style.setProperty("color-scheme", baseline.colorScheme.value, baseline.colorScheme.priority);
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
target.style.removeProperty("color-scheme");
|
|
151
|
+
}
|
|
114
152
|
restoreDocumentThemeVariables(target);
|
|
115
153
|
documentThemeBaseline = null;
|
|
116
154
|
documentThemeAppliedKeys.clear();
|
|
@@ -209,10 +247,8 @@ export class PieThemeElement extends HTMLElementBase {
|
|
|
209
247
|
if (this.previousTarget && this.previousTarget !== target) {
|
|
210
248
|
this.clearPreviousTarget();
|
|
211
249
|
}
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
provider: this.provider,
|
|
215
|
-
});
|
|
250
|
+
const dataColorScheme = this.scheme === "default" ? null : this.scheme;
|
|
251
|
+
const providerVariables = this.resolveProviderVariablesForState(target, dataTheme, dataColorScheme);
|
|
216
252
|
const resolution = resolvePieTheme({
|
|
217
253
|
baseTheme: effectiveTheme,
|
|
218
254
|
providerVariables,
|
|
@@ -221,7 +257,8 @@ export class PieThemeElement extends HTMLElementBase {
|
|
|
221
257
|
});
|
|
222
258
|
const state = Object.freeze({
|
|
223
259
|
dataTheme,
|
|
224
|
-
dataColorScheme
|
|
260
|
+
dataColorScheme,
|
|
261
|
+
colorScheme: resolution.colorScheme,
|
|
225
262
|
variables: resolution.variables,
|
|
226
263
|
});
|
|
227
264
|
if (this.scope === "document") {
|
|
@@ -245,6 +282,7 @@ export class PieThemeElement extends HTMLElementBase {
|
|
|
245
282
|
else {
|
|
246
283
|
target.removeAttribute("data-color-scheme");
|
|
247
284
|
}
|
|
285
|
+
applyColorScheme(target, state.colorScheme, null);
|
|
248
286
|
this.clearPreviousKeys(target);
|
|
249
287
|
for (const [key, value] of Object.entries(resolution.variables)) {
|
|
250
288
|
target.style.setProperty(key, value);
|
|
@@ -253,6 +291,40 @@ export class PieThemeElement extends HTMLElementBase {
|
|
|
253
291
|
this.previousTarget = target;
|
|
254
292
|
this.previousKeys = new Set(Object.keys(resolution.variables));
|
|
255
293
|
}
|
|
294
|
+
/**
|
|
295
|
+
* Provider adapters resolve by reading custom properties off the target, and
|
|
296
|
+
* those are selected by the target's own `data-theme` -- daisyUI's `--color-*`
|
|
297
|
+
* are the case that matters. So the target has to be carrying the incoming
|
|
298
|
+
* theme before the read: resolving first resolves the palette of the theme
|
|
299
|
+
* being replaced and leaves every `--pie-*` value one selection behind, with
|
|
300
|
+
* nothing to re-resolve it until some other attribute changes.
|
|
301
|
+
*
|
|
302
|
+
* The attributes are restored rather than left in place. For
|
|
303
|
+
* `scope="document"` the ownership arbitration in `applyTheme` decides whether
|
|
304
|
+
* this element may stamp them at all, and `ensureDocumentThemeBaseline` has to
|
|
305
|
+
* see the host's pre-existing value to have something to restore on
|
|
306
|
+
* disconnect. Stamp and restore happen in one synchronous pass, so the
|
|
307
|
+
* transient state is never painted.
|
|
308
|
+
*/
|
|
309
|
+
resolveProviderVariablesForState(target, dataTheme, dataColorScheme) {
|
|
310
|
+
const setOrRemove = (name, value) => {
|
|
311
|
+
if (value === null)
|
|
312
|
+
target.removeAttribute(name);
|
|
313
|
+
else
|
|
314
|
+
target.setAttribute(name, value);
|
|
315
|
+
};
|
|
316
|
+
const previousTheme = target.getAttribute("data-theme");
|
|
317
|
+
const previousColorScheme = target.getAttribute("data-color-scheme");
|
|
318
|
+
setOrRemove("data-theme", dataTheme);
|
|
319
|
+
setOrRemove("data-color-scheme", dataColorScheme);
|
|
320
|
+
try {
|
|
321
|
+
return resolveProviderVariables({ target, provider: this.provider });
|
|
322
|
+
}
|
|
323
|
+
finally {
|
|
324
|
+
setOrRemove("data-theme", previousTheme);
|
|
325
|
+
setOrRemove("data-color-scheme", previousColorScheme);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
256
328
|
resolveThemeState() {
|
|
257
329
|
const rawTheme = this.getAttribute("theme")?.trim();
|
|
258
330
|
if (rawTheme === "auto") {
|
|
@@ -286,6 +358,9 @@ export class PieThemeElement extends HTMLElementBase {
|
|
|
286
358
|
this.clearPreviousKeys(target);
|
|
287
359
|
target.removeAttribute("data-theme");
|
|
288
360
|
target.removeAttribute("data-color-scheme");
|
|
361
|
+
// Self-scoped targets are the element itself, so anything written here
|
|
362
|
+
// was written by this element -- no host baseline to preserve.
|
|
363
|
+
target.style.removeProperty("color-scheme");
|
|
289
364
|
}
|
|
290
365
|
this.previousTarget = null;
|
|
291
366
|
this.previousKeys.clear();
|
package/dist/theme-types.d.ts
CHANGED
|
@@ -41,6 +41,11 @@ export type ThemeResolution = Readonly<{
|
|
|
41
41
|
resolvedScheme: PieColorSchemeDescriptor | null;
|
|
42
42
|
status: PieThemeResolutionStatus;
|
|
43
43
|
variables: Readonly<ThemeVariables>;
|
|
44
|
+
/**
|
|
45
|
+
* The CSS `color-scheme` keyword this resolution implies, or `null` when the
|
|
46
|
+
* host keeps ownership of it -- which is every resolution without a scheme.
|
|
47
|
+
*/
|
|
48
|
+
colorScheme: "light" | "dark" | null;
|
|
44
49
|
diagnostics: readonly PieThemeDiagnostic[];
|
|
45
50
|
}>;
|
|
46
51
|
export type RegisteredPieColorScheme = Readonly<{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pie-players/pie-theme",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.68",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Shared PIE theme tokens, css variables, and theme custom element",
|
|
6
6
|
"license": "MIT",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"test": "bun test --dom tests"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
|
-
"@biomejs/biome": "^2.5.
|
|
49
|
+
"@biomejs/biome": "^2.5.8",
|
|
50
50
|
"@happy-dom/global-registrator": "^20.11.1",
|
|
51
51
|
"typescript": "^5.9.3"
|
|
52
52
|
},
|