:root { color-scheme: dark; font-family: "Inter Variable", Inter, system-ui, sans-serif; font-size: 13px; /* cool greys, against which the warm accents stand out; lines neutral */ --bg: oklch(19.9% 0.014 285.1); --panel: oklch(23.1% 0.017 285); --panel-2: oklch(27% 0.022 284.8); --border: oklch(34.7% 0 0); /* for the few lines that divide more than the rest */ --border-strong: oklch(46% 0 0); /* around the track being edited */ --border-active: oklch(64% 0 0); --text: oklch(95.4% 0.008 332.1); --muted: oklch(63.9% 0.023 333.1); --accent: oklch(82.4% 0.159 330.6); --accent-mute: oklch(63.8% 0.13 331.4); /* the accent turned halfway round the hue wheel, for melodies */ --accent-melody: oklch(82.4% 0.142 150.6); /* the accent turned towards purple, and a touch more vivid: chord notes outside the key */ --accent-outside: oklch(82.4% 0.16 295.6); /* the other keys on the key's axis: a minor third up, down, and a tritone away */ --accent-sharp: oklch(82.4% 0.127 40.6); --accent-flat: oklch(82.4% 0.127 210.6); --accent-inverse: oklch(82.4% 0.127 270.6); /* the parallel modes: quieter than the axis, and running from the brightest mode (light and warm) to the darkest (dim and cool), with the minor scales' variants off to the side in red */ --mode-lydian: oklch(84% 0.075 95); --mode-ionian: oklch(80% 0.075 125); --mode-mixolydian: oklch(76% 0.075 155); --mode-dorian: oklch(72% 0.075 185); --mode-aeolian: oklch(68% 0.075 215); --mode-phrygian: oklch(64% 0.075 245); --mode-locrian: oklch(60% 0.075 275); --mode-harmonicMinor: oklch(70% 0.085 20); --mode-melodicMinor: oklch(75% 0.085 45); --row-off: oklch(20.8% 0.014 285.1); --row-scale: oklch(24.4% 0.019 284.9); --row-tonic: oklch(27.3% 0.036 301); --line-row: oklch(0% 0 0 / 0.165); --line-bar: oklch(100% 0 0 / 0.2); --line-beat: oklch(100% 0 0 / 0.09); --c: var(--muted); } /* in the key: nothing to point out */ .on-home { --c: var(--text); } /* from none of the key, its axis, or its parallel modes */ .chromatic { --c: var(--accent); } .on-sharp { --c: var(--accent-sharp); } .on-flat { --c: var(--accent-flat); } .on-inverse { --c: var(--accent-inverse); } .from-lydian { --c: var(--mode-lydian); } .from-ionian { --c: var(--mode-ionian); } .from-mixolydian { --c: var(--mode-mixolydian); } .from-dorian { --c: var(--mode-dorian); } .from-aeolian { --c: var(--mode-aeolian); } .from-phrygian { --c: var(--mode-phrygian); } .from-locrian { --c: var(--mode-locrian); } .from-harmonicMinor { --c: var(--mode-harmonicMinor); } .from-melodicMinor { --c: var(--mode-melodicMinor); } * { box-sizing: border-box; } [hidden] { display: none !important; } body { margin: 0; background: var(--bg); color: var(--text); } button, input, select { font: inherit; color: inherit; background: var(--panel-2); border: 1px solid var(--border); padding: 3px 8px; } button { cursor: pointer; } button:hover:not(:disabled) { border-color: var(--accent-mute); } button:disabled { opacity: 0.4; cursor: default; } input[type="checkbox"] { padding: 0; accent-color: var(--accent); } kbd, code { font-family: inherit; font-size: 11px; background: var(--panel-2); border: 1px solid var(--border); padding: 0 4px; } .app { display: grid; grid-template-rows: auto 1fr auto auto auto; height: 100vh; } /* the project on the left, the transport in the very middle, settings on the right */ .toolbar { display: grid; grid-template-columns: 1fr auto 1fr; gap: 8px 20px; align-items: center; padding: 8px 12px; background: var(--panel); border-bottom: 1px solid var(--border); .tb-section { display: flex; flex-wrap: wrap; gap: 8px 20px; align-items: center; } .tb-right { justify-content: end; } .tb-group { display: flex; gap: 6px; align-items: center; } label { display: flex; gap: 6px; align-items: center; color: var(--muted); } .tb-projects { max-width: 160px; } .tb-bpm { width: 58px; } .tb-play { min-width: 70px; border-color: var(--accent); } } /* no room for three columns: let it wrap */ @media (max-width: 1080px) { .toolbar { display: flex; flex-wrap: wrap; } } .timeline { overflow: auto; user-select: none; -webkit-user-select: none; background: var(--bg); } .tl-content { position: relative; min-height: 100%; } .tl-rows { display: grid; } .tl-corner, .ruler { position: sticky; top: 0; height: 22px; background-color: var(--panel); border-bottom: 1px solid var(--border); z-index: 3; } .tl-corner { left: 0; z-index: 4; border-right: 1px solid var(--border); } .ruler { cursor: text; .loop-region { position: absolute; top: 0; bottom: 0; background: color-mix(in oklab, var(--accent) 22%, transparent); border-inline: 2px solid var(--accent); cursor: default; } .loop-region.off { background: color-mix(in oklab, var(--muted) 12%, transparent); border-color: var(--muted); } /* straddling the region's edges, to grab them by */ .loop-edge { position: absolute; top: 0; bottom: 0; width: 9px; cursor: ew-resize; } .loop-edge.start { left: -6px; } .loop-edge.end { right: -6px; } span { position: absolute; top: 4px; font-size: 11px; color: var(--muted); pointer-events: none; } } .track-head { position: sticky; left: 0; scroll-margin-top: 22px; z-index: 2; background: var(--panel); border-right: 1px solid var(--border); border-bottom: 1px solid var(--border); padding: 6px 40px 6px 8px; /* dragged by, to move the track */ cursor: grab; &.active { background: var(--panel-2); } &.active .th-name { font-weight: 650; } .th-top { display: flex; gap: 4px; } .th-name { flex: 1; min-width: 0; background: transparent; border-color: transparent; padding: 2px 4px; } .th-name:hover, .th-name:focus { border-color: var(--border); } .th-btn { padding: 0 6px; font-size: 11px; } .th-btn.on { background: var(--accent); color: var(--bg); } .th-bass { display: flex; align-items: center; gap: 6px; padding: 4px 4px 0; color: var(--muted); font-size: 11px; input { margin: 0; } } .th-gain { display: flex; align-items: center; gap: 6px; padding: 4px 4px 0; color: var(--muted); font-size: 11px; font-variant-numeric: tabular-nums; input { flex: 1; min-width: 0; margin: 0; accent-color: var(--muted); } span { width: 44px; text-align: right; } } } .keys { position: absolute; right: 0; bottom: 0; width: 34px; overflow: hidden; cursor: ns-resize; .keys-inner { display: flex; flex-direction: column; will-change: translate; } .key { flex: 0 0 10px; background: oklch(88.5% 0.014 286.1); border-bottom: 1px solid oklch(0% 0 0 / 0.2); font-size: 8px; line-height: 9px; color: oklch(32.1% 0 0); text-align: right; padding-right: 2px; } .key.black { background: oklch(28.9% 0.018 285.2); margin-right: 12px; } .key.tonic { box-shadow: inset 3px 0 var(--accent); } } .lane { position: relative; border-bottom: 1px solid var(--border); } /* over the borders it shares with the tracks either side, rather than beside them */ :is(.track-head, .lane).active::after { content: ""; position: absolute; inset: -1px 0; z-index: 1; border-block: 1px solid var(--border-active); pointer-events: none; } .track-resize { position: absolute; left: 0; right: 0; bottom: 0; height: 5px; cursor: row-resize; &:hover { background: color-mix(in oklab, var(--accent) 50%, transparent); } } /* keep the resize cursor while dragging over other things */ body.resizing-track, body.resizing-track * { cursor: row-resize !important; } body.resizing-loop, body.resizing-loop * { cursor: ew-resize !important; } body.panning, body.panning *, body.reordering, body.reordering * { cursor: grabbing !important; } .reorder-line { position: fixed; z-index: 10; height: 2px; background: var(--accent); pointer-events: none; } .roll { position: relative; overflow: hidden; cursor: crosshair; } .roll-inner { position: relative; /* moved through by transform, so its own layer: moving it then needn't paint it again */ will-change: translate; } .lane.notes { --note: var(--accent-melody); } .note { position: absolute; overflow: hidden; padding-left: 2px; font-size: 9px; font-weight: 600; line-height: 9px; white-space: nowrap; color: oklch(0% 0 0 / 0.7); background: var(--note, var(--accent)); box-shadow: inset 0 0 0 1px oklch(0% 0 0 / 0.267); &.selected { background: oklch(100% 0 0); } &.outside { --note: var(--accent-outside); } &.auto { background: color-mix(in oklab, var(--note, var(--accent)) 45%, var(--bg)); color: oklch(100% 0 0 / 0.75); } /* written but not played: a bass left to another track */ &.silent { background: none; box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--note, var(--accent)) 70%, transparent); color: var(--muted); } &.ghost { background: color-mix(in oklab, var(--accent-melody) 30%, transparent); box-shadow: none; pointer-events: none; } } .roll > .note:not(.auto) { cursor: grab; } .chord-col { position: absolute; top: 0; bottom: 0; /* chords take turns being shaded rather than getting lines: they always start on a grid line, and a line of their own would only brighten it */ &.alt { background: oklch(100% 0 0 / 0.02); } &.current { background: oklch(100% 0 0 / 0.039); } } .strip { position: relative; background: var(--panel); border-bottom: 1px solid var(--border); cursor: text; } .chord { position: absolute; top: 4px; bottom: 4px; padding: 3px 6px; overflow: hidden; white-space: nowrap; cursor: grab; background: color-mix(in oklab, var(--c) 16%, var(--panel-2)); border: 1px solid color-mix(in oklab, var(--c) 40%, transparent); border-left: 2px solid var(--c); &.current { outline: 2px solid var(--text); outline-offset: -1px; } &.selected { background: color-mix(in oklab, var(--c) 34%, var(--panel-2)); border-color: var(--c); } &.unknown { --c: oklch(51% 0 0); } &.pinned::after { content: ""; position: absolute; top: 4px; right: 8px; width: 5px; height: 5px; border-radius: 50%; background: var(--text); } .chord-name { font-weight: 600; font-size: 14px; } .chord-roman { font-size: 11px; color: var(--muted); } .chord-tag { margin-left: 6px; color: var(--c); } .chord-handle { position: absolute; top: 0; right: 0; bottom: 0; width: 6px; cursor: ew-resize; } } .tl-line { position: absolute; top: 0; bottom: 0; left: 0; width: 2px; margin-left: -1px; pointer-events: none; z-index: 1; } .tl-cursor { background: color-mix(in oklab, var(--accent) 60%, transparent); } .tl-playhead { background: var(--text); } .tl-add { position: sticky; left: 0; display: flex; gap: 6px; padding: 8px; z-index: 2; } .chord-editor { position: absolute; z-index: 5; display: flex; gap: 6px; align-items: center; padding: 6px 4px; pointer-events: none; input { pointer-events: auto; width: 110px; font-weight: 600; font-size: 14px; border-color: var(--accent); background: var(--bg); } input.invalid { border-color: oklch(68.8% 0.201 24.1); color: oklch(79% 0.121 20.1); } .chord-hint { color: var(--muted); background: var(--bg); padding: 2px 6px; } .chord-hint:empty { display: none; } } .palette { background: var(--panel); border-top: 1px solid var(--border); padding: 6px 12px 8px; &[data-open="false"] { padding-bottom: 6px; header { margin-bottom: 0; } .palette-body, .help, label { display: none; } } max-height: 34vh; overflow: auto; header { display: flex; gap: 16px; align-items: center; color: var(--muted); margin-bottom: 6px; label { display: flex; gap: 4px; align-items: center; } } } .palette-body { display: flex; gap: 32px; align-items: start; } .circle { flex: none; width: 260px; height: 260px; .circle-chord { cursor: pointer; path { fill: transparent; stroke: color-mix(in oklab, var(--border) 70%, transparent); } &:hover path { fill: var(--panel-2); } &.here path { fill: color-mix(in oklab, var(--c) 18%, var(--panel)); stroke: var(--c); } text { text-anchor: middle; pointer-events: none; } .name { fill: var(--c); font-size: 12px; font-weight: 600; } .roman { fill: var(--muted); font-size: 8.5px; } /* less room on the inner ring */ &.minor .name { font-size: 10.5px; } } } .palette-row { display: grid; grid-template-columns: 110px repeat(7, 104px); border-top: 1px solid color-mix(in oklab, var(--border) 60%, transparent); /* the key and its axis, then the modes */ &.on-inverse + .palette-row { border-top-color: var(--border-strong); } .palette-mode { display: grid; align-content: center; color: var(--c); small { color: var(--muted); font-size: 10px; } } &.on-home .palette-mode { font-weight: 650; } } .chip { display: flex; gap: 6px; align-items: baseline; padding: 4px 6px; background: none; border: none; b { color: var(--c); font-weight: 600; } span { color: var(--muted); font-size: 11px; } &:hover { background: var(--panel-2); } &.same { opacity: 0.35; } } .hints { display: flex; gap: 16px; padding: 6px 12px; color: var(--muted); font-size: 11px; border-top: 1px solid var(--border); background: var(--panel); .hint kbd + kbd { margin-left: 2px; } } .dialog { background: var(--panel); color: var(--text); border: 1px solid var(--border); padding: 16px; width: min(560px, 90vw); &::backdrop { background: oklch(0% 0 0 / 0.5); } h2 { margin: 0 0 12px; font-size: 15px; } } .dialog-actions { display: flex; justify-content: flex-end; gap: 6px; margin-top: 12px; } .tt-dialog { .tt-options { display: grid; gap: 6px; max-height: 60vh; overflow: auto; } .tt-option { display: grid; grid-template-columns: 1fr auto; gap: 2px 12px; text-align: left; padding: 8px 10px; } .tt-option:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; } .tt-detail { color: var(--muted); } .tt-preview { grid-column: 1 / -1; color: var(--muted); font-size: 11px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .tt-preview:empty { display: none; } } .midi-dialog { .midi-parts { display: grid; grid-template-columns: auto 1fr auto; gap: 6px 12px; align-items: baseline; max-height: 60vh; overflow: auto; } .midi-part { display: contents; } .midi-detail { color: var(--muted); } } .disclosure { background: none; border: none; padding: 0; font-size: 11px; white-space: nowrap; /* a chevron, as on git.t4t.associates */ &::before { content: ""; display: inline-block; position: relative; top: -0.17em; width: 0.4em; height: 0.4em; margin-right: 0.6em; border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor; transform: rotate(45deg); } &[aria-expanded="false"]::before { top: -0.05em; transform: rotate(-45deg); } } .help { display: inline-grid; place-items: center; width: 14px; height: 14px; border: 1px solid currentColor; border-radius: 50%; color: var(--muted); font-size: 9px; font-weight: 650; cursor: help; &:hover { color: var(--accent); } } .disclosure:hover:not(:disabled) { color: var(--accent); } .chord-panel { display: flex; gap: 16px; align-items: center; padding: 6px 12px; background: var(--panel); border-top: 1px solid var(--border); &[data-open="true"] .chord-panel-body { min-height: 42px; } .chord-panel-body { display: flex; flex-wrap: wrap; gap: 8px 20px; /* the buttons in a line, under their labels */ align-items: end; } .chord-panel-empty { align-self: center; color: var(--muted); } .chord-panel-name { display: grid; min-width: 72px; &:empty { display: none; } b { font-size: 16px; } span { color: var(--muted); font-size: 11px; } } .chord-panel-group { display: grid; gap: 3px; } .chord-panel-label { color: var(--muted); font-size: 11px; } .chord-panel-buttons { display: flex; gap: 4px; } .chord-panel-action { display: flex; gap: 6px; align-items: baseline; padding: 3px 8px; b { font-weight: 650; } } } .select-box { position: fixed; z-index: 10; pointer-events: none; border: 1px solid var(--accent-melody); background: color-mix(in oklab, var(--accent-melody) 15%, transparent); } .menu { /* undo the centring popovers get by default, so it can sit under its button */ inset: auto; margin: 0; padding: 4px; min-width: 170px; color: var(--text); background: var(--panel-2); border: 1px solid var(--border); box-shadow: 0 8px 24px oklch(0% 0 0 / 0.4); button { display: block; width: 100%; text-align: left; background: none; border: none; padding: 5px 10px; } button:hover { background: color-mix(in oklab, var(--accent) 18%, transparent); } hr { border: none; border-top: 1px solid var(--border); margin: 4px 2px; } }