Skip to content

Visual States ​

A connection looks different while the pointer is over it, while it is selected, or while it has keyboard focus. Every visual entity — lines, markers, ports and labels — describes those looks the same way: its own fields, plus one bucket per state with the fields that change.

ts
lines: {
  color: '#2e8b57',
  width: 2,
  hover: { width: 4, dashed: true },
  selected: { color: '#1f6feb' },
}

The four states ​

  • hover — the pointer is over the connection's own line. Needs interaction.hover.
  • highlight — the pointer is over one of the connection's blocks, which lights up every connection that ends there. Needs interaction.highlight. Until a highlight bucket is set, it looks the same as hover; set it to make a block's lines react more quietly than a line under the pointer.
  • selected — the connection is selected (see Selection & Accessibility).
  • focus — the connection's line has keyboard focus.

States combine. A connection that is selected and hovered gets the selected fields, then the hover fields on top. The order, from weakest to strongest:

  1. the entity's own fields,
  2. selected,
  3. highlight,
  4. hover,
  5. focus.

A field that no active state sets keeps the value from the layer below.

Turning hover and highlight on ​

hover and highlight are off by default: a diagram that is only looked at does not react to the pointer, and its lines show no pointer cursor. Turn them on in the interaction group of the configuration:

ts
interaction: { hover: true, highlight: true }
  • interaction.hover — boolean · default: false. The pointer over a line puts it into the hover state and shows a pointer cursor over it.
  • interaction.highlight — boolean · default: false. The pointer over a block puts the connections that end there into the highlight state.

Each can be set for a single entity, and the local value wins over the global one: hoverable on a connection and highlightable on a block. hoverable: false keeps one line still while everything else reacts; highlightable: true lets one block light up its lines on a diagram that is otherwise still.

A hover or highlight bucket does nothing while its mode is off. The selected and focus states, selection and keyboard operation do not depend on either flag, and a selectable line shows the pointer cursor anyway. The events connection:mouseenter, connection:mouseleave, block:mouseenter and block:mouseleave fire either way. Both flags can be changed at runtime with setConfig.

What a state can change ​

A state bucket takes the same fields as the entity itself:

  • Lines — color, width, dashed, opacity. Without a state width, a selected or highlighted line still gets a small width bump over its explicit width, so it stands out.
  • Markers — shape, size, color, strokeColor, strokeWidth, opacity, svg, arrow. A marker can turn from a dot into a diamond on hover, or swap a whole custom svg.
  • Ports — radius, fill, stroke, strokeWidth, opacity.
  • Labels — background, border, color, fontSize, paddingX, paddingY, opacity.

Markers, ports and labels follow the state of their connection. A port dot shared by several connections takes the strongest state among them. Blocks have no states: they are your own markup, and the engine only adds the classes vl-draggable and vl-dragging, so restyle them with CSS.

A marker follows its line ​

A marker that sets no color is painted in the color the line has in the current state. When a line turns red on hover, its arrow turns red with it, with no marker setting at all. A color set on the marker itself is kept in every state, unless that state's bucket sets another.

ts
markers: {
  end: {
    shape: 'circle',
    size: 6,
    hover: { shape: 'diamond', size: 10, strokeColor: '#ffffff', strokeWidth: 1 },
    selected: { shape: 'arrow', size: 8 },
  },
}

A marker's size is a multiple of the line's stroke width, so it grows by itself when the line gets thicker; a size in a state bucket overrides that.

Setting states in several places ​

The configuration group and a connection's own style both have buckets. They merge state by state and field by field: a connection's hover.width replaces the group's hover.width, and the group's hover.color still applies.

ts
createVisualLinker(el, { lines: { hover: { color: '#6366f1', width: 4 } } })

linker.setConnections([
  { id: 'a-b', from, to, style: { hover: { width: 6 } } }, // hover: width 6, color '#6366f1'
])

A state bucket ranks above the base fields at every level. When the group sets hover.color and a connection sets only its own base color, that connection shows the group's hover color on hover. To keep its own color, the connection sets hover.color as well.

Keyboard focus ​

The focus state applies to connections that can take focus, which needs interaction.selectable. A focused line gets a halo in the theme's focusRing color on top of whatever focus sets.

Opacity ​

opacity is a field of its own, so it can change in a state: fade connections at rest and bring one to full strength on hover.

ts
lines: { opacity: 0.55, hover: { opacity: 1 } }

A marker without its own opacity fades together with its line. An animated line is dimmed further to let the moving pattern show, so its effective opacity is the two multiplied.