Previous fix removed the old CSS when new CSS loaded, but that happened
BEFORE the out-animation completed - causing a flash of unstyled content.
Now the old stylesheet stays until replaceChildren removes the old DOM.
Audit found ~35 violations against the token system. Fixed:
- Hardcoded shadows in layout.css replaced with --shadow-sm/md
- 8 rgba colors extracted to new glass tokens (--color-glass-*)
- border-radius: 50% replaced with var(--radius-full)
- ~25 off-grid spacing values (5px, 6px, 7px, 14px, 15px, 22px,
26px, 34px) aligned to 4px grid using space tokens
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
Fix stored XSS in tasks (titles/subtasks) and settings (member list)
by applying escHtml(). Harden trust proxy to loopback default, add
OAuth state parameter for Google Calendar CSRF protection, sanitize
CSV export against formula injection, invalidate sessions on user
deletion, restrict usernames to alphanumeric chars, and require admin
role for calendar sync triggers.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace all — with - in all source files (JS, CSS, HTML, JSON,
Markdown) for consistency and readability.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add complete Italian translation (497 keys) based on PR #7 by
@albanobattistella. Fixed filename from "it. json" to "it.json" and
registered Italian in SUPPORTED_LOCALES and the locale picker component.
Co-Authored-By: albanobattistella <34811668+albanobattistella@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
cleanup() set dragging = null, then onUp accessed dragging.slot,
.mealId, .sourceDate, .sourceType on the now-null reference.
Fix: destructure all needed values before calling cleanup().
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
External image requests to openweathermap.org fail silently in Chrome
Android PWA standalone mode. Icons are now proxied via
GET /api/v1/weather/icon/:code, making them same-origin — cacheable by
the service worker and free of CORS/CSP issues.
Tightened CSP: removed openweathermap.org from imgSrc (no longer needed).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add id field and display_override to manifest.json for reliable
Chrome Android PWA recognition
- Serve manifest.json with application/manifest+json MIME type
- Add /i18n.js and locale files to SW app shell cache (were missing)
- Bump SW cache version to v21
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Android was showing only a blue circle because maskable icons had no
visible content after the adaptive icon mask was applied. All icons now
use the actual Oikos house logo from docs/logo.svg. Maskable variants
use full-bleed background with logo within the 80% safe zone.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The locale JSON files use nested structure (e.g. {"nav":{"tasks":"…"}}),
but t() did a flat lookup, always falling back to the raw key string.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace all hardcoded German strings in router.js (navItems labels,
aria-labels, skip-link, error/toast messages) with t() calls. Add a
locale-changed event listener that re-renders sidebar and bottom-nav
items on language switch.
Replace hardcoded German strings in modal.js and oikos-install-prompt.js
with t() calls; wire locale-changed event listener for live re-render on
locale switch.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Extracts all German UI strings from public/pages/, public/components/,
and public/router.js into 482 i18n keys organized by module prefix.
English translations added for all keys.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Delete orphaned Python screenshot generators, social-preview template,
and duplicate icon files in public/assets/ (superseded by public/icons/).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Uses visualViewport resize event to detect keyboard state (viewport height
< 75% of window height). Sets body.keyboard-visible class; CSS hides
.fab and .page-fab via visibility:hidden on screens < 1024px.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduced --nav-bottom-height token (56px scroll + 12px dots indicator = 68px)
so that toast-container bottom and app-content padding-bottom both account for
the full nav-bottom height including the page-dots indicator.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Admin can now enter CalDAV URL, Apple-ID and app-specific password
directly in Settings; credentials are tested live before saving and
stored in sync_config (take precedence over .env); disconnect clears
DB-stored credentials without server restart. Auto-sync interval
(15 min, configurable via SYNC_INTERVAL_MINUTES) was already in place.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Pointer Events-based drag & drop (touch + mouse compatible):
- Ghost element follows pointer; drops on empty slots move the meal,
drops on occupied slots swap both meals via concurrent PUT requests
- prefers-reduced-motion: no ghost animation, interaction still works
- Suppress-click guard prevents accidental edit modal after drag
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>