Bengal themes are fully customizable through template overrides, CSS customization, and design token configuration. You can modify the default theme or create your own.
Template Overrides
Override any theme template by placing a file with the same name in your project'stemplates/directory.
Project Structure
your-project/
├── templates/
│ ├── page.html # Overrides theme's page.html
│ ├── partials/
│ │ └── header.html # Overrides theme's header partial
│ └── layouts/
│ └── blog.html # Overrides blog layout
├── content/
└── bengal.toml
Override Priority
Bengal searches for templates in this order:
- Project
templates/directory (highest priority) - Theme
templates/directory - Built-in fallback templates
Common Overrides
Custom Page Layout
Createtemplates/page.html:
{% extends "layouts/base.html" %}
{% block content %}
<article class="custom-page">
<header>
<h1>{{ page.title }}</h1>
{% if page.date %}
<time datetime="{{ page.date | isodate }}">
{{ page.date | date("%B %d, %Y") }}
</time>
{% end %}
</header>
<div class="prose">
{{ page.content | safe }}
</div>
</article>
{% end %}
Custom Header
Createtemplates/partials/header.html:
<header class="site-header">
<nav>
<a href="/" class="logo">{{ site.title }}</a>
<ul class="nav-links">
{% for item in site.menus.main %}
<li><a href="{{ item.url }}">{{ item.name }}</a></li>
{% endfor %}
</ul>
</nav>
</header>
Custom 404 Page
Createtemplates/404.html:
{% extends "layouts/base.html" %}
{% block content %}
<div class="error-page">
<h1>Page Not Found</h1>
<p>The page you're looking for doesn't exist.</p>
<a href="/">Return home</a>
</div>
{% end %}
CSS Customization
Method 1: Custom Stylesheet
Create a custom CSS file and reference it in your templates:
/* assets/css/custom.css */
:root {
--color-primary: #3b82f6;
--color-text: #1f2937;
--font-family-body: 'Inter', sans-serif;
}
.site-header {
background: var(--color-primary);
}
Include it in your base template orhead.htmlpartial:
<link rel="stylesheet" href="{{ 'assets/css/custom.css' | asset_url }}">
Method 2: Override Design Tokens
Bengal's default theme uses CSS custom properties (design tokens). Override them in your custom CSS:
/* Override semantic tokens */
:root {
/* Colors */
--color-primary: #3b82f6;
--color-primary-hover: #2563eb;
--color-text-primary: #1f2937;
--color-bg-primary: #ffffff;
/* Typography */
--font-family-body: 'Inter', system-ui, sans-serif;
--font-family-heading: 'Cal Sans', sans-serif;
--font-family-mono: 'JetBrains Mono', monospace;
/* Spacing */
--space-4: 1rem;
--space-8: 2rem;
}
/* Dark mode overrides */
[data-theme="dark"] {
--color-text-primary: #f3f4f6;
--color-bg-primary: #1a1a1a;
}
Method 3: Import and Extend
Import the theme's styles and add your own:
/* assets/css/main.css */
/* Import theme base styles */
@import '../themes/default/assets/css/style.css';
/* Your customizations */
.my-custom-component {
padding: var(--space-4);
background: var(--color-bg-secondary);
}
Theme Configuration
Configure theme features inbengal.toml:
[theme]
name = "default"
[theme.appearance]
default_mode = "system" # light, dark, or system
palette = "default" # Color palette variant
[theme.features]
navigation.toc = true # Table of contents
navigation.breadcrumbs = true # Breadcrumb trail
navigation.footer_nav = true # Previous/next links
content.code_copy = true # Copy button on code blocks
content.heading_anchors = true # Anchor links on headings
Feature Flags
Toggle theme features without template overrides:
[theme.features]
# Navigation
navigation.toc = true
navigation.breadcrumbs = true
navigation.sidebar = true
navigation.footer_nav = true
# Content
content.code_copy = true
content.heading_anchors = true
content.reading_time = true
# Search
search.enabled = true
search.keyboard_shortcut = true
Creating a Custom Theme
For extensive customization, create your own theme:
Theme Structure
themes/my-theme/
├── theme.yaml # Theme configuration
├── templates/
│ ├── layouts/
│ │ └── base.html
│ ├── page.html
│ ├── section.html
│ └── partials/
│ ├── header.html
│ ├── footer.html
│ └── nav.html
└── assets/
├── css/
│ └── style.css
├── js/
│ └── main.js
└── icons/ # Custom icons
├── logo.svg
└── custom.svg
Theme Configuration
Createtheme.toml:
name = "my-theme"
extends = "default" # Optional: inherit from another theme
Cross-Theme Template Extends
When your theme extends another theme (likedefault), you can explicitly reference the parent theme's templates using thetheme_name/template.htmlsyntax:
{# templates/layouts/base.html #}
{% extends "default/base.html" %}
{% block head %}
{{ super() }}
<link rel="stylesheet" href="{{ 'css/my-theme.css' | asset_url }}">
{% end %}
{% block content %}
<div class="my-theme-wrapper">
{{ super() }}
</div>
{% end %}
This explicit syntax is useful when:
- Building distributable themes - Reference parent templates by name for clarity
- Avoiding ambiguity - Specify exactly which theme's template to extend
- Debugging - Make inheritance chain visible in templates
Without the prefix, templates use priority-based resolution (project > child theme > parent theme > default):
{# Uses whichever base.html is found first in the chain #}
{% extends "layouts/base.html" %}
With the prefix, templates explicitly target a specific theme:
{# Always extends default theme's base.html #}
{% extends "default/layouts/base.html" %}
Using Your Theme
Reference it inbengal.toml:
[theme]
name = "my-theme"
path = "themes/my-theme" # Path to theme directory
Template Variables
Site Variables
{{ site.title }} # Site title
{{ site.baseurl }} # Base URL
{{ site.config }} # Full site config
{{ site.menus.main }} # Navigation menus
{{ site.pages }} # All pages
Page Variables
{{ page.title }} # Page title
{{ page.content }} # Rendered HTML content
{{ page.url }} # Page URL
{{ page.date }} # Publication date
{{ page.metadata }} # All frontmatter
{{ page.section }} # Parent section
{{ page.toc }} # Table of contents
Template Functions
Bengal provides 80+ template functions:
{# String functions #}
{{ title | slugify }}
{{ content | truncatewords(50) }}
{# Date functions #}
{{ page.date | date("%B %d, %Y") }}
{{ page.date | isodate }}
{# URL functions #}
{{ "/about/" | absolute_url }}
{{ "image.png" | asset_url }}
{# Content functions #}
{{ page.content | reading_time }}
{{ pages | sort_by("date", reverse=True) }}
Best Practices
1. Start Small
Override only what you need:
templates/
└── partials/
└── header.html # Just the header
2. Use Design Tokens
Prefer token overrides over hard-coded values:
/* ✅ Good: Uses tokens */
.button {
background: var(--color-primary);
padding: var(--space-2) var(--space-4);
}
/* ❌ Avoid: Hard-coded values */
.button {
background: #3b82f6;
padding: 0.5rem 1rem;
}
3. Extend, Don't Replace
Use Jinja's{% block %}and{% extends %}:
{% extends "layouts/base.html" %}
{% block head %}
{{ super() }} {# Keep parent content #}
<link rel="stylesheet" href="{{ 'custom.css' | asset_url }}">
{% end %}
4. Test Dark Mode
Ensure customizations work in both modes:
.custom-component {
background: var(--color-bg-primary);
color: var(--color-text-primary);
}
/* Automatically works in dark mode via tokens */
Directive Styling
Bengal automatically includes base CSS for all directives (tabs, dropdowns, steps, admonitions, etc.). Your theme only needs to provide aesthetic styles—functional requirements are handled for you.
What's Included Automatically
| Directive | Base CSS Provides | Your Theme Adds |
|---|---|---|
| Tabs | Show/hide panes, list resets, focus-visible | Layout, colors, animations |
| Dropdowns | Summary marker reset, accessibility | Borders, colors, icons, animations |
| Steps | Counter reset, list reset | Layout, connector lines, colors |
| Admonitions | Reduced-motion | Layout, colors, icons |
| Cards | Focus-visible | Grid layout, shadows, hover effects |
Styling Directives
Override directive aesthetics using CSS custom properties or direct selectors:
/* Customize tab appearance */
.tab-nav {
display: flex;
background: var(--color-bg-secondary);
border-bottom: 1px solid var(--color-border);
}
.tab-nav li.active a {
color: var(--color-primary);
border-bottom: 2px solid var(--color-primary);
}
/* Add animation to active pane (base CSS handles show/hide) */
.tab-pane.active {
animation: fadeIn 0.2s ease-out;
}
Prose Contamination
Base CSS includes list-style resets to prevent tabs and steps from inheriting unwanted bullet points inside prose containers. If you need extra safety, use!important:
.prose .tab-nav,
.prose .tab-nav li {
list-style: none !important;
margin: 0 !important;
}
Accessibility
Base CSS provides:
focus-visiblestates for keyboard navigationprefers-reduced-motionmedia queries that disable animations
Your theme animations will automatically respect reduced-motion preferences.
Icon Customization
Add custom icons or override defaults by placing SVG files in your theme'sassets/icons/directory:
themes/my-theme/assets/icons/
├── company-logo.svg # New icon
└── warning.svg # Overrides default
Icons are resolved in priority order: site theme → theme → parent theme → Bengal defaults.
See Icon Reference for SVG format requirements and configuration options.
Related
- Icon Reference for custom icons and icon library
- Build Hooks for CSS preprocessing
- Configuration for theme settings
- Template Functions for template syntax
- Directives Reference for all available directives