From 6f532e45ec6aff44b1eee414f263ff77a52db71c Mon Sep 17 00:00:00 2001 From: Ulas Date: Tue, 14 Apr 2026 07:35:59 +0200 Subject: [PATCH] feat: Liquid Glass Phase 4 - vibrancy, module tint, deeper glass penetration Dashboard widgets, task cards, note items, meal slots, form inputs, toolbars, and FAB actions now use semi-transparent glass backgrounds with backdrop-filter blur. Each surface gets a subtle module accent color tint via color-mix gradient overlay. App background uses a radial accent gradient for ambient vibrancy. New tokens: --glass-bg-card, --glass-bg-input, --glass-bg-toolbar, --glass-tint-strength with full dark mode and accessibility overrides. --- CHANGELOG.md | 16 +++ README.md | 2 + docs/SPEC.md | 23 +++- package-lock.json | 4 +- package.json | 2 +- public/styles/glass.css | 287 +++++++++++++++++++++++++++++++++++++++ public/styles/tokens.css | 28 ++++ 7 files changed, 356 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bffd972..73dc95c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.18.0] - 2026-04-14 + +### Added +- Glass Phase 4: Liquid Glass Vibrancy + Tint - deeper glass penetration across all UI surfaces +- New glass tokens in `tokens.css`: `--glass-bg-card` (52% opacity), `--glass-bg-card-hover`, `--glass-bg-input`, `--glass-bg-toolbar`, `--glass-tint-strength` (6% light / 8% dark) with full dark mode and accessibility overrides +- Dashboard widgets now use semi-transparent glass backgrounds with `backdrop-filter: blur(8px) saturate(180%)` - content beneath widgets shines through +- Module tint: each widget gets a subtle accent color gradient overlay via `::after` pseudo-element using `color-mix(module-accent, 6%, transparent)` - dashboard cards carry a hint of their module's color +- Task cards, note items, and meal slots use glass backgrounds with blur for consistent vibrancy +- Page toolbars (Tasks, Notes, Contacts, Calendar) rendered as glass bars with module accent tint +- Form inputs, group toggles, and FAB speed-dial actions use glass vibrancy backgrounds +- App content background uses a radial gradient with the active module accent for ambient vibrancy +- Skeleton loading states use glass backgrounds for visual consistency +- All new glass effects gated behind `@supports (backdrop-filter)` for progressive enhancement +- Accessibility: all new effects respect `prefers-reduced-transparency` (solid fallbacks) and `prefers-reduced-motion` +- Load-order safety: all glass selectors use parent-scoped specificity (`.dashboard .widget`, `.tasks-page .task-card`) to prevent override by on-demand page CSS + ## [0.17.4] - 2026-04-13 ### Fixed diff --git a/README.md b/README.md index cc654fa..7a59a8f 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,8 @@ **Privacy First:** SQLCipher AES-256 encrypted database, fully self-hosted, zero telemetry +**Liquid Glass UI:** Translucent surfaces with backdrop blur, module-tinted glass overlays, spring animations - inspired by Apple's Liquid Glass, built in pure CSS + **PWA Native Feel:** Installable on any device, works offline, dark mode, responsive from phone to desktop **Multilingual:** German, English, Spanish, French, Italian, Swedish, Greek, Russian, Turkish, and Chinese UI with automatic locale detection diff --git a/docs/SPEC.md b/docs/SPEC.md index a09690d..3484959 100644 --- a/docs/SPEC.md +++ b/docs/SPEC.md @@ -290,6 +290,13 @@ Source of truth: `public/styles/tokens.css`. Key values: --blur-md: 16px; --radius-glass-button: 9999px; /* capsule */ --ease-glass: cubic-bezier(0.34, 1.56, 0.64, 1); /* spring */ + + /* Glass Vibrancy tokens (Phase 4): */ + --glass-bg-card: rgba(255,255,255,0.52); /* transparent for vibrancy */ + --glass-bg-card-hover: rgba(255,255,255,0.65); + --glass-bg-input: rgba(255,255,255,0.48); + --glass-bg-toolbar: rgba(255,255,255,0.58); + --glass-tint-strength: 6%; /* module accent tint */ } @media (prefers-color-scheme: dark) { @@ -301,6 +308,8 @@ Source of truth: `public/styles/tokens.css`. Key values: --color-text-secondary: #8E8E8C; --glass-bg: rgba(28,28,26,0.75); --glass-border: rgba(255,255,255,0.12); + --glass-bg-card: rgba(38,38,36,0.50); + --glass-tint-strength: 8%; } } ``` @@ -312,18 +321,26 @@ Source of truth: `public/styles/tokens.css`. Key values: ### Glass Layer (`public/styles/glass.css`) -Additive CSS file loaded globally after `layout.css`. Implements a Liquid Glass design language: +Additive CSS file loaded globally after `layout.css`. Implements a Liquid Glass design language inspired by Apple's iOS 26 Liquid Glass, adapted for CSS/web: +**Phase 1-3 (Shell + Components + Polish):** - **Translucent surfaces:** `backdrop-filter: blur()` on bottom nav, sidebar, modal overlay, cards on hover. All blur effects are inside `@supports (backdrop-filter: blur(1px))` for progressive enhancement. - **Glass tokens:** Section 16 of `tokens.css` defines `--glass-bg*`, `--glass-border*`, `--blur-xs` through `--blur-xl`, `--opacity-glass-*`, `--glass-highlight*`, `--glass-shadow-sm/md/lg`, `--radius-glass-card/inner/chip/button`, `--ease-glass`, `--transition-glass`. Full dark mode overrides. - **Capsule shapes:** Buttons, FAB, and search inputs use `--radius-glass-button` (pill shape). - **Spring animations:** Modal entrance (`glass-modal-scale-in` / `glass-sheet-in`), page transitions, and list stagger all use `cubic-bezier(0.34, 1.56, 0.64, 1)` spring easing. - **FAB attention pulse:** `fab-ring-pulse` keyframe expands a ring around the FAB to signal readiness. - **Nav auto-hide:** Bottom bar hides on scroll-down, reappears on scroll-up (mobile only, < 1024px, 4 px hysteresis). CSS: `.nav-bottom--hidden { transform: translateY(calc(100% + var(--safe-area-inset-bottom))); }`. JS: `initNavHideOnScroll()` in `router.js`. -- **Accessibility:** `prefers-reduced-transparency`, `prefers-reduced-motion`, and `prefers-contrast: more` blocks deactivate blur/animation and restore solid fallbacks. + +**Phase 4 (Vibrancy + Tint):** +- **Deeper glass penetration:** Dashboard widgets, task cards, note items, meal slots, form inputs, toolbars, group toggles, and FAB speed-dial actions all use semi-transparent glass backgrounds (`--glass-bg-card`, 52% opacity) with `backdrop-filter: blur() saturate()` so underlying content shines through. +- **Module tint:** Each glass surface receives a subtle accent color gradient overlay via `::after` pseudo-element using `color-mix(in srgb, var(--module-accent) var(--glass-tint-strength), transparent)`. Strength is 6% in light mode, 8% in dark mode. +- **App vibrancy background:** `app-content` uses a radial gradient with the active module accent at 3% opacity to provide an ambient color base that glass elements refract. +- **Load-order safety:** All Phase 4 glass selectors use parent-scoped specificity (`.dashboard .widget`, `.tasks-page .task-card`, `.meals-page .meal-slot`) to prevent override by on-demand page CSS that loads after `glass.css`. + +**Accessibility:** `prefers-reduced-transparency`, `prefers-reduced-motion`, and `prefers-contrast: more` blocks deactivate blur/animation and restore solid fallbacks across all phases. ### Components -- **Cards:** `var(--color-surface)`, `var(--radius-md)`, `var(--shadow-sm)`. Consistent padding `var(--space-4)` (16px) across all modules. +- **Cards:** `var(--color-surface)` base, glass vibrancy via `var(--glass-bg-card)` + `backdrop-filter: blur(8px) saturate(180%)` when supported. `var(--radius-md)`, `var(--shadow-sm)`. Module tint overlay via `::after`. Consistent padding `var(--space-4)` (16px) across all modules. - **Buttons:** Primary = accent + white. Secondary = outline. Min-height 44px. Capsule shape via `--radius-glass-button`. Submit buttons show success (checkmark, 700ms green via `.btn--success`) and error (shake via `.btn--shaking`). - **Inputs:** `var(--radius-sm)`, 1.5px border, padding 12px 16px. Search inputs use `--radius-glass-button` and `--glass-border-subtle`. `[required]` fields receive validation status on blur (`.form-field--error` / `.form-field--valid`). Enter moves focus to the next field; Enter on the last field triggers submit. - **FAB (Floating Action Button):** Color follows the module accent token (`--module-accent`) - each module defines its own accent color. Specular inner highlight + attention ring pulse. Hidden when the virtual keyboard is open (`visualViewport.resize`, threshold 75% of window height). diff --git a/package-lock.json b/package-lock.json index f30f19d..7a98bac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "oikos", - "version": "0.11.5", + "version": "0.18.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "oikos", - "version": "0.11.5", + "version": "0.18.0", "license": "MIT", "dependencies": { "bcrypt": "^6.0.0", diff --git a/package.json b/package.json index 7fb9723..b1be33e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "oikos", - "version": "0.17.4", + "version": "0.18.0", "description": "Self-hosted family planner - calendar, tasks, shopping, meal planning, budget and more. Private, open-source, no subscription.", "main": "server/index.js", "type": "module", diff --git a/public/styles/glass.css b/public/styles/glass.css index 8812697..ab6066b 100644 --- a/public/styles/glass.css +++ b/public/styles/glass.css @@ -488,3 +488,290 @@ textarea.form-input { animation: none; } } + +/* ================================================================ + * PHASE 4 — Liquid Glass Vibrancy + Tint + * ================================================================ */ + +/* ================================================================ + * 25. Widget Cards — Glass Vibrancy + * + * Widgets bekommen transparente Hintergruende mit Blur, + * sodass darunterliegender Content durchscheint. + * Tint: subtile Tonung durch Modul-Akzentfarbe. + * ================================================================ */ +@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) { + /* .dashboard Eltern-Selektor fuer Spezifitaet (Load-Order: glass.css < dashboard.css) */ + .dashboard .widget { + background-color: var(--glass-bg-card); + backdrop-filter: var(--blur-sm) saturate(180%); + -webkit-backdrop-filter: var(--blur-sm) saturate(180%); + } + + .dashboard .widget:hover { + background-color: var(--glass-bg-card-hover); + } + + /* Modul-Tint: subtiler Farbverlauf durch Akzentfarbe */ + .dashboard .widget::after { + content: ''; + position: absolute; + inset: 0; + border-radius: inherit; + background: linear-gradient( + 135deg, + color-mix(in srgb, var(--module-accent, var(--color-accent)) var(--glass-tint-strength), transparent), + transparent 70% + ); + pointer-events: none; + z-index: 0; + } +} + +/* ================================================================ + * 26. Greeting-Widget — Glass + Vibrancy auf Gradient + * ================================================================ */ +@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) { + .widget-greeting { + backdrop-filter: var(--blur-xs) saturate(200%); + -webkit-backdrop-filter: var(--blur-xs) saturate(200%); + } + + /* Greeting braucht keinen separaten Tint - hat eigenen Gradient */ + .widget-greeting::after { + display: none; + } +} + +/* ================================================================ + * 27. Wetter-Widget — Vibrancy auf Gradient + * ================================================================ */ +@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) { + .weather-widget { + backdrop-filter: var(--blur-xs) saturate(200%); + -webkit-backdrop-filter: var(--blur-xs) saturate(200%); + } + + .weather-widget::after { + display: none; + } +} + +/* ================================================================ + * 28. Page-Toolbars — Glass Bar mit Tint + * + * Modul-Header/Toolbars als Glass-Elemente mit + * Modul-Akzentfarbe als dezenter Tint. + * ================================================================ */ +@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) { + /* Eltern-Selektoren fuer Spezifitaet */ + .tasks-page .tasks-toolbar, + .notes-page .notes-toolbar, + .contacts-page .contacts-toolbar, + .calendar-page .cal-toolbar { + background-color: var(--glass-bg-toolbar); + backdrop-filter: var(--blur-sm) saturate(160%); + -webkit-backdrop-filter: var(--blur-sm) saturate(160%); + border-radius: var(--radius-md); + border: 1px solid var(--glass-border-subtle); + padding-left: var(--space-4); + padding-right: var(--space-4); + } + + /* Tint-Akzent oben: 3px-Linie wird zu subtiler Tonung */ + .tasks-page .tasks-toolbar { + border-top-color: color-mix(in srgb, var(--module-accent) 40%, var(--glass-border)); + } +} + +/* ================================================================ + * 29. Note-Items — Glass Cards + * ================================================================ */ +@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) { + .notes-page .note-item, + .dashboard .note-item { + background-color: var(--glass-bg-card); + backdrop-filter: var(--blur-xs) saturate(150%); + -webkit-backdrop-filter: var(--blur-xs) saturate(150%); + border: 1px solid var(--glass-border-subtle); + box-shadow: var(--glass-shadow-sm); + } + + .notes-page .note-item:hover, + .dashboard .note-item:hover { + background-color: var(--glass-bg-card-hover); + box-shadow: var(--glass-shadow-md); + } +} + +/* ================================================================ + * 30. Meal-Slots — Glass Grid-Zellen + * ================================================================ */ +@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) { + .dashboard .meal-slot, + .meals-page .meal-slot { + background-color: var(--glass-bg-card); + backdrop-filter: var(--blur-xs) saturate(140%); + -webkit-backdrop-filter: var(--blur-xs) saturate(140%); + } + + .dashboard .meal-slot:hover, + .meals-page .meal-slot:hover { + background-color: var(--glass-bg-card-hover); + } + + .dashboard .meal-slot--filled, + .meals-page .meal-slot--filled { + background-color: color-mix(in srgb, var(--module-accent, var(--module-meals)) 4%, var(--glass-bg-card)); + } +} + +/* ================================================================ + * 31. Form-Inputs — Glass Vibrancy + * ================================================================ */ +@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) { + .input, + .form-input, + select.form-input, + textarea.form-input { + background-color: var(--glass-bg-input); + backdrop-filter: var(--blur-xs) saturate(120%); + -webkit-backdrop-filter: var(--blur-xs) saturate(120%); + } +} + +/* ================================================================ + * 32. List-Items — Subtile Glass-Trennung + * ================================================================ */ +@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) { + /* Eltern-Selektoren fuer Spezifitaet (glass.css < page CSS) */ + .tasks-page .task-card { + background-color: var(--glass-bg-card); + backdrop-filter: var(--blur-xs) saturate(150%); + -webkit-backdrop-filter: var(--blur-xs) saturate(150%); + } + + .tasks-page .task-card:hover { + background-color: color-mix(in srgb, var(--module-accent, var(--color-accent)) 4%, var(--glass-bg-card-hover)); + } + + .shopping-page .shopping-item:hover, + .contacts-page .contact-item:hover { + background-color: color-mix(in srgb, var(--module-accent, var(--color-accent)) 4%, var(--glass-bg-card-hover)); + border-radius: var(--radius-sm); + } +} + +/* ================================================================ + * 33. Group-Toggle / Filter-Chips — Glass Segmented Control + * ================================================================ */ +@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) { + .group-toggle { + background-color: var(--glass-bg-input); + backdrop-filter: var(--blur-xs) saturate(140%); + -webkit-backdrop-filter: var(--blur-xs) saturate(140%); + border: 1px solid var(--glass-border-subtle); + } + + .group-toggle__btn--active { + background-color: var(--glass-bg-elevated); + box-shadow: var(--glass-shadow-sm); + } +} + +/* ================================================================ + * 34. FAB Speed-Dial Actions — Glass Labels + Buttons + * ================================================================ */ +@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) { + .fab-action__label { + background-color: var(--glass-bg-elevated); + backdrop-filter: var(--blur-sm) saturate(160%); + -webkit-backdrop-filter: var(--blur-sm) saturate(160%); + border: 1px solid var(--glass-border-subtle); + box-shadow: var(--glass-shadow-md); + } + + .fab-action__btn { + background-color: var(--glass-bg-elevated); + backdrop-filter: var(--blur-sm) saturate(160%); + -webkit-backdrop-filter: var(--blur-sm) saturate(160%); + border: 1px solid var(--glass-border-subtle); + } +} + +/* ================================================================ + * 35. App-Content-Hintergrund — Vibrancy-Basis + * + * Subtiler Gradient im Seitenhintergrund damit Glass- + * Elemente darauf lebendiger wirken. + * ================================================================ */ +.app-content { + background: + radial-gradient( + ellipse at 20% 0%, + color-mix(in srgb, var(--module-accent, var(--color-accent)) 3%, transparent), + transparent 60% + ), + var(--color-bg); +} + +/* ================================================================ + * 36. Skeleton — Glass Shimmer Update + * ================================================================ */ +@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) { + .widget-skeleton { + background-color: var(--glass-bg-card); + backdrop-filter: var(--blur-xs) saturate(140%); + -webkit-backdrop-filter: var(--blur-xs) saturate(140%); + border: 1px solid var(--glass-border-subtle); + } +} + +/* ================================================================ + * 37. Accessibility — Phase 4 + * ================================================================ */ +@media (prefers-reduced-transparency: reduce) { + .dashboard .widget, + .tasks-page .task-card, + .notes-page .note-item, + .dashboard .note-item, + .dashboard .meal-slot, + .meals-page .meal-slot, + .widget-skeleton { + background-color: var(--color-surface); + backdrop-filter: none; + -webkit-backdrop-filter: none; + } + + .dashboard .widget::after { + display: none; + } + + .app-content { + background: var(--color-bg); + } + + .group-toggle, + .fab-action__label, + .fab-action__btn { + backdrop-filter: none; + -webkit-backdrop-filter: none; + } + + .input, + .form-input, + select.form-input, + textarea.form-input { + backdrop-filter: none; + -webkit-backdrop-filter: none; + } + + .tasks-page .tasks-toolbar, + .notes-page .notes-toolbar, + .contacts-page .contacts-toolbar, + .calendar-page .cal-toolbar { + background-color: var(--color-surface); + backdrop-filter: none; + -webkit-backdrop-filter: none; + } +} diff --git a/public/styles/tokens.css b/public/styles/tokens.css index 5e224fa..fa79211 100644 --- a/public/styles/tokens.css +++ b/public/styles/tokens.css @@ -278,6 +278,15 @@ --glass-border: rgba(255, 255, 255, 0.60); --glass-border-subtle: rgba(255, 255, 255, 0.35); + /* a2) Glass-Hintergründe: Vibrancy-Stufe (transparenter, mehr Durchschein) */ + --glass-bg-card: rgba(255, 255, 255, 0.52); + --glass-bg-card-hover: rgba(255, 255, 255, 0.65); + --glass-bg-input: rgba(255, 255, 255, 0.48); + --glass-bg-toolbar: rgba(255, 255, 255, 0.58); + + /* a3) Tint: Modul-Akzentfarbe als subtile Glass-Tonung */ + --glass-tint-strength: 6%; + /* b) Blur-Stufen */ --blur-xs: blur(4px); --blur-sm: blur(8px); @@ -412,6 +421,13 @@ --glass-shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.30), 0 0 0 1px rgba(255, 255, 255, 0.08); --glass-shadow-md: 0 4px 20px rgba(0, 0, 0, 0.40), 0 0 0 1px rgba(255, 255, 255, 0.07); --glass-shadow-lg: 0 8px 40px rgba(0, 0, 0, 0.55), 0 0 0 1px rgba(255, 255, 255, 0.06); + + /* Vibrancy - Dark Mode */ + --glass-bg-card: rgba(38, 38, 36, 0.50); + --glass-bg-card-hover: rgba(48, 48, 46, 0.62); + --glass-bg-input: rgba(34, 34, 32, 0.45); + --glass-bg-toolbar: rgba(40, 40, 38, 0.55); + --glass-tint-strength: 8%; } } @@ -503,6 +519,13 @@ --glass-shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.30), 0 0 0 1px rgba(255, 255, 255, 0.08); --glass-shadow-md: 0 4px 20px rgba(0, 0, 0, 0.40), 0 0 0 1px rgba(255, 255, 255, 0.07); --glass-shadow-lg: 0 8px 40px rgba(0, 0, 0, 0.55), 0 0 0 1px rgba(255, 255, 255, 0.06); + + /* Vibrancy - Dark Mode */ + --glass-bg-card: rgba(38, 38, 36, 0.50); + --glass-bg-card-hover: rgba(48, 48, 46, 0.62); + --glass-bg-input: rgba(34, 34, 32, 0.45); + --glass-bg-toolbar: rgba(40, 40, 38, 0.55); + --glass-tint-strength: 8%; } /* ================================================================ @@ -514,10 +537,15 @@ --glass-bg: var(--color-surface); --glass-bg-hover: var(--color-surface-2); --glass-bg-elevated: var(--color-surface); + --glass-bg-card: var(--color-surface); + --glass-bg-card-hover: var(--color-surface-2); + --glass-bg-input: var(--color-surface); + --glass-bg-toolbar: var(--color-surface); --glass-border: var(--color-border); --glass-border-subtle: var(--color-border-subtle); --glass-highlight: transparent; --glass-highlight-subtle: transparent; + --glass-tint-strength: 0%; --blur-xs: blur(0px); --blur-sm: blur(0px); --blur-md: blur(0px);