Entering the Upside Down of Pivot Grid: A Stranger Things Data-Viz Tutorial
Source: Dev.to
No, it’s not another holiday project, but the subject is just as captivating. I’m sure most of you have been binge-watching “Stranger Things” season 5, and I’m not the exception. This mysterious, 1980s-style story inspired me to create a data-viz project in its style. So keep reading, I promise it’s going to be pretty interesting! No doubt, for a project like this, I should have an appropriate dataset, so I found “Stranger Things Episode Ratings” on Kaggle. It’s intriguing to find out what the most highly rated episode is per season and throughout the whole series, right? For visualizing this entire dataset in a table format, WebDataRocks will assist us. It’s pretty intuitive to use and offers numerous customization options.
So, let’s step into the Upside Down of this dataset 🔦 Step 1: Adding WebDataRocks Library and Styles in HTML 📦 As an initial step, we should load styles and the container of WebDataRocks: Moreover, here I will use the built-in theme “Striped-Teal.” (By the way, WebDataRocks suggests six more themes and allows creating your own) Step 2: Initializing the Pivot Table 🚀 Now, we need to render the pivot table for future display of data: For fetching the data, as you see, we call the getData() function, so let’s get it done now: However, I guess we don’t need that much information, which is stored in the dataset, so let’s display only the most important one. To achieve it, we need to add the Slice Object to the pivot component: As you can see, I’ve decided to display only the Episode Number, Year, Title, and Runtime. Moreover, for convenience, let’s display the Episode Number in ascending order. That means that it will go from the first episode to the newest one, and here sorting in WebDataRocks will help us make it easy. All we need to do is call the sort and set it to asc. Step 3: Conditional Formatting 🖌️ Okay, but it would be perfect somehow to highlight the most rated episodes, wouldn’t it? So, let’s do it by using conditional formatting in WebDataRocks! By using the code snippet above, we will highlight episodes with a rating of 9 or higher. Step 4: Styling the Heading 🎨 At first, I wanted so much to recreate the heading in the same style as “Stranger Things” has: font, colors, structure, etc. And I think I achieved it! Let’s take a closer look. As usual, start with an easy div element in your HTML: Now, we center our text and give it a dark cinematic background: To create something similar to the well-known “Stranger Things” red glowing, we should set these changes: Step 5: Upside Down World Effect 🔮 And here’s the time for actual magic, and I guess the main feature of this project: Upside Down World’s version of the demo! Being more precise, there will be a button, clicking on which will move the user to those mysterious and scary dimensions. At the beginning, let’s simply add a button that will be responsible for these transitions. Next step is to add our future spotlight effect wrapper: Now time to edit CSS code, here is applied all changed styles: Then, complete the project with a bit of JavaScript that will actually make our styles flip from one world to another: Finally, the last adjustments, with this code, we will make our lighter move: And that’s it, you’ve just walked through a full Stranger Things–themed data visualization with a dynamic Upside Down effect, custom styling, conditional formatting, and interactive spotlight movement. For better experience I recommend opening it in a new tab. I’d love to hear what you think! Did you enjoy the transition to the Upside Down? Any ideas for improvements?
And tell me, are you also counting down the days to the Season 5 finale on December 31? Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse CODE_BLOCK:
<link id="wdr-theme" href="https://cdn.webdatarocks.com/latest/theme/stripedteal/webdatarocks.min.css" rel="stylesheet" />
<script src="https://cdn.webdatarocks.com/latest/webdatarocks.toolbar.min.js"></script>
<script src="https://cdn.webdatarocks.com/latest/webdatarocks.js"></script> Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
<link id="wdr-theme" href="https://cdn.webdatarocks.com/latest/theme/stripedteal/webdatarocks.min.css" rel="stylesheet" />
<script src="https://cdn.webdatarocks.com/latest/webdatarocks.toolbar.min.js"></script>
<script src="https://cdn.webdatarocks.com/latest/webdatarocks.js"></script> CODE_BLOCK:
<link id="wdr-theme" href="https://cdn.webdatarocks.com/latest/theme/stripedteal/webdatarocks.min.css" rel="stylesheet" />
<script src="https://cdn.webdatarocks.com/latest/webdatarocks.toolbar.min.js"></script>
<script src="https://cdn.webdatarocks.com/latest/webdatarocks.js"></script> CODE_BLOCK:
var pivot = new WebDataRocks({ container: "wdr-component", width: "100%", height: 490, report: { dataSource: { data: getData() }, options: { grid: { showTotals: "off", showGrandTotals: "off" } } }
}); Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
var pivot = new WebDataRocks({ container: "wdr-component", width: "100%", height: 490, report: { dataSource: { data: getData() }, options: { grid: { showTotals: "off", showGrandTotals: "off" } } }
}); CODE_BLOCK:
var pivot = new WebDataRocks({ container: "wdr-component", width: "100%", height: 490, report: { dataSource: { data: getData() }, options: { grid: { showTotals: "off", showGrandTotals: "off" } } }
}); CODE_BLOCK:
function getData() { return [ { index: {caption: "index", type: "number"}, Episode_Number: {caption: "Episode Number", type: "number"}, Title: {caption: "Title", type: "string" }, Image_url: {caption: "Image_url", type: "string"}, Year: {caption: "Year", type: "string"}, Description: {caption: "Description", type: "string"}, Genre: {caption: "Genre", type: "string"}, Runtime: {caption: "Runtime", type: "string"}, Rating: {caption: "Rating", type: "number"} }
...
} Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
function getData() { return [ { index: {caption: "index", type: "number"}, Episode_Number: {caption: "Episode Number", type: "number"}, Title: {caption: "Title", type: "string" }, Image_url: {caption: "Image_url", type: "string"}, Year: {caption: "Year", type: "string"}, Description: {caption: "Description", type: "string"}, Genre: {caption: "Genre", type: "string"}, Runtime: {caption: "Runtime", type: "string"}, Rating: {caption: "Rating", type: "number"} }
...
} CODE_BLOCK:
function getData() { return [ { index: {caption: "index", type: "number"}, Episode_Number: {caption: "Episode Number", type: "number"}, Title: {caption: "Title", type: "string" }, Image_url: {caption: "Image_url", type: "string"}, Year: {caption: "Year", type: "string"}, Description: {caption: "Description", type: "string"}, Genre: {caption: "Genre", type: "string"}, Runtime: {caption: "Runtime", type: "string"}, Rating: {caption: "Rating", type: "number"} }
...
} CODE_BLOCK:
var pivot = new WebDataRocks({
… report: { dataSource: { data: getData() }, slice: { rows: [ {uniqueName: "Episode_Number"}, {uniqueName: "Year"}, {uniqueName: "Title"}, {uniqueName: "Runtime"} ], columns: [ {uniqueName: "Measures"} ], measures: [ {uniqueName: "Rating" , aggregation: "sum"} ], expands: {expandAll: true} },
… }
}); Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
var pivot = new WebDataRocks({
… report: { dataSource: { data: getData() }, slice: { rows: [ {uniqueName: "Episode_Number"}, {uniqueName: "Year"}, {uniqueName: "Title"}, {uniqueName: "Runtime"} ], columns: [ {uniqueName: "Measures"} ], measures: [ {uniqueName: "Rating" , aggregation: "sum"} ], expands: {expandAll: true} },
… }
}); CODE_BLOCK:
var pivot = new WebDataRocks({
… report: { dataSource: { data: getData() }, slice: { rows: [ {uniqueName: "Episode_Number"}, {uniqueName: "Year"}, {uniqueName: "Title"}, {uniqueName: "Runtime"} ], columns: [ {uniqueName: "Measures"} ], measures: [ {uniqueName: "Rating" , aggregation: "sum"} ], expands: {expandAll: true} },
… }
}); CODE_BLOCK:
{uniqueName: "Episode_Number", sort: "asc"}, Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
{uniqueName: "Episode_Number", sort: "asc"}, CODE_BLOCK:
{uniqueName: "Episode_Number", sort: "asc"}, CODE_BLOCK:
conditions: [ { formula: "#value >= 9", measure: "Rating", format: { backgroundColor: "#007A6E", color: "#FFFFFF", fontFamily: "Arial", fontSize: "12px" } } Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
conditions: [ { formula: "#value >= 9", measure: "Rating", format: { backgroundColor: "#007A6E", color: "#FFFFFF", fontFamily: "Arial", fontSize: "12px" } } CODE_BLOCK:
conditions: [ { formula: "#value >= 9", measure: "Rating", format: { backgroundColor: "#007A6E", color: "#FFFFFF", fontFamily: "Arial", fontSize: "12px" } } CODE_BLOCK:
<h2 id="title">Stranger Things Episode Rating</h2> Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
<h2 id="title">Stranger Things Episode Rating</h2> CODE_BLOCK:
<h2 id="title">Stranger Things Episode Rating</h2> CODE_BLOCK:
body { height: 100%; padding: 30px; margin: 0; display: grid; place-items: center; background: #100d08;
} Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
body { height: 100%; padding: 30px; margin: 0; display: grid; place-items: center; background: #100d08;
} CODE_BLOCK:
body { height: 100%; padding: 30px; margin: 0; display: grid; place-items: center; background: #100d08;
} CODE_BLOCK:
h2 { margin: 0 0 20px 0; font-family: "Benguiat Bold", "Times New Roman", serif; // Typical Stranger Things font font-size: 2.5rem; font-weight: bold; text-align: center; margin-bottom: 2rem; /* neon outlight */ color: #000; text-shadow: 0 0 5px #ff0000; -webkit-text-stroke: 2px #ff0000; text-stroke: 2px #ff0000; position: relative; z-index: 500;
} Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
h2 { margin: 0 0 20px 0; font-family: "Benguiat Bold", "Times New Roman", serif; // Typical Stranger Things font font-size: 2.5rem; font-weight: bold; text-align: center; margin-bottom: 2rem; /* neon outlight */ color: #000; text-shadow: 0 0 5px #ff0000; -webkit-text-stroke: 2px #ff0000; text-stroke: 2px #ff0000; position: relative; z-index: 500;
} CODE_BLOCK:
h2 { margin: 0 0 20px 0; font-family: "Benguiat Bold", "Times New Roman", serif; // Typical Stranger Things font font-size: 2.5rem; font-weight: bold; text-align: center; margin-bottom: 2rem; /* neon outlight */ color: #000; text-shadow: 0 0 5px #ff0000; -webkit-text-stroke: 2px #ff0000; text-stroke: 2px #ff0000; position: relative; z-index: 500;
} CODE_BLOCK:
<button id="toggle-spotlight" title="Turn the light off">⬇️ Go to the Upside Down</button> Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
<button id="toggle-spotlight" title="Turn the light off">⬇️ Go to the Upside Down</button> CODE_BLOCK:
<button id="toggle-spotlight" title="Turn the light off">⬇️ Go to the Upside Down</button> CODE_BLOCK:
<div id="wrapper"> <div class="spotlight off"></div> <div id="wdr-component"></div>
</div> Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
<div id="wrapper"> <div class="spotlight off"></div> <div id="wdr-component"></div>
</div> CODE_BLOCK:
<div id="wrapper"> <div class="spotlight off"></div> <div id="wdr-component"></div>
</div> CODE_BLOCK:
h2 { font-family: "Benguiat Bold", serif; font-size: 2.5rem; color: #000; text-shadow: 0 0 5px #ff0000; -webkit-text-stroke: 2px #ff0000;
}
h2.upside-down { transform: rotate(180deg); text-shadow: 0 0 5px #ff4444; -webkit-text-stroke: 2px #ff4444;
}
.spotlight { position: absolute; inset: 0; background-image: radial-gradient( circle at var(--clientX,50%) var(--clientY,50%), transparent 6em, black 8em );
}
.spotlight.off { opacity: 0;
}
#toggle-spotlight { position: absolute; top: 1rem; left: 1rem; background: #880808; color: #fff; padding: 8px 14px; border-radius: 8px; cursor: pointer;
} Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
h2 { font-family: "Benguiat Bold", serif; font-size: 2.5rem; color: #000; text-shadow: 0 0 5px #ff0000; -webkit-text-stroke: 2px #ff0000;
}
h2.upside-down { transform: rotate(180deg); text-shadow: 0 0 5px #ff4444; -webkit-text-stroke: 2px #ff4444;
}
.spotlight { position: absolute; inset: 0; background-image: radial-gradient( circle at var(--clientX,50%) var(--clientY,50%), transparent 6em, black 8em );
}
.spotlight.off { opacity: 0;
}
#toggle-spotlight { position: absolute; top: 1rem; left: 1rem; background: #880808; color: #fff; padding: 8px 14px; border-radius: 8px; cursor: pointer;
} CODE_BLOCK:
h2 { font-family: "Benguiat Bold", serif; font-size: 2.5rem; color: #000; text-shadow: 0 0 5px #ff0000; -webkit-text-stroke: 2px #ff0000;
}
h2.upside-down { transform: rotate(180deg); text-shadow: 0 0 5px #ff4444; -webkit-text-stroke: 2px #ff4444;
}
.spotlight { position: absolute; inset: 0; background-image: radial-gradient( circle at var(--clientX,50%) var(--clientY,50%), transparent 6em, black 8em );
}
.spotlight.off { opacity: 0;
}
#toggle-spotlight { position: absolute; top: 1rem; left: 1rem; background: #880808; color: #fff; padding: 8px 14px; border-radius: 8px; cursor: pointer;
} COMMAND_BLOCK:
toggleBtn.addEventListener("click", () => { spotlightEnabled = !spotlightEnabled; spotlight.classList.toggle("off", !spotlightEnabled); title.classList.toggle("upside-down", spotlightEnabled); if (spotlightEnabled) { themeLink.href = lightBlueTheme; toggleBtn.textContent = "🚲 Go back"; toggleBtn.title = "Light up the grid"; } else { themeLink.href = defaultTheme; toggleBtn.textContent = "⬇️ Go to the Upside Down"; toggleBtn.title = "Turn the light off"; }
}); Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
toggleBtn.addEventListener("click", () => { spotlightEnabled = !spotlightEnabled; spotlight.classList.toggle("off", !spotlightEnabled); title.classList.toggle("upside-down", spotlightEnabled); if (spotlightEnabled) { themeLink.href = lightBlueTheme; toggleBtn.textContent = "🚲 Go back"; toggleBtn.title = "Light up the grid"; } else { themeLink.href = defaultTheme; toggleBtn.textContent = "⬇️ Go to the Upside Down"; toggleBtn.title = "Turn the light off"; }
}); COMMAND_BLOCK:
toggleBtn.addEventListener("click", () => { spotlightEnabled = !spotlightEnabled; spotlight.classList.toggle("off", !spotlightEnabled); title.classList.toggle("upside-down", spotlightEnabled); if (spotlightEnabled) { themeLink.href = lightBlueTheme; toggleBtn.textContent = "🚲 Go back"; toggleBtn.title = "Light up the grid"; } else { themeLink.href = defaultTheme; toggleBtn.textContent = "⬇️ Go to the Upside Down"; toggleBtn.title = "Turn the light off"; }
}); COMMAND_BLOCK:
wrapper.addEventListener("mousemove", (e) => { const rect = wrapper.getBoundingClientRect(); const x = e.clientX - rect.left; const y = e.clientY - rect.top; wrapper.style.setProperty("--clientX", x + "px"); wrapper.style.setProperty("--clientY", y + "px");
}); Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
wrapper.addEventListener("mousemove", (e) => { const rect = wrapper.getBoundingClientRect(); const x = e.clientX - rect.left; const y = e.clientY - rect.top; wrapper.style.setProperty("--clientX", x + "px"); wrapper.style.setProperty("--clientY", y + "px");
}); COMMAND_BLOCK:
wrapper.addEventListener("mousemove", (e) => { const rect = wrapper.getBoundingClientRect(); const x = e.clientX - rect.left; const y = e.clientY - rect.top; wrapper.style.setProperty("--clientX", x + "px"); wrapper.style.setProperty("--clientY", y + "px");
});