跳转到内容

设计用户界面 ​

CSS 课程 ​

核心 UI 包装 ​

  • .pivotick——pivotick UI 的主容器。

    • .pvt-mainheader – 顶栏(搜索/过滤/注释)。
    • .pvt-sidebar – 当前选择(属性、构面、邻居)和批量操作的左侧面板。
    • .pvt-canvas – 渲染图形的区域。
    • .pvt-moderail – 左模式轨(选择/创建/查看/物理)。
    • .pvt-toolpanel – 活动模式的上下文工具面板。
    • .pvt-flyout – 保存设置弹出窗口的插槽;每个面板都是一个.pvt-flyout-panel(.pvt-flyout-view用于网格/画布切换,.pvt-flyout-physics用于布局和模拟)。
    • .pvt-graphnavigation – 视口控件(适合中心、缩放、设置、全屏)。
  • .pvt-tooltip – 节点或边的工具提示容器。

  • .pvt-contextmenu – 用于右键单击操作的上下文菜单容器。

节点类 ​

  • .pvt-node – 图中的各个节点。

边缘类 ​

  • .pvt-edge-group – 边的分组容器。
    • path – 代表边缘的SVG路径。
    • .label-container – 边缘标签的容器。

小费

这些是您可以覆盖的核心类。要查看所有可用的类,只需打开浏览器的开发人员工具并检查该元素即可。

CSS 变量 ​

设置 Pivotick 样式的另一种简单方法是覆盖默认的 CSS 变量。你都可以找到它们here。

查看所有变量
scss
@use "sass:map";
@use 'utils';


//
// LIGHT THEME
//

$light-vibrant-color-palette: (
    vibrant-blue: #007acc,
    vibrant-indigo: #7000cc,
    vibrant-lobster: #f0636a,
    vibrant-green: #81dd18,
    vibrant-amaranth: #eb2e53,
);

$light-bg-color-palette-1: (
    pvt-bg-color-0: #fff,
    pvt-bg-color-1: #fafafa,
    pvt-bg-color-2: #f3f3f3,
    pvt-bg-color-3: #ededed,
    pvt-bg-color-4: #e6e6e6,
    pvt-bg-color-5: #e0e0e0,
    pvt-bg-color-6: #d6d6d6,
    pvt-bg-color-7: #cccccc,
    pvt-bg-color-8: #c2c2c2,
);

$light-theme: (
    // #region core-variables
    pvt-bg: var(--pvt-bg-color-0),
    pvt-ui-bg: var(--pvt-bg-color-0),
    pvt-border-color: var(--pvt-bg-color-7),
    // Floating chrome surface (rails / pills / panels). Kept light here so it
    // doesn't read heavy on the white canvas; see the dark map for the dark tone.
    pvt-chrome-bg: var(--pvt-bg-color-3),
    pvt-chrome-border: #dcdcdc,
    pvt-chrome-shadow: "0 2px 6px rgba(0, 0, 0, 0.10)",

    pvt-theme-primary: map.get($light-vibrant-color-palette, vibrant-amaranth),
    pvt-theme-secondary: #f2f2f2,
    pvt-theme-info: #25B6EB,
    pvt-theme-warning: #F59E0B,
    pvt-theme-danger: #DC2626,
    pvt-theme-success: #16A34A,

    pvt-font-family: "system-ui, sans-serif",
    // #endregion core-variables

    pvt-text-color-0: #fff,
    pvt-text-color-1: #e0e0e0,
    pvt-text-color-2: #bbb,
    pvt-text-color-3: #999,
    pvt-text-color-4: #666,
    pvt-text-color-5: #333,
    pvt-text-color-6: #000,
    pvt-text-color-primary: color-mix(in srgb, var(--pvt-bg-color-primary), black 50%),
    pvt-text-color-secondary: color-mix(in srgb, var(--pvt-bg-color-secondary), black 50%),
    pvt-text-color-info: color-mix(in srgb, var(--pvt-bg-color-info), black 50%),
    pvt-text-color-warning: color-mix(in srgb, var(--pvt-bg-color-warning), black 50%),
    pvt-text-color-danger: color-mix(in srgb, var(--pvt-bg-color-danger), black 50%),
    pvt-text-color-success: color-mix(in srgb, var(--pvt-bg-color-success), black 50%),

    pvt-bg-color-primary: var(--pvt-theme-primary),
    pvt-bg-color-secondary: var(--pvt-theme-secondary),
    pvt-bg-color-info: var(--pvt-theme-info),
    pvt-bg-color-warning: var(--pvt-theme-warning),
    pvt-bg-color-danger: var(--pvt-theme-danger),
    pvt-bg-color-success: var(--pvt-theme-success),
    pvt-text-color-for-primary: #fff,
    pvt-text-color-for-secondary: #000,
    pvt-text-color-for-info: #fff,
    pvt-text-color-for-warning: #fff,
    pvt-text-color-for-danger: #fff,
    pvt-text-color-for-success: #fff,

    pvt-box-shadow-light: "rgba(0, 0, 0, 0.25) 0px 1px 2px 0px",
    pvt-box-shadow-medium: "rgba(0, 0, 0, 0.1) 0px 4px 6px -1px, rgba(0, 0, 0, 0.06) 0px 2px 4px -1px",
    pvt-box-shadow-strong: "rgba(0, 0, 0, 0.16) 0px 3px 6px, rgba(0, 0, 0, 0.23) 0px 3px 6px",
    pvt-box-shadow-color-strong: "#aaaaaa55",

    /* Scrollbar (thumb tracks the ink colour so it adapts to the theme) */
    pvt-scrollbar-size: 10px,
    pvt-scrollbar-track: transparent,
    pvt-scrollbar-thumb: color-mix(in srgb, var(--pvt-text-color-6) 22%, transparent),
    pvt-scrollbar-thumb-hover: color-mix(in srgb, var(--pvt-text-color-6) 40%, transparent),


    /* Sidebar */
    pvt-sidebar-bg: var(--pvt-bg-color-0),
    pvt-sidebar-mainpanelheader-bg: var(--pvt-bg-color-1),
    pvt-sidebar-panelheader-bg: var(--pvt-bg-color-4),
    pvt-sidebar-panelbody-bg: var(--pvt-bg-color-0),
    pvt-sidebar-panel-border: 1px solid var(--pvt-text-color-1),
    pvt-sidebar-subpanelheader-bg: var(--pvt-bg-color-2),
    pvt-mainheader-nodepreview-bg: var(--pvt-bg-color-0),
    pvt-sidebar-layout-shadow-right: "rgba(0, 0, 0, 0.07) 2px 0px 3px 0px, rgba(0, 0, 0, 0.04) 3px 0px 2px 0px",
    pvt-sidebar-layout-shadow-left: "rgba(0, 0, 0, 0.07) -2px 0px 3px 0px, rgba(0, 0, 0, 0.04) -3px 0px 2px 0px",
    pvt-sidebar-layout-shadow-bottom: "rgba(0, 0, 0, 0.07) 3px 2px 3px 0px, rgba(0, 0, 0, 0.04) 2px 3px 2px 0px",
    pvt-sidebar-layout-shadow-top: "rgba(0, 0, 0, 0.07) 3px -2px 3px 0px, rgba(0, 0, 0, 0.04) 2px -3px 2px 0px",
    pvt-sidebar-collapse-bg: var(--pvt-bg),
    pvt-sidebar-collapse-color: var(--pvt-text-color-4),
    pvt-sidebar-collapse-border-color: var(--pvt-border-color),
    pvt-sidebar-collapse-shadow: var(--pvt-box-shadow-strong),

    /* Tooltip */
    pvt-tooltip-floating-bg-color: var(--pvt-bg-color-6),
    pvt-tooltip-property-list: var(--pvt-bg-color-2),

    /* Menu */
    pvt-menu-bg-color: var(--pvt-bg-color-1),
    pvt-menu-border-color: var(--pvt-text-color-1),
    pvt-menu-shadow: var(--pvt-sidebar-layout-shadow-right),
    pvt-action-container-shadow: "inset 0 1px 2px rgba(255, 255, 255, 0.12), inset 0 -1px 2px rgba(0, 0, 0, 0.18), rgba(0, 0, 0, 0.09) 0 -1px 2px",

    /* Modal */
    pvt-modal-bg: var(--pvt-bg-color-0),
    pvt-modal-text-color: var(--pvt-text-color-5),
    pvt-modal-overlay-bg: rgba(0, 0, 0, 0.45),
    pvt-modal-shadow: "0 20px 40px rgba(0, 0, 0, 0.25), 0 4px 12px rgba(0, 0, 0, 0.15)",


    /* Graph notification */
    pvt-toast-bg-color-primary: color-mix(in srgb, var(--pvt-bg-color-primary) 40%, white),
    pvt-toast-bg-color-secondary: color-mix(in srgb, var(--pvt-bg-color-secondary) 40%, white),
    pvt-toast-bg-color-info: color-mix(in srgb, var(--pvt-bg-color-info) 40%, white),
    pvt-toast-bg-color-warning: color-mix(in srgb, var(--pvt-bg-color-warning) 40%, white),
    pvt-toast-bg-color-danger: color-mix(in srgb, var(--pvt-bg-color-danger) 40%, white),
    pvt-toast-bg-color-success: color-mix(in srgb, var(--pvt-bg-color-success) 40%, white),

    pvt-toast-text-color-primary: color-mix(in srgb, var(--pvt-bg-color-primary) 70%, black),
    pvt-toast-text-color-secondary: color-mix(in srgb, var(--pvt-bg-color-secondary) 70%, black),
    pvt-toast-text-color-info: color-mix(in srgb, var(--pvt-bg-color-info) 70%, black),
    pvt-toast-text-color-warning: color-mix(in srgb, var(--pvt-bg-color-warning) 70%, black),
    pvt-toast-text-color-danger: color-mix(in srgb, var(--pvt-bg-color-danger) 70%, black),
    pvt-toast-text-color-success: color-mix(in srgb, var(--pvt-bg-color-success) 70%, black),

    /* Graph canvas */
    // #region canvas
    pvt-graph-grid-color: var(--pvt-bg-color-2),
    pvt-graph-navigation-control-container-shadow:
        "rgba(0, 0, 0, 0.22) 0px 0px 4px,"
        "rgba(0, 0, 0, 0.30) 0px 1px 2px",
    // #endregion canvas

    pvt-selection-color: map.get($light-vibrant-color-palette, vibrant-lobster),
    pvt-selection-stroke-color: map.get($light-vibrant-color-palette, vibrant-lobster),
    pvt-selection-rectangle-fill: color-mix(in srgb, var(--pvt-selection-color) 20%, #ffffff00),
    pvt-selection-rectangle-stroke: color-mix(in srgb, var(--pvt-selection-color) 80%, #ffffff00),

    // #region node
    /* Node defaults */
    pvt-node-color: map.get($light-vibrant-color-palette, vibrant-blue),
    pvt-node-stroke: #fff,
    pvt-node-stroke-width: 2,
    pvt-node-text-color: #fff,
    pvt-node-icon-font-family: '"Font Awesome 7 Free"',
    pvt-node-icon-font-weight: 900,

    /* Node rim badges */
    pvt-badge-color: map.get($light-vibrant-color-palette, vibrant-lobster),
    pvt-badge-text-color: #fff,
    pvt-badge-stroke: var(--pvt-bg),
    // Mid-slate in both themes: the secondary background is near-white in light mode, which
    // leaves a white-on-white badge. Neutral chrome, so it borrows no single badge's colour.
    pvt-badge-overflow-color: #64748b,

    // How much thicker than its own border a node's or edge's state ring is drawn. The
    // widths below are floors, not fixed sizes: a stroke already thicker than the floor
    // grows by this instead, so decorating it can never make it thinner.
    pvt-state-stroke-boost: 1,

    /* Node selected state */
    pvt-node-selected-color: map.get($light-vibrant-color-palette, vibrant-lobster),
    pvt-node-selected-stroke: var(--pvt-selection-stroke-color),
    // The pulse animates stroke-width 3 -> 5 -> 3 and an animated declaration wins, so this
    // only matters where the animation is off. Match its resting frame.
    pvt-node-selected-stroke-width: 3,
    pvt-node-selected-stroke-opacity: 1,
    pvt-node-selected-filter: drop-shadow(0 0 2px var(--pvt-node-selected-color)),
    pvt-node-selected-animation: glowPulseSelected 1.5s infinite,
    pvt-node-selected-big-animation: glowPulseBigSelected 1.5s infinite,

    /* Node highlighted state */
    pvt-node-highlighted-stroke: map.get($light-vibrant-color-palette, vibrant-blue),
    pvt-node-highlighted-stroke-width: 2,
    pvt-node-highlighted-filter: drop-shadow(0 0 2px var(--pvt-node-highlighted-stroke)),
    pvt-node-highlighted-opacity: 1,
    pvt-node-highlighted-animation: glowPulseHighlighted 1.5s infinite,
    // #endregion node

    /* Node cluster */
    pvt-node-dash-stroke: #333,
    pvt-node-cluster-fill: #00000010,
    pvt-node-cluster-stroke: color-mix(in srgb, var(--pvt-node-dash-stroke) 60%, #fff),

    // #region edge
    /* Edge defaults */
    pvt-edge-stroke: color-mix(in srgb, map.get($light-vibrant-color-palette, vibrant-blue) 50%, #fff),
    pvt-edge-stroke-width: 2,
    pvt-edge-stroke-opacity: 1,
    pvt-edge-stroke-dasharray: "6, 4",
    pvt-edge-animation: dashmove-edge 1s linear infinite,
    pvt-edge-filter: none,
    pvt-edge-label-bg: var(--pvt-bg-color-4),
    pvt-edge-label-font-family: "system-ui, sans-serif",
    pvt-edge-label-font-size: 12px,
    pvt-edge-label-color: var(--pvt-text-color-6),
    pvt-edge-label-stroke: none,
    pvt-edge-label-stroke-width: 1,

    /* Edge highlighted state — its own, because a node's highlight width is animated and an
       edge's is not: a 0 there is overridden on a node and simply erases an edge. */
    pvt-edge-highlighted-stroke: var(--pvt-node-highlighted-stroke),
    pvt-edge-highlighted-stroke-width: 3,
    pvt-edge-highlighted-filter: drop-shadow(0 0 2px var(--pvt-edge-highlighted-stroke)),

    /* Edge selected state */
    pvt-edge-selected-stroke: var(--pvt-selection-color),
    pvt-edge-selected-stroke-width: 5,
    pvt-edge-selected-stroke-opacity: 0.8,
    pvt-edge-selected-filter: drop-shadow(0 0 1px var(--pvt-selection-color)),
    pvt-edge-label-selected-stroke: var(--pvt-selection-color),
    pvt-edge-label-selected-stroke-width: 2,
    pvt-edge-label-selected-color: var(--pvt-edge-label-color),
    pvt-edge-label-selected-font-size: var(--pvt-edge-label-font-size),
    pvt-edge-label-selected-bg: var(--pvt-bg-color-4),
    // #endregion edge

    /* Note */
    pvt-note-node-reference-dot-default: #007acc,
    pvt-note-node-reference-text-color: #333,
    // Terminal note surface — a neutral, theme-aware panel (light canvas: white
    // card, dark ink). The colour is only an accent (top hairline + prompt); the
    // jewel surface needs no vars (it is always a deep card).
    pvt-note-term-bg: #ffffff,
    pvt-note-term-border: #e6e8ec,
    pvt-note-term-text: #1c2026,
    pvt-note-term-text-dim: #61666e,

    /* Component */
    pvt-inline-bar-bg: var(--pvt-bg-color-2),
    pvt-inline-bar-fill-bg: "linear-gradient("
        "90deg,"
        "color-mix(in srgb, var(--pvt-bg-color-primary) 20%, #fff) 0%,"
        "var(--pvt-bg-color-primary) 70%,"
        "color-mix(in srgb, var(--pvt-bg-color-primary) 80%, #000) 100%"
    ")",
    pvt-inline-bar-height: 0.5rem,
    pvt-inline-bar-width: 80px,
    pvt-inline-bar-shadow: "color-mix(in srgb, var(--pvt-text-color-6) 15%, transparent) 0px 1px 1px 0px,"
        "color-mix(in srgb, var(--pvt-text-color-6) 10%, transparent) 0px 0px 2px 0px,"
        "inset 0 1px 2px rgba(0, 0, 0, 0.05)",

    pvt-json-background: var(--pvt-bg-color-3),
    pvt-json-border: var(--pvt-bg-color-7),
    pvt-json-key: oklch(0.50 0.15 255),
    pvt-json-string: oklch(0.55 0.17 25),
    pvt-json-number: oklch(0.52 0.14 145),
    pvt-json-boolean: #7a3e9d,
    pvt-json-null: #98a2b3,
    pvt-json-bracket: var(--pvt-text-color-3),
);


//
// DARK THEME
//

$dark-vibrant-color-palette: (
    vibrant-blue: #7EA2FB,
    vibrant-indigo: #A666F4,
    vibrant-lobster: #F2545B,
    vibrant-green: #85CB33,
    vibrant-amaranth: #eb2e53,
);

$dark-bg-color-palette-1: (
    pvt-bg-color-0: #050e15,
    pvt-bg-color-1: #05141f,
    pvt-bg-color-2: #071d2d,
    pvt-bg-color-3: #082032,
    pvt-bg-color-4: #2c394b,
    pvt-bg-color-5: #334756,
    pvt-bg-color-6: #465865,
    pvt-bg-color-7: #576773,
    pvt-bg-color-8: #667580,
    pvt-bg-color-9: #78848d,
);

$dark-bg-color-palette-2: (
    pvt-bg-color-0: #0F0F10,
    pvt-bg-color-1: #131314,
    pvt-bg-color-2: #171719,
    pvt-bg-color-3: #1A1A1D,
    pvt-bg-color-4: #1D1D20,
    pvt-bg-color-5: #202023,
    pvt-bg-color-6: #252528,
    pvt-bg-color-7: #343437,
    pvt-bg-color-8: #464649,
    pvt-bg-color-9: #565658,
);

$dark-bg-color-palette-3: (
    pvt-bg-color-0: hsl(240, 0%, 6%),
    pvt-bg-color-1: hsl(240, 0%, 8%),
    pvt-bg-color-2: hsl(240, 0%, 9%),
    pvt-bg-color-3: hsl(240, 0%, 11%),
    pvt-bg-color-4: hsl(240, 0%, 12%),
    pvt-bg-color-5: hsl(240, 0%, 13%),
    pvt-bg-color-6: hsl(240, 0%, 15%),
    pvt-bg-color-7: hsl(240, 0%, 21%),
    pvt-bg-color-8: hsl(240, 0%, 28%),
    pvt-bg-color-9: hsl(240, 0%, 35%),
);

$dark-theme: (
    pvt-bg: var(--pvt-bg-color-2),
    // Docked panels (sidebar + slide panels) sit on the chrome tone (bg-6);
    // borders track the chrome's border tone (bg-7) so panels read as one family.
    pvt-ui-bg: var(--pvt-bg-color-6),
    pvt-border-color: var(--pvt-bg-color-7),
    // Floating chrome surface (rails / pills / panels) — the graphite tone.
    pvt-chrome-bg: var(--pvt-bg-color-6),
    pvt-chrome-border: var(--pvt-bg-color-7),
    pvt-chrome-shadow: "0 3px 8px rgba(0, 0, 0, 0.30)",

    // pvt-theme-primary: #730ff0,
    pvt-theme-primary: map.get($dark-vibrant-color-palette, vibrant-amaranth),
    pvt-theme-secondary: #666666,
    pvt-theme-info: #2E67F8,
    pvt-theme-warning: #ff8000,
    pvt-theme-danger: #EB212E,
    pvt-theme-success: #22b91a,

    pvt-font-family: "system-ui, sans-serif",

    pvt-text-color-0: #000,
    pvt-text-color-1: #333,
    pvt-text-color-2: #666,
    pvt-text-color-3: #999,
    pvt-text-color-4: #bbb,
    pvt-text-color-5: #e0e0e0,
    pvt-text-color-6: #fff,
    pvt-text-color-primary: color-mix(in srgb, var(--pvt-bg-color-primary), white 50%),
    pvt-text-color-secondary: color-mix(in srgb, var(--pvt-bg-color-secondary), white 50%),
    pvt-text-color-info: color-mix(in srgb, var(--pvt-bg-color-info), white 50%),
    pvt-text-color-warning: color-mix(in srgb, var(--pvt-bg-color-warning), white 50%),
    pvt-text-color-danger: color-mix(in srgb, var(--pvt-bg-color-danger), white 50%),
    pvt-text-color-success: color-mix(in srgb, var(--pvt-bg-color-success), white 50%),

    pvt-bg-color-primary: var(--pvt-theme-primary),
    pvt-bg-color-secondary: var(--pvt-theme-secondary),
    pvt-bg-color-info: var(--pvt-theme-info),
    pvt-bg-color-warning: var(--pvt-theme-warning),
    pvt-bg-color-danger: var(--pvt-theme-danger),
    pvt-bg-color-success: var(--pvt-theme-success),
    pvt-text-color-for-primary: #fff,
    pvt-text-color-for-secondary: #fff,
    pvt-text-color-for-info: #fff,
    pvt-text-color-for-warning: #fff,
    pvt-text-color-for-danger: #fff,
    pvt-text-color-for-success: #000,

    pvt-box-shadow-light: "rgba(0, 0, 0, 0.25) 0px 1px 2px 0px",
    pvt-box-shadow-medium: "rgba(0, 0, 0, 0.1) 0px 4px 6px -1px, rgba(0, 0, 0, 0.06) 0px 2px 4px -1px",
    pvt-box-shadow-strong: "rgba(0, 0, 0, 0.16) 0px 3px 6px, rgba(0, 0, 0, 0.23) 0px 3px 6px",
    pvt-box-shadow-color-strong: "#00000055",

    /* Scrollbar (thumb tracks the ink colour so it adapts to the theme) */
    pvt-scrollbar-size: 10px,
    pvt-scrollbar-track: transparent,
    pvt-scrollbar-thumb: color-mix(in srgb, var(--pvt-text-color-6) 18%, transparent),
    pvt-scrollbar-thumb-hover: color-mix(in srgb, var(--pvt-text-color-6) 34%, transparent),


    /* Sidebar */
    pvt-sidebar-mainpanelheader-bg: transparent,
    pvt-sidebar-panelheader-bg: var(--pvt-bg-color-4),
    pvt-sidebar-panelbody-bg: var(--pvt-bg-color-0),
    pvt-sidebar-panel-border: 1px solid var(--pvt-text-color-1),
    pvt-sidebar-subpanelheader-bg: var(--pvt-bg-color-2),
    pvt-mainheader-nodepreview-bg: var(--pvt-bg-color-6),
    pvt-sidebar-layout-shadow-right: "rgba(0, 0, 0, 0.07) 2px 0px 3px 0px, rgba(0, 0, 0, 0.04) 3px 0px 2px 0px",
    pvt-sidebar-layout-shadow-left: "rgba(0, 0, 0, 0.07) -2px 0px 3px 0px, rgba(0, 0, 0, 0.04) -3px 0px 2px 0px",
    pvt-sidebar-layout-shadow-bottom: "rgba(0, 0, 0, 0.07) 3px 2px 3px 0px, rgba(0, 0, 0, 0.04) 2px 3px 2px 0px",
    pvt-sidebar-collapse-bg: var(--pvt-bg),
    pvt-sidebar-collapse-color: var(--pvt-text-color-4),
    pvt-sidebar-collapse-border-color: var(--pvt-border-color),
    pvt-sidebar-collapse-shadow: var(--pvt-box-shadow-strong),

    /* Tooltip */
    pvt-tooltip-floating-bg-color: var(--pvt-bg-color-7),
    pvt-tooltip-property-list: var(--pvt-bg-color-6),

    /* Menu */
    // Overlays (menu/tooltip/modal) sit one notch above the chrome tone.
    pvt-menu-bg-color: #2E2E31,
    pvt-menu-border-color: var(--pvt-bg-color-2),
    pvt-menu-shadow: var(--pvt-sidebar-layout-shadow-right),
    pvt-action-container-shadow: "inset 0 1px 2px rgba(255, 255, 255, 0.12), inset 0 -1px 2px rgba(0, 0, 0, 0.18)",

    /* Modal */
    pvt-modal-bg: #2E2E31,
    pvt-modal-text-color: var(--pvt-text-color-5),
    pvt-modal-overlay-bg: rgba(0, 0, 0, 0.45),
    pvt-modal-shadow: "0 20px 40px rgba(0, 0, 0, 0.25), 0 4px 12px rgba(0, 0, 0, 0.15)",

    /* Graph notification */
    pvt-toast-bg-color-primary: color-mix(in srgb, var(--pvt-bg-color-primary) 40%, white),
    pvt-toast-bg-color-secondary: color-mix(in srgb, var(--pvt-bg-color-secondary) 40%, white),
    pvt-toast-bg-color-info: color-mix(in srgb, var(--pvt-bg-color-info) 40%, white),
    pvt-toast-bg-color-warning: color-mix(in srgb, var(--pvt-bg-color-warning) 40%, white),
    pvt-toast-bg-color-danger: color-mix(in srgb, var(--pvt-bg-color-danger) 40%, white),
    pvt-toast-bg-color-success: color-mix(in srgb, var(--pvt-bg-color-success) 40%, white),

    pvt-toast-text-color-primary: color-mix(in srgb, var(--pvt-bg-color-primary) 70%, black),
    pvt-toast-text-color-secondary: color-mix(in srgb, var(--pvt-bg-color-secondary) 70%, black),
    pvt-toast-text-color-info: color-mix(in srgb, var(--pvt-bg-color-info) 70%, black),
    pvt-toast-text-color-warning: color-mix(in srgb, var(--pvt-bg-color-warning) 70%, black),
    pvt-toast-text-color-danger: color-mix(in srgb, var(--pvt-bg-color-danger) 70%, black),
    pvt-toast-text-color-success: color-mix(in srgb, var(--pvt-bg-color-success) 70%, black),

    /* Graph canvas */
    pvt-graph-grid-color: var(--pvt-bg-color-4),
    pvt-graph-navigation-control-container-shadow: "rgba(255, 255, 255, 0.15) 0px 0px 4px"
    "rgba(255, 255, 255, 0.10) 0px 1px 2px",

    pvt-selection-color: map.get($dark-vibrant-color-palette, vibrant-lobster),
    pvt-selection-stroke-color: map.get($dark-vibrant-color-palette, vibrant-lobster),
    pvt-selection-rectangle-fill: color-mix(in srgb, var(--pvt-selection-color) 20%, #ffffff00),
    pvt-selection-rectangle-stroke: color-mix(in srgb, var(--pvt-selection-color) 80%, #ffffff00),


    /* Node defaults */
    pvt-node-color: map.get($dark-vibrant-color-palette, vibrant-blue),
    pvt-node-stroke: #fff,
    pvt-node-stroke-width: 0,
    pvt-node-text-color: #fff,
    pvt-node-icon-font-family: '"Font Awesome 7 Free"',
    pvt-node-icon-font-weight: 900,

    /* Node rim badges */
    pvt-badge-color: map.get($dark-vibrant-color-palette, vibrant-lobster),
    pvt-badge-text-color: #fff,
    pvt-badge-stroke: var(--pvt-bg),
    // Mid-slate in both themes: the secondary background is near-white in light mode, which
    // leaves a white-on-white badge. Neutral chrome, so it borrows no single badge's colour.
    pvt-badge-overflow-color: #64748b,

    // See the light theme's note: the state widths are floors, not fixed sizes.
    pvt-state-stroke-boost: 1,

    /* Node selected state */
    pvt-node-selected-color: var(--pvt-selection-color),
    pvt-node-selected-stroke: var(--pvt-selection-stroke-color),
    // The pulse animates stroke-width 3 -> 5 -> 3 and an animated declaration wins, so this
    // only matters where the animation is off. Match its resting frame.
    pvt-node-selected-stroke-width: 3,
    pvt-node-selected-stroke-opacity: 1,
    pvt-node-selected-filter: drop-shadow(0 0 2px var(--pvt-node-selected-color)),
    pvt-node-selected-animation: glowPulseSelected 1.5s infinite,

    /* Node highlighted state */
    pvt-node-highlighted-stroke: map.get($dark-vibrant-color-palette, vibrant-blue),
    pvt-node-highlighted-stroke-width: 0,
    pvt-node-highlighted-filter: drop-shadow(0 0 2px var(--pvt-node-highlighted-stroke)),
    pvt-node-highlighted-opacity: 1,
    pvt-node-highlighted-animation: glowPulseHighlighted 1.5s infinite,

    /* Node cluster */
    pvt-node-dash-stroke: #fff,
    pvt-node-cluster-fill: #ffffff06,
    pvt-node-cluster-stroke: color-mix(in srgb, var(--pvt-node-dash-stroke) 60%, #000),

    /* Edge defaults */
    pvt-edge-stroke: color-mix(in srgb, map.get($dark-vibrant-color-palette, vibrant-blue) 50%, #000),
    pvt-edge-stroke-width: 2,
    pvt-edge-stroke-opacity: 1,
    pvt-edge-stroke-dasharray: "6, 4",
    pvt-edge-animation: dashmove-edge 1s linear infinite,
    pvt-edge-filter: none,
    pvt-edge-label-bg: var(--pvt-bg-color-5),
    pvt-edge-label-font-family: "system-ui, sans-serif",
    pvt-edge-label-font-size: 12px,
    pvt-edge-label-color: var(--pvt-text-color-5),
    pvt-edge-label-stroke: none,
    pvt-edge-label-stroke-width: 1,

    /* Edge highlighted state — see the light theme's note. */
    pvt-edge-highlighted-stroke: var(--pvt-node-highlighted-stroke),
    pvt-edge-highlighted-stroke-width: 3,
    pvt-edge-highlighted-filter: drop-shadow(0 0 2px var(--pvt-edge-highlighted-stroke)),

    /* Edge selected state */
    pvt-edge-selected-stroke: var(--pvt-selection-color),
    pvt-edge-selected-stroke-width: 5,
    pvt-edge-selected-stroke-opacity: 0.8,
    pvt-edge-selected-filter: drop-shadow(0 0 1px var(--pvt-selection-color)),
    pvt-edge-label-selected-stroke: var(--pvt-selection-color),
    pvt-edge-label-selected-stroke-width: 2,
    pvt-edge-label-selected-color:  var(--pvt-text-color-5),
    pvt-edge-label-selected-font-size: var(--pvt-edge-label-font-size),
    pvt-edge-label-selected-bg: var(--pvt-bg-color-5),

    /* Note */
    pvt-note-node-reference-dot-default: #007acc,
    pvt-note-node-reference-text-color: #333,
    // Terminal note surface — a neutral, theme-aware panel (dark canvas: near-
    // black card, light ink). The colour is only an accent (top hairline +
    // prompt); the jewel surface needs no vars (it is always a deep card).
    pvt-note-term-bg: #1a1a1e,
    pvt-note-term-border: #2a2b31,
    pvt-note-term-text: #e8eaed,
    pvt-note-term-text-dim: #a7abb2,

    /* Component */
    pvt-inline-bar-bg: var(--pvt-bg-color-6),
    pvt-inline-bar-fill-bg: "linear-gradient("
    "90deg,"
    "color-mix(in srgb, var(--pvt-bg-color-primary) 90%, #000) 0%,"
    "var(--pvt-bg-color-primary) 60%,"
    "color-mix(in srgb, var(--pvt-bg-color-primary) 60%, #fff) 80%,"
    "color-mix(in srgb, var(--pvt-bg-color-primary) 30%, #fff) 100%"
    ")",
    pvt-inline-bar-height: 0.5rem,
    pvt-inline-bar-width: 80px,
    pvt-inline-bar-shadow: inset 0 1px 2px var(--pvt-bg-color-0),

    pvt-json-background: var(--pvt-bg-color-5),
    pvt-json-border: var(--pvt-bg-color-7),
    pvt-json-key: oklch(0.74 0.10 255),
    pvt-json-string: oklch(0.74 0.11 25),
    pvt-json-number: oklch(0.78 0.13 145),
    pvt-json-boolean: #9564b2,
    pvt-json-null: #98a2b3,
    pvt-json-bracket: var(--pvt-text-color-3),
);


$dark-theme: map.merge($dark-theme,
        $dark-bg-color-palette-2);

@each $name, $value in $dark-vibrant-color-palette {
    $dark-theme: map.merge($dark-theme,
            (pvt-#{$name}: $value));
}


$light-theme: map.merge($light-theme,
        $light-bg-color-palette-1);

@each $name, $value in $light-vibrant-color-palette {
    $light-theme: map.merge($light-theme,
            (pvt-#{$name}: $value));
}

主要变量 ​

scss
pvt-bg: var(--pvt-bg-color-0),
pvt-ui-bg: var(--pvt-bg-color-0),
pvt-border-color: var(--pvt-bg-color-7),
// Floating chrome surface (rails / pills / panels). Kept light here so it
// doesn't read heavy on the white canvas; see the dark map for the dark tone.
pvt-chrome-bg: var(--pvt-bg-color-3),
pvt-chrome-border: #dcdcdc,
pvt-chrome-shadow: "0 2px 6px rgba(0, 0, 0, 0.10)",

pvt-theme-primary: map.get($light-vibrant-color-palette, vibrant-amaranth),
pvt-theme-secondary: #f2f2f2,
pvt-theme-info: #25B6EB,
pvt-theme-warning: #F59E0B,
pvt-theme-danger: #DC2626,
pvt-theme-success: #16A34A,

pvt-font-family: "system-ui, sans-serif",
scss
pvt-graph-grid-color: var(--pvt-bg-color-2),
pvt-graph-navigation-control-container-shadow:
    "rgba(0, 0, 0, 0.22) 0px 0px 4px,"
    "rgba(0, 0, 0, 0.30) 0px 1px 2px",

节点和边变量 ​

scss
/* Node defaults */
pvt-node-color: map.get($light-vibrant-color-palette, vibrant-blue),
pvt-node-stroke: #fff,
pvt-node-stroke-width: 2,
pvt-node-text-color: #fff,
pvt-node-icon-font-family: '"Font Awesome 7 Free"',
pvt-node-icon-font-weight: 900,

/* Node rim badges */
pvt-badge-color: map.get($light-vibrant-color-palette, vibrant-lobster),
pvt-badge-text-color: #fff,
pvt-badge-stroke: var(--pvt-bg),
// Mid-slate in both themes: the secondary background is near-white in light mode, which
// leaves a white-on-white badge. Neutral chrome, so it borrows no single badge's colour.
pvt-badge-overflow-color: #64748b,

// How much thicker than its own border a node's or edge's state ring is drawn. The
// widths below are floors, not fixed sizes: a stroke already thicker than the floor
// grows by this instead, so decorating it can never make it thinner.
pvt-state-stroke-boost: 1,

/* Node selected state */
pvt-node-selected-color: map.get($light-vibrant-color-palette, vibrant-lobster),
pvt-node-selected-stroke: var(--pvt-selection-stroke-color),
// The pulse animates stroke-width 3 -> 5 -> 3 and an animated declaration wins, so this
// only matters where the animation is off. Match its resting frame.
pvt-node-selected-stroke-width: 3,
pvt-node-selected-stroke-opacity: 1,
pvt-node-selected-filter: drop-shadow(0 0 2px var(--pvt-node-selected-color)),
pvt-node-selected-animation: glowPulseSelected 1.5s infinite,
pvt-node-selected-big-animation: glowPulseBigSelected 1.5s infinite,

/* Node highlighted state */
pvt-node-highlighted-stroke: map.get($light-vibrant-color-palette, vibrant-blue),
pvt-node-highlighted-stroke-width: 2,
pvt-node-highlighted-filter: drop-shadow(0 0 2px var(--pvt-node-highlighted-stroke)),
pvt-node-highlighted-opacity: 1,
pvt-node-highlighted-animation: glowPulseHighlighted 1.5s infinite,
scss
/* Edge defaults */
pvt-edge-stroke: color-mix(in srgb, map.get($light-vibrant-color-palette, vibrant-blue) 50%, #fff),
pvt-edge-stroke-width: 2,
pvt-edge-stroke-opacity: 1,
pvt-edge-stroke-dasharray: "6, 4",
pvt-edge-animation: dashmove-edge 1s linear infinite,
pvt-edge-filter: none,
pvt-edge-label-bg: var(--pvt-bg-color-4),
pvt-edge-label-font-family: "system-ui, sans-serif",
pvt-edge-label-font-size: 12px,
pvt-edge-label-color: var(--pvt-text-color-6),
pvt-edge-label-stroke: none,
pvt-edge-label-stroke-width: 1,

/* Edge highlighted state — its own, because a node's highlight width is animated and an
   edge's is not: a 0 there is overridden on a node and simply erases an edge. */
pvt-edge-highlighted-stroke: var(--pvt-node-highlighted-stroke),
pvt-edge-highlighted-stroke-width: 3,
pvt-edge-highlighted-filter: drop-shadow(0 0 2px var(--pvt-edge-highlighted-stroke)),

/* Edge selected state */
pvt-edge-selected-stroke: var(--pvt-selection-color),
pvt-edge-selected-stroke-width: 5,
pvt-edge-selected-stroke-opacity: 0.8,
pvt-edge-selected-filter: drop-shadow(0 0 1px var(--pvt-selection-color)),
pvt-edge-label-selected-stroke: var(--pvt-selection-color),
pvt-edge-label-selected-stroke-width: 2,
pvt-edge-label-selected-color: var(--pvt-edge-label-color),
pvt-edge-label-selected-font-size: var(--pvt-edge-label-font-size),
pvt-edge-label-selected-bg: var(--pvt-bg-color-4),

示例 ​

html
<style>
  :root {
    --pvt-node-color: #FDDA24;
    --pvt-node-stroke: #000000;
    --pvt-edge-stroke: #EF3340;
  }
</style>

主题 ​