Make it look like a site

So far the site works but reads as an admin demo. In this short chapter you will add a small stylesheet, render the CMS menu as a real top navigation, and replace the unstyled body in base.html with a proper layout.

Goal

At the end of this chapter, an anonymous visitor lands on a homepage with a navigation bar containing Home, About, and Menu. Pages share a visual style. The coffee cards look like cards.

1. A minimal stylesheet

Create coffeeshop/static/coffeeshop/site.css:

:root {
    --ink: #2b1d12;
    --paper: #faf6f1;
    --accent: #a4673e;
}

body {
    font-family: system-ui, sans-serif;
    margin: 0;
    background: var(--paper);
    color: var(--ink);
    line-height: 1.5;
}

header.site-header {
    background: var(--ink);
    color: var(--paper);
    padding: 1rem 2rem;
    display: flex;
    align-items: center;
    gap: 2rem;
}

header.site-header a {
    color: inherit;
    text-decoration: none;
}

nav.site-nav ul {
    list-style: none;
    display: flex;
    gap: 1.5rem;
    margin: 0;
    padding: 0;
}

main {
    max-width: 56rem;
    margin: 2rem auto;
    padding: 0 1rem;
}

.coffee-card {
    border: 1px solid var(--accent);
    border-radius: 6px;
    padding: 1rem 1.25rem;
    margin: 1rem 0;
    background: white;
}

.coffee-card h3 {
    margin: 0 0 .25rem;
}

.coffee-card .price {
    font-weight: bold;
    color: var(--accent);
}

You do not have to type this — copy any starter stylesheet you like. The point is that styling is ordinary CSS.

Run python manage.py collectstatic --noinput if your project serves static files via Django’s staticfiles app. During development with DEBUG = True you usually do not need to.

2. Render the navigation from CMS pages

The CMS menu is a template tag. Edit coffeesite/templates/base.html to import it and use it inside the header.

{% load cms_tags sekizai_tags menu_tags static %}
<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>{% page_attribute "page_title" %} – Coffee Roaster</title>
    <link rel="stylesheet" href="{% static 'coffeeshop/site.css' %}">
    {% render_block "css" %}
</head>
<body>
    {% cms_toolbar %}

    <header class="site-header">
        <a href="/"><strong>Coffee Roaster</strong></a>
        <nav class="site-nav">
            <ul>{% show_menu 0 1 100 100 %}</ul>
        </nav>
    </header>

    <main>
        {% block content %}
            {% placeholder "Body" %}
        {% endblock %}
    </main>

    {% render_block "js" %}
</body>
</html>

The four numbers on {% show_menu %} control how deep and how many levels the menu shows. Defaults are sane. The full list of menu tags is in User site navigation.

The default menu/menu.html template renders <li> items — only nested levels bring their own <ul> — so you provide the outer <ul> yourself, as in the snippet above. The CSS above styles the top level into a horizontal bar.

3. Drop the placeholder Header

The navigation is now part of base.html itself, not a placeholder. You can either delete the {% placeholder "Header" %} line from chapter 2 or leave it for future per-page banner content. For this tutorial, delete it.

Restart runserver. Refresh / in a private window. You should see:

  • a dark header with the site name and three menu items,

  • styled coffee cards on the homepage,

  • the catalogue at /menu/ inheriting the same chrome.

the final homepage in an anonymous browser — dark header with site name and horizontal nav, styled coffee cards below

The catalogue at /menu/ gets the same treatment without any extra work: it is served by your apphook view, but its template extends base.html, so the new header, navigation and stylesheet apply there too:

the /menu/ page in the same anonymous browser, showing the catalogue inheriting the header and styling

If the menu shows pages you wanted hidden, mark them as not in navigation in PageAdvanced settingsMenu.

What just happened

Two CMS-specific pieces appeared:

  • {% show_menu %} reads the page tree and renders a navigation rooted at the start of it. Pages are added, removed, and reordered in the toolbar; the menu reflects it without any code change.

  • The CMS menu is also extendable from Python — you can inject nodes that do not correspond to CMS pages (e.g. blog post entries). See How to customise navigation menus.

Going further

You have built a working django CMS site. The last short chapter points you at the rest of the documentation.