agentsclimarketplace

Theme development

Skill ColdBox/skills/contentbox-cfml/theme-development

Collection of skills for the ColdBox Platform and Claude Plugin

Install
npx -y skills add ColdBox/skills --skill theme-development

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Use this skill when creating or customizing ContentBox themes, including theme structure, metadata/settings, layout and view composition, collection templates, widget overrides, and theme lifecycle callbacks.

SKILL.md

12.1 KB, as published. Nobody here has run it

ContentBox Theme Development (CFML)

Build custom themes for ContentBox CMS using CFML. Themes control the visual presentation of all public-facing content — blog entries, pages, archives, search results, and error pages.

Theme Structure

A ContentBox theme is a directory under modules_app/contentbox-custom/_themes/ (custom) or modules/contentbox/themes/ (core) containing:

MyTheme/
├── Theme.cfc              ← Theme metadata, settings, lifecycle callbacks
├── screenshot.png         ← Theme preview image (shown in admin)
├── layouts/
│   ├── blog.cfm           ← MANDATORY: Blog entry layout
│   └── pages.cfm          ← MANDATORY: Page layout
│   ├── maintenance.cfm    ← Optional: Maintenance mode layout
│   └── search.cfm         ← Optional: Search results layout (defaults to pages)
├── views/
│   ├── index.cfm          ← MANDATORY: Home page (blog entry listing)
│   ├── entry.cfm          ← MANDATORY: Single blog entry with comments
│   ├── page.cfm           ← MANDATORY: Single page rendering
│   ├── archives.cfm       ← MANDATORY: Blog archives view
│   ├── error.cfm          ← MANDATORY: Error display
│   ├── notfound.cfm       ← Optional: Entry not found view
│   └── maintenance.cfm    ← Optional: Maintenance mode view
├── templates/
│   ├── entry.cfm          ← Collection template for entry iterations
│   ├── category.cfm       ← Collection template for category iterations
│   └── comment.cfm        ← Collection template for comment iterations
├── widgets/               ← Theme-specific widget overrides
│   └── MyWidget.cfc       ← Overrides core widgets of the same name
└── includes/              ← Help files, assets, etc.

Theme.cfc

The Theme.cfc defines metadata, settings, and lifecycle callbacks:

component {

	// Theme Metadata
	this.name          = "My Custom Theme";
	this.description   = "A beautiful custom theme for ContentBox";
	this.version       = "1.0.0";
	this.author        = "Your Name";
	this.authorURL     = "https://example.com";
	this.screenShotURL = "screenshot.png";

	// Theme Settings — array of setting structs
	this.settings = [
		{
			name         : "siteTitle",
			defaultValue : "My Site",
			type         : "text",
			label        : "Site Title:",
			required     : true,
			group        : "General"
		},
		{
			name         : "primaryColor",
			defaultValue : "#3b82f6",
			type         : "color",
			label        : "Primary Color:",
			group        : "Colors"
		},
		{
			name         : "showSidebar",
			defaultValue : true,
			type         : "boolean",
			label        : "Show Sidebar:",
			group        : "Layout"
		},
		{
			name         : "layoutStyle",
			defaultValue : "grid",
			type         : "select",
			label        : "Entry Layout:",
			options      : "grid,list,masonry",
			group        : "Layout"
		},
		{
			name         : "footerText",
			defaultValue : "",
			type         : "textarea",
			label        : "Footer Text:",
			group        : "General"
		}
	];

	/**
	 * Called when the theme is activated
	 */
	function onActivation(){
		// Run setup logic, create default content, etc.
	}

	/**
	 * Called when the theme is deactivated
	 */
	function onDeactivation(){
		// Cleanup logic
	}

	/**
	 * Called when the theme is deleted
	 */
	function onDelete(){
		// Cleanup logic
	}

}

Setting Types

TypeDescription
textSingle-line text input (default)
textareaMulti-line text area
booleanCheckbox toggle
selectDropdown select box
colorColor picker

Setting Struct Keys

KeyRequiredDescription
nameYesSetting name (saved as cb_themeName_settingName)
defaultValueYesDefault value
typeNoHTML control type (default: text)
labelNoHTML label (defaults to name)
requiredNoWhether the setting is required (default: false)
titleNoHTML title attribute
optionsNoFor select: comma-separated list or array of values, or array of {name, value} structs
optionsUDFNoUDF name (no parentheses) that returns options, e.g., getColors
groupNoGroup name for organizing settings
groupIntroNoDescription text for a group
fieldDescriptionNoDescription for an individual field
fieldHelpNoHTML for a modal help popup (use loadHelpFile() helper)

Accessing Theme Settings in Views

Theme settings are available via the cb helper:

<!--- Get a theme setting --->
#cb.getThemeSetting( "siteTitle" )#
#cb.getThemeSetting( "primaryColor" )#

<!--- With fallback default --->
#cb.getThemeSetting( "showSidebar", true )#

Layout Files

blog.cfm — Blog Entry Layout

<cfoutput>
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>#cb.getContent().getTitle()# — #cb.getThemeSetting( "siteTitle" )#</title>
	#renderView( view = "_assets/head" )#
</head>
<body>
	#renderView( view = "_assets/header" )#

	<main class="container">
		#renderView()#
	</main>

	#renderView( view = "_assets/footer" )#
</body>
</html>
</cfoutput>

pages.cfm — Page Layout

Similar structure to blog.cfm, used for rendering static pages.

View Files

index.cfm — Home Page

<cfoutput>
	<!--- Render entries using collection template --->
	#cb.renderCollection(
		template   = "entry",
		collection = prc.entries,
		counter    = prc.start,
		totalItems = prc.totalRecords
	)#

	<!--- Pagination --->
	#cb.paginator(
		totalRecords  = prc.totalRecords,
		maxRows       = prc.maxRows,
		page          = prc.page,
		pageLink      = cb.siteURL() & "/page/{page}",
		align         = "center"
	)#
</cfoutput>

entry.cfm — Single Blog Entry

<cfoutput>
	<cfset entry = cb.getContent()>

	<article class="entry">
		<header>
			<h1>#entry.getTitle()#</h1>
			<div class="meta">
				By #entry.getAuthor().getFullName()#
				on #dateFormat( entry.getCreatedDate(), "mmmm d, yyyy" )#
			</div>
		</header>

		<div class="content">
			#entry.getHTMLContent()#
		</div>

		<!--- Categories --->
		<cfif entry.getCategories().recordCount>
			<div class="categories">
				#cb.renderCollection(
					template   = "category",
					collection = entry.getCategories()
				)#
			</div>
		</cfif>

		<!--- Comments --->
		#cb.widget( "CommentForm" )#
	</article>
</cfoutput>

page.cfm — Single Page

<cfoutput>
	<cfset page = cb.getContent()>

	<article class="page">
		<h1>#page.getTitle()#</h1>
		<div class="content">
			#page.getHTMLContent()#
		</div>
	</article>
</cfoutput>

archives.cfm — Archives View

<cfoutput>
	<h1>Archives</h1>

	<!--- Monthly archives --->
	<div class="archives-by-month">
		#cb.renderCollection(
			template   = "entry",
			collection = prc.entries
		)#
	</div>

	<!--- Pagination --->
	#cb.paginator(
		totalRecords = prc.totalRecords,
		maxRows      = prc.maxRows,
		page         = prc.page
	)#
</cfoutput>

error.cfm — Error Display

<cfoutput>
	<div class="error-page">
		<h1>Error</h1>
		<p>#prc.errorMessage ?: "An unexpected error occurred."#</p>
		<a href="#cb.siteURL()#" class="btn">Return Home</a>
	</div>
</cfoutput>

Collection Templates

Templates in templates/ are used with cb.renderCollection(). Each template receives:

  • _counter — Current iteration index (1-based)
  • _items — Total number of items in the collection
  • {templateName} — The object being rendered (e.g., entry, category, comment)

templates/entry.cfm

<cfoutput>
	<article class="entry-preview">
		<h2>
			<a href="#cb.entryURL( entry )#">#entry.getTitle()#</a>
		</h2>
		<div class="meta">
			#dateFormat( entry.getCreatedDate(), "mmmm d, yyyy" )#
			by #entry.getAuthor().getFullName()#
		</div>
		<div class="excerpt">
			#entry.getHTMLContentExcerpt()#
		</div>
	</article>
</cfoutput>

templates/category.cfm

<cfoutput>
	<a href="#cb.categoryURL( category )#" class="category-tag">
		#category.getCategory()#
	</a>
</cfoutput>

templates/comment.cfm

<cfoutput>
	<div class="comment">
		<div class="comment-author">#comment.getAuthor()#</div>
		<div class="comment-date">#dateFormat( comment.getCreatedDate(), "mmmm d, yyyy" )#</div>
		<div class="comment-body">#comment.getComment()#</div>
	</div>
</cfoutput>

Widget Overrides

Place widgets in widgets/ to override core widgets of the same name:

<!--- widgets/Menu.cfc — overrides the core Menu widget --->
component extends="contentbox.models.ui.BaseWidget" singleton {

	function init(){
		setName( "Menu" );
		setVersion( "1.0.0" );
		setDescription( "Custom menu widget override" );
	}

	any function renderIt( string menuName = "main" ){
		// Custom menu rendering
	}

}

The CB Helper

The cb helper (CBHelper@contentbox) is the primary API for theme development:

<!--- Site info --->
#cb.site()#                    <!--- Current site entity --->
#cb.siteURL()#                 <!--- Site base URL --->
#cb.siteName()#                <!--- Site name --->

<!--- Content --->
#cb.getContent()#              <!--- Current content (entry/page) --->
#cb.entryURL( entry )#         <!--- Entry permalink --->
#cb.pageURL( page )#           <!--- Page URL --->
#cb.categoryURL( category )#   <!--- Category URL --->

<!--- Theme settings --->
#cb.getThemeSetting( "name" )#

<!--- Widgets --->
#cb.widget( "WidgetName", { arg1 = "value" } )#

<!--- Rendering --->
#cb.renderCollection( template = "entry", collection = query )#
#cb.renderView( view = "partial" )#

<!--- Menus --->
#cb.menu( "main" )#

<!--- RSS feeds --->
#cb.rssURL()#
#cb.rssCommentsURL()#

<!--- Search --->
#cb.searchURL()#
#cb.searchURL( "query" )#

<!--- Subscriptions --->
#cb.subscribeURL()#
#cb.unsubscribeURL()#

Theme Discovery and Registration

ContentBox discovers themes from two locations:

  1. Core themes: modules/contentbox/themes/
  2. Custom themes: modules_app/contentbox-custom/_themes/

The ThemeService@contentbox builds the theme registry at startup. Custom themes override core themes of the same name.

Theme Switching

Themes can be switched per-site via admin settings. The active theme is resolved at runtime:

<!--- In a handler or service --->
property name="themeService" inject="themeService@contentbox";

var activeTheme = themeService.getActiveTheme();
var themePath   = themeService.getThemePath( activeTheme );

Best Practices

  1. Always include mandatory files: Theme.cfc, blog.cfm, pages.cfm, index.cfm, entry.cfm, page.cfm, archives.cfm, error.cfm
  2. Use cb helper for all URL generation — never hardcode paths
  3. Use collection templates for iterating over entries, categories, comments
  4. Group theme settings logically using the group key
  5. Provide screenshot.png for admin theme preview
  6. Use loadHelpFile() for field help that reads from includes/help/
  7. Keep theme-specific widgets in the theme's widgets/ folder
  8. Test with multiple content types — entries, pages, categories, search results
  9. Use prc scope for handler-passed data in views
  10. Follow CFML compatibility — target Lucee 5+ and Adobe ColdFusion 2018+

Engine Compatibility

This skill targets CFML engines (Lucee 5+, Adobe ColdFusion 2018+). For BoxLang-specific syntax and features, see the BoxLang variant of this skill.

Key CFML considerations:

  • Use <cfoutput> for variable interpolation in CFML templates
  • Use structKeyExists() for safe struct access
  • Use listContains() for list operations
  • Use arrayLen() for array length
  • Use dateFormat() and timeFormat() for date formatting

Gives 0 of the 12 instructions most design systems skills give

Counted across 528 of the 534 authors here whose files we hold, read 2026-08-06

  • create a custom theme if neededin 54 of 528, across 10 files
  • read the corresponding theme filein 54 of 528, across 10 files
  • ask which theme to applyin 53 of 528, across 9 files
  • show the theme showcasein 53 of 528, across 9 files
  • maintain visual identity across all slidesin 50 of 528, across 6 files
  • apply the specified colors and fontsin 47 of 528, across 3 files
  • get explicit confirmationin 45 of 528, across 1 file
  • Generate a design system before codingin 19 of 528, across 6 files
  • Maintain at least 4.5:1 color contrast ratioin 19 of 528, across 8 files
  • Describe component shapes, colors, shadows, and interaction statesin 18 of 528, across 4 files
  • Check Python installation and install if missingin 17 of 528, across 4 files
  • Default to html-tailwind if stack is unspecifiedin 17 of 528, across 4 files

Said here and by no other author read

  • Include mandatory Theme.cfc, layout, and view files
  • Use the cb helper for all URL generation
  • Use collection templates for iterating entries, categories, comments
  • Group theme settings logically using the group key
  • Provide screenshot.png for admin theme preview
  • Use loadHelpFile() for field help reading from includes/help/

Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.