- Modern CSS scroll snap förvandlar enkla scrollers till karuseller med native-känsla utan JavaScript.
- Experimentella ::scroll-button och ::scroll-marker tillhandahåller inbyggda navigerings- och tillgänglighetshooks.
- Väl utformade reservfunktioner, ARIA-roller och rörelserespekterande stilar gör CSS-karuseller produktionsklara.
- Plattformsutveckling (bring-your-own elements, cyklisk scrollning) kommer att ytterligare minska behovet av JS-bibliotek.

Att bygga karuseller utan en enda rad JavaScript är inte längre science fictionmodern CSS ger dig allt du behöver för att skapa smidiga, pekvänliga reglage med hjälp av CSS-pekfunktionsegenskap som känns inbyggda i webbläsaren. Från scroll-snäppning till experimentella pseudoelement som automatiskt genererar navigeringskontroller, kan du leverera kompletta komponenter samtidigt som du håller ditt paket effektivt och ditt liv enklare.
Om du någonsin har brottats med tunga JS-karusellbibliotek, hydreringsproblem eller tillgänglighetsrelaterade egenheter, är CSS-bara karuseller en frisk fläkt.Med de senaste specifikationerna (som CSS Overflow Level 5) och väletablerade funktioner som scroll-snap-type, är det nu realistiskt att leverera produktionsklara karuseller som är snabba, tangentbordsåtkomliga och robusta när JavaScript är inaktiverat.
Varför modern CSS gör JavaScript-karuseller valfria
I åratal var standardsvaret på "hur bygger jag en karusell?" "skaffa ett JS-bibliotek".Swiper, Slick, Glide, Bootstraps karusell och många andra löste problemet genom att jonglera händelselyssnare, timers, observatörer som ändrar storlek och ARIA-roller åt dig – men de stoppade också in extra kilobyte i ditt paket och introducerade ytterligare ett beroende att underhålla.
Idag har CSS i tysthet hunnit ikapp de flesta vanliga användningsfallen för karusellerMed en handfull egenskaper kan du förvandla en vanlig horisontell scroller till en paginerad, snap-to-slide-upplevelse som fungerar med mushjul, styrplatta, tangentbordspilar och pekskärmssvep. Inga lyssnare, inga omflöden orsakade av JS-logik och inga kappvillkor med hydrering i ramverk.
Grundidén är enkel: du behandlar din karusell som ett rullbart område, inte som en magisk komponent som teleporterar sig mellan bilder. CSS "hjälper" sedan rullningen med fästpunkter, mjuka övergångar och, i den senaste specifikationen, inbyggda knappar och markörer som webbläsaren injicerar i DOM:en som riktiga interaktiva element.
Denna förändring har stora konsekvenser för prestation och motståndskraftEn CSS-karusell fungerar fortfarande perfekt när JS är blockerad, inte laddas eller inaktiveras av användaren. Webbläsarens egen scrollmotor gör det mesta av det tunga arbetet, som är starkt optimerad i alla större motorer och anpassad för strömsnåla enheter och tillgänglighetsverktyg.
Utöver det, CSS-funktioner som :has(), rutnätslayout och rullningsdrivna animationer kopplas in i samma rullningsområde, vilket låter dig bygga sofistikerade bildindikatorer, innehållsvisningar eller parallax utan att sammanfoga tre olika system (din kod, bibliotekets logik och webbläsarens scrollning) på ett känsligt sätt.
Kärnbyggnadsblock: Scroll Snap för CSS-only-karuseller
I hjärtat av en CSS-enbart karusell finns Scroll Snap-modulen., vilket låter dig definiera "magnetiska" punkter som rullningspositionen ska låsas på efter att en användare slutat rulla. Istället för att landa halvvägs mellan två bilder, fästs vyn till närmaste, vilket ger den där klassiska skjutreglagekänslan.
HTML-strukturen kan vara minimal: en behållare som rullar horisontellt, plus en serie bildelement inuti den. Du behöver inte ens ge varje bild eller kort en egen klass om din markup är konsekvent – även om namngivning av dina objekt ofta hjälper till med stil och tillgänglighet:
Exempelstruktur för en grundläggande bildkarusell skulle kunna konceptualiseras som ett omslag som <ul class="carousel"> med flera <li> objekt, som vart och ett innehåller en bild eller ett kort. Magin finns i CSS:n:
Du definierar behållaren som en horisontell flexlayout med rullning aktiverad och ange att den ska snäppa längs x-axeln:
.carousel { display: flex; overflow-x: auto; scroll-snap-type: x mandatory; scroll-behavior: smooth; }
Varje enskild bild exponerar sedan en snäppposition, vanligtvis i mitten eller början av visningsporten, och anger en fast bas så att en eller flera visas per vy:
.carousel-item { flex: 0 0 100%; scroll-snap-align: center; }
Det här mönstret ger dig redan en förvånansvärt komplett karusellAnvändare kan svepa på mobilen, skrolla med musen eller styrplattan, och visningsfönstret kommer alltid att placeras på den närmaste bilden. Det finns inget JS som övervakar rullningshändelser eller manuella positionsjusteringar.
För att skapa karuseller med flera artiklar (som produktremsor) behöver du bara justera flexbasen.Du kan till exempel visa tre objekt per vy med lite mellanrum genom att använda något i stil med flex: 0 0 calc(33.333% - 1rem) plus sidmarginaler. Scroll Snap bryr sig inte om varje "sida" är en enda bild i full bredd eller en grupp mindre kort.
Egenskaper som t.ex object-fit: cover är användbara när man arbetar med råbilder, vilket säkerställer att de fyller ut bildytan utan att sträckas ut. Kombinerat med ett fast bildförhållande kan du få bildkaruseller som förblir visuellt konsekventa på alla skärmstorlekar.
Från vanliga scrollers till karuseller med native-känsla
En karusell med bara scroll-snap-funktion känns redan bra, men moderna specifikationer tar det längre, särskilt i Chrome 135+ där CSS Overflow Level 5-utkastet börjar dyka upp som verkliga funktioner. Specifikationen behandlar en karusell som ett rullningsområde med ett valfritt, webbläsargenererat användargränssnitt runt det.
I den modellen motsvarar en "karusell": en scroller plus upp till två uppsättningar affordanser—skrollknappar och scrollmarkörer. Båda skapas av CSS-pseudoelement, men webbläsaren genererar faktiska DOM-syskon bredvid din scroller så att de beter sig som inbyggda kontroller.
Avgörande är att webbläsaren kopplar samman semantik, fokusordning och tillståndshantering åt digDe injicerade elementen har korrekta roller, de är tabbara i en rimlig ordning och de återspeglar den aktuella rullningspositionen. Det betyder att det inte är trivialt att replikera samma tillgänglighetsnivå med ett handrullat JS-bibliotek; du skulle behöva ARIA-attribut, fokusgruppsbeteende, tangentbordsnavigering och live-regionmeddelanden som alla fungerar tillsammans.
Även om du inte är redo att förlita dig på de experimentella delarna ännu, är den här riktningen viktig.Det signalerar att karuseller, flikar, scrollspy-komponenter och liknande UI-mönster upphöjs till "förstklassiga medborgare" i CSS, vilket stadigt kommer att minska behovet av anpassad JavaScript-kod.
Slutresultatet är en mer robust baslinje för alla användare—inklusive personer som inaktiverar JS, förlitar sig på hjälpmedel, har enheter med låg strömförbrukning eller surfar under ostabila nätverksförhållanden. Inbyggd scrollning och CSS-stil trumfar skripttunga widgetar i alla dessa scenarier.
Lägga till rullknappar med ::scroll-button()
På vissa plattformar ser du redan små pilar bredvid rullningslisterna, men det nya ::scroll-button() pseudo-elementet går ett steg längre och låter CSS definiera dedikerade "föregående/nästa"-knappar för alla rullbara områden – inklusive karuseller.
Dessa CSS-definierade rullningsknappar beter sig annorlunda än gammaldags rullningslistpilarIstället för att knuffa innehållet i små steg hoppar de ungefär 85 % av det synliga området per klick. I en fullbredd, snäppjusterad karusell känns det nästan exakt som en navigeringsknapp per bild.
Om du verkligen behöver en exakt rörelse för ett objekt kan du kombinera alternativen för rullningssnäppning.Ett typiskt recept är scroll-snap-type: x mandatory på behållaren och scroll-snap-stop: always på varje snap-underkort. På så sätt tar ett tryck på bläddringsknappen dig effektivt från ett kort till nästa.
Ur ett syntaxperspektiv liknar det att lägga till rullknappar att utforma andra pseudoelement.Du väljer det rullbara elementet (t.ex. .carousel) och mål ::scroll-button(left) or ::scroll-button(right) för att representera bakåt- och framåtkontroller. Ett minimalt exempel:
.carousel::scroll-button(left) { content: "⬅" / "Scroll left"; }
.carousel::scroll-button(right) { content: "⮕" / "Scroll right"; }
Det snedstreckseparerade innehållsvärdet låter dig tillhandahålla både en visuell och en tillgänglig etikettWebbläsaren skapar sedan riktiga <button> noder som syskon till ditt rullningsområde och placerar ditt innehåll inuti dem. Du kan fritt placera och utforma dem som du vill, precis som alla andra element – med absolut positionering, CSS-rutnät eller till och med ankarpositionering.
Fokusstyling spelar fortfarande roll härTill exempel, att tillämpa en regel som .carousel::scroll-button(*):focus-visible { outline-offset: 5px; } säkerställer att när användare med tabbtangenten till dessa knappar ser de en tydlig fokusring förskjuten från elementets kant, vilket bibehåller tangentbordets användbarhet och WCAG-efterlevnad.
Under huven håller webbläsaren reda på knappstatus och tillgängligt rullningsområdeDet betyder att du inte behöver oroa dig för att inaktivera knappen "föregående" när du är på den första bilden eller knappen "nästa" i slutet; plattformen kan hantera dessa villkor på ett standardiserat sätt.
Scrollmarkörer med ::scroll-marker() för punkt- eller etikettnavigering
Visuella markörer – prickar eller miniatyrbilder under karusellen – är nästan vad användare förväntar sig nuförtiden.. Den nya ::scroll-marker() pseudo-element låter dig generera dessa indikatorer strikt med CSS, där var och en är länkad till ett specifikt objekt i den rullningsbara listan.
Till skillnad från rullningslistens miniatyr representerar rullningsmarkörer semantiska intressanta punkterVarje markör motsvarar inte bara en position i pixlar, utan ett faktiskt underordnat element, vilket gör den idealisk för scenarier som säsonger i en TV-serie, produktkategorier eller logiska kapitel i en berättelse snarare än varje enskild bildruta.
Markörerna visas som giltiga <a> element, vilket ger dig två viktiga funktioner gratis: navigering på sidan och korrekt semantik för skärmläsare. De beter sig ungefär som ankarlänkar, men med vissa förbättringar anpassade för användning i karuseller.
Bland dessa förbättringar finns :target-current pseudoklass, vilket matchar när motsvarande objekt för närvarande är fäst i vy eller annars betraktas som "den aktiva bilden". Du kan använda den för att markera den aktiva markören med en annan bakgrund eller kantlinje.
För att koppla ihop detta definierar du först var gruppen av markörer ska finnas med scroll-marker-group egenskapen på containern, vanligtvis före eller efter scrollern. Till exempel, scroll-marker-group: after; skulle placera markörerna efter karusellregionen i DOM:en.
Sedan riktar du in dig på de enskilda barn som ska generera markörerOm dina bilder är <li> element, något i stil med .carousel > li::scroll-marker fungerar. Ett vanligt mönster är att skapa tomma punkter:
.carousel { scroll-marker-group: after; }
.carousel > li::scroll-marker { content: " "; }
.carousel > li::scroll-marker:target-current { background: var(--accent); }
Webbläsaren tar hand om att generera <a> markörer, gruppera dem i en dedikerad ::scroll-marker-group-behållareoch exponera dem som tangentbordsnavigerbara element som beter sig som en fokusgrupp. Skärmläsare kan presentera dem på samma sätt som en tabelllista: du flyttar fokus mellan markörer och aktiverar dem för att hoppa till en viss bild.
Markörer är inte begränsade till prickar. Du kan ställa in content till siffror ("1", "2", etc.), läsbara etiketter som "Säsong 1" eller "Kapitel 3", eller till och med inbäddade bilder för att skapa miniatyrgallerier för fototunga layouter som produktgallerier för e-handel.
Kombinera knappar, markörer och reservstrategier
När du blandar rullknappar och markörer ovanpå en rullningsbar karusell får du en komponent som känns omöjlig att skilja från en JS-lösning., men är enklare att underhålla och levererar färre rörliga delar. Användare kan klicka på pilar, trycka på punkter, svepa på pekskärmar eller använda tangentbordsnavigering – allt med konsekvent beteende.
Denna kombination åtgärdar även vanliga prestanda- och UX-problemEftersom inget skript koordinerar bildändringar, får du inte layoutförändringar från manuell scrollLeft justeringar eller timers som aktiveras vid obekväma tidpunkter. CLS (Cumulative Layout Shift) minskas eftersom DOM-trädet är klart från början och du inte injicerar eller mäter element sent i livscykeln.
Det finns dock en hake: ::scroll-button och ::scroll-marker är fortfarande experimentella. och för närvarande bara i Chrome 135+ bakom experimentella flaggor. Det betyder att du absolut måste designa eleganta reservalternativ om du planerar att använda dem på produktionsplatser.
Funktionsdetektering via @supports är din bästa allierade härDu kan rendera en traditionell navigeringsfält – länkar till #slide1, #slide2 och så vidare – som standard, dölj det sedan när de inbyggda kontrollerna är tillgängliga. Ett grovt konceptuellt mönster ser ut så här:
.carousel-nav { display: flex; gap: 0.5rem; }
@supports (scroll-button-inline: both) { .carousel-nav { display: none; } }
I HTML-koden räcker det med en enkel navigering med ankarlänkar.varje bild får en id, och navigeringen pekar på dessa ID:n. Att hoppa till ett ankare inuti en scroll-snap-behållare integreras snyggt med snapping-beteendet, så bilden snappar snyggt efter hoppet.
Det här betyder att du kan dra nytta av de experimentella pseudoelementen som stöder webbläsare.—helt inbyggda knappar, markörer och hookar för scroll state — samtidigt som de erbjuder en fullt fungerande upplevelse på andra ställen. Ingen användare sitter fast med en oanvändbar reglage.
Dessutom fungerar allt detta fortfarande när JavaScript är inaktiverat., eftersom du enbart förlitar dig på HTML-ankare, CSS-skrollbeteende och webbläsarens rullmotor. För många UX-mönster – gallerier, tidslinjer, steg-för-steg-presentationer – är det mer än tillräckligt.
CSS-endast autoplay-karuseller med nyckelbilds-animationer
En vanlig funktion som folk förväntar sig av karuseller är autouppspelning—bilderna går framåt automatiskt efter några sekunder. Även om detta ofta implementeras med JavaScript-timers, kan du också approximera detta beteende med hjälp av rena CSS-animationer.
Tricket är att animera transformationen av en flexrad som innehåller alla bilderFör ett fast antal bilder definierar du en tidslinje för nyckelbildrutor där spåret stannar vid varje bildposition ett tag och sedan hoppar till nästa. Något i stil med:
@keyframes slide { 0%, 20% { transform: translateX(0); } 25%, 45% { transform: translateX(-100%); } 50%, 70% { transform: translateX(-200%); } 75%, 95% { transform: translateX(-300%); } 100% { transform: translateX(0); } }
Sedan tillämpar du den här animationen på omslaget som innehåller dina bildelement, t.ex. .autoplay-carousel { display: flex; animation: slide 12s infinite; }Procentintervallen definierar uppehållstider på varje bild så att innehållet är läsbart innan man går vidare.
Det är avgörande att lägga till ett sätt för användare att pausa autouppspelningEtt enkelt men effektivt mönster är att pausa vid muspekning med .autoplay-carousel:hover { animation-play-state: paused; }, så att skrivbordsanvändare kan stoppa rörelsen när de interagerar med innehållet.
Tillgänglighetsöverväganden går ännu längre med rörelseinställningar. Respektera prefers-reduced-motion anses vara bästa praxis, så du kan inaktivera animationen helt för användare som föredrar mindre rörelse:
@media (prefers-reduced-motion: reduce) { .autoplay-carousel { animation: none; } }
Även om CSS-lösningar för autoplay inte enkelt kan hantera alla edge-fall som JS kan– som verkligt dynamiska bildräkningar eller komplex användardriven paus-/återupptagningslogik – är de förvånansvärt stabila för enkla, statiska karuseller när du vill undvika skript helt och hållet.
Jämförelse av CSS-only- och JavaScript-karuseller
Att välja mellan CSS och JavaScript för en karusell är inte ett allt-eller-inget-förslag; det beror på dina krav, målgrupp och underhållsbudget. Men balansen har kraftigt förändrats till förmån för CSS för många verkliga scenarier.
Ur ett prestandaperspektiv är CSS-karuseller klara vinnareDet sker ingen parsning eller exekvering av bibliotekskod, inga händelselyssnare vid varje rullningsinteraktion och inga omberäkningar av runtime-layout utlösta av skriptade justeringar. Webbläsarens rullningsmotor är skriven i mycket optimerad native-kod och finjusterad för allt från avancerade stationära datorer till telefoner med låg energiförbrukning.
När det gäller tillgänglighet höjer de nya CSS-karusellfunktionerna ribban avsevärtWebbläsarbaserade rullningsknappar och markörer är förberedda med ARIA-roller, tangentbordsbeteende och meddelanden som annars skulle behöva återskapas noggrant i JavaScript. Även utan dessa kan en rullningsbar karusell fortfarande göras tillgänglig med roller och etiketter.
Å andra sidan är JavaScript fortfarande användbart för mycket avancerade mönster.Om du behöver avancerad autoplay-logik, analyshooks, anpassad fysik eller för att synkronisera flera karuseller med delat tillstånd (t.ex. huvudbild plus miniatyrrad plus extern paginering), kan skriptdrivna metoder fortfarande vara mer flexibla.
Den viktigaste insikten är dock att många "vardagliga" karuseller faktiskt inte behöver denna nivå av komplexitet.Ett enkelt produktgalleri, en slider för omdömen eller en remsa för "utvalda inlägg" kan vanligtvis implementeras med scroll snap, några CSS-regler och valfria inbyggda kontroller – vilket sparar dig från att dra in ett 50KB-beroende bara för att flytta mellan tre bilder.
En annan faktor att beakta är motståndskraft när JavaScript misslyckasCSS-bara karuseller fortsätter att fungera perfekt om JS blockeras av en företagsproxy, trasig av en annonsblockerare eller helt enkelt inte laddas när användaren landar på sidan. Den typen av smidig nedbrytning är svår att uppnå med bibliotek som är beroende av en initialiseringsfas efter att sidan laddats.
Bästa praxis för tillgänglighet för CSS-karuseller
Även om webbläsaren hjälper till med vissa tillgänglighetsaspekter spelar du fortfarande en avgörande roll. för att göra din CSS-karusell användbar för alla. Semantik, fokushantering och rörelsekänslighet bör alla vara i fokus.
Först, ge karusellen ett lämpligt landmärke. Slå in det i ett element med role="region" och en beskrivande aria-label som ”Produktgalleri” eller ”Utvalda artiklar” gör det enklare för skärmläsaranvändare att hitta och förstå.
Varje bild kan också ha en tillgänglig etikett, till exempel via aria-label med angivande av ”Bild 1 av 3”, ”Bild 2 av 3” och så vidare. På så sätt får en användare omedelbar kontext om sin position i sekvensen när de landar på en bild.
Glöm inte synliga fokusindikatorerAlla interaktiva element – oavsett om det är en ::scroll-button, ::scroll-marker–genererad länk, eller ett manuellt ankare i din reservnavigering – behöver en tydlig fokusstil som följer kontrastriktlinjerna och inte tas bort av globala återställningar.
Om du använder autouppspelning eller scrolldrivna animationer, respektera rörelseinställningarna, Använda prefers-reduced-motion Att inaktivera eller förenkla animationer är inte valfritt om du bryr dig om användare som är benägna att bli åksjuka, och det är enkelt nog att lägga in de mer intensiva effekterna i en mediefråga.
Slutligen, tänk på storleken på pekskärmarnaOavsett om du använder inbyggda rullningsmarkörer eller dina egna navigeringslänkar, håll interaktiva områden minst runt 44×44 CSS-pixlar. Det betyder vanligtvis generös utfyllnad och avstånd snarare än små cirklar tätt packade ihop.
Verkliga arbetsflöden, verktyg och framtida riktningar
För att göra det enklare att experimentera med CSS-karuseller har vissa team skapat verktyg för livekonfiguration där du kan växla mellan brytare – aktivera rullningsknappar, växla mellan markörer, justera snap-lägen – och omedelbart se den uppdaterade CSS som krävs för den konfigurationen. Dessa lekplatser är fantastiska för lärande genom att experimentera.
Vid sidan av konfiguratorer visar utvalda gallerier med CSS-baserade karuselldemonstrationer vad som är möjligt.Du hittar mönster som flikgränssnitt, scrollspionnavigering, stegbaserade bilder, miniatyrgallerier och mer, alla med samma grund av rullningsområden plus snapping och valfria kontroller.
Många av dessa demosidor levereras avsiktligt utan JavaScript för att bevisa poängen: ett rikt, interaktivt användargränssnitt är helt uppnåeligt med enbart HTML och CSS. De exponerar ofta sina verktyg genom @layer deklarationer, så att du kan granska och välja ut stilar som passar ditt eget designsystem.
Framöver arbetar CSS-plattformen redan med två viktiga utvecklingsvägarDet första är ”ta med dina egna element”, där du istället för att förlita dig på webbläsargenererade knappar och markörer kan lägga till dina egna <a> och <button> markup, samtidigt som den fortfarande drar nytta av den underliggande rullningslogiken.
Detta skulle öppna dörren för varumärkesbyggda, rikt utformade kontroller byggda med verktygsramverk. som Tailwind, samtidigt som semantiken och beteendet styrs av plattformen. Du skulle i huvudsak dekorera användargränssnittet men låta webbläsaren förbli hjärnan i operationen.
Det andra stora området är cyklisk eller oändlig rullningMånga karuseller idag "omsluter" sig när man når slutet och loopar sömlöst tillbaka till den första bilden. Att implementera detta på ett snyggt sätt är knepigt, det involverar ofta duplicerat innehåll eller komplex logik. Stöd för cyklisk rullning på plattformsnivå skulle låta webbläsare hantera omslutningsbeteendet mer elegant och effektivt.
Allt eftersom dessa funktioner mognar och sprids bortom Chrome till andra sökmotorer, klyftan mellan vad en "native CSS-karusell" kan göra och vad tunga JavaScript-bibliotek erbjuder kommer att fortsätta att minska, vilket gör skriptfria tillvägagångssätt till standardutgångspunkten snarare än ett edge-fallsexperiment.
Sammantaget ger modern CSS dig all anledning att ompröva hur du bygger karusellerMed en enkel scroll-snap-layout som börjar med att lägga till experimentella knappar och markörer där det stöds, och sprida bästa praxis för tillgänglighet, kan du leverera responsiva, smidiga och robusta reglage utan att behöva använda ett JS-beroende. För många projekt innebär det snabbare sidor, mindre kod att underhålla och en mer robust upplevelse för varje användare.