Frosted ice glass restyle
Rebuilds the look around the spec: an ice scene, one shared glass recipe, icy accents and glass on every large surface. Visual only — layout, behaviour, copy and functionality are untouched. Background - .ice-scene: fixed, z-index -1, pointer-events none. Navy base (#070b16 to #0c1426) in dark, pale (#f4f9ff to #e6f1fb) in light, four drifting glacier blobs (#38bdf8/#60a5fa/#818cf8, 30-60s transform-only loops), five glossy ice orbs (40-220px radial gradients with a highlight at 30% 25% and a blue shadow) placed so the glass blurs them, and an SVG feTurbulence grain at 4-5%. - prefers-reduced-motion stops the drift; phones get two blobs and two orbs fewer. Glass - One shared recipe (.glass-panel, .card, .sidebar-surface, .study-chrome, .tabbar, .glass, .gate-card): rgba(255,255,255,.06) dark / .55 light, blur(24px) saturate(140%) (16px on phones), a bright 1px rim, an inset top highlight and a blue-tinted outer shadow. - Nested glass (chips, inputs, secondary buttons) is more opaque and never adds a second blur layer. @supports fallback for no backdrop-filter. Radii: 24-28px cards/sheets, 14px inputs/buttons, pills for chips. Cards hover brighter + cyan rim + 2px lift, 180ms. Colour and type - Accent is glacier cyan (#38bdf8) to ice blue (#0ea5e9); progress bars and selected states use the gradient; the active sidebar item is a frosted highlight with a cyan rim. - Primary CTA: icy gradient — white to #bfe3ff with ink text in dark, deep blue in light (biased so the label sits on the darker half and keeps 5.6:1). - Leftover slate/black greys replaced with navy-tinted frost tokens. - Headings are semibold with tighter tracking. Screens - Dashboard: the sidebar is one tall inset rounded glass panel; deck cards are glass. - Gate: one centred glass card over the scene, orbs visible behind it. - Sheets/modals: glass over the dimmed, lightly blurred backdrop. - Study view: question card more opaque (.glass-solid); top bar and pager are slim glass pills. Perf: measured on a phone viewport with 4x CPU throttling. The animated blobs' filter: blur() was the whole cost (150ms vs 33ms per frame), so they are soft radial gradients with no filter now; idle 60fps, ~17ms median while scrolling. Tests updated for the new tokens: contrast 18/18 AA, deck chrome 15/15, key gate 49/49, install sheet 64/64, deck fixes 15/15, retry 14/14, zoom 6/6, final ux 12/12, security 8/8, pwa 16/16.
This commit is contained in:
parent
586e4b5a40
commit
52cce42cde
20 changed files with 383 additions and 262 deletions
57
README.md
57
README.md
|
|
@ -112,10 +112,11 @@ no database, no data leaving your machine except the requests you send to Ollama
|
|||
list with per-folder counts, Settings/Dark mode pinned to the bottom). It **collapses** to a 68px
|
||||
icon rail (remembered in `localStorage`) and becomes a slide-in **drawer** behind a hamburger on
|
||||
phones; mobile also keeps the frosted header + iOS tab bar
|
||||
- **One navy palette pulled from the app icon, both themes**: light page `#f4f7fd` / sidebar
|
||||
`#e9eff9` / border `#dce4f1`; dark page `#0e1424` / sidebar `#0a0f1c` / surfaces `#171f33` /
|
||||
border `rgba(255,255,255,.12)`. Surfaces are frosted glass (translucent + `backdrop-blur`) over
|
||||
the icon's navy backdrop.
|
||||
- **Frosted ice glass, both themes**: light page `#eef5fd` (pale ice) with white glass panels;
|
||||
dark page `#0a1120` with `rgba(255,255,255,.06)` glass. Every large surface shares one recipe —
|
||||
translucent tint, 24px `backdrop-blur` (16px on phones), a bright 1px rim, an inner top highlight
|
||||
and a blue-tinted shadow — over a fixed ice scene (navy/pale base, drifting glacier blobs, glossy
|
||||
ice orbs and a whisper of grain).
|
||||
Text tiers are documented AA ratios (see below), and the cards fill the row with
|
||||
`repeat(auto-fill, minmax(280px, 1fr))` — no stranded cards, no dead space
|
||||
- Deck cards: title → muted metadata → neutral tags (difficulty carries the only colour:
|
||||
|
|
@ -727,10 +728,12 @@ Things I chose while building, with the reasoning:
|
|||
have no `folderId`), deleting a folder re-homes its decks instead of deleting them, and folder
|
||||
names travel in exports so an imported deck lands where it came from. 13 browser checks cover
|
||||
create → freeze → move → rename → delete → reload → import.
|
||||
18. **The visual language is glassmorphism, with restraint.** The icon's blue is the one accent
|
||||
colour driving buttons and highlights; the backdrop is a navy wash; surfaces — sidebar, cards,
|
||||
chrome, sheets — are frosted and translucent. Colour appears in activity tints and semantic
|
||||
feedback only — pretty, but still readable and quiet.
|
||||
18. **The visual language is frosted ice glass.** Glacier cyan to ice blue (`#38bdf8` → `#0ea5e9`)
|
||||
is the accent for focus rings, progress bars and selected states; the primary CTA is an icy
|
||||
gradient (deep blue in light, white-to-`#bfe3ff` in dark). The background is a fixed ice scene;
|
||||
the sidebar, cards, chrome, sheets and gate are translucent glass with a bright rim and a soft
|
||||
blue shadow. Colour appears in activity tints and semantic feedback only — pretty, but still
|
||||
readable and quiet.
|
||||
19. **Custom CSS lives in `@layer components`, and glass uses Tailwind's backdrop utilities.**
|
||||
Two build-level traps, both found by looking at real screenshots: unlayered rules beat Tailwind
|
||||
utilities (which silently broke `fixed` on the feed), and a hand-written `backdrop-filter` gets
|
||||
|
|
@ -739,21 +742,24 @@ Things I chose while building, with the reasoning:
|
|||
20. **There is no demo/sample deck.** Every activity comes from the user's own notes; the empty
|
||||
state points at the create flow and Settings instead of shipping fake content. Browser tests seed
|
||||
a fixture deck into `localStorage` rather than relying on app code.
|
||||
21. **Dark mode is token-driven, and contrast is audited.** Text colours come from
|
||||
21. **Dark mode is token-driven, and contrast is audited on glass.** Text colours come from
|
||||
`--text` / `--text-muted` / `--text-subtle` (never hardcoded), and a `contrast.mjs` audit checks
|
||||
every pairing at WCAG AA — currently **21/21 pairs pass** (e.g. dark secondary `#b7c3dc` on
|
||||
`#0e1424` = 10.4:1, white on the light blue CTA `#1d61ab` = 6.3:1). Difficulty colours were
|
||||
darkened until they cleared 4.5:1 on their pills.
|
||||
every pairing at WCAG AA against the *effective* frosted background — currently **18/18 pairs
|
||||
pass** (e.g. dark secondary `#b3c4e0` on glass `#1c2535` = 10:1, white on the light CTA
|
||||
mid-gradient `#1a5fd0` = 5.6:1). Difficulty colours were darkened until they cleared 4.5:1 on
|
||||
their pills.
|
||||
This was a real bug: an earlier edit silently failed, so `.dark` never overrode `--text`, leaving
|
||||
near-black text on a dark page.
|
||||
22. **The dashboard follows the icon's navy system.** Frosted navy/white surfaces, hairline borders,
|
||||
pill CTAs, 18px radii and restrained shadows; the sidebar, cards and chrome are translucent
|
||||
glass over the backdrop. Deck cards got real hierarchy: title → muted metadata → tags → summary
|
||||
→ secondary Study CTA, with secondary actions behind a quiet menu.
|
||||
23. **One fixed backdrop layer instead of per-page blobs.** The decorative wash used to be
|
||||
22. **The dashboard is one inset glass rail plus glass cards.** The sidebar is a single tall
|
||||
rounded glass panel inset from the viewport edges, so the ice scene shows around it; deck cards
|
||||
are 24px-radius glass panels with a secondary Study CTA and quiet metadata. Headings are
|
||||
semibold with tighter tracking. Nested glass (chips, inputs) is more opaque but never adds a
|
||||
second blur layer.
|
||||
23. **One fixed ice scene instead of per-page blobs.** The decorative background used to be
|
||||
rendered inside the content column, so on wide screens it was clipped into a visible hard edge.
|
||||
It now lives in a single `position: fixed` layer (`.app-backdrop`, `z-index: -10`) in the
|
||||
icon's navy and blue, and `body`'s colour matches its bottom so mobile overscroll shows no seam.
|
||||
It now lives in a single `position: fixed` layer (`.ice-scene`, `z-index: -1`, pointer-events
|
||||
none): base gradient, four drifting glacier blobs, five glossy ice orbs and an SVG-noise grain.
|
||||
`prefers-reduced-motion` stops the drift.
|
||||
24. **The UI was deliberately made quieter.** One accent colour (`accent-*`), a desaturated
|
||||
`frost-*` scale, hairline borders and flat surfaces replaced the saturated gradients and the
|
||||
seven-colour activity badges. Semantic colours (green/amber/red) survive only where they carry
|
||||
|
|
@ -789,10 +795,17 @@ Things I chose while building, with the reasoning:
|
|||
Desktop keeps the designed 12–14px field sizes.
|
||||
31. **The app icon drives the palette.** The navy rounded-square artwork in
|
||||
`assets/app-icon-source.jpeg` is the source of truth: `frost-*` is navy-tinted, the accent is
|
||||
the snowflake blue, the backdrop is the same navy wash, the primary CTA is the icon blue (deep
|
||||
in light mode, bright in dark), and every icon/splash/favicon is rendered from it by
|
||||
`scripts/make-icons.mjs`. The sidebar, header, key gate, empty state and install sheet all show
|
||||
glacier cyan, the ice scene uses the artwork's blues, the primary CTA is an icy gradient, and
|
||||
every icon/splash/favicon is rendered from it by `scripts/make-icons.mjs`. The sidebar, header, key gate, empty state and install sheet all show
|
||||
the real artwork instead of the old monochrome snowflake tile.
|
||||
32. **The ice scene was profiled, and the blur moved off the animated layer.** A mid-range phone
|
||||
viewport with 4x CPU throttling showed the drifting blobs were the only expensive part: a
|
||||
`filter: blur(100px)` on a huge element that is also being transformed forces a
|
||||
re-rasterisation every frame (150ms vs 33ms per frame in a software renderer). The blobs are now
|
||||
soft radial gradients with no filter — visually the same, 4-5x cheaper — phones get two blobs
|
||||
instead of four and a 16px (instead of 24px) glass blur, and nested glass never blurs. Result:
|
||||
60fps idle and ~17ms median frames while scrolling under 4x throttle.
|
||||
|
||||
14. **`gpt-oss:120b` does all content work — but it cannot read images** (`this model does not
|
||||
support image input`), so `VISION_MODEL` has to stay a multimodal model (`gemma4:31b-cloud`).
|
||||
Text-only inputs — pasted text, PDFs with a text layer, DOCX/PPTX/ODT — never touch the vision
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue