mirror of
https://github.com/coollabsio/coolify.git
synced 2026-09-24 07:25:13 -05:00
feat(ui): white settings rail in light mode, unify shadows, mobile sheet polish
- Settings rail (application-settings-navigation) is now white in light mode instead of a gray fill; separation comes from the hairline ring + soft lift. - Mobile slide-over: replace the heavy shadow-modal with a soft left-casting shadow (scrim handles content dimming, shadcn-style) + rounded-l corners. - Unify scattered shadow-lg usages onto the shadow-dropdown / shadow-modal tokens across dropdowns, tooltips, popups and slide-over. - Light-mode semantic text + tertiary/nav contrast tuning for WCAG AA. - status-summary: support right-aligned dropdown; use it on the app heading.
This commit is contained in:
committed by
Andras Bacsai
parent
fd928537e3
commit
160be6f571
+58
-9
@@ -1023,8 +1023,34 @@ html:not(.dark) .application-console-shell[data-console-theme="system"] .termina
|
||||
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);
|
||||
/* Raised from 58.97% (4.47:1 on panel, just under AA) to 61% (4.86:1) so the
|
||||
11px sidebar section labels clear 4.5:1. */
|
||||
--color-nav-muted: oklch(61% 0 0);
|
||||
--color-nav-active: oklch(96.12% 0 0);
|
||||
/* Tertiary text. Base token (59.31%) only clears 4.5:1 on app/panel; it
|
||||
failed on surface/raised/selected (4.46/4.20/3.73). Raised to 64% so the
|
||||
313 dark:text-fg-faint usages clear AA across the whole surface ladder,
|
||||
while staying clearly dimmer than fg-dim (76.99%). */
|
||||
--color-fg-faint: oklch(65% 0 0);
|
||||
/* Error text. The base error red (57.71%) failed on every dark surface
|
||||
(4.10/3.75/3.53). Brightened for dark mode so text-error clears AA on the
|
||||
card ladder while staying unmistakably red. */
|
||||
--color-error: oklch(66% 0.2 27.33);
|
||||
}
|
||||
|
||||
/* Light-mode semantic text.
|
||||
warning / success / error double as the bright brand accent and as solid
|
||||
fills, which only appear under `dark:` — so in light mode these tokens are
|
||||
used almost entirely as text, translucent tints (bg-warning/10) and borders.
|
||||
The bright values (yellow 88%, green 72%) and the base red (57.71%) fail AA
|
||||
as text on white and the light surface ladder (warning 1.43, success 2.28,
|
||||
error 4.43 on canvas). Scoped to `:not(.dark)` so dark keeps the brand
|
||||
colors and the custom theme keeps its own overrides. Every value clears
|
||||
4.5:1 on white, canvas, elevated, recessed and fill, and is in sRGB gamut. */
|
||||
html:not(.dark) {
|
||||
--color-warning: oklch(50% 0.105 80);
|
||||
--color-success: oklch(48% 0.135 149.58);
|
||||
--color-error: oklch(52% 0.2 27.33);
|
||||
}
|
||||
|
||||
/* Theme surfaces are derived from one brand color. Changing
|
||||
@@ -1493,7 +1519,14 @@ html[data-theme="custom"] textarea:disabled {
|
||||
font-weight: 500;
|
||||
line-height: 1.25rem;
|
||||
letter-spacing: 0;
|
||||
color: inherit;
|
||||
/* Card title is a heading, so give it a readable strength instead of the
|
||||
muted --coollabs-subtle (5.66:1 light / 6.98:1 dark). ~8.7:1 in both
|
||||
modes (comfortable AAA) while descriptions/labels stay muted. */
|
||||
color: oklch(40% 0 0);
|
||||
}
|
||||
|
||||
.dark .application-settings-section > :is(header, .application-settings-section-header) :is(h2, h3) {
|
||||
color: var(--color-fg-dim);
|
||||
}
|
||||
|
||||
.application-settings-section > .application-settings-section-header p {
|
||||
@@ -1503,12 +1536,16 @@ html[data-theme="custom"] textarea:disabled {
|
||||
color: var(--coollabs-subtle);
|
||||
}
|
||||
|
||||
/* Layer card body: nested rounded panel with base bg + fill ring */
|
||||
/* Layer card body: nested rounded panel with base bg. Ring uses the same
|
||||
--coollabs-hairline as the card shell (line ~1456) so the body edge and the
|
||||
header edge read as one continuous border instead of two different grays
|
||||
(fill was lighter than hairline, so the header sides and body sides did not
|
||||
match). */
|
||||
.application-settings-section-body {
|
||||
position: relative;
|
||||
border-radius: 8px;
|
||||
background: var(--coollabs-base);
|
||||
box-shadow: 0 0 0 1px var(--coollabs-fill);
|
||||
box-shadow: 0 0 0 1px var(--coollabs-hairline);
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
@@ -1549,9 +1586,12 @@ html[data-theme="custom"] textarea:disabled {
|
||||
@media (min-width: 1280px) {
|
||||
.application-settings-navigation {
|
||||
position: sticky;
|
||||
top: calc(3rem + 1rem);
|
||||
/* Clear the fixed 3rem topbar and match <main>'s pt (3rem + 1.75rem) so
|
||||
the pinned rail aligns with the content and has breathing room at the
|
||||
top instead of hugging the header when the page scrolls. */
|
||||
top: calc(3rem + 1.75rem);
|
||||
align-self: start;
|
||||
max-height: calc(100dvh - 5rem);
|
||||
max-height: calc(100dvh - 5.75rem);
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain;
|
||||
@@ -1560,9 +1600,10 @@ html[data-theme="custom"] textarea:disabled {
|
||||
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. */
|
||||
/* Contained card: a distinct surface with a hairline ring and a restrained
|
||||
lift, so the settings nav reads as a structured panel that stands off
|
||||
the content canvas. In dark mode --coollabs-elevated is a clear lighter
|
||||
step above the canvas; the light-mode fill is overridden below. */
|
||||
padding: 0.5rem;
|
||||
background: var(--coollabs-elevated);
|
||||
border: 1px solid var(--coollabs-hairline);
|
||||
@@ -1570,6 +1611,14 @@ html[data-theme="custom"] textarea:disabled {
|
||||
box-shadow: 0 1px 2px rgb(0 0 0 / 0.05);
|
||||
}
|
||||
|
||||
/* Light mode: a bright white rail on the light-gray page canvas (neutral-50,
|
||||
~98.5%). Fill contrast to the canvas is small, so separation comes from the
|
||||
hairline ring + soft lift rather than a darker gray fill — the standard
|
||||
"white rail on gray canvas" hierarchy (Linear/Vercel), not a gray rail. */
|
||||
html:not(.dark) .application-settings-navigation {
|
||||
background: var(--coollabs-base);
|
||||
}
|
||||
|
||||
.application-settings-navigation::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
}
|
||||
|
||||
@utility apexcharts-tooltip-custom {
|
||||
@apply bg-white dark:bg-coolgray-100 border border-neutral-200 dark:border-coolgray-300 rounded-lg shadow-lg p-3 text-sm;
|
||||
@apply bg-white dark:bg-coolgray-100 border border-neutral-200 dark:border-coolgray-300 rounded-lg shadow-dropdown p-3 text-sm;
|
||||
min-width: 160px;
|
||||
}
|
||||
|
||||
@@ -162,7 +162,7 @@
|
||||
}
|
||||
|
||||
@utility auth-tooltip {
|
||||
@apply fixed z-[10000] px-2.5 py-1.5 text-xs font-medium rounded-lg pointer-events-none whitespace-nowrap text-white bg-neutral-900 border border-neutral-700 shadow-lg dark:text-fg dark:bg-raised dark:border-white/10;
|
||||
@apply fixed z-[10000] px-2.5 py-1.5 text-xs font-medium rounded-lg pointer-events-none whitespace-nowrap text-white bg-neutral-900 border border-neutral-700 shadow-dropdown dark:text-fg dark:bg-raised dark:border-white/10;
|
||||
}
|
||||
|
||||
@utility alert-success {
|
||||
@@ -222,7 +222,10 @@
|
||||
}
|
||||
|
||||
@utility menu-item {
|
||||
@apply relative flex gap-2.5 items-center h-8 px-2.5 w-full text-[13px] font-medium rounded-md truncate min-w-0 transition-colors text-nav-text hover:bg-neutral-100 hover:text-nav-active dark:hover:bg-white/[0.05];
|
||||
/* Translucent hover (like the active pill) so it darkens against any surface,
|
||||
including the light-mode settings rail's gray fill; on white it matches the
|
||||
old neutral-100. */
|
||||
@apply relative flex gap-2.5 items-center h-8 px-2.5 w-full text-[13px] font-medium rounded-md truncate min-w-0 transition-colors text-nav-text hover:bg-black/[0.04] hover:text-nav-active dark:hover:bg-white/[0.05];
|
||||
}
|
||||
@utility menu-item-icon {
|
||||
@apply shrink-0 size-[18px] opacity-90;
|
||||
@@ -245,7 +248,7 @@
|
||||
/* Indented child rows in a collapsible nav group */
|
||||
@utility menu-subitem {
|
||||
/* Label owns text ellipsis; keep this row overflow-visible so the focus ring is not clipped. */
|
||||
@apply relative flex gap-2.5 items-center h-8 pl-3 pr-2.5 w-full text-[13px] font-medium rounded-md min-w-0 transition-colors text-nav-text hover:bg-neutral-100 hover:text-nav-active dark:hover:bg-white/[0.05];
|
||||
@apply relative flex gap-2.5 items-center h-8 pl-3 pr-2.5 w-full text-[13px] font-medium rounded-md min-w-0 transition-colors text-nav-text hover:bg-black/[0.04] hover:text-nav-active dark:hover:bg-white/[0.05];
|
||||
}
|
||||
@utility menu-subitem-active {
|
||||
@apply rounded-md bg-black/[0.05] text-nav-active hover:bg-black/[0.05] dark:bg-white/[0.06] dark:hover:bg-white/[0.06];
|
||||
@@ -344,7 +347,7 @@
|
||||
}
|
||||
|
||||
@utility info-helper-popup {
|
||||
@apply rounded-lg border border-neutral-200 bg-white text-neutral-600 shadow-modal whitespace-normal break-words dark:border-white/10 dark:bg-raised dark:text-fg-dim;
|
||||
@apply rounded-lg border border-neutral-200 bg-white text-neutral-600 shadow-dropdown whitespace-normal break-words dark:border-white/10 dark:bg-raised dark:text-fg-dim;
|
||||
}
|
||||
|
||||
@utility buyme {
|
||||
|
||||
@@ -132,7 +132,7 @@
|
||||
|
||||
{{-- Dropdown Options --}}
|
||||
<div x-show="open && !{{ $disabled ? 'true' : 'false' }}" x-transition
|
||||
class="absolute z-50 w-full mt-1 bg-white dark:bg-coolgray-100 border border-neutral-300 dark:border-coolgray-400 rounded shadow-lg max-h-60 overflow-auto scrollbar">
|
||||
class="absolute z-50 w-full mt-1 bg-white dark:bg-coolgray-100 border border-neutral-300 dark:border-coolgray-400 rounded shadow-dropdown max-h-60 overflow-auto scrollbar">
|
||||
|
||||
<template x-if="filteredOptions.length === 0">
|
||||
<div class="px-3 py-2 text-sm text-neutral-500 dark:text-neutral-400">
|
||||
@@ -277,7 +277,7 @@
|
||||
|
||||
{{-- Dropdown Options --}}
|
||||
<div x-show="open && !{{ $disabled ? 'true' : 'false' }}" x-transition
|
||||
class="absolute z-50 w-full mt-1 bg-white dark:bg-coolgray-100 border border-neutral-300 dark:border-coolgray-400 rounded shadow-lg max-h-60 overflow-auto scrollbar">
|
||||
class="absolute z-50 w-full mt-1 bg-white dark:bg-coolgray-100 border border-neutral-300 dark:border-coolgray-400 rounded shadow-dropdown max-h-60 overflow-auto scrollbar">
|
||||
|
||||
<template x-if="filteredOptions.length === 0">
|
||||
<div class="px-3 py-2 text-sm text-neutral-500 dark:text-neutral-400">
|
||||
|
||||
@@ -75,6 +75,6 @@
|
||||
x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0"
|
||||
:style="`left: ${x}px; top: ${y}px;`"
|
||||
:class="[below ? '' : '-translate-y-full', positioned ? 'visible' : 'invisible']"
|
||||
class="pointer-events-none fixed z-[10000] whitespace-nowrap rounded-lg border border-neutral-700 bg-neutral-900 px-2 py-1 text-xs font-medium text-white shadow-lg dark:border-white/10 dark:bg-raised">
|
||||
class="pointer-events-none fixed z-[10000] whitespace-nowrap rounded-lg border border-neutral-700 bg-neutral-900 px-2 py-1 text-xs font-medium text-white shadow-dropdown dark:border-white/10 dark:bg-raised">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -240,6 +240,6 @@
|
||||
</div>
|
||||
<div x-show="collapsed && tooltip.show" x-cloak x-transition.opacity.duration.100ms
|
||||
:style="`left: ${tooltip.x}px; top: ${tooltip.y}px;`"
|
||||
class="fixed z-[10000] -translate-y-1/2 px-2 py-1 text-xs font-medium rounded-lg bg-neutral-900 dark:bg-raised text-white whitespace-nowrap pointer-events-none shadow-lg border border-neutral-700 dark:border-white/10"
|
||||
class="fixed z-[10000] -translate-y-1/2 px-2 py-1 text-xs font-medium rounded-lg bg-neutral-900 dark:bg-raised text-white whitespace-nowrap pointer-events-none shadow-dropdown border border-neutral-700 dark:border-white/10"
|
||||
x-text="tooltip.text"></div>
|
||||
</nav>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
{{ $customActions }}
|
||||
@else
|
||||
<div
|
||||
class="flex lg:items-center flex-col justify-between w-full h-full max-w-4xl p-6 mx-auto bg-white border shadow-lg lg:border-t dark:border-coolgray-300 border-neutral-200 dark:bg-coolgray-100 lg:p-8 lg:flex-row sm:rounded-sm">
|
||||
class="flex lg:items-center flex-col justify-between w-full h-full max-w-4xl p-6 mx-auto bg-white border shadow-modal lg:border-t dark:border-coolgray-300 border-neutral-200 dark:bg-coolgray-100 lg:p-8 lg:flex-row sm:rounded-sm">
|
||||
<div
|
||||
class="flex flex-col items-start h-full pb-6 text-xs lg:items-center lg:flex-row lg:pb-0 lg:pr-6 lg:space-x-5 dark:text-neutral-300">
|
||||
@if (isset($icon))
|
||||
|
||||
@@ -35,7 +35,7 @@ x-init="$watch('slideOverOpen', value => {
|
||||
'max-w-4xl w-screen' => $fullScreen,
|
||||
])>
|
||||
<div
|
||||
class="flex flex-col h-full py-6 overflow-hidden border-l shadow-lg bg-neutral-50 dark:bg-base dark:border-neutral-800 border-neutral-200">
|
||||
class="flex flex-col h-full py-6 overflow-hidden border-l shadow-modal bg-neutral-50 dark:bg-base dark:border-neutral-800 border-neutral-200">
|
||||
<div class="px-4 pb-4 sm:px-5">
|
||||
<div class="flex items-start justify-between pb-1">
|
||||
<h2 class="text-2xl leading-6" id="slide-over-title">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@props(['status', 'title' => 'Application status', 'containerName' => 'Container'])
|
||||
@props(['status', 'title' => 'Application status', 'containerName' => 'Container', 'align' => 'left'])
|
||||
|
||||
@php
|
||||
$rawStatus = str((string) $status)->lower()->trim()->value();
|
||||
@@ -62,8 +62,12 @@
|
||||
</span>
|
||||
</x-status-badge>
|
||||
|
||||
<div x-cloak x-show="open" x-transition.origin.top.left
|
||||
class="listbox-panel top-8! right-auto! left-0! z-[90]! w-[min(16rem,calc(100vw-1.5rem))]! min-w-0! sm:w-64! sm:min-w-64!" role="menu">
|
||||
<div x-cloak x-show="open" x-transition.origin.top.{{ $align === 'right' ? 'right' : 'left' }}
|
||||
@class([
|
||||
'listbox-panel top-8! z-[90]! w-[min(16rem,calc(100vw-1.5rem))]! min-w-0! sm:w-64! sm:min-w-64!',
|
||||
'right-auto! left-0!' => $align !== 'right',
|
||||
'left-auto! right-0!' => $align === 'right',
|
||||
]) role="menu">
|
||||
<div class="px-3 py-2 text-[11px] font-medium text-neutral-400 dark:text-fg-faint">{{ $title }}</div>
|
||||
<div class="listbox-option cursor-default! gap-2.5!">
|
||||
<span @class([
|
||||
|
||||
@@ -87,7 +87,7 @@
|
||||
x-transition:enter-start="translate-x-full" x-transition:enter-end="translate-x-0"
|
||||
x-transition:leave="transform transition ease-in duration-200"
|
||||
x-transition:leave-start="translate-x-0" x-transition:leave-end="translate-x-full"
|
||||
class="relative flex h-full w-72 max-w-[85vw] min-w-0 flex-col border-l border-neutral-200 bg-white shadow-2xl dark:border-white/[0.12] dark:bg-panel">
|
||||
class="relative flex h-full w-72 max-w-[85vw] min-w-0 flex-col overflow-hidden rounded-l-2xl border-l border-neutral-200 bg-white shadow-[-8px_0_30px_-6px_rgba(0,0,0,0.18)] dark:border-white/[0.12] dark:bg-panel dark:shadow-[-8px_0_30px_-4px_rgba(0,0,0,0.5)]">
|
||||
<div data-mobile-sidebar-brand
|
||||
class="flex h-12 shrink-0 items-center justify-between gap-1.5 border-b border-neutral-200 px-4 dark:border-white/[0.06]">
|
||||
<div class="flex min-w-0 items-baseline gap-1.5">
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
{{ $application->name }}
|
||||
</h1>
|
||||
<div class="flex shrink-0 items-center gap-2">
|
||||
<x-status-summary :status="$application->status" />
|
||||
<x-status-summary :status="$application->status" align="right" />
|
||||
<x-applications.links :application="$application" compact />
|
||||
</div>
|
||||
<div class="flex w-full flex-wrap gap-1">
|
||||
|
||||
Reference in New Issue
Block a user