Skip to content

feat(studio): the timeline toolbar, headers and playhead on the shared primitives - #3776

Draft
miguel-heygen wants to merge 3 commits into
feat/studio-u13-renders-sweepfrom
feat/studio-u11-timeline-player-sweep
Draft

feat(studio): the timeline toolbar, headers and playhead on the shared primitives#3776
miguel-heygen wants to merge 3 commits into
feat/studio-u13-renders-sweepfrom
feat/studio-u11-timeline-player-sweep

Conversation

@miguel-heygen

Copy link
Copy Markdown
Collaborator

Lands the first half of unit U11 (timeline toolbar, track headers, playhead token) of the Studio design-system foundation. Stacked on the Renders sweep (#3772) and merges the menu PR (#3625). Ratchet 470 to 452. Bundle +9.1 KB gzipped. The five timeline menus and the menuKeyboardNav deletion are the second half, in the next PR.

What

The timeline toolbar, the canvas snap toolbar and the track headers move onto the shared primitives, and the playhead moves onto its own colour token.

  • Every timeline-toolbar button is an IconButton (or a Button for "Fit"), and the zoom slider is the shared Slider. The four hand-written class recipes the toolbar carried (flatBtn, flatIdle, flatActive, flatDisabled) are gone.
  • The snap toolbar's grid-spacing panel is a Popover, and its three canvas buttons are IconButtons. The hand-rolled mousedown-outside listener and its two refs are deleted; the panel's contents are unchanged.
  • The playhead reads --color-playhead for its line, head and glow instead of the accent. The group-member rail on a track header reads the accent token at a Tailwind alpha.
  • Colour literals in the touched files drop from 20 to 2. Ratchet total 470 to 452.

This is the first half of the timeline sweep. The five timeline menus (clip, track gap, keyframe, automation, speed) and the menuKeyboardNav deletion follow in a second PR, so this one stays reviewable.

Why

Two controls that do the same thing should not have two implementations, and the timeline had the densest concentration of hand-written ones left. The measurable half is the playhead: it was drawn in the accent, so a playhead crossing a selected clip crossing an accented control was one colour doing three jobs. Playhead and selection are reserved colours precisely so that cannot happen.

Two defects surfaced on the way and are fixed here rather than filed:

  • The zoom readout printed the word "Fit" in fit mode, immediately beside the Fit button. Two identical labels side by side, and the one that looked like a second button did nothing when pressed. The readout is a percentage in both modes now.
  • The grid panel dismissed itself from a bubble-phase mousedown listener on document. The canvas overlay it sits on stops propagation on its own pointer handlers, so a press on the canvas could not reach it. Base UI registers outside-press with capture, so the panel now dismisses over the overlay. Verified in a browser, not only in the unit test.

How

  • IconButton and Button already own idle, hover, press, focus and disabled. Each toolbar toggle contributes only its "on" look, as one named class string per kind: a held hover wash for the on/off switches, an accent tint for the two view controls.
  • The keyframe button's accent hover is written as enabled:hover:text-accent so it merges against the ghost variant's own enabled:hover: class rather than racing it on specificity.
  • The 24px WCAG pointer target for the zoom slider now comes from the primitive's control rather than from vendor pseudo-element classes on a native range input. Track and thumb keep their 2px and 10px.
  • Two inline border shorthands became longhands. A shorthand whose colour is a var() is a pending-substitution value that the CSSOM will not read back, so nothing could assert the playhead's or the rail's colour.
  • The group-member tint stays a literal, with a comment saying why: it is composed with the theme's own gutter fill at runtime, and a color-mix() there parses in every browser and in none of the tests, because happy-dom rejects the whole declaration and silently keeps the previous value. Its token belongs to the ratchet-to-zero unit.

Test plan

  • Full Studio suite: 442 files passed, 1 skipped, 4859 tests, capped at 4 forks.
  • Typecheck, build, lint clean.
  • New coverage: both global hotkey filters still classify every toolbar control the way they classified the hand-rolled ones; the zoom slider leaves fit mode and applies its value; the readout is a percentage; the grid panel persists a spacing change through the existing preferences writer and closes on an outside press without writing one; the playhead paints its line and head from the playhead token and from no accent.
  • The zoom-slider test was proved non-vacuous by breaking the wiring on purpose and watching it fail.
  • Browser check: the grid panel opens over the real canvas overlay at the shared surface colour and radius, and an outside press on the canvas dismisses it. The three role tokens resolve to three different values at runtime.
  • Screenshot pair: the only row that moves in the computed-style table is the toolbar button's background, from the hand-written wash to the hover token; height and radius are unchanged at 28px and 6px. The playhead reads white against mint clips where it used to read mint.
  • Timeline viewport gate, both arms, before and after, 5 of 5 runs passing each time. Interaction p95 sits at roughly two fifths of its budget on both arms and moves by well under a millisecond either way.

Not covered

  • The five timeline menus and the menuKeyboardNav deletion. Second PR.
  • The volume control's range input is still a native range; only its mute button and track colours moved.
  • The remaining colour literals in the timeline: the canvas theme, the clip and waveform colours, the group-member tint, and one amber warning label with no token yet. Those belong to the ratchet-to-zero unit.
  • No Storybook stories are added for the migrated consumers; the primitives own theirs.

One menu chrome and one popover chrome, on Base UI, wearing the theme
tokens. Dismissal is plain Base UI with no capture-phase wrapper: its
outside-press listeners are registered on document with capture set, so
the canvas overlay's bubble-phase stopPropagation cannot starve them.

Menu items carry an optional mono shortcut hint, a disabled state and a
danger tone. ContextMenu opens at the pointer. Popover shares the
surface, radius and open motion but owns no item semantics, so a text
field inside it keeps its own keys.

Also adds [role='menuitemradio'] to the playback-shortcut ignore list.
The hand-rolled speed menu spelled its rows as buttons, which that list
already matched; the shared radio item is a div with the same role, and
nothing else in the list matched it, so an open speed menu would have
leaked arrow keys and Space to the player.
…opover' into feat/studio-u11-timeline-player-sweep

# Conflicts:
#	packages/studio/src/components/ui/index.ts
…d primitives

Every button in the timeline toolbar becomes an IconButton or a Button, the
zoom slider becomes the shared Slider, and the SnapToolbar's grid-spacing
panel becomes a Popover. Four hand-written class recipes (flatBtn, flatIdle,
flatActive, flatDisabled) and one hand-rolled mousedown-outside listener go
with them.

The playhead reads --color-playhead instead of the accent, so the playhead,
a selected clip and an accented control are three colours again rather than
one (R3, AE7). The group-member rail on a track header moves to the accent
token at a Tailwind alpha.

Two behaviour notes. The zoom readout printed the word "Fit" in fit mode
directly beside the Fit button, two identical labels of which one did
nothing; it is a percentage in both modes now. And the grid panel's outside
press is Base UI's, which listens in the capture phase, so it now dismisses
over the canvas overlay that used to swallow the press.

Colour literals in the touched files drop from 20 to 2; ratchet total 470 to
452.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant