Developing on Bedrock

Writing URL Patterns

URL patterns should be as strict as possible. It should begin with a ^ and end with /$ to make sure it only matches what you specifiy. It also forces a trailing slash. You should also give the URL a name so that other pages can reference it instead of hardcoding the URL. Example:

url(r'^channel/$', channel, name='')

Bedrock comes with a handy shortcut to automate all of this:

from bedrock.mozorg.util import page
page('channel', 'mozorg/channel.html')

You don’t even need to create a view. It will serve up the specified template at the given URL (the first parameter). You can also pass template data as keyword arguments:

page('channel', 'mozorg/channel.html',

The variable latest_version will be available in the template.

Optimizing images

Images can take a long time to load and eat up a lot of bandwidth. Always take care to optimize images before uploading them to the site.

The script can be used to optimize images locally on the command line:

  1. Before you run it for the first time you will need to run yarn to install dependencies
  2. Add the image files to git’s staging area git add *
  3. Run the script ./bin/
  4. The optimized files will not automatically be staged, so be sure to add them before commiting

The script will:

  • optimize JPG and PNG files using tinypng (
    • this step is optional since running compression on the same images over and over degrades them)
    • you will be prompted to add a TinyPNG API key
  • optimize SVG images locally with svgo
  • check that SVGs have a viewbox (needed for IE support)
  • check that images that end in -high-res have low res versions as well

Embedding images

Images should be included on pages using helper functions.


For a simple image, the static() function is used to generate the image URL. For example:

<img src="{{ static('img/firefox/new/firefox-logo.png') }}" alt="Firefox" />

will output an image:

<img src="/media/img/firefox/new/firefox-logo.png" alt="Firefox">


For images that include a high-resolution alternative for displays with a high pixel density, use the high_res_img() function:

high_res_img('img/firefox/new/firefox-logo.png', {'alt': 'Firefox', 'width': '200', 'height': '100'})

The high_res_img() function will automatically look for the image in the URL parameter suffixed with ‘-high-res’, e.g. img/firefox/new/firefox-logo-high-res.png and switch to it if the display has high pixel density.

high_res_img() supports localized images by setting the ‘l10n’ parameter to True:

high_res_img('img/firefox/new/firefox-logo.png', {'l10n': True, 'alt': 'Firefox', 'width': '200', 'height': '100'})

When using localization, high_res_img() will look for images in the appropriate locale folder. In the above example, for the de locale, both standard and high-res versions of the image should be located at media/img/l10n/de/firefox/new/.


Images that have translatable text can be handled with l10n_img():

<img src="{{ l10n_img('firefox/os/have-it-all/messages.jpg') }}" />

The images referenced by l10n_img() must exist in media/img/l10n/, so for above example, the images could include media/img/l10n/en-US/firefox/os/have-it-all/messages.jpg and media/img/l10n/es-ES/firefox/os/have-it-all/messages.jpg.


Finally, for outputting an image that differs depending on the platform being used, the platform_img() function will automatically display the image for the user’s browser:

platform_img('img/firefox/new/browser.png', {'alt': 'Firefox screenshot'})

platform_img() will automatically look for the images browser-mac.png, browser-win.png, browser-linux.png, etc. Platform image also supports hi-res images by adding ‘high-res’: True to the list of optional attributes.

platform_img() supports localized images by setting the ‘l10n’ parameter to True:

platform_img('img/firefox/new/firefox-logo.png', {'l10n': True, 'alt': 'Firefox screenshot'})

When using localization, platform_img() will look for images in the appropriate locale folder. In the above example, for the es-ES locale, all platform versions of the image should be located at media/img/l10n/es-ES/firefox/new/.


This is a helper function that will output SVG data for a QR Code at the spot in the template where it is called. It caches the results to the data/qrcode_cache directory, so it only generates the SVG data one time per data and box_size combination.

qrcode('', 30)

The first argument is the data you’d like to encode in the QR Code (usually a URL), and the second is the “box size”. It’s a parameter that tells the generator how large to set the height and width parameters on the XML SVG tag, the units of which are “mm”. This can be overriden with CSS so you may not need to use it at all. The box_size parameter is optional.

Using Large Assets

We don’t want to (and if large enough GitHub won’t let us) commit large files to the bedrock repo. Files such as large PDFs or very-high-res JPG files (e.g. leadership team photos), or videos are not well-tracked in git and will make every checkout after they’re added slower and this diffs less useful. So we have another domain at which we upload these files:

This domain is simply an AWS S3 bucket with a CloudFront CDN in front of it. It is highly available and fast. We’ve made adding files to this domain very simple using git-lfs. You simply install git-lfs, clone our repo, and then add and commit files under the assets directory there as usual. Open a PR, and once it’s merged it will be automatically uploaded to the S3 buket and be available on the domain.

For example, if you add a file to the repo under assets/pdf/the-dude-abides.pdf, it will be available as Once that is done you can link to that URL from bedrock as you would any other URL.

Writing Views

You should rarely need to write a view for Most pages are static and you should use the page function documented above.

If you need to write a view and the page is translated or translatable then it should use the l10n_utils.render() function to render the template.

from lib import l10n_utils

def my_view(request):
    # do your fancy things
    ctx = {'template_variable': 'awesome data'}
    return l10n_utils.render(request, 'app/template.html', ctx)

Make sure to namespace your templates by putting them in a directory named after your app, so instead of templates/template.html they would be in templates/blog/template.html if blog was the name of your app.

If you prefer to use Django’s Generic View classes we have a convenient helper for that. You can use it either to create a custom view class of your own, or use it directly in a file.

# app/
from lib.l10n_utils import L10nTemplateView

class FirefoxRoxView(L10nTemplateView):
    template_name = 'app/firefox-rox.html'

# app/
urlpatterns = [
    # from
    path('firefox/rox/', FirefoxRoxView.as_view()),
    # directly
    path('firefox/sox/', L10nTemplateView.as_view(template_name='app/firefox-sox.html')),

The L10nTemplateView functionality is mostly in a template mixin called LangFilesMixin which you can use with other generic Django view classes if you need one other than TemplateView.

Variation Views

We have a generic view that allows you to easily create and use a/b testing templates. If you’d like to have either separate templates or just a template context variable for switching, this will help you out. For example.


from django.conf.urls import url

from bedrock.utils.views import VariationTemplateView

urlpatterns = [
                                      template_context_variations=['a', 'b']),

This will give you a context variable called variation that will either be an empty string if no param is set, or a if ?v=a is in the URL, or b if ?v=b is in the URL. No other options will be valid for the v query parameter and variation will be empty if any other value is passed in for v via the URL. So in your template code you’d simply do the following:

{% if variation == 'b' %}<p>This is the B variation of our test. Enjoy!</p>{% endif %}

If you’d rather have a fully separate template for your test, you can use the template_name_variations argument to the view instead of template_context_variations.


from django.conf.urls import url

from bedrock.utils.views import VariationTemplateView

urlpatterns = [
                                      template_name_variations=['1', '2']),

This will not provide any extra template context variables, but will instead look for alternate template names. If the URL is testing/?v=1, it will use a template named testing-1.html, if v=2 it will use testing-2.html, and for everything else it will use the default. It simply puts a dash and the variation value between the template file name and file extension.

It is theoretically possible to use the template name and template context versions of this view together, but that would be an odd situation and potentially inappropriate for this utility.

You can also limit your variations to certain locales. By default the variations will work for any localization of the page, but if you supply a list of locales to the variation_locales argument to the view then it will only set the variation context variable or alter the template name (depending on the options explained above) when requested at one of said locales. For example, the template name example above could be modified to only work for English or German like so


from django.conf.urls import url

from bedrock.utils.views import VariationTemplateView

urlpatterns = [
                                      template_name_variations=['1', '2'],
                                      variation_locales=['en-US', 'de']),

Any request to the page in for example French would not use the alternate template even if a valid variation were given in the URL.


If you’d like to add this functionality to an existing Class-Based View, there is a mixin that implements this pattern that should work with most views: bedrock.utils.views.VariationMixin.

Geo Redirect View

We sometimes need to have a special page variation for people visiting from certain countries. To make this easier we have a redirect view class that will allow you to define URLs per country as well as a default for everyone else. This redirector URL must only be a redirector since it must be uncachable by our CDN so that all visitors will hit the server and see the correct page for their location.

from bedrock.base.views import GeoRedirectView

class CanadaIsSpecialView(GeoRedirectView):
    geo_urls = {
        'CA': 'app.canada-is-special',
    default_url = 'app.everyone-else'

In this example people in Canada would go to the URL that Django returns using reverse() (i.e. the name of the URL) and everyone else would go to the app.everyone-else URL. You may also use full URLs instead of URL names if you want to. It will look for strings that start with “http(s)://” and use it as is. The country code must be 2 characters and upper case. If the patterns for the redirect and the destination(s) have URL parameters they will be passed to the reverse call for the URL pattern name. So for example if you’re doing this for a Firefox page with a version number in the URL, as long as the view and destination URLs use the same URL parameter names it will be preserved in the resulting destination URL. So /firefox/70.0beta/whatsnew/ would redirect to /firefox/70.0beta/whatsnew/canada/ for example. The redirector will also preserve query parameters by default. You can turn that off by setting the query_string = False class variable.

Coding Style Guides

Use the .open-sans, .open-sans-light and .open-sans-extrabold mixins to specify font families to allow using international fonts. See the CSS section in the l10n doc for details.

Use the .font-size() mixin to generate root-relative font sizes. You can declare a font size in pixels and the mixin will convert it to an equivalent rem (root em) unit while also including the pixel value as a fallback for older browsers that don’t support rem. This is preferable to declaring font sizes in either fixed units (px, pt, etc) or element-relative units (em, %). See this post by Jonathan Snook for more info.

When including CSS blocks, use {% block page_css %} for page specific inclusion of CSS. {% block site_css %} should only be touched in rare cases where base styles need to be overwritten.

Configuring your code editor

Bedrock includes an .editorconfig file in the root directory that you can use with your code editor to help maintain consistent coding styles. Please see for a list of supported editors and available plugins.