Deck chrome: neutral surfaces, borders and text (no navy leftovers)

- The study feed's progress header and bottom pager no longer use the old
  navy glass: new .study-chrome class uses the neutral surface token
  (same gray as the cards, #2f2f2f in dark), the neutral border
  (8% white in dark, #e5e5e5 in light) and a neutral shadow.
- Close tile, streak pill and XP pill now use --surface-sunken /
  --text-muted, matching the app's other buttons and chips; the close
  glyph is the shared XIcon.
- Bottom pager: counter in --text, arrows in --text-muted.
- .glass (mobile header) no longer has the hardcoded #101720 navy; it is
  now color-mix of the surface token. Removed the unused --color-night-*
  tokens and the last night- usage (create screen sticky bar).
- Hidden the 'Swipe up' hint on desktop, where it collided with the
  bottom pager.

New deck-chrome suite: 15/15 (computed surfaces/borders/text in both
themes, no navy); pwa 16/16, final-ux 12/12, security 8/8, contrast 20/20.
This commit is contained in:
Martin Dvořák 2026-10-07 09:06:18 +00:00
commit 9e4ecd3ee0
3 changed files with 23 additions and 20 deletions

View file

@ -35,12 +35,6 @@
--color-aurora-lilac: #c3b6f0;
--color-aurora-peach: #f6cdb4;
--color-night-50: #f6f9fc;
--color-night-700: #1d2733;
--color-night-800: #151d27;
--color-night-900: #101720;
--color-night-950: #0a1017;
--font-sans: -apple-system, BlinkMacSystemFont, ui-sans-serif, system-ui,
"Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif,
"Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji";
@ -302,11 +296,20 @@ body {
/* Tailwind emits both the prefixed and standard properties; hand-written
backdrop-filter gets minified down to -webkit- only, which paints nothing. */
@apply backdrop-blur-xl backdrop-saturate-150;
background: color-mix(in oklab, white 68%, transparent);
background: color-mix(in oklab, var(--surface) 70%, transparent);
border-bottom: 1px solid var(--hairline);
}
.dark .glass {
background: color-mix(in oklab, #101720 70%, transparent);
/* Study-feed chrome (progress header + bottom pager): the same neutral
surface as the cards, with a neutral border and shadow. */
.study-chrome {
@apply backdrop-blur-xl backdrop-saturate-150;
background: color-mix(in oklab, var(--surface) 88%, transparent);
border: 1px solid var(--hairline);
box-shadow: var(--shadow-card);
}
.dark .study-chrome {
border-color: var(--hairline-soft);
}
.card {

View file

@ -9,7 +9,7 @@ import { celebrate } from "@/lib/confetti";
import type { Activity, Deck } from "@/lib/schemas";
import { recordAttempt } from "@/lib/storage";
import { cn } from "@/lib/utils";
import { BoltIcon, FlameIcon } from "@/components/icons";
import { BoltIcon, FlameIcon, XIcon } from "@/components/icons";
import { Button, ProgressBar } from "@/components/ui";
import { ResultsScreen } from "./ResultsScreen";
@ -147,14 +147,14 @@ export function StudyFeed({ deck }: { deck: Deck }) {
<div className="frost-bg fixed inset-0 flex flex-col">
{/* Chrome ---------------------------------------------------- */}
<header className="pt-safe absolute inset-x-0 top-0 z-30 px-3">
<div className="glass mx-auto max-w-xl space-y-1.5 rounded-2xl px-3 py-2">
<div className="study-chrome mx-auto max-w-xl space-y-1.5 rounded-2xl px-3 py-2">
<div className="flex items-center gap-3">
<Link
href="/"
aria-label="Back to your library"
className="grid size-9 shrink-0 place-items-center rounded-xl bg-white/80 text-sm font-bold text-frost-800 transition hover:bg-white dark:bg-white/10 dark:text-frost-100"
className="grid size-9 shrink-0 place-items-center rounded-xl bg-[var(--surface-sunken)] text-[var(--text)] transition hover:bg-[var(--hover)]"
>
✕
<XIcon className="size-4" />
</Link>
<div className="min-w-0 flex-1">
<ProgressBar
@ -169,7 +169,7 @@ export function StudyFeed({ deck }: { deck: Deck }) {
"inline-flex items-center gap-1 rounded-xl px-2 py-1 text-xs font-extrabold transition",
streak >= 3
? "tint-peach"
: "glass-chip text-frost-600 dark:text-frost-200",
: "bg-[var(--surface-sunken)] text-[var(--text-muted)]",
)}
title={`Answer streak: ${streak}`}
aria-label={`Answer streak: ${streak}`}
@ -178,7 +178,7 @@ export function StudyFeed({ deck }: { deck: Deck }) {
{streak}
</span>
<span
className="inline-flex items-center gap-1 rounded-xl bg-frost-500/15 px-2 py-1 text-xs font-extrabold text-frost-800 dark:text-frost-100"
className="inline-flex items-center gap-1 rounded-xl bg-[var(--surface-sunken)] px-2 py-1 text-xs font-extrabold text-[var(--text-muted)]"
title={`XP earned: ${xp}`}
aria-label={`XP earned: ${xp}`}
>
@ -187,7 +187,7 @@ export function StudyFeed({ deck }: { deck: Deck }) {
</span>
</div>
</div>
<div className="flex items-center justify-between gap-2 text-[11px] font-semibold text-frost-600 dark:text-frost-300">
<div className="flex items-center justify-between gap-2 text-[11px] font-semibold text-[var(--text-muted)]">
<span className="min-w-0 truncate">{deck.title}</span>
<span className="shrink-0">
{retryMode ? "Retry · " : ""}
@ -250,7 +250,7 @@ export function StudyFeed({ deck }: { deck: Deck }) {
initial={{ opacity: 0, y: 12 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0 }}
className="pointer-events-none absolute inset-x-0 bottom-6 z-20 flex justify-center"
className="pointer-events-none absolute inset-x-0 bottom-6 z-20 flex justify-center sm:hidden"
>
<span className="rounded-full bg-frost-950/70 px-4 py-2 text-xs font-semibold text-white backdrop-blur">
Swipe up for the next activity ↑
@ -261,11 +261,11 @@ export function StudyFeed({ deck }: { deck: Deck }) {
{/* Desktop next/prev buttons --------------------------------- */}
<div className="pointer-events-none absolute inset-x-0 bottom-0 z-20 hidden justify-center pb-4 sm:flex">
<div className="glass pointer-events-auto flex items-center gap-2 rounded-2xl px-2 py-1.5">
<div className="study-chrome pointer-events-auto flex items-center gap-2 rounded-2xl px-2 py-1.5">
<Button variant="ghost" size="sm" onClick={() => goTo(index - 1)} disabled={index === 0} aria-label="Previous activity">
↑
</Button>
<span className="px-2 text-xs font-semibold text-frost-600 dark:text-frost-300">
<span className="px-2 text-xs font-semibold text-[var(--text)]">
{index + 1} / {slideCount}
</span>
<Button variant="ghost" size="sm" onClick={() => goTo(index + 1)} disabled={index >= slideCount - 1} aria-label="Next activity">

View file

@ -719,7 +719,7 @@ export function UploadScreen() {
</div>
</section>
<div className="sticky bottom-[calc(env(safe-area-inset-bottom,0px)+4.6rem)] z-20 -mx-4 border-t border-[var(--hairline)] bg-white/65 px-4 py-3 backdrop-blur-2xl dark:bg-night-950/70 sm:bottom-0 sm:mx-0 sm:rounded-[1.35rem] sm:border sm:px-5">
<div className="sticky bottom-[calc(env(safe-area-inset-bottom,0px)+4.6rem)] z-20 -mx-4 border-t border-[var(--hairline)] bg-white/65 px-4 py-3 backdrop-blur-2xl dark:bg-[color-mix(in_oklab,var(--surface)_70%,transparent)] sm:bottom-0 sm:mx-0 sm:rounded-[1.35rem] sm:border sm:px-5">
<div className="flex flex-wrap items-center justify-between gap-3">
<p className="text-[12.5px] text-[var(--text-subtle)]">
{documentsBusy