Material 3 Expressive design system overhaul - #7
Merged
Merged
Conversation
…w-playing view Lyrics, Queue and About replace the artwork in place while the artwork springs into a thumbnail beside the title (FLIP); the choice is remembered. Wavy seek bar that flattens when paused, morphing play button, tonal playback options, artwork-tinted background darkening towards the bottom. The queue timeline rests on the current item with History above, Clear actions on History and Playing next, and follows the current item only while the user isn't working in it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PwQBQmwS2cmw42GzN9EVyJ
The section Clear buttons move out of the listbox (axe: a listbox holds options only) and are anchored onto their sticky headers with CSS anchor positioning. Snackbars drop in from the top while the player is open. The e2e specs drive the Lyrics / Queue / About toggles instead of the old fullscreen tabs, and cover the remembered pane, L/Q switching, the queue resting on the current item and clearing history. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PwQBQmwS2cmw42GzN9EVyJ
…on app-wide Shape and motion tokens; pill buttons and circular icon buttons that squash on press; tonal filled selection for nav items, tabs, menus and the palette; springier tiles, dialogs and snackbars; filled rounded inputs. The player bar keeps its look. Drops the old two-column fullscreen rules. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PwQBQmwS2cmw42GzN9EVyJ
The seek bar is rebuilt to Material 3 Expressive's wavy progress: a thick round-capped wave whose amplitude is a spring (it swells on play, settles flat on pause), tapering into the start and the thumb; a gap either side of a bar thumb that stretches while held, a value bubble while dragging or keyboard-focused, a stop indicator on the track, remaining time. Drawn by a rAF loop only while moving; still under reduced motion. Wide windows get a desktop layout instead of a stretched phone column: the artwork (or the pane replacing it) fills the left, the controls are a centred column on the right with bigger type, full-width toggles and an expressive volume slider. The artwork still flies between its slots. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PwQBQmwS2cmw42GzN9EVyJ
Wide windows: in the artwork mode the cover is much smaller and sits beside the controls, the two centred together; with Lyrics, Queue or About open the pane fills the left and the cover stacks, as large as the window allows, on top of the controls column. The controls glide to their new place (FLIP) as the cover flies between its slots. Narrower windows cap the cover and give the pane-mode thumbnail more room. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PwQBQmwS2cmw42GzN9EVyJ
Icons come from Material Symbols instead of Lucide: scripts/gen-icons.mjs extracts the outlined and filled rounded glyphs the renderer uses from the Iconify dataset into material-icons.ts (only those, ~50 kB), and Icon draws them. A `filled` prop replaces the old fill-style hack: transport glyphs are filled by default, a loved heart and set stars fill, and selected states (the current sidebar destination, the player's pressed toggles and pane pills) switch to the filled glyph as Material 3 does. Drops lucide-react. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PwQBQmwS2cmw42GzN9EVyJ
…s them Shuffle and repeat now flank the transport, and autoplay, device and sleep share a row with volume, in every layout. With Lyrics, Queue or About open on a wide window the controls condense further: title and its actions on one line, a smaller transport, tighter spacing. That leaves room for the stacked cover to fill the column's width (height allowing). Toasts pop in by transform only, so they are never drawn part-transparent (axe could catch one mid-fade below 4.5:1). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PwQBQmwS2cmw42GzN9EVyJ
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PwQBQmwS2cmw42GzN9EVyJ
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PwQBQmwS2cmw42GzN9EVyJ
…e panel cards, menus, dialogs, page transition) The window background is the plain surface; the content and the side panel float on it as rounded surface-container panes with an 8px gutter. The sidebar is a navigation drawer/rail on the background with a springy tonal indicator behind a filled icon; sentence-case section headings. The top bar blends into the background around a big centred pill search bar whose results drop as an attached sheet. Queue and lyrics are two stacked cards with a segmented pill Queue/Recent control. Menus, palette, jobs popover, dialogs and snackbars get larger shapes, expressive titles and spring entrances. Router keys the view on the page so each navigation rises in on a spring (transform only; off under reduced motion). Forced-colours and the narrow layout are covered in m3-shell.css. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PwQBQmwS2cmw42GzN9EVyJ
- Settings: pill section nav with icons and a filled tonal active item; display-size page title, sentence-case section headings; rows grouped into segmented list cards (2px gaps, big corners on the ends); M3 switches over the native checkboxes; M3 Expressive sliders (thick track, gap-separated bar handle, stop dot); filled rounded fields; cache usage as segmented linear progress; big EQ well; order lists and per-category shortcut groups as grouped lists with a morphing recorder. - Setup: expressive hero (decorative M3 shapes, display wordmark) beside a tonal card form with a large pill Connect button. - Downloads: connected button-group tabs, grouped rounded rows with morphing artwork, M3 linear progress with a gap and stop dot. - Filters: grouped rows with tonal shape avatars; builder rule groups as tonal cards, assist-chip actions, capability chip, sample side card. - Stats: display-number hero cards, pill bars, tonal cards. - Mini player: rounded art, filled play button that morphs while playing. - Icons: tune, graphic_eq, swap_horiz, cast, palette, keyboard, backup, dashboard_customize, headphones, schedule, dns, key. All motion is killed by reduced motion; forced-colours equivalents for the new switches, sliders, progress and grouped rows; narrow layout kept. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PwQBQmwS2cmw42GzN9EVyJ
Display-size page titles with counts as tonal chips and sort as a connected button group; tiles whose art lifts and morphs into a squircle on a spring, with a large squircle play FAB that rounds into a circle; artists clipped to a scalloped cookie that morphs on hover; home carousels under bold headings with tonal cookie icons; detail heroes as tonal cards with big squircle art, a large Play FAB, Shuffle and secondary actions as tonal buttons; track tables with a pill header, rounded rows that square off when selected and a tonal playing row; genre cards with a per-genre coloured decorative shape; empty states with a glyph in a tonal cookie. Short windows (200% zoom) get a compact hero and a scrolling detail page. Reduced motion and forced colours covered. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PwQBQmwS2cmw42GzN9EVyJ
Native form controls are replaced by our own, drawn to Material 3 Expressive and shared by every screen (components/controls.tsx, styles/controls.css): - Slider: thick rounded track split by a gap around a bar handle, stop dot, value bubble while dragged or focused; horizontal (volume in the player bar and fullscreen player, settings) and vertical (equaliser bands). A transparent native range input on top keeps keys, assistive tech and tests unchanged. - Switch: outlined when off; filled with an always-white handle and a check when on (role=switch over a native checkbox). - Select: a select-only combobox with an M3 menu (check + tonal fill on the chosen item, type-ahead, Home/End/Page keys), which follows its field on scroll instead of closing; same <option> children and onChange shape as <select>, so call sites only swapped the tag. - Number inputs lose the spinners; colour input is a round swatch. Album, playlist and artist Play buttons now work before the page's details have loaded (they only need the id): a quick Enter used to do nothing, which is what made the keyboard e2e test flaky. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PwQBQmwS2cmw42GzN9EVyJ
One layout grid, type ramp and control-size set, shared by every page: - tokens in global.css: --page-gutter/--page-title-inset/--page-pad, --title-display/--title-section/--title-card, --btn-h/-sm/-lg, --field-h - every view header/body uses them (the tool pages had 32/34px padding and 48px titles, the library 28px and a clamp); hero, track tables, grouped lists, cards and genre cards all start at the same gutter; tables no longer depend on inline "0 8px" padding (--table-inset) - one section-heading style (with the tonal icon on Home, Search and Artist pages), settings h2s match it; one card-title for cards, the filter sample and the jobs popover - text buttons 36px, small 32px, header/dialog actions 40px everywhere; text fields 36px (prompt dialog field was 28px); badges one size - one segmented tab style for Downloads and the side panel - Filters/Playlists create buttons and the builder's Play are tonal; Downloads usage text moved beside the tabs so actions stay on one line - empty states all carry a glyph (Filters, Stats, Downloads, side panel) - context menu clamps with its layout size (it overflowed the window while scaled by its entrance animation) and scrolls if taller than the window - sidebar Settings pill no longer wider than the others; settings nav wide enough for "Keyboard shortcuts"; carousel scrollbars show on hover only; narrow Home rows fill the width - removed dead/duplicated rules (per-page header/body/h1/field/button overrides, old detail-head action sizes, .home-section h2 that beat the shared heading rule, bordered pin-row/rule-group/card bases) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PwQBQmwS2cmw42GzN9EVyJ
Conflicts in m3-pages.css: the controls branch's select/switch/slider removals win; its page-scoped .input/.btn.sm heights are now covered by the shared --field-h / --btn-h-sm tokens; .m3-select keeps its bg-elev fill on setting rows and filter rule groups. .m3-select's height uses --field-h so it tracks the text fields. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PwQBQmwS2cmw42GzN9EVyJ
The Albums/genre sort group targeted the old native .select; with the merged .m3-select it rendered as a sunken 36px field beside a tonal 40px direction button. Target both, sized with --btn-h-lg. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PwQBQmwS2cmw42GzN9EVyJ
The hover state layer (14 % of the page colour over the toast) lightened the background under the --accent-inverse action text to about 3.1:1; axe caught it once a toast appeared under the pointer. Hover now draws a ring in the text colour and leaves the background alone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PwQBQmwS2cmw42GzN9EVyJ
…2e undo is real
The native core spells its default shortcuts with Ctrl ("Ctrl+Z", "Ctrl+K"
…) on Linux and Windows, which parsed as the literal Ctrl modifier while
chordFromEvent reports Ctrl there as the platform modifier: the keymap
never matched, so page-level Ctrl shortcuts (undo, palette, find…) did
nothing unless a focused widget handled the key. Off macOS the keymap now
folds a Ctrl binding into Mod.
native.spec's rate-then-undo passed on main only by accident: its click at
the content pane's centre moved the pointer off the rating stars, and the
undo it meant to test never reached the core. Here the taller album header
puts the album's own rating at that point, which exposed it. The test now
focuses the pane, moves the pointer away from the stars' hover preview,
and presses Ctrl+Z, which really undoes the rating.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PwQBQmwS2cmw42GzN9EVyJ
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements a comprehensive Material 3 Expressive design system refresh across the entire application, replacing the previous design language with Material 3's expressive aesthetic including updated typography, spacing, motion, and component styling.
Summary
This is a major design system update that introduces Material 3 Expressive tokens, springs-based motion, and redesigned form controls. The changes affect the global design tokens, all major UI components, and introduce new stylesheets for different page contexts (shell, library, pages).
Key Changes
Design Tokens & Motion
--shape-xsthrough--shape-full) replacing ad-hoc border-radius valueslinear()easings for spatial animations (FLIP transitions) and effects--dur-short,--dur-spring,--dur-spring-long)--surface-tonal,--surface-tonal-sunken) with contrast-checked accent washes--shadow-1,--shadow-2) and layout grid tokens (--page-gutter,--page-title-inset,--page-pad)Form Controls
components/controls.tsxwith Material 3 Expressive implementations:Slider: thick rounded track with gap around handle, optional value bubble, vertical orientation supportSwitch: outlined when off, filled with check when on, over transparent native checkboxSelect: select-only combobox with M3 menu replacing platform<select>Fullscreen Player Redesign
FullscreenPlayer.tsxwith new layout and interaction modelWavySeek.tsxcomponent: Material 3's wavy progress bar with amplitude spring that responds to playback statenowPlayingModein store)Stylesheet Organization
global.css:m3-shell.css: app shell, navigation, floating panes, dialogs, menusm3-library.css: library views with tiles, grids, artist cookies, genre cardsm3-pages.css: settings, first-run setup, tool pagescontrols.css: form control stylingnow-playing.css: fullscreen player and queue timeline--bg,--bg-elev,--bg-sunken, or contrast-checked accent tintsIcon System
material-icons.tsfrom@iconify-json/material-symbolsvia newscripts/gen-icons.mjsQueue Panel Updates
Settings Redesign
Spring Motion Library
lib/spring.ts: damped spring sampled intolinear()easingsSPRING_SPATIAL,SPRING_FAST,SPRING_EFFECTSwith easing and durationAccessibility & Reduced Motion
https://claude.ai/code/session_01PwQBQmwS2cmw42GzN9EVyJ