feat(lumiere): grainy gradient background + editor chrome theme
Build and Deploy Verso / deploy (push) Successful in 15m0s

- Replace flat #f0f4f8 main content bg with layered grainy gradient:
  soft radial teal/blue orbs (background-attachment: fixed) plus
  SVG feTurbulence noise tile for organic depth
- Cards get backdrop-filter glass effect over the textured surface
- New ide-lumiere.scss: sets body[data-lumiere] via useThemedPage, then
  overrides toolbar (white + 3px teal→blue accent stripe), rail (teal
  active states), and file-tree (teal selected/hover) CSS variables

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
claude
2026-06-11 11:39:58 +00:00
parent 10a17c5443
commit ac02fd707e
4 changed files with 145 additions and 70 deletions
@@ -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])
}
@@ -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';
@@ -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;
}
@@ -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;
}
}