Skip to content

Styling UI ​

CSS Classes ​

Core UI Wrapper ​

  • .pivotick – The main container for the pivotick UI.

    • .pvt-mainheader – Top bar (search / filter / notes).
    • .pvt-sidebar – Left panel for the current selection (properties, facets, neighbours) and bulk actions.
    • .pvt-canvas – The area where the graph is rendered.
    • .pvt-moderail – Left mode rail (Select / Create / View / Physics).
    • .pvt-toolpanel – Contextual tool panel for the active mode.
    • .pvt-flyout – Slot holding the settings flyouts; each panel is a .pvt-flyout-panel (.pvt-flyout-view for grid / canvas switches, .pvt-flyout-physics for layout and simulation).
    • .pvt-graphnavigation – Viewport controls (fit-and-center, zoom, settings, fullscreen).
  • .pvt-tooltip – Tooltip container for nodes or edges.

  • .pvt-contextmenu – Context menu container for right-click actions.

Node Classes ​

  • .pvt-node – Individual nodes in the graph.

Edge Classes ​

  • .pvt-edge-group – Group container for edges.
    • path – The SVG path representing the edge.
    • .label-container – Container for the edge label.

TIP

These are the core classes you can override. To see all available classes, just open your browser's developer tools and inspect the element.

CSS Variables ​

Another easy way to style Pivotick is to override the default CSS variables. You can find them all here.

View all variables
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: unset,
    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));
}

Main Variables ​

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",

Node and Edge Variables ​

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),

Example ​

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

Theme ​