Skip to main content

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

FileRole
src/components/Homepage/DashboardSkeleton.jsNew. Skeleton UI component — renders a stylized, static illustration of the Zafron device overview dashboard.
src/components/Homepage/DashboardSkeleton.module.cssNew. All styles for the skeleton UI: gradient backdrop, skeleton blocks, chart shapes, responsive scaling.
src/components/Homepage/HeroSection.jsModify. Strip old content (badge, floating dots, stats, DashboardPreview), add new headline/subtitle/CTA/trust note, import DashboardSkeleton.
src/components/Homepage/HeroSection.module.cssModify. Remove floating dot, stats, badge styles. Add trust note style. Simplify background selectors.
src/components/Homepage/homepage-globals.cssModify. Remove unused keyframe animations and utility classes.
src/components/Homepage/FeaturesSection.jsModify. Change headline, remove card footers, remove ChevronRight import.
src/components/Homepage/FeaturesSection.module.cssModify. Remove footer/learnMore/chevron styles.
src/components/Homepage/DashboardPreview.jsDelete. Replaced by DashboardSkeleton.
src/components/Homepage/DashboardPreview.module.cssDelete.

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:

  1. Remove ChevronRight from the lucide-react import (line 9)
  2. Change headline text from "Simple and Easy" to "Everything You Need to Build IoT Projects" (line 64)
  3. 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:

  1. Hero left column: New headline, subtitle, CTA button with arrow, "No credit card required" text
  2. 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
  3. No floating dots, no animated stats, no gradient text animation
  4. Features section: New headline "Everything You Need to Build IoT Projects"
  5. Feature cards: No "Learn more" footer links
  6. 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.