open-design/apps/landing-page/app/_components/sub-page-layout.astro
ashleyashli 558fedd207
fix(landing): wire GA4 rollout config (#2615)
Co-authored-by: ashley li <ashleyli@ashleydeMacBook-Air-2.local>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-22 14:56:58 +08:00

125 lines
4.6 KiB
Text

---
/*
* Shared shell for every sub-page outside of `/` (Skills, Systems,
* Craft, Templates and their detail pages).
*
* The homepage (`/`) intentionally does NOT use this layout — its
* chrome (rails, topbar, full hero, mega-word footer) stays in
* lockstep with `design-templates/open-design-landing/example.html`. This
* layout is the lighter sibling: same Atelier Zero tokens, same
* sticky nav, but no editorial side rails or hero, and a compact
* footer focused on the site map.
*
* Every sub-page passes `title`, `description`, `active` (nav
* highlight) and an optional `jsonLd`. Catalog counts are read from
* `getCatalogCounts()` so the nav badges stay live.
*/
import '../globals.css';
import '../sub-pages.css';
import { createElement } from 'react';
import { renderToStaticMarkup } from 'react-dom/server';
import FaviconLinks from './favicon-links.astro';
import GoogleAnalytics from './google-analytics.astro';
import ResourceHints from './resource-hints.astro';
import HeaderEnhancer from './header-enhancer.astro';
import { Header, type HeaderProps } from './header';
import LocaleSwitcherScript from './locale-switcher-script.astro';
import PreciseLazyload from './precise-lazyload.astro';
import SiteFooter from './site-footer.astro';
import Topbar from './topbar.astro';
import { heroImage } from '../image-assets';
import {
LANDING_LOCALES,
alternateLinksForPath,
getLocaleDefinition,
localeFromPath,
} from '../i18n';
import { getCatalogCounts } from '../_lib/catalog';
import { getGithubRepoMeta } from '../_lib/github';
export interface Props {
title: string;
description: string;
active?: HeaderProps['active'];
ogImage?: string;
jsonLd?: Record<string, unknown> | Array<Record<string, unknown>>;
}
const { title, description, active = 'home', ogImage, jsonLd } = Astro.props;
const locale = localeFromPath(Astro.url.pathname);
const localeDef = getLocaleDefinition(locale);
const canonical = new URL(Astro.url.pathname, Astro.site).toString();
const alternateLinks = alternateLinksForPath(Astro.url.pathname).map((entry) => ({
...entry,
href: new URL(entry.hrefPath, Astro.site).toString(),
}));
const xDefaultHref = new URL(alternateLinks[0]!.hrefPath, Astro.site).toString();
const og = ogImage ?? heroImage;
const counts = await getCatalogCounts();
const github = await getGithubRepoMeta();
const headerHtml = renderToStaticMarkup(
Header({ active, counts, github, brandHref: '/', locale }) as ReturnType<typeof createElement>,
);
const ldArray = jsonLd ? (Array.isArray(jsonLd) ? jsonLd : [jsonLd]) : [];
---
<!doctype html>
<html lang={localeDef.htmlLang} dir={localeDef.dir}>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#efe7d2" />
<title>{title}</title>
<meta name="description" content={description} />
<link rel="canonical" href={canonical} />
{alternateLinks.map((entry) => (
<link rel="alternate" hreflang={entry.hreflang} href={entry.href} />
))}
<link rel="alternate" hreflang="x-default" href={xDefaultHref} />
<FaviconLinks />
<ResourceHints />
<GoogleAnalytics />
<meta property="og:type" content="website" />
<meta property="og:site_name" content="Open Design" />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:url" content={canonical} />
<meta property="og:image" content={og} />
<meta property="og:locale" content={localeDef.ogLocale} />
{LANDING_LOCALES.filter((entry) => entry.code !== locale).map((entry) => (
<meta property="og:locale:alternate" content={entry.ogLocale} />
))}
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={title} />
<meta name="twitter:description" content={description} />
<meta name="twitter:image" content={og} />
{ldArray.map((data) => (
<script is:inline type="application/ld+json" set:html={JSON.stringify(data)} />
))}
</head>
<body class="sub-page">
<div class="shell">
<div class="site-chrome" data-chrome-headroom>
<Topbar github={github} locale={locale} />
{/* Same React-rendered Header used by the homepage. SSR'd here
* so we have one nav implementation that handles active state. */}
<Fragment set:html={headerHtml} />
</div>
<main class="sub-main container">
<slot />
</main>
<SiteFooter counts={counts} locale={locale} />
</div>
<HeaderEnhancer />
<LocaleSwitcherScript />
<PreciseLazyload />
</body>
</html>