← Component Library
Infographic Components

Infographic Patterns

Infographics tell a story with numbers and visuals. These are the building blocks — big stats, donuts, comparison bars, process flows, timelines, pictographs — and a finished poster that combines them. Every piece is pure HTML & CSS, with the code and when-to-use notes.

01 · Big Stat Callout

Lead with one giant number

The simplest infographic element: a single statistic blown up large with a short label. It anchors a section and gives readers one thing to remember.

73%
of learners finish the course
based on 2,400 students · 2026

HTML & CSS

<div class="big-stat">
  <p class="num">73%</p>
  <p class="label">of learners finish the course</p>
  <p class="sub">based on 2,400 students · 2026</p>
</div>

.num   { font-size: clamp(3rem, 12vw, 5rem); font-weight: 800; color: #ec4899; line-height: 1; }
.label { font-size: 1rem; color: #475569; font-weight: 600; margin-top: 6px; }
.sub   { font-size: .82rem; color: #94a3b8; margin-top: 4px; }

When to use: the headline figure of a report or hero — one number you want to stick.

02 · Stat Grid

A row of key numbers with icons

Three or four stats side by side, each with an icon, value, and label. A responsive grid wraps them on small screens.

12k
Students
86
Courses
9.4k
Certificates
4.8
Avg. rating

HTML

<div class="grid">
  <div class="stat">
    <div class="icon" style="background:#ec4899;">👥</div>
    <div class="num" data-target="12000" data-suffix="k">0</div>
    <div class="lbl">Students</div>
  </div>
  <div class="stat">
    <div class="icon" style="background:#8b5cf6;">📖</div>
    <div class="num" data-target="86">0</div>
    <div class="lbl">Courses</div>
  </div>
  <div class="stat">
    <div class="icon" style="background:#0ea5e9;">🎓</div>
    <div class="num" data-target="9400" data-suffix="k">0</div>
    <div class="lbl">Certificates</div>
  </div>
  <div class="stat">
    <div class="icon" style="background:#22c55e;">⭐</div>
    <div class="num" data-target="4.8">0</div>
    <div class="lbl">Avg. rating</div>
  </div>
</div>

CSS

.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 16px; text-align: center; }
.stat .icon { width: 50px; height: 50px; border-radius: 14px; display: grid; place-items: center;
              margin: 0 auto 10px; font-size: 1.5rem; color: #fff; }
.stat .num  { font-size: 1.9rem; font-weight: 800; color: #0f172a; line-height: 1; }
.stat .lbl  { font-size: .8rem; color: #64748b; font-weight: 600; margin-top: 4px; }

JavaScript — count up on scroll-into-view

// Animates each .stat .num from 0 to data-target when scrolled into view.
const counters = document.querySelectorAll('.stat .num[data-target]');

function format(n, target, suffix) {
  const decimals = String(target).indexOf('.') > -1 ? 1 : 0;
  if (suffix === 'k') return (n / 1000).toFixed(n < 10000 ? 1 : 0).replace(/\.0$/, '') + 'k';
  if (decimals) return n.toFixed(1);
  return Math.round(n).toString();
}

function animate(el) {
  const target = parseFloat(el.dataset.target);
  const suffix = el.dataset.suffix || '';
  const duration = 1400;
  let start = null;
  function step(t) {
    if (start === null) start = t;
    const p = Math.min((t - start) / duration, 1);
    const eased = 1 - Math.pow(1 - p, 3);
    el.textContent = format(target * eased, target, suffix);
    if (p < 1) requestAnimationFrame(step);
  }
  requestAnimationFrame(step);
}

const io = new IntersectionObserver((entries) => {
  entries.forEach((e) => {
    if (e.isIntersecting) { animate(e.target); io.unobserve(e.target); }
  });
}, { threshold: 0.4 });

counters.forEach((c) => io.observe(c));

When to use: a quick “by the numbers” strip. The JS above turns each data-target into a smooth count-up using an IntersectionObserver.

03 · Percentage Donut

Show a proportion as a ring

A conic-gradient fills the ring to a percentage; a white circle on top makes the hole. Change one variable (--p) per donut — no SVG, no JavaScript.

73%

Completion

48%

Mobile users

91%

Satisfaction

HTML & CSS

<div class="donuts">
  <div class="donut-item">
    <div class="donut" style="--p:73; --c:#ec4899"><span>73%</span></div>
    <p class="cap">Completion</p>
  </div>
  <div class="donut-item">
    <div class="donut" style="--p:48; --c:#8b5cf6"><span>48%</span></div>
    <p class="cap">Mobile users</p>
  </div>
  <div class="donut-item">
    <div class="donut" style="--p:91; --c:#22c55e"><span>91%</span></div>
    <p class="cap">Satisfaction</p>
  </div>
</div>

.donuts { display: flex; gap: 26px; flex-wrap: wrap; justify-content: center; }
.donut-item { text-align: center; }
.donut { width: 120px; height: 120px; border-radius: 50%; display: grid; place-items: center;
  background: conic-gradient(var(--c) calc(var(--p) * 1%), #e5e7eb 0); }
.donut span { width: 88px; height: 88px; border-radius: 50%; background: #fff;
  display: grid; place-items: center; font-weight: 800; color: #0f172a; }  /* the hole */
.cap { font-size: .8rem; color: #64748b; margin-top: 8px; }

When to use: a single proportion (one part of a whole). For multiple slices, use the pie chart instead.

04 · Comparison Bars

Compare values at a glance

Labelled horizontal bars make sizes easy to compare — bar length encodes the value. Each bar is a track with a coloured fill set by width.

HTML92%
CSS78%
JavaScript64%

HTML & CSS

<div class="bars">
  <div class="bar">
    <div class="top"><span>HTML</span><span>92%</span></div>
    <div class="track"><div class="fill" style="width:92%;background:#ec4899;"></div></div>
  </div>
  <div class="bar">
    <div class="top"><span>CSS</span><span>78%</span></div>
    <div class="track"><div class="fill" style="width:78%;background:#8b5cf6;"></div></div>
  </div>
  <div class="bar">
    <div class="top"><span>JavaScript</span><span>64%</span></div>
    <div class="track"><div class="fill" style="width:64%;background:#0ea5e9;"></div></div>
  </div>
</div>

.bars  { display: grid; gap: 12px; max-width: 460px; }
.top   { display: flex; justify-content: space-between; font-size: .82rem; color: #334155; font-weight: 600; margin-bottom: 4px; }
.track { background: #e5e7eb; border-radius: 999px; height: 18px; overflow: hidden; }
.fill  { height: 100%; border-radius: 999px; background: #ec4899; transition: width 1s ease; }

JavaScript — reveal each bar on scroll-into-view

// Tweens each .fill from 0% to data-fill% when the bar scrolls into view.
// The CSS transition handles the actual animation.
const fills = document.querySelectorAll('.bar .fill[data-fill]');

// Set everyone to 0 first
fills.forEach((f) => { f.style.width = '0%'; });

const io = new IntersectionObserver((entries) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      const fill = entry.target;
      requestAnimationFrame(() => { fill.style.width = fill.dataset.fill + '%'; });
      io.unobserve(fill);
    }
  });
}, { threshold: 0.4 });

fills.forEach((f) => io.observe(f));

When to use: skill levels, survey results, “us vs. them” comparisons. For axes and gridlines, see the bar graph.

05 · Process / Step Flow

Walk through a sequence

Numbered circles joined by a line show an ordered process — “how it works” in three or four steps. The connecting line is a pseudo-element behind each circle.

1

Plan

Sketch your idea

2

Build

Write the code

3

Test

Check it works

4

Ship

Deploy it live

CSS — the connecting line

.step { flex: 1; text-align: center; position: relative; }
.step .circle { width: 46px; height: 46px; border-radius: 50%; background: #ec4899; color: #fff;
                display: grid; place-items: center; margin: 0 auto; position: relative; z-index: 1; }
.step:not(:last-child)::after {            /* line to the next step */
  content: ""; position: absolute; top: 23px; left: 50%; width: 100%; height: 3px; background: #f9a8d4;
}

When to use: onboarding, “how it works,” recipes — any ordered set of stages.

06 · Timeline

Events along a vertical line

A vertical line with dated dots tells a story over time — company history, a roadmap, a project log. The line is one pseudo-element; each event places a dot on it.

2023
Idea
The first sketch on a napkin.
2024
Launch
First version goes live.
2026
Growth
12,000 students and counting.

CSS

.timeline { position: relative; padding-left: 28px; }
.timeline::before { content: ""; position: absolute; left: 8px; top: 0; bottom: 0; width: 3px; background: #f9a8d4; }
.event::before    { content: ""; position: absolute; left: -24px; width: 13px; height: 13px;
                    border-radius: 50%; background: #ec4899; }   /* the dot */

When to use: history, roadmaps, step-by-step stories with dates. For an interactive horizontal version see Timeline Lab.

07 · Fact Cards

Bite-size facts with icons

When the data is qualitative, not numeric, icon + headline + sentence cards keep it scannable. A responsive grid lets them flow.

Eco-friendly
Runs on 100% renewable energy.
Worldwide
Learners in 36 countries.
Self-paced
Learn on your own schedule.

HTML

<div class="facts">
  <div class="fact">
    <i class="icon">🌿</i>
    <div><p class="title">Eco-friendly</p><p>Runs on 100% renewable energy.</p></div>
  </div>
  <div class="fact">
    <i class="icon">🌎</i>
    <div><p class="title">Worldwide</p><p>Learners in 36 countries.</p></div>
  </div>
  <div class="fact">
    <i class="icon">🕐</i>
    <div><p class="title">Self-paced</p><p>Learn on your own schedule.</p></div>
  </div>
</div>

.facts { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 14px; }
.fact  { display: flex; gap: 12px; align-items: flex-start; background: #fff; border: 1px solid #e5e7eb; border-radius: 12px; padding: 16px; }
.fact .icon  { color: #ec4899; font-size: 1.6rem; flex: 0 0 auto; }
.fact .title { font-weight: 700; color: #0f172a; font-size: .9rem; }

When to use: features, benefits, or “did you know” facts that aren't numbers.

08 · Ranked List

A top-N with bars

Combine a rank number, a label, and a bar to show an ordered leaderboard of values — most popular topics, top sellers, busiest days.

1CSS1,240
2HTML1,010
3JavaScript790
4Design560

HTML

<div class="rank">
  <div class="row">
    <span class="pos">1</span><span class="name">CSS</span>
    <span class="bar"><i style="width:100%"></i><b>1,240</b></span>
  </div>
  <div class="row">
    <span class="pos">2</span><span class="name">HTML</span>
    <span class="bar"><i style="width:82%"></i><b>1,010</b></span>
  </div>
  <div class="row">
    <span class="pos">3</span><span class="name">JavaScript</span>
    <span class="bar"><i style="width:64%"></i><b>790</b></span>
  </div>
  <div class="row">
    <span class="pos">4</span><span class="name">Design</span>
    <span class="bar"><i style="width:45%"></i><b>560</b></span>
  </div>
</div>

.rank { display: grid; gap: 10px; max-width: 460px; }
.row  { display: flex; align-items: center; gap: 12px; }
.pos  { width: 26px; height: 26px; border-radius: 50%; background: #fce7f3; color: #be185d;
        font-weight: 800; display: grid; place-items: center; font-size: .8rem; flex: 0 0 auto; }
.row:nth-child(1) .pos { background: #ec4899; color: #fff; }
.name { width: 84px; font-size: .82rem; color: #334155; font-weight: 600; flex: 0 0 auto; }
.bar  { flex: 1; background: #fce7f3; border-radius: 8px; height: 26px; position: relative; overflow: hidden; }
.bar i { position: absolute; inset: 0 auto 0 0; background: #ec4899; border-radius: 8px; }
.bar b { position: absolute; right: 8px; top: 50%; transform: translateY(-50%); font-size: .76rem; color: #831843; z-index: 1; }

When to use: “top 5” lists where both rank and magnitude matter.

09 · Pictograph

Count with repeated icons

Instead of a bar, repeat an icon and fill in part of it — “7 in 10.” Pictographs make proportions feel human and concrete.

7 in 10 students study on their phone

HTML — filled vs. empty icons

<div class="picto">
  <div class="row">
    <span class="person on">👤</span><span class="person on">👤</span><span class="person on">👤</span><span class="person on">👤</span><span class="person on">👤</span><span class="person on">👤</span><span class="person on">👤</span><span class="person">👤</span><span class="person">👤</span><span class="person">👤</span>
  </div>
  <p class="cap"><b>7 in 10</b> students study on their phone</p>
</div>

.person    { font-size: 1.7rem; color: #e5e7eb; }   /* empty */
.person.on { color: #ec4899; }                      /* filled */
.cap   { font-size: .85rem; color: #475569; margin-top: 8px; }
.cap b { color: #ec4899; }

When to use: “X out of Y” ratios about people or things — more relatable than a percentage alone.

10 · Putting It Together

A complete infographic poster

A real infographic combines these blocks into one story with a title, a few headline stats, a chart, and a takeaway. Here's a small poster built only from the pieces above.

2026 Report

How students learn web design

12k
Students
73%
Finish rate
4.8★
Rating
Watch on mobile70%
Watch on desktop30%

Takeaway: mobile-first design isn't optional.

Design tips: pick 2–3 colours and stick to them, keep one idea per block, guide the eye top-to-bottom, and always cite your source. Build each block with the snippets above, then stack them in a single column.

Related building blocks: the bar graph, pie chart, stat counter, and progress ring all slot into infographics too.
11 · Full Page

The whole infographic as one file

Every block above — big stat, stat grid, donut, comparison bars, process flow, timeline, ranked list, and pictograph — combined into one complete HTML file, arranged as a single-column infographic poster. Copy it into a new file called infographic.html, open it in a browser, and it just works — no libraries, no build step. Here's the live result, then the full code.

Live preview

Complete HTML — copy this whole file

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>2026 Learning Report — Infographic</title>
  <style>
    * { box-sizing: border-box; margin: 0; padding: 0; }
    body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
           background: #f6f7fb; color: #1f2433; line-height: 1.6; padding: 24px; }
    .poster { max-width: 640px; margin: 0 auto; background: linear-gradient(160deg, #fff0f6, #fff);
              border: 1px solid #fbcfe8; border-radius: 18px; padding: 32px 28px; }

    /* header */
    .ph { text-align: center; margin-bottom: 28px; }
    .ph .k { font-size: .72rem; text-transform: uppercase; letter-spacing: .18em; color: #ec4899; font-weight: 800; }
    .ph h1 { font-size: 1.7rem; color: #0f172a; margin-top: 4px; }

    /* big stat */
    .big-stat { text-align: center; margin-bottom: 28px; }
    .big-stat .num { font-size: clamp(3rem, 12vw, 4.5rem); font-weight: 800; color: #ec4899; line-height: 1; letter-spacing: -.03em; }
    .big-stat .label { font-size: 1rem; color: #475569; font-weight: 600; margin-top: 6px; }
    .big-stat .sub { font-size: .82rem; color: #94a3b8; margin-top: 4px; }

    /* stat grid */
    .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 16px; text-align: center; margin-bottom: 28px; }
    .stat .icon { width: 48px; height: 48px; border-radius: 14px; display: grid; place-items: center; margin: 0 auto 8px; font-size: 1.4rem; color: #fff; }
    .stat .num { font-size: 1.8rem; font-weight: 800; color: #0f172a; line-height: 1; }
    .stat .lbl { font-size: .8rem; color: #64748b; font-weight: 600; margin-top: 4px; }

    .block-title { font-size: .72rem; text-transform: uppercase; letter-spacing: .12em; color: #ec4899; font-weight: 800; margin-bottom: 12px; text-align: center; }

    /* donuts */
    .donuts { display: flex; gap: 22px; flex-wrap: wrap; justify-content: center; margin-bottom: 28px; }
    .donut { width: 110px; height: 110px; border-radius: 50%; display: grid; place-items: center;
             background: conic-gradient(var(--c) calc(var(--p) * 1%), #e5e7eb 0); }
    .donut span { width: 80px; height: 80px; border-radius: 50%; background: #fff; display: grid; place-items: center; font-weight: 800; font-size: 1.2rem; color: #0f172a; }
    .donut-cap { font-size: .8rem; color: #64748b; margin-top: 8px; text-align: center; }

    /* comparison bars */
    .bars { display: grid; gap: 12px; margin-bottom: 28px; }
    .bar .top { display: flex; justify-content: space-between; font-size: .82rem; color: #334155; font-weight: 600; margin-bottom: 4px; }
    .bar .track { background: #e5e7eb; border-radius: 999px; height: 18px; overflow: hidden; }
    .bar .fill { height: 100%; border-radius: 999px; }

    /* process flow */
    .steps { display: flex; gap: 0; flex-wrap: wrap; margin-bottom: 28px; }
    .step { flex: 1; min-width: 110px; text-align: center; position: relative; padding: 0 6px; }
    .step .circle { width: 44px; height: 44px; border-radius: 50%; background: #ec4899; color: #fff; font-weight: 800; display: grid; place-items: center; margin: 0 auto 8px; position: relative; z-index: 1; }
    .step:not(:last-child)::after { content: ""; position: absolute; top: 22px; left: 50%; width: 100%; height: 3px; background: #f9a8d4; z-index: 0; }
    .step h4 { font-size: .9rem; color: #0f172a; margin-bottom: 2px; }
    .step p { font-size: .76rem; color: #64748b; }

    /* timeline */
    .timeline { position: relative; padding-left: 28px; margin-bottom: 28px; }
    .timeline::before { content: ""; position: absolute; left: 8px; top: 4px; bottom: 4px; width: 3px; background: #f9a8d4; }
    .event { position: relative; margin-bottom: 14px; }
    .event::before { content: ""; position: absolute; left: -24px; top: 3px; width: 13px; height: 13px; border-radius: 50%; background: #ec4899; border: 2px solid #fff; box-shadow: 0 0 0 2px #f9a8d4; }
    .event .yr { font-weight: 800; color: #ec4899; font-size: .82rem; }
    .event .t { color: #0f172a; font-weight: 700; font-size: .9rem; }
    .event .d { color: #64748b; font-size: .8rem; }

    /* ranked list */
    .rank { display: grid; gap: 10px; margin-bottom: 28px; }
    .row { display: flex; align-items: center; gap: 12px; }
    .pos { width: 26px; height: 26px; border-radius: 50%; background: #fce7f3; color: #be185d; font-weight: 800; display: grid; place-items: center; font-size: .8rem; flex: 0 0 auto; }
    .row:nth-child(1) .pos { background: #ec4899; color: #fff; }
    .name { width: 84px; font-size: .82rem; color: #334155; font-weight: 600; flex: 0 0 auto; }
    .rbar { flex: 1; background: #fce7f3; border-radius: 8px; height: 26px; position: relative; overflow: hidden; }
    .rbar i { position: absolute; inset: 0 auto 0 0; background: #ec4899; border-radius: 8px; }
    .rbar b { position: absolute; right: 8px; top: 50%; transform: translateY(-50%); font-size: .76rem; color: #831843; z-index: 1; }

    /* pictograph */
    .picto { text-align: center; margin-bottom: 24px; }
    .person { font-size: 1.7rem; color: #e5e7eb; }
    .person.on { color: #ec4899; }
    .cap { font-size: .85rem; color: #475569; margin-top: 8px; }
    .cap b { color: #ec4899; }

    .takeaway { text-align: center; font-size: .85rem; color: #64748b; padding-top: 20px; border-top: 1px dashed #fbcfe8; }
    .takeaway b { color: #ec4899; }
  </style>
</head>
<body>

  <div class="poster">
    <div class="ph">
      <p class="k">2026 Report</p>
      <h1>How students learn web design</h1>
    </div>

    <!-- big stat -->
    <div class="big-stat">
      <p class="num">73%</p>
      <p class="label">of learners finish the course</p>
      <p class="sub">based on 2,400 students · 2026</p>
    </div>

    <!-- stat grid -->
    <div class="grid">
      <div class="stat"><div class="icon" style="background:#ec4899;">👥</div><p class="num">12k</p><p class="lbl">Students</p></div>
      <div class="stat"><div class="icon" style="background:#8b5cf6;">📖</div><p class="num">86</p><p class="lbl">Courses</p></div>
      <div class="stat"><div class="icon" style="background:#0ea5e9;">🎓</div><p class="num">9.4k</p><p class="lbl">Certificates</p></div>
      <div class="stat"><div class="icon" style="background:#22c55e;">⭐</div><p class="num">4.8</p><p class="lbl">Avg. rating</p></div>
    </div>

    <!-- donuts -->
    <p class="block-title">Key proportions</p>
    <div class="donuts">
      <div><div class="donut" style="--p:73;--c:#ec4899;"><span>73%</span></div><p class="donut-cap">Completion</p></div>
      <div><div class="donut" style="--p:48;--c:#8b5cf6;"><span>48%</span></div><p class="donut-cap">Mobile users</p></div>
      <div><div class="donut" style="--p:91;--c:#22c55e;"><span>91%</span></div><p class="donut-cap">Satisfaction</p></div>
    </div>

    <!-- comparison bars -->
    <p class="block-title">Where they watch</p>
    <div class="bars">
      <div class="bar"><div class="top"><span>Watch on mobile</span><span>70%</span></div><div class="track"><div class="fill" style="width:70%;background:#ec4899;"></div></div></div>
      <div class="bar"><div class="top"><span>Watch on desktop</span><span>30%</span></div><div class="track"><div class="fill" style="width:30%;background:#8b5cf6;"></div></div></div>
    </div>

    <!-- process flow -->
    <p class="block-title">How a course works</p>
    <div class="steps">
      <div class="step"><div class="circle">1</div><h4>Plan</h4><p>Pick a track</p></div>
      <div class="step"><div class="circle">2</div><h4>Build</h4><p>Write code</p></div>
      <div class="step"><div class="circle">3</div><h4>Test</h4><p>Check it</p></div>
      <div class="step"><div class="circle">4</div><h4>Ship</h4><p>Go live</p></div>
    </div>

    <!-- timeline -->
    <p class="block-title">Our story</p>
    <div class="timeline">
      <div class="event"><div class="yr">2023</div><div class="t">Idea</div><div class="d">The first sketch on a napkin.</div></div>
      <div class="event"><div class="yr">2024</div><div class="t">Launch</div><div class="d">First version goes live.</div></div>
      <div class="event"><div class="yr">2026</div><div class="t">Growth</div><div class="d">12,000 students and counting.</div></div>
    </div>

    <!-- ranked list -->
    <p class="block-title">Most popular topics</p>
    <div class="rank">
      <div class="row"><span class="pos">1</span><span class="name">CSS</span><span class="rbar"><i style="width:100%;"></i><b>1,240</b></span></div>
      <div class="row"><span class="pos">2</span><span class="name">HTML</span><span class="rbar"><i style="width:82%;"></i><b>1,010</b></span></div>
      <div class="row"><span class="pos">3</span><span class="name">JavaScript</span><span class="rbar"><i style="width:64%;"></i><b>790</b></span></div>
      <div class="row"><span class="pos">4</span><span class="name">Design</span><span class="rbar"><i style="width:45%;"></i><b>560</b></span></div>
    </div>

    <!-- pictograph -->
    <div class="picto">
      <div id="pictoRow"></div>
      <p class="cap"><b>7 in 10</b> students study on their phone</p>
    </div>

    <p class="takeaway"><b>Takeaway:</b> mobile-first design isn't optional. · Source: 2026 Learning Report</p>
  </div>

  <script>
    // build the pictograph: 7 filled people out of 10
    var row = document.getElementById('pictoRow');
    var html = '';
    for (var i = 0; i < 10; i++) {
      html += '<span class="person' + (i < 7 ? ' on' : '') + '">👤</span>';
    }
    row.innerHTML = html;
  </script>
</body>
</html>
That's a real, standalone infographic. The live preview above is rendered from the exact code in the box — so what you copy is precisely what you see.
12 · Related

Keep building

See Data Patterns, Visual Design Patterns, and SVG Lab — or browse the full Component Library.