Skip to content

Extension and Theme Development

This guide is the cold-start path for a third-party Capell package: scaffold it, load it in a development app, prove it renders, and prepare a Marketplace submission. The generated package README.md is the source of truth for that package’s own commands and dependencies; keep it with the package and update it when you add a public surface.

Use the colon commands for new work. The older hyphenated commands remain available for compatibility, but generated instructions use these names:

Terminal window
php artisan capell:make:extension acme/notice-board \
--profile=full \
--path=packages \
--name="Notice Board"
php artisan capell:make:theme aurora \
--package=acme/aurora-theme \
--path=packages \
--name="Aurora" \
--extends=default

Choose minimal when you only need a package provider and manifest. Choose full when you want the working example widget, settings, typed input and render Data objects, widget assets, and tests. A generated theme includes its provider, manifest, CSS source, and a rendering test.

Do not hand-copy a first-party package directory. The generators use only the public package contracts and avoid Capell’s internal development overlay.

2. Read the generated package before editing it

Section titled “2. Read the generated package before editing it”

capell.json is the package’s runtime contract. Keep its package name, version, providers, contributions, assets, and Marketplace metadata consistent with composer.json. A full extension has an example content widget blueprint; use it as the starting point for a real widget rather than placing rendering queries in a Blade view.

For themes, the themeKey value in capell.json is also the CSS registration key. The provider must register the package CSS with the matching conditional Tailwind import:

condition: 'theme-css:aurora',

This is intentionally not a requirement for a literal theme-registration.css file in every theme. Capell collects the registered Tailwind sources and produces the applicable bundle. A missing CSS source or a different key silently leaves the theme unstyled, so treat the condition as part of the theme’s public contract.

If you introduce a new design token, register its utility name in every frontend bundle that needs it, alongside the token source. Adding only a token value does not make a Tailwind utility available. This is per bundle: a utility registered for an admin bundle is not automatically available to a public theme bundle.

Use a normal Composer path repository in the consuming application’s composer.json while developing locally:

{
"repositories": [
{
"type": "path",
"url": "packages/notice-board",
"options": { "symlink": true }
}
]
}

Then install it by Composer name:

Terminal window
composer require acme/notice-board:@dev
php artisan capell:package-cache:clear
php artisan config:clear

The package manifest is cached. Run both cache-clearing commands whenever you change capell.json, especially after renaming a contribution or settings class. Otherwise a warm app can keep using a deleted class or omit a newly added widget even though the package files are correct.

For a published package, replace @dev with a tagged normal Composer constraint such as ^1.0.

Run both commands from the consuming app. They share the extension-audit contract: any result with severity=error returns a failing exit code.

Terminal window
php artisan capell:package:lint packages/notice-board
php artisan capell:extension-audit packages/notice-board

The lint checks the manifest, naming and version sanity, referenced assets, and theme CSS registration. The audit additionally verifies the declared extension contracts. Fix errors rather than suppressing them; scaffold output should be clean from its first run.

Useful failures and their fixes:

FailureWhat to check
A theme reports no theme-css:<key> registrationUse the exact theme key from capell.json in the provider’s conditional Tailwind CSS registration.
A theme CSS source is missingRestore or configure the source file that the provider registers, then regenerate the frontend assets.
A contribution or settings class cannot be found after a renameRun capell:package-cache:clear and config:clear; confirm the manifest class name and Composer PSR-4 namespace.
A screenshot or Marketplace asset is missingKeep the referenced path package-relative and commit the file before publishing; consumer audits also accept paths explicitly marked export-ignore.

Run the generated package tests as you extend the blueprint. The README names the smallest relevant command for that package.

For a full extension, open Admin → Pages, edit a development page, and find its Layout Builder field. In the main area choose Add widget, select <package display name> example, enter Dogfood heading for Heading and Rendered by the generated widget. for Body, then save. Preview or open that page publicly and confirm both strings render and the package’s widget.css/widget.js resources load. The generated widget key is the Composer package name with / changed to ., for example acme.notice-board.

Replace the example data with an Action or Data object that supplies only public, cache-safe render state.

For a theme, select the theme in the development app, request a public page, and verify that its CSS is in the generated frontend output. After changing a theme CSS source, its Tailwind registration, or token utility names, regenerate the app’s frontend Tailwind assets, then clear the package and configuration caches before checking the public page again:

Terminal window
php artisan capell:frontend-tailwind-assets
php artisan capell:package-cache:clear
php artisan config:clear

A successful provider boot is not proof that the conditional CSS made it into the page.

Keep two versions distinct:

  • manifest-version describes the capell.json schema (currently manifest v3).
  • version is your package release version and should match the published Composer release.

Neither field sets Marketplace maturity. Third-party authors submit through the Marketplace submission flow with their repository, package metadata, support, privacy, security, data, and third-party disclosures. Reviewers assign the Marketplace state. An approved community submission starts in Labs; Marketplace Beta acknowledgement is a purchaser-install-flow requirement, not an author-controlled manifest field. The public label for stable is Released.

Do not create or copy Capell’s first-party release ledgers (docs/release-catalogue.json or config/release-packages.json) into a third-party package. They are Capell-owned operational records, not a package publishing interface.

Before submitting, tag the Composer release, run the package’s tests and both validation commands, include accurate Marketplace metadata and package-relative assets, and confirm the package installs through a normal Composer repository.