Files
oikos/public/styles/glass.css
T
Ulas a7ac7d105c fix: remove will-change from nav-bottom and add iOS height fallback
will-change: transform on a position:relative flex child causes iOS WebKit
to composite the element at an incorrect position - creating a visible gap
below the nav bar. Remove it; CSS transform transitions use hardware
acceleration automatically on modern iOS without this hint.

Add -webkit-fill-available before 100dvh on .app-shell as a fallback for
iOS WebKit versions where 100dvh is computed slightly smaller than the
actual WKWebView height.
2026-04-16 12:48:43 +02:00

779 lines
24 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Modul: Liquid Glass - Shell Components (Phase 13)
* Zweck: Glass-Effekte für Navigation, Modal, Buttons, FAB, Animationen
* Rein additiv: überschreibt nur optische Eigenschaften,
* keine Layout-, Größen- oder Funktionsänderungen.
* Abhängigkeiten: tokens.css (Section 16 Glass-Tokens), layout.css
*
* Architektur:
* Nicht-Blur-Stile (background, border, shadow) sind AUSSERHALB von
* @supports gesetzt → wirken auf allen Geräten und Browsern.
* Blur-Filter (backdrop-filter) sind INNERHALB von @supports mit
* webkit-Fallback → greifen wenn vom Browser unterstützt.
* @supports-Check: (backdrop-filter) OR (-webkit-backdrop-filter)
* deckt Safari < 18 (nur webkit-Prefix) und moderne Browser ab.
*/
/* ================================================================
* 1. Bottom Navigation — Glass Bar
* ================================================================ */
/* Immer aktiv: Glass-Hintergrund, Border, Shadow */
.nav-bottom {
background-color: var(--glass-bg);
border-top: 1px solid var(--glass-border);
box-shadow: var(--glass-shadow-sm);
}
/* Blur nur wenn unterstützt (webkit-Fallback für Safari < 18) */
@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
.nav-bottom {
backdrop-filter: var(--blur-md) saturate(180%);
-webkit-backdrop-filter: var(--blur-md) saturate(180%);
}
}
/* Active-State: Glass-Kapsel hinter aktivem Nav-Item */
.nav-item[aria-current="page"] {
background: color-mix(in srgb, var(--active-module-accent, var(--color-accent)) 14%, transparent);
border-radius: var(--radius-glass-chip);
box-shadow: inset 0 1px 0 var(--glass-highlight-subtle);
}
/* Hover-State auf Desktop */
@media (hover: hover) {
.nav-item:hover:not([aria-current="page"]) {
background: color-mix(in srgb, var(--color-text-tertiary) 8%, transparent);
border-radius: var(--radius-glass-chip);
}
}
/* ================================================================
* 2. Sidebar (Desktop ≥ 1024px) — Glass Panel
* ================================================================ */
@media (min-width: 1024px) {
.nav-sidebar {
background: var(--glass-bg-elevated);
border-right: 1px solid var(--glass-border);
box-shadow: var(--glass-shadow-md);
}
@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
.nav-sidebar {
backdrop-filter: var(--blur-sm) saturate(160%);
-webkit-backdrop-filter: var(--blur-sm) saturate(160%);
}
}
/* Active-State in Sidebar */
.nav-sidebar .nav-item[aria-current="page"] {
background: color-mix(in srgb, var(--active-module-accent, var(--color-accent)) 12%, transparent);
border-radius: var(--radius-sm);
box-shadow: inset 0 1px 0 var(--glass-highlight-subtle);
}
}
/* ================================================================
* 3. Modal — Glass Overlay + Glass Panel
* ================================================================ */
/* Immer aktiv: stärkerer Schatten und Glass-Border */
.modal-panel {
border: 1px solid var(--glass-border);
box-shadow: var(--glass-shadow-lg);
}
.modal-panel__header {
border-bottom-color: var(--glass-border);
}
/* Blur nur wenn unterstützt */
@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
.modal-overlay {
background-color: rgba(0, 0, 0, 0.30);
backdrop-filter: var(--blur-xs) saturate(120%);
-webkit-backdrop-filter: var(--blur-xs) saturate(120%);
}
.modal-panel {
background: var(--glass-bg-elevated);
backdrop-filter: var(--blur-sm);
-webkit-backdrop-filter: var(--blur-sm);
}
.modal-panel__header {
background: var(--glass-bg-elevated);
}
}
/* Bottom-Sheet Handle */
@media (max-width: 767px) {
@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
.modal-panel::before {
background: var(--glass-border);
}
}
}
/* ================================================================
* 4. Buttons — Capsule Shape + Specular Highlight
* ================================================================ */
.btn--primary {
border-radius: var(--radius-glass-button);
box-shadow:
var(--shadow-sm),
inset 0 1px 0 rgba(255, 255, 255, 0.22);
}
.btn--primary:hover {
box-shadow:
var(--shadow-md),
inset 0 1px 0 rgba(255, 255, 255, 0.18);
}
.btn--secondary {
border-radius: var(--radius-glass-button);
}
/* Ghost-Buttons */
@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
.btn--ghost:hover {
background: color-mix(in srgb, var(--color-text-tertiary) 10%, transparent);
backdrop-filter: var(--blur-xs);
-webkit-backdrop-filter: var(--blur-xs);
}
}
/* ================================================================
* 5. FAB — Specular Highlight
* ================================================================ */
.fab {
box-shadow:
var(--shadow-lg),
inset 0 1px 0 rgba(255, 255, 255, 0.28),
inset 0 -1px 0 rgba(0, 0, 0, 0.12);
}
.fab:hover {
box-shadow:
var(--shadow-lg),
inset 0 1px 0 rgba(255, 255, 255, 0.32),
inset 0 -1px 0 rgba(0, 0, 0, 0.16);
}
/* ================================================================
* 6. Cards — Glass auf hover (nur interaktive Cards)
* ================================================================ */
@media (hover: hover) {
.card--interactive:hover {
border: 1px solid var(--glass-border-subtle);
box-shadow: var(--glass-shadow-md);
}
@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
.card--interactive:hover {
background: var(--glass-bg-hover);
backdrop-filter: var(--blur-xs);
-webkit-backdrop-filter: var(--blur-xs);
}
}
}
/* ================================================================
* 7. Accessibility Overrides — Phase 1
* ================================================================ */
@media (prefers-reduced-transparency: reduce) {
.nav-item[aria-current="page"],
.nav-item:hover:not([aria-current="page"]) {
box-shadow: none;
}
.btn--primary,
.btn--primary:hover,
.fab,
.fab:hover {
box-shadow: var(--shadow-sm);
}
}
@media (prefers-reduced-motion: reduce) {
.nav-item,
.nav-sidebar .nav-item,
.btn,
.fab,
.card--interactive {
transition-duration: 0.01ms !important;
}
}
/* ================================================================
* PHASE 2 — Modul-Komponenten
* ================================================================ */
/* ================================================================
* 8. Form-Inputs — Glass Border + verbesserter Focus-Ring
* ================================================================ */
.input,
.form-input,
select.form-input,
textarea.form-input {
border-color: var(--glass-border-subtle);
background-color: color-mix(in srgb, var(--color-surface) 95%, transparent);
}
.input:focus,
.form-input:focus {
border-color: var(--color-accent);
box-shadow:
0 0 0 3px color-mix(in srgb, var(--color-accent) 20%, transparent),
inset 0 1px 2px rgba(0, 0, 0, 0.04);
}
/* ================================================================
* 9. Toasts — Glass + Capsule
* ================================================================ */
.toast {
border-radius: var(--radius-glass-card);
box-shadow: var(--glass-shadow-md);
}
@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
.toast:not(.toast--success):not(.toast--danger):not(.toast--warning) {
background-color: color-mix(in srgb, var(--neutral-800) 90%, transparent);
backdrop-filter: var(--blur-sm);
-webkit-backdrop-filter: var(--blur-sm);
border: 1px solid rgba(255, 255, 255, 0.10);
}
}
.toast--success,
.toast--danger,
.toast--warning {
border-radius: var(--radius-glass-card);
box-shadow:
var(--glass-shadow-md),
inset 0 1px 0 rgba(255, 255, 255, 0.18);
}
/* ================================================================
* 10. Filter-Chips — Glass Border (immer aktiv)
* Aktiver Glass-State: direkt in tasks.css (Load-Order-Konflikt)
* ================================================================ */
.filter-chip {
border-color: var(--glass-border-subtle);
}
.filter-chip:hover:not(.filter-chip--active) {
border-color: var(--glass-border);
background-color: color-mix(in srgb, var(--color-text-tertiary) 8%, transparent);
}
/* ================================================================
* 11. Priority Badges — Capsule + Glass Border
* Immer aktiv (kein backdrop-filter nötig)
* ================================================================ */
.nav-badge {
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.20);
}
/* ================================================================
* 12. Toggle-Switch — Specular Thumb
* ================================================================ */
.toggle__track::after {
box-shadow:
var(--shadow-sm),
inset 0 1px 0 rgba(255, 255, 255, 0.90),
inset 0 -1px 0 rgba(0, 0, 0, 0.08);
}
.toggle input:checked + .toggle__track::after {
transition: transform var(--transition-glass);
}
/* ================================================================
* 13. FAB-Backdrop (Dashboard) — Glass Blur
* ================================================================ */
@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
.fab-backdrop--visible {
backdrop-filter: var(--blur-xs);
-webkit-backdrop-filter: var(--blur-xs);
background: rgba(0, 0, 0, 0.18);
}
}
/* ================================================================
* 14. Dashboard-Widgets — Glass Cards
* Glass-Styles direkt in dashboard.css (Load-Order-Konflikt)
* ================================================================ */
/* ================================================================
* 15. Modal-Inputs
* ================================================================ */
.modal-panel .form-input,
.modal-panel .input {
background-color: var(--color-surface-2);
}
/* ================================================================
* 16. Accessibility — Phase 2
* ================================================================ */
@media (prefers-reduced-transparency: reduce) {
.filter-chip--active,
.toast:not(.toast--success):not(.toast--danger):not(.toast--warning) {
background-color: var(--color-accent-light);
border-color: var(--color-accent);
backdrop-filter: none;
-webkit-backdrop-filter: none;
}
}
@media (prefers-reduced-motion: reduce) {
.toggle__track::after {
transition: none;
}
.toast {
animation-duration: 0.01ms !important;
}
}
/* ================================================================
* PHASE 3 — Micro-Interactions + Polish
* ================================================================ */
/* ================================================================
* 17. Bottom-Nav Auto-Hide on Scroll
* ================================================================ */
.nav-bottom {
transition:
transform var(--transition-base) var(--ease-out),
background-color var(--transition-fast),
box-shadow var(--transition-fast);
/* will-change: transform absichtlich weggelassen - auf iOS (WebKit) korrumpiert die
* dauerhafte GPU-Layer-Promotion eines position:relative Flex-Kinds dessen Position. */
}
.nav-bottom--hidden {
transform: translateY(calc(100% + var(--safe-area-inset-bottom)));
pointer-events: none;
}
/* ================================================================
* 18. Modal-Entrance — Glass Spring
* ================================================================ */
@keyframes glass-modal-scale-in {
from {
opacity: 0;
transform: scale(0.90) translateY(8px);
}
to {
opacity: 1;
transform: scale(1) translateY(0);
}
}
@keyframes glass-sheet-in {
from {
opacity: 0.6;
transform: translateY(40%);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@media (min-width: 768px) {
.modal-panel {
animation: glass-modal-scale-in 0.32s var(--ease-glass) forwards;
}
}
@media (max-width: 767px) {
.modal-panel {
animation: glass-sheet-in 0.30s var(--ease-glass) forwards;
}
}
/* ================================================================
* 19. Seitentransitionen — Spring Easing
* ================================================================ */
.page-transition--in-right,
.page-transition--in-left {
animation-duration: 0.30s;
animation-timing-function: var(--ease-glass);
}
.page-transition--out-left,
.page-transition--out-right {
animation-duration: 0.14s;
animation-timing-function: var(--ease-out);
}
/* ================================================================
* 20. List-Stagger — Spring Timing
* ================================================================ */
.list-stagger > * {
animation-duration: 0.28s;
animation-timing-function: var(--ease-glass);
}
/* ================================================================
* 21. Focus-Ring — Scale-In Animation
* ================================================================ */
:focus-visible {
transition:
box-shadow var(--transition-fast),
outline-offset var(--transition-fast);
outline-offset: 3px;
}
/* ================================================================
* 22. Skeleton Shimmer — Glass Shimmer
* ================================================================ */
.skeleton {
background: linear-gradient(
105deg,
var(--color-border-subtle) 0%,
var(--color-border-subtle) 30%,
color-mix(in srgb, var(--color-surface) 90%, var(--glass-highlight)) 50%,
var(--color-border-subtle) 70%,
var(--color-border-subtle) 100%
);
background-size: 250% 100%;
}
/* ================================================================
* 23. FAB — Attention Pulse
* ================================================================ */
@keyframes fab-ring-pulse {
0% { box-shadow: var(--shadow-lg), inset 0 1px 0 rgba(255,255,255,0.28), 0 0 0 0 color-mix(in srgb, var(--module-accent, var(--color-btn-primary)) 50%, transparent); }
60% { box-shadow: var(--shadow-lg), inset 0 1px 0 rgba(255,255,255,0.28), 0 0 0 10px transparent; }
100% { box-shadow: var(--shadow-lg), inset 0 1px 0 rgba(255,255,255,0.28), 0 0 0 0 transparent; }
}
.fab {
animation:
fab-in 0.35s var(--ease-out) backwards,
fab-ring-pulse 0.6s var(--ease-out) 0.4s 1 backwards;
}
/* ================================================================
* 24. Accessibility — Phase 3
* ================================================================ */
@media (prefers-reduced-motion: reduce) {
.nav-bottom {
transition: none;
}
.nav-bottom--hidden {
transition: none;
}
.modal-panel {
animation: none;
}
.page-transition--in-right,
.page-transition--in-left,
.page-transition--out-left,
.page-transition--out-right {
animation-duration: 0.01ms !important;
}
.list-stagger > * {
animation-duration: 0.01ms !important;
}
.fab {
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;
}
}