A dark-then-light, minimalist gallery site for the artist Naabi Kage, built with Next.js 14 (App Router), TypeScript, and Tailwind. The gallery auto-advances, the background animates with Vanta.BIRDS (Three.js), and every page transitions with a smooth ease-in fade.
If the inline player does not render on GitHub, click here to watch / download: media/nabi-kage-arts.mp4
This README is the operating manual: how to run the site, how to add new artwork, how to update the words on every page, and how to deploy.
git clone https://github.com/coffee-for-coding/naabikage.git
cd Art-Page
npm install
npm run devOpen http://localhost:3000.
Production build:
npm run build
npm run startLint / format:
npm run lint
npm run formatRequirements: Node 18.17+.
Art-Page/
├─ public/
│ ├─ dp.png ← profile picture (used on About if you wire it)
│ ├─ images/
│ │ └─ {YEAR}/{slug}/ ← every artwork lives here
│ │ ├─ cover.jpg ← any image file: jpg/jpeg/png/webp/avif/gif
│ │ └─ meta.json ← optional: title / series / medium / bg
│ └─ sketches/ ← every sketch lives here (flat folder)
│ ├─ <slug>.png ← any image file: jpg/jpeg/png/webp/avif/gif
│ └─ <slug>.json ← optional: title / date / medium / notes
├─ src/
│ ├─ app/
│ │ ├─ layout.tsx ← <html>, fonts, Vanta background, page-transition wrapper
│ │ ├─ globals.css ← theme variables + page-transition keyframes
│ │ ├─ page.tsx ← Home (Gallery)
│ │ ├─ about/page.tsx ← About page (bio + email)
│ │ └─ sketches/page.tsx ← Sketches page (grid of sketch images + metadata)
│ ├─ components/
│ │ ├─ Navbar.tsx ← logo + WORK/SKETCHES/ABOUT + WORK year dropdown
│ │ ├─ Gallery.tsx ← slide carousel, autoplay, prev/next previews, dots
│ │ ├─ Slide.tsx ← single artwork slide (image + caption)
│ │ ├─ NavButton.tsx ← left/right arrow buttons
│ │ ├─ RadialMenu.tsx ← logo-click radial overlay menu
│ │ ├─ VantaBackground.tsx ← loads Three.js + Vanta.BIRDS into a fixed bg
│ │ └─ PageTransition.tsx ← rerouting fade-in keyed off pathname
│ ├─ data/
│ │ └─ works.ts ← seed artworks (used only if /public/images/ is empty)
│ └─ lib/
│ ├─ loadWorks.ts ← scans /public/images/ at build time
│ └─ loadSketches.ts ← scans /public/sketches/ at build time
├─ next.config.js
├─ tailwind.config.ts
├─ tsconfig.json
├─ package.json
└─ README.md
This is the part you'll touch most often. The loader scans public/images/ at build time; no code changes are required to add new pieces.
public/images/
2024/
moonlit-figure/
cover.jpg ← the image file (any name; first image found is used)
meta.json ← optional, for nicer titles / series / medium / bg
veil/
veil.png
2023/
the-quiet-hour/
photo.webp
Rules the loader (src/lib/loadWorks.ts) enforces:
| Layer | Rule |
|---|---|
| 1st level | Folder name must be a 4-digit year (e.g. 2024). |
| 2nd level | Folder name is the slug for one artwork. |
| Image | First file with extension jpg/jpeg/png/webp/avif/gif is the cover. |
| Title | Auto-derived from slug (moonlit-figure → Moonlit Figure). |
| Series / medium | Auto-defaulted unless overridden in meta.json. |
| Background | Picked from a soft palette unless overridden. |
Place inside the artwork folder. Every key is optional.
{
"title": "Moonlit Figure",
"series": "Nocturnes",
"medium": "Oil on linen",
"bg": "#1a1814"
}- Create the folder:
public/images/2024/<slug-with-hyphens>/. - Drop the image inside (any filename).
- (Optional) Add
meta.jsonfor a custom title / series / medium. - Restart dev (
npm run dev) — in production, the nextnpm run buildpicks it up.
- Rename → rename the folder; URL slug + auto-title both update.
- Retitle without renaming → set
"title"inmeta.json.
Delete its folder. Done. Year folder can stay empty or be deleted.
If public/images/ has zero year folders, the site uses src/data/works.ts as the seed. Edit that array to add objects with shape:
{
id: "unique-id",
title: "Title",
year: 2024,
series: "Series Name",
medium: "Oil on linen",
image: "/images/your-image.jpg",
bg: "#1a1814"
}The Sketches page renders a grid of sketch images, scanned at build time from public/sketches/ by src/lib/loadSketches.ts. No code changes are required to add new sketches.
Folder convention (flat — no year subfolders):
public/sketches/
dummy.png
dummy.json ← optional metadata, same basename as image
study-hand.jpg
study-hand.json
Rules the loader enforces:
| Layer | Rule |
|---|---|
| Image | Any file with extension jpg/jpeg/png/webp/avif/gif. |
| Metadata | Optional <basename>.json alongside the image. |
| Title | From meta.title, otherwise the file basename. |
| Sort order | Alphabetical by basename. |
Optional <basename>.json shape — every key is optional:
{
"title": "Dummy Sketch",
"date": "2026-04-26",
"medium": "Graphite on paper",
"notes": "Placeholder sketch entry."
}Step-by-step: add a new sketch
- Drop the image into
public/sketches/(any filename). - (Optional) Add
<same-basename>.jsonnext to it for title / date / medium / notes. - Restart
npm run dev— in production, the nextnpm run buildpicks it up.
To remove a sketch, delete the image (and its JSON if present).
Open src/app/about/page.tsx. The bio is plain JSX in two <p> blocks. Edit the text directly. The contact link's href and label can also be updated in place.
Open src/components/Navbar.tsx. The NAV_LINKS array drives the centre nav. To add a link:
const NAV_LINKS = [
{ label: "WORK", href: "/" },
{ label: "SKETCHES", href: "/sketches" },
{ label: "ABOUT", href: "/about" },
{ label: "JOURNAL", href: "/journal" }, // ← new
];(You'd then create src/app/journal/page.tsx to back it.)
The WORK link gets a special hover dropdown listing every year (and how many works are in it). That data is computed automatically from whatever loadWorks() returned, so you never edit it by hand.
Open src/components/RadialMenu.tsx. Two arrays:
INNER— six items closer to the centre.OUTER— six items further out (year shortcuts, social, archive).
Each item is { label, href }. Replace href to point at real Instagram / store / journal URLs.
Open src/components/VantaBackground.tsx. The current effect is Vanta.BIRDS. To swap effects:
- Change the script URL (e.g.
vanta.birds.min.js→vanta.waves.min.js). - Update the call from
window.VANTA.BIRDS({...})towindow.VANTA.WAVES({...})(or NET, FOG, CELLS, RINGS, etc.). - Adjust the option object — Vanta's docs list every effect's options.
Open src/app/globals.css. The two theme variables:
--ink: #ffffff; /* page background */
--bone: #1a1814; /* primary text */Fonts are loaded from Google Fonts in src/app/layout.tsx (Cormorant Garamond + EB Garamond). Swap the <link> URL to change them.
Open src/app/layout.tsx. Edit the metadata export:
export const metadata: Metadata = {
title: "Naabi Kage",
description: "Selected works by Naabi Kage.",
openGraph: { title: "Naabi Kage", description: "…", type: "website" },
};| What | Where | Default |
|---|---|---|
| Gallery autoplay interval | src/components/Gallery.tsx |
10000 ms |
| Slide cross-fade | src/components/Slide.tsx |
1600 ms |
| Title / caption text reveal | src/components/Slide.tsx |
1200 ms |
| Page-route fade-in | src/app/globals.css |
700 ms |
| Easing curve (most things) | global | cubic-bezier(0.42, 0, 0.58, 1) ease-in-out |
Tweak any number in the file shown — no rebuild gymnastics required.
- Push to GitHub (see § 7).
- Go to https://vercel.com/new, import the repo, accept defaults.
- After every push to
main, Vercel rebuilds.loadWorks()runs at build time, so newly added images appear after the next deploy.
No environment variables are required.
git init
git add .
git commit -m "Initial commit"
git branch -M main
git remote add origin https://github.com/coffee-for-coding/Art-Page.git
git push -u origin mainIf the remote already has commits and git push is rejected, either pull and merge:
git pull --rebase origin main
git push -u origin main…or reset the remote (only if you intend to overwrite it):
git push -u origin main --forceQ — I added an image but it isn't showing.
A — Make sure the path is public/images/{4-digit-year}/{slug-folder}/{file}. The slug must be a folder, not a file. Restart npm run dev (or rebuild on Vercel).
Q — Can I have multiple images per artwork?
A — Yes. Drop them all into the same artwork folder; the first image (alphabetical) is used as the cover. Future-proofing for a "details" page is easy — loadWorks() could be extended to return an array of images instead of one.
Q — How do I change the artist name shown in the navbar?
A — Edit the button text in src/components/Navbar.tsx (search for NAABI KAGE). Also update the page title in src/app/layout.tsx.
Q — How do I disable the background animation?
A — Remove <VantaBackground /> from src/app/layout.tsx and set background: var(--ink); back on body in globals.css.
Q — How do I disable autoplay on the gallery?
A — In src/components/Gallery.tsx, comment out the setTimeout call inside the autoplay useEffect.
- Typography: Cormorant Garamond + EB Garamond via Google Fonts.
- Background: Vanta.js BIRDS effect, Three.js r134.
- Layout / engineering: built on Next.js 14 App Router with TypeScript strict mode, no external animation libraries beyond Vanta.