Bespoke.js is a super minimal (<1KB min'd and gzipped), modular presentation library for modern browsers, designed to foster a rich plugin ecosystem.
The core library sets up the presentation, provides a simple control API, manages events and adds classes to the slides to allow CSS transitions.
Any other functionality, from keyboard and touch interactions to bullet lists and hash routing, is implemented as a plugin. Joining the Bespoke.js plugin ecosystem is simple with Bespoke.js Plugin Generator.
Due to the highly modular nature of Bespoke.js, the quickest way to get started is with Bespoke.js Generator, a Yeoman generator that scaffolds a boilerplate presentation with a Grunt build system.
Assuming you have Node.js installed, in a blank directory:
$ npm install -g generator-bespoke
$ yo bespoke
In your newly scaffolded project, you can use the following Grunt tasks:
$ grunt server
to run a preview server with LiveReload.$ grunt deploy
to deploy to GitHub Pages.$ grunt
to compile static assets to 'public'.
For more detailed instructions, check out the Bespoke.js Generator repo.
If you'd prefer to craft a new presentation from scratch, you can install Bespoke.js from Bower with bower install bespoke.js
, from npm with npm install bespoke
, or manually download either the production version or the development version. The Bespoke.js core is extremely lightweight, so you'll probably want to include some plugins.
It's completely up to you which tags you use, but the following is a good starting point:
<link rel="stylesheet" href="path/to/my/theme.css">
<article id="presentation">
<section>Slide 1</section>
<section>Slide 2</section>
<section>Slide 3</section>
</article>
<script src="bespoke.min.js"></script>
<script src="bespoke-keys.min.js"></script>
<script src="bespoke-touch.min.js"></script>
<script src="path/to/my/script.js"></script>
Decks are created by selecting the parent element with the from(selector[, plugins])
method. Once a parent element is selected, the child elements become slides.
var deck = bespoke.from('#presentation', {
// Plugins:
keys: true,
touch: true
});
Programmatically control the state of the presentation.
var deck = bespoke.from('#presentation');
// Next slide
deck.next();
// Previous slide
deck.prev();
// Go to a specific slide
deck.slide(0);
// Get the active slide index
deck.slide(); // 0
To create your own custom deck styles, Bespoke.js provides the necessary classes to your elements.
bespoke-parent | The deck's containing element |
bespoke-slide | Every slide element |
bespoke-active | The active slide |
bespoke-inactive | All inactive slides |
bespoke-before | All slides before the active slide |
bespoke-before-n | All slides before the active slide, with n value incrementing |
bespoke-after | All slides after the active slide |
bespoke-after-n | All slides after the active slide, with n value incrementing |
Plugins are specified when instantiating your presentation, like so:
var deck = bespoke.from('#presentation', {
// Plugins:
keys: true,
touch: true
});
All official plugins can be installed from Bower or npm, e.g. $ bower install bespoke-keys
or $ npm install bespoke-touch
- bespoke-keys for keyboard interaction.
- bespoke-touch for touch interaction.
- bespoke-bullets for animated bullet lists.
- bespoke-scale for responsive slide scaling.
- bespoke-hash for hash routing.
- bespoke-state for slide-specific deck styles.
- bespoke-progress for progress bars.
- bespoke-forms for form element support.
- bespoke-loop for looped presentations.
- bespoke-vcr for recording and playback.
- bespoke-fx by @ebow, for configurable slide transitions.
- bespoke-dir by @ryanseddon, for direction-based deck classes.
- bespoke-spotlight by @mobz, for quick-searching slide content.
- bespoke-blackout by @originell, for temporarily blacking out the screen.
- bespoke-secondary by @joelpurra, for slide notes in a secondary window.
- bespoke-advanced by @joelpurra, to automatically advance slides on a timer.
- bespoke-jumpy by @joelpurra, for keyboard shortcuts to jump straight to specific slides.
- bespoke-run by @mcollina, for running
<code>
snippets.
If you'd like your plugin added to this list, let me know.
If you already have a reference to a DOM node, you can pass it directly to the from
method.
bespoke.from(element);
Individual deck instances can be created and controlled separately.
// First deck instance
var one = bespoke.from('#deck-one');
one.next();
one.prev();
one.slide(0);
// Second deck instance
var two = bespoke.from('#deck-two');
two.next();
two.prev();
two.slide(0);
The following properties are available on each instance.
Note: The optional eventData
parameter is an object that will be merged with the event
object in subsequent event handlers.
next([eventData]) | Next slide. |
prev([eventData]) | Previous slide. |
slide([index[, eventData]]) | Get or set the active slide index. |
on(event, callback) | Attach event handlers |
fire(event[, eventData]) | Fire custom events. This method is primarily designed for plugin authors. |
parent | The deck's parent element |
slides | An array of slide elements |
Events are bound via the deck instance. Each event is passed an event object containing a reference to the relevant slide and its index.
deck.on(eventName, function(event) {
event.slide; // Relevant slide
event.index; // Index of relevant slide
// Prevent default functionality (for deck interaction events only)
return false;
});
In most cases, you will only need to use these standard events.
activate | A slide has been activated. event.slide is the activated slide. |
deactivate | A slide has been deactivated. event.slide is the deactivated slide. |
These events are fired when the deck has been interacted with, but before the interaction has had any effect.
This allows you to intercept the default behaviour by returning false
from the event handler.
next | The next slide has been requested, even if last slide is active. event.slide is the current slide. |
prev | The previous slide has been requested, even if first slide is active. event.slide is the current slide. |
slide | A specific slide has been requested. event.slide is the requested slide. |
When binding events, the on
method returns a function that can be used to remove the event handler.
var off = deck.on('activate', function() {
// ...
});
// Unbind event
off();
Want a boilerplate plugin? Use the official Bespoke.js Plugin Generator.
If you'd like to learn by example, check out the list of existing plugins.
Plugins are simply functions that are called when presentations are created.
They are passed a deck instance which allows you to interact with the deck's state, bind events and modify its elements.
// Creating the plugin
bespoke.plugins.myPlugin = function(deck) {
deck.on('activate', function(e) {
console.log('Activated slide ' + (e.index + 1) + ' of ' + deck.slides.length);
});
};
The plugin can now be provided to the second parameter of the from(selector[, plugins])
method.
// Using the plugin
bespoke.from('#presentation', { myPlugin: true });
Note: Your plugin won't run if the option value provided is false
.
If your plugin needs some configurability, options can be passed through as the second parameter.
// Creating the plugin with options
bespoke.plugins.myPlugin = function(deck, options) {
var showTotal = options && options.showTotal;
deck.on('activate', function(e) {
console.log('Activated slide ' + (e.index + 1) +
(showTotal ? ' of ' + deck.slides.length : ''));
});
};
// Using the plugin with options
bespoke.from('#presentation', {
myPlugin: {
showTotal: true
}
});
Additional event data can be supplied to next
, prev
and slide
, which is merged with the final event
object in subsequent event handlers.
This functionality is particularly useful if you need to differentiate between events caused by your plugin, and those caused by your end users or other plugins.
bespoke.plugins.myPlugin = function(deck) {
// Differentiating our plugin's events
deck.on('activate', function(event) {
if (event.foo === 'bar') {
// Triggered by my plugin...
} else {
// Triggered by end user, or another plugin...
}
});
// Providing custom event data
deck.next({
foo: 'bar'
});
};
- Build Wars: Gulp vs Grunt by Mark Dalgleish
- Bespoke.js: The Road to 1KB by Mark Dalgleish
- DIY Presentations With Bespoke.js by Mark Dalgleish
- Javascript's Slightly Stricter Mode by Glen Maddern
- The Trials of Transition Height: Auto by Charlie Gleason
- Welcome Our New ES5 Overlords by Mike MacCana
- Rapid Web App Dev With Yeoman by Michael Taranto
- Projects vs Products by John Barton
- Learn You The Node.js For Much Win by Rod Vagg
- A Real Database Rethink by Rod Vagg
- Feature Flags with Directives by Michael Taranto
- Introduction to hapi by Mark Wolfe
- How to Cook a Graph Database in a Night by Matteo Collina
- Asynchronous JavaScript Interfaces by Mariusz Nowak
Made a presentation with Bespoke.js? Let me know.
Contact me on GitHub or Twitter: @markdalgleish