- modal/_validateField: set aria-invalid on invalid inputs so screen readers
announce field errors; login.js mirrors this for username/password fields
- color pickers (notes, calendar): wrap swatches in role="radiogroup" with
aria-labelledby, add aria-checked per swatch, localized aria-labels instead
of hex values, roving tabindex with Arrow/Enter/Space keyboard navigation
- nav badges: badge spans get aria-hidden="true"; nav link aria-label updated
to include overdue count (tasks) or pending reminder count (reminders)
- router: remove aria-live from <main> (caused full page re-reads on nav);
add dedicated #route-announcer sr-only region with aria-live=polite +
aria-atomic, announces page label 50ms after render completes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Shows a 3-screen modal overlay on first dashboard visit explaining key
features; dismissed state is persisted to localStorage so it never
reappears.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace local state mutation after list deletion with loadLists() so the
tab bar stays in sync with the server. Also add a renderTabs() call in
the error path so the UI recovers correctly on API failure.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- widget__empty: column → row layout, icon 28→20px, padding space-5 → space-3
saves ~40px vertical space per empty widget on mobile, keeps populated widgets
visible above the fold
- widget__body: bottom padding space-3 → space-4 for slightly more breathing room
- rebuildList() now uses document.startViewTransition with prefers-reduced-motion
guard; each customize-row gets a stable view-transition-name for smooth reorder
animation without a JS animation library
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When OPENWEATHER_UNITS=imperial, OpenWeatherMap returns wind speed in
mph directly — the server was incorrectly multiplying by 3.6 (m/s→km/h)
on top of that. All locale strings also hardcoded the unit label instead
of using a {{windUnit}} placeholder, so the label always read km/h.
Resolves#79
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add --color-warning-translucent and --color-soon tokens to tokens.css
- Replace hardcoded font-size 1.4rem with var(--text-xl) in dashboard.css
- Replace hardcoded rgba color with var(--color-warning-translucent)
- Remove duplicate .task-item__meta--overdue rule
- Fix hardcoded 'de-DE' locale: use formatTime() from i18n.js
- Fix formatDueDate: don't show time (23:59) when no due_time is set
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
After navigating to tasks/calendar/shopping/notes via FAB, the page's
primary add-button is programmatically clicked so the new-item modal
opens without a second tap. FAB container right-margin doubled to avoid
overlap with the browser's edge-swipe gesture zone.
Header shows current date and time instead of user name + separate date line.
urgentCount replaced by overdueCount (overdue tasks) and dueSoonCount (due today/soon),
each with a distinct chip color. formatDueDate updated to accept due_time and return
accurate overdue/soon states against the current moment.
dashboard grid expands to 4 columns at 1280px instead of 1440px.
widgetHeader replaces <a href> with <button type="button"> so iOS Safari
does not intercept the touch event before the JS click handler fires.
widget__header gets position: relative; z-index: 2 to appear above the
backdrop-filter ::after pseudo-element stacking context.
Imports fmtLocation; renderUpcomingEvents adds event-item__cal span for
cal_name and shows location with normalized ICS escapes via fmtLocation().
Event color dot gets filter: saturate(0.4) to match calendar view style.
group-mode toggle moved before view toggle in markup order.
Filter panel, task list, and FAB wrapped in .tasks-body for scroll containment.
tasks-toolbar flex-wrap removed — actions stay on one line on narrow screens.
effectiveDue() and sortTasks() added — same logic on client (tasks.js)
and server (dashboard.js urgentTasks moved from SQL to JS sort).
Applies in list-group, Kanban, and dashboard widget views.
SQLite DATE('now') replaced with new Date() for timezone-safe due_time.
formatDueDate now accepts due_time; displays it on today/tomorrow entries.
Overdue state is computed against the current moment, not midnight, so a
task due at 09:00 is correctly marked overdue once that time has passed.
Event popup, agenda, month, week, and day views now show the external
calendar name as an event-cal-label badge. Event popup and dashboard
event list display the location via fmtLocation(). Incorrect
|| 'var(--color-accent)' fallback removed from all five rendering sites.
- Implemented new recipes page with UI for managing recipes.
- Added REST API routes for recipes including create, read, update, and delete operations.
- Introduced database schema for recipes and recipe ingredients.
- Updated meals to link with recipes, allowing meals to reference specific recipes.
- Enhanced validation for recipe-related fields in meals.
- Added styles for the recipes page and components.
Root cause: when auth.me() failed during initial navigation, the catch block
called navigate('/login') without clearing _pendingLoginRedirect. The outer
finally then fired a second concurrent navigate('/login'), which held
isNavigating=true while running. If the user submitted the login form (or
iCloud Keychain autofilled credentials) before the second navigation
completed, navigate('/', user) was silently blocked by the isNavigating guard —
login appeared to succeed but the app never advanced to the dashboard.
Fix: clear _pendingLoginRedirect in the catch block so the finally handler
does not spawn the duplicate navigation.
Also adds a GET /api/v1/version endpoint (no auth required) and shows the
version on the login page, so users can verify their PWA has received the
latest cached JS.
Resolves#68
Co-authored-by: Ulas Kalayci <ulas.kalayci@googlemail.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.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>
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>