
Content Driven Development
- 1.1k installs
- 158 repo stars
- Updated August 4, 2026
- adobe/skills
content-driven-development is an Adobe AEM Edge Delivery Services skill that orchestrates content-first block development so developers shape components, blocks, and authoring interfaces from real content and author need
About
content-driven-development is an Adobe AEM Edge Delivery Services orchestration skill (release v2.0.1) that makes content structure the starting point before any block or component code ships. Bundled inside the aem-edge-delivery-services plugin alongside skills like analyze-and-plan, building-blocks, testing-blocks, and content-modeling, it enforces a sequence: discover or create test content, model author-friendly structures, implement blocks, then validate in the browser. Adobe documentation requires invoking content-driven-development for all EDS development involving blocks, core scripts, or site functionality to avoid wasted cycles. Reach for it when adding or modifying AEM.live blocks on an existing site—not for greenfield non-AEM stacks. The skill delegates to building-blocks for JavaScript decoration and CSS conventions and to testing-blocks before pull requests, keeping author experience in the document editor aligned with delivered markup. Install via aem-edge-delivery-services@adobe-skills in Claude Code or Codex plugin marketplaces.
- Prioritizes author needs as the primary users of content structures
- Aligns content models, block structures, and authoring patterns directly to content reality
- Integrates DA auth into the CDD workflow
- Follows current best practices for EDS skill intros
- Produces content-first specifications that reduce downstream rework
Content Driven Development by the numbers
- 1,078 all-time installs (skills.sh)
- +63 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #243 of 1,879 Documentation skills by installs in the Skillselion catalog
- Security screen: HIGH risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/adobe/skills --skill content-driven-developmentAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1.1k |
|---|---|
| repo stars | ★ 158 |
| Security audit | 2 / 3 scanners passed |
| Last updated | August 4, 2026 |
| Repository | adobe/skills ↗ |
How do you build AEM EDS blocks content-first?
Ensure every component, block, and authoring interface is shaped by real content and author needs instead of abstract code-first assumptions.
Who is it for?
Developers building or modifying Adobe AEM Edge Delivery Services blocks who must align code with author workflows and real document content.
Skip if: Teams outside AEM.live or Edge Delivery Services who do not need block-based content authoring pipelines.
When should I use this skill?
The user starts AEM EDS block development, asks for content-driven development on AEM.live, or needs to model test content before writing block JavaScript.
What you get
Author-friendly content models, test content, implemented EDS blocks, and browser-validated components ready for pull request.
- Content models and test content
- Implemented EDS blocks
- Browser-validated components
By the numbers
- Release version 2.0.1 documented in adobe/skills changelog
- Orchestrates at least three sibling skills: analyze-and-plan, building-blocks, testing-blocks
Files
Content Driven Development (CDD)
Orchestrate the Content Driven Development workflow for AEM Edge Delivery Services. This workflow ensures code is built against real content with author-friendly content models.
CRITICAL: Never start writing or modifying code without first identifying or creating the content you will use to test your changes.
When to Use This Skill
Use CDD for ALL AEM development tasks:
- ✅ Creating new blocks
- ✅ Modifying existing blocks (structural or functional changes)
- ✅ Changes to core decoration functionality
- ✅ Bug fixes that require validation
- ✅ Any code that affects how authors create or structure content
Do NOT use for:
- Documentation-only changes
- Configuration changes that don't affect authoring
- Research tasks that don't require making any code changes yet
Philosophy
Content Driven Development prioritizes creating or identifying test content before writing code. This ensures:
- Code is built against real content
- Author-friendly content models
- Validation throughout development
Optional: Understanding CDD Principles
Read references/cdd-philosophy.md if:
- User asks "why" questions about content-first approach
- You need to understand reasoning behind CDD decisions
- You're unsure whether to prioritize author vs developer experience
Otherwise: Follow the workflow steps below
Step 0: Create TodoList
FIRST STEP: Use the TodoWrite tool to create a todo list with the following 8 tasks:
1. Start dev server (if not running)
- Success: Dev server running, can access http://localhost:3000
2. Analyze & plan
- Success: Clear understanding documented + acceptance criteria defined
3. Design content model
- Success: Content structure documented and validated
4. Identify/create test content
- Success: Test content accessible covering all scenarios
5. Implement
- Success: Functionality works across all viewports
6. Lint & test
- Success: All checks pass
7. Final validation
- Success: All acceptance criteria met, everything works
8. Ship it
- Success: PR created with preview link for validation
Mark todo complete when: Todo list created with all 8 tasks
---
Step 1: Start Dev Server
Check if dev server is running:
curl -s -o /dev/null -w "%{http_code}" http://localhost:3000Expected: 200 (server running) or connection error (server not running)
If not running, start it:
aem up --no-open --forward-browser-logsNotes:
- Run in background if possible (dev server needs to stay running)
- Requires AEM CLI installed globally:
npm install -g @adobe/aem-cli - Alternative:
npx -y @adobe/aem-cli up --no-open --forward-browser-logs
IMPORTANT: Check the command output for errors. Common issues:
- Port 3000 already in use
- AEM CLI not installed
- Configuration errors
After starting, verify it's running:
curl -s -o /dev/null -w "%{http_code}" http://localhost:3000Expected: 200
Success criteria:
- ✅ Dev server running
- ✅ http://localhost:3000 returns 200
- ✅ No errors in server startup output
Mark todo complete when: Dev server confirmed running and accessible
---
Step 2: Analyze & Plan
Invoke: analyze-and-plan skill
Provide:
- Task description from user
- Screenshots, design files, or existing URLs to match design from (if available)
The analyze-and-plan skill will:
- Guide you through task-specific analysis
- Help define acceptance criteria
- Optionally analyze visual designs/mockups if provided
- Create documented analysis for reference
Success criteria:
- ✅ Requirements analyzed
- ✅ Acceptance criteria defined
- ✅ Analysis documented to file for later steps
Mark todo complete when: Analysis documented and acceptance criteria defined
---
Step 3: Design Content Model
Skip if: CSS-only changes that don't affect content structure
Invoke: content-modeling skill
Provide:
- Analysis from Step 2 (content requirements, author inputs)
- Block name and purpose
The content-modeling skill will:
- Design table structure (rows, columns, semantic formatting)
- Validate against best practices (4 cells/row, semantic formatting)
- Document content model for authors
Success criteria:
- ✅ Content model designed (table structure defined)
- ✅ Validated against best practices
- ✅ Content model documented
Mark todo complete when: Content model designed and documented
---
Step 4: Identify/Create Test Content
Goal: End this step with accessible test content URL(s) covering all test scenarios
Choose the best path based on your situation:
---
Option A: User Provided Test URL(s)
When to use: User already has content and provided URL(s)
What to do: 1. Validate URL loads: curl -s -o /dev/null -w "%{http_code}" http://localhost:3000/path 2. Expected: 200 status 3. Document URL(s) 4. Mark complete
---
Option B: New Block (No Existing Content)
When to use: Building a brand new block that doesn't exist yet
What to do: 1. Skip search (nothing exists yet to find) 2. Create test content using one of these approaches:
Approach 1: CMS Content (Recommended) 1. Ask user to create content in their CMS (Google Drive/SharePoint/DA/Universal Editor) 2. Provide content model from Step 3 as reference 3. Wait for user to provide URL(s) 4. Validate: curl -s -o /dev/null -w "%{http_code}" http://localhost:3000/path 5. Expected: 200 status
Using DA: If you need to push HTML content programmatically to DA (rather than asking the user to author it), invoke the da-auth skill first to obtain a validDA_TOKEN, then use the DA Admin API (POST https://admin.da.live/source/{org}/{repo}/{path}) to push the content and trigger a preview.
Approach 2: Local HTML (Temporary) 1. Create HTML file in drafts/tmp/{block-name}.plain.html 2. Follow structure from Step 3 content model 3. Read references/html-structure.md for local HTML file format guidance 4. Restart dev server: aem up --html-folder drafts --no-open --forward-browser-logs 5. Validate: curl -s -o /dev/null -w "%{http_code}" http://localhost:3000/drafts/tmp/{block-name} 6. Expected: 200 status 7. Note: User must create CMS content before PR (required for preview link)
---
Option C: Existing Block
When to use: Modifying, fixing, or styling an existing block
What to do:
First: Search for existing content 1. Invoke find-test-content skill 2. Provide: block name, dev server URL (optional, defaults to localhost:3000)
What find-test-content will do:
- Search for existing content pages containing the block
- Automatically detect and report all variants found
- Report: URLs with instance counts and variant info
Then: Assess search results
If sufficient content found: 1. Document URL(s) 2. Validate URLs load: curl -s -o /dev/null -w "%{http_code}" http://localhost:3000/path 3. Expected: 200 status 4. Mark complete
If no content found OR insufficient coverage: 1. Create additional test content using approaches from Option B 2. Validate URLs load 3. Mark complete
---
Success criteria:
- ✅ Test content accessible at known URL(s)
- ✅ Content covers all test scenarios (variants, edge cases)
- ✅ URLs validated (return 200)
Mark todo complete when: Test content identified/created and validated
---
Step 5: Implement
Invoke: building-blocks skill
Provide:
- Content model from Step 3 (if applicable)
- Test content URL(s) from Step 4
- Analysis/requirements from Step 2
- Type of changes: new block, existing block modification, CSS-only, etc.
The building-blocks skill will:
- Guide implementation approach based on change type
- Handle JavaScript decoration (if needed)
- Handle CSS styling (mobile-first, responsive)
- Ensure iterative testing in browser throughout development
Success criteria:
- ✅ Code implementation complete
- ✅ Functionality works across all viewports (mobile, tablet, desktop)
- ✅ No console errors
Mark todo complete when: building-blocks skill reports implementation complete and working across viewports
---
Step 6: Lint & Test
What to do:
npm run lintIf lint errors: 1. Fix issues (use npm run lint:fix for auto-fixable problems) 2. Re-run lint until clean
Run existing tests:
npm testNote: Unit tests are optional and only needed for logic-heavy utilities. The testing-blocks skill (invoked by building-blocks in Step 5) handles browser testing. This step catches any remaining lint issues and runs the project's test suite.
Success criteria:
- ✅
npm run lintpasses with no errors - ✅
npm testpasses (if tests exist)
Mark todo complete when: All lint and test checks pass
---
Step 7: Final Validation
What to do:
1. Review acceptance criteria from Step 2
- Read the analysis document created in Step 2
- Check each acceptance criterion is met
2. Final browser sanity check
- Load test content URL(s) in browser
- Check mobile, tablet, and desktop viewports
- Verify no console errors
- Confirm no visual regressions
3. Verify no regressions
- If modifying existing block: test existing variants still work
- If modifying core functionality: spot-check a few pages
Success criteria:
- ✅ All acceptance criteria from Step 2 met
- ✅ Works across all viewports
- ✅ No console errors
- ✅ No regressions on existing functionality
Mark todo complete when: All acceptance criteria verified and no regressions found
---
Step 8: Ship It
What to do:
1. Create feature branch (if not already on one):
git checkout -b block-name2. Stage specific files only:
git add blocks/{block-name}/{block-name}.js blocks/{block-name}/{block-name}.css
# Add only files you worked on - NEVER use `git add .`3. Commit with conventional commit format:
git commit -m "feat(block-name): add new block"Include relevant details in commit message and agent attribution in footer (agent adds Co-authored-by: cursor <noreply@cursor.com>)
4. Push to feature branch:
git push origin HEAD5. Create PR with preview link:
- Branch preview URL format:
https://{branch}--{repo}--{owner}.aem.page/{path} - Example:
https://carousel--aem-skills-demo--shsteimer.aem.page/ - REQUIRED: Include preview link in PR description (used for automated PSI checks)
- Add multiple preview links if needed (e.g., different variants, edge cases)
Determining if you need a draft PR:
Create a draft PR when:
- ✅ Only local test content exists for NEW functionality/variants
- ✅ Test content demonstrates new features not yet in CMS
- ✅ You need user to create CMS content before final validation
Create a regular PR when:
- ✅ All test content exists in CMS and is previewable
- ✅ Changes only affect existing content (regressions can be tested with existing CMS content)
Workflow for draft PRs: 1. Create the PR as a draft using gh pr create --draft 2. Include existing content preview links (for regression testing if applicable) 3. Include next steps in PR description (see template below):
- Describe the test content used locally and what scenarios it covered
- Suggest that same/similar content be created and previewed, and links added to PR
- Keep steps brief but actionable for any reviewer
4. Instruct the user to create CMS content following the steps:
- Open local test content in browser:
http://localhost:3000/drafts/tmp/[test-file] - Right-click AEM Sidekick extension
- Click "View document source" option
- Use the copy button to copy the document content
- Paste into Word/Google Docs/Document Authoring (for UE: use as guide, copy/paste won't work directly)
- Preview the CMS content
5. User adds preview URL(s) to PR description and marks PR ready for review (or agent does with user's input)
PR Description Template:
Use this template for all PRs, including all relevant preview links and adapting as needed:
## Description
Brief description of changes
[If an issue exists]
Fix #<gh-issue-id>
Test URLs:
[Repeat for all relevant test urls]
- Before: https://main--{repo}--{owner}.aem.page/{path}
- After: https://{branch}--{repo}--{owner}.aem.page/{path}
[If only local test content (draft PR):]
This PR is currently a **draft** pending creation of CMS test content.
### Next Steps to Complete PR:
[add relevant steps here]Success criteria:
- ✅ Changes committed with proper message format and attribution
- ✅ Pushed to feature branch (not main)
- ✅ PR created with preview link in description
Mark todo complete when: PR created and ready for review
---
Related Skills
- analyze-and-plan: Invoked in Step 2 for requirements analysis and acceptance criteria
- content-modeling: Invoked in Step 3 for designing content models
- da-auth: Obtain a valid Adobe IMS token before pushing content to DA or triggering DA previews programmatically — invoke at the start of Step 4 when using DA
- find-test-content: Invoked in Step 4, Option C for finding existing content
- building-blocks: Invoked in Step 5 for implementation
- testing-blocks: Invoked by building-blocks for browser testing
- block-collection-and-party: Used to find similar blocks and reference implementations
Anti-Patterns to Avoid
Common mistakes that violate CDD principles:
- ❌ Starting with code before understanding the content model
- ❌ Making assumptions about content structure without seeing real examples
- ❌ Creating developer-friendly but author-hostile content models
- ❌ Skipping content creation "to save time" (costs more time later)
Resources
- Philosophy: references/cdd-philosophy.md - Why content-first matters
- HTML Structure: references/html-structure.md - Guide for creating local HTML test files
{"extends": "../../../../../release.config.cjs"}
2.0.1 (2026-05-29)
Bug Fixes
- Update EDS skill intros to follow current best practices (c728efe)
2.0.0 (2026-05-14)
Bug Fixes
- aem-eds: align references/ with spec and silence orphan warnings (b48cf80)
1.1.0 (2026-04-27)
Features
- da-auth: add da-auth skill and surface DA auth in CDD workflow (#89) (fc7a2e8)
1.0.0 (2026-04-16)
{
"name": "content-driven-development",
"version": "0.0.0-semantically-released",
"private": true
}
Why Content-First Matters
Content Driven Development isn't just a process—it's a philosophy that prioritizes author needs and content reality over developer convenience.
Author Needs Come First
Authors are the primary users of the structures we create. When building for AEM Edge Delivery, the content models, block structures, and authoring patterns we design directly impact how easily authors can create and maintain content.
Content models must be:
- Intuitive: Authors should understand what goes where without extensive training
- Easy to work with: Creating content should feel natural, not like navigating a complex technical system
- Forgiving: Common mistakes should be easy to spot and fix
- Flexible: Authors need room for creativity within structure
This often means more complex decoration code. That's okay. Developer convenience is secondary to author experience.
Efficiency Through Preparation
Creating or identifying test content before coding isn't "extra work"—it's a multiplier that makes everything else faster and better.
Immediate Testing Capability
When you have test content ready before you write code:
- No need to stop development to create test scenarios
- Test as you write, catching issues immediately
- Faster iteration cycles
- More confidence in your implementation
Better PR Workflows
Test content serves double duty:
- Development testing during implementation
- PR validation links for PSI checks (required for all PRs)
- No scrambling to create content when you're ready to merge
Living Documentation
Well-structured test content often becomes:
- Author-facing examples and documentation
- Onboarding material for new team members
- Reference implementations for similar blocks
- Quality standards for content creation
Fewer Assumptions
Code-first development is full of assumptions. You assume:
- How authors will structure content
- What edge cases might appear
- How content will actually be used
- What reasonable defaults should be
Content-first development reveals reality. Real content shows:
- Actual author patterns and preferences
- Edge cases you never imagined
- Use cases that don't fit your assumptions
- Where your elegant design breaks down
The Cost of Skipping CDD
Every shortcut has a price:
Skipping content discovery:
- Build against imagined requirements
- Miss existing patterns and conventions
- Create inconsistent experiences
- Duplicate effort (content exists, you just didn't find it)
Skipping content modeling:
- Create developer-friendly but author-hostile structures
- Require extensive author training
- Generate support burden as authors struggle
- Need redesigns when reality doesn't match assumptions
Skipping test content creation:
- Can't validate during development
- Delay discovering issues until PR review
- Create technical debt (untested code paths)
- Rush content creation at PR time (poor quality examples)
When CDD Feels "Slow"
Sometimes CDD feels slower than jumping straight to code. That's an illusion.
Front-loaded time investment pays off:
- Time spent on content upfront = time saved debugging later
- Time spent on good content models = time saved on author support
- Time spent finding existing content = time saved on redundant work
The "fast" code-first approach: 1. Write code based on assumptions (fast) 2. Discover assumptions were wrong (slow) 3. Rewrite code to match reality (slow) 4. Create test content to validate (slow) 5. Fix bugs revealed by real content (slow) 6. Repeat steps 2-5 until it works (very slow)
The CDD approach: 1. Find or create content based on reality (moderate) 2. Write code against real content (fast) 3. Test continuously with known content (fast) 4. Ship with confidence (fast)
Content as a Contract
The initial content structure is a contract between authors and developers:
- Authors promise to structure content in a specific way
- Developers promise that structure will work correctly
Breaking this contract has consequences:
- Existing pages break
- Authors must rework content
- Trust erodes
- Technical debt accumulates
CDD makes this contract explicit and visible before code is written.
The CDD Mindset
Successful CDD requires shifting perspective:
From: "What can I build?" To: "What do authors need to create?"
From: "How should this work technically?" To: "How will authors actually use this?"
From: "This is the elegant solution." To: "This is the solution authors will understand."
From: "I'll test this later." To: "I'll test this as I build it."
This mindset shift is the foundation of sustainable, author-friendly AEM development.
HTML File Structure for Test Content
When creating local .plain.html files for testing blocks in the drafts/ folder, follow this structure to match how AEM Edge Delivery Services processes authored content.
Important Change: Plain HTML Format
The AEM CLI now automatically wraps HTML content with the headful structure (head, header, footer). When you create .plain.html files, you ONLY need to provide the section content.
What you create:
- ✅ Section divs with content:
<div>...</div>(one per section) - ✅ Blocks as
<div class="block-name">with nested divs - ✅ Default content (headings, paragraphs, links, images)
- ✅ Section metadata blocks when needed
What the AEM CLI adds automatically:
- ❌
<html>,<head>,<body>tags - ❌
<header>and<footer>elements - ❌
<main>wrapper - ❌ Head content (comes from project's head.html)
Plain HTML Structure
<div>
<!-- Section 1: Mixed content - default content and a block -->
<h1>Page Heading</h1>
<p>This is regular paragraph content.</p>
<div class="block-name">
<!-- Block content goes here -->
<div>
<div>Block content cell 1</div>
<div>Block content cell 2</div>
</div>
</div>
<p>More content after the block.</p>
</div>
<div>
<!-- Section 2: Block in its own section -->
<div class="block-name variant-name">
<!-- Block content -->
</div>
</div>
<div>
<!-- Section 3: Multiple blocks in one section -->
<div class="block-one">
<!-- First block content -->
</div>
<div class="block-two">
<!-- Second block content -->
</div>
</div>File Naming Convention
IMPORTANT: HTML files must use the .plain.html extension:
- ✅
drafts/hero-test.plain.html - ✅
drafts/blocks/cards.plain.html - ❌
drafts/hero-test.html(old format, no longer used)
Running with Local HTML
Start dev server with:
aem up --html-folder draftsPreview URLs:
- File:
drafts/hero-test.plain.html→ URL:http://localhost:3000/drafts/hero-test - File:
drafts/blocks/cards.plain.html→ URL:http://localhost:3000/drafts/blocks/cards - Special case:
drafts/blog/index.plain.html→ URL:http://localhost:3000/drafts/blog/(NOT/drafts/)
Section Structure
Content is organized into sections (top-level <div> elements).
Basic Sections
<div>
<!-- Section 1 content -->
</div>
<div>
<!-- Section 2 content -->
</div>Important notes about sections:
- Sections can contain blocks, default content, or a mix of both
- A single section can contain multiple blocks
- There are no strict rules about when to create a new section vs. adding to an existing one
- This varies by project and authoring practices
- Some blocks may require or assume they are in their own section (check block documentation)
Section Metadata
Sections can include metadata to define styling and behavior using a special section-metadata div.
In HTML (using section-metadata div):
<div>
<div class="section-metadata">
<div>
<div>Style</div>
<div>dark</div>
</div>
</div>
<!-- Section content with dark background styling -->
</div>In markdown (Section Metadata table):
+------------------------------+
| Section Metadata |
+------------------+-----------+
| style | dark |
+------------------+-----------+How it works:
- The
section-metadatadiv is placed at the beginning of a section - It uses a nested div structure to represent key-value pairs (like a table)
- The platform processes this metadata and applies it to the parent section
- After processing, the
section-metadatadiv is removed from the DOM - Metadata values become CSS classes or data attributes on the section
Common section styles:
light- White or light backgrounddark- Dark background with light textgrey- Grey or off-white backgroundaccent- Branded color background
Style naming best practices:
- Use consistent names across sections with same visual treatment
- Don't create unique style names for every section
- Example: If 3 sections have white backgrounds, use "light" for all 3
Example with multiple sections:
<!-- Section 1: Light background -->
<div>
<div class="section-metadata">
<div>
<div>Style</div>
<div>light</div>
</div>
</div>
<div class="hero">
<div><div><h1>Welcome</h1></div></div>
</div>
</div>
<!-- Section 2: Dark background -->
<div>
<div class="section-metadata">
<div>
<div>Style</div>
<div>dark</div>
</div>
</div>
<div class="cards">
<div>
<div>Card 1</div>
<div>Card 2</div>
</div>
</div>
</div>
<!-- Section 3: Light background (reuses "light") -->
<div>
<div class="section-metadata">
<div>
<div>Style</div>
<div>light</div>
</div>
</div>
<h2>About Our Company</h2>
<p>This section has default content, not blocks.</p>
</div>For guidance on identifying sections and assigning styles: Use the page-decomposition skill when migrating pages.
Section Content Types
Sections can contain any combination of:
1. Default Content
Regular HTML elements like headings, paragraphs, lists, etc.
<div>
<h1>Main Heading</h1>
<h2>Subheading</h2>
<p>Paragraph with <strong>bold</strong> and <em>italic</em> text.</p>
<ul>
<li>Unordered list item</li>
</ul>
<ol>
<li>Ordered list item</li>
</ol>
</div>Supported default elements:
- Headings:
<h1>,<h2>,<h3>,<h4>,<h5>,<h6> - Paragraphs:
<p> - Lists:
<ul>,<ol>,<li> - Inline formatting:
<strong>,<em>,<a> - Images: Use
<picture>elements with<source>and<img>tags (see Images section below) - Code blocks:
<pre>,<code> - Block quotes:
<blockquote>
2. Blocks
Blocks are <div> elements with specific class names that trigger decoration logic.
Basic block structure:
<div>
<div class="block-name">
<!-- Block content structured based on content model -->
</div>
</div>Block with variant:
<div>
<div class="block-name variant-name">
<!-- Block content -->
</div>
</div>Multiple variants:
<div>
<div class="block-name variant-one variant-two">
<!-- Block content -->
</div>
</div>3. Icons
Icons are authored using the :iconName: syntax and are processed into <span> elements with icon classes.
Author input:
:profile:HTML output:
<span class="icon icon-profile"></span>Important notes about icons:
- Icons can be wrapped in links, strong, em, or other inline elements
- The icon name corresponds to an SVG file in
/icons/(e.g.,:profile:→/icons/profile.svg) - Icon spans use two classes:
icon(base class) andicon-{name}(specific icon) - Icons are inline elements and can be combined with text
Examples:
Icon in a link:
<a href="/profile"><span class="icon icon-profile"></span> View Profile</a>Icon with emphasis:
<strong><span class="icon icon-star"></span> Featured</strong>Icon by itself:
<p><span class="icon icon-home"></span></p>4. Images
Images should always use the <picture> element with <source> elements for responsive images and format optimization.
Basic picture structure:
<picture>
<source type="image/webp" srcset="/media/image.jpg?width=2000&format=webply&optimize=medium" media="(min-width: 600px)">
<source type="image/webp" srcset="/media/image.jpg?width=750&format=webply&optimize=medium">
<source srcset="/media/image.jpg?width=2000&format=jpeg&optimize=medium" media="(min-width: 600px)">
<img loading="lazy" alt="Image description" src="/media/image.jpg?width=750&format=jpeg&optimize=medium">
</picture>Key aspects of picture elements:
- Multiple
<source>elements provide WebP format with fallbacks - Use
mediaattributes for responsive breakpoints (typically(min-width: 600px)for desktop) - Include width query parameters (
?width=750for mobile,?width=2000for desktop) - Add
formatandoptimize=mediumparameters for image optimization - The final
<img>element is the fallback - Always include
altattribute for accessibility - Use
loading="lazy"for images below the fold,loading="eager"for above-the-fold images
Standard responsive breakpoints:
<picture>
<!-- WebP desktop (600px+) -->
<source type="image/webp" srcset="/media/image.jpg?width=2000&format=webply&optimize=medium" media="(min-width: 600px)">
<!-- WebP mobile -->
<source type="image/webp" srcset="/media/image.jpg?width=750&format=webply&optimize=medium">
<!-- JPEG desktop (600px+) -->
<source srcset="/media/image.jpg?width=2000&format=jpeg&optimize=medium" media="(min-width: 600px)">
<!-- JPEG mobile (fallback) -->
<img loading="lazy" alt="Descriptive alt text" src="/media/image.jpg?width=750&format=jpeg&optimize=medium">
</picture>Important notes:
- Place images in the
/media/folder or appropriate project location - Always provide meaningful
alttext for accessibility - Use
loading="eager"only for hero/above-the-fold images - The platform's
createOptimizedPicture()JavaScript helper generates this structure automatically in decoration code
Simplified picture format for examples:
For brevity, examples in this document may show simplified picture tags:
<picture>
<img src="/media/image.jpg" alt="Description">
</picture>In actual test files, you can use either: 1. The full responsive structure shown above (recommended for realistic testing) 2. The simplified format (acceptable for quick prototyping, though less realistic)
Block Content Structure
The internal structure of a block depends on its content model. Blocks typically use nested <div> elements to represent the table-like structure from authoring.
Simple Block Example
A hero block with an image and text:
<div class="hero">
<div>
<div>
<picture>
<img src="/media/hero-image.jpg" alt="Hero image description">
</picture>
</div>
</div>
<div>
<div>
<h1>Hero Heading</h1>
<p>Hero description text</p>
</div>
</div>
</div>Block with Multiple Rows
A cards block with multiple items:
<div class="cards">
<div>
<div>
<picture>
<img src="/media/card1.jpg" alt="Card 1">
</picture>
</div>
<div>
<h3>Card 1 Title</h3>
<p>Card 1 description</p>
</div>
</div>
<div>
<div>
<picture>
<img src="/media/card2.jpg" alt="Card 2">
</picture>
</div>
<div>
<h3>Card 2 Title</h3>
<p>Card 2 description</p>
</div>
</div>
<div>
<div>
<picture>
<img src="/media/card3.jpg" alt="Card 3">
</picture>
</div>
<div>
<h3>Card 3 Title</h3>
<p>Card 3 description</p>
</div>
</div>
</div>Block Structure Mapping
The nested <div> structure in HTML corresponds to the table structure in authoring:
In document authoring (table):
| Block Name |
|-------------------|
| Cell 1 | Cell 2 |
| Cell 3 | Cell 4 |In HTML:
<div class="block-name">
<div> <!-- Row 1 -->
<div>Cell 1</div> <!-- Column 1 -->
<div>Cell 2</div> <!-- Column 2 -->
</div>
<div> <!-- Row 2 -->
<div>Cell 3</div> <!-- Column 1 -->
<div>Cell 4</div> <!-- Column 2 -->
</div>
</div>Complete Example
Here's a complete example of a test .plain.html file for a hero block:
File: drafts/hero-test.plain.html
<!-- Hero block section -->
<div>
<div class="hero">
<div>
<div>
<picture>
<img src="/media/hero-image.jpg" alt="Welcome to our site">
</picture>
</div>
</div>
<div>
<div>
<h1>Welcome to Our Site</h1>
<p>This is a compelling hero message that encourages visitors to take action.</p>
<p><a href="/contact">Get Started</a></p>
</div>
</div>
</div>
</div>
<!-- Regular content section -->
<div>
<h2>About This Test</h2>
<p>This page demonstrates the hero block in action.</p>
</div>
<!-- Hero block with variant -->
<div>
<div class="hero dark">
<div>
<div>
<picture>
<img src="/media/hero-dark.jpg" alt="Dark variant hero">
</picture>
</div>
</div>
<div>
<div>
<h2>Dark Variant Hero</h2>
<p>Testing the dark variant of the hero block.</p>
</div>
</div>
</div>
</div>Preview at: http://localhost:3000/drafts/hero-test
Important Notes
File location:
- Create test HTML files in the
drafts/folder - Can be organized in subfolders:
drafts/blocks/hero/test.plain.html - Always use
.plain.htmlextension
Running with local HTML:
- Start dev server with:
aem up --html-folder drafts - This tells the dev server to serve HTML files from the drafts folder
- The CLI automatically wraps your plain HTML with head, header, and footer
Section organization:
- Sections can contain any mix of blocks and default content
- No strict rules about section boundaries - depends on project and authoring needs
- Some blocks may require being in their own section - check block documentation
Images:
- Use
<picture>elements with proper responsive structure (see Images section) - Reference images from the
/media/folder or appropriate location - Always include
altattributes for accessibility
Testing considerations:
- Test multiple variants in the same file by adding multiple sections
- Include edge cases in your test content
- Use realistic content, not placeholder text
Content model alignment:
- The HTML structure must match your block's expected content model
- Consult your content model documentation when structuring blocks
- The decoration JavaScript will process this structure at two levels:
- Page-level decoration (e.g.,
decorateMaininscripts.js) processes all content - Block-specific decoration (
blocks/{block-name}/{block-name}.js) processes individual blocks - Classes and enhanced markup are typically added during decoration, not in the authored HTML
When to Create CMS Content Instead
Local HTML files are useful for quick iteration, but remember:
- For PRs: You need actual CMS content for PSI validation links
- For documentation: CMS content can serve as author documentation
- For collaboration: CMS content is easier for non-developers to review
Always plan to create CMS content before finalizing your PR, even if you start with local HTML for rapid development.
Related skills
How it compares
Pick content-driven-development over generic frontend planning skills when the target stack is Adobe AEM Edge Delivery Services blocks with document-author workflows.
FAQ
What does content-driven-development orchestrate?
content-driven-development runs the AEM Edge Delivery Services CDD sequence: content discovery and modeling, block implementation via building-blocks, and browser validation via testing-blocks before code review.
When is content-driven-development required?
Adobe AGENTS.md requires content-driven-development for all AEM EDS development involving blocks, core scripts, or functionality, because skipping it wastes cycles on code misaligned with author content.
Which plugin bundles content-driven-development?
content-driven-development ships in the aem-edge-delivery-services plugin from adobe/skills alongside analyze-and-plan, building-blocks, testing-blocks, and content-modeling skills.
Is Content Driven Development safe to install?
skills.sh reports 2 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.