Home Blog Certs Knowledge Base About

Templates โ€” Layout Reference

How Hugo templates work

Hugo uses the Go template language. All templates live in themes/maks/layouts/.

Key concepts:

  • {{ .Field }} โ€” output a field value
  • {{ range .Items }}...{{ end }} โ€” loop over a collection
  • {{ if .Condition }}...{{ end }} โ€” conditional block
  • {{ partial "name.html" . }} โ€” include a partial, passing current context
  • {{ block "name" . }}...{{ end }} โ€” define a named block (in baseof)
  • {{ define "name" }}...{{ end }} โ€” override a block (in child templates)
  • {{ "path" | relURL }} โ€” convert path to relative URL
  • {{ .Value | funcName }} โ€” pipe: pass value into a function

baseof.html โ€” master template

Path: themes/maks/layouts/_default/baseof.html
Rendered for: every page on the site (acts as a wrapper)

This is the foundation of the entire site. Every other template extends baseof via define blocks.

Blocks that can be overridden

BlockLocation in baseofPurpose
{{ block "head" . }}inside <head>Additional <link> or <meta> tags
{{ block "main" . }}main content areaPage content
{{ block "scripts" . }}before </body>Inline JS specific to the page

What always renders (cannot be overridden)

  • Desktop nav .desk-nav โ€” logo + links + lang-toggle + theme-toggle
  • Mobile nav .mob-nav โ€” logo + burger
  • Mobile drawer .mob-drawer โ€” slide-out menu
  • Mobile bottom nav .mob-bottom-nav โ€” bottom bar with icons
  • Footer โ€” logo + stack + location + year
  • <script> โ€” inline: toggleTheme(), setLang(), toggleMobMenu(), closeMobMenu(), restore from localStorage

Note: site.js is not loaded from baseof! Functions are inlined directly in baseof to guarantee execution order. site.js in static/js/ is a legacy file โ€” its functions are duplicated inline.

How theme switching works

<html data-theme="dark">   โ† attribute on <html>

CSS reads the attribute via [data-theme="light"] and switches variables. The value is stored in localStorage.theme.


index.html โ€” home page

Path: themes/maks/layouts/index.html
Rendered for: /
Defines block: main

Home page sections

CSS classContentData source
.heroHeading + description + buttonsHardcoded in template
.panel .recent-postsLast 5 posts{{ range first 5 .Site.RegularPages }}
.panel .kb-sectionKB quick linksHardcoded links
.panel .certs-sectionCert widget{{ partial "certs-widget.html" . }}
.cert-grid4 cert cardsHardcoded in certs-widget.html

Note: cert progress bars (width:62%) are hardcoded in certs-widget.html. To update them, edit the partial file manually.


_default/single.html โ€” article page

Path: themes/maks/layouts/_default/single.html
Rendered for: any single page without a specific template
Used by: all articles /posts/lpic2-*/, /docs/*/, /about/… (when no own template exists)
Defines blocks: main, scripts

main structure

.page.prose-page
  breadcrumb.html      โ† partial: maks.top / {section} / {title}
  article
    .post-header
      h1               โ† .Title
      .post-meta
        .post-date     โ† .Date.Format "02/01/2006"  (dd/mm/yyyy)
        .post-meta-tags
          .tag ร— N     โ† range .Params.tags โ†’ links to /tags/{tag}/
    .prose             โ† {{ .Content }} (markdown body โ†’ HTML)
    .back-link         โ† โ† Back to {section}
  .toc-aside           โ† empty div, populated by JS

scripts block โ€” ToC + progress bar + copy buttons

Reading progress bar: created dynamically via document.createElement('div'), inline style, updated on scroll event.

ToC sidebar:

  1. Finds all h2, h3 inside #articleBody
  2. If more than 2 headings โ€” generates .toc-inner with links
  3. At width >= 860px adds .has-toc to .page (two-column layout)
  4. IntersectionObserver highlights the active item on scroll

Copy buttons: for each <pre> not inside .code-block, wraps it in .code-block with a button.


_default/list.html โ€” page listing

Path: themes/maks/layouts/_default/list.html
Rendered for: any section without a specific template (/docs/, /certs/ if no own template)
Defines block: main

.page
  .sec-title           โ† .Title | default .Section
  .posts-list
    .post-card ร— N     โ† range .Paginator.Pages
      .post-card-meta  โ† date + first 2 tags
      .post-card-title โ† .Title
      .post-card-desc  โ† .Description or .Summary (truncated to 120 chars)
  pagination.html      โ† {{ partial "pagination.html" . }}

posts/list.html โ€” blog listing

Path: themes/maks/layouts/posts/list.html
Rendered for: /posts/
Overrides: _default/list.html
Adds: Pagefind search

Difference from _default/list.html: adds .tags-header with a search <input> and initializes Pagefind via dynamic import('/pagefind/pagefind.js').

How Pagefind works:

  1. GitHub Actions runs pagefind --site public after hugo build
  2. Pagefind crawls public/ and creates an index in public/pagefind/
  3. On the client, pagefind.js loads lazily (only on first search input)
  4. Results appear in an absolutely positioned div below the input

posts/linux-namespaces.html โ€” interactive page

Path: themes/maks/layouts/posts/linux-namespaces.html
Rendered for: /posts/linux-namespaces/
Loads: styles/ns.css via head block, js/ns.js via scripts block

Fully custom layout with a two-column structure:

  • Left column: markdown content + namespace map + grid + cheatsheet
  • Right column: ToC + progress bar

Namespace data (nsData, cheatData) is defined in ns.js.


about/single.html

Path: themes/maks/layouts/about/single.html
Rendered for: /about/

Renders a profile card .about-strip with data from hugo.toml [params]:

HTMLSource
.about-name.Site.Params.author
GitHub / LinkedIn / Telegram links.Site.Params.github / .Site.Params.linkedin / .Site.Params.telegram
.prose.Content (body of about.md)

Then inserts {{ partial "certs-widget.html" . }} โ€” the same cards as on the home page.


certs/single.html โ€” cert overview page

Path: themes/maks/layouts/certs/single.html
Rendered for: /certs/lpic-2/, /certs/lpic-1/, etc.
Loads: styles/cert.css via head block

Accordion build algorithm

1. $allPosts = all pages in the "posts" section
2. $prefix   = .Params.post_prefix (e.g. "lpic2")
3. For each exam in .Params.exams:
   For each topic in exam.topics:
     $topicPattern = "{prefix}-{topic.num}-"
     $topicPosts   = filter: pages whose BaseFileName starts with $topicPattern
     โ†’ if $topicPosts is not empty โ†’ render accordion with links

Function toggleTopic(btn): toggles .open class on .cert-topic, animates max-height on .cert-topic-body.


kb/section.html โ€” KB index and sub-sections

Path: themes/maks/layouts/kb/section.html
Rendered for: /kb/ (root index) and /kb/{sub-section}/ (sub-section landing pages)
Note: Hugo prefers section.html over list.html for section index pages โ€” kb/list.html is kept for reference but is not used.

Uses {{ if .Sections }} to branch between two layouts:

Root /kb/ (has child sections):

.page
  breadcrumb.html
  h1 "Knowledge Base"
  .kb-section-title "Site Documentation"
  .kb-cards  โ† docs pages
  .kb-section-title {group}  โ† for each group: "Linux Core", "Networking", etc.
  .kb-cards
    .kb-card.kb-card-section ร— N  โ† sub-sections (from .Sections)
    .kb-card ร— N                  โ† regular kb pages with Params.group

Sub-section /kb/{name}/ (no child sections):

.page
  breadcrumb.html
  h1 โ† .Title
  p.sec-desc โ† .Description
  .kb-cards
    .kb-card ร— N  โ† .Pages.ByTitle

taxonomy/tag.html โ€” tags page

Path: themes/maks/layouts/taxonomy/tag.html
Rendered for: /tags/

Embeds all posts as a POSTS JSON array directly in the HTML. Each object:

{ url, title, date, tags: [urlized], tagLabels: [display], summary }

Clicking a tag button filters the array and re-renders .posts-list via innerHTML.


partials/

certs-widget.html

Included on the home page (index.html) and about (about/single.html).
Hardcodes 4 cards with progress bars. Progress is updated manually (width:62% etc.).

pagination.html

Accepts a context with .Paginator. Page display logic:

  • Always shows first and last page
  • Shows cur-1, cur, cur+1
  • Inserts ยทยทยท between them when there’s a gap

search.html

Exists but is not used via partial. Search is inlined directly in posts/list.html and taxonomy/tag.html.


shortcodes/

code

{{< code lang="bash" label="example" >}}
command
{{< /code >}}
ParameterTypeDefaultDescription
langstring"bash"Language shown in block header
labelstringโ€”Additional label (small text)

Renders .code-block with a copy button. .Inner โ€” body between tags, passed through htmlEscape.

ns-card

{{< ns-card name="PID" flag="CLONE_NEWPID" icon="โš™๏ธ" color="#7c3aed"
    summary="Process ID isolation"
    desc="Detailed description..."
    host="PID 84521 on host"
    ns_view="PID 1 inside"
>}}
{{< /ns-card >}}
ParameterRequiredDescription
nameyesNamespace name (PID, NET, UTS…)
flagyesKernel flag (CLONE_NEWPID)
iconyesEmoji icon
coloryesCSS color (hex), used for --card-color
summaryyesShort description in the header
descyesFull description in the expanded body
hostnoValue on the host side (visual block)
ns_viewnoValue inside the namespace (visual block)

.Inner โ€” arbitrary markdown inside the tags (e.g. code block).