The official blog for the Japanese Club at VIT Chennai (ζ₯ζ¬θͺγ―γ©γ).
A newspaper-inspired web application built with Next.js 16 and Supabase.
- Overview
- Features
- Tech Stack
- Project Structure
- Getting Started
- Architecture
- Deployment
- Contributing
- License
Wabi Sabi Weekly is a blog platform built for VIT Chennai's Japanese Club. It features a vintage newspaper-style design with a creamy paper texture, serif typography (Playfair Display + Noto Serif JP), and Japanese-inspired aesthetics. Content is authored in Markdown via a password-protected editor and stored in a Supabase PostgreSQL database.
| Feature | Description |
|---|---|
| π° Newspaper Layout | Homepage with lead article + sidebar articles in a classic newspaper grid |
| βοΈ Secret Editor | Password-protected CMS at /secret-editor for creating, editing, and deleting posts |
| π Markdown Support | Write post content in Markdown β rendered to HTML via remark + remark-html |
| π¬ Comments | Readers can leave comments on posts (with optional author names) |
| β€οΈ Likes & Views | Automatic view counting and like functionality (likes capped at view count) |
| π Archives Menu | Slide-out hamburger menu listing all posts for easy navigation |
| π Japanese Typography | Noto Serif JP font + vertical text (tategaki) CSS utility |
| π Vercel-Ready | Designed for seamless deployment on Vercel |
| Technology | Purpose |
|---|---|
| Next.js 16 | React framework (App Router) |
| React 19 | UI library |
| Supabase | PostgreSQL database & client SDK |
| Remark | Markdown β HTML processing |
| date-fns | Date formatting utilities |
| gray-matter | Front-matter parsing |
Club-Blog/
βββ app/
β βββ layout.js # Root layout (fonts, metadata)
β βββ page.js # Homepage β lead + sidebar articles
β βββ globals.css # Global styles, CSS variables, utilities
β βββ Home.module.css # Homepage-specific styles
β βββ page.module.css # Additional page styles
β βββ components/
β β βββ Header.js # Blog masthead ("Wabi Sabi Weekly")
β β βββ Header.module.css
β β βββ HamburgerMenu.js # Slide-out archives navigation
β β βββ HamburgerMenu.module.css
β β βββ Engagement.js # Views, likes, and comments widget
β β βββ Engagement.module.css
β βββ posts/
β β βββ [slug]/
β β βββ page.js # Individual post page (SSG with dynamic routes)
β βββ secret-editor/
β β βββ page.js # Password-protected CMS editor
β βββ api/
β βββ create-post/
β β βββ route.js # POST β create a new post
β βββ post/
β β βββ route.js # GET / PUT / DELETE β single post CRUD
β βββ posts-list/
β β βββ route.js # GET β list all posts
β βββ engagement/
β βββ route.js # GET / POST β views, likes, comments
βββ lib/
β βββ supabase.js # Supabase client initialization
β βββ posts.js # Post data fetching & markdown processing
β βββ engagement.js # Engagement logic (views, likes, comments)
βββ public/ # Static assets (SVG icons)
βββ package.json
βββ next.config.mjs
βββ jsconfig.json
βββ eslint.config.mjs
- Node.js β₯ 18.x
- npm (comes with Node.js)
- A Supabase project (create one free)
# Clone the repository
git clone https://github.com/OscarPastry/Club-Blog.git
cd Club-Blog
# Install dependencies
npm installCreate a .env.local file in the project root:
# Supabase connection
NEXT_PUBLIC_SUPABASE_URL=https://your-project-id.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key-here
# Secret Editor password
NEXT_PUBLIC_EDITOR_PASSWORD=your-secret-password| Variable | Description |
|---|---|
NEXT_PUBLIC_SUPABASE_URL |
Your Supabase project URL (found in Project Settings β API) |
NEXT_PUBLIC_SUPABASE_ANON_KEY |
Your Supabase anonymous/public API key |
NEXT_PUBLIC_EDITOR_PASSWORD |
Password to access the /secret-editor CMS page |
β οΈ Note:NEXT_PUBLIC_EDITOR_PASSWORDis exposed to the client. This provides basic access controlβnot production-grade security. For production, consider server-side authentication.
Create the following tables in your Supabase project's SQL Editor:
-- Posts table
CREATE TABLE posts (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
slug TEXT UNIQUE NOT NULL,
title TEXT NOT NULL,
date TEXT NOT NULL,
author TEXT NOT NULL DEFAULT 'Editor',
summary TEXT,
content TEXT,
views INTEGER DEFAULT 0,
likes INTEGER DEFAULT 0,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Comments table
CREATE TABLE comments (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
post_slug TEXT REFERENCES posts(slug) ON DELETE CASCADE,
author TEXT DEFAULT 'Anonymous',
text TEXT NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
);# Development server (with hot reload)
npm run dev
# Production build
npm run build
npm startThe app will be available at http://localhost:3000.
| Route | File | Description |
|---|---|---|
/ |
app/page.js |
Homepage with lead article and sidebar β server-rendered |
/posts/[slug] |
app/posts/[slug]/page.js |
Individual post page with markdown content, engagement widget |
/secret-editor |
app/secret-editor/page.js |
Password-protected editor for CRUD operations on posts |
All pages use revalidate = 0 for fresh data on every request.
| Endpoint | Method | Description |
|---|---|---|
/api/create-post |
POST |
Create a new post (auto-generates a unique slug from the title) |
/api/post?id=<slug> |
GET |
Fetch a single post by slug |
/api/post |
PUT |
Update an existing post |
/api/post?id=<slug> |
DELETE |
Delete a post by slug |
/api/posts-list |
GET |
List all posts sorted by date (newest first) |
/api/engagement?slug=<slug> |
GET |
Get views, likes, and comments for a post |
/api/engagement |
POST |
Perform an engagement action (view, like, comment, delete-comment) |
// Increment view
{ "slug": "post-slug", "action": "view" }
// Like a post (capped at view count)
{ "slug": "post-slug", "action": "like" }
// Add a comment
{ "slug": "post-slug", "action": "comment", "payload": { "text": "...", "author": "Name" } }
// Delete a comment (editor use)
{ "slug": "post-slug", "action": "delete-comment", "payload": { "commentId": 123 } }| Module | Purpose |
|---|---|
lib/supabase.js |
Initializes and exports the Supabase client using environment variables |
lib/posts.js |
getSortedPostsData(), getAllPostIds(), getPostData(slug) β fetches post data from Supabase and converts Markdown to HTML |
lib/engagement.js |
getPostEngagement(), incrementView(), incrementLike(), addComment(), deleteComment() β manages engagement data in Supabase |
| Component | Type | Description |
|---|---|---|
Header |
Server | Blog masthead displaying "Wabi Sabi Weekly", week number, theme, and Japanese text |
HamburgerMenu |
Client | Slide-out drawer with home link and full post archive list |
Engagement |
Client | Interactive widget showing views/likes counters, comment list, and comment submission form |
This project is optimized for Vercel:
- Push your repository to GitHub
- Import the project in Vercel Dashboard
- Add the environment variables (
NEXT_PUBLIC_SUPABASE_URL,NEXT_PUBLIC_SUPABASE_ANON_KEY,NEXT_PUBLIC_EDITOR_PASSWORD) in Project Settings β Environment Variables - Deploy β Vercel will auto-detect Next.js and configure the build
- Fork the repository
- Create a feature branch:
git checkout -b feature/my-feature - Commit your changes:
git commit -m "Add my feature" - Push to the branch:
git push origin feature/my-feature - Open a Pull Request
This project is private to the Japanese Club at VIT Chennai.
For inquiries, contact the club's technical team.