Skip to content

Repository files navigation

🌸 Anime Gallery

A cozy, pastel-themed Pinterest-style gallery for browsing anime art.

An endless-scroll discovery feed plus character/anime search, backed by two purpose-built image APIs, with buttery-smooth motion and an image pipeline tuned for a masonry feed that never jank-scrolls.

Next.js TypeScript Tailwind CSS React Query Framer Motion License: MIT

πŸ”— Live Demo


✨ Features

  • πŸ–ΌοΈ Pinterest-style masonry gallery β€” CSS-column layout that flows naturally at any viewport, no grid library required.
  • ♾️ Endless discovery feed β€” a random art stream from Nekos API that never runs out, deduplicated client-side across batches.
  • πŸ” Character/anime search β€” debounced search against Safebooru's tag index, seamlessly crossfading from the discovery feed to search results.
  • πŸ›‘οΈ Forced safe-for-work content β€” every request is rating-filtered at the API level, plus a server-side tag blocklist (in the /api/discovery route) as a second gate on top of Nekos' own occasionally-unreliable rating classification β€” unsafe images never reach the browser.
  • πŸ’€ Skeleton loading states β€” pastel shimmer tiles with staggered heights so the loading state already looks like the masonry grid.
  • πŸŒ€ Blur-up images β€” Nekos images fade in from their real dominant color; Safebooru images fall back to a generated shimmer placeholder.
  • 🎨 Fully token-driven theme β€” every color, radius, and font in the app traces back to a single @theme block in globals.css. Zero hardcoded hex values in components.
  • πŸŒ— Light & dark pastel palettes β€” soft pinks and lavender in both modes, tuned for contrast, not just vibe.
  • 🎬 Tasteful motion β€” card entrances, hover lifts, and a discovery/search crossfade via Framer Motion, all defined in one variants file.

πŸ› οΈ Tech Stack

Layer Choice
Framework Next.js 16 (App Router)
Language TypeScript
Styling Tailwind CSS v4 (CSS-first @theme)
Components shadcn UI on Base UI
Data fetching TanStack Query
Motion Framer Motion
Discovery feed Nekos API (random tagged anime art)
Search Safebooru (tag/character indexed booru)
Tooling ESLint Β· Prettier Β· Husky Β· lint-staged
Package manager pnpm

πŸ“ Project Structure

src/
β”œβ”€β”€ app/
β”‚   β”œβ”€β”€ api/
β”‚   β”‚   β”œβ”€β”€ discovery/route.ts  # Same-origin proxy + normalizer for Nekos API
β”‚   β”‚   └── search/route.ts     # Same-origin proxy + normalizer for Safebooru
β”‚   └── ...                     # Routes, root layout, providers, global theme
β”œβ”€β”€ components/
β”‚   β”œβ”€β”€ ui/                # shadcn primitives (button, input, badge, skeleton)
β”‚   β”œβ”€β”€ layout/             # Header, footer
β”‚   β”œβ”€β”€ search/             # Search bar
β”‚   └── gallery/             # Masonry grid, card, skeleton, empty/error state
β”œβ”€β”€ config/
β”‚   └── motion-variants.ts  # Every Framer Motion variant, one file
β”œβ”€β”€ hooks/                 # use-discovery-feed, use-character-search, use-debounced-value, ...
β”œβ”€β”€ lib/
β”‚   β”œβ”€β”€ api-paths.ts        # Every API endpoint, centralized
β”‚   β”œβ”€β”€ content-safety.ts   # NSFW tag blocklist (defense-in-depth, applied server-side)
β”‚   └── image-placeholder.ts
β”œβ”€β”€ services/               # Thin fetch wrappers around our own /api/* routes
└── types/
    └── gallery.ts           # The unified GalleryImage type both APIs normalize into

πŸš€ Getting Started

Prerequisites

  • Node.js 20+
  • pnpm 9+

Installation

git clone https://github.com/<your-username>/anime-gallery.git
cd anime-gallery
pnpm install

Development

pnpm dev

Open http://localhost:3000.

Other scripts

pnpm build          # Production build
pnpm start          # Run the production build
pnpm lint           # ESLint
pnpm format         # Prettier, write mode
pnpm format:check   # Prettier, check mode

A Husky pre-commit hook runs lint-staged automatically, so staged files are linted and formatted before every commit.

🎨 Theming

This project uses Tailwind v4's CSS-first configuration β€” there's no tailwind.config.js. Every color, radius, and font is defined once as a CSS variable inside the @theme block in src/app/globals.css, then consumed everywhere through Tailwind utility classes (bg-primary, text-muted-foreground, rounded-2xl, etc.). Change a value there and it propagates through the entire app β€” no component ever hardcodes a color.

πŸ—οΈ Architecture: two APIs, one domain type

The discovery feed and search results come from two structurally different APIs β€” Nekos API (a random image endpoint with tags and a rating field) and Safebooru (a paginated booru with tag-based search). Neither sends CORS headers, so the browser can't call them directly β€” src/app/api/discovery/route.ts and src/app/api/search/route.ts proxy them server-side (where CORS doesn't apply) and normalize each response into the same GalleryImage type before it ever reaches the client. src/services/*.ts are thin fetch wrappers around our own routes; hooks, components, and state management only ever work with GalleryImage[] β€” swapping either upstream API later means touching one route handler, nothing else.

The two features are mutually exclusive views, not simultaneous fetches: src/app/page.tsx derives isSearching from the debounced query and only enables the matching React Query hook (useDiscoveryFeed / useCharacterSearch), so the idle view's data stays cached but stops fetching.

πŸ™ Credits

Discovery art is served by Nekos API. Search results are served by Safebooru, a safe-for-work anime imageboard. Both are free and require no API key β€” please be a good citizen of their infrastructure if you extend this project.

πŸ“„ License

Licensed under the MIT License.

Animiq

About

Anime Gallery / Developed By Next.js-TanStackQuery-FramerMotion 🌸 πŸ–ΌοΈ

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages