Files
Will Miao eebdfcb0b6 fix(widgets): theme ComfyUI node widgets from palette tokens
LM widgets were painted with a fixed dark palette: translucent slate fills
plus light text. On the dark palettes those values happened to blend into the
node, but on the Light palette the translucency composited into grey slabs and
light text landed on light surfaces. The prompt textarea was the clearest
case: it used the fixed --color-charcoal-400 scale token, which never flips
with the theme, so it stayed charcoal inside a white node.

Add web/comfyui/lm_theme.css as the single colour layer. Every value aliases the
active ComfyUI palette (--comfy-input-bg, --component-node-*, --comfy-menu-bg,
...) and derives raised/hover/selected surfaces with color-mix, so built-in and
user-imported palettes are covered without per-theme rules. Contexts select the
token family the neighbouring built-in widget uses: lm-widget-root for canvas
DOM widgets, lm-vue-node for the Vue node renderer, lm-popover for menus
appended to <body>. Only status colours, shadows and scrims keep a light/dark
variant. Whole-node containers no longer paint a background, so the node
surface shows through and only inputs, cards and buttons are filled.

lm_styles.css, the canvas widget JS (autocomplete dropdown, tag chips, group
editor) and all Vue SFC styles now consume these tokens; tag category badges
derive their hue from oklch tokens so the tint and text contrast flip with the
theme. Empty states get an explicit 12px size: the canvas DOM overlay inherits
the 16px body size while Vue nodes render at text-xs, which made the
placeholders oversized and heavy in canvas mode.

A regression test rejects new colour literals outside the preview-image
overlays, and the DOM widget guide documents the token map and the
blend-don't-overlay rule. Rebuilt the committed vue-widgets bundle.
2026-10-30 09:48:29 +08:00

126 lines
4.8 KiB
CSS

/* LoRA Manager widget palette.
*
* Every value aliases a ComfyUI theme token (or is derived from one with
* color-mix), so all built-in palettes and user-imported custom palettes are
* handled without per-theme rules. Only genuinely "directional" values
* (status colors, shadows, scrims) have a light and a dark variant, keyed on
* the `.dark-theme` class ComfyUI puts on <html>.
*
* Three contexts:
* .lm-widget-root inline node widget (canvas DOM widget or Vue node)
* .lm-vue-node same, rendered by ComfyUI's Vue node renderer; built-in
* widgets there use the --component-node-* token family
* .lm-popover menus / dropdowns appended to <body>
*
* Never style an LM widget with --color-* scale tokens (e.g.
* --color-charcoal-400): those are fixed values that do not flip with the
* theme.
*/
/* :root carries the token defaults so LM can also style ComfyUI-owned rows
(e.g. the .lm-flash value highlight) that live outside LM containers. */
:root,
.lm-widget-root,
.lm-popover {
/* Surfaces */
--lm-surface: var(--comfy-input-bg, #222);
/* Raised/hovered steps always move away from the surface towards the text
colour, so they read as "more contrast" in both directions. */
--lm-surface-raised: color-mix(in srgb, var(--lm-surface) 94%, var(--lm-fg));
--lm-surface-hover: color-mix(in srgb, var(--lm-surface) 86%, var(--lm-fg));
--lm-surface-selected: color-mix(in srgb, var(--lm-accent) 22%, var(--lm-surface));
/* Lines */
--lm-border: var(--border-color, #4e4e4e);
--lm-border-soft: color-mix(in srgb, var(--lm-border) 45%, transparent);
/* Text */
--lm-fg: var(--input-text, #ddd);
--lm-fg-muted: var(--descrip-text, #999);
--lm-fg-subtle: color-mix(in srgb, var(--lm-fg) 60%, transparent);
/* Brand accent */
--lm-accent: #4299e1;
--lm-accent-text: color-mix(in oklab, var(--lm-accent) 60%, var(--lm-fg));
--lm-accent-soft: color-mix(in srgb, var(--lm-accent) 18%, transparent);
--lm-accent-track: color-mix(in srgb, var(--lm-accent) 15%, transparent);
--lm-on-accent: #fff;
--lm-accent-fill: color-mix(in srgb, var(--lm-accent) 65%, transparent);
--lm-accent-border: color-mix(in srgb, var(--lm-accent) 55%, transparent);
/* Status */
--lm-danger: var(--error-text, #f44);
--lm-danger-text: color-mix(in oklab, var(--lm-danger) 60%, var(--lm-fg));
--lm-danger-soft: color-mix(in srgb, var(--lm-danger) 12%, transparent);
--lm-danger-border: color-mix(in srgb, var(--lm-danger) 45%, transparent);
--lm-warning: #8a5a00;
--lm-warning-soft: color-mix(in srgb, #f0b429 20%, transparent);
--lm-warning-border: color-mix(in srgb, #f0b429 55%, transparent);
--lm-ok: #0f7b3f;
/* Depth */
--lm-shadow: 0 2px 10px rgb(0 0 0 / 0.12);
--lm-shadow-lg: 0 10px 25px rgb(0 0 0 / 0.18);
--lm-scrim: rgb(0 0 0 / 0.45);
/* Autocomplete tag category hues */
--lm-cat-general: oklch(0.62 0.13 240);
--lm-cat-artist: oklch(0.65 0.14 20);
--lm-cat-copyright: oklch(0.62 0.15 300);
--lm-cat-character: oklch(0.63 0.14 145);
--lm-cat-meta: oklch(0.7 0.12 85);
--lm-cat-species: oklch(0.66 0.14 45);
--lm-cat-lore: oklch(0.64 0.11 180);
}
/* Scoped to LM surfaces: native scrollbars, <select> popups and range inputs
then follow the active palette without touching ComfyUI's own chrome. */
.lm-widget-root,
.lm-popover {
color-scheme: light;
}
/* ComfyUI Vue node mode: match the built-in widget token family. */
.lm-widget-root.lm-vue-node {
--lm-surface: var(--component-node-widget-background, var(--comfy-input-bg, #222));
--lm-fg: var(--component-node-foreground, var(--input-text, #ddd));
--lm-fg-muted: var(--component-node-foreground-secondary, var(--descrip-text, #999));
--lm-border: var(--component-node-border, var(--border-color, #4e4e4e));
}
/* Menus and dropdowns live outside the node, over the app chrome. */
.lm-popover {
--lm-surface: var(--comfy-menu-bg, #1a1a1a);
--lm-surface-raised: var(--comfy-menu-secondary-bg, #292929);
--lm-surface-hover: var(--comfy-menu-secondary-bg, #292929);
--lm-fg: var(--fg-color, #fff);
--lm-fg-muted: var(--descrip-text, #999);
--lm-border: var(--border-color, #4e4e4e);
}
/* Derived per-context values: declared on the elements that carry the context
tokens so they pick up the palette/renderer-specific --lm-warning / --lm-ok
instead of the :root defaults. */
.lm-widget-root {
--lm-warning-progress: linear-gradient(
90deg,
var(--lm-warning),
color-mix(in srgb, var(--lm-warning) 60%, #fff)
);
--lm-ok-progress: linear-gradient(
90deg,
var(--lm-ok),
color-mix(in srgb, var(--lm-ok) 60%, #fff)
);
}
html.dark-theme .lm-widget-root,
html.dark-theme .lm-popover {
--lm-warning: #f0b429;
--lm-ok: #4ade80;
--lm-shadow: 0 2px 10px rgb(0 0 0 / 0.45);
--lm-shadow-lg: 0 10px 25px rgb(0 0 0 / 0.55);
--lm-scrim: rgb(0 0 0 / 0.6);
color-scheme: dark;
}