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:
@@ -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
@@ -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
@@ -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
@@ -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.`);
|
||||
|
||||
@@ -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)
|
||||
* -------------------------------------------------------- */
|
||||
|
||||
@@ -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
|
||||
* -------------------------------------------------------- */
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user