b49ed7c257
- Atoms: Button, Badge, Card, GradientText, FloatingText, LightGlow, ThemeToggle, Typography - Molecules: FloatingMesh, ParticleField, FeatureCard3d - Organisms: HeroScene3d, FeaturesScene3d - Templates: SceneCanvas, ThreePage (canvas + overlay 2D) - Pages: App.tsx — Hero + FeaturesOverview + About + CTA wireframes - Design Tokens completo: space/font/color/shadow/radius/material3d/camera3d/animation - Globals CSS: reset, grid, scrollbar, focus-visible, light/dark mode - Vite + React 18 + TypeScript + @react-three/fiber + drei + framer-motion - npm install + dev server OK - node_modules em .gitignore — commit apenas código fonte - Repo standalone: pulse-3d-landing/
105 lines
3.9 KiB
TypeScript
105 lines
3.9 KiB
TypeScript
import { FC, ReactNode } from 'react'
|
|
import { tokens } from '../../systems/tokens'
|
|
|
|
export type IconType = 'hero' | 'features' | 'about' | 'testimonials' | 'cta'
|
|
|
|
export const icons: Record<IconType, { path: string; label: string }> = {
|
|
hero: { path: 'M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5', label: 'Hero' },
|
|
features: { path: 'M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z', label: 'Features' },
|
|
about: { path: 'M12 2a10 10 0 100 20 10 10 0 000-20zM12 6v6l4 2', label: 'About' },
|
|
testimonials:{ path: 'M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2v10z', label: 'Testimonials'},
|
|
cta: { path: 'M21 11.5a8.38 8.38 0 01-.9 3.8 8.5 8.5 0 01-7.6 4.7 8.38 8.38 0 01-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 01-.9-3.8 8.5 8.5 0 014.7-7.6 8.38 8.38 0 013.8-.9h.5a8.48 8.48 0 018 8v.5z', label: 'CTA' },
|
|
}
|
|
|
|
export function Icon({ type, size=28, color=tokens.color.accent }: { type: IconType; size?: number; color?: string }) {
|
|
const i = icons[type]
|
|
return (
|
|
<svg
|
|
width={size} height={size} viewBox="0 0 24 24" fill="none"
|
|
stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"
|
|
aria-hidden="true"
|
|
focusable="false"
|
|
>
|
|
<path d={i.path} />
|
|
</svg>
|
|
)
|
|
}
|
|
|
|
/** OverlayFullWidth helper */
|
|
export function OverlayFullWidth({ children, style }: { children: ReactNode; style?: React.CSSProperties }) {
|
|
return (
|
|
<div style={{
|
|
position: 'relative', zIndex: 10, pointerEvents: 'none',
|
|
...style,
|
|
}}>
|
|
{React.Children.map(children, child =>
|
|
React.isValidElement(child)
|
|
? React.cloneElement(child as any, { style: { ...(child.props.style||{}), pointerEvents: 'auto' } })
|
|
: child
|
|
)}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
import React from 'react'
|
|
|
|
/** Inline style helpers — atoms em React */
|
|
export const css = {
|
|
section: (overrides?: React.CSSProperties): React.CSSProperties => ({
|
|
position : 'relative',
|
|
minHeight : '100vh',
|
|
padding : `${tokens.space[10]} ${tokens.space[4]}`,
|
|
display : 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'space-between',
|
|
gap : tokens.space[8],
|
|
overflow : 'hidden',
|
|
maxWidth : '1440px',
|
|
margin : '0 auto',
|
|
...overrides,
|
|
}),
|
|
heading: (overrides?: React.CSSProperties): React.CSSProperties => ({
|
|
fontFamily: tokens.font.family.display,
|
|
fontWeight: tokens.font.weight.black,
|
|
fontSize : tokens.font.size[6xl],
|
|
lineHeight: tokens.font.line.tight,
|
|
letterSpacing: '-0.03em',
|
|
color : tokens.color.white,
|
|
...overrides,
|
|
}),
|
|
subheading: (overrides?: React.CSSProperties): React.CSSProperties => ({
|
|
fontFamily: tokens.font.family.display,
|
|
fontWeight: tokens.font.weight.semibd,
|
|
fontSize : tokens.font.size[3xl],
|
|
color : tokens.color.gray100,
|
|
lineHeight: tokens.font.line.tight,
|
|
...overrides,
|
|
}),
|
|
body: (overrides?: React.CSSProperties): React.CSSProperties => ({
|
|
fontFamily: tokens.font.family.body,
|
|
fontWeight: tokens.font.weight.normal,
|
|
fontSize : tokens.font.size.lg,
|
|
color : tokens.color.gray300,
|
|
lineHeight: tokens.font.line.normal,
|
|
maxWidth : '540px',
|
|
...overrides,
|
|
}),
|
|
badge: (overrides?: React.CSSProperties): React.CSSProperties => ({
|
|
display : 'inline-flex',
|
|
alignItems : 'center',
|
|
gap : tokens.space[2],
|
|
padding : `${tokens.space[1]} ${tokens.space[3]}`,
|
|
border : `1px solid ${tokens.color.accent}60`,
|
|
borderRadius: tokens.radius.full,
|
|
background : `${tokens.color.accent}14`,
|
|
color : tokens.color.accentLight,
|
|
fontSize : tokens.font.size.xs,
|
|
fontFamily : tokens.font.family.mono,
|
|
fontWeight : tokens.font.weight.semibd,
|
|
letterSpacing: '0.1em',
|
|
textTransform: 'uppercase',
|
|
backdropFilter: 'blur(8px)',
|
|
...overrides,
|
|
}),
|
|
}
|