Skip to content

Spike: Resolve per-type graph styling off both selectors and element data #2111

Description

@kmcginnes

Goal

Validate an architecture that resolves per-type vertex/edge styling neither through per-type Cytoscape selectors nor through per-element data() — so the canvas can be O(1) in style-context count (the #2104 win) and free of per-element display state (the #1887 reconciler's requirement), while making restyles explicit and cheap. This must be settled before the reconciler or further style work commits to an implementation, because #2104 (landed) and #1887 (planned) currently hold contradictory models of what an element carries.

Background / why now

The three-way constraint

A durable answer must satisfy all three at once:

  1. O(1) style-context count regardless of type count (Schema view lockup from per-type Cytoscape style selectors in useGraphStyles #2104).
  2. No per-element display/style state so element references stay stable across restyles (Reconcile the graph canvas — stop rebuilding on every change #1887).
  3. Explicit, cheap restyle — a Type style edit updates the canvas without rebuilding elements (Reconcile the graph canvas — stop rebuilding on every change #1887 user story; Push node background-color to Cytoscape ele.data() with styled type atoms #1725).

Cytoscape natively offers only per-type selectors (fails #1) or per-element data (fails #2). The spike finds and validates the escape.

Hypothesis to validate (the "unifying bet")

Combine three moves so styling stops being smuggled through the element pipeline:

  1. Class-based dedup — each element carries one stable style-class keyed on its distinct resolved style (not its type). Stylesheet has one rule per distinct style (usually ≪ types; a uniform schema collapses to one rule). Element data shrinks to ~1 class ref. Degrades to O(types) only in the pathological "every type uniquely styled" case — the same worst case as selectors, so no regression.
  2. Style-registry bridge — an external store (mirroring the existing iconRegistry + useSyncExternalStore pattern) owns type→style; a thin bridge patches the stylesheet / ele.data() imperatively on change, outside React render, decoupled from element identity.
  3. Incremental element updates — ride on the Reconcile the graph canvas — stop rebuilding on every change #1887 reconciler (kill cloneDeep full-replace) so element data is diffed/patched, not rebuilt + cloned.

Result: element data ≈ 1 class ref; style values live once per distinct style; restyle = patch one rule / the registry, no element rebuild. Both downsides dissolve.

Open questions the spike must answer

Expected Outcome

Out of Scope

Related Issues

Important

Internal only — this issue is maintained by the core team and is not accepting external contributions.

Metadata

Metadata

Assignees

No one assigned

    Labels

    explorationIssues related to graph exploration, node & edge details, neighbor expansion, etcinternalSignals that the team will work on this issue internally.performanceIssues relating to performanceready-for-humanNeeds human implementationtech debtIssues, typically tasks, that are mainly about cleaning up code that is problematic in some way

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions