@roxyapi/ui 0.8.1 → 0.9.0

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/AGENTS.md CHANGED
@@ -120,15 +120,17 @@ const { data: chart } = await roxy.astrology.generateNatalChart({
120
120
 
121
121
  Every chart endpoint accepts `timezone` as either a decimal-hour offset (`5.5` for IST, `-5` for EST) or an IANA name (`'Asia/Kolkata'`, `'America/New_York'`). The decimal form is what `/location/search` returns; the IANA form is correct over DST boundaries. Pick one and stay consistent in a single integration. Mixing them does not break the API but makes the bug surface area larger.
122
122
 
123
- ### 4. API key in the browser
123
+ ### 4. Secret key in the browser
124
124
 
125
- Keys are server side only. Call `createRoxy(process.env.ROXY_API_KEY!)` on your server (Node, Bun, Hono, Next.js route handlers, Workers, Edge functions), then send the response, not the key, to the component. Never ship the key in a client bundle. Browser-safe keys for direct client-side embedding are on the roadmap, not yet available.
125
+ Secret keys (`sk_*`) grant full account access and are server side only. Call `createRoxy(process.env.ROXY_API_KEY!)` on your server (Node, Bun, Hono, Next.js route handlers, Workers, Edge functions), then send the response, not the key, to the component. Never ship a secret key in a client bundle.
126
126
 
127
127
  ```ts
128
- // Server side only
128
+ // Secret key: server side only
129
129
  const roxy = createRoxy(process.env.ROXY_API_KEY!);
130
130
  ```
131
131
 
132
+ For direct client-side calls, use a **publishable key** (`pk_live_*` / `pk_test_*`) instead. Publishable keys are browser-safe: mint one at `roxyapi.com/account`, register the origins you embed on, and the API gateway returns 403 for any other origin. See the client-side pattern below.
133
+
132
134
  ### 5. Missing `'use client'` in Next.js App Router
133
135
 
134
136
  The React components in `@roxyapi/ui-react` mount Custom Elements, which need the DOM. In the App Router, files that import them must declare `'use client'` at the top. Server Components can fetch with the SDK; the client component renders.
@@ -269,9 +271,30 @@ For a static chart with no picker, fetch in a Server Component and pass `data` t
269
271
  </script>
270
272
  ```
271
273
 
272
- ### Pattern 4: widgets auto-mount (coming soon)
274
+ ### Pattern 4: fully client-side with a publishable key (no server)
275
+
276
+ When you do not want a backend at all, mint a **publishable key** (`pk_live_*`) at `roxyapi.com/account`, register the origins you embed on, and call RoxyAPI directly from the browser. The publishable key is safe to ship in client code: it is origin-restricted (any other origin gets 403) and cannot read your account. `<roxy-location-search>` accepts the key via its `publishable-key` attribute and fetches geocoding itself; for the data endpoints you make a normal browser `fetch` with the key in the `X-API-Key` header, then assign the response to the rendering component.
277
+
278
+ ```html
279
+ <roxy-location-search publishable-key="pk_live_..."></roxy-location-search>
280
+ <roxy-natal-chart></roxy-natal-chart>
281
+
282
+ <script type="module">
283
+ const chart = document.querySelector('roxy-natal-chart');
284
+ document.querySelector('roxy-location-search').addEventListener('roxy-location-select', async (e) => {
285
+ const { latitude, longitude, timezone } = e.detail;
286
+ if (latitude == null || longitude == null) return;
287
+ const res = await fetch('https://roxyapi.com/api/v2/astrology/natal-chart', {
288
+ method: 'POST',
289
+ headers: { 'X-API-Key': 'pk_live_...', 'Content-Type': 'application/json' },
290
+ body: JSON.stringify({ date: '1990-01-15', time: '14:30:00', latitude, longitude, timezone }),
291
+ });
292
+ chart.data = await res.json(); // pass the unwrapped response, not an envelope
293
+ });
294
+ </script>
295
+ ```
273
296
 
274
- A zero-wiring embed that reads `data-*` attributes and renders the matching component is on the roadmap. It needs browser-safe keys, which are not yet available. Until then, use Pattern 1 (inline JSON) for no-build pages.
297
+ Rendering components do not fetch on their own; you set `data`. Only `<roxy-location-search>` (and `<roxy-endpoint-form>` for the spec) fetch internally. A zero-wiring `data-*` auto-mount that fetches and renders with no script is still on the roadmap.
275
298
 
276
299
  ### Pattern 5: MCP tool-call response
277
300
 
@@ -339,13 +362,13 @@ This is how the WordPress plugin renders: PHP fetches the response server-side,
339
362
 
340
363
  ## Theming and dark mode
341
364
 
342
- Components react to three signals in priority order. No events to dispatch. No JS bridge to write.
365
+ Components react to three signals in priority order. No events to dispatch. No JS bridge to write. The CDN bundle (`dist/cdn/roxy-ui.js`) auto-loads the design tokens, so a single script tag yields full theming and dark mode with nothing else to add. The npm and React paths inherit the same tokens through the components; only set up `tokens.css` yourself if you import per component without the full bundle.
343
366
 
344
367
  | Signal | Where | Effect |
345
368
  |---|---|---|
346
369
  | `prefers-color-scheme: dark` | OS | Default. Follows user system setting. |
347
370
  | `data-theme="light"` or `data-theme="dark"` | `<html>` / `<body>` / any ancestor / the component itself | Wins over OS. Per-element override scope works. |
348
- | `.dark` class | Any ancestor | Equivalent to `data-theme="dark"`. Use when the host stack already ships a `.dark` toggle (Tailwind, shadcn). |
371
+ | `.dark` class | The component itself or any ancestor (typically `<html>`) | Same effect as `data-theme="dark"`. Use when the host stack already ships a `.dark` toggle (Tailwind, shadcn). |
349
372
 
350
373
  To toggle at runtime:
351
374
 
package/README.md CHANGED
@@ -55,7 +55,7 @@ Light, dark, your brand. Override one CSS variable and every component updates.
55
55
  ```css
56
56
  :root {
57
57
  /* Surface */
58
- --roxy-bg: #fafafa;
58
+ --roxy-bg: #ffffff;
59
59
  --roxy-fg: #0a0a0a;
60
60
  --roxy-muted: #71717a;
61
61
  --roxy-border: #e4e4e7;
@@ -66,13 +66,13 @@ Light, dark, your brand. Override one CSS variable and every component updates.
66
66
 
67
67
  /* Status (each has a -fg variant for WCAG-AA text contrast) */
68
68
  --roxy-success: #16a34a;
69
- --roxy-warning: #f59e0b;
69
+ --roxy-warning: #ea580c;
70
70
  --roxy-danger: #dc2626;
71
- --roxy-info: #2563eb;
71
+ --roxy-info: #0284c7;
72
72
 
73
73
  /* Shape + motion */
74
- --roxy-radius-md: 12px;
75
- --roxy-shadow-md: 0 4px 12px rgba(0,0,0,0.08);
74
+ --roxy-radius-md: 8px;
75
+ --roxy-shadow-md: 0 4px 6px -1px rgba(0,0,0,0.08), 0 2px 4px -2px rgba(0,0,0,0.06);
76
76
  --roxy-motion-duration: 200ms; /* 0ms when prefers-reduced-motion */
77
77
  }
78
78
 
@@ -294,7 +294,7 @@ Always call `/location/search` first. Every chart endpoint expects latitude, lon
294
294
 
295
295
  Server-rendered and cached pages (WordPress, JSX SSR, static HTML) cannot always run JavaScript to set the `data` property per element. Render the response into a child `<script type="application/json" class="roxy-data">` on the server instead. The component reads it on load. No per-element script, no API key in the browser.
296
296
 
297
- Load the bundle once anywhere on the page. It registers every `roxy-*` element, so every component on the page renders from that single tag.
297
+ Load the bundle once anywhere on the page. It registers every `roxy-*` element and loads the design tokens, so every component on the page renders themed, in light or dark, from that single tag. Nothing else to add.
298
298
 
299
299
  ```html
300
300
  <!-- Once per page: defines every roxy-* element -->
@@ -535,11 +535,9 @@ const { data: random } = await roxy.iching.getRandomHexagram();
535
535
 
536
536
  Get a key at <https://roxyapi.com/account>.
537
537
 
538
- Today every key is a **secret key**: use it server side only (Node, Bun, Hono, Next.js route handlers, Workers). Never commit it, never ship it in a client bundle. Fetch on your server and send the rendered response, not the key, to the browser. The [Start with one component](#start-with-one-component) section and the [framework recipes](#most-used-components-per-domain) show the pattern.
538
+ Two key types. **Secret keys** (`sk_*`) grant full account access: use them server side only (Node, Bun, Hono, Next.js route handlers, Workers). Never commit one, never ship one in a client bundle. **Publishable keys** (`pk_live_*` / `pk_test_*`) are browser-safe: mint one, register the origins you embed on, and any other origin gets a 403 at the gateway. Use a publishable key when you call RoxyAPI directly from the browser with no backend.
539
539
 
540
- Set `ROXY_API_KEY` to your secret key in your server env for every SDK example on this page.
541
-
542
- Browser-safe keys for direct client-side embedding are on the roadmap, not yet available. Until they ship, keep the fetch on your server.
540
+ Set `ROXY_API_KEY` to your secret key in your server env for the server-side SDK examples on this page. For direct client-side embedding with no backend, use a publishable key (see the fully client-side pattern in [`AGENTS.md`](AGENTS.md)).
543
541
 
544
542
  ## Distribution
545
543
 
@@ -603,7 +601,7 @@ Browser-safe keys for direct client-side embedding are on the roadmap, not yet a
603
601
 
604
602
  ## Theming
605
603
 
606
- Every component reads from `--roxy-*` CSS custom properties. Override globally on `:root` or per element. Light + dark defaults, container queries for responsive layouts at 320px and up. See [THEMING.md](https://github.com/RoxyAPI/ui/blob/main/packages/ui/THEMING.md) for the full token reference.
604
+ Every component reads from `--roxy-*` CSS custom properties. Override globally on `:root` or per element. Light + dark defaults, container queries for responsive layouts at 320px and up. The CDN bundle auto-loads these tokens; your `:root { --roxy-* }` overrides always win over the defaults. See [THEMING.md](https://github.com/RoxyAPI/ui/blob/main/packages/ui/THEMING.md) for the full token reference.
607
605
 
608
606
  ```css
609
607
  :root {
@@ -686,7 +684,7 @@ Persist the choice in `localStorage` from your own code; the components do not o
686
684
  <details>
687
685
  <summary><strong>How big is each component? What is the bundle cost?</strong></summary>
688
686
 
689
- Per-component bundles run 6-10 KB gzipped, capped at 30 KB by CI. The full bundle (every component, helpers, base styles) stays well under the 150 KB CI cap, around 45 KB gzipped today. The React package loads the runtime on mount, so a route that renders one chart pays for one component, not the whole catalog. Pin a concrete version in production for byte-stable cache hits.
687
+ Per-component bundles run 6-10 KB gzipped, capped at 30 KB by CI. The full bundle (every component, helpers, base styles, and the inlined design tokens) stays well under the 150 KB CI cap, around 54 KB gzipped today. The React package loads the runtime on mount, so a route that renders one chart pays for one component, not the whole catalog. Pin a concrete version in production for byte-stable cache hits.
690
688
  </details>
691
689
 
692
690
  <details>
@@ -799,7 +797,7 @@ Components ship in Shadow DOM for style isolation; Tailwind utilities are scoped
799
797
  <details>
800
798
  <summary><strong>What is the security model for API keys?</strong></summary>
801
799
 
802
- Today keys are secret keys: they live server side only and grant full access, so never ship one in a client bundle. Fetch on your server and pass the rendered response, not the key, to the browser. Browser-safe keys with an origin allowlist for direct client-side embedding are on the roadmap and not yet available.
800
+ Two key types. Secret keys (`sk_*`) live server side only and grant full access, so never ship one in a client bundle: fetch on your server and pass the rendered response, not the key, to the browser. Publishable keys (`pk_live_*` / `pk_test_*`) are browser-safe for direct client-side embedding: they carry an origin allowlist, so a key leaked to any other origin returns 403 instead of working. Mint either at `roxyapi.com/account`.
803
801
 
804
802
  For CSP, allow `script-src https://cdn.jsdelivr.net` if loading the bundle from the CDN. Subresource Integrity hashes are available via the jsDelivr SRI API for any pinned version.
805
803
  </details>
package/THEMING.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Theming Roxy UI
2
2
 
3
- Every Roxy UI component reads its colors, fonts, spacing, and motion from a single set of CSS custom properties on `:host`. Override them at `:root` to brand the whole library, or scope to one element to skin a single component.
3
+ Every Roxy UI component reads its colors, fonts, spacing, and motion from a single set of `--roxy-*` CSS custom properties. Override them at `:root` to brand the whole library, or scope to one element to skin a single component. Custom properties inherit through the Shadow DOM boundary, so a value set on `:root` or any light-DOM ancestor reaches every component. The CDN bundle auto-loads the token defaults; your `:root` overrides always win over them.
4
4
 
5
5
  ## Token reference
6
6
 
@@ -99,18 +99,20 @@ roxy-natal-chart {
99
99
 
100
100
  ### Dark mode
101
101
 
102
- Three opt-in mechanisms work out of the box.
102
+ Three opt-in mechanisms work out of the box. The CDN bundle auto-loads the tokens, so all three work from one script tag; on the npm path the full `@roxyapi/ui` import pulls the same tokens in.
103
103
 
104
104
  ```css
105
105
  /* System preference: nothing to do */
106
106
 
107
- /* data-theme on the document */
107
+ /* data-theme on the document, an ancestor, or the element itself */
108
108
  :root[data-theme='dark'] { /* automatic */ }
109
109
 
110
- /* Tailwind dark class on an ancestor */
111
- .dark roxy-natal-chart { /* automatic */ }
110
+ /* Tailwind dark class on the document, an ancestor, or the element itself */
111
+ .dark { /* automatic */ }
112
112
  ```
113
113
 
114
+ Tokens set on the `:root` / `.dark` / `[data-theme]` light-DOM element inherit through the shadow boundary into every component, so a `.dark` class anywhere above a component themes it. Per-element scope works too: `<roxy-natal-chart data-theme="dark">` runs one chart in dark on an otherwise light page.
115
+
114
116
  ### Map Tailwind tokens
115
117
 
116
118
  Tailwind users can map our tokens to theirs in five lines of `globals.css`. Pick the syntax that matches your Tailwind version.