Core Concepts

Content vs Presentation

Ever since Ethan Marcotte coined the term "responsive web design", it has been a core value of the modern web. From the principle insight that content is distinct from presentation, Marcotte developed the idea that the style of a website can adapt based on device capabilities, without changing the semantic meaning of the content. This has actually become more important in the intervening years, thanks to media like Accelerated Mobile Pages, Facebook Instant Articles and Apple News, to say nothing of an ever-greater variety of screens. At the end of the day, authors rarely know the exact format in which their content will reach their readers.

Tools for applying responsive principles to charts and graphs have lagged. This is because charts are neither text (which typically compress horizontally and expand vertically as the viewport shrinks), nor images (which typically compress along both dimensions to maintain the aspect ratio). What is needed is a clean separation of concerns when talking about charts: what is content (in this case, data), and what is presentation (style)?

One solution comes from a book totally unrelated to the web: The Visual Display of Quantitative Information by Edward Tufte, first published in 1975. Tufte describes the parts of a graph that do not contain information "non-data ink". For example, the two charts below both show the same data:

The point is that the widths and the heights of the ranges represent different things: the vertical positions are content and cannot be changed because they directly represent data. The horizontal distances, even though they are also composed only of distances/pixels, are not a representation of data and are properly thought of as styles.

Usually, HTML is used to talk about content and CSS is used to talk about style. If CSS had a tukey-box-width property, we could then restyle the chart with media queries, with no change to the content. Of course this doesn't work: there is no HTML (or SVG) element for data on a graph, so it must be composed out of more fundamental SVG elements, which themselves represent shapes. Hypocube uses React to compose fundamental shapes into semantically meaningful chart components, and uses a custom CSS-like scheme to provide chart styles at varying levels of specificity. As with CSS, these styles can be inherited from parent elements.


What about responsiveness? All chart styles are contextual: which means they can optionally be provided as functions of the chart's actual width and height (in pixels). This allows a similar outcome to what can be achieved with media queries, except that they are constructed as functions of the width of the chart, not the whole page.

Read more about ChartStyle.

Defining Space

When specifying distance along an axis, sometimes we want to think in terms of actual data coordinates, and sometimes we need to think in pixels. Throughout the docs, we use the following conventions to denote different types of scale:

  • In the pixel scale we are specifying units of pixels. Except for chart styles and the gutter attribute, you probably won't have to think about the pixel scale often; the conversions are handled under the hood. Coordinates in the pixel scale can be used directly used in SVG or canvas to create shapes.

  • The Cartesian scale is a linear transformation of the pixel scale which is useful for thinking about our data. It is a Cartesian plane with an x-axis increasing from left to right and a y-axis increasing from bottom to top - just like in high school math. For most kinds of graphs, the Cartesian scale is enough to plot your data.

  • If some cases where the data is logarithmic or categorical, it is first converted to a linear scale so that it can be represented on a Cartesian plane. d3-scale is an excellent tool for this. We refer to this as the data scale, though mostly Hypocube doesn't touch it.


What if you simply don't have enough space for your data? We are working in a programmatic medium, and can solve this through interaction! In other words, the chart view can be updated to give users access to data which isn't initially visible.

Specifically, an onGesture prop accepts a callback which will report the extent to which the view should be moved in response to pinch, swipe, drag and other move events. (Hypocube aims to be as declarative as possible, and won't automatically update the view: instead, hooks are provided that respond to these callbacks with state updates and animations). This is possible largely due to the fabulous @use-gesture library.

Callbacks can also be passed to most chart elements to produce other types of interactivity, for example clicking/tapping on a data point. Hypocube uses pointer events to simplify the inconsistencies between mouse and touch devices. Event handlers are called with coordinates that are semantically meaningful in the data, not (just) the raw coordinates of the event.

Read More about Hypocube events.

Basic definitions, conventions, and utilities

Hypocube represents data through a Point that is simply a bipartite (2-member) array consisting of an x and y value respectively. Thus, a set of data can be represented through an array of Points (Point[]). This convention holds even when data is categorical (has no meaningful x-value outside of the chart). An array of numbers can be converted to an array of Points by, i) => [i, val]).

A Dataseries is an object shaped like

data: Point[];
key: string;
meta: Record<string, boolean | string | number | null>

As you might guess if you're familiar with React, the key is a convenience for mapping the Dataseries to child elements. meta can contain any other information about the series, but it is best to stick to JavaScript primatives, as Hypocube uses that assumption under the hood to compare props and improve performance.

The "bounding box" of a chart is represented through a Viewbox object. They can be created with createViewbox(xMin, yMin, width, height), for consistency with the SVG element viewbox attribute. Once created, however, these objects also contain their xMax and yMax, a few other useful properties, and some methods for moving them about and the like. There is much more detail about this in the interaction section. Viewboxes are immutable.