Colours and backgrounds
Colours
Section titled “Colours”Anywhere a colour is accepted (an item’s color, the palette, a background, an edge style) you can
pass a 0xRRGGBB number or a CSS string: #rrggbb, #rgb, rgb()/rgba() or
hsl()/hsla(), with commas or spaces. Alpha is ignored, because the explorer sets transparency
per layer. Named colours ("tomato") are not supported, and a value that does not parse is ignored
as if it had not been set (parseColour, in ./headless, checks one yourself).
An item’s own color wins over its ramp position, which wins over the stable per-id colour. A
hollow aggregate keeps the palette’s aggregate colour, so it never looks like an item.
Backgrounds
Section titled “Backgrounds”background is drawn in layers, bottom to top: color, then gradient ({ from, to, angle? },
with angle in degrees as in CSS), then image, then the dot grid. Leave it out and you get the
palette’s background with the grid. Like every option, it is live: pass a new value and the canvas
redraws, with no new scene and no camera reset, and values are compared by content, so an inline
object costs nothing on re-render.
colours-and-backgrounds.tsximport { useState } from "react";import { createSceneSource, DataExplorer, type ExplorerBackground } from "@data-xplorer/core";
// An item's own `color` wins over the ramp. Any CSS colour string (or a 0xRRGGBB number) works.const languages: [string, number, string][] = [ ["TypeScript", 48, "#3178c6"], ["Rust", 21, "rgb(222 165 132)"], ["Python", 30, "hsl(50 90% 55%)"], ["Go", 12, "#00add8"], ["Kotlin", 9, "#a97bff"], ["Elixir", 5, "#6e4a7e"],];const source = createSceneSource<null>({ load: () => ({ id: "languages", heading: "Languages by repositories", clusters: [], nodes: languages.map(([name, repos, color]) => ({ id: name, label: name, sublabel: `${repos} repositories`, weight: repos, href: `#${name}`, drillsInto: null, shape: "disc" as const, color, })), edges: [], }),});
// Backgrounds are live: change one and the canvas redraws, with no new scene and no camera reset.const backgrounds: Record<string, ExplorerBackground> = { Plain: {}, Gradient: { gradient: { from: "#0f172a", to: "#3b0764", angle: 135 } }, "No grid": { color: "#111827", grid: false }, "Bright grid": { grid: { alpha: 0.6, color: "#38bdf8" } },};
export default function ColoursAndBackgrounds() { const [name, setName] = useState("Gradient"); return ( <> <div className="demo-toolbar"> {Object.keys(backgrounds).map((key) => ( <button key={key} aria-pressed={name === key} onClick={() => setName(key)}> {key} </button> ))} </div> <DataExplorer source={source} focus={null} background={backgrounds[name]} /> </> );}| Option | Default | Effect |
|---|---|---|
background.color |
palette.background |
A solid colour behind everything. |
background.gradient |
none | { from, to, angle? }: 180 (the default) runs top to bottom, 90 left to right. |
background.image |
none | A URL, or { url, fit?: "cover" | "tile", alpha? }, fixed to the view. It needs CORS. |
background.grid |
on (alpha 0.22, palette.grid) |
false hides it; { alpha, color, step, zoom, minScreenStep, tile } restyles it. See below. |
Your own grid
Section titled “Your own grid”The grid is anchored to the world, so it pans and zooms with the scene. step is the world distance
between repeats (160 by default). zoom: "snap" (the default) keeps the spacing between 36 and 190 px
by stepping it by 4× as you zoom; zoom: "continuous" keeps step fixed, so the pattern scales
smoothly like a baseplate, and fades out once it is under minScreenStep px (8 by default).
To replace the dots, give tile a lazy import, the way you give renderers. The module draws one
repeating tile from the PixiJS classes in kit, in white or greys: color tints it and alpha fades
it. It is drawn once, at 2× resolution.
custom-grid.tsximport { createSceneSource, DataExplorer } from "@data-xplorer/core";
const source = createSceneSource<null>({ load: () => ({ id: "plate", heading: "Plate", clusters: [], nodes: ["Red", "Blue", "Green", "Yellow"].map((name, i) => ({ id: name, label: name, weight: 3 + i, href: `#${name}`, drillsInto: null, shape: "square" as const, ramp: i / 3, })), edges: [], }),});
// A module-level constant: a new function on each render would reload the tile.const studs = () => import("./custom-grid.tile");
export default function CustomGrid() { // `step` is the world distance one tile covers; `continuous` scales it smoothly as you zoom. return <DataExplorer source={source} focus={null} background={{ grid: { tile: studs, step: 80, zoom: "continuous", alpha: 0.5 } }} />;}The module it loads:
// Import this module only through the dynamic import in custom-grid.tsx, so it stays out of the// page's first load. It imports PixiJS only as types: its classes come from `draw(kit)`.import { defineGridTile } from "@data-xplorer/core/pixi";
// One baseplate stud, drawn in white and greys: the grid's `color` tints it, its `alpha` fades it.export default defineGridTile({ size: 64, draw(kit) { const tile = new kit.Container(); tile.addChild( new kit.Graphics().circle(34, 35, 17).fill({ color: 0x000000, alpha: 0.35 }), new kit.Graphics().circle(32, 32, 16).fill(0xffffff).stroke({ width: 2, color: 0x7a7a7a }), ); return tile; },});Pass the same function each render (a module-level constant, as above), because a new function reloads the tile. If it fails to load or draw, the dots are used.