fix(ux): prevent iOS auto-zoom on inputs + lazy-load page CSS

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.
This commit is contained in:
Ulas
2026-04-04 06:39:45 +02:00
parent 70c1291ae7
commit 364d029950
7 changed files with 58 additions and 17 deletions
+8
View File
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [0.7.3] - 2026-04-04
### Accessibility
- Increase font-size to 16px (`--text-md`) on mobile for `quick-add__input`, `quick-add__qty`, `quick-add__cat` (shopping), `notes-toolbar__search-input`, and `contacts-toolbar__search-input` - prevents iOS auto-zoom on input focus (WCAG touch-friendly inputs)
### Performance
- Lazy-load page-specific stylesheets on route change instead of loading all 10 upfront in `index.html` - reduces initial CSS payload; only tokens, reset, pwa, layout, and login styles are render-blocking
## [0.7.2] - 2026-04-04
### Accessibility
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "oikos",
"version": "0.7.2",
"version": "0.7.3",
"description": "Self-hosted family planner - calendar, tasks, shopping, meal planning, budget and more. Private, open-source, no subscription.",
"main": "server/index.js",
"type": "module",
+1 -10
View File
@@ -31,21 +31,12 @@
<link rel="modulepreload" href="/router.js" />
<link rel="modulepreload" href="/rrule-ui.js" />
<!-- Styles -->
<!-- Styles (Basis - seitenspezifische CSS wird vom Router on-demand geladen) -->
<link rel="stylesheet" href="/styles/tokens.css" />
<link rel="stylesheet" href="/styles/reset.css" />
<link rel="stylesheet" href="/styles/pwa.css" />
<link rel="stylesheet" href="/styles/layout.css" />
<link rel="stylesheet" href="/styles/login.css" />
<link rel="stylesheet" href="/styles/dashboard.css" />
<link rel="stylesheet" href="/styles/tasks.css" />
<link rel="stylesheet" href="/styles/shopping.css" />
<link rel="stylesheet" href="/styles/meals.css" />
<link rel="stylesheet" href="/styles/calendar.css" />
<link rel="stylesheet" href="/styles/notes.css" />
<link rel="stylesheet" href="/styles/contacts.css" />
<link rel="stylesheet" href="/styles/budget.css" />
<link rel="stylesheet" href="/styles/settings.css" />
<!-- Theme: Vor CSS-Rendering anwenden (Flash-Prevention) -->
<script>
+29 -1
View File
@@ -64,6 +64,31 @@ function updateThemeColorForRoute(route) {
}
}
// --------------------------------------------------------
// Dynamisches Stylesheet-Loading pro Seitenmodul
// --------------------------------------------------------
let activePageStyle = null;
function loadPageStyle(moduleName) {
if (!moduleName) return Promise.resolve();
const href = `/styles/${moduleName}.css`;
if (activePageStyle?.getAttribute('href') === href) return Promise.resolve();
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = href;
const loaded = new Promise((resolve) => {
link.onload = resolve;
link.onerror = resolve;
});
if (activePageStyle) activePageStyle.remove();
document.head.appendChild(link);
activePageStyle = link;
return loaded;
}
// --------------------------------------------------------
// Modul-Cache: verhindert redundante dynamic imports bei Navigation
// --------------------------------------------------------
@@ -170,7 +195,10 @@ async function renderPage(route, previousPath = null) {
if (loading) loading.hidden = true;
try {
const module = await importPage(route.page);
const [module] = await Promise.all([
importPage(route.page),
loadPageStyle(route.module),
]);
if (typeof module.render !== 'function') {
throw new Error(`Seite ${route.page} exportiert keine render()-Funktion.`);
+5 -1
View File
@@ -61,7 +61,7 @@
border: 1.5px solid var(--color-border);
background-color: var(--color-surface-2);
color: var(--color-text-primary);
font-size: var(--text-sm);
font-size: var(--text-md);
transition: border-color var(--transition-fast);
min-height: 36px;
}
@@ -72,6 +72,10 @@
background-color: var(--color-surface);
}
@media (min-width: 1024px) {
.contacts-toolbar__search-input { font-size: var(--text-sm); }
}
/* --------------------------------------------------------
* Kategorie-Filter (horizontal scroll)
* -------------------------------------------------------- */
+5 -1
View File
@@ -68,7 +68,7 @@
border-radius: var(--radius-md);
background: var(--color-surface);
color: var(--color-text);
font-size: var(--text-sm);
font-size: var(--text-md);
outline-offset: 2px;
}
@@ -77,6 +77,10 @@
border-color: transparent;
}
@media (min-width: 1024px) {
.notes-toolbar__search-input { font-size: var(--text-sm); }
}
/* --------------------------------------------------------
* Masonry-Grid
* -------------------------------------------------------- */
+9 -3
View File
@@ -165,7 +165,7 @@
border: 1.5px solid var(--color-border);
background-color: var(--color-surface);
color: var(--color-text-primary);
font-size: var(--text-base);
font-size: var(--text-md);
transition: border-color var(--transition-fast);
min-height: 44px;
}
@@ -186,7 +186,7 @@
border: 1px solid var(--color-border);
background-color: var(--color-surface);
color: var(--color-text-primary);
font-size: var(--text-sm);
font-size: var(--text-md);
text-align: center;
min-height: auto;
height: auto;
@@ -206,7 +206,7 @@
border: 1.5px solid var(--color-border);
background-color: var(--color-surface);
color: var(--color-text-secondary);
font-size: var(--text-sm);
font-size: var(--text-md);
cursor: pointer;
min-height: 44px;
}
@@ -216,6 +216,12 @@
border-color: var(--color-accent);
}
@media (min-width: 1024px) {
.quick-add__input { font-size: var(--text-base); }
.quick-add__qty { font-size: var(--text-sm); }
.quick-add__cat { font-size: var(--text-sm); }
}
.quick-add__btn {
width: 44px;
height: 44px;