Summary
The homepage <h1> is just the brand word "Aspire" and carries no descriptive keywords. The H1 is the strongest on-page relevance signal. The <title> already carries descriptive copy ("Aspire — Your stack, streamlined") while the H1 does not.
Evidence
- Live
https://aspire.dev/ renders <h1 id="_top" data-page-title>Aspire</h1>.
- The
<title> is separately set to Aspire — Your stack, streamlined via the head frontmatter override in index.mdx, so title and H1 already diverge — only the H1 is undescriptive.
Where the change goes
The splash hero H1 is produced by the custom Hero override:
src/frontend/src/components/starlight/Hero.astro
- line 18:
const { title = data.title, tagline, image, actions = [] } = data.hero || {};
- line 68:
<h1 id={PAGE_TITLE_ID} data-page-title set:html={title} />
Because the hero title resolves to data.hero.title ?? data.title, the surgical fix is to set an explicit hero.title in the homepage frontmatter:
src/frontend/src/content/docs/index.mdx — add a title under the existing hero: block, e.g.:
hero:
title: Aspire — local dev-time orchestration for distributed apps
tagline: Your stack, streamlined. <p>…</p>
This changes only the visible H1. data.title stays Aspire, so <title>, og:title (resolveOgTitle), breadcrumbs/nav, and the JSON-LD headline are unaffected. Existing unit tests that assert resolveOgTitle(route, 'index') === 'Aspire' (tests/unit/page-metadata.vitest.test.ts) continue to pass, and the e2e tests only assert the H1 is visible (not its text). Keep a single H1; the 10 existing H2s stay.
Note: localized homepages (src/frontend/src/content/docs/<locale>/index.mdx) share the same brand-only H1 and can be updated via the Lunaria translation workflow. The exact wording above is a suggestion — tune the keywords as desired.
Done when
- View-source of
/ shows a single, descriptive, keyword-bearing <h1>.
Verify
- Non-brand impressions in Google Search Console trend up over ~2–4 weeks.
Source: SEO audit of https://aspire.dev/ (2026-07-29). Priority: High.
Summary
The homepage
<h1>is just the brand word "Aspire" and carries no descriptive keywords. The H1 is the strongest on-page relevance signal. The<title>already carries descriptive copy ("Aspire — Your stack, streamlined") while the H1 does not.Evidence
https://aspire.dev/renders<h1 id="_top" data-page-title>Aspire</h1>.<title>is separately set toAspire — Your stack, streamlinedvia theheadfrontmatter override inindex.mdx, so title and H1 already diverge — only the H1 is undescriptive.Where the change goes
The splash hero H1 is produced by the custom Hero override:
src/frontend/src/components/starlight/Hero.astroconst { title = data.title, tagline, image, actions = [] } = data.hero || {};<h1 id={PAGE_TITLE_ID} data-page-title set:html={title} />Because the hero title resolves to
data.hero.title ?? data.title, the surgical fix is to set an explicithero.titlein the homepage frontmatter:src/frontend/src/content/docs/index.mdx— add atitleunder the existinghero:block, e.g.:This changes only the visible H1.
data.titlestaysAspire, so<title>,og:title(resolveOgTitle), breadcrumbs/nav, and the JSON-LDheadlineare unaffected. Existing unit tests that assertresolveOgTitle(route, 'index') === 'Aspire'(tests/unit/page-metadata.vitest.test.ts) continue to pass, and the e2e tests only assert the H1 is visible (not its text). Keep a single H1; the 10 existing H2s stay.Done when
/shows a single, descriptive, keyword-bearing<h1>.Verify
Source: SEO audit of
https://aspire.dev/(2026-07-29). Priority: High.