Take Five!

A semantically-consistent pure CSS slide renderer

Introduction

Take Five! is a slide renderer written in pure CSS and oriented to the semantics of HTML (semantic nodes, microdata, microformats, RDFa). It can be used as a simple lightbox for images and videos, or as a complete slide viewer for complex contents. Before further reading, please visit the live demo.

Getting started

In order to create a slide with Take Five! all you need to do is to include takefive(.min).css in your document, then use one of the following basic syntaxes:

Etc. etc.

If you want Take Five! to behave responsively on small screens, add

<meta name="viewport" content="width=device-width, initial-scale=1.0">

to the <head> of your page. For more advanced tweaks, such as touch and keyboard events and cleaning the browser history, see the § Plugins and § FAQ sections below or visit the JavaScript Add-Ons page.

Example

The following minimalistic code

<p style="text-align: center; font-size: 2em;">
    <a href="#photo-of-the-day">Photo of the day</a>
</p>

<article class="slide" id="photo-of-the-day">
    <figure>
        <img src="media/1.jpg" alt="Photo of the day" />
        <figcaption>Photo of the day</figcaption>
    </figure>
    <nav>
        <a href="#nowhere" rel="parent">My diary</a>
    </nav>
</article>

will produce this result.

Philosophy

The philosophy of Take Five! is that of facilitating a general semantics for expressing a slide with HTML (independently of how this is rendered), rather than focusing on means to express a layout.

In the original meaning of the term a slide is a single page projected on a wall through a beamer or a lightbox. The term does not describe weather a slide is blue or red, or whether it contains a picture or text, but only that its content must fit in a single page and the lights in the room are usually switched off.

A slide can also be taken in the hands and read in its physical form with the lights on, thus the most characterizing aspect of a slide is actually its conciseness and independence, not how it is displayed.

With its inception HTML5 introduced six semantic elements (<article>, <aside>, <footer>, <header>, <nav> and <section>), one of which is well suited to represent self-contained compositions: the <article> element.

The <article> element represents a complete, or self-contained, composition in a document, page, application, or site. This could be a magazine, newspaper, technical or scholarly article, an essay or report, a blog or other social media post.

For non-semantic properties, HTML possesses also a class attribute, which although cannot describe what things are, can still be used to describe how things look. For describing an <article> that is projected on a wall it is then possible to write <article class="slide">.

Since a class attribute does not express semantics, such a slide remains primarily an <article> element, and <article class="slide"> translates as “<article> that looks like a slide” (but not as “<article> that represents a slide”).

Take Five! is a slide renderer written in CSS, which means that it can only decide how things look, not what they are. However, in deciding what to give a style to, Take Five! focuses on elements that have a meaning (semantic elements), instead of generic elements that simply claim a layout (as it would be, for instance <div class="i-want-a-layout"> ... </div>).

Concretely speaking this has two effects:

A densely populated slide (<article>), besides its main content, can possess a title, a footer and some other information aside; therefore when this happens Take Five! gives a style to the <header>, <footer> and <aside> elements nested within it. In an internet context a slide can also be linked to other slides, hence Take Five! gives a layout to the <nav> element as well. A <nav> element however does not make much sense in a printed page, so when the slide is “taken in the hands” (printed) the <nav> element is not displayed.

Elements are not the only things that can express semantics in HTML. A further place that conveys semantics is the rel attribute assigned to links. Therefore, when a slide is displayed on a screen and the <nav> element is shown, Take Five! relies on the rel attributes for deciding how to style each link and what role it has in the viewport.

The viewport

Each slide (<article id="my-hashtag" class="slide"> ... </article>)

Several slides belonging to a single gallery can be logically packed together within any parent element – normally a <section> element is chosen for it.

Children elements

Direct children of the viewport

The way this CSS operates is quite simple: the first direct child of the <article class="slide"> element found to be neither a <header>, nor a <nav>, nor a <footer>, nor an <aside> will be placed at the center of the viewport (from now on, this will be called “the fifth element” or “the <leeloo> element”, after Luc Besson's movie). Further direct children that match the previous condition, if present, will be displayed on the right side of the screen, in their normal order.

On large screens, when present, the <header>, <nav>, <footer> and <aside> elements are positioned to fill the entire space of the viewport.

Schematic of the viewport

Schematic of the viewport

On small screens everything is aligned in one scrollable single column, where only the <header>, if present, is always positioned on top of the slide, while all other elements follow their natural disposition assigned via HTML

Schematic of the viewport on small devices

Schematic of the viewport on small devices

It is possible to test the order of the elements by visiting the Schematic page.

Non-direct children of the viewport (sub-children)

Sub-children of the viewport are displayed normally, in their natural order and without any style applied beyond basic layout. The only exception is made for links that are children of the main <nav>, which are considered as internal navigation links and styled according to their rel attribute (here the list of the supported keywords).

Nested galleries

Galleries can be nested within other galleries, possibly relying on <section> elements. A link to the child gallery is usually given by an anchor with the "child" keyword assigned to its rel attribute.

<p style="text-align: center; font-size: 2em;">
    <a href="#slide-1">Nested galleries</a>
</p>

<section>
    <article id="slide-1" class="slide">
        <element-to-be-slided />
        <nav>
            <a href="#slide-1-1" rel="child">Nested gallery</a>
            <a href="#nowhere" rel="parent">My diary</a>
        </nav>
    </article>
    <section>
        <article id="slide-1-1" class="slide">
            <element-to-be-slided />
            <nav>
                <a href="#slide-1" rel="parent">Item #1</a>
            </nav>
        </article>
    </section>
</section>

A more articulated example is available here. For more information on how to create a gallery tree it is possible to follow this tutorial.

Available classes

Take Five! relies only on one single class: the "slide" class applied to an <article> element. Further classes are available for optional features, although none of them is structurally required. The complete list of available classes is shown below.

Classes for the <article> element (i.e., the slide)

Class Declared as Meaning
"slide" article.slide Declares an <article> element as a slide
"verbose" article.slide.verbose When a slide is declared with the "verbose" class a label will be added by default to all the links nested inside the main <nav>. Without "verbose" only a[rel="parent"] is displayed with a label.
"foyer" article.slide.foyer When a slide is declared with the "foyer" class links with a known rel attribute nested inside the main <nav> will appear in the left menu together with links with an unknown rel attribute. Known rel attributes are "parent", "next", "prev", "first", "last", "index", "attachment", "bookmark", "author", "discussion", "tag" and "child". The "tag" and "child" keywords always place the links they generate in the menu.
"wrap" article.slide.wrap A transparent layer will cover the entire slide. As scrolling will become inaccessible, there should be no scrollable content when a slide is declared as "wrap".
"pinned" article.slide.pinned A slide declared with the "pinned" class will not generate a clickable background layer to exit (see example).
"no-hashtag" article.slide.no-hashtag It is possible to prevent the hashtag from being displayed on the bottom-right corner of the viewport by using the "no-hashtag" class – as in <article class="slide no-hashtag" id="my-hashtag"> ... </article>.
"nonadaptive" article.slide.nonadaptive Automatic adjustments for small screens or particular devices will be disabled if the "nonadaptive" class is present. Slides will look alike on every screen.

All the classes above can form compound class names, as in <article class="verbose foyer wrap pinned nonadaptive no-hashtag slide"> ... </article>.

Classes for the links of the main <nav>

ClassDeclared asMeaningDomain
"with-label" article.slide > nav:first-of-type a[href].with-label Links declared with the "with-label" class will be accompanied by a label. verbosity
"no-label" article.slide > nav:first-of-type a[href].no-label Links declared with the "no-label" class will not be accompanied by a label.
"tab" article.slide > nav:first-of-type a[href].tab Links declared with the "tab" class will appear in the <nav> menu as tabs. appearance
"plate" article.slide > nav:first-of-type a[href].plate Links declared with the "plate" class will appear in the <nav> menu as plates. By default only a[rel="child"] is styled as such.
"vision" article.slide > nav:first-of-type a[href].vision Links declared with the "vision" class will not appear in the <nav> menu at all, but as notifications on the top right corner of the viewport. This class is meaningful only for links that generate buttons via rel attribute.
"no-rel" article.slide > nav:first-of-type a[href][rel].no-rel Links declared with the "no-rel" class will be treated as links with an unknown rel attribute (as in <a href="#sample-slide" rel="next" class="no-rel">Sample slide</a> or <a href="#sample-slide" rel="next" class="no-label no-rel">Sample slide</a>). The rel attribute is still taken into account to generate a label if necessary. relation

Classes belonging to the same domain are alternative to each other. Classes belonging to different domains can form compound class names, as in <a href="mailto:me@example.org" rel="author" class="plate with-label no-rel">John Doe</a>.

General classes

ClassDeclared asMeaningDomain
"z-low" article.slide .z-low The "z-low" class assigns a low z-index to the element (lower than the background layer). Before applying this class to subchildren keep in mind that all direct children of the slide have a z-index already assigned; this needs to be canceled by applying the "z-none" class on the direct child. stacking context
"z-mid" article.slide .z-mid The "z-mid" class assigns a middle z-index to the element (similar to that assigned by default to the <aside> and <footer> elements). Before applying this class to subchildren keep in mind that all direct children of the slide have a z-index already assigned; this needs to be canceled by applying the "z-none" class on the direct child.
"z-high" article.slide .z-high The "z-high" class assigns a high z-index to the element (above everything else). Before applying this class to subchildren keep in mind that all direct children of the slide have a z-index already assigned; this needs to be canceled by applying the "z-none" class on the direct child.
"z-auto" article.slide .z-auto The "z-auto" class leaves the z-index property assigned by default by the CSS (if given).
"z-none" article.slide .z-none The "z-none" class reverts any z-index possibly assigned by the CSS.
"clean" article.slide .clean The "clean" class removes borders and shadow from an element. decoration
"roomy" article.slide .roomy The "roomy" class sets the following properties on big screens: max-height: 100%; overflow: auto; padding: 0 .5em 0 0;. On small screens these are transformed into: max-height: none; overflow: visible; padding: 0;. size

Classes belonging to the same domain are alternative to each other. Classes belonging to different domains can form compound class names, as in <figure class="z-high roomy clean"> ... </figure>.

Supported data-* attributes

Attribute Declared as Meaning
data-context article.slide[data-context] The slide container (article.slide) can optionally possess a data-context attribute, whose content will be displayed on the top left side of the viewport – as in <article class="slide" data-context="Hello world!" id="my-hashtag"> ... </article> (it takes 2.5 seconds to show up, so you must wait to see it). See also note below.
data-label article.slide a[href][data-label] The data-label attribute can be used to create custom labels for individual links, or replace the default labels in the main <nav>'s links that possess a rel attribute.
A slide in Take Five! is an <article> element; such element delimits a self-contained content, meaning that if all the other HTML except the <article> are removed the content would still make sense to a reader. The function of the data-context attribute is that of contextualizing the <article>'s content. It is confined to an attribute, so that the content of the <article> remains still independent. It usually contains a small motto, a copyright notice or a mention of the hosting website. It should be used in complete freedom.

Plugins

This is the optional section of the framework. Some of these plugins could have been merged with the main CSS, but they have been kept separate to preserve modularity. Please feel free to bring new ideas.

takefive-counters.css
Plugin for numbering the slides
takefive-glyphs.css
Plugin for editing the default glyphs used for the navigation buttons or generating new ones
takefive-i18n-*.css
Internationalization style sheets
takefive-maxsize.css
Plugin for increasing the size of the <leeeloo> element when there are no visible links in the main <nav> and no further content besides the <leeeloo> element itself
takefive-polaroid.css
Very minimal plugin for creating a simple Polaroid-like border in pictures
takefive-slides.css
Minimal plugin for hiding all <section> elements with class "slides" on screen, in order to display them only on the printed page.
takefive-toolbar.css
Plugin for creating a toolbar of clickable <span> / <div> elements, or openable <details> elements, to be completed via JavaScript

In addition to the CSS plugins above, you can have a look at the following JavaScript snippets, to be simply included in the document as such (please keep in mind that js-add-ons/takefive-clean-empty-hash.js and js-add-ons/takefive-clean-history.js are alternative to each other).

For a live example, please visit the JavaScript Add-Ons page.

FAQ

How many class names does Take Five! require?
Only one class name is required: the "slide" class applied to an <article> element. Optionally the latter supports also the following six classes: "verbose", "foyer", "wrap", "pinned", "no-hashtag" and "nonadaptive". Six optional classes are also available to navigation links: "with-label", "no-label", "tab", "plate", "vision" and "no-rel".
How many data-* attributes does Take Five! support?
Two data-* attributes are supported: data-context, optionally applied to the slide, and data-label, optionally applied to links.
Does Take Five! require a particular DOM tree?
The only (aesthetic and logical) requirement is a custom element nested inside an <article class="slide"> element, to be displayed at the center of the screen (plain text direct child of the slide is always displayed on the right side of the screen). When present, the CSS takes also care of styling other children element of the main <article>, but these are not required.
Can I apply also my own style sheets on top of this CSS?
Of course, feel free to re-style every element in your slides. For your own sake you should try not to affect the following properties of the <leeloo> element: margin, position, left, top, box-sizing, transform, animation and z-index. No particular caution is required for <leeloo>'s children, so if you want to show fullscreen an element that relies on animations (i.e., it uses the animation property), nest it inside a blank <div> element or any other container of your choice.
I have many images in my page. How can I avoid that the browser preloads all of them?
If you are interested in this slide renderer probably you don't want to leave any semantic content out of your HTML (as it would be to load the slides through JavaScript, for example). The correct way to deal with lazy loading is to set a loading="lazy" attribute on <img> and <iframe> elements.
How can I enable the escape key for exiting the slides?
You need to use JavaScript. I intentionally did not write any line of JavaScript code for this CSS, but you can work around it. Here follows a basic script for exiting the slides by pressing the escape key.
/* close the slide currently open or do nothing */
function closeCurrentSlide () {

    var oSlide = document.querySelector("article.slide:target");

    if (oSlide) {

        var oExitLink = oSlide.querySelector("nav:first-of-type a[rel~=\"parent\"]");
        location.assign(oExitLink ? oExitLink.href : "#");

    }

}

window.addEventListener("keyup", function (oEvt) {

    if (oEvt.key === "Escape") {

        closeCurrentSlide();

    }

}, false);
For a downloadable version, please see js-add-ons/takefive-keyboard-events.js.
How can I swipe left and right through the slides with my smartphone?
You need to handle touch events via JavaScript. Here is a possible minimal snippet:
(function() {

    var nDownX = null, nDownY = null;

    function handleTouchStart (oEvt) {

        const oFirstTouch = oEvt.touches[0];

        nDownX = oFirstTouch.clientX;
        nDownY = oFirstTouch.clientY;

    };

    function handleTouchMove (oEvt) {

        if (!nDownX || !nDownY) {

            return;

        }

        var
            oLink, sRel = null,
            nDiffX = nDownX - oEvt.touches[0].clientX,
            nDiffY = nDownY - oEvt.touches[0].clientY;

        switch (Math.abs(nDiffX) > Math.abs(nDiffY) ? nDiffX > 0 ? 3 : 2 : nDiffY > 0 ? 1 : 0) {

            /*  Cases: `0` -> down swipe, `1` -> up swipe, `2` -> right swipe, `3` -> left swipe  */
            case 2: sRel = "prev"; break;    /*  Right swipe  */
            case 3: sRel = "next"; break;    /*  Left swipe  */

        }

        nDownX = null;
        nDownY = null;

        sRel && (oLink = this.querySelector("nav:first-of-type a[href][rel~=\"" + sRel + "\"]")) && location.assign(oLink.href);

    };

    window.addEventListener("DOMContentLoaded", function () {

        const aSlides = document.querySelectorAll("article.slide");

        for (var nIdx = 0, nLen = aSlides.length; nIdx < nLen; nIdx++) {

            aSlides[nIdx].addEventListener('touchstart', handleTouchStart, false);
            aSlides[nIdx].addEventListener('touchmove', handleTouchMove, false);

        }

    }, false);

})();
For a downloadable version, please see js-add-ons/takefive-touch-events.js.
Too much history!
One of the useful outcomes of relying on an hashtag in the URL is that the slides visited leave a trace in the browser history and can produce permalinks. Sometimes however the history can be too much. For these cases it is possible to use JavaScript to do a partial reset of the history every time a gallery is closed:
(function () {

    var bOpenedWithSlide = false, nHistCount = -1;

    function newSlideClicked () {

        nHistCount--;

    }

    function exitClicked (oEvt) {

        if (bOpenedWithSlide) {

            /*  The page was loaded with a slide open  */
            bOpenedWithSlide = false;

        } else {

            history.go(nHistCount);
            oEvt.preventDefault();

        }

        nHistCount = -1;

    }

    addEventListener("DOMContentLoaded", function () {

        var
            elIter,
            aLinks = document.querySelectorAll("article.slide a"),
            elTest = document.createElement("a");

        bOpenedWithSlide = Boolean(document.querySelector("article.slide:target"));
        elTest.href = location.href;

        for (var nLen = aLinks.length, nIdx = 0; nIdx < nLen; nIdx++) {

            elIter = aLinks[nIdx];
            elTest.hash = elIter.hash || "#";

            if (elTest.href === elIter.href) {
                elIter.addEventListener("click",
                    elIter.hash && document.querySelector("article.slide" + elIter.hash) ?
                        newSlideClicked
                    :
                        exitClicked
                );
            }

        }

    }, false);

})();
For a downloadable version, please see js-add-ons/takefive-clean-history.js.
Please keep in mind that this script is alternative to js-add-ons/takefive-clean-empty-hash.js.
How will the slides look on a printed page?
This CSS establishes minimal rules for the printed version of the slides (it does not attempt for example to center them vertically, as this could disrupt the printed version of your website), the rest is left to your own CSS. Writing a good CSS for printing is a forgotten art, so you should think carefully about it. A good starting point might be this CSS.
I really don't like the # or #whatever link for exiting the slides. How can I do?
The empty hash (#) is the semantically correct way to refer to the page in its entirety, so if that is what you are referring to there are no better ways to express it, especially for the sake of search engines. It is possible however to use JavaScript for cleaning the address bar after clicking on any link that ends with an empty hash:
window.addEventListener("DOMContentLoaded", function () {

    var aEmptyHashLinks = document.querySelectorAll("a[href$=\"#\"]");

    for (var nIdx = 0; nIdx < aEmptyHashLinks.length; nIdx++) {

        aEmptyHashLinks[nIdx].addEventListener("click", function (oEvt) {

            location.hash = "";
            history.replaceState(null, "", this.href.split('#')[0]);
            oEvt.preventDefault();

        });

    }

}, false);
For a downloadable version, please see js-add-ons/takefive-clean-empty-hash.js.
The history.replaceState() method might not be available locally, but only when the page is hosted in a webserver. Please keep in mind that this script is alternative to js-add-ons/takefive-clean-history.js.
Can I use Take Five! for commercial projects?
Take Five! is released under the terms of the GPL license, which encourages commercial use. The author of the default "Victoria Typewriter" font however asks for a fee for commercial use. So in this case you either contact them or choose another font (please have a look at dist/fonts for possible alternatives). Here is their message:
Hello, you've just downloaded the "Victoria Typewriter" font (2021) which has been created by Lukas Krakora and is free for non-commercial use only.

If you'd like to use the font commercially please contact me at my email krraaa@yahoo.com to get an information about pricing.

I can add any extra characters or customize the font for purchasers of the commercial license.

Any donation from non-commercial users to my Paypal krraaa@yahoo.com is welcome.

You can find my other fonts on www.typewriterfonts.net

And my FB page is: facebook.com/typewriterfonts

Thank you.

Lukas Krakora

Issues

All animations fire on page load

There is currently a bug in Chromium that causes the CSS to explode from user-agent defaults to applied styles on page load (discussion at crbug.com/332189 and crbug.com/167083). A way to fix it is to add

<script>"whatever";</script>

to the <head> of the page (any text within double quotes will do).

To test weather your browser is affected by the bug you can visit lab.laukstein.com/bug/input.

The z-index collision

The <article class="slide"> element has the z-index property set to 10000, hence no other elements within the document must have a z-index higher than 10000 in order not to be displayed above the slides. If there is no way to prevent that, it is possible to assign a higher z-index to the slides manually,

<article id="my-hashtag" class="slide" style="z-index: 99999;"> ... </article>

or nest them within a higher stacking context.

<div style="position: relative; z-index: 99999;">
    <article id="my-hashtag" class="slide"> ... </article>
</div>

For more details, see the table of all the internal z-indices assigned by Take Five!.

Internet Explorer

Internet Explorer is not officially supported. It is possible to obtain some results by adding the following style sheet to the document (but you should seriously consider to abandon hope):

/*  IE only  */
@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {

    article.slide > nav:first-of-type a[href] {
        visibility: inherit !important;
    }

}

Notes

Slides

Schematic of the viewport
Schematic of the viewport
Schematic of the viewport on small devices
Schematic of the viewport on small devices