Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: favicon, app icons and optional Web Manifest #1205

Merged
merged 32 commits into from
May 8, 2024
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
bd181a3
feat(favicon): :bento: manifest, (fav)icon(s)
RubenSibon Apr 5, 2024
50d3a5d
build(storybook): :wrench: add static dir to assets
RubenSibon Apr 6, 2024
3b06651
docs(story): :memo: favicon and app icon examples
RubenSibon Apr 6, 2024
0568ecb
docs(story): :recycle: valid jsonc in code block
RubenSibon Apr 6, 2024
4f6d011
refactor(manifest): :art: dedicated web manifest asset dir
RubenSibon Apr 8, 2024
50796b4
docs(favicon): :memo: improve documentation
RubenSibon Apr 8, 2024
c9db5ed
feat(favicon): :hammer: script to link, copy & insert assets
RubenSibon Apr 8, 2024
de8a591
build(pkgs): 🔧 pin pnpm and update lockfile
RubenSibon Apr 19, 2024
9ce807a
fix(asset): 🩹 remove whitespace from images
RubenSibon Apr 19, 2024
e1fbf88
feat(pwa): 🔧 manifest gen optional
RubenSibon Apr 19, 2024
6c48312
docs: 📝 textual improvements
RubenSibon Apr 19, 2024
9ed466e
Merge remote-tracking branch 'origin/develop' into feat/favicon-and-a…
RubenSibon Apr 19, 2024
7dac4ce
style(docs): :lipstick: add styles for figure galleries
RubenSibon Apr 19, 2024
5c1b756
ci(pkgs): :wrench: increase pnpm major version
RubenSibon Apr 19, 2024
286f8d1
Merge branch 'develop' of github.com:Amsterdam/design-system into fea…
RubenSibon Apr 19, 2024
4bc9214
Merge branch 'develop' into feat/DES-652-favicon-and-app-icons
RubenSibon Apr 19, 2024
990ba56
docs(favicon): :memo: textual edits
RubenSibon Apr 19, 2024
a299e7a
refactor(docs): :truck: rename post-install script
RubenSibon Apr 19, 2024
1ad46ea
Merge branch 'develop' into feat/DES-652-favicon-and-app-icons
VincentSmedinga Apr 24, 2024
b2af94a
Tweak visual design of figures, some naming, some docs
VincentSmedinga Apr 24, 2024
ef7592b
refactor: App name suggests max length
RubenSibon Apr 24, 2024
6c36560
refactor: Remove invalid comments
RubenSibon Apr 24, 2024
b514ff1
chore: Format object on multi lines
RubenSibon Apr 24, 2024
b3635dc
feat(favicon): :bento: add 48px size to favicon
RubenSibon May 2, 2024
46debca
style(asset): :zap: compress/resize PNGs further
RubenSibon May 2, 2024
8e2bd5c
Merge branch 'develop' of github.com:Amsterdam/design-system into fea…
RubenSibon May 2, 2024
5efc854
revert(script): :fire: remove asset linker script
RubenSibon May 2, 2024
dfd3538
feat(favicon): split favicon and pwa docs
RubenSibon May 2, 2024
d6fe8ae
style(svg): Remove clipping path
RubenSibon May 6, 2024
26a94f0
revert(pkgs): minimally require npm 8
RubenSibon May 6, 2024
81a662d
Merge branch 'develop' into feat/DES-652-favicon-and-app-icons
VincentSmedinga May 7, 2024
7c5b156
Merge branch 'develop' into feat/DES-652-favicon-and-app-icons
RubenSibon May 8, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"private": true,
"engines": {
"node": "^20",
"npm": "^8",
"npm": "^10",
dlnr marked this conversation as resolved.
Show resolved Hide resolved
"pnpm": "^9"
},
"volta": {
Expand Down
17,571 changes: 9,716 additions & 7,855 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions proprietary/assets/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,7 @@
# Assets

Assets for the City of Amsterdam

## Web app manifest, app icons and favicon

See the Design System documentation for an [overview and examples of the manifest and these icons](https://amsterdam.github.io/design-system/?path=/docs/docs-assets-favicon-app-icons--docs).
Binary file removed proprietary/assets/favicon.ico
Binary file not shown.
Binary file added proprietary/assets/favicon/apple-touch-icon.png
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found out why this image looks a bit more blurry than the others: it’s 140 pixels, not 180.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah right, the canvas should be 180px, but the image should be scaled to 140. I mixed that up.

VincentSmedinga marked this conversation as resolved.
Show resolved Hide resolved
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added proprietary/assets/favicon/favicon.ico
Binary file not shown.
Binary file added proprietary/assets/favicon/icon-192.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added proprietary/assets/favicon/icon-512.png
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you consider compressing these images? A quick try at e.g. tinypng.com indicates we could save over 60% in file size for the PNG’s.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I compressed them with the highest compression rate when exporting, but apparently much more was possible than that. I compressed them again with tinypng.com. Going from 3 and 9kB to a bit over 1 and 3kB respectively.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions proprietary/assets/favicon/icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions proprietary/assets/manifest/app.webmanifest
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also leave this file out if you remove it from the postinstall script.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With Vincent we decided to split the favicon and web app stuff.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So you'll leave this file here to be reviewed in the webapp PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, I was not thinking about splitting the PR, but only the documentation in this PR: one page about adding a (fav)icon to your website and one page about adding a web manifest and app icons to your web app.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not following you I think. If you split the favicon and the webapp stuff I would leave out all the webapp stuff in this PR, for cleanliness.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In hindsight, this should have been two PRs, but it seems we’re pretty close to merging. Splitting it up now feels like more work than gain.

Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "APP_NAME_FULL",
"short_name": "APP_NAME",
"icons": [
{
"src": "appicons/icon-192.png",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe app-icons everywhere as we use a capital I in AppIcons too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adjusted

"type": "image/png",
"sizes": "192x192"
},
{ "src": "appicons/icon-512.png", "type": "image/png", "sizes": "512x512" }
RubenSibon marked this conversation as resolved.
Show resolved Hide resolved
]
}
63 changes: 63 additions & 0 deletions proprietary/assets/postinstall.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#! /usr/bin/sh

APP_ICONS_DIR=appicons
WEB_MANIFEST_FILE=app.webmanifest

# In a default Vite React app the `public` directory will exist and `index.html` will be located in the root.
# For legacy React apps index.html may exist in the `public` directory. This script can handle those cases.
# For Next.js apps only the `public` folder will exist and the link tags will have to be added manually to `document.[j/t]sx`.
if [ -f "public/index.html" ]; then INDEX_HTML_PATH="index.html"; elif [ -f "index.html" ]; then INDEX_HTML_PATH="../index.html"; fi
if [ ! -d "public" ]; then mkdir -m 755 public; fi

cd public || exit

# Symbolic link the icons.
if [ ! -L $APP_ICONS_DIR ] && [ ! -d $APP_ICONS_DIR ]; then
ln -s ../node_modules/@amsterdam/design-system-assets/favicon $APP_ICONS_DIR
fi

# Generate the web manifest if an app name was provided and the file doesn't exist.
if [ "${1}" ] && [ ! -f $WEB_MANIFEST_FILE ]; then
cp ../node_modules/@amsterdam/design-system-assets/manifest/app.webmanifest $WEB_MANIFEST_FILE

sed -i .bak -e "s/APP_NAME_FULL/${1}/g" $WEB_MANIFEST_FILE

if [ "${2}" ]; then sed -i .bak -e "s/APP_NAME/${2}/g" $WEB_MANIFEST_FILE; fi

printf "The 'app.webmanifest' has been generated in the project with the provided name(s). Please edit it with your app's metadata.\n\n"
fi

# If an index.html file does not exist, inform the user that the link tags could not be inserted and exit the script.
if [ ! $INDEX_HTML_PATH ]; then
cd ..
printf "Finished copying and symlinking the assets, but could not insert the link tags in a non-existant index.html file. Please adjust your HTML Head tags manually.\n\n"
echo "If you're using Next.js read the documentation on a Custom Document: https://nextjs.org/docs/pages/building-your-application/routing/custom-document"
exit
fi

# Insert the icon link tags in the index.html file.
if [ -L $APP_ICONS_DIR ] && [ -d $APP_ICONS_DIR ] && ! grep -q '<link .* href=".*\.ico"' $INDEX_HTML_PATH; then
sed -i .bak -e '/<link rel="icon" type="image\//d' $INDEX_HTML_PATH

# Add the following links to your web app's <head> to get the City of Amsterdam's branding
# on your web page with an old-fashioned favicon and modern app icons.
# Adjust the route to the assets accordingly.
sed -i .bak -e 's/^ <\/head>$/\
<link rel="icon" href="public\/appicons\/favicon.ico" size="16x16 32x32 64x64" \/>\
<link rel="icon" href="public\/appicons\/icon.svg" type="image\/svg+xml" \/>\
<link rel="apple-touch-icon" href="public\/appicons\/apple-touch-icon.png" \/>\
<\/head>/g' $INDEX_HTML_PATH
fi

# Add the web manifest link to the index.html file.
if [ -f $WEB_MANIFEST_FILE ] && [ "${1}" ] && ! grep -q '<link rel="manifest" href="public\/app.webmanifest" \/>' $INDEX_HTML_PATH; then
# Add the following link to your web app's <head> to get the City of Amsterdam's branding
# on your web app with a web manifest and app icons.
# Adjust the route to the manifest accordingly.
sed -i .bak -e 's/^ <\/head>$/ <link rel="manifest" href="public\/app.webmanifest" \/>\
<\/head>/g' $INDEX_HTML_PATH
fi

cd ..

echo "Finished copying and symlinking the assets. You can edit the copied Web Manifest and inserted <link> tags and commit those changes to the repository."
1 change: 1 addition & 0 deletions storybook/config/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const config: StorybookConfig = {
docs: {
autodocs: true,
},
staticDirs: ['../../proprietary/assets'],
typescript: {
reactDocgen: 'react-docgen-typescript',
},
Expand Down
23 changes: 23 additions & 0 deletions storybook/config/preview-body.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<style>
.ams-docs-figure {
display: flex;
gap: 0.25rem;
margin: 0; /* stylelint-disable-line */
padding: 0; /* stylelint-disable-line */
}
Expand All @@ -8,6 +10,27 @@
.ams-docs-figure > img {
display: block;
max-width: 100%;
outline: 1px dashed #e8e8e8;
}

.ams-docs-figure > figcaption {
text-align: center;
}

.ams-docs-gallery {
align-items: flex-end;
display: flex;
gap: 2rem;
margin-block: 1.5rem;
}

.ams-docs-gallery .ams-docs-figure {
display: flex;
flex-direction: column;
align-items: center;
margin-inline: 0;
width: -moz-fit-content;
width: fit-content;
}

.ams-docs-paragraph {
Expand Down
7 changes: 6 additions & 1 deletion storybook/config/storybook-overrides.css
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
.sbdocs-content.sbdocs-content > :is(ol, ul) li,
.sbdocs-content.sbdocs-content > div:not(.sb-unstyled) > :is(ol, ul) li,
.sbdocs-content.sbdocs-content > table:not(.sb-unstyled) :is(td, th),
.sbdocs-content.sbdocs-content > div:not(.sb-unstyled) > table:not(.sb-unstyled) :is(td, th) {
.sbdocs-content.sbdocs-content > div:not(.sb-unstyled) > table:not(.sb-unstyled) :is(td, th),
.sbdocs-content.sbdocs-content > div:not(.sb-unstyled) figcaption {
color: #000000;
font-family: "Amsterdam Sans", Arial, sans-serif;
}
Expand Down Expand Up @@ -103,4 +104,8 @@
font-size: 1rem;
}

.sbdocs-content.sbdocs-content > div:not(.sb-unstyled) figcaption {
font-size: 0.875rem;
}

/* stylelint-enable */
166 changes: 166 additions & 0 deletions storybook/src/docs/app-icon.docs.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import { Meta } from "@storybook/blocks";
import { AppleTouchIcon, Favicon, SvgIcon, WebAppIcons } from "./components/AppIcons";

<Meta title="Docs/Assets/Favicon & App Icons" />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we split this page into two? The favicon should be required for all of our websites; the app icons are part of a PWA effort that requires more work anyway. It also makes each page a little easier to consume. Or is there a good reason against it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, we can I think. Maybe better actually.

The only thing is the script that does all of it. If we want to keep the script I'll split that in two as well.


# Favicon & App Icons

Represent the website in bookmarks, on home screens, in syndication and other tools.

Here’s how to display a website icon (a favicon) in the web browser or publish a web app with the proper app names and icons.
This set covers all combinations of common operating systems (Android, iOS, macOS, and Windows) and browsers (Chrome, Safari, Edge, and Firefox).

## Overview & examples

### Favicon

<Favicon />

We include a classic icon file with this exact name as a baseline for some browsers and RSS readers.

```html
<link rel="icon" href="favicon.ico" sizes="16x16 32x32 64x64" />
```

### SVG Icon

Roughly 75% of browsers support SVG favicons, which are more efficient and versatile.

<SvgIcon />

```html
<link rel="icon" type="image/svg+xml" href="icon.svg" />
```

### Apple Touch Icon

<AppleTouchIcon />

An iPhone or iPad uses this image when adding the webpage as a shortcut to your home screen.

```html
<link rel="apple-touch-icon" href="apple-touch-icon.png" />
```

### Web App Icons
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it correct that Android devices require these? If so, no app icon for them without a manifest? Please introduce this section with a sentence as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we're going to split the PWA/Web Manifest and favicon stuff we can move these icons over to the new PWA page. They are not used by webbrowsers.


<WebAppIcons />

Include `icons` object in `*.webmanifest` (see [Usage](#usage)):

```json
{
"icons": [
{ "src": "/icon-192.png", "type": "image/png", "sizes": "192x192" },
{ "src": "/icon-512.png", "type": "image/png", "sizes": "512x512" }
]
}
```

Link the Web Manifest in the `<head>`:

```html
<link rel="manifest" href="app.webmanifest" />
```

> Note: only include a Web Manifest if you want the website to be installable as a Progressive Web App (PWA).
> Browsers [may prompt the user to install the app](https://web.dev/learn/pwa/installation-prompt) on their device if a manifest is found.

## Usage

First, install these assets as they come with the [Amsterdam Design System Assets](https://www.npmjs.com/package/@amsterdam/design-system-assets):

```sh
npm i @amsterdam/design-system-assets
```

Then you can either use the post-install script (**Option A**) or manually copy and link the assets (**Option B**).

### (A) Link assets with a script

Use the [`postinstall.sh`](https://github.com/Amsterdam/design-system/tree/develop/proprietary/assets/postinstall.sh) script from the root of your new **Vite React**, **Next.js**, legacy React or generic HTML project to automatically link the assets.

If you only want the icons, but not the Web Manifest and PWA support:

```sh
# Make sure you are in the root directory of your project!
sh node_modules/@amsterdam/design-system-assets/postinstall.sh
```

If you want to generate a Web Manifest for a PWA as well, provide a full app name and a short app name:

```sh
# Make sure you are in the root directory of your project!
# The full app name may not be longer than 30 characters and the short name no longer than 12 characters.
sh node_modules/@amsterdam/design-system-assets/postinstall.sh "Full App Name" "Short Name"
```

Users may then be prompted to install the app as a PWA.

> If an `index.html` is found in the root or in a `public` directory the script will also insert the links in the HTML's `<head>` tag.
> This will not be the case for Next.js applications.

> This script can work for any project that has an `index.html` file in the root or `public` directory.

### (B) Link manually

You can manually [symlink (symbolic link)](https://en.wikipedia.org/wiki/Symbolic_link#POSIX_and_Unix-like_operating_systems) and copy the files to the root or a `public` folder next to where the `index.html` is located in your project.
An advantage of symlinking is that it tracks changes to the assets when there are package updates.

Symlinking [the entire favicon directory](https://github.com/Amsterdam/design-system/tree/develop/proprietary/assets/favicon) and copying the [Web Manifest](https://github.com/Amsterdam/design-system/tree/develop/proprietary/assets/app.manifest) can be done as follows:

```sh
# `cd` to the directory where you want to make the symlinks.
ln -s node_modules/@amsterdam/design-system-assets/favicon appicons
# Copy the Web Manifest if you want users to be able to and be prompted to install the app as a PWA.
cp node_modules/@amsterdam/design-system-assets/manifest/app.webmanifest app.webmanifest
```

> Note: for the symlink, the last argument in the above command cannot be a path, but can only be the file or directory name.
> I.e. you need to be in the directory where you want to make the symbolic link.

> Note that undoing a symlink can be done by simply removing the created file or directory link:
>
> ```sh
> rm -r appicons app.webmanifest
> ```

> Note: not all servers recognize the `.webmanifest` extension.
> See [Using a link element to link to a manifest on W3.org](https://www.w3.org/TR/appmanifest/#using-a-link-element-to-link-to-a-manifest) for more information.

Make sure that the Web Manifest, stating the app's name and referencing the PNG icons, is available in the same location.
Edit the `app.webmanifest` file to include the app's name and the icons you want to use.
For example:

```json
{
"name": "A maximum of thirty characters",
"short_name": "Max 12 chars",
"icons": [
{ "src": "/appicons/icon-192.png", "type": "image/png", "sizes": "192x192" },
{ "src": "/appicons/icon-512.png", "type": "image/png", "sizes": "512x512" }
]
}
```

Then link all these assets by placing the following tags in the `<head>`:

```html
<head>
...
<link rel="icon" href="appicons/favicon.ico" size="16x16 32x32 64x64" />
<link rel="icon" href="appicons/icon.svg" type="image/svg+xml" />
<link rel="apple-touch-icon" href="appicons/apple-touch-icon.png" />
<link rel="manifest" href="app.webmanifest" />
...
</head>
```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could go to the top of the Favicon page – the essential piece of HTML that people can just copy and paste.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've swapped the Overview and Usage sections. It wouldn't make sense to move this up even higher, I think. People need some context.


### Submit the changes

The symlinks and copied and edited files can be committed to the repository.

## Further Reading

- [_How to Favicon in 2024: Six files that fit most needs_ by Andrey Sitnik](https://evilmartians.com/chronicles/how-to-favicon-in-2021-six-files-that-fit-most-needs)
- [_Web App Manifest_ on web.dev](https://web.dev/learn/pwa/web-app-manifest)
dlnr marked this conversation as resolved.
Show resolved Hide resolved
49 changes: 49 additions & 0 deletions storybook/src/docs/components/AppIcons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { Image } from '@amsterdam/design-system-react'

export const AppleTouchIcon = () => (
<div className="ams-docs-gallery">
<figure className="ams-docs-figure">
<Image src="favicon/apple-touch-icon.png" width={180} height={180} />
<figcaption>apple-touch-icon.png (180px)</figcaption>
</figure>
</div>
)

export const Favicon = () => (
<div className="ams-docs-gallery">
<figure className="ams-docs-figure">
<Image src="favicon/favicon.ico" width={16} height={16} />
<figcaption>favicon.ico (16px)</figcaption>
</figure>
<figure className="ams-docs-figure">
<Image src="favicon/favicon.ico" width={32} height={32} />
<figcaption>favicon.ico (32px)</figcaption>
</figure>
<figure className="ams-docs-figure">
<Image src="favicon/favicon.ico" width={64} height={64} />
<figcaption>favicon.ico (64px)</figcaption>
</figure>
</div>
)

export const SvgIcon = () => (
<div className="ams-docs-gallery">
<figure className="ams-docs-figure">
<Image src="favicon/icon.svg" width={64} height={64} />
<figcaption>icon.svg (64px)</figcaption>
</figure>
</div>
)

export const WebAppIcons = () => (
<div className="ams-docs-gallery">
<figure className="ams-docs-figure">
<Image src="favicon/icon-192.png" width={192} height={192} />
<figcaption>icon-192.png (192px)</figcaption>
</figure>
<figure className="ams-docs-figure">
<Image src="favicon/icon-512.png" width={512} height={512} />
<figcaption>icon-512.png (512px)</figcaption>
</figure>
</div>
)
Loading