How to build a 2d density chart with React and D3.
2d density chart
A 2D density chart is a graphical representation of data that uses color to show the concentration of data points in a given area. It shows the combined distribution of two quantitative variables. 2D density charts are often used in statistical analysis and data mining to identify trends, patterns, and correlations in the data.
In this tutorial, we will use the d3.js and React libraries to build a 2D density chart. It starts by describing how the data should be organized and how to initialize the Density2d component. It then explains how to prepare the data and compute bins. Once this is done, it shows how to render the shapes and suggests a few variations. 🙇♂️.
A 2d density chart is basically a variation of the scatterplot, useful when the amount of data points is huge. As a result, it shares the same data structure.
The data is an array of object. For each object, at least 2 properties are required: x and y. The value of x is the position of the datapoint on the horizontal axis. The value of y is linked with the vertical axis.
const data = [
+How to build a 2d density chart with React and D3.
2d density chart
A 2D density chart is a graphical representation of data that uses color to show the concentration of data points in a given area. It shows the combined distribution of two quantitative variables. 2D density charts are often used in statistical analysis and data mining to identify trends, patterns, and correlations in the data.
In this tutorial, we will use the d3.js and React libraries to build a 2D density chart. It starts by describing how the data should be organized and how to initialize the Density2d component. It then explains how to prepare the data and compute bins. Once this is done, it shows how to render the shapes and suggests a few variations. 🙇♂️.
A 2d density chart is basically a variation of the scatterplot, useful when the amount of data points is huge. As a result, it shares the same data structure.
The data is an array of object. For each object, at least 2 properties are required: x and y. The value of x is the position of the datapoint on the horizontal axis. The value of y is linked with the vertical axis.
const data = [
{
x: 2,
y: 4
@@ -78,4 +78,4 @@
}
I'm in the process of writing a complete blog post on the topic. Subscribe to the project to know when it's ready.
2D Density inspiration
If you're looking for inspiration to create your next 2D Density, note that dataviz-inspiration.com showcases many examples. Definitely the best place to get ... inspiration!
dataviz-inspiration.com showcases hundreds of stunning dataviz projects. Have a look to get some ideas on how to make your 2D Density looks good!
The hexbin representation above is just one member of a family of graphics allowing to study the combined distribution of two quantitative variables. You can read more about the existing variations in the data to viz project.
To put it in a nutshell, 2d histogram, Gaussian KDE, 2d density and Contour charts are the most common variations. It is of course possible to build them all using react and d3.js.
Here is an example with a contounr chart based on the same dataset than the previous example.
Contour chart made with React and D3.js.
ToDomake the contour chart above looks better
ToDoadd examples for 2d histograms and other variations
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/404.html b/404.html
index 9e53606b..3038c482 100644
--- a/404.html
+++ b/404.html
@@ -1,4 +1,4 @@
-How to build a scatter plot with React and D3.
Oh No! (404)
It looks like the place you are looking for does not exist 🙈
The countries with the highest vulnerability to climate change have the lowest CO2 emissions
All countries sorted by their vulnerability and readiness to climate change. The size shows the CO2 emission per person in that country.
Reproduction of a chart originally published by Data Wrapper using react and d3.js.
Contact
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
+How to build a scatter plot with React and D3.
Oh No! (404)
It looks like the place you are looking for does not exist 🙈
The countries with the highest vulnerability to climate change have the lowest CO2 emissions
All countries sorted by their vulnerability and readiness to climate change. The size shows the CO2 emission per person in that country.
Reproduction of a chart originally published by Data Wrapper using react and d3.js.
Contact
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/_next/static/chunks/pages/course/hover-effect/css-descendant-selector-c601c79bbc3a1e96.js b/_next/static/chunks/pages/course/hover-effect/css-descendant-selector-44c9d9449ab2fcc7.js
similarity index 54%
rename from _next/static/chunks/pages/course/hover-effect/css-descendant-selector-c601c79bbc3a1e96.js
rename to _next/static/chunks/pages/course/hover-effect/css-descendant-selector-44c9d9449ab2fcc7.js
index 6000d340..e9f625a7 100644
--- a/_next/static/chunks/pages/course/hover-effect/css-descendant-selector-c601c79bbc3a1e96.js
+++ b/_next/static/chunks/pages/course/hover-effect/css-descendant-selector-44c9d9449ab2fcc7.js
@@ -1 +1 @@
-(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[9548],{53458:function(e,t,n){(window.__NEXT_P=window.__NEXT_P||[]).push(["/course/hover-effect/css-descendant-selector",function(){return n(37344)}])},81122:function(e,t,n){"use strict";n.d(t,{$:function(){return o}});var s=n(85893),r=n(67294),i=n(59973),a=n(80615),l=n(88578),c=n(5);let o=e=>{let{VizComponent:t,vizName:n,height:o=400,maxWidth:d=800,caption:h}=e,[u,x]=(0,r.useState)(!1),m=(0,r.useRef)(null),f=(0,i.B)(m);return(0,s.jsx)("div",{style:{marginLeft:"-50vw",left:"50%"},className:"my-4 py-4 w-screen relative",children:u?(0,s.jsxs)("div",{className:"flex flex-col items-center justify-center w-full",children:[(0,s.jsx)("div",{style:{maxWidth:2e3},className:"w-full z-50",children:(0,s.jsx)(l.X,{vizName:n})}),(0,s.jsx)("div",{className:"flex justify-center mt-2",children:(0,s.jsx)(c.z,{size:"sm",onClick:()=>x(!u),children:"Hide Sandbox"})})]}):(0,s.jsxs)("div",{className:"flex flex-col items-center justify-center",children:[(0,s.jsx)("div",{className:"bg-gray-100 bg-opacity-50 w-screen flex justify-center z-50 pointer-events-none",children:(0,s.jsx)("div",{style:{height:o,width:"100%",maxWidth:d},ref:m,className:"pointer-events-auto",children:(0,s.jsx)(t,{height:o,width:f.width})})}),(0,s.jsx)(a.Y,{children:h}),(0,s.jsx)("div",{className:"flex justify-center",children:(0,s.jsx)(c.z,{size:"sm",onClick:()=>x(!u),children:"Show code"})})]})})}},88578:function(e,t,n){"use strict";n.d(t,{X:function(){return r}});var s=n(85893);n(67294);let r=e=>{let{vizName:t}=e;return(0,s.jsx)("iframe",{src:"https://codesandbox.io/embed/github/holtzy/react-graph-gallery/tree/main/viz/"+t+"?fontsize=14&hidenavigation=1&theme=dark&expanddevtools=0",style:{width:"100%",height:"500px",border:"solid",borderWidth:2,borderRadius:"4px",overflow:"hidden"},allow:"accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking",sandbox:"allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"})}},47498:function(e,t,n){"use strict";n.d(t,{v:function(){return d}});var s=n(85893),r=n(7826),i=n(13742),a=n(61108),l=n(67294),c=n(5),o=n(77522);let d=e=>{let{exercises:t,localStorageId:n}=e,[d,h]=(0,l.useState)([]),[u,x]=(0,l.useState)("");return(0,l.useEffect)(()=>{let e=localStorage.getItem(n),s=e?JSON.parse(e):Array(t.length).fill("todo");h(s)},[]),(0,s.jsx)(r.UQ,{value:u,onValueChange:x,type:"single",collapsible:!0,className:"w-full",children:t.map((e,t)=>(0,s.jsxs)(r.Qd,{value:"item-"+t,children:[(0,s.jsx)(r.o4,{className:"no-decoration hover:bg-gray-50",children:(0,s.jsxs)("div",{className:"text-sm flex justify-start gap-2 items-center",children:[(0,s.jsx)("div",{className:(0,o.cn)("text-xs h-6 w-6 flex justify-center items-center rounded-full text-center leading-none","todo"===d[t]?"bg-gray-200":"failed"===d[t]?"bg-red-300":"bg-green-300"),children:(0,s.jsx)("span",{style:{transform:"translateX(1px)"},children:t+1})}),(0,s.jsx)("span",{children:e.title}),"ok"===d[t]&&(0,s.jsx)(i.Z,{size:16,className:"text-green-500"}),"failed"===d[t]&&(0,s.jsx)(a.Z,{size:16,className:"text-red-500"}),"todo"===d[t]&&(0,s.jsx)("span",{className:"text-gray-400 font-thin",children:"ToDo"})]})}),(0,s.jsxs)(r.vF,{children:[e.content,(0,s.jsxs)("div",{className:"flex justify-center gap-4",children:[(0,s.jsx)(c.z,{variant:"outline",onClick:()=>{let e=[...d];e[t]="failed",h(e),localStorage.setItem(n,JSON.stringify(e)),x("")},children:"Failed"}),(0,s.jsxs)(c.z,{onClick:()=>{let e=[...d];e[t]="ok",h(e),localStorage.setItem(n,JSON.stringify(e)),x("")},children:["Done",(0,s.jsx)("span",{className:"ml-2",children:"\uD83C\uDF89"})]})]})]})]}))})}},13400:function(e,t,n){"use strict";n.d(t,{q:function(){return l}});var s=n(85893),r=n(22725),i=n(88578),a=n(8117);let l=e=>{let{exercise:t}=e;return(0,s.jsxs)("div",{children:[(0,s.jsxs)("div",{className:"grid grid-cols-2 gap-4 pt-4",children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(a.C,{children:"To Do"}),(0,s.jsx)("div",{className:"mt-4",children:t.toDo})]}),(0,s.jsxs)("div",{children:[(0,s.jsx)(a.C,{children:"Why it matters"}),(0,s.jsx)("div",{className:"mt-4 pl-4",children:t.whyItMatters})]})]}),(0,s.jsxs)(r.mQ,{defaultValue:"practice",className:"",children:[(0,s.jsx)("center",{children:(0,s.jsxs)(r.dr,{children:[(0,s.jsx)(r.SP,{value:"practice",children:"Practice"}),(0,s.jsx)(r.SP,{value:"solution",children:"Solution"})]})}),(0,s.jsx)(r.nU,{value:"practice",children:(0,s.jsx)("div",{className:"full-bleed my-4 max-w-7xl mx-auto",children:(0,s.jsx)(i.X,{vizName:t.practiceSandbox})})}),(0,s.jsx)(r.nU,{value:"solution",children:(0,s.jsx)("div",{className:"full-bleed my-4 max-w-7xl mx-auto",children:(0,s.jsx)(i.X,{vizName:t.solutionSandbox})})})]})]})}},52450:function(e,t,n){"use strict";n.d(t,{Z:function(){return l}});var s=n(85893);n(67294);var r=n(92050),i=n(49975),a=n(59942);function l(e){let{images:t}=e,n=r.F.filter(e=>t.includes(e.img)).map((e,t)=>(0,s.jsx)(i.X,{link:e.link,title:e.title,description:(0,s.jsx)("p",{children:e.description}),img:e.img,alt:e.alt},t));return(0,s.jsx)(a.E,{children:n})}},41843:function(e,t,n){"use strict";n.d(t,{p:function(){return o}});var s=n(85893),r=n(49700),i=n(63476),a=n(17414),l=n(41664),c=n.n(l);let o=e=>{let{children:t,title:n,seoDescription:l,previousTocItem:o,nextTocItem:d}=e;return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(a.A,{title:n,seoDescription:l}),(0,s.jsx)(r.Z,{}),(0,s.jsx)("div",{className:"wrapper",children:t}),(0,s.jsxs)("div",{className:"flex justify-center items-center space-x-6 my-24 py-12 bg-muted/50",children:[o?(0,s.jsxs)(c(),{href:o.link,className:"text-gray-500 no-underline flex flex-col justify-start items-end w-96 h-32 border-r border-black p-8 hover:bg-muted ",children:[(0,s.jsx)("span",{className:"uppercase font-light text-transparent bg-gradient-to-l to-fuchsia-300 from-blue-400 bg-clip-text",children:"← Previous"}),(0,s.jsx)("p",{children:o.name})]}):(0,s.jsx)("div",{className:"w-96"}),d&&(0,s.jsxs)(c(),{href:d.link,className:"text-gray-500 no-underline flex flex-col justify-start w-96 h-32 border-l border-black p-8 hover:bg-muted ",children:[(0,s.jsx)("span",{className:"uppercase font-light text-transparent bg-gradient-to-l from-fuchsia-300 to-blue-400 bg-clip-text",children:"Next →"}),(0,s.jsx)("p",{children:d.name})]})]}),(0,s.jsx)("div",{className:"wrapper",children:(0,s.jsx)(i.Z,{})})]})}},80615:function(e,t,n){"use strict";n.d(t,{Y:function(){return r}});var s=n(85893);let r=e=>{let{children:t}=e;return(0,s.jsx)("p",{className:"text-sm text-gray-500 max-w-xs italic text-center mt-4 font-light",children:t})}},3572:function(e,t,n){"use strict";n.d(t,{d:function(){return d}});var s=n(85893),r=n(32581),i=n(15660),a=n.n(i),l=n(67294),c=n(45993),o=n.n(c);let d=e=>{let{code:t}=e,[n,i]=(0,l.useState)(!1),c=(0,l.useRef)(null);(0,l.useEffect)(()=>{c.current&&a().highlightElement(c.current)},[c,t]);let d=(0,s.jsx)("div",{onClick:()=>{navigator.clipboard.writeText(t),i(!0)},className:o().codeChunckCopyButton,children:n?"Copied":(0,s.jsx)(r.Z,{size:14,style:{padding:0}})});return(0,s.jsxs)("div",{className:"mb-6 relative",children:[(0,s.jsx)("pre",{className:"rounded-md line-numbers",children:(0,s.jsx)("code",{ref:c,className:"language-javascript",children:t})}),(0,s.jsx)("div",{className:o().copyButtonContainer,children:d})]})}},7826:function(e,t,n){"use strict";n.d(t,{Qd:function(){return o},UQ:function(){return c},o4:function(){return d},vF:function(){return h}});var s=n(85893),r=n(67294),i=n(47398),a=n(8971),l=n(77522);let c=i.fC,o=r.forwardRef((e,t)=>{let{className:n,...r}=e;return(0,s.jsx)(i.ck,{ref:t,className:(0,l.cn)("border-b",n),...r})});o.displayName="AccordionItem";let d=r.forwardRef((e,t)=>{let{className:n,children:r,...c}=e;return(0,s.jsx)(i.h4,{className:"flex mt-0 pb-0 font-normal",children:(0,s.jsxs)(i.xz,{ref:t,className:(0,l.cn)("flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",n),...c,children:[r,(0,s.jsx)(a.Z,{className:"h-4 w-4 shrink-0 transition-transform duration-200"})]})})});d.displayName=i.xz.displayName;let h=r.forwardRef((e,t)=>{let{className:n,children:r,...a}=e;return(0,s.jsx)(i.VY,{ref:t,className:"overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down",...a,children:(0,s.jsx)("div",{className:(0,l.cn)("pb-4 pt-0",n),children:r})})});h.displayName=i.VY.displayName},22725:function(e,t,n){"use strict";n.d(t,{SP:function(){return o},dr:function(){return c},mQ:function(){return l},nU:function(){return d}});var s=n(85893),r=n(67294),i=n(60434),a=n(77522);let l=i.fC,c=r.forwardRef((e,t)=>{let{className:n,...r}=e;return(0,s.jsx)(i.aV,{ref:t,className:(0,a.cn)("inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",n),...r})});c.displayName=i.aV.displayName;let o=r.forwardRef((e,t)=>{let{className:n,...r}=e;return(0,s.jsx)(i.xz,{ref:t,className:(0,a.cn)("inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm",n),...r})});o.displayName=i.xz.displayName;let d=r.forwardRef((e,t)=>{let{className:n,...r}=e;return(0,s.jsx)(i.VY,{ref:t,className:(0,a.cn)("mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",n),...r})});d.displayName=i.VY.displayName},59973:function(e,t,n){"use strict";n.d(t,{B:function(){return r}});var s=n(67294);let r=e=>{let t=()=>({width:e.current?e.current.offsetWidth:0,height:e.current?e.current.offsetHeight:0}),[n,r]=(0,s.useState)(t),i=()=>{r(t())};return(0,s.useEffect)(()=>(window.addEventListener("resize",i),()=>window.removeEventListener("resize",i)),[]),(0,s.useEffect)(()=>{i()},[e]),n}},37344:function(e,t,n){"use strict";n.r(t),n.d(t,{default:function(){return j}});var s=n(85893);n(67294);var r=n(43710),i=n(41843),a=n(11236),l=n(81122),c=n(3572),o=n(8117),d=n(52450),h=n(60390),u=n(41664),x=n.n(u),m=n(13400),f=n(47498);let p="/course/hover-effect/css-descendant-selector";function j(){let e=a.Y.find(e=>e.link===p),t=a.Y.findIndex(e=>e.link===p);return e?(0,s.jsxs)(i.p,{title:e.name,seoDescription:"",nextTocItem:a.Y.find(e=>"/course/hover-effect/toggle-class-in-js"===e.link),previousTocItem:a.Y.find(e=>"/course/hover-effect/css-pseudo-class"===e.link),children:[(0,s.jsx)(r.Z,{title:e.name,lessonStatus:e.status,readTime:e.readTime,topBadge:"Lesson "+t,description:(0,s.jsxs)(s.Fragment,{children:[(0,s.jsxs)("p",{children:["In the previous lesson, we learned how to modify a hovered graph item using the ",(0,s.jsx)("code",{children:":hover"})," CSS pseudo-class."]}),(0,s.jsxs)("p",{children:["However, this approach has ",(0,s.jsx)("b",{children:"design limitations"}),". To achieve a more effective highlighting effect, it's better to simultaneously"," ",(0,s.jsx)("b",{children:"dim the other graph items"}),"."]}),(0,s.jsx)("p",{children:"This can be accomplished using CSS alone, with the help of the CSS descendant selector."})]})}),(0,s.jsx)("h2",{children:"What is a css descendant selector?"}),(0,s.jsxs)("p",{children:["A"," ",(0,s.jsx)("a",{href:"https://developer.mozilla.org/en-US/docs/Web/CSS/Descendant_combinator",children:"descendant selector"})," ","allows to target elements that are children of another element."]}),(0,s.jsx)("p",{children:"Here’s an example:"}),(0,s.jsx)(c.d,{code:"\n.rectangle {\n opacity: 1;\n}\n.container:hover .rectangle {\n opacity: .1;\n}\n.container .rectangle:hover {\n opacity: 1;\n}\n".trim()}),(0,s.jsxs)("p",{children:["We assign a class called ",(0,s.jsx)("code",{children:"container"})," to the SVG container and a class called ",(0,s.jsx)("code",{children:"rectangle"})," to each rectangle in the chart."]}),(0,s.jsxs)("p",{children:["Then we set the default rectangle ",(0,s.jsx)("code",{children:"opacity"})," to 1. Using the descendant selector, you can reduce the opacity of all rectangles to 0.1 when the ",(0,s.jsx)("code",{children:"container"})," is hovered."]}),(0,s.jsx)("p",{children:"Then, use a hover selector to set the opacity of the hovered rectangle back to 1."}),(0,s.jsx)("h2",{children:"Application: treemap"}),(0,s.jsx)(l.$,{vizName:"TreemapHoverEffect",VizComponent:h.C,maxWidth:600,height:400,caption:"Strategy 2: use CSS descendant combinator to dim all markers except the one that is hovered."}),(0,s.jsx)("h2",{children:"Pros & Cons"}),(0,s.jsx)("p",{children:(0,s.jsx)(o.C,{children:"Pros"})}),(0,s.jsxs)("ul",{children:[(0,s.jsx)("li",{children:"Easy to implement"}),(0,s.jsx)("li",{children:"Improves design by making hover effects more noticeable"}),(0,s.jsx)("li",{children:"Excellent performance (no JS computation, minimal redrawing)"})]}),(0,s.jsx)("p",{children:(0,s.jsx)(o.C,{variant:"destructive",children:"Cons"})}),(0,s.jsxs)("ul",{children:[(0,s.jsxs)("li",{children:["Fades all circles if the mouse enters the chart area without hovering over a specific circle. This technique works for chart where the whole svg area is covered by markers, like a"," ",(0,s.jsx)(x(),{href:"/treemap",children:"treemap"}),"."]}),(0,s.jsxs)("li",{children:["Cannot highlight circles that are obscured by other elements. (Potentially fixed using ",(0,s.jsx)("code",{children:"z-index"}),")."]})]}),(0,s.jsx)("h2",{children:"More examples"}),(0,s.jsx)("p",{children:"The examples below all use this strategy to implement their hover effect."}),(0,s.jsx)(d.Z,{images:["lollipop-plot-hover-effect.png","streamgraph-hover-effect.gif"]}),(0,s.jsx)("h2",{children:"Exercices"}),(0,s.jsx)(f.v,{localStorageId:e.link,exercises:[{title:(0,s.jsx)("span",{children:"Your first lollipop! \uD83C\uDF6D"}),content:(0,s.jsx)(m.q,{exercise:v[0]})},{title:(0,s.jsx)("span",{children:"Lollipop with hover effect"}),content:(0,s.jsx)(m.q,{exercise:v[1]})},{title:(0,s.jsx)("span",{children:"Scatterplot = problem \uD83D\uDEA8"}),content:(0,s.jsx)(m.q,{exercise:v[2]})}]})]}):null}let v=[{whyItMatters:(0,s.jsx)(s.Fragment,{children:(0,s.jsx)("p",{children:"With the SVG and D3 foundations you’ve built, creating a new chart type becomes a breeze!"})}),toDo:(0,s.jsx)(s.Fragment,{children:(0,s.jsxs)("ul",{children:[(0,s.jsx)("li",{children:"A dataset is provided in the sandbox folder."}),(0,s.jsx)("li",{children:"Build a Cleveland chart with this dataset. (This chart is a variation of the lollipop plot and is helpful for comparing two values across multiple groups.)"}),(0,s.jsx)("li",{children:"Check the solution tab to see the intended chart appearance."})]})}),practiceSandbox:"exercise/LollipopFirstPractice",solutionSandbox:"exercise/LollipopFirstSolution"},{whyItMatters:(0,s.jsx)(s.Fragment,{children:(0,s.jsx)("p",{children:"Descendant selectors are fantastic for creating advanced hover effects with only CSS!"})}),toDo:(0,s.jsx)(s.Fragment,{children:(0,s.jsxs)("ul",{children:[(0,s.jsxs)("li",{children:["Add a class ",(0,s.jsx)("code",{children:"rowsContainer"})," to the SVG container and a class ",(0,s.jsx)("code",{children:"row"})," to each row."]}),(0,s.jsx)("li",{children:"Using a CSS descendant selector, highlight the hovered row and dim other rows."})]})}),practiceSandbox:"exercise/LollipopHoverEffectPractice",solutionSandbox:"exercise/LollipopHoverEffectSolution"},{whyItMatters:(0,s.jsx)(s.Fragment,{children:(0,s.jsx)("p",{children:"The descendant selector technique has limitations! It doesn’t work when the SVG area isn’t fully filled with elements."})}),toDo:(0,s.jsx)(s.Fragment,{children:(0,s.jsxs)("ul",{children:[(0,s.jsx)("li",{children:"Use the CSS descendant selector technique to dim circles that aren’t hovered over when one circle is hovered."}),(0,s.jsx)("li",{children:"What happens when your mouse enters the SVG area but doesn’t reach any circles?"})]})}),practiceSandbox:"exercise/Hover3CirclesDescendantSelectorIssuePractice",solutionSandbox:"exercise/Hover3CirclesDescendantSelectorIssueSolution"}]},60390:function(e,t,n){"use strict";n.d(t,{C:function(){return h}});var s=n(85893);let r={type:"node",name:"boss",value:0,children:[{type:"node",name:"Team Dataviz",value:0,children:[{type:"leaf",name:"Mark",value:90},{type:"leaf",name:"Robert",value:12},{type:"leaf",name:"Emily",value:34},{type:"leaf",name:"Marion",value:53}]},{type:"node",name:"Team DevOps",value:0,children:[{type:"leaf",name:"Nicolas",value:98},{type:"leaf",name:"Malki",value:22},{type:"leaf",name:"Dj\xe9",value:12}]},{type:"node",name:"Team Sales",value:0,children:[{type:"leaf",name:"M\xe9lanie",value:45},{type:"leaf",name:"Einstein",value:76}]}]};var i=n(67294),a=n(43950),l=n(87552),c=n.n(l);let o=["#e0ac2b","#6689c6","#a4c969","#e85252","#9a6fb0","#a53253","#7f7f7f"],d=e=>{let{width:t,height:n,data:r}=e,l=(0,i.useMemo)(()=>a.bT9(r).sum(e=>e.value),[r]),d=null==l?void 0:null===(h=l.children)||void 0===h?void 0:h.map(e=>e.data.name);var h,u=a.PKp().domain(d||[]).range(o);let x=(0,i.useMemo)(()=>{let e=a.pNI().size([t,n]).padding(4);return e(l)},[l,t,n]),m=x.leaves().map((e,t)=>{var n;let r=null===(n=e.parent)||void 0===n?void 0:n.data.name;return(0,s.jsxs)("g",{className:c().rectangle,children:[(0,s.jsx)("rect",{x:e.x0,y:e.y0,width:e.x1-e.x0,height:e.y1-e.y0,stroke:"transparent",fill:u(r),className:"opacity-80 hover:opacity-100"}),(0,s.jsx)("text",{x:e.x0+3,y:e.y0+3,fontSize:12,textAnchor:"start",alignmentBaseline:"hanging",fill:"white",className:"font-bold",children:e.data.name}),(0,s.jsx)("text",{x:e.x0+3,y:e.y0+18,fontSize:12,textAnchor:"start",alignmentBaseline:"hanging",fill:"white",className:"font-light",children:e.data.value})]},e.id)});return(0,s.jsx)("div",{children:(0,s.jsx)("svg",{width:t,height:n,className:c().container,children:m})})},h=e=>{let{width:t=700,height:n=400}=e;return(0,s.jsx)(d,{data:r,width:t,height:n})}},45993:function(e){e.exports={codeChunckCopyButton:"code-block_codeChunckCopyButton__yPrL_",copyButtonContainer:"code-block_copyButtonContainer__BrX9E"}},87552:function(e){e.exports={container:"treemap_container__nYrjn",rectangle:"treemap_rectangle__xOWar"}}},function(e){e.O(0,[2343,7754,3950,7823,9484,8190,3710,693,9774,2888,179],function(){return e(e.s=53458)}),_N_E=e.O()}]);
\ No newline at end of file
+(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[9548],{53458:function(e,t,n){(window.__NEXT_P=window.__NEXT_P||[]).push(["/course/hover-effect/css-descendant-selector",function(){return n(37344)}])},81122:function(e,t,n){"use strict";n.d(t,{$:function(){return o}});var s=n(85893),r=n(67294),i=n(59973),a=n(80615),l=n(88578),c=n(5);let o=e=>{let{VizComponent:t,vizName:n,height:o=400,maxWidth:d=800,caption:h}=e,[u,x]=(0,r.useState)(!1),m=(0,r.useRef)(null),f=(0,i.B)(m);return(0,s.jsx)("div",{style:{marginLeft:"-50vw",left:"50%"},className:"my-4 py-4 w-screen relative",children:u?(0,s.jsxs)("div",{className:"flex flex-col items-center justify-center w-full",children:[(0,s.jsx)("div",{style:{maxWidth:2e3},className:"w-full z-50",children:(0,s.jsx)(l.X,{vizName:n})}),(0,s.jsx)("div",{className:"flex justify-center mt-2",children:(0,s.jsx)(c.z,{size:"sm",onClick:()=>x(!u),children:"Hide Sandbox"})})]}):(0,s.jsxs)("div",{className:"flex flex-col items-center justify-center",children:[(0,s.jsx)("div",{className:"bg-gray-100 bg-opacity-50 w-screen flex justify-center z-50 pointer-events-none",children:(0,s.jsx)("div",{style:{height:o,width:"100%",maxWidth:d},ref:m,className:"pointer-events-auto",children:(0,s.jsx)(t,{height:o,width:f.width})})}),(0,s.jsx)(a.Y,{children:h}),(0,s.jsx)("div",{className:"flex justify-center",children:(0,s.jsx)(c.z,{size:"sm",onClick:()=>x(!u),children:"Show code"})})]})})}},88578:function(e,t,n){"use strict";n.d(t,{X:function(){return r}});var s=n(85893);n(67294);let r=e=>{let{vizName:t}=e;return(0,s.jsx)("iframe",{src:"https://codesandbox.io/embed/github/holtzy/react-graph-gallery/tree/main/viz/"+t+"?fontsize=14&hidenavigation=1&theme=dark&expanddevtools=0",style:{width:"100%",height:"500px",border:"solid",borderWidth:2,borderRadius:"4px",overflow:"hidden"},allow:"accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking",sandbox:"allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"})}},47498:function(e,t,n){"use strict";n.d(t,{v:function(){return d}});var s=n(85893),r=n(7826),i=n(13742),a=n(61108),l=n(67294),c=n(5),o=n(77522);let d=e=>{let{exercises:t,localStorageId:n}=e,[d,h]=(0,l.useState)([]),[u,x]=(0,l.useState)("");return(0,l.useEffect)(()=>{let e=localStorage.getItem(n),s=e?JSON.parse(e):Array(t.length).fill("todo");h(s)},[]),(0,s.jsx)(r.UQ,{value:u,onValueChange:x,type:"single",collapsible:!0,className:"w-full",children:t.map((e,t)=>(0,s.jsxs)(r.Qd,{value:"item-"+t,children:[(0,s.jsx)(r.o4,{className:"no-decoration hover:bg-gray-50",children:(0,s.jsxs)("div",{className:"text-sm flex justify-start gap-2 items-center",children:[(0,s.jsx)("div",{className:(0,o.cn)("text-xs h-6 w-6 flex justify-center items-center rounded-full text-center leading-none","todo"===d[t]?"bg-gray-200":"failed"===d[t]?"bg-red-300":"bg-green-300"),children:(0,s.jsx)("span",{style:{transform:"translateX(1px)"},children:t+1})}),(0,s.jsx)("span",{children:e.title}),"ok"===d[t]&&(0,s.jsx)(i.Z,{size:16,className:"text-green-500"}),"failed"===d[t]&&(0,s.jsx)(a.Z,{size:16,className:"text-red-500"}),"todo"===d[t]&&(0,s.jsx)("span",{className:"text-gray-400 font-thin",children:"ToDo"})]})}),(0,s.jsxs)(r.vF,{children:[e.content,(0,s.jsxs)("div",{className:"flex justify-center gap-4",children:[(0,s.jsx)(c.z,{variant:"outline",onClick:()=>{let e=[...d];e[t]="failed",h(e),localStorage.setItem(n,JSON.stringify(e)),x("")},children:"Failed"}),(0,s.jsxs)(c.z,{onClick:()=>{let e=[...d];e[t]="ok",h(e),localStorage.setItem(n,JSON.stringify(e)),x("")},children:["Done",(0,s.jsx)("span",{className:"ml-2",children:"\uD83C\uDF89"})]})]})]})]}))})}},13400:function(e,t,n){"use strict";n.d(t,{q:function(){return l}});var s=n(85893),r=n(22725),i=n(88578),a=n(8117);let l=e=>{let{exercise:t}=e;return(0,s.jsxs)("div",{children:[(0,s.jsxs)("div",{className:"grid grid-cols-2 gap-4 pt-4",children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(a.C,{children:"To Do"}),(0,s.jsx)("div",{className:"mt-4",children:t.toDo})]}),(0,s.jsxs)("div",{children:[(0,s.jsx)(a.C,{children:"Why it matters"}),(0,s.jsx)("div",{className:"mt-4 pl-4",children:t.whyItMatters})]})]}),(0,s.jsxs)(r.mQ,{defaultValue:"practice",className:"",children:[(0,s.jsx)("center",{children:(0,s.jsxs)(r.dr,{children:[(0,s.jsx)(r.SP,{value:"practice",children:"Practice"}),(0,s.jsx)(r.SP,{value:"solution",children:"Solution"})]})}),(0,s.jsx)(r.nU,{value:"practice",children:(0,s.jsx)("div",{className:"full-bleed my-4 max-w-7xl mx-auto",children:(0,s.jsx)(i.X,{vizName:t.practiceSandbox})})}),(0,s.jsx)(r.nU,{value:"solution",children:(0,s.jsx)("div",{className:"full-bleed my-4 max-w-7xl mx-auto",children:(0,s.jsx)(i.X,{vizName:t.solutionSandbox})})})]})]})}},52450:function(e,t,n){"use strict";n.d(t,{Z:function(){return l}});var s=n(85893);n(67294);var r=n(92050),i=n(49975),a=n(59942);function l(e){let{images:t}=e,n=r.F.filter(e=>t.includes(e.img)).map((e,t)=>(0,s.jsx)(i.X,{link:e.link,title:e.title,description:(0,s.jsx)("p",{children:e.description}),img:e.img,alt:e.alt},t));return(0,s.jsx)(a.E,{children:n})}},41843:function(e,t,n){"use strict";n.d(t,{p:function(){return o}});var s=n(85893),r=n(49700),i=n(63476),a=n(17414),l=n(41664),c=n.n(l);let o=e=>{let{children:t,title:n,seoDescription:l,previousTocItem:o,nextTocItem:d}=e;return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(a.A,{title:n,seoDescription:l}),(0,s.jsx)(r.Z,{}),(0,s.jsx)("div",{className:"wrapper",children:t}),(0,s.jsxs)("div",{className:"flex justify-center items-center space-x-6 my-24 py-12 bg-muted/50",children:[o?(0,s.jsxs)(c(),{href:o.link,className:"text-gray-500 no-underline flex flex-col justify-start items-end w-96 h-32 border-r border-black p-8 hover:bg-muted ",children:[(0,s.jsx)("span",{className:"uppercase font-light text-transparent bg-gradient-to-l to-fuchsia-300 from-blue-400 bg-clip-text",children:"← Previous"}),(0,s.jsx)("p",{children:o.name})]}):(0,s.jsx)("div",{className:"w-96"}),d&&(0,s.jsxs)(c(),{href:d.link,className:"text-gray-500 no-underline flex flex-col justify-start w-96 h-32 border-l border-black p-8 hover:bg-muted ",children:[(0,s.jsx)("span",{className:"uppercase font-light text-transparent bg-gradient-to-l from-fuchsia-300 to-blue-400 bg-clip-text",children:"Next →"}),(0,s.jsx)("p",{children:d.name})]})]}),(0,s.jsx)("div",{className:"wrapper",children:(0,s.jsx)(i.Z,{})})]})}},80615:function(e,t,n){"use strict";n.d(t,{Y:function(){return r}});var s=n(85893);let r=e=>{let{children:t}=e;return(0,s.jsx)("p",{className:"text-sm text-gray-500 max-w-xs italic text-center mt-4 font-light",children:t})}},3572:function(e,t,n){"use strict";n.d(t,{d:function(){return d}});var s=n(85893),r=n(32581),i=n(15660),a=n.n(i),l=n(67294),c=n(45993),o=n.n(c);let d=e=>{let{code:t}=e,[n,i]=(0,l.useState)(!1),c=(0,l.useRef)(null);(0,l.useEffect)(()=>{c.current&&a().highlightElement(c.current)},[c,t]);let d=(0,s.jsx)("div",{onClick:()=>{navigator.clipboard.writeText(t),i(!0)},className:o().codeChunckCopyButton,children:n?"Copied":(0,s.jsx)(r.Z,{size:14,style:{padding:0}})});return(0,s.jsxs)("div",{className:"mb-6 relative",children:[(0,s.jsx)("pre",{className:"rounded-md line-numbers",children:(0,s.jsx)("code",{ref:c,className:"language-javascript",children:t})}),(0,s.jsx)("div",{className:o().copyButtonContainer,children:d})]})}},7826:function(e,t,n){"use strict";n.d(t,{Qd:function(){return o},UQ:function(){return c},o4:function(){return d},vF:function(){return h}});var s=n(85893),r=n(67294),i=n(47398),a=n(8971),l=n(77522);let c=i.fC,o=r.forwardRef((e,t)=>{let{className:n,...r}=e;return(0,s.jsx)(i.ck,{ref:t,className:(0,l.cn)("border-b",n),...r})});o.displayName="AccordionItem";let d=r.forwardRef((e,t)=>{let{className:n,children:r,...c}=e;return(0,s.jsx)(i.h4,{className:"flex mt-0 pb-0 font-normal",children:(0,s.jsxs)(i.xz,{ref:t,className:(0,l.cn)("flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",n),...c,children:[r,(0,s.jsx)(a.Z,{className:"h-4 w-4 shrink-0 transition-transform duration-200"})]})})});d.displayName=i.xz.displayName;let h=r.forwardRef((e,t)=>{let{className:n,children:r,...a}=e;return(0,s.jsx)(i.VY,{ref:t,className:"overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down",...a,children:(0,s.jsx)("div",{className:(0,l.cn)("pb-4 pt-0",n),children:r})})});h.displayName=i.VY.displayName},22725:function(e,t,n){"use strict";n.d(t,{SP:function(){return o},dr:function(){return c},mQ:function(){return l},nU:function(){return d}});var s=n(85893),r=n(67294),i=n(60434),a=n(77522);let l=i.fC,c=r.forwardRef((e,t)=>{let{className:n,...r}=e;return(0,s.jsx)(i.aV,{ref:t,className:(0,a.cn)("inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",n),...r})});c.displayName=i.aV.displayName;let o=r.forwardRef((e,t)=>{let{className:n,...r}=e;return(0,s.jsx)(i.xz,{ref:t,className:(0,a.cn)("inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm",n),...r})});o.displayName=i.xz.displayName;let d=r.forwardRef((e,t)=>{let{className:n,...r}=e;return(0,s.jsx)(i.VY,{ref:t,className:(0,a.cn)("mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",n),...r})});d.displayName=i.VY.displayName},59973:function(e,t,n){"use strict";n.d(t,{B:function(){return r}});var s=n(67294);let r=e=>{let t=()=>({width:e.current?e.current.offsetWidth:0,height:e.current?e.current.offsetHeight:0}),[n,r]=(0,s.useState)(t),i=()=>{r(t())};return(0,s.useEffect)(()=>(window.addEventListener("resize",i),()=>window.removeEventListener("resize",i)),[]),(0,s.useEffect)(()=>{i()},[e]),n}},37344:function(e,t,n){"use strict";n.r(t),n.d(t,{default:function(){return j}});var s=n(85893);n(67294);var r=n(43710),i=n(41843),a=n(11236),l=n(81122),c=n(3572),o=n(8117),d=n(52450),h=n(60390),u=n(41664),x=n.n(u),m=n(13400),f=n(47498);let p="/course/hover-effect/css-descendant-selector";function j(){let e=a.Y.find(e=>e.link===p);return(a.Y.findIndex(e=>e.link===p),e)?(0,s.jsxs)(i.p,{title:e.name,seoDescription:"",nextTocItem:a.Y.find(e=>"/course/hover-effect/toggle-class-in-js"===e.link),previousTocItem:a.Y.find(e=>"/course/hover-effect/css-pseudo-class"===e.link),children:[(0,s.jsx)(r.Z,{title:e.name,lessonStatus:e.status,readTime:e.readTime,selectedLesson:e,description:(0,s.jsxs)(s.Fragment,{children:[(0,s.jsxs)("p",{children:["In the previous lesson, we learned how to modify a hovered graph item using the ",(0,s.jsx)("code",{children:":hover"})," CSS pseudo-class."]}),(0,s.jsxs)("p",{children:["However, this approach has ",(0,s.jsx)("b",{children:"design limitations"}),". To achieve a more effective highlighting effect, it's better to simultaneously"," ",(0,s.jsx)("b",{children:"dim the other graph items"}),"."]}),(0,s.jsx)("p",{children:"This can be accomplished using CSS alone, with the help of the CSS descendant selector."})]})}),(0,s.jsx)("h2",{children:"What is a css descendant selector?"}),(0,s.jsxs)("p",{children:["A"," ",(0,s.jsx)("a",{href:"https://developer.mozilla.org/en-US/docs/Web/CSS/Descendant_combinator",children:"descendant selector"})," ","allows to target elements that are children of another element."]}),(0,s.jsx)("p",{children:"Here’s an example:"}),(0,s.jsx)(c.d,{code:"\n.rectangle {\n opacity: 1;\n}\n.container:hover .rectangle {\n opacity: .1;\n}\n.container .rectangle:hover {\n opacity: 1;\n}\n".trim()}),(0,s.jsxs)("p",{children:["We assign a class called ",(0,s.jsx)("code",{children:"container"})," to the SVG container and a class called ",(0,s.jsx)("code",{children:"rectangle"})," to each rectangle in the chart."]}),(0,s.jsxs)("p",{children:["Then we set the default rectangle ",(0,s.jsx)("code",{children:"opacity"})," to 1. Using the descendant selector, you can reduce the opacity of all rectangles to 0.1 when the ",(0,s.jsx)("code",{children:"container"})," is hovered."]}),(0,s.jsx)("p",{children:"Then, use a hover selector to set the opacity of the hovered rectangle back to 1."}),(0,s.jsx)("h2",{children:"Application: treemap"}),(0,s.jsx)(l.$,{vizName:"TreemapHoverEffect",VizComponent:h.C,maxWidth:600,height:400,caption:"Strategy 2: use CSS descendant combinator to dim all markers except the one that is hovered."}),(0,s.jsx)("h2",{children:"Pros & Cons"}),(0,s.jsx)("p",{children:(0,s.jsx)(o.C,{children:"Pros"})}),(0,s.jsxs)("ul",{children:[(0,s.jsx)("li",{children:"Easy to implement"}),(0,s.jsx)("li",{children:"Improves design by making hover effects more noticeable"}),(0,s.jsx)("li",{children:"Excellent performance (no JS computation, minimal redrawing)"})]}),(0,s.jsx)("p",{children:(0,s.jsx)(o.C,{variant:"destructive",children:"Cons"})}),(0,s.jsxs)("ul",{children:[(0,s.jsxs)("li",{children:["Fades all circles if the mouse enters the chart area without hovering over a specific circle. This technique works for chart where the whole svg area is covered by markers, like a"," ",(0,s.jsx)(x(),{href:"/treemap",children:"treemap"}),"."]}),(0,s.jsxs)("li",{children:["Cannot highlight circles that are obscured by other elements. (Potentially fixed using ",(0,s.jsx)("code",{children:"z-index"}),")."]})]}),(0,s.jsx)("h2",{children:"More examples"}),(0,s.jsx)("p",{children:"The examples below all use this strategy to implement their hover effect."}),(0,s.jsx)(d.Z,{images:["lollipop-plot-hover-effect.png","streamgraph-hover-effect.gif"]}),(0,s.jsx)("h2",{children:"Exercices"}),(0,s.jsx)(f.v,{localStorageId:e.link,exercises:[{title:(0,s.jsx)("span",{children:"Your first lollipop! \uD83C\uDF6D"}),content:(0,s.jsx)(m.q,{exercise:v[0]})},{title:(0,s.jsx)("span",{children:"Lollipop with hover effect"}),content:(0,s.jsx)(m.q,{exercise:v[1]})},{title:(0,s.jsx)("span",{children:"Scatterplot = problem? \uD83D\uDEA8"}),content:(0,s.jsx)(m.q,{exercise:v[2]})}]}),(0,s.jsxs)("p",{children:[(0,s.jsx)("br",{}),(0,s.jsx)("br",{})]}),(0,s.jsx)("blockquote",{className:"bg-fuchsia-50 py-8",children:(0,s.jsxs)("div",{children:[(0,s.jsxs)("p",{children:["Note: When you use the ",(0,s.jsx)("code",{children:":hover"})," pseudo-class on an SVG area, it activates whenever the mouse enters the entire SVG rectangle."]}),(0,s.jsxs)("p",{children:[(0,s.jsx)("br",{}),(0,s.jsx)("br",{})]}),(0,s.jsxs)("p",{children:["However, if you apply ",(0,s.jsx)("code",{children:":hover"})," to a ",(0,s.jsx)("code",{children:"g"})," ","element, it will only trigger when the mouse hovers over one of the elements within the ",(0,s.jsx)("code",{children:"g"})," group!"]})]})})]}):null}let v=[{whyItMatters:(0,s.jsx)(s.Fragment,{children:(0,s.jsx)("p",{children:"With the SVG and D3 foundations you’ve built, creating a new chart type becomes a breeze!"})}),toDo:(0,s.jsx)(s.Fragment,{children:(0,s.jsxs)("ul",{children:[(0,s.jsx)("li",{children:"A dataset is provided in the sandbox folder."}),(0,s.jsx)("li",{children:"Build a Cleveland chart with this dataset. (This chart is a variation of the lollipop plot and is helpful for comparing two values across multiple groups.)"}),(0,s.jsx)("li",{children:"Check the solution tab to see the intended chart appearance."})]})}),practiceSandbox:"exercise/LollipopFirstPractice",solutionSandbox:"exercise/LollipopFirstSolution"},{whyItMatters:(0,s.jsx)(s.Fragment,{children:(0,s.jsx)("p",{children:"Descendant selectors are fantastic for creating advanced hover effects with only CSS!"})}),toDo:(0,s.jsx)(s.Fragment,{children:(0,s.jsxs)("ul",{children:[(0,s.jsxs)("li",{children:["Add a class ",(0,s.jsx)("code",{children:"rowsContainer"})," to the SVG container and a class ",(0,s.jsx)("code",{children:"row"})," to each row."]}),(0,s.jsx)("li",{children:"Using a CSS descendant selector, highlight the hovered row and dim other rows."})]})}),practiceSandbox:"exercise/LollipopHoverEffectPractice",solutionSandbox:"exercise/LollipopHoverEffectSolution"},{whyItMatters:(0,s.jsx)(s.Fragment,{children:(0,s.jsx)("p",{children:"The descendant selector technique has limitations! It doesn’t work when the SVG area isn’t fully filled with elements."})}),toDo:(0,s.jsx)(s.Fragment,{children:(0,s.jsxs)("ul",{children:[(0,s.jsx)("li",{children:"Use the CSS descendant selector technique to dim circles that aren’t hovered over when one circle is hovered."}),(0,s.jsx)("li",{children:"What happens when your mouse enters the SVG area but doesn’t reach any circles?"})]})}),practiceSandbox:"exercise/Hover3CirclesDescendantSelectorIssuePractice",solutionSandbox:"exercise/Hover3CirclesDescendantSelectorIssueSolution"}]},60390:function(e,t,n){"use strict";n.d(t,{C:function(){return h}});var s=n(85893);let r={type:"node",name:"boss",value:0,children:[{type:"node",name:"Team Dataviz",value:0,children:[{type:"leaf",name:"Mark",value:90},{type:"leaf",name:"Robert",value:12},{type:"leaf",name:"Emily",value:34},{type:"leaf",name:"Marion",value:53}]},{type:"node",name:"Team DevOps",value:0,children:[{type:"leaf",name:"Nicolas",value:98},{type:"leaf",name:"Malki",value:22},{type:"leaf",name:"Dj\xe9",value:12}]},{type:"node",name:"Team Sales",value:0,children:[{type:"leaf",name:"M\xe9lanie",value:45},{type:"leaf",name:"Einstein",value:76}]}]};var i=n(67294),a=n(43950),l=n(87552),c=n.n(l);let o=["#e0ac2b","#6689c6","#a4c969","#e85252","#9a6fb0","#a53253","#7f7f7f"],d=e=>{let{width:t,height:n,data:r}=e,l=(0,i.useMemo)(()=>a.bT9(r).sum(e=>e.value),[r]),d=null==l?void 0:null===(h=l.children)||void 0===h?void 0:h.map(e=>e.data.name);var h,u=a.PKp().domain(d||[]).range(o);let x=(0,i.useMemo)(()=>{let e=a.pNI().size([t,n]).padding(4);return e(l)},[l,t,n]),m=x.leaves().map((e,t)=>{var n;let r=null===(n=e.parent)||void 0===n?void 0:n.data.name;return(0,s.jsxs)("g",{className:c().rectangle,children:[(0,s.jsx)("rect",{x:e.x0,y:e.y0,width:e.x1-e.x0,height:e.y1-e.y0,stroke:"transparent",fill:u(r),className:"opacity-80 hover:opacity-100"}),(0,s.jsx)("text",{x:e.x0+3,y:e.y0+3,fontSize:12,textAnchor:"start",alignmentBaseline:"hanging",fill:"white",className:"font-bold",children:e.data.name}),(0,s.jsx)("text",{x:e.x0+3,y:e.y0+18,fontSize:12,textAnchor:"start",alignmentBaseline:"hanging",fill:"white",className:"font-light",children:e.data.value})]},e.id)});return(0,s.jsx)("div",{children:(0,s.jsx)("svg",{width:t,height:n,className:c().container,children:m})})},h=e=>{let{width:t=700,height:n=400}=e;return(0,s.jsx)(d,{data:r,width:t,height:n})}},45993:function(e){e.exports={codeChunckCopyButton:"code-block_codeChunckCopyButton__yPrL_",copyButtonContainer:"code-block_copyButtonContainer__BrX9E"}},87552:function(e){e.exports={container:"treemap_container__nYrjn",rectangle:"treemap_rectangle__xOWar"}}},function(e){e.O(0,[2343,7754,3950,7823,9484,8190,3710,693,9774,2888,179],function(){return e(e.s=53458)}),_N_E=e.O()}]);
\ No newline at end of file
diff --git a/_next/static/chunks/pages/course/hover-effect/css-pseudo-class-3878294ee7dd8c58.js b/_next/static/chunks/pages/course/hover-effect/css-pseudo-class-21763a98cb97f4af.js
similarity index 53%
rename from _next/static/chunks/pages/course/hover-effect/css-pseudo-class-3878294ee7dd8c58.js
rename to _next/static/chunks/pages/course/hover-effect/css-pseudo-class-21763a98cb97f4af.js
index 7acb4176..ef1e6959 100644
--- a/_next/static/chunks/pages/course/hover-effect/css-pseudo-class-3878294ee7dd8c58.js
+++ b/_next/static/chunks/pages/course/hover-effect/css-pseudo-class-21763a98cb97f4af.js
@@ -1 +1 @@
-(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[2172],{74730:function(e,t,s){(window.__NEXT_P=window.__NEXT_P||[]).push(["/course/hover-effect/css-pseudo-class",function(){return s(27157)}])},81122:function(e,t,s){"use strict";s.d(t,{$:function(){return o}});var i=s(85893),r=s(67294),n=s(59973),l=s(80615),a=s(88578),c=s(5);let o=e=>{let{VizComponent:t,vizName:s,height:o=400,maxWidth:d=800,caption:h}=e,[x,u]=(0,r.useState)(!1),p=(0,r.useRef)(null),m=(0,n.B)(p);return(0,i.jsx)("div",{style:{marginLeft:"-50vw",left:"50%"},className:"my-4 py-4 w-screen relative",children:x?(0,i.jsxs)("div",{className:"flex flex-col items-center justify-center w-full",children:[(0,i.jsx)("div",{style:{maxWidth:2e3},className:"w-full z-50",children:(0,i.jsx)(a.X,{vizName:s})}),(0,i.jsx)("div",{className:"flex justify-center mt-2",children:(0,i.jsx)(c.z,{size:"sm",onClick:()=>u(!x),children:"Hide Sandbox"})})]}):(0,i.jsxs)("div",{className:"flex flex-col items-center justify-center",children:[(0,i.jsx)("div",{className:"bg-gray-100 bg-opacity-50 w-screen flex justify-center z-50 pointer-events-none",children:(0,i.jsx)("div",{style:{height:o,width:"100%",maxWidth:d},ref:p,className:"pointer-events-auto",children:(0,i.jsx)(t,{height:o,width:m.width})})}),(0,i.jsx)(l.Y,{children:h}),(0,i.jsx)("div",{className:"flex justify-center",children:(0,i.jsx)(c.z,{size:"sm",onClick:()=>u(!x),children:"Show code"})})]})})}},88578:function(e,t,s){"use strict";s.d(t,{X:function(){return r}});var i=s(85893);s(67294);let r=e=>{let{vizName:t}=e;return(0,i.jsx)("iframe",{src:"https://codesandbox.io/embed/github/holtzy/react-graph-gallery/tree/main/viz/"+t+"?fontsize=14&hidenavigation=1&theme=dark&expanddevtools=0",style:{width:"100%",height:"500px",border:"solid",borderWidth:2,borderRadius:"4px",overflow:"hidden"},allow:"accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking",sandbox:"allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"})}},47498:function(e,t,s){"use strict";s.d(t,{v:function(){return d}});var i=s(85893),r=s(7826),n=s(13742),l=s(61108),a=s(67294),c=s(5),o=s(77522);let d=e=>{let{exercises:t,localStorageId:s}=e,[d,h]=(0,a.useState)([]),[x,u]=(0,a.useState)("");return(0,a.useEffect)(()=>{let e=localStorage.getItem(s),i=e?JSON.parse(e):Array(t.length).fill("todo");h(i)},[]),(0,i.jsx)(r.UQ,{value:x,onValueChange:u,type:"single",collapsible:!0,className:"w-full",children:t.map((e,t)=>(0,i.jsxs)(r.Qd,{value:"item-"+t,children:[(0,i.jsx)(r.o4,{className:"no-decoration hover:bg-gray-50",children:(0,i.jsxs)("div",{className:"text-sm flex justify-start gap-2 items-center",children:[(0,i.jsx)("div",{className:(0,o.cn)("text-xs h-6 w-6 flex justify-center items-center rounded-full text-center leading-none","todo"===d[t]?"bg-gray-200":"failed"===d[t]?"bg-red-300":"bg-green-300"),children:(0,i.jsx)("span",{style:{transform:"translateX(1px)"},children:t+1})}),(0,i.jsx)("span",{children:e.title}),"ok"===d[t]&&(0,i.jsx)(n.Z,{size:16,className:"text-green-500"}),"failed"===d[t]&&(0,i.jsx)(l.Z,{size:16,className:"text-red-500"}),"todo"===d[t]&&(0,i.jsx)("span",{className:"text-gray-400 font-thin",children:"ToDo"})]})}),(0,i.jsxs)(r.vF,{children:[e.content,(0,i.jsxs)("div",{className:"flex justify-center gap-4",children:[(0,i.jsx)(c.z,{variant:"outline",onClick:()=>{let e=[...d];e[t]="failed",h(e),localStorage.setItem(s,JSON.stringify(e)),u("")},children:"Failed"}),(0,i.jsxs)(c.z,{onClick:()=>{let e=[...d];e[t]="ok",h(e),localStorage.setItem(s,JSON.stringify(e)),u("")},children:["Done",(0,i.jsx)("span",{className:"ml-2",children:"\uD83C\uDF89"})]})]})]})]}))})}},13400:function(e,t,s){"use strict";s.d(t,{q:function(){return a}});var i=s(85893),r=s(22725),n=s(88578),l=s(8117);let a=e=>{let{exercise:t}=e;return(0,i.jsxs)("div",{children:[(0,i.jsxs)("div",{className:"grid grid-cols-2 gap-4 pt-4",children:[(0,i.jsxs)("div",{children:[(0,i.jsx)(l.C,{children:"To Do"}),(0,i.jsx)("div",{className:"mt-4",children:t.toDo})]}),(0,i.jsxs)("div",{children:[(0,i.jsx)(l.C,{children:"Why it matters"}),(0,i.jsx)("div",{className:"mt-4 pl-4",children:t.whyItMatters})]})]}),(0,i.jsxs)(r.mQ,{defaultValue:"practice",className:"",children:[(0,i.jsx)("center",{children:(0,i.jsxs)(r.dr,{children:[(0,i.jsx)(r.SP,{value:"practice",children:"Practice"}),(0,i.jsx)(r.SP,{value:"solution",children:"Solution"})]})}),(0,i.jsx)(r.nU,{value:"practice",children:(0,i.jsx)("div",{className:"full-bleed my-4 max-w-7xl mx-auto",children:(0,i.jsx)(n.X,{vizName:t.practiceSandbox})})}),(0,i.jsx)(r.nU,{value:"solution",children:(0,i.jsx)("div",{className:"full-bleed my-4 max-w-7xl mx-auto",children:(0,i.jsx)(n.X,{vizName:t.solutionSandbox})})})]})]})}},52450:function(e,t,s){"use strict";s.d(t,{Z:function(){return a}});var i=s(85893);s(67294);var r=s(92050),n=s(49975),l=s(59942);function a(e){let{images:t}=e,s=r.F.filter(e=>t.includes(e.img)).map((e,t)=>(0,i.jsx)(n.X,{link:e.link,title:e.title,description:(0,i.jsx)("p",{children:e.description}),img:e.img,alt:e.alt},t));return(0,i.jsx)(l.E,{children:s})}},72182:function(e,t,s){"use strict";s.d(t,{b:function(){return r}});var i=s(85893);let r=e=>{let{children:t}=e;return(0,i.jsx)("span",{className:"font-semibol bg-purple-100 p-1",children:t})}},80615:function(e,t,s){"use strict";s.d(t,{Y:function(){return r}});var i=s(85893);let r=e=>{let{children:t}=e;return(0,i.jsx)("p",{className:"text-sm text-gray-500 max-w-xs italic text-center mt-4 font-light",children:t})}},3572:function(e,t,s){"use strict";s.d(t,{d:function(){return d}});var i=s(85893),r=s(32581),n=s(15660),l=s.n(n),a=s(67294),c=s(45993),o=s.n(c);let d=e=>{let{code:t}=e,[s,n]=(0,a.useState)(!1),c=(0,a.useRef)(null);(0,a.useEffect)(()=>{c.current&&l().highlightElement(c.current)},[c,t]);let d=(0,i.jsx)("div",{onClick:()=>{navigator.clipboard.writeText(t),n(!0)},className:o().codeChunckCopyButton,children:s?"Copied":(0,i.jsx)(r.Z,{size:14,style:{padding:0}})});return(0,i.jsxs)("div",{className:"mb-6 relative",children:[(0,i.jsx)("pre",{className:"rounded-md line-numbers",children:(0,i.jsx)("code",{ref:c,className:"language-javascript",children:t})}),(0,i.jsx)("div",{className:o().copyButtonContainer,children:d})]})}},7826:function(e,t,s){"use strict";s.d(t,{Qd:function(){return o},UQ:function(){return c},o4:function(){return d},vF:function(){return h}});var i=s(85893),r=s(67294),n=s(47398),l=s(8971),a=s(77522);let c=n.fC,o=r.forwardRef((e,t)=>{let{className:s,...r}=e;return(0,i.jsx)(n.ck,{ref:t,className:(0,a.cn)("border-b",s),...r})});o.displayName="AccordionItem";let d=r.forwardRef((e,t)=>{let{className:s,children:r,...c}=e;return(0,i.jsx)(n.h4,{className:"flex mt-0 pb-0 font-normal",children:(0,i.jsxs)(n.xz,{ref:t,className:(0,a.cn)("flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",s),...c,children:[r,(0,i.jsx)(l.Z,{className:"h-4 w-4 shrink-0 transition-transform duration-200"})]})})});d.displayName=n.xz.displayName;let h=r.forwardRef((e,t)=>{let{className:s,children:r,...l}=e;return(0,i.jsx)(n.VY,{ref:t,className:"overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down",...l,children:(0,i.jsx)("div",{className:(0,a.cn)("pb-4 pt-0",s),children:r})})});h.displayName=n.VY.displayName},59973:function(e,t,s){"use strict";s.d(t,{B:function(){return r}});var i=s(67294);let r=e=>{let t=()=>({width:e.current?e.current.offsetWidth:0,height:e.current?e.current.offsetHeight:0}),[s,r]=(0,i.useState)(t),n=()=>{r(t())};return(0,i.useEffect)(()=>(window.addEventListener("resize",n),()=>window.removeEventListener("resize",n)),[]),(0,i.useEffect)(()=>{n()},[e]),s}},27157:function(e,t,s){"use strict";s.r(t),s.d(t,{default:function(){return v}});var i=s(85893);s(67294);var r=s(43710),n=s(41843),l=s(11236),a=s(81122),c=s(421),o=s(3572),d=s(8117),h=s(52450),x=s(13400),u=s(47498),p=s(41664),m=s.n(p),j=s(72182);let f="/course/hover-effect/css-pseudo-class";function v(){let e=l.Y.find(e=>e.link===f),t=l.Y.findIndex(e=>e.link===f);return e?(0,i.jsxs)(n.p,{title:e.name,seoDescription:"",nextTocItem:l.Y.find(e=>"/course/hover-effect/css-descendant-selector"===e.link),previousTocItem:l.Y.find(e=>"/course/hover-effect/introduction"===e.link),children:[(0,i.jsx)(r.Z,{title:e.name,lessonStatus:e.status,readTime:e.readTime,topBadge:"Lesson "+t,description:(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)("p",{children:"The simplest strategy."}),(0,i.jsxs)("p",{children:["Let's explore how to use a CSS pseudo-class to modify ",(0,i.jsx)("b",{children:"only"})," ","the graph item that is being hovered over."]})]})}),(0,i.jsx)("h2",{children:"What is a pseudo class"}),(0,i.jsxs)("p",{children:["A CSS ",(0,i.jsx)("b",{children:"pseudo-class"})," is a keyword added to a CSS selector that specifies a special state of the selected element(s). You can learn more about pseudo-classes in the"," ",(0,i.jsx)("a",{href:"https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes",children:"MDN doc"}),"."]}),(0,i.jsxs)("p",{children:["Essentially, this means you can assign a class to each shape in a graph and change its appearance ",(0,i.jsx)("b",{children:"when the user hovers"})," over it."]}),(0,i.jsx)("p",{children:"Here is an example:"}),(0,i.jsx)(o.d,{code:"\n.scatterplotCircle {\n cursor: pointer;\n fill-opacity: .3;\n stroke-width: 2px;\n}\n\n.scatterplotCircle:hover {\n fill-opacity: 1;\n stroke-width: 1px;\n}\n".trim()}),(0,i.jsx)("h2",{children:"Application on a scatterplot"}),(0,i.jsxs)("p",{children:["Consider a scatterplot with multiple SVG ",(0,i.jsx)("code",{children:"circle"})," elements, each assigned a ",(0,i.jsx)("code",{children:".scatterplotCircle"})," class. In the CSS file, you can set the ",(0,i.jsx)("code",{children:"fill-opacity"})," to ",(0,i.jsx)("code",{children:"0.3"})," using this class."]}),(0,i.jsxs)("p",{children:["To change the appearance on hover, use the"," ",(0,i.jsx)("code",{children:".scatterplotCircle:hover"})," selector to increase the opacity to 1."]}),(0,i.jsx)(a.$,{vizName:"ScatterplotHoverHighlightPseudoClass",VizComponent:c.z,maxWidth:400,height:500,caption:"Strategy 1: use a pseudo-class to change the appearance of the hovered marker"}),(0,i.jsx)("h2",{children:"Pros & Cons"}),(0,i.jsx)("p",{children:(0,i.jsx)(d.C,{children:"Pros"})}),(0,i.jsxs)("ul",{children:[(0,i.jsx)("li",{children:"Easy to implement"}),(0,i.jsx)("li",{children:"Excellent performance (no JS computation, minimal redrawing)"})]}),(0,i.jsx)("p",{children:(0,i.jsx)(d.C,{variant:"destructive",children:"Cons"})}),(0,i.jsxs)("ul",{children:[(0,i.jsx)("li",{children:"Poor design: non-hovered circles remain prominent, so the highlight effect is weak"}),(0,i.jsx)("li",{children:"If the highlight information comes as a prop, another solution is needed"})]}),(0,i.jsx)("h2",{children:"More examples"}),(0,i.jsx)("p",{children:"The examples below all use this strategy to implement their hover effect."}),(0,i.jsx)(h.Z,{images:["heatmapVaccination.png","treemap-most-basic.png"]}),(0,i.jsx)("h2",{children:"Exercices"}),(0,i.jsx)(u.v,{localStorageId:e.link,exercises:[{title:(0,i.jsx)("span",{children:"Three circles"}),content:(0,i.jsx)(x.q,{exercise:g[0]})},{title:(0,i.jsx)("span",{children:"Your first treemap"}),content:(0,i.jsx)(x.q,{exercise:g[1]})},{title:(0,i.jsxs)("span",{children:["Death by ",(0,i.jsx)("code",{children:"useState()"})]}),content:(0,i.jsx)(x.q,{exercise:g[2]})},{title:(0,i.jsx)("span",{children:"Fix the mess!"}),content:(0,i.jsx)(x.q,{exercise:g[3]})}]})]}):null}let g=[{whyItMatters:(0,i.jsx)(i.Fragment,{children:(0,i.jsxs)("p",{children:["Learn the ",(0,i.jsx)("code",{children:":hover"})," pseudo-class once, and you'll remember it forever!"]})}),toDo:(0,i.jsx)(i.Fragment,{children:(0,i.jsxs)("ul",{children:[(0,i.jsx)("li",{children:"Add 3 circles in the SVG area."}),(0,i.jsxs)("li",{children:["Apply the ",(0,i.jsx)("code",{children:":hover"})," pseudo-class to change their style on hover."]})]})}),practiceSandbox:"exercise/Hover3CirclesPractice",solutionSandbox:"exercise/Hover3CirclesSolution"},{whyItMatters:(0,i.jsxs)(i.Fragment,{children:[(0,i.jsxs)("p",{children:["The ",(0,i.jsx)("code",{children:":hover"})," pseudo-class is especially useful in treemaps, a great alternative to pie charts."]}),(0,i.jsx)("p",{children:"Let's use this opportunity to create your first treemap!"})]}),toDo:(0,i.jsx)(i.Fragment,{children:(0,i.jsxs)("ul",{children:[(0,i.jsx)("li",{children:"A dataset is provided with numeric values for 5 groups (A → E)."}),(0,i.jsxs)("li",{children:["Use d3's ",(0,i.jsx)("code",{children:"treemap()"})," function to calculate positions for the 5 rectangles."]}),(0,i.jsxs)("li",{children:["Render the 5 rectangles using ",(0,i.jsx)("code",{children:"rect"})," elements in the SVG area."]}),(0,i.jsxs)("li",{children:["For more details, refer to the"," ",(0,i.jsx)(m(),{href:"/treemap",children:"treemap section"})," in the gallery!"]})]})}),practiceSandbox:"exercise/HoverFirstTreemapPractice",solutionSandbox:"exercise/HoverFirstTreemapSolution"},{whyItMatters:(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)("p",{children:"Performance matters, and can quicly become a nightmare when dealing with interactivity."}),(0,i.jsx)("p",{children:"This exercise shows a very common mistake: too much rerendering."})]}),toDo:(0,i.jsx)(i.Fragment,{children:(0,i.jsxs)("ul",{children:[(0,i.jsx)("li",{children:"A dataset containing 500,000 data points is provided."}),(0,i.jsxs)("li",{children:["Create a state variable to store the ID of the data point to highlight. This variable should initially be set to"," ",(0,i.jsx)("code",{children:"null"})," and can later hold a value between 0 and 500,000."]}),(0,i.jsx)("li",{children:"Render all 500,000 data points as small circles."}),(0,i.jsx)("li",{children:"Display the circles in black, except for the highlighted circle, which should appear in red."}),(0,i.jsxs)("li",{children:["Add an ",(0,i.jsx)("code",{children:"onMouseOver"})," event to update the state with the ID of the hovered circle."]}),(0,i.jsxs)("li",{children:["Now play with the chart."," ",(0,i.jsx)(j.b,{children:"What do you think about performances?"})]})]})}),practiceSandbox:"exercise/HoverDeathByStatePractice",solutionSandbox:"exercise/HoverDeathByStateSolution"},{whyItMatters:(0,i.jsx)(i.Fragment,{children:(0,i.jsx)("p",{children:"When something can be achieved with CSS only, always ditch Javascript!"})}),toDo:(0,i.jsx)(i.Fragment,{children:(0,i.jsx)("ul",{children:(0,i.jsx)("li",{children:"Fix the previous exercise: instead of using a js state, use a css pseudo class for the hover effect!"})})}),practiceSandbox:"exercise/HoverDeathByStateFixPractice",solutionSandbox:"exercise/HoverDeathByStateFixSolution"}]},45993:function(e){e.exports={codeChunckCopyButton:"code-block_codeChunckCopyButton__yPrL_",copyButtonContainer:"code-block_copyButtonContainer__BrX9E"}}},function(e){e.O(0,[2343,7754,3950,7823,9484,8190,3710,693,7934,9774,2888,179],function(){return e(e.s=74730)}),_N_E=e.O()}]);
\ No newline at end of file
+(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[2172],{74730:function(e,t,s){(window.__NEXT_P=window.__NEXT_P||[]).push(["/course/hover-effect/css-pseudo-class",function(){return s(27157)}])},81122:function(e,t,s){"use strict";s.d(t,{$:function(){return o}});var i=s(85893),r=s(67294),n=s(59973),l=s(80615),a=s(88578),c=s(5);let o=e=>{let{VizComponent:t,vizName:s,height:o=400,maxWidth:d=800,caption:h}=e,[x,u]=(0,r.useState)(!1),p=(0,r.useRef)(null),m=(0,n.B)(p);return(0,i.jsx)("div",{style:{marginLeft:"-50vw",left:"50%"},className:"my-4 py-4 w-screen relative",children:x?(0,i.jsxs)("div",{className:"flex flex-col items-center justify-center w-full",children:[(0,i.jsx)("div",{style:{maxWidth:2e3},className:"w-full z-50",children:(0,i.jsx)(a.X,{vizName:s})}),(0,i.jsx)("div",{className:"flex justify-center mt-2",children:(0,i.jsx)(c.z,{size:"sm",onClick:()=>u(!x),children:"Hide Sandbox"})})]}):(0,i.jsxs)("div",{className:"flex flex-col items-center justify-center",children:[(0,i.jsx)("div",{className:"bg-gray-100 bg-opacity-50 w-screen flex justify-center z-50 pointer-events-none",children:(0,i.jsx)("div",{style:{height:o,width:"100%",maxWidth:d},ref:p,className:"pointer-events-auto",children:(0,i.jsx)(t,{height:o,width:m.width})})}),(0,i.jsx)(l.Y,{children:h}),(0,i.jsx)("div",{className:"flex justify-center",children:(0,i.jsx)(c.z,{size:"sm",onClick:()=>u(!x),children:"Show code"})})]})})}},88578:function(e,t,s){"use strict";s.d(t,{X:function(){return r}});var i=s(85893);s(67294);let r=e=>{let{vizName:t}=e;return(0,i.jsx)("iframe",{src:"https://codesandbox.io/embed/github/holtzy/react-graph-gallery/tree/main/viz/"+t+"?fontsize=14&hidenavigation=1&theme=dark&expanddevtools=0",style:{width:"100%",height:"500px",border:"solid",borderWidth:2,borderRadius:"4px",overflow:"hidden"},allow:"accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking",sandbox:"allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"})}},47498:function(e,t,s){"use strict";s.d(t,{v:function(){return d}});var i=s(85893),r=s(7826),n=s(13742),l=s(61108),a=s(67294),c=s(5),o=s(77522);let d=e=>{let{exercises:t,localStorageId:s}=e,[d,h]=(0,a.useState)([]),[x,u]=(0,a.useState)("");return(0,a.useEffect)(()=>{let e=localStorage.getItem(s),i=e?JSON.parse(e):Array(t.length).fill("todo");h(i)},[]),(0,i.jsx)(r.UQ,{value:x,onValueChange:u,type:"single",collapsible:!0,className:"w-full",children:t.map((e,t)=>(0,i.jsxs)(r.Qd,{value:"item-"+t,children:[(0,i.jsx)(r.o4,{className:"no-decoration hover:bg-gray-50",children:(0,i.jsxs)("div",{className:"text-sm flex justify-start gap-2 items-center",children:[(0,i.jsx)("div",{className:(0,o.cn)("text-xs h-6 w-6 flex justify-center items-center rounded-full text-center leading-none","todo"===d[t]?"bg-gray-200":"failed"===d[t]?"bg-red-300":"bg-green-300"),children:(0,i.jsx)("span",{style:{transform:"translateX(1px)"},children:t+1})}),(0,i.jsx)("span",{children:e.title}),"ok"===d[t]&&(0,i.jsx)(n.Z,{size:16,className:"text-green-500"}),"failed"===d[t]&&(0,i.jsx)(l.Z,{size:16,className:"text-red-500"}),"todo"===d[t]&&(0,i.jsx)("span",{className:"text-gray-400 font-thin",children:"ToDo"})]})}),(0,i.jsxs)(r.vF,{children:[e.content,(0,i.jsxs)("div",{className:"flex justify-center gap-4",children:[(0,i.jsx)(c.z,{variant:"outline",onClick:()=>{let e=[...d];e[t]="failed",h(e),localStorage.setItem(s,JSON.stringify(e)),u("")},children:"Failed"}),(0,i.jsxs)(c.z,{onClick:()=>{let e=[...d];e[t]="ok",h(e),localStorage.setItem(s,JSON.stringify(e)),u("")},children:["Done",(0,i.jsx)("span",{className:"ml-2",children:"\uD83C\uDF89"})]})]})]})]}))})}},13400:function(e,t,s){"use strict";s.d(t,{q:function(){return a}});var i=s(85893),r=s(22725),n=s(88578),l=s(8117);let a=e=>{let{exercise:t}=e;return(0,i.jsxs)("div",{children:[(0,i.jsxs)("div",{className:"grid grid-cols-2 gap-4 pt-4",children:[(0,i.jsxs)("div",{children:[(0,i.jsx)(l.C,{children:"To Do"}),(0,i.jsx)("div",{className:"mt-4",children:t.toDo})]}),(0,i.jsxs)("div",{children:[(0,i.jsx)(l.C,{children:"Why it matters"}),(0,i.jsx)("div",{className:"mt-4 pl-4",children:t.whyItMatters})]})]}),(0,i.jsxs)(r.mQ,{defaultValue:"practice",className:"",children:[(0,i.jsx)("center",{children:(0,i.jsxs)(r.dr,{children:[(0,i.jsx)(r.SP,{value:"practice",children:"Practice"}),(0,i.jsx)(r.SP,{value:"solution",children:"Solution"})]})}),(0,i.jsx)(r.nU,{value:"practice",children:(0,i.jsx)("div",{className:"full-bleed my-4 max-w-7xl mx-auto",children:(0,i.jsx)(n.X,{vizName:t.practiceSandbox})})}),(0,i.jsx)(r.nU,{value:"solution",children:(0,i.jsx)("div",{className:"full-bleed my-4 max-w-7xl mx-auto",children:(0,i.jsx)(n.X,{vizName:t.solutionSandbox})})})]})]})}},52450:function(e,t,s){"use strict";s.d(t,{Z:function(){return a}});var i=s(85893);s(67294);var r=s(92050),n=s(49975),l=s(59942);function a(e){let{images:t}=e,s=r.F.filter(e=>t.includes(e.img)).map((e,t)=>(0,i.jsx)(n.X,{link:e.link,title:e.title,description:(0,i.jsx)("p",{children:e.description}),img:e.img,alt:e.alt},t));return(0,i.jsx)(l.E,{children:s})}},72182:function(e,t,s){"use strict";s.d(t,{b:function(){return r}});var i=s(85893);let r=e=>{let{children:t}=e;return(0,i.jsx)("span",{className:"font-semibol bg-purple-100 p-1",children:t})}},80615:function(e,t,s){"use strict";s.d(t,{Y:function(){return r}});var i=s(85893);let r=e=>{let{children:t}=e;return(0,i.jsx)("p",{className:"text-sm text-gray-500 max-w-xs italic text-center mt-4 font-light",children:t})}},3572:function(e,t,s){"use strict";s.d(t,{d:function(){return d}});var i=s(85893),r=s(32581),n=s(15660),l=s.n(n),a=s(67294),c=s(45993),o=s.n(c);let d=e=>{let{code:t}=e,[s,n]=(0,a.useState)(!1),c=(0,a.useRef)(null);(0,a.useEffect)(()=>{c.current&&l().highlightElement(c.current)},[c,t]);let d=(0,i.jsx)("div",{onClick:()=>{navigator.clipboard.writeText(t),n(!0)},className:o().codeChunckCopyButton,children:s?"Copied":(0,i.jsx)(r.Z,{size:14,style:{padding:0}})});return(0,i.jsxs)("div",{className:"mb-6 relative",children:[(0,i.jsx)("pre",{className:"rounded-md line-numbers",children:(0,i.jsx)("code",{ref:c,className:"language-javascript",children:t})}),(0,i.jsx)("div",{className:o().copyButtonContainer,children:d})]})}},7826:function(e,t,s){"use strict";s.d(t,{Qd:function(){return o},UQ:function(){return c},o4:function(){return d},vF:function(){return h}});var i=s(85893),r=s(67294),n=s(47398),l=s(8971),a=s(77522);let c=n.fC,o=r.forwardRef((e,t)=>{let{className:s,...r}=e;return(0,i.jsx)(n.ck,{ref:t,className:(0,a.cn)("border-b",s),...r})});o.displayName="AccordionItem";let d=r.forwardRef((e,t)=>{let{className:s,children:r,...c}=e;return(0,i.jsx)(n.h4,{className:"flex mt-0 pb-0 font-normal",children:(0,i.jsxs)(n.xz,{ref:t,className:(0,a.cn)("flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",s),...c,children:[r,(0,i.jsx)(l.Z,{className:"h-4 w-4 shrink-0 transition-transform duration-200"})]})})});d.displayName=n.xz.displayName;let h=r.forwardRef((e,t)=>{let{className:s,children:r,...l}=e;return(0,i.jsx)(n.VY,{ref:t,className:"overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down",...l,children:(0,i.jsx)("div",{className:(0,a.cn)("pb-4 pt-0",s),children:r})})});h.displayName=n.VY.displayName},59973:function(e,t,s){"use strict";s.d(t,{B:function(){return r}});var i=s(67294);let r=e=>{let t=()=>({width:e.current?e.current.offsetWidth:0,height:e.current?e.current.offsetHeight:0}),[s,r]=(0,i.useState)(t),n=()=>{r(t())};return(0,i.useEffect)(()=>(window.addEventListener("resize",n),()=>window.removeEventListener("resize",n)),[]),(0,i.useEffect)(()=>{n()},[e]),s}},27157:function(e,t,s){"use strict";s.r(t),s.d(t,{default:function(){return v}});var i=s(85893);s(67294);var r=s(43710),n=s(41843),l=s(11236),a=s(81122),c=s(421),o=s(3572),d=s(8117),h=s(52450),x=s(13400),u=s(47498),p=s(41664),m=s.n(p),j=s(72182);let f="/course/hover-effect/css-pseudo-class";function v(){let e=l.Y.find(e=>e.link===f);return(l.Y.findIndex(e=>e.link===f),e)?(0,i.jsxs)(n.p,{title:e.name,seoDescription:"",nextTocItem:l.Y.find(e=>"/course/hover-effect/css-descendant-selector"===e.link),previousTocItem:l.Y.find(e=>"/course/hover-effect/introduction"===e.link),children:[(0,i.jsx)(r.Z,{title:e.name,lessonStatus:e.status,readTime:e.readTime,selectedLesson:e,description:(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)("p",{children:"The simplest strategy."}),(0,i.jsxs)("p",{children:["Let's explore how to use a CSS pseudo-class to modify ",(0,i.jsx)("b",{children:"only"})," ","the graph item that is being hovered over."]})]})}),(0,i.jsx)("h2",{children:"What is a pseudo class"}),(0,i.jsxs)("p",{children:["A CSS ",(0,i.jsx)("b",{children:"pseudo-class"})," is a keyword added to a CSS selector that specifies a special state of the selected element(s). You can learn more about pseudo-classes in the"," ",(0,i.jsx)("a",{href:"https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes",children:"MDN doc"}),"."]}),(0,i.jsxs)("p",{children:["Essentially, this means you can assign a class to each shape in a graph and change its appearance ",(0,i.jsx)("b",{children:"when the user hovers"})," over it."]}),(0,i.jsx)("p",{children:"Here is an example:"}),(0,i.jsx)(o.d,{code:"\n.scatterplotCircle {\n cursor: pointer;\n fill-opacity: .3;\n stroke-width: 2px;\n}\n\n.scatterplotCircle:hover {\n fill-opacity: 1;\n stroke-width: 1px;\n}\n".trim()}),(0,i.jsx)("h2",{children:"Application on a scatterplot"}),(0,i.jsxs)("p",{children:["Consider a scatterplot with multiple SVG ",(0,i.jsx)("code",{children:"circle"})," elements, each assigned a ",(0,i.jsx)("code",{children:".scatterplotCircle"})," class. In the CSS file, you can set the ",(0,i.jsx)("code",{children:"fill-opacity"})," to ",(0,i.jsx)("code",{children:"0.3"})," using this class."]}),(0,i.jsxs)("p",{children:["To change the appearance on hover, use the"," ",(0,i.jsx)("code",{children:".scatterplotCircle:hover"})," selector to increase the opacity to 1."]}),(0,i.jsx)(a.$,{vizName:"ScatterplotHoverHighlightPseudoClass",VizComponent:c.z,maxWidth:400,height:500,caption:"Strategy 1: use a pseudo-class to change the appearance of the hovered marker"}),(0,i.jsx)("h2",{children:"Pros & Cons"}),(0,i.jsx)("p",{children:(0,i.jsx)(d.C,{children:"Pros"})}),(0,i.jsxs)("ul",{children:[(0,i.jsx)("li",{children:"Easy to implement"}),(0,i.jsx)("li",{children:"Excellent performance (no JS computation, minimal redrawing)"})]}),(0,i.jsx)("p",{children:(0,i.jsx)(d.C,{variant:"destructive",children:"Cons"})}),(0,i.jsxs)("ul",{children:[(0,i.jsx)("li",{children:"Poor design: non-hovered circles remain prominent, so the highlight effect is weak"}),(0,i.jsx)("li",{children:"If the highlight information comes as a prop, another solution is needed"})]}),(0,i.jsx)("h2",{children:"More examples"}),(0,i.jsx)("p",{children:"The examples below all use this strategy to implement their hover effect."}),(0,i.jsx)(h.Z,{images:["heatmapVaccination.png","treemap-most-basic.png"]}),(0,i.jsx)("h2",{children:"Exercices"}),(0,i.jsx)(u.v,{localStorageId:e.link,exercises:[{title:(0,i.jsx)("span",{children:"Three circles"}),content:(0,i.jsx)(x.q,{exercise:g[0]})},{title:(0,i.jsx)("span",{children:"Your first treemap"}),content:(0,i.jsx)(x.q,{exercise:g[1]})},{title:(0,i.jsxs)("span",{children:["Death by ",(0,i.jsx)("code",{children:"useState()"})]}),content:(0,i.jsx)(x.q,{exercise:g[2]})},{title:(0,i.jsx)("span",{children:"Fix the mess!"}),content:(0,i.jsx)(x.q,{exercise:g[3]})}]})]}):null}let g=[{whyItMatters:(0,i.jsx)(i.Fragment,{children:(0,i.jsxs)("p",{children:["Learn the ",(0,i.jsx)("code",{children:":hover"})," pseudo-class once, and you'll remember it forever!"]})}),toDo:(0,i.jsx)(i.Fragment,{children:(0,i.jsxs)("ul",{children:[(0,i.jsx)("li",{children:"Add 3 circles in the SVG area."}),(0,i.jsxs)("li",{children:["Apply the ",(0,i.jsx)("code",{children:":hover"})," pseudo-class to change their style on hover."]})]})}),practiceSandbox:"exercise/Hover3CirclesPractice",solutionSandbox:"exercise/Hover3CirclesSolution"},{whyItMatters:(0,i.jsxs)(i.Fragment,{children:[(0,i.jsxs)("p",{children:["The ",(0,i.jsx)("code",{children:":hover"})," pseudo-class is especially useful in treemaps, a great alternative to pie charts."]}),(0,i.jsx)("p",{children:"Let's use this opportunity to create your first treemap!"})]}),toDo:(0,i.jsx)(i.Fragment,{children:(0,i.jsxs)("ul",{children:[(0,i.jsx)("li",{children:"A dataset is provided with numeric values for 5 groups (A → E)."}),(0,i.jsxs)("li",{children:["Use d3's ",(0,i.jsx)("code",{children:"treemap()"})," function to calculate positions for the 5 rectangles."]}),(0,i.jsxs)("li",{children:["Render the 5 rectangles using ",(0,i.jsx)("code",{children:"rect"})," elements in the SVG area."]}),(0,i.jsxs)("li",{children:["For more details, refer to the"," ",(0,i.jsx)(m(),{href:"/treemap",children:"treemap section"})," in the gallery!"]})]})}),practiceSandbox:"exercise/HoverFirstTreemapPractice",solutionSandbox:"exercise/HoverFirstTreemapSolution"},{whyItMatters:(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)("p",{children:"Performance matters, and can quicly become a nightmare when dealing with interactivity."}),(0,i.jsx)("p",{children:"This exercise shows a very common mistake: too much rerendering."})]}),toDo:(0,i.jsx)(i.Fragment,{children:(0,i.jsxs)("ul",{children:[(0,i.jsx)("li",{children:"A dataset containing 500,000 data points is provided."}),(0,i.jsxs)("li",{children:["Create a state variable to store the ID of the data point to highlight. This variable should initially be set to"," ",(0,i.jsx)("code",{children:"null"})," and can later hold a value between 0 and 500,000."]}),(0,i.jsx)("li",{children:"Render all 500,000 data points as small circles."}),(0,i.jsx)("li",{children:"Display the circles in black, except for the highlighted circle, which should appear in red."}),(0,i.jsxs)("li",{children:["Add an ",(0,i.jsx)("code",{children:"onMouseOver"})," event to update the state with the ID of the hovered circle."]}),(0,i.jsxs)("li",{children:["Now play with the chart."," ",(0,i.jsx)(j.b,{children:"What do you think about performances?"})]})]})}),practiceSandbox:"exercise/HoverDeathByStatePractice",solutionSandbox:"exercise/HoverDeathByStateSolution"},{whyItMatters:(0,i.jsx)(i.Fragment,{children:(0,i.jsx)("p",{children:"When something can be achieved with CSS only, always ditch Javascript!"})}),toDo:(0,i.jsx)(i.Fragment,{children:(0,i.jsx)("ul",{children:(0,i.jsx)("li",{children:"Fix the previous exercise: instead of using a js state, use a css pseudo class for the hover effect!"})})}),practiceSandbox:"exercise/HoverDeathByStateFixPractice",solutionSandbox:"exercise/HoverDeathByStateFixSolution"}]},45993:function(e){e.exports={codeChunckCopyButton:"code-block_codeChunckCopyButton__yPrL_",copyButtonContainer:"code-block_copyButtonContainer__BrX9E"}}},function(e){e.O(0,[2343,7754,3950,7823,9484,8190,3710,693,7934,9774,2888,179],function(){return e(e.s=74730)}),_N_E=e.O()}]);
\ No newline at end of file
diff --git a/_next/static/chunks/pages/course/hover-effect/internal-state-864197e2693011a1.js b/_next/static/chunks/pages/course/hover-effect/internal-state-864197e2693011a1.js
deleted file mode 100644
index ceff0fd0..00000000
--- a/_next/static/chunks/pages/course/hover-effect/internal-state-864197e2693011a1.js
+++ /dev/null
@@ -1 +0,0 @@
-(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[5517],{99591:function(e,t,s){(window.__NEXT_P=window.__NEXT_P||[]).push(["/course/hover-effect/internal-state",function(){return s(68551)}])},81122:function(e,t,s){"use strict";s.d(t,{$:function(){return o}});var r=s(85893),i=s(67294),n=s(59973),l=s(80615),a=s(88578),c=s(5);let o=e=>{let{VizComponent:t,vizName:s,height:o=400,maxWidth:d=800,caption:h}=e,[u,p]=(0,i.useState)(!1),x=(0,i.useRef)(null),f=(0,n.B)(x);return(0,r.jsx)("div",{style:{marginLeft:"-50vw",left:"50%"},className:"my-4 py-4 w-screen relative",children:u?(0,r.jsxs)("div",{className:"flex flex-col items-center justify-center w-full",children:[(0,r.jsx)("div",{style:{maxWidth:2e3},className:"w-full z-50",children:(0,r.jsx)(a.X,{vizName:s})}),(0,r.jsx)("div",{className:"flex justify-center mt-2",children:(0,r.jsx)(c.z,{size:"sm",onClick:()=>p(!u),children:"Hide Sandbox"})})]}):(0,r.jsxs)("div",{className:"flex flex-col items-center justify-center",children:[(0,r.jsx)("div",{className:"bg-gray-100 bg-opacity-50 w-screen flex justify-center z-50 pointer-events-none",children:(0,r.jsx)("div",{style:{height:o,width:"100%",maxWidth:d},ref:x,className:"pointer-events-auto",children:(0,r.jsx)(t,{height:o,width:f.width})})}),(0,r.jsx)(l.Y,{children:h}),(0,r.jsx)("div",{className:"flex justify-center",children:(0,r.jsx)(c.z,{size:"sm",onClick:()=>p(!u),children:"Show code"})})]})})}},88578:function(e,t,s){"use strict";s.d(t,{X:function(){return i}});var r=s(85893);s(67294);let i=e=>{let{vizName:t}=e;return(0,r.jsx)("iframe",{src:"https://codesandbox.io/embed/github/holtzy/react-graph-gallery/tree/main/viz/"+t+"?fontsize=14&hidenavigation=1&theme=dark&expanddevtools=0",style:{width:"100%",height:"500px",border:"solid",borderWidth:2,borderRadius:"4px",overflow:"hidden"},allow:"accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking",sandbox:"allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"})}},41843:function(e,t,s){"use strict";s.d(t,{p:function(){return o}});var r=s(85893),i=s(49700),n=s(63476),l=s(17414),a=s(41664),c=s.n(a);let o=e=>{let{children:t,title:s,seoDescription:a,previousTocItem:o,nextTocItem:d}=e;return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(l.A,{title:s,seoDescription:a}),(0,r.jsx)(i.Z,{}),(0,r.jsx)("div",{className:"wrapper",children:t}),(0,r.jsxs)("div",{className:"flex justify-center items-center space-x-6 my-24 py-12 bg-muted/50",children:[o?(0,r.jsxs)(c(),{href:o.link,className:"text-gray-500 no-underline flex flex-col justify-start items-end w-96 h-32 border-r border-black p-8 hover:bg-muted ",children:[(0,r.jsx)("span",{className:"uppercase font-light text-transparent bg-gradient-to-l to-fuchsia-300 from-blue-400 bg-clip-text",children:"← Previous"}),(0,r.jsx)("p",{children:o.name})]}):(0,r.jsx)("div",{className:"w-96"}),d&&(0,r.jsxs)(c(),{href:d.link,className:"text-gray-500 no-underline flex flex-col justify-start w-96 h-32 border-l border-black p-8 hover:bg-muted ",children:[(0,r.jsx)("span",{className:"uppercase font-light text-transparent bg-gradient-to-l from-fuchsia-300 to-blue-400 bg-clip-text",children:"Next →"}),(0,r.jsx)("p",{children:d.name})]})]}),(0,r.jsx)("div",{className:"wrapper",children:(0,r.jsx)(n.Z,{})})]})}},80615:function(e,t,s){"use strict";s.d(t,{Y:function(){return i}});var r=s(85893);let i=e=>{let{children:t}=e;return(0,r.jsx)("p",{className:"text-sm text-gray-500 max-w-xs italic text-center mt-4 font-light",children:t})}},59973:function(e,t,s){"use strict";s.d(t,{B:function(){return i}});var r=s(67294);let i=e=>{let t=()=>({width:e.current?e.current.offsetWidth:0,height:e.current?e.current.offsetHeight:0}),[s,i]=(0,r.useState)(t),n=()=>{i(t())};return(0,r.useEffect)(()=>(window.addEventListener("resize",n),()=>window.removeEventListener("resize",n)),[]),(0,r.useEffect)(()=>{n()},[e]),s}},68551:function(e,t,s){"use strict";s.r(t),s.d(t,{default:function(){return p}});var r=s(85893);s(67294);var i=s(43710),n=s(41843),l=s(11236),a=s(81122),c=s(3572),o=s(14003),d=s(8117),h=s(52450);let u="/course/hover-effect/internal-state";function p(){let e=l.Y.find(e=>e.link===u),t=l.Y.findIndex(e=>e.link===u);return e?(0,r.jsxs)(n.p,{title:e.name,seoDescription:"",nextTocItem:l.Y.find(e=>""===e.link),previousTocItem:l.Y.find(e=>"/course/hover-effect/toggle-class-in-js"===e.link),children:[(0,r.jsx)(i.Z,{title:e.name,lessonStatus:e.status,readTime:e.readTime,topBadge:"Lesson "+t,description:(0,r.jsxs)(r.Fragment,{children:[(0,r.jsxs)("p",{children:["In the previous lesson, we learned how to modify a hovered graph item using the ",(0,r.jsx)("code",{children:":hover"})," CSS pseudo-class."]}),(0,r.jsxs)("p",{children:["However, this approach has ",(0,r.jsx)("b",{children:"design limitations"}),". To achieve a more effective highlighting effect, it's better to simultaneously"," ",(0,r.jsx)("b",{children:"dim the other graph items"}),"."]}),(0,r.jsx)("p",{children:"This can be accomplished using CSS alone, with the help of the CSS descendant selector."})]})}),(0,r.jsx)("h2",{children:"Internal state & event listener"}),(0,r.jsx)("p",{children:"Add onMouseEnter event listener to all circle"}),(0,r.jsx)("p",{children:"Set an internal state"}),(0,r.jsx)("p",{children:"Trigger a redraw of all circles with conditional state."}),(0,r.jsxs)("p",{children:["As for the tooltip example above, everything starts with an internal state (called ",(0,r.jsx)("code",{children:"hoveredGroup"}),") that stores which circle is hovered hover."]}),(0,r.jsx)(c.d,{code:"\nconst [hoveredGroup, setHoveredGroup] = useState(null);\n".trim()}),(0,r.jsxs)("p",{children:["Now, this state needs to be updated when a user hovers over the circle."," ",(0,r.jsx)("code",{children:"setHoveredGroup"})," can be passed as a callback to the"," ",(0,r.jsx)("code",{children:"onMouseOver"})," attribute of each circle."]}),(0,r.jsxs)("p",{children:["On top of this, some specific css classes can be attributed to circles depending on the circle that is hovered hover. In the example above, a class called ",(0,r.jsx)("code",{children:"dimmed"})," is added to circles that must disappear."]}),(0,r.jsx)("p",{children:"To put it in a nutshell, the circles are created as follows:"}),(0,r.jsx)(c.d,{code:'\nconst allShapes = data.map((d, i) => {\n const className = // class if the circle depends on the hover state\n hoveredGroup && d.group !== hoveredGroup\n ? styles.scatterplotCircle + " " + styles.dimmed\n : styles.scatterplotCircle;\n\n return (\n setHoveredGroup(d.group)} // callback to update the state\n onMouseLeave={() => setHoveredGroup(null)} // and to set it back to null\n />\n );\n});\n'.trim()}),(0,r.jsxs)("p",{children:["Last but not least, some css needs to be added to customize the circle depending on if they are in default, ",(0,r.jsx)("code",{children:".dimmed"})," or"," ",(0,r.jsx)("code",{children:":hover"})," mode."]}),(0,r.jsxs)("p",{children:["Note that the ",(0,r.jsx)("code",{children:"filter: saturate(0)"})," is a good way to dim unwanted circles. Also, playing with ",(0,r.jsx)("code",{children:"transition-delay"})," and"," ",(0,r.jsx)("code",{children:"transition-duration"})," adds to animate the transition is a nice touch you should consider. Check the code below the example to see the full css."]}),(0,r.jsx)(a.$,{vizName:"ScatterplotHoverHighlightDim",VizComponent:o.P,maxWidth:800,height:400,caption:"TODO."}),(0,r.jsx)("h2",{children:"Pros & Cons"}),(0,r.jsx)("p",{children:(0,r.jsx)(d.C,{children:"Pros"})}),(0,r.jsxs)("ul",{children:[(0,r.jsx)("li",{children:"Allows to sync the hover effect with other UI updates. The hovered state can be used to update any other react components in the application. Like tooltip or another graph."}),(0,r.jsx)("li",{children:"Using javascript to trigger the animation can give more flexibility to customize the hover effect, using react-spring for instance."})]}),(0,r.jsx)("p",{children:(0,r.jsx)(d.C,{variant:"destructive",children:"Cons"})}),(0,r.jsx)("ul",{children:(0,r.jsx)("li",{children:"Performance \uD83D\uDEA8. Here we are redrawing all the circles each time a hover effect is hovered. This can be dramatic if you have thousands of circles!"})}),(0,r.jsx)("h2",{children:"More examples"}),(0,r.jsx)("p",{children:"The examples below all use this strategy to implement their hover effect."}),(0,r.jsx)(h.Z,{images:["line-chart-synced-cursor.gif","streamgraph-application.gif"]})]}):null}}},function(e){e.O(0,[2343,7754,3950,7823,8190,3710,693,6155,9774,2888,179],function(){return e(e.s=99591)}),_N_E=e.O()}]);
\ No newline at end of file
diff --git a/_next/static/chunks/pages/course/hover-effect/internal-state-e89978169720b2a8.js b/_next/static/chunks/pages/course/hover-effect/internal-state-e89978169720b2a8.js
new file mode 100644
index 00000000..6b7fa133
--- /dev/null
+++ b/_next/static/chunks/pages/course/hover-effect/internal-state-e89978169720b2a8.js
@@ -0,0 +1 @@
+(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[5517],{99591:function(e,t,s){(window.__NEXT_P=window.__NEXT_P||[]).push(["/course/hover-effect/internal-state",function(){return s(68551)}])},81122:function(e,t,s){"use strict";s.d(t,{$:function(){return o}});var r=s(85893),i=s(67294),n=s(59973),l=s(80615),c=s(88578),a=s(5);let o=e=>{let{VizComponent:t,vizName:s,height:o=400,maxWidth:d=800,caption:h}=e,[u,p]=(0,i.useState)(!1),x=(0,i.useRef)(null),f=(0,n.B)(x);return(0,r.jsx)("div",{style:{marginLeft:"-50vw",left:"50%"},className:"my-4 py-4 w-screen relative",children:u?(0,r.jsxs)("div",{className:"flex flex-col items-center justify-center w-full",children:[(0,r.jsx)("div",{style:{maxWidth:2e3},className:"w-full z-50",children:(0,r.jsx)(c.X,{vizName:s})}),(0,r.jsx)("div",{className:"flex justify-center mt-2",children:(0,r.jsx)(a.z,{size:"sm",onClick:()=>p(!u),children:"Hide Sandbox"})})]}):(0,r.jsxs)("div",{className:"flex flex-col items-center justify-center",children:[(0,r.jsx)("div",{className:"bg-gray-100 bg-opacity-50 w-screen flex justify-center z-50 pointer-events-none",children:(0,r.jsx)("div",{style:{height:o,width:"100%",maxWidth:d},ref:x,className:"pointer-events-auto",children:(0,r.jsx)(t,{height:o,width:f.width})})}),(0,r.jsx)(l.Y,{children:h}),(0,r.jsx)("div",{className:"flex justify-center",children:(0,r.jsx)(a.z,{size:"sm",onClick:()=>p(!u),children:"Show code"})})]})})}},88578:function(e,t,s){"use strict";s.d(t,{X:function(){return i}});var r=s(85893);s(67294);let i=e=>{let{vizName:t}=e;return(0,r.jsx)("iframe",{src:"https://codesandbox.io/embed/github/holtzy/react-graph-gallery/tree/main/viz/"+t+"?fontsize=14&hidenavigation=1&theme=dark&expanddevtools=0",style:{width:"100%",height:"500px",border:"solid",borderWidth:2,borderRadius:"4px",overflow:"hidden"},allow:"accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking",sandbox:"allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"})}},41843:function(e,t,s){"use strict";s.d(t,{p:function(){return o}});var r=s(85893),i=s(49700),n=s(63476),l=s(17414),c=s(41664),a=s.n(c);let o=e=>{let{children:t,title:s,seoDescription:c,previousTocItem:o,nextTocItem:d}=e;return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(l.A,{title:s,seoDescription:c}),(0,r.jsx)(i.Z,{}),(0,r.jsx)("div",{className:"wrapper",children:t}),(0,r.jsxs)("div",{className:"flex justify-center items-center space-x-6 my-24 py-12 bg-muted/50",children:[o?(0,r.jsxs)(a(),{href:o.link,className:"text-gray-500 no-underline flex flex-col justify-start items-end w-96 h-32 border-r border-black p-8 hover:bg-muted ",children:[(0,r.jsx)("span",{className:"uppercase font-light text-transparent bg-gradient-to-l to-fuchsia-300 from-blue-400 bg-clip-text",children:"← Previous"}),(0,r.jsx)("p",{children:o.name})]}):(0,r.jsx)("div",{className:"w-96"}),d&&(0,r.jsxs)(a(),{href:d.link,className:"text-gray-500 no-underline flex flex-col justify-start w-96 h-32 border-l border-black p-8 hover:bg-muted ",children:[(0,r.jsx)("span",{className:"uppercase font-light text-transparent bg-gradient-to-l from-fuchsia-300 to-blue-400 bg-clip-text",children:"Next →"}),(0,r.jsx)("p",{children:d.name})]})]}),(0,r.jsx)("div",{className:"wrapper",children:(0,r.jsx)(n.Z,{})})]})}},80615:function(e,t,s){"use strict";s.d(t,{Y:function(){return i}});var r=s(85893);let i=e=>{let{children:t}=e;return(0,r.jsx)("p",{className:"text-sm text-gray-500 max-w-xs italic text-center mt-4 font-light",children:t})}},59973:function(e,t,s){"use strict";s.d(t,{B:function(){return i}});var r=s(67294);let i=e=>{let t=()=>({width:e.current?e.current.offsetWidth:0,height:e.current?e.current.offsetHeight:0}),[s,i]=(0,r.useState)(t),n=()=>{i(t())};return(0,r.useEffect)(()=>(window.addEventListener("resize",n),()=>window.removeEventListener("resize",n)),[]),(0,r.useEffect)(()=>{n()},[e]),s}},68551:function(e,t,s){"use strict";s.r(t),s.d(t,{default:function(){return p}});var r=s(85893);s(67294);var i=s(43710),n=s(41843),l=s(11236),c=s(81122),a=s(3572),o=s(14003),d=s(8117),h=s(52450);let u="/course/hover-effect/internal-state";function p(){let e=l.Y.find(e=>e.link===u);return(l.Y.findIndex(e=>e.link===u),e)?(0,r.jsxs)(n.p,{title:e.name,seoDescription:"",nextTocItem:l.Y.find(e=>""===e.link),previousTocItem:l.Y.find(e=>"/course/hover-effect/toggle-class-in-js"===e.link),children:[(0,r.jsx)(i.Z,{title:e.name,lessonStatus:e.status,readTime:e.readTime,selectedLesson:e,description:(0,r.jsxs)(r.Fragment,{children:[(0,r.jsxs)("p",{children:["In the previous lesson, we learned how to modify a hovered graph item using the ",(0,r.jsx)("code",{children:":hover"})," CSS pseudo-class."]}),(0,r.jsxs)("p",{children:["However, this approach has ",(0,r.jsx)("b",{children:"design limitations"}),". To achieve a more effective highlighting effect, it's better to simultaneously"," ",(0,r.jsx)("b",{children:"dim the other graph items"}),"."]}),(0,r.jsx)("p",{children:"This can be accomplished using CSS alone, with the help of the CSS descendant selector."})]})}),(0,r.jsx)("h2",{children:"Internal state & event listener"}),(0,r.jsx)("p",{children:"Add onMouseEnter event listener to all circle"}),(0,r.jsx)("p",{children:"Set an internal state"}),(0,r.jsx)("p",{children:"Trigger a redraw of all circles with conditional state."}),(0,r.jsxs)("p",{children:["As for the tooltip example above, everything starts with an internal state (called ",(0,r.jsx)("code",{children:"hoveredGroup"}),") that stores which circle is hovered hover."]}),(0,r.jsx)(a.d,{code:"\nconst [hoveredGroup, setHoveredGroup] = useState(null);\n".trim()}),(0,r.jsxs)("p",{children:["Now, this state needs to be updated when a user hovers over the circle."," ",(0,r.jsx)("code",{children:"setHoveredGroup"})," can be passed as a callback to the"," ",(0,r.jsx)("code",{children:"onMouseOver"})," attribute of each circle."]}),(0,r.jsxs)("p",{children:["On top of this, some specific css classes can be attributed to circles depending on the circle that is hovered hover. In the example above, a class called ",(0,r.jsx)("code",{children:"dimmed"})," is added to circles that must disappear."]}),(0,r.jsx)("p",{children:"To put it in a nutshell, the circles are created as follows:"}),(0,r.jsx)(a.d,{code:'\nconst allShapes = data.map((d, i) => {\n const className = // class if the circle depends on the hover state\n hoveredGroup && d.group !== hoveredGroup\n ? styles.scatterplotCircle + " " + styles.dimmed\n : styles.scatterplotCircle;\n\n return (\n setHoveredGroup(d.group)} // callback to update the state\n onMouseLeave={() => setHoveredGroup(null)} // and to set it back to null\n />\n );\n});\n'.trim()}),(0,r.jsxs)("p",{children:["Last but not least, some css needs to be added to customize the circle depending on if they are in default, ",(0,r.jsx)("code",{children:".dimmed"})," or"," ",(0,r.jsx)("code",{children:":hover"})," mode."]}),(0,r.jsxs)("p",{children:["Note that the ",(0,r.jsx)("code",{children:"filter: saturate(0)"})," is a good way to dim unwanted circles. Also, playing with ",(0,r.jsx)("code",{children:"transition-delay"})," and"," ",(0,r.jsx)("code",{children:"transition-duration"})," adds to animate the transition is a nice touch you should consider. Check the code below the example to see the full css."]}),(0,r.jsx)(c.$,{vizName:"ScatterplotHoverHighlightDim",VizComponent:o.P,maxWidth:800,height:400,caption:"TODO."}),(0,r.jsx)("h2",{children:"Pros & Cons"}),(0,r.jsx)("p",{children:(0,r.jsx)(d.C,{children:"Pros"})}),(0,r.jsxs)("ul",{children:[(0,r.jsx)("li",{children:"Allows to sync the hover effect with other UI updates. The hovered state can be used to update any other react components in the application. Like tooltip or another graph."}),(0,r.jsx)("li",{children:"Using javascript to trigger the animation can give more flexibility to customize the hover effect, using react-spring for instance."})]}),(0,r.jsx)("p",{children:(0,r.jsx)(d.C,{variant:"destructive",children:"Cons"})}),(0,r.jsx)("ul",{children:(0,r.jsx)("li",{children:"Performance \uD83D\uDEA8. Here we are redrawing all the circles each time a hover effect is hovered. This can be dramatic if you have thousands of circles!"})}),(0,r.jsx)("h2",{children:"More examples"}),(0,r.jsx)("p",{children:"The examples below all use this strategy to implement their hover effect."}),(0,r.jsx)(h.Z,{images:["line-chart-synced-cursor.gif","streamgraph-application.gif"]})]}):null}}},function(e){e.O(0,[2343,7754,3950,7823,8190,3710,693,6155,9774,2888,179],function(){return e(e.s=99591)}),_N_E=e.O()}]);
\ No newline at end of file
diff --git a/_next/static/chunks/pages/course/hover-effect/toggle-class-in-js-417afb592eca48d2.js b/_next/static/chunks/pages/course/hover-effect/toggle-class-in-js-417afb592eca48d2.js
new file mode 100644
index 00000000..5378f895
--- /dev/null
+++ b/_next/static/chunks/pages/course/hover-effect/toggle-class-in-js-417afb592eca48d2.js
@@ -0,0 +1 @@
+(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[8947],{44484:function(e,t,n){(window.__NEXT_P=window.__NEXT_P||[]).push(["/course/hover-effect/toggle-class-in-js",function(){return n(39801)}])},81122:function(e,t,n){"use strict";n.d(t,{$:function(){return o}});var s=n(85893),r=n(67294),i=n(59973),a=n(80615),l=n(88578),c=n(5);let o=e=>{let{VizComponent:t,vizName:n,height:o=400,maxWidth:d=800,caption:h}=e,[u,x]=(0,r.useState)(!1),f=(0,r.useRef)(null),m=(0,i.B)(f);return(0,s.jsx)("div",{style:{marginLeft:"-50vw",left:"50%"},className:"my-4 py-4 w-screen relative",children:u?(0,s.jsxs)("div",{className:"flex flex-col items-center justify-center w-full",children:[(0,s.jsx)("div",{style:{maxWidth:2e3},className:"w-full z-50",children:(0,s.jsx)(l.X,{vizName:n})}),(0,s.jsx)("div",{className:"flex justify-center mt-2",children:(0,s.jsx)(c.z,{size:"sm",onClick:()=>x(!u),children:"Hide Sandbox"})})]}):(0,s.jsxs)("div",{className:"flex flex-col items-center justify-center",children:[(0,s.jsx)("div",{className:"bg-gray-100 bg-opacity-50 w-screen flex justify-center z-50 pointer-events-none",children:(0,s.jsx)("div",{style:{height:o,width:"100%",maxWidth:d},ref:f,className:"pointer-events-auto",children:(0,s.jsx)(t,{height:o,width:m.width})})}),(0,s.jsx)(a.Y,{children:h}),(0,s.jsx)("div",{className:"flex justify-center",children:(0,s.jsx)(c.z,{size:"sm",onClick:()=>x(!u),children:"Show code"})})]})})}},88578:function(e,t,n){"use strict";n.d(t,{X:function(){return r}});var s=n(85893);n(67294);let r=e=>{let{vizName:t}=e;return(0,s.jsx)("iframe",{src:"https://codesandbox.io/embed/github/holtzy/react-graph-gallery/tree/main/viz/"+t+"?fontsize=14&hidenavigation=1&theme=dark&expanddevtools=0",style:{width:"100%",height:"500px",border:"solid",borderWidth:2,borderRadius:"4px",overflow:"hidden"},allow:"accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking",sandbox:"allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"})}},47498:function(e,t,n){"use strict";n.d(t,{v:function(){return d}});var s=n(85893),r=n(7826),i=n(13742),a=n(61108),l=n(67294),c=n(5),o=n(77522);let d=e=>{let{exercises:t,localStorageId:n}=e,[d,h]=(0,l.useState)([]),[u,x]=(0,l.useState)("");return(0,l.useEffect)(()=>{let e=localStorage.getItem(n),s=e?JSON.parse(e):Array(t.length).fill("todo");h(s)},[]),(0,s.jsx)(r.UQ,{value:u,onValueChange:x,type:"single",collapsible:!0,className:"w-full",children:t.map((e,t)=>(0,s.jsxs)(r.Qd,{value:"item-"+t,children:[(0,s.jsx)(r.o4,{className:"no-decoration hover:bg-gray-50",children:(0,s.jsxs)("div",{className:"text-sm flex justify-start gap-2 items-center",children:[(0,s.jsx)("div",{className:(0,o.cn)("text-xs h-6 w-6 flex justify-center items-center rounded-full text-center leading-none","todo"===d[t]?"bg-gray-200":"failed"===d[t]?"bg-red-300":"bg-green-300"),children:(0,s.jsx)("span",{style:{transform:"translateX(1px)"},children:t+1})}),(0,s.jsx)("span",{children:e.title}),"ok"===d[t]&&(0,s.jsx)(i.Z,{size:16,className:"text-green-500"}),"failed"===d[t]&&(0,s.jsx)(a.Z,{size:16,className:"text-red-500"}),"todo"===d[t]&&(0,s.jsx)("span",{className:"text-gray-400 font-thin",children:"ToDo"})]})}),(0,s.jsxs)(r.vF,{children:[e.content,(0,s.jsxs)("div",{className:"flex justify-center gap-4",children:[(0,s.jsx)(c.z,{variant:"outline",onClick:()=>{let e=[...d];e[t]="failed",h(e),localStorage.setItem(n,JSON.stringify(e)),x("")},children:"Failed"}),(0,s.jsxs)(c.z,{onClick:()=>{let e=[...d];e[t]="ok",h(e),localStorage.setItem(n,JSON.stringify(e)),x("")},children:["Done",(0,s.jsx)("span",{className:"ml-2",children:"\uD83C\uDF89"})]})]})]})]}))})}},13400:function(e,t,n){"use strict";n.d(t,{q:function(){return l}});var s=n(85893),r=n(22725),i=n(88578),a=n(8117);let l=e=>{let{exercise:t}=e;return(0,s.jsxs)("div",{children:[(0,s.jsxs)("div",{className:"grid grid-cols-2 gap-4 pt-4",children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(a.C,{children:"To Do"}),(0,s.jsx)("div",{className:"mt-4",children:t.toDo})]}),(0,s.jsxs)("div",{children:[(0,s.jsx)(a.C,{children:"Why it matters"}),(0,s.jsx)("div",{className:"mt-4 pl-4",children:t.whyItMatters})]})]}),(0,s.jsxs)(r.mQ,{defaultValue:"practice",className:"",children:[(0,s.jsx)("center",{children:(0,s.jsxs)(r.dr,{children:[(0,s.jsx)(r.SP,{value:"practice",children:"Practice"}),(0,s.jsx)(r.SP,{value:"solution",children:"Solution"})]})}),(0,s.jsx)(r.nU,{value:"practice",children:(0,s.jsx)("div",{className:"full-bleed my-4 max-w-7xl mx-auto",children:(0,s.jsx)(i.X,{vizName:t.practiceSandbox})})}),(0,s.jsx)(r.nU,{value:"solution",children:(0,s.jsx)("div",{className:"full-bleed my-4 max-w-7xl mx-auto",children:(0,s.jsx)(i.X,{vizName:t.solutionSandbox})})})]})]})}},41843:function(e,t,n){"use strict";n.d(t,{p:function(){return o}});var s=n(85893),r=n(49700),i=n(63476),a=n(17414),l=n(41664),c=n.n(l);let o=e=>{let{children:t,title:n,seoDescription:l,previousTocItem:o,nextTocItem:d}=e;return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(a.A,{title:n,seoDescription:l}),(0,s.jsx)(r.Z,{}),(0,s.jsx)("div",{className:"wrapper",children:t}),(0,s.jsxs)("div",{className:"flex justify-center items-center space-x-6 my-24 py-12 bg-muted/50",children:[o?(0,s.jsxs)(c(),{href:o.link,className:"text-gray-500 no-underline flex flex-col justify-start items-end w-96 h-32 border-r border-black p-8 hover:bg-muted ",children:[(0,s.jsx)("span",{className:"uppercase font-light text-transparent bg-gradient-to-l to-fuchsia-300 from-blue-400 bg-clip-text",children:"← Previous"}),(0,s.jsx)("p",{children:o.name})]}):(0,s.jsx)("div",{className:"w-96"}),d&&(0,s.jsxs)(c(),{href:d.link,className:"text-gray-500 no-underline flex flex-col justify-start w-96 h-32 border-l border-black p-8 hover:bg-muted ",children:[(0,s.jsx)("span",{className:"uppercase font-light text-transparent bg-gradient-to-l from-fuchsia-300 to-blue-400 bg-clip-text",children:"Next →"}),(0,s.jsx)("p",{children:d.name})]})]}),(0,s.jsx)("div",{className:"wrapper",children:(0,s.jsx)(i.Z,{})})]})}},63265:function(e,t,n){"use strict";n.d(t,{D:function(){return r}});var s=n(85893);let r=e=>{let{text:t}=e;return(0,s.jsxs)("div",{className:"hidden absolute w-60 top-1/2 -right-10 border-l text-card-foreground border-card-foreground h-28 translate-x-full -translate-y-1/2 xl:flex items-center ",children:[(0,s.jsx)("span",{className:"",children:"→"}),(0,s.jsx)("span",{className:"text-sm ml-2 opacity-70",children:t})]})}},80615:function(e,t,n){"use strict";n.d(t,{Y:function(){return r}});var s=n(85893);let r=e=>{let{children:t}=e;return(0,s.jsx)("p",{className:"text-sm text-gray-500 max-w-xs italic text-center mt-4 font-light",children:t})}},3572:function(e,t,n){"use strict";n.d(t,{d:function(){return d}});var s=n(85893),r=n(32581),i=n(15660),a=n.n(i),l=n(67294),c=n(45993),o=n.n(c);let d=e=>{let{code:t}=e,[n,i]=(0,l.useState)(!1),c=(0,l.useRef)(null);(0,l.useEffect)(()=>{c.current&&a().highlightElement(c.current)},[c,t]);let d=(0,s.jsx)("div",{onClick:()=>{navigator.clipboard.writeText(t),i(!0)},className:o().codeChunckCopyButton,children:n?"Copied":(0,s.jsx)(r.Z,{size:14,style:{padding:0}})});return(0,s.jsxs)("div",{className:"mb-6 relative",children:[(0,s.jsx)("pre",{className:"rounded-md line-numbers",children:(0,s.jsx)("code",{ref:c,className:"language-javascript",children:t})}),(0,s.jsx)("div",{className:o().copyButtonContainer,children:d})]})}},7826:function(e,t,n){"use strict";n.d(t,{Qd:function(){return o},UQ:function(){return c},o4:function(){return d},vF:function(){return h}});var s=n(85893),r=n(67294),i=n(47398),a=n(8971),l=n(77522);let c=i.fC,o=r.forwardRef((e,t)=>{let{className:n,...r}=e;return(0,s.jsx)(i.ck,{ref:t,className:(0,l.cn)("border-b",n),...r})});o.displayName="AccordionItem";let d=r.forwardRef((e,t)=>{let{className:n,children:r,...c}=e;return(0,s.jsx)(i.h4,{className:"flex mt-0 pb-0 font-normal",children:(0,s.jsxs)(i.xz,{ref:t,className:(0,l.cn)("flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",n),...c,children:[r,(0,s.jsx)(a.Z,{className:"h-4 w-4 shrink-0 transition-transform duration-200"})]})})});d.displayName=i.xz.displayName;let h=r.forwardRef((e,t)=>{let{className:n,children:r,...a}=e;return(0,s.jsx)(i.VY,{ref:t,className:"overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down",...a,children:(0,s.jsx)("div",{className:(0,l.cn)("pb-4 pt-0",n),children:r})})});h.displayName=i.VY.displayName},22725:function(e,t,n){"use strict";n.d(t,{SP:function(){return o},dr:function(){return c},mQ:function(){return l},nU:function(){return d}});var s=n(85893),r=n(67294),i=n(60434),a=n(77522);let l=i.fC,c=r.forwardRef((e,t)=>{let{className:n,...r}=e;return(0,s.jsx)(i.aV,{ref:t,className:(0,a.cn)("inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",n),...r})});c.displayName=i.aV.displayName;let o=r.forwardRef((e,t)=>{let{className:n,...r}=e;return(0,s.jsx)(i.xz,{ref:t,className:(0,a.cn)("inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm",n),...r})});o.displayName=i.xz.displayName;let d=r.forwardRef((e,t)=>{let{className:n,...r}=e;return(0,s.jsx)(i.VY,{ref:t,className:(0,a.cn)("mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",n),...r})});d.displayName=i.VY.displayName},59973:function(e,t,n){"use strict";n.d(t,{B:function(){return r}});var s=n(67294);let r=e=>{let t=()=>({width:e.current?e.current.offsetWidth:0,height:e.current?e.current.offsetHeight:0}),[n,r]=(0,s.useState)(t),i=()=>{r(t())};return(0,s.useEffect)(()=>(window.addEventListener("resize",i),()=>window.removeEventListener("resize",i)),[]),(0,s.useEffect)(()=>{i()},[e]),n}},39801:function(e,t,n){"use strict";n.r(t),n.d(t,{default:function(){return y}});var s=n(85893);n(67294);var r=n(43710),i=n(41843),a=n(11236),l=n(81122),c=n(3572),o=n(95789),d=n(63265),h=n(41664),u=n.n(h),x=n(8117),f=n(80615),m=n(77522),p=n(5),j=n(13400),g=n(47498);let v="/course/hover-effect/toggle-class-in-js";function y(){let e=a.Y.find(e=>e.link===v);return(a.Y.findIndex(e=>e.link===v),e)?(0,s.jsxs)(i.p,{title:e.name,seoDescription:"",nextTocItem:a.Y.find(e=>"/course/hover-effect/internal-state"===e.link),previousTocItem:a.Y.find(e=>"/course/hover-effect/css-descendant-selector"===e.link),children:[(0,s.jsx)(r.Z,{title:e.name,lessonStatus:e.status,readTime:e.readTime,selectedLesson:e,description:(0,s.jsxs)(s.Fragment,{children:[(0,s.jsxs)("p",{children:["In the previous lesson, we explored how to dim elements that are"," ",(0,s.jsx)("b",{children:"not"})," being hovered over using a ",(0,s.jsx)("b",{children:"CSS-only"})," approach."]}),(0,s.jsxs)("p",{children:["However, there are times when using JavaScript can provide more"," ",(0,s.jsx)("b",{children:"precise control"})," over the hover effect. A handy technique is to ",(0,s.jsx)("b",{children:"toggle classes"})," with JavaScript. Let’s take a look at how to do this."]})]})}),(0,s.jsx)("h2",{children:"\uD83D\uDD18 Toggle Class in JavaScript"}),(0,s.jsxs)("h3",{children:["1️⃣ Create a ",(0,s.jsx)("code",{children:"ref"})]}),(0,s.jsxs)("p",{children:["We’ve discussed the"," ",(0,s.jsx)("a",{href:"https://react.dev/reference/react/useRef",children:"useRef"})," React hook a few times now."]}),(0,s.jsxs)("p",{children:["This hook allows us to ",(0,s.jsx)("b",{children:"target specific elements"})," in the DOM and manipulate them with JavaScript."]}),(0,s.jsx)(c.d,{code:"\n// Define a ref for the graph container\nconst containerRef = useRef();\n\n// Attach this ref to an element in the DOM\n\n ".trim()}),(0,s.jsx)("h3",{children:"2️⃣ Toggle classes"}),(0,s.jsxs)("p",{children:["Once we have the ",(0,s.jsx)("code",{children:"containerRef"})," set up, we can use it to make changes to the SVG container from anywhere in the graph!"]}),(0,s.jsxs)("p",{children:["For example, we can add an ",(0,s.jsx)("code",{children:"onMouseEnter"})," property to the circle that will apply a ",(0,s.jsx)("code",{children:"hasHighlight"})," class to the SVG container:"]}),(0,s.jsx)(c.d,{code:"\n {\n if (containerRef.current) {\n containerRef.current.classList.add(styles.hasHighlight);\n }\n }}\n onMouseLeave={() => {\n if (containerRef.current) {\n containerRef.current.classList.remove(styles.hasHighlight);\n }\n }}\n/>\n ".trim()}),(0,s.jsx)("h3",{children:"3️⃣ Use it for styling"}),(0,s.jsxs)("div",{className:"relative",children:[(0,s.jsx)(d.D,{text:(0,s.jsxs)("p",{children:["A compound class selector combines multiple class names to target elements that match ",(0,s.jsx)("b",{children:"all"})," of the specified classes. For example: ",(0,s.jsx)("code",{children:".class1.class2"})," ."]})}),(0,s.jsxs)("p",{children:["We can use CSS"," ",(0,s.jsx)("a",{href:"https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_selectors/Selector_structure#compound_selector",children:"compound class selectors"})," ","to apply different styles based on whether the"," ",(0,s.jsx)("code",{children:".hasHighlight"})," class is present!"]})]}),(0,s.jsxs)("p",{children:['For example, we can set the opacity of all "slices" in the container to'," ",(0,s.jsx)("code",{children:"1"}),", except when the container has the"," ",(0,s.jsx)("code",{children:".hasHighlight"})," class, in which case the opacity will be set to ",(0,s.jsx)("code",{children:"0.2"}),":"]}),(0,s.jsx)(c.d,{code:"\n.container .slice {\n opacity: 1;\n cursor: pointer;\n}\n\n.container.hasHighlight .slice {\n opacity: 0.2;\n}\n "}),(0,s.jsx)("h2",{children:"\uD83C\uDF69 Application: Donut Chart with Hover Effect"}),(0,s.jsxs)("p",{children:["A donut chart is a variation of the more well-known"," ",(0,s.jsx)(u(),{href:"/pie-plot",children:"pie chart"}),". It is easy to create using the"," ",(0,s.jsx)("code",{children:"pie()"})," function from D3.js."]}),(0,s.jsx)("p",{children:"The following example demonstrates the technique described earlier. When a slice is hovered over, a class is added to the SVG container, resulting in a CSS change for all the other slices."}),(0,s.jsx)(l.$,{vizName:"DonutChartHover",VizComponent:o.W,maxWidth:800,height:400,caption:"A donut chart with hover interaction using the class toggle strategy."}),(0,s.jsx)("h2",{children:"Pros & Cons"}),(0,s.jsx)("p",{children:(0,s.jsx)(x.C,{children:"Pros"})}),(0,s.jsxs)("ul",{children:[(0,s.jsx)("li",{children:"Fine control over interactions via JavaScript"}),(0,s.jsx)("li",{children:"Performance-friendly: no re-rendering required"})]}),(0,s.jsx)("p",{children:(0,s.jsx)(x.C,{variant:"destructive",children:"Cons"})}),(0,s.jsx)("ul",{children:(0,s.jsx)("li",{children:"Doesn't align with React’s central state management approach, which can make managing state more challenging."})}),(0,s.jsx)("h2",{children:"More examples"}),(0,s.jsx)("p",{children:"The examples below all use this strategy to implement their hover effect."}),(0,s.jsxs)("center",{children:[(0,s.jsxs)("video",{controls:!0,width:"600",children:[(0,s.jsx)("source",{src:"/video/pyramid-legend.mp4",type:"video/mp4"}),"Your browser does not support the video tag."]}),(0,s.jsx)(f.Y,{children:"Check the legend on the left hand side: it uses class toggle for its hover effect!"}),(0,s.jsx)(u(),{className:(0,m.cn)((0,p.d)(),"no-underline"),href:"/example/population-pyramid",children:"Visit project"})]}),(0,s.jsx)("h2",{children:"Exercices"}),(0,s.jsx)(g.v,{localStorageId:e.link,exercises:[{title:(0,s.jsx)("span",{children:"Your first pie chart! \uD83C\uDF6D"}),content:(0,s.jsx)(j.q,{exercise:b[0]})},{title:(0,s.jsx)("span",{children:"Pie chart with hover effect"}),content:(0,s.jsx)(j.q,{exercise:b[1]})}]})]}):null}let b=[{whyItMatters:(0,s.jsx)(s.Fragment,{children:(0,s.jsx)("p",{children:"With the SVG and D3 foundations you’ve built, creating a new chart type becomes a breeze!"})}),toDo:(0,s.jsx)(s.Fragment,{children:(0,s.jsxs)("ul",{children:[(0,s.jsx)("li",{children:"A dataset is provided in the sandbox folder."}),(0,s.jsx)("li",{children:"Build a Cleveland chart with this dataset. (This chart is a variation of the lollipop plot and is helpful for comparing two values across multiple groups.)"}),(0,s.jsx)("li",{children:"Check the solution tab to see the intended chart appearance."})]})}),practiceSandbox:"exercise/PieFirstPractice",solutionSandbox:"exercise/PieFirstSolution"},{whyItMatters:(0,s.jsx)(s.Fragment,{children:(0,s.jsx)("p",{children:"Descendant selectors are fantastic for creating advanced hover effects with only CSS!"})}),toDo:(0,s.jsx)(s.Fragment,{children:(0,s.jsxs)("ul",{children:[(0,s.jsxs)("li",{children:["Add a class ",(0,s.jsx)("code",{children:"rowsContainer"})," to the SVG container and a class ",(0,s.jsx)("code",{children:"row"})," to each row."]}),(0,s.jsx)("li",{children:"Using a CSS descendant selector, highlight the hovered row and dim other rows."})]})}),practiceSandbox:"exercise/LollipopHoverEffectPractice",solutionSandbox:"exercise/LollipopHoverEffectSolution"}]},95789:function(e,t,n){"use strict";n.d(t,{W:function(){return h}});var s=n(85893);let r=[{name:"Mark",value:90},{name:"Robert",value:12},{name:"Emily",value:34},{name:"Marion",value:53},{name:"Nicolas",value:58}];var i=n(67294),a=n(43950),l=n(18431),c=n.n(l);let o=["#e0ac2b","#e85252","#6689c6","#9a6fb0","#a53253","#69b3a2"],d=e=>{let{width:t,height:n,data:r}=e,l=(0,i.useRef)(null),d=Math.min(t-300,n-100)/2,h=d/2,u=(0,i.useMemo)(()=>{let e=a.ve8().value(e=>e.value);return e(r)},[r]),x=a.Nb1(),f=u.map((e,t)=>{let n={innerRadius:h,outerRadius:d,startAngle:e.startAngle,endAngle:e.endAngle},r=x.centroid(n),i=x(n),a={innerRadius:d+20,outerRadius:d+20,startAngle:e.startAngle,endAngle:e.endAngle},u=x.centroid(a),f=u[0]>0,m=u[0]+50*(f?1:-1),p=e.data.name+" ("+e.value+")";return(0,s.jsxs)("g",{className:c().slice,onMouseEnter:()=>{l.current&&l.current.classList.add(c().hasHighlight)},onMouseLeave:()=>{l.current&&l.current.classList.remove(c().hasHighlight)},children:[(0,s.jsx)("path",{d:i,fill:o[t]}),(0,s.jsx)("circle",{cx:r[0],cy:r[1],r:2}),(0,s.jsx)("line",{x1:r[0],y1:r[1],x2:u[0],y2:u[1],stroke:"black",fill:"black"}),(0,s.jsx)("line",{x1:u[0],y1:u[1],x2:m,y2:u[1],stroke:"black",fill:"black"}),(0,s.jsx)("text",{x:m+(f?2:-2),y:u[1],textAnchor:f?"start":"end",dominantBaseline:"middle",fontSize:14,children:p})]},t)});return(0,s.jsx)("svg",{width:t,height:n,style:{display:"inline-block"},children:(0,s.jsx)("g",{transform:"translate(".concat(t/2,", ").concat(n/2,")"),className:c().container,ref:l,children:f})})},h=e=>{let{width:t=700,height:n=400}=e;return(0,s.jsx)(d,{data:r,width:t,height:n})}},45993:function(e){e.exports={codeChunckCopyButton:"code-block_codeChunckCopyButton__yPrL_",copyButtonContainer:"code-block_copyButtonContainer__BrX9E"}},18431:function(e){e.exports={container:"donut-chart_container__TD26m",slice:"donut-chart_slice__DiRt3",hasHighlight:"donut-chart_hasHighlight__CYvny"}}},function(e){e.O(0,[2343,7754,3950,7823,9484,8190,3710,9774,2888,179],function(){return e(e.s=44484)}),_N_E=e.O()}]);
\ No newline at end of file
diff --git a/_next/static/chunks/pages/course/hover-effect/toggle-class-in-js-7209eeba65776501.js b/_next/static/chunks/pages/course/hover-effect/toggle-class-in-js-7209eeba65776501.js
deleted file mode 100644
index 6702b9f8..00000000
--- a/_next/static/chunks/pages/course/hover-effect/toggle-class-in-js-7209eeba65776501.js
+++ /dev/null
@@ -1 +0,0 @@
-(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[8947],{44484:function(e,t,n){(window.__NEXT_P=window.__NEXT_P||[]).push(["/course/hover-effect/toggle-class-in-js",function(){return n(39801)}])},81122:function(e,t,n){"use strict";n.d(t,{$:function(){return o}});var s=n(85893),r=n(67294),i=n(59973),l=n(80615),a=n(88578),c=n(5);let o=e=>{let{VizComponent:t,vizName:n,height:o=400,maxWidth:d=800,caption:h}=e,[u,f]=(0,r.useState)(!1),m=(0,r.useRef)(null),x=(0,i.B)(m);return(0,s.jsx)("div",{style:{marginLeft:"-50vw",left:"50%"},className:"my-4 py-4 w-screen relative",children:u?(0,s.jsxs)("div",{className:"flex flex-col items-center justify-center w-full",children:[(0,s.jsx)("div",{style:{maxWidth:2e3},className:"w-full z-50",children:(0,s.jsx)(a.X,{vizName:n})}),(0,s.jsx)("div",{className:"flex justify-center mt-2",children:(0,s.jsx)(c.z,{size:"sm",onClick:()=>f(!u),children:"Hide Sandbox"})})]}):(0,s.jsxs)("div",{className:"flex flex-col items-center justify-center",children:[(0,s.jsx)("div",{className:"bg-gray-100 bg-opacity-50 w-screen flex justify-center z-50 pointer-events-none",children:(0,s.jsx)("div",{style:{height:o,width:"100%",maxWidth:d},ref:m,className:"pointer-events-auto",children:(0,s.jsx)(t,{height:o,width:x.width})})}),(0,s.jsx)(l.Y,{children:h}),(0,s.jsx)("div",{className:"flex justify-center",children:(0,s.jsx)(c.z,{size:"sm",onClick:()=>f(!u),children:"Show code"})})]})})}},88578:function(e,t,n){"use strict";n.d(t,{X:function(){return r}});var s=n(85893);n(67294);let r=e=>{let{vizName:t}=e;return(0,s.jsx)("iframe",{src:"https://codesandbox.io/embed/github/holtzy/react-graph-gallery/tree/main/viz/"+t+"?fontsize=14&hidenavigation=1&theme=dark&expanddevtools=0",style:{width:"100%",height:"500px",border:"solid",borderWidth:2,borderRadius:"4px",overflow:"hidden"},allow:"accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking",sandbox:"allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"})}},41843:function(e,t,n){"use strict";n.d(t,{p:function(){return o}});var s=n(85893),r=n(49700),i=n(63476),l=n(17414),a=n(41664),c=n.n(a);let o=e=>{let{children:t,title:n,seoDescription:a,previousTocItem:o,nextTocItem:d}=e;return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(l.A,{title:n,seoDescription:a}),(0,s.jsx)(r.Z,{}),(0,s.jsx)("div",{className:"wrapper",children:t}),(0,s.jsxs)("div",{className:"flex justify-center items-center space-x-6 my-24 py-12 bg-muted/50",children:[o?(0,s.jsxs)(c(),{href:o.link,className:"text-gray-500 no-underline flex flex-col justify-start items-end w-96 h-32 border-r border-black p-8 hover:bg-muted ",children:[(0,s.jsx)("span",{className:"uppercase font-light text-transparent bg-gradient-to-l to-fuchsia-300 from-blue-400 bg-clip-text",children:"← Previous"}),(0,s.jsx)("p",{children:o.name})]}):(0,s.jsx)("div",{className:"w-96"}),d&&(0,s.jsxs)(c(),{href:d.link,className:"text-gray-500 no-underline flex flex-col justify-start w-96 h-32 border-l border-black p-8 hover:bg-muted ",children:[(0,s.jsx)("span",{className:"uppercase font-light text-transparent bg-gradient-to-l from-fuchsia-300 to-blue-400 bg-clip-text",children:"Next →"}),(0,s.jsx)("p",{children:d.name})]})]}),(0,s.jsx)("div",{className:"wrapper",children:(0,s.jsx)(i.Z,{})})]})}},80615:function(e,t,n){"use strict";n.d(t,{Y:function(){return r}});var s=n(85893);let r=e=>{let{children:t}=e;return(0,s.jsx)("p",{className:"text-sm text-gray-500 max-w-xs italic text-center mt-4 font-light",children:t})}},3572:function(e,t,n){"use strict";n.d(t,{d:function(){return d}});var s=n(85893),r=n(32581),i=n(15660),l=n.n(i),a=n(67294),c=n(45993),o=n.n(c);let d=e=>{let{code:t}=e,[n,i]=(0,a.useState)(!1),c=(0,a.useRef)(null);(0,a.useEffect)(()=>{c.current&&l().highlightElement(c.current)},[c,t]);let d=(0,s.jsx)("div",{onClick:()=>{navigator.clipboard.writeText(t),i(!0)},className:o().codeChunckCopyButton,children:n?"Copied":(0,s.jsx)(r.Z,{size:14,style:{padding:0}})});return(0,s.jsxs)("div",{className:"mb-6 relative",children:[(0,s.jsx)("pre",{className:"rounded-md line-numbers",children:(0,s.jsx)("code",{ref:c,className:"language-javascript",children:t})}),(0,s.jsx)("div",{className:o().copyButtonContainer,children:d})]})}},59973:function(e,t,n){"use strict";n.d(t,{B:function(){return r}});var s=n(67294);let r=e=>{let t=()=>({width:e.current?e.current.offsetWidth:0,height:e.current?e.current.offsetHeight:0}),[n,r]=(0,s.useState)(t),i=()=>{r(t())};return(0,s.useEffect)(()=>(window.addEventListener("resize",i),()=>window.removeEventListener("resize",i)),[]),(0,s.useEffect)(()=>{i()},[e]),n}},39801:function(e,t,n){"use strict";n.r(t),n.d(t,{default:function(){return h}});var s=n(85893);n(67294);var r=n(43710),i=n(41843),l=n(11236),a=n(81122),c=n(3572),o=n(95789);let d="/course/hover-effect/toggle-class-in-js";function h(){let e=l.Y.find(e=>e.link===d),t=l.Y.findIndex(e=>e.link===d);return e?(0,s.jsxs)(i.p,{title:e.name,seoDescription:"",nextTocItem:l.Y.find(e=>"/course/hover-effect/internal-state"===e.link),previousTocItem:l.Y.find(e=>"/course/hover-effect/css-descendant-selector"===e.link),children:[(0,s.jsx)(r.Z,{title:e.name,lessonStatus:e.status,readTime:e.readTime,topBadge:"Lesson "+t,description:(0,s.jsxs)(s.Fragment,{children:[(0,s.jsxs)("p",{children:["In the previous lesson, we learned how to modify a hovered graph item using the ",(0,s.jsx)("code",{children:":hover"})," CSS pseudo-class."]}),(0,s.jsxs)("p",{children:["However, this approach has ",(0,s.jsx)("b",{children:"design limitations"}),". To achieve a more effective highlighting effect, it's better to simultaneously"," ",(0,s.jsx)("b",{children:"dim the other graph items"}),"."]}),(0,s.jsx)("p",{children:"This can be accomplished using CSS alone, with the help of the CSS descendant selector."})]})}),(0,s.jsx)("h2",{children:"Toggle class in JS"}),(0,s.jsx)("p",{children:"Problem above: when mouse enter the chart area, triggers effect even if no marker hovered over."}),(0,s.jsxs)("p",{children:["Solution: CSS compound class selecter (",(0,s.jsx)("a",{href:"https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_selectors/Selector_structure#compound_selector",children:"MDN doc"}),")"]}),(0,s.jsx)("p",{children:"In CSS, a compound class selector combines multiple class names to target elements that match all of the specified classes."}),(0,s.jsx)("p",{children:"We can use the same css as the above example, but add the highlight class using javascript:"}),(0,s.jsx)(c.d,{code:"\nonMouseEnter={() => {\n if (ref.current) {\n ref.current.classList.add(styles.hasHighlight);\n }\n}}\nonMouseLeave={() => {\n if (ref.current) {\n ref.current.classList.remove(styles.hasHighlight);\n }\n}}\n".trim()}),(0,s.jsx)(a.$,{vizName:"DonutChartHover",VizComponent:o.W,maxWidth:800,height:400,caption:"A donut chart with clean inline legends, built thanks to the centroid function of d3.js."}),(0,s.jsx)("h2",{children:"Pros & Cons"}),(0,s.jsx)("h2",{children:"More examples"}),(0,s.jsx)("p",{children:"The examples below all use this strategy to implement their hover effect."})]}):null}},95789:function(e,t,n){"use strict";n.d(t,{W:function(){return h}});var s=n(85893);let r=[{name:"Mark",value:90},{name:"Robert",value:12},{name:"Emily",value:34},{name:"Marion",value:53},{name:"Nicolas",value:58}];var i=n(67294),l=n(43950),a=n(18431),c=n.n(a);let o=["#e0ac2b","#e85252","#6689c6","#9a6fb0","#a53253","#69b3a2"],d=e=>{let{width:t,height:n,data:r}=e,a=(0,i.useRef)(null),d=Math.min(t-300,n-100)/2,h=d/2,u=(0,i.useMemo)(()=>{let e=l.ve8().value(e=>e.value);return e(r)},[r]),f=l.Nb1(),m=u.map((e,t)=>{let n={innerRadius:h,outerRadius:d,startAngle:e.startAngle,endAngle:e.endAngle},r=f.centroid(n),i=f(n),l={innerRadius:d+20,outerRadius:d+20,startAngle:e.startAngle,endAngle:e.endAngle},u=f.centroid(l),m=u[0]>0,x=u[0]+50*(m?1:-1),p=e.data.name+" ("+e.value+")";return(0,s.jsxs)("g",{className:c().slice,onMouseEnter:()=>{a.current&&a.current.classList.add(c().hasHighlight)},onMouseLeave:()=>{a.current&&a.current.classList.remove(c().hasHighlight)},children:[(0,s.jsx)("path",{d:i,fill:o[t]}),(0,s.jsx)("circle",{cx:r[0],cy:r[1],r:2}),(0,s.jsx)("line",{x1:r[0],y1:r[1],x2:u[0],y2:u[1],stroke:"black",fill:"black"}),(0,s.jsx)("line",{x1:u[0],y1:u[1],x2:x,y2:u[1],stroke:"black",fill:"black"}),(0,s.jsx)("text",{x:x+(m?2:-2),y:u[1],textAnchor:m?"start":"end",dominantBaseline:"middle",fontSize:14,children:p})]},t)});return(0,s.jsx)("svg",{width:t,height:n,style:{display:"inline-block"},children:(0,s.jsx)("g",{transform:"translate(".concat(t/2,", ").concat(n/2,")"),className:c().container,ref:a,children:m})})},h=e=>{let{width:t=700,height:n=400}=e;return(0,s.jsx)(d,{data:r,width:t,height:n})}},45993:function(e){e.exports={codeChunckCopyButton:"code-block_codeChunckCopyButton__yPrL_",copyButtonContainer:"code-block_copyButtonContainer__BrX9E"}},18431:function(e){e.exports={container:"donut-chart_container__TD26m",slice:"donut-chart_slice__DiRt3",hasHighlight:"donut-chart_hasHighlight__CYvny"}}},function(e){e.O(0,[2343,7754,3950,7823,8190,3710,9774,2888,179],function(){return e(e.s=44484)}),_N_E=e.O()}]);
\ No newline at end of file
diff --git a/_next/static/p_WfkCWVWJq62UdEsYwrf/_buildManifest.js b/_next/static/p_WfkCWVWJq62UdEsYwrf/_buildManifest.js
new file mode 100644
index 00000000..af6bdecf
--- /dev/null
+++ b/_next/static/p_WfkCWVWJq62UdEsYwrf/_buildManifest.js
@@ -0,0 +1 @@
+self.__BUILD_MANIFEST=function(s,a,e,c,t,i,o,r,n,d,p,u,l,h,b,g,f,m,k,j,v,x,w,z,y,_,I,B,F,A,D){return{__rewrites:{beforeFiles:[],afterFiles:[],fallback:[]},"/":[s,"static/chunks/2718-c1b4ae8aebf23716.js",a,o,"static/chunks/pages/index-d41f25467ee6dcf3.js"],"/2d-density-plot":[s,e,t,i,a,c,o,x,r,"static/chunks/pages/2d-density-plot-8e76d55419f0acb0.js"],"/404":[s,e,t,a,c,o,k,"static/css/8a734e64c19c3976.css","static/chunks/pages/404-bc0bf98e29fa24ec.js"],"/_error":["static/chunks/pages/_error-82b79221b9ed784b.js"],"/about":[s,e,t,a,c,o,h,"static/chunks/pages/about-279e5ce9b29737d7.js"],"/all":[s,e,a,c,o,n,f,"static/chunks/pages/all-c5ea439386fed3d0.js"],"/animation":[s,e,a,c,o,n,f,"static/chunks/pages/animation-fa9dfe1d4e131eb9.js"],"/arc-diagram":[s,e,t,i,a,c,o,n,u,"static/chunks/pages/arc-diagram-1b852c00738e15fe.js"],"/area-plot":[s,e,t,i,a,c,o,r,"static/chunks/pages/area-plot-2c1f5647cd9c2fb0.js"],"/articles":[s,e,a,c,o,h,"static/chunks/pages/articles-4f71b8e4bef1386a.js"],"/barplot":[s,e,t,i,d,a,c,o,n,j,u,"static/chunks/pages/barplot-b7a43827bd298922.js"],"/boxplot":[s,e,t,i,a,c,o,n,u,"static/chunks/pages/boxplot-7898638b4c49cdc5.js"],"/bubble-map":[s,e,t,i,d,a,c,o,n,m,u,"static/chunks/pages/bubble-map-f787184b8e2ba32c.js"],"/bubble-plot":[s,e,t,i,d,a,c,o,n,k,w,"static/css/ec7558f4d3e32e69.css","static/chunks/pages/bubble-plot-87c67ddfc6048cec.js"],"/build-axis-with-react":[s,e,t,i,a,c,o,r,"static/chunks/pages/build-axis-with-react-c1f3852e6eee15fc.js"],"/cartogram":[s,e,a,c,o,h,"static/chunks/pages/cartogram-3909805a96300cc2.js"],"/chord-diagram":[s,e,t,i,a,c,o,r,"static/chunks/pages/chord-diagram-8adcd5589ce53e82.js"],"/choropleth-map":[s,e,t,i,a,c,o,m,r,"static/chunks/pages/choropleth-map-34a03dbfb95e6e18.js"],"/circular-barplot":[s,e,t,i,a,c,o,r,"static/chunks/pages/circular-barplot-c606b62198ece2a6.js"],"/circular-packing":[s,e,t,i,d,a,c,o,n,u,"static/chunks/pages/circular-packing-d137c5f57bd7ba04.js"],"/connected-scatter-plot":[s,e,t,i,a,c,o,r,"static/chunks/pages/connected-scatter-plot-65ae0694e111681c.js"],"/connection-map":[s,e,t,i,a,c,o,m,r,"static/chunks/pages/connection-map-31fd04baca6aa136.js"],"/correlogram":[s,e,t,i,a,c,o,"static/css/d54486714a313ae1.css","static/chunks/pages/correlogram-c20f4ec376cdb735.js"],"/course/axis/axis-variations":[s,e,a,c,b,"static/chunks/pages/course/axis/axis-variations-e8cf5bd8282a7c5f.js"],"/course/axis/axis-with-d3":[s,e,t,i,a,c,p,"static/chunks/pages/course/axis/axis-with-d3-9b5f696b50a43db5.js"],"/course/axis/bottom-axis":[s,e,t,i,l,a,c,p,"static/chunks/pages/course/axis/bottom-axis-e0152d4e56db8cc8.js"],"/course/axis/introduction":[s,e,t,a,c,b,"static/chunks/pages/course/axis/introduction-66ef24957a309b5d.js"],"/course/axis/margin-and-translation":[s,e,i,l,a,c,p,"static/chunks/pages/course/axis/margin-and-translation-37a39e31753b7631.js"],"/course/axis/project":[s,e,t,i,a,c,w,p,"static/chunks/pages/course/axis/project-922eb5cecf8afa75.js"],"/course/hover-effect/css-descendant-selector":[s,e,t,i,l,a,c,n,"static/css/8666b20defb3e011.css","static/chunks/pages/course/hover-effect/css-descendant-selector-44c9d9449ab2fcc7.js"],"/course/hover-effect/css-pseudo-class":[s,e,t,i,l,a,c,n,z,"static/css/69b5bd49186db9de.css","static/chunks/pages/course/hover-effect/css-pseudo-class-21763a98cb97f4af.js"],"/course/hover-effect/internal-state":[s,e,t,i,a,c,n,v,"static/css/b8e43ac6f7da4f41.css","static/chunks/pages/course/hover-effect/internal-state-e89978169720b2a8.js"],"/course/hover-effect/introduction":[s,e,t,d,a,c,z,"static/css/0ec73f568dde8513.css","static/chunks/pages/course/hover-effect/introduction-d91fdd943b95d1ff.js"],"/course/hover-effect/link-two-graphs":[s,e,t,i,a,c,o,n,v,"static/css/c76003f718a0f9be.css","static/chunks/pages/course/hover-effect/link-two-graphs-7aca58ee5c14c8ba.js"],"/course/hover-effect/toggle-class-in-js":[s,e,t,i,l,a,c,"static/css/f5258d81ed13d0d5.css","static/chunks/pages/course/hover-effect/toggle-class-in-js-417afb592eca48d2.js"],"/course/introduction/initial-setup":[s,e,t,i,a,c,p,"static/chunks/pages/course/introduction/initial-setup-18f7a18d8a190a1a.js"],"/course/introduction/introduction-to-d3":[s,e,t,a,c,b,"static/chunks/pages/course/introduction/introduction-to-d3-2e614dcac280c5bc.js"],"/course/introduction/introduction-to-react":[s,e,a,c,b,"static/chunks/pages/course/introduction/introduction-to-react-c637887e6d1abdb2.js"],"/course/introduction/js-dataviz-libraries":[s,e,i,a,c,p,"static/chunks/pages/course/introduction/js-dataviz-libraries-f7f28061427db698.js"],"/course/responsiveness/code-organization":[s,e,i,a,c,p,"static/chunks/pages/course/responsiveness/code-organization-4dee38ef916f6e9b.js"],"/course/responsiveness/common-pitfalls":[s,e,a,c,b,"static/chunks/pages/course/responsiveness/common-pitfalls-b052d1cc732e6cb4.js"],"/course/responsiveness/introduction":[s,e,t,i,a,c,p,"static/chunks/pages/course/responsiveness/introduction-638287c3a0c0f3e3.js"],"/course/responsiveness/use-dimension-hook":[s,e,i,a,c,p,"static/chunks/pages/course/responsiveness/use-dimension-hook-035fc5d826f40d64.js"],"/course/responsiveness/using-the-hook":[s,e,i,l,a,c,p,"static/chunks/pages/course/responsiveness/using-the-hook-468d826f84201c3b.js"],"/course/scales/introduction":[s,e,i,a,c,y,p,"static/chunks/pages/course/scales/introduction-4801b90c32a802e0.js"],"/course/scales/linear-scale":[s,e,i,l,a,c,y,p,"static/chunks/pages/course/scales/linear-scale-269e54cc7b8d53b2.js"],"/course/scales/other-scale-types":[s,e,t,i,l,a,c,p,"static/chunks/pages/course/scales/other-scale-types-c4ac79f0a493d826.js"],"/course/scales/project":[s,e,t,i,a,c,p,"static/chunks/pages/course/scales/project-6fa3b1ea1f05a1cb.js"],"/course/svg/d3-shape":[s,e,i,l,a,c,p,"static/chunks/pages/course/svg/d3-shape-05e6ff15795469eb.js"],"/course/svg/introduction":[s,e,t,l,a,c,b,"static/chunks/pages/course/svg/introduction-bf4f648178f82258.js"],"/course/svg/main-svg-elements":[s,e,i,l,a,c,p,"static/chunks/pages/course/svg/main-svg-elements-28f74f38034845c0.js"],"/course/svg/path-element":[s,e,i,l,a,c,p,"static/chunks/pages/course/svg/path-element-8f9232fd1e26028f.js"],"/course/svg/tips-and-tricks":[s,e,i,"static/chunks/2860-d038ef2b7795bb05.js",a,c,p,"static/chunks/pages/course/svg/tips-and-tricks-9a97bd616b48d95e.js"],"/cross-graph-highlight-interaction":[s,e,t,i,"static/chunks/8446-4892663fdb75b8e1.js",a,c,o,"static/css/507114ea18f6728e.css","static/chunks/pages/cross-graph-highlight-interaction-e64828c936d371cb.js"],"/dataset-transition":[s,e,t,i,d,a,c,o,j,r,"static/chunks/pages/dataset-transition-8c5d143918b9ba98.js"],"/dendrogram":[s,e,t,i,a,c,o,_,r,"static/chunks/pages/dendrogram-437272f9020c7aab.js"],"/density-plot":[s,e,t,i,a,c,o,I,r,"static/chunks/pages/density-plot-79697b21faa580c8.js"],"/donut":[s,e,t,i,d,g,a,c,o,B,"static/css/306a1a30c14677d4.css","static/chunks/pages/donut-d014466e3ae35d6c.js"],"/example/arc-diagram-vertical":[s,e,t,i,a,c,o,r,"static/chunks/pages/example/arc-diagram-vertical-1d2936674bad45ec.js"],"/example/barplot-data-transition-animation":[s,e,t,i,d,a,c,o,j,r,"static/chunks/pages/example/barplot-data-transition-animation-387eb73885807d83.js"],"/example/barplot-stacked-horizontal":[s,e,t,i,a,c,o,n,u,"static/chunks/pages/example/barplot-stacked-horizontal-15eb521694d01eab.js"],"/example/barplot-stacked-vertical":[s,e,t,a,c,o,n,f,"static/chunks/pages/example/barplot-stacked-vertical-cf6ea7f18bef5a48.js"],"/example/boxplot-horizontal":[s,e,t,i,a,c,o,r,"static/chunks/pages/example/boxplot-horizontal-40407f99d182392c.js"],"/example/boxplot-jitter":[s,e,t,i,a,c,o,r,"static/chunks/pages/example/boxplot-jitter-c1135daa1aa5ebf8.js"],"/example/circle-packing-with-d3-force":[s,e,t,i,a,c,o,r,"static/chunks/pages/example/circle-packing-with-d3-force-c0aecac0282c5ae4.js"],"/example/histogram-mirror":[s,e,t,i,a,c,o,r,"static/chunks/pages/example/histogram-mirror-530991f192320177.js"],"/example/histogram-slider-bin-size":[s,e,t,a,c,o,F,"static/css/f7129d2d0aac0f23.css","static/chunks/pages/example/histogram-slider-bin-size-59efba2c90a549c2.js"],"/example/histogram-small-multiple":[s,e,t,i,a,c,o,r,"static/chunks/pages/example/histogram-small-multiple-0649a35931c6dd35.js"],"/example/histogram-with-several-groups":[s,e,t,i,a,c,o,r,"static/chunks/pages/example/histogram-with-several-groups-4cc9ad645fa4a918.js"],"/example/line-chart-synchronized-cursors":[s,e,t,i,d,a,c,o,r,"static/chunks/pages/example/line-chart-synchronized-cursors-57e0aee3880a52cc.js"],"/example/network-diagram-with-colored-groups":[s,e,t,i,a,c,o,r,"static/chunks/pages/example/network-diagram-with-colored-groups-df23823c173a3ab7.js"],"/example/population-pyramid":[t,d,"static/css/d71181803328322d.css","static/chunks/pages/example/population-pyramid-7f97eae907059157.js"],"/example/radar-chart-animation":[s,e,t,i,d,a,c,o,r,"static/chunks/pages/example/radar-chart-animation-6533e3600e4b0c25.js"],"/example/sankey-bump-chart":[s,e,t,a,c,o,A,h,"static/chunks/pages/example/sankey-bump-chart-b9427974518b1c28.js"],"/example/scatterplot-basic-canvas":[s,e,t,i,a,c,o,r,"static/chunks/pages/example/scatterplot-basic-canvas-01297e9fe27a49ad.js"],"/example/scatterplot-tooltip-with-voronoi-for-closest-point-detection":[s,e,t,i,a,c,o,r,"static/chunks/pages/example/scatterplot-tooltip-with-voronoi-for-closest-point-detection-eb84ebb4bbbe7710.js"],"/example/timeseries-moving-average":[s,e,t,i,a,c,o,n,"static/css/0051346e12b181a2.css","static/chunks/pages/example/timeseries-moving-average-055f2aa890478916.js"],"/fix-canvas-blurry-dataviz":[s,e,i,a,c,o,r,"static/chunks/pages/fix-canvas-blurry-dataviz-4af0c3d36cc71b61.js"],"/heatmap":[s,e,t,i,a,c,o,"static/css/efd490e62b6b40d6.css","static/chunks/pages/heatmap-3ef60d84bee3b76f.js"],"/hexbin-map":[s,e,t,i,a,c,o,x,r,"static/chunks/pages/hexbin-map-31ef98adffd4584e.js"],"/hierarchical-edge-bundling":[s,e,t,i,a,c,o,_,r,"static/chunks/pages/hierarchical-edge-bundling-13f282ff59e73fb9.js"],"/histogram":[s,e,t,i,d,a,c,o,n,F,"static/css/516fbb6bc1065354.css","static/chunks/pages/histogram-ee18170ebe3da0fa.js"],"/interactivity":[s,e,a,c,o,n,f,"static/chunks/pages/interactivity-54e994fb8fdc812d.js"],"/line-chart":[s,e,t,i,d,a,c,o,n,u,"static/chunks/pages/line-chart-24238f4796c26add.js"],"/lollipop-plot":[s,e,t,i,d,a,c,o,n,"static/css/64f55f4f67c1b792.css","static/chunks/pages/lollipop-plot-74730c7cb95c7877.js"],"/map":[s,e,t,i,a,c,o,m,r,"static/chunks/pages/map-6865122529d4f83b.js"],"/network-chart":[s,e,t,i,a,c,o,n,u,"static/chunks/pages/network-chart-49c30f19e190064b.js"],"/parallel-plot":[s,e,t,i,a,c,o,r,"static/chunks/pages/parallel-plot-d74a000124397511.js"],"/pie-plot":[s,e,t,i,d,g,a,c,o,B,"static/css/7b5f8c9d016b3f7c.css","static/chunks/pages/pie-plot-ed5e04bd637cc3ee.js"],"/radar-chart":[s,e,t,i,a,c,o,n,u,"static/chunks/pages/radar-chart-5c17d391fda4e035.js"],"/react-d3-dataviz-course":[s,e,a,c,o,h,"static/chunks/pages/react-d3-dataviz-course-f771d9fed9984c64.js"],"/react-dataviz-animation-with-react-spring":[s,e,t,d,a,c,o,h,"static/chunks/pages/react-dataviz-animation-with-react-spring-d409a9cf2859c8e7.js"],"/ridgeline":[s,e,t,i,a,c,o,n,I,u,"static/chunks/pages/ridgeline-14216a18f86b30c6.js"],"/sankey-diagram":[s,e,t,i,a,c,o,A,r,"static/chunks/pages/sankey-diagram-c434de2d87eb75b2.js"],"/scatter-plot":[s,e,t,i,a,c,o,n,k,v,"static/css/1973cdfb43563a59.css","static/chunks/pages/scatter-plot-0c42cc3a9aa6eb33.js"],"/shape-morphism-for-dataviz-with-react":[s,e,t,i,d,g,a,c,o,r,"static/chunks/pages/shape-morphism-for-dataviz-with-react-97f414e3b4e782fe.js"],"/stacked-area-plot":[s,e,t,i,d,g,a,c,o,D,"static/css/c79e5809ea8fe7eb.css","static/chunks/pages/stacked-area-plot-1c2a5aa2e559ecc9.js"],"/stacked-barplot-with-negative-values":[s,e,t,a,c,o,h,"static/chunks/pages/stacked-barplot-with-negative-values-7274d37ff65a9fa1.js"],"/streamchart":[s,e,t,i,d,g,a,c,o,D,"static/css/003d5c48b04543ca.css","static/chunks/pages/streamchart-96d1a06fd77223b9.js"],"/subscribe":[s,e,a,c,o,h,"static/chunks/pages/subscribe-cd6b3b49e0189ed1.js"],"/timeseries":[s,e,t,i,d,a,c,o,n,u,"static/chunks/pages/timeseries-f587a86e168103e3.js"],"/treemap":[s,e,t,i,a,c,o,"static/css/25919ae4f53719a0.css","static/chunks/pages/treemap-7b74913dbf0ccb6f.js"],"/typescript-d3-axis":[s,e,t,i,a,c,o,r,"static/chunks/pages/typescript-d3-axis-ce34932531553e42.js"],"/violin-plot":[s,e,t,i,a,c,o,r,"static/chunks/pages/violin-plot-6158245d4ece132a.js"],"/viz-from-the-future":[s,e,a,c,o,"static/css/8d4c0e152872e92c.css","static/chunks/pages/viz-from-the-future-49fa6f74df593093.js"],"/voronoi":[s,e,t,i,a,c,o,n,u,"static/chunks/pages/voronoi-d0a49555058afa4d.js"],"/what-is-a-color":[s,e,a,c,o,h,"static/chunks/pages/what-is-a-color-2ceba4025243af3a.js"],"/wordcloud":[s,e,t,i,a,c,o,r,"static/chunks/pages/wordcloud-d3ab46d822e87c14.js"],sortedPages:["/","/2d-density-plot","/404","/_app","/_error","/about","/all","/animation","/arc-diagram","/area-plot","/articles","/barplot","/boxplot","/bubble-map","/bubble-plot","/build-axis-with-react","/cartogram","/chord-diagram","/choropleth-map","/circular-barplot","/circular-packing","/connected-scatter-plot","/connection-map","/correlogram","/course/axis/axis-variations","/course/axis/axis-with-d3","/course/axis/bottom-axis","/course/axis/introduction","/course/axis/margin-and-translation","/course/axis/project","/course/hover-effect/css-descendant-selector","/course/hover-effect/css-pseudo-class","/course/hover-effect/internal-state","/course/hover-effect/introduction","/course/hover-effect/link-two-graphs","/course/hover-effect/toggle-class-in-js","/course/introduction/initial-setup","/course/introduction/introduction-to-d3","/course/introduction/introduction-to-react","/course/introduction/js-dataviz-libraries","/course/responsiveness/code-organization","/course/responsiveness/common-pitfalls","/course/responsiveness/introduction","/course/responsiveness/use-dimension-hook","/course/responsiveness/using-the-hook","/course/scales/introduction","/course/scales/linear-scale","/course/scales/other-scale-types","/course/scales/project","/course/svg/d3-shape","/course/svg/introduction","/course/svg/main-svg-elements","/course/svg/path-element","/course/svg/tips-and-tricks","/cross-graph-highlight-interaction","/dataset-transition","/dendrogram","/density-plot","/donut","/example/arc-diagram-vertical","/example/barplot-data-transition-animation","/example/barplot-stacked-horizontal","/example/barplot-stacked-vertical","/example/boxplot-horizontal","/example/boxplot-jitter","/example/circle-packing-with-d3-force","/example/histogram-mirror","/example/histogram-slider-bin-size","/example/histogram-small-multiple","/example/histogram-with-several-groups","/example/line-chart-synchronized-cursors","/example/network-diagram-with-colored-groups","/example/population-pyramid","/example/radar-chart-animation","/example/sankey-bump-chart","/example/scatterplot-basic-canvas","/example/scatterplot-tooltip-with-voronoi-for-closest-point-detection","/example/timeseries-moving-average","/fix-canvas-blurry-dataviz","/heatmap","/hexbin-map","/hierarchical-edge-bundling","/histogram","/interactivity","/line-chart","/lollipop-plot","/map","/network-chart","/parallel-plot","/pie-plot","/radar-chart","/react-d3-dataviz-course","/react-dataviz-animation-with-react-spring","/ridgeline","/sankey-diagram","/scatter-plot","/shape-morphism-for-dataviz-with-react","/stacked-area-plot","/stacked-barplot-with-negative-values","/streamchart","/subscribe","/timeseries","/treemap","/typescript-d3-axis","/violin-plot","/viz-from-the-future","/voronoi","/what-is-a-color","/wordcloud"]}}("static/chunks/2343-fd77427f6d276a64.js","static/chunks/8190-bc54b059aeb5396b.js","static/chunks/7754-86d7d1d8f1f2cbb4.js","static/chunks/3710-53dd7f451d4900ff.js","static/chunks/3950-38f2f386edd33f86.js","static/chunks/7823-7fe55b50301cbfb1.js","static/chunks/2594-166023395f0e3eec.js","static/css/3f9dc76f65000636.css","static/chunks/693-fc37b082b568d0e9.js","static/chunks/2719-ff70805b79da7e6d.js","static/css/0fa95a85e3c094ce.css","static/css/bf3bc3250ab9e729.css","static/chunks/9484-bdf662975f6102ab.js","static/css/2247407c30b587b7.css","static/css/4aaaca05e34861fe.css","static/chunks/7303-d19d970f114fd8c4.js","static/css/537902226fc25775.css","static/chunks/301-9f8e51278408a4e0.js","static/chunks/814-9ee4c533d2314e88.js","static/chunks/4369-4b12bd667d2c23c6.js","static/chunks/6155-b7c4ab66eeb918c4.js","static/chunks/4131-4e76bf573a6a50e1.js","static/chunks/6535-02079a66600e2e5f.js","static/chunks/7934-43612e1419e096cd.js","static/chunks/4969-a489b2c1a112f705.js","static/chunks/9041-36869f8d32c0dad9.js","static/chunks/7443-7f37deefad5f9f77.js","static/chunks/7067-cedba442dead6f80.js","static/chunks/105-2667407bb5b68dee.js","static/chunks/4590-3c7f7845bc94d3b1.js","static/chunks/3646-27dc3d55488f3ffd.js"),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB();
\ No newline at end of file
diff --git a/_next/static/tQ5BdFMKfGT1EGevRpuDt/_ssgManifest.js b/_next/static/p_WfkCWVWJq62UdEsYwrf/_ssgManifest.js
similarity index 100%
rename from _next/static/tQ5BdFMKfGT1EGevRpuDt/_ssgManifest.js
rename to _next/static/p_WfkCWVWJq62UdEsYwrf/_ssgManifest.js
diff --git a/_next/static/tQ5BdFMKfGT1EGevRpuDt/_buildManifest.js b/_next/static/tQ5BdFMKfGT1EGevRpuDt/_buildManifest.js
deleted file mode 100644
index 549ce359..00000000
--- a/_next/static/tQ5BdFMKfGT1EGevRpuDt/_buildManifest.js
+++ /dev/null
@@ -1 +0,0 @@
-self.__BUILD_MANIFEST=function(s,a,e,c,t,i,o,r,n,d,p,u,l,h,b,g,f,m,k,j,v,x,w,z,y,_,I,B,F,A,D){return{__rewrites:{beforeFiles:[],afterFiles:[],fallback:[]},"/":[s,"static/chunks/2718-c1b4ae8aebf23716.js",a,o,"static/chunks/pages/index-d41f25467ee6dcf3.js"],"/2d-density-plot":[s,e,t,i,a,c,o,x,r,"static/chunks/pages/2d-density-plot-8e76d55419f0acb0.js"],"/404":[s,e,t,a,c,o,k,"static/css/8a734e64c19c3976.css","static/chunks/pages/404-bc0bf98e29fa24ec.js"],"/_error":["static/chunks/pages/_error-82b79221b9ed784b.js"],"/about":[s,e,t,a,c,o,h,"static/chunks/pages/about-279e5ce9b29737d7.js"],"/all":[s,e,a,c,o,n,f,"static/chunks/pages/all-c5ea439386fed3d0.js"],"/animation":[s,e,a,c,o,n,f,"static/chunks/pages/animation-fa9dfe1d4e131eb9.js"],"/arc-diagram":[s,e,t,i,a,c,o,n,u,"static/chunks/pages/arc-diagram-1b852c00738e15fe.js"],"/area-plot":[s,e,t,i,a,c,o,r,"static/chunks/pages/area-plot-2c1f5647cd9c2fb0.js"],"/articles":[s,e,a,c,o,h,"static/chunks/pages/articles-4f71b8e4bef1386a.js"],"/barplot":[s,e,t,i,d,a,c,o,n,j,u,"static/chunks/pages/barplot-b7a43827bd298922.js"],"/boxplot":[s,e,t,i,a,c,o,n,u,"static/chunks/pages/boxplot-7898638b4c49cdc5.js"],"/bubble-map":[s,e,t,i,d,a,c,o,n,m,u,"static/chunks/pages/bubble-map-f787184b8e2ba32c.js"],"/bubble-plot":[s,e,t,i,d,a,c,o,n,k,w,"static/css/ec7558f4d3e32e69.css","static/chunks/pages/bubble-plot-87c67ddfc6048cec.js"],"/build-axis-with-react":[s,e,t,i,a,c,o,r,"static/chunks/pages/build-axis-with-react-c1f3852e6eee15fc.js"],"/cartogram":[s,e,a,c,o,h,"static/chunks/pages/cartogram-3909805a96300cc2.js"],"/chord-diagram":[s,e,t,i,a,c,o,r,"static/chunks/pages/chord-diagram-8adcd5589ce53e82.js"],"/choropleth-map":[s,e,t,i,a,c,o,m,r,"static/chunks/pages/choropleth-map-34a03dbfb95e6e18.js"],"/circular-barplot":[s,e,t,i,a,c,o,r,"static/chunks/pages/circular-barplot-c606b62198ece2a6.js"],"/circular-packing":[s,e,t,i,d,a,c,o,n,u,"static/chunks/pages/circular-packing-d137c5f57bd7ba04.js"],"/connected-scatter-plot":[s,e,t,i,a,c,o,r,"static/chunks/pages/connected-scatter-plot-65ae0694e111681c.js"],"/connection-map":[s,e,t,i,a,c,o,m,r,"static/chunks/pages/connection-map-31fd04baca6aa136.js"],"/correlogram":[s,e,t,i,a,c,o,"static/css/d54486714a313ae1.css","static/chunks/pages/correlogram-c20f4ec376cdb735.js"],"/course/axis/axis-variations":[s,e,a,c,b,"static/chunks/pages/course/axis/axis-variations-e8cf5bd8282a7c5f.js"],"/course/axis/axis-with-d3":[s,e,t,i,a,c,p,"static/chunks/pages/course/axis/axis-with-d3-9b5f696b50a43db5.js"],"/course/axis/bottom-axis":[s,e,t,i,l,a,c,p,"static/chunks/pages/course/axis/bottom-axis-e0152d4e56db8cc8.js"],"/course/axis/introduction":[s,e,t,a,c,b,"static/chunks/pages/course/axis/introduction-66ef24957a309b5d.js"],"/course/axis/margin-and-translation":[s,e,i,l,a,c,p,"static/chunks/pages/course/axis/margin-and-translation-37a39e31753b7631.js"],"/course/axis/project":[s,e,t,i,a,c,w,p,"static/chunks/pages/course/axis/project-922eb5cecf8afa75.js"],"/course/hover-effect/css-descendant-selector":[s,e,t,i,l,a,c,n,"static/css/8666b20defb3e011.css","static/chunks/pages/course/hover-effect/css-descendant-selector-c601c79bbc3a1e96.js"],"/course/hover-effect/css-pseudo-class":[s,e,t,i,l,a,c,n,z,"static/css/69b5bd49186db9de.css","static/chunks/pages/course/hover-effect/css-pseudo-class-3878294ee7dd8c58.js"],"/course/hover-effect/internal-state":[s,e,t,i,a,c,n,v,"static/css/b8e43ac6f7da4f41.css","static/chunks/pages/course/hover-effect/internal-state-864197e2693011a1.js"],"/course/hover-effect/introduction":[s,e,t,d,a,c,z,"static/css/0ec73f568dde8513.css","static/chunks/pages/course/hover-effect/introduction-d91fdd943b95d1ff.js"],"/course/hover-effect/link-two-graphs":[s,e,t,i,a,c,o,n,v,"static/css/c76003f718a0f9be.css","static/chunks/pages/course/hover-effect/link-two-graphs-7aca58ee5c14c8ba.js"],"/course/hover-effect/toggle-class-in-js":[s,e,t,i,a,c,"static/css/f5258d81ed13d0d5.css","static/chunks/pages/course/hover-effect/toggle-class-in-js-7209eeba65776501.js"],"/course/introduction/initial-setup":[s,e,t,i,a,c,p,"static/chunks/pages/course/introduction/initial-setup-18f7a18d8a190a1a.js"],"/course/introduction/introduction-to-d3":[s,e,t,a,c,b,"static/chunks/pages/course/introduction/introduction-to-d3-2e614dcac280c5bc.js"],"/course/introduction/introduction-to-react":[s,e,a,c,b,"static/chunks/pages/course/introduction/introduction-to-react-c637887e6d1abdb2.js"],"/course/introduction/js-dataviz-libraries":[s,e,i,a,c,p,"static/chunks/pages/course/introduction/js-dataviz-libraries-f7f28061427db698.js"],"/course/responsiveness/code-organization":[s,e,i,a,c,p,"static/chunks/pages/course/responsiveness/code-organization-4dee38ef916f6e9b.js"],"/course/responsiveness/common-pitfalls":[s,e,a,c,b,"static/chunks/pages/course/responsiveness/common-pitfalls-b052d1cc732e6cb4.js"],"/course/responsiveness/introduction":[s,e,t,i,a,c,p,"static/chunks/pages/course/responsiveness/introduction-638287c3a0c0f3e3.js"],"/course/responsiveness/use-dimension-hook":[s,e,i,a,c,p,"static/chunks/pages/course/responsiveness/use-dimension-hook-035fc5d826f40d64.js"],"/course/responsiveness/using-the-hook":[s,e,i,l,a,c,p,"static/chunks/pages/course/responsiveness/using-the-hook-468d826f84201c3b.js"],"/course/scales/introduction":[s,e,i,a,c,y,p,"static/chunks/pages/course/scales/introduction-4801b90c32a802e0.js"],"/course/scales/linear-scale":[s,e,i,l,a,c,y,p,"static/chunks/pages/course/scales/linear-scale-269e54cc7b8d53b2.js"],"/course/scales/other-scale-types":[s,e,t,i,l,a,c,p,"static/chunks/pages/course/scales/other-scale-types-c4ac79f0a493d826.js"],"/course/scales/project":[s,e,t,i,a,c,p,"static/chunks/pages/course/scales/project-6fa3b1ea1f05a1cb.js"],"/course/svg/d3-shape":[s,e,i,l,a,c,p,"static/chunks/pages/course/svg/d3-shape-05e6ff15795469eb.js"],"/course/svg/introduction":[s,e,t,l,a,c,b,"static/chunks/pages/course/svg/introduction-bf4f648178f82258.js"],"/course/svg/main-svg-elements":[s,e,i,l,a,c,p,"static/chunks/pages/course/svg/main-svg-elements-28f74f38034845c0.js"],"/course/svg/path-element":[s,e,i,l,a,c,p,"static/chunks/pages/course/svg/path-element-8f9232fd1e26028f.js"],"/course/svg/tips-and-tricks":[s,e,i,"static/chunks/2860-d038ef2b7795bb05.js",a,c,p,"static/chunks/pages/course/svg/tips-and-tricks-9a97bd616b48d95e.js"],"/cross-graph-highlight-interaction":[s,e,t,i,"static/chunks/8446-4892663fdb75b8e1.js",a,c,o,"static/css/507114ea18f6728e.css","static/chunks/pages/cross-graph-highlight-interaction-e64828c936d371cb.js"],"/dataset-transition":[s,e,t,i,d,a,c,o,j,r,"static/chunks/pages/dataset-transition-8c5d143918b9ba98.js"],"/dendrogram":[s,e,t,i,a,c,o,_,r,"static/chunks/pages/dendrogram-437272f9020c7aab.js"],"/density-plot":[s,e,t,i,a,c,o,I,r,"static/chunks/pages/density-plot-79697b21faa580c8.js"],"/donut":[s,e,t,i,d,g,a,c,o,B,"static/css/306a1a30c14677d4.css","static/chunks/pages/donut-d014466e3ae35d6c.js"],"/example/arc-diagram-vertical":[s,e,t,i,a,c,o,r,"static/chunks/pages/example/arc-diagram-vertical-1d2936674bad45ec.js"],"/example/barplot-data-transition-animation":[s,e,t,i,d,a,c,o,j,r,"static/chunks/pages/example/barplot-data-transition-animation-387eb73885807d83.js"],"/example/barplot-stacked-horizontal":[s,e,t,i,a,c,o,n,u,"static/chunks/pages/example/barplot-stacked-horizontal-15eb521694d01eab.js"],"/example/barplot-stacked-vertical":[s,e,t,a,c,o,n,f,"static/chunks/pages/example/barplot-stacked-vertical-cf6ea7f18bef5a48.js"],"/example/boxplot-horizontal":[s,e,t,i,a,c,o,r,"static/chunks/pages/example/boxplot-horizontal-40407f99d182392c.js"],"/example/boxplot-jitter":[s,e,t,i,a,c,o,r,"static/chunks/pages/example/boxplot-jitter-c1135daa1aa5ebf8.js"],"/example/circle-packing-with-d3-force":[s,e,t,i,a,c,o,r,"static/chunks/pages/example/circle-packing-with-d3-force-c0aecac0282c5ae4.js"],"/example/histogram-mirror":[s,e,t,i,a,c,o,r,"static/chunks/pages/example/histogram-mirror-530991f192320177.js"],"/example/histogram-slider-bin-size":[s,e,t,a,c,o,F,"static/css/f7129d2d0aac0f23.css","static/chunks/pages/example/histogram-slider-bin-size-59efba2c90a549c2.js"],"/example/histogram-small-multiple":[s,e,t,i,a,c,o,r,"static/chunks/pages/example/histogram-small-multiple-0649a35931c6dd35.js"],"/example/histogram-with-several-groups":[s,e,t,i,a,c,o,r,"static/chunks/pages/example/histogram-with-several-groups-4cc9ad645fa4a918.js"],"/example/line-chart-synchronized-cursors":[s,e,t,i,d,a,c,o,r,"static/chunks/pages/example/line-chart-synchronized-cursors-57e0aee3880a52cc.js"],"/example/network-diagram-with-colored-groups":[s,e,t,i,a,c,o,r,"static/chunks/pages/example/network-diagram-with-colored-groups-df23823c173a3ab7.js"],"/example/population-pyramid":[t,d,"static/css/d71181803328322d.css","static/chunks/pages/example/population-pyramid-7f97eae907059157.js"],"/example/radar-chart-animation":[s,e,t,i,d,a,c,o,r,"static/chunks/pages/example/radar-chart-animation-6533e3600e4b0c25.js"],"/example/sankey-bump-chart":[s,e,t,a,c,o,A,h,"static/chunks/pages/example/sankey-bump-chart-b9427974518b1c28.js"],"/example/scatterplot-basic-canvas":[s,e,t,i,a,c,o,r,"static/chunks/pages/example/scatterplot-basic-canvas-01297e9fe27a49ad.js"],"/example/scatterplot-tooltip-with-voronoi-for-closest-point-detection":[s,e,t,i,a,c,o,r,"static/chunks/pages/example/scatterplot-tooltip-with-voronoi-for-closest-point-detection-eb84ebb4bbbe7710.js"],"/example/timeseries-moving-average":[s,e,t,i,a,c,o,n,"static/css/0051346e12b181a2.css","static/chunks/pages/example/timeseries-moving-average-055f2aa890478916.js"],"/fix-canvas-blurry-dataviz":[s,e,i,a,c,o,r,"static/chunks/pages/fix-canvas-blurry-dataviz-4af0c3d36cc71b61.js"],"/heatmap":[s,e,t,i,a,c,o,"static/css/efd490e62b6b40d6.css","static/chunks/pages/heatmap-3ef60d84bee3b76f.js"],"/hexbin-map":[s,e,t,i,a,c,o,x,r,"static/chunks/pages/hexbin-map-31ef98adffd4584e.js"],"/hierarchical-edge-bundling":[s,e,t,i,a,c,o,_,r,"static/chunks/pages/hierarchical-edge-bundling-13f282ff59e73fb9.js"],"/histogram":[s,e,t,i,d,a,c,o,n,F,"static/css/516fbb6bc1065354.css","static/chunks/pages/histogram-ee18170ebe3da0fa.js"],"/interactivity":[s,e,a,c,o,n,f,"static/chunks/pages/interactivity-54e994fb8fdc812d.js"],"/line-chart":[s,e,t,i,d,a,c,o,n,u,"static/chunks/pages/line-chart-24238f4796c26add.js"],"/lollipop-plot":[s,e,t,i,d,a,c,o,n,"static/css/64f55f4f67c1b792.css","static/chunks/pages/lollipop-plot-74730c7cb95c7877.js"],"/map":[s,e,t,i,a,c,o,m,r,"static/chunks/pages/map-6865122529d4f83b.js"],"/network-chart":[s,e,t,i,a,c,o,n,u,"static/chunks/pages/network-chart-49c30f19e190064b.js"],"/parallel-plot":[s,e,t,i,a,c,o,r,"static/chunks/pages/parallel-plot-d74a000124397511.js"],"/pie-plot":[s,e,t,i,d,g,a,c,o,B,"static/css/7b5f8c9d016b3f7c.css","static/chunks/pages/pie-plot-ed5e04bd637cc3ee.js"],"/radar-chart":[s,e,t,i,a,c,o,n,u,"static/chunks/pages/radar-chart-5c17d391fda4e035.js"],"/react-d3-dataviz-course":[s,e,a,c,o,h,"static/chunks/pages/react-d3-dataviz-course-f771d9fed9984c64.js"],"/react-dataviz-animation-with-react-spring":[s,e,t,d,a,c,o,h,"static/chunks/pages/react-dataviz-animation-with-react-spring-d409a9cf2859c8e7.js"],"/ridgeline":[s,e,t,i,a,c,o,n,I,u,"static/chunks/pages/ridgeline-14216a18f86b30c6.js"],"/sankey-diagram":[s,e,t,i,a,c,o,A,r,"static/chunks/pages/sankey-diagram-c434de2d87eb75b2.js"],"/scatter-plot":[s,e,t,i,a,c,o,n,k,v,"static/css/1973cdfb43563a59.css","static/chunks/pages/scatter-plot-0c42cc3a9aa6eb33.js"],"/shape-morphism-for-dataviz-with-react":[s,e,t,i,d,g,a,c,o,r,"static/chunks/pages/shape-morphism-for-dataviz-with-react-97f414e3b4e782fe.js"],"/stacked-area-plot":[s,e,t,i,d,g,a,c,o,D,"static/css/c79e5809ea8fe7eb.css","static/chunks/pages/stacked-area-plot-1c2a5aa2e559ecc9.js"],"/stacked-barplot-with-negative-values":[s,e,t,a,c,o,h,"static/chunks/pages/stacked-barplot-with-negative-values-7274d37ff65a9fa1.js"],"/streamchart":[s,e,t,i,d,g,a,c,o,D,"static/css/003d5c48b04543ca.css","static/chunks/pages/streamchart-96d1a06fd77223b9.js"],"/subscribe":[s,e,a,c,o,h,"static/chunks/pages/subscribe-cd6b3b49e0189ed1.js"],"/timeseries":[s,e,t,i,d,a,c,o,n,u,"static/chunks/pages/timeseries-f587a86e168103e3.js"],"/treemap":[s,e,t,i,a,c,o,"static/css/25919ae4f53719a0.css","static/chunks/pages/treemap-7b74913dbf0ccb6f.js"],"/typescript-d3-axis":[s,e,t,i,a,c,o,r,"static/chunks/pages/typescript-d3-axis-ce34932531553e42.js"],"/violin-plot":[s,e,t,i,a,c,o,r,"static/chunks/pages/violin-plot-6158245d4ece132a.js"],"/viz-from-the-future":[s,e,a,c,o,"static/css/8d4c0e152872e92c.css","static/chunks/pages/viz-from-the-future-49fa6f74df593093.js"],"/voronoi":[s,e,t,i,a,c,o,n,u,"static/chunks/pages/voronoi-d0a49555058afa4d.js"],"/what-is-a-color":[s,e,a,c,o,h,"static/chunks/pages/what-is-a-color-2ceba4025243af3a.js"],"/wordcloud":[s,e,t,i,a,c,o,r,"static/chunks/pages/wordcloud-d3ab46d822e87c14.js"],sortedPages:["/","/2d-density-plot","/404","/_app","/_error","/about","/all","/animation","/arc-diagram","/area-plot","/articles","/barplot","/boxplot","/bubble-map","/bubble-plot","/build-axis-with-react","/cartogram","/chord-diagram","/choropleth-map","/circular-barplot","/circular-packing","/connected-scatter-plot","/connection-map","/correlogram","/course/axis/axis-variations","/course/axis/axis-with-d3","/course/axis/bottom-axis","/course/axis/introduction","/course/axis/margin-and-translation","/course/axis/project","/course/hover-effect/css-descendant-selector","/course/hover-effect/css-pseudo-class","/course/hover-effect/internal-state","/course/hover-effect/introduction","/course/hover-effect/link-two-graphs","/course/hover-effect/toggle-class-in-js","/course/introduction/initial-setup","/course/introduction/introduction-to-d3","/course/introduction/introduction-to-react","/course/introduction/js-dataviz-libraries","/course/responsiveness/code-organization","/course/responsiveness/common-pitfalls","/course/responsiveness/introduction","/course/responsiveness/use-dimension-hook","/course/responsiveness/using-the-hook","/course/scales/introduction","/course/scales/linear-scale","/course/scales/other-scale-types","/course/scales/project","/course/svg/d3-shape","/course/svg/introduction","/course/svg/main-svg-elements","/course/svg/path-element","/course/svg/tips-and-tricks","/cross-graph-highlight-interaction","/dataset-transition","/dendrogram","/density-plot","/donut","/example/arc-diagram-vertical","/example/barplot-data-transition-animation","/example/barplot-stacked-horizontal","/example/barplot-stacked-vertical","/example/boxplot-horizontal","/example/boxplot-jitter","/example/circle-packing-with-d3-force","/example/histogram-mirror","/example/histogram-slider-bin-size","/example/histogram-small-multiple","/example/histogram-with-several-groups","/example/line-chart-synchronized-cursors","/example/network-diagram-with-colored-groups","/example/population-pyramid","/example/radar-chart-animation","/example/sankey-bump-chart","/example/scatterplot-basic-canvas","/example/scatterplot-tooltip-with-voronoi-for-closest-point-detection","/example/timeseries-moving-average","/fix-canvas-blurry-dataviz","/heatmap","/hexbin-map","/hierarchical-edge-bundling","/histogram","/interactivity","/line-chart","/lollipop-plot","/map","/network-chart","/parallel-plot","/pie-plot","/radar-chart","/react-d3-dataviz-course","/react-dataviz-animation-with-react-spring","/ridgeline","/sankey-diagram","/scatter-plot","/shape-morphism-for-dataviz-with-react","/stacked-area-plot","/stacked-barplot-with-negative-values","/streamchart","/subscribe","/timeseries","/treemap","/typescript-d3-axis","/violin-plot","/viz-from-the-future","/voronoi","/what-is-a-color","/wordcloud"]}}("static/chunks/2343-fd77427f6d276a64.js","static/chunks/8190-bc54b059aeb5396b.js","static/chunks/7754-86d7d1d8f1f2cbb4.js","static/chunks/3710-53dd7f451d4900ff.js","static/chunks/3950-38f2f386edd33f86.js","static/chunks/7823-7fe55b50301cbfb1.js","static/chunks/2594-166023395f0e3eec.js","static/css/3f9dc76f65000636.css","static/chunks/693-fc37b082b568d0e9.js","static/chunks/2719-ff70805b79da7e6d.js","static/css/0fa95a85e3c094ce.css","static/css/bf3bc3250ab9e729.css","static/chunks/9484-bdf662975f6102ab.js","static/css/2247407c30b587b7.css","static/css/4aaaca05e34861fe.css","static/chunks/7303-d19d970f114fd8c4.js","static/css/537902226fc25775.css","static/chunks/301-9f8e51278408a4e0.js","static/chunks/814-9ee4c533d2314e88.js","static/chunks/4369-4b12bd667d2c23c6.js","static/chunks/6155-b7c4ab66eeb918c4.js","static/chunks/4131-4e76bf573a6a50e1.js","static/chunks/6535-02079a66600e2e5f.js","static/chunks/7934-43612e1419e096cd.js","static/chunks/4969-a489b2c1a112f705.js","static/chunks/9041-36869f8d32c0dad9.js","static/chunks/7443-7f37deefad5f9f77.js","static/chunks/7067-cedba442dead6f80.js","static/chunks/105-2667407bb5b68dee.js","static/chunks/4590-3c7f7845bc94d3b1.js","static/chunks/3646-27dc3d55488f3ffd.js"),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB();
\ No newline at end of file
diff --git a/about.html b/about.html
index 42b4a63d..cb650b36 100644
--- a/about.html
+++ b/about.html
@@ -1,4 +1,4 @@
-About the gallery
React + D3.js = ❤️
A love story – So simplepowerful yet so complicated
A few years ago I created the d3 graph gallery, a website showcasing hundreds of simple charts made with d3.js. It worked well! Thousands of people use it daily to learn d3. 🎉
Since then, React became the most popular framework to build user interfaces. This rose a question: how to build a chart in react? That's a complicated question with many answers. Here are the 3 most common approaches.
3 ways to draw a chart in react
→ 1️⃣ Charting libraries
There is a myriad of charting libraries offering react components for every chart type. HighChart, ReCharts, React-viz, plot, visX and so many more. Those libraries are awesome: you'll get a working chart in minutes using them.
But simplicity comes with a cost: the time you saved in the first place will be lost when you'll try to reach a high level of customization.
If you want to build something unique, you need to draw shapes one by one.
→ 2️⃣ D3 for rendering in a useEffect hook
If you're familiar with d3.js already, it's possible to use any of its examples (from a block or the gallery) by using a useEffect hook.
Basically, you can create a div in the DOM using react. You can then use the drawing methods of the d3-selection module like append or axisBottom to target this div, and add the content of the chart.
Let's apply this to draw axes:
You can use all the d3 knowlege you have in a useEffect hook to build the graph in a react context.
This works but comes with some caveats. To put it in a nutshell you now have 2 tools trying to control the DOM: react and d3. That's hard to maintain for large applications.
→ 3️⃣ D3 for maths, React for rendering
This gallery suggests using d3.js only for the math utils it provides. And to add entries to the DOM using react, like for any other UI element.
Let's say we want to build a scatterplot. The scaleLinear function of d3.js is used to build the scales. Now that we can easily know the position of a circle on the screen, we can just loop through all data items and render them as a circle svg element.
Use d3.js to compute the scales. Use React to render the circles.
Learn concepts, Get templates
This gallery is all about using the power of the d3 math utils and the react rendering engine.
The first goal is to teach the concepts. Many examples are provided for each chart type. Each one targets a specific theme like color, axis, responsiveness, hover effect, or tooltips.
The second goal is to provide templates for each viz type. Building a viz from scratch is time-consuming, so better tweak an existing example.
All graph examples come with an explanation and a code sandbox allowing you to play with the code.
I built this website with ❤️. I hope it will help you create stunning vizs in a minimum amount of time. Reach me on Twitter, contribute on github and subscribe to the newsletter to know when new chart types are published!
Contact
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
+About the gallery
React + D3.js = ❤️
A love story – So simplepowerful yet so complicated
A few years ago I created the d3 graph gallery, a website showcasing hundreds of simple charts made with d3.js. It worked well! Thousands of people use it daily to learn d3. 🎉
Since then, React became the most popular framework to build user interfaces. This rose a question: how to build a chart in react? That's a complicated question with many answers. Here are the 3 most common approaches.
3 ways to draw a chart in react
→ 1️⃣ Charting libraries
There is a myriad of charting libraries offering react components for every chart type. HighChart, ReCharts, React-viz, plot, visX and so many more. Those libraries are awesome: you'll get a working chart in minutes using them.
But simplicity comes with a cost: the time you saved in the first place will be lost when you'll try to reach a high level of customization.
If you want to build something unique, you need to draw shapes one by one.
→ 2️⃣ D3 for rendering in a useEffect hook
If you're familiar with d3.js already, it's possible to use any of its examples (from a block or the gallery) by using a useEffect hook.
Basically, you can create a div in the DOM using react. You can then use the drawing methods of the d3-selection module like append or axisBottom to target this div, and add the content of the chart.
Let's apply this to draw axes:
You can use all the d3 knowlege you have in a useEffect hook to build the graph in a react context.
This works but comes with some caveats. To put it in a nutshell you now have 2 tools trying to control the DOM: react and d3. That's hard to maintain for large applications.
→ 3️⃣ D3 for maths, React for rendering
This gallery suggests using d3.js only for the math utils it provides. And to add entries to the DOM using react, like for any other UI element.
Let's say we want to build a scatterplot. The scaleLinear function of d3.js is used to build the scales. Now that we can easily know the position of a circle on the screen, we can just loop through all data items and render them as a circle svg element.
Use d3.js to compute the scales. Use React to render the circles.
Learn concepts, Get templates
This gallery is all about using the power of the d3 math utils and the react rendering engine.
The first goal is to teach the concepts. Many examples are provided for each chart type. Each one targets a specific theme like color, axis, responsiveness, hover effect, or tooltips.
The second goal is to provide templates for each viz type. Building a viz from scratch is time-consuming, so better tweak an existing example.
All graph examples come with an explanation and a code sandbox allowing you to play with the code.
I built this website with ❤️. I hope it will help you create stunning vizs in a minimum amount of time. Reach me on Twitter, contribute on github and subscribe to the newsletter to know when new chart types are published!
Contact
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/all.html b/all.html
index a9f1db6c..48347c35 100644
--- a/all.html
+++ b/all.html
@@ -1,4 +1,4 @@
-All graphs
All graphs
The react graph gallery displays hundreds of graphs made with React, often with the help of d3.js. This page provides an overview of all charts showcased in this gallery.
Note that all chart types are presented on the welcome page of the gallery. It is probably a more convenient way to browse this website if you know what you are looking for!
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
+All graphs
All graphs
The react graph gallery displays hundreds of graphs made with React, often with the help of d3.js. This page provides an overview of all charts showcased in this gallery.
Note that all chart types are presented on the welcome page of the gallery. It is probably a more convenient way to browse this website if you know what you are looking for!
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/animation.html b/animation.html
index 819a13ae..0baba707 100644
--- a/animation.html
+++ b/animation.html
@@ -1,4 +1,4 @@
-Animation
Animation
Animation is both the most challenging and the most exciting part of an interactive chart. Animation is like salt: use the right amount of it and your creation is a delight. Too much of it and it spoils the dish 🤌.
There are many ways to animate the transition between 2 chart states. Here I suggest to use react-spring in combination with react andd3.js.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
+Animation
Animation
Animation is both the most challenging and the most exciting part of an interactive chart. Animation is like salt: use the right amount of it and your creation is a delight. Too much of it and it spoils the dish 🤌.
There are many ways to animate the transition between 2 chart states. Here I suggest to use react-spring in combination with react andd3.js.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/arc-diagram.html b/arc-diagram.html
index 7c750de7..38156f2f 100644
--- a/arc-diagram.html
+++ b/arc-diagram.html
@@ -1,4 +1,4 @@
-How to build an Arc Diagram with React and D3.
Arc diagram
An arc diagram is a special kind of network graph. It is consituted by nodes that represent entities and by links that show relationships between entities. In arc diagrams, nodes are displayed along a single axis and links are represented with arcs.
This page is a step by step tutorial explaining how to build an Arc diagram component with React and D3.js. It comes with explanations and code sandboxes. It starts by simple concept like how to format the data and how to draw arcs in SVG, and then goes further with hover effect, tooltip and more.
Two layers of information are required to build an arc diagram: a list of nodes to build the circles and a list of links to build the arcs.
Many different data structures can be used to store such information. In this tutorial I suggest to start with the following:
export const data = {
+How to build an Arc Diagram with React and D3.
Arc diagram
An arc diagram is a special kind of network graph. It is consituted by nodes that represent entities and by links that show relationships between entities. In arc diagrams, nodes are displayed along a single axis and links are represented with arcs.
This page is a step by step tutorial explaining how to build an Arc diagram component with React and D3.js. It comes with explanations and code sandboxes. It starts by simple concept like how to format the data and how to draw arcs in SVG, and then goes further with hover effect, tooltip and more.
I'm in the process of writing a complete blog post on the topic. Subscribe to the project to know when it's ready.
Arc Diagram inspiration
If you're looking for inspiration to create your next Arc Diagram, note that dataviz-inspiration.com showcases many examples. Definitely the best place to get ... inspiration!
dataviz-inspiration.com showcases hundreds of stunning dataviz projects. Have a look to get some ideas on how to make your Arc Diagram looks good!
Once you've understood how to build a basic arc diagram with d3 and react, it opens an infinite world of customization. Here are a few examples highlighting what it is possible to do with arc diagrams.
Click on the overview below to get details and code.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/area-plot.html b/area-plot.html
index 240b746b..29b2d0db 100644
--- a/area-plot.html
+++ b/area-plot.html
@@ -1,4 +1,4 @@
-Area charts with React
Area charts
An area chart displays the evolution of one numeric variables. It is like a line chart, but with the area below the line being filled.
This section describes how to build area charts on the web with d3.js and react. It starts very basic and then explains how to add more complex features like brushing, adding hover effects and more.
The dataset required to build a line chart is usually an array where each item is an object providing the x and the y values of the data point.
Here is a minimal example:
const data = [
+Area charts with React
Area charts
An area chart displays the evolution of one numeric variables. It is like a line chart, but with the area below the line being filled.
This section describes how to build area charts on the web with d3.js and react. It starts very basic and then explains how to add more complex features like brushing, adding hover effects and more.
Both a y0 and a y1 arguments are used. They provide both the bottom and the top position of the shape for each x position.
The output areaPath can now be passed to a path resulting in the following area chart:
A very basic area chart made using react and the area() function of d3.js
Area chart inspiration
If you're looking for inspiration to create your next Area chart, note that dataviz-inspiration.com showcases many examples. Definitely the best place to get ... inspiration!
dataviz-inspiration.com showcases hundreds of stunning dataviz projects. Have a look to get some ideas on how to make your Area chart looks good!
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/articles.html b/articles.html
index 1b38daca..df948f56 100644
--- a/articles.html
+++ b/articles.html
@@ -1,7 +1,7 @@
-How to make react and d3.js work together
Dataviz Insights with React and D3.js
While our gallery showcases a myriad of graph examples, this space is dedicated to delving into the intricacies of data visualization using React and D3.js.
From unraveling the complexities of creating stacked bar plots with negative values to envisioning futuristic visualizations, our articles aim to enlighten, inspire, and guide you through the advanced realms of dataviz.
Whether you're a seasoned developer or just starting out, these articles offer a wealth of knowledge to elevate your visualization game.
Let's embark on this enlightening journey together! 🔥
Viz components often take a width and a height properties as input. This blogposts explains how to build a wrapper around it that computes the parent's div dimension and pass it as propsRead more
Interactivity is an important part of dataviz when working in the browser. Adding a hover effect can improve the user experience by highlighting a series on the chart. Here are a couple way to implement it, always keeping performances in mind.Read more
A stacked barchart displays the values of items split in group and subgroups. It's a quite common chart type, but dealing with negative values in the dataset brings some interesting dataviz discussions.Read more
6 minutes read
Dataviz
The next articles are currently in writing mode. ⬇️
They will be released soon and you can be updated through my newsletter:
+How to make react and d3.js work together
Dataviz Insights with React and D3.js
While our gallery showcases a myriad of graph examples, this space is dedicated to delving into the intricacies of data visualization using React and D3.js.
From unraveling the complexities of creating stacked bar plots with negative values to envisioning futuristic visualizations, our articles aim to enlighten, inspire, and guide you through the advanced realms of dataviz.
Whether you're a seasoned developer or just starting out, these articles offer a wealth of knowledge to elevate your visualization game.
Let's embark on this enlightening journey together! 🔥
Viz components often take a width and a height properties as input. This blogposts explains how to build a wrapper around it that computes the parent's div dimension and pass it as propsRead more
Interactivity is an important part of dataviz when working in the browser. Adding a hover effect can improve the user experience by highlighting a series on the chart. Here are a couple way to implement it, always keeping performances in mind.Read more
A stacked barchart displays the values of items split in group and subgroups. It's a quite common chart type, but dealing with negative values in the dataset brings some interesting dataviz discussions.Read more
6 minutes read
Dataviz
The next articles are currently in writing mode. ⬇️
They will be released soon and you can be updated through my newsletter:
Using react and d3.js: The 2 strategies
React modifies the DOM. So does d3.js. It makes it notoriously hard to have them work together. This blog post describes the 2 main strategies to use d3.js in a react app, with their pros and cons.
4 minutes read
Fundamental
Axes: build them with react (and a bit of d3)
Most of the viz types need some axes to be insightful. This post explains how to build them from a d3 scale, using the tick() method of d3 to create re-usable react components.
8 minutes read
Fundamental
Axis
Graph to graph interaction
Let's say you have a choropleth map on a side, a timeseries on the other. How can you add cross-viz interactions, like hovering a country to highlight its trend on the timeseries?
10 minutes read
Interaction
Advanced
Spring animations with react spring
It's often necessary to transition between 2 ys of a graph. React-spring is here to help, allowing to use spring animations easily.
5 minutes read
Fundamental
Animation
Dataset transition
Adding a smooth transition between dataset often adds a nice touch to your viz component. Let's see how to implement it with react-spring.
5 minutes read
Animation
Shape morphism: animate the transition between 2 distincts charts
How can we build a smooth transition between a pie chart and a barplot? The flubber js library allows to interpolate shapes and react-spring can animate this interpolation.
5 minutes read
Advanced
Animation
Improve chart performance with Canvas
Rendering a chart using svg is limited in term of performace. The DOM gets to crowded and updating it ends up being slow. Using canvas is the best workaround but you need to be able to draw your svg path using it!
5 minutes read
Fundamental
Canvas
Fix the blurry canvas on Retina screens
When using canvas for your viz, the result will be blurry on retina screens if you don't scale the canvas properly. Here is why and how to implement it.
5 minutes read
Canvas
Advanced
What is a color
There are so many ways to define a color when talking with a computer. Let's take a tour and see what's the most appropriate for a dataviz point of view.
3 minutes read
Fundamental
Axis
Buiding a futuristic viz
What makes a viz look from the future. And how to implement it with d3.js and reac.
3 minutes read
Fundamental
Axis
Contact
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/barplot.html b/barplot.html
index a949e55a..c3c59c2f 100644
--- a/barplot.html
+++ b/barplot.html
@@ -1,4 +1,4 @@
-Barplot with React
Barplot
A barplot displays a numeric value for several groups of a dataset using rectangles. This page is a step-by-step guide on how to build your own barplot for the web, using React and D3.js.
It starts with very basic concepts like data structure, scales and svg rectangle rendering. It then shows how to add interactivity to the chart with hover effects. Last but not least it explains how to build variations like the stacked barplot.
The dataset required to build a barplot is usually an array where each item is an object providing the name and the value of the group.
Here is a minimal example
const data = [
+Barplot with React
Barplot
A barplot displays a numeric value for several groups of a dataset using rectangles. This page is a step-by-step guide on how to build your own barplot for the web, using React and D3.js.
It starts with very basic concepts like data structure, scales and svg rectangle rendering. It then shows how to add interactivity to the chart with hover effects. Last but not least it explains how to build variations like the stacked barplot.
I'm in the process of writing a complete blog post on the topic. Subscribe to the project to know when it's ready.
Transition
When the dataset updates, it adds a nice touch to smoothly animate the transition. In the example below, changing the dataset will update the bar sizes and their positions on the Y axis to keep the ranking accurate.
Animation is a complicated topic in dataviz. We have to deal with updates (an element changes its features), enter (a new element appears) and exit (an element is not present anymore) patterns.
I suggest to rely on the react-spring library to help here. Please check this dedicated blogpost to get explanations about the code of this example.
Most basic barplot built with d3.js for scales, and react for rendering
Stacking
A stacked barplot is a variation of a barplot where an additional level of grouping is represented. Each bar represent the value of a group, for instance how much each my friend spent in the last month. Each bar is then subdivided, each part representing the value of a subgroup, for instance the category of expense.
D3 comes with a very handy stack() function. The 2 tutorials below explain how this function works, and how to use it to render a clean stacked barplot.
Vertical barplot
The vertical option is less common since it makes is much harder to read the labels. But if you really need it, it is just a matter of swaping the X and Y axes of the previous example.
This example will be publish soon, please subscribe below if you want to be notified.
Hover effect
This example will be publish soon, please subscribe to the newsletter if you want to be notified.
Variations
Let's go beyond the basic barcharts. Click on the overview images below to get details and code.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/boxplot.html b/boxplot.html
index 37db2f43..2d271709 100644
--- a/boxplot.html
+++ b/boxplot.html
@@ -1,4 +1,4 @@
-Boxplot with React
Boxplot with React and d3.js
A boxplot summarizes the distribution of a numeric variable, often for several groups of a dataset. This page is a step-by-step guide on how to build a reusable boxplot component for the web using React and D3.js.
It starts by describing how to format the dataset and how to initialize the boxplot component. It then explains how to create a Box component that displays a single box. Finally, it shows how to render the boxplot and suggests a few variations. 🙇♂️.
The dataset used to build a boxplot is usually an array of objects. For each object, a name property provides the group name, and a value property provides the numeric value. It looks like this:
const data = [
+Boxplot with React
Boxplot with React and d3.js
A boxplot summarizes the distribution of a numeric variable, often for several groups of a dataset. This page is a step-by-step guide on how to build a reusable boxplot component for the web using React and D3.js.
It starts by describing how to format the dataset and how to initialize the boxplot component. It then explains how to create a Box component that displays a single box. Finally, it shows how to render the boxplot and suggests a few variations. 🙇♂️.
The dataset used to build a boxplot is usually an array of objects. For each object, a name property provides the group name, and a value property provides the numeric value. It looks like this:
I'm in the process of writing a complete blog post on the topic. Subscribe to the project to know when it's ready.
Boxplot inspiration
If you're looking for inspiration to create your next Boxplot, note that dataviz-inspiration.com showcases many examples. Definitely the best place to get ... inspiration!
dataviz-inspiration.com showcases hundreds of stunning dataviz projects. Have a look to get some ideas on how to make your Boxplot looks good!
Even if powerful to summarize the distribution of a numeric variable, the boxplot has flaws.
It indeed hides the underlying distribution. For instance, a low sample size or a bi-modal distribution is impossible to detect by reading the boxes only.
Jittering is a good workaround. Add all individual data points with low size, low opacity, and some random shift to the right or the left (jitter). The underlying distribution becomes instantly available.
Note that another good alternative is the violin plot, especially for a high sample size.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/bubble-map.html b/bubble-map.html
index d7a111ba..397a79d7 100644
--- a/bubble-map.html
+++ b/bubble-map.html
@@ -1,4 +1,4 @@
-How to build a bubble map component with React and D3.
Bubble Map
A bubble map uses circles of different size to represent a numeric value on a territory. It displays one bubble per geographic coordinate, or one bubble per region.
This page explains how to build bubble maps for the web using d3.js and react. Several tools can be used to display the background map as shown in the dedicated section. Circles are then computed with d3 and render using SVG or canvas elements with react.
Examples start easy and add layers of complexity progressively. You will always find explanations and code sandboxes for each step.
Two pieces of information are required to build a bubble map:
→ Geographic information
The first thing you need is the 2d coordinates of the boundaries of the regions you want to represent. If you are trying to build a world map, you need to know where the country boundaries are located 🤷♀️.
Several formats exist to store such a piece of information. When working with d3.js, the expected format is geoJSON. A geoJSON file looks pretty much like this:
{
+How to build a bubble map component with React and D3.
Bubble Map
A bubble map uses circles of different size to represent a numeric value on a territory. It displays one bubble per geographic coordinate, or one bubble per region.
This page explains how to build bubble maps for the web using d3.js and react. Several tools can be used to display the background map as shown in the dedicated section. Circles are then computed with d3 and render using SVG or canvas elements with react.
Examples start easy and add layers of complexity progressively. You will always find explanations and code sandboxes for each step.
Two pieces of information are required to build a bubble map:
→ Geographic information
The first thing you need is the 2d coordinates of the boundaries of the regions you want to represent. If you are trying to build a world map, you need to know where the country boundaries are located 🤷♀️.
Several formats exist to store such a piece of information. When working with d3.js, the expected format is geoJSON. A geoJSON file looks pretty much like this:
A bubble chart component that smoothly animates changes between datasets.
Animation in dataviz using React is a big topic. It's impossible to go in-depth here! I will publish a dedicated blog post on the topic soon. Please subscribe to the newsletter if you want to be notified.
Bubble inspiration
If you're looking for inspiration to create your next Bubble, note that dataviz-inspiration.com showcases many examples. Definitely the best place to get ... inspiration!
dataviz-inspiration.com showcases hundreds of stunning dataviz projects. Have a look to get some ideas on how to make your Bubble looks good!
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/bubble-plot.html b/bubble-plot.html
index 348449f9..e36779c0 100644
--- a/bubble-plot.html
+++ b/bubble-plot.html
@@ -1,4 +1,4 @@
-Bubble plot with React
Bubble plot
A bubble plot is an extension of a scatterplot, where each circle has its size proportional to a numeric value. This page is a step-by-step guide on how to build your own bubble chart for the web, using React and D3.js.
This page focuses on the implementation of features that are different from the scatterplot that has its dedicated section. It describes how the dataset differs, how the circle size can be mapped to a numeric value, and how to explicit it using a legend. Last but not least it explains how to add interactivity: hover effect, tooltip, and dataset transition. 🙇♂️.
The dataset used to build a bubble plot is usually an array of objects where each object is a data point. For each object, at least 3 properties are required.
Two properties are used for the X and Y axis, the third one is used for the circle size.
Note that you can add more properties to the object. For instance, a name can be displayed in the tooltip, and a group can be used to color the bubbles.
const data = [
+Bubble plot with React
Bubble plot
A bubble plot is an extension of a scatterplot, where each circle has its size proportional to a numeric value. This page is a step-by-step guide on how to build your own bubble chart for the web, using React and D3.js.
This page focuses on the implementation of features that are different from the scatterplot that has its dedicated section. It describes how the dataset differs, how the circle size can be mapped to a numeric value, and how to explicit it using a legend. Last but not least it explains how to add interactivity: hover effect, tooltip, and dataset transition. 🙇♂️.
The dataset used to build a bubble plot is usually an array of objects where each object is a data point. For each object, at least 3 properties are required.
Two properties are used for the X and Y axis, the third one is used for the circle size.
Note that you can add more properties to the object. For instance, a name can be displayed in the tooltip, and a group can be used to color the bubbles.
A bubble chart component that smoothly animates changes between datasets.
Animation in dataviz using React is a big topic. It's impossible to go in-depth here! I will publish a dedicated blog post on the topic soon. Please subscribe to the newsletter if you want to be notified.
Real-life application
Let's apply the concepts learned above to a real-life example.
I like this scatterplot originally published on the data wrapper blog. It shows a strong correlation between vulnerability to climate change and CO2 emissions.
The chart has several features that are interesting to reproduce from a technical point of view:
Custom annotation: only a fraction of the country names are written
Hover effect: the hovered country is highlighted with a black stroke. After a short delay, countries of other groups are dimmed. Note that the effect is triggered once the mouse approaches the marker, no need to be perfectly on top.
Tooltip: highly customized and linked to the mouse position
The countries with the highest vulnerability to climate change have the lowest CO2 emissions
All countries sorted by their vulnerability and readiness to climate change. The size shows the CO2 emission per person in that country.
Reproduction of a chart originally published by Data Wrapper using react and d3.js.
Variations
Once you've understood how to build a basic bubble chart with d3 and react, it opens an infinite world of customization. Here are a few examples using the same concepts.
Click on the overview below to get details and code.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/build-axis-with-react.html b/build-axis-with-react.html
index fe03b90f..cdb13779 100644
--- a/build-axis-with-react.html
+++ b/build-axis-with-react.html
@@ -1,4 +1,4 @@
-Building graph axes with React (and d3.js)
Building graph axes with React (and d3.js)
This post explains how to build axes from d3 scales for a chart. It relies on the tick() method to compute the tick positions and use react for the rendering. The code of the BottomAxis and LeftAxiscomponents is provided, together with some reproducible examples.
This minimal example uses scaleLinear() to compute the scales, ticks() to compute tick positions and react to render the axes.
Bottom Axis
The code snippet below builds a AxisBottom component. It is very much inspired from this blogpost by Amelia Wattenberger. I've just changed a few things, notably passing a scale as input instead of a range and a domain.
The logic mainly relies on the ticks() method of a d3 scale. It takes a target number of ticks as input, find the most appropriate way to build smart ticks based on this target, and returns an array with all the tick positions.
What follows is just some svg drawings based on those tick positions.
This post explains how to build axes from d3 scales for a chart. It relies on the tick() method to compute the tick positions and use react for the rendering. The code of the BottomAxis and LeftAxiscomponents is provided, together with some reproducible examples.
This minimal example uses scaleLinear() to compute the scales, ticks() to compute tick positions and react to render the axes.
Bottom Axis
The code snippet below builds a AxisBottom component. It is very much inspired from this blogpost by Amelia Wattenberger. I've just changed a few things, notably passing a scale as input instead of a range and a domain.
The logic mainly relies on the ticks() method of a d3 scale. It takes a target number of ticks as input, find the most appropriate way to build smart ticks based on this target, and returns an array with all the tick positions.
What follows is just some svg drawings based on those tick positions.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/cartogram.html b/cartogram.html
index ed42dbc1..36de464d 100644
--- a/cartogram.html
+++ b/cartogram.html
@@ -1,4 +1,4 @@
-How to build a cartogram with React and D3.
Cartogram
A cartogram is a map in which the geometry of regions is distorted in order to convey the information of an alternate variable.
It is possible to build a Cartogram react component thanks to a js library called topogram. This page provides step-by-step explanations on how to use the library based on a geoJson file with the help of d3.js for manipulating such a data source.
Probably uses the same as for a choropleth map or for a bubble map.
The Topogram library
As far as I can tell the best way to create a cartogram in JS is the topogram library.
However it looks like there is no easy way to install it using npm. The easiest way is probably to clone the repo and create the build, or to copy the content of the cartogram.js file.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
+How to build a cartogram with React and D3.
Cartogram
A cartogram is a map in which the geometry of regions is distorted in order to convey the information of an alternate variable.
It is possible to build a Cartogram react component thanks to a js library called topogram. This page provides step-by-step explanations on how to use the library based on a geoJson file with the help of d3.js for manipulating such a data source.
Probably uses the same as for a choropleth map or for a bubble map.
The Topogram library
As far as I can tell the best way to create a cartogram in JS is the topogram library.
However it looks like there is no easy way to install it using npm. The easiest way is probably to clone the repo and create the build, or to copy the content of the cartogram.js file.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/chord-diagram.html b/chord-diagram.html
index 026aec7a..ff3c17cc 100644
--- a/chord-diagram.html
+++ b/chord-diagram.html
@@ -1,4 +1,4 @@
-How to build a chord diagram with React and D3.
Chord diagram
A chord diagram represents flows between several entities called nodes. Each node is represented by a fragment on the outer part of the circular layout. Then, arcs are drawn between each entities. The size of the arc is proportional to the importance of the flow..
Building a chord diagram with React and D3.js relies on the d3-chord module that computes the node and arc positions for us. React can then be used to draw everything in SVG. This page is a step by step tutorial with code sandboxes. It will teach you how to build a ChordDiagram component.
The dataset required to build a chord diagram is a square matrix. It has a dimension of n x n where n is the number of nodes.
In javascript, this matrix is represented as an array of n array. Each individual array also has n items. The matrix of flow has a direction: the second item of the third row gives the flow from element 2 to element 3.
Usually an additional array is provided, giving the name of each node.
Here is a minimal example of the data structure:
// matrix of flow
+How to build a chord diagram with React and D3.
Chord diagram
A chord diagram represents flows between several entities called nodes. Each node is represented by a fragment on the outer part of the circular layout. Then, arcs are drawn between each entities. The size of the arc is proportional to the importance of the flow..
Building a chord diagram with React and D3.js relies on the d3-chord module that computes the node and arc positions for us. React can then be used to draw everything in SVG. This page is a step by step tutorial with code sandboxes. It will teach you how to build a ChordDiagram component.
The dataset required to build a chord diagram is a square matrix. It has a dimension of n x n where n is the number of nodes.
In javascript, this matrix is represented as an array of n array. Each individual array also has n items. The matrix of flow has a direction: the second item of the third row gives the flow from element 2 to element 3.
Usually an additional array is provided, giving the name of each node.
I'm in the process of writing a complete blog post on the topic. Subscribe to the project to know when it's ready.
Chord Diagram inspiration
If you're looking for inspiration to create your next Chord Diagram, note that dataviz-inspiration.com showcases many examples. Definitely the best place to get ... inspiration!
dataviz-inspiration.com showcases hundreds of stunning dataviz projects. Have a look to get some ideas on how to make your Chord Diagram looks good!
I suggest 2 improvements to get a descent chord diagram:
→ Colors
Pretty straightforward to implement. You just need to create an array of colors. Then, for each item to draw the index is always available. It can be used to retrieve the color in the color array.
→ Labels
A new prop needs to be passed to the component with a list of names for the nodes. I suggest to position labels as for a donut chart but many other possibilities are available.
Connections between nodes are drawn thanks to the ribbon() function of d3.js.
ToDoAdd section on hover effect
ToDoTalk about chordDirected() and chordTranspose()
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/choropleth-map.html b/choropleth-map.html
index 0c4811aa..0b7770f6 100644
--- a/choropleth-map.html
+++ b/choropleth-map.html
@@ -1,4 +1,4 @@
-Building Choropleth Maps with React and D3.js: A Step-by-Step Tutorial
Choropleth Map
A choropleth map displays divided geographical areas or regions that are coloured in relation to a numeric variable. It enables the study of how a variable evolves across a geographical area.
Once you understood how to draw a map background from a geoJson file, it is just a matter of coloring each region with the appropriate color. On top of this, it is advised to add a color legend and some interactivity (hover effect and tooltip).
This webpage is a tutorial coming with explanation and code sandboxes. It explains how to build interactive choropleth map with React and D3.js.
Two pieces of information are required to build a choropleth map:
→ Geographic information
The first thing you need to build a choropleth map is the 2d coordinates of the boundaries of the regions you want to represent. If you are trying to build a world map, you need to know where the country boundaries are located 🤷♀️.
Several formats exist to store such a piece of information. When working with d3.js, the expected format is geoJSON. A geoJSON file looks pretty much like this:
{
+Building Choropleth Maps with React and D3.js: A Step-by-Step Tutorial
Choropleth Map
A choropleth map displays divided geographical areas or regions that are coloured in relation to a numeric variable. It enables the study of how a variable evolves across a geographical area.
Once you understood how to draw a map background from a geoJson file, it is just a matter of coloring each region with the appropriate color. On top of this, it is advised to add a color legend and some interactivity (hover effect and tooltip).
This webpage is a tutorial coming with explanation and code sandboxes. It explains how to build interactive choropleth map with React and D3.js.
Two pieces of information are required to build a choropleth map:
→ Geographic information
The first thing you need to build a choropleth map is the 2d coordinates of the boundaries of the regions you want to represent. If you are trying to build a world map, you need to know where the country boundaries are located 🤷♀️.
Several formats exist to store such a piece of information. When working with d3.js, the expected format is geoJSON. A geoJSON file looks pretty much like this:
Then you probably want to add some ticks on top of the color graduation to make it insightful.
Fortunately, the d3 linearScale comes with a handy tick() function. Basically, calling xScale.ticks(4) will create an array with approximately 4 items, each providing everything you need to draw a smartly located tick.
Color Legend is a big topic. There is much more to say about it and I'll post a complete blog post on the topic soon. Subscribe to the gallery if interested!
ToDoHover effect section
ToDoTalk more about color scale. Hover effect linked with color scale
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/circular-barplot.html b/circular-barplot.html
index 880a028d..463c45c3 100644
--- a/circular-barplot.html
+++ b/circular-barplot.html
@@ -1,4 +1,4 @@
-Circular Barplot with React
Circular Barplot
A circular barplot is a variation of a barplot where bars are displayed around a circle using polar coordinates. It is a less accurate representation of the data, but provides a strong eye-catching effect.
This page describes how to deal with radial coordinates with d3.js and react to build a circular barplot. It's a step by step tutorial with several interactive sandboxes.
The dataset required to build a circular barplot is usually an array where each item is an object providing the name and the value of the group.
Here is a minimal example
const data = [
+Circular Barplot with React
Circular Barplot
A circular barplot is a variation of a barplot where bars are displayed around a circle using polar coordinates. It is a less accurate representation of the data, but provides a strong eye-catching effect.
This page describes how to deal with radial coordinates with d3.js and react to build a circular barplot. It's a step by step tutorial with several interactive sandboxes.
Not much to add. Just include the paths in a svg element. Remember that 0,0 is the center of the chart instead of being the top-left corner. So we need to apply a translate at some point.
Most basic circular barplot built with d3.js and react, using radial coordinates and path instead of rect.
That's a good start but it looks pretty much like a snail so far. Let's make it a real chart with labels and values.
Labels
It is necessary to add a text element to show the name of each bar.
We need those labels to be readable (like not written upside down). So a bit of logic is necessary to determine wether or not a label must be flipped, and how to position it properly.
To do so it is necessary to switch from radians (use for the xScale) to degrees (used for the transform property).
Please check the code below for full explanation.
Add some labels to each bar of the circular barchart to make it insightful
Circular Barplot inspiration
If you're looking for inspiration to create your next Circular Barplot, note that dataviz-inspiration.com showcases many examples. Definitely the best place to get ... inspiration!
dataviz-inspiration.com showcases hundreds of stunning dataviz projects. Have a look to get some ideas on how to make your Circular Barplot looks good!
Stacking is a process where a chart is broken up across more than one categoric variables which make up the whole.
d3 comes with some handy functions for stacking. The process is extensively described in this stacked barplot tutorial. There is nothing really different to make it circular and here is a working sandbox to discover the code.
Add some labels to each bar of the circular barchart to make it insightful
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
+})
→ Rendering
Not much to add. Just include the paths in a svg element. Remember that 0,0 is the center of the chart instead of being the top-left corner. So we need to apply a translate at some point.
Most basic circular barplot built with d3.js and react, using radial coordinates and path instead of rect.
That's a good start but it looks pretty much like a snail so far. Let's make it a real chart with labels and values.
Labels
It is necessary to add a text element to show the name of each bar.
We need those labels to be readable (like not written upside down). So a bit of logic is necessary to determine wether or not a label must be flipped, and how to position it properly.
To do so it is necessary to switch from radians (use for the xScale) to degrees (used for the transform property).
Please check the code below for full explanation.
Add some labels to each bar of the circular barchart to make it insightful
Circular Barplot inspiration
If you're looking for inspiration to create your next Circular Barplot, note that dataviz-inspiration.com showcases many examples. Definitely the best place to get ... inspiration!
dataviz-inspiration.com showcases hundreds of stunning dataviz projects. Have a look to get some ideas on how to make your Circular Barplot looks good!
Stacking is a process where a chart is broken up across more than one categoric variables which make up the whole.
d3 comes with some handy functions for stacking. The process is extensively described in this stacked barplot tutorial. There is nothing really different to make it circular and here is a working sandbox to discover the code.
Add some labels to each bar of the circular barchart to make it insightful
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/circular-packing.html b/circular-packing.html
index 4789ddb2..fad6c49d 100644
--- a/circular-packing.html
+++ b/circular-packing.html
@@ -1,4 +1,4 @@
-Circular Packing chart with React
Circular Packing
A circular packing chart displays a hierarchical dataset as a set of nested circles, each circle representing a node of the data structure. Size is usually proportional to a numeric variable.
This page is a tutorial teaching how to create a circle pack chart with d3.js and React. It starts with a very basic version, adds some levels of nesting and finishes with usual customization like animating the transition between datasets.
The dataset describes a hierarchy using a recursive structure. It is similar to a dendrogram or to a treemap.
Each item in this structure is called a node, the lowest nodes of the hierarchy being called leaves. The dataset is an object that has at least 3 properties: name, value and children. Children is an array of nodes that have this structure too.
Here is a minimal example of the data structure:
const data = {
+Circular Packing chart with React
Circular Packing
A circular packing chart displays a hierarchical dataset as a set of nested circles, each circle representing a node of the data structure. Size is usually proportional to a numeric variable.
This page is a tutorial teaching how to create a circle pack chart with d3.js and React. It starts with a very basic version, adds some levels of nesting and finishes with usual customization like animating the transition between datasets.
The dataset describes a hierarchy using a recursive structure. It is similar to a dendrogram or to a treemap.
Each item in this structure is called a node, the lowest nodes of the hierarchy being called leaves. The dataset is an object that has at least 3 properties: name, value and children. Children is an array of nodes that have this structure too.
This component uses the useSpring hook of react spring to interpolate the cx, cy and r properties. Those values are passed to a special svg element (animated.circle) that does the animation.
Animating the transition between 2 similar dataset with react and d3.js (for rendering) and react spring (for animation).
Animation in dataviz using React is a big topic. It's impossible to go in depth here! I will publish a dedicated blog post on the topic soon. Please subscribe to the newsletter if you want to be notified.
ToDoBetter dataset transition where circle keep position
Variations
Once you've understood how to build a basic circular packing with d3 and react, it opens an infinite world of customization. Here are a few examples using the same concepts.
Click on the overview below to get details and code.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/connected-scatter-plot.html b/connected-scatter-plot.html
index 7a9a2e2a..32b87616 100644
--- a/connected-scatter-plot.html
+++ b/connected-scatter-plot.html
@@ -1,4 +1,4 @@
-Connected scatterplot with React and D3.js
Connected Scatterplot
A connected scatterplot displays the evolution of a numeric variable. Data points are represented by a dot and connected with straight line segments. A variation of the connected scatterplot allows to study the evolution of 2 numeric variables together.
This page explains how to build a connected scatterplot using react andd3.js. It is highly connected with the line chart section of the gallery but provides further information concerning connected scatterplot specific features.
The dataset required to build a connected scatterplot is the same as for a line chart. It is usually an array where each item is an object providing the x and the y values of the data point.
Here is a minimal example:
const data = [
+Connected scatterplot with React and D3.js
Connected Scatterplot
A connected scatterplot displays the evolution of a numeric variable. Data points are represented by a dot and connected with straight line segments. A variation of the connected scatterplot allows to study the evolution of 2 numeric variables together.
This page explains how to build a connected scatterplot using react andd3.js. It is highly connected with the line chart section of the gallery but provides further information concerning connected scatterplot specific features.
The dataset required to build a connected scatterplot is the same as for a line chart. It is usually an array where each item is an object providing the x and the y values of the data point.
I'm in the process of writing a complete blog post on the topic. Subscribe to the project to know when it's ready.
ToDoadd links to line chart examples
ToDoreproduce the connected scatter from the state of JS survey
Connected Scatter inspiration
If you're looking for inspiration to create your next Connected Scatter, note that dataviz-inspiration.com showcases many examples. Definitely the best place to get ... inspiration!
dataviz-inspiration.com showcases hundreds of stunning dataviz projects. Have a look to get some ideas on how to make your Connected Scatter looks good!
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/connection-map.html b/connection-map.html
index f4005ceb..5e69b2cd 100644
--- a/connection-map.html
+++ b/connection-map.html
@@ -1,4 +1,4 @@
-How to build a connection map component with React and D3.
Connection Map
A connection map is a map where links between geographical positions are represented using lines or arcs. Most of the time, great circles are used.
This page explains how to build connection maps for the web using d3.js and react. Several tools can be used to display the background map as shown in the dedicated section. The path used to show the connection can then be computed thanks to the geoPath() function of d3.
Two pieces of information are required to build a connection map:
→ Geographic information
The first thing you need is the 2d coordinates of the boundaries of the regions you want to represent. If you are trying to build a world map, you need to know where the country boundaries are located 🤷♀️.
Several formats exist to store such a piece of information. When working with d3.js, the expected format is geoJSON. A geoJSON file looks pretty much like this:
{
+How to build a connection map component with React and D3.
Connection Map
A connection map is a map where links between geographical positions are represented using lines or arcs. Most of the time, great circles are used.
This page explains how to build connection maps for the web using d3.js and react. Several tools can be used to display the background map as shown in the dedicated section. The path used to show the connection can then be computed thanks to the geoPath() function of d3.
Two pieces of information are required to build a connection map:
→ Geographic information
The first thing you need is the 2d coordinates of the boundaries of the regions you want to represent. If you are trying to build a world map, you need to know where the country boundaries are located 🤷♀️.
Several formats exist to store such a piece of information. When working with d3.js, the expected format is geoJSON. A geoJSON file looks pretty much like this:
I'm in the process of writing a complete blog post on the topic. Subscribe to the project to know when it's ready.
Connection inspiration
If you're looking for inspiration to create your next Connection, note that dataviz-inspiration.com showcases many examples. Definitely the best place to get ... inspiration!
dataviz-inspiration.com showcases hundreds of stunning dataviz projects. Have a look to get some ideas on how to make your Connection looks good!
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/correlogram.html b/correlogram.html
index 3bfcdbd6..2a93947f 100644
--- a/correlogram.html
+++ b/correlogram.html
@@ -1,4 +1,4 @@
-Correlogram with React
Correlogram
In this blog post, we will be exploring how to build a correlogram with React and D3.js. A correlogram is a graphical representation of the correlation matrix for a given dataset. It is a useful tool for visualizing the relationships between different variables in a dataset, and can help identify potential correlations that may not be immediately obvious.
Building a correlogram with React and D3.js allows us to create a highly interactive and customizable visualization. We will be able to use React's powerful component-based approach to build our visualization, while leveraging the flexibility and power of D3.js to create a dynamic and engaging visual representation of our data.
A correlogram uses histograms to show the distribution of each numeric variable on the diagonal of the matrix. It uses scatter plots to show the relationship of each pair of variable on every other cells.
As a result, it is required to understand how to build a histogram and a scatter plot component using React and d3.js! In this post, we will just show how to leverage those reusable components to build a correlogram.
The Data
The dataset provides several numeric values for a set of data points. It can also add some categorical variables that can be added to customize the marker colors.
The suggested data structure is an array of object, where each object is a data point. It can have as many numeric properties as needed.
Here is a minimal example of the data structure:
const data = [
+Correlogram with React
Correlogram
In this blog post, we will be exploring how to build a correlogram with React and D3.js. A correlogram is a graphical representation of the correlation matrix for a given dataset. It is a useful tool for visualizing the relationships between different variables in a dataset, and can help identify potential correlations that may not be immediately obvious.
Building a correlogram with React and D3.js allows us to create a highly interactive and customizable visualization. We will be able to use React's powerful component-based approach to build our visualization, while leveraging the flexibility and power of D3.js to create a dynamic and engaging visual representation of our data.
A correlogram uses histograms to show the distribution of each numeric variable on the diagonal of the matrix. It uses scatter plots to show the relationship of each pair of variable on every other cells.
As a result, it is required to understand how to build a histogram and a scatter plot component using React and d3.js! In this post, we will just show how to leverage those reusable components to build a correlogram.
The Data
The dataset provides several numeric values for a set of data points. It can also add some categorical variables that can be added to customize the marker colors.
The suggested data structure is an array of object, where each object is a data point. It can have as many numeric properties as needed.
And voilà, a first decent correlogram for your data analysis pipeline 😊. It's not perfect yet. You probably want to give more love to axes and labels, add hover effect and tooltips. But hopefully that's a good template to get started.
A correlogram built with react and d3.js. It shows the relationship between the 4 numeric variables of the famous iris dataset.
Note: You can compare this code with the pure d3 alternative. I find it much more readable.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/course/axis/axis-variations.html b/course/axis/axis-variations.html
index 904b9612..7f29c701 100644
--- a/course/axis/axis-variations.html
+++ b/course/axis/axis-variations.html
@@ -1 +1 @@
-Axis component variations
Axis component variations
In the previous lesson we made a reusable component for the bottom axis.
This lesson suggests many variation: left axis, adding grids, dealing with titles...
Wip
4 minutes read
Gallery
Show a gallery with the various axis styles available in the gallery.
I need a first series of button: linear / ordinal / bandwidth / time / any = type of scale
Then a second series: left / bottom
Then it shows all the example in the gallery using this setup with a set of images
When user clicks on an image, it opens the sandbox so user has the code ready to copy paste.
\ No newline at end of file
diff --git a/course/axis/axis-with-d3.html b/course/axis/axis-with-d3.html
index 8bcb99ca..1611190f 100644
--- a/course/axis/axis-with-d3.html
+++ b/course/axis/axis-with-d3.html
@@ -1,4 +1,4 @@
-Alternative: use d3 helper
Alternative: use d3 helper
The previous lessons taught us how to build React axis components that can be used in any of your charts.
However, there's an alternative worth mentioning: D3 can also draw axes. Let's explore this option and see which one works best for you.
Free
4 minutes read
The d3 axis module
D3 has a whole module dedicated to drawing axes! It is called ... d3-axis 🙃
It performs essentially the same function as the AxisBottom andAxisLeft components we created in the previous lesson: taking a scale and rendering lines and ticks based on it on the screen.
A few axes made with d3.js and its d3-axis module.
😳 Did you say rendering?
We have a challenge: in a React environment where rendering is managed by React, how can we delegate part of the rendering process to D3?
This is possible using a react useEffect()!
Here is an example:
This axis is rendered using d3. The d3 necessary functions are called from a useEffect
How to Use D3 to Render Axes in a React App
Let's clarify the code from the example above.
⛳️ Using a ref
A ref acts as a pointer to a specific part of the DOM. We need to initialize a ref and assign it to the SVG element we want to manipulate with JavaScript later on.
To create the ref, use the following code:
const axesRef = useRef(null);
Next, assign the ref to the <g> element where D3 will render the axis:
<g
+Alternative: use d3 helper
Alternative: use d3 helper
The previous lessons taught us how to build React axis components that can be used in any of your charts.
However, there's an alternative worth mentioning: D3 can also draw axes. Let's explore this option and see which one works best for you.
Free
4 minutes read
The d3 axis module
D3 has a whole module dedicated to drawing axes! It is called ... d3-axis 🙃
It performs essentially the same function as the AxisBottom andAxisLeft components we created in the previous lesson: taking a scale and rendering lines and ticks based on it on the screen.
A few axes made with d3.js and its d3-axis module.
😳 Did you say rendering?
We have a challenge: in a React environment where rendering is managed by React, how can we delegate part of the rendering process to D3?
This is possible using a react useEffect()!
Here is an example:
This axis is rendered using d3. The d3 necessary functions are called from a useEffect
How to Use D3 to Render Axes in a React App
Let's clarify the code from the example above.
⛳️ Using a ref
A ref acts as a pointer to a specific part of the DOM. We need to initialize a ref and assign it to the SVG element we want to manipulate with JavaScript later on.
To create the ref, use the following code:
const axesRef = useRef(null);
Next, assign the ref to the <g> element where D3 will render the axis:
Both options have their merits, each with its own set of pros and cons. Personally, I prefer the React component approach for creating axes. Here’s why:
🎨 Styling: You can customize axis elements individually, allowing for precise styling.
🔄 Lifecycle: When using D3 to create axes, they operate outside of React's lifecycle events, making it challenging to ensure they update at the right times.
♻️ Reusability: React emphasizes the creation of reusable components. Building axes with D3 each time goes against this philosophy, which simplifies development.
🛠️ Maintainability / Readability: Other developers in your organization will likely find it easier to understand the SVG markup of the AxisBottom component compared to the D3.js functions from the d3-axis module.
\ No newline at end of file
+ .call(xAxisGenerator);
Done! 🎉
So, React or D3.js for Axes?
Both options have their merits, each with its own set of pros and cons. Personally, I prefer the React component approach for creating axes. Here’s why:
🎨 Styling: You can customize axis elements individually, allowing for precise styling.
🔄 Lifecycle: When using D3 to create axes, they operate outside of React's lifecycle events, making it challenging to ensure they update at the right times.
♻️ Reusability: React emphasizes the creation of reusable components. Building axes with D3 each time goes against this philosophy, which simplifies development.
🛠️ Maintainability / Readability: Other developers in your organization will likely find it easier to understand the SVG markup of the AxisBottom component compared to the D3.js functions from the d3-axis module.
\ No newline at end of file
diff --git a/course/axis/bottom-axis.html b/course/axis/bottom-axis.html
index 0b1eaa26..5bf87e17 100644
--- a/course/axis/bottom-axis.html
+++ b/course/axis/bottom-axis.html
@@ -1,4 +1,4 @@
-Build a bottom axis
Build a bottom axis
In the previous lesson, we learned how to manage margins effectively in our chart. Now, let's explore how to create a AxisBottom react component that draws a bottom axis!
Free
8 minutes read
🔍 More about scaleLinear()
In the previous lessons we talked a lot about the scaleLinear() function of d3.js.
You should perfectly understand the code below. If not, go back to the scale module of this course!
const xScale = d3.scaleLinear()
+Build a bottom axis
Build a bottom axis
In the previous lesson, we learned how to manage margins effectively in our chart. Now, let's explore how to create a AxisBottom react component that draws a bottom axis!
Free
8 minutes read
🔍 More about scaleLinear()
In the previous lessons we talked a lot about the scaleLinear() function of d3.js.
You should perfectly understand the code below. If not, go back to the scale module of this course!
\ No newline at end of file
diff --git a/course/axis/introduction.html b/course/axis/introduction.html
index 4d24190b..12dae6aa 100644
--- a/course/axis/introduction.html
+++ b/course/axis/introduction.html
@@ -1 +1 @@
-Introduction
Introduction
In the previous module on scales, we learned how to position SVG shapes precisely, adding meaning to the growing graph we're constructing.
Now, it's time to provide context to these shape positions. For most chart types, this is achieved using axes. Axes can be complex elements, so let's explore how to create them effectively.
Free
2 minutes read
This module explains how to build axes like the one you can see at the bottom and on the left of this chart area.
Key Terminology
To work effectively with axes in data visualization, it's important to understand the key terms highlighted on the figure below.
While left and bottom axes are the most common, they aren’t the only options. In the previous module on scales, we created a barplot with the x-axis positioned at the top.
Right-side axes can also be useful, though they’re typically associated with dual y-axis line charts—a practice generally best avoided.
Margins
You may have noticed that axes take up some space! To accommodate them, we'll need to add margins around the chart area.
Managing margins can quickly become challenging, but don’t worry—I’ll show you a simple trick to make it easy.
In the previous module on scales, we learned how to position SVG shapes precisely, adding meaning to the growing graph we're constructing.
Now, it's time to provide context to these shape positions. For most chart types, this is achieved using axes. Axes can be complex elements, so let's explore how to create them effectively.
Free
2 minutes read
This module explains how to build axes like the one you can see at the bottom and on the left of this chart area.
Key Terminology
To work effectively with axes in data visualization, it's important to understand the key terms highlighted on the figure below.
While left and bottom axes are the most common, they aren’t the only options. In the previous module on scales, we created a barplot with the x-axis positioned at the top.
Right-side axes can also be useful, though they’re typically associated with dual y-axis line charts—a practice generally best avoided.
Margins
You may have noticed that axes take up some space! To accommodate them, we'll need to add margins around the chart area.
Managing margins can quickly become challenging, but don’t worry—I’ll show you a simple trick to make it easy.
\ No newline at end of file
diff --git a/course/axis/margin-and-translation.html b/course/axis/margin-and-translation.html
index 0c81144a..988d8e4d 100644
--- a/course/axis/margin-and-translation.html
+++ b/course/axis/margin-and-translation.html
@@ -1,4 +1,4 @@
-Margin and translation
Margin and translation
Most chart types use a bottom and a left axis.
In these cases, we need to leave space for tick labels and axis titles. Let’s look at how to implement this effectively.
Free
4 minutes read
SVG Area and Bounds Area
Imagine an SVG area with a width of 500px and a height of 300px.
The left and bottom axes aren’t displayed right at the SVG border. Instead, we add margins on all sides: left, right, bottom, and top.
The area within these margins is known as the bounds, where the chart content is positioned between the x and y axes. In our code, we’ll refer to the width and height of this bounds area as boundsWidth and boundsHeight.
Anatomy of the chart areas: some margins are set all around the SVG area. The area inside is called the Bounds.
Implementation
A chart component often starts by defining its margins. An object with 4 properties is ideal for that:
const MARGIN = {
+Margin and translation
Margin and translation
Most chart types use a bottom and a left axis.
In these cases, we need to leave space for tick labels and axis titles. Let’s look at how to implement this effectively.
Free
4 minutes read
SVG Area and Bounds Area
Imagine an SVG area with a width of 500px and a height of 300px.
The left and bottom axes aren’t displayed right at the SVG border. Instead, we add margins on all sides: left, right, bottom, and top.
The area within these margins is known as the bounds, where the chart content is positioned between the x and y axes. In our code, we’ll refer to the width and height of this bounds area as boundsWidth and boundsHeight.
Anatomy of the chart areas: some margins are set all around the SVG area. The area inside is called the Bounds.
Implementation
A chart component often starts by defining its margins. An object with 4 properties is ideal for that:
const MARGIN = {
top: 10,
right: 30,
bottom: 50,
@@ -12,4 +12,4 @@
>
// ... all shapes go here
</g>
-</svg>
What's going on here? 😱
1️⃣ The SVG area is created as usual with the svg element, along with the specified width and height.
2️⃣ The g element is used to group other SVG elements, similar to how a div works in HTML. This group represents the bounds, defined by its boundsWidth and boundsHeight dimensions!
3️⃣ The transform property is used to translate the bounds slightly to the right and down, creating space for the left and top margins!
Exercices
Drawing the Axis
Now that we’ve created space for it, it’s time to draw the axis. Let’s build some reusable components for this!
1️⃣ The SVG area is created as usual with the svg element, along with the specified width and height.
2️⃣ The g element is used to group other SVG elements, similar to how a div works in HTML. This group represents the bounds, defined by its boundsWidth and boundsHeight dimensions!
3️⃣ The transform property is used to translate the bounds slightly to the right and down, creating space for the left and top margins!
Exercices
Drawing the Axis
Now that we’ve created space for it, it’s time to draw the axis. Let’s build some reusable components for this!
\ No newline at end of file
diff --git a/course/axis/project.html b/course/axis/project.html
index b905b275..138cc364 100644
--- a/course/axis/project.html
+++ b/course/axis/project.html
@@ -1,4 +1,4 @@
-Project
Project
We've built a solid foundation in D3, SVG, and scales! Now, let's put that knowledge to the test by recreating a barplot inspired by The Economist.
Wip
4 minutes read
Our Objective
In this lesson, we aim to recreate a chart from The Economist with several key design elements:
Title, subtitle, and footer
Grid lines with values displayed at the top
Inline labels placed inside or outside the bars
The data
The dataset is very simple! It looks like this:
data = [
+Project
Project
We've built a solid foundation in D3, SVG, and scales! Now, let's put that knowledge to the test by recreating a barplot inspired by The Economist.
Wip
4 minutes read
Our Objective
In this lesson, we aim to recreate a chart from The Economist with several key design elements:
\ No newline at end of file
diff --git a/course/hover-effect/css-descendant-selector.html b/course/hover-effect/css-descendant-selector.html
index 1ddc7204..02f23376 100644
--- a/course/hover-effect/css-descendant-selector.html
+++ b/course/hover-effect/css-descendant-selector.html
@@ -1,4 +1,4 @@
-Strategy 2: CSS descendant selector
Strategy 2: CSS descendant selector
In the previous lesson, we learned how to modify a hovered graph item using the :hover CSS pseudo-class.
However, this approach has design limitations. To achieve a more effective highlighting effect, it's better to simultaneously dim the other graph items.
This can be accomplished using CSS alone, with the help of the CSS descendant selector.
Free
10 minutes read
What is a css descendant selector?
A descendant selector allows to target elements that are children of another element.
Here’s an example:
.rectangle {
+Strategy 2: CSS descendant selector
Strategy 2: CSS descendant selector
In the previous lesson, we learned how to modify a hovered graph item using the :hover CSS pseudo-class.
However, this approach has design limitations. To achieve a more effective highlighting effect, it's better to simultaneously dim the other graph items.
This can be accomplished using CSS alone, with the help of the CSS descendant selector.
Free
10 minutes read
What is a css descendant selector?
A descendant selector allows to target elements that are children of another element.
We assign a class called container to the SVG container and a class called rectangle to each rectangle in the chart.
Then we set the default rectangle opacity to 1. Using the descendant selector, you can reduce the opacity of all rectangles to 0.1 when the container is hovered.
Then, use a hover selector to set the opacity of the hovered rectangle back to 1.
Application: treemap
Strategy 2: use CSS descendant combinator to dim all markers except the one that is hovered.
Pros & Cons
Pros
Easy to implement
Improves design by making hover effects more noticeable
Excellent performance (no JS computation, minimal redrawing)
Cons
Fades all circles if the mouse enters the chart area without hovering over a specific circle. This technique works for chart where the whole svg area is covered by markers, like a treemap.
Cannot highlight circles that are obscured by other elements. (Potentially fixed using z-index).
More examples
The examples below all use this strategy to implement their hover effect.
We assign a class called container to the SVG container and a class called rectangle to each rectangle in the chart.
Then we set the default rectangle opacity to 1. Using the descendant selector, you can reduce the opacity of all rectangles to 0.1 when the container is hovered.
Then, use a hover selector to set the opacity of the hovered rectangle back to 1.
Application: treemap
Strategy 2: use CSS descendant combinator to dim all markers except the one that is hovered.
Pros & Cons
Pros
Easy to implement
Improves design by making hover effects more noticeable
Excellent performance (no JS computation, minimal redrawing)
Cons
Fades all circles if the mouse enters the chart area without hovering over a specific circle. This technique works for chart where the whole svg area is covered by markers, like a treemap.
Cannot highlight circles that are obscured by other elements. (Potentially fixed using z-index).
More examples
The examples below all use this strategy to implement their hover effect.
Exercices
Note: When you use the :hover pseudo-class on an SVG area, it activates whenever the mouse enters the entire SVG rectangle.
However, if you apply :hover to a g element, it will only trigger when the mouse hovers over one of the elements within the g group!
\ No newline at end of file
diff --git a/course/hover-effect/css-pseudo-class.html b/course/hover-effect/css-pseudo-class.html
index 05b64574..75f6cb6d 100644
--- a/course/hover-effect/css-pseudo-class.html
+++ b/course/hover-effect/css-pseudo-class.html
@@ -1,4 +1,4 @@
-Strategy 1: CSS pseudo element
Strategy 1: CSS pseudo element
The simplest strategy.
Let's explore how to use a CSS pseudo-class to modify only the graph item that is being hovered over.
Free
4 minutes read
What is a pseudo class
A CSS pseudo-class is a keyword added to a CSS selector that specifies a special state of the selected element(s). You can learn more about pseudo-classes in the MDN doc.
Essentially, this means you can assign a class to each shape in a graph and change its appearance when the user hovers over it.
Here is an example:
.scatterplotCircle {
+Strategy 1: CSS pseudo element
Strategy 1: CSS pseudo element
The simplest strategy.
Let's explore how to use a CSS pseudo-class to modify only the graph item that is being hovered over.
Free
4 minutes read
What is a pseudo class
A CSS pseudo-class is a keyword added to a CSS selector that specifies a special state of the selected element(s). You can learn more about pseudo-classes in the MDN doc.
Essentially, this means you can assign a class to each shape in a graph and change its appearance when the user hovers over it.
Consider a scatterplot with multiple SVG circle elements, each assigned a .scatterplotCircle class. In the CSS file, you can set the fill-opacity to 0.3 using this class.
To change the appearance on hover, use the .scatterplotCircle:hover selector to increase the opacity to 1.
Strategy 1: use a pseudo-class to change the appearance of the hovered marker
Pros & Cons
Pros
Easy to implement
Excellent performance (no JS computation, minimal redrawing)
Cons
Poor design: non-hovered circles remain prominent, so the highlight effect is weak
If the highlight information comes as a prop, another solution is needed
More examples
The examples below all use this strategy to implement their hover effect.
Consider a scatterplot with multiple SVG circle elements, each assigned a .scatterplotCircle class. In the CSS file, you can set the fill-opacity to 0.3 using this class.
To change the appearance on hover, use the .scatterplotCircle:hover selector to increase the opacity to 1.
Strategy 1: use a pseudo-class to change the appearance of the hovered marker
Pros & Cons
Pros
Easy to implement
Excellent performance (no JS computation, minimal redrawing)
Cons
Poor design: non-hovered circles remain prominent, so the highlight effect is weak
If the highlight information comes as a prop, another solution is needed
More examples
The examples below all use this strategy to implement their hover effect.
\ No newline at end of file
diff --git a/course/hover-effect/internal-state.html b/course/hover-effect/internal-state.html
index e9916bd9..00dec0a1 100644
--- a/course/hover-effect/internal-state.html
+++ b/course/hover-effect/internal-state.html
@@ -1,4 +1,4 @@
-Strategy 4: react internal state
Strategy 4: react internal state
In the previous lesson, we learned how to modify a hovered graph item using the :hover CSS pseudo-class.
However, this approach has design limitations. To achieve a more effective highlighting effect, it's better to simultaneously dim the other graph items.
This can be accomplished using CSS alone, with the help of the CSS descendant selector.
Wip
4 minutes read
Internal state & event listener
Add onMouseEnter event listener to all circle
Set an internal state
Trigger a redraw of all circles with conditional state.
As for the tooltip example above, everything starts with an internal state (called hoveredGroup) that stores which circle is hovered hover.
Now, this state needs to be updated when a user hovers over the circle. setHoveredGroup can be passed as a callback to the onMouseOver attribute of each circle.
On top of this, some specific css classes can be attributed to circles depending on the circle that is hovered hover. In the example above, a class called dimmed is added to circles that must disappear.
To put it in a nutshell, the circles are created as follows:
In the previous lesson, we learned how to modify a hovered graph item using the :hover CSS pseudo-class.
However, this approach has design limitations. To achieve a more effective highlighting effect, it's better to simultaneously dim the other graph items.
This can be accomplished using CSS alone, with the help of the CSS descendant selector.
Wip
4 minutes read
Internal state & event listener
Add onMouseEnter event listener to all circle
Set an internal state
Trigger a redraw of all circles with conditional state.
As for the tooltip example above, everything starts with an internal state (called hoveredGroup) that stores which circle is hovered hover.
Now, this state needs to be updated when a user hovers over the circle. setHoveredGroup can be passed as a callback to the onMouseOver attribute of each circle.
On top of this, some specific css classes can be attributed to circles depending on the circle that is hovered hover. In the example above, a class called dimmed is added to circles that must disappear.
To put it in a nutshell, the circles are created as follows:
const allShapes = data.map((d, i) => {
const className = // class if the circle depends on the hover state
hoveredGroup && d.group !== hoveredGroup
? styles.scatterplotCircle + " " + styles.dimmed
@@ -17,4 +17,4 @@
onMouseLeave={() => setHoveredGroup(null)} // and to set it back to null
/>
);
-});
Last but not least, some css needs to be added to customize the circle depending on if they are in default, .dimmed or :hover mode.
Note that the filter: saturate(0) is a good way to dim unwanted circles. Also, playing with transition-delay and transition-duration adds to animate the transition is a nice touch you should consider. Check the code below the example to see the full css.
TODO.
Pros & Cons
Pros
Allows to sync the hover effect with other UI updates. The hovered state can be used to update any other react components in the application. Like tooltip or another graph.
Using javascript to trigger the animation can give more flexibility to customize the hover effect, using react-spring for instance.
Cons
Performance 🚨. Here we are redrawing all the circles each time a hover effect is hovered. This can be dramatic if you have thousands of circles!
More examples
The examples below all use this strategy to implement their hover effect.
Last but not least, some css needs to be added to customize the circle depending on if they are in default, .dimmed or :hover mode.
Note that the filter: saturate(0) is a good way to dim unwanted circles. Also, playing with transition-delay and transition-duration adds to animate the transition is a nice touch you should consider. Check the code below the example to see the full css.
TODO.
Pros & Cons
Pros
Allows to sync the hover effect with other UI updates. The hovered state can be used to update any other react components in the application. Like tooltip or another graph.
Using javascript to trigger the animation can give more flexibility to customize the hover effect, using react-spring for instance.
Cons
Performance 🚨. Here we are redrawing all the circles each time a hover effect is hovered. This can be dramatic if you have thousands of circles!
More examples
The examples below all use this strategy to implement their hover effect.
\ No newline at end of file
diff --git a/course/hover-effect/introduction.html b/course/hover-effect/introduction.html
index 26aa4f8c..a76a9e15 100644
--- a/course/hover-effect/introduction.html
+++ b/course/hover-effect/introduction.html
@@ -1 +1 @@
-What is it?
What is it?
In the previous modules, you learned how to create a wide variety of static charts. 👏
However, interactivity is essential web applications. Adding hover effects significantly enhances the user experience by highlighting specific series on the chart.
In this module, we'll explore several strategies for implementing hover effects using both CSS and React. Before diving into the code, let's ensure we have a clear understanding of what hover effects are.
Free
3 minutes read
Definition
A hover effect is a visual change that occurs when a user moves their cursor over a specific element on a webpage.
→
Technically speaking, tooltip are also a hover effect. But we will talk about them in a dedicated section.
When implementing a hover effect, you should be careful about:
Design: hover effect is visually appealing and enhances the user experience. It is consistent with the overall design of your application and does not distract or confuse the user.
Performance: hover effect is fast: no lag or delay. It does not significantly impact the performance of your application, especially when dealing with large datasets or complex viz.
Three Types of Hover Effects
In my opinion, there are three main types of hover effects: those that only modify the hovered graph item, those that modify other graph markers, and those that affect other related elements in the user interface.
Click the button below to see examples of all three types:
Observe this graph: when you hover over a circle, its style changes slightly!
While this isn't the most dramatic visual effect, it is very easy to implement using a single CSS pseudo-element.
We'll learn how to implement this in the next lesson.
Let's code
Enough theory.
Let's dive into the simplest hover effect you can create: just a few lines of CSS using pseudo-elements.
In the previous modules, you learned how to create a wide variety of static charts. 👏
However, interactivity is essential web applications. Adding hover effects significantly enhances the user experience by highlighting specific series on the chart.
In this module, we'll explore several strategies for implementing hover effects using both CSS and React. Before diving into the code, let's ensure we have a clear understanding of what hover effects are.
Free
3 minutes read
Definition
A hover effect is a visual change that occurs when a user moves their cursor over a specific element on a webpage.
→
Technically speaking, tooltip are also a hover effect. But we will talk about them in a dedicated section.
When implementing a hover effect, you should be careful about:
Design: hover effect is visually appealing and enhances the user experience. It is consistent with the overall design of your application and does not distract or confuse the user.
Performance: hover effect is fast: no lag or delay. It does not significantly impact the performance of your application, especially when dealing with large datasets or complex viz.
Three Types of Hover Effects
In my opinion, there are three main types of hover effects: those that only modify the hovered graph item, those that modify other graph markers, and those that affect other related elements in the user interface.
Click the button below to see examples of all three types:
Observe this graph: when you hover over a circle, its style changes slightly!
While this isn't the most dramatic visual effect, it is very easy to implement using a single CSS pseudo-element.
We'll learn how to implement this in the next lesson.
Let's code
Enough theory.
Let's dive into the simplest hover effect you can create: just a few lines of CSS using pseudo-elements.
\ No newline at end of file
diff --git a/course/hover-effect/link-two-graphs.html b/course/hover-effect/link-two-graphs.html
index 497ec172..fa881b9e 100644
--- a/course/hover-effect/link-two-graphs.html
+++ b/course/hover-effect/link-two-graphs.html
@@ -1,4 +1,4 @@
-Hover interaction on a chart with React
Hover interaction on a chart with React
Interactivity is crucial in data visualization, especially for web applications. Adding hover effects enhances user experience by highlighting specific series on the chart.
This post suggests a few strategies to implement hover effects using css and react.
Note: this article does not talk about tooltips that has its dedicated section.
4️⃣ Internal state & event listener
Add onMouseEnter event listener to all circle
Set an internal state
Trigger a redraw of all circles with conditional state.
As for the tooltip example above, everything starts with an internal state (called hoveredGroup) that stores which circle is hovered hover.
Now, this state needs to be updated when a user hovers over the circle. setHoveredGroup can be passed as a callback to the onMouseOver attribute of each circle.
On top of this, some specific css classes can be attributed to circles depending on the circle that is hovered hover. In the example above, a class called dimmed is added to circles that must disappear.
To put it in a nutshell, the circles are created as follows:
const allShapes = data.map((d, i) => {
+Hover interaction on a chart with React
Hover interaction on a chart with React
Interactivity is crucial in data visualization, especially for web applications. Adding hover effects enhances user experience by highlighting specific series on the chart.
This post suggests a few strategies to implement hover effects using css and react.
Note: this article does not talk about tooltips that has its dedicated section.
4️⃣ Internal state & event listener
Add onMouseEnter event listener to all circle
Set an internal state
Trigger a redraw of all circles with conditional state.
As for the tooltip example above, everything starts with an internal state (called hoveredGroup) that stores which circle is hovered hover.
Now, this state needs to be updated when a user hovers over the circle. setHoveredGroup can be passed as a callback to the onMouseOver attribute of each circle.
On top of this, some specific css classes can be attributed to circles depending on the circle that is hovered hover. In the example above, a class called dimmed is added to circles that must disappear.
To put it in a nutshell, the circles are created as follows:
const allShapes = data.map((d, i) => {
const className = // class if the circle depends on the hover state
hoveredGroup && d.group !== hoveredGroup
? styles.scatterplotCircle + " " + styles.dimmed
@@ -20,4 +20,4 @@
});
Last but not least, some css needs to be added to customize the circle depending on if they are in default, .dimmed or :hover mode.
Note that the filter: saturate(0) is a good way to dim unwanted circles. Also, playing with transition-delay and transition-duration adds to animate the transition is a nice touch you should consider. Check the code below the example to see the full css.
TODO.
Pros
Allows to sync the hover effect with other UI updates. The hovered state can be used to update any other react components in the application. Like tooltip or another graph.
Using javascript to trigger the animation can give more flexibility to customize the hover effect, using react-spring for instance.
Cons
Performance 🚨. Here we are redrawing all the circles each time a hover effect is hovered. This can be dramatic if you have thousands of circles!
4️⃣ Canvas
Using the useDimensions hook described above is pretty straight-forward. You first need to create a ref using the react useRef()function:
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/course/hover-effect/toggle-class-in-js.html b/course/hover-effect/toggle-class-in-js.html
index 47675b97..d95fac04 100644
--- a/course/hover-effect/toggle-class-in-js.html
+++ b/course/hover-effect/toggle-class-in-js.html
@@ -1,10 +1,30 @@
-Strategy 3: toggle css classes
Strategy 3: toggle css classes
In the previous lesson, we learned how to modify a hovered graph item using the :hover CSS pseudo-class.
However, this approach has design limitations. To achieve a more effective highlighting effect, it's better to simultaneously dim the other graph items.
This can be accomplished using CSS alone, with the help of the CSS descendant selector.
Wip
4 minutes read
Toggle class in JS
Problem above: when mouse enter the chart area, triggers effect even if no marker hovered over.
\ No newline at end of file
+Strategy 3: toggle css classes
Strategy 3: toggle css classes
In the previous lesson, we explored how to dim elements that are not being hovered over using a CSS-only approach.
However, there are times when using JavaScript can provide more precise control over the hover effect. A handy technique is to toggle classes with JavaScript. Let’s take a look at how to do this.
Wip
4 minutes read
🔘 Toggle Class in JavaScript
1️⃣ Create a ref
We’ve discussed the useRef React hook a few times now.
This hook allows us to target specific elements in the DOM and manipulate them with JavaScript.
// Define a ref for the graph container
+const containerRef = useRef();
+
+// Attach this ref to an element in the DOM
+<svg ref={containerRef}>
+ <circle ... />
+ <circle ... />
+ <circle ... />
+</svg>
2️⃣ Toggle classes
Once we have the containerRef set up, we can use it to make changes to the SVG container from anywhere in the graph!
For example, we can add an onMouseEnter property to the circle that will apply a hasHighlight class to the SVG container:
A compound class selector combines multiple class names to target elements that match all of the specified classes. For example: .class1.class2 .
We can use CSS compound class selectors to apply different styles based on whether the .hasHighlight class is present!
For example, we can set the opacity of all "slices" in the container to 1, except when the container has the .hasHighlight class, in which case the opacity will be set to 0.2:
A donut chart is a variation of the more well-known pie chart. It is easy to create using the pie() function from D3.js.
The following example demonstrates the technique described earlier. When a slice is hovered over, a class is added to the SVG container, resulting in a CSS change for all the other slices.
A donut chart with hover interaction using the class toggle strategy.
Pros & Cons
Pros
Fine control over interactions via JavaScript
Performance-friendly: no re-rendering required
Cons
Doesn't align with React’s central state management approach, which can make managing state more challenging.
More examples
The examples below all use this strategy to implement their hover effect.
Check the legend on the left hand side: it uses class toggle for its hover effect!
\ No newline at end of file
diff --git a/course/introduction/initial-setup.html b/course/introduction/initial-setup.html
index 05f9422c..202c76df 100644
--- a/course/introduction/initial-setup.html
+++ b/course/introduction/initial-setup.html
@@ -1,7 +1,7 @@
-Initial setup
Initial setup
Creating graphs in a React environment using D3.js can be quite complex.
Here’s a list of tools I highly recommend to simplify your journey in building a dataviz web application. 🔧
Free
4 minutes read
1️⃣ React: Use a Framework
React is a JavaScript library, not a framework.
Several frameworks are built on top of it to manage common requirements such as routing, static page generation, server-side rendering, and more.
Personally, I'm a huge fan of Next.js, but other options like Gatsby, Remix, or even sticking with Create React App are excellent choices as well!
List of next.js features that will make your life easier. Source.
2️⃣ D3.js: Load What You Need
D3.js is not a monolithic library but a collection of around 30 discrete modules.
You don’t need to install and load the entire D3.js library for your work; it's likely that you'll only use a fraction of its capabilities.
For example, if you need to work with scales, you can simply install the d3-scale module using npm install d3-scale and utilize only the functions you require from it!
+Initial setup
Initial setup
Creating graphs in a React environment using D3.js can be quite complex.
Here’s a list of tools I highly recommend to simplify your journey in building a dataviz web application. 🔧
Free
4 minutes read
1️⃣ React: Use a Framework
React is a JavaScript library, not a framework.
Several frameworks are built on top of it to manage common requirements such as routing, static page generation, server-side rendering, and more.
Personally, I'm a huge fan of Next.js, but other options like Gatsby, Remix, or even sticking with Create React App are excellent choices as well!
List of next.js features that will make your life easier. Source.
2️⃣ D3.js: Load What You Need
D3.js is not a monolithic library but a collection of around 30 discrete modules.
You don’t need to install and load the entire D3.js library for your work; it's likely that you'll only use a fraction of its capabilities.
For example, if you need to work with scales, you can simply install the d3-scale module using npm install d3-scale and utilize only the functions you require from it!
// import the scaleLinear function from the d3-scale module
import { scaleLinear } from "d3";
// Use it in your code
cons xScale = scaleLinear()
-
3️⃣ Typescript: your best friend
TypeScript is like a special version of JavaScript that helps you write better code by letting you add labels to your variables. These labels, or "types," tell you what kind of data you're working with, like number or string.
This way, if you make a mistake, TypeScript can help you catch it before you run your program, making it easier to fix problems and build bigger projects!
I strongly advise to learn and use typescript. I'll use it in this course, but you do not have to if you do not want to.
D3.js & Typescript
Fortunately, DefinitelyTyped provides the types of all d3.js functions and objects! So you can install all the types we'll need with:
If you like those tools too, check out my boilerplate! This repository is a minimal project with all the tools mentioned in this lesson already installed. Just clone it, run npm run dev, and you’ll have a working environment that looks like this right away:
Overview of my dataviz project boilerplate. Use next.js + d3 + tailwind + typescript + shadcn/UI in 2 minutes. .
✌️ No Worries!
Don’t want to install any of those tools? No worries!
All the lessons in this course feature interactive sandboxes, allowing you to learn and experiment without ever leaving this website!
Here is an example with a basic barplot you can edit by clicking on the "see code" button:
Most basic barplot built with d3.js for scales, and react for rendering
Now, let’s start creating some dataviz magic on that screen, shall we?
TypeScript is like a special version of JavaScript that helps you write better code by letting you add labels to your variables. These labels, or "types," tell you what kind of data you're working with, like number or string.
This way, if you make a mistake, TypeScript can help you catch it before you run your program, making it easier to fix problems and build bigger projects!
I strongly advise to learn and use typescript. I'll use it in this course, but you do not have to if you do not want to.
D3.js & Typescript
Fortunately, DefinitelyTyped provides the types of all d3.js functions and objects! So you can install all the types we'll need with:
If you like those tools too, check out my boilerplate! This repository is a minimal project with all the tools mentioned in this lesson already installed. Just clone it, run npm run dev, and you’ll have a working environment that looks like this right away:
Overview of my dataviz project boilerplate. Use next.js + d3 + tailwind + typescript + shadcn/UI in 2 minutes. .
✌️ No Worries!
Don’t want to install any of those tools? No worries!
All the lessons in this course feature interactive sandboxes, allowing you to learn and experiment without ever leaving this website!
Here is an example with a basic barplot you can edit by clicking on the "see code" button:
Most basic barplot built with d3.js for scales, and react for rendering
Now, let’s start creating some dataviz magic on that screen, shall we?
\ No newline at end of file
diff --git a/course/introduction/introduction-to-d3.html b/course/introduction/introduction-to-d3.html
index 283c2662..f172b28e 100644
--- a/course/introduction/introduction-to-d3.html
+++ b/course/introduction/introduction-to-d3.html
@@ -1 +1 @@
-What is d3
What is d3
D3.js is a powerful JavaScript library that offers a wide range of functions for creating dynamic graphs and visualizations.
Its popularity is immense; you’ll find it hard to encounter a web-based graph that doesn’t leverage D3 in some capacity. Let’s explore why it’s become such an essential tool.
It is this kind of graph, where rectangles are used to represent hierarchical data, with size proportional to a value:
A basic treemap made with React and D3.js
Drawing rectangles with React and some divs is simple. But how do you calculate the coordinates to ensure each rectangle's area matches its corresponding value? 🙈
That's exactly what the treemap() function in d3.js does for you.
And that's why we need d3!
Note: the React Graph Gallery has a whole section on treemaps! But do not try to understand the code for now!
❌ Not a Charting library
It's important to understand that D3 is not a charting library.
A charting library would offer a ready-made Scatterplot component where you pass some properties, and it renders the chart for you.
D3 doesn’t do that. It provides utility functions to help you create your scatterplot, but it won’t build it for you.
D3.js website screenshot.
That's why d3 is ideal for "bespoke" dataviz. It requires more effort but gives you complete control for any customization.
🗡️ Only a fraction
D3.js offers a wide range of functions, which can be grouped into two main categories:
📐 Math functions
These include functions like treemap() mentioned earlier. They return numbers, positions, colors, or text, but don’t interact with the DOM.
✍️ Rendering functions
Functions that modify the DOM, like append, which can add elements (e.g., a circle) to an SVG.
Here’s the good news: in a React environment, we only need D3's math functions, since all the rendering will be handled by React and its JSX!
D3.js is a powerful JavaScript library that offers a wide range of functions for creating dynamic graphs and visualizations.
Its popularity is immense; you’ll find it hard to encounter a web-based graph that doesn’t leverage D3 in some capacity. Let’s explore why it’s become such an essential tool.
It is this kind of graph, where rectangles are used to represent hierarchical data, with size proportional to a value:
A basic treemap made with React and D3.js
Drawing rectangles with React and some divs is simple. But how do you calculate the coordinates to ensure each rectangle's area matches its corresponding value? 🙈
That's exactly what the treemap() function in d3.js does for you.
And that's why we need d3!
Note: the React Graph Gallery has a whole section on treemaps! But do not try to understand the code for now!
❌ Not a Charting library
It's important to understand that D3 is not a charting library.
A charting library would offer a ready-made Scatterplot component where you pass some properties, and it renders the chart for you.
D3 doesn’t do that. It provides utility functions to help you create your scatterplot, but it won’t build it for you.
D3.js website screenshot.
That's why d3 is ideal for "bespoke" dataviz. It requires more effort but gives you complete control for any customization.
🗡️ Only a fraction
D3.js offers a wide range of functions, which can be grouped into two main categories:
📐 Math functions
These include functions like treemap() mentioned earlier. They return numbers, positions, colors, or text, but don’t interact with the DOM.
✍️ Rendering functions
Functions that modify the DOM, like append, which can add elements (e.g., a circle) to an SVG.
Here’s the good news: in a React environment, we only need D3's math functions, since all the rendering will be handled by React and its JSX!
\ No newline at end of file
diff --git a/course/introduction/introduction-to-react.html b/course/introduction/introduction-to-react.html
index 9c6c3ebd..b1847601 100644
--- a/course/introduction/introduction-to-react.html
+++ b/course/introduction/introduction-to-react.html
@@ -1 +1 @@
-What is react
What is react
React is a JavaScript library used for creating dynamic and interactive user interfaces through reusable components. Developed by Facebook, it simplifies the process of managing application state and updating the user interface efficiently.
This lesson will offer a concise overview of React and its key features.
Free
4 minutes read
Why React?
React was introduced in 2013, transforming the landscape of web development.
Prior to its component-based architecture, building web applications was complex and cumbersome.
If you make charts for the web and don't know react, you're missing out!
This is particularly evident in applications that involve data visualization. The ability to create reusable components, like a Scatterplot component, that can be utilized throughout your app, is a game-changer for your development process.
If you're building a data visualization application, React offers everything you need:
Robust state management, perfectly suited for dynamic data.
A component-driven approach, ideal for creating interactive visualizations.
Extensive community support and comprehensive documentation.
Expected knowledge
This course focuses on Data Visualization using React.
We won’t cover the basics of React, as that’s a vast topic deserving its own dedicated course! For a solid introduction, check out this intro by Codecademy.
Basically, you should just be able to understand what's happening in this sandbox:
That's it, no need to be a React expert.
Even seasoned developers will find valuable insights here: creating visualizations with React and D3 involves specialized knowledge that differs significantly from typical UI development.
Lastly, you don’t need prior experience with D3 to get started! We’ll use it sparingly and provide a thorough explanation of how it works.
🤔Alternatives
While React is a leading choice for building user interfaces, there are notable alternatives, primarily Vue and Angular. Additionally, Svelte has gained considerable traction within the data visualization community.
However, React's popularity is significantly larger, as illustrated in the graph below from npmtrends.
For further insights, you can explore a variety of metrics on this repository. It's clear to me that React has established itself as the de-facto library for creating user interfaces and is here to stay.
Evolution of the number of downloads for React (green), Angular, Vue and Svelte
React is a JavaScript library used for creating dynamic and interactive user interfaces through reusable components. Developed by Facebook, it simplifies the process of managing application state and updating the user interface efficiently.
This lesson will offer a concise overview of React and its key features.
Free
4 minutes read
Why React?
React was introduced in 2013, transforming the landscape of web development.
Prior to its component-based architecture, building web applications was complex and cumbersome.
If you make charts for the web and don't know react, you're missing out!
This is particularly evident in applications that involve data visualization. The ability to create reusable components, like a Scatterplot component, that can be utilized throughout your app, is a game-changer for your development process.
If you're building a data visualization application, React offers everything you need:
Robust state management, perfectly suited for dynamic data.
A component-driven approach, ideal for creating interactive visualizations.
Extensive community support and comprehensive documentation.
Expected knowledge
This course focuses on Data Visualization using React.
We won’t cover the basics of React, as that’s a vast topic deserving its own dedicated course! For a solid introduction, check out this intro by Codecademy.
Basically, you should just be able to understand what's happening in this sandbox:
That's it, no need to be a React expert.
Even seasoned developers will find valuable insights here: creating visualizations with React and D3 involves specialized knowledge that differs significantly from typical UI development.
Lastly, you don’t need prior experience with D3 to get started! We’ll use it sparingly and provide a thorough explanation of how it works.
🤔Alternatives
While React is a leading choice for building user interfaces, there are notable alternatives, primarily Vue and Angular. Additionally, Svelte has gained considerable traction within the data visualization community.
However, React's popularity is significantly larger, as illustrated in the graph below from npmtrends.
For further insights, you can explore a variety of metrics on this repository. It's clear to me that React has established itself as the de-facto library for creating user interfaces and is here to stay.
Evolution of the number of downloads for React (green), Angular, Vue and Svelte
\ No newline at end of file
diff --git a/course/introduction/js-dataviz-libraries.html b/course/introduction/js-dataviz-libraries.html
index f0acd75d..ba257417 100644
--- a/course/introduction/js-dataviz-libraries.html
+++ b/course/introduction/js-dataviz-libraries.html
@@ -1,7 +1,7 @@
-Chart libraries: don't
Chart libraries: don't
When creating a chart, your first instinct is usually to reach for a JavaScript library. While that’s a great starting point, it often leads to unnecessary complications.
Let’s explore why. 🤓
Free
4 minutes read
In a hurry
You're building an impressive app and need to add a barplot. You'll need rectangles, axes, labels, a title, and maybe even a tooltip—all responsive and well-designed.
The good news?
There are plenty of JavaScript dataviz libraries that can handle this perfectly 🔥:
List of the biggest javascript libraries for data visualization
Let's try with Nivo (I used it for a client recently 🙃).
All you have to do is to npm install the lib, import it ResponsiveBar component and use it with the 3 required arguments: data, keys and indexBy.
+Chart libraries: don't
Chart libraries: don't
When creating a chart, your first instinct is usually to reach for a JavaScript library. While that’s a great starting point, it often leads to unnecessary complications.
Let’s explore why. 🤓
Free
4 minutes read
In a hurry
You're building an impressive app and need to add a barplot. You'll need rectangles, axes, labels, a title, and maybe even a tooltip—all responsive and well-designed.
The good news?
There are plenty of JavaScript dataviz libraries that can handle this perfectly 🔥:
List of the biggest javascript libraries for data visualization
Let's try with Nivo (I used it for a client recently 🙃).
All you have to do is to npm install the lib, import it ResponsiveBar component and use it with the 3 required arguments: data, keys and indexBy.
// Import lib
import { ResponsiveBar } from '@nivo/bar';
// Make a barchart
<ResponsiveBar data={data} keys={['value']} indexBy="country"/>
-
Just like that, you’ve created a fantastic barplot! Time to celebrate 🎉.
A basic barplot made using the Nivo library in 3 minutes.
Yes, But...
The excitement fades, and it’s time to elevate your chart to the next level. Here’s a list of common tweaks you’ll likely need:
📊 Chart type
The user actually needs a diverging bar chart, with individual data points overlayed.
🎨 Design
Enhanced colors, labels on top, subtle arrows, custom gridlines, and a unique font.
🎮 Interactivity
Smooth hover effect to highlight specific groups which also triggers a change on a minimap elsewhere in the app. Oh, and clicking on text should also highlight certain dots/rectangles.
Essentially, you’re aiming for something like this:
→
I made this app to win a dataviz competition: the Pacific Dataviz Challenge
Interactive barplot made to win a dataviz challenge. Check it out.
Now, you're in trouble!
Some of the features listed above can be achieved with a dataviz library, but they will require endless tweaks beyond what the library is designed to do.
Others simply aren’t possible, meaning you’ll have to convince the product manager to let them go. 😞
JavaScript libraries are great for quick visualizations, but their limitations will stop you from winning a dataviz challenge or meeting your PM's complex needs.
To take data visualization seriously, you should move away from those JavaScript libraries and switch to D3.js.
Let’s explore what D3.js is and how it stands apart. ➡️
Just like that, you’ve created a fantastic barplot! Time to celebrate 🎉.
A basic barplot made using the Nivo library in 3 minutes.
Yes, But...
The excitement fades, and it’s time to elevate your chart to the next level. Here’s a list of common tweaks you’ll likely need:
📊 Chart type
The user actually needs a diverging bar chart, with individual data points overlayed.
🎨 Design
Enhanced colors, labels on top, subtle arrows, custom gridlines, and a unique font.
🎮 Interactivity
Smooth hover effect to highlight specific groups which also triggers a change on a minimap elsewhere in the app. Oh, and clicking on text should also highlight certain dots/rectangles.
Essentially, you’re aiming for something like this:
→
I made this app to win a dataviz competition: the Pacific Dataviz Challenge
Interactive barplot made to win a dataviz challenge. Check it out.
Now, you're in trouble!
Some of the features listed above can be achieved with a dataviz library, but they will require endless tweaks beyond what the library is designed to do.
Others simply aren’t possible, meaning you’ll have to convince the product manager to let them go. 😞
JavaScript libraries are great for quick visualizations, but their limitations will stop you from winning a dataviz challenge or meeting your PM's complex needs.
To take data visualization seriously, you should move away from those JavaScript libraries and switch to D3.js.
Let’s explore what D3.js is and how it stands apart. ➡️
\ No newline at end of file
diff --git a/course/responsiveness/code-organization.html b/course/responsiveness/code-organization.html
index e7cbbdd7..327a7da3 100644
--- a/course/responsiveness/code-organization.html
+++ b/course/responsiveness/code-organization.html
@@ -1,4 +1,4 @@
-Best Practices for Code Organization
Best Practices for Code Organization
Let's create a confortable working environment: let's use Next.js.
Wip
4 minutes read
🎁 Wrapper component
I like to create a "wrapper" component that manages the responsiveness and pass all the other props to the viz component, plus width and height.
If you already have a dataviz component and just want to make it responsive, this template should be useful to you:
type ResponsiveDensityChartProps = {
+Best Practices for Code Organization
Best Practices for Code Organization
Let's create a confortable working environment: let's use Next.js.
Wip
4 minutes read
🎁 Wrapper component
I like to create a "wrapper" component that manages the responsiveness and pass all the other props to the viz component, plus width and height.
If you already have a dataviz component and just want to make it responsive, this template should be useful to you:
type ResponsiveDensityChartProps = {
data: number[];
};
@@ -28,4 +28,4 @@
// Non responsive component
const DensityChart = ({ width, height, data }: DensityChartProps) => {
//... dataviz code goes here
-}
\ No newline at end of file
diff --git a/course/responsiveness/common-pitfalls.html b/course/responsiveness/common-pitfalls.html
index fbf24e32..acd310be 100644
--- a/course/responsiveness/common-pitfalls.html
+++ b/course/responsiveness/common-pitfalls.html
@@ -1 +1 @@
-Common Pitfalls and How to Avoid Them
Common Pitfalls and How to Avoid Them
Let's create a confortable working environment: let's use Next.js.
Wip
4 minutes read
🐞 Caveats
Here are some potential caveats to consider when using the useDimensions hook:
1️⃣ Container Needs Dimensions
In HTML, not all <div> elements have dimensions by default. If a <div> lacks dimensions, the useDimensions hook won't be able to measure it!
An inline <div> cannot have width and height.
By default, a <div> has no height. Its width is 100%, but its height is determined by its content. This means that without content, the <div> will have no height.
2️⃣ Performance
When the graph dimensions change, ensure that you only recompute what is necessary within the visualization component. useMemo and useCallback are valuable tools for optimizing performance.
3️⃣ Design
A graph is not merely a piece of text; its aspect ratio significantly affects its appearance. A wide graph might look great, while a narrower version may not. Sometimes, adapting the chart type based on window size is the best approach.
4️⃣ Zero Dimensions
Be cautious with the useDimensions hook. When your app first loads, the reference dimensions might be null or zero. Ensure your code handles this scenario properly to avoid errors.
\ No newline at end of file
+Common Pitfalls and How to Avoid Them
Common Pitfalls and How to Avoid Them
Let's create a confortable working environment: let's use Next.js.
Wip
4 minutes read
🐞 Caveats
Here are some potential caveats to consider when using the useDimensions hook:
1️⃣ Container Needs Dimensions
In HTML, not all <div> elements have dimensions by default. If a <div> lacks dimensions, the useDimensions hook won't be able to measure it!
An inline <div> cannot have width and height.
By default, a <div> has no height. Its width is 100%, but its height is determined by its content. This means that without content, the <div> will have no height.
2️⃣ Performance
When the graph dimensions change, ensure that you only recompute what is necessary within the visualization component. useMemo and useCallback are valuable tools for optimizing performance.
3️⃣ Design
A graph is not merely a piece of text; its aspect ratio significantly affects its appearance. A wide graph might look great, while a narrower version may not. Sometimes, adapting the chart type based on window size is the best approach.
4️⃣ Zero Dimensions
Be cautious with the useDimensions hook. When your app first loads, the reference dimensions might be null or zero. Ensure your code handles this scenario properly to avoid errors.
\ No newline at end of file
diff --git a/course/responsiveness/introduction.html b/course/responsiveness/introduction.html
index 21eeb743..340d2691 100644
--- a/course/responsiveness/introduction.html
+++ b/course/responsiveness/introduction.html
@@ -1,4 +1,4 @@
-Introduction to responsiveness
Introduction to responsiveness
To build a graph, you need to know the width and height of the SVG area.
However, we often don't know the exact dimensions we need for our visualizations; we just want them to fit their container.
This module explains how to make a chart responsive. It provides the code for a hook that detects changes in a div's dimensions and explains how to inject its returned values into a visualization component.
Free
4 minutes read
What is responsiveness?
Responsiveness refers to the ability of a data visualization (such as charts, graphs, or dashboards) to adapt and display correctly across different devices and screen sizes.
This ensures that users have an optimal viewing experience whether they are accessing the visualizations on a desktop, tablet, or mobile device
This density chart is responsive! Resize your window to see how the graph fits its container.
Responsiveness is a key aspect of web development. This module specifically focuses on making charts responsive. It won't cover general topics like CSS, Flexbox, Grid, or other concepts related to overall website responsiveness.
Design Considerations
A graph can appear visually appealing when its width exceeds its height, but it may lose its effectiveness if shrunk too much. In some cases, using a completely different chart type on smaller screens, removing a chart, or flipping it may be necessary.
This course offers technical guidelines on making graphs responsive. It focuses on the "how" rather than the "what," providing you with the tools to achieve your desired outcomes.
😢 Unresponsive chart
When building a chart with JavaScript, knowing its dimensions — width and height — is essential. These dimensions are needed to compute scales, draw axes, and determine where to place shapes.
Consequently, a visualization component always expects width and heightproperties.
Consider a simple density plot, for example. The code looks like this:
import * as d3 from "d3";
+Introduction to responsiveness
Introduction to responsiveness
To build a graph, you need to know the width and height of the SVG area.
However, we often don't know the exact dimensions we need for our visualizations; we just want them to fit their container.
This module explains how to make a chart responsive. It provides the code for a hook that detects changes in a div's dimensions and explains how to inject its returned values into a visualization component.
Free
4 minutes read
What is responsiveness?
Responsiveness refers to the ability of a data visualization (such as charts, graphs, or dashboards) to adapt and display correctly across different devices and screen sizes.
This ensures that users have an optimal viewing experience whether they are accessing the visualizations on a desktop, tablet, or mobile device
This density chart is responsive! Resize your window to see how the graph fits its container.
Responsiveness is a key aspect of web development. This module specifically focuses on making charts responsive. It won't cover general topics like CSS, Flexbox, Grid, or other concepts related to overall website responsiveness.
Design Considerations
A graph can appear visually appealing when its width exceeds its height, but it may lose its effectiveness if shrunk too much. In some cases, using a completely different chart type on smaller screens, removing a chart, or flipping it may be necessary.
This course offers technical guidelines on making graphs responsive. It focuses on the "how" rather than the "what," providing you with the tools to achieve your desired outcomes.
😢 Unresponsive chart
When building a chart with JavaScript, knowing its dimensions — width and height — is essential. These dimensions are needed to compute scales, draw axes, and determine where to place shapes.
Consequently, a visualization component always expects width and heightproperties.
Consider a simple density plot, for example. The code looks like this:
import * as d3 from "d3";
type DensityProps = {
width: number; // 🙁 not responsive!
@@ -17,4 +17,4 @@
</svg>
</div>
);
-};
Let's Code
Let's assume we have a div that is responsive and takes the full width of an app.
How can we draw a chart inside this div that also takes up the entire space and remains responsive?
\ No newline at end of file
diff --git a/course/responsiveness/use-dimension-hook.html b/course/responsiveness/use-dimension-hook.html
index d0f2846a..7ed0ae8e 100644
--- a/course/responsiveness/use-dimension-hook.html
+++ b/course/responsiveness/use-dimension-hook.html
@@ -1,4 +1,4 @@
-Creating a useDimensions Hook
Creating a useDimensions Hook
The visualization component is going to be wrapped in a responsive div. We need a way to retrieve this container's dimensions.
To achieve this, let's create a hook called useDimensions.
Free
4 minutes read
What is a hook
A hook is a special function that lets you use state and other React features in functional components, as explained in the documentation.
That's exactly what we need! A function that we can reuse in all our chart components and that manages its internal state.
🎣 Hook to get the container dimensions
That's how the hook looks like:
import { useEffect, useLayoutEffect, useState } from "react";
+Creating a useDimensions Hook
Creating a useDimensions Hook
The visualization component is going to be wrapped in a responsive div. We need a way to retrieve this container's dimensions.
To achieve this, let's create a hook called useDimensions.
Free
4 minutes read
What is a hook
A hook is a special function that lets you use state and other React features in functional components, as explained in the documentation.
That's exactly what we need! A function that we can reuse in all our chart components and that manages its internal state.
🎣 Hook to get the container dimensions
That's how the hook looks like:
import { useEffect, useLayoutEffect, useState } from "react";
// Hook to retrieve the dimensions of a div.
// react-graph-gallery.com
@@ -27,4 +27,4 @@
}, []);
return dimensions;
-}
This hook is essentially a function that checks the offsetWidth and offsetHeight of a provided ref.
An event listener for the resize event is added to the window to ensure the dimensions are updated when the window size changes.
I do not want to go too much in depth on how it works. More importantly, let's see how to use it!
\ No newline at end of file
diff --git a/course/responsiveness/using-the-hook.html b/course/responsiveness/using-the-hook.html
index 4d4406ea..30d4f43f 100644
--- a/course/responsiveness/using-the-hook.html
+++ b/course/responsiveness/using-the-hook.html
@@ -1,7 +1,7 @@
-Integrating the Hook with Your Graph
Integrating the Hook with Your Graph
Our use-dimensions hook monitors changes in a div's dimensions and returns those values. Now, let’s use this hook to feed the dimensions directly into our graph!
Free
4 minutes read
1️⃣ Create a Ref
Begin by creating a ref with the React useRef() function.
A ref lets you directly target and interact with a specific HTML element in your app’s DOM. Remember, we covered this concept in the module on D3.js axes.
const chartRef = useRef(null);
Finally, pass the chartRef to the container you want to monitor.
return(
+Integrating the Hook with Your Graph
Integrating the Hook with Your Graph
Our use-dimensions hook monitors changes in a div's dimensions and returns those values. Now, let’s use this hook to feed the dimensions directly into our graph!
Free
4 minutes read
1️⃣ Create a Ref
Begin by creating a ref with the React useRef() function.
A ref lets you directly target and interact with a specific HTML element in your app’s DOM. Remember, we covered this concept in the module on D3.js axes.
const chartRef = useRef(null);
Finally, pass the chartRef to the container you want to monitor.
\ No newline at end of file
diff --git a/course/scales/introduction.html b/course/scales/introduction.html
index 07bd21bc..440562a2 100644
--- a/course/scales/introduction.html
+++ b/course/scales/introduction.html
@@ -1,5 +1,5 @@
-Introduction to scales
Introduction to scales
Building a graph requires to transform a dimension (a numeric variable in your dataset) in a position in pixels. This is done using a fundamental dataviz concept called scale.
Before implementing it in the next lesson, let's describe a bit what this crucial concept of dataviz is.
Free
4 minutes read
Test your intuition
Let's test your intuition with the following exercise.
1️⃣ You have a SVG area with a width of 500px.
2️⃣ You can place circles anywhere along this area horizontally.
3️⃣ You have a dataset with 5 values: 0, 50, 60, 82, 100
→ How do you position your circles to represent this dataset? Drag the circles below following your intuition:
Note: the number in each circle represents its value in the dataset.
How it actually works
The obvious part:
→ For a value of 0, the circle should be placed at the extreme left of the SVG. This corresponds to cx = 0px.
→ For the highest value in the dataset, 100, the circle should be positioned at the extreme right of the SVG. This corresponds to cx = width (i.e., 500px).
→ For a value of 50, which is the midpoint of our dataset, the circle should be positioned at the center of the SVG. This corresponds to cx = width / 2 (i.e., 250px).
The math part:
For a value of 82, which is not an exact midpoint, you need to calculate the position proportionally.
The position can be calculated as:
// Linear scale equation
+Introduction to scales
Introduction to scales
Building a graph requires to transform a dimension (a numeric variable in your dataset) in a position in pixels. This is done using a fundamental dataviz concept called scale.
Before implementing it in the next lesson, let's describe a bit what this crucial concept of dataviz is.
Free
4 minutes read
Test your intuition
Let's test your intuition with the following exercise.
1️⃣ You have a SVG area with a width of 500px.
2️⃣ You can place circles anywhere along this area horizontally.
3️⃣ You have a dataset with 5 values: 0, 50, 60, 82, 100
→ How do you position your circles to represent this dataset? Drag the circles below following your intuition:
Note: the number in each circle represents its value in the dataset.
How it actually works
The obvious part:
→ For a value of 0, the circle should be placed at the extreme left of the SVG. This corresponds to cx = 0px.
→ For the highest value in the dataset, 100, the circle should be positioned at the extreme right of the SVG. This corresponds to cx = width (i.e., 500px).
→ For a value of 50, which is the midpoint of our dataset, the circle should be positioned at the center of the SVG. This corresponds to cx = width / 2 (i.e., 250px).
The math part:
For a value of 82, which is not an exact midpoint, you need to calculate the position proportionally.
Manually calculating positions for each data point would be incredibly tedious for every graph you create.
Fortunately, d3.js provides a function called scaleLinear() that handles this task for you. In the next lesson, we'll explore how it works and simplifies your data visualization process.
Manually calculating positions for each data point would be incredibly tedious for every graph you create.
Fortunately, d3.js provides a function called scaleLinear() that handles this task for you. In the next lesson, we'll explore how it works and simplifies your data visualization process.
\ No newline at end of file
diff --git a/course/scales/linear-scale.html b/course/scales/linear-scale.html
index a93df390..f8b613f6 100644
--- a/course/scales/linear-scale.html
+++ b/course/scales/linear-scale.html
@@ -1,4 +1,4 @@
-Linear Scales
Linear Scales
The previous lesson described the concept of scale in data visualization. Scales allow, for instance, to translate a value in our dataset to a position on the screen.
Now, let's study the most common scale type and its d3.js implementation: the linear scale and its scaleLinear() function.
Free
4 minutes read
The scaleLinear() function
The scaleLinear() function is part of the d3-scale module of d3.js.
It expects 2 inputs: a domain and a range.
🏠 Domain
Usually an array of length 2. It provides the min and the max of the values we have in the dataset.
📏 Range
Usually an array of length 2. It provides the start and the end of the positions we are targeting in pixel.
The output is a function that takes a single argument. You provide a value from the domain, and it returns the corresponding value from the range.
Let's create a scale to address the issue with the green circles above!
+Linear Scales
Linear Scales
The previous lesson described the concept of scale in data visualization. Scales allow, for instance, to translate a value in our dataset to a position on the screen.
Now, let's study the most common scale type and its d3.js implementation: the linear scale and its scaleLinear() function.
Free
4 minutes read
The scaleLinear() function
The scaleLinear() function is part of the d3-scale module of d3.js.
It expects 2 inputs: a domain and a range.
🏠 Domain
Usually an array of length 2. It provides the min and the max of the values we have in the dataset.
📏 Range
Usually an array of length 2. It provides the start and the end of the positions we are targeting in pixel.
The output is a function that takes a single argument. You provide a value from the domain, and it returns the corresponding value from the range.
Let's create a scale to address the issue with the green circles above!
\ No newline at end of file
diff --git a/course/scales/other-scale-types.html b/course/scales/other-scale-types.html
index dd698389..24029d8d 100644
--- a/course/scales/other-scale-types.html
+++ b/course/scales/other-scale-types.html
@@ -1,4 +1,4 @@
-Other scale types
Other scale types
While scaleLinear is one of the most commonly used D3 scales, other scale types are essential for creating even basic charts.
Let's explore scaleBand, scaleOrdinal, and other indispensable functions!
Free
4 minutes read
scaleBand() to create... bands 🙂
scaleBand() is ideal for categorical variables as it allocates equal space for each category, ensuring that every discrete value is uniformly represented on the axis.
Example
You want to create a horizontal bar chart with 3 bars in a figure that is 240 pixels high. You want to dedicate 33% of the total height for white space: this is the padding between bars.
You'll end up with the dimensions outlined below:
All the numbers produced by the scaleBand() function.
You can easily compute these values using the scaleBand() function!
→
There are many ways to control the padding. The official doc explains very well!
The domain is an array that lists the groups.
The range is a two-element array that specifies the pixel positions where the shapes will be drawn.
The optional padding method is expressed as a fraction of the band width.
const yScale = d3.scaleBand()
+Other scale types
Other scale types
While scaleLinear is one of the most commonly used D3 scales, other scale types are essential for creating even basic charts.
Let's explore scaleBand, scaleOrdinal, and other indispensable functions!
Free
4 minutes read
scaleBand() to create... bands 🙂
scaleBand() is ideal for categorical variables as it allocates equal space for each category, ensuring that every discrete value is uniformly represented on the axis.
Example
You want to create a horizontal bar chart with 3 bars in a figure that is 240 pixels high. You want to dedicate 33% of the total height for white space: this is the padding between bars.
You'll end up with the dimensions outlined below:
All the numbers produced by the scaleBand() function.
You can easily compute these values using the scaleBand() function!
→
There are many ways to control the padding. The official doc explains very well!
The domain is an array that lists the groups.
The range is a two-element array that specifies the pixel positions where the shapes will be drawn.
The optional padding method is expressed as a fraction of the band width.
Now that this scale is available, let's use it to get all the positions we need! Note that the yScale function we now have returns the start of the corresponding band:
// Top of each bar:
@@ -12,4 +12,4 @@
.range(["red", "green", "blue"]) // I want to assign 3 colors to them
colorScale("a") // --> red!
-colorScale("b") // --> green
More Scale Types to Explore!
The d3-scale module includes 13 different types of scales.
However, they all follow the same core pattern. Rather than covering each one individually now, we'll explore them throughout the course as they come up.
By now, you should have a solid understanding of how scales work.
While I don’t recommend it, you can always check out the full list of scale types in the official documentation if you're curious!
Exercices
It's taking shape! 🎉
You've now mastered two fundamental concepts of dataviz with React and D3: SVG and Scales. This means we're ready to build actual graphs!
In the next lesson, you'll dive into a hands-on exercise where we recreate a real-world chart using everything you've learned so far.
\ No newline at end of file
+colorScale("b") // --> green
More Scale Types to Explore!
The d3-scale module includes 13 different types of scales.
However, they all follow the same core pattern. Rather than covering each one individually now, we'll explore them throughout the course as they come up.
By now, you should have a solid understanding of how scales work.
While I don’t recommend it, you can always check out the full list of scale types in the official documentation if you're curious!
Exercices
It's taking shape! 🎉
You've now mastered two fundamental concepts of dataviz with React and D3: SVG and Scales. This means we're ready to build actual graphs!
In the next lesson, you'll dive into a hands-on exercise where we recreate a real-world chart using everything you've learned so far.
\ No newline at end of file
diff --git a/course/scales/project.html b/course/scales/project.html
index aca19995..ad729f7a 100644
--- a/course/scales/project.html
+++ b/course/scales/project.html
@@ -1,4 +1,4 @@
-Project
Project
We've built a solid foundation in D3, SVG, and scales! Now, let's put that knowledge to the test by recreating a barplot inspired by The Economist.
Free
4 minutes read
Our Objective
In this lesson, we aim to recreate a chart from The Economist with several key design elements:
Title, subtitle, and footer
Grid lines with values displayed at the top
Inline labels placed inside or outside the bars
Escape artists Number of laboratory-acquired infections, 1970-2021
Sources: Laboratory-Acquired Infection Database; American Biological Safety Association The Economist
The data
The dataset is very simple! It looks like this:
export const data = [
+Project
Project
We've built a solid foundation in D3, SVG, and scales! Now, let's put that knowledge to the test by recreating a barplot inspired by The Economist.
Free
4 minutes read
Our Objective
In this lesson, we aim to recreate a chart from The Economist with several key design elements:
Title, subtitle, and footer
Grid lines with values displayed at the top
Inline labels placed inside or outside the bars
Escape artists Number of laboratory-acquired infections, 1970-2021
Sources: Laboratory-Acquired Infection Database; American Biological Safety Association The Economist
\ No newline at end of file
diff --git a/course/svg/d3-shape.html b/course/svg/d3-shape.html
index f0ab357a..18f87000 100644
--- a/course/svg/d3-shape.html
+++ b/course/svg/d3-shape.html
@@ -1,4 +1,4 @@
-Building shapes with d3
Building shapes with d3
Some shapes can be quite complex—like rings, slices, paths, polygons, and curves. Manually creating the d attribute for these shapes would be a nightmare!
Fortunately, D3 provides a module called d3-shape specifically for handling all these shapes. Let’s explore how it can simplify our work.
Free
4 minutes read
Building the line you need
In the previous lesson we introduced the path element, and showed that a line chart is basically rendered this way:
<svg height="300" width="400">
+Building shapes with d3
Building shapes with d3
Some shapes can be quite complex—like rings, slices, paths, polygons, and curves. Manually creating the d attribute for these shapes would be a nightmare!
Fortunately, D3 provides a module called d3-shape specifically for handling all these shapes. Let’s explore how it can simplify our work.
Free
4 minutes read
Building the line you need
In the previous lesson we introduced the path element, and showed that a line chart is basically rendered this way:
<svg height="300" width="400">
<path
d="M0 105 L100 200 L200 200 L300 20 L350 120"
fill="none"
@@ -14,4 +14,4 @@
// Use the function, providing it the 2 paramters
multiply(4, 6))
-// 24
But that's how d3.js works, and I promise you will soon be used to it!
Other helpers
The d3.line() function is one of the most versatile in d3, enabling the creation of key chart types like line charts, lollipop plots, and even radar charts.
However, you can’t use it to create a donut chart! For that, you’ll need to draw arcs, and d3 provides a specific helper for this: d3.arc().
The d3-shape module includes 15 functions in total. Here's a diagram that highlights the most important ones, which can help you build about 80% of chart types!
Main functions of the d3-shape module
Describing each of these functions in detail one by one would be tedious. Instead, we’ll introduce them gradually throughout the course.
What’s crucial to remember is that they all share a common structure: path generators, method chaining, and accessor functions are key concepts you'll need to fully grasp.
But that's how d3.js works, and I promise you will soon be used to it!
Other helpers
The d3.line() function is one of the most versatile in d3, enabling the creation of key chart types like line charts, lollipop plots, and even radar charts.
However, you can’t use it to create a donut chart! For that, you’ll need to draw arcs, and d3 provides a specific helper for this: d3.arc().
The d3-shape module includes 15 functions in total. Here's a diagram that highlights the most important ones, which can help you build about 80% of chart types!
Main functions of the d3-shape module
Describing each of these functions in detail one by one would be tedious. Instead, we’ll introduce them gradually throughout the course.
What’s crucial to remember is that they all share a common structure: path generators, method chaining, and accessor functions are key concepts you'll need to fully grasp.
\ No newline at end of file
diff --git a/course/svg/introduction.html b/course/svg/introduction.html
index edbc1a83..6a5df8e5 100644
--- a/course/svg/introduction.html
+++ b/course/svg/introduction.html
@@ -1 +1 @@
-Introduction to svg
Introduction to svg
At its core, a graph is just a bunch of shapes such as circles, rectangles, and lines displayed on a screen. Using standard HTML elements to draw these shapes would be cumbersome and limiting.
Fortunately, there's a more efficient solution: SVG. SVG is a versatile and widely-used technology that allows us to draw shapes in the browser with precision and ease.
In this module, we'll explore how SVG works and how it can be harnessed to create dynamic and insightful data visualizations.
Free
4 minutes read
What is SVG
Scalable Vector Graphics (SVG) is an XML-based format for vector graphics. Unlike raster graphics which are made up of pixels, SVG uses vector data (points, lines, and curves) to create images. This means SVG images are resolution-independent and can scale up or down without losing quality.
Anatomy of a React app with a graph built in a svg area.
Most simple example
Here is a very simple react app that uses SVG to render a circle:
A react component called SvgCircle is defined in a file called SvgCircle.tsx.
It renders an SVG area thank to a <svg> HTML element. It has some width and height
In this SVG area, a <circle> is drawn. cx and cy set the position of the circle. r its radius.
Awesome! 🔆
With just a few more circles like this, we'll have a complete scatterplot!
Note: The coordinate system of SVG starts at the top-left corner, where the origin point (0,0) is located.
Coordinates are upside down 🙃
SVG uses a coordinate system where the origin (0, 0) is at the top-left corner of the canvas.
As you move right, the x values increase, just like you'd expect. But here's the catch: the y values increase as you move down instead of up, which is different from typical Cartesian coordinates.
So, it's like the coordinate system is upside down for the y axis.
SVG coordinates are upside-down: 0,0 is at the top left!
Benefits
Pros
Scalability: SVG images maintain their quality at any size, making them ideal for responsive design. Whether viewed on a small mobile screen or a large desktop monitor, SVG graphics look crisp and clear.
Interactivity: SVG elements can be styled and animated using CSS and JavaScript, allowing for dynamic and interactive graphics. This makes SVG a powerful tool for creating engaging data visualizations.
Accessibility: Text within SVG is searchable and accessible by screen readers. This enhances the accessibility of web content, making it more usable for people with disabilities.
Usability: SVG elements are easy to manipulate, allowing you to create complex shapes from simple ones with ease.
Limitations
Cons
SVG has performance limitations, especially when used for data visualization.
→
💡 DOM = Document Object Model. It is basically list that shows all the elements of a webpage and how they're arranged.
SVG graphs are defined in the DOM. Each SVG element increases the number of DOM nodes. If you create a scatterplot with 1 million circles, it will make the make the DOM very heavy.
As a result, the browser may become slow and unresponsive due to the increased workload of rendering and managing numerous SVG elements. This can lead to performance bottlenecks, particularly on devices with limited processing power or memory.
At its core, a graph is just a bunch of shapes such as circles, rectangles, and lines displayed on a screen. Using standard HTML elements to draw these shapes would be cumbersome and limiting.
Fortunately, there's a more efficient solution: SVG. SVG is a versatile and widely-used technology that allows us to draw shapes in the browser with precision and ease.
In this module, we'll explore how SVG works and how it can be harnessed to create dynamic and insightful data visualizations.
Free
4 minutes read
What is SVG
Scalable Vector Graphics (SVG) is an XML-based format for vector graphics. Unlike raster graphics which are made up of pixels, SVG uses vector data (points, lines, and curves) to create images. This means SVG images are resolution-independent and can scale up or down without losing quality.
Anatomy of a React app with a graph built in a svg area.
Most simple example
Here is a very simple react app that uses SVG to render a circle:
A react component called SvgCircle is defined in a file called SvgCircle.tsx.
It renders an SVG area thank to a <svg> HTML element. It has some width and height
In this SVG area, a <circle> is drawn. cx and cy set the position of the circle. r its radius.
Awesome! 🔆
With just a few more circles like this, we'll have a complete scatterplot!
Note: The coordinate system of SVG starts at the top-left corner, where the origin point (0,0) is located.
Coordinates are upside down 🙃
SVG uses a coordinate system where the origin (0, 0) is at the top-left corner of the canvas.
As you move right, the x values increase, just like you'd expect. But here's the catch: the y values increase as you move down instead of up, which is different from typical Cartesian coordinates.
So, it's like the coordinate system is upside down for the y axis.
SVG coordinates are upside-down: 0,0 is at the top left!
Benefits
Pros
Scalability: SVG images maintain their quality at any size, making them ideal for responsive design. Whether viewed on a small mobile screen or a large desktop monitor, SVG graphics look crisp and clear.
Interactivity: SVG elements can be styled and animated using CSS and JavaScript, allowing for dynamic and interactive graphics. This makes SVG a powerful tool for creating engaging data visualizations.
Accessibility: Text within SVG is searchable and accessible by screen readers. This enhances the accessibility of web content, making it more usable for people with disabilities.
Usability: SVG elements are easy to manipulate, allowing you to create complex shapes from simple ones with ease.
Limitations
Cons
SVG has performance limitations, especially when used for data visualization.
→
💡 DOM = Document Object Model. It is basically list that shows all the elements of a webpage and how they're arranged.
SVG graphs are defined in the DOM. Each SVG element increases the number of DOM nodes. If you create a scatterplot with 1 million circles, it will make the make the DOM very heavy.
As a result, the browser may become slow and unresponsive due to the increased workload of rendering and managing numerous SVG elements. This can lead to performance bottlenecks, particularly on devices with limited processing power or memory.
\ No newline at end of file
diff --git a/course/svg/main-svg-elements.html b/course/svg/main-svg-elements.html
index cbf2e18b..54ada220 100644
--- a/course/svg/main-svg-elements.html
+++ b/course/svg/main-svg-elements.html
@@ -1 +1 @@
-Main SVG elements
Main SVG elements
In the previous lesson, we learned about SVG and how to draw a circle with it.
To create comprehensive graphs, we'll also need other shapes such as rectangles, text, and lines. Let's explore how to create these as well.
Free
4 minutes read
Rectangle
SVG rectangles are created using the <rect> element, which requires the position and size attributes. The x and y attributes specify the coordinates of the top-left corner, while width and height determine the size of the rectangle.
For example, this code will draw a blue rectangle with the top-left corner at (10,10) and dimensions of 80x50.
Here is an interactive example so that you can play a bit with lines and understand how they work.
Text
SVG text is created using the <text> element, which requires coordinates to specify where the text should be placed. The x and y attributes define the position of the starting point of the text.
For example, this code will display the text "Hello, SVG!" at the coordinates (50,50).
Here is an interactive example so that you can experiment with text and understand how it works.
Circle
We talked about them already, but remember that SVG circles are created using the <circle> element, which requires the center coordinates and radius as attributes.
The cx and cy attributes specify the center point, while the r attribute determines the radius of the circle.
For example, this code will draw a red circle with a center at (50,50) and a radius of 40.
<circle cx="50" cy="50" r="40" fill="red" />
Here is an interactive example so that you can experiment with circles and understand how they work.
In the previous lesson, we learned about SVG and how to draw a circle with it.
To create comprehensive graphs, we'll also need other shapes such as rectangles, text, and lines. Let's explore how to create these as well.
Free
4 minutes read
Rectangle
SVG rectangles are created using the <rect> element, which requires the position and size attributes. The x and y attributes specify the coordinates of the top-left corner, while width and height determine the size of the rectangle.
For example, this code will draw a blue rectangle with the top-left corner at (10,10) and dimensions of 80x50.
Here is an interactive example so that you can play a bit with lines and understand how they work.
Text
SVG text is created using the <text> element, which requires coordinates to specify where the text should be placed. The x and y attributes define the position of the starting point of the text.
For example, this code will display the text "Hello, SVG!" at the coordinates (50,50).
Here is an interactive example so that you can experiment with text and understand how it works.
Circle
We talked about them already, but remember that SVG circles are created using the <circle> element, which requires the center coordinates and radius as attributes.
The cx and cy attributes specify the center point, while the r attribute determines the radius of the circle.
For example, this code will draw a red circle with a center at (50,50) and a radius of 40.
<circle cx="50" cy="50" r="40" fill="red" />
Here is an interactive example so that you can experiment with circles and understand how they work.
\ No newline at end of file
diff --git a/course/svg/path-element.html b/course/svg/path-element.html
index 68231f7a..ca797a60 100644
--- a/course/svg/path-element.html
+++ b/course/svg/path-element.html
@@ -1,7 +1,7 @@
-The path element
The path element
In this lesson, we'll dive into one of the most versatile and powerful elements in SVG: the <path> element.
The <path> element allows you to create complex shapes and lines that go beyond the basic geometric shapes. It is essential to build some graph types like an area chart or a line chart.
Free
4 minutes read
Most basic example
The <path> element allows to draw literally any shape in the SVG area.
You provide it with a series of drawing commands that will make straight or curved lines, resulting in your final shape.
Let's check a basic example:
<svg height="300" width="400">
+The path element
The path element
In this lesson, we'll dive into one of the most versatile and powerful elements in SVG: the <path> element.
The <path> element allows you to create complex shapes and lines that go beyond the basic geometric shapes. It is essential to build some graph types like an area chart or a line chart.
Free
4 minutes read
Most basic example
The <path> element allows to draw literally any shape in the SVG area.
You provide it with a series of drawing commands that will make straight or curved lines, resulting in your final shape.
As you can see the <path> element expect a required d attribute. This attribute defines the shape of the path. Let's discover its syntax.
Understanding the d Attribute
The d attribute defines the shape and outline of the path by specifying a series of drawing commands.
Each command consists of a letter that represents the type of drawing action (such as M for Move To) followed by one or more numbers that define the coordinates or control points.
Here is a breakdown of the svg path above:
→
Remember that the coordinate system starts from the top left in svg!
M0 105: moves the starting point of the path to coordinates 0, 105. (M stands for move to)
L100 200: draws a straight line from the current point (0, 105) to the point (100, 200). (L stands for line to)
200 200: draws a new straight line from the current point to the point (200, 200).
Now, experiment with some changes in the sandbox below to get a better understanding of how it works:
Not only lines!
The path element can create a wide variety of shapes, including the most complex ones.
Two particularly useful features are the Bézier curve (C) and the arc curve (A). Check how they work with those 2 diagrams:
Ok, that's definitely not easy to grasp.
In the sandbox below, try to play with the numbers, and see what happens.
Exercises
The good news 🎁
You won’t have to manually write the d attribute yourself. 🎉
While it’s helpful to understand its role, in practice, you’ll rely on d3.js helper functions to generate paths for you.
These functions are all part of a d3 module called d3-shape. They follow a consistent pattern: taking your data and converting it into the appropriate path d string.
The d3-shape module provides functions that generate SVG paths from a dataset. It’s a core component of d3.js!
The d3-shape module is one of the most useful in d3.js! 💙
As you can see the <path> element expect a required d attribute. This attribute defines the shape of the path. Let's discover its syntax.
Understanding the d Attribute
The d attribute defines the shape and outline of the path by specifying a series of drawing commands.
Each command consists of a letter that represents the type of drawing action (such as M for Move To) followed by one or more numbers that define the coordinates or control points.
Here is a breakdown of the svg path above:
→
Remember that the coordinate system starts from the top left in svg!
M0 105: moves the starting point of the path to coordinates 0, 105. (M stands for move to)
L100 200: draws a straight line from the current point (0, 105) to the point (100, 200). (L stands for line to)
200 200: draws a new straight line from the current point to the point (200, 200).
Now, experiment with some changes in the sandbox below to get a better understanding of how it works:
Not only lines!
The path element can create a wide variety of shapes, including the most complex ones.
Two particularly useful features are the Bézier curve (C) and the arc curve (A). Check how they work with those 2 diagrams:
Ok, that's definitely not easy to grasp.
In the sandbox below, try to play with the numbers, and see what happens.
Exercises
The good news 🎁
You won’t have to manually write the d attribute yourself. 🎉
While it’s helpful to understand its role, in practice, you’ll rely on d3.js helper functions to generate paths for you.
These functions are all part of a d3 module called d3-shape. They follow a consistent pattern: taking your data and converting it into the appropriate path d string.
The d3-shape module provides functions that generate SVG paths from a dataset. It’s a core component of d3.js!
The d3-shape module is one of the most useful in d3.js! 💙
\ No newline at end of file
diff --git a/course/svg/tips-and-tricks.html b/course/svg/tips-and-tricks.html
index cfce86b4..c7ca9cae 100644
--- a/course/svg/tips-and-tricks.html
+++ b/course/svg/tips-and-tricks.html
@@ -1,7 +1,7 @@
-SVG tips & tricks
SVG tips & tricks
By now, SVG might seem straightforward, but trust me, it has its quirks that can make your data visualization journey challenging.
In this lesson, I'll share some tips and tricks that can save yo hours of frustration — lessons I learned the hard way.
Wip
4 minutes read
1️⃣ SVG Elements Don’t Have a Background Color
Unlike HTML elements, SVG elements do not support background colors directly. They also do not support borders.
If you want to create a background or add a border to your SVG, you need to draw a rectangle that covers the desired area. CSS properties like background-color or fill or border on the SVG element itself are not recognized and will be ignored.
Example:
2️⃣ Text Alignment in SVG
Text alignment in SVG works differently compared to HTML. You can control both horizontal and vertical alignment using the text-anchor and alignment-baseline properties, which are unique to SVG.
→
Remember, in JSX, CSS properties should be camelCased (e.g., textAnchor instead of text-anchor).
Here’s a summary of how they work:
text-anchor controls the horizontal alignment →
alignment-baseline controls the vertical alignment →
3️⃣ Grouping Elements with <g>
The <g> element in SVG is used to group multiple elements together.
This is especially useful for applying transformations, styles, or events to a collection of elements as a single unit.
By now, SVG might seem straightforward, but trust me, it has its quirks that can make your data visualization journey challenging.
In this lesson, I'll share some tips and tricks that can save yo hours of frustration — lessons I learned the hard way.
Wip
4 minutes read
1️⃣ SVG Elements Don’t Have a Background Color
Unlike HTML elements, SVG elements do not support background colors directly. They also do not support borders.
If you want to create a background or add a border to your SVG, you need to draw a rectangle that covers the desired area. CSS properties like background-color or fill or border on the SVG element itself are not recognized and will be ignored.
Example:
2️⃣ Text Alignment in SVG
Text alignment in SVG works differently compared to HTML. You can control both horizontal and vertical alignment using the text-anchor and alignment-baseline properties, which are unique to SVG.
→
Remember, in JSX, CSS properties should be camelCased (e.g., textAnchor instead of text-anchor).
Here’s a summary of how they work:
text-anchor controls the horizontal alignment →
alignment-baseline controls the vertical alignment →
3️⃣ Grouping Elements with <g>
The <g> element in SVG is used to group multiple elements together.
This is especially useful for applying transformations, styles, or events to a collection of elements as a single unit.
In SVG, the concepts of stroke, fill, and color work differently than in standard HTML.
The fill property controls the interior color of shapes, while stroke affects the outline. Unlike div elements, SVG shapes don’t have separate properties for borders and backgrounds; instead, you use stroke and fill to control these aspects.
For text elements, avoid using stroke to outline text, as it can result in poor readability. Instead, focus on using fill for color and text-anchor for alignment.
5️⃣ CSS Specificity in SVG
CSS specificity in SVG can be a bit more complex than in HTML. Styles applied to SVG elements can come from inline styles, internal stylesheets, or external stylesheets, and the rules of specificity still apply. However, because SVG elements are often nested and grouped, understanding which styles take precedence requires a good grasp of CSS specificity rules.
6️⃣ Text Wrapping
In SVG, there is no built-in functionality for automatic text wrapping like you would find in HTML or CSS. 😱
You have to manage text wrapping manually or use JavaScript libraries to handle it.
This is very very annoying. In practice, we'll often use a HTML layer on top of the SVG layer to add text (like in tooltips) to make our life simpler.
7️⃣ SVG Dimensions: The Impact of “100%”
Setting SVG dimensions to "100%" can lead to unexpected results, especially in responsive designs. SVGs can scale based on their container, but how they scale depends on the viewBox and preserveAspectRatio attributes. Understanding these attributes is key to ensuring your SVGs display correctly across different screen sizes. For more details, refer to our module on responsiveness.
8️⃣ Filters, Blur Effects, and Gradients
SVG offers powerful capabilities for applying visual effects, such as filters and gradients. Filters like blur, drop-shadow, and grayscale can add depth and dimension to your graphics. Gradients allow for smooth transitions between colors, which can be applied to fills or strokes, adding richness to your visualizations.
9️⃣ Stacking Order Matters
In SVG, elements are rendered in the order they appear in the markup, creating a natural stacking order. This means that the order of elements in your SVG code affects their visual layering. Elements later in the code will appear on top of earlier ones, so careful planning is needed when layering elements to achieve the desired visual effect.
🔟 Dealing with Blurry SVG Elements
SVG elements can sometimes appear blurry, especially when scaled. This is often due to anti-aliasing, which can smooth edges but also cause them to lose sharpness. To fix this, you can use the shape-rendering="crispEdges" property to make edges appear sharper, especially for pixel-perfect designs.
In SVG, the concepts of stroke, fill, and color work differently than in standard HTML.
The fill property controls the interior color of shapes, while stroke affects the outline. Unlike div elements, SVG shapes don’t have separate properties for borders and backgrounds; instead, you use stroke and fill to control these aspects.
For text elements, avoid using stroke to outline text, as it can result in poor readability. Instead, focus on using fill for color and text-anchor for alignment.
5️⃣ CSS Specificity in SVG
CSS specificity in SVG can be a bit more complex than in HTML. Styles applied to SVG elements can come from inline styles, internal stylesheets, or external stylesheets, and the rules of specificity still apply. However, because SVG elements are often nested and grouped, understanding which styles take precedence requires a good grasp of CSS specificity rules.
6️⃣ Text Wrapping
In SVG, there is no built-in functionality for automatic text wrapping like you would find in HTML or CSS. 😱
You have to manage text wrapping manually or use JavaScript libraries to handle it.
This is very very annoying. In practice, we'll often use a HTML layer on top of the SVG layer to add text (like in tooltips) to make our life simpler.
7️⃣ SVG Dimensions: The Impact of “100%”
Setting SVG dimensions to "100%" can lead to unexpected results, especially in responsive designs. SVGs can scale based on their container, but how they scale depends on the viewBox and preserveAspectRatio attributes. Understanding these attributes is key to ensuring your SVGs display correctly across different screen sizes. For more details, refer to our module on responsiveness.
8️⃣ Filters, Blur Effects, and Gradients
SVG offers powerful capabilities for applying visual effects, such as filters and gradients. Filters like blur, drop-shadow, and grayscale can add depth and dimension to your graphics. Gradients allow for smooth transitions between colors, which can be applied to fills or strokes, adding richness to your visualizations.
9️⃣ Stacking Order Matters
In SVG, elements are rendered in the order they appear in the markup, creating a natural stacking order. This means that the order of elements in your SVG code affects their visual layering. Elements later in the code will appear on top of earlier ones, so careful planning is needed when layering elements to achieve the desired visual effect.
🔟 Dealing with Blurry SVG Elements
SVG elements can sometimes appear blurry, especially when scaled. This is often due to anti-aliasing, which can smooth edges but also cause them to lose sharpness. To fix this, you can use the shape-rendering="crispEdges" property to make edges appear sharper, especially for pixel-perfect designs.
\ No newline at end of file
diff --git a/cross-graph-highlight-interaction.html b/cross-graph-highlight-interaction.html
index 549af032..c6f8284d 100644
--- a/cross-graph-highlight-interaction.html
+++ b/cross-graph-highlight-interaction.html
@@ -1,4 +1,4 @@
-Cross graph highlight interaction
Cross graph highlight interaction
This post explains how to add cross graph interactions in a react app. It focus on performance, showing how a naive approach with a shared state leads to a disappointing result and how a react context can solve the problem.
Disclaimer: the concepts explained here where showed to me by my colleague Gabriel Vergnaud.
What is cross graph interaction
What it is.
working example
Why do we care?
Focus on performance
Naive solution: shared state
A first solution: a shared state. You defined a react state at the level of the component that wraps all your viz with useState:
You then pass the state and the setter function to each viz. Something like:
<Barplot
+Cross graph highlight interaction
Cross graph highlight interaction
This post explains how to add cross graph interactions in a react app. It focus on performance, showing how a naive approach with a shared state leads to a disappointing result and how a react context can solve the problem.
Disclaimer: the concepts explained here where showed to me by my colleague Gabriel Vergnaud.
What is cross graph interaction
What it is.
working example
Why do we care?
Focus on performance
Naive solution: shared state
A first solution: a shared state. You defined a react state at the level of the component that wraps all your viz with useState:
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/dataset-transition.html b/dataset-transition.html
index e75617f6..3a2e94b8 100644
--- a/dataset-transition.html
+++ b/dataset-transition.html
@@ -1,4 +1,4 @@
-Smooth dataset transition
Animated dataset transition
How to smoothly transition from a dataset to the other. Use the react-spring library. Several examples with explanation. Start simple. Then focus on tricky things like enter, exit, axis, path.
A minimal react-spring dataviz example. A viz component expect a position and a color prop. But it smoothly animates the transition.
Most basic
Consider a change where dataset has the same structure, just the values change. Same number of items.
How to smoothly transition from a dataset to the other. Use the react-spring library. Several examples with explanation. Start simple. Then focus on tricky things like enter, exit, axis, path.
A minimal react-spring dataviz example. A viz component expect a position and a color prop. But it smoothly animates the transition.
Most basic
Consider a change where dataset has the same structure, just the values change. Same number of items.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/dendrogram.html b/dendrogram.html
index cce03ed8..974f16d5 100644
--- a/dendrogram.html
+++ b/dendrogram.html
@@ -1,4 +1,4 @@
-Dendrogram | The React Graph Gallery
Dendrogram
A dendrogram is a network structure. It is constituted of a root node that gives birth to several nodes connected by edges or branches. The last nodes of the hierarchy are called leaves.
This page explains how to build a dendrogram using d3.js to compute the node position, and React to render the nodes and edges. It starts by describing the required data format, explains how to build a very basicdendrogram and then shows how to customize it.
The dataset describes a hierarchy using a recursive structure.
Each item in this structure is called a node. The lowest nodes of the hierarchy being called leaves. The dataset is an object that has at least 3 properties: name, value and children. Children is an array of nodes that have this structure too.
Here is a minimal example of the data structure:
const data = {
+Dendrogram | The React Graph Gallery
Dendrogram
A dendrogram is a network structure. It is constituted of a root node that gives birth to several nodes connected by edges or branches. The last nodes of the hierarchy are called leaves.
This page explains how to build a dendrogram using d3.js to compute the node position, and React to render the nodes and edges. It starts by describing the required data format, explains how to build a very basicdendrogram and then shows how to customize it.
The dataset describes a hierarchy using a recursive structure.
Each item in this structure is called a node. The lowest nodes of the hierarchy being called leaves. The dataset is an object that has at least 3 properties: name, value and children. Children is an array of nodes that have this structure too.
Edges are not horizontal anymore, so the linkHorizontal won't be helpful this time. But instead, the d3.linkRadial function does the job based on an angle and a distance.
→ Smart labels
Please make sure your labels are properly oriented. It always give a bit of a headhache to pivot them correctly, and to control the anchoring appropriately. I talked about it extensively in the circular barplot section so please take a look for this matter.
A minimalist radial dendrogram built using d3 and react.
Note: please check of the first level edges are straight lines. IMO it does not make sense to use linkRadial for the first level.
Coming next
There is much more that needs to be added to this tutorial.
Using canvas for rendering is often a requirement when the number of nodes gets big. Interactivity is often necessary, for hover effect or to collapse a part of the tree. It also possible to map the node circle size to a numeric variable.
This will come soon! I have a newsletter called the dataviz universe where I share my latest updates.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/density-plot.html b/density-plot.html
index a0f7abef..cb670282 100644
--- a/density-plot.html
+++ b/density-plot.html
@@ -1,4 +1,4 @@
-Density chart with React
Density chart with React and d3.js
A density plot is a chart type that shows the distribution of a numeric variable. This page is a step-by-step guide on how to build your own density plot for the web, using React and D3.js.
It starts by describing how the data should be organized and how to initialize the density component. It then explains how to compute a kernel density. Once this is done, it shows how to render the density shape and suggests a few variations. 🙇♂️.
Building a density chart only requires a set of numeric values.
As a result, the dataset is pretty simple: just an array of number.
Here is a minimal example of the data structure:
export const data = [
+Density chart with React
Density chart with React and d3.js
A density plot is a chart type that shows the distribution of a numeric variable. This page is a step-by-step guide on how to build your own density plot for the web, using React and D3.js.
It starts by describing how the data should be organized and how to initialize the density component. It then explains how to compute a kernel density. Once this is done, it shows how to render the density shape and suggests a few variations. 🙇♂️.
I'm in the process of writing a complete blog post on the topic. Subscribe to the project to know when it's ready.
Density inspiration
If you're looking for inspiration to create your next Density, note that dataviz-inspiration.com showcases many examples. Definitely the best place to get ... inspiration!
dataviz-inspiration.com showcases hundreds of stunning dataviz projects. Have a look to get some ideas on how to make your Density looks good!
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/donut.html b/donut.html
index 86b675c8..a4c5dd9b 100644
--- a/donut.html
+++ b/donut.html
@@ -1,4 +1,4 @@
-Donut chart | The React Graph Gallery
Donut chart
The donut chart is a very common yet criticized way to represent the value of a few groups in a dataset. It is very close to the pie chart and thus suffers the same downsides.
This page explains how to build a donut chart using d3.js and React. It starts with a basic example and then focus on customization like legends, hover effect and dataset transition.
The dataset required to build a donut chart is an array where each item represents a group. Each item is an object with 2 properties. They provide the group name (name) and its value (value).
For instance, here is the dataset used for the simple donut chart below:
const data = [
+Donut chart | The React Graph Gallery
Donut chart
The donut chart is a very common yet criticized way to represent the value of a few groups in a dataset. It is very close to the pie chart and thus suffers the same downsides.
This page explains how to build a donut chart using d3.js and React. It starts with a basic example and then focus on customization like legends, hover effect and dataset transition.
The dataset required to build a donut chart is an array where each item represents a group. Each item is an object with 2 properties. They provide the group name (name) and its value (value).
For instance, here is the dataset used for the simple donut chart below:
I'm in the process of writing a complete blog post on the topic. Subscribe to the project to know when it's ready.
Donut inspiration
If you're looking for inspiration to create your next Donut, note that dataviz-inspiration.com showcases many examples. Definitely the best place to get ... inspiration!
dataviz-inspiration.com showcases hundreds of stunning dataviz projects. Have a look to get some ideas on how to make your Donut looks good!
On the graph below, hovering over a slice will smoothly highlight it, giving a nice polish touch to the widget.
The process is quickly described on the pie chart hover effect section. But hover effect is a topic on itself. As a result, I plan to write a full tutorial targeting this topic only. You can subscribe to know when it's ready!
Meanwhile, here is a donut chart with a hover effect, together with its React code:
A donut chart with clean inline legends, built thanks to the centroid function of d3.js.
Data transition
The Pie component expects a data prop. What should we do when this data changes?
By default, the chart will update instantly, with no transition. Adding a smooth transition gives a nice polish touch to the graph. Try to switch between the 2 datasets below to see the animation in action.
The code below relies on the react-spring library. Instead of rendering a path for each slice, it uses a animated.path component that handles the spring animation for us.
The implementation is not trivial. I plan to publish a full tutorial on react-spring for data visualization soon. You can subscribe here to be notified when it is ready.
A donut chart with clean inline legends, built thanks to the centroid function of d3.js.
Note: check the blue group that appears / disappears between dataset. This kind of enter/exit pattern is something to keep in mind when building animations.
Pie chart to barplot
Pie charts are often criticized since angles are hard to read. Let's represent the same data using a pie chart or a barplot, to see what's the most insightful 🤷♂️.
Note that here we animate the transition between different shape types: each arc becomes a rectangle and reciprocally. This is made possible thanks to the flubber library, used in coordination with react-spring.
Once more, a full tutorial is needed here. You can subscribe here to be notified when it is ready. In the meanwhile, the code of this specific example is provided below.
Transition from a pie chart to a barplot with a smooth animation using the buttons on top.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/example/arc-diagram-vertical.html b/example/arc-diagram-vertical.html
index f20a055d..ebfee8f5 100644
--- a/example/arc-diagram-vertical.html
+++ b/example/arc-diagram-vertical.html
@@ -1,4 +1,4 @@
-How to build a vertical arc diagram with React and D3.js.
Vertical arc diagram
This tutorial is a variation around the general introduction to arc diagram with react and d3.js. You should probably understand the concepts described there before reading here.
This example explains how to make a vertical version of the arc diagram. The vertical version is sometimes prefered as it makes it easier to read the node labels.
A code sandbox is provided for the final result, but explanations target what's different compared to a classic horizontal arc diagram.
The main difficulty when it comes to make an arc diagram is to draw arcs in SVG.
The function allowing to draw arcs between 2 data points is a bit complicated since it requires to use elliptical arc curves.
You can read more explanation about the syntax in the official doc. But in the meantime here is the function I suggest to draw an arc connecting 2 points vertically:
const verticalArcGenerator = (
+How to build a vertical arc diagram with React and D3.js.
Vertical arc diagram
This tutorial is a variation around the general introduction to arc diagram with react and d3.js. You should probably understand the concepts described there before reading here.
This example explains how to make a vertical version of the arc diagram. The vertical version is sometimes prefered as it makes it easier to read the node labels.
A code sandbox is provided for the final result, but explanations target what's different compared to a classic horizontal arc diagram.
The main difficulty when it comes to make an arc diagram is to draw arcs in SVG.
The function allowing to draw arcs between 2 data points is a bit complicated since it requires to use elliptical arc curves.
You can read more explanation about the syntax in the official doc. But in the meantime here is the function I suggest to draw an arc connecting 2 points vertically:
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/example/barplot-data-transition-animation.html b/example/barplot-data-transition-animation.html
index 4a7457df..8bce4916 100644
--- a/example/barplot-data-transition-animation.html
+++ b/example/barplot-data-transition-animation.html
@@ -1,4 +1,4 @@
-Barplot with smooth dataset transition.
Barplot with animated dataset transition
This tutorial is a variation around the general introduction to barplot with react and d3.js. You should probably understand the concepts described there before digging into animation.
This example focus on how to transition between datasets. It explains how to animate the change thanks to the react-spring library.
A code sandbox is provided for the final result, but explanations target what's different compared to an usual barplot.
If you are in a hurry, this is what we're trying to achieve here 🙇♂️. The value of several individuals is represented, with one bar per individual. It's just a horizontal barplot.
It is possible to switch from one dataset to another using the buttons above the chart. A few notes on the 3 usual animation patterns:
update: bars smoothly update their rank and size when the data changes. So does their label.
enter: when the chart first loads, bar starts from 0 and grows to its real size. This is also true for items that are available in the new dataset but not in the previous. Check Christophe when you switch to data 2.
exit: when an item is not available in the next dataset, it disappears with no animation (see Paul when switching to data2).
Barplot with smooth transition between dataset
The Data
The dataset used here is exactly the same as the one used for the simple barplot. Note that 2 similar datasets are used: data1 and data2.
Animation
Most of the code is similar to the basic barplot component. But instead of building one rect per item in the dataset, a BarItem component is called to render a rectangle that supports animation.
The react-spring library is used to create a spring animation. The rectangle properties are passed to a useSpring hook that will build the animation for us.
This is how the BarItem component looks like:
import { useSpring, animated } from "@react-spring/web";
+Barplot with smooth dataset transition.
Barplot with animated dataset transition
This tutorial is a variation around the general introduction to barplot with react and d3.js. You should probably understand the concepts described there before digging into animation.
This example focus on how to transition between datasets. It explains how to animate the change thanks to the react-spring library.
A code sandbox is provided for the final result, but explanations target what's different compared to an usual barplot.
If you are in a hurry, this is what we're trying to achieve here 🙇♂️. The value of several individuals is represented, with one bar per individual. It's just a horizontal barplot.
It is possible to switch from one dataset to another using the buttons above the chart. A few notes on the 3 usual animation patterns:
update: bars smoothly update their rank and size when the data changes. So does their label.
enter: when the chart first loads, bar starts from 0 and grows to its real size. This is also true for items that are available in the new dataset but not in the previous. Check Christophe when you switch to data 2.
exit: when an item is not available in the next dataset, it disappears with no animation (see Paul when switching to data2).
Barplot with smooth transition between dataset
The Data
The dataset used here is exactly the same as the one used for the simple barplot. Note that 2 similar datasets are used: data1 and data2.
Animation
Most of the code is similar to the basic barplot component. But instead of building one rect per item in the dataset, a BarItem component is called to render a rectangle that supports animation.
The react-spring library is used to create a spring animation. The rectangle properties are passed to a useSpring hook that will build the animation for us.
This is how the BarItem component looks like:
import { useSpring, animated } from "@react-spring/web";
type BarItemProps = {
name: string;
@@ -76,4 +76,4 @@
};
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/example/barplot-stacked-horizontal.html b/example/barplot-stacked-horizontal.html
index f3ce6141..004c7098 100644
--- a/example/barplot-stacked-horizontal.html
+++ b/example/barplot-stacked-horizontal.html
@@ -1,4 +1,4 @@
-Horizontal stacked barplot.
Horizontal Stacked Barplot
This tutorial is a variation around the general introduction to barplot with react and d3.js. You should probably understand the concepts described there before digging into stacking.
This example shows how to represent 2 levels of grouping in a barplot, resulting in a stacked barplot. The items of the dataset are divided in groups (reprented as bars) and subgroups (represented as sections in each bar).
A code sandbox is provided for the final result, but explanations target what's different compared to an usual barplot.
This is a stacked barplot built using React and d3.js. The dummy dataset provides information about how much my friends spent the last month. The people are the group here. Each bar represents a group.
A second level of grouping is available. We know if the money was spent on travel, food or beer. It is possible to represent this additional amount of info using a process called stacking.
How much my friends spend on travel, food and beer.
A horizontal stacked barplot built with d3.js for scales, and react for rendering
Now, let's see how to implement such a graph.
The Data
There are several ways to store this information in javascript. I suggest an array of object where each object provides the valueof 1 specific expense, with the group (friend name) and the subgroup (category of expense).
export const data = [
+Horizontal stacked barplot.
Horizontal Stacked Barplot
This tutorial is a variation around the general introduction to barplot with react and d3.js. You should probably understand the concepts described there before digging into stacking.
This example shows how to represent 2 levels of grouping in a barplot, resulting in a stacked barplot. The items of the dataset are divided in groups (reprented as bars) and subgroups (represented as sections in each bar).
A code sandbox is provided for the final result, but explanations target what's different compared to an usual barplot.
This is a stacked barplot built using React and d3.js. The dummy dataset provides information about how much my friends spent the last month. The people are the group here. Each bar represents a group.
A second level of grouping is available. We know if the money was spent on travel, food or beer. It is possible to represent this additional amount of info using a process called stacking.
How much my friends spend on travel, food and beer.
A horizontal stacked barplot built with d3.js for scales, and react for rendering
Now, let's see how to implement such a graph.
The Data
There are several ways to store this information in javascript. I suggest an array of object where each object provides the valueof 1 specific expense, with the group (friend name) and the subgroup (category of expense).
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/example/barplot-stacked-vertical.html b/example/barplot-stacked-vertical.html
index e8a2691b..ebfd5077 100644
--- a/example/barplot-stacked-vertical.html
+++ b/example/barplot-stacked-vertical.html
@@ -1,4 +1,4 @@
-Vertical stacked barplot.
Vertical Stacked Barplot
This tutorial is a variation around the general introduction to barplot with react and d3.js. You should probably understand the concepts described there before digging into stacking.
This example shows how to represent 2 levels of grouping in a barplot, resulting in a stacked barplot. The items of the dataset are divided in groups (reprented as bars) and subgroups (represented as sections in each bar).
A code sandbox is provided for the final result, but explanations target what's different compared to an usual barplot.
This is a vertical stacked barplot built using React and d3.js. It is very similar to the horizontal version. So very little explanation is provided here. Enjoy the sandbox!
Most basic barplot built with d3.js for scales, and react for rendering
Variation
Check those other barplot and stacked barplot that can interest you:
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
+Vertical stacked barplot.
Vertical Stacked Barplot
This tutorial is a variation around the general introduction to barplot with react and d3.js. You should probably understand the concepts described there before digging into stacking.
This example shows how to represent 2 levels of grouping in a barplot, resulting in a stacked barplot. The items of the dataset are divided in groups (reprented as bars) and subgroups (represented as sections in each bar).
A code sandbox is provided for the final result, but explanations target what's different compared to an usual barplot.
This is a vertical stacked barplot built using React and d3.js. It is very similar to the horizontal version. So very little explanation is provided here. Enjoy the sandbox!
Most basic barplot built with d3.js for scales, and react for rendering
Variation
Check those other barplot and stacked barplot that can interest you:
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/example/boxplot-horizontal.html b/example/boxplot-horizontal.html
index e496a32e..a4e42e4c 100644
--- a/example/boxplot-horizontal.html
+++ b/example/boxplot-horizontal.html
@@ -1,4 +1,4 @@
-Horizontal Boxplot
Horizontal Boxplot
This tutorial is a variation around the general introduction to boxplot with react and d3.js. You should probably understand the concepts described there before reading here.
The general introduction relies on a VerticalBox function that draws a.. vertical box 🙃. The main difference here is that we are now building a HorizontalBox function and swap the X and Y axes.
As usual, this post comes with explanation, a code sandbox and a template that you can reuse instantly in your web application.
Here is a proposal of implementation for a horizontal boxplot with react and d3.js.
D3 actually has a very minor role here. It is only used to compute the xand y scales. The x scale is a linear scale made with the scaleLinear() function. The Y scale shows groups thanks to the scaleBand() function.
A horizontal boxplot made with React and D3.js.
Horizontal box drawing
We need a function that draws a horizontal box in SVG based on the quartiles position in pixels.
This tutorial is a variation around the general introduction to boxplot with react and d3.js. You should probably understand the concepts described there before reading here.
The general introduction relies on a VerticalBox function that draws a.. vertical box 🙃. The main difference here is that we are now building a HorizontalBox function and swap the X and Y axes.
As usual, this post comes with explanation, a code sandbox and a template that you can reuse instantly in your web application.
Here is a proposal of implementation for a horizontal boxplot with react and d3.js.
D3 actually has a very minor role here. It is only used to compute the xand y scales. The x scale is a linear scale made with the scaleLinear() function. The Y scale shows groups thanks to the scaleBand() function.
A horizontal boxplot made with React and D3.js.
Horizontal box drawing
We need a function that draws a horizontal box in SVG based on the quartiles position in pixels.
This post is a translation of the basic boxplot example, switching from vertical to horizontal mode.
Now that this basic horizontal boxplot is available, it should be straightforward to add interesting features like showing individual data points or switching to a violin plot.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/example/boxplot-jitter.html b/example/boxplot-jitter.html
index 452ffe57..0f54fca4 100644
--- a/example/boxplot-jitter.html
+++ b/example/boxplot-jitter.html
@@ -1,4 +1,4 @@
-How to build a boxplot with individual data points displayed on top using jittering.
Boxplot with jittering
This tutorial is a variation around the general introduction to boxplot with react and d3.js. You should probably understand the concepts described there before reading here.
This example explains how to display all individual data points of the dataset on top of each box, using jittering to avoid overlaps.
A code sandbox is provided for the final result, but explanations target what's different compared to an usual boxplot.
If you are in a hurry, this is what we're trying to achieve here.🙇♂️
The distribution of several groups is represented, with one box for each group. This kind of viz is called a boxplot and has its own boxplot section.
Even if powerful to summarize the distribution of a numeric variable, the boxplot has flaws.
It basically hides the underlying distribution. For instance, a low sample size or a bi-modal distribution are impossible to detect reading the boxes only.
Jittering is a good workaround. Add all individual data points with low size, low opacity, and some random shift to the right or to the left (jitter). The underlying distribution becomes instantly available.
Showing individual data points using jittering on top of your boxplot adds trust. Reader now knows you're not hiding anything.
The Data
The dataset used here is exactly the same as the one used for the simple boxplot.
Jittering
We want to show each individual data point on top of each box for accuracy. If the dataset is big, this would result in a cluttered figure with a lot of overlap.
Jittering is a process that shifts each circle by a random value. It avoids the overlap, and allows to make the circle layer insightful.
Here is a suggestion to implement it:
const allCircles = groupData.map((value, i) => (
+How to build a boxplot with individual data points displayed on top using jittering.
Boxplot with jittering
This tutorial is a variation around the general introduction to boxplot with react and d3.js. You should probably understand the concepts described there before reading here.
This example explains how to display all individual data points of the dataset on top of each box, using jittering to avoid overlaps.
A code sandbox is provided for the final result, but explanations target what's different compared to an usual boxplot.
If you are in a hurry, this is what we're trying to achieve here.🙇♂️
The distribution of several groups is represented, with one box for each group. This kind of viz is called a boxplot and has its own boxplot section.
Even if powerful to summarize the distribution of a numeric variable, the boxplot has flaws.
It basically hides the underlying distribution. For instance, a low sample size or a bi-modal distribution are impossible to detect reading the boxes only.
Jittering is a good workaround. Add all individual data points with low size, low opacity, and some random shift to the right or to the left (jitter). The underlying distribution becomes instantly available.
Showing individual data points using jittering on top of your boxplot adds trust. Reader now knows you're not hiding anything.
The Data
The dataset used here is exactly the same as the one used for the simple boxplot.
Jittering
We want to show each individual data point on top of each box for accuracy. If the dataset is big, this would result in a cluttered figure with a lot of overlap.
Jittering is a process that shifts each circle by a random value. It avoids the overlap, and allows to make the circle layer insightful.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/example/circle-packing-with-d3-force.html b/example/circle-packing-with-d3-force.html
index bb403a16..d6c22b00 100644
--- a/example/circle-packing-with-d3-force.html
+++ b/example/circle-packing-with-d3-force.html
@@ -1,4 +1,4 @@
-Circle Packing with d3-force.
Circle Packing with d3-force
This tutorial is a variation around the general introduction to circle packing with react and d3.js. You should probably understand the concepts described there before reading here.
Instead of relying on the pack() function of d3.js to compute the best node positions, this example suggests to rely on the d3-force plugin to apply physical forces on each node.
A code sandbox is provided for the final result, but explanations target what's different compared to a classic circular packing based on some concepts described in the network diagram section.
A circle packing chart made using the d3-force plugin, with bubbles being attracted by the y=0 baseline.
Using d3-force
This example is actually a variation of a network diagram, but with no links between nodes.
Some physical forces are applied to each node to compute their position through an iterative simulation:
d3.forceSimulation(nodes)
+Circle Packing with d3-force.
Circle Packing with d3-force
This tutorial is a variation around the general introduction to circle packing with react and d3.js. You should probably understand the concepts described there before reading here.
Instead of relying on the pack() function of d3.js to compute the best node positions, this example suggests to rely on the d3-force plugin to apply physical forces on each node.
A code sandbox is provided for the final result, but explanations target what's different compared to a classic circular packing based on some concepts described in the network diagram section.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/example/histogram-mirror.html b/example/histogram-mirror.html
index dc929483..b85c7460 100644
--- a/example/histogram-mirror.html
+++ b/example/histogram-mirror.html
@@ -1,4 +1,4 @@
-How to build a mirror histogram with React and D3.
Mirror histogram
This tutorial is a variation around the general introduction to histogram with react and d3.js. You should probably understand the concepts described there before reading here.
This example explains how to compare the distribution of 2 groups using amirror histogram. One group is displayed above the X axis, the other onebelow. It allows a direct comparison without having any overlap.
A code sandbox is provided for the final result, but explanations target what's different compared to an usual histogram.
If you are in a hurry, this is what we're trying to achieve here.🙇♂️
The distributions of 2 groups are displayed on the same figure, 1 group being on top and the other being below the X axis. It allows to compare them efficiently, with no overlap. .
A mirror histogram made with react and d3.js to compare the distribution of 2 groups in a dataset
The Data
The dataset used here is slightly different as the one used for the simple 1 group histogram.
An array is used, with each object in it providing the name (group property here) and the values of a group. Note that if more than 2 items are available in the array, only the 2 first will be used by the component.
Here is a minimal example of the data structure:
const data = [
+How to build a mirror histogram with React and D3.
Mirror histogram
This tutorial is a variation around the general introduction to histogram with react and d3.js. You should probably understand the concepts described there before reading here.
This example explains how to compare the distribution of 2 groups using amirror histogram. One group is displayed above the X axis, the other onebelow. It allows a direct comparison without having any overlap.
A code sandbox is provided for the final result, but explanations target what's different compared to an usual histogram.
If you are in a hurry, this is what we're trying to achieve here.🙇♂️
The distributions of 2 groups are displayed on the same figure, 1 group being on top and the other being below the X axis. It allows to compare them efficiently, with no overlap. .
A mirror histogram made with react and d3.js to compare the distribution of 2 groups in a dataset
The Data
The dataset used here is slightly different as the one used for the simple 1 group histogram.
An array is used, with each object in it providing the name (group property here) and the values of a group. Note that if more than 2 items are available in the array, only the 2 first will be used by the component.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/example/histogram-slider-bin-size.html b/example/histogram-slider-bin-size.html
index ab48f97a..e59cb8b4 100644
--- a/example/histogram-slider-bin-size.html
+++ b/example/histogram-slider-bin-size.html
@@ -1,4 +1,4 @@
-Histogram with Adjustable Bin Size Slider
Histogram with Adjustable Bin Size Slider
Constructing a histogram carries a significant responsibility: selecting the appropriate bin size. It's crucial to experiment with different values, as an incorrect choice can obscure the underlying narrative.
Many aspire to complete a marathon in under four hours, leading to a pronounced gap in the histogram. Choosing a bin size that is too large can cause you to overlook this important detail!
Countless marathons are held around the globe each year. This article examines the completion times for male participants across various events.
I downloaded the data and used R to filter it down to just the first 100,000 rows focused on male finishers.
The cleaned dataset is just an array of numbers, stored in an array called data.
Plot and code
The goal of this post is to create a histogram that illustrates the distribution of finishing times for a marathon.
Specifically, it demonstrates how to use D3 to compute bins from the original dataset and how React can be utilized to render the rectangles that form the histogram.
Additionally, it features a slider that allows users to adjust the bin size, dynamically recalculating and rendering the bar sizes in real time.
More importantly, this post highlights the significance of experimenting with bin sizes in a histogram.
With a lower bin count (around 30), readers will observe a nearly normal distribution. However, increasing the number of bins reveals a significant gap around the 4-hour mark, showcasing the intense effort many runners put in to finish under this threshold!
A histogram with a slider that controls the bin size.
Dataviz caveat
A great way to improve your data visualization skills is by learning the most common pitfalls when creating charts.
I’ve compiled a large collection of these mistakes and created flashcards that summarize each one. For example, here’s the card that explains the bin size issue.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
+Histogram with Adjustable Bin Size Slider
Histogram with Adjustable Bin Size Slider
Constructing a histogram carries a significant responsibility: selecting the appropriate bin size. It's crucial to experiment with different values, as an incorrect choice can obscure the underlying narrative.
Many aspire to complete a marathon in under four hours, leading to a pronounced gap in the histogram. Choosing a bin size that is too large can cause you to overlook this important detail!
Countless marathons are held around the globe each year. This article examines the completion times for male participants across various events.
I downloaded the data and used R to filter it down to just the first 100,000 rows focused on male finishers.
The cleaned dataset is just an array of numbers, stored in an array called data.
Plot and code
The goal of this post is to create a histogram that illustrates the distribution of finishing times for a marathon.
Specifically, it demonstrates how to use D3 to compute bins from the original dataset and how React can be utilized to render the rectangles that form the histogram.
Additionally, it features a slider that allows users to adjust the bin size, dynamically recalculating and rendering the bar sizes in real time.
More importantly, this post highlights the significance of experimenting with bin sizes in a histogram.
With a lower bin count (around 30), readers will observe a nearly normal distribution. However, increasing the number of bins reveals a significant gap around the 4-hour mark, showcasing the intense effort many runners put in to finish under this threshold!
A histogram with a slider that controls the bin size.
Dataviz caveat
A great way to improve your data visualization skills is by learning the most common pitfalls when creating charts.
I’ve compiled a large collection of these mistakes and created flashcards that summarize each one. For example, here’s the card that explains the bin size issue.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/example/histogram-small-multiple.html b/example/histogram-small-multiple.html
index bf391f94..424a83d2 100644
--- a/example/histogram-small-multiple.html
+++ b/example/histogram-small-multiple.html
@@ -1,4 +1,4 @@
-How to build a small multiple histogram with React and D3 to show the distribution of several groups.
Histogram with small multiples
This tutorial is a variation around the general introduction to histogram with react and d3.js. You should probably understand the concepts described there before reading here.
This example explains how to plot the distribution of several groups, each distribution being drawn on its own pannel. This dataviz technique is called small multiples. It can be useful to compare the distribution of several items in a dataset.
A code sandbox is provided for the final result, but explanations target what's different compared to an usual histogram.
If you are in a hurry, this is what we're trying to achieve here.🙇♂️
The distribution of several groups are displayed, one on each panel of the graphing window. It allows to compare the distributions.
Note that this works even if groups have very similar distributions as the bars won't overlap each other. It is thus a very good alternative to the histogram with multiple groups that would get unreadable in this condition.
Histogram representing the distribution of 4 groups in a dataset using the small multiple display. Made with react (rendering) and d3.js (scales)
The Data
The dataset used here is slightly different as the one used for the simple 1 group histogram.
An array is used, with each object in it providing the name (group property here) and the values of a group.
Here is a minimal example of the data structure:
const data = [
+How to build a small multiple histogram with React and D3 to show the distribution of several groups.
Histogram with small multiples
This tutorial is a variation around the general introduction to histogram with react and d3.js. You should probably understand the concepts described there before reading here.
This example explains how to plot the distribution of several groups, each distribution being drawn on its own pannel. This dataviz technique is called small multiples. It can be useful to compare the distribution of several items in a dataset.
A code sandbox is provided for the final result, but explanations target what's different compared to an usual histogram.
If you are in a hurry, this is what we're trying to achieve here.🙇♂️
The distribution of several groups are displayed, one on each panel of the graphing window. It allows to compare the distributions.
Note that this works even if groups have very similar distributions as the bars won't overlap each other. It is thus a very good alternative to the histogram with multiple groups that would get unreadable in this condition.
Histogram representing the distribution of 4 groups in a dataset using the small multiple display. Made with react (rendering) and d3.js (scales)
The Data
The dataset used here is slightly different as the one used for the simple 1 group histogram.
An array is used, with each object in it providing the name (group property here) and the values of a group.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/example/histogram-with-several-groups.html b/example/histogram-with-several-groups.html
index 41c90bb5..a9e0db74 100644
--- a/example/histogram-with-several-groups.html
+++ b/example/histogram-with-several-groups.html
@@ -1,4 +1,4 @@
-How to build a histogram with React and D3.
Histogram with several groups
This tutorial is a variation around the general introduction to histogram with react and d3.js. You should probably understand the concepts described there before reading here.
This example explains how to plot several groups on the same histogram, by overlapping them on the same X axis. It can be useful to compare the distribution of several items in a dataset.
A code sandbox is provided for the final result, but explanations target what's different compared to an usual histogram.
If you are in a hurry, this is what we're trying to achieve here.🙇♂️
The distribution of several groups are displayed on the same figure, allowing to easily compare them. Please note that this kind of visual works well when there is a clear distinction between groups. Otherwise, bars will overlap each other resulting in an unreadable chart.
Histogram representing the distribution of 3 groups in a dataset. Made with react (rendering) and d3.js (scales)
The Data
The dataset used here is slightly different as the one used for the simple 1 group histogram.
An array is used, with each object in it providing the name (group property here) and the values of a group.
Here is a minimal example of the data structure:
const data = [
+How to build a histogram with React and D3.
Histogram with several groups
This tutorial is a variation around the general introduction to histogram with react and d3.js. You should probably understand the concepts described there before reading here.
This example explains how to plot several groups on the same histogram, by overlapping them on the same X axis. It can be useful to compare the distribution of several items in a dataset.
A code sandbox is provided for the final result, but explanations target what's different compared to an usual histogram.
If you are in a hurry, this is what we're trying to achieve here.🙇♂️
The distribution of several groups are displayed on the same figure, allowing to easily compare them. Please note that this kind of visual works well when there is a clear distinction between groups. Otherwise, bars will overlap each other resulting in an unreadable chart.
Histogram representing the distribution of 3 groups in a dataset. Made with react (rendering) and d3.js (scales)
The Data
The dataset used here is slightly different as the one used for the simple 1 group histogram.
An array is used, with each object in it providing the name (group property here) and the values of a group.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/example/line-chart-synchronized-cursors.html b/example/line-chart-synchronized-cursors.html
index 160a337e..c08ce07a 100644
--- a/example/line-chart-synchronized-cursors.html
+++ b/example/line-chart-synchronized-cursors.html
@@ -1,4 +1,4 @@
-Line charts with synchronized cursors.
Line charts with synchronized cursors
This tutorial is a variation around the general introduction to line chart with react and d3.js. You should probably understand the concepts described there before reading here.
This example explains how to create several line charts and add asynchronized cursor on all of them. Hovering over one graph will display a cursor on all of them, easing the understanding of synchronized patterns.
A code sandbox is provided for the final result, but explanations target what's different compared to an usual line chart.
If you are in a hurry, this is what we're trying to achieve here.🙇♂️
Two line charts are displayed one next to each other. It shows the evolution of 2 numeric valriables.
Hovering over a chart will display a cursor that is synced with the other chart. This helps finding a relationship between them.
Hover over a chart to see a cursor on both of them, easing the time comparison.
The Data
Two dataset are used here. Both with the same format as described in theline chart section of the gallery.
Here is a minimal example of the data structure:
const data = [
+Line charts with synchronized cursors.
Line charts with synchronized cursors
This tutorial is a variation around the general introduction to line chart with react and d3.js. You should probably understand the concepts described there before reading here.
This example explains how to create several line charts and add asynchronized cursor on all of them. Hovering over one graph will display a cursor on all of them, easing the understanding of synchronized patterns.
A code sandbox is provided for the final result, but explanations target what's different compared to an usual line chart.
I used react spring to animate the cursor transition. When the mouse moves, the cursor position does not change instantly but moves smoothly to the new position.
Animation in dataviz using React is a big topic. It's impossible to go in-depth here! I will publish a dedicated blog post on the topic soon. Please subscribe to the newsletter if you want to be notified.
Hover over a chart to see a cursor on both of them, easing the time comparison.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/example/network-diagram-with-colored-groups.html b/example/network-diagram-with-colored-groups.html
index 0ee4e82f..4fb631c2 100644
--- a/example/network-diagram-with-colored-groups.html
+++ b/example/network-diagram-with-colored-groups.html
@@ -1,4 +1,4 @@
-Force-directed Network diagram with React and D3.js.
Force-directed Network diagram with React and D3.js
This tutorial is a variation around the general introduction to network diagram with react and d3.js. You should probably understand the concepts described there before reading here, notably everything related to the d3-force plugin.
This example shows how to apply the concepts of the general tutorial to a real dataset. It creates a force-directed graph that describes the network of character co-occurrence in Les Misérables.
A code sandbox is provided for the final result, but explanations target what's different compared to a classic network diagram.
If you are in a hurry, here is the final plot we're trying to achieve here, together with its code:🙇♂️
In this network diagram, each node is a character of the book Les Misérables. The network chart tries to localise characters who often appear together close to each other.
A network diagram made with d3.js and react. It shows the character co-occurence in Les Misérables.
Color Palette
The only difference with the very simple network chart of the mainnetwork tutorial is the presence of a color palette used to color the nodes.
Each node of the dataset is attributed to a group. It is thus possible to create a color scale using a scaleOrdinal:
// List of Groups
+Force-directed Network diagram with React and D3.js.
Force-directed Network diagram with React and D3.js
This tutorial is a variation around the general introduction to network diagram with react and d3.js. You should probably understand the concepts described there before reading here, notably everything related to the d3-force plugin.
This example shows how to apply the concepts of the general tutorial to a real dataset. It creates a force-directed graph that describes the network of character co-occurrence in Les Misérables.
A code sandbox is provided for the final result, but explanations target what's different compared to a classic network diagram.
If you are in a hurry, here is the final plot we're trying to achieve here, together with its code:🙇♂️
In this network diagram, each node is a character of the book Les Misérables. The network chart tries to localise characters who often appear together close to each other.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/example/population-pyramid.html b/example/population-pyramid.html
index 7d3937bb..4a876268 100644
--- a/example/population-pyramid.html
+++ b/example/population-pyramid.html
@@ -1 +1 @@
-
Some countries are experiencing an aging population, while others are in the midst of a baby boom. Some face unusual demographic shifts when some others are surprisingly stable. Dive into the intriguing patterns of global population dynamics with this captivating set of lines.
1950
1965
1980
1995
2010
2025
2040
2055
2070
2085
2100
Looks good but
What's this?
These organic shapes look quite appealing. But do they actually mean anything? 🤔
They do!
They're a creative way to represent the evolution of the population pyramid of a country over time.
Let me walk you through it step by step:
How I made this
Material & Method
This project is inspired by the work of Jorge Camoes. His approach resonated with me, especially since I'm not fond of getting older. So I decided it was a great time to dive into some D3.jswork.
If you're curious about how this is made, most of the concepts are explained in my React Graph Gallery, and the code is available on GitHub.
Essentially, it's a substantial amount of React code for rendering, with a bit of D3.js to calculate the shape positions. React Spring is used for all the animations. That's it!
Oh, and the data comes from the United Nations. Thanks to them for providing it!
Some countries are experiencing an aging population, while others are in the midst of a baby boom. Some face unusual demographic shifts when some others are surprisingly stable. Dive into the intriguing patterns of global population dynamics with this captivating set of lines.
1950
1965
1980
1995
2010
2025
2040
2055
2070
2085
2100
Looks good but
What's this?
These organic shapes look quite appealing. But do they actually mean anything? 🤔
They do!
They're a creative way to represent the evolution of the population pyramid of a country over time.
Let me walk you through it step by step:
How I made this
Material & Method
This project is inspired by the work of Jorge Camoes. His approach resonated with me, especially since I'm not fond of getting older. So I decided it was a great time to dive into some D3.jswork.
If you're curious about how this is made, most of the concepts are explained in my React Graph Gallery, and the code is available on GitHub.
Essentially, it's a substantial amount of React code for rendering, with a bit of D3.js to calculate the shape positions. React Spring is used for all the animations. That's it!
Oh, and the data comes from the United Nations. Thanks to them for providing it!
\ No newline at end of file
diff --git a/example/radar-chart-animation.html b/example/radar-chart-animation.html
index 22a34e53..f893684a 100644
--- a/example/radar-chart-animation.html
+++ b/example/radar-chart-animation.html
@@ -1,4 +1,4 @@
-Types of Data Professionals
Types of Data Professionals
The field of data offers a diverse array of job titles, making it challenging to navigate without getting lost in the jargon and uncertainty about which roles to pursue.
The charts below offer deeper insights into the competencies needed, salary ranges, and popularity trends for the four primary job titles.
It's a good opportunity to learn how to make a little application with React and D3.js, featuring 3 inter-connected chart types with smooth animated transition: radar chart, line chart and lollipop.
Here is what we want to build. It is highly inspired by a work from Kevin Rosamont Prombo.
Four buttons are provided, one for each type of data professional. Clicking on it will update the 3 following charts.
Types of Data Professionals
The field of data offers a diverse array of job titles, making it challenging to navigate without getting lost in the jargon and uncertainty about which roles to pursue. The charts below offer deeper insights into the competencies needed, salary ranges, and popularity trends for the four primary job titles.
Dive deep into the 4 main types of Data Professionals. Understand their main required competencies, their salary ranges and their popularity.
Data
The radar chart explains what competencies are required to be a Data Engineer, a Machine Learning Engineer, a Data Scientist or a Data Analyst. The data and the representation have been first suggested by Kevin Rosamont Prombo.
The little lollipop plot provides the estimated salary range for the job. Data from review n prep and glassdoor.
The line chart gives an estimate of the job popularity based on its google trend score in the last ~10 years.
Click the Show code button above to get a sandbox with the 3 datasets at json format.
Global state
The first thing that is required here is a global state.
The root file of the application computes all the available groups in the dataset (allGroups) and use the first group to state the initial state of the application.
const allGroups = data.map((d) => d.name);
+Types of Data Professionals
Types of Data Professionals
The field of data offers a diverse array of job titles, making it challenging to navigate without getting lost in the jargon and uncertainty about which roles to pursue.
The charts below offer deeper insights into the competencies needed, salary ranges, and popularity trends for the four primary job titles.
It's a good opportunity to learn how to make a little application with React and D3.js, featuring 3 inter-connected chart types with smooth animated transition: radar chart, line chart and lollipop.
Here is what we want to build. It is highly inspired by a work from Kevin Rosamont Prombo.
Four buttons are provided, one for each type of data professional. Clicking on it will update the 3 following charts.
Types of Data Professionals
The field of data offers a diverse array of job titles, making it challenging to navigate without getting lost in the jargon and uncertainty about which roles to pursue. The charts below offer deeper insights into the competencies needed, salary ranges, and popularity trends for the four primary job titles.
Dive deep into the 4 main types of Data Professionals. Understand their main required competencies, their salary ranges and their popularity.
Data
The radar chart explains what competencies are required to be a Data Engineer, a Machine Learning Engineer, a Data Scientist or a Data Analyst. The data and the representation have been first suggested by Kevin Rosamont Prombo.
The little lollipop plot provides the estimated salary range for the job. Data from review n prep and glassdoor.
The line chart gives an estimate of the job popularity based on its google trend score in the last ~10 years.
Click the Show code button above to get a sandbox with the 3 datasets at json format.
Global state
The first thing that is required here is a global state.
The root file of the application computes all the available groups in the dataset (allGroups) and use the first group to state the initial state of the application.
When the user clicks a button, the state is updated thanks to the setSelectedGroup() function. The newly selected group (selectedGroup) is then passed to the graphing component as a prop.
Charts
Then we need to build 3 charts based on the 3 filtered dataset.
The React graph gallery already provides in-depth tutorials for those 3 chart types. So please refer to the radar chart, line chart and lollipop sections to learn how to build them.
Most of the fun here comes from the smooth, animated transition between groups. It is also the trickiest part to implement.
As always in this gallery, the animation is computed thanks to react-spring, a javascript library for spring animation in React.
It would be way too long to explain how to implement it properly in this page. But to put it in a nutshell, you need to render animated elements instead of usual SVG elements. Here is how the line is rendered for instance:
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/example/sankey-bump-chart.html b/example/sankey-bump-chart.html
index f68aaec7..d30ca3eb 100644
--- a/example/sankey-bump-chart.html
+++ b/example/sankey-bump-chart.html
@@ -1,4 +1,4 @@
-How to build a vertical arc diagram with React and D3.js.
Sankey Bump Chart
A Sankey bump chart is a variation around the bump chart
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
+How to build a vertical arc diagram with React and D3.js.
Sankey Bump Chart
A Sankey bump chart is a variation around the bump chart
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/example/scatterplot-basic-canvas.html b/example/scatterplot-basic-canvas.html
index a3e18d76..5d48533b 100644
--- a/example/scatterplot-basic-canvas.html
+++ b/example/scatterplot-basic-canvas.html
@@ -1,4 +1,4 @@
-Scatterplot with Canvas
Scatterplot with Canvas
This tutorial is a variation around the general introduction to scatterplot with react and d3.js. You should probably understand the concepts described there before reading here.
Rendering a scatterplot using SVG can arm the performance of your webpage if the number of data points is high. A common workaround is to draw the circles in canvas instead.
This post explains how to combine SVG for the axes and canvas for the scatterplot markers in React.
A scatterplot is a chart type that is commonly used to display a high amount of data points.
When you draw it in SVG, it means adding a bunch of circle elements. As a result, your DOM will quickly become really heavy. It can result in poor performances. If you want to add some animation on top of it, you are pretty sure to freeze the page.
When using canvas, you add only 1 node in your DOM, and benefit the very high speed of it. It is game changer for any viz type that requires good performances.
Scatterplot canvas implementation
The trick here is to use 2 layers of drawing:
The first layer is for the axes. It is an SVG element that will add the X and Y axes using some usual AxisLeft and AxisBottom components.
The second layer is for the markers, it is the canvas element. It has a ref. We can then call a function in a useEffect hook to draw inside this canvas element.
This is how the useEffect hook looks like, drawing our circles:
useEffect(() => {
+Scatterplot with Canvas
Scatterplot with Canvas
This tutorial is a variation around the general introduction to scatterplot with react and d3.js. You should probably understand the concepts described there before reading here.
Rendering a scatterplot using SVG can arm the performance of your webpage if the number of data points is high. A common workaround is to draw the circles in canvas instead.
This post explains how to combine SVG for the axes and canvas for the scatterplot markers in React.
A scatterplot is a chart type that is commonly used to display a high amount of data points.
When you draw it in SVG, it means adding a bunch of circle elements. As a result, your DOM will quickly become really heavy. It can result in poor performances. If you want to add some animation on top of it, you are pretty sure to freeze the page.
When using canvas, you add only 1 node in your DOM, and benefit the very high speed of it. It is game changer for any viz type that requires good performances.
Scatterplot canvas implementation
The trick here is to use 2 layers of drawing:
The first layer is for the axes. It is an SVG element that will add the X and Y axes using some usual AxisLeft and AxisBottom components.
The second layer is for the markers, it is the canvas element. It has a ref. We can then call a function in a useEffect hook to draw inside this canvas element.
This is how the useEffect hook looks like, drawing our circles:
Here is a complete implementation of the scatterplot using this technique with 10000 data points:
A scatterplot made with React, using SVG for the axes and Canvas for the markers to improve performance.
Canvas is an important topic in data visualization for the web. I plan to write complete articles on the topic. You can know when it's ready by subscribing to the project.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/example/scatterplot-tooltip-with-voronoi-for-closest-point-detection.html b/example/scatterplot-tooltip-with-voronoi-for-closest-point-detection.html
index 9cf79657..f340057d 100644
--- a/example/scatterplot-tooltip-with-voronoi-for-closest-point-detection.html
+++ b/example/scatterplot-tooltip-with-voronoi-for-closest-point-detection.html
@@ -1,8 +1,8 @@
-Scatterplot tooltip with React and D3: use Voronoi to detect closest point.
Scatterplot tooltip: use Voronoi for closest point detection
This tutorial is a variation around the general introduction to scatterplot with react and d3.js. You should probably understand the concepts described there before reading here.
This example explains how to use an invisible Voronoi diagram in the background to detect the mouse closest point of the scatterplot.
A code sandbox is provided for the final result, but explanations target what's different compared to an usual scatter plot.
If you are in a hurry, this is what we're trying to achieve here.🙇♂️
This is a scatterplot with voronoi detection. When you enter the chart area with your mouse, the closest point is found using a Delaunay triangulation. The corresponding circle is highlighted on the chart.
Same concept could easily be used to add a tooltip with great performance.
A scatterplot with tooltip. The closest point is detected using Voronoi to trigger the tooltip apparition.
The Data
The dataset used here is the same as for an usual scatterplot.
It is an array of objects where each object is a data point. The object can have many properties, but at least a x and a y prop are needed to provide the 2d coordinates.
Here is a minimal example of the data structure:
const data = [
+Scatterplot tooltip with React and D3: use Voronoi to detect closest point.
Scatterplot tooltip: use Voronoi for closest point detection
This tutorial is a variation around the general introduction to scatterplot with react and d3.js. You should probably understand the concepts described there before reading here.
This example explains how to use an invisible Voronoi diagram in the background to detect the mouse closest point of the scatterplot.
A code sandbox is provided for the final result, but explanations target what's different compared to an usual scatter plot.
If you are in a hurry, this is what we're trying to achieve here.🙇♂️
This is a scatterplot with voronoi detection. When you enter the chart area with your mouse, the closest point is found using a Delaunay triangulation. The corresponding circle is highlighted on the chart.
Same concept could easily be used to add a tooltip with great performance.
A scatterplot with tooltip. The closest point is detected using Voronoi to trigger the tooltip apparition.
The Data
The dataset used here is the same as for an usual scatterplot.
It is an array of objects where each object is a data point. The object can have many properties, but at least a x and a y prop are needed to provide the 2d coordinates.
The shapes drawn in the background are called a Voronoi diagram.
A voronoi diagram is a is a partition of a plane into regions called voronoi cells. A voronoi cell consists of every point in the plane whose distance to its linked data point is less than or equal to its distance to any other data point.
This is very handy to detect the mouse closest point on a scatterplot! 🔥
The react graph gallery has a dedicated section on the topic. Once you understood how Voronoi works with d3, it is just a matter of adding axes to get a scatterplot with point detection.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/example/timeseries-moving-average.html b/example/timeseries-moving-average.html
index 1afa3b34..9df9da5a 100644
--- a/example/timeseries-moving-average.html
+++ b/example/timeseries-moving-average.html
@@ -1,4 +1,4 @@
-Timeseries with moving average
Timeseries with moving average
In this tutorial, we'll explore the art of plotting time series data, incorporating a dynamic moving average line chart overlaid on the primary plot.
Inspired by a visualization featured in the renowned French newspaper Le Monde, depicting the blooming patterns of cherry trees over time, we'll recreate this compelling chart.
With readily available code and straightforward explanations, you'll witness the prowess of React in seamlessly stacking multiple layers of rendering – from scatterplots to line charts and annotations.
Amidst the myriad visualizations of global warming, one stands out for its poetic resonance – an unexpected gem found within the pages of a French newspaper: the flourishing timelines of cherry trees in Japan.
Intriguingly, Yasuyuki Aono delved into ancient texts dating back to the 800s to unearth this data. Thanks to the meticulous efforts of Our World in Data in cleaning and refining the dataset, we're now presented with a captivating portrayal of our changing world.
Here is the chart we're learning to build today:
Plein floraison annuelleMoyenne de la floraison (20 ans)
Prerequisite
This graph combines elements of both a scatterplot and a line chart, making it essential to grasp the fundamentals of each chart type before diving in.
Good news, the react graph gallery has some very basic examples to illustrate those concepts!
Annotation
The deepest layer of this chart is the annotation that says 1950 - 2023. It uses stripes in the background. Stripes can be build in SVG as follow:
<defs>
+Timeseries with moving average
Timeseries with moving average
In this tutorial, we'll explore the art of plotting time series data, incorporating a dynamic moving average line chart overlaid on the primary plot.
Inspired by a visualization featured in the renowned French newspaper Le Monde, depicting the blooming patterns of cherry trees over time, we'll recreate this compelling chart.
With readily available code and straightforward explanations, you'll witness the prowess of React in seamlessly stacking multiple layers of rendering – from scatterplots to line charts and annotations.
Amidst the myriad visualizations of global warming, one stands out for its poetic resonance – an unexpected gem found within the pages of a French newspaper: the flourishing timelines of cherry trees in Japan.
Intriguingly, Yasuyuki Aono delved into ancient texts dating back to the 800s to unearth this data. Thanks to the meticulous efforts of Our World in Data in cleaning and refining the dataset, we're now presented with a captivating portrayal of our changing world.
Here is the chart we're learning to build today:
Plein floraison annuelleMoyenne de la floraison (20 ans)
Prerequisite
This graph combines elements of both a scatterplot and a line chart, making it essential to grasp the fundamentals of each chart type before diving in.
Good news, the react graph gallery has some very basic examples to illustrate those concepts!
Annotation
The deepest layer of this chart is the annotation that says 1950 - 2023. It uses stripes in the background. Stripes can be build in SVG as follow:
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/fix-canvas-blurry-dataviz.html b/fix-canvas-blurry-dataviz.html
index 2ca8d2a5..a5371839 100644
--- a/fix-canvas-blurry-dataviz.html
+++ b/fix-canvas-blurry-dataviz.html
@@ -1,7 +1,7 @@
-Fixing the blurry canvas on retina screens
Fixing the blurry canvas on retina screens
Drawing a chart on a canvas element instead of using svg elements can be a huge performance boost. However, it leads to a blurry and unreadable viz on retina screens if the resolution is not taken into account. Here is how to fix.
⬜ Pixel, Resolution and DPI
To understand why a canvas can get blurry on a retina screen, you first need to have some basic knowledge about how an image is displayed on a screen.
Screens are made up of thousands of tiny dots all bunched together called pixels. Each pixel has the ability to change its color. The total number of pixels on a screen differs from one monitor to another, we call it the screen resolution. If the screen has 1,024 pixels horizontally, and 768 vertically it has a 'resolution' of 1,024 x 768.
When you watch dataviz-inspiration.com on your screen, you actually watch thousands of pixels.
It's important to understand that 2 screens with the same physical size (let's say 30 inches) can have very different amount of pixels. The density of pixels on a screen is called DPI for dots per inch or ppi for pixels per inch.
Retina screens have a very high DPI, and it is where our troubbles begin
Physical vs CSS resolution
Let's say that you create a html element and give it a width of 100px using css. This is the css width. If you use a screen that has a very high resolution, pixels are very very small. As a result, your element of 100px would appear very small too on the screen.
To avoid this, each monitor applies a pixel ratio. On a retina screen this ratio equals 2. When you ask the monitor to draw an element of 100px, it will actually draw it with a length of 200px. This is the physical width.
In javascript, you can access this ratio with window.devicePixelRatio and here is the complete doc about it.
🐛 Canvas, High-DPI and the bug
Let's add a canvas element in our DOM, with a width of 100px. It is the equivalent of building an image, 100px wide, that we insert in the DOM.
If we display the result on a retina screen with a devicePixelRatio of 2, the image will be scaled up to 200px wide. The browser will interpolate pixels to make the image bigger, and it results in a blurry output.
To avoid this issue, we the canvas we're creating must be twice bigger on retina screen
A small img or canvas on a retina screen will be scaled up, resulting in a pixelated / blurry output.
🤦♂️ The 2 dimensions of the canvas element
There are 2 different ways to control the dimension of a canvas element.
width and height attributes: They control the size of the image that is created and inserted in the DOM. Default to 300px and 150px so always specify them.
css style: the canvas element can be styled using CSS. As a result we can also pass a width and a height here. It controls the size of the element on the screen.
Here is a html code snippet illustrating this concept:
Now, let's play with those 2 values to see what happens. In the examples below, a segment going from 0,0 (top left) to 100,100 is drawn.
→ Small image, big output
If I create a small image with my canvas (100x100) and output it in big (300x300), the browser has to scale it up to display it, resulting in a blurry output
Much better. But my diagonal is now wrong since it goes to 100,100, which is half way to 200,200. Fortunately javascript is here to the rescue. I will just have to use the scale function to automatically correct those coordinates.
🔨 Fixing the Canvas and Retina screens issue
To finally fix the retina bug, we need to:
Control the size of the canvas output using the css dimension
Find the device pixel ratio using window.devicePixelRatio
Create a bigger canvas image if the pixel ratio is over 1. This is done thanks to the width and height attributes.
Use the scale() function in our canvas context to correct our coordinates
Here are 2 examples. The first one does not apply the correction (left). The second does the correction (right)
On retina screens, the left shape is blurry when the right one is not.
To see the full code of those 2 examples, click the buttons below.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
+Fixing the blurry canvas on retina screens
Fixing the blurry canvas on retina screens
Drawing a chart on a canvas element instead of using svg elements can be a huge performance boost. However, it leads to a blurry and unreadable viz on retina screens if the resolution is not taken into account. Here is how to fix.
⬜ Pixel, Resolution and DPI
To understand why a canvas can get blurry on a retina screen, you first need to have some basic knowledge about how an image is displayed on a screen.
Screens are made up of thousands of tiny dots all bunched together called pixels. Each pixel has the ability to change its color. The total number of pixels on a screen differs from one monitor to another, we call it the screen resolution. If the screen has 1,024 pixels horizontally, and 768 vertically it has a 'resolution' of 1,024 x 768.
When you watch dataviz-inspiration.com on your screen, you actually watch thousands of pixels.
It's important to understand that 2 screens with the same physical size (let's say 30 inches) can have very different amount of pixels. The density of pixels on a screen is called DPI for dots per inch or ppi for pixels per inch.
Retina screens have a very high DPI, and it is where our troubbles begin
Physical vs CSS resolution
Let's say that you create a html element and give it a width of 100px using css. This is the css width. If you use a screen that has a very high resolution, pixels are very very small. As a result, your element of 100px would appear very small too on the screen.
To avoid this, each monitor applies a pixel ratio. On a retina screen this ratio equals 2. When you ask the monitor to draw an element of 100px, it will actually draw it with a length of 200px. This is the physical width.
In javascript, you can access this ratio with window.devicePixelRatio and here is the complete doc about it.
🐛 Canvas, High-DPI and the bug
Let's add a canvas element in our DOM, with a width of 100px. It is the equivalent of building an image, 100px wide, that we insert in the DOM.
If we display the result on a retina screen with a devicePixelRatio of 2, the image will be scaled up to 200px wide. The browser will interpolate pixels to make the image bigger, and it results in a blurry output.
To avoid this issue, we the canvas we're creating must be twice bigger on retina screen
A small img or canvas on a retina screen will be scaled up, resulting in a pixelated / blurry output.
🤦♂️ The 2 dimensions of the canvas element
There are 2 different ways to control the dimension of a canvas element.
width and height attributes: They control the size of the image that is created and inserted in the DOM. Default to 300px and 150px so always specify them.
css style: the canvas element can be styled using CSS. As a result we can also pass a width and a height here. It controls the size of the element on the screen.
Here is a html code snippet illustrating this concept:
Now, let's play with those 2 values to see what happens. In the examples below, a segment going from 0,0 (top left) to 100,100 is drawn.
→ Small image, big output
If I create a small image with my canvas (100x100) and output it in big (300x300), the browser has to scale it up to display it, resulting in a blurry output
Much better. But my diagonal is now wrong since it goes to 100,100, which is half way to 200,200. Fortunately javascript is here to the rescue. I will just have to use the scale function to automatically correct those coordinates.
🔨 Fixing the Canvas and Retina screens issue
To finally fix the retina bug, we need to:
Control the size of the canvas output using the css dimension
Find the device pixel ratio using window.devicePixelRatio
Create a bigger canvas image if the pixel ratio is over 1. This is done thanks to the width and height attributes.
Use the scale() function in our canvas context to correct our coordinates
Here are 2 examples. The first one does not apply the correction (left). The second does the correction (right)
On retina screens, the left shape is blurry when the right one is not.
To see the full code of those 2 examples, click the buttons below.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
Contact
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/heatmap.html b/heatmap.html
index 6997acb2..f5cf4ab3 100644
--- a/heatmap.html
+++ b/heatmap.html
@@ -1,4 +1,4 @@
-How to build a heatmap with React and D3.
Heatmap with React and d3.js
A heat map (or heatmap) is a chart type that shows the magnitude of a numeric variable as a color in two dimensions. This page is a step-by-step guide on how to build your own heatmap for the web, using React and D3.js.
It starts by describing how the data should be organized and potentially normalized. It then shows how to initialize the heatmap component, build band scales and add rectangles to get a first heatmap. Last but not least, responsiveness and the tooltip are described in depth and a real dataset is used to get a heatmap application. 🙇♂️.
The dataset is usually an array where each item provides information for a cell of the heatmap.
Each item is an object that requires at least a value property that is a number. This number will be used to color the cell.
Each item also requires an x and a y property, providing the position of the cell in the 2-d space. Note that those values are strings since anything can be used. We are dealing with ordinal scales here.
Note that you can add any kind of information to those cell objects. Such information can be included in tooltips later on.
Here is a minimal example of the data structure:
const data = [
+How to build a heatmap with React and D3.
Heatmap with React and d3.js
A heat map (or heatmap) is a chart type that shows the magnitude of a numeric variable as a color in two dimensions. This page is a step-by-step guide on how to build your own heatmap for the web, using React and D3.js.
It starts by describing how the data should be organized and potentially normalized. It then shows how to initialize the heatmap component, build band scales and add rectangles to get a first heatmap. Last but not least, responsiveness and the tooltip are described in depth and a real dataset is used to get a heatmap application. 🙇♂️.
The dataset is usually an array where each item provides information for a cell of the heatmap.
Each item is an object that requires at least a value property that is a number. This number will be used to color the cell.
Each item also requires an x and a y property, providing the position of the cell in the 2-d space. Note that those values are strings since anything can be used. We are dealing with ordinal scales here.
Note that you can add any kind of information to those cell objects. Such information can be included in tooltips later on.
Note that for the X and Y axis labels, just adding a set of svg text element does a pretty good job, so no need to build complicated axis components as for a scatterplot.
Most basic heatmap made with react and d3.js. d3 is used to compute scales, react for the rendering.
That's it, we have a first good looking heatmap!
The process used to build it with react is pretty close from building it with d3.js only. (Check the pure d3 implementation here).
Responsive Heatmap with react
The component above is not responsive. It expects 2 props called width and height and will render a Heatmap of those dimensions.
Making the Heatmap responsive requires adding a wrapper component that gets the dimension of the parent div, and listening to a potential dimension change. This is possible thanks to a hook called useDimensions that will do the job for us.
Note that for the X and Y axis labels, just adding a set of svg text element does a pretty good job, so no need to build complicated axis components as for a scatterplot.
Most basic heatmap made with react and d3.js. d3 is used to compute scales, react for the rendering.
That's it, we have a first good looking heatmap!
The process used to build it with react is pretty close from building it with d3.js only. (Check the pure d3 implementation here).
Responsive Heatmap with react
The component above is not responsive. It expects 2 props called width and height and will render a Heatmap of those dimensions.
Making the Heatmap responsive requires adding a wrapper component that gets the dimension of the parent div, and listening to a potential dimension change. This is possible thanks to a hook called useDimensions that will do the job for us.
I'm in the process of writing a complete blog post on the topic. Subscribe to the project to know when it's ready.
Tooltip
Adding a tooltip is an important improvement for a heatmap. It allows us to get as much detail as needed for each cell.
There are many different approaches to building tooltips, and I'm preparing a whole dedicated blog post on the topic.
In the example below I suggest using the same strategy as for scatterplots. So you probably want to read it there for an in-depth explanation.
→ Two layers: renderer and tooltip
The first task is to split the Heatmap component into 2 layers. The first layer called Renderer will render the cells as seen previously. The second is an absolute div put on top of the first one, used only to show the tooltip div.
This way, the x and y coordinates of cells in the first layer match with the coordinate of the second layer.
On top of the 2 layers, we need a state that stores information about the cell being hovered over. You can create it with a useState statement. I usually call it interactionData in this website.
This state is passed to the Tooltip layer. The function to update it (the "setter") is passed to the Renderer layer. When the user hovers over a cell, this setter is triggered to update the state and thus the tooltip.
The heatmap cells listen to onMouseEnter events and update the tooltip state (hoveredCell) with accurate coordinates when it happens.
This state is passed to the Tooltip component. It renders a div at the right position thanks to the information. A bit of smart css is used to make it pretty and include a little arrow.
This heatmap has a tooltip. Hover over a cell to get its exact value.
There is much more to say about tooltips but hopefully that should get you started. Subscribe to the gallery, I'll post more on this topic soon.
Heatmap inspiration
If you're looking for inspiration to create your next Heatmap, note that dataviz-inspiration.com showcases many examples. Definitely the best place to get ... inspiration!
dataviz-inspiration.com showcases hundreds of stunning dataviz projects. Have a look to get some ideas on how to make your Heatmap looks good!
A heatmap uses a color scale to encode a numeric value into a color. As a result, it is very much advised to add a color legend to explicit how this color scale works.
Let's consider a variable that goes from 0 to 100. We want to encode 0 in blue and 100 in purple. The color scale is built thanks to the scaleLinear() function of d3 as described above.
→ A common state
On top of the 2 layers, we need a state that stores information about the cell being hovered over. You can create it with a useState statement. I usually call it interactionData in this website.
This state is passed to the Tooltip layer. The function to update it (the "setter") is passed to the Renderer layer. When the user hovers over a cell, this setter is triggered to update the state and thus the tooltip.
The heatmap cells listen to onMouseEnter events and update the tooltip state (hoveredCell) with accurate coordinates when it happens.
This state is passed to the Tooltip component. It renders a div at the right position thanks to the information. A bit of smart css is used to make it pretty and include a little arrow.
This heatmap has a tooltip. Hover over a cell to get its exact value.
There is much more to say about tooltips but hopefully that should get you started. Subscribe to the gallery, I'll post more on this topic soon.
Heatmap inspiration
If you're looking for inspiration to create your next Heatmap, note that dataviz-inspiration.com showcases many examples. Definitely the best place to get ... inspiration!
dataviz-inspiration.com showcases hundreds of stunning dataviz projects. Have a look to get some ideas on how to make your Heatmap looks good!
A heatmap uses a color scale to encode a numeric value into a color. As a result, it is very much advised to add a color legend to explicit how this color scale works.
Let's consider a variable that goes from 0 to 100. We want to encode 0 in blue and 100 in purple. The color scale is built thanks to the scaleLinear() function of d3 as described above.
A color legend built with react, canvas and d3.
The trick here is to create a canvas element of the desired width and height. Then, loop from left to right and add one rectangle for each pixel with the corresponding color using the same color scale as the one used on the chart. It's important to do it in canvas: you don't want to add 300 elements in your DOM if your legend is 300px wide.
Once the canvas element is instantiated with a ref, you can draw the color scale thanks to a useEffect like this:
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/hexbin-map.html b/hexbin-map.html
index 73f54ef9..9e249f66 100644
--- a/hexbin-map.html
+++ b/hexbin-map.html
@@ -1,4 +1,4 @@
-How to build a hexbin map component with React and D3.
Hexbin Map
A hexbin map is a visual representation of data that aggregates individual data points into hexagonal bins or cells, typically used for spatial analysis. It helps to condense large datasets and identify patterns or trends within specific geographic regions.
Two types of hexbin map exist. The first one is based on a geoJsonfile that provides the hexagon boundaries. It is similar to a choropleth map, with hexagons instead of real regions. The second is actually a 2d density chart, but with GPS locations for the x and y coordinates.
This page is a long form tutorial explaining how to build hexbin maps using react and d3.js. It provides interactive examples for both types of hexbin maps with code explanation and should get you started for your web app.
Drawing a map using react and d3.js usually relies on a geoJson file. This file format provides information for geographical regions. It stores the 2d coordinates of shapes.
This is the way it looks:
{
+How to build a hexbin map component with React and D3.
Hexbin Map
A hexbin map is a visual representation of data that aggregates individual data points into hexagonal bins or cells, typically used for spatial analysis. It helps to condense large datasets and identify patterns or trends within specific geographic regions.
Two types of hexbin map exist. The first one is based on a geoJsonfile that provides the hexagon boundaries. It is similar to a choropleth map, with hexagons instead of real regions. The second is actually a 2d density chart, but with GPS locations for the x and y coordinates.
This page is a long form tutorial explaining how to build hexbin maps using react and d3.js. It provides interactive examples for both types of hexbin maps with code explanation and should get you started for your web app.
Drawing a map using react and d3.js usually relies on a geoJson file. This file format provides information for geographical regions. It stores the 2d coordinates of shapes.
I'm in the process of writing a complete blog post on the topic. Subscribe to the project to know when it's ready.
Hexbin inspiration
If you're looking for inspiration to create your next Hexbin, note that dataviz-inspiration.com showcases many examples. Definitely the best place to get ... inspiration!
dataviz-inspiration.com showcases hundreds of stunning dataviz projects. Have a look to get some ideas on how to make your Hexbin looks good!
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/hierarchical-edge-bundling.html b/hierarchical-edge-bundling.html
index 7fc146e3..857689f3 100644
--- a/hierarchical-edge-bundling.html
+++ b/hierarchical-edge-bundling.html
@@ -1,4 +1,4 @@
-Hierarchical Edge Bundling | React Graph Gallery
Hierarchical edge bundling
A hierarchical edge bundling chart allows to visualize relationships between entities organized in a hierarchy. The idea is to bundle the adjacency edges together to decrease the clutter usually observed in complex networks.
This page explains how to build a hierarchical edge bundling chart using d3.js to compute the node position, and React to render the nodes and edges. It starts by describing the required data format, explains how to build a very basic hierarchical edge bundling and then shows how to customize it.
The dataset describes a hierarchy using a recursive structure.
Each item in this structure is called a node. The lowest nodes of the hierarchy being called leaves.
The dataset is an object that has at least 3 properties: name, value and children. children is an array of nodes that have this structure too.
This kind of data is very close to what's required for a dendrogram. But an additional property is added for the leaves: links. It provides a list of all the other leaves this leaf is connected with.
A hierarchical edge bundling chart allows to visualize relationships between entities organized in a hierarchy. The idea is to bundle the adjacency edges together to decrease the clutter usually observed in complex networks.
This page explains how to build a hierarchical edge bundling chart using d3.js to compute the node position, and React to render the nodes and edges. It starts by describing the required data format, explains how to build a very basic hierarchical edge bundling and then shows how to customize it.
The dataset describes a hierarchy using a recursive structure.
Each item in this structure is called a node. The lowest nodes of the hierarchy being called leaves.
The dataset is an object that has at least 3 properties: name, value and children. children is an array of nodes that have this structure too.
This kind of data is very close to what's required for a dendrogram. But an additional property is added for the leaves: links. It provides a list of all the other leaves this leaf is connected with.
Resulting in our first hierarchical edge bundling example 🎉
A first hierarchical edge bundling chart made with d3 and react.
Coming soon
Using canvas for rendering is often a requirement when the number of nodes gets big. Interactivity is often necessary, for hover effect or to collapse a part of the tree. It also possible to map the node circle size to a numeric variable.
This will come soon! I have a newsletter called the dataviz universe where I share my latest updates.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/histogram.html b/histogram.html
index 2e7f929f..e8e6d421 100644
--- a/histogram.html
+++ b/histogram.html
@@ -1,4 +1,4 @@
-How to build a histogram with React and D3.
Histogram with React and d3.js
A histogram is a chart type that shows the distribution of a numeric variable. This page is a step-by-step guide on how to build your own histogram for the web, using React and D3.js.
It starts by describing how the data should be organized and how to initialize the histogram component. It then explains how to compute the buckets composing the histogram. Once this is done, it shows how to render the bars and suggests a few variations. 🙇♂️.
Building a histogram only requires a set of numeric values.
As a result, the dataset is pretty simple: an array of numbers.
Here is a minimal example of the data structure:
const data = [1, 2, 2, 2, 3, 4, 5, 6, 6, 6, 9]
Component skeleton
The goal here is to create a Histogram component that will be stored in a Histogram.tsx file. This component requires 3 props to render: a width, a height, and some data.
The shape of the data is described above. The width and height will be used to render an svg element in the DOM, in which we will insert the histogram.
To put it in a nutshell, that's the skeleton of our Histogram component:
import * as d3 from "d3"; // we will need d3.js
+How to build a histogram with React and D3.
Histogram with React and d3.js
A histogram is a chart type that shows the distribution of a numeric variable. This page is a step-by-step guide on how to build your own histogram for the web, using React and D3.js.
It starts by describing how the data should be organized and how to initialize the histogram component. It then explains how to compute the buckets composing the histogram. Once this is done, it shows how to render the bars and suggests a few variations. 🙇♂️.
Building a histogram only requires a set of numeric values.
As a result, the dataset is pretty simple: an array of numbers.
Here is a minimal example of the data structure:
const data = [1, 2, 2, 2, 3, 4, 5, 6, 6, 6, 9]
Component skeleton
The goal here is to create a Histogram component that will be stored in a Histogram.tsx file. This component requires 3 props to render: a width, a height, and some data.
The shape of the data is described above. The width and height will be used to render an svg element in the DOM, in which we will insert the histogram.
To put it in a nutshell, that's the skeleton of our Histogram component:
import * as d3 from "d3"; // we will need d3.js
type HistogramProps = {
width: number;
@@ -82,7 +82,7 @@
}, []);
return dimensions;
-}
I'm in the process of writing a complete blog post on the topic. Subscribe to the project to know when it's ready.
Histogram inspiration
If you're looking for inspiration to create your next Histogram, note that dataviz-inspiration.com showcases many examples. Definitely the best place to get ... inspiration!
dataviz-inspiration.com showcases hundreds of stunning dataviz projects. Have a look to get some ideas on how to make your Histogram looks good!
Once you've understood how to build a basic histogram with d3 and react, it opens an infinite world of customization. Here are a few examples showing how to add several groups on the same axis or how to use small multiple with histograms to compare distributions.
Click on the overview below to get details and code.
Dataset transition
The last step needed for a powerful histogram React component is a proper way to transition between various datasets. When the data prop updates, we need a stunning way to transition to the new values.
There are many different strategies to approach this problem. I suggest to rely on the react-spring library that has everything we need to compute spring animations.
Instead of rendering usual rect elements, the library provides a animated.rect element, that is linked to a useSpringhook.
A histogram that smoothly transition from 1 dataset to another
This is how the Rectangle component I use looks like:
Rectangle: a component that animates the transition of a rect
import { useSpring, animated } from "@react-spring/web";
+}
I'm in the process of writing a complete blog post on the topic. Subscribe to the project to know when it's ready.
Histogram inspiration
If you're looking for inspiration to create your next Histogram, note that dataviz-inspiration.com showcases many examples. Definitely the best place to get ... inspiration!
dataviz-inspiration.com showcases hundreds of stunning dataviz projects. Have a look to get some ideas on how to make your Histogram looks good!
Once you've understood how to build a basic histogram with d3 and react, it opens an infinite world of customization. Here are a few examples showing how to add several groups on the same axis or how to use small multiple with histograms to compare distributions.
Click on the overview below to get details and code.
Dataset transition
The last step needed for a powerful histogram React component is a proper way to transition between various datasets. When the data prop updates, we need a stunning way to transition to the new values.
There are many different strategies to approach this problem. I suggest to rely on the react-spring library that has everything we need to compute spring animations.
Instead of rendering usual rect elements, the library provides a animated.rect element, that is linked to a useSpringhook.
A histogram that smoothly transition from 1 dataset to another
This is how the Rectangle component I use looks like:
Rectangle: a component that animates the transition of a rect
import { useSpring, animated } from "@react-spring/web";
type RectangleProps = {
width: number;
@@ -123,4 +123,4 @@
};
Animation in dataviz using React is a big topic. It's impossible to go in depth here! I will publish a dedicated blog post on the topic soon. Please subscribe to the newsletter if you want to be notified.
Mind the bin size
When creating a histogram, remember that bin size plays a crucial role in shaping the story your data tells.
For example, the histogram below displays the finish times of 400,000 marathoners. With smaller bins, patterns around 3h, 3:30h, and 4h emerge. However, using larger bins can obscure these details entirely!
Number of bins (target):300
A histogram with a slider that controls the bin size. With big bins, the breaks in the distribution are completely hidden.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/index.html b/index.html
index 504c6953..377db2a8 100644
--- a/index.html
+++ b/index.html
@@ -1,4 +1,4 @@
-Learn to build unique charts with React
Build unique React charts
Have you ever wanted to create your own chart for the web?
Based on hundreds of graph examples, this gallery guides you through the basic concepts of data visualization with React and D3.js. It also provides ready-to-use templates to get started quicker.
Stop using pre-made dataviz components → imagination will become the only boundary to your creativity.
The gallery is organized by chart types following the data-to-viz classification.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
+Learn to build unique charts with React
Build unique React charts
Have you ever wanted to create your own chart for the web?
Based on hundreds of graph examples, this gallery guides you through the basic concepts of data visualization with React and D3.js. It also provides ready-to-use templates to get started quicker.
Stop using pre-made dataviz components → imagination will become the only boundary to your creativity.
The gallery is organized by chart types following the data-to-viz classification.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/interactivity.html b/interactivity.html
index 699bc495..9f3621c8 100644
--- a/interactivity.html
+++ b/interactivity.html
@@ -1,4 +1,4 @@
-Interactivity
Interactivity
Interactivity is a key feature of any chart displayed on the web. It includes tooltip, hover effect, cross-graph interaction, zooming, panning and more.
Interactivity is a big topic. This section aims at giving general tips and tricks on the topic together with applied examples ready to be used in your project.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
+Interactivity
Interactivity
Interactivity is a key feature of any chart displayed on the web. It includes tooltip, hover effect, cross-graph interaction, zooming, panning and more.
Interactivity is a big topic. This section aims at giving general tips and tricks on the topic together with applied examples ready to be used in your project.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/line-chart.html b/line-chart.html
index 512ae5b1..d3de9ef7 100644
--- a/line-chart.html
+++ b/line-chart.html
@@ -1,4 +1,4 @@
-Line chart | The React Graph Gallery
Line chart
A line chart or line graph displays the evolution of one or several numeric variables. This page is a step-by-step guide on how to build your own line chart component for the web, using React and D3.js.
It starts by describing how the data should be organized and how to initialize the react component. It then explains how to compute the scales, and axes. Once this is done, it shows how to render the lines and suggests a few variations. 🙇♂️.
The dataset required to build a line chart is usually an array where each item is an object providing the x and the y values of the data point.
Here is a minimal example:
const data = [
+Line chart | The React Graph Gallery
Line chart
A line chart or line graph displays the evolution of one or several numeric variables. This page is a step-by-step guide on how to build your own line chart component for the web, using React and D3.js.
It starts by describing how the data should be organized and how to initialize the react component. It then explains how to compute the scales, and axes. Once this is done, it shows how to render the lines and suggests a few variations. 🙇♂️.
Click on the buttons to trigger a smooth transition between the 2 line charts.
Animation in dataviz using React is a big topic. It's impossible to go in-depth here! I will publish a dedicated blog post on the topic soon. Please subscribe to the newsletter if you want to be notified.
Variations
You now have the basic understanding on how to build a basic line chart component with React and d3.js. Below are a few examples showing how to build more complex graphs based on those principles.
Next
The react graph gallery is under heavy development. Here is a list of things that will be added soon.
ToDoHow to add a tooltip
ToDoMulti groups line charts aka Spaghetti chart
ToDoSync with a bar chart
ToDoDual Y Axis
ToDoInline legend with Reppel
Subscribe to the gallery to know when it is ready!
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/lollipop-plot.html b/lollipop-plot.html
index e09ce610..48c0df8e 100644
--- a/lollipop-plot.html
+++ b/lollipop-plot.html
@@ -1,4 +1,4 @@
-Lollipop plot with React
Lollipop plot
A lollipop plot is a variation of the more common barplot. This page is a step-by-step guide on how to build your own lollipop for the web, using React and D3.js.
It starts by describing how the data should be organized and how to initialize the lollipop component. It explains how to build the scales and axes and how to add the shapes. A few variations are described and a focus is made on the hover interaction. 🙇♂️.
The dataset required to build a lollipop is usually an array where each item is an object providing the name and the value of the group.
Here is a minimal example:
const data = [
+Lollipop plot with React
Lollipop plot
A lollipop plot is a variation of the more common barplot. This page is a step-by-step guide on how to build your own lollipop for the web, using React and D3.js.
It starts by describing how the data should be organized and how to initialize the lollipop component. It explains how to build the scales and axes and how to add the shapes. A few variations are described and a focus is made on the hover interaction. 🙇♂️.
→ For the second effect, 2 svg rectangles must be drawn. The first one fills the full width and height of the row. It is the one that triggers the mouse event. (It is important to remember that a svg g element does not trigger mouse events. Only what is drawn inside it does). The second rectangle is the one that we see. We can add some vertical padding to it since it is not use for mouse detection.
Try to hover a row in the lollipop above to reveal the hover interaction.
Data transition
It is very common to deal with various variables and compare the behaviour of some data items for them. It adds a nice touch to the graph to smoothly transition between 2 states using a quick animation.
For the example below I rely on the react-spring library. This lib allows to quickly create spring animations using javascript. It results in a very a natural transition that can be interrupted without restarting from 0. (try to toggle between datasets quickly).
It would be too long to explain the code here. Instead, I'm currently writing a set of dedicated tutorials. Please subscribe to the newsletter to know when this will be released.
A lollipop chart with smooth transition between dataset.
Variation
Check a few other examples of the gallery involving lollipop plots:
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/map.html b/map.html
index 74345cde..a321fc6f 100644
--- a/map.html
+++ b/map.html
@@ -1,4 +1,4 @@
-How to build a background map with React and D3.
Background Map
This section is dedicated to background maps. It is the fundation required to build more interesting dataviz-related mapslike bubble maps, choropleth maps and more.
The interactive sandboxes and explanation below explain how to read a geoJson file and draw its content using React. It also show how useful the d3-geo module can be to deal with this format, notably to control the various existing projections.
Last but not least, this document also briefly explains how Leaflet can be used in a React environment to display interactive maps.
The first thing you need to build a map is the 2d coordinates of the boundaries of the regions you want to represent. If you are trying to build a world map, you need to know where the country boundaries are located 🤷♀️.
Several formats exist to store such a piece of information. When working with d3.js, the expected format is geoJSON. A geoJSON file looks pretty much like this:
Here is a minimal example of the data structure:
{
+How to build a background map with React and D3.
Background Map
This section is dedicated to background maps. It is the fundation required to build more interesting dataviz-related mapslike bubble maps, choropleth maps and more.
The interactive sandboxes and explanation below explain how to read a geoJson file and draw its content using React. It also show how useful the d3-geo module can be to deal with this format, notably to control the various existing projections.
Last but not least, this document also briefly explains how Leaflet can be used in a React environment to display interactive maps.
The first thing you need to build a map is the 2d coordinates of the boundaries of the regions you want to represent. If you are trying to build a world map, you need to know where the country boundaries are located 🤷♀️.
Several formats exist to store such a piece of information. When working with d3.js, the expected format is geoJSON. A geoJSON file looks pretty much like this:
I'm in the process of writing a complete blog post on the topic. Subscribe to the project to know when it's ready.
Map inspiration
If you're looking for inspiration to create your next Map, note that dataviz-inspiration.com showcases many examples. Definitely the best place to get ... inspiration!
dataviz-inspiration.com showcases hundreds of stunning dataviz projects. Have a look to get some ideas on how to make your Map looks good!
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/network-chart.html b/network-chart.html
index e0fa0ac7..ff067c51 100644
--- a/network-chart.html
+++ b/network-chart.html
@@ -1,4 +1,4 @@
-How to build a Network Diagram with React and D3.
Network diagram
A network diagram shows the interconnections between entities. It is consituted by nodes that represent entities and by links that show relationships between entities.
This page is a step by step tutorial explaining how to build a network diagram component with React and D3.js. It relies on the d3-force plugin to compute the node positions. It comes with explanations and code sandboxes. It starts by simple concepts like how to format the data and how to draw nodes and links in SVG, and then goes further with hover effect, tooltip and more.
Two layers of information are required to build a network diagram: a list of nodes to build the circles and a list of links to build the lines.
Many different data structures can be used to store such information. In this tutorial I suggest to start with the following:
export const data = {
+How to build a Network Diagram with React and D3.
Network diagram
A network diagram shows the interconnections between entities. It is consituted by nodes that represent entities and by links that show relationships between entities.
This page is a step by step tutorial explaining how to build a network diagram component with React and D3.js. It relies on the d3-force plugin to compute the node positions. It comes with explanations and code sandboxes. It starts by simple concepts like how to format the data and how to draw nodes and links in SVG, and then goes further with hover effect, tooltip and more.
I'm in the process of writing a complete blog post on the topic. Subscribe to the project to know when it's ready.
Network inspiration
If you're looking for inspiration to create your next Network, note that dataviz-inspiration.com showcases many examples. Definitely the best place to get ... inspiration!
dataviz-inspiration.com showcases hundreds of stunning dataviz projects. Have a look to get some ideas on how to make your Network looks good!
The exausthive list of forces that can be applied to nodes is available in the official documentation. Here is an overview of the main ones:
d3.forceManyBody() → simulates attraction between nodes if its strength is positive, repulsion otherwise.
d3.forceCenter() → translates nodes uniformly so that the mean position of all nodes is at a given position.
d3.forceCollide() → tries to avoid node collision and overlap. You can provide a radius and a strength.
d3.forceLink() → pushes linked nodes together or apart according to the desired link distance.
d3.forceX() → applies a force toward a X position to all nodes. d3.forceY() is also available.
Radius used to avoid collision: 25
ManyBody strength: 0
ForceY strength: 0.1
A first network diagram built using react and d3-force.
Variations
Once you've understood how to build a basic network diagram with d3 and react, it opens an infinite world of customization. Here are a few examples using the same concepts.
Click on the overview below to get details and code.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/parallel-plot.html b/parallel-plot.html
index c72d0377..ea5b845d 100644
--- a/parallel-plot.html
+++ b/parallel-plot.html
@@ -1,4 +1,4 @@
-Parallel Coordinate chart | React graph gallery
Parallel coordinates
A parallel coordinate chart is a type of visualization used to represent multivariate data on a two-dimensional plane by plotting each variable as a separate axis arranged in parallel, and then connecting the data points with lines.
This page is a step-by-step guide on how to build your own parallel coordinate chart for the web, using React (for rendering) and D3.js (to compute the axis, and shape coordinates).
It starts by describing how the data should be organized and how to initialize the parallel coordinate component. It then explains how to compute axis dynamically, and plot the lines and axis. Once this is done, it shows how to deal with scaling and how to add an interactive legend. 🙇♂️.
The dataset provides several numeric values for a set of data points. It can also add some categorical variables that can be added to customize the marker colors.
The suggested data structure is an array of object, where each object is a data point. It can have as many numeric properties as needed.
A parallel coordinate chart is a type of visualization used to represent multivariate data on a two-dimensional plane by plotting each variable as a separate axis arranged in parallel, and then connecting the data points with lines.
This page is a step-by-step guide on how to build your own parallel coordinate chart for the web, using React (for rendering) and D3.js (to compute the axis, and shape coordinates).
It starts by describing how the data should be organized and how to initialize the parallel coordinate component. It then explains how to compute axis dynamically, and plot the lines and axis. Once this is done, it shows how to deal with scaling and how to add an interactive legend. 🙇♂️.
The dataset provides several numeric values for a set of data points. It can also add some categorical variables that can be added to customize the marker colors.
The suggested data structure is an array of object, where each object is a data point. It can have as many numeric properties as needed.
I'm in the process of writing a complete blog post on the topic. Subscribe to the project to know when it's ready.
Parallel inspiration
If you're looking for inspiration to create your next Parallel, note that dataviz-inspiration.com showcases many examples. Definitely the best place to get ... inspiration!
dataviz-inspiration.com showcases hundreds of stunning dataviz projects. Have a look to get some ideas on how to make your Parallel looks good!
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/pie-plot.html b/pie-plot.html
index 6d2fe833..fd34fba7 100644
--- a/pie-plot.html
+++ b/pie-plot.html
@@ -1,4 +1,4 @@
-Pie chart with React
Pie chart
A pie chart is a type of graph used to visually represent data as a circular, segmented chart. The chart is divided into slices, where the size of each slice represents the proportion of data that falls within a particular category or group.
This post explains how to build a pie chart with react, using the pie() function of d3.js. It describes the expected data format, how the Pie component must be structured, how to compute the slice positions and how to render those slices. Last but not least, it provides the implementation for common use-cases like hover effect and data transition.
The dataset required to build a pie chart is pretty simple. It is an array where each item represents a group of the pie chart. Each item is an object with 2 properties. They provide the group name (name) and its value (value).
For instance, here is the dataset used for the simple pie chart below:
const data = [
+Pie chart with React
Pie chart
A pie chart is a type of graph used to visually represent data as a circular, segmented chart. The chart is divided into slices, where the size of each slice represents the proportion of data that falls within a particular category or group.
This post explains how to build a pie chart with react, using the pie() function of d3.js. It describes the expected data format, how the Pie component must be structured, how to compute the slice positions and how to render those slices. Last but not least, it provides the implementation for common use-cases like hover effect and data transition.
The dataset required to build a pie chart is pretty simple. It is an array where each item represents a group of the pie chart. Each item is an object with 2 properties. They provide the group name (name) and its value (value).
For instance, here is the dataset used for the simple pie chart below:
I'm in the process of writing a complete blog post on the topic. Subscribe to the project to know when it's ready.
Pie Chart inspiration
If you're looking for inspiration to create your next Pie Chart, note that dataviz-inspiration.com showcases many examples. Definitely the best place to get ... inspiration!
dataviz-inspiration.com showcases hundreds of stunning dataviz projects. Have a look to get some ideas on how to make your Pie Chart looks good!
The Pie component expects a data prop. What should we do when this data changes?
By default, the chart will update instantly, with no transition. Adding a smooth transition gives a nice polish touch to the graph. Try to switch between the 2 datasets below to see the animation in action.
The code below relies on the react-spring library. Instead of rendering a path for each slice, it uses a animated.path component that handles the spring animation for us.
The implementation is not trivial. I plan to publish a full tutorial on react-spring for data visualization soon. You can subscribe here to be notified when it is ready.
A donut chart with clean inline legends, built thanks to the centroid function of d3.js.
Note: check the blue group that appears / disappears between dataset. This kind of enter/exit pattern is something to keep in mind when building animations.
Pie chart to barplot
Pie charts are often criticized since angles are hard to read. Let's represent the same data using a pie chart or a barplot, to see what's the most insightful 🤷♂️.
Note that here we animate the transition between different shape types: each arc becomes a rectangle and reciprocally. This is made possible thanks to the flubber library, used in coordination with react-spring.
Once more, a full tutorial is needed here. You can subscribe here to be notified when it is ready. In the meanwhile, the code of this specific example is provided below.
Transition from a pie chart to a barplot with a smooth animation using the buttons on top.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/radar-chart.html b/radar-chart.html
index 5c896c98..2d565790 100644
--- a/radar-chart.html
+++ b/radar-chart.html
@@ -1,4 +1,4 @@
-Radar chart | React graph gallery
Radar Chart
A radar or spider or web chart is a two-dimensional chart type designed to plot one or more series of values over multiple quantitative variables. Each variable has its own axis, all axes are joined in the center of the figure.
This page is a step-by-step guide on how to build your own radar chart for the web, using React (for rendering) and D3.js (to compute the axis, and shape coordinates).
It starts by describing how the data should be organized and how to initialize the Radar component. It then explains how to compute axis dynamically, and plot the lines and axis. Once this is done, it shows how to deal with scaling and how to add an interactive legend. 🙇♂️.
The dataset provides several numeric values for a set of data items.
The suggested data structure is an array of object, where each object is a data item. It can have as many numeric properties as needed. It also has a name property that identifies the data item.
Here is a minimal example of the data structure:
const data = [
+Radar chart | React graph gallery
Radar Chart
A radar or spider or web chart is a two-dimensional chart type designed to plot one or more series of values over multiple quantitative variables. Each variable has its own axis, all axes are joined in the center of the figure.
This page is a step-by-step guide on how to build your own radar chart for the web, using React (for rendering) and D3.js (to compute the axis, and shape coordinates).
It starts by describing how the data should be organized and how to initialize the Radar component. It then explains how to compute axis dynamically, and plot the lines and axis. Once this is done, it shows how to deal with scaling and how to add an interactive legend. 🙇♂️.
The dataset provides several numeric values for a set of data items.
The suggested data structure is an array of object, where each object is a data item. It can have as many numeric properties as needed. It also has a name property that identifies the data item.
I'm in the process of writing a complete blog post on the topic. Subscribe to the project to know when it's ready.
Spider / Radar inspiration
If you're looking for inspiration to create your next Spider / Radar, note that dataviz-inspiration.com showcases many examples. Definitely the best place to get ... inspiration!
dataviz-inspiration.com showcases hundreds of stunning dataviz projects. Have a look to get some ideas on how to make your Spider / Radar looks good!
The process to get a radar chart with several groups is very similar to the previous example.
We just need to create a color scale and add a shape for each item of the dataset through a loop. Do not try to add too many groups on the same figure, it make it totally unreadable.
A radar chart with several groups displayed on the same figure. Made with React and d3.js
ToDospider chart with small multiple to make it more readable
Animation
It is common to have a radar chart that is updated when a button is clicked on the application. It is possible to implement a smooth, animated transition between states thanks to the react-spring library.
The following example illustrates this with a real world example. The radar chart is animated, together with a line chart and a lollipop.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/react-d3-dataviz-course.html b/react-d3-dataviz-course.html
index b2a1fd4c..0be28b1c 100644
--- a/react-d3-dataviz-course.html
+++ b/react-d3-dataviz-course.html
@@ -1,4 +1,4 @@
-Dataviz with react online course
Dataviz with React fundamentals
Mastering chart creation with React and D3 involves understanding a multitude of concepts. It took me 5 years to get them.
The gallery offers hundreds of examples and several ready-to-use components to help you get started.
However, to truly grasp the big picture, guidance is essential. It took me thousands of hours to achieve this understanding, and I'm here to guide you!
Module 0
Introduction
While you're likely familiar with React, you might not know that D3 is the essential JavaScript library for data visualization.
This module provides a quick overview of the tools needed to create great charts in a browser.
Module 2
Scales
Your svg area goes from 0 to 500px. Your data goes from 0 to 100. If the value of a data point is 30, where should you draw it?
You need a scale to find out.
Module 4
Responsiveness
Some people have big monitors when others will read your work on their phone. Let's see how to make your graph look good in any situation.
Module 6
Tooltip
Let's check how to add a tooltip. Give a tooltip definition. Give several tooltip component starters.
Module 8
Legend
Those little legends are often small, but as complicated to build as the main graph. This module provides a few ready to use legend components.
Module 10
Canvas
With thousands of shapes on your graph, using SVG will make your graph slow.
Canvas is an alternative way to draw on a screen. Much more performant, but harder to deal with.
Module 1
SVG
A graph is basically a compendium of shapes drawn on a screen.
The most common way to draw shapes in a browser is to use SVG. Let's learn the essential of what SVG is, step by step.
Module 3
Axes
Now that we know how to add shapes on the graph, it is time to add context to it. Let's see how to draw axes.
Module 5
Hover effect
You want something to happen when a graph element is hovered. This module dives into several strategies using CSS and Javascript. It provides a clear mental modal of the main use cases and how to deal with each of them.
Module 7
Reading data
Your data can be a json file or a tabular format. It can be hosted locally or available through an API. How can you read it?
Module 9
Animation
Let's dig into those smooth dataset transition that make the viz magical. It's challenging, but react-spring is of great help here.
Contact
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
+Dataviz with react online course
Dataviz with React fundamentals
Mastering chart creation with React and D3 involves understanding a multitude of concepts. It took me 5 years to get them.
The gallery offers hundreds of examples and several ready-to-use components to help you get started.
However, to truly grasp the big picture, guidance is essential. It took me thousands of hours to achieve this understanding, and I'm here to guide you!
Module 0
Introduction
While you're likely familiar with React, you might not know that D3 is the essential JavaScript library for data visualization.
This module provides a quick overview of the tools needed to create great charts in a browser.
Module 2
Scales
Your svg area goes from 0 to 500px. Your data goes from 0 to 100. If the value of a data point is 30, where should you draw it?
You need a scale to find out.
Module 4
Responsiveness
Some people have big monitors when others will read your work on their phone. Let's see how to make your graph look good in any situation.
Module 6
Tooltip
Let's check how to add a tooltip. Give a tooltip definition. Give several tooltip component starters.
Module 8
Legend
Those little legends are often small, but as complicated to build as the main graph. This module provides a few ready to use legend components.
Module 10
Canvas
With thousands of shapes on your graph, using SVG will make your graph slow.
Canvas is an alternative way to draw on a screen. Much more performant, but harder to deal with.
Module 1
SVG
A graph is basically a compendium of shapes drawn on a screen.
The most common way to draw shapes in a browser is to use SVG. Let's learn the essential of what SVG is, step by step.
Module 3
Axes
Now that we know how to add shapes on the graph, it is time to add context to it. Let's see how to draw axes.
Module 5
Hover effect
You want something to happen when a graph element is hovered. This module dives into several strategies using CSS and Javascript. It provides a clear mental modal of the main use cases and how to deal with each of them.
Module 7
Reading data
Your data can be a json file or a tabular format. It can be hosted locally or available through an API. How can you read it?
Module 9
Animation
Let's dig into those smooth dataset transition that make the viz magical. It's challenging, but react-spring is of great help here.
Contact
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/react-dataviz-animation-with-react-spring.html b/react-dataviz-animation-with-react-spring.html
index bf702098..2e5c3d82 100644
--- a/react-dataviz-animation-with-react-spring.html
+++ b/react-dataviz-animation-with-react-spring.html
@@ -1,7 +1,7 @@
-React-spring for data visualization
React-spring for data visualization
This post is dedicated to react-spring, a javascript library for spring animations. It quickly explains what it is and how it works, and then explains how it can be used in the field of data visualization.
A minimal react-spring dataviz example. A viz component expect a position and a color prop. But it smoothly animates the transition.
What is spring animation
Spring animations are different than typical CSS easing animation. They're built on spring physics, which gives a more fluid and organic feel. The best way to be introduced to this concept is to read Josh Comeau's friendly Introduction to Spring Physics
To quote him directly:
Spring physics are like a secret ingredient; they make all animations taste better
Basic react spring animation
The best tool to build a spring animation in the react world is react-spring, a library dedicated to it. If you're not familiar with it already you probably want to take a look at their home page.
Let's start with a basic example showing how to animate a very basic viz component that just renders a circle.
A very basic animation using react and react-spring.
The useSpring funtion can take 2 types of input: an object or a function
if object, it returns an object that has the values of our animation
if function, it returns an array of length 2. First item is the object mentionned above. item 2 is the api that controls the spring. api is used for imperative style
Animating axes
A common hassle is to animate axes, since it's a lot of svg elements.
A very basic animation using react and react-spring.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
+React-spring for data visualization
React-spring for data visualization
This post is dedicated to react-spring, a javascript library for spring animations. It quickly explains what it is and how it works, and then explains how it can be used in the field of data visualization.
A minimal react-spring dataviz example. A viz component expect a position and a color prop. But it smoothly animates the transition.
What is spring animation
Spring animations are different than typical CSS easing animation. They're built on spring physics, which gives a more fluid and organic feel. The best way to be introduced to this concept is to read Josh Comeau's friendly Introduction to Spring Physics
To quote him directly:
Spring physics are like a secret ingredient; they make all animations taste better
Basic react spring animation
The best tool to build a spring animation in the react world is react-spring, a library dedicated to it. If you're not familiar with it already you probably want to take a look at their home page.
Let's start with a basic example showing how to animate a very basic viz component that just renders a circle.
A very basic animation using react and react-spring.
The useSpring funtion can take 2 types of input: an object or a function
if object, it returns an object that has the values of our animation
if function, it returns an array of length 2. First item is the object mentionned above. item 2 is the api that controls the spring. api is used for imperative style
Animating axes
A common hassle is to animate axes, since it's a lot of svg elements.
A very basic animation using react and react-spring.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
Contact
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/ridgeline.html b/ridgeline.html
index da0dc115..74d106ee 100644
--- a/ridgeline.html
+++ b/ridgeline.html
@@ -1,4 +1,4 @@
-Ridgeline chart with React
Ridgeline chart with React
A ridgeline chart is a chart type that shows the distribution of a numeric variable for several groups, stacked one of top of each other. This page is a step-by-step guide on how to build your own histogram for the web, using React and D3.js.
It starts by describing how the data should be organized and how to initialize the ridgeline component. It then quickly recalls how to compute and draw a kernel density, and shows how to stack this information for several groups of a dataset. 🙇♂️.
To build a ridgeline chart, the input dataset must provide a set ofnumeric values for several groups.
The best way to format this kind of information is with an array of object. Each object represents a group. Agroup property provides the group name, and a values property provides the values.
Here is a minimal example of the data structure:
export const data = [
+Ridgeline chart with React
Ridgeline chart with React
A ridgeline chart is a chart type that shows the distribution of a numeric variable for several groups, stacked one of top of each other. This page is a step-by-step guide on how to build your own histogram for the web, using React and D3.js.
It starts by describing how the data should be organized and how to initialize the ridgeline component. It then quickly recalls how to compute and draw a kernel density, and shows how to stack this information for several groups of a dataset. 🙇♂️.
To build a ridgeline chart, the input dataset must provide a set ofnumeric values for several groups.
The best way to format this kind of information is with an array of object. Each object represents a group. Agroup property provides the group name, and a values property provides the values.
I'm in the process of writing a complete blog post on the topic. Subscribe to the project to know when it's ready.
Ridgeline inspiration
If you're looking for inspiration to create your next Ridgeline, note that dataviz-inspiration.com showcases many examples. Definitely the best place to get ... inspiration!
dataviz-inspiration.com showcases hundreds of stunning dataviz projects. Have a look to get some ideas on how to make your Ridgeline looks good!
Ridgeline charts are not the only way to compare the distribution of several groups in a dataset. Boxplots, violins, histograms and densities are variations that could interst you in order to solve this goal. Check the examples below to get the code.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/sankey-diagram.html b/sankey-diagram.html
index b07daee4..4d9c4fa7 100644
--- a/sankey-diagram.html
+++ b/sankey-diagram.html
@@ -1,4 +1,4 @@
-How to build a Sankey Diagram with React and D3.
Sankey Diagram
A Sankey Diagram display flows. Several entities (nodes) are represented by rectangles or text. Directed links are represented with arrows or arcs that have a width proportional to the importance of the flow.
This tutorial explains how to use React, D3.js and the d3-sankey plugin to build a Sankey diagram. It comes with explanations and code sandboxes to play along with the suggested implementation.
Two layers of information are required to build a Sankey diagram: a list of nodes to build the rectangles and a list of links to build the paths between them.
Many different data structures can be used to store such information. In this tutorial I suggest to start with the following:
const data = {
+How to build a Sankey Diagram with React and D3.
Sankey Diagram
A Sankey Diagram display flows. Several entities (nodes) are represented by rectangles or text. Directed links are represented with arrows or arcs that have a width proportional to the importance of the flow.
This tutorial explains how to use React, D3.js and the d3-sankey plugin to build a Sankey diagram. It comes with explanations and code sandboxes to play along with the suggested implementation.
Two layers of information are required to build a Sankey diagram: a list of nodes to build the rectangles and a list of links to build the paths between them.
Many different data structures can be used to store such information. In this tutorial I suggest to start with the following:
I'm in the process of writing a complete blog post on the topic. Subscribe to the project to know when it's ready.
Sankey inspiration
If you're looking for inspiration to create your next Sankey, note that dataviz-inspiration.com showcases many examples. Definitely the best place to get ... inspiration!
dataviz-inspiration.com showcases hundreds of stunning dataviz projects. Have a look to get some ideas on how to make your Sankey looks good!
This Sankey diagram visualizes the flow of energy: supplies are on the left, and demands are on the right. It is a reproduction of this famous observable example. Links show how varying amounts of energy are converted or transmitted before being consumed or lost.
The code is very similar to the example above. On top of it, a color scale is used for the node and connection colors, and some text labels have been added.
A Sankey diagram showing the flow of energy. Supplies on the left, demands on the right.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/scatter-plot.html b/scatter-plot.html
index 3e993e32..a4db228e 100644
--- a/scatter-plot.html
+++ b/scatter-plot.html
@@ -1,4 +1,4 @@
-How to build a scatter plot with React and D3.
Scatterplot with React and d3.js
A scatterplot displays the relationship between 2 numeric variables. This page is a step-by-step guide on how to build your own scatterplot for the web, using React and D3.js.
It starts with very basic concepts like data structure, scales and svg circle rendering. It then shows how to add interactivity to the chart with hover effects and tooltips. At the end of the post, you should be able to build you own ready-to-publish scatterplot 🙇♂️.
The dataset used to build a scatterplot is usually an array of objects.
For each object, at least 2 properties are required: x and y. The value of x will control the position of the datapoint on the horizontal axis. The value of y will be linked with the vertical axis.
const data = [
+How to build a scatter plot with React and D3.
Scatterplot with React and d3.js
A scatterplot displays the relationship between 2 numeric variables. This page is a step-by-step guide on how to build your own scatterplot for the web, using React and D3.js.
It starts with very basic concepts like data structure, scales and svg circle rendering. It then shows how to add interactivity to the chart with hover effects and tooltips. At the end of the post, you should be able to build you own ready-to-publish scatterplot 🙇♂️.
The dataset used to build a scatterplot is usually an array of objects.
For each object, at least 2 properties are required: x and y. The value of x will control the position of the datapoint on the horizontal axis. The value of y will be linked with the vertical axis.
Last but not least, some css needs to be added to customize the circle depending on if they are in default, .dimmed or :hover mode.
Note that the filter: saturate(0) is a good way to dim unwanted circles. Also, playing with transition-delay and transition-duration adds to animate the transition is a nice touch you should consider. Check the code below the example to see the full css.
The hover effect is another big topic in data visualization. A dedicated post will be published soon on the topic, feel free to subscribe to know when.
Scatterplot inspiration
If you're looking for inspiration to create your next Scatterplot, note that dataviz-inspiration.com showcases many examples. Definitely the best place to get ... inspiration!
dataviz-inspiration.com showcases hundreds of stunning dataviz projects. Have a look to get some ideas on how to make your Scatterplot looks good!
Let's apply the concepts learned above to a real-life example.
I like this scatterplot originally published on the data wrapper blog. It shows a strong correlation between vulnerability to climate change and CO2 emissions.
The chart has several features that are interesting to reproduce fom a technical point of view:
Custom annotation: only a fraction of the country names are written
Hover effect: the hovered country is highlighted with a black stroke. After a short delay, countries of other groups are dimmed. Note that the effect is triggered once the mouse approaches the marker, no need to be perfectly on top.
Tooltip: highly customized and linked to the mouse position
The countries with the highest vulnerability to climate change have the lowest CO2 emissions
All countries sorted by their vulnerability and readiness to climate change. The size shows the CO2 emission per person in that country.
Reproduction of a chart originally published by Data Wrapper using react and d3.js.
Variations
The scatterplot examples described above are just the beginning of your journey. There is an infinite world of customization that is open to you. You can also explore related chart types that can be a good fit for your data:
Click on the overview below to get details and code.
Useful links
The following links have been useful to create this page:
Building axes in d3.js and the scatterplot section from the d3 graph gallery
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/shape-morphism-for-dataviz-with-react.html b/shape-morphism-for-dataviz-with-react.html
index d538a85f..32a8b9e3 100644
--- a/shape-morphism-for-dataviz-with-react.html
+++ b/shape-morphism-for-dataviz-with-react.html
@@ -1,4 +1,4 @@
-Shape morphism for data visualization
Shape morphism for data visualization
Shape morphism is the art of transitioning between 2 shapes as smoothly as possible. This post explores how it can be useful for data visualization and how it can be done using React, d3.js, react-spring and flubber.
This post is about shape morphism, which means animating the properties that define the actual shape of the elements. As always when talking about animation, it is good to recall this citation by Josh Comeau:
Animation is like salt: too much of it spoils the dish
Examples
Here is a list of nifty viz examples using shape morphism to transition between several viz types
In the field of data visualization shape morphism is mostly usefull to transition between 2 chart types. It is pretty hard to implement that kind of smooth transition so why should we even care?
Eye catching effect
Highlight the direct relationship between 2 charts. During the transition one can follow a specific item and understand it's the same.
Make sure that we're looking at the same dataset, but represented differently
What are we trying to do
Sometimes in dataviz we want to transition between 2 chart types, let's say between a pie chart and a barplot. This is pretty hard since the shapes used for those 2 charts are very different: arc versus rectangle.
This is still possible thanks to a few libraries. This post suggests to use d3.js to build the start and end svg path, flubberto interpolate those path, react-spring for the animation and react for the rendering.
This is the kind of thing we're gonna learn to build:
Shape morphism on the web: a review
I knew nothing about shape morphism 3 weeks ago. It took me a lot of effort to browse the web and find what the most appropriate tools are. To avoid you the hassle, here is a quick summary:
SMIL (Synchronized Multimedia Integration Language) is a feature introduced in firefox 4, allowing to follow a motion path. Basically it means you can use an animate element in your svg that will support shape morphism. Unfortunately, this feature is probably get deprecated soon. Furthermore, it supports transition only between shapes with the same number of nodes. DocCodePen.
pure CSS: Chrome has started to allow shape morphing through css. You can simply change the d attribute of a path in a css file and add sometransition to it. But chrome only and same number of nodes only. CodePen
greenSock MorphSvg plugin: a promising javascript library for shape morphism, widely cited on the internet. Supports shapes with different number of nodes. But it's not free and not open source. WebsiteCodePen
superformula is a mathematic formula that can be used to describe many complex shapes. Using 6 numbers as parameters, this formula can build many complex shapes. Interpolating between 2 shapes becomes easy: we just have to interpolate those numbers. Problem: it does not work with any shape and building a chart from this formula is thus impossible. Example
d3-interpolate is a d3 module that provides a variety of interpolation methods. It works for paths, even with different number of nodes. But when the shape 2 has more nodes than shape 1 it just adds some nodes to the end of the shape 1 path. This result in a bad visual effect. Doc
d3-interpolate-path is an open-source js library that adds an interpolator optimized for SVG path elements. It works very well for path including segments only, but from my experience less well for arcs. DocDemo
Vizzu is a library for animated data visualizations and data stories. It looks very promising for transition between chart types. But since it is a library, it means that customization is limited to the offered options. DocDemo
None of the item of this list suits my need. We need an open source library capable of interpolating any path, even with different number of nodes..
Shape interpolation with flubber
flubber is an open source javascript library built by Noah Veltman. Unlike most of the shape morphism libraries it works very well to interpolate shapes that are completely different and don't have the same number of nodes.
Let's start by creating 2 svg shapes
const shape1 = "M10,140 L50,60 L90,140 Z"; // triangle
+Shape morphism for data visualization
Shape morphism for data visualization
Shape morphism is the art of transitioning between 2 shapes as smoothly as possible. This post explores how it can be useful for data visualization and how it can be done using React, d3.js, react-spring and flubber.
This post is about shape morphism, which means animating the properties that define the actual shape of the elements. As always when talking about animation, it is good to recall this citation by Josh Comeau:
Animation is like salt: too much of it spoils the dish
Examples
Here is a list of nifty viz examples using shape morphism to transition between several viz types
In the field of data visualization shape morphism is mostly usefull to transition between 2 chart types. It is pretty hard to implement that kind of smooth transition so why should we even care?
Eye catching effect
Highlight the direct relationship between 2 charts. During the transition one can follow a specific item and understand it's the same.
Make sure that we're looking at the same dataset, but represented differently
What are we trying to do
Sometimes in dataviz we want to transition between 2 chart types, let's say between a pie chart and a barplot. This is pretty hard since the shapes used for those 2 charts are very different: arc versus rectangle.
This is still possible thanks to a few libraries. This post suggests to use d3.js to build the start and end svg path, flubberto interpolate those path, react-spring for the animation and react for the rendering.
This is the kind of thing we're gonna learn to build:
Shape morphism on the web: a review
I knew nothing about shape morphism 3 weeks ago. It took me a lot of effort to browse the web and find what the most appropriate tools are. To avoid you the hassle, here is a quick summary:
SMIL (Synchronized Multimedia Integration Language) is a feature introduced in firefox 4, allowing to follow a motion path. Basically it means you can use an animate element in your svg that will support shape morphism. Unfortunately, this feature is probably get deprecated soon. Furthermore, it supports transition only between shapes with the same number of nodes. DocCodePen.
pure CSS: Chrome has started to allow shape morphing through css. You can simply change the d attribute of a path in a css file and add sometransition to it. But chrome only and same number of nodes only. CodePen
greenSock MorphSvg plugin: a promising javascript library for shape morphism, widely cited on the internet. Supports shapes with different number of nodes. But it's not free and not open source. WebsiteCodePen
superformula is a mathematic formula that can be used to describe many complex shapes. Using 6 numbers as parameters, this formula can build many complex shapes. Interpolating between 2 shapes becomes easy: we just have to interpolate those numbers. Problem: it does not work with any shape and building a chart from this formula is thus impossible. Example
d3-interpolate is a d3 module that provides a variety of interpolation methods. It works for paths, even with different number of nodes. But when the shape 2 has more nodes than shape 1 it just adds some nodes to the end of the shape 1 path. This result in a bad visual effect. Doc
d3-interpolate-path is an open-source js library that adds an interpolator optimized for SVG path elements. It works very well for path including segments only, but from my experience less well for arcs. DocDemo
Vizzu is a library for animated data visualizations and data stories. It looks very promising for transition between chart types. But since it is a library, it means that customization is limited to the offered options. DocDemo
None of the item of this list suits my need. We need an open source library capable of interpolating any path, even with different number of nodes..
Shape interpolation with flubber
flubber is an open source javascript library built by Noah Veltman. Unlike most of the shape morphism libraries it works very well to interpolate shapes that are completely different and don't have the same number of nodes.
It's very straightforward to interpolate a y between the 2 of them thanks to the interpolate() function offlubber. This function expects 2 arguments: the starting shape and the ending shape:
const interpolator = interpolate(shape1, shape2);
interpolate() returns a function. This function accepts only 1 argument: a value between 0 (start) and 1 (end). It will return the interpolated shape for this progress.
Now that we know how to build an interpolated shape between a starting and an ending point, let's animated this transition using react-spring.
The transition is now animated thanks to react-spring, a react library for spring animation.
Pie chart to barplot transition
I knew nothing about shape morphism 3 weeks ago. It took me a lot of effort to browse the web and find what the most appropriate tools are. To avoid you the hassle, here is a quick summary:
Violin to boxplot transition
I knew nothing about shape morphism 3 weeks ago. It took me a lot of effort to browse the web and find what the most appropriate tools are. To avoid you the hassle, here is a quick summary:
How to smoothly transition between a boxplot and a violin plot. Math by d3.js, rendering using react, animation using react-spring and interpolation using flubber.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
@@ -6,4 +6,4 @@
Contact
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
A stacked area chart is an evolution of an area chart used to display the evolution of several groups in a dataset. This section explains how to build it with d3.js and react. It focus on stacking, so make sure to read the area chart section first.
Most of the time the input dataset is an array where each item is an object. Each object provides information for a step on the X axis. It has a value like x that provides the exact position on the X axis. It then has several numeric values, one for each group of the dataset.
Here is a minimal example:
const data = [
+Stacked Area charts with React
Stacked Area charts
A stacked area chart is an evolution of an area chart used to display the evolution of several groups in a dataset. This section explains how to build it with d3.js and react. It focus on stacking, so make sure to read the area chart section first.
Most of the time the input dataset is an array where each item is an object. Each object provides information for a step on the X axis. It has a value like x that provides the exact position on the X axis. It then has several numeric values, one for each group of the dataset.
I'm in the process of writing a complete blog post on the topic. Subscribe to the project to know when it's ready.
Stacked Area inspiration
If you're looking for inspiration to create your next Stacked Area, note that dataviz-inspiration.com showcases many examples. Definitely the best place to get ... inspiration!
dataviz-inspiration.com showcases hundreds of stunning dataviz projects. Have a look to get some ideas on how to make your Stacked Area looks good!
Stacked area charts can easily be customized to use other offset and smoothing algorithm. This process can be used to create streamgraphs which are a varation of the stacked area graph.
The offset type controls how the data are stacked. You can read about the offset options available in the official documentation or play with the little example below.
The curve type controls how the smoothing of each shape is made. There are a myriad of options described in the official documentation.
Offset typeCurve type
Try d3.js various options to offset the data and smooth shapes. See a smooth transition between options.
The animation uses react-spring to run. I'll publish a full blogpost on the topic soon!
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/stacked-barplot-with-negative-values.html b/stacked-barplot-with-negative-values.html
index c4df349b..349aa83a 100644
--- a/stacked-barplot-with-negative-values.html
+++ b/stacked-barplot-with-negative-values.html
@@ -1,4 +1,4 @@
-Dealing with negative values on a stacked barplot
Dealing with negative values on a stacked barplot
Drawing a stacked barplot sounds like pretty basic task for somebody into dataviz. But it gets surprisingly tricky once the dataset includes negative values.
This post explains how to deal with it, suggesting several options coming together with some reproducible code examples.
🍔 Regular stacked barplot with positive values
Let's consider a company that has 3 employees: a, b and c. Each month those employees sell umbrellas and make money.
In January, they made 1, 1 and 2 dollars respectively. It's pretty easy to represent those sales as a stacked barplot!
There is even a bonus! The total height of those bars is 4. So is the value of the stack on the Y axis. We now know what's the total revenue of the company 🎉!
Stacking positive values is straightforward
Now let's follow the same process for the 12 months of the year. That makes a stacked area barplot built with react and d3.js. If you need explanations for the code, please refer to the d3.js gallery or to the barplot section of the react gallery.
A stacked area chart with positive values only. Built with react and d3.js
That's the end of the easy part. Now, let's say that sometimes employees spend more money than what they make.
We now have some negative values in the dataset 😳. How can we deal with it?
1️⃣ Stacked barplot with negative values: the diverging strategy
We are in January but this time, employee A lost 1$! 😥
To represent this on the stack, we can add all the positive values on top of the chart, and all the negative ones below the 0 axis.
It is still very easy to see how much each employee made in a glimpse!
However, it is now impossible to know what's the company revenue! Indeed, the total height of the bars is 4, the value on the y axis is 3, but the real revenue is 1 + 2 - 1 = 2!
Stacking with negative values with all negative values below the 0 axis
Using almost the same code we can build the stacked barplot including those negative values. Note that when stacking the data with the stack() function of d3, the specific stackOffsetDiverging offset parameter must be passed, handling all the work for us (doc).
With the diverging strategy, all negative values are located under the 0 baseline.
Let's recap the pros and cons of this diverging option:
Pros
Easy to read the value of each item
Obvious what's negative and what's positive
Cons
Impossible to know the total value of each stack
A series can jump from the bottom to the top of the chart and is thus hard to follow
2️⃣ Stacked barplot with negative values: the overlapping strategy
Another strategy can be applied to stack the items including negative values.
Items can be added one by one, with rectangles going up when values are positive and going down when values are negative.
Stacking items by overlapping the items on top of each other.
It's important to understand that here, the item order is important. We will get very different results depending on the order since not all groups are visible.
With the overlapping strategy items are drawn successively, going up and down and overlapping if necessary
Let's recap the pros and cons of this diverging option:
Pros
Depending on the group order, the Y value can reflect the sum of the items. But it's not guarantee.
Cons
Groups overlap each other. Information is hidden. Chart is unreadable
Conclusion
In my opinion the first option (diverging) makes much more sense than the second one (overlapping). The cons are very limited:
It is true that the net total value is not available. But if that's what interests you, you don't have to split the dataset by subgroups, just create a line chart with a single line!
Having a group flipping from top to bottom is indeed annoying. However, the hover effect that is included allows to quickly see what happens for a specific group.
It's also important to note that most dataviz tools choosed this approach. Here is an example using the same dataset using the ggplot2library (left) and data wrapper (right).
Same dataset that includes negative values plotted with ggplot2 (left) and data wrapper (right)
Last but not least, I really like this example by chartio that fixes the non available total issue by adding a line on top of the stacked barchart to show the total:
Chartio displays the total of each timestamp using a line chart on top of the stacked items.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
+Dealing with negative values on a stacked barplot
Dealing with negative values on a stacked barplot
Drawing a stacked barplot sounds like pretty basic task for somebody into dataviz. But it gets surprisingly tricky once the dataset includes negative values.
This post explains how to deal with it, suggesting several options coming together with some reproducible code examples.
🍔 Regular stacked barplot with positive values
Let's consider a company that has 3 employees: a, b and c. Each month those employees sell umbrellas and make money.
In January, they made 1, 1 and 2 dollars respectively. It's pretty easy to represent those sales as a stacked barplot!
There is even a bonus! The total height of those bars is 4. So is the value of the stack on the Y axis. We now know what's the total revenue of the company 🎉!
Stacking positive values is straightforward
Now let's follow the same process for the 12 months of the year. That makes a stacked area barplot built with react and d3.js. If you need explanations for the code, please refer to the d3.js gallery or to the barplot section of the react gallery.
A stacked area chart with positive values only. Built with react and d3.js
That's the end of the easy part. Now, let's say that sometimes employees spend more money than what they make.
We now have some negative values in the dataset 😳. How can we deal with it?
1️⃣ Stacked barplot with negative values: the diverging strategy
We are in January but this time, employee A lost 1$! 😥
To represent this on the stack, we can add all the positive values on top of the chart, and all the negative ones below the 0 axis.
It is still very easy to see how much each employee made in a glimpse!
However, it is now impossible to know what's the company revenue! Indeed, the total height of the bars is 4, the value on the y axis is 3, but the real revenue is 1 + 2 - 1 = 2!
Stacking with negative values with all negative values below the 0 axis
Using almost the same code we can build the stacked barplot including those negative values. Note that when stacking the data with the stack() function of d3, the specific stackOffsetDiverging offset parameter must be passed, handling all the work for us (doc).
With the diverging strategy, all negative values are located under the 0 baseline.
Let's recap the pros and cons of this diverging option:
Pros
Easy to read the value of each item
Obvious what's negative and what's positive
Cons
Impossible to know the total value of each stack
A series can jump from the bottom to the top of the chart and is thus hard to follow
2️⃣ Stacked barplot with negative values: the overlapping strategy
Another strategy can be applied to stack the items including negative values.
Items can be added one by one, with rectangles going up when values are positive and going down when values are negative.
Stacking items by overlapping the items on top of each other.
It's important to understand that here, the item order is important. We will get very different results depending on the order since not all groups are visible.
With the overlapping strategy items are drawn successively, going up and down and overlapping if necessary
Let's recap the pros and cons of this diverging option:
Pros
Depending on the group order, the Y value can reflect the sum of the items. But it's not guarantee.
Cons
Groups overlap each other. Information is hidden. Chart is unreadable
Conclusion
In my opinion the first option (diverging) makes much more sense than the second one (overlapping). The cons are very limited:
It is true that the net total value is not available. But if that's what interests you, you don't have to split the dataset by subgroups, just create a line chart with a single line!
Having a group flipping from top to bottom is indeed annoying. However, the hover effect that is included allows to quickly see what happens for a specific group.
It's also important to note that most dataviz tools choosed this approach. Here is an example using the same dataset using the ggplot2library (left) and data wrapper (right).
Same dataset that includes negative values plotted with ggplot2 (left) and data wrapper (right)
Last but not least, I really like this example by chartio that fixes the non available total issue by adding a line on top of the stacked barchart to show the total:
Chartio displays the total of each timestamp using a line chart on top of the stacked items.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/streamchart.html b/streamchart.html
index ce615a8a..66fb5b8d 100644
--- a/streamchart.html
+++ b/streamchart.html
@@ -1,4 +1,4 @@
-Streamchart with React
Streamchart
A streamgraph is a variation of the more common stacked area chart. It rounds edges and displays areas around the central axis which gives a nice impression of flow.
This section explains how to stack and smooth the data with d3.js, and render the shapes with react. It starts from the basic and goes until necessary customization like tooltips, hover effect, legend and annotation. Examples always come with editable sandboxes.
Most of the time the input dataset is an array where each item is an object.
Each object provides information for a step on the X axis. It has a value like x or date that provides the exact position on the X axis. Then it has several numeric values, one for each group of the dataset.
Here is a minimal example:
const data = [
+Streamchart with React
Streamchart
A streamgraph is a variation of the more common stacked area chart. It rounds edges and displays areas around the central axis which gives a nice impression of flow.
This section explains how to stack and smooth the data with d3.js, and render the shapes with react. It starts from the basic and goes until necessary customization like tooltips, hover effect, legend and annotation. Examples always come with editable sandboxes.
Most of the time the input dataset is an array where each item is an object.
Each object provides information for a step on the X axis. It has a value like x or date that provides the exact position on the X axis. Then it has several numeric values, one for each group of the dataset.
I'm in the process of writing a complete blog post on the topic. Subscribe to the project to know when it's ready.
Hover effect
It is pretty hard to follow the evolution of a specific group on a streamgraph.
It is common to add an hover effect to the figure: hovering over a group will highlight it, making it easier to follow its evolution. Try it on the graph below:
StreamGraph with hover effect that highlights a specific series
There are various strategies to implement such an hover effect.
Here, I suggest to do everything in css using pseudo classes, and targetting svg elements only. Basically, everything in the svg container will be dimmed (lower opacity and saturation) when the mouse goes over the chart. But the specific shape that is hovered over will keep its full opacity thanks to a more specific css selector.
Hover effect is a big topic and I will post more about it soon!
If you're looking for inspiration to create your next Streamgraph, note that dataviz-inspiration.com showcases many examples. Definitely the best place to get ... inspiration!
dataviz-inspiration.com showcases hundreds of stunning dataviz projects. Have a look to get some ideas on how to make your Streamgraph looks good!
Our streamgraph is renderer using a set of path. The d attribute of those paths provides the boundary coordinates of those paths.
When a prop of the StreamGraph component updates, we might want to update the paths to represent the latest state of our application. It can be an update of the dataset, or an update of the function used to stack the data or smooth the area as below.
It is possible to smoothly animate this transition thanks to react-spring.
Offset typeCurve type
Try d3.js various options to offset the data and smooth shapes. See a smooth transition between options.
The animation suggested above is a bit tricky to implement. Indeed, we need to transition from paths that do not have the same number of edges. It is possible thanks to a library called flubber but definitely deserves its own blogpost.
ToDofind why flubber does some weird interpolation in some cases
Application
The following chart is a real-life application of a streamgraph. It shows the evolution if the number of page-views for 5 tech websites in the last 7 years. My goal was to assess if the rise of chat-GPT had an impact on it.
This interactive chart has several interesting features:
slider: you can control the displayed time-frame thanks to a slider.
inline legend: label of each series are written inline. A background proportional to their value provides additional insight.
hover effect: legend will be updated with precise values at the hovered timestamp.
A customized streamgraph built with React and D3.js. It has inline legends, slider to control timeframe, hover effect and more.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/subscribe.html b/subscribe.html
index 6f0eb8ad..6e40f374 100644
--- a/subscribe.html
+++ b/subscribe.html
@@ -1,7 +1,7 @@
-Subscribe
Want more graph examples?
The react graph gallery is a project that just started! If you want to know when a new section gets released, follow me on twitter or subscribe below!
+Subscribe
Want more graph examples?
The react graph gallery is a project that just started! If you want to know when a new section gets released, follow me on twitter or subscribe below!
Contact
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/timeseries.html b/timeseries.html
index c680161b..53651049 100644
--- a/timeseries.html
+++ b/timeseries.html
@@ -1,4 +1,4 @@
-Timeseries| The React Graph Gallery
Timeseries
This section does not target a specific chart type, even though timeseries are often represented as line charts or area charts.
Instead, it provides a tips and tricks to deal with charts that represent the evolution in time of a numeric variable. For instance, it provides hints on how to deal with dates, how to pan on a chart and more
This page is not about a specific chart type. Instead it provides hints on how to deal with time.
Check the graph sections that are used to visualize timeseries below if there is a specific chart type you want to make.
The Date format
The first struggle when dealing with timeseries is to work with the javascript Date format. A Date represents a single moment in time and is usually stored as a string in the input dataset.
Here is an FAQ about the Date format. I constantly forget how this works and come back here for reference. Note that it heavily relies on the d3-time-format module that provides great helpers.
Transform a string into a date
In your dataset, the date of each data point is probably stored as a string, like 2015-06-01 for instance.
In order to manipulate and plot those dates, you need to transform it into a javascript date format.
// import the timeParse function from d3
+Timeseries| The React Graph Gallery
Timeseries
This section does not target a specific chart type, even though timeseries are often represented as line charts or area charts.
Instead, it provides a tips and tricks to deal with charts that represent the evolution in time of a numeric variable. For instance, it provides hints on how to deal with dates, how to pan on a chart and more
This page is not about a specific chart type. Instead it provides hints on how to deal with time.
Check the graph sections that are used to visualize timeseries below if there is a specific chart type you want to make.
The Date format
The first struggle when dealing with timeseries is to work with the javascript Date format. A Date represents a single moment in time and is usually stored as a string in the input dataset.
Here is an FAQ about the Date format. I constantly forget how this works and come back here for reference. Note that it heavily relies on the d3-time-format module that provides great helpers.
Transform a string into a date
In your dataset, the date of each data point is probably stored as a string, like 2015-06-01 for instance.
In order to manipulate and plot those dates, you need to transform it into a javascript date format.
// import the timeParse function from d3
import { timeParse } from "d3";
// create a time parser function that works for our time format
@@ -41,4 +41,4 @@
.range([0, width]);
Following this code block xScale is a function. You give it a Date, it returns a position in pixels
You can finally use this scale to draw the X axis using your favorite method:
create your own react component
call d3.axisBottom() in a useEffect
I'm preparing a full post on axes with d3 and react, subscribe to the newsletter if you want to be notified when it's out!
d3 is very smart at picking the right label format. If you're dealing with a very long period of time (years), it will display years. If you're dealing with a month, it will display days. And so on. It is very convenient. The logic controlling this formatting is provided here.
Note that you can customize how dates are formatted along the X axis thanks to the tickFormat function.
Line chart application
If you already understood the content of the line chart section of the gallery, you just have to use a scaleTime instead of a scaleLinear and that's it, you have your first timeseries visualization. 😋
A first timeseries line chart made thanks to the scaleTime function of d3.
Synchronized cursor
Another pretty common task when dealing with timeseries is to add a synchronized cursor on all charts.
This makes the dashboard more insightful: hovering over an interesting part of a chart reveals where the timestamp is localized on other charts instantly.
The implementation required to build a shared state between all charts of the webpage. Hovering a specific chart will update this state and thus update all other plots. This process is extensively described in this synchronized cursor for timeseries post.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/treemap.html b/treemap.html
index 3bd813d4..0ca43a84 100644
--- a/treemap.html
+++ b/treemap.html
@@ -1,4 +1,4 @@
-Treemap with React
Treemap
A treemap displays a hierarchical dataset (a tree) as a set of rectangles. Rectangle sizes are proportional to their numeric value.
d3.js has some handy functions to compute the rectangle positions. React becomes useful to render those rectangles, animate transitions and more. This post explains how to make those 2 tools work together to build a Treemap component.
The dataset describes a hierarchy using a recursive structure. Each item in this structure is called a node, the lowest nodes of the hierarchy being called leaves.
The dataset is an object that has at least 3 properties: name, value and children.children is an array of nodes that have this structure too.
Here is a minimal example of the data structure:
const data = {
+Treemap with React
Treemap
A treemap displays a hierarchical dataset (a tree) as a set of rectangles. Rectangle sizes are proportional to their numeric value.
d3.js has some handy functions to compute the rectangle positions. React becomes useful to render those rectangles, animate transitions and more. This post explains how to make those 2 tools work together to build a Treemap component.
The dataset describes a hierarchy using a recursive structure. Each item in this structure is called a node, the lowest nodes of the hierarchy being called leaves.
The dataset is an object that has at least 3 properties: name, value and children.children is an array of nodes that have this structure too.
Treemap with 2 levels of hierarchy and a color scale, made with react and d3.js.
Hover effect
Adding a hover effect to your treemap is a nice polish detail. Here I suggest to highlight the slice that is hovered over by dimming all the other slices.
There are several strategies available to implement such an effect. One can rely on css pseudo classes only, or add a css class using javascript and the onMouseEnter event. It's also possible to rely on an animation library like react-spring.
I'm preparing a full section on the topic. You can subscribe to my dataviz-universe newsletter to know when it will be ready. Meanwhile, there is a code sandbox waiting for you below to reveal the code of this example.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/typescript-d3-axis.html b/typescript-d3-axis.html
index a41833f5..6cd37401 100644
--- a/typescript-d3-axis.html
+++ b/typescript-d3-axis.html
@@ -1,4 +1,4 @@
-Dealing with d3 axis in a typescript environment
D3 axis and typescript
What is typescript and why it is awesome to use it. How are d3 functions typed.
A graph axis is based on a scale. But what is a scale? This is how thed3-scale module describes it:
Scales are a convenient abstraction for a fundamental task in visualization: mapping a dimension of abstract data to a visual representation
So basically a scale is a function. You provide it with an input like the value of a data point, and it returns another value like a position in pixel.
Here is a very basic implementation of a scale using the scaleLinear function of d3.
const xScale = d3.scaleLinear()
+Dealing with d3 axis in a typescript environment
D3 axis and typescript
What is typescript and why it is awesome to use it. How are d3 functions typed.
A graph axis is based on a scale. But what is a scale? This is how thed3-scale module describes it:
Scales are a convenient abstraction for a fundamental task in visualization: mapping a dimension of abstract data to a visual representation
So basically a scale is a function. You provide it with an input like the value of a data point, and it returns another value like a position in pixel.
Here is a very basic implementation of a scale using the scaleLinear function of d3.
const xScale = d3.scaleLinear()
.domain([0, 10]) // data can go from 0 to 10
.range([0, width]); // it will result in a value that goes from 0 to width
@@ -23,4 +23,4 @@
.range(COLORS);
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/video/pyramid-legend.mp4 b/video/pyramid-legend.mp4
new file mode 100644
index 00000000..fa97ef39
Binary files /dev/null and b/video/pyramid-legend.mp4 differ
diff --git a/violin-plot.html b/violin-plot.html
index 931d31b1..9d4c1de7 100644
--- a/violin-plot.html
+++ b/violin-plot.html
@@ -1,4 +1,4 @@
-Violin plot with React
Violin plot
A violin chart displays the distribution of a numeric variable, often for several groups of a dataset. This page is a step-by-step guide on how to build your own violin component for the web, using React and D3.js.
It starts by describing how the data should be organized and how to initialize the violin component. D3.js is then used to split the data in buckets thanks to the bin() function. It then adds smoothing to it with curve(). React is finally used to render the violin using a SVG path.
The dataset used to build a violin chart is usually an array of object. For each object, a name property provides the group name, and a value property provides the numeric value. It looks like this:
const data = [
+Violin plot with React
Violin plot
A violin chart displays the distribution of a numeric variable, often for several groups of a dataset. This page is a step-by-step guide on how to build your own violin component for the web, using React and D3.js.
It starts by describing how the data should be organized and how to initialize the violin component. D3.js is then used to split the data in buckets thanks to the bin() function. It then adds smoothing to it with curve(). React is finally used to render the violin using a SVG path.
The dataset used to build a violin chart is usually an array of object. For each object, a name property provides the group name, and a value property provides the numeric value. It looks like this:
I'm in the process of writing a complete blog post on the topic. Subscribe to the project to know when it's ready.
Violin inspiration
If you're looking for inspiration to create your next Violin, note that dataviz-inspiration.com showcases many examples. Definitely the best place to get ... inspiration!
dataviz-inspiration.com showcases hundreds of stunning dataviz projects. Have a look to get some ideas on how to make your Violin looks good!
It's important to understand that under the hood, a violin shape is nothing else than a smoothed histogram. You can use the sentence below the following chart to switch from one to the other and understand the tight connection.
As a result the violin plot suffers the same flaw as the histogram: its shape highly depends on the number of buckets used for the computation. Use the slider to see the impact of the target bucket number on the violin shape.
Each violin shape based on approx. 5 buckets
You can use smoothing or steps.
Interactive violin plot: try to toggle smoothing and change the number of buckets in use.
Note: the requested number of buckets is a target. The bin() function of d3 will create smart buckets around this value.
Comparison with a boxplot
The boxplot is an alternative to represent the exact same kind of dataset. You can visit the boxplot section of the gallery or play with the interactive example below to understand how those 2 options behave on the same dataset.
Use the slider to switch from the violin to the box. Play with the sentence below the chart to toggle smoothing on the violin.
You can use smoothing or steps.
Compare how violins and boxplots look like for the same dataset.
Animation
Animating the transition between 2 datasets, or from/to another chart type is hard, because the violin plot is based on SVG path. It is doable though and I'm working on a specific post that will be released soon.
Using shape morphism to transition between a boxplot and a violin plot. Blog post coming soon!
If you're interested in this topic, feel free to subscribe to the newsletter to be informed when this post is available!
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
+}
I'm in the process of writing a complete blog post on the topic. Subscribe to the project to know when it's ready.
Violin inspiration
If you're looking for inspiration to create your next Violin, note that dataviz-inspiration.com showcases many examples. Definitely the best place to get ... inspiration!
dataviz-inspiration.com showcases hundreds of stunning dataviz projects. Have a look to get some ideas on how to make your Violin looks good!
It's important to understand that under the hood, a violin shape is nothing else than a smoothed histogram. You can use the sentence below the following chart to switch from one to the other and understand the tight connection.
As a result the violin plot suffers the same flaw as the histogram: its shape highly depends on the number of buckets used for the computation. Use the slider to see the impact of the target bucket number on the violin shape.
Each violin shape based on approx. 5 buckets
You can use smoothing or steps.
Interactive violin plot: try to toggle smoothing and change the number of buckets in use.
Note: the requested number of buckets is a target. The bin() function of d3 will create smart buckets around this value.
Comparison with a boxplot
The boxplot is an alternative to represent the exact same kind of dataset. You can visit the boxplot section of the gallery or play with the interactive example below to understand how those 2 options behave on the same dataset.
Use the slider to switch from the violin to the box. Play with the sentence below the chart to toggle smoothing on the violin.
You can use smoothing or steps.
Compare how violins and boxplots look like for the same dataset.
Animation
Animating the transition between 2 datasets, or from/to another chart type is hard, because the violin plot is based on SVG path. It is doable though and I'm working on a specific post that will be released soon.
Using shape morphism to transition between a boxplot and a violin plot. Blog post coming soon!
If you're interested in this topic, feel free to subscribe to the newsletter to be informed when this post is available!
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/viz-from-the-future.html b/viz-from-the-future.html
index dd737856..c45616ce 100644
--- a/viz-from-the-future.html
+++ b/viz-from-the-future.html
@@ -1,7 +1,7 @@
-How to build a responsive chart with React and d3.js
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
+How to build a responsive chart with React and d3.js
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
Contact
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/voronoi.html b/voronoi.html
index 562d4182..f7b51fbc 100644
--- a/voronoi.html
+++ b/voronoi.html
@@ -1,4 +1,4 @@
-How to build a Voronoi Diagram with React and D3.
Voronoi Diagram
A voronoi diagram is a partition of a plane into regions called voronoi cells. A voronoi cell consists of every point in the plane whose distance to its linked data point is less than or equal to its distance to any other data point.
This page is a step-by-step guide on how to build your own voronoi diagram for the web, using React and D3.js.
It starts by describing how the data should be organized and explains how to run and plot a Delaunay triangulation. Based on this, it explains how to build the voronoi diagram. Finally it shows how this can be used for real life application like for a scatterplot or to build a voronoi treemap.
Everything starts with a set of two-dimensional points. Their coordinates are available with x representing the position on the horizontal axis and y being for the vertical axis.
As a result, the dataset is pretty simple: an array of objects that looks like this:
export const data = [
+How to build a Voronoi Diagram with React and D3.
Voronoi Diagram
A voronoi diagram is a partition of a plane into regions called voronoi cells. A voronoi cell consists of every point in the plane whose distance to its linked data point is less than or equal to its distance to any other data point.
This page is a step-by-step guide on how to build your own voronoi diagram for the web, using React and D3.js.
It starts by describing how the data should be organized and explains how to run and plot a Delaunay triangulation. Based on this, it explains how to build the voronoi diagram. Finally it shows how this can be used for real life application like for a scatterplot or to build a voronoi treemap.
Everything starts with a set of two-dimensional points. Their coordinates are available with x representing the position on the horizontal axis and y being for the vertical axis.
As a result, the dataset is pretty simple: an array of objects that looks like this:
I'm in the process of writing a complete blog post on the topic. Subscribe to the project to know when it's ready.
Voronoi inspiration
If you're looking for inspiration to create your next Voronoi, note that dataviz-inspiration.com showcases many examples. Definitely the best place to get ... inspiration!
dataviz-inspiration.com showcases hundreds of stunning dataviz projects. Have a look to get some ideas on how to make your Voronoi looks good!
The voronoi diagram is commonly used to detect the closest data point of the mouse position. This can be pretty useful to highlight the closest point without having to hover exactly over it.
In the example below, the closest dot will be highlighted with a red circle ⭕️ using the voronoi cells.
Use the voronoi algorithm to detect the closest point of the mouse position.
Variations
A glimpse of what it is possible to do using the voronoi diagram for data visualization.
Click on the overview below to get details and code.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/what-is-a-color.html b/what-is-a-color.html
index 5a2ea655..d968fb86 100644
--- a/what-is-a-color.html
+++ b/what-is-a-color.html
@@ -1,7 +1,7 @@
-What is a color
What is a color
There are so many ways to define a color when talking with a computer. Let's take a tour and see what's the most appropriate in a dataviz point of view.
Color on screen
A computer screen is made of thousands of pixels.
Each pixel generates three colors of light (red, green, and blue) and the different colors we see are due to different combinations and intensities of these three primary colors.
red, green and blue are the primary colors.
The RGB color model
The color information for each pixel is typically stored in a 24-bit format: 8 bit per primary color. This is 2^8 = 256 possibilities for each primary color. This means more than 16M possible color variations for each pixel (256^3)!
The HSV, HSL and HSB color model
That's the famous color wheel you see in every color picker!
On colorpicker.me you can define a color using its hue (vertical bar), its saturation (x axis of the square) and its lightness (vertical axis)
HSL stands for hue, saturation, lightness
HSV and HSB are the same and stand for hue, saturation, value or hue, saturation, brightness respectively.
But what does it even mean?
Hue: the initial color. Between 0 and 360.
Saturation: when closer to 100, the color shines. represents how “colorful” the color is. Intensity. Purity. When 0, you get grey
Lighness, Value, Brightness
The color cylinder of HSV
There are an alternative to the RGB color model to more closely align with the way human vision perceives color-making attributes
Indeed, it allows us to describe meaningful relationships between colors. For instance, to create 2 complementary colors you can select 2 colors on the opposite side of the wheel, with same saturation and brithness. Same principle for analogous, monochrome palette and other famous combinations.
HSV is criticized. This is because in HSV the V (value) is just a measure for the physical lightness of color, but not for the perceived brightness. So 2 colors with the same value are not perceive with the same lightness by the Human Eye. There is a hue-dependency of brightness in this model.
Note: pastel color = high lightness and low saturation.
The CIELAB color model
Try to fix this difference between computer and human perception.
unintuitive to use to generate colors
The cielab color space
The HCL color model
Stands for Hue-Chroma-Lightness. A cylindrical transformation of CIE Lab*
Luminance of a color
TODO
The Contrast Ratio
The Web Content Accessibility Guidelines (WCAG) include convenient quantitative recommendations for making a color accessible based on the minimum acceptable contrast of foreground against background.
Basically, you can compare the luminance of 2 colors and compute their contrast ratio. The luminance is computed using the amount of red, green and blue in it.
To see the exact formulas and compute the contrast ratio between 2 colors, see this notebook by Mike Bostock
A tool to compute the contrast ratio between 2 colors.
Note: the notebook provides the d3 code to compute this contrast ratio.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
+What is a color
What is a color
There are so many ways to define a color when talking with a computer. Let's take a tour and see what's the most appropriate in a dataviz point of view.
Color on screen
A computer screen is made of thousands of pixels.
Each pixel generates three colors of light (red, green, and blue) and the different colors we see are due to different combinations and intensities of these three primary colors.
red, green and blue are the primary colors.
The RGB color model
The color information for each pixel is typically stored in a 24-bit format: 8 bit per primary color. This is 2^8 = 256 possibilities for each primary color. This means more than 16M possible color variations for each pixel (256^3)!
The HSV, HSL and HSB color model
That's the famous color wheel you see in every color picker!
On colorpicker.me you can define a color using its hue (vertical bar), its saturation (x axis of the square) and its lightness (vertical axis)
HSL stands for hue, saturation, lightness
HSV and HSB are the same and stand for hue, saturation, value or hue, saturation, brightness respectively.
But what does it even mean?
Hue: the initial color. Between 0 and 360.
Saturation: when closer to 100, the color shines. represents how “colorful” the color is. Intensity. Purity. When 0, you get grey
Lighness, Value, Brightness
The color cylinder of HSV
There are an alternative to the RGB color model to more closely align with the way human vision perceives color-making attributes
Indeed, it allows us to describe meaningful relationships between colors. For instance, to create 2 complementary colors you can select 2 colors on the opposite side of the wheel, with same saturation and brithness. Same principle for analogous, monochrome palette and other famous combinations.
HSV is criticized. This is because in HSV the V (value) is just a measure for the physical lightness of color, but not for the perceived brightness. So 2 colors with the same value are not perceive with the same lightness by the Human Eye. There is a hue-dependency of brightness in this model.
Note: pastel color = high lightness and low saturation.
The CIELAB color model
Try to fix this difference between computer and human perception.
unintuitive to use to generate colors
The cielab color space
The HCL color model
Stands for Hue-Chroma-Lightness. A cylindrical transformation of CIE Lab*
Luminance of a color
TODO
The Contrast Ratio
The Web Content Accessibility Guidelines (WCAG) include convenient quantitative recommendations for making a color accessible based on the minimum acceptable contrast of foreground against background.
Basically, you can compare the luminance of 2 colors and compute their contrast ratio. The luminance is computed using the amount of red, green and blue in it.
To see the exact formulas and compute the contrast ratio between 2 colors, see this notebook by Mike Bostock
A tool to compute the contrast ratio between 2 colors.
Note: the notebook provides the d3 code to compute this contrast ratio.
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
Contact
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!
\ No newline at end of file
diff --git a/wordcloud.html b/wordcloud.html
index 6b738fa8..096ca652 100644
--- a/wordcloud.html
+++ b/wordcloud.html
@@ -1,4 +1,4 @@
-Wordcloud with React
Wordcloud
This page explains how to build a wordcloud using react and d3.js. It uses the d3-cloud plugin to compute the position of each word, and render them with react.
This section is rather short as I'm not a big fan of wordclouds. They can be quite misleading and you should consider building a barplot or a lollipop plot instead.
This page uses the d3-cloud plugin that you can install in your project with npm install d3-cloud
The Data
The data is an array. Each item is an object describing a word. Its name is provided, together with a related value that will be used to size the word on the final figure.
Note that you can add any additional property here, like a color, a font weight or anything else that you want to use to draw the word later on.
const data = [
+Wordcloud with React
Wordcloud
This page explains how to build a wordcloud using react and d3.js. It uses the d3-cloud plugin to compute the position of each word, and render them with react.
This section is rather short as I'm not a big fan of wordclouds. They can be quite misleading and you should consider building a barplot or a lollipop plot instead.
This page uses the d3-cloud plugin that you can install in your project with npm install d3-cloud
The Data
The data is an array. Each item is an object describing a word. Its name is provided, together with a related value that will be used to size the word on the final figure.
Note that you can add any additional property here, like a color, a font weight or anything else that you want to use to draw the word later on.
Everything starts by instantiating a wordcloud layout using thed3Cloud() function of the d3-cloud library.
const layout = d3Cloud()
@@ -11,4 +11,4 @@
});
This layout can then be called from a useEffect using layout.start(). The layout algorithm will loop through each word of the dataset and try to place them on the chart, avoiding overlaps with other words.
Once the loop is over, the layout algorithm will produce a words object and provide it to the end() function. This function update a state that stores the position and feature of each word.
It is thus possible to map through those word features and draw them using html, svg or canvas. Here is an example using HTML
Most basic Wordcloud made with react and d3.js
Todo: write better explanation
Todo: the layout algorithm currently provides unperfect values, resulting in a lot of word overlaps. Please tell me if you find where the bug is.
Warning
Wordclouds are useful for quickly perceiving the most prominent terms. They are widely used in media and well understood by the public. However, they are criticized for 2 main reasons:
Area is a poor metaphor of a numeric value, it is hardly perceive by the human eye
Longer words appear bigger by construction
To put it in a nutshell, wordclouds must be avoided. You can read more about that in data-to-viz. Why not consider a lollipop plot or a barplot instead?
👋 Hey, I'm Yan and I'm currently working on this project!
Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!