3bfda59fc0
- widget__empty: column → row layout, icon 28→20px, padding space-5 → space-3 saves ~40px vertical space per empty widget on mobile, keeps populated widgets visible above the fold - widget__body: bottom padding space-3 → space-4 for slightly more breathing room - rebuildList() now uses document.startViewTransition with prefers-reduced-motion guard; each customize-row gets a stable view-transition-name for smooth reorder animation without a JS animation library Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4.0 KiB
4.0 KiB
Oikos UX/UI Audit & Implementation Plan
1. Design-System & Styling
Issues Identified
- Touch Targets Too Small:
--target-sm: 32pxand.btn--icon-sm(36x36px) violate minimum touch target guidelines (44x44px or 48x48px). This causes usability issues on mobile devices (fat-finger syndrome). - Duplicated Dark Mode Tokens: Dark mode CSS variables in
tokens.cssare duplicated across@media (prefers-color-scheme: dark)and[data-theme="dark"], creating a maintenance nightmare.
Implementation Steps
- Fix Touch Targets:
--target-base: 44pxToken ergänzt;.btn--icon-smaufmin-height/min-width: var(--target-base)korrigiert;--target-smbleibt 32px als visuelle Größe (kein Touch-Target). - Refactor Theme Tokens: Bewusst übersprungen — der CSS-native
@media (prefers-color-scheme: dark)-Block ist eine Stärke (Dark Mode ohne JS). Entfernen würde Nutzer ohne JS ohne Dark Mode lassen.
2. Components & Interaction
Issues Identified
- Mobile Modal Swipe-to-Close Bug: If a user drags the modal down (
dy > 0) and then back up (dy < 0) without lifting their finger,draggingis set tofalse. Thetouchendevent is then ignored, leaving the modal stuck out of position. - Accessibility (A11y) Violation: The
.modal-overlayelement usesrole="presentation"alongside anaria-label.role="presentation"hides the element from screen readers, conflicting with the label and its function as a clickable close area. - Misplaced Utility Functions: Generic UI helpers (
wireBlurValidation,validateAll,btnSuccess,btnLoading,btnError) are hardcoded inpages/dashboard.jsinstead of a shared utility file.
Implementation Steps
- Fix Swipe Bug (
modal.js):touchmove-Handler korrigiert — beidy < 0wird Panel auftranslateY(0)zurückgesetzt,draggingbleibttrue. - Fix Modal A11y (
modal.js):role="presentation"aus.modal-overlayentfernt. - Relocate Utilities: Bereits erledigt —
wireBlurValidation,validateAll,btnSuccess,btnLoading,btnErrorsind inutils/ux.js(Zeilen 538–620).
3. Layout, Navigation & Routing
Issues Identified
- FOUC (Flash of Unstyled Content) on Navigation: In
router.js,loadPageStyleremoves the old stylesheet before the new page transition animation (page-transition--in-right) completes, causing layout jumps. - Missing Focus Trap in Global Search: The
#search-overlaydoes not use the focus trap logic frommodal.js. Users can tab out of the search overlay into the hidden page below. - SVG ID Collision Risk: The logo generated in
router.jsuses a hardcoded ID (id="oikos-logo-bg") for its gradient. If reused, this will break rendering.
Implementation Steps
- Fix Routing FOUC (
router.js): Kein echter Bug —style.cleanup()wird vormodule.render()aufgerufen, aber die neue Seite startetopacity: 0. Kein sichtbares FOUC in der aktuellen Implementierung. - Add Search Focus Trap (
router.js): Eigenständiger Focus Trap inopenSearch/closeSearchimplementiert (ohne modal.js-Kopplung). - Fix SVG IDs (
router.js): Gradient-ID wird nun mitMath.random().toString(36)-Suffix generiert.
4. Dashboard
Issues Identified
- Wasted Space from Large Empty States: Empty widgets (e.g., no tasks) render a large "Empty State" UI. On mobile, this pushes populated widgets below the fold.
- Lack of Visual Feedback in Customization: Reordering widgets in the customize modal (
rebuildList()) happens instantly without transition, feeling jarring.
Implementation Steps
- Compact Empty States (
dashboard.css):.widget__emptyauf horizontales Row-Layout umgestellt, Icon 28→20px, Padding reduziert — spart ~40px vertikalen Platz pro leerem Widget. - Animate Widget Reordering (
dashboard.js):rebuildList()nutzt nundocument.startViewTransition()mitprefers-reduced-motion-Guard undview-transition-nameje Row.