diff --git a/apps/web/src/m3-theme.scss b/apps/web/src/m3-theme.scss index f37a3465b..57ce4decc 100644 --- a/apps/web/src/m3-theme.scss +++ b/apps/web/src/m3-theme.scss @@ -1,39 +1,128 @@ @use 'sass:map'; @use '@angular/material' as mat; -@use 'styles' ; +@use 'styles'; @include mat.elevation-classes(); @include mat.app-background(); -$my-typography: mat.m2-define-typography-config(); +// ─── Typography ───────────────────────────────────────────────────────────── +$my-typography: mat.m2-define-typography-config( + $font-family: "'DM Sans', 'Inter', sans-serif", +); @include mat.typography-hierarchy($my-typography); -// Note: Color palettes are generated from primary: #ffffff -$_palettes: ( - primary: mat.$blue-palette, - tertiary: mat.$red-palette, +// ─── M3 Themes ─────────────────────────────────────────────────────────────── +$light-theme: mat.define-theme( + ( + color: ( + theme-type: light, + primary: mat.$violet-palette, + tertiary: mat.$cyan-palette, + ), + typography: ( + plain-family: "'DM Sans', 'Inter', sans-serif", + brand-family: "'DM Sans', 'Inter', sans-serif", + ), + ) ); -$_primary: map.get($_palettes, primary); - -$light-theme: mat.define-theme(( - color: ( - theme-type: light, - primary: $_primary, - ), -)); - -$dark-theme: mat.define-theme(( - color: ( - theme-type: dark, - primary: $_primary, - ), -)); +$dark-theme: mat.define-theme( + ( + color: ( + theme-type: dark, + primary: mat.$violet-palette, + tertiary: mat.$cyan-palette, + ), + typography: ( + plain-family: "'DM Sans', 'Inter', sans-serif", + brand-family: "'DM Sans', 'Inter', sans-serif", + ), + ) +); +// ─── Apply Themes ──────────────────────────────────────────────────────────── html { - @include mat.all-component-themes($light-theme); + @include mat.all-component-themes($light-theme); - .dark-theme { - @include mat.all-component-colors($dark-theme); - } -} \ No newline at end of file + // ── Light theme design tokens ───────────────────────────────────────── + --app-shell-bg: #eef0f3; + --app-rail-bg: #e8eaed; + --app-header-bg: #eef0f3; + --app-content-bg: #f5f6f8; + --app-rail-border: rgba(0, 0, 0, 0.08); + --app-search-bg: rgba(255, 255, 255, 0.7); + --app-search-border: rgba(0, 0, 0, 0.1); + + // Widget surfaces — no borders, layered backgrounds + --app-widget-bg: #ffffff; + --app-widget-header-bg: #fafbfc; + --app-widget-border: transparent; + --app-widget-shadow: 0 1px 3px rgba(0, 0, 0, 0.06), 0 4px 12px rgba(0, 0, 0, 0.04); + --app-widget-header-border: rgba(0, 0, 0, 0.06); + --app-card-hover-bg: #f0f2f5; + + // Typography — subdued, not pure black + --app-heading-color: #1a1d23; + --app-body-color: #4a5060; + --app-muted-color: #8891a4; + --app-eyebrow-color: #7c8495; + + // Provider accent colors + --app-provider-xtream: #6366f1; + --app-provider-stalker: #10b981; + --app-provider-m3u: #d97706; + + // Hero + --app-accent-gradient: linear-gradient( + 135deg, + rgba(99, 102, 241, 0.08) 0%, + rgba(168, 85, 247, 0.05) 100% + ); + --app-hero-surface: rgba(99, 102, 241, 0.04); + + .dark-theme { + @include mat.all-component-colors($dark-theme); + + // ── Dark graphite theme ──────────────────────────────────────────── + // Inspired by both Slack (sidebar distinction) and GitButler (data density) + // Key principle: clearly distinct surface layers using graphite steps + // Darkest → lightest: #0d0f12 → #141720 → #1c2030 → #222840 + + // App shell & navigation + --app-shell-bg: #0d0f12; + --app-rail-bg: #111318; + --app-header-bg: #111318; + --app-content-bg: #161a22; + --app-rail-border: rgba(255, 255, 255, 0.05); + --app-search-bg: rgba(255, 255, 255, 0.05); + --app-search-border: rgba(255, 255, 255, 0.09); + + // Widget surfaces — NO border, surface contrast does the work + // Widgets sit on content-bg (#161a22), so widget-bg is notably lighter + --app-widget-bg: #1e2330; + --app-widget-header-bg: #232840; + --app-widget-border: transparent; + --app-widget-shadow: none; + --app-widget-header-border: rgba(255, 255, 255, 0.05); + --app-card-hover-bg: #252b3a; + + // Typography — NOT pure white. Warm graphite-tinted whites + --app-heading-color: #d8dce8; // soft off-white, not #fff + --app-body-color: #8b93a8; // muted blue-grey + --app-muted-color: #5c6478; // dimmed + --app-eyebrow-color: #6b7494; // eyebrow labels + + // Provider accents (slightly brighter for dark bg) + --app-provider-xtream: #818cf8; + --app-provider-stalker: #34d399; + --app-provider-m3u: #fbbf24; + + // Hero section + --app-accent-gradient: linear-gradient( + 135deg, + rgba(99, 102, 241, 0.12) 0%, + rgba(168, 85, 247, 0.07) 100% + ); + --app-hero-surface: rgba(30, 35, 48, 0.9); + } +} diff --git a/apps/web/src/styles.scss b/apps/web/src/styles.scss index cdc8a8c5a..8eff31530 100644 --- a/apps/web/src/styles.scss +++ b/apps/web/src/styles.scss @@ -1,8 +1,21 @@ -// @import 'themes/'; +// ─── Tailwind CSS ───────────────────────────────────────────────────────────── +// Preflight is disabled in tailwind.config.js to avoid conflicts with +// Angular Material. Only components + utilities are loaded. +@tailwind base; +@tailwind components; +@tailwind utilities; + +// ─── Third-party ───────────────────────────────────────────────────────────── @import '@yangkghjh/videojs-aspect-ratio-panel/dist/videojs-aspect-ratio-panel.css'; @import '../../../libs/ui/components/src/lib/resizable/resizable.scss'; -// macOS specific styles - space for traffic lights +// ─── Google Fonts ───────────────────────────────────────────────────────────── +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap'); + +// ─── Shared Components ──────────────────────────────────────────────────────── +@import './nav-list.scss'; + +// ─── macOS specific: space for traffic lights ───────────────────────────────── app-root.macos-platform app-header .modern-header { padding-left: 80px; } @@ -15,13 +28,20 @@ app-root.macos-platform app-navigation .portal-status-container { padding-top: 30px; } +// ─── Base ───────────────────────────────────────────────────────────────────── body, html { user-select: none; margin: 0; padding: 0; height: 100%; - font-family: Roboto, 'Helvetica Neue', sans-serif; + font-family: + 'Inter', + 'Roboto', + 'Helvetica Neue', + sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; } // Re-enable context menu and text selection for input fields @@ -37,9 +57,7 @@ textarea { } } -.full-width { - width: 100%; -} +// ─── Utilities ──────────────────────────────────────────────────────────────── .mat-tab-label-active { opacity: 1 !important; } @@ -51,26 +69,36 @@ textarea, outline: none; } +// ─── Dark Theme ─────────────────────────────────────────────────────────────── .dark-theme { + background: var(--mat-sys-surface) !important; + + // Refined scrollbar for dark mode ::-webkit-scrollbar { - overflow-y: scroll; + width: 6px; + height: 6px; + } + + ::-webkit-scrollbar-track { + background: transparent; } ::-webkit-scrollbar-thumb { - background-color: rgb(60 60 60); - border-radius: 10px; + background-color: var(--mat-sys-outline-variant); + border-radius: 999px; + + &:hover { + background-color: var(--mat-sys-outline); + } } } -.mb-10 { - margin-bottom: 10px; -} - +// ─── Dialogs ────────────────────────────────────────────────────────────────── .about-dialog-overlay { .mat-mdc-dialog-surface { text-align: center; - background: black !important; - color: #fff !important; + background: var(--mat-sys-surface) !important; + color: var(--mat-sys-on-surface) !important; padding: 10px; } } @@ -94,3 +122,4 @@ textarea, .global-search-overlay .mat-mdc-dialog-container { app-region: no-drag; } + diff --git a/apps/web/tailwind.config.js b/apps/web/tailwind.config.js new file mode 100644 index 000000000..371bbc8c5 --- /dev/null +++ b/apps/web/tailwind.config.js @@ -0,0 +1,56 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + // Content paths scoped to the Angular web app and shared libs + content: [ + './apps/web/src/**/*.{html,ts,scss}', + './libs/**/*.{html,ts,scss}', + ], + // Match Angular Material's dark theme class strategy + darkMode: 'class', + theme: { + extend: { + fontFamily: { + sans: ['Inter', 'Roboto', 'Helvetica Neue', 'sans-serif'], + }, + colors: { + // Surface colors wired to Material sys vars so Tailwind + // utilities automatically respect the active theme + surface: 'var(--mat-sys-surface)', + 'surface-low': 'var(--mat-sys-surface-container-low)', + 'surface-mid': 'var(--mat-sys-surface-container)', + 'surface-high': 'var(--mat-sys-surface-container-high)', + 'on-surface': 'var(--mat-sys-on-surface)', + 'on-surface-variant': 'var(--mat-sys-on-surface-variant)', + outline: 'var(--mat-sys-outline)', + 'outline-variant': 'var(--mat-sys-outline-variant)', + primary: 'var(--mat-sys-primary)', + 'on-primary': 'var(--mat-sys-on-primary)', + 'primary-container': 'var(--mat-sys-primary-container)', + 'on-primary-container': 'var(--mat-sys-on-primary-container)', + secondary: 'var(--mat-sys-secondary)', + 'secondary-container': 'var(--mat-sys-secondary-container)', + 'on-secondary-container': + 'var(--mat-sys-on-secondary-container)', + tertiary: 'var(--mat-sys-tertiary)', + 'tertiary-container': 'var(--mat-sys-tertiary-container)', + error: 'var(--mat-sys-error)', + 'error-container': 'var(--mat-sys-error-container)', + }, + borderRadius: { + widget: '14px', + pill: '999px', + card: '18px', + }, + boxShadow: { + widget: '0 1px 4px rgba(0,0,0,0.12), 0 0 0 1px rgba(255,255,255,0.04)', + 'widget-hover': + '0 4px 16px rgba(0,0,0,0.22), 0 0 0 1px rgba(255,255,255,0.06)', + card: '0 2px 8px rgba(0,0,0,0.15)', + }, + }, + }, + // CRITICAL: disable preflight to avoid conflicts with Angular Material + corePlugins: { + preflight: false, + }, +};