Template blocks¶
django_countdown/blocked_base.html is the extension point for the
maintenance page. It defines seventeen blocks — some structural, most of them
just the CSS classes on an element, so you can retarget the page at a
different framework without rewriting the markup.
{% extends "django_countdown/blocked_base.html" %}
{% load static %}
{% block blocked_stylesheets %}
<link rel="stylesheet" href="{% static 'css/site.css' %}">
{% endblock %}
Structural blocks¶
| Block | Default | Purpose |
|---|---|---|
blocked_stylesheets |
empty | Everything that goes in <head> after the viewport meta. All three shipped variants override only this. |
blocked_body |
The whole page | Replaces the entire content of <body>. The timer script sits outside it and still runs. |
blocked_header_icon_left |
<div class="maintenance-icon icon-key"><span class="fi-wrench"></span></div> |
Icon left of the headline |
blocked_header_icon_right |
<div class="maintenance-icon icon-cog"><span class="fi-widget"></span></div> |
Icon right of the headline |
blocked_status_line |
<div class="maintenance-status" id="countdown-status"> |
The line the background poll writes its findings into. Rendered only when polling is on. Override to move or restyle it; empty it out to keep the poll and drop the visible feedback. |
The default icons use Foundation icon classes (fi-*), which is why the
plain variant shows empty boxes unless its own CSS styles them. Override both
blocks — with an emoji, an inline SVG, or nothing at all — when you are not
on Foundation.
Class blocks¶
Each of these replaces the class attribute of one element. The element,
its content and its id stay put.
| Block | Default value | Element |
|---|---|---|
blocked_body_class |
maintenance-page |
<body> |
blocked_container_class |
maintenance-container |
Outer wrapper <div> |
blocked_header_class |
maintenance-header |
Header row holding icons and title |
blocked_title_class |
maintenance-title |
<h1> — "System under maintenance" |
blocked_message_class |
maintenance-message |
countdown.message |
blocked_description_class |
maintenance-description |
countdown.long_description, rendered only when non-empty |
blocked_timer_class |
countdown-timer |
Timer wrapper, bounded window |
blocked_label_class |
countdown-label |
"Estimated end of maintenance in:" |
blocked_timer_class_indef |
countdown-timer countdown-timer--indefinite |
Timer wrapper, indefinite window |
blocked_label_class_indef |
countdown-label |
"Maintenance has no scheduled end." |
blocked_status_class |
maintenance-status |
Status line written by the background poll |
blocked_footer_class |
maintenance-footer |
Footer with the apology and site.name |
The _indef pair exists because the two states need different styling —
usually a calmer treatment for the case where there is no number to show.
Element IDs the script depends on¶
The countdown script is outside every block and cannot be overridden without replacing the whole template. It looks for four IDs:
| ID | Element | Written by the script | Present when |
|---|---|---|---|
countdown-display |
Timer wrapper | — (marker only) | maintenance_until is set |
countdown-label |
Label above the timer | Swaps between "Estimated end of maintenance in:" and "Planned end exceeded by:" | maintenance_until is set |
countdown-value |
Inner <div> |
The formatted time remaining, or the overrun once the planned end passes | maintenance_until is set |
countdown-status |
Status line | What the background poll last learned about the server | polling is on |
If you override blocked_body, keep id="countdown-value" and
id="countdown-label" on some element or the page renders without a timer:
both are looked for before the clock starts, and their absence skips it. The
background poll runs either way — losing the clock costs the reader a number,
while losing the poll would strand them on the page for good. The status line
is optional in the same way.
Structure at a glance¶
<body class="{{ blocked_body_class }}">
<div class="{{ blocked_container_class }}">
<div class="{{ blocked_header_class }}">
{{ blocked_header_icon_left }}
<h1 class="{{ blocked_title_class }}">System under maintenance</h1>
{{ blocked_header_icon_right }}
</div>
<div class="{{ blocked_message_class }}">{{ countdown.message }}</div>
<div class="{{ blocked_description_class }}">{{ countdown.long_description }}</div>
<!-- one of the two, depending on maintenance_until -->
<div class="{{ blocked_timer_class }}" id="countdown-display">
<div class="{{ blocked_label_class }}" id="countdown-label">Estimated end of maintenance in:</div>
<div id="countdown-value">…</div>
</div>
<div class="{{ blocked_timer_class_indef }}">
<div class="{{ blocked_label_class_indef }}">Maintenance has no scheduled end.</div>
<div>The site will become available once an administrator unblocks it.</div>
</div>
<!-- only when polling is on -->
<div class="{{ blocked_status_class }}" id="countdown-status"></div>
<div class="{{ blocked_footer_class }}">…{{ site.name }}</div>
</div>
</body>
<script>…</script>
Braces above stand for block output, not template variables — this is the rendered shape, not copy-pastable source.
Worked example: Bootstrap-style overrides¶
How the shipped Bootstrap variant does it, condensed:
{% extends "django_countdown/blocked_base.html" %}
{% block blocked_stylesheets %}
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css">
<style>
body.maintenance-page { min-height: 100vh; display: flex; align-items: center; }
.maintenance-container { max-width: 600px; margin: auto; text-align: center; }
</style>
{% endblock %}
{% block blocked_header_icon_left %}<i class="bi bi-wrench-adjustable"></i>{% endblock %}
{% block blocked_header_icon_right %}<i class="bi bi-gear"></i>{% endblock %}
It keeps the default class names and styles them, rather than swapping every class block — usually the shorter path. Reach for the class blocks when your framework is utility-first and the classes are the styling.
The banner has no blocks¶
countdown_banner.html is a flat partial with no {% extends %} and no
{% block %}. To change it, shadow the whole file from your own templates
directory — see Countdown banner.