SPB Git forge
28commits 1branches 0releases
7.7 MBsize
maindefault branch
10 days agolast push
Python 66.3% TypeScript 22.7% JavaScript 8.6% HTML 1.4% CSS 0.7%
3.0 KB · 70 lines tsx
Raw Blame History
1import type { Metadata, Viewport } from 'next';2import Script from 'next/script';3import './globals.css';4import { EventDrawer } from '@/components/events/event-drawer';5import { EventDrawerProvider } from '@/components/events/event-drawer-context';6import { MobileTabBar } from '@/components/layout/mobile-tab-bar';7import { SearchProvider } from '@/components/layout/search-context';8import { SearchDialog } from '@/components/layout/search-dialog';9import { SiteFooter } from '@/components/layout/site-footer';10import { SiteHeader } from '@/components/layout/site-header';11import { fontMono, fontUi } from '@/lib/fonts';12import { PREPAINT_SCRIPT } from '@/lib/prepaint';13import { DESCRIPTION, SITE_NAME, SITE_URL, TAGLINE, THEME_DARK, THEME_LIGHT } from '@/lib/site';1415export const metadata: Metadata = {16  metadataBase: new URL(SITE_URL),17  title: { default: `${SITE_NAME} — ${TAGLINE}`, template: `%s | ${SITE_NAME}` },18  description: DESCRIPTION,19  applicationName: SITE_NAME,20  robots: { index: true, follow: true },21  alternates: { canonical: '/' },22  openGraph: { type: 'website', siteName: SITE_NAME, url: SITE_URL, title: `${SITE_NAME} — ${TAGLINE}`, description: DESCRIPTION },23  twitter: { card: 'summary_large_image', title: `${SITE_NAME} — ${TAGLINE}`, description: DESCRIPTION },24  // Favicon set built by `pnpm icons` (scripts/build-icons.mjs): multi-size ICO for URL bars, SVG for modern browsers, PNG for iOS.25  icons: {26    icon: [27      { url: '/favicon.ico', sizes: 'any', type: 'image/x-icon' },28      { url: '/icon.svg', type: 'image/svg+xml' },29    ],30    apple: [{ url: '/apple-touch-icon.png', sizes: '180x180', type: 'image/png' }],31  },32};3334export const viewport: Viewport = {35  width: 'device-width',36  initialScale: 1,37  viewportFit: 'cover',38  themeColor: [39    { media: '(prefers-color-scheme: light)', color: THEME_LIGHT },40    { media: '(prefers-color-scheme: dark)', color: THEME_DARK },41  ],42};4344export default function RootLayout({ children }: { children: React.ReactNode }) {45  return (46    <html lang="en" className={`${fontUi.variable} ${fontMono.variable} h-full antialiased`} suppressHydrationWarning>47      <head>48        <Script id="ca-prepaint" strategy="beforeInteractive" dangerouslySetInnerHTML={{ __html: PREPAINT_SCRIPT }} />49      </head>50      <body className="flex min-h-full flex-col pb-[calc(var(--tabbar-h)+env(safe-area-inset-bottom,0px))] lg:pb-0">51        <a href="#main" className="sr-only focus:not-sr-only focus:fixed focus:left-3 focus:top-3 focus:z-[200] focus:bg-accent focus:px-3 focus:py-2 focus:text-sm focus:text-accent-ink">52          Skip to content53        </a>54        <SearchProvider>55          <EventDrawerProvider>56            <SiteHeader />57            <main id="main" className="flex-1">58              {children}59            </main>60            <SiteFooter />61            <MobileTabBar />62            <SearchDialog />63            <EventDrawer />64          </EventDrawerProvider>65        </SearchProvider>66      </body>67    </html>68  );69}70