Creating Editable Areas¶
Summary¶
A practical guide for developers to define editable regions in templates and stand-alone articles, using the code editor (Monaco) with CKEditor-based Visual Editor.
How CKEditor Fits Into a Page¶
Before writing any markup, it helps to understand one fundamental constraint: CKEditor 5 edits HTML fragments, not full pages.
CKEditor is designed to own clearly bounded content regions — article bodies, text sections, structured rich text. It is not designed to control <html>, <head>, navigation, global chrome, or layout containers. Those parts of the page must exist outside the editor's reach.
In SkyCMS this means:
- Editable regions are specific elements you explicitly mark in your layout or template HTML.
- Everything outside those marked elements — the layout shell, header, footer, scripts — is never touched by the editor.
- Editors interact only with the regions you define.
This boundary is enforced by the data-ccms-ceid attribute. Any element carrying that attribute becomes an editable region. Any element without it is invisible to CKEditor.
Quick Start (5 minutes)¶
- Mark editable regions with
data-ccms-ceid="my-id"(ordata-ccms-new="true"for auto-GUID on first save). - Use block elements for rich content (e.g.,
div,section,article) and addclass="ck-content"so CKEditor styles apply. - Use headings (
h1–h6) ordata-editor-config="title"/"heading"for minimal toolbar (no embeds/tables). - Use
data-editor-config="simple","standard", or"advanced"to explicitly control toolbar richness; omit the attribute for thestandarddefault. - Use the image widget via
data-editor-config="image-widget"; first image is typically treated as the page banner. - Do not nest editable regions; backend rejects nested regions.
Full page template skeleton¶
The following is the recommended starting structure when building a new layout or template. Non-editable zones carry contenteditable="false" so that browsers and screen readers cannot accidentally place focus inside them. Editable regions are isolated inside <main>.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>{{PageTitle}}</title>
<link rel="stylesheet" href="/css/site.css" />
<link rel="stylesheet" href="/css/content.css" />
</head>
<body>
<header contenteditable="false">
<!-- Site navigation and global branding — never editor-managed -->
</header>
<main>
<!-- Title region: minimal toolbar, heading-only -->
<h1 data-ccms-ceid="page-title" data-editor-config="title">Page Title</h1>
<!-- Body region: standard rich content -->
<section data-ccms-ceid="body" class="ck-content">
<p>Page body…</p>
</section>
</main>
<footer contenteditable="false">
<!-- Site footer — never editor-managed -->
</footer>
</body>
</html>
Key points:
contenteditable="false"on<header>and<footer>prevents the browser from making those zones accidentally focusable or editable.- Only elements with
data-ccms-ceidare activated as editable regions. - There is no limit on how many editable regions a template can have, as long as none are nested inside each other.
Element Types & Toolbar Behavior¶
| Use Case | HTML | Config/Classes | Toolbar |
|---|---|---|---|
| Title/subtitle | <h1>–<h6> or any block with data-editor-config="title"/"heading" |
data-ccms-ceid |
Minimal toolbar (bold/italic only) |
| Simple text | any block | data-ccms-ceid + data-editor-config="simple" + class="ck-content" |
Compact toolbar (basic formatting + links) |
| Standard content | any block | data-ccms-ceid + class="ck-content" |
Standard toolbar — default when data-editor-config is omitted |
| Advanced content | any block | data-ccms-ceid + data-editor-config="advanced" + class="ck-content" |
Full toolbar (images, media, tables, lists, code blocks, etc.) |
| Image widget | <div data-editor-config="image-widget"> |
data-ccms-ceid or data-ccms-new + class="ck-content" |
FilePond-based upload UI |
Notes:
- ck-content keeps rendered content aligned with CKEditor’s CSS; avoid overriding its base rules.
- Additional block tags are allowed; prefer semantic blocks for layout.
Required Data Attributes¶
data-ccms-ceid: Stable ID for the region. Use descriptive IDs when practical.data-ccms-new="true": Auto-assigns a GUID on first save (shared generator across widgets/editors).data-editor-config(optional):titleorheadingfor minimal toolbar;simple,standard, oradvancedto explicitly select toolbar richness;image-widgetfor image uploads. Omit for standard rich content (defaults tostandard).- Optional for image widget:
data-ccms-enable-alt-editor="true"to enable the alt/title modal per widget.
Examples¶
Rich body block:
<section data-ccms-ceid="body" class="ck-content">
<p>Welcome to our product page.</p>
</section>
Title/subtitle:
<h1 data-ccms-ceid="page-title" data-editor-config="title">Product Name</h1>
<h2 data-ccms-ceid="page-subtitle" data-editor-config="heading">Great tagline goes here</h2>
Image widget (auto-ID on first save):
<div data-editor-config="image-widget" data-ccms-new="true" class="ck-content"></div>
Image widget with opt-in alt/title editor:
<div data-editor-config="image-widget"
data-ccms-new="true"
data-ccms-enable-alt-editor="true"
class="ck-content"></div>
Rules & Conventions¶
- No nesting: Do not place an editable element inside another; backend validation will reject it and nested regions may be removed.
- Stable IDs: Keep
data-ccms-ceidstable between template updates so content maps correctly. - Banner image convention: The first image uploaded on a page is treated as the banner image by convention.
- Iframe context: Visual Editor runs inside an iframe; parent functions handle saving and autosave.
- Autosave: CKEditor autosaves after ~1s idle when
parent.enableAutoSaveis true.
Styling Guidance¶
- Apply
ck-contentto non-title editable areas so CKEditor’s content CSS (ckeditor5-content.css) styles lists, tables, images, etc. - Avoid overriding
.ck-contentbase rules; layer your component/page classes instead. - Headings with minimal toolbar do not require
ck-content, but can have it if you need consistent typography.
Image Widget Highlights¶
- Uses FilePond uploads; supports drag/drop, replace, library browsing, delete.
- Upload path:
/pub/articles/{articleNumber}/whenarticleNumberis available; otherwise/pub/images/. - Alt/Title modal is enabled per widget via
data-ccms-enable-alt-editor="true"; global toggle viaCCMS_IMAGE_WIDGET_CONFIG.enableAltTextEditor. - Emits custom events via
window.CCMSImageWidgetEvents(imageChangedwith{ type, id, element, imageSrc }).
Duplicator Pattern (Reusable Blocks)¶
Use the duplicator to clone repeatable sections (e.g., cards, FAQs, feature rows):
- Wrap repeatable items with classes/components the duplicator expects (see
Editor/wwwroot/lib/cosmos/dublicator/dublicator.js). - Each cloned child should contain editable elements with
data-ccms-ceid(ordata-ccms-new). - On clone, editors/widgets are re-initialized; on delete, editors/widgets are torn down to save the page body cleanly.
- New clones automatically get fresh
data-ccms-ceidvalues to avoid ID collisions; avoid nesting editable regions inside each other.
Minimal pattern example:
<div class="ccms-clone" data-ccms-ceid="feature-1">
<h3 data-ccms-ceid="feature-1-title" data-editor-config="heading">Fast</h3>
<div data-ccms-ceid="feature-1-body" class="ck-content">Our platform is fast.</div>
</div>
Duplicator.create(element) on each clone container in script.
What Gets Stored¶
SkyCMS stores only the inner HTML of each editable region — not the surrounding element, not class="ck-content", not any wrapper markup.
For example, if the template contains:
<section data-ccms-ceid="body" class="ck-content">
<p>Welcome.</p>
</section>
What is saved to the database for the body region is:
<p>Welcome.</p>
At render time, SkyCMS places that saved fragment back inside the .ck-content element. This means:
- The wrapping element and its attributes come from the template or layout, not from stored content.
- Changing the wrapper element (for example from
<section>to<div>) in a template update has no effect on the saved content — it just changes the container. - Never store or pass the outer
.ck-contentelement as the content payload; only the inner HTML belongs in storage.
Troubleshooting¶
- Editable regions missing in Visual Editor: Check for
data-ccms-ceid(ordata-ccms-new) and ensure no nesting. - Content lost after template update: IDs changed or regions removed; keep IDs stable.
- Toolbar too minimal: Remove
data-editor-config="title"/"heading"or switch to a non-heading block for rich content. - Image widget not initializing: Ensure
data-editor-config="image-widget"and that FilePond scripts are loaded. - Styling looks off: Confirm
class="ck-content"on rich areas and avoid overriding CKEditor base styles.
See Also¶
- Templates guide: ../../for-site-builders/templates.md
- Visual Editor reference: ../../for-editors/visual-editor.md
- Visual Editor technical reference: ../../for-editors/visual-editor-technical-reference.md
- Duplicator code: SkyCMS source on GitHub
- Image widget code: SkyCMS source on GitHub
- CKEditor content CSS: SkyCMS source on GitHub