What it does

scriptor-markdown-containers adds fenced-container syntax to page Markdown. Write :::note, :::warning, or :::details "Summary" in a page's content and the plugin renders each as a styled block with a theme-neutral CSS class. Containers nest, and ::: lines inside a fenced code block are left alone so you can document the syntax itself.

It is for sites that want callouts, admonitions, and collapsible sections in editor-authored content without HTML in the page body. It works on any content that flows through the frontend ContentRendering event, so it composes with both database pages and scriptor-markdown-pages.

The plugin is stateless: it subscribes to one frontend event and owns no database schema, so there is no bin/scriptor plugin:install step and composer remove is the complete uninstall.

Install

Host install. Not on Packagist, so register the VCS repo, then require:

composer config repositories.scriptor-markdown-containers \
  vcs https://github.com/bigin/scriptor-markdown-containers
composer require bigins/scriptor-markdown-containers:^0.1

Docker. Bake it in with the two build args:

services:
  scriptor:
    build:
      args:
        SCRIPTOR_PLUGIN_REPOS: "https://github.com/bigin/scriptor-markdown-containers"
        SCRIPTOR_PLUGINS:      "bigins/scriptor-markdown-containers:^0.1"

Discovery is automatic via installed.json; no theme code changes are needed. To get the default look, copy assets/containers.css from the package into your theme (or map the classes onto your own framework, see Configure).

Configure

All keys are optional, under plugins.markdown_containers in data/settings/custom.scriptor-config.php:

return [
    'plugins' => [
        'markdown_containers' => [
            // BEM base for derived classes (default: 'md-container').
            'prefix' => 'md-container',

            // Replace a type's full class string, e.g. map onto UIkit.
            'classes' => [
                'note'    => 'uk-alert',
                'warning' => 'uk-alert uk-alert-warning',
            ],

            // Add your own types, or disable a built-in with `false`.
            'types' => [
                'aside' => ['tag' => 'div'],
                'faq'   => ['tag' => 'details'],
                'tip'   => false,
            ],
        ],
    ],
];
  • prefix changes the BEM base: with cb, :::warning becomes cb cb--warning and titles use cb__title.
  • classes replaces the entire class string for a type, to map containers onto an existing framework instead of the bundled CSS.
  • types adds custom types (['tag' => 'div'|'details']) or disables a built-in ('name' => false); a disabled type's :::name falls back to literal text.

The built-in types are note, info, tip, warning, danger (rendered as div) and details (rendered as <details>).

Use

A container opens on a line of exactly :::name or :::name "Title" and closes on a line of exactly ::::

:::warning "Heads up"
This ships to production on Friday.

:::note
You can nest containers.
:::
:::

renders to:

<div class="md-container md-container--warning">
<p class="md-container__title">Heads up</p>
<p>This ships to production on Friday.</p>
<div class="md-container md-container--note">
<p>You can nest containers.</p>
</div>
</div>

An unknown name is left as literal text, so a stray :::foo never silently disappears. Container bodies are rendered through Scriptor's Sanitizer::markdown() (safe mode), the same pipeline as normal page content, and every class string is filtered to [A-Za-z0-9 _-], so a config value can never break out of the class attribute.