Primer vistaso
This commit is contained in:
177
src/components/Hero/Hero.tsx
Normal file
177
src/components/Hero/Hero.tsx
Normal file
@@ -0,0 +1,177 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useRef } from 'react';
|
||||
import styles from './Hero.module.css';
|
||||
|
||||
const stats = [
|
||||
{ value: '10K+', label: 'Equipos activos' },
|
||||
{ value: '99.9%', label: 'Uptime garantizado' },
|
||||
{ value: '3x', label: 'Más productividad' },
|
||||
{ value: '140+', label: 'Países' },
|
||||
];
|
||||
|
||||
export default function Hero() {
|
||||
const heroRef = useRef<HTMLElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
entry.target.classList.add(styles.visible);
|
||||
}
|
||||
});
|
||||
},
|
||||
{ threshold: 0.1 }
|
||||
);
|
||||
|
||||
const elements = heroRef.current?.querySelectorAll(`.${styles.reveal}`);
|
||||
elements?.forEach((el) => observer.observe(el));
|
||||
|
||||
return () => observer.disconnect();
|
||||
}, []);
|
||||
|
||||
const handleScrollToContact = () => {
|
||||
document.querySelector('#contact')?.scrollIntoView({ behavior: 'smooth' });
|
||||
};
|
||||
|
||||
const handleScrollToFeatures = () => {
|
||||
document.querySelector('#features')?.scrollIntoView({ behavior: 'smooth' });
|
||||
};
|
||||
|
||||
return (
|
||||
<section className={styles.hero} id="hero" ref={heroRef} aria-label="Sección principal">
|
||||
<div className={`container ${styles.inner}`}>
|
||||
{/* Badge */}
|
||||
<div className={`${styles.reveal} ${styles.badgeWrap}`}>
|
||||
<span className="badge badge-dark">
|
||||
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" aria-hidden="true">
|
||||
<circle cx="4" cy="4" r="4" fill="#00c853" />
|
||||
</svg>
|
||||
Nuevo — Ahora con IA integrada
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Heading */}
|
||||
<h1 className={`${styles.reveal} ${styles.heading}`}>
|
||||
El flujo de trabajo
|
||||
<br />
|
||||
<em className={styles.emphasis}>que tu equipo</em>
|
||||
<br />
|
||||
siempre necesitó
|
||||
</h1>
|
||||
|
||||
{/* Subheading */}
|
||||
<p className={`${styles.reveal} ${styles.subheading}`}>
|
||||
FlowSync conecta a tu equipo, automatiza tareas repetitivas y te da
|
||||
visibilidad total de cada proyecto — todo en un solo lugar.
|
||||
</p>
|
||||
|
||||
{/* CTA Buttons */}
|
||||
<div className={`${styles.reveal} ${styles.ctas}`}>
|
||||
<button
|
||||
className="btn btn-primary btn-lg"
|
||||
id="hero-cta-primary"
|
||||
onClick={handleScrollToContact}
|
||||
>
|
||||
Empieza gratis
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">
|
||||
<path d="M3 8h10M9 4l4 4-4 4" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-secondary btn-lg"
|
||||
id="hero-cta-secondary"
|
||||
onClick={handleScrollToFeatures}
|
||||
>
|
||||
Ver características
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Trust note */}
|
||||
<p className={`${styles.reveal} ${styles.trustNote}`}>
|
||||
Sin tarjeta de crédito · 14 días gratis · Cancela cuando quieras
|
||||
</p>
|
||||
|
||||
{/* Dashboard mockup */}
|
||||
<div className={`${styles.reveal} ${styles.mockupWrap}`}>
|
||||
<div className={styles.mockup} role="img" aria-label="Vista previa del dashboard de FlowSync">
|
||||
{/* Window chrome */}
|
||||
<div className={styles.mockupBar}>
|
||||
<span className={styles.dot} style={{ background: '#ff5f57' }} />
|
||||
<span className={styles.dot} style={{ background: '#febc2e' }} />
|
||||
<span className={styles.dot} style={{ background: '#28c840' }} />
|
||||
<span className={styles.mockupUrl}>app.flowsync.io/dashboard</span>
|
||||
</div>
|
||||
|
||||
{/* Mock dashboard content */}
|
||||
<div className={styles.mockupBody}>
|
||||
{/* Sidebar */}
|
||||
<nav className={styles.mockupSidebar} aria-hidden="true">
|
||||
<div className={styles.sidebarLogo} />
|
||||
{[...Array(5)].map((_, i) => (
|
||||
<div key={i} className={`${styles.sidebarItem} ${i === 0 ? styles.sidebarActive : ''}`} />
|
||||
))}
|
||||
</nav>
|
||||
|
||||
{/* Main content */}
|
||||
<main className={styles.mockupMain} aria-hidden="true">
|
||||
{/* Header row */}
|
||||
<div className={styles.mockupHeader}>
|
||||
<div className={styles.mockupTitle} />
|
||||
<div className={styles.mockupBtn} />
|
||||
</div>
|
||||
|
||||
{/* Metric cards */}
|
||||
<div className={styles.metricsRow}>
|
||||
{['#0066ff', '#00c853', '#ff9500', '#6c5ce7'].map((color, i) => (
|
||||
<div key={i} className={styles.metricCard}>
|
||||
<div className={styles.metricIcon} style={{ background: `${color}18`, color }} />
|
||||
<div className={styles.metricLines}>
|
||||
<div className={styles.metricValue} />
|
||||
<div className={styles.metricLabel} />
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Chart area */}
|
||||
<div className={styles.chartArea}>
|
||||
<div className={styles.chartBars}>
|
||||
{[60, 80, 50, 90, 70, 85, 65, 95, 75, 88].map((h, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className={styles.bar}
|
||||
style={{ height: `${h}%`, animationDelay: `${i * 0.05}s` }}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Task list */}
|
||||
<div className={styles.taskList}>
|
||||
{[85, 60, 95, 70].map((w, i) => (
|
||||
<div key={i} className={styles.taskRow}>
|
||||
<div className={styles.taskCheck} />
|
||||
<div className={styles.taskLine} style={{ width: `${w}%` }} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Stats */}
|
||||
<div className={`${styles.reveal} ${styles.stats}`}>
|
||||
{stats.map((stat) => (
|
||||
<div key={stat.label} className={styles.statItem}>
|
||||
<span className={styles.statValue}>{stat.value}</span>
|
||||
<span className={styles.statLabel}>{stat.label}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user