diff options
author | Chris Swithinbank | 2023-06-22 18:22:54 +0200 |
---|---|---|
committer | GitHub | 2023-06-22 18:22:54 +0200 |
commit | 1aa2187944dde4419e523f0087139f5a21efd826 (patch) | |
tree | af603e5fb667bc103609ef6b3b7090e43f127bff | |
parent | 4279d7512a8261b576056471f5aa1ede1e6aae4a (diff) | |
download | IT.starlight-1aa2187944dde4419e523f0087139f5a21efd826.tar.gz IT.starlight-1aa2187944dde4419e523f0087139f5a21efd826.tar.bz2 IT.starlight-1aa2187944dde4419e523f0087139f5a21efd826.zip |
Support custom 404 pages (#226)
Co-authored-by: Josh Pollara <75546+joshpollara@users.noreply.github.com>
-rw-r--r-- | .changeset/tiny-sheep-raise.md | 5 | ||||
-rw-r--r-- | docs/src/content/docs/404.md | 13 | ||||
-rw-r--r-- | docs/src/content/docs/guides/customization.mdx | 29 | ||||
-rw-r--r-- | packages/starlight/404.astro | 83 | ||||
-rw-r--r-- | packages/starlight/components/EmptyMarkdown.md | 0 | ||||
-rw-r--r-- | packages/starlight/index.astro | 113 | ||||
-rw-r--r-- | packages/starlight/layout/Page.astro | 116 | ||||
-rw-r--r-- | packages/starlight/schemas/i18n.ts | 4 | ||||
-rw-r--r-- | packages/starlight/translations/de.json | 3 | ||||
-rw-r--r-- | packages/starlight/translations/en.json | 3 | ||||
-rw-r--r-- | packages/starlight/translations/es.json | 3 | ||||
-rw-r--r-- | packages/starlight/translations/fr.json | 3 | ||||
-rw-r--r-- | packages/starlight/translations/it.json | 3 | ||||
-rw-r--r-- | packages/starlight/translations/ja.json | 3 | ||||
-rw-r--r-- | packages/starlight/translations/pt.json | 3 |
15 files changed, 216 insertions, 168 deletions
diff --git a/.changeset/tiny-sheep-raise.md b/.changeset/tiny-sheep-raise.md new file mode 100644 index 00000000..07dfedd6 --- /dev/null +++ b/.changeset/tiny-sheep-raise.md @@ -0,0 +1,5 @@ +--- +"@astrojs/starlight": minor +--- + +Add support for custom 404 pages. diff --git a/docs/src/content/docs/404.md b/docs/src/content/docs/404.md new file mode 100644 index 00000000..6772acfb --- /dev/null +++ b/docs/src/content/docs/404.md @@ -0,0 +1,13 @@ +--- +title: Not found +template: splash +editUrl: false +hero: + title: '404' + tagline: <strong>Houston, we have a problem.</strong> We couldn’t find that page.<br>Check the URL or try using the search bar. + actions: + - text: Go home + icon: right-arrow + link: / + variant: primary +--- diff --git a/docs/src/content/docs/guides/customization.mdx b/docs/src/content/docs/guides/customization.mdx index d7d3e5bd..b39c6815 100644 --- a/docs/src/content/docs/guides/customization.mdx +++ b/docs/src/content/docs/guides/customization.mdx @@ -236,6 +236,35 @@ export default defineConfig({ }); ``` +## Custom 404 page + +Starlight sites display a simple 404 page by default. +You can customize this by adding a `404.md` (or `404.mdx`) file to your `src/content/docs/` directory: + +<FileTree> + +- src/ + - content/ + - docs/ + - **404.md** + - index.md +- astro.config.mjs + +</FileTree> + +You can use all of Starlight’s page layout and customization techniques in your 404 page. For example, the default 404 page uses the [`splash` template](#page-layout) and [`hero`](/reference/frontmatter/#hero) component in frontmatter: + +```md +--- +title: '404' +template: splash +editUrl: false +hero: + title: '404' + tagline: Page not found. Check the URL or try using the search bar. +--- +``` + ## Custom CSS styles Customize the styles applied to your Starlight site by providing additional CSS files to modify or extend Starlight’s default styles. diff --git a/packages/starlight/404.astro b/packages/starlight/404.astro index 46cf5189..ecd95447 100644 --- a/packages/starlight/404.astro +++ b/packages/starlight/404.astro @@ -1,58 +1,39 @@ --- +import { getEntry } from 'astro:content'; import config from 'virtual:starlight/user-config'; -import { pathWithBase } from './utils/base'; +import EmptyContent from './components/EmptyMarkdown.md'; +import Page from './layout/Page.astro'; +import type { StarlightDocsEntry } from './utils/routing'; +import { useTranslations } from './utils/translations'; -// Built-in CSS styles. -import './style/props.css'; -import './style/reset.css'; -import './style/shiki.css'; -import './style/util.css'; - -// Layout -import PageFrame from './layout/PageFrame.astro'; - -// Components -import Header from './components/Header.astro'; -import MarkdownContent from './components/MarkdownContent.astro'; -import ThemeProvider from './components/ThemeProvider.astro'; -import SkipLink from './components/SkipLink.astro'; +const { lang = 'en', dir = 'ltr', locale } = config.defaultLocale || {}; +const entryMeta = { dir, lang, locale }; +const t = useTranslations(locale); -// Important that this is the last import so it can override built-in styles. -import 'virtual:starlight/user-css'; +const fallbackEntry: StarlightDocsEntry = { + slug: '404', + id: '404.md' as StarlightDocsEntry['id'], + body: '', + collection: 'docs', + data: { + title: '404', + template: 'splash', + editUrl: false, + head: [], + hero: { tagline: t('404.text'), actions: [] }, + }, + render: async () => ({ + Content: EmptyContent, + headings: [], + remarkPluginFrontmatter: {}, + }), +}; -const { lang = 'en', dir = 'ltr', locale } = config.defaultLocale || {}; +const userEntry = await getEntry('docs', '404'); +const entry = userEntry || fallbackEntry; +const { Content, headings } = await entry.render(); --- -<html lang={lang} dir={dir}> - <head> - <meta charset="utf-8" /> - <meta name="viewport" content="width=device-width" /> - <title>Not found</title> - </head> - <body> - <ThemeProvider /> - <SkipLink {locale} /> - <PageFrame {locale} hasSidebar={false}> - <Header slot="header" {locale} /> - <main> - <MarkdownContent> - <h1 id="_top" data-page-title>404</h1> - <p>Houston, we have a problem.</p> - <p> - We couldn’t find that link. Check the address or - <a href={pathWithBase('/')}>head back home</a>. - </p> - </MarkdownContent> - </main> - </PageFrame> - - <style> - main { - margin: auto; - padding: clamp(2rem, 10vmin, 6rem) var(--sl-nav-pad-x); - max-width: var(--sl-content-width); - max-width: max-content; - } - </style> - </body> -</html> +<Page {headings} entry={entry} slug={entry.slug} {...entryMeta} {entryMeta}> + <Content /> +</Page> diff --git a/packages/starlight/components/EmptyMarkdown.md b/packages/starlight/components/EmptyMarkdown.md new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/packages/starlight/components/EmptyMarkdown.md diff --git a/packages/starlight/index.astro b/packages/starlight/index.astro index 6b5193aa..65c19d85 100644 --- a/packages/starlight/index.astro +++ b/packages/starlight/index.astro @@ -1,122 +1,15 @@ --- import type { InferGetStaticPropsType } from 'astro'; -import config from 'virtual:starlight/user-config'; - -import { getSidebar } from './utils/navigation'; import { paths } from './utils/routing'; -// Built-in CSS styles. -import './style/props.css'; -import './style/reset.css'; -import './style/shiki.css'; -import './style/util.css'; - -// Components — can override built-in CSS, but not user CSS. -import ContentPanel from './components/ContentPanel.astro'; -import FallbackContentNotice from './components/FallbackContentNotice.astro'; -import HeadSEO from './components/HeadSEO.astro'; -import Header from './components/Header.astro'; -import Footer from './components/Footer.astro'; -import MarkdownContent from './components/MarkdownContent.astro'; -import RightSidebar from './components/RightSidebar.astro'; -import Sidebar from './components/Sidebar.astro'; -import SkipLink from './components/SkipLink.astro'; -import ThemeProvider from './components/ThemeProvider.astro'; -import PageFrame from './layout/PageFrame.astro'; -import TwoColumnContent from './layout/TwoColumnContent.astro'; -import Hero from './components/Hero.astro'; - -// Remark component CSS (needs to override `MarkdownContent.astro`) -import './style/asides.css'; - -// Important that this is the last import so it can override built-in styles. -import 'virtual:starlight/user-css'; +import Page from './layout/Page.astro'; export async function getStaticPaths() { return paths; } type Props = InferGetStaticPropsType<typeof getStaticPaths>; - -const { dir, entry, entryMeta, isFallback, lang, locale } = Astro.props; -const { Content, headings } = await entry.render(); -const sidebar = getSidebar(Astro.url.pathname, locale); - -const hasSidebar = entry.data.template !== 'splash'; -const tocConfig = !hasSidebar - ? false - : entry.data.tableOfContents !== undefined - ? entry.data.tableOfContents - : config.tableOfContents; -const hasToC = Boolean(tocConfig); -const hasHero = Boolean(entry.data.hero); +const { Content, headings } = await Astro.props.entry.render(); --- -<html lang={lang} dir={dir} data-has-toc={hasToC} data-has-sidebar={hasSidebar} data-has-hero={hasHero}> - <head> - <HeadSEO data={entry.data} lang={lang} /> - <style> - html:not([data-has-toc]) { - --sl-mobile-toc-height: 0rem; - } - html:not([data-has-sidebar]) { - --sl-content-width: 67.5rem; - } - /* Add scroll padding to ensure anchor headings aren't obscured by nav */ - html { - /* Additional padding is needed to account for the mobile TOC */ - scroll-padding-top: calc( - 1.5rem + var(--sl-nav-height) + var(--sl-mobile-toc-height) - ); - } - main { - padding-bottom: 3vh; - } - @media (min-width: 50em) { - [data-has-sidebar] { - --sl-content-inline-start: var(--sl-sidebar-width); - } - } - @media (min-width: 72em) { - html { - scroll-padding-top: calc(1.5rem + var(--sl-nav-height)); - } - } - </style> - </head> - <body> - <ThemeProvider /> - <SkipLink {locale} /> - <PageFrame {locale} {hasSidebar}> - <Header slot="header" {locale} /> - {hasSidebar && <Sidebar slot="sidebar" {sidebar} {locale} />} - <TwoColumnContent {hasToC}> - <RightSidebar slot="right-sidebar" {headings} {locale} {tocConfig} /> - <main data-pagefind-body lang={entryMeta.lang} dir={entryMeta.dir}> - {/* TODO: Revisit how this logic flows. */} - {entry.data.hero ? ( - <ContentPanel> - <Hero hero={entry.data.hero} fallbackTitle={entry.data.title} /> - <MarkdownContent><Content /></MarkdownContent> - </ContentPanel> - ) : ( - <ContentPanel> - <h1 - id="_top" - data-page-title - style="font-size: var(--sl-text-h1); line-height: var(--sl-line-height-headings); font-weight: 600; color: var(--sl-color-white); margin-top: 1rem;" - > - {entry.data.title} - </h1> - {isFallback && <FallbackContentNotice {locale} />} - </ContentPanel> - <ContentPanel> - <MarkdownContent><Content /></MarkdownContent> - <Footer {...{ entry, dir, lang, locale, sidebar }} /> - </ContentPanel> - )} - </main> - </TwoColumnContent> - </PageFrame> - </body> -</html> +<Page {...Astro.props} {headings}><Content /></Page> diff --git a/packages/starlight/layout/Page.astro b/packages/starlight/layout/Page.astro new file mode 100644 index 00000000..b09abe8c --- /dev/null +++ b/packages/starlight/layout/Page.astro @@ -0,0 +1,116 @@ +--- +import config from 'virtual:starlight/user-config'; +import type { MarkdownHeading } from 'astro'; +import { getSidebar } from '../utils/navigation'; +import type { Route } from '../utils/routing'; + +// Built-in CSS styles. +import '../style/props.css'; +import '../style/reset.css'; +import '../style/shiki.css'; +import '../style/util.css'; + +// Components — can override built-in CSS, but not user CSS. +import ContentPanel from '../components/ContentPanel.astro'; +import FallbackContentNotice from '../components/FallbackContentNotice.astro'; +import Footer from '../components/Footer.astro'; +import HeadSEO from '../components/HeadSEO.astro'; +import Header from '../components/Header.astro'; +import Hero from '../components/Hero.astro'; +import MarkdownContent from '../components/MarkdownContent.astro'; +import RightSidebar from '../components/RightSidebar.astro'; +import Sidebar from '../components/Sidebar.astro'; +import SkipLink from '../components/SkipLink.astro'; +import ThemeProvider from '../components/ThemeProvider.astro'; +import PageFrame from '../layout/PageFrame.astro'; +import TwoColumnContent from '../layout/TwoColumnContent.astro'; + +// Remark component CSS (needs to override `MarkdownContent.astro`) +import '../style/asides.css'; + +// Important that this is the last import so it can override built-in styles. +import 'virtual:starlight/user-css'; + +type Props = Route & { headings: MarkdownHeading[] }; + +const { dir, entry, entryMeta, headings, isFallback, lang, locale } = Astro.props; +const sidebar = getSidebar(Astro.url.pathname, locale); + +const hasSidebar = entry.data.template !== 'splash'; +const tocConfig = !hasSidebar + ? false + : entry.data.tableOfContents !== undefined + ? entry.data.tableOfContents + : config.tableOfContents; +const hasToC = Boolean(tocConfig); +const hasHero = Boolean(entry.data.hero); +--- + +<html lang={lang} dir={dir} data-has-toc={hasToC} data-has-sidebar={hasSidebar} data-has-hero={hasHero}> + <head> + <HeadSEO data={entry.data} lang={lang} /> + <style> + html:not([data-has-toc]) { + --sl-mobile-toc-height: 0rem; + } + html:not([data-has-sidebar]) { + --sl-content-width: 67.5rem; + } + /* Add scroll padding to ensure anchor headings aren't obscured by nav */ + html { + /* Additional padding is needed to account for the mobile TOC */ + scroll-padding-top: calc( + 1.5rem + var(--sl-nav-height) + var(--sl-mobile-toc-height) + ); + } + main { + padding-bottom: 3vh; + } + @media (min-width: 50em) { + [data-has-sidebar] { + --sl-content-inline-start: var(--sl-sidebar-width); + } + } + @media (min-width: 72em) { + html { + scroll-padding-top: calc(1.5rem + var(--sl-nav-height)); + } + } + </style> + </head> + <body> + <ThemeProvider /> + <SkipLink {locale} /> + <PageFrame {locale} {hasSidebar}> + <Header slot="header" {locale} /> + {hasSidebar && <Sidebar slot="sidebar" {sidebar} {locale} />} + <TwoColumnContent {hasToC}> + <RightSidebar slot="right-sidebar" {headings} {locale} {tocConfig} /> + <main data-pagefind-body={entry.slug !== '404'} lang={entryMeta.lang} dir={entryMeta.dir}> + {/* TODO: Revisit how this logic flows. */} + {entry.data.hero ? ( + <ContentPanel> + <Hero hero={entry.data.hero} fallbackTitle={entry.data.title} /> + <MarkdownContent><slot /></MarkdownContent> + </ContentPanel> + ) : ( + <ContentPanel> + <h1 + id="_top" + data-page-title + style="font-size: var(--sl-text-h1); line-height: var(--sl-line-height-headings); font-weight: 600; color: var(--sl-color-white); margin-top: 1rem;" + > + {entry.data.title} + </h1> + {isFallback && <FallbackContentNotice {locale} />} + </ContentPanel> + <ContentPanel> + <MarkdownContent><slot /></MarkdownContent> + <Footer {...{ entry, dir, lang, locale, sidebar }} /> + </ContentPanel> + )} + </main> + </TwoColumnContent> + </PageFrame> + </body> +</html>
\ No newline at end of file diff --git a/packages/starlight/schemas/i18n.ts b/packages/starlight/schemas/i18n.ts index be0aea70..4bac4bec 100644 --- a/packages/starlight/schemas/i18n.ts +++ b/packages/starlight/schemas/i18n.ts @@ -98,6 +98,10 @@ function starlightI18nSchema() { .describe( 'Label shown on the “next page” pagination arrow in the page footer.' ), + + '404.text': z + .string() + .describe('Text shown on Starlight’s default 404 page'), }) .partial(); } diff --git a/packages/starlight/translations/de.json b/packages/starlight/translations/de.json index fee8b961..da82f791 100644 --- a/packages/starlight/translations/de.json +++ b/packages/starlight/translations/de.json @@ -17,5 +17,6 @@ "page.editLink": "Seite bearbeiten", "page.lastUpdated": "Zuletzt bearbeitet:", "page.previousLink": "Vorherige Seite", - "page.nextLink": "Nächste Seite" + "page.nextLink": "Nächste Seite", + "404.text": "Seite nicht gefunden. Überprüfe die URL oder nutze die Suchleiste." } diff --git a/packages/starlight/translations/en.json b/packages/starlight/translations/en.json index 4bf800a5..5bc396b2 100644 --- a/packages/starlight/translations/en.json +++ b/packages/starlight/translations/en.json @@ -17,5 +17,6 @@ "page.editLink": "Edit page", "page.lastUpdated": "Last updated:", "page.previousLink": "Previous", - "page.nextLink": "Next" + "page.nextLink": "Next", + "404.text": "Page not found. Check the URL or try using the search bar." } diff --git a/packages/starlight/translations/es.json b/packages/starlight/translations/es.json index e03ae58d..8adbe302 100644 --- a/packages/starlight/translations/es.json +++ b/packages/starlight/translations/es.json @@ -17,5 +17,6 @@ "page.editLink": "Edita esta página", "page.lastUpdated": "Última actualización:", "page.previousLink": "Página anterior", - "page.nextLink": "Siguiente página" + "page.nextLink": "Siguiente página", + "404.text": "Página no encontrada. Verifique la URL o intente usar la barra de búsqueda." } diff --git a/packages/starlight/translations/fr.json b/packages/starlight/translations/fr.json index a1c437b7..345cb05c 100644 --- a/packages/starlight/translations/fr.json +++ b/packages/starlight/translations/fr.json @@ -17,5 +17,6 @@ "page.editLink": "Editer la page", "page.lastUpdated": "Dernière mise à jour :", "page.previousLink": "Précédent", - "page.nextLink": "Suivant" + "page.nextLink": "Suivant", + "404.text": "Page non trouvée. Vérifiez l'URL ou essayez d'utiliser la barre de recherche." } diff --git a/packages/starlight/translations/it.json b/packages/starlight/translations/it.json index b9991267..0560fb90 100644 --- a/packages/starlight/translations/it.json +++ b/packages/starlight/translations/it.json @@ -17,5 +17,6 @@ "page.editLink": "Modifica pagina", "page.lastUpdated": "Ultimo aggiornamento:", "page.previousLink": "Indietro", - "page.nextLink": "Avanti" + "page.nextLink": "Avanti", + "404.text": "Pagina non trovata. Verifica l'URL o prova a utilizzare la barra di ricerca." } diff --git a/packages/starlight/translations/ja.json b/packages/starlight/translations/ja.json index 75e2b06e..1f199b48 100644 --- a/packages/starlight/translations/ja.json +++ b/packages/starlight/translations/ja.json @@ -17,5 +17,6 @@ "page.editLink": "ページを編集", "page.lastUpdated": "最終更新日:", "page.previousLink": "前へ", - "page.nextLink": "次へ" + "page.nextLink": "次へ", + "404.text": "ページが見つかりません。 URL を確認するか、検索バーを使用してみてください。" } diff --git a/packages/starlight/translations/pt.json b/packages/starlight/translations/pt.json index 17908cd1..2b379035 100644 --- a/packages/starlight/translations/pt.json +++ b/packages/starlight/translations/pt.json @@ -17,5 +17,6 @@ "page.editLink": "Editar página", "page.lastUpdated": "Última atualização:", "page.previousLink": "Anterior", - "page.nextLink": "Próximo" + "page.nextLink": "Próximo", + "404.text": "Página não encontrada. Verifique o URL ou tente usar a barra de pesquisa." } |