diff --git a/DESIGN.md b/DESIGN.md index 1664c5866e..f6d6afcd9f 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -36,10 +36,10 @@ change. pure neutral gray. - **One accent.** Coolify purple in both themes (`#6b16ed`, hover `#7317ff`, pressed `#5a12c7`, depth `#4a0fa3`, light text and icons `#a391ff`). Yellow - is only for real warnings. The dark sidebar, including the logo cell, is - quiet neutral near-black (`#0d0d0d`), so the content keeps the focus; only - the current item's tile is purple. The dark command palette has a - light purple tint. The page canvas stays neutral. + is only for real warnings. In the default dark theme the sidebar, including + the logo cell, is purple-black (`#120e1a`) with a faint brand wash. Users + who want a neutral sidebar can pick a custom theme. The dark command palette + has a light purple tint. The page canvas stays neutral. - **Quiet text.** Sentence case. A clear text ladder: primary, dim, faint. No em dashes in UI copy. @@ -127,7 +127,7 @@ follow the theme. --ui-accent-edge: #4a0fa3; --ui-accent-fg: #ffffff; --ui-accent-light: #a391ff; - --ui-sidebar: #0d0d0d; + --ui-sidebar: #120e1a; --ui-card: #121212; --ui-success: oklch(72.27% 0.192 149.58); --ui-warning: oklch(88.13% 0.1507 91.7); @@ -144,8 +144,9 @@ Rules for the tokens: - Floating surfaces (menus, popovers, tooltips, modals) must be opaque. Use `--ui-menu-bg`, never a translucent value, or the content behind shows through. -- Keep the page canvas, cards, menus, logs, and the sidebar pure neutral - (chroma 0). The dark command palette is the only tinted surface. +- Keep the page canvas, cards, menus, and logs pure neutral (chroma 0). The + default dark sidebar, the logo cell above it, and the dark command palette + are the tinted surfaces. - **Dark lightness level.** The canvas is at 14.48% and the header panel at 19.13%. To change the level, move every dark surface and border token by the same @@ -220,11 +221,12 @@ Class names below are examples. Keep the values and structure. well is `--ui-panel` (`#121212`). Tables use the same pair: the darker mix for the header row and `--ui-panel` for the rows. - In light mode the sidebar is the canvas color and has a 1px - `--ui-chrome-border` right edge. In dark mode the sidebar column, including - the logo cell, is a flat neutral `#0d0d0d` (one step above the `#0a0a0a` - canvas) with a faint `rgb(255 255 255 / 0.06)` right edge. Do not add a - brand tint or wash to the sidebar: a colored sidebar competes with the - content for attention. + `--ui-chrome-border` right edge. In the default dark theme the sidebar + column, including the logo cell, is `#120e1a` with a faint lavender right + edge (`rgb(163 145 255 / 0.10)`). The nav has a brand-purple wash that + starts at 9% and fades out; the logo cell has a flat 9% wash, so the two + read as one surface. A custom theme gets the neutral sidebar rules and its + own sidebar color. - The top bar matches the canvas. It has no bottom border at rest. After the page scrolls under it, dark mode draws a neutral hairline (`rgb(255 255 255 / 0.06)`), a translucent bar, blur, and a soft shadow. @@ -679,12 +681,13 @@ lift off the content: mode, top-level card wells and table rows use `--color-card` (`#121212`), not the top bar. - **Desktop sidebar and the topbar brand block** (Coolify name and version) - use the canvas color in light mode. In dark mode they use `--color-sidebar` - (`#0d0d0d`), flat and neutral, with a `rgb(255 255 255 / 0.06)` right edge. - Inactive icon tiles, Search, the account button in the footer, and section - labels (10.5px uppercase mono, `--color-nav-muted`) are neutral gray. Only - the current item's tile uses the brand purple, so the sidebar does not - compete with the content. Chevrons are hidden until the row is hovered, + use the canvas color in light mode. In the default dark theme they use + `--color-sidebar` (`#120e1a`): a vertical brand wash on the nav, a flat 9% + wash on the logo cell, and a lavender right edge. Inactive icon tiles, + Search, the account button, and section labels (10.5px uppercase mono) are + lavender. The Admin icon stays neutral. These purple rules use + `html.dark:not([data-theme="custom"])`; a custom theme keeps neutral tiles, + Search, and account button. Chevrons are hidden until the row is hovered, focused, or current. Do not use `dark:border-white/[0.06]` on these chrome edges; a global rule forces that utility to the solid hairline grey. @@ -695,9 +698,9 @@ value. The dark ladder is `--color-app` 10, `--color-panel` 20, `--coollabs-recessed` 34 (sRGB, r=g=b), which reads as distinct surfaces. Temperature: the page canvas, cards, menus, listboxes, modals, and logs stay -**pure neutral gray** (r=g=b). Do not tint those. The dark sidebar -(`--color-sidebar`, `#0d0d0d`) is neutral too. The dark command palette is the -only exception: it carries a faint brand-purple wash. A custom theme derives all +**pure neutral gray** (r=g=b). Do not tint those. The exceptions are the +default dark sidebar (`--color-sidebar`, `#120e1a`, with a brand-purple wash) +and the dark command palette (a faint brand-purple wash). A custom theme derives all of these surfaces from its background and sidebar colors (see the token rules in P2). 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 brand accent. Yellow is for warnings. diff --git a/resources/css/app.css b/resources/css/app.css index 368219b4ab..e41d8f654c 100644 --- a/resources/css/app.css +++ b/resources/css/app.css @@ -1319,7 +1319,7 @@ html:not(.dark) .application-console-shell[data-console-theme="system"] .termina /* Cards sit one step above the #0a0a0a canvas. The top bar uses the canvas, not this color. The sidebar uses --color-sidebar. */ --color-card: #121212; - --color-sidebar: #0d0d0d; + --color-sidebar: #120e1a; /* 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, @@ -6250,9 +6250,9 @@ html.dark .canvas-zoom-max { } } -/* Brand shell (dark): black canvas and top bar, quiet neutral sidebar, scroll hairline. - The sidebar stays neutral so the content keeps the focus; only the current item - uses the brand purple. Light mode keeps the neutral shell. Floating navigation +/* Brand shell (dark): black canvas and top bar, sidebar, scroll hairline. + The default dark theme tints the sidebar purple (below); a custom theme gets + these neutral rules. Light mode keeps the neutral shell. Floating navigation keeps its own frame. */ html.dark .app-sidebar nav.sidebar-nav, html.dark .mobile-sidebar-sheet [role="dialog"] { @@ -6279,20 +6279,20 @@ html.dark :is(.app-sidebar, .mobile-sidebar-sheet) .menu-search { box-shadow: inset 0 0 0 1px rgb(255 255 255 / 0.06) !important; } -/* The account button is a .button, which is purple in dark mode; keep it neutral in the sidebar. */ -html.dark :is(.app-sidebar, .mobile-sidebar-sheet) .sidebar-footer .button:not(.button-highlighted):not(.button-error):not([isHighlighted]):not(:disabled) { +/* The account button is a .button, which is purple in dark mode; keep it neutral in a custom theme sidebar. */ +html.dark[data-theme="custom"] :is(.app-sidebar, .mobile-sidebar-sheet) .sidebar-footer .button:not(.button-highlighted):not(.button-error):not([isHighlighted]):not(:disabled) { --button-depth-color: rgb(255 255 255 / 0.05); background-color: rgb(255 255 255 / 0.04); border-color: rgb(255 255 255 / 0.08); color: var(--color-nav-text); } -html.dark :is(.app-sidebar, .mobile-sidebar-sheet) .sidebar-footer .button:not(.button-highlighted):not(.button-error):not([isHighlighted]):not(:disabled):hover { +html.dark[data-theme="custom"] :is(.app-sidebar, .mobile-sidebar-sheet) .sidebar-footer .button:not(.button-highlighted):not(.button-error):not([isHighlighted]):not(:disabled):hover { background-color: rgb(255 255 255 / 0.07); border-color: rgb(255 255 255 / 0.12); } -html.dark :is(.app-sidebar, .mobile-sidebar-sheet) .sidebar-footer .button:not(.button-highlighted):not(.button-error):not([isHighlighted]):not(:disabled) svg { +html.dark[data-theme="custom"] :is(.app-sidebar, .mobile-sidebar-sheet) .sidebar-footer .button:not(.button-highlighted):not(.button-error):not([isHighlighted]):not(:disabled) svg { color: var(--color-nav-muted); } @@ -6327,6 +6327,62 @@ html.dark .app-sidebar .menu-item:not(:hover):not(:focus-visible):not(.menu-item opacity: 0; } +/* Default dark theme sidebar: purple-black with a faint brand wash and lavender + tiles, text, and edges. A custom theme keeps the neutral rules above. */ +html.dark:not([data-theme="custom"]) .app-sidebar nav.sidebar-nav, +html.dark:not([data-theme="custom"]) .mobile-sidebar-sheet [role="dialog"] { + background-image: linear-gradient(180deg, rgb(107 22 237 / 0.09), transparent 520px); + border-right-color: rgb(163 145 255 / 0.10); + --color-nav-text: oklch(80% 0.02 300); + --color-nav-muted: oklch(58% 0.04 300); +} + +/* Same color as the top of the sidebar gradient, so the brand and sidebar read as one surface. */ +html.dark:not([data-theme="custom"]) .app-topbar-brand { + background-image: linear-gradient(rgb(107 22 237 / 0.09), rgb(107 22 237 / 0.09)); + border-right-color: rgb(163 145 255 / 0.10); +} + +html.dark:not([data-theme="custom"]) :is(.app-sidebar, .mobile-sidebar-sheet) .menu-search { + background-color: rgb(163 145 255 / 0.06) !important; + box-shadow: inset 0 0 0 1px rgb(163 145 255 / 0.10) !important; +} + +html.dark:not([data-theme="custom"]) :is(.app-sidebar, .mobile-sidebar-sheet) .menu-search:hover { + background-color: rgb(163 145 255 / 0.10) !important; +} + +html.dark:not([data-theme="custom"]) :is(.app-sidebar, .mobile-sidebar-sheet) .menu-search kbd { + border-color: rgb(163 145 255 / 0.18) !important; +} + +/* Group dividers use the lavender sidebar edge: the neutral hairline looks olive on the purple-black sidebar. */ +html.dark:not([data-theme="custom"]) :is(.app-sidebar, .mobile-sidebar-sheet) .sidebar-nav :is([class~="dark:border-white/[0.06]"], [class~="dark:border-white/[0.07]"], [class~="dark:border-white/[0.08]"]) { + border-color: rgb(163 145 255 / 0.10) !important; +} + +/* Admin keeps the neutral tile unless it is the current page. */ +html.dark:not([data-theme="custom"]):where(:not([data-nav-items="flat"])) :is(.app-sidebar, .mobile-sidebar-sheet) :is(.menu-item, .menu-subitem):not(.sidebar-toggle):not(.menu-search):not(.nav-admin):not(.menu-item-active):not(.menu-subitem-active) .menu-item-icon, +html.dark:not([data-theme="custom"]):where(:not([data-nav-items="flat"])) .app-sidebar nav.sidebar-collapsed .menu-search.menu-item .menu-item-icon { + border-color: rgb(163 145 255 / 0.13); + background: rgb(107 22 237 / 0.07); + color: #b9adff; +} + +html.dark:not([data-theme="custom"]) :is(.app-sidebar, .mobile-sidebar-sheet) nav:not(.sidebar-collapsed) .nav-section { + color: rgb(163 145 255 / 0.55); +} + +/* Sidebar account menu: the purple tint of the sidebar it opens from. */ +html.dark:not([data-theme="custom"]) .listbox-panel.top-user-menu-panel-sidebar { + background: color-mix(in oklab, #6b16ed 10%, var(--coollabs-elevated)); + border-color: rgb(163 145 255 / 0.22); +} + +html.dark:not([data-theme="custom"]) .top-user-menu-panel-sidebar .listbox-option:hover { + background: rgb(163 145 255 / 0.10); +} + /* Custom theme sidebar. These rules come after the dark sidebar rules above, so they win. */ /* !important: the light custom theme maps bg-neutral-50 to the canvas with !important. */ html[data-theme="custom"] :is(.app-sidebar nav.sidebar-nav, .app-topbar-brand, .mobile-sidebar-sheet [role="dialog"]) { diff --git a/resources/views/layouts/base.blade.php b/resources/views/layouts/base.blade.php index 6dd415b03c..cbfb637636 100644 --- a/resources/views/layouts/base.blade.php +++ b/resources/views/layouts/base.blade.php @@ -76,8 +76,9 @@ : { sidebar: mixHex('#0c0c0e', accent, 0.1), selected: mixHex('#1c1c1f', accent, 0.3), accent, background: mixHex('#0f0f11', accent, 0.12) }; }; window.customThemePresets = [ - // The built-in Dark theme, not a custom theme. Its colors start the fields when the user edits it. - { name: 'Coolify', theme: null, swatch: { sidebar: '#0d0d0d', selected: '#1c1c1c', accent: '#6b16ed', background: '#0a0a0a' } }, + // The built-in Dark theme, not a custom theme. Its colors start the fields when the user edits it. The sidebar is + // the color that the Dark sidebar shows: #120e1a with its 9% purple wash. + { name: 'Coolify', theme: null, swatch: { sidebar: '#1a0f2d', selected: '#1c1c1c', accent: '#6b16ed', background: '#0a0a0a' } }, { name: 'Midnight', theme: { sidebar: '#0f172a', selected: '#2e4a7d', accent: '#f2458f', background: '#1a2338' } }, { name: 'Forest', theme: { sidebar: '#12342b', selected: '#2c6e57', accent: '#f4b860', background: '#1b2a25' } }, { name: 'Ember', theme: { sidebar: '#3b1410', selected: '#8f2d1f', accent: '#ffb86b', background: '#241816' } },