Skip to content
homeworkplayaboutcontact
resume
homeworkplayaboutcontact

This is a footer (ew stinky...)
work about contact flowerboxes linkedin x
josue:~$ Josué Avalos Jiménez · Designer · Prev. @ Nintendo · Open to work © 2026 · built from scratch · press

This is a footer (ew stinky...)
work about contact flowerboxes
@1kind.px @josueava.los linkedin x
Josué Avalos Jiménez · Designer · Prev. @ Nintendo · Open to work © 2026 · built from scratch
a bevelled chrome window titled panelware, with an LCD time readout, a progress bar, Apply, Cancel, and Repeat buttons, and two status badges

Panelware

Project Open source component kit

Year2026

Team Josué Avalos Jiménez

Type
ProductDesign SystemsPrototypeCase Study
skip to the running player live demo github npm

Overview

Panelware is an accessible React component kit for retro interfaces. Bevelled chrome, LCD segment readouts, a Web 2.0 gloss, and a 1998 bitmap skin, all on top of Radix UI.

It is on npm as @josueavalosjim/panelware. I designed and built it alone in September 2026, and it reached 0.9.1 in 162 commits.

14

looks from one set of markup. Four skins and three presets, each in light and dark


01 · Two halves of a trade

Retro kits are decorative. They put a convincing surface over a div with a click handler, and the keyboard, the screen reader, and the contrast ratio are somebody else's problem.

Accessible primitive libraries are the other half. The interaction is correct, and the surface is a grey rectangle you are expected to design yourself.

Panelware joins them. Radix handles the interaction and focus, since doing that from scratch is years of work somebody has already done twice, and Panelware draws everything you see.


02 · A music player made of it

Every control in this player is a Panelware component. Press play. The music is synthesised in the browser, and the equaliser changes what you hear.

The buttons under the playlist switch skin and theme. They set the same two attributes on <html> that anyone using the kit would set.

the Panelware demo player, running
The demo player, running here. Switch skins while it plays. · open full screen

03 · One class per element

A component is styled by the class it already has. <button class="pw-button"> is the whole of it, and a primary button adds data-variant="primary".

There are no treatment classes to memorise, so restyling is a token change and not a find and replace.

The stylesheet also works with no React at all. The package ships a page that renders every component in every state an attribute can reach, with no JavaScript.

Every state, CSS only. Open it to scroll the whole sheet. · open full screen

That page and the React demo render the same DOM, and a check compares their geometry. A static page that positioned things more simply than the real component would be testing a shape that never ships.


04 · A skin moves more than colour

the same window in four skins: chrome with a glossy blue title bar, cyber in teal monospace capitals, paper in dark brown and cream serif, and dialup in navy and silver
Identical markup in all four. Only the attributes on the root changed.

A skin that changes all of its colour and none of its rhythm reads as the same design twice. So each one also changes type, padding, gaps, timing, corner geometry, and what carries elevation. Paper has no shadows. Its depth is a dithered screen.

Skin, theme, and density are three separate attributes, and they work on any element. A dark toolbar inside a light page is one data-theme away.

No skin moves the 44px touch target. The compact density goes to 32px, and that is a choice the consumer makes, not the skin.

The radio is the one round thing in the kit. The shape tells you a control is choose-one before you have read a word, so the convention stays even where it breaks the look.


05 · The equaliser is ten sliders

the player in the dialup skin: a green LCD reading GLASS PAD, a spectrum visualiser, transport buttons, and an equaliser with a preamp and ten vertical band sliders from 60 hertz to 16 kilohertz

The obvious build is one slider with ten thumbs. Radix has one. Give it three thumbs at 20, 60, and 40, drive the middle one up, and it returns 20, 40, and 100.

It re-sorted. A multi-thumb slider is a range, and its thumbs are ordered. The thumb you are holding now sits at a different index, and the focused element reports the value of a band nobody touched.

For a price filter that is correct. For an equaliser it is a bug with no fix, because 3kHz is always 3kHz and dragging it up must never turn it into 6kHz.

So the bands are separate sliders that share a scale, a zero line, and a frame. Each keeps its own name and value.

That has two costs, and the docs name both. Ten bands are ten tab stops. And on touch, a vertical slider takes vertical drags, which is also how a page scrolls, so the equaliser stays narrow with page to scroll on either side.


06 · Contrast is checked in CI

The palette does not claim to be accessible. taste-check, a tool I wrote, reads every token file and measures every pairing on every build. The repo also keeps a fixture whose only job is to fail, so a silent gate gets noticed.

3:1

the floor for a bevel's frame, because the frame is what says where a control ends

The white highlight inside a bevel is left out of the check. It measures 1.47:1 on a silver face and marks no edge by itself, and pushing it to 3:1 would turn it grey.

That exception is written down next to the token it applies to, and every ratio in the token comments comes from the same parser the build runs.


Try it

live demo github npm

SOJO StatBar

all work