Countdown banner¶
The banner is the announcement half of the package: a partial you include in your own layout, which renders itself only while there is something to announce.
Put it immediately after <body> — the banner is a full-width strip and
expects to sit above your content. When no countdown is active the partial
outputs nothing, so it costs one if per render and can stay in place
permanently.
It needs the context processor
The partial reads active_countdown and maintenance_countdown from the
template context. Without
django_countdown.context_processors.countdown_context registered, both
are undefined and the banner never appears. See
Installation.
The two banners¶
One template, two mutually exclusive states — see Template context for exactly when each variable is populated.
Announcement banner¶
Shown to everyone while now < countdown_time. It carries:
- the
messageas a headline; - a live timer counting down to the cutoff (
id="countdown-timer"); - "you can keep working, but save your data regularly";
- the planned duration — "Estimated maintenance duration: 30 minutes" from
maintenance_duration_minutes(), or an explicit "indefinite" warning whenmaintenance_untilis empty.
When the timer hits zero it swaps to "Maintenance running — page will refresh shortly" and reloads after three seconds. That reload is the request that gets the 503.
Maintenance banner¶
Shown to superusers only, while the window is open. It is deliberately
quieter — a single line reading "System under maintenance — {message}" plus a
timer to reopening (id="maintenance-timer"), or "Indefinite — remove the
countdown to unblock the site" when there is no end.
Its job is to stop you forgetting that the public is currently locked out while you work.
Styling¶
The partial loads its own stylesheet with
{% static 'django_countdown/scss/countdown.css' %} — plain CSS despite the
directory name, with no build step and no framework dependency.
The <link> is emitted inside <body>
Browsers accept it, but it is a render-blocking stylesheet outside
<head>. If that bothers you — or if you use a strict CSP or a hashed
asset pipeline — copy the stylesheet reference into your own base
template's <head> and override the partial (below) to drop it.
Hooks available without touching the template:
| Class | Applies to |
|---|---|
.countdown-banner |
Announcement strip |
.maintenance-banner |
Superuser maintenance strip |
.countdown-timer |
Live timer inside either strip |
.countdown-timer--indefinite |
Timer slot when there is no end time |
.countdown-message |
The message text |
.hide-on-print |
Set on both strips; hidden in print stylesheets |
There is also one piece of legacy adaptation: if the page contains an element
with id="grp-header" (Grappelli's admin header), the script adds
has-countdown-banner to <body> so you can offset a fixed header in your
own CSS.
Overriding the template¶
Django resolves templates by name in loader order, so a file at
django_countdown/countdown_banner.html inside your own templates directory
wins over the packaged one:
For that to work, your directory must be searched first:
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [BASE_DIR / "templates"],
"APP_DIRS": True,
# ...
},
]
DIRS is searched before app directories, so your copy shadows the package's.
Start from the
original
and edit it — the file is self-contained markup plus one inline script, with
no {% extends %} to reason about.
Or skip the partial entirely
The context variables are ordinary model instances. If you want the countdown inside an existing alert component, ignore the partial and read the object directly:
{% if active_countdown %}
<div class="my-alert">
{{ active_countdown.message }} —
closing at {{ active_countdown.countdown_time }}
({{ active_countdown.time_remaining }} left)
</div>
{% endif %}
time_remaining renders server-side and does not tick. Use it for
non-JS contexts, or when a live timer would be noise.
Where the banner will not appear¶
- The Django admin — admin templates do not include your base layout, so
the banner is absent there unless you override
admin/base_site.htmlyourself. - The maintenance page — it is a standalone document with its own timer; see Blocked page.
- Anything rendered without a request context — the context processor
needs a request, so
render_to_string()without one produces no banner.