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:
Top comments (5)
👍
🎹🕶️
thats is something i got amazed Mads, since many years i was not aware that css gone that far. a great article with insightful demonstration
Thank you!
wow, this is very intresting, nowadays most of the game development companies in India were using such types of codes while developing games for specific audiences.