Interactive Examples

Popups, links, and charts that update themselves

Pop-Ups

A <dc-popup> inside a data element turns that element into its own trigger. trigger="hover" opens on pointer, trigger="click" on click — and because the popup body is markup you wrote, it can hold anything, including a link.

Bar Chart with Hover Popups

<dc-chart width="500" height="350">
  <dc-title>Quarterly Revenue (in thousands)</dc-title>
  <dc-bar value="150" fill="#FF6B6B" label="Q1">
    <dc-popup trigger="hover">
      <strong>Q1 2024</strong><br>
      Revenue: $150k<br>
      Growth: +12%
    </dc-popup>
  </dc-bar>
  <dc-bar value="180" fill="#4ECDC4" label="Q2">
    <dc-popup trigger="hover">
      <strong>Q2 2024</strong><br>
      Revenue: $180k<br>
      Growth: +20%
    </dc-popup>
  </dc-bar>
  <dc-bar value="165" fill="#45B7D1" label="Q3">
    <dc-popup trigger="hover">
      <strong>Q3 2024</strong><br>
      Revenue: $165k<br>
      Growth: -8%
    </dc-popup>
  </dc-bar>
  <dc-bar value="220" fill="#96CEB4" label="Q4">
    <dc-popup trigger="hover">
      <strong>Q4 2024</strong><br>
      Revenue: $220k<br>
      Growth: +33%
    </dc-popup>
  </dc-bar>
</dc-chart>
Quarterly Revenue (in thousands) Q1 2024
Revenue: $150k
Growth: +12%
Q2 2024
Revenue: $180k
Growth: +20%
Q3 2024
Revenue: $165k
Growth: -8%
Q4 2024
Revenue: $220k
Growth: +33%

Traffic Sources

<dc-pie-chart width="500" height="350">
  <dc-title>Traffic Sources</dc-title>
  <dc-pie-slice value="30" fill="#FF6B6B" label="Desktop">
    <dc-popup trigger="hover">
      <strong>Desktop Traffic</strong><br>
      Sessions: 3,240<br>
      Avg. Duration: 4m 32s
    </dc-popup>
  </dc-pie-slice>
  <dc-pie-slice value="45" fill="#4ECDC4" label="Mobile">
    <dc-popup trigger="hover">
      <strong>Mobile Traffic</strong><br>
      Sessions: 4,860<br>
      Avg. Duration: 2m 15s
    </dc-popup>
  </dc-pie-slice>
  <dc-pie-slice value="15" fill="#45B7D1" label="Tablet">
    <dc-popup trigger="hover">
      <strong>Tablet Traffic</strong><br>
      Sessions: 1,620<br>
      Avg. Duration: 3m 45s
    </dc-popup>
  </dc-pie-slice>
  <dc-pie-slice value="10" fill="#96CEB4" label="Other">
    <dc-popup trigger="hover">
      <strong>Other Devices</strong><br>
      Sessions: 1,080<br>
      Avg. Duration: 1m 20s
    </dc-popup>
  </dc-pie-slice>
</dc-pie-chart>
Traffic Sources Desktop Traffic
Sessions: 3,240
Avg. Duration: 4m 32s
Mobile Traffic
Sessions: 4,860
Avg. Duration: 2m 15s
Tablet Traffic
Sessions: 1,620
Avg. Duration: 3m 45s
Other Devices
Sessions: 1,080
Avg. Duration: 1m 20s

Sales Funnel with Click Popups

<dc-funnel-chart width="500" height="350" palette="blues">
  <dc-title>Sales Funnel - Click a Stage!</dc-title>
  <dc-funnel-stage value="1000" label="Visitors">
    <dc-popup trigger="click">
      <strong>Website Visitors</strong><br>
      Total: 1,000 unique visitors<br>
      Source: Organic + Paid
    </dc-popup>
  </dc-funnel-stage>
  <dc-funnel-stage value="450" label="Leads">
    <dc-popup trigger="click">
      <strong>Qualified Leads</strong><br>
      Count: 450 (45% conversion)<br>
      <a href="#">View Lead Details</a>
    </dc-popup>
  </dc-funnel-stage>
  <dc-funnel-stage value="150" label="Opportunities">
    <dc-popup trigger="click">
      <strong>Sales Opportunities</strong><br>
      Count: 150 (33% conversion)<br>
      Avg. Deal Size: $5,200
    </dc-popup>
  </dc-funnel-stage>
  <dc-funnel-stage value="50" label="Customers">
    <dc-popup trigger="click">
      <strong>New Customers</strong><br>
      Count: 50 (33% close rate)<br>
      Total Revenue: $260,000
    </dc-popup>
  </dc-funnel-stage>
</dc-funnel-chart>
Sales Funnel - Click a Stage! Website Visitors
Total: 1,000 unique visitors
Source: Organic + Paid
Qualified Leads
Count: 450 (45% conversion)
View Lead Details
Sales Opportunities
Count: 150 (33% conversion)
Avg. Deal Size: $5,200
New Customers
Count: 50 (33% close rate)
Total Revenue: $260,000

Links

Any data element can carry href and target, which makes the shape itself a link. The line chart below shows the two approaches side by side: New York opens a popup containing a link, San Francisco navigates directly.

Line Chart with Click Actions

<dc-chart width="500" height="350">
  <dc-title>City Temperature Comparison - Click a Line!</dc-title>
  <dc-line stroke="#2196F3" label="New York">
    <dc-popup trigger="click">
      <strong>New York City</strong><br>
      Population: 8.3 million<br>
      <a href="https://en.wikipedia.org/wiki/New_York_City" target="_blank"
         style="color: #64B5F6; text-decoration: underline;">
        View on Wikipedia
      </a>
    </dc-popup>
    <dc-point value="15" label="Mon"></dc-point>
    <dc-point value="18" label="Tue"></dc-point>
    <dc-point value="22" label="Wed"></dc-point>
    <dc-point value="20" label="Thu"></dc-point>
    <dc-point value="25" label="Fri"></dc-point>
  </dc-line>
  <dc-line stroke="#FF5722" label="San Francisco"
           href="https://en.wikipedia.org/wiki/San_Francisco"
           target="_blank">
    <dc-point value="12" label="Mon"></dc-point>
    <dc-point value="16" label="Tue"></dc-point>
    <dc-point value="19" label="Wed"></dc-point>
    <dc-point value="23" label="Thu"></dc-point>
    <dc-point value="21" label="Fri"></dc-point>
  </dc-line>
</dc-chart>
City Temperature Comparison - Click a Line! New York City
Population: 8.3 million
View on Wikipedia

Bar Chart with Links

<dc-chart width="500" height="350">
  <dc-title>Top Programming Languages - Click to Learn More</dc-title>
  <dc-bar value="45" fill="#f1e05a" label="JavaScript"
          href="https://en.wikipedia.org/wiki/JavaScript"
          target="_blank"></dc-bar>
  <dc-bar value="38" fill="#3572A5" label="Python"
          href="https://en.wikipedia.org/wiki/Python_(programming_language)"
          target="_blank"></dc-bar>
  <dc-bar value="28" fill="#b07219" label="Java"
          href="https://en.wikipedia.org/wiki/Java_(programming_language)"
          target="_blank"></dc-bar>
  <dc-bar value="22" fill="#e34c26" label="TypeScript"
          href="https://en.wikipedia.org/wiki/TypeScript"
          target="_blank"></dc-bar>
</dc-chart>
Top Programming Languages - Click to Learn More

Dynamic Updates

Charts re-render when you change their markup, so the update is the DOM operation. Adding or removing an element changes the chart's structure; setting an attribute changes what an existing element says.

Adding and Removing Bars

<dc-chart id="dynamic-structure" width="500" height="350">
  <dc-title>Structure</dc-title>
  <dc-bar value="25" fill="#FF5722" label="A"></dc-bar>
  <dc-bar value="35" fill="#FFC107" label="B"></dc-bar>
  <dc-bar value="45" fill="#4CAF50" label="C"></dc-bar>
</dc-chart>

<script>
  function addBar() {
    const bar = document.createElement('dc-bar');
    bar.setAttribute('value', Math.floor(Math.random() * 50) + 20);
    bar.setAttribute('fill', colors[Math.floor(Math.random() * colors.length)]);
    bar.setAttribute('label', labels[labelIndex++ % labels.length]);
    document.getElementById('dynamic-structure').appendChild(bar);
  }

  function removeBar() {
    const bars = document.querySelectorAll('#dynamic-structure dc-bar');
    if (bars.length) bars[bars.length - 1].remove();
  }
</script>
Structure

Changing Values and Colours

<dc-chart id="dynamic-values" width="500" height="350">
  <dc-title>Values</dc-title>
  <dc-bar value="25" fill="#FF5722" label="A"></dc-bar>
  <dc-bar value="35" fill="#FFC107" label="B"></dc-bar>
  <dc-bar value="45" fill="#4CAF50" label="C"></dc-bar>
</dc-chart>

<script>
  function updateBar() {
    document.querySelector('#dynamic-values dc-bar')
      .setAttribute('value', Math.floor(Math.random() * 60) + 10);
  }

  // `fill`, not `color`. The `color` spelling was removed in 0.3.0, and
  // setting it now does nothing at all - silently.
  function changeColors() {
    document.querySelectorAll('#dynamic-values dc-bar').forEach(bar =>
      bar.setAttribute('fill', colors[Math.floor(Math.random() * colors.length)]));
  }
</script>
Values

Live Data

Neither chart below is redrawn by hand. One appends a <dc-point> every five seconds, the other re-sets the value attribute on bars that are already there — and because a chart observes its own children, both re-render on their own. There is no redraw(), no setData() and no requestUpdate() in either script.

Streaming Line Chart

<dc-chart id="live-line" width="500" height="350" show-value="false">
  <dc-title>Requests per second</dc-title>
  <dc-line stroke="#2196F3" label="web-01">
    <dc-point value="46" label="1"></dc-point>
    <!-- five more seed points, then the script appends -->
  </dc-line>
</dc-chart>

<script>
  const line = document.querySelector('#live-line dc-line');
  let sample = 6;

  setInterval(() => {
    const point = document.createElement('dc-point');
    point.setAttribute('value', String(30 + Math.round(Math.random() * 50)));
    point.setAttribute('label', String(++sample));
    line.appendChild(point);
  }, 5000);
</script>
Requests per second

The series only grows, so the points pack tighter as it runs. The axis thins its own labels once they start to collide, and show-value="false" keeps a number off every point — at this density they are unreadable anyway.

Live Bar Chart

<dc-chart id="live-bars" width="500" height="350" show-value="false">
  <dc-title>Requests by region</dc-title>
  <dc-bar value="48" label="North"></dc-bar>
  <dc-bar value="35" label="South"></dc-bar>
  <dc-bar value="62" label="East"></dc-bar>
  <dc-bar value="41" label="West"></dc-bar>
</dc-chart>

<style>
  /* `part="bar"` is a public contract, and the same <rect> survives a
     re-render, so an ordinary CSS transition smooths the change. */
  #live-bars::part(bar) {
    transition: y 900ms cubic-bezier(0.4, 0, 0.2, 1),
                height 900ms cubic-bezier(0.4, 0, 0.2, 1);
  }
</style>

<script>
  const bars = document.querySelectorAll('#live-bars dc-bar');

  setInterval(() => {
    bars.forEach(bar =>
      bar.setAttribute('value', String(20 + Math.round(Math.random() * 60))));
  }, 5000);
</script>
Requests by region

The bars glide because the library reuses the same <rect> elements and exposes them as part="bar". Value labels are off here for a reason: <text> has no CSS y, so a number would snap to its new position while the bar beneath it was still moving.

The same two moves cover most live-data cases. Appending is for a series that grows; re-setting value is for a fixed set of things whose numbers change, and it is the cheaper of the two — no element is created or destroyed, so nothing but the geometry moves. Only the second can be smoothed this way, for exactly that reason: a transition needs an element that persists across the update.

Interaction Events

Charts emit dc-click, dc-mouseenter, dc-mouseleave and dc-render. Events are dispatched from the element in your markup — the <dc-bar> — and bubble, so you can listen on the element, the chart, or the document. Click and hover the bars.

Listening for Events

<dc-chart id="events-demo" width="500" height="350">
  <dc-title>Monthly Sales</dc-title>
  <dc-bar value="120" fill="#4CAF50" label="Jan"></dc-bar>
  <!-- three more months -->
</dc-chart>

<script>
  document.querySelector('#events-demo')
    .addEventListener('dc-click', (event) => {
      const { label, value, percent } = event.detail;
      // percent is a decimal: 0.25 means 25%
      showDetail(label, value, percent);
    });
</script>
Monthly Sales

What Arrives

Selected: nothing yet - click a bar

Event log (most recent first):

waiting for events...