Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
211 changes: 145 additions & 66 deletions public/mdbase-theme.css
Original file line number Diff line number Diff line change
@@ -1,25 +1,151 @@
/*
* mdbase semantic roles. Components consume roles rather than palette values so
* both themes keep the same hierarchy. Short aliases (`--ink`, `--muted`, ...) are
* app-local and must map onto these roles.
*/
:root {
color-scheme: light;
color: var(--color-text);
background: var(--color-canvas);
font-family: "Atkinson Hyperlegible", "Segoe UI", sans-serif;
--color-canvas: oklch(99.5% 0.002 255);
--color-surface: oklch(99.5% 0.002 255);
--color-surface-subtle: oklch(97.8% 0.003 255);
--color-text: oklch(21% 0.018 255);
--color-text-soft: oklch(50% 0.014 255);
--color-text-muted: oklch(54% 0.01 255);
--color-text-faint: oklch(70% 0.009 255);
--color-border: oklch(92% 0.006 255);
--color-border-strong: oklch(82% 0.008 255);
--color-selected: oklch(96.8% 0.004 255);
--color-accent: oklch(47% 0.1 238);
--color-accent-strong: oklch(41% 0.1 238);
--color-accent-soft: oklch(93.5% 0.035 238);
--color-on-accent: oklch(98% 0.004 245);
--color-success: oklch(44% 0.085 153);
--color-danger: oklch(50% 0.11 28);
--color-danger-soft: oklch(97% 0.018 28);
--color-warning: oklch(60% 0.09 75);
--color-warning-border: oklch(83% 0.055 82);
--color-warning-surface: oklch(98% 0.018 86);
--color-warning-text: oklch(43% 0.055 69);
--color-warning-strong: oklch(36% 0.052 67);
--color-editor-text: oklch(28% 0.014 255);
--color-placeholder: oklch(78% 0.007 255);
--color-skeleton: var(--color-border);
--color-shadow: oklch(20% 0.02 255 / 0.06);
--color-scrim: oklch(21% 0.018 255 / 0.14);
--scrollbar-thumb-rest: oklch(51% 0.014 255 / 0.07);
--scrollbar-thumb: oklch(51% 0.014 255 / 0.22);
--scrollbar-thumb-hover: oklch(42% 0.016 255 / 0.4);
--syntax-comment: oklch(58% 0.012 255);
--syntax-keyword: oklch(42% 0.09 294);
--syntax-string: oklch(39% 0.075 153);
--syntax-number: oklch(43% 0.1 45);
--syntax-link: oklch(43% 0.1 238);
--syntax-heading: var(--color-text);
--app-editor: #005c88;
--app-reader: #a4467f;
--app-writer: #d55e00;
--sans: "Atkinson Hyperlegible", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
--mono: "Azeret Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
/* Motion: popovers and small entrances are quick and ease out; a settled
confirmation holds, then fades away (see controls.css). */
--ease-out: cubic-bezier(0.22, 1, 0.36, 1);
--motion-quick: 150ms;
--motion-base: 180ms;
--motion-settle: 2.4s;
}

:root[data-theme="dark"] {
color-scheme: dark;
--color-canvas: oklch(17.5% 0.012 255);
--color-surface: oklch(19.5% 0.012 255);
--color-surface-subtle: oklch(22.5% 0.012 255);
--color-text: oklch(92% 0.008 255);
--color-text-soft: oklch(70% 0.012 255);
--color-text-muted: oklch(62% 0.011 255);
--color-text-faint: oklch(56% 0.011 255);
--color-border: oklch(29% 0.012 255);
--color-border-strong: oklch(39% 0.012 255);
--color-selected: oklch(23.5% 0.012 255);
--color-accent: oklch(73% 0.105 238);
--color-accent-strong: oklch(65% 0.105 238);
--color-accent-soft: oklch(31% 0.045 238);
--color-on-accent: oklch(17% 0.012 255);
--color-success: oklch(72% 0.09 153);
--color-danger: oklch(72% 0.115 28);
--color-danger-soft: oklch(25% 0.03 28);
--color-warning: oklch(78% 0.09 75);
--color-warning-border: oklch(48% 0.06 82);
--color-warning-surface: oklch(25% 0.026 82);
--color-warning-text: oklch(78% 0.07 82);
--color-warning-strong: oklch(86% 0.065 82);
--color-editor-text: oklch(88% 0.009 255);
--color-placeholder: oklch(56% 0.01 255);
--color-skeleton: oklch(25% 0.012 255);
--color-shadow: oklch(5% 0.01 255 / 0.22);
--color-scrim: oklch(5% 0.01 255 / 0.46);
--scrollbar-thumb-rest: oklch(72% 0.012 255 / 0.06);
--scrollbar-thumb: oklch(72% 0.012 255 / 0.2);
--scrollbar-thumb-hover: oklch(78% 0.012 255 / 0.38);
--syntax-comment: oklch(62% 0.012 255);
--syntax-keyword: oklch(76% 0.09 294);
--syntax-string: oklch(75% 0.08 153);
--syntax-number: oklch(78% 0.1 65);
--syntax-link: oklch(77% 0.1 238);
--app-editor: #63b1e2;
--app-reader: #e08cc0;
--app-writer: #f58d4a;
}

@media (prefers-color-scheme: dark) {
:root:not([data-theme]) {
color-scheme: dark;
--color-canvas: oklch(17.5% 0.012 255);
--color-surface: oklch(19.5% 0.012 255);
--color-surface-subtle: oklch(22.5% 0.012 255);
--color-text: oklch(92% 0.008 255);
--color-text-soft: oklch(70% 0.012 255);
--color-text-muted: oklch(62% 0.011 255);
--color-text-faint: oklch(56% 0.011 255);
--color-border: oklch(29% 0.012 255);
--color-border-strong: oklch(39% 0.012 255);
--color-selected: oklch(23.5% 0.012 255);
--color-accent: oklch(73% 0.105 238);
--color-accent-strong: oklch(65% 0.105 238);
--color-accent-soft: oklch(31% 0.045 238);
--color-on-accent: oklch(17% 0.012 255);
--color-success: oklch(72% 0.09 153);
--color-danger: oklch(72% 0.115 28);
--color-danger-soft: oklch(25% 0.03 28);
--color-warning: oklch(78% 0.09 75);
--color-warning-border: oklch(48% 0.06 82);
--color-warning-surface: oklch(25% 0.026 82);
--color-warning-text: oklch(78% 0.07 82);
--color-warning-strong: oklch(86% 0.065 82);
--color-editor-text: oklch(88% 0.009 255);
--color-placeholder: oklch(56% 0.01 255);
--color-skeleton: oklch(25% 0.012 255);
--color-shadow: oklch(5% 0.01 255 / 0.22);
--color-scrim: oklch(5% 0.01 255 / 0.46);
--scrollbar-thumb-rest: oklch(72% 0.012 255 / 0.06);
--scrollbar-thumb: oklch(72% 0.012 255 / 0.2);
--scrollbar-thumb-hover: oklch(78% 0.012 255 / 0.38);
--syntax-comment: oklch(62% 0.012 255);
--syntax-keyword: oklch(76% 0.09 294);
--syntax-string: oklch(75% 0.08 153);
--syntax-number: oklch(78% 0.1 65);
--syntax-link: oklch(77% 0.1 238);
--app-editor: #63b1e2;
--app-reader: #e08cc0;
--app-writer: #f58d4a;
}
}


:root {
color: var(--ink);
background: var(--paper);
font-family: var(--sans);
font-synthesis: none;
--color-canvas: #ffffff;
--color-surface: #ffffff;
--color-surface-subtle: #ffffff;
--color-selected: #ffffff;
--color-text: #171717;
--color-text-soft: rgb(23 23 23 / 74%);
--color-text-muted: rgb(23 23 23 / 54%);
--color-text-faint: rgb(23 23 23 / 30%);
--color-border: rgb(23 23 23 / 18%);
--color-border-strong: rgb(23 23 23 / 42%);
--color-accent: #171717;
--color-accent-strong: #171717;
--color-success: #171717;
--color-warning: #171717;
--color-danger: #171717;
--color-scrim: rgb(23 23 23 / 22%);
--paper: var(--color-surface);
--hover: var(--color-surface-subtle);
--selected: var(--color-selected);
Expand All @@ -35,52 +161,5 @@
--connected: var(--color-success);
--warning: var(--color-warning);
--danger: var(--color-danger);
--mono: "Azeret Mono", "SFMono-Regular", "Cascadia Code", monospace;
--ease: cubic-bezier(0.16, 1, 0.3, 1);
}

html {
font-size: 17px;
}

:root[data-theme="dark"] {
color-scheme: dark;
--color-canvas: #121212;
--color-surface: #121212;
--color-surface-subtle: #181818;
--color-selected: #1b1b1b;
--color-text: #f0f0ed;
--color-text-soft: rgb(240 240 237 / 78%);
--color-text-muted: rgb(240 240 237 / 56%);
--color-text-faint: rgb(240 240 237 / 30%);
--color-border: rgb(240 240 237 / 22%);
--color-border-strong: rgb(240 240 237 / 48%);
--color-accent: #f0f0ed;
--color-accent-strong: #f0f0ed;
--color-success: #f0f0ed;
--color-warning: #f0f0ed;
--color-danger: #f0f0ed;
--color-scrim: rgb(0 0 0 / 62%);
}

@media (prefers-color-scheme: dark) {
:root:not([data-theme]) {
color-scheme: dark;
--color-canvas: #121212;
--color-surface: #121212;
--color-surface-subtle: #181818;
--color-selected: #1b1b1b;
--color-text: #f0f0ed;
--color-text-soft: rgb(240 240 237 / 78%);
--color-text-muted: rgb(240 240 237 / 56%);
--color-text-faint: rgb(240 240 237 / 30%);
--color-border: rgb(240 240 237 / 22%);
--color-border-strong: rgb(240 240 237 / 48%);
--color-accent: #f0f0ed;
--color-accent-strong: #f0f0ed;
--color-success: #f0f0ed;
--color-warning: #f0f0ed;
--color-danger: #f0f0ed;
--color-scrim: rgb(0 0 0 / 62%);
}
}
15 changes: 4 additions & 11 deletions public/theme-bootstrap.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,8 @@
try {
const theme = /^\/authorize\/[0-9a-f-]+$/i.test(location.pathname)
? null
: localStorage.getItem("mdbase:theme");
if (theme === "light" || theme === "dark") {
document.documentElement.dataset.theme = theme;
}
const dark = theme === "dark"
|| (theme !== "light" && matchMedia("(prefers-color-scheme: dark)").matches);
document
.querySelector('meta[name="theme-color"]')
?.setAttribute("content", dark ? "#121212" : "#ffffff");
const theme = localStorage.getItem("mdbase:theme");
if (theme === "light" || theme === "dark") document.documentElement.dataset.theme = theme;
const dark = theme === "dark" || (theme !== "light" && matchMedia("(prefers-color-scheme: dark)").matches);
document.querySelector('meta[name="theme-color"]')?.setAttribute("content", dark ? "#1c1e24" : "#fcfcfd");
} catch {
// The system preference remains available when local storage is unavailable.
}
25 changes: 24 additions & 1 deletion scripts/sync-sources.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ if (connectThemeBoundary === -1) {
}
writeFileSync(
join(root, "public", "mdbase-theme.css"),
`${connectTheme.slice(0, connectThemeBoundary).trim()}\n`
`${resolveConnectThemeImports(connectTheme.slice(0, connectThemeBoundary)).trim()}\n`
);
cpSync(
join(connectDir, "apps", "portal", "public", "theme-bootstrap.js"),
Expand Down Expand Up @@ -422,6 +422,29 @@ function replaceCanvasFunction(source, pattern, replacement) {
return source.replace(pattern, replacement);
}

// The Connect theme imports its tokens from the shared @mdbase-dev/ui package.
// Browsers cannot resolve bare package specifiers, so inline the tokens and
// drop the app component styles, which this site does not use.
function resolveConnectThemeImports(source) {
const inlined = { "tokens.css": true, "brand.css": false, "controls.css": false };
const resolved = source.replace(
/^@import\s+"@mdbase-dev\/ui\/([^"]+)";[^\S\n]*\n?/gm,
(statement, name) => {
if (!(name in inlined)) {
throw new Error(`Unexpected Connect theme import: ${statement.trim()}`);
}
if (!inlined[name]) return "";
const path = join(connectDir, "packages", "app-ui", "css", name);
required(path, `@mdbase-dev/ui ${name}`);
return `${readFileSync(path, "utf8").trim()}\n\n`;
}
);
if (/@import/.test(resolved)) {
throw new Error("Connect theme still contains an @import after inlining");
}
return resolved;
}

function copyAlias(source, destination) {
cpSync(
join(schemaSource, source),
Expand Down
Loading