@luckystack/docs-ui 0.1.3 → 0.1.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/docs/theming.md CHANGED
@@ -1,172 +1,172 @@
1
- # Theming the docs UI
2
-
3
- > Dev-only. The default docs page is a single self-contained HTML document with inline CSS. Theming is intentionally minimal — three branding tokens for cosmetic tweaks, plus a full template-builder hook when you need a different layout.
4
-
5
- This document covers:
6
-
7
- - `DocsBranding` — the three branding tokens and how they map to CSS variables.
8
- - The default palette + dark/light auto-switch.
9
- - `DocsTemplateBuilder` — the escape hatch for full layout replacement.
10
- - When to choose `branding` vs `template`.
11
-
12
- For the renderer that consumes these tokens see `./html-generation.md`. For the route handler that wires them in see `./mounting.md`.
13
-
14
- ## `DocsBranding`
15
-
16
- ```ts
17
- export interface DocsBranding {
18
- logoUrl?: string;
19
- brandColor?: string;
20
- fontFamily?: string;
21
- }
22
- ```
23
-
24
- All three keys are optional. Pass through `mountDocsUi({ branding: { ... } })` or directly to `renderDocsHtml(jsonPath, pageTitle, { branding })`.
25
-
26
- | Token | Type | Default | Mapping |
27
- | --- | --- | --- | --- |
28
- | `logoUrl` | `string` | _(no logo)_ | Injected as `<img src="..." alt="logo" style="height:32px;width:auto;margin-right:12px;" />` in the brand row, to the left of the `<h1>`. The URL is HTML-escaped before injection. |
29
- | `brandColor` | `string` (CSS color) | `#58a6ff` | Sets the CSS variable `--accent` and (because POST is the canonical authenticated verb in LuckyStack) also `--post`, the POST method pill color. |
30
- | `fontFamily` | `string` (CSS font-family) | `system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif` | Sets `--font-family`. Applied to `<body>` and inherited by inputs (the search bar uses `font-family: inherit`). |
31
-
32
- The renderer applies these tokens in the inline `<style>` block at the top of the document. There is no runtime style swap; changing branding requires a fresh HTML response.
33
-
34
- ### Logo handling
35
-
36
- - The renderer accepts PNG, SVG, and `data:` URLs equally.
37
- - The string is escaped via the same `escapeHtml` helper used for `pageTitle` and `jsonPath`. Embedding hostile content in the `logoUrl` cannot break out of the `src` attribute.
38
- - The img tag is fixed at `height: 32px; width: auto;`. If you need a different size, embed an SVG with a `viewBox` so it scales correctly, or use a `template` builder.
39
- - When `logoUrl` is omitted, the brand row contains only the `<h1>`. There is no placeholder block.
40
-
41
- ### Brand color cascade
42
-
43
- The branding color is wired into two CSS variables:
44
-
45
- ```
46
- --accent: <brandColor>
47
- --post: <brandColor> // (POST method pill)
48
- ```
49
-
50
- All other method colors are fixed:
51
-
52
- ```
53
- --get: #3fb950 (green)
54
- --put: #d29922 (amber, with dark text)
55
- --delete: #f85149 (red)
56
- ```
57
-
58
- This intentional split keeps GET/PUT/DELETE recognizable across themes while letting the POST pill match the brand. If you need per-method theming, use a `template` builder.
59
-
60
- ### Font handling
61
-
62
- `fontFamily` flows into `--font-family` and is consumed by `<body>` via `font-family: var(--font-family);`. The `<input type="search">` uses `font-family: inherit` so the search bar adopts the brand font. Code blocks (`pre`, `.endpoint`, `.try-it-out textarea`) use a fixed monospace stack (`ui-monospace, SFMono-Regular, "SF Mono", Consolas, monospace`) and are not affected by `fontFamily`.
63
-
64
- Quoting and escaping: `fontFamily` is interpolated **as-is** into the CSS string. Provide a valid CSS font-family value, including quotes around multi-word family names when needed (e.g. `'Inter', system-ui, sans-serif`).
65
-
66
- ## Dark / light auto-switch
67
-
68
- The page respects the OS preference via `prefers-color-scheme`. There is no in-page theme toggle.
69
-
70
- Dark palette (default `:root` values):
71
-
72
- ```
73
- --bg: #0e1116 --container: #161b22 --container-hover: #1c232b
74
- --border: #2a313c --title: #e6edf3 --common: #c9d1d9
75
- --muted: #8b949e
76
- ```
77
-
78
- Light palette (overridden inside `@media (prefers-color-scheme: light)`):
79
-
80
- ```
81
- --bg: #ffffff --container: #f6f8fa --container-hover: #eaeef2
82
- --border: #d0d7de --title: #1f2328 --common: #1f2328
83
- --muted: #59636e
84
- ```
85
-
86
- Variables that are **not** overridden in the light block — `--accent`, `--font-family`, `--get`, `--post`, `--put`, `--delete` — are chosen to be legible against both surfaces. The PUT pill flips its text color to dark via `.method.PUT { color: #1f2328; }` to remain legible on amber.
87
-
88
- ## When to use `branding` vs `template`
89
-
90
- Use `branding` (default template) when:
91
-
92
- - You only need to swap the logo, the accent color, and/or the font.
93
- - You're happy with the single-column endpoints-grouped-by-page layout.
94
- - You want light/dark auto-switching for free.
95
- - You don't need to change the inline runner, the filter UI, or the JSON-fetch flow.
96
-
97
- Use `template: DocsTemplateBuilder` when:
98
-
99
- - You need a different layout — sidebar navigation, tabs, a marketing-page wrapper, an embedded auth-token field, a two-pane editor/inspector view.
100
- - You want a per-method palette beyond the POST = `brandColor` cascade.
101
- - You need to integrate with a corporate design system (component library, web fonts loaded from a CDN, etc.).
102
- - You want an in-page light/dark toggle independent of the OS preference.
103
-
104
- ## Example — corporate brand palette
105
-
106
- ```ts
107
- import { mountDocsUi } from '@luckystack/docs-ui';
108
-
109
- export const docsUiHandler = mountDocsUi({
110
- pageTitle: 'Acme — API docs',
111
- branding: {
112
- logoUrl: 'https://cdn.acme.example/logo.svg',
113
- brandColor: '#7c3aed',
114
- fontFamily: `'Inter', system-ui, -apple-system, sans-serif`,
115
- },
116
- });
117
- ```
118
-
119
- This renders the default layout with the Acme logo, purple accent (also applied to the POST pill), and Inter as the body font. GET/PUT/DELETE pills remain green/amber/red.
120
-
121
- ## Example — full template replacement
122
-
123
- ```ts
124
- import { mountDocsUi, type DocsTemplateBuilder } from '@luckystack/docs-ui';
125
-
126
- const sidebarTemplate: DocsTemplateBuilder = ({ jsonPath, pageTitle, branding }) => `
127
- <!DOCTYPE html>
128
- <html lang="en">
129
- <head>
130
- <meta charset="utf-8" />
131
- <title>${pageTitle}</title>
132
- <link rel="stylesheet" href="https://cdn.example.com/corporate-tokens.css" />
133
- </head>
134
- <body>
135
- <aside class="sidebar">${branding.logoUrl ? `<img src="${branding.logoUrl}" />` : ''}</aside>
136
- <main id="content">Loading...</main>
137
- <script>
138
- fetch(${JSON.stringify(jsonPath)}, { credentials: 'include' })
139
- .then(r => r.json())
140
- .then(data => { /* render however you like */ });
141
- </script>
142
- </body>
143
- </html>`;
144
-
145
- export const docsUiHandler = mountDocsUi({
146
- template: sidebarTemplate,
147
- branding: { logoUrl: 'https://cdn.example.com/logo.svg' },
148
- });
149
- ```
150
-
151
- Contract reminders for custom templates (see `./html-generation.md` for the full contract):
152
-
153
- - Return a complete `<!DOCTYPE html>...</html>` document.
154
- - HTML-escape any inputs that end up in markup — the builder receives raw strings.
155
- - Fetch `jsonPath` yourself; the handler does not pre-fetch.
156
- - You may receive a non-empty `branding` even when you don't use it — that's fine, ignore the fields you don't need.
157
-
158
- ## Limits
159
-
160
- - No per-endpoint color override. The method pill colors are fixed CSS variables.
161
- - No runtime theme switcher beyond OS preference in the default template. Implement one inside a custom template if needed.
162
- - No web-font loading. The default template does not inject `<link rel="stylesheet">` tags — bring your own via a custom template.
163
- - No localization. Strings like "Try it out (live request)", "No matches.", "Run `npm run generateArtifacts`" are hardcoded English. Replace via a custom template if you need translations.
164
- - `brandColor` must be a valid single CSS color value (hex, named, `rgb()`, `hsl()`). Gradients are not supported.
165
-
166
- ## Related
167
-
168
- - The renderer that consumes these tokens: `./html-generation.md`
169
- - The mount handler that accepts them: `./mounting.md`
170
- - The JSON fields the page renders: `./extension-fields.md`
171
- - AI summary + function index: `../CLAUDE.md`
172
- - Source: `../src/index.ts`, `../src/docsHtml.ts`
1
+ # Theming the docs UI
2
+
3
+ > Dev-only. The default docs page is a single self-contained HTML document with inline CSS. Theming is intentionally minimal — three branding tokens for cosmetic tweaks, plus a full template-builder hook when you need a different layout.
4
+
5
+ This document covers:
6
+
7
+ - `DocsBranding` — the three branding tokens and how they map to CSS variables.
8
+ - The default palette + dark/light auto-switch.
9
+ - `DocsTemplateBuilder` — the escape hatch for full layout replacement.
10
+ - When to choose `branding` vs `template`.
11
+
12
+ For the renderer that consumes these tokens see `./html-generation.md`. For the route handler that wires them in see `./mounting.md`.
13
+
14
+ ## `DocsBranding`
15
+
16
+ ```ts
17
+ export interface DocsBranding {
18
+ logoUrl?: string;
19
+ brandColor?: string;
20
+ fontFamily?: string;
21
+ }
22
+ ```
23
+
24
+ All three keys are optional. Pass through `mountDocsUi({ branding: { ... } })` or directly to `renderDocsHtml(jsonPath, pageTitle, { branding })`.
25
+
26
+ | Token | Type | Default | Mapping |
27
+ | --- | --- | --- | --- |
28
+ | `logoUrl` | `string` | _(no logo)_ | Injected as `<img src="..." alt="logo" style="height:32px;width:auto;margin-right:12px;" />` in the brand row, to the left of the `<h1>`. The URL is HTML-escaped before injection. |
29
+ | `brandColor` | `string` (CSS color) | `#58a6ff` | Sets the CSS variable `--accent` and (because POST is the canonical authenticated verb in LuckyStack) also `--post`, the POST method pill color. |
30
+ | `fontFamily` | `string` (CSS font-family) | `system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif` | Sets `--font-family`. Applied to `<body>` and inherited by inputs (the search bar uses `font-family: inherit`). |
31
+
32
+ The renderer applies these tokens in the inline `<style>` block at the top of the document. There is no runtime style swap; changing branding requires a fresh HTML response.
33
+
34
+ ### Logo handling
35
+
36
+ - The renderer accepts PNG, SVG, and `data:` URLs equally.
37
+ - The string is escaped via the same `escapeHtml` helper used for `pageTitle` and `jsonPath`. Embedding hostile content in the `logoUrl` cannot break out of the `src` attribute.
38
+ - The img tag is fixed at `height: 32px; width: auto;`. If you need a different size, embed an SVG with a `viewBox` so it scales correctly, or use a `template` builder.
39
+ - When `logoUrl` is omitted, the brand row contains only the `<h1>`. There is no placeholder block.
40
+
41
+ ### Brand color cascade
42
+
43
+ The branding color is wired into two CSS variables:
44
+
45
+ ```
46
+ --accent: <brandColor>
47
+ --post: <brandColor> // (POST method pill)
48
+ ```
49
+
50
+ All other method colors are fixed:
51
+
52
+ ```
53
+ --get: #3fb950 (green)
54
+ --put: #d29922 (amber, with dark text)
55
+ --delete: #f85149 (red)
56
+ ```
57
+
58
+ This intentional split keeps GET/PUT/DELETE recognizable across themes while letting the POST pill match the brand. If you need per-method theming, use a `template` builder.
59
+
60
+ ### Font handling
61
+
62
+ `fontFamily` flows into `--font-family` and is consumed by `<body>` via `font-family: var(--font-family);`. The `<input type="search">` uses `font-family: inherit` so the search bar adopts the brand font. Code blocks (`pre`, `.endpoint`, `.try-it-out textarea`) use a fixed monospace stack (`ui-monospace, SFMono-Regular, "SF Mono", Consolas, monospace`) and are not affected by `fontFamily`.
63
+
64
+ Quoting and escaping: `fontFamily` is interpolated **as-is** into the CSS string. Provide a valid CSS font-family value, including quotes around multi-word family names when needed (e.g. `'Inter', system-ui, sans-serif`).
65
+
66
+ ## Dark / light auto-switch
67
+
68
+ The page respects the OS preference via `prefers-color-scheme`. There is no in-page theme toggle.
69
+
70
+ Dark palette (default `:root` values):
71
+
72
+ ```
73
+ --bg: #0e1116 --container: #161b22 --container-hover: #1c232b
74
+ --border: #2a313c --title: #e6edf3 --common: #c9d1d9
75
+ --muted: #8b949e
76
+ ```
77
+
78
+ Light palette (overridden inside `@media (prefers-color-scheme: light)`):
79
+
80
+ ```
81
+ --bg: #ffffff --container: #f6f8fa --container-hover: #eaeef2
82
+ --border: #d0d7de --title: #1f2328 --common: #1f2328
83
+ --muted: #59636e
84
+ ```
85
+
86
+ Variables that are **not** overridden in the light block — `--accent`, `--font-family`, `--get`, `--post`, `--put`, `--delete` — are chosen to be legible against both surfaces. The PUT pill flips its text color to dark via `.method.PUT { color: #1f2328; }` to remain legible on amber.
87
+
88
+ ## When to use `branding` vs `template`
89
+
90
+ Use `branding` (default template) when:
91
+
92
+ - You only need to swap the logo, the accent color, and/or the font.
93
+ - You're happy with the single-column endpoints-grouped-by-page layout.
94
+ - You want light/dark auto-switching for free.
95
+ - You don't need to change the inline runner, the filter UI, or the JSON-fetch flow.
96
+
97
+ Use `template: DocsTemplateBuilder` when:
98
+
99
+ - You need a different layout — sidebar navigation, tabs, a marketing-page wrapper, an embedded auth-token field, a two-pane editor/inspector view.
100
+ - You want a per-method palette beyond the POST = `brandColor` cascade.
101
+ - You need to integrate with a corporate design system (component library, web fonts loaded from a CDN, etc.).
102
+ - You want an in-page light/dark toggle independent of the OS preference.
103
+
104
+ ## Example — corporate brand palette
105
+
106
+ ```ts
107
+ import { mountDocsUi } from '@luckystack/docs-ui';
108
+
109
+ export const docsUiHandler = mountDocsUi({
110
+ pageTitle: 'Acme — API docs',
111
+ branding: {
112
+ logoUrl: 'https://cdn.acme.example/logo.svg',
113
+ brandColor: '#7c3aed',
114
+ fontFamily: `'Inter', system-ui, -apple-system, sans-serif`,
115
+ },
116
+ });
117
+ ```
118
+
119
+ This renders the default layout with the Acme logo, purple accent (also applied to the POST pill), and Inter as the body font. GET/PUT/DELETE pills remain green/amber/red.
120
+
121
+ ## Example — full template replacement
122
+
123
+ ```ts
124
+ import { mountDocsUi, type DocsTemplateBuilder } from '@luckystack/docs-ui';
125
+
126
+ const sidebarTemplate: DocsTemplateBuilder = ({ jsonPath, pageTitle, branding }) => `
127
+ <!DOCTYPE html>
128
+ <html lang="en">
129
+ <head>
130
+ <meta charset="utf-8" />
131
+ <title>${pageTitle}</title>
132
+ <link rel="stylesheet" href="https://cdn.example.com/corporate-tokens.css" />
133
+ </head>
134
+ <body>
135
+ <aside class="sidebar">${branding.logoUrl ? `<img src="${branding.logoUrl}" />` : ''}</aside>
136
+ <main id="content">Loading...</main>
137
+ <script>
138
+ fetch(${JSON.stringify(jsonPath)}, { credentials: 'include' })
139
+ .then(r => r.json())
140
+ .then(data => { /* render however you like */ });
141
+ </script>
142
+ </body>
143
+ </html>`;
144
+
145
+ export const docsUiHandler = mountDocsUi({
146
+ template: sidebarTemplate,
147
+ branding: { logoUrl: 'https://cdn.example.com/logo.svg' },
148
+ });
149
+ ```
150
+
151
+ Contract reminders for custom templates (see `./html-generation.md` for the full contract):
152
+
153
+ - Return a complete `<!DOCTYPE html>...</html>` document.
154
+ - HTML-escape any inputs that end up in markup — the builder receives raw strings.
155
+ - Fetch `jsonPath` yourself; the handler does not pre-fetch.
156
+ - You may receive a non-empty `branding` even when you don't use it — that's fine, ignore the fields you don't need.
157
+
158
+ ## Limits
159
+
160
+ - No per-endpoint color override. The method pill colors are fixed CSS variables.
161
+ - No runtime theme switcher beyond OS preference in the default template. Implement one inside a custom template if needed.
162
+ - No web-font loading. The default template does not inject `<link rel="stylesheet">` tags — bring your own via a custom template.
163
+ - No localization. Strings like "Try it out (live request)", "No matches.", "Run `npm run generateArtifacts`" are hardcoded English. Replace via a custom template if you need translations.
164
+ - `brandColor` must be a valid single CSS color value (hex, named, `rgb()`, `hsl()`). Gradients are not supported.
165
+
166
+ ## Related
167
+
168
+ - The renderer that consumes these tokens: `./html-generation.md`
169
+ - The mount handler that accepts them: `./mounting.md`
170
+ - The JSON fields the page renders: `./extension-fields.md`
171
+ - AI summary + function index: `../CLAUDE.md`
172
+ - Source: `../src/index.ts`, `../src/docsHtml.ts`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@luckystack/docs-ui",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -50,6 +50,6 @@
50
50
  "test": "vitest run"
51
51
  },
52
52
  "dependencies": {
53
- "@luckystack/core": "^0.1.3"
53
+ "@luckystack/core": "^0.1.4"
54
54
  }
55
55
  }