Pug in highlight.js

@nera-static/highlightjs-pug on highlight.js 11, with no extra CSS: the colors come from a stock highlight.js theme. Hover a token to see its scope.

From nera.js.org

Three of the eight Pug blocks on the Nera website, unchanged.

Custom navigation
include ../vendor/plugin-navigation/partials/link-list-navigation

header.site-header
  if app.nav && app.nav.main
    +linkListNav(app.nav.main.elements, 'nav--main')
A local plugin
if app.tutorials && app.tutorials.length
  ul.tutorials__list
    each post in app.tutorials
      li.tutorials__item
        a.tutorials__link(href=post.href)
          h2 #{ post.title }
          p #{ post.description }
Tag pages
extends ../layouts/layout

block content
  .container
    include ../vendor/plugin-tags/pages/tag-overview

A real template

The header mixin of a production Nera theme: comments, code lines, attributes with JavaScript, conditionals, loops and interpolation.

mixins/header.pug
//- Sticky top bar. The home page gets the full section navigation, every other
//- page (legal pages, blog posts, tag overviews) the "back to home" button.
//- Navigation entries pointing at an index.html are the language switchers of
//- config/navigation.yaml; they are rendered as the [DE] EN toggle instead.
mixin header(lang, isHome)
    - const tx = app.theme.config.text[lang]
    - const homeHref = lang === app.lang ? '/' : `/${ lang }/`
    - const elements = app.nav['main_' + lang].elements
    - const sectionLinks = elements.filter((item) => !item.href.includes('index.html'))
    - const langLinks = elements.filter((item) => item.href.includes('index.html'))

    header.nav(data-nav)
        .nav-in
            a.brand(href=`${ homeHref }#home`)
                span.sq
                | Michael Becker

            if isHome
                nav.links#main-nav(aria-label=tx.main_nav)
                    each item in sectionLinks
                        a(href=item.href.replace(/^\/(en\/)?/, ''), data-nav-link) #{ item.name }

                .nav-right
                    span.lang
                        span [#{ lang.toUpperCase() }]
                        each item in langLinks
                            a(href=item.href, hreflang=item.name.toLowerCase(), lang=item.name.toLowerCase()) #{ item.name }
                    a.btn.btn-contact(href="#contact") #{ tx.contact }
                    button.nav-toggle(type="button", aria-controls="main-nav", aria-expanded="false", data-nav-toggle)
                        span.nav-toggle-bars(aria-hidden="true")
                        span.sr-only Menu
            else
                a.back(href=`${ homeHref }#home`) #{ tx.back_home }

Every construct

One template that touches each construct the grammar knows.

tour.pug
doctype html
//- A Pug comment, kept out of the HTML
// A comment that ends up in the HTML
html(lang="en")
  head
    title= pageTitle
    include ../partials/head
  body.home#top(data-page=slug)
    - const posts = app.posts.filter((post) => post.published)
    +header(lang, isHome)
    if posts.length > 0
      ul.posts
        each post, i in posts
          li: a(href=post.href, class=i === 0 ? 'first' : undefined) #{ post.title }
    else
      p.empty No posts yet. #[a(href="/new") Write one]
    case kind
      when 'note': p.note= text
      default: p!= html
    p
      | Escaped: \#{ not interpolated }
    :markdown-it(linkify)
    <hr class="divider">

mixin header(lang, isHome = false)
  header.site-header&attributes(attributes)
    block

Known limits

Indentation decides what these mean, and highlight.js does not track indentation. The README documents each one.

limits.pug
p.
  A text block: highlight.js matches line by line,
  so each continuation line starts like a tag.
//-
  A multi-line comment: only the marker line
  is a comment.
:markdown-it
  Filter bodies are not highlighted as Markdown.
a(href=x disabled) Space-separated boolean after a value

Scopes

The grammar uses only standard highlight.js scopes, so every theme colors it. JavaScript inside attributes, code lines and interpolation is highlighted by highlight.js's own JavaScript grammar.

ExampleScopePug
headernametag
.containerselector-classclass shorthand
#mainselector-idid shorthand
href=attrattribute name
extendskeywordkeyword
../layouts/layoutstringpath or string
mixin cardtitle.functionmixin name
(titleparamsmixin parameters
#{ x }substinterpolation
=operatoroutput and code markers
|punctuationpipe, block expansion
//commentcomment
doctype htmlmetadoctype, filter

Try your own Pug

Paste or type a template; it is highlighted as you type.