131 lines
3.6 KiB
TypeScript
131 lines
3.6 KiB
TypeScript
import type { Metadata, Viewport } from 'next';
|
||
import { Poppins } from 'next/font/google';
|
||
import { Toaster } from 'react-hot-toast';
|
||
import { LanguageProvider } from '@/context/LanguageContext';
|
||
import { AuthProvider } from '@/context/AuthContext';
|
||
import PlausibleAnalytics from '@/components/PlausibleAnalytics';
|
||
import './globals.css';
|
||
|
||
// Self-hosted fonts via next/font - eliminates render-blocking external requests
|
||
// Poppins: entire site
|
||
// - Titles: Medium (500) / SemiBold (600)
|
||
// - Body: Regular (400)
|
||
const poppins = Poppins({
|
||
subsets: ['latin'],
|
||
display: 'swap',
|
||
variable: '--font-poppins',
|
||
weight: ['400', '500', '600', '700'],
|
||
});
|
||
|
||
const siteUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://spanglish.com.py';
|
||
|
||
export const metadata: Metadata = {
|
||
metadataBase: new URL(siteUrl),
|
||
title: {
|
||
default: 'Spanglish – Language Exchange Events in Asunción',
|
||
template: '%s – Spanglish',
|
||
},
|
||
description: 'Practice English and Spanish at relaxed social events in Asunción. Meet locals and internationals. Join the next Spanglish meetup.',
|
||
authors: [{ name: 'Spanglish' }],
|
||
creator: 'Spanglish',
|
||
publisher: 'Spanglish',
|
||
formatDetection: {
|
||
email: false,
|
||
address: false,
|
||
telephone: false,
|
||
},
|
||
openGraph: {
|
||
title: 'Spanglish – Language Exchange Events in Asunción',
|
||
description: 'Practice English and Spanish at relaxed social events in Asunción. Meet locals and internationals. Join the next Spanglish meetup.',
|
||
url: siteUrl,
|
||
siteName: 'Spanglish',
|
||
type: 'website',
|
||
locale: 'en_US',
|
||
alternateLocale: 'es_PY',
|
||
images: [
|
||
{
|
||
url: '/images/og-image.jpg',
|
||
width: 1200,
|
||
height: 630,
|
||
alt: 'Spanglish – Language Exchange Events in Asunción, Paraguay',
|
||
},
|
||
],
|
||
},
|
||
twitter: {
|
||
card: 'summary_large_image',
|
||
title: 'Spanglish – Language Exchange Events in Asunción',
|
||
description: 'Practice English and Spanish at relaxed social events in Asunción. Meet locals and internationals.',
|
||
images: ['/images/og-image.jpg'],
|
||
},
|
||
robots: {
|
||
index: true,
|
||
follow: true,
|
||
googleBot: {
|
||
index: true,
|
||
follow: true,
|
||
'max-video-preview': -1,
|
||
'max-image-preview': 'large',
|
||
'max-snippet': -1,
|
||
},
|
||
},
|
||
// Each route overrides this with its own path so the canonical is
|
||
// self-referential. Resolved against metadataBase above.
|
||
alternates: {
|
||
canonical: '/',
|
||
},
|
||
category: 'events',
|
||
manifest: '/manifest.json',
|
||
icons: {
|
||
icon: '/images/favicon.png',
|
||
apple: '/images/favicon_icon.png',
|
||
},
|
||
};
|
||
|
||
export const viewport: Viewport = {
|
||
themeColor: '#FFD700',
|
||
width: 'device-width',
|
||
initialScale: 1,
|
||
maximumScale: 5,
|
||
};
|
||
|
||
export default function RootLayout({
|
||
children,
|
||
}: {
|
||
children: React.ReactNode;
|
||
}) {
|
||
return (
|
||
<html lang="en" className={poppins.variable}>
|
||
<body className={poppins.className}>
|
||
<PlausibleAnalytics />
|
||
<AuthProvider>
|
||
<LanguageProvider>
|
||
{children}
|
||
<Toaster
|
||
position="top-right"
|
||
toastOptions={{
|
||
duration: 4000,
|
||
style: {
|
||
borderRadius: '12px',
|
||
padding: '16px',
|
||
},
|
||
success: {
|
||
style: {
|
||
background: '#F0FDF4',
|
||
color: '#166534',
|
||
},
|
||
},
|
||
error: {
|
||
style: {
|
||
background: '#FEF2F2',
|
||
color: '#991B1B',
|
||
},
|
||
},
|
||
}}
|
||
/>
|
||
</LanguageProvider>
|
||
</AuthProvider>
|
||
</body>
|
||
</html>
|
||
);
|
||
}
|