Dynamically Styling a D3 and Svelte Beeswarm Chart
In this lesson, we will learn how to dynamically style nodes in a force layout. The color and radii of nodes, represented as circles, in a beeswarm plot will be determined by specific data dimensions and scales defined via methods (e.g., scaleOrdinal for mapping discrete values of a categorical variable to visual elements like colors) of the d3-scale module.
Get the project source code below, and follow along with the lesson material.
Download Project Source CodeTo set up the project on your local machine, please follow the directions provided in the README.md
file. If you run into any issues with running the project source code, then feel free to reach out to the author in the course's Discord channel.
Lesson Transcript
[00:00 - 00:11] And this lesson will be styling our circles. So although our force diagram works and each circle is positioned properly and the force diagram is responsive, this still is pretty imperfect.
[00:12 - 00:24] Every circle looks identical and we have no idea why they are positioned the way they're positioned. First we're going to make each circle look distinct from one another. Now we can target two properties to do this.
[00:25 - 00:33] The first is each circle's fill or its color. Basically we're going to fill each circle according to its continent.
[00:34 - 00:41] So we have some clear grouping as well as on the y-axis also in each circle's fill. And the second will be each circle's size.
[00:42 - 00:55] We'll go ahead and add a radius to each circle that is dynamic and updates depending on that country's happiness score. So first let's do the easier of the two which is add a color to each circle.
[00:56 - 01:10] In order to do this, yes according to its continent, we're going to need a brand new D3 scale that's called scale ordinal. And observable as it usually does will have really good documentation for this and I would encourage you to read through this.
[01:11 - 01:19] But what you're going to notice over and over again is this use of color. In specific we could pass in a domain and range.
[01:20 - 01:27] It could be quantitative in nature or it could be categorical or ordinal. And it will return an equivalent position within the range.
[01:28 - 01:38] So this is essentially the exact same as what you've already seen in D3 scales. And that it takes in a domain which is an input and a range which is an output.
[01:39 - 01:46] And in our case it's pretty easy to imagine what our input and output are going to be. But first let's go ahead and import it.
[01:47 - 01:57] And again this is from the exact same D3 scale module. And then we're going to create a new scale and we will call it color scale.
[01:58 - 02:12] Now this is going to be scale ordinal again because that's what we just imported. And just to remind you it uses the exact same domain which is an input and range which is an output. So what is the input?
[02:13 - 02:22] Well you might notice that I place this directly below the continents group that we've already created online 57. Let's review what this is by console.loggingit.
[02:23 - 02:31] And what you'll notice is that continents... oh I need to comment on this because it'll be broken for now.
[02:32 - 03:11] Continence is an array of six items and you'll notice these are actually arranged according to their mean happiness score in ascending order where Oceana is the happiest continent according to the data. Although there's only two countries but still its mean is highest of those in the data. So we have six items in our continents array and already we can imagine this is what will be put within our domain right? We want to fill things according to their continent so that will be the domain. Then the question is what's the range? In the range we basically need six colors.
[03:12 - 03:40] You could invent these colors on your own or if you'd like you can copy the exact same colors that I use which I'll go ahead and paste right here. Now I'm going to save this and nothing's going to change yet because I'm not using the color scale but then using this is quite simple. I'm down here in our circle element that we're rendering online 92 right now we 're passing this constant fill of steel blue. Let's instead pass the color scale of node.cont inent.
[03:41 - 12:39] Remember that node.continent exists within our data right? We have continent as a property and then we're passing this so it'll look for the equivalent input and based on its position within color scale we'll return its equivalent color in range. Okay so if just to bel abor this point maybe too much if Africa is the first point in our continents array if we pass Africa into color scale it will return the first item in our color range in this case DDAODD and the same will go for the second item Asia will be equivalent to this South America will be equivalent to this North America Europe, Oceania. So that's how we'll use color scale let's go ahead and save and we already see these beautiful colors begin to populate and replace our ugly steel blue. You might notice you can't really see these top elements so I would recommend you put a stroke on your circles which is as simple as stroke and whatever color you want I'm going to do a stroke of black optionally you can make them thicker if you want you know you can make it a very thick width although I would recommend just using one which is also the default so you can omit it and now we have each of these circles looking pretty nice we actually start to see the beginnings of a real styled chart because all of our colors all of our circles are not the same color so that's the first thing that we're going to tackle is styling our circles color you know to be consistent you might want to import the scale ordinal on the same line that you import the other scales so you can move it up to line 49 that's optional but if you'd like to do that to keep your imports consistent you can definitely do so now we have our circles properly filled and now we can move on to the second objective of this lesson which is sizing our circles according to their happiness score now this is going to use yet another d3 scale this one called d3 scale square root d3 in depth article kind of explains the intuition behind why we use it and the reason is we want to set the area rather than the radius proportional to data and this is just how you know humans and brains perceive the size of elements you know circles increase according to their area so we need to account for that in how we create scales it's pretty fun to read about this so I would encourage you to do so just know that if you're working with circles you should probably be using scale square root with that being said the internals of scale square root are essentially the same as every other d3 scale you've seen they take in a domain and a range where domain is the input and range is the output so it's very easy to see how we could use this in our example we'll go ahead and create a new d3 scale and we'll call it radius scale and this will be a scale square root with a domain again I'm writing this over and over again so it really gets in your head range is output now what is the domain and what is the range right those are the questions well we already know the domain of potential happiness scores and we have it right up here on line 51 remember as I said earlier that we're kind of double encoding this happiness score we already know from our brains that it goes from 0 to 10 and that realistically nothing goes below one or above nine which is why we made this the x scale domain so we actually want it to be the exact same domain for our radius scale so we'll go ahead and paste that here now what do we want the range to be well the answer is whatever you want the size of your circles to be so let's actually just put some some testing numbers in and see what we like and then come to a final conclusion later start off with 1 and 15 and then radius scale now can be used in our application down here we have our equals radius where radius is this hard coded variable that you might recall is five we don't want to use the hard coded radius variable anymore we want to use radius scale of node dot happiness let's save this and see what happens we see that our circles definitely increase in size but they don't look very good there's something broken and what's broken is that these circles are overlapping the reason for this is we 're referencing radius that all caps variable in one other place and that's in our force simulation up here in this reactive block you might remember that we have a force collide which basically tells the force diagram how to account for each element's size so that they don't overlap with one another right it prevents collision we're using radius here which is obviously an issue so rather than passing radius directly we need to use our radius scale now right now the force collide is then passing in radius as a standalone variable but we need to use another accessor just like this where we take in d and we return d dot happiness just like this where we take in d and we return d dot continent we need to take in d and return radius scale of happiness now you might see that it's suggesting you add one and the reason for this is that there's a little bit more space between elements so you definitely can do that if you'd like you don't need to it's up to personal preference but now we can see that our circles are actually being positioned properly they're actually respecting each other's radii so they're not all overlapping in some huge jumbled mess now the issue is obviously that they're just too big so this is why i said we should just go ahead and try some test numbers and then adjust later i think one might be too small over here maybe so i'm gonna make that three and then i think 15 is simply too large let's change it to eight and then let's refresh and now you see that these elements actually look pretty good there's a very marginal difference between the three and the eight but it's enough to be noticeable and i think that that's that's pretty good i like these sizes so i'm gonna leave three and eight which i think is pretty good but what you might also want to do is change these on really small screens so say you were even tinier than this screen you were on a very small mobile device and these bubbles are looking a little bit crowded we could make this element reactive to the size of the entire application to make even smaller bubbles on small screens how do we do that well we're going to want to pay attention to the width variable and update scale square root if it passes a certain threshold how do we do that yes a reactive label you're going to do dollar sign colon radius scale and then where do we want to reflect the width within our application or within our scale it's going to be in the range right if width is above a certain number or below a certain number we want to return a different range smaller numbers then if it is above or below that threshold so let's go ahead and say if width is less than 568 random breakpoint but people love to use 68 as kind of their suffix like breakpoints and web design you might see 968 a lot i don't know it's up to you but you could do 568 you could do 600 whatever you want and then if it's smaller than 568 we want these circles to be even tinier let's say we want it to be two to six and if not if it's above 568 we want it to be three to eight let's go ahead and save this refresh now you can see those bubbles are a bit smaller and if i resize you see they get bigger so notice right as i pass this 568 point you see the bubbles get bigger smaller so you could make this any number of things you could check for an even smaller break point like 368 make them super tiny but here you know what you would be using is this ternary operator which we've reviewed before the question mark and the colon basically says if this is true return this if it's false return this so here we're dynamically creating a new radius scale if and when we need to based on the width of our screen so where what have we done in this lesson we've gone from an unstyled yet functional d3 force diagram to a pretty nice looking um force diagram that has circles that are styled according to their content
[00:00 - 00:11] And this lesson will be styling our circles. So although our force diagram works and each circle is positioned properly and the force diagram is responsive, this still is pretty imperfect.
[00:12 - 00:24] Every circle looks identical and we have no idea why they are positioned the way they're positioned. First we're going to make each circle look distinct from one another. Now we can target two properties to do this.
[00:25 - 00:33] The first is each circle's fill or its color. Basically we're going to fill each circle according to its continent.
[00:34 - 00:41] So we have some clear grouping as well as on the y-axis also in each circle's fill. And the second will be each circle's size.
[00:42 - 00:55] We'll go ahead and add a radius to each circle that is dynamic and updates depending on that country's happiness score. So first let's do the easier of the two which is add a color to each circle.
[00:56 - 01:10] In order to do this, yes according to its continent, we're going to need a brand new D3 scale that's called scale ordinal. And observable as it usually does will have really good documentation for this and I would encourage you to read through this.
[01:11 - 01:19] But what you're going to notice over and over again is this use of color. In specific we could pass in a domain and range.
[01:20 - 01:27] It could be quantitative in nature or it could be categorical or ordinal. And it will return an equivalent position within the range.
[01:28 - 01:38] So this is essentially the exact same as what you've already seen in D3 scales. And that it takes in a domain which is an input and a range which is an output.
[01:39 - 01:46] And in our case it's pretty easy to imagine what our input and output are going to be. But first let's go ahead and import it.
[01:47 - 01:57] And again this is from the exact same D3 scale module. And then we're going to create a new scale and we will call it color scale.
[01:58 - 02:12] Now this is going to be scale ordinal again because that's what we just imported. And just to remind you it uses the exact same domain which is an input and range which is an output. So what is the input?
[02:13 - 02:22] Well you might notice that I place this directly below the continents group that we've already created online 57. Let's review what this is by console.loggingit.
[02:23 - 02:31] And what you'll notice is that continents... oh I need to comment on this because it'll be broken for now.
[02:32 - 03:11] Continence is an array of six items and you'll notice these are actually arranged according to their mean happiness score in ascending order where Oceana is the happiest continent according to the data. Although there's only two countries but still its mean is highest of those in the data. So we have six items in our continents array and already we can imagine this is what will be put within our domain right? We want to fill things according to their continent so that will be the domain. Then the question is what's the range? In the range we basically need six colors.
[03:12 - 03:40] You could invent these colors on your own or if you'd like you can copy the exact same colors that I use which I'll go ahead and paste right here. Now I'm going to save this and nothing's going to change yet because I'm not using the color scale but then using this is quite simple. I'm down here in our circle element that we're rendering online 92 right now we 're passing this constant fill of steel blue. Let's instead pass the color scale of node.cont inent.
[03:41 - 12:39] Remember that node.continent exists within our data right? We have continent as a property and then we're passing this so it'll look for the equivalent input and based on its position within color scale we'll return its equivalent color in range. Okay so if just to bel abor this point maybe too much if Africa is the first point in our continents array if we pass Africa into color scale it will return the first item in our color range in this case DDAODD and the same will go for the second item Asia will be equivalent to this South America will be equivalent to this North America Europe, Oceania. So that's how we'll use color scale let's go ahead and save and we already see these beautiful colors begin to populate and replace our ugly steel blue. You might notice you can't really see these top elements so I would recommend you put a stroke on your circles which is as simple as stroke and whatever color you want I'm going to do a stroke of black optionally you can make them thicker if you want you know you can make it a very thick width although I would recommend just using one which is also the default so you can omit it and now we have each of these circles looking pretty nice we actually start to see the beginnings of a real styled chart because all of our colors all of our circles are not the same color so that's the first thing that we're going to tackle is styling our circles color you know to be consistent you might want to import the scale ordinal on the same line that you import the other scales so you can move it up to line 49 that's optional but if you'd like to do that to keep your imports consistent you can definitely do so now we have our circles properly filled and now we can move on to the second objective of this lesson which is sizing our circles according to their happiness score now this is going to use yet another d3 scale this one called d3 scale square root d3 in depth article kind of explains the intuition behind why we use it and the reason is we want to set the area rather than the radius proportional to data and this is just how you know humans and brains perceive the size of elements you know circles increase according to their area so we need to account for that in how we create scales it's pretty fun to read about this so I would encourage you to do so just know that if you're working with circles you should probably be using scale square root with that being said the internals of scale square root are essentially the same as every other d3 scale you've seen they take in a domain and a range where domain is the input and range is the output so it's very easy to see how we could use this in our example we'll go ahead and create a new d3 scale and we'll call it radius scale and this will be a scale square root with a domain again I'm writing this over and over again so it really gets in your head range is output now what is the domain and what is the range right those are the questions well we already know the domain of potential happiness scores and we have it right up here on line 51 remember as I said earlier that we're kind of double encoding this happiness score we already know from our brains that it goes from 0 to 10 and that realistically nothing goes below one or above nine which is why we made this the x scale domain so we actually want it to be the exact same domain for our radius scale so we'll go ahead and paste that here now what do we want the range to be well the answer is whatever you want the size of your circles to be so let's actually just put some some testing numbers in and see what we like and then come to a final conclusion later start off with 1 and 15 and then radius scale now can be used in our application down here we have our equals radius where radius is this hard coded variable that you might recall is five we don't want to use the hard coded radius variable anymore we want to use radius scale of node dot happiness let's save this and see what happens we see that our circles definitely increase in size but they don't look very good there's something broken and what's broken is that these circles are overlapping the reason for this is we 're referencing radius that all caps variable in one other place and that's in our force simulation up here in this reactive block you might remember that we have a force collide which basically tells the force diagram how to account for each element's size so that they don't overlap with one another right it prevents collision we're using radius here which is obviously an issue so rather than passing radius directly we need to use our radius scale now right now the force collide is then passing in radius as a standalone variable but we need to use another accessor just like this where we take in d and we return d dot happiness just like this where we take in d and we return d dot continent we need to take in d and return radius scale of happiness now you might see that it's suggesting you add one and the reason for this is that there's a little bit more space between elements so you definitely can do that if you'd like you don't need to it's up to personal preference but now we can see that our circles are actually being positioned properly they're actually respecting each other's radii so they're not all overlapping in some huge jumbled mess now the issue is obviously that they're just too big so this is why i said we should just go ahead and try some test numbers and then adjust later i think one might be too small over here maybe so i'm gonna make that three and then i think 15 is simply too large let's change it to eight and then let's refresh and now you see that these elements actually look pretty good there's a very marginal difference between the three and the eight but it's enough to be noticeable and i think that that's that's pretty good i like these sizes so i'm gonna leave three and eight which i think is pretty good but what you might also want to do is change these on really small screens so say you were even tinier than this screen you were on a very small mobile device and these bubbles are looking a little bit crowded we could make this element reactive to the size of the entire application to make even smaller bubbles on small screens how do we do that well we're going to want to pay attention to the width variable and update scale square root if it passes a certain threshold how do we do that yes a reactive label you're going to do dollar sign colon radius scale and then where do we want to reflect the width within our application or within our scale it's going to be in the range right if width is above a certain number or below a certain number we want to return a different range smaller numbers then if it is above or below that threshold so let's go ahead and say if width is less than 568 random breakpoint but people love to use 68 as kind of their suffix like breakpoints and web design you might see 968 a lot i don't know it's up to you but you could do 568 you could do 600 whatever you want and then if it's smaller than 568 we want these circles to be even tinier let's say we want it to be two to six and if not if it's above 568 we want it to be three to eight let's go ahead and save this refresh now you can see those bubbles are a bit smaller and if i resize you see they get bigger so notice right as i pass this 568 point you see the bubbles get bigger smaller so you could make this any number of things you could check for an even smaller break point like 368 make them super tiny but here you know what you would be using is this ternary operator which we've reviewed before the question mark and the colon basically says if this is true return this if it's false return this so here we're dynamically creating a new radius scale if and when we need to based on the width of our screen so where what have we done in this lesson we've gone from an unstyled yet functional d3 force diagram to a pretty nice looking um force diagram that has circles that are styled according to their content