diff --git a/assets/css/style.css b/assets/css/style.css index 591c198..95be709 100644 --- a/assets/css/style.css +++ b/assets/css/style.css @@ -4,10 +4,37 @@ --secondary-color: #f5f5f5; --text-color: #333; --border-color: #ddd; + + --fh-duration-fast: 0.18s; + --fh-duration-med: 0.28s; + --fh-ease-out: cubic-bezier(0.22, 1, 0.36, 1); + + --fh-shadow-tab: 0 2px 8px rgba(15, 23, 42, 0.12); + --fh-shadow-tab-md: 0 3px 12px rgba(15, 23, 42, 0.14); + + --fh-shadow-raised: + 0 1px 2px rgba(15, 23, 42, 0.06), + 0 4px 14px rgba(15, 23, 42, 0.06), + inset 0 1px 0 rgba(255, 255, 255, 0.55); + --fh-shadow-lift: + 0 4px 8px rgba(15, 23, 42, 0.08), + 0 12px 28px rgba(15, 23, 42, 0.1), + inset 0 1px 0 rgba(255, 255, 255, 0.55); } -body { - font-family: Arial, sans-serif; +@keyframes fh-content-in { + from { + opacity: 0; + transform: translateY(6px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +body.family-hub-body { + font-family: "Figtree", system-ui, -apple-system, sans-serif; line-height: 1.6; margin: 0; padding: 0; @@ -62,6 +89,36 @@ main { .persona-chip { min-height: 2.75rem; touch-action: manipulation; + transition: + background-color var(--fh-duration-fast) var(--fh-ease-out), + border-color var(--fh-duration-fast) var(--fh-ease-out), + color var(--fh-duration-fast) var(--fh-ease-out), + box-shadow var(--fh-duration-med) var(--fh-ease-out), + transform var(--fh-duration-med) var(--fh-ease-out); +} + +.persona-chip i { + display: inline-block; + transition: transform var(--fh-duration-med) var(--fh-ease-out); +} + +@media (hover: hover) and (pointer: fine) { + .persona-chip:not(:disabled):hover, + .persona-chip:not(:disabled):focus-visible { + transform: translateY(-1px); + box-shadow: var(--fh-shadow-tab); + } + + .persona-chip:not(:disabled):hover i, + .persona-chip:not(:disabled):focus-visible i { + transform: scale(1.08); + } + + .persona-chip.btn-light.active:hover, + .persona-chip.btn-light.active:focus-visible { + transform: translateY(-1px); + box-shadow: var(--fh-shadow-tab-md); + } } .persona-chip.active { @@ -69,13 +126,55 @@ main { box-shadow: 0 0 0 0.15rem rgba(255, 255, 255, 0.35); } +.persona-chip-mobile { + transition: + background-color var(--fh-duration-fast) var(--fh-ease-out), + transform var(--fh-duration-med) var(--fh-ease-out); +} + +.persona-chip-mobile i { + display: inline-block; + transition: transform var(--fh-duration-med) var(--fh-ease-out); +} + +@media (hover: hover) and (pointer: fine) { + .persona-chip-mobile:hover i, + .persona-chip-mobile:focus-visible i { + transform: scale(1.06); + } +} + .persona-chip-mobile.active { font-weight: 600; background-color: rgba(74, 144, 226, 0.14); } -.family-hub-body .card { - border-color: var(--border-color); +.app-header .app-header-actions .dropdown-toggle { + transition: + background-color var(--fh-duration-fast) var(--fh-ease-out), + box-shadow var(--fh-duration-med) var(--fh-ease-out), + transform var(--fh-duration-med) var(--fh-ease-out); +} + +@media (hover: hover) and (pointer: fine) { + .app-header .app-header-actions .dropdown-toggle:hover, + .app-header .app-header-actions .dropdown-toggle:focus-visible { + transform: translateY(-1px); + box-shadow: var(--fh-shadow-tab); + } +} + +.app-header-balance { + transition: + box-shadow var(--fh-duration-med) var(--fh-ease-out), + transform var(--fh-duration-med) var(--fh-ease-out); +} + +@media (hover: hover) and (pointer: fine) { + .app-header-balance:hover { + transform: translateY(-1px); + box-shadow: var(--fh-shadow-tab); + } } .chore-thumb { @@ -97,6 +196,21 @@ main { .grocery-store-nav .nav-link { text-align: left; touch-action: manipulation; + border-radius: 0.45rem; + transition: + background-color var(--fh-duration-fast) var(--fh-ease-out), + color var(--fh-duration-fast) var(--fh-ease-out), + box-shadow var(--fh-duration-med) var(--fh-ease-out), + transform var(--fh-duration-med) var(--fh-ease-out), + border-color var(--fh-duration-fast) var(--fh-ease-out); +} + +@media (hover: hover) and (pointer: fine) { + .grocery-store-nav .nav-link:hover, + .grocery-store-nav .nav-link:focus-visible { + transform: translateX(2px); + box-shadow: 0 2px 8px rgba(15, 23, 42, 0.08); + } } .grocery-line-thumb { @@ -111,6 +225,131 @@ main { padding: 20px; } +/* Main area: cards depth & accent border */ +.family-hub-body main .card { + border-radius: 0.55rem; + border-color: var(--border-color); + box-shadow: var(--fh-shadow-raised); + transition: + transform var(--fh-duration-med) var(--fh-ease-out), + box-shadow var(--fh-duration-med) var(--fh-ease-out), + border-color var(--fh-duration-fast) var(--fh-ease-out); +} + +@supports (color: color-mix(in srgb, #fff 50%, #000)) { + .family-hub-body main .card { + border-color: color-mix(in srgb, var(--person-accent, #4a90e2) 14%, var(--border-color)); + } +} + +@media (hover: hover) and (pointer: fine) { + .family-hub-body main .card:hover { + transform: translateY(-2px); + box-shadow: var(--fh-shadow-lift); + } +} + +@supports (color: color-mix(in srgb, #fff 50%, #000)) { + .family-hub-body main .card:focus-within { + border-color: color-mix(in srgb, var(--person-accent, #4a90e2) 28%, var(--border-color)); + box-shadow: + var(--fh-shadow-lift), + 0 0 0 3px color-mix(in srgb, var(--person-accent, #4a90e2) 32%, transparent); + outline: none; + } +} + +@supports not (color: color-mix(in srgb, #fff 50%, #000)) { + .family-hub-body main .card:focus-within { + box-shadow: var(--fh-shadow-lift); + outline: 2px solid var(--person-accent, #4a90e2); + outline-offset: 2px; + } +} + +/* Solid (non-subtle) colored card headers: drop inset highlight */ +.family-hub-body main .card:has(> .card-header.bg-dark), +.family-hub-body main .card:has(> .card-header[class*="bg-"]:not(.bg-light):not(.bg-white):not([class*="-subtle"])) { + box-shadow: + 0 1px 2px rgba(15, 23, 42, 0.08), + 0 4px 14px rgba(15, 23, 42, 0.07); +} + +@media (hover: hover) and (pointer: fine) { + .family-hub-body main .card:has(> .card-header.bg-dark):hover, + .family-hub-body main .card:has(> .card-header[class*="bg-"]:not(.bg-light):not(.bg-white):not([class*="-subtle"])):hover { + box-shadow: + 0 4px 8px rgba(15, 23, 42, 0.12), + 0 12px 24px rgba(15, 23, 42, 0.1); + } +} + +/* Tab panel entrance */ +@media (prefers-reduced-motion: no-preference) { + #dashboardContentArea.tab-content { + animation: fh-content-in 0.3s var(--fh-ease-out) both; + } +} + +/* Buttons (main only); flatten in button groups */ +.family-hub-body main .btn { + transition: + transform var(--fh-duration-fast) var(--fh-ease-out), + box-shadow var(--fh-duration-fast) var(--fh-ease-out), + background-color var(--fh-duration-fast) var(--fh-ease-out), + border-color var(--fh-duration-fast) var(--fh-ease-out), + color var(--fh-duration-fast) var(--fh-ease-out); +} + +@media (hover: hover) and (pointer: fine) { + .family-hub-body main .btn:hover:not(:disabled):not(.disabled) { + transform: translateY(-1px); + box-shadow: 0 3px 10px rgba(15, 23, 42, 0.12); + } +} + +.family-hub-body main .btn:focus-visible:not(:disabled):not(.disabled) { + transform: translateY(-1px); +} + +.family-hub-body main .btn:active:not(:disabled):not(.disabled) { + transform: scale(0.98); + box-shadow: none; +} + +.family-hub-body main .btn-group .btn, +.family-hub-body main .btn-group-vertical .btn { + box-shadow: none; +} + +@media (hover: hover) and (pointer: fine) { + .family-hub-body main .btn-group .btn:hover:not(:disabled), + .family-hub-body main .btn-group .btn:focus-visible:not(:disabled), + .family-hub-body main .btn-group-vertical .btn:hover:not(:disabled), + .family-hub-body main .btn-group-vertical .btn:focus-visible:not(:disabled) { + transform: none; + z-index: 2; + } +} + +.family-hub-body main .btn-group .btn:active:not(:disabled), +.family-hub-body main .btn-group-vertical .btn:active:not(:disabled) { + transform: scale(0.98); +} + +.family-hub-body main .badge { + transition: + transform var(--fh-duration-fast) var(--fh-ease-out), + box-shadow var(--fh-duration-fast) var(--fh-ease-out); +} + +@media (hover: hover) and (pointer: fine) { + .family-hub-body main a.badge:hover, + .family-hub-body main a.badge:focus-visible { + transform: scale(1.03); + } +} + /* Header tab nav */ .app-header.app-header-with-tabs .tabs { display: flex; @@ -140,7 +379,43 @@ main { border: 1px solid transparent; text-decoration: none; font-weight: 500; - transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease; + transition: + background-color var(--fh-duration-fast) var(--fh-ease-out), + color var(--fh-duration-fast) var(--fh-ease-out), + border-color var(--fh-duration-fast) var(--fh-ease-out), + box-shadow var(--fh-duration-med) var(--fh-ease-out), + transform var(--fh-duration-med) var(--fh-ease-out); +} + +.app-header .tab i { + display: inline-block; + transition: transform var(--fh-duration-med) var(--fh-ease-out); +} + +@media (hover: hover) and (pointer: fine) { + body.header-tone-dark .app-header .tab:hover, + body.header-tone-dark .app-header .tab:focus-visible { + transform: translateY(-1px); + box-shadow: var(--fh-shadow-tab); + } + + body.header-tone-light .app-header .tab:hover, + body.header-tone-light .app-header .tab:focus-visible { + transform: translateY(-1px); + box-shadow: var(--fh-shadow-tab); + } + + .app-header .tab:hover i, + .app-header .tab:focus-visible i { + transform: scale(1.08); + } +} + +@media (min-width: 768px) and (hover: hover) and (pointer: fine) { + .app-header .tab:hover, + .app-header .tab:focus-visible { + transform: translateY(-1px) scale(1.02); + } } body.header-tone-dark .app-header .tab { @@ -237,3 +512,13 @@ body.header-tone-light .app-header .tab.active { min-height: 78vh; } } + +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + } +} diff --git a/includes/header.php b/includes/header.php index eddfc4d..409714d 100644 --- a/includes/header.php +++ b/includes/header.php @@ -5,6 +5,10 @@