Styling Markdown generated HTML with Tailwind CSS and Parsedown
Tags: markdown, tailwindcss, php, laravel
Published: 2021-08-28 11:49:43

---
published: true
title: Styling Markdown generated HTML with Tailwind CSS and Parsedown
description: Possibilities to style generated HTML out of Markdown with Tailwind CSS and Parsedown
tags: #markdown #tailwind #php #laravel
---

## Introduction

Parsedown is a lightweight Markdown parser written in PHP. It's included by default in Laravel.
When generating HTML from Markdown it's necessary to add classes to the generated HTML tags,
especially if your whole site is based on a functional CSS framework like Tailwind CSS.

I had the use case that I built an application and some content pages are driven by HTML generated from markdown.
I only wanted to style the HTML on these markdown based content pages in a certain way and
prevent side-effects on other styles of the same HTML elements. Additionally when using a functional CSS framework and don't want to create custom classes this creates a challange.

That's why I want to outline the possibilities I thought about to style the generated HTML with this post.

## A concrete example

Let's first look at an example what the resulting HTML looks like of parsed markdown snippet processed through Parsedown:

```markdown
# Welcome to Markdown

## Explanation

In this post we will show what *Parsedown* generates out of this **markdown** snippet.
We will show what the resulting `HTML` looks like. We will look at:

  * Headlines
  * Formatting within text
  * Lists
  * Paragraphs
  * Blockquotes

Note that --- not considering the asterisk --- the actual text
content starts indentend.

> If you want to use a quote by someone else you can do this with a 
> blockquote like this.
```

```HTML
<h1>Welcome to Markdown</h1>
<h2>Explanation</h2>
<p>
    In this post we will show what <em>Parsedown</em> generates out of this <strong>markdown</strong> snippet.
    We will show what the resulting <code>HTML</code> looks like. We will look at:
</p>
<ul>
    <li>Headlines</li>
    <li>Formatting within text</li>
    <li>Lists</li>
    <li>Paragraphs</li>
    <li>Blockquotes</li>
</ul>
<p>Note that --- not considering the asterisk --- the actual text
    content starts at 4-columns in.
</p>
<blockquote>
    <p>
        If you want to use a quote by someone else you can do this with a
        blockquote like this.
    </p>
</blockquote>
```

We can see that there are no classes applied to the HTML. With Tailwind this will look pretty unformatted
as tailwind normalizes the styling of the browser with a small set of presets.

## The `@apply` directive from Tailwind

In many of my solutions it makes sense to use the `@apply` directive. That's why I shortly want to introduce it to you. With Tailwind it's possible to combine multiple classes and generate a new custom class out of it using `@apply`.

A short example:

```css
.my-custom-class {
    @apply mt-8 text-2xl /* apply tailwind classes here */
}
```

will result in a class defintion that looks like this:

```css
	margin-top: 2rem;
	font-size: 1.5rem;
	line-height: 2rem;
```

If you don't know this method or want to read more about you can have a look [here](https://tailwindcss.com/docs/functions-and-directives#apply).

## What are the possibilities to style this part of HTML ?

1. Adding base styles (`@layer`, or wrting a plugin)
2. Using CSS specifity to overwrite base styles 
3. Extending the PHP Class and directly rendering Tailwind classes
4. Extending the PHP Class and using `@apply`

## 1. Adding new base styles

Tailwind CSS makes it possible to overwrite the base styles.
This can be done with the `@layer` directive or by using a [plugin](https://tailwindcss.com/docs/adding-base-styles#using-a-plugin).

Let's see how to define the styles for a headline using the `@layer` directive:
```css
@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
  h1 {
    @apply mt-8 text-2xl;
  }
}
```

To rewrite the layout of the `h1` tag with a plugin one can use the following code:

```javascript
const plugin = require('tailwindcss/plugin')

module.exports = {
  plugins: [
    plugin(function({ addBase, theme }) {
      addBase({
        'h1': { fontSize: theme('fontSize.2xl'), marginTop: '2rem', lineHeight: '2rem' },
      })
    })
  ]
}
```

### Pros & Cons

🟡 base styles will apply globally on every page (depending on your situation can be positve or negative)
🟢 the solution is easily maintainable and needs only adjustments in 1 file
🟢 the amount of customization small (uses Tailwind features to customize the result)
🔴 the plugin for that case is in my opinon overengineering


## 2. Using CSS specifity to overwrite base styles

When rendering our Markdown at a certain point in our template (i.e. a Laravel blade template) we can wrap that part with a `div` and add a class to the `div`. Here is an example:

```html
<div class="parsedown">
    {!! (new Parsedown)->text($markdownText) !!}
</div>
```

Now we can increase the specifity for that HTML block by writing the following CSS code:

```css
.parsedown h1 {
	margin-top: 2rem;
	font-size: 1.5rem;
	line-height: 2rem;
}
```

Alternatively we can directly use Tailwind classes here by using the `@apply` method:
```css
.parsedown h1 {
    @apply mt-8 text-2xl
}
```

### Pros & Cons

🟢 styles will be scoped by using a class (or could also be an id)
🟡 the solution is maintainable and needs adjustments in the CSS and the HTML file
🟢 no real customization needed (to prevent customized CSS from the Tailwind CSS perspectiv `@apply`is used)
🟢 only uses standard mechanisms of HTML and CSS

## 3. Extending the PHP Class and directly rendering Tailwind classes

The PHP class of Parsedown can be easily extended to add additional syntax to the generated HTML part.
Let's suppose we want to add Tailwind CSS classes to a headline.

```php
<?php

namespace App\Utils;

use Parsedown;

class ParseDownTailwind extends Parsedown {

    protected function blockHeader($Line) {
        $header = parent::blockHeader($Line);
        $header['element']['attributes']['class'] = "mt-8 text-2xl";
        return $header;
    }
}
```

Parsedown has a `protected` function called `blockHeader($Line)` which is repsonsible for rendering
all headline tags (h1 - h6). 

First we need to make sure that the normal generation keeps working:
```php
$header = parent::blockHeader($Line);
```

This line makes sure to call the parent method of the original Parsedown class.
After that we are adding the `class` attribute to the html.

```php
$header['element']['attributes']['class'] = "mt-8 text-2xl";
```

In this case we would format all headlines with the Tailwind classes `mt-8 text-2xl`.

If we want to differentiate we can do that like this:
```php
if ($header['element']['name'] === 'h1') {
    $header['element']['attributes']['class'] = "mt-8 text-2xl";
} else {
    $header['element']['attributes']['class'] = "mt-4 text-xl";
}
```

### Pros & Cons

🟢 styles will be scoped depending on the classes you use in your customized PHP class (theoretically multiple classes could be created for different styles in multiple blocks)
🟡 the solution is maintainable however CSS classes are rendered and hardcoded in a PHP class
🟡 a certain of customization of Parsedown is needed
🔴 Against seperation of concerncs (backend code deciding on visual styling)


## 4. Extending the PHP Class and using `@apply`

With Tailwind it's possible to combine multiple classes and generate a custom class out of it using `@apply`.
If you don't know this method you can have a look [here](https://tailwindcss.com/docs/functions-and-directives#apply).
Let's suppose we want to create a `.markdown-h1` css class from the above definitions.

```css
.markdown-h1 {
    @apply mt-8 text-2xl
}
```

Now we can use the solution again to extend the base Parsedown class.
This time we render the `.markdown-h1` css class.
This has the benefit of decoupling our PHP code a little bit more from the CSS code.

```php
if ($header['element']['name'] === 'h1') {
    $header['element']['attributes']['class'] = "markdown-h1";
} 
```

### Pros & Cons

🟢 styles will be scoped to the point where you call your customized PHP class (theoretically multiple classes could be created for different classes in multiple blocks)
🔴 the solution needs higher maintainance as it requires creating CSS classes and rendering them in the PHP code 
🟡 a certain of customization of Parsedown is needed
🟡 In comparison to solution #3 a higher seperation of concerncs (backend code deciding on CSS classes however your decision on how it looks like will be in your CSS file)


## Conclusion

I currently have solution #3 implemented in the project. However I might switch to solution #2 as it's less customizing and uses the technologies how they are meant to be used.

Even if this a very specific problem, I wanted to write about it and thought it's a good topic for my first
post. If you have alternative solutions please leave a comment.
I hope this post was interesting and helpful for you.
I am happy for every feedback.