69bcc3e342
- 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
707 lines
14 KiB
Markdown
707 lines
14 KiB
Markdown
đš 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.
|