Starlight shell
The Starlight docs shell: fixed header, version label, search, social icons, theme select, sidebar, table of contents, hero, page links and 404.
<starlight.Header site={starlight.Config{
Title: "Deedbox",
Version: "latest",
Links: []starlight.Link{{Label: "GitHub", Href: gx.URL("https://example.com/gx"), Icon: "github"}},
EditBase: "https://example.com/edit/main/content/docs/",
}} />Installation
Run the command in the app module.
gx add starlight-shellThe command also installs docs and starlight.
The command writes these files.
| File | Path in the app |
|---|---|
Header.fixtures.go |
ui/starlight-shell/Header.fixtures.go |
Header.gx |
ui/starlight-shell/Header.gx |
Hero.fixtures.go |
ui/starlight-shell/Hero.fixtures.go |
Hero.gx |
ui/starlight-shell/Hero.gx |
MobileToc.fixtures.go |
ui/starlight-shell/MobileToc.fixtures.go |
MobileToc.gx |
ui/starlight-shell/MobileToc.gx |
NotFound.fixtures.go |
ui/starlight-shell/NotFound.fixtures.go |
NotFound.gx |
ui/starlight-shell/NotFound.gx |
PageFooter.fixtures.go |
ui/starlight-shell/PageFooter.fixtures.go |
PageFooter.gx |
ui/starlight-shell/PageFooter.gx |
Pagination.fixtures.go |
ui/starlight-shell/Pagination.fixtures.go |
Pagination.gx |
ui/starlight-shell/Pagination.gx |
SearchDialog.fixtures.go |
ui/starlight-shell/SearchDialog.fixtures.go |
SearchDialog.gx |
ui/starlight-shell/SearchDialog.gx |
Shell.fixtures.go |
ui/starlight-shell/Shell.fixtures.go |
Shell.gx |
ui/starlight-shell/Shell.gx |
Sidebar.fixtures.go |
ui/starlight-shell/Sidebar.fixtures.go |
Sidebar.gx |
ui/starlight-shell/Sidebar.gx |
SidebarItem.fixtures.go |
ui/starlight-shell/SidebarItem.fixtures.go |
SidebarItem.gx |
ui/starlight-shell/SidebarItem.gx |
SiteTitle.fixtures.go |
ui/starlight-shell/SiteTitle.fixtures.go |
SiteTitle.gx |
ui/starlight-shell/SiteTitle.gx |
SocialIcons.fixtures.go |
ui/starlight-shell/SocialIcons.fixtures.go |
SocialIcons.gx |
ui/starlight-shell/SocialIcons.gx |
ThemeSelect.fixtures.go |
ui/starlight-shell/ThemeSelect.fixtures.go |
ThemeSelect.gx |
ui/starlight-shell/ThemeSelect.gx |
Toc.fixtures.go |
ui/starlight-shell/Toc.fixtures.go |
Toc.gx |
ui/starlight-shell/Toc.gx |
fixtures.go |
ui/starlight-shell/fixtures.go |
helpers.go |
ui/starlight-shell/helpers.go |
nav.go |
ui/starlight-shell/nav.go |
Install docs and starlight first.
Copy each file to its path in the app. Change each import of a registry package to the path of that package in the app.
package starlight
import "github.com/alternayte/gx"
var HeaderFixtures = gx.Fixtures[HeaderProps]{
"Default": {Site: fixtureSite},
}package starlight
props {
// Site is the site-wide data: the title, the versions and the social links.
Site Config
}
<header class="sl-header">
<div class="sl-header-inner">
<div class="sl-title-wrapper">
<SiteTitle site={p.Site} />
</div>
<div class="sl-header-search">
<button type="button" class="sl-search-button" data-gx-search-open aria-label="Search" aria-keyshortcuts="Control+K" aria-haspopup="dialog">
{icon("magnifier", "")}
<span aria-hidden="true">Search</span>
<kbd><kbd data-gx-mod-key>Ctrl</kbd><kbd>K</kbd></kbd>
</button>
</div>
<div class="sl-right-group">
<SocialIcons links={p.Site.Links} />
<ThemeSelect />
</div>
</div>
</header>package starlight
import "github.com/alternayte/gx"
var HeroFixtures = gx.Fixtures[HeroProps]{
"Default": {Title: "Deedbox", Tagline: "Event-source part of your app."},
"Title": {Title: "Deedbox"},
}package starlight
props {
// Title is the heading of the hero.
Title string
// Tagline is the text below the title. Empty omits it.
Tagline string = ""
// Actions is the row of link buttons below the tagline. Nil omits it.
Actions gx.Node = nil
// Image is the picture beside the text. Nil omits it.
Image gx.Node = nil
}
<div class="sl-hero">
if p.Image != nil {
<div class="sl-hero-image">{p.Image}</div>
}
<div class="sl-stack">
<div class="sl-copy">
<h1 id="_top">{p.Title}</h1>
if p.Tagline != "" {
<div class="sl-tagline">{p.Tagline}</div>
}
</div>
if p.Actions != nil {
<div class="sl-actions">{p.Actions}</div>
}
</div>
</div>package starlight
import "github.com/alternayte/gx"
var MobileTocFixtures = gx.Fixtures[MobileTocProps]{
"Default": {Headings: fixtureHeadings},
}package starlight
props {
// Headings holds the entries of the table of contents, below the entry "Overview" for the
// top of the page.
Headings []Heading
}
<div class="sl-mobile-toc">
<nav data-gx-toc aria-labelledby="sl-on-this-page-mobile">
<details data-gx-toc-menu>
<summary id="sl-on-this-page-mobile">
<span class="sl-toggle">
On this page
{icon("right-caret", "sl-caret")}
</span>
<span class="sl-display-current" data-gx-toc-current></span>
</summary>
<div class="sl-dropdown">
<ul class="sl-toc-list">
<li>
<a href="#_top" data-gx-toc-target="_top" data-level="2"><span>Overview</span></a>
</li>
for _, h := range p.Headings {
<li>
<a href={gx.URL("#" + h.ID)} data-gx-toc-target={h.ID} data-level={h.Level}><span>{h.Text}</span></a>
</li>
}
</ul>
</div>
</details>
</nav>
</div>package starlight
import "github.com/alternayte/gx"
var NotFoundFixtures = gx.Fixtures[NotFoundProps]{
"Default": {Site: fixtureSite},
}package starlight
props {
// Site is the site-wide data: the title, the versions and the social links.
Site Config
// Title is the heading of the page.
Title string = "404"
// Message is the text below the title.
Message string = "Page not found. Check the URL or try using the search bar."
}
<Shell site={p.Site} nav={Nav{}} page={Page{Title: p.Title, Splash: true}} hero={Hero(HeroProps{Title: p.Title, Tagline: p.Message})} children={gx.Text("")} />package starlight
import "github.com/alternayte/gx"
var PageFooterFixtures = gx.Fixtures[PageFooterProps]{
"Default": {Site: fixtureSite, Page: fixturePage},
}package starlight
props {
// Site is the site-wide data. Its EditBase builds the edit link; empty hides the link.
Site Config
// Page is the data of the current page: its path, its date and its previous and next
// items.
Page Page
}
<footer class="sl-footer">
<div class="sl-meta">
if p.Site.EditURL(p.Page) != "" {
<a class="sl-edit-link" href={gx.URL(p.Site.EditURL(p.Page))}>
{icon("pencil", "")}
Edit page
</a>
}
if p.Page.Updated != "" {
<p data-gx-updated>Last updated: <time>{p.Page.Updated}</time></p>
}
</div>
<Pagination prev={p.Page.Prev} next={p.Page.Next} />
</footer>package starlight
import "github.com/alternayte/gx"
var PaginationFixtures = gx.Fixtures[PaginationProps]{
"Both": {Prev: fixturePrev, Next: fixtureNext},
"Next": {Next: fixtureNext},
"Prev": {Prev: fixturePrev},
}package starlight
props {
// Prev is the previous sidebar item. Nil omits the link.
Prev *NavItem
// Next is the next sidebar item. Nil omits the link.
Next *NavItem
}
<div class="sl-pagination">
if p.Prev != nil {
<a href={p.Prev.Href} rel="prev">
{icon("left-arrow", "")}
<span>Previous<br /><span class="sl-link-title">{p.Prev.Label}</span></span>
</a>
}
if p.Next != nil {
<a href={p.Next.Href} rel="next">
{icon("right-arrow", "")}
<span>Next<br /><span class="sl-link-title">{p.Next.Label}</span></span>
</a>
}
</div>package starlight
import "github.com/alternayte/gx"
var SearchDialogFixtures = gx.Fixtures[SearchDialogProps]{
"Default": {},
}package starlight
<dialog id="gx-search" class="sl-search-dialog" data-gx-search data-gx-search-src="/pagefind/pagefind.js" aria-label="Search">
<div class="sl-search-frame">
<form method="get" action="/search" class="sl-search-form" data-gx-search-form>
<div class="sl-search-field">
{icon("magnifier", "")}
<input type="search" name="q" class="sl-search-input" placeholder="Search" aria-label="Search" data-gx-search-input autocomplete="off" />
</div>
<button type="button" class="sl-search-close" data-gx-search-close>Cancel</button>
</form>
<div class="sl-search-results" data-gx-search-results></div>
</div>
</dialog>package starlight
import "github.com/alternayte/gx"
var ShellFixtures = gx.Fixtures[ShellProps]{
"Default": {Site: fixtureSite, Nav: fixtureNav, Page: fixturePage, Children: gx.Text("Body.")},
"Splash": {
Site: fixtureSite,
Nav: fixtureNav,
Page: Page{Title: "Deedbox", Path: "/", Splash: true},
Hero: Hero(HeroProps{Title: "Deedbox", Tagline: "Event-source part of your app."}),
Children: gx.Text("Body."),
},
}package starlight
props {
// Site is the site-wide data: the title, the versions and the social links.
Site Config
// Nav is the sidebar model: the groups and their items.
Nav Nav
// Page is the data of the current page: its title, its path, its headings and its previous
// and next items. A page with Splash has no sidebar and no table of contents.
Page Page
// Hero is the hero of the page. It takes the place of the page title. Nil shows the title.
Hero gx.Node = nil
// Children is the content of the page.
Children gx.Node
}
<gx.Head htmlClass={RootClass(p.Page)} />
<a class="sl-skip-link" href="#_top">Skip to content</a>
<div class="sl-page">
<Header site={p.Site} />
if !p.Page.Splash {
<Sidebar site={p.Site} nav={p.Nav} path={p.Page.Path} />
}
<div class="sl-main-frame">
<div class="sl-two-column">
if len(p.Page.ShownTOC()) > 0 || !p.Page.Splash {
<aside class="sl-right-sidebar-container">
<div class="sl-right-sidebar">
<MobileToc headings={p.Page.ShownTOC()} />
<div class="sl-right-sidebar-panel">
<div class="sl-toc-container">
<Toc headings={p.Page.ShownTOC()} />
</div>
</div>
</div>
</aside>
}
<div class="sl-main-pane">
<main class="sl-main" data-pagefind-body>
if p.Hero != nil {
<div class="sl-content-panel">
<div class="sl-container">
{p.Hero}
<div class="sl-markdown-content gx-content">{p.Children}</div>
<PageFooter site={p.Site} page={p.Page} />
</div>
</div>
} else {
<div class="sl-content-panel">
<div class="sl-container">
<h1 id="_top" class="sl-page-title">{p.Page.Title}</h1>
</div>
</div>
<div class="sl-content-panel">
<div class="sl-container">
<div class="sl-markdown-content gx-content">{p.Children}</div>
<PageFooter site={p.Site} page={p.Page} />
</div>
</div>
}
</main>
</div>
</div>
</div>
</div>
<SearchDialog />package starlight
import "github.com/alternayte/gx"
var SidebarFixtures = gx.Fixtures[SidebarProps]{
"Default": {Site: fixtureSite, Nav: fixtureNav, Path: "/start/"},
}package starlight
props {
// Site is the site-wide data. The mobile menu shows its social links.
Site Config
// Nav is the sidebar model: the groups and their items.
Nav Nav
// Path is the site path of the current page. A closed group that holds the page starts
// open.
Path string
}
<nav class="sl-sidebar" aria-label="Main">
<button type="button" class="sl-menu-button" data-gx-menu aria-expanded="false" aria-controls="gx-sidebar">
{icon("bars", "sl-open-menu")}
{icon("close", "sl-close-menu")}
<span class="sr-only">Menu</span>
</button>
<div id="gx-sidebar" class="sl-sidebar-pane" data-gx-sidebar="50rem">
<div class="sl-sidebar-content">
<ul class="sl-top-level">
for _, g := range p.Nav.Groups {
<li>
<details open={GroupOpen(p.Path, g.Collapsed, g.Items)}>
<summary>
<span class="sl-group-label">
<span class="sl-large">{g.Label}</span>
if g.Badge != "" {
<span class="sl-badge">{g.Badge}</span>
}
</span>
{icon("right-caret", "sl-caret")}
</summary>
<ul>
for _, it := range g.Items {
<SidebarItem item={it} path={p.Path} />
}
</ul>
</details>
</li>
}
</ul>
<div class="sl-mobile-preferences">
<SocialIcons links={p.Site.Links} />
<ThemeSelect />
</div>
</div>
</div>
</nav>package starlight
import "github.com/alternayte/gx"
var SidebarItemFixtures = gx.Fixtures[SidebarItemProps]{
"Link": {Item: NavItem{Label: "Introduction", Href: gx.URL("/start/"), Badge: "New"}, Path: "/start/"},
"Group": {Item: NavItem{Label: "Errors", Items: []NavItem{{Label: "GX1000", Href: gx.URL("/errors/GX1000/")}}}},
"Closed": {Item: NavItem{Label: "Errors", Collapsed: true, Items: []NavItem{{Label: "GX1000", Href: gx.URL("/errors/GX1000/")}}}},
}package starlight
props {
// Item is the sidebar link. An item with nested items shows as a group that opens and
// closes.
Item NavItem
// Path is the site path of the current page. A closed group that holds the page starts
// open.
Path string
}
<li>
if len(p.Item.Items) > 0 {
<details open={GroupOpen(p.Path, p.Item.Collapsed, p.Item.Items)}>
<summary>
<span class="sl-group-label">
<span class="sl-large">{p.Item.Label}</span>
if p.Item.Badge != "" {
<span class="sl-badge">{p.Item.Badge}</span>
}
</span>
{icon("right-caret", "sl-caret")}
</summary>
<ul>
for _, child := range p.Item.Items {
<SidebarItem item={child} path={p.Path} />
}
</ul>
</details>
} else {
<a class="sl-nav-link" href={p.Item.Href} active="page">
<span>{p.Item.Label}</span>
if p.Item.Badge != "" {
<span class="sl-badge">{p.Item.Badge}</span>
}
</a>
}
</li>package starlight
import "github.com/alternayte/gx"
var SiteTitleFixtures = gx.Fixtures[SiteTitleProps]{
"Label": {Site: fixtureSite},
"Versions": {Site: fixtureVersions},
"NoLabel": {Site: Config{Title: "Deedbox"}},
}package starlight
props {
// Site is the site-wide data. With two or more Versions, a select shows after the title;
// with fewer, the Version label shows.
Site Config
}
<div class="sl-title-row">
<a class="sl-site-title" href={gx.URL("/")}>
<span translate="no">{p.Site.Title}</span>
</a>
if len(p.Site.Versions) > 1 {
<select class="sl-version-select" aria-label="Documentation version" data-gx-goto autocomplete="off">
for _, v := range p.Site.Versions {
<option value={string(v.Href)} selected={v.Label == p.Site.Version}>{v.Label}</option>
}
</select>
} else if p.Site.Version != "" {
<span class="sl-version" data-gx-version>{p.Site.Version}</span>
}
</div>package starlight
import "github.com/alternayte/gx"
var SocialIconsFixtures = gx.Fixtures[SocialIconsProps]{
"Default": {Links: fixtureSite.Links},
}package starlight
props {
// Links holds the social links. Each one shows the built-in icon that its Icon names.
Links []Link
}
<div class="sl-social-icons">
for _, l := range p.Links {
<a class="sl-social-link" href={l.Href} rel="me">
<span class="sr-only">{l.Label}</span>
{icon(l.Icon, "")}
</a>
}
</div>package starlight
import "github.com/alternayte/gx"
var ThemeSelectFixtures = gx.Fixtures[ThemeSelectProps]{
"Default": {},
}package starlight
<label class="sl-select">
<span class="sr-only">Select theme</span>
{icon("laptop", "sl-select-icon")}
<select data-gx-theme="select" autocomplete="off">
<option value="dark">Dark</option>
<option value="light">Light</option>
<option value="auto" selected>Auto</option>
</select>
{icon("down-caret", "sl-select-caret")}
</label>package starlight
import "github.com/alternayte/gx"
var TocFixtures = gx.Fixtures[TocProps]{
"Default": {Headings: fixtureHeadings},
}package starlight
props {
// Headings holds the entries of the table of contents, below the entry "Overview" for the
// top of the page.
Headings []Heading
}
<nav class="sl-toc" data-gx-toc aria-labelledby="sl-on-this-page">
<h2 id="sl-on-this-page">On this page</h2>
<ul class="sl-toc-list">
<li>
<a href="#_top" data-gx-toc-target="_top" data-level="2"><span>Overview</span></a>
</li>
for _, h := range p.Headings {
<li>
<a href={gx.URL("#" + h.ID)} data-gx-toc-target={h.ID} data-level={h.Level}><span>{h.Text}</span></a>
</li>
}
</ul>
</nav>package starlight
import "github.com/alternayte/gx"
// fixtureNav is the sidebar model of the fixtures: two open groups, with a
// nested group that starts closed.
var fixtureNav = Nav{Groups: []NavGroup{
{Label: "Start", Items: []NavItem{
{Label: "Introduction", Href: gx.URL("/start/"), Badge: "New"},
{Label: "Install", Href: gx.URL("/start/install/")},
}},
{Label: "Reference", Items: []NavItem{
{Label: "Configuration", Href: gx.URL("/reference/configuration/")},
{Label: "Errors", Collapsed: true, Items: []NavItem{
{Label: "GX1000", Href: gx.URL("/errors/GX1000/")},
}},
}},
}}
// fixtureSite is the site config of the fixtures.
var fixtureSite = Config{
Title: "Deedbox",
Version: "latest",
Links: []Link{{Label: "GitHub", Href: gx.URL("https://example.com/gx"), Icon: "github"}},
EditBase: "https://example.com/edit/main/content/docs/",
}
// fixtureVersions is the site config of the fixture with a version select.
var fixtureVersions = Config{
Title: "Deedbox",
Version: "latest",
Versions: []Version{
{Label: "latest", Href: gx.URL("https://example.com/")},
{Label: "0.4", Href: gx.URL("https://example.com/0.4/")},
},
}
// fixtureNext is the next page of the fixtures.
var fixtureNext = &NavItem{Label: "Install", Href: gx.URL("/start/install/")}
// fixturePrev is the previous page of the fixtures.
var fixturePrev = &NavItem{Label: "Introduction", Href: gx.URL("/start/")}
// fixtureHeadings are the headings of the fixtures.
var fixtureHeadings = []Heading{
{Level: 2, Text: "Install", ID: "install"},
{Level: 3, Text: "First page", ID: "first-page"},
}
// fixturePage is the page data of the fixtures.
var fixturePage = Page{
Title: "Introduction",
Path: "/start/",
Updated: "Oct 2, 2026",
Next: fixtureNext,
TOC: fixtureHeadings,
}package starlight
import (
"strings"
"github.com/alternayte/gx"
"github.com/alternayte/gx/registry/docs"
)
// icon renders one built-in icon of the docs kit at the size of the text.
func icon(name docs.IconName, class string) gx.Node {
return gx.Icon(string(docs.IconBody(name)), gx.IconProps{Class: gx.Cx("sl-icon", class)})
}
// Active reports whether href is the current page.
func Active(path string, href string) bool {
return href != "" && path == href
}
// ActiveItem reports whether item or one of its descendants is the current
// page.
func ActiveItem(path string, item NavItem) bool {
if Active(path, string(item.Href)) {
return true
}
for _, child := range item.Items {
if ActiveItem(path, child) {
return true
}
}
return false
}
// GroupOpen reports whether a sidebar group starts open: it is not
// collapsed, or it holds the current page.
func GroupOpen(path string, collapsed bool, items []NavItem) bool {
if !collapsed {
return true
}
for _, item := range items {
if ActiveItem(path, item) {
return true
}
}
return false
}
// TocDepth returns the heading depth limit of the page.
func (p Page) TocDepth() int {
if p.Depth <= 0 {
return 3
}
return p.Depth
}
// ShownTOC returns the headings that the table of contents shows. A splash
// page has none.
func (p Page) ShownTOC() []Heading {
if p.Splash {
return nil
}
depth := p.TocDepth()
var out []Heading
for _, h := range p.TOC {
if h.Level >= 2 && h.Level <= depth && h.Text != "" {
out = append(out, h)
}
}
return out
}
// RootClass returns the classes of the html element. The theme sets the
// widths of the layout from them.
func RootClass(p Page) string {
if p.Splash {
return ""
}
// A page with a sidebar always has the entry "Overview" in its table
// of contents.
return "sl-has-sidebar sl-has-toc"
}
// EditURL returns the edit link of the page. The page path maps to its
// Markdown file: "/start/" to "start.md".
func (c Config) EditURL(p Page) string {
if c.EditBase == "" || p.Path == "" {
return ""
}
base := c.EditBase
if base[len(base)-1] != '/' {
base += "/"
}
path := strings.TrimPrefix(p.Path, "/")
path = strings.TrimSuffix(path, "/")
if path == "" {
path = "index"
}
return base + path + ".md"
}// Package starlight is the starlight-shell registry block: the page
// structure of the default Starlight theme. It has a fixed header, a
// sidebar, a table of contents, a hero, page links and a search dialog.
// The `starlight` theme styles it; the `docs` kit holds its icons.
package starlight
import (
"github.com/alternayte/gx"
"github.com/alternayte/gx/registry/docs"
)
// Nav is the sidebar model.
type Nav struct {
Groups []NavGroup
}
// NavGroup is one top-level sidebar section. A group with Collapsed starts
// closed, unless it holds the current page.
type NavGroup struct {
Label string
Badge string
Collapsed bool
Items []NavItem
}
// NavItem is one sidebar link. An item with Items is a nested group; a
// nested group with Collapsed starts closed, unless it holds the current
// page.
type NavItem struct {
Label string
Href gx.URL
Badge string
Collapsed bool
Items []NavItem
}
// Link is one social link of the header. Icon is the name of a built-in
// icon of the docs kit, for example "github".
type Link struct {
Label string
Href gx.URL
Icon docs.IconName
}
// Version is one version of the docs: its label and the address of its
// site.
type Version struct {
Label string
Href gx.URL
}
// Config is the site-wide shell data.
type Config struct {
Title string
// Version is the label of the version that this site shows. The header
// shows it after the title. Empty hides it.
Version string
// Versions lists the versions of the docs. With two or more, the
// header shows a select in place of the label, and a choice opens the
// site of that version.
Versions []Version
Links []Link
// EditBase is the repository edit URL up to the file path, for example
// https://github.com/acme/site/edit/main/content/docs/. Empty hides
// the edit link.
EditBase string
}
// Page is the shell data of one page.
type Page struct {
Title string
// Path is the site path of the page, for example /start/.
Path string
// Splash is true for a page with no sidebar and no table of contents,
// such as a home page.
Splash bool
// Updated is the date of the last change. Empty hides it.
Updated string
// Prev and Next are the previous and next sidebar items.
Prev *NavItem
Next *NavItem
// TOC holds the page headings, in document order.
TOC []Heading
// Depth limits the table of contents to headings at or below this
// level. Zero means 3.
Depth int
}
// Heading is one table-of-contents entry.
type Heading struct {
Level int
Text string
ID string
}The theme must define these tokens: --sl-color-bg, --sl-color-text, --sl-color-text-accent, --sl-nav-height, --sl-sidebar-width, --sl-content-width.
Usage
The page structure of the default Starlight theme. It has these parts:
- A fixed header with the site title, a version label, the search button, social icons and a theme select.
- A sidebar and a table of contents.
- A hero for a splash page.
- Page links, a search dialog and a 404 page.
The block needs the starlight theme for its look and the docs kit for
its icons.
var Site = starlight.Config{
Title: "Deedbox",
Version: "latest",
Links: []starlight.Link{{Label: "GitHub", Href: gx.URL("https://github.com/acme/site"), Icon: "github"}},
EditBase: "https://github.com/acme/site/edit/main/content/docs/",
}
func View(e gx.Entry[DocMeta]) gx.Node {
return starlight.Shell(starlight.ShellProps{Site: Site, Nav: Nav(), Page: PageFor(e), Children: DocsBody(e)})
}A splash page has no sidebar and no table of contents. Give it a hero:
starlight.Shell(starlight.ShellProps{
Site: Site,
Page: starlight.Page{Title: "Deedbox", Path: "/", Splash: true},
Hero: starlight.Hero(starlight.HeroProps{
Title: "Deedbox",
Tagline: "Event-source part of your app.",
Actions: docs.LinkButton(docs.LinkButtonProps{Href: gx.URL("/start/"), Icon: "right-arrow", Children: gx.Text("Start")}),
}),
Children: DocsBody(e),
})With two or more Versions in the config, the header shows a select. A
choice opens the site of that version.
Examples
Header: Default
<starlight.Header site={starlight.Config{
Title: "Deedbox",
Version: "latest",
Links: []starlight.Link{{Label: "GitHub", Href: gx.URL("https://example.com/gx"), Icon: "github"}},
EditBase: "https://example.com/edit/main/content/docs/",
}} />Hero: Default
<starlight.Hero title="Deedbox" tagline="Event-source part of your app." />Hero: Title
<starlight.Hero title="Deedbox" />MobileToc: Default
<starlight.MobileToc headings={[]starlight.Heading{
{Level: 2, Text: "Install", ID: "install"},
{Level: 3, Text: "First page", ID: "first-page"},
}} />NotFound: Default
<starlight.NotFound site={starlight.Config{
Title: "Deedbox",
Version: "latest",
Links: []starlight.Link{{Label: "GitHub", Href: gx.URL("https://example.com/gx"), Icon: "github"}},
EditBase: "https://example.com/edit/main/content/docs/",
}} />PageFooter: Default
<starlight.PageFooter site={starlight.Config{
Title: "Deedbox",
Version: "latest",
Links: []starlight.Link{{Label: "GitHub", Href: gx.URL("https://example.com/gx"), Icon: "github"}},
EditBase: "https://example.com/edit/main/content/docs/",
}} page={starlight.Page{
Title: "Introduction",
Path: "/start/",
Updated: "Oct 2, 2026",
Next: &starlight.NavItem{Label: "Install", Href: gx.URL("/start/install/")},
TOC: []starlight.Heading{
{Level: 2, Text: "Install", ID: "install"},
{Level: 3, Text: "First page", ID: "first-page"},
},
}} />Pagination: Both
<starlight.Pagination prev={&starlight.NavItem{Label: "Introduction", Href: gx.URL("/start/")}} next={&starlight.NavItem{Label: "Install", Href: gx.URL("/start/install/")}} />Pagination: Next
<starlight.Pagination next={&starlight.NavItem{Label: "Install", Href: gx.URL("/start/install/")}} prev={(*starlight.NavItem)(nil)} />Pagination: Prev
<starlight.Pagination prev={&starlight.NavItem{Label: "Introduction", Href: gx.URL("/start/")}} next={(*starlight.NavItem)(nil)} />SearchDialog: Default
<starlight.SearchDialog />Shell: Default
<starlight.Shell site={starlight.Config{
Title: "Deedbox",
Version: "latest",
Links: []starlight.Link{{Label: "GitHub", Href: gx.URL("https://example.com/gx"), Icon: "github"}},
EditBase: "https://example.com/edit/main/content/docs/",
}} nav={starlight.Nav{Groups: []starlight.NavGroup{
{Label: "Start", Items: []starlight.NavItem{
{Label: "Introduction", Href: gx.URL("/start/"), Badge: "New"},
{Label: "Install", Href: gx.URL("/start/install/")},
}},
{Label: "Reference", Items: []starlight.NavItem{
{Label: "Configuration", Href: gx.URL("/reference/configuration/")},
{Label: "Errors", Collapsed: true, Items: []starlight.NavItem{
{Label: "GX1000", Href: gx.URL("/errors/GX1000/")},
}},
}},
}}} page={starlight.Page{
Title: "Introduction",
Path: "/start/",
Updated: "Oct 2, 2026",
Next: &starlight.NavItem{Label: "Install", Href: gx.URL("/start/install/")},
TOC: []starlight.Heading{
{Level: 2, Text: "Install", ID: "install"},
{Level: 3, Text: "First page", ID: "first-page"},
},
}}>
Body.
</starlight.Shell>Shell: Splash
<starlight.Shell site={starlight.Config{
Title: "Deedbox",
Version: "latest",
Links: []starlight.Link{{Label: "GitHub", Href: gx.URL("https://example.com/gx"), Icon: "github"}},
EditBase: "https://example.com/edit/main/content/docs/",
}} nav={starlight.Nav{Groups: []starlight.NavGroup{
{Label: "Start", Items: []starlight.NavItem{
{Label: "Introduction", Href: gx.URL("/start/"), Badge: "New"},
{Label: "Install", Href: gx.URL("/start/install/")},
}},
{Label: "Reference", Items: []starlight.NavItem{
{Label: "Configuration", Href: gx.URL("/reference/configuration/")},
{Label: "Errors", Collapsed: true, Items: []starlight.NavItem{
{Label: "GX1000", Href: gx.URL("/errors/GX1000/")},
}},
}},
}}} page={starlight.Page{Title: "Deedbox", Path: "/", Splash: true}}>
<:hero><starlight.Hero title="Deedbox" tagline="Event-source part of your app." /></:hero>
Body.
</starlight.Shell>Sidebar: Default
<starlight.Sidebar site={starlight.Config{
Title: "Deedbox",
Version: "latest",
Links: []starlight.Link{{Label: "GitHub", Href: gx.URL("https://example.com/gx"), Icon: "github"}},
EditBase: "https://example.com/edit/main/content/docs/",
}} nav={starlight.Nav{Groups: []starlight.NavGroup{
{Label: "Start", Items: []starlight.NavItem{
{Label: "Introduction", Href: gx.URL("/start/"), Badge: "New"},
{Label: "Install", Href: gx.URL("/start/install/")},
}},
{Label: "Reference", Items: []starlight.NavItem{
{Label: "Configuration", Href: gx.URL("/reference/configuration/")},
{Label: "Errors", Collapsed: true, Items: []starlight.NavItem{
{Label: "GX1000", Href: gx.URL("/errors/GX1000/")},
}},
}},
}}} path="/start/" />SidebarItem: Link
<starlight.SidebarItem item={starlight.NavItem{Label: "Introduction", Href: gx.URL("/start/"), Badge: "New"}} path="/start/" />SidebarItem: Group
<starlight.SidebarItem item={starlight.NavItem{Label: "Errors", Items: []starlight.NavItem{{Label: "GX1000", Href: gx.URL("/errors/GX1000/")}}}} path="" />SidebarItem: Closed
<starlight.SidebarItem item={starlight.NavItem{Label: "Errors", Collapsed: true, Items: []starlight.NavItem{{Label: "GX1000", Href: gx.URL("/errors/GX1000/")}}}} path="" />SiteTitle: Label
<starlight.SiteTitle site={starlight.Config{
Title: "Deedbox",
Version: "latest",
Links: []starlight.Link{{Label: "GitHub", Href: gx.URL("https://example.com/gx"), Icon: "github"}},
EditBase: "https://example.com/edit/main/content/docs/",
}} />SiteTitle: Versions
<starlight.SiteTitle site={starlight.Config{
Title: "Deedbox",
Version: "latest",
Versions: []starlight.Version{
{Label: "latest", Href: gx.URL("https://example.com/")},
{Label: "0.4", Href: gx.URL("https://example.com/0.4/")},
},
}} />SiteTitle: No label
<starlight.SiteTitle site={starlight.Config{Title: "Deedbox"}} />SocialIcons: Default
<starlight.SocialIcons links={starlight.Config{
Title: "Deedbox",
Version: "latest",
Links: []starlight.Link{{Label: "GitHub", Href: gx.URL("https://example.com/gx"), Icon: "github"}},
EditBase: "https://example.com/edit/main/content/docs/",
}.Links} />ThemeSelect: Default
<starlight.ThemeSelect />Toc: Default
<starlight.Toc headings={[]starlight.Heading{
{Level: 2, Text: "Install", ID: "install"},
{Level: 3, Text: "First page", ID: "first-page"},
}} />API reference
A tag sets a prop by its name with a lower-case first letter: Class is class.
starlight.Header
| Prop | Type | Default | Description |
|---|---|---|---|
Site |
Config |
Required | Site is the site-wide data: the title, the versions and the social links. |
starlight.Hero
| Prop | Type | Default | Description |
|---|---|---|---|
Title |
string |
Required | Title is the heading of the hero. |
Tagline |
string |
"" |
Tagline is the text below the title. Empty omits it. |
Actions |
gx.Node |
nil |
Actions is the row of link buttons below the tagline. Nil omits it. |
Image |
gx.Node |
nil |
Image is the picture beside the text. Nil omits it. |
starlight.MobileToc
| Prop | Type | Default | Description |
|---|---|---|---|
Headings |
[]Heading |
Required | Headings holds the entries of the table of contents, below the entry "Overview" for the top of the page. |
starlight.NotFound
| Prop | Type | Default | Description |
|---|---|---|---|
Site |
Config |
Required | Site is the site-wide data: the title, the versions and the social links. |
Title |
string |
"404" |
Title is the heading of the page. |
Message |
string |
"Page not found. Check the URL or try using the search bar." |
Message is the text below the title. |
starlight.PageFooter
| Prop | Type | Default | Description |
|---|---|---|---|
Site |
Config |
Required | Site is the site-wide data. Its EditBase builds the edit link; empty hides the link. |
Page |
Page |
Required | Page is the data of the current page: its path, its date and its previous and next items. |
starlight.Pagination
| Prop | Type | Default | Description |
|---|---|---|---|
Prev |
*NavItem |
Required | Prev is the previous sidebar item. Nil omits the link. |
Next |
*NavItem |
Required | Next is the next sidebar item. Nil omits the link. |
starlight.SearchDialog
The component has no props.
starlight.Shell
| Prop | Type | Default | Description |
|---|---|---|---|
Site |
Config |
Required | Site is the site-wide data: the title, the versions and the social links. |
Nav |
Nav |
Required | Nav is the sidebar model: the groups and their items. |
Page |
Page |
Required | Page is the data of the current page: its title, its path, its headings and its previous and next items. A page with Splash has no sidebar and no table of contents. |
Hero |
gx.Node |
nil |
Hero is the hero of the page. It takes the place of the page title. Nil shows the title. |
Children |
gx.Node |
Required | Children is the content of the page. |
starlight.Sidebar
| Prop | Type | Default | Description |
|---|---|---|---|
Site |
Config |
Required | Site is the site-wide data. The mobile menu shows its social links. |
Nav |
Nav |
Required | Nav is the sidebar model: the groups and their items. |
Path |
string |
Required | Path is the site path of the current page. A closed group that holds the page starts open. |
starlight.SidebarItem
| Prop | Type | Default | Description |
|---|---|---|---|
Item |
NavItem |
Required | Item is the sidebar link. An item with nested items shows as a group that opens and closes. |
Path |
string |
Required | Path is the site path of the current page. A closed group that holds the page starts open. |
starlight.SiteTitle
| Prop | Type | Default | Description |
|---|---|---|---|
Site |
Config |
Required | Site is the site-wide data. With two or more Versions, a select shows after the title; with fewer, the Version label shows. |
starlight.SocialIcons
| Prop | Type | Default | Description |
|---|---|---|---|
Links |
[]Link |
Required | Links holds the social links. Each one shows the built-in icon that its Icon names. |
starlight.ThemeSelect
The component has no props.
starlight.Toc
| Prop | Type | Default | Description |
|---|---|---|---|
Headings |
[]Heading |
Required | Headings holds the entries of the table of contents, below the entry "Overview" for the top of the page. |
Do and do not
Do
- Keep the page
Pathwith a trailing slash. The sidebar marks the item with the same path. - Name a built-in icon of the docs kit for each social link, for example
github. - Set
Collapsedon a long nested group. The group opens when it holds the current page.
Don't
- Do not use the block without the
starlighttheme. The markup has no utility classes. - Do not put a
Tocor aSidebaroutside theShell.
Keyboard
| Key | Action |
|---|---|
| Tab | Moves through the skip link, the header, the sidebar, the content and the table of contents. |
| Enter, Space | Opens the search dialog, the mobile menu, a sidebar group or the table of contents menu. |
| Ctrl/Cmd K | Opens the search dialog. |
| Escape | Closes the search dialog and the table of contents menu. |
| Arrow up, Arrow down | Changes the theme or the version in its select. |