March 23, 2023March 24, 2023 How to Make an SVG Line Graph From Scratch Graphs are very common components used in admin areas because they give us insights into our data whether its product revenue or website visitors. Lots of web graphs and charts are built with libraries like D3.js or chart.js. But jumping straight into a library can really limit your understanding of what’s going on under the surface. In this post we will build one from scratch using SVG, CSS, and javascript to have a better grasp. Starting With HTML, CSS, and SVGRendering Our SVG Graph With Javascript Starting With HTML, CSS, and SVG SVG is ideal for graphs because of its flexibility. You can read more about SVG here. We’ll start with our base HTML file with a graph heading and container that our SVG code will live inside. <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="graph.css"> <title>Line Graph</title> </head> <body> <div class="graph-container"> <div id="graph-header"> <h3 id="graph-title">Revenue</h3> </div> <!-- SVG Graph Goes Here --> </div> </body> </html> As you can see we have a link to a CSS file. To save time you can copy and paste all the CSS code for the graph below. Don’t worry about not having all the elements. This post is more focused on SVG and javascript. Create a CSS file named ‘graph.css’ and put this code inside. :root { --dark-bg-color: black; --dark-comp-color: #424242; --dark-text-color: #ced4d6; --prime-color: #b9c8f7; } body { background-color: var(--dark-comp-color); font-family: Roboto; } .graph-container { width: 50%; background-color: var(--dark-bg-color); color: var(--dark-text-color); box-shadow: 0 0 5px 3px rgb(0,0,0,0.4); padding: 20px; border-radius: 10px; margin: 100px auto; } .line-graph { width: 100%; height: 300px; } text { fill: var(--dark-text-color); } #y-axis line { stroke: var(--dark-text-color); stroke-opacity: 0.4; } #graph-header { display: flex; justify-content: space-between; } #graph-header h3 { padding: 0px; margin: 0px; } #graph-header button { margin-left: auto; } .data-line, #points circle { stroke: var(--prime-color); fill: var(--prime-color); } .graph-heading { font: bold 24px Verdana; } #y-axis text { text-anchor: end; } #x-axis text { text-anchor: middle; } Before we put in our SVG we should know what parts make up a line graph. The basic parts would be the X-axis, Y-axis, lines, and points. First we will add our X-axis with short day names. Because we want this graph to be responsive we must use percentages for each coordinate, or at least the X coordinates as they are the ones that change when the window resizes. <div class="graph-container"> <div id="graph-header"> <h3 id="graph-title">Revenue</h3> </div> <svg class="line-graph" xmlns="http://www.w3.org/2000/svg"> <g class="graph"> <g id="x-axis"> <text x="11%" y="99.8%">Sun</text> <text x="25%" y="99.8%">Mon</text> <text x="39%" y="99.8%">Tue</text> <text x="53%" y="99.8%">Wed</text> <text x="67%" y="99.8%">Thu</text> <text x="81%" y="99.8%">Fri</text> <text x="95%" y="99.8%">Sat</text> </g> </g> </svg> </div> I measured out the position for each data point so you don’t have to. Feel free to play around with the X and Y values to have a better idea of positioning in SVG. Later we’ll render our graph using javascript so we can use math to space everything evenly instead of having to guess with HTML. We would have to use javascript anyway inside a real app that uses dynamic data. Unlike HTML divs, elements inside a SVG tag have an absolute position by default. So if we gave the text elements the same X and Y values they would sit on top of each other. That is why we must set the position of our elements manually. Now let’s add the Y-axis, lines, and points. <g class="graph"> <g id="y-axis"> <text x="40" y="20%">500</text> <line x1="7%" y1="18%" x2="99.5%" y2="18%" /> <text x="40" y="35%">400</text> <line x1="7%" y1="33%" x2="99.5%" y2="33%" /> <text x="40" y="50%">300</text> <line x1="7%" y1="48%" x2="99.5%" y2="48%" /> <text x="40" y="65%">200</text> <line x1="7%" y1="63%" x2="99.5%" y2="63%" /> <text x="40" y="80%">100</text> <line x1="7%" y1="78%" x2="99.5%" y2="78%" /> <text x="40" y="95%">0</text> <line x1="7%" y1="93%" x2="99.5%" y2="93%" /> </g> <g id="x-axis"> <text x="11%" y="99.8%">Sun</text> <text x="25%" y="99.8%">Mon</text> <text x="39%" y="99.8%">Tue</text> <text x="53%" y="99.8%">Wed</text> <text x="67%" y="99.8%">Thu</text> <text x="81%" y="99.8%">Fri</text> <text x="95%" y="99.8%">Sat</text> </g> <g id="points"> <circle cx="11%" cy="48%" r="3" /> <circle cx="25%" cy="3%" r="3" /> <circle cx="39%" cy="55%" r="3" /> <circle cx="53%" cy="79%" r="3" /> <circle cx="67%" cy="22%" r="3" /> <circle cx="81%" cy="42%" r="3" /> <circle cx="95%" cy="88%" r="3" /> </g> <g id="lines"> <line class="data-line" x1="11%" y1="48%" x2="25%" y2="3%" /> <line class="data-line" x1="25%" y1="3%" x2="39%" y2="55%" /> <line class="data-line" x1="39%" y1="55%" x2="53%" y2="79%" /> <line class="data-line" x1="53%" y1="79%" x2="67%" y2="22%" /> <line class="data-line" x1="67%" y1="22%" x2="81%" y2="42%" /> <line class="data-line" x1="81%" y1="42%" x2="95%" y2="88%" /> </g> </g> Inside the Y-axis we have the text followed by a line. The line represents the Y value. We want to keep it faint since the focus is on the data lines. Because we used percentages for all the coordinates the graph will stretch well when we resize the window. It can be painstaking to have to measure the coordinates for every SVG element. And that is where javascript comes in. Rendering Our SVG Graph With Javascript What we can do is use javascript with some basic math to space out the elements. We will start with the values on the X-axis. Create a new js file and remember to link it at the bottom of your HTML file. Paste in this code. let xAxis = document.getElementById('x-axis'); let xAxisValues = [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ]; xAxisValues.forEach((val, ind) => { let xMargin = 11; let len = xAxisValues.length; let xVal = (ind/len * 100 + xMargin) + '%'; let yVal = '99.8%'; let text = document.createElementNS('http://www.w3.org/2000/svg', 'text'); text.setAttribute('x', xVal); text.setAttribute('y', yVal); text.textContent = val; xAxis.appendChild(text); }); Now be sure to delete all the X-axis text elements inside your HTML file. In this code we grab the list of X-axis variables in an array and loop through each one. Then measure the distance we want between them and create the text elements right inside our forEach loop. This is likely what a library like d3.js does to evenly space out the data points. Since the points are going to have the same X coordinates we can set their attributes to the same x value. And for the y values let’s set them to the zero Y axis which on this graph is ‘93%’. First we set the points and then store the coordinates in an array to use for the lines. let xAxis = document.getElementById('x-axis'); let points = document.getElementById('points'); let ptCoords = []; let xAxisValues = [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ]; xAxisValues.forEach((val, ind) => { let xMargin = 11; let len = xAxisValues.length; let xVal = (ind/len * 100 + xMargin) + '%'; let yVal = '99.8%'; let coords = { x: 0, y: 0 }; let text = document.createElementNS('http://www.w3.org/2000/svg', 'text'); text.setAttribute('x', xVal); text.setAttribute('y', yVal); text.textContent = val; let circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle'); circle.setAttribute('cx', xVal); circle.setAttribute('cy', '93%'); circle.setAttribute('r', '3'); coords.x = xVal, coords.y = '93%'; ptCoords.push(coords); xAxis.appendChild(text); points.appendChild(circle); }); When setting the coordinates for the lines it is a bit tricky because we must know the coordinates of two points before drawing the line between them. Rather than render the lines inside the same forEach loop why don’t we loop through the points after they are rendered before drawing the lines to keep things simple. Insert this at the end of your javascript file. ptCoords.forEach((val, ind) => { if(ind == ptCoords.length-1) return; let line = document.createElementNS('http://www.w3.org/2000/svg', 'line'); line.classList.add('data-line'); line.setAttribute('x1', val.x); line.setAttribute('y1', '93%'); line.setAttribute('x2', ptCoords[ind+1].x); line.setAttribute('y2', '93%'); lines.appendChild(line); }); Notice when we set ‘x2’ we are reaching for the next point in our array. Also since we are only drawing 6 lines and the array we loop through has 7 points we cut the loop short by using that if statement. So that is our basic graph. Feel free to play around with it. Like changing the Y values of the points. Of course on a real site we would be rendering real data so we would have to measure out where the points would be. This can be a great way to challenge yourself. CSS Javascript