add theme.js — shared design tokens module referenced by App.jsx

This commit is contained in:
2026-06-14 17:29:19 -04:00
parent 0af8ed05b0
commit a5a01fda02
10 changed files with 67 additions and 116 deletions
+31 -32
View File
@@ -125,13 +125,12 @@ function getBlockDisplayName(block) {
const css = `
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&family=Space+Grotesk:wght@300;400;500;600&display=swap');
* { box-sizing: border-box; margin: 0; padding: 0; -webkit-tap-highlight-color: transparent; user-select: none; }
html, body { height: 100%; overflow: hidden; background: ${T.bg}; }
body { color: ${T.textPrimary}; font-family: 'Space Grotesk', sans-serif; }
:root { --amber: ${T.amber}; --blue: ${T.blue}; --green: ${T.green}; }
html, body { height: 100%; overflow: hidden; background: var(--bg); }
body { color: var(--text-primary); font-family: 'Space Grotesk', sans-serif; }
.mono { font-family: 'JetBrains Mono', monospace; }
::-webkit-scrollbar { width: 4px; height: 4px; }
::-webkit-scrollbar-track { background: ${T.panel}; }
::-webkit-scrollbar-thumb { background: ${T.border}; border-radius: 2px; }
::-webkit-scrollbar-track { background: var(--panel); }
::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
/* ── Knob ── */
.knob-wrap { display: flex; flex-direction: column; align-items: center; gap: 4px; cursor: pointer; }
@@ -141,21 +140,21 @@ const css = `
box-shadow: 0 2px 8px rgba(0,0,0,.6), inset 0 1px 0 rgba(255,255,255,.07); }
.knob-dot { position: absolute; width: 3px; border-radius: 2px; background: var(--amber);
left: 50%; transform-origin: bottom center; box-shadow: 0 0 6px var(--amber); }
.knob-label { font-size: 9px; color: ${T.textSec}; letter-spacing: .06em; text-transform: uppercase; text-align: center; }
.knob-value { font-family: 'JetBrains Mono', monospace; font-size: 10px; color: ${T.amber}; }
.knob-label { font-size: 9px; color: var(--text-sec); letter-spacing: .06em; text-transform: uppercase; text-align: center; }
.knob-value { font-family: 'JetBrains Mono', monospace; font-size: 10px; color: var(--amber); }
/* ── Buttons ── */
.btn { padding: 8px 14px; border-radius: 6px; font-size: 12px; font-weight: 600;
letter-spacing: .04em; border: none; cursor: pointer; transition: all .12s; }
.btn-primary { background: ${T.amber}; color: #000; }
.btn-primary { background: var(--amber); color: #000; }
.btn-primary:active { filter: brightness(.85); transform: scale(.97); }
.btn-ghost { background: ${T.surface}; color: ${T.textPrimary}; border: 1px solid ${T.border}; }
.btn-ghost:active { background: ${T.border}; }
.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;
justify-content: center; background: ${T.surface}; border: 1px solid ${T.border}; cursor: pointer;
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); }
.btn-icon.active { background: rgba(232,160,48,.15); border-color: ${T.amber}; color: ${T.amber}; }
.btn-icon.active { background: rgba(232,160,48,.15); border-color: var(--amber); color: var(--amber); }
.btn:focus-visible { outline: 2px solid #3AB87A; outline-offset: 2px; }
.btn-icon:focus-visible { outline: 2px solid #3AB87A; outline-offset: 2px; }
@@ -163,43 +162,43 @@ const css = `
.badge { display: inline-flex; align-items: center; justify-content: center;
padding: 2px 7px; border-radius: 4px; font-size: 10px; font-weight: 700;
letter-spacing: .06em; text-transform: uppercase; }
.badge-amber { background: rgba(232,160,48,.2); color: ${T.amber}; }
.badge-green { background: rgba(58,184,122,.2); color: ${T.green}; }
.badge-blue { background: rgba(74,139,200,.2); color: ${T.blue}; }
.badge-amber { background: rgba(232,160,48,.2); color: var(--amber); }
.badge-green { background: rgba(58,184,122,.2); color: var(--green); }
.badge-blue { background: rgba(74,139,200,.2); color: var(--blue); }
/* ── Param slider ── */
.param-panel { background: ${T.surface}; padding: 8px 12px 6px; border-top: 1px solid ${T.border}; flex-shrink: 0; }
.param-panel { background: var(--surface); padding: 8px 12px 6px; border-top: 1px solid var(--border); flex-shrink: 0; }
.param-panel-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 2px; }
.param-panel-title { font-size: 10px; font-weight: 600; letter-spacing: .07em; text-transform: uppercase; color: ${T.textSec}; }
.param-panel-title { font-size: 10px; font-weight: 600; letter-spacing: .07em; text-transform: uppercase; color: var(--text-sec); }
.param-row { display: flex; flex-direction: column; gap: 1px; padding: 1px 0; }
.param-header { display: flex; justify-content: space-between; align-items: center; }
.param-label { font-size: 9px; font-weight: 500; text-transform: uppercase; letter-spacing: .06em; color: ${T.textSec}; }
.param-label { font-size: 9px; font-weight: 500; text-transform: uppercase; letter-spacing: .06em; color: var(--text-sec); }
.param-val { font-family: 'JetBrains Mono',monospace; font-size: 11px; font-weight: 600; }
.param-track-wrap { position: relative; height: 30px; display: flex; align-items: center; cursor: pointer; touch-action: none; }
.param-track { width: 100%; height: 6px; background: ${T.border}; border-radius: 3px; position: relative; overflow: hidden; }
.param-track { width: 100%; height: 6px; background: var(--border); border-radius: 3px; position: relative; overflow: hidden; }
.param-fill { height: 100%; border-radius: 3px; position: absolute; top: 0; left: 0; transition: width .04s; }
.param-thumb { width: 22px; height: 22px; border-radius: 50%; position: absolute; top: 50%;
transform: translate(-50%,-50%); box-shadow: 0 1px 6px rgba(0,0,0,.6); pointer-events: none;
border: 3px solid ${T.bg}; }
.param-btn { width: 28px; height: 28px; border-radius: 50%; border: 1px solid ${T.border};
background: ${T.panel}; color: ${T.textPrimary}; font-size: 14px; font-weight: 700;
border: 3px solid var(--bg); }
.param-btn { width: 28px; height: 28px; border-radius: 50%; border: 1px solid var(--border);
background: var(--panel); color: var(--text-primary); font-size: 14px; font-weight: 700;
display: flex; align-items: center; justify-content: center; cursor: pointer; line-height: 1; }
.param-btn:active { background: ${T.border}; transform: scale(.92); }
.param-btn:active { background: var(--border); transform: scale(.92); }
.param-knobs { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; padding: 4px 0; }
/* ── Screen header (overlays) ── */
.screen-header { padding: 10px 14px 8px; display: flex; align-items: center;
justify-content: space-between; border-bottom: 1px solid ${T.border}; }
.screen-title { font-size: 12px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: ${T.textSec}; }
justify-content: space-between; border-bottom: 1px solid var(--border); }
.screen-title { font-size: 12px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--text-sec); }
.screen-body { flex: 1; overflow-y: auto; padding: 10px 12px; display: flex; flex-direction: column; gap: 10px; }
/* ── Preset sidebar chip ── */
.preset-chip-s { width: 100%; padding: 7px 6px; border-radius: 5px; background: ${T.surface}; border: 1px solid ${T.border};
.preset-chip-s { width: 100%; padding: 7px 6px; border-radius: 5px; background: var(--surface); border: 1px solid var(--border);
display: flex; flex-direction: column; align-items: center; cursor: pointer; transition: all .12s; }
.preset-chip-s.active { border-color: ${T.amber}; background: rgba(232,160,48,.08); }
.preset-chip-s.active { border-color: var(--amber); background: rgba(232,160,48,.08); }
.preset-chip-s:active { transform: scale(.96); }
.preset-chip-s .pcs-num { font-family: 'JetBrains Mono',monospace; font-size: 14px; font-weight: 700; color: ${T.amber}; line-height: 1.1; }
.preset-chip-s .pcs-name { font-size: 9px; color: ${T.textSec}; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; text-align: center; line-height: 1.2; }
.preset-chip-s .pcs-num { font-family: 'JetBrains Mono',monospace; font-size: 14px; font-weight: 700; color: var(--amber); line-height: 1.1; }
.preset-chip-s .pcs-name { font-size: 9px; color: var(--text-sec); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; text-align: center; line-height: 1.2; }
.preset-chip-s .pcs-indicator { width: 4px; height: 4px; border-radius: 50%; margin-top: 3px; }
/* ── Preset row (captures) ── */
@@ -207,9 +206,9 @@ const css = `
border-radius: 6px; cursor: pointer; transition: background .12s; border: 1px solid transparent; }
.preset-row:active, .preset-row.active { background: rgba(232,160,48,.08); border-color: rgba(232,160,48,.25); }
.section-label { font-size: 10px; letter-spacing: .1em; text-transform: uppercase;
color: ${T.textDim}; font-weight: 600; margin-bottom: 8px; }
.card { background: ${T.panel}; border: 1px solid ${T.border}; border-radius: 8px; padding: 14px; }
.card-sm { background: ${T.surface}; border: 1px solid ${T.border}; border-radius: 6px; padding: 10px; }
color: var(--text-dim); font-weight: 600; margin-bottom: 8px; }
.card { background: var(--panel); border: 1px solid var(--border); border-radius: 8px; padding: 14px; }
.card-sm { background: var(--surface); border: 1px solid var(--border); border-radius: 6px; padding: 10px; }
/* ── Responsive: narrow screens ── */
@media (max-width: 640px) {
+1 -17
View File
@@ -1,21 +1,5 @@
import { useState, useRef, useCallback, useMemo } from "react";
// ── Design tokens ──────────────────────────────────
const T = {
bg: "#0A0A0C",
panel: "#141418",
surface: "#1C1C22",
border: "#2A2A32",
amber: "#E8A030",
amberDim: "#7A5218",
blue: "#4A8BC8",
blueDim: "#1E4060",
green: "#3AB87A",
red: "#E06060",
textPrimary: "#F0EDE6",
textSec: "#A0A0BC",
textDim: "#7878A0",
};
import { T } from "./theme.js";
// ── Split type metadata ────────────────────────────
const SPLIT_TYPES = {
+1 -17
View File
@@ -1,21 +1,5 @@
import { useState, useRef, useCallback, useMemo } from "react";
// ── Design tokens ──
const T = {
bg: "#0A0A0C",
panel: "#141418",
surface: "#1C1C22",
border: "#2A2A32",
amber: "#E8A030",
amberDim: "#7A5218",
blue: "#4A8BC8",
blueDim: "#1E4060",
green: "#3AB87A",
red: "#E06060",
textPrimary: "#F0EDE6",
textSec: "#A0A0BC",
textDim: "#7878A0",
};
import { T } from "./theme.js";
// ── Block param definitions (mirrored from App.jsx) ──
const BLOCK_PARAMS = {
+1 -6
View File
@@ -1,10 +1,5 @@
import { useRef, useEffect } from "react";
const T = {
bg: "#0A0A0C", panel: "#141418", surface: "#1C1C22", border: "#2A2A32",
amber: "#E8A030", amberDim: "#7A5218", blue: "#4A8BC8", blueDim: "#1E4060",
green: "#3AB87A", red: "#E06060", textPrimary: "#F0EDE6", textSec: "#A0A0BC", textDim: "#7878A0",
};
import { T } from "./theme.js";
const TYPE_COLORS = {
overdrive: T.amber, drive: T.amber, distortion: T.red, fuzz: "#D060E0",
+1 -6
View File
@@ -1,10 +1,5 @@
import { useState, useRef, useCallback, useEffect } from "react";
const T = {
bg: "#0A0A0C", panel: "#141418", surface: "#1C1C22", border: "#2A2A32",
amber: "#E8A030", amberDim: "#7A5218", blue: "#4A8BC8", blueDim: "#1E4060",
green: "#3AB87A", red: "#E06060", textPrimary: "#F0EDE6", textSec: "#A0A0BC", textDim: "#7878A0",
};
import { T } from "./theme.js";
// ── EQ Band definitions ──
const EQ_BANDS = [
+2 -7
View File
@@ -1,10 +1,5 @@
import { useState, useCallback, useRef, useEffect, useMemo } from "react";
const T = {
bg: "#0A0A0C", panel: "#141418", surface: "#1C1C22", border: "#2A2A32",
amber: "#E8A030", amberDim: "#7A5218", blue: "#4A8BC8", blueDim: "#1E4060",
green: "#3AB87A", red: "#E06060", textPrimary: "#F0EDE6", textSec: "#A0A0BC", textDim: "#7878A0",
};
import { useState, useRef, useEffect, useCallback, useMemo } from "react";
import { T } from "./theme.js";
async function api(method, path, body) {
const opts = { method, headers: { "Content-Type": "application/json" } };
+1 -6
View File
@@ -1,10 +1,5 @@
import { useRef, useEffect } from "react";
const T = {
bg: "#0A0A0C", panel: "#141418", surface: "#1C1C22", border: "#2A2A32",
amber: "#E8A030", amberDim: "#7A5218", blue: "#4A8BC8", blueDim: "#1E4060",
green: "#3AB87A", red: "#E06060", textPrimary: "#F0EDE6", textSec: "#A0A0BC", textDim: "#7878A0",
};
import { T } from "./theme.js";
const MENU_ITEMS = [
{ id: "save", label: "Save Preset", icon: "💾", desc: "Save current preset to bank" },
+1 -17
View File
@@ -1,21 +1,5 @@
import { useState, useEffect, useMemo, useCallback, useRef } from "react";
// ── Design tokens ──
const T = {
bg: "#0A0A0C",
panel: "#141418",
surface: "#1C1C22",
border: "#2A2A32",
amber: "#E8A030",
amberDim: "#7A5218",
blue: "#4A8BC8",
blueDim: "#1E4060",
green: "#3AB87A",
red: "#E06060",
textPrimary: "#F0EDE6",
textSec: "#A0A0BC",
textDim: "#7878A0",
};
import { T } from "./theme.js";
const IMG_PATH = (import.meta.env.BASE_URL || '/ui/') + 'img/';
+1 -8
View File
@@ -1,12 +1,5 @@
import { useState, useEffect, useRef, useCallback } from "react";
// ── Design tokens (mirrored) ──────────────────────────────────
const T = {
bg: "#0A0A0C", panel: "#141418", surface: "#1C1C22", border: "#2A2A32",
amber: "#E8A030", amberDim: "#7A5218", blue: "#4A8BC8",
green: "#3AB87A", red: "#E06060", textPrimary: "#F0EDE6",
textSec: "#A0A0BC", textDim: "#7878A0",
};
import { T } from "./theme.js";
// ── String labels matching _STRING_NAMES ──────────────────────
const STRING_LABELS = [
+27
View File
@@ -0,0 +1,27 @@
/**
* theme.js — Shared design tokens
*
* Single source of truth: CSS custom properties in index.css :root {}
* This module reads them at load time so JS components use the same
* values as the stylesheet. To theme the app, override the CSS vars.
*/
function v(name) {
return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
}
export const T = {
bg: v('--bg'),
panel: v('--panel'),
surface: v('--surface'),
border: v('--border'),
amber: v('--amber'),
amberDim: v('--amber-dim'),
blue: v('--blue'),
blueDim: v('--blue-dim'),
green: v('--green'),
red: v('--red'),
textPrimary: v('--text-primary'),
textSec: v('--text-sec'),
textDim: v('--text-dim'),
};