feat(marketing): editorial fonts (Fraunces/Newsreader/JetBrains Mono) site-wide

Loading:
- Fraunces, Newsreader, JetBrains_Mono added to (marketing)/layout.tsx
  as CSS variables -- one load point, cached for all marketing pages
- 3 utility classes added to globals.css: .font-fraunces, .font-newsreader,
  .font-jetbrains
- about/page.tsx: removed duplicate font loading (now from layout)

Font roles applied:
  Fraunces   → all h1/h2/h3 headings + blog card titles + hero h1 (italic)
  Newsreader → long prose blocks: TheProblem, FounderStory card, Privacy
               intro, HeirloomVision description, blog article paragraphs,
               blog post excerpt
  JetBrains  → all small uppercase eyebrow labels across every section
  Geist      → nav, buttons, feature card body, short UI text (unchanged)

Pages updated: homepage, blog listing, blog articles, pricing, partners
About page: fonts resolve identically via layout variables, no visual change

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Manohar Gupta 2026-05-31 11:32:44 +05:30
parent 2a450c7644
commit c523533531
8 changed files with 72 additions and 66 deletions

View file

@ -1,29 +1,9 @@
import type { Metadata } from "next";
import Link from "next/link";
import { Fraunces, Newsreader, JetBrains_Mono } from "next/font/google";
import { AboutScrollReveal } from "@/components/marketing/AboutScrollReveal";
// ── Fonts loaded only for this page ──────────────────────────────
const fraunces = Fraunces({
subsets: ["latin"],
variable: "--font-fraunces",
weight: ["300", "400", "500", "600"],
style: ["normal", "italic"],
display: "swap",
});
const newsreader = Newsreader({
subsets: ["latin"],
variable: "--font-newsreader",
weight: ["300", "400", "500", "600"],
style: ["normal", "italic"],
display: "swap",
});
const jetbrainsMono = JetBrains_Mono({
subsets: ["latin"],
variable: "--font-jetbrains",
weight: ["400", "500"],
display: "swap",
});
// Fonts (Fraunces, Newsreader, JetBrains Mono) are now loaded globally
// in (marketing)/layout.tsx and available via CSS variables.
// ── Page-level styles (design tokens + letter layout) ─────────────
const CSS = `
@ -318,7 +298,7 @@ const CREED = [
export default function AboutPage() {
return (
<div className={`${fraunces.variable} ${newsreader.variable} ${jetbrainsMono.variable}`}>
<div>
{/* Page-specific design system — inline so it's scoped and SSR-safe */}
{/* eslint-disable-next-line react/no-danger */}
<style dangerouslySetInnerHTML={{ __html: CSS }} />

View file

@ -102,10 +102,10 @@ export default async function BlogPostPage({
<span className="text-xs text-gray-400">{post.readTime}</span>
</div>
<h1 className="text-3xl sm:text-4xl font-bold text-gray-900 leading-tight mb-4">
<h1 className="font-fraunces italic text-3xl sm:text-4xl font-bold text-gray-900 leading-tight mb-4">
{post.emoji} {post.title}
</h1>
<p className="text-lg text-gray-500 leading-relaxed">{post.excerpt}</p>
<p className="font-newsreader text-lg text-gray-500 leading-relaxed">{post.excerpt}</p>
<div className="mt-5 text-sm text-gray-400">
By <span className="text-gray-600 font-medium">{post.author}</span>
@ -132,7 +132,7 @@ export default async function BlogPostPage({
{/* ── LEFT SIDEBAR: Table of Contents ── */}
<aside className="hidden lg:block">
<div className="sticky top-24">
<p className="text-xs font-bold text-gray-400 uppercase tracking-widest mb-4">In this article</p>
<p className="font-jetbrains text-xs font-bold text-gray-400 uppercase tracking-widest mb-4">In this article</p>
{headings.length > 0 ? (
<ol className="flex flex-col gap-2">
{headings.map((h, i) => (
@ -172,14 +172,14 @@ export default async function BlogPostPage({
{section.heading && (
<h2
id={slugifyHeading(section.heading)}
className="text-xl font-bold text-gray-900 mb-4 mt-2 scroll-mt-28"
className="font-fraunces text-xl font-bold text-gray-900 mb-4 mt-2 scroll-mt-28"
>
{section.heading}
</h2>
)}
{section.paragraphs?.map((p, j) => (
<p key={j} className="text-gray-600 leading-relaxed mb-4">
<p key={j} className="font-newsreader text-gray-600 leading-relaxed mb-4">
{p}
</p>
))}
@ -244,7 +244,7 @@ export default async function BlogPostPage({
<div className="mt-14 pt-10 border-t border-gray-100">
<div className="bg-gradient-to-br from-rose-50 to-pink-50 rounded-2xl p-8 text-center border border-rose-100">
<div className="text-3xl mb-3">🌸</div>
<h3 className="text-xl font-bold text-gray-900 mb-2">Try Tia free during early access</h3>
<h3 className="font-fraunces text-xl font-bold text-gray-900 mb-2">Try Tia free during early access</h3>
<p className="text-gray-500 text-sm mb-6 max-w-sm mx-auto">
Log feeds, track vaccinations, and build a digital heirloom for your child. Built for Indian families.
</p>
@ -277,7 +277,7 @@ export default async function BlogPostPage({
{/* More articles */}
<div>
<p className="text-xs font-bold text-gray-400 uppercase tracking-widest mb-4">More articles</p>
<p className="font-jetbrains text-xs font-bold text-gray-400 uppercase tracking-widest mb-4">More articles</p>
<div className="flex flex-col gap-4">
{otherPosts.map((p) => (
<Link
@ -301,7 +301,7 @@ export default async function BlogPostPage({
{/* Category badge */}
<div>
<p className="text-xs font-bold text-gray-400 uppercase tracking-widest mb-3">Filed under</p>
<p className="font-jetbrains text-xs font-bold text-gray-400 uppercase tracking-widest mb-3">Filed under</p>
<span className={`text-xs font-semibold px-2.5 py-1 rounded-full ${post.categoryColor}`}>
{post.category}
</span>

View file

@ -63,8 +63,8 @@ export default function BlogPage() {
{/* Page header */}
<div className="bg-gradient-to-br from-rose-50 to-pink-50 border-b border-rose-100">
<div className="max-w-6xl mx-auto px-5 py-12 text-center">
<div className="mb-2 text-sm font-medium text-rose-500 uppercase tracking-widest">Journal</div>
<h1 className="text-4xl font-bold text-gray-900 mb-3 leading-tight">The Tia Blog</h1>
<div className="font-jetbrains mb-2 text-sm font-medium text-rose-500 uppercase tracking-widest">Journal</div>
<h1 className="font-fraunces italic text-4xl font-bold text-gray-900 mb-3 leading-tight">The Tia Blog</h1>
<p className="text-base text-gray-500 leading-relaxed max-w-xl mx-auto">
Practical guides on baby feeding, health, vaccination schedules, and getting the most out of Tia for Indian families.
</p>
@ -83,7 +83,7 @@ export default function BlogPage() {
{/* ── LEFT SIDEBAR: timeline ── */}
<aside className="hidden lg:block">
<div className="sticky top-24">
<p className="text-xs font-bold text-gray-400 uppercase tracking-widest mb-5">All posts</p>
<p className="font-jetbrains text-xs font-bold text-gray-400 uppercase tracking-widest mb-5">All posts</p>
<ol className="relative border-l-2 border-rose-100 pl-4 space-y-5">
{POSTS.map((p) => (
<li key={p.slug} className="group relative">
@ -134,7 +134,7 @@ export default function BlogPage() {
<span className="text-xs text-gray-400">{post.readTime}</span>
</div>
<h2 className="text-lg font-bold text-gray-900 leading-snug mb-2 group-hover:text-rose-700 transition-colors duration-150">
<h2 className="font-fraunces text-lg font-bold text-gray-900 leading-snug mb-2 group-hover:text-rose-700 transition-colors duration-150">
{post.title}
</h2>
<p className="text-gray-500 text-sm leading-relaxed line-clamp-2">
@ -158,7 +158,7 @@ export default function BlogPage() {
{/* Categories */}
<div>
<p className="text-xs font-bold text-gray-400 uppercase tracking-widest mb-4">Browse by topic</p>
<p className="font-jetbrains text-xs font-bold text-gray-400 uppercase tracking-widest mb-4">Browse by topic</p>
<div className="flex flex-col gap-2.5">
{CATEGORIES.map((cat) => (
<div key={cat.name} className="flex items-center justify-between">
@ -176,7 +176,7 @@ export default function BlogPage() {
{/* Quick links */}
<div>
<p className="text-xs font-bold text-gray-400 uppercase tracking-widest mb-4">Quick reads</p>
<p className="font-jetbrains text-xs font-bold text-gray-400 uppercase tracking-widest mb-4">Quick reads</p>
<div className="flex flex-col gap-3">
{POSTS.slice(0, 3).map((p) => (
<Link

View file

@ -1,7 +1,30 @@
import type { Metadata } from "next";
import Link from "next/link";
import { Fraunces, Newsreader, JetBrains_Mono } from "next/font/google";
import { MarketingNav } from "@/components/marketing/MarketingNav";
// ── Editorial fonts — loaded once for all marketing pages ─────────
const fraunces = Fraunces({
subsets: ["latin"],
variable: "--font-fraunces",
weight: ["300", "400", "500", "600"],
style: ["normal", "italic"],
display: "swap",
});
const newsreader = Newsreader({
subsets: ["latin"],
variable: "--font-newsreader",
weight: ["300", "400", "500", "600"],
style: ["normal", "italic"],
display: "swap",
});
const jetbrainsMono = JetBrains_Mono({
subsets: ["latin"],
variable: "--font-jetbrains",
weight: ["400", "500"],
display: "swap",
});
export const metadata: Metadata = {
title: {
default: "Tia — Your baby's digital heirloom",
@ -30,9 +53,7 @@ export default function MarketingLayout({
children: React.ReactNode;
}) {
return (
<>
{/* Scroll-reveal nav — appears after scrolling past hero */}
{/* Analytics now provided globally by Umami in root layout.tsx */}
<div className={`${fraunces.variable} ${newsreader.variable} ${jetbrainsMono.variable}`}>
<MarketingNav />
<main>{children}</main>
@ -104,6 +125,6 @@ export default function MarketingLayout({
</div>
</div>
</footer>
</>
</div>
);
}

View file

@ -59,7 +59,7 @@ function Hero() {
<span></span> Free during early access
</div>
<h1 className="text-4xl sm:text-5xl font-bold text-gray-900 leading-tight mb-5">
<h1 className="font-fraunces italic text-4xl sm:text-5xl font-bold text-gray-900 leading-tight mb-5">
Your baby&apos;s story,{" "}
<span className="text-rose-500" style={{ fontFamily: "var(--font-caveat)", fontSize: "1.1em" }}>
preserved for a lifetime.
@ -102,14 +102,14 @@ function TheProblem() {
return (
<section className="py-20 px-5 bg-white">
<div className="max-w-2xl mx-auto">
<p className="text-xs font-semibold text-rose-500 uppercase tracking-widest mb-4">The 3am reality</p>
<p className="font-jetbrains text-xs font-semibold text-rose-500 uppercase tracking-widest mb-4">The 3am reality</p>
<h2 className="text-3xl font-bold text-gray-900 mb-6 leading-tight">
<h2 className="font-fraunces text-3xl font-bold text-gray-900 mb-6 leading-tight">
You&apos;re awake at 3am.<br />
When did she last feed?
</h2>
<div className="space-y-4 text-gray-600 leading-relaxed">
<div className="font-newsreader space-y-4 text-gray-600 leading-relaxed">
<p>
You scroll back through WhatsApp messages to your mother-in-law. You check a
sticky note on the refrigerator. You open a notes app you downloaded last week
@ -186,8 +186,8 @@ function Features() {
return (
<section className="py-20 px-5 bg-gray-50">
<div className="max-w-5xl mx-auto">
<p className="text-xs font-semibold text-rose-500 uppercase tracking-widest mb-4 text-center">What Tia does</p>
<h2 className="text-3xl font-bold text-gray-900 text-center mb-12">
<p className="font-jetbrains text-xs font-semibold text-rose-500 uppercase tracking-widest mb-4 text-center">What Tia does</p>
<h2 className="font-fraunces text-3xl font-bold text-gray-900 text-center mb-12">
Everything in one private place.
</h2>
@ -200,7 +200,7 @@ function Features() {
<span className="text-3xl mb-4 transition-transform duration-200 group-hover:scale-110 block">
{f.icon}
</span>
<h3 className="font-bold text-gray-900 text-lg mb-2">{f.title}</h3>
<h3 className="font-fraunces font-bold text-gray-900 text-lg mb-2">{f.title}</h3>
<p
className="text-gray-600 text-sm leading-relaxed mb-4 flex-1"
dangerouslySetInnerHTML={{ __html: f.body }}
@ -221,14 +221,14 @@ function FounderStory() {
return (
<section className="py-20 px-5 bg-white">
<div className="max-w-2xl mx-auto">
<p className="text-xs font-semibold text-rose-500 uppercase tracking-widest mb-4">Why Tia exists</p>
<p className="font-jetbrains text-xs font-semibold text-rose-500 uppercase tracking-widest mb-4">Why Tia exists</p>
<h2 className="text-3xl font-bold text-gray-900 mb-3 leading-tight">
<h2 className="font-fraunces text-3xl font-bold text-gray-900 mb-3 leading-tight">
TIA began with a promise.
</h2>
<div className="bg-amber-50 border border-amber-200 rounded-2xl p-6 sm:p-8 mt-6">
<div className="text-gray-700 leading-relaxed space-y-4 text-sm sm:text-base">
<div className="font-newsreader text-gray-700 leading-relaxed space-y-4 text-sm sm:text-base">
<p>
When our daughter, Tia, was born, we wanted to remember everything the tiny
stretches, the sleepy smiles, the moments that felt too precious to forget. But we
@ -280,16 +280,16 @@ function HeirloomVision() {
return (
<section className="py-20 px-5 bg-gradient-to-br from-rose-50 to-amber-50">
<div className="max-w-2xl mx-auto text-center">
<p className="text-xs font-semibold text-rose-500 uppercase tracking-widest mb-4">The heirloom vision</p>
<p className="font-jetbrains text-xs font-semibold text-rose-500 uppercase tracking-widest mb-4">The heirloom vision</p>
<h2 className="text-3xl font-bold text-gray-900 mb-6 leading-tight">
<h2 className="font-fraunces text-3xl font-bold text-gray-900 mb-6 leading-tight">
One day, your child will be able to{" "}
<span className="text-rose-500" style={{ fontFamily: "var(--font-caveat)", fontSize: "1.1em" }}>
read their own story.
</span>
</h2>
<p className="text-gray-600 leading-relaxed mb-8 max-w-xl mx-auto">
<p className="font-newsreader text-gray-600 leading-relaxed mb-8 max-w-xl mx-auto">
Everything you log today is a letter to your future child. The 2:47am feed.
The first solid. The doctor visit you worried about for a week. The photo from
the moment you realised she could recognise your voice.
@ -303,7 +303,7 @@ function HeirloomVision() {
].map(item => (
<div key={item.title} className="bg-white/80 rounded-2xl p-5 border border-rose-100 hover:border-rose-300 hover:shadow-sm transition-all duration-200">
<span className="text-2xl mb-3 block">{item.icon}</span>
<h3 className="font-bold text-gray-900 mb-1">{item.title}</h3>
<h3 className="font-fraunces font-bold text-gray-900 mb-1">{item.title}</h3>
<p className="text-sm text-gray-600 leading-relaxed">{item.desc}</p>
</div>
))}
@ -318,14 +318,14 @@ function Privacy() {
return (
<section className="py-20 px-5 bg-white">
<div className="max-w-2xl mx-auto">
<p className="text-xs font-semibold text-rose-500 uppercase tracking-widest mb-4">Privacy & trust</p>
<p className="font-jetbrains text-xs font-semibold text-rose-500 uppercase tracking-widest mb-4">Privacy & trust</p>
<h2 className="text-3xl font-bold text-gray-900 mb-4">
<h2 className="font-fraunces text-3xl font-bold text-gray-900 mb-4">
We don&apos;t sell your data <br />
<span className="text-rose-500">we preserve it.</span>
</h2>
<p className="text-gray-600 leading-relaxed mb-8">
<p className="font-newsreader text-gray-600 leading-relaxed mb-8">
Tia is a baby-tracking app. Your child&apos;s records are not the product.
They are the point.
</p>
@ -356,8 +356,8 @@ function EarlyAccess() {
return (
<section className="py-16 px-5 bg-gray-50 border-y border-gray-100">
<div className="max-w-2xl mx-auto">
<p className="text-xs font-semibold text-rose-500 uppercase tracking-widest mb-4 text-center">Private early access</p>
<h2 className="text-2xl font-bold text-gray-900 text-center mb-6">
<p className="font-jetbrains text-xs font-semibold text-rose-500 uppercase tracking-widest mb-4 text-center">Private early access</p>
<h2 className="font-fraunces text-2xl font-bold text-gray-900 text-center mb-6">
Built by a parent, being tested by parents.
</h2>
@ -368,7 +368,7 @@ function EarlyAccess() {
].map(item => (
<div key={item.title} className="bg-white rounded-2xl p-5 border border-gray-100 shadow-sm">
<span className="text-2xl mb-3 block">{item.icon}</span>
<h3 className="font-bold text-gray-900 mb-2">{item.title}</h3>
<h3 className="font-fraunces font-bold text-gray-900 mb-2">{item.title}</h3>
<p className="text-sm text-gray-600 leading-relaxed">{item.desc}</p>
</div>
))}
@ -383,7 +383,7 @@ function FinalCTA() {
return (
<section className="py-24 px-5 bg-gradient-to-br from-rose-500 to-rose-600 text-white text-center">
<div className="max-w-xl mx-auto">
<h2 className="text-3xl sm:text-4xl font-bold mb-4 leading-tight">
<h2 className="font-fraunces text-3xl sm:text-4xl font-bold mb-4 leading-tight">
Start preserving your<br />child&apos;s story today.
</h2>

View file

@ -17,8 +17,8 @@ export default function PartnersPage() {
return (
<div className="min-h-screen bg-white">
<div className="max-w-2xl mx-auto px-5 py-20">
<div className="mb-3 text-sm font-medium text-rose-500 uppercase tracking-widest">Work with us</div>
<h1 className="text-4xl font-bold text-gray-900 mb-6 leading-tight">Partners</h1>
<div className="font-jetbrains mb-3 text-sm font-medium text-rose-500 uppercase tracking-widest">Work with us</div>
<h1 className="font-fraunces text-4xl font-bold text-gray-900 mb-6 leading-tight">Partners</h1>
<div className="text-gray-600 leading-relaxed space-y-5 mb-10">
<p>

View file

@ -16,9 +16,9 @@ export const metadata: Metadata = {
export default function PricingPage() {
return (
<div className="max-w-2xl mx-auto px-4 py-20">
<p className="text-xs font-semibold text-rose-500 uppercase tracking-widest mb-4 text-center">Pricing</p>
<p className="font-jetbrains text-xs font-semibold text-rose-500 uppercase tracking-widest mb-4 text-center">Pricing</p>
<h1 className="text-3xl sm:text-4xl font-bold text-gray-900 text-center mb-4">
<h1 className="font-fraunces text-3xl sm:text-4xl font-bold text-gray-900 text-center mb-4">
Founder pricing.
</h1>
<p className="text-gray-500 text-center mb-12 max-w-md mx-auto">

View file

@ -56,6 +56,11 @@
animation: cta-pulse 2.6s ease-out infinite;
}
/* ── Editorial font utilities (marketing pages) ── */
.font-fraunces { font-family: var(--font-fraunces, Georgia, serif); }
.font-newsreader { font-family: var(--font-newsreader, Georgia, serif); }
.font-jetbrains { font-family: var(--font-jetbrains, ui-monospace, monospace); }
/* hide scrollbar but keep scroll */
.scrollbar-hide {
scrollbar-width: none;