OG
JS
Docs
Demos
Examples
Edge style gallery
Core strokes, outlines, halos, motion, text, badges, and parallel routes share one edge record.
7 relationships · 4 presentation recipes · one instanced renderer
// Edge records declare semantic style data such as route kind, direction, or label. // OGJS rules resolve strokes, outlines, halos, arrows, badges, and parallel routing in one // render path; applications do not draw separate canvases for each visual treatment. import { Ogjs } from '../../src/index'; const og = new Ogjs({ container: '#viz', theme: 'light' }); const rows = [ ['solid', -150, '#64748b'], ['curve', -50, '#2563eb'], ['dash', 50, '#7c3aed'], ['directed', 150, '#ea580c'], ]; og.setGraph({ nodes: [...rows.flatMap(([id, y, color]) => [ { id: `${id}-a`, label: id[0].toUpperCase() + id.slice(1), x: -185, y, size: 12, color }, { id: `${id}-b`, x: 185, y, size: 12, color }, ]), { id: 'parallel-a', label: 'Parallel', x: -185, y: 235, size: 12, color: '#059669' }, { id: 'parallel-b', x: 185, y: 235, size: 12, color: '#059669' }, ], edges: [ ...rows.map(([id]) => ({ id: `${id}-edge`, source: `${id}-a`, target: `${id}-b`, data: { recipe: id } })), { id: 'parallel-1', source: 'parallel-a', target: 'parallel-b', data: { recipe: 'parallel', rank: 1 } }, { id: 'parallel-2', source: 'parallel-a', target: 'parallel-b', data: { recipe: 'parallel', rank: 2 } }, { id: 'parallel-3', source: 'parallel-b', target: 'parallel-a', data: { recipe: 'parallel', rank: 3 } }, ], }); // Resolve domain recipes into declarative edge attributes. Curvature, dashes, arrows, badges, // outlines, and halos remain in the same picking, camera, and export pipeline. og.styles.addEdgeRule(({ data }) => { const recipe = data.recipe; if (recipe === 'solid') return { width: 2, color: '#64748b', caption: 'baseline' }; if (recipe === 'curve') return { width: 3, color: '#2563eb', curvature: 0.22, caption: 'outlined', outlineWidth: 2, outlineColor: '#0f172a' }; if (recipe === 'dash') return { width: 3, color: '#7c3aed', dash: [10, 6], caption: 'halo', haloWidth: 6, haloColor: '#7c3aed55' }; if (recipe === 'directed') return { width: 4, color: '#ea580c', dash: [8, 5], arrow: true, caption: 'live risk', badge: 'P1', pulse: 0.65 }; return { width: 2.5, color: '#059669', caption: data.rank === 2 ? 'parallel routes' : undefined }; }); // Fan repeated endpoint pairs after styling so every relationship remains distinguishable and // pickable without application-calculated Bézier control points. og.fanEdges(0.2); og.setEdgeArrows({ sizePx: 11, offsetPx: 30 }); og.camera.fit();