Skip to content

Instantly share code, notes, and snippets.

@ErHaWeb
Last active May 13, 2024 14:27
Show Gist options
  • Save ErHaWeb/284b5168554dfb3ee734ec0998d80238 to your computer and use it in GitHub Desktop.
Save ErHaWeb/284b5168554dfb3ee734ec0998d80238 to your computer and use it in GitHub Desktop.
Integration of App Icons in TYPO3

Integration of App Icons in TYPO3

→ to the german version

I would like to show you my way how I integrate app icons cleanly into my own TYPO3 sitepackage.

App Icon Preview generated with https://realfavicongenerator.net/

App Icon Generator

As app icons have to be integrated into HTML in different ways depending on the manufacturer and browser, and a correspondingly large number of graphics and meta files are involved, it is advisable to use a generator for this. This saves you the work of researching all relevant and up-to-date files and HTML lines yourself with the respective manufacturers (Apple, Microsoft, Google, etc.). For example, I can recommend this generator here: https://realfavicongenerator.net/

After you have clicked through the GUI of the generator, you will receive these (or similar) files:

.
├── README.md
├── android-chrome-144x144.png
├── android-chrome-192x192.png
├── android-chrome-256x256.png
├── android-chrome-36x36.png
├── android-chrome-384x384.png
├── android-chrome-48x48.png
├── android-chrome-512x512.png
├── android-chrome-72x72.png
├── android-chrome-96x96.png
├── apple-touch-icon.png
├── browserconfig.xml
├── favicon-16x16.png
├── favicon-194x194.png
├── favicon-32x32.png
├── favicon.ico
├── html_code.html
├── mstile-144x144.png
├── mstile-150x150.png
├── mstile-310x150.png
├── mstile-310x310.png
├── mstile-70x70.png
├── safari-pinned-tab.svg
└── site.webmanifest

The installation instructions from the README.md are kept quite simple. Here we are advised to store all files in the project root. However, we can do this more elegantly in the TYPO3 context 😉

TYPO3 integration of the App Icons

Since I like to keep the project root clean, I would advise you to store the files in the usual TYPO3 structures of your sitepackage, e.g. under EXT:sitepackage/Resources/Public/Icons/Favicon/.

You do not need to copy the following files into this directory:

  • README.md → This contains the installation instructions
  • html_code.html → This contains the HTML code to be integrated
  • browserconfig.xml → This is generated dynamically later using TypoScript
  • site.webmanifest → This will be generated dynamically later using TypoScript

HTML code to be embedded in the <head> tag

According to README.md/html_code.html the following code should be included:

<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="194x194" href="/favicon-194x194.png">
<link rel="icon" type="image/png" sizes="192x192" href="/android-chrome-192x192.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="manifest" href="/site.webmanifest">
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5">
<meta name="msapplication-config" content="/browserconfig.xml">
<meta name="msapplication-TileColor" content="#da532c">
<meta name="msapplication-TileImage" content="/mstile-144x144.png">
<meta name="theme-color" content="#ffffff">

Side note: I added the line <meta name="msapplication-config" content="/browserconfig.xml"> to explicitly instruct IE to use the browserconfig.xml from the root for security reasons. There are various discussions about this and I think better safe than sorry 😉.

This is integrated with the following TypoScript setup code:

page {
    headerData {
        10 = TEXT
        10 {
            value (
<link rel="apple-touch-icon" sizes="180x180" href="{path:EXT:sitepackage/Resources/Public/Icons/Favicon/apple-touch-icon.png}">
<link rel="icon" type="image/png" sizes="32x32" href="{path:EXT:sitepackage/Resources/Public/Icons/Favicon/favicon-32x32.png}">
<link rel="icon" type="image/png" sizes="194x194" href="{path:EXT:sitepackage/Resources/Public/Icons/Favicon/favicon-194x194.png}">
<link rel="icon" type="image/png" sizes="192x192" href="{path:EXT:sitepackage/Resources/Public/Icons/Favicon/android-chrome-192x192.png}">
<link rel="icon" type="image/png" sizes="16x16" href="{path:EXT:sitepackage/Resources/Public/Icons/Favicon/favicon-16x16.png}">
<link rel="manifest" href="/site.webmanifest">
<link rel="mask-icon" href="{path:EXT:sitepackage/Resources/Public/Icons/Favicon/safari-pinned-tab.svg}" color="#5bbad5">
<meta name="msapplication-config" content="/browserconfig.xml">
<meta name="msapplication-TileColor" content="#da532c">
<meta name="msapplication-TileImage" content="{path:EXT:sitepackage/Resources/Public/Icons/Favicon/mstile-144x144.png}">
<meta name="theme-color" content="#ffffff">
            )
            insertData = 1
        }
    }
}

It is assumed here that you have already defined page = PAGE somewhere in order to define the default frontend rendering of your page under the theoretically freely selectable identifier page. The use of page for the default output is established practice. It is also assumed that you have not already made other specifications under page.headerData.10. If you have, you can simply increase the Content Object Array Index 10 (recommended in increments of ten) up to a free space.

The specifications {path:EXT:sitepackage/Resources/Public/Icons/Favicon/<FILENAME>} within the TEXT value in combination with the specification insertData = 1 ensures that the files in Composer installations are dynamically referenced under /_assets/<HASH>/. Here we have the situation that there is no longer a static path under which resources of the extension directory /Resources/Public/ can be called up (more here).

favicon.ico for legacy browsers

The classic favicon.ico file for desktop browsers is generated but no longer explicitly provided in the embed code. Even if it is no longer necessary in most cases, I would still include this file, not least because TYPO3 provides its own TypoScript property for it.

page.shortcutIcon = EXT:sitepackage/Resources/Public/Icons/Favicon/favicon.ico

In the case of this file, however, you could also make an exception and consider placing it directly in the public root. There seem to be some tools (e.g. RSS readers) that cannot handle other paths.

Information in the site configuration

Now we just have to make sure that site.webmanifest and browserconfig.xml are also callable in the root, although they do not actually exist there as files.

We achieve this by making two additions to the PageTypeSuffix route enhancer mapping in our site configuration config/sites/<YOUR_IDENTIFIER>/config.yaml:

browserconfig.xml: 2943879438
site.webmanifest: 3478304621

The page type numbers 2943879438 and 3478304621 are therefore mapped to the readable paths browserconfig.xml and site.webmanifest. The two numbers are freely chosen by me. The only important thing is that they are not used anywhere else. For example, the path sitemap.xml of the SEO Core Extension already claims the type number 1533906435.

The entire PageTypeSuffix configuration block could end up looking like this (depending on the project):

routeEnhancers:
  PageTypeSuffix:
    type: PageType
    default: /
    index: ''
    map:
      /: 0
      feed.rss: 9818
      sitemap.xml: 1533906435
      browserconfig.xml: 2943879438
      site.webmanifest: 3478304621

Now we have to create these page types via TypoScript:

TypoScript page type for browserconfig.xml

The XML file browserconfig.xml contains icon meta information for Windows devices. It looks something like this:

<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
    <msapplication>
        <tile>
            <square70x70logo src="/mstile-70x70.png"/>
            <square150x150logo src="/mstile-150x150.png"/>
            <wide310x150logo src="/mstile-310x150.png"/>
            <square310x310logo src="/mstile-310x310.png"/>            
            <square144x144logo src="/mstile-144x144.png"/>
            <TileColor>#da532c</TileColor>
        </tile>
    </msapplication>
</browserconfig>

For example, we can create a corresponding page type with the TypoScript file EXT:sitepackage/Configuration/TypoScript/Setup/browserconfig.xml.typoscript as follows:

browserconfig\.xml = PAGE
browserconfig\.xml {
    typeNum = 2943879438
    config {
        disableAllHeaderCode = 1
        additionalHeaders.10.header = Content-type:text/xml
        admPanel = 0
        debug = 0
    }

    10 = TEXT
    10 {
        wrap (
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
    <msapplication>
        <tile>
            |
            <TileColor>#da532c</TileColor>
        </tile>
    </msapplication>
</browserconfig>
        )

        value = square70x70, square150x150, wide310x150, square310x310, square144x144
        split {
            token = ,
            cObjNum = 1
            1.cObject = COA
            1.cObject {
                stdWrap.wrap = <|/>

                10 = TEXT
                10 {
                    current = 1
                    trim = 1
                    noTrimWrap = ||logo |
                }

                20 = TEXT
                20 {
                    current = 1
                    trim = 1
                    replacement.10 {
                        search = #(square|wide)#i
                        replace =
                        useRegExp = 1
                    }

                    dataWrap = src="{path:EXT:sitepackage/Resources/Public/Icons/Favicon/mstile-|.png}"
                }
            }
        }
    }
}

We use the specification typeNum = 2943879438 to inform the system of the type number. In addition, we enter a few details under config that enable the output as an XML document.

TypoScript page type for site.webmanifest

The JSON file site.webmanifest (possibly also called manifest.json → pay attention to the file name in the rel="manifest" link tag) contains icon meta information for Android devices. It looks something like this:

{
    "name": "",
    "short_name": "",
    "icons": [
        {
            "src": "/android-chrome-36x36.png",
            "sizes": "36x36",
            "type": "image/png"
        },
        {
            "src": "/android-chrome-48x48.png",
            "sizes": "48x48",
            "type": "image/png"
        },
        {
            "src": "/android-chrome-72x72.png",
            "sizes": "72x72",
            "type": "image/png"
        },
        {
            "src": "/android-chrome-96x96.png",
            "sizes": "96x96",
            "type": "image/png"
        },
        {
            "src": "/android-chrome-144x144.png",
            "sizes": "144x144",
            "type": "image/png"
        },
        {
            "src": "/android-chrome-192x192.png",
            "sizes": "192x192",
            "type": "image/png"
        },
        {
            "src": "/android-chrome-256x256.png",
            "sizes": "256x256",
            "type": "image/png"
        },
        {
            "src": "/android-chrome-384x384.png",
            "sizes": "384x384",
            "type": "image/png"
        },
        {
            "src": "/android-chrome-512x512.png",
            "sizes": "512x512",
            "type": "image/png"
        }
    ],
    "theme_color": "#ffffff",
    "background_color": "#ffffff",
    "display": "standalone"
}

For example, we can create a corresponding page type with the TypoScript file EXT:sitepackage/Configuration/TypoScript/Setup/site.webmanifest.typoscript as follows:

site\.webmanifest = PAGE
site\.webmanifest {
    typeNum = 3478304621
    config {
        disableAllHeaderCode = 1
        additionalHeaders.10.header = Content-type:application/json
        admPanel = 0
        debug = 0
    }

    10 = COA
    10 {
        stdWrap.wrap = {|}

        10 = TEXT
        10 {
            dataWrap (
"name":"{siteLanguage:websiteTitle // site:websiteTitle}",
"short_name":"{siteLanguage:websiteTitle // site:websiteTitle}",
"icons":[|],
"theme_color":"#ffffff",
"background_color":"#ffffff",
"display":"standalone"
            )

            value = 36x36, 48x48, 72x72, 96x96, 144x144, 192x192, 256x256, 384x384, 512x512
            split {
                token = ,
                wrap = {|},|*|{|},|*|{|}
                cObjNum = 1
                1.cObject = COA
                1.cObject {
                    10 = TEXT
                    10 {
                        current = 1
                        trim = 1
                        dataWrap = "src":"{path:EXT:sitepackage/Resources/Public/Icons/Favicon/android-chrome-|.png}",
                    }

                    20 = TEXT
                    20 {
                        current = 1
                        trim = 1
                        wrap = "sizes":"|","type":"image/png"
                    }
                }
            }
        }
    }
}

Here, too, the type number typeNum = 3478304621 and the config specifications for the output of a JSON file are specified.

The specifications

"name":"{siteLanguage:websiteTitle // site:websiteTitle}",
"short_name":"{siteLanguage:websiteTitle // site:websiteTitle}",

can be adapted to your needs if necessary. Currently, the website title used in the site configuration (language-dependent if necessary) is simply used here.

Conclusion

Done, have fun with the new dynamic app icons!

This is one of many ways in which you can output app icons in TYPO3. The nice thing about this solution is that the TypoScript files can be reused directly in the next project via "copy and paste". To do this, start the generator again with the same basic settings and new graphics, save the image files again under the path defined above, expand the site configuration, place the two new TypoScript files in the site package and, if necessary, adjust the extension key in the TypoScript.

Sebastian Klein, for example, offers an alternative approach for integrating the manifest JSON via middleware in the following Gist. This approach could certainly also be transferred to the browserconfig.xml.

Now you are free to decide which way you want to go 😉

browserconfig\.xml = PAGE
browserconfig\.xml {
typeNum = 2943879438
config {
disableAllHeaderCode = 1
additionalHeaders.10.header = Content-type:text/xml
admPanel = 0
debug = 0
}
10 = TEXT
10 {
wrap (
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
|
<TileColor>#da532c</TileColor>
</tile>
</msapplication>
</browserconfig>
)
value = square70x70, square150x150, wide310x150, square310x310, square144x144
split {
token = ,
cObjNum = 1
1.cObject = COA
1.cObject {
stdWrap.wrap = <|/>
10 = TEXT
10 {
current = 1
trim = 1
noTrimWrap = ||logo |
}
20 = TEXT
20 {
current = 1
trim = 1
replacement.10 {
search = #(square|wide)#i
replace =
useRegExp = 1
}
dataWrap = src="{path:EXT:sitepackage/Resources/Public/Icons/Favicon/mstile-|.png}"
}
}
}
}
}
routeEnhancers:
PageTypeSuffix:
type: PageType
default: /
index: ''
map:
/: 0
feed.rss: 9818
sitemap.xml: 1533906435
browserconfig.xml: 2943879438
site.webmanifest: 3478304621
page {
headerData {
10 = TEXT
10 {
value (
<link rel="apple-touch-icon" sizes="180x180" href="{path:EXT:sitepackage/Resources/Public/Icons/Favicon/apple-touch-icon.png}">
<link rel="icon" type="image/png" sizes="32x32" href="{path:EXT:sitepackage/Resources/Public/Icons/Favicon/favicon-32x32.png}">
<link rel="icon" type="image/png" sizes="194x194" href="{path:EXT:sitepackage/Resources/Public/Icons/Favicon/favicon-194x194.png}">
<link rel="icon" type="image/png" sizes="192x192" href="{path:EXT:sitepackage/Resources/Public/Icons/Favicon/android-chrome-192x192.png}">
<link rel="icon" type="image/png" sizes="16x16" href="{path:EXT:sitepackage/Resources/Public/Icons/Favicon/favicon-16x16.png}">
<link rel="manifest" href="/site.webmanifest">
<link rel="mask-icon" href="{path:EXT:sitepackage/Resources/Public/Icons/Favicon/safari-pinned-tab.svg" color="#5bbad5}">
<meta name="msapplication-config" content="/browserconfig.xml">
<meta name="msapplication-TileColor" content="#da532c">
<meta name="msapplication-TileImage" content="{path:EXT:sitepackage/Resources/Public/Icons/Favicon/mstile-144x144.png}">
<meta name="theme-color" content="#ffffff">
)
insertData = 1
}
}
}
site\.webmanifest = PAGE
site\.webmanifest {
typeNum = 3478304621
config {
disableAllHeaderCode = 1
additionalHeaders.10.header = Content-type:application/json
admPanel = 0
debug = 0
}
10 = COA
10 {
stdWrap.wrap = {|}
10 = TEXT
10 {
dataWrap (
"name":"{siteLanguage:websiteTitle // site:websiteTitle}",
"short_name":"{siteLanguage:websiteTitle // site:websiteTitle}",
"icons":[|],
"theme_color":"#ffffff",
"background_color":"#ffffff",
"display":"standalone"
)
# Insert available icon sizes here
value = 36x36, 48x48, 72x72, 96x96, 144x144, 192x192, 256x256, 384x384, 512x512
split {
token = ,
wrap = {|},|*|{|},|*|{|}
cObjNum = 1
1.cObject = COA
1.cObject {
10 = TEXT
10 {
current = 1
trim = 1
dataWrap = "src":"{path:EXT:sitepackage/Resources/Public/Icons/Favicon/android-chrome-|.png}",
}
20 = TEXT
20 {
current = 1
trim = 1
wrap = "sizes":"|","type":"image/png"
}
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment