Mobile UI pass: sheet swipe, ice-scene layering, contrast, touch targets
Phones (<=639px) only for layout/gestures; the theme-level fixes apply
everywhere, as they are token/design changes.
Settings sheet
- Swipe-to-dismiss reusing the InstallSheet gesture: the sheet follows the
finger, closes past ~90px or on a fast flick, snaps back otherwise. Drag
starts only from the handle/header and only while the body is scrolled to
the top. Backdrop tap still closes.
- Fixed header + scrollable body: pb-safe + spacer, so nothing is clipped;
a scroll cue (chevron) fades in while more content is below and out at
the end.
- Show/Save stack under the field at 44px on phones; close, Test connection
and Remove key are 44px too. Remove key is now a rose destructive action
with a confirmation that the full-screen key setup comes back.
- API key note split into a short privacy line plus a collapsible
'Using the local daemon?' tip (OLLAMA_AUTH=local / .env.local / signin).
- Fixed the missing space in the English tip ('ollama signin instead.').
- Health: kept the auto-check and banner; added a loading row and a
'Checked just now' / 'Checked at HH:MM' freshness label (also for errors).
Ice scene / readability
- Verified the orbs were never above content (fixed z-index:-1 layer,
pointer-events:none); they were just shining through the glass. Phones get
lower blob/orb opacity and orbs tucked to the edges; the bottom-right orb
is moved out from under the tab bar and the tab bar paints a more opaque
surface so its labels hold contrast.
Contrast / themes
- Dark --text-subtle raised to #9db1d0 (8.6:1 page, 7.2:1 glass); helper
text now uses the tokens.
- Disabled buttons are a visible muted chip (.btn-disabled) instead of a
40%-opacity ghost.
- One ice primary gradient in both themes; danger/success now actually show
their colour (bg-none strips the primary background-image that used to
paint over !bg-rose-600).
- Light mode: cool card rims instead of white-on-white, stronger shadow.
Redundancy / touch
- Removed only the header gear; the hamburger drawer (folders, Settings,
theme) and the tab-bar Settings stay.
- Theme toggle is a bordered chip with an action aria-label (drawer row too).
- Empty library hides the folder pills, demotes Import to a quiet link and
keeps a single primary CTA. 44px targets for nav rows, segmented control
and folder pills on phones.
- Fixed a pre-existing 16px horizontal overflow (header -mx-4 vs a
padding-less parent) and let info values wrap instead of truncating.
Verified: mobile suite 47/47 (EN+CS, 360/390px), pixel-sampled contrast
audit 0 runs below WCAG AA (8 runs, both themes/locales; axe marks the glass
'incomplete'), i18n 27/27, desktop regression 72/72, lint + build clean.
Service worker cache bumped to freezmo-v5.
This commit is contained in:
parent
618ad5fb9c
commit
9fb728d735
12 changed files with 519 additions and 235 deletions
|
|
@ -95,13 +95,15 @@
|
|||
--glass-tint: rgb(255 255 255 / 0.55);
|
||||
--text: #0b1830; /* 16.6:1 on #eef5fd */
|
||||
--text-muted: #46586f; /* 6.6:1 on light glass */
|
||||
--text-subtle: #566a84; /* 5.0:1 on light glass */
|
||||
--text-subtle: #566a84; /* 5.6:1 on light glass */
|
||||
--hover: rgb(12 32 62 / 0.05);
|
||||
--focus: #0ea5e9;
|
||||
|
||||
/* Frosted ice glass — one shared recipe for every large surface. */
|
||||
--glass-bg: rgb(255 255 255 / 0.55);
|
||||
--glass-border: rgb(255 255 255 / 0.8);
|
||||
/* A cool rim instead of near-white: white-on-white made light-mode card
|
||||
edges invisible against the pale page. */
|
||||
--glass-border: rgb(143 174 212 / 0.55);
|
||||
--glass-highlight: rgb(255 255 255 / 0.85);
|
||||
--glass-shadow: 0 20px 50px -20px rgb(30 64 175 / 0.35);
|
||||
--glass-solid-bg: rgb(255 255 255 / 0.8); /* question card: more opaque */
|
||||
|
|
@ -121,7 +123,7 @@
|
|||
--install-chip: rgb(255 255 255 / 0.55);
|
||||
--install-border: rgb(255 255 255 / 0.8);
|
||||
--shadow-card: 0 1px 2px rgb(14 21 40 / 0.06), 0 1px 3px rgb(14 21 40 / 0.04);
|
||||
--card-shadow: 0 1px 2px rgb(14 21 40 / 0.05), 0 5px 14px -6px rgb(14 21 40 / 0.12);
|
||||
--card-shadow: 0 1px 2px rgb(14 21 40 / 0.06), 0 9px 22px -8px rgb(14 21 40 / 0.16);
|
||||
--shadow-card-hover: 0 24px 60px -24px rgb(30 64 175 / 0.45), 0 2px 6px rgb(30 64 175 / 0.12);
|
||||
}
|
||||
|
||||
|
|
@ -141,7 +143,7 @@
|
|||
--glass-tint: rgb(255 255 255 / 0.06);
|
||||
--text: #e8f0ff; /* 16.3:1 on #0a1120 */
|
||||
--text-muted: #b3c4e0; /* 10.1:1 */
|
||||
--text-subtle: #90a4c6; /* 6.9:1 */
|
||||
--text-subtle: #9db1d0; /* 8.6:1 on page, 7.2:1 on dark glass */
|
||||
--hover: rgb(255 255 255 / 0.07);
|
||||
--focus: #38bdf8;
|
||||
|
||||
|
|
@ -323,6 +325,25 @@ body {
|
|||
.ice-orb-3 { width: 150px; height: 150px; }
|
||||
}
|
||||
|
||||
/* Phones: the decoration is already *behind* the content (the scene is a
|
||||
fixed, pointer-events:none layer at z-index -1), but it shines through the
|
||||
translucent page and glass, so it reads as overlap. Thin it right down and
|
||||
tuck the orbs to the edges, away from the text column and the tab bar. */
|
||||
@media (max-width: 639px) {
|
||||
:root {
|
||||
--ice-blob-opacity: 0.1;
|
||||
--ice-orb-opacity: 0.32;
|
||||
}
|
||||
.dark {
|
||||
--ice-blob-opacity: 0.2;
|
||||
--ice-orb-opacity: 0.42;
|
||||
}
|
||||
.ice-orb-1 { width: 96px; height: 96px; top: 8%; left: -26px; }
|
||||
.ice-orb-2 { width: 72px; height: 72px; left: -32px; }
|
||||
/* Was sitting right behind the bottom-nav labels. */
|
||||
.ice-orb-3 { width: 116px; height: 116px; bottom: 15vh; right: -46px; }
|
||||
}
|
||||
|
||||
@layer components {
|
||||
/* Frosted ice glass ------------------------------------------------------
|
||||
One shared recipe for the large surfaces only: translucent tint, 24px
|
||||
|
|
@ -487,6 +508,13 @@ body {
|
|||
.tabbar {
|
||||
border-top: 1px solid var(--glass-border);
|
||||
}
|
||||
/* Phones: the tab bar sits over the bottom of the ice scene, so give the
|
||||
labels a calmer surface than the barely-there shared glass tint. */
|
||||
@media (max-width: 639px) {
|
||||
.tabbar {
|
||||
background: color-mix(in oklab, var(--page) 84%, transparent);
|
||||
}
|
||||
}
|
||||
|
||||
/* Soft tinted chip for activity types (a hint of colour, not a rainbow) -- */
|
||||
.tint-sky { background: linear-gradient(140deg, rgb(156 205 245 / 0.34), rgb(156 205 245 / 0.14)); color: #1c4e72 }
|
||||
|
|
@ -531,32 +559,38 @@ body {
|
|||
box-shadow: inset 0 0 0 1px var(--nav-active-border), inset 0 1px 0 var(--glass-highlight);
|
||||
}
|
||||
|
||||
/* Shared CTA language: the icon's blue, with a deeper fill in light mode. */
|
||||
/* Shared CTA language: one ice gradient in *both* themes (it mirrors the
|
||||
segmented control, the progress bars and the tab-bar plus). #06111f on
|
||||
#38bdf8 is ~8.8:1, so the dark label clears AA everywhere on the ramp. */
|
||||
.btn-primary {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
border-radius: 14px;
|
||||
background-image: linear-gradient(135deg, #1d4ed8 0%, #1a5fd0 55%, #0ea5e9 100%);
|
||||
color: #ffffff;
|
||||
font-weight: 500;
|
||||
box-shadow: 0 10px 24px -12px rgb(14 165 233 / 0.7);
|
||||
background-image: linear-gradient(135deg, #38bdf8 0%, #60a5fa 100%);
|
||||
color: #06111f;
|
||||
font-weight: 600;
|
||||
box-shadow: 0 10px 24px -14px rgb(14 165 233 / 0.75);
|
||||
transition: box-shadow 180ms ease, filter 180ms ease, transform 160ms ease;
|
||||
}
|
||||
.btn-primary:hover {
|
||||
filter: brightness(1.07);
|
||||
box-shadow: 0 14px 32px -12px rgb(56 189 248 / 0.85);
|
||||
filter: brightness(1.06);
|
||||
box-shadow: 0 14px 32px -14px rgb(56 189 248 / 0.9);
|
||||
}
|
||||
.btn-primary:active { transform: scale(0.985); }
|
||||
.dark .btn-primary {
|
||||
background-image: linear-gradient(135deg, #ffffff 0%, #bfe3ff 100%);
|
||||
color: #0b1220;
|
||||
box-shadow: 0 10px 26px -14px rgb(56 189 248 / 0.6);
|
||||
}
|
||||
.dark .btn-primary:hover {
|
||||
filter: brightness(1.03);
|
||||
box-shadow: 0 14px 34px -14px rgb(56 189 248 / 0.95);
|
||||
|
||||
/* Disabled: a real, visible chip instead of a 40%-opacity ghost. Important
|
||||
declarations deliberately live in this (earlier) layer so they beat the
|
||||
important rose/emerald overrides of the danger/success variants. */
|
||||
.btn-disabled {
|
||||
background-image: none !important;
|
||||
background-color: var(--surface-sunken) !important;
|
||||
color: var(--text-subtle) !important;
|
||||
box-shadow: none !important;
|
||||
filter: none !important;
|
||||
transform: none !important;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
|
|
@ -653,6 +687,22 @@ body {
|
|||
@media (display-mode: standalone) {
|
||||
.standalone-hide { display: none }
|
||||
}
|
||||
|
||||
/* Phones: Apple's 44px minimum for touch targets. The class is a no-op on
|
||||
desktop (and on tablets), so components can carry it unconditionally. */
|
||||
@media (max-width: 639px) {
|
||||
.touch-target { min-height: 44px; min-width: 44px; }
|
||||
.nav-row { min-height: 44px; }
|
||||
.segmented > button { min-height: 44px; }
|
||||
.folder-pill { min-height: 44px; }
|
||||
}
|
||||
|
||||
/* Settings sheet: contained momentum scrolling; the safe-area padding comes
|
||||
from the pb-safe utility plus a trailing spacer. */
|
||||
.sheet-scroll {
|
||||
overscroll-behavior: contain;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
}
|
||||
|
||||
/* Mobile form fields ----------------------------------------------------- */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue