starlight-theme-bejamas 0.0.0-canary.0d34eb2

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,139 @@
1
+ ---
2
+ import { Button } from "virtual:starlight-theme-bejamas/components/button";
3
+
4
+ import { ArrowRight, ArrowLeft, ArrowUpRight } from "@lucide/astro";
5
+
6
+ const { dir, pagination, template, entry } = Astro.locals.starlightRoute;
7
+ const { prev, next } = pagination;
8
+ const isRtl = dir === "rtl";
9
+ ---
10
+
11
+ <div
12
+ class:list={[
13
+ "sl-bejamas-docs-title",
14
+ entry.data.template === "splash" && "mt-10",
15
+ ]}
16
+ >
17
+ <h1 id="_top" class="tracking-tight">
18
+ {Astro.locals.starlightRoute.entry.data.title}
19
+ </h1>
20
+
21
+ {
22
+ entry.data.template !== "splash" && (
23
+ <div class="sl-bejamas-docs-title-buttons">
24
+ {entry.data.figmaUrl && (
25
+ <Button
26
+ variant="outline"
27
+ size="sm"
28
+ as="a"
29
+ href={entry.data.figmaUrl}
30
+ target="_blank"
31
+ >
32
+ <svg
33
+ fill="none"
34
+ height="24"
35
+ viewBox="0 0 38 57"
36
+ width="24"
37
+ xmlns="http://www.w3.org/2000/svg"
38
+ class="text-lg"
39
+ >
40
+ <>
41
+ <path
42
+ d="M19 28.5a9.5 9.5 0 1 1 19 0 9.5 9.5 0 0 1-19 0Z"
43
+ fill="#1ABCFE"
44
+ />
45
+ <path
46
+ d="M0 47.5A9.5 9.5 0 0 1 9.5 38H19v9.5a9.5 9.5 0 1 1-19 0Z"
47
+ fill="#0ACF83"
48
+ />
49
+ <path d="M19 0v19h9.5a9.5 9.5 0 1 0 0-19H19Z" fill="#FF7262" />
50
+ <path
51
+ d="M0 9.5A9.5 9.5 0 0 0 9.5 19H19V0H9.5A9.5 9.5 0 0 0 0 9.5Z"
52
+ fill="#F24E1E"
53
+ />
54
+ <path
55
+ d="M0 28.5A9.5 9.5 0 0 0 9.5 38H19V19H9.5A9.5 9.5 0 0 0 0 28.5Z"
56
+ fill="#A259FF"
57
+ />
58
+ </>
59
+ </svg>
60
+ Figma
61
+ <ArrowUpRight />
62
+ </Button>
63
+ )}
64
+ {prev && (
65
+ <Button
66
+ as="a"
67
+ href={isRtl ? next.href : prev.href}
68
+ variant="outline"
69
+ size="icon-sm"
70
+ >
71
+ <ArrowLeft class="size-4" />
72
+ </Button>
73
+ )}
74
+ {next && (
75
+ <Button
76
+ as="a"
77
+ href={isRtl ? prev.href : next.href}
78
+ variant="outline"
79
+ size="icon-sm"
80
+ >
81
+ <ArrowRight class="size-4" />
82
+ </Button>
83
+ )}
84
+ </div>
85
+ )
86
+ }
87
+
88
+ {
89
+ entry.data.description && (
90
+ <p class="sl-bejamas-description">{entry.data.description}</p>
91
+ )
92
+ }
93
+ </div>
94
+
95
+ <style>
96
+ @layer bejamas {
97
+ .sl-bejamas-docs-title {
98
+ display: flex;
99
+ justify-content: space-between;
100
+ flex-wrap: wrap;
101
+ margin-top: 0.5rem;
102
+ align-items: center;
103
+ }
104
+ .sl-bejamas-docs-title-buttons {
105
+ gap: 0.5rem;
106
+ display: none;
107
+ }
108
+
109
+ @media (min-width: 50rem) {
110
+ .sl-bejamas-docs-title-buttons {
111
+ display: flex;
112
+ }
113
+ }
114
+ }
115
+
116
+ .sl-bejamas-description {
117
+ color: var(--muted-foreground);
118
+ text-wrap: balance;
119
+ margin-top: calc(var(--spacing) * 4);
120
+ margin-bottom: calc(var(--spacing) * 2);
121
+ width: 100%;
122
+ }
123
+
124
+ @layer starlight.core {
125
+ h1 {
126
+ font-size: 1.875rem;
127
+ line-height: 1.2;
128
+ font-weight: 500;
129
+ color: var(--sl-color-white);
130
+ }
131
+
132
+ @media (min-width: 50rem) {
133
+ h1 {
134
+ font-size: 2.25rem;
135
+ line-height: calc(2.5 / 2.25);
136
+ }
137
+ }
138
+ }
139
+ </style>
@@ -0,0 +1,55 @@
1
+ ---
2
+ import { Button } from "virtual:starlight-theme-bejamas/components/button";
3
+ import { ArrowRight, ArrowLeft } from "@lucide/astro";
4
+
5
+ const { dir, pagination } = Astro.locals.starlightRoute;
6
+ const { prev, next } = pagination;
7
+ const isRtl = dir === "rtl";
8
+ ---
9
+
10
+ <div class="sl-flex sl-justify-between print:hidden z-10" dir={dir}>
11
+ {
12
+ prev && (
13
+ <Button as="a" href={prev.href} rel="prev" variant="outline">
14
+ {isRtl ? <ArrowRight class="size-4" /> : <ArrowLeft class="size-4" />}
15
+ {prev.label}
16
+ </Button>
17
+ )
18
+ }
19
+ {
20
+ next && (
21
+ <Button as="a" href={next.href} rel="next" variant="outline">
22
+ {isRtl ? <ArrowLeft class="size-4" /> : <ArrowRight class="size-4" />}
23
+ {next.label}
24
+ </Button>
25
+ )
26
+ }
27
+ </div>
28
+
29
+ <style>
30
+ @layer starlight.core {
31
+ .pagination-links {
32
+ display: grid;
33
+ grid-template-columns: repeat(auto-fit, minmax(min(18rem, 100%), 1fr));
34
+ gap: 1rem;
35
+ }
36
+
37
+ [rel="next"] {
38
+ justify-content: end;
39
+ text-align: end;
40
+ flex-direction: row-reverse;
41
+ }
42
+
43
+ svg {
44
+ flex-shrink: 0;
45
+ }
46
+
47
+ .page-footer {
48
+ margin-top: calc(var(--spacing) * 6);
49
+ }
50
+
51
+ .sl-justify-between {
52
+ justify-content: space-between;
53
+ }
54
+ }
55
+ </style>
@@ -0,0 +1,43 @@
1
+ ---
2
+ import MobileMenuFooter from "virtual:starlight/components/MobileMenuFooter";
3
+ import SidebarPersister from "@astrojs/starlight/components/SidebarPersister.astro";
4
+ import SidebarSublist from "@astrojs/starlight/components/SidebarSublist.astro";
5
+
6
+ const { sidebar } = Astro.locals.starlightRoute;
7
+ ---
8
+
9
+ <SidebarPersister>
10
+ <div class="fade"></div>
11
+ <SidebarSublist sublist={sidebar} />
12
+ </SidebarPersister>
13
+
14
+ <div class="md:sl-hidden">
15
+ <MobileMenuFooter />
16
+ </div>
17
+
18
+ <style>
19
+ @layer bejamas {
20
+ .fade {
21
+ --blur: 4px;
22
+ --stop: 50%;
23
+ --height: 72px;
24
+ position: fixed;
25
+ pointer-events: none;
26
+ width: 100%;
27
+ height: var(--height);
28
+ user-select: none;
29
+ -webkit-user-select: none;
30
+ left: 0;
31
+ -webkit-backdrop-filter: blur(var(--blur));
32
+ backdrop-filter: blur(var(--blur));
33
+
34
+ background: linear-gradient(to top, transparent, var(--background));
35
+ mask-image: linear-gradient(
36
+ to bottom,
37
+ var(--background) var(--stop),
38
+ transparent
39
+ );
40
+ top: 0;
41
+ }
42
+ }
43
+ </style>
@@ -0,0 +1,59 @@
1
+ ---
2
+ import { logos } from "virtual:starlight/user-images";
3
+ import config from "virtual:starlight/user-config";
4
+ const { siteTitle, siteTitleHref } = Astro.locals.starlightRoute;
5
+ ---
6
+
7
+ <a href={siteTitleHref} class="site-title sl-flex text-sm text-foreground">
8
+ {
9
+ config.logo && logos.dark && (
10
+ <>
11
+ <img
12
+ class:list={{
13
+ "light:sl-hidden print:hidden": !("src" in config.logo),
14
+ }}
15
+ alt={config.logo.alt}
16
+ src={logos.dark.src}
17
+ width={logos.dark.width}
18
+ height={logos.dark.height}
19
+ />
20
+ {/* Show light alternate if a user configure both light and dark logos. */}
21
+ {!("src" in config.logo) && (
22
+ <img
23
+ class="dark:sl-hidden print:block"
24
+ alt={config.logo.alt}
25
+ src={logos.light?.src}
26
+ width={logos.light?.width}
27
+ height={logos.light?.height}
28
+ />
29
+ )}
30
+ </>
31
+ )
32
+ }
33
+ <span class:list={{ "sr-only": config.logo?.replacesTitle }} translate="no">
34
+ {siteTitle}
35
+ </span>
36
+ </a>
37
+
38
+ <style>
39
+ @layer starlight.core {
40
+ .site-title {
41
+ align-items: center;
42
+ gap: var(--sl-nav-gap);
43
+ font-weight: 600;
44
+ text-decoration: none;
45
+ white-space: nowrap;
46
+ min-width: 0;
47
+ }
48
+ span {
49
+ overflow: hidden;
50
+ }
51
+ img {
52
+ height: calc(var(--sl-nav-height) - 2 * var(--sl-nav-pad-y));
53
+ width: auto;
54
+ max-width: 100%;
55
+ object-fit: contain;
56
+ object-position: 0 50%;
57
+ }
58
+ }
59
+ </style>
@@ -0,0 +1,88 @@
1
+ ---
2
+ import type { MarkdownHeading } from "astro";
3
+
4
+ interface TocItem extends MarkdownHeading {
5
+ children: TocItem[];
6
+ }
7
+
8
+ interface Props {
9
+ toc: TocItem[];
10
+ depth?: number;
11
+ isMobile?: boolean;
12
+ }
13
+
14
+ const { toc, isMobile = false, depth = 0 } = Astro.props;
15
+ ---
16
+
17
+ <ul class:list={{ isMobile }}>
18
+ {
19
+ toc.map((heading) => (
20
+ <li>
21
+ <a href={"#" + heading.slug}>
22
+ <span>{heading.text}</span>
23
+ </a>
24
+ {heading.children.length > 0 && (
25
+ <Astro.self
26
+ toc={heading.children}
27
+ depth={depth + 1}
28
+ isMobile={isMobile}
29
+ />
30
+ )}
31
+ </li>
32
+ ))
33
+ }
34
+ </ul>
35
+
36
+ <style define:vars={{ depth }}>
37
+ @layer starlight.core {
38
+ ul {
39
+ padding: 0;
40
+ list-style: none;
41
+ }
42
+ a {
43
+ --pad-inline: 0.5rem;
44
+ display: block;
45
+ border-radius: 0.25rem;
46
+ padding-block: 0.25rem;
47
+ padding-inline: calc(1rem * var(--depth) + var(--pad-inline))
48
+ var(--pad-inline);
49
+ line-height: 1.25;
50
+ }
51
+ a[aria-current="true"] {
52
+ color: var(--sl-color-text-accent);
53
+ }
54
+ .isMobile a {
55
+ --pad-inline: 1rem;
56
+ display: flex;
57
+ justify-content: space-between;
58
+ gap: var(--pad-inline);
59
+ border-top: 1px solid var(--sl-color-gray-6);
60
+ border-radius: 0;
61
+ padding-block: 0.5rem;
62
+ color: var(--sl-color-text);
63
+ font-size: var(--sl-text-sm);
64
+ text-decoration: none;
65
+ outline-offset: var(--sl-outline-offset-inside);
66
+ }
67
+ .isMobile:first-child > li:first-child > a {
68
+ border-top: 0;
69
+ }
70
+ .isMobile a[aria-current="true"],
71
+ .isMobile a[aria-current="true"]:hover,
72
+ .isMobile a[aria-current="true"]:focus {
73
+ color: var(--sl-color-white);
74
+ background-color: unset;
75
+ }
76
+ .isMobile a[aria-current="true"]::after {
77
+ content: "";
78
+ width: 1rem;
79
+ background-color: var(--sl-color-text-accent);
80
+ /* Check mark SVG icon */
81
+ -webkit-mask-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9JzAgMCAxNCAxNCc+PHBhdGggZD0nTTEwLjkxNCA0LjIwNmEuNTgzLjU4MyAwIDAgMC0uODI4IDBMNS43NCA4LjU1NyAzLjkxNCA2LjcyNmEuNTk2LjU5NiAwIDAgMC0uODI4Ljg1N2wyLjI0IDIuMjRhLjU4My41ODMgMCAwIDAgLjgyOCAwbDQuNzYtNC43NmEuNTgzLjU4MyAwIDAgMCAwLS44NTdaJy8+PC9zdmc+Cg==");
82
+ mask-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9JzAgMCAxNCAxNCc+PHBhdGggZD0nTTEwLjkxNCA0LjIwNmEuNTgzLjU4MyAwIDAgMC0uODI4IDBMNS43NCA4LjU1NyAzLjkxNCA2LjcyNmEuNTk2LjU5NiAwIDAgMC0uODI4Ljg1N2wyLjI0IDIuMjRhLjU4My41ODMgMCAwIDAgLjgyOCAwbDQuNzYtNC43NmEuNTgzLjU4MyAwIDAgMCAwLS44NTdaJy8+PC9zdmc+Cg==");
83
+ -webkit-mask-repeat: no-repeat;
84
+ mask-repeat: no-repeat;
85
+ flex-shrink: 0;
86
+ }
87
+ }
88
+ </style>
@@ -0,0 +1,125 @@
1
+ const PAGE_TITLE_ID = "_top";
2
+
3
+ export class StarlightTOC extends HTMLElement {
4
+ private _current = this.querySelector<HTMLAnchorElement>(
5
+ 'a[aria-current="true"]',
6
+ );
7
+ private minH = parseInt(this.dataset.minH || "2", 10);
8
+ private maxH = parseInt(this.dataset.maxH || "3", 10);
9
+
10
+ protected set current(link: HTMLAnchorElement) {
11
+ if (link === this._current) return;
12
+ if (this._current) this._current.removeAttribute("aria-current");
13
+ link.setAttribute("aria-current", "true");
14
+ this._current = link;
15
+ }
16
+
17
+ private onIdle = (cb: IdleRequestCallback) =>
18
+ (window.requestIdleCallback || ((cb) => setTimeout(cb, 1)))(cb);
19
+
20
+ constructor() {
21
+ super();
22
+ this.onIdle(() => this.init());
23
+ }
24
+
25
+ private init = (): void => {
26
+ /** All the links in the table of contents. */
27
+ const links = [...this.querySelectorAll("a")];
28
+
29
+ /** Test if an element is a table-of-contents heading. */
30
+ const isHeading = (el: Element): el is HTMLHeadingElement => {
31
+ if (el instanceof HTMLHeadingElement) {
32
+ // Special case for page title h1
33
+ if (el.id === PAGE_TITLE_ID) return true;
34
+ // Check the heading level is within the user-configured limits for the ToC
35
+ const level = el.tagName[1];
36
+ if (level) {
37
+ const int = parseInt(level, 10);
38
+ if (int >= this.minH && int <= this.maxH) return true;
39
+ }
40
+ }
41
+ return false;
42
+ };
43
+
44
+ /** Walk up the DOM to find the nearest heading. */
45
+ const getElementHeading = (
46
+ el: Element | null,
47
+ ): HTMLHeadingElement | null => {
48
+ if (!el) return null;
49
+ const origin = el;
50
+ while (el) {
51
+ if (isHeading(el)) return el;
52
+ // Assign the previous sibling’s last, most deeply nested child to el.
53
+ el = el.previousElementSibling;
54
+ while (el?.lastElementChild) {
55
+ el = el.lastElementChild;
56
+ }
57
+ // Look for headings amongst siblings.
58
+ const h = getElementHeading(el);
59
+ if (h) return h;
60
+ }
61
+ // Walk back up the parent.
62
+ return getElementHeading(origin.parentElement);
63
+ };
64
+
65
+ /** Handle intersections and set the current link to the heading for the current intersection. */
66
+ const setCurrent: IntersectionObserverCallback = (entries) => {
67
+ for (const { isIntersecting, target } of entries) {
68
+ if (!isIntersecting) continue;
69
+ const heading = getElementHeading(target);
70
+ if (!heading) continue;
71
+ const link = links.find(
72
+ (link) => link.hash === "#" + encodeURIComponent(heading.id),
73
+ );
74
+ if (link) {
75
+ this.current = link;
76
+ break;
77
+ }
78
+ }
79
+ };
80
+
81
+ // Observe elements with an `id` (most likely headings) and their siblings.
82
+ // Also observe direct children of `.content` to include elements before
83
+ // the first heading.
84
+ const toObserve = document.querySelectorAll(
85
+ "main [id], main [id] ~ *, main .content > *",
86
+ );
87
+
88
+ let observer: IntersectionObserver | undefined;
89
+ const observe = () => {
90
+ if (observer) return;
91
+ observer = new IntersectionObserver(setCurrent, {
92
+ rootMargin: this.getRootMargin(),
93
+ });
94
+ toObserve.forEach((h) => observer!.observe(h));
95
+ };
96
+ observe();
97
+
98
+ let timeout: NodeJS.Timeout;
99
+ window.addEventListener("resize", () => {
100
+ // Disable intersection observer while window is resizing.
101
+ if (observer) {
102
+ observer.disconnect();
103
+ observer = undefined;
104
+ }
105
+ clearTimeout(timeout);
106
+ timeout = setTimeout(() => this.onIdle(observe), 200);
107
+ });
108
+ };
109
+
110
+ private getRootMargin(): `-${number}px 0% ${number}px` {
111
+ const navBarHeight =
112
+ document.querySelector("header")?.getBoundingClientRect().height || 0;
113
+ // `<summary>` only exists in mobile ToC, so will fall back to 0 in large viewport component.
114
+ const mobileTocHeight =
115
+ this.querySelector("summary")?.getBoundingClientRect().height || 0;
116
+ /** Start intersections at nav height + 2rem padding. */
117
+ const top = navBarHeight + mobileTocHeight + 32;
118
+ /** End intersections `53px` later. This is slightly more than the maximum `margin-top` in Markdown content. */
119
+ const bottom = top + 53;
120
+ const height = document.documentElement.clientHeight;
121
+ return `-${top}px 0% ${bottom - height}px`;
122
+ }
123
+ }
124
+
125
+ customElements.define("starlight-toc", StarlightTOC);
@@ -0,0 +1,106 @@
1
+ {/* This is intentionally inlined to avoid FOUC. */}
2
+ <script is:inline>
3
+ window.StarlightThemeProvider = (() => {
4
+ const themeMetaMedias = [
5
+ "(prefers-color-scheme: light)",
6
+ "(prefers-color-scheme: dark)",
7
+ ];
8
+ const fallbackThemeColors = {
9
+ light: "#ffffff",
10
+ dark: "#0f171f",
11
+ };
12
+
13
+ const themeColors = (() => {
14
+ const html = document.documentElement;
15
+ const prevTheme = html.dataset.theme;
16
+ const prevDark = html.classList.contains("dark");
17
+
18
+ const readBackground = (theme) => {
19
+ html.dataset.theme = theme;
20
+ html.classList.toggle("dark", theme === "dark");
21
+ return (
22
+ getComputedStyle(html).getPropertyValue("--background").trim() ||
23
+ undefined
24
+ );
25
+ };
26
+
27
+ const light = readBackground("light") || fallbackThemeColors.light;
28
+ const dark = readBackground("dark") || fallbackThemeColors.dark;
29
+
30
+ if (prevTheme !== undefined && prevTheme !== "") {
31
+ html.dataset.theme = prevTheme;
32
+ html.classList.toggle("dark", prevDark);
33
+ } else {
34
+ html.removeAttribute("data-theme");
35
+ html.classList.toggle("dark", prevDark);
36
+ }
37
+
38
+ return { light, dark };
39
+ })();
40
+
41
+ function ensureThemeMeta(head, media) {
42
+ const baseSelector = `meta[name="theme-color"][media="${media}"]`;
43
+ let meta = head.querySelector(`${baseSelector}[data-theme-managed]`);
44
+ if (meta instanceof HTMLMetaElement) return meta;
45
+
46
+ meta = head.querySelector(baseSelector);
47
+ if (meta instanceof HTMLMetaElement) {
48
+ meta.setAttribute("data-theme-managed", "");
49
+ return meta;
50
+ }
51
+
52
+ meta = document.createElement("meta");
53
+ meta.setAttribute("name", "theme-color");
54
+ meta.setAttribute("media", media);
55
+ meta.setAttribute("data-theme-managed", "");
56
+ head.appendChild(meta);
57
+ return meta;
58
+ }
59
+
60
+ function syncThemeColor(theme) {
61
+ const color = themeColors[theme];
62
+ const head = document.head;
63
+ if (!color || !head) return;
64
+
65
+ themeMetaMedias.forEach((media) => {
66
+ const meta = ensureThemeMeta(head, media);
67
+ meta.setAttribute("content", color);
68
+ });
69
+ }
70
+
71
+ const storedTheme =
72
+ typeof localStorage !== "undefined" &&
73
+ localStorage.getItem("starlight-theme");
74
+ const theme =
75
+ storedTheme ||
76
+ (window.matchMedia("(prefers-color-scheme: light)").matches
77
+ ? "light"
78
+ : "dark");
79
+ const effectiveTheme = theme === "light" ? "light" : "dark";
80
+ document.documentElement.dataset.theme = effectiveTheme;
81
+ document.documentElement.classList.toggle(
82
+ "dark",
83
+ effectiveTheme === "dark",
84
+ );
85
+ syncThemeColor(effectiveTheme);
86
+ return {
87
+ updatePickers(theme = storedTheme || "auto") {
88
+ document
89
+ .querySelectorAll("starlight-theme-select")
90
+ .forEach((picker) => {
91
+ const select = picker.querySelector("select");
92
+ if (select) select.value = theme;
93
+ /** @type {HTMLTemplateElement | null} */
94
+ const tmpl = document.querySelector(`#theme-icons`);
95
+ const newIcon = tmpl && tmpl.content.querySelector("." + theme);
96
+ if (newIcon) {
97
+ const oldIcon = picker.querySelector("svg.label-icon");
98
+ if (oldIcon) {
99
+ oldIcon.replaceChildren(...newIcon.cloneNode(true).childNodes);
100
+ }
101
+ }
102
+ });
103
+ },
104
+ };
105
+ })();
106
+ </script>