v1.1.4: light-theme support (LED wells, panel buttons, readouts)
The controls whose chrome was hardcoded dark now adapt to a light base —
previously they rendered as dark chips/holes on light themes. (Flat knobs and
faders already adapted: their face is the `face` token.)
- `ledWell` theme token — the recessed screen behind LED segments (LEDKnob,
LEDFader, Meter) and the default well of SegmentDisplay / AlphaDisplay now
comes from the theme. Dark base keeps #0b0d0e; light base is `transparent`,
so LED controls and readouts render as clean flat rings / bars / digits on
the light panel. faceColor / background props still override.
- `scheme` theme token ('dark' | 'light', set by base) + a shared
panelButtonChrome() helper drive the raised gradient/border/shadow of
PushButton (and Button), TransportButton and SegmentSwitch — light raised
buttons on light themes instead of dark chips; unlit LED pips flip to a
faint dark so they still read.
- Docs: a "Light theme" gallery card showing the panel family under
base="light"; ledWell/scheme documented; install URLs bumped to v1.1.4.
Library + docs typecheck and build; 46 tests pass.
This commit is contained in:
parent
d183d7d16b
commit
8b462dfb83
17 changed files with 180 additions and 44 deletions
|
|
@ -51,7 +51,7 @@ See `packages/dreamknob/README.md` and the docs app for the full API.
|
|||
The easiest way is the published release on our Forgejo:
|
||||
|
||||
```bash
|
||||
pnpm add https://git.dreamodus.software/Dreamodus/DreamKnob/releases/download/v1.1.3/dreamknob-1.1.3.tgz
|
||||
pnpm add https://git.dreamodus.software/Dreamodus/DreamKnob/releases/download/v1.1.4/dreamknob-1.1.4.tgz
|
||||
```
|
||||
|
||||
Or build locally — the package builds to `packages/dreamknob/dist` (ESM + CJS + types):
|
||||
|
|
|
|||
|
|
@ -195,6 +195,7 @@ export const ApiDocs: React.FC = () => (
|
|||
ledGreen: '#3df2ad', // SegmentDisplay default lit color
|
||||
ledAmber: '#ffb84d', // AlphaDisplay default lit color
|
||||
panel: 'rgba(255,255,255,0.03)', // Rack / panel chrome
|
||||
ledWell: '#0b0d0e', // LED display well (LEDKnob/LEDFader/Meter)
|
||||
}}
|
||||
>
|
||||
<FlatKnob ... /> <Fader ... /> <LEDKnob ... />
|
||||
|
|
|
|||
|
|
@ -352,6 +352,42 @@ const PanDemo: React.FC = () => (
|
|||
</div>
|
||||
);
|
||||
|
||||
const LightThemeDemo: React.FC = () => {
|
||||
const [play, setPlay] = React.useState(false);
|
||||
const [mute, setMute] = React.useState(true);
|
||||
return (
|
||||
<DreamknobProvider base="light" theme={{ accent: '#0868c8' }}>
|
||||
<div
|
||||
style={{
|
||||
background: '#eceef2',
|
||||
borderRadius: 12,
|
||||
padding: 18,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 16,
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', gap: 18, alignItems: 'center' }}>
|
||||
<LEDKnob size={74} min={20} max={2000} defaultValue={440} taper={logTaper} label="Freq" unit="Hz" aria-label="Light freq" />
|
||||
<SegmentDisplay value={128.4} digits={4} decimals={1} height={22} />
|
||||
<Meter value={[62, 44]} min={0} max={100} length={92} breadth={14} aria-label="Light meter" />
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 10, alignItems: 'center' }}>
|
||||
<PushButton pressed={mute} onChange={setMute} color="#d92546" size={32} aria-label="Mute">
|
||||
Mute
|
||||
</PushButton>
|
||||
<Button color="#0868c8" size={32} onClick={() => undefined} aria-label="Apply">
|
||||
Apply
|
||||
</Button>
|
||||
<TransportButton kind="play" active={play} onClick={() => setPlay(p => !p)} size={32} />
|
||||
<SegmentSwitch options={['LP', 'BP', 'HP']} defaultValue={1} aria-label="Light filter" />
|
||||
</div>
|
||||
</div>
|
||||
</DreamknobProvider>
|
||||
);
|
||||
};
|
||||
|
||||
const ImageKnobDemo: React.FC = () => {
|
||||
const src = useGeneratedStrip();
|
||||
if (!src) return null;
|
||||
|
|
@ -565,6 +601,10 @@ export const Gallery: React.FC = () => (
|
|||
</div>
|
||||
</Card>
|
||||
|
||||
<Card title="Light theme" desc="base='light' adapts the whole panel — LED wells go transparent so readouts and meters read as flat segments, and the buttons/switches become light raised chrome instead of dark chips." tag="<DreamknobProvider base='light' />">
|
||||
<LightThemeDemo />
|
||||
</Card>
|
||||
|
||||
<Card title="Meters" desc="Stereo metering from one component (value={[l, r]}), or a full labeled bridge with per-channel peak text. Click the clip LED to clear it." tag="<Meter /> · <MeterBridge />">
|
||||
<MeterDemo />
|
||||
</Card>
|
||||
|
|
|
|||
|
|
@ -30,12 +30,12 @@ export const GettingStarted: React.FC = () => (
|
|||
|
||||
<Step n={1} title="Install">
|
||||
<CodeBlock
|
||||
code={`pnpm add https://git.dreamodus.software/Dreamodus/DreamKnob/releases/download/v1.1.3/dreamknob-1.1.3.tgz\n# (or npm install <same url>) · peer deps: react >= 18, react-dom >= 18`}
|
||||
code={`pnpm add https://git.dreamodus.software/Dreamodus/DreamKnob/releases/download/v1.1.4/dreamknob-1.1.4.tgz\n# (or npm install <same url>) · peer deps: react >= 18, react-dom >= 18`}
|
||||
/>
|
||||
<p className="api-note" style={{ marginTop: 10 }}>
|
||||
Installs straight from the{' '}
|
||||
<a href="https://git.dreamodus.software/Dreamodus/DreamKnob/releases">
|
||||
v1.1.3 release
|
||||
v1.1.4 release
|
||||
</a>{' '}
|
||||
on our Forgejo. Zero runtime dependencies, ESM + CJS, full TypeScript types. No
|
||||
CSS file to import — everything is SVG and inline styles.
|
||||
|
|
|
|||
|
|
@ -25,10 +25,10 @@ export const Hero: React.FC = () => {
|
|||
range, any decimal precision, and colors you fully control.
|
||||
</p>
|
||||
<div className="install">
|
||||
<span>pnpm add https://git.dreamodus.software/Dreamodus/DreamKnob/releases/download/v1.1.3/dreamknob-1.1.3.tgz</span>
|
||||
<span>pnpm add https://git.dreamodus.software/Dreamodus/DreamKnob/releases/download/v1.1.4/dreamknob-1.1.4.tgz</span>
|
||||
<button
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText('pnpm add https://git.dreamodus.software/Dreamodus/DreamKnob/releases/download/v1.1.3/dreamknob-1.1.3.tgz');
|
||||
navigator.clipboard.writeText('pnpm add https://git.dreamodus.software/Dreamodus/DreamKnob/releases/download/v1.1.4/dreamknob-1.1.4.tgz');
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 1400);
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,29 @@
|
|||
# Changelog
|
||||
|
||||
## 1.1.4 — 2026-07-14
|
||||
|
||||
Light-theme support — the controls whose chrome was hardcoded dark now adapt.
|
||||
|
||||
### Themeable LED well
|
||||
- **`ledWell` theme token** — the recessed "screen" fill behind the LED
|
||||
segments of `LEDKnob`, `LEDFader`, `Meter`, and the default well of
|
||||
`SegmentDisplay` / `AlphaDisplay`, now comes from the theme instead of a
|
||||
hardcoded near-black. Dark base keeps the classic dark well (`#0b0d0e`); the
|
||||
**light base is `transparent`**, so LED controls and readouts render as clean
|
||||
flat segment rings / bar meters / digits on the light panel (still clearly
|
||||
"LED" from the lit segments) instead of dark blobs. The `faceColor` /
|
||||
`background` props still override per-instance.
|
||||
|
||||
### Light-theme panel chrome
|
||||
- **`scheme` theme token** (`'dark' | 'light'`, set by the `base`) drives the
|
||||
raised gradient/border/shadow of `PushButton` (and `Button`),
|
||||
`TransportButton` and `SegmentSwitch`. On a light base they render as light
|
||||
raised buttons instead of dark chips; unlit LED pips flip to a faint dark so
|
||||
they still read. New `panelButtonChrome(scheme, pressed)` helper.
|
||||
- Fixes the whole panel-button/switch family looking like dark chips on light
|
||||
themes; the flat knobs and faders already adapted (their face is the `face`
|
||||
token).
|
||||
|
||||
## 1.1.3 — 2026-07-14
|
||||
|
||||
Fourth integration-feedback release — a composability fix for buttons, a much
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ headless core, any number range, any decimal precision, and colors you fully con
|
|||
Zero runtime dependencies.
|
||||
|
||||
```
|
||||
pnpm add https://git.dreamodus.software/Dreamodus/DreamKnob/releases/download/v1.1.3/dreamknob-1.1.3.tgz
|
||||
pnpm add https://git.dreamodus.software/Dreamodus/DreamKnob/releases/download/v1.1.4/dreamknob-1.1.4.tgz
|
||||
```
|
||||
|
||||
(Latest release tarballs: https://git.dreamodus.software/Dreamodus/DreamKnob/releases)
|
||||
|
|
@ -137,7 +137,9 @@ import { DreamknobProvider } from 'dreamknob'
|
|||
|
||||
`base="light"` swaps in light-background defaults. Tokens: `accent`, `track`, `face`,
|
||||
`text`, `label`, `ticks`, `focusRing`, `zoneGood`/`zoneWarn`/`zoneHot` (meter zones),
|
||||
`ledGreen`/`ledAmber` (display defaults), `panel` (rack chrome), `fontMono`, `fontUI`.
|
||||
`ledGreen`/`ledAmber` (display defaults), `panel` (rack chrome), `ledWell` (LED display
|
||||
well — transparent on the light base), `fontMono`, `fontUI`. `scheme` (`'dark'|'light'`)
|
||||
is set by `base` and drives the panel-button/switch chrome; you rarely set it directly.
|
||||
|
||||
Every knob skin and fader takes the core props plus `size`, `label`, `showValue`,
|
||||
`unit`, `format`, and per-part color props (`color`, `trackColor`, `faceColor`,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "dreamknob",
|
||||
"version": "1.1.3",
|
||||
"version": "1.1.4",
|
||||
"description": "A feature-full, studio-grade React knob & fader library. Rotary, linear, 2D, 3D, digital and analog styles with a headless core.",
|
||||
"license": "MIT",
|
||||
"author": "Dreamodus Software Inc.",
|
||||
|
|
|
|||
|
|
@ -40,10 +40,58 @@ export interface KnobTheme {
|
|||
ledAmber: string;
|
||||
/** Panel/rack chrome background (`Rack`, panel containers). */
|
||||
panel: string;
|
||||
/**
|
||||
* The "well" behind LED displays — the recessed screen of `LEDKnob`,
|
||||
* `LEDFader`, `Meter` and the default panel of `SegmentDisplay` /
|
||||
* `AlphaDisplay`. Dark base keeps the classic near-black well (`#0b0d0e`) so
|
||||
* lit segments read as glowing; the light base is `transparent`, so LED
|
||||
* controls and readouts render as clean flat segment rings / bar meters /
|
||||
* digits on the light panel instead of dark blobs. Per-instance
|
||||
* `faceColor` / `background` props still override it.
|
||||
*/
|
||||
ledWell: string;
|
||||
/**
|
||||
* Which base this theme derives from. Panel chrome that can't be expressed
|
||||
* as a single token — the raised gradient/border/shadow of `PushButton`,
|
||||
* `TransportButton`, `SegmentSwitch` — switches between dark and light
|
||||
* treatments on this. Set automatically by the `base`; overrides rarely
|
||||
* need to touch it.
|
||||
*/
|
||||
scheme: 'dark' | 'light';
|
||||
fontMono: string;
|
||||
fontUI: string;
|
||||
}
|
||||
|
||||
/** Raised panel-button chrome (background gradient, border, inset/drop shadow)
|
||||
* for the current scheme — shared by PushButton, TransportButton and the
|
||||
* SegmentSwitch container so they stay consistent and adapt to light themes
|
||||
* instead of being hardcoded dark chips. */
|
||||
export function panelButtonChrome(
|
||||
scheme: 'dark' | 'light',
|
||||
pressed: boolean,
|
||||
): { background: string; border: string; boxShadow: string } {
|
||||
if (scheme === 'light') {
|
||||
return {
|
||||
border: '1px solid rgba(0,0,0,0.16)',
|
||||
background: pressed
|
||||
? 'linear-gradient(180deg, #d9dbe0, #e9eaee)'
|
||||
: 'linear-gradient(180deg, #ffffff, #e7e8ec)',
|
||||
boxShadow: pressed
|
||||
? 'inset 0 2px 4px rgba(0,0,0,0.16)'
|
||||
: 'inset 0 1px 0 rgba(255,255,255,0.9), 0 1px 2px rgba(0,0,0,0.13)',
|
||||
};
|
||||
}
|
||||
return {
|
||||
border: '1px solid rgba(0,0,0,0.7)',
|
||||
background: pressed
|
||||
? 'linear-gradient(180deg, #17181d, #232429)'
|
||||
: 'linear-gradient(180deg, #35363d, #1d1e24)',
|
||||
boxShadow: pressed
|
||||
? 'inset 0 2px 5px rgba(0,0,0,0.65)'
|
||||
: 'inset 0 1px 0 rgba(255,255,255,0.09), 0 2px 4px rgba(0,0,0,0.45)',
|
||||
};
|
||||
}
|
||||
|
||||
export const darkTheme: KnobTheme = {
|
||||
track: 'rgba(255,255,255,0.12)',
|
||||
face: 'rgba(255,255,255,0.05)',
|
||||
|
|
@ -57,6 +105,8 @@ export const darkTheme: KnobTheme = {
|
|||
ledGreen: '#3df2ad',
|
||||
ledAmber: '#ffb84d',
|
||||
panel: 'rgba(255,255,255,0.03)',
|
||||
ledWell: '#0b0d0e',
|
||||
scheme: 'dark',
|
||||
fontMono: MONO_FONT,
|
||||
fontUI: UI_FONT,
|
||||
};
|
||||
|
|
@ -74,6 +124,8 @@ export const lightTheme: KnobTheme = {
|
|||
ledGreen: '#0fa571',
|
||||
ledAmber: '#b26a00',
|
||||
panel: 'rgba(0,0,0,0.04)',
|
||||
ledWell: 'transparent',
|
||||
scheme: 'light',
|
||||
fontMono: MONO_FONT,
|
||||
fontUI: UI_FONT,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -145,7 +145,7 @@ export const AlphaDisplay: React.FC<AlphaDisplayProps> = ({
|
|||
color,
|
||||
weight = 1,
|
||||
gap = DEFAULT_GAP,
|
||||
background = '#0a0d0c',
|
||||
background: backgroundProp,
|
||||
ghostOpacity = 0.08,
|
||||
skew = 6,
|
||||
glow = 1.4,
|
||||
|
|
@ -156,6 +156,9 @@ export const AlphaDisplay: React.FC<AlphaDisplayProps> = ({
|
|||
}) => {
|
||||
const theme = useKnobTheme();
|
||||
const lit = color ?? theme.ledAmber;
|
||||
// Default the display well to the theme's ledWell (transparent on the light
|
||||
// base) so readouts adapt to light themes.
|
||||
const background = backgroundProp ?? theme.ledWell ?? '#0a0d0c';
|
||||
const filterId = React.useId();
|
||||
const half = BASE_HALF * weight;
|
||||
const POLY = buildPoly(half);
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ export const SegmentDisplay: React.FC<SegmentDisplayProps> = ({
|
|||
zones,
|
||||
weight = 1,
|
||||
gap,
|
||||
background = '#0a0d0c',
|
||||
background: backgroundProp,
|
||||
ghostOpacity = 0.09,
|
||||
skew = 6,
|
||||
glow = 1.6,
|
||||
|
|
@ -67,6 +67,9 @@ export const SegmentDisplay: React.FC<SegmentDisplayProps> = ({
|
|||
style,
|
||||
}) => {
|
||||
const theme = useKnobTheme();
|
||||
// Default the display well to the theme's ledWell (dark base = dark well,
|
||||
// light base = transparent) so readouts adapt to light themes.
|
||||
const background = backgroundProp ?? theme.ledWell ?? '#0a0d0c';
|
||||
const filterId = React.useId();
|
||||
const numeric = typeof value === 'number' ? value : NaN;
|
||||
const resolved =
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ export const LEDFader = React.forwardRef<HTMLDivElement, LEDFaderProps>(function
|
|||
zones,
|
||||
offOpacity = 0.13,
|
||||
glow = true,
|
||||
faceColor = '#0b0d0e',
|
||||
faceColor: faceColorProp,
|
||||
focusRing,
|
||||
label,
|
||||
showValue = true,
|
||||
|
|
@ -89,6 +89,7 @@ export const LEDFader = React.forwardRef<HTMLDivElement, LEDFaderProps>(function
|
|||
const [editing, setEditing] = React.useState(false);
|
||||
const theme = useKnobTheme();
|
||||
const color = colorProp ?? theme.accent;
|
||||
const faceColor = faceColorProp ?? theme.ledWell ?? '#0b0d0e';
|
||||
const ring = focusRing ?? theme.focusRing;
|
||||
const vertical = orientation === 'vertical';
|
||||
const pad = 5;
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ export const LEDKnob = React.forwardRef<HTMLDivElement, LEDKnobProps>(function L
|
|||
digits = 3,
|
||||
displayDecimals,
|
||||
labelColor,
|
||||
faceColor = '#0b0d0e',
|
||||
faceColor: faceColorProp,
|
||||
label,
|
||||
sublabel,
|
||||
labelSize,
|
||||
|
|
@ -102,6 +102,7 @@ export const LEDKnob = React.forwardRef<HTMLDivElement, LEDKnobProps>(function L
|
|||
}, ref) {
|
||||
const theme = useKnobTheme();
|
||||
const color = colorProp ?? theme.accent ?? '#3df2ad';
|
||||
const faceColor = faceColorProp ?? theme.ledWell ?? '#0b0d0e';
|
||||
const id = React.useId();
|
||||
const glowId = `${id}-glow`;
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ export const Meter: React.FC<MeterProps> = ({
|
|||
clipHold = 1500,
|
||||
clipColor = '#ff2b39',
|
||||
onClip,
|
||||
faceColor = '#0b0d0e',
|
||||
faceColor: faceColorProp,
|
||||
label,
|
||||
showValue = false,
|
||||
digits = 4,
|
||||
|
|
@ -163,6 +163,7 @@ export const Meter: React.FC<MeterProps> = ({
|
|||
...aria
|
||||
}) => {
|
||||
const theme = useKnobTheme();
|
||||
const faceColor = faceColorProp ?? theme.ledWell ?? '#0b0d0e';
|
||||
const vertical = orientation === 'vertical';
|
||||
const dbScale = scale === 'db';
|
||||
const min = minProp ?? (dbScale ? -60 : 0);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import * as React from 'react';
|
||||
import { useKnobTheme } from '../core/theme';
|
||||
import { panelButtonChrome, useKnobTheme } from '../core/theme';
|
||||
|
||||
/** Call every handler in order (skipping undefined) — for merging our own
|
||||
* handlers with consumer/Radix-injected ones on the same event. */
|
||||
|
|
@ -87,6 +87,9 @@ export const PushButton = React.forwardRef<HTMLButtonElement, PushButtonProps>(
|
|||
const latched = isControlled ? (pressed as boolean) : internal;
|
||||
const on = mode === 'action' ? held : latched; // press-down / background inset
|
||||
const ledOn = mode === 'action' ? held || flash : latched;
|
||||
const chrome = panelButtonChrome(theme.scheme, on);
|
||||
// Unlit LED tint: faint white on dark chrome, faint black on light.
|
||||
const ledOff = theme.scheme === 'light' ? 'rgba(0,0,0,0.13)' : 'rgba(255,255,255,0.09)';
|
||||
|
||||
const set = (next: boolean) => {
|
||||
if (next === latched) return;
|
||||
|
|
@ -174,16 +177,12 @@ export const PushButton = React.forwardRef<HTMLButtonElement, PushButtonProps>(
|
|||
minWidth: size * 1.4,
|
||||
padding: '0 12px',
|
||||
borderRadius: 7,
|
||||
border: '1px solid rgba(0,0,0,0.7)',
|
||||
background: on
|
||||
? 'linear-gradient(180deg, #17181d, #232429)'
|
||||
: 'linear-gradient(180deg, #35363d, #1d1e24)',
|
||||
border: chrome.border,
|
||||
background: chrome.background,
|
||||
outline: 'none',
|
||||
boxShadow: `${
|
||||
on
|
||||
? 'inset 0 2px 5px rgba(0,0,0,0.65)'
|
||||
: 'inset 0 1px 0 rgba(255,255,255,0.09), 0 2px 4px rgba(0,0,0,0.45)'
|
||||
}${focusVisible ? `, 0 0 0 2px ${theme.focusRing}` : ''}`,
|
||||
boxShadow: `${chrome.boxShadow}${
|
||||
focusVisible ? `, 0 0 0 2px ${theme.focusRing}` : ''
|
||||
}`,
|
||||
color: on ? theme.text : theme.label,
|
||||
fontFamily: theme.fontUI,
|
||||
fontSize: Math.max(10, size * 0.28),
|
||||
|
|
@ -206,7 +205,7 @@ export const PushButton = React.forwardRef<HTMLButtonElement, PushButtonProps>(
|
|||
width: '55%',
|
||||
height: Math.max(3, size * 0.09),
|
||||
borderRadius: 3,
|
||||
background: ledOn ? accent : 'rgba(255,255,255,0.09)',
|
||||
background: ledOn ? accent : ledOff,
|
||||
boxShadow: ledOn ? `0 0 ${size * 0.22}px ${accent}` : 'inset 0 1px 1px rgba(0,0,0,0.6)',
|
||||
transition: 'background 80ms, box-shadow 80ms',
|
||||
}}
|
||||
|
|
@ -222,7 +221,7 @@ export const PushButton = React.forwardRef<HTMLButtonElement, PushButtonProps>(
|
|||
width: Math.max(4, size * 0.14),
|
||||
height: Math.max(4, size * 0.14),
|
||||
borderRadius: '50%',
|
||||
background: ledOn ? accent : 'rgba(255,255,255,0.09)',
|
||||
background: ledOn ? accent : ledOff,
|
||||
boxShadow: ledOn ? `0 0 ${size * 0.25}px ${accent}` : 'inset 0 1px 1px rgba(0,0,0,0.6)',
|
||||
transition: 'background 80ms, box-shadow 80ms',
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ export const SegmentSwitch = React.forwardRef<HTMLDivElement, SegmentSwitchProps
|
|||
) {
|
||||
const theme = useKnobTheme();
|
||||
const accent = color ?? theme.accent ?? '#4cc2ff';
|
||||
const light = theme.scheme === 'light';
|
||||
const isControlled = value !== undefined;
|
||||
const [internal, setInternal] = React.useState(defaultValue);
|
||||
const active = Math.max(0, Math.min(options.length - 1, isControlled ? (value as number) : internal));
|
||||
|
|
@ -88,9 +89,13 @@ export const SegmentSwitch = React.forwardRef<HTMLDivElement, SegmentSwitchProps
|
|||
display: 'inline-flex',
|
||||
height: size,
|
||||
borderRadius: 8,
|
||||
border: '1px solid rgba(0,0,0,0.7)',
|
||||
background: 'linear-gradient(180deg, #16171c, #101116)',
|
||||
boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.06), 0 2px 4px rgba(0,0,0,0.4)',
|
||||
border: light ? '1px solid rgba(0,0,0,0.14)' : '1px solid rgba(0,0,0,0.7)',
|
||||
background: light
|
||||
? 'linear-gradient(180deg, #e3e4e8, #edeef1)'
|
||||
: 'linear-gradient(180deg, #16171c, #101116)',
|
||||
boxShadow: light
|
||||
? 'inset 0 1px 2px rgba(0,0,0,0.1)'
|
||||
: 'inset 0 1px 0 rgba(255,255,255,0.06), 0 2px 4px rgba(0,0,0,0.4)',
|
||||
overflow: 'hidden',
|
||||
opacity: disabled ? 0.45 : 1,
|
||||
userSelect: 'none',
|
||||
|
|
@ -122,11 +127,20 @@ export const SegmentSwitch = React.forwardRef<HTMLDivElement, SegmentSwitchProps
|
|||
padding: '0 12px',
|
||||
minWidth: size * 1.6,
|
||||
border: 'none',
|
||||
borderRight: i < options.length - 1 ? '1px solid rgba(0,0,0,0.55)' : 'none',
|
||||
borderRight:
|
||||
i < options.length - 1
|
||||
? `1px solid ${light ? 'rgba(0,0,0,0.1)' : 'rgba(0,0,0,0.55)'}`
|
||||
: 'none',
|
||||
background: isActive
|
||||
? 'linear-gradient(180deg, rgba(0,0,0,0.5), rgba(0,0,0,0.25))'
|
||||
? light
|
||||
? 'linear-gradient(180deg, #ffffff, #f4f5f7)'
|
||||
: 'linear-gradient(180deg, rgba(0,0,0,0.5), rgba(0,0,0,0.25))'
|
||||
: 'transparent',
|
||||
boxShadow: isActive ? 'inset 0 2px 4px rgba(0,0,0,0.55)' : undefined,
|
||||
boxShadow: isActive
|
||||
? light
|
||||
? '0 1px 2px rgba(0,0,0,0.16)'
|
||||
: 'inset 0 2px 4px rgba(0,0,0,0.55)'
|
||||
: undefined,
|
||||
color: isActive ? theme.text : theme.label,
|
||||
fontFamily: theme.fontUI,
|
||||
fontSize: Math.max(10, size * 0.34),
|
||||
|
|
@ -145,7 +159,7 @@ export const SegmentSwitch = React.forwardRef<HTMLDivElement, SegmentSwitchProps
|
|||
width: '55%',
|
||||
height: Math.max(2, size * 0.08),
|
||||
borderRadius: 2,
|
||||
background: isActive ? accent : 'rgba(255,255,255,0.08)',
|
||||
background: isActive ? accent : light ? 'rgba(0,0,0,0.12)' : 'rgba(255,255,255,0.08)',
|
||||
boxShadow: isActive ? `0 0 ${size * 0.2}px ${accent}` : undefined,
|
||||
transition: 'background 80ms, box-shadow 80ms',
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import * as React from 'react';
|
||||
import { useKnobTheme } from '../core/theme';
|
||||
import { panelButtonChrome, useKnobTheme } from '../core/theme';
|
||||
|
||||
export type TransportKind =
|
||||
| 'play'
|
||||
|
|
@ -90,6 +90,7 @@ export const TransportButton = React.forwardRef<HTMLButtonElement, TransportButt
|
|||
const theme = useKnobTheme();
|
||||
const [focusVisible, setFocusVisible] = React.useState(false);
|
||||
const glyphRef = React.useRef<SVGSVGElement>(null);
|
||||
const chrome = panelButtonChrome(theme.scheme, active);
|
||||
|
||||
const accent =
|
||||
color ??
|
||||
|
|
@ -145,17 +146,11 @@ export const TransportButton = React.forwardRef<HTMLButtonElement, TransportButt
|
|||
height: size,
|
||||
padding: 0,
|
||||
borderRadius: 8,
|
||||
border: '1px solid rgba(0,0,0,0.6)',
|
||||
background: active
|
||||
? 'linear-gradient(180deg, #17181d, #232429)'
|
||||
: 'linear-gradient(180deg, #35363d, #1d1e24)',
|
||||
boxShadow: `${
|
||||
active
|
||||
? 'inset 0 2px 5px rgba(0,0,0,0.6)'
|
||||
: 'inset 0 1px 0 rgba(255,255,255,0.09), 0 2px 4px rgba(0,0,0,0.45)'
|
||||
}${active ? `, 0 0 ${size * 0.28}px ${accent}55` : ''}${
|
||||
focusVisible ? `, 0 0 0 2px ${theme.focusRing}` : ''
|
||||
}`,
|
||||
border: chrome.border,
|
||||
background: chrome.background,
|
||||
boxShadow: `${chrome.boxShadow}${
|
||||
active ? `, 0 0 ${size * 0.28}px ${accent}55` : ''
|
||||
}${focusVisible ? `, 0 0 0 2px ${theme.focusRing}` : ''}`,
|
||||
outline: 'none',
|
||||
cursor: disabled ? 'not-allowed' : 'pointer',
|
||||
opacity: disabled ? 0.45 : 1,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue