@a4ui/core 0.3.1 → 0.4.1

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,22 @@
1
+ import { JSX } from 'solid-js';
2
+ /** Props for {@link ThemedScenery}. */
3
+ export interface ThemedSceneryProps {
4
+ /** Emoji/text glyphs scattered and gently floating across the backdrop. */
5
+ motifs: string[];
6
+ /** How many glyphs to render. @default 16 */
7
+ count?: number;
8
+ }
9
+ /**
10
+ * A lightweight, theme-tinted backdrop for non-`space` themes: the same
11
+ * token-driven nebula as {@link SpaceBackground} plus floating motif glyphs you
12
+ * pass in, cursor glow, magnetic buttons, card edge-glow, and motifs that streak
13
+ * across (ambiently and on a background click). Mount it as {@link AppShell}'s
14
+ * `background` (fixed z-0 layer behind all content). Motion is skipped under
15
+ * `motionReduced()`.
16
+ *
17
+ * @example
18
+ * ```tsx
19
+ * <AppShell background={<ThemedScenery motifs={['ðŸĶ•', 'ðŸŒŋ', 'ðŸĶī']} />}>â€Ķ</AppShell>
20
+ * ```
21
+ */
22
+ export declare function ThemedScenery(props: ThemedSceneryProps): JSX.Element;
@@ -0,0 +1 @@
1
+ export declare function bindPointerFx(root: HTMLElement): () => void;
package/dist/styles.css CHANGED
@@ -23,7 +23,7 @@
23
23
  --accent: 199 89% 55%;
24
24
  --accent-foreground: 0 0% 100%;
25
25
  --ring: 217 91% 52%;
26
- --destructive: 0 72% 55%;
26
+ --destructive: 0 72% 52%;
27
27
  --destructive-foreground: 0 0% 100%;
28
28
 
29
29
  /* Semantic data colors (financial direction) — constant across themes. */
@@ -59,7 +59,7 @@
59
59
  --accent: 199 89% 48%;
60
60
  --accent-foreground: 0 0% 100%;
61
61
  --ring: 217 91% 52%;
62
- --destructive: 0 72% 51%;
62
+ --destructive: 0 72% 50%;
63
63
  --destructive-foreground: 0 0% 100%;
64
64
  }
65
65
 
@@ -227,21 +227,25 @@ body {
227
227
  overflow: hidden;
228
228
  }
229
229
 
230
+ /* Nebula tinted from the active theme (--primary / --accent / --destructive), so
231
+ switching palettes recolors the whole backdrop. Space's tokens keep it blue. */
230
232
  #space .sky {
231
233
  position: absolute;
232
234
  inset: 0;
233
235
  background:
234
- radial-gradient(1200px 700px at 82% -8%, hsl(217 91% 60% / 0.34), transparent 60%),
235
- radial-gradient(900px 600px at 8% 12%, hsl(275 80% 55% / 0.28), transparent 60%),
236
- radial-gradient(1000px 800px at 50% 120%, hsl(199 89% 55% / 0.24), transparent 55%),
237
- radial-gradient(700px 500px at 30% 55%, hsl(330 80% 55% / 0.14), transparent 60%), hsl(var(--background));
236
+ radial-gradient(1200px 700px at 82% -8%, hsl(var(--primary) / 0.34), transparent 60%),
237
+ radial-gradient(900px 600px at 8% 12%, hsl(var(--accent) / 0.28), transparent 60%),
238
+ radial-gradient(1000px 800px at 50% 120%, hsl(var(--accent) / 0.22), transparent 55%),
239
+ radial-gradient(700px 500px at 30% 55%, hsl(var(--destructive) / 0.12), transparent 60%),
240
+ hsl(var(--background));
238
241
  }
239
242
  [data-theme='light'] #space .sky {
240
243
  background:
241
- radial-gradient(1100px 680px at 84% -10%, hsl(38 95% 70% / 0.38), transparent 60%),
242
- radial-gradient(900px 600px at 6% 8%, hsl(217 91% 70% / 0.32), transparent 60%),
243
- radial-gradient(1000px 820px at 50% 120%, hsl(275 70% 75% / 0.26), transparent 55%),
244
- radial-gradient(700px 500px at 30% 55%, hsl(340 85% 70% / 0.18), transparent 60%), hsl(var(--background));
244
+ radial-gradient(1100px 680px at 84% -10%, hsl(var(--primary) / 0.22), transparent 60%),
245
+ radial-gradient(900px 600px at 6% 8%, hsl(var(--accent) / 0.2), transparent 60%),
246
+ radial-gradient(1000px 820px at 50% 120%, hsl(var(--accent) / 0.16), transparent 55%),
247
+ radial-gradient(700px 500px at 30% 55%, hsl(var(--destructive) / 0.1), transparent 60%),
248
+ hsl(var(--background));
245
249
  }
246
250
  @keyframes nebulaShift {
247
251
  0%,
@@ -260,7 +264,12 @@ body {
260
264
  left: -10%;
261
265
  right: -10%;
262
266
  height: 46%;
263
- background: linear-gradient(180deg, hsl(160 84% 45% / 0.16), hsl(199 89% 55% / 0.1) 45%, transparent 80%);
267
+ background: linear-gradient(
268
+ 180deg,
269
+ hsl(var(--accent) / 0.16),
270
+ hsl(var(--primary) / 0.1) 45%,
271
+ transparent 80%
272
+ );
264
273
  filter: blur(40px);
265
274
  opacity: 0.55;
266
275
  mix-blend-mode: screen;
@@ -552,3 +561,65 @@ body {
552
561
  display: none;
553
562
  }
554
563
  }
564
+
565
+ /* ---- Generic themed scenery (non-space themes) --------------------------
566
+ A themed nebula (same token-tinted gradients as the starfield) plus a field
567
+ of slowly floating motif glyphs — <ThemedScenery motifs={[...]} />. Lighter
568
+ than the bespoke SpaceBackground so every extra theme costs almost nothing. */
569
+ #scenery {
570
+ position: fixed;
571
+ inset: 0;
572
+ z-index: 0;
573
+ pointer-events: none;
574
+ overflow: hidden;
575
+ }
576
+ #scenery .themed-sky {
577
+ position: absolute;
578
+ inset: 0;
579
+ animation: sceneryPulse 16s ease-in-out infinite;
580
+ background:
581
+ radial-gradient(1200px 700px at 82% -8%, hsl(var(--primary) / 0.34), transparent 60%),
582
+ radial-gradient(900px 600px at 8% 12%, hsl(var(--accent) / 0.28), transparent 60%),
583
+ radial-gradient(1000px 800px at 50% 120%, hsl(var(--accent) / 0.22), transparent 55%),
584
+ radial-gradient(700px 500px at 30% 55%, hsl(var(--destructive) / 0.12), transparent 60%),
585
+ hsl(var(--background));
586
+ }
587
+ [data-theme='light'] #scenery .themed-sky {
588
+ background:
589
+ radial-gradient(1100px 680px at 84% -10%, hsl(var(--primary) / 0.22), transparent 60%),
590
+ radial-gradient(900px 600px at 6% 8%, hsl(var(--accent) / 0.2), transparent 60%),
591
+ radial-gradient(1000px 820px at 50% 120%, hsl(var(--accent) / 0.16), transparent 55%),
592
+ radial-gradient(700px 500px at 30% 55%, hsl(var(--destructive) / 0.1), transparent 60%),
593
+ hsl(var(--background));
594
+ }
595
+ #scenery .motif {
596
+ position: absolute;
597
+ user-select: none;
598
+ will-change: transform;
599
+ animation: motifFloat var(--dur, 9s) ease-in-out var(--delay, 0s) infinite;
600
+ }
601
+ @keyframes motifFloat {
602
+ 0%,
603
+ 100% {
604
+ transform: translateY(0) rotate(var(--r, 0deg));
605
+ }
606
+ 50% {
607
+ transform: translateY(-16px) rotate(calc(var(--r, 0deg) + 5deg));
608
+ }
609
+ }
610
+ /* Subtle nebula "breathing" so the themed backdrop isn't static. */
611
+ @keyframes sceneryPulse {
612
+ 0%,
613
+ 100% {
614
+ filter: brightness(1);
615
+ }
616
+ 50% {
617
+ filter: brightness(1.06);
618
+ }
619
+ }
620
+ @media (prefers-reduced-motion: reduce) {
621
+ html:not(.force-motion) #scenery .motif,
622
+ html:not(.force-motion) #scenery .themed-sky {
623
+ animation: none !important;
624
+ }
625
+ }
@@ -0,0 +1,34 @@
1
+ import { Palette, ThemeDefinition } from './palettes';
2
+ export type { Palette, ThemeDefinition } from './palettes';
3
+ export { themes, space, dino, doctor, scientist, soccer, TOKEN_ORDER } from './palettes';
4
+ /**
5
+ * Render a theme to the CSS you'd paste into your own stylesheet: a `:root`
6
+ * block (dark) plus a `:root[data-theme='light']` block. Same output the docs
7
+ * Theme Builder exports.
8
+ */
9
+ export declare function themeToCss(theme: ThemeDefinition): string;
10
+ /** Render a theme's tokens to a `{ dark, light }` JSON object. */
11
+ export declare function themeToJson(theme: ThemeDefinition): {
12
+ dark: Palette;
13
+ light: Palette;
14
+ };
15
+ declare const activeTheme: import('solid-js').Accessor<ThemeDefinition>;
16
+ /** Reactive accessor for the currently applied theme. */
17
+ export { activeTheme };
18
+ /**
19
+ * Apply a theme's palette globally by injecting/replacing the
20
+ * `<style id="a4ui-theme">` element. No-op during SSR. Does not persist or update
21
+ * the reactive signal — use {@link selectTheme} for the full, persisted version.
22
+ */
23
+ export declare function applyThemeDefinition(theme: ThemeDefinition): void;
24
+ /**
25
+ * Select the active theme by definition or name: applies it, updates the
26
+ * reactive {@link activeTheme} signal, and persists the choice to localStorage.
27
+ * Unknown names are ignored.
28
+ */
29
+ export declare function selectTheme(theme: ThemeDefinition | string): void;
30
+ /**
31
+ * Restore the persisted theme (or fall back to Space) and apply it. Call once at
32
+ * startup. No-op during SSR.
33
+ */
34
+ export declare function initTheme(): void;
@@ -0,0 +1,42 @@
1
+ /** The 15 recolorable tokens, each an "H S% L%" channel string. */
2
+ export interface Palette {
3
+ background: string;
4
+ foreground: string;
5
+ card: string;
6
+ 'card-foreground': string;
7
+ muted: string;
8
+ 'muted-foreground': string;
9
+ border: string;
10
+ input: string;
11
+ primary: string;
12
+ 'primary-foreground': string;
13
+ accent: string;
14
+ 'accent-foreground': string;
15
+ ring: string;
16
+ destructive: string;
17
+ 'destructive-foreground': string;
18
+ }
19
+ /** A named theme: a label plus a dark and a light palette. */
20
+ export interface ThemeDefinition {
21
+ /** URL/storage-safe slug, e.g. `space`. */
22
+ name: string;
23
+ /** Human label for the picker, e.g. `Space`. */
24
+ label: string;
25
+ /** One-line flavour text. */
26
+ description: string;
27
+ /** Emoji shown in the theme picker. */
28
+ icon: string;
29
+ dark: Palette;
30
+ light: Palette;
31
+ /** Motif glyphs for the generic `ThemedScenery` backdrop. Omit for themes that
32
+ ship their own bespoke scenery (e.g. `space` uses `SpaceBackground`). */
33
+ motifs?: string[];
34
+ }
35
+ export declare const TOKEN_ORDER: (keyof Palette)[];
36
+ export declare const space: ThemeDefinition;
37
+ export declare const dino: ThemeDefinition;
38
+ export declare const doctor: ThemeDefinition;
39
+ export declare const scientist: ThemeDefinition;
40
+ export declare const soccer: ThemeDefinition;
41
+ /** All built-in themes, in picker order. Space is the default. */
42
+ export declare const themes: ThemeDefinition[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@a4ui/core",
3
- "version": "0.3.1",
3
+ "version": "0.4.1",
4
4
  "description": "A4ui — Spatial Glass design system & component library for SolidJS (Kobalte behavior + Tailwind glass tokens + motion).",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -58,6 +58,7 @@
58
58
  "test:unit": "vitest run",
59
59
  "test": "playwright test",
60
60
  "test:ui": "playwright test --ui",
61
+ "test:a11y": "playwright test a11y --project=desktop",
61
62
  "test:report": "playwright show-report",
62
63
  "prepublishOnly": "npm run build"
63
64
  },
@@ -74,10 +75,12 @@
74
75
  "tailwind-merge": "^3.6.0"
75
76
  },
76
77
  "devDependencies": {
78
+ "@axe-core/playwright": "^4.12.1",
77
79
  "@eslint/js": "^9.39.5",
78
80
  "@playwright/test": "^1.61.1",
79
81
  "@solidjs/testing-library": "^0.8.10",
80
82
  "@testing-library/jest-dom": "^6.9.1",
83
+ "axe-core": "^4.12.1",
81
84
  "eslint": "^9.39.5",
82
85
  "eslint-config-prettier": "^10.1.8",
83
86
  "eslint-plugin-solid": "^0.14.5",