Repository: sphinx-gallery/sphinx-gallery

Issue:
Thumbnail title regression since 0.11.0
#906 changed the way thumbnail views of galleries are created. It now exposes the rst formatting in the caption:

[Current](https://matplotlib.org/devdocs/tutorials/index.html#intermediate):

![image](https://user-images.githubusercontent.com/19879328/189995776-e11a23b2-5f03-4f9b-bf51-e8bc0d9076f4.png)

[Previous:](https://matplotlib.org/2.2.5/tutorials/index.html)

![grafik](https://user-images.githubusercontent.com/19879328/190082348-9c0e7231-f4b0-41ea-a7ac-4f069d9867db.png)
See also https://github.com/sphinx-gallery/sphinx-gallery/issues/998#issuecomment-1245912973

Discussion:
larsoner: Isn't regex fun? 😆 
mgeier: > As it's handled by `sphinx` I don't think I can customize its output, which is the reason why it is hidden.

But as a Sphinx extension you can generate arbitrary HTML, right?
Or you could create the appropriate `docutils` nodes which are then turned into the desired HTML.
I don't understand why you use the detour via reST markup, doesn't that just make it harder?
larsoner: > I don't understand why you use the detour via reST markup, doesn't that just make it harder?

One reason is that I think way more people understand and can work with reST markup than can work with docutils nodes. In this sense sticking with reST is perhaps more maintainable. But if someone with docutils expertise and experience can come up with a better and still maintainable/understandable solution that would be great!
alexisthual: I gave this PR some more thoughts and effort.

It's not trivial to replace links generated by `sphinx` directives with manually generated links. I think `sphinx` deduces at build time a relative path going from the generated file with a `ref` directive to the reference's target, which is hard to do in `_thumbnail_div()` (I think I could replace relative links with absolute ones if I had a simple way of assessing the documentation's base url, but I couldn't find this in `sphinx`).

Moreover, I'm afraid that changing all this HTML/CSS could eventually break of lot of things (especially if we don't generate the exact same URLs). I won't have that much bandwidth to work on this in the coming weeks, and I think @mgeier wanted this to be merged soon, but I feel like we need to get this right the first time or it could have dramatic effects haha :smile: 
larsoner: @alexisthual should we merge this as is to remove the redundancy for now at least then, or no?
alexisthual: So far, this PR:
- sanitizes thumbnail titles
- removes the `alt` in thumbnail images

I think it could use one last review, but otherwise it's probably good to go!
Changing the HTML/CSS structure of thumbnails would be useful, but I don't have the bandwidth now to test these changes thoroughly. I'm not saying it's not doable though, and maybe someone more used to `sphinx` could find a elegant solution!
mgeier: Thanks for the update @alexisthual!

> I think @mgeier wanted this to be merged soon

I'm not in a hurry at all. I prefer a good solution to a fast solution.

I agree that the current state of this PR is a nice incremental improvement, and as far as I'm concerned there's nothing speaking against merging it (except for my comment above about the documentation: https://github.com/sphinx-gallery/sphinx-gallery/pull/1005#discussion_r990822617).

However, I think the generated HTML is still awkward and bloated, and most of the CSS is for working around that awkwardness.

Regarding the implementation in `nbsphinx`, I don't think it makes sense for me to copy the awkward HTML and fetch CSS from Sphinx Gallery with all those work-arounds.
It would make more sense for me to generate good HTML myself and then manually include much less CSS in `nbsphinx` (which users can still override if they want).

So if Sphinx Gallery produces nice HTML and lean CSS, I'm happy to keep compatibility in `nbsphinx`, but if not, I think I'll switch to a custom HTML+CSS solution that won't be compatible with SG anymore.
mgeier: FYI: I'm currently working on https://github.com/spatialaudio/nbsphinx/pull/706, which uses custom HTML/CSS and does not use Sphinx Gallery anymore.

<details>
<summary>CSS comparison</summary>

Sphinx Gallery:

```css
.sphx-glr-thumbnails {
  width: 100%;
  margin: 0px 0px 20px 0px;

  /* align thumbnails on a grid */
  justify-content: space-between;
  display: grid;
  /* each grid column should be at least 160px (this will determine
  the actual number of columns) and then take as much of the
  remaining width as possible */
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: 15px;
}
.sphx-glr-thumbnails .toctree-wrapper {
  /* hide empty toctree divs added to the DOM
  by sphinx even though the toctree is hidden
  (they would fill grid places with empty divs) */
  display: none;
}
.sphx-glr-thumbcontainer {
  background: transparent;
  -moz-border-radius: 5px;
  -webkit-border-radius: 5px;
  border-radius: 5px;
  box-shadow: 0 0 10px var(--sg-thumb-box-shadow-color);

  /* useful to absolutely position link in div */
  position: relative;

  /* thumbnail width should include padding and borders
  and take all available space */
  box-sizing: border-box;
  width: 100%;
  padding: 10px;
  border: 1px solid transparent;

  /* align content in thumbnail */
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 7px;
}
.sphx-glr-thumbcontainer p {
  position: absolute;
  top: 0;
  left: 0;
}
.sphx-glr-thumbcontainer p,
.sphx-glr-thumbcontainer p a {
  /* link should cover the whole thumbnail div */
  width: 100%;
  height: 100%;
}
.sphx-glr-thumbcontainer p a span {
  /* text within link should be masked
  (we are just interested in the href) */
  display: none;
}
.sphx-glr-thumbcontainer:hover {
  border: 1px solid;
  border-color: var(--sg-thumb-hover-border);
  cursor: pointer;
}
.sphx-glr-thumbcontainer a.internal {
  bottom: 0;
  display: block;
  left: 0;
  box-sizing: border-box;
  padding: 150px 10px 0;
  position: absolute;
  right: 0;
  top: 0;
}
/* Next one is to avoid Sphinx traditional theme to cover all the
thumbnail with its default link Background color */
.sphx-glr-thumbcontainer a.internal:hover {
  background-color: transparent;
}

.sphx-glr-thumbcontainer p {
  margin: 0 0 0.1em 0;
}
.sphx-glr-thumbcontainer .figure {
  margin: 10px;
  width: 160px;
}
.sphx-glr-thumbcontainer img {
  display: inline;
  max-height: 112px;
  max-width: 160px;
}
.sphx-glr-thumbcontainer[tooltip]:hover:after {
  background: var(--sg-tooltip-background);
  -webkit-border-radius: 4px;
  -moz-border-radius: 4px;
  border-radius: 4px;
  color: var(--sg-tooltip-foreground);
  content: attr(tooltip);
  padding: 10px;
  z-index: 98;
  width: 100%;
  height: 100%;
  position: absolute;
  pointer-events: none;
  top: 0;
  box-sizing: border-box;
  overflow: hidden;
  backdrop-filter: blur(3px);
}
```

proposed for `nbsphinx` in https://github.com/spatialaudio/nbsphinx/pull/706:

```css
.nbsphinx-gallery {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
    gap: 10px;
    margin-top: 1em;
    margin-bottom: 1em;
}

.nbsphinx-gallery > a {
    padding: 5px;
    border: 1px dotted currentColor;
    border-radius: 2px;
    text-align: center;
}

.nbsphinx-gallery > a:hover {
    border-style: solid;
}

.nbsphinx-gallery img {
    max-width: 100%;
    max-height: 100%;
}

.nbsphinx-gallery > a > div:first-child {
    display: flex;
    align-items: start;
    justify-content: center;
    height: 120px;
    margin-bottom: 5px;
}
```

</details>
larsoner: Sounds like alt-as-blank is the way to go here since the contents are elsewhere / redundant for screen readers. I'll fix the conflict and mark for merge when green, then cut 0.12

Patch:
diff --git a/.gitignore b/.gitignore
index 2ca5414..5c35e97 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,7 @@
 __pycache__/
 *.py[cod]
 .coverage*
+.vscode
 
 # C extensions
 *.so
diff --git a/README.rst b/README.rst
index 5f868ad..ee844f3 100644
--- a/README.rst
+++ b/README.rst
@@ -25,40 +25,39 @@ HTML gallery of examples from any set of Python scripts.
 Who uses Sphinx-Gallery
 =======================
 
-* `Sphinx-Gallery <https://sphinx-gallery.github.io/stable/auto_examples/index.html>`_
+An incomplete list:
 
 .. projects_list_start
 
-* `Scikit-learn <http://scikit-learn.org/stable/auto_examples/index.html>`_
-* `Nilearn <https://nilearn.github.io/stable/auto_examples/index.html>`_
-* `MNE-python <https://mne.tools/stable/auto_examples/index.html>`_
-* `PyStruct <https://pystruct.github.io/auto_examples/index.html>`_
+* `Apache TVM <https://tvm.apache.org/docs/tutorial/index.html>`_
+* `Astropy <https://docs.astropy.org/en/stable/generated/examples/index.html>`_
+* `Biotite <https://www.biotite-python.org/examples/gallery/index.html>`_
+* `Auto-sklearn <https://automl.github.io/auto-sklearn/master/examples/index.html>`_
+* `Cartopy <https://scitools.org.uk/cartopy/docs/latest/gallery/>`_
+* `Fury <https://fury.gl/latest/auto_examples/index.html>`_
 * `GIMLi <https://www.pygimli.org/_examples_auto/index.html>`_
+* `Matplotlib <https://matplotlib.org/stable/index.html>`_:
+* `MNE-python <https://mne.tools/stable/auto_examples/index.html>`_
 * `Nestle <http://kylebarbary.com/nestle/examples/index.html>`_
+* `NetworkX <https://networkx.org/documentation/stable/auto_examples/index.html>`_
+* `Neuraxle <https://www.neuraxle.org/stable/examples/index.html>`_
+* `Nilearn <https://nilearn.github.io/stable/auto_examples/index.html>`_
+* `OpenML-Python <https://openml.github.io/openml-python/main/examples/index.html>`_
+* `Optuna <https://optuna.readthedocs.io/en/stable/tutorial/index.html>`_
+* `PlasmaPy <https://docs.plasmapy.org/en/latest/examples.html>`_
 * `pyRiemann <https://pyriemann.readthedocs.io/en/latest/index.html>`_
-* `scikit-image <https://scikit-image.org/docs/dev/auto_examples/>`_
-* `Astropy <https://docs.astropy.org/en/stable/generated/examples/index.html>`_
-* `SunPy <https://docs.sunpy.org/en/stable/generated/gallery/index.html>`_
+* `PyStruct <https://pystruct.github.io/auto_examples/index.html>`_
 * `PySurfer <https://pysurfer.github.io/>`_
-* `Matplotlib <https://matplotlib.org/stable/index.html>`_:
-  `Examples <https://matplotlib.org/stable/gallery/index.html>`_ and
-  `Tutorials <https://matplotlib.org/stable/tutorials/index.html>`_
 * `PyTorch tutorials <https://pytorch.org/tutorials>`_
-* `Cartopy <https://scitools.org.uk/cartopy/docs/latest/gallery/>`_
 * `PyVista <https://docs.pyvista.org/examples/>`_
+* `Radis <https://radis.readthedocs.io/en/latest/auto_examples/index.html>`_
+* `scikit-image <https://scikit-image.org/docs/dev/auto_examples/>`_
+* `Scikit-learn <http://scikit-learn.org/stable/auto_examples/index.html>`_
 * `SimPEG <https://docs.simpeg.xyz/content/examples/>`_
-* `PlasmaPy <https://docs.plasmapy.org/en/latest/examples.html>`_
-* `Fury <https://fury.gl/latest/auto_examples/index.html>`_
-* `NetworkX <https://networkx.org/documentation/stable/auto_examples/index.html>`_
-* `Optuna <https://optuna.readthedocs.io/en/stable/tutorial/index.html>`_
-* `Auto-sklearn <https://automl.github.io/auto-sklearn/master/examples/index.html>`_
-* `OpenML-Python <https://openml.github.io/openml-python/main/examples/index.html>`_
-* `TorchIO <https://torchio.readthedocs.io/auto_examples/index.html>`_
-* `Neuraxle <https://www.neuraxle.org/stable/examples/index.html>`_
-* `Biotite <https://www.biotite-python.org/examples/gallery/index.html>`_
-* `Apache TVM <https://tvm.apache.org/docs/tutorial/index.html>`_
+* `Sphinx-Gallery <https://sphinx-gallery.github.io/stable/auto_examples/index.html>`_
 * `Tonic <https://tonic.readthedocs.io/en/latest/auto_examples/index.html>`_
-* `Radis <https://radis.readthedocs.io/en/latest/auto_examples/index.html>`_
+* `TorchIO <https://torchio.readthedocs.io/auto_examples/index.html>`_
+* `SunPy <https://docs.sunpy.org/en/stable/generated/gallery/index.html>`_
 
 .. projects_list_end
 
@@ -87,8 +86,8 @@ the file sizes of the generated PNG files.
 
 .. installation-end-content
 
-Install as a Sphinx-Gallery developer
--------------------------------------
+Contributing
+============
 
 You can get the latest development source from our `Github repository
 <https://github.com/sphinx-gallery/sphinx-gallery>`_. You need
@@ -99,10 +98,17 @@ you can do:
 
     $ git clone https://github.com/sphinx-gallery/sphinx-gallery
     $ cd sphinx-gallery
-    $ pip install -r dev-requirements.txt
+    $ pip install -r requirements.txt -r dev-requirements.txt
+    $ conda install graphviz  # if using conda, you can get graphviz this way
     $ pip install -e .
 
 
+Check that you are all set by running:
+
+.. code-block:: console
+
+    $ pytest sphinx_gallery
+
 How to cite
 ===========
 
diff --git a/sphinx_gallery/backreferences.py b/sphinx_gallery/backreferences.py
index 01015e6..7085218 100644
--- a/sphinx_gallery/backreferences.py
+++ b/sphinx_gallery/backreferences.py
@@ -265,7 +265,7 @@ THUMBNAIL_TEMPLATE = """
 .. only:: html
 
   .. image:: /{thumbnail}
-    :alt: {title}
+    :alt:
 
   :ref:`sphx_glr_{ref_name}`
 
diff --git a/sphinx_gallery/gen_rst.py b/sphinx_gallery/gen_rst.py
index 24a5153..e0aea51 100644
--- a/sphinx_gallery/gen_rst.py
+++ b/sphinx_gallery/gen_rst.py
@@ -210,6 +210,17 @@ def _sanitize_rst(string):
     string = re.sub(p + r'`([^`]+)`' + e, r'\1\2\3', string)
     # `whatever thing` --> whatever thing
     string = re.sub(p + r'([^`]+)' + e, r'\1\2\3', string)
+
+    # **string** --> string
+    string = re.sub(r'\*\*([^\*]*)\*\*', r'\1', string)
+    # *string* --> string
+    string = re.sub(r'\*([^\*]*)\*', r'\1', string)
+    # `link text <url>`_ --> link text
+    string = re.sub(r'`([^`<>]+) <[^`<>]+>`\_\_?', r'\1', string)
+
+    # :anchor:`the term` --> the term
+    string = re.sub(r':[a-z]+:`([^`<>]+)( <[^`<>]+>)?`', r'\1', string)
+
     return string
 
 
@@ -244,6 +255,9 @@ def extract_intro_and_title(filename, docstring):
     intro = _sanitize_rst(intro)
     if len(intro) > 95:
         intro = intro[:95] + '...'
+
+    title = _sanitize_rst(title)
+
     return intro, title
 
 


Test patch:
diff --git a/sphinx_gallery/tests/test_backreferences.py b/sphinx_gallery/tests/test_backreferences.py
index 4aa8e44..ce5fe07 100644
--- a/sphinx_gallery/tests/test_backreferences.py
+++ b/sphinx_gallery/tests/test_backreferences.py
@@ -21,7 +21,7 @@ REFERENCE = r"""
 .. only:: html
 
   .. image:: /fake_dir/images/thumb/sphx_glr_test_file_thumb.png
-    :alt: test title
+    :alt:
 
   :ref:`sphx_glr_fake_dir_test_file.py`
 
diff --git a/sphinx_gallery/tests/test_full.py b/sphinx_gallery/tests/test_full.py
index 7c4f9fa..a90defc 100644
--- a/sphinx_gallery/tests/test_full.py
+++ b/sphinx_gallery/tests/test_full.py
@@ -866,19 +866,19 @@ def test_alt_text_thumbnail(sphinx_app):
     generated_examples_index = op.join(out_dir, 'auto_examples', 'index.html')
     with codecs.open(generated_examples_index, 'r', 'utf-8') as fid:
         html = fid.read()
-    assert 'alt="&quot;SVG&quot;:-`graphics_`"' in html
+    assert 'alt=""' in html
     # check backreferences thumbnail, html
     backref_html = op.join(out_dir, 'gen_modules',
                            'sphinx_gallery.backreferences.html')
     with codecs.open(backref_html, 'r', 'utf-8') as fid:
         html = fid.read()
-    assert 'alt="Link to other packages"' in html
+    assert 'alt=""' in html
     # check gallery index thumbnail, rst
     generated_examples_index = op.join(src_dir, 'auto_examples',
                                        'index.rst')
     with codecs.open(generated_examples_index, 'r', 'utf-8') as fid:
         rst = fid.read()
-    assert ':alt: Trivial module to provide a value for plot_numpy_matplotlib.py' in rst  # noqa: E501
+    assert ':alt:' in rst
 
 
 def test_backreference_labels(sphinx_app):
diff --git a/sphinx_gallery/tests/test_gen_rst.py b/sphinx_gallery/tests/test_gen_rst.py
index 46622e4..158cb14 100644
--- a/sphinx_gallery/tests/test_gen_rst.py
+++ b/sphinx_gallery/tests/test_gen_rst.py
@@ -295,7 +295,7 @@ def test_extract_intro_and_title():
     # Title with punctuation (gh-517)
     intro, title = sg.extract_intro_and_title('<string>',
                                               '    ------------\n"-`Header"-with:; `punct` mark\'s\n----------------')  # noqa: E501
-    assert title == '"-`Header"-with:; `punct` mark\'s'
+    assert title == '"-`Header"-with:; punct mark\'s'
 
     # Long intro paragraph gets shortened
     intro_paragraph = '\n'.join(['this is one line' for _ in range(10)])