style: refactor UI to a minimalist design system using monochromatic black-and-white theme
This commit is contained in:
@@ -15,38 +15,37 @@ export const Header = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<header className="sticky top-0 z-30 bg-white/70 backdrop-blur-lg border-b border-slate-200/50 shadow-sm shadow-slate-200/20">
|
||||
<div className="mx-auto flex h-16 sm:h-20 max-w-6xl items-center justify-between px-3 sm:px-6">
|
||||
<header className="sticky top-0 z-30 bg-white border-b border-zinc-200">
|
||||
<div className="mx-auto flex h-16 sm:h-20 max-w-6xl items-center justify-between px-4 sm:px-6">
|
||||
<Link
|
||||
to="/"
|
||||
className="text-2xl font-black tracking-tighter text-slate-900 group flex items-center gap-2"
|
||||
className="text-xl font-bold tracking-tight text-black group flex items-center gap-3"
|
||||
onClick={() => setMobileMenuOpen(false)}
|
||||
>
|
||||
<div className="w-8 h-8 rounded-lg bg-indigo-600 flex items-center justify-center text-white shadow-lg shadow-indigo-200">
|
||||
<User size={18} fill="currentColor" />
|
||||
<div className="w-8 h-8 bg-black flex items-center justify-center text-white">
|
||||
<User size={16} fill="currentColor" />
|
||||
</div>
|
||||
<span className="group-hover:text-indigo-600 transition-colors uppercase italic font-black">Smart Agenda</span>
|
||||
<span className="font-bold uppercase tracking-tight">Smart Agenda</span>
|
||||
</Link>
|
||||
|
||||
{/* Desktop Navigation */}
|
||||
<nav className="hidden md:flex items-center gap-8">
|
||||
<nav className="hidden md:flex items-center gap-6">
|
||||
{user?.role !== 'barbearia' && (
|
||||
<>
|
||||
<Link
|
||||
to="/explorar"
|
||||
className="text-sm font-bold text-slate-600 hover:text-slate-900 transition-all flex items-center gap-2"
|
||||
className="text-xs font-bold uppercase text-zinc-600 hover:text-black transition-colors"
|
||||
>
|
||||
<MapPin size={16} className="text-indigo-500" />
|
||||
<span>Descobrir Barbearias</span>
|
||||
Explorar
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
to="/carrinho"
|
||||
className="relative text-slate-700 hover:text-slate-900 transition-all p-2 rounded-xl hover:bg-slate-50"
|
||||
className="relative text-zinc-600 hover:text-black transition-colors"
|
||||
>
|
||||
<ShoppingCart size={20} className="text-slate-700" />
|
||||
<ShoppingCart size={20} />
|
||||
{cart.length > 0 && (
|
||||
<span className="absolute -right-1 -top-1 rounded-full bg-slate-900 px-1.5 py-0.5 text-[10px] font-black text-indigo-400 shadow-md min-w-[18px] text-center">
|
||||
<span className="absolute -right-2 -top-2 bg-black px-1.5 py-0.5 text-[10px] font-bold text-white">
|
||||
{cart.length}
|
||||
</span>
|
||||
)}
|
||||
@@ -54,43 +53,40 @@ export const Header = () => {
|
||||
</>
|
||||
)}
|
||||
|
||||
<div className="h-6 w-px bg-slate-200 mx-1" />
|
||||
<div className="h-4 w-px bg-zinc-200 mx-2" />
|
||||
|
||||
{user ? (
|
||||
<div className="flex items-center gap-4">
|
||||
<button
|
||||
onClick={() => navigate(user.role === 'barbearia' ? '/painel' : '/perfil')}
|
||||
className="flex items-center gap-3 bg-slate-50 hover:bg-slate-100 border border-slate-200/60 pl-3 pr-4 py-1.5 rounded-full transition-all group"
|
||||
className="flex items-center gap-2 text-xs font-bold uppercase text-zinc-900"
|
||||
type="button"
|
||||
>
|
||||
<div className="w-7 h-7 rounded-full bg-indigo-600 flex items-center justify-center text-white shadow-sm">
|
||||
<User size={14} fill="currentColor" />
|
||||
</div>
|
||||
<span className="text-sm font-bold text-slate-700 group-hover:text-slate-900 max-w-[120px] truncate">{user.name}</span>
|
||||
<User size={14} />
|
||||
<span>{user.name}</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={handleLogout}
|
||||
className="p-2 text-slate-400 hover:text-rose-600 hover:bg-rose-50 rounded-xl transition-all"
|
||||
title="Sair"
|
||||
className="text-zinc-400 hover:text-black transition-colors"
|
||||
type="button"
|
||||
>
|
||||
<LogOut size={18} />
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex items-center gap-4">
|
||||
<Link
|
||||
to="/login"
|
||||
className="text-sm font-bold text-slate-600 hover:text-slate-900 px-4 py-2 transition-colors"
|
||||
className="text-xs font-bold uppercase text-zinc-600 hover:text-black"
|
||||
>
|
||||
Login
|
||||
</Link>
|
||||
<Link
|
||||
to="/registo"
|
||||
className="inline-flex items-center justify-center rounded-xl bg-indigo-600 px-5 py-2 text-sm font-bold text-white shadow-lg shadow-indigo-100 hover:bg-indigo-700 transition-all font-black uppercase italic tracking-widest"
|
||||
className="bg-black text-white px-4 py-2 text-xs font-bold uppercase tracking-wider hover:bg-zinc-800"
|
||||
>
|
||||
Criar Conta
|
||||
Registo
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
@@ -99,7 +95,7 @@ export const Header = () => {
|
||||
{/* Mobile Menu Button */}
|
||||
<button
|
||||
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
|
||||
className="md:hidden p-2.5 bg-slate-50 border border-slate-200 rounded-xl text-slate-900 transition-all"
|
||||
className="md:hidden p-2 text-black"
|
||||
type="button"
|
||||
>
|
||||
{mobileMenuOpen ? <X size={20} /> : <Menu size={20} />}
|
||||
@@ -108,75 +104,69 @@ export const Header = () => {
|
||||
|
||||
{/* Mobile Menu */}
|
||||
{mobileMenuOpen && (
|
||||
<div className="md:hidden border-t border-slate-100 bg-white shadow-2xl animate-in slide-in-from-top-4 duration-300">
|
||||
<div className="md:hidden border-t border-zinc-200 bg-white">
|
||||
<nav className="px-6 py-6 space-y-4">
|
||||
{user?.role !== 'barbearia' && (
|
||||
<>
|
||||
<Link
|
||||
to="/explorar"
|
||||
onClick={() => setMobileMenuOpen(false)}
|
||||
className="flex items-center gap-3 text-base font-black text-slate-700 hover:text-indigo-600 p-3 rounded-2xl bg-slate-50 transition-all uppercase italic"
|
||||
className="block text-xs font-bold uppercase text-zinc-900 p-2"
|
||||
>
|
||||
<MapPin size={18} className="text-indigo-600" />
|
||||
Descobrir Barbearias
|
||||
Explorar
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
to="/carrinho"
|
||||
onClick={() => setMobileMenuOpen(false)}
|
||||
className="flex items-center gap-3 text-base font-black text-slate-700 hover:text-indigo-600 p-3 rounded-2xl bg-slate-50 transition-all uppercase italic"
|
||||
className="flex items-center justify-between text-xs font-bold uppercase text-zinc-900 p-2"
|
||||
>
|
||||
<ShoppingCart size={18} className="text-indigo-600" />
|
||||
Meu Carrinho
|
||||
Carrinho
|
||||
{cart.length > 0 && (
|
||||
<span className="ml-auto rounded-full bg-slate-900 px-2 py-0.5 text-[10px] font-black text-indigo-400">
|
||||
{cart.length}
|
||||
</span>
|
||||
<span className="bg-black px-2 py-0.5 text-white">{cart.length}</span>
|
||||
)}
|
||||
</Link>
|
||||
</>
|
||||
)}
|
||||
|
||||
<div className="h-px bg-slate-100 w-full" />
|
||||
<div className="h-px bg-zinc-100 w-full" />
|
||||
|
||||
{user ? (
|
||||
<>
|
||||
<div className="space-y-2">
|
||||
<button
|
||||
onClick={() => {
|
||||
navigate(user.role === 'barbearia' ? '/painel' : '/perfil')
|
||||
setMobileMenuOpen(false)
|
||||
}}
|
||||
className="w-full flex items-center gap-3 text-base font-bold text-slate-700 p-3 rounded-2xl hover:bg-slate-50 transition-all text-left"
|
||||
className="w-full text-left text-xs font-bold uppercase text-zinc-900 p-2"
|
||||
type="button"
|
||||
>
|
||||
<User size={18} className="text-slate-400" />
|
||||
{user.name}
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={handleLogout}
|
||||
className="w-full flex items-center gap-3 text-base font-bold text-rose-600 p-3 rounded-2xl hover:bg-rose-50 transition-all text-left"
|
||||
className="w-full text-left text-xs font-bold uppercase text-zinc-400 p-2"
|
||||
type="button"
|
||||
>
|
||||
<LogOut size={18} />
|
||||
Sair da Conta
|
||||
Sair
|
||||
</button>
|
||||
</>
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div className="flex flex-col gap-2 p-2">
|
||||
<Link
|
||||
to="/login"
|
||||
onClick={() => setMobileMenuOpen(false)}
|
||||
className="flex items-center justify-center rounded-2xl border border-slate-200 bg-white py-3 text-sm font-bold text-slate-700"
|
||||
className="text-xs font-bold uppercase text-zinc-900"
|
||||
>
|
||||
Entrar
|
||||
Login
|
||||
</Link>
|
||||
<Link
|
||||
to="/registo"
|
||||
onClick={() => setMobileMenuOpen(false)}
|
||||
className="flex items-center justify-center rounded-2xl bg-slate-900 py-3 text-sm font-bold text-indigo-400 shadow-lg shadow-slate-200 font-black uppercase italic tracking-widest"
|
||||
className="bg-black text-white text-center py-3 text-xs font-bold uppercase"
|
||||
>
|
||||
Criar Conta
|
||||
Registo
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -10,15 +10,15 @@ type Props = React.ButtonHTMLAttributes<HTMLButtonElement> & {
|
||||
export const Button = ({ className, variant = 'solid', size = 'md', asChild, ...props }: Props) => {
|
||||
const base = 'inline-flex items-center justify-center font-semibold transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed';
|
||||
const variants = {
|
||||
solid: 'bg-gradient-to-r from-cyan-500 to-sky-600 text-white hover:from-cyan-600 hover:to-sky-700 shadow-xl shadow-cyan-500/10 focus:ring-cyan-500/50 active:scale-[0.98]',
|
||||
outline: 'border border-white/10 text-white bg-white/5 hover:bg-white/10 hover:border-white/20 focus:ring-white/20 active:scale-[0.98]',
|
||||
ghost: 'text-zinc-400 hover:text-white hover:bg-white/5 focus:ring-white/20 active:scale-[0.98]',
|
||||
danger: 'bg-gradient-to-r from-rose-500 to-red-600 text-white hover:from-rose-600 hover:to-red-700 shadow-xl shadow-rose-500/10 focus:ring-rose-500/50 active:scale-[0.98]',
|
||||
solid: 'bg-black text-white hover:bg-zinc-800',
|
||||
outline: 'border border-black text-black hover:bg-zinc-50',
|
||||
ghost: 'text-zinc-600 hover:text-black hover:bg-zinc-100',
|
||||
danger: 'bg-red-600 text-white hover:bg-red-700',
|
||||
};
|
||||
const sizes = {
|
||||
sm: 'text-xs px-3 py-1.5 rounded-lg',
|
||||
md: 'text-sm px-4 py-2.5 rounded-lg',
|
||||
lg: 'text-base px-6 py-3 rounded-xl',
|
||||
sm: 'text-xs px-3 py-1',
|
||||
md: 'text-sm px-4 py-2',
|
||||
lg: 'text-base px-6 py-3',
|
||||
};
|
||||
const cls = cn(base, variants[variant], sizes[size], className);
|
||||
if (asChild && React.isValidElement(props.children)) {
|
||||
|
||||
@@ -3,8 +3,8 @@ import { cn } from '../../lib/cn';
|
||||
export const Card = ({ children, className = '', hover = false }: { children: React.ReactNode; className?: string; hover?: boolean }) => (
|
||||
<div
|
||||
className={cn(
|
||||
'bg-zinc-900/80 backdrop-blur-sm rounded-2xl shadow-2xl border border-white/5 transition-all duration-300',
|
||||
hover && 'hover:border-cyan-500/30 hover:bg-zinc-800/80',
|
||||
'bg-white border border-zinc-200 transition-colors',
|
||||
hover && 'hover:bg-zinc-50',
|
||||
className
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -3,24 +3,21 @@ import { cn } from '../../lib/cn';
|
||||
type Tab = { id: string; label: string; badge?: number };
|
||||
|
||||
export const Tabs = ({ tabs, active, onChange, className }: { tabs: Tab[]; active: string; onChange: (id: string) => void; className?: string }) => (
|
||||
<div className={cn("flex gap-1.5 p-1 bg-zinc-950/50 backdrop-blur-md rounded-xl border border-white/5 overflow-x-auto scrollbar-hide w-full", className)}>
|
||||
<div className={cn("flex border-b border-zinc-200 w-full", className)}>
|
||||
{tabs.map((t) => (
|
||||
<button
|
||||
key={t.id}
|
||||
onClick={() => onChange(t.id)}
|
||||
className={cn(
|
||||
"px-4 sm:px-6 py-2 sm:py-3 text-[10px] sm:text-xs font-black uppercase tracking-widest transition-all rounded-lg whitespace-nowrap shrink-0",
|
||||
"px-4 py-3 text-xs font-bold uppercase transition-all whitespace-nowrap",
|
||||
active === t.id
|
||||
? "bg-cyan-500 text-white shadow-xl shadow-cyan-500/20"
|
||||
: "text-zinc-500 hover:text-zinc-200 hover:bg-white/5"
|
||||
? "border-b-2 border-black text-black"
|
||||
: "text-zinc-400 hover:text-black"
|
||||
)}
|
||||
>
|
||||
{t.label}
|
||||
{t.badge && (
|
||||
<span className={cn(
|
||||
"ml-2 inline-flex items-center justify-center w-4 h-4 sm:w-5 sm:h-5 text-[8px] sm:text-[10px] font-black rounded-full border transition-colors",
|
||||
active === t.id ? "bg-white text-cyan-600 border-transparent" : "bg-zinc-800 text-zinc-400 border-white/5"
|
||||
)}>
|
||||
<span className="ml-2 inline-flex items-center justify-center w-4 h-4 bg-zinc-100 text-[8px] font-bold text-zinc-600">
|
||||
{t.badge}
|
||||
</span>
|
||||
)}
|
||||
|
||||
@@ -4,17 +4,17 @@
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--brand-primary: #6366f1; /* Indigo 500 */
|
||||
--brand-primary-light: #a5b4fc;
|
||||
--brand-primary-dark: #4338ca;
|
||||
--obsidian: #0f172a;
|
||||
--obsidian-light: #1e293b;
|
||||
--slate-950: #020617;
|
||||
--brand-primary: #000000;
|
||||
--brand-primary-light: #404040;
|
||||
--brand-primary-dark: #000000;
|
||||
--obsidian: #000000;
|
||||
--obsidian-light: #171717;
|
||||
--slate-950: #000000;
|
||||
color-scheme: light;
|
||||
}
|
||||
|
||||
* {
|
||||
@apply border-slate-200;
|
||||
@apply border-zinc-200;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@@ -23,33 +23,31 @@
|
||||
width: 100%;
|
||||
max-width: 100vw;
|
||||
position: relative;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
body {
|
||||
@apply bg-slate-50 text-slate-900 font-sans antialiased;
|
||||
@apply bg-white text-zinc-900 font-sans antialiased;
|
||||
touch-action: pan-y;
|
||||
background-image:
|
||||
radial-gradient(at 0% 0%, rgba(99, 102, 241, 0.03) 0px, transparent 50%),
|
||||
radial-gradient(at 100% 0%, rgba(15, 23, 42, 0.03) 0px, transparent 50%);
|
||||
font-feature-settings: 'cv02', 'cv03', 'cv04', 'cv11';
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
a {
|
||||
@apply text-inherit no-underline transition-colors;
|
||||
@apply text-inherit no-underline;
|
||||
}
|
||||
|
||||
/* Scrollbar styling */
|
||||
::-webkit-scrollbar {
|
||||
@apply w-2 h-2;
|
||||
@apply w-1.5 h-1.5;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
@apply bg-slate-100;
|
||||
@apply bg-zinc-100;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
@apply bg-indigo-200 rounded-full hover:bg-indigo-300;
|
||||
@apply bg-zinc-300 rounded-none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +57,7 @@
|
||||
}
|
||||
|
||||
.glass-card {
|
||||
@apply bg-white/80 backdrop-blur-md border border-white/20 shadow-xl shadow-slate-200/50;
|
||||
@apply bg-white border border-zinc-200;
|
||||
}
|
||||
|
||||
/* Hide scrollbar but keep scroll functionality */
|
||||
@@ -72,10 +70,10 @@
|
||||
}
|
||||
|
||||
.indigo-gradient {
|
||||
@apply bg-gradient-to-br from-indigo-400 via-indigo-500 to-indigo-600;
|
||||
@apply bg-black text-white;
|
||||
}
|
||||
|
||||
.obsidian-gradient {
|
||||
@apply bg-gradient-to-br from-slate-800 via-slate-900 to-slate-950;
|
||||
@apply bg-black text-white;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@ module.exports = {
|
||||
extend: {
|
||||
colors: {
|
||||
amber: colors.amber,
|
||||
slate: colors.slate,
|
||||
indigo: colors.indigo,
|
||||
slate: colors.zinc,
|
||||
indigo: colors.black, // Solid black for the simplest look
|
||||
violet: colors.violet,
|
||||
emerald: colors.emerald,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user