Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Lora:ital,wght@0,400..700;1,400..700&display=swap"
href="https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght,SOFT,WONK@9..144,400..700,0..100,0..1&family=Lora:ital,wght@0,400..700;1,400..700&display=swap"
/>
</head>
<body>
Expand Down
11 changes: 7 additions & 4 deletions src/components/layout/site-footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ const quickLinks = [

export function SiteFooter() {
return (
<footer className="bg-muted/50 border-t">
<footer className="bg-muted/50">
<div aria-hidden="true" className="texture-wood h-1.5" />
<div className="mx-auto max-w-7xl px-4 py-12 sm:px-6 lg:px-8">
<div className="grid gap-8 sm:grid-cols-2 lg:grid-cols-3">
{/* Brand */}
Expand All @@ -22,7 +23,9 @@ export function SiteFooter() {
alt="Tree Politics"
className="h-12 w-12 rounded-md"
/>
<span className="text-lg font-semibold">Tree Politics</span>
<span className="font-heading text-lg font-semibold">
Tree Politics
</span>
</Link>
<p className="text-muted-foreground text-sm leading-relaxed">
Woody Political Ecology, the use and abuse of trees in history,
Expand All @@ -32,7 +35,7 @@ export function SiteFooter() {

{/* Quick Links */}
<div>
<h3 className="mb-3 text-sm font-semibold tracking-wider uppercase">
<h3 className="font-body mb-3 text-sm font-semibold tracking-wider uppercase">
Quick Links
</h3>
<ul className="space-y-2">
Expand All @@ -51,7 +54,7 @@ export function SiteFooter() {

{/* Social */}
<div>
<h3 className="mb-3 text-sm font-semibold tracking-wider uppercase">
<h3 className="font-body mb-3 text-sm font-semibold tracking-wider uppercase">
Follow
</h3>
<SocialLinks className="flex gap-4" />
Expand Down
9 changes: 6 additions & 3 deletions src/components/layout/site-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ function MobileNav() {
alt="Tree Politics"
className="h-10 w-10 rounded-md"
/>
<span className="text-base font-semibold">Tree Politics</span>
<span className="font-heading text-base font-semibold">
Tree Politics
</span>
</Link>
</SheetTitle>
</SheetHeader>
Expand Down Expand Up @@ -71,7 +73,7 @@ function MobileNav() {

export function SiteHeader() {
return (
<header className="bg-background/95 supports-[backdrop-filter]:bg-background/60 sticky top-0 z-50 w-full border-b backdrop-blur">
<header className="bg-background/95 supports-[backdrop-filter]:bg-background/60 sticky top-0 z-50 w-full backdrop-blur">
<div className="mx-auto flex h-16 max-w-7xl items-center justify-between px-4 sm:px-6 lg:px-8">
<div className="flex items-center gap-8">
<Link to="/" className="flex items-center gap-3">
Expand All @@ -80,7 +82,7 @@ export function SiteHeader() {
alt="Tree Politics"
className="h-9 w-9 rounded-md"
/>
<span className="text-lg font-semibold tracking-wide">
<span className="font-heading text-lg font-semibold">
Tree Politics
</span>
</Link>
Expand All @@ -105,6 +107,7 @@ export function SiteHeader() {
<MobileNav />
</div>
</div>
<div aria-hidden="true" className="texture-wood h-1.5" />
</header>
)
}
2 changes: 1 addition & 1 deletion src/components/layout/site-layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { SiteHeader } from "./site-header"

export function SiteLayout({ children }: { children: React.ReactNode }) {
return (
<div className="flex min-h-svh flex-col">
<div className="texture-paper flex min-h-svh flex-col">
<a
href="#main-content"
className="bg-background text-foreground sr-only z-50 px-4 py-2 focus:not-sr-only focus:fixed focus:top-2 focus:left-2"
Expand Down
2 changes: 1 addition & 1 deletion src/components/ui/card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ function Card({ className, ...props }: React.ComponentProps<"div">) {
<div
data-slot="card"
className={cn(
"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-(--shadow-card)",
className
)}
{...props}
Expand Down
61 changes: 54 additions & 7 deletions src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,16 @@
:root {
--radius: 0.625rem;

/* Brand fonts — Lora (warm serif) for both headings and body. Split into
two vars so heading/body can diverge later without a site-wide refactor. */
--font-heading: "Lora", Georgia, serif;
/* Brand fonts — Fraunces (soft display serif) for headings, Lora (warm
serif) for body. Fraunces' SOFT/WONK axes are tuned in @layer base. */
--font-heading: "Fraunces", "Lora", Georgia, serif;
--font-body: "Lora", Georgia, serif;

/* Texture knobs — paper grain overlays the page shell; wood tints the
header/footer accent strips. */
--paper-opacity: 0.14;
--wood: oklch(0.48 0.055 60);

/* Green candidates — change --tp-green to compare */
--tp-green-mint: #4ade80;
--tp-green-emerald: #34d399;
Expand All @@ -34,8 +39,11 @@
--accent-foreground: oklch(0.25 0.01 50);
--destructive: oklch(0.577 0.245 27.325);
--destructive-foreground: oklch(0.577 0.245 27.325);
--border: oklch(0.9 0.005 100);
--input: oklch(0.9 0.005 100);
--border: oklch(0.86 0.025 80);
--input: oklch(0.86 0.025 80);
/* Warm-toned card lift — replaces the neutral shadow-sm on Card. */
--shadow-card:
0 1px 2px oklch(0.35 0.05 65 / 0.1), 0 3px 12px oklch(0.35 0.05 65 / 0.07);
--ring: var(--tp-green);
--chart-1: var(--tp-green);
--chart-2: oklch(0.6 0.118 184.704);
Expand All @@ -53,6 +61,8 @@
}

.dark {
--paper-opacity: 0.11;
--wood: oklch(0.34 0.045 55);
--background: oklch(0.16 0.01 50);
--foreground: oklch(0.95 0.005 80);
--card: oklch(0.19 0.01 50);
Expand All @@ -69,8 +79,9 @@
--accent-foreground: oklch(0.95 0.005 80);
--destructive: oklch(0.396 0.141 25.723);
--destructive-foreground: oklch(0.637 0.237 25.331);
--border: oklch(0.28 0.01 50);
--input: oklch(0.28 0.01 50);
--border: oklch(0.31 0.02 60);
--input: oklch(0.31 0.02 60);
--shadow-card: 0 1px 2px oklch(0 0 0 / 0.5), 0 3px 12px oklch(0 0 0 / 0.35);
--ring: var(--tp-green);
--chart-1: var(--tp-green);
--chart-2: oklch(0.696 0.17 162.48);
Expand Down Expand Up @@ -145,9 +156,45 @@
h5,
h6 {
@apply font-heading;
/* Fraunces wants tighter tracking than Lora did; SOFT rounds the
terminals (0-100), WONK swaps in angled/wonky alternates (0 or 1). */
letter-spacing: -0.015em;
font-variation-settings:
"SOFT" 30,
"WONK" 0;
}

h1 {
font-variation-settings:
"SOFT" 60,
"WONK" 1;
}
}

/* Paper-grain shell — SVG fractal noise behind content, above the page
background. Tune strength via --paper-opacity. */
.texture-paper {
position: relative;
isolation: isolate;
}

.texture-paper::before {
content: "";
position: absolute;
inset: 0;
z-index: -1;
pointer-events: none;
opacity: var(--paper-opacity);
background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='240' height='240'><filter id='p'><feTurbulence type='fractalNoise' baseFrequency='0.55' numOctaves='4' stitchTiles='stitch'/><feColorMatrix type='saturate' values='0'/></filter><rect width='100%25' height='100%25' filter='url(%23p)'/></svg>");
}

/* Wood accent — walnut base with horizontal grain streaks (low x-frequency
turbulence stretches the noise into fibers). Used as thin strips. */
.texture-wood {
background-color: var(--wood);
background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='420' height='60'><filter id='w'><feTurbulence type='fractalNoise' baseFrequency='0.006 0.14' numOctaves='3' seed='7' stitchTiles='stitch'/><feColorMatrix type='saturate' values='0'/></filter><rect width='100%25' height='100%25' filter='url(%23w)' opacity='0.4'/></svg>");
}

/* Ghost Content API HTML styling */
.prose-ghost .kg-image-card img {
@apply rounded-lg shadow-md;
Expand Down
6 changes: 2 additions & 4 deletions src/pages/about/about-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ export function AboutPage() {
<div className="mx-auto w-full max-w-3xl px-4 py-12 sm:px-6">
{/* Why Tree Politics */}
<section className="mb-16">
<h1 className="mb-6 text-3xl tracking-wide sm:text-4xl">
Why Tree Politics?
</h1>
<h1 className="mb-6 text-3xl sm:text-4xl">Why Tree Politics?</h1>
<div className="prose prose-lg dark:prose-invert prose-headings:font-heading max-w-none">
<p>
"Trees are the answer" read the bumper sticker on the back of my
Expand All @@ -30,7 +28,7 @@ export function AboutPage() {

{/* Author */}
<section>
<h2 className="mb-8 text-2xl tracking-wide">About the Author</h2>
<h2 className="mb-8 text-2xl">About the Author</h2>
<AuthorBio />
</section>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/pages/blog/blog-listing-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
filter: dateFilter,
})

const allPosts = data?.posts ?? []

Check warning on line 28 in src/pages/blog/blog-listing-page.tsx

View workflow job for this annotation

GitHub Actions / lint-and-test

The 'allPosts' logical expression could make the dependencies of useMemo Hook (at line 41) change on every render. Move it inside the useMemo callback. Alternatively, wrap the initialization of 'allPosts' in its own useMemo() Hook
const pagination = data?.meta?.pagination

const posts = useMemo(() => {
Expand All @@ -46,7 +46,7 @@

return (
<div className="mx-auto w-full max-w-5xl px-4 py-12 sm:px-6 lg:px-8">
<h1 className="mb-2 text-3xl tracking-wide sm:text-4xl">Blog</h1>
<h1 className="mb-2 text-3xl sm:text-4xl">Blog</h1>
<p className="text-muted-foreground mb-8">
Commentary on the political life of trees.
</p>
Expand Down
4 changes: 2 additions & 2 deletions src/pages/blog/blog-post-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export function BlogPostPage({ slug }: { slug: string }) {
</Link>

<header className="mb-10">
<h1 className="mb-4 text-3xl leading-tight tracking-wide sm:text-4xl lg:text-5xl">
<h1 className="mb-4 text-3xl leading-tight sm:text-4xl lg:text-5xl">
{post.title}
</h1>
<div className="text-muted-foreground flex flex-wrap items-center gap-2 text-sm">
Expand Down Expand Up @@ -83,7 +83,7 @@ export function BlogPostPage({ slug }: { slug: string }) {
)}

<div
className="prose-ghost prose prose-lg dark:prose-invert prose-headings:font-heading prose-headings:tracking-wide prose-a:text-primary max-w-none"
className="prose-ghost prose prose-lg dark:prose-invert prose-headings:font-heading prose-a:text-primary max-w-none"
dangerouslySetInnerHTML={{ __html: sanitizeHtml(post.html) }}
/>

Expand Down
6 changes: 3 additions & 3 deletions src/pages/contact/contact-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,21 @@ export function ContactPage() {

return (
<div className="mx-auto w-full max-w-2xl px-4 py-12 sm:px-6">
<h1 className="mb-2 text-3xl tracking-wide sm:text-4xl">Contact</h1>
<h1 className="mb-2 text-3xl sm:text-4xl">Contact</h1>
<p className="text-muted-foreground mb-8">
Have a question or want to get in touch? Follow along on social media or
send a message below.
</p>

{/* Social Links */}
<section className="mb-12">
<h2 className="mb-4 text-xl tracking-wide">Find Us Online</h2>
<h2 className="mb-4 text-xl">Find Us Online</h2>
<SocialLinks className="flex gap-5" />
</section>

{/* Contact Form */}
<section>
<h2 className="mb-4 text-xl tracking-wide">Send a Message</h2>
<h2 className="mb-4 text-xl">Send a Message</h2>
{submitted ? (
<Card>
<CardContent className="py-12 text-center">
Expand Down
8 changes: 3 additions & 5 deletions src/pages/home/home-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@ export function HomePage() {
alt="Tree Politics"
className="h-32 w-32 rounded-xl lg:h-40 lg:w-40"
/>
<h1 className="text-4xl tracking-wide sm:text-5xl lg:text-6xl">
Tree Politics
</h1>
<h1 className="text-4xl sm:text-5xl lg:text-6xl">Tree Politics</h1>
<p className="text-muted-foreground max-w-2xl text-lg leading-relaxed lg:text-xl">
Woody Political Ecology, the use and abuse of trees in history, and
tree facts to impress your friends
Expand All @@ -37,7 +35,7 @@ export function HomePage() {
{posts.length > 0 && (
<section className="mx-auto w-full max-w-5xl px-6 pb-16">
<div className="mb-8">
<h2 className="text-2xl tracking-wide">Recent Posts</h2>
<h2 className="text-2xl">Recent Posts</h2>
</div>
<div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
{posts.map((post) => (
Expand Down Expand Up @@ -77,7 +75,7 @@ export function HomePage() {

{/* About */}
<section className="mx-auto w-full max-w-5xl px-6 pb-20">
<h2 className="mb-8 text-2xl tracking-wide">About the Author</h2>
<h2 className="mb-8 text-2xl">About the Author</h2>
<AuthorBio />
</section>
</div>
Expand Down
Loading