@browser.style/content-card
v1.0.1
Published
Modular content card components for web applications with responsive layouts and flexible theming
Maintainers
Readme
Content Card
Content Card is a generic card-component that can be extended into a variety of card types, such as articles, products, videos, and more. It can output structured markup for SEO and rich snippets. It consists of two areas: Media Area and Content Area.
Media Area
This area contains visual elements like images or videos.
- Media Source(s): The primary visual content.
- Image: Can include
src,srcsetfor different resolutions,alttext,width,height,decodeandloadingattributes. - Video: Can include
src,posterimage,tracksfor subtitles/captions, and playeroptions(like autoplay, controls, loop, muted). - YouTube: Can be embedded.
- Image: Can include
- Media Caption: A
figcaptionto describe the media. - Overlays: Elements positioned over the media.
- Ribbon: A banner-like element, often used for "Featured" status.
- Sticker/Badge: A smaller element, like a "New" sticker.
Content Area
This area contains the main information of the card, including text, metadata, and actions.
Core Content:
- Category/Tagline: A short string to categorize the content (e.g., "Technology").
- Headline: The main title of the item. The heading level (e.g.,
h2,h3) can be specified with theheadlineTagproperty in thecontentobject. Defaults toh2. - Subheadline: An optional secondary title.
- Summary/Text: A paragraph of descriptive text. The component can render both a
summaryand a more detailedtextfield, which can contain simple HTML.
Metadata & Engagement:
- Publication Info:
publisheddate/time (can be formatted as absolute, relative, or a full string).modifieddate/time.readingTime.
- Engagement Counts:
reactions(e.g., Likes, Dislikes, withicon,count, andactivestate).commentCount.shareCount.viewCount.
- Publication Info:
Attribution & Grouping:
- Authors:
- A list of one or more authors.
- Each author can have a
name,role, and a clickableavatarimage. - Each author can have a list of
contacts(e.g., email, Twitter) which are rendered as links. - The author's name can also be a
urlto their profile.
- Tags:
- A list of tags, where each tag is a link (
url) with aname.
- A list of tags, where each tag is a link (
- Authors:
Product Information (for product-type cards):
- Product Name: The name of the product.
- Price:
currentprice.originalprice (for showing discounts).currency.discountText(e.g., "Save 28%").
- Availability: Stock status (e.g., "InStock").
- Rating:
- Star rating (e.g., 4.7 out of 5).
countof ratings.
Actions:
- A list of actions, split into
primaryandsecondarygroups. - Each action can be a
linkor abutton. - Can have
text, aurl(for links), anicon, and anariaLabel. - A link can be a full-card wrapper by setting
isWrapper: true. - A button can trigger a popover by setting
popover: true.
- A list of actions, split into
Popover
A generic popover can be attached to any card type.
- Activation: Add a
popoverobject to the root of the card's data. - Trigger: An action button with
"popover": truewill open it. - Content Types: The
popoverobject has atypewhich determines its content.type: "video": Expects avideoobject withsrcandoptions.type: "article": Expects acontentstring, which can be HTML.
Card Types
Here are the card types supported by the component, with examples in data.json. Each card type can optionally include Schema.org structured data markup when useSchema: true (default setting).
1. Article Card
A standard card for blog posts, news, or articles.
- Schema.org Type:
Article(optional) - Media Area:
- Media Source(s): An
Imageis typical. - Media Caption: Optional.
- Media Source(s): An
- Content Area:
- Core Content:
Category/Tagline,Headline, optionalSubheadline, andSummary/Text. - Metadata & Engagement:
Publication Info(especiallypublisheddate andreadingTime), andEngagement Counts. - Attribution & Grouping:
AuthorsandTags. - Actions: A primary "Read more" link and secondary "Save" or "Share" buttons.
- Core Content:
2. Product Card
Designed to showcase a product for e-commerce.
- Schema.org Type:
Product(optional) - Media Area:
- Media Source(s):
Imageor a shortVideo. - Overlays:
RibbonorStickerto highlight sales or new items (e.g., "20% OFF").
- Media Source(s):
- Content Area:
- Core Content:
Category/Taglinefor the product category,Headlinefor the product name. - Product Information:
Price,Availability, andRatingare key components. - Actions: A primary "Add to Cart" or "View Details" button, and a secondary "Add to Wishlist" action.
- Core Content:
3. Video Card
A card focused on presenting a video.
- Schema.org Type:
VideoObject(optional) - Media Area:
- Media Source(s): A
VideoorYouTubeembed, usually with aposterimage to attract clicks.
- Media Source(s): A
- Content Area:
- Core Content:
Headlinefor the video title, andSummary/Textfor a brief description. - Metadata & Engagement:
Publication Info(upload date) andEngagement Counts(especiallyviewCount). - Actions: The primary action would be to play the video, which could happen in a popover or by navigating to a new page.
- Core Content:
4. Author/Profile Card
A simple card to feature a person.
- Schema.org Type:
Person(optional) - Media Area:
- Media Source(s): An
Imagefor the author's avatar/profile picture.
- Media Source(s): An
- Content Area:
- Attribution:
Authorssection would be the main content, showingname,role, andcontacts. - Actions: A primary action to "View Profile" or follow links to social media.
- Attribution:
5. Event Card
Used to announce and provide details about an event.
- Schema.org Type:
Eventor specific event types likeBusinessEvent(optional) - Media Area: A promotional
Image. - Content Area:
Headlinefor the event title,summaryfor the description, andpublishedfor the date. - Actions: "Get Tickets" or a "View Agenda" button that can open a
popoverwith more details.
6. Poll Card
An interactive card to engage users with a question.
- Schema.org Type:
Question(optional) - Content Area:
Headlinefor the poll question. - Actions: A series of buttons representing the poll options.
- Engagement:
viewCountcan show how many people have participated.
7. Quote Card
A simple, visual card for displaying quotes.
- Schema.org Type:
Quotation(optional) - Media Area: An optional background
Image. - Content Area:
summaryfor the quote itself. - Attribution:
Authorssection to attribute the quote. - Actions: A "Share" button.
8. News Card
Similar to Article Card but specifically for news content with NewsArticle schema.
- Schema.org Type:
NewsArticle(optional) - Media Area:
- Media Source(s): An
Imageis typical, often with news-specific overlays like "LIVE" ribbons. - Overlays:
Ribbonfor breaking news or live events,Stickerfor "New" or "Updated" status.
- Media Source(s): An
- Content Area:
- Core Content:
Category(e.g., "World News", "Technology"),Headline, optionalSubheadline, andSummary. - Metadata & Engagement:
Publication Infowith bothpublishedandmodifieddates,readingTime, and engagement metrics. - Attribution & Grouping:
Authors(journalists/reporters) and news-specificTags. - Actions: "Read More", "Watch Highlights" (for sports), or sharing options.
- Core Content:
9. Recipe Card
A specialized card for food recipes with Recipe schema.
- Schema.org Type:
Recipe(optional) - Media Area:
- Media Source(s): High-quality food
Image. - Overlays:
Ribbonfor "Popular", "Featured", or dietary badges like "Vegetarian".
- Media Source(s): High-quality food
- Content Area:
- Core Content:
Category(cuisine type),Headline(recipe name), andSummary(brief description). - Recipe Information:
prepTime: Preparation time.cookTime: Cooking time.servings: Number of servings.instructions: Step-by-step cooking instructions.- Ingredients list in the
textfield as an array.
- Metadata & Engagement:
readingTime(total time), star ratings, and engagement counts. - Attribution:
Authors(chefs/recipe creators). - Actions: "Save Recipe", "Share", or "View Full Recipe".
- Core Content:
10. Event Card
Designed for events, conferences, and gatherings with Event schema.
- Schema.org Type:
Eventor specific subtypes likeBusinessEvent(optional) - Media Area:
- Media Source(s): Event promotional
Image.
- Media Source(s): Event promotional
- Content Area:
- Core Content:
Category(e.g., "BusinessEvent", "Conference"),Headline(event name), andSummary. - Event Information:
startDateandendDate: Event timing.location: Venue name and address.organizer: Event organizer information.offers: Ticket pricing information.status: Event status (Scheduled, Cancelled, etc.).attendanceMode: Online, offline, or mixed.
- Actions: "View Agenda", "Get Tickets", or "Register" buttons that can open popovers with more details.
- Core Content:
11. Business Card
A professional card for local businesses with LocalBusiness schema.
- Schema.org Type:
LocalBusiness(optional) - Media Area:
- Media Source(s): Business logo or storefront
Image.
- Media Source(s): Business logo or storefront
- Content Area:
- Core Content:
Headline(business name) andSummary(business description). - Business Information:
address: Complete business address with street, city, region, postal code.geo: Latitude and longitude coordinates.mapProvider: Configuration for embedded maps (OpenStreetMap, Google Maps, etc.).telephoneandemail: Contact information.website: Business website URL.sameAs: Social media profiles array.foundingDate: When the business was established.openingHours: Business hours with both schema and display formats.
- Interactive Elements: Embedded map showing business location.
- Actions: "Visit Website", "Call Now", "Get Directions".
- Core Content:
12. Poll Card
An interactive card for user polls and surveys.
- Schema.org Type:
Question(optional) - Content Area:
- Core Content:
Category("Community Poll"),Headline(poll question), andSummary. - Poll Options: Array of choices in the
textfield withid,headline, and description. - Poll Configuration:
endpoint: API endpoint for vote submission.allowMultiple: Whether multiple selections are allowed.showResults: When to show results ("afterVote", "always", etc.).totalVotes: Current vote count.labels: Customizable text labels for the interface.
- Interactive Form: Radio buttons or checkboxes for voting.
- Actions: "Vote" button with form submission functionality.
- Engagement:
viewCountshowing poll participation.
- Core Content:
13. FAQ Card
A card for frequently asked questions with FAQPage schema.
- Schema.org Type:
FAQPage(optional) - Content Area:
- Core Content:
Headline("Frequently Asked Questions") and optional category. - FAQ Items: Array of questions and answers in the
textfield.- Each item has
headline(question) andtext(answer).
- Each item has
- Interactive Elements: Collapsible details/summary elements for each Q&A pair.
- Accordion Behavior: Grouped details elements with shared names for exclusive expansion.
- Core Content:
14. Timeline Card
A card for displaying chronological events with EventSeries schema.
- Schema.org Type:
EventSeries(optional) - Content Area:
- Core Content:
Category(e.g., "History"),Headline(timeline title). - Timeline Items: Ordered list of events in the
textfield.- Each item has
headline(time period/date) andtext(event description). - Optional
startDate,endDate, andlocationfor enhanced schema markup.
- Each item has
- Visual Structure: Ordered list presentation with timeline styling.
- Core Content:
Schema.org Integration
Overview
All card types support optional Schema.org structured data markup, which enhances SEO and enables rich snippets in search results. Schema markup is enabled by default (useSchema: true) but can be disabled per card or globally.
Disabling Schema Markup
// Disable for a specific card
const cardSettings = { useSchema: false };
card.dataset = { data: cardData, settings: cardSettings };
// Disable via HTML attribute
<article-card settings='{"useSchema": false}'></article-card>Schema Benefits
- SEO Enhancement: Improved search engine understanding of content
- Rich Snippets: Enhanced search result displays with ratings, prices, dates
- Accessibility: Better screen reader interpretation
- Social Sharing: Enhanced link previews on social platforms
- Voice Search: Better compatibility with voice assistants
Schema Properties by Card Type
Article/News Cards
headline: Main titlearticleSection: CategorydatePublished: Publication datedateModified: Last update datedescription: Summary textarticleBody: Full contentauthor: Author information
Recipe Cards
name: Recipe titledescription: Recipe summaryrecipeCategory: Cuisine typeprepTime: Preparation timecookTime: Cooking timerecipeYield: Number of servingsrecipeIngredient: List of ingredientsrecipeInstructions: Cooking steps
Event Cards
name: Event namedescription: Event descriptionstartDate: Event start timeendDate: Event end timelocation: Venue informationorganizer: Event organizereventStatus: Event statuseventAttendanceMode: Attendance type
Business Cards
name: Business namedescription: Business descriptionaddress: Full address detailsgeo: Geographic coordinatestelephone: Phone numberemail: Email addressopeningHours: Business hourssameAs: Social media profiles
Product Cards
name: Product namedescription: Product descriptionsku: Product identifiercategory: Product categoryoffers: Pricing informationaggregateRating: Customer ratings
Component Architecture
BaseCard Class
The BaseCard class serves as the foundation for all card types, providing:
- Schema.org Support: Automatic structured data markup when
useSchema: true(default). - Settings Management: Configurable styling and behavior through the
settingsattribute. - Responsive Images: Integration with layout-aware srcset generation.
- Lifecycle Management: Proper initialization and attribute change handling.
- Extensibility: Abstract
render()method for subclass implementation.
Key Features
- Custom Elements: Each card type is registered as a custom element (e.g.,
<article-card>,<recipe-card>). - Data Binding: Cards accept data through the
datasetproperty or by settingdatadirectly. - Progressive Enhancement: Works without JavaScript for basic content display.
- Accessibility: Built-in ARIA attributes, semantic HTML, and keyboard navigation support.
- Performance: Lazy loading for media, efficient rendering, and minimal DOM manipulation.
Usage Examples
// Setting data programmatically
const articleCard = document.createElement('article-card');
articleCard.dataset = {
data: articleData,
settings: {
useSchema: true,
styles: { 'cc-headline': 'class="custom-headline"' }
}
};
// Using settings attribute
<recipe-card settings='{"useSchema": false}'></recipe-card>
// Accessing card properties
const settings = articleCard.settings;
const data = articleCard.data;Data Structure
Common Data Properties
All card types share a common data structure with the following top-level properties:
{
"id": "unique-identifier", // Unique card identifier
"type": "article|news|recipe|...", // Card type for component selection
"media": { ... }, // Media configuration (optional)
"ribbon": { ... }, // Ribbon overlay (optional)
"sticker": { ... }, // Sticker/badge overlay (optional)
"content": { ... }, // Core content data
"authors": [...], // Author information (optional)
"engagement": { ... }, // Engagement metrics (optional)
"tags": [...], // Tag/category links (optional)
"links": [...], // Navigation links (optional)
"actions": [...], // Action buttons (optional)
// Type-specific properties (recipe, event, business, poll, etc.)
}Media Configuration
"media": {
"sources": [
{
"type": "image|video",
"src": "path/to/media",
"alt": "Description",
"srcset": "responsive-sizes", // Optional
"width": 800, // Optional
"height": 600, // Optional
"loading": "lazy|eager", // Optional
"poster": "path/to/poster" // For videos
}
],
"caption": "Media description" // Optional figcaption
}Overlay Elements
// Ribbon - larger banner overlay
"ribbon": {
"text": "FEATURED",
"style": "featured|live|popular",
"color": "#ff0000" // Optional custom color
}
// Sticker - smaller badge overlay
"sticker": {
"text": "NEW",
"style": "badge|pill",
"position": "top-left|top-right|bottom-left|bottom-right"
}Content Structure
"content": {
"category": "Category Name", // Content categorization
"headline": "Main Title", // Primary heading
"headlineTag": "h2", // HTML heading level (default: h2)
"subheadline": "Secondary Title", // Optional subtitle
"summary": "Brief description", // Short description text
"text": "Detailed content", // Full content (can be HTML or array)
"published": {
"datetime": "2025-07-16T10:00:00Z",
"formatted": "July 16, 2025"
},
"modified": { // Optional update information
"datetime": "2025-07-16T12:00:00Z",
"formatted": "(Updated July 16, 2025)"
},
"readingTime": "5 min read" // Time estimate
}Author Information
"authors": [
{
"name": "John Doe",
"role": "Senior Developer", // Optional job title/role
"avatar": "path/to/avatar.jpg", // Optional profile image
"url": "https://johndoe.com", // Optional profile link
"contacts": [ // Optional contact methods
{
"type": "email|twitter|linkedin",
"url": "mailto:[email protected]",
"label": "Email John"
}
]
}
]Engagement Metrics
"engagement": {
"reactions": [
{
"type": "like|dislike|love",
"icon": "thumbs_up",
"count": 150,
"value": 4.5, // For ratings (optional)
"max": 5, // Maximum rating value (optional)
"min": 0, // Minimum rating value (optional)
"active": false, // User's current state (optional)
"ariaLabel": "Like this post"
}
],
"commentCount": 25,
"shareCount": 80,
"viewCount": 1500
}Actions and Links
"links": [
{
"url": "https://example.com",
"text": "Read More",
"icon": "external-link", // Optional icon
"hideText": false, // Show/hide text (default: false)
"isWrapper": false // Make entire card clickable (default: false)
}
]
"actions": [
{
"text": "Save Article",
"icon": "bookmark", // Optional icon
"url": "https://save.com", // For link actions
"ariaLabel": "Save this article", // Accessibility label
"popover": { // For popover actions
"content": "<h3>Saved!</h3><p>Article saved to your reading list.</p>"
},
"attributes": { // Additional HTML attributes
"type": "submit",
"data-action": "save"
}
}
]Type-Specific Properties
Recipe Data
"recipe": {
"prepTime": "PT15M", // ISO 8601 duration or readable format
"cookTime": "PT30M",
"servings": "4",
"instructions": [ // Step-by-step instructions
"Preheat oven to 350°F",
"Mix ingredients in a bowl",
"Bake for 30 minutes"
]
}Event Data
"event": {
"startDate": "2025-10-25T09:00:00",
"endDate": "2025-10-26T17:00:00",
"location": {
"name": "Convention Center",
"address": "123 Main St, City, State 12345"
},
"organizer": {
"name": "Event Organization"
},
"offers": [
{
"name": "Early Bird",
"price": "299",
"currency": "USD"
}
],
"status": "Scheduled", // EventStatus
"attendanceMode": "OfflineEventAttendanceMode"
}Business Data
"business": {
"address": {
"streetAddress": "123 Main Street",
"addressLocality": "San Francisco",
"addressRegion": "CA",
"postalCode": "94105",
"addressCountry": "US"
},
"geo": {
"latitude": "37.7749",
"longitude": "-122.4194",
"mapProvider": {
"type": "openstreetmap",
"name": "OpenStreetMap",
"url": "https://www.openstreetmap.org/export/embed.html?bbox={lng1},{lat1},{lng2},{lat2}",
"latOffset": 0.003,
"lngOffset": 0.005,
"zoom": 15
}
},
"telephone": "+1-415-555-0123",
"email": "[email protected]",
"website": "https://business.com",
"sameAs": [ // Social media profiles
"https://facebook.com/business",
"https://twitter.com/business"
],
"foundingDate": "2018-03-15",
"openingHours": [
{
"schema": "Mo-Fr 06:00-20:00", // Schema.org format
"display": "Monday - Friday: 6:00 AM - 8:00 PM"
}
]
}Poll Data
"poll": {
"endpoint": "./api/polls/poll-1.json",
"allowMultiple": false, // Allow multiple selections
"showResults": "afterVote", // When to show results
"totalVotes": 5420,
"labels": { // Customizable UI labels
"vote": "Vote",
"submitVote": "Submit Vote",
"results": "Results",
"totalVotes": "Total votes:",
"votes": "votes"
}
}Below is the proposed semantic HTML markup for each component part.
Media Area
<figure class="ic-media">
<!-- Image -->
<img
class="ic-media-image"
src="..."
srcset="..."
alt="..."
width="..."
height="..."
loading="lazy"
>
<!-- Video -->
<video
class="ic-media-video"
src="..."
poster="..."
controls
autoplay
muted
loop
>
<track kind="captions" src="..." srclang="en" label="English">
</video>
<!--
Overlays
Using role="status" helps assistive technologies identify these as status indicators.
-->
<div class="ic-ribbon" role="status">FEATURED</div>
<span class="ic-sticker" role="status">NEW</span>
<!-- Caption -->
<figcaption class="ic-media-caption">This is a caption.</figcaption>
</figure>Note on Gallery Usage: The <figure> element is flexible and can contain multiple images or videos to create a small gallery. The figcaption would then describe the entire gallery.
<figure class="ic-media">
<img class="ic-media-image" src="gallery-1.jpg" alt="...">
<img class="ic-media-image" src="gallery-2.jpg" alt="...">
<figcaption class="ic-media-caption">A gallery of items.</figcaption>
</figure>Content Area
<article class="ic-content">
<!-- Core Content -->
<strong class="ic-category">Technology</strong>
<!-- The heading tag below is dynamic (h2, h3, etc.) based on content.headlineTag -->
<h2 class="ic-headline">Card Headline</h2>
<h3 class="ic-subheadline">Card Subheadline</h3>
<p class="ic-summary">A brief summary of the content.</p>
<div class="ic-text">
<p>More detailed text can go here, including <strong>HTML</strong> elements.</p>
</div>
<!-- Metadata & Engagement -->
<div class="ic-meta">
<time class="ic-published" datetime="2023-10-01T12:00:00Z">October 1, 2023</time>
<span class="ic-reading-time">5 min read</span>
</div>
<div class="ic-engagement">
<button class="ic-reaction" aria-pressed="false">
<span class="ic-reaction-icon">👍</span> 100
</button>
<span class="ic-comments">25 comments</span>
<span class="ic-views">1.5k views</span>
</div>
<!--
Attribution & Grouping
The <address> tag is used here because it semantically represents the contact information
for the author(s) of the parent <article> element.
-->
<address class="ic-authors">
<div class="ic-author">
<a href="/authors/john-doe" class="ic-author-link">
<img class="ic-avatar" src="..." alt="John Doe" width="40" height="40">
<span class="ic-author-name">John Doe</span>
</a>
<span class="ic-author-role">Author</span>
</div>
<!-- Repeat .ic-author div for multiple authors -->
</address>
<ul class="ic-tags">
<li><a href="/tags/tech" class="ic-tag">Technology</a></li>
</ul>
<!-- Product Information -->
<section class="ic-product">
<h4 class="ic-product-name">Premium Course</h4>
<div class="ic-product-price">
<span>USD 49.99</span>
<del class="ic-product-price-original">USD 69.99</del>
<span class="ic-product-discount">Save 28%</span>
</div>
<div class="ic-product-rating" role="img" aria-label="Rating: 4.7 out of 5 stars">
<span aria-hidden="true">★★★★☆</span>
<span>(4.7/5 from 356 ratings)</span>
</div>
<span class="ic-product-availability">In Stock</span>
</section>
<!-- Actions -->
<div class="ic-actions">
<a href="..." class="ic-action-primary">
<span class="ic-action-icon">▶</span> Read more
</a>
<button type="button" class="ic-action-secondary">
<span class="ic-action-icon">...</span> Save
</button>
</div>
</article>
<!-- Popover Structure -->
<div id="popover-id" popover class="ic-popover">
<button type="button" popovertarget="popover-id" popovertargetaction="hide" class="ic-popover-close">✕</button>
<!-- Popover content is rendered here based on type (video, article, etc.) -->
<div class="ic-popover-article">...</div>
</div>Customization and Styling
Settings Configuration
Each card can be customized through the settings object:
const settings = {
// Schema.org structured data
useSchema: true, // Enable/disable schema markup (default: true)
// Custom styling
styles: {
'cc-headline': 'class="custom-headline text-lg font-bold"',
'cc-summary': 'class="text-gray-600"',
'cc-content': 'class="p-4"'
},
// Responsive image configuration
srcsetBreakpoints: [280, 500, 720, 1080, 1440], // Custom breakpoints
layoutIndex: 0, // Layout position for responsive sizing
layoutSrcsets: [...], // Pre-calculated srcset configurations
layoutSrcset: "...", // Computed srcset for current layout
// Component-specific settings
componentSettings: {
// Card-type specific configurations
}
};CSS Class Structure
The component uses a consistent CSS class naming convention:
/* Base component classes */
.cc /* Base card container */
.cc-content /* Content area wrapper */
.cc-media /* Media area wrapper */
/* Content elements */
.cc-category /* Content category/tagline */
.cc-headline /* Primary heading */
.cc-subheadline /* Secondary heading */
.cc-summary /* Brief description */
.cc-text /* Detailed content */
/* Metadata and engagement */
.cc-meta /* Publication metadata wrapper */
.cc-published /* Publication date */
.cc-reading-time /* Reading/prep time */
.cc-engagement /* Engagement metrics wrapper */
.cc-reaction /* Reaction buttons */
.cc-comments /* Comment count */
.cc-views /* View count */
/* Attribution */
.cc-authors /* Authors wrapper */
.cc-author /* Individual author */
.cc-avatar /* Author avatar image */
.cc-author-name /* Author name */
.cc-author-role /* Author role/title */
.cc-tags /* Tags wrapper */
.cc-tag /* Individual tag */
/* Actions and links */
.cc-actions /* Actions wrapper */
.cc-action-primary /* Primary action button */
.cc-action-secondary /* Secondary action button */
.cc-links /* Links wrapper */
/* Media elements */
.cc-media-image /* Media images */
.cc-media-video /* Media videos */
.cc-media-caption /* Media captions */
.cc-ribbon /* Ribbon overlays */
.cc-sticker /* Sticker/badge overlays */
/* Type-specific classes */
.cc-recipe-meta /* Recipe metadata */
.cc-recipe-ingredients /* Recipe ingredients */
.cc-recipe-instructions /* Recipe instructions */
.cc-event-location /* Event location */
.cc-event-organizer /* Event organizer */
.cc-business-map /* Business map embed */
.cc-faq /* FAQ wrapper */
.cc-faq-item /* FAQ item */
.cc-faq-title /* FAQ question */
.cc-faq-panel /* FAQ answer */
.cc-timeline /* Timeline wrapper */
.cc-timeline-item /* Timeline item */
.cc-timeline-headline /* Timeline event title */
.cc-timeline-text /* Timeline event description */
.cc-poll-form /* Poll form */
.cc-poll-option /* Poll option */
.cc-poll-results /* Poll results */Style Injection
The getStyle() utility function allows dynamic style injection:
// In card render methods
${getStyle('cc-headline', settings)}
// Outputs: class="cc-headline" or custom styles from settings.styles['cc-headline']
// Custom styles example
settings.styles['cc-headline'] = 'class="text-2xl font-bold text-blue-600"';Responsive Behavior
Cards are designed to be responsive by default:
- Flexible Layout: Content adapts to container width
- Responsive Images: Automatic srcset generation based on layout context
- Mobile-First: Optimized for touch interactions and small screens
- Progressive Enhancement: Works without JavaScript
Theme Integration
Cards can integrate with various CSS frameworks and design systems:
Tailwind CSS Example
const tailwindSettings = {
styles: {
'cc-content': 'class="p-6 bg-white rounded-lg shadow-md"',
'cc-headline': 'class="text-xl font-bold text-gray-900 mb-2"',
'cc-summary': 'class="text-gray-600 leading-relaxed"',
'cc-actions': 'class="flex gap-2 mt-4"',
'cc-action-primary': 'class="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700"'
}
};CSS Custom Properties
.cc {
--cc-border-radius: 8px;
--cc-padding: 1rem;
--cc-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
--cc-primary-color: #3b82f6;
--cc-text-color: #374151;
}
.cc-content {
padding: var(--cc-padding);
border-radius: var(--cc-border-radius);
box-shadow: var(--cc-shadow);
color: var(--cc-text-color);
}Implementation Examples
Basic Usage
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="./index.css">
</head>
<body>
<!-- Article Card -->
<article-card id="article-1"></article-card>
<!-- Recipe Card with custom settings -->
<recipe-card
id="recipe-1"
settings='{"useSchema": true, "styles": {"cc-headline": "class=\"large-title\""}}'>
</recipe-card>
<script type="module">
import './cards/ArticleCard.js';
import './cards/RecipeCard.js';
// Load and set data
fetch('./data.json')
.then(response => response.json())
.then(data => {
const articleCard = document.getElementById('article-1');
const articleData = data.find(item => item.id === 'article-1');
articleCard.dataset = { data: articleData };
const recipeCard = document.getElementById('recipe-1');
const recipeData = data.find(item => item.id === 'recipe-1');
recipeCard.dataset = { data: recipeData };
});
</script>
</body>
</html>Dynamic Card Creation
import { ArticleCard } from './cards/ArticleCard.js';
import { RecipeCard } from './cards/RecipeCard.js';
import { BusinessCard } from './cards/BusinessCard.js';
class CardRenderer {
static cardTypes = {
'article': ArticleCard,
'recipe': RecipeCard,
'business': BusinessCard,
// ... other card types
};
static createCard(data, settings = {}) {
const CardClass = this.cardTypes[data.type];
if (!CardClass) {
console.warn(`Unknown card type: ${data.type}`);
return null;
}
const card = new CardClass();
card.dataset = { data, settings };
return card;
}
static renderCards(container, cardsData, globalSettings = {}) {
const fragment = document.createDocumentFragment();
cardsData.forEach(cardData => {
const card = this.createCard(cardData, globalSettings);
if (card) {
fragment.appendChild(card);
}
});
container.appendChild(fragment);
}
}
// Usage
const container = document.getElementById('cards-container');
const cardsData = await fetch('./data.json').then(r => r.json());
const settings = {
useSchema: true,
styles: {
'cc-content': 'class="card-content"'
}
};
CardRenderer.renderCards(container, cardsData, settings);Custom Card Type
import { BaseCard } from '../base/BaseCard.js';
import { getStyle, renderActions, renderHeader, renderMedia } from '../base/utils.js';
export class CustomCard extends BaseCard {
constructor() {
super();
}
render() {
const renderContext = this._setSchema('Thing'); // Set appropriate schema
if (!renderContext) return '';
const { settings, useSchema, content, headlineTag } = renderContext;
const { customData = {} } = this.data;
return `
${this.data.media ? renderMedia(this.data.media, this.data.ribbon, this.data.sticker, useSchema, settings) : ''}
<div ${getStyle('cc-content', settings)}>
${renderHeader(content, settings)}
${content.headline ? `<${headlineTag} ${getStyle('cc-headline', settings)} ${useSchema ? 'itemprop="name"' : ''}>${content.headline}</${headlineTag}>` : ''}
<!-- Custom content rendering -->
${customData.specialField ? `<div ${getStyle('cc-custom-field', settings)}>${customData.specialField}</div>` : ''}
${renderActions(this.data.actions, useSchema, settings)}
</div>
`;
}
}
// Register the custom element
customElements.define('custom-card', CustomCard);API Reference
BaseCard Methods
dataset (getter/setter)
Sets or gets the complete card data and settings.
card.dataset = { data: cardData, settings: cardSettings };
const { data, settings } = card.dataset;data (getter)
Returns the card's data object.
const cardData = card.data;settings (getter)
Returns the resolved settings object with defaults applied.
const cardSettings = card.settings;getStyle(componentName)
Returns the style string for a given component class name.
const headlineStyle = card.getStyle('cc-headline');
// Returns: 'class="cc-headline"' or custom stylesUtility Functions
The utils.js module provides common rendering functions:
renderMedia(media, ribbon, sticker, useSchema, settings)
Renders the media area with images, videos, and overlays.
renderHeader(content, settings)
Renders the category/tagline header section.
renderAuthors(authors, useSchema, settings)
Renders author information with avatars and contact links.
renderEngagement(engagement, useSchema, settings)
Renders engagement metrics (likes, comments, shares, views).
renderTags(tags, settings)
Renders tag links.
renderLinks(links, settings, actions)
Renders navigation links.
renderActions(actions, useSchema, settings)
Renders action buttons with popover support.
getStyle(className, settings)
Returns appropriate style attributes for a given class name.
Browser Support
- Modern Browsers: Full support for Chrome 79+, Firefox 72+, Safari 13.1+, Edge 79+
- Custom Elements: Requires native support or polyfill
- ES Modules: Native module support required
- CSS Grid/Flexbox: Used for layout
- Progressive Enhancement: Graceful degradation for older browsers
Performance Considerations
- Lazy Loading: Images use
loading="lazy"by default - Efficient Rendering: Minimal DOM manipulation after initial render
- Bundle Size: Import only needed card types
- Memory: Automatic cleanup on element disconnection
- Responsive Images: Automatic srcset generation reduces bandwidth
Accessibility Features
- Semantic HTML: Proper use of
article,figure,address, etc. - ARIA Attributes: Comprehensive labeling for screen readers
- Keyboard Navigation: Full keyboard accessibility
- Schema.org: Rich structured data for assistive technologies
- Color Contrast: Follows WCAG guidelines
- Focus Management: Proper focus indicators and order
Backend Implementation
For complete Umbraco CMS implementation details including document types, compositions, property editors, and deployment strategies, see the dedicated Umbraco Implementation Guide.
