Sparkline

스파크라인

A tiny axis-less trend line that sits beside text — it adds "recent shape" context to a single number.

Also known as: Inline trend line
···
html
<div class="viz">
  <div class="tile">
    <div class="lbl">주간 방문자</div>
    <div class="row">
      <div class="num" id="num">1,284</div>
      <div class="delta" id="delta">▲ 4.2%</div>
    </div>
  </div>
  <svg id="svg"></svg>
</div>
css
body{display:block}
.viz{display:grid;grid-template-rows:auto 1fr;width:100%;height:100%;padding:10px 12px;box-sizing:border-box;gap:2px}
.lbl{font:600 10px/1 system-ui,sans-serif;color:var(--muted)}
.row{display:flex;align-items:baseline;gap:8px}
.num{font:700 20px/1 system-ui,sans-serif;color:var(--fg)}
.delta{font:700 11px/1 system-ui,sans-serif}
.delta.up{color:var(--accent-3)}
.delta.down{color:var(--accent-2)}
svg{display:block;width:100%;height:100%;overflow:visible}
.ln{fill:none;stroke:var(--muted);stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round;opacity:.55}
.dot{fill:var(--accent)}
js
var svg = document.getElementById('svg');
var numEl = document.getElementById('num');
var deltaEl = document.getElementById('delta');
var NS = 'http://www.w3.org/2000/svg';
function se(t){ return document.createElementNS(NS,t); }
function sa(e,o){ for (var k in o) e.setAttribute(k,o[k]); }
function R(a,b){ return a + Math.random()*(b-a); }

var N = 14;
var series = [];
var v = 50;
for (var i=0;i<N;i++){ v = Math.max(15,Math.min(90, v+R(-14,14))); series.push(v); }
var cur = series.slice();
var W=300, H=200;

function measure(){
  var r = svg.getBoundingClientRect();
  W = Math.max(r.width,10); H = Math.max(r.height,10);
  sa(svg,{ viewBox: '0 0 ' + W + ' ' + H });
}

function draw(){
  svg.innerHTML = '';
  var padX=4, padY=8;
  var pw=W-padX*2, ph=H-padY*2;
  var pts = cur.map(function(val,i){ return { x: padX+(pw*i)/(N-1), y: padY+ph-(val/100)*ph }; });
  var d = pts.map(function(p,i){ return (i===0?'M':'L')+p.x.toFixed(1)+','+p.y.toFixed(1); }).join(' ');
  var path = se('path');
  sa(path,{d:d,class:'ln'});
  svg.appendChild(path);
  var last = pts[pts.length-1];
  var dot = se('circle');
  sa(dot,{cx:last.x,cy:last.y,r:3,class:'dot'});
  svg.appendChild(dot);
}

function updateFacts(){
  var val = cur[cur.length-1];
  var prev = cur[cur.length-2];
  numEl.textContent = Math.round(val*14).toLocaleString();
  var pct = ((val-prev)/prev*100);
  var up = pct >= 0;
  deltaEl.textContent = (up?'▲ ':'▼ ') + Math.abs(pct).toFixed(1) + '%';
  deltaEl.className = 'delta ' + (up?'up':'down');
}

function animate(){
  var from = cur.slice();
  var t0 = performance.now();
  function step(t){
    var p = Math.min(1,(t-t0)/700);
    var e = 1-Math.pow(1-p,3);
    cur = from.map(function(vv,i){ return vv+(series[i]-vv)*e; });
    draw();
    if (p<1) requestAnimationFrame(step);
  }
  requestAnimationFrame(step);
}

new ResizeObserver(function(){ measure(); draw(); }).observe(svg);
measure();
draw();
updateFacts();
setInterval(function(){
  series.shift();
  var last = series[series.length-1];
  series.push(Math.max(12,Math.min(92, last+R(-16,16))));
  cur = [series[0]].concat(cur.slice(1));
  animate();
  updateFacts();
}, 2600);

Edward Tufte's term for it is a "word-sized graphic." A sparkline isn't built for reading exact values — it only needs to convey the rough shape: rising, falling, volatile. No axis, no ticks, no legend is the correct default, not an omission.

It means nothing alone. It needs a label (a trend of *what*), the current value, and ideally a delta right beside it — that's what completes the context the line is pointing at. Mark only the last point in the accent color, as "you are here."

Don't add a tooltip or let people zoom into it — the moment you do, it's not a sparkline anymore, it's a small line chart. If the values need close reading, just use a line chart from the start.

When to use

Use it in a table row or beside a stat-tile number, wherever you need "recent trend" as a side note, not the main event.