Six tabs (General, Meals, Budget, Shopping, Calendar, Account) replace
the flat single-page layout. Active tab persists via sessionStorage.
Calendar tab auto-activates on OAuth redirect. Tab bar is sticky.
All labels translated in de/en/es/it/sv.
- Added user-scalable=no, maximum-scale=1 to viewport meta tag to prevent
pinch-to-zoom in standalone PWA mode
- Added overflow: hidden to html, body so any minimal content overflow
cannot make the body scrollable (belt-and-suspenders alongside
overscroll-behavior: none)
- Service worker cache bumped to v28/v27
Root cause 1 (scroll bleed): padding-top was applied to body in standalone
mode. Since .app-shell has height: 100dvh, body-padding shifted the shell
beyond the viewport bottom - enabling body-level scrolling.
Fix: moved padding-top from body to .app-shell in the standalone media query.
Root cause 2 (content overflow): fixed-height page containers
(Calendar, Shopping, Meals, Notes, Budget, Contacts) calculated height as
100dvh - nav-bottom - safe-area-inset-bottom, but never subtracted the top
safe area. This caused each page to overflow .app-content by exactly
env(safe-area-inset-top) pixels in standalone mode.
Fix: added --safe-area-inset-top token and subtracted it in all 6 height
calculations.
Service worker cache bumped to v27/v26.
- Add cursor:pointer to .modal-overlay so iOS Safari fires click events
on the backdrop (iOS ignores clicks on non-interactive divs without it)
- Add touchend fallback listener on overlay for belt-and-suspenders iOS support
- Enlarge close button from target-sm (32px) to target-md (40px) to meet
Apple touch-target guidelines; remove now-redundant ::before expansion
- Swipe-to-close now only activates from the top handle zone (< 48px) or
when the panel is scrolled to top, preventing accidental dismissal while
scrolling form content downward
- Calendar toolbar now wraps view buttons to a second row on viewports
< 580px so nav controls and label stay readable on all iOS devices
- Tasks toolbar title no longer bleeds over action buttons; uses
min-width:min-content so flex-wrap kicks in before overflow occurs
- Shopping list-header name gets flex:1/overflow:hidden so it truncates
cleanly instead of colliding with the clear-checked / delete buttons
- New optional recipe_url field in the meal modal (below Notes)
- Link icon appears on meal cards when a URL is set, opens in new tab
- DB migration v6: ALTER TABLE meals ADD COLUMN recipe_url TEXT
- API: recipe_url supported in POST /meals and PUT /meals/:id
- i18n: new keys recipeUrlLabel, recipeUrlPlaceholder, openRecipe (de, en, sv, it)
- New DB table shopping_categories (migration v5) seeds 9 default
categories with Lucide icons and sort_order
- Backend CRUD routes: GET/POST/PUT/DELETE /shopping/categories
plus PATCH /shopping/categories/reorder
- Category validation now uses DB instead of hardcoded constant;
items of deleted category are moved to the next available one
- Frontend shopping page loads categories from API, dropdown and
grouping reflect custom order dynamically
- Settings -> Shopping section: list categories with up/down buttons,
click-to-rename, delete with confirmation; add new categories inline
- i18n keys added in de/en/sv/it
- README: GHCR badge, Kanban quick-status buttons and configurable
currency mentioned in highlights
- installation.md: Option A (pre-built image, no clone) as primary path,
Option B (build from source) as alternative; Updates section updated;
SQLCipher troubleshooting tip added
- index.html: Get Started block now shows pre-built image path;
task and budget feature cards updated (EN + DE translations)
docker-compose.yml now references ghcr.io/ulsklyc/oikos:latest so users
can start the app with a single 'docker compose up' without cloning or
building locally. The build: . entry is retained for contributors who
want to build from source with --build.
README Quick Start updated to document both the no-clone path (curl
docker-compose + .env.example) and the build-from-source path.
Adds a small button on each kanban card that cycles the task status
(open → in_progress → done → open) without requiring drag-and-drop.
Useful for touch devices and kiosk browsers (e.g. Fully Kiosk Browser)
where drag-and-drop is unavailable. All four locales updated.
- Shopping category dropdown options now use CATEGORY_LABELS() for
translated display text instead of raw German internal keys
- rrule-ui.js now imports t() from /i18n.js; all hardcoded German
strings (freq options, weekday labels, form labels, unit labels)
replaced with i18n keys under the new 'rrule' namespace
- Added 'rrule' section to de.json and en.json with 22 new keys
Fixes#21
- tasks.js: add maybeShowSwipeHint (long loop, max 3x) - matches shopping.js pattern
- tasks.js: vibrate(15) on task status toggle
- oikos-locale-picker: show disabled/loading state for both reload and setLocale paths
- dashboard: show success toast after weather refresh (all 4 locales)
- dashboard: add semi-transparent FAB backdrop to signal open mode
global.window assignment doesn't expose 'window' as an identifier in ESM modules
in Node.js 22. matchMedia() is a global in browsers - no window. prefix needed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Locale-Picker: disable select + fade before location.reload() (system mode)
gives visual feedback before the page jumps
- Extract _validateField() helper from wireBlurValidation to avoid duplication
- Add validateAll(formContainer): validates all required fields on demand,
marks inline errors, focuses first invalid field
- tasks.js: call validateAll() at submit to catch untouched required fields
Swedish translation contributed by @olsson82 (PR #19), integrated with
minor corrections (dayShortSunday: Sun → Sön, amountLabel aligned with
v0.11.2, new v0.11.2 currency keys added).
Italian was already supported server-side but is now explicitly listed
in the locale picker alongside German, English, and Swedish.
Add household-wide currency preference for the budget section.
Users can select from 13 currencies (EUR, USD, GBP, SEK, NOK, DKK,
CHF, PLN, CZK, HUF, JPY, AUD, CAD) in Settings → Budget.
- preferences API (GET/PUT) now includes currency field
- budget page loads currency from preferences on render
- formatAmount() uses locale-aware Intl.NumberFormat with chosen currency
- settings page gains a Budget section with a currency select
- all three locales (de, en, it) updated with new i18n keys
The dashboard meal widget was showing all meal types regardless of the
household meal visibility settings configured in Settings > Meal Plan.
Root cause: the todayMeals SQL query in dashboard.js did not read
visible_meal_types from sync_config. The Meals page applied this filter
client-side, but the dashboard API returned unfiltered data.
Fix: read visible_meal_types from sync_config before the query and inject
the active types as IN (?) placeholders. Falls back to all four types when
no preference is stored.
min-height: 100dvh lets the shell grow unbounded, so app-content never
overflows and touch scroll has nothing to scroll. height: 100dvh constrains
the shell to exactly the viewport, making overflow-y: auto on app-content work.
- inputmode=decimal on budget amount input for correct decimal keyboard
- inputmode=numeric on rrule interval input for numeric keyboard
- autocomplete attributes on contacts form (name, tel, email, street-address)
- Replace hardcoded px values with design tokens (--space-0h, --space-px,
--space-1, --target-sm/md/lg) across layout, calendar, dashboard CSS
- Fix rgba(255,255,255,0.35) spinner border to use var(--color-glass-hover)
- Fix modal close icon from off-grid 18px to 16px
- Fix touch scrolling on mobile: add min-height: 0 and
-webkit-overflow-scrolling: touch to .app-content
- Add --color-text-on-accent token to tokens.css
- Migrate all hardcoded color:#fff/#ffffff to var(--color-text-on-accent) across all module stylesheets
- Fix toggle thumb background (#fff -> var(--color-surface)) for dark mode
- Migrate hardcoded transition durations to token vars (--transition-fast/base/slow)
- Replace off-grid spacing (3px, 5px) with space tokens (--space-0h, --space-1)
- Replace below-minimum font-size 9px with var(--text-xs) in calendar, dashboard, notes
- Replace hardcoded 2.5rem with var(--text-4xl) in weather widget
- Replace hardcoded box-shadow with var(--shadow-sm) in toggle thumb
- Replace 0.85em and #666 with type/color tokens in print styles