Repository: facelessuser/pymdown-extensions

Issue:
Ability to focus on a given tab in a tab set.
### Description

Currently, there is no way to focus on a given tab (among tabs). The first tab always gets the focus. It would be nice if there was a way to put focus on a specific tab.

Relevant `mkdocs-material` discussion https://github.com/squidfunk/mkdocs-material/discussions/3982

### Benefits

One use case I have is described here https://github.com/squidfunk/mkdocs-material/discussions/3982#discussioncomment-2881853

### Solution Idea

One possible solution using `===+` is described here https://github.com/squidfunk/mkdocs-material/discussions/3982#discussioncomment-2881832

Discussion:
facelessuser: Need to figure out what to do in docs as we wanted to use `content.tabs.link` in our docs, but we can't demonstrate tab selection and demonstrate tab linking at the same time due to the current implementation.
facelessuser: @gir-bot lgtm

Patch:
diff --git a/docs/src/markdown/about/changelog.md b/docs/src/markdown/about/changelog.md
index 61d28262..9a54fffd 100644
--- a/docs/src/markdown/about/changelog.md
+++ b/docs/src/markdown/about/changelog.md
@@ -1,5 +1,9 @@
 # Changelog
 
+## 9.7
+
+- **NEW**: Tabbed: Add new syntax to allow forcing a specific tab to be select by default.
+
 ## 9.6
 
 - **NEW**: Highlight: Allow greater granularity of specifying where language guessing takes place via `guess_lang`
diff --git a/docs/src/markdown/extensions/tabbed.md b/docs/src/markdown/extensions/tabbed.md
index dbf2d33a..2ee39e39 100644
--- a/docs/src/markdown/extensions/tabbed.md
+++ b/docs/src/markdown/extensions/tabbed.md
@@ -96,6 +96,47 @@ of a new tab set with `!`.
             ```
         ```
 
+## Tab Select
+
+If you'd like to force a tab to be selected, simply use `===+`, otherwise, the first tab will be selected by default.
+
+!!! example "Tab Select"
+
+    === "Output"
+        === "Not Me"
+            Markdown **content**.
+
+            Multiple paragraphs.
+
+        ===+ "Select Me"
+            More Markdown **content**.
+
+            - list item a
+            - list item b
+
+        === "Not Me Either"
+            Another Tab
+
+    === "Markdown"
+        ```
+        === "Not Me"
+            Markdown **content**.
+
+            Multiple paragraphs.
+
+        ===+ "Select Me"
+            More Markdown **content**.
+
+            - list item a
+            - list item b
+
+        === "Not Me Either"
+            Another Tab
+        ```
+
+!!! new "New 9.5"
+    Tab select syntax with `+` was introduced in 9.5.
+
 ## Tab IDs
 
 By default, tabs generate IDs for each tab using the following template `__tabbed_<tab_set_number>_<tab_number>`. If it
@@ -262,6 +303,13 @@ const tabSync = () => {
 }
 ```
 
+!!! tip "Special Considerations"
+    If you are combining this feature with something like ["tab select"](#tab-select), you may have to not link tabs on
+    initial page load to ensure the default is not overridden. The example above does not initiate tab linking on
+    initial page load, only registration of the event. Some may attempt to implement tab linkage such that selected tabs
+    are remembered across pages, special consideration would be required in such situations and is beyond the scope of
+    this simple example.
+
 ## Alternate Style
 
 !!! new "New 9.0"
diff --git a/pymdownx/__meta__.py b/pymdownx/__meta__.py
index 5f4a36fd..bc0bf532 100644
--- a/pymdownx/__meta__.py
+++ b/pymdownx/__meta__.py
@@ -185,5 +185,5 @@ def parse_version(ver, pre=False):
     return Version(major, minor, micro, release, pre, post, dev)
 
 
-__version_info__ = Version(9, 6, 0, "final")
+__version_info__ = Version(9, 7, 0, "final")
 __version__ = __version_info__._get_canonical()
diff --git a/pymdownx/tabbed.py b/pymdownx/tabbed.py
index dfff9314..5db214d2 100644
--- a/pymdownx/tabbed.py
+++ b/pymdownx/tabbed.py
@@ -33,7 +33,7 @@ class TabbedProcessor(BlockProcessor):
     """Tabbed block processor."""
 
     START = re.compile(
-        r'(?:^|\n)={3}(!)? +"(.*?)" *(?:\n|$)'
+        r'(?:^|\n)={3}(\+|\+!|!\+|!)? +"(.*?)" *(?:\n|$)'
     )
     COMPRESS_SPACES = re.compile(r' {2,}')
 
@@ -176,7 +176,7 @@ class TabbedProcessor(BlockProcessor):
             if (
                 sibling and sibling.tag.lower() == 'div' and
                 sibling.attrib.get('class', '') == tabbed_set and
-                special != '!'
+                '!' not in special
             ):
                 first = False
                 sfences = sibling
@@ -221,8 +221,13 @@ class TabbedProcessor(BlockProcessor):
             if not self.slugify:
                 attributes['id'] = "__tabbed_%d_%d" % (tab_set, tab_count)
 
-            if first:
+            if first or '+' in special:
                 attributes['checked'] = 'checked'
+                # Remove any previously assigned "checked states" to siblings
+                for i in sfences.findall('input'):
+                    if i.attrib.get('name', '') == '__tabbed_{}'.format(tab_set):
+                        if 'checked' in i.attrib:
+                            del i.attrib['checked']
 
             attributes2 = {"for": "__tabbed_%d_%d" % (tab_set, tab_count)} if not self.slugify else {}
 


Test patch:
diff --git a/tests/test_extensions/test_tabbed.py b/tests/test_extensions/test_tabbed.py
index 3f925320..f1bdd95f 100644
--- a/tests/test_extensions/test_tabbed.py
+++ b/tests/test_extensions/test_tabbed.py
@@ -65,7 +65,7 @@ class TestLegacyTab(util.MdCase):
         """Test tabbed."""
 
         self.check_markdown(
-            r'''
+            R'''
             === "**Tab**"
                 Some *content*
 
@@ -202,6 +202,64 @@ class TestLegacyTab(util.MdCase):
             True
         )
 
+    def test_tabbed_select(self):
+        """Test selecting a tab."""
+
+        self.check_markdown(
+            r'''
+            === "Tab 1"
+                content
+
+            ===+ "Tab 2"
+                content
+
+            === "Tab 3"
+                content
+            ''',
+            r'''
+            <div class="tabbed-set" data-tabs="1:3"><input id="__tabbed_1_1" name="__tabbed_1" type="radio" /><label for="__tabbed_1_1">Tab 1</label><div class="tabbed-content">
+            <p>content</p>
+            </div>
+            <input checked="checked" id="__tabbed_1_2" name="__tabbed_1" type="radio" /><label for="__tabbed_1_2">Tab 2</label><div class="tabbed-content">
+            <p>content</p>
+            </div>
+            <input id="__tabbed_1_3" name="__tabbed_1" type="radio" /><label for="__tabbed_1_3">Tab 3</label><div class="tabbed-content">
+            <p>content</p>
+            </div>
+            </div>
+            ''',  # noqa: E501
+            True
+        )
+
+    def test_tabbed_select_mulitiple(self):
+        """Test selecting multiple tabs."""
+
+        self.check_markdown(
+            r'''
+            === "Tab 1"
+                content
+
+            ===+ "Tab 2"
+                content
+
+            ===+ "Tab 3"
+                content
+            ''',
+            r'''
+            <div class="tabbed-set" data-tabs="1:3"><input id="__tabbed_1_1" name="__tabbed_1" type="radio" /><label for="__tabbed_1_1">Tab 1</label><div class="tabbed-content">
+            <p>content</p>
+            </div>
+            <input id="__tabbed_1_2" name="__tabbed_1" type="radio" /><label for="__tabbed_1_2">Tab 2</label><div class="tabbed-content">
+            <p>content</p>
+            </div>
+            <input checked="checked" id="__tabbed_1_3" name="__tabbed_1" type="radio" /><label for="__tabbed_1_3">Tab 3</label><div class="tabbed-content">
+            <p>content</p>
+            </div>
+            </div>
+            ''',  # noqa: E501
+            True
+        )
+
     def test_with_lists(self):
         """Test with lists."""
 
diff --git a/tests/test_extensions/test_tabbed_alternate.py b/tests/test_extensions/test_tabbed_alternate.py
index 28e2f861..8a2f51aa 100644
--- a/tests/test_extensions/test_tabbed_alternate.py
+++ b/tests/test_extensions/test_tabbed_alternate.py
@@ -226,6 +226,70 @@ class TestLegacyTab(util.MdCase):
             True
         )
 
+    def test_tabbed_select(self):
+        """Test selecting a tab."""
+
+        self.check_markdown(
+            r'''
+            === "Tab 1"
+                content
+
+            ===+ "Tab 2"
+                content
+
+            === "Tab 3"
+                content
+            ''',
+            r'''
+            <div class="tabbed-set tabbed-alternate" data-tabs="1:3"><input id="__tabbed_1_1" name="__tabbed_1" type="radio" /><input checked="checked" id="__tabbed_1_2" name="__tabbed_1" type="radio" /><input id="__tabbed_1_3" name="__tabbed_1" type="radio" /><div class="tabbed-labels"><label for="__tabbed_1_1">Tab 1</label><label for="__tabbed_1_2">Tab 2</label><label for="__tabbed_1_3">Tab 3</label></div>
+            <div class="tabbed-content">
+            <div class="tabbed-block">
+            <p>content</p>
+            </div>
+            <div class="tabbed-block">
+            <p>content</p>
+            </div>
+            <div class="tabbed-block">
+            <p>content</p>
+            </div>
+            </div>
+            </div>
+            ''',  # noqa: E501
+            True
+        )
+
+    def test_tabbed_select_mulitiple(self):
+        """Test selecting multiple tabs."""
+
+        self.check_markdown(
+            r'''
+            === "Tab 1"
+                content
+
+            ===+ "Tab 2"
+                content
+
+            ===+ "Tab 3"
+                content
+            ''',
+            r'''
+            <div class="tabbed-set tabbed-alternate" data-tabs="1:3"><input id="__tabbed_1_1" name="__tabbed_1" type="radio" /><input id="__tabbed_1_2" name="__tabbed_1" type="radio" /><input checked="checked" id="__tabbed_1_3" name="__tabbed_1" type="radio" /><div class="tabbed-labels"><label for="__tabbed_1_1">Tab 1</label><label for="__tabbed_1_2">Tab 2</label><label for="__tabbed_1_3">Tab 3</label></div>
+            <div class="tabbed-content">
+            <div class="tabbed-block">
+            <p>content</p>
+            </div>
+            <div class="tabbed-block">
+            <p>content</p>
+            </div>
+            <div class="tabbed-block">
+            <p>content</p>
+            </div>
+            </div>
+            </div>
+            ''',  # noqa: E501
+            True
+        )
+
     def test_with_lists(self):
         """Test with lists."""