Custom CSS per blocks of paragraphs with Redcarpet

I’m using Redcarpet for my markdown, and sometimes want to render specific paragraphs with css classes:

<p>normal paragraph</p>

<p class="specific">special css paragraph</p>

<p class="specific">special css paragraph</p>

<p>normal paragraph</p>

Usually the paragraphs are in groups, so I tried

<div class="specific">
special css paragraph

special css paragraph
</div>

But the renderer doesn’t insert <p> tags, so the output all runs together:

<div class="specific">
special css paragraph special css paragraph
</div>

I know I can put <p> tags around each paragraph, but that seems to defeat the purpose of using a Markdown parser.

Is there an “easy” way to style specific paragraphs with markdown, specifically Redcarpet, if possible?

For a bit of context, here is the repo for this project, and its current front page on archive.today

That’s a known limitation of Redcarpet, but the default Markdown renderer has a way to do it:

<!-- source/index.html.md -->

This paragraph is special.
{:special}

  {:special: .some-class .some-other-class}
<!-- build/index.html -->

<p class="some-class some-other-class">This paragraph is special.</p>

See this proposal for a better explanation of how to use this syntax.

If you must use Redcarpet, you might try exploring custom post-processing, e.g.:

<!-- source/index.html.md -->

This paragraph is special.
{:special}
# data/definitions.yml

special: 'some-class some-other-class'
# config.rb

require 'oga'

set :markdown_engine, :redcarpet

after_render do |content, _path, _locals, template_class|
  return unless template_class == Middleman::Renderers::RedcarpetTemplate

  Oga.parse_html(content).tap do |document|
    document.css('p').each do |p|
      p.at_xpath('text()[last()]').text.sub!(/\s*{:(\w+)}$/, '')
      p.set 'class', data.definitions[$1] if $1
    end
  end.to_xml
end
<!-- build/index.html -->

<p class="some-class some-other-class">This paragraph is special.</p>

though this has certainly left the realm of “easy” solutions.

1 Like

Wow great! Thank you so much for clear formatting and samples!!

Adding the {.special-class} text to each paragraph might be a bit more fiddly than I want, but I’ll consider it. (Most of the special paragraphs are consecutive.)

I used Redcarpet because I wanted double-tildes to make strikethroughs. If a different parser can do strikethroughs and custom fenced code blocks, that may be ideal.

One thing I enjoy about working with static site generators is that there’s typically little harm that can be caused by trying silly things like this:

<!-- source/index.html.md -->

<div apply class='some-class'>
This paragraph is special.

This paragraph is ~~not very~~ special.
</div>
<!-- build/index.html -->

<p class="some-class">This paragraph is special.</p>

<p class="some-class">This paragraph is <del>not very</del> special.</p>
# config.rb

require 'oga'

set :markdown, parse_block_html: true

after_render do |content, _path, _locals, template_class|
  return unless template_class == Middleman::Renderers::KramdownTemplate

  # Strikethrough
  content.gsub!(/~~(.+?)~~/, '<del>\1</del>')

  # Bulk attribute application
  Oga.parse_html(content).tap do |document|
    document.css('div[apply]').each do |wrapper|
      attributes = wrapper.attributes.reject { |a| a.name == 'apply' }

      wrapper.children.each do |node|
        node.attributes = attributes if node.try(:name) == 'p'

        wrapper.before node
      end

      wrapper.remove
    end
  end.to_xml
end

Especially in personal projects, I encourage you to experiment.

2 Likes