The source for rishimamenon.com — a portfolio for Rishima Menon. Marketer · Creator · London → India. Content people actually want to watch.
A multi-page Next.js static site that does two jobs at once:
- Sells Rishima to brands who want to hire her as a creator (UGC, reels, DVCs).
- Sells Rishima to clients who want a freelance marketer with opinions.
Asks for one thing only: a brief. No newsletter pop-up. No exit-intent modal. No cookie-banner guilt-trip. Type something, hit Send, get a reply within 48 hours.
The design is cream paper + pastel + soft black. The photography is deliberately darker than the surface — so the page looks like a magazine spread, not a Canva template. Typography is Fraunces (warm display serif) and Inter (clean grotesque body), both self-hosted via next/font/google.
![]() #F4ECDFcream — paper |
![]() #A6BCD0dusty blue — accent |
![]() #EBC8B7blush peach — warmth |
![]() #C2D2AFsage — grounded |
![]() #A48FAAlavender — italic flourishes |
![]() #1F1C18soft black — type |
Each service tile cycles through a different pastel — gives the grid a satisfying rhythm without anything ever clashing.
graph LR
classDef static fill:#F4ECDF,stroke:#1F1C18,color:#1F1C18;
classDef dynamic fill:#A48FAA,stroke:#1F1C18,color:#F4ECDF;
classDef seo fill:#EBC8B7,stroke:#1F1C18,color:#1F1C18;
Home["/<br/>Home"]:::static
Work["/work/<br/>Index"]:::static
Case1["/work/fb-launch-reel/"]:::dynamic
Case2["/work/skincare-ugc/"]:::dynamic
Case3["/work/real-estate-launch/"]:::dynamic
Case4["/work/fashion-editorial/"]:::dynamic
Case5["/work/dvc-acting/"]:::dynamic
Case6["/work/lifestyle-personal/"]:::dynamic
About["/about/"]:::static
Contact["/contact/"]:::static
NotFound["/404"]:::static
Sitemap["/sitemap.xml"]:::seo
Robots["/robots.txt"]:::seo
OG["/opengraph-image"]:::seo
Home --> Work
Home --> About
Home --> Contact
Work --> Case1
Work --> Case2
Work --> Case3
Work --> Case4
Work --> Case5
Work --> Case6
Case1 -.next.-> Case2
Case2 -.next.-> Case3
Case3 -.next.-> Case4
Case4 -.next.-> Case5
Case5 -.next.-> Case6
Case6 -.next.-> Case1
Home --- Sitemap
Sitemap --- Robots
Robots --- OG
Static export → 16 prerendered HTML pages, all routes hashed and gzipped.
graph TD
classDef layout fill:#A6BCD0,stroke:#1F1C18,color:#1F1C18;
classDef page fill:#F4ECDF,stroke:#1F1C18,color:#1F1C18;
classDef component fill:#C2D2AF,stroke:#1F1C18,color:#1F1C18;
classDef data fill:#EBC8B7,stroke:#1F1C18,color:#1F1C18;
classDef client fill:#A48FAA,stroke:#1F1C18,color:#F4ECDF;
Layout[app/layout.tsx]:::layout
Fonts[app/fonts.ts<br/>Fraunces + Inter]:::layout
Globals[app/globals.css<br/>palette + grain]:::layout
Nav[components/nav.tsx]:::component
Footer[components/footer.tsx]:::component
Home[app/page.tsx]:::page
Work[app/work/page.tsx]:::page
Case[app/work/[slug]/page.tsx]:::page
About[app/about/page.tsx]:::page
Contact[app/contact/page.tsx]:::page
Hero[components/hero.tsx]:::component
ProjectCard[components/project-card.tsx]:::component
ServiceCard[components/service-card.tsx]:::component
CtaBand[components/cta-band.tsx]:::component
ContactForm[components/contact-form.tsx<br/>'use client']:::client
Content[(lib/content.ts<br/>services · projects · socials)]:::data
Layout --> Fonts
Layout --> Globals
Layout --> Nav
Layout --> Footer
Layout --> Home
Layout --> Work
Layout --> Case
Layout --> About
Layout --> Contact
Home --> Hero
Home --> ProjectCard
Home --> ServiceCard
Home --> CtaBand
Work --> ProjectCard
Work --> CtaBand
Case --> CtaBand
About --> ServiceCard
About --> CtaBand
Contact --> ContactForm
Content -.-> Home
Content -.-> Work
Content -.-> Case
Content -.-> About
Content -.-> ContactForm
Content -.-> Footer
Everything except ContactForm is a server component. lib/content.ts is the single source of truth for services, projects, and social links — edit there, the whole site updates.
graph LR
classDef src fill:#F4ECDF,stroke:#1F1C18,color:#1F1C18;
classDef build fill:#C2D2AF,stroke:#1F1C18,color:#1F1C18;
classDef deploy fill:#A48FAA,stroke:#1F1C18,color:#F4ECDF;
Src[/source<br/>app + components + lib/]:::src
Photos[/public/media/photos<br/>JPGs/]:::src
Build["next build<br/>(Turbopack)"]:::build
Out["/out/<br/>static HTML + JS + CSS"]:::build
Deploy[deploy.mjs<br/>basic-ftp]:::deploy
Host[(Hostinger<br/>LiteSpeed + .htaccess)]:::deploy
Src --> Build
Photos --> Build
Build --> Out
Out --> Deploy
Deploy --> Host
| Layer | Tool |
|---|---|
| Framework | Next.js 16 — App Router, static export |
| Library | React 19 — mostly server, one client island |
| Language | TypeScript 5 — strict |
| Styles | CSS Modules + custom properties (no Tailwind) |
| Fonts | Fraunces + Inter via next/font/google |
| Form relay | Web3Forms — free, no backend |
| Deploy | basic-ftp — six lines mirror out/ to FTP |
| Hosting | Hostinger — LiteSpeed shared, with .htaccess |
Build output is out/. Drag-and-droppable into any folder labelled public_html. Will outlive most SaaS providers.
pnpm install
pnpm dev # next dev → http://localhost:3000
pnpm build # static export → ./out/
pnpm preview # serve out → http://localhost:4000
pnpm deploy # build + ftp mirror to Hostinger (needs env, see below)Static export. Drops out/ onto any static host.
For the Hostinger pipeline, create .env.local:
FTP_HOST=<the host>
FTP_USER=<the user>
FTP_PASS=<the password>Then pnpm deploy and walk away.
Plain FTP (port 21) because Hostinger's shared plan still hasn't shipped FTPS on every box. public/.htaccess handles the LiteSpeed side: HSTS, nosniff, gzip, year-long cache on hashed assets.
app/
├─ page.tsx ← home
├─ layout.tsx ← <html>, metadata, Nav + Footer
├─ fonts.ts ← Fraunces + Inter, swap-loaded
├─ globals.css ← the entire palette lives here
├─ opengraph-image.tsx ← 1200×630 OG built at build time
├─ sitemap.ts ← all routes, force-static
├─ robots.ts ← allow everything
├─ not-found.tsx ← 404
├─ work/
│ ├─ page.tsx ← /work index
│ └─ [slug]/page.tsx ← /work/:slug case study
├─ about/page.tsx
└─ contact/page.tsx
components/
├─ nav.tsx + .module.css
├─ footer.tsx + .module.css
├─ hero.tsx + .module.css
├─ project-card.tsx + .module.css
├─ service-card.tsx + .module.css
├─ cta-band.tsx + .module.css
└─ contact-form.tsx + .module.css ← only client component
lib/
└─ content.ts ← services · projects · socials. one source of truth.
public/
├─ .htaccess ← LiteSpeed config: HSTS, gzip, cache
└─ media/photos/ ← editorial + lifestyle stills
docs/images/ ← README screenshots
deploy.mjs ← basic-ftp mirror script
next.config.ts ← output: "export", trailingSlash: true
A small list of decisions made on purpose:
- Cream, not white. White is a browser default. Cream is a paper.
- Fraunces with
SOFT: 100, WONK: 1in italics — gives those moments the slight handwritten warble that pulls the whole site closer to magazine and further from template. - The hero portrait is in daylight. The work-section photos are darker. The contrast is the move.
- A faint SVG paper-grain overlays the body at
opacity: 0.05. Without it, the cream reads as flat. With it, paper. - The CTA on every page is "Work with me", not "Get in touch". One has gravity. One sounds like a contact form.
- No floating WhatsApp button. If you want her, write a brief.
Open lib/content.ts. Append to the projects array:
{
slug: "kebab-cased-slug",
brand: "Brand name",
title: "Sentence that earns the click",
tag: "Creator" | "Marketing" | "Photography" | "DVC" | "Editorial",
outcome: "One or two lines. Numbers help.",
cover: "/media/photos/your-cover.jpg",
tint: "var(--peach)" | "var(--blue)" | "var(--sage)" | "var(--lavender)",
year: 2026,
role: "Concept · Script · Edit",
brief: "Why it existed.",
approach: ["Step one.", "Step two.", "Step three."],
results: [
{ label: "Reach", value: "1.4M" },
{ label: "CPC", value: "₹6.20" }
],
gallery: ["/media/photos/a.jpg", "/media/photos/b.jpg"]
}Drop the images in public/media/photos/. Rebuild. That's the entire CMS.
- Static HTML, ~11 MB total across 16 routes (photos included).
- Fonts self-hosted with
display: swap, two woff2 files, ~80 KB combined. - No client JS on the home, work, work/[slug], or about pages — only the Contact page hydrates one form component.
- LiteSpeed cache + gzip means each route is one round trip.
| Route | Perf | A11y | SEO | BP |
|---|---|---|---|---|
/ desktop |
82 | 96 | 100 | 100 |
/ mobile |
74 | 96 | 100 | 100 |
/work/:slug desktop |
93 | 94 | 100 | 100 |
/work/:slug mobile |
74 | 94 | 100 | 100 |
The target is ≥95 / 100 / 100 / 100. We have a pre-launch audit punch-list — most of the perf gap is photo compression + WebP twins (#5), and most of the a11y gap is the cream-on-lavender CTA band (#2).
Eight critical items block launch (see label:critical):
- #1 Mobile nav links hidden below 480px
- #2 CTA-band contrast fails WCAG AA (2.53:1)
- #3 Case studies are fictional — flag or replace
- #4 Hero + cover photos crop faces
- #5 Hero LCP 18.7s on mobile
- #6 Contact form Send button spans full column
- #7 Domain hardcoded in 9 places
- #8 Web3Forms key is placeholder
Plus 16 more triaged at high, medium, low. Track here: github.com/Piyushmishra29/rishima-web/issues.
- Built by: Piyush Mishra → piyushmishra.online
- Talent / words / everything: Rishima Menon → @rishimamenon
- Photography: hers, plus whichever friend was holding the camera.
- Type: Fraunces (Undercase Type, OFL) · Inter (Rasmus Andersson, OFL)
The code: MIT, take it. The photographs: © Rishima Menon, ask first.
· end of file ·












