diff --git a/Documentation/STYLING-THEME-SAMPLE.json b/Documentation/STYLING-THEME-SAMPLE.json new file mode 100644 index 0000000..194b895 --- /dev/null +++ b/Documentation/STYLING-THEME-SAMPLE.json @@ -0,0 +1,177 @@ +{ + "schemaVersion": "1.0.0", + "id": "moku-styling-sample", + "name": "Moku Styling Sample", + "description": "Sample theme showing the background, surface, border, accent, and primary tokens documented in Documentation/STYLING.md.", + "author": "Moku Work", + "tokens": { + "shared": { + "palette": { + "gray": { + "0": "hsl(210 20% 99%)", + "50": "hsl(220 20% 97%)", + "100": "hsl(220 16% 93%)", + "200": "hsl(220 13% 87%)", + "300": "hsl(220 11% 75%)", + "400": "hsl(220 9% 58%)", + "500": "hsl(220 10% 45%)", + "600": "hsl(220 14% 34%)", + "700": "hsl(220 18% 24%)", + "800": "hsl(220 22% 16%)", + "900": "hsl(220 28% 10%)" + }, + "blue": { + "400": "hsl(218 88% 61%)", + "500": "hsl(221 83% 53%)", + "600": "hsl(224 76% 48%)" + }, + "green": { + "500": "hsl(154 60% 40%)" + }, + "red": { + "500": "hsl(0 72% 54%)" + }, + "amber": { + "500": "hsl(36 100% 50%)" + } + }, + "space": { + "1": "0.25rem", + "2": "0.5rem", + "3": "0.75rem", + "4": "1rem", + "5": "1.25rem", + "6": "1.5rem", + "8": "2rem", + "10": "2.5rem", + "12": "3rem" + }, + "radius": { + "sm": "0.375rem", + "md": "0.625rem", + "lg": "0.875rem", + "xl": "1.25rem", + "pill": "999px" + }, + "size": { + "controlMd": "2.25rem", + "controlLg": "2.5rem", + "contentWidthWide": "72rem", + "blurOverlay": "18px" + }, + "shadow": { + "soft": "0 12px 32px hsl(220 30% 10% / 0.08)", + "strong": "0 20px 48px hsl(220 30% 10% / 0.16)" + }, + "zIndex": { + "base": "1", + "dropdown": "100", + "sticky": "200", + "overlay": "400", + "modal": "500", + "toast": "600" + }, + "motion": { + "durationFast": "140ms", + "durationBase": "220ms", + "durationSlow": "320ms", + "easeStandard": "cubic-bezier(0.2, 0.8, 0.2, 1)" + }, + "typography": { + "fontFamily": { + "sans": "ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Helvetica Neue\", Arial, sans-serif", + "heading": "\"Avenir Next\", \"Segoe UI\", \"Helvetica Neue\", Arial, sans-serif", + "display": "\"Avenir Next\", \"Segoe UI\", \"Helvetica Neue\", Arial, sans-serif", + "serif": "ui-serif, Georgia, Cambria, \"Times New Roman\", Times, serif", + "mono": "ui-monospace, \"SF Mono\", \"SFMono-Regular\", Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace" + }, + "fontSize": { + "caption": "0.75rem", + "label": "0.875rem", + "body": "1rem", + "title": "clamp(1.125rem, 1.05rem + 0.3vw, 1.25rem)", + "heading": "clamp(1.5rem, 1.2rem + 1vw, 2.125rem)", + "display": "clamp(2.25rem, 1.7rem + 2.2vw, 3.75rem)" + }, + "lineHeight": { + "caption": "1.4", + "label": "1.35", + "body": "1.55", + "title": "1.3", + "heading": "1.15", + "display": "1.05" + }, + "fontWeight": { + "caption": "500", + "label": "600", + "body": "400", + "title": "600", + "heading": "600", + "display": "700" + }, + "letterSpacing": { + "caption": "0.01em", + "label": "0.005em", + "body": "0", + "title": "-0.01em", + "heading": "-0.02em", + "display": "-0.03em" + } + } + }, + "modes": { + "light": { + "colorScheme": "light", + "colors": { + "canvas": "var(--gray-50)", + "surface": "hsl(0 0% 100% / 0.9)", + "surfaceMuted": "var(--gray-0)", + "surfaceHover": "var(--gray-100)", + "border": "hsl(220 15% 85% / 0.9)", + "borderStrong": "hsl(220 12% 70% / 0.9)", + "text": "var(--gray-800)", + "textMuted": "var(--gray-500)", + "accent": "var(--blue-500)", + "accentStrong": "var(--blue-600)", + "accentSoft": "hsl(218 88% 61% / 0.12)", + "accentContrast": "hsl(0 0% 100%)", + "primaryOne": "var(--blue-500)", + "primaryTwo": "hsl(271 72% 60%)", + "primaryThree": "hsl(192 76% 48%)", + "success": "var(--green-500)", + "danger": "var(--red-500)", + "warning": "var(--amber-500)", + "focusRing": "hsl(221 83% 53% / 0.55)" + } + }, + "dark": { + "colorScheme": "dark", + "colors": { + "canvas": "var(--gray-900)", + "surface": "hsl(220 23% 14% / 0.92)", + "surfaceMuted": "hsl(220 22% 12% / 0.96)", + "surfaceHover": "hsl(220 18% 20% / 0.96)", + "border": "hsl(220 12% 26% / 0.9)", + "borderStrong": "hsl(220 12% 38% / 0.9)", + "text": "hsl(210 20% 96%)", + "textMuted": "hsl(220 12% 70%)", + "accent": "hsl(217 91% 67%)", + "accentStrong": "hsl(218 88% 61%)", + "accentSoft": "hsl(217 91% 67% / 0.18)", + "accentContrast": "hsl(220 28% 10%)", + "primaryOne": "hsl(217 91% 67%)", + "primaryTwo": "hsl(272 80% 70%)", + "primaryThree": "hsl(190 84% 62%)", + "success": "hsl(154 55% 48%)", + "danger": "hsl(0 72% 62%)", + "warning": "hsl(36 100% 60%)", + "focusRing": "hsl(217 91% 67% / 0.65)" + }, + "shadow": { + "soft": "0 16px 40px hsl(220 40% 3% / 0.45)", + "strong": "0 24px 60px hsl(220 40% 3% / 0.55)" + } + } + } + } +} diff --git a/Documentation/STYLING.md b/Documentation/STYLING.md new file mode 100644 index 0000000..a4c7a51 --- /dev/null +++ b/Documentation/STYLING.md @@ -0,0 +1,327 @@ +# Styling Reference + +This document explains which theme tokens control the main backgrounds, surfaces, +borders, and shell gradients in Moku Work. + +It is focused on the current frontend shell scaffold so future visual tuning can +be done intentionally instead of by guesswork. + +## Source Of Truth + +There are two places to look when changing styling tokens: + +- Runtime theme payload: + - `Frontend/public/themes/moku-default.json` +- SCSS fallback defaults: + - `Frontend/src/styles/themes/_light.scss` + - `Frontend/src/styles/themes/_dark.scss` +- Full sample theme file: + - `Documentation/STYLING-THEME-SAMPLE.json` + +If you want to change the actual themed values used by the app, update the theme +JSON first. The SCSS files act as fallback/default variables. + +--- + +## Core Surface Tokens + +These are the main tokens currently driving shell backgrounds and card surfaces: + +- `--color-canvas` + - outer page background +- `--color-surface` + - standard panel/card/topbar surface +- `--color-surface-muted` + - quieter secondary panels and dropdown surfaces +- `--color-surface-hover` + - hover state for solid surface rows +- `--color-border` + - standard card/panel border +- `--color-border-strong` + - stronger shell edges, separators, and emphasized borders +- `--color-text` + - primary text color +- `--color-text-muted` + - secondary/meta text color +- `--color-accent` + - primary accent lane +- `--color-accent-strong` + - stronger accent emphasis +- `--color-accent-soft` + - soft accent wash for subtle fills + +There are also ring-only multi-primary tokens currently used for the top-right +profile ring: + +- `--color-primary-1` +- `--color-primary-2` +- `--color-primary-3` + +--- + +## Light Mode Values + +Current light-mode surface values: + +```text +--color-canvas: var(--gray-50) +--color-surface: hsl(0 0% 100% / 0.9) +--color-surface-muted: var(--gray-0) +--color-surface-hover: var(--gray-100) +--color-border: hsl(220 15% 85% / 0.9) +--color-border-strong: hsl(220 12% 70% / 0.9) +--color-text: var(--gray-800) +--color-text-muted: var(--gray-500) +--color-accent: var(--blue-500) +--color-accent-strong: var(--blue-600) +--color-accent-soft: hsl(218 88% 61% / 0.12) +--color-primary-1: var(--blue-500) +--color-primary-2: hsl(271 72% 60%) +--color-primary-3: hsl(192 76% 48%) +``` + +## Dark Mode Values + +Current dark-mode surface values: + +```text +--color-canvas: var(--gray-900) +--color-surface: hsl(220 23% 14% / 0.92) +--color-surface-muted: hsl(220 22% 12% / 0.96) +--color-surface-hover: hsl(220 18% 20% / 0.96) +--color-border: hsl(220 12% 26% / 0.9) +--color-border-strong: hsl(220 12% 38% / 0.9) +--color-text: hsl(210 20% 96%) +--color-text-muted: hsl(220 12% 70%) +--color-accent: hsl(217 91% 67%) +--color-accent-strong: hsl(218 88% 61%) +--color-accent-soft: hsl(217 91% 67% / 0.18) +--color-primary-1: hsl(217 91% 67%) +--color-primary-2: hsl(272 80% 70%) +--color-primary-3: hsl(190 84% 62%) +``` + +--- + +## What Controls What + +### 1. App Background + +File: + +- `Frontend/src/components/shell/AppShell/AppShell.module.scss` + +The outer app frame uses: + +```scss +background: var(--color-canvas); +color: var(--color-text); +``` + +So if you want to change the overall page backdrop, change `--color-canvas`. + +### 2. Main Shell Split Background + +File: + +- `Frontend/src/components/shell/AppShell/AppShell.module.scss` + +The shell derives two important internal surface blends: + +```text +--sidebar-panel-surface: color-mix(in srgb, var(--color-surface-muted) 92%, transparent) +--workspace-panel-surface: color-mix(in srgb, var(--color-canvas) 94%, var(--color-surface)) +``` + +It also derives frame/separator borders: + +```text +--shell-frame-border: color-mix(in srgb, var(--color-border-strong) 44%, transparent) +--shell-divider-border: color-mix(in srgb, var(--color-border-strong) 34%, transparent) +``` + +Surface usage inside the shell: + +- `.body` → `var(--color-surface)` +- `.railColumn` → `var(--color-surface)` +- `.sidebarColumn` → `var(--sidebar-panel-surface)` +- `.workspaceMain` → `var(--workspace-panel-surface)` + +The major left/right shell background is drawn by `workspaceRegion::before` using a +horizontal gradient from sidebar surface to workspace surface. + +### 3. Standard Content Cards + +File: + +- `Frontend/src/components/workspace-home/WorkspaceHome/WorkspaceHome.module.scss` + +Cards currently use: + +```text +background: var(--color-surface) +border: 1px solid var(--color-border) +box-shadow: var(--shadow-soft) +``` + +If cards feel too flat or too strong, start by adjusting: + +- `--color-surface` +- `--color-border` + +### 4. Top Bar + +File: + +- `Frontend/src/components/shell/TopBar/TopBar.module.scss` + +The top bar itself uses: + +```text +background: var(--color-surface) +``` + +Hover/focus states for top-right controls use transparent mixes based on: + +- `--color-text` +- `--color-accent-strong` + +So the bar is mostly controlled by `--color-surface`, while the interactive polish +comes from text/accent tokens. + +### 5. Server Dock + +File: + +- `Frontend/src/components/shell/ServerDock/ServerDock.module.scss` + +The dock uses two derived tokens: + +```text +--server-dock-border: color-mix(in srgb, var(--color-border-strong) 75%, transparent) +--server-dock-surface: color-mix(in srgb, var(--color-surface) 94%, transparent) +``` + +That means the dock is visually tied most strongly to: + +- `--color-surface` +- `--color-border-strong` + +The server glyph fill uses a soft accent wash derived from: + +- `--color-accent-soft` + +### 6. Project Drawer + +File: + +- `Frontend/src/components/shell/ProjectSelector/ProjectSelector.module.scss` + +Important layers: + +- scrim: + - `color-mix(in srgb, black 8%, transparent)` +- drawer panel surface: + - defined in `.drawer::before` + - vertical gradient from `--color-surface` to `--color-surface-muted` +- current-project summary block: + - `color-mix(in srgb, var(--color-surface) 72%, transparent)` +- menu row hover: + - based on `--color-surface-hover` +- menu row active: + - based on `--color-surface` + +So the drawer’s look is mainly shaped by: + +- `--color-surface` +- `--color-surface-muted` +- `--color-surface-hover` +- `--color-border-strong` + +### 7. Department Selector Dropdown + +File: + +- `Frontend/src/components/shell/DepartmentSelector/DepartmentSelector.module.scss` + +The department dropdown is intentionally solid, not blurred. + +It uses: + +```text +.menu background: var(--color-surface-muted) +.menu border: 1px solid var(--color-border-strong) +.menuItem background: var(--color-surface-muted) +.submenuItem background: var(--color-surface-muted) +hover/active rows: var(--color-surface) +``` + +If the department menu feels too heavy or too subtle, start by adjusting: + +- `--color-surface-muted` +- `--color-surface` +- `--color-border-strong` + +--- + +## Quick Tuning Guide + +If you want to change the overall visual mood quickly, these are the highest-leverage tokens: + +### Make the app feel lighter / airier + +Adjust: + +- `--color-canvas` +- `--color-surface` +- `--color-surface-muted` + +### Make shells/cards feel more separated + +Adjust: + +- `--color-border` +- `--color-border-strong` +- `--color-surface-muted` + +### Make accent washes more or less noticeable + +Adjust: + +- `--color-accent-soft` + +### Change the visual personality of the profile ring + +Adjust: + +- `--color-primary-1` +- `--color-primary-2` +- `--color-primary-3` + +--- + +## Practical Rule Of Thumb + +Use this mental model: + +```text +canvas = app/page background +surface = primary panel or card +surface-muted = quieter secondary panel +surface-hover = solid hover state +border = normal edge +border-strong = stronger shell edge or divider +accent-soft = subtle tinted wash +primary-1/2/3 = decorative multi-color accents +``` + +If you are unsure where to start, tune these in this order: + +1. `--color-canvas` +2. `--color-surface` +3. `--color-surface-muted` +4. `--color-border` +5. `--color-border-strong` +6. `--color-accent-soft` + +That usually gives the biggest visual shift with the fewest unintended side effects. diff --git a/Frontend/public/themes/moku-default.json b/Frontend/public/themes/moku-default.json index 3a21476..a0aea32 100644 --- a/Frontend/public/themes/moku-default.json +++ b/Frontend/public/themes/moku-default.json @@ -134,6 +134,9 @@ "accentStrong": "var(--blue-600)", "accentSoft": "hsl(218 88% 61% / 0.12)", "accentContrast": "hsl(0 0% 100%)", + "primaryOne": "var(--blue-500)", + "primaryTwo": "hsl(271 72% 60%)", + "primaryThree": "hsl(192 76% 48%)", "success": "var(--green-500)", "danger": "var(--red-500)", "warning": "var(--amber-500)", @@ -155,6 +158,9 @@ "accentStrong": "hsl(218 88% 61%)", "accentSoft": "hsl(217 91% 67% / 0.18)", "accentContrast": "hsl(220 28% 10%)", + "primaryOne": "hsl(217 91% 67%)", + "primaryTwo": "hsl(272 80% 70%)", + "primaryThree": "hsl(190 84% 62%)", "success": "hsl(154 55% 48%)", "danger": "hsl(0 72% 62%)", "warning": "hsl(36 100% 60%)", diff --git a/Frontend/public/themes/moku-midnight.json b/Frontend/public/themes/moku-midnight.json new file mode 100644 index 0000000..8bd2561 --- /dev/null +++ b/Frontend/public/themes/moku-midnight.json @@ -0,0 +1,177 @@ +{ + "schemaVersion": "1.0.0", + "id": "moku-midnight", + "name": "Moku Midnight", + "description": "A warm, low-light Moku theme inspired by the mood and palette direction of refact0r's Midnight Discord theme, adapted to Moku's token schema.", + "author": "Moku Work", + "tokens": { + "shared": { + "palette": { + "gray": { + "0": "#f9f5d7", + "50": "#fbf1c7", + "100": "#ebdbb2", + "200": "#d5c4a1", + "300": "#bdae93", + "400": "#a89984", + "500": "#928374", + "600": "#7c6f64", + "700": "#665c54", + "800": "#3c3836", + "900": "#282828" + }, + "blue": { + "400": "hsl(167 24% 68%)", + "500": "#7caea3", + "600": "hsl(167 24% 48%)" + }, + "green": { + "500": "#a8b665" + }, + "red": { + "500": "#ea6962" + }, + "amber": { + "500": "#d8a656" + } + }, + "space": { + "1": "0.25rem", + "2": "0.5rem", + "3": "0.75rem", + "4": "1rem", + "5": "1.25rem", + "6": "1.5rem", + "8": "2rem", + "10": "2.5rem", + "12": "3rem" + }, + "radius": { + "sm": "0.375rem", + "md": "0.625rem", + "lg": "0.875rem", + "xl": "1.25rem", + "pill": "999px" + }, + "size": { + "controlMd": "2.25rem", + "controlLg": "2.5rem", + "contentWidthWide": "72rem", + "blurOverlay": "18px" + }, + "shadow": { + "soft": "0 12px 28px hsl(28 16% 12% / 0.08)", + "strong": "0 18px 40px hsl(28 18% 10% / 0.14)" + }, + "zIndex": { + "base": "1", + "dropdown": "100", + "sticky": "200", + "overlay": "400", + "modal": "500", + "toast": "600" + }, + "motion": { + "durationFast": "140ms", + "durationBase": "220ms", + "durationSlow": "320ms", + "easeStandard": "cubic-bezier(0.2, 0.8, 0.2, 1)" + }, + "typography": { + "fontFamily": { + "sans": "Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Helvetica Neue\", Arial, sans-serif", + "heading": "Inter, \"Avenir Next\", \"Segoe UI\", \"Helvetica Neue\", Arial, sans-serif", + "display": "Inter, \"Avenir Next\", \"Segoe UI\", \"Helvetica Neue\", Arial, sans-serif", + "serif": "ui-serif, Georgia, Cambria, \"Times New Roman\", Times, serif", + "mono": "ui-monospace, \"SF Mono\", \"SFMono-Regular\", Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace" + }, + "fontSize": { + "caption": "0.75rem", + "label": "0.875rem", + "body": "1rem", + "title": "clamp(1.125rem, 1.05rem + 0.3vw, 1.25rem)", + "heading": "clamp(1.5rem, 1.2rem + 1vw, 2.125rem)", + "display": "clamp(2.25rem, 1.7rem + 2.2vw, 3.75rem)" + }, + "lineHeight": { + "caption": "1.4", + "label": "1.35", + "body": "1.55", + "title": "1.3", + "heading": "1.15", + "display": "1.05" + }, + "fontWeight": { + "caption": "500", + "label": "600", + "body": "400", + "title": "600", + "heading": "600", + "display": "700" + }, + "letterSpacing": { + "caption": "0.01em", + "label": "0.005em", + "body": "0", + "title": "-0.01em", + "heading": "-0.02em", + "display": "-0.03em" + } + } + }, + "modes": { + "light": { + "colorScheme": "light", + "colors": { + "canvas": "hsl(38 24% 97%)", + "surface": "hsl(36 22% 99% / 0.94)", + "surfaceMuted": "hsl(36 20% 96%)", + "surfaceHover": "hsl(34 18% 93%)", + "border": "hsl(30 14% 76% / 0.72)", + "borderStrong": "hsl(28 16% 60% / 0.82)", + "text": "hsl(22 16% 22%)", + "textMuted": "hsl(28 10% 42%)", + "accent": "#d3869b", + "accentStrong": "hsl(344 47% 56%)", + "accentSoft": "hsl(344 47% 70% / 0.12)", + "accentContrast": "var(--gray-0)", + "primaryOne": "#7caea3", + "primaryTwo": "#d3869b", + "primaryThree": "#d8a656", + "success": "#a8b665", + "danger": "#ea6962", + "warning": "#d8a656", + "focusRing": "hsl(344 47% 56% / 0.28)" + } + }, + "dark": { + "colorScheme": "dark", + "colors": { + "canvas": "var(--gray-900)", + "surface": "hsl(20 8% 16% / 0.94)", + "surfaceMuted": "var(--gray-800)", + "surfaceHover": "hsl(22 9% 24% / 0.96)", + "border": "hsl(20 10% 30% / 0.72)", + "borderStrong": "hsl(30 14% 55% / 0.62)", + "text": "#d4be98", + "textMuted": "#a79a83", + "accent": "#d3869b", + "accentStrong": "hsl(344 47% 63%)", + "accentSoft": "hsl(344 47% 63% / 0.18)", + "accentContrast": "var(--gray-900)", + "primaryOne": "#7caea3", + "primaryTwo": "#d3869b", + "primaryThree": "#d8a656", + "success": "#a8b665", + "danger": "#ea6962", + "warning": "#d8a656", + "focusRing": "hsl(344 47% 63% / 0.45)" + }, + "shadow": { + "soft": "0 14px 32px hsl(20 16% 3% / 0.28)", + "strong": "0 20px 48px hsl(20 16% 2% / 0.38)" + } + } + } + } +} diff --git a/Frontend/src/components/shell/AppShell/AppShell.module.scss b/Frontend/src/components/shell/AppShell/AppShell.module.scss index 4c1ac03..54c29c1 100644 --- a/Frontend/src/components/shell/AppShell/AppShell.module.scss +++ b/Frontend/src/components/shell/AppShell/AppShell.module.scss @@ -15,6 +15,7 @@ --shell-divider-border: color-mix(in srgb, var(--color-border-strong) 34%, transparent); --sidebar-panel-surface: color-mix(in srgb, var(--color-surface-muted) 92%, transparent); --workspace-panel-surface: color-mix(in srgb, var(--color-canvas) 94%, var(--color-surface)); + position: relative; min-height: 0; display: grid; grid-template-columns: var(--rail-width) minmax(0, 1fr); @@ -26,7 +27,9 @@ min-height: 0; display: flex; position: relative; - z-index: 1; + z-index: 6; + isolation: isolate; + overflow: visible; background: var(--color-surface); } @@ -92,9 +95,10 @@ .sidebarDock { position: absolute; - right: var(--space-1); bottom: var(--space-3); - left: calc(var(--space-1) - (var(--rail-width) * 0.9)); + left: calc(var(--space-1) + (var(--rail-width) * 0.1)); + width: calc(var(--sidebar-width) + (var(--rail-width) * 0.9) - var(--space-2)); + right: auto; z-index: calc(var(--z-modal) + 1); pointer-events: none; diff --git a/Frontend/src/components/shell/AppShell/AppShell.tsx b/Frontend/src/components/shell/AppShell/AppShell.tsx index e57fb8f..cd17542 100644 --- a/Frontend/src/components/shell/AppShell/AppShell.tsx +++ b/Frontend/src/components/shell/AppShell/AppShell.tsx @@ -26,24 +26,28 @@ export const AppShell = (): JSX.Element => { return (