<scroll-snap-carousel alignment="center">
<iframe src="https://www.youtube.com/embed/3ZTvsUeQkOM?si=iNSnBcZRKWsMUB2e" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
<iframe src="https://www.youtube.com/embed/7Dr5LW9xnSs?si=aLyux8R2QGk61XwU" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
<iframe src="https://www.youtube.com/embed/31lbp1dolAI?si=B1SYEzLmlZ8QN3pG" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
<iframe src="https://www.youtube.com/embed/onthvMAIpUI?si=XZK9y1OkfAhMJUFY" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
<iframe src="https://www.youtube.com/embed/Ij-1kXYKD3c?si=ha_b0VuYevv-6q-V" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
<iframe src="https://www.youtube.com/embed/RFdLLDmTTk8?si=1RVxxa3Hw-nL8ph8" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
<p slot="disclaimer">
Check out <a href="https://rayray.komi.io/" target="_blank" rel="noopener noreferrer">RayRay!</a>
</p>
</scroll-snap-carousel>
:root {
align-items: center;
background: linear-gradient(45deg, #82ff2e, #ff7c7c);
display: flex;
height: 100%;
justify-content: center;
}
iframe {
aspect-ratio: 16/9;
border-radius: 10px;
width: 75%;
max-height: 700px;
}
p {
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
"Liberation Mono", "Courier New", monospace;
font-size: 14px;
padding-top: 8px;
}
scroll-snap-container {
margin: 16px;
}
/**
* A custom element that creates a scrollable carousel with snap points.
* Based on an example from this article: https://web.dev/articles/css-scroll-snap
*
* @attr {'start' | 'center' | 'end' | 'none'} alignment - The alignment of the carousel scroll snap.
*/
class ScrollSnapCarousel extends HTMLElement {
/**
* Define the properties of the custom element.
*/
static get observedAttributes() {
return ["alignment"];
}
/**
* Create a new instance of the custom element.
*/
constructor() {
super();
this.shadow = this.attachShadow({ mode: "open" });
this.alignment = "start";
this.scrollToNextPage = this.scrollToNextPage.bind(this);
this.scrollToPreviousPage = this.scrollToPreviousPage.bind(this);
this.calculateGalleryItemSize = this.calculateGalleryItemSize.bind(this);
}
/**
* Called when an attribute on the custom element is changed.
*/
attributeChangedCallback(name, oldValue, newValue) {
if (name === "alignment") {
this.alignment = newValue;
}
}
/**
* Called when the element is added to the DOM.
*/
connectedCallback() {
this.render();
this.gallery = this.shadowRoot.querySelector("#paginated-gallery");
this.galleryScroller = this.gallery.querySelector(".gallery-scroller");
this.calculateGalleryItemSize();
this.gallery
.querySelector("button.next")
.addEventListener("click", this.scrollToNextPage);
this.gallery
.querySelector("button.previous")
.addEventListener("click", this.scrollToPreviousPage);
/** Recalculate the gallery item size when the window is resized. */
window.addEventListener("resize", this.calculateGalleryItemSize);
}
/**
* Called when the element is removed from the DOM.
*/
disconnectedCallback() {
window.removeEventListener("resize", this.calculateGalleryItemSize);
}
/**
* Calculate the size of the gallery items.
*/
calculateGalleryItemSize() {
const slotElement = this.galleryScroller.querySelector("slot");
const nodes = slotElement.assignedNodes({ flatten: true });
const firstSlottedElement = nodes.find(
(node) => node.nodeType === Node.ELEMENT_NODE
);
this.galleryItemSize = firstSlottedElement.clientWidth;
}
/**
* Scroll the carousel to the previous page.
*/
scrollToPreviousPage() {
this.galleryScroller.scrollBy(-this.galleryItemSize, 0);
}
/**
* Scroll the carousel to the next page.
*/
scrollToNextPage() {
this.galleryScroller.scrollBy(this.galleryItemSize, 0);
}
/**
* Handles the rendering of the component markup and CSS.
*/
render() {
this.shadowRoot.innerHTML = `
<style>
* {
scroll-behavior: smooth;
}
::slotted(*) {
margin: 0 16px;
min-width: 75%;
position: relative;
scroll-snap-align: ${this.alignment};
}
.gallery {
position: relative;
}
.gallery-scroller {
-webkit-overflow-scrolling: touch;
align-items: center;
display: flex;
overflow-x: scroll;
overflow-y: hidden;
scroll-snap-type: x mandatory;
}
button {
background-color: rgba(0,0,0,0.5);
background-position: 50% 50%;
background-repeat: no-repeat;
border-radius: 4px;
height: 30px;
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 30px;
z-index: 1;
}
button:hover {
cursor: pointer;
}
button.next {
background-image: url('data:image/svg+xml;charset=utf-8,<svg width="18" height="18" viewBox="0 0 34 34" xmlns="http://www.w3.org/2000/svg"><title>Shape</title><path d="M25.557 14.7L13.818 2.961 16.8 0l16.8 16.8-16.8 16.8-2.961-2.961L25.557 18.9H0v-4.2z" fill="%23FFF" fill-rule="evenodd"/></svg>');
right: 10px;
}
button.previous {
background-image: url('data:image/svg+xml;charset=utf-8,<svg width="18" height="18" viewBox="0 0 34 34" xmlns="http://www.w3.org/2000/svg"><title>Shape</title><path d="M33.6 14.7H8.043L19.782 2.961 16.8 0 0 16.8l16.8 16.8 2.961-2.961L8.043 18.9H33.6z" fill="%23FFF" fill-rule="evenodd"/></svg>');
left: 10px;
}
</style>
<div id="paginated-gallery" class="gallery">
<button class="previous" aria-label="Previous"></button>
<button class="next" aria-label="Next" ></button>
<div class="gallery-scroller">
<slot></slot>
</div>
</div>
<slot name="disclaimer"></slot>
`;
}
}
window.customElements.define("scroll-snap-carousel", ScrollSnapCarousel);
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.