Building a Responsive Reveal.js Theme

I’m going to be speaking at Drupalcon Vienna, and am incredibly excited ⁽¹⁾. Speaking at a conference can be a lot of effort, so naturally I’ve started by avoiding the real work and doing some productive procrastinating instead:

It may have been easier to just use the great looking Keynote template already created by our designers, but I can justify the effort with the opportunity it provides to experiment. Unlike building websites that have to survive the wilds of the internet, this is something that only has to work on my machine (and those of my colleagues) where I know it will be seen in the latest browser and can rely on things like Viewport Units, Flexbox, and CSS Grids, without worrying about needing polyfills, fallbacks, or patching buggy behaviour in older browsers.

It’s also a unique environment to apply responsive web design. When presenting, the projector may be HDMI at either 1280⨯720, or 1920⨯1080. At a past conference one of the rooms I presented in unexpectedly only had VGA to a projector that supported 1024⨯768. Since my presentation will also be published after the conference people may view it at whatever desktop resolution their computer is, some odd sized portion of the screen their browser is set to, or even on a mobile device. While a 16px font size may be easily readable on any sized desktop computer, it will appear increasingly smaller the higher the resolution of the projector; not good for people at the back of the room trying to read and understand a code example.


By default Reveal.js acts like most desktop presentation software: you set the dimensions of your presentation (typically at a 4:3 or 16:9 ratio), and then everything is placed by fixed units. When presenting, everything will be scaled in proportion to maintain relative sizes. For example, if you set your presentation to 720px high and a base font size of 16px, presenting on a 1080px high display will appear to scale your base font size ⨯1.5 to 24px. If the screen you’re outputting to doesn’t match the aspect ratio you created your presentation in, your presentation is scaled to fit within the smallest dimension of the display and extra space is added as needed to fill the rest of the screen. The first step is telling reveal.js that it should instead size slides to the full dimensions of the viewport, and not scale them:

Reveal.initialize({
width: "100%",
height: "100%",
margin: 0,
minScale: 1,
maxScale: 1
});

Now that the slides are responsive, absolute font sizes become a problem. The $mainFontSize SASS variable is applied to the .reveal container and defaults to 40px, which will be gigantic on small displays and won’t adjust to display at a consistent size between 720p and 1080p displays. In order to adjust for the screen size it should be a viewport relative unit, and to make use of rem units the base font size needs to be set on the html element. Using 16px on a 1280⨯720 display as the default font size, after a little rounding that results in 2.25vmin; using vmin instead of vh keeps the font size consistent if the display is portrait oriented instead of landscape. Then, to prevent the $mainFontSize variable from taking precedence over the root value, it is set to inherit instead of a length unit. Lastly, the viewport units work negatively on very small displays (on an iPhone6 2.25vmin is only 8.4px), so a media query ensures that on the smallest of displays the base font size is a readable 14px. Viewport units are also helpful for margins and padding, keeping whitespace consistent between display sizes.

The part I was most excited to experiment with was using CSS Grids for layout. While the end result is pretty simple, using grids makes positioning items substantially easier, using much less markup than older strategies. Columns and rows can be set to fixed sizes, adjust to the size of their contents, or expand to fill available space. On the title slide layout, presentation author information is placed at the bottom of the slide, 40% of the available width from the right edge. The title and description, placed within a containing element, is vertically aligned within the available space left between the logo at the top and the author information at the bottom.

.reveal .slides section.title .grid-wrapper {    
box-sizing: border-box;
display: grid;
grid-template-areas:
"header header"
"content content"
". credit";
grid-template-columns: auto 40%;
grid-template-rows: 6vmin 1fr auto;
grid-column-gap: 10vmin;
height: 100%;
padding: 10vmin;
}
.logo {
grid-area: header;
}
.content {
align-self: center;
grid-area: content;
}
.credit {
grid-area: credit;
}
The title layout overlaid with Firefox’s CSS Grid Inspector

It’s also easy to adjust the layout with media queries. For small displays, the grid is changed to one column, with the author information getting shifted to the right.

@media (max-width: 568px) {
.grid-wrapper {
grid-template-areas: "header" "content" "credit";
grid-template-columns: auto;
}
.credit {
justify-self: end;
}
}

On wider displays the contents container receives its own grid, placing the title and description on one row and lining the description up with the author information.

@media (min-width: 1280px) {
.content {
display: grid;
grid-template-areas: "title description";
grid-template-columns: auto 40%;
grid-column-gap: 10vmin;
}
h1 {
grid-area: title;
}
.description {
grid-area: description;
}
}

And there we have it; a Reveal.js theme for our new branding. I look forward to working more with CSS Grid to replicate some of the more complex slide templates. Now, I need to cycle some kilometres ⁽²⁾.


⁽¹⁾ And incredibly nervous.

⁽²⁾ And prepare the contents of my presentation.