Módulo 1 - Aula 3: Primeiro Componente
Implementando um componente Cult UI completo com live preview e aplicação prática
✅ O que você vai dominar:
- • Implementar um componente Cult UI completo e funcional
- • Entender todos os estados e variantes do componente
- • Usar o live preview para testar em tempo real
- • Aplicar o componente em interface real de restaurante
- • Criar suas próprias variações e customizações
🎨 Componente Neumorph Button Completo
🔍 Anatomia do componente
Funcionalidades:
- • forwardRef para referências DOM
- • Framer Motion para animações
- • Sistema de variantes dinâmico
- • Loading states gerenciados
- • Acessibilidade nativa
Características:
- • Efeito neumórfico com sombras
- • Animações de hover e tap
- • Responsivo e customizável
- • TypeScript type-safe
- • Performance otimizada
Neumorph Button - Todos os Estados
Teste interativo com todas as variantes, tamanhos e estados
Variantes disponíveis:
Tamanhos disponíveis:
Estados interativos:
Layout responsivo:
'use client';
import { cn } from '@/lib/utils';
import { forwardRef } from 'react';
import { motion, HTMLMotionProps } from 'framer-motion';
// 🎯 Interface completa do componente
interface NeumorphButtonProps extends Omit<HTMLMotionProps<'button'>, 'ref'> {
variant?: 'default' | 'primary' | 'secondary' | 'danger';
size?: 'sm' | 'md' | 'lg';
isLoading?: boolean;
fullWidth?: boolean;
children: React.ReactNode;
}
// 🎨 Estilos base para efeito neumórfico
const baseStyles = cn(
'relative inline-flex items-center justify-center',
'font-medium transition-all duration-200',
'focus:outline-none focus:ring-2 focus:ring-offset-2',
'disabled:opacity-50 disabled:cursor-not-allowed',
'shadow-neumorph hover:shadow-neumorph-hover',
'active:shadow-neumorph-active'
);
// 🎭 Variantes de cores
const variants = {
default: cn(
'bg-bg-2 text-fg-muted border border-border',
'hover:bg-bg-3 hover:text-foreground',
'focus:ring-gray-500'
),
primary: cn(
'bg-lime-600 text-black border border-lime-500',
'hover:bg-lime-500 hover:text-black',
'focus:ring-lime-400'
),
secondary: cn(
'bg-blue-600 text-foreground border border-blue-500',
'hover:bg-blue-500',
'focus:ring-blue-400'
),
danger: cn(
'bg-red-600 text-foreground border border-red-500',
'hover:bg-red-500',
'focus:ring-red-400'
),
};
// 📏 Tamanhos disponíveis
const sizes = {
sm: 'px-3 py-1.5 text-sm rounded-md',
md: 'px-4 py-2 text-base rounded-lg',
lg: 'px-6 py-3 text-lg rounded-xl',
};
export const NeumorphButton = forwardRef<HTMLButtonElement, NeumorphButtonProps>(
({
className,
variant = 'default',
size = 'md',
isLoading = false,
fullWidth = false,
children,
disabled,
...props
}, ref) => {
return (
<motion.button
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}
className={cn(
baseStyles,
variants[variant],
sizes[size],
fullWidth && 'w-full',
className
)}
disabled={disabled || isLoading}
ref={ref}
{...props}
>
{/* 🔄 Loading spinner */}
{isLoading && (
<div className="mr-2 animate-spin">
<div className="w-4 h-4 border-2 border-current border-t-transparent rounded-full" />
</div>
)}
{/* 📝 Conteúdo do botão */}
<span className={cn(isLoading && 'opacity-70')}>
{children}
</span>
</motion.button>
);
}
);
NeumorphButton.displayName = 'NeumorphButton';🔧 Customizações avançadas
💡 Estendendo o componente
Você pode criar variações personalizadas do NeumorphButton para casos específicos:
// 🎨 Exemplo de customização avançada
const CustomNeumorphButton = forwardRef<HTMLButtonElement, NeumorphButtonProps>(
({ className, ...props }, ref) => {
return (
<NeumorphButton
className={cn(
// Customizações específicas
'transition-all duration-300',
'hover:shadow-lg hover:shadow-lime-500/20',
'active:transform active:scale-95',
className
)}
ref={ref}
{...props}
/>
);
}
);
// 🎯 Variante customizada para notificações
const NotificationButton = ({ count, ...props }) => {
return (
<div className="relative">
<NeumorphButton {...props} />
{count > 0 && (
<span className="absolute -top-2 -right-2 bg-red-500 text-foreground text-xs rounded-full w-5 h-5 flex items-center justify-center">
{count}
</span>
)}
</div>
);
};⚡ Dicas de performance
React.memo: Use para componentes que re-renderizam frequentemente com as mesmas props.
useCallback: Memoize funções onClick para evitar re-renders desnecessários.
Framer Motion: As animações são otimizadas automaticamente para 60fps.
CSS-in-JS: Classes Tailwind são compiladas em tempo de build, sem overhead runtime.
🏪 Aplicação Real: SaaS de Restaurante
🎯 Cenário da aplicação
RestaurantePro: Sistema completo de gestão para restaurantes. O NeumorphButton é usado em todas as interações principais.
Funcionalidades: Dashboard com métricas, gestão de pedidos, controle de cardápio e notificações em tempo real.
UX/UI: Interface moderna com efeitos neumórficos que transmitem profissionalismo e confiabilidade aos usuários.
RestaurantePro - Sistema Completo
Dashboard funcional de um SaaS de restaurante usando Neumorph Buttons
RestaurantePro
Dashboard Administrativo
47
Clientes Ativos
23
Pedidos Hoje
R$ 2.847
Faturamento
'use client';
import { NeumorphButton } from '@/components/cult-ui/neumorph-button';
import { ChefHat, Users, TrendingUp, Clock, MapPin, Star } from 'lucide-react';
export function RestaurantDashboard() {
const [activeTab, setActiveTab] = useState('dashboard');
const [notifications, setNotifications] = useState(3);
const [isProcessing, setIsProcessing] = useState(false);
const handleProcessOrder = () => {
setIsProcessing(true);
setTimeout(() => {
setIsProcessing(false);
setNotifications(prev => prev + 1);
}, 1500);
};
return (
<div className="min-h-screen bg-bg-1 p-6">
<div className="max-w-7xl mx-auto">
{/* 🎯 Header com navegação */}
<div className="mb-8">
<div className="flex items-center justify-between mb-6">
<div className="flex items-center gap-3">
<div className="w-10 h-10 bg-lime-500/20 rounded-lg flex items-center justify-center">
<ChefHat className="w-6 h-6 text-lime-400" />
</div>
<div>
<h1 className="text-foreground text-2xl font-bold">RestaurantePro</h1>
<p className="text-fg-muted text-sm">Dashboard Administrativo</p>
</div>
</div>
<div className="flex items-center gap-3">
<div className="relative">
<NeumorphButton variant="secondary" size="sm">
<span className="w-4 h-4 mr-2">🔔</span>
Notificações
</NeumorphButton>
{notifications > 0 && (
<span className="absolute -top-2 -right-2 bg-red-500 text-foreground text-xs rounded-full w-5 h-5 flex items-center justify-center">
{notifications}
</span>
)}
</div>
<NeumorphButton variant="primary">
+ Novo Pedido
</NeumorphButton>
</div>
</div>
{/* 🎛️ Tabs de navegação */}
<div className="flex gap-2">
{[
{ id: 'dashboard', label: 'Dashboard', icon: TrendingUp },
{ id: 'pedidos', label: 'Pedidos', icon: Clock },
{ id: 'cardapio', label: 'Cardápio', icon: ChefHat }
].map((tab) => (
<NeumorphButton
key={tab.id}
variant={activeTab === tab.id ? 'primary' : 'default'}
size="sm"
onClick={() => setActiveTab(tab.id)}
>
<tab.icon className="w-4 h-4 mr-2" />
{tab.label}
</NeumorphButton>
))}
</div>
</div>
{/* 📊 Conteúdo das tabs */}
{activeTab === 'dashboard' && (
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<StatsCard
icon={Users}
title="Clientes Ativos"
value="47"
change="+12%"
color="lime"
/>
<StatsCard
icon={Clock}
title="Pedidos Hoje"
value="23"
change="+8%"
color="blue"
/>
<StatsCard
icon={TrendingUp}
title="Faturamento"
value="R$ 2.847"
change="+23%"
color="green"
/>
</div>
)}
{activeTab === 'pedidos' && (
<div className="space-y-4">
<OrderCard
id="PED-001"
table="Mesa 12"
items={["Pizza Margherita", "Coca-Cola 350ml"]}
total="R$ 45,90"
status="preparing"
onProcess={handleProcessOrder}
isProcessing={isProcessing}
/>
<OrderCard
id="PED-002"
table="Mesa 8"
items={["Hambúrguer Gourmet", "Batata Frita"]}
total="R$ 32,50"
status="ready"
/>
</div>
)}
{activeTab === 'cardapio' && (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<MenuCard
name="Pizza Margherita"
description="Molho de tomate, mussarela, manjericão"
price="R$ 35,90"
image="🍕"
/>
<MenuCard
name="Hambúrguer Gourmet"
description="Carne artesanal, queijo, alface, tomate"
price="R$ 28,50"
image="🍔"
/>
</div>
)}
</div>
</div>
);
}
// 📊 Componente de estatísticas
function StatsCard({ icon: Icon, title, value, change, color }) {
return (
<div className="bg-bg-2/80 border border-border rounded-lg p-6">
<div className="flex items-center justify-between mb-4">
<div className={`w-10 h-10 rounded-lg flex items-center justify-center ${
color === 'lime' ? 'bg-lime-500/20' :
color === 'blue' ? 'bg-blue-500/20' : 'bg-green-500/20'
}`}>
<Icon className={`w-5 h-5 ${
color === 'lime' ? 'text-lime-400' :
color === 'blue' ? 'text-blue-400' : 'text-green-400'
}`} />
</div>
<span className="text-green-400 text-sm font-semibold">{change}</span>
</div>
<h3 className="text-foreground text-2xl font-bold mb-1">{value}</h3>
<p className="text-fg-muted text-sm">{title}</p>
</div>
);
}
// 🍽️ Componente de pedido
function OrderCard({ id, table, items, total, status, onProcess, isProcessing }) {
const statusConfig = {
preparing: { label: 'Preparando', color: 'yellow' },
ready: { label: 'Pronto', color: 'green' },
delivered: { label: 'Entregue', color: 'gray' }
};
return (
<div className="bg-bg-2/80 border border-border rounded-lg p-6">
<div className="flex items-center justify-between mb-4">
<div>
<h3 className="text-foreground font-semibold">{id}</h3>
<p className="text-fg-muted text-sm">{table}</p>
</div>
<div className="text-right">
<p className="text-foreground font-semibold">{total}</p>
<span className={`text-xs px-2 py-1 rounded ${
statusConfig[status].color === 'yellow' ? 'bg-yellow-500/20 text-yellow-400' :
statusConfig[status].color === 'green' ? 'bg-green-500/20 text-green-400' :
'bg-bg-3/80 text-fg-muted'
}`}>
{statusConfig[status].label}
</span>
</div>
</div>
<div className="mb-4">
<h4 className="text-fg-muted text-sm mb-2">Itens:</h4>
<ul className="text-fg-muted text-sm space-y-1">
{items.map((item, index) => (
<li key={index}>• {item}</li>
))}
</ul>
</div>
<div className="flex gap-2">
{status === 'preparing' && (
<NeumorphButton
variant="primary"
size="sm"
onClick={onProcess}
isLoading={isProcessing}
fullWidth
>
{isProcessing ? 'Processando...' : 'Marcar como Pronto'}
</NeumorphButton>
)}
{status === 'ready' && (
<NeumorphButton variant="secondary" size="sm" fullWidth>
Entregar
</NeumorphButton>
)}
<NeumorphButton variant="danger" size="sm">
Cancelar
</NeumorphButton>
</div>
</div>
);
}
// 🍽️ Componente de item do cardápio
function MenuCard({ name, description, price, image }) {
return (
<div className="bg-bg-2/80 border border-border rounded-lg p-6">
<div className="text-center mb-4">
<div className="text-4xl mb-2">{image}</div>
<h3 className="text-foreground font-semibold">{name}</h3>
<p className="text-fg-muted text-sm mt-1">{description}</p>
</div>
<div className="flex items-center justify-between">
<span className="text-lime-400 font-bold text-lg">{price}</span>
<div className="flex gap-2">
<NeumorphButton variant="secondary" size="sm">
Editar
</NeumorphButton>
<NeumorphButton variant="primary" size="sm">
+ Adicionar
</NeumorphButton>
</div>
</div>
</div>
);
}🔍 Análise da implementação
Casos de uso do NeumorphButton:
- • Navegação: Tabs do dashboard
- • Ações principais: Novo pedido, processar
- • Estados: Loading, disabled, notifications
- • Feedback: Confirmações e alertas
Benefícios da abordagem:
- • Consistência: Mesmo comportamento em todo sistema
- • Acessibilidade: Focus states e aria attributes
- • Performance: Animações otimizadas
- • Manutenibilidade: Código reutilizável
🚀 Próximos Passos
🎯 Módulo 1: Fundamentos - Completado!
✅ Aula 1: Ambiente configurado com Next.js, TypeScript, Tailwind CSS e Framer Motion
✅ Aula 2: Utilitários avançados implementados (cn, animations, colorVariants)
✅ Aula 3: Primeiro componente completo com live preview e aplicação prática
🎛️ Módulo 2: Buttons & Controls - Preview
Aula 1: Family Button - Botões que funcionam em conjunto
Aula 2: Texture Button - Efeitos de textura avançados
Aula 3: Bg Animate Button - Animações de background
// 🎛️ Próximo: Family Button (Módulo 2, Aula 1)
import { NeumorphButton } from '@/components/cult-ui/neumorph-button';
export function FamilyButton() {
return (
<div className="flex items-center gap-2 bg-bg-2/80 p-3 rounded-lg">
<NeumorphButton variant="secondary" size="sm">
Pai
</NeumorphButton>
<NeumorphButton variant="primary" size="sm">
Ativo
</NeumorphButton>
<NeumorphButton variant="default" size="sm">
Filho
</NeumorphButton>
</div>
);
}
// 🎨 Texture Button (Módulo 2, Aula 3)
export function TextureButton() {
return (
<button className="relative bg-gradient-to-br from-gray-700 to-gray-900 text-foreground px-6 py-3 rounded-lg overflow-hidden group">
<div className="absolute inset-0 bg-[url('/noise.png')] opacity-20 mix-blend-overlay" />
<span className="relative z-10">Texture Effect</span>
</button>
);
}🏗️ Construindo o RestaurantePro
Progresso atual: Dashboard básico com NeumorphButton implementado
Próximas features: Sistema de pedidos, controle de estoque, relatórios
Componentes que vamos adicionar: Cards, Carousels, Modais, Formulários
🎨 Desafio: Customize o NeumorphButton
Missão: Criar 2 variações personalizadas do NeumorphButton
Sugestões:IconButton (apenas ícone), GradientButton (com gradiente), PulseButton (com animação pulsante)
Dica: Use o componente base e estenda com suas próprias classes CSS
🌟 Habilidades adquiridas
Técnicas:
- • Implementação de componentes copy-paste
- • Sistema de variantes com TypeScript
- • Animações com Framer Motion
- • Live preview para desenvolvimento
Conceitos:
- • Neumorfismo e design moderno
- • Filosofia copy-paste vs NPM packages
- • Aplicação prática em SaaS real
- • Performance e acessibilidade
📚 Recursos complementares
🎉 Parabéns!
Você completou o Módulo 1 e já domina os fundamentos do Cult UI!