• HTML

    my-slider.html html

    <my-slider>
    	<h2 class="visually-hidden">Slides</h2>
    	<div class="slides">
    		<div class="slide active">
    			<h3>Slide 1</h3>
    			<hello-world>
    				<label>Your name<br>
    					<input type="text">
    				</label>
    				<p>Hello, <span>World</span>!</p>
    			</hello-world>
    		</div>
    		<div class="slide">
    			<h3>Slide 2</h3>
    			<spin-button value="0" zero-label="Add to Cart" increment-label="Increment">
    				<button type="button" class="decrement" aria-label="Decrement" hidden>−</button>
    				<p class="value" hidden>0</p>
    				<button type="button" class="increment primary">Add to Cart</button>
    			</spin-button>
    		</div>
    		<div class="slide">
    			<h3>Slide 3</h3>
    			<rating-feedback>
    				<form>
    					<rating-stars>
    						<fieldset>
    							<legend class="visually-hidden">Rate</legend>
    							<label>
    								<input type="radio" class="visually-hidden" name="rating" value="1">
    								<span class="label">☆</span>
    							</label>
    							<label>
    								<input type="radio" class="visually-hidden" name="rating" value="2">
    								<span class="label">☆</span>
    							</label>
    							<label>
    								<input type="radio" class="visually-hidden" name="rating" value="3">
    								<span class="label">☆</span>
    							</label>
    							<label>
    								<input type="radio" class="visually-hidden" name="rating" value="4">
    								<span class="label">☆</span>
    							</label>
    							<label>
    								<input type="radio" class="visually-hidden" name="rating" value="5">
    								<span class="label">☆</span>
    							</label>
    						</fieldset>
    					</rating-stars>
    					<div class="feedback" hidden>
    						<header>
    							<button button="button" class="hide" aria-label="Hide">×</button>
    							<p hidden>We're sorry to hear that! Your feedback is important, and we'd love to improve. Let us know how we can do better.</p>
    							<p hidden>Thank you for your honesty. We appreciate your feedback and will work on making things better.</p>
    							<p hidden>Thanks for your rating! If there's anything we can improve, we'd love to hear your thoughts.</p>
    							<p hidden>We're glad you had a good experience! If there's anything that could make it even better, let us know.</p>
    							<p hidden>Thank you for your support! We're thrilled you had a great experience. Your feedback keeps us motivated!</p>
    						</header>
    						<fieldset>
    							<label for="rating-feedback">Describe your experience (optional)</label>
    							<textarea id="rating-feedback"></textarea>
    							<input-button disabled>
    								<button type="submit" class="primary" disabled>Submit</button>
    							</input-button>
    						</fieldset>
    					</div>
    				</form>
    			</rating-feedback>
    		</div>
    	</div>
    	<button type="button" class="prev" aria-label="Previous">‹</button>
    	<button type="button" class="next" aria-label="Next">›</button>
    	<div class="dots">
    		<span class="active"></span>
    		<span></span>
    		<span></span>
    	</div>
    </my-slider>
    CSS

    my-slider.css css

    my-slider {
    	display: flex;
    	overflow: hidden;
    	aspect-ratio: 16 / 9;
    	position: relative;
    	
    	.slides {
    		display: flex;
    		align-items: center;
    		justify-content: center;
    		width: 100%;
    	}
    	
    	.slide {
    		min-width: 100%;
    		text-align: center;
    
    		&:not(.active) {
    			display: none;
    		}
    	}
    	
    	.dots {
    		position: absolute;
    		bottom: 0;
    		left: 0;
    		width: 100%;
    		display: flex;
    		justify-content: center;
    		gap: var(--space-xs);
    		padding-block: var(--space-s);
    
    		> span {
                width: var(--space-s);
                height: var(--space-s);
                border-radius: 50%;
                background-color: var(--color-text);
    			opacity: var(--opacity-translucent);
    
                &.active {
                    opacity: var(--opacity-solid);
                }
    		}
    	}
    	
    	> button {
    		position: absolute;
    		top: 2%;
    		height: 96%;
    		border: 0;
    		border-radius: var(--space-xs);
    		background: transparent;
    		padding: var(--space-m);
    		font-size: var(--font-size-xxl);
    		color: var(--color-text);
    		opacity: var(--opacity-dimmed);
    		transition: opacity var(--transition-short) var(--easing-inout);
    
    		&:hover,
    		&:active,
    		&:focus {
                opacity: var(--opacity-solid);
    			background-color: rgba(0, 0, 0, 0.05);
            }
    
    		&:active {
    			background-color: rgba(0, 0, 0, 0.1);
    		}
    
    		&.prev {
    			left: 1%;
    		}
    
    		&.next {
                right: 1%;
            }
    	}
    }
    TS

    my-slider.ts ts

    import { type SignalValueProvider, UIElement, toggleClass } from "../../../"
    
    export class MySlider extends UIElement<{ active: number }> {
    	static localName ='my-slider'
    
    	init = {
            active: 0,
        }
    
    	connectedCallback() {
    		super.connectedCallback()
    
            // Event listeners for navigation
    		const total = this.querySelectorAll('.slide').length
    		const setNextSlide = (direction: number) => () => {
    			this.set('active', v => (v + direction + total) % total)
    		}
    		this.first('.prev').on('click', setNextSlide(-1))
    		this.first('.next').on('click', setNextSlide(1))
    
    		// Effects for updating slides and dots
    		const getActiveByIndex: SignalValueProvider<boolean> = (_, index) =>
    			this.get('active') === index
    		this.all('.slide').sync(toggleClass('active', getActiveByIndex))
    		this.all('.dots span').sync(toggleClass('active', getActiveByIndex))
    	}
    }
    MySlider.define()