Customize posts' contents with your own styles


:information_source: To be able to use these tips and tricks, you need to be an administrator of either a self-hosted Discourse instance or a Discourse-hosted plan higher than Basic.


Discourse supports several methods to format and customize a post’s contents. You can find the list here:

But sometimes, you’ll want something more specific, for example, a link that looks like a button.

Green button

This is the kind of modification we’ll learn here.

The logic

I’ll briefly explain the logic behind but you can go to the next step and jump into a practical example :slight_smile:

Discourse allows any HTML attribute starting with data- in a post’s content.
Those are the attributes we’ll target with CSS to customize our content.

I’ll call them data- attributes in this tutorial :slight_smile:

One way to create elements with these attributes is a BBcode-like tag: [wrap], to which we’ll add a value of our choice. Here we choose “button” (that could be anything else, even the name of your dog :dog:):

[wrap=button]some text[/wrap]

This will output an HTML element having the following attribute: data-wrap="button".

First example: a pink background

Let’s start with a practical example. We’ll create text with a pink background.

As a block element

In your post, on an empty line, write:

[wrap=pink]pink text[/wrap]

block wrap

It will create a div element having the attribute data-wrap="pink".

Then, add the following CSS to your theme.
Go to Admin panel → Customize → Themes → your theme → Edit CSS/HTML → CSS.

Put the following CSS code inside:

[data-wrap="pink"] {
    background: pink;

Then click the Save button.

wrap css

Go back to your post, and see the result:

Yes, it is already beautiful :cherry_blossom:

You’ll notice that the background covers the whole post width. Because our wrap is the only element on its line, it outputs a block element.
You can learn more about the difference between blocks and inline HTML elements here: HTML Block and Inline Elements.

If you want your pink background on multiple lines (still as a block), you’ll need both your [wrap] tags having no other content or text on the same line:

pink text
pink text
pink text
pink text

This will look like this:

As an inline element

Now, let’s add some text before the [wrap], or after, or both :smile:. For example:

Here is some [wrap=pink]pink text[/wrap] and it's awesome ✨

Here’s the result:

If text or other elements are on the same line as one of your [wrap] tags, it will output an inline element.

Second example: a link with a button’s appearance.

Fiddling with the [wrap] tag can sometimes lead to unwanted results for various reasons, one being that it can be a block or an inline element depending on the context.
So, we’ll describe two different methods that achieve the same result, but you’ll be able to pick the one that suits you the most :v:

An inline button link with [wrap]

The syntax to create a link using markdown is: [some text](https://some-link.etc).
To customize the text and make it appear like a button, we’ll insert the wrap inside the square brackets. Here’s an example:

This [[wrap=button]nice link[/wrap]]( is a blue button 🐳 !

We won’t comment on what this code outputs. You know that because you wrote [wrap=button], you’ll have to target [data-wrap="button"] in your CSS.

So, let’s go, let’s add some fancy CSS to make it pretty! :sparkles:

[data-wrap="button"] {
    display: inline-block;
    padding: 0.5em 1em;
    background: DodgerBlue;
    color: White;

We won’t detail the CSS rules here. There are many CSS resources on the Internet, so if you want to do more specific modifications, you’ll have to learn about it first. :slight_smile:

The result :magic_wand: :

That looks good, right?

An inline button link with regular HTML content

Since Discourse accepts HTML code, we can decide not to use the [wrap] tags and use HTML with a data- attribute. In this example, we’ll use the regular Markdown syntax for the link and surround it with <span> tags.
:information_source: We can’t directly use a link <a> tag because it’s an exception and won’t allow any data- attribute.


This <span data-button>[link](</span> is a green button 🐸 !

It will output a link inside a <span> tag having a data-button attribute, which means the CSS will be a bit more complicated. We will have to target both data-button and the link:

[data-button] {
    display: inline-block;
    padding: .5em 1em;
    background: ForestGreen;
    a {
        color: White;

And here’s the result!

To go further

A customized list using [wrap]

[wrap] tags and data- attributes can be used in many contexts and you can customize more advanced content. The limit is mostly your CSS knowledge (and HTML to a lesser extent).

I’ll give a single example without explanation by customizing a list in which each element will be prepended with a cat emoji:


* Felix
* Garfield
* Nat's cat


[data-wrap="cat"] ul {
    list-style: none;
    li:before {
        content: "🐈";
        margin-right: 0.25em;


Using your own theme’s colors variables

If you allow users to use different themes or colors, your modifications may not look good for each one, especially if they have choices between light and dark color schemes.

A good practice is using Discourse’s color variables instead of “hardcoded” colors such as red, #FF0000 or rgb(255,0,0).

Here’s an example in which the button’s background color will use the primary color of the current palette, and the text will use the secondary color:


This [[wrap=button]nice link[/wrap]]( is a button 🌈 !


[data-wrap="button"] {
    display: inline-block;
    padding: 0.5em 1em;
    background: var(--primary);
    color: var(--secondary);

Here’s how it will look for a user using the Solarized Light color scheme:

And if they use the Solarized Dark color scheme:


You now have the basics to create custom elements using the [wrap] element and the data- attributes.

To make more advanced customizations, learning CSS is primordial. You’ll find many tutorials on the Internet.

The following Discourse’s guide can also be of some help: Make CSS changes on Your Site.
Using the developer’s tools of your Internet Browser will also easily show you the list of your Discourse’s color variables and what each looks like:

:raised_hand_with_fingers_splayed: Feel free to suggest any modification for this guide!


Thank you @Canapin

A good example of a Theme Component using some of these concepts is this one:


Nice work!

Im curious to see what other creative solutions users come up with using the data attribute.

Is there any advantage of using HTML <span data-button> over BBCode [wrap="button"] ?


Without having thought about it too much, I’d say using a <span> allows you to put an inline element as the only content on a single line.

Using a [wrap] on a single line without any other content next to it will automatically output a block element. The text inside will also be wrapped with paragraph <p> tags.

Other than that, it’s probably a matter of taste. I also didn’t mention that a [wrap] and an HTML element can have multiple -data attributes as I don’t think it’s very useful for most purposes.


How to add Bootstrap “Cards” in your Posts/Topics

…some might say this is crazy, to complicated, or overboard but I love it :smiley:

  • Added some colours to better see the nesting of the BBCode.

STOP! Don’t use my code

Instead, use the improved code posted by @Canapin CLICK HERE

BBCode to include in Topic/Post

[wrap="card-header"]**Card Header**[/wrap]
[wrap="card-title"]**Card Title**[/wrap]
[wrap="card-text"]Card Text, Card Text, Card Text, Card Text, Card Text, Card Text, Card Text, Card Text, Card Text, Card Text, Card Text, Card Text, Card Text, Card Text, Card Text, Card Text, Card Text, Card Text, Card Text, Card Text, Card Text, Card Text, Card Text, Card Text, Card Text, Card Text, Card Text, Card Text[/wrap]

CSS Code to add to theme.

// Bootstrap Card Box
[data-wrap="card"] {
    position: relative;
    display: flex;
    flex-direction: column;
    min-width: 0;
    word-wrap: break-word;
    background-color: #fff;
    background-clip: border-box;
    border: 1px solid rgba(0,0,0,.125);
    border-radius: 0.25rem;

// Bootstrap Card Header
[data-wrap="card-header"] {
    padding: 0.5rem 1rem;
    margin-bottom: 0;
    border-bottom: 1px solid rgba(0,0,0,.125);
    background: #007bff;
    color: #fff;
    border-radius: 5px 5px 0px 0px;

// Bootstrap Card Body
[data-wrap="card-body"] {
    flex: 1 1 auto;
    padding: 1rem 1rem;

// Bootstrap Card Title
[data-wrap="card-title"] {
    margin-bottom: 0.5rem;

// Bootstrap Card Text
[data-wrap="card-text"] {
    margin-top: 0;
    margin-bottom: 1rem;

Of course, it depends on what you intend to put in it, but to achieve the same exact visual as your example, you can optimize your code a lot:

[wrap="card-header"]**Card Header**[/wrap]
**Card Title**

Card Text, Card Text, Card Text, Card Text, Card Text, Card Text, Card Text, Card Text, Card Text, Card Text, Card Text, Card Text, Card Text, Card Text, Card Text, Card Text, Card Text, Card Text, Card Text, Card Text, Card Text, Card Text, Card Text, Card Text, Card Text, Card Text, Card Text, Card Text
[data-wrap="card-header"] {
    padding: 0.5em 1em;
    border: 1px solid rgba(0,0,0,.125);
    border-bottom: 0;
    background: #007bff;
    color: #fff;
    border-radius: 5px 5px 0 0;

[data-wrap="card-body"] {
    padding: 1em;    
    border: 1px solid rgba(0,0,0,.125);
    border-radius: 0 0 5px 5px;


Ahhh! Thats so much better! Thank you so much for making the improvements! <3

Nice! When is the markdown in the [wrap]...[/wrap] element processed, or is there a trick to getting it to render before it’s included in the wrap?

For example, I’ve tried formatting some text in the element as bold or italic, and it doesn’t render like that - I just see _text_ or **text** on the page in my browser, once I save it :frowning:

1 Like

It indeed seems formatting (whether it’s HTML, Markdow or BBcode) won’t work in [wrap] if it’s an inline element (if there’s other content on the same line):

You’ll need to create a <span> for this.