summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Swithinbank2025-04-07 17:33:51 +0200
committerGitHub2025-04-07 17:33:51 +0200
commitf87e9acbf5090a31858c1cde568cc798140f1366 (patch)
tree16fa97748215a8dfb2ca4180ef06238f05b72183
parentc46904c4a16cf1c7f4f895e42cb164474b2301b3 (diff)
downloadIT.starlight-f87e9acbf5090a31858c1cde568cc798140f1366.tar.gz
IT.starlight-f87e9acbf5090a31858c1cde568cc798140f1366.tar.bz2
IT.starlight-f87e9acbf5090a31858c1cde568cc798140f1366.zip
Make `social` icon configuration more flexible (#3025)
Co-authored-by: HiDeoo <494699+HiDeoo@users.noreply.github.com>
-rw-r--r--.changeset/gold-sloths-unite.md24
-rw-r--r--.changeset/swift-hotels-move.md5
-rw-r--r--docs/astro.config.mjs8
-rw-r--r--docs/src/components/social-links-type.astro11
-rw-r--r--docs/src/content/docs/guides/customization.mdx23
-rw-r--r--docs/src/content/docs/reference/configuration.mdx29
-rw-r--r--docs/src/content/docs/reference/plugins.md14
-rw-r--r--examples/basics/astro.config.mjs4
-rw-r--r--examples/markdoc/astro.config.mjs4
-rw-r--r--examples/tailwind/astro.config.mjs4
-rw-r--r--packages/starlight/__tests__/basics/config-errors.test.ts6
-rw-r--r--packages/starlight/__tests__/basics/user-config.test.ts12
-rw-r--r--packages/starlight/__tests__/head/head.test.ts12
-rw-r--r--packages/starlight/__tests__/head/vitest.config.ts1
-rw-r--r--packages/starlight/components/SocialIcons.astro10
-rw-r--r--packages/starlight/schemas/hero.ts8
-rw-r--r--packages/starlight/schemas/icon.ts7
-rw-r--r--packages/starlight/schemas/social.ts125
-rw-r--r--packages/starlight/utils/head.ts5
19 files changed, 137 insertions, 175 deletions
diff --git a/.changeset/gold-sloths-unite.md b/.changeset/gold-sloths-unite.md
new file mode 100644
index 00000000..d81e7ea1
--- /dev/null
+++ b/.changeset/gold-sloths-unite.md
@@ -0,0 +1,24 @@
+---
+'@astrojs/starlight': minor
+---
+
+Makes `social` configuration more flexible.
+
+⚠️ **BREAKING CHANGE:** The `social` configuration option has changed syntax. You will need to update this in `astro.config.mjs` when upgrading.
+
+Previously, a limited set of platforms were supported using a shorthand syntax with labels built in to Starlight. While convenient, this approach was less flexible and required dedicated code for each social platform added.
+
+Now, you must specify the icon and label for each social link explicitly and you can use any of [Starlight’s built-in icons](https://starlight.astro.build/reference/icons/) for social links.
+
+The following example shows updating the old `social` syntax to the new:
+
+```diff
+- social: {
+- github: 'https://github.com/withastro/starlight',
+- discord: 'https://astro.build/chat',
+- },
++ social: [
++ { icon: 'github', label: 'GitHub', href: 'https://github.com/withastro/starlight' },
++ { icon: 'discord', label: 'Discord', href: 'https://astro.build/chat' },
++ ],
+```
diff --git a/.changeset/swift-hotels-move.md b/.changeset/swift-hotels-move.md
new file mode 100644
index 00000000..78a43e6d
--- /dev/null
+++ b/.changeset/swift-hotels-move.md
@@ -0,0 +1,5 @@
+---
+'@astrojs/starlight': patch
+---
+
+Fixes Starlight’s autogenerated `<meta name="twitter:site">` tags when a Twitter link is set in `social` config. Previously these incorrectly rendered `content="/username"` and now correctly render `content="@username"`.
diff --git a/docs/astro.config.mjs b/docs/astro.config.mjs
index 9906df38..7923a0e7 100644
--- a/docs/astro.config.mjs
+++ b/docs/astro.config.mjs
@@ -45,10 +45,10 @@ export default defineConfig({
editLink: {
baseUrl: 'https://github.com/withastro/starlight/edit/main/docs/',
},
- social: {
- github: 'https://github.com/withastro/starlight',
- discord: 'https://astro.build/chat',
- },
+ social: [
+ { icon: 'github', label: 'GitHub', href: 'https://github.com/withastro/starlight' },
+ { icon: 'discord', label: 'Discord', href: 'https://astro.build/chat' },
+ ],
head: [
{
tag: 'script',
diff --git a/docs/src/components/social-links-type.astro b/docs/src/components/social-links-type.astro
index fc62630d..d60e2071 100644
--- a/docs/src/components/social-links-type.astro
+++ b/docs/src/components/social-links-type.astro
@@ -1,7 +1,10 @@
---
-import { socialLinks } from '../../../packages/starlight/schemas/social';
-
-const socials = [...socialLinks].sort((a, b) => a.localeCompare(b, 'en'));
+import { getRelativeLocaleUrl } from 'astro:i18n';
+const href = getRelativeLocaleUrl(Astro.currentLocale ?? 'en', '/reference/icons/');
---
-<code>{`Partial<Record<${socials.map((social) => `'${social}'`).join(' | ')}, string>>`}</code>
+<code>
+ <Fragment set:text="Array<{" />
+ label: string; icon: <a {href}>StarlightIcon</a>; href: string
+ <Fragment set:text="}>" />
+</code>
diff --git a/docs/src/content/docs/guides/customization.mdx b/docs/src/content/docs/guides/customization.mdx
index c77b0cee..c53fa0ba 100644
--- a/docs/src/content/docs/guides/customization.mdx
+++ b/docs/src/content/docs/guides/customization.mdx
@@ -200,10 +200,15 @@ defineConfig({
Starlight has built-in support for adding links to your social media accounts to the site header via the [`social`](/reference/configuration/#social) option in the Starlight integration.
-You can find a full list of supported link icons in the [Configuration Reference](/reference/configuration/#social).
-Let us know on GitHub or Discord if you need support for another service!
+Each entry in the `social` array must be an object with three properties:
-```js {9-12}
+- `icon`: one of Starlight’s [built-in icons](/reference/icons/), e.g. `"github"`.
+- `label`: an accessible label for the link, e.g. `"GitHub"`.
+- `href`: the URL for the link, e.g. `"https://github.com/withastro/starlight"`.
+
+The following example adds links to the Astro Discord chat and the Starlight GitHub repository:
+
+```js {9-16}
// astro.config.mjs
import { defineConfig } from 'astro/config';
import starlight from '@astrojs/starlight';
@@ -212,10 +217,14 @@ export default defineConfig({
integrations: [
starlight({
title: 'Docs With Social Links',
- social: {
- discord: 'https://astro.build/chat',
- github: 'https://github.com/withastro/starlight',
- },
+ social: [
+ { icon: 'discord', label: 'Discord', href: 'https://astro.build/chat' },
+ {
+ icon: 'github',
+ label: 'GitHub',
+ href: 'https://github.com/withastro/starlight',
+ },
+ ],
}),
],
});
diff --git a/docs/src/content/docs/reference/configuration.mdx b/docs/src/content/docs/reference/configuration.mdx
index 62107d8c..8a9f2638 100644
--- a/docs/src/content/docs/reference/configuration.mdx
+++ b/docs/src/content/docs/reference/configuration.mdx
@@ -341,28 +341,21 @@ The default locale will be used to provide fallback content where translations a
### `social`
-import SocialLinksType from '~/components/social-links-type.astro';
+**type:** <code>{`Array<{ label: string; icon: `}[StarlightIcon](/reference/icons/){`; href: string }>`}</code>
-**type:** <SocialLinksType />
-
-Optional details about the social media accounts for this site. Adding any of these will display them as icon links in the site header.
+Optional details about the social media accounts for this site.
+Each entry will be displayed as an icon link in the site header.
```js
starlight({
- social: {
- codeberg: 'https://codeberg.org/knut/examples',
- discord: 'https://astro.build/chat',
- github: 'https://github.com/withastro/starlight',
- gitlab: 'https://gitlab.com/delucis',
- linkedin: 'https://www.linkedin.com/company/astroinc',
- mastodon: 'https://m.webtoo.ls/@astro',
- threads: 'https://www.threads.net/@nmoodev',
- twitch: 'https://www.twitch.tv/bholmesdev',
- twitter: 'https://twitter.com/astrodotbuild',
- 'x.com': 'https://x.com/astrodotbuild',
- youtube: 'https://youtube.com/@astrodotbuild',
- },
-});
+ social: [
+ { icon: 'codeberg', label: 'Codeberg', href: 'https://codeberg.org/knut' },
+ { icon: 'discord', label: 'Discord', href: 'https://astro.build/chat' },
+ { icon: 'github', label: 'GitHub', href: 'https://github.com/withastro' },
+ { icon: 'gitlab', label: 'GitLab', href: 'https://gitlab.com/delucis' },
+ { icon: 'mastodon', label: 'Mastodon', href: 'https://m.webtoo.ls/@astro' },
+ ],
+}),
```
### `customCss`
diff --git a/docs/src/content/docs/reference/plugins.md b/docs/src/content/docs/reference/plugins.md
index d170a4fd..1c46004a 100644
--- a/docs/src/content/docs/reference/plugins.md
+++ b/docs/src/content/docs/reference/plugins.md
@@ -163,19 +163,23 @@ Provide the root-level configuration keys you want to override.
To update nested configuration values, you must provide the entire nested object.
To extend an existing config option without overriding it, spread the existing value into your new value.
-In the following example, a new [`social`](/reference/configuration/#social) media account is added to the existing configuration by spreading `config.social` into the new `social` object:
+In the following example, a new [`social`](/reference/configuration/#social) media account is added to the existing configuration by spreading `config.social` into the new `social` array:
-```ts {6-11}
+```ts {6-15}
// plugin.ts
export default {
name: 'add-twitter-plugin',
hooks: {
'config:setup'({ config, updateConfig }) {
updateConfig({
- social: {
+ social: [
...config.social,
- twitter: 'https://twitter.com/astrodotbuild',
- },
+ {
+ icon: 'twitter',
+ label: 'Twitter',
+ href: 'https://twitter.com/astrodotbuild',
+ },
+ ],
});
},
},
diff --git a/examples/basics/astro.config.mjs b/examples/basics/astro.config.mjs
index 1b393646..9a25601b 100644
--- a/examples/basics/astro.config.mjs
+++ b/examples/basics/astro.config.mjs
@@ -7,9 +7,7 @@ export default defineConfig({
integrations: [
starlight({
title: 'My Docs',
- social: {
- github: 'https://github.com/withastro/starlight',
- },
+ social: [{ icon: 'github', label: 'GitHub', href: 'https://github.com/withastro/starlight' }],
sidebar: [
{
label: 'Guides',
diff --git a/examples/markdoc/astro.config.mjs b/examples/markdoc/astro.config.mjs
index 7f814cbb..949f3fcc 100644
--- a/examples/markdoc/astro.config.mjs
+++ b/examples/markdoc/astro.config.mjs
@@ -9,9 +9,7 @@ export default defineConfig({
markdoc(),
starlight({
title: 'My Docs',
- social: {
- github: 'https://github.com/withastro/starlight',
- },
+ social: [{ icon: 'github', label: 'GitHub', href: 'https://github.com/withastro/starlight' }],
sidebar: [
{
label: 'Guides',
diff --git a/examples/tailwind/astro.config.mjs b/examples/tailwind/astro.config.mjs
index a3a3ba5e..872a2896 100644
--- a/examples/tailwind/astro.config.mjs
+++ b/examples/tailwind/astro.config.mjs
@@ -8,9 +8,7 @@ export default defineConfig({
integrations: [
starlight({
title: 'Docs with Tailwind',
- social: {
- github: 'https://github.com/withastro/starlight',
- },
+ social: [{ icon: 'github', label: 'GitHub', href: 'https://github.com/withastro/starlight' }],
sidebar: [
{
label: 'Guides',
diff --git a/packages/starlight/__tests__/basics/config-errors.test.ts b/packages/starlight/__tests__/basics/config-errors.test.ts
index a010015f..6f9dad71 100644
--- a/packages/starlight/__tests__/basics/config-errors.test.ts
+++ b/packages/starlight/__tests__/basics/config-errors.test.ts
@@ -122,8 +122,10 @@ test('errors with bad social icon config', () => {
"[AstroUserError]:
Invalid config passed to starlight integration
Hint:
- **social.unknown**: Invalid enum value. Expected 'twitter' | 'mastodon' | 'github' | 'gitlab' | 'bitbucket' | 'discord' | 'gitter' | 'codeberg' | 'codePen' | 'youtube' | 'threads' | 'linkedin' | 'twitch' | 'azureDevOps' | 'microsoftTeams' | 'instagram' | 'stackOverflow' | 'x.com' | 'telegram' | 'rss' | 'facebook' | 'email' | 'reddit' | 'patreon' | 'signal' | 'slack' | 'matrix' | 'openCollective' | 'hackerOne' | 'blueSky' | 'discourse' | 'zulip' | 'pinterest' | 'tiktok' | 'nostr' | 'backstage' | 'farcaster' | 'confluence' | 'jira' | 'storybook' | 'npm' | 'sourcehut' | 'substack', received 'unknown'
- **social.unknown**: Invalid url"
+ Starlight v0.33.0 changed the \`social\` configuration syntax. Please specify an array of link items instead of an object.
+ See the Starlight changelog for details: https://github.com/withastro/starlight/blob/main/packages/starlight/CHANGELOG.md#0330
+
+ **social**: Expected type \`"array"\`, received \`"object"\`"
`
);
});
diff --git a/packages/starlight/__tests__/basics/user-config.test.ts b/packages/starlight/__tests__/basics/user-config.test.ts
index e3eeb723..c0f115f6 100644
--- a/packages/starlight/__tests__/basics/user-config.test.ts
+++ b/packages/starlight/__tests__/basics/user-config.test.ts
@@ -4,11 +4,11 @@ import { StarlightConfigSchema } from '../../utils/user-config';
test('preserve social config order', () => {
const config = StarlightConfigSchema.parse({
title: 'Test',
- social: {
- twitch: 'https://www.twitch.tv/bholmesdev',
- github: 'https://github.com/withastro/starlight',
- discord: 'https://astro.build/chat',
- },
+ social: [
+ { icon: 'twitch', label: 'Twitch', href: 'https://www.twitch.tv/bholmesdev' },
+ { icon: 'github', label: 'GitHub', href: 'https://github.com/withastro/starlight' },
+ { icon: 'discord', label: 'Discord', href: 'https://astro.build/chat' },
+ ],
});
- expect(Object.keys(config.social || {})).toEqual(['twitch', 'github', 'discord']);
+ expect((config.social || []).map(({ icon }) => icon)).toEqual(['twitch', 'github', 'discord']);
});
diff --git a/packages/starlight/__tests__/head/head.test.ts b/packages/starlight/__tests__/head/head.test.ts
index 966635ef..46205b89 100644
--- a/packages/starlight/__tests__/head/head.test.ts
+++ b/packages/starlight/__tests__/head/head.test.ts
@@ -23,6 +23,18 @@ test('includes custom tags defined in the Starlight configuration', () => {
});
});
+test('includes `twitter:site` based on Starlight `social` configuration', () => {
+ const head = getTestHead();
+ expect(head).toContainEqual({
+ tag: 'meta',
+ attrs: {
+ name: 'twitter:site',
+ content: '@astrodotbuild',
+ },
+ content: '',
+ });
+});
+
test('merges two <title> tags', () => {
const head = getTestHead([{ tag: 'title', content: 'Override', attrs: {} }]);
expect(head.filter((tag) => tag.tag === 'title')).toEqual([
diff --git a/packages/starlight/__tests__/head/vitest.config.ts b/packages/starlight/__tests__/head/vitest.config.ts
index 889b9dd5..b13fef59 100644
--- a/packages/starlight/__tests__/head/vitest.config.ts
+++ b/packages/starlight/__tests__/head/vitest.config.ts
@@ -17,4 +17,5 @@ export default defineVitestConfig({
},
},
],
+ social: [{ icon: 'twitter', label: 'Twitter', href: 'https://twitter.com/astrodotbuild' }],
});
diff --git a/packages/starlight/components/SocialIcons.astro b/packages/starlight/components/SocialIcons.astro
index ad9056ff..b8c5bd4c 100644
--- a/packages/starlight/components/SocialIcons.astro
+++ b/packages/starlight/components/SocialIcons.astro
@@ -2,18 +2,16 @@
import config from 'virtual:starlight/user-config';
import Icon from '../user-components/Icon.astro';
-type Platform = keyof NonNullable<typeof config.social>;
-type SocialConfig = NonNullable<NonNullable<typeof config.social>[Platform]>;
-const links = Object.entries(config.social || {}) as [Platform, SocialConfig][];
+const links = config.social || [];
---
{
links.length > 0 && (
<>
- {links.map(([platform, { label, url }]) => (
- <a href={url} rel="me" class="sl-flex">
+ {links.map(({ label, href, icon }) => (
+ <a {href} rel="me" class="sl-flex">
<span class="sr-only">{label}</span>
- <Icon name={platform} />
+ <Icon name={icon} />
</a>
))}
</>
diff --git a/packages/starlight/schemas/hero.ts b/packages/starlight/schemas/hero.ts
index 86105523..e1af1f90 100644
--- a/packages/starlight/schemas/hero.ts
+++ b/packages/starlight/schemas/hero.ts
@@ -1,8 +1,6 @@
import { z } from 'astro/zod';
import type { SchemaContext } from 'astro:content';
-import { Icons, type StarlightIcon } from '../components/Icons';
-
-const iconNames = Object.keys(Icons) as [StarlightIcon, ...StarlightIcon[]];
+import { IconSchema } from './icon';
export const HeroSchema = ({ image }: SchemaContext) =>
z.object({
@@ -55,9 +53,9 @@ export const HeroSchema = ({ image }: SchemaContext) =>
* Can be an inline `<svg>` or the name of one of Starlight’s built-in icons.
*/
icon: z
- .union([z.enum(iconNames), z.string().startsWith('<svg')])
+ .union([IconSchema(), z.string().startsWith('<svg')])
.transform((icon) => {
- const parsedIcon = z.enum(iconNames).safeParse(icon);
+ const parsedIcon = IconSchema().safeParse(icon);
return parsedIcon.success
? ({ type: 'icon', name: parsedIcon.data } as const)
: ({ type: 'raw', html: icon } as const);
diff --git a/packages/starlight/schemas/icon.ts b/packages/starlight/schemas/icon.ts
new file mode 100644
index 00000000..49462371
--- /dev/null
+++ b/packages/starlight/schemas/icon.ts
@@ -0,0 +1,7 @@
+import { z } from 'astro/zod';
+import { Icons, type StarlightIcon } from '../components/Icons';
+
+const iconNames = Object.keys(Icons) as [StarlightIcon, ...StarlightIcon[]];
+
+/** String that matches the name of one of Starlight’s built-in icons. */
+export const IconSchema = () => z.enum(iconNames);
diff --git a/packages/starlight/schemas/social.ts b/packages/starlight/schemas/social.ts
index 7880f5cd..f2129fe5 100644
--- a/packages/starlight/schemas/social.ts
+++ b/packages/starlight/schemas/social.ts
@@ -1,111 +1,22 @@
import { z } from 'astro/zod';
+import { IconSchema } from './icon';
-export const socialLinks = [
- 'twitter',
- 'mastodon',
- 'github',
- 'gitlab',
- 'bitbucket',
- 'discord',
- 'gitter',
- 'codeberg',
- 'codePen',
- 'youtube',
- 'threads',
- 'linkedin',
- 'twitch',
- 'azureDevOps',
- 'microsoftTeams',
- 'instagram',
- 'stackOverflow',
- 'x.com',
- 'telegram',
- 'rss',
- 'facebook',
- 'email',
- 'reddit',
- 'patreon',
- 'signal',
- 'slack',
- 'matrix',
- 'openCollective',
- 'hackerOne',
- 'blueSky',
- 'discourse',
- 'zulip',
- 'pinterest',
- 'tiktok',
- 'nostr',
- 'backstage',
- 'farcaster',
- 'confluence',
- 'jira',
- 'storybook',
- 'npm',
- 'sourcehut',
- 'substack',
-] as const;
+const LinksSchema = z
+ .object({ icon: IconSchema(), label: z.string().min(1), href: z.string() })
+ .array()
+ .optional();
export const SocialLinksSchema = () =>
- z
- .record(
- z.enum(socialLinks),
- // Link to the respective social profile for this site
- z.string().url()
- )
- .transform((links) => {
- const labelledLinks: Partial<Record<keyof typeof links, { label: string; url: string }>> = {};
- for (const _k in links) {
- const key = _k as keyof typeof links;
- const url = links[key];
- if (!url) continue;
- const label = {
- github: 'GitHub',
- gitlab: 'GitLab',
- bitbucket: 'Bitbucket',
- discord: 'Discord',
- gitter: 'Gitter',
- twitter: 'Twitter',
- mastodon: 'Mastodon',
- codeberg: 'Codeberg',
- codePen: 'CodePen',
- youtube: 'YouTube',
- threads: 'Threads',
- linkedin: 'LinkedIn',
- twitch: 'Twitch',
- azureDevOps: 'Azure DevOps',
- microsoftTeams: 'Microsoft Teams',
- instagram: 'Instagram',
- stackOverflow: 'Stack Overflow',
- 'x.com': 'X',
- telegram: 'Telegram',
- rss: 'RSS',
- facebook: 'Facebook',
- email: 'Email',
- reddit: 'Reddit',
- patreon: 'Patreon',
- signal: 'Signal',
- slack: 'Slack',
- matrix: 'Matrix',
- openCollective: 'Open Collective',
- hackerOne: 'Hacker One',
- blueSky: 'BlueSky',
- discourse: 'Discourse',
- zulip: 'Zulip',
- pinterest: 'Pinterest',
- tiktok: 'TikTok',
- nostr: 'Nostr',
- backstage: 'Backstage',
- farcaster: 'Farcaster',
- confluence: 'Confluence',
- jira: 'Jira',
- storybook: 'Storybook',
- npm: 'npm',
- sourcehut: 'SourceHut',
- substack: 'Substack',
- }[key];
- labelledLinks[key] = { label, url };
- }
- return labelledLinks;
- })
- .optional();
+ // Add a more specific error message to help people migrate from the old object syntax.
+ // TODO: remove once most people have updated to v0.33 or higher (e.g. when releasing Starlight v1)
+ z.preprocess((value, ctx) => {
+ if (value && typeof value === 'object' && !Array.isArray(value)) {
+ ctx.addIssue({
+ code: z.ZodIssueCode.custom,
+ message:
+ 'Starlight v0.33.0 changed the `social` configuration syntax. Please specify an array of link items instead of an object.\n' +
+ 'See the Starlight changelog for details: https://github.com/withastro/starlight/blob/main/packages/starlight/CHANGELOG.md#0330\n',
+ });
+ }
+ return value;
+ }, LinksSchema) as unknown as typeof LinksSchema;
diff --git a/packages/starlight/utils/head.ts b/packages/starlight/utils/head.ts
index 9a3ee178..62b49038 100644
--- a/packages/starlight/utils/head.ts
+++ b/packages/starlight/utils/head.ts
@@ -73,12 +73,13 @@ export function getHead(
}
// Link to Twitter account if set in Starlight config.
- if (config.social?.twitter) {
+ const twitterLink = config.social?.find(({ icon }) => icon === 'twitter' || icon === 'x.com');
+ if (twitterLink) {
headDefaults.push({
tag: 'meta',
attrs: {
name: 'twitter:site',
- content: new URL(config.social.twitter.url).pathname,
+ content: new URL(twitterLink.href).pathname.replace('/', '@'),
},
});
}