From d91d2fa35dd067c7035b06d326026477ea111996 Mon Sep 17 00:00:00 2001 From: Aditya Tripathi Date: Mon, 7 Sep 2026 02:35:56 +0000 Subject: [PATCH] feat(ui): WCAG contrast, neutral oklch surface system, and mobile nav overhaul Design tokens - Fix WCAG AA text failures: darken light muted text and lighten dark tertiary text so 13-14px copy clears 4.5:1 on every surface. - Rebuild the dark surface ladder in correct values (oklch compresses to near-black below ~15%), giving visible steps: content, chrome, cards. - Establish a 3-layer shell (deep content, lighter sidebar/topbar chrome, lifted cards) with crisp hairline rings and a restrained card shadow. - Unify every surface to one neutral temperature (pure gray); neutralize the cool-tinted sidebar text and legacy surface tokens. - Convert all color tokens to oklch (neutrals + brand). - Unify inputs to the recessed token; fix invisible dark placeholders. - One shared --shadow-dropdown for all menus/listboxes/command palette. Settings sidebar - Sticky contained-card rail with subtle scrollbar; collapsible sub-section groups (active open by default), cross-page section links. - Mobile: collapsible disclosure with animated open, outside/Escape dismiss, press feedback, and reduced-motion support. Mobile - Consolidate the resource header (title + status + links on one row). - Tighten settings-page vertical rhythm. - Rebuild the main sidebar as an animated shadcn-style sheet (slide/fade, Escape, scroll lock, in-panel close, close-on-navigate). Fixes - #11532: selected server invisible in the light-mode terminal toolbar. Docs: update DESIGN.md tokens, shell layering, and temperature rules. --- DESIGN.md | 48 +++- resources/css/app.css | 231 ++++++++++++------ resources/css/utilities.css | 4 +- resources/js/app.js | 13 + .../configuration-sidebar.blade.php | 122 ++++++--- .../components/boarding-progress.blade.php | 2 +- resources/views/components/dropdown.blade.php | 2 +- .../resources/breadcrumbs.blade.php | 6 +- .../server/provider-token-picker.blade.php | 2 +- .../terminal/theme-selector.blade.php | 2 +- .../components/upgrade-progress.blade.php | 2 +- resources/views/layouts/app.blade.php | 54 ++-- resources/views/layouts/simple.blade.php | 2 +- .../views/livewire/boarding/index.blade.php | 22 +- resources/views/livewire/dashboard.blade.php | 4 +- .../dashboard/active-deployments.blade.php | 8 +- .../livewire/destination/index.blade.php | 6 +- .../livewire/destination/resources.blade.php | 2 +- .../views/livewire/global-search.blade.php | 10 +- .../livewire/profile/appearance.blade.php | 4 +- .../views/livewire/profile/index.blade.php | 2 +- .../livewire/project/add-empty.blade.php | 2 +- .../application/configuration.blade.php | 4 +- .../project/application/general.blade.php | 2 +- .../project/application/heading.blade.php | 7 +- .../project/application/source.blade.php | 8 +- .../project/database/import-form.blade.php | 10 +- .../database/scheduled-backups.blade.php | 2 +- .../views/livewire/project/index.blade.php | 10 +- .../livewire/project/new/select.blade.php | 8 +- .../livewire/project/resource/index.blade.php | 10 +- .../project/service/configuration.blade.php | 4 +- .../project/service/edit-compose.blade.php | 2 +- .../execute-container-command.blade.php | 4 +- .../volume-backups/executions.blade.php | 4 +- .../storages/volume-backups/general.blade.php | 2 +- .../views/livewire/project/show.blade.php | 10 +- .../security/cloud-init-scripts.blade.php | 2 +- .../cloud-provider-token-form.blade.php | 2 +- .../security/cloud-provider-tokens.blade.php | 2 +- .../security/private-key/index.blade.php | 4 +- .../views/livewire/server/create.blade.php | 8 +- .../views/livewire/server/index.blade.php | 8 +- .../server/validate-and-install.blade.php | 2 +- .../environment/index.blade.php | 4 +- .../livewire/shared-variables/index.blade.php | 8 +- .../shared-variables/project/index.blade.php | 4 +- .../shared-variables/server/index.blade.php | 4 +- .../shared/list-search-empty.blade.php | 2 +- .../livewire/source/github/change.blade.php | 2 +- .../source/github/resources.blade.php | 2 +- .../livewire/source/gitlab/change.blade.php | 2 +- .../views/livewire/storage/index.blade.php | 6 +- .../livewire/storage/resources.blade.php | 2 +- .../subscription/pricing-plans.blade.php | 2 +- .../views/livewire/tags/deployments.blade.php | 2 +- resources/views/livewire/tags/show.blade.php | 22 +- .../views/livewire/terminal/index.blade.php | 4 +- resources/views/source/all.blade.php | 8 +- 59 files changed, 467 insertions(+), 272 deletions(-) diff --git a/DESIGN.md b/DESIGN.md index 11048ad5d..4e42ec404 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -34,7 +34,9 @@ ## 1. Visual direction - near-neutral layered surfaces instead of large bordered boxes; - 13–14px UI typography and 32px controls; -- hairline rings instead of heavy borders; +- crisp hairline rings plus a restrained card lift (single 1px ring + + `0 1px 2px rgb(0 0 0 / 0.05)`) so cards and tables separate from the canvas, + never heavy borders or a strong floating shadow; - full-width data tables for dense collections; - outline Reicon glyphs through ``; - the Coolify purple brand accent in light mode; @@ -89,14 +91,14 @@ ## 3. Tokens and color behavior | Token | Light | Dark | Use | |---|---|---|---| -| `--coollabs-canvas` | near white | 10% neutral | page canvas | +| `--coollabs-canvas` | 97% off-white | 10% neutral | page canvas (kept below card fills so cards lift) | | `--coollabs-elevated` | 98% neutral | 15% neutral | shells and card headers | | `--coollabs-base` | white | 17% neutral | nested card bodies | | `--coollabs-recessed` | 96% neutral | 20% neutral | inputs and listboxes | | `--coollabs-fill` | 92.2% neutral | 26.9% neutral | dividers and passive fills | | `--coollabs-line` | translucent dark | 32% neutral | control borders | -| `--coollabs-hairline` | 93.5% neutral | 26.9% neutral | shell rings | -| `--coollabs-subtle` | 55.6% neutral | 70.8% neutral | labels and muted titles | +| `--coollabs-hairline` | 85.5% neutral | 32% neutral | shell rings (crisp enough to read as a card edge, ~1.5:1) | +| `--coollabs-subtle` | 50% neutral | 70.8% neutral | labels and muted titles (light darkened for WCAG AA 4.5:1) | Accent behavior is intentionally theme-aware: @@ -116,6 +118,38 @@ ## 3. Tokens and color behavior The filled top-level action/tab treatment uses the same palette at a restrained opacity rather than a fully saturated fill. +### Shell layering + +The app shell is three distinct surface layers, not one flat color. Chrome +lifts, content is the base, cards lift off the content: + +- **Content canvas** is the base layer: `bg-app` in dark (deepest, + `--color-app` `#0a0a0b`), `bg-gray-50` in light. The `
` content area + and page body use it. +- **Sidebar and topbar chrome** use `bg-panel` in dark (`--color-panel` + `#141418`, a clear step lighter than the content canvas) and `bg-white` in + light, so the chrome reads as a separate panel from the content. + +Dark surface tokens are hex, not oklch. oklch lightness compresses toward pure +black below ~15% (oklch(10%) renders as sRGB 3, oklch(15%) as sRGB 11), so oklch +values there give no visible step between layers. The dark ladder is +`--color-app` 10, `--coollabs-elevated` 22, `--coollabs-base` 28, +`--coollabs-recessed` 34 (sRGB), which reads as distinct surfaces. + +Temperature: every panel is **pure neutral gray** (r=g=b), one consistent +temperature across the sidebar, tables, cards, inputs, dividers, borders, and +text, in both modes. Do not give one surface a cool (blue) or warm cast while +the others stay neutral. The light page canvas uses `bg-neutral-50` (not +`bg-gray-50`, which is faintly cool) so it matches the neutral cards and chrome. +The only intentional color is the purple/yellow brand accent. +- **Cards, tables, and collection tiles** lift off the content canvas with + `dark:bg-white/[0.05]` plus the crisp `--coollabs-hairline` ring; in light + they are `bg-white` with the ring and the restrained card lift. + +Do not paint the content area with the same `bg-panel` as the sidebar, and do +not drop card fills below `dark:bg-white/[0.05]`; both make surfaces read as one +color. Row-hover states keep the lighter `dark:hover:bg-white/[0.025]`. + --- ## 4. Page shells and navigation @@ -419,6 +453,12 @@ ### Dropdowns the option content left-aligned and size the panel to its content or trigger; do not create an unnecessarily wide menu. +Every dropdown, menu, listbox panel, and the command palette uses the shared +`--shadow-dropdown` token (`0 4px 12px rgb(0 0 0 / 0.12), 0 2px 4px +rgb(0 0 0 / 0.08)`) for a restrained, consistent lift. Do not hand-roll a +heavier `shadow-lg` / `0 18px 50px` / `0.45`-alpha drop shadow on a menu. +Reserve the stronger `--shadow-modal` for actual modals, dialogs, and toasts. + Toolbar filter and sort buttons keep static labels (`Filter`, `Sort`). The selected option is indicated inside the menu, not repeated on the trigger. diff --git a/resources/css/app.css b/resources/css/app.css index 199f904d0..f2e7d9951 100644 --- a/resources/css/app.css +++ b/resources/css/app.css @@ -20,51 +20,63 @@ @theme { --font-geist-sans: 'Geist Sans', Inter, sans-serif; --font-logs: 'Geist Mono', 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace; - --color-base: #101010; - --color-warning: #fcd452; - --color-warning-50: #fefce8; - --color-warning-100: #fef9c3; - --color-warning-200: #fef08a; - --color-warning-300: #fde047; - --color-warning-400: #fcd452; - --color-warning-500: #facc15; - --color-warning-600: #ca8a04; - --color-warning-700: #a16207; - --color-warning-800: #854d0e; - --color-warning-900: #713f12; - --color-success: #22C55E; - --color-error: #dc2626; - --color-coollabs-50: #f5f0ff; - --color-coollabs: #6b16ed; - --color-coollabs-100: #7317ff; - --color-coollabs-200: #5a12c7; - --color-coollabs-300: #4a0fa3; - --color-coolgray-100: #181818; - --color-coolgray-200: #202020; - --color-coolgray-300: #242424; - --color-coolgray-400: #282828; - --color-coolgray-500: #323232; + --color-base: oklch(17.3% 0 0); + --color-warning: oklch(88.13% 0.1507 91.7); + --color-warning-50: oklch(98.73% 0.0262 102.21); + --color-warning-100: oklch(97.29% 0.0693 103.19); + --color-warning-200: oklch(94.51% 0.1243 101.54); + --color-warning-300: oklch(90.52% 0.1657 98.11); + --color-warning-400: oklch(88.13% 0.1507 91.7); + --color-warning-500: oklch(86.06% 0.1731 91.94); + --color-warning-600: oklch(68.06% 0.1423 75.83); + --color-warning-700: oklch(55.38% 0.1207 66.44); + --color-warning-800: oklch(47.62% 0.1034 61.91); + --color-warning-900: oklch(42.1% 0.0897 57.71); + --color-success: oklch(72.27% 0.192 149.58); + --color-error: oklch(57.71% 0.2152 27.33); + --color-coollabs-50: oklch(96.33% 0.0206 301.15); + --color-coollabs: oklch(49.65% 0.2709 289.33); + --color-coollabs-100: oklch(52.34% 0.287 289.16); + --color-coollabs-200: oklch(43.76% 0.2369 289.82); + --color-coollabs-300: oklch(38.04% 0.2031 290.47); + --color-coolgray-100: oklch(20.9% 0 0); + --color-coolgray-200: oklch(24.35% 0 0); + --color-coolgray-300: oklch(26.03% 0 0); + --color-coolgray-400: oklch(27.68% 0 0); + --color-coolgray-500: oklch(31.71% 0 0); /* Graphite design language (ported from ref/frontend). Layered neutral surfaces + translucent hairlines. See DESIGN.md. */ - --color-app: #0c0c0d; - /* Canvas: neutral near-black (oklch), not pure black */ - --color-panel: oklch(10% 0 0); - --color-surface: #161618; - --color-raised: #1c1c1e; - --color-selected: #26262a; - --color-fg: #f2f2f2; - --color-fg-dim: #b4b4b8; - --color-fg-faint: #6e6e74; - --color-accent: #6b16ed; - --color-accent-foreground: #ffffff; - --color-hairline: rgba(255, 255, 255, 0.08); - --color-nav-text: #525252; - --color-nav-muted: #666666; - --color-nav-active: #171717; - --color-log: #0d0d0d; + /* Content canvas: the darkest shell layer (sRGB ~10). Hex, not oklch: + oklch lightness compresses to near-black below ~15%, so oklch(7.5%) + renders as sRGB 1 with no visible step. */ + --color-app: oklch(14.48% 0 0); + /* Sidebar + topbar chrome (sRGB ~20): a clear step lighter than the content + canvas so the shell chrome separates from the content area. */ + --color-panel: oklch(19.13% 0 0); + /* Pure neutral (r=g=b). Were cool-tinted (#161618/#1c1c1e/#26262a, b>r), + which clashed with the neutral panel ladder. */ + --color-surface: oklch(20.02% 0 0); + --color-raised: oklch(22.64% 0 0); + --color-selected: oklch(26.86% 0 0); + --color-fg: oklch(96.12% 0 0); + --color-fg-dim: oklch(76.99% 0 0); + /* Tertiary text. Raised from #6e6e74 (3.78:1 on base, failed WCAG AA) + to #7e7e84 so 13-14px text clears 4.5:1 on the dark surface ladder. */ + --color-fg-faint: oklch(59.31% 0 0); + --color-accent: oklch(49.65% 0.2709 289.33); + --color-accent-foreground: oklch(100.0% 0 0); + --color-hairline: oklch(100.0% 0 0 / 0.08); + --color-nav-text: oklch(43.86% 0 0); + --color-nav-muted: oklch(51.03% 0 0); + --color-nav-active: oklch(20.46% 0 0); + --color-log: oklch(15.91% 0 0); --shadow-modal: 0 24px 64px rgba(0, 0, 0, 0.55), 0 4px 16px rgba(0, 0, 0, 0.4); + /* One restrained lift shared by every dropdown/menu/listbox panel and the + command palette. Lighter and consistent, replacing the old scattered + 0.45 / 0.35 / shadow-lg / shadow-sm mix. Modals keep --shadow-modal. */ + --shadow-dropdown: 0 4px 12px rgb(0 0 0 / 0.12), 0 2px 4px rgb(0 0 0 / 0.08); } /* @@ -258,7 +270,7 @@ @layer components { */ html, body { - @apply w-full min-h-full bg-gray-50 dark:bg-app dark:text-fg-dim; + @apply w-full min-h-full bg-neutral-50 dark:bg-app dark:text-fg-dim; } body { @@ -505,8 +517,8 @@ .terminal-fullscreen-shell::before { .application-console-shell[data-console-theme="system"], .terminal-fullscreen-shell[data-console-theme="system"] { - --console-theme-background: #fff; - --console-theme-border: #d4d4d8; + --console-theme-background: oklch(100.0% 0 0); + --console-theme-border: oklch(87.11% 0.0055 286.29); --console-theme-opacity: 1; } @@ -517,13 +529,20 @@ html:not(.dark) .application-console-shell[data-console-theme="system"] .applica backdrop-filter: none; } -html:not(.dark) .application-console-shell[data-console-theme="system"] .application-console-header [class*="text-white"] { +html:not(.dark) .application-console-shell[data-console-theme="system"] .application-console-header [class*="text-white"], +/* Session toolbar (selected-target trigger + label) sits over the white + system console in light mode. Without this its text-white/* stayed white on + white and the selected server was invisible (issue #11532). */ +html:not(.dark) .application-console-shell[data-console-theme="system"] .terminal-session-toolbar[class*="text-white"], +html:not(.dark) .application-console-shell[data-console-theme="system"] .terminal-session-toolbar [class*="text-white"], +html:not(.dark) .terminal-fullscreen-shell[data-console-theme="system"] .terminal-session-toolbar[class*="text-white"], +html:not(.dark) .terminal-fullscreen-shell[data-console-theme="system"] .terminal-session-toolbar [class*="text-white"] { color: #52525b !important; } html.dark .application-console-shell[data-console-theme="system"], html.dark .terminal-fullscreen-shell[data-console-theme="system"] { - --console-theme-background: #121214; + --console-theme-background: oklch(18.31% 0.004 285.99); --console-theme-border: rgb(255 255 255 / 0.08); } @@ -851,7 +870,7 @@ html:not(.dark) .application-console-shell[data-console-theme="system"] .termina color: #52525b; background: #fff; border-color: #d4d4d8; - box-shadow: 0 18px 50px rgb(0 0 0 / 0.18); + box-shadow: var(--shadow-dropdown); } html:not(.dark) .application-console-shell[data-console-theme="system"] .terminal-target-picker [class*="text-white"] { @@ -924,37 +943,53 @@ .terminal-fullscreen-btn:hover { * utility classes placed in the card markup. */ :root { - --coollabs-canvas: oklch(98.75% 0 0); + /* Page canvas is a faint off-white so white/elevated cards lift off it + (shadcn pattern), instead of card and page reading as one flat color. */ + --coollabs-canvas: oklch(97% 0 0); --coollabs-elevated: oklch(98% 0 0); --coollabs-recessed: oklch(96% 0 0); - --coollabs-base: #ffffff; + --coollabs-base: oklch(100.0% 0 0); --coollabs-fill: oklch(92.2% 0 0); --coollabs-line: oklch(14.5% 0 0 / 0.1); - --coollabs-hairline: oklch(93.5% 0 0); - --coollabs-subtle: oklch(55.6% 0 0); + /* Card/table edge ring. Crisped from 93.5% (1.17:1 on canvas, invisible) + to 85.5% (1.51:1) so surfaces read as bordered cards, not flat fills. */ + --coollabs-hairline: oklch(85.5% 0 0); + /* Muted labels/titles. Darkened from 55.6% (failed WCAG AA on elevated + 4.46, recessed 4.24, fill 3.76) to 50% so muted text clears 4.5:1 on + every light surface above white. Dark stays 70.8% (already passes). */ + --coollabs-subtle: oklch(50% 0 0); } .dark { - --color-accent: #fcd452; - --coollabs-canvas: oklch(10% 0 0); - /* elevated (card shells/headers) and recessed (input fills) sit a touch - above the canvas so they read lighter than near-black */ - --coollabs-elevated: oklch(15% 0 0); - --coollabs-recessed: oklch(20% 0 0); - --coollabs-base: oklch(17% 0 0); - --coollabs-fill: oklch(26.9% 0 0); + --color-accent: oklch(88.13% 0.1507 91.7); + /* Dark surface ladder in hex (sRGB), not oklch: oklch lightness compresses + to near-black below ~15%, so oklch(15%) rendered as sRGB 11 and cards + could not lift off the content canvas (sRGB 10). These give clear, + visible steps: content 10 -> elevated 22 -> base 28 -> recessed 34. + Pure neutral (r=g=b), matching the neutral borders/text and the light + ladder, so every panel shares one temperature (no blue cast). */ + --coollabs-canvas: oklch(14.48% 0 0); + --coollabs-elevated: oklch(20.02% 0 0); + --coollabs-recessed: oklch(25.2% 0 0); + --coollabs-base: oklch(22.64% 0 0); + --coollabs-fill: oklch(29.31% 0 0); --coollabs-line: oklch(32% 0 0); - --coollabs-hairline: oklch(26.9% 0 0); + /* Crisped from 26.9% (1.36:1 on canvas) to 32% (1.63:1) so dark cards + and tables show a visible edge instead of blending into the canvas. */ + --coollabs-hairline: oklch(32% 0 0); --coollabs-subtle: oklch(70.8% 0 0); - --color-nav-text: #a8a8b0; - --color-nav-muted: #7a7a84; - --color-nav-active: #f2f2f2; + /* Neutral (was #a8a8b0 / #7a7a84, both cool-tinted). Sidebar text now shares + the same neutral temperature as the content text, so the sidebar no longer + reads cooler than the panels/cards. */ + --color-nav-text: oklch(73.8% 0 0); + --color-nav-muted: oklch(58.97% 0 0); + --color-nav-active: oklch(96.12% 0 0); } /* Theme surfaces are derived from one brand color. Changing --theme-base-color is enough to generate a complete dark surface ladder. */ html[data-theme="custom"] { - --theme-base-color: #6b16ed; + --theme-base-color: oklch(49.65% 0.2709 289.33); --theme-bright-color: color-mix(in srgb, var(--theme-base-color) 85%, white); --theme-scrollbar-thumb: color-mix(in srgb, var(--theme-bright-color) 70%, var(--theme-accent-foreground)); --theme-border-color: color-mix(in oklab, var(--theme-base-color) 42%, #52525b); @@ -985,14 +1020,14 @@ html[data-theme="custom"] { --coollabs-fill: color-mix(in oklab, var(--theme-base-color) 32%, #18181a); --coollabs-line: var(--theme-border-color); --coollabs-hairline: color-mix(in srgb, var(--theme-border-color) 55%, var(--color-panel)); - --color-nav-text: #e4dfea; - --color-nav-muted: #d8d2df; - --color-nav-active: #ffffff; + --color-nav-text: oklch(91.08% 0.0157 306.4); + --color-nav-muted: oklch(87.24% 0.0188 306.63); + --color-nav-active: oklch(100.0% 0 0); --color-log: color-mix(in oklab, var(--theme-base-color) 20%, #080809); --color-log-toolbar: color-mix(in oklab, var(--theme-base-color) 26%, #101011); - --color-fg-dim: #e4dfea; - --color-fg-faint: #d8d2df; - --coollabs-subtle: #d8d2df; + --color-fg-dim: oklch(91.08% 0.0157 306.4); + --color-fg-faint: oklch(87.24% 0.0188 306.63); + --coollabs-subtle: oklch(87.24% 0.0188 306.63); } html[data-theme="custom"] .control-selected, @@ -1349,7 +1384,9 @@ .application-settings-section { border-radius: 8px; border: none; background: var(--coollabs-elevated); - box-shadow: 0 0 0 1px var(--coollabs-hairline); + /* crisp ring + a restrained lift so the card separates from the canvas + (shadcn-style), not a heavy floating shadow */ + box-shadow: 0 0 0 1px var(--coollabs-hairline), 0 1px 2px 0 rgb(0 0 0 / 0.05); /* keep anchored scrolls (scrollIntoView) clear of the fixed topbar + layer-2 nav */ scroll-margin-top: 7rem; } @@ -1471,16 +1508,49 @@ .server-settings-workspace > :not(.application-settings-navigation) { @media (min-width: 1280px) { .application-settings-navigation { position: sticky; - top: calc(3rem + 1.75rem); + top: calc(3rem + 1rem); align-self: start; - max-height: calc(100dvh - 5.5rem); - padding-right: 0.375rem; + max-height: calc(100dvh - 5rem); overflow-x: hidden; overflow-y: auto; overscroll-behavior: contain; + /* Thin, subtle overlay scrollbar so it does not fight the card's rounded + edge. Gutter stays stable so content does not shift when it appears. */ scrollbar-width: thin; + scrollbar-color: var(--coollabs-fill) transparent; + scrollbar-gutter: stable; + /* Contained card: a distinct elevated surface with a hairline ring and a + restrained lift, so the settings nav reads as a structured panel that + floats above the content canvas instead of loose links on the page. */ + padding: 0.5rem; + background: var(--coollabs-elevated); + border: 1px solid var(--coollabs-hairline); + border-radius: 12px; + box-shadow: 0 1px 2px rgb(0 0 0 / 0.05); } + .application-settings-navigation::-webkit-scrollbar { + width: 6px; + } + + .application-settings-navigation::-webkit-scrollbar-track { + background: transparent; + } + + .application-settings-navigation::-webkit-scrollbar-thumb { + border-radius: 9999px; + background: var(--coollabs-fill); + } + + .application-settings-navigation::-webkit-scrollbar-thumb:hover { + background: var(--coollabs-line); + } + + /* Resource settings rail: pinned as a stacked second sidebar, flush to the + main sidebar's right edge (left tracks its collapsed width via --rail-left). + A subtle step lighter than the main sidebar chrome, with a hairline divider, + so the two rails read as a cohesive stacked pair and the content is the + distinct surface to their right. */ /* Resource pages have a second fixed header row below the main header. */ .application-settings-workspace > .application-settings-navigation { top: 4rem; @@ -1494,6 +1564,17 @@ @media (min-width: 1280px) { } } +/* Honor reduced-motion for the settings nav disclosure/accordion and the mobile + sidebar sheet: keep open/close functional but drop the height/slide/fade. */ +@media (prefers-reduced-motion: reduce) { + .application-settings-navigation, + .application-settings-navigation *, + .mobile-sidebar-sheet * { + transition-duration: 0.01ms !important; + animation-duration: 0.01ms !important; + } +} + /* Field labels */ .application-settings-workspace label, .application-settings-form label { @@ -1603,7 +1684,7 @@ .resource-heading-tabs::-webkit-scrollbar { * Circular chevrons sit over gradient fades when more tabs are off-screen. */ .resource-heading-tabs-scroller { - --resource-heading-tabs-fade: #f5f5f5; /* neutral-100, matches pill track */ + --resource-heading-tabs-fade: oklch(97.02% 0 0); /* neutral-100, matches pill track */ } .dark .resource-heading-tabs-scroller { @@ -1944,7 +2025,7 @@ .listbox-panel { border-radius: 10px; border: 1px solid var(--coollabs-line); background: var(--coollabs-recessed); - box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45); + box-shadow: var(--shadow-dropdown); } @media (max-width: 767px) { diff --git a/resources/css/utilities.css b/resources/css/utilities.css index 6fdd260b5..e9ab0fafb 100644 --- a/resources/css/utilities.css +++ b/resources/css/utilities.css @@ -59,7 +59,7 @@ @utility input-focus { /* input, select before */ @utility input-select { - @apply block h-9 px-3 py-1.5 w-full text-sm text-black rounded-md border border-neutral-200 bg-white dark:bg-surface dark:text-fg dark:border-white/[0.08] transition-colors disabled:bg-neutral-100 disabled:text-neutral-400 dark:disabled:bg-white/[0.03] dark:disabled:text-fg-faint; + @apply block h-9 px-3 py-1.5 w-full text-sm text-black rounded-md border border-neutral-200 bg-[var(--coollabs-recessed)] dark:text-fg dark:border-white/[0.08] transition-colors disabled:bg-neutral-100 disabled:text-neutral-400 dark:disabled:bg-white/[0.03] dark:disabled:text-fg-faint; box-shadow: none; &:where(.dark, .dark *) { @@ -77,7 +77,7 @@ @utility input-select { /* Readonly */ @utility input { - @apply dark:read-only:text-neutral-500 dark:read-only:bg-coolgray-100/40 placeholder:text-neutral-300 dark:placeholder:text-neutral-700 read-only:text-neutral-500 read-only:bg-neutral-200; + @apply dark:read-only:text-neutral-500 dark:read-only:bg-coolgray-100/40 placeholder:text-neutral-400 dark:placeholder:text-neutral-500 read-only:text-neutral-500 read-only:bg-neutral-200; @apply input-select; @apply focus-visible:outline-none; diff --git a/resources/js/app.js b/resources/js/app.js index 11681efa0..10d3c2d01 100644 --- a/resources/js/app.js +++ b/resources/js/app.js @@ -124,3 +124,16 @@ window.scrollToSettingsSection = function scrollToSettingsSection(id) { rafId = window.requestAnimationFrame(tick); }; + +// When a settings sub-section link navigates across pages (href="route#section-id"), +// scroll to that section once the destination page has rendered. +function scrollToHashSettingsSection() { + const hash = window.location.hash; + if (!hash || hash.length < 2) { + return; + } + const id = decodeURIComponent(hash.slice(1)); + window.requestAnimationFrame(() => window.scrollToSettingsSection?.(id)); +} +document.addEventListener('livewire:navigated', scrollToHashSettingsSection); +document.addEventListener('DOMContentLoaded', scrollToHashSettingsSection); diff --git a/resources/views/components/application/configuration-sidebar.blade.php b/resources/views/components/application/configuration-sidebar.blade.php index 774bc49f0..8e901709a 100644 --- a/resources/views/components/application/configuration-sidebar.blade.php +++ b/resources/views/components/application/configuration-sidebar.blade.php @@ -238,52 +238,98 @@ ]; @endphp +@php + $activeMenuLabel = collect($groupedMenuItems)->flatMap(fn ($items) => $items)->firstWhere('active', true)['label'] ?? 'Settings'; +@endphp