@import "tailwindcss"; /* ─────────────────────────────────────────────────────────────── muse design system Governing rule: NEUTRAL CHROME, COLOUR IN CONTENT. The previous system sampled the album artwork and repainted the whole interface from it. That is the inverse of how a native application behaves -- Music, Mail and Finder keep their chrome grey and let the artwork be the only saturated thing on screen -- and it is most of why this read as a themed web page rather than an app. So there are two colours now: --accent fixed. Controls, selection, focus rings. Never sampled. --art-r/g/b sampled from the cover. Hero washes and now-playing only. That split also closes a real bug: the focus ring used to be drawn in the sampled colour, so its contrast against the page depended on which album you happened to be looking at. ─────────────────────────────────────────────────────────────── */ @theme inline { --color-accent: var(--accent); --color-accent-soft: var(--accent-soft); --color-accent-line: var(--accent-line); --color-accent-ink: var(--accent-ink); --color-art: rgb(var(--a-r) var(--a-g) var(--a-b)); --color-art-soft: rgb(var(--a-r) var(--a-g) var(--a-b) / 0.16); --color-ink: var(--fg); --color-dim: var(--dim); --color-mute: var(--mute); --color-edge: var(--edge); --color-edge-hi: var(--edge-hi); --color-faint: var(--faint); /* Opaque surfaces. Content sits on a real surface and vibrancy is reserved for the sidebar and toolbar; previously `glass` was on everything, which is why nothing read as layered. */ --color-surface: var(--surface); --color-surface-2: var(--surface-2); --color-surface-3: var(--surface-3); --color-ok: var(--ok); --color-err: var(--err); --color-warn: var(--warn); --radius-control: 8px; --radius-card: 12px; --radius-panel: 18px; --radius-sheet: 22px; --radius-pill: 999px; /* One shared curve, so unrelated components feel like one physical system. */ --ease-out: cubic-bezier(0.22, 0.9, 0.28, 1); --ease-spring: linear( 0, 0.006, 0.025, 0.101, 0.539, 0.721, 0.849, 0.937, 0.968, 0.991, 1 ); /* System faces on purpose. next/font self-hosts a webfont, which would make the Docker build reach out to Google, and `font-src 'self'` in the CSP blocks a CDN outright. -apple-system resolves to SF Pro on the devices this is used from, which is the target anyway. */ --font-sans: -apple-system, BlinkMacSystemFont, "SF Pro Text", ui-sans-serif, system-ui, "Segoe UI", Roboto, sans-serif; --font-display: -apple-system, BlinkMacSystemFont, "SF Pro Display", ui-sans-serif, system-ui, "Segoe UI", Roboto, sans-serif; --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace; /* Type scale. Tracking tightens as size grows -- display text at default tracking looks loose, small text at display tracking looks cramped. */ --text-hero: 2.75rem; --text-hero--line-height: 1.05; --text-hero--letter-spacing: -0.038em; --text-hero--font-weight: 700; --text-display: 2.125rem; --text-display--line-height: 1.08; --text-display--letter-spacing: -0.033em; --text-display--font-weight: 700; --text-title: 1.4375rem; --text-title--line-height: 1.18; --text-title--letter-spacing: -0.024em; --text-title--font-weight: 700; --text-headline: 1.0625rem; --text-headline--line-height: 1.35; --text-headline--letter-spacing: -0.014em; --text-headline--font-weight: 600; --text-body: 0.9375rem; --text-body--line-height: 1.5; --text-body--letter-spacing: -0.008em; --text-callout: 0.8125rem; --text-callout--line-height: 1.45; --text-callout--letter-spacing: -0.003em; --text-caption: 0.6875rem; --text-caption--line-height: 1.35; --text-caption--letter-spacing: 0.005em; /* Named layout constants. These were arbitrary literals repeated across files: max-w-[940px] twice, gap-[7px] five times. */ --spacing-row: 7px; --container-3xl: 1280px; --sidebar-w: 240px; --rail-w: 68px; --toolbar-h: 52px; } :root { color-scheme: light dark; /* Default artwork tint, used until a cover is sampled. */ --a-r: 120; --a-g: 140; --a-b: 255; --bg: #0a0b0f; --surface: #121319; --surface-2: #181a22; --surface-3: #20232c; --fg: #f4f6fb; --dim: #a0a8bb; --mute: #6d7488; --faint: rgb(255 255 255 / 0.06); --edge: rgb(255 255 255 / 0.09); --edge-hi: rgb(255 255 255 / 0.16); /* Vibrancy, for sidebar and toolbar only. */ --material: rgb(18 19 25 / 0.72); --material-hi: rgb(24 26 34 / 0.86); --blur: saturate(180%) blur(20px); --scrim: rgb(10 11 15 / 0.76); --scrim-2: rgb(10 11 15 / 0.35); /* Fixed accent. Apple's system blue, which stays legible on both grounds and is independent of whatever is playing. */ --accent: #0a84ff; --accent-soft: rgb(10 132 255 / 0.16); --accent-line: rgb(10 132 255 / 0.45); --accent-ink: #ffffff; --ok: #30d158; --err: #ff453a; --warn: #ff9f0a; /* The specular edge. A real frosted panel catches light along its top rim; without this, blur alone reads as a flat grey rectangle. */ --spec: inset 0 1px 0 rgb(255 255 255 / 0.07); --spec-hi: inset 0 1px 0 rgb(255 255 255 / 0.12); /* Elevation. Two shadows each: a tight contact shadow that anchors the element and a wide soft one for the light. One alone always looks fake. */ --e1: 0 1px 2px rgb(0 0 0 / 0.3), 0 4px 12px rgb(0 0 0 / 0.16); --e2: 0 2px 6px rgb(0 0 0 / 0.34), 0 12px 32px rgb(0 0 0 / 0.26); --e3: 0 6px 16px rgb(0 0 0 / 0.38), 0 28px 68px rgb(0 0 0 / 0.4); } @media (prefers-color-scheme: light) { :root { --bg: #f6f7f9; --surface: #ffffff; --surface-2: #f2f3f6; --surface-3: #e9ebf0; --fg: #0d1117; --dim: #596273; --mute: #858da0; --faint: rgb(10 14 25 / 0.05); --edge: rgb(10 14 25 / 0.1); --edge-hi: rgb(10 14 25 / 0.18); --material: rgb(246 247 249 / 0.72); --material-hi: rgb(255 255 255 / 0.88); --scrim: rgb(255 255 255 / 0.8); --scrim-2: rgb(255 255 255 / 0.45); --accent: #0071e3; --accent-soft: rgb(0 113 227 / 0.12); --accent-line: rgb(0 113 227 / 0.4); --accent-ink: #ffffff; /* Darkened to clear 4.5:1 on the light ground. Same hues, so a status still reads as the same status when the theme flips. */ --ok: #0a7d43; --err: #c62828; --warn: #8a5a00; /* On light material the specular rim is near-white and the shadows have to lose most of their weight, or every card looks like it is hovering. */ --spec: inset 0 1px 0 rgb(255 255 255 / 0.9); --spec-hi: inset 0 1px 0 rgb(255 255 255 / 1); --e1: 0 1px 2px rgb(20 30 60 / 0.06), 0 4px 12px rgb(20 30 60 / 0.05); --e2: 0 2px 6px rgb(20 30 60 / 0.07), 0 12px 32px rgb(20 30 60 / 0.08); --e3: 0 6px 16px rgb(20 30 60 / 0.09), 0 28px 68px rgb(20 30 60 / 0.12); } } @layer base { body { background: var(--bg); color: var(--fg); font-family: var(--font-sans); font-size: 15px; line-height: 1.55; letter-spacing: -0.008em; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-rendering: optimizeLegibility; overflow-x: hidden; } /* Headings take the display face and its tighter tracking automatically, so a component never has to remember to ask for it. */ h1, h2, h3 { font-family: var(--font-display); text-wrap: balance; } /* Ragged-right paragraphs with one orphan word are the giveaway of an interface nobody typeset. */ p { text-wrap: pretty; } button, input, select, textarea { font: inherit; letter-spacing: inherit; color: inherit; } ::selection { background: var(--accent-soft); } input[type="search"]::-webkit-search-cancel-button { display: none; } } /* Vibrancy, for chrome that sits over scrolling content. */ @utility material { background: var(--material); backdrop-filter: var(--blur); -webkit-backdrop-filter: var(--blur); } @utility material-hi { background: var(--material-hi); backdrop-filter: var(--blur); -webkit-backdrop-filter: var(--blur); } /* An opaque content surface. The default for cards, rows and panels. */ @utility card { background: var(--surface); border: 1px solid var(--edge); box-shadow: var(--spec), var(--e1); } @utility sheet { background: var(--surface); border: 1px solid var(--edge-hi); box-shadow: var(--spec-hi), var(--e3); } /* A card that lifts on hover. The translate is tiny on purpose: Apple's hover feedback is barely perceptible frame to frame and obvious in aggregate. Anything larger reads as a web page, not an interface. */ @utility lift { transition: transform 0.28s var(--ease-out), box-shadow 0.28s var(--ease-out), border-color 0.28s var(--ease-out), background 0.28s var(--ease-out); &:hover { transform: translateY(-2px); box-shadow: var(--spec-hi), var(--e2); border-color: var(--edge-hi); } &:active { transform: translateY(0) scale(0.994); transition-duration: 0.06s; } } /* ONE focus treatment for the whole app. There used to be two that looked nothing alike -- an outline utility on ~12 elements and a box-shadow ring on buttons -- so keyboard focus changed shape depending on what you tabbed to. */ @utility focus-ring { &:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: inherit; } } /* Numbers that change in place (counts, durations, percentages) must not reflow the text around them as digits swap. */ @utility nums { font-variant-numeric: tabular-nums; font-feature-settings: "tnum"; } @utility clamp1 { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } @utility clamp2 { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; } @utility no-scrollbar { scrollbar-width: none; &::-webkit-scrollbar { display: none; } } /* Keyframes live here rather than in a