Add theme system, project banners, mobile nav, and hero logo

- System-first theming via light-dark() with explicit .light/.dark overrides
- Project banners: Racetix, beenvoice, RaceHub, HRIStudio
- Mobile navigation with portaled blur backdrop
- Hero logo placement and page copy polish
- Remove tRPC, migrate contact flow to direct API route
This commit is contained in:
2026-08-02 02:40:27 -04:00
parent bf10f26d26
commit d0ffd7dc04
36 changed files with 2191 additions and 525 deletions
+56 -5
View File
@@ -1,7 +1,14 @@
import "~/styles/globals.css";
import { type Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import {
Geist,
Geist_Mono,
Inter,
Playfair_Display,
Rajdhani,
Titillium_Web,
} from "next/font/google";
import Script from "next/script";
import { env } from "~/env";
@@ -13,7 +20,7 @@ export const metadata: Metadata = {
template: "%s · Hadlock Technologies",
},
description:
"Hadlock Technologies is a full-stack technical partner: networks, servers, cloud, databases, web applications, and commerce systems — infrastructure to applications, one accountable name.",
"Hadlock Technologies runs networks, servers, cloud systems, databases, web applications, and commerce. One accountable name covers everything from infrastructure to applications.",
keywords: [
"IT consulting",
"UniFi",
@@ -29,7 +36,7 @@ export const metadata: Metadata = {
openGraph: {
title: "Hadlock Technologies — Everything technical, handled",
description:
"Networks, servers, cloud, databases, web, and commerce — one accountable technical partner.",
"One accountable technical partner for networks, servers, cloud, databases, web, and commerce.",
url: "https://hadlock.tech",
siteName: "Hadlock Technologies",
type: "website",
@@ -39,9 +46,20 @@ export const metadata: Metadata = {
const umamiEnabled =
!!env.NEXT_PUBLIC_UMAMI_SRC && !!env.NEXT_PUBLIC_UMAMI_WEBSITE_ID;
const themeScript = `
try {
const theme = localStorage.getItem("hadlock_theme");
if (theme === "light") {
document.documentElement.classList.add("light");
} else if (theme === "dark") {
document.documentElement.classList.add("dark");
}
} catch {}
`;
const geist = Geist({
subsets: ["latin"],
variable: "--font-geist-sans",
subsets: ["latin"],
});
const geistMono = Geist_Mono({
@@ -49,12 +67,45 @@ const geistMono = Geist_Mono({
variable: "--font-geist-mono",
});
const playfair = Playfair_Display({
variable: "--font-playfair",
subsets: ["latin"],
weight: ["400", "500", "600", "700"],
});
const rajdhani = Rajdhani({
variable: "--font-rajdhani",
subsets: ["latin"],
weight: ["400", "500", "600", "700"],
});
const titillium = Titillium_Web({
variable: "--font-titillium",
subsets: ["latin"],
weight: ["400", "600", "700"],
});
const inter = Inter({
variable: "--font-inter",
subsets: ["latin"],
weight: ["400", "500", "600", "700"],
});
export default function RootLayout({
children,
}: Readonly<{ children: React.ReactNode }>) {
return (
<html lang="en" className={`${geist.variable} ${geistMono.variable}`}>
<html
lang="en"
suppressHydrationWarning
className={`${geist.variable} ${geistMono.variable} ${playfair.variable} ${rajdhani.variable} ${titillium.variable} ${inter.variable}`}
>
<body className="font-sans">
<Script
id="hadlock-theme"
strategy="beforeInteractive"
dangerouslySetInnerHTML={{ __html: themeScript }}
/>
{children}
{umamiEnabled && (
<Script