3

I have added a css button from http://www.cssbuttongenerator.com/ to Sphinx using the suggestions from How do I create a global role/roles in Sphinx?. I have

.. role:: button
   :class: button 

And I can get it with

:button:`button text`

I now want this button to be a link to cross reference to a different page, basically, be the "text" for

:ref:`text <Reference>`
mzjn
  • 48,958
  • 13
  • 128
  • 248
asmeurer
  • 86,894
  • 26
  • 169
  • 240

3 Answers3

1

Here is a simple Sphinx extension to do the trick. Take the CSS for the button you want and add it as source/_static/button.css (call the tag button).

Then add button.py alongside source, and add

sys.path.insert(0, os.path.abspath('..'))

to your conf.py (if it isn't there already) so that it can find it, and add 'button' to the extensions list in conf.py.

Then use it like

.. button::
   :text: Button Text
   :link: link/to/stuff.html

Thanks a ton to Bryan Van de Ven for writing most of this extension.

asmeurer
  • 86,894
  • 26
  • 169
  • 240
  • 3
    Your link to the button.py Sphinx extension no longer works as that particular repository went through refactoring and it was removed. The latest version of button.py that existed there can be found here: https://github.com/conda/conda-docs/blob/04613488d57688d77b0bc20618b9a4d5b56947ed/web/button.py – Nebbles Jul 10 '19 at 18:36
1

This script is great !!!!!!

but I want to use the role, so I do some modify:

...

def button_role(role, rawtext, text, line_no, in_liner: Inliner, options: Dict = None, content: List = None):
    text, link = text.split('|')
    node = ButtonNode()
    node['text'] = text.rstrip()
    node['link'] = link.rstrip()
    return [node], []


def setup(app: Sphinx):

    ...

    """
    :btn:`text | link`
    """
    roles.register_canonical_role('btn', button_role)

usage:

Hello :btn:`Click me to SO | https://stackoverflow.com/` !!!

Full code (v2)

version 2:

  • add style
  • and now you don't need to add :text: on the directives (use has_content instead of it.)
from docutils import nodes
import jinja2
from docutils.parsers.rst.directives import unchanged
from docutils.parsers.rst import Directive
from sphinx.application import Sphinx
from docutils.parsers.rst import roles
from docutils.parsers.rst.states import Inliner
from typing import List, Dict
import re

BUTTON_TEMPLATE = jinja2.Template("""
{% if link  %} 
    <a href="{{ link }}">    
        <button class="{{ class }}" {{ style |default('') }}>{{ text }}</button>
    </a>
{% else %}
    <button class="{{ class }}" {{ style |default('') }}>{{ text }}</button>
{% endif %}
""")


# placeholder node for document graph
class ButtonNode(nodes.Element):
    ...


class ButtonDirective(Directive):
    has_content = True
    required_arguments = 0

    """
    You have to set `option_spec` before you do the following
    .. xxx::
        :link:
        :class:
    """
    option_spec = {
        'link': unchanged,
        'class': unchanged,
        'style': unchanged,
    }

    # this will execute when your directive is encountered
    # it will insert a ButtonNode into the document that will
    # get visisted during the build phase
    def run(self):
        # env = self.state.document.settings.env
        # app: Sphinx = env.app
        # app.add_css_file('css/button/solid-blue-btn.css')  # or you can set it on the conf.py

        node = ButtonNode()
        node['text'] = ''.join(self.content.data)
        """
        .. button::
            :class: solid-blue-btn

            content.data[0]
            content.data[1]

        .. button:: content.data[0]
            :class: solid-blue-btn                
        """
        # style = "background-color:green; font-size: 30px;"

        node['class'] = self.options['class'].strip()
        node['link'] = self.options.get('link')
        if self.options.get('style'):
            style = 'style=' + self.options['style']  # style = "background-color:green; font-size: 30px;"
            node['style'] = style
        return [node]


# build phase visitor emits HTML to append to output
def html_visit_button_node(self, node: ButtonNode):
    # html = BUTTON_TEMPLATE.render(text=node['text'], link=node['link'])
    html = BUTTON_TEMPLATE.render({k: v for k, v in node.attributes.items()})
    self.body.append(''.join([_.strip() for _ in html.split('\n') if _.strip()]))
    raise nodes.SkipNode


def button_role(role, rawtext, text, line_no, in_liner: Inliner, options: Dict = None, content: List = None):
    # 'link="https://www.google.com/" style="background-color:green; font-size: 30px;"'
    regex = re.compile(f"(?:.*"
                       fr"(?P<link>link=\S*)|"
                       fr"(?P<style>style=\S*)"
                       f")",
                       )

    text, cls, *options = text.split('|')
    node = ButtonNode()  # nodes.paragraph
    node['class'] = cls.strip()
    node['text'] = text.rstrip()

    if options:
        options: str = options[0].strip()
        for attr in options.split(' '):
            m = regex.match(attr)
            if m:
                dict_option = m.groupdict()
                if dict_option.get('link'):
                    dict_option['link'] = dict_option['link'][5:]  # remove link=
                for key, value in dict_option.items():
                    if value:
                        node[key] = value.strip()
    return [node], []


def setup(app: Sphinx):

    """
    .. button::
        option1:
        option2:
    """
    app.add_node(ButtonNode,
                 # ↓ kwargs
                 html=(html_visit_button_node, lambda: None)
                 # other_2: Tuple[Callable, Callable],
                 # other_3: Tuple[visit_xxx, depart_xxx],
                 )
    app.add_directive('button', ButtonDirective)

    """
    :btn:``
    """
    roles.register_canonical_role('btn', button_role)

Test

.. button:: Google
    :class: btn-solid-blue
    :style: "background-color:green; font-size:30px;"
    :link: https://www.google.com/

.. button:: FB (no link)
    :class: btn-solid-blue


- .. button:: Google
    :class: btn-solid-blue
    :link: https://www.google.com/

- .. button:: FB (no link)
    :class: btn-solid-blue

----

Hello :btn:`link | btn-solid-blue | link=https://stackoverflow.com/` !!!

Hello :btn:`bg f-size | btn-solid-blue | style="font-size:30px;"` !!!

Hello :btn:`bg font-size | btn-hollow-red | style="background-color:yellow; font-size:30px;"` !!!

Hello :btn:`font-size | btn-hollow-red | style="font-size:50px;"` !!!

Hello :btn:`link | btn-hollow-red | link=https://stackoverflow.com/` !!!

Hello\ :btn:`none | btn-hollow-black`\!!!

where class btn-hollow was modified from https://www.w3schools.com/css/tryit.asp?filename=trycss_buttons_hover

enter image description here

Reference

Carson
  • 6,105
  • 2
  • 37
  • 45
0

<a href="https://any.website.notExistWebsite.weirdExtension"><button>Text here...</button></a>

Use this