fix: btn-icon touch targets to 44px WCGA minimum

- CSS classes: index.css (28→44px base, 24→44px mobile), App.jsx (36→44px base, 24→44px narrow)
- 19 inline btn-icon buttons in App.jsx: param view toggle, CapturesScreen/Presets close, sidebar toggle, undo/redo, status bar nav (captures/presets/tuner/fullscreen/menu), overlay closes, global bypass toggle, block bypass toggle, Scan/ON buttons, add block close
- Footswitch mode SM/PR buttons: height 20→44px, minWidth 34→44px
- FocusView.jsx: param −/+ buttons 34→44px, close button 34→44px
- GlobalSettings.jsx: param −/+ buttons 26→44px, close button 34→44px
- border-radius bumped from 7→8px (base), 5→8px (index.css) for proportion
This commit is contained in:
2026-06-14 18:37:58 -04:00
parent a5a01fda02
commit 3dcf31f600
4 changed files with 38 additions and 36 deletions
+26 -26
View File
@@ -150,7 +150,7 @@ const css = `
.btn-primary:active { filter: brightness(.85); transform: scale(.97); }
.btn-ghost { background: var(--surface); color: var(--text-primary); border: 1px solid var(--border); }
.btn-ghost:active { background: var(--border); }
.btn-icon { width: 36px; height: 36px; border-radius: 7px; display: flex; align-items: center;
.btn-icon { width: 44px; height: 44px; border-radius: 8px; display: flex; align-items: center;
justify-content: center; background: var(--surface); border: 1px solid var(--border); cursor: pointer;
font-size: 15px; transition: all .12s; flex-shrink: 0; }
.btn-icon:active { transform: scale(.93); }
@@ -222,7 +222,7 @@ const css = `
.screen-title { font-size: 11px; }
.screen-body { padding: 8px 10px; gap: 8px; }
.knob-wrap { transform: scale(.9); }
.btn-icon { width: 24px; height: 24px; font-size: 10px; }
.btn-icon { width: 44px; height: 44px; font-size: 12px; }
}
/* ── Responsive: landscape short ── */
@@ -377,7 +377,7 @@ function ParameterPanel({ block,params,onParamChange,onParamChangeEnd,viewMode,o
<div style={{display:'flex',gap:4,alignItems:'center'}}>
{onFocus&&<button className="btn" style={{padding:'4px 8px',fontSize:9,fontWeight:600,background:`${T.amber}22`,border:`1px solid ${T.amber}44`,color:T.amber,borderRadius:5,letterSpacing:'.04em',cursor:'pointer'}}
onClick={onFocus}>🔍 Focus</button>}
<button className="btn-icon" style={{width:26,height:26,fontSize:11,flexShrink:0}}
<button className="btn-icon" style={{width:44,height:44,fontSize:14,flexShrink:0}}
onClick={onToggleView}>{viewMode==='slider'?'◉':'▦'}</button>
</div>
</div>
@@ -422,7 +422,7 @@ function CapturesScreen({onClose}){
const doSearch=async()=>{if(!query.trim())return;setLoading(true);try{const data=tab==="models"?await API.searchModels(query):await API.searchIrs(query);setResults(data.results||[]);}catch{setResults([]);}setLoading(false);};
const install=async(item)=>{try{if(tab==="models"){await API.installModel(item.download_url,item.name);}else{await API.installIr(item.download_url,item.name);}API.listModels().then(d=>setModels(d.models||[])).catch(()=>{});}catch(e){alert("Install failed: "+e.message);}};
return(<div style={{display:"flex",flexDirection:"column",height:"100%",background:T.bg}}>
<div className="screen-header"><div className="screen-title">Downloads</div><button className="btn-icon" style={{width:36,height:36,fontSize:13}}onClick={onClose}></button></div>
<div className="screen-header"><div className="screen-title">Downloads</div><button className="btn-icon" style={{width:44,height:44,fontSize:16}}onClick={onClose}></button></div>
<div style={{padding:"6px 12px",borderBottom:`1px solid ${T.border}`}}><div className="toggle">
<button className={`toggle-btn ${tab==="models"?"active":""}`}onClick={()=>setTab("models")}>NAM Models</button>
<button className={`toggle-btn ${tab==="irs"?"active":""}`}onClick={()=>setTab("irs")}>IR Cabs</button></div></div>
@@ -831,7 +831,7 @@ export default function App(){
padding:isLandscapeShort?"2px 10px":"4px 12px",background:T.panel,borderBottom:`1px solid ${T.border}`,flexShrink:0,minHeight:isLandscapeShort?26:32}}>
<div style={{display:"flex",gap:8,alignItems:"center"}}>
{/* Sidebar toggle (narrow only) */}
{isNarrow&&<button className="btn-icon" style={{width:24,height:24,fontSize:13,position:'relative',marginRight:-2}}
{isNarrow&&<button className="btn-icon" style={{width:44,height:44,fontSize:16,position:'relative',marginRight:-2}}
onClick={()=>setSidebarOpen(s=>!s)} title="Presets">
<span style={{fontSize:12,lineHeight:1}}></span>
</button>}
@@ -839,11 +839,11 @@ export default function App(){
background:state.connected?T.green:T.red,boxShadow:`0 0 6px ${state.connected?T.green:T.red}`}}/>
{!isNarrow&&<span className="mono" style={{fontSize:9,color:state.connected?T.green:T.red}}>{state.connected?"CONNECTED":"OFFLINE"}</span>}
{/* Undo/Redo */}
<button className="btn-icon" style={{width:20,height:20,fontSize:11,opacity:undoStack.length>0?1:0.3}}
<button className="btn-icon" style={{width:44,height:44,fontSize:14,opacity:undoStack.length>0?1:0.3}}
onClick={handleUndo} disabled={undoStack.length===0} title="Undo (Ctrl+Z)">
</button>
<button className="btn-icon" style={{width:20,height:20,fontSize:11,opacity:redoStack.length>0?1:0.3}}
<button className="btn-icon" style={{width:44,height:44,fontSize:14,opacity:redoStack.length>0?1:0.3}}
onClick={handleRedo} disabled={redoStack.length===0} title="Redo (Ctrl+Shift+Z)">
</button>
@@ -859,34 +859,34 @@ export default function App(){
<span className="mono" style={{fontSize:9,color:T.textSec}}>CPU <span style={{color:T.amber,fontWeight:700}}>{state.cpu_percent!=null?`${state.cpu_percent}%`:"—"}</span></span>
</div>}
<div style={{width:1,height:12,background:T.border}}/>
<button className="btn-icon" style={{width:24,height:24,position:'relative'}}onClick={()=>setView("captures")}title="Downloads">
<button className="btn-icon" style={{width:44,height:44,position:"relative"}}onClick={()=>setView("captures")} title="Downloads">
<img src={`${import.meta.env.BASE_URL}img/ic_bank.svg`} alt="captures" style={{width:14,height:14}}/>
</button>
<button className="btn-icon" style={{width:24,height:24,position:'relative'}}onClick={()=>setView("presets")}title="Presets">
<button className="btn-icon" style={{width:44,height:44,position:"relative"}}onClick={()=>setView("presets")}title="Presets">
<img src={`${import.meta.env.BASE_URL}img/ic_presets.svg`} alt="presets" style={{width:14,height:14}}/>
</button>
<div style={{display:"flex",gap:2,alignItems:"center",background:T.surface,borderRadius:4,border:`1px solid ${T.border}`,padding:2,height:24}}>
{["stomp","preset"].map(m=>(<button key={m} onClick={()=>setFootswitchMode(m)} style={{
padding:"1px 5px",borderRadius:3,border:"none",height:20,
padding:"1px 5px",borderRadius:3,border:"none",height:44,
fontSize:8,fontWeight:700,letterSpacing:".04em",textTransform:"uppercase",
background:footswitchMode===m?T.amber:"transparent",
color:footswitchMode===m?"#000":T.textDim,
cursor:"pointer",lineHeight:1.5,minWidth:34,display:"flex",alignItems:"center",justifyContent:"center",
cursor:"pointer",lineHeight:1.5,minWidth:44,display:"flex",alignItems:"center",justifyContent:"center",
}}>{m==="stomp"?"SM":"PR"}</button>))}
</div>
<button className="btn-icon" style={{width:24,height:24,position:'relative',
background:state.tuner_enabled?'rgba(128,208,160,.15)':T.surface,
borderColor:state.tuner_enabled?T.green:T.border}}
onClick={handleTunerToggle} title={state.tuner_enabled?"Exit Tuner":"Tuner"}>
<button className="btn-icon" style={{width:44,height:44,position:"relative",
background:state.tuner_enabled?"rgba(128,208,160,.15)":T.surface,
borderColor:state.tuner_enabled?T.green:T.border}}
onClick={handleTunerToggle} title={state.tuner_enabled?"Exit Tuner":"Tuner"}>
<img src={`${import.meta.env.BASE_URL}img/fx_analyzer.svg`} alt="tuner" style={{width:14,height:14,filter:state.tuner_enabled?'brightness(0) saturate(100%) invert(0.9) sepia(0.4) hue-rotate(80deg)':'none'}}/>
</button>
<button className="btn-icon" style={{width:24,height:24,position:'relative',background:'rgba(232,160,48,.12)',borderColor:T.amber}}onClick={()=>{if(!document.fullscreenElement){document.documentElement.requestFullscreen()}else{document.exitFullscreen()}}}title="Fullscreen">
<button className="btn-icon" style={{width:44,height:44,position:'relative',background:'rgba(232,160,48,.12)',borderColor:T.amber}}onClick={()=>{if(!document.fullscreenElement){document.documentElement.requestFullscreen()}else{document.exitFullscreen()}}}title="Fullscreen">
<svg viewBox="0 0 24 24" width="14" height="14" fill="#E8A030">
<path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"/>
</svg>
</button>
<div style={{width:1,height:16,background:T.border}}/>
<button className="btn-icon" style={{width:24,height:24,position:'relative'}}
<button className="btn-icon" style={{width:44,height:44,position:"relative"}}
onClick={()=>setMenuOpen(true)} title="Main Menu">
<span style={{fontSize:14,color:T.amber,lineHeight:1}}></span>
</button>
@@ -1027,7 +1027,7 @@ export default function App(){
{view==="captures"&&<CapturesScreen onClose={()=>setView("main")}/>}
{view==="presets"&&(
<div style={{display:"flex",flexDirection:"column",height:"100%",background:T.bg}}>
<div className="screen-header"><div className="screen-title">Presets</div><button className="btn-icon"style={{width:36,height:36,fontSize:13}}onClick={()=>setView("main")}></button></div>
<div className="screen-header"><div className="screen-title">Presets</div><button className="btn-icon"style={{width:44,height:44,fontSize:16}}onClick={()=>setView("main")}></button></div>
<div className="screen-body">
{presets.map((p,i)=>(
<div key={i} className={`preset-row ${p.num===currentPreset?"active":""}`}
@@ -1102,7 +1102,7 @@ export default function App(){
<div className="card" style={{width:320,maxWidth:"85vw",padding:20}}>
<div style={{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:16}}>
<div style={{fontSize:14,fontWeight:600,color:T.textPrimary}}>💾 Save Preset</div>
<button className="btn-icon" style={{width:26,height:26,fontSize:12}}onClick={()=>setOverlayView(null)}></button>
<button className="btn-icon" style={{width:44,height:44,fontSize:14}}onClick={()=>setOverlayView(null)}></button>
</div>
<div style={{display:"flex",flexDirection:"column",gap:12}}>
<div>
@@ -1133,12 +1133,12 @@ export default function App(){
<div className="card" style={{width:340,maxWidth:"85vw",padding:20}}>
<div style={{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:16}}>
<div style={{fontSize:14,fontWeight:600,color:T.textPrimary}}>🔌 Bypass / Control</div>
<button className="btn-icon" style={{width:26,height:26,fontSize:12}}onClick={()=>setOverlayView(null)}></button>
<button className="btn-icon" style={{width:44,height:44,fontSize:14}}onClick={()=>setOverlayView(null)}></button>
</div>
<div style={{display:"flex",flexDirection:"column",gap:14}}>
<div style={{display:"flex",justifyContent:"space-between",alignItems:"center"}}>
<span style={{fontSize:12,color:T.textPrimary}}>🚫 Global Bypass</span>
<button className="btn-icon" style={{width:40,height:26,fontSize:10,background:globalBypass?`${T.red}22`:T.surface,borderColor:globalBypass?T.red:T.border}}
<button className="btn-icon" style={{width:44,height:44,fontSize:10,background:globalBypass?`${T.red}22`:T.surface,borderColor:globalBypass?T.red:T.border}}
onClick={()=>{setGlobalBypass(p=>!p);API.bypassToggle().catch(()=>{});}}>
{globalBypass?"ON":"OFF"}
</button>
@@ -1149,7 +1149,7 @@ export default function App(){
{blocks.map(b=>(
<div key={b.id} style={{display:"flex",justifyContent:"space-between",alignItems:"center",padding:"4px 0"}}>
<span style={{fontSize:11,color:T.textPrimary}}>{b.name||b.type}</span>
<button className="btn-icon" style={{width:36,height:22,fontSize:9,background:b.bypassed?`${T.red}22`:T.surface,borderColor:b.bypassed?T.red:T.border}}
<button className="btn-icon" style={{width:44,height:44,fontSize:11,background:b.bypassed?`${T.red}22`:T.surface,borderColor:b.bypassed?T.red:T.border}}
onClick={()=>handleToggleBlock(b.id)}>
{b.bypassed?"BYP":"ACT"}
</button>
@@ -1180,12 +1180,12 @@ export default function App(){
<div className="card" style={{width:340,maxWidth:"85vw",padding:20}}>
<div style={{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:16}}>
<div style={{fontSize:14,fontWeight:600,color:T.textPrimary}}>📶 Wireless</div>
<button className="btn-icon" style={{width:26,height:26,fontSize:12}}onClick={()=>setOverlayView(null)}></button>
<button className="btn-icon" style={{width:44,height:44,fontSize:14}}onClick={()=>setOverlayView(null)}></button>
</div>
<div style={{display:"flex",flexDirection:"column",gap:14}}>
<div style={{display:"flex",justifyContent:"space-between",alignItems:"center"}}>
<span style={{fontSize:12,color:T.textPrimary}}>📶 WiFi</span>
<button className="btn-icon" style={{width:40,height:26,fontSize:10}}>Scan</button>
<button className="btn-icon" style={{width:44,height:44,fontSize:11}}>Scan</button>
</div>
<div className="card-sm" style={{textAlign:"center",padding:16}}>
<span style={{fontSize:11,color:T.textSec}}>Use pedal as access point or connect to existing network via the settings panel.</span>
@@ -1193,7 +1193,7 @@ export default function App(){
<div style={{height:1,background:T.border}}/>
<div style={{display:"flex",justifyContent:"space-between",alignItems:"center"}}>
<span style={{fontSize:12,color:T.textPrimary}}>🔵 Bluetooth</span>
<button className="btn-icon" style={{width:40,height:26,fontSize:10,background:`${T.blue}22`,borderColor:T.blue,color:T.blue}}>ON</button>
<button className="btn-icon" style={{width:44,height:44,fontSize:11,background:`${T.blue}22`,borderColor:T.blue,color:T.blue}}>ON</button>
</div>
<div style={{fontSize:10,color:T.textSec}}>Bluetooth MIDI is enabled. Pair with your mobile device or MIDI controller.</div>
</div>
@@ -1208,7 +1208,7 @@ export default function App(){
<div className="card" style={{width:320,maxWidth:"85vw",padding:20,maxHeight:"80vh",overflowY:"auto"}}>
<div style={{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:12}}>
<div style={{fontSize:13,fontWeight:600,color:T.textPrimary}}>+ Add Block</div>
<button className="btn-icon" style={{width:24,height:24,fontSize:11}}onClick={()=>setAddBlockType(null)}></button>
<button className="btn-icon" style={{width:44,height:44,fontSize:14}}onClick={()=>setAddBlockType(null)}></button>
</div>
<div style={{display:"grid",gridTemplateColumns:"1fr 1fr",gap:6}}>
{Object.entries(BLOCK_DISPLAY_NAMES).filter(([k])=>!["loop","split","merge"].includes(k)).map(([type,name])=>(
+3 -3
View File
@@ -164,7 +164,7 @@ function FocusSlider({ label, value = 50, onChange, onChangeEnd, min = 0, max =
<span style={{ fontSize: 11, fontWeight: 500, textTransform: 'uppercase', letterSpacing: '.06em', color }}>{label}</span>
<div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
<button style={{
width: 34, height: 34, borderRadius: '50%', border: `1px solid ${T.border}`,
width: 44, height: 44, borderRadius: '50%', border: `1px solid ${T.border}`,
background: T.panel, color: T.textPrimary, fontSize: 16, fontWeight: 700,
display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer',
lineHeight: 1,
@@ -174,7 +174,7 @@ function FocusSlider({ label, value = 50, onChange, onChangeEnd, min = 0, max =
minWidth: 40, textAlign: 'center',
}}>{touchVal ?? value}</span>
<button style={{
width: 34, height: 34, borderRadius: '50%', border: `1px solid ${T.border}`,
width: 44, height: 44, borderRadius: '50%', border: `1px solid ${T.border}`,
background: T.panel, color: T.textPrimary, fontSize: 16, fontWeight: 700,
display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer',
lineHeight: 1,
@@ -336,7 +336,7 @@ export default function FocusView({
<div style={{ display: 'flex', alignItems: 'center', gap: 10, minWidth: 0 }}>
<button onClick={onClose}
style={{
width: 34, height: 34, borderRadius: 8,
width: 44, height: 44, borderRadius: 8,
display: 'flex', alignItems: 'center', justifyContent: 'center',
background: T.surface, border: `1px solid ${T.border}`,
color: T.textPrimary, fontSize: 16, cursor: 'pointer',
+3 -3
View File
@@ -169,7 +169,7 @@ function SettingsSlider({ label, value, min, max, unit, onChange }) {
<span style={{ fontSize: 11, fontWeight: 500, color: T.textSec }}>{label}</span>
<div style={{ display: "flex", gap: 6, alignItems: "center" }}>
<button style={{
width: 26, height: 26, borderRadius: "50%", border: `1px solid ${T.border}`,
width: 44, height: 44, borderRadius: "50%", border: `1px solid ${T.border}`,
background: T.panel, color: T.textPrimary, fontSize: 14, fontWeight: 700,
display: "flex", alignItems: "center", justifyContent: "center", cursor: "pointer", lineHeight: 1,
}} onMouseDown={quick(-1)} onTouchStart={quick(-1)}></button>
@@ -178,7 +178,7 @@ function SettingsSlider({ label, value, min, max, unit, onChange }) {
minWidth: 36, textAlign: "center",
}}>{value}{unit}</span>
<button style={{
width: 26, height: 26, borderRadius: "50%", border: `1px solid ${T.border}`,
width: 44, height: 44, borderRadius: "50%", border: `1px solid ${T.border}`,
background: T.panel, color: T.textPrimary, fontSize: 14, fontWeight: 700,
display: "flex", alignItems: "center", justifyContent: "center", cursor: "pointer", lineHeight: 1,
}} onMouseDown={quick(1)} onTouchStart={quick(1)}>+</button>
@@ -392,7 +392,7 @@ export default function GlobalSettings({ onClose, onMasterVolume, onAudioProfile
<div style={{ position: "fixed", inset: 0, zIndex: 998, display: "flex", flexDirection: "column", background: T.bg, color: T.textPrimary, fontFamily: "'Space Grotesk', sans-serif" }}>
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "10px 14px", borderBottom: `1px solid ${T.border}`, background: T.panel, flexShrink: 0 }}>
<div style={{ display: "flex", alignItems: "center", gap: 10 }}>
<button onClick={onClose} style={{ width: 34, height: 34, borderRadius: 8, display: "flex", alignItems: "center", justifyContent: "center", background: T.surface, border: `1px solid ${T.border}`, color: T.textPrimary, fontSize: 16, cursor: "pointer" }}></button>
<button onClick={onClose} style={{ width: 44, height: 44, borderRadius: 8, display: "flex", alignItems: "center", justifyContent: "center", background: T.surface, border: `1px solid ${T.border}`, color: T.textPrimary, fontSize: 16, cursor: "pointer" }}></button>
<div>
<div style={{ fontSize: 15, fontWeight: 600 }}>Settings</div>
{!loaded && <span style={{ fontSize: 10, color: T.textDim }}>Loading</span>}
+6 -4
View File
@@ -42,9 +42,11 @@ body { background: #0A0A0C; color: #F0EDE6; font-family: 'Space Grotesk', sans-s
.btn-ghost { background: transparent; border-color: transparent; }
.btn-danger { border-color: #E06060; background: rgba(224,96,96,.15); color: #E06060; }
.btn-icon { display: inline-flex; align-items: center; justify-content: center;
width: 28px; height: 28px; border-radius: 5px; border: 1px solid #2A2A32;
width: 44px; height: 44px; border-radius: 8px; border: 1px solid #2A2A32;
background: #1C1C22; cursor: pointer; transition: all .12s; touch-action: manipulation; }
.btn-icon:active { transform: scale(.92); }
.btn:focus-visible { outline: 2px solid #3AB87A; outline-offset: 2px; }
.btn-icon:focus-visible { outline: 2px solid #3AB87A; outline-offset: 2px; }
/* Slide panel (bottom drawer) */
.slide-panel { position: fixed; inset: 0; background: rgba(0,0,0,.7); z-index: 100;
@@ -186,9 +188,9 @@ body { background: #0A0A0C; color: #F0EDE6; font-family: 'Space Grotesk', sans-s
font-size: 11px;
}
.btn-icon {
width: 24px;
height: 24px;
font-size: 10px;
width: 44px;
height: 44px;
font-size: 12px;
}
.card {
padding: 10px;