Summary
A short opening that sums up the post. Usually placed first, right under the title.
Every post is built from ready-made blocks: a summary, body text, quotes, images, videos and more. You pick a block and fill in the content. Colours, font sizes and layout are already designed for you.
Setting up a theme? Jump to Part 2: For developers.
Click in the top-left corner of the editor, or between two blocks, to open the block list. The Pangolin Blocks group is at the top. You can also type a block's name in the search box.
Pangolin Blocks
Depending on how your site is set up, you may see only the Pangolin blocks plus WordPress's paragraph, heading and list blocks, or all WordPress blocks as well.
Click a block to select it. A small toolbar appears above it.
For each block: when to use it, what you fill in, and a screenshot of how it looks on the live site. Click a screenshot to see it full size.
A short opening that sums up the post. Usually placed first, right under the title.
The main body of the post: paragraphs, subheadings and lists.
Highlights a saying or a quotation.
A tip or a heads-up that readers shouldn't miss.
One large image, as wide as the text column.
Two images side by side, for example to compare them.
Three images in a row. On phones they stack on top of each other.
Four images in a 2 × 2 grid.
An animated GIF, for example to show a few steps.
A smaller, centred image. Good for portrait images, or images that don't need the full width.
A YouTube or Vimeo video, or a video file you upload.
https://www.youtube.com/watch?v=…Shows another page inside the post: a map, a form, a spreadsheet…
src="…".Shows a code snippet, keeping its spacing and line breaks.
Nothing extra to do: blocks adapt to the screen on their own. For example, Three Images shows its images in a row on a computer and stacks them on a phone.
Try typing a block's name (for example "Quotes") into the block list's search box. If it still doesn't show, the Pangolin Blocks plugin may not be active: tell your administrator.
Check that the block has content (empty blocks aren't shown) and that you clicked Update after editing. Then reload the post.
Hold Shift and click to select several blocks in a row, then use ▲ ▼ or drag them. For long posts, List View is quicker.
Press Ctrl + Z (Windows) or ⌘ + Z (Mac) to undo, or click the Undo button (curved arrow) in the top bar.
Make sure the link points to a single video on YouTube or Vimeo (not a channel or a playlist), and that the video allows embedding.
Everything a theme developer needs to install Pangolin Blocks on a site and make the blocks match its design. No changes to the plugin are needed.
The plugin decides what each block contains: its fields and its HTML structure. Your theme decides how it looks, by giving a CSS class to each part of each block. The plugin never outputs colours, fonts or spacing of its own, except a plain fallback style.
<blockquote> with a text part and an author part.pangolin-blocks.php gives each part a class: quote.text → "post-quote__text".Each block is styled either entirely by your theme or entirely by the plugin's default style. The two are never mixed:
| Situation | Classes used | Plugin's default CSS |
|---|---|---|
| The block is listed in your theme file | Only your classes | Not loaded |
| The block is not listed, or there is no theme file | Default classes (pangolin-quote, …) | Loaded |
If you list a block but leave out one of its parts, that part gets no class at all rather than the default one. You can style the blocks one at a time: the ones you haven't listed yet keep working with the default look.
pangolin-blocks folder into wp-content/plugins/ and activate it. Include build/; node_modules/ and src/ are not needed on a live site. The blocks now work with the default style.pangolin-blocks.php in the root of your theme, next to functions.php. In a child theme, the child theme's file is used first.add_filter( 'pangolin_blocks_restrict_inserter', '__return_true' ); to your theme's functions.php.A starter file covering all 13 blocks. Keys are block names without the pangolin/ prefix. Values are plain class strings: use your own class names, BEM, Tailwind utilities, or anything else your CSS understands.
<?php
// wp-content/themes/your-theme/pangolin-blocks.php
return array(
'summary' => array(
'text' => 'post-lead',
),
'paragraph' => array(
'wrapper' => 'post-prose',
),
'quote' => array(
'wrapper' => 'post-quote',
'text' => 'post-quote__text',
'author' => 'post-quote__author',
),
'note' => array(
'wrapper' => 'post-note',
'icon' => 'post-note__icon',
'text' => 'post-note__text',
),
'image-one' => array(
'wrapper' => 'post-media',
'figure' => 'post-media__item',
'image' => 'post-media__img',
'caption' => 'post-media__caption',
),
'single-image' => array(
'wrapper' => 'post-media post-media--narrow',
'figure' => 'post-media__item',
'image' => 'post-media__img',
'caption' => 'post-media__caption',
),
'image-two' => array(
'wrapper' => 'post-media post-media--2',
'figure' => 'post-media__item',
'image' => 'post-media__img',
'caption' => 'post-media__caption',
),
'image-three' => array(
'wrapper' => 'post-media post-media--3',
'figure' => 'post-media__item',
'image' => 'post-media__img',
),
'image-four' => array(
'wrapper' => 'post-media post-media--2',
'figure' => 'post-media__item',
'image' => 'post-media__img',
),
'gif-image' => array(
'wrapper' => 'post-media',
'figure' => 'post-media__item',
'image' => 'post-media__img',
),
'video' => array(
'wrapper' => 'post-video',
'video' => 'post-video__file',
),
'iframe' => array(
'wrapper' => 'post-embed',
'iframe' => 'post-embed__frame',
),
'code-editor' => array(
'pre' => 'post-code',
'code' => 'post-code__code',
),
);
Then style those classes in your theme's CSS, for example:
/* your-theme/style.css */
.post-quote {
margin: 0 0 2rem;
padding: 1.5rem;
border-left: 4px solid var(--brand);
background: #f6f6f8;
}
.post-quote__text { font-size: 1.25rem; margin: 0 0 .5rem; }
.post-quote__author{ font-style: normal; color: #666; }
.post-media { display: grid; gap: .75rem; margin: 0 0 2rem; }
.post-media--2 { grid-template-columns: repeat(2, 1fr); }
.post-media--3 { grid-template-columns: repeat(3, 1fr); }
.post-media--narrow{ max-width: 28rem; margin-inline: auto; }
.post-media__item { margin: 0; }
.post-media__img { width: 100%; height: auto; border-radius: 8px; }
.post-media__caption { font-size: .875rem; text-align: center; color: #666; }
Utility classes work the same way, for example 'text' => 'text-lg leading-relaxed border-l-4 border-brand pl-4'. Make sure Tailwind scans the theme file so those classes end up in the compiled CSS. A root-level './*.php' content pattern already covers it; otherwise add the file to content in tailwind.config.js and rebuild your CSS.
The classes can also come from code, for example a site plugin. The filter receives the theme file's array, or an empty array when there is no file:
add_filter('pangolin_blocks_theme_classes', function ($classes) {
$classes['note'] = array(
'wrapper' => 'alert alert-info',
'icon' => 'bi bi-info-circle',
'text' => 'mb-0',
);
return $classes;
});
functions.php or in a plugin's main file. The classes are read once, when WordPress runs init, so a filter added later (inside a template, for example) is ignored.The HTML each block outputs on the site, with the part names in {braces}. Style these parts rather than bare elements: the editor adds its own wrappers and classes around blocks.
| Block | Markup on the site |
|---|---|
summary | <p class="{text}">…</p> |
paragraph | <div class="{wrapper}"> + core blocks: h2.wp-block-heading, p, ul.wp-block-list </div>The inner text is not classed by the theme file: style it with your normal post typography, e.g. .post-prose h2, .post-prose ul. |
quote | <blockquote class="{wrapper}"><p class="{text}">“…”</p><cite class="{author}">— …</cite></blockquote>The quotation marks and dash are added by the block. cite is omitted when there is no author. |
note | <div class="{wrapper}"><i class="{icon}" aria-hidden="true"></i><p class="{text}">…</p></div>The <i> is only output when icon has a class, so an icon-font class (Remix Icon, Bootstrap Icons, …) works well there. |
image-one, single-image, image-two | <div class="{wrapper}"> one <figure class="{figure}"><img class="{image}"><figcaption class="{caption}">…</figcaption></figure> per image </div>figcaption only when a caption is set. Images come from wp_get_attachment_image(), so they have srcset, width/height and lazy loading. |
image-three, image-four, gif-image | Same as above, without captions. The GIF is output at full size so it stays animated. |
video | <div class="{wrapper}"> YouTube/Vimeo <iframe> from oEmbed, or <video class="{video}" controls> </div>The oEmbed iframe has no class: size it through the wrapper, e.g. .post-video { aspect-ratio: 16/9 } .post-video iframe { width: 100%; height: 100% }. |
iframe | <div class="{wrapper}"><iframe class="{iframe}" style="height: …px" loading="lazy"></iframe></div>The height comes from the editor's field; set the width in your CSS (usually width: 100%). |
code-editor | <pre class="{pre}"><code class="{code} language-…">…</code></pre>language-… is always added, so highlight.js or Prism can pick it up. The plugin doesn't load a highlighter. |
An empty block outputs nothing at all, so you don't need :empty rules.
The plugin sends your classes to the editor, so blocks there are built with the same classes as on the site. For them to look the same, your CSS must also be loaded inside the editor. The standard way:
// functions.php add_action('after_setup_theme', function () { add_theme_support('editor-styles'); add_editor_style('style.css'); // path relative to the theme folder });
body don't leak into the WordPress admin.enqueue_block_assets action loads assets into both the site and the editor.If you don't need a custom design, skip the theme file. The default style is deliberately plain: it inherits the site's font and text colour. Two CSS variables tint it:
:root {
--pangolin-accent: #2563eb; /* borders, note icon */
--pangolin-surface: rgba(0, 0, 0, .04); /* quote and note backgrounds */
}
The default classes, useful if you only want to adjust the default look with a few overrides, are listed under pangolinClasses in each block's block.json (e.g. pangolin-quote, pangolin-quote__text, pangolin-images--cols-2).
| Filter | What it changes |
|---|---|
pangolin_blocks_restrict_inserter | Return true to limit the inserter on posts to Pangolin Blocks plus a few core text blocks. Off by default. |
pangolin_blocks_theme_classes | The whole block → part → classes array (see above). |
pangolin_blocks_allowed_core_blocks | When the inserter is restricted, which core blocks can be added next to Pangolin Blocks. Default: paragraph, heading, list, list item and Classic. |
pangolin_blocks_order | The order of the blocks in the inserter: a list of block names without the prefix. |
// Also allow core images and tables on posts.
add_filter('pangolin_blocks_allowed_core_blocks', function ($blocks) {
return array_merge($blocks, array('core/image', 'core/table'));
});
By default the plugin doesn't restrict the inserter. Once a theme opts in with pangolin_blocks_restrict_inserter, only posts are restricted; pages and custom post types keep every block, and Pangolin Blocks are available there too.
pangolin-… classes.Check that the file is named exactly pangolin-blocks.php, sits in the root of the active theme (or child theme), and returns an array. The block key must be the name without the prefix, e.g. 'image-two', and its value must be an array.
Your CSS for those classes isn't loaded, or (with Tailwind) the classes weren't compiled. Rebuild your CSS and check that the theme file is in Tailwind's content list.
Load your stylesheet into the editor with add_editor_style() (see Editor preview), then hard-reload the editor.
It isn't in your theme file yet, so it uses the default style. Add an entry for it, with the part names from its block.json.
Not from the theme: structure and fields belong to the plugin, so every site stays compatible with saved content. To add or change blocks, see the plugin's README.md.