@astrojs/starlight 0.7.3 → 0.8.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # @astrojs/starlight
2
2
 
3
+ ## 0.8.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#612](https://github.com/withastro/starlight/pull/612) [`1b367e3`](https://github.com/withastro/starlight/commit/1b367e3f65e3736b5f91c9853a487f7f5d174a6f) Thanks [@KubaJastrz](https://github.com/KubaJastrz)! - Avoid applying hovered `<select>` text color to its `<options>`
8
+
9
+ ## 0.8.0
10
+
11
+ ### Minor Changes
12
+
13
+ - [#529](https://github.com/withastro/starlight/pull/529) [`c2d0e7f`](https://github.com/withastro/starlight/commit/c2d0e7f2699e60a48a3a9074eee6439dee8624a1) Thanks [@delucis](https://github.com/delucis)! - For improved compatibility with Tailwind, some Starlight built-in class names are now prefixed with `"sl-"`.
14
+
15
+ While not likely, if you were relying on one of these internal class names in your own components or custom CSS, you will need to update to use the prefixed version.
16
+
17
+ - **Before:** `flex`, `md:flex`, `lg:flex`, `block`, `md:block`, `lg:block`, `hidden`, `md:hidden`, `lg:hidden`.
18
+ - **After:** `sl-flex`, `md:sl-flex`, `lg:sl-flex`, `sl-block`, `md:sl-block`, `lg:sl-block`, `sl-hidden`, `md:sl-hidden`, `lg:sl-hidden`.
19
+
20
+ - [#593](https://github.com/withastro/starlight/pull/593) [`5b8af95`](https://github.com/withastro/starlight/commit/5b8af95049781954eabc3895027218b3de8ff054) Thanks [@delucis](https://github.com/delucis)! - Add announcement banner feature
21
+
22
+ - [#516](https://github.com/withastro/starlight/pull/516) [`70a32a1`](https://github.com/withastro/starlight/commit/70a32a1736c776febb34cf0ca3014f375ff9fec8) Thanks [@kevinzunigacuellar](https://github.com/kevinzunigacuellar)! - Support adding badges to sidebar links from config file and frontmatter
23
+
24
+ ### Patch Changes
25
+
26
+ - [#569](https://github.com/withastro/starlight/pull/569) [`a7691f8`](https://github.com/withastro/starlight/commit/a7691f82fdabb1c4e6b14bcfa8289aaceb929997) Thanks [@HiDeoo](https://github.com/HiDeoo)! - Use locale when sorting autogenerated sidebar entries
27
+
28
+ - [#559](https://github.com/withastro/starlight/pull/559) [`5726353`](https://github.com/withastro/starlight/commit/5726353f1a4815df9ab5a7acd7aea6af53383adc) Thanks [@delucis](https://github.com/delucis)! - Add Stack Overflow icon
29
+
3
30
  ## 0.7.3
4
31
 
5
32
  ### Patch Changes
package/README.md CHANGED
@@ -1,15 +1,17 @@
1
1
  # <img src="https://github.com/withastro/starlight/assets/357379/494fcd83-42aa-4891-87e0-87402fa0b6f3" alt="" align="left" width="40" height="40"> Starlight
2
2
 
3
- > **Warning**
4
- > This tool is an early release and very much still work in progress!
5
- > [Open an issue on GitHub](https://github.com/withastro/starlight/issues/new/choose) if you find something that’s not working.
6
-
7
3
  Starlight is a documentation website framework for [Astro][astro].
8
4
 
9
5
  ## Documentation
10
6
 
11
7
  [Read the Starlight docs][docs] (they’re built with Starlight!)
12
8
 
9
+ ## Support
10
+
11
+ Get help in the [Astro Discord][discord]. Post questions in our `#support` forum with the “starlight” tag, or visit our dedicated `#starlight` channel to discuss current development and more!
12
+
13
+ You can also submit bug reports and feature requests as [GitHub issues][issues].
14
+
13
15
  ## Contributing
14
16
 
15
17
  Join us as a Starlight contributor! These links will help you get started:
@@ -17,6 +19,7 @@ Join us as a Starlight contributor! These links will help you get started:
17
19
  - [Contributor Manual][contributing]
18
20
  - [Code of Conduct][coc]
19
21
  - [Community Guide][community]
22
+ - [Join the `#starlight` channel on Discord][discord]
20
23
 
21
24
  ## License
22
25
 
@@ -30,3 +33,5 @@ Copyright (c) 2023–present [Starlight contributors][contributors]
30
33
  [coc]: https://github.com/withastro/.github/blob/main/CODE_OF_CONDUCT.md
31
34
  [community]: https://github.com/withastro/.github/blob/main/COMMUNITY_GUIDE.md
32
35
  [contributors]: https://github.com/withastro/starlight/graphs/contributors
36
+ [discord]: https://astro.build/chat/
37
+ [issues]: https://github.com/withastro/starlight/issues
@@ -0,0 +1,86 @@
1
+ ---
2
+ import type { Badge } from '../schemas/badge';
3
+
4
+ interface Props {
5
+ variant?: Badge['variant'] | 'outline';
6
+ text?: string;
7
+ }
8
+ const { variant = 'default', text } = Astro.props;
9
+ ---
10
+
11
+ <span class:list={['sl-badge', variant]} set:html={text} />
12
+
13
+ <style>
14
+ .sl-badge {
15
+ display: inline-block;
16
+ border: 1px solid var(--sl-color-border-badge);
17
+ border-radius: 0.25rem;
18
+ font-family: var(--sl-font-system-mono);
19
+ font-size: var(--sl-text-xs);
20
+ font-weight: 400;
21
+ padding: 0.125rem 0.375rem;
22
+ line-height: 1;
23
+ color: #fff;
24
+ background-color: var(--sl-color-bg-badge);
25
+ }
26
+
27
+ .outline {
28
+ --sl-color-bg-badge: transparent;
29
+ --sl-color-border-badge: currentColor;
30
+ color: inherit;
31
+ }
32
+
33
+ .default {
34
+ --sl-color-bg-badge: var(--sl-color-accent-low);
35
+ --sl-color-border-badge: var(--sl-color-accent);
36
+ }
37
+
38
+ .note {
39
+ --sl-color-bg-badge: var(--sl-color-blue-low);
40
+ --sl-color-border-badge: var(--sl-color-blue);
41
+ }
42
+
43
+ .danger {
44
+ --sl-color-bg-badge: var(--sl-color-red-low);
45
+ --sl-color-border-badge: var(--sl-color-red);
46
+ }
47
+
48
+ .success {
49
+ --sl-color-bg-badge: var(--sl-color-green-low);
50
+ --sl-color-border-badge: var(--sl-color-green);
51
+ }
52
+
53
+ .caution {
54
+ --sl-color-bg-badge: var(--sl-color-orange-low);
55
+ --sl-color-border-badge: var(--sl-color-orange);
56
+ }
57
+
58
+ .tip {
59
+ --sl-color-bg-badge: var(--sl-color-purple-low);
60
+ --sl-color-border-badge: var(--sl-color-purple);
61
+ }
62
+
63
+ :global([data-theme='light']) .default {
64
+ --sl-color-bg-badge: var(--sl-color-accent-high);
65
+ }
66
+
67
+ :global([data-theme='light']) .note {
68
+ --sl-color-bg-badge: var(--sl-color-blue-high);
69
+ }
70
+
71
+ :global([data-theme='light']) .danger {
72
+ --sl-color-bg-badge: var(--sl-color-red-high);
73
+ }
74
+
75
+ :global([data-theme='light']) .success {
76
+ --sl-color-bg-badge: var(--sl-color-green-high);
77
+ }
78
+
79
+ :global([data-theme='light']) .caution {
80
+ --sl-color-bg-badge: var(--sl-color-orange-high);
81
+ }
82
+
83
+ :global([data-theme='light']) .tip {
84
+ --sl-color-bg-badge: var(--sl-color-purple-high);
85
+ }
86
+ </style>
@@ -0,0 +1,23 @@
1
+ ---
2
+ interface Props {
3
+ content: string;
4
+ }
5
+ ---
6
+
7
+ <div class="sl-banner" role="banner" set:html={Astro.props.content} />
8
+
9
+ <style>
10
+ .sl-banner {
11
+ --__sl-banner-text: var(--sl-color-banner-text, var(--sl-color-text-invert));
12
+ padding: var(--sl-nav-pad-y) var(--sl-nav-pad-x);
13
+ background-color: var(--sl-color-banner-bg, var(--sl-color-bg-accent));
14
+ color: var(--__sl-banner-text);
15
+ line-height: var(--sl-line-height-headings);
16
+ text-align: center;
17
+ text-wrap: balance;
18
+ box-shadow: var(--sl-shadow-sm);
19
+ }
20
+ .sl-banner :global(a) {
21
+ color: var(--__sl-banner-text);
22
+ }
23
+ </style>
@@ -11,7 +11,7 @@ interface Props {
11
11
  const { link, variant, icon } = Astro.props;
12
12
  ---
13
13
 
14
- <a class:list={['flex action', variant]} href={link}>
14
+ <a class:list={['sl-flex action', variant]} href={link}>
15
15
  <slot />
16
16
  {icon?.type === 'icon' && <Icon name={icon.name} size="1.5rem" />}
17
17
  {icon?.type === 'raw' && <Fragment set:html={icon.html} />}
@@ -1,5 +1,5 @@
1
1
  <div class="content-panel">
2
- <div class="container"><slot /></div>
2
+ <div class="sl-container"><slot /></div>
3
3
  </div>
4
4
 
5
5
  <style>
@@ -9,16 +9,16 @@
9
9
  .content-panel + .content-panel {
10
10
  border-top: 1px solid var(--sl-color-hairline);
11
11
  }
12
- .container {
12
+ .sl-container {
13
13
  max-width: var(--sl-content-width);
14
14
  }
15
15
 
16
- .container > :global(* + *) {
16
+ .sl-container > :global(* + *) {
17
17
  margin-top: 1.5rem;
18
18
  }
19
19
 
20
20
  @media (min-width: 72rem) {
21
- .container {
21
+ .sl-container {
22
22
  margin-inline: var(--sl-content-margin-inline, auto);
23
23
  }
24
24
  }
@@ -26,7 +26,7 @@ const url =
26
26
 
27
27
  {
28
28
  editUrl !== false && url && (
29
- <a href={url} class="flex">
29
+ <a href={url} class="sl-flex">
30
30
  <Icon name="pencil" size="1.2em" />
31
31
  {t('page.editLink')}
32
32
  </a>
@@ -9,7 +9,7 @@ interface Props {
9
9
  const t = useTranslations(Astro.props.locale);
10
10
  ---
11
11
 
12
- <p class="flex">
12
+ <p class="sl-flex">
13
13
  <Icon name={'warning'} size="1.5em" color="var(--sl-color-orange-high)" /><span
14
14
  >{t('i18n.untranslatedContent')}</span
15
15
  >
@@ -21,7 +21,7 @@ const prevNextLinks = getPrevNextLinks(sidebar, config.pagination, {
21
21
  ---
22
22
 
23
23
  <footer>
24
- <div class="meta flex">
24
+ <div class="meta sl-flex">
25
25
  {config.editLink.baseUrl && <EditLink data={entry.data} id={entry.id} {locale} />}
26
26
  {
27
27
  (entry.data.lastUpdated ?? config.lastUpdated) && (
@@ -12,10 +12,10 @@ interface Props {
12
12
  const { locale } = Astro.props;
13
13
  ---
14
14
 
15
- <div class="header flex">
15
+ <div class="header sl-flex">
16
16
  <SiteTitle {locale} />
17
17
  <Search {locale} />
18
- <div class="hidden md:flex right-group">
18
+ <div class="sl-hidden md:sl-flex right-group">
19
19
  <SocialIcons />
20
20
  <ThemeSelect {locale} />
21
21
  <LanguageSelect {locale} />
@@ -28,17 +28,17 @@ const imageAttrs = {
28
28
  <Image src={image.file} {...imageAttrs} />
29
29
  )
30
30
  ) : (
31
- image?.html && <div class="hero-html flex" set:html={image.html} />
31
+ image?.html && <div class="hero-html sl-flex" set:html={image.html} />
32
32
  )
33
33
  }
34
- <div class="flex stack">
35
- <div class="flex copy">
34
+ <div class="sl-flex stack">
35
+ <div class="sl-flex copy">
36
36
  <h1 id="_top" data-page-title set:html={title} />
37
37
  {tagline && <div class="tagline" set:html={tagline} />}
38
38
  </div>
39
39
  {
40
40
  actions.length > 0 && (
41
- <div class="flex actions">
41
+ <div class="sl-flex actions">
42
42
  {actions.map(({ text, ...attrs }) => (
43
43
  <CallToAction {...attrs} set:html={text} />
44
44
  ))}
@@ -84,4 +84,6 @@ export const Icons = {
84
84
  '<path d="M13.78 7.2a3.63 3.63 0 1 0-4.3-3.68h1.78a2.52 2.52 0 0 1 2.52 2.53V7.2zM7.34 18.8h3.92a2.52 2.52 0 0 0 2.52-2.52V8.37h4.17c.58.01 1.04.5 1.03 1.07v6.45a6.3 6.3 0 0 1-6.14 6.43 6.3 6.3 0 0 1-5.5-3.52zm16.1-14.06a2.51 2.51 0 1 1-5.02 0 2.51 2.51 0 0 1 5.02 0zm-3.36 14.24h-.17c.4-1 .59-2.05.57-3.11V9.46c0-.38-.07-.75-.23-1.09h2.69c.58 0 1.06.48 1.06 1.06v5.65a3.9 3.9 0 0 1-3.9 3.9h-.02z"/><path d="M1.02 5.02h10.24c.56 0 1.02.46 1.02 1.03v10.23a1.02 1.02 0 0 1-1.02 1.02H1.02A1.02 1.02 0 0 1 0 16.28V6.04c0-.56.46-1.02 1.02-1.02zm7.81 3.9V7.84H3.45v1.08h2.03v5.57h1.3V8.92h2.05z"/>',
85
85
  instagram:
86
86
  '<path d="M17.3 5.5a1.2 1.2 0 1 0 1.2 1.2 1.2 1.2 0 0 0-1.2-1.2ZM22 7.9a7.6 7.6 0 0 0-.4-2.5 5 5 0 0 0-1.2-1.7 4.7 4.7 0 0 0-1.8-1.2 7.3 7.3 0 0 0-2.4-.4L12 2H7.9a7.3 7.3 0 0 0-2.5.5 4.8 4.8 0 0 0-1.7 1.2 4.7 4.7 0 0 0-1.2 1.8 7.3 7.3 0 0 0-.4 2.4L2 12v4.1a7.3 7.3 0 0 0 .5 2.4 4.7 4.7 0 0 0 1.2 1.8 4.8 4.8 0 0 0 1.8 1.2 7.3 7.3 0 0 0 2.4.4l4.1.1h4.1a7.3 7.3 0 0 0 2.4-.5 4.7 4.7 0 0 0 1.8-1.2 4.8 4.8 0 0 0 1.2-1.7 7.6 7.6 0 0 0 .4-2.5L22 12V7.9ZM20.1 16a5.6 5.6 0 0 1-.3 1.9A3 3 0 0 1 19 19a3.2 3.2 0 0 1-1.1.8 5.6 5.6 0 0 1-1.9.3H8a5.7 5.7 0 0 1-1.9-.3A3.3 3.3 0 0 1 5 19a3 3 0 0 1-.7-1.1 5.5 5.5 0 0 1-.4-1.9l-.1-4V8a5.5 5.5 0 0 1 .4-1.9A3 3 0 0 1 5 5a3.1 3.1 0 0 1 1.1-.8A5.7 5.7 0 0 1 8 3.9l4-.1h4a5.6 5.6 0 0 1 1.9.4A3 3 0 0 1 19 5a3 3 0 0 1 .7 1.1A5.6 5.6 0 0 1 20 8l.1 4v4ZM12 6.9a5.1 5.1 0 1 0 5.1 5.1A5.1 5.1 0 0 0 12 6.9Zm0 8.4a3.3 3.3 0 1 1 3.3-3.3 3.3 3.3 0 0 1-3.3 3.3Z"/>',
87
+ stackOverflow:
88
+ '<path d="M15.72 0 14 1.28l6.4 8.58 1.7-1.26L15.73 0zm-3.94 3.42-1.36 1.64 8.22 6.85 1.37-1.64-8.23-6.85zM8.64 7.88l-.91 1.94 9.7 4.52.9-1.94-9.7-4.52zm-1.86 4.86-.44 2.1 10.48 2.2.44-2.1-10.47-2.2zM1.9 15.47V24h19.19v-8.53h-2.13v6.4H4.02v-6.4H1.9zm4.26 2.13v2.13h10.66V17.6H6.15Z"/>',
87
89
  };
@@ -14,7 +14,7 @@ const t = useTranslations(Astro.props.locale);
14
14
  aria-expanded="false"
15
15
  aria-label={t('menuButton.accessibleLabel')}
16
16
  aria-controls="starlight__sidebar"
17
- class="flex md:hidden"
17
+ class="sl-flex md:sl-hidden"
18
18
  >
19
19
  <Icon name="bars" />
20
20
  </button>
@@ -15,7 +15,7 @@ const isRtl = dir === 'rtl';
15
15
  const t = useTranslations(locale);
16
16
  ---
17
17
 
18
- <div class="pagination-links flex" dir={dir}>
18
+ <div class="pagination-links sl-flex" dir={dir}>
19
19
  {
20
20
  prev && (
21
21
  <a href={prev.href} rel="prev">
@@ -1,5 +1,5 @@
1
- <div class="right-sidebar-panel hidden lg:block">
2
- <div class="container">
1
+ <div class="right-sidebar-panel sl-hidden lg:sl-block">
2
+ <div class="sl-container">
3
3
  <slot />
4
4
  </div>
5
5
  </div>
@@ -11,7 +11,7 @@
11
11
  .right-sidebar-panel + .right-sidebar-panel {
12
12
  border-top: 1px solid var(--sl-color-hairline);
13
13
  }
14
- .container {
14
+ .sl-container {
15
15
  width: calc(var(--sl-sidebar-width) - 2 * var(--sl-sidebar-pad-x));
16
16
  }
17
17
  .right-sidebar-panel :global(h2) {
@@ -32,7 +32,7 @@
32
32
  color: var(--sl-color-white);
33
33
  }
34
34
  @media (min-width: 72rem) {
35
- .container {
35
+ .sl-container {
36
36
  max-width: calc(
37
37
  (
38
38
  (
@@ -22,16 +22,16 @@ const pagefindTranslations = {
22
22
  /* The span is `aria-hidden` because it is not shown on small screens. Instead, the icon label is used for accessibility purposes. */
23
23
  }
24
24
  <Icon name="magnifier" label={t('search.label')} />
25
- <span class="hidden md:block" aria-hidden="true">{t('search.label')}</span>
26
- <Icon name="forward-slash" class="hidden md:block" label={t('search.shortcutLabel')} />
25
+ <span class="sl-hidden md:sl-block" aria-hidden="true">{t('search.label')}</span>
26
+ <Icon name="forward-slash" class="sl-hidden md:sl-block" label={t('search.shortcutLabel')} />
27
27
  </button>
28
28
 
29
29
  <dialog style="padding:0" aria-label={t('search.label')}>
30
- <div class="dialog-frame flex">
30
+ <div class="dialog-frame sl-flex">
31
31
  {
32
32
  /* TODO: Make the layout of this button flexible to accommodate different word lengths. Currently hard-coded for English: “Cancel” */
33
33
  }
34
- <button data-close-modal class="flex md:hidden">
34
+ <button data-close-modal class="sl-flex md:sl-hidden">
35
35
  {t('search.cancelLabel')}
36
36
  </button>
37
37
  {
@@ -73,6 +73,7 @@ interface Props {
73
73
 
74
74
  option {
75
75
  background-color: var(--sl-color-bg-nav);
76
+ color: var(--sl-color-gray-1);
76
77
  }
77
78
 
78
79
  @media (min-width: 50rem) {
@@ -12,9 +12,9 @@ interface Props {
12
12
  const { sidebar, locale } = Astro.props;
13
13
  ---
14
14
 
15
- <div class="sidebar flex">
15
+ <div class="sidebar sl-flex">
16
16
  <SidebarSublist sublist={sidebar} />
17
- <div class="mobile-preferences flex md:hidden">
17
+ <div class="mobile-preferences sl-flex md:sl-hidden">
18
18
  <ThemeSelect {locale} />
19
19
  <LanguageSelect {locale} />
20
20
  </div>
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  import { flattenSidebar, type SidebarEntry } from '../utils/navigation';
3
3
  import Icon from '../user-components/Icon.astro';
4
+ import Badge from './Badge.astro';
4
5
 
5
6
  interface Props {
6
7
  sublist: SidebarEntry[];
@@ -18,7 +19,13 @@ interface Props {
18
19
  aria-current={entry.isCurrent && 'page'}
19
20
  class:list={{ large: !Astro.props.nested }}
20
21
  >
21
- {entry.label}
22
+ <span>{entry.label}</span>
23
+ {entry.badge && (
24
+ <Badge
25
+ text={entry.badge.text}
26
+ variant={entry.isCurrent ? 'outline' : entry.badge.variant}
27
+ />
28
+ )}
22
29
  </a>
23
30
  ) : (
24
31
  <details
@@ -104,6 +111,10 @@ interface Props {
104
111
  background-color: var(--sl-color-text-accent);
105
112
  }
106
113
 
114
+ a > *:not(:last-child) {
115
+ margin-inline-end: 0.25em;
116
+ }
117
+
107
118
  @media (min-width: 50rem) {
108
119
  .top-level > li + li {
109
120
  margin-top: 0.5rem;
@@ -26,7 +26,7 @@ if (config.logo) {
26
26
  const href = pathWithBase(Astro.props.locale || '/');
27
27
  ---
28
28
 
29
- <a {href} class="site-title flex">
29
+ <a {href} class="site-title sl-flex">
30
30
  {
31
31
  config.logo && logos.dark && (
32
32
  <>
@@ -20,6 +20,7 @@ const labels: Record<Platform, string> = {
20
20
  twitch: 'Twitch',
21
21
  microsoftTeams: 'Microsoft Teams',
22
22
  instagram: 'Instagram',
23
+ stackOverflow: 'Stack Overflow',
23
24
  };
24
25
 
25
26
  const links = Object.entries(config.social || {}).filter(([, url]) => Boolean(url)) as [
@@ -32,7 +33,7 @@ const links = Object.entries(config.social || {}).filter(([, url]) => Boolean(ur
32
33
  links.length > 0 && (
33
34
  <>
34
35
  {links.map(([platform, url]) => (
35
- <a href={url} rel="me">
36
+ <a href={url} rel="me" class="sl-flex">
36
37
  <span class="sr-only">{labels[platform]}</span>
37
38
  <Icon name={platform} />
38
39
  </a>
@@ -44,7 +45,6 @@ const links = Object.entries(config.social || {}).filter(([, url]) => Boolean(ur
44
45
 
45
46
  <style>
46
47
  a {
47
- display: flex;
48
48
  color: var(--sl-color-text-accent);
49
49
  }
50
50
  a:hover {
@@ -16,10 +16,10 @@ const t = useTranslations(locale);
16
16
  ---
17
17
 
18
18
  <mobile-starlight-toc data-min-h={minHeadingLevel} data-max-h={maxHeadingLevel}>
19
- <nav aria-labelledby="starlight__on-this-page--mobile" class="lg:hidden">
19
+ <nav aria-labelledby="starlight__on-this-page--mobile" class="lg:sl-hidden">
20
20
  <details id="starlight__mobile-toc">
21
- <summary id="starlight__on-this-page--mobile" class="flex">
22
- <div class="toggle flex">
21
+ <summary id="starlight__on-this-page--mobile" class="sl-flex">
22
+ <div class="toggle sl-flex">
23
23
  {t('tableOfContents.onThisPage')}
24
24
  <Icon name={'right-caret'} class="caret" size="1rem" />
25
25
  </div>
package/layout/Page.astro CHANGED
@@ -24,6 +24,7 @@ import SkipLink from '../components/SkipLink.astro';
24
24
  import ThemeProvider from '../components/ThemeProvider.astro';
25
25
  import PageFrame from '../layout/PageFrame.astro';
26
26
  import TwoColumnContent from '../layout/TwoColumnContent.astro';
27
+ import Banner from '../components/Banner.astro';
27
28
 
28
29
  // Remark component CSS (needs to override `MarkdownContent.astro`)
29
30
  import '../style/asides.css';
@@ -92,6 +93,7 @@ const hasHero = Boolean(entry.data.hero);
92
93
  <RightSidebar slot="right-sidebar" {headings} {locale} {tocConfig} />
93
94
  <main data-pagefind-body={entry.slug !== '404'} lang={entryMeta.lang} dir={entryMeta.dir}>
94
95
  {/* TODO: Revisit how this logic flows. */}
96
+ {entry.data.banner && <Banner {...entry.data.banner} />}
95
97
  {
96
98
  entry.data.hero ? (
97
99
  <ContentPanel>
@@ -11,7 +11,7 @@ const { hasSidebar, locale } = Astro.props;
11
11
  const t = useTranslations(locale);
12
12
  ---
13
13
 
14
- <div class="page flex">
14
+ <div class="page sl-flex">
15
15
  <header class="header"><slot name="header" /></header>
16
16
  {
17
17
  hasSidebar && (
@@ -4,7 +4,7 @@ interface Props {
4
4
  }
5
5
  ---
6
6
 
7
- <div class="lg:flex">
7
+ <div class="lg:sl-flex">
8
8
  {
9
9
  Astro.props.hasToC && (
10
10
  <aside class="right-sidebar-container">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrojs/starlight",
3
- "version": "0.7.3",
3
+ "version": "0.8.1",
4
4
  "description": "Build beautiful, high-performance documentation websites with Astro",
5
5
  "keywords": [
6
6
  "docs",
package/schema.ts CHANGED
@@ -3,6 +3,7 @@ import { HeadConfigSchema } from './schemas/head';
3
3
  import { PrevNextLinkConfigSchema } from './schemas/prevNextLink';
4
4
  import { TableOfContentsSchema } from './schemas/tableOfContents';
5
5
  import { Icons } from './components/Icons';
6
+ import { BadgeConfigSchema } from './schemas/badge';
6
7
  export { i18nSchema } from './schemas/i18n';
7
8
 
8
9
  type IconName = keyof typeof Icons;
@@ -147,7 +148,22 @@ export function docsSchema() {
147
148
  * Prevents this page from being included in autogenerated sidebar groups.
148
149
  */
149
150
  hidden: z.boolean().default(false),
151
+ /**
152
+ * Adds a badge to the sidebar link.
153
+ * Can be a string or an object with a variant and text.
154
+ * Variants include 'note', 'tip', 'caution', 'danger', 'success', and 'default'.
155
+ * Passing only a string defaults to the 'default' variant which uses the site accent color.
156
+ */
157
+ badge: BadgeConfigSchema(),
150
158
  })
151
159
  .default({}),
160
+
161
+ /** Display an announcement banner at the top of this page. */
162
+ banner: z
163
+ .object({
164
+ /** The content of the banner. Supports HTML syntax. */
165
+ content: z.string(),
166
+ })
167
+ .optional(),
152
168
  });
153
169
  }
@@ -0,0 +1,20 @@
1
+ import { z } from 'astro/zod';
2
+
3
+ const badgeSchema = () =>
4
+ z.object({
5
+ variant: z.enum(['note', 'danger', 'success', 'caution', 'tip', 'default']).default('default'),
6
+ text: z.string(),
7
+ });
8
+
9
+ export const BadgeConfigSchema = () =>
10
+ z
11
+ .union([z.string(), badgeSchema()])
12
+ .transform((badge) => {
13
+ if (typeof badge === 'string') {
14
+ return { variant: 'default' as const, text: badge };
15
+ }
16
+ return badge;
17
+ })
18
+ .optional();
19
+
20
+ export type Badge = z.output<ReturnType<typeof badgeSchema>>;
package/style/props.css CHANGED
@@ -42,6 +42,7 @@
42
42
  --sl-color-bg-nav: var(--sl-color-gray-6);
43
43
  --sl-color-bg-sidebar: var(--sl-color-gray-6);
44
44
  --sl-color-bg-inline-code: var(--sl-color-gray-5);
45
+ --sl-color-bg-accent: var(--sl-color-accent-high);
45
46
  --sl-color-hairline-light: var(--sl-color-gray-5);
46
47
  --sl-color-hairline: var(--sl-color-gray-6);
47
48
  --sl-color-hairline-shade: var(--sl-color-black);
@@ -148,6 +149,7 @@
148
149
  --sl-color-bg-nav: var(--sl-color-gray-7);
149
150
  --sl-color-bg-sidebar: var(--sl-color-bg);
150
151
  --sl-color-bg-inline-code: var(--sl-color-gray-6);
152
+ --sl-color-bg-accent: var(--sl-color-accent);
151
153
  --sl-color-hairline-light: var(--sl-color-gray-6);
152
154
  --sl-color-hairline-shade: var(--sl-color-gray-6);
153
155
 
package/style/util.css CHANGED
@@ -10,34 +10,34 @@
10
10
  border-width: 0;
11
11
  }
12
12
 
13
- .hidden {
13
+ .sl-hidden {
14
14
  display: none;
15
15
  }
16
- .flex {
16
+ .sl-flex {
17
17
  display: flex;
18
18
  }
19
- .block {
19
+ .sl-block {
20
20
  display: block;
21
21
  }
22
22
  @media (min-width: 50rem) {
23
- .md\:hidden {
23
+ .md\:sl-hidden {
24
24
  display: none;
25
25
  }
26
- .md\:flex {
26
+ .md\:sl-flex {
27
27
  display: flex;
28
28
  }
29
- .md\:block {
29
+ .md\:sl-block {
30
30
  display: block;
31
31
  }
32
32
  }
33
33
  @media (min-width: 72rem) {
34
- .lg\:hidden {
34
+ .lg\:sl-hidden {
35
35
  display: none;
36
36
  }
37
- .lg\:flex {
37
+ .lg\:sl-flex {
38
38
  display: flex;
39
39
  }
40
- .lg\:block {
40
+ .lg\:sl-block {
41
41
  display: block;
42
42
  }
43
43
  }
@@ -10,8 +10,8 @@ interface Props {
10
10
  const { icon, title } = Astro.props;
11
11
  ---
12
12
 
13
- <article class="card flex">
14
- <p class="title flex">
13
+ <article class="card sl-flex">
14
+ <p class="title sl-flex">
15
15
  {icon && <Icon name={icon} class="icon" size="1.333em" />}
16
16
  <span set:html={title} />
17
17
  </p>
@@ -11,7 +11,7 @@ const { title, description, ...attributes } = Astro.props;
11
11
  ---
12
12
 
13
13
  <div>
14
- <span class="flex stack">
14
+ <span class="sl-flex stack">
15
15
  <a {...attributes}>
16
16
  <span class="title" set:html={title} />
17
17
  </a>
@@ -7,6 +7,7 @@ import { getLocaleRoutes, type Route } from './routing';
7
7
  import { localeToLang, slugToPathname } from './slugs';
8
8
  import type { AutoSidebarGroup, SidebarItem, SidebarLinkItem } from './user-config';
9
9
  import { ensureLeadingAndTrailingSlashes, ensureTrailingSlash } from './path';
10
+ import type { Badge } from '../schemas/badge';
10
11
 
11
12
  const DirKey = Symbol('DirKey');
12
13
 
@@ -15,6 +16,7 @@ export interface Link {
15
16
  label: string;
16
17
  href: string;
17
18
  isCurrent: boolean;
19
+ badge: Badge | undefined;
18
20
  }
19
21
 
20
22
  interface Group {
@@ -112,14 +114,14 @@ function linkFromConfig(
112
114
  if (locale) href = '/' + locale + href;
113
115
  }
114
116
  const label = pickLang(item.translations, localeToLang(locale)) || item.label;
115
- return makeLink(href, label, currentPathname);
117
+ return makeLink(href, label, currentPathname, item.badge);
116
118
  }
117
119
 
118
120
  /** Create a link entry. */
119
- function makeLink(href: string, label: string, currentPathname: string): Link {
121
+ function makeLink(href: string, label: string, currentPathname: string, badge?: Badge): Link {
120
122
  if (!isAbsolute(href)) href = pathWithBase(href);
121
123
  const isCurrent = href === ensureTrailingSlash(currentPathname);
122
- return { type: 'link', label, href, isCurrent };
124
+ return { type: 'link', label, href, isCurrent, badge };
123
125
  }
124
126
 
125
127
  /** Get the segments leading to a page. */
@@ -168,7 +170,8 @@ function linkFromRoute(route: Route, currentPathname: string): Link {
168
170
  return makeLink(
169
171
  slugToPathname(route.slug),
170
172
  route.entry.data.sidebar.label || route.entry.data.title,
171
- currentPathname
173
+ currentPathname,
174
+ route.entry.data.sidebar.badge
172
175
  );
173
176
  }
174
177
 
@@ -184,13 +187,17 @@ function getOrder(routeOrDir: Route | Dir): number {
184
187
  }
185
188
 
186
189
  /** Sort a directory’s entries by user-specified order or alphabetically if no order specified. */
187
- function sortDirEntries(dir: [string, Dir | Route][]): [string, Dir | Route][] {
188
- return dir.sort(([, a], [, b]) => {
190
+ function sortDirEntries(
191
+ dir: [string, Dir | Route][],
192
+ locale: string | undefined
193
+ ): [string, Dir | Route][] {
194
+ const collator = new Intl.Collator(localeToLang(locale));
195
+ return dir.sort(([keyA, a], [keyB, b]) => {
189
196
  const [aOrder, bOrder] = [getOrder(a), getOrder(b)];
190
197
  // Pages are sorted by order in ascending order.
191
198
  if (aOrder !== bOrder) return aOrder < bOrder ? -1 : 1;
192
199
  // If two pages have the same order value they will be sorted by their slug.
193
- return a.slug < b.slug ? -1 : a.slug > b.slug ? 1 : 0;
200
+ return collator.compare(isDir(a) ? keyA : a.slug, isDir(b) ? keyB : b.slug);
194
201
  });
195
202
  }
196
203
 
@@ -203,7 +210,7 @@ function groupFromDir(
203
210
  locale: string | undefined,
204
211
  collapsed: boolean
205
212
  ): Group {
206
- const entries = sortDirEntries(Object.entries(dir)).map(([key, dirOrRoute]) =>
213
+ const entries = sortDirEntries(Object.entries(dir), locale).map(([key, dirOrRoute]) =>
207
214
  dirToItem(dirOrRoute, `${fullPath}/${key}`, key, currentPathname, locale, collapsed)
208
215
  );
209
216
  return {
@@ -235,7 +242,7 @@ function sidebarFromDir(
235
242
  locale: string | undefined,
236
243
  collapsed: boolean
237
244
  ) {
238
- return sortDirEntries(Object.entries(tree)).map(([key, dirOrRoute]) =>
245
+ return sortDirEntries(Object.entries(tree), locale).map(([key, dirOrRoute]) =>
239
246
  dirToItem(dirOrRoute, key, key, currentPathname, locale, collapsed)
240
247
  );
241
248
  }
@@ -4,6 +4,7 @@ import { HeadConfigSchema } from '../schemas/head';
4
4
  import { LogoConfigSchema } from '../schemas/logo';
5
5
  import { TableOfContentsSchema } from '../schemas/tableOfContents';
6
6
  import { FaviconSchema } from '../schemas/favicon';
7
+ import { BadgeConfigSchema } from '../schemas/badge';
7
8
 
8
9
  const LocaleSchema = z.object({
9
10
  /** The label for this language to show in UI, e.g. `"English"`, `"العربية"`, or `"简体中文"`. */
@@ -42,6 +43,8 @@ const SidebarGroupSchema = SidebarBaseSchema.extend({
42
43
  const SidebarLinkItemSchema = SidebarBaseSchema.extend({
43
44
  /** The link to this item’s content. Can be a relative link to local files or the full URL of an external page. */
44
45
  link: z.string(),
46
+ /** Adds a badge to the link item */
47
+ badge: BadgeConfigSchema(),
45
48
  });
46
49
  export type SidebarLinkItem = z.infer<typeof SidebarLinkItemSchema>;
47
50
 
@@ -148,6 +151,7 @@ const UserConfigSchema = z.object({
148
151
  'twitch',
149
152
  'microsoftTeams',
150
153
  'instagram',
154
+ 'stackOverflow',
151
155
  ]),
152
156
  // Link to the respective social profile for this site
153
157
  z.string().url()