From 49fbc98dd00d46b0ae75ed4adac9e426de3d2f4c Mon Sep 17 00:00:00 2001 From: Arthur Bassi Date: Fri, 12 Jun 2026 11:44:36 -0300 Subject: [PATCH] feat: add theme system and global application styles --- src/lib/theme/css-vars.ts | 32 ++++ src/lib/theme/mui-theme.ts | 303 +++++++++++++++++++++++++++++++++++++ src/styles/FormPage.css | 151 ------------------ src/styles/fonts.css | 13 ++ src/styles/globals.css | 7 + src/styles/motion.css | 5 + src/styles/theme.css | 290 +++++++++++++++++++++++++++++++++++ src/styles/typography.css | 73 +++++++++ 8 files changed, 723 insertions(+), 151 deletions(-) create mode 100644 src/lib/theme/css-vars.ts create mode 100644 src/lib/theme/mui-theme.ts delete mode 100644 src/styles/FormPage.css create mode 100644 src/styles/fonts.css create mode 100644 src/styles/globals.css create mode 100644 src/styles/motion.css create mode 100644 src/styles/theme.css create mode 100644 src/styles/typography.css diff --git a/src/lib/theme/css-vars.ts b/src/lib/theme/css-vars.ts new file mode 100644 index 00000000..89dfdf96 --- /dev/null +++ b/src/lib/theme/css-vars.ts @@ -0,0 +1,32 @@ +/** Read a CSS custom property from :root (browser only). */ +export function getCssVar(name: string, fallback = ""): string { + if (typeof document === "undefined") { + return fallback; + } + + const value = getComputedStyle(document.documentElement).getPropertyValue(name).trim(); + return value || fallback; +} + +/** Static fallbacks for SSR / theme creation before paint. */ +export const CSS_VAR_FALLBACKS = { + primary: "#1c75bc", + primaryHover: "#155a92", + primaryForeground: "#ffffff", + destructive: "#dc2626", + success: "#00a79d", + warning: "#d97706", + background: "#ffffff", + foreground: "#262262", + border: "#dfe3ea", + muted: "#e6e7e8", + mutedForeground: "#58595b", + inputBackground: "#f9fafb", + ring: "#1c75bc", + bgPage: "#f9fafb", + radius: "8", + headerStart: "#1b1f52", + headerMid: "#1c4f8f", + headerEnd: "#1c75bc", + headerText: "#ffffff", +} as const; diff --git a/src/lib/theme/mui-theme.ts b/src/lib/theme/mui-theme.ts new file mode 100644 index 00000000..5b2eaa90 --- /dev/null +++ b/src/lib/theme/mui-theme.ts @@ -0,0 +1,303 @@ +import { createTheme, type ThemeOptions } from "@mui/material/styles"; + +import { CSS_VAR_FALLBACKS, getCssVar } from "@/lib/theme/css-vars"; + +declare module "@mui/material/styles" { + interface Palette { + header: Palette["primary"]; + } + interface PaletteOptions { + header?: PaletteOptions["primary"]; + } +} + +function v(name: string, fallback: string): string { + return getCssVar(name, fallback); +} + +const primary = v("--primary", CSS_VAR_FALLBACKS.primary); +const primaryHover = v("--primary-hover", CSS_VAR_FALLBACKS.primaryHover); +const primaryForeground = v("--primary-foreground", CSS_VAR_FALLBACKS.primaryForeground); +const destructive = v("--destructive", CSS_VAR_FALLBACKS.destructive); +const success = v("--success", CSS_VAR_FALLBACKS.success); +const warning = v("--warning", CSS_VAR_FALLBACKS.warning); +const background = v("--background", CSS_VAR_FALLBACKS.background); +const foreground = v("--foreground", CSS_VAR_FALLBACKS.foreground); +const border = v("--border", CSS_VAR_FALLBACKS.border); +const muted = v("--muted", CSS_VAR_FALLBACKS.muted); +const mutedForeground = v("--muted-foreground", CSS_VAR_FALLBACKS.mutedForeground); +const inputBackground = v("--input-background", CSS_VAR_FALLBACKS.inputBackground); +const ring = v("--ring", CSS_VAR_FALLBACKS.ring); +const bgPage = v("--color-bg-page", CSS_VAR_FALLBACKS.bgPage); +const radius = Number.parseInt(v("--radius", CSS_VAR_FALLBACKS.radius).replace("px", ""), 10) || 8; +const headerEnd = v("--color-header-bg-end", CSS_VAR_FALLBACKS.headerEnd); +const headerText = v("--color-header-text", CSS_VAR_FALLBACKS.headerText); + +const fontDisplay = '"Montserrat", ui-sans-serif, system-ui, sans-serif'; +const fontSans = '"DM Sans", ui-sans-serif, system-ui, sans-serif'; + +const shadowSm = v( + "--shadow-sm", + "0 1px 3px rgba(22, 30, 56, .06), 0 1px 2px rgba(22, 30, 56, .04)", +); +const shadowMd = v( + "--shadow-md", + "0 4px 6px -1px rgba(22, 30, 56, .08), 0 2px 4px -2px rgba(22, 30, 56, .06)", +); +const shadowLg = v( + "--shadow-lg", + "0 10px 15px -3px rgba(22, 30, 56, .1), 0 4px 6px -4px rgba(22, 30, 56, .08)", +); + +const themeOptions: ThemeOptions = { + typography: { + fontFamily: fontSans, + fontSize: 14, + fontWeightLight: 300, + fontWeightRegular: 400, + fontWeightMedium: 500, + fontWeightBold: 600, + h1: { fontFamily: fontDisplay, fontWeight: 800, fontSize: "1.75rem", lineHeight: 1.25 }, + h2: { fontFamily: fontDisplay, fontWeight: 700, fontSize: "1.375rem", lineHeight: 1.25 }, + h3: { fontFamily: fontDisplay, fontWeight: 600, fontSize: "1.125rem", lineHeight: 1.25 }, + h4: { fontFamily: fontDisplay, fontWeight: 600, fontSize: "1rem", lineHeight: 1.25 }, + h5: { fontFamily: fontDisplay, fontWeight: 600, fontSize: "0.875rem", lineHeight: 1.25 }, + h6: { fontFamily: fontDisplay, fontWeight: 600, fontSize: "0.8125rem", lineHeight: 1.25 }, + subtitle1: { fontWeight: 600, fontSize: "1rem" }, + subtitle2: { fontWeight: 500, fontSize: "0.875rem", color: mutedForeground }, + body1: { fontSize: "0.875rem", lineHeight: 1.5 }, + body2: { fontSize: "0.8125rem", lineHeight: 1.5, color: mutedForeground }, + caption: { fontSize: "0.75rem", lineHeight: 1.5, color: mutedForeground }, + overline: { + fontSize: "0.6875rem", + fontWeight: 600, + letterSpacing: "0.08em", + textTransform: "uppercase", + color: mutedForeground, + }, + }, + shape: { + borderRadius: radius, + }, + shadows: [ + "none", + shadowSm, + shadowSm, + shadowMd, + shadowMd, + shadowMd, + shadowLg, + shadowLg, + shadowLg, + shadowLg, + shadowLg, + shadowLg, + shadowLg, + shadowLg, + shadowLg, + shadowLg, + shadowLg, + shadowLg, + shadowLg, + shadowLg, + shadowLg, + shadowLg, + shadowLg, + shadowLg, + shadowLg, + ], + palette: { + mode: "light", + primary: { + main: primary, + dark: primaryHover, + contrastText: primaryForeground, + }, + secondary: { + main: muted, + contrastText: foreground, + }, + error: { + main: destructive, + contrastText: "#ffffff", + }, + success: { + main: success, + contrastText: "#ffffff", + }, + warning: { + main: warning, + contrastText: "#ffffff", + }, + background: { + default: bgPage, + paper: background, + }, + text: { + primary: foreground, + secondary: mutedForeground, + }, + divider: border, + header: { + main: headerEnd, + contrastText: headerText, + }, + }, + components: { + MuiCssBaseline: { + styleOverrides: { + body: { + fontSize: 14, + backgroundColor: bgPage, + color: foreground, + }, + }, + }, + MuiButton: { + defaultProps: { + disableElevation: true, + }, + styleOverrides: { + root: { + borderRadius: v("--radius-md", "6px"), + textTransform: "none", + fontWeight: 600, + fontSize: "0.875rem", + transition: `background-color var(--duration-fast, 150ms) var(--ease-default, ease), box-shadow var(--duration-fast, 150ms) var(--ease-default, ease)`, + "&:focus-visible": { + outline: `2px solid ${ring}`, + outlineOffset: 2, + }, + }, + containedPrimary: { + "&:hover": { + backgroundColor: primaryHover, + }, + }, + outlined: { + borderColor: border, + "&:hover": { + borderColor: v("--border-hover", border), + backgroundColor: v("--secondary", "#f1f4f9"), + }, + }, + }, + }, + MuiTextField: { + defaultProps: { + size: "small", + }, + }, + MuiOutlinedInput: { + styleOverrides: { + root: { + backgroundColor: inputBackground, + borderRadius: v("--radius-md", "6px"), + "& .MuiOutlinedInput-notchedOutline": { + borderColor: border, + }, + "&:hover .MuiOutlinedInput-notchedOutline": { + borderColor: v("--border-hover", border), + }, + "&.Mui-focused .MuiOutlinedInput-notchedOutline": { + borderColor: ring, + borderWidth: 2, + }, + "&.Mui-focused": { + boxShadow: `0 0 0 3px color-mix(in srgb, ${ring} 25%, transparent)`, + }, + }, + input: { + fontSize: "0.875rem", + padding: "8.5px 13px", + "&.MuiInputBase-inputAdornedEnd": { + paddingRight: 40, + }, + }, + }, + }, + MuiTableCell: { + styleOverrides: { + root: { + fontSize: "0.875rem", + borderColor: border, + padding: "10px 16px", + }, + head: { + fontWeight: 600, + backgroundColor: v("--color-bg-muted", "#f1f4f9"), + color: foreground, + }, + }, + }, + MuiTableRow: { + styleOverrides: { + root: { + "&:last-child td": { + borderBottom: 0, + }, + }, + }, + }, + MuiChip: { + styleOverrides: { + root: { + borderRadius: v("--radius-sm", "4px"), + fontWeight: 500, + fontSize: "0.75rem", + }, + outlined: { + borderColor: border, + }, + }, + }, + MuiPaper: { + defaultProps: { + elevation: 0, + }, + styleOverrides: { + root: { + backgroundImage: "none", + borderRadius: radius, + }, + outlined: { + borderColor: border, + }, + }, + }, + MuiDialog: { + styleOverrides: { + paper: { + borderRadius: v("--radius-xl", "12px"), + boxShadow: shadowLg, + }, + }, + }, + MuiAlert: { + styleOverrides: { + root: { + borderRadius: v("--radius-md", "6px"), + fontSize: "0.875rem", + }, + standardSuccess: { + backgroundColor: v("--status-completed-bg", "#f0fdf4"), + color: v("--status-completed-text", "#166534"), + }, + standardError: { + backgroundColor: v("--doc-no-bg", "#fff1f2"), + color: v("--doc-no-text", "#be123c"), + }, + standardWarning: { + backgroundColor: v("--status-pending-bg", "#fff7ed"), + color: v("--status-pending-text", "#92400e"), + }, + standardInfo: { + backgroundColor: v("--color-primary-tint", "#e6f4fb"), + color: v("--sidebar-accent-foreground", "#155a92"), + }, + }, + }, + }, +}; + +export const muiTheme = createTheme(themeOptions); diff --git a/src/styles/FormPage.css b/src/styles/FormPage.css deleted file mode 100644 index 0079d9c0..00000000 --- a/src/styles/FormPage.css +++ /dev/null @@ -1,151 +0,0 @@ -.formPage { - background: #f5f5f5; - min-height: 100vh; - padding: 0; -} - -.panel { - margin-bottom: 20px; - background-color: #fff; - border: 1px solid transparent; - border-radius: 4px; - box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); -} - -.panel-default { - border-color: #ddd; -} - -.panel-connected { - border-radius: 0; - border: none; - box-shadow: none; -} - -.panel-body { - padding: 15px; -} - -/* Breadcrumb */ -.breadcrumb { - padding: 8px 15px; - margin-bottom: 20px; - list-style: none; - background-color: #f5f5f5; - border-radius: 4px; -} - -.breadcrumb-alt { - background-color: white; - border-bottom: 1px solid #e5e5e5; - padding: 10px 20px; - margin: 0 0 20px 0; - border-radius: 0; -} - -.breadcrumb > li { - display: inline-block; -} - -.breadcrumb > li + li:before { - content: ">\00a0"; - padding: 0 5px; - color: #ccc; -} - -.breadcrumb > li a { - color: #0088cc; - text-decoration: none; -} - -.breadcrumb > li a:hover { - text-decoration: underline; -} - -.breadcrumb > .active { - color: #00a0d1; - font-weight: bold; - cursor: default; -} - -/* Container */ -.container { - padding-right: 15px; - padding-left: 15px; - margin-right: auto; - margin-left: auto; -} - -/* Form Header */ -.header { - margin-bottom: 15px; -} - -.heading { - margin: 0 0 10px 0; - font-weight: 300; - line-height: 1.1; - color: #333; -} - -.heading.thin { - font-weight: 300; - font-size: 24px; - display: flex; - align-items: center; - gap: 10px; -} - -.heading.text-uppercase { - text-transform: uppercase; - font-size: 14px; - margin-bottom: 15px; -} - -.text-primary { - color: #0088cc; -} - -.fa-stack { - position: relative; - display: inline-block; - width: 2em; - height: 2em; - line-height: 2em; - vertical-align: middle; -} - -.fa-stack-1x, -.fa-stack-2x { - position: absolute; - left: 0; - width: 100%; - text-align: center; -} - -.fa-stack-2x { - font-size: 2em; -} - -.fa-stack-1x { - font-size: 1em; - line-height: inherit; -} - -.fa-inverse { - color: #fff; -} - -/* Form Groups */ -.form-group { - margin-bottom: 15px; - position: relative; -} - -/* Row and Columns */ -.row { - margin-right: -15px; - margin-left: -15px; - display: flex; - flex-wrap: wrap; -} \ No newline at end of file diff --git a/src/styles/fonts.css b/src/styles/fonts.css new file mode 100644 index 00000000..eb862666 --- /dev/null +++ b/src/styles/fonts.css @@ -0,0 +1,13 @@ +/* Seahaven DS — self-hosted via @fontsource (matches theme.css stacks) */ + +@import "@fontsource/montserrat/600.css"; +@import "@fontsource/montserrat/700.css"; +@import "@fontsource/montserrat/800.css"; + +@import "@fontsource/dm-sans/400.css"; +@import "@fontsource/dm-sans/500.css"; +@import "@fontsource/dm-sans/600.css"; + +@import "@fontsource/jetbrains-mono/400.css"; +@import "@fontsource/jetbrains-mono/500.css"; +@import "@fontsource/jetbrains-mono/600.css"; diff --git a/src/styles/globals.css b/src/styles/globals.css new file mode 100644 index 00000000..e776d53a --- /dev/null +++ b/src/styles/globals.css @@ -0,0 +1,7 @@ +@import "tailwindcss"; +@import "./fonts.css"; +@import "./theme.css"; +@import "./typography.css"; +@import "./motion.css"; + +@custom-variant dark (&:is(.dark *)); diff --git a/src/styles/motion.css b/src/styles/motion.css new file mode 100644 index 00000000..156e82ac --- /dev/null +++ b/src/styles/motion.css @@ -0,0 +1,5 @@ +:root { + --duration-fast: 150ms; + --duration-normal: 250ms; + --ease-default: cubic-bezier(0.4, 0, 0.2, 1); +} diff --git a/src/styles/theme.css b/src/styles/theme.css new file mode 100644 index 00000000..787445be --- /dev/null +++ b/src/styles/theme.css @@ -0,0 +1,290 @@ +:root { + /* Body typography reference — do not set on html (rem/Tailwind use 16px root) */ + --font-size: 14px; + + /* ── TYPOGRAPHY (runtime CSS vars — used by inline styles + Tailwind) */ + --font-family-display: "Montserrat", ui-sans-serif, system-ui, sans-serif; + --font-family-sans: "DM Sans", ui-sans-serif, system-ui, sans-serif; + --font-family-mono: "JetBrains Mono", ui-monospace, monospace; + --font-display: var(--font-family-display); + --font-sans: var(--font-family-sans); + --font-mono: var(--font-family-mono); + --font-heading: var(--font-family-display); + + /* ── TYPOGRAPHY SCALE ───────────────────────────────────────────── */ + --text-xs: 11px; + --text-sm: 12px; + --text-base-sm: 13px; + --text-base: 14px; + --text-md: 16px; + --text-lg: 18px; + --text-xl: 22px; + --text-2xl: 28px; + --leading-tight: 1.25; + --leading-normal: 1.5; + --leading-relaxed: 1.625; + --tracking-tight: -0.01em; + --tracking-normal: 0; + --tracking-wide: 0.05em; + --tracking-wider: 0.08em; + + /* ── LAYOUT SPACING ─────────────────────────────────────────────── */ + --spacing-sidebar: 220px; + --spacing-topbar: 64px; + --spacing-sidebar-collapsed: 56px; + + /* ── RADIUS SCALE (scale resolves to 4 / 6 / 8 / 12) ────────────── */ + --radius: 8px; + --radius-md: 6px; + --radius-sm: 4px; + --radius-xl: 12px; + + /* ── ELEVATION ──────────────────────────────────────────────────── */ + --shadow-sm: 0 1px 3px rgba(22, 30, 56, 0.06), 0 1px 2px rgba(22, 30, 56, 0.04); + --shadow-md: 0 4px 6px -1px rgba(22, 30, 56, 0.08), 0 2px 4px -2px rgba(22, 30, 56, 0.06); + --shadow-lg: 0 10px 15px -3px rgba(22, 30, 56, 0.1), 0 4px 6px -4px rgba(22, 30, 56, 0.08); + --shadow-brand-mark: 0 2px 6px -1px rgba(28, 117, 188, 0.45); + + /* ── SHADCN BASE (Seahaven DS) ──────────────────────────────────── */ + --background: #ffffff; + --foreground: #262262; + --placeholder-foreground: color-mix(in srgb, var(--foreground) 50%, transparent); + --card: #ffffff; + --card-foreground: #262262; + --popover: #ffffff; + --popover-foreground: #262262; + --primary: #1c75bc; + --primary-hover: #155a92; + --primary-foreground: #ffffff; + --secondary: #f1f4f9; + --secondary-foreground: #262262; + --muted: #e6e7e8; + --muted-foreground: #58595b; + --accent: #27aae1; + --accent-foreground: #ffffff; + --success: #00a79d; + --warning: #d97706; + --destructive: #dc2626; + --destructive-foreground: #ffffff; + --border: #dfe3ea; + --border-hover: #c8d0e0; + --input: transparent; + --input-background: #f9fafb; + --switch-background: #c8d0e0; + --ring: #1c75bc; + + --chart-1: #1c75bc; + --chart-2: #00a79d; + --chart-3: #d97706; + --chart-4: #7c3aed; + --chart-5: #dc2626; + + /* ── SIDEBAR (light surface — Seahaven DS) ──────────────────────── */ + --sidebar: #f6f8fb; + --sidebar-bg: #f6f8fb; + --sidebar-foreground: #262262; + --sidebar-primary: #1c75bc; + --sidebar-primary-foreground: #ffffff; + --sidebar-accent: #e6f4fb; + --sidebar-accent-foreground: #155a92; + --sidebar-border: #dfe3ea; + --sidebar-ring: #1c75bc; + + /* ── SEAHAVEN SEMANTIC ALIASES (consumed across the app) ────────── */ + --color-primary: var(--primary); + --color-primary-hover: var(--primary-hover); + --color-primary-foreground: #ffffff; + --color-primary-tint: #e6f4fb; + --color-accent: var(--accent); + + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--foreground); + --color-ring: var(--ring); + + --color-bg-page: #f9fafb; + --color-bg-surface: var(--card); + --color-bg-muted: #f1f4f9; + --color-border: var(--border); + --color-border-hover: #c8d0e0; + --color-text-primary: var(--foreground); + --color-text-secondary: #58595b; + --color-text-muted: #6b7a96; + --color-text-inverse: #ffffff; + + /* ── SIDEBAR ALIASES (light) ────────────────────────────────────── */ + --color-sidebar-bg: var(--sidebar-bg); + --color-sidebar-text: #262262; + --color-sidebar-text-hover: #155a92; + --color-sidebar-active-bg: var(--sidebar-accent); + --color-sidebar-active-text: var(--sidebar-accent-foreground); + --color-sidebar-hover-bg: #eef2f7; + --color-sidebar-label: #6b7a96; + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-badge-bg: #e6f4fb; + --color-sidebar-badge-text: #155a92; + + /* ── SEMANTIC (universal meaning) ───────────────────────────────── */ + --color-destructive: var(--destructive); + --color-destructive-fg: #ffffff; + --color-warning: var(--warning); + --color-success: var(--success); + + /* ── WO TYPE TOKENS (immutable — CLAUDE.md §3) ──────────────────── */ + --type-pm-bg: #eff6ff; + --type-pm-text: #1d4ed8; + --type-reactive-bg: #fff7ed; + --type-reactive-text: #c2410c; + --type-emergency-bg: #fef2f2; + --type-emergency-text: #b91c1c; + --type-addon-bg: #f5f3ff; + --type-addon-text: #6d28d9; + --type-overdue-bg: #fff1f2; + --type-overdue-text: #9f1239; + + /* ── WO STATUS TOKENS (immutable) ───────────────────────────────── */ + --status-incomplete-bg: #f3f4f6; + --status-incomplete-text: #374151; + --status-pending-bg: #fff7ed; + --status-pending-text: #92400e; + --status-scheduled-bg: #eff6ff; + --status-scheduled-text: #1e40af; + --status-inroute-bg: #f0f9ff; + --status-inroute-text: #0369a1; + --status-onsite-bg: #ecfdf5; + --status-onsite-text: #065f46; + --status-inprogress-bg: #eff6ff; + --status-inprogress-text: #1d4ed8; + --status-completed-bg: #f0fdf4; + --status-completed-text: #166534; + --status-rescheduled-bg: #fffbeb; + --status-rescheduled-text: #92400e; + --status-canceled-bg: #f9fafb; + --status-canceled-text: #6b7280; + --status-pendingquote-bg: #faf5ff; + --status-pendingquote-text: #6b21a8; + --status-pastdue-bg: #fff1f2; + --status-pastdue-text: #9f1239; + + /* ── COMPLETION DOCUMENT (immutable) ────────────────────────────── */ + --doc-yes-bg: #f0fdf4; + --doc-yes-text: #166534; + --doc-no-bg: #fff1f2; + --doc-no-text: #be123c; + --doc-nn-bg: #f3f4f6; + --doc-nn-text: #6b7280; + + /* ── CARRIED OVER / RESCHEDULE INDICATORS (immutable) ───────────── */ + --indicator-co-1-bg: #fffbeb; + --indicator-co-1-text: #92400e; + --indicator-co-2-bg: #fef3c7; + --indicator-co-2-text: #b45309; + --indicator-co-3-bg: #fef2f2; + --indicator-co-3-text: #9f1239; + + /* ── SLA (immutable) ────────────────────────────────────────────── */ + --sla-safe: #16a34a; + --sla-warning: #d97706; + --sla-critical: #dc2626; + + /* ── LAB (experiment UI accents) ────────────────────────────────── */ + --color-lab-accent-bg: #fef3c7; + --color-lab-accent-text: #92400e; + --color-lab-accent-border: #f59e0b; + + /* ── HEADER (app shell top bar) ─────────────────────────────────── */ + --color-header-bg-start: #1b1f52; + --color-header-bg-mid: #1c4f8f; + --color-header-bg-end: var(--primary); + --color-header-gradient: linear-gradient( + 100deg, + var(--color-header-bg-start) 0%, + var(--color-header-bg-mid) 58%, + var(--color-header-bg-end) 100% + ); + --color-header-shadow: 0 1px 0 rgba(38, 34, 98, 0.18), 0 6px 18px -10px rgba(28, 117, 188, 0.5); + --color-header-surface: rgba(255, 255, 255, 0.08); + --color-header-surface-hover: rgba(255, 255, 255, 0.18); + --color-header-chip-hover: rgba(255, 255, 255, 0.12); + --color-header-icon: #eaf3fb; + --color-header-text: #ffffff; + --color-header-text-muted: #bdd8f0; + --color-header-avatar-ring: rgba(255, 255, 255, 0.25); + + /* ── SERVICE TYPE TOKENS (Completion Docs) ──────────────────────── */ + --service-scrubber-pit-bg: #eff6ff; + --service-scrubber-pit-text: #1d4ed8; + --service-backflow-preventers-bg: #ecfdf5; + --service-backflow-preventers-text: #065f46; + --service-tank-water-heater-bg: #fff7ed; + --service-tank-water-heater-text: #c2410c; + --service-floor-drain-bg: #f0f9ff; + --service-floor-drain-text: #0369a1; + --service-hvac-bg: #f5f3ff; + --service-hvac-text: #6d28d9; + --service-parking-lot-lighting-bg: #fffbeb; + --service-parking-lot-lighting-text: #92400e; + --service-pumping-systems-bg: #faf5ff; + --service-pumping-systems-text: #6b21a8; + --service-door-hardware-bg: #f0fdf4; + --service-door-hardware-text: #166534; + --service-pressure-reducing-valve-bg: #fff1f2; + --service-pressure-reducing-valve-text: #9f1239; + --service-fire-suppression-bg: #fef2f2; + --service-fire-suppression-text: #b91c1c; +} + +@theme inline { + --font-display: var(--font-family-display); + --font-sans: var(--font-family-sans); + --font-mono: var(--font-family-mono); + --font-heading: var(--font-family-display); + + --spacing-sidebar: var(--spacing-sidebar); + --spacing-topbar: var(--spacing-topbar); + --spacing-sidebar-collapsed: var(--spacing-sidebar-collapsed); + + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-card: var(--card); + --color-card-foreground: var(--card-foreground); + --color-popover: var(--popover); + --color-popover-foreground: var(--popover-foreground); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); + --color-destructive: var(--destructive); + --color-destructive-foreground: var(--destructive-foreground); + --color-border: var(--border); + --color-input: var(--input); + --color-input-background: var(--input-background); + --color-ring: var(--ring); + --color-chart-1: var(--chart-1); + --color-chart-2: var(--chart-2); + --color-chart-3: var(--chart-3); + --color-chart-4: var(--chart-4); + --color-chart-5: var(--chart-5); + --color-sidebar: var(--sidebar); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-ring: var(--sidebar-ring); + --color-success: var(--success); + --color-warning: var(--warning); + + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + + --shadow-sm: var(--shadow-sm); + --shadow-md: var(--shadow-md); + --shadow-lg: var(--shadow-lg); +} diff --git a/src/styles/typography.css b/src/styles/typography.css new file mode 100644 index 00000000..720652bb --- /dev/null +++ b/src/styles/typography.css @@ -0,0 +1,73 @@ +html { + /* 16px root — Tailwind spacing/radius and MUI rem scale assume browser default */ + font-size: 16px; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +body { + margin: 0; + font-family: var(--font-sans); + /* font-size: var(--text-base); */ + /* line-height: var(--leading-normal); */ + color: var(--foreground); + background-color: var(--color-bg-page); +} + +h1, +h2, +h3, +h4, +h5, +h6 { + font-family: var(--font-heading); + color: var(--foreground); + margin: 0; +} + +h1 { + font-size: var(--text-2xl); + font-weight: 800; +} + +h2 { + font-size: var(--text-xl); + font-weight: 700; +} + +h3 { + font-size: var(--text-lg); + font-weight: 600; +} + +h4 { + font-size: var(--text-md); + font-weight: 600; +} + +h5 { + font-size: var(--text-base); + font-weight: 600; +} + +h6 { + font-size: var(--text-base-sm); + font-weight: 600; +} + +.text-caption { + font-family: var(--font-sans); + font-size: var(--text-sm); + line-height: var(--leading-normal); + color: var(--muted-foreground); +} + +.text-label { + font-family: var(--font-sans); + font-size: var(--text-xs); + font-weight: 600; + line-height: 1.5; + letter-spacing: var(--tracking-wider); + text-transform: uppercase; + color: var(--color-sidebar-label); +}