@bluepic/embed 0.4.0-next.110 → 0.4.0-next.111

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.
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Legacy variable name (without leading `--`) → CSS value expression.
3
+ * May use `var(--bx-<semantic>)` and plain CSS (px, calc, shorthands).
4
+ */
5
+ export declare const LEGACY_BRIDGE: Record<string, string>;
6
+ /**
7
+ * Legacy keys whose CSS custom property IS a semantic token (`--<key>` === `--bx-<semantic>`),
8
+ * e.g. `bx-focus-ring-width` ↔ `--bx-focus-ring-width`. Their bridge value would reference itself.
9
+ * They are listed in `LEGACY_BRIDGE` for coverage only — emitters MUST skip them (the resolver
10
+ * already emits the semantic token with the same name).
11
+ */
12
+ export declare const LEGACY_BRIDGE_IDENTITY_KEYS: ReadonlySet<string>;
13
+ /** `LEGACY_BRIDGE` minus the identity keys — the entries an emitter should actually write out. */
14
+ export declare const LEGACY_BRIDGE_ENTRIES: ReadonlyArray<readonly [legacyName: string, value: string]>;
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Bluepic theme system v2 — colour utilities.
3
+ *
4
+ * Pure, dependency-free, SSR-safe (no Vue, no DOM). Everything the theme engine needs to
5
+ * turn "any CSS colour string" into numbers, reason about it perceptually (OKLab / OKLCH,
6
+ * Björn Ottosson 2020) and for accessibility (WCAG 2.x relative luminance / contrast), and
7
+ * serialise it back into CSS.
8
+ *
9
+ * Conventions
10
+ * RGB — r,g,b in 0..255 (floats allowed internally, rounded on format), a in 0..1
11
+ * OKLCH — l in 0..1, c ≥ 0, h in degrees 0..360
12
+ */
13
+ export type RGB = {
14
+ r: number;
15
+ g: number;
16
+ b: number;
17
+ a: number;
18
+ };
19
+ export type OKLCH = {
20
+ l: number;
21
+ c: number;
22
+ h: number;
23
+ a: number;
24
+ };
25
+ export declare const WHITE: RGB;
26
+ export declare const BLACK: RGB;
27
+ /**
28
+ * Parse any CSS colour: `#rgb` / `#rgba` / `#rrggbb` / `#rrggbbaa`, `rgb()` / `rgba()` (comma AND modern space
29
+ * syntax, `%`, `none`, alpha as 0..1 or `%`), `hsl()` / `hsla()` (`deg` / `turn` / `rad` / `grad`, `%`), all CSS
30
+ * named colours, `transparent`, and a bare legacy triplet `"r, g, b"` (→ a = 1).
31
+ * Case-insensitive, trims whitespace. Returns `null` for anything else (e.g. values containing `var()`).
32
+ */
33
+ export declare function parseColor(input: string | null | undefined): RGB | null;
34
+ /** sRGB → linear → OKLab → OKLCH. */
35
+ export declare function toOklch(c: RGB): OKLCH;
36
+ /** OKLCH → sRGB with gamut mapping (see `oklchToRgbMapped`). */
37
+ export declare function fromOklch(c: OKLCH): RGB;
38
+ /** `#rrggbb`, or `#rrggbbaa` when a < 1. */
39
+ export declare function formatHex(c: RGB): string;
40
+ /** Legacy comma syntax: `rgb(r, g, b)` when a ≥ 1, else `rgba(r, g, b, a)` (ints for rgb, alpha ≤ 3 decimals). */
41
+ export declare function formatRgb(c: RGB): string;
42
+ /** `r, g, b` (ints) — the legacy `*-rgb` triplet for `rgba(var(--x), α)` usage. Alpha is dropped. */
43
+ export declare function formatTriplet(c: RGB): string;
44
+ /** WCAG 2.x relative luminance (0..1). Alpha is ignored. */
45
+ export declare function relativeLuminance(c: RGB): number;
46
+ /**
47
+ * WCAG 2.x contrast ratio (1..21).
48
+ * If `fg.a < 1` the foreground is composited over `bg` first. `bg` is ALWAYS treated as opaque — its alpha is
49
+ * ignored (what it would actually sit on is unknown here; callers that know should composite it themselves).
50
+ */
51
+ export declare function contrastRatio(fg: RGB, bg: RGB): number;
52
+ /** Alpha-over (`fg` over `bg`). Result alpha = fg.a + bg.a·(1 − fg.a). Fully transparent result → {0,0,0,0}. */
53
+ export declare function composite(fg: RGB, bg: RGB): RGB;
54
+ /**
55
+ * Perceptual mix in OKLab (t 0..1; 0 → a, 1 → b). Alpha interpolates linearly; the colour channels are
56
+ * interpolated alpha-premultiplied (as CSS `color-mix()` does) so fading towards a transparent colour keeps
57
+ * the hue of the opaque one. The result is gamut-mapped back into sRGB.
58
+ */
59
+ export declare function mix(a: RGB, b: RGB, t: number): RGB;
60
+ export declare function withAlpha(c: RGB, a: number): RGB;
61
+ /** OKLCH lightness (0..1). */
62
+ export declare function lightness(c: RGB): number;
63
+ /** Shift OKLCH lightness by `dl` (clamped to 0..1), hue/chroma kept, gamut-mapped. */
64
+ export declare function adjustL(c: RGB, dl: number): RGB;
65
+ /** Set OKLCH lightness to `l` (clamped to 0..1), hue/chroma kept, gamut-mapped. */
66
+ export declare function setL(c: RGB, l: number): RGB;
67
+ /**
68
+ * Move `fg`'s OKLCH lightness away from `bg` until `contrastRatio(fg, bg) >= min`, keeping hue/chroma as far as
69
+ * possible (chroma is reduced progressively only if the target can't be reached at the extremes).
70
+ * `direction: 'auto'` picks whichever side (lighter or darker than `bg`) can reach the higher contrast.
71
+ * If `fg` already satisfies `min` it is returned unchanged. If `min` is unreachable the best achievable colour is
72
+ * returned. `fg`'s alpha is preserved (and taken into account through `contrastRatio`'s compositing).
73
+ * The result satisfies `min` both as returned (floats) and after 8-bit rounding (`formatHex` / `formatRgb`).
74
+ * Bounded: ≤ 5 chroma levels × (1 + 24) lightness probes per direction.
75
+ */
76
+ export declare function ensureContrast(fg: RGB, bg: RGB, min: number, direction?: 'auto' | 'lighter' | 'darker'): RGB;
77
+ /** True when white contrasts at least as well against `c` as black does (i.e. `c` wants light ink). */
78
+ export declare function isDarkSurface(c: RGB): boolean;
79
+ /** The candidate with the highest contrast on `bg`. Default candidates: white and #111111. */
80
+ export declare function bestOn(bg: RGB, candidates?: RGB[]): RGB;
@@ -0,0 +1,8 @@
1
+ import type { ResolvedTheme } from './resolve';
2
+ /** All custom properties (keys include the leading `--`). */
3
+ export declare function themeToCssVars(theme: ResolvedTheme): Record<string, string>;
4
+ /**
5
+ * CSS text for one or more selectors. Also sets `color-scheme` so UA-painted controls,
6
+ * scrollbars and form elements follow the theme.
7
+ */
8
+ export declare function themeToCssText(theme: ResolvedTheme, selectors: string | string[]): string;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @bluepic/embed — theme system v2 (pure, SSR-safe).
3
+ *
4
+ * import { resolveTheme, themeToCssText, themeColor } from '@bluepic/embed';
5
+ * const theme = resolveTheme({ version: 2, surface: '#000', primary: '#ffd400' });
6
+ * theme.tokens['on-primary'] // → contrast-corrected foreground
7
+ * theme.report.checks // → WCAG pairs with pass/fail
8
+ * themeToCssText(theme, '.my-root');
9
+ * themeColor.contrastRatio(themeColor.parseColor('#fff')!, themeColor.parseColor('#000')!); // 21
10
+ *
11
+ * The DOM-binding (useThemeV2 / applyTheme / legacy useTheme) lives in ../util/useTheme.ts.
12
+ */
13
+ export * from './tokens';
14
+ export * from './resolve';
15
+ export * from './css';
16
+ export * from './legacy';
17
+ export { LEGACY_BRIDGE } from './bridge';
18
+ export * as themeColor from './color';
19
+ export type { RGB as ThemeRGB, OKLCH as ThemeOKLCH } from './color';
@@ -0,0 +1,3 @@
1
+ import type { ThemeInput } from './tokens';
2
+ export declare function looksLikeThemeInput(x: unknown): x is ThemeInput;
3
+ export declare function themeInputFromLegacyCustomStyle(customStyle: Record<string, string> | undefined | null): ThemeInput;
@@ -0,0 +1,8 @@
1
+ import { type SemanticTokens, type ThemeInput, type ThemeReport } from './tokens';
2
+ export type ResolvedTheme = {
3
+ input: ThemeInput;
4
+ mode: 'dark' | 'light';
5
+ tokens: SemanticTokens;
6
+ report: ThemeReport;
7
+ };
8
+ export declare function resolveTheme(rawInput: ThemeInput | undefined | null): ResolvedTheme;
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Bluepic theme system v2 — token vocabulary.
3
+ *
4
+ * Three tiers:
5
+ * 0. ThemeInput — what a user/host actually chooses (a handful of values).
6
+ * 1. Semantic tokens — `--bx-*`, ~60 role tokens derived from the input by `resolveTheme()`.
7
+ * Dark and light differ ONLY here.
8
+ * 2. Component tokens — the historical `--button-*`, `--input-*`, … variables that the
9
+ * components read today. They are defined ONCE, mode-agnostic, as
10
+ * references to semantic tokens (see ./bridge.ts). No more
11
+ * darkmode.scss / lightmode.scss forks.
12
+ *
13
+ * Everything in this file is pure data/types: no Vue, no DOM, SSR-safe.
14
+ */
15
+ export type ThemePresetName = 'light' | 'dark';
16
+ export type ThemeContrast = 'normal' | 'high';
17
+ export type ThemeRadius = 'none' | 'sm' | 'md' | 'lg' | 'full';
18
+ /**
19
+ * What a campaign (or any host) persists. Small, human-readable, forward-compatible:
20
+ * the library derives everything else, so campaigns automatically pick up design fixes.
21
+ */
22
+ export type ThemeInput = {
23
+ /** Schema marker so legacy `customStyle` records can never be mistaken for this. */
24
+ version: 2;
25
+ /**
26
+ * Base preset. Only decides the defaults of `surface` / `primary` when those are absent.
27
+ * Mode (dark vs light) is ALWAYS derived from the resolved `surface`, never from this.
28
+ */
29
+ preset?: ThemePresetName;
30
+ /** Page / editor background. Any CSS colour. */
31
+ surface?: string;
32
+ /** Brand colour used for primary actions, selection, focus, links. Any CSS colour. */
33
+ primary?: string;
34
+ /** High-contrast modifier (replaces the separate *-wcag presets). */
35
+ contrast?: ThemeContrast;
36
+ /** Corner radius scale. */
37
+ radius?: ThemeRadius;
38
+ /** Typography. */
39
+ font?: {
40
+ family?: string;
41
+ size?: number;
42
+ };
43
+ /** 0..1 — how strongly neutral surfaces/borders are tinted with the primary hue. Default 0. */
44
+ tint?: number;
45
+ /**
46
+ * Detailed tier: pin individual semantic tokens. Keys are semantic token names without the
47
+ * `--bx-` prefix (e.g. `"surface-1"`, `"on-primary"`). Anything not listed stays "auto".
48
+ */
49
+ overrides?: Partial<Record<SemanticTokenName, string>>;
50
+ /**
51
+ * Escape hatch for hosts/API users: raw component-tier variables (legacy names without `--`),
52
+ * applied LAST. Prefer `overrides`.
53
+ */
54
+ customStyle?: Record<string, string>;
55
+ };
56
+ /**
57
+ * Semantic token names (without the `--bx-` prefix). Grouped by role. Every one of these is
58
+ * emitted as a concrete CSS value by `resolveTheme()`; colour tokens are full CSS colours
59
+ * (`rgb()`/`rgba()`), `*-rgb` tokens are bare `r, g, b` triplets for `rgba(var(--x), a)` use.
60
+ */
61
+ export declare const SEMANTIC_TOKEN_NAMES: readonly ["color-scheme", "surface", "surface-rgb", "surface-1", "surface-1-rgb", "surface-2", "surface-2-rgb", "surface-3", "surface-inverse", "scrim", "ink", "ink-rgb", "text", "text-muted", "text-faint", "text-inverse", "text-on-surface-rgb", "text-on-inverse-rgb", "border", "border-strong", "divider", "border-rgb", "fill", "fill-hover", "fill-active", "field", "field-hover", "field-focus", "hover", "pressed", "primary", "primary-rgb", "primary-hover", "primary-active", "on-primary", "primary-text", "primary-border", "primary-soft", "primary-soft-hover", "focus-ring", "focus-ring-width", "focus-ring-offset", "primary-dark", "primary-dark-rgb", "primary-light", "primary-light-rgb", "primary-bright", "primary-bright-rgb", "success", "success-rgb", "on-success", "success-text", "success-soft", "success-border", "success-dark-rgb", "success-light-rgb", "success-darker-rgb", "warning", "warning-rgb", "on-warning", "warning-text", "warning-soft", "warning-border", "warning-dark-rgb", "warning-light-rgb", "warning-darker-rgb", "error", "error-rgb", "on-error", "error-text", "error-soft", "error-border", "error-dark-rgb", "error-light-rgb", "error-darker-rgb", "info", "info-rgb", "on-info", "info-text", "info-soft", "info-border", "info-dark-rgb", "info-light-rgb", "info-bright-rgb", "shadow-1", "shadow-2", "shadow-3", "radius-sm", "radius-md", "radius-lg", "radius-full", "font-family", "font-size", "root-font-size", "canvas", "canvas-image", "canvas-control", "canvas-control-hover", "on-canvas-control", "disabled-opacity"];
62
+ export type SemanticTokenName = (typeof SEMANTIC_TOKEN_NAMES)[number];
63
+ export type SemanticTokens = Record<SemanticTokenName, string>;
64
+ export declare const SEMANTIC_PREFIX = "--bx-";
65
+ /** Human-facing groups — used by the Studio "Advanced" panel and the playground Theme Lab. */
66
+ export declare const SEMANTIC_TOKEN_GROUPS: {
67
+ label: string;
68
+ tokens: SemanticTokenName[];
69
+ }[];
70
+ export declare const THEME_PRESETS: Record<ThemePresetName, Required<Pick<ThemeInput, 'surface' | 'primary'>>>;
71
+ /** Default status hues (mode-independent bases; the resolver adapts them per surface). */
72
+ export declare const STATUS_BASES: {
73
+ readonly success: "#28a552";
74
+ readonly warning: "#d9a400";
75
+ readonly error: "#dc2f2f";
76
+ readonly info: "#3b82f6";
77
+ };
78
+ export type ContrastCheck = {
79
+ /** Human label, e.g. "Text on surface". */
80
+ label: string;
81
+ fg: SemanticTokenName;
82
+ bg: SemanticTokenName;
83
+ ratio: number;
84
+ /** WCAG threshold applied (4.5 text, 3 non-text; 7 / 4.5 in high-contrast). */
85
+ min: number;
86
+ pass: boolean;
87
+ };
88
+ export type ThemeReport = {
89
+ mode: 'dark' | 'light';
90
+ checks: ContrastCheck[];
91
+ /** Tokens that the resolver had to nudge away from the user's literal choice to stay readable. */
92
+ adjustments: {
93
+ token: SemanticTokenName;
94
+ reason: string;
95
+ }[];
96
+ };