Adaptive colors with dark and light mode variants. Built on layered backgrounds for depth perception.
| Token | Dark Mode | Light Mode | Usage |
|---|---|---|---|
textPrimary | White 92% | Black 88% | Headings, body text |
textSecondary | White 74% | Black 66% | Labels, metadata |
textTertiary | White 56% | Black 55% | Hints, disabled, timestamps |
The native interface uses system SF Pro through SwiftUI system fonts. Use monospaced digits for numeric statistics and a 15pt reading style for dictations, meeting notes, and transcripts. Browser examples use the local system font; no remote font dependency.
| Variant | Usage | Example |
|---|---|---|
| Monospaced 13pt | Timestamps; transcript editing uses the reading style | 2026-03-16 09:41 |
| Rounded 24pt Semibold | Hotkey display in onboarding | Left Cmd |
| Monospaced 12pt Semibold | Version display | v0.4.0 |
4pt base grid. Seven increments cover all layout needs.
SF Symbols for UI chrome, animated waveform bars for the floating indicator, custom MWaveformIcon for branding.
Timeline (clock) leads: one chronological feed combining dictations and meetings. The Meetings row expands to reveal meeting folders, with children indented as a group (position carries nesting, not icon size). Insights is a first-class sidebar item. Models, Shortcuts, Settings, and About sit in the bottom section. The rail is fixed at 240pt expanded and collapses to a 68pt icon strip via the header toggle. Navigation labels are 14pt, medium normally and semibold when selected. Milestone-gated sharing links live in About.
Icons are outline-only at 14pt medium; .fill is reserved for selection state, keeping ink weight even across the column. The icon column is center-aligned and starts at the search field's inner padding, so row glyphs sit directly under the magnifier.
| Glyph | State | Context |
|---|---|---|
| ❚❚❚ | Idle, Preparing, Recording | Animated MWaveformIcon bars — dictation states render live amplitude, not emoji |
| ⏸ | Meeting recording | Pause — click to pause recording (recordingControlSymbol()) |
| ▶ | Meeting paused | Resume — click to continue recording |
| × | Dictation recording | Discard — cancel recording |
| ⏹ | Meeting (menu/loading) | Stop glyph in menu-style indicator contexts |
M-shaped waveform rendered as rounded bars. The full app icon uses ~21 bars forming a symmetrical "M" pattern with two peaks and a central valley. Simplified 9-bar preset for small contexts: [0.45, 0.65, 0.90, 1.0, 0.45, 1.0, 0.90, 0.65, 0.45]
Always-on-top frosted glass pill. Draggable (position persisted), clickable, with smooth animated transitions between states.
| Property | Value |
|---|---|
| Material | NSVisualEffectView with .hudWindow material, .withinWindow blending |
| Idle background | Frosted glass (vibrancy blur, no solid color) |
| Recording background | Solid accent color at 72% opacity (hides frost) |
| Corner radius | Half of pill height (fully rounded) |
| Window level | .floating |
| Accent color | User-configurable via Settings — shared with dashboard controls and indicator previews; semantic success/error colors stay distinct |
| Draggable | Click+drag to reposition. Anchor point persisted to config as indicatorAnchor |
| State | Width | Height | Tint | Alpha | Frost |
|---|---|---|---|---|---|
| Idle (compact) | 44pt | 28pt | #1e1e2e 44% | 0.85 | Visible |
| Idle (hover) | 220pt | 36pt | #1e1e2e 72% | 1.0 | Visible |
| Preparing | 44pt | 28pt | #1e1e2e 62% | 1.0 | Visible |
| Recording | 76pt | 22pt | recordingColorHex 85% | 1.0 | Hidden (solid) |
| Transcribing | 120pt | 32pt | #1e1e2e 62% | 1.0 | Visible |
| Warning | 260pt | 36pt | — | 1.0 | — |
| Loading | 180pt | 36pt | — | 1.0 | — |
| Zone | X Range | Action |
|---|---|---|
| Left | x < 30pt | Discard meeting (with confirmation alert) |
| Right | x ≥ 30pt | Stop meeting recording |
Toggle/hands-free dictation uses the same zones: left = cancel, right = stop.
| State | Border Color |
|---|---|
| Idle (compact) | White 22% |
| Idle (hover) | White 14% |
| Preparing / Recording / Transcribing | White 16% |
| Property | Value |
|---|---|
| Bar width | 3pt |
| Bar spacing | 4pt |
| Min height | 5pt |
| Max height | 26pt |
| Bar color | White 85% |
| Stop square | 6×6pt, White 85%, 1pt radius |
| 5-bar multipliers | [0.6, 0.85, 1.0, 0.85, 0.6] |
| 4-bar multipliers | [0.7, 1.0, 1.0, 0.7] |
| Animation | Scale Y, 0.6s, easeInEaseOut, autoreverses, 0.07s stagger |
| Amplitude smoothing | Exponential average, 0.5 weight, 30fps |
| Transition | Duration | Easing |
|---|---|---|
| Idle ↔ Hover | 0.24s in / 0.20s out | easeInEaseOut |
| Idle ↔ Active state | 0.18s | easeInEaseOut |
| Between active states | 0.16s | easeInEaseOut |
| Hover exit delay | 0.14s | — |
Atomic components used across the app interface.
| Variant | Background | Text | Padding | Radius |
|---|---|---|---|---|
| Primary | accent | White | H:20 V:8 | 6pt |
| Secondary / Icon | surfacePrimary | textSecondary | H:8 V:5 | 6pt |
| Join & Transcribe (split) | joinRecord | White, 10pt semi | H:10 V:5 + chevron 6pt | 5pt (split left/right) |
| Join & Transcribe (inline) | joinRecord | White, 10pt medium | H:8 V:4 | 5pt |
| Disabled | surfacePrimary | textTertiary | H:8 V:5 | 6pt |
Disabled state: no border, not-allowed cursor, tooltip explains why. Used for actions unavailable on the current system (e.g. a model requiring a newer macOS) — see Models tab, issue #479.
Review deployment timelines and the next release.
Capture decisions and follow-up work.
| State | Background | Separation | Radius |
|---|---|---|---|
| Default | Transparent | 1pt inset bottom divider | 6pt |
| Selected meeting | Accent at 15% | 1pt inset bottom divider | 6pt |
| Hover | backgroundHover | 1pt inset bottom divider | 6pt |
Meeting rows use 16pt padding; timeline/dictation rows use 20pt horizontal and 16pt vertical padding. Long dictations show a three-line preview with Show more / Show less. Copy always includes the complete text. Card outlines remain on statistics and grouped settings, where they establish a useful boundary.
Models tab: a model gated to a newer macOS than the current system is marked Incompatible — the title, status badge, description text, Download/Set Active button, and any language picker all read as disabled (textTertiary), with an inline reason instead of a doomed download attempt (issue #479). No progress bar is ever shown for this state. The Incompatible badge takes priority even when the model happens to be downloaded or active already (e.g. downloaded on a newer macOS, then this system was downgraded).
Top-right floating notification for upcoming and in-progress calendar meetings.
| Property | Value |
|---|---|
| Size | 360 × 70pt |
| Position | Top-right of screen containing mouse cursor, 16pt margin |
| Window level | CGShieldingWindowLevel() + 1 (above fullscreen apps) |
| Auto-dismiss | 15s (default), 30s for "starting now" |
| Progress bar | 3pt blue bar at bottom, drains left-to-right over dismiss duration |
| Fade in | 0.25s alpha 0→1 |
| Fade out | 0.2s alpha 1→0 |
| Platform icons | Zoom (PNG), Google Meet (PNG) — 26×26pt, 14pt left offset |
| Chevron dropdown | NSMenu with "Join Only" + "Transcribe Only" — darker green (#2a9e74), 24pt wide |
| No URL fallback | Single blue "Start Transcribing" button (126×30pt) |
| Property | Value |
|---|---|
| Background | joinRecord (#33B888) |
| Text | White, 10pt medium |
| Padding | H:8 V:4 |
| Radius | 5pt |
| Visibility | Hidden when isMeetingRecording |
| Action | joinAndRecord(title:meetingURL:endDate:) |
Consistent easing and timing across all interactive elements.
| Context | Duration | Easing |
|---|---|---|
| Hover state changes (buttons, cards) | 0.15s | easeInOut |
| Indicator state transitions | 0.16–0.18s | easeInEaseOut |
| Onboarding page navigation | 0.20s | easeInOut |
| Permission grant animation | 0.25s | easeInOut |
| Waveform pulse cycle | 0.60s | easeInEaseOut |
| Stat number transitions | System default | .numericText() |
| Element | Click | Right-click | Option+click | Drag |
|---|---|---|---|---|
| Indicator (idle) | — | — | — | Reposition |
| Indicator (recording) | Stop & save | — | — | Reposition |
| Indicator (toggle) | Left=discard, Right=save | Discard | Discard | Reposition |
| Indicator (meeting) | Stop & save | Discard confirm | Discard confirm | Reposition |
| Meeting card | Select | Move to folder | — | — |
| Folder row | Filter | Rename / Delete | — | — |
A stable global sidebar with page-specific content. Meeting details retain their compact layout below 600pt when a meeting is open.
| Property | Value |
|---|---|
| Default size | 1120 × 790 |
| Minimum size | 520 × 600pt |
| Sidebar width | 240pt expanded / 68pt collapsed |
| Meeting list pane | 280–380pt |
| Detail pane | Flexible (fills remaining) |
Calendar events grouped by day in the meetings browser. Shown when upcomingCalendarEvents is non-empty.
| Property | Value |
|---|---|
| Container | backgroundRaised, 12pt radius, 1pt surfaceBorder, 20pt padding |
| Header | "Coming Up" — Cormorant Garamond 22pt medium |
| Date column | 60pt wide — day number (24pt light), label (11pt medium), weekday (10pt) |
| Event row | 3pt accent bar (today) or 40% secondary bar · title 13pt medium · time 11pt secondary |
| Join & Record | Green button, shown when meetingURL present and not recording |
| Add to folder | Menu button, 10pt medium, surfacePrimary background |
| Google Calendar link | "Add Google to macOS Calendar for real-time sync" — accent color, opens System Settings > Internet Accounts |
| Property | Value |
|---|---|
| Screen selection | NSEvent.mouseLocation → NSMouseInRect against all screens |
| Position | Top-right of screen.visibleFrame, 16pt margin from edges |
| Collection behavior | .canJoinAllSpaces, .fullScreenAuxiliary, .stationary |
| Window level | CGShieldingWindowLevel() + 1 |
| Borderless | No shadows on content, opaque false, clear background |
| Item | Action |
|---|---|
| Open muesli | Show dashboard window |
| Next meeting countdown | Title + time until start (updates every 30s) |
| Upcoming Meetings → | Submenu with today's events, start recording from menu |
| Start/Stop Meeting Recording | Toggle meeting session |
| Discard Recording... | Abandon with confirmation (shown only when recording) |
| Recent Dictations → | Submenu with last 10 |
| Recent Meetings → | Submenu with last 5 |
| STT Backend → | Model selection submenu (dictation) |
| Meeting Backend → | Model selection submenu (meetings, independent) |
| Meeting Summary → | OpenAI / OpenRouter / ChatGPT |
| Settings... | Open settings tab |
| Check for Updates... | Sparkle update check |
| Quit | Terminate app |
Current PR #584 reference. Values follow the native components; web examples illustrate geometry and hierarchy rather than reproducing AppKit rendering.
Stack the icon, value, and caption vertically, all centered within each card. Use 8pt between items, 16pt card gaps, 16pt inner padding, 10pt corners, and 1pt surface borders. Icons use 20pt SF Symbols in the app. Values use 20pt semibold monospaced digits; captions use 12pt secondary text. The cards open their corresponding Insights section. The native four-column layout still needs a narrow-window adaptation; horizontal scrolling here only preserves the desktop specimen.
Useful content has the strongest contrast. Meeting notes and transcripts use 15pt text in a centered 720pt maximum column, with the text itself left-aligned.
Long dictation previews expand in place. Copy and history actions continue to work on the full record, regardless of the preview state.
Page edges use 32pt insets. Source filters stay left-aligned with the list and Apps filter; labels stay centered inside their segments. Recording-indicator choice titles, captions, and the notch explanatory text are centered under the previews.
Default is adaptive blue: #6BA3F7 in dark mode, #2563EB in light mode. Its stored legacy value, 1e1e2e, is a sentinel, never a dark swatch. Custom colors use their literal hex. A white checkmark plus ring indicates selection; accessibility announces the name and selected state.
Insights uses a large native bordered-prominent Share button, tinted with the chosen accent. Always show both the icon and the Share label. At narrow widths, move actions below the range picker instead of compressing the label. Disable sharing until a snapshot loads; activating it opens the existing anonymous-image preview.
Sync uses subtle accent fill without an enclosing card. Account status and Sign Out are separate. Destructive maintenance actions keep red labels and confirmation, but use compact controls with quiet borders. These specimens are non-interactive; they do not perform account or data actions.
Production uses an image-only SF Symbol xmark at 10pt medium in a 22pt circular NSButton. Do not use a text multiplication sign: its baseline shifts the mark down. The accessibility label and tooltip are Dismiss. Native 1×/2× rendering was verified; this SVG is only a browser specimen.
| Region | Behavior |
|---|---|
| Camera gap | Uses detected hardware geometry; never shifted to center the asymmetric panel. The example uses 192pt. |
| Left / right wings | Normally 110pt / 78pt. Height remains the display's top inset. Stop and Cancel retain 20pt and 22pt widths. |
| Waveform | Shrinks to remaining width before controls; right padding is 6pt per side, gaps 4pt. Instruction disclosure moves left. |
| Clearance | 32pt less coverage at the usual right edge. This is a fixed first pass, not live detection of the system mic indicator. Other display and menu-bar layouts need validation. |
Center the expanded panel beneath the visible asymmetric bar, 16pt left of the camera center for the usual 110pt/78pt wings. Keep the camera gap fixed. Instruction text uses 13pt; up to three recent tool summaries use 10pt and at most two lines each. The panel starts at 100pt and adds 30pt per response, to 190pt. Summaries describe the operation and outcome (for example, “Read page text”); full responses, observations, and tool arguments remain in Timeline.
Can you open Twitter on Google Chrome?
✓ Opened app
✓ Navigated to page
Provider marks occupy a consistent 32×32pt slot. Apple and the existing OpenAI vector use a 23pt primary-color mark; other provider assets use a 24pt image on a white 32pt badge with 8pt corners. Preserve original brand colors. Social sharing moves from the sidebar to About, retaining the existing word-milestone eligibility.
App/model rankings count one saved, non-deleted dictation record as one session within the selected date range, including synced dictations. Duration and streaming chunks do not multiply the count. Meetings, Quill, and computer-use records are excluded from these rankings. Rank by session count, then word count.
Known counting inconsistency: the overview's Dictation sessions total uses the broader history cache, which still includes saved Quill/computer-use records. This reference documents current behavior; this PR does not change the aggregation policy.
muesli design language system · PR #584 · October 8, 2026
Source of truth: MuesliTheme.swift · FloatingIndicatorController.swift · SidebarView.swift