Skip to content

Commit 8a2c522

Browse files
delucisHiDeoo
andauthored
Add a new dedicated theme showcase page (#2911)
Co-authored-by: HiDeoo <494699+HiDeoo@users.noreply.github.com>
1 parent 89bf6b1 commit 8a2c522

20 files changed

+383
-36
lines changed

‎CONTRIBUTING.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,8 @@ To add a language, you will need its BCP-47 tag and a label. See [“Adding a ne
290290

291291
## Showcase
292292

293+
### Sites
294+
293295
We love to see websites built with Starlight and share them with the community on our [showcase](https://starlight.astro.build/resources/showcase/) page.
294296
If you’ve built a documentation site with Starlight, adding it to the showcase is just a pull request away!
295297

@@ -312,3 +314,43 @@ If you’ve built a documentation site with Starlight, adding it to the showcase
312314
```
313315

314316
4. Open a pull request on GitHub to add your changes.
317+
318+
### Themes
319+
320+
Share themes for Starlight you built by adding them to our [themes](https://starlight.astro.build/resources/themes/) page. Here’s how!
321+
322+
1. Set up a development environment by following the [“Setting up a development environment”](#setting-up-a-development-environment) instructions.
323+
324+
2. Take screenshots of your theme’s light and dark modes using our demo project.
325+
326+
1. Open the [theme demo project](https://stackblitz.com/edit/github-jj1kzx5x?file=astro.config.mjs) on StackBlitz.
327+
328+
2. Install your theme using StackBlitz’s integrated terminal:
329+
330+
```sh
331+
npm i your-theme-name
332+
```
333+
334+
3. Update `astro.config.mjs` to import your theme and add it to Starlight’s `plugins` array.
335+
336+
4. Run the dev server:
337+
338+
```sh
339+
npm run dev
340+
```
341+
342+
5. Open the theme preview in a new tab and use dev tools’ responsive view to take screenshots with the screen sized to 1280×720 pixels.
343+
344+
3. Add your screenshots of the theme’s light and dark modes to the `docs/src/assets/themes/` directory. The images must:
345+
346+
- be PNG files with your theme’s name and the color variant, e.g. a theme named “Moon” would have files named `moon-light.png` and `moon-dark.png`
347+
- have dimensions of 1280×720 pixels
348+
349+
4. Add a new entry for your website in `docs/src/content/docs/resources/themes.mdx`.
350+
You can look at existing entries to see how to format this.
351+
352+
- The new entry must be appended at the end of the existing list of sites.
353+
- The `title` attribute must be the name of your theme.
354+
- The `description` attribute should briefly describe your theme’s aesthetic, inspiration, or key features.
355+
- The `href` attribute must be the URL of your theme’s website demonstrating what the theme looks like.
356+
- The `previews` attribute must be an object listing the filenames of the screenshots you added in step 3.

‎docs/src/assets/themes/black-dark.png

123 KB
Loading
111 KB
Loading
144 KB
Loading
134 KB
Loading
133 KB
Loading
137 KB
Loading

‎docs/src/assets/themes/ion-dark.png

108 KB
Loading

‎docs/src/assets/themes/ion-light.png

118 KB
Loading
148 KB
Loading
141 KB
Loading
138 KB
Loading
132 KB
Loading

‎docs/src/components/fluid-grid.astro

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
---
22
interface Props {
33
minColumnWidth?: string;
4+
gap?: string;
45
}
5-
const { minColumnWidth } = Astro.props;
6+
const { minColumnWidth, gap } = Astro.props;
67
---
78

89
<ul class="fluid-grid"><slot /></ul>
910

10-
<style define:vars={{ minColumnWidth }}>
11+
<style define:vars={{ minColumnWidth, gap }}>
1112
.fluid-grid {
1213
display: grid;
1314
grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--minColumnWidth, 11rem)), 1fr));
14-
gap: 1rem;
15+
gap: var(--gap, 1rem);
1516
list-style: none;
1617
padding: 0;
1718
}

‎docs/src/components/media-card.astro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ const El = href ? 'a' : 'span';
3434

3535
.media :global(img) {
3636
display: block;
37-
max-width: 100%;
37+
width: 100%;
3838
height: auto;
3939
aspect-ratio: 16 / 9;
4040
object-fit: cover;

‎docs/src/components/theme-grid.astro

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
---
2+
import { Icon } from '@astrojs/starlight/components';
3+
import FluidGrid from './fluid-grid.astro';
4+
import MediaCard from './media-card.astro';
5+
import ThemeImage from './theme-image.astro';
6+
7+
interface Props {
8+
labels: {
9+
/** Accessible label for the theme toggle. */
10+
legend: string;
11+
/** Accessible label for the dark color scheme variant. */
12+
dark: string;
13+
/** Accessible label for the light color scheme variant. */
14+
light: string;
15+
};
16+
themes: Array<{
17+
/** The name of this theme. */
18+
title: string;
19+
/** A short description of this theme. */
20+
description: string;
21+
/** URL for this theme’s website. Ideally should link to a demo site showing off the theme. */
22+
href: string;
23+
previews: {
24+
/** Image filename as found in `src/assets/themes/`, e.g. `ion-light.png`. */
25+
light: string;
26+
/** Image filename as found in `src/assets/themes/`, e.g. `ion-dark.png`. */
27+
dark: string;
28+
};
29+
}>;
30+
}
31+
32+
const { labels, themes } = Astro.props;
33+
---
34+
35+
<starlight-theme-preview data-theme-preview="dark">
36+
<fieldset class="not-content sl-flex">
37+
{
38+
/* `<legend>` is a pain to position, so we visually hide it and use an `aria-hidden` duplicate
39+
for the visual representation. See https://adrianroselli.com/2022/07/use-legend-and-fieldset.html */
40+
}
41+
<legend class="sr-only">{labels.legend}</legend>
42+
<strong aria-hidden="true">{labels.legend}</strong>
43+
{
44+
(['dark', 'light'] as const).map((variant) => (
45+
<label class:list={['sl-flex', variant]} for={`${variant}-input`}>
46+
<span class="radio">
47+
<Icon name="approve-check" />
48+
</span>
49+
<span>{labels[variant]}</span>
50+
<input
51+
id={`${variant}-input`}
52+
class="sr-only"
53+
name="preview-theme"
54+
value={variant}
55+
type="radio"
56+
checked={variant === 'dark'}
57+
/>
58+
</label>
59+
))
60+
}
61+
{/* If the currently active theme is light mode, default to showing the light mode previews. */}
62+
<script is:inline>
63+
// @ts-nocheck
64+
const theme = document.documentElement.dataset.theme || 'dark';
65+
const input = document.querySelector(`input[value="${theme}"]`);
66+
if (input) input.checked = true;
67+
const customEl = document.querySelector('starlight-theme-preview');
68+
if (customEl) customEl.dataset.themePreview = theme;
69+
</script>
70+
</fieldset>
71+
72+
<FluidGrid minColumnWidth="25rem" gap="clamp(1rem, calc(0.75rem + 1vw), 1.5rem)">
73+
{
74+
themes.map(
75+
async ({ title, description, href, previews }) =>
76+
previews && (
77+
<MediaCard {href}>
78+
<div class="preview-images" slot="media" aria-hidden="true">
79+
<ThemeImage src={previews.light} class="light" />
80+
<ThemeImage src={previews.dark} class="dark" />
81+
</div>
82+
<div class="meta sl-flex">
83+
<p class="title">{title}</p>
84+
<p class="description" set:html={description} />
85+
</div>
86+
</MediaCard>
87+
)
88+
)
89+
}
90+
</FluidGrid>
91+
</starlight-theme-preview>
92+
93+
<script>
94+
customElements.define(
95+
'starlight-theme-preview',
96+
class ThemePreview extends HTMLElement {
97+
constructor() {
98+
super();
99+
this.querySelector('fieldset')?.addEventListener('change', ({ target }) => {
100+
if (target instanceof HTMLInputElement) {
101+
this.dataset.themePreview = target.value;
102+
}
103+
});
104+
}
105+
}
106+
);
107+
</script>
108+
109+
<style>
110+
starlight-theme-preview {
111+
--transition-time: 0.25s;
112+
display: block;
113+
}
114+
115+
fieldset {
116+
/* Snappier transitions for the form controls. */
117+
--transition-time: 0.15s;
118+
119+
/* Stick the control bar to bottom of the nav bar on scroll. */
120+
position: sticky;
121+
top: var(--sl-nav-height);
122+
z-index: var(--sl-z-index-toc);
123+
/* Pull the bar out so it is full width on small screens. */
124+
margin-inline: calc(-1 * var(--sl-nav-pad-x));
125+
padding: var(--sl-nav-pad-y) var(--sl-nav-pad-x);
126+
/* Internal layout and styles. */
127+
gap: 1rem;
128+
align-items: center;
129+
justify-content: end;
130+
border: 1px solid var(--sl-color-hairline);
131+
font-size: var(--sl-text-sm);
132+
background-color: var(--sl-color-bg-nav);
133+
box-shadow: var(--sl-shadow-md);
134+
}
135+
@media (min-width: 50rem) and (max-width: 72rem) {
136+
/* Medium-width screens need a custom value to avoid x-axis overflow. */
137+
fieldset {
138+
margin-inline: -1rem;
139+
}
140+
}
141+
142+
label {
143+
align-items: center;
144+
gap: 0.375rem;
145+
color: var(--sl-color-gray-3);
146+
transition: color var(--transition-time) ease-in-out;
147+
}
148+
label:hover,
149+
label:has(:checked) {
150+
color: var(--sl-color-white);
151+
}
152+
153+
.radio {
154+
display: grid;
155+
place-content: center;
156+
border: 1px solid var(--sl-color-gray-5);
157+
border-radius: 100%;
158+
width: 1.5rem;
159+
height: 1.5rem;
160+
background-color: #000;
161+
color: #fff;
162+
transition: transform var(--transition-time) ease-in-out;
163+
}
164+
label:not(:has(:checked)):hover .radio {
165+
transform: scale(1.15);
166+
}
167+
.light .radio {
168+
background-color: #fff;
169+
color: #000;
170+
}
171+
172+
/* Show the input’s focus ring on the label. */
173+
label:has(:focus-visible) {
174+
outline: auto;
175+
outline-offset: 2px;
176+
}
177+
178+
/* Show check mark icon only when input is checked. */
179+
label svg {
180+
opacity: 0;
181+
transition: opacity var(--transition-time) ease-in-out;
182+
}
183+
label:has(:checked) svg {
184+
opacity: 1;
185+
}
186+
187+
/* Stack light/dark images in the same grid row and column. */
188+
.preview-images {
189+
display: grid;
190+
grid-template-columns: 1fr;
191+
}
192+
.preview-images > * {
193+
grid-area: 1 / 1;
194+
}
195+
196+
/* Fade out the preview image that isn’t currently selected. */
197+
img {
198+
transition: opacity var(--transition-time) ease-in-out;
199+
}
200+
[data-theme-preview='dark'] img.light {
201+
opacity: 0;
202+
}
203+
[data-theme-preview='light'] img.dark {
204+
opacity: 0;
205+
}
206+
207+
.meta {
208+
padding: 1rem;
209+
flex-direction: column;
210+
gap: 0.5rem;
211+
}
212+
213+
.title {
214+
font-size: var(--sl-text-lg);
215+
}
216+
217+
.description {
218+
color: var(--sl-color-gray-3);
219+
line-height: 1.5;
220+
}
221+
</style>

‎docs/src/components/theme-image.astro

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
---
2+
import { AstroError } from 'astro/errors';
3+
import { Image } from 'astro:assets';
4+
5+
interface Props {
6+
src: string;
7+
class: 'light' | 'dark';
8+
}
9+
10+
const thumbnails = import.meta.glob<{ default: ImageMetadata }>(
11+
'../assets/themes/*{.png,.jpg,.jpeg,.webp,.avif}'
12+
);
13+
14+
const thumbnail = thumbnails[`../assets/themes/${Astro.props.src}`];
15+
if (!thumbnail) {
16+
throw new Error(`Could not resolve showcase thumbnail: ${Astro.props.src}`);
17+
}
18+
const src = (await thumbnail()).default;
19+
20+
if (src.width !== 1280 || src.height !== 720) {
21+
let fileName = src.src.split('/').pop();
22+
const queryIndex = fileName?.indexOf('?');
23+
if (queryIndex !== undefined && queryIndex > -1) {
24+
fileName = fileName?.slice(0, queryIndex);
25+
}
26+
throw new AstroError(
27+
'Theme screenshots must be **1280×720px**',
28+
`Dimensions of **${src.width}×${src.height}px** found for theme image \`${fileName || src.src}\`\n\n` +
29+
`For best results:\n\n` +
30+
`1. Take a screenshot of the site using a browser resized to **1280×720px**. The responsive view in dev tools can be helpful for this.\n\n` +
31+
`2. Resize the screenshot to **1280×720px** if necessary and make sure it is saved as a PNG. An online tool like [Squoosh](https://squoosh.app/) can help here.\n\n` +
32+
`See more details in the [Starlight contributing guide](https://github.com/withastro/starlight/blob/main/CONTRIBUTING.md#themes)\n`
33+
);
34+
}
35+
---
36+
37+
<Image {src} alt="" width="960" class={Astro.props.class} />

‎docs/src/content/docs/guides/css-and-tailwind.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import { Tabs, TabItem, Steps } from '@astrojs/starlight/components';
77

88
You can style your Starlight site with custom CSS files or use the Starlight Tailwind plugin.
99

10+
For a quick way to change the default style of your site, check out [community themes](/resources/themes/).
11+
1012
## Custom CSS styles
1113

1214
Customize the styles applied to your Starlight site by providing additional CSS files to modify or extend Starlight’s default styles.

‎docs/src/content/docs/resources/plugins.mdx

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -130,38 +130,6 @@ Extend your site with official plugins supported by the Starlight team and commu
130130
/>
131131
</CardGrid>
132132

133-
### Community themes
134-
135-
A theme is a Starlight plugin that changes the visual appearance of a site with component overrides, custom CSS, or other new features.
136-
137-
<CardGrid>
138-
<LinkCard
139-
href="https://github.com/HiDeoo/starlight-theme-rapide"
140-
title="starlight-theme-rapide"
141-
description="Starlight theme inspired by the Visual Studio Code Vitesse theme."
142-
/>
143-
<LinkCard
144-
href="https://github.com/Fevol/starlight-theme-obsidian"
145-
title="starlight-theme-obsidian"
146-
description="Starlight theme inspired by the style of Obsidian Publish sites."
147-
/>
148-
<LinkCard
149-
href="https://github.com/TheOtterlord/catppuccin-starlight"
150-
title="catppuccin-starlight"
151-
description="Soothing pastel theme for Starlight"
152-
/>
153-
<LinkCard
154-
href="https://github.com/louisescher/starlight-ion-theme"
155-
title="starlight-ion-theme"
156-
description="A sleek, modern theme for Starlight."
157-
/>
158-
<LinkCard
159-
href="https://github.com/adrian-ub/starlight-theme-black"
160-
title="starlight-theme-black"
161-
description="Starlight theme inspired by shadcn docs"
162-
/>
163-
</CardGrid>
164-
165133
## Community tools and integrations
166134

167135
import { CardGrid, LinkCard } from '@astrojs/starlight/components';

0 commit comments

Comments
 (0)