@farming-labs/astro-theme 0.0.2-beta.15 → 0.0.2-beta.17

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farming-labs/astro-theme",
3
- "version": "0.0.2-beta.15",
3
+ "version": "0.0.2-beta.17",
4
4
  "description": "Astro UI components for @farming-labs/docs — layout, sidebar, TOC, search, and theme toggle",
5
5
  "type": "module",
6
6
  "exports": {
@@ -25,12 +25,19 @@
25
25
  "import": "./src/themes/darksharp.js",
26
26
  "default": "./src/themes/darksharp.js"
27
27
  },
28
+ "./colorful": {
29
+ "types": "./src/themes/colorful.d.ts",
30
+ "import": "./src/themes/colorful.js",
31
+ "default": "./src/themes/colorful.js"
32
+ },
28
33
  "./css": "./styles/docs.css",
29
34
  "./fumadocs/css": "./styles/docs.css",
30
35
  "./styles/pixel-border.css": "./styles/pixel-border.css",
31
36
  "./styles/darksharp.css": "./styles/darksharp.css",
32
37
  "./pixel-border/css": "./styles/pixel-border-bundle.css",
33
- "./darksharp/css": "./styles/darksharp-bundle.css"
38
+ "./darksharp/css": "./styles/darksharp-bundle.css",
39
+ "./styles/colorful.css": "./styles/colorful.css",
40
+ "./colorful/css": "./styles/colorful-bundle.css"
34
41
  },
35
42
  "typesVersions": {
36
43
  "*": {
@@ -62,8 +69,8 @@
62
69
  "license": "MIT",
63
70
  "dependencies": {
64
71
  "sugar-high": "^0.9.5",
65
- "@farming-labs/astro": "0.0.2-beta.15",
66
- "@farming-labs/docs": "0.0.2-beta.15"
72
+ "@farming-labs/docs": "0.0.2-beta.17",
73
+ "@farming-labs/astro": "0.0.2-beta.17"
67
74
  },
68
75
  "peerDependencies": {
69
76
  "astro": ">=4.0.0"
@@ -8,6 +8,7 @@ const titleSuffix = config?.metadata?.titleTemplate
8
8
  : " – Docs";
9
9
 
10
10
  const tocEnabled = config?.theme?.ui?.layout?.toc?.enabled ?? true;
11
+ const tocStyle = config?.theme?.ui?.layout?.toc?.style ?? "default";
11
12
 
12
13
  const breadcrumbEnabled = (() => {
13
14
  const bc = config?.breadcrumb;
@@ -29,11 +30,13 @@ const showLastModified = !!data.lastModified;
29
30
  <DocsPage
30
31
  entry={config?.entry ?? "docs"}
31
32
  tocEnabled={tocEnabled}
33
+ tocStyle={tocStyle}
32
34
  breadcrumbEnabled={breadcrumbEnabled}
33
35
  previousPage={data.previousPage}
34
36
  nextPage={data.nextPage}
35
37
  editOnGithub={showEditOnGithub ? data.editOnGithub : null}
36
38
  lastModified={showLastModified ? data.lastModified : null}
37
39
  >
40
+ {data.description && <p class="fd-page-description">{data.description}</p>}
38
41
  <Fragment set:html={data.html} />
39
42
  </DocsPage>
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  import ThemeToggle from "./ThemeToggle.astro";
3
+ import FloatingAIChat from "./FloatingAIChat.astro";
3
4
 
4
5
  const { tree, config = null, title, titleUrl } = Astro.props;
5
6
 
@@ -60,7 +61,7 @@ function buildColorsCSS(colors?: Record<string, string | undefined>): string {
60
61
  vars.push(`${COLOR_MAP[key]}: ${value};`);
61
62
  }
62
63
  if (vars.length === 0) return "";
63
- return `:root, .dark {\n ${vars.join("\n ")}\n}`;
64
+ return `.dark {\n ${vars.join("\n ")}\n}`;
64
65
  }
65
66
 
66
67
  function buildFontStyleVars(prefix: string, style?: { size?: string; weight?: string | number; lineHeight?: string; letterSpacing?: string }): string {
@@ -270,6 +271,20 @@ const showFloatingAI = aiConfig?.mode === "floating" && aiConfig?.enabled;
270
271
  </main>
271
272
  </div>
272
273
 
274
+ {showFloatingAI && (
275
+ <FloatingAIChat
276
+ api="/api/docs"
277
+ suggestedQuestions={aiConfig?.suggestedQuestions ?? []}
278
+ aiLabel={aiConfig?.aiLabel ?? "AI"}
279
+ position={aiConfig?.position ?? "bottom-right"}
280
+ floatingStyle={aiConfig?.floatingStyle ?? "panel"}
281
+ >
282
+ {Astro.slots.has("ai-trigger") && (
283
+ <slot name="ai-trigger" slot="trigger" />
284
+ )}
285
+ </FloatingAIChat>
286
+ )}
287
+
273
288
  <script>
274
289
  // Sidebar toggle
275
290
  const menuBtn = document.getElementById('fd-menu-toggle');
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  const {
3
3
  tocEnabled = true,
4
+ tocStyle = "default",
4
5
  breadcrumbEnabled = true,
5
6
  entry = "docs",
6
7
  previousPage = null,
@@ -10,16 +11,12 @@ const {
10
11
  } = Astro.props;
11
12
 
12
13
  const pathname = Astro.url.pathname;
13
- const segments = pathname.split("/").filter(Boolean).filter(s => s.toLowerCase() !== entry.toLowerCase());
14
+ const segments = pathname.split("/").filter(Boolean);
14
15
  const parentLabel = segments.length >= 2 ? segments[segments.length - 2].replace(/-/g, " ").replace(/\b\w/g, c => c.toUpperCase()) : "";
15
16
  const currentLabel = segments.length >= 2 ? segments[segments.length - 1].replace(/-/g, " ").replace(/\b\w/g, c => c.toUpperCase()) : "";
16
- const parentUrl = (() => {
17
- if (segments.length < 2) return "";
18
- const all = pathname.split("/").filter(Boolean);
19
- const parentSegment = segments[segments.length - 2];
20
- const parentIndex = all.indexOf(parentSegment);
21
- return "/" + all.slice(0, parentIndex + 1).join("/");
22
- })();
17
+ const parentUrl = segments.length >= 2
18
+ ? "/" + segments.slice(0, segments.length - 1).join("/")
19
+ : "";
23
20
  ---
24
21
 
25
22
  <div class="fd-page">
@@ -89,7 +86,7 @@ const parentUrl = (() => {
89
86
 
90
87
  {tocEnabled && (
91
88
  <aside class="fd-toc">
92
- <div class="fd-toc-inner">
89
+ <div class={`fd-toc-inner ${tocStyle === "directional" ? "fd-toc-directional" : ""}`} data-toc-style={tocStyle}>
93
90
  <h3 class="fd-toc-title">
94
91
  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
95
92
  <line x1="3" y1="6" x2="21" y2="6" />
@@ -98,42 +95,205 @@ const parentUrl = (() => {
98
95
  </svg>
99
96
  On this page
100
97
  </h3>
101
- <ul class="fd-toc-list" id="fd-toc-list"></ul>
98
+ <ul class={`fd-toc-list ${tocStyle === "directional" ? "fd-toc-clerk" : ""}`} id="fd-toc-list" style={tocStyle === "directional" ? "position:relative" : ""}></ul>
102
99
  </div>
103
100
  </aside>
104
101
  )}
105
102
  </div>
106
103
 
107
104
  <script>
105
+ function getItemOffset(depth) {
106
+ if (depth <= 2) return 14;
107
+ if (depth === 3) return 26;
108
+ return 36;
109
+ }
110
+
111
+ function getLineOffset(depth) {
112
+ return depth >= 3 ? 10 : 0;
113
+ }
114
+
108
115
  function initDocsPage() {
109
- // Scan headings for TOC
110
116
  const container = document.querySelector('.fd-page-body');
111
117
  const tocList = document.getElementById('fd-toc-list');
112
118
  if (container && tocList) {
113
119
  const headings = container.querySelectorAll('h2[id], h3[id], h4[id]');
114
120
  tocList.innerHTML = '';
121
+ const isClerk = tocList.closest('[data-toc-style]')?.getAttribute('data-toc-style') === 'directional';
122
+
123
+ const tocItems = [];
115
124
  headings.forEach(el => {
116
- const depth = parseInt(el.tagName[1], 10);
125
+ tocItems.push({
126
+ id: el.id,
127
+ title: el.textContent?.replace(/^#\s*/, '') || '',
128
+ depth: parseInt(el.tagName[1], 10),
129
+ });
130
+ });
131
+
132
+ tocItems.forEach((item, index) => {
117
133
  const li = document.createElement('li');
118
134
  li.className = 'fd-toc-item';
119
135
  const a = document.createElement('a');
120
- a.className = 'fd-toc-link';
121
- a.href = `#${el.id}`;
122
- a.textContent = el.textContent?.replace(/^#\s*/, '') || '';
123
- a.style.paddingLeft = `${12 + (depth - 2) * 12}px`;
136
+ a.href = `#${item.id}`;
137
+
138
+ if (isClerk) {
139
+ a.className = 'fd-toc-link fd-toc-clerk-link';
140
+ a.style.position = 'relative';
141
+ a.style.paddingLeft = `${getItemOffset(item.depth)}px`;
142
+ a.style.paddingTop = '6px';
143
+ a.style.paddingBottom = '6px';
144
+ a.style.fontSize = item.depth <= 2 ? '14px' : '13px';
145
+ a.style.overflowWrap = 'anywhere';
146
+
147
+ const prevDepth = index > 0 ? tocItems[index - 1].depth : item.depth;
148
+ const nextDepth = index < tocItems.length - 1 ? tocItems[index + 1].depth : item.depth;
149
+ const lineDiv = document.createElement('div');
150
+ lineDiv.style.position = 'absolute';
151
+ lineDiv.style.left = `${getLineOffset(item.depth)}px`;
152
+ lineDiv.style.top = prevDepth !== item.depth ? '6px' : '0';
153
+ lineDiv.style.bottom = nextDepth !== item.depth ? '6px' : '0';
154
+ lineDiv.style.width = '1px';
155
+ lineDiv.style.background = 'hsla(0, 0%, 50%, 0.1)';
156
+ a.appendChild(lineDiv);
157
+
158
+ if (index > 0 && tocItems[index - 1].depth !== item.depth) {
159
+ const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
160
+ svg.setAttribute('viewBox', '0 0 16 16');
161
+ svg.setAttribute('width', '16');
162
+ svg.setAttribute('height', '16');
163
+ svg.style.position = 'absolute';
164
+ svg.style.top = '-6px';
165
+ svg.style.left = '0';
166
+ const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
167
+ line.setAttribute('x1', String(getLineOffset(tocItems[index - 1].depth)));
168
+ line.setAttribute('y1', '0');
169
+ line.setAttribute('x2', String(getLineOffset(item.depth)));
170
+ line.setAttribute('y2', '12');
171
+ line.setAttribute('stroke', 'hsla(0, 0%, 50%, 0.1)');
172
+ line.setAttribute('stroke-width', '1');
173
+ svg.appendChild(line);
174
+ a.appendChild(svg);
175
+ }
176
+
177
+ a.appendChild(document.createTextNode(item.title));
178
+ } else {
179
+ a.className = 'fd-toc-link';
180
+ a.style.paddingLeft = `${12 + (item.depth - 2) * 12}px`;
181
+ a.textContent = item.title;
182
+ }
183
+
124
184
  li.appendChild(a);
125
185
  tocList.appendChild(li);
126
186
  });
127
187
 
128
- // Intersection observer for active heading
188
+ let maskEl = null;
189
+ let thumbEl = null;
190
+
191
+ if (isClerk) {
192
+ function buildSvgPath() {
193
+ const links = tocList.querySelectorAll('.fd-toc-clerk-link');
194
+ if (links.length === 0) return null;
195
+ const d = [];
196
+ let w = 0, h = 0;
197
+ links.forEach((el, i) => {
198
+ if (i >= tocItems.length) return;
199
+ const depth = tocItems[i].depth;
200
+ const x = getLineOffset(depth) + 1;
201
+ const styles = getComputedStyle(el);
202
+ const top = el.offsetTop + parseFloat(styles.paddingTop);
203
+ const bottom = el.offsetTop + el.clientHeight - parseFloat(styles.paddingBottom);
204
+ w = Math.max(x, w);
205
+ h = Math.max(h, bottom);
206
+ d.push(`${i === 0 ? "M" : "L"}${x} ${top}`);
207
+ d.push(`L${x} ${bottom}`);
208
+ });
209
+ return { path: d.join(" "), width: w + 1, height: h };
210
+ }
211
+
212
+ function updateThumb(activeSet) {
213
+ if (!thumbEl) return;
214
+ const links = tocList.querySelectorAll('.fd-toc-clerk-link');
215
+ if (activeSet.size === 0 || links.length === 0) {
216
+ thumbEl.style.marginTop = '0px';
217
+ thumbEl.style.height = '0px';
218
+ return;
219
+ }
220
+ let upper = Infinity, lower = 0;
221
+ for (const id of activeSet) {
222
+ const el = tocList.querySelector(`a[href="#${id}"]`);
223
+ if (!el) continue;
224
+ const styles = getComputedStyle(el);
225
+ upper = Math.min(upper, el.offsetTop + parseFloat(styles.paddingTop));
226
+ lower = Math.max(lower, el.offsetTop + el.clientHeight - parseFloat(styles.paddingBottom));
227
+ }
228
+ if (upper === Infinity) {
229
+ thumbEl.style.marginTop = '0px';
230
+ thumbEl.style.height = '0px';
231
+ return;
232
+ }
233
+ thumbEl.style.marginTop = `${upper}px`;
234
+ thumbEl.style.height = `${lower - upper}px`;
235
+ }
236
+
237
+ requestAnimationFrame(() => {
238
+ const svgData = buildSvgPath();
239
+ if (svgData) {
240
+ maskEl = document.createElement('div');
241
+ maskEl.className = 'fd-toc-clerk-mask';
242
+ maskEl.style.position = 'absolute';
243
+ maskEl.style.left = '0';
244
+ maskEl.style.top = '0';
245
+ maskEl.style.width = svgData.width + 'px';
246
+ maskEl.style.height = svgData.height + 'px';
247
+ maskEl.style.pointerEvents = 'none';
248
+ const maskSvg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${svgData.width} ${svgData.height}"><path d="${svgData.path}" stroke="black" stroke-width="1" fill="none"/></svg>`;
249
+ const maskUrl = `url("data:image/svg+xml,${encodeURIComponent(maskSvg)}")`;
250
+ maskEl.style.maskImage = maskUrl;
251
+ maskEl.style.webkitMaskImage = maskUrl;
252
+ maskEl.style.maskRepeat = 'no-repeat';
253
+ maskEl.style.webkitMaskRepeat = 'no-repeat';
254
+
255
+ thumbEl = document.createElement('div');
256
+ thumbEl.className = 'fd-toc-clerk-thumb';
257
+ thumbEl.style.background = 'var(--color-fd-primary)';
258
+ thumbEl.style.transition = 'all 0.15s';
259
+ thumbEl.style.willChange = 'height, margin-top';
260
+ thumbEl.style.marginTop = '0px';
261
+ thumbEl.style.height = '0px';
262
+ maskEl.appendChild(thumbEl);
263
+ tocList.appendChild(maskEl);
264
+ }
265
+ });
266
+
267
+ var clerkActiveSet = new Set();
268
+ }
269
+
270
+ const activeSet = new Set();
271
+
129
272
  const observer = new IntersectionObserver((entries) => {
130
273
  for (const entry of entries) {
131
274
  if (entry.isIntersecting) {
132
- tocList.querySelectorAll('.fd-toc-link').forEach(link => {
133
- link.classList.toggle('fd-toc-link-active', link.getAttribute('href') === `#${entry.target.id}`);
134
- });
275
+ activeSet.add(entry.target.id);
276
+ } else {
277
+ activeSet.delete(entry.target.id);
135
278
  }
136
279
  }
280
+
281
+ if (isClerk) {
282
+ tocList.querySelectorAll('.fd-toc-clerk-link').forEach(link => {
283
+ const id = link.getAttribute('href')?.slice(1);
284
+ if (activeSet.has(id)) {
285
+ link.setAttribute('data-active', 'true');
286
+ } else {
287
+ link.removeAttribute('data-active');
288
+ }
289
+ });
290
+ if (typeof updateThumb === 'function') updateThumb(activeSet);
291
+ } else {
292
+ tocList.querySelectorAll('.fd-toc-link').forEach(link => {
293
+ const id = link.getAttribute('href')?.slice(1);
294
+ link.classList.toggle('fd-toc-link-active', activeSet.has(id));
295
+ });
296
+ }
137
297
  }, { rootMargin: '-80px 0px -80% 0px' });
138
298
 
139
299
  headings.forEach(el => observer.observe(el));
@@ -7,6 +7,8 @@ const {
7
7
  floatingStyle = "panel",
8
8
  } = Astro.props;
9
9
 
10
+ const hasCustomTrigger = Astro.slots.has("trigger");
11
+
10
12
  const isFullModal = floatingStyle === "full-modal";
11
13
 
12
14
  const BTN_POSITIONS: Record<string, string> = {
@@ -55,13 +57,19 @@ const containerStyle = getContainerStyle(floatingStyle, position);
55
57
 
56
58
  <!-- Bottom input bar -->
57
59
  <div class="fd-ai-fm-input-bar fd-ai-fm-input-bar--closed" id="fd-float-bar" style={btnStyle}>
58
- <button id="fd-float-trigger" class="fd-ai-fm-trigger-btn" aria-label={`Ask ${aiLabel}`}>
59
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
60
- <path d="M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z"/>
61
- <path d="M20 3v4"/><path d="M22 5h-4"/>
62
- </svg>
63
- <span>Ask {aiLabel}</span>
64
- </button>
60
+ {hasCustomTrigger ? (
61
+ <div id="fd-float-trigger" class="fd-ai-floating-trigger">
62
+ <slot name="trigger" />
63
+ </div>
64
+ ) : (
65
+ <button id="fd-float-trigger" class="fd-ai-fm-trigger-btn" aria-label={`Ask ${aiLabel}`}>
66
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
67
+ <path d="M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z"/>
68
+ <path d="M20 3v4"/><path d="M22 5h-4"/>
69
+ </svg>
70
+ <span>Ask {aiLabel}</span>
71
+ </button>
72
+ )}
65
73
 
66
74
  <div id="fd-float-input-container" class="fd-ai-fm-input-container" style="display:none">
67
75
  <div class="fd-ai-fm-input-wrap">
@@ -161,12 +169,19 @@ const containerStyle = getContainerStyle(floatingStyle, position);
161
169
  </div>
162
170
 
163
171
  <!-- Floating trigger button -->
164
- <button id="fd-float-btn" class="fd-ai-floating-btn" style={btnStyle} aria-label={`Ask ${aiLabel}`}>
165
- <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
166
- <path d="M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z"/>
167
- <path d="M20 3v4"/><path d="M22 5h-4"/>
168
- </svg>
169
- </button>
172
+ {hasCustomTrigger ? (
173
+ <div id="fd-float-btn" class="fd-ai-floating-trigger" style={btnStyle}>
174
+ <slot name="trigger" />
175
+ </div>
176
+ ) : (
177
+ <button id="fd-float-btn" class="fd-ai-floating-btn" style={btnStyle} aria-label={`Ask ${aiLabel}`}>
178
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
179
+ <path d="M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z"/>
180
+ <path d="M20 3v4"/><path d="M22 5h-4"/>
181
+ </svg>
182
+ <span>Ask {aiLabel}</span>
183
+ </button>
184
+ )}
170
185
  </Fragment>
171
186
  )}
172
187
 
@@ -0,0 +1,2 @@
1
+ export declare const colorful: (overrides?: { ui?: Record<string, unknown> }) => import("@farming-labs/docs").DocsTheme;
2
+ export declare const ColorfulUIDefaults: Record<string, unknown>;
@@ -0,0 +1,42 @@
1
+ import { createTheme } from "@farming-labs/docs";
2
+
3
+ const ColorfulUIDefaults = {
4
+ colors: {
5
+ primary: "hsl(40, 96%, 40%)",
6
+ background: "#ffffff",
7
+ muted: "#64748b",
8
+ border: "#e5e7eb",
9
+ },
10
+ typography: {
11
+ font: {
12
+ style: {
13
+ sans: "Inter, system-ui, sans-serif",
14
+ mono: "JetBrains Mono, monospace",
15
+ },
16
+ h1: { size: "1.875rem", weight: 700, lineHeight: "1.2", letterSpacing: "-0.02em" },
17
+ h2: { size: "1.5rem", weight: 600, lineHeight: "1.3" },
18
+ h3: { size: "1.25rem", weight: 600, lineHeight: "1.4" },
19
+ h4: { size: "1.125rem", weight: 600, lineHeight: "1.4" },
20
+ body: { size: "1rem", weight: 400, lineHeight: "1.75" },
21
+ small: { size: "0.875rem", weight: 400, lineHeight: "1.5" },
22
+ },
23
+ },
24
+ layout: {
25
+ contentWidth: 768,
26
+ sidebarWidth: 260,
27
+ toc: { enabled: true, depth: 3, style: "directional" },
28
+ header: { height: 56, sticky: true },
29
+ },
30
+ components: {
31
+ Callout: { variant: "soft", icon: true },
32
+ CodeBlock: { showCopyButton: true },
33
+ Tabs: { style: "default" },
34
+ },
35
+ };
36
+
37
+ export const colorful = createTheme({
38
+ name: "fumadocs-colorful",
39
+ ui: ColorfulUIDefaults,
40
+ });
41
+
42
+ export { ColorfulUIDefaults };
@@ -0,0 +1,2 @@
1
+ @import "./docs.css";
2
+ @import "./colorful.css";
@@ -0,0 +1,148 @@
1
+ /* @farming-labs/astro-theme — colorful theme overrides
2
+ * Fumadocs-inspired theme with warm yellow/amber accent colors.
3
+ * Import AFTER the base theme CSS (docs.css).
4
+ */
5
+
6
+ /* ─── Colorful yellow accent overrides ────────────────────────────── */
7
+
8
+ :root {
9
+ --color-fd-primary: hsl(40, 96%, 40%);
10
+ --color-fd-primary-foreground: hsl(0, 0%, 100%);
11
+ --color-fd-ring: hsl(40, 80%, 50%);
12
+ }
13
+
14
+ .dark {
15
+ --color-fd-primary: hsl(45, 100%, 60%);
16
+ --color-fd-primary-foreground: hsl(0, 0%, 5%);
17
+ --color-fd-ring: hsl(45, 90%, 55%);
18
+ }
19
+
20
+ /* ─── Description under title ──────────────────────────────────────── */
21
+
22
+ .fd-page-description {
23
+ margin-bottom: 1rem;
24
+ font-size: 1.125rem;
25
+ line-height: 1.75;
26
+ color: var(--color-fd-muted-foreground);
27
+ }
28
+
29
+ /* ─── Sidebar dark overrides (fumadocs neutral) ────────────────────── */
30
+
31
+ .dark .fd-sidebar {
32
+ --color-fd-muted: hsl(0, 0%, 16%);
33
+ --color-fd-secondary: hsl(0, 0%, 18%);
34
+ --color-fd-muted-foreground: hsl(0, 0%, 72%);
35
+ }
36
+
37
+ /* ─── Cards (fumadocs style) ────────────────────────────────────────── */
38
+
39
+ .fd-card {
40
+ display: block;
41
+ border-radius: 0.75rem;
42
+ border: 1px solid var(--color-fd-border);
43
+ background: var(--color-fd-card);
44
+ padding: 1rem;
45
+ font-size: 0.875rem;
46
+ color: var(--color-fd-card-foreground);
47
+ box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1);
48
+ transition: background-color 150ms, border-color 150ms;
49
+ }
50
+
51
+ .fd-card:hover {
52
+ background: var(--color-fd-accent);
53
+ }
54
+
55
+ .fd-card-icon {
56
+ margin-bottom: 0.5rem;
57
+ width: fit-content;
58
+ border-radius: 0.375rem;
59
+ border: 1px solid var(--color-fd-border);
60
+ padding: 0.375rem;
61
+ color: var(--color-fd-muted-foreground);
62
+ }
63
+
64
+ .fd-card-title {
65
+ font-weight: 500;
66
+ }
67
+
68
+ .fd-card-description {
69
+ color: var(--color-fd-muted-foreground);
70
+ margin-top: 0.25rem;
71
+ }
72
+
73
+ .fd-cards {
74
+ display: grid;
75
+ grid-template-columns: 1fr;
76
+ gap: 1rem;
77
+ }
78
+
79
+ @media (min-width: 640px) {
80
+ .fd-cards {
81
+ grid-template-columns: repeat(2, 1fr);
82
+ }
83
+ }
84
+
85
+ /* ─── Page nav cards ───────────────────────────────────────────────── */
86
+
87
+ .fd-page-nav-card {
88
+ border-radius: 0.75rem;
89
+ }
90
+
91
+ /* ─── Inline code ──────────────────────────────────────────────────── */
92
+
93
+ .fd-docs-content :not(pre) > code {
94
+ padding: 3px;
95
+ border: 1px solid var(--color-fd-border);
96
+ font-size: 13px;
97
+ border-radius: 5px;
98
+ background: var(--color-fd-muted);
99
+ }
100
+
101
+ /* ─── Links in prose ───────────────────────────────────────────────── */
102
+
103
+ .fd-docs-content a:not(.fd-page-nav-card):not([class]) {
104
+ text-decoration: underline;
105
+ text-underline-offset: 3.5px;
106
+ text-decoration-color: var(--color-fd-primary);
107
+ text-decoration-thickness: 1.5px;
108
+ font-weight: 500;
109
+ }
110
+
111
+ /* ─── Tables (rounded) ─────────────────────────────────────────────── */
112
+
113
+ .fd-docs-content table {
114
+ border-collapse: separate;
115
+ border-spacing: 0;
116
+ background: var(--color-fd-card);
117
+ border-radius: 0.75rem;
118
+ border: 1px solid var(--color-fd-border);
119
+ overflow: hidden;
120
+ }
121
+
122
+ .fd-docs-content th {
123
+ background: var(--color-fd-muted);
124
+ font-weight: 600;
125
+ }
126
+
127
+ .fd-docs-content th,
128
+ .fd-docs-content td {
129
+ padding: 0.625rem;
130
+ border-bottom: 1px solid var(--color-fd-border);
131
+ }
132
+
133
+ .fd-docs-content tr:last-child td {
134
+ border-bottom: none;
135
+ }
136
+
137
+ /* ─── Blockquotes ──────────────────────────────────────────────────── */
138
+
139
+ .fd-docs-content blockquote {
140
+ border-left: 2px solid var(--color-fd-primary);
141
+ padding-left: 1rem;
142
+ color: var(--color-fd-foreground);
143
+ font-style: normal;
144
+ }
145
+
146
+ .fd-docs-content hr {
147
+ border-color: var(--color-fd-border);
148
+ }
package/styles/docs.css CHANGED
@@ -477,6 +477,37 @@ code, kbd, pre, samp {
477
477
  border-left-color: var(--color-fd-primary);
478
478
  }
479
479
 
480
+ /* ─── Clerk TOC (tree-line style) ────────────────────────────────────── */
481
+
482
+ .fd-toc-clerk {
483
+ border-left: none !important;
484
+ }
485
+
486
+ .fd-toc-clerk .fd-toc-link {
487
+ display: block;
488
+ border-left: none;
489
+ margin-left: 0;
490
+ color: var(--color-fd-muted-foreground);
491
+ text-decoration: none;
492
+ transition: color 0.15s;
493
+ }
494
+
495
+ .fd-toc-clerk .fd-toc-link:hover {
496
+ color: var(--color-fd-foreground);
497
+ }
498
+
499
+ .fd-toc-clerk .fd-toc-link[data-active="true"] {
500
+ color: var(--color-fd-primary);
501
+ }
502
+
503
+ .fd-toc-clerk-mask {
504
+ overflow: hidden;
505
+ }
506
+
507
+ .fd-toc-clerk-thumb {
508
+ width: 100%;
509
+ }
510
+
480
511
  @media (max-width: 1279px) {
481
512
  .fd-toc {
482
513
  display: none;
@@ -497,6 +528,15 @@ code, kbd, pre, samp {
497
528
  }
498
529
  }
499
530
 
531
+ /* ─── Page description (frontmatter) ─────────────────────────────────── */
532
+
533
+ .fd-page-description {
534
+ margin-bottom: 1rem;
535
+ font-size: 1.125rem;
536
+ line-height: 1.75;
537
+ color: var(--color-fd-muted-foreground);
538
+ }
539
+
500
540
  /* ─── Breadcrumb ─────────────────────────────────────────────────────── */
501
541
 
502
542
  .fd-breadcrumb {
@@ -1749,11 +1789,6 @@ html.dark pre.shiki {
1749
1789
 
1750
1790
  .fd-ai-floating-btn {
1751
1791
  border-radius: 26px;
1752
- box-shadow: 0 8px 32px rgba(99, 102, 241, 0.3);
1753
- }
1754
-
1755
- .fd-ai-floating-btn:hover {
1756
- box-shadow: 0 10px 40px rgba(99, 102, 241, 0.4);
1757
1792
  }
1758
1793
 
1759
1794
  .fd-ai-suggestion {
@@ -1802,24 +1837,32 @@ html.dark pre.shiki {
1802
1837
  .fd-ai-floating-btn {
1803
1838
  position: fixed;
1804
1839
  z-index: 9997;
1805
- width: 52px;
1806
- height: 52px;
1807
- border-radius: var(--radius, 26px);
1808
- border: 1px solid var(--color-fd-border, rgba(255, 255, 255, 0.1));
1809
- background: var(--color-fd-primary, #6366f1);
1810
- color: var(--color-fd-primary-foreground, #fff);
1811
- cursor: pointer;
1812
1840
  display: flex;
1813
1841
  align-items: center;
1814
1842
  justify-content: center;
1815
- box-shadow: 0 8px 32px color-mix(in srgb, var(--color-fd-primary, #6366f1) 30%, transparent);
1816
- transition: all 200ms;
1843
+ gap: 8px;
1844
+ padding: 8px 12px;
1845
+ height: 40px;
1846
+ border-radius: 16px;
1847
+ border: 1px solid var(--color-fd-border, rgba(255, 255, 255, 0.1));
1848
+ background: color-mix(in srgb, var(--color-fd-secondary, #f4f4f5) 80%, transparent);
1849
+ backdrop-filter: blur(4px);
1850
+ color: var(--color-fd-muted-foreground, #71717a);
1851
+ cursor: pointer;
1852
+ font-size: 14px;
1853
+ box-shadow: 0 1px 3px color-mix(in srgb, var(--color-fd-background, #000) 20%, transparent);
1854
+ transition: transform 150ms, background 150ms, color 150ms;
1817
1855
  animation: fd-ai-fade-in 300ms ease-out;
1818
1856
  }
1819
1857
 
1820
1858
  .fd-ai-floating-btn:hover {
1821
- transform: scale(1.05);
1822
- box-shadow: 0 10px 40px color-mix(in srgb, var(--color-fd-primary, #6366f1) 40%, transparent);
1859
+ background: var(--color-fd-accent);
1860
+ color: var(--color-fd-accent-foreground);
1861
+ transform: scale(1.03);
1862
+ }
1863
+
1864
+ .fd-ai-floating-btn:active {
1865
+ transform: scale(0.97);
1823
1866
  }
1824
1867
 
1825
1868
  .fd-ai-floating-trigger {
@@ -2153,24 +2196,28 @@ html.dark pre.shiki {
2153
2196
  align-items: center;
2154
2197
  justify-content: center;
2155
2198
  gap: 8px;
2156
- padding: 8px 16px;
2199
+ padding: 8px 12px;
2157
2200
  height: 40px;
2158
- border-radius: var(--radius, 16px);
2201
+ border-radius: 16px;
2159
2202
  border: 1px solid var(--color-fd-border, rgba(255, 255, 255, 0.1));
2160
- background: var(--color-fd-secondary, rgba(255, 255, 255, 0.06));
2203
+ background: color-mix(in srgb, var(--color-fd-secondary, #f4f4f5) 80%, transparent);
2204
+ backdrop-filter: blur(4px);
2161
2205
  color: var(--color-fd-muted-foreground, #71717a);
2162
2206
  font-family: inherit;
2163
2207
  font-size: 14px;
2164
2208
  cursor: pointer;
2165
- backdrop-filter: blur(8px);
2166
- box-shadow: 0 8px 32px color-mix(in srgb, var(--color-fd-background, #000) 40%, transparent);
2167
- transition: all 200ms;
2209
+ box-shadow: 0 1px 3px color-mix(in srgb, var(--color-fd-background, #000) 20%, transparent);
2210
+ transition: transform 150ms, background 150ms, color 150ms;
2168
2211
  animation: fd-ai-fade-in 300ms ease-out;
2169
2212
  white-space: nowrap;
2170
2213
  }
2171
2214
 
2172
2215
  .fd-ai-fm-trigger-btn:hover {
2173
- background: var(--color-fd-accent, rgba(255, 255, 255, 0.1));
2174
- color: var(--color-fd-accent-foreground, #fff);
2216
+ background: var(--color-fd-accent);
2217
+ color: var(--color-fd-accent-foreground);
2175
2218
  transform: scale(1.03);
2176
2219
  }
2220
+
2221
+ .fd-ai-fm-trigger-btn:active {
2222
+ transform: scale(0.97);
2223
+ }