Files
browser/platform_web/theme
npc0-hue c959a5bc03 Give the SCUM live map the full width and add catalog icon pickers
- Drop the live-map stat strip and the half-width detail column: the map board now spans the row, is taller, and the selected point renders as a compact strip under it.
- Fit the map filters into a single row with small layer chips, and switch the board between terrain, topo and night base layers plus the extracted building-zone overlay.
- Add a searchable SCUM catalog picker with icons for gift items and event produces, show gift item and vehicle icons in the lists, and keep the picker grid closed until it is opened so no icon is requested up front.
2026-09-16 07:58:48 +08:00
..
2026-08-20 17:05:13 +08:00
2026-07-21 13:44:23 +08:00
2026-07-22 15:27:26 +08:00

platform_web/theme

This directory owns the platform_web visual system. Keep the console in a unified game-operations style with two first-party theme families: the default black mecha console and the optional magical-girl console.

Style Contract

  • The UI is a game operations console, not a landing page and not a generic SaaS dashboard.
  • The default visual language is black mecha: dark cockpit panels, cyan scanner light, angular frame cuts, tactical grid lines, and amber energy accents.
  • The optional magical-girl visual language uses pink moonlight, jelly glass, gold star borders, ribbon glow, and large visible magic-circle motifs.
  • The primary menu is a compact admin sidebar with two states: an expanded text menu and a collapsed icon rail. It must keep the same route order, readable Chinese labels, icon-only tooltips in collapsed mode, and active state framing. In the magical-girl theme it should feel like a pink moonlit operations rail; in the default theme it should feel like a black mecha console rail with restrained scanner accents.
  • Global floating decoration must come from components/MagicalParticleLayer.tsx, using a full-workspace background image layer plus a lightweight global particle DOM layer that reads the active theme variables. Do not add fixed page-local decorative spans or page-local backdrop systems for sparkles, hearts, moons, sigils, or snowflakes.
  • Each theme must have its own visible low-cost ultimate effect: mecha themes use scanner/core/targeting effects, and magical-girl themes use large magic circles, star glints, and ribbon bursts. Do not implement the global effect as dozens of tiny rotating particles.
  • The background desktop is part of the interface. Major surfaces must remain translucent enough for the selected built-in or uploaded background to show through while preserving readable text.
  • Operational clarity wins over decoration. Logs, diffs, forms, errors, warning states, destructive confirmations, LLM review output, and operation/job feedback must stay readable and traceable.
  • Default visual assets must be original CSS/generated motifs. Do not bundle recognizable third-party character art. Users may upload their own backgrounds.

Files

  • tokens.ts: theme palette IDs, built-in desktop preset IDs, CSS variable values, local-storage keys, apply/persist helpers, and the live palette-change event used to keep shell chrome synchronized after theme switches.
  • base.css: shared CSS primitives for the shell, collapsible sidebar navigation, account/profile panel, cards, panels, tables, drawers, dialogs, logs, diffs, plugin groups, command buttons, mecha frame cuts, magical star frames, global background/particle layer placement, and responsive behavior.
  • tokens.test.ts: tests that lock the default black mecha palette, built-in mecha/magical desktop presets, palette-variable cleanup, and theme-change notifications.
  • base-css.test.js: CSS contract tests for shared frame behavior that should not be represented in TypeScript token tests.

Background Rules

  • defaultThemePaletteId must remain mecha-black unless an explicit future product/design decision replaces the visual direction.
  • defaultThemeBackgroundId should point to a built-in mecha desktop preset that works without uploaded imagery.
  • Built-in presets use CSS variables named --workspace-background-pattern-* and render behind the app shell.
  • Uploaded backgrounds use --workspace-background-image, set data-custom-background="true", and take visual precedence over the selected preset.
  • Magical-girl plus uploaded backgrounds must stay restrained: use muted translucent surfaces, low-opacity frame accessories, and reduced pink/gold glow so busy user imagery remains readable instead of becoming a saturated wash.
  • Removing an uploaded background must reveal the selected built-in preset again.
  • Theme palette switches must keep uploaded backgrounds intact. Changing from magical-girl to black mecha, or back again, must not clear --workspace-background-image or alter the custom-background fallback preset.
  • Any new preset must include an id, label, summary, preview, and all required --workspace-background-pattern-* variables. Current presets are 机甲格纳库 and 粉月魔法阵.

Theme Switching Rules

  • applyThemePalette() is the single writer for active palette variables. It must remove the union of known palette variables before applying the selected palette so previous-theme variables, such as magical-girl accessory SVGs, cannot leak into black mecha.
  • After applying a palette, applyThemePalette() must dispatch themePaletteChangeEvent. Shell chrome such as the sidebar subtitle and mini swatch strip should subscribe to that event instead of keeping stale local theme labels.
  • data-theme-palette on the document root is the source of truth for theme-specific CSS selectors. Do not infer the active theme from route state, component-local state, or background preset IDs.
  • Theme changes must be visually atomic: root marker, CSS variables, menu/sidebar chrome, active navigation frame, and shared surface accessories should all reflect the same selected palette immediately.

Typography Rules

  • Theme-level font differences must come from palette variables: --font-ui, --font-display, --font-mono, --title-weight, --title-tracking, --heading-weight, and --heading-tracking.
  • Page and component CSS should not hardcode independent font stacks for headings, code, logs, diffs, or textareas. Use the shared font variables so black mecha and magical-girl can diverge without page-local overrides.
  • Display headings should use --font-display; normal UI copy should inherit --font-ui; code-like surfaces such as logs and diffs should use --font-mono.

Surface Rules

  • Use shared outer surface classes such as metric-card, overview-card, console-panel, catalog-card, server-card, server-detail-header, resource-table-wrap, provider-table-wrap, drawer-panel, confirm-panel, and plugin-group instead of creating page-local card styles.
  • Repeated console content must reuse the internal shared classes: console-module for panel modules, console-stat-strip for compact metric strips, console-row-list and console-row for compact status rows, console-record-list and console-record for operation/history records, console-row-actions for action rows, and access-list / access-list-row for access-management rows.
  • Legacy page-specific classes such as operations-module, operations-pulse-strip, operations-endpoint-row, operation-list, operation-item, maintenance-actions, user-actions, and user-management-item are compatibility aliases only. New UI should not make these classes the style owner; pair or replace them with the shared console-* / access-* classes.
  • Shared framed surfaces should use var(--panel-material), var(--panel-shadow), var(--frame-corner), and var(--frame-accent) so each theme can change structure, fill, and glow style beyond simple color swaps.
  • A visual region should have only one ornamental frame at a hierarchy level. If a .state-view is nested inside a shared framed parent such as .console-panel, .catalog-card, .server-card, .resource-table-wrap, .provider-table-wrap, .server-table-wrap, .plugin-group, .console-record, or compatibility .operation-item, the parent owns the frame and the nested state view must render as transparent, borderless content with no ::before or ::after accessory.
  • Standalone .state-view instances may keep their own readable state treatment when they are not inside an already framed surface.
  • Menu frames should use var(--menu-item-bg), var(--menu-item-active-bg), var(--menu-glyph-bg), var(--menu-title-shadow), and var(--menu-active-outline) so each theme changes active-state treatment, icon material, and rail/sidebar structure.
  • Action dropdowns and contextual menus are small operational overlays, not decorative panels. They must stay anchored to the trigger, fit within the viewport, use compact rows, and avoid moving or resizing the parent card, row, grid, or table.
  • Do not render a dropdown as a tall vertical tower of large command buttons. Do not let a menu cover server metrics, progress bars, titles, status badges, or adjacent cards. If a server/resource has too many runtime actions for a compact menu, route those actions to a grouped drawer, detail page, or command dialog.
  • Menu items may use theme-appropriate icons for recognition, but repeated decorative glyph rails on every action row are forbidden. Icons must clarify action meaning or safety state, not become visual clutter.
  • Shared decoration variables are part of the contract: --frosted-edge, --frosted-surface, --corner-sparkle, --jelly-highlight, --sugar-dust, --crystal-edge-glow, and --jelly-inset. In mecha themes these become scanner/grid/bevel materials; in magical themes they become star, ribbon, and jelly-glass materials.
  • Full-screen ambient motifs use the shared MagicalParticleLayer background layer and global particle DOM layer. They should remain non-interactive, theme-colored, reduced-motion aware, and behind operational surfaces. Page code should not create one-off fixed decoration containers.
  • Keep framed repeated items at 8px radius or less. Pills and circular avatars are allowed for native pill/circle controls.
  • Do not replace shared panels with opaque white cards, heavy dark themes, unthemed gradients, or one-off color systems.
  • Do not use color as the only status signal. Pair color with text or familiar status icons.

Adding UI

  1. Reuse existing shared surface, command, table, form, status, and state-view classes first.
  2. For new panel-internal records, rows, stat strips, and action rows, start from console-record, console-row, console-stat-strip, and console-row-actions before adding a narrowly scoped modifier class.
  3. If a new shared pattern is truly needed, add it in base.css and describe its intended use here.
  4. When placing empty/loading/error states inside an existing shared panel, verify the state view does not introduce a second framed panel or accessory layer.
  5. If a new palette or background preset is added, update tokens.ts, tokens.test.ts, and any CSS contract tests together.
  6. Run the relevant focused checks before claiming completion. Use npm run typecheck, npm test, npm run build, and scripts/check-structure.sh when the scope warrants them.