# Web app-manifest

## Hvad det er

Et web app-manifest er et JSON-dokument, der beskriver et website som et
program: hvad det hedder, hvilken adresse det åbner på, hvilke adresser der
hører til det, hvordan vinduet skal se ud, og hvilke billeder der står for det.
Siden peger på filen med et `link`-element, hvis `rel` er `manifest`, og en
browser, der tilbyder installation, læser web app-manifestet for at afgøre, hvad
den installerede kopi skal være. W3C-specifikationen bag formatet kalder det
blot manifestet, og det gør de fleste i daglig tale også.

Filen er almindelig JSON. Den registrerede medietype er
`application/manifest+json`, og den registrerede filendelse er `.webmanifest`,
men specifikationen godtager enhver JSON-medietype, og mange sites serverer en
`manifest.json`. Alle medlemmer er valgfrie. Et lille manifest til et opdigtet
site med tidevandstabeller for en havn kan se sådan ud:

```json
{
    "name": "Havnens tidevandstabeller",
    "short_name": "Tidevand",
    "start_url": "/tidevand/",
    "display": "standalone",
    "background_color": "#ffffff",
    "icons": [
        {
            "src": "/ikoner/tidevand-512.png",
            "sizes": "512x512",
            "type": "image/png"
        }
    ]
}
```

Tre medlemmer bestemmer, hvad appen er, til forskel fra hvordan den ser ud.
`start_url` er den side, den installerede app åbner på. `scope` er de adresser,
der regnes for at høre til inde i appen; mangler det, tager specifikationen
start-URL'en og skærer filnavn, forespørgsel og fragment af, så eksemplet
ovenfor ejer alt under `/tidevand/`. `id` er appens identitet, og mangler det,
falder det tilbage på start-URL'en. Relative adresser i filen, til ikoner såvel
som til start-URL'en, opløses i forhold til manifestets egen URL og ikke i
forhold til den side, der linkede til det.

Resten handler om udseende. `display` beder om `fullscreen`, `standalone`,
`minimal-ui` eller `browser`, og en browser, der ikke kan imødekomme ønsket, går
ned ad listen i netop den rækkefølge. Hvert ikon kan have et `purpose`: `any`
som standard, `maskable` til et billede tegnet til at tåle beskæring eller
`monochrome` til et billede, hvis farver smides væk, så kun alfakanalen bruges.
`theme_color` farver vinduet omkring appen. En ledsagende W3C-note tilføjer
`description`, `categories` og `screenshots`, som installationsdialoger og
appbutikker kan vise.

## Hvorfor det betyder noget

Manifestet er den eneste del af en installation, sitet selv styrer.
Specifikationen overlader beslutningen til browseren: den må tilbyde
installation, og hver browser fastsætter sine egne betingelser, hvoraf nogle
kræver bestemte medlemmer. Et team leverer råmaterialet, og browseren afgør,
hvad der sker med det, så det nyttige spørgsmål handler mindre om, hvorvidt der
skal være et manifest, end om, hvad det lover.

Identiteten er det løfte, der lever længere end filen. Når en browser henter
manifestet igen, kræver specifikationen, at den bruger `id` til at afgøre, om
den nye fil gælder en app, der allerede er installeret. Fordi et manglende `id`
er lig med start-URL'en, skifter identiteten i stilhed, når startsiden flyttes,
og den nye fil beskriver så et andet program end det, der ligger på læserens
enhed. At låse `id` fast i den første udgivelse koster én linje og fjerner den
risiko.

Omfanget afgør, hvor appvinduet slutter. Navigation inden for `scope` bliver i
det installerede vindue. Et link, der fører ud, åbner stadig, men
specifikationen forventer, at browseren gør det synligt for læseren, at siden
ligger uden for appen, så et omfang, der er smallere end sitet, får almindelige
links mellem dets afsnit til at ligne udgange.

Ikonerne er dér, hvor det meste af den synlige skade sker. Hjemmeskærme og
appstartere beskærer ikoner til cirkler, afrundede firkanter og andre former, og
et logo, der fylder hele sit kvadrat, mister kanterne. Specifikationen definerer
sikkerhedszonen for et maskable ikon som en cirkel i midten med en radius på to
femtedele af ikonets størrelse, og alt uden for den kan blive skåret væk. Et
monokromt ikon beholder kun sin alfakanal, så et ikon tegnet på en
uigennemsigtig baggrund bliver til en massiv firkant.

Farvemedlemmerne rækker kortere, end de ser ud til. `background_color` er kun
ment til øjeblikket, før stylesheetet når frem, og specifikationen forbyder
browseren at bruge den, når stylesheetet er klar. Den farver det første billede,
og intet efter det.

## Sådan ser det ud i praksis

Dette site bygger sit manifest ud fra en skabelon i stedet for at vedligeholde
en fil i hånden. Hugo-konfigurationen registrerer `application/manifest+json`
med endelsen `webmanifest` og gør manifestet til et output af hvert sprogs
forside, så hvert sprog får sin egen fil, og hovedet på hver side linker til
filen for sit eget sprog. Konfigurationen noterer også hvorfor: én fælles kopi
kan kun angive én start-URL, og en dansk besøgende, der installerede sitet, fik
en app, der åbnede den engelske forside.

Skabelonen samler manifestet med Hugos `dict` og sender det gennem Hugos
JSON-encoder, så escaping og kommaer er encoderens ansvar, og et glemt komma når
aldrig ud. Hver ikonadresse starter ved roden af værten, og kommentaren i
skabelonen forklarer grunden: en relativ adresse i `/da/manifest.webmanifest`
ville blive opløst under `/da/`, og hvert ikon på dansk ville give 404. Selve
ikonerne læses fra disken i tre mapper, én pr. formål, med ti størrelser i hver
fra 36 til 512 pixel, og størrelsen aflæses i filnavnet, så en størrelse, som
generatoren tilføjer, dukker op i manifestet, uden at nogen skal rette i det.

`id` er låst fast til sprogets forsideadresse, og `scope` er hele originet, så
et link over i et andet sprog bliver i det installerede vindue. Skærmbilleder
navngives `<lang>-<form_factor>-<WxH>.webp`, og skabelonen aflæser alle tre
oplysninger i filnavnet: engelsk og dansk har hver ét bredt billede på 1.280
gange 800 og ét smalt på 780 gange 1.688. Et sprog uden skærmbilleder får slet
intet `screenshots`-medlem, fordi en tom liste ville påstå, at appen intet har
at vise.

[Kolofonen](/da/credits/) nævner W3C-specifikationen blandt de standarder, sitet
følger, og PWABuilder blandt de eksterne tjek, enhver selv kan køre: en tjeneste
på Microsofts servere, der gennemgår manifestet og alt det øvrige, som
installation afhænger af.

[Installationssiden](/da/install/) er læserens halvdel af den samme fil. Den
viser, hvor hver browser gemmer installationskommandoen, fortæller, at Apples
browsere aldrig byder sig til, og at desktopudgaven af Firefox overhovedet ikke
installerer webapps, og den afstemmer forventningerne: den installerede app er
et ikon og et selvstændigt vindue, og den kræver forbindelse, fordi offlinebrug
forudsætter en service worker.

## Hvor det går galt

**Alle ikoner giver 404 på en oversat sti.** En relativ `src` opløses i forhold
til manifestets URL, så et manifest, der serveres under et sprogpræfiks, beder
om ikoner under samme præfiks. Skriv ikonadresser fra værtens rod eller som
fulde URL'er.

**Den installerede app åbner på det forkerte sprog.** Ét manifest kan angive én
start-URL, så alle læsere installerer den samme forside. Server ét manifest pr.
sprog med sin egen `start_url` og sit eget `id`, og link til det passende fra
hver side.

**Installerede kopier overser det nye manifest.** Uden `id` er identiteten
start-URL'en, så når startsiden flyttes, får filen en ny identitet, og browseren
anvender den derefter ikke på den app, læserne allerede har. Sæt `id` før første
udgivelse, og lad det ligge bagefter.

**Hjemmeskærmen beskærer logoet.** Et ikon med formålet `any` placeres eller
beskæres, som platformen finder passende. Tilføj et `maskable` ikon, hvor alt
vigtigt ligger inden for den midterste cirkel, hvis radius er 40 % af ikonets
størrelse.

**Rettelser i manifestet gør ingen forskel.** HTML-standarden bruger kun det
første `link`-element, hvis `rel` indeholder `manifest`, så har et tema eller et
plugin sat sit eget link tidligere på siden, bliver den fil, der rettes i,
aldrig læst. Hold præcis ét manifestlink pr. side.

## Læs mere

- [Web Application Manifest](https://www.w3.org/TR/appmanifest/) — alle
  medlemmer, hvordan en browser behandler filen, og reglerne for `id`, `scope`
  og ikonformål.
- [Web App Manifest - Application Information](https://www.w3.org/TR/manifest-app-info/)
  — `description`, `categories` og `screenshots` med formfaktorer og etiketter.
- [IANA: application/manifest+json](https://www.iana.org/assignments/media-types/application/manifest+json)
  — registreringen af medietypen og filendelsen `.webmanifest`.
- [HTML Standard: link type manifest](https://html.spec.whatwg.org/multipage/links.html#link-type-manifest)
  — hvordan manifestlinket hentes, og hvorfor kun det første tæller.

<https://engineer.company/da/glossary/webmanifest/>
