Files
SoulStream/src/mobile/mobile.css
T
KeelrLeek 9df162360e android: the phone turned sideways — the player beside itself, the chrome cut down
The phone UI was drawn portrait-first and had never been looked at in
landscape. The player sheet was the casualty: its cover is a square sized by
width, so at 915×411 it came out 879px tall and the title, the scrubber, the
transport and the face buttons all sat below the bottom of a sheet that does
not scroll — a corner of the artwork and no way to pause. Sideways the sheet
is two columns now, the cover sized by height beside the controls, with a
lyrics or queue face taking the cover's column; the nav and the mini bar come
down to 48 and 60dp, the artist hero becomes the banner the desktop's hero has
always been rather than 300px of a 411px screen, the record head puts the
cover beside the title, and the shell reads the side insets the cutout and the
gesture pill take when the phone is on its side. The walk turns the phone over
and back on its own: `{ view: [w, h] }` is a step kind now, and the steps
assert what used to be off the screen.
2026-09-14 00:14:23 -05:00

147 lines
7.6 KiB
CSS

/* ═══════════════════════════════════════════════════════════════════════════
SoulStream on Android — foundations.
The palette is the desktop palette, unchanged (global.css / the mockup's
tokens.css): same greys, same accent, same surface ladder. What is new is
spatial — radii open up, type goes up a step, every tappable thing is 48dp
— and the material: the page ground is the artwork in play, blurred.
Text is authored in px; the Android WebView applies the system font scale
to CSS px on its own, so there is no sp arithmetic here.
═══════════════════════════════════════════════════════════════════════════ */
/* the desktop's face, from the same files */
@font-face { font-family: "Nunito"; font-style: normal; font-weight: 400; font-display: swap; src: url("/assets/fonts/nunito-400.woff2") format("woff2"); }
@font-face { font-family: "Nunito"; font-style: normal; font-weight: 600; font-display: swap; src: url("/assets/fonts/nunito-600.woff2") format("woff2"); }
@font-face { font-family: "Nunito"; font-style: normal; font-weight: 700; font-display: swap; src: url("/assets/fonts/nunito-700.woff2") format("woff2"); }
/* Typed registration so the per-artwork accent can TRANSITION. The desktop
crossfades its accent on a rAF loop (state.svelte.js); on the phone the
compositor does it — see the transition on html below. */
@property --accent { syntax: "<color>"; inherits: true; initial-value: #1f9ad6; }
@property --accent-hover { syntax: "<color>"; inherits: true; initial-value: #41b7ee; }
:root {
--bg-base: #121212;
--bg-elevated: #181818;
--bg-highlight: #242424;
--bg-press: #2a2a2a;
--text-primary: #ffffff;
--text-secondary: #b3b3b3;
--text-muted: #7a7a7a;
--accent: #1f9ad6;
--accent-hover: #41b7ee;
--accent-soft: color-mix(in srgb, var(--accent) 15%, transparent);
--border: #2a2a2a;
/* the surface ladder: the same three alphas the desktop's glass mode
uses, because the ground under a surface is the ambience here */
--surface: rgba(255, 255, 255, 0.03);
--hover: rgba(255, 255, 255, 0.08);
--press: rgba(255, 255, 255, 0.11);
--hairline: rgba(255, 255, 255, 0.1);
--ring: inset 0 0 0 1px rgba(255, 255, 255, 0.06);
--well: rgba(0, 0, 0, 0.18);
--r-art: 10px;
--r-card: 14px;
--r-sheet: 26px;
--r-pill: 999px;
--pad: 18px;
--gap: 10px;
--nav-h: 60px;
--mini-h: 70px; /* a 62dp row over a 16dp lane strip */
--bar-h: 52px; /* the glass bar */
--tap: 48px;
/* Android's own insets: the status bar above, the gesture handle below. */
--inset-top: env(safe-area-inset-top, 0px);
--inset-bottom: env(safe-area-inset-bottom, 0px);
/* landscape moves the cutout and the gesture pill to the SIDES, and
viewport-fit=cover puts our content under both */
--inset-left: env(safe-area-inset-left, 0px);
--inset-right: env(safe-area-inset-right, 0px);
--top-h: calc(var(--bar-h) + var(--inset-top));
/* ── motion: real springs, sampled ────────────────────────────────────
Material 3 Expressive specifies motion as springs; CSS takes a sampled
curve through linear(). These are the mockup's generated tokens
(research/mobile-mockup/tools/springs.mjs): the spatial ones overshoot
because real objects do, the effects ones never do. */
--sp-spatial-fast: linear(0, 0.0512, 0.176, 0.3375, 0.5079, 0.6679, 0.8057, 0.9155, 0.9963, 1.0502, 1.081, 1.0937, 1.0931, 1.0838, 1.0696, 1.0535, 1.0378, 1.0237, 1.0121, 1.0033, 0.9971, 0.9933, 0.9915, 0.991, 0.9916, 0.9928, 0.9943, 0.9958, 0.9972, 0.9984, 0.9994, 1, 1.0005, 1.0008, 1);
--spd-spatial-fast: 413ms;
--sp-spatial-default: linear(0, 0.0329, 0.1132, 0.2191, 0.335, 0.4503, 0.558, 0.6543, 0.7374, 0.8069, 0.8634, 0.9081, 0.9425, 0.9681, 0.9865, 0.9992, 1.0074, 1.0122, 1.0145, 1.0152, 1.0146, 1.0134, 1.0118, 1.01, 1.0083, 1.0067, 1.0052, 1.0039, 1.0029, 1.002, 1.0014, 1.0009, 1.0005, 1.0002, 1);
--spd-spatial-default: 482ms;
--sp-spatial-slow: linear(0, 0.0309, 0.1068, 0.2078, 0.3194, 0.4314, 0.5372, 0.6329, 0.7165, 0.7874, 0.8459, 0.893, 0.9298, 0.9579, 0.9787, 0.9935, 1.0035, 1.0098, 1.0134, 1.0149, 1.0151, 1.0143, 1.013, 1.0114, 1.0097, 1.008, 1.0065, 1.0051, 1.0039, 1.0029, 1.002, 1.0014, 1.0009, 1.0005, 1);
--spd-spatial-slow: 641ms;
--sp-effects-fast: linear(0, 0.0527, 0.1672, 0.3005, 0.4304, 0.5464, 0.6448, 0.7255, 0.7901, 0.8409, 0.8803, 0.9105, 0.9334, 0.9507, 0.9637, 0.9733, 0.9804, 0.9857, 0.9896, 0.9924, 0.9945, 0.996, 0.9971, 0.9979, 0.9985, 0.9989, 0.9992, 0.9994, 0.9996, 0.9997, 0.9998, 0.9999, 0.9999, 0.9999, 1);
--spd-effects-fast: 202ms;
--sp-effects-default: linear(0, 0.0436, 0.1412, 0.2593, 0.3786, 0.4892, 0.5866, 0.6696, 0.7385, 0.7947, 0.84, 0.876, 0.9044, 0.9266, 0.9439, 0.9572, 0.9675, 0.9754, 0.9814, 0.986, 0.9894, 0.9921, 0.9941, 0.9956, 0.9967, 0.9975, 0.9982, 0.9986, 0.999, 0.9992, 0.9994, 0.9996, 0.9997, 0.9998, 1);
--spd-effects-default: 280ms;
--sp-effects-slow: linear(0, 0.0388, 0.1273, 0.2365, 0.3492, 0.4558, 0.5517, 0.635, 0.7056, 0.7644, 0.8128, 0.8521, 0.8837, 0.9089, 0.929, 0.9448, 0.9572, 0.9669, 0.9745, 0.9804, 0.9849, 0.9885, 0.9912, 0.9933, 0.9949, 0.9961, 0.997, 0.9977, 0.9983, 0.9987, 0.999, 0.9993, 0.9994, 0.9996, 1);
--spd-effects-slow: 371ms;
--m-fast: var(--spd-effects-fast) var(--sp-effects-fast);
--m-base: var(--spd-spatial-fast) var(--sp-spatial-fast);
--m-sheet: var(--spd-spatial-default) var(--sp-spatial-default);
--m-spring: var(--spd-spatial-fast) var(--sp-spatial-fast);
--art-grow: 1.035;
--ambience: 0.55; /* how much of the artwork the page ground admits */
--grain: 0.05;
color-scheme: dark;
}
/* The registered property only makes --accent animatable; this is the
crossfade. Slow on purpose — it is the app changing colour, and it should
read as a change rather than a flicker. */
html {
transition:
--accent var(--spd-effects-slow) linear,
--accent-hover var(--spd-effects-slow) linear;
}
/* Appearance → Accent from artwork OFF: the brand blue everywhere */
html[data-accent="brand"] { --accent: #1f9ad6 !important; --accent-hover: #41b7ee !important; }
/* Appearance → Ambience OFF: the honest floor, the desktop's measured greys */
html[data-material="solid"] {
--ambience: 0;
--surface: var(--bg-elevated);
--hover: var(--bg-highlight);
--press: var(--bg-press);
--hairline: rgba(255, 255, 255, 0.07);
}
* { box-sizing: border-box; }
html, body {
margin: 0;
padding: 0;
height: 100%;
background: var(--bg-base);
color: var(--text-primary);
font-family: "Nunito", -apple-system, "Segoe UI", Roboto, sans-serif;
font-size: 15px;
-webkit-font-smoothing: antialiased;
-webkit-tap-highlight-color: transparent;
overscroll-behavior: none;
user-select: none;
-webkit-user-select: none;
}
#mobile { height: 100%; }
/* :where() so the reset carries zero specificity and never beats a
component rule (the mockup lost every accent-filled control to this once) */
:where(button) {
font: inherit;
color: inherit;
background: none;
border: 0;
padding: 0;
margin: 0;
cursor: default;
touch-action: manipulation;
}
:where(input) { font: inherit; color: inherit; }
img { display: block; }