Quarkus Roq FrontMatter

Quarkus Roq FrontMatter is a Quarkus extension that create a website from your Markdown/Asciidoc/Html pages using FrontMatter headers (url, layout, seo, data).

Roq FrontMatter is already included as part of the Roq Static Site Generator extension io.quarkiverse.roq:quarkus-roq, Follow Standalone installation section to use it standalone.

Getting started

By default, your site should be located in the project root directory.

you can also put it in the resources (i.e. src/main/resources/).

The default directory structure is:

Roq directory structure
The Roq blog is a good example of site made with Roq (sources).

Qute and FrontMatter

All templates can use the awesome type-safe Qute template engine.

Templates for layout, documents and pages can declare a FrontMatter (referred as FM data) header delimited by two ---. This header contains yaml data used to configure many things such as:

  • Routing

  • Data for the templates

  • Content generation

  • Pagination

Layouts and Partials

For your site, you will have one or more kind of pages, this is what we call "layouts", located by default in templates/layouts/. For example:

  • main: the base layout for all kind of pages

  • page: the layout of normal pages

  • post: the layout for blog posts

  • recipe: the layout for recipes or whatever

A layout can be included in pages and documents through the layout FrontMatter key (e.g. layout: page).

Roq layouts are using the Qute include section under the hood to achieve template inheritance. For more details, see the Qute documentation on includes: Qute includes. Unlike partials, layouts can also define Frontmatter data, which is inherited along with the template structure.

You can also prepare partial templates. They are located by default in templates/partials/. For example:

  • pagination.html

  • head.html

if you don’t already have layouts and partials in your project, Install a theme or create your own templates (example templates).

Install a theme

To install a theme, simply add the dependency to your pom.xml. Example with Roq’s default theme:

<dependency>
    <groupId>io.quarkiverse.roq</groupId>
    <artifactId>quarkus-roq-theme-default</artifactId>
    <version>1.0.4</version>
</dependency>

It will provide templates, scripts and styles for your site. To use a theme layout, refer to it with :theme/ prefix (there is an example in the next part). For advanced usage, refer to the Theme section.

Site index template

Your site index template is required and should be located in content/index.html.

content/index.html
---
title: Hello Roqers (1)
description: It is time to start Roqing 🎸!
layout: :theme/main (2)
---

<h1>Hello fellow Roqers 🀘</h1>

<p>
  With Roq, it is very easy to a link to another.
  <a href="{site.url('/roq-bottom')}">page</a>. (3)
</p>
1 The index.html also describe your site information through a FrontMatter header.
2 The layout to use (in this case :theme/main which refers to the main layout from the theme).
3 We use the {site.url(path)} using Qute to manual resolve other pages urls.
There are different ways to link your pages as explained in the Links & Urls section.

Variables

You can use Qute to access site and pages data. For this use the site and page variables:

  • The site variable allow to access site global info from any page, document, layout or partial.

    Show attributes
    Variable Type Description Example

    site.url

    RoqUrl

    The Roq site URL

    http://example.com/my-roq-site/

    site.imagesUrl

    RoqUrl

    Directory to resolve images URL (e.g. /static/images)

    http://example.com/static/images

    site.data

    JsonObject

    The site FM data (declared in the index.html)

    {"title": "My Site", "description": "A description"}

    site.pages

    java.util.List<NormalPage>

    All the pages in this site (without the documents)

    [Page1, Page2, Page3]

    site.collections

    RoqCollections

    All the collections in this site (containing documents)

    {"collection1": Collection1, "collection2": Collection2}

    site.title

    String

    The site title

    My Site

    site.description

    String

    The site description

    A description

    site.image

    RoqUrl

    The site image URL if present

    http://example.com/static/images/site.png

    site.url(Object path, Object…​ others)

    RoqUrl

    Shortcut for site.url.resolve(path)

    site.url.resolve("/about") ⇒ http://example.com/my-roq-site/about

  • The page variable is available in pages, documents, layouts, and partials. It contains the info for the page it is used from.

    Show attributes
    Variable Type Description Example

    page.url

    RoqUrl

    The URL to this page

    http://example.com/about

    page.info

    PageInfo

    The page info (file name, …​)

    page.data

    JsonObject

    The FM data of this page

    {"title": "About Us", "description": "This is the about us page."}

    page.paginator

    Paginator

    The paginator if any

    Paginator{currentPage=1, totalPages=5}

    page.collection

    String

    The collection id if this a document

    posts

    page.title()

    String

    The title of the page (shortcut from FM)

    About Us

    page.description()

    String

    The description of the page (shortcut from FM)

    This is the about us page.

    page.image()

    RoqUrl

    The image URL of the page if present

    http://example.com/static/images/about.png

    page.date()

    ZonedDateTime

    The publication date of the page

    2023-10-01T12:00:00Z

Pages

Any directory without the _ prefix in the site directory will be scanned for pages content.

Let’s create your first page and spice things up a bit by using Markdown.

roq-bottom.md
---
title: Roq Bottom
description: When you hit Roq bottom, try Roq to climb back up!
layout: :theme/page
link: /climb-back-up (1)
the-rope: You Roq! (2)
---

# Roq Bottom

If you thought you hit Roq Bottom, take this πŸͺ’ because :

__{page.data.the-rope}!__ (3)
1 you can use link to give this page a custom link (by default it will use the file-name).
2 you can add other FM data.
3 FM data is available through page.data.

Asciidoc support

Asciidoc is supported by Roq, though partially for the moment.

All the formatting features are available. But others such as diagrams are not yet supported.

Custom attributes can also be used, but be sure to escape their use -{custom-attribute}-. Other wise the Qute rendering engine will try to interpolate them.

Includes

The standard Asciidoc include is not supported, but you can use Qute includes instead:

  1. Place your file in a folder under the template directory (for example partials)

  2. Use Qute include directive {# partials/your_included_file.adoc/} to inject it

Images

The standard imagedir attribute is not supported.

You should instead place your images under the static/assets/images folder, and reference them with the image macro :

image::/static/assets/images/your_image.png[]

Collections

Collections are a great way to group related content such as blog posts, recipes, member of a team or talks at a conference. Once created you can easily iterate and link to them.

By default, Roq is configured with a posts collection using the content/posts directory. Let’s create our first post:

content/posts/2024-10-14-roq-solid.md
---
title: Roq bad puns
description: Roq is very good for bad puns 🀭
layout: :theme/post (1)
tags: (2)
  - funny
  - ai
img: 2024/10/roq-solid.jpg
---

# {page.title} (3)

Here is a list of puns suggested by Chat GPT:
1.	Roq and Rule – A play on β€œrock and roll,” implying dominance or success.
2.	Between a Roq and a Hard Place – Classic pun meaning stuck in a difficult situation.
3.	Roq Solid – Something that is extremely reliable or stable.
4.	You Roq! – A compliment, suggesting someone is awesome or does something well.
5.	Roq Bottom – Referring to the lowest possible point, often used metaphorically.
6.	Roq the Boat – To cause trouble or disturb the status quo.
7.	Roq Star – A person who excels or stands out in their field.
8.	Let’s Roq – Slang for getting started or doing something exciting.
9.	Roq On! – An enthusiastic way to say β€œkeep going” or β€œstay awesome.”
10.	Roqy Road – Could be literal (the type of road) or metaphorical for a difficult journey.
11.	Roq of Ages – A historical reference, often implying something long-standing and unchanging.
12.	Roq the Cradle – Can be literal or a pun about nurturing or starting something new.
13.	Roqy Relationship – A tumultuous or unstable relationship.
14.	Heavy as a Roq – Something burdensome or difficult to manage.
15.	Stone Cold Roq – Referring to something very cool or emotionless.
1 This time we use the post layout from the theme.
2 You can define tags (see Plugins to create pages for tags).
3 You have shortcut on the page to access title and description.

Then let’s edit our index page to show the list of posts:

content/index.html
---
title: Hello Roqers
description: It is time to start Roqing 🎸!
layout: main
---

<h1>Hello fellow Roqers 🀘</h1>


{#for post in site.collections.posts} (1)
<article class="post">
  {#if post.image}
  <a class="post-thumbnail" style="background-image: url({post.image})" href="{post.url}"></a> (2) (3)
  {/if}
  <div class="post-content">
    <h2 class="post-title"><a href="{post.url}">{post.title}</a></h2>
    <p>{post.description}</p>
    <span class="post-date">{post.date.format('yyyy, MMM dd')}&nbsp;&nbsp;&nbsp;β€”&nbsp;</span> (4)
    <span class="post-words">
      {post.readTime} minute(s) read (5)
    </span>
  </div>
</article>
{/for}
1 You can use site.collections.[collection id] to access the full list of documents (it is also possible to paginate.
2 post.image is smart and is already resolved to the image url (as a RoqUrl).
3 post.url contains the post url (as a RoqUrl), you could also use post.url.absolute to get the absolute url.
4 post.date returns a ZonedDateTime and can be formatted the way you want.
5 post.readTime is a Qute template extension which compute the read time based on the post content.

Static files

By default, all files in static/ are treated as static..

Styles and Javascript

The Quarkus Web Bundler is included by default with the Roq extension.

You can add css and scripts in your static directory or bundle them. To use bundling scripts (js, ts) and styles (css, scss) should be located in src/main/web/app/. To include the generated bundle in your template, specify the bundle tag in the html>head tag:

layouts/head.html
<head>
  ...
  {#bundle /}
</head>

It will be rendered with the relevant <script> and <style> tags to include your bundle.

You may also consume and bundle npm dependencies among other cool things. For more info, read the Quarkus Web Bundler documentation.

Pagination

Adding pagination to your Roq site is an easy way to improve content navigation. Let’s walk through how to implement pagination and customize its behavior in your site.

Step 1: Iterate on the paginated collection

First, include the following in your FrontMatter header on the page which will iterate on the paginated collection:

paginate: posts

Next, in your template, loop through the paginated posts using:

{#for post in site.collections.posts.paginated(page.paginator)} (1)
<article class="post">
  ...
</article>
{/for}

{#include includes/pagination.html/}
1 Calling .paginated(page.paginator) will resolve to the posts for the computed page.

Step 2: Including Pagination Controls

To add pagination controls, use the provided fm/pagination.html in your own _includes/pagination.html:

{#include fm/pagination.html}
{#newer}<i class="fa fa-long-arrow-left" aria-hidden="true"></i>{/newer}
{#older}<i class="fa fa-long-arrow-right" aria-hidden="true"></i>{/older}
{/include}
If you want to write your own controls, find inspiration in the FM sources fm/pagination.html.

Just by doing so, Roq will generate a bunch of pages based on the pagination setting. For example with a pagination size of 4 and with 9 posts, you would get:

  • index.html (posts 1 to 4)

  • posts/page-2 (posts 5 to 8)

  • posts/page-3 (post 9)

the first page uses the declaring page link.

You can further customize your pagination by setting the page size and link format:

paginate:
  size: 4
  collection: posts
  link: posts/page-:page

With these steps, you can create a flexible pagination system to improve your site’s navigation.

SEO

Incorporating search engine optimization (SEO) is as simple as inserting this tag into your <head> section:

<head>
{#seo page site /}
</head>

It will automatically use the FrontMatter data to fill the tags.

<title>My Blog Post</title>
<meta name="author" content="ia3andy" />
<meta name="generator" content="Quarkus Roq v1.0.0" />
<meta property="article:author" content="ia3andy" />
<meta property="article:published_time" content="2024-09-23T12:00Z[Etc/UTC]" />
<meta property="og:image" content="https://images.unsplash.com/photo.webp" />
<meta property="og:locale" content="en-US" />
<meta property="og:type" content="article" />

RSS

Incorporating and RSS feed of your posts is as simple as inserting this tag into your <head> section:

<head>
{#rss site /}
</head>

It will automatically use the FrontMatter data generate an RSS feed link.

<link rel="alternate" type="application/rss+xml" title="Your Blog" href="/rss.xml"/>

It will automatically utilize the Frontmatter data from all your blog posts to generate a valid Atom RSS feed link at rss.xml. Ensure you create an rss.xml file at the root of your site and include this single line of code:

{#include fm/rss.html}

Themes

Overriding theme layouts

In Roq, you can override theme layouts by inserting an extra layout layer. This allows you to override only specific sections of the theme layout, without duplicating the entire layout structure.

Roq layouts are leveraging Qute includes under the hood. It is possible to define insert sections which can define overridable default content.

For example, let’s override the roq-default theme’s main layout so that our customizations apply everywhere it is used.

templates/layouts/roq-default/main.html
---
layout: theme-layouts/roq-default/main (1)
---

{#insert /} (2)

{#description} (3)
Here I can override the description section
{/}

{#footer}
<footer>
And here the footer
</footer>
{/}
1 Inherits from the theme layout: This layout uses the original theme layout (roq-default/main) as a base.
2 Inheritance mechanism: {#insert /} ensures that this layout will inherit sections defined in the theme layout.
3 Override specific sections: You can override individual sections such as description and footer without affecting other parts of the layout.

Now, everywhere layouts: :theme/main (even in the theme), your override will be used.

Developing a theme

To develop a theme, create a Maven module which will contain the theme layouts, partials, scripts and styles.

.
└── main
    β”œβ”€β”€ resources
    β”‚   β”œβ”€β”€ application.properties
    β”‚   └── templates
    β”‚       β”œβ”€β”€ partials
    β”‚       β”‚   └── roq-default (1)
    β”‚       β”‚       β”œβ”€β”€ head.html
    β”‚       β”‚       β”œβ”€β”€ pagination.html
    β”‚       β”‚       β”œβ”€β”€ sidebar-about.html
    β”‚       β”‚       β”œβ”€β”€ sidebar-contact.html
    β”‚       β”‚       β”œβ”€β”€ sidebar-copyright.html
    β”‚       β”‚       └── sidebar-menu.html
    β”‚       └── theme-layouts (2)
    β”‚           └── roq-default
    β”‚               β”œβ”€β”€ default.html
    β”‚               β”œβ”€β”€ index.html
    β”‚               β”œβ”€β”€ main.html
    β”‚               β”œβ”€β”€ page.html
    β”‚               β”œβ”€β”€ post.html
    β”‚               └── tag.html
    └── web
        β”œβ”€β”€ roq.js
        β”œβ”€β”€ roq.scss
1 You can add partials for your theme, they need to be located in a directory with the theme name.
2 Layouts needs to be declared in the theme-layouts so that they can be overridden by consuming websites.

Same as for a site, script and styles can either be added to src/main/resoucres/META-INF/resources or bundled using Maven esbuild plugin:

pom.xml
             <plugin>
                <groupId>io.mvnpm</groupId>
                <artifactId>esbuild-maven-plugin</artifactId>
                <version>0.0.2</version>
                <executions>
                    <execution>
                        <id>esbuild</id>
                        <goals>
                            <goal>esbuild</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <entryPoint>roq.js</entryPoint> (1)
                </configuration>
                <dependencies> (2)
                    <dependency>
                        <groupId>org.mvnpm.at.fortawesome</groupId>
                        <artifactId>fontawesome-free</artifactId>
                        <version>6.6.0</version>
                    </dependency>
                    <dependency>
                        <groupId>org.mvnpm.at.fontsource</groupId>
                        <artifactId>pt-serif</artifactId>
                        <version>5.1.0</version>
                    </dependency>
                </dependencies>
            </plugin>
1 Add your esbuild entrypoint from src/main/resources/web
2 Add mvnpm or webjars dependencies

This bundle will be available in /static/bundle/roq.js and /static/bundle/roq.css which can be used in your theme html <head>

Create an application.properties:

src/main/resources/application.properties
site.theme=roq-default (1)
1 Thanks to this, all call to layout: :theme/…​ will automatically refer to this theme.

The output location of pages and documents is determined by the FrontMatter link key. This link value can include placeholders, which will be dynamically replaced with relevant values for routing.

Those links are also available in the Qute data to allow Creating links between your pages.
Type of page Placeholder Description Example Output

All

:path

The file path of the page, slugified (converted to a URL-friendly format) without the extension.

my-page, search or docs/my-doc

All

:slug

The slugified title of the page, derived from the title. Defaults to the slug property in data, if available or using the slugified title, falling back to the file name.

my-page-title

All

:ext

The file extension with the dot. Empty for all files with html output (md, asciidoc, html, …​).

.json

All

:ext!

Force the output file extension.

.html, .json

All

:year

The year of the page’s date or the current year if the date is not available.

2024

All

:month

The month (formatted as two digits) of the page’s date or the current month if the date is not available.

10

All

:day

The day (formatted as two digits) of the page’s date or the current day if the date is not available.

28

Document

:collection

Represents the collection to which the document belongs, such as a specific category or folder name.

blog, articles, recipes

Paginated

:page

Represents the current page.

1, 2

Default link value:

  • for pages: /:path:ext.

  • for documents: /:collection/:slug.

  • for paginated page: /:collection/page:page.

You can define link in a layout to affect all the pages using that layout.

Creating links between your pages

The pages links are automatically converted to urls by Roq, they are available in the site.url and the page.url variables. This makes creating links very easy:

<a href="{site.url}">Back to main page</a>

or

<a href="{page.nextPage.url}">{page.nextPage.title}</a>

or

{#for post in site.collections.posts}
  <a href="{post.url}">{post.title}</a>
{/for}
By default, url will be rendered as relative from the site root. You can also get the full absolute url (i.e. from http(s)://) by using absolute on any url (e.g. {site.url.absolute}).

Manual linking

Sometimes, you want to create a link for a page without holding the variable, in this case, you can use site.url(relativePath) which will be automatically resolved from the site root path.

Images url resolution

By default, pages and documents images should be located in /static/assets/images (you can choose another path in the config). To define an image for a page or document, you can use the FrontMatter image (or img or picture) key. It should contain the path to your page image relative to the configured images location (e.g. image: foo.webp will refer to /static/assets/images/foo.webp), or an absolute path starting with http(s)://.

Roq will automatically resolve the url for the image in page.image (or site.image). This makes showing images very easy:

<img src="{site.image.absolute}" title="{site.title}" /> (1)
<img src="{site.image('something.png')}" title="{site.title}" /> (2)
1 Will print the site image absolute url.
2 Will print /static/assets/images/something.png.

or

---
layout: :theme/page
title: Foo
image: foo.webp (1)
---

<img src="{page.image}" title="{page.title}" /> (2)
1 The path to your page image relative to the configured images location (or an absolute path starting with http(s)://).
2 page.image will be resolved to /static/assets/images/foo.webp.
By default, url will be rendered as relative from the site root. You can also get the full absolute url (i.e. from http(s)://) by using absolute on any url (e.g. {page.image.absolute}).

You can have other FrontMatter data with images, to convert them to url, use page.dataAsImage('foo').

---
layout: :theme/page
title: Foo
image: foo.webp
other-image: other-foo.webp
list-of-images:
  - https://my-picture.io/foo-1.webp (1)
  - foo-2.webp
images-with-titles:
  - title: Foo 1
    path: foo-1.webp
  - title: Foo 2
    path: foo-2.webp
---

<img src="{page.dataAsImage('other-image')}" title="Another image" /> (2)

{#for image in page.dataAsImages('list-of-images')} (3)
  <img src="{image}" />
{/for}

{#for image in page.data('images-with-titles')}
  <img src="{page.image(image.path)}" title="{image.title}" /> (4)
{/for}
1 The resolution is smart and also allows using absolute urls (they won’t be changed).
2 page.dataAsImage will resolve the data from other-image and resolve the image url from its value.
3 It also works with arrays using page.dataAsImages.
4 If you want to resolve any image path use page.image(path).

Standalone installation

It is included as part of the Roq Static Site Generator extension io.quarkiverse.roq:quarkus-roq. You can also use it standalone.

If you want to use this extension standalone, you need to add the io.quarkiverse.roq:quarkus-roq-frontmatter extension first to your build file.

For instance, with Maven, add the following dependency to your POM file:

<dependency>
    <groupId>io.quarkiverse.roq</groupId>
    <artifactId>quarkus-roq-frontmatter</artifactId>
    <version>1.0.4</version>
</dependency>

Extension Configuration Reference

Configuration property fixed at build time - All other configuration properties are overridable at runtime

Configuration property

Type

Default

The root path of your site (e.g. /blog) relative the quarkus http root path. This is to be used only when the site is living next to a Quarkus application. Use quarkus.http.root-path for GitHub Pages relative url.

Environment variable: QUARKUS_ROQ_SITE_ROOT_PATH

string

/

the base hostname & protocol for your site, e.g. http://example.com

Environment variable: QUARKUS_ROQ_SITE_URL

string

The order of the route which handles the templates. <p> By default, the route is executed before the default routes (static resources, etc.).

Environment variable: QUARKUS_ROQ_SITE_ROUTE_ORDER

int

1100

Configuration property fixed at build time - All other configuration properties are overridable at runtime

Configuration property

Type

Default

The ignored files in the Roq site directory (you can use glob expressions).

Environment variable: ROQ_SITE_IGNORED_FILES

list of string

/,,.,${quarkus.roq.data.dir}/**

The files to use as raw templates (n the Roq site directory (you can use glob expressions)),

Environment variable: ROQ_SITE_TEMPLATE_FILES

list of string

partials/,includes/,tags/**

Files to serve as static in the Roq site directory (you can use glob expressions)

Environment variable: ROQ_SITE_STATIC_FILES

list of string

static/**

The directory containing layouts in the Roq site directory (dir name).

Environment variable: ROQ_SITE_LAYOUTS_DIR

string

layouts

When enabled it will select all FrontMatter pages in Roq Generator

Environment variable: ROQ_SITE_GENERATOR

boolean

true

Show future pages

Environment variable: ROQ_SITE_FUTURE

boolean

false

The public path containing pages and posts images (relative to the site path)

Environment variable: ROQ_SITE_IMAGES_PATH

string

static/assets/images

This will be used to replace :theme when resolving layouts (e.g. layout: :theme/main.html)

Environment variable: ROQ_SITE_THEME

string

Show draft pages

Environment variable: ROQ_SITE_DRAFT

boolean

false

Format for dates

Environment variable: ROQ_SITE_DATE_FORMAT

string

yyyy-MM-dd[ HH:mm][:ss][ Z]

The default timezone

Environment variable: ROQ_SITE_TIME_ZONE

string

document timezone if provided or system timezone

If this collection is enabled

Environment variable: ROQ_SITE_COLLECTIONS__COLLECTIONS_MAP_

boolean

true

If true, the collection won’t be available on path but consumable as data.

Environment variable: ROQ_SITE_COLLECTIONS__COLLECTIONS_MAP__HIDDEN

boolean

false