Files
404-Layers/src/ui/Panel.module.css
ncode 6889167fcf Fix lit pads not highlighting; stop swallowing haptics errors
A lit pad kept its grey text and only its border turned orange, so pads
looked unhighlighted next to every other key. Cause was cascade order,
not logic: .pad is a single-class selector like .lit, so equal
specificity meant whichever came last won, and .pad was declared after.
The state classes now sit at the bottom of the file with a comment
saying why, and a lit pad computes rgb(255,74,0) for text and border.

native.ts reported nothing when haptics failed because I wrote
catch(() => {}) and discarded the reason. It now reports once per
distinct failure - enough to read in logcat, quiet enough not to spam on
every key press.

That reporting shows the software path is fine: isNativePlatform true,
Haptics available and bridged, VIBRATE granted, and both impact and
vibrate resolve without rejecting. So the silence is below the bridge -
most likely the phone's own touch-vibration setting - and not something
this code can detect.

141 tests, typecheck clean, installed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 21:05:01 +03:00

304 lines
5.6 KiB
CSS

/* Measured geometry: 8 + 36 + 8 + 124 + 10 + 134 + 10 + 187 + 8 = 525.
*
* Ordering matters here: the state classes (.lit, .sel, .held, .down) live at
* the BOTTOM of this file. They are single-class selectors, same specificity as
* .pad or .fx, so whichever comes last wins. With .pad declared after .lit, a
* lit pad kept its grey text and only the border turned orange — pads looked
* unhighlighted next to every other key. */
.panel {
height: var(--panel-h);
padding: 8px;
background: var(--body);
border: var(--outline);
box-shadow: var(--shadow);
display: flex;
flex-direction: column;
/* Without this the browser claims a second finger as a pinch or a pan, and a
chord can never be formed. This is what makes multitouch work at all. */
touch-action: none;
user-select: none;
-webkit-user-select: none;
-webkit-touch-callout: none;
}
/* ---- shared key look ---- */
.key,
.pad,
.side,
.fx,
.knob,
.valueKnob {
position: relative;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
border: var(--outline);
background: var(--key);
color: var(--ink);
/* 7px with no tracking: a left-block key is 35.9px wide and only 27.9px of
that is usable, so 8px made SAMPLE sit flush against the border. */
font-size: 7px;
line-height: 1.05;
letter-spacing: 0;
padding: 2px 1px;
border-radius: 3px;
hyphens: manual;
/* Lit keys show the entry's short label, which comes from the data and can be
one long unbreakable word — SHIFT + LOOP is PINGPONG. Soft hyphens only
cover the printed labels, so allow a break anywhere and clip what is left,
rather than letting it spill outside the key. */
overflow-wrap: anywhere;
overflow: hidden;
}
.badge {
position: absolute;
top: 1px;
right: 2px;
font-family: var(--mono);
font-size: 6px;
opacity: 0.8;
}
/* ---- knobs ---- */
.knobs {
height: 36px;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 4px;
}
.knob {
width: 34px;
height: 34px;
flex: 0 0 auto;
border-radius: 50%;
background: var(--pad);
color: var(--pad-ink);
}
/* ---- fx and the screen ---- */
.screenRow {
height: 124px;
margin-top: 8px;
display: grid;
grid-template-columns: 62px minmax(0, 1fr) 62px;
gap: 8px;
}
.fxCol {
display: flex;
flex-direction: column;
gap: 5px;
}
.fx {
flex: 1;
background: var(--pad);
color: var(--accent);
font-weight: 700;
font-size: 7.5px;
border-radius: 10px;
}
.screenWrap {
position: relative;
display: flex;
align-items: center;
justify-content: center;
}
.circle {
position: absolute;
left: 50%;
top: 0;
width: 124px;
height: 124px;
margin-left: -62px;
border-radius: 50%;
background: var(--pad);
border: var(--outline);
}
.lcd {
position: relative;
width: 168px;
height: 84px;
background: var(--lcd);
border: var(--outline);
color: #111111;
padding: 4px 6px;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.lcdRow {
display: flex;
justify-content: space-between;
align-items: center;
gap: 3px;
font-family: var(--mono);
font-size: 6.5px;
letter-spacing: 0.06em;
}
.lcdMain {
font-size: 17px;
font-weight: 700;
line-height: 1;
text-align: center;
}
.chip {
background: #111111;
color: var(--lcd);
padding: 1px 4px;
}
/* ---- control block ---- */
.ctl {
height: 134px;
margin-top: 10px;
display: flex;
flex-direction: column;
gap: 7px;
}
.group {
height: 40px;
display: grid;
grid-template-columns: minmax(0, 3fr) minmax(0, 5fr);
gap: 10px;
}
.col {
display: flex;
flex-direction: column;
}
.caption {
height: 8px;
display: flex;
align-items: center;
gap: 4px;
font-family: var(--mono);
font-size: 6px;
letter-spacing: 0.11em;
color: var(--muted);
white-space: nowrap;
}
.rule {
flex: 1;
height: 1px;
background: var(--rule);
}
.btnRow {
height: 32px;
display: flex;
gap: 5px;
}
.key {
flex: 1;
min-width: 0;
}
/* SHIFT is the longest label in the six-button bank row, as on the hardware. */
.btnRow .key:last-child:nth-child(6) {
flex: 1.25;
}
.valueKnob {
width: 30px;
flex: 0 0 auto;
border-radius: 50%;
background: var(--pad);
color: var(--pad-ink);
font-size: 6.5px;
}
/* ---- pads ---- */
.pads {
height: 187px;
margin-top: 10px;
display: flex;
flex-direction: column;
gap: 5px;
}
.padRow {
flex: 1;
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr)) 0.9fr;
gap: 5px;
}
.pad,
.side {
background: var(--pad);
color: var(--pad-ink);
font-size: 9px;
border-radius: 4px;
}
.side {
color: var(--accent);
font-weight: 700;
}
/* The pad's number, as printed on the hardware. */
.num {
font-family: var(--mono);
font-size: 16px;
font-weight: 700;
color: var(--muted);
}
/* ---- state: LAST in the file, so it wins over .pad, .fx, .side and .knob ---- */
/* Lit is an outline, not a fill. The SHIFT layer lights 35 of 54 controls, and
35 solid orange keys is a wall; solid is reserved for the selected one. */
.lit {
border-color: var(--accent);
color: var(--accent);
font-weight: 700;
}
.sel {
background: var(--accent);
border-color: var(--accent);
color: var(--on-accent);
font-weight: 700;
}
.held {
background: var(--ink);
color: var(--page);
font-weight: 700;
}
/* A round key cannot hold a lit function's label at full size: SHIFT + VALUE is
MARK FN, which spilled outside the circle. Shrink it when lit. */
.knob.lit,
.knob.sel,
.valueKnob.lit,
.valueKnob.sel {
font-size: 6px;
}
/* Pressed, whether or not it is lit. */
.down {
transform: scale(0.95);
}