starlight-theme-bejamas 0.0.0-canary.58c2686

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 ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "starlight-theme-bejamas",
3
+ "author": "Bejamas",
4
+ "version": "0.0.0-canary.58c2686",
5
+ "license": "MIT",
6
+ "description": "A Starlight theme using bejamas/ui",
7
+ "type": "module",
8
+ "exports": {
9
+ ".": "./src/index.ts",
10
+ "./config": "./src/config.ts",
11
+ "./styles/theme.css": "./src/styles/theme.css",
12
+ "./overrides/Header.astro": "./src/overrides/Header.astro",
13
+ "./overrides/PageFrame.astro": "./src/overrides/PageFrame.astro",
14
+ "./overrides/SiteTitle.astro": "./src/overrides/SiteTitle.astro",
15
+ "./overrides/Pagination.astro": "./src/overrides/Pagination.astro",
16
+ "./overrides/PageTitle.astro": "./src/overrides/PageTitle.astro",
17
+ "./overrides/Hero.astro": "./src/overrides/Hero.astro"
18
+ },
19
+ "scripts": {},
20
+ "keywords": [
21
+ "starlight",
22
+ "theme",
23
+ "bejamas",
24
+ "bejamas/ui",
25
+ "astro",
26
+ "astro-component",
27
+ "withastro"
28
+ ],
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "https://github.com/bejamas/ui.git",
32
+ "directory": "packages/starlight-theme-bejamas"
33
+ },
34
+ "bugs": "https://github.com/bejamas/ui/issues",
35
+ "peerDependencies": {
36
+ "@astrojs/starlight": ">=0.33.0",
37
+ "astro": ">=5.5.0"
38
+ },
39
+ "dependencies": {
40
+ "@expressive-code/plugin-line-numbers": "^0.41.3",
41
+ "@fontsource-variable/inter": "^5.2.8",
42
+ "@fontsource/inter": "^5.2.8",
43
+ "add": "^2.0.6",
44
+ "bun": "^1.2.22"
45
+ }
46
+ }
package/src/config.ts ADDED
@@ -0,0 +1,50 @@
1
+ import type { AstroBuiltinAttributes } from "astro";
2
+ import type { HTMLAttributes } from "astro/types";
3
+ import { z } from "astro/zod";
4
+
5
+ const linkHTMLAttributesSchema = z.record(
6
+ z.union([z.string(), z.number(), z.boolean(), z.undefined()])
7
+ ) as z.Schema<
8
+ Omit<HTMLAttributes<"a">, keyof AstroBuiltinAttributes | "children">
9
+ >;
10
+
11
+ // eslint-disable-next-line ts/explicit-function-return-type
12
+ const LinkItemHTMLAttributesSchema = () => linkHTMLAttributesSchema.default({});
13
+
14
+ const navLinkSchema = z.object({
15
+ /**
16
+ * An optional badge to display next to the topic label.
17
+ *
18
+ * This option accepts the same configuration as the Starlight badge sidebar item configuration.
19
+ * @see https://starlight.astro.build/guides/sidebar/#badges
20
+ */
21
+ badge: z.string().optional(),
22
+ /**
23
+ * The topic label visible at the top of the sidebar.
24
+ *
25
+ * The value can be a string, or for multilingual sites, an object with values for each different locale. When using
26
+ * the object form, the keys must be BCP-47 tags (e.g. en, fr, or zh-CN).
27
+ */
28
+ label: z.union([z.string(), z.record(z.string())]),
29
+ /**
30
+ * The link to the topic’s content which an be a relative link to local files or the full URL of an external page.
31
+ *
32
+ * For internal links, the link can either be a page included in the items array or a different page acting as the
33
+ * topic’s landing page.
34
+ */
35
+ href: z.string(),
36
+ /** HTML attributes to add to the link item. */
37
+ attrs: LinkItemHTMLAttributesSchema(),
38
+ });
39
+
40
+ export const StarlightThemeBejamasConfigSchema = z.object({
41
+ nav: z.array(navLinkSchema).optional(),
42
+ components: z.record(z.string()).optional(),
43
+ });
44
+
45
+ export type StarlightThemeBejamasUserConfig = z.input<
46
+ typeof StarlightThemeBejamasConfigSchema
47
+ >;
48
+ export type StarlightThemeBejamasConfig = z.output<
49
+ typeof StarlightThemeBejamasConfigSchema
50
+ >;
package/src/index.ts ADDED
@@ -0,0 +1,150 @@
1
+ import type { StarlightPlugin } from "@astrojs/starlight/types";
2
+
3
+ import { createInlineSvgUrl } from "@astrojs/starlight/expressive-code";
4
+
5
+ import {
6
+ StarlightThemeBejamasConfigSchema,
7
+ type StarlightThemeBejamasUserConfig,
8
+ } from "./config";
9
+ import { vitePluginStarlightThemeBejamas } from "./lib/vite";
10
+ import { pluginLineNumbers } from "@expressive-code/plugin-line-numbers";
11
+
12
+ export default function starlightThemeBejamas(
13
+ userConfig: StarlightThemeBejamasUserConfig,
14
+ ): StarlightPlugin {
15
+ const parsedConfig = StarlightThemeBejamasConfigSchema.safeParse(userConfig);
16
+
17
+ if (!parsedConfig.success) {
18
+ throw new Error(
19
+ `The provided plugin configuration is invalid.\n${parsedConfig.error.issues.map((issue) => issue.message).join("\n")}`,
20
+ );
21
+ }
22
+
23
+ const config = parsedConfig.data;
24
+
25
+ return {
26
+ name: "starlight-theme-bejamas-plugin",
27
+ hooks: {
28
+ "config:setup": function ({
29
+ config: starlightConfig,
30
+ logger,
31
+ updateConfig,
32
+ addIntegration,
33
+ }) {
34
+ const userExpressiveCodeConfig =
35
+ starlightConfig.expressiveCode === false ||
36
+ starlightConfig.expressiveCode === true
37
+ ? {}
38
+ : starlightConfig.expressiveCode;
39
+
40
+ console.log({ plugins: starlightConfig.plugins });
41
+
42
+ starlightConfig.plugins?.push(
43
+ pluginLineNumbers({
44
+ showLineNumbers: true,
45
+ }),
46
+ );
47
+
48
+ const componentOverrides: typeof config.components = {};
49
+
50
+ type OverridableComponent =
51
+ | "Header"
52
+ | "PageFrame"
53
+ | "SiteTitle"
54
+ | "Pagination"
55
+ | "PageTitle"
56
+ | "Hero";
57
+
58
+ const overridableComponents: OverridableComponent[] = [
59
+ "Header",
60
+ "PageFrame",
61
+ "SiteTitle",
62
+ "Pagination",
63
+ "PageTitle",
64
+ "Hero",
65
+ ];
66
+
67
+ for (const component of overridableComponents) {
68
+ if (config.components?.[component]) {
69
+ logger.warn(
70
+ `It looks like you already have a \`${component}\` component override in your Starlight configuration.`,
71
+ );
72
+ } else {
73
+ componentOverrides[component] =
74
+ `starlight-theme-bejamas/overrides/${component}.astro`;
75
+ }
76
+ }
77
+
78
+ updateConfig({
79
+ components: {
80
+ ...componentOverrides,
81
+ ...config.components,
82
+ },
83
+ customCss: [
84
+ "@fontsource/inter/400.css",
85
+ "@fontsource/inter/500.css",
86
+ "@fontsource/inter/700.css",
87
+ // "@fontsource-variable/inter",
88
+ ...(starlightConfig.customCss ?? []),
89
+ // "starlight-theme-bejamas/styles/layers",
90
+ "starlight-theme-bejamas/styles/theme.css",
91
+ // "starlight-theme-bejamas/styles/base",
92
+ ],
93
+ expressiveCode:
94
+ starlightConfig.expressiveCode === false
95
+ ? false
96
+ : {
97
+ themes: ["github-dark-default", "github-light-default"],
98
+ ...userExpressiveCodeConfig,
99
+ styleOverrides: {
100
+ codeBackground: "var(--code-background)",
101
+ borderWidth: "0px",
102
+ borderRadius: "calc(var(--radius) + 4px)",
103
+ gutterBorderWidth: "0px",
104
+ ...userExpressiveCodeConfig?.styleOverrides,
105
+ frames: {
106
+ editorBackground: "var(--code-background)",
107
+ terminalBackground: "var(--code-background)",
108
+ copyIcon: createInlineSvgUrl(
109
+ `<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-clipboard"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"></rect><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"></path></svg>`,
110
+ ),
111
+ ...userExpressiveCodeConfig?.styleOverrides?.frames,
112
+ },
113
+ textMarkers: {
114
+ markBackground: "var(--mark-background)",
115
+ markBorderColor: "var(--border)",
116
+ ...userExpressiveCodeConfig?.styleOverrides?.textMarkers,
117
+ },
118
+ },
119
+ // plugins: [
120
+ // pluginLineNumbers({
121
+ // showLineNumbers: false,
122
+ // }),
123
+ // ],
124
+ defaultProps: {
125
+ showLineNumbers: false,
126
+ overridesByLang: {
127
+ astro: {
128
+ showLineNumbers: true,
129
+ },
130
+ },
131
+ },
132
+ },
133
+ });
134
+
135
+ addIntegration({
136
+ name: "starlight-theme-bejamas-integration",
137
+ hooks: {
138
+ "astro:config:setup": ({ updateConfig }) => {
139
+ updateConfig({
140
+ vite: {
141
+ plugins: [vitePluginStarlightThemeBejamas(config)],
142
+ },
143
+ });
144
+ },
145
+ },
146
+ });
147
+ },
148
+ },
149
+ };
150
+ }
@@ -0,0 +1,52 @@
1
+ import type { ViteUserConfig } from "astro";
2
+ import type { StarlightThemeBejamasConfig } from "../config";
3
+
4
+ export function vitePluginStarlightThemeBejamas(
5
+ config: StarlightThemeBejamasConfig
6
+ ): VitePlugin {
7
+ const moduleId = "virtual:starlight-theme-bejamas/user-config";
8
+ const resolvedModuleId = `\0${moduleId}`;
9
+ const moduleContent = `export default ${JSON.stringify(config)}`;
10
+
11
+ const componentsPrefix = "virtual:starlight-theme-bejamas/components/";
12
+ const resolvedComponentsPrefix = `\0${componentsPrefix}`;
13
+
14
+ return {
15
+ name: "vite-plugin-starlight-theme-bejamas",
16
+ load(id) {
17
+ if (id === resolvedModuleId) {
18
+ return moduleContent;
19
+ }
20
+
21
+ if (id.startsWith(resolvedComponentsPrefix)) {
22
+ const componentId = id.slice(resolvedComponentsPrefix.length);
23
+ const componentName = componentId.replace(/\.astro$/, "");
24
+ const target = config.components?.[componentName];
25
+
26
+ console.log({ target });
27
+ if (!target) {
28
+ return `export default (()=>{ throw new Error(\"starlight-theme-bejamas: No component mapping found for '${componentName}'. Add it under 'components' in your starlightThemeBejamas config.\"); })()`;
29
+ }
30
+
31
+ return `export { default } from "${target}"; export * from "${target}";`;
32
+ }
33
+
34
+ return undefined;
35
+ },
36
+ resolveId(id) {
37
+ if (id === moduleId) {
38
+ return resolvedModuleId;
39
+ }
40
+
41
+ if (id.startsWith(componentsPrefix)) {
42
+ const componentId = id.slice(componentsPrefix.length);
43
+ const componentName = componentId.replace(/\.astro$/, "");
44
+ return `${resolvedComponentsPrefix}${componentName}`;
45
+ }
46
+
47
+ return undefined;
48
+ },
49
+ };
50
+ }
51
+
52
+ type VitePlugin = NonNullable<ViteUserConfig["plugins"]>[number];
@@ -0,0 +1,140 @@
1
+ ---
2
+ import config from "virtual:starlight/user-config";
3
+
4
+ import LanguageSelect from "virtual:starlight/components/LanguageSelect";
5
+ import Search from "virtual:starlight/components/Search";
6
+ import SiteTitle from "virtual:starlight/components/SiteTitle";
7
+ import SocialIcons from "virtual:starlight/components/SocialIcons";
8
+ import ThemeSelect from "virtual:starlight/components/ThemeSelect";
9
+
10
+ import options from "virtual:starlight-theme-bejamas/user-config";
11
+
12
+ import Button from "virtual:starlight-theme-bejamas/components/Button.astro";
13
+ /**
14
+ * Render the `Search` component if Pagefind is enabled or the default search component has been overridden.
15
+ */
16
+ const shouldRenderSearch =
17
+ config.pagefind ||
18
+ config.components.Search !== "@astrojs/starlight/components/Search.astro";
19
+
20
+ /**
21
+ * Render the `nav` items if they are defined.
22
+ */
23
+
24
+ const shouldRenderNav = options.nav && options.nav.length > 0;
25
+ ---
26
+
27
+ <div class="header">
28
+ <div class="sl-bejamas-header-title">
29
+ <SiteTitle />
30
+ {
31
+ shouldRenderNav && (
32
+ <nav>
33
+ <ul class="gap-1 flex items-center">
34
+ {options.nav.map((item) => (
35
+ <li>
36
+ <Button as="a" variant="ghost" href={item.href}>
37
+ {item.label}
38
+ </Button>
39
+ </li>
40
+ ))}
41
+ </ul>
42
+ </nav>
43
+ )
44
+ }
45
+ </div>
46
+ <div class="sl-flex sl-justify-end print:hidden">
47
+ {shouldRenderSearch && <Search />}
48
+ </div>
49
+ <div class="sl-hidden md:sl-flex print:hidden right-group">
50
+ <div class="sl-flex social-icons">
51
+ <SocialIcons />
52
+ </div>
53
+ <ThemeSelect />
54
+ <LanguageSelect />
55
+ </div>
56
+ </div>
57
+
58
+ <style>
59
+ @layer bejamas {
60
+ .sl-bejamas-header-title {
61
+ display: flex;
62
+ align-items: center;
63
+ gap: 1.25rem;
64
+ }
65
+ }
66
+ @layer bejamas {
67
+ .header {
68
+ display: flex;
69
+ gap: var(--sl-nav-gap);
70
+ justify-content: space-between;
71
+ align-items: center;
72
+ height: 100%;
73
+ position: relative;
74
+ z-index: 10;
75
+ }
76
+
77
+ .sl-justify-end {
78
+ justify-content: flex-end;
79
+ }
80
+
81
+ .title-wrapper {
82
+ /* Prevent long titles overflowing and covering the search and menu buttons on narrow viewports. */
83
+ overflow: clip;
84
+ /* Avoid clipping focus ring around link inside title wrapper. */
85
+ padding: 0.25rem;
86
+ margin: -0.25rem;
87
+ min-width: 0;
88
+ }
89
+
90
+ .right-group,
91
+ .social-icons {
92
+ gap: 1rem;
93
+ align-items: center;
94
+ }
95
+ .social-icons::after {
96
+ content: "";
97
+ height: 2rem;
98
+ border-inline-end: 1px solid var(--sl-color-gray-5);
99
+ }
100
+
101
+ @media (min-width: 50rem) {
102
+ :global(:root[data-has-sidebar]) {
103
+ --__sidebar-pad: calc(2 * var(--sl-nav-pad-x));
104
+ }
105
+ :global(:root:not([data-has-toc])) {
106
+ --__toc-width: 0rem;
107
+ }
108
+ .header {
109
+ --__sidebar-width: max(
110
+ 0rem,
111
+ var(--sl-content-inline-start, 0rem) - var(--sl-nav-pad-x)
112
+ );
113
+ --__main-column-fr: calc(
114
+ (
115
+ 100% + var(--__sidebar-pad, 0rem) -
116
+ var(--__toc-width, var(--sl-sidebar-width)) -
117
+ (2 * var(--__toc-width, var(--sl-nav-pad-x))) -
118
+ var(--sl-content-inline-start, 0rem) - var(--sl-content-width)
119
+ ) /
120
+ 2
121
+ );
122
+ display: grid;
123
+ grid-template-columns:
124
+ /* 1 (site title): runs up until the main content column’s left edge or the width of the title, whichever is the largest */
125
+ minmax(
126
+ calc(
127
+ var(--__sidebar-width) +
128
+ max(0rem, var(--__main-column-fr) - var(--sl-nav-gap))
129
+ ),
130
+ auto
131
+ )
132
+ /* 2 (search box): all free space that is available. */
133
+ 1fr
134
+ /* 3 (right items): use the space that these need. */
135
+ auto;
136
+ align-content: center;
137
+ }
138
+ }
139
+ }
140
+ </style>
@@ -0,0 +1,175 @@
1
+ ---
2
+ import { Image } from "astro:assets";
3
+ import Button from "virtual:starlight-theme-bejamas/components/Button.astro";
4
+
5
+ const { data } = Astro.locals.starlightRoute.entry;
6
+ const { title = data.title, tagline, image, actions = [] } = data.hero || {};
7
+
8
+ const PAGE_TITLE_ID = "_top";
9
+
10
+ const imageAttrs = {
11
+ loading: "eager" as const,
12
+ decoding: "async" as const,
13
+ width: 400,
14
+ height: 400,
15
+ alt: image?.alt || "",
16
+ };
17
+
18
+ let darkImage: ImageMetadata | undefined;
19
+ let lightImage: ImageMetadata | undefined;
20
+ let rawHtml: string | undefined;
21
+ if (image) {
22
+ if ("file" in image) {
23
+ darkImage = image.file;
24
+ } else if ("dark" in image) {
25
+ darkImage = image.dark;
26
+ lightImage = image.light;
27
+ } else {
28
+ rawHtml = image.html;
29
+ }
30
+ }
31
+
32
+ const startlightVariantToBejamasVariant = {
33
+ primary: "default",
34
+ secondary: "secondary",
35
+ minimal: "outline",
36
+ };
37
+ ---
38
+
39
+ <div class="hero">
40
+ {
41
+ darkImage && (
42
+ <Image
43
+ src={darkImage}
44
+ {...imageAttrs}
45
+ class:list={{ "light:sl-hidden": Boolean(lightImage) }}
46
+ />
47
+ )
48
+ }
49
+ {
50
+ lightImage && (
51
+ <Image src={lightImage} {...imageAttrs} class="dark:sl-hidden" />
52
+ )
53
+ }
54
+ {rawHtml && <div class="hero-html sl-flex" set:html={rawHtml} />}
55
+ <div class="sl-flex stack">
56
+ <div class="sl-flex copy">
57
+ <h1 id={PAGE_TITLE_ID} data-page-title set:html={title} />
58
+ {tagline && <div class="tagline" set:html={tagline} />}
59
+ </div>
60
+ {
61
+ actions.length > 0 && (
62
+ <div class="sl-flex actions">
63
+ {actions.map(
64
+ ({
65
+ attrs: { class: className, ...attrs } = {},
66
+ icon,
67
+ link: href,
68
+ text,
69
+ variant,
70
+ }) => (
71
+ <Button
72
+ {href}
73
+ variant={startlightVariantToBejamasVariant[variant]}
74
+ as="a"
75
+ icon={icon?.name}
76
+ class:list={[className]}
77
+ {...attrs}
78
+ >
79
+ {text}
80
+ {icon?.html && <Fragment set:html={icon.html} />}
81
+ </Button>
82
+ ),
83
+ )}
84
+ </div>
85
+ )
86
+ }
87
+ </div>
88
+ </div>
89
+
90
+ <style>
91
+ @layer starlight.core {
92
+ .hero {
93
+ display: grid;
94
+ align-items: center;
95
+ gap: 1rem;
96
+ padding-bottom: 1rem;
97
+ }
98
+
99
+ .hero > img,
100
+ .hero > .hero-html {
101
+ object-fit: contain;
102
+ width: min(70%, 20rem);
103
+ height: auto;
104
+ margin-inline: auto;
105
+ }
106
+
107
+ .stack {
108
+ flex-direction: column;
109
+ gap: clamp(1.5rem, calc(1.5rem + 1vw), 2rem);
110
+ text-align: center;
111
+ }
112
+
113
+ .copy {
114
+ flex-direction: column;
115
+ gap: 1rem;
116
+ align-items: center;
117
+ }
118
+
119
+ .copy > * {
120
+ max-width: 50ch;
121
+ }
122
+
123
+ h1 {
124
+ font-size: clamp(
125
+ var(--sl-text-3xl),
126
+ calc(0.25rem + 5vw),
127
+ var(--sl-text-6xl)
128
+ );
129
+ line-height: var(--sl-line-height-headings);
130
+ font-weight: 600;
131
+ color: var(--foreground);
132
+ }
133
+
134
+ .tagline {
135
+ font-size: clamp(
136
+ var(--sl-text-base),
137
+ calc(0.0625rem + 2vw),
138
+ var(--sl-text-xl)
139
+ );
140
+ color: var(--muted-foreground);
141
+ }
142
+
143
+ .actions {
144
+ gap: 1rem 0.75rem;
145
+ flex-wrap: wrap;
146
+ justify-content: center;
147
+ }
148
+
149
+ @media (min-width: 50rem) {
150
+ .hero {
151
+ grid-template-columns: 7fr 4fr;
152
+ gap: 3%;
153
+ padding-block: clamp(2.5rem, calc(1rem + 10vmin), 10rem);
154
+ }
155
+
156
+ .hero > img,
157
+ .hero > .hero-html {
158
+ order: 2;
159
+ width: min(100%, 25rem);
160
+ }
161
+
162
+ .stack {
163
+ text-align: start;
164
+ }
165
+
166
+ .copy {
167
+ align-items: flex-start;
168
+ }
169
+
170
+ .actions {
171
+ justify-content: flex-start;
172
+ }
173
+ }
174
+ }
175
+ </style>
@@ -0,0 +1,126 @@
1
+ ---
2
+ import MobileMenuToggle from "virtual:starlight/components/MobileMenuToggle";
3
+
4
+ const { hasSidebar } = Astro.locals.starlightRoute;
5
+ ---
6
+
7
+ <div class="page sl-flex">
8
+ <header class="header">
9
+ <slot name="header" />
10
+ <div class="fade"></div>
11
+ </header>
12
+ {
13
+ hasSidebar && (
14
+ <nav
15
+ class="sidebar print:hidden"
16
+ aria-label={Astro.locals.t("sidebarNav.accessibleLabel")}
17
+ >
18
+ <MobileMenuToggle />
19
+ <div id="starlight__sidebar" class="sidebar-pane">
20
+ <div class="sidebar-content sl-flex">
21
+ <slot name="sidebar" />
22
+ </div>
23
+ </div>
24
+ </nav>
25
+ )
26
+ }
27
+ <div class="main-frame"><slot /></div>
28
+ </div>
29
+
30
+ <style>
31
+ @layer bejamas {
32
+ .page {
33
+ flex-direction: column;
34
+ min-height: 100vh;
35
+ }
36
+
37
+ .fade {
38
+ --blur: 4px;
39
+ --stop: 50%;
40
+ --height: 120px;
41
+ position: fixed;
42
+ pointer-events: none;
43
+ width: 100%;
44
+ height: var(--height);
45
+ user-select: none;
46
+ -webkit-user-select: none;
47
+ left: 0;
48
+ -webkit-backdrop-filter: blur(var(--blur));
49
+ backdrop-filter: blur(var(--blur));
50
+
51
+ background: linear-gradient(to top, transparent, var(--background));
52
+ mask-image: linear-gradient(
53
+ to bottom,
54
+ var(--background) var(--stop),
55
+ transparent
56
+ );
57
+ top: 0;
58
+ }
59
+
60
+ .header {
61
+ z-index: var(--sl-z-index-navbar);
62
+ position: fixed;
63
+ inset-inline-start: 0;
64
+ inset-block-start: 0;
65
+ width: 100%;
66
+ height: var(--sl-nav-height);
67
+ /* border-bottom: 1px solid var(--sl-color-hairline-shade); */
68
+ padding: var(--sl-nav-pad-y) var(--sl-nav-pad-x);
69
+ padding-inline-end: var(--sl-nav-pad-x);
70
+ /* background-color: var(--sl-color-bg-nav); */
71
+ }
72
+
73
+ :global([data-has-sidebar]) .header {
74
+ padding-inline-end: calc(
75
+ var(--sl-nav-gap) + var(--sl-nav-pad-x) + var(--sl-menu-button-size)
76
+ );
77
+ }
78
+
79
+ .sidebar-pane {
80
+ visibility: var(--sl-sidebar-visibility, hidden);
81
+ position: fixed;
82
+ z-index: var(--sl-z-index-menu);
83
+ inset-block: var(--sl-nav-height) 0;
84
+ inset-inline-start: 0;
85
+ width: 100%;
86
+ background-color: var(--sl-color-black);
87
+ overflow-y: auto;
88
+ }
89
+
90
+ :global([aria-expanded="true"]) ~ .sidebar-pane {
91
+ --sl-sidebar-visibility: visible;
92
+ }
93
+
94
+ .sidebar-content {
95
+ height: 100%;
96
+ min-height: max-content;
97
+ padding: 1rem var(--sl-sidebar-pad-x) 0;
98
+ flex-direction: column;
99
+ gap: 1rem;
100
+ }
101
+
102
+ @media (min-width: 50rem) {
103
+ .sidebar-content::after {
104
+ content: "";
105
+ padding-bottom: 1px;
106
+ }
107
+ }
108
+
109
+ .main-frame {
110
+ padding-top: calc(var(--sl-nav-height) + var(--sl-mobile-toc-height));
111
+ padding-inline-start: var(--sl-content-inline-start);
112
+ }
113
+
114
+ @media (min-width: 50rem) {
115
+ :global([data-has-sidebar]) .header {
116
+ padding-inline-end: var(--sl-nav-pad-x);
117
+ }
118
+ .sidebar-pane {
119
+ --sl-sidebar-visibility: visible;
120
+ width: var(--sl-sidebar-width);
121
+ background-color: var(--sl-color-bg-sidebar);
122
+ border-inline-end: 1px solid var(--sl-color-hairline-shade);
123
+ }
124
+ }
125
+ }
126
+ </style>
@@ -0,0 +1,71 @@
1
+ ---
2
+ import Button from "virtual:starlight-theme-bejamas/components/Button.astro";
3
+ import { Icon } from "@astrojs/starlight/components";
4
+ import { ArrowLeft, ArrowRight } 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="test">{Astro.locals.starlightRoute.entry.data.title}</h1>
18
+
19
+ {
20
+ entry.data.template !== "splash" && (
21
+ <div class="sl-bejamas-docs-title-buttons">
22
+ <Button as="a" size="sm" href="#content" variant="secondary">
23
+ Copy Markdown
24
+ </Button>
25
+ {prev && (
26
+ <Button
27
+ as="a"
28
+ href={isRtl ? next.href : prev.href}
29
+ variant="secondary"
30
+ size="icon-sm"
31
+ >
32
+ <ArrowLeft size="1.5rem" />
33
+ </Button>
34
+ )}
35
+ {next && (
36
+ <Button
37
+ as="a"
38
+ href={isRtl ? prev.href : next.href}
39
+ variant="secondary"
40
+ size="icon-sm"
41
+ >
42
+ <ArrowRight size="1.5rem" />
43
+ </Button>
44
+ )}
45
+ </div>
46
+ )
47
+ }
48
+ </div>
49
+
50
+ <style>
51
+ @layer bejamas {
52
+ .sl-bejamas-docs-title {
53
+ display: flex;
54
+ justify-content: space-between;
55
+ margin-top: 0.5rem;
56
+ }
57
+ .sl-bejamas-docs-title-buttons {
58
+ display: flex;
59
+ gap: 0.5rem;
60
+ }
61
+ }
62
+
63
+ @layer starlight.core {
64
+ h1 {
65
+ font-size: var(--sl-text-h1);
66
+ line-height: var(--sl-line-height-headings);
67
+ font-weight: 500;
68
+ color: var(--sl-color-white);
69
+ }
70
+ }
71
+ </style>
@@ -0,0 +1,47 @@
1
+ ---
2
+ import { Icon } from "@astrojs/starlight/components";
3
+ import Button from "virtual:starlight-theme-bejamas/components/Button.astro";
4
+
5
+ const { dir, pagination } = Astro.locals.starlightRoute;
6
+ const { prev, next } = pagination;
7
+ const isRtl = dir === "rtl";
8
+ ---
9
+
10
+ <div class="flex justify-between print:hidden" dir={dir}>
11
+ {
12
+ prev && (
13
+ <Button as="a" href={prev.href} rel="prev" variant="outline">
14
+ <Icon name={isRtl ? "right-arrow" : "left-arrow"} size="1.25rem" />
15
+ {prev.label}
16
+ </Button>
17
+ )
18
+ }
19
+ {
20
+ next && (
21
+ <Button as="a" href={next.href} rel="next" variant="outline">
22
+ <Icon name={isRtl ? "left-arrow" : "right-arrow"} size="1.25rem" />
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
+ </style>
@@ -0,0 +1,15 @@
1
+ ---
2
+ import MobileMenuFooter from 'virtual:starlight/components/MobileMenuFooter';
3
+ import SidebarPersister from './SidebarPersister.astro';
4
+ import SidebarSublist from './SidebarSublist.astro';
5
+
6
+ const { sidebar } = Astro.locals.starlightRoute;
7
+ ---
8
+
9
+ <SidebarPersister>
10
+ <SidebarSublist sublist={sidebar} />
11
+ </SidebarPersister>
12
+
13
+ <div class="md:sl-hidden">
14
+ <MobileMenuFooter />
15
+ </div>
@@ -0,0 +1,150 @@
1
+ ---
2
+ import { flattenSidebar } from '../utils/navigation';
3
+ import type { SidebarEntry } from '../utils/routing/types';
4
+ import Icon from '../user-components/Icon.astro';
5
+ import Badge from '../user-components/Badge.astro';
6
+ import SidebarRestorePoint from './SidebarRestorePoint.astro';
7
+
8
+ interface Props {
9
+ sublist: SidebarEntry[];
10
+ nested?: boolean;
11
+ }
12
+
13
+ const { sublist, nested } = Astro.props;
14
+ ---
15
+
16
+ <ul class:list={{ 'top-level': !nested }}>
17
+ asdas
18
+ {
19
+ sublist.map((entry) => (
20
+ <li>
21
+ {entry.type === 'link' ? (
22
+ <a
23
+ href={entry.href}
24
+ aria-current={entry.isCurrent && 'page'}
25
+ class:list={[{ large: !nested }, entry.attrs.class]}
26
+ {...entry.attrs}
27
+ >
28
+ <span>{entry.label}</span>
29
+ {entry.badge && (
30
+ <Badge
31
+ variant={entry.badge.variant}
32
+ class={entry.badge.class}
33
+ text={entry.badge.text}
34
+ />
35
+ )}
36
+ </a>
37
+ ) : (
38
+ <details
39
+ open={flattenSidebar(entry.entries).some((i) => i.isCurrent) || !entry.collapsed}
40
+ >
41
+ <SidebarRestorePoint />
42
+ <summary>
43
+ <div class="group-label">
44
+ <span class="large">{entry.label}</span>
45
+ {entry.badge && (
46
+ <Badge
47
+ variant={entry.badge.variant}
48
+ class={entry.badge.class}
49
+ text={entry.badge.text}
50
+ />
51
+ )}
52
+ </div>
53
+ <Icon name="right-caret" class="caret" size="1.25rem" />
54
+ </summary>
55
+ <Astro.self sublist={entry.entries} nested />
56
+ </details>
57
+ )}
58
+ </li>
59
+ ))
60
+ }
61
+ </ul>
62
+
63
+ <style>
64
+ @layer starlight.core {
65
+ ul {
66
+ --sl-sidebar-item-padding-inline: 0.5rem;
67
+ list-style: none;
68
+ padding: 0;
69
+ }
70
+
71
+ li {
72
+ overflow-wrap: anywhere;
73
+ }
74
+
75
+
76
+ .large {
77
+ font-size: var(--sl-text-lg);
78
+ font-weight: 600;
79
+ color: var(--sl-color-white);
80
+ }
81
+
82
+ .top-level > li + li {
83
+ margin-top: 0.75rem;
84
+ }
85
+
86
+ summary {
87
+ display: flex;
88
+ align-items: center;
89
+ justify-content: space-between;
90
+ padding: 0.2em var(--sl-sidebar-item-padding-inline);
91
+ line-height: 1.4;
92
+ cursor: pointer;
93
+ user-select: none;
94
+ }
95
+ summary::marker,
96
+ summary::-webkit-details-marker {
97
+ display: none;
98
+ }
99
+
100
+ .caret {
101
+ transition: transform 0.2s ease-in-out;
102
+ flex-shrink: 0;
103
+ }
104
+ :global([dir='rtl']) .caret {
105
+ transform: rotateZ(180deg);
106
+ }
107
+ [open] > summary .caret {
108
+ transform: rotateZ(90deg);
109
+ }
110
+
111
+ a {
112
+ display: block;
113
+ border-radius: 0.25rem;
114
+ text-decoration: none;
115
+ color: var(--sl-color-gray-2);
116
+ padding: 0.3em var(--sl-sidebar-item-padding-inline);
117
+ line-height: 1.4;
118
+ }
119
+
120
+ a:hover,
121
+ a:focus {
122
+ color: var(--sl-color-white);
123
+ }
124
+
125
+ [aria-current='page'],
126
+ [aria-current='page']:hover,
127
+ [aria-current='page']:focus {
128
+ font-weight: 600;
129
+ color: var(--sl-color-text-invert);
130
+ background-color: var(--sl-color-text-accent);
131
+ }
132
+
133
+ a > *:not(:last-child),
134
+ .group-label > *:not(:last-child) {
135
+ margin-inline-end: 0.25em;
136
+ }
137
+
138
+ @media (min-width: 50rem) {
139
+ .top-level > li + li {
140
+ margin-top: 0.5rem;
141
+ }
142
+ .large {
143
+ font-size: var(--sl-text-base);
144
+ }
145
+ a {
146
+ font-size: var(--sl-text-sm);
147
+ }
148
+ }
149
+ }
150
+ </style>
@@ -0,0 +1,57 @@
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={{ 'light:sl-hidden print:hidden': !('src' in config.logo) }}
13
+ alt={config.logo.alt}
14
+ src={logos.dark.src}
15
+ width={logos.dark.width}
16
+ height={logos.dark.height}
17
+ />
18
+ {/* Show light alternate if a user configure both light and dark logos. */}
19
+ {!('src' in config.logo) && (
20
+ <img
21
+ class="dark:sl-hidden print:block"
22
+ alt={config.logo.alt}
23
+ src={logos.light?.src}
24
+ width={logos.light?.width}
25
+ height={logos.light?.height}
26
+ />
27
+ )}
28
+ </>
29
+ )
30
+ }
31
+ <span class:list={{ 'sr-only': config.logo?.replacesTitle }} translate="no">
32
+ {siteTitle}
33
+ </span>
34
+ </a>
35
+
36
+ <style>
37
+ @layer starlight.core {
38
+ .site-title {
39
+ align-items: center;
40
+ gap: var(--sl-nav-gap);
41
+ font-weight: 600;
42
+ text-decoration: none;
43
+ white-space: nowrap;
44
+ min-width: 0;
45
+ }
46
+ span {
47
+ overflow: hidden;
48
+ }
49
+ img {
50
+ height: calc(var(--sl-nav-height) - 2 * var(--sl-nav-pad-y));
51
+ width: auto;
52
+ max-width: 100%;
53
+ object-fit: contain;
54
+ object-position: 0 50%;
55
+ }
56
+ }
57
+ </style>
@@ -0,0 +1,292 @@
1
+ @layer starlight, bejamas;
2
+
3
+ @import "@fontsource-variable/inter/index.css";
4
+
5
+ @layer bejamas {
6
+ :root {
7
+ --sl-font: "Inter Variable", sans-serif;
8
+ --sl-font-mono: "Geist Mono", monospace;
9
+
10
+ --radius: 0.5rem;
11
+
12
+ --sl-color-text-accent: var(--accent-foreground);
13
+
14
+ --sl-color-banner-text: var(--foreground);
15
+
16
+ --sl-color-bg-inline-code: var(--muted);
17
+
18
+ --sl-color-hairline-shade: var(--border);
19
+
20
+ --sl-color-bg-sidebar: var(--background);
21
+
22
+ --sl-color-gray-5: var(--input);
23
+ --sl-color-black: transparent;
24
+
25
+ --code-background: var(--muted);
26
+ --sl-color-text: var(--foreground);
27
+
28
+ --sl-text-h1: 2.25rem;
29
+ --sl-text-h2: var(--text-xl);
30
+ --sl-text-h3: var(--text-lg);
31
+
32
+ --sl-color-gray-2: var(--input);
33
+
34
+ --default-font-family: "Inter Variable", sans-serif;
35
+ --font-sans: "Inter Variable", sans-serif;
36
+
37
+ --sl-color-bg: var(--background);
38
+ --sl-color-fg: var(--foreground);
39
+ --sl-color-primary: var(--primary);
40
+ --sl-color-primary-fg: var(--primary-foreground);
41
+
42
+ --sl-color-hairline: var(--border);
43
+ --sl-color-bg-nav: var(--background);
44
+
45
+ --sl-color-text-accent: var(--primary);
46
+ --sl-color-accent: var(--primary);
47
+ }
48
+ }
49
+
50
+ @layer bejamas {
51
+ .sidebar-pane .sidebar-content,
52
+ .right-sidebar-panel {
53
+ --sl-sidebar-item-padding-inline: 1rem;
54
+ padding-top: 3rem;
55
+ }
56
+
57
+ .right-sidebar,
58
+ .sidebar .sidebar-pane {
59
+ border: 0;
60
+ }
61
+
62
+ .sidebar-content a {
63
+ background-color: var(--background);
64
+ padding: 0;
65
+ }
66
+
67
+ .group-label > span {
68
+ color: var(--muted-foreground);
69
+ font-size: 0.875rem;
70
+ font-weight: 500;
71
+ }
72
+
73
+ .group-label + svg {
74
+ color: var(--muted-foreground);
75
+ font-size: 1rem;
76
+ }
77
+
78
+ .sidebar-content a > span {
79
+ color: var(--foreground);
80
+ padding-inline: 0.5rem;
81
+ padding-block: 0.375rem;
82
+ border-radius: 0.5rem;
83
+ /* transition-property: color, background-color;
84
+ transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
85
+ transition-duration: 0.05s; */
86
+ display: inline-block;
87
+ }
88
+
89
+ .sidebar-content a[aria-current="page"] > span {
90
+ background-color: var(--secondary);
91
+ color: var(--secondary-foreground);
92
+ font-weight: 500;
93
+ }
94
+
95
+ .sidebar-content a:not([aria-current="page"]):hover > span {
96
+ background-color: var(--secondary);
97
+ color: var(--secondary-foreground);
98
+ }
99
+
100
+ .sidebar-content ul ul li {
101
+ margin-inline-start: 0;
102
+ border-inline-start: 0;
103
+ padding-inline-start: 0;
104
+ margin-bottom: 1px;
105
+ }
106
+
107
+ .right-sidebar-container {
108
+ max-width: 20rem;
109
+ margin-right: 0;
110
+ margin-left: auto;
111
+ }
112
+
113
+ .content-panel {
114
+ border-top: 0;
115
+ }
116
+
117
+ .content-panel:first-child {
118
+ padding-bottom: 0;
119
+ padding-top: 2.5rem;
120
+ }
121
+
122
+ .right-sidebar-panel h2 {
123
+ color: var(--color-muted-foreground);
124
+ font-size: 0.875rem;
125
+ font-weight: 500;
126
+ padding-inline: 0.5rem;
127
+ }
128
+
129
+ site-search button {
130
+ --tw-shadow: var(--shadow-sm);
131
+ box-shadow:
132
+ var(--tw-inset-shadow), var(--tw-inset-ring-shadow),
133
+ var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
134
+ border-radius: calc(var(--radius) - 2px);
135
+ }
136
+
137
+ .expressive-code .frame {
138
+ box-shadow: none;
139
+ }
140
+
141
+ ul.top-level li details ul li:last-child {
142
+ margin-bottom: 1.25rem;
143
+ }
144
+
145
+ ul.top-level summary {
146
+ margin-bottom: 0.25rem;
147
+ }
148
+
149
+ starlight-tabs[data-sync-key="pkg"] .tablist-wrapper {
150
+ background-color: var(--code-background);
151
+ border-bottom: 1px solid var(--color-border);
152
+
153
+ padding-top: 0.5rem;
154
+ border-top-left-radius: 0.5rem;
155
+ border-top-right-radius: 0.5rem;
156
+ font-family: var(--sl-font-mono);
157
+ font-size: 0.875rem;
158
+ }
159
+
160
+ starlight-tabs[data-sync-key="pkg"] .tablist-wrapper ul {
161
+ padding-bottom: 0.5rem;
162
+ padding-inline: 0.75rem;
163
+ border-bottom: 0;
164
+ gap: 0;
165
+ }
166
+
167
+ starlight-tabs[data-sync-key="pkg"] .tablist-wrapper a {
168
+ border-bottom: 0;
169
+ padding-block: 0.5rem;
170
+ padding-inline: 0.75rem;
171
+ }
172
+
173
+ starlight-tabs[data-sync-key="pkg"] .tablist-wrapper a[aria-selected="true"] {
174
+ background-color: var(--color-accent);
175
+ color: var(--color-accent-foreground);
176
+ border-bottom: 0;
177
+ border-radius: 0.5rem;
178
+ }
179
+
180
+ starlight-tabs[data-sync-key="pkg"] .tablist-wrapper ~ div[role="tabpanel"] {
181
+ margin-top: 0;
182
+ }
183
+
184
+ starlight-tabs[data-sync-key="pkg"] figcaption.header {
185
+ display: none;
186
+ }
187
+
188
+ .tablist-wrapper > ul {
189
+ border: 0;
190
+ gap: 1.25rem;
191
+ }
192
+
193
+ .tablist-wrapper > ul a {
194
+ padding-inline: 0;
195
+ }
196
+
197
+ .sl-heading-wrapper.level-h2 {
198
+ margin-top: 4rem;
199
+ }
200
+
201
+ .sl-heading-wrapper.level-h3 {
202
+ margin-top: 3rem;
203
+ }
204
+
205
+ .sl-markdown-content a:not(:where(.not-content *)) {
206
+ text-decoration: underline;
207
+ text-underline-offset: 4px;
208
+ }
209
+
210
+ button[data-open-modal] {
211
+ border-color: var(--border);
212
+ color: var(--foreground);
213
+ box-shadow: var(--shadow-xs);
214
+ background-color: var(--background);
215
+ height: 2.375rem;
216
+ }
217
+
218
+ button[data-open-modal] kbd {
219
+ background-color: var(--muted);
220
+ color: var(--muted-foreground);
221
+ border-radius: calc(var(--radius) - 4px);
222
+ gap: 0;
223
+ }
224
+
225
+ .tablist-wrapper ~ [role="tabpanel"] {
226
+ margin-top: 0.5rem;
227
+ }
228
+
229
+ .expressive-code .title {
230
+ background-color: var(--color-muted);
231
+ color: var(--color-muted-foreground);
232
+ border-color: var(--color-border);
233
+
234
+ font-family: var(--sl-font-mono);
235
+ width: 100%;
236
+ border-bottom: 1px solid var(--color-border);
237
+ font-size: 0.875rem;
238
+ padding-block: 0.75rem;
239
+ }
240
+
241
+ .sl-markdown-content :is(h1, h2, h3, h4, h5, h6):not(:where(.not-content *)) {
242
+ font-weight: 500;
243
+ }
244
+
245
+ .sl-bejamas-component-preview {
246
+ min-height: 450px;
247
+ padding: 1rem;
248
+ border: 1px solid var(--border);
249
+ border-radius: 0.5rem;
250
+ display: flex;
251
+ justify-content: center;
252
+ align-items: center;
253
+ background-color: var(--background);
254
+ }
255
+
256
+ .sl-markdown-content ul:not(:where(.not-content *)) {
257
+ list-style: disc;
258
+ padding-left: 1rem;
259
+ }
260
+
261
+ .sl-markdown-content ol:not(:where(.not-content *)) {
262
+ list-style: decimal;
263
+ padding-left: 1rem;
264
+ }
265
+
266
+ .tablist-wrapper {
267
+ overflow-x: inherit;
268
+ }
269
+
270
+ .tablist-wrapper a[role="tab"] {
271
+ border-bottom: 0;
272
+ padding: 0;
273
+ display: inline-block;
274
+ }
275
+
276
+ .sl-markdown-content :is(th, td):not(:where(.not-content *)) {
277
+ min-width: 10rem;
278
+ padding-inline: 1rem;
279
+ border-color: var(--border);
280
+ }
281
+
282
+ .sl-markdown-content
283
+ :is(tr):last-child
284
+ :is(th, td):not(:where(.not-content *)) {
285
+ border-bottom: 0;
286
+ }
287
+
288
+ .sl-markdown-content table:not(:where(.not-content *)) {
289
+ border: 1px solid var(--border);
290
+ border-radius: calc(var(--radius) - 2px);
291
+ }
292
+ }