@a4ui/core 0.2.0 β†’ 0.3.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.
Files changed (56) hide show
  1. package/README.md +61 -7
  2. package/dist/index.js +2 -1
  3. package/dist/layout/AppShell.d.ts +19 -1
  4. package/dist/layout/EffectsToggle.d.ts +14 -0
  5. package/dist/layout/NavGroup.d.ts +15 -0
  6. package/dist/layout/SpaceBackground.d.ts +13 -0
  7. package/dist/layout/ThemeToggle.d.ts +12 -0
  8. package/dist/lib/cn.d.ts +11 -1
  9. package/dist/lib/effects.d.ts +21 -0
  10. package/dist/lib/media.d.ts +12 -0
  11. package/dist/lib/motion.d.ts +37 -0
  12. package/dist/lib/theme.d.ts +52 -3
  13. package/dist/lib/virtual.d.ts +16 -0
  14. package/dist/styles.css +344 -95
  15. package/dist/ui/Accordion.d.ts +19 -0
  16. package/dist/ui/Alert.d.ts +14 -0
  17. package/dist/ui/AlertDialog.d.ts +15 -0
  18. package/dist/ui/Avatar.d.ts +11 -0
  19. package/dist/ui/Badge.d.ts +12 -0
  20. package/dist/ui/Breadcrumb.d.ts +17 -0
  21. package/dist/ui/Button.d.ts +11 -0
  22. package/dist/ui/Card.d.ts +18 -0
  23. package/dist/ui/Checkbox.d.ts +11 -0
  24. package/dist/ui/Combobox.d.ts +16 -0
  25. package/dist/ui/ContextMenu.d.ts +14 -0
  26. package/dist/ui/DateField.d.ts +14 -0
  27. package/dist/ui/Drawer.d.ts +16 -0
  28. package/dist/ui/Dropdown.d.ts +18 -1
  29. package/dist/ui/Dropzone.d.ts +11 -0
  30. package/dist/ui/HoverCard.d.ts +13 -0
  31. package/dist/ui/Input.d.ts +12 -0
  32. package/dist/ui/Meter.d.ts +12 -0
  33. package/dist/ui/Modal.d.ts +21 -0
  34. package/dist/ui/NumberInput.d.ts +12 -0
  35. package/dist/ui/PageHeader.d.ts +17 -0
  36. package/dist/ui/Pagination.d.ts +14 -0
  37. package/dist/ui/Popover.d.ts +13 -0
  38. package/dist/ui/Progress.d.ts +11 -0
  39. package/dist/ui/RadioGroup.d.ts +20 -0
  40. package/dist/ui/SegmentedControl.d.ts +19 -0
  41. package/dist/ui/Select.d.ts +14 -0
  42. package/dist/ui/Separator.d.ts +11 -0
  43. package/dist/ui/Skeleton.d.ts +11 -0
  44. package/dist/ui/Slider.d.ts +14 -0
  45. package/dist/ui/Spinner.d.ts +10 -0
  46. package/dist/ui/Stat.d.ts +12 -0
  47. package/dist/ui/Switch.d.ts +11 -0
  48. package/dist/ui/Table.d.ts +61 -0
  49. package/dist/ui/Tabs.d.ts +20 -0
  50. package/dist/ui/Textarea.d.ts +12 -0
  51. package/dist/ui/Toast.d.ts +12 -0
  52. package/dist/ui/Toggle.d.ts +12 -0
  53. package/dist/ui/ToggleGroup.d.ts +20 -0
  54. package/dist/ui/Tooltip.d.ts +13 -0
  55. package/dist/ui/VirtualList.d.ts +14 -0
  56. package/package.json +18 -5
package/README.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  [![npm](https://img.shields.io/npm/v/@a4ui/core.svg)](https://www.npmjs.com/package/@a4ui/core)
4
4
  [![license](https://img.shields.io/npm/l/@a4ui/core.svg)](./LICENSE)
5
+ [![Lighthouse: Performance 100](https://img.shields.io/badge/Lighthouse-Perf_100-success)](https://a4uikit.github.io/a4ui/)
6
+ [![Lighthouse: Accessibility 100](https://img.shields.io/badge/A11y-100-success)](https://a4uikit.github.io/a4ui/)
7
+ [![Lighthouse: Best Practices 100](https://img.shields.io/badge/Best_Practices-100-success)](https://a4uikit.github.io/a4ui/)
8
+ [![Lighthouse: SEO 100](https://img.shields.io/badge/SEO-100-success)](https://a4uikit.github.io/a4ui/)
9
+ [![npm downloads](https://img.shields.io/npm/dm/@a4ui/core.svg)](https://www.npmjs.com/package/@a4ui/core)
10
+ [![minzipped size](https://img.shields.io/bundlephobia/minzip/@a4ui/core.svg)](https://bundlephobia.com/package/@a4ui/core)
5
11
 
6
12
  **Spatial Glass** β€” a design system & component library for **SolidJS**
7
13
  (glassmorphism + starfield backdrop + light/dark themes). Named after the 4
@@ -11,11 +17,11 @@ people in the Rivera family. πŸ™‚
11
17
 
12
18
  Three layers, one identity:
13
19
 
14
- | Layer | What it gives | Tech |
15
- |-------|---------------|------|
16
- | Behavior / a11y | focus, keyboard, ARIA, portals | **Kobalte** |
17
- | Motion | transitions, count-up, calm mode | **solid-transition-group + solid-motionone** |
18
- | Visual | glass surfaces, tokens, glow, starfield | **Tailwind preset + `styles.css`** |
20
+ | Layer | What it gives | Tech |
21
+ | --------------- | --------------------------------------- | -------------------------------------------- |
22
+ | Behavior / a11y | focus, keyboard, ARIA, portals | **Kobalte** |
23
+ | Motion | transitions, count-up, calm mode | **solid-transition-group + solid-motionone** |
24
+ | Visual | glass surfaces, tokens, glow, starfield | **Tailwind preset + `styles.css`** |
19
25
 
20
26
  ## Install
21
27
 
@@ -44,6 +50,31 @@ import '@a4ui/core/styles.css'
44
50
  import { Button, Card, Modal } from '@a4ui/core'
45
51
  ```
46
52
 
53
+ ## Customization
54
+
55
+ Colors, radius and fonts are driven by CSS variables (the `@a4ui/core/preset`
56
+ maps Tailwind's semantic names to them). Override any token in your own CSS β€”
57
+ scope it to `:root` (or `:root[data-theme='light']`) after importing the styles:
58
+
59
+ ```css
60
+ @import '@a4ui/core/styles.css';
61
+
62
+ :root {
63
+ --primary: 262 83% 58%; /* HSL channels β€” makes everything primary purple */
64
+ --radius-xl: 0.75rem;
65
+ }
66
+ ```
67
+
68
+ Dark is the default; add `data-theme="light"` on `<html>` (or use the exported
69
+ `toggleTheme()` / `<ThemeToggle />`) for the light palette.
70
+
71
+ ## Server rendering
72
+
73
+ A4ui is **client-first** β€” the components render in the browser (the glass,
74
+ starfield and theme rely on the DOM/`localStorage`). Importing the package is
75
+ SSR-safe (no crash at import), but for **SolidStart** render the components on the
76
+ client, e.g. via `clientOnly(() => import('...'))`.
77
+
47
78
  ## What's inside
48
79
 
49
80
  ~40 components across actions, forms, data, overlays, feedback, navigation and
@@ -51,16 +82,39 @@ layout β€” plus a virtualized list, motion helpers, and a generic `AppShell` wit
51
82
  the animated `SpaceBackground`. Browse them all (with live prop controls and
52
83
  copyable code) in the **[docs site](https://a4uikit.github.io/a4ui/)**.
53
84
 
85
+ ## Using with AI agents
86
+
87
+ Everything an AI coding agent (Claude Code, Codex, Cursor, …) needs to use A4ui
88
+ ships **in the package**, so it works from `node_modules` without visiting the docs:
89
+
90
+ - **Typed API + examples** β€” every export has JSDoc with an `@example` in the
91
+ shipped `.d.ts` (`node_modules/@a4ui/core/dist/index.d.ts`), so editor
92
+ autocomplete and agents get the props and usage inline.
93
+ - **`llms.txt`** β€” a machine-readable summary of every component:
94
+ https://a4uikit.github.io/a4ui/llms.txt
95
+
96
+ Drop this into your project's `AGENTS.md` / `CLAUDE.md` to prime your agent:
97
+
98
+ > This project uses **@a4ui/core** (SolidJS design system). Import components as
99
+ > named exports from `@a4ui/core`; import `@a4ui/core/styles.css` once in the app
100
+ > entry; add `@a4ui/core/preset` to `tailwind.config`. Props are typed with
101
+ > JSDoc/`@example` in the package's `.d.ts`. Full component list:
102
+ > https://a4uikit.github.io/a4ui/llms.txt
103
+
54
104
  ## Develop
55
105
 
56
106
  ```bash
57
107
  npm install
58
- npm run build # library build (ESM + .d.ts)
59
108
  npm run preview # docs site (dev server)
60
109
  npm run typecheck
61
- npm test # Playwright suite (render + behavior, desktop + mobile)
110
+ npm run lint # ESLint Β· npm run format to auto-format
111
+ npm run test:unit # Vitest β€” helper unit tests
112
+ npm test # Playwright β€” docs render + behavior (desktop + mobile)
113
+ npm run build # library build (ESM + .d.ts + styles.css)
62
114
  ```
63
115
 
116
+ See **[CONTRIBUTING.md](./CONTRIBUTING.md)** and **[AGENTS.md](./AGENTS.md)**.
117
+
64
118
  ## License
65
119
 
66
120
  [MIT](./LICENSE) Β© Luis Alfredo Rivera AcuΓ±a
package/dist/index.js CHANGED
@@ -50,6 +50,7 @@ function Ot() {
50
50
  return zt(e) ? e : "dark";
51
51
  }
52
52
  function Ke(e) {
53
+ if (typeof document > "u") return;
53
54
  const t = document.documentElement;
54
55
  e === "light" ? t.setAttribute("data-theme", "light") : t.removeAttribute("data-theme");
55
56
  }
@@ -88,7 +89,7 @@ function Yt() {
88
89
  return e === null ? !0 : e === "1";
89
90
  }
90
91
  function tt(e) {
91
- document.documentElement.classList.toggle("calm", !e);
92
+ typeof document > "u" || document.documentElement.classList.toggle("calm", !e);
92
93
  }
93
94
  const rt = Yt();
94
95
  tt(rt);
@@ -1,4 +1,5 @@
1
1
  import { JSX, ParentProps } from 'solid-js';
2
+ /** Props for {@link AppShell}. All slots are optional β€” pass only what your app needs. */
2
3
  interface AppShellProps extends ParentProps {
3
4
  /** Left column (e.g. a Sidebar). Rendered as a flex child; owns its own width. */
4
5
  sidebar?: JSX.Element;
@@ -11,8 +12,25 @@ interface AppShellProps extends ParentProps {
11
12
  background?: JSX.Element;
12
13
  /** Max content width for <main>. Default '1400px'. */
13
14
  maxWidth?: string;
14
- /** Override the default error fallback shown when a route throws. */
15
+ /** Override the default error fallback shown when a route throws. Receives
16
+ the caught error and a `reset` callback that re-renders the ErrorBoundary's
17
+ children. */
15
18
  errorFallback?: (err: unknown, reset: () => void) => JSX.Element;
16
19
  }
20
+ /**
21
+ * Generic app shell: a fixed {@link SpaceBackground} behind a flex layout of
22
+ * an optional sidebar slot plus a content column (optional banner + topbar +
23
+ * routed `<main>`). Slot-based and unopinionated about the sidebar β€” pass
24
+ * your own component via `sidebar` and it manages its own width/collapse.
25
+ * `<main>` is wrapped in `Suspense` + an `ErrorBoundary` (with a ~160ms page
26
+ * cross-fade) so a route chunk failing or still loading never blanks the shell.
27
+ *
28
+ * @example
29
+ * ```tsx
30
+ * <AppShell sidebar={<Sidebar />} topbar={<Topbar />}>
31
+ * <Route />
32
+ * </AppShell>
33
+ * ```
34
+ */
17
35
  export declare function AppShell(props: ParentProps<AppShellProps>): JSX.Element;
18
36
  export {};
@@ -1,5 +1,19 @@
1
+ /** Props for {@link EffectsToggle}. */
1
2
  interface EffectsToggleProps {
3
+ /** Accessible label / tooltip text. Defaults to `'Visual effects'`. */
2
4
  label?: string;
3
5
  }
6
+ /**
7
+ * Icon button that toggles the "visual effects" switch: pressed (default) is
8
+ * the full space-glass experience (translucent glass, starfield, animation);
9
+ * un-pressed is calm mode (opaque surfaces, no starfield, no motion) β€” useful
10
+ * for contrast, motion sensitivity, and low-power devices. Wraps
11
+ * {@link useEffects} + {@link setEffects}.
12
+ *
13
+ * @example
14
+ * ```tsx
15
+ * <EffectsToggle label="Toggle effects" />
16
+ * ```
17
+ */
4
18
  export declare function EffectsToggle(props: EffectsToggleProps): import("solid-js").JSX.Element;
5
19
  export {};
@@ -1,6 +1,21 @@
1
1
  import { JSX, ParentProps } from 'solid-js';
2
+ /** Props for {@link NavGroup}. */
2
3
  interface NavGroupProps extends ParentProps {
4
+ /** Category heading shown in the summary row (rendered uppercase). */
3
5
  title: string;
4
6
  }
7
+ /**
8
+ * Collapsible sidebar nav category. Uses native `<details>`/`<summary>` (no
9
+ * JS state), with a chevron that rotates via a `group-[[open]]` CSS variant.
10
+ * Expanded by default; wrap a list of nav links as `children`.
11
+ *
12
+ * @example
13
+ * ```tsx
14
+ * <NavGroup title="Settings">
15
+ * <NavLink href="/settings/profile">Profile</NavLink>
16
+ * <NavLink href="/settings/billing">Billing</NavLink>
17
+ * </NavGroup>
18
+ * ```
19
+ */
5
20
  export declare function NavGroup(props: NavGroupProps): JSX.Element;
6
21
  export {};
@@ -1 +1,14 @@
1
+ /**
2
+ * The "space glass" backdrop: a fixed, `aria-hidden` layer (z-0) meant to sit
3
+ * behind all page content (this is {@link AppShell}'s default `background`).
4
+ * Renders a randomized starfield (with twinkle + occasional shooting stars,
5
+ * or rockets in light theme), static nebula/planets, gyroscope parallax on
6
+ * phones, and cursor-glow/magnetic-button effects. Takes no props; reacts to
7
+ * {@link useTheme} for star tints and to `motionReduced()` to skip all motion.
8
+ *
9
+ * @example
10
+ * ```tsx
11
+ * <SpaceBackground />
12
+ * ```
13
+ */
1
14
  export declare function SpaceBackground(): import("solid-js").JSX.Element;
@@ -1,6 +1,18 @@
1
1
  import { JSX } from 'solid-js';
2
+ /** Props for {@link ThemeToggle}. */
2
3
  interface ThemeToggleProps {
4
+ /** Accessible label / tooltip text. Defaults to `'Toggle theme'`. */
3
5
  label?: string;
4
6
  }
7
+ /**
8
+ * Icon button that flips between dark and light theme. The icon shows the
9
+ * CURRENT theme (moon when dark, sun when light), not the theme you'd switch
10
+ * to. Wraps {@link useTheme} + {@link toggleTheme}.
11
+ *
12
+ * @example
13
+ * ```tsx
14
+ * <ThemeToggle label="Switch theme" />
15
+ * ```
16
+ */
5
17
  export declare function ThemeToggle(props: ThemeToggleProps): JSX.Element;
6
18
  export {};
package/dist/lib/cn.d.ts CHANGED
@@ -1,3 +1,13 @@
1
1
  import { ClassValue } from 'clsx';
2
- /** Merge Tailwind class lists, resolving conflicting utilities left-to-right. */
2
+ /**
3
+ * Merge Tailwind class lists, resolving conflicting utilities left-to-right
4
+ * (via `tailwind-merge`) after flattening conditionals/arrays/objects (via `clsx`).
5
+ * Use this instead of template-string concatenation whenever a component
6
+ * accepts a `class` override that should win over its own defaults.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * cn('px-2 py-1 text-sm', isActive && 'bg-primary text-primary-foreground', props.class)
11
+ * ```
12
+ */
3
13
  export declare function cn(...inputs: ClassValue[]): string;
@@ -1,4 +1,25 @@
1
+ /**
2
+ * Reactive accessor for the "visual effects" switch: `true` = full
3
+ * space-glass experience (glass, starfield, animation), `false` = calm mode.
4
+ * Backing signal shared module-wide, so every consumer (e.g.
5
+ * {@link EffectsToggle}) reads the same source of truth.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * const effectsOn = useEffects()
10
+ * console.log(effectsOn()) // true | false
11
+ * ```
12
+ */
1
13
  export declare function useEffects(): import('solid-js').Accessor<boolean>;
2
14
  /** True when visual effects are OFF (calm mode). Read by motion + charts. */
3
15
  export declare function isCalm(): boolean;
16
+ /**
17
+ * Persist and apply the visual-effects switch: toggles `html.calm` (opaque
18
+ * surfaces, no starfield, no motion when off) and updates {@link useEffects}.
19
+ *
20
+ * @example
21
+ * ```ts
22
+ * setEffects(false) // enter calm mode
23
+ * ```
24
+ */
4
25
  export declare function setEffects(on: boolean): void;
@@ -1 +1,13 @@
1
+ /**
2
+ * Reactive `matchMedia` matcher: returns a signal accessor that updates when
3
+ * the query's match state changes, cleaning up its listener on unmount.
4
+ * Handy for rendering either a desktop or mobile layout instead of hiding one
5
+ * with CSS.
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * const isDesktop = useMediaQuery('(min-width: 768px)')
10
+ * return isDesktop() ? <DesktopTable /> : <MobileCards />
11
+ * ```
12
+ */
1
13
  export declare function useMediaQuery(query: string): () => boolean;
@@ -3,15 +3,46 @@ import { Accessor } from 'solid-js';
3
3
  * Snapshot of the user's reduced-motion preference at call time. Not
4
4
  * reactive to preference changes mid-session (matches theme.ts's one-shot
5
5
  * environment read) β€” reload picks up changes.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * if (prefersReducedMotion()) skipEntranceAnimation()
10
+ * ```
6
11
  */
7
12
  export declare function prefersReducedMotion(): boolean;
13
+ /**
14
+ * Reactive accessor for the persisted "force full motion" opt-out (the ⚑
15
+ * MotionToggle) β€” `true` re-enables animation even when the OS requests
16
+ * reduced motion.
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * const forced = useMotionForced()
21
+ * console.log(forced()) // true | false
22
+ * ```
23
+ */
8
24
  export declare function useMotionForced(): Accessor<boolean>;
25
+ /**
26
+ * Persist the "force full motion" choice, toggle `html.force-motion`
27
+ * (which the reduced-motion `@media` block in styles.css keys off), and
28
+ * update {@link useMotionForced}.
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * setMotionForced(true) // opt back into full motion despite OS setting
33
+ * ```
34
+ */
9
35
  export declare function setMotionForced(on: boolean): void;
10
36
  /**
11
37
  * Effective reduced-motion: reduced when calm mode (visual effects OFF) is on,
12
38
  * or when the OS asks for reduced motion and the user hasn't forced it. JS-driven
13
39
  * motion (SpaceBackground, count-up) gates on this rather than
14
40
  * prefersReducedMotion() directly.
41
+ *
42
+ * @example
43
+ * ```ts
44
+ * if (!motionReduced()) startParallaxLoop()
45
+ * ```
15
46
  */
16
47
  export declare function motionReduced(): boolean;
17
48
  /**
@@ -20,5 +51,11 @@ export declare function motionReduced(): boolean;
20
51
  * yet, and this needs to interpolate a plain number rather than a DOM
21
52
  * property, so it's a small hand-rolled WAAPI-adjacent tween for KPI count-up).
22
53
  * Jumps straight to the target under prefers-reduced-motion.
54
+ *
55
+ * @example
56
+ * ```tsx
57
+ * const animated = createCountUp(() => props.value, 800)
58
+ * return <span>{Math.round(animated())}</span>
59
+ * ```
23
60
  */
24
61
  export declare function createCountUp(target: Accessor<number>, durationMs?: number): Accessor<number>;
@@ -1,10 +1,59 @@
1
+ /** The two supported color schemes. `'dark'` (the "night" palette) is the default. */
1
2
  export type Theme = 'dark' | 'light';
2
- /** The persisted choice, defaulting to dark when nothing is stored. */
3
+ /**
4
+ * Read the persisted theme choice from `localStorage`, defaulting to `'dark'`
5
+ * when nothing is stored (or storage is unavailable).
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * const initial = storedTheme() // 'dark' | 'light'
10
+ * ```
11
+ */
3
12
  export declare function storedTheme(): Theme;
4
- /** Reflect the theme onto <html data-theme>: dark is the default (no attribute). */
13
+ /**
14
+ * Reflect a theme onto `<html data-theme>` without touching storage or the
15
+ * shared signal. Dark is the default (no attribute); light sets
16
+ * `data-theme="light"`. Prefer {@link setTheme} unless you specifically need
17
+ * to apply without persisting.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * applyTheme('light')
22
+ * ```
23
+ */
5
24
  export declare function applyTheme(theme: Theme): void;
6
- /** Persist and apply a theme choice. */
25
+ /**
26
+ * Persist a theme choice to `localStorage` and apply it to `<html>`. Does not
27
+ * update the shared `useTheme()` signal directly β€” {@link toggleTheme} does
28
+ * that after calling this.
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * setTheme('light')
33
+ * ```
34
+ */
7
35
  export declare function setTheme(theme: Theme): void;
36
+ /**
37
+ * The opposite of the given theme. Pure helper (no signal/storage side effects)
38
+ * used by {@link toggleTheme}.
39
+ *
40
+ * @example
41
+ * ```ts
42
+ * toggled('dark') // 'light'
43
+ * ```
44
+ */
8
45
  export declare function toggled(theme: Theme): Theme;
46
+ /**
47
+ * Reactive accessor for the current theme, shared module-wide so every
48
+ * consumer (e.g. {@link ThemeToggle}, `SpaceBackground`) reads the same
49
+ * source of truth.
50
+ *
51
+ * @example
52
+ * ```ts
53
+ * const theme = useTheme()
54
+ * console.log(theme()) // 'dark' | 'light'
55
+ * ```
56
+ */
9
57
  export declare function useTheme(): import('solid-js').Accessor<Theme>;
58
+ /** Flip the current theme (dark↔light), persisting and applying the new value. */
10
59
  export declare function toggleTheme(): void;
@@ -1,2 +1,18 @@
1
1
  import { Accessor } from 'solid-js';
2
+ /**
3
+ * Works around a `@tanstack/solid-virtual` bug where the virtualizer measures
4
+ * a 0Γ—0 viewport: it attaches its `ResizeObserver` during the render pass
5
+ * (when `getScrollElement` first returns the scroll container), before the
6
+ * container has been laid out, so it caches a 0Γ—0 size and never renders any
7
+ * rows. Once layout settles, this detaches+reattaches the scroll element so
8
+ * the observer re-measures the real height. Call it right after wiring up
9
+ * the virtualizer, passing the scroll-element signal's getter and setter.
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * const [scrollEl, setScrollEl] = createSignal<HTMLElement>()
14
+ * const virtualizer = createVirtualizer({ getScrollElement: scrollEl, ... })
15
+ * remeasureAfterLayout(scrollEl, setScrollEl)
16
+ * ```
17
+ */
2
18
  export declare function remeasureAfterLayout(scrollEl: Accessor<HTMLElement | undefined>, setScrollEl: (v: HTMLElement | undefined) => void): void;