@pebble-software/bonsai 0.0.3 → 0.0.4
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 +21 -45
- package/dist/index.css +453 -0
- package/dist/index.d.ts +100 -4
- package/package.json +17 -8
- package/dist/components/core/bonsai.d.ts +0 -17
- package/dist/components/ui/a.d.ts +0 -3
- package/dist/components/ui/accordion.d.ts +0 -17
- package/dist/components/ui/alert-dialog.d.ts +0 -23
- package/dist/components/ui/alert.d.ts +0 -12
- package/dist/components/ui/avatar.d.ts +0 -21
- package/dist/components/ui/badge.d.ts +0 -10
- package/dist/components/ui/breadcrumb.d.ts +0 -10
- package/dist/components/ui/button.d.ts +0 -14
- package/dist/components/ui/calendar.d.ts +0 -9
- package/dist/components/ui/card.d.ts +0 -15
- package/dist/components/ui/chart.d.ts +0 -44
- package/dist/components/ui/checkbox.d.ts +0 -7
- package/dist/components/ui/collapsible.d.ts +0 -5
- package/dist/components/ui/combobox.d.ts +0 -31
- package/dist/components/ui/command.d.ts +0 -23
- package/dist/components/ui/dialog.d.ts +0 -23
- package/dist/components/ui/drawer.d.ts +0 -22
- package/dist/components/ui/dropdown-menu.d.ts +0 -29
- package/dist/components/ui/form.d.ts +0 -24
- package/dist/components/ui/h.d.ts +0 -10
- package/dist/components/ui/input-group.d.ts +0 -18
- package/dist/components/ui/input-otp.d.ts +0 -11
- package/dist/components/ui/input.d.ts +0 -3
- package/dist/components/ui/label.d.ts +0 -10
- package/dist/components/ui/loader.d.ts +0 -14
- package/dist/components/ui/map.d.ts +0 -30
- package/dist/components/ui/p.d.ts +0 -11
- package/dist/components/ui/popover.d.ts +0 -9
- package/dist/components/ui/progress.d.ts +0 -31
- package/dist/components/ui/scroll-area.d.ts +0 -13
- package/dist/components/ui/select.d.ts +0 -17
- package/dist/components/ui/separator.d.ts +0 -8
- package/dist/components/ui/sidebar.d.ts +0 -70
- package/dist/components/ui/signature-pad.d.ts +0 -7
- package/dist/components/ui/skeleton.d.ts +0 -7
- package/dist/components/ui/switch.d.ts +0 -8
- package/dist/components/ui/table.d.ts +0 -14
- package/dist/components/ui/tabs.d.ts +0 -15
- package/dist/components/ui/textarea.d.ts +0 -5
- package/dist/components/ui/toast.d.ts +0 -46
- package/dist/components/ui/tooltip.d.ts +0 -15
- package/dist/hooks/haptics.d.ts +0 -8
- package/dist/hooks/use-mobile.d.ts +0 -1
- package/dist/lib/ast/cache.d.ts +0 -8
- package/dist/lib/ast/evaluator.d.ts +0 -2
- package/dist/lib/ast/helpers.d.ts +0 -2
- package/dist/lib/ast/index.d.ts +0 -4
- package/dist/lib/ast/setup.d.ts +0 -2
- package/dist/lib/ast/types.d.ts +0 -1
- package/dist/lib/ast/utils.d.ts +0 -3
- package/dist/lib/haptics.d.ts +0 -8
- package/dist/lib/ui/registry.d.ts +0 -8
- package/dist/lib/utils.d.ts +0 -3
- package/dist/tests/ast.bugs.test.d.ts +0 -1
- package/dist/tests/ast.engine.test.d.ts +0 -1
- package/dist/tests/ast.helpers.test.d.ts +0 -1
- package/dist/tests/fixtures/examples.d.ts +0 -8
- package/dist/tests/helpers.d.ts +0 -4
- package/dist/tests/render.bindings.test.d.ts +0 -1
- package/dist/tests/render.controlled.test.d.ts +0 -1
- package/dist/tests/render.examples.test.d.ts +0 -1
- package/dist/tests/render.granularity.test.d.ts +0 -1
- package/dist/tests/render.ssr-client.test.d.ts +0 -1
- package/dist/tests/render.structural.test.d.ts +0 -1
- package/dist/tests/schema.contract.test.d.ts +0 -1
- package/dist/tests/schema.examples.test.d.ts +0 -1
- package/dist/tests/schema.kitchen-sink.test.d.ts +0 -1
- package/dist/tests/setup.d.ts +0 -1
- package/dist/tests/store.paths.test.d.ts +0 -1
- package/dist/tests/ui.contracts.test.d.ts +0 -1
package/README.md
CHANGED
|
@@ -1,15 +1,7 @@
|
|
|
1
1
|
# @pebble-software/bonsai
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/@pebble-software/bonsai)
|
|
4
|
-
[](https://github.com/PebbleSoftwareInc/pebble-bonsai)
|
|
5
|
-
[](https://react.dev)
|
|
6
|
-
|
|
7
|
-
> ⚠️ **Active development / preview** — this package is pre-1.0 and evolving quickly. APIs, the configuration schema, and the component set may change between releases without a deprecation cycle. Pin an exact version and read release notes before upgrading. Feedback and issues are very welcome!
|
|
8
|
-
|
|
9
3
|
The React runtime that turns a validated Bonsai configuration into a fully interactive, styled experience. It's built for UI that has to be delivered remotely (email, SMS, embedded experiences) but rendered locally with real React components, consistent styling, and predictable data flow.
|
|
10
4
|
|
|
11
|
-
Bonsai is the rendering engine behind [Pebble Bonsai](https://github.com/PebbleSoftwareInc/pebble-bonsai), an open-source form and signature service — but the renderer is useful anywhere you need to ship UI as data: define a screen as a JSON template, bind it to your data with `{{ }}` expressions, and let one component render it on the server (SSR, PDF) or in the browser (fully interactive).
|
|
12
|
-
|
|
13
5
|
## Table of contents
|
|
14
6
|
|
|
15
7
|
- [Features](#features)
|
|
@@ -28,31 +20,26 @@ Bonsai is the rendering engine behind [Pebble Bonsai](https://github.com/PebbleS
|
|
|
28
20
|
|
|
29
21
|
## Features
|
|
30
22
|
|
|
31
|
-
- **Schema-driven UI** — every screen is a Zod-validated `Config` tree
|
|
32
|
-
- **Universal rendering** — one component (`Bonsai`) picks server (SSR) or client (interactive) rendering automatically based on environment
|
|
23
|
+
- **Schema-driven UI** — every screen is a Zod-validated `Config` tree from `@bonsai/schemas`, so the renderer only ever processes known-safe structures.
|
|
24
|
+
- **Universal rendering** — one component (`Bonsai`) picks server (SSR) or client (interactive) rendering automatically based on environment.
|
|
33
25
|
- **Composable components** — schema component identifiers map to real React implementations via a `componentMap`, so the same schema can target different component sets.
|
|
34
26
|
- **Isolated per-instance state** — each mounted `Bonsai` tree owns its own `valtio` store, scoped via React context. Nothing about which data renders lives in shared or module-level state.
|
|
35
27
|
- **Fine-grained reactivity** — every leaf node reads the store itself and evaluates only its own bindings, so editing one field re-renders just that field and whatever depends on it.
|
|
36
28
|
- **Structural nodes** — `each` and `if` handle looping and conditional rendering directly in the schema, with two-way binding preserved inside loops.
|
|
37
29
|
- **Sandboxed expression language** — `{{ }}` bindings are evaluated by a small, fixed-surface formula language, never executable JavaScript.
|
|
38
|
-
- **Tailwind CSS v4
|
|
30
|
+
- **Tailwind CSS v4** theme with a `bonsai:` prefix to avoid collisions with a host app's own styles.
|
|
39
31
|
|
|
40
32
|
## Installation
|
|
41
33
|
|
|
42
34
|
```bash
|
|
43
|
-
npm install @pebble-software/bonsai
|
|
44
|
-
# or
|
|
45
35
|
pnpm add @pebble-software/bonsai
|
|
46
|
-
# or
|
|
47
|
-
yarn add @pebble-software/bonsai
|
|
48
36
|
```
|
|
49
37
|
|
|
50
|
-
Peer
|
|
38
|
+
Peer expectations:
|
|
51
39
|
|
|
52
|
-
-
|
|
40
|
+
- React 18 or newer
|
|
53
41
|
- A bundler that understands ES modules (Vite, Next, Rspack, etc.)
|
|
54
|
-
|
|
55
|
-
> **Preview note:** while the package is pre-1.0, pin an exact version (`npm install @pebble-software/bonsai --save-exact`) — minor releases may contain breaking changes.
|
|
42
|
+
- Tailwind CSS support if you intend to merge or override Bonsai styles
|
|
56
43
|
|
|
57
44
|
### Working in this monorepo
|
|
58
45
|
|
|
@@ -61,7 +48,7 @@ pnpm install
|
|
|
61
48
|
pnpm --filter @pebble-software/bonsai run build
|
|
62
49
|
```
|
|
63
50
|
|
|
64
|
-
The shared workspaces (`@bonsai/constants`, `@bonsai/schemas`)
|
|
51
|
+
The build script compiles the shared workspaces (`@bonsai/constants`, `@bonsai/schemas`) before bundling this package with Vite.
|
|
65
52
|
|
|
66
53
|
## Quick start
|
|
67
54
|
|
|
@@ -70,7 +57,7 @@ import { Bonsai, type Config } from "@pebble-software/bonsai";
|
|
|
70
57
|
|
|
71
58
|
const template: Config = {
|
|
72
59
|
type: "card",
|
|
73
|
-
args: { className: "p-6 flex flex-col gap-4" },
|
|
60
|
+
args: { className: "bonsai:p-6 bonsai:flex bonsai:flex-col bonsai:gap-4" },
|
|
74
61
|
children: [
|
|
75
62
|
{
|
|
76
63
|
type: "label",
|
|
@@ -111,7 +98,7 @@ export function Example() {
|
|
|
111
98
|
| `useUpdateData()` | hook | Returns an `updateData(path, value)` function bound to the nearest `Bonsai` instance's store. Must be called from a component mounted inside it. |
|
|
112
99
|
| `componentMap` | object | Maps schema component identifiers to React implementations. |
|
|
113
100
|
| `controlledInputMap` | object | Defines how specific component types behave as controlled inputs (value prop, change handler, value extractor). |
|
|
114
|
-
| `Config` | type | Runtime type describing a Bonsai node
|
|
101
|
+
| `Config` | type | Runtime type describing a Bonsai node, re-exported from `@bonsai/schemas`. |
|
|
115
102
|
| `SignatureProps` | type | Props contract for the built-in signature capture component. |
|
|
116
103
|
|
|
117
104
|
`Bonsai` accepts:
|
|
@@ -127,14 +114,11 @@ interface BonsaiProps {
|
|
|
127
114
|
}
|
|
128
115
|
```
|
|
129
116
|
|
|
130
|
-
- `render: "web"` (default) mounts the tree inside a `<form>` whose submit handler POSTs the captured data to `captureUrl`. `"preview"` renders the same form UI flagged with `data-mode="preview"` for host styling. `"pdf"` renders a plain `<div>` wrapper with no form — the layout used for print/PDF output.
|
|
131
|
-
- `status` other than `"pending"` disables capture: the submit handler no-ops instead of POSTing.
|
|
132
|
-
|
|
133
117
|
Each mounted instance owns its own store — there's no shared module-level state and no global `updateData`; `useUpdateData()` is the only way to reach a given instance's data, and only from within its subtree.
|
|
134
118
|
|
|
135
119
|
## Configuration schema
|
|
136
120
|
|
|
137
|
-
The schema is defined in
|
|
121
|
+
The schema is defined in `@bonsai/schemas/src/bonsai.schema.ts` and exported as `Config`.
|
|
138
122
|
|
|
139
123
|
- **Node shape**: each node is either a string literal (rendered directly, and itself eligible for `{{ }}` bindings) or an object with:
|
|
140
124
|
- `type`: a value from `COMPONENT` (see [Component reference](#component-reference)), including the structural `each` and `if` types (see [Structural nodes](#structural-nodes-each--if)).
|
|
@@ -151,21 +135,18 @@ Example node:
|
|
|
151
135
|
"args": {
|
|
152
136
|
"id": "terms",
|
|
153
137
|
"checked": "{{agreements.terms}}",
|
|
154
|
-
"className": "mt-4"
|
|
138
|
+
"className": "bonsai:mt-4"
|
|
155
139
|
},
|
|
156
140
|
"children": ["I agree to the terms"]
|
|
157
141
|
}
|
|
158
142
|
```
|
|
159
143
|
|
|
160
|
-
Complete example templates live in the repo's [`examples/`](https://github.com/PebbleSoftwareInc/pebble-bonsai/tree/main/examples) directory, including a kitchen-sink template that exercises every component type.
|
|
161
|
-
|
|
162
144
|
## Dynamic data binding
|
|
163
145
|
|
|
164
146
|
- Use `{{ }}` to declare bindings. A binding must span the *entire* string value — `"{{a}}"` is evaluated, `"{{a}} of {{b}}"` is not (put each placeholder in its own child/string instead). Whitespace inside `{{ }}` is trimmed automatically.
|
|
165
147
|
- Paths support dot notation (`user.email`) and array indexing (`addresses[0].street`).
|
|
166
148
|
- Missing keys resolve to `undefined`; use the `default(value, fallback)` helper for a fallback inline, or handle defaults in host data.
|
|
167
149
|
- Any `args` prop or string child can contain a binding.
|
|
168
|
-
- Besides your own `data`, the instance's top-level state fields — `status`, `expiry`, and `submitting` — are readable from bindings too (e.g. an `if` on `{{submitting}}`).
|
|
169
150
|
- Inside an `each` row, an identifier resolves against the loop alias first (e.g. `applicant.name`), then falls back to the surrounding data for anything the alias doesn't shadow (e.g. `orgName` from outside the loop) — see [Structural nodes](#structural-nodes-each--if).
|
|
170
151
|
- On the client, evaluating a node's bindings is also what subscribes that node to the store — see [Controlled inputs & state](#controlled-inputs--state).
|
|
171
152
|
|
|
@@ -282,7 +263,7 @@ Allowlisted methods: `map`, `filter`, `some`, `every`, `find`. Anything else —
|
|
|
282
263
|
|
|
283
264
|
## Component reference
|
|
284
265
|
|
|
285
|
-
The full, authoritative list of component types is the `COMPONENT` enum in `@bonsai/constants` (
|
|
266
|
+
The full, authoritative list of component types is the `COMPONENT` enum in `@bonsai/constants` (`library/constants/src/components.ts`) — `componentMap` (`src/lib/ui/registry.ts`) has an entry for every value in it.
|
|
286
267
|
|
|
287
268
|
| Category | Schema types |
|
|
288
269
|
| --- | --- |
|
|
@@ -299,12 +280,10 @@ Controlled inputs (registered in `controlledInputMap` — `input`, `otp`, `check
|
|
|
299
280
|
|
|
300
281
|
## Styling
|
|
301
282
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
-
|
|
305
|
-
-
|
|
306
|
-
- **Getting the stylesheet**: inside the Pebble Bonsai platform, CSS is compiled per-template at form-creation time — only the classes a template actually uses are emitted. When consuming this package standalone, compile the styles with your own Tailwind v4 build: import the Bonsai theme and add the package (and your template sources) as content via `@source` so the utilities your templates reference are generated. A prebuilt, ready-to-import stylesheet export is planned while the packaging story stabilizes during the preview period.
|
|
307
|
-
- **Theming**: the theme is token-driven — redefine the CSS variables (e.g. `--color-main`, `--font-heading`) after the theme to restyle every component at once, or target `.bonsai-root` descendants for finer overrides.
|
|
283
|
+
- Tailwind CSS v4 is bundled with a `bonsai:` prefix to avoid class collisions. Host applications can safely include their own Tailwind build.
|
|
284
|
+
- `src/index.css` registers all theme tokens (colors, spacing, shadows, fonts) and safelists commonly generated utility classes.
|
|
285
|
+
- Custom fonts (e.g. for signatures) are declared locally and loaded via `@font-face`.
|
|
286
|
+
- To override styling, supply a `className` through the schema or wrap Bonsai components in host containers.
|
|
308
287
|
|
|
309
288
|
## Extending Bonsai
|
|
310
289
|
|
|
@@ -316,17 +295,14 @@ Bonsai is styled with **Tailwind CSS v4**. Components emit plain Tailwind utilit
|
|
|
316
295
|
2. **Custom data handling**:
|
|
317
296
|
- Use `useUpdateData()` from a component mounted inside a `Bonsai` instance's subtree for simple mutations.
|
|
318
297
|
3. **Alternate styling**:
|
|
319
|
-
-
|
|
298
|
+
- Tailwind tokens can be overridden by redefining CSS variables before mounting Bonsai.
|
|
320
299
|
|
|
321
300
|
## Development
|
|
322
301
|
|
|
323
|
-
Issues and pull requests are welcome at [PebbleSoftwareInc/pebble-bonsai](https://github.com/PebbleSoftwareInc/pebble-bonsai). This package lives at `apps/bonsai` in the monorepo.
|
|
324
|
-
|
|
325
302
|
```bash
|
|
326
|
-
pnpm lint
|
|
327
|
-
pnpm test
|
|
328
|
-
pnpm
|
|
329
|
-
pnpm build # Bundles dist/ (ESM + type declarations) via Vite
|
|
303
|
+
pnpm lint # Static analysis using ESLint
|
|
304
|
+
pnpm test # Run the test suite (vitest)
|
|
305
|
+
pnpm build # Builds dependencies then bundles dist/ via Vite
|
|
330
306
|
```
|
|
331
307
|
|
|
332
|
-
`dist/` contains the ESM bundle and TypeScript declarations consumed by downstream applications.
|
|
308
|
+
`dist/` contains the ESM bundle and TypeScript declarations consumed by downstream applications.
|
package/dist/index.css
ADDED
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
@import "tailwindcss";
|
|
2
|
+
@import "tw-animate-css";
|
|
3
|
+
|
|
4
|
+
/* --- Fonts --- */
|
|
5
|
+
@font-face {
|
|
6
|
+
font-family: "Great Vibes";
|
|
7
|
+
src: url("https://brand.pebble.software/fonts/GreatVibes-Regular.ttf")
|
|
8
|
+
format("truetype");
|
|
9
|
+
font-weight: auto;
|
|
10
|
+
font-style: normal;
|
|
11
|
+
font-display: swap;
|
|
12
|
+
}
|
|
13
|
+
@font-face {
|
|
14
|
+
font-family: "Sora";
|
|
15
|
+
src: url("https://brand.pebble.software/fonts/Sora-VariableFont_wght.ttf")
|
|
16
|
+
format("truetype");
|
|
17
|
+
font-weight: 100 900;
|
|
18
|
+
font-style: normal;
|
|
19
|
+
font-display: swap;
|
|
20
|
+
}
|
|
21
|
+
@font-face {
|
|
22
|
+
font-family: "Fredoka";
|
|
23
|
+
src: url("https://brand.pebble.software/fonts/Fredoka-VariableFont_wdth,wght.ttf")
|
|
24
|
+
format("truetype");
|
|
25
|
+
font-weight: 300 700;
|
|
26
|
+
font-style: normal;
|
|
27
|
+
font-display: swap;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@theme {
|
|
31
|
+
/* --- Typography --- */
|
|
32
|
+
--font-body: "Sora", ui-sans-serif, system-ui, sans-serif;
|
|
33
|
+
--font-heading: "Fredoka", ui-monospace, monospace;
|
|
34
|
+
--font-signature: "Great Vibes", cursive;
|
|
35
|
+
--font-weight-base: 400;
|
|
36
|
+
--font-weight-heading: 600;
|
|
37
|
+
|
|
38
|
+
/* --- Core Colors (Default - Emerald) --- */
|
|
39
|
+
--color-main: var(--color-accent-emerald);
|
|
40
|
+
--color-main-secondary: var(--color-accent-emerald-secondary);
|
|
41
|
+
--color-main-foreground: oklch(0% 0 0);
|
|
42
|
+
|
|
43
|
+
--color-background: var(--color-white);
|
|
44
|
+
--color-foreground: oklch(0% 0 0);
|
|
45
|
+
--color-surface: oklch(100% 0 0);
|
|
46
|
+
--color-muted: oklch(0% 0 0 / 0.65);
|
|
47
|
+
--color-subtle: oklch(0% 0 0 / 0.5);
|
|
48
|
+
|
|
49
|
+
--color-border: oklch(0% 0 0);
|
|
50
|
+
--color-ring: oklch(0% 0 0);
|
|
51
|
+
--color-overlay: oklch(0% 0 0 / 0.8);
|
|
52
|
+
|
|
53
|
+
/* --- Accent Palette --- */
|
|
54
|
+
--color-accent-red: oklch(67.28% 0.2147 24.22);
|
|
55
|
+
--color-accent-red-secondary: oklch(93.3% 0.0339 17.77);
|
|
56
|
+
|
|
57
|
+
--color-accent-orange: oklch(72.27% 0.1894 50.19);
|
|
58
|
+
--color-accent-orange-secondary: oklch(95.38% 0.0357 72.89);
|
|
59
|
+
|
|
60
|
+
--color-accent-amber: oklch(84.08% 0.1725 84.2);
|
|
61
|
+
--color-accent-amber-secondary: oklch(96% 0.04 85);
|
|
62
|
+
|
|
63
|
+
--color-accent-yellow: oklch(86.03% 0.176 92.36);
|
|
64
|
+
--color-accent-yellow-secondary: oklch(96.79% 0.0654 102.26);
|
|
65
|
+
|
|
66
|
+
--color-accent-lime: oklch(83.29% 0.2331 132.51);
|
|
67
|
+
--color-accent-lime-secondary: oklch(95.37% 0.0549 125.19);
|
|
68
|
+
|
|
69
|
+
--color-accent-green: oklch(79.76% 0.2044 153.08);
|
|
70
|
+
--color-accent-green-secondary: oklch(96.47% 0.0401 157.79);
|
|
71
|
+
|
|
72
|
+
--color-accent-emerald: oklch(77.54% 0.1681 162.78);
|
|
73
|
+
--color-accent-emerald-secondary: oklch(95.31% 0.0496 169.04);
|
|
74
|
+
|
|
75
|
+
--color-accent-teal: oklch(78.57% 0.1422 180.36);
|
|
76
|
+
--color-accent-teal-secondary: oklch(95.08% 0.0481 184.07);
|
|
77
|
+
|
|
78
|
+
--color-accent-cyan: oklch(76.89% 0.139164 219.13);
|
|
79
|
+
--color-accent-cyan-secondary: oklch(94.61% 0.043 211.12);
|
|
80
|
+
|
|
81
|
+
--color-accent-sky: oklch(66.9% 0.18368 248.8066);
|
|
82
|
+
--color-accent-sky-secondary: oklch(94.27% 0.0268 242.57);
|
|
83
|
+
|
|
84
|
+
--color-accent-blue: oklch(67.47% 0.1726 259.49);
|
|
85
|
+
--color-accent-blue-secondary: oklch(93.46% 0.0305 255.11);
|
|
86
|
+
|
|
87
|
+
--color-accent-indigo: oklch(66.34% 0.1806 277.2);
|
|
88
|
+
--color-accent-indigo-secondary: oklch(92.13% 0.0388 282.36);
|
|
89
|
+
|
|
90
|
+
--color-accent-violet: oklch(70.28% 0.1753 295.36);
|
|
91
|
+
--color-accent-violet-secondary: oklch(93% 0.04 295);
|
|
92
|
+
|
|
93
|
+
--color-accent-purple: oklch(71.9% 0.198 310.03);
|
|
94
|
+
--color-accent-purple-secondary: oklch(94.11% 0.036556 308.0303);
|
|
95
|
+
|
|
96
|
+
--color-accent-fuchsia: oklch(73.43% 0.2332 321.41);
|
|
97
|
+
--color-accent-fuchsia-secondary: oklch(94% 0.04 321);
|
|
98
|
+
|
|
99
|
+
--color-accent-pink: oklch(71.5% 0.197 354.23);
|
|
100
|
+
--color-accent-pink-secondary: oklch(95.16% 0.0242 343.23);
|
|
101
|
+
|
|
102
|
+
--color-accent-rose: oklch(70.79% 0.1862 16.25);
|
|
103
|
+
--color-accent-rose-secondary: oklch(93.37% 0.0339 12.05);
|
|
104
|
+
|
|
105
|
+
/* --- Chart Colors (for Recharts graphs only) --- */
|
|
106
|
+
--color-chart-1: var(--color-accent-red);
|
|
107
|
+
--color-chart-2: var(--color-accent-orange);
|
|
108
|
+
--color-chart-3: var(--color-accent-amber);
|
|
109
|
+
--color-chart-4: var(--color-accent-yellow);
|
|
110
|
+
--color-chart-5: var(--color-accent-lime);
|
|
111
|
+
--color-chart-6: var(--color-accent-green);
|
|
112
|
+
--color-chart-7: var(--color-accent-emerald);
|
|
113
|
+
--color-chart-8: var(--color-accent-teal);
|
|
114
|
+
--color-chart-9: var(--color-accent-cyan);
|
|
115
|
+
--color-chart-10: var(--color-accent-sky);
|
|
116
|
+
--color-chart-11: var(--color-accent-blue);
|
|
117
|
+
--color-chart-12: var(--color-accent-indigo);
|
|
118
|
+
--color-chart-13: var(--color-accent-violet);
|
|
119
|
+
--color-chart-14: var(--color-accent-purple);
|
|
120
|
+
--color-chart-15: var(--color-accent-fuchsia);
|
|
121
|
+
--color-chart-16: var(--color-accent-pink);
|
|
122
|
+
--color-chart-17: var(--color-accent-rose);
|
|
123
|
+
--color-chart-active-dot: var(--color-foreground);
|
|
124
|
+
|
|
125
|
+
/* --- Borders --- */
|
|
126
|
+
--default-border-width: 2px;
|
|
127
|
+
|
|
128
|
+
/* --- Shadows & Spacing --- */
|
|
129
|
+
--radius-base: var(--radius-lg);
|
|
130
|
+
--spacing-boxShadowX: 3px;
|
|
131
|
+
--spacing-boxShadowY: 3px;
|
|
132
|
+
|
|
133
|
+
/* --- Drop Shadows --- */
|
|
134
|
+
--shadow-min: 1px 1px 0px 0px var(--color-border);
|
|
135
|
+
--shadow-xs: 2px 2px 0px 0px var(--color-border);
|
|
136
|
+
--shadow-sm: 3px 3px 0px 0px var(--color-border);
|
|
137
|
+
--shadow-md: 4px 4px 0px 0px var(--color-border);
|
|
138
|
+
--shadow-lg: 4.75px 4.75px 0px 0px var(--color-border);
|
|
139
|
+
--shadow-xl: 5.5px 5.5px 0px 0px var(--color-border);
|
|
140
|
+
|
|
141
|
+
--shadow-default: var(--shadow-md);
|
|
142
|
+
|
|
143
|
+
/* --- Inset Shadows --- */
|
|
144
|
+
--inset-shadow-xs: inset 1px 1px 0px 0px var(--color-border);
|
|
145
|
+
--inset-shadow-sm: inset 2px 2px 0px 0px var(--color-border);
|
|
146
|
+
--inset-shadow-md: inset 3px 3px 0px 0px var(--color-border);
|
|
147
|
+
--inset-shadow-lg: inset 4px 4px 0px 0px var(--color-border);
|
|
148
|
+
--inset-shadow-xl: inset 5px 5px 0px 0px var(--color-border);
|
|
149
|
+
|
|
150
|
+
/* --- Aspect Ratios --- */
|
|
151
|
+
--aspect-a4: 210/297;
|
|
152
|
+
--aspect-a4-landscape: 297/210;
|
|
153
|
+
|
|
154
|
+
/* --- Animations & Keyframes --- */
|
|
155
|
+
--animate-cloud-move: cloud-move 60s linear infinite alternate;
|
|
156
|
+
--animate-float: float 3s ease-in-out infinite;
|
|
157
|
+
--animate-subtle-float: subtle-float 3s ease-in-out infinite;
|
|
158
|
+
--animate-color-cycle: color-cycle 30s ease-in-out infinite;
|
|
159
|
+
--animate-color-cycle-vibrant: color-cycle-vibrant 30s ease-in-out infinite;
|
|
160
|
+
--animate-bg-color-cycle: bg-color-cycle 30s ease-in-out infinite;
|
|
161
|
+
--animate-bg-color-cycle-vibrant: bg-color-cycle-vibrant 30s ease-in-out
|
|
162
|
+
infinite;
|
|
163
|
+
--animate-font-color-cycle-vibrant: font-color-cycle-vibrant 30s ease-in-out
|
|
164
|
+
infinite;
|
|
165
|
+
|
|
166
|
+
@keyframes cloud-move {
|
|
167
|
+
0% {
|
|
168
|
+
transform: translateX(0);
|
|
169
|
+
}
|
|
170
|
+
100% {
|
|
171
|
+
transform: translateX(-100vw);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
@keyframes float {
|
|
175
|
+
0%,
|
|
176
|
+
100% {
|
|
177
|
+
transform: translateY(0%);
|
|
178
|
+
}
|
|
179
|
+
50% {
|
|
180
|
+
transform: translateY(-5%);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
@keyframes subtle-float {
|
|
184
|
+
0%,
|
|
185
|
+
100% {
|
|
186
|
+
transform: translateY(0%);
|
|
187
|
+
}
|
|
188
|
+
50% {
|
|
189
|
+
transform: translateY(-2%);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
@keyframes bg-color-cycle {
|
|
194
|
+
0% {
|
|
195
|
+
background-color: var(--color-accent-red-secondary);
|
|
196
|
+
}
|
|
197
|
+
50% {
|
|
198
|
+
background-color: var(--color-accent-cyan-secondary);
|
|
199
|
+
}
|
|
200
|
+
100% {
|
|
201
|
+
background-color: var(--color-accent-rose-secondary);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
@keyframes bg-color-cycle-vibrant {
|
|
205
|
+
0% {
|
|
206
|
+
background-color: var(--color-accent-red);
|
|
207
|
+
}
|
|
208
|
+
50% {
|
|
209
|
+
background-color: var(--color-accent-cyan);
|
|
210
|
+
}
|
|
211
|
+
100% {
|
|
212
|
+
background-color: var(--color-accent-rose);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
@keyframes font-color-cycle-vibrant {
|
|
216
|
+
0% {
|
|
217
|
+
color: var(--color-accent-red);
|
|
218
|
+
}
|
|
219
|
+
50% {
|
|
220
|
+
color: var(--color-accent-cyan);
|
|
221
|
+
}
|
|
222
|
+
100% {
|
|
223
|
+
color: var(--color-accent-rose);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/* --- Global Resets & Theme Overrides --- */
|
|
229
|
+
@layer base {
|
|
230
|
+
/* THEME DEFINITIONS
|
|
231
|
+
Usage: <html data-theme="red"> or <body data-theme="indigo">
|
|
232
|
+
Logic:
|
|
233
|
+
- Main: The vibrant accent color
|
|
234
|
+
- Background: The secondary pastel version
|
|
235
|
+
- Main-Foreground: White for dark accents, Black for light accents (EX. text on buttons)
|
|
236
|
+
*/
|
|
237
|
+
|
|
238
|
+
[data-theme="red"] {
|
|
239
|
+
--color-main: var(--color-accent-red);
|
|
240
|
+
--color-main-secondary: var(--color-accent-red-secondary);
|
|
241
|
+
--color-background: var(--color-accent-red-secondary);
|
|
242
|
+
--color-main-foreground: oklch(100% 0 0);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
[data-theme="orange"] {
|
|
246
|
+
--color-main: var(--color-accent-orange);
|
|
247
|
+
--color-main-secondary: var(--color-accent-orange-secondary);
|
|
248
|
+
--color-background: var(--color-accent-orange-secondary);
|
|
249
|
+
--color-main-foreground: oklch(0% 0 0);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
[data-theme="amber"] {
|
|
253
|
+
--color-main: var(--color-accent-amber);
|
|
254
|
+
--color-main-secondary: var(--color-accent-amber-secondary);
|
|
255
|
+
--color-background: var(--color-accent-amber-secondary);
|
|
256
|
+
--color-main-foreground: oklch(0% 0 0);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
[data-theme="yellow"] {
|
|
260
|
+
--color-main: var(--color-accent-yellow);
|
|
261
|
+
--color-main-secondary: var(--color-accent-yellow-secondary);
|
|
262
|
+
--color-background: var(--color-accent-yellow-secondary);
|
|
263
|
+
--color-main-foreground: oklch(0% 0 0);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
[data-theme="lime"] {
|
|
267
|
+
--color-main: var(--color-accent-lime);
|
|
268
|
+
--color-main-secondary: var(--color-accent-lime-secondary);
|
|
269
|
+
--color-background: var(--color-accent-lime-secondary);
|
|
270
|
+
--color-main-foreground: oklch(0% 0 0);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
[data-theme="green"] {
|
|
274
|
+
--color-main: var(--color-accent-green);
|
|
275
|
+
--color-main-secondary: var(--color-accent-green-secondary);
|
|
276
|
+
--color-background: var(--color-accent-green-secondary);
|
|
277
|
+
--color-main-foreground: oklch(0% 0 0);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
[data-theme="emerald"] {
|
|
281
|
+
--color-main: var(--color-accent-emerald);
|
|
282
|
+
--color-main-secondary: var(--color-accent-emerald-secondary);
|
|
283
|
+
--color-background: var(--color-accent-emerald-secondary);
|
|
284
|
+
--color-main-foreground: oklch(0% 0 0);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
[data-theme="teal"] {
|
|
288
|
+
--color-main: var(--color-accent-teal);
|
|
289
|
+
--color-main-secondary: var(--color-accent-teal-secondary);
|
|
290
|
+
--color-background: var(--color-accent-teal-secondary);
|
|
291
|
+
--color-main-foreground: oklch(0% 0 0);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
[data-theme="cyan"] {
|
|
295
|
+
--color-main: var(--color-accent-cyan);
|
|
296
|
+
--color-main-secondary: var(--color-accent-cyan-secondary);
|
|
297
|
+
--color-background: var(--color-accent-cyan-secondary);
|
|
298
|
+
--color-main-foreground: oklch(0% 0 0);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
[data-theme="sky"] {
|
|
302
|
+
--color-main: var(--color-accent-sky);
|
|
303
|
+
--color-main-secondary: var(--color-accent-sky-secondary);
|
|
304
|
+
--color-background: var(--color-accent-sky-secondary);
|
|
305
|
+
--color-main-foreground: oklch(0% 0 0);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
[data-theme="blue"] {
|
|
309
|
+
--color-main: var(--color-accent-blue);
|
|
310
|
+
--color-main-secondary: var(--color-accent-blue-secondary);
|
|
311
|
+
--color-background: var(--color-accent-blue-secondary);
|
|
312
|
+
--color-main-foreground: oklch(100% 0 0);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
[data-theme="indigo"] {
|
|
316
|
+
--color-main: var(--color-accent-indigo);
|
|
317
|
+
--color-main-secondary: var(--color-accent-indigo-secondary);
|
|
318
|
+
--color-background: var(--color-accent-indigo-secondary);
|
|
319
|
+
--color-main-foreground: oklch(100% 0 0);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
[data-theme="violet"] {
|
|
323
|
+
--color-main: var(--color-accent-violet);
|
|
324
|
+
--color-main-secondary: var(--color-accent-violet-secondary);
|
|
325
|
+
--color-background: var(--color-accent-violet-secondary);
|
|
326
|
+
--color-main-foreground: oklch(100% 0 0);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
[data-theme="purple"] {
|
|
330
|
+
--color-main: var(--color-accent-purple);
|
|
331
|
+
--color-main-secondary: var(--color-accent-purple-secondary);
|
|
332
|
+
--color-background: var(--color-accent-purple-secondary);
|
|
333
|
+
--color-main-foreground: oklch(100% 0 0);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
[data-theme="fuchsia"] {
|
|
337
|
+
--color-main: var(--color-accent-fuchsia);
|
|
338
|
+
--color-main-secondary: var(--color-accent-fuchsia-secondary);
|
|
339
|
+
--color-background: var(--color-accent-fuchsia-secondary);
|
|
340
|
+
--color-main-foreground: oklch(0% 0 0);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
[data-theme="pink"] {
|
|
344
|
+
--color-main: var(--color-accent-pink);
|
|
345
|
+
--color-main-secondary: var(--color-accent-pink-secondary);
|
|
346
|
+
--color-background: var(--color-accent-pink-secondary);
|
|
347
|
+
--color-main-foreground: oklch(0% 0 0);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
[data-theme="rose"] {
|
|
351
|
+
--color-main: var(--color-accent-rose);
|
|
352
|
+
--color-main-secondary: var(--color-accent-rose-secondary);
|
|
353
|
+
--color-background: var(--color-accent-rose-secondary);
|
|
354
|
+
--color-main-foreground: oklch(100% 0 0);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
html { }
|
|
358
|
+
|
|
359
|
+
body {
|
|
360
|
+
font-family: var(--font-body);
|
|
361
|
+
background-color: var(--color-background);
|
|
362
|
+
color: var(--color-foreground);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
h1,
|
|
366
|
+
h2,
|
|
367
|
+
h3,
|
|
368
|
+
h4,
|
|
369
|
+
h5,
|
|
370
|
+
h6 {
|
|
371
|
+
font-family: var(--font-heading);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/* --- Utilities --- */
|
|
376
|
+
@layer utilities {
|
|
377
|
+
.neo-base {
|
|
378
|
+
@apply touch-manipulation select-none appearance-none outline-none;
|
|
379
|
+
}
|
|
380
|
+
.neo-popup-focus {
|
|
381
|
+
@apply focus-visible:after:absolute focus-visible:after:-inset-1 focus-visible:after:border-8 focus-visible:after:pointer-events-none
|
|
382
|
+
focus-visible:after:border-foreground focus-visible:after:rounded-[inherit] focus-visible:after:blur-[2px];
|
|
383
|
+
}
|
|
384
|
+
.neo-accordion-focus {
|
|
385
|
+
@apply focus:outline-none focus-visible:after:absolute focus-visible:after:-inset-px focus-visible:after:border-2 focus-visible:after:pointer-events-none
|
|
386
|
+
focus-visible:after:border-foreground/30 focus-visible:after:rounded-sm;
|
|
387
|
+
}
|
|
388
|
+
.neo-button-focus {
|
|
389
|
+
@apply focus:outline-none focus-visible:after:absolute focus-visible:after:-inset-px focus-visible:after:border-3 focus-visible:after:pointer-events-none
|
|
390
|
+
focus-visible:after:border-foreground/30 focus-visible:after:rounded-[inherit];
|
|
391
|
+
}
|
|
392
|
+
.neo-input-focus {
|
|
393
|
+
@apply outline-none hover:shadow-sm hover:-translate-[3px] focus-visible:shadow-sm focus-visible:-translate-[3px];
|
|
394
|
+
}
|
|
395
|
+
.neo-switch-focus {
|
|
396
|
+
@apply outline-none hover:shadow-xs hover:-translate-[2px] focus-visible:shadow-xs focus-visible:-translate-[2px];
|
|
397
|
+
}
|
|
398
|
+
.neo-chips-focus {
|
|
399
|
+
@apply translate-0 has-data-pressed:shadow-sm has-data-pressed:-translate-[3px] outline-none hover:shadow-sm hover:-translate-[3px] focus-visible:shadow-sm focus-visible:-translate-[3px];
|
|
400
|
+
}
|
|
401
|
+
.neo-checkbox-focus {
|
|
402
|
+
@apply hover:shadow-xs hover:-translate-0.5 focus-visible:shadow-xs focus-visible:-translate-0.5 active:translate-0 active:shadow-none;
|
|
403
|
+
}
|
|
404
|
+
.neo-sidebar-button-focus {
|
|
405
|
+
@apply border-2 border-transparent focus-visible:border-black hover:border-black hover:shadow-sm hover:-translate-[3px] focus-visible:shadow-sm focus-visible:-translate-[3px] active:shadow-none active:translate-0;
|
|
406
|
+
}
|
|
407
|
+
.neo-scrollbar-focus {
|
|
408
|
+
@apply active:z-14 hover:z-12 focus:[&_svg]:scale-110 focus:z-12 focus:[&_svg]:fill-main hover:[&_svg]:fill-main focus:[&_svg]:text-main hover:[&_svg]:text-main;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
.no-scrollbar::-webkit-scrollbar {
|
|
412
|
+
display: none;
|
|
413
|
+
}
|
|
414
|
+
.no-scrollbar {
|
|
415
|
+
-ms-overflow-style: none;
|
|
416
|
+
scrollbar-width: none;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/* --- 3rd Party Overrides --- */
|
|
421
|
+
.recharts-wrapper > svg > g {
|
|
422
|
+
outline: none !important;
|
|
423
|
+
}
|
|
424
|
+
.recharts-wrapper .recharts-cartesian-grid-horizontal line:last-child {
|
|
425
|
+
stroke-opacity: 0;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
.maplibregl-ctrl {
|
|
429
|
+
@apply bg-surface! outline-0! shadow-none! *:[button]:rounded-base *:[button]:bg-surface! *:[button]:hover:bg-main! *:[button]:transition-all *:[button]:duration-200 *:[button]:border-2! *:[button]:border-border! *:[button]:shadow-xs! *:[button]:-translate-0.5! *:[button]:hover:shadow-min! *:[button]:hover:-translate-px! *:[button]:focus-visible:-translate-px! *:[button]:focus-visible:shadow-min! *:[button]:active:shadow-none! *:[button]:active:translate-0! *:[button]:aria-disabled:shadow-none! *:[button]:aria-disabled:translate-0!;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
.maplibregl-ctrl-zoom-in {
|
|
433
|
+
@apply -mb-0.5! rounded-b-none!;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
.maplibregl-ctrl-zoom-out {
|
|
437
|
+
@apply rounded-t-none!;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
.maplibregl-ctrl-fullscreen {
|
|
441
|
+
@apply p-1!;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
.maplibregl-ctrl-scale {
|
|
445
|
+
@apply transition-[width] duration-500 delay-100 rounded-sm! border-2! border-border! hover:bg-main! bg-surface! font-bold! leading-none! py-1! shadow-sm! font-mono! text-xs!;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
.maplibregl-user-location-dot {
|
|
449
|
+
@apply shadow-xl! bg-accent-blue! before:bg-accent-blue! after:border-border!;
|
|
450
|
+
}
|
|
451
|
+
.maplibregl-user-location-accuracy-circle {
|
|
452
|
+
@apply bg-accent-blue/20!;
|
|
453
|
+
}
|