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.
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.
Add QEMU and multi-platform build (linux/amd64 + linux/arm64) to
GitHub Actions workflow, enabling self-hosting on Raspberry Pi and
other ARM64 devices.
- 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
With the previous default of 'loopback', Express ignored X-Forwarded-Proto
headers from Caddy/nginx when running in Docker (bridge IP, not loopback).
This caused req.secure=false, which made express-session silently drop the
session cookie on login - resulting in a 401 on every subsequent request.
Changing the default to 1 (trust one proxy hop) fixes this for all standard
Docker+reverse-proxy deployments without requiring manual configuration.
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.
- All @supports checks extended to include or (-webkit-backdrop-filter: blur(1px))
so Safari < 18 (which only recognizes the -webkit- prefix) no longer skips
the entire @supports block and receives no glass styles at all
- Non-blur glass styles (background-color, border, box-shadow) moved outside
@supports blocks - always active on all browsers regardless of blur support
- Capsule buttons, specular highlights, glass borders and shadows now visible
on all devices, blur effects added on top where supported
Safari's ITP blocks Strict cookies on certain navigations (direct URL entry,
reverse proxy context), resulting in a 401 on login even with valid credentials.
Lax is safe: CSRF attacks are prevented by the double-submit token and the
HTTPS-only secure flag. Firefox and Chrome were unaffected.
Bump version to 0.17.0, update CHANGELOG with full Phase 0-3 glass layer
changes, and update SPEC.md design system documentation with glass tokens,
glass.css layer architecture, and corrected color token values.
- 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.
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.
Users can now toggle which meal types (breakfast, lunch, dinner, snack)
are displayed in the meal planner via a new Settings section. Preference
is stored household-wide in sync_config and applied as a filter on the
meals page. Includes preferences API, i18n (DE/EN/IT), and Settings UI.
View mode (list/kanban) is now saved to localStorage and restored on
page load. URL parameter ?view=kanban takes precedence, enabling tablet
kiosk setups to default to Kanban view. Toggle buttons reflect the
active view correctly on initial render.
Closes#17
New tasks default to "none" priority instead of "medium". Tasks with no
priority hide the badge in list and dashboard views, reducing visual noise
for routine items. Includes DB migration v4 and i18n keys (de, en, it).
Closes#15
Three root causes fixed:
1. Double safe-area padding: pwa.css set padding-top/bottom on body
globally, but page containers already account for safe-area-inset
in their height calculations. Removed body vertical padding (kept
only in standalone media query for padding-top).
2. Wrong nav token: all page containers used --nav-height-mobile (56px)
instead of --nav-bottom-height (68px = 56px scroll + 12px dots),
causing 12px of content to render behind the bottom nav.
3. Scroll bleed: fixed-height page containers lacked overflow:hidden,
allowing scroll events to propagate to the body. Added
overscroll-behavior-y:contain on app-content globally.
Fixes#16
Native browser prompt() is unreliable on mobile browsers and PWAs,
often requiring multiple clicks to close. Replace all prompt() calls
with custom promptModal() and selectModal() functions that use the
existing modal system with proper focus management and animations.
Affected pages: shopping (create/rename list), tasks (add subtask),
meals (choose shopping list).
Fixes#12
Show shopping lists with open items directly on the dashboard.
Each list displays a progress bar, the first few unchecked items,
and a "+N more" overflow indicator. Widget only appears when there
are lists with open items.
Backend: new shoppingLists query in /api/v1/dashboard (up to 3 lists,
6 open items each). Frontend: renderShoppingLists() widget following
existing widget pattern. CSS: compact list/progress/item styles.
i18n: shoppingMore key added to de/en/it.
Requested in discussion #9
On mobile, closeModal() relies on the CSS animationend event to call
_doClose(). When the animation does not fire (prefers-reduced-motion,
tab switch, browser quirk), the modal stays stuck and the user cannot
dismiss it. A 300ms fallback timer now guarantees cleanup runs.
Reported in discussion #9
Category group headers in tasks and bar chart labels / transaction meta
in budget were showing raw German database keys instead of going through
CATEGORY_LABELS() i18n mapping.
Closes#11
Increase font-size to 16px on mobile for shopping quick-add inputs,
notes search, and contacts search. Desktop breakpoint restores compact
sizes. Move 9 page-specific stylesheets from index.html to on-demand
loading in router.js, reducing initial CSS payload.
- Rename #page-content to #main-content so skip-to-content link
targets the semantic <main> landmark
- Add sr-only priority labels to dashboard task items for screen
readers (WCAG 1.4.1 color-not-only)
- Replace hardcoded hex in greeting gradient with accent tokens
so dark mode themes the banner correctly
- Replace hardcoded gap: 2px with --space-0h token
- Bump version to 0.7.2
- Extract shared esc() utility (public/utils/html.js) replacing 8
duplicate escHtml() functions across all page modules
- Apply HTML escaping to all user-controlled data in innerHTML
templates: titles, names, locations, descriptions, colors, notes
content, weather data, autocomplete suggestions
- Remove user-scalable=no and maximum-scale=1 from viewport meta
tag, restoring pinch-to-zoom for WCAG 1.4.4 compliance
- Bump version to 0.7.1
Convert all server/, test, and setup files from require()/module.exports
to import/export syntax. Activate ESM globally via "type": "module" in
package.json and load dotenv via --import dotenv/config in npm scripts.