npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

friendly-charts

v0.0.23

Published

Screen reader friendly charts with minimal effort

Downloads

4

Readme

Friendly Charts

Screen reader friendly charts with minimal effort

Install

npm install friendly-charts

Warning

Under construction

Features

You ✨ bring your own chart ✨ and Friendly Charts will:

  • expose a chart description to screen readers
  • make your chart keyboard accessible, allowing screen reader users to explore charts via keyboard interactions

Friendly Charts exports a number of functions that expect a DOM element as input. Friendly Charts is framework agnostic but in Svelte and Solid, for example, the exported functions can be applied as use directives/actions.

The interaction design for keyboard accessible charts is borrowed from Visa Chart Components:

  • TAB to the chart area
  • ENTER to drill down a level
  • ESCAPE to drill up a level
  • LEFT and RIGHT arrows to move between sibling elements
  • UP and DOWN arrows to move across groups

https://user-images.githubusercontent.com/12461810/191855514-5c5db343-8b8c-48e2-8829-bf7314938def.mov

Example

To make a chart friendly, apply the chart function to a container element, providing a chart title, subtitle and description. To make the chart keyboard accessible, use the axis, symbol and group functions to outline the chart's structure and provide accessible names. Friendly charts will then wire up the necessary focus and event management to let a screen reader user navigate the chart via keyboard interactions. For example, in Svelte:

<!-- BarChart.svelte -->

<script>
  import { chart } from 'friendly-charts';
  import locale from 'friendly-charts/locale/en-US';

  const data = [
    { category: "A", value: 82 },
    { category: "B", value: 50 },
    { category: "C", value: 10 }
  ];
</script>

<!-- FRIENDLY ACTION: declare a bar chart and link to its title and subtitle -->
<div use:chart={{ title: '.title', subtitle: '.subtitle', type: 'bar', locale }} >
  <hgroup>
    <h2 class="title"> Chart title </h2>
    <p class="subtitle"> Chart subtitle </p>
  </hgroup>

  <svg>
    <AxisX />
    <Bars {data} />
  </svg>
</div>
<!-- AxisX.svelte -->

<script>
  import { axis } from 'friendly-charts';
</script>

<!-- FRIENDLY ACTION: declare the x-axis, give it a label and link to its ticks -->
<g use:axis={{ label: 'Axis label', direction: 'x', ticks: '.tick text' }}>
  {#each [0, 20, 40, 60, 80] as tick (tick)}
    <g class="tick" transform="translate({getX(tick)},{boundedHeight})">
      <line y1={-boundedHeight} stroke="lightgray" />
      <text fill="gray"> {tick} </text>
    </g>
  {/each}
</g>
<!-- Bars.svelte -->

<script>
  import { symbol } from 'friendly-charts';

  export let data;
</script>

<g>
  {#each data as d, i (d.category)}
    <g class="bar" transform="translate(0,{getY(i)})">

      <!-- FRIENDLY ACTION: declare a bar and give it an accessible label -->
      <rect use:symbol={{ label: `${d.category}. ${d.value}`, type: 'bar' }} width={getX(d.value)} height={barHeight} fill="#0284c7" />

      <text y="0.75em"> {d.category} </text>
    </g>
  {/each}
</g>

CodeSandbox: https://codesandbox.io/s/friendly-tiny-bar-chart-10weeu?file=/App.svelte

Documentation

Exported are:

  • chart: exposes a chart description and makes the chart keyboard accessible
  • axis: declares an axis
  • symbol: declares a symbol (e.g. a single bar)
  • group: declares a group of symbols (e.g. bars of the same category)
  • focus: custom focus ring to highlight active elements on navigation (only needed to override default styles)

chart(node, options)

Exposes a chart description and makes the chart keyboard accessible

Examples:

import { chart } from 'friendly-charts';
import locale from 'friendly-charts/locale/en-US.json';

// minimal configuration
chart(node, {
	title: 'Chart title',
	type: 'bar',
	locale,
});

// .title and .subtitle are selectors that point to elements within the chart container
chart(node, { title: '.title', subtitle: '.subtitle', type: 'bar', locale });

// debug mode
chart(node, {
	title: 'Chart title',
	subtitle: 'Chart subtitle',
	type: 'bar',
	locale,
	debug: true,
});

Options:

  • title (required): chart title, either the title itself or a selector that points to an element within the chart container
  • type (required; 'line', 'bar', 'scatter', 'slope' or 'area'): chart type
  • locale (required): locale, usually imported from Friendly Charts, but you can bring your own
  • subtitle: chart subtitle, either the subtitle itself or a selector that points to an element within the chart container
  • summary: brief summary of the chart, either the summary itself or a selector that points to an element within the chart container
  • purpose: an explanation of the chart's purpose, either the explanation itself or a selector that points to an element within the chart container
  • description: long description of the chart, either the description itself or a selector that points to an element within the chart container
  • axes: list of axis descriptions (see Section axis), only needed if an axis is not visually present in the chart
  • debug (default: false): display an overlay that shows what a screen reader would announce on interaction with the chart

axis(node, options)

Declares an axis

Examples:

import { axis } from 'friendly-charts';

// minimal configuration
axis(node, { label: 'Axis label' });

// .label is a selector that points to a descendant of `node`
axis(node, { label: '.label' });

// specify a direction
axis(node, { label: 'Axis label', direction: 'x' });

// infer ticks from the DOM by specifying a selector (by default, this is a continuous axis)
axis(node, { label: 'Axis label', direction: 'y', ticks: '.tick text' });

// set the axis to be categorical
axis(node, {
	label: 'Axis label',
	direction: 'x',
	type: 'categorical',
	ticks: '.tick text',
});

// specify ticks manually
axis(node, { label: 'Axis label', direction: 'y', ticks: [0, 2, 4, 6] });
axis(node, {
	label: 'Axis label',
	direction: 'x',
	type: 'categorical',
	ticks: ['Spain', 'Italy', 'France'],
});

Options:

  • label (required): axis label, either the label itself or a selector that points to a descendant of node
  • direction ('x' or 'y'): axis direction
  • type ('continuous' or 'categorical'; default: 'continuous'): axis type
  • ticks: either a list of ticks or a selector that points to the tick labels (must be descendants of node)

symbol(node, options)

Declares a symbol (e.g. a single bar)

Examples:

import { symbol } from 'friendly-charts';

// line
symbol(node, { label: 'Symbol label', type: 'line' });

// bar
symbol(node, { label: 'Symbol label', type: 'bar' });

// symbol contained within a group with id `some-unique-group-id`
// (only necessary if the symbol is not nested within that group)
symbol(node, {
	label: 'Symbol label',
	type: 'point',
	parentId: 'some-unique-group-id',
});

Options:

  • label (required): symbol label, either the label itself or a selector that points to the label element
  • type (required; 'line', 'bar', 'point' or 'area'): symbol type
  • id: symbol id (automatically generated if not given)
  • parentId: id of the group that contains the symbol, only needed if the symbol is not a descendant of the group element

group(node, options)

Declares a group of symbols (e.g. bars of the same category)

Examples:

import { group } from 'friendly-charts';

// minimal configuration
group(node, { label: 'Group label' });

// a group that also represents a symbol of type line (e.g. a line that contains the points it is made of)
group(node, {
	label: 'Group label',
	id: 'unique-group-id',
	type: 'line',
});

Options:

  • label (required): group label, either the label itself or a selector that points to a descendant of node
  • id: group id (automatically generated if not given)
  • type ('line', 'bar' or 'point'): some groups also represent a symbol of some type (e.g. a line that contains the points it is made of)

focus(node)

Focus ring to highlight active elements on navigation (only needed to override default styles)

Examples:

import { focus } from 'friendly-charts';

// the given element will be used as focus ring
focus(node);