@knitli/docs-components 1.0.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.
@@ -0,0 +1,154 @@
1
+ ---
2
+ // SPDX-FileCopyrightText: 2025 Knitli Inc.
3
+ // SPDX-FileContributor: Adam Poulemanos <adam@knit.li>
4
+ //
5
+ // SPDX-License-Identifier: MIT OR Apache-2.0
6
+ //
7
+ // Optional breadcrumb component for product-specific navigation
8
+
9
+ interface Props {
10
+ product?: 'ReCoco' | 'CodeWeaver' | 'Thread';
11
+ productUrl?: string;
12
+ path?: string;
13
+ variant?: 'default' | 'compact';
14
+ }
15
+
16
+ const {
17
+ product,
18
+ productUrl = product ? `/${product}` : '',
19
+ path = '',
20
+ variant = 'default',
21
+ } = Astro.props;
22
+
23
+ // Parse path into segments for breadcrumb display
24
+ const segments = path
25
+ .split('/')
26
+ .filter(Boolean)
27
+ .map((segment) => ({
28
+ name: segment.replace(/-/g, ' ').replace(/\b\w/g, (l) => l.toUpperCase()),
29
+ path: segment,
30
+ }));
31
+ ---
32
+
33
+ {product && (
34
+ <nav
35
+ class:list={['docs-breadcrumb-nav', { compact: variant === 'compact' }]}
36
+ aria-label="Breadcrumb"
37
+ >
38
+ <ol class="breadcrumb-list">
39
+ <li class="breadcrumb-item">
40
+ <a href="/">Documentation</a>
41
+ </li>
42
+
43
+ <li class="breadcrumb-separator" aria-hidden="true">→</li>
44
+
45
+ <li class="breadcrumb-item">
46
+ {productUrl ? (
47
+ <a href={productUrl}>{product}</a>
48
+ ) : (
49
+ <span class="breadcrumb-current">{product}</span>
50
+ )}
51
+ </li>
52
+
53
+ {segments.map((segment, index) => (
54
+ <>
55
+ <li class="breadcrumb-separator" aria-hidden="true">→</li>
56
+ <li class="breadcrumb-item">
57
+ {index < segments.length - 1 ? (
58
+ <a href={`${productUrl}/${segments.slice(0, index + 1).map((s) => s.path).join('/')}`}>
59
+ {segment.name}
60
+ </a>
61
+ ) : (
62
+ <span class="breadcrumb-current" aria-current="page">
63
+ {segment.name}
64
+ </span>
65
+ )}
66
+ </li>
67
+ </>
68
+ ))}
69
+ </ol>
70
+ </nav>
71
+ )}
72
+
73
+ <style>
74
+ :root {
75
+ --docs-copper: oklch(0.58 0.08 50);
76
+ --docs-slate: oklch(0.35 0.02 240);
77
+ --docs-parchment: oklch(0.96 0.015 70);
78
+ }
79
+
80
+ .docs-breadcrumb-nav {
81
+ padding: 1rem 0;
82
+ font-family: "Inter", -apple-system, BlinkMacSystemFont, sans-serif;
83
+ }
84
+
85
+ .docs-breadcrumb-nav.compact {
86
+ padding: 0.5rem 0;
87
+ }
88
+
89
+ .breadcrumb-list {
90
+ display: flex;
91
+ flex-wrap: wrap;
92
+ align-items: center;
93
+ gap: 0.5rem;
94
+ list-style: none;
95
+ margin: 0;
96
+ padding: 0;
97
+ }
98
+
99
+ .breadcrumb-item {
100
+ font-size: 0.875rem;
101
+ }
102
+
103
+ .breadcrumb-item a {
104
+ color: var(--docs-slate);
105
+ text-decoration: none;
106
+ opacity: 0.7;
107
+ transition: all 0.2s ease;
108
+ }
109
+
110
+ .breadcrumb-item a:hover {
111
+ opacity: 1;
112
+ text-decoration: underline;
113
+ color: var(--docs-copper);
114
+ }
115
+
116
+ .breadcrumb-current {
117
+ color: var(--docs-slate);
118
+ font-weight: 500;
119
+ }
120
+
121
+ .breadcrumb-separator {
122
+ color: var(--docs-slate);
123
+ opacity: 0.4;
124
+ font-size: 0.875rem;
125
+ }
126
+
127
+ /* Mobile Responsive */
128
+ @media (max-width: 768px) {
129
+ .breadcrumb-list {
130
+ gap: 0.375rem;
131
+ font-size: 0.8125rem;
132
+ }
133
+
134
+ .breadcrumb-item,
135
+ .breadcrumb-separator {
136
+ font-size: 0.8125rem;
137
+ }
138
+ }
139
+
140
+ /* Accessibility */
141
+ .breadcrumb-item a:focus-visible {
142
+ outline: 3px solid var(--docs-copper);
143
+ outline-offset: 4px;
144
+ border-radius: 4px;
145
+ }
146
+
147
+ /* Reduced motion */
148
+ @media (prefers-reduced-motion: reduce) {
149
+ * {
150
+ animation-duration: 0.01ms !important;
151
+ transition-duration: 0.01ms !important;
152
+ }
153
+ }
154
+ </style>
@@ -0,0 +1,181 @@
1
+ ---
2
+ // SPDX-FileCopyrightText: 2025 Knitli Inc.
3
+ // SPDX-FileContributor: Adam Poulemanos <adam@knit.li>
4
+ //
5
+ // SPDX-License-Identifier: MIT OR Apache-2.0
6
+ //
7
+ // Simple footer for Knitli documentation sites
8
+
9
+ interface Props {
10
+ variant?: 'default' | 'minimal';
11
+ }
12
+
13
+ const { variant = 'default' } = Astro.props;
14
+
15
+ // Environment-aware URL configuration
16
+ const isDev = import.meta.env.DEV;
17
+
18
+ const getSiteUrls = () => {
19
+ if (isDev) {
20
+ return {
21
+ marketing: import.meta.env.PUBLIC_MARKETING_URL || 'http://localhost:4321',
22
+ blog: import.meta.env.PUBLIC_BLOG_URL || 'http://localhost:4322',
23
+ docs: import.meta.env.PUBLIC_DOCS_URL || 'http://localhost:4323',
24
+ };
25
+ }
26
+
27
+ return {
28
+ marketing: 'https://knitli.com',
29
+ blog: 'https://blog.knitli.com',
30
+ docs: 'https://docs.knitli.com',
31
+ };
32
+ };
33
+
34
+ const urls = getSiteUrls();
35
+ const currentYear = new Date().getFullYear();
36
+ ---
37
+
38
+ <footer class:list={['docs-footer', { minimal: variant === 'minimal' }]}>
39
+ <div class="footer-content">
40
+ {variant === 'default' && (
41
+ <nav class="footer-nav" aria-label="Footer navigation">
42
+ <a href={urls.marketing}>Main Site</a>
43
+ <a href={urls.blog}>Blog</a>
44
+ <a href="https://github.com/knitli" target="_blank" rel="noopener noreferrer">
45
+ GitHub
46
+ </a>
47
+ <a href={`${urls.marketing}/privacy`}>Privacy</a>
48
+ </nav>
49
+ )}
50
+
51
+ <div class="footer-legal">
52
+ <p class="copyright">
53
+ © {currentYear} <a href={urls.marketing}>Knitli Inc.</a> All rights reserved.
54
+ </p>
55
+ {variant === 'default' && (
56
+ <p class="license">
57
+ Documentation licensed under{' '}
58
+ <a
59
+ href="https://github.com/knitli/knitli-site/blob/main/LICENSE"
60
+ target="_blank"
61
+ rel="noopener noreferrer"
62
+ >
63
+ MIT OR Apache-2.0
64
+ </a>
65
+ </p>
66
+ )}
67
+ </div>
68
+ </div>
69
+ </footer>
70
+
71
+ <style>
72
+ :root {
73
+ --docs-copper: oklch(0.58 0.08 50);
74
+ --docs-slate: oklch(0.35 0.02 240);
75
+ --docs-parchment: oklch(0.96 0.015 70);
76
+ }
77
+
78
+ .docs-footer {
79
+ background: var(--docs-parchment);
80
+ border-top: 1px solid oklch(0.85 0.01 50);
81
+ margin-top: 4rem;
82
+ padding: 2rem 0;
83
+ }
84
+
85
+ .docs-footer.minimal {
86
+ margin-top: 2rem;
87
+ padding: 1rem 0;
88
+ }
89
+
90
+ .footer-content {
91
+ max-width: 1400px;
92
+ margin: 0 auto;
93
+ padding: 0 2rem;
94
+ display: flex;
95
+ flex-direction: column;
96
+ gap: 1.5rem;
97
+ align-items: center;
98
+ }
99
+
100
+ .footer-nav {
101
+ display: flex;
102
+ gap: 2rem;
103
+ align-items: center;
104
+ flex-wrap: wrap;
105
+ justify-content: center;
106
+ }
107
+
108
+ .footer-nav a {
109
+ font-family: "Inter", -apple-system, BlinkMacSystemFont, sans-serif;
110
+ font-size: 0.875rem;
111
+ color: var(--docs-slate);
112
+ text-decoration: none;
113
+ transition: color 0.2s ease;
114
+ }
115
+
116
+ .footer-nav a:hover {
117
+ color: var(--docs-copper);
118
+ }
119
+
120
+ .footer-legal {
121
+ display: flex;
122
+ flex-direction: column;
123
+ gap: 0.5rem;
124
+ text-align: center;
125
+ }
126
+
127
+ .copyright,
128
+ .license {
129
+ font-family: "Inter", -apple-system, BlinkMacSystemFont, sans-serif;
130
+ font-size: 0.8125rem;
131
+ color: var(--docs-slate);
132
+ opacity: 0.7;
133
+ margin: 0;
134
+ }
135
+
136
+ .copyright a,
137
+ .license a {
138
+ color: inherit;
139
+ text-decoration: none;
140
+ transition: opacity 0.2s ease;
141
+ }
142
+
143
+ .copyright a:hover,
144
+ .license a:hover {
145
+ opacity: 1;
146
+ text-decoration: underline;
147
+ }
148
+
149
+ /* Mobile Responsive */
150
+ @media (max-width: 768px) {
151
+ .footer-content {
152
+ padding: 0 1rem;
153
+ }
154
+
155
+ .footer-nav {
156
+ gap: 1rem;
157
+ flex-direction: column;
158
+ }
159
+
160
+ .footer-legal {
161
+ font-size: 0.75rem;
162
+ }
163
+ }
164
+
165
+ /* Accessibility */
166
+ .footer-nav a:focus-visible,
167
+ .copyright a:focus-visible,
168
+ .license a:focus-visible {
169
+ outline: 3px solid var(--docs-copper);
170
+ outline-offset: 4px;
171
+ border-radius: 4px;
172
+ }
173
+
174
+ /* Reduced motion */
175
+ @media (prefers-reduced-motion: reduce) {
176
+ * {
177
+ animation-duration: 0.01ms !important;
178
+ transition-duration: 0.01ms !important;
179
+ }
180
+ }
181
+ </style>
@@ -0,0 +1,328 @@
1
+ ---
2
+ // SPDX-FileCopyrightText: 2025 Knitli Inc.
3
+ // SPDX-FileContributor: Adam Poulemanos <adam@knit.li>
4
+ //
5
+ // SPDX-License-Identifier: MIT OR Apache-2.0
6
+ //
7
+ // Main branded header for Knitli documentation sites
8
+ // Provides navigation, branding, and breadcrumb support
9
+
10
+ interface Props {
11
+ currentProduct?: 'ReCoco' | 'CodeWeaver' | 'Thread';
12
+ currentPath?: string;
13
+ productUrl?: string;
14
+ showBreadcrumb?: boolean;
15
+ variant?: 'default' | 'minimal';
16
+ }
17
+
18
+ const {
19
+ currentProduct,
20
+ currentPath = '',
21
+ productUrl = '',
22
+ showBreadcrumb = true,
23
+ variant = 'default',
24
+ } = Astro.props;
25
+
26
+ // Environment-aware URL configuration
27
+ const isDev = import.meta.env.DEV;
28
+
29
+ const getSiteUrls = () => {
30
+ if (isDev) {
31
+ return {
32
+ marketing: import.meta.env.PUBLIC_MARKETING_URL || 'http://localhost:4321',
33
+ blog: import.meta.env.PUBLIC_BLOG_URL || 'http://localhost:4322',
34
+ docs: import.meta.env.PUBLIC_DOCS_URL || 'http://localhost:4323',
35
+ };
36
+ }
37
+
38
+ return {
39
+ marketing: 'https://knitli.com',
40
+ blog: 'https://blog.knitli.com',
41
+ docs: 'https://docs.knitli.com',
42
+ };
43
+ };
44
+
45
+ const urls = getSiteUrls();
46
+ const isDocsHome = currentPath === '/' || currentPath === '';
47
+ const shouldShowBreadcrumb = showBreadcrumb && !isDocsHome && currentProduct;
48
+ ---
49
+
50
+ <header class:list={['docs-header', { minimal: variant === 'minimal' }]}>
51
+ <div class="docs-header-content">
52
+ <!-- Knitli Logo + Docs Section -->
53
+ <a href={urls.marketing} class="docs-logo" aria-label="Knitli home">
54
+ <svg class="logo-icon" width="32" height="32" viewBox="0 0 100 100" fill="none" aria-hidden="true">
55
+ <!-- Simplified Knitli knot icon -->
56
+ <circle cx="50" cy="50" r="35" fill="var(--docs-copper)" opacity="0.15"/>
57
+ <path
58
+ d="M50 20 L50 50 L65 50"
59
+ stroke="var(--docs-slate)"
60
+ stroke-width="4"
61
+ fill="none"
62
+ stroke-linecap="round"
63
+ stroke-linejoin="round"
64
+ />
65
+ <path
66
+ d="M35 50 L50 50 L50 80"
67
+ stroke="var(--docs-slate)"
68
+ stroke-width="4"
69
+ fill="none"
70
+ stroke-linecap="round"
71
+ stroke-linejoin="round"
72
+ />
73
+ <circle cx="50" cy="50" r="6" fill="var(--docs-copper)"/>
74
+ </svg>
75
+ <span class="logo-text">
76
+ <span class="logo-brand">Knitli</span>
77
+ <span class="logo-separator">/</span>
78
+ <span class="logo-section">docs</span>
79
+ </span>
80
+ </a>
81
+
82
+ <!-- Breadcrumb (if not on docs home and product specified) -->
83
+ {shouldShowBreadcrumb && (
84
+ <nav class="docs-breadcrumb" aria-label="Breadcrumb">
85
+ <a href="/">Documentation</a>
86
+ <span class="breadcrumb-separator" aria-hidden="true">→</span>
87
+ <a href={productUrl || `/${currentProduct}`} class="breadcrumb-product">
88
+ {currentProduct}
89
+ </a>
90
+ </nav>
91
+ )}
92
+
93
+ <div class="spacer"></div>
94
+
95
+ <!-- Cross-site Navigation -->
96
+ <nav class="docs-nav" aria-label="Site navigation">
97
+ <a
98
+ href="/"
99
+ class:list={[{ active: isDocsHome }]}
100
+ aria-current={isDocsHome ? 'page' : undefined}
101
+ >
102
+ Docs Home
103
+ </a>
104
+ <a href={urls.marketing}>
105
+ Main Site
106
+ </a>
107
+ <a href={urls.blog}>
108
+ Blog
109
+ </a>
110
+ <a
111
+ href="https://github.com/knitli"
112
+ class="external"
113
+ target="_blank"
114
+ rel="noopener noreferrer"
115
+ aria-label="Knitli on GitHub (opens in new tab)"
116
+ >
117
+ GitHub
118
+ <span class="external-icon" aria-hidden="true">↗</span>
119
+ </a>
120
+ </nav>
121
+ </div>
122
+ </header>
123
+
124
+ <style>
125
+ :root {
126
+ --docs-copper: oklch(0.58 0.08 50); /* #b56c30 */
127
+ --docs-slate: oklch(0.35 0.02 240); /* #485563 */
128
+ --docs-parchment: oklch(0.96 0.015 70);
129
+ }
130
+
131
+ .docs-header {
132
+ background: var(--docs-parchment);
133
+ border-bottom: 1px solid oklch(0.85 0.01 50);
134
+ position: sticky;
135
+ top: 0;
136
+ z-index: 100;
137
+
138
+ /* Copper accent line */
139
+ box-shadow:
140
+ inset 0 -2px 0 var(--docs-copper),
141
+ 0 1px 3px rgba(0, 0, 0, 0.05);
142
+ }
143
+
144
+ .docs-header.minimal {
145
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
146
+ border-bottom: 1px solid oklch(0.90 0.01 60);
147
+ }
148
+
149
+ .docs-header-content {
150
+ max-width: 1400px;
151
+ margin: 0 auto;
152
+ padding: 1rem 2rem;
153
+ display: flex;
154
+ align-items: center;
155
+ gap: 2rem;
156
+ }
157
+
158
+ .docs-logo {
159
+ display: flex;
160
+ align-items: center;
161
+ gap: 0.75rem;
162
+ text-decoration: none;
163
+ transition: opacity 0.2s ease;
164
+ }
165
+
166
+ .docs-logo:hover {
167
+ opacity: 0.8;
168
+ }
169
+
170
+ .logo-icon {
171
+ flex-shrink: 0;
172
+ }
173
+
174
+ .logo-text {
175
+ font-family: "DM Mono", "Roboto Mono", "Courier New", monospace;
176
+ font-size: 1.125rem;
177
+ font-weight: 600;
178
+ line-height: 1;
179
+ }
180
+
181
+ .logo-brand {
182
+ color: var(--docs-slate);
183
+ }
184
+
185
+ .logo-separator {
186
+ color: var(--docs-copper);
187
+ opacity: 0.5;
188
+ margin: 0 0.25rem;
189
+ }
190
+
191
+ .logo-section {
192
+ color: var(--docs-copper);
193
+ }
194
+
195
+ .docs-breadcrumb {
196
+ display: flex;
197
+ align-items: center;
198
+ gap: 0.5rem;
199
+ font-family: "Inter", -apple-system, BlinkMacSystemFont, sans-serif;
200
+ font-size: 0.875rem;
201
+ color: var(--docs-slate);
202
+ opacity: 0.7;
203
+ }
204
+
205
+ .docs-breadcrumb a {
206
+ color: inherit;
207
+ text-decoration: none;
208
+ transition: opacity 0.2s ease;
209
+ }
210
+
211
+ .docs-breadcrumb a:hover {
212
+ opacity: 1;
213
+ text-decoration: underline;
214
+ }
215
+
216
+ .breadcrumb-separator {
217
+ opacity: 0.5;
218
+ }
219
+
220
+ .breadcrumb-product {
221
+ font-weight: 500;
222
+ opacity: 1;
223
+ }
224
+
225
+ .spacer {
226
+ flex: 1;
227
+ }
228
+
229
+ .docs-nav {
230
+ display: flex;
231
+ gap: 2rem;
232
+ align-items: center;
233
+ }
234
+
235
+ .docs-nav a {
236
+ font-family: "Inter", -apple-system, BlinkMacSystemFont, sans-serif;
237
+ font-size: 0.9375rem;
238
+ color: var(--docs-slate);
239
+ text-decoration: none;
240
+ position: relative;
241
+ padding: 0.25rem 0;
242
+ transition: color 0.2s ease;
243
+ }
244
+
245
+ .docs-nav a::after {
246
+ content: '';
247
+ position: absolute;
248
+ bottom: 0;
249
+ left: 0;
250
+ width: 0;
251
+ height: 1px;
252
+ background: var(--docs-copper);
253
+ transition: width 0.2s ease;
254
+ }
255
+
256
+ .docs-nav a:hover::after,
257
+ .docs-nav a.active::after {
258
+ width: 100%;
259
+ }
260
+
261
+ .docs-nav a.active {
262
+ color: var(--docs-copper);
263
+ font-weight: 500;
264
+ }
265
+
266
+ .docs-nav a.external {
267
+ display: inline-flex;
268
+ align-items: center;
269
+ gap: 0.25rem;
270
+ }
271
+
272
+ .external-icon {
273
+ font-size: 0.75em;
274
+ opacity: 0.6;
275
+ }
276
+
277
+ /* Mobile Responsive */
278
+ @media (max-width: 768px) {
279
+ .docs-header-content {
280
+ flex-wrap: wrap;
281
+ padding: 1rem;
282
+ gap: 1rem;
283
+ }
284
+
285
+ .docs-breadcrumb {
286
+ order: 3;
287
+ width: 100%;
288
+ padding-left: 0;
289
+ }
290
+
291
+ .spacer {
292
+ display: none;
293
+ }
294
+
295
+ .docs-nav {
296
+ gap: 1rem;
297
+ font-size: 0.875rem;
298
+ }
299
+
300
+ .logo-text {
301
+ font-size: 1rem;
302
+ }
303
+ }
304
+
305
+ /* Tablet */
306
+ @media (min-width: 769px) and (max-width: 1024px) {
307
+ .docs-nav {
308
+ gap: 1.5rem;
309
+ }
310
+ }
311
+
312
+ /* Accessibility */
313
+ .docs-logo:focus-visible,
314
+ .docs-nav a:focus-visible,
315
+ .docs-breadcrumb a:focus-visible {
316
+ outline: 3px solid var(--docs-copper);
317
+ outline-offset: 4px;
318
+ border-radius: 4px;
319
+ }
320
+
321
+ /* Reduced motion */
322
+ @media (prefers-reduced-motion: reduce) {
323
+ * {
324
+ animation-duration: 0.01ms !important;
325
+ transition-duration: 0.01ms !important;
326
+ }
327
+ }
328
+ </style>
package/src/index.ts ADDED
@@ -0,0 +1,15 @@
1
+ // SPDX-FileCopyrightText: 2025 Knitli Inc.
2
+ // SPDX-FileContributor: Adam Poulemanos <adam@knit.li>
3
+ //
4
+ // SPDX-License-Identifier: MIT OR Apache-2.0
5
+ //
6
+ // Main exports for @knitli/docs-components package
7
+
8
+ // Export type definitions
9
+ export * from './types/index.js';
10
+
11
+ // Components are exported via package.json exports field
12
+ // Import them directly:
13
+ // import DocsHeader from '@knitli/docs-components/DocsHeader.astro';
14
+ // import DocsFooter from '@knitli/docs-components/DocsFooter.astro';
15
+ // import DocsBreadcrumb from '@knitli/docs-components/DocsBreadcrumb.astro';