diff --git a/services/web/frontend/js/shared/hooks/use-themed-page.tsx b/services/web/frontend/js/shared/hooks/use-themed-page.tsx index 1809ffb871..2cb6aca61a 100644 --- a/services/web/frontend/js/shared/hooks/use-themed-page.tsx +++ b/services/web/frontend/js/shared/hooks/use-themed-page.tsx @@ -1,12 +1,16 @@ import { useLayoutEffect } from 'react' import { useActiveOverallTheme } from './use-active-overall-theme' +import { useUserSettingsContext } from '@/shared/context/user-settings-context' export default function useThemedPage(featureFlag?: string) { const activeOverallTheme = useActiveOverallTheme(featureFlag) + const { + userSettings: { overallTheme }, + } = useUserSettingsContext() useLayoutEffect(() => { - // Sets the body's data-theme attribute for theming document.body.dataset.theme = activeOverallTheme === 'dark' ? 'default' : 'light' - }, [activeOverallTheme]) + document.body.dataset.lumiere = overallTheme === 'lumiere-' ? 'true' : 'false' + }, [activeOverallTheme, overallTheme]) } diff --git a/services/web/frontend/stylesheets/pages/all.scss b/services/web/frontend/stylesheets/pages/all.scss index dba082608d..4645dace09 100644 --- a/services/web/frontend/stylesheets/pages/all.scss +++ b/services/web/frontend/stylesheets/pages/all.scss @@ -8,6 +8,7 @@ @import 'sidebar-v2-dash-pane'; @import 'editor/ide'; @import 'editor/ide-redesign'; +@import 'editor/ide-lumiere'; @import 'editor/rail'; @import 'editor/settings'; @import 'editor/toolbar'; diff --git a/services/web/frontend/stylesheets/pages/editor/ide-lumiere.scss b/services/web/frontend/stylesheets/pages/editor/ide-lumiere.scss new file mode 100644 index 0000000000..4234554861 --- /dev/null +++ b/services/web/frontend/stylesheets/pages/editor/ide-lumiere.scss @@ -0,0 +1,79 @@ +// Verso Lumière — editor chrome overrides. +// Scoped to body[data-lumiere='true'] so zero impact on Classic themes. +// Sets CSS variable overrides and adds the signature teal/blue accent stripe. + +$lum-teal: #2a9d8f; +$lum-blue: #3d7ebf; +$lum-border: #e2eaf2; + +// ── CSS variable overrides ───────────────────────────────────────────────── + +[data-lumiere='true'] { + // Toolbar + --redesign-toolbar-background: #ffffff; + --redesign-toolbar-border-divider: #{$lum-border}; + --redesign-toolbar-home-button-hover-background: rgba(#{$lum-teal}, 0.08); + --redesign-subdued-button-hover-background: rgba(42, 157, 143, 0.08); + --redesign-subdued-button-color: #1a2e3b; + + // Rail + --ide-rail-background: #ffffff; + --ide-rail-border-colour: #{$lum-border}; + --ide-rail-color: #1a2e3b; + --ide-rail-link-background: #ffffff; + --ide-rail-link-hover-background: rgba(42, 157, 143, 0.08); + --ide-rail-link-hover-color: #{$lum-teal}; + --ide-rail-link-active-color: #ffffff; + --ide-rail-link-active-background: #{$lum-teal}; + --ide-rail-header-subdued-button-color: #1a2e3b; + --ide-rail-header-subdued-button-hover-background: rgba(42, 157, 143, 0.08); + + // File tree + --file-tree-bg: #ffffff; + --file-tree-item-color: #1a2e3b; + --file-tree-icon-colour: #64748b; + --file-tree-item-hover-bg: rgba(42, 157, 143, 0.07); + --file-tree-item-selected-bg: rgba(42, 157, 143, 0.14); + --file-tree-item-selected-color: #{$lum-teal}; + --file-tree-expand-button-color: #1a2e3b; +} + +// ── Toolbar — gradient accent stripe ────────────────────────────────────── + +[data-lumiere='true'] .ide-redesign-toolbar { + position: relative; + overflow: hidden; + border-bottom-color: $lum-border; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04); + + // 3px teal→blue stripe at the very top (same motif as the dashboard navbar) + &::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 3px; + background: linear-gradient(90deg, $lum-teal 0%, $lum-blue 100%); + z-index: 1; + pointer-events: none; + } +} + +// ── Rail — active tab indicator uses teal ───────────────────────────────── + +[data-lumiere='true'] .ide-rail-tab-link.open-rail::after { + background-color: #ffffff; +} + +// ── Rail panel headers ───────────────────────────────────────────────────── + +[data-lumiere='true'] .rail-panel-title { + color: #1a2e3b; +} + +// ── File tree toolbar ────────────────────────────────────────────────────── + +[data-lumiere='true'] .file-tree-toolbar { + border-bottom-color: $lum-border; +} diff --git a/services/web/frontend/stylesheets/pages/project-list-lumiere.scss b/services/web/frontend/stylesheets/pages/project-list-lumiere.scss index bc3a9cfd02..97ff9a2f6c 100644 --- a/services/web/frontend/stylesheets/pages/project-list-lumiere.scss +++ b/services/web/frontend/stylesheets/pages/project-list-lumiere.scss @@ -11,9 +11,12 @@ $lum-blue: #3d7ebf; $lum-text: #1a2e3b; $lum-text-sub: #64748b; $lum-text-muted: #94a3b8; -$lum-bg: #f0f4f8; $lum-border: #e2eaf2; +// Grainy SVG noise tile (fractalNoise, greyscale, stitched for seamless tiling). +// The rect opacity (0.06) controls grain intensity — increase for more texture. +$lum-noise: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.75' numOctaves='4' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.06'/%3E%3C/svg%3E"); + .project-list-lumiere { // ══════════════════════════════════════════════════════════════════════════ @@ -21,7 +24,6 @@ $lum-border: #e2eaf2; // ══════════════════════════════════════════════════════════════════════════ .navbar-default { - // Lift the hard border; replace with soft shadow border-bottom: none !important; box-shadow: 0 1px 0 rgba(0, 0, 0, 0.06), 0 2px 8px rgba(0, 0, 0, 0.04); background-color: #ffffff !important; @@ -43,9 +45,7 @@ $lum-border: #e2eaf2; .navbar-nav > li > .dropdown-toggle { border-color: transparent !important; border-radius: 20px; - transition: - background-color 0.15s ease, - color 0.15s ease; + transition: background-color 0.15s ease, color 0.15s ease; &:hover, &:focus, @@ -56,7 +56,6 @@ $lum-border: #e2eaf2; } } - // The instance title (e.g. "Verso V0.185 Alpha") .navbar-title { color: $lum-text !important; font-weight: 600; @@ -87,9 +86,7 @@ $lum-border: #e2eaf2; font-weight: 600; border-radius: 10px !important; box-shadow: 0 2px 8px rgba($lum-teal, 0.25); - transition: - box-shadow 0.18s ease, - filter 0.18s ease; + transition: box-shadow 0.18s ease, filter 0.18s ease; &:hover, &:focus { @@ -98,7 +95,6 @@ $lum-border: #e2eaf2; color: #ffffff !important; } - // Caret color &::after { border-top-color: rgba(255, 255, 255, 0.8); } @@ -116,9 +112,7 @@ $lum-border: #e2eaf2; font-size: 0.875rem; font-weight: 500; color: $lum-text-sub; - transition: - background-color 0.15s ease, - color 0.15s ease; + transition: background-color 0.15s ease, color 0.15s ease; } > li:hover button { @@ -132,7 +126,6 @@ $lum-border: #e2eaf2; font-weight: 600; } - // Tags section header .dropdown-header { font-size: 0.7rem; font-weight: 700; @@ -142,7 +135,6 @@ $lum-border: #e2eaf2; padding: 0.85rem 0.75rem 0.35rem; } - // Individual tag items > li.tag { button.tag-name { border-radius: 8px; @@ -152,21 +144,9 @@ $lum-border: #e2eaf2; align-items: center; gap: 0.45rem; - &:hover { - color: $lum-teal; - } + &:hover { color: $lum-teal; } } - &.active button.tag-name { - color: #ffffff !important; - - // The colored dot inherits white from the active button — keep it visible - .badge { - opacity: 0.9; - } - } - - // Overflow "..." menu button .tag-menu button.dropdown-toggle { border-radius: 50%; transition: background-color 0.15s ease; @@ -180,13 +160,11 @@ $lum-border: #e2eaf2; } } - hr { - border-color: $lum-border; - } + hr { border-color: $lum-border; } } // ══════════════════════════════════════════════════════════════════════════ - // SIDEBAR — lower section (help / account icons + Verso logo) + // SIDEBAR — lower section // ══════════════════════════════════════════════════════════════════════════ .ds-nav-sidebar-lower { @@ -194,30 +172,24 @@ $lum-border: #e2eaf2; padding-top: 0.75rem; } - .ds-nav-icon-dropdown { - .dropdown-toggle { - border-radius: 50% !important; - color: $lum-text-sub !important; - transition: - background-color 0.15s ease, - color 0.15s ease, - box-shadow 0.15s ease; + .ds-nav-icon-dropdown .dropdown-toggle { + border-radius: 50% !important; + color: $lum-text-sub !important; + transition: background-color 0.15s ease, color 0.15s ease, box-shadow 0.15s ease; - &:hover { - background-color: rgba($lum-teal, 0.09) !important; - color: $lum-teal !important; - box-shadow: 0 0 0 3px rgba($lum-teal, 0.12); - } + &:hover { + background-color: rgba($lum-teal, 0.09) !important; + color: $lum-teal !important; + box-shadow: 0 0 0 3px rgba($lum-teal, 0.12); + } - &.show { - background-color: rgba($lum-teal, 0.15) !important; - color: $lum-teal !important; - box-shadow: 0 0 0 3px rgba($lum-teal, 0.18); - } + &.show { + background-color: rgba($lum-teal, 0.15) !important; + color: $lum-teal !important; + box-shadow: 0 0 0 3px rgba($lum-teal, 0.18); } } - // Verso wordmark at the very bottom — subtle separator + gentle fade .ds-nav-verso-logo { border-top: 1px solid $lum-border; padding-top: 0.6rem; @@ -225,12 +197,9 @@ $lum-border: #e2eaf2; opacity: 0.7; transition: opacity 0.15s ease; - &:hover { - opacity: 1; - } + &:hover { opacity: 1; } } - // Theme toggle: teal selected indicator .theme-toggle-radios { background-color: rgba($lum-teal, 0.07); } @@ -239,17 +208,38 @@ $lum-border: #e2eaf2; background-color: $lum-teal !important; color: #ffffff; - .material-symbols { - color: #ffffff; - } + .material-symbols { color: #ffffff; } } // ══════════════════════════════════════════════════════════════════════════ - // MAIN CONTENT AREA + // MAIN CONTENT AREA — grainy gradient background + // + // Three layers, back to front: + // 1. Solid base colour (#fafbff — cool off-white) + // 2. Two soft radial-gradient "orbs" (teal top-right, blue bottom-left), + // rendered fixed so they stay in the corner as the user scrolls. + // 3. SVG feTurbulence noise tile (200×200, repeating, scrolls with content) + // at 6% opacity — the "grain" effect seen in Linear / Raycast / UmbrelOS. // ══════════════════════════════════════════════════════════════════════════ .project-ds-nav-content { - background-color: $lum-bg; + background-image: + #{$lum-noise}, + radial-gradient(ellipse 80% 60% at 88% 0%, rgba($lum-teal, 0.14) 0%, transparent 60%), + radial-gradient(ellipse 70% 55% at 4% 98%, rgba($lum-blue, 0.10) 0%, transparent 55%); + background-size: + 200px 200px, + cover, + cover; + background-repeat: + repeat, + no-repeat, + no-repeat; + background-attachment: + scroll, // grain tiles with scroll — seamless + fixed, // teal orb stays pinned to viewport corner + fixed; // blue orb stays pinned to viewport corner + background-color: #fafbff; } // ── Page header ──────────────────────────────────────────────────────────── @@ -304,18 +294,19 @@ $lum-border: #e2eaf2; flex-direction: column; text-decoration: none; border-radius: 10px; - background: #ffffff; + background: rgba(255, 255, 255, 0.82); + backdrop-filter: blur(8px); + -webkit-backdrop-filter: blur(8px); + border: 1px solid rgba(255, 255, 255, 0.6); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.07); - transition: - transform 0.18s ease, - box-shadow 0.18s ease; + transition: transform 0.18s ease, box-shadow 0.18s ease; overflow: hidden; color: inherit; &:hover, &:focus-visible { transform: translateY(-3px); - box-shadow: 0 8px 24px rgba(0, 0, 0, 0.13); + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12); text-decoration: none; color: inherit; } @@ -427,18 +418,18 @@ $lum-border: #e2eaf2; } .lumiere-format-badge--latex { - background: #e8f5ee; + background: rgba(#e8f5ee, 0.9); color: $lum-teal; } .lumiere-format-badge--typst { - background: #e0f2fe; + background: rgba(#e0f2fe, 0.9); color: $lum-blue; } .lumiere-format-badge--quarto, .lumiere-format-badge--quarto-slides { - background: #ede9fe; + background: rgba(#ede9fe, 0.9); color: #7c4dff; } }