4 bit color illustration of a mastodon feed in a columns layout

Include your Mastodon feed in a WordPress post

Display your personal mastodon feed or a tag timeline easily and efficiently in a WordPress post, and style it in a Pinteresque way with a sprinkle of CSS.

Table of Contents

How to get started in just 30 seconds

  1. Install Include Mastodon Feed from WordPress.org
  2. Locate your account-id using Wolfgang’s handy tool
  3. Insert a shortcode in your post, like in this example:
Screenshot of shortcode reading "  <script>
window.addEventListener("load", () => {
mastodonFeedLoad(
"https://%26quot%3Bmastodon.social%26quot%3B/api/v1/accounts/"13179"/statuses",
"include-mastodon-feed-6a1ca286f2e86",
{
linkTarget: "_self",
showPreviewCards: true,
excludeConversationStarters: false,
excludeTags: "",
content: {
hideStatusMeta: false,
hideDateTime: false          },
images: {
preserveImageAspectRatio: false,
size: "preview",
link: "status",
},
text: {
boosted: "boosted 🚀",
noStatuses: "No statuses available",
viewOnInstance: "view on instance",
showContent: "Show content",
permalinkPre: "on",
permalinkPost: "",
edited: "(edited)",
},
localization: {
date: {
locale: "en-US",
options: {},
}
}
}
);
});
</script>
<div class="include-mastodon-feed-wrapper"><ol class="include-mastodon-feed" id="include-mastodon-feed-6a1ca286f2e86"><li>Loading Mastodon feed...</li></ol></div>
"
A basic include-mastodon-feed shortcode

And you will see a single post, still unstyled for my theme’s dark mode:

  1. Loading Mastodon feed...

Include Mastodon Feed works so well, here’s why:

Well, that’s what an open protocol and a public API can do, in the hands of a skilled programmer with some time to spare. There is no “secret source”; the API simply returns the content of latest posts (statuses), as if you visited the public feed at mastodon.social/@Mastodon, but without the native UI.

Wolfgang even tweaked the plugin to support lazy load, smaller and faster image sizes, and a bunch of accessibility improvements for even better UX.

A bit of CSS makes a pretty column layout

As the HTML output provided by the plugin has good semantics, meaningful class names, and useful custom properties, let’s hit that secret hacking inspect button, and see what we can do with pure CSS. Maybe this?

  1. Loading Mastodon feed...
Shortcode used for showing the @oldrup mastodon feed
[include-mastodon-feed instance="mastodon.green" account="109535931552740701" limit="14" excludeReplies="true" excludeConversationStarters="true" onlyMedia="true" preserveImageAspectRatio="true" imageSize="preview" imageLink="status" showPreviewCards="false" hideStatusMeta="true" cache="true"]

How to build the block structure and the shortcode attributes

To enable the column layout, add two nested group blocks (three if you fancy the tilted and clipped layout), assign the classes and add the shortcode to the inner block. You can play around with the content width, but I’ve added some recommended starting points to the recipe below:

  1. Create container group with class imf-colums (full width)
  2. Optional: Create clipping group with class imf-clipper (90vw)
  3. Create inner group with class imf-inner (1440–1920px)
  4. Add heading and insert Include Mastodon Feed shortcode

Sample container group structure

Screenshot of the block editor overview, showing the tree structure of the three nested group blocks containing the shortcode

What attributes to use in the shortcode?

The appropriate attributes for the Include Mastodon Feed shortcode, depends on your use case. The goal of this little CSS project, is to show visual content in a “pinteresque style”, filtering out most text, but obviously linking to the original post’s creator. While speaking of posts vs toots vs statuses…

A mastodon post is also called a toot or a status. It’s just a post, don’t worry.

Github: Change the nouns “toot” and “status” to “post”

I suggest you start simple and follow the instructions at the Include Mastodon Feed plugin installation page. Here are some of the attributes I set specifically for the example above, and why:

AttributeReason           
excludeReplies=”true”Only show original status, no replies
excludeConversationStarters=”true”Ignore statuses directed at someone
onlyMedia=”true”Only return status with media
preserveImageAspectRatio=”true”Show original aspect ratio, no crop
imageSize=”preview”“Low-res” images, very important
imageLink=”status”Clicking the image leads to status
showPreviewCards=”false”Not supported by this styling
hideStatusMeta=”true”If showing your own account only
cache=”true”Enables 5 minutes caching of feed

How to add the imf-columns stylesheet

With the block structure and shortcode in place, all we need to enable the column layout, is to add the stylesheet to the website. Use any preferred custom CSS plugin or your theme’s ability to add custom CSS.

But before we do that, let’s have at the current posts tagged #SilentSunday on the mastodon.green instance. Breathe in, hold it … breath out. Gong! 🧘

  1. Loading Mastodon feed...

mastodon.green/tags/SilentSunday

Shortcode used for showing #SilentSunday posts from mastodon.green
[include-mastodon-feed instance="mastodon.green" tag="#silentsunday" limit="20" excludeReplies="true" excludeConversationStarters="true" onlyMedia="true" preserveImageAspectRatio="true" imageSize="preview" imageLink="status" showPreviewCards="false" hideStatusMeta="false" hideDateTime="true" cache="true"]

Custom CSS for mastodon feed column layout

The following CSS provides the three classes .imf-columns, .imf-clipper and .imf-inner as well as a set of CSS custom properties to tweak the design of the posts. I hope they are somewhat self-explanatory, a few notes, though:

  • We use CSS columns, meaning the --imf-column-width is the minimum width, and --imf-column-count is the maximum number of columns; the browser will fit in a number of columns within these constraints.
  • All my custom properties are prefixed --imf
  • The plugin custom properties are prefixed --include-mastodon-feed
/***
* IMF Columns; Image focused column styling for Include Mastodon Feed shortcode.
* 1.1.1: Added support for video and limited long hashtags
* Adjust the following settings to fit you theme.
***/

.imf-columns {
    --imf-column-width: 24ch;
    --imf-column-count: 5;
    --imf-column-gap: 2rem;
    --imf-clipper-max-height: min(80vh, 60rem);
    --imf-inner-rotation: 7deg;
    --imf-status-padding: .75rem;
    --imf-status-font-size: var(--wp--preset--font-size--small, 0.8rem);
    --imf-status-color: #404040;
    --imf-status-avatar-height: 1.5rem;
    --imf-status-box-shadow: 0.6rem 0.8rem 1rem #0003;
    --imf-status-media-hover: contrast(90%) brightness(120%) saturate(80%);
    --include-mastodon-feed-border-radius: 1.5rem;
    --include-mastodon-feed-bg-light: #fafafa;
    --include-mastodon-feed-accent-color: #563acc;
}

/***
* How to use: In block editor, create 2-3 nested groups with imf classes and shortcode:
* 1: Create container group with class imf-columns, this can have background
* 2:  Optional: create clipping group with class imf-clipper; recommended width: 90vw
* 3:   Create inner group with class imf-inner; recommended width: 1440-1920px
* 4:      In that, create heading (optional) and insert Include Mastodon Feed shortcode
***/


/** Include Mastodon Feed **/

/* Use CSS Columns for the mastodon feed */
.imf-columns .include-mastodon-feed {
    column-width: var(--imf-column-width);
    column-count: var(--imf-column-count);
    column-gap: var(--imf-column-gap);
    margin: 0 0 var(--imf-column-gap);
}


/** Status Cards **/

/* Display as INLINE-grid (safari-fix) for reordering of content and avoid column-breaking ⭐ */
.imf-columns .include-mastodon-feed .status {
    display: inline-grid;
    break-inside: avoid-column;
    margin: calc(var(--imf-column-gap)/2) 0;
    border: .5px solid #fff7;
    /* antialiasing*/
    padding: var(--imf-status-padding, 1rem);
    color: var(--imf-status-color);
    font-size: var(--imf-status-font-size);
    overflow-wrap: break-word;
    box-shadow: var(--imf-status-box-shadow, unset);
}


/* Hide statuses with videos or content warnings or unknown content */
.imf-columns .include-mastodon-feed .status:has(.audio, .gifv, .contentWarning, .unknown) {
    display: none;
}


/** Status Card > Content **/

/* Display as flex for reordering media and collapse ALL content (text nodes without html markup) ⭐ */
.imf-columns .include-mastodon-feed .status .content {
    display: flex;
    flex-wrap: wrap;
    visibility: collapse;
    line-height: 0;
}

/* Un-collapse proper marked up content (paragraphs, images, links) to make them visible again ⭐ */
.imf-columns .include-mastodon-feed .status .content * {
    visibility: visible;
    line-height: 1.33;
}

/* In the status, show hashtags and mentions links, but hide other arbitrary links */
.imf-columns .include-mastodon-feed .status .content p a:not(.hashtag, .mention) {
    display: none;
}


/** Status Card > Content > Media **/

/* Pull media up to be displayed first on the status ️ (Note; tabindex order will mess up) */
.imf-columns .include-mastodon-feed .status .content .media {
    order: -1;
    margin: 0 0 .5em 0;
    flex-grow: 1;
}

/* And round the corners */
.imf-columns .include-mastodon-feed .status .content .media :where(a, img, video) {
    border-radius: calc(var(--include-mastodon-feed-border-radius) / 1.5);
	width: 100%;
}

/* Use custom image hover filter */
.imf-columns .include-mastodon-feed .status .content .media a:hover {
    filter: var(--imf-status-media-hover);
}

/* Unset some video related rules */
.imf-columns .include-mastodon-feed .status .content .media .video {
    margin-top: unset;
    font-size: var(--imf-status-font-size);
}

/* Hotfix: Remove extra whitespace below some image links */
.imf-columns .include-mastodon-feed .status .content .media .image a {
    aspect-ratio: unset;
}


/** Status Card > Content > Text & Tags **/

/* Paragraphs; show only the first one ⭐ */
.imf-columns .include-mastodon-feed .status .content p:not(:first-of-type) {
    display: none;
}

/* If status last paragraph has hashtags, then limit first paragraph to 2 lines only ⭐ */
.imf-columns .include-mastodon-feed .status .content p:first-of-type:has(~p:last-of-type>a.hashtag) {
    --imf-status-line-clamp: 2;
}

/* Paragraphs; show only the first 2-3 lines ⭐ */
.imf-columns .include-mastodon-feed .status .content p:first-of-type {
	display: -webkit-box;
    -webkit-line-clamp: var(--imf-status-line-clamp, 3);
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* Paragraphs; Remove the margins, keeping this card tight */
.imf-columns .include-mastodon-feed .status .content p {
    margin: unset;
}

/* Breaks and blockquotes looks weird on cards, hide them! */
.imf-columns .include-mastodon-feed .status .content :is(br, blockquote) {
    display: none;
}


/** Hashtags **/

/* Make exception to 1 paragraph, but show the last paragraph too if it has hashtags, ... */
.imf-columns .include-mastodon-feed .status .content p:not(:first-of-type):last-of-type:has(.hashtag) {
    display: inline-block;
}

/* Display maximum 3 hashtags, hide the rest ⭐ */
.imf-columns .include-mastodon-feed .status .content .hashtag:not(:nth-of-type(-n + 3)) {
    display: none;
}

/* Hashtags; make sure they don't exceed #TheWithOfTheColumnsYouManiacs */
.imf-columns .include-mastodon-feed .status .content .hashtag span {
	display: inline-block;
	max-width: calc(var(--imf-column-width) - 6ch);
}


/** Status Cards > Account Info **/

/* Order account info below the content */
.imf-columns .include-mastodon-feed .account {
    order: 2;
    margin-top: 0.5em;
    font-size: var(--imf-status-font-size);
    line-height: 1;
}

/* Tweak account avatar and name spacing*/
.imf-columns .include-mastodon-feed .account,
.imf-columns .include-mastodon-feed .account a {
    display: flex;
    align-items: flex-end;
    gap: .4em;
}

/* Make avatar a little larger */
.imf-columns .include-mastodon-feed .account .avatar {
    height: var(--imf-status-avatar-height, 1.5rem);
}


/** OPTIONAL; Clipper and rotated Inner container */

/* Inner Container; rotate on laptops and larger */
@media screen and (min-width: 1440px) {

    /* Right align headings and text so it fits nicely when inner is rotated */
    .imf-columns:has(.imf-clipper) .imf-inner .wp-block-heading {
        text-align: right;
    }

    /* Rotate inner container */
    .imf-columns:has(.imf-clipper) .imf-inner {
        transform-origin: 46% 21%;
        rotate: var(--imf-inner-rotation);
    }

    /* Clip overflow that occurs when rotating inner container */
    .imf-columns .imf-clipper {
        height: var(--imf-clipper-max-height);
        max-height: var(--imf-clipper-max-height);
        overflow: clip;
    }

    /* Remove inner padding for that tight tilted section */
    .imf-columns:has(.imf-clipper) {
        padding: 0;
    }
}

/** OPTIONAL; Limit number of statuses on phones to 6 to avoid excessive scrolling */

@media (max-width: 768px) {
    .imf-columns .include-mastodon-feed .status:not(:nth-of-type(-n + 6)) {
        display: none;
    }
}


/* OPTIONAL; Fade in inner section to reduce layout shifts and because it looks nice. */

/* Fade in the entire inner container in .8 seconds (adjust to your site) */
.imf-columns .imf-inner {
    animation: fadeIn 0.8s linear forwards;
}

/* 0 opacity (invisible) for half the time, then fade to full opacity */
@keyframes fadeIn {

    0%,
    50% {
        opacity: 0;
    }

    100% {
        opacity: 1;
    }
}

Accessibility of Include Mastodon Feed

Wolfgang — the author of Include Mastodon Feed — and I, pledged to improve the plugin during the GAAD 2025 Global Accessibility Awareness Day. We worked on the HTML markup and the overall “out of the box experience”, divs were replaced with lists where appropriate, and we ensured that alt text and language attributes are present and tested screen reader and keyboard.

Still, I’m aware of two minor imperfections left:

  • As I use CSS order to pull post images to the top of the card, the focus order (aka tab order) is slightly off. Links in the post text (hashtags) are focused before the image link. Not a huge deal, but now you know.
  • If you use the imf-clipping class to visually cut off the bottom of the posts, for that “special effect”, the entire post text will still be read out aloud in screen-readers. Not bad, actually, but a little inconsistent.

I honestly think this is a fairly acceptable implementation of mastodon feeds on WordPress, but of course I may have missed something, so if you spot any opportunity to improve accessibility further, please get in touch.

Final thoughts

The Fediverse is just such a pleasure; people are helpful, API’s are open and stuff just works. But hosting stuff isn’t free, so when you have found the right instance for you, please consider supporting it.

I pay a few euros for my mastodon.green membership each month, in return for a social media experience that is ad-free and doesn’t sell my data. It even plants trees, ~600 each month, to offset the carbon emission caused by keeping the servers on. It’s well worth it, I can assure you that.

I’ll finish off by saluting all the great artists, who, in the age of AI-slop, creates and shares pieces of real human creativity. Please never stop ❤️

  1. Loading Mastodon feed...

https://mastodon.art/tags/MastoArt

Shortcode used for showing #MastoArt posts from mastodon.art
[include-mastodon-feed instance="mastodon.art" tag="#MastoArt" limit="20" excludeReplies="true" excludeConversationStarters="true" onlyMedia="true" preserveImageAspectRatio="true" imageSize="preview" imageLink="status" showPreviewCards="false" hideStatusMeta="false" hideDateTime="true" cache="true"]
Oldrup.dk site icon, 4-bit colour

Bjarne Oldrup

Bjarne is a web developer with a passion for a sustainable, inclusive and respectful internet.

With a background as a computer technician, graduating in 1992, he has worked as a programmer, system administrator and network specialist. Today, he focuses on website carbon footprint, web accessibility and GDPR compliance.

Bjarne has spent the last decade working with medium-sized businesses to help them reduce their websites' environmental impact and promote healthy online practices.

WordPress, HTML, CSS and LiteSpeed web servers are Bjarne's favourite tools, and the open-source community is his comfort zone.