Stop guessing. Start growing.
CodeLens aggregates your GitHub, LeetCode, and Codeforces data into a single AI-powered command center that tells you exactly what to learn next.
- Overview
- Problem Statement
- Tech Stack
- Features
- Project Architecture
- API Reference
- Database Schema
- Frontend Pages & Components
- Backend Modules
- Middleware & Utilities
- Email Service
- Environment Variables
- Getting Started
- Design System
- Contributing
- License
CodeLens is a full-stack MERN (MongoDB, Express, React, Node.js) application that solves developer skill fragmentation. It unifies a developer's technical footprint across GitHub, LeetCode, and Codeforces into a single dashboard and uses Google Gemini AI to synthesize an actionable, personalized learning roadmap.
The platform operates through three core phases:
- Aggregation — Normalizes streaming APIs from GitHub, LeetCode, and Codeforces into a unified telemetry profile.
- AI Synthesis — Passes the normalized data to Google Gemini, which acts as a Staff Engineer cross-referencing algorithmic bottlenecks with commit history.
- Actionable Output — Generates a dynamic, milestone-based roadmap pinpointing the exact sequence of topics to master.
The typical developer's technical growth is fragmented:
- GitHub tracks code velocity, architectural complexity, and OSS collaboration.
- LeetCode evaluates algorithmic prowess and data structure fluency.
- Codeforces measures competitive agility and mathematical optimization.
Existing tools are single-dimensional and leave developers with:
- Directional Paralysis — "What do I learn next?"
- Inefficient Skill Building — Wasting time on mastered concepts while ignoring critical weaknesses.
- Imposter Syndrome — Inability to accurately gauge capability against the broader engineering landscape.
CodeLens stops the guesswork. It dictates the optimal path to engineering supremacy.
| Technology | Version | Purpose |
|---|---|---|
| React | ^19.2.4 | UI framework |
| React Router DOM | ^7.13.2 | Client-side routing |
| Tailwind CSS | ^4.2.2 | Utility-first styling |
| Axios | ^1.14.0 | HTTP client |
| Vite | ^8.0.1 | Build tool & dev server |
| ESLint | ^9.39.4 | Code linting |
| Technology | Version | Purpose |
|---|---|---|
| Node.js (ESM) | — | Runtime |
| Express | ^5.2.1 | Web framework |
| MongoDB / Mongoose | ^9.3.3 | Database & ODM |
| JSON Web Token (JWT) | ^9.0.3 | Authentication |
| bcryptjs | ^3.0.3 | Password hashing |
| Nodemailer | ^8.0.4 | Email/OTP service |
| Zod | ^4.3.6 | Request validation |
| @google/generative-ai | ^0.24.1 | Google Gemini AI integration |
| dotenv | ^17.3.1 | Environment management |
| cors | ^2.8.6 | Cross-origin resource sharing |
- User Registration with email OTP verification
- User Login with JWT-based authentication
- Forgot Password via OTP email flow
- Password Reset with OTP verification
- OTP Resend with 60 second cooldown
- Protected Routes — client-side route guards
- Persistent Sessions — token stored in
localStorage, re-validated on app load - Responsive Navbar — mobile hamburger menu + desktop navigation
- User Profile management (get, update, delete)
- Explore Page — 14-section informational landing showcasing platform features
- Dashboard — "Command Center" with placeholder stats layout
- Global Error Handling — centralized middleware for all error types
- Input Validation — Zod schemas for all API endpoints
- Structured Email Templates — styled HTML emails for OTP verification and password reset
- AI Module — Gemini-powered analysis and roadmap generation
- CP Module — Codeforces & LeetCode data aggregation
- GitHub Module — Repository and commit data fetching
- Tasks Module — Personalized task/challenge tracking
CodeLens/
├── frontend/ # React + Vite + Tailwind CSS
│ ├── public/ # Static assets
│ ├── src/
│ │ ├── assets/ # Images and SVGs
│ │ ├── components/
│ │ │ ├── ai/ # AI insight components
│ │ │ ├── auth/ # Authentication components
│ │ │ ├── codeforces/ # Codeforces specific components
│ │ │ ├── explore/ # Explore page sections
│ │ │ ├── github/ # GitHub data visualization components
│ │ │ ├── shared/ # Reusable UI components
│ │ │ └── ui/ # Generic UI components
│ │ ├── context/
│ │ │ └── AuthContext.jsx # Global auth state (React Context)
│ │ ├── hooks/ # Custom React hooks
│ │ ├── layouts/
│ │ │ └── MainLayout.jsx # Navbar + Footer wrapper
│ │ ├── pages/ # Application routes
│ │ │ ├── AccountCenterPage.jsx
│ │ │ ├── AlgoVersePage.jsx
│ │ │ ├── ApexAIPage.jsx
│ │ │ ├── CodeforcesPage.jsx
│ │ │ ├── ContestAtCoderPage.jsx
│ │ │ ├── ContestCodeChefPage.jsx
│ │ │ ├── ContestCodeforcesPage.jsx
│ │ │ ├── ContestLeetCodePage.jsx
│ │ │ ├── DashboardPage.jsx
│ │ │ ├── ExplorePage.jsx
│ │ │ ├── GitHubCallbackPage.jsx
│ │ │ ├── GitHubIntelligencePage.jsx
│ │ │ ├── LandingPage.jsx
│ │ │ ├── LoginPage.jsx
│ │ │ ├── NotFoundPage.jsx
│ │ │ ├── PracticePage.jsx
│ │ │ ├── PrivacyPage.jsx
│ │ │ ├── SignupPage.jsx
│ │ │ └── TermsPage.jsx
│ │ ├── services/ # API integration
│ │ │ ├── api.js # Axios instance with interceptors
│ │ │ ├── authService.js
│ │ │ ├── codeforcesService.js
│ │ │ ├── githubService.js
│ │ │ └── userService.js
│ │ ├── App.jsx # Root component + routing
│ │ ├── index.css # Global styles
│ │ └── main.jsx # React DOM entry point
│ ├── index.html # HTML entry point
│ ├── package.json
│ └── vite.config.js
│
└── server/ # Node.js + Express (ESM)
├── config/
│ ├── db.js # MongoDB connection
│ ├── env.js # Environment variable validation
│ ├── gemini.js # Gemini AI client
│ └── nvidia.js # Nvidia NIM / AI integrations
├── middlewares/
│ ├── authMiddleware.js # JWT verification middleware
│ └── errorHandler.js # Global error handler
├── models/
│ ├── CodeforcesProfile.js
│ ├── CodeforcesRatingHistory.js
│ ├── CodeforcesSubmission.js
│ ├── Otp.js # OTP schema (10-min TTL)
│ └── User.js # User schema (full telemetry model)
├── modules/
│ ├── ai/ # AI integration module
│ ├── auth/ # Authentication module
│ ├── codeforces/ # Codeforces stats and synchronization
│ ├── cp/ # Competitive programming module
│ ├── github/ # GitHub telemetry module
│ ├── tasks/ # Task tracking module
│ └── user/ # User profile module
├── utils/
│ ├── ApiError.js # Custom error class
│ ├── ApiResponse.js # Standardized response class
│ ├── codeforcesApi.js # Codeforces API wrapper
│ ├── emailService.js # Nodemailer OTP email templates
│ ├── otpHelper.js # OTP generation utility
│ └── tokenHelper.js # JWT sign & verify utilities
├── app.js # Express app config (CORS, routes)
├── server.js # Entry point (DB connect + listen)
└── package.json
The frontend is a React 19 SPA built with Vite and styled exclusively using Tailwind CSS v4 (Brutalist design system — strict black/white/grayscale palette).
Routing is handled by React Router v7. Routes are protected by two guard components:
ProtectedRoute— redirects unauthenticated users to/loginPublicRoute— redirects already-authenticated users away from auth pages
Application Route Map:
/ → LandingPage
/login → LoginPage (PublicRoute)
/signup → SignupPage (PublicRoute)
/forgot-password → ForgotPassword (PublicRoute)
/dashboard → DashboardPage (ProtectedRoute)
/explore → ExplorePage (public)
/terms → TermsPage (public)
/privacy → PrivacyPage (public)
/account → AccountCenterPage (ProtectedRoute)
/codeforces → CodeforcesPage (ProtectedRoute)
/github → GitHubIntelligencePage (ProtectedRoute)
/practice → PracticePage (ProtectedRoute)
/apex → ApexAIPage (ProtectedRoute)
/algoverse → AlgoVersePage (ProtectedRoute)
/contests/codeforces → ContestCodeforcesPage (ProtectedRoute)
/contests/leetcode → ContestLeetCodePage (ProtectedRoute)
/contests/codechef → ContestCodeChefPage (ProtectedRoute)
/contests/atcoder → ContestAtCoderPage (ProtectedRoute)
/auth/github/callback → GitHubCallbackPage
/* → NotFoundPage
Layout: All pages are wrapped in MainLayout which renders <Navbar> at the top and <Footer> at the bottom with a flex-col min-h-screen structure.
State Management: A single AuthContext (React Context API) provides global authentication state: user, token, isAuthenticated, loading, login(), logout(). The context auto-validates the stored token against the /api/user/profile endpoint on every app load.
HTTP Layer: A centralized Axios instance (api.js) with:
- Request interceptor: Automatically attaches
Authorization: Bearer <token>to all outgoing requests. - Response interceptor: On receiving a
401, clears storage and redirects to/login.
The backend follows a strict feature-based modular architecture using ESM (import/export). Each feature module has exactly five files separating concerns:
module/
├── routes.js — Express router + validation middleware mounting
├── controller.js — Parses req/res, delegates to service, returns ApiResponse
├── service.js — Core business logic, orchestration, third-party calls
├── repository.js — All database queries (Mongoose layer)
└── validation.js — Zod schemas + validate() middleware factory
Request Lifecycle:
Request → CORS → express.json() → Route → Zod Validation → Auth Middleware (if protected) → Controller → Service → Repository → MongoDB
↓ (any error)
Global Error Handler
Base path: /api/auth
| Method | Endpoint | Description | Auth Required | Body |
|---|---|---|---|---|
POST |
/register |
Register new user, sends OTP | No | { name, email, password } |
POST |
/verify-otp |
Verify signup OTP, returns JWT | No | { email, otp } |
POST |
/login |
Login with credentials, returns JWT | No | { email, password } |
POST |
/forgot-password |
Sends password reset OTP | No | { email } |
POST |
/reset-password |
Resets password using OTP | No | { email, otp, newPassword } |
POST |
/resend-otp |
Resend OTP for signup or password reset | No | { email, purpose } |
OTP purpose values: "signup" | "forgot-password"
POST /api/auth/register
{
"name": "Kunal Verma",
"email": "kunal@example.com",
"password": "securepass123"
}Response 201:
{
"success": true,
"message": "Registration successful. Please check your email for OTP verification.",
"data": { "id": "...", "name": "Kunal Verma", "email": "kunal@example.com", "isVerified": false }
}POST /api/auth/login
{
"email": "kunal@example.com",
"password": "securepass123"
}Response 200:
{
"success": true,
"message": "Login successful",
"data": {
"token": "<jwt>",
"user": { "id": "...", "name": "Kunal Verma", "email": "...", "role": "user", "handles": {...} }
}
}Base path: /api/user — All endpoints require Authorization: Bearer <token>
| Method | Endpoint | Description | Body |
|---|---|---|---|
GET |
/profile |
Get current user's profile | — |
PUT |
/profile |
Update profile fields | { name?, profile?, handles?, preferences? } |
DELETE |
/profile |
Delete account permanently | — |
PUT /api/user/profile
Authorization: Bearer <token>
{
"handles": {
"github": "kunalverma2512",
"leetcode": "kunalverma",
"codeforces": "kunalv"
},
"profile": {
"bio": "Full-stack developer",
"college": "BITS Pilani",
"location": "India"
}
}GET /api/health
Response: { "status": "ok", "message": "CodeLens API is running" }
| Field | Type | Description |
|---|---|---|
name |
String (required) | Display name |
email |
String (required, unique) | Login identifier, lowercased |
password |
String (select: false) | bcrypt hashed, min 6 chars |
role |
Enum: user | admin |
User role (default: user) |
isVerified |
Boolean | Email verified flag (default: false) |
authProvider |
Enum: local | google | github |
Auth method |
profile.avatar |
String | Avatar URL |
profile.bio |
String | Short biography |
profile.college |
String | College/University |
profile.location |
String | Geographic location |
profile.skills |
[{ name, level }] |
Skill list with beginner/intermediate/advanced level |
handles.codeforces |
String | Codeforces username |
handles.leetcode |
String | LeetCode username |
handles.github |
String | GitHub username |
stats.cpStats |
ObjectId → CPStats | Reference to CP stats (future) |
stats.githubStats |
ObjectId → GithubStats | Reference to GitHub stats (future) |
stats.analytics |
ObjectId → Analytics | Reference to analytics (future) |
goals |
[{ title, type, target, progress, deadline }] |
User-defined goals |
activity.lastActive |
Date | Timestamp of last login |
activity.streak.current |
Number | Current coding streak (days) |
activity.streak.longest |
Number | Longest streak ever |
preferences.theme |
Enum: light | dark |
UI theme preference |
preferences.emailNotifications |
Boolean | Email notifications toggle |
security.resetPasswordToken |
String | Token for password reset |
security.resetPasswordExpiry |
Date | Token expiry |
metadata.onboardingCompleted |
Boolean | First-time setup flag |
metadata.tags |
[String] |
Arbitrary metadata tags |
createdAt / updatedAt |
Date | Mongoose timestamps |
| Field | Type | Description |
|---|---|---|
email |
String (required) | Recipient email, lowercased |
otp |
String (required) | bcrypt-hashed OTP string |
purpose |
Enum: signup | forgot-password |
OTP use case |
createdAt |
Date (TTL: 600s) | Auto-expires after 10 minutes |
Security Note: OTPs are stored as bcrypt hashes (cost factor 4 for speed). MongoDB TTL index auto-deletes records after 10 minutes. Old OTPs for the same email+purpose are deleted before creating a new one.
| Page | Route | Access | Description |
|---|---|---|---|
LandingPage |
/ |
Public | Hero landing (currently minimal wrapper) |
LoginPage |
/login |
PublicRoute | Email/password form, JWT login |
SignupPage |
/signup |
PublicRoute | Two-step form: registration → OTP verification with 60s resend cooldown |
DashboardPage |
/dashboard |
ProtectedRoute | Command Center with GitHub/LeetCode/CF stat cards + Gemini AI insight panel |
ExplorePage |
/explore |
Public | 14-section platform showcase page |
PrivacyPage |
/privacy |
Public | Privacy policy |
TermsPage |
/terms |
Public | Terms of service |
- Step 1: Collects
name,email,password. On submit, callsPOST /api/auth/register. Advances to Step 2. - Step 2: Displays OTP input field. Calls
POST /api/auth/verify-otp. On success, logs the user in and navigates to/dashboard. Features a 60-second resend cooldown managed byuseEffect+setInterval.
| Component | Description |
|---|---|
Navbar.jsx |
Sticky top nav. Responsive with hamburger menu on mobile. Shows Login/Signup for guests; Dashboard link + user avatar initial + Logout for authenticated users. Reads from AuthContext. |
Footer.jsx |
Full-width footer with Platform links (Dashboard, Explore), Integrations (GitHub Sync, LeetCode Auth, Codeforces API), Legal links (Privacy, Terms), and social links. |
ProtectedRoute.jsx |
Renders <Loader> while auth is initializing; redirects to /login if not authenticated. |
PublicRoute.jsx |
Redirects authenticated users away from auth pages (to /dashboard). |
Loader.jsx |
Full-screen loading spinner component. |
| Component | Description |
|---|---|
ForgotPassword.jsx |
Multi-step forgot password flow: email input → OTP verification → new password entry. |
| Component | Description |
|---|---|
ExploreHero.jsx |
Full-width hero banner for the Explore page |
AIExplanation.jsx |
Explains the Gemini AI synthesis engine |
FeatureGrid.jsx |
Grid of platform features |
PlatformSync.jsx |
Showcases GitHub, LeetCode, Codeforces sync |
ArchitectureDeepDive.jsx |
Technical breakdown of the platform architecture |
RoadmapVisualizer.jsx |
Visual representation of AI-generated learning roadmaps |
DailyChallenge.jsx |
Daily problem challenge feature preview |
Leaderboard.jsx |
Community leaderboard preview |
Testimonials.jsx |
User testimonials section |
DataPrivacy.jsx |
Data privacy and security commitment section |
OpenSourceVision.jsx |
Open-source mission and community vision |
FAQSection.jsx |
Frequently Asked Questions accordion |
SubscribeNewsletter.jsx |
Newsletter subscription form |
FinalCTA.jsx |
Final call-to-action to sign up |
| Component | Description |
|---|---|
Hero.jsx |
Generic hero component (used in the UI component library) |
baseURL: set fromVITE_API_BASE_URLenv variable- Request interceptor: Reads
tokenfromlocalStorageand appendsAuthorization: Bearer <token>header. - Response interceptor: Catches
401errors globally, clearslocalStorage, and redirects to/login.
Wraps all POST /api/auth/* endpoints:
register(name, email, password)verifyOtp(email, otp)login(email, password)forgotPassword(email)resetPassword(email, otp, newPassword)resendOtp(email, purpose)
getProfile()→GET /api/user/profileupdateProfile(data)→PUT /api/user/profile
Provides global auth state using React Context API.
State:
| Value | Type | Description |
|---|---|---|
user |
Object | null | Current authenticated user object |
token |
String | null | JWT token from localStorage |
isAuthenticated |
Boolean | True if both token and user are set |
loading |
Boolean | True while initial auth check is in progress |
Methods:
| Method | Description |
|---|---|
login(token, userData) |
Stores token in localStorage, sets user/token state |
logout() |
Clears localStorage and resets user/token to null |
Initialization (useEffect): On mount, reads token from localStorage. If found, calls GET /api/user/profile to validate the token and populate the user object. If the request fails (token expired/invalid), clears all auth state.
Path: server/modules/auth/
Implements complete email-based authentication:
| File | Responsibility |
|---|---|
routes.js |
Mounts Zod validation middleware and delegates to controller methods |
controller.js |
Extracts request body, calls AuthService, returns ApiResponse or forwards errors to global handler |
service.js |
All business logic: hashing passwords/OTPs, generating tokens, orchestrating email sends |
repository.js |
All Mongoose queries: create/find/update users, OTP CRUD |
validation.js |
Zod schemas for all 6 auth endpoints + reusable validate() middleware factory |
Auth Flow — Registration:
POST /register → validate(registerSchema) → AuthController.register
→ AuthService.register():
1. Check if email already exists (throw 409 if so)
2. Hash password with bcrypt (cost 10)
3. Create user (isVerified: false)
4. Generate 6-digit plain OTP
5. Hash OTP (bcrypt cost 4)
6. Store hashed OTP to MongoDB (TTL 10 min)
7. Send styled HTML verification email
→ Return user object (201)
Auth Flow — OTP Verification:
POST /verify-otp → validate(verifyOtpSchema) → AuthController.verifyOtp
→ AuthService.verifyOtp():
1. Find OTP record by email+purpose (throw 400 if not found)
2. bcrypt.compare(plain, hashed) — throw 400 if mismatch
3. Mark user isVerified: true
4. Delete OTP record
5. Generate JWT access token
→ Return { token, user } (200)
Path: server/modules/user/
Manages authenticated user profile operations.
| Endpoint | Method | Description |
|---|---|---|
GET /profile |
authMiddleware → getProfile | Returns full user document |
PUT /profile |
authMiddleware → validate → updateProfile | Updates whitelisted fields only |
DELETE /profile |
authMiddleware → deleteAccount | Hard deletes user document |
Whitelisted update fields: name, profile, handles, preferences
Path: server/modules/ai/
Folder scaffolded. Intended integration with @google/generative-ai to:
- Accept normalized platform stats payload
- Construct a prompt instructing Gemini to act as a Staff Engineer
- Return a deterministic JSON roadmap
- Config:
server/config/gemini.js
Path: server/modules/cp/
Folder scaffolded. Intended to:
- Fetch and store Codeforces rating, contest history, problem-solving stats
- Fetch and store LeetCode solved problem counts by difficulty
Path: server/modules/github/
Folder scaffolded. Intended to:
- Fetch GitHub commit history, repository count, contribution graph
- Store aggregated stats linked to the User document
Path: server/modules/tasks/
Folder scaffolded. Intended to:
- Store AI-generated task items and learning milestones
- Track user task completion progress
JWT authentication guard applied to all protected routes.
- Reads
Authorizationheader, expectsBearer <token>format. - Calls
verifyToken(token)(wrapsjwt.verify). - Extracts
userIdfrom decoded payload. - Fetches user from DB (excludes password).
- Attaches
usertoreq.userfor downstream handlers. - Returns
401on any failure without forwarding to global handler.
Centralized Express error-handling middleware (4-arg signature). Handles:
| Error Type | HTTP Status | Handling |
|---|---|---|
ApiError |
err.statusCode |
Passes through custom message |
Mongoose ValidationError |
400 |
Collects all field messages |
Mongoose CastError |
400 |
Invalid ObjectId format |
Mongoose duplicate key (11000) |
409 |
Extracts conflicting field name |
JsonWebTokenError |
401 |
"Invalid token" |
TokenExpiredError |
401 |
"Token expired" |
Generic Error |
500 |
err.message |
In
developmentmode, thestacktrace is included in the response.
Custom error class extending Error:
new ApiError(statusCode, message, isOperational?, stack?)Standardized response factory:
ApiResponse.success("message", data) // { success: true, message, data }
ApiResponse.error("message", data) // { success: false, message, data }generateAccessToken(payload) // Signs JWT with JWT_SECRET + JWT_EXPIRES_IN
verifyToken(token) // Verifies and decodes JWTgenerateOTP() // Returns a 6-digit numeric string OTPPath: server/utils/emailService.js
Uses Nodemailer with SMTP transport. Two styled HTML email templates:
- Subject: "Verify Your CodeLens Account"
- Purple gradient header (
#667eea → #764ba2) - Displays the 6-digit OTP in large monospace font
- Warning: expires in 10 minutes
- Subject: "Reset Your CodeLens Password"
- Red gradient header (
#ff6b6b → #ee5a24) - Same OTP display format
- Warning: expires in 10 minutes, security advisory not to share
| Variable | Required | Description |
|---|---|---|
PORT |
✅ | Server port (e.g., 5000) |
MONGO_URI |
✅ | MongoDB connection string |
JWT_SECRET |
✅ | Secret key for JWT signing |
JWT_EXPIRES_IN |
✅ | JWT expiry duration (e.g., 7d) |
GEMINI_API_KEY |
✅ | Google Gemini API key |
CLIENT_URL |
✅ | Frontend origin for CORS |
NODE_ENV |
✅ | development | production |
SMTP_HOST |
✅ | SMTP server hostname |
SMTP_PORT |
✅ | SMTP server port (e.g., 587) |
SMTP_USER |
✅ | SMTP sender email |
SMTP_PASS |
✅ | SMTP sender password |
Startup validation:
config/env.jschecks for all required variables at startup and throws immediately if any are missing.
Template (server/.env.example):
PORT=
MONGO_URI=| Variable | Description |
|---|---|
VITE_API_BASE_URL |
Backend API base URL (e.g., http://localhost:5000/api) |
- Node.js v18+ (recommend v20 LTS)
- MongoDB (local instance or MongoDB Atlas)
- npm v9+
- SMTP credentials (Gmail, Mailtrap, or similar)
- Google Gemini API key (from Google AI Studio)
# 1. Navigate to the server directory
cd CodeLens/server
# 2. Install dependencies
npm install
# 3. Create environment file
cp .env.example .env
# Fill in all required variables in .env
# 4. Start the development server
node server.jsThe API will be available at http://localhost:5000.
Health check:
curl http://localhost:5000/api/health
# → { "status": "ok", "message": "CodeLens API is running" }# 1. Navigate to the frontend directory
cd CodeLens/frontend
# 2. Install dependencies
npm install
# 3. Create environment file
echo "VITE_API_BASE_URL=http://localhost:5000/api" > .env
# 4. Start the development server
npm run devThe app will be available at http://localhost:5173.
Other commands:
npm run build # Production build → dist/
npm run preview # Preview production build
npm run lint # ESLint checkCodeLens enforces a strict Brutalist design aesthetic across all UI:
| Design Token | Value |
|---|---|
| Colors | Pure black (#000) and white (#fff) — no other colors |
| Border radius | rounded-none — zero corner radius on all interactive elements |
| Border width | border-4 (4px solid borders) — thick, assertive outlines |
| Typography | font-black, uppercase, tracking-widest or tracking-tighter |
| Header scale | text-5xl to text-9xl — dramatically large headings |
| Shadows | Offset box shadows (shadow-[16px_16px_0_0_rgba(0,0,0,1)]) — no blurred shadows |
| Spacing | Extremely generous padding (py-20, py-32) |
| Disabled states | opacity-50 with grayscale fallbacks |
| Hover effects | Color inversion (white bg → black, black bg → white) or underline with thick decoration-[3px] |
Tailwind CSS version: v4 (via @tailwindcss/vite plugin).
Please read CONTRIBUTING.md before submitting issues or PRs.
Branch naming:
feat/feature-name— new featuresfix/bug-description— bug fixeschore/task-name— docs, dependencies, tooling
Frontend rules:
- No
rounded-*classes (exceptrounded-none) - No colors outside black/white/grayscale
- All headers must use
font-black uppercase
Backend rules:
require()is strictly forbidden — use ESMimport/exportonly- All local imports must include
.jsextension - Follow the Controller → Service → Repository pattern
- All request bodies must have a Zod validation schema
PR checklist:
- Code passes
npm run lint - UI tested on mobile and desktop viewports
- PR description maps solution to the original GitHub issue
This project uses the MIT License.
Built with ❤️ for the developer community as part of GSSoC 2026.