Industrial Dark UI Overhaul: Updated colors, typography, navigation, and component styling across the application

This commit is contained in:
2025-12-22 00:05:31 +01:00
parent cf491d83b6
commit f0588418c8
28 changed files with 582 additions and 613 deletions

View File

@@ -36,71 +36,65 @@ function BottleCard({ bottle, sessionId }: BottleCardProps) {
return (
<Link
href={`/bottles/${bottle.id}${sessionId ? `?session_id=${sessionId}` : ''}`}
className="block h-[420px] group relative overflow-hidden rounded-2xl border border-white/5 transition-all duration-700 hover:shadow-[0_20px_50px_rgba(0,0,0,0.5)] active:scale-[0.98]"
className="block h-fit group relative overflow-hidden rounded-xl bg-zinc-900 border border-zinc-800 transition-all duration-300 hover:border-zinc-700 active:scale-[0.98]"
>
{/* Image Layer - Edge to Edge */}
<div className="absolute inset-0">
{/* Image Layer - Clean Split Top */}
<div className="aspect-[4/3] overflow-hidden">
<img
src={getStorageUrl(bottle.image_url)}
alt={bottle.name}
className="w-full h-full object-cover group-hover:scale-110 transition-transform duration-1000 ease-out"
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500 ease-out"
/>
{/* Gradient Overlay as requested: bottom third, black to transparent */}
<div className="absolute inset-0 bg-gradient-to-t from-black via-black/60 to-transparent" />
</div>
{/* Content Layer */}
<div className="absolute inset-0 flex flex-col justify-end p-6">
<div className="space-y-3">
{/* Tags Layer - Minimalist Glassmorphism */}
<div className="flex flex-wrap gap-2 opacity-0 group-hover:opacity-100 translate-y-4 group-hover:translate-y-0 transition-all duration-500">
<span className="px-3 py-1 bg-white/10 backdrop-blur-md border border-white/10 text-[9px] font-sans font-bold uppercase tracking-widest text-[#C89D46] rounded-full">
{shortenCategory(bottle.category)}
</span>
<span className="px-3 py-1 bg-white/10 backdrop-blur-md border border-white/10 text-[9px] font-sans font-bold uppercase tracking-widest text-white/60 rounded-full">
{bottle.abv}% VOL
</span>
</div>
{/* Info Layer - Clean Split Bottom */}
<div className="p-4 space-y-4">
<div className="space-y-1">
<p className="text-[10px] font-bold text-orange-500 uppercase tracking-widest leading-none">
{bottle.distillery}
</p>
<h3 className="font-bold text-lg text-zinc-50 leading-tight">
{bottle.name || t('grid.unknownBottle')}
</h3>
</div>
<div>
<p className="text-[10px] font-sans font-bold text-[#C89D46] uppercase tracking-[0.2em] mb-1">
{bottle.distillery}
</p>
<h3 className="font-display font-bold text-2xl text-white leading-tight drop-shadow-lg">
{bottle.name || t('grid.unknownBottle')}
</h3>
</div>
<div className="flex flex-wrap gap-2">
<span className="px-2 py-0.5 bg-zinc-800 text-zinc-400 text-[9px] font-bold uppercase tracking-widest rounded-md">
{shortenCategory(bottle.category)}
</span>
<span className="px-2 py-0.5 bg-zinc-800 text-zinc-400 text-[9px] font-bold uppercase tracking-widest rounded-md">
{bottle.abv}% VOL
</span>
</div>
{/* Metadata items */}
<div className="flex items-center gap-4 pt-2 border-t border-white/10 opacity-60 group-hover:opacity-100 transition-opacity">
<div className="flex items-center gap-1.5 text-[10px] font-sans font-medium text-white/70">
<Calendar size={12} className="text-[#C89D46]" />
{new Date(bottle.created_at).toLocaleDateString(locale === 'de' ? 'de-DE' : 'en-US')}
{/* Metadata items */}
<div className="flex items-center gap-4 pt-3 border-t border-zinc-800/50">
<div className="flex items-center gap-1 text-[10px] font-medium text-zinc-500">
<Calendar size={12} className="text-zinc-500" />
{new Date(bottle.created_at).toLocaleDateString(locale === 'de' ? 'de-DE' : 'en-US')}
</div>
{bottle.last_tasted && (
<div className="flex items-center gap-1 text-[10px] font-medium text-zinc-500">
<Clock size={12} className="text-zinc-500" />
{new Date(bottle.last_tasted).toLocaleDateString(locale === 'de' ? 'de-DE' : 'en-US')}
</div>
{bottle.last_tasted && (
<div className="flex items-center gap-1.5 text-[10px] font-sans font-medium text-white/70">
<Clock size={12} className="text-[#C89D46]" />
{new Date(bottle.last_tasted).toLocaleDateString(locale === 'de' ? 'de-DE' : 'en-US')}
</div>
)}
</div>
)}
</div>
</div>
{/* Top Overlays */}
{(bottle.is_whisky === false || (bottle.confidence && bottle.confidence < 70)) && (
<div className="absolute top-4 right-4 z-10">
<div className="bg-red-500/90 backdrop-blur-sm text-white p-2 rounded-full animate-pulse shadow-lg">
<AlertCircle size={14} />
<div className="absolute top-3 right-3 z-10">
<div className="bg-red-500 text-white p-1.5 rounded-full shadow-lg">
<AlertCircle size={12} />
</div>
</div>
)}
{sessionId && (
<div className="absolute top-4 left-4 z-10 bg-[#C89D46] text-[#0F1014] text-[9px] font-black px-3 py-1.5 rounded-full flex items-center gap-2 border border-white/20 shadow-xl">
<PlusCircle size={14} />
ADD TO SESSION
<div className="absolute top-3 left-3 z-10 bg-orange-600 text-white text-[9px] font-bold px-2 py-1 rounded-md flex items-center gap-1.5 shadow-xl">
<PlusCircle size={12} />
ADD
</div>
)}
</Link>
@@ -200,13 +194,13 @@ export default function BottleGrid({ bottles }: BottleGridProps) {
<div id="search-filter" className="w-full max-w-6xl mx-auto px-4 space-y-6 scroll-mt-32">
<div className="flex flex-col md:flex-row gap-4">
<div className="relative flex-1 group">
<Search className="absolute left-0 top-1/2 -translate-y-1/2 text-[#8F9096] group-focus-within:text-[#C89D46] transition-colors" size={20} />
<Search className="absolute left-0 top-1/2 -translate-y-1/2 text-zinc-500 group-focus-within:text-orange-500 transition-colors" size={20} />
<input
type="text"
placeholder={t('grid.searchPlaceholder')}
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="w-full pl-8 pr-8 py-4 bg-transparent border-b border-white/10 focus:border-[#C89D46] outline-none transition-all text-white placeholder:text-[#8F9096] font-sans"
className="w-full pl-8 pr-8 py-4 bg-transparent border-b border-zinc-800 focus:border-orange-500 outline-none transition-all text-zinc-50 placeholder:text-zinc-500"
/>
{searchQuery && (
<button
@@ -222,24 +216,24 @@ export default function BottleGrid({ bottles }: BottleGridProps) {
<select
value={sortBy}
onChange={(e) => setSortBy(e.target.value as any)}
className="bg-transparent border-none text-[#8F9096] text-xs font-sans font-bold uppercase tracking-widest outline-none cursor-pointer hover:text-white transition-colors appearance-none"
className="bg-transparent border-none text-zinc-500 text-xs font-bold uppercase tracking-widest outline-none cursor-pointer hover:text-white transition-colors appearance-none"
>
<option value="created_at" className="bg-[#0F1014]">{t('grid.sortBy.createdAt')}</option>
<option value="last_tasted" className="bg-[#0F1014]">{t('grid.sortBy.lastTasted')}</option>
<option value="name" className="bg-[#0F1014]">{t('grid.sortBy.name')}</option>
<option value="created_at" className="bg-zinc-950">{t('grid.sortBy.createdAt')}</option>
<option value="last_tasted" className="bg-zinc-950">{t('grid.sortBy.lastTasted')}</option>
<option value="name" className="bg-zinc-950">{t('grid.sortBy.name')}</option>
</select>
<button
onClick={() => setIsFiltersOpen(!isFiltersOpen)}
className={`flex items-center gap-2 text-xs font-sans font-bold uppercase tracking-widest transition-all ${isFiltersOpen || activeFiltersCount > 0
? 'text-[#C89D46]'
: 'text-[#8F9096] hover:text-white'
className={`flex items-center gap-2 text-xs font-bold uppercase tracking-widest transition-all ${isFiltersOpen || activeFiltersCount > 0
? 'text-orange-500'
: 'text-zinc-500 hover:text-white'
}`}
>
<Filter size={18} />
<span className="hidden sm:inline">{t('grid.filters')}</span>
{activeFiltersCount > 0 && (
<span className="bg-[#C89D46] text-[#0F1014] w-4 h-4 rounded-full flex items-center justify-center text-[8px] font-black">
<span className="bg-orange-600 text-white w-4 h-4 rounded-full flex items-center justify-center text-[8px] font-bold">
{activeFiltersCount}
</span>
)}
@@ -247,13 +241,13 @@ export default function BottleGrid({ bottles }: BottleGridProps) {
</div>
</div>
{/* Category Quick Filter - Glass Chips */}
{/* Category Quick Filter - Flat Chips */}
<div className="flex gap-3 overflow-x-auto pb-2 -mx-4 px-4 scrollbar-hide touch-pan-x">
<button
onClick={() => setSelectedCategory(null)}
className={`px-4 py-2 rounded-full text-[10px] font-sans font-bold uppercase tracking-widest whitespace-nowrap transition-all border ${selectedCategory === null
? 'bg-[#C89D46] border-[#C89D46] text-[#0F1014]'
: 'bg-white/5 border-white/10 text-[#8F9096] hover:border-white/20'
className={`px-4 py-2 rounded-full text-[10px] font-bold uppercase tracking-widest whitespace-nowrap transition-all border ${selectedCategory === null
? 'bg-orange-600 border-orange-600 text-white'
: 'bg-zinc-900 border-zinc-800 text-zinc-400 hover:border-zinc-700'
}`}
>
{t('common.all')}
@@ -262,9 +256,9 @@ export default function BottleGrid({ bottles }: BottleGridProps) {
<button
key={cat}
onClick={() => setSelectedCategory(selectedCategory === cat ? null : cat)}
className={`px-4 py-2 rounded-full text-[10px] font-sans font-bold uppercase tracking-widest whitespace-nowrap transition-all border ${selectedCategory === cat
? 'bg-[#C89D46] border-[#C89D46] text-[#0F1014]'
: 'bg-white/5 border-white/10 text-[#8F9096] hover:border-white/20'
className={`px-4 py-2 rounded-full text-[10px] font-bold uppercase tracking-widest whitespace-nowrap transition-all border ${selectedCategory === cat
? 'bg-orange-600 border-orange-600 text-white'
: 'bg-zinc-900 border-zinc-800 text-zinc-400 hover:border-zinc-700'
}`}
>
{shortenCategory(cat)}
@@ -272,17 +266,17 @@ export default function BottleGrid({ bottles }: BottleGridProps) {
))}
</div>
{/* Collapsible Advanced Filters - Minimalist Overlay */}
{/* Collapsible Advanced Filters - Industrial Overlay */}
{isFiltersOpen && (
<div className="p-8 bg-[#1A1B20] border border-white/10 rounded-3xl space-y-8 animate-in fade-in slide-in-from-top-4 duration-500">
<div className="p-8 bg-zinc-900 border border-zinc-800 rounded-2xl space-y-8 animate-in fade-in slide-in-from-top-4 duration-500">
<div className="space-y-4">
<label className="text-[10px] font-sans font-bold uppercase tracking-[0.2em] text-[#8F9096]">{t('grid.filter.distillery')}</label>
<label className="text-[10px] font-bold uppercase tracking-[0.2em] text-zinc-500">{t('grid.filter.distillery')}</label>
<div className="flex flex-wrap gap-2">
<button
onClick={() => setSelectedDistillery(null)}
className={`px-4 py-2 rounded-full text-[10px] font-sans font-bold uppercase tracking-widest transition-all border ${selectedDistillery === null
? 'bg-[#C89D46] border-[#C89D46] text-[#0F1014]'
: 'bg-white/5 border-white/10 text-[#8F9096]'
className={`px-4 py-2 rounded-full text-[10px] font-bold uppercase tracking-widest transition-all border ${selectedDistillery === null
? 'bg-orange-600 border-orange-600 text-white'
: 'bg-zinc-800 border-zinc-700 text-zinc-400'
}`}
>
{t('common.all')}
@@ -291,9 +285,9 @@ export default function BottleGrid({ bottles }: BottleGridProps) {
<button
key={dist}
onClick={() => setSelectedDistillery(selectedDistillery === dist ? null : dist)}
className={`px-4 py-2 rounded-full text-[10px] font-sans font-bold uppercase tracking-widest transition-all border ${selectedDistillery === dist
? 'bg-[#C89D46] border-[#C89D46] text-[#0F1014]'
: 'bg-white/5 border-white/10 text-[#8F9096]'
className={`px-4 py-2 rounded-full text-[10px] font-bold uppercase tracking-widest transition-all border ${selectedDistillery === dist
? 'bg-orange-600 border-orange-600 text-white'
: 'bg-zinc-800 border-zinc-700 text-zinc-400'
}`}
>
{dist}
@@ -302,20 +296,20 @@ export default function BottleGrid({ bottles }: BottleGridProps) {
</div>
</div>
<div className="pt-6 border-t border-white/5 flex justify-between items-center">
<div className="pt-6 border-t border-zinc-800 flex justify-between items-center">
<button
onClick={() => {
setSelectedCategory(null);
setSelectedDistillery(null);
setSearchQuery('');
}}
className="text-[10px] font-sans font-bold text-red-500 uppercase tracking-widest hover:text-red-400"
className="text-[10px] font-bold text-red-500 uppercase tracking-widest hover:text-red-400"
>
{t('grid.resetAll')}
</button>
<button
onClick={() => setIsFiltersOpen(false)}
className="px-8 py-3 bg-white text-[#0F1014] text-[10px] font-sans font-bold rounded-full uppercase tracking-widest transition-transform active:scale-95"
className="px-8 py-3 bg-zinc-50 text-zinc-950 text-[10px] font-bold rounded-full uppercase tracking-widest transition-transform active:scale-95"
>
{t('grid.close')}
</button>