Tactile
August 9, 2026

Recreating the joy of physical buttons
Sometimes you find a design that strikes a chord of wonder and nostalgia. As we race to put glossy, glassy touch screens on everything around us, there is a rising wave of desire for actual engineered mechanisms. Touch, it turns out, must also include a physicality in order for us, as humans, to be satisfied. Buttons, toggles, switches, knobs, levers, handles, pulls — all are designed to connect with our most basic and arguably most important tool, our hands.
Car manufacturers are reverting from touch interfaces to go back to physical buttons. People are building and customizing mechanical keyboards. Even the kids are feeling the nostalgia and the bite of monthly streaming subscription fees and going back to dedicated MP3 players with physical buttons.
One notable company who has been designing and building stylish products even before this latest nostalgia wave, is Teenage Engineering. I first came across them when they released the gorgeous TP-7 audio recorder, but alas, the price was…let’s say significant. You can see the physicality and intention in all their products. They want to be touched. You want to hold them in your hands and explore the surfaces and interactions.
When they launched the K.O. II sampler, I was immediately drawn in by all the buttons and knobs. The whole thing just draws you in with color and texture. It’s somehow a 1980s vintage calculator masquerading as an audio sampler. Even the gray base is reminiscent of the beige computers I grew up with. I was so enamored that, in an act of skeuomorphism, I decided to try to recreate the buttons for the web. As a side note, there is a fine line between too much mimicry and just enough. My goal was to try to capture that fine balance as designed by Teenage Engineering — a beautiful, tactile experience.
Design & Development
In dissecting the photo of the sampler, a few key details stand out. Namely, the shadows and the borders are really doing most of the work to create the different button states. In fact, since the we’re trying to mimic physical buttons, there is no :hover state at all. There are only a default and an :active state. For the active button state, the borders get darker and the shadow moves from outside to inside. To get the lighting right for the borders, the top and left borders use the same color to set a single light source hitting the buttons from the top left. The same, albeit reversed, is true for the bottom and right borders — they share the same color. The majority of the colors are set via CSS variables, but you can also see some colors using hard coded RGB values. This is just a case of me tweaking the exact shade and never going back to create a new variable. Working at the speed of lazy.
I also considered using a gradient for the border to have a more seamless edge around the corners. Since gradients are not supported for the border CSS property, it would mean using a extra container with a gradient fill and then a mask or clip-path to cut out the middle for the button surface. It’s certainly possible, but seemed like a heck of a lot of extra work (and code) for just a smoother corner color.
Part of what makes the button press depth work is a visual trick. The square background behind the rounded button serves as the “seat” of the button. It’s the space the physical button sits. Think about your keyboard and you’ll get the idea. The default or “raised” state has the button sitting higher via the outer shadow. The active or “pressed” state has the button sitting lower via the inset shadow. It gives the impression of a size change, but it’s all accomplished via the shadows and the darker container.
The HTML is quite simple as I wanted to make the buttons actually use the <button> element. These are meant for actions, not links.
The toggles are almost identical in the design. Toggles are just sticky buttons really. Instead of an active state, it’s just an “on” state. The big difference is that I’m using the checkbox hack in the HTML to create the effect. Using an <input type="checkbox"> with the appearance set to none allows us to style it like the other buttons. Instead of the button’s :active pseudo selector, the inputs use the :checkbox selector so we can target the “on” state and style it.
I’m not sure I got the colors and shadow depth quite right, but it does give the correct effect. You can feel the button press and it is quite satisfying.
The text for the buttons in mostly inline as part of the <button> element. The small text is set inline via a data-* attribute and then appended to the button by a :before pseudo class which is all very cool.
/* buttons with secondary text/symbols added */
<button class="btn shift" id="btnC" data-copy-shift="↑">C</button>
.btn.shift::before {
content: attr(data-copy-shift) / "Shift icon";
}
/* toggles with small text alone */
<input class="toggle org" id="btnR" data-copy="Record" type="checkbox">
.toggle::before {
content: attr(data-copy) / "Record";
}
I’m not sure if this data attribute is the most accessible thing to do as it kind of triggers the “don’t hide content” part of my brain, so be sure to do a more research if you want to use it in production. Speaking of testing, it’s worth noting that there will be code differences between the Codepen examples and the actual project page. Different systems, different goals and hence, different approaches. For example, on the toggles I’ve approached the text copy in two different ways. In the Codepen, I used the CSS content property to add the text with a :before pseudo-selector. On the project page, I’ve used data-* attributes to add the text copy and then added some extra HTML for accessibility. I’ve been enamored with data attributes at work, so that’s probably why I used them. Of course, I then doubted myself and went back into the Codepen to add in the <span> and aria-labelledby for the toggles which may be overkill (or just straight up broken).
I didn’t recreate the entire sampler as I was really focused on the buttons, but I did drop in some of the little lights which are fun. They’re activated with Javascript on the buttons to toggle a “on” CSS class changing the color and shadow. There are also some half height buttons, but I never got quite happy with the result. I think the border-radius on those corners needs to be smaller.
I’m also enamored with the unit’s speaker grid as it reminds me of all the (now retro) devices from my teenage years. Maybe I’ll recreate it for a future project as it seems like it could be a nice reusable pattern.
In the meantime, let’s go back to physical buttons and where we can, let’s make our web buttons cool too. Go ahead and fork the Codepen and start making some true button buttons for the web. Heck, even the bevel edge web 1.0 88×31 buttons are back in fashion (and might show up here as well some day).