A presentation component that extends adapt-contrib-media to provide a gallery of videos from which the user can select.
Media Gallery is a fully accessible, responsive component for the Adapt Learning Framework that allows learners to browse and play multiple media items (video or audio) from an elegant thumbnail gallery interface. Each media item can have its own poster image, closed captions, and transcripts.
- Features
- Demo
- Installation
- Usage
- Configuration
- Examples
- Accessibility
- Internationalization
- Browser Support
- Responsive Design
- Styling & Customization
- Performance
- Known Issues
- Developer Guide
- Contributing
- Changelog
- License
- About Adapt Learning
✨ Core Functionality
- 📺 Multiple video/audio items in a single component
- 🖼️ Individual thumbnail images for each media item
- 🎬 Automatic poster frame selection per video
- 📝 Per-item closed captioning with multi-language support
- 📄 Inline and external transcript options (global and per-item)
- ✅ Flexible completion tracking (play, allPlayed, ended, allEnded, inview)
🎨 User Experience
- 📱 Fully responsive grid layout
- 👆 Touch-device optimized
- ⌨️ Complete keyboard navigation support
- 🔊 Volume controls and playback speed options
- 🖥️ Full-screen viewing capability
- 🎯 Visual feedback for visited/selected items
🛠️ Technical Excellence
- 🔌 Extends official adapt-contrib-media component
- 🎛️ MediaElement.js v7.0.7 for consistent playback
- 🔄 Backward compatible data structure
- ✏️ Full Adapt Authoring Tool (AAT) support (legacy v4-5 and modern v6+)
- ♿ WCAG 2.1 AA compliant
- 🌍 RTL (Right-to-Left) language support
- 🌐 Internationalization ready
Screenshot showing the media gallery component with thumbnail grid and active video player
Via Adapt CLI:
adapt install adapt-mediaGalleryManual Installation:
- Download the plugin from the releases page
- Extract to
src/components/adapt-mediaGallery - Run
adapt build
Via package.json:
Add to your package.json:
{
"adapt": {
"componentTypes": [
"adapt-mediaGallery"
]
}
}The Media Gallery component is compatible with both legacy (v4-5) and modern (v6+) Adapt Authoring Tool versions.
Installation:
- Navigate to Plugin Management in your AAT instance
- Search for "adapt-mediaGallery"
- Click Install
Manual Upload:
- Download the latest release
- Go to Plugin Management → Upload
- Select the zip file and upload
Add the component to your components.json:
{
"_id": "c-05",
"_parentId": "b-05",
"_type": "component",
"_component": "mediagallery",
"_classes": "",
"_layout": "full",
"title": "Media Gallery",
"displayTitle": "Video Library",
"body": "Select a video thumbnail below to view the content.",
"instruction": "Click on any thumbnail to play the video.",
"_setCompletionOn": "allPlayed",
"_mediaGallery": {
"_items": [
{
"title": "Introduction",
"subtitle": "Getting Started",
"ariaLabel": "Play introduction video",
"_graphic": {
"src": "course/en/images/intro-thumb.jpg",
"alt": "Introduction video thumbnail"
},
"_media": {
"mp4": "course/en/video/intro.mp4",
"poster": "course/en/video/intro-poster.jpg"
}
}
]
}
}See example.json for a complete configuration example.
| Attribute | Type | Required | Default | Description |
|---|---|---|---|---|
_component |
string | Yes | - | Must be "mediagallery" |
_classes |
string | No | "" |
CSS classes for custom styling |
_layout |
string | No | "full" |
Layout: "full", "left", or "right" |
title |
string | No | "" |
Component title |
displayTitle |
string | No | "" |
Display title shown to learners |
body |
string | No | "" |
Component body text |
instruction |
string | No | "" |
Instructional text for learners |
| Attribute | Type | Required | Default | Description |
|---|---|---|---|---|
_setCompletionOn |
string | Yes | "allPlayed" |
Completion trigger: "inview", "play", "allPlayed", "ended", "allEnded" |
_useClosedCaptions |
boolean | No | false |
Enable closed captions |
_allowFullScreen |
boolean | No | false |
Enable full-screen mode |
_playsinline |
boolean | No | false |
Play videos inline on iOS (iOS 10+) |
_startLanguage |
string | No | "en" |
Default caption language code |
_showVolumeControl |
boolean | No | true |
Show volume control (not on mobile) |
_startVolume |
string | No | "80%" |
Initial volume level |
inview- Component completes when it comes into viewplay- Completes when any single media item starts playingallPlayed- Completes when all media items have been playedended- Completes when any single media item finishesallEnded- Completes when all media items have finished playing
The _mediaGallery._items array contains configuration for each video/audio item:
{
"title": "Video Title",
"subtitle": "Optional Subtitle",
"ariaLabel": "Descriptive label for screen readers",
"_graphic": {
"src": "path/to/thumbnail.jpg",
"alt": "Alternative text for thumbnail"
},
"_media": {
"mp4": "path/to/video.mp4",
"webm": "path/to/video.webm",
"ogv": "path/to/video.ogv",
"poster": "path/to/poster.jpg",
"cc": [
{
"srclang": "en",
"src": "path/to/captions-en.vtt"
},
{
"srclang": "es",
"src": "path/to/captions-es.vtt"
}
]
},
"_transcript": {
"_inlineTranscript": true,
"inlineTranscriptBody": "Full transcript text here..."
}
}For audio-only items:
{
"title": "Audio Title",
"_graphic": {
"src": "path/to/thumbnail.jpg"
},
"_media": {
"mp3": "path/to/audio.mp3",
"ogg": "path/to/audio.ogg",
"poster": "path/to/cover-art.jpg"
}
}Global Transcript (applies to all items unless overridden):
"_transcript": {
"_setCompletionOnView": true,
"_inlineTranscript": true,
"_externalTranscript": false,
"inlineTranscriptButton": "Show transcript",
"inlineTranscriptCloseButton": "Hide transcript",
"inlineTranscriptBody": "Default transcript text...",
"transcriptLinkButton": "Download transcript",
"transcriptLink": "course/en/pdf/transcript.pdf"
}Per-Item Transcript Override:
"_transcript": {
"_inlineTranscript": true,
"inlineTranscriptBody": "Specific transcript for this video..."
}{
"_component": "mediagallery",
"_setCompletionOn": "allPlayed",
"title": "Training Videos",
"_mediaGallery": {
"_items": [
{
"title": "Module 1",
"_graphic": { "src": "course/en/images/mod1.jpg" },
"_media": { "mp4": "course/en/video/module1.mp4" }
},
{
"title": "Module 2",
"_graphic": { "src": "course/en/images/mod2.jpg" },
"_media": { "mp4": "course/en/video/module2.mp4" }
}
]
}
}{
"_component": "mediagallery",
"_useClosedCaptions": true,
"_startLanguage": "en",
"_mediaGallery": {
"_items": [
{
"title": "Multilingual Content",
"_graphic": { "src": "course/en/images/thumb.jpg" },
"_media": {
"mp4": "course/en/video/content.mp4",
"cc": [
{ "srclang": "en", "src": "course/en/video/cc-en.vtt" },
{ "srclang": "es", "src": "course/en/video/cc-es.vtt" },
{ "srclang": "fr", "src": "course/en/video/cc-fr.vtt" },
{ "srclang": "de", "src": "course/en/video/cc-de.vtt" }
]
}
}
]
}
}{
"_component": "mediagallery",
"_setCompletionOn": "allEnded",
"title": "Audio Interviews",
"_mediaGallery": {
"_items": [
{
"title": "Expert Interview #1",
"_graphic": { "src": "course/en/images/expert1.jpg" },
"_media": { "mp3": "course/en/audio/interview1.mp3" },
"_transcript": {
"_inlineTranscript": true,
"inlineTranscriptBody": "Interview transcript..."
}
}
]
},
"_transcript": {
"_setCompletionOnView": true,
"_inlineTranscript": true,
"inlineTranscriptButton": "View transcript"
}
}The Media Gallery component is fully compliant with WCAG 2.1 Level AA standards:
- Text Alternatives (1.1.1): All thumbnails include configurable
alttext - Captions (1.2.2): Full closed caption support for video content
- Audio Description or Media Alternative (1.2.3): Transcript functionality for both inline and external formats
- Adaptable Content (1.3.x): Semantic HTML structure with proper heading hierarchy
- Distinguishable (1.4.x): High contrast ratios, clear focus indicators, responsive text sizing
- Keyboard Accessible (2.1.1): Complete keyboard navigation with Tab, Enter, Space, Arrow keys
- No Keyboard Trap (2.1.2): Users can navigate away from media controls
- Focus Order (2.4.3): Logical tab order through thumbnails and controls
- Focus Visible (2.4.7): Clear focus indicators on all interactive elements
- Target Size (2.5.5): Touch targets meet minimum 44×44px requirement
- Language of Page (3.1.1): Proper
langattributes - On Focus/Input (3.2.1, 3.2.2): No unexpected context changes
- Labels or Instructions (3.3.2): Clear aria-labels and instructions
- Parsing (4.1.1): Valid, semantic HTML5
- Name, Role, Value (4.1.2): Comprehensive ARIA attributes
| Key | Action |
|---|---|
Tab / Shift+Tab |
Navigate between thumbnails and controls |
Enter / Space |
Select thumbnail or activate control |
Arrow Keys |
Navigate media controls timeline |
M |
Mute/unmute (when media has focus) |
F |
Toggle full-screen (when enabled) |
C |
Toggle captions (when enabled) |
- Tested with JAWS, NVDA, VoiceOver
- Descriptive ARIA labels for all controls
- Live region announcements for state changes
- Skip links to transcript content
- Proper
roleattributes
All ARIA labels can be customized via course.json globals:
"_globals": {
"_components": {
"_mediagallery": {
"ariaRegion": "Media Gallery. Select a video from the list to play.",
"skipToTranscript": "Skip to transcript",
"playText": "Play",
"pauseText": "Pause",
"stopText": "Stop"
}
}
}Full support for RTL languages (Arabic, Hebrew, Persian, Urdu, etc.):
- Automatic layout flipping for RTL content
- Proper text alignment and direction
- Mirrored control positioning
- Tested with Arabic and Hebrew language courses
All user-facing text is translatable via the schema:
- Button labels
- Instructions
- ARIA labels
- Transcript text
- Error messages
Language Configuration:
"_globals": {
"_components": {
"_mediagallery": {
"ariaRegion": "معرض الوسائط. اختر فيديو من القائمة.",
"playText": "تشغيل",
"pauseText": "إيقاف مؤقت"
}
}
}Include multiple WebVTT caption files:
"cc": [
{ "srclang": "en", "src": "captions-en.vtt" },
{ "srclang": "es", "src": "captions-es.vtt" },
{ "srclang": "fr", "src": "captions-fr.vtt" },
{ "srclang": "zh", "src": "captions-zh.vtt" },
{ "srclang": "ar", "src": "captions-ar.vtt" }
]| Browser | Version | Status |
|---|---|---|
| Chrome | Latest + ESR | ✅ Fully Supported |
| Firefox | Latest + ESR | ✅ Fully Supported |
| Edge | Latest | ✅ Fully Supported |
| Safari | 14+ | ✅ Fully Supported |
| Opera | Latest | ✅ Fully Supported |
| Browser | Platform | Status |
|---|---|---|
| Chrome | Android | ✅ Fully Supported |
| Safari | iOS 14+ / iPadOS | ✅ Fully Supported |
| Samsung Internet | Android | ✅ Supported |
| Firefox Mobile | Android | ✅ Supported |
For maximum compatibility, provide multiple formats:
"_media": {
"mp4": "video.mp4", // H.264 + AAC (widest support)
"webm": "video.webm", // VP8/VP9 + Vorbis/Opus
"ogv": "video.ogv" // Theora + Vorbis (fallback)
}Recommended Encoding:
- MP4: H.264 (High Profile), AAC audio, 30fps, CBR
- WebM: VP9, Opus audio, 30fps
- Resolution: 1280×720 (720p) or 1920×1080 (1080p)
- Bitrate: 2-5 Mbps for 720p, 5-8 Mbps for 1080p
The component adapts to Adapt Framework's standard breakpoints:
| Breakpoint | Screen Width | Columns | Thumbnail Size |
|---|---|---|---|
| Extra Large | ≥ 1440px | 4 | 25% width |
| Large | 1024px - 1439px | 4 | 25% width |
| Medium | 768px - 1023px | 3 | 33% width |
| Small | 520px - 767px | 2 | 50% width |
| Extra Small | < 520px | 1 | 100% width |
- Touch Targets: Minimum 44×44px for all interactive elements
- Gesture Support: Tap to play, pinch to zoom (when in full-screen)
- Native Controls: iOS devices can use native video controls via
_playsinline: false - Reduced Motion: Respects
prefers-reduced-motionmedia query - Bandwidth Awareness: Supports adaptive streaming when available
iOS / iPadOS:
- Option to play inline (
_playsinline: true) or full-screen - Volume controlled by device hardware
- Native caption styling option
Android:
- Full MediaElement.js control support
- Software volume controls
- Consistent caption styling
The component uses LESS/CSS variables and follows Adapt's theming system.
.mediagallery {
&__item-container { } // Grid container
&__item { } // Individual thumbnail
&__item-image { } // Thumbnail image
&__item-title { } // Title text
&__item-subtitle { } // Subtitle text
}// custom-theme.less
.mediagallery {
&__item {
width: calc(~'20% - 1rem'); // 5 columns on large screens
margin: 0.5rem;
&:hover {
transform: translateY(-4px);
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}
}
&__item-image {
border-radius: 8px;
border: 3px solid @primary-color;
}
&__item.is-selected &__item-image {
border-color: @secondary-color;
}
}Configure in your theme's variables.less:
@item-color: #0066cc;
@item-color-hover: #0052a3;
@item-color-selected: #ff6600;
@visited: #9933cc;Extend MediaElement.js options:
"_playerOptions": {
"features": ["playpause", "current", "progress", "duration", "volume", "fullscreen"],
"alwaysShowControls": false,
"hideVolumeOnTouchDevices": true,
"enableAutosize": true,
"toggleCaptionsButtonWhenOnlyOne": true,
"startVolume": 0.8
}- Use appropriate compression: Balance quality vs file size
- Progressive download: Enable HTTP progressive download for MP4
- Lazy loading: Videos load only when component is in view
- Poster images: Provide optimized poster frames (JPEG, WebP)
- Thumbnails: 300×200px recommended, < 50KB
- Posters: Match video resolution, < 100KB
- Videos: Aim for < 50MB per video for web delivery
Consider hosting large video files on a CDN:
"_media": {
"mp4": "https://cdn.example.com/videos/content.mp4"
}The component implements:
- Lazy loading for off-screen media
- Preload only the first video's metadata
- On-demand loading for subsequent videos
- Automatic resource cleanup when items are deselected
The following issues are inherited from adapt-contrib-media and MediaElement.js:
- iOS Full-Screen Captions: When
startLanguageis specified, captions may not display when entering full-screen on iOS - iOS Caption Duplication: Enabling captions in full-screen can cause duplicate captions when exiting full-screen
- Android Caption Position: Changing device orientation with captions enabled may misalign caption position
Workarounds:
- For issue #1: Let users manually enable captions after entering full-screen
- For issue #2: Recommend disabling captions before full-screen, then re-enabling
- For issue #3: Pause playback during orientation change
Found a bug? Please report it:
- Check existing issues
- Create a new issue with:
- Adapt version
- Browser and device information
- Steps to reproduce
- Expected vs actual behavior
adapt-mediaGallery/
├── js/
│ ├── adapt-mediaGallery.js # Entry point
│ ├── mediaGalleryModel.js # Data model
│ └── mediaGalleryView.js # View logic
├── less/
│ └── mediagallery.less # Styles
├── templates/
│ └── mediagallery.hbs # Handlebars template
├── schema/
│ ├── component.schema.json # Modern AAT schema (v6+)
│ └── course.schema.json # Global strings schema
├── properties.schema # Legacy AAT schema (v4-5)
├── bower.json # Bower metadata
├── package.json # npm metadata
├── example.json # Configuration example
├── LICENSE # GPL-3.0 license
├── README.md # This file
└── CHANGELOG.md # Version history
Create a Custom Variant:
// js/customMediaGalleryView.js
import MediaGalleryView from './mediaGalleryView';
class CustomMediaGalleryView extends MediaGalleryView {
onItemClicked(e) {
// Custom click behavior
console.log('Custom click handler');
super.onItemClicked(e);
}
// Override other methods as needed
}
export default CustomMediaGalleryView;# Clone the repository
git clone https://github.com/cgkineo/adapt-mediaGallery.git
cd adapt-mediaGallery
# Install dependencies (if using npm)
npm install
# Test in Adapt Framework
cd /path/to/adapt-framework
adapt install adapt-mediaGallery
# Build and preview
adapt build
adapt previewWe welcome contributions! Please see CONTRIBUTING.md for:
- Code style guidelines
- Pull request process
- Testing requirements
- Documentation standards
Quick Contribution Steps:
- Fork the repository
- Create a feature branch:
git checkout -b feature/my-feature - Make your changes with clear commit messages
- Test thoroughly across devices and browsers
- Submit a pull request with detailed description
See CHANGELOG.md for a detailed version history and migration guides.
Latest Version: 2.0.0 - Full Adapt Authoring Tool support, enhanced accessibility, comprehensive documentation
This project is licensed under the GNU General Public License v3.0 - see the LICENSE file for details.
Copyright © 2024 Kineo and contributors
- MediaElement.js - MIT License
- Adapt Learning Framework - GPL-3.0
Website | Community | Documentation | Twitter
Adapt Learning is a free and open-source e-learning authoring tool that creates responsive, multi-device HTML5 content. The framework is maintained by the Adapt Core Team and a vibrant community of contributors worldwide.
- Adapt Framework on GitHub
- Adapt Authoring Tool
- Community Forums
- Plugin Registry
- Developer Documentation
- 💬 Community Support: Adapt Community Forums
- 🐛 Bug Reports: GitHub Issues
- 📖 Documentation: Wiki
- ⭐ Feature Requests: GitHub Discussions
