Using Spectrum Components in Adobe Edge Delivery Services Blocks

Why Spectrum Components Work Well with EDS
Full code available at: https://github.com/ddttom/spectrum-with-eds
Demo: https://allabout.network/blogs/ddt/integrations/spectrum-component-as-eds-block-in-gdoc
Spectrum Web Components give you professional UI elements that work straight away. You get theming, responsive design, keyboard navigation, and screen reader support without extra work. They slot naturally into EDS's block-based architecture.
The components follow Adobe's design language, so your blocks look and feel like native Adobe experiences. Better yet, they're built on web standards - no framework lock-in or complex build processes.
Creating an Enhanced Card Block with Numbered Slides and Immersive Modal Overlays
Let's build something sophisticated - a card component that combines numbered slide functionality with immersive full-screen modal overlays for content display. This component fetches dynamic content from EDS query-index.json endpoints and provides a modern, visually striking user experience with glassmorphism effects and hero-style typography. It follows the modern EDS pattern for content-driven applications described in our Query-Index PRD.
Here's what the development structure looks like:
/build/spectrum-card/
├── spectrum-card.js
├── spectrum-card.css
├── index.html
├── package.json
├── vite.config.js
└── README.md
This is your development environment. The /blocks/
directory gets created automatically when you build.
Development Workflow
Complexities in the Build Process for Spectrum Components in Adobe Edge Delivery Services Blocks
Integrating Spectrum components within Adobe Edge Delivery Services blocks introduces a multi-layered build process that presents a level of complexity users should be aware of. This process isn't a simple, single-step operation; instead, it involves two distinct stages: component building and a subsequent test build.
Understanding this dual-level structure is crucial for developers working with these technologies. Comprehensive details regarding each of these stages, along with the necessary commands and configurations, can be found in the dedicated documentation file: BUILD_PROCESS.md. This document serves as an essential resource for navigating the intricacies of the build process and ensuring a smooth development workflow.
1. Start Development
cd build/spectrum-card
npm install # First time only
npm run dev # Starts http://localhost:5173
2. Edit Component Files
- Logic:
spectrum-card.js
- Query-index.json integration - Styles:
spectrum-card.css
- Component styling - Test:
index.html
- Test
3. Build and Deploy Changes
# Bundles dependencies and copies to /blocks/ for EDS
npm run build:component
This command:
- Runs
npm install
andnpm run build
in the build directory - Bundles all Spectrum Web Components into a single file
- Copies bundled files to
/blocks/spectrum-card/
for EDS deployment - Creates browser-compatible files that work without module resolution
The Query-Index Pattern
This component fetches dynamic data from EDS query-index.json endpoints. This enables content-driven applications with excellent performance.
Enhanced Features - Numbered Slides and Modal Overlays
Our enhanced Spectrum Card component includes two key features that elevate the user experience:
1. Numbered Slide Badges
Each card displays a circular blue badge with the slide number, positioned in the top-left corner. This provides a clear visual hierarchy and helps users navigate through content sequences.
// Add slide number badge positioned over the card
const slideNumber = document.createElement('div');
slideNumber.textContent = (index + 1).toString(
slideNumber.style.cssText = `
position: absolute; top: 10px; left: 10px;
background-color: #0265DC; color: white;
border-radius: 50%; width: 32px; height: 32px;
display: flex; align-items: center; justify-content: center;
font-size: 14px; font-weight: bold; z-index: 10;
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
`;
2. Immersive Modal Overlay with Full Content
Clicking "Read More" opens a full-screen immersive modal that displays content from the .plain.html
endpoint with a visual design. This provides a magazine-style reading experience with background imagery and glassmorphism effects.
// Fetch plain HTML content for modal display
async function fetchPlainHtml(path) {
try {
const { baseUrl } = getConfig();
const response = await fetch(`${baseUrl}${path}.plain.html`, {
mode: 'cors',
headers: { 'Accept': 'text/html' }
});
if (!response.ok) {
throw new Error(`Failed to fetch: ${response.status}`);
}
return await response.text();
} catch (error) {
console.error('[spectrum-card] fetch error:', error);
return null;
}
}
The immersive modal includes:
- Full-screen background imagery using the card's image for visual impact
- Glassmorphism design with translucent elements and backdrop blur effects
- Hero typography with large-scale text and gradient overlays
- Slide number badge with glassmorphism styling in the top-left corner
- Glassmorphism close button for elegant dismissal
- Click-outside-to-close functionality
- ESC key support for keyboard accessibility
- Responsive design that adapts typography and spacing for mobile devices
- Cross-browser compatibility with webkit prefixes for Safari support
- Loading states with elegant feedback during content fetching
Data Source Configuration
// Default endpoint
const QUERY_INDEX_PATH = '/slides/query-index.json';
Custom endpoint via block content:
Expected Data Format
The component expects query-index.json to return data in this format (see live example at https://allabout.network/slides/query-index.json):
{
"total": 5,
"offset": 0,
"limit": 5,
"data": [
{
"path": "/slides/york-minster",
"title": "York Minster",
"image": "/slides/media_188fa5bcd003e5a2d56e7ad3ca233300c9e52f1e5.png?width=1200&format=pjpg&optimize=medium",
"description": "A magnificent Gothic cathedral with centuries of history and breathtaking architecture",
"lastModified": "1719573871"
},
{
"path": "/slides/the-shambles",
"title": "The Shambles",
"image": "/slides/media_14e918fa88c2a9a810fd454fa04f0bd152c01fed2.jpeg?width=1200&format=pjpg&optimize=medium",
"description": "A picturesque medieval street with overhanging buildings and quaint shops.",
"lastModified": "1720028161"
}
],
"columns": ["path", "title", "image", "description", "lastModified"],
":type": "sheet"
}
The Component File
Here's the core structure of the component (see full implementation at https://github.com/ddttom/spectrum-with-eds):
// spectrum-card.js
// Import Spectrum Web Components
import '@spectrum-web-components/theme/sp-theme.js';
import '@spectrum-web-components/card/sp-card.js';
import '@spectrum-web-components/button/sp-button.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-arrow-right.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-close.js';
// Configuration
const SPECTRUM_CARD_CONFIG = {
CARD_VARIANT: '',
BUTTON_TREATMENT: 'accent',
BUTTON_SIZE: 'm',
MAX_WIDTH: '400px',
QUERY_INDEX_PATH: '/slides/query-index.json',
};
// Fetch content from EDS query-index.json
async function fetchCardData(queryPath) {
try {
const response = await fetch(queryPath, {
mode: 'cors',
headers: { 'Accept': 'application/json' }
});
if (!response.ok) {
throw new Error(`Failed to fetch card data: ${response.status}`);
}
const json = await response.json();
return json.data || [];
} catch (error) {
console.error('[spectrum-card] fetch error:', error);
return [];
}
}
// Create a single card element with enhanced features
function createCard(cardData, index) {
// Create wrapper container for the card and number badge
const cardWrapper = document.createElement('div');
cardWrapper.style.position = 'relative';
cardWrapper.style.maxWidth = SPECTRUM_CARD_CONFIG.MAX_WIDTH;
// Add slide number badge
const slideNumber = document.createElement('div');
slideNumber.textContent = (index + 1).toString();
slideNumber.style.cssText = `
position: absolute; top: 10px; left: 10px;
background-color: #0265DC; color: white;
border-radius: 50%; width: 32px; height: 32px;
display: flex; align-items: center; justify-content: center;
font-size: 14px; font-weight: bold; z-index: 10;
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
`;
cardWrapper.appendChild(slideNumber);
// Create the actual Spectrum Card
const card = document.createElement('sp-card');
card.setAttribute('heading', cardData.title || 'Card Title');
card.style.width = '100%';
// ... Add image, description, and button elements
// See full implementation on GitHub
cardWrapper.appendChild(card);
return cardWrapper;
}
// The decorate function is called by Franklin/EDS
export default async function decorate(block) {
try {
const queryPath = block.dataset.queryPath || SPECTRUM_CARD_CONFIG.QUERY_INDEX_PATH;
const cardData = await fetchCardData(queryPath);
// Create responsive grid container
const cardsContainer = document.createElement('div');
cardsContainer.style.cssText = `
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
`;
// Create cards from data
for (const item of cardData) {
const card = createCard(item);
cardsContainer.appendChild(card);
}
block.appendChild(cardsContainer);
} catch (err) {
console.error('[spectrum-card] decorate error', err);
block.textContent = 'Error loading cards';
}
}
Testing Your Component Locally
The development environment includes hot reload and live preview. Here's a simplified test file:
<html>
<head>
<title>Spectrum Card Test</title>
<script type="module">
import '@spectrum-web-components/theme/theme-light.js';
import '@spectrum-web-components/theme/sp-theme.js';
</script>
</head>
<body>
<sp-theme color="light" scale="medium" system="spectrum">
<!-- Default endpoint test -->
<div class="spectrum-card block"></div>
<!-- Custom endpoint test -->
<div class="spectrum-card block">
<div>/custom/query-index.json</div>
</div>
</sp-theme>
<script type="module">
import decorate from './spectrum-card.js';
document.querySelectorAll('.spectrum-card.block').forEach(decorate);
</script>
</body>
</html>
AEM Emulation Layer Implementation
The project includes an AEM emulation layer built with Node.js that provides a production-accurate testing environment:
// server.js - Core emulation layer implementation
import { createServer } from 'http';
import { readFile, access } from 'fs/promises';
import { join, extname } from 'path';
const PORT = process.env.PORT || 3000;
const PROXY_HOST = 'https://allabout.network';
// Intelligent file resolution strategy
async function handleRequest(req, res) {
const url = req.url === '/' ? '/aem.html' : req.url;
const filePath = join(__dirname, url.startsWith('/') ? url.slice(1) : url);
// 1. Try to serve local file first
if (await fileExists(filePath)) {
console.log(`Serving local file: ${filePath}`);
await serveLocalFile(filePath, res);
return;
}
// 2. Proxy to a live environment if local file missing
console.log(`Proxying request to: ${PROXY_HOST}${url}`);
await proxyRequest(url, res);
}
Key Architecture Features
- Local File Priority: Always serves project files directly for fast development
- Intelligent Proxy: Automatically fetches missing resources from production
- MIME Type Support: Proper content types for all file formats (JS, CSS, images, fonts)
- Error Handling: Graceful fallbacks with detailed logging
- Development Integration: Works with existing development tools
Server Configuration
The emulation layer supports MIME type handling:
const mimeTypes = {
'.html': 'text/html',
'.js': 'application/javascript',
'.css': 'text/css',
'.json': 'application/json',
'.png': 'image/png',
'.jpg': 'image/jpeg',
'.svg': 'image/svg+xml',
'.woff2': 'font/woff2',
'.ttf': 'font/ttf'
};
This configuration:
- Eliminates CORS issues through intelligent proxying
- Provides accurate production environment simulation
- Supports all EDS file types and formats
- Enables real-time development with live data
Vite Configuration
The project uses Vite for development with proxy support for EDS endpoints:
import { defineConfig } from 'vite';
export default defineConfig({
server: {
port: 5173,
proxy: {
'/slides': {
target: 'https://allabout.network',
changeOrigin: true,
secure: true
}
}
},
build: {
lib: {
entry: 'spectrum-card.js',
name: 'SpectrumCard',
fileName: () => 'spectrum-card.js',
formats: ['es']
}
}
});
Full configuration available on GitHub with complete build settings.
Building a Component in a Page
Create a block in your EDS document:
Basic Usage
See my implementation here https://allabout.network/blogs/ddt/integrations/spectrum-component-as-eds-block-in-gdoc
Custom Query Path
Setting Up Content
- Create Content Folder: Create a folder in your EDS project (e.g.,
/slides/
,/products/
) - Add Content Pages: Create individual pages for each slide
- Create Query Index: Add a
query-index
file to the folder with required columns - Configure Metadata: Ensure each page has the required metadata fields
- Publish: Publish the query-index to generate the JSON endpoint
How EDS Integration Works
When you deploy your block to /blocks/spectrum-card/
, EDS automatically calls your decorate
function for each block instance on the page. The block fetches content from the specified query-index.json endpoint and transforms it into fully interactive Spectrum cards.
Authors manage content through familiar document editing tools, while visitors see polished, accessible components that match Adobe's design standards. The beauty lies in this flexibility.
Build Process
The project uses a streamlined build process with dependency bundling:
- Development: Work in
/build/spectrum-card/
with Vite tooling - Testing: Hot reload at
http://localhost:5173
- Building:
npm run build:component
bundles and copies to/blocks/
- Deployment: Copy
/blocks/spectrum-card/
to your EDS project
AEM Emulation Layer Testing
Start the test environment:
# Starts AEM emulation server from root folder
npm run serve
The server provides:
- ✅ Complete environment simulation
- ✅ Local file priority for fast development
- ✅ Intelligent proxy to production
- ✅ Real-time logging for debugging
Full setup instructions and scripts available on GitHub, in README.md
Performance Considerations
Vite handles the heavy lifting of optimisation. It tree-shakes unused code from Spectrum components, ensuring you only ship what you actually use. The components themselves are built for performance, with efficient rendering and minimal runtime overhead.
The query-index pattern provides excellent performance:
- Cached responses: Browser caches JSON responses
- Lazy loading: Images load only when needed
- Progressive enhancement: Works without JavaScript for basic content
- Efficient rendering: Minimal DOM operations
Every interactive element works with keyboards by default. Screen readers announce content properly. Focus indicators appear where expected. These aren't features you add - they come built in.
Common Issues and Solutions
Component not loading data
- Check browser console for fetch errors
- Verify query-index.json endpoint is accessible
- Ensure proxy configuration is correct for development
- Check CORS headers for production deployment
Cards not rendering
- Verify Spectrum dependencies are loaded
- Check that
decorate
function is being called - Ensure
sp-theme
wrapper is present withsystem="spectrum"
CORS Issues
- Development: Ensure proxy is configured in package.json
- Production: Verify CORS headers or use same-origin deployment
- Check browser console for CORS-related errors
User Experience Features
Modal Overlay System
The component includes a sophisticated modal system that provides a smooth reading experience:
// Create and show modal overlay with content
function showContentModal(cardData, index) {
// Create modal overlay
const overlay = document.createElement('div');
overlay.style.cssText = `
position: fixed; top: 0; left: 0;
width: 100%; height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1000; display: flex;
align-items: center; justify-content: center;
`;
// Create modal content container
const modal = document.createElement('div');
modal.style.cssText = `
background-color: white; border-radius: 8px;
max-width: 800px; max-height: 80vh; width: 100%;
position: relative; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
overflow: hidden;
`;
// Fetch and display full content from .plain.html
if (cardData.path) {
fetchPlainHtml(cardData.path).then(html => {
if (html) {
content.innerHTML = html;
}
});
}
// ... Full implementation on GitHub
}
Key Modal Features
- Full Content Display: Fetches and displays complete
.plain.html
content - Professional Styling: Clean, modern design with shadows and rounded corners
- Multiple Close Methods: X button, click outside, and ESC key support
- Responsive Design: Adapts to different screen sizes with max-width and max-height
- Loading States: Shows loading message while fetching content
- Error Handling: Graceful fallback when content is unavailable
- Accessibility: Keyboard navigation and screen reader support
Technical Implementation
The component uses modern JavaScript ES modules and integrates with Adobe's Spectrum Web Components. Here's the key architecture for the numbered badge overlay:
// Create wrapper container for positioning
const cardWrapper = document.createElement('div');
cardWrapper.style.position = 'relative';
// Add numbered slide badge with absolute positioning
const slideNumber = document.createElement('div');
slideNumber.style.position = 'absolute';
slideNumber.style.top = '10px';
slideNumber.style.left = '10px';
slideNumber.style.zIndex = '10';
cardWrapper.appendChild(slideNumber);
// Add the actual Spectrum Card to the wrapper
cardWrapper.appendChild(card);
The full implementation includes:
- ES Module Architecture: Clean imports and exports for maintainability
- Spectrum Web Components: Professional Adobe design system integration
- Modal Overlay Architecture: Full-screen content display with proper event handling
- Async Content Loading: Fetches
.plain.html
content with error handling - Event Management: Proper event listeners with cleanup for memory efficiency
See the complete source code at: https://github.com/ddttom/spectrum-with-eds
Unusual Pattern - Wrapper Container for Numbered Badges
The component uses a wrapper pattern to achieve the numbered badge overlay effect. This is necessary because Spectrum Card's encapsulated styling prevents direct positioning of external elements over it.
// Create wrapper for positioning context
const cardWrapper = document.createElement('div');
cardWrapper.style.position = 'relative';
// Badge needs absolute positioning
const slideNumber = document.createElement('div');
slideNumber.style.position = 'absolute';
slideNumber.style.zIndex = '10';
// Add both to wrapper
cardWrapper.appendChild(slideNumber);
cardWrapper.appendChild(card);
This pattern maintains compatibility with Spectrum Card updates while enabling custom overlays. See the full implementation notes on GitHub for alternative approaches considered.
Development Features
- Live Preview: See component as it appears in EDS with numbered slides
- Hot Reload: Changes update instantly without refresh
- Debug Logs: Component logs fetch and rendering steps, including modal interactions
- Spectrum Integration: Full Adobe design system with theming and icons
- Responsive Testing: Test mobile and desktop layouts with modal overlays
- Error Handling: Graceful handling of network failures and empty data
- Modal Testing: Interactive modal system with real content loading
Moving Beyond Basic Cards
This card example shows the pattern, but you can build far more complex interfaces. The query-index pattern works for any content type:
- Product Catalogues: Fetch product data with prices, categories, and images
- Blog Posts: Display articles with authors, dates, and categories
- Team Members: Show staff with roles, departments, and contact info
- Events: List upcoming events with dates, locations, and registration
Each follows the same integration pattern: create content, configure query-index, fetch and render. The real power comes from combining EDS's document-driven approach with Spectrum's component library and the flexibility of query-index.json for dynamic content.
Authors work in familiar tools while developers build sophisticated experiences. Everyone wins - especially your users who get fast, accessible, professional interfaces that work everywhere, with Adobe's design language and tested components.
Current Status
The Spectrum Card component is production-ready with:
✅ Numbered Slide Badges - Visual hierarchy indicators
✅ Modal Overlay System - In-page content display
✅ Full Content Loading - Fetches .plain.html
content
✅ Professional Styling - Adobe Spectrum design
✅ Multiple Close Methods - X button, click outside, ESC key
✅ Responsive Design - Works across all devices
✅ Error Handling - Graceful fallbacks
✅ Accessibility - Keyboard and screen reader support
The component successfully demonstrates how to build professional, accessible web components using Adobe's design system while maintaining simplicity and performance.
Get the Code
Full source code and documentation available at: https://github.com/ddttom/spectrum-with-eds
The repository includes:
- Complete component implementation with all features
- Development environment setup
- Build scripts and configuration
- Example usage and testing files
- Detailed technical documentation
Resources and Next Steps
For further learning, explore the spectrum-components, Adobe Edge Delivery Services, and the project repository.
⭐ Found this helpful? Star the https://github.com/ddttom/spectrum-with-eds on GitHub!
Related Articles