Configuration
Reference every DevLab setting, default and optional content contract.
DevLab Theme reads homepage options from [extra] and component settings from structured [extra.devlab.*] tables. The fields below are the theme's configuration contract.
Recommended site settings
base_url = "https://example.com"
title = "DevLab"
description = "A modern Zola theme for developers, documentation and technical blogs."
author = "Your name"
default_language = "en"
compile_sass = true
build_search_index = true
generate_feeds = true
feed_filenames = ["atom.xml"]
exclude_paginated_pages_in_sitemap = "all"
[search]
include_title = true
include_description = true
include_content = true
[markdown]
[markdown.highlighting]
light_theme = "github-light"
dark_theme = "github-dark"
DevLab uses these settings for the document language, Sass, search, syntax highlighting, Atom feed generation and SEO fallbacks. Keep base_url accurate before publishing, because canonical and sharing URLs use it. Set default_language to the site's content language and align non-English search index filenames with it.
Theme settings
[extra]
home_layout = "default"
home_eyebrow = "Developer-first Zola theme"
home_hero_image = ""
home_hero_image_alt = ""
home_primary_action_label = "Get started"
home_primary_action_path = ""
home_secondary_action_label = "Learn more"
home_secondary_action_path = ""
[extra.devlab.brand]
logo_text = "DevLab"
footer_text = "DevLab"
show_logo_mark = false
logo_mark_path = ""
[extra.devlab.footer]
note = ""
links_label = "Footer links"
links = []
[extra.devlab.navigation]
links = [
{ name = "Home", path = "/" },
{ name = "Docs", path = "/docs/", kind = "docs" },
{ name = "Blog", path = "/blog/", kind = "blog" },
]
[extra.devlab.docs]
section = ""
path = ""
get_started_path = ""
[extra.devlab.blog]
path = ""
date_format = "%d.%m.%Y"
[extra.devlab.appearance]
default_mode = "system"
show_toggle = true
[extra.devlab.search]
index = "search_index.en.js"
[extra.devlab.seo]
og_image = ""
devlab.brandcontrols the header logo text, optional mark and footer label.devlab.footeradds an optional note and project links around the centered footer label.devlab.navigation.linkscontrols desktop and mobile navigation.devlab.docsdefines the optional Docs section, public path, getting-started target and top-level navigation manifest.devlab.blogdefines the optional public Blog path and the displayed date format.devlab.appearancecontrols the initial color mode and toggle visibility.devlab.search.indexcontrols the generated search index filename.devlab.seo.og_imagecontrols the optional Open Graph and Twitter preview image.home_layoutcontrols the homepage layout. Usedefaultfor the original constrained layout orwidefor a full-width homepage shell.home_eyebrowcontrols the short label above the homepage title.home_hero_imageandhome_hero_image_altadd a right-side hero image inwidemode.home_primary_action_*andhome_secondary_action_*control homepage hero buttons.
The header is text-only by default, so consumer sites do not inherit the DevLab mark. Set show_logo_mark = true to add a mark. With an empty logo_mark_path, the theme uses its built-in triangle; set the path to a local static asset, an absolute HTTP(S) URL or a protocol-relative URL to use your own image instead.
Set logo_text = "" for a mark-only header. The home link keeps config.title as its accessible name. If both the mark and text are disabled, the visible text safely falls back to config.title instead of leaving an empty link.
Use kind = "docs" for the link that should open the mobile docs panel and use devlab.docs.path. Use kind = "blog" for the optional blog link that should use devlab.blog.path. These special entries can omit their own path; Docs and Blog links are skipped while the corresponding configured path is empty.
Each navigation entry is a TOML table. name is required. A regular entry requires path; a special entry uses kind = "docs" or kind = "blog" and ignores its own path in favor of the configured Docs or Blog path. Other kind values have no special behavior.
Navigation name, path and kind values are strings. devlab.brand.show_logo_mark, devlab.appearance.show_toggle and other switches are booleans. devlab.appearance.default_mode accepts system, light or dark; any other value is normalized to system.
Set devlab.navigation.links = [] to omit desktop and mobile navigation entirely. Search and the color mode toggle remain independent. When a Docs link has a configured path but no section tree, it behaves as a regular mobile link instead of opening an empty Docs panel.
Regular navigation links, devlab.docs.get_started_path, homepage actions, homepage CTA links, Download channel and verification links, and Card shortcodes accept local paths, absolute HTTP(S) URLs, protocol-relative URLs, mailto:, tel:, page fragments and query-only references. Local paths are resolved through Zola so deployments under a subpath keep the correct base URL.
Footer link entries require name and path. An optional icon accepts github, gitlab, codeberg, matrix, zulip, telegram, discord, rss, email or link. A supported icon produces a compact icon link with name as its accessible label; an omitted or unsupported icon keeps the visible text. note is plain text, and links_label names the links for assistive technology. Empty values preserve the copyright-only footer.
devlab.docs.section is a Zola source path relative to content/, for example docs/_index.md; it is not a public URL or a filesystem path beginning with content/. A non-empty missing section stops the build. Each item in devlab.docs.navigation uses the same source-path format and must identify a section _index.md.
When devlab.docs.navigation contains entries, its array selects and orders the top-level sectors shared by the Docs sidebar, mobile panel and root catalog. Descendants are read automatically from the selected sections. Omitting the setting or using an empty array preserves legacy automatic discovery. A section omitted from a non-empty manifest remains published and directly addressable.
devlab.docs.path and devlab.blog.path do not create routes: they must match content already generated by Zola. Use root-relative prefixes with both slashes, such as /docs/ and /blog/, so active states do not miss or overmatch routes.
devlab.search.index is a local generated JavaScript asset path and must match the filename produced by Zola. devlab.seo.og_image supports a local static path or an absolute HTTP(S) URL.
home_hero_image accepts a local static path, an absolute HTTP(S) image URL or a protocol-relative image URL.
The Downloads layout uses section-scoped front matter rather than global zola.toml settings. Its complete contract and release example are maintained in the Downloads guide.
Publishing
The theme includes the basic release-facing pieces: SEO metadata, feed autodiscovery, an SVG favicon and a custom 404 page. You usually only need to configure the site values:
base_url = "https://example.com"
title = "DevLab"
description = "Developer documentation and technical notes."
author = "Your name"
generate_feeds = true
feed_filenames = ["atom.xml"]
[extra.devlab.seo]
og_image = ""
Set og_image only when you have a real sharing image. Leave it empty to skip og:image and twitter:image.
More details are in the publishing guide.
Optional Docs
DevLab does not require content/docs/_index.md. A clean site can build without a docs section if the values under extra.devlab.docs stay empty.
To enable docs, create content/docs/_index.md:
+++
title = "Documentation"
description = "Guides and reference pages."
sort_by = "weight"
template = "docs.html"
page_template = "doc-page.html"
+++
Add nested directories with _index.md when you need documentation sectors and expandable descendant groups. Section landing pages expose one level at a time: direct pages and direct child sections become cards, while deeper entries remain on their own parent landing.
Then configure the site:
[extra.devlab.docs]
section = "docs/_index.md"
path = "/docs/"
get_started_path = "/docs/getting-started/"
navigation = [
{ section = "docs/getting-started/_index.md" },
{ section = "docs/guides/_index.md" },
{ section = "docs/reference/_index.md" },
]
[extra.devlab.navigation]
links = [
{ name = "Home", path = "/" },
{ name = "Docs", path = "/docs/", kind = "docs" },
]Optional Blog
DevLab does not require content/blog/_index.md. A clean site can build without a blog section if extra.devlab.blog.path stays empty.
To enable blog, create content/blog/_index.md:
+++
title = "Blog"
description = "Release notes and articles."
sort_by = "date"
paginate_by = 10
template = "blog.html"
page_template = "blog-page.html"
+++
Then configure the site:
[extra.devlab.blog]
path = "/blog/"
date_format = "%Y-%m-%d"
[extra.devlab.navigation]
links = [
{ name = "Home", path = "/" },
{ name = "Blog", path = "/blog/", kind = "blog" },
]
Blog posts use Zola's native date and updated fields. Add an optional release badge through page extra data:
+++
title = "Project v1.2.0"
date = 2026-07-01
updated = 2026-07-03
[extra]
release = "v1.2.0"
+++
devlab.blog.date_format accepts a date-format pattern. Use %Y-%m-%d for ISO-style dates or %m/%d/%Y for month-first dates. Omit it or leave it empty to keep the default day-first format %d.%m.%Y.
Omit paginate_by when all posts should remain on one page. The template supports both paginated and non-paginated sections.
Homepage
The homepage always renders the hero from content/_index.md. The richer landing sections are opt-in: add home_features, home_learn, home_workflow or home_cta only when you want those sections.
+++
title = "DevLab Theme"
description = "A modern Zola theme for developers, documentation and technical blogs."
[extra]
home_layout = "wide"
home_eyebrow = "Developer-first Zola theme"
home_hero_image = ""
home_hero_image_alt = ""
home_primary_action_label = "Get started"
home_primary_action_path = "/docs/getting-started/"
home_secondary_action_label = "Learn more"
home_secondary_action_path = "#home-learn-more"
home_features = [
{ kicker = "Docs", title = "Structured documentation", description = "Build clean technical documentation with sections, pages and readable navigation." },
{ kicker = "Blog", title = "Technical writing", description = "Publish development notes, release posts, tutorials and engineering articles." },
{ kicker = "Projects", title = "Project showcase", description = "Present open-source work, internal tools and product experiments in one place." },
]
[extra.home_learn]
eyebrow = "Features"
title = "Why DevLab is different"
description = "DevLab keeps the theme small and Zola-native while giving documentation sites the navigation patterns readers expect."
cards = [
{ kicker = "Navigation", title = "One docs structure", description = "Sidebar, mobile menu, overview, breadcrumbs and prev/next links follow the same section tree." },
{ kicker = "Mobile", title = "Single site menu", description = "One mobile drawer handles global navigation and documentation links without adding a second burger menu." },
]
[extra.home_workflow]
eyebrow = "Workflow"
title = "Built for maintainable documentation"
description = "DevLab favors reusable partials, Zola section trees and plain Markdown examples."
points = [
{ title = "Shared partials", description = "Docs navigation is rendered once and reused across desktop and mobile." },
{ title = "Live examples", description = "Feature docs show rendered output and the Markdown syntax behind it." },
]
[extra.home_cta]
title = "Ready to build your docs?"
description = "Start with the getting started guide or jump straight into the documentation overview."
primary_label = "Get started"
primary_path = "/docs/getting-started/"
secondary_label = "View docs"
secondary_path = "/docs/"
+++
Use home_layout = "default" to keep the original homepage width. Use home_layout = "wide" when the homepage should span the same broad canvas as feature grids and richer landing pages.
Homepage values can live globally in config.extra or locally in the homepage front matter. Local values in content/_index.md win over global values.
home_layout accepts default or wide; any other value renders the default layout. Hero and CTA buttons render only when their path is non-empty. With no explicit global hero path, home_primary_action_path falls back to devlab.docs.get_started_path; an explicitly defined local homepage value, including an empty string, has final priority.
If the optional homepage sections are omitted, DevLab does not render placeholder feature cards or a default call to action. This keeps clean external sites from inheriting DevLab marketing copy.
home_features controls the feature row. Each entry supports:
kickertitle(required)description
home_learn controls the learn-more section:
eyebrowtitledescriptioncards, where each card requirestitleand optionally supportskickeranddescription
When the home_learn table exists but omits text fields, its defaults are Features, Why DevLab is different and the built-in theme description.
home_workflow controls the split workflow section:
eyebrowtitledescriptionpoints, where each point requirestitleand optionally supportsdescription
When the home_workflow table exists but omits text fields, its defaults are Workflow, Built for maintainable documentation and the built-in theme description.
home_cta controls the final homepage call to action:
titledescriptionprimary_labelprimary_pathsecondary_labelsecondary_path
When the home_cta table exists, omitted values fall back to Ready to build your docs?, the built-in description, Get started, devlab.docs.get_started_path, View docs and devlab.docs.path, respectively. An empty resolved path hides its button.
Defaults
| Setting | Default | Purpose |
|---|---|---|
devlab.brand.logo_text | config.title | Header logo text. |
devlab.brand.footer_text | config.title | Footer copyright label. |
devlab.brand.show_logo_mark | false | Whether to render a compact image or the built-in theme mark. |
devlab.brand.logo_mark_path | Empty | Optional local or remote image that replaces the built-in mark. |
devlab.footer.note | Empty | Optional plain-text footer information. |
devlab.footer.links | Empty array | Optional project, social or utility links. |
devlab.footer.links_label | Footer links | Accessible name for the footer link group. |
devlab.seo.og_image | Empty | Optional Open Graph and Twitter preview image path or absolute URL. |
devlab.appearance.default_mode | system | Initial color mode before a visitor choice is saved. |
devlab.appearance.show_toggle | true | Whether to render the color mode toggle button. |
devlab.docs.section | Empty | Optional section used by docs sidebar, mobile docs menu and links. |
devlab.docs.path | Empty | Optional public Docs prefix and fallback target for the homepage CTA secondary action. |
devlab.docs.get_started_path | Empty | Optional fallback target for the hero primary action and homepage CTA primary action. |
devlab.docs.navigation | Not set | Optional ordered top-level section manifest for the Docs sidebar, mobile panel and root catalog; omission or an empty array keeps legacy automatic discovery. |
devlab.blog.path | Empty | Optional public URL prefix for blog links and active blog states. |
devlab.blog.date_format | %d.%m.%Y | Display format for published and updated dates. |
home_layout | default | Homepage layout mode. |
home_eyebrow | Developer-first Zola theme | Homepage hero eyebrow. |
home_hero_image | Empty | Optional local path or absolute URL for the wide hero image. |
home_hero_image_alt | Empty | Alt text for home_hero_image. |
home_primary_action_label | Get started | Homepage hero primary button label. |
home_primary_action_path | devlab.docs.get_started_path, then empty | Local path, absolute URL or fragment for the primary hero action. |
home_secondary_action_label | Learn more | Homepage hero secondary button label. |
home_secondary_action_path | Empty | Local path, absolute URL or fragment for the secondary hero action. |
devlab.search.index | search_index.en.js | Search index script generated by Zola. |
devlab.navigation.links | Home plus hidden Docs/Blog entries | Header and mobile links; special entries appear when their configured paths are non-empty. |
Interface text
Supported global interface labels live directly under [extra]:
[extra]
docs_sidebar_title = "Documentation"
docs_section_toggle_label = "Toggle section: {title}"
breadcrumb_label = "Breadcrumb"
toc_label = "On this page"
previous_label = "Previous"
next_label = "Next"
search_label = "Search"
search_placeholder = "Search..."
menu_open_label = "Open menu"
menu_label = "Menu"
menu_close_label = "Close menu"
menu_back_label = "Back to main menu"
menu_docs_label = "Open documentation navigation"| Setting | Used for |
|---|---|
docs_sidebar_title | Visible desktop Docs sidebar title and its navigation label |
docs_section_toggle_label | Accessible label for subsection disclosure buttons; keep the {title} placeholder |
breadcrumb_label | Accessible label for breadcrumb navigation |
toc_label | Visible and accessible title of the Docs table of contents |
previous_label, next_label | Docs previous and next controls |
search_label, search_placeholder | Search field accessible name and placeholder |
menu_open_label | Mobile menu button accessible name |
menu_label, menu_close_label, menu_back_label, menu_docs_label | Mobile menu title and controls |
docs_overview_label is no longer used. Docs navigation now takes landing-link text from each section's title; an older consumer configuration can leave the removed field in place without breaking a build, but it has no effect. To expose an item named Overview, create a real page or section with that title and position it with weight.
Search states, Blog metadata and Copy feedback use [extra.labels]:
[extra.labels]
search_start = "Start typing to search."
search_no_results = "No results found."
search_unavailable = "Search is currently unavailable."
search_results = "Search results"
search_results_count = "Results found: {count}."
published = "Published"
updated = "Updated"
copy = "Copy"
copied = "Copied"
copy_failed = "Failed"
copy_success = "Code copied to clipboard."
copy_error = "Unable to copy code."| Setting | Used for |
|---|---|
search_start | Empty search prompt |
search_no_results | Visible message when no result matches |
search_unavailable | Visible message when the search index or library is unavailable |
search_results | Accessible results-list label |
search_results_count | Search status announcement; keep the {count} placeholder |
published, updated | Blog date labels |
copy, copied, copy_failed | Code-block button states |
copy_success, copy_error | Clipboard live-region announcements |
Every interface label value is a string. These fields are the supported label contract, not full theme internationalization. Static strings such as the skip link, theme-mode names and generic pagination labels remain theme-owned in v0.3.1.
Search
Search is optional. If build_search_index is false, the search field and search scripts are not rendered.
When search is enabled, keep the generated index filename aligned with extra.devlab.search.index:
build_search_index = true
[extra.devlab.search]
index = "search_index.en.js"
For non-English Zola search indexes, set devlab.search.index to the generated file name.
Customization
Configuration is the preferred customization layer. When a site needs palette changes, additional assets or markup overrides, keep them outside themes/devlab-theme/ and follow the Customization overview.
- Brand and colors documents logo assets, CSS loading and supported tokens.
- Template extensions documents base hooks, partial precedence and layout overrides.