From 1038178066f7078eb8953d26a3478afc23637df1 Mon Sep 17 00:00:00 2001
From: treeder <75826+treeder@users.noreply.github.com>
Date: Tue, 12 May 2026 00:39:35 +0000
Subject: [PATCH] feat(carousel): Implement Material 3 carousel component
Adds `md-carousel` and `md-carousel-item` components based on Material 3 guidelines.
- `md-carousel` serves as an uncontained scrolling container leveraging flexbox and CSS `scroll-snap-type: x mandatory`.
- `md-carousel-item` implements individual items with `scroll-snap-align: start`.
- Components correctly wire accessibility ARIA roles `list` and `listitem` via ElementInternals.
- Updates `all.js` and `common.js` to include the new components.
Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
---
all.js | 4 ++++
carousel/carousel-item.js | 27 +++++++++++++++++++++++++++
carousel/carousel.js | 32 ++++++++++++++++++++++++++++++++
common.js | 4 ++++
4 files changed, 67 insertions(+)
create mode 100644 carousel/carousel-item.js
create mode 100644 carousel/carousel.js
diff --git a/all.js b/all.js
index bc4175c..f3d9678 100644
--- a/all.js
+++ b/all.js
@@ -9,6 +9,8 @@ import './buttons/button.js'
import './buttons/filled-tonal-button.js'
import './buttons/outlined-button.js'
import './buttons/text-button.js'
+import './carousel/carousel.js'
+import './carousel/carousel-item.js'
import './checkbox/checkbox.js'
import './chips/assist-chip.js'
import './chips/chip-set.js'
@@ -48,6 +50,8 @@ import './text/text-field.js'
// LINT.IfChange(exports)
// go/keep-sorted start
export * from './buttons/button.js'
+export * from './carousel/carousel.js'
+export * from './carousel/carousel-item.js'
export * from './checkbox/checkbox.js'
export * from './chips/chip.js'
export * from './chips/chip-set.js'
diff --git a/carousel/carousel-item.js b/carousel/carousel-item.js
new file mode 100644
index 0000000..ef18c7a
--- /dev/null
+++ b/carousel/carousel-item.js
@@ -0,0 +1,27 @@
+import { LitElement, html, css } from 'lit'
+
+export class CarouselItem extends LitElement {
+ constructor() {
+ super()
+ this.internals = this.attachInternals()
+ this.internals.role = 'listitem'
+ }
+
+ static styles = [
+ css`
+ :host {
+ display: flex;
+ flex-shrink: 0;
+ scroll-snap-align: start;
+ border-radius: var(--md-carousel-item-shape, 28px);
+ overflow: hidden;
+ }
+ `,
+ ]
+
+ render() {
+ return html``
+ }
+}
+
+customElements.define('md-carousel-item', CarouselItem)
diff --git a/carousel/carousel.js b/carousel/carousel.js
new file mode 100644
index 0000000..5e88d08
--- /dev/null
+++ b/carousel/carousel.js
@@ -0,0 +1,32 @@
+import { LitElement, html, css } from 'lit'
+
+export class Carousel extends LitElement {
+ constructor() {
+ super()
+ this.internals = this.attachInternals()
+ this.internals.role = 'list'
+ }
+
+ static styles = [
+ css`
+ :host {
+ display: flex;
+ overflow-x: auto;
+ scroll-snap-type: x mandatory;
+ scrollbar-width: none; /* Firefox */
+ gap: var(--md-carousel-gap, 8px);
+ padding: var(--md-carousel-padding, 16px);
+ }
+
+ :host::-webkit-scrollbar {
+ display: none; /* Safari and Chrome */
+ }
+ `,
+ ]
+
+ render() {
+ return html``
+ }
+}
+
+customElements.define('md-carousel', Carousel)
diff --git a/common.js b/common.js
index d063462..15f8797 100644
--- a/common.js
+++ b/common.js
@@ -7,6 +7,8 @@
* for production.
*/
import './buttons/button.js'
+import './carousel/carousel.js'
+import './carousel/carousel-item.js'
import './checkbox/checkbox.js'
import './chips/chip.js'
import './chips/chip-set.js'
@@ -27,6 +29,8 @@ import './tabs/tabs.js'
import './text/text-field.js'
export * from './buttons/button.js'
+export * from './carousel/carousel.js'
+export * from './carousel/carousel-item.js'
export * from './checkbox/checkbox.js'
export * from './chips/chip.js'
export * from './chips/chip-set.js'