|
3 | 3 | * Aligns with VocaHQ marketing design language ("quiet paper desk"): |
4 | 4 | * https://github.com/VocaHQ/vocahq/blob/main/web/DESIGN.md |
5 | 5 | * |
6 | | - * Rules: |
7 | | - * 1. Warm paper ground, near-black ink, teal for actions/status only. |
8 | | - * 2. Hierarchy from type and hairlines, not nested cards or glows. |
9 | | - * 3. Surfaces are earned (inputs, metric grids, QR plate); prose sits on paper. |
| 6 | + * Light is the brand default; dark is a graphite pair of the same teal accent. |
| 7 | + * html[data-theme="light"|"dark"] is set by a FOUC-safe boot script (system |
| 8 | + * preference unless the operator overrides with the header toggle). |
10 | 9 | */ |
11 | 10 |
|
12 | 11 | :root { |
13 | | - color-scheme: light; |
| 12 | + /* Shared tokens (theme-independent) */ |
| 13 | + --r: 8px; |
| 14 | + --r-lg: 16px; |
| 15 | + --r-pill: 999px; |
| 16 | + --mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace; |
| 17 | + --font-display: "Epilogue", system-ui, sans-serif; |
| 18 | + --font-body: "Atkinson Hyperlegible", system-ui, sans-serif; |
| 19 | + |
| 20 | + /* One gutter for the header, the nav and the content, so their left edges line |
| 21 | + up at every width. It tracks `main`'s own max-width and padding. */ |
| 22 | + /* 100%, not 100vw: 100vw includes the scrollbar, which would push the header a |
| 23 | + few pixels left of the content it is supposed to line up with. */ |
| 24 | + --gutter: max(24px, calc((100% - 1048px) / 2 + 24px)); |
| 25 | +} |
14 | 26 |
|
15 | | - /* VocaHQ DESIGN.md neutrals + accent */ |
| 27 | +/* Light: VocaHQ DESIGN.md paper desk */ |
| 28 | +html, html[data-theme="light"] { |
| 29 | + color-scheme: light; |
16 | 30 | --bg: #f7f6f3; |
17 | 31 | --surface: #ffffff; |
18 | 32 | --raised: #efeeea; |
19 | 33 | --sunken: #efeeea; |
20 | | - |
21 | 34 | --line: #e2e0da; |
22 | 35 | --line-soft: #eceae4; |
23 | 36 | --line-strong: #cdcbc3; |
24 | | - |
25 | 37 | --text: #1a1c1b; |
26 | 38 | --muted: #5c6360; |
27 | 39 | --faint: #8a918d; |
28 | | - |
29 | 40 | --accent: #1f8f75; |
30 | 41 | --accent-hover: #176b58; |
31 | 42 | --accent-ink: #ffffff; |
32 | 43 | --accent-line: #1f8f75; |
33 | 44 | --accent-wash: #e6f4ef; |
34 | | - |
35 | 45 | --warn: #9a6b16; |
36 | 46 | --warn-wash: rgba(154, 107, 22, 0.1); |
37 | 47 | --error: #b42318; |
38 | 48 | --error-wash: rgba(180, 35, 24, 0.08); |
39 | | - |
40 | | - --r: 8px; |
41 | | - --r-lg: 16px; |
42 | | - --r-pill: 999px; |
43 | | - --mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace; |
44 | | - --font-display: "Epilogue", system-ui, sans-serif; |
45 | | - --font-body: "Atkinson Hyperlegible", system-ui, sans-serif; |
46 | 49 | --shadow-soft: 0 10px 30px rgba(26, 28, 27, 0.08); |
47 | 50 | --shadow-modal: 0 18px 48px rgba(26, 28, 27, 0.12); |
48 | | - |
49 | | - /* One gutter for the header, the nav and the content, so their left edges line |
50 | | - up at every width. It tracks `main`'s own max-width and padding. */ |
51 | | - /* 100%, not 100vw: 100vw includes the scrollbar, which would push the header a |
52 | | - few pixels left of the content it is supposed to line up with. */ |
53 | | - --gutter: max(24px, calc((100% - 1048px) / 2 + 24px)); |
| 51 | + --overlay-scrim: rgba(26, 28, 27, 0.36); |
| 52 | + --tabs-bg: rgba(247, 246, 243, 0.92); |
| 53 | + --theme-color: #f7f6f3; |
| 54 | + --select-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8'%3E%3Cpath d='M1 1.75 6 6.25l5-4.5' fill='none' stroke='%235c6360' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); |
| 55 | +} |
| 56 | + |
| 57 | +/* Dark: graphite desk, same teal family (readable on near-black) */ |
| 58 | +html[data-theme="dark"] { |
| 59 | + color-scheme: dark; |
| 60 | + --bg: #141614; |
| 61 | + --surface: #1b1c1b; |
| 62 | + --raised: #232522; |
| 63 | + --sunken: #101210; |
| 64 | + --line: #2e312e; |
| 65 | + --line-soft: #262924; |
| 66 | + --line-strong: #3d423e; |
| 67 | + --text: #e3e4e1; |
| 68 | + --muted: #a2aba5; |
| 69 | + --faint: #79827c; |
| 70 | + --accent: #77d0b2; |
| 71 | + --accent-hover: #8fdcc1; |
| 72 | + --accent-ink: #00382a; |
| 73 | + --accent-line: #2f6e5c; |
| 74 | + --accent-wash: rgba(119, 208, 178, 0.12); |
| 75 | + --warn: #e3b778; |
| 76 | + --warn-wash: rgba(227, 183, 120, 0.12); |
| 77 | + --error: #ffb4ab; |
| 78 | + --error-wash: rgba(255, 180, 171, 0.12); |
| 79 | + --shadow-soft: 0 12px 32px rgba(0, 0, 0, 0.35); |
| 80 | + --shadow-modal: 0 20px 50px rgba(0, 0, 0, 0.5); |
| 81 | + --overlay-scrim: rgba(10, 12, 10, 0.72); |
| 82 | + --tabs-bg: rgba(20, 22, 20, 0.92); |
| 83 | + --theme-color: #141614; |
| 84 | + --select-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8'%3E%3Cpath d='M1 1.75 6 6.25l5-4.5' fill='none' stroke='%23a2aba5' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); |
54 | 85 | } |
55 | 86 |
|
56 | 87 | * { box-sizing: border-box; } |
@@ -166,6 +197,60 @@ header { |
166 | 197 | padding: 18px var(--gutter) 14px; |
167 | 198 | } |
168 | 199 |
|
| 200 | +.header-actions { |
| 201 | + display: flex; |
| 202 | + align-items: center; |
| 203 | + gap: 10px; |
| 204 | + min-width: 0; |
| 205 | +} |
| 206 | + |
| 207 | +.theme-toggle { |
| 208 | + position: relative; |
| 209 | + flex: 0 0 auto; |
| 210 | + display: inline-flex; |
| 211 | + align-items: center; |
| 212 | + justify-content: center; |
| 213 | + width: 40px; |
| 214 | + height: 40px; |
| 215 | + margin: 0; |
| 216 | + padding: 0; |
| 217 | + border: 1px solid var(--line); |
| 218 | + border-radius: var(--r-pill); |
| 219 | + background: var(--surface); |
| 220 | + color: var(--muted); |
| 221 | + cursor: pointer; |
| 222 | + transition: border-color 0.15s ease, background 0.15s ease, color 0.15s ease; |
| 223 | +} |
| 224 | +.theme-toggle:hover { |
| 225 | + border-color: var(--accent); |
| 226 | + color: var(--text); |
| 227 | + background: var(--accent-wash); |
| 228 | +} |
| 229 | +.theme-toggle:focus-visible { |
| 230 | + outline: 2px solid var(--accent); |
| 231 | + outline-offset: 2px; |
| 232 | +} |
| 233 | +.theme-toggle svg { |
| 234 | + display: block; |
| 235 | + width: 18px; |
| 236 | + height: 18px; |
| 237 | +} |
| 238 | +/* Show the icon for the action (moon in light → go dark; sun in dark → go light). */ |
| 239 | +html[data-theme="light"] .theme-icon-sun, |
| 240 | +html[data-theme="dark"] .theme-icon-moon { display: none; } |
| 241 | +html[data-theme="light"] .theme-icon-moon, |
| 242 | +html[data-theme="dark"] .theme-icon-sun { display: block; } |
| 243 | +/* System preference: small badge on the control. */ |
| 244 | +.theme-toggle[data-preference="system"] { |
| 245 | + box-shadow: inset 0 0 0 1px var(--accent-wash); |
| 246 | +} |
| 247 | +.theme-toggle[data-preference="system"]::after { |
| 248 | + content: ""; |
| 249 | + position: absolute; |
| 250 | + /* decorative only; preference is in aria-label */ |
| 251 | + display: none; |
| 252 | +} |
| 253 | + |
169 | 254 | .brand { display: flex; align-items: center; gap: 12px; } |
170 | 255 | .logo { display: block; width: 28px; height: 28px; border-radius: 50%; } |
171 | 256 | .brand h1 { |
@@ -234,7 +319,7 @@ header { |
234 | 319 | z-index: 20; |
235 | 320 | padding: 0 var(--gutter); |
236 | 321 | border-bottom: 1px solid var(--line); |
237 | | - background: rgba(247, 246, 243, 0.92); |
| 322 | + background: var(--tabs-bg); |
238 | 323 | backdrop-filter: saturate(140%) blur(10px); |
239 | 324 | } |
240 | 325 | .tab { |
@@ -748,7 +833,7 @@ select { |
748 | 833 | appearance: none; |
749 | 834 | padding-right: 32px; |
750 | 835 | background-color: var(--surface); |
751 | | - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8'%3E%3Cpath d='M1 1.75 6 6.25l5-4.5' fill='none' stroke='%235c6360' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); |
| 836 | + background-image: var(--select-chevron); |
752 | 837 | background-repeat: no-repeat; |
753 | 838 | background-position: right 12px center; |
754 | 839 | cursor: pointer; |
@@ -816,7 +901,7 @@ select { |
816 | 901 | .overlay { |
817 | 902 | position: fixed; |
818 | 903 | inset: 0; |
819 | | - background: rgba(26, 28, 27, 0.36); |
| 904 | + background: var(--overlay-scrim); |
820 | 905 | backdrop-filter: blur(4px); |
821 | 906 | display: flex; |
822 | 907 | align-items: center; |
|
0 commit comments