From e203ecf687f54b720ac3f207915f4dd7d5abbfee Mon Sep 17 00:00:00 2001 From: nitrix Date: Mon, 9 Mar 2026 17:50:20 +0100 Subject: [PATCH 01/11] Apply glassmorphism styling to event cards on list view Replace solid white event cards with glass-effect cards featuring backdrop blur, semi-transparent gradient backgrounds, and light borders that blend with the Electric Dusk gradient background. Co-Authored-By: Claude Opus 4.6 --- .specify/memory/research/modern-ui-effects.md | 37 +++++++++++++++++++ frontend/src/assets/main.css | 5 ++- frontend/src/components/EventCard.vue | 21 ++++++++--- frontend/src/components/SectionHeader.vue | 2 +- 4 files changed, 57 insertions(+), 8 deletions(-) create mode 100644 .specify/memory/research/modern-ui-effects.md diff --git a/.specify/memory/research/modern-ui-effects.md b/.specify/memory/research/modern-ui-effects.md new file mode 100644 index 0000000..9867ec9 --- /dev/null +++ b/.specify/memory/research/modern-ui-effects.md @@ -0,0 +1,37 @@ +# Modern UI Effects Research (2025-2026) + +## Liquid Glass (Apple WWDC 2025) +Evolved glassmorphism with directional lighting. Three-layer approach: highlight, shadow, illumination. +- `backdrop-filter: blur(20px) saturate(1.5)` — higher saturation than basic glass +- `inset 0 1px 0 rgba(255,255,255,0.15)` — top highlight (light direction) +- `inset 0 -1px 0 rgba(0,0,0,0.1)` — bottom shadow +- Outer drop shadow for depth: `0 8px 32px rgba(0,0,0,0.3)` +- Advanced: SVG `feTurbulence` + `feSpecularLighting` for refraction (Chromium only) +- Browser support: `backdrop-filter` ~88%, Firefox since v103 + +## Aurora / Gradient Mesh Backgrounds +Stacked animated radial gradients simulating northern lights. Pairs well with glass cards on dark backgrounds. +- Multiple `radial-gradient(ellipse ...)` layers with partial opacity +- Animated via `background-position` shift (GPU-friendly) +- `@property` rule enables direct gradient color animation (broad support since 2024) +- Best for ambient background movement, not for content areas + +## Animated Glow Borders +Rotating `conic-gradient` borders with blur halo. Striking on dark backgrounds. +- Outer wrapper with `conic-gradient(from var(--angle), color1, color2, color3, color1)` +- `::before` pseudo with `filter: blur(12px)` and `opacity: 0.5` for glow halo +- `@property --angle` trick to animate custom property inside `conic-gradient` +- Use sparingly — best for single highlight elements (FAB, CTA), not all cards + +## Modern Neumorphism (2025-2026 revision) +Subtler than the original trend. Higher contrast, less extreme extrusion, combined with accent colors. +- Light and dark shadow pair: `6px 6px 12px rgba(0,0,0,0.5)` + `-6px -6px 12px rgba(60,50,80,0.15)` +- `border: 1px solid rgba(255,255,255,0.05)` for definition +- Works on dark backgrounds with slightly lighter "uplift" shadow direction +- Better suited for interactive elements (buttons, toggles) than content cards + +## Sources +- Apple Liquid Glass CSS: dev.to/gruszdev, dev.to/kevinbism, css-tricks.com, kube.io +- Aurora: dev.to/oobleck, daltonwalsh.com, github.com/mattnewdavid +- Glow borders: frontendmasters.com (Kevin Powell), docode.co.in +- Trends overview: medium.com/design-bootcamp, index.dev, bighuman.com diff --git a/frontend/src/assets/main.css b/frontend/src/assets/main.css index e054431..a81d003 100644 --- a/frontend/src/assets/main.css +++ b/frontend/src/assets/main.css @@ -16,6 +16,9 @@ --color-text-on-gradient: #ffffff; --color-surface: #fff5f8; --color-card: #ffffff; + --color-glass: rgba(255, 255, 255, 0.1); + --color-glass-border: rgba(255, 255, 255, 0.18); + --color-glass-hover: rgba(255, 255, 255, 0.18); /* Gradient */ --gradient-primary: linear-gradient(135deg, #f06292 0%, #ab47bc 50%, #5c6bc0 100%); @@ -33,7 +36,7 @@ --radius-button: 14px; /* Shadows */ - --shadow-card: 0 2px 8px rgba(0, 0, 0, 0.1); + --shadow-card: 0 4px 24px rgba(0, 0, 0, 0.12); --shadow-button: 0 2px 8px rgba(0, 0, 0, 0.15); /* Layout */ diff --git a/frontend/src/components/EventCard.vue b/frontend/src/components/EventCard.vue index e788e32..5587b8a 100644 --- a/frontend/src/components/EventCard.vue +++ b/frontend/src/components/EventCard.vue @@ -93,11 +93,20 @@ function onTouchEnd() { .event-card { display: flex; align-items: center; - background: var(--color-card); + background: linear-gradient(135deg, rgba(255, 255, 255, 0.15) 0%, rgba(255, 255, 255, 0.05) 100%); + border: 1px solid var(--color-glass-border); border-radius: var(--radius-card); box-shadow: var(--shadow-card); + backdrop-filter: blur(16px); + -webkit-backdrop-filter: blur(16px); padding: var(--spacing-md) var(--spacing-lg); gap: var(--spacing-sm); + transition: background 0.2s ease, border-color 0.2s ease; +} + +.event-card:hover { + background: var(--color-glass-hover); + border-color: rgba(255, 255, 255, 0.3); } .event-card--past { @@ -122,7 +131,7 @@ function onTouchEnd() { .event-card__title { font-size: 0.95rem; font-weight: 600; - color: var(--color-text); + color: var(--color-text-on-gradient); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; @@ -131,7 +140,7 @@ function onTouchEnd() { .event-card__time { font-size: 0.8rem; font-weight: 400; - color: #888; + color: rgba(255, 255, 255, 0.7); } .event-card__badge { @@ -149,8 +158,8 @@ function onTouchEnd() { } .event-card__badge--attendee { - background: #e0e0e0; - color: #555; + background: rgba(255, 255, 255, 0.15); + color: rgba(255, 255, 255, 0.9); } .event-card__delete { @@ -163,7 +172,7 @@ function onTouchEnd() { background: none; border: none; font-size: 1.2rem; - color: #bbb; + color: rgba(255, 255, 255, 0.5); cursor: pointer; border-radius: 50%; transition: color 0.15s ease, background 0.15s ease; diff --git a/frontend/src/components/SectionHeader.vue b/frontend/src/components/SectionHeader.vue index a507b3c..035361c 100644 --- a/frontend/src/components/SectionHeader.vue +++ b/frontend/src/components/SectionHeader.vue @@ -15,7 +15,7 @@ defineProps<{ .section-header { font-size: 1rem; font-weight: 700; - color: var(--color-text); + color: var(--color-text-on-gradient); margin: 0; padding: var(--spacing-sm) 0; } From 9f82275c63cd33baf6e0b4050e214d641dca435c Mon Sep 17 00:00:00 2001 From: nitrix Date: Mon, 9 Mar 2026 18:01:46 +0100 Subject: [PATCH 02/11] Replace linear gradient background with aurora mesh gradient Use layered radial gradients on a dark base (#1B1730) with backdrop blur for an organic, aurora-like background effect that better complements the glassmorphism event cards. Co-Authored-By: Claude Opus 4.6 --- frontend/src/assets/main.css | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/frontend/src/assets/main.css b/frontend/src/assets/main.css index a81d003..69a4a3b 100644 --- a/frontend/src/assets/main.css +++ b/frontend/src/assets/main.css @@ -63,7 +63,22 @@ html { body { min-height: 100vh; - background: var(--gradient-primary); + background-color: #1B1730; + position: relative; +} + +body::before { + content: ''; + position: fixed; + inset: 0; + background-color: #1B1730; + background-image: + radial-gradient(at 70% 20%, rgba(240, 98, 146, 0.55) 0px, transparent 50%), + radial-gradient(at 25% 50%, rgba(171, 71, 188, 0.5) 0px, transparent 55%), + radial-gradient(at 80% 70%, rgba(92, 107, 192, 0.55) 0px, transparent 50%), + radial-gradient(at 35% 85%, rgba(255, 112, 67, 0.3) 0px, transparent 40%); + filter: blur(80px); + z-index: -1; } #app { From a9743025a71f2f8872f9af3f1472a04a95defe67 Mon Sep 17 00:00:00 2001 From: nitrix Date: Mon, 9 Mar 2026 18:13:57 +0100 Subject: [PATCH 03/11] Fix hero image transition on event detail page Replace hard-edged color overlay with CSS mask-image fade-out and increase hero height to 420px for a seamless blend into the aurora mesh background. Co-Authored-By: Claude Opus 4.6 --- frontend/src/views/EventDetailView.vue | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/frontend/src/views/EventDetailView.vue b/frontend/src/views/EventDetailView.vue index 36a8cbd..646ab09 100644 --- a/frontend/src/views/EventDetailView.vue +++ b/frontend/src/views/EventDetailView.vue @@ -268,15 +268,19 @@ onMounted(fetchEvent) .detail__hero { position: relative; width: 100%; - height: 260px; - overflow: hidden; + height: 420px; + overflow: visible; flex-shrink: 0; } .detail__hero-img { + position: absolute; + inset: 0; width: 100%; height: 100%; object-fit: cover; + mask-image: linear-gradient(to bottom, black 40%, transparent 100%); + -webkit-mask-image: linear-gradient(to bottom, black 40%, transparent 100%); } .detail__hero-overlay { @@ -284,9 +288,8 @@ onMounted(fetchEvent) inset: 0; background: linear-gradient( to bottom, - rgba(0, 0, 0, 0.4) 0%, - transparent 50%, - var(--color-gradient-start) 100% + rgba(27, 23, 48, 0.4) 0%, + transparent 50% ); } From 877c869a222a3f5ae208cffe2af511202656be29 Mon Sep 17 00:00:00 2001 From: nitrix Date: Mon, 9 Mar 2026 18:20:50 +0100 Subject: [PATCH 04/11] Restyle FAB with glass effect and static glow border Replace solid orange FAB with glassmorphism inner and a conic gradient border (pink-purple-indigo) with subtle glow halo. Co-Authored-By: Claude Opus 4.6 --- frontend/src/components/CreateEventFab.vue | 37 +++++++++++++++++++--- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/frontend/src/components/CreateEventFab.vue b/frontend/src/components/CreateEventFab.vue index a8736c5..30d48a9 100644 --- a/frontend/src/components/CreateEventFab.vue +++ b/frontend/src/components/CreateEventFab.vue @@ -1,6 +1,8 @@ @@ -16,20 +18,44 @@ import { RouterLink } from 'vue-router' width: 56px; height: 56px; border-radius: 50%; - background: var(--color-accent); + background: conic-gradient(from 135deg, #F06292, #AB47BC, #5C6BC0, #F06292); color: #fff; display: flex; align-items: center; justify-content: center; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25); text-decoration: none; z-index: 100; - transition: transform 0.15s ease, box-shadow 0.15s ease; + padding: 2px; + + transition: transform 0.15s ease; +} + +.fab::before { + content: ''; + position: absolute; + inset: -4px; + border-radius: 50%; + background: conic-gradient(from 135deg, #F06292, #AB47BC, #5C6BC0, #F06292); + filter: blur(8px); + opacity: 0.3; + z-index: -1; + +} + +.fab__inner { + width: 100%; + height: 100%; + border-radius: 50%; + background: rgba(27, 23, 48, 0.55); + backdrop-filter: blur(16px); + -webkit-backdrop-filter: blur(16px); + display: flex; + align-items: center; + justify-content: center; } .fab:hover { transform: scale(1.08); - box-shadow: 0 6px 16px rgba(0, 0, 0, 0.3); } .fab:active { @@ -41,6 +67,7 @@ import { RouterLink } from 'vue-router' outline-offset: 3px; } + .fab__icon { font-size: 1.8rem; font-weight: 300; From 29974704d07359d658a448b65b33206bab2cc1f0 Mon Sep 17 00:00:00 2001 From: nitrix Date: Mon, 9 Mar 2026 18:22:39 +0100 Subject: [PATCH 05/11] Apply glassmorphism to meta icon boxes on event detail view Co-Authored-By: Claude Opus 4.6 --- frontend/src/views/EventDetailView.vue | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/frontend/src/views/EventDetailView.vue b/frontend/src/views/EventDetailView.vue index 646ab09..c8deee9 100644 --- a/frontend/src/views/EventDetailView.vue +++ b/frontend/src/views/EventDetailView.vue @@ -369,8 +369,11 @@ onMounted(fetchEvent) display: flex; align-items: center; justify-content: center; - background: rgba(255, 255, 255, 0.15); + background: linear-gradient(135deg, rgba(255, 255, 255, 0.15) 0%, rgba(255, 255, 255, 0.05) 100%); + border: 1px solid var(--color-glass-border); border-radius: 10px; + backdrop-filter: blur(16px); + -webkit-backdrop-filter: blur(16px); color: var(--color-text-on-gradient); } From 019ead7be3cb59cdcedd0838c30642f234ddfc6f Mon Sep 17 00:00:00 2001 From: nitrix Date: Mon, 9 Mar 2026 18:35:36 +0100 Subject: [PATCH 06/11] Extract glass system into shared CSS utilities and design tokens Centralize all hardcoded rgba color values into CSS custom properties and extract glass/glow styles into reusable utility classes (.glass, .glass-inner, .glow-border, .glow-border--animated) in main.css. Co-Authored-By: Claude Opus 4.6 --- frontend/src/assets/main.css | 83 +++++++++++++++++++++- frontend/src/components/AttendeeList.vue | 6 +- frontend/src/components/CreateEventFab.vue | 22 +----- frontend/src/components/DateSubheader.vue | 2 +- frontend/src/components/EmptyState.vue | 33 ++++++++- frontend/src/components/EventCard.vue | 20 ++---- frontend/src/views/EventDetailView.vue | 22 +++--- 7 files changed, 133 insertions(+), 55 deletions(-) diff --git a/frontend/src/assets/main.css b/frontend/src/assets/main.css index 69a4a3b..aa1a138 100644 --- a/frontend/src/assets/main.css +++ b/frontend/src/assets/main.css @@ -16,9 +16,26 @@ --color-text-on-gradient: #ffffff; --color-surface: #fff5f8; --color-card: #ffffff; + --color-dark-base: #1B1730; + + /* Glass system */ --color-glass: rgba(255, 255, 255, 0.1); + --color-glass-strong: rgba(255, 255, 255, 0.15); + --color-glass-subtle: rgba(255, 255, 255, 0.05); --color-glass-border: rgba(255, 255, 255, 0.18); + --color-glass-border-hover: rgba(255, 255, 255, 0.3); --color-glass-hover: rgba(255, 255, 255, 0.18); + --color-glass-inner: rgba(27, 23, 48, 0.55); + --color-glass-overlay: rgba(27, 23, 48, 0.4); + + /* Text on gradient (opacity variants) */ + --color-text-muted: rgba(255, 255, 255, 0.5); + --color-text-secondary: rgba(255, 255, 255, 0.7); + --color-text-soft: rgba(255, 255, 255, 0.85); + --color-text-bright: rgba(255, 255, 255, 0.9); + + /* Glow border */ + --gradient-glow: conic-gradient(from 135deg, var(--color-gradient-start), var(--color-gradient-mid), var(--color-gradient-end), var(--color-gradient-start)); /* Gradient */ --gradient-primary: linear-gradient(135deg, #f06292 0%, #ab47bc 50%, #5c6bc0 100%); @@ -63,7 +80,7 @@ html { body { min-height: 100vh; - background-color: #1B1730; + background-color: var(--color-dark-base); position: relative; } @@ -71,7 +88,7 @@ body::before { content: ''; position: fixed; inset: 0; - background-color: #1B1730; + background-color: var(--color-dark-base); background-image: radial-gradient(at 70% 20%, rgba(240, 98, 146, 0.55) 0px, transparent 50%), radial-gradient(at 25% 50%, rgba(171, 71, 188, 0.5) 0px, transparent 55%), @@ -194,6 +211,68 @@ textarea.form-field { 100% { background-position: -200% 0; } } +/* ── Glass System ── */ + +/* Glass surface: passive containers on gradient (cards, icon boxes) */ +.glass { + background: linear-gradient(135deg, var(--color-glass-strong) 0%, var(--color-glass-subtle) 100%); + border: 1px solid var(--color-glass-border); + box-shadow: var(--shadow-card); + backdrop-filter: blur(16px); + -webkit-backdrop-filter: blur(16px); +} + +.glass:hover { + background: var(--color-glass-hover); + border-color: var(--color-glass-border-hover); +} + +/* Glass interactive inner: dark translucent fill for interactive elements (FAB, CTA) */ +.glass-inner { + background: var(--color-glass-inner); + backdrop-filter: blur(16px); + -webkit-backdrop-filter: blur(16px); +} + +/* Glow border: conic gradient wrapper with halo (static) */ +.glow-border { + background: var(--gradient-glow); + padding: 2px; + position: relative; +} + +.glow-border::before { + content: ''; + position: absolute; + inset: -4px; + border-radius: inherit; + background: var(--gradient-glow); + filter: blur(8px); + opacity: 0.3; + z-index: -1; +} + +/* Glow border animated variant */ +@property --glow-angle { + syntax: ''; + initial-value: 0deg; + inherits: false; +} + +.glow-border--animated { + background: conic-gradient(from var(--glow-angle), var(--color-gradient-start), var(--color-gradient-mid), var(--color-gradient-end), var(--color-gradient-start)); + animation: glow-rotate 4s linear infinite; +} + +.glow-border--animated::before { + background: conic-gradient(from var(--glow-angle), var(--color-gradient-start), var(--color-gradient-mid), var(--color-gradient-end), var(--color-gradient-start)); + animation: glow-rotate 4s linear infinite; +} + +@keyframes glow-rotate { + to { --glow-angle: 360deg; } +} + /* Utility */ .text-center { text-align: center; diff --git a/frontend/src/components/AttendeeList.vue b/frontend/src/components/AttendeeList.vue index d826a8a..d8b5630 100644 --- a/frontend/src/components/AttendeeList.vue +++ b/frontend/src/components/AttendeeList.vue @@ -28,7 +28,7 @@ defineProps<{ .attendee-list__heading { font-size: 0.75rem; font-weight: 700; - color: rgba(255, 255, 255, 0.5); + color: var(--color-text-muted); text-transform: uppercase; letter-spacing: 0.08em; } @@ -44,7 +44,7 @@ defineProps<{ .attendee-list__item { font-size: 0.95rem; - color: rgba(255, 255, 255, 0.85); + color: var(--color-text-soft); line-height: 1.4; overflow: hidden; text-overflow: ellipsis; @@ -53,7 +53,7 @@ defineProps<{ .attendee-list__empty { font-size: 0.9rem; - color: rgba(255, 255, 255, 0.5); + color: var(--color-text-muted); font-style: italic; } diff --git a/frontend/src/components/CreateEventFab.vue b/frontend/src/components/CreateEventFab.vue index 30d48a9..f10489d 100644 --- a/frontend/src/components/CreateEventFab.vue +++ b/frontend/src/components/CreateEventFab.vue @@ -1,6 +1,6 @@