Repository: Python-Markdown/markdown (python)

Issue:
Allow TOC element to be fully customizable
```
[TOC]
# Header 1
```

will currently generate the following HTML:

```
<div class="toc">
  <ul>
    <li><a href="#header-1">Header 1</a></li>
  </ul>
</div>
<h1 id="header-1">Header 1</h1>
```

The `div` element cannot be changed and so it is for the title `span` element and its `toctitle` class.

I suggest that other config parameters (`toc_tag`, `title_class`, `title_tag`) that defaults to their current defaults are introduced.

This would be very useful for producing semantic HTML, using specific HMTL elements.

For example to use the [details disclosure element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details) as toc, the extension could be called like so:

```
md = markdown.Markdown(extensions=[TocExtension(
    title='Summary',
    title_class="",
    title_tag="summary",
    toc_class="",
    toc_tag="details"
)]]
```

```
<details>
<summary>Summary</summary>
<ul>
<li><a href="#header">Header</a></li>
</ul>
</details>
<h1 id="header">Header</h1>
```

This will render an initially closed details element with a summary:

--- 

<details>
<summary>Summary</summary>
<ul>
<li><a href="#header">Header</a></li>
</ul>
</details>
<h1 id="header">Header</h1>

Pull request description:
Fix #1293 Add customizable TOC title class

Patch:
diff --git a/.spell-dict b/.spell-dict
index 8719883..91cd7bd 100644
--- a/.spell-dict
+++ b/.spell-dict
@@ -28,6 +28,7 @@ Cogumbreiro
 CommonMark
 convertFile
 CSS
+customizable
 dedent
 deliminators
 deregister
diff --git a/docs/changelog.md b/docs/changelog.md
index 5d4e800..d233abf 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -25,7 +25,8 @@ links to the header, placing them after all other header content.
 * Add support for cPython version 3.12 (and PyPy 3.10) and drop support for
   Python version 3.7 (#1357).
 * Refactor changelog to use the format defined at <https://keepachangelog.com/>.
-* Updated the list of empty HTML tags (#1353).
+* Update the list of empty HTML tags (#1353).
+* Add customizable TOC title class to TOC extension (#1293).
 
 ## [3.4.4] -- 2023-07-25
 
diff --git a/docs/extensions/toc.md b/docs/extensions/toc.md
index f8d7479..1f80c7e 100644
--- a/docs/extensions/toc.md
+++ b/docs/extensions/toc.md
@@ -151,6 +151,9 @@ The following options are provided to configure the output:
 * **`title`**:
     Title to insert in the Table of Contents' `<div>`. Defaults to `None`.
 
+* **`title_class`**:
+    CSS class used for the title contained in the Table of Contents. Defaults to `toctitle`.
+
 * **`toc_class`**:
     CSS class(es) used for the `<div>` containing the Table of Contents. Defaults to `toc`.
 
diff --git a/markdown/extensions/toc.py b/markdown/extensions/toc.py
index 33746d5..6d080de 100644
--- a/markdown/extensions/toc.py
+++ b/markdown/extensions/toc.py
@@ -161,6 +161,7 @@ class TocTreeprocessor(Treeprocessor):
         self.slugify = config["slugify"]
         self.sep = config["separator"]
         self.toc_class = config["toc_class"]
+        self.title_class = config["title_class"]
         self.use_anchors = parseBoolValue(config["anchorlink"])
         self.anchorlink_class = config["anchorlink_class"]
         self.use_permalinks = parseBoolValue(config["permalink"], False)
@@ -251,7 +252,8 @@ class TocTreeprocessor(Treeprocessor):
         # Add title to the div
         if self.title:
             header = etree.SubElement(div, "span")
-            header.attrib["class"] = "toctitle"
+            if self.title_class:
+                header.attrib["class"] = self.title_class
             header.text = self.title
 
         def build_etree_ul(toc_list, parent):
@@ -335,6 +337,9 @@ class TocExtension(Extension):
             "title": ["",
                       "Title to insert into TOC <div> - "
                       "Defaults to an empty string"],
+            "title_class": ['toctitle',
+                            'CSS class used for the title. '
+                            'Defaults to "toctitle"'],
             "toc_class": ['toc',
                           'CSS class(es) used for the link. '
                           'Defaults to "toclink"'],


Test patch:
diff --git a/tests/test_syntax/extensions/test_toc.py b/tests/test_syntax/extensions/test_toc.py
index d64c6d3..7976436 100644
--- a/tests/test_syntax/extensions/test_toc.py
+++ b/tests/test_syntax/extensions/test_toc.py
@@ -629,3 +629,45 @@ class TestTOC(TestCase):
             ),
             extensions=[TocExtension(toc_class="custom1 custom2")]
         )
+
+    def testTOCWithEmptyTitleClass(self):
+
+        self.assertMarkdownRenders(
+            self.dedent(
+                '''
+                [TOC]
+                # Header
+                '''
+            ),
+            self.dedent(
+                '''
+                <div class="toc"><span>ToC</span><ul>
+                <li><a href="#header">Header</a></li>
+                </ul>
+                </div>
+                <h1 id="header">Header</h1>
+                '''
+            ),
+            extensions=[TocExtension(title_class="", title='ToC')]
+        )
+
+    def testTOCWithCustomTitleClass(self):
+
+        self.assertMarkdownRenders(
+            self.dedent(
+                '''
+                [TOC]
+                # Header
+                '''
+            ),
+            self.dedent(
+                '''
+                <div class="toc"><span class="tocname">ToC</span><ul>
+                <li><a href="#header">Header</a></li>
+                </ul>
+                </div>
+                <h1 id="header">Header</h1>
+                '''
+            ),
+            extensions=[TocExtension(title_class="tocname", title='ToC')]
+        )