Design Direction
Purpose: Market a serious, production-grade AI platform for enterprises and government with an India-first identity. Convey trust, scale, and clarity while keeping the interface modern and confident.
Tone:
-
Clean, assertive, and product-focused.
-
No “AI slop” gradients or neon overload.
-
Strong typographic hierarchy with bold headlines, tight line lengths, and concise copy.
-
Subtle motion; everything feels composed, never chaotic.
Differentiating Moves:
-
Hero section is sparse but powerful: a single strong headline, subhead, and one primary CTA, supported by logos and a simple “platform pillars” strip.
-
Reusable “section patterns”: three-up cards, badge rows, testimonial modules, research/update cards.
-
India-first cues via copy (“India’s Sovereign AI Platform”, “AI for all from India”), not cliché tricolor overload.
Tech Stack
-
Framework: React (Vite or Next.js).
-
Animation: Framer Motion for:
-
Hero text entrance (fade + slight upward motion).
-
Logo/marquee scroll or fade-in.
-
Card hover elevation & shadow transitions.
-
Styling:
-
CSS Modules or a single global CSS file with CSS variables for colors and typography.
-
Prefer utility-ish classes but handcrafted, not Tailwind.
Typography
Use a strong sans family with good weight range and a refined secondary face.
Recommended pairing:
-
Display / Headings: Satoshi, General Sans, or Metropolis (download via Fontshare or local files).
-
Body: IBM Plex Sans, Source Sans 3, or System UI as fallback.
Example @font-face setup:
@font-face {
font-family: 'Satoshi';
src: url('/fonts/Satoshi-Regular.woff2') format('woff2');
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Satoshi';
src: url('/fonts/Satoshi-Medium.woff2') format('woff2');
font-weight: 500;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Satoshi';
src: url('/fonts/Satoshi-Bold.woff2') format('woff2');
font-weight: 700;
font-style: normal;
font-display: swap;
}
Global typography::root {
--font-sans: 'Satoshi', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}
body {
font-family: var(--font-sans);
color: #050816;
background-color: #f5f5f7;
}
Color System
Extracted-style palette inspired by Sarvam::root {
/* Neutral surface */
--bg-body: #f5f5f7;
--bg-surface: #ffffff;
--bg-subtle: #f0f2f5;
/* Primary */
--primary: #111827; /* deep ink */
--primary-soft: #1f2937;
--primary-contrast: #f97316; /* warm accent (CTA) */
/* Borders & lines */
--border-subtle: #e5e7eb;
--border-strong: #d1d5db;
/* Text */
--text-primary: #020617;
--text-muted: #6b7280;
--text-soft: #9ca3af;
/* Badges */
--badge-bg: #eef2ff;
--badge-text: #4338ca;
/* Shadow */
--shadow-soft: 0 18px 45px rgba(15, 23, 42, 0.08);
}
Layout & Composition
-
Max-width container ~1120–1200px, centered with generous top/bottom padding.
-
Hero area:
▫ Left: text stack (badge, eyebrow, h1, subcopy, CTA + secondary link).
▫ Right: could be interactive demo or cards (like “Population-scale Applications / State-of-the-art Models / Infrastructure…”).
- Sections have clear titles + descriptions + content grids (3 columns on desktop, stack on mobile).
.page {
min-height: 100vh;
background: radial-gradient(circle at top left, #e5f0ff 0, #f5f5f7 40%, #f5f5f7 100%);
}
.container {
max-width: 1120px;
margin: 0 auto;
padding: 3rem 1.5rem 5rem;
}
Motion Guidelines
Use Framer Motion:
▫ Fade in + slight upward movement on load.
▫ Stagger text elements (badge → h1 → subcopy → buttons).
▫ Either static grid with subtle hover scale, or slow continuous marquee.
▫ Hover: scale to 1.02, elevate shadow, subtle border-color shift.
▫ On scroll: fade in + translateY 16px.
Example Framer Motion pattern:const fadeUp = {
hidden: { opacity: 0, y: 24 },
show: (i = 0) => ({
opacity: 1,
y: 0,
transition: {
delay: 0.15 + i * 0.07,
duration: 0.5,
ease: [0.21, 0.47, 0.32, 0.98],
},
}),
};
Use this for hero lines and cards via custom={index}.
Components
Build everything as composable React components:
-
HeroSection: hero copy + CTA + supporting “pillar cards”
-
LogoStrip: partner logos grid/marquee
-
PlatformPillarsSection: three cards with icon/eyebrow/title/body
-
CapabilitiesTabs: tabs for Conversational Agents / TTS / STT / Vision / Dubbing with a simple demo area
-
DeploymentOptions: Sarvam Cloud / Private Cloud / On-Prem cards
-
TestimonialSection: quote + person + logo
-
UpdatesSection: research & updates cards
-
Footer: structured nav + metadata
Each component:
-
Accepts data via props (titles, copy, links).
-
Encodes Sarvam-style layout and motion internally.
Constraints
-
Avoid overusing gradients; when used, they’re soft and minimal.
-
Don’t use generic purple/blue “AI gradient blobs”.
-
Don’t over-animate; keep motion focused and meaningful.
-
Respect accessibility: color contrast, focus styles on buttons/links, semantic HTML.
Developer Workflow
-
Define global tokens (colors, typography, spacing) via CSS variables.
-
Implement <HeroSection /> with Framer Motion animations and test on multiple viewport sizes.
-
Add <LogoStrip /> and <PlatformPillarsSection /> to match the “Powering India’s AI-first future” and “Full-stack platform” narrative.
-
Layer in remaining sections: capabilities tabs, deployment options, testimonial, updates.
-
Iterate on microcopy and motion timing to ensure the page feels confident, not hyperactive.
You can drop this in SKILL.md for your /how-to-web flow.
2. Implementing the hero / top section in React + CSS + Framer Motion
Let’s implement one specific element inspired by the current page:
-
The hero headline: “India’s Sovereign AI Platform / AI for all from India”
-
Subtext blocks like “Built on sovereign compute…”
-
Primary CTA: “Experience Sarvam”
-
Partner logos section (“India builds with Sarvam”)
-
“Powering India’s AI-first future” strip with 3 pillar cards.
2.1. File structure
Something like:
-
src/components/Hero.tsx
-
src/components/LogoStrip.tsx
-
src/components/PlatformPillars.tsx
-
src/styles/global.css (or Hero.css etc.)
2.2. Global CSS: fonts + colors
Assuming you’ve downloaded fonts like Satoshi and placed in public/fonts.
/* src/styles/global.css */
/* Fonts */
@font-face {
font-family: 'Satoshi';
src: url('/fonts/Satoshi-Regular.woff2') format('woff2');
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Satoshi';
src: url('/fonts/Satoshi-Medium.woff2') format('woff2');
font-weight: 500;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Satoshi';
src: url('/fonts/Satoshi-SemiBold.woff2') format('woff2');
font-weight: 600;
font-style: normal;
font-display: swap;
}
:root {
--bg-body: #f5f5f7;
--bg-surface: #ffffff;
--bg-subtle: #f0f2f5;
--primary: #111827;
--primary-soft: #1f2937;
--primary-contrast: #f97316;
--border-subtle: #e5e7eb;
--border-strong: #d1d5db;
--text-primary: #020617;
--text-muted: #6b7280;
--text-soft: #9ca3af;
--badge-bg: #eef2ff;
--badge-text: #4338ca;
--shadow-soft: 0 18px 45px rgba(15, 23, 42, 0.08);
}
*,
*::before,
*::after {
box-sizing: border-box;
}
body {
margin: 0;
font-family: 'Satoshi', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
background: radial-gradient(circle at top left, #e5f0ff 0, #f5f5f7 40%, #f5f5f7 100%);
color: var(--text-primary);
}
.page {
min-height: 100vh;
}
.container {
max-width: 1120px;
margin: 0 auto;
padding: 3rem 1.5rem 5rem;
}
2.3. Hero component (React + Framer Motion)// src/components/Hero.tsx
import { motion } from 'framer-motion';
import './Hero.css';
const fadeUp = {
hidden: { opacity: 0, y: 24 },
show: (i: number = 0) => ({
opacity: 1,
y: 0,
transition: {
delay: 0.15 + i * 0.08,
duration: 0.55,
ease: [0.21, 0.47, 0.32, 0.98],
},
}),
};
export function Hero() {
return (
<section className="hero">
<div className="hero-inner">
<div className="hero-text">
<motion.div
className="hero-badge"
initial="hidden"
animate="show"
variants={fadeUp}
custom={0}
>
India's Sovereign AI Platform
</motion.div>
<motion.h1
className="hero-title"
initial="hidden"
animate="show"
variants={fadeUp}
custom={1}
>
AI for all <br /> from India
</motion.h1>
<motion.p
className="hero-subtitle"
initial="hidden"
animate="show"
variants={fadeUp}
custom={2}
>
Built on sovereign compute. Powered by frontier-class models. Delivering population-scale
impact.
</motion.p>
<motion.div
className="hero-cta-row"
initial="hidden"
animate="show"
variants={fadeUp}
custom={3}
>
<button className="hero-primary-btn">
Experience Sarvam
</button>
<button className="hero-secondary-btn">
Talk to Sales
</button>
</motion.div>
<motion.p
className="hero-footnote"
initial="hidden"
animate="show"
variants={fadeUp}
custom={4}
>
Built for Enterprise · Government · Developers
</motion.p>
</div>
<motion.div
className="hero-side"
initial={{ opacity: 0, y: 32 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.4, duration: 0.7, ease: [0.21, 0.47, 0.32, 0.98] }}
>
<div className="hero-pillars">
<div className="pillar-card">
<div className="pillar-eyebrow">Sovereign by design</div>
<h3>Full control, entirely in India</h3>
<p>
Build, deploy, and run AI with full sovereignty over data, models, and infrastructure.
</p>
</div>
<div className="pillar-card">
<div className="pillar-eyebrow">State-of-the-art models</div>
<h3>Built for India's languages</h3>
<p>
Industry-leading performance tuned for Indian languages, culture, and context.
</p>
</div>
<div className="pillar-card">
<div className="pillar-eyebrow">Human at the core</div>
<h3>Engineers in the loop</h3>
<p>
Forward-deployed teams work with you to ship production-ready AI agents.
</p>
</div>
</div>
</motion.div>
</div>
</section>
);
}
2.4. Hero-specific CSS/* src/components/Hero.css */
.hero {
padding-top: 5rem;
padding-bottom: 3rem;
}
.hero-inner {
max-width: 1120px;
margin: 0 auto;
padding: 0 1.5rem;
display: grid;
grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr);
gap: 3.5rem;
align-items: center;
}
.hero-text {
max-width: 560px;
}
.hero-badge {
display: inline-flex;
align-items: center;
padding: 0.35rem 0.8rem;
border-radius: 999px;
background-color: var(--badge-bg);
color: var(--badge-text);
font-size: 0.75rem;
letter-spacing: 0.06em;
text-transform: uppercase;
font-weight: 600;
margin-bottom: 1rem;
}
.hero-title {
font-size: clamp(2.6rem, 3.5vw, 3.4rem);
line-height: 1.05;
letter-spacing: -0.04em;
margin: 0 0 1.2rem;
color: var(--text-primary);
}
.hero-subtitle {
margin: 0 0 1.8rem;
font-size: 1rem;
line-height: 1.6;
color: var(--text-muted);
max-width: 440px;
}
.hero-cta-row {
display: flex;
align-items: center;
gap: 0.9rem;
margin-bottom: 1.3rem;
}
.hero-primary-btn {
border: none;
cursor: pointer;
padding: 0.75rem 1.4rem;
border-radius: 999px;
background-color: var(--primary-contrast);
color: #111827;
font-weight: 600;
font-size: 0.95rem;
box-shadow: 0 16px 30px rgba(249, 115, 22, 0.3);
transition: transform 150ms ease, box-shadow 150ms ease, background-color 150ms ease;
}
.hero-primary-btn:hover {
transform: translateY(-1px);
box-shadow: 0 20px 40px rgba(249, 115, 22, 0.35);
background-color: #ea580c;
}
.hero-secondary-btn {
border-radius: 999px;
border: 1px solid var(--border-subtle);
background-color: rgba(255, 255, 255, 0.85);
color: var(--text-primary);
padding: 0.75rem 1.2rem;
cursor: pointer;
font-size: 0.9rem;
font-weight: 500;
display: inline-flex;
align-items: center;
gap: 0.4rem;
backdrop-filter: blur(8px);
transition: background-color 150ms ease, border-color 150ms ease, transform 150ms ease;
}
.hero-secondary-btn:hover {
background-color: #ffffff;
border-color: var(--border-strong);
transform: translateY(-1px);
}
.hero-footnote {
margin: 0;
font-size: 0.85rem;
color: var(--text-soft);
}
/* Right side pillars */
.hero-side {
display: flex;
justify-content: flex-end;
}
.hero-pillars {
background-color: var(--bg-surface);
border-radius: 1.5rem;
padding: 1.8rem 1.6rem;
border: 1px solid rgba(209, 213, 219, 0.8);
box-shadow: var(--shadow-soft);
display: grid;
grid-template-columns: 1fr;
gap: 1.3rem;
}
.pillar-card {
padding: 0.9rem 0.9rem 1rem;
border-radius: 1rem;
border: 1px solid rgba(229, 231, 235, 0.9);
background: linear-gradient(
135deg,
rgba(248, 250, 252, 0.95),
rgba(241, 245, 249, 0.9)
);
transition: border-color 150ms ease, transform 150ms ease, box-shadow 150ms ease;
}
.pillar-card:hover {
transform: translateY(-3px);
box-shadow: 0 16px 40px rgba(15, 23, 42, 0.12);
border-color: rgba(148, 163, 184, 0.9);
}
.pillar-eyebrow {
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--text-soft);
margin-bottom: 0.3rem;
}
.pillar-card h3 {
font-size: 0.98rem;
margin: 0 0 0.35rem;
color: var(--text-primary);
}
.pillar-card p {
margin: 0;
font-size: 0.86rem;
line-height: 1.5;
color: var(--text-muted);
}
@media (min-width: 960px) {
.hero-pillars {
grid-template-columns: 1fr;
}
}
@media (max-width: 960px) {
.hero-inner {
grid-template-columns: minmax(0, 1fr);
gap: 2.8rem;
}
.hero-side {
justify-content: flex-start;
}
}
@media (max-width: 640px) {
.hero {
padding-top: 3.5rem;
}
.hero-cta-row {
flex-direction: column;
align-items: flex-start;
}
.hero-pillars {
padding: 1.4rem 1.2rem;
}
}
2.5. Partner logo strip
Sarvam has an “India builds with Sarvam” logo grid. You can implement a reusable version:// src/components/LogoStrip.tsx
import { motion } from 'framer-motion';
import './LogoStrip.css';
const logos = [
'Partner 1',
'Partner 2',
'Partner 3',
'Partner 4',
'Partner 5',
'Partner 6',
'Partner 7',
'Partner 8',
];
export function LogoStrip() {
return (
<section className="logo-strip">
<div className="logo-strip-inner">
<div className="logo-strip-heading">
<span className="logo-strip-eyebrow">India builds with Sarvam</span>
</div>
<div className="logo-grid">
{logos.map((name, idx) => (
<motion.div
key={name}
className="logo-pill"
initial={{ opacity: 0, y: 10 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-40px' }}
transition={{ delay: idx * 0.03, duration: 0.4 }}
>
<span>{name}</span>
</motion.div>
))}
</div>
</div>
</section>
);
}
/* src/components/LogoStrip.css */
.logo-strip {
padding: 1.8rem 0 2.6rem;
}
.logo-strip-inner {
max-width: 1120px;
margin: 0 auto;
padding: 0 1.5rem;
}
.logo-strip-heading {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1.4rem;
}
.logo-strip-eyebrow {
font-size: 0.8rem;
text-transform: uppercase;
letter-spacing: 0.12em;
color: var(--text-soft);
font-weight: 600;
}
.logo-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
gap: 0.9rem;
}
.logo-pill {
border-radius: 999px;
padding: 0.7rem 1.2rem;
border: 1px solid var(--border-subtle);
background: rgba(255, 255, 255, 0.88);
backdrop-filter: blur(8px);
display: flex;
align-items: center;
justify-content: center;
color: var(--text-muted);
font-size: 0.8rem;
font-weight: 500;
transition: border-color 150ms ease, transform 150ms ease, box-shadow 150ms ease;
}
.logo-pill:hover {
border-color: var(--border-strong);
transform: translateY(-2px);
box-shadow: 0 12px 30px rgba(15, 23, 42, 0.08);
}
2.6. Wiring it up in App.tsx// src/App.tsx
import './styles/global.css';
import { Hero } from './components/Hero';
import { LogoStrip } from './components/LogoStrip';
function App() {
return (
<div className="page">
<Hero />
<LogoStrip />
{/* next sections: PlatformPillars, Capabilities, etc. */}
</div>
);
}
export default App;
Step-by-step reasoning for implementation
- Set up React + Framer Motion
▫ Create a Vite React project.
▫ npm install framer-motion.
▫ Add global.css with fonts and color tokens. Import it in main.tsx/App.tsx.
- Define typography + color tokens first
▫ This locks in the vibe (like Sarvam) before layout.
▫ Use CSS variables and @font-face for your chosen display/body fonts.
- Build the hero layout in raw HTML/JSX
▫ Grid with two columns: text + side card.
▫ Use proper semantic tags (section, h1, p, button).
▫ Keep copy short and stacked like on sarvam.ai.
- Style hero with CSS
▫ Match spacing: big top padding, constrained text width.
▫ Badge style for “India’s Sovereign AI Platform”.
▫ High-contrast headline, muted subcopy.
- Add Framer Motion for entrance animations
▫ Create a shared fadeUp variant and assign custom={index} to elements.
▫ Animate hero side card separately so it feels like it slides in as one block.
- Implement partner logo strip
▫ Create a simple grid with pill-shaped tiles.
▫ Use Framer Motion whileInView for subtle entrance when scrolled into view.
- Refine micro-interactions
▫ Hover on buttons and pillar cards: tiny translateY changes, box-shadow bump.
▫ Keep motion durations between 0.3–0.6s; easing cubic-bezier to feel smooth.
Fonts and color recap
Fonts you may want to download (or equivalents):
-
Satoshi (or General Sans, Metropolis) for headings and body.
-
Alternative system-safe stack: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif.
Key CSS color variables (you can tweak to taste):--bg-body: #f5f5f7;
--bg-surface: #ffffff;
--bg-subtle: #f0f2f5;
--primary: #111827;
--primary-soft: #1f2937;
--primary-contrast: #f97316;
--border-subtle: #e5e7eb;
--border-strong: #d1d5db;
--text-primary: #020617;
--text-muted: #6b7280;
--text-soft: #9ca3af;
--badge-bg: #eef2ff;
--badge-text: #4338ca;
--shadow-soft: 0 18px 45px rgba(15, 23, 42, 0.08);
If you want, we can next drill down into another specific element from the page (e.g., the “See it in action” tabbed section) and build that as a React + Framer Motion component set.