<title>Styling SVG Markers</title>
background: hsl(0, 0%, 0%);
-webkit-background-size: cover;
-moz-background-size: cover;
font-family: "Lucida Grande", "Lucida Sans Unicode", Helvetica, Arial, Verdana, sans-serif;
-webkit-user-select: none;
font-family: "Lucida Grande", "Lucida Sans Unicode", Helvetica, Arial, Verdana, sans-serif;
color: hsl(203, 100%, 50%);
color: hsl(332, 98%, 51%);
and (max-width : 870px) {
<div class="svg-container">
<svg xmlns="https://www.w3.org/2000/svg" xmlns:xlink="https://www.w3.org/1999/xlink"
xmlns:xml="https://www.w3.org/XML/1998/namespace" xml:space="preserve"
x="0" y="0" width="400px" height="440px" viewBox="0 0 500 550" >
<marker id="marker-circle" markerWidth="10" markerHeight="10" refx="5" refy="5">
<circle cx="5" cy="5" r="3" class="marker"/>
<marker id="marker-square" markerWidth="7" markerHeight="7" refx="4" refy="4"orient="auto">
<rect x="1" y="1" width="5" height="5" class="marker"/>
<marker id="marker-arrow" markerWidth="12" markerHeight="12" refx="6" refy="4" orient="auto">
<path d="M 1 1 7 4 1 7 Z" class="marker"/>
<linearGradient id="btn-light" x1="0%" y1="0%" x2="0%" y2="100%" spreadMethod="pad">
<stop stop-color="hsl(0, 0%, 90%)" offset="0"></stop>
<stop stop-color="hsl(0, 0%, 92%)" offset="0.05"></stop>
<stop stop-color="hsl(0, 0%, 97%)" offset="0.33"></stop>
<stop stop-color="hsl(0, 0%, 87%)" offset="0.67"></stop>
<stop stop-color="hsl(0, 0%, 84%)" offset="1"></stop>
<path id="start-path" class="layout-path" d="M210,250 a40,40 0 1,0 80,0 a40,40 0 1,0 -80,0"></path>
<path id="quad-path" class="layout-path" d="M165,250 a85,85 0 1,0 170,0 a85,85 0 1,0 -170,0"></path>
<path id="mid-path" class="layout-path" d="M110,250 a140,140 0 1,0 280,0 a140,140 0 1,0 -280,0"></path>
<path id="end-path" class="layout-path" d="M10,250 a240,240 0 1,0 480,0 a240,240 0 1,0 -480,0"></path>
<g id="cycle-btn" transform="translate(186,518)">
<rect class="btn-light" x="0" y="0" rx="4" ry="4" width="128" height="32"></rect>
<text class="btn-light-txt" x="64" y="22">Cycle Colors</text>
<h2>Styling SVG Markers</h2>
<p>Using an array of color keys, this demo:</p>
<li>appends CSS for each color to the stylesheet</li>
<li>clones svg markers for each color into the defs</li>
<li>creates a path for each color, with markers</li>
<li>cycles the CSS color class to the next path</li>
<p><strong><em>Thanks to:</em></strong></p>
<p>O'Reilly <a href="https://shop.oreilly.com/product/9780596002237.do" target="_blank">SVG essentials</a> for the quadratic beziér curve.</p>
<p>Jacob Jenkov for <a href="https://tutorials.jenkov.com/svg/marker-element.html" target="_blank">SVG markers</a>.</p>
<p>David Walsh for <a href="https://davidwalsh.name/add-rules-stylesheets" target="_blank">adding styles</a>.</p>
<p>complexdan for <a href="https://complexdan.com/svg-circleellipse-to-path-converter/" target="_blank">SVG circles to paths</a>.</p>
<p><a href="https://plus.google.com/+PaulIrish/posts" target="_blank"><span class="pi">Paul Irish</span></a> for <a href="https://mothereffinghsl.com/" target="_blank"><span class="mef">m</span><span class="mef">o</span><span class="mef">t</span><span class="mef">h</span><span class="mef">e</span><span class="mef">r</span><span class="mef">e</span><span class="mef">f</span><span class="mef">f</span><span class="mef">i</span><span class="mef">n</span><span class="mef">'</span> <span class="mef">h</span><span class="mef">s</span><span class="mef">l</span></a>.</p>
window.addEventListener('DOMContentLoaded', function () {
// rainbow starting with blue
var colors = ['hsl-242', 'hsl-259', 'hsl-273', 'hsl-296', 'hsl-341',
'hsl-359', 'hsl-18', 'hsl-35', 'hsl-52', 'hsl-83',
'hsl-127', 'hsl-160', 'hsl-190', 'hsl-212', 'hsl-227'];
// add styles to the page for colors and SVG paths
var addStyles = function () {
// select the <style> tag
var styles = document.querySelector('#styles');
// select the <style> tag's CSSStyleSheet
var styleSheet = styles.sheet;
// create styles for the colors, the paths, and the markers
colors.forEach(function (color) {
var hslColor = color.replace( '-', '(' ) + ', 100%, 50%)';
var colorStyle = '.' + color + ' { stroke: ' + hslColor + '; fill: ' + hslColor + '; color: ' + hslColor+ '; }';
var pathStyle = '.color-path.' + color + ' { ' +
'marker-start: url(#marker-circle-' + color + '); ' +
'marker-mid: url(#marker-square-' + color + '); ' +
'marker-end: url(#marker-arrow-' + color + '); ' +
// index 0 to add to the front of the CSSRuleList,
// to avoid adding !important to the styles already present
styleSheet.insertRule(pathStyle, 0);
styleSheet.insertRule(colorStyle, 0);
// create def elements for each color and marker type; append to defs
var addDefs = function () {
var defs = document.querySelector('defs');
// add the appropriate id to the defs element, add the appropriate class to its marker
var colorDef = function (def, color) {
// add a color class to the def element's marker child
var marker = def.querySelector('.marker');
marker.classList.add(color);
// set the appropriate id on the def element
def.id = def.id + '-' + color;
colors.forEach(function (color) {
// for each color, select and clone the def element for each marker
defArray.push(defs.querySelector('#marker-circle').cloneNode(true));
defArray.push(defs.querySelector('#marker-square').cloneNode(true));
defArray.push(defs.querySelector('#marker-arrow').cloneNode(true));
defArray.forEach(function (def) {
// create def elements for each color and marker type; append to defs
var addMefs = function () {
var forEach = Array.prototype.forEach;
var mefs = document.querySelectorAll('span.mef');
colors.forEach(function ( color, cx ) {
mefs[cx].classList.add(color);
// create arrays of x,y objects for the start, middle and end of paths
var crtCtrlPts = function (markerPos) {
var ctrlPath = document.querySelector('#' + markerPos + '-path')
var getPts = function (path) {
var pathLen = path.getTotalLength();
var pathPart = 1 / colors.length;
colors.forEach(function (color, cx) {
var pathTo = pathLen * ( pathPart * (cx + 1) );
if ( markerPos === 'quad' ) { pathTo -= 25; }
var gp = ctrlPath.getPointAtLength( pathTo );
var pt = { x: Math.round(gp.x), y: Math.round(gp.y) };
return ctrlArr.reverse();
// control points for paths
start : crtCtrlPts('start'),
quad : crtCtrlPts('quad'),
var crtPaths = function () {
var viz = document.querySelector('.viz');
var addPath = function(color, cx) {
var path = document.createElementNS('https://www.w3.org/2000/svg', 'path');
var posArr = Object.keys(ctrlPts);
posArr.forEach(function (pos) {
var pt = ctrlPts[pos][cx];
dString = 'M ' + pts.start.x + ' ' + pts.start.y +
' Q ' + pts.quad.x + ' ' + pts.quad.y +
', ' + pts.mid.x + ' ' + pts.mid.y +
' T ' + pts.end.x + ' ' + pts.end.y;
path.setAttribute( 'd', dString );
path.classList.add( 'color-path', color );
colors.forEach(function ( color, cx ) { addPath( color, cx ); });
var forEach = Array.prototype.forEach;
var colorsLength = colors.length;
var cycleColors = function ( paths, mefs ) {
var nextColor = function(colorClass) {
var colorX = colors.indexOf(colorClass);
if ( colorX < 0 ) { colorX = colors.length -1; }
forEach.call( paths, function ( path ) {
var colorClass = path.getAttribute('class')
.replace('color-path ', '');
var nextHsl = nextColor(colorClass);
path.setAttribute('class', 'color-path ' + nextHsl);
forEach.call( mefs, function ( mef ) {
var colorClass = mef.className.replace('mef ', '');
var nextHsl = nextColor(colorClass);
mef.className = 'mef ' + nextHsl;
cycleTo = setTimeout(function () {
cycleColors( paths, mefs );
document.querySelector('#cycle-btn').addEventListener('click', function(evt) {
var cycleTxt = document.querySelector('.btn-light-txt');
var cycleStr = cycleTxt.innerHTML;
if ( cycleStr === 'Stop' ) {
cycleTxt.innerHTML = 'Cycle Colors';
paths = document.querySelectorAll('.viz .color-path');
mefs = document.querySelectorAll('span.mef');
cycleTxt.innerHTML = 'Stop';
cycleTo = setTimeout(function () {
cycleColors( paths, mefs );