Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tagging "tasklist" lists with a class for easy styling? #137

Closed
nesquena opened this issue Apr 13, 2021 · 4 comments
Closed

Tagging "tasklist" lists with a class for easy styling? #137

nesquena opened this issue Apr 13, 2021 · 4 comments

Comments

@nesquena
Copy link

nesquena commented Apr 13, 2021

Hi all! With tasklist extension enabled, this markdown:

- [ ] foo
- [x] bar

Generates the following HTML:

<ul>
    <li><input type="checkbox" disabled="">foo</li>
    <li><input type="checkbox" disabled="" checked="">bar</li>
</ul>

Is it possible to add classes (as GitHub does) to make these style-able?

GitHub renders as such:

<ul class="contains-task-list">
  <li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox"> foo</li>
  <li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> bar</li>
</ul> 

Why is this feature necessary?

I want to add "list-style: none;" to the <ul> class, as it currently doesn't look good:

image

and I'd want to see this rendered as:

image

Apologies if I've missed a way to do this, I've looked through the docs but couldn't see a way.

@nesquena nesquena changed the title Tagging "tasklist" lists with a class? Tagging "tasklist" lists with a class for easy styling? Apr 13, 2021
@kivikakk
Copy link
Collaborator

There's no straightforward way to do this using commonmarker itself; the recommendation is to do as GitHub do and use something like html-pipeline to post-process the generated HTML in stages to apply transforms as needed. This is a fair bit cleaner than trying to do everything in the Markdown→HTML step itself, and offers a lot of flexibility.

You'd need to define a filter that searches for ul li input[type=checkbox] and mark the <ul>s and <li>s appropriately. An alternative would be to create your own tasklist transformer, which is how GitHub do it themselves — they don't actually use the tasklist extension provided in cmark-gfm (and thus commonmarker).

@kivikakk
Copy link
Collaborator

Oh, what am I saying — you can of course subclass Renderer. See how the provided HtmlRenderer renders tasklist items.

@nesquena
Copy link
Author

nesquena commented Apr 14, 2021

Thanks @kivikakk, good to know, I'll look at that and see if I can take the renderer subclass approach then. If I figure out the best way to modify, I am assuming I'd need to determine if a node is a tasklist and then use that to override list_item

def list_item(node)
block do
tasklist_data = tasklist(node)
container("<li#{sourcepos(node)}#{tasklist_data}>#{' ' if tasklist?(node)}", '</li>') do
out(:children)
end
end
end
since tasklist seems to only have control of the "input" not the li or ul. I'll respond back here if I figure things out. If anyone else has done this before in another project, would be interested to hear if this seems like the best path forward and if you have any prior work on this. Good to know about the html-pipeline option as a fallback.

Maybe something like:

def list_item(node) 
   block do 
     tasklist_data = tasklist(node) 
     container("<li#{" class='task-list-item'" if tasklist?(node)}#{sourcepos(node)}#{tasklist_data}>#{' ' if tasklist?(node)}", '</li>') do 
       out(:children) 
     end 
   end 
 end 

I am happy for this to be closed for now, although I would wonder since 9/10 times you don't want to render tasklists with a bullet if it wouldn't be worth making this easier to achieve in the future. I understand that this may also be partly a responsibility of the underlying commonmark library though to expose that or simply out of scope.

@nesquena
Copy link
Author

nesquena commented Apr 15, 2021

Update, I ended up going the html-pipeline approach, here's what I did for anyone else that stumbles upon this later. Setup html-pipeline:

gem 'html-pipeline', '~> 2.14'

Created a custom filter to add the classes to the ul and li:

class MarkdownTaskListClassFilter < HTML::Pipeline::Filter
  def call
    doc.search("ul > li > input[type=checkbox]").each do |input|
      input.parent['class'] = "task-list-item" # li
      input.parent.parent['class'] = "contains-task-list" # ul
    end
    doc
  end
end

Process the filter in my code here after rendering the markdown to HTML:

# Render the body to HTML to `html_body` using commonmark above... ​
pipeline = HTML::Pipeline.new [MarkdownTaskListClassFilter]
result = pipeline.call html_body
result[:output].to_s

I'm all set with that solution, thanks again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants