Follow-up: colour token migration (compat → --c-*, hardcoded hex) (#7011)

Follow-up to #7009, which built the theme token layer (`primitives` →
`colors` → `compat`). This PR moves the whole app onto it, removes
hardcoded colours, turns on enforcement so they can't come back, and
adds a user-selectable accent.

## What this does
- **Semantic tokens everywhere** — legacy colour aliases and raw hex are
rewritten to `--c-*` tokens (`--c-surface*`, `--c-text*`, `--c-primary`,
…). Straight rename, no visual change. Genuine literals (brand/OAuth,
colour pickers, data-viz) are left as-is.
- **Fixes missing colours** — some tokens the migration referenced were
never defined, so a few surfaces (login button, auth banners, badges,
procurement view) silently lost their colour. All defined now, and they
adapt to light/dark and the accent automatically.
- **Blocking colour lint** — CI now fails on hardcoded colours,
undefined tokens, or unreadable low-contrast status colours.
- **User-selectable accent** — light and dark each get their own accent
from Settings → Appearance, contrast-clamped so text stays legible.
"Default" keeps the standard blue.

## Still to come
Remaining inline-style hex, the legacy token-definition files
(`theme.css`, `tokens.css`), and folding `zIndex.ts` onto the dimension
tokens.

## Testing
`task frontend:check:all` green; light/dark and accent switching
spot-checked.
This commit is contained in:
EthanHealy01
2026-07-23 09:05:56 +00:00
committed by GitHub
parent 8de94ff152
commit 67e10138b5
280 changed files with 4030 additions and 3474 deletions
+5 -1
View File
@@ -207,10 +207,14 @@ tasks:
- task: lint:colors
lint:colors:
desc: "Enforce theme tokens — colours in core/theme route through the palette"
desc: "Enforce theme tokens — no hardcoded colours or raw primitives in components"
aliases: [lint:colours]
deps: [install]
cmds:
- node editor/scripts/lint/theme-lint.mjs
- node editor/scripts/lint/theme-lint.mjs css-colors
- node editor/scripts/lint/theme-lint.mjs code-colors
- node editor/scripts/lint/theme-lint.mjs no-primitives
contrast:
desc: "Report low-contrast theme token pairs (warning only, never blocks)"
+2 -2
View File
@@ -226,8 +226,8 @@ const preview: Preview = {
backgrounds: {
default: "app",
values: [
{ name: "app", value: "var(--color-bg)" },
{ name: "surface", value: "var(--color-surface)" },
{ name: "app", value: "var(--c-bg)" },
{ name: "surface", value: "var(--c-surface)" },
],
},
a11y: {
+638 -99
View File
@@ -1,19 +1,24 @@
#!/usr/bin/env node
// Theme colour lint — guards the theme SYSTEM (core/theme/). Two modes:
// Theme colour lint — guards the theme system + app-wide colour hygiene. Modes:
//
// node theme-lint.mjs enforce: literal colours live ONLY in
// primitives.css; colors/compat/dimensions must
// reference tokens; no duplicate primitives.
// (blocking)
// node theme-lint.mjs contrast warn-only WCAG contrast report (never blocks)
//
// Scope is deliberately just core/theme/ — the palette + token layer this PR
// owns, which is clean, so no baseline file is needed. Enforcing "no hardcoded
// colours" across the whole app (260+ existing sites) is a separate migration.
// primitives.css; core/theme references tokens;
// no duplicate primitives; every referenced
// --p-*/--c-* token resolves. (blocking)
// node theme-lint.mjs css-colors enforce: NO hardcoded colour in any source
// .css (primitives.css + generated output.css
// exempt). Scope: all editor/src. (blocking)
// node theme-lint.mjs code-colors enforce: no hardcoded colour in TS/TSX DOM
// code (default-deny with layered exemptions;
// `// theme-allow-color` opt-out). (blocking)
// node theme-lint.mjs contrast warn-only WCAG report — fixed --c-* pairs +
// status-tone text/fill pairs. (The tone gate
// below 1.6:1 is enforced in the default run.)
//
// Structural black / white / transparent (shadows, scrims) are always allowed.
import { readFileSync, readdirSync } from "node:fs";
import { execSync } from "node:child_process";
import { relative, resolve, join } from "node:path";
const THEME = resolve(process.cwd(), "editor/src/core/theme");
@@ -26,7 +31,6 @@ const PRIMITIVES = "editor/src/core/theme/primitives.css";
const THEME_FILES = [
"primitives.css",
"colors.css",
"compat.css",
"dimensions.css",
"index.css",
];
@@ -34,8 +38,156 @@ const THEME_FILES = [
// ── colour helpers ─────────────────────────────────────────────────────────
const HEX_RE = /#[0-9a-fA-F]{3,8}\b/g;
const FUNC_RE = /\b(?:rgba?|hsla?)\(\s*[^)]*\)/g;
const NAMED_RE =
/\b(?:white|black|red|green|blue|orange|yellow|purple|gray|grey|silver|transparent)\b/g;
// CSS named colours (structural white/black/transparent handled separately).
// Kept broad so `color: teal`/`navy`/`crimson`/`cyan` can't slip past the gate.
const NAMED_COLORS = [
"aliceblue",
"antiquewhite",
"aqua",
"aquamarine",
"azure",
"beige",
"bisque",
"blanchedalmond",
"blue",
"blueviolet",
"brown",
"burlywood",
"cadetblue",
"chartreuse",
"chocolate",
"coral",
"cornflowerblue",
"cornsilk",
"crimson",
"cyan",
"darkblue",
"darkcyan",
"darkgoldenrod",
"darkgray",
"darkgreen",
"darkgrey",
"darkkhaki",
"darkmagenta",
"darkolivegreen",
"darkorange",
"darkorchid",
"darkred",
"darksalmon",
"darkseagreen",
"darkslateblue",
"darkslategray",
"darkslategrey",
"darkturquoise",
"darkviolet",
"deeppink",
"deepskyblue",
"dimgray",
"dimgrey",
"dodgerblue",
"firebrick",
"floralwhite",
"forestgreen",
"fuchsia",
"gainsboro",
"ghostwhite",
"gold",
"goldenrod",
"gray",
"green",
"greenyellow",
"grey",
"honeydew",
"hotpink",
"indianred",
"indigo",
"ivory",
"khaki",
"lavender",
"lavenderblush",
"lawngreen",
"lemonchiffon",
"lightblue",
"lightcoral",
"lightcyan",
"lightgoldenrodyellow",
"lightgray",
"lightgreen",
"lightgrey",
"lightpink",
"lightsalmon",
"lightseagreen",
"lightskyblue",
"lightslategray",
"lightslategrey",
"lightsteelblue",
"lightyellow",
"lime",
"limegreen",
"linen",
"magenta",
"maroon",
"mediumaquamarine",
"mediumblue",
"mediumorchid",
"mediumpurple",
"mediumseagreen",
"mediumslateblue",
"mediumspringgreen",
"mediumturquoise",
"mediumvioletred",
"midnightblue",
"mintcream",
"mistyrose",
"moccasin",
"navajowhite",
"navy",
"oldlace",
"olive",
"olivedrab",
"orange",
"orangered",
"orchid",
"palegoldenrod",
"palegreen",
"paleturquoise",
"palevioletred",
"papayawhip",
"peachpuff",
"peru",
"pink",
"plum",
"powderblue",
"purple",
"rebeccapurple",
"red",
"rosybrown",
"royalblue",
"saddlebrown",
"salmon",
"sandybrown",
"seagreen",
"seashell",
"sienna",
"silver",
"skyblue",
"slateblue",
"slategray",
"slategrey",
"snow",
"springgreen",
"steelblue",
"tan",
"teal",
"thistle",
"tomato",
"turquoise",
"violet",
"wheat",
"yellow",
"yellowgreen",
];
const NAMED_RE = new RegExp(`\\b(?:${NAMED_COLORS.join("|")})\\b`, "g");
function expandHex(hex) {
let h = hex.slice(1).toLowerCase();
@@ -66,6 +218,98 @@ function stripComments(text) {
return text.replace(/\/\*[\s\S]*?\*\//g, (m) => m.replace(/[^\n]/g, " "));
}
// ── shared colour math + token resolution (used by contrast report + tone guard)
function readPrimitives(css) {
const primitives = {};
for (const m of css.matchAll(
/(--p-[a-z0-9-]+)\s*:\s*(#[0-9a-fA-F]{3,8})\s*;/g,
))
primitives[m[1]] = m[2];
return primitives;
}
function hexToRgb(hex) {
// expandHex normalises 3/4-digit shorthand to 6/8; parse alpha from 8-digit.
const h = expandHex(hex.startsWith("#") ? hex : "#" + hex).slice(1);
return {
r: parseInt(h.slice(0, 2), 16),
g: parseInt(h.slice(2, 4), 16),
b: parseInt(h.slice(4, 6), 16),
a: h.length >= 8 ? parseInt(h.slice(6, 8), 16) / 255 : 1,
};
}
const over = (f, b) => ({
r: f.r * f.a + b.r * (1 - f.a),
g: f.g * f.a + b.g * (1 - f.a),
b: f.b * f.a + b.b * (1 - f.a),
a: 1,
});
const mix = (a, b, p) => ({
r: (a.r * p + b.r * (100 - p)) / 100,
g: (a.g * p + b.g * (100 - p)) / 100,
b: (a.b * p + b.b * (100 - p)) / 100,
a: 1,
});
// Resolve any token value: hex, rgb(a), var(--x[, fallback]) (--p-* → palette,
// else the theme map), or color-mix(in srgb, A n%, B|transparent).
function resolveColorValue(v, t, primitives, seen) {
if (v == null) return null;
// Collapse internal whitespace so multi-line values (e.g. a color-mix() split
// across lines in tokens.css) match the single-line grammars below.
v = v.replace(/\s+/g, " ").trim();
let m;
if (v.startsWith("#")) return hexToRgb(v);
if ((m = v.match(/^rgba?\(([^)]+)\)$/))) {
const n = m[1]
.split(/[,/\s]+/)
.map(Number)
.filter((x) => !Number.isNaN(x));
return { r: n[0], g: n[1], b: n[2], a: n[3] ?? 1 };
}
if ((m = v.match(/^var\(\s*(--[a-z0-9-]+)\s*(?:,\s*([\s\S]+))?\)$/))) {
return resolveColorVar(m[1], m[2], t, primitives, seen);
}
if (
(m = v.match(
/^color-mix\(\s*in srgb\s*,\s*(.+?)\s+(\d+)%\s*,\s*(.+?)\s*\)$/,
))
) {
const a = resolveColorValue(m[1], t, primitives, seen);
if (!a) return null;
if (m[3].trim() === "transparent") return { ...a, a: +m[2] / 100 };
const b = resolveColorValue(m[3].trim(), t, primitives, seen);
return b ? mix(a, b, +m[2]) : null;
}
return null;
}
function resolveColorVar(name, fallback, t, primitives, seen) {
if (name.startsWith("--p-")) {
return primitives[name]
? hexToRgb(primitives[name])
: fallback
? resolveColorValue(fallback, t, primitives, seen)
: null;
}
if (!seen.has(name) && t[name] !== undefined) {
const next = new Set(seen).add(name);
const r = resolveColorValue(t[name], t, primitives, next);
if (r) return r;
}
return fallback ? resolveColorValue(fallback, t, primitives, seen) : null;
}
const relativeLuminance = ({ r, g, b }) => {
const f = (v) => {
v /= 255;
return v <= 0.03928 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4;
};
return 0.2126 * f(r) + 0.7152 * f(g) + 0.0722 * f(b);
};
const contrastRatio = (a, b) => {
const [hi, lo] = [relativeLuminance(a), relativeLuminance(b)].sort(
(x, y) => y - x,
);
return (hi + 0.05) / (lo + 0.05);
};
// ── enforce: literals only in primitives.css, no duplicate primitives ────────
function check() {
const violations = [];
@@ -149,11 +393,7 @@ function check() {
function reportContrast() {
const primitivesCss = readFileSync(join(THEME, "primitives.css"), "utf8");
const colorsCss = readFileSync(join(THEME, "colors.css"), "utf8");
const primitives = {};
for (const m of primitivesCss.matchAll(
/(--p-[a-z0-9-]+)\s*:\s*(#[0-9a-fA-F]{3,8})\s*;/g,
))
primitives[m[1]] = m[2];
const primitives = readPrimitives(primitivesCss);
const blocks = [];
for (const m of colorsCss.matchAll(/([^{}]+)\{([^}]*)\}/g)) {
const decls = {};
@@ -189,82 +429,8 @@ function reportContrast() {
};
const flatten = (list) =>
Object.assign({ ...SEED }, ...list.map((b) => b.decls));
const hexToRgb = (h) => {
h = h.replace("#", "");
if (h.length === 3)
h = h
.split("")
.map((c) => c + c)
.join("");
return {
r: parseInt(h.slice(0, 2), 16),
g: parseInt(h.slice(2, 4), 16),
b: parseInt(h.slice(4, 6), 16),
a: 1,
};
};
const over = (f, b) => ({
r: f.r * f.a + b.r * (1 - f.a),
g: f.g * f.a + b.g * (1 - f.a),
b: f.b * f.a + b.b * (1 - f.a),
a: 1,
});
const mix = (a, b, p) => ({
r: (a.r * p + b.r * (100 - p)) / 100,
g: (a.g * p + b.g * (100 - p)) / 100,
b: (a.b * p + b.b * (100 - p)) / 100,
a: 1,
});
// Resolve any token value: hex, rgb(a), var(--x[, fallback]) (--p-* → palette,
// else the theme map/seed), or color-mix(in srgb, A n%, B|transparent).
function resolveValue(v, t, seen) {
if (v == null) return null;
v = v.trim();
let m;
if (v.startsWith("#")) return hexToRgb(v);
if ((m = v.match(/^rgba?\(([^)]+)\)$/))) {
const n = m[1]
.split(/[,/\s]+/)
.map(Number)
.filter((x) => !Number.isNaN(x));
return { r: n[0], g: n[1], b: n[2], a: n[3] ?? 1 };
}
if ((m = v.match(/^var\(\s*(--[a-z0-9-]+)\s*(?:,\s*([\s\S]+))?\)$/))) {
return resolveVar(m[1], m[2], t, seen);
}
if ((m = v.match(/^color-mix\(in srgb,\s*(.+?)\s+(\d+)%\s*,\s*(.+)\)$/))) {
const a = resolveValue(m[1], t, seen);
if (!a) return null;
if (m[3].trim() === "transparent") return { ...a, a: +m[2] / 100 };
const b = resolveValue(m[3].trim(), t, seen);
return b ? mix(a, b, +m[2]) : null;
}
return null;
}
function resolveVar(name, fallback, t, seen) {
if (name.startsWith("--p-")) {
return primitives[name]
? hexToRgb(primitives[name])
: fallback
? resolveValue(fallback, t, seen)
: null;
}
if (!seen.has(name) && t[name] !== undefined) {
const next = new Set(seen).add(name);
const r = resolveValue(t[name], t, next);
if (r) return r;
}
return fallback ? resolveValue(fallback, t, seen) : null;
}
const resolve = (token, t) =>
resolveValue(t[token] ?? null, t, new Set([token]));
const lum = ({ r, g, b }) => {
const f = (v) => {
v /= 255;
return v <= 0.03928 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4;
};
return 0.2126 * f(r) + 0.7152 * f(g) + 0.0722 * f(b);
};
resolveColorValue(t[token] ?? null, t, primitives, new Set([token]));
const contrast = (t1, t2, t) => {
const surface = resolve("--c-surface", t);
let a = resolve(t1, t);
@@ -272,8 +438,7 @@ function reportContrast() {
if (!a || !b || !surface) return null;
if (a.a < 1) a = over(a, surface);
if (b.a < 1) b = over(b, surface);
const [hi, lo] = [lum(a), lum(b)].sort((x, y) => y - x);
return (hi + 0.05) / (lo + 0.05);
return contrastRatio(a, b);
};
const PAIRS = [
["--c-text", "--c-surface", 4.5],
@@ -307,23 +472,397 @@ function reportContrast() {
);
}
// ── status-tone text on its own fill ─────────────────────────────────────────
// Checks --color-{hue} text against its --color-{hue}-light fill, per theme.
// Below TONE_INVISIBLE blocks the build; TONE_FLOOR (WCAG AA) is advisory.
const TOKENS_CSS = resolve(process.cwd(), "editor/src/core/tokens/tokens.css");
const TONE_FLOOR = 3.0;
const TONE_INVISIBLE = 1.6;
// Compute the contrast of every --color-{hue} text on its own --color-{hue}-light
// fill, per theme. Returns [{ theme, base, fill, ratio|null }] — no printing, so
// both the warn-only report and the blocking guard can share it.
function toneContrastResults() {
const primitives = readPrimitives(
readFileSync(join(THEME, "primitives.css"), "utf8"),
);
// Strip comments first: a selector's captured prefix can otherwise include a
// preceding comment that mentions data-theme="dark", misclassifying the block.
const css = stripComments(readFileSync(TOKENS_CSS, "utf8"));
const isDark = (sel) => /data-theme="dark"|color-scheme="dark"/.test(sel);
const lightTones = {};
const darkTones = {};
for (const m of css.matchAll(/([^{}]+)\{([^}]*)\}/g)) {
const target = isDark(m[1]) ? darkTones : lightTones;
for (const d of m[2].matchAll(/(--color-[a-z0-9-]+)\s*:\s*([^;]+);/g))
target[d[1]] = d[2].trim();
}
// Dark only overrides the tones it redefines; unspecified ones inherit light.
const themes = { light: lightTones, dark: { ...lightTones, ...darkTones } };
const white = { r: 255, g: 255, b: 255, a: 1 };
const results = [];
for (const [theme, t] of Object.entries(themes)) {
const bases = Object.keys(t)
.map((k) => /^(--color-[a-z]+)-light$/.exec(k)?.[1])
.filter((base) => base && t[base] !== undefined);
for (const base of bases) {
const fill = `${base}-light`;
const fg = resolveColorValue(t[base], t, primitives, new Set([base]));
const bg = resolveColorValue(t[fill], t, primitives, new Set([fill]));
const ratio =
fg && bg
? contrastRatio(
fg.a < 1 ? over(fg, bg) : fg,
bg.a < 1 ? over(bg, white) : bg,
)
: null;
results.push({ theme, base, fill, ratio });
}
}
return results;
}
function reportToneContrast() {
const results = toneContrastResults();
let warnings = 0;
let invisible = 0;
console.log("tone-contrast report: text on its own -light fill\n");
let theme = "";
for (const { theme: th, base, fill, ratio } of results) {
if (th !== theme) {
theme = th;
console.log(` ${theme}`);
}
if (ratio == null) {
console.log(` ? ${base} on ${fill} (unresolved)`);
continue;
}
// ✖ = near-invisible (blocks CI), ⚠ = below the WCAG floor (warn only).
const mark =
ratio < TONE_INVISIBLE ? "✖ " : ratio < TONE_FLOOR ? "⚠ " : " ";
if (ratio < TONE_INVISIBLE) invisible++;
else if (ratio < TONE_FLOOR) warnings++;
console.log(
` ${mark}${ratio.toFixed(2).padStart(5)} (floor ${TONE_FLOOR}, blocks <${TONE_INVISIBLE}) ${base} on ${fill}`,
);
}
const parts = [];
if (invisible)
parts.push(`${invisible} near-invisible (<${TONE_INVISIBLE}:1)`);
if (warnings) parts.push(`${warnings} below the ${TONE_FLOOR} floor`);
console.log(
parts.length
? `\n${parts.join(", ")}. ✖ blocks the build; ⚠ is advisory.\n`
: "\n✓ all tones clear the floor.\n",
);
}
// ── css-colors (blocking): no hardcoded colour in ANY source .css ────────────
// App-wide guard that source CSS routes every colour through the palette. File
// list from `git ls-files` (a VCS query, never a directory walk feeding a read).
// primitives.css (the literal home) and generated output.css are exempt.
function checkAppCss() {
const EXEMPT = /(?:^|\/)(?:primitives\.css|output\.css)$/;
const listed = execSync("git ls-files -- editor/src", { encoding: "utf8" })
.split("\n")
.map((l) => l.trim())
.filter((l) => l && l.endsWith(".css") && !EXEMPT.test(l));
const violations = [];
const lineOf = (text, index) => text.slice(0, index).split("\n").length;
for (const rel of listed) {
const text = stripComments(readFileSync(rel, "utf8"));
for (const re of [HEX_RE, FUNC_RE]) {
re.lastIndex = 0;
let m;
while ((m = re.exec(text)) !== null) {
if (/var\(/i.test(m[0])) continue; // rgb()/color-mix wrapping a token
const norm = normalizeColor(m[0]);
if (isStructuralColor(norm)) continue;
violations.push({
file: rel,
line: lineOf(text, m.index),
msg: `raw colour ${m[0]} — define it in primitives.css and use var(--p-…)`,
});
}
}
text.split("\n").forEach((line, i) => {
const colon = line.indexOf(":");
if (colon < 0 || /[{}]/.test(line)) return;
if (!/^\s*(?:--)?[a-z][a-z0-9-]*\s*$/i.test(line.slice(0, colon))) return;
const value = line
.slice(colon + 1)
.replace(/--[a-z0-9-]+/gi, " ")
.replace(/url\([^)]*\)/g, " ")
.replace(/["'][^"']*["']/g, " ");
for (const nm of value.match(NAMED_RE) || []) {
if (isStructuralName(nm)) continue;
violations.push({
file: rel,
line: i + 1,
msg: `named colour "${nm}" — define it in primitives.css and use var(--p-…)`,
});
}
});
}
return violations;
}
// ── code-colors (blocking): no hardcoded colour in TS/TSX DOM code ───────────
// Default-deny with layered exemptions: structural black/white/transparent;
// safe contexts on the line (var() fallback, canvas/pdf-lib rgb, an explicit
// `// theme-allow-color` opt-out); and exempt PATHS where colour literals are
// inherent (rendering/vendor/config/tests/stories).
const CODE_EXEMPT_PATH = [
/Thumbnail|Overlay|DrawingCanvas|PageEditor|MobileScannerPage/,
// PDF rendering/drawing surfaces that legitimately carry colour literals —
// scoped to specific tool paths, not a blanket "pdf" substring (which used to
// exempt most of the app in a PDF product).
/pdfTextEditor|pixelCompare|\/compare\.ts$|customPrimary|accentColors/,
/validateSignature\/outputtedPDFSections|CenteredMessageSection|StatusBadgeSection/,
/\/viewer\/|Annotation|useViewerReadAloud|CommentsSidebar|\/constants\/search\.ts$|SignaturePreview/,
/ColorPicker|ColorControl|WatchedFolderManagementModal|watchedFolderPresets|fileColors|unifiedBackground|folder\.ts$|policyFolders/,
/OAuthButtons|oauthCallbackHtml/,
/mantineTheme|\/theme\.ts$|toolsTaxonomy|LayoutPreview|PageNumberPreview|CloudStorageIcons/,
/\/onboarding\//,
/addStamp|addWatermark|\/tooltips\//,
/UpgradeBanner|AdminPlanSection/,
/\.test\.[jt]sx?$|\.stories\.[jt]sx?$|\/types\//,
];
const CODE_HEX =
/#(?:[0-9a-fA-F]{8}|[0-9a-fA-F]{6}|[0-9a-fA-F]{4}|[0-9a-fA-F]{3})(?![0-9a-fA-F])/g;
const CODE_RGB = /rgba?\(([^)]*)\)/gi;
const CODE_HSL = /hsla?\(([^)]*)\)/gi;
// NOTE: bare named colours (e.g. 'blue') are intentionally NOT flagged in
// TS/TSX — they are dominated by legitimate Mantine palette props (`c="red"`,
// `color="blue"`, which are theme-routed) and provider names ('azure'), so a
// string match produces mostly false positives. hsl()/hex/rgb() are unambiguous.
const codeStructuralHex = (h) =>
/^#(?:000|fff|000f|ffff|000000|ffffff|00000000|ffffffff)$/i.test(h);
// Line-level skips for contexts where a colour literal is inherent (canvas /
// pdf-lib drawing, computed-style reads) or explicitly opted out. NOTE: a
// `var(--x, #hex)` literal fallback is deliberately NOT skipped — the hex in the
// fallback is still a hardcoded colour and must route through a token.
const CODE_SKIP_LINE =
/theme-allow-color|readColor\(|getPropertyValue|\.colors\.[a-z]+\?\.\[|fillStyle|strokeStyle|shadowColor|addColorStop|createLinearGradient|ctx\.|getContext/i;
function codeStripNonCode(text) {
return text
.replace(/\/\*[\s\S]*?\*\//g, (m) => m.replace(/[^\n]/g, " "))
.replace(
/(^|[^:])\/\/[^\n]*/g,
(m, p) => p + m.slice(p.length).replace(/[^\n]/g, " "),
)
.replace(/&#\d+;/g, " ");
}
function codeRgbIsColour(inner) {
if (/var\(/.test(inner)) return false;
const p = inner
.split(/[, /]+/)
.map((x) => x.trim())
.filter(Boolean);
if (!/^\d/.test(p[0] || "")) return false;
const nums = p.slice(0, 3).map(Number);
if (nums.some((n) => Number.isNaN(n))) return false;
if (nums.every((n) => n <= 1)) return false; // pdf-lib rgb(0..1)
const k = `${nums[0]},${nums[1]},${nums[2]}`;
return k !== "0,0,0" && k !== "255,255,255";
}
function checkCodeColors() {
const files = execSync("git ls-files -- editor/src", { encoding: "utf8" })
.split("\n")
.map((l) => l.trim())
.filter(
(l) =>
/\.(ts|tsx)$/.test(l) && !CODE_EXEMPT_PATH.some((re) => re.test(l)),
);
const violations = [];
for (const rel of files) {
const raw = readFileSync(rel, "utf8");
const rawLines = raw.split("\n");
const text = codeStripNonCode(raw);
text.split("\n").forEach((line, i) => {
if (/theme-allow-color/.test(rawLines[i] || "")) return;
if (CODE_SKIP_LINE.test(line)) return;
const hits = [];
for (const h of line.match(CODE_HEX) || [])
if (!codeStructuralHex(h)) hits.push(h);
for (const m of line.match(CODE_RGB) || [])
if (codeRgbIsColour(m.replace(/rgba?\(|\)/gi, ""))) hits.push(m);
for (const m of line.match(CODE_HSL) || [])
if (!/var\(/.test(m)) hits.push(m);
for (const h of hits)
violations.push({
file: rel,
line: i + 1,
msg: `hardcoded colour ${h} — use a var(--c-*/--p-*) token, or add \`// theme-allow-color <reason>\` if it must be a literal`,
});
});
}
return violations;
}
// ── token-resolution (blocking): every referenced --p-*/--c-* must resolve ────
// A `var(--p-…)`/`var(--c-…)` with no fallback silently computes to the initial
// value (transparent/inherited) when the token is undefined, so the colour just
// disappears — invisible to the literal checks above. Assert every such
// reference has a definition somewhere (any source .css/.ts/.tsx) or a fallback.
// Runtime-injected families (--user-*, --mantine-*, --accent-*) are out of scope.
function checkTokenResolution() {
const files = execSync("git ls-files -- editor/src", { encoding: "utf8" })
.split("\n")
.map((l) => l.trim())
.filter((l) => /\.(css|ts|tsx)$/.test(l));
const DEF_RE = /(--[a-z0-9-]+)\s*:/gi;
// Capture the token and the char that follows it (`,` ⇒ has a fallback).
const REF_RE = /var\(\s*(--[a-z0-9-]+)\s*(,|\))/gi;
const defined = new Set();
const refs = [];
const lineOf = (text, index) => text.slice(0, index).split("\n").length;
for (const rel of files) {
const text = stripComments(readFileSync(rel, "utf8"));
for (const m of text.matchAll(DEF_RE)) defined.add(m[1]);
for (const m of text.matchAll(REF_RE)) {
const [token, next] = [m[1], m[2]];
if (!/^--[pc]-/.test(token)) continue; // only our theme tokens
if (next === ",") continue; // has a fallback → degrades safely
refs.push({ file: rel, line: lineOf(text, m.index), token });
}
}
return refs.filter((r) => !defined.has(r.token));
}
// ── no-primitives (blocking): components must reference --c-* SEMANTIC tokens,
// not raw --p-* palette primitives — otherwise a colour-scheme change to the
// semantic layer won't reach them. --p-* is allowed ONLY in the token-definition
// layer (primitives/colors/dimensions + legacy vocab files + accents).
const PRIMITIVE_LAYER = [
/^editor\/src\/core\/theme\//,
/^editor\/src\/core\/styles\/theme\.css$/,
/^editor\/src\/core\/tokens\/tokens\.css$/,
/^editor\/src\/saas\/styles\/saas-theme\.css$/,
/^editor\/src\/proprietary\/auth\/ui\/auth-theme\.css$/,
/^editor\/src\/core\/ui\/accents\.css$/,
];
function checkNoPrimitives() {
const files = execSync("git ls-files -- editor/src", { encoding: "utf8" })
.split("\n")
.map((l) => l.trim())
.filter(
(l) =>
/\.(css|scss|ts|tsx)$/.test(l) &&
!PRIMITIVE_LAYER.some((re) => re.test(l)),
);
const REF = /var\(\s*(--p-[a-z0-9-]+)/g;
const violations = [];
const lineOf = (text, index) => text.slice(0, index).split("\n").length;
for (const rel of files) {
const text = stripComments(readFileSync(rel, "utf8"));
for (const m of text.matchAll(REF))
violations.push({ file: rel, line: lineOf(text, m.index), token: m[1] });
}
return violations;
}
// ── CLI ──────────────────────────────────────────────────────────────────────
if (process.argv.includes("no-primitives")) {
const v = checkNoPrimitives();
if (v.length) {
console.error(
`\n✖ theme-lint no-primitives: ${v.length} raw --p-* primitive ref(s) in components — use a --c-* semantic token so colour-scheme changes propagate:\n`,
);
for (const x of v) console.error(` ${x.file}:${x.line} var(${x.token})`);
console.error("");
process.exit(1);
}
console.log(
"✓ theme-lint no-primitives: components reference --c-* semantic tokens (no raw --p-*)",
);
process.exit(0);
}
if (process.argv.includes("contrast")) {
reportContrast();
reportToneContrast();
process.exit(0); // never blocks
}
if (process.argv.includes("css-colors")) {
const v = checkAppCss();
if (v.length) {
console.error(
`\n✖ theme-lint css-colors: ${v.length} hardcoded colour(s) in source CSS:\n`,
);
for (const x of v) console.error(` ${x.file}:${x.line} ${x.msg}`);
console.error(
`\nDefine every colour once in core/theme/primitives.css and reference it with var(--p-…).\n`,
);
process.exit(1);
}
console.log(
"✓ theme-lint css-colors: source CSS is free of hardcoded colour",
);
process.exit(0);
}
if (process.argv.includes("code-colors")) {
const v = checkCodeColors();
if (v.length) {
console.error(
`\n✖ theme-lint code-colors: ${v.length} hardcoded colour(s) in TS/TSX:\n`,
);
for (const x of v) console.error(` ${x.file}:${x.line} ${x.msg}`);
console.error("");
process.exit(1);
}
console.log(
"✓ theme-lint code-colors: no hardcoded colour in TS/TSX DOM code (rendering/vendor/config areas exempt)",
);
process.exit(0);
}
const violations = check();
if (violations.length) {
// Block near-invisible status tones (< TONE_INVISIBLE) in either theme.
const toneViolations = toneContrastResults().filter(
(r) => r.ratio != null && r.ratio < TONE_INVISIBLE,
);
// Block references to --p-*/--c-* tokens that are defined nowhere (no fallback).
const unresolvedTokens = checkTokenResolution();
if (unresolvedTokens.length) {
console.error(
`\n✖ theme-lint: ${violations.length} raw/duplicate colour(s) in core/theme/:\n`,
`\n✖ theme-lint: ${unresolvedTokens.length} reference(s) to undefined --p-*/--c-* token(s) (colour silently drops to transparent/inherited):\n`,
);
for (const v of violations) console.error(` ${v.file}:${v.line} ${v.msg}`);
for (const r of unresolvedTokens)
console.error(
` ${r.file}:${r.line} var(${r.token}) — not defined anywhere`,
);
console.error(
`\nDefine every colour once in core/theme/primitives.css and reference it with var(--p-…).\n`,
`\nDefine the token (primitive in primitives.css, semantic in colors.css) or give the var() a fallback.\n`,
);
}
if (violations.length || toneViolations.length || unresolvedTokens.length) {
if (violations.length) {
console.error(
`\n✖ theme-lint: ${violations.length} raw/duplicate colour(s) in core/theme/:\n`,
);
for (const v of violations)
console.error(` ${v.file}:${v.line} ${v.msg}`);
console.error(
`\nDefine every colour once in core/theme/primitives.css and reference it with var(--p-…).\n`,
);
}
if (toneViolations.length) {
console.error(
`\n✖ theme-lint: ${toneViolations.length} status tone(s) below the ${TONE_INVISIBLE}:1 legibility floor (text nearly invisible on its own fill):\n`,
);
for (const v of toneViolations)
console.error(
` ${v.base} on ${v.fill}${v.ratio.toFixed(2)}:1 in ${v.theme} theme`,
);
console.error(
`\nGive --color-{hue}-light a paler/darker tint in core/tokens/tokens.css so the label is legible.\n`,
);
}
process.exit(1);
}
console.log(
"✓ theme-lint: core/theme colours all route through the primitive palette",
"✓ theme-lint: core/theme colours route through the primitive palette; status tones clear the legibility floor",
);
@@ -8,7 +8,7 @@
}
.freeHighlight {
background: linear-gradient(135deg, #6366f1, #ec4899);
background: linear-gradient(135deg, var(--c-hue-indigo), var(--c-hue-pink));
-webkit-background-clip: text;
background-clip: text;
color: transparent;
@@ -36,7 +36,7 @@
flex-direction: column;
max-height: 200px;
overflow-y: auto;
border: 1px solid var(--border-default, #d1d5db);
border: 1px solid var(--c-border);
border-radius: 12px;
margin-bottom: 12px;
}
@@ -49,7 +49,7 @@
}
.memberRow + .memberRow {
border-top: 1px solid var(--border-subtle, #e5e7eb);
border-top: 1px solid var(--c-border-subtle);
}
.memberIdentity {
@@ -62,7 +62,7 @@
.memberName {
font-size: 14px;
font-weight: 600;
color: var(--onboarding-title, #111827);
color: var(--c-text);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
@@ -70,7 +70,7 @@
.memberEmail {
font-size: 12px;
color: var(--onboarding-body, #6b7280);
color: var(--c-text-muted);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
@@ -87,7 +87,7 @@ export function FreeLimitReachedModal({ onClose }: FreeLimitReachedModalProps) {
content: {
overflow: "hidden",
border: "none",
background: "var(--bg-surface)",
background: "var(--c-surface)",
maxHeight: "90vh",
display: "flex",
flexDirection: "column",
@@ -87,7 +87,7 @@ export function SpendCapReachedModal({ onClose }: SpendCapReachedModalProps) {
content: {
overflow: "hidden",
border: "none",
background: "var(--bg-surface)",
background: "var(--c-surface)",
maxHeight: "90vh",
display: "flex",
flexDirection: "column",
@@ -2,25 +2,22 @@
Uses the app's semantic theme tokens (theme.css) so it tracks light/dark. */
.payg {
--payg-accent: #0a8bff;
--payg-accent-soft: rgba(10, 139, 255, 0.1);
--payg-accent: var(--c-primary);
--payg-accent-soft: color-mix(in srgb, var(--c-primary) 10%, transparent);
--payg-radius: 14px;
--payg-gap: 20px;
/* Cards sit ON the modal content bg; in light that's white-on-white so we
lean on border + shadow. Tokens are overridden per scheme below. */
--payg-card-bg: var(--bg-surface);
--payg-card-border: var(--border-default);
--payg-inset-bg: var(--bg-raised);
--payg-divider: var(--border-subtle);
--payg-card-bg: var(--c-surface);
--payg-card-border: var(--c-border);
--payg-inset-bg: var(--c-surface-raised);
--payg-divider: var(--c-border-subtle);
}
/* Dark mode: modal content bg is #131729 (--bg-surface), so lift cards above it */
/* Dark mode: surfaces/borders track the neutral --c-* tokens (set on the base
rule); only the brand-azure accent tint is tuned brighter for dark. */
[data-mantine-color-scheme="dark"] .payg {
--payg-card-bg: #1c2340;
--payg-card-border: #2d3560;
--payg-inset-bg: #0d1020;
--payg-accent-soft: rgba(79, 142, 245, 0.16);
--payg-divider: #2d3560;
--payg-accent-soft: color-mix(in srgb, var(--c-primary) 16%, transparent);
}
/* ── Header ─────────────────────────────────────────────────────────── */
@@ -29,12 +26,12 @@
font-size: 1.35rem;
font-weight: 700;
letter-spacing: -0.01em;
color: var(--text-primary);
color: var(--c-text);
}
.payg-header__subtitle {
margin-top: 4px;
font-size: 0.875rem;
color: var(--text-muted);
color: var(--c-text-subtle);
}
.payg-role-pill {
display: inline-flex;
@@ -49,12 +46,12 @@
.payg-role-pill[data-leader="true"] {
background: var(--payg-accent-soft);
color: var(--payg-accent);
border: 1px solid rgba(10, 139, 255, 0.25);
border: 1px solid color-mix(in srgb, var(--c-primary) 25%, transparent);
}
.payg-role-pill[data-leader="false"] {
background: var(--bg-muted);
color: var(--text-muted);
border: 1px solid var(--border-default);
background: var(--c-surface-sunken);
color: var(--c-text-subtle);
border: 1px solid var(--c-border);
}
/* ── Plan header: free-vs-metered split (shared by free + subscribed) ──── */
@@ -73,7 +70,7 @@
}
.payg-planhead__eyebrow {
font-size: 0.78rem;
color: var(--text-muted);
color: var(--c-text-subtle);
}
.payg-planhead__split {
display: grid;
@@ -98,7 +95,7 @@
margin-bottom: 8px;
}
.payg-planhead__lbl--free {
color: #10b981;
color: var(--c-success);
}
.payg-planhead__lbl--meter {
color: var(--payg-accent);
@@ -110,14 +107,14 @@
margin: 0;
font-size: 1.05rem;
font-weight: 700;
color: var(--text-primary);
color: var(--c-text);
letter-spacing: -0.01em;
line-height: 1.25;
}
.payg-planhead__body {
margin: 5px 0 0;
font-size: 0.85rem;
color: var(--text-muted);
color: var(--c-text-subtle);
line-height: 1.5;
}
@media (max-width: 640px) {
@@ -147,11 +144,11 @@
.payg-card__title {
font-size: 0.95rem;
font-weight: 650;
color: var(--text-primary);
color: var(--c-text);
}
.payg-card__subtitle {
font-size: 0.8125rem;
color: var(--text-muted);
color: var(--c-text-subtle);
margin-top: 2px;
}
@@ -175,22 +172,22 @@
.payg-hero[data-state="FULL"]::before {
background: linear-gradient(
135deg,
rgba(10, 139, 255, 0.12) 0%,
rgba(10, 139, 255, 0) 55%
color-mix(in srgb, var(--c-primary) 12%, transparent) 0%,
color-mix(in srgb, var(--c-primary) 0%, transparent) 55%
);
}
.payg-hero[data-state="WARNED"]::before {
background: linear-gradient(
135deg,
rgba(234, 179, 8, 0.16) 0%,
rgba(234, 179, 8, 0) 55%
color-mix(in srgb, var(--c-warning) 16%, transparent) 0%,
color-mix(in srgb, var(--c-warning) 0%, transparent) 55%
);
}
.payg-hero[data-state="DEGRADED"]::before {
background: linear-gradient(
135deg,
rgba(239, 68, 68, 0.16) 0%,
rgba(239, 68, 68, 0) 55%
color-mix(in srgb, var(--c-danger) 16%, transparent) 0%,
color-mix(in srgb, var(--c-danger) 0%, transparent) 55%
);
}
.payg-hero__inner {
@@ -202,7 +199,7 @@
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--text-muted);
color: var(--c-text-subtle);
}
.payg-hero__figure {
display: flex;
@@ -215,12 +212,12 @@
font-weight: 750;
line-height: 1;
letter-spacing: -0.03em;
color: var(--text-primary);
color: var(--c-text);
font-variant-numeric: tabular-nums;
}
.payg-hero__cap {
font-size: 1rem;
color: var(--text-muted);
color: var(--c-text-subtle);
font-variant-numeric: tabular-nums;
}
.payg-hero__meta {
@@ -229,10 +226,10 @@
flex-wrap: wrap;
gap: 6px 18px;
font-size: 0.8125rem;
color: var(--text-secondary);
color: var(--c-text-muted);
}
.payg-hero__meta-dot {
color: var(--border-strong);
color: var(--c-border-strong);
}
.payg-hero__credit {
display: inline-flex;
@@ -242,11 +239,11 @@
font-size: 0.75rem;
font-weight: 600;
color: var(--color-green-700);
background: rgba(34, 197, 94, 0.14);
background: color-mix(in srgb, var(--c-success) 14%, transparent);
}
[data-mantine-color-scheme="dark"] .payg-hero__credit {
color: #4ade80;
background: rgba(34, 197, 94, 0.18);
color: var(--c-success);
background: color-mix(in srgb, var(--c-success) 18%, transparent);
}
/* Make Mantine default-variant buttons read clearly on the lifted cards in
@@ -257,16 +254,24 @@
/* Dark mode: the stock default button is a flat slab that disappears into the
card. Lift it with a soft top-down gradient + brighter border, and warm the
hover so the buttons feel tactile against --payg-card-bg (#1c2340). Driven
through Mantine's --button-* vars. The same rule covers disabled buttons
hover so the buttons feel tactile against the card surface. Driven through
Mantine's --button-* vars. The same rule covers disabled buttons
(Cancel/Update cap before the form is dirty) so they read identically to the
always-enabled ones — Mantine only fades them via opacity. */
[data-mantine-color-scheme="dark"]
.payg
.mantine-Button-root[data-variant="default"] {
--button-bg: linear-gradient(180deg, #232a50 0%, #1c2340 100%);
--button-hover: linear-gradient(180deg, #2d3560 0%, #232a50 100%);
--button-bd: 1px solid #2d3560;
--button-bg: linear-gradient(
180deg,
var(--c-surface-raised) 0%,
var(--c-surface) 100%
);
--button-hover: linear-gradient(
180deg,
var(--c-border) 0%,
var(--c-surface-raised) 100%
);
--button-bd: 1px solid var(--c-border);
--button-color: var(--mantine-color-white);
box-shadow:
0 1px 0 rgba(255, 255, 255, 0.04) inset,
@@ -307,7 +312,7 @@
}
.payg-status[data-state="FULL"] .payg-status__dot {
background: var(--color-green-500);
box-shadow: 0 0 0 3px rgba(34, 197, 94, 0.18);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--c-success) 18%, transparent);
}
.payg-status[data-state="WARNED"] {
background: var(--color-yellow-100);
@@ -315,7 +320,7 @@
}
.payg-status[data-state="WARNED"] .payg-status__dot {
background: var(--color-yellow-500);
box-shadow: 0 0 0 3px rgba(234, 179, 8, 0.2);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--c-warning) 20%, transparent);
}
.payg-status[data-state="DEGRADED"] {
background: var(--color-red-100);
@@ -323,7 +328,7 @@
}
.payg-status[data-state="DEGRADED"] .payg-status__dot {
background: var(--color-red-500);
box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.2);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--c-danger) 20%, transparent);
}
/* Segmented usage bar */
@@ -331,7 +336,7 @@
margin-top: 18px;
height: 10px;
border-radius: 999px;
background: var(--bg-muted);
background: var(--c-surface-sunken);
overflow: hidden;
position: relative;
}
@@ -341,13 +346,13 @@
transition: width 0.5s cubic-bezier(0.16, 1, 0.3, 1);
}
.payg-bar__fill[data-state="FULL"] {
background: linear-gradient(90deg, #0a8bff, #38bdf8);
background: var(--c-primary);
}
.payg-bar__fill[data-state="WARNED"] {
background: linear-gradient(90deg, #f59e0b, #fbbf24);
background: var(--c-warning);
}
.payg-bar__fill[data-state="DEGRADED"] {
background: linear-gradient(90deg, #dc2626, #f87171);
background: var(--c-danger);
}
/* ── "What counts as a document?" expandable help ───────────────────── */
@@ -365,10 +370,10 @@
font: inherit;
font-size: 0.8125rem;
font-weight: 600;
color: var(--text-secondary);
color: var(--c-text-muted);
}
.payg-help__toggle:hover {
color: var(--text-primary);
color: var(--c-text);
}
.payg-help__chevron {
transition: transform 0.15s ease;
@@ -381,10 +386,10 @@
padding: 12px 14px;
border-radius: 10px;
border: 1px solid var(--payg-card-border);
background: var(--bg-muted);
background: var(--c-surface-sunken);
font-size: 0.8125rem;
line-height: 1.45;
color: var(--text-secondary);
color: var(--c-text-muted);
}
.payg-help__panel ul {
margin: 0;
@@ -401,7 +406,7 @@
padding: 13px 16px;
border-radius: 10px;
background: var(--payg-accent-soft);
border: 1px solid rgba(10, 139, 255, 0.2);
border: 1px solid color-mix(in srgb, var(--c-primary) 20%, transparent);
}
.payg-preview__icon {
color: var(--payg-accent);
@@ -409,12 +414,12 @@
}
.payg-preview__main {
font-size: 0.875rem;
color: var(--text-primary);
color: var(--c-text);
font-weight: 550;
}
.payg-preview__note {
font-size: 0.75rem;
color: var(--text-muted);
color: var(--c-text-subtle);
margin-top: 1px;
}
@@ -453,17 +458,17 @@
flex-shrink: 0;
}
.payg-gate[data-enabled="true"] .payg-gate__chip {
background: rgba(34, 197, 94, 0.16);
color: #22c55e;
background: color-mix(in srgb, var(--c-success) 16%, transparent);
color: var(--c-success);
}
.payg-gate[data-enabled="false"] .payg-gate__chip {
background: rgba(239, 68, 68, 0.16);
color: #f87171;
background: color-mix(in srgb, var(--c-danger) 16%, transparent);
color: var(--c-danger);
}
.payg-gate__label {
font-size: 0.8125rem;
line-height: 1.35;
color: var(--text-primary);
color: var(--c-text);
flex: 1;
}
.payg-gate__tag {
@@ -477,16 +482,16 @@
white-space: nowrap;
}
.payg-gate__tag[data-variant="on"] {
color: var(--text-muted);
background: var(--bg-muted);
color: var(--c-text-subtle);
background: var(--c-surface-sunken);
}
.payg-gate__tag[data-variant="pause"] {
color: #dc2626;
background: rgba(239, 68, 68, 0.12);
color: var(--c-danger);
background: color-mix(in srgb, var(--c-danger) 12%, transparent);
}
[data-mantine-color-scheme="dark"] .payg-gate__tag[data-variant="pause"] {
color: #f87171;
background: rgba(239, 68, 68, 0.18);
color: var(--c-danger);
background: color-mix(in srgb, var(--c-danger) 18%, transparent);
}
/* ── Member rows ────────────────────────────────────────────────────── */
@@ -509,17 +514,17 @@
justify-content: center;
font-size: 0.8125rem;
font-weight: 650;
color: #fff;
color: var(--c-text-on-primary);
flex-shrink: 0;
}
.payg-member__name {
font-size: 0.875rem;
font-weight: 550;
color: var(--text-primary);
color: var(--c-text);
}
.payg-member__email {
font-size: 0.75rem;
color: var(--text-muted);
color: var(--c-text-subtle);
}
.payg-member__usage {
text-align: right;
@@ -527,7 +532,7 @@
}
.payg-member__usage-num {
font-size: 0.8125rem;
color: var(--text-primary);
color: var(--c-text);
font-variant-numeric: tabular-nums;
}
.payg-member__minibar {
@@ -570,27 +575,27 @@
}
.payg-activity__label {
font-size: 0.8438rem;
color: var(--text-primary);
color: var(--c-text);
flex: 1;
}
.payg-activity__ts {
font-size: 0.75rem;
color: var(--text-muted);
color: var(--c-text-subtle);
}
.payg-activity__kind {
font-size: 0.625rem;
font-weight: 700;
letter-spacing: 0.05em;
text-transform: uppercase;
color: var(--text-muted);
background: var(--bg-muted);
color: var(--c-text-subtle);
background: var(--c-surface-sunken);
padding: 2px 8px;
border-radius: 999px;
}
.payg-activity__units {
font-size: 0.8125rem;
font-weight: 650;
color: var(--text-primary);
color: var(--c-text);
font-variant-numeric: tabular-nums;
min-width: 58px;
text-align: right;
@@ -604,20 +609,20 @@
gap: 16px;
padding: 20px 24px;
border-radius: var(--payg-radius);
border: 1px solid rgba(99, 91, 255, 0.28);
border: 1px solid color-mix(in srgb, var(--c-accent-stripe) 28%, transparent);
background: linear-gradient(
135deg,
rgba(99, 91, 255, 0.1) 0%,
rgba(99, 91, 255, 0.02) 100%
color-mix(in srgb, var(--c-accent-stripe) 10%, transparent) 0%,
color-mix(in srgb, var(--c-accent-stripe) 2%, transparent) 100%
);
}
.payg-stripe__title {
font-size: 0.9375rem;
font-weight: 650;
color: var(--text-primary);
color: var(--c-text);
}
.payg-stripe__subtitle {
font-size: 0.8125rem;
color: var(--text-muted);
color: var(--c-text-subtle);
margin-top: 2px;
}
@@ -60,12 +60,12 @@ interface PaygProps {
// Stable-ish avatar colour from a string.
const AVATAR_COLORS = [
"#0a8bff",
"#8b5cf6",
"#ec4899",
"#10b981",
"#f59e0b",
"#06b6d4",
"var(--c-avatar-1)",
"var(--c-avatar-2)",
"var(--c-avatar-3)",
"var(--c-avatar-4)",
"var(--c-avatar-5)",
"var(--c-avatar-6)",
];
function avatarColor(seed: string): string {
let h = 0;
@@ -70,11 +70,11 @@
gap: 8px;
font-size: 0.8rem;
line-height: 1.45;
color: var(--text-muted);
color: var(--c-text-subtle);
}
.paygf-proc__membernote-icon {
flex-shrink: 0;
color: var(--text-muted);
color: var(--c-text-subtle);
font-size: 1.1rem !important;
margin-top: 1px;
}
@@ -113,12 +113,12 @@
font-weight: 750;
line-height: 1;
letter-spacing: -0.02em;
color: var(--text-primary);
color: var(--c-text);
font-variant-numeric: tabular-nums;
}
.paygf-meter__cap {
font-size: 0.85rem;
color: var(--text-muted);
color: var(--c-text-subtle);
font-variant-numeric: tabular-nums;
}
.paygf-meter .payg-bar {
@@ -131,7 +131,7 @@
align-items: center;
gap: 6px 10px;
font-size: 0.78rem;
color: var(--text-secondary);
color: var(--c-text-muted);
}
/* ── Leader CTA card ──────────────────────────────────────────────────── */
@@ -145,8 +145,10 @@
border: 1.5px solid transparent;
background:
linear-gradient(var(--payg-card-bg), var(--payg-card-bg)) padding-box,
linear-gradient(135deg, var(--payg-accent) 0%, #6c5ce7 100%) border-box;
box-shadow: 0 10px 28px -18px rgba(10, 139, 255, 0.4);
linear-gradient(135deg, var(--payg-accent) 0%, var(--c-hue-purple) 100%)
border-box;
box-shadow: 0 10px 28px -18px
color-mix(in srgb, var(--c-primary) 40%, transparent);
display: flex;
flex-direction: column;
gap: 16px;
@@ -161,8 +163,12 @@
flex-shrink: 0;
padding: 10px;
border-radius: 12px;
background: linear-gradient(135deg, var(--payg-accent) 0%, #6c5ce7 100%);
color: white !important;
background: linear-gradient(
135deg,
var(--payg-accent) 0%,
var(--c-hue-purple) 100%
);
color: var(--c-text-on-primary) !important;
font-size: 1.7rem !important;
}
.paygf-cta__heading-text {
@@ -172,14 +178,14 @@
margin: 0;
font-size: 1.2rem;
font-weight: 700;
color: var(--text-primary);
color: var(--c-text);
letter-spacing: -0.01em;
line-height: 1.25;
}
.paygf-cta__subtitle {
margin: 4px 0 0;
font-size: 0.9rem;
color: var(--text-muted);
color: var(--c-text-subtle);
line-height: 1.5;
}
@@ -196,7 +202,7 @@
align-items: flex-start;
gap: 8px;
font-size: 0.85rem;
color: var(--text-primary);
color: var(--c-text);
line-height: 1.45;
}
.paygf-cta__check {
@@ -220,7 +226,7 @@
.paygf-cta__reassurance {
margin: 0;
font-size: 0.78rem;
color: var(--text-muted);
color: var(--c-text-subtle);
text-align: right;
}
@@ -237,7 +243,7 @@
}
.paygf-member-note__icon {
flex-shrink: 0;
color: var(--text-muted);
color: var(--c-text-subtle);
padding: 8px;
background: var(--payg-card-bg);
border-radius: 10px;
@@ -247,14 +253,14 @@
margin: 0;
font-size: 1rem;
font-weight: 700;
color: var(--text-primary);
color: var(--c-text);
letter-spacing: -0.005em;
line-height: 1.3;
}
.paygf-member-note__body {
margin: 6px 0 0;
font-size: 0.875rem;
color: var(--text-muted);
color: var(--c-text-subtle);
line-height: 1.5;
}
@@ -285,14 +291,14 @@
font-weight: 700;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--text-muted);
color: var(--c-text-subtle);
margin-bottom: 10px;
}
.paygf-explainer__icon {
font-size: 1rem !important;
}
.paygf-explainer__icon--free {
color: #10b981;
color: var(--c-success);
}
.paygf-explainer__icon--paid {
color: var(--payg-accent);
@@ -300,6 +306,6 @@
.paygf-explainer__text {
margin: 0;
font-size: 0.85rem;
color: var(--text-primary);
color: var(--c-text);
line-height: 1.55;
}
@@ -9,12 +9,12 @@
Payg.css / UpgradeModal.css treatments the control sits beside. */
.scc {
--scc-accent: #0a8bff;
--scc-accent-text: #0a8bff;
--scc-accent-soft: rgba(10, 139, 255, 0.12);
--scc-accent-border: rgba(10, 139, 255, 0.25);
--scc-chip-bg: var(--bg-muted);
--scc-chip-border: var(--border-default);
--scc-accent: var(--c-primary);
--scc-accent-text: var(--c-primary);
--scc-accent-soft: color-mix(in srgb, var(--c-primary) 12%, transparent);
--scc-accent-border: color-mix(in srgb, var(--c-primary) 25%, transparent);
--scc-chip-bg: var(--c-surface-sunken);
--scc-chip-border: var(--c-border);
/* Consistent vertical rhythm between the control row, the estimate, and any
note — without it the blocks sit flush. */
display: flex;
@@ -22,10 +22,10 @@
gap: 14px;
}
[data-mantine-color-scheme="dark"] .scc {
--scc-accent-text: #7ab4ff;
--scc-accent-soft: rgba(79, 142, 245, 0.16);
--scc-chip-bg: #1c2340;
--scc-chip-border: #2d3560;
/* Chip surface/border track the neutral --c-* tokens (base rule); only the
brand-azure accent is tuned brighter for dark. */
--scc-accent-text: var(--c-primary);
--scc-accent-soft: color-mix(in srgb, var(--c-primary) 16%, transparent);
}
/* ── Inline row: presets · custom · no-cap · (save) ──────────────────── */
@@ -47,7 +47,7 @@
border-radius: 999px;
border: 1px solid var(--scc-chip-border);
background: var(--scc-chip-bg);
color: var(--text-secondary);
color: var(--c-text-muted);
font: inherit;
font-size: 0.85rem;
font-weight: 600;
@@ -59,8 +59,8 @@
background 0.12s ease;
}
.scc-chip:hover:not(:disabled) {
color: var(--text-primary);
border-color: var(--border-strong);
color: var(--c-text);
border-color: var(--c-border-strong);
}
.scc-chip[data-selected="true"] {
background: var(--scc-accent-soft);
@@ -90,7 +90,7 @@
background 0.12s ease;
}
.scc-custom:hover {
border-color: var(--border-strong);
border-color: var(--c-border-strong);
}
.scc-custom[data-active="true"] {
border-style: solid;
@@ -98,7 +98,7 @@
background: var(--scc-accent-soft);
}
.scc-custom__symbol {
color: var(--text-muted);
color: var(--c-text-subtle);
font-size: 0.85rem;
font-weight: 600;
}
@@ -111,14 +111,14 @@
border: none;
outline: none;
background: transparent;
color: var(--text-primary);
color: var(--c-text);
font: inherit;
font-size: 0.85rem;
font-weight: 600;
font-variant-numeric: tabular-nums;
}
.scc-custom__input::placeholder {
color: var(--text-muted);
color: var(--c-text-subtle);
font-weight: 600;
}
.scc-custom:disabled,
@@ -148,17 +148,17 @@
.scc-estimate__main {
font-size: 0.875rem;
font-weight: 550;
color: var(--text-primary);
color: var(--c-text);
}
.scc-estimate__sub {
font-size: 0.75rem;
color: var(--text-muted);
color: var(--c-text-subtle);
margin-top: 1px;
}
/* Quiet helper line (e.g. "Shown in USD — change later in your currency"). */
.scc-note {
font-size: 0.8125rem;
color: var(--text-muted);
color: var(--c-text-subtle);
line-height: 1.45;
}
@@ -11,6 +11,7 @@ import {
} from "@mantine/core";
import { Button } from "@app/ui/Button";
import { ActionIcon } from "@app/ui/ActionIcon";
import { StatusBadge } from "@app/ui/StatusBadge";
import { useTranslation } from "react-i18next";
import { useSaaSTeam } from "@app/contexts/SaaSTeamContext";
import LocalIcon from "@app/components/shared/LocalIcon";
@@ -265,12 +266,14 @@ const TeamSection: React.FC = () => {
</ActionIcon>
)}
{isTeamLeader && (
<Badge color="blue">{t("team.leader", "LEADER")}</Badge>
<StatusBadge tone="info" showDot={false}>
{t("team.leader", "LEADER")}
</StatusBadge>
)}
{isPersonalTeam && (
<Badge color="gray" variant="light" size="xs">
<StatusBadge tone="neutral" showDot={false}>
{t("team.personal", "Personal")}
</Badge>
</StatusBadge>
)}
</Group>
)}
@@ -430,8 +433,8 @@ const TeamSection: React.FC = () => {
style={
member.role !== "LEADER"
? {
backgroundColor: "var(--tool-header-badge-bg)",
color: "var(--tool-header-badge-text)",
backgroundColor: "var(--c-surface-raised)",
color: "var(--c-accent-fg)",
}
: undefined
}
@@ -5,22 +5,21 @@
user feels like they're filling out one form, not navigating pages. */
.upm {
--upm-accent: #0a8bff;
--upm-accent-2: #6c5ce7;
--upm-accent-soft: rgba(10, 139, 255, 0.08);
--upm-success: #10b981;
--upm-accent: var(--c-primary);
--upm-accent-2: var(--c-hue-purple);
--upm-accent-soft: color-mix(in srgb, var(--c-primary) 8%, transparent);
--upm-success: var(--c-success);
--upm-radius: 18px;
--upm-card-bg: var(--bg-surface);
--upm-border: var(--border-default);
--upm-divider: var(--border-subtle);
--upm-text: var(--text-primary);
--upm-muted: var(--text-muted);
--upm-card-bg: var(--c-surface);
--upm-border: var(--c-border);
--upm-divider: var(--c-border-subtle);
--upm-text: var(--c-text);
--upm-muted: var(--c-text-subtle);
}
[data-mantine-color-scheme="dark"] .upm {
--upm-card-bg: #1c2340;
--upm-border: #2d3560;
--upm-divider: #2d3560;
--upm-accent-soft: rgba(79, 142, 245, 0.14);
/* Card/border/divider track the neutral --c-* tokens (base rule); only the
brand-azure accent tint is tuned for dark. */
--upm-accent-soft: color-mix(in srgb, var(--c-primary) 14%, transparent);
}
/* Backdrop locks the page and centres the modal. z-index matches
@@ -150,7 +149,7 @@
.upm-step[data-state="active"] .upm-step__dot {
background: linear-gradient(135deg, var(--upm-accent), var(--upm-accent-2));
border-color: transparent;
color: white;
color: var(--c-text-on-primary);
}
.upm-step[data-state="done"] {
color: var(--upm-success);
@@ -158,7 +157,7 @@
.upm-step[data-state="done"] .upm-step__dot {
background: var(--upm-success);
border-color: transparent;
color: white;
color: var(--c-text-on-primary);
}
.upm-step__connector {
flex: 1 1 auto;
@@ -254,7 +253,7 @@
border-radius: 6px;
}
.upm-step__edit:hover {
background: rgba(10, 139, 255, 0.12);
background: color-mix(in srgb, var(--c-primary) 12%, transparent);
}
/* Placeholder card for the loading / error / mock states — a dashed, centered
@@ -327,8 +326,10 @@
}
.upm-confirm__icon {
font-size: 64px !important;
color: #10b981;
filter: drop-shadow(0 6px 18px rgba(16, 185, 129, 0.35));
color: var(--upm-success);
filter: drop-shadow(
0 6px 18px color-mix(in srgb, var(--c-success) 35%, transparent)
);
}
.upm-confirm__title {
margin: 6px 0 0;
@@ -61,7 +61,9 @@ function Swatch({ label, src, onDark, h }: Asset & { h: number }) {
>
<img src={src} alt={label} style={{ height: h, maxWidth: 200 }} />
</div>
<figcaption style={{ fontSize: 12, color: "var(--text-muted, #71717a)" }}>
<figcaption
style={{ fontSize: 12, color: "var(--c-text-subtle, #71717a)" }}
>
{label}
</figcaption>
</figure>
@@ -221,7 +221,7 @@ const FileManager: React.FC<FileManagerProps> = ({ selectedTool }) => {
width: "100%",
border: "none",
borderRadius: "var(--radius-md)",
backgroundColor: "var(--bg-file-manager)",
backgroundColor: "var(--c-bg)",
}}
styles={{
inner: { pointerEvents: "all" },
@@ -227,7 +227,7 @@ export const TextInputWithFont: React.FC<TextInputWithFontProps> = ({
width: 24,
height: 24,
backgroundColor: textColor,
border: "1px solid #ccc",
border: "1px solid var(--c-border)",
borderRadius: 4,
cursor: disabled ? "default" : "pointer",
}}
@@ -110,7 +110,7 @@ const AddFileCard = ({
variant="tertiary"
style={{
backgroundColor: "var(--landing-button-bg)",
color: "var(--landing-button-color)",
color: "var(--c-accent-fg)",
border: "1px solid var(--landing-button-border)",
borderRadius: "2rem",
height: "38px",
@@ -129,7 +129,7 @@ const AddFileCard = ({
icon="add"
width="1.5rem"
height="1.5rem"
className="text-[var(--accent-interactive)]"
className="text-[var(--c-primary)]"
/>
<span>{t("landing.addFiles", "Add Files")}</span>
</Button>
@@ -140,7 +140,7 @@ const AddFileCard = ({
title={terminology.uploadFromComputer}
style={{
backgroundColor: "var(--landing-button-bg)",
color: "var(--landing-button-color)",
color: "var(--c-accent-fg)",
border: "1px solid var(--landing-button-border)",
borderRadius: "1rem",
height: "38px",
@@ -161,7 +161,7 @@ const AddFileCard = ({
icon={icons.uploadIconName}
width="1.25rem"
height="1.25rem"
style={{ color: "var(--accent-interactive)", flexShrink: 0 }}
style={{ color: "var(--c-primary)", flexShrink: 0 }}
/>
{isUploadHover && (
<span
@@ -181,7 +181,7 @@ const AddFileCard = ({
{/* Instruction Text */}
<span
className="text-[var(--accent-interactive)]"
className="text-[var(--c-primary)]"
style={{
fontSize: ".8rem",
textAlign: "center",
@@ -3,7 +3,7 @@
========================= */
.card {
background: var(--file-card-bg);
background: var(--c-surface-raised);
border-radius: 0.0625rem;
cursor: pointer;
transition:
@@ -28,7 +28,7 @@
/* While dragging */
.card.dragging,
.card:global(.dragging) {
outline: 1px solid var(--border-strong);
outline: 1px solid var(--c-border-strong);
box-shadow: var(--shadow-md);
transform: none !important;
}
@@ -42,15 +42,17 @@
justify-content: space-between;
padding: 0 6px;
user-select: none;
background: var(--bg-toolbar);
color: var(--text-primary);
border-bottom: 1px solid var(--border-default);
background: var(--c-bg-raised);
color: var(--c-text);
border-bottom: 1px solid var(--c-border);
}
.headerResting {
background: #3b4b6e; /* dark blue for unselected in light mode */
color: #ffffff;
border-bottom: 1px solid var(--border-default);
background: var(
--c-file-header-resting
); /* branded navy for unselected files */
color: var(--c-text-on-primary);
border-bottom: 1px solid var(--c-border);
}
.headerSelected {
@@ -62,20 +64,20 @@
/* Error highlight (transient) */
.headerError {
background: var(--color-red-200);
color: var(--text-primary);
color: var(--c-text);
border-bottom: 2px solid var(--color-red-500);
}
/* Unsupported (but not errored) header appearance */
.headerUnsupported {
background: var(--unsupported-bar-bg); /* neutral gray */
color: #ffffff;
border-bottom: 1px solid var(--unsupported-bar-border);
background: var(--c-bg-raised); /* neutral gray */
color: var(--c-text-on-primary);
border-bottom: 1px solid var(--c-border);
}
/* Selected border color in light mode */
:global([data-mantine-color-scheme="light"]) .card[data-selected="true"] {
outline-color: var(--card-selected-border);
outline-color: var(--c-primary);
}
/* Reserve space for checkbox instead of logo */
@@ -106,7 +108,7 @@
}
.headerIconButton {
color: #ffffff !important;
color: var(--c-text-on-primary) !important;
}
/* Menu dropdown */
@@ -117,12 +119,12 @@
/* -------- Title / Meta -------- */
.title {
line-height: 1.2;
color: var(--text-primary);
color: var(--c-text);
}
.meta {
margin-top: 2px;
color: var(--text-secondary);
color: var(--c-text-muted);
}
/* -------- Preview area -------- */
@@ -131,7 +133,7 @@
width: 100%;
height: 100%;
overflow: hidden;
background: var(--file-card-bg);
background: var(--c-surface-raised);
}
.previewPaper {
@@ -142,7 +144,7 @@
display: grid;
position: relative;
overflow: hidden;
background: var(--file-card-bg);
background: var(--c-surface-raised);
}
/* Thumbnail fallback */
@@ -152,7 +154,7 @@
inset: 0;
display: grid;
place-items: center;
color: var(--text-secondary);
color: var(--c-text-muted);
font-weight: 600;
font-size: 12px;
}
@@ -162,7 +164,7 @@
position: absolute;
bottom: 6px;
right: 6px;
color: var(--text-secondary);
color: var(--c-text-muted);
z-index: 1;
cursor: grab;
display: inline-flex;
@@ -173,12 +175,12 @@
position: absolute;
left: 0;
top: 44px; /* just below header */
background: var(--bg-toolbar);
border-bottom: 1px solid var(--border-default);
background: var(--c-bg-raised);
border-bottom: 1px solid var(--c-border);
z-index: 20;
overflow: hidden;
animation: slideDown 140ms ease-out;
color: var(--text-primary);
color: var(--c-text);
}
@keyframes slideDown {
@@ -200,13 +202,13 @@
padding: 12px 16px;
background: transparent;
border: none;
color: var(--text-primary);
color: var(--c-text);
cursor: pointer;
text-align: left;
}
.actionRow:hover {
background: var(--hover-bg);
background: var(--c-hover);
}
.actionDanger {
@@ -215,7 +217,7 @@
.actionsDivider {
height: 1px;
background: var(--border-default);
background: var(--c-border);
margin: 4px 0;
}
@@ -229,13 +231,13 @@
}
.pinned {
color: #ffc107 !important;
color: var(--c-warning) !important;
}
/* Unsupported file indicator */
.unsupportedPill {
margin-left: 1.75rem;
background: #6b7280;
background: var(--c-neutral-fill);
color: white;
padding: 4px 8px;
border-radius: 12px;
@@ -252,7 +254,7 @@
.errorPill {
margin-left: 1.75rem;
background: var(--color-red-500);
color: #ffffff;
color: var(--c-text-on-primary);
padding: 4px 8px;
border-radius: 12px;
font-size: 10px;
@@ -291,36 +293,36 @@
DARK MODE OVERRIDES
========================= */
:global([data-mantine-color-scheme="dark"]) .card {
outline-color: #1c2340; /* deselected stroke */
outline-color: var(--c-border); /* deselected stroke */
}
:global([data-mantine-color-scheme="dark"]) .card[data-selected="true"] {
outline-color: #2d3560; /* selected stroke (subtle navy) */
outline-color: var(--c-primary); /* selected stroke (subtle navy) */
}
:global([data-mantine-color-scheme="dark"]) .headerResting {
background: #0d1020; /* requested default unselected color */
color: var(--tool-header-text); /* #D0D6DC */
border-bottom-color: var(--tool-header-border); /* #3A4047 */
/* Background flips via the themed --c-file-header-resting token. */
color: var(--c-accent-fg, var(--c-primary));
border-bottom-color: var(--c-border);
}
:global([data-mantine-color-scheme="dark"]) .headerSelected {
background: var(--tool-header-border); /* #3A4047 */
color: var(--tool-header-text); /* #D0D6DC */
border-bottom-color: var(--tool-header-border);
background: var(--c-border);
color: var(--c-accent-fg, var(--c-primary));
border-bottom-color: var(--c-border);
}
:global([data-mantine-color-scheme="dark"]) .title {
color: #d0d6dc; /* title text */
color: var(--c-text); /* title text */
}
:global([data-mantine-color-scheme="dark"]) .meta {
color: #6b7280; /* subtitle text */
color: var(--c-text-muted); /* subtitle text */
}
/* Light mode selected header stroke override */
:global([data-mantine-color-scheme="light"]) .card[data-selected="true"] {
outline-color: #3b4b6e;
outline-color: var(--c-primary);
}
/* =========================
@@ -333,7 +335,7 @@
height: calc(310px - 0.5rem);
margin: 0.5rem auto 0;
background: var(--c-bg);
border: 1.5px solid var(--border-default);
border: 1.5px solid var(--c-border);
border-radius: 12px;
box-shadow: var(--shadow-md);
cursor: pointer;
@@ -347,11 +349,11 @@
.addFileCard:hover {
box-shadow: var(--shadow-lg);
transform: translateY(-1px);
border-color: var(--accent-interactive);
border-color: var(--c-primary);
}
.addFileCard:focus-visible {
outline: 2px solid var(--accent-interactive);
outline: 2px solid var(--c-primary);
outline-offset: 2px;
}
@@ -386,7 +388,7 @@
}
.addFileCard:hover .addFileText {
color: var(--text-primary);
color: var(--c-text);
}
.addFileSubtext {
@@ -75,7 +75,7 @@
box-shadow: var(--shadow-md);
overflow: hidden;
position: relative;
background: var(--bg-surface);
background: var(--c-surface);
transition:
box-shadow 0.15s ease,
transform 0.2s ease;
@@ -91,7 +91,7 @@
height: 100%;
object-fit: contain;
border-radius: 12px;
background: var(--bg-surface);
background: var(--c-surface);
display: block;
position: relative;
z-index: 1;
@@ -115,7 +115,7 @@
.fileName {
font-size: 13px;
font-weight: 600;
color: var(--text-primary);
color: var(--c-text);
line-height: var(--file-name-line-height);
height: calc(var(--file-name-lines) * var(--file-name-line-height));
margin: 0;
@@ -149,7 +149,7 @@
.fileMeta {
font-size: 11px;
color: var(--text-secondary);
color: var(--c-text-muted);
line-height: var(--file-meta-line-height);
height: var(--file-meta-line-height);
overflow: hidden;
@@ -183,7 +183,7 @@
display: flex;
align-items: center;
justify-content: center;
background: rgba(220, 38, 38, 0.12);
background: color-mix(in srgb, var(--c-danger) 12%, transparent);
border-radius: 12px;
z-index: 2;
pointer-events: none;
@@ -191,7 +191,7 @@
.errorPill {
background: var(--mantine-color-red-6);
color: white;
color: var(--c-text-on-primary);
border-radius: 12px;
padding: 4px 12px;
font-size: 12px;
@@ -233,7 +233,7 @@
.ownershipBadge {
background: var(--mantine-color-blue-6);
color: white;
color: var(--c-text-on-primary);
border-radius: 6px;
padding: 2px 7px;
font-size: 10px;
@@ -247,14 +247,14 @@
padding: 1px 5px;
font-size: 10px;
font-weight: 700;
color: white;
color: var(--c-text-on-primary);
line-height: 16px;
white-space: nowrap;
}
.pinnedBadge {
background: var(--mantine-color-yellow-6);
color: white;
color: var(--c-text-on-primary);
border-radius: 4px;
padding: 2px 4px;
display: flex;
@@ -268,7 +268,7 @@
bottom: 6px;
right: 6px;
cursor: grab;
color: var(--text-secondary);
color: var(--c-text-muted);
opacity: 0;
transition: opacity 0.15s ease;
pointer-events: none;
@@ -50,7 +50,7 @@ const DesktopLayout: React.FC = () => {
flex: 1,
display: "flex",
flexDirection: "column",
backgroundColor: "var(--bg-file-list)",
backgroundColor: "var(--c-surface)",
border: "1px solid var(--mantine-color-gray-2)",
boxShadow: "0 2px 8px rgba(0, 0, 0, 0.1)",
overflow: "hidden",
@@ -81,8 +81,8 @@ const EmptyFilesState: React.FC = () => {
variant="tertiary"
aria-label={t("emptyFilesState.upload", "Upload")}
style={{
backgroundColor: "var(--bg-file-manager)",
color: "var(--landing-button-color)",
backgroundColor: "var(--c-bg)",
color: "var(--c-accent-fg)",
border: "1px solid var(--landing-button-border)",
borderRadius: isUploadHover ? "2rem" : "1rem",
height: "38px",
@@ -103,7 +103,7 @@ const EmptyFilesState: React.FC = () => {
icon={icons.uploadIconName}
width="1.25rem"
height="1.25rem"
style={{ color: "var(--accent-interactive)" }}
style={{ color: "var(--c-primary)" }}
/>
{isUploadHover && (
<span style={{ marginLeft: ".5rem" }}>
@@ -115,7 +115,7 @@ const EmptyFilesState: React.FC = () => {
{/* Instruction Text */}
<span
className="text-[var(--accent-interactive)]"
className="text-[var(--c-primary)]"
style={{ fontSize: ".8rem", textAlign: "center" }}
>
{terminology.dropFilesHere}
@@ -189,7 +189,7 @@ const FileListItem: React.FC<FileListItemProps> = ({
backgroundColor:
isSelected || shouldShowHovered
? "var(--mantine-color-gray-1)"
: "var(--bg-file-list)",
: "var(--c-surface)",
opacity: 1,
transition: "background-color 0.15s ease",
userSelect: "none",
@@ -47,7 +47,7 @@ const MobileLayout: React.FC = () => {
flex: 1,
display: "flex",
flexDirection: "column",
backgroundColor: "var(--bg-file-list)",
backgroundColor: "var(--c-surface)",
borderRadius: "0.5rem",
border: "1px solid var(--mantine-color-gray-2)",
overflow: "hidden",
@@ -201,7 +201,7 @@ export function FileDetailsPanel({
<img src={single.thumbnailUrl} alt="" />
) : (
<PictureAsPdfIcon
style={{ fontSize: "3rem", color: "var(--text-muted)" }}
style={{ fontSize: "3rem", color: "var(--c-text-subtle)" }}
/>
)}
</div>
@@ -499,7 +499,7 @@ function FolderCard({
<div
className="files-page-card-thumb"
style={{
background: `linear-gradient(135deg, color-mix(in srgb, ${folder.color ?? "var(--accent-interactive, #6366f1)"} 18%, var(--bg-surface)), color-mix(in srgb, ${folder.color ?? "var(--accent-interactive, #6366f1)"} 6%, var(--bg-surface)))`,
background: `linear-gradient(135deg, color-mix(in srgb, ${folder.color ?? "var(--c-primary)"} 18%, var(--c-surface)), color-mix(in srgb, ${folder.color ?? "var(--c-primary)"} 6%, var(--c-surface)))`,
}}
>
<FolderThumbnail
@@ -845,7 +845,7 @@ export default function FileManagerView() {
fontSize: "0.95rem",
fontWeight: 600,
padding: "0.25rem 0.5rem",
color: "var(--text-primary)",
color: "var(--c-text)",
}}
>
{currentTab === "local"
@@ -991,9 +991,9 @@ export default function FileManagerView() {
style={{
padding: "0.6rem 1.25rem",
background:
"color-mix(in srgb, var(--mantine-color-red-6, #e03131) 12%, transparent)",
color: "var(--text-primary)",
borderBottom: "1px solid var(--border-subtle)",
"color-mix(in srgb, var(--mantine-color-red-6) 12%, transparent)",
color: "var(--c-text)",
borderBottom: "1px solid var(--c-border-subtle)",
fontSize: "0.85rem",
display: "flex",
justifyContent: "space-between",
@@ -1083,15 +1083,15 @@ export default function FileManagerView() {
style={{
background:
currentTab === tab.id
? "var(--hover-bg)"
? "var(--c-hover)"
: "transparent",
border: "none",
borderRadius: "0.3rem",
padding: "0.2rem 0.6rem",
color:
currentTab === tab.id
? "var(--text-primary)"
: "var(--text-muted)",
? "var(--c-text)"
: "var(--c-text-subtle)",
fontWeight: currentTab === tab.id ? 500 : 400,
fontSize: "0.75rem",
cursor: "pointer",
@@ -1662,7 +1662,7 @@ const SearchField = React.forwardRef<
const { t } = useTranslation();
return (
<div className="files-page-search">
<SearchIcon fontSize="small" style={{ color: "var(--text-muted)" }} />
<SearchIcon fontSize="small" style={{ color: "var(--c-text-subtle)" }} />
<input
ref={ref}
type="text"
@@ -27,19 +27,17 @@ const styles = {
lineHeight: 1.2,
},
local: {
background:
"color-mix(in srgb, var(--text-muted, #6b7280) 16%, transparent)",
color: "var(--text-secondary)",
background: "color-mix(in srgb, var(--c-text-subtle) 16%, transparent)",
color: "var(--c-text-muted)",
},
cloud: {
background:
"color-mix(in srgb, var(--accent-interactive, #6366f1) 16%, transparent)",
color: "var(--accent-interactive, #6366f1)",
background: "color-mix(in srgb, var(--c-primary) 16%, transparent)",
color: "var(--c-primary)",
},
shared: {
background:
"color-mix(in srgb, var(--mantine-color-orange-6, #f97316) 16%, transparent)",
color: "var(--mantine-color-orange-6, #f97316)",
"color-mix(in srgb, var(--mantine-color-orange-6) 16%, transparent)",
color: "var(--mantine-color-orange-6)",
},
};
@@ -3,8 +3,8 @@
flex-direction: column;
width: 100%;
height: 100%;
background: var(--bg-background);
color: var(--text-primary);
background: var(--c-bg);
color: var(--c-text);
overflow: hidden;
}
@@ -22,8 +22,8 @@
gap: 1rem;
min-height: 48px;
padding: 0 0.75rem;
border-bottom: 1px solid var(--border-subtle);
background: var(--bg-toolbar);
border-bottom: 1px solid var(--c-border-subtle);
background: var(--c-bg-raised);
flex-shrink: 0;
}
@@ -52,7 +52,7 @@
border: none;
padding: 0.25rem 0.5rem;
border-radius: 0.375rem;
color: var(--text-secondary);
color: var(--c-text-muted);
cursor: pointer;
font-size: inherit;
font-weight: 500;
@@ -64,19 +64,14 @@
overflow: hidden;
text-overflow: ellipsis;
}
.files-page-breadcrumb:hover {
background: var(--hover-bg);
color: var(--text-primary);
}
.files-page-breadcrumb:hover,
.files-page-breadcrumb.is-current {
color: var(--text-primary);
background: var(--hover-bg);
background: var(--c-hover);
color: var(--c-text);
}
.files-page-breadcrumb-sep {
color: var(--text-muted);
color: var(--c-text-subtle);
font-size: 1rem !important;
width: 1rem;
height: 1rem;
@@ -88,8 +83,8 @@
display: flex;
align-items: center;
gap: 0.35rem;
background: var(--bg-surface);
border: 1px solid var(--border-subtle);
background: var(--c-surface);
border: 1px solid var(--c-border-subtle);
border-radius: 999px;
padding: 0.2rem 0.75rem;
/* Fills its grid cell; the cell's minmax(...) clamps to a sensible range. */
@@ -102,7 +97,7 @@
border: none;
outline: none;
flex: 1;
color: var(--text-primary);
color: var(--c-text);
font-size: 0.9rem;
}
@@ -116,8 +111,8 @@
.files-page-tree {
width: 17rem;
flex-shrink: 0;
border-right: 1px solid var(--border-subtle);
background: var(--bg-toolbar);
border-right: 1px solid var(--c-border-subtle);
background: var(--c-bg-raised);
overflow-y: auto;
display: flex;
flex-direction: column;
@@ -135,7 +130,7 @@
font-size: 0.8rem;
text-transform: uppercase;
letter-spacing: 0.04em;
color: var(--text-muted);
color: var(--c-text-subtle);
}
/* Tree node styles moved to FolderTreePanel.css - that's where the
@@ -156,8 +151,8 @@
align-items: center;
gap: 0.75rem;
padding: 0.55rem 1.25rem;
border-bottom: 1px solid var(--border-subtle);
background: var(--bg-background);
border-bottom: 1px solid var(--c-border-subtle);
background: var(--c-bg);
flex-shrink: 0;
flex-wrap: wrap;
min-height: 3rem;
@@ -165,7 +160,7 @@
.files-page-toolbar-info {
font-size: 0.85rem;
color: var(--text-secondary);
color: var(--c-text-muted);
font-weight: 500;
}
@@ -178,7 +173,7 @@
}
.files-page-toolbar-info span {
color: var(--text-muted);
color: var(--c-text-subtle);
}
.files-page-toolbar-actions {
@@ -226,7 +221,7 @@
.files-page-toolbar-divider {
width: 1px;
height: 1.4rem;
background: var(--border-subtle);
background: var(--c-border-subtle);
margin: 0 0.15rem;
}
@@ -264,7 +259,7 @@
padding: 1.25rem;
position: relative;
scrollbar-width: thin;
scrollbar-color: var(--border-subtle) transparent;
scrollbar-color: var(--c-border-subtle) transparent;
}
.files-page-content::-webkit-scrollbar {
@@ -273,14 +268,14 @@
}
.files-page-content::-webkit-scrollbar-thumb {
background: var(--border-subtle);
background: var(--c-border-subtle);
border-radius: 999px;
border: 2px solid transparent;
background-clip: content-box;
}
.files-page-content::-webkit-scrollbar-thumb:hover {
background: var(--text-muted);
background: var(--c-text-subtle);
background-clip: content-box;
}
@@ -288,7 +283,7 @@
.folder-tree-panel-inner,
.files-page-details-body {
scrollbar-width: thin;
scrollbar-color: var(--border-subtle) transparent;
scrollbar-color: var(--c-border-subtle) transparent;
}
.files-page-tree-list::-webkit-scrollbar,
@@ -300,7 +295,7 @@
.files-page-tree-list::-webkit-scrollbar-thumb,
.folder-tree-panel-inner::-webkit-scrollbar-thumb,
.files-page-details-body::-webkit-scrollbar-thumb {
background: var(--border-subtle);
background: var(--c-border-subtle);
border-radius: 999px;
}
@@ -313,10 +308,10 @@
.files-page-list {
display: flex;
flex-direction: column;
border: 1px solid var(--border-subtle);
border: 1px solid var(--c-border-subtle);
border-radius: 0.6rem;
overflow: hidden;
background: var(--bg-surface);
background: var(--c-surface);
}
.files-page-list-row {
@@ -325,7 +320,7 @@
gap: 0.5rem;
align-items: center;
padding: 0.5rem 0.75rem;
border-bottom: 1px solid var(--border-subtle);
border-bottom: 1px solid var(--c-border-subtle);
cursor: pointer;
font-size: 0.9rem;
transition: background-color 0.12s ease;
@@ -336,8 +331,8 @@
}
.files-page-list-row.is-header {
background: var(--bg-toolbar);
color: var(--text-muted);
background: var(--c-bg-raised);
color: var(--c-text-subtle);
text-transform: uppercase;
font-size: 0.72rem;
letter-spacing: 0.05em;
@@ -356,33 +351,25 @@
}
.files-page-list-row.is-header [data-sortable="true"]:hover {
background: var(--hover-bg);
color: var(--text-primary);
background: var(--c-hover);
color: var(--c-text);
}
.files-page-list-row:not(.is-header):hover {
background: var(--hover-bg);
}
.files-page-list-row:not(.is-header):hover,
.files-page-list-row.is-selected {
background: var(--hover-bg);
background: var(--c-hover);
}
.files-page-list-row.is-drop-target {
background: color-mix(
in srgb,
var(--accent-interactive, #6366f1) 12%,
transparent
);
box-shadow: inset 0 0 0 1px var(--accent-interactive, #6366f1);
background: color-mix(in srgb, var(--c-primary) 12%, transparent);
box-shadow: inset 0 0 0 1px var(--c-primary);
}
.files-page-card {
position: relative;
display: flex;
flex-direction: column;
background: var(--bg-surface);
border: 1px solid var(--border-subtle);
background: var(--c-surface);
border: 1px solid var(--c-border-subtle);
border-radius: 0.85rem;
overflow: hidden;
cursor: pointer;
@@ -397,8 +384,8 @@
transform: translateY(-2px);
border-color: color-mix(
in srgb,
var(--accent-interactive, #6366f1) 35%,
var(--border-subtle)
var(--c-primary) 35%,
var(--c-border-subtle)
);
box-shadow:
0 4px 6px -1px rgba(0, 0, 0, 0.08),
@@ -406,16 +393,15 @@
}
.files-page-card:focus-visible {
outline: 2px solid var(--accent-interactive, #6366f1);
outline: 2px solid var(--c-primary);
outline-offset: 2px;
}
.files-page-card.is-selected {
border-color: var(--accent-interactive, #6366f1);
border-color: var(--c-primary);
box-shadow:
0 0 0 2px var(--accent-interactive, #6366f1),
0 6px 18px -6px
color-mix(in srgb, var(--accent-interactive, #6366f1) 35%, transparent);
0 0 0 2px var(--c-primary),
0 6px 18px -6px color-mix(in srgb, var(--c-primary) 35%, transparent);
}
.files-page-card.is-folder {
@@ -426,8 +412,8 @@
aspect-ratio: 4 / 3;
background: linear-gradient(
180deg,
color-mix(in srgb, var(--text-muted) 5%, var(--bg-surface)),
var(--bg-surface)
color-mix(in srgb, var(--c-text-subtle) 5%, var(--c-surface)),
var(--c-surface)
);
display: flex;
align-items: center;
@@ -452,7 +438,7 @@
flex-direction: column;
align-items: center;
gap: 0.4rem;
color: var(--text-muted);
color: var(--c-text-subtle);
font-size: 0.72rem;
font-weight: 600;
letter-spacing: 0.04em;
@@ -472,13 +458,13 @@
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: var(--text-primary);
color: var(--c-text);
letter-spacing: -0.005em;
}
.files-page-card-meta {
font-size: 0.76rem;
color: var(--text-muted);
color: var(--c-text-subtle);
display: flex;
align-items: center;
gap: 0.4rem;
@@ -488,7 +474,7 @@
the user can tell which folder each hit lives in without navigating. */
.files-page-card-path {
font-size: 0.72rem;
color: var(--text-muted);
color: var(--c-text-subtle);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@@ -517,8 +503,8 @@
/* Style the Mantine checkbox so it reads as a soft pill rather than a
bare form control. */
.files-page-card-selector :where(.mantine-Checkbox-input) {
background: var(--bg-surface);
border-color: var(--border-subtle);
background: var(--c-surface);
border-color: var(--c-border-subtle);
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.18);
}
@@ -536,26 +522,26 @@
Use stronger specificity + !important to win over Mantine's
variant=filled inline styles. */
.files-page-card .files-page-card-actions button.mantine-ActionIcon-root {
background-color: var(--bg-raised, var(--bg-surface)) !important;
color: var(--text-primary) !important;
border: 1px solid var(--border-default, var(--border-subtle)) !important;
background-color: var(--c-surface-raised, var(--c-surface)) !important;
color: var(--c-text) !important;
border: 1px solid var(--c-border, var(--c-border-subtle)) !important;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.25);
backdrop-filter: blur(6px);
}
.files-page-card .files-page-card-actions button.mantine-ActionIcon-root:hover {
background-color: var(--hover-bg) !important;
background-color: var(--c-hover) !important;
}
/* Row-view kebab: same high-contrast treatment so it's readable
against selected (tinted) and in-workspace rows. */
.files-page-list-row .mantine-ActionIcon-root[aria-label$="actions"] {
color: var(--text-secondary);
color: var(--c-text-muted);
}
.files-page-list-row .mantine-ActionIcon-root[aria-label$="actions"]:hover {
background-color: var(--hover-bg) !important;
color: var(--text-primary);
background-color: var(--c-hover) !important;
color: var(--c-text);
}
.files-page-card-origin {
@@ -578,8 +564,8 @@
gap: 0.35rem;
padding: 0.22rem 0.55rem;
border-radius: 999px;
background: var(--mantine-color-teal-6, #10b981);
color: #ffffff;
background: var(--mantine-color-teal-6, var(--c-success));
color: var(--c-text-on-primary);
font-size: 0.68rem;
font-weight: 700;
letter-spacing: 0.04em;
@@ -594,7 +580,7 @@
width: 0.45rem;
height: 0.45rem;
border-radius: 50%;
background: #ffffff;
background: var(--c-text-on-primary);
box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.35);
animation: files-page-open-pulse 1.8s ease-in-out infinite;
}
@@ -614,8 +600,8 @@
.files-page-card.is-in-workspace {
border-color: color-mix(
in srgb,
var(--mantine-color-teal-6, #10b981) 35%,
var(--border-subtle)
var(--mantine-color-teal-6, var(--c-success)) 35%,
var(--c-border-subtle)
);
}
@@ -627,7 +613,12 @@
pointer-events: none;
background: linear-gradient(
180deg,
color-mix(in srgb, var(--mantine-color-teal-6, #10b981) 6%, transparent) 0%,
color-mix(
in srgb,
var(--mantine-color-teal-6, var(--c-success)) 6%,
transparent
)
0%,
transparent 40%
);
z-index: 0;
@@ -639,8 +630,8 @@
gap: 0.3rem;
padding: 0.12rem 0.5rem;
border-radius: 999px;
background: var(--mantine-color-teal-6, #10b981);
color: #ffffff;
background: var(--mantine-color-teal-6, var(--c-success));
color: var(--c-text-on-primary);
font-size: 0.68rem;
font-weight: 700;
letter-spacing: 0.04em;
@@ -653,7 +644,7 @@
.files-page-list-row.is-in-workspace {
background: color-mix(
in srgb,
var(--mantine-color-teal-6, #10b981) 4%,
var(--mantine-color-teal-6, var(--c-success)) 4%,
transparent
);
}
@@ -685,13 +676,9 @@
}
.files-page-card.is-drop-target {
background: color-mix(
in srgb,
var(--accent-interactive, #6366f1) 10%,
var(--bg-surface)
);
box-shadow: 0 0 0 2px var(--accent-interactive, #6366f1);
border-color: var(--accent-interactive, #6366f1);
background: color-mix(in srgb, var(--c-primary) 10%, var(--c-surface));
box-shadow: 0 0 0 2px var(--c-primary);
border-color: var(--c-primary);
}
@keyframes files-page-skeleton-pulse {
@@ -707,9 +694,9 @@
display: block;
background: linear-gradient(
90deg,
color-mix(in srgb, var(--text-muted) 12%, transparent) 25%,
color-mix(in srgb, var(--text-muted) 22%, transparent) 50%,
color-mix(in srgb, var(--text-muted) 12%, transparent) 75%
color-mix(in srgb, var(--c-text-subtle) 12%, transparent) 25%,
color-mix(in srgb, var(--c-text-subtle) 22%, transparent) 50%,
color-mix(in srgb, var(--c-text-subtle) 12%, transparent) 75%
);
background-size: 200% 100%;
animation: files-page-skeleton-pulse 1.4s ease-in-out infinite;
@@ -718,14 +705,14 @@
.files-page-skeleton-card {
pointer-events: none;
border-color: var(--border-subtle);
border-color: var(--c-border-subtle);
box-shadow: none;
}
.files-page-skeleton-card:hover {
transform: none;
box-shadow: none;
border-color: var(--border-subtle);
border-color: var(--c-border-subtle);
}
.files-page-skeleton-row {
@@ -743,7 +730,7 @@
justify-content: center;
height: 100%;
gap: 0.75rem;
color: var(--text-muted);
color: var(--c-text-subtle);
padding: 4rem 1.5rem;
text-align: center;
}
@@ -755,25 +742,21 @@
width: 5rem;
height: 5rem;
border-radius: 50%;
background: color-mix(
in srgb,
var(--accent-interactive, #6366f1) 12%,
transparent
);
color: var(--accent-interactive, #6366f1);
background: color-mix(in srgb, var(--c-primary) 12%, transparent);
color: var(--c-primary);
margin-bottom: 0.25rem;
}
.files-page-empty-title {
font-size: 1.15rem;
color: var(--text-primary);
color: var(--c-text);
font-weight: 600;
letter-spacing: -0.01em;
}
.files-page-empty-hint {
font-size: 0.9rem;
color: var(--text-muted);
color: var(--c-text-subtle);
max-width: 30rem;
line-height: 1.5;
}
@@ -797,8 +780,8 @@
.files-page-details {
width: 22rem;
flex-shrink: 0;
border-left: 1px solid var(--border-subtle);
background: var(--bg-toolbar);
border-left: 1px solid var(--c-border-subtle);
background: var(--c-bg-raised);
display: flex;
flex-direction: column;
overflow: hidden;
@@ -817,7 +800,7 @@
font-size: 0.78rem;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--text-muted);
color: var(--c-text-subtle);
}
.files-page-details-header strong {
@@ -846,8 +829,8 @@
flex-direction: column;
gap: 0.4rem;
padding: 0.6rem 1.1rem;
border-top: 1px solid var(--border-subtle);
background: var(--bg-toolbar);
border-top: 1px solid var(--c-border-subtle);
background: var(--c-bg-raised);
}
/* Collapsible "File info" header (size/type/dates). */
@@ -857,11 +840,11 @@
justify-content: space-between;
width: 100%;
padding: 0.4rem 0.6rem;
background: var(--bg-surface);
border: 1px solid var(--border-subtle);
background: var(--c-surface);
border: 1px solid var(--c-border-subtle);
border-radius: 0.5rem;
cursor: pointer;
color: var(--text-secondary, var(--text-primary));
color: var(--c-text-muted, var(--c-text));
font-size: 0.78rem;
text-transform: uppercase;
letter-spacing: 0.04em;
@@ -878,8 +861,8 @@
/* Compact fixed height (was a tall 4:3 box that dominated the panel and
pushed the action buttons off the bottom). */
height: 6.5rem;
background: var(--bg-surface);
border: 1px solid var(--border-subtle);
background: var(--c-surface);
border: 1px solid var(--c-border-subtle);
border-radius: 0.6rem;
display: flex;
align-items: center;
@@ -911,8 +894,8 @@
gap: 0.55rem;
font-size: 0.85rem;
padding: 0.65rem 0.75rem;
background: var(--bg-surface);
border: 1px solid var(--border-subtle);
background: var(--c-surface);
border: 1px solid var(--c-border-subtle);
border-radius: 0.5rem;
}
@@ -923,12 +906,12 @@
}
.files-page-details-field-label {
color: var(--text-muted);
color: var(--c-text-subtle);
font-size: 0.78rem;
}
.files-page-details-field-value {
color: var(--text-primary);
color: var(--c-text);
text-align: right;
word-break: break-word;
font-weight: 500;
@@ -939,8 +922,8 @@
flex-direction: column;
gap: 0.3rem;
padding: 0.5rem 0.6rem;
background: var(--bg-surface);
border: 1px solid var(--border-subtle);
background: var(--c-surface);
border: 1px solid var(--c-border-subtle);
border-radius: 0.5rem;
}
@@ -948,7 +931,7 @@
font-size: 0.72rem;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--text-muted);
color: var(--c-text-subtle);
}
/* Filename chip next to the title - "PDF", "DOCX", etc.
@@ -959,9 +942,9 @@
align-items: center;
padding: 0.12rem 0.5rem;
border-radius: 0.4rem;
background: var(--bg-surface);
border: 1px solid var(--border-subtle);
color: var(--text-secondary, var(--text-primary));
background: var(--c-surface);
border: 1px solid var(--c-border-subtle);
color: var(--c-text-muted, var(--c-text));
font-size: 0.65rem;
font-weight: 700;
letter-spacing: 0.05em;
@@ -980,8 +963,8 @@
flex-direction: column;
gap: 0.4rem;
padding: 0.55rem 0.7rem 0.7rem;
background: var(--bg-surface);
border: 1px solid var(--border-subtle);
background: var(--c-surface);
border: 1px solid var(--c-border-subtle);
border-radius: 0.5rem;
}
.files-page-details-version-timeline-label {
@@ -991,12 +974,12 @@
font-size: 0.72rem;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--text-muted);
color: var(--c-text-subtle);
}
.files-page-details-version-timeline-count {
margin-left: auto;
font-weight: 600;
color: var(--text-secondary, var(--text-muted));
color: var(--c-text-muted, var(--c-text-subtle));
text-transform: none;
letter-spacing: 0;
}
@@ -1026,27 +1009,26 @@
width: 0.55rem;
height: 0.55rem;
border-radius: 50%;
background: var(--bg-toolbar);
border: 2px solid var(--border-strong, var(--border-subtle));
background: var(--c-bg-raised);
border: 2px solid var(--c-border-strong, var(--c-border-subtle));
z-index: 1;
flex-shrink: 0;
}
.files-page-details-version-timeline-rail-dot.is-active {
background: var(--accent-interactive, #6366f1);
border-color: var(--accent-interactive, #6366f1);
box-shadow: 0 0 0 3px
color-mix(in srgb, var(--accent-interactive, #6366f1) 25%, transparent);
background: var(--c-primary);
border-color: var(--c-primary);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--c-primary) 25%, transparent);
}
.files-page-details-version-timeline-rail-dot.is-ellipsis {
width: 0.35rem;
height: 0.35rem;
background: var(--text-muted);
background: var(--c-text-subtle);
border-color: transparent;
}
.files-page-details-version-timeline-rail-line {
width: 2px;
flex: 1;
background: var(--border-subtle);
background: var(--c-border-subtle);
min-height: 0.6rem;
margin-top: 2px;
}
@@ -1062,11 +1044,7 @@
}
.files-page-details-version-timeline-row.is-active
.files-page-details-version-timeline-body {
background: color-mix(
in srgb,
var(--accent-interactive, #6366f1) 10%,
transparent
);
background: color-mix(in srgb, var(--c-primary) 10%, transparent);
}
.files-page-details-version-timeline-summary {
appearance: none;
@@ -1084,11 +1062,11 @@
}
.files-page-details-version-timeline-summary:hover
.files-page-details-version-timeline-chevron {
color: var(--text-primary);
color: var(--c-text);
}
.files-page-details-version-timeline-delta {
font-size: 0.82rem;
color: var(--text-primary);
color: var(--c-text);
font-weight: 500;
white-space: nowrap;
overflow: hidden;
@@ -1099,30 +1077,30 @@
}
.files-page-details-version-timeline-delta.is-origin {
font-weight: 400;
color: var(--text-muted);
color: var(--c-text-subtle);
font-style: italic;
}
.files-page-details-version-timeline-delta-plus {
color: var(--accent-interactive, #6366f1);
color: var(--c-primary);
font-weight: 700;
}
.files-page-details-version-timeline-spacer {
flex: 1;
}
.files-page-details-version-timeline-chevron {
color: var(--text-muted);
color: var(--c-text-subtle);
transition: transform 0.15s ease;
}
.files-page-details-version-timeline-chevron.is-expanded {
transform: rotate(180deg);
color: var(--text-primary);
color: var(--c-text);
}
.files-page-details-version-timeline-meta-line {
display: flex;
align-items: center;
gap: 0.35rem;
font-size: 0.7rem;
color: var(--text-muted);
color: var(--c-text-subtle);
}
.files-page-details-version-timeline-expanded {
display: flex;
@@ -1130,7 +1108,7 @@
gap: 0.4rem;
margin-top: 0.45rem;
padding-top: 0.5rem;
border-top: 1px dashed var(--border-subtle);
border-top: 1px dashed var(--c-border-subtle);
}
.files-page-details-version-timeline-toolchain {
display: flex;
@@ -1139,7 +1117,7 @@
}
.files-page-details-version-timeline-toolchain-label {
font-size: 0.65rem;
color: var(--text-muted);
color: var(--c-text-subtle);
text-transform: uppercase;
letter-spacing: 0.05em;
}
@@ -1147,12 +1125,12 @@
.files-page-details-version-timeline-collapse-btn {
appearance: none;
background: none;
border: 1px dashed var(--border-subtle);
border: 1px dashed var(--c-border-subtle);
border-radius: 0.3rem;
padding: 0.25rem 0.5rem;
margin: 0.1rem 0;
font-size: 0.72rem;
color: var(--text-muted);
color: var(--c-text-subtle);
cursor: pointer;
text-align: left;
transition:
@@ -1161,8 +1139,8 @@
}
.files-page-details-version-timeline-ellipsis-btn:hover,
.files-page-details-version-timeline-collapse-btn:hover {
color: var(--text-primary);
border-color: var(--border-strong, var(--text-muted));
color: var(--c-text);
border-color: var(--c-border-strong, var(--c-text-subtle));
}
.files-page-details-version-timeline-collapse-btn {
align-self: flex-start;
@@ -1172,12 +1150,8 @@
.files-page-drop-overlay {
position: absolute;
inset: 1rem;
background: color-mix(
in srgb,
var(--accent-interactive, #6366f1) 14%,
var(--bg-background)
);
border: 2px dashed var(--accent-interactive, #6366f1);
background: color-mix(in srgb, var(--c-primary) 14%, var(--c-bg));
border: 2px dashed var(--c-primary);
border-radius: 1rem;
display: flex;
flex-direction: column;
@@ -1185,7 +1159,7 @@
justify-content: center;
gap: 1rem;
pointer-events: none;
color: var(--accent-interactive, #6366f1);
color: var(--c-primary);
font-weight: 600;
font-size: 1.1rem;
z-index: 10;
@@ -1200,40 +1174,28 @@
width: 4.5rem;
height: 4.5rem;
border-radius: 50%;
background: color-mix(
in srgb,
var(--accent-interactive, #6366f1) 30%,
transparent
);
background: color-mix(in srgb, var(--c-primary) 30%, transparent);
}
.files-page-drop-overlay-icon svg {
font-size: 2.5rem !important;
color: var(--bg-background);
color: var(--c-bg);
}
.files-page-drop-overlay-sub {
font-size: 0.85rem;
font-weight: 500;
color: color-mix(
in srgb,
var(--accent-interactive, #6366f1) 80%,
var(--text-secondary)
);
color: color-mix(in srgb, var(--c-primary) 80%, var(--c-text-muted));
}
@keyframes files-page-drop-overlay-pulse {
0%,
100% {
border-color: var(--accent-interactive, #6366f1);
border-color: var(--c-primary);
transform: scale(1);
}
50% {
border-color: color-mix(
in srgb,
var(--accent-interactive, #6366f1) 60%,
transparent
);
border-color: color-mix(in srgb, var(--c-primary) 60%, transparent);
transform: scale(1.005);
}
}
@@ -70,7 +70,7 @@ export function FolderAppearancePicker({
borderRadius: "50%",
border:
folder.color === c
? "2px solid var(--text-primary)"
? "2px solid var(--c-text)"
: "2px solid transparent",
background: c,
cursor: disabled ? "not-allowed" : "pointer",
@@ -123,7 +123,7 @@ function Section({
fontSize: "0.7rem",
textTransform: "uppercase",
letterSpacing: "0.05em",
color: "var(--text-muted)",
color: "var(--c-text-subtle)",
fontWeight: 600,
}}
>
@@ -163,13 +163,13 @@ function IconButton({
justifyContent: "center",
fontSize: "1.1rem",
borderRadius: "0.4rem",
background: selected ? "var(--hover-bg)" : "transparent",
background: selected ? "var(--c-hover)" : "transparent",
border: selected
? "1px solid var(--accent-interactive, #6366f1)"
? "1px solid var(--c-primary)"
: "1px solid transparent",
cursor: disabled ? "not-allowed" : "pointer",
padding: 0,
color: "var(--text-primary)",
color: "var(--c-text)",
}}
>
{icon.glyph || "-"}
@@ -30,7 +30,7 @@ export function FolderThumbnail({
size = "thumb",
iconGlyph,
}: FolderThumbnailProps) {
const accent = color ?? "var(--accent-interactive, #6366f1)";
const accent = color ?? "var(--c-primary, #6366f1)";
const px = SIZE_PX[size];
const showBadge = size === "thumb" && (fileCount ?? 0) > 0;
// Per-instance unique ids - `${color}` previously embedded `#` and CSS
@@ -149,7 +149,7 @@ export function FolderThumbnail({
minWidth: "1.4rem",
height: "1.4rem",
borderRadius: "999px",
background: "var(--bg-surface, #fff)",
background: "var(--c-surface, #fff)",
border: `1px solid ${accent}`,
color: accent,
fontSize: "0.7rem",
@@ -15,8 +15,8 @@
.folder-tree-panel {
width: 0;
flex-shrink: 0;
background: var(--bg-toolbar);
border-right: 0 solid var(--border-subtle);
background: var(--c-bg-raised);
border-right: 0 solid var(--c-border-subtle);
overflow: hidden;
height: 100%;
pointer-events: none;
@@ -65,11 +65,7 @@
.folder-tree-panel-resizer:hover,
.folder-tree-panel-resizer:focus-visible {
background: color-mix(
in srgb,
var(--accent-interactive, #6366f1) 35%,
transparent
);
background: color-mix(in srgb, var(--c-primary) 35%, transparent);
outline: none;
}
@@ -87,7 +83,7 @@
font-size: 13px;
font-weight: 600;
letter-spacing: 0.02em;
color: var(--text-muted);
color: var(--c-text-subtle);
text-transform: uppercase;
}
@@ -112,7 +108,7 @@
margin: 0 8px;
cursor: pointer;
user-select: none;
color: var(--text-secondary);
color: var(--c-text-muted);
font-size: 14px;
position: relative;
transition: background-color 0.15s ease;
@@ -120,23 +116,19 @@
}
.files-page-tree-node:hover {
background: var(--hover-bg);
background: var(--c-hover);
}
.files-page-tree-node.is-active {
background: var(--hover-bg);
color: var(--text-primary);
background: var(--c-hover);
color: var(--c-text);
font-weight: 500;
}
.files-page-tree-node.is-drop-target {
background: color-mix(
in srgb,
var(--accent-interactive, #6366f1) 12%,
transparent
);
box-shadow: inset 0 0 0 1px var(--accent-interactive, #6366f1);
color: var(--text-primary);
background: color-mix(in srgb, var(--c-primary) 12%, transparent);
box-shadow: inset 0 0 0 1px var(--c-primary);
color: var(--c-text);
}
.files-page-tree-toggle {
@@ -145,7 +137,7 @@
justify-content: center;
width: 16px;
height: 16px;
color: var(--text-muted);
color: var(--c-text-subtle);
flex-shrink: 0;
}
@@ -165,7 +157,7 @@
align-items: center;
justify-content: center;
flex-shrink: 0;
color: var(--text-muted);
color: var(--c-text-subtle);
margin-left: 8px;
font-size: 18px;
}
@@ -183,7 +175,7 @@
}
.files-page-tree-count {
color: var(--text-muted);
color: var(--c-text-subtle);
font-size: 12px;
flex-shrink: 0;
margin-left: 8px;
@@ -127,7 +127,7 @@ export function MoveToFolderDialog({
</Text>
<div
style={{
border: "1px solid var(--border-subtle)",
border: "1px solid var(--c-border-subtle)",
borderRadius: "0.5rem",
maxHeight: "20rem",
overflowY: "auto",
@@ -343,8 +343,8 @@ function FolderPick({
opacity: disabled ? 0.45 : 1,
gap: "0.5rem",
padding: `0.4rem 0.75rem 0.4rem ${0.75 + depth * 0.85}rem`,
background: isActive ? "var(--hover-bg)" : "transparent",
borderBottom: "1px solid var(--border-subtle)",
background: isActive ? "var(--c-hover)" : "transparent",
borderBottom: "1px solid var(--c-border-subtle)",
boxSizing: "border-box",
fontWeight: isActive ? 600 : 400,
}}
@@ -218,7 +218,7 @@ export default function Workbench() {
<Box
className="flex-1 h-full min-w-0 relative flex flex-col"
data-tour="workbench"
style={{ backgroundColor: "var(--bg-background)", minWidth: 0 }}
style={{ backgroundColor: "var(--c-bg)", minWidth: 0 }}
>
{/* Workbench Bar - animates in/out based on file presence */}
{currentView !== "myFiles" &&
@@ -22,7 +22,7 @@
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 16px 32px rgba(15, 23, 42, 0.18);
box-shadow: 0 16px 32px rgba(0, 0, 0, 0.18);
animation: heroLogoScale 0.25s ease forwards;
}
@@ -49,10 +49,10 @@
.securityCard {
width: 100%;
max-width: 480px;
background: var(--bg-surface, #ffffff);
background: var(--c-surface);
border-radius: 16px;
padding: 20px;
box-shadow: 0 12px 32px rgba(15, 23, 42, 0.12);
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.12);
display: flex;
flex-direction: column;
gap: 16px;
@@ -65,7 +65,7 @@
font-weight: 500;
font-size: 15px;
line-height: 1.5;
color: var(--onboarding-body, #1f2937);
color: var(--c-text-muted);
}
.mfaSlideContent {
@@ -77,10 +77,10 @@
.mfaCard {
width: 100%;
max-width: 560px;
background: var(--bg-surface, #ffffff);
background: var(--c-surface);
border-radius: 16px;
padding: 20px;
box-shadow: 0 12px 32px rgba(15, 23, 42, 0.12);
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.12);
}
.mfaSetupGrid {
@@ -94,7 +94,7 @@
padding: 12px;
background: #ffffff;
border-radius: 12px;
box-shadow: 0 8px 16px rgba(15, 23, 42, 0.12);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.12);
display: flex;
justify-content: center;
}
@@ -102,7 +102,7 @@
.mfaSteps {
margin: 0;
padding-left: 18px;
color: var(--onboarding-body, #1f2937);
color: var(--c-text-muted);
font-size: 14px;
line-height: 1.5;
}
@@ -149,11 +149,7 @@
padding: 4px;
outline: none;
}
.iconButton:focus {
outline: none;
}
.iconButton:focus,
.iconButton:focus-visible {
outline: none;
}
@@ -194,14 +190,7 @@
text-align: center;
animation: heroLogoEnter 0.25s ease forwards;
}
.title {
text-align: center;
opacity: 0;
transform: translateX(24px);
animation: bodySlideIn 0.25s ease forwards;
}
.title,
.bodyCopy {
text-align: center;
opacity: 0;
@@ -273,13 +262,13 @@
opacity: 1;
border: 1px solid rgba(255, 255, 255, 0.9);
background: rgba(255, 255, 255, 0.9);
color: #1f2933;
color: var(--c-text);
box-shadow: 0 0 8px rgba(255, 255, 255, 0.7);
}
/* Modal content styles */
.modalContent {
background: var(--bg-surface);
background: var(--c-surface);
position: relative;
}
@@ -299,7 +288,7 @@
sans-serif;
font-weight: 600;
font-size: 22px;
color: var(--onboarding-title);
color: var(--c-text);
}
/* Body text styles */
@@ -313,7 +302,7 @@
Arial,
sans-serif;
font-size: 16px;
color: var(--onboarding-body);
color: var(--c-text-muted);
line-height: 1.5;
}
@@ -335,8 +324,8 @@
}
.v2Badge {
background: #dbefff;
color: #2a4bff;
background: var(--c-primary-tint);
color: var(--c-accent-fg);
padding: 3px 9px;
border-radius: 6px;
font-size: 12px;
@@ -345,7 +334,7 @@
/* Icon styles */
.heroIcon {
color: var(--onboarding-title, #0f172a);
color: var(--c-text);
}
/* ===================================================================== */
@@ -353,7 +342,7 @@
/* ===================================================================== */
.card {
background: var(--bg-surface);
background: var(--c-surface);
display: flex;
flex-direction: column;
}
@@ -383,7 +372,7 @@
font-size: 17px;
font-weight: 700;
letter-spacing: -0.01em;
color: var(--onboarding-title, #0f172a);
color: var(--c-text);
}
.headerRight {
@@ -395,8 +384,8 @@
.stepPill {
font-size: 12px;
font-weight: 600;
color: var(--onboarding-body, #64748b);
background: var(--bg-muted, #f1f5f9);
color: var(--c-text-muted);
background: var(--c-surface-sunken);
padding: 5px 11px;
border-radius: 9999px;
white-space: nowrap;
@@ -413,17 +402,17 @@
flex: 1;
height: 5px;
border-radius: 9999px;
background: var(--onboarding-step-inactive, #e2e8f0);
background: var(--onboarding-step-inactive, var(--c-border));
transition: background 0.2s ease;
}
.progressSegDone {
background: var(--color-blue, #3b82f6);
background: var(--c-primary);
}
.divider {
height: 1px;
background: var(--border-subtle, rgba(15, 23, 42, 0.08));
background: var(--c-border-subtle);
}
/* ---- content ---- */
@@ -445,7 +434,7 @@
min-height: 128px;
border-radius: 14px;
overflow: hidden;
background: linear-gradient(155deg, #eef1fb 0%, #f6f4fc 55%, #fbf4f7 100%);
background: var(--c-onboarding-hero);
}
.heroArt {
@@ -459,18 +448,18 @@
height: 62px;
border-radius: 15px;
object-fit: contain;
filter: drop-shadow(0 10px 20px rgba(15, 23, 42, 0.16));
filter: drop-shadow(0 10px 20px rgba(0, 0, 0, 0.16));
}
.heroTile {
width: 62px;
height: 62px;
border-radius: 15px;
background: #ffffff;
background: var(--c-onboarding-hero-tile);
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 10px 22px rgba(15, 23, 42, 0.14);
box-shadow: 0 10px 22px rgba(0, 0, 0, 0.14);
}
/* ---- title + body (left-aligned, clean) ---- */
@@ -479,14 +468,14 @@
font-weight: 600;
letter-spacing: -0.01em;
line-height: 1.3;
color: var(--onboarding-title, #0f172a);
color: var(--c-text);
text-align: left;
}
.bodyNew {
font-size: 14px;
line-height: 1.5;
color: var(--onboarding-body, #475569);
color: var(--c-text-muted);
text-align: left;
}
@@ -513,16 +502,9 @@
gap: 12px;
}
/* ---- dark theme surface tuning ---- */
:global([data-mantine-color-scheme="dark"]) .heroPanel {
background: linear-gradient(155deg, #1a2236 0%, #171e30 55%, #201a28 100%);
}
/* ---- dark theme surface tuning ----
Hero panel + tile colours flip via the themed --c-onboarding-hero* tokens;
only the tile's heavier dark shadow needs a per-scheme override. */
:global([data-mantine-color-scheme="dark"]) .heroTile {
background: #0f1626;
box-shadow: 0 12px 28px rgba(0, 0, 0, 0.4);
}
:global([data-mantine-color-scheme="dark"]) .divider {
background: rgba(255, 255, 255, 0.08);
}
@@ -116,7 +116,7 @@ export default function OnboardingSlideShell({
content: {
overflow: "hidden",
border: "none",
background: "var(--bg-surface)",
background: "var(--c-surface)",
maxHeight: "90vh",
},
}}
@@ -193,7 +193,7 @@ export default function OnboardingSlideShell({
<div key={`body-${slideKey}`} className={styles.bodyNew}>
{body}
<style>{`.${styles.bodyNew} strong{color: var(--onboarding-title); font-weight: 600;}`}</style>
<style>{`.${styles.bodyNew} strong{color: var(--c-text); font-weight: 600;}`}</style>
</div>
<div className={styles.footer}>
@@ -13,7 +13,7 @@
box-shadow:
0 0 0 2px var(--mantine-primary-color-filled),
0 0 15px var(--mantine-primary-color-filled),
inset 0 0 15px rgba(59, 130, 246, 0.1);
inset 0 0 15px color-mix(in srgb, var(--c-primary) 10%, transparent);
border-radius: 8px;
}
@@ -23,13 +23,13 @@
box-shadow:
0 0 0 3px var(--mantine-primary-color-filled),
0 0 20px var(--mantine-primary-color-filled),
inset 0 0 20px rgba(59, 130, 246, 0.1);
inset 0 0 20px color-mix(in srgb, var(--c-primary) 10%, transparent);
}
50% {
box-shadow:
0 0 0 3px var(--mantine-primary-color-filled),
0 0 30px var(--mantine-primary-color-filled),
inset 0 0 30px rgba(59, 130, 246, 0.2);
inset 0 0 30px color-mix(in srgb, var(--c-primary) 20%, transparent);
}
}
@@ -40,7 +40,7 @@
position: absolute;
border-radius: 50%;
pointer-events: none;
box-shadow: 0 18px 36px rgba(15, 23, 42, 0.12);
box-shadow: 0 18px 36px rgba(0, 0, 0, 0.12);
animation-name: circleSway;
animation-timing-function: ease-in-out;
animation-iteration-count: infinite;
@@ -129,9 +129,9 @@ export const DesktopInstallTitle: React.FC<DesktopInstallTitleProps> = ({
leftSection={iconKey ? <OsIcon os={iconKey} /> : undefined}
style={{
backgroundColor: isSelected
? "var(--bg-muted, #f1f5f9)"
? "var(--c-surface-sunken, #f1f5f9)"
: "transparent",
color: "var(--onboarding-title, #0f172a)",
color: "var(--c-text, #0f172a)",
fontWeight: isSelected ? 600 : 500,
}}
>
@@ -125,7 +125,7 @@ function FirstLoginForm({
icon="info-rounded"
width={20}
height={20}
style={{ color: "#3B82F6", flexShrink: 0 }}
style={{ color: "var(--c-primary)", flexShrink: 0 }}
/>
<span>
{t(
@@ -26,7 +26,7 @@ export default function SecurityCheckSlide({
icon="error"
width={20}
height={20}
style={{ color: "#F04438", flexShrink: 0 }}
style={{ color: "var(--c-danger)", flexShrink: 0 }}
/>
<span>
{i18n.t(
@@ -28,15 +28,15 @@
.selectionBox {
position: absolute;
border: 2px dashed #3b82f6;
background-color: rgba(59, 130, 246, 0.1);
border: 2px dashed var(--c-primary);
background-color: color-mix(in srgb, var(--c-primary) 10%, transparent);
pointer-events: none;
}
.dropIndicator {
position: absolute;
width: 4px;
background-color: rgba(96, 165, 250, 0.8);
background-color: color-mix(in srgb, var(--c-primary) 80%, transparent);
border-radius: 2px;
pointer-events: none;
}
@@ -50,8 +50,8 @@
position: absolute;
top: -8px;
right: -8px;
background-color: #3b82f6;
color: #ffffff;
background-color: var(--c-primary);
color: var(--c-text-on-primary);
border-radius: 50%;
width: 32px;
height: 32px;
@@ -241,7 +241,7 @@ const FileThumbnail = ({
<CheckboxIndicator
checked={isSelected}
onChange={() => onToggleFile(file.id)}
color="var(--checkbox-checked-bg)"
color="var(--c-primary)"
/>
) : (
<div className={styles.unsupportedPill}>
@@ -372,7 +372,7 @@ const FileThumbnail = ({
objectFit: "contain",
borderRadius: 0,
background: "#ffffff",
border: "1px solid var(--border-default)",
border: "1px solid var(--c-border)",
display: "block",
marginLeft: "auto",
marginRight: "auto",
@@ -21,13 +21,13 @@
@keyframes pageMovedHighlight {
0% {
background-color: rgba(59, 130, 246, 0.32);
background-color: color-mix(in srgb, var(--c-primary) 32%, transparent);
}
60% {
background-color: rgba(59, 130, 246, 0.12);
background-color: color-mix(in srgb, var(--c-primary) 12%, transparent);
}
100% {
background-color: rgba(59, 130, 246, 0);
background-color: color-mix(in srgb, var(--c-primary) 0%, transparent);
}
}
@@ -63,7 +63,7 @@
/* Action styles */
.actionRow:hover {
background: var(--hover-bg);
background: var(--c-hover);
}
.actionDanger {
@@ -72,7 +72,7 @@
.actionsDivider {
height: 1px;
background: var(--border-default);
background: var(--c-border);
margin: 4px 0;
}
@@ -86,7 +86,7 @@
.unsupportedPill {
margin-left: 1.75rem;
background: #6b7280;
background: var(--c-text-subtle);
color: white;
padding: 4px 8px;
border-radius: 12px;
@@ -121,8 +121,8 @@ const PageEditorControls = ({
borderBottomLeftRadius: 0,
borderBottomRightRadius: 0,
boxShadow: "0 -2px 8px rgba(0,0,0,0.04)",
backgroundColor: "var(--bg-toolbar)",
border: "1px solid var(--border-default)",
backgroundColor: "var(--c-bg-raised)",
border: "1px solid var(--c-border)",
borderRadius: "16px 16px 0 0",
pointerEvents: "auto",
minWidth: 360,
@@ -31,7 +31,7 @@
.rightCol {
width: 8rem;
border-left: 0.0625rem solid var(--border-default);
border-left: 0.0625rem solid var(--c-border);
padding-left: 0.75rem;
display: flex;
flex-direction: column;
@@ -47,15 +47,15 @@
width: 100%;
border-radius: 1.25rem;
border: 0.0625rem solid var(--bulk-card-border);
background-color: var(--bulk-card-bg);
color: var(--text-primary);
background-color: var(--c-surface);
color: var(--c-text);
transition: all 0.2s ease;
min-height: 2rem;
}
.operatorChip:hover:not(:disabled) {
border-color: var(--bulk-card-hover-border);
background-color: var(--hover-bg);
background-color: var(--c-hover);
transform: translateY(-0.0625rem);
box-shadow: var(--shadow-sm);
}
@@ -70,24 +70,12 @@
cursor: not-allowed;
}
:global([data-mantine-color-scheme="dark"]) .operatorChip {
background-color: var(--bulk-card-bg);
border-color: var(--bulk-card-border);
color: var(--text-primary);
}
:global([data-mantine-color-scheme="dark"]) .operatorChip:hover:not(:disabled) {
background-color: var(--hover-bg);
border-color: var(--bulk-card-hover-border);
color: var(--text-primary);
}
.dropdownHeader {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.75rem;
border-bottom: 0.0625rem solid var(--border-default);
border-bottom: 0.0625rem solid var(--c-border);
margin-bottom: 0.5rem;
}
@@ -111,7 +99,7 @@
}
.chevron {
color: var(--text-muted);
color: var(--c-text-subtle);
}
/* Icon-based chevrons */
@@ -151,13 +139,13 @@
.selectedList {
max-height: 8rem;
overflow: auto;
background-color: var(--bg-raised);
border: 0.0625rem solid var(--border-default);
background-color: var(--c-surface-raised);
border: 0.0625rem solid var(--c-border);
border-radius: 0.75rem;
padding: 0.5rem 0.75rem;
margin-top: 0.5rem;
min-width: 24rem;
color: var(--text-primary);
color: var(--c-text);
}
.selectedText {
@@ -175,7 +163,7 @@
justify-content: space-between;
align-items: center;
padding: 0.75rem;
border-bottom: 0.0625rem solid var(--border-default);
border-bottom: 0.0625rem solid var(--c-border);
margin-bottom: 0.5rem;
}
@@ -193,26 +181,18 @@
}
.advancedItem:hover {
background-color: var(--hover-bg);
}
:global([data-mantine-color-scheme="dark"]) .advancedItem:hover {
background-color: var(--hover-bg);
background-color: var(--c-hover);
}
.advancedCard {
background-color: var(--bulk-card-bg);
background-color: var(--c-surface);
border: none;
border-radius: 0.75rem;
padding: 0.25rem;
margin-bottom: 0.5rem;
width: 100%;
box-sizing: border-box;
color: var(--text-primary);
}
:global([data-mantine-color-scheme="dark"]) .advancedCard {
background-color: var(--bulk-card-bg);
color: var(--c-text);
}
.inputGroup {
@@ -226,24 +206,17 @@
.applyButton {
min-width: 4rem;
flex-shrink: 0;
}
/* Style inputs and buttons within advanced cards to match bg-raised */
.advancedCard :global(.mantine-NumberInput-input) {
background-color: var(--bg-raised) !important;
border-color: var(--border-default) !important;
color: var(--text-primary) !important;
}
} /* Style inputs and buttons within advanced cards to match bg-raised */
.advancedCard :global(.mantine-NumberInput-input),
.advancedCard :global(.mantine-Button-root) {
background-color: var(--bg-raised) !important;
border-color: var(--border-default) !important;
color: var(--text-primary) !important;
background-color: var(--c-surface-raised) !important;
border-color: var(--c-border) !important;
color: var(--c-text) !important;
}
.advancedCard :global(.mantine-Button-root:hover) {
background-color: var(--hover-bg) !important;
border-color: var(--border-strong) !important;
background-color: var(--c-hover) !important;
border-color: var(--c-border-strong) !important;
}
/* Error helper text above the input */
@@ -254,8 +227,8 @@
/* Compact error container for inline tool settings */
.errorCompact {
background-color: var(--bg-raised);
border: 0.0625rem solid var(--border-default);
background-color: var(--c-surface-raised);
border: 0.0625rem solid var(--c-border);
border-radius: 0.75rem;
padding: 0.5rem 0.75rem;
margin-top: 0.5rem;
@@ -271,11 +244,6 @@
overflow: hidden;
}
/* Dark-mode adjustments */
:global([data-mantine-color-scheme="dark"]) .selectedList {
background-color: var(--bg-raised);
}
/* Small screens: allow the section to shrink instead of enforcing a large min width */
@media (max-width: 480px) {
.panelGroup,
@@ -290,16 +258,16 @@
.panelContainer {
max-height: 95vh;
overflow: auto;
background-color: var(--bulk-panel-bg);
color: var(--text-primary);
background-color: var(--c-surface);
color: var(--c-text);
border-radius: 0.5rem;
}
/* Override Mantine Popover dropdown background */
:global(.mantine-Popover-dropdown) {
background-color: var(--bulk-panel-bg) !important;
background-color: var(--c-surface) !important;
border-color: var(--bulk-card-border) !important;
color: var(--text-primary) !important;
color: var(--c-text) !important;
}
/* Override Mantine Switch outline */
@@ -17,7 +17,7 @@ const OperatorsSection = ({
return (
<div>
<Text size="xs" c="var(--text-muted)" fw={500} mb="xs">
<Text size="xs" c="var(--c-text-subtle)" fw={500} mb="xs">
{t("bulkSelection.keywords.title", "Keywords")}:
</Text>
<Group gap="sm" wrap="nowrap">
@@ -44,7 +44,7 @@ const PageSelectionInput = ({
icon="gpp-maybe-outline-rounded"
width="1rem"
height="1rem"
style={{ color: "var(--text-instruction)" }}
style={{ color: "var(--c-accent-fg)" }}
/>
<Text>
{t("bulkSelection.pageSelection.title", "Page Selection")}
@@ -53,7 +53,7 @@ const PageSelectionInput = ({
</Tooltip>
{typeof advancedOpened === "boolean" && (
<Flex align="center" gap="xs">
<Text size="sm" c="var(--text-secondary)">
<Text size="sm" c="var(--c-text-muted)">
{t("bulkSelection.advanced.title", "Advanced")}
</Text>
<Switch
@@ -50,7 +50,7 @@ const SelectPages = ({
return (
<div className={classes.advancedCard}>
<Text size="sm" fw={600} c="var(--text-secondary)" mb="xs">
<Text size="sm" fw={600} c="var(--c-text-muted)" mb="xs">
{title}
</Text>
{error && (
@@ -63,7 +63,8 @@
}
.modal-nav-scroll:hover {
scrollbar-color: rgba(128, 128, 128, 0.5) transparent;
scrollbar-color: color-mix(in srgb, var(--c-border-strong) 50%, transparent)
transparent;
}
.modal-nav-scroll::-webkit-scrollbar {
@@ -81,11 +82,11 @@
}
.modal-nav-scroll:hover::-webkit-scrollbar-thumb {
background-color: rgba(128, 128, 128, 0.5);
background-color: color-mix(in srgb, var(--c-border-strong) 50%, transparent);
}
.modal-nav-scroll::-webkit-scrollbar-thumb:hover {
background-color: rgba(128, 128, 128, 0.7);
background-color: color-mix(in srgb, var(--c-border-strong) 70%, transparent);
}
.modal-nav-section {
@@ -131,7 +132,8 @@
}
.modal-content-scroll:hover {
scrollbar-color: rgba(128, 128, 128, 0.5) transparent;
scrollbar-color: color-mix(in srgb, var(--c-border-strong) 50%, transparent)
transparent;
}
.modal-content-scroll::-webkit-scrollbar {
@@ -149,11 +151,11 @@
}
.modal-content-scroll:hover::-webkit-scrollbar-thumb {
background-color: rgba(128, 128, 128, 0.5);
background-color: color-mix(in srgb, var(--c-border-strong) 50%, transparent);
}
.modal-content-scroll::-webkit-scrollbar-thumb:hover {
background-color: rgba(128, 128, 128, 0.7);
background-color: color-mix(in srgb, var(--c-border-strong) 70%, transparent);
}
.modal-header {
@@ -212,8 +214,8 @@
bottom: 0;
left: 0;
right: 0;
background: var(--modal-content-bg);
border-top: 1px solid var(--modal-header-border);
background: var(--c-surface);
border-top: 1px solid var(--c-border-subtle);
padding: 1rem 2rem;
margin: 0 -2rem;
margin-bottom: -1rem;
@@ -165,13 +165,13 @@ const AppConfigModalInner: React.FC<AppConfigModalProps> = ({
const colors = useMemo(
() => ({
navBg: "var(--modal-nav-bg)",
sectionTitle: "var(--modal-nav-section-title)",
navBg: "var(--c-bg-raised)",
sectionTitle: "var(--c-text-subtle)",
navItem: "var(--modal-nav-item)",
navItemActive: "var(--modal-nav-item-active)",
navItemActiveBg: "var(--modal-nav-item-active-bg)",
contentBg: "var(--modal-content-bg)",
headerBorder: "var(--modal-header-border)",
navItemActive: "var(--c-accent-fg)",
navItemActiveBg: "var(--c-primary-subtle)",
contentBg: "var(--c-surface)",
headerBorder: "var(--c-border-subtle)",
}),
[],
);
@@ -9,15 +9,15 @@
background: none;
cursor: pointer;
border-radius: var(--radius-sm);
color: var(--color-text-4);
color: var(--c-text-subtle);
transition:
background var(--motion-fast),
color var(--motion-fast);
}
.app-switch-btn:hover {
background: var(--color-bg-hover);
color: var(--color-text-2);
background: var(--c-hover);
color: var(--c-text-muted);
}
.app-switch-icon {
@@ -79,8 +79,8 @@ const Badge: React.FC<BadgeProps> = ({
// Default styling
return {
background: "var(--tool-header-badge-bg)",
color: "var(--tool-header-badge-text)",
background: "var(--c-surface-raised)",
color: "var(--c-accent-fg)",
};
};
@@ -78,7 +78,11 @@ export default function EditableSecretField({
)}
{description && (
<p
style={{ margin: "4px 0 12px 0", fontSize: "0.75rem", color: "#666" }}
style={{
margin: "4px 0 12px 0",
fontSize: "0.75rem",
color: "var(--c-text-muted)",
}}
>
{description}
</p>
@@ -117,7 +117,7 @@ export default class ErrorBoundary extends React.Component<
style={{
fontSize: "0.75rem",
overflow: "auto",
backgroundColor: "#f5f5f5",
backgroundColor: "var(--c-surface-sunken)",
padding: "1rem",
borderRadius: "4px",
maxHeight: "300px",
@@ -74,7 +74,7 @@ const FileCard = ({
<Stack gap={6} align="center">
<Box
style={{
border: "2px solid #e0e0e0",
border: "2px solid var(--c-border)",
borderRadius: 8,
width: 90,
height: 120,
@@ -82,7 +82,7 @@ const FileCard = ({
alignItems: "center",
justifyContent: "center",
margin: "0 auto",
background: "#fafbfc",
background: "var(--c-surface)",
position: "relative",
}}
>
@@ -54,8 +54,8 @@ export const FileDropdownMenu: React.FC<FileDropdownMenuProps> = ({
</Menu.Target>
<Menu.Dropdown
style={{
backgroundColor: "var(--bg-file-manager)",
border: "1px solid var(--border-subtle)",
backgroundColor: "var(--c-bg)",
border: "1px solid var(--c-border-subtle)",
borderRadius: "8px",
boxShadow: "0 2px 8px rgba(0, 0, 0, 0.15)",
maxHeight: "50vh",
@@ -1,9 +1,9 @@
/* ── Trigger ─────────────────────────────────────── */
.trigger {
border: 1px solid var(--border-default);
border: 1px solid var(--c-border);
border-radius: var(--radius-md);
padding: 0.5rem 0.75rem;
background: var(--bg-surface);
background: var(--c-surface);
cursor: pointer;
flex: 1;
min-width: 0;
@@ -47,8 +47,8 @@
justify-content: space-between;
padding: 0.3rem 0.45rem;
gap: 0.4rem;
border-bottom: 1px solid var(--border-default);
background: var(--bg-surface);
border-bottom: 1px solid var(--c-border);
background: var(--c-surface);
}
.tabGroup {
@@ -66,7 +66,7 @@
min-width: 0;
min-height: 24px;
max-height: 26px;
border: 1px solid var(--border-default);
border: 1px solid var(--c-border);
border-radius: var(--radius-sm);
background: color-mix(
in srgb,
@@ -185,8 +185,8 @@
/* ── Search / filter row ─────────────────────────── */
.searchRow {
padding: 0.3rem 0.45rem;
border-bottom: 1px solid var(--border-default);
background: var(--bg-surface);
border-bottom: 1px solid var(--c-border);
background: var(--c-surface);
}
.searchInput {
@@ -195,7 +195,7 @@
height: 1.625rem;
padding: 0 0.5rem;
font-size: 0.7rem;
border: 1px solid var(--border-subtle, var(--border-default));
border: 1px solid var(--c-border-subtle, var(--c-border));
border-radius: var(--radius-sm);
background: var(--mantine-color-default-hover);
color: var(--mantine-color-text);
@@ -233,7 +233,7 @@
cursor: pointer;
background: transparent;
border: none;
border-top: 1px solid var(--border-default);
border-top: 1px solid var(--c-border);
text-align: left;
transition: background 0.08s ease;
}
@@ -1,8 +1,8 @@
/* ========== FILE SIDEBAR ========== */
.file-sidebar {
background-color: var(--bg-toolbar);
border-right: 1px solid var(--border-subtle);
background-color: var(--c-bg-raised);
border-right: 1px solid var(--c-border-subtle);
display: flex;
flex-direction: column;
height: 100%;
@@ -24,7 +24,7 @@
/* ---- Native file drag-and-drop ---- */
.file-sidebar[data-file-drag-over] {
outline: 2px dashed var(--mantine-color-blue-6, #3b82f6);
outline: 2px dashed var(--mantine-color-blue-6, var(--c-primary));
outline-offset: -4px;
}
@@ -42,20 +42,20 @@
pointer-events: none;
background-color: color-mix(
in srgb,
var(--mantine-color-blue-6, #3b82f6) 12%,
var(--bg-toolbar)
var(--mantine-color-blue-6, var(--c-primary)) 12%,
var(--c-bg-raised)
);
}
.file-sidebar-drop-overlay-icon {
color: var(--mantine-color-blue-6, #3b82f6) !important;
color: var(--mantine-color-blue-6, var(--c-primary)) !important;
font-size: 28px !important;
}
.file-sidebar-drop-overlay-text {
font-size: 13px;
font-weight: 600;
color: var(--mantine-color-blue-6, #3b82f6);
color: var(--mantine-color-blue-6, var(--c-primary));
}
/* ---- Header ---- */
@@ -75,11 +75,11 @@
/* Icons stay left-aligned during animation; overflow:hidden on inner clips text naturally */
.file-sidebar-header:hover {
background-color: var(--hover-bg);
background-color: var(--c-hover);
}
.file-sidebar-menu-icon {
color: var(--text-muted) !important;
color: var(--c-text-subtle) !important;
font-size: 18px !important;
flex-shrink: 0;
}
@@ -129,11 +129,11 @@
}
.file-sidebar-search-row:not(.active):hover {
background-color: var(--hover-bg);
background-color: var(--c-hover);
}
.file-sidebar-search-icon {
color: var(--text-muted) !important;
color: var(--c-text-subtle) !important;
font-size: 18px !important;
flex-shrink: 0;
}
@@ -148,19 +148,19 @@
border: none;
outline: none;
font-size: 14px;
color: var(--text-primary);
color: var(--c-text);
margin-left: 12px;
min-width: 0;
}
.file-sidebar-search-input::placeholder {
color: var(--text-muted);
color: var(--c-text-subtle);
}
.file-sidebar-search-label {
margin-left: 12px;
font-size: 14px;
color: var(--text-secondary);
color: var(--c-text-muted);
}
/* ---- Scrollable content ---- */
@@ -199,7 +199,7 @@
}
.file-sidebar-action-row:hover {
background-color: var(--hover-bg);
background-color: var(--c-hover);
}
.file-sidebar-action-row.disabled {
@@ -212,7 +212,7 @@
}
.file-sidebar-action-icon {
color: var(--text-muted) !important;
color: var(--c-text-subtle) !important;
font-size: 18px !important;
flex-shrink: 0;
display: inline-flex;
@@ -234,7 +234,7 @@
.file-sidebar-action-label {
margin-left: 12px;
font-size: 14px;
color: var(--text-secondary);
color: var(--c-text-muted);
white-space: nowrap;
}
@@ -253,7 +253,7 @@
}
.file-sidebar-cloud-row:not(.disabled):hover {
background-color: var(--hover-bg);
background-color: var(--c-hover);
}
.file-sidebar-cloud-row.disabled {
@@ -320,19 +320,19 @@
border: none;
background: transparent;
cursor: pointer;
color: var(--text-default, var(--color-text-1));
color: var(--text-default, var(--c-text));
border-radius: 0;
font-size: 13px;
font-weight: 600;
text-align: left;
}
.file-sidebar-group-header:hover {
background: var(--hover-bg, var(--color-bg-hover));
background: var(--c-hover, var(--c-hover));
}
.file-sidebar-group-icon {
flex: none;
color: var(--text-muted, var(--color-text-3));
color: var(--c-text-subtle, var(--c-text-subtle));
}
.file-sidebar-group-label {
@@ -347,7 +347,7 @@
flex: none;
font-size: 11px;
font-weight: 500;
color: var(--text-muted, var(--color-text-3));
color: var(--c-text-subtle, var(--c-text-subtle));
}
/* Full-width, flat rows: no inset, no radius, so the hover/selected highlight
@@ -363,7 +363,7 @@
}
/* Hover reads neutral (not the default blue tint); selected stays blue. */
.file-sidebar-group-items .file-sidebar-file-item:hover:not(.selected) {
background-color: var(--color-bg-hover, rgba(120, 120, 120, 0.1));
background-color: var(--c-hover);
}
/* A plain circle where the selected check sits — on hover, and blue when selected. */
.file-sidebar-group-items .file-sidebar-file-checkbox-hover,
@@ -413,7 +413,7 @@
border: none;
background: transparent;
cursor: pointer;
color: var(--color-blue, #2563eb);
color: var(--c-accent-fg, var(--c-primary));
font-size: 12px;
font-weight: 500;
border-radius: 6px;
@@ -424,7 +424,7 @@
display: none;
}
.file-sidebar-view-all:hover {
background: var(--color-bg-hover, rgba(120, 120, 120, 0.1));
background: var(--c-hover);
}
.file-sidebar-file-list::-webkit-scrollbar {
@@ -438,11 +438,11 @@
border-radius: 2px;
}
.file-sidebar-file-list::-webkit-scrollbar-thumb:hover {
background: var(--text-muted);
background: var(--c-text-subtle);
}
.file-sidebar-section-divider {
border-top: 1px solid var(--border-subtle);
border-top: 1px solid var(--c-border-subtle);
margin: 10px 14px 6px 14px;
}
@@ -457,7 +457,7 @@
.file-sidebar-section-header {
position: relative;
border-top: 1px solid var(--border-subtle);
border-top: 1px solid var(--c-border-subtle);
padding-top: 10px;
margin-top: 4px;
flex-shrink: 0;
@@ -467,7 +467,7 @@
font-size: 13px;
font-weight: 600;
letter-spacing: 0.02em;
color: var(--text-muted);
color: var(--c-text-subtle);
text-transform: uppercase;
}
@@ -481,7 +481,7 @@
align-items: center;
justify-content: space-between;
font-size: 11px;
color: var(--text-muted);
color: var(--c-text-subtle);
margin-bottom: 4px;
}
.file-sidebar-bulk-add-count {
@@ -490,19 +490,19 @@
.file-sidebar-bulk-add-track {
height: 3px;
border-radius: 999px;
background: color-mix(in srgb, var(--color-blue, #3b82f6) 15%, transparent);
background: color-mix(in srgb, var(--c-primary) 15%, transparent);
overflow: hidden;
}
.file-sidebar-bulk-add-bar {
height: 100%;
border-radius: 999px;
background: var(--color-blue, #3b82f6);
background: var(--c-primary);
transition: width 0.15s ease-out;
}
.file-sidebar-section-count {
font-size: 11px;
color: var(--text-muted);
color: var(--c-text-subtle);
}
.file-sidebar-section-btn {
@@ -515,7 +515,7 @@
background: transparent;
border-radius: 4px;
cursor: pointer;
color: var(--text-muted);
color: var(--c-text-subtle);
padding: 0;
outline: none;
transition:
@@ -525,13 +525,13 @@
}
.file-sidebar-section-btn:focus-visible {
outline: 2px solid #3b82f6;
outline: 2px solid var(--c-primary);
outline-offset: 1px;
}
.file-sidebar-section-btn:hover {
background-color: var(--hover-bg);
color: var(--text-primary);
background-color: var(--c-hover);
color: var(--c-text);
}
.file-sidebar-section-btn-external {
@@ -576,7 +576,7 @@
align-items: center;
gap: 8px;
padding: 8px 10px;
border-top: 1px solid var(--border-subtle);
border-top: 1px solid var(--c-border-subtle);
flex-shrink: 0;
min-height: 48px;
}
@@ -587,8 +587,8 @@
width: 28px;
height: 28px;
border-radius: 50%;
background-color: var(--mantine-color-blue-6, #3b82f6);
color: #fff;
background-color: var(--mantine-color-blue-6, var(--c-primary));
color: var(--c-text-on-primary);
font-size: 12px;
font-weight: 600;
display: flex;
@@ -615,7 +615,7 @@
flex: 1;
font-size: 13px;
font-weight: 500;
color: var(--text-primary);
color: var(--c-text);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@@ -623,11 +623,11 @@
}
.file-sidebar-bottom-bar[role="button"]:hover {
background-color: var(--hover-bg);
background-color: var(--c-hover);
}
.file-sidebar-bottom-bar[role="button"]:focus-visible {
outline: 2px solid #3b82f6;
outline: 2px solid var(--c-primary);
outline-offset: -2px;
}
@@ -638,7 +638,7 @@
width: 28px;
height: 28px;
border-radius: 6px;
color: var(--text-muted);
color: var(--c-text-subtle);
padding: 0;
flex-shrink: 0;
margin-left: auto;
@@ -1114,7 +1114,7 @@ const FileSidebar = forwardRef<HTMLDivElement, FileSidebarProps>(
<div className="file-sidebar-cloud-icon-wrapper">
<GoogleDriveIcon
className="file-sidebar-cloud-icon-gray"
style={{ color: "var(--text-secondary)" }}
style={{ color: "var(--c-text-muted)" }}
/>
{isGoogleDriveEnabled && (
<GoogleDriveIcon
@@ -1145,7 +1145,7 @@ const FileSidebar = forwardRef<HTMLDivElement, FileSidebarProps>(
aria-label={t("watchedFolders.sidebarTitle", "Watched Folders")}
style={
isWatchedFoldersActive
? { backgroundColor: "var(--active-bg)" }
? { backgroundColor: "var(--c-active)" }
: undefined
}
>
@@ -1197,7 +1197,7 @@ const FileSidebar = forwardRef<HTMLDivElement, FileSidebarProps>(
{!stubsLoaded ? (
<div className="file-sidebar-loading">
<Loader size="sm" color="var(--text-muted)" />
<Loader size="sm" color="var(--c-text-subtle)" />
</div>
) : filteredFileStubs.length > 0 ? (
<div className="file-sidebar-file-list">
@@ -17,7 +17,7 @@
background: transparent;
}
.file-sidebar-file-list::-webkit-scrollbar-thumb {
background: rgb(var(--border));
background: var(--c-border);
border-radius: 2px;
}
@@ -63,15 +63,15 @@
}
.file-sidebar-file-item:hover:not(.selected) {
background-color: rgba(59, 130, 246, 0.06);
background-color: color-mix(in srgb, var(--c-primary) 6%, transparent);
}
.file-sidebar-file-item.selected {
background-color: rgba(59, 130, 246, 0.12);
background-color: color-mix(in srgb, var(--c-primary) 12%, transparent);
}
.file-sidebar-file-item.active:not(.selected) {
background-color: rgba(59, 130, 246, 0.06);
background-color: color-mix(in srgb, var(--c-primary) 6%, transparent);
}
/* Icon wrapper */
@@ -95,7 +95,7 @@
width: 20px;
height: 20px;
border-radius: 5px;
border: 1.5px solid var(--border-hover, var(--border-strong));
border: 1.5px solid var(--c-border-strong);
}
.file-sidebar-file-item:hover .file-sidebar-file-checkbox-hover {
@@ -116,7 +116,7 @@
width: 20px;
height: 20px;
border-radius: 5px;
background-color: #3b82f6;
background-color: var(--c-primary);
display: flex;
align-items: center;
justify-content: center;
@@ -124,7 +124,7 @@
}
.file-sidebar-check-svg {
color: white;
color: var(--c-text-on-primary);
flex-shrink: 0;
}
@@ -179,14 +179,14 @@
display: block;
font-size: 13px;
font-weight: 500;
color: var(--text-primary);
color: var(--c-text);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.file-sidebar-file-item.selected .file-sidebar-file-name {
color: #3b82f6;
color: var(--c-accent-fg);
}
.file-sidebar-file-meta-row {
@@ -199,7 +199,7 @@
.file-sidebar-file-meta {
font-size: 11px;
color: var(--text-muted);
color: var(--c-text-subtle);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@@ -211,7 +211,7 @@
align-items: center;
justify-content: center;
flex-shrink: 0;
color: var(--accent-interactive, #6366f1);
color: var(--c-primary);
}
/* ---- Folder membership tags ---- */
@@ -250,7 +250,7 @@
font-size: 10px;
font-weight: 600;
line-height: 1.2;
color: var(--text-secondary);
color: var(--c-text-muted);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@@ -260,7 +260,7 @@
font-size: 10px;
font-weight: 600;
line-height: 1;
color: var(--text-muted);
color: var(--c-text-subtle);
cursor: default;
flex-shrink: 0;
}
@@ -277,7 +277,7 @@
background: transparent;
border-radius: 4px;
cursor: pointer;
color: var(--text-muted);
color: var(--c-text-subtle);
padding: 0;
opacity: 0;
transition:
@@ -297,48 +297,45 @@
bottom: 0;
z-index: 2;
/* solid base + green tint overlay so content behind doesn't bleed through */
background-color: var(--bg-toolbar);
background-color: var(--c-bg-raised);
background-image: linear-gradient(
rgba(34, 197, 94, 0.1),
rgba(34, 197, 94, 0.1)
color-mix(in srgb, var(--c-success) 10%, transparent),
color-mix(in srgb, var(--c-success) 10%, transparent)
);
}
.file-sidebar-file-item.viewed .file-sidebar-file-name {
color: #22c55e;
color: var(--c-success);
}
.file-sidebar-file-item.viewed .file-sidebar-file-check {
background-color: #22c55e;
background-color: var(--c-success);
}
/* viewed takes precedence over selected */
.file-sidebar-file-item.viewed.selected {
background-color: var(--bg-toolbar);
background-color: var(--c-bg-raised);
background-image: linear-gradient(
rgba(34, 197, 94, 0.1),
rgba(34, 197, 94, 0.1)
color-mix(in srgb, var(--c-success) 10%, transparent),
color-mix(in srgb, var(--c-success) 10%, transparent)
);
}
/* Always show eye for the currently viewed file */
.file-sidebar-file-item.viewed .file-sidebar-eye-btn {
opacity: 1;
color: #22c55e;
color: var(--c-success);
}
.file-sidebar-eye-btn:hover {
color: var(--text-primary);
color: var(--c-text);
}
/* Eye open: shown by default */
.file-sidebar-eye-open {
display: block !important;
}
.file-sidebar-eye-closed {
display: none !important;
}
.file-sidebar-eye-closed,
/* On hover over a viewed item: swap to eye-closed */
.file-sidebar-file-item.viewed:hover .file-sidebar-eye-open {
display: none !important;
@@ -359,7 +356,7 @@
background: transparent;
border-radius: 4px;
cursor: pointer;
color: var(--text-muted);
color: var(--c-text-subtle);
padding: 0;
opacity: 0;
transition:
@@ -376,14 +373,14 @@
}
.file-sidebar-kebab-btn:hover {
color: var(--text-primary);
color: var(--c-text);
}
/* ---- Date group headers ---- */
.file-sidebar-date-group-header {
font-size: 11px;
font-weight: 500;
color: var(--text-muted);
color: var(--c-text-subtle);
padding: 0 10px 2px 10px;
margin-top: 8px;
user-select: none;
@@ -410,13 +407,13 @@
.file-sidebar-empty-text {
font-size: 12px;
color: var(--text-muted);
color: var(--c-text-subtle);
margin: 0 0 2px 0;
}
.file-sidebar-empty-hint {
font-size: 11px;
color: var(--text-muted);
color: var(--c-text-subtle);
opacity: 0.7;
margin: 0;
}
@@ -426,8 +423,8 @@
position: fixed;
z-index: 9999;
transform: translateY(-50%);
background: var(--bg-surface, #fff);
border: 1px solid var(--border-subtle);
background: var(--c-surface);
border: 1px solid var(--c-border-subtle);
border-radius: 8px;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
padding: 4px;
@@ -54,8 +54,8 @@ export default function Footer({
<div
style={{
height: "var(--footer-height)",
backgroundColor: "var(--bg-surface)",
borderTop: "1px solid var(--border-subtle)",
backgroundColor: "var(--c-surface)",
borderTop: "1px solid var(--c-border-subtle)",
display: "flex",
flexDirection: "column",
justifyContent: "center",
@@ -6,8 +6,8 @@
display: flex;
gap: 8px;
align-items: center;
background: var(--bg-toolbar);
border: 1px solid var(--border-default);
background: var(--c-bg-raised);
border: 1px solid var(--c-border);
padding: 6px 12px;
border-radius: 20px;
box-shadow: var(--shadow-md);
@@ -70,7 +70,7 @@ const HoverActionMenu: React.FC<HoverActionMenuProps> = ({
disabled={action.disabled}
onClick={action.onClick}
aria-label={action.label}
style={{ color: action.color || "var(--text-secondary)" }}
style={{ color: action.color || "var(--c-text-muted)" }}
data-tour={action.dataTour}
>
{action.icon}
@@ -10,7 +10,7 @@
font-size: 2rem;
font-weight: 700;
line-height: 1.25;
color: var(--text-primary);
color: var(--c-text);
}
.landing-subtitle {
@@ -20,7 +20,7 @@
font-size: 0.9375rem;
line-height: 1.5;
max-width: 28rem;
color: var(--text-secondary);
color: var(--c-text-muted);
}
/* ── Document stack ──────────────────────────────────────── */
@@ -49,7 +49,7 @@
width: 128px;
height: 160px;
transform-origin: bottom center;
border: 1px solid #e5e7eb;
border: 1px solid var(--c-illustration-line);
box-shadow: var(--landing-doc-shadow-back-idle);
}
@@ -103,10 +103,10 @@
/* Bars — static light colours, never change with theme */
.landing-bar {
border-radius: 9999px;
background-color: #e5e7eb;
background-color: var(--c-illustration-line);
}
.landing-bar--strong {
background-color: #d1d5db;
background-color: var(--c-illustration-line-strong);
}
/* ── Action buttons ──────────────────────────────────────── */
@@ -121,27 +121,27 @@
.landing-btn-secondary {
border-radius: 0.75rem !important;
font-weight: 600 !important;
border-color: var(--landing-button-border, var(--border-default)) !important;
background-color: var(--landing-button-bg, var(--bg-surface)) !important;
color: var(--landing-button-color, var(--text-primary)) !important;
border-color: var(--landing-button-border, var(--c-border)) !important;
background-color: var(--landing-button-bg, var(--c-surface)) !important;
color: var(--c-accent-fg, var(--c-text)) !important;
}
.landing-btn-secondary:hover {
background-color: var(
--landing-button-hover-bg,
var(--landing-button-bg, var(--bg-surface))
var(--landing-button-bg, var(--c-surface))
) !important;
}
/* Icon-only variant: accent colour instead of button text colour */
.landing-btn-icon {
color: var(--accent-interactive) !important;
color: var(--c-primary) !important;
}
/* Dropzone accept/reject outlines. Mantine 8 no longer supports nested
* `&[data-accept]` selectors inside the `styles` prop object, so these are
* plain CSS attribute selectors on a class applied to the Dropzone root. */
.landing-dropzone[data-accept] {
outline: 2px dashed var(--accent-interactive);
outline: 2px dashed var(--c-primary);
outline-offset: 4px;
}
.landing-dropzone[data-reject] {
@@ -6,7 +6,7 @@
}
.languageItem {
border-right: 2px solid var(--mantine-color-gray-3);
border-right: 2px solid var(--c-border);
}
.languageItem:nth-child(4n) {
@@ -20,7 +20,7 @@
}
.languageItem:nth-child(4n) {
border-right: 2px solid var(--mantine-color-gray-3);
border-right: 2px solid var(--c-border);
}
.languageItem:nth-child(2n) {
@@ -34,7 +34,7 @@
}
.languageItem:nth-child(4n) {
border-right: 2px solid var(--mantine-color-gray-3);
border-right: 2px solid var(--c-border);
}
.languageItem:nth-child(3n) {
@@ -42,23 +42,12 @@
}
}
/* Dark theme support */
[data-mantine-color-scheme="dark"] .languageItem {
border-right-color: var(--mantine-color-dark-3);
}
/* Dark theme: divider colour comes from the adaptive --c-border token on the
base rules; only the layout quirk (no right border on 4n) differs. */
[data-mantine-color-scheme="dark"] .languageItem:nth-child(4n) {
border-right: none;
}
[data-mantine-color-scheme="dark"] .languageItem:nth-child(2n) {
border-right-color: var(--mantine-color-dark-3);
}
[data-mantine-color-scheme="dark"] .languageItem:nth-child(3n) {
border-right-color: var(--mantine-color-dark-3);
}
/* Responsive text visibility */
.languageText {
display: none;
@@ -91,10 +91,10 @@ const LanguageItem: React.FC<LanguageItemProps> = ({
? "light-dark(var(--mantine-color-blue-1), var(--mantine-color-blue-8))"
: undefined,
color: disabled
? "var(--text-muted)"
? "var(--c-text-subtle)"
: isSelected
? "light-dark(var(--mantine-color-blue-9), var(--mantine-color-blue-3))"
: "var(--text-primary)",
: "var(--c-text)",
}}
>
{label}
@@ -12,7 +12,7 @@
padding: 16px;
color: #ffffff;
font-weight: 600;
background: rgba(16, 18, 27, 0.55);
background: rgba(0, 0, 0, 0.55);
backdrop-filter: blur(6px);
-webkit-backdrop-filter: blur(6px);
border: 1px solid rgba(255, 255, 255, 0.06);
@@ -197,8 +197,8 @@ export const PageEditorFileDropdown: React.FC<PageEditorFileDropdownProps> = ({
<Menu.Dropdown
className="ph-no-capture"
style={{
backgroundColor: "var(--bg-file-manager)",
border: "1px solid var(--border-subtle)",
backgroundColor: "var(--c-bg)",
border: "1px solid var(--c-border-subtle)",
borderRadius: "8px",
boxShadow: "0 2px 8px rgba(0, 0, 0, 0.15)",
maxHeight: "80vh",
@@ -231,7 +231,7 @@ export const PageEditorFileDropdown: React.FC<PageEditorFileDropdownProps> = ({
marginTop: "0.5rem",
cursor: "pointer",
backgroundColor: "transparent",
borderTop: "1px solid var(--border-subtle)",
borderTop: "1px solid var(--c-border-subtle)",
transition: "background-color 0.15s ease",
}}
onMouseEnter={(e) => {
@@ -17,7 +17,7 @@
width: 15px;
height: 15px;
border-radius: 4px;
color: var(--text-secondary);
color: var(--c-text-muted);
background: color-mix(in srgb, currentColor 16%, transparent);
pointer-events: auto;
}
@@ -14,7 +14,7 @@ interface ToolIconProps {
export const ToolIcon: React.FC<ToolIconProps> = ({
icon,
opacity = 1,
color = "var(--tools-text-and-icon-color)",
color = "var(--c-text)",
marginRight = "0.5rem",
}) => {
return (
@@ -18,7 +18,7 @@
flex: 1;
min-width: 0;
padding: 0.4rem 0.75rem 0.4rem 0.4rem;
border: 1px solid var(--border-subtle, var(--mantine-color-default-border));
border: 1px solid var(--c-border-subtle, var(--mantine-color-default-border));
border-radius: 9999px;
background: var(--mantine-color-body);
text-align: left;
@@ -70,7 +70,7 @@
so it doesn't read as a clashing lighter card on the dark toolbar. */
[data-mantine-color-scheme="dark"] .sui-panelhdr__bar {
background: transparent;
border-color: var(--border-subtle, var(--mantine-color-default-border));
border-color: var(--c-border-subtle, var(--mantine-color-default-border));
}
[data-mantine-color-scheme="dark"] .sui-panelhdr__icon {
@@ -383,7 +383,7 @@ export const Tooltip: React.FC<TooltipProps> = ({
zIndex: Z_INDEX_OVER_FULLSCREEN_SURFACE,
visibility: positionReady ? "visible" : "hidden",
opacity: positionReady ? 1 : 0,
color: "var(--text-primary)",
color: "var(--c-text)",
...containerStyle,
}}
className={`${styles["tooltip-container"]} ${isPinned ? styles.pinned : ""}`}
@@ -291,7 +291,7 @@ const UpdateModal: React.FC<UpdateModalProps> = ({
<Box
style={{
border:
"1px solid var(--border-subtle, var(--mantine-color-default-border))",
"1px solid var(--c-border-subtle, var(--mantine-color-default-border))",
borderRadius: 12,
padding: "24px 28px",
background:
@@ -493,10 +493,10 @@ const UpdateModal: React.FC<UpdateModalProps> = ({
style={{
borderTop:
idx === 0
? "1px solid var(--border-subtle, var(--mantine-color-default-border))"
? "1px solid var(--c-border-subtle, var(--mantine-color-default-border))"
: undefined,
borderBottom:
"1px solid var(--border-subtle, var(--mantine-color-default-border))",
"1px solid var(--c-border-subtle, var(--mantine-color-default-border))",
padding: "10px 12px",
}}
>
@@ -576,10 +576,10 @@ const UpdateModal: React.FC<UpdateModalProps> = ({
style={{
borderTop:
index === 0
? "1px solid var(--border-subtle, var(--mantine-color-default-border))"
? "1px solid var(--c-border-subtle, var(--mantine-color-default-border))"
: undefined,
borderBottom:
"1px solid var(--border-subtle, var(--mantine-color-default-border))",
"1px solid var(--c-border-subtle, var(--mantine-color-default-border))",
}}
>
<Group
@@ -656,7 +656,7 @@ const UpdateModal: React.FC<UpdateModalProps> = ({
pb="sm"
style={{
borderTop:
"1px solid var(--border-subtle, var(--mantine-color-default-border))",
"1px solid var(--c-border-subtle, var(--mantine-color-default-border))",
}}
>
<Stack gap="sm" mt="sm">
@@ -749,7 +749,7 @@ const UpdateModal: React.FC<UpdateModalProps> = ({
<Box
style={{
border:
"1px solid var(--border-subtle, var(--mantine-color-default-border))",
"1px solid var(--c-border-subtle, var(--mantine-color-default-border))",
borderRadius: 12,
padding: "16px 20px",
}}
@@ -834,7 +834,7 @@ const UpdateModal: React.FC<UpdateModalProps> = ({
<Box
style={{
borderTop:
"1px solid var(--border-subtle, var(--mantine-color-default-border))",
"1px solid var(--c-border-subtle, var(--mantine-color-default-border))",
padding: "16px 28px",
flexShrink: 0,
}}
@@ -8,8 +8,8 @@
align-content: flex-start;
min-height: 40px;
padding: 0 8px;
background-color: var(--bg-toolbar);
border-bottom: 1px solid var(--border-subtle);
background-color: var(--c-bg-raised);
border-bottom: 1px solid var(--c-border-subtle);
flex-shrink: 0;
z-index: 50;
}
@@ -33,7 +33,7 @@
border: none;
border-radius: 8px;
background: transparent;
color: var(--text-secondary);
color: var(--c-text-muted);
font-size: 13px;
font-weight: 500;
cursor: pointer;
@@ -44,27 +44,23 @@
}
.workbench-bar-view-btn:hover {
background-color: var(--hover-bg);
color: var(--text-primary);
background-color: var(--c-hover);
color: var(--c-text);
}
.workbench-bar-view-btn.active {
background-color: var(--mantine-color-blue-light, rgba(59, 130, 246, 0.12));
color: var(--mantine-color-blue-6, #3b82f6);
background-color: var(--c-primary-subtle);
color: var(--c-accent-fg);
}
.workbench-bar-view-btn.workbench-bar-back-btn {
color: var(--text-primary);
color: var(--c-text);
font-weight: 600;
}
.workbench-bar-view-btn.workbench-bar-back-btn:hover {
background-color: color-mix(
in srgb,
var(--accent-interactive, #4a90e2) 12%,
transparent
);
color: var(--accent-interactive, #4a90e2);
background-color: color-mix(in srgb, var(--c-primary) 12%, transparent);
color: var(--c-accent-fg);
}
.workbench-bar-view-btn svg {
@@ -106,7 +102,7 @@
flex: 0 0 100%;
height: auto;
padding: 4px 0;
border-top: 1px solid var(--border-subtle);
border-top: 1px solid var(--c-border-subtle);
justify-content: flex-start;
overflow-x: auto;
overflow-y: hidden;
@@ -138,7 +134,7 @@
* Tight 24px hit-box and no hover background: these are quiet toolbar icons —
* hover feedback comes from the icon colour darkening, not a bg tint. */
.workbench-bar-action-icon {
color: var(--text-secondary) !important;
color: var(--c-text-muted) !important;
/* min-* too: Mantine's ActionIcon sets min-width/min-height from --ai-size
* (36px), which floors the width and defeats the 24px clamp. */
width: 24px !important;
@@ -155,17 +151,17 @@
}
.workbench-bar-action-icon:hover {
color: var(--text-primary) !important;
color: var(--c-text) !important;
}
.workbench-bar-action-icon[data-variant="filled"] {
background: var(--mantine-color-blue-6, #3b82f6) !important;
color: #fff !important;
background: var(--c-primary) !important;
color: var(--c-text-on-primary) !important;
}
.workbench-bar-action-icon[data-variant="filled"]:hover {
background: var(--mantine-color-blue-7, #2563eb) !important;
color: #fff !important;
background: var(--c-primary-hover) !important;
color: var(--c-text-on-primary) !important;
}
.workbench-bar-action-wrapper {
@@ -181,7 +177,7 @@
.workbench-bar-divider {
width: 1px;
height: 16px;
background-color: var(--border-subtle);
background-color: var(--c-border-subtle);
margin: 0 4px;
}
@@ -200,7 +196,7 @@
.viewer-inline-controls__page {
font-size: 12px;
font-weight: 500;
color: var(--text-secondary);
color: var(--c-text-muted);
min-width: 3rem;
text-align: center;
white-space: nowrap;
@@ -214,7 +210,7 @@
.viewer-inline-controls__zoom-pct {
font-size: 12px;
color: var(--text-muted);
color: var(--c-text-subtle);
min-width: 2.5rem;
text-align: right;
white-space: nowrap;
@@ -153,7 +153,7 @@ const DocumentThumbnail: React.FC<DocumentThumbnailProps> = ({
fontWeight: 700,
letterSpacing: "0.1em",
textTransform: "uppercase",
color: "var(--text-secondary)",
color: "var(--c-text-muted)",
background: "rgb(var(--border))",
padding: "3px 10px",
borderRadius: "6px",
@@ -15,7 +15,7 @@
display: flex;
align-items: center;
justify-content: center;
color: var(--search-text-and-icon-color);
color: var(--c-text-subtle);
}
.input {
@@ -27,12 +27,12 @@
outline: none;
box-shadow: none;
transition: box-shadow 0.2s ease;
background-color: var(--input-bg);
color: var(--search-text-and-icon-color);
background-color: var(--c-input-bg);
color: var(--c-text-subtle);
}
.input::placeholder {
color: var(--search-text-and-icon-color);
color: var(--c-text-subtle);
opacity: 1;
}
@@ -65,13 +65,9 @@
justify-content: center;
font-size: 16px;
transition: background-color 0.2s ease;
color: var(--search-text-and-icon-color);
color: var(--c-text-subtle);
}
.clearButton:hover {
background-color: rgba(0, 0, 0, 0.1);
}
[data-mantine-color-scheme="dark"] .clearButton:hover {
background-color: rgba(255, 255, 255, 0.1);
background-color: var(--c-hover);
}
@@ -1,9 +1,9 @@
/* Tooltip Container */
.tooltip-container {
position: fixed;
border: 0.0625rem solid var(--border-default);
border: 0.0625rem solid var(--c-border);
border-radius: 0.75rem;
background-color: var(--bg-raised);
background-color: var(--c-surface-raised);
box-shadow:
0 0.625rem 0.9375rem -0.1875rem rgba(0, 0, 0, 0.1),
0 0.25rem 0.375rem -0.125rem rgba(0, 0, 0, 0.05);
@@ -15,25 +15,25 @@
transform 100ms ease-out;
max-width: 50vh;
max-height: 80vh;
color: var(--text-primary);
color: var(--c-text);
display: flex;
flex-direction: column;
}
/* Pinned tooltip indicator */
.tooltip-container.pinned {
border-color: var(--primary-color, #3b82f6);
border-color: var(--primary-color, var(--c-primary));
box-shadow:
0 0.625rem 0.9375rem -0.1875rem rgba(0, 0, 0, 0.1),
0 0.25rem 0.375rem -0.125rem rgba(0, 0, 0, 0.05),
0 0 0 0.125rem rgba(59, 130, 246, 0.1);
0 0 0 0.125rem color-mix(in srgb, var(--c-primary) 10%, transparent);
}
/* Pinned tooltip header */
.tooltip-container.pinned .tooltip-header {
background-color: var(--primary-color, #3b82f6);
background-color: var(--primary-color, var(--c-primary));
color: white;
border-color: var(--primary-color, #3b82f6);
border-color: var(--primary-color, var(--c-primary));
}
/* Close button */
@@ -42,10 +42,10 @@
top: 0.5rem;
right: 0.5rem;
font-size: 0.875rem;
background: var(--bg-raised);
background: var(--c-surface-raised);
padding: 0.25rem;
border-radius: 0.25rem;
border: 0.0625rem solid var(--border-default);
border: 0.0625rem solid var(--c-border);
cursor: pointer;
transition:
background-color 0.2s ease,
@@ -64,15 +64,15 @@
}
.tooltip-pin-button:hover {
background-color: #ef4444 !important;
border-color: #ef4444 !important;
background-color: var(--c-danger) !important;
border-color: var(--c-danger) !important;
}
.tooltip-pin-button:focus,
.tooltip-pin-button:focus-visible {
outline: none;
border-color: var(--border-default) !important;
background-color: var(--bg-raised) !important;
border-color: var(--c-border) !important;
background-color: var(--c-surface-raised) !important;
}
/* Tooltip Header */
@@ -107,7 +107,7 @@
/* Tooltip Body */
.tooltip-body {
padding: 1rem;
color: var(--text-primary) !important;
color: var(--c-text) !important;
font-size: 0.875rem !important;
line-height: 1.6 !important;
overflow-y: auto;
@@ -116,47 +116,49 @@
}
.tooltip-body * {
color: var(--text-primary) !important;
color: var(--c-text) !important;
}
/* Link styling within tooltips */
.tooltip-body a {
color: var(--link-color, #3b82f6) !important;
color: var(--link-color, var(--c-primary)) !important;
text-decoration: underline;
text-decoration-color: var(--link-underline-color, rgba(59, 130, 246, 0.3));
text-decoration-color: var(
--link-underline-color,
color-mix(in srgb, var(--c-primary) 30%, transparent)
);
transition:
color 0.2s ease,
text-decoration-color 0.2s ease;
}
.tooltip-body a:hover {
color: var(--link-hover-color, #2563eb) !important;
color: var(--link-hover-color, var(--c-primary-hover)) !important;
text-decoration-color: var(
--link-hover-underline-color,
rgba(37, 99, 235, 0.5)
color-mix(in srgb, var(--c-primary-hover) 50%, transparent)
);
}
.tooltip-container .tooltip-body {
color: var(--text-primary) !important;
}
.tooltip-container .tooltip-body,
.tooltip-container .tooltip-body * {
color: var(--text-primary) !important;
color: var(--c-text) !important;
}
/* Ensure links maintain their styling */
.tooltip-container .tooltip-body a {
color: var(--link-color, #3b82f6) !important;
color: var(--link-color, var(--c-primary)) !important;
text-decoration: underline;
text-decoration-color: var(--link-underline-color, rgba(59, 130, 246, 0.3));
text-decoration-color: var(
--link-underline-color,
color-mix(in srgb, var(--c-primary) 30%, transparent)
);
}
.tooltip-container .tooltip-body a:hover {
color: var(--link-hover-color, #2563eb) !important;
color: var(--link-hover-color, var(--c-primary-hover)) !important;
text-decoration-color: var(
--link-hover-underline-color,
rgba(37, 99, 235, 0.5)
color-mix(in srgb, var(--c-primary-hover) 50%, transparent)
);
}
@@ -165,8 +167,8 @@
position: absolute;
width: 0.5rem;
height: 0.5rem;
background: var(--bg-raised);
border: 0.0625rem solid var(--border-default);
background: var(--c-surface-raised);
border: 0.0625rem solid var(--c-border);
transform: rotate(45deg);
}
@@ -17,13 +17,13 @@ export const TooltipContent: React.FC<TooltipContentProps> = ({
<div
className={`${styles["tooltip-body"]}`}
style={{
color: "var(--text-primary)",
color: "var(--c-text)",
padding: `16px ${16 + extraRightPadding}px 16px 16px`,
fontSize: "14px",
lineHeight: "1.6",
}}
>
<div style={{ color: "var(--text-primary)" }}>
<div style={{ color: "var(--c-text)" }}>
{tips ? (
<>
{tips.map((tip, index) => (
@@ -35,8 +35,8 @@ export const TooltipContent: React.FC<TooltipContentProps> = ({
<div
style={{
display: "inline-block",
backgroundColor: "var(--tooltip-title-bg)",
color: "var(--tooltip-title-color)",
backgroundColor: "var(--c-primary-tint)",
color: "var(--c-text)",
padding: "6px 12px",
borderRadius: "16px",
fontSize: "12px",
@@ -51,7 +51,7 @@ export const TooltipContent: React.FC<TooltipContentProps> = ({
<p
style={{
margin: "0 0 12px 0",
color: "var(--text-secondary)",
color: "var(--c-text-muted)",
fontSize: "13px",
}}
dangerouslySetInnerHTML={{ __html: tip.description }}
@@ -62,7 +62,7 @@ export const TooltipContent: React.FC<TooltipContentProps> = ({
style={{
margin: "0",
paddingLeft: "16px",
color: "var(--text-secondary)",
color: "var(--c-text-muted)",
fontSize: "13px",
}}
>
@@ -75,26 +75,26 @@
/* Toast Alert Type Colors */
.toast-item--success {
background: var(--color-green-100);
color: var(--text-primary);
color: var(--c-text);
border: 1px solid var(--color-green-400);
}
.toast-item--error {
background: var(--color-red-100);
color: var(--text-primary);
color: var(--c-text);
border: 1px solid var(--color-red-400);
}
.toast-item--warning {
background: var(--color-yellow-100);
color: var(--text-primary);
color: var(--c-text);
border: 1px solid var(--color-yellow-400);
}
.toast-item--neutral {
background: var(--bg-surface);
color: var(--text-primary);
border: 1px solid var(--border-default);
background: var(--c-surface);
color: var(--c-text);
border: 1px solid var(--c-border);
}
/* Toast Header Row */
@@ -146,7 +146,7 @@
border-radius: 999px;
border: none;
background: transparent;
color: var(--text-secondary);
color: var(--c-text-muted);
cursor: pointer;
display: flex;
align-items: center;
@@ -166,7 +166,7 @@
.toast-progress-container {
margin-top: 8px;
height: 6px;
background: var(--bg-muted);
background: var(--c-surface-sunken);
border-radius: 999px;
overflow: hidden;
}
@@ -217,21 +217,21 @@
}
.toast-action-button--success {
color: var(--text-primary);
color: var(--c-text);
border-color: var(--color-green-400);
}
.toast-action-button--error {
color: var(--text-primary);
color: var(--c-text);
border-color: var(--color-red-400);
}
.toast-action-button--warning {
color: var(--text-primary);
color: var(--c-text);
border-color: var(--color-yellow-400);
}
.toast-action-button--neutral {
color: var(--text-primary);
border-color: var(--border-default);
color: var(--c-text);
border-color: var(--c-border);
}
@@ -168,7 +168,7 @@ export default function RightSidebar() {
ref={toolPanelRef}
data-sidebar="tool-panel"
data-tour={fullscreenExpanded ? undefined : "tool-panel"}
className={`tool-panel flex flex-col ${fullscreenExpanded ? "tool-panel--fullscreen-active" : "overflow-hidden"} bg-[var(--bg-toolbar)] border-l border-[var(--border-subtle)] transition-all duration-300 ease-out ${isMobile ? "h-full border-r-0" : "h-screen"} ${fullscreenExpanded ? "tool-panel--fullscreen" : ""}`}
className={`tool-panel flex flex-col ${fullscreenExpanded ? "tool-panel--fullscreen-active" : "overflow-hidden"} bg-[var(--c-bg-raised)] border-l border-[var(--c-border-subtle)] transition-all duration-300 ease-out ${isMobile ? "h-full border-r-0" : "h-screen"} ${fullscreenExpanded ? "tool-panel--fullscreen" : ""}`}
style={{
width: computedWidth(),
padding: "0",
@@ -4,8 +4,8 @@
align-items: center;
gap: 2px;
padding: 4px 8px;
border-bottom: 1px solid var(--border-subtle);
background-color: var(--bg-toolbar);
border-bottom: 1px solid var(--c-border-subtle);
background-color: var(--c-bg-raised);
flex-shrink: 0;
}
@@ -13,115 +13,107 @@
.tool-panel__fullscreen-surface-inner {
--fullscreen-bg-surface-1: color-mix(
in srgb,
var(--bg-toolbar) 96%,
var(--c-bg-raised) 96%,
transparent
);
--fullscreen-bg-surface-2: color-mix(
in srgb,
var(--bg-background) 90%,
transparent
);
--fullscreen-bg-header: var(--bg-toolbar);
--fullscreen-bg-controls-1: var(--bg-toolbar);
--fullscreen-bg-surface-2: color-mix(in srgb, var(--c-bg) 90%, transparent);
--fullscreen-bg-header: var(--c-bg-raised);
--fullscreen-bg-controls-1: var(--c-bg-raised);
--fullscreen-bg-controls-2: color-mix(
in srgb,
var(--bg-toolbar) 95%,
var(--bg-background)
);
--fullscreen-bg-body-1: color-mix(
in srgb,
var(--bg-background) 86%,
transparent
var(--c-bg-raised) 95%,
var(--c-bg)
);
--fullscreen-bg-body-1: color-mix(in srgb, var(--c-bg) 86%, transparent);
--fullscreen-bg-body-2: color-mix(
in srgb,
var(--bg-toolbar) 78%,
var(--c-bg-raised) 78%,
transparent
);
--fullscreen-bg-group: color-mix(in srgb, var(--bg-toolbar) 82%, transparent);
--fullscreen-bg-item: color-mix(in srgb, var(--bg-toolbar) 88%, transparent);
--fullscreen-bg-group: color-mix(
in srgb,
var(--c-bg-raised) 82%,
transparent
);
--fullscreen-bg-item: color-mix(in srgb, var(--c-bg-raised) 88%, transparent);
--fullscreen-bg-list-item: color-mix(
in srgb,
var(--bg-toolbar) 86%,
var(--c-bg-raised) 86%,
transparent
);
--fullscreen-bg-icon-detailed: color-mix(
in srgb,
var(--bg-muted) 75%,
var(--c-surface-sunken) 75%,
transparent
);
--fullscreen-bg-icon-compact: color-mix(
in srgb,
var(--bg-muted) 70%,
var(--c-surface-sunken) 70%,
transparent
);
--fullscreen-border-subtle-75: color-mix(
in srgb,
var(--border-subtle) 75%,
var(--c-border-subtle) 75%,
transparent
);
--fullscreen-border-subtle-70: color-mix(
in srgb,
var(--border-subtle) 70%,
var(--c-border-subtle) 70%,
transparent
);
--fullscreen-border-subtle-65: color-mix(
in srgb,
var(--border-subtle) 65%,
var(--c-border-subtle) 65%,
transparent
);
--fullscreen-border-favorites: color-mix(
in srgb,
var(--special-color-favorites) 25%,
var(--border-subtle)
var(--c-border-subtle)
);
--fullscreen-border-recommended: color-mix(
in srgb,
var(--special-color-recommended) 25%,
var(--border-subtle)
var(--c-border-subtle)
);
--fullscreen-shadow-primary: color-mix(
in srgb,
var(--shadow-color, rgba(15, 23, 42, 0.55)) 25%,
var(--shadow-color, color-mix(in srgb, black 55%, transparent)) 25%,
transparent
);
--fullscreen-shadow-secondary: color-mix(
in srgb,
var(--shadow-color, rgba(15, 23, 42, 0.35)) 30%,
var(--shadow-color, color-mix(in srgb, black 35%, transparent)) 30%,
transparent
);
--fullscreen-shadow-group: color-mix(
in srgb,
var(--shadow-color, rgba(15, 23, 42, 0.45)) 18%,
var(--shadow-color, color-mix(in srgb, black 45%, transparent)) 18%,
transparent
);
--fullscreen-accent-hover: color-mix(
in srgb,
var(--text-primary) 20%,
var(--border-subtle)
var(--c-text) 20%,
var(--c-border-subtle)
);
--fullscreen-accent-selected: color-mix(
in srgb,
var(--text-primary) 30%,
var(--border-subtle)
);
--fullscreen-accent-ring: color-mix(
in srgb,
var(--text-primary) 15%,
transparent
var(--c-text) 30%,
var(--c-border-subtle)
);
--fullscreen-accent-ring: color-mix(in srgb, var(--c-text) 15%, transparent);
--fullscreen-accent-list-bg: color-mix(
in srgb,
var(--text-primary) 8%,
var(--bg-toolbar)
var(--c-text) 8%,
var(--c-bg-raised)
);
--fullscreen-accent-list-border: color-mix(
in srgb,
var(--text-primary) 20%,
var(--border-subtle)
var(--c-text) 20%,
var(--c-border-subtle)
);
--fullscreen-text-icon: var(--text-primary);
--fullscreen-text-icon-compact: var(--text-primary);
--fullscreen-text-icon: var(--c-text);
--fullscreen-text-icon-compact: var(--c-text);
}
.tool-panel {
@@ -153,7 +145,7 @@
.tool-panel__collapsed-divider {
height: 1px;
background: var(--border-subtle);
background: var(--c-border-subtle);
margin: 0 0.5rem 8px;
}
@@ -178,7 +170,7 @@
border: 1px solid transparent;
border-radius: 0.5rem;
background: transparent;
color: var(--tools-text-and-icon-color);
color: var(--c-text);
cursor: pointer;
transition:
background 120ms ease-out,
@@ -198,8 +190,8 @@
.tool-panel__expand-btn {
flex-shrink: 0;
color: var(--text-secondary) !important;
border-color: var(--border-subtle) !important;
color: var(--c-text-muted) !important;
border-color: var(--c-border-subtle) !important;
}
/* The collapse/expand toggle keeps a stable identity across the collapsed strip
@@ -210,25 +202,25 @@
}
.tool-panel__expand-btn svg {
color: var(--text-secondary) !important;
color: var(--c-text-muted) !important;
}
.tool-panel__expand-btn:hover {
color: var(--text-primary) !important;
color: var(--c-text) !important;
border-color: var(--border) !important;
}
.tool-panel__collapsed-search-btn {
flex-shrink: 0;
color: var(--text-secondary) !important;
color: var(--c-text-muted) !important;
}
.tool-panel__collapsed-search-btn svg {
color: var(--text-secondary) !important;
color: var(--c-text-muted) !important;
}
.tool-panel__collapsed-search-btn:hover {
color: var(--text-primary) !important;
color: var(--c-text) !important;
}
.tool-panel__back-btn {
@@ -237,7 +229,7 @@
.tool-panel__back-bar {
flex-shrink: 0;
border-bottom: 1px solid var(--border-subtle) !important;
border-bottom: 1px solid var(--c-border-subtle) !important;
}
.tool-panel--fullscreen-active {
@@ -282,7 +274,7 @@
/* Search that separates the Policies section from the Tools list below it. */
.tool-panel__between-search {
padding: 0.5rem 0.75rem 0.25rem;
border-top: 1px solid var(--border-subtle, var(--color-border));
border-top: 1px solid var(--c-border-subtle, var(--c-border));
}
.tool-panel__between-search .search-input-container {
@@ -292,7 +284,7 @@
/* Slightly recessed search field so it reads as distinct from the panel. */
.tool-panel__between-search input {
background-color: var(--bg-muted);
background-color: var(--c-surface-sunken);
}
::view-transition-old(tool-rail) {
@@ -334,7 +326,7 @@
}
.tool-panel--fullscreen {
background: var(--bg-toolbar);
background: var(--c-bg-raised);
}
.tool-panel__placeholder {
@@ -342,7 +334,7 @@
display: flex;
align-items: center;
justify-content: center;
color: var(--text-muted);
color: var(--c-text-subtle);
font-size: 0.9rem;
padding: 1.5rem;
text-align: center;
@@ -403,8 +395,8 @@
align-items: center;
gap: 1rem;
padding: 0.75rem 1.75rem;
border-bottom: 1px solid var(--border-subtle);
background: var(--bg-toolbar);
border-bottom: 1px solid var(--c-border-subtle);
background: var(--c-bg-raised);
}
.tool-panel__fullscreen-brand {
@@ -428,8 +420,8 @@
align-items: center;
gap: 1rem;
padding: 0.75rem 1.75rem;
border-bottom: 1px solid var(--tool-panel-search-border-bottom);
background: var(--tool-panel-search-bg);
border-bottom: 1px solid var(--c-border);
background: var(--c-surface-sunken);
}
.tool-panel__fullscreen-controls .search-input-container {
@@ -546,7 +538,10 @@
.tool-panel__fullscreen-item:hover:not([aria-disabled="true"]):not(:disabled) {
transform: translateY(-2px);
border-color: var(--fullscreen-accent-hover);
box-shadow: var(--shadow-xl, 0 18px 34px rgba(15, 23, 42, 0.14));
box-shadow: var(
--shadow-xl,
0 18px 34px color-mix(in srgb, black 14%, transparent)
);
}
.tool-panel__fullscreen-item--selected {
@@ -619,8 +614,8 @@
inset: 0;
background: linear-gradient(
135deg,
color-mix(in srgb, var(--text-primary) 12%, transparent),
color-mix(in srgb, var(--text-primary) 4%, transparent)
color-mix(in srgb, var(--c-text) 12%, transparent),
color-mix(in srgb, var(--c-text) 4%, transparent)
);
opacity: 0;
transition: opacity 0.2s ease;
@@ -630,8 +625,7 @@
.tool-panel__fullscreen-item:hover:not([aria-disabled="true"])
.tool-panel__fullscreen-icon {
transform: scale(1.08);
box-shadow: 0 4px 12px
color-mix(in srgb, var(--text-primary) 15%, transparent);
box-shadow: 0 4px 12px color-mix(in srgb, var(--c-text) 15%, transparent);
}
.tool-panel__fullscreen-item:hover:not([aria-disabled="true"])
@@ -656,7 +650,7 @@
}
.tool-panel__fullscreen-name {
color: var(--text-primary);
color: var(--c-text);
font-size: 13px !important;
font-weight: 500 !important;
}
@@ -753,8 +747,8 @@
inset: 0;
background: linear-gradient(
135deg,
color-mix(in srgb, var(--text-primary) 10%, transparent),
color-mix(in srgb, var(--text-primary) 3%, transparent)
color-mix(in srgb, var(--c-text) 10%, transparent),
color-mix(in srgb, var(--c-text) 3%, transparent)
);
opacity: 0;
transition: opacity 0.2s ease;
@@ -764,7 +758,7 @@
.tool-panel__fullscreen-list-item:hover:not([aria-disabled="true"])
.tool-panel__fullscreen-list-icon {
transform: scale(1.06);
box-shadow: 0 2px 8px color-mix(in srgb, var(--text-primary) 12%, transparent);
box-shadow: 0 2px 8px color-mix(in srgb, var(--c-text) 12%, transparent);
}
.tool-panel__fullscreen-list-item:hover:not([aria-disabled="true"])
@@ -1,10 +1,10 @@
.tool-panel-mode-prompt__modal {
background: color-mix(in srgb, var(--bg-toolbar) 94%, transparent);
border: 1px solid color-mix(in srgb, var(--border-subtle) 70%, transparent);
background: color-mix(in srgb, var(--c-bg-raised) 94%, transparent);
border: 1px solid color-mix(in srgb, var(--c-border-subtle) 70%, transparent);
box-shadow: 0 32px 64px
color-mix(
in srgb,
var(--shadow-color, rgba(15, 23, 42, 0.55)) 20%,
var(--shadow-color, color-mix(in srgb, black 55%, transparent)) 20%,
transparent
);
max-width: min(46rem, 100%);
@@ -22,8 +22,8 @@
gap: 1rem;
background: linear-gradient(
145deg,
color-mix(in srgb, var(--bg-surface) 96%, transparent),
color-mix(in srgb, var(--bg-muted) 70%, transparent)
color-mix(in srgb, var(--c-surface) 96%, transparent),
color-mix(in srgb, var(--c-surface-sunken) 70%, transparent)
);
width: 100%;
max-width: 19rem;
@@ -31,28 +31,20 @@
.tool-panel-mode-prompt__card--sidebar {
border: 1px solid
color-mix(
in srgb,
var(--accent-primary, var(--mantine-color-blue-6, #228be6)) 18%,
var(--border-subtle)
);
color-mix(in srgb, var(--c-primary) 18%, var(--c-border-subtle));
background: linear-gradient(
165deg,
color-mix(in srgb, var(--bg-surface) 96%, transparent),
color-mix(
in srgb,
var(--accent-primary, var(--mantine-color-blue-6, #228be6)) 8%,
transparent
)
color-mix(in srgb, var(--c-surface) 96%, transparent),
color-mix(in srgb, var(--c-primary) 8%, transparent)
);
}
.tool-panel-mode-prompt__preview {
border-radius: 0.9rem;
border: 1px solid color-mix(in srgb, var(--border-subtle) 70%, transparent);
border: 1px solid color-mix(in srgb, var(--c-border-subtle) 70%, transparent);
background: linear-gradient(
135deg,
color-mix(in srgb, var(--bg-muted) 82%, transparent),
color-mix(in srgb, var(--c-surface-sunken) 82%, transparent),
transparent 75%
);
padding: 0.75rem;
@@ -76,54 +68,49 @@
gap: 0.45rem;
background: linear-gradient(
180deg,
color-mix(in srgb, var(--bg-muted) 88%, transparent),
color-mix(in srgb, var(--bg-muted) 72%, transparent)
color-mix(in srgb, var(--c-surface-sunken) 88%, transparent),
color-mix(in srgb, var(--c-surface-sunken) 72%, transparent)
);
border: 1px solid color-mix(in srgb, var(--border-subtle) 65%, transparent);
border: 1px solid color-mix(in srgb, var(--c-border-subtle) 65%, transparent);
}
.tool-panel-mode-prompt__sidebar-search {
height: 0.5rem;
border-radius: 0.4rem;
background: color-mix(in srgb, var(--bg-background) 90%, transparent);
border: 1px solid color-mix(in srgb, var(--border-subtle) 60%, transparent);
background: color-mix(in srgb, var(--c-bg) 90%, transparent);
border: 1px solid color-mix(in srgb, var(--c-border-subtle) 60%, transparent);
}
.tool-panel-mode-prompt__sidebar-item {
height: 0.55rem;
border-radius: 0.35rem;
background: color-mix(
in srgb,
var(--accent-primary, var(--mantine-color-blue-6, #228be6)) 32%,
var(--bg-muted)
);
background: color-mix(in srgb, var(--c-primary) 32%, var(--c-surface-sunken));
}
.tool-panel-mode-prompt__sidebar-item--muted {
background: color-mix(in srgb, var(--bg-background) 88%, transparent);
background: color-mix(in srgb, var(--c-bg) 88%, transparent);
}
.tool-panel-mode-prompt__workspace {
flex: 1;
border-radius: 0.65rem;
border: 1px solid color-mix(in srgb, var(--border-subtle) 65%, transparent);
border: 1px solid color-mix(in srgb, var(--c-border-subtle) 65%, transparent);
padding: 0.5rem;
display: grid;
gap: 0.35rem;
grid-template-rows: 1.4fr 0.6fr;
background: linear-gradient(
160deg,
color-mix(in srgb, var(--bg-background) 94%, transparent),
color-mix(in srgb, var(--bg-muted) 68%, transparent)
color-mix(in srgb, var(--c-bg) 94%, transparent),
color-mix(in srgb, var(--c-surface-sunken) 68%, transparent)
);
}
.tool-panel-mode-prompt__workspace-page {
border-radius: 0.45rem;
background: color-mix(in srgb, var(--bg-surface) 96%, transparent);
border: 1px solid color-mix(in srgb, var(--border-subtle) 55%, transparent);
box-shadow: inset 0 0 0 1px
color-mix(in srgb, var(--bg-background) 60%, transparent);
background: color-mix(in srgb, var(--c-surface) 96%, transparent);
border: 1px solid color-mix(in srgb, var(--c-border-subtle) 55%, transparent);
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--c-bg) 60%, transparent);
}
.tool-panel-mode-prompt__workspace-page--secondary {
@@ -151,11 +138,11 @@
.tool-panel-mode-prompt__legacy-card {
border-radius: 0.45rem;
border: 1px solid color-mix(in srgb, var(--border-subtle) 55%, transparent);
border: 1px solid color-mix(in srgb, var(--c-border-subtle) 55%, transparent);
background: linear-gradient(
150deg,
color-mix(in srgb, var(--bg-muted) 88%, transparent),
color-mix(in srgb, var(--bg-background) 76%, transparent)
color-mix(in srgb, var(--c-surface-sunken) 88%, transparent),
color-mix(in srgb, var(--c-bg) 76%, transparent)
);
height: 1.2rem;
}
@@ -185,11 +172,11 @@
.tool-panel-mode-prompt__fullscreen-card {
border-radius: 0.45rem;
border: 1px solid color-mix(in srgb, var(--border-subtle) 55%, transparent);
border: 1px solid color-mix(in srgb, var(--c-border-subtle) 55%, transparent);
background: linear-gradient(
150deg,
color-mix(in srgb, var(--bg-muted) 88%, transparent),
color-mix(in srgb, var(--bg-background) 76%, transparent)
color-mix(in srgb, var(--c-surface-sunken) 88%, transparent),
color-mix(in srgb, var(--c-bg) 76%, transparent)
);
height: 1.2rem;
}
@@ -210,20 +197,15 @@
}
.tool-panel-mode-prompt__action:hover {
box-shadow: 0 10px 18px
color-mix(
in srgb,
var(--accent-primary, var(--mantine-color-blue-6, #228be6)) 25%,
transparent
);
box-shadow: 0 10px 18px color-mix(in srgb, var(--c-primary) 25%, transparent);
}
.tool-panel-mode-prompt__maybe-later {
color: color-mix(in srgb, var(--text-secondary) 90%, var(--text-muted));
color: color-mix(in srgb, var(--c-text-muted) 90%, var(--c-text-subtle));
}
.tool-panel-mode-prompt__maybe-later:hover {
background: color-mix(in srgb, var(--bg-muted) 78%, transparent);
background: color-mix(in srgb, var(--c-surface-sunken) 78%, transparent);
}
@media (max-width: 600px) {
@@ -37,7 +37,7 @@ const HEADER_TEXT_STYLE: React.CSSProperties = {
padding: "0.25rem 0 0.35rem 0.5rem",
textTransform: "uppercase",
letterSpacing: "0.06em",
color: "var(--text-muted)",
color: "var(--c-text-subtle)",
};
const SCROLLABLE_STYLE: React.CSSProperties = {
flex: 1,
@@ -50,7 +50,7 @@ const SCROLLABLE_STYLE: React.CSSProperties = {
const CONTAINER_STYLE: React.CSSProperties = {
display: "flex",
flexDirection: "column",
background: "var(--bg-toolbar)",
background: "var(--c-bg-raised)",
};
const toTitleCase = (s: string) =>
s.replace(
@@ -16,7 +16,7 @@
}
.containerBorder {
border: 1px solid var(--border-default, #333);
border: 1px solid var(--c-border);
}
/* Page thumbnail styles */
@@ -105,8 +105,8 @@
.gridTileSelected,
.gridTileHovered {
border: 2px solid var(--mantine-primary-color-filled, #3b82f6);
background-color: rgba(59, 130, 246, 0.2);
border: 2px solid var(--c-primary);
background-color: color-mix(in srgb, var(--c-primary) 20%, transparent);
}
/* Preview header */
@@ -116,14 +116,14 @@
.divider {
height: 1px;
background-color: var(--border-default, #333);
background-color: var(--c-border);
margin-bottom: 8px;
}
.previewLabel {
font-size: 14px;
font-weight: 500;
color: var(--text-primary);
color: var(--c-text);
text-align: center;
}
@@ -220,7 +220,7 @@ export default function PageNumberPreview({
width: "100%",
aspectRatio: `${(pageSize?.widthPts ?? 595.28) / (pageSize?.heightPts ?? 841.89)} / 1`,
backgroundColor: pageThumbnail ? "white" : "rgba(255,255,255,0.03)",
border: "1px solid var(--border-default, #333)",
border: "1px solid var(--c-border, #333)",
overflow: "hidden" as const,
}),
[pageSize, pageThumbnail],
@@ -16,7 +16,7 @@
}
.containerBorder {
border: 1px solid var(--border-default, #333);
border: 1px solid var(--c-border);
}
/* Page thumbnail styles */
@@ -95,7 +95,7 @@
.gridTileSelected,
.gridTileHovered {
border: 2px solid var(--mantine-primary-color-filled, #3b82f6);
border: 2px solid var(--c-primary);
}
/* Preview header */
@@ -105,14 +105,14 @@
.divider {
height: 1px;
background-color: var(--border-default, #333);
background-color: var(--c-border);
margin-bottom: 8px;
}
.previewLabel {
font-size: 14px;
font-weight: 500;
color: var(--text-primary);
color: var(--c-text);
text-align: center;
}
@@ -127,7 +127,7 @@
/* Information text container */
.informationContainer {
background-color: var(--information-text-bg);
background-color: var(--c-surface);
padding: 2px;
padding-left: 8px;
padding-right: 8px;
@@ -280,7 +280,7 @@ export function computeStampPreviewStyle(
width: "100%",
aspectRatio: `${(pageSize?.widthPts ?? 595.28) / (pageSize?.heightPts ?? 841.89)} / 1`,
backgroundColor: hasPageThumbnail ? "white" : "rgba(255,255,255,0.03)",
border: "1px solid var(--border-default, #333)",
border: "1px solid var(--c-border, #333)",
overflow: "hidden",
},
item: {
@@ -191,11 +191,9 @@ export default function AutomationEntry({
className="tool-button"
style={{
borderRadius: 0,
color: "var(--tools-text-and-icon-color)",
color: "var(--c-text)",
overflow: "visible",
backgroundColor: shouldShowMenu
? "var(--automation-entry-hover-bg)"
: undefined,
backgroundColor: shouldShowMenu ? "var(--c-hover)" : undefined,
}}
>
{buttonContent}
@@ -153,7 +153,7 @@ export default function AutomationRun({
style={{
width: 16,
height: 16,
border: "2px solid #ccc",
border: "2px solid var(--c-border)",
borderRadius: "50%",
}}
/>
@@ -148,7 +148,7 @@ export default function ToolList({
borderTop: "none",
borderRadius:
"0 0 var(--mantine-radius-lg) var(--mantine-radius-lg)",
backgroundColor: "var(--active-bg)",
backgroundColor: "var(--c-active)",
padding: "var(--mantine-spacing-xs)",
}}
>
@@ -75,7 +75,7 @@ const CertificateTypeSettings = ({
if (!hasAlternativeSources) {
return (
<Stack gap="md">
<div style={{ color: "var(--text-muted)", fontSize: "11px" }}>
<div style={{ color: "var(--c-text-subtle)", fontSize: "11px" }}>
{t(
"certSign.source.noOtherSources",
"No other certificate sources are available.",
@@ -317,9 +317,9 @@ const SignRequestPanel = ({ data }: SignRequestPanelProps) => {
onClick={handleAddToActiveFiles}
fullWidth
style={{
backgroundColor: "var(--landing-inner-paper-bg)",
color: "var(--btn-open-file)",
border: "1px solid var(--landing-inner-paper-border)",
backgroundColor: "var(--c-surface-raised)",
color: "var(--c-primary)",
border: "1px solid var(--c-border)",
}}
>
{t("certSign.collab.signRequest.addToFiles", "Add to Active Files")}
@@ -5,19 +5,14 @@
.compare-dropdown-sticky {
position: sticky;
z-index: 2;
background: var(--compare-page-label-bg);
background: var(--c-surface-sunken);
color: var(--compare-page-label-fg);
font-size: 0.75rem;
padding: 0.25rem 0.5rem;
border-bottom: 1px solid var(--border-subtle);
border-bottom: 1px solid var(--c-border-subtle);
pointer-events: none;
}
[data-mantine-color-scheme="dark"] .compare-dropdown-sticky {
background: var(--compare-page-label-bg);
color: var(--compare-page-label-fg);
border-bottom: 1px solid var(--border-default);
}
.compare-workbench {
display: flex;
flex-direction: column;
@@ -91,9 +86,9 @@
position: sticky;
top: 0;
z-index: 10;
background: var(--bg-toolbar);
background: var(--c-bg-raised);
backdrop-filter: blur(8px);
border-bottom: 1px solid var(--border-default);
border-bottom: 1px solid var(--c-border);
padding: 0.5rem;
margin: -0.5rem -0.5rem 0.5rem -0.5rem;
}
@@ -164,21 +159,13 @@
margin: 0 !important;
border-top-left-radius: 8px !important;
border-top-right-radius: 8px !important;
}
/* Style the dropdown container */
.compare-changes-select .mantine-Combobox-dropdown {
border: 1px solid var(--border-subtle) !important;
border-radius: 8px !important;
box-shadow: var(--shadow-md) !important;
background-color: var(--bg-surface) !important;
}
} /* Style the dropdown container */
.compare-changes-select .mantine-Combobox-dropdown,
.compare-changes-select--comparison .mantine-Combobox-dropdown {
border: 1px solid var(--border-subtle) !important;
border: 1px solid var(--c-border-subtle) !important;
border-radius: 8px !important;
box-shadow: var(--shadow-md) !important;
background-color: var(--bg-surface) !important;
background-color: var(--c-surface) !important;
}
/* Custom scrollbar for ScrollArea */
@@ -187,18 +174,18 @@
}
.compare-changes-select .mantine-ScrollArea-viewport::-webkit-scrollbar-track {
background: var(--bg-muted) !important;
background: var(--c-surface-sunken) !important;
border-radius: 3px !important;
}
.compare-changes-select .mantine-ScrollArea-viewport::-webkit-scrollbar-thumb {
background: var(--border-strong) !important;
background: var(--c-border-strong) !important;
border-radius: 3px !important;
}
.compare-changes-select
.mantine-ScrollArea-viewport::-webkit-scrollbar-thumb:hover {
background: var(--text-muted) !important;
background: var(--c-text-subtle) !important;
}
.compare-changes-select--comparison
@@ -208,27 +195,21 @@
.compare-changes-select--comparison
.mantine-ScrollArea-viewport::-webkit-scrollbar-track {
background: var(--bg-muted) !important;
background: var(--c-surface-sunken) !important;
border-radius: 3px !important;
}
.compare-changes-select--comparison
.mantine-ScrollArea-viewport::-webkit-scrollbar-thumb {
background: var(--border-strong) !important;
background: var(--c-border-strong) !important;
border-radius: 3px !important;
}
.compare-changes-select--comparison
.mantine-ScrollArea-viewport::-webkit-scrollbar-thumb:hover {
background: var(--text-muted) !important;
}
/* Style the dropdown options */
.compare-changes-select .mantine-Combobox-option {
font-size: 0.875rem !important;
padding: 8px 12px !important;
}
background: var(--c-text-subtle) !important;
} /* Style the dropdown options */
.compare-changes-select .mantine-Combobox-option,
.compare-changes-select--comparison .mantine-Combobox-option {
font-size: 0.875rem !important;
padding: 8px 12px !important;
@@ -240,27 +221,16 @@
.compare-changes-select--comparison .mantine-Combobox-option:hover {
background-color: var(--spdf-compare-added-badge-bg) !important;
}
/* Style the search input */
.compare-changes-select .mantine-Combobox-search {
font-size: 0.875rem !important;
padding: 8px 12px !important;
border-bottom: 1px solid var(--border-subtle) !important;
}
} /* Style the search input */
.compare-changes-select .mantine-Combobox-search,
.compare-changes-select--comparison .mantine-Combobox-search {
font-size: 0.875rem !important;
padding: 8px 12px !important;
border-bottom: 1px solid var(--border-subtle) !important;
border-bottom: 1px solid var(--c-border-subtle) !important;
}
.compare-changes-select .mantine-Combobox-search::placeholder {
color: var(--text-muted) !important;
}
.compare-changes-select .mantine-Combobox-search::placeholder,
.compare-changes-select--comparison .mantine-Combobox-search::placeholder {
color: var(--text-muted) !important;
color: var(--c-text-subtle) !important;
}
/* Style the chevron - ensure proper coloring */
@@ -273,29 +243,49 @@
/* Flash/pulse highlight for navigated change */
@keyframes compare-flash {
0% {
outline: 4px solid rgba(255, 235, 59, 0);
box-shadow: 0 0 0 rgba(255, 235, 59, 0);
background-color: rgba(255, 235, 59, 0.2) !important;
outline: 4px solid color-mix(in srgb, var(--c-highlight) 0%, transparent);
box-shadow: 0 0 0 color-mix(in srgb, var(--c-highlight) 0%, transparent);
background-color: color-mix(
in srgb,
var(--c-highlight) 20%,
transparent
) !important;
}
25% {
outline: 4px solid rgba(255, 235, 59, 1);
box-shadow: 0 0 20px rgba(255, 235, 59, 0.8);
background-color: rgba(255, 235, 59, 0.4) !important;
outline: 4px solid color-mix(in srgb, var(--c-highlight) 100%, transparent);
box-shadow: 0 0 20px color-mix(in srgb, var(--c-highlight) 80%, transparent);
background-color: color-mix(
in srgb,
var(--c-highlight) 40%,
transparent
) !important;
}
50% {
outline: 4px solid rgba(255, 235, 59, 1);
box-shadow: 0 0 30px rgba(255, 235, 59, 0.9);
background-color: rgba(255, 235, 59, 0.5) !important;
outline: 4px solid color-mix(in srgb, var(--c-highlight) 100%, transparent);
box-shadow: 0 0 30px color-mix(in srgb, var(--c-highlight) 90%, transparent);
background-color: color-mix(
in srgb,
var(--c-highlight) 50%,
transparent
) !important;
}
75% {
outline: 4px solid rgba(255, 235, 59, 0.8);
box-shadow: 0 0 15px rgba(255, 235, 59, 0.6);
background-color: rgba(255, 235, 59, 0.3) !important;
outline: 4px solid color-mix(in srgb, var(--c-highlight) 80%, transparent);
box-shadow: 0 0 15px color-mix(in srgb, var(--c-highlight) 60%, transparent);
background-color: color-mix(
in srgb,
var(--c-highlight) 30%,
transparent
) !important;
}
100% {
outline: 4px solid rgba(255, 235, 59, 0);
box-shadow: 0 0 0 rgba(255, 235, 59, 0);
background-color: rgba(255, 235, 59, 0) !important;
outline: 4px solid color-mix(in srgb, var(--c-highlight) 0%, transparent);
box-shadow: 0 0 0 color-mix(in srgb, var(--c-highlight) 0%, transparent);
background-color: color-mix(
in srgb,
var(--c-highlight) 0%,
transparent
) !important;
}
}
@@ -304,14 +294,18 @@
z-index: 1000;
position: relative;
/* Bonus: temporarily override red/green to yellow during flash for clarity */
background-color: rgba(255, 235, 59, 0.5) !important;
background-color: color-mix(
in srgb,
var(--c-highlight) 50%,
transparent
) !important;
}
/* Union overlay for group flash */
.compare-diff-flash-overlay {
animation: compare-flash 1.5s ease-in-out 1;
z-index: 999;
background-color: rgba(255, 235, 59, 0.4);
background-color: color-mix(in srgb, var(--c-highlight) 40%, transparent);
pointer-events: none;
border-radius: 2px;
}
@@ -328,7 +322,7 @@
width: 0.75rem;
height: 0.75rem;
border-radius: 999px;
border: 1px solid rgba(15, 23, 42, 0.15);
border: 1px solid var(--c-border);
}
.compare-summary__stats {
@@ -343,10 +337,10 @@
}
.compare-summary__segment {
border: 1px solid var(--mantine-color-gray-3);
border: 1px solid var(--c-border);
border-radius: 0.5rem;
padding: 0.75rem;
background-color: var(--mantine-color-gray-0);
background-color: var(--c-surface);
}
.compare-diff-page {
@@ -357,10 +351,10 @@
.compare-diff-page__canvas {
position: relative;
border: 1px solid var(--border-strong);
border: 1px solid var(--c-border-strong);
border-radius: 0.75rem;
overflow: hidden;
background-color: var(--bg-surface);
background-color: var(--c-surface);
width: 100%;
}
@@ -381,7 +375,7 @@
margin-right: auto;
max-width: 100%;
background-color: #fff; /* ensure stable white backing during load */
border: 1px solid var(--border-subtle);
border: 1px solid var(--c-border-subtle);
will-change: transform;
}
@@ -403,7 +397,7 @@
display: inline-block;
padding: 2px 8px;
border-radius: 8px;
background-color: var(--compare-page-label-bg);
background-color: var(--c-surface-sunken);
color: var(--compare-page-label-fg);
}
@@ -432,7 +426,7 @@
}
.compare-dropdown-option__page {
font-size: 0.7rem;
color: var(--text-muted);
color: var(--c-text-subtle);
}
.compare-dropdown-option__text {
display: -webkit-box;
@@ -446,11 +440,11 @@
/* Non-sticky in-flow group headers; sticky handled by floating header */
.compare-dropdown-group {
position: static;
background: var(--compare-page-label-bg);
background: var(--c-surface-sunken);
color: var(--compare-page-label-fg);
font-size: 0.75rem;
padding: 0.25rem 0.5rem;
border-bottom: 1px solid var(--border-subtle);
border-bottom: 1px solid var(--c-border-subtle);
}
.compare-dropdown-group.compare-dropdown-group--hidden {
@@ -461,15 +455,9 @@
overflow: hidden;
}
[data-mantine-color-scheme="dark"] .compare-dropdown-group {
background: var(--compare-page-label-bg);
color: var(--compare-page-label-fg);
border-bottom: 1px solid var(--border-default);
}
/* Light grey rendering flag next to page labels in the dropdown */
.compare-dropdown-rendering-flag {
color: var(--text-muted);
color: var(--c-text-subtle);
margin-left: 0.25rem;
}
@@ -489,7 +477,7 @@
position: sticky;
top: 0;
z-index: 2;
background: var(--bg-background);
background: var(--c-bg);
padding: 0.25rem 0;
}
@@ -550,7 +538,7 @@
padding-bottom: 32px;
}
.compare-pixel-page {
border: 1px solid var(--mantine-color-default-border, #dee2e6);
border: 1px solid var(--mantine-color-default-border, var(--c-border));
border-radius: 8px;
padding: 12px;
background: var(--mantine-color-body, #fff);
@@ -568,13 +556,13 @@
}
.compare-pixel-triptych figure img {
display: block;
border: 1px solid var(--mantine-color-default-border, #dee2e6);
border: 1px solid var(--mantine-color-default-border, var(--c-border));
background: #fff;
}
.compare-pixel-triptych figcaption {
font-size: 11px;
text-align: center;
color: var(--mantine-color-dimmed, #868e96);
color: var(--mantine-color-dimmed, var(--c-text-muted));
}
.compare-pixel-overlay {
position: relative;
@@ -583,7 +571,7 @@
.compare-pixel-overlay-img {
display: block;
width: 100%;
border: 1px solid var(--mantine-color-default-border, #dee2e6);
border: 1px solid var(--mantine-color-default-border, var(--c-border));
}
.compare-pixel-overlay-top {
position: absolute;
@@ -104,7 +104,7 @@ const GroupedFormatDropdown = ({
cursor: disabled ? "not-allowed" : "pointer",
width: "100%",
color: disabled
? "var(--dropdown-trigger-text-disabled)"
? "var(--c-text-subtle)"
: "var(--dropdown-trigger-text)",
}}
>
@@ -219,8 +219,9 @@ export default function BookmarkEditor({
withBorder
p="md"
style={{
borderColor: "var(--border-default)",
background: level === 0 ? "var(--bg-surface)" : "var(--bg-muted)",
borderColor: "var(--c-border)",
background:
level === 0 ? "var(--c-surface)" : "var(--c-surface-sunken)",
}}
>
<Stack gap="sm">
@@ -380,7 +381,7 @@ export default function BookmarkEditor({
<Stack
gap="sm"
pl="lg"
style={{ borderLeft: "1px solid var(--border-default)" }}
style={{ borderLeft: "1px solid var(--c-border)" }}
>
{bookmark.children.map((child) => (
<Fragment key={child.id}>
@@ -100,7 +100,7 @@ const EditTableOfContentsWorkbenchView = ({
width: "100%",
height: "100%",
overflowY: "auto",
background: "var(--bg-raised)",
background: "var(--c-surface-raised)",
}}
>
<Stack gap="xl" maw={1200} mx="auto">
@@ -121,8 +121,8 @@ const EditTableOfContentsWorkbenchView = ({
radius="md"
p="xl"
style={{
backgroundColor: "var(--bg-surface)",
borderColor: "var(--border-default)",
backgroundColor: "var(--c-surface)",
borderColor: "var(--c-border)",
boxShadow: "var(--shadow-md)",
}}
>
@@ -167,8 +167,8 @@ const EditTableOfContentsWorkbenchView = ({
radius="md"
p="xl"
style={{
backgroundColor: "var(--bg-surface)",
borderColor: "var(--border-default)",
backgroundColor: "var(--c-surface)",
borderColor: "var(--c-border)",
boxShadow: "var(--shadow-md)",
}}
>
@@ -125,8 +125,8 @@ const PerPageSection: React.FC<PerPageSectionProps> = ({
<Accordion.Panel>
<div
style={{
backgroundColor: "var(--bg-raised)",
color: "var(--text-primary)",
backgroundColor: "var(--c-surface-raised)",
color: "var(--c-text)",
borderRadius: 8,
padding: 12,
}}
@@ -32,8 +32,8 @@ const ScrollableCodeBlock: React.FC<ScrollableCodeBlockProps> = ({
block
style={{
whiteSpace: "pre-wrap",
backgroundColor: "var(--bg-raised)",
color: "var(--text-primary)",
backgroundColor: "var(--c-surface-raised)",
color: "var(--c-text)",
maxHeight,
overflowY: "auto",
}}
@@ -5,7 +5,7 @@ type AccordionStyles = Partial<Record<AccordionStylesNames, CSSProperties>>;
export const pdfInfoAccordionStyles: AccordionStyles = {
item: {
backgroundColor: "var(--accordion-item-bg)",
backgroundColor: "var(--c-surface-raised)",
},
control: {
backgroundColor: "transparent",
@@ -5,9 +5,9 @@
flex-direction: row;
align-items: center; /* Center align items vertically */
height: 32px;
border: 1px solid var(--border-default);
background-color: var(--mantine-color-white); /* Use Mantine color variable */
color: var(--text-secondary);
border: 1px solid var(--c-border);
background-color: var(--c-input-bg);
color: var(--c-text-muted);
border-radius: var(--radius-sm);
padding: 4px 8px;
font-size: 13px;
@@ -15,25 +15,9 @@
transition: all 0.2s ease;
}
/* Dark mode background */
[data-mantine-color-scheme="dark"] .languagePicker {
background-color: var(
--mantine-color-dark-6
); /* Use Mantine dark color instead of hardcoded */
}
.languagePicker:hover {
border-color: var(--border-strong);
background-color: var(
--mantine-color-gray-0
); /* Light gray on hover for light mode */
}
/* Dark mode hover */
[data-mantine-color-scheme="dark"] .languagePicker:hover {
background-color: var(
--mantine-color-dark-5
); /* Use Mantine color variable */
border-color: var(--c-border-strong);
background-color: var(--c-hover);
}
.languagePicker:disabled {
@@ -43,30 +27,25 @@
.languagePickerIcon {
font-size: 16px;
color: var(--text-muted);
color: var(--c-text-subtle);
margin-left: auto;
display: flex;
align-items: center; /* Center the icon vertically */
}
.languagePickerDropdown {
background-color: var(--mantine-color-white); /* Use Mantine color variable */
border: 1px solid var(--border-default);
background-color: var(--c-surface);
border: 1px solid var(--c-border);
border-radius: var(--radius-sm);
padding: 4px;
}
/* Dark mode dropdown background */
[data-mantine-color-scheme="dark"] .languagePickerDropdown {
background-color: var(--mantine-color-dark-6);
}
.languagePickerOption {
padding: 6px 10px;
cursor: pointer;
border-radius: var(--radius-xs);
font-size: 13px;
color: var(--text-primary);
color: var(--c-text);
transition: background-color 0.2s ease;
}
@@ -81,14 +60,7 @@
}
.languagePickerOption:hover {
background-color: var(
--mantine-color-gray-0
); /* Light gray on hover for light mode */
}
/* Dark mode option hover */
[data-mantine-color-scheme="dark"] .languagePickerOption:hover {
background-color: var(--mantine-color-dark-5);
background-color: var(--c-hover);
}
/* Additional helper classes for the component */
@@ -110,7 +82,7 @@
.languagePickerScrollArea {
max-height: 180px;
border-bottom: 1px solid var(--border-default);
border-bottom: 1px solid var(--c-border);
padding-bottom: 8px;
}
@@ -121,12 +93,7 @@
}
.languagePickerLink {
color: var(--mantine-color-blue-6);
color: var(--c-accent-fg);
text-decoration: underline;
cursor: pointer;
}
/* Dark mode link */
[data-mantine-color-scheme="dark"] .languagePickerLink {
color: var(--mantine-color-blue-4);
}
@@ -158,7 +158,7 @@ const LanguagePicker: React.FC<LanguagePickerProps> = ({
<Text
size="xs"
style={{
color: "#3b82f6",
color: "var(--c-primary)",
cursor: "pointer",
textDecoration: "underline",
textAlign: "center",

Some files were not shown because too many files have changed in this diff Show More