Tutorial on a Circular Packing with D3.js imbedded in Idyll:

This document explains how to set up a circular packing in D3.js

Create an HTML project:

In this tutorial we will create our HTML page with the text editor : Visual studio code.

Create your HTML page

First add a folder in your workspace, once it’s done, open VSCode and click on “Open folder…”. You will then be able to add a new file by clicking on the “Add file” button and name it “index.html”.
In this file you could for example write the code bellow :

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Tutorial D3.js</title>

        <!-- CSS -->
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <h1>Here is an HTML code</h1>

        <p>Hello world !</p>
    
    </body>
</html>

As you can see, inside the head tag you have:

<!-- CSS -->
<link rel="stylesheet" href="style.css">

That means your HTML page is linked to a CSS file.

Create your CSS file

Just like the way you created your HTML page, click on the “Add file” button and name it “style.css”.

Congrats, you’ve created your HTML project.

If you don’t like to refresh the browser every time you make a change in the code and you want to have it automatically refreshed, I recommend installing the extension Live Server in VSCode by clicking on the install button, you will finally get a build-in Live Server in your text editor.
You then just have to right-click on the VSCode content and choose “Open with Live Server”.


Every time you will save a file, it will automatically refresh in the new window.


Create an SVG and view it in a browser:

D3’s job is to create SVG’s which we can then render to the browser, so let’s talk a little bit about SVG’s before we dive into D3. SVG’s allows to create diagrams or things like logos in web pages. There are several ways to create SVG’s and view it in a browser.

  • The first way is by creating SVG’s directly in the HTML code using the tags just like you can see in the image :

As you can see you just have to create and SVG container with a width and a height. You can add any type of shapes or diagrams inside those tags to draw them. In this example a red circle will appear (we will talk about the properties cx, cy and r right after) but you can also create rectangles, lines, paths etc.

  • An another way to create SVG’s is by using D3 in javascript to create this SVG’s and render them into the DOM as tags. It’s going to output the same code as in the first way but just it will be created by using javascript and D3.

Important syntax to create SVG’s

Before starting with D3 let’s just see some syntax to create SVG’s such as circles for example.
So in your HTML code, create an SVG tag, as we said just before, this will be a container for all the different shapes that you want to create. But before that, you have to supply a width and height to this SVG container, that doesn’t mean that everything inside the container is going to be of this width and height, it just means that the SVG container itself is this width and height, it’s like the drawing board.

<svg width="960" height="960"></svg>

Now to add a circle in the container write the tag :

<svg width="960" height="960">
  <circle></circle>
</svg>

But you have to specify some properties if you want to view it in your browser.
The cx and cy properties indicates the position of the circle.
The fill property indicates the color of the circle, stroke indicates the outline and r his radius.
We can have for example :

<svg width="960" height="960">
  <circle cx= "250" cy= "250" r= "20" fill= "lightblue" stroke= "darkblue" ></circle>
</svg>

Now if you save that you will be able to view it on your browser : Here is how to create a circle directly inside the HTML code using these SVG tags. With D3 you don’t have to manually create all the different shapes, otherwise it would be very tiresome to create graphs or more complex visualizations. D3 will create our SVG’s for us.


Set up D3:

Include the D3.js library into your HTML web page

You will have to include the D3.js library into your HTML web page in order to use D3 to create data visualization.
To create a circular packing, you need to use this link of d3 version 4 library, the circular packing will not appear if you use a higher version of d3: https://d3js.org/d3.v4.min.js

Copy this link and paste it in your code at the bottom of your head tag. You should have something like this:
<script src="https://d3js.org/d3.v4.min.js"></script>

Javascript file

All the javascript will be written in an external file.
Create a new file that you can call “index.js”, all the D3 code will be written in this file to generate these different SVG’s elements.

Add the source of the script inside or outside the body tag, it doesn’t matter:
<script src="index.js"></script>

At the end you should have an HTML code like this :


Write your D3 code:

D3 will be communicating with the HTML page, it will either add, remove or change SVG elements into the DOM. So D3 needs to reach into the DOM.

For that you need to add a <div> tag in your body with an “id”.
To reach into the DOM and grab an element you have to use a method called .select(). But first you will have to create a variable which would be equal to D3, you will then have access now to the D3 library, and then use the method select and pass in what you want to select from the page, here we want to select the div element of the HTML page.
To recap, you should have a <div> tag in your HTML page with an “id” :

<div id="canvas"></div>

And we want in our index.js file, to select the div element :

var svg = d3.select("#canvas")

This select down here wraps the returned object to us (so the div element) which is going to be stored in the SVG variable in a d3 wrapper and gives us access to different methods and properties which we will ultimately use. (A wrapper is any entity that encapsulates (wraps around) another item)

Now, what you want to do is append an SVG tag to the variable you just created by using the method “append”. So you should have :

var svg = d3.select("#canvas")
    .append("svg")

As you know, the SVG container need a width and a height attribute to say how big the SVG container will be. To add those attributes to your element use the method .attr(). This method takes two arguments, the first argument is the attribute you want to apply to the element. The second argument will be the value of the attribute.

var svg = d3.select("#canvas") 
    .append("svg") 
    .attr("width", dimension) 
    .attr("height", dimension)
    .attr("id", "svg");

To simplify the code we’ve added a variable “dimension” at the very top of the code, which will be equal to 960:

var dimension=960;

For the moment, your index.html and index.js files should look like this:

Add a group element

This element groups all the shapes or elements together. It will group all our circles together. We are going to append this to the SVG, the element is g so write :

var g=svg.append("g")
    .attr("transform", "translate("+dimension/2+","+dimension/2+")");

The transform attribute we applied works just like in CSS, it allows to modify the coordinate space of the whole group. So we use the translate() function which takes the value dimension/2 in x-axis and y-axis so that the group element is well centered.

Next add a margin variable:

var margin=40;

It will make the circular packing smaller.

Add your data

To join your data to your different shapes, we are going to use the data method.
But first, how to get data from an external source ? Here we will see how to work with an external JSON file. You can create a new json file, in this example for this circular packing we’ve used the teams.json file representing all the different employees of Cerfacs.
To do so, click on the “Add file” button and name your file with ”.json” in extension. Then, you just have to enter your data in this file.
We are going to grab this data and then we are going to output a series of circles to the DOM using D3.
So in your index.js file, let’s grab the data that you want to base these circles on. To do that, use the method d3.json(). Between parentheses, enter the name of your file and set up the callback function.

d3.json("teams.json", function(data) {
  // Here will be written the rest of the code...
})

All the rest of the javascript code will be written inside this function.

This function, that it fires, is going to take the data as a callback and inside this function, we want to take the data and join it to our circle elements. Of course we don’t have any circle elements in our SVG yet, and before appending those, let’s first of all see the notion of pack layout.

Pack layout

D3 uses layout functions to help you create hierarchical chart, layout function takes your data as input and adds visual variables such as position and size to it.
The pack layout is a hierarchical data structure, circles are used to represent nodes. Each node has one parent node (node.parent), except for the root. And each node has one or more child nodes (node.children), except for the leaves.
In the json file, I’v added the “size” property to each node and I supplied the root node with the largest value and the values are descending the further down we come down the hierarchy.

You first have to create a pack layout function using d3.pack() like this :

var pack = d3.pack()

You have to then configure its size by passing an array [width, height] into the .size() method, we will set the size of the pack layout to the dimension variable minus the margin layout :

var pack = d3.pack()
    .size([dimension - margin, dimension - margin])
    .padding(2);

We are also giving to each circle a padding of 2 pixels because it’s more aesthetic.

To traverse hierarchical data you will have to use d3.hierarchy(). You can use this only if your data is in a hierarchical format, such as nested Json.
Then you must call the method .sum(), this function will return the desired value for each individual node, here it returns the size property.
Then you will have to use the .sort()method, it will sorts children at every level of the tree according to the specified comparator function.

var rootNode = d3.hierarchy(data)
      .sum(function(d) { return d.size; })
      .sort(function(a, b) { return b.value - a.value; });

Now, initiate the variable “nodes” like this :

var nodes = pack(rootNode).descendants();

And add the variables “focus” and “view”, they will be useful for later.

var focus = rootNode;

var view;

Set up the color of the graph

Here is the color variable which allows to define the shades of colors of the circular packing :

var color = d3.scaleLinear()
    .domain([-1, 5])
    .range(["hsl(152,80%,80%)", "hsl(228,30%,40%)"]) //or for example .range["red","yellow"]
    .interpolate(d3.interpolateHcl);

Add circle and text elements

You can now join circle elements to each descendant of the root:

var circle = g.selectAll("circle")
    .data(nodes)
    .enter().append("circle")
      .attr("class", function(d) { return d.parent ? d.children ? "node" : "node node--leaf" : "node node--root"; })
      .style("fill", function(d) { return d.children ? color(d.depth) : null; })
      .on("click", function(d) { if (focus !== d) zoom(d), d3.event.stopPropagation(); });

So here you select all the circles which are in the group element g (even if we know that there are none), then you have to join the data to it.
To create extra circle elements you will have to use the enter selection : .enter().
To make it simple, the enter selection act like a container for virtual elements and you can access the enter selection by chaining on the .enter() method after the data has been joined.
After accessing those virtual elements in the enter selection you have to append a circle for each one of these. It will appen a new circle to the DOM for each element inside the enter selection, for each virtual element.
Let’s add an attribute “class”. If the node is a parent, the class of the circle will be “node”, if it’s a children it will be “node node—leaf”, and otherwise it will be “node node—root”.
Now go to your CSS sheet and write :

/* the cursor will appear when the mouse is passed over the nodes */
.node {
  cursor: pointer;
}

/* a black stroke will appear on the node when the user hovers over it with the pointer */
.node:hover {
  stroke: #000;
  stroke-width: 1.5px;
}

/* the leaves are colored in white */
.node--leaf {
  fill: white;
}

Go back to your javascript file and add some style with .style() (It’s a way of writing CSS, all crammed into one line). If the node is a children, it will be colored according to d.depth, that means the depth of the nodes from the root.
Add an .on("click") behavior which call the function “zoom”, we will create this function later.
The last line d3.event.stopPropagation() is used to prevent the propagation of the event

Before talking about the function zoom, let’s see how to display text on the circular packing :

var text = g.selectAll("text") 
    .data(nodes)
    .enter().append("text")
      .attr("class", "label")
      .style("fill-opacity", function(d) { return d.parent === rootNode ? 1 : 0; })
      .style("display", function(d) { return d.parent === rootNode ? "inline" : "none"; })
      .text(function(d) { return d.data.name; });

And just add to your CSS sheet :

/* defines the font */
.label {
  font: 11px "Helvetica Neue", Helvetica, Arial, sans-serif;
  text-anchor: middle;
  text-shadow: 0 1px 0 #fff, 1px 0 0 #fff, -1px 0 0 #fff, 0 -1px 0 #fff;
}

/* when you move the mouse over the root node, leaf nodes or text, the pointer is disabled */
.label,
.node--root,
.node--leaf {
  pointer-events: none;
}

It is the same principle as for circle elements.
You select all the texts that are in the group element g (even if we know that there are none), then you have to join the data to it.
To create extra text elements you will have to use the enter selection : .enter(). And you will now be able to append some text.
Then add an attribute “class”, that will be useful for the CSS. You can add some style if you want.
And finally, the most important, use the method .text(), the “d” of the function references the data and will return d.data.name so set the text to the name property.

Now set a node variable to :

var node = g.selectAll("circle,text");

Let’s make the point, here’s what you should have in your index.jsand style.css files:

The zoom animation

First, write the function “zoomTo” :

function zoomTo(v) { 
    var k = dimension / v[2]; view = v;
    node.attr("transform", function(d) { return "translate(" + (d.x - v[0]) * k + "," + (d.y - v[1]) * k + ")"; });
    circle.attr("r", function(d) { return d.r * k; });
}

Without this, the circles will not appear on the graph because this function sets the radius of the circles as you can see in the last line. Each circle has an attribute “r” representing the radius. The function “d” returns the radius times the variable k.
The second line manages the arrangement of the circles and the texts with the transform attribute.

To call this function add the line :

zoomTo([rootNode.x, rootNode.y, rootNode.r * 2 + margin]);

Here is the function that allows the zoom event :

function zoom(d) {
    var focus0 = focus; focus = d;

    var transition = d3.transition()
        .duration(d3.event.altKey ? 7500 : 750) //oula pas supprimer
        .tween("zoom", function(d) {
          var i = d3.interpolateZoom(view, [focus.x, focus.y, focus.r * 2 + margin]);
          return function(t) { zoomTo(i(t)); };
        });

    transition.selectAll("text")
      .filter(function(d) { return d.parent === focus || this.style.display === "inline"; })
        .style("fill-opacity", function(d) { return d.parent === focus ? 1 : 0; })
        .on("start", function(d) { if (d.parent === focus) this.style.display = "inline"; })
        .on("end", function(d) { if (d.parent !== focus) this.style.display = "none"; });
}

To call this function at every click event, add in your code :

svg
  .style("background", color(-1))
  .on("click", function() { zoom(rootNode); });

This will set a color background and after each click the zoom function will be called with the rootNode as a parameter.

To recap here is the whole index.js file:
Congrats, you know now how to realise a Circular Packing in D3.js !


Include the Circular Packing in an Idyll post:

Create an Idyll project

To add this graph in an Idyll post you have to first create an Idyll project.
If you are beginning with the framework I recommend checking this article to see how to get started.

Integrate D3 into an Idyll project

To integrate D3 into your Idyll projects, write in your terminal:

npm install --save idyll-d3-component

Create a component

Add a new file in the “component” folder of your idyll project and name it “circular-packing.js”.

This file will contain our javascript code to generate the circular packing.
Write in your circular-packing.js file :

const D3Component = require('idyll-d3-component');
const d3 = require('d3');
const React = require('react');

class CircularPacking extends D3Component {

    // Here will be your code...

} module.exports = CircularPacking;

Idyll defines a custom D3Component that you can use to create a custom D3 component. To use it, you have to create a class that extends from it, and in this class, implement the initialize method :

const D3Component = require('idyll-d3-component');
const d3 = require('d3');
const React = require('react');

class CircularPacking extends D3Component {
  initialize(node) {
      // Here will be your code...
  }
  
} module.exports = CircularPacking;

This function is used to create all the necessary DOM elements.

Now you just have to stick the index.js code into the initialize function.
You should have something like that :

const D3Component = require('idyll-d3-component');
const d3 = require('d3');
const React = require('react');
<link rel="stylesheet" href="style.css"></link>

var margin=10;

class CircularPacking extends D3Component {
  initialize(node) {
    var dimension = node.getBoundingClientRect().width;
    
    var svg = d3.select(node)
        .append('svg')
        .attr("width", dimension)
        .attr("height", dimension)
        .attr("id", "svg");
        
    var g=svg.append('g')
    .attr("transform", "translate("+dimension/2+","+dimension/2+")");
    
    var data={
      "name": "CERFACS",
      "children": [
       {
        "name": "GlobalChange",
        "children": [
         {
          "name": "Permanents",
          "children": [
           {"name": "DUCHAINE", "size": 1200},
           {"name": "CASSOU", "size": 1200},
           {"name": "THUAL", "size": 1200},
           {"name": "COQUART", "size": 1200},
           {"name": "TERRAY", "size": 1200},
           {"name": "MOINE", "size": 1200},
           {"name": "VALCKE", "size": 1200},
           {"name": "BOE", "size": 1200},
           {"name": "ROGEL", "size": 1200},
           {"name": "SANCHEZ", "size": 1200},
           {"name": "PAGE", "size": 1200},
           {"name": "ROCHOUX", "size": 1200},
           {"name": "RICCI", "size": 1200},
           {"name": "MAISONNAVE", "size": 1200},
           {"name": "JONVILLE", "size": 1200},
           {"name": "CARIOLLE", "size": 1200},
           {"name": "AUGUSTE", "size": 1200},
           {"name": "SANDERSON", "size": 1200},
           {"name": "FISHER", "size": 1200},
           {"name": "EMILI", "size": 1200},
           {"name": "MIROUZE", "size": 1200}
          ]
         },
         {
          "name": "Temporary",
          "children": [
              {"name": "SAYSSET", "size": 1200},
              {"name": "DELOZZO", "size": 1200},
              {"name": "EFOUI", "size": 1200},
              {"name": "FREBOURG", "size": 1200},
              {"name": "ROUSSEAU", "size": 1200},
              {"name": "CHRIPKO", "size": 1200},
              {"name": "BROUSSET", "size": 1200},
              {"name": "MSADEK", "size": 1200},
              {"name": "BONNET", "size": 1200},
              {"name": "QASMI", "size": 1200},
              {"name": "PAUGAM", "size": 1200},
              {"name": "COLONNETTE", "size": 1200},
              {"name": "MARTIN DEL REY", "size": 1200},
              {"name": "LOPEZ", "size": 1200},
              {"name": "ASSIR", "size": 1200},
              {"name": "MASS", "size": 1200}
             ]
         }
        ]
       },
       {
        "name": "Algo/Coop",
        "children": [
         {
          "name": "Permanent",
          "children": [
              {"name": "MOHANAMURALY", "size": 1200},
              {"name": "GIRAUD", "size": 1200},
              {"name": "DAUPTAIN", "size": 1200},
              {"name": "WEAVER", "size": 1200},
              {"name": "GUROL", "size": 1200},
              {"name": "STAFFELBACH", "size": 1200},
              {"name": "LAPEYRE", "size": 1200},
              {"name": "MYCEK", "size": 1200},
              {"name": "KRUSE", "size": 1200},
              {"name": "RUEDE", "size": 1200}
             ]
         },
         {
          "name": "Temporary",
          "children": [
              {"name": "SUAU", "size": 1200},
              {"name": "GULLAUD", "size": 1200},
              {"name": "MONNIER", "size": 1200},
              {"name": "YZEL", "size": 1200},
              {"name": "BRIANT", "size": 1200},
              {"name": "DESTOUCHES", "size": 1200},
              {"name": "EDF PhD5", "size": 1200},
              {"name": "VENKOVIC", "size": 1200},
              {"name": "LELEUX", "size": 1200},
              {"name": "VONGPASEUT", "size": 1200},
              {"name": "HOSTE", "size": 1200},
              {"name": "COULON", "size": 1200},
              {"name": "JANG", "size": 1200},
              {"name": "JEANMASSON", "size": 1200},
              {"name": "DEMOLIS", "size": 1200},
              {"name": "ROBERT", "size": 1200},
              {"name": "PERREIRA", "size": 1200},
              {"name": "ROSSI", "size": 1200},
              {"name": "XING", "size": 1200},
              {"name": "BESOMBES", "size": 1200},
              {"name": "DROZDA", "size": 1200},
              {"name": "MATALON", "size": 1200},
              {"name": "MEQUIGNON", "size": 1200},
              {"name": "SENGUPTA", "size": 1200},
              {"name": "LEGAUX", "size": 1200}
             ]
         }
        ]
      },
       {
        "name": "ComputationalFluidDynamics",
        "children": [
         {
          "name": "Permanents",
          "children": [
              {"name": "LAERA", "size": 1200},
              {"name": "BOUSSUGE", "size": 1200},
              {"name": "POINSOT", "size": 1200},
              {"name": "GICQUEL", "size": 1200},
              {"name": "CUENOT", "size": 1200},
              {"name": "MONTAGNAC", "size": 1200},
              {"name": "ODIER", "size": 1200},
              {"name": "VERMOREL", "size": 1200},
              {"name": "RIBER", "size": 1200},
              {"name": "JARAVEL", "size": 1200},
              {"name": "DAVILLER", "size": 1200},
              {"name": "LABADENS", "size": 1200},
              {"name": "DOMBARD", "size": 1200}
             ]
         },
       {
        "name": "Temporary",
        "children": [
          {"name": "AJURIA", "size": 1200},
          {"name": "CELLIER", "size": 1200},
          {"name": "DUTKA", "size": 1200},
          {"name": "BOUDIN", "size": 1200},
          {"name": "PETIT", "size": 1200},
          {"name": "GOUT", "size": 1200},
          {"name": "STREMPFL", "size": 1200},
          {"name": "CANCES", "size": 1200},
          {"name": "NOUN", "size": 1200},
          {"name": "GARNIER", "size": 1200},
          {"name": "ROQUÉ", "size": 1200},
          {"name": "LESAFFRE", "size": 1200},
          {"name": "ROULAND", "size": 1200},
          {"name": "ALAS", "size": 1200},
          {"name": "CAPURSO", "size": 1200},
          {"name": "DURANTON", "size": 1200},
          {"name": "GIOUD", "size": 1200},
          {"name": "DABAS", "size": 1200},
          {"name": "AGOSTINELLI", "size": 1200},
          {"name": "RECIO", "size": 1200},
          {"name": "LAMBOLEY", "size": 1200},
          {"name": "PERROT", "size": 1200},
          {"name": "AGARWAL", "size": 1200},
          {"name": "WINGEL", "size": 1200},
          {"name": "BADHE", "size": 1200},
          {"name": "DETOMASO", "size": 1200},
          {"name": "DE BRAUER", "size": 1200},
          {"name": "CASTELLS", "size": 1200},
          {"name": "ASTOUL", "size": 1200},
          {"name": "PEDEN", "size": 1200},
          {"name": "NGUYEN", "size": 1200},
          {"name": "VIENNE", "size": 1200},
          {"name": "DEGRIGNY", "size": 1200},
          {"name": "MARTIN", "size": 1200},
          {"name": "SUBRAMANYA", "size": 1200},
          {"name": "RAPPENEAU", "size": 1200},
          {"name": "JOUHAUD", "size": 1200},
          {"name": "BOGOPOLSKY", "size": 1200},
          {"name": "VILLAFANA", "size": 1200},
          {"name": "LAFARGE", "size": 1200},
          {"name": "TRELEAVEN", "size": 1200},
          {"name": "MUSCAT", "size": 1200},
          {"name": "GIANOLI", "size": 1200},
          {"name": "KIFFER", "size": 1200},
          {"name": "NAESS", "size": 1200},
          {"name": "SCHWEIGER", "size": 1200},
          {"name": "WERNER", "size": 1200},
          {"name": "RENARD", "size": 1200},
          {"name": "DOUNIA", "size": 1200},
          {"name": "CRESPO", "size": 1200},
          {"name": "DUPUY", "size": 1200},
          {"name": "VILLARD", "size": 1200},
          {"name": "FERNEY", "size": 1200},
          {"name": "COUDRAY", "size": 1200},
          {"name": "MARCHAL", "size": 1200},
          {"name": "SHASTRY", "size": 1200},
          {"name": "DOUASBIN", "size": 1200},
          {"name": "GALLEN", "size": 1200},
          {"name": "MALE", "size": 1200},
          {"name": "WIRTZ", "size": 1200},
          {"name": "CAZERES", "size": 1200},
          {"name": "LAROCHE", "size": 1200},
          {"name": "PESTRE", "size": 1200},
          {"name": "DI RENZO", "size": 1200},
          {"name": "LAMELOISE", "size": 1200},
          {"name": "VANBERSEL", "size": 1200},
          {"name": "HOK", "size": 1200},
          {"name": "CHENG", "size": 1200},
          {"name": "BARLEON", "size": 1200},
          {"name": "BLANCHARD", "size": 1200},
          {"name": "CARMONA", "size": 1200},
          {"name": "OGIER", "size": 1200},
          {"name": "VILLENAVE", "size": 1200},
          {"name": "MEZIAT", "size": 1200},
          {"name": "LAMIDEL", "size": 1200},
          {"name": "MOCQUARD", "size": 1200},
          {"name": "GENTIL", "size": 1200},
          {"name": "ZAPATA", "size": 1200}
         ]
       }
      ]
      },
      {
          "name": "Support",
          "children": [
           {
            "name": "Administration",
            "children": [
              {"name": "ROFFI", "size": 1200},
              {"name": "CAMPASSENS", "size": 1200},
              {"name": "NASRI", "size": 1200},
              {"name": "LAMBERT", "size": 1200},
              {"name": "MERIENNE", "size": 1200},
              {"name": "MOITY", "size": 1200},
              {"name": "SINGLA", "size": 1200}
            ]
           },
         {
          "name": "Computer",
          "children": [
              {"name": "LAPORTE", "size": 1200},
              {"name": "MONNIER", "size": 1200},
              {"name": "DEJEAN", "size": 1200},
              {"name": "BLAIN", "size": 1200},
              {"name": "FLEURY", "size": 1200},
              {"name": "D'AST", "size": 1200}
          ]
         }
        ]
        }
      ]
     }

    var pack = d3.pack()
        .size([dimension - margin, dimension - margin])
        .padding(2);

    var rootNode = d3.hierarchy(data)
        .sum(function(d) { return d.size; })
        .sort(function(a, b) { return b.value - a.value; });
    
    var focus = rootNode,
        nodes = pack(rootNode).descendants(),
        view;

    var color = d3.scaleLinear()
        .domain([-1, 5])
        .range(["hsl(152,80%,80%)", "hsl(228,30%,40%)"])
        .interpolate(d3.interpolateHcl);

    var circle = g.selectAll("circle")
        .data(nodes)
        .enter().append("circle")
            .attr("class", function(d) { return d.parent ? d.children ? "node" : "node node--leaf" : "node node--root"; })
            .style("fill", function(d) { return d.children ? color(d.depth) : null; })
            .on("click", function(d) { if (focus !== d) zoom(d), d3.event.stopPropagation(); });

    var text = g.selectAll("text")
        .data(nodes)
        .enter().append("text")
            .attr("class", "label")
            .style("fill-opacity", function(d) { return d.parent === rootNode ? 1 : 0; })
            .style("display", function(d) { return d.parent === rootNode ? "inline" : "none"; })
            .text(function(d) { return d.data.name; });

    var node = g.selectAll("circle,text");

    function zoomTo(v) {
        var k = dimension / v[2]; view = v;
        node.attr("transform", function(d) { return "translate(" + (d.x - v[0]) * k + "," + (d.y - v[1]) * k + ")"; });
        circle.attr("r", function(d) { return d.r * k; });
    }

    zoomTo([rootNode.x, rootNode.y, rootNode.r * 2 + margin]);

    function zoom(d) {
        var focus0 = focus; focus = d;
        
        var transition = d3.transition()
            .duration(d3.event.altKey ? 7500 : 750)
            .tween("zoom", function(d) {
                var i = d3.interpolateZoom(view, [focus.x, focus.y, focus.r * 2 + margin]);
                return function(t) { zoomTo(i(t)); };
            });
        
        transition.selectAll("text")
            .filter(function(d) { return d.parent === focus || this.style.display === "inline"; })
            .style("fill-opacity", function(d) { return d.parent === focus ? 1 : 0; })
            .on("start", function(d) { if (d.parent === focus) this.style.display = "inline"; })
            .on("end", function(d) { if (d.parent !== focus) this.style.display = "none"; });
    }

    svg
        .style("background", color(-1))
        .on("click", function() { zoom(rootNode); });
  }
} module.exports = CircularPacking;

The only difference is that for the data we don’t use an external source.

Also the “dimension” variable is now equal to 690. But we have:

var dimension = node.getBoundingClientRect().width;

The getBoundingClientRect() method returns the size of an element and its position relative to the viewport, so thanks to that the dimension variable will take the perfect size depending on the available space in the post. But you can just give a number to the variable if you prefer.

Here “node” is like a <div> container. So instead of writing :

var svg = d3.select("#canvas") 
    .append("svg") 

Let’s write:

var svg = d3.select(node)
  .append('svg')

Add at the top of your circular-packing.js file this line :

<link rel="stylesheet" href="style.css"></link>

Then just go to the styles.css file of the idyll project and copy/past your own CSS file.

Display the component in the idyll post

You have to point Idyll to the folder where you created your Circular packing component, it will then be available in your markup.
So in your index.idyll file, instantiate the component like this :

[CircularPacking /]

You will now be able to view your Circular Packing in the Idyll post.