I build my talks and workshops as code. The slides live in Git, are reproducible on any machine, and are published from CI with the same toolchain I run locally. The source for my decks is public:
- Repository: https://github.com/caring-coder/presentations
This post documents the approach and shows a concrete pom.xml I use to compile
AsciiDoc into a Reveal.js deck, with diagrams rendered at build time.
Abstract
- Authoring format: AsciiDoc (AsciidoctorJ)
- Slide engine: Reveal.js
- Build tool: Maven, fully reproducible, downloads its own dependencies (JRuby, gems, Reveal.js)
- Extras:
asciidoctor-diagramfor PlantUML/Graphviz and friends; include tagged source code from the repo.
Slide deck source and layout
I keep each presentation as an isolated Maven module or folder under the repo. The typical layout is:
<deck-name>/
pom.xml
src/docs/asciidoc/
index.adoc # entrypoint
images/ # generated or static assets
fragments/*.adoc # reusable sections
docs/ # real code samples to include via tags (optional)
AsciiDoc gives me strong authoring primitives (includes, conditionals, attributes) and lets me pull real, compilable code straight into slides with tagged regions:
[source,java]
----
include::../../docs/src/main/java/com/example/Thing.java[tags=demo]
----
When the implementation changes, the slides change with it.
Build: Maven + Asciidoctor Reveal.js
The Maven build downloads Reveal.js, provisions gems, enables
asciidoctor-diagram, and emits HTML into target/generated-slides. Here is a
representative pom.xml I use in those presentation modules:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>pro.verron</groupId>
<artifactId>diagram-as-code</artifactId>
<version>1.0.0-SNAPSHOT</version>
<name>Diagram as Code</name>
<description>A presentation about diagram-as-code tools.</description>
<properties>
<project.slides.directory>${project.build.directory}/generated-slides
</project.slides.directory>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<asciidoctor.maven.plugin.version>3.2.0
</asciidoctor.maven.plugin.version>
<asciidoctorj.version>3.0.0</asciidoctorj.version>
<asciidoctorj.diagram.version>3.0.1</asciidoctorj.diagram.version>
<jruby.version>10.0.2.0</jruby.version>
<revealjs.version>3.9.2</revealjs.version>
<!-- Use 'master' as version and remove the 'v' prefixing the download url to use the current snapshot version -->
<asciidoctor-revealjs.version>master</asciidoctor-revealjs.version>
</properties>
<dependencies>
<dependency>
<groupId>rubygems</groupId>
<artifactId>asciidoctor-revealjs</artifactId>
<version>4.0.1</version>
<type>gem</type>
</dependency>
</dependencies>
<build>
<defaultGoal>process-resources</defaultGoal>
<extensions>
<extension>
<!-- this allows us to download gems -->
<groupId>org.torquebox.mojo</groupId>
<artifactId>mavengem-wagon</artifactId>
<version>1.0.3</version>
</extension>
</extensions>
<plugins>
<plugin>
<groupId>com.googlecode.maven-download-plugin</groupId>
<artifactId>download-maven-plugin</artifactId>
<version>1.13.0</version>
<executions>
<execution>
<id>install-revealjs</id>
<phase>generate-resources</phase>
<goals>
<goal>wget</goal>
</goals>
<configuration>
<url>
https://github.com/hakimel/reveal.js/archive/${revealjs.version}.zip
</url>
<unpack>true</unpack>
<outputFileName>reveal.js-${revealjs.version}.zip
</outputFileName>
<outputDirectory>${project.slides.directory}
</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>de.saumya.mojo</groupId>
<artifactId>gem-maven-plugin</artifactId>
<version>2.0.1</version>
<configuration>
<jrubyVersion>${jruby.version}</jrubyVersion>
<gemHome>${project.build.directory}/gems</gemHome>
<gemPath>${project.build.directory}/gems</gemPath>
</configuration>
<executions>
<execution>
<id>install-gems</id>
<goals>
<goal>initialize</goal>
</goals>
<phase>initialize</phase>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<version>${asciidoctor.maven.plugin.version}</version>
<dependencies>
<!-- Comment this section to use the default jruby artifact provided by the plugin -->
<dependency>
<groupId>org.jruby</groupId>
<artifactId>jruby-complete</artifactId>
<version>${jruby.version}</version>
</dependency>
<!-- Comment this section to use the default AsciidoctorJ artifact provided by the plugin -->
<dependency>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctorj</artifactId>
<version>${asciidoctorj.version}</version>
</dependency>
<dependency>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctorj-diagram</artifactId>
<version>${asciidoctorj.diagram.version}</version>
</dependency>
<dependency>
<groupId>fr.jmini.asciidoctorj</groupId>
<artifactId>sourcedir</artifactId>
<version>1.1.0</version>
</dependency>
</dependencies>
<configuration>
<requires>
<require>asciidoctor-diagram</require>
</requires>
<sourceDirectory>src/docs/asciidoc</sourceDirectory>
<attributes>
<sourcedir-definition>${project.basedir}/docs
</sourcedir-definition>
</attributes>
</configuration>
<executions>
<execution>
<id>generate-slides</id>
<phase>process-resources</phase>
<goals>
<goal>process-asciidoc</goal>
</goals>
<configuration>
<gemPath>${project.build.directory}/gems</gemPath>
<requires>
<require>asciidoctor-revealjs</require>
<require>asciidoctor-diagram</require>
</requires>
<outputDirectory>${project.slides.directory}
</outputDirectory>
<backend>revealjs</backend>
<attributes>
<revealjsdir>reveal.js-${revealjs.version}
</revealjsdir>
<revealjs_theme>serif</revealjs_theme>
<revealjs_transition>linear
</revealjs_transition>
<project-version>${project.version}
</project-version>
</attributes>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>mavengems</id>
<url>mavengem:https://rubygems.org</url>
</repository>
</repositories>
</project>
With this configuration, a simple mvn clean package produces HTML slides in
target/generated-slides. Open the generated index.html (or the name of your
index.adoc output) directly in a browser, or serve the folder via any static
file server.
Authoring: slides, code, and diagrams
- Source includes: I prefer tagging production-like examples and reusing them in
slides via
include::. - Diagrams:
asciidoctor-diagramrenders PlantUML, Mermaid (via Kroki), Graphviz, and more at build time. This is a practical application of the Diagrams as Code philosophy: the diagrams are version-controlled, diffable, and stay in sync with the slide narrative.
Example:
PlantUML
Alice -> Bob: request
Bob --> Alice: response
Mermaid
flowchart TD
A[Idea] --> B{Prototype?}
B -- Yes --> C[Demo]
B -- No --> D[Spike]
C --> E{Ship?}
E -- Yes --> F[Release]
E -- No --> D
If your diagrams require native tools (e.g., Graphviz), install them locally or route rendering through Kroki.
Local run and CI
- Prerequisites: Java 17+ and Maven. JRuby and gems are provisioned by the build; no system Ruby is required.
- Build locally:
mvn clean packagethen opentarget/generated-slides/*.html. - CI publishing: copy the
generated-slidesfolder to GitHub Pages (I use a simple deploy step per deck) so attendees always see the version tied to a tag.
Troubleshooting
- If Reveal.js assets don’t load, ensure the archive was downloaded into
target/generated-slides/and therevealjsdirattribute matches the folder name. - For diagram failures, check that
asciidoctor-diagramis required and that any native dependencies (like Graphviz) are installed or that Kroki is enabled.
References
- Presentations repo: https://github.com/caring-coder/presentations
- Asciidoctor Maven Plugin: https://github.com/asciidoctor/asciidoctor-maven-plugin
- Asciidoctor Reveal.js: https://github.com/asciidoctor/asciidoctor-reveal.js
- Asciidoctor Diagram: https://github.com/asciidoctor/asciidoctor-diagram