On Safari/iOS PWA cold start or after cookie clear, logging in with wrong
credentials triggered auth:expired, re-rendering the login page and losing
the error message. The login endpoint returns 401 for invalid credentials,
not for session expiry, so apiFetch must not fire auth:expired in that path.
Resolves#68
Co-authored-by: Ulas Kalayci <ulas.kalayci@googlemail.com>
'Sonstiges' ist DEFAULT_CATEGORY_NAME und dient als allgemeiner Fallback für
Zutaten (Gewürze, Saucen etc.). Beim Ausschluss würde die Kategorie bestehender
Zutaten beim Bearbeiten einer Mahlzeit lautlos überschrieben (Datenintegrität).
Nur haushaltsfremde Kategorien (Haushalt, Drogerie) werden ausgeblendet.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
All tokens with dark-mode overrides gain a private --_name counterpart in :root.
Public tokens (--color-*, --module-*, --glass-* etc.) become stable var(--_name)
references. Both dark blocks now only override compact private tokens — no more
manual two-block sync for every future colour change.
Also removes the redundant --color-surface-2 dark override (already auto-derived
via var(--neutral-50)). No visual change.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Bundles the Indigo accent migration, module-colour decoupling, WCAG
contrast improvements and nav-badge base-style relocation into one
release. See CHANGELOG.md [0.20.15] for full details.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The .nav-badge base styles (background, size, color) were defined in tasks.css,
which is dynamically unloaded when navigating away from /tasks. This caused the
overdue badge in the nav to become invisible on every other page, even though
the badge element remained in the DOM.
Also refactors subtask checkbox icon to use a CSS class instead of inline styles.
Resolves#56
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Root cause: the badge was `position: absolute` relative to the entire
`.nav-item`, which stretches to `flex: 1` on mobile (up to ~75 px wide).
With `right: 4px` the badge sat far from the icon on the bottom bar and
overlapped label text in the expanded desktop sidebar.
Fix: `updateOverdueBadge()` now wraps the nav icon in a
`.nav-item__icon-wrap` span (created once, reused on subsequent calls).
The badge is appended there instead of to the nav item root.
CSS changes:
- Remove `.nav-item .nav-badge` positional override
- Add `.nav-item__icon-wrap { position: relative; display: inline-flex }`
- Add `.nav-item__icon-wrap .nav-badge { position: absolute; top: -4px; right: -4px }`
The badge now consistently overlays the top-right corner of the icon
across all nav layouts (mobile column, collapsed sidebar row, expanded
sidebar row with label).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The nav-badge was appended as an in-flow flex child, breaking nav-item
layout: on mobile (column flex) it appeared below the label, on desktop
sidebar (row flex + justify-content:center) it was pushed far right via
margin-left:auto. Fix positions it absolutely within the nav-item and
uses DOM API instead of insertAdjacentHTML per project convention.
Co-authored-by: Ulas Kalayci <ulas.kalayci@googlemail.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
innerHTML violates project convention (PostToolUse hook blocks it); use
replaceChildren with createElement/textContent instead. Also restore the
trailing newline removed from uk.json.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
render() always fetched /tasks without query params, so active filter
chips appeared selected but all tasks were shown after navigating away
and back. Fixed by building the same filter query in render() that
loadTasks() uses, keeping both parallel fetches and correct filtering.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
position: sticky on .modal-panel__header failed on iOS WebKit when the
scroll container had padding-top applied (drag-handle spacing). Restructured
modal layout: .modal-panel is now a flex-column with overflow:hidden and
.modal-panel__body handles scrolling (overflow-y:auto, flex:1). The header
is a non-scrolled flex sibling, so it stays visible without sticky. Updated
swipe-to-close to read .modal-panel__body scroll position.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add Ukrainian (uk) locale to SUPPORTED_LOCALES and locale picker
- Add public/locales/uk.json (622 keys, full Ukrainian translation)
- Add UAH (Ukrainian Hryvnia) to SUPPORTED_CURRENCIES and VALID_CURRENCIES
- Add CATEGORY_I18N map and catLabel() in settings.js to translate default
shopping category names in the settings panel; rename and delete dialogs
now also use the translated name instead of the raw German DB string
- Align server VALID_CURRENCIES with frontend: add missing AED, BRL, INR, SAR
Co-Authored-By: baragoon <baragoon@users.noreply.github.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- pwa.css: body::after now uses var(--glass-bg) matching the nav's glass
background exactly; z-index lowered to z-nav-1 so the nav renders
on top in the overlap area (safe-area padding), removing the visible
color mismatch that appeared as empty space
- router.js: add iOS focusin/focusout handlers that temporarily set
maximum-scale=1 on input focus to prevent WKWebView auto-zoom;
restores original viewport content 150ms after blur so manual
zoom remains available for accessibility
Two causes of ~1s navigation delay fixed:
1. glass.css Section 19 was extending the page-in animation from 0.20s
to 0.30s using spring easing. Reverted to 0.20s in / 0.12s out.
2. During transitions, dozens of backdrop-filter layers (widgets, cards,
inputs, toolbars) were composited simultaneously for both the outgoing
and incoming page, overloading mid-range Android GPUs.
Added html.navigating class: router.js sets it at transition start,
glass.css overrides all app-content backdrop-filters to none while
active, animationend removes it once the in-animation completes.
will-change: transform on a position:relative flex child causes iOS WebKit
to composite the element at an incorrect position - creating a visible gap
below the nav bar. Remove it; CSS transform transitions use hardware
acceleration automatically on modern iOS without this hint.
Add -webkit-fill-available before 100dvh on .app-shell as a fallback for
iOS WebKit versions where 100dvh is computed slightly smaller than the
actual WKWebView height.
Switch .nav-bottom from position: fixed to a flex child of .app-shell.
With position: fixed and will-change: transform (used for the hide/show
animation), iOS compositor layers can misplace the element. As a flex
child (position: relative; flex-shrink: 0) at the end of a height: 100dvh
container the nav is guaranteed to sit flush at the physical screen bottom.
Remove padding-bottom clearance from .app-content, .tasks-page and
.dashboard - no longer needed since the nav no longer overlaps the
content area.
- Add _pendingLoginRedirect flag in router.js to handle auth:expired
events that fire during active navigation (isNavigating=true).
Previously the navigate('/login') call was silently dropped, leaving
the user stuck on the dashboard with an error toast.
- Bump SHELL_CACHE v33→v34 and PAGES_CACHE v28→v29 to force cache
invalidation on iOS devices still running the pre-CSRF-fix code.
- Add err.status to dashboard error log for easier diagnostics.
iOS Safari in PWA standalone mode unreliably handles cookies, causing
CSRF token desync between client and server after app resume. Previous
fixes (response body token in /auth/me and /auth/login) still left a
window where the token could go stale.
Now the server sends X-CSRF-Token response header on every API response
(via csrfMiddleware), including 403 error responses. The client reads
this header from every response, enabling instant self-healing: a 403
extracts the correct token from the error response itself and retries
without needing an extra /auth/me round-trip.
SW cache bumped to v33 to ensure existing iOS PWA installs pick up the
new client code.
When adding ingredients in the meal editor, each ingredient now has a
category dropdown. Categories are stored on the ingredient and applied
automatically when transferring to the shopping list, so items appear
pre-grouped by category without manual re-sorting.
iOS reserves the home indicator area outside the CSS viewport, leaving
a black strip below the bottom navigation. A fixed body::after pseudo-
element now fills this zone with the surface color. Also added explicit
background-color on body for consistent rendering.
iOS Safari (especially PWA/standalone mode) unreliably exposes cookies
via document.cookie, causing CSRF token mismatch on state-changing
requests. The CSRF token is now included in /auth/login and /auth/me
response bodies and stored in-memory on the client. Cookie remains as
fallback. Retry mechanism also improved to read token from response
body and handle expired sessions.
- FAB focus ring: double-ring pattern replacing invisible #fff outline
- forced-colors media query for Windows High Contrast Mode
- New tokens: --color-accent-secondary, --content-max-width-narrow, --cal-hour-height
- Apple sync logo uses semantic tokens for correct dark mode inversion
- Sidebar logo gradient references token instead of hardcoded #7C5CFC
Users can now show/hide widgets and reorder them via a settings button
in the greeting header. Configuration is persisted server-side in
sync_config (dashboard_widgets key) and shared across all family members.
- Greeting widget gets a settings icon button opening a customize modal
- Modal lists all widgets (tasks, calendar, shopping, meals, notes,
weather) with toggle switches and up/down reorder buttons
- Reset to default layout available in the modal
- GET /preferences now returns dashboard_widgets; PUT accepts it
- All 10 locales updated with new i18n keys
Dashboard widgets, task cards, note items, meal slots, form inputs,
toolbars, and FAB actions now use semi-transparent glass backgrounds
with backdrop-filter blur. Each surface gets a subtle module accent
color tint via color-mix gradient overlay. App background uses a
radial accent gradient for ambient vibrancy.
New tokens: --glass-bg-card, --glass-bg-input, --glass-bg-toolbar,
--glass-tint-strength with full dark mode and accessibility overrides.
In iOS WebKit standalone (home screen) mode, position:fixed elements
move with the page when the body itself becomes scrollable - unlike
regular Safari where fixed elements stay pinned. The root cause was
body having min-height: 100dvh without overflow: hidden, which allowed
body scroll to occur when content overflowed.
Fix: html and body are now overflow: hidden with fixed height (100% / 100dvh)
so all scrolling is confined to .app-content. Service worker bumped to
shell-v30 to force re-download of reset.css on installed PWAs.