Skip to content
Branch: master
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
359 lines (262 sloc) 18 KB

Presentation Plugin

The Presentation Plugin is an extension for Grav CMS, and provides a simple way of creating fullscreen slideshows that can be navigated two-dimensionally, using the Reveal.js-library.

At its core the plugin facilitates efficient handling of content for use with the library. You can utilize Reveal.js however you want through custom initialization, and still leverage the plugin's content-handling. A demo is available and a Skeleton, as well as the demo content.


  • Presentations through two-dimensional slideshows
  • Responsive, multi-device capable styling
  • Granular control over presentation and slides
    • Cascading-styles and options with Shortcodes and the Admin-plugin
  • Portable content: Everything is contained in Markdown, including settings
  • Flexible, ambigious, recursive Page-structure
  • Extendable through your own theme or plugin
  • Print-friendly presentations, with or without notes or in text-only mode
  • Presenter-mode: A modern, powerful, easy-to-use alternative to PowerPoint
    • Include notes with your presentation
    • Synchronize Presenter-mode and the Presentation locally or remotely


Installing the presentation-plugin can be done in one of two ways. The GPM (Grav Package Manager) installation method enables you to quickly and easily install the plugin with a simple terminal command, while the manual method enables you to do so via a zip file.

GPM Installation (Preferred)

The simplest way to install this plugin is via the Grav Package Manager (GPM) through your system's terminal (also called the command line). From the root of your Grav install type:

bin/gpm install presentation

This will install the Presentation-plugin into your /user/plugins directory within Grav. Its files can be found under /your/site/grav/user/plugins/presentation.

Manual Installation

To install this plugin, just download the zip version of this repository and unzip it under /your/site/grav/user/plugins. Then rename the folder to presentation. You can find these files on GitHub or via

You should now have all the plugin files under


NOTE: This plugin is a modular component for Grav which requires Grav and the Error and Problems to operate.


This plugin will only work with Grav v1.6 or higher, as it requires PHP v7.1 or higher.


Before configuring this plugin, you should copy the user/plugins/presentation/presentation.yaml to user/config/plugins/presentation.yaml and only edit that copy.

Here is the default configuration and an explanation of available options:

# Enable Plugin if true, disable if false
enabled: true
# Theme to use from Reveal.js (
theme: moon
# Order of how pages are rendered
  by: folder
  dir: asc
# Include Theme's custom.css
theme_css: true
# Enable Plugin's CSS
builtin_css: true
# Enable Plugin's JS
builtin_js: true
# Enable Plugin's dynamic text sizing
textsizing: true
# Synchronize Slide-navigation
sync: "none"
# URL Route to use for Poll-sync
api_route: "presentationapi"
# Poll-sync timeout in milliseconds
poll_timeout: 2000
# Poll-sync retry limit
poll_retry_limit: 10
# Enable Poll-sync token-authorization
token_auth: false
# Poll-sync token to use for authorization
token: Hd4HFdPvbpKzTqz
# Enable Save Content button and CTRL+SHIFT+S combo
admin_async_save: false
# Enable Save Content when typing
admin_async_save_typing: false
# Twig-template to inject below content
footer: ""
# Enable onLoad transition
transition: true
# Enable Plugin's shortcodes
shortcodes: true
# Default class for Presentation-shortcode
shortcode_classes: "presentation-iframe"
# Unwrap images from paragraph
unwrap_images: true
# Class to use for Content building
content: "Content"
# Class to use for Content parsing
parser: "Parser"
# Class to use for Styles, Classes, and Data management
transport: "Transport"
# Breakpoints for responsive textsizing
  240: "16"
  320: "20"
  576: "24"
  768: "28"
  992: "32"
  1200: "36"
  1600: "40"
# Styles to use as defaults for Presentations
# Dynamic text scaling to use as defaults for Presentations
  scale: string
  modifier: float
# Stack slides horizontally or vertically (default), on thematic breaks
horizontal: false
# Options to pass to Reveal.js
  width: "100%"
  height: "100%"
  margin: "0"
  minScale: "1"
  maxScale: "1"
  transition: "fade"
  controlsTutorial: "false"
  history: "true"
  display: "flex"
  pdfSeparateFragments: false

All configuration-options available to the Reveal.js-library can be configured through options, see its documentation for available options.

Page-specific configuration

Any configuration set in presentation.yaml can be overridden through a Page's FrontMatter, like this:

title: Alice’s Adventures in Wonderland
    by: date
    dir: desc
    transition: "fade"


The Page-structure used in the Presentation-plugin is essentially the same as normally in Grav, with a few notable exceptions: Any horizontal rule, --- in Markdown and <hr /> in HTML, is treated as a thematic break, as it is defined in HTML5. This means that every Page in Grav is treated as a normal, horizontal Slide when the Plugin iterates over them, but a thematic break creates a vertical Slide.

You can have as many Pages below the root-page as you want, each of them will be treated as a Slide. When you create thematic breaks within the Page, the Slides are then created below the Page itself -- accommodating Reveal.js' two-dimensional slideshows.

For using Reveal.js itself, see their documentation on Getting Started with Presenting and Speaker View.


With Reveal.js the presentation is not entirely linear. Rather, it has a linear, left-to-right set of sections that each make up a slide, and can have additional slides going downwards. Thus you can progress through the presentation linearly starting at each section, moving downwards until the end, and continuing onto the next section, or move between them as you choose.

Further, there are Fragments that can be used within each slide. These reveal linearly like slides, but make one element appear at a time rather than the full contents of the slide.


├── 01.down-the-rabbit-hole
│   └──
├── 02.advice-from-a-caterpillar
│   └──
├── 03.were-all-mad-here
│   └──
├── 04.a-mad-tea-party
│   └──
├── 05.the-queens-crocquet-ground
│   └──
├── 06.postscript

As seen in this example structure, only the initial page uses the presentation.html.twig-template. The template used for child-pages is slide.html.twig, though the content of these pages are processed regardless of template. Naming them enables the blueprints for slides in the Admin-plugin. The plugin defines the presentation.html.twig-template, but you can override it through your theme.


The plugin emulates the logic of Cascading Style Sheets (CSS), in that pages can be assigned a class, style-property, or be hidden using FrontMatter or shortcodes. This is as simple as using class: custom-slide-class in FrontMatter or [class=custom-slide-class] with a shortcode in the Markdown-content. Styles are applied the same way, where FrontMatter accepts CSS-properties like this:

  color: green

That is, mapping each property to a value, not as a list. The same could be set for any single slide using [style-color=green], as described below. Styles are given precedence by where they appear, so the plugins looks for them in this order:

  1. Plugin-options
  2. Page FrontMatter
  3. Child page FrontMatter
  4. Page Content (Markdown) as shortcodes

The properties are gathered cumulatively, and a property farther down the chain takes precedence over a property further up.

You can of course also style the plugin using your theme's /css/custom.css-file, by targeting the .reveal-selector which wraps around all of the plugin's content. This behavior can be enabled or disabled with the theme_css-setting. All slides have an id-attribute set on their sections, which can be utilized by CSS like this:

.reveal #down-the-rabbit-hole-0 {
  background: red;

Fitting text to a slide

The plugin makes available a method of dynamically scaling text within a slide, which is similar yet distinct from what happens in PowerPoint 2016. Rather than do this scaling entirely automatically, which tends to work poorly across devices and resolutions, you set a scale and an optional modifier, eg.:


If Textsizing is enabled in the plugin's options and on the Page, the relation between block text -- any text not in a header-element -- and header-text (h1, h2, h3, h4, h5, h6) is determined by the textsize-scale-property. That is, the size of the header-element's text relative to the base font-size.

In the example above, the scale is set to the "Major Second" rhythm, and with a base font size of 16 -- the minimum font-size recommended for web -- this yields the following sizes for headers: 28.83 (h1), 25.63 (h2), 22.78 (h3), 20.25 (h4), 18 (h5), and 16 (h6). The base font size, and hence text, is adjusted upwards as the size of the screen increases to enable dynamic, responsive text-sizing. This is done through the breakpoints-option.

The modifier, if set, changes the matched breakpoint's base font size by multiplication. So if set to 1.05 it makes it 5% larger than it normally would be at this breakpoint.

Using section- or slide-specific styles

If configured with shortcodes: true any section or slide can use shortcodes to declare specific styles. These take the format of [style-property=value] and are defined in multiples, eg:


If the shortcode is found and applied, it is stripped from the further evaluated content. This method uses regular expressions for speed, and takes precedence over plugin- or page-defined styles.

Note: The syntax is restricted to [style-property=value]. Quotes or other unexpected characters not conforming to alphanumerics or dashes will make the expression fail to pick up the shortcode. The style-property or value must basically conform to the [a-zA-Z0-9-]+ regular expression, separated by an equal-character (=) and wrapped in square brackets ([]). For testing, use Regex101.

Center content

To center content vertically in the slide, when Reveal.js has display: 'flex' set, you need to add justify-content: center to the slides. This is as simple as adding the following to a Page's FrontMatter:

  justify-content: center

Or the shortcode [style-justify-content=center] to an individual slide.

Full background image or video with Reveal.js, through data-attributres

Reveal.js supports easy usage of background images or videos for slides, with their Slide backgrounds. As well as inline styles through shortcodes, any property that begins with data is passed as a data-attribute to the slide, so you can do things like add a background video:


Injecting Twig

Using the footer-setting you can append a Twig-template to each section globally, or a specific page's section. For example, footer: "partials/presentation_footer.html.twig" will render the theme's partials/presentation_footer.html.twig-template and append it to the section(s). If the element was constructed like this: <div class="footer">My footer</div>, you could style it like this:

.reveal .slides .footer {
  display: block;
  position: absolute;
  bottom: 2em;

You can also arbitrarily execute Twig within a page's Markdown by enabling it in the FrontMatter with:

twig_first: true
  twig: true

For example, <p>{{ }}</p> will render the name of the author defined in site.yaml.

Creating a menu

The plugin makes a presentation_menu-variable available through Twig on pages which use the fullscreen-template, which can be used to construct an overall menu of pages. It is an array with anchors and titles for each page, and a list of them with links to sections can be constructed like this:

<ul id="menu" class="menu">
{% for anchor, title in presentation_menu %}
    <a href="#{{ anchor }}">{{ title }}</a>
{% endfor %}

Each slide is assigned an id-attribute based on the page's slug and its index, as well as a data-title-attribute containing the title of the page. A menu could also be made using this data with JavaScript: document.getElementById('presentation').querySelectorAll('*[id]').


Each slide can have notes associated with it, like a PowerPoint-presentation would. These can be set on any slide using [notes] ... [/notes], where the shortcodes should envelop the Markdown-content that makes up your notes. Eg:


- Rabbits don't lay eggs
- Porpoises don't tell lies
- Camels don't smoke cigarettes



The plugin, like the Reveal.js-library, makes available a Presenter-mode. There are two modes available for using this: Locally, with sync: 'browser', or remotely, with sync: 'poll'. When running locally, you need to access your presentation with a special URL -- http://yourgrav.tld/book?admin=yes&showNotes=true -- and in a new window from the same browser open the same URL without these parameters -- http://yourgrav.tld/book.

The synchronization between Presenter-mode and the Presentation happens by sending data from one browser-window to the other, requiring JavaScript. When running remotely, the synchronization happens by polling and checking if the presentation has changed.

Note: The polling approach needs a stable server to work, more so than Grav itself. It has been tested extensively with PHP 7.1 and 7.2, running on Caddy Server and with PHP's built-in server, with a fairly standard production-setup of PHP. If your server-connection crashes with a 502 error -- usually with the error "No connection could be made because the target machine actively refused it.", it is because PHP is set up to forcibly time out despite being long-polled.


A Presentation-shortcode is available for embedding a presentation in another Page; [presentation="./route/to/presentation"]. This creates an iFrame with the presentation in it. You can also add your own classes to the iFrame with the class-parameter: [presentation "introduction-to-ux/chromeless:true" class="class-one class-two"], or default classes with the shortcode_classes-option.


PHP Code Standards

This plugin follows PSR-1, PSR-2, and PEAR coding standards (use CodeSniffer), as well as PSR-4.


Use a SCSS-compiler, like LibSass, eg. node-sass and compiled scss/presentation.scss to css/presentation.css in the theme-folder. For example: node-sass --watch --source-map true --output-style compressed scss/presentation.scss css/presentation.css. Requires Node-modules to be installed first.


As demonstrated by the content, parser, and transport options above, you can fairly easily extend the behavior of the plugin. For example, if you install the Presentation Deckset Plugin, you could set this to parser: 'DecksetParser' to use the Deckset-syntax. Addons written this way must implement the correspond interface, and extend the base class provided by the plugin. Eg., class DecksetParser extends Parser implements ParserInterface.

Customizing the blueprints

The plugin searches for presentation.yaml and slide.yaml in the current theme's blueprints-folder, and then user's blueprints-folder, to override the Plugin's own Page-blueprints. With you can use your Theme or Skeleton to create custom blueprints for this Page Types.


  • Include/exclude per-page option from DirectoryListing
  • Scaling for square, 4:3 ("truncated"), 16:9 (normal widescreen) - should height be taken into account? Not applicable with regular breakpoints and Transport application
  • Safari: Images are too wide - no obvious reason as to why, other than Safari


You can’t perform that action at this time.