feat(web): improve web build configuration and accessibility
- Add source maps and web renderer build arguments with defaults - Configure Caddy with CSP headers, cache policies, and service worker handling - Defer loading of import screen for performance optimization - Add semantic labels to icons for accessibility - Update web index.html with Swedish language, meta tags, and description - Add robots.txt and lighthouse configuration - Add new planning documents and archive entries
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
# Plan: Projektanpassad Lighthouse-plan for Flutter-web
|
||||
|
||||
## Mål
|
||||
Höja Lighthouse-resultaten för Flutter-webklienten i detta repo utan att bryta befintlig Docker/Caddy-deploy, med fokus på mätbar förbättring av prestanda, tillgänglighet och grundläggande SEO.
|
||||
|
||||
## Kontext och nuläge (verifierat i projektet)
|
||||
- Flutter-web byggs redan i release-läge i `flutter/Dockerfile` via `flutter build web --release`.
|
||||
- Webb klient körs via Caddy i `flutter/Caddyfile` och har redan `encode gzip`.
|
||||
- `flutter/web/index.html` är minimal och innehåller redan korrekt viewport utan `user-scalable=no`.
|
||||
- `flutter/web/` innehåller idag endast `index.html` (ingen `robots.txt`/`sitemap.xml` i Flutter-mappen).
|
||||
- API-basurl injiceras redan korrekt med `--dart-define=API_BASE_URL=/api` via `compose.flutter.yml`.
|
||||
|
||||
## Problem i befintlig flutter-lighthouse.md som inte passar repo exakt
|
||||
- Planen utgår delvis från Nginx/Apache, men projektet använder Caddy för Flutter-spåret.
|
||||
- Påståendet om `user-scalable=no` gäller inte nuvarande `flutter/web/index.html`.
|
||||
- Påståendet om ogiltig `robots.txt` kan inte verifieras i nuvarande Flutter-webmapp.
|
||||
- Förslag om tvingad HTML-renderer är för grovt; bör vara experiment med mätning och rollback-kriterier.
|
||||
|
||||
## Strategi
|
||||
Arbeta i tre iterationer med baseline -> lågriskoptimeringar -> tyngre optimeringar, och låt varje steg vara datadrivet.
|
||||
|
||||
---
|
||||
|
||||
## Fas 1: Baseline och mätprotokoll (dag 1)
|
||||
1. Skapa en reproducerbar mätbaseline för både lokal container och produktionsdomän.
|
||||
2. Kör Lighthouse minst 3 gånger per miljö och ta median för:
|
||||
- Performance score
|
||||
- LCP
|
||||
- TBT
|
||||
- INP (om rapporteras)
|
||||
- Transfer size / antal requests
|
||||
3. Dokumentera nuläge och tröskelvärden före ändringar.
|
||||
|
||||
### Acceptanskriterier Fas 1
|
||||
- Baseline-tabell finns med mätvärden från 3 körningar per miljö.
|
||||
- Samma URL, samma nätprofil och samma emulering används konsekvent.
|
||||
|
||||
---
|
||||
|
||||
## Fas 2: Lågriskfixar med hög nytta (dag 1-2)
|
||||
|
||||
### 2.1 Index och metadata (`flutter/web/index.html`)
|
||||
- Lägg till `lang="sv"` på `<html>`.
|
||||
- Lägg till relevant `meta description` för appens huvudsakliga nytta.
|
||||
- Behåll `viewport` som den är (ingen ändring behövs kring zoom-blockering).
|
||||
|
||||
### 2.2 Caddy-headerhygien (`flutter/Caddyfile`)
|
||||
- Behåll `encode gzip`.
|
||||
- Lägg till explicita cache-headers för hashade statiska assets (js/wasm/fonts) med lång TTL och immutable.
|
||||
- Lägg till kortare/konservativ cache för `index.html` så nya deploys slår igenom snabbt.
|
||||
- Lägg till säkerhetsheaders som är kompatibla med Flutter-web (minst grundnivå: `X-Content-Type-Options`, `X-Frame-Options`, `Referrer-Policy`).
|
||||
|
||||
### 2.3 Byggoptimering (`flutter/Dockerfile`)
|
||||
- Utvärdera `--no-source-maps` i produktionsbuild för mindre artifact-storlek.
|
||||
- Säkerställ att eventuella ändringar inte påverkar felsökning i miljö där sourcemaps behövs.
|
||||
|
||||
### Acceptanskriterier Fas 2
|
||||
- Lighthouse visar förbättring i minst två nyckelmått (t.ex. LCP/TBT).
|
||||
- Ingen regress i appstart, routning eller API-proxy `/api/*`.
|
||||
- Build pipeline passerar oförändrat i Docker.
|
||||
|
||||
---
|
||||
|
||||
## Fas 3: Prestandaexperiment med tydlig rollback (dag 2-4)
|
||||
|
||||
### 3.1 Rendering-strategi (CanvasKit vs HTML/Skwasm)
|
||||
- Kör A/B-test av rendererstrategi i en separat branch/buildvariant.
|
||||
- Mät skillnad i initial transfer size, LCP och renderingkvalitet på kritiska vyer.
|
||||
- Beslut endast baserat på mätdata + visuell/regressionskontroll.
|
||||
|
||||
### 3.2 Deferred loading i tunga featureflöden
|
||||
- Identifiera kandidater för deferred imports (exempel: import/admin-vyer med hög kodvikt).
|
||||
- Introducera gradvis och validera att navigation inte blir ryckig.
|
||||
|
||||
### 3.3 Bootstrap-laddning
|
||||
- Behåll asynkron laddning i `index.html` om mätning visar bäst resultat.
|
||||
- Undvik manuella hacks som injicerar canvaskit-script ad hoc utan evidens i mätning.
|
||||
|
||||
### Acceptanskriterier Fas 3
|
||||
- Minst 15-25% förbättring i TBT eller tydlig minskning i JS-exekveringstid jämfört med baseline.
|
||||
- Ingen funktionell regress i kärnflöden (login, inventarie, recept, import).
|
||||
|
||||
---
|
||||
|
||||
## Fas 4: Tillgänglighet (A11y) med repo-fokus (dag 3-5)
|
||||
1. Inventera ikonknappar och actions i Flutter-kod och säkra `tooltip`/`semanticsLabel` där det saknas.
|
||||
2. Lägg till/justera `Semantics` för centrala actions (import, spara, ta bort, navigering).
|
||||
3. Verifiera tangentbordsnavigering i webbläsare för huvudflöden.
|
||||
|
||||
### Acceptanskriterier Fas 4
|
||||
- Lighthouse Accessibility förbättras mätbart.
|
||||
- Inga nya fokusfällor eller förlorad keyboard-navigering introduceras.
|
||||
|
||||
---
|
||||
|
||||
## Fas 5: SEO-minimum för app-shell (dag 4-5)
|
||||
1. Säkerställ att titel och meta description är korrekta för startsidan.
|
||||
2. Besluta om `robots.txt` och `sitemap.xml` ska hanteras i Flutter-web, Caddy eller upstream-domänkonfiguration (inte antagande).
|
||||
3. Implementera endast den väg som matchar faktisk domänrouting i drift.
|
||||
|
||||
### Acceptanskriterier Fas 5
|
||||
- Lighthouse SEO får förbättring från baseline.
|
||||
- Robots/sitemap-lösning är verifierad mot faktisk driftarkitektur.
|
||||
|
||||
---
|
||||
|
||||
## Fas 6: Säkerhet och CSP (dag 5)
|
||||
1. Introducera en pragmatisk CSP för Flutter-web i Caddy med minsta nödvändiga undantag.
|
||||
2. Testa särskilt att Flutter bootstrap, API-anrop och ev. externa resurser fungerar.
|
||||
3. Strama åt policyn iterativt istället för en aggressiv engångspolicy.
|
||||
|
||||
### Acceptanskriterier Fas 6
|
||||
- Säkerhetsheaders levereras korrekt från Flutter-Caddy.
|
||||
- Ingen blockerad kärnfunktion pga CSP.
|
||||
|
||||
---
|
||||
|
||||
## Prioriterad genomförandeordning
|
||||
1. Fas 1 (baseline)
|
||||
2. Fas 2 (lågriskfixar)
|
||||
3. Re-mätning
|
||||
4. Fas 3 (prestandaexperiment)
|
||||
5. Fas 4 (tillgänglighet)
|
||||
6. Fas 5 (SEO-minimum)
|
||||
7. Fas 6 (CSP hardening)
|
||||
8. Slutlig Lighthouse-jämförelse och dokumentation
|
||||
|
||||
## Definition of Done
|
||||
- Reproducerbar före/efter-mätning finns dokumenterad.
|
||||
- Performance, Accessibility och SEO har förbättrats jämfört med baseline.
|
||||
- Inga regressioner i Docker/Caddy-flödet eller appens kärnflöden.
|
||||
- Åtgärderna är anpassade till faktisk stack (Flutter + Caddy), inte generiska Nginx-råd.
|
||||
|
||||
## Konkreta filer som sannolikt berörs vid implementation
|
||||
- `flutter/web/index.html`
|
||||
- `flutter/Caddyfile`
|
||||
- `flutter/Dockerfile`
|
||||
- ev. flera Flutter-vyer med knapp-/ikonsemantik under `flutter/lib/**`
|
||||
|
||||
## Risker och motåtgärder
|
||||
- **Risk:** För aggressiv CSP bryter Flutter-bootstrap.
|
||||
**Motåtgärd:** Iterativ policy + verifiering efter varje ändring.
|
||||
- **Risk:** Rendererbyte förbättrar vikt men försämrar visual fidelity.
|
||||
**Motåtgärd:** A/B-test med tydliga rollback-kriterier.
|
||||
- **Risk:** Cache-policy gör deploys "stale".
|
||||
**Motåtgärd:** Lång cache endast för fingerprintade assets, kort cache för `index.html`.
|
||||
@@ -0,0 +1,706 @@
|
||||
🚨 Kritiska Problem (Prioritet 1: Fixa Omedelbart)
|
||||
Dessa problem påverkar användarupplevelsen mest och bör åtgärdas först.
|
||||
|
||||
1. Prestanda: Långsam inladdning & Stora Filer
|
||||
🔴 Problem:
|
||||
|
||||
Total storlek: 2,978 KiB (för stor för en webbapp).
|
||||
|
||||
/main.dart.js: 1,216 KiB (Flutter’s kompilade JavaScript).
|
||||
canvaskit.wasm: 1,592 KiB (Flutter’s CanvasKit-renderare).
|
||||
MaterialIcons-Regular.otf: 9.8 KiB (ikoner).
|
||||
|
||||
Largest Contentful Paint (LCP) misslyckades (tidsgräns överskreds).
|
||||
Total Blocking Time (TBT) misslyckades (långa JavaScript-uppgifter blockerar huvudtråden).
|
||||
JavaScript-exekveringstid: 1.8s (för långt).
|
||||
🟢 Lösningar:
|
||||
A. Optimera Flutter för Web
|
||||
Flutter-webbappar är tyngre än traditionella webbappar på grund av CanvasKit. Här är hur du minskar storleken:
|
||||
|
||||
|
||||
Använd HTML-renderaren istället för CanvasKit
|
||||
|
||||
CanvasKit ger bättre grafik men är mycket tyngre.
|
||||
Ändra i index.html:
|
||||
html
|
||||
Copy
|
||||
|
||||
<script>
|
||||
window.flutterConfiguration = {
|
||||
renderMode: "html", // Istället för "canvas" (CanvasKit)
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
Fördel: Minskar storleken med ~1.5MB (WASM-filen laddas inte).
|
||||
Nackdel: Vissa avancerade animationer/efekter fungerar inte lika bra.
|
||||
|
||||
|
||||
Aktivera komprimering (GZIP/Brotli)
|
||||
|
||||
Din server skickar okomprimerade filer.
|
||||
Lösning:
|
||||
|
||||
Nginx: Lägg till i konfigurationen:
|
||||
nginx
|
||||
Copy
|
||||
|
||||
gzip on;
|
||||
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
|
||||
brotli on;
|
||||
brotli_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
|
||||
|
||||
|
||||
|
||||
|
||||
Apache: Använd mod_deflate eller mod_brotli.
|
||||
Cloudflare: Aktivera "Brotli Compression" i inställningarna.
|
||||
|
||||
|
||||
|
||||
Cachea statiska resurser
|
||||
|
||||
Problem: /main.dart.js och flutter_bootstrap.js har ingen cache-TTL.
|
||||
Lösning: Lägg till Cache-Control-headers:
|
||||
nginx
|
||||
Copy
|
||||
|
||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|wasm|otf)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Besparing: ~1.2MB vid upprepade besök.
|
||||
|
||||
|
||||
Dela upp JavaScript-koden (Code Splitting)
|
||||
|
||||
Flutter laddar allt i en stor fil (main.dart.js).
|
||||
Lösning: Använd deferred imports för att ladda funktioner på begäran:
|
||||
dart
|
||||
Copy
|
||||
|
||||
// I din Dart-kod:
|
||||
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
|
||||
import 'package:my_app/receipt_import.dart' deferred as receipt_import;
|
||||
|
||||
// Ladda endast när behövs:
|
||||
Future<void> loadReceiptImport() async {
|
||||
await receipt_import.loadLibrary();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Effekt: Minskar initial laddningstid.
|
||||
|
||||
|
||||
Optimera bilder
|
||||
|
||||
Problem: Bilder laddas inte optimalt.
|
||||
Lösning:
|
||||
|
||||
Använd flutter_image_compress för att komprimera bilder innan uppladdning.
|
||||
För webb: Använd <picture> med srcset för responsiva bilder.
|
||||
Exempel:
|
||||
html
|
||||
Copy
|
||||
|
||||
<picture>
|
||||
<source srcset="image-480w.jpg" media="(max-width: 600px)">
|
||||
<source srcset="image-800w.jpg" media="(max-width: 1200px)">
|
||||
<img src="image-1200w.jpg" alt="Receptbild">
|
||||
</picture>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
B. Förbättra Laddningsordningen
|
||||
|
||||
Fördröj laddning av icke-kritiska resurser
|
||||
|
||||
Ladda canvaskit.wasm efter att sidan har renderats:
|
||||
html
|
||||
Copy
|
||||
|
||||
<script>
|
||||
window.addEventListener('load', () => {
|
||||
const script = document.createElement('script');
|
||||
script.src = 'https://www.gstatic.com/canvaskit/v1/chromium/canvaskit.wasm';
|
||||
document.body.appendChild(script);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Använd preload för kritiska resurser
|
||||
|
||||
Lägg till i <head>:
|
||||
html
|
||||
Copy
|
||||
|
||||
<link rel="preload" href="/main.dart.js" as="script">
|
||||
<link rel="preload" href="/flutter_bootstrap.js" as="script">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
2. Tillgänglighet: Grundläggande Problem
|
||||
🔴 Problem:
|
||||
|
||||
[user-scalable="no"] i viewport-meta-taggen
|
||||
|
||||
Varför det är dåligt: Användare med nedsatt syn kan inte zooma in.
|
||||
Lösning: Ta bort user-scalable="no":
|
||||
html
|
||||
Copy
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Saknas alt-texter på bilder
|
||||
|
||||
Problem: Skärmläsare kan inte beskriva bilder.
|
||||
Lösning: Lägg till alt-texter i Flutter:
|
||||
dart
|
||||
Copy
|
||||
|
||||
Image.network(
|
||||
'https://example.com/recept.jpg',
|
||||
semanticLabel: 'Bild på lasagne med ost och tomatsås', // Alt-text
|
||||
),
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Saknas lang-attribut
|
||||
|
||||
Problem: Skärmläsare vet inte vilket språk sidan använder.
|
||||
Lösning: Lägg till i <html>:
|
||||
html
|
||||
Copy
|
||||
|
||||
<html lang="sv">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
3. SEO: Grundläggande Problem
|
||||
🔴 Problem:
|
||||
|
||||
Saknas meta description
|
||||
|
||||
Varför det är dåligt: Sökmotorer visar ingen beskrivning i resultaten.
|
||||
Lösning: Lägg till i <head>:
|
||||
html
|
||||
Copy
|
||||
|
||||
<meta name="description" content="Upptäck och lagra dina recept. Importera kvitton och håll koll på ditt kylskåp med vår smarta app.">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Ogiltig robots.txt
|
||||
|
||||
Problem: Din robots.txt innehåller HTML-kod istället för korrekt syntax.
|
||||
Lösning: Skapa en korrekt robots.txt:
|
||||
text
|
||||
Copy
|
||||
|
||||
User-agent: *
|
||||
Allow: /
|
||||
Sitemap: https://recept.gynther.se/sitemap.xml
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
⚠️ Viktiga Förbättringar (Prioritet 2: Fixa Inom 1-2 Veckor)
|
||||
Dessa problem påverkar användarupplevelsen och SEO, men är inte lika kritiska.
|
||||
|
||||
1. Prestanda: JavaScript & Rendering
|
||||
🟡 Problem:
|
||||
|
||||
Lång JavaScript-exekveringstid (1.8s)
|
||||
|
||||
Orsak: Flutter’s main.dart.js tar 1.7s att parsas och exekveras.
|
||||
|
||||
Minify CSS/JS misslyckades
|
||||
|
||||
Flutter genererar redan minifierad kod, men du kan optimera vidare.
|
||||
|
||||
🟢 Lösningar:
|
||||
|
||||
|
||||
Aktivera Flutter’s --release flagga
|
||||
|
||||
Bygg appen med:
|
||||
bash
|
||||
Copy
|
||||
|
||||
flutter build web --release
|
||||
|
||||
|
||||
|
||||
|
||||
Detta genererar optimerad och minifierad kod.
|
||||
|
||||
|
||||
Använd flutter build web --no-source-maps
|
||||
|
||||
Source maps ökar filstorleken. Ta bort dem i produktion:
|
||||
bash
|
||||
Copy
|
||||
|
||||
flutter build web --no-source-maps
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Fördröj laddning av icke-kritisk JavaScript
|
||||
|
||||
Använd defer eller async för skript som inte behövs omedelbart:
|
||||
html
|
||||
Copy
|
||||
|
||||
<script src="/flutter_bootstrap.js" defer></script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reducera DOM-storlek
|
||||
|
||||
Problem: Din sida har 21 element med en max-djup på 7 (acceptabelt, men kan optimeras).
|
||||
Lösning: Undvik onödiga Container-widgets i Flutter. Använd const widgets där möjligt.
|
||||
|
||||
|
||||
2. Tillgänglighet: Interaktiva Element
|
||||
🟡 Problem:
|
||||
|
||||
Saknas focus-indikatorer
|
||||
|
||||
Användare som navigerar med tangentbord ser inte vilka element som är fokuserade.
|
||||
|
||||
Saknas aria-label på anpassade knappar
|
||||
|
||||
Skärmläsare vet inte vad knapparna gör.
|
||||
|
||||
🟢 Lösningar:
|
||||
|
||||
Lägg till focus-stilar i CSS
|
||||
|
||||
Exempel:
|
||||
css
|
||||
Copy
|
||||
|
||||
button:focus, [tabindex="0"]:focus {
|
||||
outline: 2px solid #4285F4;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Använd semantics i Flutter för tillgänglighet
|
||||
|
||||
Exempel för en knapp:
|
||||
dart
|
||||
Copy
|
||||
|
||||
Semantics(
|
||||
label: 'Importera kvitto',
|
||||
button: true,
|
||||
child: ElevatedButton(
|
||||
onPressed: () => _importReceipt(),
|
||||
child: Text('Importera'),
|
||||
),
|
||||
),
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Lägg till aria-label på ikonknappar
|
||||
|
||||
Exempel:
|
||||
dart
|
||||
Copy
|
||||
|
||||
IconButton(
|
||||
icon: Icon(Icons.upload),
|
||||
onPressed: () => _uploadFile(),
|
||||
tooltip: 'Ladda upp kvitto', // Visas på hover
|
||||
semanticsLabel: 'Ladda upp kvitto', // För skärmläsare
|
||||
),
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
3. Säkerhet: Content Security Policy (CSP)
|
||||
🟡 Problem:
|
||||
|
||||
Saknas CSP-header
|
||||
|
||||
Din sida är sårbar för XSS-attacker (Cross-Site Scripting).
|
||||
|
||||
🟢 Lösning:
|
||||
Lägg till en stark CSP i din serverkonfiguration:
|
||||
nginx
|
||||
Copy
|
||||
|
||||
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://www.gstatic.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https://*.gstatic.com; font-src 'self' https://fonts.gstatic.com; connect-src 'self' https://recept.gynther.se; frame-src 'none'; object-src 'none';";
|
||||
|
||||
|
||||
|
||||
|
||||
Förklaring:
|
||||
|
||||
script-src: Tillåter skript från self och gstatic.com (för Flutter).
|
||||
unsafe-inline: Nödvändigt för Flutter (men försök minska användningen).
|
||||
img-src: Tillåter bilder från self och gstatic.com.
|
||||
|
||||
|
||||
📌 Mindre Problem (Prioritet 3: Fixa När Tid Finns)
|
||||
Dessa är förbättringar som inte är kritiska men kan förbättra UX och SEO.
|
||||
|
||||
1. Prestanda: Bildoptimering
|
||||
|
||||
Problem: Bilder laddas utan width och height (orsakar layout shifts).
|
||||
Lösning: Använd Image.network med explicit storlek:
|
||||
dart
|
||||
Copy
|
||||
|
||||
Image.network(
|
||||
'https://example.com/recept.jpg',
|
||||
width: 300,
|
||||
height: 200,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
2. SEO: Structured Data
|
||||
|
||||
Problem: Saknas schema.org-markup för recept (gör att Google kan visa "rich results").
|
||||
Lösning: Lägg till JSON-LD i <head>:
|
||||
html
|
||||
Copy
|
||||
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "Recipe",
|
||||
"name": "Lasagne",
|
||||
"author": {
|
||||
"@type": "Person",
|
||||
"name": "Nils-Johan Gynther"
|
||||
},
|
||||
"datePublished": "2026-05-21",
|
||||
"description": "En klassisk lasagne med köttfärs och bechamelsås.",
|
||||
"prepTime": "PT30M",
|
||||
"cookTime": "PT45M",
|
||||
"totalTime": "PT1H15M",
|
||||
"recipeYield": "4 portioner",
|
||||
"recipeCategory": "Middag",
|
||||
"recipeCuisine": "Italiensk",
|
||||
"keywords": "lasagne, pasta, köttfärs",
|
||||
"recipeIngredient": ["500g köttfärs", "250g ost", "1 paket lasagneplattor"],
|
||||
"recipeInstructions": [
|
||||
{
|
||||
"@type": "HowToStep",
|
||||
"name": "Börja med att fräsa köttfärsen.",
|
||||
"text": "Fräs köttfärsen i en stekpanna tills den är genomstekt."
|
||||
},
|
||||
{
|
||||
"@type": "HowToStep",
|
||||
"name": "Lagra lasagnen i ugnen.",
|
||||
"text": "Skikta lasagneplattor, köttfärs och sås i en ugnsform. Grädda i 45 minuter på 200°C."
|
||||
}
|
||||
]
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
Effekt: Google kan visa receptkort i sökresultaten (ökad klickfrekvens).
|
||||
|
||||
3. Tillgänglighet: Logisk Tab-Order
|
||||
|
||||
Problem: Användare kan tabba till element som är dolda eller off-screen.
|
||||
Lösning: Använd FocusableAction i Flutter för att kontrollera tab-order:
|
||||
dart
|
||||
Copy
|
||||
|
||||
FocusableActionDetector(
|
||||
autofocus: true,
|
||||
onFocusChange: (hasFocus) {
|
||||
if (hasFocus) {
|
||||
// Hantera focus
|
||||
}
|
||||
},
|
||||
child: MyWidget(),
|
||||
),
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
📊 Sammanfattning av Prioriteringar
|
||||
|
||||
|
||||
|
||||
|
||||
Prioritet
|
||||
Problem
|
||||
Lösning
|
||||
Tidsuppskattning
|
||||
Impact
|
||||
|
||||
|
||||
|
||||
|
||||
1 (Kritisk)
|
||||
Stora filer (2.9MB)
|
||||
Byt till HTML-renderare, aktivera GZIP, cachea resurser
|
||||
1-2 timmar
|
||||
⭐⭐⭐⭐⭐ (Hög)
|
||||
|
||||
|
||||
1 (Kritisk)
|
||||
Lång JavaScript-exekvering (1.8s)
|
||||
Code splitting, defer non-critical JS
|
||||
2-4 timmar
|
||||
⭐⭐⭐⭐⭐ (Hög)
|
||||
|
||||
|
||||
1 (Kritisk)
|
||||
user-scalable="no"
|
||||
Ta bort från viewport-meta-taggen
|
||||
5 minuter
|
||||
⭐⭐⭐⭐ (Hög)
|
||||
|
||||
|
||||
1 (Kritisk)
|
||||
Saknas meta description
|
||||
Lägg till i
|
||||
5 minuter
|
||||
⭐⭐⭐ (Medel)
|
||||
|
||||
|
||||
1 (Kritisk)
|
||||
Ogiltig robots.txt
|
||||
Skapa korrekt robots.txt
|
||||
10 minuter
|
||||
⭐⭐⭐ (Medel)
|
||||
|
||||
|
||||
2 (Viktig)
|
||||
Saknas CSP-header
|
||||
Lägg till i serverkonfigurationen
|
||||
1 timme
|
||||
⭐⭐⭐⭐ (Hög)
|
||||
|
||||
|
||||
2 (Viktig)
|
||||
Saknas alt-texter
|
||||
Lägg till semanticLabel i Flutter
|
||||
1-2 timmar
|
||||
⭐⭐⭐ (Medel)
|
||||
|
||||
|
||||
2 (Viktig)
|
||||
Lång DOM-parsning
|
||||
Minska onödiga widgets
|
||||
1-2 timmar
|
||||
⭐⭐ (Låg)
|
||||
|
||||
|
||||
3 (Mindre)
|
||||
Saknas structured data
|
||||
Lägg till JSON-LD för recept
|
||||
1 timme
|
||||
⭐⭐ (Låg)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
🛠️ Konkreta Åtgärder (Steg-för-Steg)
|
||||
Steg 1: Fixa Prestanda (1-2 dagar)
|
||||
|
||||
Byt till HTML-renderare (sparar ~1.5MB):
|
||||
|
||||
Ändra index.html som visat ovan.
|
||||
|
||||
Aktivera GZIP/Brotli på servern.
|
||||
Cachea statiska filer (1 år för JS/CSS).
|
||||
Bygg appen med --release:
|
||||
bash
|
||||
Copy
|
||||
|
||||
flutter build web --release --no-source-maps
|
||||
|
||||
|
||||
|
||||
|
||||
Ladd upp den optimerade versionen till din server.
|
||||
|
||||
Steg 2: Fixa Tillgänglighet (1 dag)
|
||||
|
||||
Ta bort user-scalable="no" från viewport.
|
||||
Lägg till lang="sv" i <html>.
|
||||
Lägg till alt-texter på alla bilder.
|
||||
Lägg till semanticsLabel på knappar och ikoner.
|
||||
|
||||
Steg 3: Fixa SEO (1 dag)
|
||||
|
||||
Lägg till meta description.
|
||||
Fixa robots.txt.
|
||||
Lägg till structured data för recept.
|
||||
|
||||
Steg 4: Säkerhet (1 timme)
|
||||
|
||||
Lägg till CSP-header i serverkonfigurationen.
|
||||
|
||||
📈 Förväntade Resultat
|
||||
|
||||
|
||||
|
||||
|
||||
Mätning
|
||||
Nuvarande
|
||||
Efter Fixar
|
||||
Förbättring
|
||||
|
||||
|
||||
|
||||
|
||||
Total storlek
|
||||
2,978 KiB
|
||||
~500-800 KiB
|
||||
-70%
|
||||
|
||||
|
||||
Laddningstid (LCP)
|
||||
Timeout
|
||||
< 2.5s
|
||||
✅ Fixad
|
||||
|
||||
|
||||
JavaScript-exekvering
|
||||
1.8s
|
||||
< 1s
|
||||
-45%
|
||||
|
||||
|
||||
Tillgänglighet
|
||||
82/100
|
||||
95+/100
|
||||
+15 poäng
|
||||
|
||||
|
||||
SEO
|
||||
6/10
|
||||
9/10
|
||||
+50%
|
||||
|
||||
|
||||
Säkerhet
|
||||
Risk för XSS
|
||||
Skyddad
|
||||
✅ Fixad
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
🔍 Hur du Testar Förbättringarna
|
||||
|
||||
Kör Lighthouse igen i Chrome DevTools (F12 → "Lighthouse").
|
||||
Testa på mobil (använd Chrome’s "Device Toolbar").
|
||||
Använd WebPageTest (webpagetest.org) för att mäta prestanda globalt.
|
||||
Testa tillgänglighet med:
|
||||
|
||||
WAVE Evaluation Tool
|
||||
Skärmläsare (t.ex. NVDA eller VoiceOver).
|
||||
|
||||
|
||||
💡 Extra Tips för Flutter-Webbappar
|
||||
|
||||
|
||||
Använd flutter build web --target=lib/main_deferred.dart
|
||||
|
||||
Förbättrar code splitting.
|
||||
|
||||
|
||||
Ladda Flutter asynkront
|
||||
|
||||
Lägg till async i <script>-taggen:
|
||||
html
|
||||
Copy
|
||||
|
||||
<script src="/flutter_bootstrap.js" async></script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Använd flutter_tex för text-rendering
|
||||
|
||||
Minskar beroendet av CanvasKit.
|
||||
|
||||
|
||||
Override Flutter’s default fonts
|
||||
|
||||
Använd systemtypsnitt för snabbare laddning:
|
||||
css
|
||||
Copy
|
||||
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
src: local('Roboto'), local('Roboto-Regular');
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
🎯 Sammanfattning: Vad du ska göra nu
|
||||
|
||||
Fixa de kritiska prestandaproblemen (HTML-renderare, GZIP, caching).
|
||||
Fixa tillgänglighetsproblemen (user-scalable, alt-texter, lang).
|
||||
Fixa SEO (meta description, robots.txt).
|
||||
Lägg till säkerhetsheaders (CSP).
|
||||
Testa och iterera med Lighthouse.
|
||||
@@ -0,0 +1,118 @@
|
||||
# Plan: Anpassad multiplattformsplan for recipe-app
|
||||
|
||||
## Mal
|
||||
Gora Flutter-klienten i `flutter/` till en riktig multiplattformsklient (web + Android + iOS) utan att bryta befintligt webbflode via Docker/Caddy, och med tydlig miljohantering for API-anrop pa mobil.
|
||||
|
||||
## Nulagesanalys (projektanpassad)
|
||||
- Flutter-projektet ar i praktiken web-only just nu: `flutter/` saknar `android/` och `ios/`.
|
||||
- Webbbygget ar redan etablerat och stabilt via Docker:
|
||||
- `flutter/Dockerfile` bygger `flutter build web --dart-define=API_BASE_URL=/api`.
|
||||
- `compose.flutter.yml` och `flutter/Caddyfile` proxar `/api/*` till `recipe-api:8080`.
|
||||
- API-basurl hanteras redan med `--dart-define` (bra grund for multiplattform):
|
||||
- `flutter/lib/core/api/api_client.dart`
|
||||
- `flutter/lib/features/import/data/import_repository.dart`
|
||||
- Token-lagring ar forberedd for mobil men inte implementerad:
|
||||
- `flutter/lib/core/platform/token_storage.dart`
|
||||
- `flutter/lib/core/platform/platform_providers.dart` (har TODO om secure storage).
|
||||
|
||||
## Viktig skillnad mot gamla planen
|
||||
- Ingen hardkodad `Config.apiUrl` med fasta domaner ska inforas som huvudlosning.
|
||||
- Projektet anvander redan `String.fromEnvironment('API_BASE_URL')`; vi behaller detta och utokar till mobil.
|
||||
- Befintlig Docker-setup for web ska inte ersattas, bara kompletteras med mobil-byggflode.
|
||||
|
||||
## Foreslagen implementation
|
||||
|
||||
### Fas 1: Aktivera plattformsstommar utan att rora webdeploy
|
||||
1. Skapa Android/iOS-mappar i `flutter/`:
|
||||
- `flutter create --platforms android,ios .`
|
||||
2. Verifiera att webfiler och befintliga Dart-filer inte overskrivs pa ett destruktivt satt.
|
||||
3. Bekrafta att Docker-webbygget fortfarande fungerar oforandrat.
|
||||
|
||||
**Leverabel:** Projektet innehaller `flutter/android/` och `flutter/ios/` samtidigt som webflodet ar intakt.
|
||||
|
||||
### Fas 2: Plattformsaker konfiguration av API-basurl
|
||||
1. Standardisera API-konfiguration kring en enda kontraktspunkt:
|
||||
- Behall `API_BASE_URL` via `--dart-define`.
|
||||
2. Satt tydliga miljoer:
|
||||
- Web i Docker: `API_BASE_URL=/api` (som idag).
|
||||
- Android emulator lokalt: t.ex. `http://10.0.2.2:8080/api` (vid lokal backend utan reverse proxy).
|
||||
- Fysisk mobil/test/prod: publik HTTPS-url (doman som ar natbar utanfor Docker).
|
||||
3. Se over alla direkta API-basar i Flutter-koden sa att de gar via samma pattern (inga hardkodade hostnamn).
|
||||
|
||||
**Leverabel:** Samma kodbas fungerar pa web och mobil genom miljoinjektering, inte forks av API-klient.
|
||||
|
||||
### Fas 3: Mobilanpassad auth/tokenlagring
|
||||
1. Implementera `SecureTokenStorage` med `flutter_secure_storage` for mobil.
|
||||
2. Uppdatera `platform_providers.dart` till plattformsval:
|
||||
- Web -> befintlig `WebTokenStorage`.
|
||||
- Android/iOS -> `SecureTokenStorage`.
|
||||
3. Verifiera att inloggning/logout/session beter sig lika mellan web och mobil.
|
||||
|
||||
**Leverabel:** JWT lagras sakert pa mobil, befintligt webbeteende bibehalls.
|
||||
|
||||
### Fas 4: UI- och UX-hardning for mindre skarmar
|
||||
1. Identifiera skarmar med hog informationsdensitet (admin/import/tabeller).
|
||||
2. Lagg in responsiva brytpunkter med `LayoutBuilder`/`MediaQuery` dar det behovs.
|
||||
3. Prioritera funktionellt minimum for mobil i forsta iteration:
|
||||
- Login
|
||||
- Inventarie
|
||||
- Receptlista
|
||||
- Grundlaggande importfloden
|
||||
4. Markera admin-tunga vyer som sekundara om de inte ar mobilkritiska i fas 1.
|
||||
|
||||
**Leverabel:** Nyckelfloden ar anvandbara pa telefon utan horisontell overflow eller blockerande layoutfel.
|
||||
|
||||
### Fas 5: Build- och releaseflode (utan att blanda ihop med Docker-runtime)
|
||||
1. Dokumentera separata kommandon:
|
||||
- Web (befintligt): Docker/compose.
|
||||
- Android: `flutter build apk --release` (och ev. `appbundle`).
|
||||
- iOS: `flutter build ios --release` (kraver macOS/Xcode).
|
||||
2. Behall principen: mobilappar kor inte i Docker; Docker far anvandas som byggmiljo dar det ar rimligt.
|
||||
3. Om CI ska byggas senare: separera web-jobb och mobil-jobb for tydlighet.
|
||||
|
||||
**Leverabel:** Reproducerbar byggprocess for web och mobil med tydlig ansvarsskillnad.
|
||||
|
||||
### Fas 6: Test- och verifieringsplan
|
||||
1. Statisk kvalitet:
|
||||
- `flutter analyze`
|
||||
- `flutter test`
|
||||
2. Plattformsverifiering:
|
||||
- Web via befintlig container
|
||||
- Android emulator + fysisk enhet
|
||||
- iOS simulator (pa macOS)
|
||||
3. Natverksverifiering:
|
||||
- Bekrafta att mobil kan na vald `API_BASE_URL` over HTTPS/CORS/proxyregler.
|
||||
4. Regression:
|
||||
- Inloggning, token-refresh/logout
|
||||
- CRUD i inventarie/recept
|
||||
- Importendpoints med storre payloads
|
||||
|
||||
**Leverabel:** Checklista med passerade verifieringspunkter innan distribution.
|
||||
|
||||
## Konkreta filer som sannolikt berors
|
||||
- `flutter/pubspec.yaml` (nytt beroende for secure storage)
|
||||
- `flutter/lib/core/platform/platform_providers.dart`
|
||||
- `flutter/lib/core/platform/token_storage.dart` (ev. endast kontraktsjustering)
|
||||
- Ny fil: `flutter/lib/core/platform/secure_token_storage.dart`
|
||||
- Mobilplattformar som genereras: `flutter/android/**`, `flutter/ios/**`
|
||||
- Dokumentation: `README.md` och/eller `TEKNISK_BESKRIVNING.md` (kommandon och miljoexempel)
|
||||
|
||||
## Risker och hantering
|
||||
- iOS-bygg kan inte verifieras i Windows/Linux-miljo -> hanteras med separat macOS-steg.
|
||||
- Hardkodade URL:er kan smyga sig in i featurekod -> hanteras med kodsok + central konfigpolicy.
|
||||
- UI-regression pa web vid responsiva andringar -> hanteras med web-regressionstest av kritiska vyer.
|
||||
|
||||
## Prioriterad ordning for implementation
|
||||
1. Fas 1 (plattformsstommar)
|
||||
2. Fas 2 (API-konfiguration)
|
||||
3. Fas 3 (secure token storage)
|
||||
4. Fas 6 del 1 (analyze/test tidigt)
|
||||
5. Fas 4 (mobil UI-hardning)
|
||||
6. Fas 5 + Fas 6 slutlig verifiering och dokumentation
|
||||
|
||||
## Definition of Done
|
||||
- Flutter-projektet bygger for web + android (och ios dar macOS finns).
|
||||
- Mobil och web anvander samma API-konfigmodell via `--dart-define`.
|
||||
- Mobil lagrar token sakert; webflodet ar oforandrat.
|
||||
- Minst nyckelfloden login/inventarie/recept/import ar verifierade pa mobil.
|
||||
- Dokumentationen beskriver exakt hur man bygger och kor respektive plattform i detta repo.
|
||||
Reference in New Issue
Block a user