feat: add animated components for enhanced UI/UX
- Introduced DrawLine component for drawing animations. - Added LazyRevealImage component for lazy loading images with reveal animations. - Implemented TextDiffuseReveal for animated text reveals with staggered effects. - Created TopProgressBar for a dynamic loading indicator. - Added PointCloud mock for testing animations. - Developed useReducedMotion hook to handle user preferences for reduced motion. - Centralized animation configurations in config.ts for easier management. - Defined animation variants in variants.ts for consistent animation effects. - Established AnimationProvider to supply global animation settings. - Created tests for AnimationProvider and useReducedMotion hook. - Added SVG assets for branding and UI elements. - Documented frontend review plan and hero circuit board animation plan.
This commit is contained in:
1 parent
0ab6930f92
commit
08dd046887
76 files changed
+21682
-1197
No files matched your search
+102
-180
@@ -1,8 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { motion, type Variants, useReducedMotion } from "framer-motion";
|
||||
import { Section } from "./Section";
|
||||
import { Reveal } from "./Reveal";
|
||||
import { DrawLine } from "./animations/DrawLine";
|
||||
|
||||
interface Tier {
|
||||
id: string;
|
||||
@@ -24,20 +24,20 @@ const TIERS: Tier[] = [
|
||||
id: "discovery",
|
||||
badge: "Entrada / Baixo Risco",
|
||||
level: "01",
|
||||
title: "Auditoria & Diagnóstico Técnico",
|
||||
title: "Auditoria & Diagnostico Tecnico",
|
||||
tagline: "Fotografia clara do problema antes de qualquer comprometimento.",
|
||||
description:
|
||||
"Análise profunda de arquitetura, performance ou segurança com entrega de um plano de ação executável. Ideal para quem precisa de direção antes de contratar execução.",
|
||||
"Analise profunda de arquitetura, performance ou seguranca com entrega de um plano de acao executavel. Ideal para quem precisa de direcao antes de contratar execucao.",
|
||||
deliverables: [
|
||||
"Relatório de gargalos e dívida técnica",
|
||||
"Mapeamento de riscos e dependências",
|
||||
"Sugestão de stack e cronograma de mitigação",
|
||||
"Plano de ação priorizado",
|
||||
"Relatorio de gargalos e divida tecnica",
|
||||
"Mapeamento de riscos e dependencias",
|
||||
"Sugestao de stack e cronograma de mitigacao",
|
||||
"Plano de acao priorizado",
|
||||
],
|
||||
pricing: "Valor Fixo",
|
||||
pricingNote: "Contrato único. Entrega em até 5 dias úteis.",
|
||||
pricingNote: "Contrato unico. Entrega em ate 5 dias uteis.",
|
||||
featured: false,
|
||||
ctaLabel: "Solicitar diagnóstico",
|
||||
ctaLabel: "Solicitar diagnostico",
|
||||
ctaHref: "#contact",
|
||||
},
|
||||
{
|
||||
@@ -47,12 +47,12 @@ const TIERS: Tier[] = [
|
||||
title: "Ciclos de Engenharia",
|
||||
tagline: "Capacidade produtiva garantida. Entrega a cada 2 semanas.",
|
||||
description:
|
||||
"Desenvolvimento contínuo com a previsibilidade de orçamento do escopo fechado e a agilidade de sprints. Milestones claros, sem amarras de contrato longo.",
|
||||
"Desenvolvimento continuo com a previsibilidade de orcamento do escopo fechado e a agilidade de sprints. Milestones claros, sem amarras de contrato longo.",
|
||||
deliverables: [
|
||||
"Features em produção a cada ciclo",
|
||||
"Refatoração iterativa com rastreabilidade",
|
||||
"Documentação técnica atualizada",
|
||||
"Capacidade de pivotar prioridades no início de cada sprint",
|
||||
"Features em producao a cada ciclo",
|
||||
"Refatoracao iterativa com rastreabilidade",
|
||||
"Documentacao tecnica atualizada",
|
||||
"Capacidade de pivotar prioridades no inicio de cada sprint",
|
||||
],
|
||||
pricing: "Fixo por Ciclo",
|
||||
pricingNote: "Assinatura quinzenal ou mensal. Cancele a qualquer ciclo.",
|
||||
@@ -65,226 +65,148 @@ const TIERS: Tier[] = [
|
||||
badge: "Premium / High-Ticket",
|
||||
level: "03",
|
||||
title: "Fractional Tech Lead",
|
||||
tagline: "Liderança técnica senior. Sem o custo de um CTO full-time.",
|
||||
tagline: "Lideranca tecnica senior. Sem o custo de um CTO full-time.",
|
||||
description:
|
||||
"Direção arquitetural, gestão de risco e sustentação de operações críticas. Para empresas que precisam de decisões de alto impacto com responsabilidade contínua.",
|
||||
"Direcao arquitetural, gestao de risco e sustentacao de operacoes criticas. Para empresas que precisam de decisoes de alto impacto com responsabilidade continua.",
|
||||
deliverables: [
|
||||
"SLA prioritário Zero-Risk",
|
||||
"Code review e liderança do time interno",
|
||||
"Decisões de infra: Cloud, VPS, CI/CD",
|
||||
"Consultoria estratégica recorrente",
|
||||
"SLA prioritario Zero-Risk",
|
||||
"Code review e lideranca do time interno",
|
||||
"Decisoes de infra: Cloud, VPS, CI/CD",
|
||||
"Consultoria estrategica recorrente",
|
||||
],
|
||||
pricing: "Retainer Mensal",
|
||||
pricingNote: "Renovação mensal. Vagas limitadas.",
|
||||
pricingNote: "Renovacao mensal. Vagas limitadas.",
|
||||
featured: false,
|
||||
ctaLabel: "Agendar conversa estratégica",
|
||||
ctaLabel: "Agendar conversa estrategica",
|
||||
ctaHref: "#contact",
|
||||
},
|
||||
];
|
||||
|
||||
function TierCard({ tier }: { tier: Tier }) {
|
||||
const [hovered, setHovered] = useState(false);
|
||||
function TierCard({ tier }: Readonly<{ tier: Tier }>) {
|
||||
const shouldReduceMotion = useReducedMotion();
|
||||
|
||||
return (
|
||||
<article
|
||||
onMouseEnter={() => setHovered(true)}
|
||||
onMouseLeave={() => setHovered(false)}
|
||||
onFocus={() => setHovered(true)}
|
||||
onBlur={() => setHovered(false)}
|
||||
<motion.article
|
||||
tabIndex={0}
|
||||
className={`ds-card h-full relative outline-none ${
|
||||
tier.featured ? "border-[var(--slash)] shadow-sm" : ""
|
||||
}`}
|
||||
style={{
|
||||
border: tier.featured
|
||||
? "2px solid #0A0A0A"
|
||||
: "1px solid rgba(10,10,10,0.1)",
|
||||
background: tier.featured ? "#0A0A0A" : "#fff",
|
||||
padding: "var(--sp-6)",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "var(--sp-4)",
|
||||
position: "relative",
|
||||
transition: "border-color 0.3s ease, box-shadow 0.3s ease",
|
||||
outline: "none",
|
||||
cursor: "default",
|
||||
boxShadow: hovered
|
||||
? "0 8px 32px rgba(10,10,10,0.1)"
|
||||
: "0 1px 4px rgba(10,10,10,0.04)",
|
||||
borderWidth: tier.featured ? "2px" : "1px",
|
||||
}}
|
||||
whileHover={
|
||||
shouldReduceMotion
|
||||
? undefined
|
||||
: {
|
||||
y: -4,
|
||||
boxShadow: "0 24px 50px rgba(0, 0, 0, 0.08)",
|
||||
}
|
||||
}
|
||||
whileTap={shouldReduceMotion ? undefined : { scale: 0.99 }}
|
||||
transition={{ type: "spring", stiffness: 180, damping: 18 }}
|
||||
>
|
||||
{/* Top row */}
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "flex-start",
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
fontSize: "9px",
|
||||
letterSpacing: "0.16em",
|
||||
textTransform: "uppercase",
|
||||
color: tier.featured ? "#E8FF00" : "rgba(10,10,10,0.4)",
|
||||
}}
|
||||
>
|
||||
{tier.badge}
|
||||
</span>
|
||||
<span
|
||||
style={{
|
||||
fontFamily: "var(--font-display)",
|
||||
fontSize: "11px",
|
||||
color: tier.featured
|
||||
? "rgba(255,255,255,0.15)"
|
||||
: "rgba(10,10,10,0.12)",
|
||||
fontWeight: 800,
|
||||
letterSpacing: "0.04em",
|
||||
}}
|
||||
>
|
||||
<div className="flex justify-between items-start">
|
||||
<span className="ds-kicker">{tier.badge}</span>
|
||||
<span className="font-display text-[11px] font-extrabold tracking-widest text-[var(--meta)]">
|
||||
{tier.level}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Title */}
|
||||
<div>
|
||||
<h3
|
||||
style={{
|
||||
fontFamily: "var(--font-display)",
|
||||
fontSize: "clamp(18px, 2.2vw, 26px)",
|
||||
lineHeight: 1.1,
|
||||
letterSpacing: "-0.02em",
|
||||
fontWeight: 800,
|
||||
color: tier.featured ? "#fff" : "#0A0A0A",
|
||||
}}
|
||||
>
|
||||
{tier.title}
|
||||
</h3>
|
||||
<p
|
||||
style={{
|
||||
marginTop: "var(--sp-2)",
|
||||
fontSize: "11px",
|
||||
color: tier.featured
|
||||
? "rgba(232,255,0,0.85)"
|
||||
: "rgba(10,10,10,0.5)",
|
||||
lineHeight: 1.6,
|
||||
}}
|
||||
>
|
||||
<DrawLine delay={0.04} />
|
||||
|
||||
<div className="mt-sp-4">
|
||||
<h3 className="ds-card-title">{tier.title}</h3>
|
||||
<p className="mt-sp-2 text-[11px] font-medium leading-relaxed text-[var(--meta)]">
|
||||
{tier.tagline}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Description */}
|
||||
<p
|
||||
style={{
|
||||
fontSize: "12px",
|
||||
color: tier.featured
|
||||
? "rgba(255,255,255,0.7)"
|
||||
: "rgba(10,10,10,0.65)",
|
||||
lineHeight: 1.8,
|
||||
}}
|
||||
>
|
||||
{tier.description}
|
||||
</p>
|
||||
<p className="ds-copy">{tier.description}</p>
|
||||
|
||||
{/* Deliverables */}
|
||||
<ul
|
||||
style={{
|
||||
paddingLeft: "var(--sp-5)",
|
||||
fontSize: "12px",
|
||||
color: tier.featured ? "rgba(255,255,255,0.6)" : "rgba(10,10,10,0.6)",
|
||||
lineHeight: 1.9,
|
||||
flexGrow: 1,
|
||||
}}
|
||||
>
|
||||
{tier.deliverables.map((d) => (
|
||||
<li key={d} style={{ listStyleType: "disc" }}>
|
||||
{d}
|
||||
</li>
|
||||
<ul className="ds-list">
|
||||
{tier.deliverables.map((deliverable) => (
|
||||
<li key={deliverable}>{deliverable}</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
{/* Pricing */}
|
||||
<div
|
||||
style={{
|
||||
borderTop: tier.featured
|
||||
? "1px solid rgba(255,255,255,0.1)"
|
||||
: "1px solid rgba(10,10,10,0.08)",
|
||||
paddingTop: "var(--sp-4)",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
fontFamily: "var(--font-display)",
|
||||
fontSize: "clamp(14px, 1.5vw, 18px)",
|
||||
fontWeight: 800,
|
||||
color: tier.featured ? "#E8FF00" : "#0A0A0A",
|
||||
letterSpacing: "-0.01em",
|
||||
}}
|
||||
>
|
||||
<div className="mt-sp-6 border-t border-[var(--border)] pt-sp-4">
|
||||
<div className="font-display text-[clamp(14px,1.5vw,18px)] font-extrabold tracking-tight">
|
||||
{tier.pricing}
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
fontSize: "10px",
|
||||
color: tier.featured
|
||||
? "rgba(255,255,255,0.3)"
|
||||
: "rgba(10,10,10,0.35)",
|
||||
marginTop: "4px",
|
||||
letterSpacing: "0.06em",
|
||||
}}
|
||||
>
|
||||
<div className="mt-1 text-[10px] tracking-widest text-[var(--meta)] uppercase">
|
||||
{tier.pricingNote}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* CTA — reveals on hover/focus */}
|
||||
<a
|
||||
<motion.a
|
||||
href={tier.ctaHref}
|
||||
style={{
|
||||
display: "inline-flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
padding: "12px 18px",
|
||||
fontSize: "10px",
|
||||
letterSpacing: "0.12em",
|
||||
textTransform: "uppercase",
|
||||
textDecoration: "none",
|
||||
fontWeight: 700,
|
||||
background: tier.featured ? "#E8FF00" : "#0A0A0A",
|
||||
color: tier.featured ? "#0A0A0A" : "#fff",
|
||||
transition: "opacity 0.25s ease, transform 0.25s ease",
|
||||
opacity: hovered ? 1 : 0,
|
||||
transform: hovered ? "translateY(0)" : "translateY(6px)",
|
||||
pointerEvents: hovered ? "auto" : "none",
|
||||
}}
|
||||
className={`ds-link mt-sp-6 justify-center w-full ${
|
||||
tier.featured ? "ds-link-accent" : "ds-link-muted"
|
||||
}`}
|
||||
whileHover={shouldReduceMotion ? undefined : { scale: 1.02 }}
|
||||
whileTap={shouldReduceMotion ? undefined : { scale: 0.98 }}
|
||||
transition={{ type: "spring", stiffness: 260, damping: 20 }}
|
||||
>
|
||||
{tier.ctaLabel} →
|
||||
</a>
|
||||
</article>
|
||||
{tier.ctaLabel}
|
||||
{" ->"}
|
||||
</motion.a>
|
||||
</motion.article>
|
||||
);
|
||||
}
|
||||
|
||||
export function WorkModalities() {
|
||||
const shouldReduceMotion = useReducedMotion();
|
||||
|
||||
const containerVariants: Variants = {
|
||||
hidden: {},
|
||||
visible: {
|
||||
transition: {
|
||||
delayChildren: 0.08,
|
||||
staggerChildren: 0.08,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const itemVariants: Variants = {
|
||||
hidden: shouldReduceMotion
|
||||
? { opacity: 0 }
|
||||
: { opacity: 0, y: 20, clipPath: "inset(100% 0 0 0)" },
|
||||
visible: {
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
clipPath: "inset(0% 0 0 0)",
|
||||
transition: shouldReduceMotion
|
||||
? { duration: 0.25 }
|
||||
: { duration: 0.75, ease: [0.16, 1, 0.3, 1] },
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<Section
|
||||
id="modalities"
|
||||
eyebrow="CONTRACT_PROTOCOLS.sys"
|
||||
title="Engenharia sob demanda. Previsibilidade de entrega sem overhead."
|
||||
description="Três níveis de contratação desenhados como uma esteira de valor — do diagnóstico rápido à parceria estratégica de longo prazo."
|
||||
description="Tres niveis de contratacao desenhados como uma esteira de valor, do diagnostico rapido a parceria estrategica de longo prazo."
|
||||
disableChildrenAnimation
|
||||
>
|
||||
<div
|
||||
<motion.div
|
||||
style={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: "repeat(3, minmax(0, 1fr))",
|
||||
gap: "var(--sp-6)",
|
||||
}}
|
||||
className="max-md:grid-cols-1 max-md:!flex max-md:!flex-col"
|
||||
initial="hidden"
|
||||
whileInView="visible"
|
||||
viewport={{ once: true, amount: 0.2 }}
|
||||
variants={containerVariants}
|
||||
>
|
||||
{TIERS.map((tier, index) => (
|
||||
<Reveal key={tier.id} y={40} duration={0.8} delay={0.1 * index}>
|
||||
{TIERS.map((tier) => (
|
||||
<motion.div key={tier.id} variants={itemVariants}>
|
||||
<TierCard tier={tier} />
|
||||
</Reveal>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
</Section>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user