Read this file before touching any code. Every decision in this project flows from this document. This file is the single source of truth for the entire codebase. If something is not documented here, ask before assuming.
What this project is: codeddevs.com is the official company website for CODEDDEVS TECHNOLOGY LTD — a Nigerian technology startup building AI-first software products for African markets. The site audience is investors, press, and partners.
How the system works: The project is a full-stack Next.js 15 monolith — the frontend (public pages), backend (API routes), and admin dashboard all live in one codebase and deploy together on Vercel.
The CMS:
There is a built-in admin dashboard at /admin that serves as the company's CMS. Every piece of content on the public site — team members, products, blog posts, job listings — is managed through this dashboard. No code changes are needed to update content. The admin dashboard is protected by authentication and is only accessible to the single admin user.
The public site:
The public site at / reads all content from a Neon PostgreSQL database via Drizzle ORM. Pages are statically generated at build time and revalidated every hour via ISR. This means the site is fast for visitors but content updates appear within 60 minutes of being published from the admin dashboard.
The database:
A single Neon PostgreSQL database stores all content — team members, products, blog posts, careers, applications, and contact submissions. The schema is defined in src/db/schema.ts and managed via Drizzle Kit migrations. The database stores only text, JSON, and Cloudinary URLs — no images or binary files.
External services:
- Cloudinary — stores all content images. Images are uploaded via the admin dashboard, never stored locally.
- Resend — sends email notifications when someone submits the contact form or applies for a job.
- Vercel — hosts the entire application. Deploys automatically when code merges to
main.
What this site is NOT:
- Not the twizrr product site (twizrr.com is a separate codebase)
- Not a portfolio site
- Not a static site — it has a real backend, database, and CMS
Official company website for CODEDDEVS TECHNOLOGY LTD (RC: 9426867).
- URL: codeddevs.com (placeholder until domain is confirmed)
- Audience: Investors, press, and partners — NOT merchants, buyers, or end users
- Purpose: Present CODEDDEVS as a serious, product-driven technology company. Communicate what we are building, what's coming next, and how our products are evolving. Share product updates, releases, version changes, roadmaps, and announcements.
- Tone: Professional, minimal, text-first — like Anthropic.com or Stripe.com
- This is NOT a portfolio site. Do not treat it like a project showcase or personal portfolio. It is an official company website structured the way established tech companies present themselves.
- This is NOT the twizrr product site. twizrr.com is a completely separate codebase and repo. Every mention of twizrr on this site links OUT to twizrr.com.
Do not change any of these without explicit instruction from the user.
| Layer | Choice |
|---|---|
| Framework | Next.js 15, App Router, TypeScript |
| Runtime | React 19 |
| Database | Neon PostgreSQL |
| ORM | Drizzle ORM |
| Auth | NextAuth.js v5 (credentials, single admin) |
| Blog editor | TipTap (rich text, stores JSON) |
| File storage | Cloudinary |
| Resend | |
| Image cropping | react-image-crop (admin only — never on public pages) |
| Fonts | JetBrains Mono + IBM Plex Sans |
| Hosting | Vercel |
| Package manager | pnpm — NEVER use npm or yarn |
These values are the single source of truth. Never deviate.
Primary Navy: #121F38 — the main brand color
Brand Silver: #D1D6E0 — the secondary brand color
--color-bg: #FFFFFF; /* page background — pure white */
--color-surface: #F4F5F8; /* cards, input fields, subtle sections */
--color-surface-2: #D1D6E0; /* dividers, section backgrounds, tags */
--color-border: #C4CAD6; /* all borders */
--color-text-primary: #121F38; /* headings, nav, important text */
--color-text-body: #2C3A52; /* body copy */
--color-text-muted: #6B7896; /* captions, labels, secondary */
--color-accent: #121F38; /* primary buttons, links, highlights */
--color-accent-hover: #1A2D4F; /* button/link hover */
--color-success: #16A34A;
--color-error: #DC2626;Fonts loaded via next/font/google in src/app/layout.tsx. Never use a <link> tag or CDN.
| Element | Font | Size | Weight | Line Height |
|---|---|---|---|---|
| H1 | JetBrains Mono | 56px | 700 | 1.1 |
| H2 | JetBrains Mono | 40px | 700 | 1.2 |
| H3 | JetBrains Mono | 28px | 600 | 1.3 |
| H4 / Subheading | JetBrains Mono | 20px | 500 | 1.4 |
| Body large | IBM Plex Sans | 18px | 400 | 1.75 |
| Body | IBM Plex Sans | 16px | 400 | 1.7 |
| Small / caption | IBM Plex Sans | 14px | 400 | 1.6 |
| Label / UI tag | IBM Plex Sans | 12px | 500 | — |
- Base unit: 4px (Tailwind default)
- Section vertical padding:
py-24desktop,py-16mobile - Max content width:
max-w-5xl(1024px), centered withmx-auto px-6
Navbar: bg-white border-b border-[#C4CAD6], sticky top
Button primary: bg-[#121F38] text-white hover:bg-[#1A2D4F]
Button secondary: border border-[#C4CAD6] text-[#121F38] hover:bg-[#F4F5F8]
Cards: bg-[#F4F5F8] border border-[#C4CAD6] rounded-lg
Inputs: bg-white border border-[#C4CAD6] text-[#121F38] rounded-md
Active/selected: bg-[#D1D6E0] text-[#121F38]
Badge / tag: bg-[#D1D6E0] text-[#121F38]
Footer: bg-[#F4F5F8] border-t border-[#C4CAD6]
- Professional, not generic. Must feel like a real company website — not AI-generated.
- Minimal and clean. Strong typography, clear messaging, generous whitespace.
- Content balance: 70% text, 30% images.
- Light theme only. No dark mode. No
dark:Tailwind variants. - No animations. Nothing moves. No keyframes, no motion libraries.
- Minimal hover effects. Color or opacity changes only.
- No UI component libraries. Build everything from scratch with Tailwind.
- No gradients. Solid colors only.
- Use borders sparingly. Prefer spacing and background contrast.
- No shadows except subtle
shadow-smon cards where needed. - No visual clutter. Every element must earn its place.
- No generic AI-style layouts.
Logo files in public/logos/:
- Full logo SVG (
/public/logos/wordmark.svg) — Navbar, Footer, formal contexts - Icon-only SVG (
/public/logos/mark.svg) — small spaces, mobile nav - PNG (
/public/fav-icon/logo.png) — favicon only - Never recreate the logo in code. Always use the actual files.
- Navbar logo always links to
/
- Never install an icon library (no lucide-react, heroicons package, react-icons, etc.)
- All icons live in the shared file
src/components/ui/icons.tsx - Only add icons to icons.tsx that are actually used — never pre-populate with unused icons
- Import only what you need in each component — named imports only
- SVG paths sourced from heroicons.com or lucide.dev — copy raw SVG markup only
- All SVGs: appropriate size per context, stroke="currentColor" or fill="#121F38"
- Never use emojis as UI icons — always use inline SVGs from icons.tsx
// CORRECT — named import from shared icons file
import { TrophyIcon, ArrowRightIcon } from '@/components/ui/icons'
// WRONG — never install or import from icon libraries
import { Trophy } from 'lucide-react'
import * as Icons from '@/components/ui/icons' // never import everythingcodeddevs-website/
├── src/
│ ├── app/
│ │ ├── (public)/
│ │ │ ├── layout.tsx
│ │ │ ├── page.tsx # Home
│ │ │ ├── about/page.tsx
│ │ │ ├── products/
│ │ │ │ ├── page.tsx
│ │ │ │ └── [slug]/page.tsx
│ │ │ ├── blog/
│ │ │ │ ├── page.tsx # displayed as "Updates"
│ │ │ │ └── [slug]/page.tsx
│ │ │ ├── team/page.tsx
│ │ │ ├── careers/page.tsx
│ │ │ └── contact/page.tsx
│ │ ├── admin/
│ │ │ ├── layout.tsx
│ │ │ ├── login/page.tsx
│ │ │ ├── dashboard/page.tsx
│ │ │ ├── team/ (page, new, [id])
│ │ │ ├── products/ (page, new, [id])
│ │ │ ├── blog/ (page, new, [id])
│ │ │ ├── careers/ (page, new, [id])
│ │ │ ├── applications/page.tsx
│ │ │ └── messages/page.tsx
│ │ ├── api/
│ │ │ ├── auth/[...nextauth]/route.ts
│ │ │ ├── contact/route.ts
│ │ │ ├── careers/apply/route.ts
│ │ │ ├── upload/route.ts
│ │ │ └── admin/
│ │ │ ├── team/ (route, [id])
│ │ │ ├── products/ (route, [id])
│ │ │ ├── blog/ (route, [id])
│ │ │ ├── careers/ (route, [id])
│ │ │ ├── applications/ (route, [id])
│ │ │ └── messages/ (route, [id])
│ │ ├── layout.tsx
│ │ ├── not-found.tsx
│ │ ├── robots.ts
│ │ ├── sitemap.ts
│ │ └── globals.css
│ ├── components/
│ │ ├── layout/
│ │ │ ├── Navbar.tsx
│ │ │ ├── Footer.tsx
│ │ │ └── AdminSidebar.tsx
│ │ ├── ui/
│ │ │ ├── Button.tsx
│ │ │ ├── Badge.tsx
│ │ │ ├── Card.tsx
│ │ │ ├── Input.tsx
│ │ │ ├── Textarea.tsx
│ │ │ └── icons.tsx # shared inline SVG icons
│ │ ├── sections/
│ │ │ ├── HeroSection.tsx
│ │ │ ├── ProductsSection.tsx
│ │ │ ├── LatestReleasesSection.tsx
│ │ │ ├── RecognitionSection.tsx
│ │ │ ├── AboutTeaser.tsx
│ │ │ └── TeamSection.tsx
│ │ ├── blog/
│ │ │ └── PostContent.tsx
│ │ ├── careers/
│ │ │ └── ApplicationForm.tsx
│ │ ├── contact/
│ │ │ └── ContactForm.tsx
│ │ └── admin/
│ │ ├── RichTextEditor.tsx
│ │ ├── ImageUpload.tsx # includes react-image-crop
│ │ └── DataTable.tsx
│ ├── db/
│ │ ├── index.ts
│ │ ├── schema.ts
│ │ ├── queries.ts
│ │ └── migrations/
│ ├── lib/
│ │ ├── auth.ts
│ │ ├── email.ts
│ │ ├── cloudinary.ts
│ │ └── utils.ts
│ └── types/
│ └── index.ts
├── scripts/
│ └── seed-admin.ts # gitignored — local use only
├── drizzle.config.ts
├── middleware.ts
├── next.config.mjs
├── tailwind.config.ts
├── tsconfig.json
├── .env.local # gitignored
├── .env.example
├── AGENTS.md
└── package.json
id, name, role, bio, photo_url, linkedin_url, github_url,
twitter_url, order_index, is_active, created_at, updated_atid, name, slug, tagline, description, cover_url,
external_url, github_url, status, is_featured,
order_index, created_at, updated_atid, title, slug, excerpt, content (json — TipTap),
cover_url, author, category, is_published,
show_in_recognition, placement, published_at,
created_at, updated_atcategory enum: 'Product Update' | 'Announcement' | 'Roadmap' | 'Story'
show_in_recognition: boolean, notNull, default(false)
Controls whether post appears in the Recognition section on the home page.
Admin toggles this manually per post.
placement: text, nullable
Controls the placement badge shown on the Recognition card.
Values: '1st' | '2nd' | '3rd' | 'winner' | null
Only relevant when show_in_recognition is true.
Displayed as an inline SVG icon + label from icons.tsx — never as an emoji.
id, title, type, location, description,
requirements, is_open, created_at, updated_attype enum: 'full-time' | 'contract' | 'volunteer'
id, career_id (→ careers.id onDelete cascade),
full_name, email, portfolio_url, github_url,
cover_letter, status, created_atstatus enum: 'pending' | 'reviewed' | 'rejected'
id, full_name, email, subject, message, is_read, created_atid, email, password_hash, created_atDATABASE_URL= # Neon pooled connection string
DATABASE_URL_UNPOOLED= # Neon direct connection (migrations only)
NEXTAUTH_SECRET=
NEXTAUTH_URL=http://localhost:3000
NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME=
NEXT_PUBLIC_CLOUDINARY_UPLOAD_PRESET=
CLOUDINARY_API_KEY=
CLOUDINARY_API_SECRET=
RESEND_API_KEY=
CONTACT_NOTIFICATION_EMAIL=codeddevs.team@gmail.comLocal development: .env.local points to Neon dev branch connection strings.
Production: Vercel environment variables point to Neon production branch.
Use DATABASE_URL for all app queries.
Use DATABASE_URL_UNPOOLED only in drizzle.config.ts for migrations.
| Method | Route | Description |
|---|---|---|
| POST | /api/contact |
Save + email notification |
| POST | /api/careers/apply |
Save + email notification |
| Method | Route | Description |
|---|---|---|
| POST | /api/upload?folder=[folder] |
Upload to Cloudinary in correct subfolder |
| GET/POST | /api/admin/team |
List / create |
| GET/PUT/DELETE | /api/admin/team/[id] |
Read / update / delete |
| GET/POST | /api/admin/products |
List / create |
| GET/PUT/DELETE | /api/admin/products/[id] |
Read / update / delete |
| GET/POST | /api/admin/blog |
List / create |
| GET/PUT/DELETE | /api/admin/blog/[id] |
Read / update / delete |
| GET/POST | /api/admin/careers |
List / create |
| GET/PUT/DELETE | /api/admin/careers/[id] |
Read / update / delete |
| GET | /api/admin/applications |
List |
| PUT | /api/admin/applications/[id] |
Update status |
| GET | /api/admin/messages |
List |
| PUT/DELETE | /api/admin/messages/[id] |
Mark read / delete |
- Team photos →
?folder=team - Product covers →
?folder=products - Blog covers →
?folder=blogs - Inline blog images →
?folder=blogs/inline
All uploads go to codeddevs-website/[folder]/ in Cloudinary.
// middleware.ts — uses getToken from next-auth/jwt
// /api/admin/* + no session → 401 JSON
// /admin/* + no session → redirect to /admin/login
// /admin/login + session → redirect to /admin/dashboardSix sections in this exact order:
1. Hero
- Headline: "Engineering Software That Works for Africa"
- Subtext: "We build AI-first software products for African markets — from first principles, not adaptations."
- CTAs: "See Our Products" → /products | "Get in Touch" → /contact
- Kody mascot (kodyfigma.svg — neutral) featured in hero
2. Products Section
- Heading: "What We're Building"
- Fetches products where is_featured = true
- Cards: name, tagline, status badge, external link
3. Latest Releases
- Heading: "Latest Releases"
- Fetches 3 most recent published posts (ALL categories)
- Cards: title, excerpt, date, category badge, dynamic CTA
4. Recognition
- Heading: "Recognition"
- Fetches blog posts where show_in_recognition = true AND is_published = true
- Ordered by published_at DESC, limit 3
- Cards — text only, no cover image
- Placement display uses inline SVG icons from icons.tsx — never emojis
- If no recognition posts exist, section does not render in production
5. About Teaser
- Company mission — 2 paragraphs
- No "Meet the Team" link — TeamSection below handles that
6. Team Section
- Heading: "The Team"
- Shows only 3 founders (order_index 0, 1, 2)
- Each card: photo (80x80 rounded-full), name, role — no bio
- "Meet the full team →" link to /team
- TeamSection fetches its own data internally — no props from page.tsx
Sections behave differently based on environment:
const isDev = process.env.NODE_ENV === 'development'
// In development: show section with empty state message
// In production: return null (hide section completely)This applies to: ProductsSection, LatestReleasesSection, RecognitionSection, TeamSection. Empty state style: bg-[#F4F5F8] rounded-lg p-8, text-sm text-[#6B7896], centered.
- Mission, approach, open-source commitment
- Company facts: RC 9426867 | Lagos, Nigeria | Est. March 2026
- Lists all products from DB ordered by order_index
- Each card: name, tagline, status badge
- Links to /products/[slug] (internal) and external_url (external)
- Full product page: name, tagline, description, status, cover image
- External link + GitHub link
- Related blog posts
- URL stays /blog. All user-facing labels say "Updates"
- Lists published posts ordered by published_at DESC
- Filterable by: All | Product Update | Announcement | Roadmap | Story
- Each card: category badge, title, excerpt, author, date, dynamic CTA
Editorial layout:
[Cover image — full width, 1200x630px, priority prop for LCP]
CATEGORY BADGE
Title (JetBrains Mono, H1)
By [author] · [formatted date] · [X min read]
─────────────────────────────────────────────
[TipTap rendered content — IBM Plex Sans body, max-w-3xl]
- Fetches ALL team_members where is_active = true, ordered by order_index
- Each card: photo, name, role, bio, social links
- Founders:
- Kareem Aliameen — Founder & CEO Kareem is the Founder and CEO of CodedDevs Technology LTD, leading the company's strategy, product vision and development, and technical direction. A full-stack engineer working primarily in JavaScript and TypeScript, he is highly skilled at leveraging AI for development, research, and productivity. He brings a background spanning graphic design, digital commerce, and entrepreneurship, and is currently studying at Miva University.
- Yusuf Ibrahim Ayinla — Co-Founder & CTO Yusuf is the Co-Founder and CTO of CodedDevs Technology LTD, responsible for the technical architecture across the company's products. A full-stack engineer working in JavaScript and TypeScript, he is highly skilled at leveraging AI for development and research, and is known for his curiosity, depth of thinking, and ability to move quickly across technologies.
- Amoo Mustakheem Olamilekan — Co-Founder & COO Mustakheem is the Co-Founder and COO of CodedDevs Technology LTD, leading business development, partnerships, and growth strategy. A full-stack engineer with a background in Node.js and Python, he brings strong skills in networking, outreach, and identifying opportunities.
- Lists open roles
- Empty state: "No open roles right now. Send us a message." → /contact
- Application form: inline below role card, 'use client'
- Two columns desktop, stacked mobile
- Left: company info, email, social links
- Right: contact form
- Subjects: General Inquiry | Partnership | Press | Investment | Other
- Email: codeddevs.team@gmail.com
- Socials: GitHub, X, TikTok, YouTube, Instagram
/public/logos/wordmark.svg— full logo/public/logos/mark.svg— icon only/public/mascot/kody-smilefigma.svg— smiling Kody/public/mascot/kodyfigma.svg— neutral Kody/public/fav-icon/logo.png— favicon- Nothing else goes in public/
- Team photos:
codeddevs-website/team/— 800x800px - Product covers:
codeddevs-website/products/— 1200x630px - Blog covers:
codeddevs-website/blogs/— 1200x630px - Inline article images: 1200x800px
- Team photos → square crop (1:1)
- Blog covers → landscape crop (1200:630)
- Product covers → landscape crop (1200:630)
- Inline images → free crop
react-image-cropimported ONLY inImageUpload.tsx— never on public pages
getBlogCoverUrl(url) // f_auto,q_auto,w_1200,h_630,c_fill
getBlogThumbnailUrl(url) // f_auto,q_auto,w_800,h_420,c_fill
getRecognitionCardUrl(url) // f_auto,q_auto,w_600,h_315,c_fill
getTeamPhotoUrl(url) // f_auto,q_auto,w_400,h_400,c_fill,g_face
getProductCoverUrl(url) // f_auto,q_auto,w_1200,h_630,c_fill- Always use Next.js
<Image>for Cloudinary images - SVGs from public/ can use
<Image>or<img> - Never use raw
<img>for content images - Always set meaningful
alttext - Add
priorityprop to above-the-fold images
| File | Variant | Use where |
|---|---|---|
kody-smilefigma.svg |
Smiling | 404 page, empty states, contact page |
kodyfigma.svg |
Neutral/confident | Hero section, careers page |
- Never smaller than 120px
- Always on white or light surface
- Use sparingly — not as filler
export const revalidate = 3600 // all public pagesconst [products, posts] = await Promise.all([...])- Blog list: never fetch
contentcolumn - Products list: never fetch
descriptionon list view
In Next.js 15, fetch() is NOT cached by default. If using fetch() directly in server components, add appropriate cache settings explicitly.
This codebase runs on Next.js 15 + React 19. Any new dynamic route page MUST follow these patterns:
// CORRECT — Next.js 15 way
export default async function Page({
params,
}: {
params: Promise<{ slug: string }>
}) {
const { slug } = await params
// use slug
}
// WRONG — Next.js 14 way, will break
export default function Page({
params,
}: {
params: { slug: string }
}) {
const { slug } = params // breaks in Next.js 15
}export default async function Page({
searchParams,
}: {
searchParams: Promise<{ [key: string]: string | string[] | undefined }>
}) {
const { q } = await searchParams
}generateStaticParams still works the same way as Next.js 14. No changes needed.
fetch() responses are no longer cached by default. Add cache: 'force-cache' explicitly if caching is needed.
- Server components by default —
'use client'only when needed - Drizzle for all DB queries — no raw SQL
- Auth check first on every admin route — 401 if no session
- Zod validation on every API route that accepts a body
- pnpm only — never npm or yarn
- Cloudinary for all content images — use transformation helpers
- Resend for all email — never nodemailer or sendgrid
- next/font/google for fonts — no CDN link tags
- No UI component libraries on public pages — build from scratch with Tailwind. Exception:
react-image-cropinImageUpload.tsxadmin only - cn() for all conditional classNames
- No animations — nothing moves
- Light theme only — no dark: variants
- No gradients — solid colors only
- TypeScript strict — no any, no @ts-ignore
- @/ imports only — no relative ../../ imports
- Product/external links always target="_blank" rel="noopener noreferrer"
- migrations/ is read-only — only Drizzle Kit writes here
- Logo files only — never recreate logo in code
- "Products" not "Projects" — everywhere in UI, routes, and code
- Blog URL /blog, displayed as "Updates" in all user-facing labels
- Use borders sparingly — prefer spacing and background contrast
- Design must feel human, not AI-generated
- No emojis in UI components — use inline SVG icons from icons.tsx only
- Never install icon libraries — use shared icons.tsx with raw SVG paths
- Always await params and searchParams in dynamic route pages (Next.js 15)
- seed scripts go in scripts/ folder and are gitignored — never commit them
- Mobile-first responsive design always. Use Tailwind breakpoints in this order: base (mobile) → md (tablet 768px) → lg (desktop 1024px). Never design desktop-first and patch mobile after.
| Field | Value |
|---|---|
| Company | CODEDDEVS TECHNOLOGY LTD |
| RC Number | 9426867 |
| Incorporated | 18 March 2026 |
| Location | Lagos, Nigeria |
| codeddevs.team@gmail.com | |
| GitHub | github.com/coded-devs |
| X | @CodedDevs |
| TikTok | @CodedDevs |
| YouTube | @CodedDevs |
| @codeddevs_ | |
| Main product | twizrr → twizrr.com |
Admin visits /admin/blog/new
Writes post in TipTap editor
Uploads cover image (crops → Cloudinary → URL saved)
Sets category, show_in_recognition, placement if applicable
Clicks Publish (is_published = true, published_at = now())
↓
Post saved to blog_posts table in Neon dev branch (local)
or Neon production branch (live site)
↓
Within 1 hour (ISR revalidation):
- Appears in /blog list
- Appears in Latest Releases on home page (if one of 3 most recent)
- Appears in Recognition section (only if show_in_recognition = true)
- Has its own page at /blog/[slug]
Admin visits /admin/team/new
Fills in name, role, bio
Uploads photo (crops to square → Cloudinary → URL saved)
Adds social links, sets order_index
↓
Record saved to team_members table
↓
Within 1 hour:
- Appears on /team page (full bio, all details)
- Appears in TeamSection on home page (photo + name + role only, limit 3)
Admin visits /admin/products/new
Fills in name, slug, tagline, description, status
Uploads cover image, sets is_featured = true for home page
Sets external_url (e.g. twizrr.com)
↓
Record saved to products table
↓
Within 1 hour:
- Appears on /products list
- Appears on home page Products section (if is_featured = true)
- Has its own page at /products/[slug]
Visitor submits /contact form
POST /api/contact validates → saves to contact_submissions
Sends email via Resend to CONTACT_NOTIFICATION_EMAIL
Admin reads in /admin/messages, marks as read
Visitor applies on /careers
POST /api/careers/apply validates → verifies role is open
Saves to career_applications → sends email notification
Admin reviews in /admin/applications, updates status
| Section | URL | What it controls |
|---|---|---|
| Dashboard | /admin/dashboard | Overview stats, recent messages, recent applications |
| Team | /admin/team | Team member profiles on /team and TeamSection on home |
| Products | /admin/products | Products on /products and home page |
| Blog | /admin/blog | All posts — /blog, Latest Releases, Recognition |
| Careers | /admin/careers | Job listings on /careers |
| Applications | /admin/applications | Career applications |
| Messages | /admin/messages | Contact form submissions |
/bloglist — all published posts- Home Latest Releases — automatic, 3 most recent published
- Home Recognition — manual, only posts with show_in_recognition = true
When creating a blog post, admin sets:
category— filter tab on /blogshow_in_recognition— toggle to feature in Recognition sectionplacement— SVG medal icon (only shown when show_in_recognition is on)
main → production (deploys to codeddevs.com via Vercel)
dev → staging (integration branch)
feature/* → individual features or fixes
Neon production branch → used by Vercel production
Neon dev branch → used by local .env.local
Neon preview/pr-[n] → auto-created per PR by GitHub Actions,
auto-deleted when PR closes
1. Branch from dev:
git checkout dev && git pull origin dev
git checkout -b feature/your-feature-name
2. Build on the feature branch
3. Open PR: feature/* → dev
- Neon auto-creates a preview DB branch
- CodeRabbit reviews automatically
- CI must pass (type check + lint + build)
- @onerandomdevv reviews and approves
4. Merge to dev → test on staging
5. PR: dev → main → Vercel deploys to production
Neon preview branch auto-deleted on close
mainanddev— require PR, CI passing, @onerandomdevv approval, no direct pushes- Feature branches — push freely
feat: new feature
fix: bug fix
perf: performance improvement
chore: config, deps, tooling
refactor: restructure, no behaviour change
docs: documentation only
return NextResponse.json({ data: record }, { status: 200 })
return NextResponse.json({ data: records }, { status: 200 })
return NextResponse.json({ success: true }, { status: 200 })
return NextResponse.json({ data: record }, { status: 201 })return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
return NextResponse.json({ error: 'Invalid input', details: zodError.flatten() }, { status: 400 })
return NextResponse.json({ error: 'Not found' }, { status: 404 })
return NextResponse.json({ error: 'Something went wrong' }, { status: 500 })export async function GET() {
// 1. Auth check — always first
const session = await auth()
if (!session) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
// 2. Business logic
try {
const data = await db.select()...
return NextResponse.json({ data })
} catch (error) {
console.error(error)
return NextResponse.json({ error: 'Something went wrong' }, { status: 500 })
}
}- Never commit secrets — .env.local is gitignored
- seed scripts are gitignored — never commit scripts/seed-admin.ts
- All PRs require review from @onerandomdevv before merging
- Auth, DB schema, deployment changes need explicit human approval
- Never auto-merge agent-generated code
- Rotate keys immediately if credentials are exposed
- Security contact: codeddevs.team@gmail.com
Mobile-first always. Every component is designed for mobile (375px) first, then enhanced for larger screens using Tailwind breakpoints. Never write desktop styles first and try to patch mobile after.
base → 0px+ mobile phones (375px target)
sm → 640px+ large phones / small tablets (use sparingly)
md → 768px+ tablets
lg → 1024px+ desktop (most layout changes happen here)
xl → 1280px+ large desktop (use sparingly)
Navbar:
mobile: hamburger menu, logo left, menu button right
lg: full nav links visible, logo left, CTA button right
Hero section:
mobile: single column, text stacked, Kody mascot hidden
lg: two columns — text left (flex-1), Kody right (w-80)
Products section:
mobile: single column, full width cards
lg: max-w-xl cards, left-aligned — do not stretch to full width
Latest Releases & Recognition:
mobile: grid-cols-1 (single column)
md: grid-cols-2
lg: grid-cols-3
gap: gap-6 at all breakpoints
About page — two-column sections:
mobile: stacked — heading above, body below
lg: flex-row — heading left (w-1/3), body right (flex-1)
Team section (home page):
mobile: grid-cols-1
md: grid-cols-3
Team page (/team):
mobile: grid-cols-1
md: grid-cols-2
lg: grid-cols-3
Contact page:
mobile: stacked — info above, form below
lg: two columns — info left (w-1/3), form right (flex-1)
Footer:
mobile: stacked — logo+tagline, then nav columns, then bottom bar
lg: logo+tagline left, nav columns right, bottom bar as row
H1 hero: text-4xl md:text-5xl lg:text-7xl
H2 section: text-2xl md:text-3xl lg:text-[40px]
H3: text-xl md:text-2xl
Body: text-base (16px) — never scale down on mobile
Kody mascot in Hero: hidden on mobile, block on lg
Full nav links: hidden on mobile, flex on lg
Hamburger menu: flex on mobile, hidden on lg
Two-column layouts: stack on mobile, side-by-side on lg
Before committing any visual design work, test at these widths:
- 375px — iPhone SE (smallest target)
- 390px — iPhone 14
- 768px — iPad portrait
- 1024px — iPad landscape / small desktop
- 1280px — standard desktop
- 1440px — large desktop
Use browser DevTools responsive mode. Never ship a change that breaks any of these widths.
Last updated: May 2026