This content originally appeared on DEV Community and was authored by Mads Stoumann
A couple of months ago, I wrote an article about Guitar Chords in CSS, using the new typed attr()
method in CSS. I didn’t plan to write a follow-up, but then I saw these beautiful posters and wanted to do something similar. It turned out to be way more complicated than guitar chords!
Markup
The markup consists of a single custom element, with a keys
-attribute:
<piano-chord keys="14">
</piano-chord>
keys
indicates how many white keys we want to show on our piano. For now, that’s all we need; all the heavy lifting is done in CSS.
CSS
The first thing we do in CSS is grab the keys
attribute:
piano-chord {
--_keys: attr(keys type(<number>), 8);
}
Next, we need a private const to indicate how many white keys are in a regular octave:
--_octave-keys: 7;
OK, so now we can already draw the white keys, which will be simple lines at calculated intervals:
background: linear-gradient(90deg,
var(--piano-chord-black-key)
var(--piano-chord-key-gap),
#0000 0 var(--piano-chord-key-gap));
border-block:
var(--piano-chord-key-gap)
solid var(--piano-chord-black-key);
I’ve added a few properties to set the line color and width. We also need to set the background-size
:
background-size: calc(100% / var(--_keys) - (var(--piano-chord-key-gap) / var(--_keys))) 100%;
This gives us:
Next, we need to calculate the octave width, as the five black keys in a regular octave will repeat in the next octave, creating the distinct pattern we know from a piano.
For the next calculations, we need a few more private properties:
Octave width
--_ow: calc(100% / (var(--_keys) / var(--_octave-keys)));
Black key ratio
The black key is always 60% of the width of a white key, but because we change the octave width dynamically, we need another calculation to keep this ratio:
--_r: calc(var(--_keys) / var(--_octave-keys) * 0.6);
White key width:
--_wkw: calc(100% / var(--_keys));
Black key width
--_bkw: calc(var(--_wkw) * var(--_r));
Phew! And we’re not done yet! You might recall that the black key is 60% of a white key and placed exactly in the middle between two white keys. Half of 60% is 30%, so we need to place the first black key (between C and D on a piano) at 70% of the first white key (C):
--_off1: calc(0.7 / var(--_octave-keys) * 100%);
To skip forward: The next ones are placed at 1.7
, 3.7
, 4.7
, and 5.7
. Now, we need to add five extra gradients to our background, one for each black key in an octave.
I’ll just show the code for the first here:
linear-gradient(90deg,
#0000 0 var(--_off1),
var(--piano-chord-black-key) var(--_off1)
calc(var(--_off1) + var(--_bkw)),
#0000 0))
Thankfully, the background-size
is simple this time: var(--_ow) 60%
Let’s see what we’ve got:
Cool! Let’s change keys
to keys="21"
:
Isn’t that cool? A CSS-only, attribute-controlled piano pattern?
Adding chord indicators
Now, let’s add chords. For that, we need a new custom element, <piano-key>
:
<piano-chord aria-label="C" keys="14">
<piano-key note="C"></piano-key>
<piano-key note="E"></piano-key>
<piano-key note="G"></piano-key>
</piano-chord>
That wraps up what we need to do in HTML. Let’s go back to CSS. First, we set a piano key to be a grid:
piano-key { display: grid; }
Next, we need to set at which column location the key is:
piano-key[note="C"] { grid-column: 1; }
piano-key[note="D"] { grid-column: 2; }
/* etc. */
Next, we add a pseudo-element showing a small dot:
piano-key::after {
background: var(--_bg, #000);
border-radius: 50%;
content: "";
display: block;
height: 10px;
margin-block: 8px;
place-self: end center;
width: 10px;
}
This renders:
… and that’s almost it! We need to handle sharp and flat notes:
piano-key[note*="#"] {
translate: 50% -40%;
}
piano-key[note*="b"] {
translate: -50% -40%;
}
And to make them look just as cool as in the posters I mentioned in the beginning, let’s wrap the piano in a wrapper with a dusty blue color and a retro SVG filter (see the final demo for the code!):
And that’s it! There is, of course, much more to it, but I’ve covered most of the basics. Here’s a CodePen with a bunch of chords:
This content originally appeared on DEV Community and was authored by Mads Stoumann