Creating a CSS-based 3D extruded Word Art picker

"HTML-only, no JS!" ✨🪄

Hey all! Today's Modulo.js tutorial will be another "HTML-only, no JS" tutorial. The 2000-line, small-but-mighty Modulo framework adds just enough JS magic that we can build these cool design tools in pure HTML. Have an idea for a next topic? Be sure to let me know in the comments!

In previous tutorials, I covered topics like adding an API or JSON/CSV file in a few lines of HTML, and more recently, I showed how to make word art skew adjusters or a word art shadow throw and range picker tool. Today's tutorial will be similar to these last two, and could even be combined (lemme know if you try this!). However, today we're focused on generating a more advanced 3D effect from a palette of colors using the power of Modulo's {% for %} loop template tag. Before we begin, a warning: Text-shadows are intensive and can slow down pages (especially on mobile), so use this technique cautiously on any public-facing pages!

The final result

Animated GIF

An animated GIF of extruding 3D text letters, in a three striped palette pattern of pastel colors

Screenshot

A 3D text designer showing extruded letters with shading that shows them in 3D, in a three striped palette pattern of pastel colors: Yellow, mint, and lavendar.

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 has no dependencies and even runs embedded in local HTML files, so it's really that easy!


Starting with H1 and a text input

As with previous tutorials, we'll start with <Template> and <State>:

<Template> <label>Text: <input [state.bind] name="text" /></label> <h1 style="text-shadow: 0 0 3px #fff ,1px 1px 0 red ,1px 1px 10px #00000033;">{{ state.text }}</h1> </Template> <State text="Modulo 3D Text Generator!" ></State>

In this snippet, we have a <input> element that's bound to State using [state.bind], followed by an H1 element with a hard-coded (red) text-shadow effect: The 0 0 3px #fff creates a 3PX white glow around the top, the 1px 1px 0 #B90183 creates the "solid"-looking 3D extrusion effect, and finally the 1px 1px 10px #00000033 creates a 10px blurred shadow at about ~20% opacity. If the State concept is giving you conceptual trouble, try this tutorial on a font-picker, or the play around with the interactive examples in Part 2 of the Modulo.js tutorial.

Generating a shadow from State

Our next step is to make the shadow get auto-generated from state. Let's start by hard-coding 6 shadows (3 red, 3 green):

<State text="Modulo 3D Text Generator!" shadows:='[ "red", "red", "red", "green", "green", "green" ]' ></State>

Next, we'll use a {% for %} for loop that uses the index as the shadow distance. This creates the "3D" effect. This replaces the hard-coded "red" values with for-loop generated shadows:

<h1 style=" text-shadow: 0 0 3px #fff {% for index, shadow in state.shadows %} ,{{ index }}px {{ index }}px 0 {{ shadow }} ,{{ index }}px {{ index }}px 10px #00000033 {% endfor %}; "> ...

This generates a total of 13 shadows (1 extra "white glow" shadow, plus each item in the "shadows" array gets 2 shadows each, so 2 * 6 + 1 = 13). The :='[ and ]' syntax is the syntax for creating Arrays, and the {% for %} template-tag is the syntax to duplicate a bit of HTML for every item in the array (not to mention each index, e.g. a number counting up from 0). For further practice, both of these concepts have lots of interactive examples in part 4 of the Modulo.js tutorial.

Adding a palette and shadow add button

Right now, our 3D text generator is sort of working, but it can't be adjusted. Let's make it customizable: First we clear hard-coded values from shadows:= array, and then create a new array for our color palette:

<State text="Modulo 3D Text Generator!" shadows:='[ ]' colors:='[ "#B90183", "#A2E4B8", "#FFE45A" ]' ></State>

Now, in a similar procedure my last tutorial, let's write a for loop that generates an input for each color in our palette, using the {{ index }} variable to "bind" each input to each element in the colors:= Array:

{% for index, color in state.colors %} <div style="background: {{ color }}"> <button @click:=state.shadows.push payload="{{ color }}">+</button> <label>Palette #{{ index }} <input [state.bind] name="colors.{{ index }}" type="color" /></label> </div> {% endfor %}

Here we loop through each color in our color palette, creating "divs" with the background color to preview it, along with buttons that, when clicked, invoke the state.shadows.push function to push the "payload" (in this case, the {{ color }} itself) onto the shadow array. Thus, every time you click the button, another shadow gets added! If the @click event concept is giving you trouble, there are many interactive examples in part 5 of the Modulo.js tutorial.

<x-WordArt3DTool> - Embeddable snippet

Combining it all, and adding a few final CSS tweaks to the H1 (font-size, color, and transform), and we get the following results that can be embedded anywhere:

<!DOCTYPE html> <template Modulo> <Component name="WordArt3DTool"> <Template> <label>Text: <input [state.bind] name="text" /></label> {% for index, color in state.colors %} <div style="background: {{ color }}"> <button @click:=state.shadows.push payload="{{ color }}">+</button> <label>Palette #{{ index }} <input [state.bind] name="colors.{{ index }}" type="color" /></label> </div> {% endfor %} <h1 style=" font-size: 64px; transform: skew(-10deg, -3deg); color: white; text-shadow: 0 0 3px #fff {% for index, shadow in state.shadows %} ,{{ index }}px {{ index }}px 0 {{ shadow }} ,{{ index }}px {{ index }}px 10px #00000033 {% endfor %}; ">{{ state.text }}</h1> </Template> <State text="Modulo 3D Text Generator!" colors:='[ "#B90183", "#A2E4B8", "#FFE45A" ]' shadows:=[] ></State> </Component> </template> <script src="https://unpkg.com/mdu.js"></script> <x-WordArt3DTool></x-WordArt3DTool>