Modal (media)

The Media modal is a variant of the modal component designed for displaying media such as images and videos. It builds on the existing modal foundation but is optimized for viewing media clearly and without distraction, supporting both landscape and portrait formats.

Overview

DsMediaModal is a component for presenting images, zoomable images, YouTube embeds, Vimeo embeds, or mixed media collections inside a modal dialog. It solves the implementation problem of combining modal focus management, responsive media sizing, gallery navigation, captions, provider embed URLs, video reset behaviour, and optional custom video players behind one component API.

The component builds on the design system modal, icon button, button group, caption, text, media image, and media video primitives. Consumers usually combine it with a trigger such as DsButton or another accessible interactive element.

Basic usage

Importing the component


                                                        
                                                        
                                                            import { DsMediaModal } from "@coloplast/design-system";
                                                        import "@coloplast/design-system/dist/components/media-modal/ds-media-modal.css";
                                                        
                                                            

Example


                                                        
                                                        
                                                            <script setup lang="ts">
                                                        import { ref } from "vue";
                                                        import { DsButton, DsMediaModal } from "@coloplast/design-system";
                                                        import "@coloplast/design-system/dist/components/media-modal/ds-media-modal.css";
                                                        
                                                        const isOpen = ref(false);
                                                        const activeMediaIndex = ref(0);
                                                        
                                                        const mediaItems = [
                                                          {
                                                            type: "image",
                                                            src: "https://placehold.co/960x540",
                                                            srcset:
                                                              "https://placehold.co/640x360 640w, https://placehold.co/1280x720 1280w",
                                                            sizes: "(max-width: 768px) 100vw, 1280px",
                                                            alt: "Product shown in use",
                                                            caption: {
                                                              name: "Product image",
                                                              description: "A product image shown inside the media modal.",
                                                            },
                                                          },
                                                          {
                                                            type: "youtube",
                                                            src: "https://www.youtube.com/embed/oICIXAIN0kA",
                                                            title: "Product video",
                                                            caption: {
                                                              name: "Product video",
                                                              description: "A YouTube video shown inside the media modal.",
                                                            },
                                                          },
                                                        ] as const;
                                                        
                                                        const open = () => {
                                                          isOpen.value = true;
                                                        };
                                                        
                                                        const close = () => {
                                                          isOpen.value = false;
                                                        };
                                                        
                                                        const handleMediaChange = ({ index }: { index: number }) => {
                                                          activeMediaIndex.value = index;
                                                        };
                                                        
                                                        </script>
                                                        
                                                        <template>
                                                          <DsButton aria-haspopup="dialog" @click="open">Open media modal</DsButton
                                                          ><DsMediaModal
                                                            :is-open="isOpen"
                                                            :items="mediaItems"
                                                            aspect-ratio="16/9"
                                                            close-label="Close"
                                                            previous-label="Previous media"
                                                            next-label="Next media"
                                                            title="Product media"
                                                            @change="handleMediaChange"
                                                            @close-modal="close"
                                                          />
                                                        </template>
                                                        
                                                        
                                                            

Props

View on Storybook.

Media item data

Standard image item


                                                        
                                                        
                                                            const imageItem = {
                                                          type: "image",
                                                          src: "https://placehold.co/960x540",
                                                          srcset:
                                                            "https://placehold.co/640x360 640w, https://placehold.co/1280x720 1280w",
                                                          sizes: "(max-width: 768px) 100vw, 1280px",
                                                          alt: "Product shown in use",
                                                          caption: {
                                                            name: "Product image",
                                                            role: "Product specialist",
                                                            description: "A product image shown inside the media modal.",
                                                          },
                                                        };
                                                        
                                                        
                                                            

Zoomable image item

Set zoomable: true for high-resolution images where users need closer inspection. Zoomable images support src, alt, caption, and optional zoomScale.


                                                        
                                                        
                                                            const zoomableImageItem = {
                                                          type: "image",
                                                          src: "/assets/demo/product-detail.jpg",
                                                          alt: "Close-up product detail",
                                                          zoomable: true,
                                                          zoomScale: 8,
                                                          caption: {
                                                            name: "Product detail",
                                                            description: "A high-resolution image that can be inspected more closely.",
                                                          },
                                                        };
                                                        
                                                        
                                                            

Embedded video item

Use type: "youtube" or type: "vimeo" with an embed URL. The title property is required because the iframe needs an accessible name.


                                                        
                                                        
                                                            const videoItem = {
                                                          type: "youtube",
                                                          src: "https://www.youtube.com/embed/oICIXAIN0kA",
                                                          title: "Product video",
                                                          autoplay: true,
                                                          muted: true,
                                                          caption: {
                                                            name: "Product video",
                                                            description: "A YouTube video shown inside the media modal.",
                                                          },
                                                        };
                                                        
                                                        
                                                            

When autoplay is enabled, the component adds provider-specific autoplay query parameters while the video is active. Muted autoplay is the most reliable option; browser and provider policies can still block autoplay with sound.

Events

Event

Payload

Description

close-modal

void

Emitted when the underlying modal requests to close.

change

{ index: number; item: MediaModalItem }

Emitted when the modal opens with an active item and when gallery navigation changes the active item.

Slots

#video


                                                        
                                                        
                                                            <DsMediaModal
                                                          :is-open="isOpen"
                                                          :items="mediaItems"
                                                          close-label="Close"
                                                          @close-modal="close"
                                                        >    
                                                          <template #video="{ src, title, active, describedBy, contentClass, markLoaded }">        
                                                            <CustomYouTubePlayer            
                                                              :class="contentClass"          
                                                              :src="src"        
                                                              :title="title"      
                                                              :active="active"         
                                                              :aria-describedby="describedBy"       
                                                              @ready="markLoaded"
                                                            />
                                                          </template>
                                                        </DsMediaModal>
                                                        
                                                        
                                                            

Behaviour

  • A single media item is rendered in a compact modal layout; multiple items use gallery layout and show previous and next controls.
  • The media stage scales to the available viewport while preserving the configured aspect ratio.
  • Images can be standard responsive images or zoomable high-resolution images.
  • Non-active gallery slides are hidden from assistive technology and made inert.
  • Embedded videos are reset when users navigate away from them and when the modal closes, preventing off-screen playback from continuing.
  • Arrow-left and arrow-right keyboard navigation moves through galleries unless the event starts inside embedded video controls.

Accessibility

  • Use an accessible trigger element and set aria-haspopup="dialog" on the trigger.
  • Provide a visible title, or pass aria-label to DsMediaModal when no visible title is used.
  • Localise closeLabel, previousLabel, nextLabel, counterLabel, and counterAnnouncementLabel.
  • Provide useful image alt text when the image needs a text alternative. If the visible caption already describes the image sufficiently, omit alt and the image is rendered with an empty alternative.
  • Provide descriptive title values for all YouTube and Vimeo items.
  • Captions are associated with the active media item and video slot consumers should bind describedBy to their custom player element.