A deep green manifesto with a serif headline whose last word cycles, an arched photo and three principles between dashed rules. React and Tailwind CSS.
ManifestoWithCyclingKeywords.tsx
"use client";
// Manifesto band on deep green: a serif headline whose last word cycles
// through keywords, an arched photo, and three principles between dashed rules.
// Pure Tailwind + React, no dependencies. Works with Tailwind v3 and v4.
import { useEffect, useState } from "react";
type Props = {
heading?: string;
keywords?: string[];
image?: string;
imageAlt?: string;
principles?: string[];
interval?: number;
};
export default function ManifestoCycler({
heading = "Food grown with",
keywords = ["patience", "care", "the seasons", "our neighbours"],
image = "https://images.unsplash.com/photo-1441974231531-c6227db76b6e?w=700&q=80&auto=format&fit=crop",
imageAlt = "Sunlight through a forest",
principles = ["We never spray before a harvest.", "Every field rests one year in five.", "We pay growers before we get paid."],
interval = 2200,
}: Props) {
const [index, setIndex] = useState(0);
useEffect(() => {
if (keywords.length < 2 || window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
const id = setInterval(() => setIndex((i) => (i + 1) % keywords.length), interval);
return () => clearInterval(id);
}, [keywords.length, interval]);
return (
<section className="relative isolate overflow-hidden bg-[#024b2f] py-28 font-['Geist',ui-sans-serif,system-ui,sans-serif] text-[#f6f0de] antialiased md:py-40">
<div aria-hidden className="absolute inset-x-0 top-0 -z-10 h-2/3 bg-[radial-gradient(ellipse_at_top,#035a39,transparent_70%)]" />
<div className="mx-auto flex max-w-7xl flex-col items-center px-4 text-center md:px-8">
<h2 className="font-['Newsreader',Georgia,serif] text-[clamp(2.75rem,6vw,6rem)] leading-[1] tracking-[-0.02em]">
{heading}
<br />
<span className="relative inline-grid italic text-lime-300" aria-live="polite">
{keywords.map((word, i) => (
<span
key={word}
aria-hidden={i !== index}
className={`col-start-1 row-start-1 transition-all duration-700 ${i === index ? "translate-y-0 opacity-100" : "translate-y-4 opacity-0"}`}
>
{word}
</span>
))}
</span>
</h2>
<div className="mt-16 aspect-[3/4] w-52 overflow-hidden rounded-b-2xl rounded-t-full md:mt-20 md:w-72">
<img src={image} alt={imageAlt} className="h-full w-full object-cover" />
</div>
<div className="mt-16 grid w-full border-y border-dashed border-[#f6f0de]/20 md:mt-20 md:grid-cols-3">
{principles.map((line, i) => (
<p key={line} className={`border-dashed border-[#f6f0de]/20 px-6 py-7 font-['Newsreader',Georgia,serif] text-xl leading-snug text-[#f6f0de]/85 ${i > 0 ? "border-t md:border-l md:border-t-0" : ""}`}>
{line}
</p>
))}
</div>
</div>
</section>
);
}
How to use it
- Make sure your Next.js or React project has Tailwind CSS set up (v3 or v4 both work).
- Create
components/ManifestoWithCyclingKeywords.tsxand paste the code. - Import it into a page and replace the sample text and images with your own.
