The tiniest presentation framework
I did roughly 50 talks over the years, and for almost all of them, I used Reveal.js. I like it because it's HTML-based. I have complete control of everything and can easily publish the slides online. However, one thing bugs me every single time - the size of my content. I want to use all the available space. This becomes very important when I'm showing code to people. That's why I wrote SimPre. It's a 10KB HTML presentation framework that properly scales and positions my content.
A demo presentation could be seen here simpre.vercel.app.
Let me list all the framework features and see if I can sell them to you.
It's just a HTML file
So, first, we want a quick start. With SimPre you only need a single HTML file:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SimPre - The Simplest Presentation HTML Framework</title>
<link rel="stylesheet" href="https://simpre.vercel.app/assets/styles.css" />
<script src="https://simpre.vercel.app/assets/simpre.js"></script>
</head>
<body>
<section>
<h1>Hey 👋</h1>
</section>
<div id="progress"></div>
</body>
</html>
Of course, to ensure yourself, you may download that HTML file with the necessary JavaScript and CSS files. It's here.
Next, every <section>
tag is a slide. The framework is minimalistic. There are no nested slides. Even though I used this feature a lot, I preferred to leave it out from the initial implementation.
The scaling
The most important code that I wrote is the one that scales up the slide, so it takes all the available space. This is done after the slide becomes visible. SimPre checks the current viewport size and applies CSS transformation. Here's my naive solution:
window.fit = function fit(node) {
if (node.scaled) return;
const rect = node.getBoundingClientRect();
const w = rect.width
const h = rect.height;
const resize = () => {
const vw = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0);
const vh = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0);
const ratio = w / h;
// setting the right scale
let newW = vw;
let newH = newW / ratio;
if (newH > vh) {
newH = vh;
newW = ratio * newH;
}
const scale = newW / w;
node.style.display = 'block';
node.style.transformOrigin = 'top left';
node.style.transform = `scale(${scale}, ${scale})`;
// setting the position
const wAfterScale = node.getBoundingClientRect().width;
const hAfterScale = node.getBoundingClientRect().height;
let top = false;
let left = false;
if (wAfterScale < vw - 10) {
top = 0;
left = (vw - wAfterScale) / 2;
}
if (hAfterScale < vh - 10) {
top = (vh - hAfterScale) / 2;
}
if (top !== false || left !== false) {
node.style.position = 'absolute';
node.style.top = top + 'px';
node.style.left = left + 'px';
}
node.scaled = true;
}
resize();
window.addEventListener('resize', resize);
}
It uses the good old technique where we calculate the ratio between width and height and then nail the optimal sizing.
The code snippets
Another "problem" that I have had so far was the need to put all my code samples in one place. It becomes messy with 30+ slides. I decided to load and render the snippets on the fly to solve this problem. The technique that I used is described in this article "Here is how call-to-action widgets probably work". The usage is down to adding a <script>
tag:
<section>
<script
src="https://simpre.vercel.app/assets/is.js"
data-file="<path to a file>">
</script>
<section>
Where data-file
attribute points to the snippet.
For the syntax highlighting, I picked Prism. In fact, most of that 10 KB is Prism's JavaScript and CSS.
Smart selection
There is a "smart" selection mode. When showing code, I often want to emphasize or hide part of it. To help myself in this direction, I did a little feature that you can turn on and off via the Shift key. I select part of the text on the page, and if I press the Shift key SimPre changes the styling of the selection. Check out here https://simpre.vercel.app/#5, for example. Select some part of the text and press Shift.
Final touches
The last two things I did were a progress bar at the bottom of the page. It mimics the Reveal.js one. Just a line that grows from left to right. And, of course, the hash-based routing to copy/paste a URL to a specific slide.
Wrapping up
I recently used the frame for an online event, and it worked pretty well. I'm quite happy with the minimalism. So, check it out on github.com/krasimir/simpre or view the demo at simpre.vercel.app.