@specglass/theme-default 0.0.9 → 0.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/__tests__/design-tokens.test.d.ts +1 -0
  2. package/dist/__tests__/design-tokens.test.js +107 -0
  3. package/dist/islands/CopyButton.js +2 -6
  4. package/dist/islands/LanguageToggle.d.ts +16 -0
  5. package/dist/islands/LanguageToggle.js +88 -0
  6. package/dist/islands/SearchPalette.js +57 -8
  7. package/dist/islands/ThemeToggle.js +1 -1
  8. package/dist/scripts/code-block-enhancer.js +6 -3
  9. package/dist/themes/notdiamond-dark.json +168 -0
  10. package/dist/themes/notdiamond-light.json +168 -0
  11. package/dist/ui/command.js +2 -2
  12. package/dist/ui/dialog.js +2 -2
  13. package/dist/utils/shiki.d.ts +1 -1
  14. package/dist/utils/shiki.js +5 -3
  15. package/package.json +5 -3
  16. package/src/components/ApiAuth.astro +31 -4
  17. package/src/components/ApiEndpoint.astro +67 -44
  18. package/src/components/ApiNavigation.astro +8 -11
  19. package/src/components/ApiParameters.astro +113 -162
  20. package/src/components/ApiResponse.astro +1 -1
  21. package/src/components/Callout.astro +59 -18
  22. package/src/components/Card.astro +4 -4
  23. package/src/components/CodeBlock.astro +7 -7
  24. package/src/components/CodeBlockGroup.astro +3 -3
  25. package/src/components/CodeExample.astro +183 -0
  26. package/src/components/EditLink.astro +53 -0
  27. package/src/components/Footer.astro +87 -25
  28. package/src/components/Header.astro +63 -7
  29. package/src/components/Sidebar.astro +43 -11
  30. package/src/components/TableOfContents.astro +5 -5
  31. package/src/components/Tabs.astro +51 -20
  32. package/src/islands/CopyButton.tsx +36 -34
  33. package/src/islands/LanguageToggle.tsx +214 -0
  34. package/src/islands/SearchPalette.tsx +121 -39
  35. package/src/islands/ThemeToggle.tsx +45 -48
  36. package/src/layouts/ApiReferencePage.astro +67 -56
  37. package/src/layouts/DocPage.astro +32 -27
  38. package/src/layouts/LandingPage.astro +348 -27
  39. package/src/scripts/code-block-enhancer.ts +8 -3
  40. package/src/styles/global.css +388 -59
  41. package/src/themes/notdiamond-dark.json +168 -0
  42. package/src/themes/notdiamond-light.json +168 -0
  43. package/src/ui/command.tsx +1 -2
  44. package/src/ui/dialog.tsx +8 -5
  45. package/src/utils/shiki.ts +5 -3
@@ -52,56 +52,53 @@ export function ThemeToggle() {
52
52
  type="button"
53
53
  onClick={toggleTheme}
54
54
  aria-label={isDark ? "Switch to light mode" : "Switch to dark mode"}
55
- className="inline-flex items-center justify-center rounded-md p-2 text-text-muted hover:text-(--color-text) hover:bg-hover-bg focus:outline-none focus-visible:ring-2 focus-visible:ring-primary transition-colors"
55
+ className="relative inline-flex items-center justify-center w-8 h-8 rounded-lg text-text-muted hover:text-text hover:bg-surface-2 focus:outline-none focus-visible:ring-2 focus-visible:ring-primary transition-colors"
56
56
  >
57
- {/* Icon containerboth icons rendered, crossfade via CSS transitions */}
58
- <span className="relative inline-flex items-center justify-center w-5 h-5">
59
- {/* Sun icon — visible in dark mode, click to switch to light */}
60
- <svg
61
- xmlns="http://www.w3.org/2000/svg"
62
- width="20"
63
- height="20"
64
- viewBox="0 0 24 24"
65
- fill="none"
66
- stroke="currentColor"
67
- strokeWidth="2"
68
- strokeLinecap="round"
69
- strokeLinejoin="round"
70
- aria-hidden="true"
71
- className={`absolute inset-0 transition-transform duration-300 ${
72
- isDark ? "rotate-0 scale-100" : "-rotate-90 scale-0"
73
- }`}
74
- >
75
- <circle cx="12" cy="12" r="4" />
76
- <path d="M12 2v2" />
77
- <path d="M12 20v2" />
78
- <path d="m4.93 4.93 1.41 1.41" />
79
- <path d="m17.66 17.66 1.41 1.41" />
80
- <path d="M2 12h2" />
81
- <path d="M20 12h2" />
82
- <path d="m6.34 17.66-1.41 1.41" />
83
- <path d="m19.07 4.93-1.41 1.41" />
84
- </svg>
57
+ {/* Sun iconvisible in dark mode, click to switch to light */}
58
+ <svg
59
+ xmlns="http://www.w3.org/2000/svg"
60
+ width="20"
61
+ height="20"
62
+ viewBox="0 0 24 24"
63
+ fill="none"
64
+ stroke="currentColor"
65
+ strokeWidth="2"
66
+ strokeLinecap="round"
67
+ strokeLinejoin="round"
68
+ aria-hidden="true"
69
+ className={`absolute inset-0 m-auto transition-all duration-300 ${
70
+ isDark ? "rotate-0 scale-100" : "-rotate-90 scale-0"
71
+ }`}
72
+ >
73
+ <circle cx="12" cy="12" r="4" />
74
+ <path d="M12 2v2" />
75
+ <path d="M12 20v2" />
76
+ <path d="m4.93 4.93 1.41 1.41" />
77
+ <path d="m17.66 17.66 1.41 1.41" />
78
+ <path d="M2 12h2" />
79
+ <path d="M20 12h2" />
80
+ <path d="m6.34 17.66-1.41 1.41" />
81
+ <path d="m19.07 4.93-1.41 1.41" />
82
+ </svg>
85
83
 
86
- {/* Moon icon — visible in light mode, click to switch to dark */}
87
- <svg
88
- xmlns="http://www.w3.org/2000/svg"
89
- width="20"
90
- height="20"
91
- viewBox="0 0 24 24"
92
- fill="none"
93
- stroke="currentColor"
94
- strokeWidth="2"
95
- strokeLinecap="round"
96
- strokeLinejoin="round"
97
- aria-hidden="true"
98
- className={`absolute inset-0 transition-transform duration-300 ${
99
- !isDark ? "rotate-0 scale-100" : "rotate-90 scale-0"
100
- }`}
101
- >
102
- <path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z" />
103
- </svg>
104
- </span>
84
+ {/* Moon icon — visible in light mode, click to switch to dark */}
85
+ <svg
86
+ xmlns="http://www.w3.org/2000/svg"
87
+ width="20"
88
+ height="20"
89
+ viewBox="0 0 24 24"
90
+ fill="none"
91
+ stroke="currentColor"
92
+ strokeWidth="2"
93
+ strokeLinecap="round"
94
+ strokeLinejoin="round"
95
+ aria-hidden="true"
96
+ className={`absolute inset-0 m-auto transition-all duration-300 ${
97
+ !isDark ? "rotate-0 scale-100" : "rotate-90 scale-0"
98
+ }`}
99
+ >
100
+ <path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z" />
101
+ </svg>
105
102
  </button>
106
103
  );
107
104
  }
@@ -17,6 +17,8 @@ import ApiExampleResponse from "../components/ApiExampleResponse.astro";
17
17
  import ApiEndpointFallback from "../components/ApiEndpointFallback.astro";
18
18
  import { ThemeToggle } from "../islands/ThemeToggle";
19
19
  import { SearchPalette } from "../islands/SearchPalette";
20
+ import { LanguageToggle } from "../islands/LanguageToggle";
21
+
20
22
  import "../styles/global.css";
21
23
  import type {
22
24
  NavigationTree,
@@ -85,30 +87,21 @@ const currentEndpointId = `${displayMethod}-${displayPath}`;
85
87
  <link rel="preconnect" href="https://fonts.googleapis.com" />
86
88
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
87
89
  <link
88
- href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap"
90
+ href="https://fonts.googleapis.com/css2?family=Geist:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap"
89
91
  rel="stylesheet"
90
92
  />
91
- <!-- FOUC prevention: apply stored theme before first paint -->
93
+
92
94
  <script is:inline>
93
95
  (function () {
94
- function applyStoredTheme() {
95
- var stored = null;
96
- try {
97
- stored = localStorage.getItem("specglass-theme");
98
- } catch (e) {
99
- /* noop */
100
- }
101
- var theme = stored === "dark" || stored === "light" ? stored : "dark";
102
- if (theme === "dark") {
103
- document.documentElement.classList.add("dark");
104
- } else {
105
- document.documentElement.classList.remove("dark");
106
- }
107
- }
108
- applyStoredTheme();
109
- if (!window.__sgThemeInit) {
110
- window.__sgThemeInit = true;
111
- document.addEventListener("astro:after-swap", applyStoredTheme);
96
+ var stored = null;
97
+ try {
98
+ stored = localStorage.getItem("specglass-theme");
99
+ } catch (e) {}
100
+ var theme = stored === "dark" || stored === "light" ? stored : "dark";
101
+ if (theme === "dark") {
102
+ document.documentElement.classList.add("dark");
103
+ } else {
104
+ document.documentElement.classList.remove("dark");
112
105
  }
113
106
  })();
114
107
  </script>
@@ -117,12 +110,13 @@ const currentEndpointId = `${displayMethod}-${displayPath}`;
117
110
  <!-- Skip to content link for accessibility -->
118
111
  <a
119
112
  href="#main-content"
120
- class="sr-only focus:not-sr-only focus:absolute focus:top-2 focus:left-2 focus:z-50 focus:bg-primary focus:text-white focus:px-4 focus:py-2 focus:rounded"
113
+ class="sr-only focus:not-sr-only focus:absolute focus:top-2 focus:left-2 focus:z-50 focus:bg-text focus:text-surface focus:px-4 focus:py-2 focus:rounded"
121
114
  >
122
115
  Skip to content
123
116
  </a>
124
117
 
125
118
  <Header>
119
+ <LanguageToggle client:idle />
126
120
  <SearchPalette client:idle />
127
121
  <ThemeToggle client:load />
128
122
  </Header>
@@ -132,12 +126,12 @@ const currentEndpointId = `${displayMethod}-${displayPath}`;
132
126
  <nav
133
127
  id="sidebar"
134
128
  aria-label="API endpoint navigation"
135
- class="hidden md:block fixed top-(--height-header) left-0 bottom-0 w-(--width-sidebar) overflow-y-auto border-r border-border bg-sidebar-bg px-4 py-6"
129
+ class="hidden md:block fixed top-(--height-header) left-0 bottom-0 w-(--width-sidebar) overflow-y-auto border-r border-border/40 bg-surface-1 px-4 py-6"
136
130
  data-pagefind-ignore
137
131
  >
138
132
  <!-- Spec title header -->
139
133
  <div class="mb-4 px-3">
140
- <h2 class="text-sm font-semibold text-text truncate">{specInfo.title}</h2>
134
+ <h2 class="text-[0.8125rem] font-semibold text-text truncate">{specInfo.title}</h2>
141
135
  <span class="text-xs text-text-muted">v{specInfo.version}</span>
142
136
  </div>
143
137
 
@@ -150,17 +144,21 @@ const currentEndpointId = `${displayMethod}-${displayPath}`;
150
144
  </nav>
151
145
 
152
146
  <!-- Mobile sidebar overlay -->
153
- <div id="sidebar-overlay" class="fixed inset-0 bg-black/50 z-30 hidden" aria-hidden="true">
147
+ <div
148
+ id="sidebar-overlay"
149
+ class="fixed inset-0 bg-black/50 backdrop-blur-sm z-30 hidden"
150
+ aria-hidden="true"
151
+ >
154
152
  </div>
155
153
 
156
154
  <!-- Mobile sidebar drawer -->
157
155
  <nav
158
156
  id="sidebar-mobile"
159
157
  aria-label="API endpoint navigation"
160
- class="fixed top-(--height-header) left-0 bottom-0 w-(--width-sidebar) overflow-y-auto border-r border-border bg-sidebar-bg px-4 py-6 z-40 -translate-x-full transition-transform duration-(--transition-normal) md:hidden"
158
+ class="fixed top-(--height-header) left-0 bottom-0 w-(--width-sidebar) overflow-y-auto border-r border-border/40 bg-surface-1 px-4 py-6 z-40 -translate-x-full transition-transform duration-(--transition-normal) md:hidden"
161
159
  >
162
160
  <div class="mb-4 px-3">
163
- <h2 class="text-sm font-semibold text-text truncate">{specInfo.title}</h2>
161
+ <h2 class="text-[0.8125rem] font-semibold text-text truncate">{specInfo.title}</h2>
164
162
  <span class="text-xs text-text-muted">v{specInfo.version}</span>
165
163
  </div>
166
164
  <ApiNavigation
@@ -173,47 +171,60 @@ const currentEndpointId = `${displayMethod}-${displayPath}`;
173
171
 
174
172
  <!-- Main content area -->
175
173
  <main id="main-content" class="flex-1 min-w-0 md:ml-(--width-sidebar)" data-pagefind-body>
176
- <article class="max-w-(--width-content-max) mx-auto px-(--spacing-page) py-8">
174
+ <article class="px-(--spacing-page) py-8">
177
175
  {
178
176
  isErrorPage ? (
179
177
  /* Fallback rendering for errored endpoints (FR40) */
180
- <ApiEndpointFallback error={endpointError!} />
178
+ <div class="max-w-(--width-content-max) mx-auto">
179
+ <ApiEndpointFallback error={endpointError!} />
180
+ </div>
181
181
  ) : (
182
182
  <>
183
- {/* Endpoint header */}
184
- <ApiEndpoint endpoint={endpoint!} />
183
+ {/* Endpoint header — full width */}
184
+ <div class="max-w-360 mx-auto mb-8">
185
+ <ApiEndpoint endpoint={endpoint!} />
186
+ </div>
185
187
 
186
- {/* Authentication */}
187
- {endpoint!.security && endpoint!.security.length > 0 && (
188
- <ApiAuth security={endpoint!.security} securitySchemes={securitySchemes} />
189
- )}
188
+ {/* Two-column layout: explanation (left) + code (right) */}
189
+ <div class="max-w-360 mx-auto grid grid-cols-1 lg:grid-cols-2 gap-8 lg:gap-12 items-start">
190
+ {/* Left column: textual explanation */}
191
+ <div class="min-w-0 space-y-6">
192
+ {/* Authentication */}
193
+ {endpoint!.security && endpoint!.security.length > 0 && (
194
+ <ApiAuth security={endpoint!.security} securitySchemes={securitySchemes} />
195
+ )}
190
196
 
191
- {/* Parameters & Request Body */}
192
- {(endpoint!.parameters.length > 0 || endpoint!.requestBody) && (
193
- <div>
194
- <h2 class="text-lg font-semibold text-text mb-4 mt-0">Parameters</h2>
195
- <ApiParameters
196
- parameters={endpoint!.parameters}
197
- requestBody={endpoint!.requestBody}
198
- />
199
- </div>
200
- )}
197
+ {/* Parameters & Request Body */}
198
+ {(endpoint!.parameters.length > 0 || endpoint!.requestBody) && (
199
+ <div>
200
+ <h2 class="text-lg font-semibold text-text mb-4 mt-0">Parameters</h2>
201
+ <ApiParameters
202
+ parameters={endpoint!.parameters}
203
+ requestBody={endpoint!.requestBody}
204
+ />
205
+ </div>
206
+ )}
201
207
 
202
- {/* Responses */}
203
- {endpoint!.responses.length > 0 && (
204
- <div>
205
- <h2 class="text-lg font-semibold text-text mb-4">Responses</h2>
206
- <ApiResponse responses={endpoint!.responses} />
208
+ {/* Responses */}
209
+ {endpoint!.responses.length > 0 && (
210
+ <div>
211
+ <h2 class="text-lg font-semibold text-text mb-4">Responses</h2>
212
+ <ApiResponse responses={endpoint!.responses} />
213
+ </div>
214
+ )}
207
215
  </div>
208
- )}
209
216
 
210
- {/* Request Example (code tabs: cURL, Python, Node.js) */}
211
- <ApiExampleRequest endpoint={endpoint!} />
217
+ {/* Right column: code examples (sticky on desktop) */}
218
+ <div class="min-w-0 lg:sticky lg:top-[calc(var(--height-header)+2rem)] lg:max-h-[calc(100vh-var(--height-header)-4rem)] lg:overflow-y-auto space-y-6">
219
+ {/* Request Example (code tabs: cURL, Python, Node.js) */}
220
+ <ApiExampleRequest endpoint={endpoint!} />
212
221
 
213
- {/* Response Examples (per status code) */}
214
- {endpoint!.responses.length > 0 && (
215
- <ApiExampleResponse responses={endpoint!.responses} />
216
- )}
222
+ {/* Response Examples (per status code) */}
223
+ {endpoint!.responses.length > 0 && (
224
+ <ApiExampleResponse responses={endpoint!.responses} />
225
+ )}
226
+ </div>
227
+ </div>
217
228
  </>
218
229
  )
219
230
  }
@@ -3,8 +3,11 @@ import Header from "../components/Header.astro";
3
3
  import Footer from "../components/Footer.astro";
4
4
  import Sidebar from "../components/Sidebar.astro";
5
5
  import TableOfContents from "../components/TableOfContents.astro";
6
+ import EditLink from "../components/EditLink.astro";
6
7
  import { ThemeToggle } from "../islands/ThemeToggle";
7
8
  import { SearchPalette } from "../islands/SearchPalette";
9
+ import { LanguageToggle } from "../islands/LanguageToggle";
10
+
8
11
  import "../styles/global.css";
9
12
  import type { NavigationTree } from "@specglass/core";
10
13
  import { config } from "virtual:specglass/config";
@@ -42,29 +45,21 @@ const { title, description, frontmatter, navigation, headings, currentSlug } = A
42
45
  <link rel="preconnect" href="https://fonts.googleapis.com" />
43
46
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
44
47
  <link
45
- href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap"
48
+ href="https://fonts.googleapis.com/css2?family=Geist:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap"
46
49
  rel="stylesheet"
47
50
  />
48
- <!-- FOUC prevention: apply stored theme before first paint -->
51
+
49
52
  <script is:inline>
50
53
  (function () {
51
- function applyStoredTheme() {
52
- var stored = null;
53
- try {
54
- stored = localStorage.getItem("specglass-theme");
55
- } catch (e) {}
56
- var theme = stored === "dark" || stored === "light" ? stored : "dark";
57
- if (theme === "dark") {
58
- document.documentElement.classList.add("dark");
59
- } else {
60
- document.documentElement.classList.remove("dark");
61
- }
62
- }
63
- applyStoredTheme();
64
- // Re-apply after Astro ViewTransitions page swap (guard prevents duplicate listeners)
65
- if (!window.__sgThemeInit) {
66
- window.__sgThemeInit = true;
67
- document.addEventListener("astro:after-swap", applyStoredTheme);
54
+ var stored = null;
55
+ try {
56
+ stored = localStorage.getItem("specglass-theme");
57
+ } catch (e) {}
58
+ var theme = stored === "dark" || stored === "light" ? stored : "dark";
59
+ if (theme === "dark") {
60
+ document.documentElement.classList.add("dark");
61
+ } else {
62
+ document.documentElement.classList.remove("dark");
68
63
  }
69
64
  })();
70
65
  </script>
@@ -73,36 +68,41 @@ const { title, description, frontmatter, navigation, headings, currentSlug } = A
73
68
  <!-- Skip to content link for accessibility -->
74
69
  <a
75
70
  href="#main-content"
76
- class="sr-only focus:not-sr-only focus:absolute focus:top-2 focus:left-2 focus:z-50 focus:bg-primary focus:text-white focus:px-4 focus:py-2 focus:rounded"
71
+ class="sr-only focus:not-sr-only focus:absolute focus:top-2 focus:left-2 focus:z-50 focus:bg-text focus:text-surface focus:px-4 focus:py-2 focus:rounded"
77
72
  >
78
73
  Skip to content
79
74
  </a>
80
75
 
81
76
  <Header>
77
+ <LanguageToggle client:idle />
82
78
  <SearchPalette client:idle />
83
79
  <ThemeToggle client:load />
84
80
  </Header>
85
81
 
86
- <div class="flex pt-(--height-header)">
82
+ <div class="flex flex-col min-h-[calc(100vh-var(--height-header))] pt-(--height-header)">
87
83
  <!-- Sidebar navigation -->
88
84
  <nav
89
85
  id="sidebar"
90
86
  aria-label="Documentation navigation"
91
- class="hidden md:block fixed top-(--height-header) left-0 bottom-0 w-(--width-sidebar) overflow-y-auto border-r border-border bg-sidebar-bg px-4 py-6"
87
+ class="hidden md:block fixed top-(--height-header) left-0 bottom-0 w-(--width-sidebar) overflow-y-auto border-r border-border/20 bg-surface-0 px-4 py-6"
92
88
  data-pagefind-ignore
93
89
  >
94
90
  <Sidebar items={navigation.items} currentSlug={currentSlug} />
95
91
  </nav>
96
92
 
97
93
  <!-- Mobile sidebar overlay -->
98
- <div id="sidebar-overlay" class="fixed inset-0 bg-black/50 z-30 hidden" aria-hidden="true">
94
+ <div
95
+ id="sidebar-overlay"
96
+ class="fixed inset-0 bg-black/50 backdrop-blur-sm z-30 hidden"
97
+ aria-hidden="true"
98
+ >
99
99
  </div>
100
100
 
101
101
  <!-- Mobile sidebar drawer -->
102
102
  <nav
103
103
  id="sidebar-mobile"
104
104
  aria-label="Documentation navigation"
105
- class="fixed top-(--height-header) left-0 bottom-0 w-(--width-sidebar) overflow-y-auto border-r border-border bg-sidebar-bg px-4 py-6 z-40 -translate-x-full transition-transform duration-(--transition-normal) md:hidden"
105
+ class="fixed top-(--height-header) left-0 bottom-0 w-(--width-sidebar) overflow-y-auto border-r border-border/20 bg-surface-0 px-4 py-6 z-40 -translate-x-full transition-transform duration-(--transition-normal) md:hidden"
106
106
  >
107
107
  <Sidebar items={navigation.items} currentSlug={currentSlug} />
108
108
  </nav>
@@ -113,16 +113,21 @@ const { title, description, frontmatter, navigation, headings, currentSlug } = A
113
113
  class="flex-1 min-w-0 md:ml-(--width-sidebar) lg:mr-(--width-toc)"
114
114
  data-pagefind-body
115
115
  >
116
- <article class="sg-content max-w-(--width-content-max) mx-auto px-(--spacing-page) py-8">
117
- <h1 class="text-3xl font-bold tracking-tight mb-2">
116
+ <article
117
+ class="sg-content max-w-(--width-content-max) mx-auto px-(--spacing-page) py-8"
118
+ >
119
+ <h1>
118
120
  {frontmatter.title}
119
121
  </h1>
120
122
  {
121
123
  frontmatter.description && (
122
- <p class="text-text-muted text-lg mb-8 mt-0">{frontmatter.description}</p>
124
+ <p class="text-text-muted text-base mt-0 mb-8 leading-relaxed">
125
+ {frontmatter.description}
126
+ </p>
123
127
  )
124
128
  }
125
129
  <slot />
130
+ <EditLink currentSlug={currentSlug} />
126
131
  </article>
127
132
  </main>
128
133