Homepage Hero Redesign Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Replace the template-style hero section with honest messaging and a skeleton UI product showcase to drive registrations from developers, makers, students, and educators.
Architecture: Two-column hero layout (kept). Left: simplified headline, subtitle, CTA. Right: new DashboardSkeleton React component rendering a stylized skeleton of the Zafron device dashboard on a gradient backdrop. Minor cleanup to the features section (headline + dead links).
Tech Stack: React, CSS Modules, Docusaurus 3.7, lucide-react icons
Spec: docs/superpowers/specs/2026-03-21-homepage-hero-redesign-design.md
File Structure
| File | Role |
|---|---|
src/components/Homepage/DashboardSkeleton.js | New. Skeleton UI component — renders a stylized, static illustration of the Zafron device overview dashboard. |
src/components/Homepage/DashboardSkeleton.module.css | New. All styles for the skeleton UI: gradient backdrop, skeleton blocks, chart shapes, responsive scaling. |
src/components/Homepage/HeroSection.js | Modify. Strip old content (badge, floating dots, stats, DashboardPreview), add new headline/subtitle/CTA/trust note, import DashboardSkeleton. |
src/components/Homepage/HeroSection.module.css | Modify. Remove floating dot, stats, badge styles. Add trust note style. Simplify background selectors. |
src/components/Homepage/homepage-globals.css | Modify. Remove unused keyframe animations and utility classes. |
src/components/Homepage/FeaturesSection.js | Modify. Change headline, remove card footers, remove ChevronRight import. |
src/components/Homepage/FeaturesSection.module.css | Modify. Remove footer/learnMore/chevron styles. |
src/components/Homepage/DashboardPreview.js | Delete. Replaced by DashboardSkeleton. |
src/components/Homepage/DashboardPreview.module.css | Delete. |
Task 1: Create DashboardSkeleton Component
Files:
-
Create:
src/components/Homepage/DashboardSkeleton.js -
Create:
src/components/Homepage/DashboardSkeleton.module.css -
Step 1: Create
DashboardSkeleton.module.css
/* Gradient backdrop wrapper */
.wrapper {
position: relative;
padding: 1.5rem;
border-radius: 1.5rem;
background: linear-gradient(135deg, rgba(99, 152, 255, 0.15), rgba(147, 51, 234, 0.12), rgba(6, 182, 212, 0.1));
}
/* Main skeleton container */
.skeleton {
background: #ffffff;
border-radius: 1rem;
overflow: hidden;
box-shadow:
0 20px 40px -12px rgba(0, 0, 0, 0.15),
0 0 0 1px rgba(255, 255, 255, 0.1);
}
/* --- Nav Bar --- */
.navBar {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.75rem 1rem;
background: #f8fafc;
border-bottom: 1px solid #e2e8f0;
}
.navLogo {
width: 1.5rem;
height: 1.5rem;
border-radius: 0.375rem;
background: linear-gradient(135deg, #3b82f6, #2563eb);
flex-shrink: 0;
}
.navItems {
display: flex;
gap: 0.5rem;
}
.navItem {
height: 0.5rem;
border-radius: 0.25rem;
background: #cbd5e1;
}
/* --- Device Header --- */
.deviceHeader {
display: flex;
align-items: center;
justify-content: space-between;
padding: 1rem 1.25rem 0.5rem;
}
.deviceInfo {
display: flex;
align-items: center;
gap: 0.625rem;
}
.deviceIcon {
width: 2rem;
height: 2rem;
border-radius: 0.5rem;
background: #e2e8f0;
}
.deviceText {
display: flex;
flex-direction: column;
gap: 0.25rem;
}
.deviceName {
height: 0.625rem;
width: 7rem;
border-radius: 0.25rem;
background: #334155;
}
.deviceId {
height: 0.5rem;
width: 5rem;
border-radius: 0.25rem;
background: #94a3b8;
}
.onlineBadge {
display: flex;
align-items: center;
gap: 0.375rem;
padding: 0.25rem 0.625rem;
border-radius: 9999px;
background: #dcfce7;
border: 1px solid #bbf7d0;
}
.onlineDot {
width: 0.375rem;
height: 0.375rem;
border-radius: 50%;
background: #22c55e;
}
.onlineText {
height: 0.5rem;
width: 2rem;
border-radius: 0.25rem;
background: #16a34a;
}
/* --- Tab Bar --- */
.tabBar {
display: flex;
gap: 1rem;
padding: 0.5rem 1.25rem;
border-bottom: 1px solid #e2e8f0;
}
.tab {
height: 0.5rem;
border-radius: 0.25rem;
background: #cbd5e1;
}
.tabActive {
background: #3b82f6;
}
/* --- Chart Cards Grid --- */
.chartsGrid {
display: grid;
grid-template-columns: 5fr 5fr 3fr;
gap: 0.75rem;
padding: 1rem 1.25rem;
}
.card {
background: #f8fafc;
border: 1px solid #e2e8f0;
border-radius: 0.75rem;
padding: 0.75rem;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.cardLabel {
height: 0.5rem;
width: 4rem;
border-radius: 0.25rem;
background: #94a3b8;
}
/* Area chart shape (temperature) */
.areaChart {
position: relative;
height: 3.5rem;
overflow: hidden;
border-radius: 0.375rem;
}
.areaChartSvg {
width: 100%;
height: 100%;
}
/* Bar chart shape (SNR / humidity) */
.barChart {
display: flex;
align-items: flex-end;
gap: 2px;
height: 3.5rem;
}
.bar {
flex: 1;
border-radius: 1px 1px 0 0;
background: #93c5fd;
}
/* Battery ring */
.batteryCard {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 0.5rem;
}
.batteryRing {
width: 3.5rem;
height: 3.5rem;
border-radius: 50%;
border: 3px solid #e2e8f0;
border-top-color: #3b82f6;
display: flex;
align-items: center;
justify-content: center;
}
.batteryValue {
height: 0.625rem;
width: 1.5rem;
border-radius: 0.25rem;
background: #334155;
}
.batteryLabel {
height: 0.375rem;
width: 2rem;
border-radius: 0.25rem;
background: #94a3b8;
}
/* --- Second Row --- */
.secondRow {
display: grid;
grid-template-columns: 5fr 4fr;
gap: 0.75rem;
padding: 0 1.25rem 1rem;
}
.metricCard {
background: #f8fafc;
border: 1px solid #e2e8f0;
border-radius: 0.75rem;
padding: 0.75rem;
display: flex;
align-items: center;
gap: 0.625rem;
}
.metricIcon {
width: 1.5rem;
height: 1.5rem;
border-radius: 0.375rem;
background: #dbeafe;
flex-shrink: 0;
}
.metricValue {
height: 0.75rem;
width: 2.5rem;
border-radius: 0.25rem;
background: #334155;
}
.metricLabel {
height: 0.375rem;
width: 1.5rem;
border-radius: 0.25rem;
background: #94a3b8;
}
.metricText {
display: flex;
flex-direction: column;
gap: 0.25rem;
}
.smallMetrics {
display: flex;
flex-direction: column;
gap: 0.75rem;
}
/* --- Responsive --- */
@media (max-width: 1024px) {
.wrapper {
max-width: 28rem;
margin: 0 auto;
}
}
@media (max-width: 480px) {
.wrapper {
padding: 0.75rem;
}
.chartsGrid {
grid-template-columns: 1fr 1fr;
padding: 0.75rem;
}
.batteryCard {
display: none;
}
.secondRow {
grid-template-columns: 1fr;
padding: 0 0.75rem 0.75rem;
}
}
- Step 2: Create
DashboardSkeleton.js
import React from 'react';
import styles from './DashboardSkeleton.module.css';
export default function DashboardSkeleton() {
// Bar heights for SNR-style chart
const snrBars = [65, 45, 78, 52, 89, 34, 67, 92, 43, 76, 88, 55, 70, 48, 82];
// Bar heights for humidity-style chart
const humidityBars = [72, 68, 75, 70, 74, 69, 73, 71, 76, 72, 74, 70, 73, 68, 75, 71, 74, 69];
return (
<div className={styles.wrapper}>
<div className={styles.skeleton}>
{/* Nav Bar */}
<div className={styles.navBar}>
<div className={styles.navLogo} />
<div className={styles.navItems}>
<div className={styles.navItem} style={{ width: '2.5rem' }} />
<div className={styles.navItem} style={{ width: '2rem' }} />
<div className={styles.navItem} style={{ width: '2.5rem' }} />
<div className={styles.navItem} style={{ width: '3rem' }} />
</div>
</div>
{/* Device Header */}
<div className={styles.deviceHeader}>
<div className={styles.deviceInfo}>
<div className={styles.deviceIcon} />
<div className={styles.deviceText}>
<div className={styles.deviceName} />
<div className={styles.deviceId} />
</div>
</div>
<div className={styles.onlineBadge}>
<div className={styles.onlineDot} />
<div className={styles.onlineText} />
</div>
</div>
{/* Tab Bar */}
<div className={styles.tabBar}>
<div className={`${styles.tab} ${styles.tabActive}`} style={{ width: '3rem' }} />
<div className={styles.tab} style={{ width: '1.5rem' }} />
<div className={styles.tab} style={{ width: '2rem' }} />
<div className={styles.tab} style={{ width: '2.5rem' }} />
<div className={styles.tab} style={{ width: '2rem' }} />
</div>
{/* First Row: Temperature + SNR + Battery */}
<div className={styles.chartsGrid}>
{/* Temperature - area chart */}
<div className={styles.card}>
<div className={styles.cardLabel} />
<div className={styles.areaChart}>
<svg className={styles.areaChartSvg} viewBox="0 0 200 60" preserveAspectRatio="none">
<path
d="M0,45 C20,42 40,38 60,30 C80,22 100,18 120,25 C140,32 160,28 180,20 L200,22 L200,60 L0,60 Z"
fill="#dbeafe"
/>
<path
d="M0,45 C20,42 40,38 60,30 C80,22 100,18 120,25 C140,32 160,28 180,20 L200,22"
fill="none"
stroke="#93c5fd"
strokeWidth="1.5"
/>
</svg>
</div>
</div>
{/* SNR - bar chart */}
<div className={styles.card}>
<div className={styles.cardLabel} />
<div className={styles.barChart}>
{snrBars.map((h, i) => (
<div key={i} className={styles.bar} style={{ height: `${h}%` }} />
))}
</div>
</div>
{/* Battery - ring */}
<div className={`${styles.card} ${styles.batteryCard}`}>
<div className={styles.cardLabel} />
<div className={styles.batteryRing}>
<div className={styles.batteryValue} />
</div>
<div className={styles.batteryLabel} />
</div>
</div>
{/* Second Row: Humidity chart + small metric cards */}
<div className={styles.secondRow}>
{/* Humidity - bar chart */}
<div className={styles.card}>
<div className={styles.cardLabel} />
<div className={styles.barChart}>
{humidityBars.map((h, i) => (
<div key={i} className={styles.bar} style={{ height: `${h}%` }} />
))}
</div>
</div>
{/* RSSI + Voltage stacked */}
<div className={styles.smallMetrics}>
<div className={styles.metricCard}>
<div className={styles.metricIcon} />
<div className={styles.metricText}>
<div className={styles.metricValue} />
<div className={styles.metricLabel} />
</div>
</div>
<div className={styles.metricCard}>
<div className={styles.metricIcon} />
<div className={styles.metricText}>
<div className={styles.metricValue} />
<div className={styles.metricLabel} />
</div>
</div>
</div>
</div>
</div>
</div>
);
}
- Step 3: Verify the component renders without errors
Run: cd /Users/asanchezdelc/Ticadia/zafron/zafron-docs && npm run build
This won't show DashboardSkeleton yet (not imported anywhere), but confirms no syntax errors in the new files. Expected: Build succeeds.
- Step 4: Commit
git add src/components/Homepage/DashboardSkeleton.js src/components/Homepage/DashboardSkeleton.module.css
git commit -m "feat: add DashboardSkeleton component
Skeleton UI illustration of the Zafron device overview dashboard
for use in the hero section. Renders stylized chart cards, metric
blocks, and nav bar on a gradient backdrop."
Task 2: Rewrite HeroSection (content + DashboardSkeleton swap)
Files:
-
Modify:
src/components/Homepage/HeroSection.js(full rewrite) -
Modify:
src/components/Homepage/HeroSection.module.css(major cleanup) -
Delete:
src/components/Homepage/DashboardPreview.js -
Delete:
src/components/Homepage/DashboardPreview.module.css -
Step 1: Rewrite
HeroSection.js
Replace the entire file contents with:
import React from 'react';
import { ArrowRight } from 'lucide-react';
import DashboardSkeleton from './DashboardSkeleton';
import styles from './HeroSection.module.css';
export default function HeroSection() {
return (
<section className={styles.hero}>
{/* Background */}
<div className={styles.background}>
<div className={styles.gradientOverlay}></div>
</div>
<div className={styles.container}>
<div className={styles.grid}>
{/* Left Content */}
<div className={styles.leftContent}>
<h1 className={styles.title}>
Connect, Monitor, and Visualize Your IoT Devices
</h1>
<p className={styles.description}>
A free IoT platform for developers, makers, and educators.
Connect your devices over MQTT or LoRaWAN and start
visualizing data in minutes.
</p>
<div className={styles.ctaSection}>
<a
href="https://app.zafron.dev?utm_source=homepage&utm_medium=cta"
className={styles.primaryButton}
>
Get Started Free
<ArrowRight className={styles.buttonIcon} />
</a>
<p className={styles.trustNote}>No credit card required</p>
</div>
</div>
{/* Right Content */}
<div className={styles.rightContent}>
<DashboardSkeleton />
</div>
</div>
</div>
{/* Bottom Wave Effect */}
<div className={styles.bottomWave}></div>
</section>
);
}
- Step 2: Rewrite
HeroSection.module.css
Replace the entire file contents with:
.hero {
position: relative;
min-height: 70vh;
background: linear-gradient(135deg, var(--hp-gradient-start), var(--hp-gradient-middle), var(--hp-gradient-end));
color: var(--hp-text-white);
overflow: hidden;
}
/* Background */
.background {
position: absolute;
inset: 0;
}
.gradientOverlay {
position: absolute;
inset: 0;
background: radial-gradient(circle at 30% 20%, rgba(56, 189, 248, 0.1), transparent 50%);
}
/* Main Container */
.container {
position: relative;
max-width: 1200px;
margin: 0 auto;
padding: 4rem 1.5rem;
z-index: 10;
}
.grid {
display: grid;
grid-template-columns: 1fr;
gap: 3rem;
align-items: center;
min-height: 50vh;
}
@media (min-width: 1024px) {
.grid {
grid-template-columns: 1fr 1fr;
}
}
/* Left Content */
.leftContent {
display: flex;
flex-direction: column;
gap: 1.5rem;
}
.title {
font-size: 3.25rem;
font-weight: bold;
line-height: 1.1;
letter-spacing: -0.025em;
margin: 0;
color: var(--hp-text-white);
}
.description {
font-size: 1.25rem;
color: var(--hp-text-slate-300);
line-height: 1.6;
max-width: 32rem;
margin: 0;
}
/* CTA Section */
.ctaSection {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.primaryButton {
display: inline-flex;
align-items: center;
justify-content: center;
align-self: flex-start;
background: linear-gradient(135deg, var(--hp-bg-blue-600), var(--hp-bg-blue-700));
color: var(--hp-text-white);
padding: 1rem 2.5rem;
font-size: 1.125rem;
font-weight: 600;
border-radius: var(--hp-border-radius);
text-decoration: none;
gap: 0.5rem;
box-shadow: 0 25px 50px -12px rgba(59, 130, 246, 0.25);
transition: all 0.3s ease;
}
.primaryButton:hover {
background: linear-gradient(135deg, #3b82f6, var(--hp-bg-blue-600));
box-shadow: 0 25px 50px -12px rgba(59, 130, 246, 0.4);
transform: translateY(-2px);
color: var(--hp-text-white);
text-decoration: none;
}
.buttonIcon {
width: 1.25rem;
height: 1.25rem;
transition: transform 0.3s ease;
}
.primaryButton:hover .buttonIcon {
transform: translateX(0.25rem);
}
.trustNote {
font-size: 0.875rem;
color: var(--hp-text-slate-400);
margin: 0;
}
/* Right Content */
.rightContent {
position: relative;
display: flex;
justify-content: center;
align-items: center;
}
/* Bottom Wave Effect */
.bottomWave {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 1px;
background: linear-gradient(90deg, transparent, var(--hp-bg-blue-600), transparent);
}
/* Responsive */
@media (max-width: 1024px) {
.title {
font-size: 2.75rem;
}
.description {
font-size: 1.125rem;
}
}
@media (max-width: 768px) {
.container {
padding: 3rem 1rem;
}
.title {
font-size: 2.25rem;
}
.description {
font-size: 1rem;
}
}
@media (max-width: 480px) {
.title {
font-size: 2rem;
}
.primaryButton {
padding: 0.75rem 2rem;
font-size: 1rem;
}
}
- Step 3: Delete old DashboardPreview files
rm src/components/Homepage/DashboardPreview.js src/components/Homepage/DashboardPreview.module.css
- Step 4: Verify build succeeds
Run: cd /Users/asanchezdelc/Ticadia/zafron/zafron-docs && npm run build
Expected: Build succeeds with no errors.
- Step 5: Commit
git add -A src/components/Homepage/HeroSection.js src/components/Homepage/HeroSection.module.css
git add src/components/Homepage/DashboardPreview.js src/components/Homepage/DashboardPreview.module.css
git commit -m "feat: rewrite hero section with clean messaging and skeleton UI
Replace template-style hero (fake stats, animated dots, simulated
dashboard) with honest headline, subtitle, CTA, and DashboardSkeleton
component. Delete DashboardPreview."
Task 3: Clean Up homepage-globals.css
Files:
-
Modify:
src/components/Homepage/homepage-globals.css -
Step 1: Remove unused animations
Remove these keyframe blocks and utility class from homepage-globals.css:
@keyframes hp-gradient-x(lines 41-50)@keyframes hp-float-around(lines 62-89)@keyframes hp-drift-wide(lines 91-118)@keyframes hp-wander(lines 120-147)@keyframes hp-circle-drift(lines 149-176)@keyframes hp-zigzag(lines 178-205).homepage-container .hp-animate-gradient-x(lines 208-211).homepage-container .hp-animate-float(lines 213-215)
Keep only:
-
@keyframes hp-float(lines 52-54) -
@keyframes hp-pulse-soft(lines 56-59) -
.homepage-container .hp-animate-pulse-soft(lines 217-219) -
Step 2: Verify build succeeds
Run: cd /Users/asanchezdelc/Ticadia/zafron/zafron-docs && npm run build
Expected: Build succeeds.
- Step 3: Commit
git add src/components/Homepage/homepage-globals.css
git commit -m "chore: remove unused homepage animation keyframes"
Task 4: Update FeaturesSection
Files:
-
Modify:
src/components/Homepage/FeaturesSection.js -
Modify:
src/components/Homepage/FeaturesSection.module.css -
Step 1: Update
FeaturesSection.js
In src/components/Homepage/FeaturesSection.js:
- Remove
ChevronRightfrom the lucide-react import (line 9) - Change headline text from
"Simple and Easy"to"Everything You Need to Build IoT Projects"(line 64) - Remove the card footer JSX block (lines 100-105):
// DELETE this entire block from inside the card map:
<div className={styles.cardFooter}>
<div className={styles.learnMore}>
Learn more
<ChevronRight className={styles.chevron} />
</div>
</div>
- Step 2: Update
FeaturesSection.module.css
Remove these style blocks from src/components/Homepage/FeaturesSection.module.css:
-
.cardFooter(lines 199-201) -
.learnMore(lines 203-209) -
.card:hover .learnMore(lines 211-213) -
.chevron(lines 215-220) -
.card:hover .chevron(lines 222-224) -
Step 3: Verify build succeeds
Run: cd /Users/asanchezdelc/Ticadia/zafron/zafron-docs && npm run build
Expected: Build succeeds.
- Step 4: Commit
git add src/components/Homepage/FeaturesSection.js src/components/Homepage/FeaturesSection.module.css
git commit -m "fix: update features headline and remove dead Learn more links"
Task 5: Visual Verification
- Step 1: Start dev server and visually inspect
Run: cd /Users/asanchezdelc/Ticadia/zafron/zafron-docs && npm run start
Open http://localhost:3000 and verify:
- Hero left column: New headline, subtitle, CTA button with arrow, "No credit card required" text
- Hero right column: Skeleton dashboard on gradient backdrop — nav bar, device header with online badge, tab bar, chart cards (area chart, bar chart, battery ring), second row metrics
- No floating dots, no animated stats, no gradient text animation
- Features section: New headline "Everything You Need to Build IoT Projects"
- Feature cards: No "Learn more" footer links
- Mobile responsive: Resize browser to verify stacking works
- Step 2: Check for console errors
Open browser dev tools console. Expected: No React errors or warnings related to the homepage components.