@brightweblabs/theme 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,10 +2,65 @@
2
2
 
3
3
  BrightWeb themes follow one cascade: L0 brand primitives feed L1 shadcn semantics, L2 describes brand-agnostic states, and L3 exposes component and experience controls. Package components consume L1-L3 tokens only; theme files such as `themes/mq.css` override values without changing component code.
4
4
 
5
+ ## Typography and font loading
6
+
7
+ Geist is the offline-safe platform default. Applications load `GeistSans` and `GeistMono` from the `geist/font` package and attach their variables to the app root (the platform layout uses `<body>`):
8
+
9
+ ```tsx
10
+ import { GeistMono } from "geist/font/mono";
11
+ import { GeistSans } from "geist/font/sans";
12
+
13
+ <body className={`${GeistSans.variable} ${GeistMono.variable}`}>
14
+ ```
15
+
16
+ The family contract starts at `--font-body`, `--font-heading`, and `--font-code`; Tailwind-facing `--font-sans`, `--font-display`, and `--font-mono` aliases resolve through those tokens. Raw sizes live only in the `--text-ui-*`, `--type-*`, weight, leading, and tracking scales in `src/tokens.css`. Components consume semantic utilities such as `text-ui-body`, `portal-title`, and `heading-2`, so changing a family or scale does not require component edits.
17
+
18
+ Client themes can override the family without forking recipes. The MQ path is:
19
+
20
+ ```css
21
+ @import "@brightweblabs/theme/css";
22
+ @import "@brightweblabs/theme/themes/mq";
23
+ @import "@brightweblabs/theme/themes/mq-aliases";
24
+
25
+ :root {
26
+ --font-client-body: "Mulish", sans-serif;
27
+ --font-body: var(--font-client-body);
28
+ }
29
+ ```
30
+
31
+ `themes/mq.css` supplies MQ color and surface values, while `themes/mq-aliases.css` maps legacy MQ typography names onto the same tokenized scale. Import the MQ stylesheet after the base theme. Keep font loading at the app root; theme packages only select variables and never fetch fonts.
32
+
33
+ ## Runtime theme switching
34
+
35
+ Theme tokens respond to both `html.dark` and `html[data-theme="dark"]`. Platform apps should use the shell-owned controller so those selectors never drift:
36
+
37
+ ```tsx
38
+ import { ThemeProvider, ThemeScript } from "@brightweblabs/app-shell";
39
+
40
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
41
+ return (
42
+ <html lang="en" suppressHydrationWarning>
43
+ <head>
44
+ <ThemeScript defaultTheme="light" />
45
+ </head>
46
+ <body>
47
+ <ThemeProvider defaultTheme="light">{children}</ThemeProvider>
48
+ </body>
49
+ </html>
50
+ );
51
+ }
52
+ ```
53
+
54
+ `ThemeScript` applies the stored `bw-theme` mode before hydration to prevent a light-theme flash. `ThemeProvider` keeps the root class, `data-theme`, and `color-scheme` synchronized, persists `"light" | "dark" | "system"`, and follows `prefers-color-scheme` while system mode is selected. Account menus rendered below the provider use this controller automatically.
55
+
5
56
  ## L3 visual tokens
6
57
 
7
58
  | Tokens | Purpose |
8
59
  | --- | --- |
60
+ | `--font-body`, `--font-heading`, `--font-code`; `--font-sans`, `--font-display`, `--font-mono` | Root font families and their Tailwind-facing aliases. |
61
+ | `--type-weight-*`, `--type-leading-*`, `--type-tracking-*` | Shared typography weight, rhythm, and tracking primitives. |
62
+ | `--text-ui-*`, `--type-paragraph*`, `--type-heading-2`, `--type-label` | Tokenized platform and compatibility type scales. |
63
+ | `--foreground-muted-accessible`, `--foreground-inverse-muted`, `--foreground-inverse-subtle` | Contrast-safe secondary and inverse text roles. |
9
64
  | `--text-ui-chip`, `--text-ui-action`, `--text-ui-calendar` | Compact control and calendar type sizes. |
10
65
  | `--text-ui-shell-title`, `--text-ui-report-title`, `--text-ui-report-title-lg`, `--text-ui-report-metric` | Shell and CRM report display type sizes. |
11
66
  | `--surface-overlay`, `--surface-overlay-strong`, `--surface-tooltip`, `--surface-badge-tint` | Overlay, tooltip, and tint surfaces. |
@@ -15,6 +70,7 @@ BrightWeb themes follow one cascade: L0 brand primitives feed L1 shadcn semantic
15
70
  | `--surface-account-team`, `--surface-account-client`, `--surface-account`, `--surface-account-hover`, `--account-presence` | Account rail identity, presence, and interaction surfaces. |
16
71
  | `--account-presence-size`, `--radius-swatch`, `--radius-pill`, `--radius-scrollbar` | Presence, swatch, pill, avatar, and scrollbar geometry. |
17
72
  | `--row-hover-sweep`, `--surface-status-success`, `--surface-status-warning`, `--surface-status-danger` | Shared row and status surfaces. |
73
+ | `--dashboard-*` | Aggregate dashboard glows, shadows, task groups, milestone states, and local shell contrast recipes. |
18
74
  | `--tint-soft-border`, `--tint-soft-bg`, `--tint-soft-hover`, `--tint-hero-border`, `--tint-hero-bg`, `--tint-hero-fg`, `--tint-hero-hover` | Dynamic tint recipes resolved where a component supplies `--tint`. |
19
75
  | `--scrollbar-thumb`, `--scrollbar-thumb-hover`, `--scrollbar-size` | Shared scrollbar colour and geometry. |
20
76
  | `--toast-success-bg`, `--toast-success-border`, `--toast-warning-bg`, `--toast-warning-text`, `--toast-warning-border`, `--toast-error-bg`, `--toast-error-border`, `--toast-info-bg`, `--toast-info-text`, `--toast-info-border` | Sonner semantic toast palette. |
@@ -36,3 +92,5 @@ BrightWeb themes follow one cascade: L0 brand primitives feed L1 shadcn semantic
36
92
  | `--section-icon-size`, `--surface-enter-offset`, `--space-eyebrow-y` | Shared surface icon, entrance, and eyebrow geometry. |
37
93
 
38
94
  All tokens above have neutral defaults in `src/tokens.css`. Values that are derived from semantic tokens automatically follow `themes/mq.css`; a direct MQ declaration is needed only when MQ intentionally differs from the neutral default. `themes/mq-aliases.css` remains the compatibility layer for legacy MQ primitive and typography names.
95
+
96
+ The base stylesheet also provides the global `prefers-reduced-motion: reduce` contract. It disables smooth scrolling and collapses animation and transition timing; skeleton and package styles add static fallbacks where their animated state would otherwise remain off-screen or transparent.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@brightweblabs/theme",
3
3
  "private": false,
4
- "version": "0.3.0",
4
+ "version": "0.4.0",
5
5
  "main": "./src/tint.ts",
6
6
  "types": "./src/tint.ts",
7
7
  "files": [
package/src/base.css CHANGED
@@ -14,7 +14,7 @@
14
14
  padding: 0;
15
15
  background: var(--page-background);
16
16
  color: var(--foreground);
17
- font-family: var(--font-sans), sans-serif;
17
+ font-family: var(--font-sans);
18
18
  -webkit-font-smoothing: antialiased;
19
19
  -moz-osx-font-smoothing: grayscale;
20
20
  text-rendering: optimizeLegibility;
@@ -31,4 +31,28 @@
31
31
  input {
32
32
  font: inherit;
33
33
  }
34
+
35
+ /* Hand-rolled controls keep a visible keyboard state. Component utilities
36
+ can replace this fallback with their own focus-visible treatment. */
37
+ :where(button, a[href], [role="button"]):focus-visible {
38
+ outline: 2px solid currentColor;
39
+ outline-offset: 2px;
40
+ }
41
+
42
+ @media (prefers-reduced-motion: reduce) {
43
+ html {
44
+ scroll-behavior: auto !important;
45
+ }
46
+
47
+ *,
48
+ ::before,
49
+ ::after {
50
+ animation-delay: 0ms !important;
51
+ animation-duration: 0ms !important;
52
+ animation-iteration-count: 1 !important;
53
+ scroll-behavior: auto !important;
54
+ transition-delay: 0ms !important;
55
+ transition-duration: 0ms !important;
56
+ }
57
+ }
34
58
  }
package/src/surfaces.css CHANGED
@@ -1,42 +1,47 @@
1
1
  /* ── Shared structural surfaces ───────────────────────────────────────── */
2
- .surface-card {
3
- border-radius: var(--radius-card);
4
- border: 1px solid var(--border-hairline);
5
- background: var(--surface-card);
6
- padding: var(--surface-card-padding);
7
- animation: surface-fade-in 640ms cubic-bezier(0.2, 0.7, 0.2, 1) both;
8
- }
2
+ /* Layered so Tailwind utilities (p-0, bg-*, …) can override per-instance.
3
+ Unlayered, these beat @layer utilities and e.g. p-0 on the CRM table card
4
+ silently loses to the default card padding. */
5
+ @layer components {
6
+ .surface-card {
7
+ border-radius: var(--radius-card);
8
+ border: 1px solid var(--border-hairline);
9
+ background: var(--surface-card);
10
+ padding: var(--surface-card-padding);
11
+ animation: surface-fade-in 640ms cubic-bezier(0.2, 0.7, 0.2, 1) both;
12
+ }
9
13
 
10
- .surface-card.is-light {
11
- background: var(--surface-card-light);
12
- }
14
+ .surface-card.is-light {
15
+ background: var(--surface-card-light);
16
+ }
13
17
 
14
- /* A near-black panel has strong figure/ground in light mode. In dark mode it
15
- elevates above the page through a lighter accent mix, border, and shadow. */
16
- .brand-panel {
17
- --row-sweep-tint: var(--brand-panel-foreground);
18
- background: var(--brand-panel-surface);
19
- }
18
+ /* A near-black panel has strong figure/ground in light mode. In dark mode it
19
+ elevates above the page through a lighter accent mix, border, and shadow. */
20
+ .brand-panel {
21
+ --row-sweep-tint: var(--brand-panel-foreground);
22
+ background: var(--brand-panel-surface);
23
+ }
20
24
 
21
- .dark .brand-panel,
22
- .theme-dark .brand-panel {
23
- border: 1px solid var(--brand-panel-border);
24
- box-shadow: var(--brand-shadow-lift);
25
- }
25
+ .dark .brand-panel,
26
+ .theme-dark .brand-panel {
27
+ border: 1px solid var(--brand-panel-border);
28
+ box-shadow: var(--brand-shadow-lift);
29
+ }
26
30
 
27
- .row-hover-sweep {
28
- pointer-events: none;
29
- position: absolute;
30
- inset: 0;
31
- border-radius: var(--radius-card);
32
- opacity: 0;
33
- transition: opacity 300ms ease;
34
- background: var(--row-hover-sweep);
35
- }
31
+ .row-hover-sweep {
32
+ pointer-events: none;
33
+ position: absolute;
34
+ inset: 0;
35
+ border-radius: var(--radius-card);
36
+ opacity: 0;
37
+ transition: opacity 300ms ease;
38
+ background: var(--row-hover-sweep);
39
+ }
36
40
 
37
- .group:hover > .row-hover-sweep,
38
- .group:focus-visible > .row-hover-sweep {
39
- opacity: 1;
41
+ .group:hover > .row-hover-sweep,
42
+ .group:focus-visible > .row-hover-sweep {
43
+ opacity: 1;
44
+ }
40
45
  }
41
46
 
42
47
  /* ── Canonical tinted-pill recipes ────────────────────────────────────── */
@@ -62,7 +67,8 @@
62
67
  }
63
68
  }
64
69
 
65
- .section-icon {
70
+ @layer components {
71
+ .section-icon {
66
72
  display: flex;
67
73
  width: var(--section-icon-size);
68
74
  height: var(--section-icon-size);
@@ -74,11 +80,12 @@
74
80
  color: var(--muted-foreground);
75
81
  }
76
82
 
77
- .stat-cell {
78
- padding: var(--stat-cell-padding);
83
+ .stat-cell {
84
+ padding: var(--stat-cell-padding);
85
+ }
79
86
  }
80
87
 
81
- /* Skeleton shimmer respects reduced-motion with a quiet breathe fallback. */
88
+ /* Skeleton shimmer becomes a static surface under reduced motion. */
82
89
  @keyframes surface-fade-in {
83
90
  from {
84
91
  opacity: 0;
@@ -96,17 +103,8 @@
96
103
  }
97
104
  }
98
105
 
99
- @keyframes skeleton-breathe {
100
- 0%,
101
- 100% {
102
- opacity: 1;
103
- }
104
- 50% {
105
- opacity: 0.6;
106
- }
107
- }
108
-
109
- .skeleton-ghost {
106
+ @layer components {
107
+ .skeleton-ghost {
110
108
  position: relative;
111
109
  overflow: hidden;
112
110
  background: var(--skeleton);
@@ -127,7 +125,9 @@
127
125
  }
128
126
 
129
127
  .skeleton-ghost {
130
- animation: skeleton-breathe 2.4s ease-in-out infinite;
128
+ animation: none;
129
+ opacity: 1;
130
+ transform: none;
131
131
  }
132
132
  }
133
133
 
@@ -140,7 +140,7 @@
140
140
  color: var(--brand-accent);
141
141
  font-size: var(--text-ui-label);
142
142
  font-weight: var(--font-weight-bold);
143
- letter-spacing: 0.04em;
143
+ letter-spacing: var(--type-tracking-040);
144
144
  text-transform: uppercase;
145
145
  }
146
146
 
@@ -164,7 +164,8 @@
164
164
  color: var(--semantic-warning-strong);
165
165
  }
166
166
 
167
- .status.error {
168
- background: var(--surface-status-danger);
169
- color: var(--semantic-danger-strong);
167
+ .status.error {
168
+ background: var(--surface-status-danger);
169
+ color: var(--semantic-danger-strong);
170
+ }
170
171
  }
package/src/theme.css CHANGED
@@ -12,6 +12,7 @@
12
12
  --color-secondary-foreground: var(--secondary-foreground);
13
13
  --color-muted: var(--muted);
14
14
  --color-muted-foreground: var(--muted-foreground);
15
+ --color-foreground-muted-accessible: var(--foreground-muted-accessible);
15
16
  --color-accent: var(--accent);
16
17
  --color-accent-foreground: var(--accent-foreground);
17
18
  --color-destructive: var(--destructive);
@@ -83,6 +84,45 @@
83
84
  --radius-2xl: var(--radius-panel);
84
85
  --radius-3xl: var(--radius-panel);
85
86
 
87
+ /* Tailwind typography utilities resolve through the same token contract. */
88
+ --font-sans: var(--font-body);
89
+ --font-display: var(--font-heading);
90
+ --font-mono: var(--font-code);
91
+ --font-weight-regular: var(--type-weight-regular);
92
+ --font-weight-normal: var(--type-weight-regular);
93
+ --font-weight-medium: var(--type-weight-medium);
94
+ --font-weight-semibold: var(--type-weight-medium);
95
+ --font-weight-bold: var(--type-weight-bold);
96
+ --font-weight-heavy: var(--type-weight-heavy);
97
+ --font-weight-extrabold: var(--type-weight-heavy);
98
+ --font-weight-black: var(--type-weight-black);
99
+ --text-xs: var(--text-ui-meta);
100
+ --text-xs--line-height: var(--type-leading-133);
101
+ --text-sm: var(--text-ui-body);
102
+ --text-sm--line-height: var(--type-leading-143);
103
+ --text-base: var(--type-paragraph);
104
+ --text-base--line-height: var(--type-leading-normal);
105
+ --text-lg: var(--text-ui-subhead);
106
+ --text-lg--line-height: var(--type-leading-156);
107
+ --text-xl: var(--text-ui-panel-title);
108
+ --text-xl--line-height: var(--type-leading-140);
109
+ --text-2xl: var(--text-ui-heading);
110
+ --text-2xl--line-height: var(--type-leading-133);
111
+ --text-3xl: var(--text-ui-title-sm);
112
+ --text-3xl--line-height: var(--type-leading-120);
113
+ --leading-none: var(--type-leading-none);
114
+ --leading-tight: var(--type-leading-tight);
115
+ --leading-snug: var(--type-leading-snug);
116
+ --leading-normal: var(--type-leading-normal);
117
+ --leading-relaxed: var(--type-leading-relaxed);
118
+ --leading-loose: var(--type-leading-loose);
119
+ --tracking-tighter: var(--type-tracking-n050);
120
+ --tracking-tight: var(--type-tracking-tight);
121
+ --tracking-normal: var(--type-tracking-normal);
122
+ --tracking-wide: var(--type-tracking-wide);
123
+ --tracking-wider: var(--type-tracking-wider);
124
+ --tracking-widest: var(--type-tracking-widest);
125
+
86
126
  /* Named spacing keys sit alongside Tailwind's numeric scale. */
87
127
  --spacing-2xs: var(--space-2xs);
88
128
  --spacing-xs: var(--space-xs);
package/src/tokens.css CHANGED
@@ -1,11 +1,75 @@
1
1
  /* ── L0: brand primitives ─────────────────────────────────────────────── */
2
2
  :root {
3
3
  color-scheme: light;
4
- --font-body: "Mulish", "Mulish Fallback", "Segoe UI", Arial, sans-serif;
4
+ /* Font loaders expose these variables at the app root. Client themes only
5
+ override this family layer; component recipes never name a font. The system
6
+ fallback after the loaded-font var is REQUIRED: without it a missing
7
+ --font-geist-sans makes the whole var() chain "guaranteed-invalid" and
8
+ font-family silently reverts to the browser default (Times), instead of a
9
+ clean sans. */
10
+ --font-body: var(--font-geist-sans, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);
5
11
  --font-heading: var(--font-body);
6
- --font-sans: var(--font-body);
7
- --font-display: var(--font-heading);
8
- --font-mono: ui-monospace, "SF Mono", "JetBrains Mono", "Menlo", monospace;
12
+ --font-code: var(--font-geist-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace);
13
+
14
+ /* Typography primitives. These raw values live here—and only here—so a
15
+ maintainer can retune family, scale, weight, rhythm, or tracking globally. */
16
+ --type-weight-regular: 400;
17
+ --type-weight-medium: 600;
18
+ --type-weight-bold: 700;
19
+ --type-weight-heavy: 800;
20
+ --type-weight-black: 900;
21
+
22
+ --type-leading-090: 0.9;
23
+ --type-leading-092: 0.92;
24
+ --type-leading-095: 0.95;
25
+ --type-leading-096: 0.96;
26
+ --type-leading-none: 1;
27
+ --type-leading-102: 1.02;
28
+ --type-leading-104: 1.04;
29
+ --type-leading-105: 1.05;
30
+ --type-leading-110: 1.1;
31
+ --type-leading-115: 1.15;
32
+ --type-leading-120: 1.2;
33
+ --type-leading-124: 1.238;
34
+ --type-leading-tight: 1.25;
35
+ --type-leading-130: 1.3;
36
+ --type-leading-133: calc(1 / 0.75);
37
+ --type-leading-snug: 1.375;
38
+ --type-leading-140: 1.4;
39
+ --type-leading-143: calc(1.25 / 0.875);
40
+ --type-leading-145: 1.45;
41
+ --type-leading-normal: 1.5;
42
+ --type-leading-155: 1.55;
43
+ --type-leading-156: calc(1.75 / 1.125);
44
+ --type-leading-160: 1.6;
45
+ --type-leading-relaxed: 1.625;
46
+ --type-leading-165: 1.65;
47
+ --type-leading-166: 1.66;
48
+ --type-leading-172: 1.72;
49
+ --type-leading-loose: 2;
50
+
51
+ --type-tracking-n050: -0.05em;
52
+ --type-tracking-n045: -0.045em;
53
+ --type-tracking-n040: -0.04em;
54
+ --type-tracking-n035: -0.035em;
55
+ --type-tracking-n030: -0.03em;
56
+ --type-tracking-n025: -0.025em;
57
+ --type-tracking-n020: -0.02em;
58
+ --type-tracking-tight: -0.025em;
59
+ --type-tracking-n010: -0.01em;
60
+ --type-tracking-normal: 0;
61
+ --type-tracking-010: 0.01em;
62
+ --type-tracking-wide: 0.025em;
63
+ --type-tracking-040: 0.04em;
64
+ --type-tracking-wider: 0.05em;
65
+ --type-tracking-060: 0.06em;
66
+ --type-tracking-080: 0.08em;
67
+ --type-tracking-100: 0.1em;
68
+ --type-tracking-120: 0.12em;
69
+ --type-tracking-140: 0.14em;
70
+ --type-tracking-160: 0.16em;
71
+ --type-tracking-widest: 0.1em;
72
+ --type-tracking-180: 0.18em;
9
73
 
10
74
  --brand-accent: #4a7dbd;
11
75
  --brand-accent-soft: #7196c4;
@@ -34,6 +98,7 @@
34
98
  --secondary-foreground: var(--brand-ink);
35
99
  --muted: rgba(203, 213, 225, 0.25);
36
100
  --muted-foreground: var(--brand-neutral-500);
101
+ --foreground-muted-accessible: #5f6b7a;
37
102
  --foreground-inverse: var(--brand-white);
38
103
  --foreground-inverse-muted: rgba(255, 255, 255, 0.88);
39
104
  --foreground-inverse-subtle: rgba(255, 255, 255, 0.84);
@@ -60,6 +125,34 @@
60
125
  --semantic-neutral: #71717a;
61
126
  --semantic-neutral-strong: #52525b;
62
127
 
128
+ --project-state-planned: var(--semantic-info);
129
+ --project-state-planned-strong: var(--semantic-info-strong);
130
+ --project-state-active: var(--semantic-success);
131
+ --project-state-active-strong: var(--semantic-success-strong);
132
+ --project-state-blocked: var(--semantic-special);
133
+ --project-state-blocked-strong: var(--semantic-special-strong);
134
+ --project-state-completed: var(--semantic-success);
135
+ --project-state-completed-strong: var(--semantic-success-strong);
136
+ --project-state-canceled: var(--semantic-neutral);
137
+ --project-state-canceled-strong: var(--semantic-neutral-strong);
138
+ --project-risk-at-risk: var(--semantic-warning);
139
+ --project-risk-at-risk-strong: var(--semantic-warning-strong);
140
+ --project-risk-overdue: var(--semantic-danger);
141
+ --project-risk-overdue-strong: var(--semantic-danger-strong);
142
+ --project-risk-overdue-soft: var(--semantic-danger-soft);
143
+ /* Ink to use ON a solid health fill: light-mode fills are saturated so
144
+ ink flips per theme (see dark block). */
145
+ --project-risk-overdue-ink: #ffffff;
146
+ --project-risk-at-risk-ink: #3a2a05;
147
+ --project-state-completed-ink: #06281e;
148
+ --project-health-on-track: var(--project-state-active-strong);
149
+ --project-health-at-risk: var(--project-risk-at-risk);
150
+ --project-health-at-risk-strong: var(--project-risk-at-risk-strong);
151
+ --project-health-off-track: var(--project-risk-overdue);
152
+ --project-health-progress: var(--project-state-active);
153
+ --project-health-off-track-strong: var(--project-risk-overdue-strong);
154
+ --project-health-off-track-soft: var(--project-risk-overdue-soft);
155
+
63
156
  /* ── L3: experience tokens ─────────────────────────────────────────── */
64
157
  --space-2xs: 0.25rem;
65
158
  --space-xs: 0.5rem;
@@ -89,8 +182,13 @@
89
182
  --radius-pill: 999px;
90
183
  --radius-scrollbar: 9999px;
91
184
 
185
+ /* MQ-derived size scale. Aliases below retain the current rendered sizes. */
186
+ --text-ui-nano: 0.4375rem;
187
+ --text-ui-nano-lg: 0.5rem;
188
+ --text-ui-fine: 0.5625rem;
92
189
  --text-ui-micro: 0.625rem;
93
190
  --text-ui-label: 0.6875rem;
191
+ --text-ui-label-relaxed: 0.7rem;
94
192
  --text-ui-meta: 0.75rem;
95
193
  --text-ui-chip: 0.78125rem;
96
194
  --text-ui-action: 0.8125rem;
@@ -108,22 +206,22 @@
108
206
  --text-ui-report-metric: 2.5rem;
109
207
  --text-ui-metric: 2.75rem;
110
208
  --text-ui-metric-xl: 3.5rem;
209
+ --text-ui-dashboard-card-title: 1.375rem;
210
+ --text-ui-dashboard-title: 2.125rem;
211
+ --text-ui-preview-card-title: 2.375rem;
212
+ --text-ui-dashboard-title-lg: 2.75rem;
213
+ --text-ui-dashboard-metric: 3.25rem;
214
+ --text-ui-preview-stat: clamp(2rem, 4vw, 3rem);
215
+ --text-ui-preview-title: clamp(2.4rem, 5.5vw, 4.8rem);
216
+ --text-ui-auth-title: clamp(2.4rem, 5vw, 4.75rem);
111
217
  --text-ui-metric-display: var(--text-ui-metric);
112
218
  --portal-text-micro: 0.625rem;
113
- --portal-text-label: 0.6875rem;
114
- --portal-text-meta: 0.75rem;
115
- --portal-text-body: 0.875rem;
116
- --portal-text-card-title: 0.9375rem;
117
- --portal-text-subhead: 1.125rem;
118
- --portal-text-panel: 1.25rem;
119
- --portal-text-heading: 1.5rem;
120
- --portal-text-title-sm: 1.875rem;
121
- --portal-text-title: clamp(1.75rem, 1.6rem + 0.7vw, 2rem);
122
- --portal-text-metric: 2.75rem;
123
- --portal-text-metric-lg: 3.5rem;
124
219
  --type-paragraph-large: 1.09375rem;
220
+ --type-heading-2: clamp(2rem, 4vw, 2.8125rem);
221
+ --type-paragraph: 1rem;
125
222
  --type-paragraph-small: 0.875rem;
126
223
  --type-paragraph-mini: 0.75rem;
224
+ --type-label: 0.625rem;
127
225
 
128
226
  --elevate-1: color-mix(in srgb, var(--foreground) 3%, transparent);
129
227
  --elevate-2: color-mix(in srgb, var(--foreground) 5%, transparent);
@@ -141,6 +239,8 @@
141
239
  --surface-button-soft: color-mix(in srgb, var(--foreground) 3.5%, var(--card));
142
240
  --surface-button-soft-hover: color-mix(in srgb, var(--foreground) 7%, var(--card));
143
241
  --border-button-soft-hover: color-mix(in srgb, var(--foreground) 16%, transparent);
242
+ --surface-button-brand: color-mix(in srgb, var(--brand-accent) 84%, var(--foreground));
243
+ --foreground-accent-link: color-mix(in srgb, var(--brand-accent) 45%, var(--foreground));
144
244
  --surface-selection: color-mix(in srgb, var(--accent) 12%, var(--card));
145
245
  --border-selection: color-mix(in srgb, var(--accent) 40%, var(--hairline));
146
246
  --surface-pagination-active: color-mix(in srgb, var(--accent) 10%, var(--card));
@@ -275,6 +375,12 @@
275
375
 
276
376
  --badge-count-bg: var(--semantic-danger);
277
377
  --badge-count-fg: var(--brand-white);
378
+ --sheet-edit-border: color-mix(in srgb, var(--accent) 24%, var(--border));
379
+ --sheet-edit-surface: color-mix(in srgb, var(--accent) 4%, var(--card));
380
+ --sheet-edit-header: color-mix(in srgb, var(--accent) 20%, var(--card));
381
+ --sheet-edit-control-border: color-mix(in srgb, var(--accent) 22%, var(--border));
382
+ --sheet-edit-control-ring: color-mix(in srgb, var(--accent) 22%, transparent);
383
+ --sheet-edit-header-surface: linear-gradient(135deg, color-mix(in srgb, var(--accent) 16%, transparent), transparent 62%);
278
384
  --on-fill-danger: var(--brand-white);
279
385
  --on-fill-warning: #3a2a05;
280
386
  --on-fill-success: #06281e;
@@ -296,14 +402,43 @@
296
402
  --project-surface-primary: var(--card);
297
403
  --project-surface-secondary: color-mix(in srgb, var(--foreground) 2%, var(--card) 98%);
298
404
  --project-surface-item: color-mix(in srgb, var(--card) 96%, transparent);
299
- --project-surface-hero: color-mix(in srgb, var(--accent) 8%, var(--card) 92%);
300
- --project-surface-hero-border: color-mix(in srgb, var(--accent) 26%, var(--foreground) 8%);
301
405
  --project-hero-surface: var(--brand-panel-surface);
302
406
  --project-hero-surface-raised: var(--brand-panel-surface-raised);
303
407
  --project-hero-foreground: var(--brand-panel-foreground);
304
408
  --project-hero-muted: var(--brand-panel-muted);
305
409
  --project-hero-subtle: var(--brand-panel-subtle);
306
410
  --project-hero-border: var(--brand-panel-border);
411
+ --project-hero-base: var(--brand-surface-dark);
412
+ --project-hero-shadow: var(--brand-shadow-soft);
413
+ --dashboard-shadow-sm: 0 1px 2px color-mix(in srgb, var(--foreground) 4%, transparent);
414
+ --dashboard-shadow-md: 0 8px 24px color-mix(in srgb, var(--foreground) 8%, transparent);
415
+ --dashboard-shadow-lg: 0 10px 30px color-mix(in srgb, var(--foreground) 8%, transparent);
416
+ --dashboard-hero-glow: radial-gradient(circle, color-mix(in srgb, var(--accent) 38%, transparent), transparent 70%);
417
+ --dashboard-hero-highlight: linear-gradient(90deg, transparent, color-mix(in srgb, var(--accent) 60%, transparent), transparent);
418
+ --dashboard-tab-hover: color-mix(in srgb, var(--foreground) 7%, transparent);
419
+ --dashboard-tab-shadow: 0 4px 14px color-mix(in srgb, var(--accent) 45%, transparent);
420
+ --dashboard-breakdown-track: color-mix(in srgb, var(--foreground) 7%, transparent);
421
+ --dashboard-accent-soft: color-mix(in srgb, var(--accent) 16%, transparent);
422
+ --dashboard-neutral-rule: color-mix(in srgb, var(--foreground) 14%, transparent);
423
+ --dashboard-task-risk-bg: color-mix(in srgb, var(--project-risk-overdue) 11%, transparent);
424
+ --dashboard-task-risk-hover: color-mix(in srgb, var(--project-risk-overdue) 16%, transparent);
425
+ --dashboard-task-due-bg: color-mix(in srgb, var(--project-risk-at-risk) 13%, transparent);
426
+ --dashboard-task-due-hover: color-mix(in srgb, var(--project-risk-at-risk) 18%, transparent);
427
+ --dashboard-task-flight-bg: color-mix(in srgb, var(--foreground) 5%, transparent);
428
+ --dashboard-task-flight-hover: color-mix(in srgb, var(--foreground) 7%, transparent);
429
+ --dashboard-task-row-hover: color-mix(in srgb, var(--accent) 6%, transparent);
430
+ --dashboard-milestone-glow: radial-gradient(circle, color-mix(in srgb, var(--accent) 30%, transparent), transparent 70%);
431
+ --dashboard-milestone-icon: color-mix(in srgb, var(--accent) 18%, transparent);
432
+ --dashboard-milestone-skeleton: color-mix(in srgb, var(--project-hero-foreground) 12%, transparent);
433
+ --dashboard-milestone-empty: color-mix(in srgb, var(--project-hero-foreground) 10%, transparent);
434
+ --dashboard-milestone-date: color-mix(in srgb, var(--project-hero-foreground) 8%, transparent);
435
+ --dashboard-period-glow: color-mix(in srgb, var(--accent) 22%, transparent);
436
+ --dashboard-client-avatar: color-mix(in srgb, var(--role-client) 18%, var(--card));
437
+ --dashboard-local-muted-foreground: color-mix(in srgb, var(--foreground) 62%, transparent);
438
+ --dashboard-local-border: color-mix(in srgb, var(--foreground) 14%, transparent);
439
+ --dashboard-local-muted: color-mix(in srgb, var(--foreground) 6%, transparent);
440
+ --dashboard-danger-soft: color-mix(in srgb, var(--project-risk-overdue) 15%, transparent);
441
+ --dashboard-danger-border: color-mix(in srgb, var(--project-risk-overdue) 40%, transparent);
307
442
  --project-surface-padding: 1.25rem;
308
443
  --row-sweep-tint: var(--foreground);
309
444
  --surface-card: color-mix(in srgb, var(--foreground) 2%, var(--card) 98%);
@@ -363,6 +498,7 @@
363
498
  --secondary-foreground: #dce5f0;
364
499
  --muted: rgba(148, 163, 184, 0.16);
365
500
  --muted-foreground: #a6b4c6;
501
+ --foreground-muted-accessible: #a6b4c6;
366
502
  --foreground-inverse: #f8fafc;
367
503
  --foreground-inverse-muted: rgba(248, 250, 252, 0.9);
368
504
  --foreground-inverse-subtle: rgba(248, 250, 252, 0.84);
@@ -388,6 +524,19 @@
388
524
  --semantic-neutral: #a1a1aa;
389
525
  --semantic-neutral-strong: #d4d4d8;
390
526
 
527
+ --project-risk-overdue-soft: var(--semantic-danger-soft);
528
+ /* Dark-mode health fills are light pastels, so on-fill ink stays dark. */
529
+ --project-risk-overdue-ink: #350a11;
530
+ --project-risk-at-risk-ink: #3a2a05;
531
+ --project-state-completed-ink: #06281e;
532
+ --project-health-on-track: var(--project-state-active-strong);
533
+ --project-health-at-risk: var(--project-risk-at-risk);
534
+ --project-health-at-risk-strong: var(--project-risk-at-risk-strong);
535
+ --project-health-off-track: var(--project-risk-overdue);
536
+ --project-health-progress: var(--project-state-active);
537
+ --project-health-off-track-strong: var(--project-risk-overdue);
538
+ --project-health-off-track-soft: var(--project-risk-overdue-soft);
539
+
391
540
  --skeleton: color-mix(in srgb, var(--foreground) 9%, transparent);
392
541
  --skeleton-strong: color-mix(in srgb, var(--foreground) 14%, transparent);
393
542
  --skeleton-highlight: color-mix(in srgb, var(--brand-white) 12%, transparent);
@@ -449,6 +598,7 @@
449
598
  --secondary-foreground: var(--brand-ink);
450
599
  --muted: rgba(203, 213, 225, 0.25);
451
600
  --muted-foreground: var(--brand-neutral-500);
601
+ --foreground-muted-accessible: #5f6b7a;
452
602
  --foreground-inverse: var(--brand-white);
453
603
  --foreground-inverse-muted: rgba(255, 255, 255, 0.88);
454
604
  --foreground-inverse-subtle: rgba(255, 255, 255, 0.84);
@@ -1,35 +1,24 @@
1
- :root {
2
- --font-weight-regular: 400;
3
- /* Mulish weight 500 is not loaded; medium deliberately resolves to 600. */
4
- --font-weight-medium: 600;
5
- --font-weight-bold: 700;
6
- --font-weight-heavy: 800;
7
- }
8
-
9
1
  @utility text-ui-micro {
10
- font-family: var(--font-body), sans-serif;
2
+ font-family: var(--font-body);
11
3
  font-size: var(--text-ui-micro);
12
4
  font-weight: var(--font-weight-medium);
13
- line-height: 1.3;
14
- color: var(--muted-foreground);
5
+ line-height: var(--type-leading-130);
15
6
  }
16
7
 
17
8
  @utility text-ui-label {
18
- font-family: var(--font-body), sans-serif;
9
+ font-family: var(--font-body);
19
10
  font-size: var(--text-ui-label);
20
11
  font-weight: var(--font-weight-medium);
21
- line-height: 1;
12
+ line-height: var(--type-leading-none);
22
13
  text-transform: uppercase;
23
- letter-spacing: 0.14em;
24
- color: var(--muted-foreground);
14
+ letter-spacing: var(--type-tracking-140);
25
15
  }
26
16
 
27
17
  @utility text-ui-meta {
28
- font-family: var(--font-body), sans-serif;
18
+ font-family: var(--font-body);
29
19
  font-size: var(--text-ui-meta);
30
20
  font-weight: var(--font-weight-regular);
31
- line-height: 1.4;
32
- color: var(--muted-foreground);
21
+ line-height: var(--type-leading-140);
33
22
  }
34
23
 
35
24
  /* Size-only utilities for component recipes that supply their own weight and rhythm. */
@@ -62,65 +51,58 @@
62
51
  }
63
52
 
64
53
  @utility text-ui-body {
65
- font-family: var(--font-body), sans-serif;
54
+ font-family: var(--font-body);
66
55
  font-size: var(--text-ui-body);
67
56
  font-weight: var(--font-weight-regular);
68
- line-height: 1.5;
69
- color: var(--foreground);
57
+ line-height: var(--type-leading-normal);
70
58
  }
71
59
 
72
60
  @utility text-ui-card-title {
73
- font-family: var(--font-body), sans-serif;
61
+ font-family: var(--font-body);
74
62
  font-size: var(--text-ui-card-title);
75
63
  font-weight: var(--font-weight-medium);
76
- line-height: 1.3;
77
- letter-spacing: -0.025em;
78
- color: var(--foreground);
64
+ line-height: var(--type-leading-130);
65
+ letter-spacing: var(--type-tracking-n025);
79
66
  }
80
67
 
81
68
  @utility text-ui-subhead {
82
- font-family: var(--font-display), sans-serif;
69
+ font-family: var(--font-display);
83
70
  font-size: var(--text-ui-subhead);
84
71
  font-weight: var(--font-weight-bold);
85
- line-height: 1.25;
86
- letter-spacing: -0.02em;
87
- color: var(--foreground);
72
+ line-height: var(--type-leading-tight);
73
+ letter-spacing: var(--type-tracking-n020);
88
74
  }
89
75
 
90
76
  @utility text-ui-panel-title {
91
- font-family: var(--font-display), sans-serif;
77
+ font-family: var(--font-display);
92
78
  font-size: var(--text-ui-panel-title);
93
79
  font-weight: var(--font-weight-bold);
94
- line-height: 1.2;
95
- letter-spacing: -0.02em;
96
- color: var(--foreground);
80
+ line-height: var(--type-leading-120);
81
+ letter-spacing: var(--type-tracking-n020);
97
82
  }
98
83
 
99
84
  @utility text-ui-heading {
100
- font-family: var(--font-display), sans-serif;
85
+ font-family: var(--font-display);
101
86
  font-size: var(--text-ui-heading);
102
87
  font-weight: var(--font-weight-bold);
103
- line-height: 1.15;
104
- letter-spacing: -0.02em;
105
- color: var(--foreground);
88
+ line-height: var(--type-leading-115);
89
+ letter-spacing: var(--type-tracking-n020);
106
90
  }
107
91
 
108
92
  @utility text-ui-title-sm {
109
- font-family: var(--font-display), sans-serif;
93
+ font-family: var(--font-display);
110
94
  font-size: var(--text-ui-title-sm);
111
95
  font-weight: var(--font-weight-heavy);
112
- line-height: 1.1;
113
- letter-spacing: -0.03em;
114
- color: var(--foreground);
96
+ line-height: var(--type-leading-110);
97
+ letter-spacing: var(--type-tracking-n030);
115
98
  }
116
99
 
117
100
  @utility text-ui-title {
118
- font-family: var(--font-display), sans-serif;
101
+ font-family: var(--font-display);
119
102
  font-size: var(--text-ui-title);
120
103
  font-weight: var(--font-weight-heavy);
121
- line-height: 1.05;
122
- letter-spacing: -0.03em;
123
- color: var(--foreground);
104
+ line-height: var(--type-leading-105);
105
+ letter-spacing: var(--type-tracking-n030);
124
106
  }
125
107
 
126
108
  @utility text-ui-metric {
@@ -128,29 +110,26 @@
128
110
  font-variant-numeric: tabular-nums;
129
111
  font-size: var(--text-ui-metric);
130
112
  font-weight: var(--font-weight-heavy);
131
- line-height: 1;
132
- letter-spacing: -0.04em;
133
- color: var(--foreground);
113
+ line-height: var(--type-leading-none);
114
+ letter-spacing: var(--type-tracking-n040);
134
115
  }
135
116
 
136
117
  @utility text-ui-metric-xl {
137
- font-family: var(--font-display), sans-serif;
118
+ font-family: var(--font-display);
138
119
  font-variant-numeric: tabular-nums;
139
120
  font-size: var(--text-ui-metric-xl);
140
121
  font-weight: var(--font-weight-heavy);
141
- line-height: 0.95;
142
- letter-spacing: -0.04em;
143
- color: var(--foreground);
122
+ line-height: var(--type-leading-095);
123
+ letter-spacing: var(--type-tracking-n040);
144
124
  }
145
125
 
146
126
  @utility text-ui-metric-display {
147
- font-family: var(--font-display), sans-serif;
127
+ font-family: var(--font-display);
148
128
  font-variant-numeric: tabular-nums;
149
129
  font-size: var(--text-ui-metric-display);
150
130
  font-weight: var(--font-weight-heavy);
151
- line-height: 1;
152
- letter-spacing: -0.04em;
153
- color: var(--foreground);
131
+ line-height: var(--type-leading-none);
132
+ letter-spacing: var(--type-tracking-n040);
154
133
  }
155
134
 
156
135
  /* Thin scrollbar for bounded surface lists. */
@@ -16,81 +16,91 @@
16
16
  }
17
17
 
18
18
  @utility portal-micro {
19
- font-family: var(--font-body), sans-serif;
19
+ font-family: var(--font-body);
20
20
  font-size: var(--text-ui-micro);
21
21
  font-weight: var(--font-weight-medium);
22
- line-height: 1.3;
22
+ line-height: var(--type-leading-130);
23
+ /* MQ parity: apps/portal/app/globals.css:718. */
23
24
  color: var(--muted-foreground);
24
25
  }
25
26
  @utility portal-label {
26
- font-family: var(--font-body), sans-serif;
27
+ font-family: var(--font-body);
27
28
  font-size: var(--text-ui-label);
28
29
  font-weight: var(--font-weight-medium);
29
- line-height: 1;
30
+ line-height: var(--type-leading-none);
30
31
  text-transform: uppercase;
31
- letter-spacing: 0.14em;
32
+ letter-spacing: var(--type-tracking-140);
33
+ /* MQ parity: apps/portal/app/globals.css:663. */
32
34
  color: var(--muted-foreground);
33
35
  }
34
36
  @utility portal-meta {
35
- font-family: var(--font-body), sans-serif;
37
+ font-family: var(--font-body);
36
38
  font-size: var(--text-ui-meta);
37
39
  font-weight: var(--font-weight-regular);
38
- line-height: 1.4;
40
+ line-height: var(--type-leading-140);
41
+ /* MQ parity: apps/portal/app/globals.css:711. */
39
42
  color: var(--muted-foreground);
40
43
  }
41
44
  @utility portal-body {
42
- font-family: var(--font-body), sans-serif;
45
+ font-family: var(--font-body);
43
46
  font-size: var(--text-ui-body);
44
47
  font-weight: var(--font-weight-regular);
45
- line-height: 1.5;
48
+ line-height: var(--type-leading-normal);
49
+ /* MQ parity: apps/portal/app/globals.css:705. */
46
50
  color: var(--foreground);
47
51
  }
48
52
  @utility portal-card-title {
49
- font-family: var(--font-body), sans-serif;
53
+ font-family: var(--font-body);
50
54
  font-size: var(--text-ui-card-title);
51
55
  font-weight: var(--font-weight-medium);
52
- line-height: 1.3;
53
- letter-spacing: -0.025em;
56
+ line-height: var(--type-leading-130);
57
+ letter-spacing: var(--type-tracking-n025);
58
+ /* MQ parity: apps/portal/app/globals.css:654. */
54
59
  color: var(--foreground);
55
60
  }
56
61
  @utility portal-subhead {
57
- font-family: var(--font-display), sans-serif;
62
+ font-family: var(--font-display);
58
63
  font-size: var(--text-ui-subhead);
59
64
  font-weight: var(--font-weight-bold);
60
- line-height: 1.25;
61
- letter-spacing: -0.02em;
65
+ line-height: var(--type-leading-tight);
66
+ letter-spacing: var(--type-tracking-n020);
67
+ /* MQ parity: apps/portal/app/globals.css:646. */
62
68
  color: var(--foreground);
63
69
  }
64
70
  @utility portal-panel-title {
65
- font-family: var(--font-display), sans-serif;
71
+ font-family: var(--font-display);
66
72
  font-size: var(--text-ui-panel-title);
67
73
  font-weight: var(--font-weight-bold);
68
- line-height: 1.2;
69
- letter-spacing: -0.02em;
74
+ line-height: var(--type-leading-120);
75
+ letter-spacing: var(--type-tracking-n020);
76
+ /* MQ parity: apps/portal/app/globals.css:636. */
70
77
  color: var(--foreground);
71
78
  }
72
79
  @utility portal-heading {
73
- font-family: var(--font-display), sans-serif;
80
+ font-family: var(--font-display);
74
81
  font-size: var(--text-ui-heading);
75
82
  font-weight: var(--font-weight-bold);
76
- line-height: 1.15;
77
- letter-spacing: -0.02em;
83
+ line-height: var(--type-leading-115);
84
+ letter-spacing: var(--type-tracking-n020);
85
+ /* MQ parity: apps/portal/app/globals.css:626. */
78
86
  color: var(--foreground);
79
87
  }
80
88
  @utility portal-title-sm {
81
- font-family: var(--font-display), sans-serif;
89
+ font-family: var(--font-display);
82
90
  font-size: var(--text-ui-title-sm);
83
91
  font-weight: var(--font-weight-heavy);
84
- line-height: 1.1;
85
- letter-spacing: -0.03em;
92
+ line-height: var(--type-leading-110);
93
+ letter-spacing: var(--type-tracking-n030);
94
+ /* MQ parity: apps/portal/app/globals.css:617. */
86
95
  color: var(--foreground);
87
96
  }
88
97
  @utility portal-title {
89
- font-family: var(--font-display), sans-serif;
98
+ font-family: var(--font-display);
90
99
  font-size: var(--text-ui-title);
91
100
  font-weight: var(--font-weight-heavy);
92
- line-height: 1.05;
93
- letter-spacing: -0.03em;
101
+ line-height: var(--type-leading-105);
102
+ letter-spacing: var(--type-tracking-n030);
103
+ /* MQ parity: apps/portal/app/globals.css:606. */
94
104
  color: var(--foreground);
95
105
  }
96
106
  @utility portal-metric {
@@ -98,26 +108,29 @@
98
108
  font-variant-numeric: tabular-nums;
99
109
  font-size: var(--text-ui-metric);
100
110
  font-weight: var(--font-weight-heavy);
101
- line-height: 1;
102
- letter-spacing: -0.04em;
111
+ line-height: var(--type-leading-none);
112
+ letter-spacing: var(--type-tracking-n040);
113
+ /* MQ parity: apps/portal/app/globals.css:673. */
103
114
  color: var(--foreground);
104
115
  }
105
116
  @utility portal-metric-xl {
106
- font-family: var(--font-display), sans-serif;
117
+ font-family: var(--font-display);
107
118
  font-variant-numeric: tabular-nums;
108
119
  font-size: var(--text-ui-metric-xl);
109
120
  font-weight: var(--font-weight-heavy);
110
- line-height: 0.95;
111
- letter-spacing: -0.04em;
121
+ line-height: var(--type-leading-095);
122
+ letter-spacing: var(--type-tracking-n040);
123
+ /* MQ parity: apps/portal/app/globals.css:697. */
112
124
  color: var(--foreground);
113
125
  }
114
126
  @utility portal-metric-display {
115
- font-family: var(--font-display), sans-serif;
127
+ font-family: var(--font-display);
116
128
  font-variant-numeric: tabular-nums;
117
129
  font-size: var(--text-ui-metric-display);
118
130
  font-weight: var(--font-weight-heavy);
119
- line-height: 1;
120
- letter-spacing: -0.04em;
131
+ line-height: var(--type-leading-none);
132
+ letter-spacing: var(--type-tracking-n040);
133
+ /* MQ parity: apps/portal/app/globals.css:685. */
121
134
  color: var(--foreground);
122
135
  }
123
136
  @utility portal-scroll {
@@ -138,25 +151,54 @@
138
151
  }
139
152
  }
140
153
 
141
- .paragraph-large,
142
- .paragraph-small,
143
- .paragraph-mini {
144
- font-family: var(--font-body), sans-serif;
145
- font-weight: var(--font-weight-regular);
146
- color: var(--foreground);
147
- }
154
+ @layer components {
155
+ .heading-2 {
156
+ margin: 0;
157
+ font-family: var(--font-display);
158
+ font-size: var(--type-heading-2);
159
+ font-weight: var(--font-weight-black);
160
+ line-height: var(--type-leading-110);
161
+ letter-spacing: var(--type-tracking-n025);
162
+ text-wrap: balance;
163
+ }
148
164
 
149
- .paragraph-large {
150
- font-size: var(--type-paragraph-large);
151
- line-height: 1.55;
152
- }
165
+ .paragraph,
166
+ .paragraph-large,
167
+ .paragraph-small,
168
+ .paragraph-mini {
169
+ margin: 0;
170
+ font-family: var(--font-body);
171
+ font-weight: var(--font-weight-regular);
172
+ letter-spacing: var(--type-tracking-010);
173
+ }
153
174
 
154
- .paragraph-small {
155
- font-size: var(--type-paragraph-small);
156
- line-height: 1.5;
157
- }
175
+ .paragraph {
176
+ font-size: var(--type-paragraph);
177
+ line-height: var(--type-leading-172);
178
+ }
158
179
 
159
- .paragraph-mini {
160
- font-size: var(--type-paragraph-mini);
161
- line-height: 1.45;
180
+ .paragraph-large {
181
+ font-size: var(--type-paragraph-large);
182
+ line-height: var(--type-leading-155);
183
+ }
184
+
185
+ .paragraph-small {
186
+ font-size: var(--type-paragraph-small);
187
+ line-height: var(--type-leading-normal);
188
+ }
189
+
190
+ .paragraph-mini {
191
+ font-size: var(--type-paragraph-mini);
192
+ line-height: var(--type-leading-145);
193
+ }
194
+
195
+ .label {
196
+ margin: 0;
197
+ font-family: var(--font-display);
198
+ font-size: var(--type-label);
199
+ font-weight: var(--font-weight-heavy);
200
+ line-height: var(--type-leading-120);
201
+ letter-spacing: var(--type-tracking-120);
202
+ text-transform: uppercase;
203
+ }
162
204
  }
package/themes/mq.css CHANGED
@@ -1,5 +1,7 @@
1
1
  /* MQ compatibility palette. Import after @brightweblabs/theme/css. */
2
2
  :root {
3
+ /* MQ deployments load their client font at the app root and restore it with
4
+ one theme override: --font-body: var(--font-client-body); */
3
5
  --brand-accent: #5bc5f2;
4
6
  --brand-accent-soft: #6fc4e7;
5
7
  --brand-ink: #0a151a;
@@ -26,6 +28,7 @@
26
28
  --secondary-foreground: var(--brand-ink);
27
29
  --muted: rgba(191, 192, 207, 0.18);
28
30
  --muted-foreground: var(--brand-neutral-500);
31
+ --foreground-muted-accessible: #606570;
29
32
  --foreground-inverse: var(--brand-white);
30
33
  --foreground-inverse-muted: rgba(255, 255, 255, 0.88);
31
34
  --foreground-inverse-subtle: rgba(255, 255, 255, 0.84);
@@ -59,17 +62,34 @@
59
62
  --brand-panel-muted: rgba(246, 251, 255, 0.66);
60
63
  --brand-panel-subtle: rgba(246, 251, 255, 0.46);
61
64
  --brand-panel-border: rgba(145, 211, 241, 0.2);
65
+ --auth-wash: radial-gradient(circle at 50% 0%, color-mix(in srgb, var(--brand-accent) 11%, transparent), transparent 54%);
66
+ --auth-card-surface: color-mix(in srgb, var(--card) 96%, transparent);
67
+ --auth-card-border: color-mix(in srgb, var(--brand-accent) 18%, var(--border));
68
+ --auth-card-shadow: var(--brand-shadow-soft);
69
+ --auth-card-radius: var(--radius-panel);
70
+ --auth-panel-surface: var(--brand-panel-surface);
71
+ --auth-panel-foreground: var(--brand-panel-foreground);
72
+ --auth-panel-muted: var(--brand-panel-muted);
73
+ --auth-panel-border: var(--brand-panel-border);
74
+ --auth-success-surface: var(--surface-status-success);
75
+ --auth-success-border: color-mix(in srgb, var(--semantic-success) 30%, transparent);
76
+ --auth-success-foreground: var(--semantic-success-strong);
77
+ --auth-error-surface: var(--surface-danger-subtle);
78
+ --auth-error-border: color-mix(in srgb, var(--destructive) 28%, transparent);
79
+ --auth-error-foreground: var(--destructive);
80
+ --auth-skeleton-surface: var(--elevate-2);
81
+ --auth-skeleton-highlight: color-mix(in srgb, var(--card) 72%, transparent);
62
82
  --project-surface-primary: var(--card);
63
83
  --project-surface-secondary: color-mix(in srgb, var(--foreground) 2%, var(--card) 98%);
64
84
  --project-surface-item: color-mix(in srgb, var(--card) 96%, transparent);
65
- --project-surface-hero: color-mix(in srgb, var(--accent) 8%, var(--card) 92%);
66
- --project-surface-hero-border: color-mix(in srgb, var(--accent) 26%, var(--foreground) 8%);
67
85
  --project-hero-surface: linear-gradient(158deg, #02070d 0%, #06111c 30%, #071d2b 63%, #123747 100%);
68
86
  --project-hero-surface-raised: #0b2634;
69
87
  --project-hero-foreground: #f6fbff;
70
88
  --project-hero-muted: rgba(246, 251, 255, 0.66);
71
89
  --project-hero-subtle: rgba(246, 251, 255, 0.46);
72
90
  --project-hero-border: rgba(145, 211, 241, 0.2);
91
+ --project-hero-base: #061827;
92
+ --project-hero-shadow: 0 18px 50px rgba(6, 29, 51, 0.14);
73
93
  --project-surface-padding: 1.25rem;
74
94
  }
75
95
 
@@ -102,6 +122,7 @@
102
122
  --secondary-foreground: #dceaf7;
103
123
  --muted: rgba(132, 170, 198, 0.16);
104
124
  --muted-foreground: #9eb6cd;
125
+ --foreground-muted-accessible: #9eb6cd;
105
126
  --foreground-inverse: #f7fbff;
106
127
  --foreground-inverse-muted: rgba(247, 251, 255, 0.9);
107
128
  --foreground-inverse-subtle: rgba(247, 251, 255, 0.84);
@@ -152,14 +173,14 @@
152
173
  --project-surface-primary: var(--card);
153
174
  --project-surface-secondary: color-mix(in srgb, var(--foreground) 2%, var(--card) 98%);
154
175
  --project-surface-item: color-mix(in srgb, var(--card) 96%, transparent);
155
- --project-surface-hero: color-mix(in srgb, var(--accent) 8%, var(--card) 92%);
156
- --project-surface-hero-border: color-mix(in srgb, var(--accent) 26%, var(--foreground) 8%);
157
176
  --project-hero-surface: linear-gradient(158deg, #0c2433 0%, #143b4e 52%, #1b4f66 100%);
158
177
  --project-hero-surface-raised: #0b2634;
159
178
  --project-hero-foreground: #f6fbff;
160
179
  --project-hero-muted: rgba(246, 251, 255, 0.66);
161
180
  --project-hero-subtle: rgba(246, 251, 255, 0.46);
162
181
  --project-hero-border: color-mix(in srgb, var(--accent) 20%, transparent);
182
+ --project-hero-base: #0c2433;
183
+ --project-hero-shadow: 0 18px 50px rgba(0, 0, 0, 0.32);
163
184
  }
164
185
 
165
186
  :root.light,
@@ -190,6 +211,7 @@
190
211
  --secondary-foreground: var(--brand-ink);
191
212
  --muted: rgba(191, 192, 207, 0.18);
192
213
  --muted-foreground: var(--brand-neutral-500);
214
+ --foreground-muted-accessible: #606570;
193
215
  --accent: var(--brand-accent);
194
216
  --accent-foreground: var(--brand-ink);
195
217
  --destructive: #b43f3f;
@@ -206,4 +228,6 @@
206
228
  --project-hero-muted: rgba(246, 251, 255, 0.66);
207
229
  --project-hero-subtle: rgba(246, 251, 255, 0.46);
208
230
  --project-hero-border: rgba(145, 211, 241, 0.2);
231
+ --project-hero-base: #061827;
232
+ --project-hero-shadow: 0 18px 50px rgba(6, 29, 51, 0.14);
209
233
  }