Editing with Python Markdown

Last modified: October 20th, 2023

Working with a specific static site generator?
Customize CloudCannon's documentation to suit your SSG.

Great! We'll show you documentation relevant to .
You can change this any time using the dropdown in the navigation bar.

CloudCannon supports embedding rich Snippets in Markdown content when using the CloudCannon Content Editor. Once configured, Snippets in your content will be presented as blocks in rich text views, with the ability to add them as Snippets via the toolbar:

The CloudCannon Content Editor toolbar with a cursor hovering over the "Snippet" button

To start configuring Snippets in your Markdown content, a Snippet configuration must be imported using the _snippets_imports key in your CloudCannon global configuration file. Your SSG and theme may not enable the full set of Python Markdown extensions so we recommend using the include option to import only the Snippets you're using.

cloudcannon.config.yaml
copied
_snippets_imports:
  python_markdown_extensions:
    include:
      - ...
cloudcannon.config.json
copied
{
  "_snippets_imports": {
    "python_markdown_extensions": {
      "include": [
        "..."
      ]
    }
  }
}
cloudcannon.config.js
copied
module.exports = {
  _snippets_imports: {
    python_markdown_extensions: {
      include: [
        "..."
      ]
    }
  }
};

Enabling snippets in the toolbar#

By default, CloudCannon will show the snippet toolbar action in the content editor if snippets are available.

If you have already customized which options are available via _editables in your CloudCannon config, you will need to include snippet: true for Snippets to be available. See the Editables options documentation for more details.

Supported extensions#

Abbreviations#

The Abbreviation snippet allows you to add standard Python Markdown abbreviations to your content.

example.md
copied
The HTML specification is maintained by the W3C.

*[HTML]: Hyper Text Markup Language
*[W3C]: World Wide Web Consortium

Example Config

cloudcannon.config.yaml
copied
_snippets_imports:
  python_markdown_extensions:
    include:
      - python_markdown_abbreviation
cloudcannon.config.json
copied
{
  "_snippets_imports": {
    "python_markdown_extensions": {
      "include": [
        "python_markdown_abbreviation"
      ]
    }
  }
}
cloudcannon.config.js
copied
module.exports = {
  _snippets_imports: {
    python_markdown_extensions: {
      include: [
        "python_markdown_abbreviation"
      ]
    }
  }
};

Admonitions#

The Admonition snippet allows you to add standard Python Markdown admonitions to your content. In addition, collapsible admonitions from the Details extension are supported with the Collapsible Admonition snippet:

example.md
copied
!!! note

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla et euismod
    nulla. Curabitur feugiat, tortor non consequat finibus, justo purus auctor
    massa, nec semper lorem quam in massa.
example.md
copied
??? note

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla et euismod
    nulla. Curabitur feugiat, tortor non consequat finibus, justo purus auctor
    massa, nec semper lorem quam in massa.

Example Config

cloudcannon.config.yaml
copied
_snippets_imports:
  python_markdown_extensions:
    include:
      - python_markdown_admonition
      - python_markdown_collapsible_admonition
cloudcannon.config.json
copied
{
  "_snippets_imports": {
    "python_markdown_extensions": {
      "include": [
        "python_markdown_admonition",
        "python_markdown_collapsible_admonition"
      ]
    }
  }
}
cloudcannon.config.js
copied
module.exports = {
  _snippets_imports: {
    python_markdown_extensions: {
      include: [
        "python_markdown_admonition",
        "python_markdown_collapsible_admonition"
      ]
    }
  }
};

Code Blocks#

CloudCannon supports both fenced codeblocks with the Code Block snippet, and inline code highlighting from the InlineHilite extension with the Inline Code snippet:

example.md
copied
``` py title="bubble_sort.py"
def bubble_sort(items):
    for i in range(len(items)):
        for j in range(len(items) - 1 - i):
            if items[j] > items[j + 1]:
                items[j], items[j + 1] = items[j + 1], items[j]
```
example.md
copied
The `#!python range()` function is used to generate a sequence of numbers.

Example Config

cloudcannon.config.yaml
copied
_snippets_imports:
  python_markdown_extensions:
    include:
      - python_markdown_code_block
      - python_markdown_inline_code
cloudcannon.config.json
copied
{
  "_snippets_imports": {
    "python_markdown_extensions": {
      "include": [
        "python_markdown_code_block",
        "python_markdown_inline_code"
      ]
    }
  }
}
cloudcannon.config.js
copied
module.exports = {
  _snippets_imports: {
    python_markdown_extensions: {
      include: [
        "python_markdown_code_block",
        "python_markdown_inline_code"
      ]
    }
  }
};

Content Tabs#

The Tabs snippet supports creating tabbed content using the Tabbed extension.

example.md
copied
=== "Unordered list"

    * Sed sagittis eleifend rutrum
    * Donec vitae suscipit est
    * Nulla tempor lobortis orci

=== "Ordered list"

    1. Sed sagittis eleifend rutrum
    2. Donec vitae suscipit est
    3. Nulla tempor lobortis orci

Example Config

cloudcannon.config.yaml
copied
_snippets_imports:
  python_markdown_extensions:
    include:
      - python_markdown_tabs
cloudcannon.config.json
copied
{
  "_snippets_imports": {
    "python_markdown_extensions": {
      "include": [
        "python_markdown_tabs"
      ]
    }
  }
}
cloudcannon.config.js
copied
module.exports = {
  _snippets_imports: {
    python_markdown_extensions: {
      include: [
        "python_markdown_tabs"
      ]
    }
  }
};

Footnotes#

Footnote support consists of two snippets, a footnote snippet and a footnote marker snippet. The footnote snippets lets you define the content of a footnote, and then the marker snippet allows you to reference a footnote in text.

example.md
copied
Lorem ipsum[^1] dolor sit amet, consectetur adipiscing elit.

[^1]: Lorem ipsum dolor sit amet, consectetur adipiscing elit.

Example Config

cloudcannon.config.yaml
copied
_snippets_imports:
  python_markdown_extensions:
    include:
      - python_markdown_footnote
      - python_markdown_footnote_marker
cloudcannon.config.json
copied
{
  "_snippets_imports": {
    "python_markdown_extensions": {
      "include": [
        "python_markdown_footnote",
        "python_markdown_footnote_marker"
      ]
    }
  }
}
cloudcannon.config.js
copied
module.exports = {
  _snippets_imports: {
    python_markdown_extensions: {
      include: [
        "python_markdown_footnote",
        "python_markdown_footnote_marker"
      ]
    }
  }
};

Icons#

The Icon snippet adds support for adding graphics with the Emoji extension.

example.md
copied
:smile: :heart: :thumbsup:

Example Config

cloudcannon.config.yaml
copied
_snippets_imports:
  python_markdown_extensions:
    include:
      - python_markdown_icon
cloudcannon.config.json
copied
{
  "_snippets_imports": {
    "python_markdown_extensions": {
      "include": [
        "python_markdown_icon"
      ]
    }
  }
}
cloudcannon.config.js
copied
module.exports = {
  _snippets_imports: {
    python_markdown_extensions: {
      include: [
        "python_markdown_icon"
      ]
    }
  }
};

Arithmatex#

The Arithmatex snippets allow you to insert LaTeX style math equations which work with the Arithmatex extension.

example.md
copied
$$
\operatorname{ker} f=\{g\in G:f(g)=e_{H}\}{\mbox{.}}
$$

The homomorphism $f$ is injective if and only if its kernel is only the 
singleton set $e_G$, because otherwise $\exists a,b\in G$ with $a\neq b$ such 
that $f(a)=f(b)$.

Example Config

cloudcannon.config.yaml
copied
_snippets_imports:
  python_markdown_extensions:
    include:
      - python_markdown_arithmatex
      - python_markdown_inline_arithmatex
cloudcannon.config.json
copied
{
  "_snippets_imports": {
    "python_markdown_extensions": {
      "include": [
        "python_markdown_arithmatex",
        "python_markdown_inline_arithmatex"
      ]
    }
  }
}
cloudcannon.config.js
copied
module.exports = {
  _snippets_imports: {
    python_markdown_extensions: {
      include: [
        "python_markdown_arithmatex",
        "python_markdown_inline_arithmatex"
      ]
    }
  }
};

The first part of reference-style links support is the reference snippet, which allows you to create a named reference and set its URL.

example.md
copied
[my-reference]: markdownguide.org/basic-syntax/#reference-style-links
[my-image]: example.com/image.png

You can then use that reference in links in your page using the reference link snippet, or the reference template snippet if your link title is the same as the reference name. If you're using reference links you must also include the regular link snippet.

example.md
copied
[my-reference]
[My link text][my-reference]

You can also use references in images with the reference image snippet, or the reference template image snippet if image text is the same as the reference name. If you're using reference images you must also include the regular image snippet.

example.md
copied
![my-image]
![My image text][my-image]
cloudcannon.config.yaml
copied
_snippets_imports:
  python_markdown_extensions:
    include:
      - python_markdown_reference
      - python_markdown_reference_link
      - python_markdown_reference_image
      - python_markdown_link
      - python_markdown_image
      - python_markdown_reference_template_image
      - python_markdown_reference_template
cloudcannon.config.json
copied
{
  "_snippets_imports": {
    "python_markdown_extensions": {
      "include": [
        "python_markdown_reference",
        "python_markdown_reference_link",
        "python_markdown_reference_image",
        "python_markdown_link",
        "python_markdown_image",
        "python_markdown_reference_template_image",
        "python_markdown_reference_template"
      ]
    }
  }
}
cloudcannon.config.js
copied
module.exports = {
  _snippets_imports: {
    python_markdown_extensions: {
      include: [
        "python_markdown_reference",
        "python_markdown_reference_link",
        "python_markdown_reference_image",
        "python_markdown_link",
        "python_markdown_image",
        "python_markdown_reference_template_image",
        "python_markdown_reference_template"
      ]
    }
  }
};

Custom Snippets#

Some Python extensions don't work out of the box and require additional configuration. These are the Snippet extension and the Markdown in HTML extension. By default any instance of these extensions will be escaped into either an HTML element or an Unknown Snippet, this allows an editor to move and delete these Snippets, but prevents editing of their arguments or content:

A screenshot of an unknown Snippet displayed as a block in the CloudCannon Content Editor

To enable editing and provide the custom snippet in the toolbar, your custom snippet must be configured using the _snippets object in your CloudCannon global config file. CloudCannon Snippets can be built from scratch to support nearly any syntax or SSG, but importing a Snippet configuration provides a set of snippet templates for common use cases in Python Markdown.

To help illustrate configuring custom Snippets, we will first cover a few examples. First, let's look a custom glossary snippet that includes a set of abbreviations for our site:

post.md
copied
# My Post

--8<-- "glossary.html"

The first thing we need to do is decide which Snippet template to use. Looking at the list of snippet templates further down this page, we see that Python Markdown has two templates python_markdown_snippet and python_markdown_markdown_in_html. Our glossary is using the Snippet extension so we should configure this snippet using the python_markdown_snippet template. A full example configuration for this Snippet thus might look like the following:

cloudcannon.config.yaml
copied
_snippets_imports:
  python_markdown_extensions: 
_snippets:
  glossary:
    template: python_markdown_snippet
    inline: false
    preview:
      text: Glossary
    definitions:
      snippet_name: glossary.html
cloudcannon.config.json
copied
{
  "_snippets_imports": {
    "python_markdown_extensions": null
  },
  "_snippets": {
    "glossary": {
      "template": "python_markdown_snippet",
      "inline": false,
      "preview": {
        "text": "Glossary"
      },
      "definitions": {
        "snippet_name": "glossary.html"
      }
    }
  }
}
cloudcannon.config.js
copied
module.exports = {
  _snippets_imports: {
    python_markdown_extensions: null
  },
  _snippets: {
    glossary: {
      template: "python_markdown_snippet",
      inline: false,
      preview: {
        text: "Glossary"
      },
      definitions: {
        snippet_name: "glossary.html"
      }
    }
  }
};

Each snippet definition lives under a top level key, glossary in this example. This is the unique name that CloudCannon uses to identify this snippet, but is otherwise unused in the snippet configuration itself.

We specify the template that this snippet should inherit from, and also specify that it is not an inline snippet, so it can only be used on a line on its own.

In the preview object we configure how this snippet is displayed while editing, using the CloudCannon card preview options.

In definitions we need to specify some values that are required for the template we picked. For the python_markdown_snippet template, we need to specify:

  • The snippet_name — in this case we're configuring a snippet for the glossary.html file.

Finally, we can specify any other keys from the CloudCannon configuration cascade here. In this example, we configure the inputs for the keys that this snippet will generate. With that in place, we can now add and edit our glossary snippet anywhere on our site.

A screenshot of the "glossary" snippet in the Content Editor

Next, let's quickly look at a snippet using Markdown in HTML:

post.md
copied
# My Post

<figure markdown class="diagram">
  [My figure](figure.png)
  An example of a figure
</figure>

This time, we have an HTML figure with a class attribute of diagram and we want to be able to edit its contents in Markdown. This syntax matches the python_markdown_markdown_in_html template.

In CloudCannon, we could configure this snippet using the following global configuration:

cloudcannon.config.yaml
copied
_snippets_imports:
  python_markdown_extensions: 
_snippets:
  diagram:
    template: python_markdown_markdown_in_html
    inline: false
    preview:
      text: Diagram
    definitions:
      tag_name: figure
      content_key: content
      tag_attributes:
        - source_key: class
          type: string
          default: diagram
          allowed_values:
            - diagram
cloudcannon.config.json
copied
{
  "_snippets_imports": {
    "python_markdown_extensions": null
  },
  "_snippets": {
    "diagram": {
      "template": "python_markdown_markdown_in_html",
      "inline": false,
      "preview": {
        "text": "Diagram"
      },
      "definitions": {
        "tag_name": "figure",
        "content_key": "content",
        "tag_attributes": [
          {
            "source_key": "class",
            "type": "string",
            "default": "diagram",
            "allowed_values": [
              "diagram"
            ]
          }
        ]
      }
    }
  }
}
cloudcannon.config.js
copied
module.exports = {
  _snippets_imports: {
    python_markdown_extensions: null
  },
  _snippets: {
    diagram: {
      template: "python_markdown_markdown_in_html",
      inline: false,
      preview: {
        text: "Diagram"
      },
      definitions: {
        tag_name: "figure",
        content_key: "content",
        tag_attributes: [
          {
            source_key: "class",
            type: "string",
            default: "diagram",
            allowed_values: [
              "diagram"
            ]
          }
        ]
      }
    }
  }
};

In this example, we want to ensure that our block has a class of diagram but we don't want the class to be editable in CloudCannon. By using source_key with no editor_key we prevent the class from being editable in CloudCannon, then the default and allowed_values settings ensure that all instances of this snippet have class diagram.

A screenshot of the "diagram" Snippet in the Content Editor, with the editing panel open showing the snippet data

Snippet options#

The following options are available for custom snippets:

template - String#

The template that this snippet should inherit, out of the available Python Markdown Templates.

definitions - Object#

The variables required for the selected template.

inline - Boolean#

Whether this snippet can appear inline (within a sentence). Defaults to false, which will treat this snippet as a block-level element in the content editor.

preview - Object#

A preview definition for displaying this snippet in the CloudCannon editor. See the preview options documentation.

_inputs - Object#

Input configurations for the keys contained in this snippet. See the input configuration documentation.

Python Markdown Templates#

The first step to configure your custom snippet is to identify which snippet template to use, as each snippet template requires a set of definitions keys to be configured. The following snippet templates are available:

Snippets#

example.md
copied
--8<-- "filename.ext"

Example config

cloudcannon.config.yaml
copied
_snippets:
  custom_snippet:
    template: python_markdown_snippet
    inline: false
    preview:
      text: My Custom Snippet
    definitions:
      snippet_name: filename.ext
cloudcannon.config.json
copied
{
  "_snippets": {
    "custom_snippet": {
      "template": "python_markdown_snippet",
      "inline": false,
      "preview": {
        "text": "My Custom Snippet"
      },
      "definitions": {
        "snippet_name": "filename.ext"
      }
    }
  }
}
cloudcannon.config.js
copied
module.exports = {
  _snippets: {
    custom_snippet: {
      template: "python_markdown_snippet",
      inline: false,
      preview: {
        text: "My Custom Snippet"
      },
      definitions: {
        snippet_name: "filename.ext"
      }
    }
  }
};

Definitions

snippet_name - String#

The file name of the snippet that you want to include.

Markdown in HTML#

example.md
copied
<div markdown class="result">
This is a *Markdown* Paragraph.
</div>

Example config

cloudcannon.config.yaml
copied
_snippets:
  custom_html:
    template: python_markdown_markdown_in_html
    inline: false
    preview:
      text: My Custom HTML
    definitions:
      tag_name: div
      content_key: content
      tag_attributes:
        - source_key: class
          default: result
          allowed_values:
            - result
cloudcannon.config.json
copied
{
  "_snippets": {
    "custom_html": {
      "template": "python_markdown_markdown_in_html",
      "inline": false,
      "preview": {
        "text": "My Custom HTML"
      },
      "definitions": {
        "tag_name": "div",
        "content_key": "content",
        "tag_attributes": [
          {
            "source_key": "class",
            "default": "result",
            "allowed_values": [
              "result"
            ]
          }
        ]
      }
    }
  }
}
cloudcannon.config.js
copied
module.exports = {
  _snippets: {
    custom_html: {
      template: "python_markdown_markdown_in_html",
      inline: false,
      preview: {
        text: "My Custom HTML"
      },
      definitions: {
        tag_name: "div",
        content_key: "content",
        tag_attributes: [
          {
            source_key: "class",
            default: "result",
            allowed_values: [
              "result"
            ]
          }
        ]
      }
    }
  }
};

Definitions

tag_name - String#

The HTML tag name of your element.

content_key - String#

The key to use in the data panel for editing the inner contents of the element.

tag_attributes - Array of argument options#

A list of the HTML attributes to include on your element.

Argument options#

The following options are available for named argument option objects:

editor_key - String#

Determines the key that will be used for this argument in the CloudCannon data panel.

source_key - String#

Determines the key of the key-value pair as it appears in the shortcode.

Will default to the editor_key if unset.

default - Any#

The default value that should be used for this argument when creating a new snippet in the CloudCannon editor.

type - String#

Restrict this argument to parse as the specified type. Useful to ensure booleans get parsed as the boolean value, rather than a string such as "true".

One of:

  • string
  • boolean
  • number
  • array
optional - Boolean#

Whether this argument is required for the shortcode. If false, shortcodes in your templates missing this argument will not match this snippet definition.

Defaults to false.

remove_empty - Boolean#

Whether this argument should be omitted from the output shortcode if the value is empty.

Defaults to false.

allowed_values - Array of values#

A list of values that this argument must be in order to match this snippet definition. Allows you to match different usages of the same shortcode to separate snippet definitions based on the value of an argument.

Related Articles

Open in a new tab