Repository: readthedocs/sphinx-hoverxref

Issue:
Hoverxref still not working on Jupyter Book?
Hi there,

I'm currently trying to implement sphinx-hoverxref in my Jupyter Book and I can't make it work. I'm aware of the relatively new version of Jupyter Book working with RTD theme and that makes me wonder why it won't work. So, have I missed something or does sphinx-hoverxref not work with Jupyter Book? 



Discussion:

Patch:
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 0000000..9a2e7e2
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1,2 @@
+*       @readthedocs/backend
+/docs/  @readthedocs/advocacy
\ No newline at end of file
diff --git a/hoverxref/_static/css/tooltipster.custom.css b/hoverxref/_static/css/tooltipster.custom.css_t
similarity index 91%
rename from hoverxref/_static/css/tooltipster.custom.css
rename to hoverxref/_static/css/tooltipster.custom.css_t
index 45914be..8bc54d6 100644
--- a/hoverxref/_static/css/tooltipster.custom.css
+++ b/hoverxref/_static/css/tooltipster.custom.css_t
@@ -1,4 +1,4 @@
-.hoverxref {
+.{{ hoverxref_css_class_prefix }}hoverxref {
     border-bottom: 1px dotted;
     border-color: gray;
 }
diff --git a/hoverxref/_static/js/hoverxref.js_t b/hoverxref/_static/js/hoverxref.js_t
index 348d95e..fe9286b 100644
--- a/hoverxref/_static/js/hoverxref.js_t
+++ b/hoverxref/_static/js/hoverxref.js_t
@@ -96,9 +96,9 @@ $(document).ready(function() {
     // Remove ``title=`` attribute for intersphinx nodes that have hoverxref enabled.
     // It doesn't make sense the browser shows the default tooltip (browser's built-in)
     // and immediately after that our tooltip was shown.
-    $('.hoverxref.external').each(function () { $(this).removeAttr('title') });
+    $('.{{ hoverxref_css_class_prefix }}hoverxref.external').each(function () { $(this).removeAttr('title') });
 
-    $('.hoverxref.tooltip').tooltipster({
+    $('.{{ hoverxref_css_class_prefix }}hoverxref.{{ hoverxref_css_class_prefix }}tooltip').tooltipster({
         theme: {{ hoverxref_tooltip_theme }},
         interactive: {{ 'true' if hoverxref_tooltip_interactive else 'false' }},
         maxWidth: {{ hoverxref_tooltip_maxwidth }},
@@ -235,7 +235,7 @@ $(document).ready(function() {
     };
 
     var delay = {{ hoverxref_modal_hover_delay }}, setTimeoutConst;
-    $('.hoverxref.modal').hover(function(event) {
+    $('.{{ hoverxref_css_class_prefix }}hoverxref.{{ hoverxref_css_class_prefix }}modal').hover(function(event) {
         var element = $(this);
         console.debug('Event: ' + event + ' Element: ' + element);
         event.preventDefault();
diff --git a/hoverxref/domains.py b/hoverxref/domains.py
index 20e12c7..03360d7 100644
--- a/hoverxref/domains.py
+++ b/hoverxref/domains.py
@@ -14,14 +14,15 @@ class HoverXRefBaseDomain:
     )
 
     def _inject_hoverxref_data(self, env, refnode, typ):
-        classes = ['hoverxref']
+        from .extension import CSS_CLASSES, CSS_DEFAULT_CLASS
+
+        classes = [CSS_DEFAULT_CLASS]
         type_class = None
         if typ == 'hoverxreftooltip':
             type_class = 'tooltip'
-            classes.append(type_class)
         elif typ == 'hoverxrefmodal':
             type_class = 'modal'
-            classes.append(type_class)
+
         if not type_class:
             type_class = env.config.hoverxref_role_types.get(typ)
             if not type_class:
@@ -33,7 +34,9 @@ class HoverXRefBaseDomain:
                     default,
                     typ,
                 )
-            classes.append(type_class)
+
+        # Examples: hxr-tooltip, hxr-modal
+        classes.append(CSS_CLASSES[type_class])
 
         refnode.replace_attr('classes', classes)
         # TODO: log something else here, so we can unique identify this node
diff --git a/hoverxref/extension.py b/hoverxref/extension.py
index 0dc27af..a3ce46c 100644
--- a/hoverxref/extension.py
+++ b/hoverxref/extension.py
@@ -19,6 +19,12 @@ from .domains import (
 
 logger = logging.getLogger(__name__)
 
+CSS_CLASS_PREFIX = 'hxr-'
+CSS_DEFAULT_CLASS = f'{CSS_CLASS_PREFIX}hoverxref'
+CSS_CLASSES = {
+    'tooltip': f'{CSS_CLASS_PREFIX}tooltip',
+    'modal': f'{CSS_CLASS_PREFIX}modal',
+}
 
 HOVERXREF_ASSETS_FILES = [
     'js/hoverxref.js_t',  # ``_t`` tells Sphinx this is a template
@@ -27,7 +33,7 @@ HOVERXREF_ASSETS_FILES = [
 TOOLTIP_ASSETS_FILES = [
     # Tooltipster's Styles
     'js/tooltipster.bundle.min.js',
-    'css/tooltipster.custom.css',
+    'css/tooltipster.custom.css_t',
     'css/tooltipster.bundle.min.css',
 
     # Tooltipster's Themes
@@ -65,6 +71,7 @@ def copy_asset_files(app, exception):
                 # Then, add the values that the user overrides
                 context[attr] = getattr(app.config, attr)
 
+        context['hoverxref_css_class_prefix'] = CSS_CLASS_PREFIX
         context['http_hoverxref_version'] = __version__
 
         # Finally, add some non-hoverxref extra configs
@@ -73,10 +80,13 @@ def copy_asset_files(app, exception):
             context[attr] = getattr(app.config, attr)
 
         for f in ASSETS_FILES:
+            # Example: "./_static/js/hoverxref.js_t"
             path = os.path.join(os.path.dirname(__file__), '_static', f)
+            # Example: "<app.outdir>/_static/css" or "<app.outdir>/_static/js"
+            output = os.path.join(app.outdir, '_static', f.split('/')[0])
             copy_asset(
                 path,
-                os.path.join(app.outdir, '_static', f.split('.')[-1].replace('js_t', 'js')),
+                output,
                 context=context,
             )
 
@@ -264,7 +274,7 @@ def missing_reference(app, env, node, contnode):
         hoverxref_type = hoverxref_type or app.config.hoverxref_default_type
 
         classes = newnode.get('classes')
-        classes.extend(['hoverxref', hoverxref_type])
+        classes.extend([CSS_DEFAULT_CLASS, CSS_CLASSES[hoverxref_type]])
         newnode.replace_attr('classes', classes)
 
     return newnode
@@ -374,11 +384,13 @@ def setup(app):
 
     app.connect('missing-reference', missing_reference)
 
+    # Include all assets previously copied/rendered by ``copy_asset_files`` as
+    # Javascript and CSS files into the Sphinx application
     for f in ASSETS_FILES:
         if f.endswith('.js') or f.endswith('.js_t'):
             app.add_js_file(f.replace('.js_t', '.js'))
-        if f.endswith('.css'):
-            app.add_css_file(f)
+        if f.endswith('.css') or f.endswith('.css_t'):
+            app.add_css_file(f.replace('.css_t', '.css'))
 
     return {
         'version': __version__,


Test patch:
diff --git a/tests/test_htmltag.py b/tests/test_htmltag.py
index 9097041..c89bcd2 100644
--- a/tests/test_htmltag.py
+++ b/tests/test_htmltag.py
@@ -20,7 +20,7 @@ def test_default_settings(app, status, warning):
 
     chunks = [
         '<a class="reference internal" href="chapter-i.html#chapter-i"><span class="std std-ref">This a :ref: to Chapter I</span></a>',
-        '<a class="hoverxref tooltip reference internal" href="chapter-i.html#section-i"><span class="std std-ref">This a :hoverxref: to Chapter I, Section I</span></a>',
+        '<a class="hxr-hoverxref hxr-tooltip reference internal" href="chapter-i.html#section-i"><span class="std std-ref">This a :hoverxref: to Chapter I, Section I</span></a>',
     ]
 
     for chunk in chunks:
@@ -75,7 +75,7 @@ def test_autosectionlabel_project_version_settings(app, status, warning):
 
     chunks = [
         '<a class="reference internal" href="chapter-i.html#chapter-i"><span class="std std-ref">This a :ref: to Chapter I</span></a>.',
-        '<a class="hoverxref tooltip reference internal" href="chapter-i.html#chapter-i"><span class="std std-ref">This a :hoverxref: to Chapter I</span></a>',
+        '<a class="hxr-hoverxref hxr-tooltip reference internal" href="chapter-i.html#chapter-i"><span class="std std-ref">This a :hoverxref: to Chapter I</span></a>',
     ]
 
     for chunk in chunks:
@@ -93,9 +93,9 @@ def test_custom_object(app, status, warning):
     content = open(path).read()
 
     chunks = [
-        '<a class="hoverxref tooltip reference internal" href="configuration.html#confval-conf-title"><code class="xref std std-confval docutils literal notranslate"><span class="pre">This</span> <span class="pre">is</span> <span class="pre">a</span> <span class="pre">:confval:</span> <span class="pre">to</span> <span class="pre">conf-title</span></code></a>',
-        '<a class="hoverxref tooltip reference internal" href="configuration.html#configuration"><span class="std std-ref">This is a :hoverxref: to Configuration document</span></a>',
-        '<a class="hoverxref tooltip reference internal" href="code.html#python-code-block"><span class="std std-numref">This is a :numref: to a Python code block (PyExample)</span></a>'
+        '<a class="hxr-hoverxref hxr-tooltip reference internal" href="configuration.html#confval-conf-title"><code class="xref std std-confval docutils literal notranslate"><span class="pre">This</span> <span class="pre">is</span> <span class="pre">a</span> <span class="pre">:confval:</span> <span class="pre">to</span> <span class="pre">conf-title</span></code></a>',
+        '<a class="hxr-hoverxref hxr-tooltip reference internal" href="configuration.html#configuration"><span class="std std-ref">This is a :hoverxref: to Configuration document</span></a>',
+        '<a class="hxr-hoverxref hxr-tooltip reference internal" href="code.html#python-code-block"><span class="std std-numref">This is a :numref: to a Python code block (PyExample)</span></a>'
     ]
 
     for chunk in chunks:
@@ -115,9 +115,9 @@ def test_python_domain(app, status, warning):
     content = open(path).read()
 
     chunks = [
-        '<a class="hoverxref tooltip reference internal" href="api.html#hoverxref.extension.HoverXRefStandardDomainMixin" title="hoverxref.extension.HoverXRefStandardDomainMixin"><code class="xref py py-class docutils literal notranslate"><span class="pre">This</span> <span class="pre">is</span> <span class="pre">a</span> <span class="pre">:py:class:</span> <span class="pre">role</span> <span class="pre">to</span> <span class="pre">a</span> <span class="pre">Python</span> <span class="pre">object</span></code></a>',
-        '<a class="hoverxref tooltip reference internal" href="api.html#module-hoverxref.extension" title="hoverxref.extension"><code class="xref py py-mod docutils literal notranslate"><span class="pre">hoverxref.extension</span></code></a>',
-        '<a class="hoverxref tooltip reference internal" href="api.html#hoverxref.extension.setup" title="hoverxref.extension.setup"><code class="xref py py-func docutils literal notranslate"><span class="pre">hoverxref.extension.setup()</span></code></a>',
+        '<a class="hxr-hoverxref hxr-tooltip reference internal" href="api.html#hoverxref.extension.HoverXRefStandardDomainMixin" title="hoverxref.extension.HoverXRefStandardDomainMixin"><code class="xref py py-class docutils literal notranslate"><span class="pre">This</span> <span class="pre">is</span> <span class="pre">a</span> <span class="pre">:py:class:</span> <span class="pre">role</span> <span class="pre">to</span> <span class="pre">a</span> <span class="pre">Python</span> <span class="pre">object</span></code></a>',
+        '<a class="hxr-hoverxref hxr-tooltip reference internal" href="api.html#module-hoverxref.extension" title="hoverxref.extension"><code class="xref py py-mod docutils literal notranslate"><span class="pre">hoverxref.extension</span></code></a>',
+        '<a class="hxr-hoverxref hxr-tooltip reference internal" href="api.html#hoverxref.extension.setup" title="hoverxref.extension.setup"><code class="xref py py-func docutils literal notranslate"><span class="pre">hoverxref.extension.setup()</span></code></a>',
         '<code class="xref py py-const docutils literal notranslate"><span class="pre">Constant</span></code>',
     ]
 
@@ -146,9 +146,9 @@ def test_python_domain_intersphinx(app, status, warning):
     content = open(path).read()
 
     chunks = [
-        '<a class="hoverxref tooltip reference internal" href="api.html#hoverxref.extension.HoverXRefStandardDomainMixin" title="hoverxref.extension.HoverXRefStandardDomainMixin"><code class="xref py py-class docutils literal notranslate"><span class="pre">This</span> <span class="pre">is</span> <span class="pre">a</span> <span class="pre">:py:class:</span> <span class="pre">role</span> <span class="pre">to</span> <span class="pre">a</span> <span class="pre">Python</span> <span class="pre">object</span></code></a>',
-        '<a class="hoverxref tooltip reference internal" href="api.html#module-hoverxref.extension" title="hoverxref.extension"><code class="xref py py-mod docutils literal notranslate"><span class="pre">hoverxref.extension</span></code></a>',
-        '<a class="hoverxref tooltip reference internal" href="api.html#hoverxref.extension.setup" title="hoverxref.extension.setup"><code class="xref py py-func docutils literal notranslate"><span class="pre">hoverxref.extension.setup()</span></code></a>',
+        '<a class="hxr-hoverxref hxr-tooltip reference internal" href="api.html#hoverxref.extension.HoverXRefStandardDomainMixin" title="hoverxref.extension.HoverXRefStandardDomainMixin"><code class="xref py py-class docutils literal notranslate"><span class="pre">This</span> <span class="pre">is</span> <span class="pre">a</span> <span class="pre">:py:class:</span> <span class="pre">role</span> <span class="pre">to</span> <span class="pre">a</span> <span class="pre">Python</span> <span class="pre">object</span></code></a>',
+        '<a class="hxr-hoverxref hxr-tooltip reference internal" href="api.html#module-hoverxref.extension" title="hoverxref.extension"><code class="xref py py-mod docutils literal notranslate"><span class="pre">hoverxref.extension</span></code></a>',
+        '<a class="hxr-hoverxref hxr-tooltip reference internal" href="api.html#hoverxref.extension.setup" title="hoverxref.extension.setup"><code class="xref py py-func docutils literal notranslate"><span class="pre">hoverxref.extension.setup()</span></code></a>',
         '<code class="xref py py-const docutils literal notranslate"><span class="pre">Constant</span></code>',
     ]
 
@@ -173,7 +173,7 @@ def test_bibtex_domain(app, status, warning):
     content = open(path).read()
 
     chunks = [
-        '<p>See <span id="id1">Nelson [<a class="hoverxref tooltip reference internal" href="#id4" title="Edward Nelson. Radically Elementary Probability Theory. Princeton University Press, 1987.">Nel87</a>]</span> for an introduction to non-standard analysis.\nNon-standard analysis is fun <span id="id2">[<a class="hoverxref tooltip reference internal" href="#id4" title="Edward Nelson. Radically Elementary Probability Theory. Princeton University Press, 1987.">Nel87</a>]</span>.</p>',
+        '<p>See <span id="id1">Nelson [<a class="hxr-hoverxref hxr-tooltip reference internal" href="#id4" title="Edward Nelson. Radically Elementary Probability Theory. Princeton University Press, 1987.">Nel87</a>]</span> for an introduction to non-standard analysis.\nNon-standard analysis is fun <span id="id2">[<a class="hxr-hoverxref hxr-tooltip reference internal" href="#id4" title="Edward Nelson. Radically Elementary Probability Theory. Princeton University Press, 1987.">Nel87</a>]</span>.</p>',
     ]
 
     for chunk in chunks:
@@ -193,7 +193,7 @@ def test_glossary_term_domain(app, status, warning):
     content = open(path).read()
 
     chunks = [
-        '<p>See definition <a class="hoverxref tooltip reference internal" href="#term-builder"><span class="xref std std-term">builder</span></a> for more information.</p>',
+        '<p>See definition <a class="hxr-hoverxref hxr-tooltip reference internal" href="#term-builder"><span class="xref std std-term">builder</span></a> for more information.</p>',
     ]
 
     for chunk in chunks:
@@ -214,7 +214,7 @@ def test_default_type(app, status, warning):
 
     chunks = [
         '<a class="reference internal" href="chapter-i.html#chapter-i"><span class="std std-ref">This a :ref: to Chapter I</span></a>',
-        '<a class="hoverxref modal reference internal" href="chapter-i.html#section-i"><span class="std std-ref">This a :hoverxref: to Chapter I, Section I</span></a>',
+        '<a class="hxr-hoverxref hxr-modal reference internal" href="chapter-i.html#section-i"><span class="std std-ref">This a :hoverxref: to Chapter I, Section I</span></a>',
     ]
 
     for chunk in chunks:
@@ -244,7 +244,7 @@ def test_ignore_refs(app, status, warning):
         assert chunk in content
 
     ignored_chunks = [
-        '<a class="hoverxref reference internal" href="chapter-i.html#section-i"><span class="std std-ref">This a :hoverxref: to Chapter I, Section I</span></a>',
+        '<a class="hxr-hoverxref reference internal" href="chapter-i.html#section-i"><span class="std std-ref">This a :hoverxref: to Chapter I, Section I</span></a>',
     ]
     for chunk in ignored_chunks:
         assert chunk not in content
@@ -303,9 +303,9 @@ def test_intersphinx_python_mapping(app, status, warning):
 
     chunks_regex = [
         # Python's links do have hoverxref enabled
-        r'<a class="hoverxref tooltip reference external" href="https://docs.python.org/3/tutorial/index.html#tutorial-index" title="\(in Python v3.\d\d?\)"><span class="xref std std-ref">This a :ref: to The Python Tutorial using intersphinx</span></a>',
-        r'<a class="hoverxref tooltip reference external" href="https://docs.python.org/3/library/datetime.html#datetime-datetime" title="\(in Python v3.\d\d?\)"><span class="xref std std-ref">This a :ref: to datetime.datetime Python’s function using intersphinx</span></a>',
-        r'<a class="hoverxref tooltip reference external" href="https://docs.python.org/3/library/functions.html#float" title="\(in Python v3.\d\d?\)"><code class="xref py py-class docutils literal notranslate"><span class="pre">float</span></code></a>',
+        r'<a class="hxr-hoverxref hxr-tooltip reference external" href="https://docs.python.org/3/tutorial/index.html#tutorial-index" title="\(in Python v3.\d\d?\)"><span class="xref std std-ref">This a :ref: to The Python Tutorial using intersphinx</span></a>',
+        r'<a class="hxr-hoverxref hxr-tooltip reference external" href="https://docs.python.org/3/library/datetime.html#datetime-datetime" title="\(in Python v3.\d\d?\)"><span class="xref std std-ref">This a :ref: to datetime.datetime Python’s function using intersphinx</span></a>',
+        r'<a class="hxr-hoverxref hxr-tooltip reference external" href="https://docs.python.org/3/library/functions.html#float" title="\(in Python v3.\d\d?\)"><code class="xref py py-class docutils literal notranslate"><span class="pre">float</span></code></a>',
 
         # Read the Docs' link does not have hoverxref enabled
         r'<a class="reference external" href="https://docs.readthedocs.io/en/stable/config-file/v2.html#python" title="\(in Read the Docs user documentation v\d\d?.\d\d?.\d\d?\)"><span class="xref std std-ref">This a :ref: to Config File v2 Read the Docs’ page using intersphinx</span></a>',
@@ -349,21 +349,21 @@ def test_intersphinx_all_mappings(app, status, warning):
 
     chunks_regex = [
         # Python's links do have hoverxref enabled
-        r'<a class="hoverxref tooltip reference external" href="https://docs.python.org/3/tutorial/index.html#tutorial-index" title="\(in Python v3.\d\d?\)"><span class="xref std std-ref">This a :ref: to The Python Tutorial using intersphinx</span></a>',
-        r'<a class="hoverxref tooltip reference external" href="https://docs.python.org/3/library/datetime.html#datetime-datetime" title="\(in Python v3.\d\d?\)"><span class="xref std std-ref">This a :ref: to datetime.datetime Python’s function using intersphinx</span></a>',
-        r'<a class="hoverxref modal reference external" href="https://docs.python.org/3/library/functions.html#float" title="\(in Python v3.\d\d?\)"><code class="xref py py-class docutils literal notranslate"><span class="pre">float</span></code></a>',
+        r'<a class="hxr-hoverxref hxr-tooltip reference external" href="https://docs.python.org/3/tutorial/index.html#tutorial-index" title="\(in Python v3.\d\d?\)"><span class="xref std std-ref">This a :ref: to The Python Tutorial using intersphinx</span></a>',
+        r'<a class="hxr-hoverxref hxr-tooltip reference external" href="https://docs.python.org/3/library/datetime.html#datetime-datetime" title="\(in Python v3.\d\d?\)"><span class="xref std std-ref">This a :ref: to datetime.datetime Python’s function using intersphinx</span></a>',
+        r'<a class="hxr-hoverxref hxr-modal reference external" href="https://docs.python.org/3/library/functions.html#float" title="\(in Python v3.\d\d?\)"><code class="xref py py-class docutils literal notranslate"><span class="pre">float</span></code></a>',
 
         # Read the Docs' link does have hoverxref enabled
-        r'<a class="hoverxref modal reference external" href="https://docs.readthedocs.io/en/stable/config-file/v2.html#python" title="\(in Read the Docs user documentation v\d\d?.\d\d?.\d+\)"><span class="xref std std-ref">This a :ref: to Config File v2 Read the Docs’ page using intersphinx</span></a>',
+        r'<a class="hxr-hoverxref hxr-modal reference external" href="https://docs.readthedocs.io/en/stable/config-file/v2.html#python" title="\(in Read the Docs user documentation v\d\d?.\d\d?.\d+\)"><span class="xref std std-ref">This a :ref: to Config File v2 Read the Docs’ page using intersphinx</span></a>',
 
         # Using `default_role = 'obj'`
         # Note the difference with the same `float` line previouly. Here it uses `py-obj` instead of `py-class`.
-        '<a class="hoverxref tooltip reference external" href="https://docs.python.org/3/library/functions.html#float" title="\(in Python v3.\d\d?\)"><code class="xref py py-obj docutils literal notranslate"><span class="pre">float</span></code></a>'
+        r'<a class="hxr-hoverxref hxr-tooltip reference external" href="https://docs.python.org/3/library/functions.html#float" title="\(in Python v3.\d\d?\)"><code class="xref py py-obj docutils literal notranslate"><span class="pre">float</span></code></a>'
     ]
 
     chunks = [
         # Python domain's link does have hoverxref enabled
-        '<a class="hoverxref tooltip reference internal" href="#hoverxref.extension.setup" title="hoverxref.extension.setup"><code class="xref py py-func docutils literal notranslate"><span class="pre">hoverxref.extension.setup()</span></code></a>',
+        '<a class="hxr-hoverxref hxr-tooltip reference internal" href="#hoverxref.extension.setup" title="hoverxref.extension.setup"><code class="xref py py-func docutils literal notranslate"><span class="pre">hoverxref.extension.setup()</span></code></a>',
     ]
 
     for chunk in chunks: