Roq the basics

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

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

Directory Structure

The default directory structure is:

my-site/
├── data/                                (1)
│   ├── menu.yml
│   └── tags.yml
│
├── content/                             (2)
│   ├── posts/                           (3)
│   │   ├── 2024-10-14-roq-ssg/
│   │   │   ├── index.md
│   │   │   └── image.jpg
│   │   └── 2024-10-20-heart-roq.md
│   │
│   ├── roq-bottom.md                    (4)
│   └── index.html                       (5)
│
├── public/                              (6)
│   └── style.css
│
└── templates/                           (7)
    ├── partials/                        (8)
    │   ├── head.html
    │   └── pagination.html
    │
    └── layouts/                         (9)
        ├── base.html
        ├── page.html
        └── post.html
└── pom.xml                              (10)
1 Roq Data Files The data/ directory contains data files like menu.yml and tags.yml. These files can hold structured data used across the site.
2 Content Files The content/ directory is where all content of the site that will be generated as pages resides (.html, .md, .adoc*, .json, .xml, …​)
3 Collections The posts/ directory is the default collection in Roq, it is optional. It holds content files for blog posts or similar structured documents. You can configure multiples collections (recipes, events, …​).
4 Additional pages You may provide additional pages files like roq-bottom.md outside a collection. You can also use sub-directories which will be part of the resulting path.
5 Index File The index.html file is required and serves as the homepage. It provides site-wide data using FrontMatter.
6 Static Files The public/ directory holds static files, such as images, CSS, or JavaScript files. These files will not be processed.
7 Templates The templates/ directory is optional as templates can be provided by a theme. It contains Qute templates for partials and layouts.
8 Qute Partials The partials/ directory contains reusable Qute template fragments, such as head.html and pagination.html, which can be included in layouts.
9 Layouts The layouts/ directory defines the structure for pages and documents. For example:
  • base.html is the main layout.

  • page.html and post.html are specific layouts for pages and posts.

10 Build files The build file such as a pom.xml is needed to configure the build. It contains dependencies for your site such as theme and plugins.

Qute and FrontMatter

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

Type-safety doesn’t make it more complex, it just means that using wrong variables and such will result in build error. This way it won’t leak in production.

Templates for layout, documents and pages may also 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

Content

The content/ directory contains the index page and all the pages and collections (such as blog posts). It may also contain attached static files (Page attached static files).

We sometimes refer to pages in collections as documents, documents are just a special kind of pages.

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 may be specified in pages through the layout FrontMatter key (e.g. layout: page). By default, posts will use the post layout and normal pages the page layout (this can be configured through site configuration).

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.2.0</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/index (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/index which refers to the index 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.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 cover image URL of the page with disk check

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

    site.image(String relativePath)

    RoqUrl

    The image from the public images directory with disk check

    site.image('foo.jpg') ⇒ http://example.com/images/foo.jpg

    site.file(String relativePath)

    RoqUrl

    The file from the public directory with disk check

    site.file('foo.pdf') ⇒ http://example.com/foo.pdf

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

    RoqUrl

    Shortcut for site.url.resolve(path)

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

    site.page(String sourcePath)

    Page

    Get a page or document page by source path (e.g. pages/first-page.html)

    site.page('foo.html').url.absolute ⇒ http://example.com/the-foo-page

  • 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 cover image URL of the page with disk check

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

    page.image(String relativePath)

    RoqUrl

    The image from the attached files (for index pages) or from the public image directory with disk check (for other pages)

    page.image('foo.jpg') ⇒ http://example.com/foo-page/foo.jpg

    site.file(String relativePath)

    RoqUrl

    The file from the attached files with disk check

    page.file('foo.pdf') ⇒ http://example.com/foo-page/foo.pdf

    page.date

    ZonedDateTime

    The publication date of the page

    2023-10-01T12:00:00Z

Pages

Any template file without the _ prefix in the site content/ directory (and subdirectories) will be scanned as pages.

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.

Global data

It is possible to declare global data as yaml or json in data/ directory.

For example:

data/foo.yml
bar: Roq

Can be access with {cdi:foo.bar} in any template.

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.

Ok, to dive a bit deeper, we could create a json listing all posts with some info:

content/posts.json
[
{#for post in site.collections.posts} (1)
  {
    "title": "{post.title}",
    "url": "{post.url.absolute}", (2)
    "image": "{post.image.absolute}", (3)
    "date": "{post.date}", (4)
    "read-time": "{post.readTime}" (5)
  }{#if !post_isLast},{/if}
{/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), absolute to get the absolute url.
3 post.url contains the post url (as a RoqUrl), you could also use 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.

How to create custom collections?

You can easily create your own collection, such as documentation, recipes, team members, or conference talks. To do this, simply create a new folder under the content directory. For example, if you’re adding docs, it would look like this:

content/
├── docs
│   ├── 01-chap
│   │   ├── image1.png
│   │   └── index.adoc
│   ├── 02-chap
│   │   ├── image2.png
│   │   ├── index.adoc
└── posts
    └── 2025-01-02-my-first-blog
        └── index.md

In this example, we have two collections: posts and docs. Finally, you need to define the new collection in the config/application.properties (or src/main/resources/application.properties) file using the following properties:

site.collections.docs=true (1)
site.collections.docs.layout=":theme/post" (2)
site.collections.docs.future=true (3)

site.collections.posts=true
site.collections.posts.layout=":theme/post"
Since we’re adding a new collection, it’s also necessary to declare the existing posts collection to ensure it continues to function correctly.
  1. We are adding the new collection docs;

  2. Here, we are declaring the docs layout;

  3. Finally, since the new collection is not a time-based collection, we need to set future as true to show all files.

Now, we can access all the new collection docs data as follows:

{#for doc in site.collections.docs}
- [{doc.title}]({doc.url})
{/for}

Since the new collection is also a normal page, we can use all variables described in the variable section.

Site static files

Site static files are served as-is without any additional processing.

public/
├── image.jpg
└── presentation.pdf

By default, all files in public/ are scanned as static files.

Site static files url can be accessed through site.file('presentation.pdf').

site.file(path) also checks that the file exists on disk and will adapt on site configuration (e.g. root path change)

Page attached static files

Pages may have attached static files (image, pdf, slides, …​). For this, instead of creating a file page, create a directory with an index page:

content/my-page/
        ├── image.jpg (1)
        ├── slide.pdf (1)
        └── index.md  (2)
1 Every non page files in the directory will be attached to the page.
2 Use an index.(html,md,…​) for the page content;
this also works in collections.

In that case, those attached files will be served under the same path as the page and can be accessed via a relative link:

[slide](./slide.pdf)

The resulting link for a page can be different from its directory name, attached files will be relative to the resulting link. This way it works both in IDEs preview and in the browser.

Let’s imagine for a minute that the page link is https://my-site.org/awesome-page/, then the slide will be served on https://my-site.org/awesome-page/slide.pdf.

You can use {page.file("slide.pdf")} to resolve the file url and check that the file exists. This is also useful in other cases, for example from another page (e.g. {site.page("my-page/index.md").file("slide.pdf")}) or if you want the absolute url (e.g. {page.file("slide.pdf").absolute}):

If you want to iterate over page files, they can be listed using {page.files}.

Images

Site images

The site images should be added in the public image directory (e.g. my-site/public/images/image-1.png).

The default public path prefix for images is images/ (this can be changed in the site configuration).

Url can be accessed using the site variable as shown in this example: <img src="{site.image('image-1.png')}" />.

The image method is a convenience and is equivalent to using <img src="{site.file('images/image-1.png')}" />.

Page images

When using pages as directories (such as posts/surf/index.html), {page.image(name)} checks if the file is attached to the given page and return its url.

In other pages (such as posts/basketball.md), {page.image(name)} will act the same as site.image(name) and resolve from the site image directory.

Let’s take this example structure:

my-site/
├── content/
│   └── posts/
|       └── basketball-article.md               (1)
│       └── surf-article/
            ├── cover.jpg
│           ├── surf.jpg                (2)
│           └── index.html
└── public/
    └── images/                         (3)
        ├── basketball-cover.png
        ├── basketball.png
        └── football.jpg
1 With non directory pages, page.image() is equivalent to site.image().
2 Accessible via page.image('surf.jpg') or via a simple relative link only from the index page.
3 Accessible via site.image('basketball.png') on all pages.

Here is how to access those images from the article:

surf-article/index.html
---
image: cover.jpg
---
<h2>👍</h2>
<img src="surf.jpg" /> (1)
<img src="{page.image()}" /> (2)
<img src="{page.image('surf.jpg')}" /> (3)
<img src="{site.image('basketball.jpg')}" /> (4)
<img src="{site.image('basketball.png').absolute}" /> (5)

<h2>👎</h2>
<img src="{site.image('surf.jpg')}" /> (6)
<img src="{page.image('soccer.jpg')}" /> (6)
<img src="{page.image('basketball.jpg')}" /> (6)
1 Relative links are working when using Page attached static files.
2 Will show the page cover image (same as {page.image('cover.png')})
3 surf.jpg is also attached to this page
4 site.image(path) looks into /public/images/ by default (with disk checking).
5 render the absolute url (e.g. https://my-site.org/images/basketball.png)
6 this would throw an error!

Page & Site cover image

Page cover image is referenced in the page FM image data.

some-page.md
---
image: my-page.png
---

{page.image}

The url can be accessed from this template (and its parent layouts) through {page.image}.

index.html
---
image: my-site.png
---

It can be accessed in any template through {site.image}.

Styles and Javascript

Here are two options to consume scripts and styles:

  • Add css and scripts in your site static directory, see Site static files section.

  • Use the Quarkus Web Bundler to bundle your script and styles 👇.

The Quarkus Web Bundler is included by default in Roq.

To use bundling scripts (js, ts) and styles (css, scss), locate them in src/main/resources/web/app/.

my-site/
├── src/main/resources/web/app/
│                           ├── scripts.js
│                           └── styles.scss

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.

Asciidoc support

Asciidoc is supported by Roq using plugins.

Using {something} will be parsed by Qute, to avoid issues with custom attributes, you can either escape it \{something}, or wrap more content inside {| and |}.

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 include it