style: polish bottle cards with premium aesthetic and better readability

This commit is contained in:
2025-12-17 23:20:34 +01:00
parent 939d69a634
commit fe82d52a85
29 changed files with 224 additions and 33 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@@ -125,7 +125,7 @@
/******/
/******/ /* webpack/runtime/getFullHash */
/******/ (() => {
/******/ __webpack_require__.h = () => ("2462377f92a8bd62")
/******/ __webpack_require__.h = () => ("b2fa540f7745b378")
/******/ })();
/******/
/******/ /* webpack/runtime/hasOwnProperty shorthand */

View File

@@ -91,7 +91,7 @@ eval(__webpack_require__.ts("\nif (false) {} else {\n module.exports = __webp
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval(__webpack_require__.ts("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (\"2a33e5bce687\");\nif (true) { module.hot.accept() }\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKGFwcC1wYWdlcy1icm93c2VyKS8uL3NyYy9hcHAvZ2xvYmFscy5jc3MiLCJtYXBwaW5ncyI6IjtBQUFBLCtEQUFlLGNBQWM7QUFDN0IsSUFBSSxJQUFVLElBQUksaUJBQWlCIiwic291cmNlcyI6WyJ3ZWJwYWNrOi8vX05fRS8uL3NyYy9hcHAvZ2xvYmFscy5jc3M/ZWY4NSJdLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgZGVmYXVsdCBcIjJhMzNlNWJjZTY4N1wiXG5pZiAobW9kdWxlLmhvdCkgeyBtb2R1bGUuaG90LmFjY2VwdCgpIH1cbiJdLCJuYW1lcyI6W10sInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///(app-pages-browser)/./src/app/globals.css\n"));
eval(__webpack_require__.ts("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (\"6bf1088430fe\");\nif (true) { module.hot.accept() }\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKGFwcC1wYWdlcy1icm93c2VyKS8uL3NyYy9hcHAvZ2xvYmFscy5jc3MiLCJtYXBwaW5ncyI6IjtBQUFBLCtEQUFlLGNBQWM7QUFDN0IsSUFBSSxJQUFVLElBQUksaUJBQWlCIiwic291cmNlcyI6WyJ3ZWJwYWNrOi8vX05fRS8uL3NyYy9hcHAvZ2xvYmFscy5jc3M/ZWY4NSJdLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgZGVmYXVsdCBcIjZiZjEwODg0MzBmZVwiXG5pZiAobW9kdWxlLmhvdCkgeyBtb2R1bGUuaG90LmFjY2VwdCgpIH1cbiJdLCJuYW1lcyI6W10sInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///(app-pages-browser)/./src/app/globals.css\n"));
/***/ }),

File diff suppressed because one or more lines are too long

View File

@@ -192,7 +192,7 @@
/******/
/******/ /* webpack/runtime/getFullHash */
/******/ !function() {
/******/ __webpack_require__.h = function() { return "f57e4df30613b29d"; }
/******/ __webpack_require__.h = function() { return "4f3d48d519328cf2"; }
/******/ }();
/******/
/******/ /* webpack/runtime/global */

View File

@@ -674,6 +674,12 @@ video {
.ml-0\.5 {
margin-left: 0.125rem;
}
.line-clamp-2 {
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}
.block {
display: block;
}
@@ -698,6 +704,9 @@ video {
.aspect-\[4\/5\] {
aspect-ratio: 4/5;
}
.aspect-\[4\/3\] {
aspect-ratio: 4/3;
}
.h-12 {
height: 3rem;
}
@@ -722,6 +731,9 @@ video {
.min-h-screen {
min-height: 100vh;
}
.min-h-\[3\.5rem\] {
min-height: 3.5rem;
}
.w-12 {
width: 3rem;
}
@@ -806,6 +818,9 @@ video {
.flex-col {
flex-direction: column;
}
.flex-wrap {
flex-wrap: wrap;
}
.items-start {
align-items: flex-start;
}
@@ -918,6 +933,9 @@ video {
.rounded {
border-radius: 0.25rem;
}
.rounded-\[2rem\] {
border-radius: 2rem;
}
.border {
border-width: 1px;
}
@@ -930,6 +948,9 @@ video {
.border-l-2 {
border-left-width: 2px;
}
.border-t {
border-top-width: 1px;
}
.border-dashed {
border-style: dashed;
}
@@ -1005,6 +1026,9 @@ video {
.border-zinc-200\/50 {
border-color: rgb(228 228 231 / 0.5);
}
.border-amber-200\/50 {
border-color: rgb(253 230 138 / 0.5);
}
.bg-amber-600 {
--tw-bg-opacity: 1;
background-color: rgb(217 119 6 / var(--tw-bg-opacity, 1));
@@ -1101,6 +1125,25 @@ video {
.bg-transparent {
background-color: transparent;
}
.bg-amber-50 {
--tw-bg-opacity: 1;
background-color: rgb(255 251 235 / var(--tw-bg-opacity, 1));
}
.bg-gradient-to-t {
background-image: linear-gradient(to top, var(--tw-gradient-stops));
}
.from-black\/40 {
--tw-gradient-from: rgb(0 0 0 / 0.4) var(--tw-gradient-from-position);
--tw-gradient-to: rgb(0 0 0 / 0) var(--tw-gradient-to-position);
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
}
.via-transparent {
--tw-gradient-to: rgb(0 0 0 / 0) var(--tw-gradient-to-position);
--tw-gradient-stops: var(--tw-gradient-from), transparent var(--tw-gradient-via-position), var(--tw-gradient-to);
}
.to-transparent {
--tw-gradient-to: transparent var(--tw-gradient-to-position);
}
.fill-amber-500 {
fill: #f59e0b;
}
@@ -1126,6 +1169,9 @@ video {
.p-1 {
padding: 0.25rem;
}
.p-5 {
padding: 1.25rem;
}
.px-6 {
padding-left: 1.5rem;
padding-right: 1.5rem;
@@ -1178,6 +1224,10 @@ video {
padding-top: 0.625rem;
padding-bottom: 0.625rem;
}
.px-2\.5 {
padding-left: 0.625rem;
padding-right: 0.625rem;
}
.pl-10 {
padding-left: 2.5rem;
}
@@ -1273,6 +1323,9 @@ video {
.leading-tight {
line-height: 1.25;
}
.leading-none {
line-height: 1;
}
.tracking-wider {
letter-spacing: 0.05em;
}
@@ -1285,6 +1338,9 @@ video {
.tracking-widest {
letter-spacing: 0.1em;
}
.tracking-\[0\.2em\] {
letter-spacing: 0.2em;
}
.text-green-500 {
--tw-text-opacity: 1;
color: rgb(34 197 94 / var(--tw-text-opacity, 1));
@@ -1357,9 +1413,19 @@ video {
--tw-text-opacity: 1;
color: rgb(126 34 206 / var(--tw-text-opacity, 1));
}
.text-zinc-300 {
--tw-text-opacity: 1;
color: rgb(212 212 216 / var(--tw-text-opacity, 1));
}
.accent-amber-600 {
accent-color: #d97706;
}
.opacity-0 {
opacity: 0;
}
.opacity-70 {
opacity: 0.7;
}
.shadow-2xl {
--tw-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25);
--tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);
@@ -1438,9 +1504,20 @@ video {
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
.transition-opacity {
transition-property: opacity;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
.duration-500 {
transition-duration: 500ms;
}
.duration-300 {
transition-duration: 300ms;
}
.duration-700 {
transition-duration: 700ms;
}
:root {
--foreground-rgb: 0, 0, 0;
@@ -1466,6 +1543,11 @@ body {
rgb(var(--background-start-rgb));
}
.hover\:-translate-y-1:hover {
--tw-translate-y: -0.25rem;
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}
.hover\:scale-\[1\.02\]:hover {
--tw-scale-x: 1.02;
--tw-scale-y: 1.02;
@@ -1563,6 +1645,17 @@ body {
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
.hover\:shadow-2xl:hover {
--tw-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25);
--tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
.hover\:shadow-amber-900\/10:hover {
--tw-shadow-color: rgb(120 53 15 / 0.1);
--tw-shadow: var(--tw-shadow-colored);
}
.focus\:border-transparent:focus {
border-color: transparent;
}
@@ -1600,6 +1693,10 @@ body {
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}
.group:hover .group-hover\:border-amber-500\/30 {
border-color: rgb(245 158 11 / 0.3);
}
.group:hover .group-hover\:bg-black\/10 {
background-color: rgb(0 0 0 / 0.1);
}
@@ -1609,6 +1706,15 @@ body {
color: rgb(245 158 11 / var(--tw-text-opacity, 1));
}
.group:hover .group-hover\:text-amber-600 {
--tw-text-opacity: 1;
color: rgb(217 119 6 / var(--tw-text-opacity, 1));
}
.group:hover .group-hover\:opacity-100 {
opacity: 1;
}
@media (min-width: 640px) {
.sm\:grid-cols-2 {
@@ -1703,6 +1809,14 @@ body {
border-color: rgb(39 39 42 / 0.5);
}
.dark\:border-amber-800\/20 {
border-color: rgb(146 64 14 / 0.2);
}
.dark\:border-zinc-700\/50 {
border-color: rgb(63 63 70 / 0.5);
}
.dark\:bg-green-900\/10 {
background-color: rgb(20 83 45 / 0.1);
}
@@ -1777,6 +1891,10 @@ body {
background-color: rgb(88 28 135 / 0.3);
}
.dark\:bg-amber-900\/20 {
background-color: rgb(120 53 15 / 0.2);
}
.dark\:text-zinc-100 {
--tw-text-opacity: 1;
color: rgb(244 244 245 / var(--tw-text-opacity, 1));

View File

@@ -0,0 +1,22 @@
"use strict";
/*
* ATTENTION: An "eval-source-map" devtool has been used.
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
self["webpackHotUpdate_N_E"]("app/layout",{
/***/ "(app-pages-browser)/./src/app/globals.css":
/*!*****************************!*\
!*** ./src/app/globals.css ***!
\*****************************/
/***/ (function(module, __webpack_exports__, __webpack_require__) {
eval(__webpack_require__.ts("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (\"6bf1088430fe\");\nif (true) { module.hot.accept() }\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKGFwcC1wYWdlcy1icm93c2VyKS8uL3NyYy9hcHAvZ2xvYmFscy5jc3MiLCJtYXBwaW5ncyI6IjtBQUFBLCtEQUFlLGNBQWM7QUFDN0IsSUFBSSxJQUFVLElBQUksaUJBQWlCIiwic291cmNlcyI6WyJ3ZWJwYWNrOi8vX05fRS8uL3NyYy9hcHAvZ2xvYmFscy5jc3M/ZWY4NSJdLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgZGVmYXVsdCBcIjZiZjEwODg0MzBmZVwiXG5pZiAobW9kdWxlLmhvdCkgeyBtb2R1bGUuaG90LmFjY2VwdCgpIH1cbiJdLCJuYW1lcyI6W10sInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///(app-pages-browser)/./src/app/globals.css\n"));
/***/ })
});

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
{"c":["app/page","app/layout","webpack"],"r":[],"m":[]}

View File

@@ -0,0 +1,18 @@
"use strict";
/*
* ATTENTION: An "eval-source-map" devtool has been used.
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
self["webpackHotUpdate_N_E"]("webpack",{},
/******/ function(__webpack_require__) { // webpackRuntimeModules
/******/ /* webpack/runtime/getFullHash */
/******/ !function() {
/******/ __webpack_require__.h = function() { return "4f3d48d519328cf2"; }
/******/ }();
/******/
/******/ }
);

File diff suppressed because one or more lines are too long

View File

@@ -9,53 +9,62 @@ interface BottleCardProps {
}
function BottleCard({ bottle }: BottleCardProps) {
const statusConfig = {
open: { icon: Unlock, color: 'bg-amber-500/80 border-amber-400/50', label: 'Offen' },
sampled: { icon: FlaskConical, color: 'bg-purple-500/80 border-purple-400/50', label: 'Sample' },
empty: { icon: Ghost, color: 'bg-zinc-500/80 border-zinc-400/50', label: 'Leer' },
sealed: { icon: Lock, color: 'bg-blue-600/80 border-blue-400/50', label: 'Versiegelt' },
};
const StatusIcon = statusConfig[bottle.status as keyof typeof statusConfig]?.icon || Lock;
const statusStyle = statusConfig[bottle.status as keyof typeof statusConfig] || statusConfig.sealed;
return (
<Link href={`/bottles/${bottle.id}`} className="block">
<div className="bg-white dark:bg-zinc-900 rounded-2xl overflow-hidden border border-zinc-200 dark:border-zinc-800 shadow-md transition-all hover:scale-[1.02] hover:shadow-xl group relative">
<div className="aspect-[3/2] overflow-hidden bg-zinc-100 dark:bg-zinc-800 relative">
<Link href={`/bottles/${bottle.id}`} className="block h-full group">
<div className="h-full bg-white dark:bg-zinc-900 rounded-[2rem] overflow-hidden border border-zinc-200 dark:border-zinc-800 shadow-sm transition-all duration-300 hover:shadow-2xl hover:shadow-amber-900/10 hover:-translate-y-1 group-hover:border-amber-500/30">
<div className="aspect-[4/3] overflow-hidden bg-zinc-100 dark:bg-zinc-800 relative">
<img
src={bottle.image_url}
alt={bottle.name}
className="w-full h-full object-cover transition-transform duration-500 group-hover:scale-110"
className="w-full h-full object-cover transition-transform duration-700 group-hover:scale-110"
/>
<div className="absolute inset-0 bg-black/0 group-hover:bg-black/10 transition-colors" />
<div className="absolute inset-0 bg-gradient-to-t from-black/40 via-transparent to-transparent opacity-0 group-hover:opacity-100 transition-opacity" />
{bottle.last_tasted && (
<div className="absolute top-3 right-3 bg-zinc-900/80 backdrop-blur-md text-white text-[10px] font-bold px-2 py-1 rounded-md flex items-center gap-1 border border-white/10">
<Calendar size={10} />
ZULETZT: {new Date(bottle.last_tasted).toLocaleDateString('de-DE')}
<div className="absolute top-3 right-3 bg-zinc-900/80 backdrop-blur-md text-white text-[9px] font-black px-2 py-1 rounded-lg flex items-center gap-1 border border-white/10 ring-1 ring-black/5">
<Clock size={10} />
{new Date(bottle.last_tasted).toLocaleDateString('de-DE')}
</div>
)}
<div className={`absolute bottom-3 left-3 px-2 py-1 rounded-md text-[10px] font-black uppercase flex items-center gap-1.5 backdrop-blur-md border ${bottle.status === 'open'
? 'bg-amber-500/80 text-white border-amber-400/50'
: bottle.status === 'sampled'
? 'bg-purple-500/80 text-white border-purple-400/50'
: bottle.status === 'empty'
? 'bg-zinc-500/80 text-white border-zinc-400/50'
: 'bg-blue-600/80 text-white border-blue-400/50'
}`}>
{bottle.status === 'open' ? <Unlock size={10} /> : bottle.status === 'sampled' ? <FlaskConical size={10} /> : bottle.status === 'empty' ? <Ghost size={10} /> : <Lock size={10} />}
{bottle.status}
<div className={`absolute bottom-3 left-3 px-3 py-1.5 rounded-xl text-[10px] font-black uppercase flex items-center gap-2 backdrop-blur-md border shadow-lg ${statusStyle.color}`}>
<StatusIcon size={12} />
{statusStyle.label}
</div>
</div>
<div className="p-4">
<h3 className="font-bold text-lg text-zinc-800 dark:text-zinc-100 truncate">{bottle.name}</h3>
<p className="text-zinc-500 text-sm truncate">{bottle.distillery}</p>
<div className="mt-2 flex items-center gap-1.5 text-[10px] font-bold text-zinc-400 uppercase tracking-tight">
<Clock size={12} />
Hinzugefügt am {new Date(bottle.created_at).toLocaleDateString('de-DE')}
<div className="p-5 space-y-4">
<div>
<p className="text-[10px] font-black text-amber-600 uppercase tracking-[0.2em] mb-1 leading-none">{bottle.distillery}</p>
<h3 className="font-black text-xl text-zinc-900 dark:text-zinc-100 leading-tight group-hover:text-amber-600 transition-colors line-clamp-2 min-h-[3.5rem] flex items-center">
{bottle.name}
</h3>
</div>
<div className="mt-3 flex items-center justify-between">
<span className="px-2 py-1 bg-amber-100 dark:bg-amber-900/30 text-amber-700 dark:text-amber-400 text-xs font-semibold rounded-md">
<div className="flex flex-wrap gap-2">
<span className="px-2.5 py-1 bg-zinc-100 dark:bg-zinc-800 text-zinc-600 dark:text-zinc-400 text-[10px] font-black uppercase tracking-widest rounded-lg border border-zinc-200/50 dark:border-zinc-700/50">
{bottle.category}
</span>
<span className="text-xs text-zinc-400">
{bottle.abv}% Vol.
<span className="px-2.5 py-1 bg-amber-50 dark:bg-amber-900/20 text-amber-700 dark:text-amber-400 text-[10px] font-black uppercase tracking-widest rounded-lg border border-amber-200/50 dark:border-amber-800/20">
{bottle.abv}% VOL
</span>
</div>
<div className="pt-2 flex items-center gap-2 text-[10px] font-bold text-zinc-400 uppercase tracking-wider border-t border-zinc-100 dark:border-zinc-800">
<Calendar size={12} className="text-zinc-300" />
<span className="opacity-70 text-[9px]">Hinzugefügt am</span>
<span className="text-zinc-500 dark:text-zinc-300">{new Date(bottle.created_at).toLocaleDateString('de-DE')}</span>
</div>
</div>
</div>
</Link>