Skip to content

Styling ​

Graphora styling is renderer-owned and framework-agnostic. The engine combines graph data, theme inputs, explicit item overrides, and interaction state into resolved render snapshots.

Theme And Style Helpers ​

@graphora/renderer exposes pure helpers:

  • resolveRenderTheme
  • resolveNodeStyle
  • resolveEdgeStyle
  • resolveLabelStyle
  • resolveInteractionStyles
  • mergeInteractionRenderState

The style priority order is:

  1. built-in default theme tokens
  2. user-provided theme overrides
  3. explicit item style overrides
  4. interaction overlays

Interaction overlays apply in this order: dimmed context, programmatic highlight, hover, focus, selection, active drag. Later overlays win for the same property only.

Theme Input ​

Pass theme and interaction style inputs through createGraphEngine:

ts
const engine = createGraphEngine({
  graph,
  renderer,
  theme: {
    backgroundColor: '#f8fafc',
    node: { fill: '#94a3b8', radius: 18 },
    edge: { stroke: '#475569' },
    label: { visible: true }
  },
  interactionStyles: {
    hover: { node: { fill: '#38bdf8' } },
    selection: { node: { stroke: '#7c3aed', strokeWidth: 3 } }
  }
})

Reusable Light And Dark Presets ​

Import GRAPHORA_LIGHT_THEME or GRAPHORA_DARK_THEME from @graphora/renderer and pass it directly as createGraphEngine({ theme }). Light is the canonical DEFAULT_RENDER_THEME; both presets and all nested objects are frozen. Resolution copies caller overrides into immutable tokens.

ts
import { createGraphEngine } from '@graphora/engine'
import { GRAPHORA_DARK_THEME } from '@graphora/renderer'

const engine = createGraphEngine({
  graph,
  renderer,
  theme: {
    ...GRAPHORA_DARK_THEME,
    node: { ...GRAPHORA_DARK_THEME.node, radius: 22 },
    edge: {
      ...GRAPHORA_DARK_THEME.edge,
      arrow: { ...GRAPHORA_DARK_THEME.edge.arrow, size: 10 }
    }
  },
  interactionStyles: {
    highlight: {
      node: { stroke: '#5eead4' },
      edge: { stroke: '#5eead4' },
      label: { fill: '#99f6e4' }
    },
    dimmed: { label: { fill: '#94a3b8' } }
  }
})

Spread each nested object you override to retain the remaining preset tokens. Passing only node: { radius: 22 } replaces that input object; omitted fields then fall back to built-in light defaults. Nested arrow overrides work the same way. No merge helper is needed for the existing small token surface.

A node's explicit GRAPHORA_STYLE_ATTRIBUTE fill still wins over the preset fill. Interaction overlays remain higher priority and independent of presets: the example above supplies bright highlight labels for a dark background. Review application-specific hover, focus, selection and drag colors alongside any item colors you introduce. Presets do not automatically choose overlays or follow OS color preferences. The engine accepts theme configuration at creation; this slice adds no runtime theme setter.

The custom-styles vanilla example compares both presets and toggles path highlighting. Themes remain renderer configuration, with no new package or React provider. A future @graphora/themes package needs multiple renderer consumers, larger collections, palette dependencies or external reuse demand; see ADR 0020 in the product workspace.

Item Overrides ​

Graph data can opt into item-level style overrides with GRAPHORA_STYLE_ATTRIBUTE:

ts
import { GRAPHORA_STYLE_ATTRIBUTE } from '@graphora/renderer'

const graph = {
  nodes: [
    {
      id: 'api',
      attributes: {
        [GRAPHORA_STYLE_ATTRIBUTE]: {
          fill: '#16a34a',
          radius: 24,
          label: { visible: true, fontSize: 14 }
        }
      }
    }
  ]
}

V0.1 Style Surface ​

Nodes support fill, stroke, stroke width, radius, opacity, visibility, and label style overrides.

Edges support stroke, stroke width, opacity, visibility, curved flag, arrow end settings, and label style overrides.

Labels support fill, font family, font size, font weight, and visibility.

Current Boundary ​

V0.1 styling is not a selector engine. Data-driven rule systems, palettes, themes as plugins, custom renderers, label collision, wrapping, halos, and level-of-detail styling are deferred.