Files
oikos/public/styles/glass.css
T
Ulas 37a783b9c7 fix: resolve CSS load-order conflicts between glass.css and module stylesheets
- dashboard.css: move .widget glass styles (shadow, border, ::before highlight)
  directly here since module CSS loads after glass.css and would override it
- tasks.css: move .filter-chip--active glass state and fix .priority-badge
  border-radius to use --radius-glass-chip (capsule) instead of --radius-xs
- glass.css: remove dead .sticky-header rule (class not used in HTML) and
  remove duplicate .widget rules now handled by dashboard.css
2026-04-13 21:12:09 +02:00

582 lines
19 KiB
CSS

/**
* Modul: Liquid Glass - Shell Components (Phase 1)
* Zweck: Glass-Effekte für Navigation, Modal, Buttons, FAB
* Rein additiv: überschreibt nur optische Eigenschaften,
* keine Layout-, Größen- oder Funktionsänderungen.
* Abhängigkeiten: tokens.css (Section 16 Glass-Tokens), layout.css
*
* Aufbau:
* 1. Bottom Navigation
* 2. Sidebar (Desktop)
* 3. Modal (Overlay + Panel)
* 4. Buttons (Capsule + Specular)
* 5. FAB
* 6. Karten (hover-only)
* 7. Reduced-motion / Reduced-transparency Overrides
*/
/* ================================================================
* 1. Bottom Navigation — Glass Bar
*
* Upgrade von color-mix-Fallback auf --glass-* Tokens.
* Bereits vorhandenes backdrop-filter wird auf Tokens umgestellt.
* ================================================================ */
@supports (backdrop-filter: blur(1px)) {
.nav-bottom {
background-color: var(--glass-bg);
backdrop-filter: var(--blur-md) saturate(180%);
-webkit-backdrop-filter: var(--blur-md) saturate(180%);
border-top: 1px solid var(--glass-border);
box-shadow: var(--glass-shadow-sm);
}
}
/* Active-State: Konzentrische 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 (falls Maus vorhanden) */
@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
*
* backdrop-filter saturate() gibt dem Sidebar-Hintergrund einen
* leichten Farbboost vom Content. blur() wirkt auf überlappenden
* Content beim Scrollen in engen Layouts.
* ================================================================ */
@media (min-width: 1024px) {
@supports (backdrop-filter: blur(1px)) {
.nav-sidebar {
background: var(--glass-bg-elevated);
backdrop-filter: var(--blur-sm) saturate(160%);
-webkit-backdrop-filter: var(--blur-sm) saturate(160%);
border-right: 1px solid var(--glass-border);
box-shadow: var(--glass-shadow-md);
}
}
/* Active-State in Sidebar: Glass Pill mit Akzentlinie */
.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
*
* Overlay: leichter Page-Blur ("Tiefenschärfe"-Effekt) +
* reduziertes Dimming (Glass braucht weniger Overlay-Dunkel).
* Panel: Glass-Hintergrund mit --glass-bg-elevated + leichtem blur.
* Header: Sticky-Header im Panel bekommt identisches Glass-BG.
* ================================================================ */
@supports (backdrop-filter: blur(1px)) {
.modal-overlay {
/* Weniger Dimming, dafür Page-Blur für Tiefeneffekt */
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);
border: 1px solid var(--glass-border);
box-shadow: var(--glass-shadow-lg);
}
/* Sticky-Header: identisches Glass-BG damit beim Scrollen
der Blur-Layer nicht durch den Inhalt bricht */
.modal-panel__header {
background: var(--glass-bg-elevated);
border-bottom-color: var(--glass-border);
}
}
/* Bottom-Sheet Handle: etwas prominenter auf Glass */
@media (max-width: 767px) {
@supports (backdrop-filter: blur(1px)) {
.modal-panel::before {
background: var(--glass-border);
}
}
}
/* ================================================================
* 4. Buttons — Capsule Shape + Specular Highlight
*
* Primär-Button: Kapsel-Radius (--radius-glass-button = full)
* + subtiles Specular-Highlight (inset box-shadow oben)
* Sekundär-Button: leichte Glass-Border-Verfeinerung
* ================================================================ */
.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 bekommen beim Hover ein leichtes Glass-Pill */
@supports (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 (Floating Action Button) — Specular Highlight
*
* Inset box-shadow erzeugt Specular-Highlight-Illusion (Lichtquelle
* von oben) ohne Canvas-Manipulation oder SVG-Filter.
* ================================================================ */
.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)
*
* .card--interactive bekommt beim Hover einen Glass-Lift:
* leichter Blur + Glass-Border + erhöhter Schatten.
* Nicht auf Touch-Geräten (hover: hover).
* ================================================================ */
@media (hover: hover) {
@supports (backdrop-filter: blur(1px)) {
.card--interactive:hover {
background: var(--glass-bg-hover);
backdrop-filter: var(--blur-xs);
-webkit-backdrop-filter: var(--blur-xs);
border: 1px solid var(--glass-border-subtle);
box-shadow: var(--glass-shadow-md);
}
}
}
/* ================================================================
* 7. Accessibility Overrides
*
* prefers-reduced-transparency: Tokens aus tokens.css schalten
* backdrop-filter bereits ab. Hier zusätzliche Sicherheit für
* Elemente, die ohne @supports-Guard arbeiten (Specular-Highlights).
*
* prefers-reduced-motion: Alle Glass-Transitionen deaktivieren.
* (Animationen in layout.css mit animation: none bereits gedeckt,
* hier nur Glass-spezifische Transitions.)
* ================================================================ */
@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
*
* Kein backdrop-filter auf Inputs (würde Inhalte hinter dem Input
* in Modals unattraktiv unscharf rendern). Stattdessen: subtile
* Glass-Border + abgestufter Focus-Glow via CSS-Shadow.
* ================================================================ */
.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);
}
/* ================================================================
* 10. Toasts — Glass + Capsule
*
* Standard-Toast: Dark-Glass (neutrales --neutral-800 bleibt als
* Fallback). Farbige Toasts (success/danger/warning) bekommen nur
* den Capsule-Radius.
* ================================================================ */
.toast {
border-radius: var(--radius-glass-card);
box-shadow: var(--glass-shadow-md);
}
@supports (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);
}
}
/* Farbige Toasts: nur Capsule + specular, kein Blur */
.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);
}
/* ================================================================
* 11. Filter-Chips — Glass Active State
*
* Inaktiver Chip: glass border (subtil).
* Aktiver Chip: voller Glass-Pip mit backdrop-filter + Accent-Tint.
* ================================================================ */
.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);
}
@supports (backdrop-filter: blur(1px)) {
.filter-chip--active {
background: color-mix(in srgb, var(--color-accent) 18%, transparent);
backdrop-filter: var(--blur-xs);
-webkit-backdrop-filter: var(--blur-xs);
border-color: color-mix(in srgb, var(--color-accent) 50%, transparent);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.20);
}
}
/* ================================================================
* 12. Priority Badges — Capsule Radius + Glass Border
*
* Upgradet --radius-xs auf --radius-glass-chip (capsule) für einen
* moderneren Look. Behält semantic colors bei.
* ================================================================ */
.priority-badge {
border-radius: var(--radius-glass-chip);
border: 1px solid color-mix(in srgb, currentColor 25%, transparent);
}
/* Nav-Badge (Overdue-Indikator) */
.nav-badge {
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.20);
}
/* ================================================================
* 13. Toggle-Switch — Specular Thumb
*
* Der weiße Thumb bekommt einen subtilen inset-Schatten der eine
* Specular-Highlight-Illusion erzeugt (Lichtquelle von oben-rechts).
* Die Animation verwendet bereits --ease-glass cubic-bezier.
* ================================================================ */
.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);
}
/* ================================================================
* 14. Dashboard-Widgets — Glass Cards
*
* Glass-Styles (border, shadow, ::before highlight) direkt in
* dashboard.css, da dieses Modul-CSS nach glass.css geladen wird
* und sonst den box-shadow überschreiben würde.
* ================================================================ */
/* ================================================================
* 15. FAB-Backdrop (Dashboard) — Glass Blur
*
* Der Overlay-Hintergrund beim Dashboard-FAB-Menü bekommt einen
* dezenten Blur-Effekt für mehr Tiefe.
* ================================================================ */
@supports (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);
}
}
/* ================================================================
* 16. Search-Inputs (Modul-Toolbars)
*
* Modul-CSS wird on-demand nach glass.css geladen. Glass-Styles für
* modul-spezifische Search-Inputs stehen daher in den jeweiligen
* Modul-CSS-Dateien (contacts.css, notes.css, shopping.css).
* Hier werden nur token-basierte Fallback-Regeln für generische Inputs
* innerhalb von Modals definiert (via .modal-panel .form-input).
* ================================================================ */
.modal-panel .form-input,
.modal-panel .input {
background-color: var(--color-surface-2);
}
/* ================================================================
* 17. Accessibility — Phase 2 Overrides
* ================================================================ */
@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;
}
.sticky-header {
background-color: var(--color-bg);
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
* ================================================================ */
/* ================================================================
* 18. Bottom-Nav Auto-Hide on Scroll
*
* .nav-bottom--hidden: Nav gleitet nach unten aus dem Viewport.
* Per JS über scroll-Listener auf .app-content gesetzt.
* FAB bleibt sichtbar (Daumen-Erreichbarkeit wichtiger als Konsistenz).
* ================================================================ */
.nav-bottom {
transition:
transform var(--transition-base) var(--ease-out),
background-color var(--transition-fast),
box-shadow var(--transition-fast);
will-change: transform;
}
.nav-bottom--hidden {
transform: translateY(calc(100% + var(--safe-area-inset-bottom)));
pointer-events: none;
}
/* ================================================================
* 19. Modal-Entrance — Glass Spring
*
* Overlay: Blur-Ramp statt harten Fade (backdrop-filter via opacity).
* Panel Desktop: Spring-Overshooting statt linearem scale-in.
* Panel Mobile: Spring-Slide-up mit leichtem Bounce.
* ================================================================ */
@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;
}
}
/* ================================================================
* 20. Seitentransitionen — Spring Easing
*
* Ersetzt die festen 0.2s ease durch --transition-base + --ease-glass
* für eine weichere, physikalisch plausible Bewegung.
* ================================================================ */
.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);
}
/* ================================================================
* 21. List-Stagger — Spring Timing + erhöhter Offset
*
* Leicht längere Laufzeit + spring-Easing für einen
* physikalischeren Erscheinungseffekt.
* ================================================================ */
.list-stagger > * {
animation-duration: 0.28s;
animation-timing-function: var(--ease-glass);
}
/* ================================================================
* 22. Focus-Ring — Scale-In Animation
*
* Fokus-Indikator erscheint mit sanftem Scale-Pop statt hart.
* Kein outline-transition (outline ist nicht transition-fähig),
* daher box-shadow als Alternative für focus-visible.
* ================================================================ */
:focus-visible {
transition:
box-shadow var(--transition-fast),
outline-offset var(--transition-fast);
outline-offset: 3px;
}
/* ================================================================
* 23. Skeleton Shimmer — Glass Shimmer
*
* Helleres, richtungstreues Shimmer mit glass-artiger Lichtquelle.
* Behält bestehende Animation, verbessert nur den Farbverlauf.
* ================================================================ */
.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%;
}
/* ================================================================
* 24. FAB — Attention Pulse (subtil, einmalig beim Erscheinen)
*
* Leichter Ring-Expand nach dem fab-in, signalisiert Interaktivität.
* Nur einmal, kein dauerhaftes Blinken (kein infinite loop).
* ================================================================ */
@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;
}
/* ================================================================
* 25. Accessibility — Phase 3 Overrides
* ================================================================ */
@media (prefers-reduced-motion: reduce) {
.nav-bottom {
transition: none;
}
.nav-bottom--hidden {
/* Sofort verstecken ohne Animation */
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;
}
}