@ewc-lib/ewc-card
v1.0.0-alpha
Published
Card component to render a card accepting input parameters
Downloads
16
Keywords
Readme
ewc-card
A flexible, media-rich card component for navigation and content, built with Lit and designed for both HTML and JavaScript usage.
Version: 1.0.0-alpha
Table of contents
- Overview
- Features
- Installation
- Quick usage
- Quick usage (non-bundled / no package manager)
- API
- Events
- Accessibility
- Multiple cards (HTML)
- Basic CSS for flex grid layout
- In-card media options
- Card footer options
- Programmatic usage
- Display multiple cards programmatically
- Getting started (development)
- Peer review workflow
- Media copyright
- License
Overview
ewc-card is a reusable Lit web component for building interactive, media-rich navigation cards.
It supports flexible media (images or video), customisable footer icons and arrows, and works seamlessly in both HTML and JavaScript-driven applications.
Ideal for dashboards, navigation grids, and content-driven UIs.
- Configurable content – The image, title, description (optional), footer icon, and footer arrow can be customised by the consuming application.
- Flexible usage – Supports multiple instances, media from any URL/CDN, and hover/focus effects.
- Peer-review tarballs – Allows testing of builds locally or in staging environments before publishing to NPM.
Model at-a-glance
ewc-card is:
- A container component
- With slots for media and footer elements
- And properties for content and behavior
Features
- Flexible Media – Supports images and videos via the
imageslot (HTML or JavaScript), from any URL, CDN, or cloud source. - Customisable Content – Title, description (optional), labels, and media fully controlled by the consuming application.
- Flexible Footer Icons & Arrows – Supports:
- Inline SVG (fully styleable)
- Font Awesome icons
- Image-based assets (SVG/PNG)
- Built-in Interactions – Hover, focus, and scaling effects handled internally.
- Multiple Instances – Easily render responsive card grids.
- Accessibility – Keyboard support, ARIA roles, and
card-clickevent emission.
Installation
npm i @ewc-lib/ewc-cardQuick usage
import '@ewc-lib/ewc-card';<!-- Quick start: Basic card with no configuration (default settings) -->
<ewc-card></ewc-card>
<!-- Then configure the card -->
<ewc-card
cardId="card-1"
title="Card title"
footerLabelNumber="7"
footerLabel="indicators"
credit="© Credit"
href=""
>
<img slot="image" src="/img/card-image.jpg" alt="Card title" loading="lazy" />
</ewc-card>Quick usage (non-bundled / no package manager)
For applications that do not use a package manager or bundler:
- Download the prebuilt bundle, or grab the ewc-card tarball, or the dist folder from the repository or NPM.
- Copy to your application
- Place the bundle in a publicly accessible folder, e.g., assets/ewc-card/ or dist/ewc-card/.
- Include the component in your HTML
<!-- Quick start: Basic card with no configuration (default settings) -->
<ewc-card></ewc-card>
<!-- Then configure the card -->
<ewc-card
cardId="card-1"
title="Card title"
footerLabelNumber="7"
footerLabel="indicators"
credit="© Credit"
href=""
>
<img slot="image" src="/img/card-image.jpg" alt="Card title" loading="lazy" />
</ewc-card>
<script type="module" src="/path-to/main.js"></script>Notes:
- Ensure the path in the
<script>tag correctly points to the downloaded main.js file. - This allows usage in plain HTML/JS applications without Node, NPM, or a bundler.
API
Properties vs Attributes
Attributes (HTML)
| Name | Type | Description | Required | Default |
| ----------------- | ------ | -------------------------------------- | -------- | ---------------- |
| cardId | String | Unique identifier for the card | Yes | '' |
| title | String | Card title | Yes | 'Card title' |
| description | String | Optional text between title and footer | No | '' |
| footerLabel | String | Footer label text | No | 'Footer label' |
| footerLabelNumber | Number | Number shown before label | No | null |
| credit | String | Image credit text | No | '' |
| href | String | If provided, card acts as a link | No | '' |
Example (HTML)
<ewc-card
title="Card title"
description="Optional truncated description">
</ewc-card>Properties (JavaScript)
| Name | Type | Description | Default |
| ----------------- | ------- | -------------------------------------- | ---------------- |
| cardId | String | Unique identifier for the card | '' |
| title | String | Card title | 'Card title' |
| description | String | Optional text between title and footer | '' |
| footerLabel | String | Footer label text | 'Footer label' |
| footerLabelNumber | Number | Number shown before label | null |
| credit | String | Image credit text | '' |
| href | String | Navigation target | '' |
| clicked | Boolean | Reflects active/selected state | false |
Example (JavaScript):
card.title = 'Example title';
card.description = "Optional truncated description";Note: Attributes are used in HTML. Properties are used when working with the component via JavaScript. In most cases, attributes map to their corresponding properties.
Events
Handling card clicks
| Event | Emitted detail |
|------------|----------------|
| card-click | { cardId, title, description, imageSrc, credit, footerLabel, footerLabelNumber } |
Note: These values are emitted in the
detailobject when the card is clicked, and correspond to the component’s current properties. They are not HTML attributes; use them directly fromevent.detail.
A consuming application can capture any or all of the emitted detail when a card is clicked like this:
const container = document.getElementById('cardContainer');
// If only cardId and title are needed
container.addEventListener('card-click', (event) => {
const { cardId, title } = event.detail;
console.log('Clicked card:', cardId, title);
doSomethingWithcardIdAndTitle(cardId, title);
});If all emitted values are required:
const container = document.getElementById('cardContainer');
// All card details
container.addEventListener('card-click', (event) => {
const { cardId, title, description, imageSrc, credit, footerLabel, footerLabelNumber } = event.detail;
console.log('Clicked card:', event.detail);
doSomethingWithAllEmittedValues(event.detail);
});
doSomethingWithAllEmittedValues(data) {
const cardDetails = document.getElementById('cardDetails');
const details = `
<div class="info-row">
<div class="info-label">Card id</div>
<div class="info-value">${data.cardId}</div>
</div>
<div class="info-row">
<div class="info-label">Card title</div>
<div class="info-value">${data.title}</div>
</div>
<div class="info-row">
<div class="info-label">Optional card description</div>
<div class="info-value">${data.description}</div>
</div>
<div class="info-row">
<div class="info-label">Image source</div>
<div class="info-value">${data.imageSrc}</div>
</div>
<div class="info-row">
<div class="info-label">Credit</div>
<div class="info-value">${data.credit}</div>
</div>
<!-- More emitted data usage -->
`
cardDetails.innerHTML = details
}This allows a consuming application to centrally handle interactions for any card instance.
Accessibility
- Keyboard accessible (Enter / Space)
- Screen readers
- Uses
role="button" - Supports focus states (
:focus-visible) - Emits events for custom navigation logic
Note: Ensure images/icons include meaningful
alttext, and any other accessibility requirements.
Multiple cards (HTML)
You can manually add multiple ewc-card components in your HTML like this:
<div id="cardContainer">
<ewc-card
cardId="card-1"
title="Card Title 1"
footerLabelNumber="1"
footerLabel="items"
credit="© Credit 1"
href="#"
>
<img slot="image" src="/img/card-image-1.jpg" alt="Card Title 1" loading="lazy" />
</ewc-card>
<ewc-card
cardId="card-2"
title="Card Title 2"
footerLabelNumber="2"
footerLabel="items"
credit="© Credit 2"
href="#"
>
<img slot="image" src="/img/card-image-2.jpg" alt="Card Title 2" loading="lazy" />
</ewc-card>
<!-- Add more <ewc-card> components here as required -->
</div>Basic CSS for flex grid layout
#cardContainer {
display: flex;
flex-wrap: wrap;
gap: 16px;
justify-content: flex-start;
}
#cardContainer ewc-card {
flex: 1 1 300px;
max-width: 300px;
}
@media (max-width: 600px) {
#cardContainer ewc-card {
flex: 1 1 100%;
max-width: 100%;
}
}This setup creates a responsive two-column grid on desktop view, collapsing to one column on mobile view.
Grid (optional)
#cardContainer {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 16px;
}In-card media options
The image slot in ewc-card supports flexible media. You can use either images or videos, sourced from any URL, CDN, or cloud service.
Images
- Use standard
<img>tags. - Supports lazy loading and any common formats (JPEG, PNG, WebP, etc.).
- Example:
<ewc-card title="My Card">
<img slot="image" src="/img/example.jpg" alt="Card image" loading="lazy">
</ewc-card>Note: Use an SVG image where possibile for better scalability.
Videos
- Use
videotags to embed playable content. - Supports controls, muted, autoplay, loop, and playsinline.
Example:
<ewc-card title="Video Card">
<video slot="image"
src="/media/example.mp4"
muted
autoplay
loop
playsinline
preload="metadata"
>
</video>
</ewc-card>Note: The
videosrcmust point directly to a video file (e.g..mp4).
URLs to web pages (such as Vimeo or Pixabay pages) will not work unless they expose a direct video file.
Card footer options
Custom icons / arrows
The card footer can accept custom icons and arrows. If custom icon or arrow elements are omitted, the component will render its default fallback icon and arrow.
Note:
- Custom icons and arrows can be resized using inline styles (e.g.
width,height).- For best results, keep icon sizes between
14pxand16px(arrows can be slightly larger if needed).- Larger sizes may break the footer layout or cause alignment issues.
- Use an SVG icon/arrow (inline or image-based) where possibile for better scalability.
Example of a custom icon/arrow using an inline svg:
<!-- Custom icon -->
<svg slot="icon" viewBox="0 0 24 24" fill="none" stroke="green">
<circle cx="12" cy="12" r="8"></circle>
</svg>
<!-- Custom arrow -->
<svg slot="arrow" viewBox="0 0 24 24">
<polygon points="9,6 17,12 9,18"></polygon>
</svg>Custom icons/arrows can include the following:
| Custom Icon / Arrow Type | Notes |
|--------------------------|-------|
| Inline SVG | Fully controllable via style or CSS; works with hover/click effects. |
| Font Awesome | Color can be controlled via inline style or stylesheet; good for icon libraries. |
| Image-based (SVG/PNG/JPG) | Cannot be recolored via CSS; uses the image’s original colors. |
Example
<div id="icon-demo" style="display: flex; gap: 24px; flex-wrap: wrap;">
<!-- Inline SVG icon and arrow -->
<ewc-card cardId="card-inline" title="Card with SVG Icon">
<svg slot="icon" viewBox="0 0 24 24" stroke="currentColor" style="color: #0e47cb;">
<circle cx="12" cy="12" r="8"></circle>
</svg>
<svg slot="arrow" viewBox="0 0 24 24" stroke="currentColor" style="color: #0e47cb;">
<path d="m9 18 6-6-6-6"></path>
</svg>
<img slot="image" src="/img/card-image-1.jpg" alt="Card image" loading="lazy" />
<small>Inline SVG: color controllable via style</small>
</ewc-card>
<!-- Font Awesome icon and arrow -->
<ewc-card cardId="card-fa" title="Card with Font Awesome Icon">
<i slot="icon" class="fa-solid fa-circle-info" style="color: #0e47cb;"></i>
<i slot="arrow" class="fa-solid fa-arrow-right" style="color: #0e47cb;"></i>
<img slot="image" src="/img/card-image-2.jpg" alt="Card image" loading="lazy" />
<small>Font Awesome: color controllable via style</small>
</ewc-card>
<!-- Image-based icon and arrow (fixed colors) -->
<ewc-card cardId="card-image" title="Card with Image Icon">
<img slot="icon" src="/img/myCustomIcon.svg" alt="Custom icon" />
<img slot="arrow" src="/img/myCustomArrow.svg" alt="Custom arrow" />
<img slot="image" src="/img/card-image-3.jpg" alt="Card image" loading="lazy" />
<small>Image-based: colors are fixed</small>
</ewc-card>
<!-- Add more cards as required -->
</div>Footer custom icons & arrows options at a glance
| Approach | Example | Notes |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ |
| Inline | <i slot="icon" class="fa-solid fa-circle-info" style="color: #0e47cb;"></i><svg slot="icon" viewBox="0 0 24 24" fill="none" stroke="green" style="width:16px;height:16px;"><circle cx="12" cy="12" r="8"></circle></svg><svg slot="arrow" viewBox="0 0 24 24" stroke="currentColor" style="color: #0e47cb;"><path d="m9 18 6-6-6-6"></path></svg> | Quick for single cards; color controlled via style. Hover/click effects handled internally. Works for inline SVGs and font icons. |
| Stylesheet | css ewc-card::part(icon) i.fa-circle-info { color: #0e47cb; } ewc-card::part(icon) svg { stroke: green; } ewc-card::part(arrow) svg { color: #0e47cb; } | Keeps styling consistent across multiple cards. Use color or stroke to control icons and SVG arrows. Works for inline SVGs and font icons. |
| Image-based | <img slot="icon" src="/img/myCustomIcon.svg" alt="My icon" /><img slot="arrow" src="/img/myCustomArrow.svg" alt="Arrow" /> | Cannot be recolored with inline styles or CSS color. The image’s original colors are used. Use images if a fixed design is required. |
Programmatic usage
You can create and configure the <ewc-card> component dynamically using JavaScript.
Example
const container = document.getElementById('cardContainer');
const card = document.createElement('ewc-card');
// Set properties
card.cardId = 'card-99';
card.title = 'New dynamic card';
card.footerLabelNumber = 10;
card.footerLabel = 'indicators';
card.credit = '© Credit';
card.href = '#';
// Create and assign slotted image
const img = document.createElement('img');
img.src = '/img/card-image.jpg';
img.alt = 'New dynamic card';
img.loading = 'lazy';
img.slot = 'image';
// Append image to card
card.appendChild(img);
// Append card to container
container.appendChild(card);Notes
- Use JavaScript properties instead of HTML attributes when creating components programmatically.
- The card image must be provided via the
imageslot. - The
creditcan be:- Set as a simple property, or
- Provided via the
creditslot for more complex content.
Navigation behavior (href)
The href property controls how the card behaves when clicked:
If
hrefis provided
The card navigates usingwindow.location.href.If
hrefis omitted
The card does not navigate and only emits acard-clickevent for custom handling.
Optional: Make it reusable
For repeated usage, you can wrap card creation into a helper:
const container = document.getElementById('cardContainer');
container.appendChild(createCard({
cardId: 'card-99',
title: 'New dynamic card',
footerLabelNumber: 10,
footerLabel: 'indicators',
credit: '© Credit',
href: '#',
image: '/img/card-image.jpg'
}));
function createCard(data) {
const card = document.createElement('ewc-card');
card.cardId = data.cardId;
card.title = data.title;
card.footerLabelNumber = data.footerLabelNumber;
card.footerLabel = data.footerLabel;
card.credit = data.credit;
card.href = data.href;
const img = document.createElement('img');
img.src = data.image;
img.alt = data.title;
img.loading = 'lazy';
img.slot = 'image';
card.appendChild(img);
return card;
}Custom icons and arrows
Custom icons and arrows can also be created dynamically using JavaScript. This works the same way as the image slot—create the element, assign the appropriate slot, and append it to the card.
Example (inline SVG)
const card = document.createElement('ewc-card');
card.cardId = 'card-1';
card.title = 'Programmatic Card';
// Icon (inline SVG)
const icon = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
icon.setAttribute('slot', 'icon');
icon.setAttribute('viewBox', '0 0 24 24');
icon.setAttribute('stroke', 'currentColor');
icon.style.color = '#0e47cb';
const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
circle.setAttribute('cx', '12');
circle.setAttribute('cy', '12');
circle.setAttribute('r', '8');
icon.appendChild(circle);
// Arrow (inline SVG)
const arrow = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
arrow.setAttribute('slot', 'arrow');
arrow.setAttribute('viewBox', '0 0 24 24');
arrow.setAttribute('stroke', 'currentColor');
arrow.style.color = '#0e47cb';
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
path.setAttribute('d', 'm9 18 6-6-6-6');
arrow.appendChild(path);
card.appendChild(icon);
card.appendChild(arrow);Example (Font Awesome)
const icon = document.createElement('i');
icon.slot = 'icon';
icon.className = 'fa-solid fa-circle-info';
const arrow = document.createElement('i');
arrow.slot = 'arrow';
arrow.className = 'fa-solid fa-arrow-right';
card.appendChild(icon);
card.appendChild(arrow);Example (image-based)
const icon = document.createElement('img');
icon.slot = 'icon';
icon.src = '/img/myCustomIcon.svg';
icon.alt = 'Icon';
const arrow = document.createElement('img');
arrow.slot = 'arrow';
arrow.src = '/img/myCustomArrow.svg';
arrow.alt = 'Arrow';
card.appendChild(icon);
card.appendChild(arrow);If no custom icon or arrow is provided, the component automatically falls back to its default inline SVGs.
Programmatic video usage
You can also provide video content dynamically using the image slot. This works the same way as images—create a <video> element, assign the slot, and append it to the card.
Example
const card = document.createElement('ewc-card');
card.cardId = 'card-video';
card.title = 'Video Card';
// Create video element
const video = document.createElement('video');
video.src = '/media/example.mp4';
video.autoplay = true;
video.muted = true;
video.loop = true;
video.playsInline = true;
video.preload = 'metadata';
video.slot = 'image';
// Append video to card
card.appendChild(video);
container.appendChild(card);Display multiple cards programmatically
You can generate ewc-card components dynamically using JavaScript. This is useful when you have a list of items, keys, or JSON data.
Example 1: Using an array of keys
<div id="cardContainer"></div>
<script>
// image names as keys
const cardKeys = [
'overall-experience-of-life',
'material-living-conditions',
'productivity-or-main-activity',
'education',
'health',
'leisure-and-social-interactions',
'economic-and-physical-safety',
'governance-and-basic-rights',
'natural-and-living-environment'
];
// Helper to convert keys to title capitalising the first word
function formatTitle(key) {
const text = key.replace(/-/g, ' ');
return text.charAt(0).toUpperCase() + text.slice(1);
}
const container = document.getElementById('cardContainer');
cardKeys.forEach((key, index) => {
const card = document.createElement('ewc-card');
card.cardId = `card-${index + 1}`;
card.title = formatTitle(key);
card.footerLabelNumber = Math.floor(Math.random() * 10) + 1; // demo data
card.footerLabel = 'indicators';
card.credit = '© Unsplash';
// Uncomment to make the card navigate when clicked
// card.href = '#';
const img = document.createElement('img');
img.src = `/img/${key}.jpg`;
img.alt = formatTitle(key);
img.loading = 'lazy';
img.slot = 'image';
card.appendChild(img);
container.appendChild(card);
});
</script>Notes
- Titles are automatically formatted from the image keys (
overall-experience-of-life→Overall Experience Of Life). footerLabelNumberis set randomly here for demo purposes.- You can include an
hrefon a card if you want it to navigate somewhere on click; otherwise, cards are clickable without navigation. - This works with the same CSS flex grid layout as manual cards.
Example 2: Using JSON
You can also dynamically generate ewc-card components from a JSON array. Each object can define properties like cardId, title, footerLabelNumber, footerLabel, credit, href, and image.
<div id="cardContainer"></div>
<script>
// JSON data for the cards
const cardData = [
{
"cardId": "card-1",
"title": "Overall Experience Of Life",
"footerLabelNumber": 7,
"footerLabel": "indicators",
"credit": "© Unsplash",
"href": "#",
"image": "/img/overall-experience-of-life.jpg"
},
{
"cardId": "card-2",
"title": "Material Living Conditions",
"footerLabelNumber": 6,
"footerLabel": "indicators",
"credit": "© Unsplash",
"href": "#",
"image": "/img/material-living-conditions.jpg"
}
// Add more card objects as needed
];
const container = document.getElementById('cardContainer');
cardData.forEach(cardObj => {
const card = document.createElement('ewc-card');
card.cardId = cardObj.cardId;
card.title = cardObj.title;
card.footerLabelNumber = cardObj.footerLabelNumber;
card.footerLabel = cardObj.footerLabel;
card.credit = cardObj.credit;
card.href = cardObj.href;
const img = document.createElement('img');
img.src = cardObj.image;
img.alt = cardObj.title;
img.loading = 'lazy';
img.slot = 'image';
card.appendChild(img);
container.appendChild(card);
});
// Handle card clicks
container.addEventListener('card-click', (event) => {
const { cardId, title } = event.detail;
console.log('Clicked card:', cardId, title);
// Example: display details in a detail pane
const detailPane = document.getElementById('cardDetails');
detailPane.innerHTML = `<h3>${title}</h3><p>Card ID: ${cardId}</p>`;
});
</script>
Notes
- JSON allows you to manage card data separately from HTML.
- Each object maps directly to component properties and slots.
- Works with the same flex grid layout as manual cards.
- Ideal for API-driven or dynamic UIs.
This approach is ideal when fetching card data from an API or maintaining card information in a structured format.
Getting started (development)
Clone the repository and install dependencies:
Requirements
- Node >= 22.12.0
git clone <your-repo-url>
cd ewc-card
npm installLaunching the component
npm run devThe component will be available at:
http://localhost:5174Note: The port can be changed in vite.config.js.
server: {
port: 5174,
...
}Project structure
ewc-card/
├── src/ # Component source (Lit component, styles)
├── public/ # Image and video assets (sample media)
├── dist/ # Generated build output
├── package.json # Package metadata and scripts
├── vite.config.js # Build configuration
└── README.mdRender a card
In the component HTML file:
<!-- Basic card (default settings) -->
<ewc-card></ewc-card>
<!-- Configuration example -->
<ewc-card
cardId="card-1"
title="Economic and physical safety"
description="This is a brief description that will truncate if too long"
footerLabelNumber="7"
footerLabel="indicators"
credit="© Unsplash"
href="#"
>
<img slot="image" src="/img/economic-and-physical-safety.jpg" alt="Economic and physical safety" loading="lazy" />
</ewc-card>Render a card with video
<ewc-card
cardId="video-card"
title="Example video card"
footerLabelNumber="1"
footerLabel="video demo"
credit="© Vimeo"
>
<video
slot="image"
src="/video/mountain-climbing.mp4"
controls
muted
autoplay
loop
playsinline
></video>
</ewc-card>Notes:
- All three examples above are included in the component HTML file.
- The component HTML also includes a CDN link to a Font Awesome css min stylesheet for testing custom footer icons and arrows.
- The HTML file will not be included in the dist build or tarball.
Further details
Consult the sections below:
Notes:
ewc-cardrenders a single card only- It should not be extended to manage multiple cards
- Rendering multiple cards is the responsibility of the consuming application
Building the component
npm run buildCreating a tarball for testing
npm run tarNote: The tarball will be created in the root directory of the component sources.
Peer review workflow
Reviewers can test the component using a tarball before it is published to NPM:
1. Install a tarball using NPM aliases
Place the tarball in the root of the consuming application (recommended), or another location.
If the tarball is in the root:
npm install ewc-lib-ewc-card@file:ewc-lib-ewc-card-1.0.0-alpha.tgzIf the tarball is located elsewhere:
npm install ewc-lib-ewc-card@file:../ewc-lib-ewc-card-1.0.0-alpha.tgz../ewc-lib-ewc-card-1.0.0-alpha.tgz being the path to the location of the tarball
2. Import
import 'ewc-lib-ewc-card';3. HTML remains the same
<ewc-card
cardId="card-1"
title="Card title"
footerLabelNumber="7"
footerLabel="indicators"
credit="© Credit"
href=""
>
<img slot="image" src="/img/card-image.jpg" alt="Card title" loading="lazy" />
</ewc-card>Media copyright
Important: In order to avoid copyright infringement, any media (images/video) that is/are not owned by the consuming application's organisation and/or developer must include a credit.
Example:
<!-- html -->
credit="© Media credit" // JavaScript
card.credit = '© Media credit';license
Licensed under the European Union Public License 1.1 (EUPL-1.1).
- Download the English PDF: https://joinup.ec.europa.eu/sites/default/files/custom-page/attachment/eupl1.1.-licence-en_0.pdf
