Building an API-driven dance party

Hey everyone! Are you excited about anything this beautiful Spring Friday? I am excited to be back to writing another tutorial after a month-long hiatus… and also excited to make it about one of my favorite video games of all time: Pokémon! As always, if you have an idea for a next topic, be sure to let me know in the comments!

"HTML-only, no JS!" ✨🪄

In previous tutorials, I focused more on design: I showed how to make a simple gradient picker, or a more flashy 3D extruded text art effect, all written in pure HTML and templating, leaving the JS heavy-lifting to the framework.

Today's tutorial takes a very different approach: It shows how you can quickly display public "web APIs" (that is, public data sets or web services) with just a few lines of code.

How to use the PokéAPI

This tutorial is 100% thanks to PokéAPI, which is a fun, fan-maintained database and API for retrieval of Pokémon franchise information and media. For example, this link provides info in JSON format for my favorite pink puff: https://pokeapi.co/api/v2/pokemon/jigglypuff

The final result

A screenshot of eight dancing Jigglypuffs on a web page with the title "Jigglypuff Party"


Try it out now, in less than 30 seconds: 🚀🚀🚀 Wanna skip ahead? Scroll to the end and copy the ~30 lines of HTML code into any local HTML file, and then open it in your browser. Modulo is a single-file framework with no dependencies and even runs embedded in local HTML files, so it's really that easy!

Starting with a basic H1 and StaticData

Let's start with something super basic: Let's get info from the API. We can start with <Template> and <StaticData>:

<Template> <h1>{{ staticdata.name|capfirst }} Party!</h1> </Template> <StaticData -src="https://pokeapi.co/api/v2/pokemon/jigglypuff" ></StaticData>

In this snippet, we have a very simple, one-line <Template> that shows the name property of the returned PokéAPI. We apply the |capfirst filter to capitalize it (it is all-lowercase by default). The StaticData supports JSON (among other formats) out of the box, which means all it needs is the URL to the API and it will integrate it into your component. If the StaticData concept is giving you conceptual trouble, try this tutorial on integrating the GitHub API, or the play around with the interactive examples in Part 4 of the Modulo.js tutorial.

Looping through generations and the games

We have to write 2 for loops now: One for the generations, and one for the games.

How did I figure this out? I clicked through the results of the API, and deduced that .sprites, and specifically .sprites.versions has all the "generations" of games in the JSON Object data structure. This is where the data gets tricky, since it's not accessible "at the top level" (like .name). We'll need to "drill down" to the right data.

So, nesting the two for-loops, we get:

{% for generation, games in staticdata.sprites.versions %} {% for game, images in games %}

Showing the front_default image in the loop

Finally, we'll need to reference the front_default image in an <img> tag to display it. See below:

{% for generation, games in staticdata.sprites.versions %} {% for game, images in games %} <img src="{{ images.front_default }}" class="dancing-image" /> {% endfor %} {% endfor %}

The {% for %} loop repeats the HTML code contained within. This is how it "duplicates" the <img src="..."> over and over to show all the images. For more practice on {% for %} loops, play around with the interactive examples in the documentation.

Adding the CSS animation

Finally, to wrap this up, we'll need to make 'em dance! I made a "PokeDance" animation, which rocks back and forth, notably making the transform-origin: bottom center to make it look like their "center of gravity" is at the bottom (at their feet), as such:

.dancing-image { transform-origin: bottom center; animation: PokeDance 3s ease-in-out infinite alternate; width: 200px; } @keyframes PokeDance { 20% { transform: rotate(15deg); } 40% { transform: rotate(-10deg); } 60% { transform: rotate(5deg); } 80% { transform: rotate(-5deg); } 100% { transform: rotate(10deg); } }

<x-PokeParty> - Embeddable snippet

Combining it all, and adding a few final tweaks (the title on the image, and a tweak of adding -auto-isolate:=false to prevent PokeDance animation from being modified), we get the following:

<template Modulo> <Component name="PokeParty"> <Template> <h1>{{ staticdata.name|capfirst }} Party!</h1> {% for generation, games in staticdata.sprites.versions %} {% for game, images in games %} <img class="dancing-image" title="{{ generation }}: {{ game }}" src="{{ images.front_default }}" /> {% endfor %} {% endfor %} </Template> <StaticData -src="https://pokeapi.co/api/v2/pokemon/bellsprout" ></StaticData> <Style -auto-isolate:=false> .dancing-image { transform-origin: bottom center; animation: PokeDance 3s ease-in-out infinite alternate; width: 200px; } @keyframes PokeDance { 20% { transform: rotate(15deg); } 40% { transform: rotate(-10deg); } 60% { transform: rotate(5deg); } 80% { transform: rotate(-5deg); } 100% { transform: rotate(10deg); } } </Style> </Component> </template> <script src="https://unpkg.com/mdu.js"></script> <x-PokeParty></x-PokeParty>