updates.thunderbird.net (UTN)
What’s New and Donation Appeal pages are hosted on updates.thunderbird.net
.
Setup
Install the dependencies
Follow the basic setup instructions for thunderbird-website found in the main README.md.
There are extra dependencies for creating compressed image assets. Install them by running:
pip install -r requirements-image.txt
Run the dev server
When you finish installing the dependencies, start the UTN dev server with:
python build-site.py --watch --updates --debug --enus
This command builds the site for a single locale (English US) and rebuilds the site when you make changes to the site files.
The output should look like this:
Rendering updates in en-US only.
Building pages for en-US...
Updating website when templates, CSS, or JS are modified. Press Ctrl-C to end.
HTTP Server running on: http://localhost:8000
The default address is http://localhost:8000
, though you can specify a different port with the --port
option.
A Donations Appeal Page
Let’s look at the December 2024 Donation Appeal page as an example.
The source for this page is /sites/updates.thunderbird.net/thunderbird/128.0/dec24/index.html
.
It is built from the following:
a Jinja template
styles from one or more compiled LESS files
high and normal resolution images in multiple formats: SVG, png, jpg, webp, and avif
If you are new to Jinja, read the “Template Designer Documentation” and keep it handy as you look through some of the existing whats-new and appeal pages.
Where to put files
For an appeal page whose URL path will be /en-US/thunderbird/128.0/dec24/
, the files are organized as follows:
The Jinja template is /sites/updates.thunderbird.net/thunderbird/128.0/dec24/index.html
The LESS file is at /assets/less/appeals/dec24.less
SVGs and raster images (e.g, .png
, .jpg
) go in different directories:
SVGs go in
/media/svg/appeal/dec24
Raster images go in
/media/img/thunderbird/appeal/dec24
(If this were a “what’s new” page, those paths would have been something like /media/svg/whatsnew/dec24
and /media/img/thunderbird/whatsnew/dec24
.)
To generalize, we created the following directory paths for our files:
Type |
Location |
---|---|
Jinja template |
|
SVGs |
|
Images |
|
In addition to following these conventions, some files (such as .less
styles) require configuration. We’ll take a look at each file type in the remainder of this section.
Templates
Generally, Jinja templates will go somewhere inside the thunderbird
or thunderbird-android
folders. These are the folders that get rendered by our custom static site generator.
Each appeal or what’s new page will either be associated with a particular release or a particular project.
For example, if you were making a what’s new page for Thunderbird 400.0 ESR you would create your index.html
at sites/updates.thunderbird.net/thunderbird/400.0esr/whats-new/index.html
.
Appeals are associated with the most recent ESR; the December 2024 appeal is associated with the 128.0
ESR, which gives us the file path /sites/updates.thunderbird.net/thunderbird/128.0/dec24/index.html
Lastly, an appeal may be attached to a specific project. For example, the donation appeal for Thunderbird for Android goes in sites/updates.thunderbird.net/thunderbird-android/40.0/appeal
.
Creating A New Page
A copy-and-pastable template is included in sites/updates.thunderbird.net/includes/_templates/basic-page.html.
Simply copy and paste that template to its new home. Once resettled, change the active_page
Jinja variable at the top of the template to match your page name in kebab-case. For the December 2024 appeal, we specified the following:
{% set active_page = "appeal-dec24" %}
This will namespace your page for styling by giving you the class page-<active_page>
. Additionally, the page_title
and page_desc
variables controls the rendered HTML page title and page description respectively.
The includes
folder
The includes
folder is used for jinja includes, extends, and macros.
The donation_button
macro is one that we’ll look at more closely in a later section.
LESS files
Working with .less
files involves two steps:
Creating the
.less
file in the appropriate directoryTelling the build tool about the new
.less
file.
Just like with templates and images, we follow a pattern. The December 2024 appeal .less
file path is /assets/less/appeals/dec24.less
Once you’ve created this file, add an entry for it to the UPDATES_CSS
dict in the settings.py
file. This configures the build tool to compile your new .less
file.
There should be several entries in UPDATES_CSS
. Follow the established naming convention when adding a key; the value is the path to the new less file you’ve just created.
'appeal-dec24-style': ['less/appeals/dec24.less'],
After making changes to settings.py
, you must restart the dev server.
Images
When exporting an asset from a tool like Figma or Zeplin, make sure to export two versions: one at 1x resolution and another at 2x resolution.
Follow the following naming convention:
1x:
forest-roc.png
2x:
forest-roc-high-res.png
Note: The high resolution naming convention deviates from the industry standard “@2x”.
Save both to the appropriate directory. For the December 2024 appeal, that’s /media/img/thunderbird/appeal/dec24
.
The December 2024 appeal did not use any SVGs. However, they would have been placed in this directory: /media/svg/appeal/dec24
.
Creating compressed image assets
Unfortunately we don’t compress assets automatically, you’ll have to use the tools/compress_assets.py at least once.
You’ve already installed the dependencies. Now all you need to do is run the command:
cd ../
python tools/compress_assets.py -r -o media/img/thunderbird/appeal/mycoolappeal/
This will search recursively (-r
) and overwrite (-o
) previously compressed files.
This step is only necessary for raster images. Run the tool any time you add or update .png
and .jpg
files.
Working in with Jinja templates
Linking the compiled .css
The compiled .css
filename will be based on the key you put in the UPDATES_CSS
dict in settings.py
.
For example, the entry for the December 2024 appeal is:
'appeal-dec24-style': ['less/appeals/dec24.less'],
This tells the build tool to compile the styles to css/appeal-dec24-style.css
.
To use the compiled CSS file:
Declare a
base_css
block in your template.Create a
<link>
tag.In the
href
, call thestatic()
helper, passing it the path to your compiled.css
file.
Here’s the result:
{% block base_css %}
<link href="{{ static('css/appeal-dec24-style.css') }}" rel="stylesheet" type="text/css"/>
{% endblock %}
Using the high_res_img()
helper
The high_res_img()
helper renders a <picture>
element that displays the image at the best resolution for the user’s device.
Pass it the path to the low resolution version of your image. The helper function will automatically include the -high-res
variant of your image in a srcset
attribute.
{{ high_res_img('thunderbird/appeal/dec24/forest-roc.png', {'alt': _('')}) }}
To reiterate, we have two versions of this image:
1x:
forest-roc.png
2x:
forest-roc-high-res.png
But we pass the 1x version to high_res_img()
.
Specifying alternate image formats
In an earlier step, you produced compressed/optimized versions of your image assets.
You should allow the browser to use one of these alternate formats when possible.
Update the call to high_res_img()
, passing the compressed file extensions in the alt_formats
list:
{{ high_res_img('thunderbird/appeal/dec24/forest-roc.png', {'alt': _('')}, alt_formats=('webp', 'avif')) }}
Localization
All text in the Jinja template should be translated into the user’s local language.
Specifying translatable strings
There are two ways to specify that a string should be translated. You can wrap the string in one of the following:
The
_()
function is used for shorter pieces of text.A
{% trans %}
block wraps longer strings.
Use _()
for simple strings:
<p class="closing-text">{{ _('The Thunderbird Team') }}</p>
HTML tags are allowed inside a translatable string:
<h1>
{{ _('Help Keep <span>Thunderbird Alive</span>') }}
</h1>
When possible, avoid putting HTML tags inside a translatable string. One of our volunteer translators may accidentally change the tags, causing an error when building the site.
aria-label
(and alt
text) should be translated:
<h1 id="appeal-heading" aria-label="{{ _('Help Keep Thunderbird Alive!') }}">
{{ _('Help Keep <span>Thunderbird Alive</span>') }}
</h1>
The December 2024 appeal does not use {% trans %}
blocks, but here is an example from the main index.html
:
{% trans trimmed %}
Meet Thunderbird, the <strong>email and productivity</strong> app that maximizes your freedoms.
{% endtrans %}
Extracting strings for translation
Follow the instructions located in l10n_tools/readme.md to compile and extract strings.
Accessibility
Provide a label using the aria-label
for interactive elements that assistive software may not understand (such as a close button).
This is an example from the November 2024 appeal page:
<a id="donate-footer"
class="btn btn-no-bg"
aria-label="{{ _('Donate') }}"
href="{{ donate_url(content='bottom_cta', campaign=utm_campaign, form_id=fru_form_id, base_url=donation_base_url, source=utm_source) }}">
<span aria-hidden="true" class="heart-svg">{{ svg('donate-heart') }}</span>
{{ _('Donate') }}
</a>
Use aria-hidden="true"
and an empty alt
for purely decorative content (i.e., images) that should be ignored by assistive software:
<aside id="illustration" aria-hidden="true">
<div id="roc">
{{ high_res_img('thunderbird/appeal/dec24/forest-roc.png', {'alt': _('')}, alt_formats=('webp', 'avif')) }}
</div>
</aside>
Make all other images accessible by adding appropriate alt
text.
Using a Screen Reader
It’s helpful to test the page using a screen reader.
On macOS, use the built-in VoiceOver
screen reader.
For Linux, the Orca screen reader is recommended by the Mozilla Wiki.
In particular, you may find that a screen reader does not pause adequately between sentences. Add a period (.
) to remedy this.