Connected Nodes
Four color-coded nodes positioned with CSS and connected by an SVG overlay — the ready-to-wire HTML/CSS foundation for any interactive graph or dependency map.
This effect is free — the Navigation category contains 36 effects total, including 4 free. Effect.Labs has 811 vanilla effects. Explore the category →
Usage examples
How it works
Each node is a <div class="node-point"> set to position: absolute inside a .nodes-scene container that fills 100 % × 100 % of its parent. Initial coordinates are declared as percentages via the classes .node-a through .node-d (e.g. top: 20%; left: 15%) — no hard-coded pixel values. Each node carries a 135° linear gradient (two tones of the same hue) to simulate a subtle bevel; user-select: none prevents text selection during dragging.
A <svg class="node-lines"> covers the whole scene at position: absolute; inset: 0 with pointer-events: none to never block clicks on the nodes below. The four <line> elements (a→b, b→c, c→d, d→a) define the connections: drawn in light indigo (stroke: rgb(129,140,248)), width 2, stroke-dasharray: 6 4 (6 px dash, 4 px gap), opacity 0.6.
On hover, each node emits a glow halo via box-shadow with a 0.2s transition — the only animation in the provided code. The cursor: grab property (and grabbing on :active) signals drag intent without including any drag logic in the base code.
The x1/y1/x2/y2 attributes of each <line> start at 0,0 and remain invisible until initialized via getBoundingClientRect() on each node pair after DOM render. Real drag behavior — updating SVG coordinates on every move — requires about twenty lines of vanilla JavaScript, shown in the interactive examples below.
Accessibility
- No prefers-reduced-motion handling in the provided code — the only animation is
transition: box-shadow 0.2son hover, non-repetitive and imperceptible. Pair with@media (prefers-reduced-motion: reduce) { .node-point { transition: none } }if the drag implementation adds further transitions. - Nodes are
<div>elements with notabindexorrole— not keyboard-reachable in the base state. For an accessible graph, addtabindex="0",role="button", and handle arrow keys to move the active node. - The
<svg>lines element is purely decorative but lacksaria-hidden="true"— add it to prevent screen readers from traversing it unnecessarily. - Contrast fails in the default configuration: white text on amber (
#f59e0b) and pink (#ec4899) backgrounds does not reach 4.5:1 WCAG AA at 0.75 rem — use dark text (color:#1e1b4b) or deeper background tones.
Browser compatibility
Pure CSS + native SVG — zero external dependencies, no canvas element required.
If SVG is not supported (very old browsers), connection lines do not appear — colored nodes remain visible and the page stays functional.
The code
Copy the three blocks into your page. No dependencies.
<div class="anchor-demo">
<div class="nodes-scene" id="nodesScene">
<svg class="node-lines">
<line id="line-ab" x1="0" y1="0" x2="0" y2="0">
</line>
<line id="line-bc" x1="0" y1="0" x2="0" y2="0">
</line>
<line id="line-cd" x1="0" y1="0" x2="0" y2="0">
</line>
<line id="line-da" x1="0" y1="0" x2="0" y2="0">
</line>
</svg>
<div class="node-point node-a" data-node="a">A</div>
<div class="node-point node-b" data-node="b">B</div>
<div class="node-point node-c" data-node="c">C</div>
<div class="node-point node-d" data-node="d">D</div>
</div>
</div>
.anchor-demo {
position: relative;
width: 100%;
height: 220px;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.anchor-demo * {
box-sizing: border-box;
}
.nodes-scene {
position: relative;
width: 100%;
height: 100%;
}
.nodes-scene svg.node-lines {
position: absolute;
inset: 0px;
width: 100%;
height: 100%;
pointer-events: none;
}
.nodes-scene svg.node-lines line {
stroke: rgb(129, 140, 248);
stroke-width: 2;
stroke-dasharray: 6, 4;
opacity: 0.6;
}
.node-point {
position: absolute;
width: 48px;
height: 48px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.75rem;
font-weight: 700;
color: rgb(255, 255, 255);
cursor: grab;
user-select: none;
z-index: 2;
transition: box-shadow 0.2s;
}
.node-point:active {
cursor: grabbing;
}
.node-point:hover {
box-shadow: rgba(129, 140, 248, 0.6) 0px 0px 20px;
}
.node-a {
background: linear-gradient(135deg, rgb(99, 102, 241), rgb(129, 140, 248));
top: 20%;
left: 15%;
}
.node-b {
background: linear-gradient(135deg, rgb(236, 72, 153), rgb(244, 114, 182));
top: 60%;
left: 55%;
}
.node-c {
background: linear-gradient(135deg, rgb(16, 185, 129), rgb(52, 211, 153));
top: 25%;
left: 70%;
}
.node-d {
background: linear-gradient(135deg, rgb(245, 158, 11), rgb(251, 191, 36));
top: 70%;
left: 20%;
}
// Effet CSS pur — pas de JavaScript nécessaire
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
| .anchor-demo height | 220px | Container height. Set to 100% or 360px to fill a full card or hero section. |
| .node-point width / height | 48px | Node diameter. 64 px for longer labels, 32 px for a dense graph with many nodes. |
| stroke (svg line) | rgb(129,140,248) | SVG line color. Replace with your accent color or vary per connection type (required vs optional). |
| stroke-width (svg line) | 2 | Line thickness. 1 = subtle, 3–4 = strong connections. Can encode edge weight in a weighted graph. |
| stroke-dasharray (svg line) | 6 4 | Dash pattern. '0' = solid line (direct link), '2 6' = fine dotted (weak link), '8 3 2 3' = dash-dot. |
| opacity (svg line) | 0.6 | Line opacity. 1 = full visibility, 0.3 = receding connections that put nodes in the foreground. |
| Node backgrounds (.node-a through .node-d background) | 4 gradients (indigo, pink, green, amber) | Swap for your brand colors or encode node state: green = healthy, red = error, grey = offline. |
FAQ
Why are SVG lines invisible on page load?
The x1/y1/x2/y2 attributes of each <line> start at 0 — they must be initialized with getBoundingClientRect() after DOM render. Call the update in a requestAnimationFrame or right after DOMContentLoaded to ensure CSS percentage positions have been computed.
How do I add a fifth node and new connections?
Add a <div class="node-point"> with data-node="e" and its CSS coordinates, then a <line id="line-ae"> in the SVG. The endpoint-update logic is identical — just pass the new node pair to your recalculate function.
Can I add directional arrows to connections?
Yes: define a <marker> inside an SVG <defs> block, then apply marker-end="url(#arrow)" to the target <line> elements. Existing CSS styles (color, width, dashes) stay untouched — only the SVG markup changes.