1

My question: Can svg <marker> elements inherit color from the <line> they are referenced on?

The background: I have a D3 graph that has different styled lines, and I want my lines to have arrows at the end. So at the top of my <svg> I have const defs = svg.append('defs'); and then further along I generate my defs using a generator function:

function makeDefs(defs: Selection<SVGDefsElement, unknown, null, undefined>, color: string, status: string) {
const markerSize = 3

const start = defs.append
   .append('marker')
   .attr('id', `arrow-start-${color}-${status}`)
   .attr('viewBox', '-5 -10 20 20')
   .attr('markerWidth', markerSize)
   .attr('markerHeight', markerSize)
   .attr('orient', 'auto-start-reverse');
start
   .append('path')
   .attr(
     'd',
     status === 'PUBLISHED' ? customPaths.arrowLarge : customPaths.arrowSmall
   )
   .attr('stroke', color)
   .attr('fill', color);
}

And use it like so:

makeDefs(defs, 'red', 'DRAFT');

And then I add the markers to my lines with:

 // d3 code to draw the lines etc
 line.attr(
 'marker-start',
 (d) =>
  `url(
    #arrow-start-${d.color}-${d.status}
  )`
 );

This all works great, my arrows have lines. But my current setup feels burdensome and clunky. I have about 20 colors and 3 statuses. With my current setup that would be 60 different:

makeDefs(defs, 'one-of-20-colors', 'one-of-3-statues');

My understanding of markers is that they can inherit color using the currentcolor attribute. Currently my <defs> sit up top underneath my main <svg> so any color inherited is inherited directly from that top level svg which is not what I want. The issue in my case is my <line> elements are the elements who's color I want to inherit, but according to the MDN docs <line>s cannot have <defs> as children, thus leaving me with the only option, of defining all my <defs> up front all at once.

Is there a trick or some attribute I'm missing here?

Any way to pass color to my marker when doing:

line.attr(
 'marker-start',
 (d) =>
  `url(
    #arrow-start-${d.color}-${d.status}
  )`
 );

?

For what is is worth, I'm currently wrapping all my <line>s in <g>. I suppose I could wrap them in <svg>s instead, and apply the fill and stroke there, and then define my <defs> per svg container? I tried this briefly and swapping the <g> for an <svg> broke a lot, but I'm not even sure if it would work, or be better for that matter.

Syed Jafri
  • 416
  • 8
  • 17

0 Answers0