@knitli/docs-components 1.3.4 → 1.3.5
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/dist/AllContributors-7n8pafkm.astro +38 -0
- package/dist/Contributors-he0tpj6t.astro +30 -0
- package/dist/DocsBreadcrumb-120137a7.astro +152 -0
- package/dist/Footer-eayrt0r3.astro +269 -0
- package/dist/PageFrame-y7qynxws.astro +187 -0
- package/dist/ProductCard-gebg6mpp.astro +289 -0
- package/dist/ProductGrid-j9rxskvp.astro +54 -0
- package/dist/ProductShowcase-xjwdmd15.astro +22 -0
- package/dist/SeoMeta-h32x587e.astro +53 -0
- package/dist/index.js +197 -18
- package/package.json +31 -23
- package/dist/assets/favicon/index.js +0 -8
- package/dist/assets/logos/index.js +0 -25
- package/dist/assets/styles/index.js +0 -12
- package/dist/data/products.js +0 -23
- package/dist/styles/index.js +0 -7
- package/dist/types/index.js +0 -1
- package/scripts/copy-assets.mjs +0 -42
- package/scripts/copy-dist-assets.mjs +0 -38
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
// SPDX-FileCopyrightText: 2026 Knitli Inc.
|
|
3
|
+
// SPDX-FileContributor: Adam Poulemanos <adam@knit.li>
|
|
4
|
+
//
|
|
5
|
+
// SPDX-License-Identifier: MIT OR Apache-2.0
|
|
6
|
+
//
|
|
7
|
+
// Knitli-branded wrapper around astro-contributors AllContributors grid.
|
|
8
|
+
// Reads from .all-contributorsrc and pre-applies brand colors.
|
|
9
|
+
//
|
|
10
|
+
// Usage:
|
|
11
|
+
// <AllContributors />
|
|
12
|
+
|
|
13
|
+
import { AllContributors as BaseAllContributors } from "astro-contributors";
|
|
14
|
+
|
|
15
|
+
export interface Props {
|
|
16
|
+
/** Focus/accent color for hover outlines */
|
|
17
|
+
focusColor?: string;
|
|
18
|
+
/** Card background color */
|
|
19
|
+
bgColor?: string;
|
|
20
|
+
/** Card border color */
|
|
21
|
+
borderColor?: string;
|
|
22
|
+
/** Primary text color */
|
|
23
|
+
textColor?: string;
|
|
24
|
+
/** Contribution badge background color */
|
|
25
|
+
badgeBgColor?: string;
|
|
26
|
+
/** Contribution badge text color */
|
|
27
|
+
badgeTextColor?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const {
|
|
31
|
+
focusColor = "var(--sl-color-accent, #ea5932)",
|
|
32
|
+
bgColor = "var(--sl-color-bg-nav, #f8f9fa)",
|
|
33
|
+
borderColor = "var(--sl-color-hairline, #e5e7eb)",
|
|
34
|
+
...rest
|
|
35
|
+
} = Astro.props;
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
<BaseAllContributors focusColor={focusColor} bgColor={bgColor} borderColor={borderColor} {...rest} />
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
// SPDX-FileCopyrightText: 2026 Knitli Inc.
|
|
3
|
+
// SPDX-FileContributor: Adam Poulemanos <adam@knit.li>
|
|
4
|
+
//
|
|
5
|
+
// SPDX-License-Identifier: MIT OR Apache-2.0
|
|
6
|
+
//
|
|
7
|
+
// Knitli-branded wrapper around astro-contributors ContributorList.
|
|
8
|
+
// Pre-applies brand colors while allowing per-instance overrides.
|
|
9
|
+
//
|
|
10
|
+
// Usage:
|
|
11
|
+
// <Contributors githubRepo="knitli/recoco" />
|
|
12
|
+
|
|
13
|
+
import { ContributorList } from "astro-contributors";
|
|
14
|
+
|
|
15
|
+
export interface Props {
|
|
16
|
+
/** GitHub repo in "owner/repo" format */
|
|
17
|
+
githubRepo?: `${string}/${string}`;
|
|
18
|
+
/** Usernames to exclude from the list */
|
|
19
|
+
ignore?: string[];
|
|
20
|
+
/** Focus/accent color for hover states. Defaults to Knitli rust. */
|
|
21
|
+
focusColor?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const {
|
|
25
|
+
focusColor = "var(--sl-color-accent, #ea5932)",
|
|
26
|
+
...rest
|
|
27
|
+
} = Astro.props;
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
<ContributorList focusColor={focusColor} {...rest} />
|
|
@@ -0,0 +1,152 @@
|
|
|
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
|
+
interface Props {
|
|
8
|
+
/** Product name — validated against the product catalog */
|
|
9
|
+
product?: string;
|
|
10
|
+
productUrl?: string;
|
|
11
|
+
path?: string;
|
|
12
|
+
/** 'inline' — no padding, designed for embedding in a subnav bar */
|
|
13
|
+
variant?: "default" | "compact" | "inline";
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const {
|
|
17
|
+
product,
|
|
18
|
+
productUrl = "/",
|
|
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, index, all) => ({
|
|
28
|
+
name: segment.replace(/-/g, " ").replace(/\b\w/g, (l) => l.toUpperCase()),
|
|
29
|
+
href: `${productUrl}${all.slice(0, index + 1).join("/")}/`,
|
|
30
|
+
isLast: index === all.length - 1,
|
|
31
|
+
}));
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
{product && (
|
|
35
|
+
<nav
|
|
36
|
+
class:list={['docs-breadcrumb-nav', variant]}
|
|
37
|
+
aria-label="Breadcrumb"
|
|
38
|
+
>
|
|
39
|
+
<ol class="breadcrumb-list">
|
|
40
|
+
<li class="breadcrumb-item">
|
|
41
|
+
<a href="https://docs.knitli.com">Documentation</a>
|
|
42
|
+
</li>
|
|
43
|
+
|
|
44
|
+
<li class="breadcrumb-separator" aria-hidden="true">›</li>
|
|
45
|
+
|
|
46
|
+
<li class="breadcrumb-item">
|
|
47
|
+
{segments.length > 0 ? (
|
|
48
|
+
<a href={productUrl}>{product}</a>
|
|
49
|
+
) : (
|
|
50
|
+
<span class="breadcrumb-current" aria-current="page">{product}</span>
|
|
51
|
+
)}
|
|
52
|
+
</li>
|
|
53
|
+
|
|
54
|
+
{segments.map((segment) => (
|
|
55
|
+
<>
|
|
56
|
+
<li class="breadcrumb-separator" aria-hidden="true">›</li>
|
|
57
|
+
<li class="breadcrumb-item">
|
|
58
|
+
{!segment.isLast ? (
|
|
59
|
+
<a href={segment.href}>{segment.name}</a>
|
|
60
|
+
) : (
|
|
61
|
+
<span class="breadcrumb-current" aria-current="page">
|
|
62
|
+
{segment.name}
|
|
63
|
+
</span>
|
|
64
|
+
)}
|
|
65
|
+
</li>
|
|
66
|
+
</>
|
|
67
|
+
))}
|
|
68
|
+
</ol>
|
|
69
|
+
</nav>
|
|
70
|
+
)}
|
|
71
|
+
|
|
72
|
+
<style>
|
|
73
|
+
.docs-breadcrumb-nav {
|
|
74
|
+
font-size: var(--sl-text-xs);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.docs-breadcrumb-nav.default {
|
|
78
|
+
padding: 1rem 0;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.docs-breadcrumb-nav.compact {
|
|
82
|
+
padding: 0.5rem 0;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/* inline: no padding, fits inside a subnav bar */
|
|
86
|
+
.docs-breadcrumb-nav.inline {
|
|
87
|
+
padding: 0;
|
|
88
|
+
display: flex;
|
|
89
|
+
align-items: center;
|
|
90
|
+
height: 100%;
|
|
91
|
+
min-width: 0;
|
|
92
|
+
overflow: hidden;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.breadcrumb-list {
|
|
96
|
+
display: flex;
|
|
97
|
+
flex-wrap: nowrap;
|
|
98
|
+
align-items: center;
|
|
99
|
+
gap: 0.35rem;
|
|
100
|
+
list-style: none;
|
|
101
|
+
margin: 0;
|
|
102
|
+
padding: 0;
|
|
103
|
+
min-width: 0;
|
|
104
|
+
overflow: hidden;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.breadcrumb-item {
|
|
108
|
+
font-size: inherit;
|
|
109
|
+
white-space: nowrap;
|
|
110
|
+
overflow: hidden;
|
|
111
|
+
text-overflow: ellipsis;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/* Only ellipsis the last item — earlier ones can wrap/hide naturally */
|
|
115
|
+
.breadcrumb-item:last-child {
|
|
116
|
+
min-width: 0;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.breadcrumb-item a {
|
|
120
|
+
color: var(--sl-color-gray-3);
|
|
121
|
+
text-decoration: none;
|
|
122
|
+
transition: color 0.15s ease;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.breadcrumb-item a:hover {
|
|
126
|
+
color: var(--sl-color-gray-1);
|
|
127
|
+
text-decoration: none;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.breadcrumb-current {
|
|
131
|
+
color: var(--sl-color-gray-2);
|
|
132
|
+
font-weight: 500;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.breadcrumb-separator {
|
|
136
|
+
color: var(--sl-color-gray-5);
|
|
137
|
+
font-size: inherit;
|
|
138
|
+
flex-shrink: 0;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.breadcrumb-item a:focus-visible {
|
|
142
|
+
outline: 2px solid var(--sl-color-accent);
|
|
143
|
+
outline-offset: 2px;
|
|
144
|
+
border-radius: 2px;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
@media (prefers-reduced-motion: reduce) {
|
|
148
|
+
.breadcrumb-item a {
|
|
149
|
+
transition-duration: 0.01ms !important;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
</style>
|
|
@@ -0,0 +1,269 @@
|
|
|
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
|
+
// Starlight Footer override for Knitli product documentation sites.
|
|
8
|
+
// Renders Starlight's native page-level footer (edit link, last updated,
|
|
9
|
+
// pagination, optional credits) followed by the Knitli site footer.
|
|
10
|
+
//
|
|
11
|
+
// Usage in astro.config.mjs:
|
|
12
|
+
// starlight({ components: { Footer: '@knitli/docs-components/Footer.astro' } })
|
|
13
|
+
|
|
14
|
+
// @ts-expect-error: virtual modules are typed in virtual-internal.d.ts (not in Starlight's public exports map)
|
|
15
|
+
import EditLink from "virtual:starlight/components/EditLink";
|
|
16
|
+
// @ts-expect-error
|
|
17
|
+
import LastUpdated from "virtual:starlight/components/LastUpdated";
|
|
18
|
+
// @ts-expect-error
|
|
19
|
+
import Pagination from "virtual:starlight/components/Pagination";
|
|
20
|
+
// @ts-expect-error
|
|
21
|
+
import config from "virtual:starlight/user-config";
|
|
22
|
+
import { Icon } from "@astrojs/starlight/components";
|
|
23
|
+
import { products } from "../data/products.js";
|
|
24
|
+
|
|
25
|
+
const currentYear = new Date().getFullYear();
|
|
26
|
+
|
|
27
|
+
const product = (import.meta.env.PUBLIC_DOCS_PRODUCT ?? "") as string;
|
|
28
|
+
|
|
29
|
+
const shouldShowDocsHomeLink = Astro.url.pathname !== "/";
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
<footer class="sl-flex knitli-page-footer">
|
|
33
|
+
<!-- Starlight's native page-level footer: edit link, last updated, pagination, optional credits -->
|
|
34
|
+
<div class="sl-flex page-actions">
|
|
35
|
+
<div class="meta sl-flex">
|
|
36
|
+
<EditLink />
|
|
37
|
+
<LastUpdated />
|
|
38
|
+
</div>
|
|
39
|
+
<Pagination />
|
|
40
|
+
{config.credits && (
|
|
41
|
+
<a class="kudos sl-flex" href="https://starlight.astro.build">
|
|
42
|
+
|
|
43
|
+
<Icon name="starlight" /> {
|
|
44
|
+
// @ts-ignore: 2339
|
|
45
|
+
Astro.locals.t('builtWithStarlight.label')}
|
|
46
|
+
</a>
|
|
47
|
+
)}
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
<!-- Knitli site footer -->
|
|
51
|
+
<div class="site-footer sl-flex">
|
|
52
|
+
<div class="footer-content sl-flex">
|
|
53
|
+
<div class="footer-brand">
|
|
54
|
+
<p class="copyright">
|
|
55
|
+
© {currentYear}Knitli, Inc. All rights reserved.
|
|
56
|
+
</p>
|
|
57
|
+
<div class="social-links sl-flex">
|
|
58
|
+
<a
|
|
59
|
+
href="https://github.com/knitli"
|
|
60
|
+
aria-label="GitHub"
|
|
61
|
+
class="social-link"
|
|
62
|
+
target="_blank"
|
|
63
|
+
rel="noopener noreferrer"
|
|
64
|
+
>
|
|
65
|
+
<Icon name="github" size="1.1rem" />
|
|
66
|
+
</a>
|
|
67
|
+
<a
|
|
68
|
+
href="https://x.com/knitli_inc"
|
|
69
|
+
aria-label="X"
|
|
70
|
+
class="social-link"
|
|
71
|
+
target="_blank"
|
|
72
|
+
rel="noopener noreferrer"
|
|
73
|
+
>
|
|
74
|
+
<Icon name="x.com" size="1.1rem" />
|
|
75
|
+
</a>
|
|
76
|
+
<a
|
|
77
|
+
href="https://bsky.app/profile/knitli.com"
|
|
78
|
+
aria-label="Bluesky"
|
|
79
|
+
class="social-link"
|
|
80
|
+
target="_blank"
|
|
81
|
+
rel="noopener noreferrer"
|
|
82
|
+
>
|
|
83
|
+
<Icon name="blueSky" size="1.1rem" />
|
|
84
|
+
</a>
|
|
85
|
+
<a
|
|
86
|
+
href="https://linkedin.com/company/knitli"
|
|
87
|
+
aria-label="LinkedIn"
|
|
88
|
+
class="social-link"
|
|
89
|
+
target="_blank"
|
|
90
|
+
rel="noopener noreferrer"
|
|
91
|
+
>
|
|
92
|
+
<Icon name="linkedin" size="1.1rem" />
|
|
93
|
+
</a>
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
96
|
+
|
|
97
|
+
<nav class="footer-nav" aria-label="Knitli">
|
|
98
|
+
<p class="nav-heading">Knitli</p>
|
|
99
|
+
<ul>
|
|
100
|
+
<li><a href="https://knitli.com">knitli.com</a></li>
|
|
101
|
+
<li><a href="https://blog.knitli.com">Blog</a></li>
|
|
102
|
+
</ul>
|
|
103
|
+
</nav>
|
|
104
|
+
|
|
105
|
+
<nav class="footer-nav" aria-label="Documentation">
|
|
106
|
+
<p class="nav-heading">Docs</p>
|
|
107
|
+
<ul>
|
|
108
|
+
{shouldShowDocsHomeLink && <li><a href="https://docs.knitli.com">Developer Docs</a></li>}
|
|
109
|
+
{products
|
|
110
|
+
.filter((p) => p.docsUrl)
|
|
111
|
+
.sort((a, b) => a.order - b.order)
|
|
112
|
+
.map((p) => (
|
|
113
|
+
<li key={p.name}>
|
|
114
|
+
{product === p.name ? (
|
|
115
|
+
<span class="link-current">{p.name}</span>
|
|
116
|
+
) : p.external ? (
|
|
117
|
+
<a
|
|
118
|
+
href={p.docsUrl}
|
|
119
|
+
target="_blank"
|
|
120
|
+
rel="noopener noreferrer"
|
|
121
|
+
>
|
|
122
|
+
{p.name} ↗
|
|
123
|
+
</a>
|
|
124
|
+
) : (
|
|
125
|
+
<a href={`https://docs.knitli.com/${p.slug}/`}>{p.name}</a>
|
|
126
|
+
)}
|
|
127
|
+
</li>
|
|
128
|
+
))}
|
|
129
|
+
</ul>
|
|
130
|
+
</nav>
|
|
131
|
+
</div>
|
|
132
|
+
</div>
|
|
133
|
+
</footer>
|
|
134
|
+
|
|
135
|
+
<style>
|
|
136
|
+
@layer starlight.core {
|
|
137
|
+
/* ---- Outer wrapper ---- */
|
|
138
|
+
.knitli-page-footer {
|
|
139
|
+
flex-direction: column;
|
|
140
|
+
gap: 0;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/* ---- Starlight native page actions (replicated from Starlight's Footer) ---- */
|
|
144
|
+
.page-actions {
|
|
145
|
+
flex-direction: column;
|
|
146
|
+
gap: 1.5rem;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.meta {
|
|
150
|
+
gap: 0.75rem 3rem;
|
|
151
|
+
justify-content: space-between;
|
|
152
|
+
flex-wrap: wrap;
|
|
153
|
+
margin-top: 3rem;
|
|
154
|
+
font-size: var(--sl-text-sm);
|
|
155
|
+
color: var(--sl-color-gray-3);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.meta > :global(p:only-child) {
|
|
159
|
+
margin-inline-start: auto;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.kudos {
|
|
163
|
+
align-items: center;
|
|
164
|
+
gap: 0.5em;
|
|
165
|
+
margin: 1.5rem auto;
|
|
166
|
+
font-size: var(--sl-text-xs);
|
|
167
|
+
text-decoration: none;
|
|
168
|
+
color: var(--sl-color-gray-3);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.kudos:hover {
|
|
172
|
+
color: var(--sl-color-white);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/* ---- Knitli site footer ---- */
|
|
176
|
+
.site-footer {
|
|
177
|
+
flex-direction: column;
|
|
178
|
+
border-top: 1px solid var(--sl-color-gray-5);
|
|
179
|
+
padding-top: 2rem;
|
|
180
|
+
margin-top: 2rem;
|
|
181
|
+
gap: 1.5rem;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.footer-content {
|
|
185
|
+
flex-wrap: wrap;
|
|
186
|
+
gap: 2rem 4rem;
|
|
187
|
+
align-items: flex-start;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.footer-brand {
|
|
191
|
+
flex: 1;
|
|
192
|
+
min-width: 200px;
|
|
193
|
+
display: flex;
|
|
194
|
+
flex-direction: column;
|
|
195
|
+
gap: 0.75rem;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.copyright {
|
|
199
|
+
margin: 0;
|
|
200
|
+
font-size: var(--sl-text-sm);
|
|
201
|
+
color: var(--sl-color-gray-3);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.social-links {
|
|
205
|
+
gap: 0.75rem;
|
|
206
|
+
align-items: center;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.social-link {
|
|
210
|
+
display: flex;
|
|
211
|
+
align-items: center;
|
|
212
|
+
color: var(--sl-color-gray-3);
|
|
213
|
+
text-decoration: none;
|
|
214
|
+
transition: color 0.2s ease;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.social-link:hover {
|
|
218
|
+
color: var(--sl-color-accent-high);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.footer-nav {
|
|
222
|
+
display: flex;
|
|
223
|
+
flex-direction: column;
|
|
224
|
+
gap: 0.5rem;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
.nav-heading {
|
|
228
|
+
margin: 0 0 0.5rem;
|
|
229
|
+
font-size: var(--sl-text-xs);
|
|
230
|
+
font-weight: 600;
|
|
231
|
+
text-transform: uppercase;
|
|
232
|
+
letter-spacing: 0.08em;
|
|
233
|
+
color: var(--sl-color-accent);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
.footer-nav ul {
|
|
237
|
+
list-style: none;
|
|
238
|
+
padding: 0;
|
|
239
|
+
margin: 0;
|
|
240
|
+
display: flex;
|
|
241
|
+
flex-direction: column;
|
|
242
|
+
gap: 0.4rem;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.footer-nav a {
|
|
246
|
+
font-size: var(--sl-text-sm);
|
|
247
|
+
color: var(--sl-color-gray-2);
|
|
248
|
+
text-decoration: none;
|
|
249
|
+
transition: color 0.2s ease;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.footer-nav a:hover {
|
|
253
|
+
color: var(--sl-color-accent-high);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
.link-current {
|
|
257
|
+
font-size: var(--sl-text-sm);
|
|
258
|
+
color: var(--sl-color-white);
|
|
259
|
+
font-weight: 500;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
@layer starlight.components {
|
|
264
|
+
/* Starlight icon color in the kudos link */
|
|
265
|
+
.kudos :global(svg) {
|
|
266
|
+
color: var(--sl-color-orange);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
</style>
|
|
@@ -0,0 +1,187 @@
|
|
|
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
|
+
// Starlight PageFrame override for Knitli product documentation sites.
|
|
8
|
+
// Adds the cross-site subnav bar (with breadcrumb) above the Starlight header.
|
|
9
|
+
//
|
|
10
|
+
// Usage in astro.config.mjs:
|
|
11
|
+
// starlight({ components: { PageFrame: '@knitli/docs-components/PageFrame.astro' } })
|
|
12
|
+
//
|
|
13
|
+
// Required env variable: PUBLIC_DOCS_PRODUCT = 'Recoco' | 'CodeWeaver' | 'Thread'
|
|
14
|
+
// The --sl-subnav-height CSS variable defaults to 2rem if not set in customCss.
|
|
15
|
+
|
|
16
|
+
// @ts-expect-error: virtual:starlight/components/* are typed in virtual-internal.d.ts,
|
|
17
|
+
// which is not in Starlight's public exports map
|
|
18
|
+
import MobileMenuToggle from "virtual:starlight/components/MobileMenuToggle";
|
|
19
|
+
import DocsBreadcrumb from "./DocsBreadcrumb.astro";
|
|
20
|
+
|
|
21
|
+
const { hasSidebar } = Astro.locals.starlightRoute;
|
|
22
|
+
|
|
23
|
+
const product = import.meta.env.PUBLIC_DOCS_PRODUCT as string | undefined;
|
|
24
|
+
|
|
25
|
+
// Strip the configured base URL to get the within-product path for the breadcrumb.
|
|
26
|
+
// e.g. for base='/Recoco/' and pathname='/Recoco/guide/start', path = 'guide/start'
|
|
27
|
+
const basePath = (import.meta.env.BASE_URL ?? "/").replace(/\/?$/, "/");
|
|
28
|
+
const currentPath = Astro.url.pathname
|
|
29
|
+
.slice(basePath.length)
|
|
30
|
+
.replace(/^\//, "");
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
<div class="page sl-flex">
|
|
34
|
+
<nav class="knitli-subnav sl-flex" aria-label="Knitli">
|
|
35
|
+
{product && (
|
|
36
|
+
<div class="subnav-breadcrumb">
|
|
37
|
+
<DocsBreadcrumb
|
|
38
|
+
product={product}
|
|
39
|
+
productUrl={basePath}
|
|
40
|
+
path={currentPath}
|
|
41
|
+
variant="inline"
|
|
42
|
+
/>
|
|
43
|
+
</div>
|
|
44
|
+
)}
|
|
45
|
+
<div class="subnav-links sl-flex">
|
|
46
|
+
<a href="https://knitli.com" class="subnav-link">Knitli Home</a>
|
|
47
|
+
<a href="https://blog.knitli.com" class="subnav-link">Blog</a>
|
|
48
|
+
</div>
|
|
49
|
+
</nav>
|
|
50
|
+
|
|
51
|
+
<header class="header"><slot name="header" /></header>
|
|
52
|
+
|
|
53
|
+
{hasSidebar && (
|
|
54
|
+
<nav class="sidebar print:hidden" aria-label={Astro.locals.t('sidebarNav.accessibleLabel')}>
|
|
55
|
+
<MobileMenuToggle />
|
|
56
|
+
<div id="starlight__sidebar" class="sidebar-pane">
|
|
57
|
+
<div class="sidebar-content sl-flex">
|
|
58
|
+
<slot name="sidebar" />
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
</nav>
|
|
62
|
+
)}
|
|
63
|
+
|
|
64
|
+
<div class="main-frame"><slot /></div>
|
|
65
|
+
</div>
|
|
66
|
+
|
|
67
|
+
<style>
|
|
68
|
+
@layer starlight.core {
|
|
69
|
+
/* Replicated from Starlight's PageFrame — only positional values modified */
|
|
70
|
+
.page {
|
|
71
|
+
flex-direction: column;
|
|
72
|
+
min-height: 100vh;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.header {
|
|
76
|
+
z-index: var(--sl-z-index-navbar);
|
|
77
|
+
position: fixed;
|
|
78
|
+
inset-inline-start: 0;
|
|
79
|
+
inset-block-start: var(--sl-subnav-height, 2rem); /* offset below subnav */
|
|
80
|
+
width: 100%;
|
|
81
|
+
height: var(--sl-nav-height);
|
|
82
|
+
border-bottom: 1px solid var(--sl-color-hairline-shade);
|
|
83
|
+
padding: var(--sl-nav-pad-y) var(--sl-nav-pad-x);
|
|
84
|
+
padding-inline-end: var(--sl-nav-pad-x);
|
|
85
|
+
background-color: var(--sl-color-bg-nav);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
:global([data-has-sidebar]) .header {
|
|
89
|
+
padding-inline-end: calc(
|
|
90
|
+
var(--sl-nav-gap) +
|
|
91
|
+
var(--sl-nav-pad-x) +
|
|
92
|
+
var(--sl-menu-button-size)
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.sidebar-pane {
|
|
97
|
+
visibility: var(--sl-sidebar-visibility, hidden);
|
|
98
|
+
position: fixed;
|
|
99
|
+
z-index: var(--sl-z-index-menu);
|
|
100
|
+
inset-block: calc(var(--sl-nav-height) + var(--sl-subnav-height, 2rem)) 0;
|
|
101
|
+
inset-inline-start: 0;
|
|
102
|
+
width: 100%;
|
|
103
|
+
background-color: var(--sl-color-black);
|
|
104
|
+
overflow-y: auto;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
:global([aria-expanded="true"]) ~ .sidebar-pane {
|
|
108
|
+
--sl-sidebar-visibility: visible;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.sidebar-content {
|
|
112
|
+
height: 100%;
|
|
113
|
+
min-height: max-content;
|
|
114
|
+
padding: 1rem var(--sl-sidebar-pad-x) 0;
|
|
115
|
+
flex-direction: column;
|
|
116
|
+
gap: 1rem;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
@media (min-width: 50rem) {
|
|
120
|
+
.sidebar-content::after {
|
|
121
|
+
content: "";
|
|
122
|
+
padding-bottom: 1px;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.main-frame {
|
|
127
|
+
padding-top: calc(
|
|
128
|
+
var(--sl-nav-height) +
|
|
129
|
+
var(--sl-subnav-height, 2rem) +
|
|
130
|
+
var(--sl-mobile-toc-height)
|
|
131
|
+
);
|
|
132
|
+
padding-inline-start: var(--sl-content-inline-start);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
@media (min-width: 50rem) {
|
|
136
|
+
:global([data-has-sidebar]) .header {
|
|
137
|
+
padding-inline-end: var(--sl-nav-pad-x);
|
|
138
|
+
}
|
|
139
|
+
.sidebar-pane {
|
|
140
|
+
--sl-sidebar-visibility: visible;
|
|
141
|
+
width: var(--sl-sidebar-width);
|
|
142
|
+
background-color: var(--sl-color-bg-sidebar);
|
|
143
|
+
border-inline-end: 1px solid var(--sl-color-hairline-shade);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/* Knitli cross-site subnav */
|
|
148
|
+
.knitli-subnav {
|
|
149
|
+
position: fixed;
|
|
150
|
+
inset-block-start: 0;
|
|
151
|
+
inset-inline-start: 0;
|
|
152
|
+
width: 100%;
|
|
153
|
+
height: var(--sl-subnav-height, 2rem);
|
|
154
|
+
z-index: var(--sl-z-index-navbar);
|
|
155
|
+
background-color: var(--sl-color-bg-nav);
|
|
156
|
+
padding-inline: var(--sl-nav-pad-x);
|
|
157
|
+
justify-content: space-between;
|
|
158
|
+
align-items: center;
|
|
159
|
+
gap: 1rem;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.subnav-breadcrumb {
|
|
163
|
+
flex: 1;
|
|
164
|
+
min-width: 0; /* allows breadcrumb to shrink and truncate */
|
|
165
|
+
overflow: hidden;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.subnav-links {
|
|
169
|
+
gap: 1.25rem;
|
|
170
|
+
align-items: center;
|
|
171
|
+
flex-shrink: 0;
|
|
172
|
+
margin-inline-start: auto;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.subnav-link {
|
|
176
|
+
font-size: var(--sl-text-xs);
|
|
177
|
+
color: var(--sl-color-gray-3);
|
|
178
|
+
text-decoration: none;
|
|
179
|
+
transition: color 0.2s ease;
|
|
180
|
+
white-space: nowrap;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.subnav-link:hover {
|
|
184
|
+
color: var(--sl-color-white);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
</style>
|