--- /dev/null
+:root {
+ --sl-cap: 20px;
+ --sl-dots: 10px;
+ --sl-transition: 0.5s;
+}
+
+div.slideshow {
+ max-width: 1000px;
+ position: relative;
+ margin: auto;
+}
+
+
+/*
+ * Slides
+ */
+
+figure.slide:first-child {
+ /* Position the first slide relatively to set the height (others are
+ * positioned absolutely to allow overlapping) */
+ position: relative;
+}
+
+figure.slide {
+ /* Position slides absolutely to allow overlapping */
+ position: absolute;
+ top: 0;
+ left: 0;
+ margin: 0;
+ /* Slides start at 0 opacity */
+ opacity: 0;
+ /* Fade out duration is double the fade in duration (approximates a cross
+ * dissolve) */
+ transition: opacity calc(2 * var(--sl-transition)) ease;
+ -webkit-transition: opacity calc(2 * var(--sl-transition)) ease;
+ -moz-transition: opacity calc(2 * var(--sl-transition)) ease;
+ z-index: -1;
+}
+
+figure.slide.active {
+ opacity: 1;
+ z-index: 0;
+ /*display: block;*/
+ transition: opacity 1s ease;
+ -webkit-transition: opacity 1s ease;
+ -moz-transition: opacity 1s ease;
+}
+
+figure.slide *,
+div.slideshow div#controls {
+ width: 100%;
+}
+
+figure.slide figcaption {
+ height: var(--sl-cap);
+ text-align: center;
+}
+
+
+/*
+ * Buttons
+ */
+
+#prev, #next, #state {
+ cursor: pointer;
+ font-weight: bold;
+ font-size: 1.5em;
+ user-select: none;
+ opacity: 0;
+ transition: 0.2s ease;
+ position: absolute;
+ color: white;
+}
+
+#prev, #next {
+ top: calc(50% - calc(var(--sl-cap) + var(--sl-dots) + 50px) / 2);
+ width: auto;
+ padding: 16px;
+ border-radius: 0 3px 3px 0;
+}
+
+#state {
+ width: 1.5em;
+ height: 1.5em;
+ text-align: center;
+ font-family: monospace;
+ top: 15px;
+ right: 15px;
+ padding: 4px;
+ border-radius: 3px;
+}
+
+#next {
+ /* Position next button on right */
+ right: 0;
+ border-radius: 3px 0 0 3px;
+}
+
+div.slideshow:hover #prev,
+div.slideshow:hover #next,
+div.slideshow:hover #state {
+ /* Show buttons on slideshow hover */
+ opacity: 100;
+ background-color: rgba(0,0,0,0.3);
+}
+
+#prev:hover, #next:hover, #state:hover {
+ /* Button hover */
+ background-color: rgba(0,0,0,0.8) !important;
+}
+
+
+/*
+ * Dot controls/indicators
+ */
+
+div.slideshow div#controls {
+ text-align: center;
+}
+
+div.slideshow div#controls span {
+ cursor: pointer;
+ height: var(--sl-dots);
+ width: var(--sl-dots);
+ margin: 0 2px;
+ background-color: #bbb;
+ border-radius: 50%;
+ display: inline-block;
+ transition: background-color 0.6s ease;
+}
+
+div#controls span.active,
+div#controls span:hover {
+ background-color: #717171 !important;
+}
--- /dev/null
+<html>
+
+ <head>
+ <link href="slideshow.css" rel="stylesheet" type="text/css" media="all" />
+ </head>
+
+ <body>
+
+ <div class="slideshow" onmouseenter="mouseEnter()" onmouseleave="mouseLeave()">
+
+ <figure class="slide">
+ <img src="test-img/1.jpg">
+ <figcaption><span>This is a long caption yeah yeah yeah</figcaption>
+ </figure>
+
+ <figure class="slide">
+ <img src="test-img/2.jpg">
+ <figcaption><span>Shorter caption</figcaption>
+ </figure>
+
+ <figure class="slide">
+ <img src="test-img/3.jpg">
+ <figcaption><span>Caption 3</figcaption>
+ </figure>
+
+ <a id="prev" onclick="prevSlide()">❮</a>
+ <a id="next" onclick="nextSlide()">❯</a>
+ <a id="state" onclick="changeState()">⏸︎</a>
+
+ <div id="controls">
+ <span class="dot" onclick="setSlide(0)"></span>
+ <span class="dot" onclick="setSlide(1)"></span>
+ <span class="dot" onclick="setSlide(2)"></span>
+ </div>
+
+ </div>
+
+ <script src="slideshow.js" type="text/javascript"></script>
+
+ </body>
+</html>
--- /dev/null
+const INTERVAL = 2000;
+const STATE_PAUSE = "⏸︎";
+const STATE_PLAY = "⏵︎";
+
+// Start at -1 - the recursive function playSlideshow() increases this to 0
+// on page load
+let currentSlide = -1;
+
+// Elements
+let slides = document.getElementsByClassName("slide");
+let dots = document.getElementsByClassName("dot");
+let stateBtn = document.getElementById("state");
+
+// Global state
+let maxIndex = slides.length - 1;
+var timeout;
+var paused = false;
+
+// Start the slideshow
+playSlideshow();
+
+// Recursive function that advances the slide and then calls itself with a
+// delay
+function playSlideshow() {
+ nextSlide();
+ timeout = setTimeout(playSlideshow, INTERVAL);
+}
+
+// Change the slide to a given index
+function setSlide(slide) {
+
+ if (slide < 0 || slide > maxIndex) {
+ console.log("Attempted to change to slide outside range");
+ return;
+ }
+
+ // Remove active class from figure and dot
+ for (i = 0; i <= maxIndex; i++) {
+ slides[i].className = slides[i].className.replace(" active", "");
+ dots[i].className = dots[i].className.replace(" active", "");
+ }
+
+ // Add active class to figure and dot
+ slides[slide].className += " active";
+ dots[slide].className += " active";
+
+ currentSlide = slide;
+}
+
+function nextSlide() {
+ // Go to the next slide
+ if (currentSlide == maxIndex) {
+ setSlide(0);
+ }
+ else {
+ setSlide(currentSlide + 1);
+ }
+}
+
+function prevSlide() {
+ // Go to the previous slide
+ if (currentSlide == 0) {
+ setSlide(maxIndex);
+ }
+ else {
+ setSlide(currentSlide - 1);
+ }
+}
+
+function mouseEnter() {
+ // Pause the slideshow
+ clearTimeout(timeout);
+ timeout = null;
+}
+
+function mouseLeave() {
+ // Start the slideshow if not manually paused
+ if (!paused && timeout == null) {
+ timeout = setTimeout(playSlideshow, INTERVAL);
+ }
+}
+
+function changeState() {
+ if (paused) {
+ // Play immediately despite mouseover
+ stateBtn.innerHTML = STATE_PAUSE;
+ timeout = setTimeout(playSlideshow, INTERVAL);
+ }
+ else {
+ // Pause until user presses play again
+ clearTimeout(timeout); // make sure timer is stopped
+ timeout = null;
+ stateBtn.innerHTML = STATE_PLAY;
+ }
+ paused = !paused;
+}