## Using Roq (copy this section to your project's CLAUDE.md / AGENTS.md) Roq is a static site generator built on Quarkus. It uses the Qute template engine with FrontMatter headers (YAML between `---` delimiters). ### Quick Start 1. Install via JBang: `curl -Ls https://sh.jbang.dev | bash -s - app install --fresh --force roq@quarkiverse/quarkus-roq` 2. Create a site: `roq create my-site` (adds the default theme with example content) 3. Start dev mode: `cd my-site && roq start` (live-reload on http://localhost:8080, most changes are picked up automatically, use `-p 9090` for a custom port, press `s` to force a soft restart if needed) 4. Build static site: `roq generate` (output in `target/roq/`), preview with `roq serve` 5. Add a plugin or theme: `roq add plugin:tagging`, `roq add theme:default` 6. Update to latest versions: `roq update` For a minimal HTML structure without the default theme: `roq create my-site -x theme:base`. Use `--no-code` to skip example content, `--gradle` for Gradle. Available commands: `roq create`, `roq start`, `roq generate`, `roq serve`, `roq add`, `roq update`, `roq blog`. There is NO `roq dev` command, use `roq start` for dev mode. ### AI Agent Integration (Quarkus Agent MCP) AI coding agents (Claude Code, VS Code Copilot, Cursor, etc.) can manage Roq dev servers using [Quarkus Agent MCP](https://github.com/quarkusio/quarkus-agent-mcp). It handles lifecycle (start, stop, soft restart via `s`), log capture, and proxies to Dev MCP tools in the running app. **Install via JBang:** ``` jbang app install --fresh --force quarkus-agent-mcp@quarkusio ``` **Configure in Claude Code:** ``` claude mcp add quarkus-agent -- jbang quarkus-agent-mcp@quarkusio ``` **Configure in VS Code / Cursor (`.vscode/mcp.json`):** ```json { "servers": { "quarkus-agent": { "type": "stdio", "command": "jbang", "args": ["quarkus-agent-mcp@quarkusio"] } } } ``` Once configured, the agent uses `quarkus_start` to launch the Roq dev server, `quarkus_restart` for soft restart, `quarkus_logs` to read output, and `quarkus_searchTools` / `quarkus_callTool` to interact with the running app. ### Directory Structure ``` my-site/ ├── content/ # Pages, collections (posts/, etc.), index.html (required) ├── templates/ │ ├── layouts/ # Page layouts (page.html, post.html) │ └── partials/ # Reusable template fragments ├── data/ # Structured data files (YAML/JSON), accessible via {cdi:filename.property} ├── public/ # Static assets served as-is (images, PDFs) ├── web/ # JS/CSS sources (bundled by Quarkus Web Bundler) └── config/ └── application.properties # Site config with site.* prefix ``` ## Installing Roq Skill Files for AI Coding Assistants Roq extensions ship detailed reference docs as skill files inside their deployment JARs at `META-INF/quarkus-skill.md`. Follow these steps to install them in your project. ### 1. Find your Roq extensions and version ``` mvn dependency:list -DincludeGroupIds=io.quarkiverse.roq -DoutputAbsoluteArtifactFilename=true ``` ### 2. Extract the relevant skills Extract from the matching JAR in your local Maven repository: ``` unzip -p ~/.m2/repository/io/quarkiverse/roq/ARTIFACT_ID/VERSION/ARTIFACT_ID-VERSION.jar META-INF/quarkus-skill.md > .claude/skills/SHORT_NAME.md ``` Install to the appropriate skills directory (`.claude/skills/` for Claude Code), or append to your CLAUDE.md/AGENTS.md. Install a skill for each `*-deployment` JAR listed in the dependency output. ### 3. Keep skills in sync After running `roq update`, re-extract the skill files to match the new version. Compare the Roq version in your dependencies with the version of the installed skill to detect staleness. **Migrating to Roq:** - [Migrating to Roq](https://iamroq.dev/docs/migrating/): phased workflow, syntax mappings, configuration reference, and LLM prompts for migrating a Jekyll/Hugo site to Roq **Skill files (latest version for reference):** - [quarkus-roq-frontmatter-deployment](https://raw.githubusercontent.com/quarkiverse/quarkus-roq/main/roq-frontmatter/deployment/src/main/resources/META-INF/quarkus-skill.md): base reference (FrontMatter pages, layouts, collections, pagination, template variables, Qute syntax, built-in tags, data files, template extensions, configuration, common pitfalls) - [quarkus-roq-deployment](https://raw.githubusercontent.com/quarkiverse/quarkus-roq/main/roq/deployment/src/main/resources/META-INF/quarkus-skill.md): full Roq extras (directory structure, themes, RSS, LLMs.txt, static generation, testing, CLI commands) - [quarkus-roq-data-deployment](https://raw.githubusercontent.com/quarkiverse/quarkus-roq/main/roq-data/deployment/src/main/resources/META-INF/quarkus-skill.md): data file mapping with @DataMapping and CDI --- The following is the content of this blog site: # Hello, world! I'm Roq — a funny little SSG (Static Site Generator) with a Java soul and Quarkus energy — Open Source and Free. > An Open Source static site generator (SSG) that makes it fun and easy to build websites and blogs. It's built with Java and Quarkus under the hood. ## Posts ### [How AI Helped Me Rebuild My Blog and Move from Jekyll to Quarkus Roq](/posts/how-ai-helped-me-rebuild-my-blog-and-move-from-jekyll-to-quarkus-roq/) Introduction I don't blog that often. It's not because I don't have ideas, quite the opposite. I have lots of them: ideas I want to share, experiments I want to document, things I want to remember. The real problem has always been time. And yet, a personal blog is such a great place for all of this: sharing ideas, writing things down before they're forgotten, and keeping track of what you've learned along the way. How many times have you been in this situation? "I've already used this technology before… but it was on a customer project." Three years later, you're asked to work on it again, and you have absolutely no idea how you did things back then. A blog is a memory extension. A personal site is also a good way to keep your CV up to date: to show what you've worked on, what you've learned, and how your experience evolves over time. It's not just about writing posts, it's about telling your professional story. Another thing that slowed me down was language. I'm never fully sure whether I should write in English or French. English makes more sense if I want to reach more people, but it's not my native language, so I've always felt a bit uncomfortable writing long-form content in it. On top of that, my blog itself felt old. The design was… okay-ish. Good enough when I started. I used a Jekyll theme made by someone else (Freshman21), and I'm genuinely thankful to the author for that work, it helped me get started. But over time, I realized the site didn't really match my personality anymore. The theme felt too "lambda" (too generic). On the technical side, Jekyll with GitHub Pages works fine, but as soon as you want to customize things, you start feeling a bit naked. I also kept wishing for a site generator more aligned with my daily work, something Java-based. So yeah: time, language, visual identity, and technology were all reasons why I kept postponing this refresh. But recently, AI changed the equation. This is what my blog looked like before the redesign and migration. The old Jekyll version is now archived at https://sunix.github.io/old-jekyll-blog.sunix.org/ Keeping Jekyll, Redoing the Design (Without a Theme) Let's be honest: I'm not very good at design stuff. I may have ideas, but CSS and I… we don't really get along. And it's not only about CSS. Whether it's Jekyll with Ruby, or even Hugo, I only know the basics. Once you want to go a bit further, things quickly become harder, and progress slows down. At some point, I seriously considered switching technologies. I had heard about Quarkus Roq some time ago, and the idea of a Java-based static site generator was very appealing. But there was one big question in my head: How am I going to migrate my existing Jekyll theme to Roq? After discussing with Andy, the creator of Roq, it became clear pretty quickly that this wouldn't be easy, at least not at my level. Roq uses Qute as its templating engine, which is very different from what Jekyll uses. I took a look at how a Roq theme is structured, and honestly, it's quite straightforward. But migrating the Freshman21 Jekyll theme to Roq? That felt like a rabbit hole I really didn't want to go down. So I gave up. That was last summer. A Real Project, at the Right Time Pretty much at the same time, I got a call from my tennis club asking for help. Recently, the FFT (French Tennis Federation) shut down all CMS-based websites for many tennis clubs across France. Since I'm part of one of those clubs, I volunteered to rebuild a brand-new website from scratch. It turned out to be the perfect opportunity to finally try Roq. This time, I didn't have the Jekyll theme migration problem. No legacy constraints, no existing templates to port. I could start fresh. So I asked ChatGPT to generate a pure HTML/CSS website that could later be moved easily to any static site generator. The result was simple and clean: Tailwind CSS for styling Two static HTML pages: a main page with different sections an article page with a real content structure Once I had that, moving it to Roq was surprisingly straightforward. I extracted the common parts (header, footer, navigation) into templates/partials HTML fragments, then created proper layouts for: the main pages the article/post pages Each layout simply includes the relevant partials. For some parts of the site, I needed dynamic content. That's where I really enjoyed working with Roq. Instead of fighting a plugin system, I could just write Java. For example, I wanted file and image attachments to be automatically added to certain pages for download. This wasn't available out of the box, but creating a Qute template extension in Java was trivial. I ended up with a small helper like this: https://github.com/tc11-fr/tc11.fr/blob/main/src/main/java/fr/tc11/FilesViewHelpers.java Which is then used directly inside templates, for example here: https://github.com/tc11-fr/tc11.fr/blob/main/templates/layouts/post.html#L55 An example article page displaying an image and a downloadable attachment, powered by a custom Java extension. That moment really clicked for me. I wasn't hacking around limitations anymore, I was extending the system in a clean, explicit way, using a language I'm comfortable with. The Paris 11 Tennis Club website 🎾 is now live: 🌐 Website: https://tc11.fr/ 💻 GitHub: https://github.com/tc11-fr/tc11.fr Coming Back to My Blog, Starting With the Design With the Paris 11 Tennis Club website done, I finally had my first real Roq static website in production. At that point, I knew two important things. First, I wouldn't need to rely on an existing theme to get a nice design anymore. Second, I could rely on AI to handle most of the CSS part, which, for me, is a huge relief. I also realized that my initial idea, migrating an existing Jekyll theme to Roq, was simply not the right strategy. The theme itself was the problem. Migrating it would take a lot of time, and I'd rather spend that time building my own design that fits my personality. Getting rid of the Freshman21 Jekyll theme on my blog would make the transition from Jekyll to Roq much easier. The theme was the real blocker, not the content. So I came up with a simple plan. The first step was to completely drop the existing Jekyll theme and ask ChatGPT to generate a fresh website skeleton with my design. Just like I did for the tennis club site, the idea was to start with a pure HTML/CSS static website. From there, I could extract reusable HTML fragments and make them work with Jekyll. Once I had the new design running with Jekyll, without any theme, I knew the final step would be much simpler: moving the site to Quarkus Roq. Finding a Visual Direction (With a Lot of Help) When it came to design, I had ideas… but nothing very precise. My very first prompt was extremely simple: "I'd like to refresh my blog website from a design point of view. Can you propose something cool?" ChatGPT came back with several design directions. They were all fine, but nothing really clicked. Everything felt a bit too safe, a bit too generic. So I pushed further: "I want something with a lot of colors, but still tech-oriented. Something that inspires joy." It generated a few concepts, but they didn't really feel joyful. After asking for something colorful and tech-oriented, I wondered what would happen if I went even further: "Ok, what would a street art style look like?" The results were interesting: bold, very expressive, lots of neon colors and heavy contrast. After ruling out the street art direction, I tried to refine the idea instead of pushing it further: "Street art that inspires creativity… but also code." Once again, ChatGPT proposed several examples. Most of them were interesting, but still not there. And then one image caught my eye. It wasn't loud. It wasn't aggressive. But it was expressive, colorful, and clearly modern. After a bit of digging, I realized it came from an article called "How To Design the Perfect Hero Image": https://htmlburger.com/blog/hero-image-guide/ From Inspiration to a First Hero Illustration Attempt I really liked the hero image style I had discovered, so the next step felt obvious: try to generate my own version of it. I described what I had in mind like this: "A banner with a drawing similar to the 'How To Design The Perfect Hero Image' example: mostly black-and-white with a few elements in color. But instead of a woman on a couch, a developer wearing headphones, coding, and generating code that transforms into something (I don't know what yet)." The idea was clear: mostly black and white a calm, focused developer a splash of color to represent creativity code flowing out and turning into ideas ChatGPT generated an image… and it was close. Very close. But not quite what I wanted. Narrowing the Style: Less Noise, More Intention At that point, I realized something important: the problem wasn't the idea, it was the style. So I copied the reference image and told ChatGPT, very clearly: "No, in this style." That made all the difference. The illustration wasn't black and white as I originally suggested, but the result was even better: a pastel style with controlled colors. And the developer really looked like me… except for the missing glasses. Then I added one last detail: "And with glasses." 👓 And honestly… that was it. It felt modern, joyful, and clearly tech-oriented. Most importantly, it finally felt like my blog. From a Pretty PNG to a Working Website Skeleton Once the hero illustration direction was clear, I wanted to stop "designing in my head" and start moving pixels on a real page. So I asked for what I actually needed: not another image, not a moodboard, but a concrete, runnable mockup. "Can you make me a complete mockup in this style?" "Make me an HTML/CSS/Tailwind template, ready to plug in." That changed the workflow completely. Instead of iterating on vague concepts, I now had a full HTML page with: a navbar (Articles, Tags, About…) a hero section designed around the illustration basic typography, spacing, and layout rules a structure reusable for posts and content pages The first version used Tailwind via CDN, which was perfect for prototyping: copy/paste, open a browser, iterate fast. Getting the Hero Illustration Into the Skeleton (The "Transparent Background" Trap) My skeleton was working, but it was still missing the most important part: the hero illustration. So I tried the obvious next step: ask ChatGPT to generate the same hero image again, but with a transparent background. In theory: perfect. In practice: not really. The new image had a transparent background, as requested, but some details had disappeared, the mouth was missing, and the rocket and the light bulb looked unfinished. It felt more like a draft than the polished illustration I had before. It was a good image… just not the one. So I went back to the previous "perfect" image. AI got me 90% of the way, and for the last 10%, I brought out the classic: GIMP. I extracted the character + desk + rainbow flow, cleaned the edges, removed the background, and exported a version ready to be integrated into the HTML page. Not glamorous, but effective. And once it was done… huhey! 🎉 I finally had the hero image exactly the way I wanted, in a format that actually works on the web. At that point, the design was no longer just an idea. I had the hero illustration, the HTML layout, and the Tailwind CSS, something real I could build on. The next step was simple: make it work with Jekyll first. I wasn't going to redesign and migrate to Quarkus Roq all at once. I preferred small steps, each one reducing risk and keeping things manageable. GitHub Issues Driven Development (a.k.a. Coding Without an IDE) Recently, I adopted a new way of building applications with AI, and without even opening an IDE. I call it GitHub Issues Driven Development. The idea is simple: instead of starting in my IDE, I start with GitHub Issues. For each new feature or bug fix, I create an issue, assign it to @Copilot, and let it handle the first iteration. Copilot creates a pull request and does the work. I don't sit there watching it, I just go on with my day. Then, during my next coffee break, I come back to review the PR. If something isn't right, I leave feedback directly in the PR comments, mentioning @copilot. It adjusts the code, I review it again, and we iterate like that. Short cycles, low mental load, very little context switching. Most of the time, I end up merging the PR without ever opening my IDE. I've already written more details about this workflow in a previous post: 👉 https://blog.sunix.org/posts/building-a-gift-card-management-app-with-github-copilot-my-first-completed-side-project/ Applying This Workflow to My Blog Redesign So once I had the HTML/CSS skeleton and the hero illustration ready, I didn't start manually refactoring files. Instead, I went back to GitHub. In my blog repository, I created a new issue with a very explicit description: Title: Redesign the website Remove the current theme Use a custom design inspired by the provided HTML/CSS Include the new hero illustration on the main page I pasted: the generated HTML/CSS the hero illustration That was enough. Copilot picked up the issue and did the heavy lifting: removed the existing Jekyll theme reorganized the layouts integrated the new design wired everything together so the site still builds correctly You can see the issue here: 👉 https://github.com/sunix/blog.sunix.org/issues/44 And the resulting pull request here: 👉 https://github.com/sunix/blog.sunix.org/pull/45 Of course, it wasn't perfect on the first try. I had to adjust a few minor things, fix small issues, and guide Copilot through comments. But overall, it did the job. More importantly, it fit perfectly within my constraints: limited time, short bursts of focus, and the desire to keep momentum without mentally reopening a big "side project." Previewing a Pull Request (Without an IDE) With this workflow, working mostly from GitHub and without an IDE, it's hard to validate changes just by looking at a diff in a pull request. Sure, GitHub Copilot runs tests (and you can trust them… to a point), but when you're working on a website, you really want to see the result. With Quarkus Roq in a local development environment, I would normally just run: roq But here, I'm only using the GitHub UI and reviewing a PR. So I needed a way to preview what Copilot generated. The /preview Command On several GitHub Pages projects I've worked on recently, I set up a simple but very effective mechanism: a /preview comment on a pull request. When I comment /preview on a PR, it triggers a GitHub Action that: checks out the PR branch builds the static site deploys it to surge.sh (a static site hosting service with a free plan, more than enough for preview environments) posts the preview URL directly as a comment on the PR adds a clear banner to indicate that the preview site is not the production version Technically, the deployment is very simple: build the site, then run the surge command. This gives me a real, clickable version of the site to review during my coffee break, exactly what I need when I'm not opening an IDE. I simply comment /preview on the PR, and the preview site is automatically deployed and made available. If the deployment fails, I can investigate by checking the GitHub Actions workflow logs. In some projects, I also added a banner to clearly indicate that this is only a preview site, with a link back to the corresponding pull request. Bootstrapping the Feature With… an Issue And yes, I use the same GitHub Issues Driven Development workflow to set this up. When I want this feature in a new GitHub Pages project, I usually create an issue like this: Title: Add a preview command in GitHub PR comments to have a preview on surge.sh Description: Getting inspiration from this PR: https://github.com/SCIAM-FR/sciam-fr.github.io/pull/151 Assignee: @copilot Most of the time, Copilot proposes a pull request that includes: the GitHub Action workflow the /preview command handling the deployment logic From there, I review it, tweak it if needed, and merge it. This preview mechanism is what makes the whole workflow viable. Without it, reviewing HTML/CSS changes blindly would be frustrating. With it, I can confidently validate design changes, layouts, and content, even when everything happens through GitHub. Moving from Jekyll to Quarkus Roq At this point, I had a Jekyll site without a theme. No complex plugins, no hidden magic, just content, layouts, and HTML. So I thought: This should be easy now. Time to move to Quarkus Roq. And of course, I followed the same workflow as before: create a GitHub issue and assign it to @copilot. Since I had already built a GitHub Pages site with Roq for the tennis club, I simply reused that repository as a reference. Issue: https://github.com/sunix/blog.sunix.org/issues/60 Title: Replace Jekyll with Quarkus Roq Description: Getting inspiration from https://github.com/tc11-fr/tc11.fr Copilot picked it up and produced a pull request: 👉 https://github.com/sunix/blog.sunix.org/pull/61 At first glance, it looked good. The project structure was there, the site was building, and the content was being rendered. But… it didn't work out of the box. Reality Check: Migration Is Never Just One Issue Once I started testing things more carefully, a few problems surfaced. So I did what I now do instinctively: I created more issues. https://github.com/sunix/blog.sunix.org/issues/63 Qute was interpreting ${current.class.fqn} inside code blocks as a template expression, causing rendering failures with errors like: Key 'current' not found. (Note: Roq 2.1 now supports an alternative expression syntax using {# and {= prefixes, which avoids this issue entirely.) https://github.com/sunix/blog.sunix.org/issues/65 The GitHub workflow was uploading the GitHub Pages artifact twice. The quarkiverse/quarkus-roq@v1.1 action already uploads it, and the workflow tried to upload it again, causing a conflict. Each issue described one concrete problem. Each one was assigned to @copilot. And step by step, Copilot fixed them. At that point, everything still wasn't perfect. But the important thing was this: 👉 The site was now running on Roq. That was a big milestone. From there, I wasn't migrating anymore, I was improving. The Next Problems to Solve Once the migration was complete, a new checklist appeared. The site was running on Roq, but it wasn't fully production-ready yet. Here's what still needed to be addressed: Excerpts were no longer working Old URLs were broken and needed proper redirects Disqus comments were gone Tailwind was still being loaded via CDN instead of using a production build None of these issues were critical on their own. The site worked. The content was accessible. But together, they made the difference between: "It runs." and "It's clean, polished, and production-ready." Each of these problems deserved its own issue, and its own iteration. And that's exactly how I approached them. Bringing Back Excerpts (<!-- more -->) One thing that immediately stood out after the migration was the lack of excerpts. On my Jekyll blog, I was using the classic <!-- more --> marker to define a short preview of each post, displayed on the homepage. It's a small detail, but an important one: excerpts make the homepage more readable and give context before clicking into an article. After migrating to Quarkus Roq, that feature was simply gone. The posts were still there, but the homepage list only showed metadata: title, tags, date, and a "read more" button. No preview text anymore. After migrating to Roq, the homepage list only displays metadata (title, tags, date) and a "read more" button, the excerpt is missing. This is how it should be: meaningful preview text extracted from each article, giving context before clicking "read more." I noticed it right away during the migration PR, but I deliberately chose not to fix it there. The migration pull request was already quite large, and I prefer one PR per concern. Also, the site was usable without excerpts, so it wasn't blocking. I even documented the limitation directly in the PR comments: Note on article excerpts: Roq doesn't expose the content before <!-- more --> through the template API. The articles contain the marker in their Markdown, but there's no excerpt or content property available on the post object. The current layout only shows tags, title, date, and the read more button. At that point, it was clear that this wasn't just a template tweak. Turning It Into a Proper Issue Later on, once the migration had stabilized, I went back to my usual GitHub Issues Driven Development workflow. I created a dedicated issue: https://github.com/sunix/blog.sunix.org/issues/68 Title: Implement Excerpt capability In the issue description, I explained the problem and proposed a technical solution. To extract the content before <!-- more -->, we would need to: Create a Qute @TemplateExtension that adds an excerpt() method to DocumentPage Access the raw Markdown content Parse it and extract everything before the marker Convert it to HTML Strip the HTML tags and return clean preview text In short: this wasn't just templating, it required custom Java code. I even sketched the idea directly in the issue: @TemplateExtension public class ExcerptExtension { public static String excerpt(DocumentPage post) { // Read markdown file // Extract content before <!-- more --> // Convert to HTML // Strip tags return extractedText; } } And then I concluded the issue with a simple decision: Let's create this extension. And… It Worked 🎉 Copilot picked up the issue and implemented the whole solution in this PR: 👉 https://github.com/sunix/blog.sunix.org/pull/69 And just like that, excerpts were back. The homepage now displays meaningful previews again, just like it did with Jekyll, but this time implemented cleanly in Java as a proper extension. This was one of those moments where Roq really shined for me: No plugin hacks No fragile template tricks Just explicit, readable Java code Problem solved. Hooray 🚀 Update: Since Roq 2.1, content abstracts are now built in. You can use {=post.contentAbstract} in your templates to get a word-limited preview of any post, no custom extension needed. Tailwind in Production Once the site was running on Roq, I opened the browser console. And there it was: (index):64 cdn.tailwindcss.com should not be used in production. To use Tailwind CSS in production, install it as a PostCSS plugin or use the Tailwind CLI: https://tailwindcss.com/docs/installation And indeed, I was still using: <script src="https://cdn.tailwindcss.com"></script> <script> tailwind.config = { darkMode: "class", theme: { extend: { fontFamily: { ... }, boxShadow: { ... } } } } </script> In this mode, Tailwind downloads a JavaScript file in the browser and generates the final CSS dynamically at runtime. That's perfectly fine for development. But not ideal for production. Why CDN Tailwind Is Not Ideal There are a few reasons: It relies on an external JavaScript resource (which can be problematic in restricted environments). The JS file is larger than the compiled CSS would be. CSS is generated dynamically in the browser on every page load. Slightly slower performance overall. For a proper production setup, Tailwind should: Scan your templates at build time. Generate only the CSS classes you actually use. Output a small, optimized static CSS file. That's the correct way to use Tailwind in production. Doing It Properly With Roq I had already solved this problem for the Paris 11 Tennis Club website. At first, I implemented a manual Maven exec command: 👉 https://github.com/tc11-fr/tc11.fr/pull/111 Later, I discovered that Quarkus Roq supports Web Bundler + Tailwind (and since Roq 2.1, Tailwind works with zero configuration via the default theme), so I switched to the proper integration: 👉 https://github.com/tc11-fr/tc11.fr/pull/120 So applying the same approach to my blog looked straightforward. I implemented it here: 👉 https://github.com/sunix/blog.sunix.org/pull/85 But… it wasn't working. This is what happened when switching to dark mode: the text color changed, but the background didn't, leaving grey text on a white page and making the article difficult to read. Dark Mode Was Broken After switching to the production build setup, the dark/light mode toggle stopped working properly. Copilot tried to fix it: 👉 https://github.com/sunix/blog.sunix.org/pull/93/commits/b74d4bd5fbe4053882d9f98805b8d72f1f9b4ed9 But the fix only addressed the button state, not the actual theme-switching logic. So I went into debugging mode. I compared the new version with an older working one and started investigating the differences. During a live Copilot session, I finally discovered the real issue: 👉 I wasn't using the correct Tailwind version. I provided some guidance, but Copilot did most of the work, and helped uncover that the project was using an outdated version of the Quarkus Web Bundler. The setup was supposed to work with Tailwind CSS 4, but my project was effectively using an older configuration via an outdated Quarkus Web Bundler plugin. Even worse, Copilot hadn't initially spotted that the project was using older versions of: Quarkus quarkus-web-bundler-tailwindcss Once I upgraded the relevant dependencies, everything fell back into place. The final fix was mainly: Upgrading Quarkus Upgrading the Tailwind Web Bundler extension Aligning everything with Tailwind CSS v4 Tip: Since Roq 2.1, you can simply run roq update to keep all your dependencies in sync and avoid this kind of version mismatch. After that, dark mode worked perfectly again, and Tailwind is now compiled at build time into a properly optimized CSS file. Tailwind is compiled at build time in both dev and production modes, so what you see in dev mode is the same optimized output you get in production. After upgrading the dependencies, dark mode finally behaves as expected, proper background, proper contrast, fully readable content. Minor but Important: Old URL Redirects Another issue appeared after the migration. Over the years, I had shared blog posts on social media using Jekyll-style URLs like: /articles/howto/2026/01/11/feeling-powerful-with-just-a-browser.html But the new Roq site uses cleaner URLs like: /posts/feeling-powerful-with-just-a-browser-working-around-a-broken-tennis-booking-system/ The result? Ugly 404 pages for old links. Not great. These links were already out there, on social media, in bookmarks, maybe even in other blog posts. Breaking them wasn’t acceptable. First Attempt: The Wrong Direction Once again, I created an issue: 👉 https://github.com/sunix/blog.sunix.org/issues/80 Copilot’s first solution was to generate static HTML redirect files using a separate Main class. Technically, it worked. But architecturally? I didn’t like it. It felt like stepping outside the spirit of Quarkus Roq. It introduced a custom mechanism that lived outside the framework instead of using the tools already provided by Roq. It solved the problem, but not in the right way. The Proper Way: Plugin Aliases While researching, I discovered the Roq plugin-aliases feature: 👉 https://iamroq.dev/docs/plugins/#plugin-aliases That looked much cleaner. So I commented directly in the pull request: @copilot Sorry, I don’t like the idea of going outside Quarkus Roq with an external Main class. Could we explore aliases instead? That was the right direction. The final implementation uses Roq’s alias mechanism properly: 👉 https://github.com/sunix/blog.sunix.org/pull/83/changes Each old article now defines its legacy paths as aliases. Yes, it required updating each article to declare its old URLs. But that’s actually what I wanted: explicit, controlled redirects, fully inside Roq. Clean. Maintainable. Aligned with the framework. And That’s It There were other small fixes and improvements along the way. But these were the most interesting ones: redesigning without a theme using AI for mockups and iteration GitHub Issues Driven Development previewing PRs with /preview migrating to Quarkus Roq implementing excerpts properly fixing Tailwind for production handling old URL redirects cleanly This whole journey wasn’t just about changing a blog engine. It was about: reducing friction owning the design simplifying the stack and building in small, controlled iterations AI has been a game changer in recent months. Things are moving very fast. I would never have done all of this without it. Sometimes you have ideas, and you know how something works in theory, but implementing it is slow and painful. For me, CSS is one of those areas. It’s not that I don’t understand it… it’s just time-consuming and frustrating. AI helped remove that friction. But in the end, this isn’t just something generated by a machine. I now have a design that truly fits my personality. I made the decisions. I iterated. I refined. AI was a powerful assistant, not the author. And honestly, I don’t think I would have gone this far without these tools. That’s where I am right now. If you’re considering moving to Quarkus Roq, or refreshing your blog design, I hope this gives you ideas, and maybe the confidence to try. You don’t need to be a designer. You don’t need to have weeks of free time. You just need a few good issues… and a couple of coffee breaks ☕🚀 Last but not least, if you liked this post, feel free to leave a ⭐ on the GitHub repository of my blog https://blog.sunix.org. It helps me know the content was useful to someone 😉 Happy coding. Happy blogging. ### [GFM Alert Blocks: Styled Callouts in Your Markdown](/posts/gfm-alert-blocks-styled-callouts-in-your-markdown/) Roq now supports GitHub Flavored Markdown (GFM) alert blocks (also known as admonition blocks) — the styled callouts you see on GitHub READMEs and issues, complete with icons and color themes: Note Useful information that users should know, even when skimming content. Tip Helpful advice for doing things better or more easily. Important Key information users need to achieve their goal. Warning Urgent info that needs immediate user attention to avoid problems. Caution Advises about risks or negative outcomes of certain actions. How It Works Alert blocks use a special blockquote syntax with a type identifier: > [!NOTE] > Your note content here. The five standard types are: Type Icon Color Use Case NOTE Info circle Blue General information TIP Light bulb Green Helpful suggestions IMPORTANT Verified badge Purple Critical information WARNING Alert triangle Orange Potential issues CAUTION Stop octagon Red Dangerous actions Icons are from GitHub Octicons (MIT license). Custom Alert Types You can configure custom alert types beyond the standard five. Add this to your application.properties: quarkus.qute.web.markdown.plugin.alerts.custom-types.INFO=Information quarkus.qute.web.markdown.plugin.alerts.custom-types.BUG=Known Bug quarkus.qute.web.markdown.plugin.alerts.custom-types.SECURITY=Security Notice Then use them in your markdown: > [!INFO] > This is a custom info alert. > [!BUG] > This is a known issue. > [!SECURITY] > This is a security notice. Custom alert types get basic styling (border, padding, rounded corners) but no icon or color by default. To add them, see the Styling section below. Custom alerts without custom CSS: Information This alert has basic styling but no icon or color. Known Bug Same here — add custom CSS to style it. Security Notice And this one too. Styling The roq-default theme includes full styling for the 5 standard GFM alert types: icons, colored borders, pastel backgrounds, and dark mode support. How SVG Icons Work Icons use the mask-image + background-color technique. The SVG defines only the shape (mask), and background-color: var(--alert-color) fills that shape with a color. This means one SVG works in any color — including dark mode. The SVGs are inlined as data URIs in the CSS using URL-encoded format: --alert-icon: url("data:image/svg+xml,%3Csvg%20xmlns%3D...%3E%3Cpath%20d%3D%22...%22/%3E%3C/svg%3E"); Icons are from GitHub Octicons (MIT license). For Custom Themes If you're using a custom theme, add alert styling to your CSS: .markdown-alert { padding: 1rem; margin: 1rem 0; border-radius: 0.5rem; border-left: 4px solid; } .markdown-alert-title { display: flex; align-items: center; gap: 0.5rem; font-weight: 600; margin-bottom: 0.25rem; } .markdown-alert-title::before { content: ""; display: inline-block; width: 1rem; height: 1rem; flex-shrink: 0; background-color: var(--alert-color); mask-image: var(--alert-icon, none); mask-size: 100%; mask-repeat: no-repeat; mask-position: center; } /* Standard types */ .markdown-alert-note { --alert-color: #0969da; --alert-icon: url("data:image/svg+xml,..."); /* info-16 SVG as data URI */ border-color: #0969da; background: #0969da08; } .markdown-alert-note .markdown-alert-title { color: #0969da; } Adding Icons & Colors for Custom Types To style a custom alert type (e.g., INFO), add CSS with the --alert-icon and --alert-color variables: .markdown-alert-info { --alert-color: #0550ae; --alert-icon: url("data:image/svg+xml,..."); /* your SVG as data URI */ border-color: #0550ae; background: #0550ae08; } .markdown-alert-info .markdown-alert-title { color: #0550ae; } .markdown-alert-bug { --alert-color: #cf222e; --alert-icon: url("data:image/svg+xml,..."); /* bug SVG as data URI */ border-color: #cf222e; background: #cf222e08; } .markdown-alert-bug .markdown-alert-title { color: #cf222e; } .markdown-alert-security { --alert-color: #da3633; --alert-icon: url("data:image/svg+xml,..."); /* shield-16 SVG */ border-color: #da3633; background: #da363308; } .markdown-alert-security .markdown-alert-title { color: #da3633; } Which of these alert blocks will you use first? ### [Set It in Roq: The Editor that changes the game!](/posts/set-it-in-roq-the-editor-that-changes-the-game/) Roq started as a solid foundation for building modern apps and static sites. But now, it’s leveling up in a big way. With the introduction of a TipTap-powered Editor with Markdown support, Roq is no longer just an SSG tool, it’s stepping into CMS territory. Why This Is a Big Deal Until now, writers had to: Use an IDE or a text editor. Manually create new article files. Manually open the article preview. Use Markdown as code Now, with Roq’s built-in editor: Native integration: all integrated in Quarkus dev experience. Rich Text Editor with Markdown support: write your content in a Notion-like editor, render beautifully. Preview article: directly from the editor or using a new tab. This makes Roq feel less like a static site generator and more like a developer-friendly CMS, closer to the flexibility of WordPress but without the heaviness. Key Features Rich formatting: Bold, italic, headings, lists. Markdown support: Switch between rich text and Markdown seamlessly. Code editor with syntax highlighting: For HTML and AsciiDoc content. Media embedding: Images, links, and more. How to Try It The editor is natively integrated into Roq 2.1. roq create my-blog Start it roq 🚀 Hit a (like Admin) to Open The Roq Editor. ### [Roq 2.1 is here!](/posts/roq-2-1-is-here/) Hello fellow Roqers, Roq 2.1 is officially out, and it's packed with features that make content creation smoother, the developer experience richer, and the tooling more powerful than ever. This release is big enough that we're covering it as a series of posts. We already published posts about the built-in editor and GFM alert blocks, and this post gives you the full picture of everything else that landed. The highlights Here's what's new in Roq 2.1: Built-in Content Editor with rich text, Markdown support, and image upload GFM Alert Blocks (NOTE, TIP, WARNING, CAUTION, IMPORTANT) with icons and themed colors Standalone Roq CLI powered by Quarkus Picocli LLMs.txt generation for AI discoverability Custom error pages with source info and hints in dev mode Default theme with TailwindCSS and zero configuration Raclette link checker integration in roq-testing RSS content modes (full content or word-limited abstracts) Dynamic pages from data collections Qute alternative expression syntax support Simplified layout resolution with theme-layout support The Roq CLI Roq now ships as a standalone CLI tool, no Maven or Gradle knowledge required: roq create my-blog cd my-blog roq That's it. The CLI handles project creation, dev mode, static generation, plugin management, and updates. It's built as a Quarkus Picocli application and distributed via JBang. LLMs.txt Roq can auto-generate /llms.txt and /llms-full.txt following the llms.txt specification. AI systems like ChatGPT, Claude, and Perplexity use these files to understand your site's structure and content. Dynamic pages from data You can now generate pages dynamically from data collections, perfect for catalogs, team pages, or any content driven by structured data files. Developer experience Dev mode got a lot of love in this release: Custom error pages with source file location, detailed hints, and available alternatives when something goes wrong Centralized filesystem watcher for reliable live reload Simplified layout resolution with the new theme-layout key for explicit theme layout targeting What's coming next in this series Upcoming posts will dive deeper into: The Roq CLI LLMs.txt and AI discoverability Dynamic pages from data The new default theme and TailwindCSS integration Ready to try it? Check out the Getting Started guide, or if you're already on Roq, see how to update. Stay tuned and happy Roqing! ### [Roq 2.0 and Java Advent Calendar article](/posts/roq-2-0-and-java-advent-calendar-article/) Hello fellow Roqers, I’m thrilled to announce that Roq 2.0 is here 🚀—and it’s a big one! 🔥 Think of it like a new iPhone launch: most of the magic happens under the hood. Many changes aren’t immediately visible, but they’re packed with powerful developer features that make a huge difference. 🕵️ Added lightning filesystem watcher for live reload 📂 Allow web directory at the root of the Roq site 🧩 Simplified default app structure: supports web/app.js and web/app.scss (or web/app/app.js like before …​) ⚡️ TailwindCSS support without any config 💫 Directory support for data and allow iterating on nested data files using the directory name It might not look like much at first glance, but this release represents a long journey to build a solid foundation. That foundation now makes it possible to support plugins like TailwindCSS, Svelte, and Vue — true to the Quarkus spirit, with zero configuration required. I might write a blog post about Web Bundler 2.0, which makes all this possible. The native binding with architecture driven Maven/Gradle dependencies is pretty cool.. let me know in the comments if that would interest you. I’ve also spent time writing a tutorial to showcase all these new features. It’s published in the Java Advent Calendar alongside other cool Java content to explore this Christmas. Take care and happy coding! 🎄 ### [Major site migrations to Roq](/posts/major-site-migrations-to-roq/) Two prominent websites have recently completed their migration to Roq: wildfly.org and jbang.dev. These migrations mark a significant milestone in the adoption of Roq as a modern static site generator. WildFly.org on the roq The official WildFly project site, wildfly.org, has transitioned from its legacy setup on Jekyll. This move brings faster build times, simplified deployment, and better integration with modern Java tooling. WildFly is a powerful, modular, and lightweight Java application server that provides all the tools you need to build robust enterprise applications. https://github.com/wildfly/wildfly.org/ JBang.dev is awesome right? Similarly, jbang.dev, the home of the JBang scripting tool, has now adopted Roq for its site generation instead of Jekyll. JBang let Students, Educators and Professional Developers create, edit and run self-contained source-only Java programs with unprecedented ease. https://github.com/jbangdev/jbang.dev Roq’s Enhanced AsciiDoc Support Roq now offers robust support for AsciiDoc content. This includes: Header parsing: Roq can extract metadata from AsciiDoc headers, enabling dynamic routing and layout selection without FrontMatter header. Includes support: Authors can modularize content using AsciiDoc includes, making documentation more maintainable and reusable. These features make Roq an excellent choice for documentation-heavy sites and technical blogs. Read more about Roq with Asciidoc…​ More Sites Coming Soon! The momentum doesn’t stop here. Several other sites are currently in the pipeline to migrate to Roq, signaling growing confidence in its capabilities and developer experience. With its blazing-fast builds and flexible content handling, Roq is quickly becoming the go-to solution for modern static site generation in the Java ecosystem. Stay tuned for more announcements! ### [More diagram than you could have dreamed of.](/posts/more-diagram-than-you-could-have-dreamed-of/) As much as you love writing content in a textual format, you like to produce your diagram as code. But there are so many: PlantUML, Ditaa, Mermaid, BPMN and so on and so forth. Integrating all those formats would be a real pain. Hopefully you don’t have to, Kroki.io has already done it for you. A new plugin has been added to integrate its capability seamlessly to ROQ You can use it through an already deployed server or let the plugin make use of Quarkus dev services to do the job for you. 👉 Full documentation is available here, let’s diagram all the things! ### [🔎 Your users deserve searching capabilities!](/posts/your-users-deserve-searching-capabilities/) So your site is growing larger and larger and so it becomes harder and harder to find anything you wrote more than a few weeks ago. And that is frustrating. Words vanish, writing remains Yes…​ But if it remains buried deep in a pile of posts, it won’t be of any use to any one. It seems you need to add a search engine to your site. But…​ you choose static generation for a reason, right ? Economy of resources, matters to you. And so you don’t want to add a full-blown search engine like ElasticSearch or Solr. And guess what ? We couldn’t agree more with you 🤩. We think you are perfectly right, and that people should listen to you more. At least that’s what we do. 👂 So we did a bit of research and found out exactly what you need : Lunr.js, it’s a small, full-text search engine written in JavaScript. It runs in the browser based on a static generated json index and don’t need any other third party services ✨. Tadddaaah! We wrote a Lunr.js plugin for Roq.   👉 Full documentation is available here, don’t wait any longer, go check it out. ### [No pain updates with Roq](/posts/no-pain-updates-with-roq/) One of the most overlooked aspects when choosing a Static Site Generator (SSG) is how easy it is to keep your project up to date. Many developers have struggled with complex upgrade processes, dependency conflicts, and breaking changes when using traditional SSGs like Jekyll or Hugo. With Roq, upgrading is refreshingly simple. Updating Roq: A One-Command Upgrade Roq is built on Quarkus, which provides a streamlined upgrade process via the Quarkus CLI. To update Roq to the latest version, all you need to do is run: quarkus update ### [Comparing Roq with Hugo, Jekyll, and JBake: A Feature Breakdown](/posts/comparing-roq-with-hugo-jekyll-and-jbake-a-feature-breakdown/) Here’s a feature comparison with some popular SSGs to highlight how Roq stacks up. Feature Roq Hugo Jekyll JBake Performance Fast, and leveraging Quarkus dev-mode Extremely fast (written in Go) Slower due to Ruby and plugins Slower, runs on Java with Freemarker/Groovy templates Templating Qute (simple & readable) Go templates (powerful but complex) Liquid (easy but limited) Freemarker, Groovy, or Thymeleaf Extensibility Leverages Quarkus extensions Limited plugin system Large plugin ecosystem Limited, Java-based plugins Setup Requires JDK (for now) Single binary install Requires Ruby & Bundler Requires Java & Gradle/Maven Dynamic Features Can integrate with Quarkus for hybrid use Mostly static, some JS workarounds Plugins enable some dynamic behavior Fully static Community Growing, part of Quarkus ecosystem Large, well-established Large, long history Niche, less active Learning Curve Beginner friendly, easier for Java developers Can be difficult due to Go templates Complex to setup and update Moderate, depends on template engine A Quick Note About Roq Roq is highly extensible through plugins, which are built as Quarkus extensions (dependencies). Key features like SEO, Search and Sitemap are already available, with more features in the works, including: Image processing (Issue #42) Theme catalog to help get started (Issue #270). While it’s not difficult to convert themes from other SSGs to Roq, I’m working on an AI-based theme converter (Issue #365) to make this process even easier 😁. Considering its young age, Roq is still very complete! Conclusion Jekyll is widely used but comes with the complexity of setting up Ruby environments, which often causes headaches. It has a strong plugin system and is easy to get started with, but performance can be an issue for large sites. JBake was the only Java-based SSG before Roq, but it feels outdated compared to modern alternatives. It lacks the flexibility and performance optimizations of newer SSGs like Hugo and Roq. Roq aims to offer a modern, Java-friendly alternative that brings the ease of Jekyll, the speed of Hugo (to some extent), and the flexibility of Quarkus. ### [Roq n Roll Your Tests 🎶](/posts/roq-n-roll-your-tests/) Hello folks, I'm excited to share something very cool! I've developed a way to: Test the full generation of your website. Use RestAssured to test the generated site (thanks to an already started static server). Step 1: Add the Dependency First, include the quarkus-roq-testing test dependency in your pom.xml. Step 2: Basic Test Example Once you've added the dependency, you can easily ensure all pages are generated without errors: @QuarkusTest @RoqAndRoll public class RoqSiteTest { @Test public void testGen() { // All pages will be generated/validated during test setup } } That's it! This basic test already verifies that your site generation is error-free. Step 3: Test the Generated Content To go even further, you can test the actual content of your generated site. The RestAssured port will automatically use the Roq static server. Here's how: @QuarkusTest @RoqAndRoll public class RoqSiteTest { @Test public void testIndex() { RestAssured.when().get("/") .then() .statusCode(200) .body(containsString( "Roq is a static site generator that makes it easy to build websites and blogs" )); } } Why I Love It ❤️ With just a few annotations and a bit of setup, you can effortlessly test both the generation process and the content of your site. It's powerful, elegant, and super simple to use. Give it a try and let me know how it works for you. Happy testing! 🚀 ### [Easily Generate a `sitemap.xml` for Your Site with Roq](/posts/easily-generate-a-sitemap-xml-for-your-site-with-roq/) Creating a sitemap.xml for your site has never been easier! With the Sitemap plugin, you can automatically generate a well-structured sitemap for search engines to crawl your pages efficiently. Installation To get started, install the plugin by running the following command: roq add plugin:sitemap Setting Up the Sitemap Next, create a new sitemap file in the content/sitemap.xml: <!-- Include your sitemap template --> {#include fm/sitemap.xml} And that's it! Your sitemap is now ready. Excluding Pages from the Sitemap If there are pages you don't want included in the sitemap, simply set the sitemap property to false in the FM of those pages. For example: --- title: "Hidden Page" sitemap: false --- Accessing Your Sitemap Once your site is up and running, you can view your sitemap by navigating to: http://localhost:8080/sitemap.xml Congratulations! You’ve successfully set up a sitemap.xml for your site. ### [Static attached files for posts and pages](/posts/static-attached-files-for-posts-and-pages/) This Christmas, I’m Roq-ing a cool new feature (inspired by Hugo 😅): it is possible to attach static files to posts and pages. They will be served relative to the page. 🎁🤩 I love it because it allows to put all the content related to one page or post in the same place. Bonus, images are displayed on previews since they are relative to the template. For example here is a sample pdf: link. Fun fact: @parisjug is already using this feature on their site (which is on Hugo 🤪)! The doc for this feature is here. ### [Already some happy users 🧑‍💻](/posts/already-some-happy-users/) Roq is a rookie and still needs to prove itself, but already, there are good signs ☀️ The very first to give Roq a shot was David—a talented developer from the Quarkus Team! No images on your blog, David? Maybe it’s because you don’t want them stealing the spotlight from your content, right? Don’t worry, I’ll create an Unsplash plugin just for you. It will automatically pick images based on your content, so you don’t have to lift a finger! David's was nice enough to share about switching from Jekyll to Roq (in his first Roq post:): https://word-bits.flurg.com/posts/it-s-alive/ (repo) I live near Marseille and know the owners of MarsJug. I decided to try switching their website to Roq. Their original site consisted of plain HTML pages with raw event details hardcoded, making updates and maintenance a nightmare. It took just a minute to convince them of the benefits of using Roq. https://marsjug.org/ (repo) Jotak, a Red Hat developer working on the NetObserv project, used to lean more towards Vert.x than Quarkus—something we debated quite a bit back in the day. It’s a shame he had to step away from Java development due to project constraints a few years ago. Still, he managed to convince his team (working with Go and React) to switch to Roq 🚀. https://netobserv.io (repo) Will you be the next to share your Roq's journey? Come on 😎 !!! ### [Do you want to publish a blog post series ?](/posts/do-you-want-to-publish-a-blog-post-series/) So you plan to do a series of blog posts about a given subject. This is as simple as adding a series attribute to the front matter of your posts. Step 1: Add the Series plugin in your dependencies file: <dependency> <groupId>io.quarkiverse.roq</groupId> <artifactId>quarkus-roq-plugin-series</artifactId> <version>${quarkus-roq.version}</version> </dependency> Step 2: Edit the layout for your posts, for example when using roq-default theme: templates/layouts/post-series.html --- theme-layout: post --- {#include partials/roq-series /} (1) {#insert /} (2) 1 This will add the series partial before the post content, if it’s declared. 2 This is the post content. And finally, use this layout and add the series attribute in the Front Matter of the posts you want to join. --- layout: series-post title: Assemble you blog post in a series description: Automatically series header for your posts tags: plugin, frontmatter, guide, series author: John Doe series: My series Title (1) --- 1 You should use the exact same title for all documents in the series. It will add the following at the head of your post: A bit like what you see at the very begining of this post. ### [Need a QR Code?](/posts/need-a-qr-code/) Need to add a scannable QR Code to your website? Whether it's for a restaurant menu, event ticket, or any other use case where you want to make your content easily accessible via mobile devices, the Roq QR Code plugin has you covered. Step 1: Add the QRCode plugin in your dependencies file: <dependency> <groupId>io.quarkiverse.roq</groupId> <artifactId>quarkus-roq-plugin-qrcode</artifactId> <version>...</version> </dependency> Step 2: Add the QRCode tag to your template with all the parameters you need: {#qrcode value="https://luigis.com/menu/" alt="Luigi's Menu" foreground="#000066" background="#FFFFFF" width=300 height=300 /} It will render a QR Code like this: ### [Roq with Blogs](/posts/roq-with-blogs/) Hello folks, First let me thanks the Roq contributors, they have been awesome and this has been so fun to create Roq! If you want to get started quickly: Click here to generate your Roq Starter App. or use the Roq CLI: roq create Then cd blog-with-roq roq If you have a bit of time, with this release, I think it's time for me to give you the full story 📖: It all started a while back when I helped my wife create her blog. After reviewing a few options, I decided to use Jekyll, as it was the easiest solution with GitHub Pages. Over time, I grew quite frustrated with the process: It was hard for my wife to install and start using. It was challenging to maintain and keep updated. Using Ruby didn’t feel great. Plugins were often outdated or unmaintained. Then my wife said: My wife: “But why don’t you use your famous Quarkus?” Me: “This is not the right tool to create a blog 😭” I think this was around the time Quarkus 1.0 was being released... ... 😴 Time passes ... 🗓️ Mar 23, 2022: quarkus-quinoa 🗓️ Feb 3, 2023: quarkus-web-bundler 🗓️ Early 2024: Quarkus web guide At this point, I thought back on what my wife had said... maybe it was time to reconsider? But Qute processes things at runtime, so it didn’t seem possible 😤 ... 😴 Time passes ... 🗓️ May 7, 2024: My idea was to generate static pages at runtime… because then all of Quarkus could become static without any changes 😍. 🗓️ May 17, 2024: quarkus-roq (generator part) At this point, I thought we (mostly) had everything in Quarkus to change my answer to my wife 🤓 For those who wonder, "Roq" was chosen because: static = rock, rock + quarkus = roq 🗓️ June 19, 2024: Roq Focus Group And now, thanks to the awesome team 🧑‍💻👩🏻‍💻! 🗓️ October 31, 2024: Roq 1.0 🎉🍾🥂 If you like the idea, support us, give us a star ⭐ or start contributing... ### [Write your blog posts in AsciiDoc](/posts/write-your-blog-posts-in-asciidoc/) Writing content is AsciiDoc format is an absolut no brainer. Roq provides a plugin to handle it transparently for you. To use it, you need to add the `quarkus-roq-plugin-asciidoc' to your project. Details You can do that using several ways : Manually pom.xml <dependency> <groupId>io.quarkiverse.roq</groupId> <artifactId>quarkus-roq-plugin-asciidoc</artifactId> <version>${quarkus-roq.version}</version> </dependency> Using the Roq CLI roq add plugin:asciidoc Using Maven ./mvnw quarkus:add-extension -Dextensions="io.quarkiverse.roq:quarkus-roq-plugin-asciidoc" Using the Gradle ./gradlew addExtension --extensions="io.quarkiverse.roq:quarkus-roq-plugin-asciidoc" Once done, you can start writing your blog posts in AsciiDoc format. ### [RSS Feed of your blog posts](/posts/rss-feed-of-your-blog-posts/) Adding RSS is as easy as adding this tag to your <head> section: {#rss site /} Like this: <link rel="alternate" type="application/rss+xml" title="Hello, world! I'm Roq — a funny little SSG (Static Site Generator) with a Java soul and Quarkus energy — Open Source and Free." href="https://iamroq.dev/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 /} The Atom Syndication Format is an XML language used for web feeds. A web feed (also called ‘news feed’ or ‘RSS feed’) is a data format used for providing users with frequently updated content. Content distributors syndicate a web feed, thereby allowing users to subscribe a channel to it. A typical scenario of web-feed use might involve the following: a content provider publishes a feed link on its site which end users can register with an aggregator program (also called a feed reader or a newsreader) running on their own machines. <rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"> <channel> <title><![CDATA[ Hello, world! I'm Roq — a funny little SSG (Static Site Generator) with a Java soul and Quarkus energy — Open Source and Free. ]]></title> <description><![CDATA[ An Open Source static site generator (SSG) that makes it fun and easy to build websites and blogs. It's built with Java and Quarkus under the hood. ]]></description> <link>https://iamroq.dev/</link> <atom:link href="https://iamroq.dev/rss.xml" rel="self" type="application/rss+xml"/> <generator>Quarkus Roq</generator> <lastBuildDate>Wed, 06 May 2026 08:00:00 +0000</lastBuildDate> <item> <title><![CDATA[How AI Helped Me Rebuild My Blog and Move from Jekyll to Quarkus Roq]]></title> <link>https://iamroq.dev/posts/how-ai-helped-me-rebuild-my-blog-and-move-from-jekyll-to-quarkus-roq/</link> <guid isPermaLink="false">https://iamroq.dev/posts/how-ai-helped-me-rebuild-my-blog-and-move-from-jekyll-to-quarkus-roq/</guid> <pubDate>Wed, 06 May 2026 08:00:00 +0000</pubDate> <description><![CDATA[A comprehensive journey of rebuilding a personal blog with the help of AI, moving from Jekyll to Quarkus Roq, exploring GitHub Issues Driven Development, and discovering how modern AI tools can transform the way we build and maintain websites.]]></description> <content:encoded><![CDATA[<p>A comprehensive journey of rebuilding a personal blog with the help of AI, moving from Jekyll to Quarkus Roq, exploring GitHub Issues Driven Development, and discovering how modern AI tools can transform the way we build and maintain websites.</p><div style="margin-top: 50px; font-style: italic;"><strong><a href="https://iamroq.dev/posts/how-ai-helped-me-rebuild-my-blog-and-move-from-jekyll-to-quarkus-roq/">Keep reading</a>.</strong></div><br /> <br />]]></content:encoded> </item> <item> <title><![CDATA[GFM Alert Blocks: Styled Callouts in Your Markdown]]></title> <link>https://iamroq.dev/posts/gfm-alert-blocks-styled-callouts-in-your-markdown/</link> <guid isPermaLink="false">https://iamroq.dev/posts/gfm-alert-blocks-styled-callouts-in-your-markdown/</guid> <pubDate>Mon, 04 May 2026 09:00:00 +0000</pubDate> <description><![CDATA[Roq supports GitHub Flavored Markdown alert blocks with icons and themed colors. Learn how to use NOTE, TIP, IMPORTANT, WARNING, and CAUTION blocks, and how to add custom alert types.]]></description> <content:encoded><![CDATA[<p>Roq supports GitHub Flavored Markdown alert blocks with icons and themed colors. Learn how to use NOTE, TIP, IMPORTANT, WARNING, and CAUTION blocks, and how to add custom alert types.</p><div style="margin-top: 50px; font-style: italic;"><strong><a href="https://iamroq.dev/posts/gfm-alert-blocks-styled-callouts-in-your-markdown/">Keep reading</a>.</strong></div><br /> <br />]]></content:encoded> </item> <item> <title><![CDATA[Set It in Roq: The Editor that changes the game!]]></title> <link>https://iamroq.dev/posts/set-it-in-roq-the-editor-that-changes-the-game/</link> <guid isPermaLink="false">https://iamroq.dev/posts/set-it-in-roq-the-editor-that-changes-the-game/</guid> <pubDate>Mon, 04 May 2026 08:00:00 +0000</pubDate> <description><![CDATA[Roq introduces a TipTap-powered editor with Markdown support, transforming it from a static site generator into a lightweight, developer-friendly CMS. Create, edit, and preview content seamlessly within the Quarkus dev experience.]]></description> <content:encoded><![CDATA[<p>Roq introduces a TipTap-powered editor with Markdown support, transforming it from a static site generator into a lightweight, developer-friendly CMS. Create, edit, and preview content seamlessly within the Quarkus dev experience.</p><div style="margin-top: 50px; font-style: italic;"><strong><a href="https://iamroq.dev/posts/set-it-in-roq-the-editor-that-changes-the-game/">Keep reading</a>.</strong></div><br /> <br />]]></content:encoded> </item> <item> <title><![CDATA[Roq 2.1 is here!]]></title> <link>https://iamroq.dev/posts/roq-2-1-is-here/</link> <guid isPermaLink="false">https://iamroq.dev/posts/roq-2-1-is-here/</guid> <pubDate>Fri, 01 May 2026 08:00:00 +0000</pubDate> <description><![CDATA[Roq 2.1 brings a standalone CLI, LLMs.txt generation, dynamic pages from data, custom error pages, and much more. This post kicks off a series covering all the new features.]]></description> <content:encoded><![CDATA[<p>Roq 2.1 brings a standalone CLI, LLMs.txt generation, dynamic pages from data, custom error pages, and much more. This post kicks off a series covering all the new features.</p><div style="margin-top: 50px; font-style: italic;"><strong><a href="https://iamroq.dev/posts/roq-2-1-is-here/">Keep reading</a>.</strong></div><br /> <br />]]></content:encoded> </item> <item> <title><![CDATA[Roq 2.0 and Java Advent Calendar article]]></title> <link>https://iamroq.dev/posts/roq-2-0-and-java-advent-calendar-article/</link> <guid isPermaLink="false">https://iamroq.dev/posts/roq-2-0-and-java-advent-calendar-article/</guid> <pubDate>Tue, 09 Dec 2025 00:00:00 +0000</pubDate> <description><![CDATA[An introduction to Roq 2.0, a Quarkus-inspired approach to static site generation in Java. Learn about its new foundation, plugin support, and live-reload feature through a practical tutorial.]]></description> <content:encoded><![CDATA[<p>An introduction to Roq 2.0, a Quarkus-inspired approach to static site generation in Java. Learn about its new foundation, plugin support, and live-reload feature through a practical tutorial.</p><div style="margin-top: 50px; font-style: italic;"><strong><a href="https://iamroq.dev/posts/roq-2-0-and-java-advent-calendar-article/">Keep reading</a>.</strong></div><br /> <br />]]></content:encoded> </item> <item> <title><![CDATA[Major site migrations to Roq]]></title> <link>https://iamroq.dev/posts/major-site-migrations-to-roq/</link> <guid isPermaLink="false">https://iamroq.dev/posts/major-site-migrations-to-roq/</guid> <pubDate>Tue, 26 Aug 2025 00:00:00 +0000</pubDate> <description><![CDATA[✨ Two prominent websites have just migrated to Roq—any guesses who they might be?]]></description> <content:encoded><![CDATA[<p>✨ Two prominent websites have just migrated to Roq—any guesses who they might be?</p><div style="margin-top: 50px; font-style: italic;"><strong><a href="https://iamroq.dev/posts/major-site-migrations-to-roq/">Keep reading</a>.</strong></div><br /> <br />]]></content:encoded> </item> <item> <title><![CDATA[More diagram than you could have dreamed of.]]></title> <link>https://iamroq.dev/posts/more-diagram-than-you-could-have-dreamed-of/</link> <guid isPermaLink="false">https://iamroq.dev/posts/more-diagram-than-you-could-have-dreamed-of/</guid> <pubDate>Wed, 11 Jun 2025 00:00:00 +0000</pubDate> <description><![CDATA[Leveraging Kroki.io to generate diagram from text]]></description> <content:encoded><![CDATA[<p>Leveraging Kroki.io to generate diagram from text</p><div style="margin-top: 50px; font-style: italic;"><strong><a href="https://iamroq.dev/posts/more-diagram-than-you-could-have-dreamed-of/">Keep reading</a>.</strong></div><br /> <br />]]></content:encoded> </item> <item> <title><![CDATA[🔎 Your users deserve searching capabilities!]]></title> <link>https://iamroq.dev/posts/your-users-deserve-searching-capabilities/</link> <guid isPermaLink="false">https://iamroq.dev/posts/your-users-deserve-searching-capabilities/</guid> <pubDate>Fri, 04 Apr 2025 00:00:00 +0000</pubDate> <description><![CDATA[No third party service needed 🚀]]></description> <content:encoded><![CDATA[<p>No third party service needed 🚀</p><div style="margin-top: 50px; font-style: italic;"><strong><a href="https://iamroq.dev/posts/your-users-deserve-searching-capabilities/">Keep reading</a>.</strong></div><br /> <br />]]></content:encoded> </item> <item> <title><![CDATA[No pain updates with Roq]]></title> <link>https://iamroq.dev/posts/no-pain-updates-with-roq/</link> <guid isPermaLink="false">https://iamroq.dev/posts/no-pain-updates-with-roq/</guid> <pubDate>Mon, 24 Mar 2025 00:00:00 +0000</pubDate> <description><![CDATA[One of the most overlooked aspects when choosing a Static Site Generator (SSG) is how easy it is to keep your project up to date. Many developers have struggled with complex upgrade processes, dependency conflicts, and breaking changes when using traditional SSGs like Jekyll or Hugo.]]></description> <content:encoded><![CDATA[<p>One of the most overlooked aspects when choosing a Static Site Generator (SSG) is how easy it is to keep your project up to date. Many developers have struggled with complex upgrade processes, dependency conflicts, and breaking changes when using traditional SSGs like Jekyll or Hugo.</p><div style="margin-top: 50px; font-style: italic;"><strong><a href="https://iamroq.dev/posts/no-pain-updates-with-roq/">Keep reading</a>.</strong></div><br /> <br />]]></content:encoded> </item> <item> <title><![CDATA[Comparing Roq with Hugo, Jekyll, and JBake: A Feature Breakdown]]></title> <link>https://iamroq.dev/posts/comparing-roq-with-hugo-jekyll-and-jbake-a-feature-breakdown/</link> <guid isPermaLink="false">https://iamroq.dev/posts/comparing-roq-with-hugo-jekyll-and-jbake-a-feature-breakdown/</guid> <pubDate>Thu, 27 Feb 2025 00:00:00 +0000</pubDate> <description><![CDATA[]]></description> <content:encoded><![CDATA[<p></p><div style="margin-top: 50px; font-style: italic;"><strong><a href="https://iamroq.dev/posts/comparing-roq-with-hugo-jekyll-and-jbake-a-feature-breakdown/">Keep reading</a>.</strong></div><br /> <br />]]></content:encoded> </item> <item> <title><![CDATA[Roq n Roll Your Tests 🎶]]></title> <link>https://iamroq.dev/posts/roq-n-roll-your-tests/</link> <guid isPermaLink="false">https://iamroq.dev/posts/roq-n-roll-your-tests/</guid> <pubDate>Tue, 28 Jan 2025 00:00:00 +0000</pubDate> <description><![CDATA[Testing the actual Roq generation has never been this cool! 🎸]]></description> <content:encoded><![CDATA[<p>Testing the actual Roq generation has never been this cool! 🎸</p><div style="margin-top: 50px; font-style: italic;"><strong><a href="https://iamroq.dev/posts/roq-n-roll-your-tests/">Keep reading</a>.</strong></div><br /> <br />]]></content:encoded> </item> <item> <title><![CDATA[Easily Generate a `sitemap.xml` for Your Site with Roq]]></title> <link>https://iamroq.dev/posts/easily-generate-a-sitemap-xml-for-your-site-with-roq/</link> <guid isPermaLink="false">https://iamroq.dev/posts/easily-generate-a-sitemap-xml-for-your-site-with-roq/</guid> <pubDate>Wed, 08 Jan 2025 00:00:00 +0000</pubDate> <description><![CDATA[Learn how to quickly set up and customize a sitemap.xml for your site using the Roq plugin.]]></description> <content:encoded><![CDATA[<p>Learn how to quickly set up and customize a sitemap.xml for your site using the Roq plugin.</p><div style="margin-top: 50px; font-style: italic;"><strong><a href="https://iamroq.dev/posts/easily-generate-a-sitemap-xml-for-your-site-with-roq/">Keep reading</a>.</strong></div><br /> <br />]]></content:encoded> </item> <item> <title><![CDATA[Static attached files for posts and pages]]></title> <link>https://iamroq.dev/posts/static-attached-files-for-posts-and-pages/</link> <guid isPermaLink="false">https://iamroq.dev/posts/static-attached-files-for-posts-and-pages/</guid> <pubDate>Thu, 26 Dec 2024 00:00:00 +0000</pubDate> <description><![CDATA[This Christmas, I’m Roq-ing a cool new feature (inspired by Hugo 😅): it is possible to attach static files to posts and pages. They will be served relative to the page. 🎁🤩 ]]></description> <content:encoded><![CDATA[<p>This Christmas, I’m Roq-ing a cool new feature (inspired by Hugo 😅): it is possible to attach static files to posts and pages. They will be served relative to the page. 🎁🤩 </p><div style="margin-top: 50px; font-style: italic;"><strong><a href="https://iamroq.dev/posts/static-attached-files-for-posts-and-pages/">Keep reading</a>.</strong></div><br /> <br />]]></content:encoded> </item> <item> <title><![CDATA[Already some happy users 🧑‍💻]]></title> <link>https://iamroq.dev/posts/already-some-happy-users/</link> <guid isPermaLink="false">https://iamroq.dev/posts/already-some-happy-users/</guid> <pubDate>Tue, 10 Dec 2024 00:00:00 +0000</pubDate> <description><![CDATA[This is a good start, we already have a few happy users!]]></description> <content:encoded><![CDATA[<p>This is a good start, we already have a few happy users!</p><div style="margin-top: 50px; font-style: italic;"><strong><a href="https://iamroq.dev/posts/already-some-happy-users/">Keep reading</a>.</strong></div><br /> <br />]]></content:encoded> </item> <item> <title><![CDATA[Do you want to publish a blog post series ?]]></title> <link>https://iamroq.dev/posts/do-you-want-to-publish-a-blog-post-series/</link> <guid isPermaLink="false">https://iamroq.dev/posts/do-you-want-to-publish-a-blog-post-series/</guid> <pubDate>Fri, 06 Dec 2024 07:00:00 +0000</pubDate> <description><![CDATA[Make your blog posts part of a series.]]></description> <content:encoded><![CDATA[<p>Make your blog posts part of a series.</p><div style="margin-top: 50px; font-style: italic;"><strong><a href="https://iamroq.dev/posts/do-you-want-to-publish-a-blog-post-series/">Keep reading</a>.</strong></div><br /> <br />]]></content:encoded> </item> <item> <title><![CDATA[Need a QR Code?]]></title> <link>https://iamroq.dev/posts/need-a-qr-code/</link> <guid isPermaLink="false">https://iamroq.dev/posts/need-a-qr-code/</guid> <pubDate>Thu, 14 Nov 2024 12:00:00 +0000</pubDate> <description><![CDATA[Add a QR Code to your Roq website.]]></description> <content:encoded><![CDATA[<p>Add a QR Code to your Roq website.</p><div style="margin-top: 50px; font-style: italic;"><strong><a href="https://iamroq.dev/posts/need-a-qr-code/">Keep reading</a>.</strong></div><br /> <br />]]></content:encoded> </item> <item> <title><![CDATA[Roq with Blogs]]></title> <link>https://iamroq.dev/posts/roq-with-blogs/</link> <guid isPermaLink="false">https://iamroq.dev/posts/roq-with-blogs/</guid> <pubDate>Thu, 31 Oct 2024 00:00:00 +0000</pubDate> <description><![CDATA[🚀 Roq 1.0 is ON! It is time to give it a shot and give us feedback 🚀]]></description> <content:encoded><![CDATA[<p>🚀 Roq 1.0 is ON! It is time to give it a shot and give us feedback 🚀</p><div style="margin-top: 50px; font-style: italic;"><strong><a href="https://iamroq.dev/posts/roq-with-blogs/">Keep reading</a>.</strong></div><br /> <br />]]></content:encoded> </item> <item> <title><![CDATA[Write your blog posts in AsciiDoc]]></title> <link>https://iamroq.dev/posts/write-your-blog-posts-in-asciidoc/</link> <guid isPermaLink="false">https://iamroq.dev/posts/write-your-blog-posts-in-asciidoc/</guid> <pubDate>Tue, 22 Oct 2024 00:00:00 +0000</pubDate> <description><![CDATA[Automatically generate html from AsciiDoc content]]></description> <content:encoded><![CDATA[<p>Automatically generate html from AsciiDoc content</p><div style="margin-top: 50px; font-style: italic;"><strong><a href="https://iamroq.dev/posts/write-your-blog-posts-in-asciidoc/">Keep reading</a>.</strong></div><br /> <br />]]></content:encoded> </item> <item> <title><![CDATA[RSS Feed of your blog posts]]></title> <link>https://iamroq.dev/posts/rss-feed-of-your-blog-posts/</link> <guid isPermaLink="false">https://iamroq.dev/posts/rss-feed-of-your-blog-posts/</guid> <pubDate>Thu, 10 Oct 2024 00:00:00 +0000</pubDate> <description><![CDATA[Automatically generate an RSS feed of your blog links.]]></description> <content:encoded><![CDATA[<p>Automatically generate an RSS feed of your blog links.</p><div style="margin-top: 50px; font-style: italic;"><strong><a href="https://iamroq.dev/posts/rss-feed-of-your-blog-posts/">Keep reading</a>.</strong></div><br /> <br />]]></content:encoded> </item> <item> <title><![CDATA[The second Roq plugin is for redirecting your page to a better place!]]></title> <link>https://iamroq.dev/posts/the-second-roq-plugin-is-for-redirecting-your-page-to-a-better-place/</link> <guid isPermaLink="false">https://iamroq.dev/posts/the-second-roq-plugin-is-for-redirecting-your-page-to-a-better-place/</guid> <pubDate>Wed, 09 Oct 2024 00:00:00 +0000</pubDate> <description><![CDATA[We introduced a way to declare aliases in FrontMatter. It is now easy create redirections to your blog posts!]]></description> <content:encoded><![CDATA[<p>We introduced a way to declare aliases in FrontMatter. It is now easy create redirections to your blog posts!</p><div style="margin-top: 50px; font-style: italic;"><strong><a href="https://iamroq.dev/posts/the-second-roq-plugin-is-for-redirecting-your-page-to-a-better-place/">Keep reading</a>.</strong></div><br /> <br />]]></content:encoded> </item> <item> <title><![CDATA[The first Roq plugin is for tagging (with pagination)]]></title> <link>https://iamroq.dev/posts/the-first-roq-plugin-is-for-tagging-with-pagination/</link> <guid isPermaLink="false">https://iamroq.dev/posts/the-first-roq-plugin-is-for-tagging-with-pagination/</guid> <pubDate>Tue, 08 Oct 2024 00:00:00 +0000</pubDate> <description><![CDATA[We introduced the first Roq plugin, it is for collection tagging & with pagination support!]]></description> <content:encoded><![CDATA[<p>We introduced the first Roq plugin, it is for collection tagging & with pagination support!</p><div style="margin-top: 50px; font-style: italic;"><strong><a href="https://iamroq.dev/posts/the-first-roq-plugin-is-for-tagging-with-pagination/">Keep reading</a>.</strong></div><br /> <br />]]></content:encoded> </item> <item> <title><![CDATA[Out of the box awesome SEO]]></title> <link>https://iamroq.dev/posts/out-of-the-box-awesome-seo/</link> <guid isPermaLink="false">https://iamroq.dev/posts/out-of-the-box-awesome-seo/</guid> <pubDate>Mon, 23 Sep 2024 12:00:00 +0000</pubDate> <description><![CDATA[Learn how to implement SEO in Roq in a blink of an eye.]]></description> <content:encoded><![CDATA[<p>Learn how to implement SEO in Roq in a blink of an eye.</p><div style="margin-top: 50px; font-style: italic;"><strong><a href="https://iamroq.dev/posts/out-of-the-box-awesome-seo/">Keep reading</a>.</strong></div><br /> <br />]]></content:encoded> </item> <item> <title><![CDATA[Mastering Pagination in Roq]]></title> <link>https://iamroq.dev/posts/mastering-pagination-in-roq/</link> <guid isPermaLink="false">https://iamroq.dev/posts/mastering-pagination-in-roq/</guid> <pubDate>Fri, 20 Sep 2024 12:00:00 +0000</pubDate> <description><![CDATA[Learn how to implement pagination in Roq to enhance your content navigation. This article walks through the process of adding pagination, configuring page size, and customizing links.]]></description> <content:encoded><![CDATA[<p>Learn how to implement pagination in Roq to enhance your content navigation. This article walks through the process of adding pagination, configuring page size, and customizing links.</p><div style="margin-top: 50px; font-style: italic;"><strong><a href="https://iamroq.dev/posts/mastering-pagination-in-roq/">Keep reading</a>.</strong></div><br /> <br />]]></content:encoded> </item> <item> <title><![CDATA[How to add syntax highlighting to your Roq site]]></title> <link>https://iamroq.dev/posts/how-to-add-syntax-highlighting-to-your-roq-site/</link> <guid isPermaLink="false">https://iamroq.dev/posts/how-to-add-syntax-highlighting-to-your-roq-site/</guid> <pubDate>Fri, 20 Sep 2024 09:00:00 +0000</pubDate> <description><![CDATA[Learn how to integrate syntax highlighting into your Roq site using Highlight.js and the Quarkus web-bundler extension. This guide walks you through the simple steps to add it via the pom.xml, JavaScript, and SCSS files.]]></description> <content:encoded><![CDATA[<p>Learn how to integrate syntax highlighting into your Roq site using Highlight.js and the Quarkus web-bundler extension. This guide walks you through the simple steps to add it via the pom.xml, JavaScript, and SCSS files.</p><div style="margin-top: 50px; font-style: italic;"><strong><a href="https://iamroq.dev/posts/how-to-add-syntax-highlighting-to-your-roq-site/">Keep reading</a>.</strong></div><br /> <br />]]></content:encoded> </item> <item> <title><![CDATA[Easily manage Drafts and Future articles in Roq]]></title> <link>https://iamroq.dev/posts/easily-manage-drafts-and-future-articles-in-roq/</link> <guid isPermaLink="false">https://iamroq.dev/posts/easily-manage-drafts-and-future-articles-in-roq/</guid> <pubDate>Thu, 19 Sep 2024 08:45:00 +0000</pubDate> <description><![CDATA[Roq SSG introduces a new feature that allows you to hide or show draft and future articles using simple Quarkus configurations. This update gives developers greater control over which content is visible, improving content management and workflow.]]></description> <content:encoded><![CDATA[<p>Roq SSG introduces a new feature that allows you to hide or show draft and future articles using simple Quarkus configurations. This update gives developers greater control over which content is visible, improving content management and workflow.</p><div style="margin-top: 50px; font-style: italic;"><strong><a href="https://iamroq.dev/posts/easily-manage-drafts-and-future-articles-in-roq/">Keep reading</a>.</strong></div><br /> <br />]]></content:encoded> </item> <item> <title><![CDATA[Effortless URL Handling in Roq with Qute super-power]]></title> <link>https://iamroq.dev/posts/effortless-url-handling-in-roq-with-qute-super-power/</link> <guid isPermaLink="false">https://iamroq.dev/posts/effortless-url-handling-in-roq-with-qute-super-power/</guid> <pubDate>Mon, 16 Sep 2024 11:32:20 +0000</pubDate> <description><![CDATA[Effortlessly manage both relative and absolute URLs with our enhanced Qute-powered feature. Utilizing the RoqUrl class, you can easily join and resolve paths, ensuring clean and predictable URLs. This update simplifies URL handling, making your code more efficient and your content easier to navigate and share.]]></description> <content:encoded><![CDATA[<p>Effortlessly manage both relative and absolute URLs with our enhanced Qute-powered feature. Utilizing the RoqUrl class, you can easily join and resolve paths, ensuring clean and predictable URLs. This update simplifies URL handling, making your code more efficient and your content easier to navigate and share.</p><div style="margin-top: 50px; font-style: italic;"><strong><a href="https://iamroq.dev/posts/effortless-url-handling-in-roq-with-qute-super-power/">Keep reading</a>.</strong></div><br /> <br />]]></content:encoded> </item> <item> <title><![CDATA[Welcome to Roq!]]></title> <link>https://iamroq.dev/posts/welcome-to-roq/</link> <guid isPermaLink="false">https://iamroq.dev/posts/welcome-to-roq/</guid> <pubDate>Thu, 29 Aug 2024 11:32:20 +0000</pubDate> <description><![CDATA[This is the first article ever made with Quarkus Roq]]></description> <content:encoded><![CDATA[<p>This is the first article ever made with Quarkus Roq</p><div style="margin-top: 50px; font-style: italic;"><strong><a href="https://iamroq.dev/posts/welcome-to-roq/">Keep reading</a>.</strong></div><br /> <br />]]></content:encoded> </item> </channel> </rss> ### [The second Roq plugin is for redirecting your page to a better place!](/posts/the-second-roq-plugin-is-for-redirecting-your-page-to-a-better-place/) In the last post, we saw how easy it is to use Quarkus for static site generator (@ia3andy's was right!). I am excited to share that we now have a new plugin that allows you to set up redirects for your blog posts! For this post, I've created three aliases. aliases-very-cool aliases-4-ever aliases-again If you click on at least one alias, you will be redirected here again! And how did I work my magic to set this up? Step 1: Add the aliases plugin in your dependencies file: <dependency> <groupId>io.quarkiverse.roq</groupId> <artifactId>quarkus-roq-plugin-aliases</artifactId> <version>...</version> </dependency> Step 2: Add a new entry aliases: [name-of-aliases-here] in your FM data. In this blog post I used the following FM: ... aliases: [aliases-very-cool, aliases-4-ever, aliases-again] ... For more info check out the doc. ### [The first Roq plugin is for tagging (with pagination)](/posts/the-first-roq-plugin-is-for-tagging-with-pagination/) My mind is getting blown by how much Quarkus was made for Static Site Generation. I just implemented a new plugin to generate tag pages and that was soooo easy. To use it: <dependency> <groupId>io.quarkiverse.roq</groupId> <artifactId>quarkus-roq-plugin-tagging</artifactId> <version>...</version> </dependency> and adding a new layouts/tag.html page or any layout with tagging: [name of collection] as FM data. For more info check out the doc. ### [Out of the box awesome SEO](/posts/out-of-the-box-awesome-seo/) Adding SEO is as easy as adding this tag to your <head> section: {#seo page site /} It will automatically use the Frontmatter data to fill the tags. Read the Roq documentation for more... Like this: <!-- SEO TITLE --> <title>Out of the box awesome SEO - Hello, world! I'm Roq — a funny little SSG (Static Site Generator) with a Java soul and Quarkus energy — Open Source and Free.</title> <meta property="og:title" content="Out of the box awesome SEO" /> <meta name="twitter:title" content="Out of the box awesome SEO"> <meta property="og:site_name" content="Hello, world! I'm Roq — a funny little SSG (Static Site Generator) with a Java soul and Quarkus energy — Open Source and Free."> <!-- SEO DESCRIPTION --> <meta name="description" content="Learn how to implement SEO in Roq in a blink of an eye."> <meta property="og:description" content="Learn how to implement SEO in Roq in a blink of an eye." /> <meta name="twitter:description" content="Learn how to implement SEO in Roq in a blink of an eye."> <!-- SEO AUTHOR --> <meta property="article:author" content="ia3andy" /> <meta name="author" content="ia3andy" /> <!-- SEO URL --> <link rel="canonical" href="https://iamroq.dev/posts/out-of-the-box-awesome-seo/" /> <meta property="og:url" content="https://iamroq.dev/posts/out-of-the-box-awesome-seo/" /> <meta name="twitter:url" content="https://iamroq.dev/posts/out-of-the-box-awesome-seo/"> <!-- SEO TYPE --> <meta property="og:type" content="article" /> <meta property="article:published_time" content="2024-09-23T12:00Z[Etc/UTC]" /> <!-- SEO IMAGE --> <meta name="twitter:card" content="summary_large_image" /> <meta name="twitter:image:src" content="https://images.unsplash.com/photo-1562577309-2592ab84b1bc?q=80&w=3474&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" /> <meta property="og:image" content="https://images.unsplash.com/photo-1562577309-2592ab84b1bc?q=80&w=3474&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" /> <!-- SEO COLLECTION PAGES --> <link rel="prev" href="https://iamroq.dev/posts/the-first-roq-plugin-is-for-tagging-with-pagination/" /> <link rel="next" href="https://iamroq.dev/posts/mastering-pagination-in-roq/" /> <!-- SEO LOCALE --> <meta property="og:locale" content="en" /> <!-- SEO GENERATOR --> <meta name="generator" content="Quarkus Roq v999-SNAPSHOT" /> ### [Mastering Pagination in Roq](/posts/mastering-pagination-in-roq/) 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: Basic Pagination Setup First, include the following in your frontmatter on the page which will iterate on the paginated collection: layout: main paginate: posts Next, in your template, loop through the paginated posts using: {#for post in site.collections.posts.paginated(page.paginator)} <article class="post">...</article> {/for} Step 2: Adding Pagination Controls To add pagination controls, add something like this to partials/pagination.html and include it in your page {#include partials/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} 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. ### [How to add syntax highlighting to your Roq site](/posts/how-to-add-syntax-highlighting-to-your-roq-site/) Adding syntax highlighting to your Roq project has never been easier. Here’s a quick guide to help you integrate Highlight.js in your project with the help of the Quarkus web-bundler extension. Step 1: Add Highlight.js Dependency Next, add Highlight.js to your pom.xml like this: <dependency> <groupId>org.mvnpm</groupId> <artifactId>highlight.js</artifactId> <version>11.10.0</version> <scope>provided</scope> </dependency> This will make the Highlight.js library available to your project. Step 2: Initialize Highlight.js Roq is pre-configured with the Quarkus Web-Bundler to automatically bundle you Javascripts and Styles located in src/main/resource/web/app. The Roq default theme includes the {#bundle /} tag, if you are using your own templates, make sure it is present. Now, let’s configure Highlight.js. In your src/main/resources/web/app/main.js, import the library and activate it: import hljs from 'highlight.js'; import 'highlight.js/scss/default.scss'; hljs.highlightAll(); And that's it! Now your code blocks will be beautifully highlighted, adding a more polished and professional look to your content. This process is quick and effective, making it easy to provide clear, readable syntax highlighting for your users. Happy coding! ### [Easily manage Drafts and Future articles in Roq](/posts/easily-manage-drafts-and-future-articles-in-roq/) Roq just made content management easier with a cool new feature that lets you control drafts and future articles directly in your configuration. No more messing around with hard-to-track content—now you can manage everything through the Quarkus config: roq -Dsite.draft -Dsite.future This is using frontmatter data in articles and pages draft: true and date: 2024-09-19 10:45:00 +0200 to take the decision. By default, both options are set to false, meaning that drafts and future pages will stay hidden until you’re ready to reveal them. All you need to do is update these configs when you're ready to publish. This simple feature adds flexibility and control, making your publishing process more streamlined. Happy content managing! ### [Effortless URL Handling in Roq with Qute super-power](/posts/effortless-url-handling-in-roq-with-qute-super-power/) Managing URLs is now very easy! With our updated Qute-powered feature, you can now manage relative and absolute URLs with more flexibility, thanks to new methods for joining paths and handling absolute URLs. Let’s explore some examples. How to Use It: Relative URL Example (toString prints the relative url): <a class="post-thumbnail" href="{post.url}"> </a> Absolute URL Example: <a class="post-thumbnail" href="{post.url.absolute}"> </a> ** Smart URL:** <meta name="twitter:image:src" content="{page.image.absolute}" > There is a method in Page to retrieve the image url as a RoqUrl from the configured site images path. It is smart so that if the page image is external, it won't be affected. Under the Hood: The Power of RoqUrl At the core of this feature is the RoqUrl class that you can leverage from Qute, which makes joining and resolving URLs super easy. With this structure, joining paths is as simple as calling resolve(). This ensures your URLs are clean, predictable, and easy to manage—whether they’re relative or absolute. Wrapping Up: With Qute’s URL handling, you can now dynamically create and manage both relative and absolute URLs without any hassle. This new implementation will help keep your code clean while making it easier to navigate, link, and share content across your site. ### [Welcome to Roq!](/posts/welcome-to-roq/) Hello folks, A bunch of Quarkus contributors started this new initiative to allow Static Site Generation with Quarkus (similar to Hugo, Jekyll, Lume, ...). Quarkus already provides most of the pieces to create great web applications (https://quarkus.io/guides/web). And Roq adds the missing pieces: Roq Generator: allows to generate a static website out of any Quarkus application (it starts the app, fetch all the configured pages and assets, generate a static website and stop). Roq Data: allows to create json or yaml data file and consume them from your templates. It is also possible to map them to beans to get type-safe validation in bonus! Roq FrontMatter: allow to create pages and collections (posts, ...) using Markdown or Asciidoc with layouting. In fact, your static website content. What's missing? we now need to incrementally add the toolkit to ease the process of creating static content through Quarkus: SEO Image processing (quarkus-web-bundler/issues/42) Pagination (quarkus-roq/issues/65) Advanced routing (redirect, ...) To go further: Compat with tools like https://frontmatter.codes/ Compat with IDEs plugins Roq GitHub action Dev-UI integrated headless CMS (to edit md/asciidoc on the fs) With Roq you can develop the content using Quarkus dev-mode, and then generate (on CI) for Github Pages or similar when it's ready. Bonus, everything added will benefit any "non-static" Quarkus app and any static Quarkus app could also go back to being non static. This effort is now tracked using a "Focus Group" (temporary wording) project: https://github.com/orgs/quarkiverse/projects/6 This is a great opportunity to participate in a fun focus group and be involved with the Quarkus community, if anyone is interested in being a part of this, please reach out to me 🚀 There will be small, medium, bigger features to develop with any level of involvement. Participating could just be giving thoughts and discussing things.. Check out the Roq docs for more info on how to get the most out of Roq. File all bugs/feature requests at Roq’s GitHub repo. ## Marketplace ### [Tailwind CSS](/web/tailwind-css/) Add Tailwind CSS support to your Roq project. Write utility-first CSS classes directly in your templates, with automatic purging of unused styles for optimized production builds. Configuration Tailwind configuration is automatic (detecting site content and templates) via the Quarkus Web Bundler. Getting started After installing, create a CSS file that imports Tailwind: @import "tailwindcss"; @plugin "@tailwindcss/typography"; The @tailwindcss/typography plugin provides the prose class for beautifully styled content rendering (used by Roq for markdown and AsciiDoc output). Then use Tailwind classes in your templates: <div class="flex items-center gap-4 p-6 bg-white dark:bg-gray-800 rounded-lg shadow"> <h2 class="text-xl font-bold text-gray-900 dark:text-white">Hello Tailwind</h2> </div> ### [Svelte](/web/svelte/) Add Svelte component support to your Roq project. Build interactive UI components with Svelte's reactive framework and embed them in your static pages. Getting started Create .svelte files in your web/ directory: <!-- web/components/Counter.svelte --> <script> let count = 0; </script> <button on:click={() => count++}> Clicks: {count} </button> Then mount the component in your templates using Web Bundler's script injection. ### [Sass](/web/sass/) Add Sass/SCSS support to your Roq project. Use variables, nesting, mixins, and all Sass features to write maintainable stylesheets. Sass is the default Web Bundler preprocessor. It is included automatically when using the Web Bundler without Tailwind. Getting started Create .scss files in your web/ directory: // web/style.scss $primary: #3b82f6; $radius: 0.5rem; .card { border-radius: $radius; background: white; &-title { color: $primary; font-weight: 600; } } ### [mvnpm](/web/mvnpm/) Add mvnpm support to your Roq project. Import npm packages directly through Maven coordinates, with no Node.js or npm installation required. mvnpm bridges Maven and npm, converting npm packages into Maven artifacts that the Web Bundler can resolve and bundle. Getting started Add npm packages as Maven dependencies in your pom.xml: <dependency> <groupId>org.mvnpm</groupId> <artifactId>htmx.org</artifactId> <version>2.0.4</version> <scope>provided</scope> </dependency> Then import them in your JavaScript: import 'htmx.org'; Browse available packages at mvnpm.org. ### [Tagging](/plugin/tagging/) Generate a dynamic (derived) collection based on a given collection's tags. For example, if multiple posts have tags: guide, a /posts/tag/guide page is generated listing all matching posts. This works for any collection. If you are using a theme that supports it (includes a tagging layout), you should now have tags pages available for all the tags in your posts! You can use theme override to customize the theme tagging layout. To enable tagging without a theme, create a layout template and add tagging: [collection id] in FM. As a result you will have access to a new derived collection named tagCollection: <!-- templates/layouts/tag.html --> --- layout: main tagging: posts --- {#for post in site.collections.get(page.data.tagCollection)} <div>{post.title}</div> {/for} This also supports pagination. Since tagging already specifies the target collection, pagination can be enabled with paginate: true in FM: --- layout: main tagging: posts paginate: true --- {#for post in site.collections.get(page.data.tagCollection).paginated(page.paginator)} <div>{post.title}</div> {/for} Template Extensions Usage Description collection.allTags Returns a list of all tags from the collection, each tag slugified collection.tagsCount Returns a list of all tags slugified (name) with their count (count) in the collection ### [Sitemap](/plugin/sitemap/) Easily create a sitemap.xml for your site. Create a new sitemap file: <!-- content/sitemap.xml --> {#include fm/sitemap.xml} To remove pages from the sitemap, use sitemap: false in the FM data. Browse http://localhost:8080/sitemap.xml to verify. ### [Series](/plugin/series/) Join multiple posts into a series with automatic series headers. Edit the layout for your posts, for example when using the roq-default theme: <!-- templates/layouts/post-series.html --> --- theme-layout: post --- {#include partials/roq-series /} {#insert /} Then use this layout and add the series attribute in the Front Matter of the posts you want to join: --- layout: post-series title: Assemble your blog post in a series description: Automatically series header for your posts tags: plugin, frontmatter, guide, series author: John Doe series: My series Title --- Use the exact same series title for all documents in the series. ### [QR Code](/plugin/qr-code/) Add QR codes to your website. Create a template and add the #qrcode tag to it, then style and size it as you want. By default, the plugin produces HTML output compatible with both HTML and Markdown templates. To use the plugin with AsciiDoc, set the asciidoc attribute to true. {#qrcode value="https://luigis.com/menu/" alt="Luigi's Menu" foreground="#000066" background="#FFFFFF" width=300 height=300 /} {#qrcode value="https://luigis.com/menu/" alt="Luigi's Menu" foreground="#000066" background="#FFFFFF" width=300 height=300 asciidoc=true /} ### [Markdown](/plugin/markdown/) Process .md and .markdown files using CommonMark Java. Markdown plugin is already included in Quarkus Roq extension. No separate installation needed unless you removed it. Every file with .md or .markdown extension will be processed. ### [Lunr Search](/plugin/lunr-search/) Enable search for your site without the need for external, server-side, search services. Setup Add the search index JSON: <!-- content/search-index.json --> {#include fm/search-index.json} Inject the search script in the <head> of your layout. For example with the default theme: <!-- templates/layouts/default.html --> --- theme-layout: default --- {#insert /} {#head} {#search-script /} {/} Inject the search overlay in the <body> and search button in the navigation: <!-- templates/layouts/main.html --> --- theme-layout: main --- {#search-overlay /} {#insert /} {#menu} {#search-button /} {#include partials/roq-default/sidebar-menu menu=cdi:menu.items /} {/} Controlling indexing You can prevent content from being indexed: --- title: I don't want to be indexed search: false --- You can also boost specific pages or layouts in the results: --- title: I want to be first in the result search-boost: 30 --- ### [Diagram](/plugin/diagram/) Diagram-as-code support by leveraging Kroki.io. It delegates image rendering to Kroki either by using a provided server or by popping a dev service. Please take a look at the full Kroki reference documentation. Use it in your content: {#diagram asciidoc=true language="pikchr" alt="Impossible trident" width=500 height=500 diagramOutputFormat="svg"} scale = 1.0 eh = 0.5cm ew = 0.2cm ... {/} You can either use a deployed server or let the dev services provide one for you, but in this case you won't have all languages available. ### [AsciiDoc](/plugin/asciidoc/) Fast Java-based AsciiDoc processor (based on Yupiik asciidoc-java). Provides fast startup but does not support all AsciiDoc options yet. For the full feature set, see AsciiDoc JRuby. Add the .adoc or .asciidoc file extension to pages and they will be processed. Use Qute in AsciiDoc files Qute parsing is disabled by default on AsciiDoc files, to enable it: quarkus.asciidoc.qute=true You can also use the :qute: AsciiDoc header attribute to enable Qute parsing (or not :qute: false) per page. AsciiDoc includes You may use includes from anywhere in the site directory. Make sure the included file is ignored by Roq by prefixing the file or directory with _. include::_includes/attributes.adoc[] Headers AsciiDoc headers are parsed by Roq and used as page data: = Title is used as page title author is available through page.data.author and page.data.author-email revision is available through page.data.revision.number, page.data.revision.date and page.data.revision.remark attribute :description: is used as page description attributes starting with page- will be used as page data (:page-image: becomes image in the data) all other header attributes are also available in page.data.attributes You can also use FrontMatter headers to set the page data like any other page. Roq attributes Name Description | {site-url} | The full site url (e.g. https://my-site.com/blog/) | | {site-path} | The site path (e.g. /blog/) | | {page-url} | The full page url (e.g. https://my-site.com/blog/about/) | | {page-path} | The page path (e.g. /blog/about) | AsciiDoc attributes configuration Attributes can be configured globally: quarkus.asciidoc.attributes.source-highlighter=highlight.js quarkus.asciidoc.attributes.icons=font Or as an include file in the AsciiDoc headers, or as part of the Frontmatter data asciidoc-attributes in a page or layout: --- asciidoc-attributes: notitle: true --- Table of Contents (TOC) To add a Table of Contents, use the page-content-toc attribute in your AsciiDoc header: :page-content-toc: true :page-content-toc-title: Contents :page-content-toc-levels: 2 This works with the default Roq theme and creates a dynamic sidebar TOC that highlights the current section as you scroll. AsciiDoc Data Conversion Convert data containing AsciiDoc into HTML using the asciidocToHtml template extension: --- bar: | == Hello * that's nice * I can use asciidoc in the data --- {page.data.bar.asciidocToHtml} ### [AsciiDoc JRuby](/plugin/asciidoc-jruby/) Full-featured Asciidoctor implementation based on AsciidoctorJ. Offers the complete AsciiDoc feature set including all extensions. Slower startup than the Java variant but covers all advanced AsciiDoc options. Add the .adoc or .asciidoc file extension to pages and they will be processed using Asciidoctor. Use Qute in AsciiDoc files Qute parsing is disabled by default on AsciiDoc files, to enable it: quarkus.asciidoc.qute=true You can also use the :qute: AsciiDoc header attribute to enable Qute parsing (or not :qute: false) per page. AsciiDoc includes You may use includes from anywhere in the site directory. Make sure the included file is ignored by Roq by prefixing the file or directory with _. include::_includes/attributes.adoc[] Headers AsciiDoc headers are parsed by Roq and used as page data: = Title is used as page title author is available through page.data.author and page.data.author-email revision is available through page.data.revision.number, page.data.revision.date and page.data.revision.remark attribute :description: is used as page description attributes starting with page- will be used as page data (:page-image: becomes image in the data) all other header attributes are also available in page.data.attributes You can also use FrontMatter headers to set the page data like any other page. Roq attributes Name Description | {site-url} | The full site url (e.g. https://my-site.com/blog/) | | {site-path} | The site path (e.g. /blog/) | | {page-url} | The full page url (e.g. https://my-site.com/blog/about/) | | {page-path} | The page path (e.g. /blog/about) | AsciiDoc attributes configuration Attributes can be configured globally: quarkus.asciidoc.attributes.source-highlighter=highlight.js quarkus.asciidoc.attributes.icons=font Or as an include file in the AsciiDoc headers, or as part of the Frontmatter data asciidoc-attributes in a page or layout: --- asciidoc-attributes: notitle: true --- Table of Contents (TOC) To add a Table of Contents, use the page-content-toc attribute in your AsciiDoc header: :page-content-toc: true :page-content-toc-title: Contents :page-content-toc-levels: 2 This works with the default Roq theme and creates a dynamic sidebar TOC that highlights the current section as you scroll. AsciiDoc Data Conversion Convert data containing AsciiDoc into HTML using the asciidocToHtml template extension: --- bar: | == Hello * that's nice * I can use asciidoc in the data --- {page.data.foo.asciidocToHtml} ### [Aliases](/plugin/aliases/) Create one or many aliases (redirections) for a page. Add aliases: [your-alias-here, another-alias-here] in the Front Matter to access the page using a customized URL. # content/posts/2024-08-29-welcome-to-roq.md --- layout: post title: "Welcome to Roq!" date: 2024-08-29 13:32:20 +0200 description: This is the first article ever made with Quarkus Roq tags: blogging aliases: [first-roq-article-ever] --- Now, when you access http://localhost:8080/first-roq-article-ever, you will be redirected to the 2024-08-29-welcome-to-roq blog post. You can use link templating in aliases. ### [Resume Theme](/theme/resume-theme/) Build a personal resume or CV with a data-driven YAML configuration. Data Files Add your resume info in the data/ directory: profile.yml firstName: Ada lastName: Lovelace jobTitle: Computational Pioneer city: London country: United Kingdom bio: | Ada Lovelace was a 19th-century mathematician known for her visionary work on Charles Babbage's Analytical Engine. bio.yml - title: Experience items: - header: "1842 - 1843" title: "Mathematician · Self-initiated · London" content: | Translated and annotated Luigi Menabrea's paper on Charles Babbage's Analytical Engine. Added extensive original notes, including the first published algorithm designed for a machine. - title: Education items: - header: "1830 - 1835" title: "Private Tutoring" content: | Studied mathematics and science under Augustus De Morgan and Mary Somerville. The bio data supports hierarchical items with subItems, collapsible/collapsed flags, ruler separators, and logo objects with label, imageUrl, and link. social.yml - name: LinkedIn url: https://www.linkedin.com/in/ada-lovelace/ - name: X url: https://x.com/ada-lovelace Color Themes The theme comes with 6 pre-configured color schemes (Purple, Blue, Emerald, Amber, Rose, Cyan). To use an alternate theme, import it in your web/style.css: /* Available: _theme-blue.css, _theme-emerald.css, _theme-amber.css, _theme-rose.css, _theme-cyan.css */ @import "./_theme-blue.css"; You can also create a custom color scheme by overriding the theme variables. The theme uses Tailwind CSS v4 color palettes. ### [Default Theme](/theme/default-theme/) The default Roq theme for blogs and sites (used on this site). Built with Tailwind CSS, featuring dark mode, responsive design, sidebar navigation, and social media links. The accent color palette can easily be customized with your own colors or any existing Tailwind color palette. Site Data Configure your site through the index page frontmatter (e.g. content/index.html): Key Description name Site name displayed in the sidebar simple-name Short name used in the copyright notice logo Logo image path displayed in the sidebar (falls back to image) description Site tagline shown below the logo theme-color Color used for the browser address bar on mobile (default: #263959) Analytics analytics: ga4: G-XXXXXXXXXX Social Brands Add social media links to your site through the index page frontmatter: social-github: quarkiverse social-twitter: quarkusio social-linkedin: john-doe social-mastodon: https://mastodon.social/@username Available keys: social-twitter, social-github, social-linkedin, social-linkedin-company, social-facebook, social-youtube, social-discord, social-email, social-bluesky, social-mastodon, social-slack, social-whatsapp, social-instagram, social-telegram. For Mastodon and Slack, you must provide the complete URL as these platforms don't have a standard prefix. Menu Define navigation menus in data/menu.yml. Each key becomes a menu section in the sidebar: nav: - title: "Home" path: "/" icon: "fa-solid fa-house" - title: "Blog" path: "/blog" icon: "fa-regular fa-newspaper" doc: - title: "Getting Started" path: "/docs/getting-started/" icon: "fa fa-bolt" Each item supports title, path, icon (Font Awesome class), and optionally target (e.g. _blank for external links). Authors Define authors in data/authors.yml to display author info on blog posts: ada: name: "Ada Lovelace" avatar: "https://example.com/ada.png" job: Software Pioneer profile: "https://x.com/ada" nickname: "ada" bio: "Passionate about algorithms and analytical engines." Then reference an author in a post's frontmatter with author: ada. Layouts Theme layouts are automatically available: use layout: foo and it resolves local first, then falls back to the theme. To override a theme layout, create your own layout file and use theme-layout: foo to extend from the original. default // Base HTML structure ├── main // Shared site layout (sidebar, nav, footer) │ ├── home // Home page │ ├── blog // Blog listing with pagination │ ├── page // Generic page │ ├── post // Blog post with author and tags │ └── tag // Tag archive page └── 404 // Error page Page Data Frontmatter keys available to control page behavior per layout. All layouts Key Description Default body-class Custom CSS class on the body element page-class CSS class for page-specific styling robots Robots meta tag value Page / Post Key Description Default show-header Show the page header true show-header-date Show the date in the header true show-header-intro Show the description in the header true content-toc Enable table of contents false content-toc-title TOC section title Contents content-toc-levels Heading levels to include in TOC 2 Post Key Description Default author Author key from data/authors.yml tags List of tags for the post fig-caption Caption for the post cover image Blog Key Description Default featured Number of featured posts Partials Override any theme partial by creating a file with the same name in templates/partials/roq-default/: partials/roq-default/ ├── 404.html ├── head.html ├── head-scripts.html ├── page-header.html ├── page-toc.html ├── pagination.html ├── sidebar-about.html ├── sidebar-contact.html ├── sidebar-copyright.html ├── sidebar-darkmode.html └── sidebar-menu.html Qute User-Tags The theme provides reusable Qute user-tags for building pages: roq/hero Hero section for the home page. {#roq/hero logo="roq-logo.svg"} {#title}My Site{/title} {#tagline}A tagline for my site{/tagline} {#subtitle}Some extra info{/subtitle} {/roq/hero} roq/featureCard Feature card, typically used on the home page. {#roq/featureCard icon="fa-solid fa-bolt" title="Fast" link="/docs/" link-text="Learn more" highlighted=true} Feature description here. {/roq/featureCard} roq/postCard Blog post preview card. Used in blog and tag layouts, can also be used in custom pages. {#roq/postCard post=myPost /} roq/authorCard Author profile card. {#roq/authorCard name="Ada Lovelace" avatar="ada.png" profile="https://example.com" nickname="ada"} Author bio here. {/roq/authorCard} roq/terminal Terminal emulator component for displaying commands. {#roq/terminal title="Getting Started"} {#commands} {#command} {#prompt}${/prompt} {#cmd}quarkus{/cmd} {#args}create app my-site -x roq{/args} {/command} {/commands} {/roq/terminal} CSS Customization Create a web/_custom.css file in your site to override theme styles. This file is processed by Tailwind, so you can use Tailwind utilities, @apply, @theme, and other Tailwind features. Other CSS files added to web/ are bundled as plain CSS without Tailwind processing. Color Palettes The theme is built on three color palettes. Override any combination to completely transform the look and feel of your site: Palette Role Default accent Structure: headings, links, page headers, sidebar slate pop Energy: buttons, hover effects, gradients, icons sky neutral Text and backgrounds: body text, cards, sidebar background gray To swap a palette, map all 11 shades (50 through 950) in a @theme block in your web/_custom.css. You can use any Tailwind color or custom hex values: @theme { /* Accent: indigo instead of slate */ --color-accent-50: var(--color-indigo-50); --color-accent-100: var(--color-indigo-100); --color-accent-200: var(--color-indigo-200); --color-accent-300: var(--color-indigo-300); --color-accent-400: var(--color-indigo-400); --color-accent-500: var(--color-indigo-500); --color-accent-600: var(--color-indigo-600); --color-accent-700: var(--color-indigo-700); --color-accent-800: var(--color-indigo-800); --color-accent-900: var(--color-indigo-900); --color-accent-950: var(--color-indigo-950); /* Pop: rose instead of sky */ --color-pop-50: var(--color-rose-50); --color-pop-100: var(--color-rose-100); --color-pop-200: var(--color-rose-200); --color-pop-300: var(--color-rose-300); --color-pop-400: var(--color-rose-400); --color-pop-500: var(--color-rose-500); --color-pop-600: var(--color-rose-600); --color-pop-700: var(--color-rose-700); --color-pop-800: var(--color-rose-800); --color-pop-900: var(--color-rose-900); --color-pop-950: var(--color-rose-950); /* Neutral: stone instead of gray */ --color-neutral-50: var(--color-stone-50); --color-neutral-100: var(--color-stone-100); --color-neutral-200: var(--color-stone-200); --color-neutral-300: var(--color-stone-300); --color-neutral-400: var(--color-stone-400); --color-neutral-500: var(--color-stone-500); --color-neutral-600: var(--color-stone-600); --color-neutral-700: var(--color-stone-700); --color-neutral-800: var(--color-stone-800); --color-neutral-900: var(--color-stone-900); --color-neutral-950: var(--color-stone-950); } Changing all three palettes gives your site a completely different identity while keeping the same layout and structure. You can also override just one or two palettes. The Roq blog itself uses a custom cyan for accent and orange for pop. Sidebar The sidebar is fully customizable through theme variables in web/_custom.css. Color variables are defined in @theme and can be used as Tailwind utilities (e.g. text-sidebar, bg-sidebar-subtle): Variable Role Default Utility --color-sidebar Main text color neutral-300 text-sidebar --color-sidebar-heading Site name, headings white text-sidebar-heading --color-sidebar-muted Secondary text, separators neutral-500 text-sidebar-muted --color-sidebar-subtle Subtle borders, hover backgrounds rgba(255,255,255,0.06) border-sidebar-subtle --color-sidebar-border Sidebar right border neutral-700 border-sidebar-border --sidebar-bg Background (supports gradients) neutral 800→900 gradient By default the sidebar is dark in both modes. To create a light sidebar in light mode with a dark sidebar in dark mode: @theme { --color-sidebar: var(--color-indigo-800); --color-sidebar-heading: var(--color-indigo-950); --color-sidebar-muted: var(--color-indigo-500); --color-sidebar-subtle: rgba(0, 0, 0, 0.08); --color-sidebar-border: var(--color-indigo-200); --sidebar-bg: linear-gradient(180deg, var(--color-indigo-50) 0%, var(--color-indigo-100) 100%); } .dark { --color-sidebar: var(--color-indigo-200); --color-sidebar-heading: white; --color-sidebar-muted: var(--color-indigo-400); --color-sidebar-subtle: rgba(255, 255, 255, 0.06); --color-sidebar-border: var(--color-indigo-800); --sidebar-bg: linear-gradient(180deg, var(--color-indigo-950) 0%, var(--color-indigo-900) 100%); } For best dark mode legibility with colored sidebars, test your chosen palette carefully. Alternatively, omit the .dark block to fall back to the default dark sidebar. Dark Mode Dark mode is built-in with automatic system preference detection and a toggle in the sidebar. No configuration needed. SEO The theme includes built-in SEO support with meta tags, Open Graph, Twitter cards, favicon auto-discovery, and RSS. For more details, see the SEO, Favicon, Analytics, and RSS documentation. ### [Base Theme](/theme/base-theme/) The base theme is a minimal starting point included with Roq. It provides the essential HTML structure with SEO, favicon, and Web Bundler support, giving you full control over your site's design. This is the ideal choice when you want to build a fully custom site from scratch. Layouts default // Base HTML structure (SEO, favicon, bundle) ├── page // Simple page with title └── post // Post with title and date The default layout provides the HTML skeleton with: {#seo /} for meta tags, Open Graph, and Twitter cards {#favicon /} for automatic favicon discovery {#bundle /} for CSS and JS bundling via Web Bundler The page and post layouts extend default with minimal markup (title, content, and date for posts). Customization Since the base theme provides only the HTML structure, you style everything through your own CSS in web/app.css. The starter CSS includes basic variables for colors, typography, and a simple card layout that you can replace entirely. See the Favicon, SEO, and Analytics documentation for configuring the built-in tags. ## Pages ### [Markup Examples](/markups/) Markup Examples This section contains pages demonstrating the rendering of different markup languages in Roq. Available Examples AsciiDoc Example - AsciiDoc content types including headings, lists, tables, code blocks, admonitions, and more Markdown Example - Markdown content types including headings, lists, tables, code blocks, and more These pages are used for seeing theme styling and are excluded from search engines and sitemaps. ### [Oops! Roq is saying 404](/404.html) ### [About Roq](/about/) This tool is a testament to how extensible and powerful Quarkus is, offering a low-risk yet highly capable platform that will evolve as demand grows. Origins I wrote a blog post explaining how it all started. Credits Those are generated as a JSON by all-contributors, then we leverage roq-data to print them... slick 🏄! Thanks goes to these wonderful people: Andy Damevin author @ia3andy Matheus Cruz author @mcruzdev Melloware @melloware Max Rydahl Andersen @maxandersen Holly Cummins @holly-cummins Erik Jan de Wit @edewit Jérôme Tama author @jtama Rayza Luana @RayzaAnchayhua Martin Kouba @mkouba Foivos @zakkak Joel Takvorian @jotak Pablo Gutierrez @pablomxnl Pedro Hos @pedro-hos OKC JUG @okcjug Jason Lee @jasondlee João Nascimento @jotaNas janwesterkamp @janwesterkamp Clément de Tastes @CodeSimcoe Stéphane Philippart @philippart-s Patrik Duditš @pdudits Rolfe Dlugy-Hegwer @rolfedh Matheus André @matheusandre1 Sun S. D. Tan @sunix Matheus Oliveira @omatheusmesmo Dimitri Hautot @DimitriHautot ### [Roq Advanced Stuff](/docs/advanced/) If you find any issue or missing info, be awesome and edit this document to help others Roqers. Built-in features Roq and its themes include several built-in features for SEO, feeds, and discoverability. The base theme provides the essential tags (SEO, favicon, Web Bundler), while the default theme builds on top of it with a full layout, dark mode, sidebar, and more. If you are using either theme, most of these features are already enabled. Configure them through your site index frontmatter or by adding content files. For details on each feature, see the Base Theme, Favicon, SEO, Analytics, RSS, and LLMs.txt sections in the basics guide. 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 partials/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 partials/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. Themes Browse all available themes in the Plugins & Themes directory. Overriding theme In Roq, you can override theme partials or layouts. To override a theme partial, it is very simple, you just need to add the same template in your site. For example, adding your own templates/partials/roq-default/pagination.html will override the one from the default Roq theme To override a theme layout, you need to insert an extra layout layer. This allows you to override only specific sections of the theme layout, without duplicating the entire layout structure. Roq layouts leverage Qute include under the hood. It is possible to define insert sections that provide overridable default content. Example Let’s override the roq-default theme’s main layout so that our customizations apply everywhere it is used. templates/layouts/main.html --- theme-layout: 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: theme-layout: main explicitly targets the theme’s main layout 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 layout: main is used (even in the theme), your override will be used. How it Works Internally Roq handles theme layouts in two layers: The original theme layouts are always kept under theme-layouts/…​. These are the base templates that overrides can extend for partial customization. Roq also produces corresponding site layouts for each theme layout. Site layouts are the versions that your pages actually reference. When Roq builds your site, it checks for overrides: If you provide an override: Roq uses your override as the site layout, while still keeping the theme’s original layout under theme-layouts/…​ for inheritance. If you don’t provide an override: Roq copies the theme layout as the site layout, so it can be used directly. This mechanism ensures you only need to override the parts you want to customize, everything else automatically falls back to the theme. 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 templates/partials/{theme-name}/. 2 Layouts need to be declared in theme-layouts/ using a directory with the theme name templates/theme-layouts/{theme-name}/. Same as for a site, scripts and styles can either be added to src/main/resources/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> You need to create an application.properties: src/main/resources/application.properties site.theme=roq-default (1) 1 This allows site referencing the theme to default to this theme. Links & Urls 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. Link placeholders 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 :raw-path The raw file path of the page without the extension. My$, my car or été/2024 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 name. my-page-title All :Slug The case-preserving 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 name. My-Page-Title All :name The slugified name of the file (or directory if index). If the filename contains a date (e.g., '2025-08-21-My-Blog-Post.md'), the date portion will be stripped away. This behavior mimics that found in Jekyll builds, making any migrations simpler. my-blog-post All :Name The case-preserving slugified name of the file (or directory if index). If the filename contains a date (e.g., '2025-08-21-My-Blog-Post.md'), the date portion will be stripped away. This behavior mimics that found in Jekyll builds, making any migrations simpler. My-Blog-Post 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 The slug derivation replaces all non-alphanumeric characters by - to make them url friendly. 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 to get the next page url in a document: <a href="{page.next.url}">{page.next.title}</a> or when iterating on documents: {#for post in site.collections.posts} <a href="{post.url}">{post.title}</a> {/for} or also to manually retrieve a page url with site.page(sourcePath): <a href="{site.page('foo.html').url}">{site.page('foo.html').title}</a> By default, url will be rendered as the path 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. Alternative expression syntax Qute supports an alternative expression syntax where output expressions use {=expr} instead of {expr}. This makes templates safer when content contains curly braces (e.g. code samples, JSON) since only {=…​} and {#…​} are interpreted as Qute expressions, everything else is plain text. This will become the default syntax for Roq in a future version. To enable it, add to your configuration: quarkus.qute.alt-expr-syntax=true With this enabled, templates use {=page.title} for expressions and {#for …​} / {#include …​} for sections (sections are unaffected). Regular {foo} is treated as plain text. Escaping pages content There are cases where you might not want your page content to be parsed by Qute, to avoid conflicts with the content. You have different options: Configure it globally via site.escaped-pages (globs are allowed): config/application.properties site.escaped-pages=posts/escaped**,my-page.html Set it in FrontMatter by adding escape: true in your page data (not working with layouts). Escape inline content by wrapping the section with \{| and |\}, or by manually escaping Qute expressions using \{. Setting the Root Path for your site (base-path) When the entire Roq site is under a root path such as mysite.io/foo/, configure quarkus.http.root-path in the Quarkus configuration: config/application.properties quarkus.http.root-path=/foo Environment variable: QUARKUS_HTTP_ROOT_PATH For GitHub Pages, this is already detected and handled by the Roq GitHub Action, no need to do anything. Debugging To debug errors, you may print debug information in the logs: roq -Dquarkus.log.category.\"io.quarkiverse.roq.frontmatter\".level=DEBUG Testing All templates will be validated at generation. Sometimes, for example on Pull-Request, you want to detect issues before actual generation. Roq provides a way to generate the full site during the test phase. First, include the quarkus-roq-testing test dependency in your pom.xml. pom.xml <dependency> <groupId>io.quarkiverse.roq</groupId> <artifactId>quarkus-roq-testing</artifactId> <version>{cdi:project-info.release.current-version}</version> <scope>test</scope> </dependency> Test Site Generation Once you’ve added the dependency, you can easily ensure all pages are generated without errors: src/test/java/RoqSiteTest.java @QuarkusTest @RoqAndRoll public class RoqSiteTest { @Test public void testGen() { // All pages will be generated/validated during test setup } } That’s it! This basic test already verifies that your site generation is error-free. You can also add checks on the actual generated content as it is served using a static file server: src/test/java/RoqSiteTest.java @QuarkusTest @RoqAndRoll public class RoqSiteTest { @Test public void testIndex() { RestAssured.when().get("/") .then() .statusCode(200) .body(containsString( "Ready to Roq my world!" )); } } The RestAssured port will automatically use the Roq static test server, running on port 8082 by default. The Roq test server port could be modified by an annotation parameter like this @RoqAndRoll(port=9090). Using standard Quarkus test It’s possible to use the standard Quarkus test support (Testing Your Application) to check the content, but then pages will be rendered dynamically on demand at runtime: src/test/java/QuteWebSiteTest.java @QuarkusTest public class QuteWebSiteTest { @Test public void testIndex() { RestAssured.when().get("/") .then() .statusCode(200) .body(containsString( "Ready to Roq my world!" )); } } In this case the RestAssured port will automatically use the Quarkus dynamic test server, running on port 8081 by default. Updating Roq Run the update command from your project directory: $ roq update This will update the Quarkus version and extensions (including Roq) and make sure they are compatible together. The releases and migration info can be found here. Site Configuration Site configuration is done in config/application.properties (or src/main/resources/application.properties): In a multi-module Maven project, use src/main/resources/application.properties instead. The config/application.properties location is resolved relative to the JVM working directory, which may not match the module directory when building from the reactor root. Configuration property fixed at build time - All other configuration properties are overridable at runtime Configuration property Type Default site.url the base hostname & protocol for your site, e.g. http://example.com Environment variable: SITE_URL string site.route-order 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: SITE_ROUTE_ORDER int 1100 site.ignored-files Add new ignored files to the default list. The ignored files (relative to the site directory). Only the content/, public/, and static/ directories are scanned. Environment variable: SITE_IGNORED_FILES list of string site.default-ignored-files The default ignored files (relative to the site directory) include: All files or directories starting with an underscore (_) These patterns are additional to the scanner’s own OS-level defaults (e.g. .DS_Store, Thumbs.db, *~, .class). Environment variable: SITE_DEFAULT_IGNORED_FILES list of string **/_**, _** site.escaped-pages Pages whose content should be escaped— i.e., included in Qute rendering but not parsed for Qute expressions. This is based on the page’s relative path from the content directory. This applies only to pages (not layouts or partials). Supports glob expressions. Environment variable: SITE_ESCAPED_PAGES list of string site.page-layout The layout to use for normal html pages if not specified in FM. When empty, the page will not use a layout when it doesn’t specify it in FM. Resolves local layout first, then theme layout as fallback. Environment variable: SITE_PAGE_LAYOUT string page site.content-dir The directory which contains content (pages and collections) in the Roq site directory. Environment variable: SITE_CONTENT_DIR string content site.static-dir The directory (dir name) which contains static files to be served (with 'static/' prefix). Environment variable: SITE_STATIC_DIR string static site.public-dir The directory which contains public static files to be served without processing (dir name) Environment variable: SITE_PUBLIC_DIR string public site.images-path The path containing static images (in the public directory) Environment variable: SITE_IMAGES_PATH string images/ site.generator When enabled it will select all FrontMatter pages in Roq Generator Environment variable: SITE_GENERATOR boolean true site.future Show future documents Environment variable: SITE_FUTURE boolean false site.theme The theme name. Used to resolve theme layouts when using theme-layout: in front matter. With a theme, layout: foo resolves local first, then theme layout as fallback. Environment variable: SITE_THEME string roq-base site.draft Show draft pages Environment variable: SITE_DRAFT boolean false site.draft-directory Directory name used to mark collection documents as draft when frontmatter does not define attribute draft. Frontmatter draft takes precedence over this directory-based fallback. Environment variable: SITE_DRAFT_DIRECTORY string drafts site.date-format Format for dates Environment variable: SITE_DATE_FORMAT string yyyy-M-d[ HH:mm][:ss][ Z] site.time-zone The default timezone Environment variable: SITE_TIME_ZONE string document timezone if provided or system timezone site.default-locale The default language to use when no language is specified in the frontmatter. This language will be used as a fallback for articles that don’t have a 'locale' property. Environment variable: SITE_DEFAULT_LOCALE string en site.slugify-files Indicates whether file names in the public directory and files attached to pages should be slugified (converted to a URL-friendly format). When enabled, file names will automatically be transformed into a URL-safe format. Additionally, page.file and site.file references can use the original file names, as they will also be slugified during the process. Environment variable: SITE_SLUGIFY_FILES boolean true site.collections."collections-map" If this collection is enabled Environment variable: SITE_COLLECTIONS__COLLECTIONS_MAP_ boolean true site.collections."collections-map".future Show future documents (overrides global future for this collection) Environment variable: SITE_COLLECTIONS__COLLECTIONS_MAP__FUTURE boolean false site.collections."collections-map".hidden If true, the collection won’t be available on path but consumable as data. Environment variable: SITE_COLLECTIONS__COLLECTIONS_MAP__HIDDEN boolean false site.collections."collections-map".layout The layout to use if not specified in FM data. When empty, the document will not use a layout when it doesn’t specify it in FM. Resolves local layout first, then theme layout as fallback. Environment variable: SITE_COLLECTIONS__COLLECTIONS_MAP__LAYOUT string site.collections."collections-map".from-data.id-key The data attribute to use as the page identifier (slug). Environment variable: SITE_COLLECTIONS__COLLECTIONS_MAP__FROM_DATA_ID_KEY string required site.generated-templates-output-dir The directory where the generated templates should be created inside the output directory. Environment variable: SITE_GENERATED_TEMPLATES_OUTPUT_DIR string roq-templates site.path-prefix READ CAREFULLY: The root path of your site (e.g. /blog) should be set using quarkus.http.root-path. This path prefix should be relative to the Quarkus HTTP root path and is meant to be used only when the Roq site is served alongside a Quarkus application on a separate path. Environment variable: SITE_PATH_PREFIX string ### [Roq the basics](/docs/basics/) If you find any issue or missing info, be awesome and edit this document to help others Roqers. By default, your site files should be located in the project root directory (or in the Java resources dir: 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-page.md (4) │ └── index.html (5) │ ├── public/ (6) │ └── images/ │ └── logo.png │ ├── web/ (7) │ ├── app.js │ └── app.css │ ├── templates/ (8) │ ├── partials/ (9) │ │ ├── head.html │ │ └── pagination.html │ │ │ └── layouts/ (10) │ ├── base.html │ ├── page.html │ └── post.html ├── config/ │ └── application.properties (11) └── pom.xml (12) 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-page.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, PDFs, or other assets. These files are served as-is without processing. The default image directory is public/images/. 7 Web Bundler The web/ directory contains JavaScript and CSS source files bundled by the Quarkus Web Bundler (included by default with Roq). 8 Templates The templates/ directory is optional as templates can be provided by a theme. It contains Qute templates for partials and layouts. 9 Qute Partials The partials/ directory contains reusable Qute template fragments, such as head.html and pagination.html, which can be included in layouts. 10 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. 11 Configuration The config/application.properties file contains the site configuration such as collections, theme, and other settings. 12 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 will result in a build error. This prevents issues from leaking into production. Templates for layouts, documents, and pages may also declare a FrontMatter (FM) header, delimited by two ---. This header contains YAML data used to configure things like: Routing Data for templates Content generation Pagination For example, a page template blog-post.html might start with: --- title: "My First Blog Post" date: 2025-09-08 author: "Andy" layout: post tags: qute, roq, tutorial --- **Hello World** Content The content/ directory contains the site index page and all the Pages and Collections (such as blog posts). It may also contain attached static files (Page attached static files). Content templates can be written in html, json, yaml, yml, or xml or using Markup languages. We sometimes refer to pages in collections as documents, documents are just a special kind of pages. Site index Your site index page is required and should be located in content/index.html (you can also use a markup extension). content/index.html --- title: Hello Roqers (1) description: It is time to start Roqing 🎸! --- <h1>Hello fellow Roqers 🤘</h1> <p> With Roq, it is very easy to link to another <a href="{site.url('/roq-page')}">page</a>. (2) </p> 1 The index.html also describe your site information through a FrontMatter header. 2 We use the {site.url(path)} using Qute to manually resolve other pages urls. There are different ways to link your pages as explained in the Links & Urls section. Pages Any content 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 (included by default with Roq). roq-bottom.md --- title: Roq Bottom description: When you hit Roq bottom, try Roq to climb back up! 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. By default, pages use the page layout and documents in collections use the post layout (from the theme if using one). You can override this with layout: in the FrontMatter. Markup languages You can use different markup languages in Roq content. Markdown Roq ships with Markdown support out of the box (powered by commonmark-java). To write pages or documents in Markdown, simply use the .md or .markdown file extension. This plugin also allows converting Qute data that contains Markdown into HTML using the mdToHtml template extension. content/page.html --- bar: | ## Hello This is using **Markdown** --- {page.data.bar.mdToHtml} If you don’t need Markdown, you can disable it by excluding the Markdown plugin from the Roq extension in your pom.xml. AsciiDoc AsciiDoc is also fully supported in Roq via the AsciiDoc plugin. Once installed, to write pages or documents in AsciiDoc, simply use the .adoc or .asciidoc file extension. 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 🤭 tags: (1) - funny - ai img: 2024/10/roq-solid.jpg --- # {page.title} (2) 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 You can define tags (see Tagging plugin to create pages for tags). 2 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.url contains the post url (as a RoqUrl), absolute to get the absolute url. 3 post.image is smart and is already resolved to the image url (as a RoqUrl), 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. Draft and Future To create a draft page or document, you can use the frontmatter field draft: true. Drafts are hidden by default unless you set %dev.site.draft=true in your Quarkus configuration (the %dev makes it effective only in dev mode). You can also place draft documents (for collections) in a drafts/ (for example posts/drafts/) directory. Documents in that directory are treated as drafts only when their frontmatter does not define draft. If frontmatter explicitly sets draft, that value takes precedence. You may also start roq with the option: roq -Dsite.draft. By default, documents with a date in the future (FrontMatter data or file name) will not be visible unless you have %dev.site.future=true in your Quarkus configuration (the %dev makes it only available in dev-mode). You may also start roq with the option: roq -Dsite.future. It is possible to configure a collection to always show future documents: site.collections.events.future=true (1) 1 Always show future documents for the "events" collection. 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. You need to define the new collection in the config/application.properties (or src/main/resources/application.properties) file. If you created your site with roq create, this file already exists with the posts collection configured: site.collections.docs=true (1) site.collections.docs.layout="page" (2) site.collections.docs.future=true (3) site.collections.posts=true site.collections.posts.layout="post" 1 We are adding the new collection docs; 2 Here, we set the layout for docs pages to page; 3 Since the new collection is not a time-based collection, we need to set future as true to show all files. Since we’re adding a new collection, it’s also necessary to declare the existing posts collection to ensure it continues to function correctly. 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. Using a theme If you created your site with roq create, you already have a theme installed. A theme provides layouts, styles, and scripts for your site. Pages automatically use layouts from the theme (local layouts take priority if they exist). The default theme comes with a full blog layout, dark mode, sidebar, and SEO support. The base theme provides a minimal HTML structure with just SEO, favicon, and Web Bundler, giving you full control over the design. Browse all available themes in the Plugins & Themes directory. For advanced usage (overriding, developing), refer to the Themes section. To add a theme to an existing project (adds the dependency only, without initial site files): roq add theme:default Templates (Layouts, Partials, and User Tags) Layouts, partials, and user tags are templates and must use one of the following extensions: html, xhtml, htm, json, yaml, yml, or xml. INFO: .md and .adoc files are not parsed as templates. Instead, you can use markup inside templates through Qute sections {#md} and {#adoc}, provided the corresponding plugins are available. Layouts 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 (if the content of the file is not a full HTML page — i.e., it does not contain a <html> tag or <!DOCTYPE declaration), posts will use the post layout and normal pages will use 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. If you’re not using a theme, you can create your own templates (example templates). How to create a layout page If you’re using a theme, you can take advantage of its layouts, but you’re also free to add your own. Layouts are built on a powerful inheritance system, allowing flexibility and customization. When using a theme, you can create new layouts that extend the existing theme layouts or partially override them to better suit your needs. Learn more here: Overriding Theme. In this sample we will create a simple 3 steps inheritance layout. All below files should be located in the templates/layouts/ directory. To do so, you will first need a default.html file as followed: default.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>{page.title}</title> (1) </head> <body> {#insert /} (2) </body> </html> 1 Use the title page attribute that will be defined by the using page 2 Allows to insert arbitrary content from the page using the layout. Then you can create a main.html file as followed: main.html --- layout: default (1) title: And now for something completely different (2) --- <header> Head </header> {#insert /} <footer> Toes </footer> 1 Uses the default layout. 2 Defines a default title attribute value for all its children. Finally, you can create a content.html file as followed: content.html --- layout: main (1) --- {#include partials/image /} (2) knees 1 Uses the main layout. 2 Includes a partial template. Then it will be rendered as followed: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>And now for something completely different</title> </head> <body> <header> Head </header> <img src="https://supersimple.com/wp-content/uploads/head-shoulders-knees-and-toes-flashcards-726x1024.png" alt="Illustration" /> knees <footer> toes </footer> </body> </html> To summarize, each page will be rendered using their parent layout recursively. The inheritance system works for the page content as much as for the FrontMatter data. Partials You can split layouts into partial, reusable templates. Partials make it easier to maintain different sections of your layouts. By default, they are located in templates/partials/. For example: ➡️ templates/partials/pagination.html can be included using: {#include partials/pagination /} Tags Tags (User Tags) are custom reusable snippets of code, similar to small components. They help reduce duplication and let you create reusable sections. For example, create a tag in templates/partials/post-link.html: {@io.quarkiverse.roq.frontmatter.runtime.model.DocumentPage post} <div class="post-link"> <a href="{post.url}"> <img src="{post.image}" alt="{post.title}" class="post-image"/> <h3>{post.title}</h3> <p>{post.description}</p> </a> </div> You can then use it in another template like this : {#post-link post=site.document('posts/2025-09-08-my-article.md')/} This would render a styled card linking to the given DocumentPage post. You can reference pages and documents by their source path. For example: site.document('posts/2025-09-08-my-article.md') site.page('about.md') To learn more, see the Qute User Tags guide. Built-in features Favicon Roq automatically discovers favicon files from your public/ directory. Place any of the following files and they will be included in the HTML head: favicon.svg (preferred, scalable) favicon.ico (legacy fallback) favicon.png (PNG fallback) apple-touch-icon.png (iOS devices) To override auto-discovery, set icon or favicon in your site index frontmatter: icon: my-custom-icon.svg SEO Roq includes built-in SEO support with meta tags, Open Graph, and Twitter cards. The \{#seo page site /} tag is automatically included by the default theme. If you use a custom layout, add it to your HTML head: <head> {#seo page site /} </head> It will automatically generate <title>, <meta> author/description, Open Graph and Twitter card tags from the FrontMatter data. Configure SEO data through the site index frontmatter: Key Description author Default author name for meta tags (can be overridden per page) lang Default language/locale (e.g. en, fr) twitter Twitter/X handle for Twitter cards (e.g. quarkusio) Facebook Open Graph facebook: app_id: "123456789" publisher: "https://www.facebook.com/yourpage" admins: "your-fb-admin-id" Webmaster Verifications webmasterVerifications: google: "your-google-verification-code" bing: "your-bing-verification-code" yandex: "your-yandex-verification-code" Available keys: google, bing, alexa, yandex, baidu, facebook. Analytics To add Google Analytics 4, configure it in the site index frontmatter (used by the default theme): analytics: ga4: XXXXXXXXXX If you use a custom layout, add the \{#ga4 /} tag to your HTML head. RSS The \{#rss site /} tag is automatically included by the default theme. If you use a custom layout, add it to your HTML head: <head> {#rss site /} </head> Then create a content/rss.xml file with: {#include fm/rss.html /} Roq will generate a valid RSS feed from your blog posts FrontMatter data. By default, <content:encoded> contains the post description. Use the contentLimit parameter to include richer content: \{#include fm/rss.html contentLimit=0 /} (1) \{#include fm/rss.html contentLimit=150 /} (2) 1 Full rendered content 2 Content abstract limited to 150 words LLMs.txt Roq includes built-in support for generating /llms.txt and /llms-full.txt following the llms.txt specification. AI systems like ChatGPT, Claude, and Perplexity use these files to understand site structure and content. /llms.txt — A structured index containing the site title, summary, and a list of all pages with their titles and descriptions. /llms-full.txt — The same structure, with the full plain-text content of each page included. To enable llms.txt generation, create the following content files: content/llms.qute.txt: {#include fm/llms.html} content/llms-full.qute.txt: {#include fm/llms-full.html} To exclude a specific page, set llmstxt: false in its frontmatter. Loading Roq context in your AI assistant You can give your AI coding assistant full context about Roq by pointing it to https://iamroq.dev/llms.txt (or /llms-full.txt for complete content). This provides the project structure, quick start instructions, template syntax, and links to detailed skill files. Variables You can use Qute to access site and pages data. For this use the site and page variables: The site (javadoc) 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 (javadoc) 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.source Origin The page source (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 page.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 or null 2023-10-01T12:00:00Z 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 accessed with {cdi:foo.bar} in any template. You can also have a structure (Java type mapping) for the data among other features, learn more. Template Extensions The Qute templating language supports a concept called template extension methods. These methods allow us to add functionality to types and expose it in our templates. Moving logic from the template to Java code gives us the ability to use a more robust language for more complex logic and makes the functionality more easily reusable, as well as keeping our templates clean: Qute provides built-in template extensions Roq has several built-in template extensions (javadoc): Usage in a Qute template Description text.numberOfWords Returns the number of words in the string text.wordLimit(limit) Returns the text limited to limit words, adds "…​" if truncated text.slugify Returns a slugified version of the text htmlContent.contentAbstract(limit) Returns the HTML content limited to limit words htmlContent.stripHtml Returns the text with all HTML tags removed page.readTime Returns the estimated reading time in minutes for the page content page.contentAbstract Returns the first 75 words of the page content page.contentAbstract(limit) Returns the page content limited to limit words list.randomise Returns the list in random order jsonArray.asJsonObjects Returns a list of JsonObject. All items must be JSON objects. collections.collection(key) Returns the collection for the given key posts.filter(key, value) Returns only documents matching the front-matter key/value posts.future Returns only documents dated in the future posts.past Returns only documents dated in the past posts.sortBy(key, reverse) Sorts documents by a front-matter key (string), optionally reversed posts.sortByDate(reverse) Sorts documents by date, optionally reversed field.asStrings Normalizes a front-matter field into a list of strings fileName.mimeType Returns the MIME type based on the file name extension You can provide your own template extensions by adding a class to your projects src/main/java directory and annotating either class or the public static method with @TemplateExtension: public class Extensions { @TemplateExtension public static String doSomething(String text) { // .... } } and then you can use that extension in your template: {site.pageContent(page).doSomething} For more details, see the Quarkus and Qute documentation. Site static files Site static files are served as-is without any additional processing. By default, all files in public/ are scanned as static files. public/ ├── images/image.jpg (1) ├── scripts/script (2).js (2) └── presentation.pdf (3) 1 generated as on /images/image.jpg 2 generated as /scripts/script-2.js(slugified 👇) 3 generated as /presentation.pdf Site static files url can be accessed through site.file('presentation.pdf') or site.file('scripts/script (2).js') or just their relative paths. site.file(path) also checks that the file exists on disk and will adapt on site configuration (e.g. root path change). To improve SEO, all static files are slugified, Roq replaces non-URL-friendly characters with -. URL-friendly characters are alphanumeric, -, and _ (multiple dots are also tolerated for files). Using site.file and page.file variables automatically applies this replacement on returned url (same for images). To disable this behavior, set site.slugify-files=false in Roq’s configuration. 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 This section explains how to access images in your site or in a specific page. Site images Site‑level images live in public/images/ (e.g. my-site/public/images/image-1.png). The default public path is images/ and can be customized in the site configuration. Use site.image() to generate the correct URL: <img src="{site.image('image-1.png')}" /> site.image(name) is equivalent to: <img src="{site.file('images/' + name)}" /> Page images When a page is a directory (for example posts/surf/index.html), the method {page.image(name)} checks if the image is attached to that page and returns its URL. For single‑file pages (posts/basketball.md), {page.image(name)} behaves like site.image(name) and resolves from public/images/. 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 Non-directory pages → page.image() == site.image(). 2 Page-attached file → accessible via {page.image('surf.jpg')}. 3 Site images → accessible everywhere via site.image(name). Usage example File: `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 --> 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, add your scripts (js, ts) and styles (css, scss) in the web/ directory at the project root: my-site/ ├── web/ │ ├── app.js │ └── app.scss src/main/resources/web/app/ also works if you prefer the standard Java resources layout. 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. ### [Getting started](/docs/getting-started/) Install the Roq CLI with JBang and create your first site in seconds. Get up and running with Roq 1. Install the Roq CLI via JBang (installs JBang if needed): Linux/macOS Windows $ curl -Ls https://sh.jbang.dev | bash -s - trust add https://repo1.maven.org/maven2/io/quarkiverse/roq/ $ curl -Ls https://sh.jbang.dev | bash -s - app install --fresh --force roq@quarkiverse/quarkus-roq ✓ roq installed > iex "& { $(iwr https://ps.jbang.dev) } trust add https://repo1.maven.org/maven2/io/quarkiverse/roq/" > iex "& { $(iwr https://ps.jbang.dev) } app install --fresh --force roq@quarkiverse/quarkus-roq" ✓ roq installed 2. Create your site (you can change the name): Create $ roq create my-site Creating Roq site: my-site ✓ Roq site created in ./my-site This creates a site with the default theme (blog layout, dark mode, sidebar, SEO). To start from scratch with a minimal HTML structure and full control over the design, use the base theme instead: roq create my-site -x theme:base. Browse all available themes. 3. Start dev mode: Dev $ cd my-site $ roq start Listening on http://localhost:8080 ✓ Live-reload enabled Open localhost Roq the basics → ### [Migrating to Roq](/docs/migrating/) If you find any issue or missing info, be awesome and edit this document to help others Roqers. Roq uses Qute templates and a different content model than Jekyll, Hugo, or other static site generators. This guide covers the workflow, syntax mappings, and Roq-specific pitfalls for migrating an existing static site to Roq. Prerequisites Familiarity with Roq concepts (see the Getting Started guide) An existing static site you want to migrate (Jekyll, Hugo, or similar) The Roq CLI installed (see Getting Started) Java 21+ installed Using an LLM to accelerate migration An LLM (Large Language Model) such as Claude, ChatGPT, or a locally hosted model can accelerate the migration of your templates, layouts, and content files. Template conversion is mostly mechanical syntax mapping, which LLMs handle effectively. To give your LLM full context about Roq, point it to https://iamroq.dev/llms-full.txt. LLM-assisted migration is not fully automatic. Expect to review and adjust the output. The prompts in this guide are a starting point, refine them as you learn what works for your site. Overview of the migration process Migrating a static site to Roq involves converting four categories of files: Project scaffold — roq create, application.properties, and directory structure Layouts and templates — Liquid/Jinja/Go templates to Qute templates Content files — Markdown or AsciiDoc with front matter adjustments Static assets — JavaScript, SCSS/CSS, images, and data files Content files typically need minimal changes. Configuration and asset migration require more manual work. Roq expects content in a content/ directory by default (configurable via site.content-dir). Templates go in templates/layouts/, static files in public/, and data files in a configurable data directory. Recommended phased approach Do not attempt to migrate everything at once. Use a phased approach with validation gates: Phase Scope Gate A: Foundation Roq project + one page rendering correctly Stop if content does not render. Verify quarkus.qute.alt-expr-syntax=true is set so curly braces in code samples are treated as plain text. B: Styling + scale JavaScript, CSS/SCSS, all content pages Stop if build time exceeds 10 minutes or memory exceeds 4 GB C: Full site All templates, blog, homepage, static pages, redirects, CI/CD Production-ready Commit after each step. Prepare reference material Having concrete examples of working Roq templates makes migration smoother (whether you are converting manually or with an LLM). Collect these files from a working Roq project (the Roq blog is a good source): pom.xml — for Maven dependency structure config/application.properties — for Roq configuration patterns A sample Qute layout (for example, templates/layouts/default.html) A sample content page with front matter (for example, a .md or .adoc file from content/) If you are using an LLM, attach these files directly or paste them as code blocks. You can also point the LLM to https://iamroq.dev/llms-full.txt for complete Roq documentation. Create the project scaffold Before converting any templates, set up the project and validate that a single page renders. Create a new Roq project with the Roq CLI: roq create my-site This creates a project with the default theme (full blog layout, dark mode, sidebar, SEO support). If you want full control over the design and prefer to build your own layouts from scratch, use the base theme instead: roq create my-site -x theme:base The base theme provides a minimal HTML structure with just SEO, favicon, and Web Bundler. It is a better starting point if you plan to port your existing site’s design rather than adopt Roq’s default look. Add any plugins you need: roq add plugin:asciidoc # if using AsciiDoc content roq add plugin:sitemap roq add plugin:aliases # for URL redirects roq add plugin:tagging # if using tags LLM prompt for project setup I am migrating a static website from Jekyll to Roq (a Quarkus-based static site generator). I have already created the project with `roq create` and the base theme. Help me configure application.properties with: - site.url=https://mysite.example.com - site.collections.posts.layout=post - quarkus.qute.alt-expr-syntax=true - site.slugify-files=false - quarkus.default-locale=en Here is my Jekyll _config.yml for reference: [paste or attach _config.yml] Roq-specific configuration details Alternative expression syntax (recommended) Roq supports an alternative expression syntax where output expressions use {=expr} instead of {expr}. With alt syntax enabled, only {=...} and {#...} are interpreted as Qute expressions. Regular {...} is treated as plain text, so curly braces in code samples and JSON are safe without escaping. Add to your application.properties: quarkus.qute.alt-expr-syntax=true This will become the default syntax for Roq in a future version. All Qute examples in this guide use the alt syntax ({=expr} for output, {#...} for sections). Qute escaping (without alt syntax) If you choose not to enable the alternative expression syntax, Qute treats {...} as template expressions. Content with curly braces (Java code, JSON examples) must be escaped from Qute parsing. AsciiDoc files: Qute parsing is disabled by default (quarkus.asciidoc.qute=false). Curly braces in AsciiDoc content are safe without any extra configuration. To enable Qute parsing for a specific AsciiDoc file, add the :qute: attribute to the document header. Markdown and HTML files: Qute parsing is enabled by default. If your Markdown files contain curly braces in code samples, set site.escaped-pages in application.properties: site.escaped-pages=posts/** This wraps matched page content with Qute escape markers so curly braces are not parsed as template expressions. site.collections Defining any custom collection replaces Roq’s defaults. If you define site.collections.guides.layout=guide, you must also explicitly add site.collections.posts.layout=post — otherwise Roq’s default posts collection is silently dropped. quarkus.roq.data.dir Set this to _data to reuse Jekyll’s data directory in place, avoiding the need to move data files. site.slugify-files Set to false to preserve original filenames in URLs instead of slugifying them. Validate the scaffold (Phase A gate) Copy a single content page into content/ and start dev mode: roq start Verify: The page renders with your content (AsciiDoc or Markdown) Curly braces in code samples are treated as plain text (verify quarkus.qute.alt-expr-syntax=true is set) Content files without YAML front matter are recognized as collection members (Roq discovers content by directory, not by front matter presence) Files and directories starting with _ inside content/ (such as _includes/ or _attributes.adoc) are NOT rendered as standalone pages If you use AsciiDoc include:: directives, they resolve correctly AsciidoctorJ may enforce a security boundary (ROOTDIR) that prevents include:: directives from resolving paths outside the content directory. If includes fail with a security error, move the included files inside content/ or configure AsciidoctorJ’s safe mode. Do not proceed to templates until this gate passes. Convert layouts and templates Layouts are the highest-value conversion target. Jekyll uses Liquid templates; Hugo uses Go templates. Roq uses Qute, which has a different syntax but similar concepts. If you created your project with the default theme, it already provides main, page, and post layouts with a full blog design. You can override any theme layout by creating a file with the same name in templates/layouts/. If you used the base theme, you have a minimal HTML structure and will need to create your own layouts to match your existing site’s design. Key syntax differences (Jekyll Liquid to Qute) Concept Jekyll (Liquid) Roq (Qute) Variable output {{ page.title }} {=page.title} Conditional {% if page.image %}...{% endif %} {#if page.image}...{/if} Loop {% for post in site.posts %}...{% endfor %} {#for post in site.collections.get('posts')}...{/for} Include / partial {% include header.html %} {#include header.html /} Layout inheritance layout: default in front matter layout: default in front matter (same concept) Content insertion {{ content }} {#insert /} Date formatting {{ post.date | date: "%B %d, %Y" }} {=post.date.format('MMMM dd, yyyy')} Null / empty check {% if page.image %} {#if page.image} (Qute has different null semantics — test carefully) If you are migrating from Hugo rather than Jekyll, replace the Liquid syntax column with Go template equivalents ({{ .Title }}, {{ if .Params.image }}, {{ range .Pages }}, etc.) and include the Hugo equivalents in your prompt. The Qute (alt syntax) column stays the same. LLM prompt for layout conversion I am migrating a static website from Jekyll to Roq (a Quarkus-based static site generator that uses Qute templates). Convert the attached Jekyll Liquid layout to a Roq Qute template. Roq uses the alternative expression syntax: output expressions use {=expr} instead of {expr}. Section tags ({#if}, {#for}, {#include}) are unchanged. Key rules: - Replace Liquid {{ variable }} with Qute {=variable} syntax - Replace {% if %} with {#if } ... {/if} - Replace {% for item in collection %} with {#for item in collection} ... {/for} - Replace {{ content }} with {#insert /} - Replace {% include file.html %} with {#include file /} - Replace Liquid filters (| date, | upcase, etc.) with Qute method calls (e.g., {=post.date.format('yyyy, MMM dd')}) - Site-level variables use the `site` object (e.g., {=site.title}) - Page-level variables use the `page` object (e.g., {=page.title}) - Collections are accessed via site.collections.get('name') - Preserve all HTML structure and CSS classes unchanged - Qute null handling differs from Liquid: test conditionals carefully Here is my Jekyll layout: [paste or attach your layout file] Here is an example of a working Roq Qute layout for reference: [paste or attach a working Roq layout] Iterate on complex layouts Template porting is skilled translation work. Liquid and Qute differ in conditionals, loops, filters, null handling, and partial inclusion syntax. Budget extra time for complex templates like headers, footers, and sidebars. For layouts with many includes, navigation logic, or pagination: Convert the main layout first (usually default.html) Convert each include/partial file separately Convert pagination logic last (Roq pagination works differently from Jekyll) After each conversion, test in dev mode (roq start). Qute provides clear error messages with line numbers. Fix errors before moving to the next file. Convert content files Content files (blog posts, pages) usually need fewer changes than layouts. Content that works without changes Markdown files (.md) with YAML front matter work as-is in most cases AsciiDoc files (.adoc, .asciidoc) are supported natively by the quarkus-roq-plugin-asciidoc-jruby plugin AsciiDoc files without YAML front matter are recognized as collection members based on their directory — Roq discovers content by directory, not by front matter presence The layout field in front matter resolves automatically — writing layout: post is sufficient because Roq maps it to templates/layouts/post.html Front matter differences Field Jekyll Roq Layout layout: post layout: post (same — Roq resolves to templates/layouts/post.html) Date date: 2024-01-15 date: 2024-01-15 (same) Permalink permalink: /about/ Use file path or redirect_from for old URLs Categories categories: [blog, tech] Use tags or directory-based collections Excerpt excerpt: "…​" description: "…​" LLM prompt for content migration I am migrating content files from Jekyll to Roq. Convert the YAML front matter in these files to Roq format: - Keep `layout`, `title`, `date`, and `author` fields unchanged - Rename `excerpt` to `description` - Convert `permalink: /path/` to `redirect_from: [/path/]` (Roq uses the file path as the URL by default; redirect_from handles old URLs) - Remove `categories` (use directory structure or `tags` instead) - Keep the body content unchanged (Markdown and AsciiDoc work as-is) Here are my content files: [paste or attach files] For bulk content migration, write a script (or ask an LLM to generate one) that processes all files in a directory rather than converting files one at a time. Moving content directories When moving content from Jekyll directories to Roq’s content/ directory, use git mv instead of cp to preserve file history: git mv _posts content/posts If the target directory already exists, git mv moves the source directory into it (for example, content/posts/_posts/). Remove the target first if it was created during earlier testing. Migrate static assets JavaScript Copy JavaScript files to the public/ directory: mkdir -p public/js cp path/to/your/javascript/*.js public/js/ Add <script> tags to your root layout, or place JS/CSS sources in web/ to use the built-in Web Bundler (see Styles and Javascript). SCSS/CSS Jekyll processes Sass files automatically and supports Jekyll-specific features in SCSS entry points. With Roq, the quarkus-web-bundler extension (a transitive dependency of quarkus-roq) handles CSS/SCSS bundling. Watch for these Jekyll-specific patterns in your SCSS entry point: Jekyll front matter (a pair of --- lines at the top of .scss files) — remove it Liquid variables (for example, $baseurl: "{{ site.baseurl }}") — replace with hardcoded values @import paths may need updating for the new directory structure Place SCSS files in the web/ directory. The import hierarchy (@import partials referencing other partials) must be ported as a complete tree — do not split individual files. Images and other static files Copy images and other static files (fonts, favicons, etc.) to the public/ directory. Jekyll typically serves these from assets/ or directory-relative paths. In Roq, files in public/ are served at the site root. cp -r assets/images public/images Update any hardcoded image paths in templates and content to match the new location. Data files Set quarkus.roq.data.dir=_data in application.properties to reuse Jekyll’s data directory in place. Each YAML or JSON file in _data/ automatically registers as a named CDI bean accessible in Qute templates. For example, _data/books.yaml becomes accessible as {=cdi:books}, and nested fields via dot notation such as {=cdi:books.items}. The file format (YAML/JSON) stays the same. Only the access syntax in templates changes: replace Jekyll’s {{ site.data.books.items }} with Roq’s {=cdi:books.items}. Convert site configuration Site configuration does not map cleanly between generators. Key configuration mappings Jekyll (_config.yml) Roq (application.properties) title: My Site Set in a data file or template partial url / baseurl site.url=https://mysite.example.com permalink: /:categories/:title/ Roq uses file-path-based URLs by default plugins: [jekyll-sitemap] roq add plugin:sitemap collections: site.collections.<name>.layout=<layout> exclude: [vendor, node_modules] site.ignored-files=vendor/**,node_modules/** LLM prompt for configuration I am migrating from Jekyll to Roq. Convert my Jekyll _config.yml to Roq application.properties format. Key mappings: - url / baseurl → site.url in application.properties - permalink → Roq uses file-path-based URLs by default - plugins → Roq uses plugins (install with `roq add plugin:<name>`) - collections → site.collections.<name>.layout in application.properties - exclude → site.ignored-files Important Roq behaviors: - Defining ANY custom collection replaces ALL defaults (including posts) so always explicitly define site.collections.posts if you have blog posts - Set quarkus.roq.data.dir=_data to reuse Jekyll's data directory - Set quarkus.qute.alt-expr-syntax=true (uses {=expr} for output, plain {..} is not parsed) Here is my Jekyll _config.yml: [paste or attach _config.yml] Scale to all content (Phase B gate) After the foundation works with a single page, move all remaining content directories using git mv (as described in Convert content files) and measure performance: roq start -Djvm.args="-Xmx4g" Check: Build time: under 5 minutes is good, under 10 is acceptable, over 10 minutes is a signal to investigate Memory: should stay under 4 GB for most sites Spot-check 5-10 pages across different content types: verify includes, code samples, images, and cross-references If the build is too slow, try reducing the content to a subset during development (move extra files to a temporary directory outside content/) and build the full site only for production validation. Handle redirects Jekyll sites often have permalink-based URLs that differ from Roq’s file-path-based URLs. Use the quarkus-roq-plugin-aliases extension to set up redirects from old URLs to new ones. The plugin recognizes three equivalent front matter keys: redirect_from, redirect-from, and aliases. --- title: My Page redirect_from: - /old/permalink/path/ - /another/old/path/ --- For bulk redirect migration, write a script that reads your Jekyll _config.yml permalink patterns and generates redirect_from front matter for each content file. Tips General Commit after each step Frequent commits let you roll back if a conversion introduces issues. Test in dev mode after each change Run roq start after each conversion. Qute provides clear error messages with line numbers. When using an LLM Ask for explanations When the LLM converts a template, ask it to explain each change. This helps you learn Qute syntax and catch incorrect conversions. Provide error messages Paste the full error message from roq start back to the LLM. Qute error messages are specific enough for the LLM to diagnose. Batch similar files Group similar templates or content files and convert them together. The LLM produces more consistent output when it can see patterns across files. Start new conversations for each phase After 2-3 steps of implementation, build output and file contents fill the LLM context window. A fresh conversation with "Continue from Phase B" works better than pushing through a long session. Known limitations Liquid filters: Jekyll’s Liquid filters (for example, | date: "%B %d, %Y") do not have direct Qute equivalents. Qute uses method calls instead (for example, {=post.date.format('MMMM dd, yyyy')}). The LLM usually handles common filters, but verify date formats and edge cases. Jekyll plugins: Jekyll plugins cannot be reused in Roq, but Roq has its own plugin ecosystem covering common needs (sitemap, tagging, aliases, RSS, etc.). Check the Plugins & Themes directory and install with roq add plugin:<name>. SCSS processing: Jekyll processes Sass files automatically and supports Jekyll-specific front matter and Liquid variables in SCSS. With Roq, the web-bundler handles SCSS, but you must remove Jekyll-specific syntax from entry points and update import paths. The LLM can help with the syntax changes but not with debugging build tool differences. Data file access: Jekyll’s _data/ files are accessed as {{ site.data.filename.key }} in Liquid. In Roq, data files register as CDI beans and are accessed as {=cdi:filename.key} in Qute templates. The file format (YAML/JSON) stays the same. Build error strictness: Jekyll silently ignores missing includes. Roq and AsciidoctorJ may fail the entire build on a single broken include:: directive. Validate includes early when scaling to all content. AsciidoctorJ security boundaries: AsciidoctorJ may block include:: directives that resolve to paths outside the content directory. If you see security errors, move included files inside content/ or adjust the safe mode configuration. RSS feeds: Jekyll’s feed.xml uses Liquid syntax. Roq has built-in RSS support (see RSS). Create a content/rss.xml with {#include fm/rss.html} and add {#rss site /} to your layout’s <head>. Clean up Jekyll artifacts After migration is complete and validated, remove Jekyll-specific files: _config.yml (and any variant config files) _layouts/, _includes/ (top-level Jekyll directories) _sass/ (if ported to web/) _plugins/ Gemfile, Gemfile.lock, .bundle/, .ruby-version Any Jekyll serve scripts Keep files that Roq still uses: _data/ (if quarkus.roq.data.dir=_data is configured) What’s next Read the Roq the basics for the full feature set Browse the Plugins & Themes directory Check the Roq blog source for a complete working example If you develop migration scripts or improved prompts, consider contributing them back to the project (see issue #780) ### [Publishing a Roq Site](/docs/publishing/) If you find any issue or missing info, be awesome and edit this document to help others Roqers. Generating your Roq site This command: roq generate 🚀 The site will be generated in target/roq, use roq serve to serve it. Without the Roq CLI using Maven: QUARKUS_ROQ_GENERATOR_BATCH=true ./mvnw -B package quarkus:run Roq GitHub Action Roq provides a GitHub action to publish to GitHub pages or other services. To GitHub Pages Check if you already have the .github/workflows/deploy.yml file, if you don’t create it: .github/workflows/deploy.yml ## Deploy to GH-Pages for your Quarkus Roq site. name: Roq Site Deploy on: push: branches: [ main ] # Switch to the branch which should be deployed to GitHub Pages schedule: - cron: "0 5 * * *" # Runs every day at 05:00 UTC (10:30 AM IST) workflow_dispatch: jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - name: Generate Roq Site uses: quarkiverse/quarkus-roq@v1.1 with: github-token: ${{ secrets.GITHUB_TOKEN }} # Used to automatically get the GitHub Pages url deploy: environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} permissions: pages: write # to deploy to Pages id-token: write # to verify the deployment originates from an appropriate source runs-on: ubuntu-latest needs: build steps: - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v5 Then to enable GitHub Pages: Open your GitHub repository page Go to Settings→Page Pick: Source: GitHub Actions, that’s enough (no save button) It’s all good, restart your deploy workflow and enjoy! To other services .github/workflows/deploy-other.yml ## Deploy to another service for your Quarkus Roq site. name: Roq Site Deploy other on: push: branches: [ main ] # Switch to the branch which should be deployed to GitHub Pages workflow_dispatch: jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Generate Roq Site uses: quarkiverse/quarkus-roq@v1.1 with: github-pages: false - name: Publishing blog uses: actions/upload-artifact@v4 with: name: site path: target/roq retention-days: 3 This will create a GitHub artifact named site that you can download from another job (or another workflow). For example, the PR Preview workflow of Roq publishes to Surge. Gitlab CI Add this file at the root of your Gitlab repository .gitlab-ci.yml stages: - build - deploy build_roq: # Look for appropriate maven docker images in https://hub.docker.com/_/maven/tags image: "maven:3.9.9-eclipse-temurin-23-alpine" stage: build # Generate the static site on merge request events and on the main branch script: - QUARKUS_ROQ_GENERATOR_BATCH=true mvn -B -q package quarkus:run rules: - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' artifacts: reports: junit: target/surefire-reports/*.xml paths: - target/roq - target/surefire-reports deploy_roq: image: alpine pages: true stage: deploy # For main branch take the artifacts from `build_roq` and deploy them. needs: - build_roq script: - cp -R target/roq public - echo "Quarkus Roq static site deployed to Gitlab Pages at $CI_PAGES_URL" rules: - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' artifacts: paths: - public If everything goes well the pipeline will deploy, the url of the deployment is found via these options: Console output of deploy_roq job. Clicking Deploy ⇒ Pages on the project sidebar Navigating to the url https://gitlab.example.com/user-or-organization/projectpath/project/pages Other CIs Using the command above should be easy to configure on any CI. if you created a configuration for a given CI which could help others, please share it here (or create an issue) 🙏 ### [Roq Release Notes](/docs/releases/) If you find any issue or missing info, be awesome and edit this document to help others Roqers. We aim to keep the API stable, but since Roq is still young, breaking changes may happen. The good news: almost all breakage will be caught during build or site generation tests. Want to follow Roq’s progress and update your project safely? You’re in the right place. Roq 2.1 ✏️ Built-in Block Editor: a powerful block editor right in your browser during dev mode, with live preview, front matter editing, and image management. No IDE needed to write your next post! 🎨 Simplified layout resolution: layout: page now resolves local layouts first, then falls back to the theme. No more :theme/ prefix needed 🎨 Default theme rewritten with TailwindCSS instead of SCSS for better maintainability and modern styling 🌙 Built-in dark mode with automatic system preference detection 🎨 Color palette system: three customizable palettes (accent, pop, neutral) via CSS variables 🖼️ Favicon auto-discovery: automatically detects favicon.svg, favicon.ico, favicon.png, apple-touch-icon.png in public/ 🛒 Marketplace: new collection type for discovering plugins, themes, and web extensions 🤖 llms.txt: automatic generation of llms.txt for AI-friendly site indexing 🖥️ CLI: new roq add command for adding plugins, themes, and web extensions 🔧 Internal refactoring: the frontmatter pipeline is now split into clear numbered steps (Step0-Step6) for better maintainability 🔧 Migrated to ProjectScanner API and StringPaths library for file scanning ⚠️ page.date now returns null for normal pages without a date. Collection documents (posts) still default to the current date. 🔧 Common RoqException: all Roq exceptions (roq-frontmatter, roq-data) now extend a common io.quarkiverse.roq.exception.RoqException in roq-common, providing structured error pages with title, detail, hint, and source info across all modules → Guide for migrating to 2.1 Roq 2.0 🕵️ Added lightning filesystem watcher for live reload 📂 Allow web directory at the root of the Roq site 🧩 Simplified default app structure: supports web/app.js and web/app.scss (or web/app/app.js like before …​) ⚡️ TailwindCSS support without any config 💫 Directory support for data and allow iterating on nested data files using the directory name → Guide for migrating to 2.0 Roq 1.8 Sorry for the breaking releases back to back but this includes a refactor to allow safely including files from the whole site directory when using AsciidocJ (requested by a user). This mostly change internal api, but can eventually break really specific usage. → Guide for migrating to 1.8 Roq 1.7 The Asciidoc support was already available, but with this new release, we made it a Roq top level citizen: Support for Asciidoc headers to control the Roq data Includes Roq page and site attributes (urls, …​) xref are working out of the box for structured content such as docs Fine grained Asciidoc attributes (config, layout, page) Harmonization between Ruby and Java implementation Dynamic TOC support 👉 The Roq Asciidoc plugin doc A few weeks ago, we added support for search as a plugin to Roq. I wasn’t fully happy with the style and the fact that it was targetting the full page instead of the nearest fragment for the actual keyword. I spend a bit of time on this and came up with a new way of indexing the content which slice the content based on fragments. Currently, it supports both Asciidoc and Markdown output. Give it a try, it is enabled on this site. If you want this for your site: 👉 The Roq Search plugin doc → Guide for migrating to 1.7 Migration guide Make sure you update using Quarkus CLI Make sure you implemented generation tests to 2.1 Applies to Before After Action All users io.quarkiverse.roq.util.PathUtils io.quarkiverse.tools.stringpaths.StringPaths ⚠ Update import and adapt usage All users layout: :theme/foo layout: foo or theme-layout: foo ⚠ Update layout references in front matter All users Layouts in templates/layouts/{theme-name}/ templates/layouts/ ⚠ Move custom layouts All users page.date returns current date for all pages Returns null for non-collection pages ⚠ Wrap with {#if page.date}…​{/if} Theme users index.html layout for blog listing New blog.html layout (with pagination support); index.html now delegates to it ⚠ If using layout: blog without pagination (paginate: false), add paginate: posts; if you have a custom blog.html override, use {#if page.paginator and page.paginator.isFirst} instead of {#if page.paginator.isFirst} Theme users SCSS (app.scss) CSS with TailwindCSS ⚠ Migrate custom styles Theme users {#author-card …​} (kebab-case) {#roq/authorCard …​} (namespaced camelCase) ⚠ Update tag references Plugin authors RoqFrontMatterScanProcessor Step-based build items ⚠ Update plugin code Plugin authors deployment.scan.\* deployment.items.scan.* (same for data, publish) ⚠ Update imports All users Generated templates in target/roq-templates/full/ Now in target/roq-templates/content/ (#861) ℹ Update scripts or workflows referencing this path Plugin authors TemplateSource.generatedQuteContentTemplateId(), RoqFrontMatterRawPageBuildItem.generatedContentTemplate() Removed, page content now extracted via {#fragment RoqPageContent} (#861) ⚠ Update plugin code Plugin authors PAGINATE_KEY in scan package RoqFrontMatterKeys (runtime) ⚠ Update imports Plugin authors io.quarkiverse.roq.frontmatter.runtime.exception.RoqException io.quarkiverse.roq.exception.RoqException (in roq-common) ⚠ Update imports Plugin authors RoqException.Builder.source(TemplateSource) RoqException.Builder.sourceInfo(new RoqSourceInfo(…​)) ⚠ Update builder calls Plugin authors roq-data exceptions extend RuntimeException / UncheckedIOException All extend RoqException with builder pattern ⚠ Update exception construction Theme users Custom SCSS/CSS overrides Theme CSS completely rewritten ⚠ Review your overrides — some may be unnecessary now, others may need updating Theme users Custom color variables Three customizable color palettes: accent, pop, neutral ℹ See Color Palettes Theme users Custom CSS classes for prose not-prose class to exclude from prose styling ℹ Good to know Theme users No dark mode Built-in with dark: variants ℹ Good to know to 2.0 Applies to Before After Action All users Nested data mapped with _ (e.g. dir_bar) Mapped with / (e.g. dir/bar) ⚠ Update data references to 1.8 Applies to Before After Action All users page.info() page.source() and page.source().template() ⚠ Update page info calls Plugin authors Previous BuildItems API Restructured BuildItems ⚠ Update plugin code to 1.7 Applies to Before After Action AsciiDoc users Qute parsing enabled by default Disabled by default ⚠ Use :qute: per page or quarkus.asciidoc.qute=false AsciiDoc users quarkus.asciidoctorj quarkus.asciidoc ⚠ Rename config AsciiDoc users quarkus.asciidoctorj.templates-dir Removed (TOC handled by script) ⚠ Remove config Search users Previous search result DOM Updated DOM structure ⚠ Verify custom search styles All users site.ignored-files replaces defaults Now extends site.default-ignored-files ⚠ Check ignore config ### [Roq Events](/events/) 26 May 2026 Static You Can Maintain. Static With a Live CMS… What Is the Missing Roq? Andy Damevin talks about Quarkus Roq at JNation 2026. 09 Jul 2025 Roq: Create your static site with superpowers – fun, powerful, and easy to maintain! Andy Damevin talks about Quarkus Roq at Riviera Dev. 09 Dec 2025 Roq 2.0 Roq 2.0 release featuring enhanced capabilities and improved developer experience. Learn more 11 Nov 2024 Quarkus Insight - What is Quarkus Roq? Andy Damevin, Matheus Cruz and Melloware join us to discuss Quarkus Roq, which includes tooling for generating a static website with Quarkus. Learn more 31 Oct 2024 Roq 1.0 Roq 1.0 is out, it's time to Roq with blogs! Learn more 20 Oct 2024 Roq 1.0 Beta You can start building your site or blog with Roq. More features will come to cover all the needs you can expect from an awesome SSG! ### [Plugins & Themes](/marketplace/) ### [AsciiDoc Markup Example](/markups/asciidoc/) This is a paragraph under heading 1. It contains strong text, emphasized text, and inline code. Heading 2 This is a paragraph under heading 2 with a link to example.com. Heading 3 This is a paragraph under heading 3. Heading 4 This is a paragraph under heading 4. Heading 5 This is a paragraph under heading 5. Heading 6 This is a paragraph under heading 6. Blockquotes This is a blockquote. It can span multiple lines. It can even have multiple paragraphs. Strong and Emphasis This paragraph contains bold/strong text, italic/emphasized text, and bold and italic text. Code Inline Code This paragraph contains inline code within regular text. Code Blocks public class HelloWorld { public static void main(String[] args) { System.out.println("Hello, World!"); } } function greet(name) { return `Hello, ${name}!`; } console.log(greet("World")); Code Blocks with Callouts public class Example { public static void main(String[] args) { String message = "Hello"; (1) System.out.println(message); (2) } } 1 Initialize the message variable 2 Print the message to console server: port: 8080 (1) host: localhost (2) database: url: jdbc:postgresql://localhost:5432/mydb (3) username: admin 1 Configure server port 2 Set server host 3 Database connection URL Lists Unordered Lists First item Second item Third item Nested item 1 Nested item 2 Fourth item Ordered Lists First step Second step Third step Nested step 1 Nested step 2 Fourth step Tables Header 1 Header 2 Header 3 Cell 1 Cell 2 Cell 3 Cell 4 Cell 5 Cell 6 Cell 7 Cell 8 Cell 9 Cell 10 Cell 11 Cell 12 Table with Alignment Left Aligned Center Aligned Right Aligned Left Center Right Left Center Right Horizontal Rule Paragraphs This is a standard paragraph with regular text. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. This is another paragraph. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Links Here is an inline link and here is a link with title. Mixed Content Here’s a paragraph with strong text, emphasized text, inline code, and a link all in one. And here’s a blockquote that contains strong text, emphasized text, and inline code as well. Complex List First item with strong text Second item with inline code Third item with a link Nested unordered item Another nested item with emphasis Fourth item Code in Other Elements Code in Headings: inline-code This heading has inline code: variable = value Code in Lists Item with inline code Item with code block: def hello(): print("Hello") Code in Blockquotes This blockquote contains inline code and shows how code is styled within quotes. AsciiDoc-Specific Elements Admonition Blocks This is a note admonition. It provides additional information. This is a tip admonition. It offers helpful advice. This is an important admonition. Pay attention to this. This is a warning admonition. Be careful about this. This is a caution admonition. Proceed with care. Verse Block This is a verse block. It preserves line breaks and formatting exactly as written. — Attribution Source Example Block This is an example block. It’s used to highlight examples or demonstrate concepts. It can contain multiple paragraphs and other elements. Sidebar Block Optional Title This is a sidebar. It contains supplementary information that’s related but not essential to the main content. Definition Lists Term 1 Definition 1 Term 2 Definition 2 Term 3 Definition 3 With additional paragraph. Checklist Checked item Another checked item Unchecked item Another unchecked item Special List Styles Circle item 1 Circle item 2 Square item 1 Square item 2 Code Listing with Language fn main() { println!("Hello, world!"); } Complex Table Name Description Status Feature 1 This is a longer description of feature 1 ✓ Feature 2 This is a longer description of feature 2 ✓ Feature 3 This is a longer description of feature 3 Pending ### [Markdown Markup Test](/markups/markdown/) Heading 1 This is a paragraph under heading 1. It contains strong text, emphasized text, and inline code. Heading 2 This is a paragraph under heading 2 with a link to example.com. Heading 3 This is a paragraph under heading 3. Heading 4 This is a paragraph under heading 4. Heading 5 This is a paragraph under heading 5. Heading 6 This is a paragraph under heading 6. Blockquotes This is a blockquote. It can span multiple lines. It can even have multiple paragraphs. Strong and Emphasis This paragraph contains bold/strong text, italic/emphasized text, and bold and italic text. Code Inline Code This paragraph contains inline code within regular text. Code Blocks public class HelloWorld { public static void main(String[] args) { System.out.println("Hello, World!"); } } function greet(name) { return `Hello, ${name}!`; } console.log(greet("World")); Lists Unordered Lists First item Second item Third item Nested item 1 Nested item 2 Fourth item Ordered Lists First step Second step Third step Nested step 1 Nested step 2 Fourth step Tables Header 1 Header 2 Header 3 Cell 1 Cell 2 Cell 3 Cell 4 Cell 5 Cell 6 Cell 7 Cell 8 Cell 9 Cell 10 Cell 11 Cell 12 Table with Alignment Left Aligned Center Aligned Right Aligned Left Center Right Left Center Right Horizontal Rule Paragraphs This is a standard paragraph with regular text. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. This is another paragraph. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Links Here is an inline link and here is a link with title. Mixed Content Here's a paragraph with strong text, emphasized text, inline code, and a link all in one. And here's a blockquote that contains strong text, emphasized text, and inline code as well. Complex List First item with strong text Second item with inline code Third item with a link Nested unordered item Another nested item with emphasis Fourth item Code in Other Elements Code in Headings: inline-code This heading has inline code: variable = value Code in Lists Item with inline code Item with code block: def hello(): print("Hello") Code in Blockquotes This blockquote contains inline code and shows how code is styled within quotes. ### [🎸 The Roqers Hall Of Fame](/roqers/) const giscusScript = document.createElement('script'); giscusScript.src = 'https://giscus.app/client.js'; giscusScript.setAttribute('data-repo', 'quarkiverse/quarkus-roq'); giscusScript.setAttribute('data-repo-id', 'R_kgDOL4WdMA'); giscusScript.setAttribute('data-category', 'Comments'); giscusScript.setAttribute('data-category-id', 'DIC_kwDOL4WdMM4CjtXB'); giscusScript.setAttribute('data-mapping', 'pathname'); giscusScript.setAttribute('data-strict', '0'); giscusScript.setAttribute('data-reactions-enabled', '1'); giscusScript.setAttribute('data-emit-metadata', '0'); giscusScript.setAttribute('data-input-position', 'bottom'); giscusScript.setAttribute('data-theme', document.documentElement.classList.contains('dark') ? 'dark' : 'light'); giscusScript.setAttribute('data-lang', 'en'); giscusScript.setAttribute('crossorigin', 'anonymous'); giscusScript.async = true; document.currentScript.parentNode.insertBefore(giscusScript, document.currentScript.nextSibling); ### [Blog](/blog/) ### [Blog](/posts/page2/) ### [Blog](/posts/page3/) ### [#blogging](/posts/tag/blogging/) const savedTheme = localStorage.getItem('darkMode'); const isDark = savedTheme !== null ? savedTheme === 'true' : window.matchMedia('(prefers-color-scheme: dark)').matches; if (isDark) { document.documentElement.classList.add('dark'); } #blogging - Hello, world! I'm Roq — a funny little SSG (Static Site Generator) with a Java soul and Quarkus energy — Open Source and Free. setupSearch({ url: '/search-index.json' }); I am ROQ Java Static Site Generator 2.1.1 Search... ⌘K MENU Home Blog Events About Roqers Doc Getting Started Roq the Basics Plugins & Themes Publishing Advanced stuff Migrating LLMs.txt Social 2026 © ROQ × blogging How AI Helped Me Rebuild My Blog and Move from Jekyll to Quarkus Roq A comprehensive journey of rebuilding a personal blog with the help of AI, moving from Jekyll to Quarkus Roq, exploring GitHub Issues Driven Development, and discovering how modern AI tools can transform the way we build and maintain websites. 2026, May 06 — 26 minute(s) read Roq 2.0 and Java Advent Calendar article An introduction to Roq 2.0, a Quarkus-inspired approach to static site generation in Java. Learn about its new foundation, plugin support, and live-reload feature through a practical tutorial. 2025, Dec 09 — 2 minute(s) read Major site migrations to Roq ✨ Two prominent websites have just migrated to Roq—any guesses who they might be? 2025, Aug 26 — 2 minute(s) read Comparing Roq with Hugo, Jekyll, and JBake: A Feature Breakdown Here’s a feature comparison with some popular SSGs to highlight how Roq stacks up. Feature Roq Hugo Jekyll JBake Performance Fast, and leveraging Quarkus dev-mode Extremely fast (written in Go) Slower due to Ruby and plugins Slower, runs on Java with Freemarker/Groovy templates Templating Qute (simple & readable) Go templates (powerful but complex) Liquid (easy but limited) Freemarker, Groovy, or Thymeleaf Extensibility Leverages Quarkus extensions Limited plugin system Large plugin ecosystem Limited... 2025, Feb 27 — 2 minute(s) read Roq with Blogs 🚀 Roq 1.0 is ON! It is time to give it a shot and give us feedback 🚀 2024, Oct 31 — 2 minute(s) read Page 1 of 2 ### [#blogging](/posts/tag/blogging/page2/) const savedTheme = localStorage.getItem('darkMode'); const isDark = savedTheme !== null ? savedTheme === 'true' : window.matchMedia('(prefers-color-scheme: dark)').matches; if (isDark) { document.documentElement.classList.add('dark'); } #blogging - Hello, world! I'm Roq — a funny little SSG (Static Site Generator) with a Java soul and Quarkus energy — Open Source and Free. setupSearch({ url: '/search-index.json' }); I am ROQ Java Static Site Generator 2.1.1 Search... ⌘K MENU Home Blog Events About Roqers Doc Getting Started Roq the Basics Plugins & Themes Publishing Advanced stuff Migrating LLMs.txt Social 2026 © ROQ × blogging Welcome to Roq! This is the first article ever made with Quarkus Roq 2024, Aug 29 — 2 minute(s) read Page 2 of 2 ### [#github-copilot](/posts/tag/github-copilot/) const savedTheme = localStorage.getItem('darkMode'); const isDark = savedTheme !== null ? savedTheme === 'true' : window.matchMedia('(prefers-color-scheme: dark)').matches; if (isDark) { document.documentElement.classList.add('dark'); } #github-copilot - Hello, world! I'm Roq — a funny little SSG (Static Site Generator) with a Java soul and Quarkus energy — Open Source and Free. setupSearch({ url: '/search-index.json' }); I am ROQ Java Static Site Generator 2.1.1 Search... ⌘K MENU Home Blog Events About Roqers Doc Getting Started Roq the Basics Plugins & Themes Publishing Advanced stuff Migrating LLMs.txt Social 2026 © ROQ × github-copilot How AI Helped Me Rebuild My Blog and Move from Jekyll to Quarkus Roq A comprehensive journey of rebuilding a personal blog with the help of AI, moving from Jekyll to Quarkus Roq, exploring GitHub Issues Driven Development, and discovering how modern AI tools can transform the way we build and maintain websites. 2026, May 06 — 26 minute(s) read ### [#release](/posts/tag/release/) const savedTheme = localStorage.getItem('darkMode'); const isDark = savedTheme !== null ? savedTheme === 'true' : window.matchMedia('(prefers-color-scheme: dark)').matches; if (isDark) { document.documentElement.classList.add('dark'); } #release - Hello, world! I'm Roq — a funny little SSG (Static Site Generator) with a Java soul and Quarkus energy — Open Source and Free. setupSearch({ url: '/search-index.json' }); I am ROQ Java Static Site Generator 2.1.1 Search... ⌘K MENU Home Blog Events About Roqers Doc Getting Started Roq the Basics Plugins & Themes Publishing Advanced stuff Migrating LLMs.txt Social 2026 © ROQ × release Roq 2.1 is here! Roq 2.1 brings a standalone CLI, LLMs.txt generation, dynamic pages from data, custom error pages, and much more. This post kicks off a series covering all the new features. 2026, May 01 — 2 minute(s) read ### [#happy-users](/posts/tag/happy-users/) const savedTheme = localStorage.getItem('darkMode'); const isDark = savedTheme !== null ? savedTheme === 'true' : window.matchMedia('(prefers-color-scheme: dark)').matches; if (isDark) { document.documentElement.classList.add('dark'); } #happy-users - Hello, world! I'm Roq — a funny little SSG (Static Site Generator) with a Java soul and Quarkus energy — Open Source and Free. setupSearch({ url: '/search-index.json' }); I am ROQ Java Static Site Generator 2.1.1 Search... ⌘K MENU Home Blog Events About Roqers Doc Getting Started Roq the Basics Plugins & Themes Publishing Advanced stuff Migrating LLMs.txt Social 2026 © ROQ × happy-users Already some happy users 🧑‍💻 This is a good start, we already have a few happy users! 2024, Dec 10 — 2 minute(s) read ### [#ai](/posts/tag/ai/) const savedTheme = localStorage.getItem('darkMode'); const isDark = savedTheme !== null ? savedTheme === 'true' : window.matchMedia('(prefers-color-scheme: dark)').matches; if (isDark) { document.documentElement.classList.add('dark'); } #ai - Hello, world! I'm Roq — a funny little SSG (Static Site Generator) with a Java soul and Quarkus energy — Open Source and Free. setupSearch({ url: '/search-index.json' }); I am ROQ Java Static Site Generator 2.1.1 Search... ⌘K MENU Home Blog Events About Roqers Doc Getting Started Roq the Basics Plugins & Themes Publishing Advanced stuff Migrating LLMs.txt Social 2026 © ROQ × ai How AI Helped Me Rebuild My Blog and Move from Jekyll to Quarkus Roq A comprehensive journey of rebuilding a personal blog with the help of AI, moving from Jekyll to Quarkus Roq, exploring GitHub Issues Driven Development, and discovering how modern AI tools can transform the way we build and maintain websites. 2026, May 06 — 26 minute(s) read ### [#jekyll](/posts/tag/jekyll/) const savedTheme = localStorage.getItem('darkMode'); const isDark = savedTheme !== null ? savedTheme === 'true' : window.matchMedia('(prefers-color-scheme: dark)').matches; if (isDark) { document.documentElement.classList.add('dark'); } #jekyll - Hello, world! I'm Roq — a funny little SSG (Static Site Generator) with a Java soul and Quarkus energy — Open Source and Free. setupSearch({ url: '/search-index.json' }); I am ROQ Java Static Site Generator 2.1.1 Search... ⌘K MENU Home Blog Events About Roqers Doc Getting Started Roq the Basics Plugins & Themes Publishing Advanced stuff Migrating LLMs.txt Social 2026 © ROQ × jekyll How AI Helped Me Rebuild My Blog and Move from Jekyll to Quarkus Roq A comprehensive journey of rebuilding a personal blog with the help of AI, moving from Jekyll to Quarkus Roq, exploring GitHub Issues Driven Development, and discovering how modern AI tools can transform the way we build and maintain websites. 2026, May 06 — 26 minute(s) read ### [#frontmatter](/posts/tag/frontmatter/) const savedTheme = localStorage.getItem('darkMode'); const isDark = savedTheme !== null ? savedTheme === 'true' : window.matchMedia('(prefers-color-scheme: dark)').matches; if (isDark) { document.documentElement.classList.add('dark'); } #frontmatter - Hello, world! I'm Roq — a funny little SSG (Static Site Generator) with a Java soul and Quarkus energy — Open Source and Free. setupSearch({ url: '/search-index.json' }); I am ROQ Java Static Site Generator 2.1.1 Search... ⌘K MENU Home Blog Events About Roqers Doc Getting Started Roq the Basics Plugins & Themes Publishing Advanced stuff Migrating LLMs.txt Social 2026 © ROQ × frontmatter Easily Generate a `sitemap.xml` for Your Site with Roq Learn how to quickly set up and customize a sitemap.xml for your site using the Roq plugin. 2025, Jan 08 — 1 minute(s) read ### [#gfm](/posts/tag/gfm/) const savedTheme = localStorage.getItem('darkMode'); const isDark = savedTheme !== null ? savedTheme === 'true' : window.matchMedia('(prefers-color-scheme: dark)').matches; if (isDark) { document.documentElement.classList.add('dark'); } #gfm - Hello, world! I'm Roq — a funny little SSG (Static Site Generator) with a Java soul and Quarkus energy — Open Source and Free. setupSearch({ url: '/search-index.json' }); I am ROQ Java Static Site Generator 2.1.1 Search... ⌘K MENU Home Blog Events About Roqers Doc Getting Started Roq the Basics Plugins & Themes Publishing Advanced stuff Migrating LLMs.txt Social 2026 © ROQ × gfm GFM Alert Blocks: Styled Callouts in Your Markdown Roq supports GitHub Flavored Markdown alert blocks with icons and themed colors. Learn how to use NOTE, TIP, IMPORTANT, WARNING, and CAUTION blocks, and how to add custom alert types. 2026, May 04 — 4 minute(s) read ### [#features](/posts/tag/features/) const savedTheme = localStorage.getItem('darkMode'); const isDark = savedTheme !== null ? savedTheme === 'true' : window.matchMedia('(prefers-color-scheme: dark)').matches; if (isDark) { document.documentElement.classList.add('dark'); } #features - Hello, world! I'm Roq — a funny little SSG (Static Site Generator) with a Java soul and Quarkus energy — Open Source and Free. setupSearch({ url: '/search-index.json' }); I am ROQ Java Static Site Generator 2.1.1 Search... ⌘K MENU Home Blog Events About Roqers Doc Getting Started Roq the Basics Plugins & Themes Publishing Advanced stuff Migrating LLMs.txt Social 2026 © ROQ × features GFM Alert Blocks: Styled Callouts in Your Markdown Roq supports GitHub Flavored Markdown alert blocks with icons and themed colors. Learn how to use NOTE, TIP, IMPORTANT, WARNING, and CAUTION blocks, and how to add custom alert types. 2026, May 04 — 4 minute(s) read ### [#styling](/posts/tag/styling/) const savedTheme = localStorage.getItem('darkMode'); const isDark = savedTheme !== null ? savedTheme === 'true' : window.matchMedia('(prefers-color-scheme: dark)').matches; if (isDark) { document.documentElement.classList.add('dark'); } #styling - Hello, world! I'm Roq — a funny little SSG (Static Site Generator) with a Java soul and Quarkus energy — Open Source and Free. setupSearch({ url: '/search-index.json' }); I am ROQ Java Static Site Generator 2.1.1 Search... ⌘K MENU Home Blog Events About Roqers Doc Getting Started Roq the Basics Plugins & Themes Publishing Advanced stuff Migrating LLMs.txt Social 2026 © ROQ × styling How to add syntax highlighting to your Roq site Learn how to integrate syntax highlighting into your Roq site using Highlight.js and the Quarkus web-bundler extension. This guide walks you through the simple steps to add it via the pom.xml, JavaScript, and SCSS files. 2024, Sep 20 — 2 minute(s) read ### [#plugin](/posts/tag/plugin/) const savedTheme = localStorage.getItem('darkMode'); const isDark = savedTheme !== null ? savedTheme === 'true' : window.matchMedia('(prefers-color-scheme: dark)').matches; if (isDark) { document.documentElement.classList.add('dark'); } #plugin - Hello, world! I'm Roq — a funny little SSG (Static Site Generator) with a Java soul and Quarkus energy — Open Source and Free. setupSearch({ url: '/search-index.json' }); I am ROQ Java Static Site Generator 2.1.1 Search... ⌘K MENU Home Blog Events About Roqers Doc Getting Started Roq the Basics Plugins & Themes Publishing Advanced stuff Migrating LLMs.txt Social 2026 © ROQ × plugin More diagram than you could have dreamed of. Leveraging Kroki.io to generate diagram from text 2025, Jun 11 — 1 minute(s) read 🔎 Your users deserve searching capabilities! No third party service needed 🚀 2025, Apr 04 — 2 minute(s) read Easily Generate a `sitemap.xml` for Your Site with Roq Learn how to quickly set up and customize a sitemap.xml for your site using the Roq plugin. 2025, Jan 08 — 1 minute(s) read Do you want to publish a blog post series ? Make your blog posts part of a series. 2024, Dec 06 — 2 minute(s) read Need a QR Code? Add a QR Code to your Roq website. 2024, Nov 14 — 1 minute(s) read Page 1 of 2 ### [#plugin](/posts/tag/plugin/page2/) const savedTheme = localStorage.getItem('darkMode'); const isDark = savedTheme !== null ? savedTheme === 'true' : window.matchMedia('(prefers-color-scheme: dark)').matches; if (isDark) { document.documentElement.classList.add('dark'); } #plugin - Hello, world! I'm Roq — a funny little SSG (Static Site Generator) with a Java soul and Quarkus energy — Open Source and Free. setupSearch({ url: '/search-index.json' }); I am ROQ Java Static Site Generator 2.1.1 Search... ⌘K MENU Home Blog Events About Roqers Doc Getting Started Roq the Basics Plugins & Themes Publishing Advanced stuff Migrating LLMs.txt Social 2026 © ROQ × plugin Write your blog posts in AsciiDoc Automatically generate html from AsciiDoc content 2024, Oct 22 — 1 minute(s) read RSS Feed of your blog posts Automatically generate an RSS feed of your blog links. 2024, Oct 10 — 28 minute(s) read The second Roq plugin is for redirecting your page to a better place! We introduced a way to declare aliases in FrontMatter. It is now easy create redirections to your blog posts! 2024, Oct 09 — 1 minute(s) read The first Roq plugin is for tagging (with pagination) We introduced the first Roq plugin, it is for collection tagging & with pagination support! 2024, Oct 08 — 1 minute(s) read Page 2 of 2 ### [#design](/posts/tag/design/) const savedTheme = localStorage.getItem('darkMode'); const isDark = savedTheme !== null ? savedTheme === 'true' : window.matchMedia('(prefers-color-scheme: dark)').matches; if (isDark) { document.documentElement.classList.add('dark'); } #design - Hello, world! I'm Roq — a funny little SSG (Static Site Generator) with a Java soul and Quarkus energy — Open Source and Free. setupSearch({ url: '/search-index.json' }); I am ROQ Java Static Site Generator 2.1.1 Search... ⌘K MENU Home Blog Events About Roqers Doc Getting Started Roq the Basics Plugins & Themes Publishing Advanced stuff Migrating LLMs.txt Social 2026 © ROQ × design How AI Helped Me Rebuild My Blog and Move from Jekyll to Quarkus Roq A comprehensive journey of rebuilding a personal blog with the help of AI, moving from Jekyll to Quarkus Roq, exploring GitHub Issues Driven Development, and discovering how modern AI tools can transform the way we build and maintain websites. 2026, May 06 — 26 minute(s) read ### [#migration](/posts/tag/migration/) const savedTheme = localStorage.getItem('darkMode'); const isDark = savedTheme !== null ? savedTheme === 'true' : window.matchMedia('(prefers-color-scheme: dark)').matches; if (isDark) { document.documentElement.classList.add('dark'); } #migration - Hello, world! I'm Roq — a funny little SSG (Static Site Generator) with a Java soul and Quarkus energy — Open Source and Free. setupSearch({ url: '/search-index.json' }); I am ROQ Java Static Site Generator 2.1.1 Search... ⌘K MENU Home Blog Events About Roqers Doc Getting Started Roq the Basics Plugins & Themes Publishing Advanced stuff Migrating LLMs.txt Social 2026 © ROQ × migration How AI Helped Me Rebuild My Blog and Move from Jekyll to Quarkus Roq A comprehensive journey of rebuilding a personal blog with the help of AI, moving from Jekyll to Quarkus Roq, exploring GitHub Issues Driven Development, and discovering how modern AI tools can transform the way we build and maintain websites. 2026, May 06 — 26 minute(s) read ### [#markdown](/posts/tag/markdown/) const savedTheme = localStorage.getItem('darkMode'); const isDark = savedTheme !== null ? savedTheme === 'true' : window.matchMedia('(prefers-color-scheme: dark)').matches; if (isDark) { document.documentElement.classList.add('dark'); } #markdown - Hello, world! I'm Roq — a funny little SSG (Static Site Generator) with a Java soul and Quarkus energy — Open Source and Free. setupSearch({ url: '/search-index.json' }); I am ROQ Java Static Site Generator 2.1.1 Search... ⌘K MENU Home Blog Events About Roqers Doc Getting Started Roq the Basics Plugins & Themes Publishing Advanced stuff Migrating LLMs.txt Social 2026 © ROQ × markdown GFM Alert Blocks: Styled Callouts in Your Markdown Roq supports GitHub Flavored Markdown alert blocks with icons and themed colors. Learn how to use NOTE, TIP, IMPORTANT, WARNING, and CAUTION blocks, and how to add custom alert types. 2026, May 04 — 4 minute(s) read ### [#cool-stuff](/posts/tag/cool-stuff/) const savedTheme = localStorage.getItem('darkMode'); const isDark = savedTheme !== null ? savedTheme === 'true' : window.matchMedia('(prefers-color-scheme: dark)').matches; if (isDark) { document.documentElement.classList.add('dark'); } #cool-stuff - Hello, world! I'm Roq — a funny little SSG (Static Site Generator) with a Java soul and Quarkus energy — Open Source and Free. setupSearch({ url: '/search-index.json' }); I am ROQ Java Static Site Generator 2.1.1 Search... ⌘K MENU Home Blog Events About Roqers Doc Getting Started Roq the Basics Plugins & Themes Publishing Advanced stuff Migrating LLMs.txt Social 2026 © ROQ × cool-stuff Set It in Roq: The Editor that changes the game! Roq introduces a TipTap-powered editor with Markdown support, transforming it from a static site generator into a lightweight, developer-friendly CMS. Create, edit, and preview content seamlessly within the Quarkus dev experience. 2026, May 04 — 2 minute(s) read Roq 2.1 is here! Roq 2.1 brings a standalone CLI, LLMs.txt generation, dynamic pages from data, custom error pages, and much more. This post kicks off a series covering all the new features. 2026, May 01 — 2 minute(s) read Roq 2.0 and Java Advent Calendar article An introduction to Roq 2.0, a Quarkus-inspired approach to static site generation in Java. Learn about its new foundation, plugin support, and live-reload feature through a practical tutorial. 2025, Dec 09 — 2 minute(s) read Major site migrations to Roq ✨ Two prominent websites have just migrated to Roq—any guesses who they might be? 2025, Aug 26 — 2 minute(s) read Roq n Roll Your Tests 🎶 Testing the actual Roq generation has never been this cool! 🎸 2025, Jan 28 — 2 minute(s) read Page 1 of 3 ### [#cool-stuff](/posts/tag/cool-stuff/page2/) const savedTheme = localStorage.getItem('darkMode'); const isDark = savedTheme !== null ? savedTheme === 'true' : window.matchMedia('(prefers-color-scheme: dark)').matches; if (isDark) { document.documentElement.classList.add('dark'); } #cool-stuff - Hello, world! I'm Roq — a funny little SSG (Static Site Generator) with a Java soul and Quarkus energy — Open Source and Free. setupSearch({ url: '/search-index.json' }); I am ROQ Java Static Site Generator 2.1.1 Search... ⌘K MENU Home Blog Events About Roqers Doc Getting Started Roq the Basics Plugins & Themes Publishing Advanced stuff Migrating LLMs.txt Social 2026 © ROQ × cool-stuff Easily Generate a `sitemap.xml` for Your Site with Roq Learn how to quickly set up and customize a sitemap.xml for your site using the Roq plugin. 2025, Jan 08 — 1 minute(s) read Static attached files for posts and pages This Christmas, I’m Roq-ing a cool new feature (inspired by Hugo 😅): it is possible to attach static files to posts and pages. They will be served relative to the page. 🎁🤩 2024, Dec 26 — 1 minute(s) read Do you want to publish a blog post series ? Make your blog posts part of a series. 2024, Dec 06 — 2 minute(s) read Need a QR Code? Add a QR Code to your Roq website. 2024, Nov 14 — 1 minute(s) read The second Roq plugin is for redirecting your page to a better place! We introduced a way to declare aliases in FrontMatter. It is now easy create redirections to your blog posts! 2024, Oct 09 — 1 minute(s) read Page 2 of 3 ### [#cool-stuff](/posts/tag/cool-stuff/page3/) const savedTheme = localStorage.getItem('darkMode'); const isDark = savedTheme !== null ? savedTheme === 'true' : window.matchMedia('(prefers-color-scheme: dark)').matches; if (isDark) { document.documentElement.classList.add('dark'); } #cool-stuff - Hello, world! I'm Roq — a funny little SSG (Static Site Generator) with a Java soul and Quarkus energy — Open Source and Free. setupSearch({ url: '/search-index.json' }); I am ROQ Java Static Site Generator 2.1.1 Search... ⌘K MENU Home Blog Events About Roqers Doc Getting Started Roq the Basics Plugins & Themes Publishing Advanced stuff Migrating LLMs.txt Social 2026 © ROQ × cool-stuff The first Roq plugin is for tagging (with pagination) We introduced the first Roq plugin, it is for collection tagging & with pagination support! 2024, Oct 08 — 1 minute(s) read Out of the box awesome SEO Learn how to implement SEO in Roq in a blink of an eye. 2024, Sep 23 — 3 minute(s) read How to add syntax highlighting to your Roq site Learn how to integrate syntax highlighting into your Roq site using Highlight.js and the Quarkus web-bundler extension. This guide walks you through the simple steps to add it via the pom.xml, JavaScript, and SCSS files. 2024, Sep 20 — 2 minute(s) read Effortless URL Handling in Roq with Qute super-power Effortlessly manage both relative and absolute URLs with our enhanced Qute-powered feature. Utilizing the RoqUrl class, you can easily join and resolve paths, ensuring clean and predictable URLs. This update simplifies URL handling, making your code more efficient and your content easier to navigate and share. 2024, Sep 16 — 2 minute(s) read Page 3 of 3 ### [#seo](/posts/tag/seo/) const savedTheme = localStorage.getItem('darkMode'); const isDark = savedTheme !== null ? savedTheme === 'true' : window.matchMedia('(prefers-color-scheme: dark)').matches; if (isDark) { document.documentElement.classList.add('dark'); } #seo - Hello, world! I'm Roq — a funny little SSG (Static Site Generator) with a Java soul and Quarkus energy — Open Source and Free. setupSearch({ url: '/search-index.json' }); I am ROQ Java Static Site Generator 2.1.1 Search... ⌘K MENU Home Blog Events About Roqers Doc Getting Started Roq the Basics Plugins & Themes Publishing Advanced stuff Migrating LLMs.txt Social 2026 © ROQ × seo Out of the box awesome SEO Learn how to implement SEO in Roq in a blink of an eye. 2024, Sep 23 — 3 minute(s) read ### [#guide](/posts/tag/guide/) const savedTheme = localStorage.getItem('darkMode'); const isDark = savedTheme !== null ? savedTheme === 'true' : window.matchMedia('(prefers-color-scheme: dark)').matches; if (isDark) { document.documentElement.classList.add('dark'); } #guide - Hello, world! I'm Roq — a funny little SSG (Static Site Generator) with a Java soul and Quarkus energy — Open Source and Free. setupSearch({ url: '/search-index.json' }); I am ROQ Java Static Site Generator 2.1.1 Search... ⌘K MENU Home Blog Events About Roqers Doc Getting Started Roq the Basics Plugins & Themes Publishing Advanced stuff Migrating LLMs.txt Social 2026 © ROQ × guide Set It in Roq: The Editor that changes the game! Roq introduces a TipTap-powered editor with Markdown support, transforming it from a static site generator into a lightweight, developer-friendly CMS. Create, edit, and preview content seamlessly within the Quarkus dev experience. 2026, May 04 — 2 minute(s) read More diagram than you could have dreamed of. Leveraging Kroki.io to generate diagram from text 2025, Jun 11 — 1 minute(s) read 🔎 Your users deserve searching capabilities! No third party service needed 🚀 2025, Apr 04 — 2 minute(s) read No pain updates with Roq One of the most overlooked aspects when choosing a Static Site Generator (SSG) is how easy it is to keep your project up to date. Many developers have struggled with complex upgrade processes, dependency conflicts, and breaking changes when using traditional SSGs like Jekyll or Hugo. 2025, Mar 24 — 1 minute(s) read Easily Generate a `sitemap.xml` for Your Site with Roq Learn how to quickly set up and customize a sitemap.xml for your site using the Roq plugin. 2025, Jan 08 — 1 minute(s) read Page 1 of 4 ### [#guide](/posts/tag/guide/page2/) const savedTheme = localStorage.getItem('darkMode'); const isDark = savedTheme !== null ? savedTheme === 'true' : window.matchMedia('(prefers-color-scheme: dark)').matches; if (isDark) { document.documentElement.classList.add('dark'); } #guide - Hello, world! I'm Roq — a funny little SSG (Static Site Generator) with a Java soul and Quarkus energy — Open Source and Free. setupSearch({ url: '/search-index.json' }); I am ROQ Java Static Site Generator 2.1.1 Search... ⌘K MENU Home Blog Events About Roqers Doc Getting Started Roq the Basics Plugins & Themes Publishing Advanced stuff Migrating LLMs.txt Social 2026 © ROQ × guide Static attached files for posts and pages This Christmas, I’m Roq-ing a cool new feature (inspired by Hugo 😅): it is possible to attach static files to posts and pages. They will be served relative to the page. 🎁🤩 2024, Dec 26 — 1 minute(s) read Do you want to publish a blog post series ? Make your blog posts part of a series. 2024, Dec 06 — 2 minute(s) read Need a QR Code? Add a QR Code to your Roq website. 2024, Nov 14 — 1 minute(s) read Write your blog posts in AsciiDoc Automatically generate html from AsciiDoc content 2024, Oct 22 — 1 minute(s) read RSS Feed of your blog posts Automatically generate an RSS feed of your blog links. 2024, Oct 10 — 28 minute(s) read Page 2 of 4 ### [#guide](/posts/tag/guide/page3/) const savedTheme = localStorage.getItem('darkMode'); const isDark = savedTheme !== null ? savedTheme === 'true' : window.matchMedia('(prefers-color-scheme: dark)').matches; if (isDark) { document.documentElement.classList.add('dark'); } #guide - Hello, world! I'm Roq — a funny little SSG (Static Site Generator) with a Java soul and Quarkus energy — Open Source and Free. setupSearch({ url: '/search-index.json' }); I am ROQ Java Static Site Generator 2.1.1 Search... ⌘K MENU Home Blog Events About Roqers Doc Getting Started Roq the Basics Plugins & Themes Publishing Advanced stuff Migrating LLMs.txt Social 2026 © ROQ × guide The second Roq plugin is for redirecting your page to a better place! We introduced a way to declare aliases in FrontMatter. It is now easy create redirections to your blog posts! 2024, Oct 09 — 1 minute(s) read The first Roq plugin is for tagging (with pagination) We introduced the first Roq plugin, it is for collection tagging & with pagination support! 2024, Oct 08 — 1 minute(s) read Out of the box awesome SEO Learn how to implement SEO in Roq in a blink of an eye. 2024, Sep 23 — 3 minute(s) read Mastering Pagination in Roq Learn how to implement pagination in Roq to enhance your content navigation. This article walks through the process of adding pagination, configuring page size, and customizing links. 2024, Sep 20 — 1 minute(s) read How to add syntax highlighting to your Roq site Learn how to integrate syntax highlighting into your Roq site using Highlight.js and the Quarkus web-bundler extension. This guide walks you through the simple steps to add it via the pom.xml, JavaScript, and SCSS files. 2024, Sep 20 — 2 minute(s) read Page 3 of 4 ### [#guide](/posts/tag/guide/page4/) const savedTheme = localStorage.getItem('darkMode'); const isDark = savedTheme !== null ? savedTheme === 'true' : window.matchMedia('(prefers-color-scheme: dark)').matches; if (isDark) { document.documentElement.classList.add('dark'); } #guide - Hello, world! I'm Roq — a funny little SSG (Static Site Generator) with a Java soul and Quarkus energy — Open Source and Free. setupSearch({ url: '/search-index.json' }); I am ROQ Java Static Site Generator 2.1.1 Search... ⌘K MENU Home Blog Events About Roqers Doc Getting Started Roq the Basics Plugins & Themes Publishing Advanced stuff Migrating LLMs.txt Social 2026 © ROQ × guide Easily manage Drafts and Future articles in Roq Roq SSG introduces a new feature that allows you to hide or show draft and future articles using simple Quarkus configurations. This update gives developers greater control over which content is visible, improving content management and workflow. 2024, Sep 19 — 1 minute(s) read Effortless URL Handling in Roq with Qute super-power Effortlessly manage both relative and absolute URLs with our enhanced Qute-powered feature. Utilizing the RoqUrl class, you can easily join and resolve paths, ensuring clean and predictable URLs. This update simplifies URL handling, making your code more efficient and your content easier to navigate and share. 2024, Sep 16 — 2 minute(s) read Page 4 of 4 ### [#quarkus-roq](/posts/tag/quarkus-roq/) const savedTheme = localStorage.getItem('darkMode'); const isDark = savedTheme !== null ? savedTheme === 'true' : window.matchMedia('(prefers-color-scheme: dark)').matches; if (isDark) { document.documentElement.classList.add('dark'); } #quarkus-roq - Hello, world! I'm Roq — a funny little SSG (Static Site Generator) with a Java soul and Quarkus energy — Open Source and Free. setupSearch({ url: '/search-index.json' }); I am ROQ Java Static Site Generator 2.1.1 Search... ⌘K MENU Home Blog Events About Roqers Doc Getting Started Roq the Basics Plugins & Themes Publishing Advanced stuff Migrating LLMs.txt Social 2026 © ROQ × quarkus-roq How AI Helped Me Rebuild My Blog and Move from Jekyll to Quarkus Roq A comprehensive journey of rebuilding a personal blog with the help of AI, moving from Jekyll to Quarkus Roq, exploring GitHub Issues Driven Development, and discovering how modern AI tools can transform the way we build and maintain websites. 2026, May 06 — 26 minute(s) read