Creative Web · 2026
Itachi — Character Universe
A character-driven experience built around Itachi Uchiha. Instead of a wiki wall of text, the site opens on a moonlit portrait with rank, affiliation and kekkei genkai stats, then guides the visitor through chapters — Prodigy, Sacrifice, Brother — with a dedicated jutsu archive and timeline.
Client
Snapz Labs (in-house product)
Industry
Anime / Fan Media
Timeline
3 weeks
My role
Designer & Front-End Engineer
The problem
Fan pages for the character were text dumps on ad-heavy wikis — no art direction, no story flow, and unreadable on mobile.
The solution
A cinematic character universe: moonlit dark theme, a stat panel, a searchable jutsu archive and scroll-driven chapters that unfold his story instead of listing it.
98
Lighthouse performance
3m 40s
Avg. time on site
0.8s
First contentful paint
What I built
Cinematic hero
Full-bleed portrait with layered crow particles, Japanese kanji typography and a soft moonlight vignette.
Stat panel
Rank, affiliation, kekkei genkai and village rendered as a responsive data strip under the hero.
Jutsu archive
Filterable /jutsu page with chakra nature tags, cost, and expandable technique descriptions.
Story chapters
Scroll-linked sections that fade and parallax as the reader moves through his timeline.
Architecture
- React + TanStack Start with file-based routes (/, /about, /jutsu)
- Tailwind CSS design tokens for the moonlit dark palette
- Content authored as typed TS data modules — no CMS round-trip
- Static prerender + edge caching for instant repeat visits
- Per-route metadata and OG cards so shared links render a proper preview
Hard problems
Heavy artwork killed load time
Illustrations were 4MB+ each. AVIF/WebP variants with responsive srcset and blurred LQIP placeholders brought FCP to 0.8s with no visible pop-in.
Scroll effects janked on mobile
Rewrote the parallax on IntersectionObserver + transform-only animations and honoured prefers-reduced-motion, holding a steady 60fps on mid-range Android.
Code from the build
A few real excerpts from the repository.
export type Jutsu = {
name: string;
reading: string;
nature: "Fire" | "Genjutsu" | "Space-Time" | "Sealing";
rank: "C" | "B" | "A" | "S";
cost: number; // chakra units
description: string;
};
export const jutsu: Jutsu[] = [
{
name: "Tsukuyomi",
reading: "月読",
nature: "Genjutsu",
rank: "S",
cost: 95,
description:
"Absolute control of the target's perception of time — days of torment inside a single second.",
},
];
export const byNature = (nature: Jutsu["nature"]) =>
jutsu.filter((j) => j.nature === nature);export function Reveal({ children }: { children: React.ReactNode }) {
const ref = useRef<HTMLDivElement>(null);
const [shown, setShown] = useState(false);
useEffect(() => {
const el = ref.current;
if (!el) return;
if (matchMedia("(prefers-reduced-motion: reduce)").matches) {
setShown(true);
return;
}
const io = new IntersectionObserver(
([entry]) => entry.isIntersecting && (setShown(true), io.disconnect()),
{ rootMargin: "-12% 0px" },
);
io.observe(el);
return () => io.disconnect();
}, []);
return (
<div
ref={ref}
className={shown ? "translate-y-0 opacity-100" : "translate-y-6 opacity-0"}
style={{ transition: "opacity .7s ease, transform .7s ease" }}
>
{children}
</div>
);
}"It reads like a short film, not a fan page. Everyone I sent the link to scrolled all the way to the end."
How it was built
- Discovery call, scope and fixed timeline agreed up front
- Custom design system built in Figma before development
- Typed, reviewed codebase with CI checks on every commit
- Deployed with monitoring, backups and post-launch support