Skip to content

Commit 5063750

Browse files
authored
feat(watch/ui): implement BEM compliance and CSS modularization (#978)
1 parent 3056a3e commit 5063750

17 files changed

Lines changed: 441 additions & 398 deletions

js/ui-core/src/variables.css

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22
--color-white: #ffffff;
33
--color-white-alpha-05: rgba(255, 255, 255, 0.05);
44
--color-white-alpha-10: rgba(255, 255, 255, 0.1);
5+
--color-white-alpha-15: rgba(255, 255, 255, 0.15);
56
--color-white-alpha-20: rgba(255, 255, 255, 0.2);
67
--color-white-alpha-25: rgba(255, 255, 255, 0.25);
78
--color-white-alpha-30: rgba(255, 255, 255, 0.3);
89
--color-white-alpha-70: rgba(255, 255, 255, 0.7);
910
--color-white-alpha-90: rgba(255, 255, 255, 0.9);
1011
--color-gray-100: #9ca3af;
12+
--color-gray-950: #1a1a1a;
1113
--color-blue-400: #00dfff;
1214
--color-blue-500: #52a1ff;
1315
--color-blue-900: #1b3243;
@@ -19,6 +21,7 @@
1921
--color-yellow-400: #facc15;
2022
--color-orange-300: rgb(253, 186, 116);
2123
--color-orange-400: #ff9900;
24+
--color-orange-450: #fb923c;
2225
--color-orange-500: #f97316;
2326
--color-orange-900: #4d301b;
2427
--color-amber-600: #d97706;
@@ -41,6 +44,7 @@
4144
--color-black-alpha-8: rgba(0, 0, 0, 0.08);
4245
--color-black-alpha-10: rgba(0, 0, 0, 0.1);
4346
--color-black-alpha-60: rgba(0, 0, 0, 0.6);
47+
--color-black-alpha-70: rgba(0, 0, 0, 0.7);
4448
--color-black-alpha-85: rgba(0, 0, 0, 0.85);
4549
--color-black: #000000;
4650
--spacing-1: 0.0625rem; /* 1px */
@@ -50,6 +54,7 @@
5054
--spacing-12: 0.75rem; /* 12px */
5155
--spacing-16: 1rem; /* 16px */
5256
--spacing-24: 1.5rem; /* 24px */
57+
--spacing-32: 2rem; /* 32px */
5358
--spacing-48: 3rem; /* 48px */
5459
--border-radius-sm: 0.5rem;
5560
--border-radius-md: 0.75rem;

js/watch/src/ui/components/BufferControl.tsx

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,10 @@ export default function BufferControl(props: BufferControlProps) {
8686
});
8787

8888
return (
89-
<div class="buffer__container">
89+
<div class="watch-ui__buffer">
9090
{/* Buffer Visualization - interactive, click/drag to set buffer */}
9191
<div
92-
class={`buffer__visualization ${isDragging() ? "buffer__visualization--dragging" : ""}`}
92+
class={`watch-ui__buffer-visualization ${isDragging() ? "watch-ui__buffer-visualization--dragging" : ""}`}
9393
ref={containerRef}
9494
onMouseDown={onMouseDown}
9595
role="slider"
@@ -100,11 +100,11 @@ export default function BufferControl(props: BufferControlProps) {
100100
aria-label="Buffer jitter"
101101
>
102102
{/* Playhead (left edge = current time) */}
103-
<div class="buffer__playhead" />
103+
<div class="watch-ui__buffer-playhead" />
104104

105105
{/* Video buffer track */}
106-
<div class="buffer__track buffer__track--video">
107-
<span class="buffer__track-label">Video</span>
106+
<div class="watch-ui__buffer-track watch-ui__buffer-track--video">
107+
<span class="watch-ui__buffer-track-label">Video</span>
108108
<For each={context.videoBuffered()}>
109109
{(range, i) => {
110110
const info = () => {
@@ -115,9 +115,11 @@ export default function BufferControl(props: BufferControlProps) {
115115
return (
116116
<Show when={info()}>
117117
{(rangeInfo) => (
118-
<div class="buffer__range" style={rangeInfo().style}>
118+
<div class="watch-ui__buffer-range" style={rangeInfo().style}>
119119
<Show when={rangeInfo().isOverflow}>
120-
<span class="buffer__overflow-label">{rangeInfo().overflowSec}s</span>
120+
<span class="watch-ui__buffer-overflow-label">
121+
{rangeInfo().overflowSec}s
122+
</span>
121123
</Show>
122124
</div>
123125
)}
@@ -128,8 +130,8 @@ export default function BufferControl(props: BufferControlProps) {
128130
</div>
129131

130132
{/* Audio buffer track */}
131-
<div class="buffer__track buffer__track--audio">
132-
<span class="buffer__track-label">Audio</span>
133+
<div class="watch-ui__buffer-track watch-ui__buffer-track--audio">
134+
<span class="watch-ui__buffer-track-label">Audio</span>
133135
<For each={context.audioBuffered()}>
134136
{(range, i) => {
135137
const info = () => {
@@ -140,9 +142,11 @@ export default function BufferControl(props: BufferControlProps) {
140142
return (
141143
<Show when={info()}>
142144
{(rangeInfo) => (
143-
<div class="buffer__range" style={rangeInfo().style}>
145+
<div class="watch-ui__buffer-range" style={rangeInfo().style}>
144146
<Show when={rangeInfo().isOverflow}>
145-
<span class="buffer__overflow-label">{rangeInfo().overflowSec}s</span>
147+
<span class="watch-ui__buffer-overflow-label">
148+
{rangeInfo().overflowSec}s
149+
</span>
146150
</Show>
147151
</div>
148152
)}
@@ -153,9 +157,9 @@ export default function BufferControl(props: BufferControlProps) {
153157
</div>
154158

155159
{/* Buffer target line (draggable) - wrapped in track-area container */}
156-
<div class="buffer__target-area">
157-
<div class="buffer__target-line" style={{ left: `${bufferTargetPct()}%` }}>
158-
<span class="buffer__target-label">{`${Math.round(context.jitter())}ms`}</span>
160+
<div class="watch-ui__buffer-target-area">
161+
<div class="watch-ui__buffer-target-line" style={{ left: `${bufferTargetPct()}%` }}>
162+
<span class="watch-ui__buffer-target-label">{`${Math.round(context.jitter())}ms`}</span>
159163
</div>
160164
</div>
161165
</div>

js/watch/src/ui/components/BufferingIndicator.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ export default function BufferingIndicator() {
66

77
return (
88
<Show when={context.buffering()}>
9-
<div class="bufferingContainer">
10-
<div class="bufferingSpinner" />
9+
<div class="watch-ui__buffering flex--center">
10+
<div class="watch-ui__buffering-spinner" />
1111
</div>
1212
</Show>
1313
);

js/watch/src/ui/components/LatencySlider.tsx

Lines changed: 0 additions & 34 deletions
This file was deleted.

js/watch/src/ui/components/QualitySelector.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@ export default function QualitySelector() {
1010
};
1111

1212
return (
13-
<div class="qualitySelectorContainer">
14-
<label for="quality-select" class="qualityLabel">
13+
<div class="watch-ui__quality-selector">
14+
<label for="quality-select" class="watch-ui__quality-label">
1515
Quality:{" "}
1616
</label>
1717
<select
1818
id="quality-select"
1919
onChange={handleQualityChange}
20-
class="qualitySelect"
20+
class="watch-ui__quality-select"
2121
value={context.activeRendition() ?? ""}
2222
>
2323
<option value="">Auto</option>

js/watch/src/ui/components/VolumeSlider.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,12 @@ export default function VolumeSlider() {
3030
});
3131

3232
return (
33-
<div class="volumeSliderContainer">
33+
<div class="watch-ui__volume-slider flex--center">
3434
<Button title={context.isMuted() ? "Unmute" : "Mute"} onClick={() => context.toggleMuted()}>
3535
{getVolumeIcon(context.currentVolume(), context.isMuted())}
3636
</Button>
3737
<input type="range" onChange={onInputChange} min="0" max="100" value={context.currentVolume()} />
38-
<span class="volumeLabel">{volumeLabel()}</span>
38+
<span class="watch-ui__volume-label">{volumeLabel()}</span>
3939
</div>
4040
);
4141
}

js/watch/src/ui/components/WatchControls.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@ import WatchStatusIndicator from "./WatchStatusIndicator";
88

99
export default function WatchControls() {
1010
return (
11-
<div class="watchControlsContainer">
12-
<div class="playbackControlsRow flex--align-center">
11+
<div class="watch-ui__controls">
12+
<div class="watch-ui__playback-controls flex--align-center">
1313
<PlayPauseButton />
1414
<VolumeSlider />
1515
<WatchStatusIndicator />
1616
<StatsButton />
1717
<FullscreenButton />
1818
</div>
19-
<div class="latencyControlsRow">
19+
<div class="watch-ui__latency-controls">
2020
<BufferControl />
2121
<QualitySelector />
2222
</div>
Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,34 @@
1-
import { Match, Switch } from "solid-js";
1+
import type { WatchStatus } from "../context";
22
import useWatchUIContext from "../hooks/use-watch-ui";
33

4+
type StatusIndicatorConfig = { variant: string; text: string };
5+
6+
const STATUS_MAP: Record<WatchStatus, StatusIndicatorConfig> = {
7+
"no-url": { variant: "error", text: "No URL" },
8+
disconnected: { variant: "error", text: "Disconnected" },
9+
connecting: { variant: "connecting", text: "Connecting..." },
10+
offline: { variant: "error", text: "Offline" },
11+
loading: { variant: "loading", text: "Loading..." },
12+
live: { variant: "live", text: "Live" },
13+
connected: { variant: "connected", text: "Connected" },
14+
};
15+
16+
const unknownStatus: StatusIndicatorConfig = { variant: "error", text: "Unknown" };
17+
418
export default function WatchStatusIndicator() {
519
const context = useWatchUIContext();
620

21+
const statusConfig = (): StatusIndicatorConfig => {
22+
const status: WatchStatus = context.watchStatus();
23+
return STATUS_MAP[status] || unknownStatus;
24+
};
25+
726
return (
8-
<output>
9-
<Switch>
10-
<Match when={context.watchStatus() === "no-url"}>🔴 No URL</Match>
11-
<Match when={context.watchStatus() === "disconnected"}>🔴 Disconnected</Match>
12-
<Match when={context.watchStatus() === "connecting"}>🟡 Connecting...</Match>
13-
<Match when={context.watchStatus() === "offline"}>🔴 Offline</Match>
14-
<Match when={context.watchStatus() === "loading"}>🟡 Loading...</Match>
15-
<Match when={context.watchStatus() === "live"}>🟢 Live</Match>
16-
<Match when={context.watchStatus() === "connected"}>🟢 Connected</Match>
17-
</Switch>
18-
</output>
27+
<div class="watch-ui__status-indicator flex--center">
28+
<span class={`watch-ui__status-indicator-dot watch-ui__status-indicator-dot--${statusConfig().variant}`} />
29+
<span class={`watch-ui__status-indicator-text watch-ui__status-indicator-text--${statusConfig().variant}`}>
30+
{statusConfig().text}
31+
</span>
32+
</div>
1933
);
2034
}

js/watch/src/ui/context.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ type WatchUIContextProviderProps = {
1010
children: JSX.Element;
1111
};
1212

13-
type WatchStatus = "no-url" | "disconnected" | "connecting" | "offline" | "loading" | "live" | "connected";
13+
export type WatchStatus = "no-url" | "disconnected" | "connecting" | "offline" | "loading" | "live" | "connected";
1414

1515
export type Rendition = {
1616
name: string;

js/watch/src/ui/element.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export function WatchUI(props: { watch: MoqWatch }) {
1212
return (
1313
<WatchUIContextProvider moqWatch={props.watch}>
1414
<style>{styles}</style>
15-
<div class="watchVideoContainer">
15+
<div class="watch-ui__video-container">
1616
<slot />
1717
{(() => {
1818
const context = useContext(WatchUIContext);

0 commit comments

Comments
 (0)