Changeset 3495763
- Timestamp:
- 03/31/2026 03:32:12 PM (42 hours ago)
- Location:
- metro-tabs
- Files:
-
- 1 added
- 4 edited
-
assets/screenshot-6.png (added)
-
trunk/css/metrotabs-block.css (modified) (4 diffs)
-
trunk/js/metrotabs-block.js (modified) (6 diffs)
-
trunk/metro-tabs.php (modified) (2 diffs)
-
trunk/readme.txt (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
metro-tabs/trunk/css/metrotabs-block.css
r3481381 r3495763 8 8 9 9 /* Tab bar – discrete bar with light border */ 10 . simple-tabs-block ul {10 .metro-tabs-block ul { 11 11 margin: 2em 0; 12 12 gap: 0; … … 17 17 } 18 18 19 . simple-tabs-block ul li {19 .metro-tabs-block ul li { 20 20 list-style: none; 21 21 margin: 0; … … 24 24 25 25 /* Tab triggers – subtle padding, no heavy styling */ 26 . simple-tabs-block ul li .metrotabs-tab-trigger,27 . simple-tabs-block ul li a,28 . simple-tabs-block ul li a:link {26 .metro-tabs-block ul li .metrotabs-tab-trigger, 27 .metro-tabs-block ul li a, 28 .metro-tabs-block ul li a:link { 29 29 display: inline-flex; 30 30 font-size: 1rem; … … 44 44 } 45 45 46 . simple-tabs-block ul li .metrotabs-tab-trigger:hover,47 . simple-tabs-block ul li a:hover {46 .metro-tabs-block ul li .metrotabs-tab-trigger:hover, 47 .metro-tabs-block ul li a:hover { 48 48 color: #1d2327; 49 49 } 50 50 51 51 /* Active tab – discrete underline */ 52 . simple-tabs-block ul li .metrotabs-tab-trigger.active,53 . simple-tabs-block ul li a.active {52 .metro-tabs-block ul li .metrotabs-tab-trigger.active, 53 .metro-tabs-block ul li a.active { 54 54 color: #1d2327; 55 55 border-bottom-color: currentColor; 56 56 } 57 57 58 .simple-tabs-block ul li .metrotabs-tab-trigger, 59 .simple-tabs-block ul li a, 60 .simple-tabs-block.simple-tabs-block-collapsible 61 .simple-tabs-block-nav 62 li.simple-tabs-block-selected 58 /* Focus indicator for keyboard navigation */ 59 .metro-tabs-block ul li .metrotabs-tab-trigger:focus-visible { 60 outline: 2px solid #2271b1; 61 outline-offset: -2px; 62 border-radius: 2px; 63 } 64 65 /* Tab panel focus indicator */ 66 .metro-tabs-block .tab-pane:focus-visible { 67 outline: 2px solid #2271b1; 68 outline-offset: 2px; 69 border-radius: 2px; 70 } 71 72 .metro-tabs-block ul li .metrotabs-tab-trigger, 73 .metro-tabs-block ul li a, 74 .metro-tabs-block.metro-tabs-block-collapsible 75 .metro-tabs-block-nav 76 li.metro-tabs-block-selected 63 77 a { 64 78 cursor: pointer; 65 79 } 66 80 67 . simple-tabs-block .ui-tabs-hide {81 .metro-tabs-block .ui-tabs-hide { 68 82 display: none !important; 69 83 } 70 84 71 85 /* Tab panes */ 72 . simple-tabs-block .tab-pane {86 .metro-tabs-block .tab-pane { 73 87 padding: 1em 0 0; 74 88 } 75 89 76 . simple-tabs-block .ui-tabs-panel {90 .metro-tabs-block .ui-tabs-panel { 77 91 overflow: hidden; 78 92 background: #fff; -
metro-tabs/trunk/js/metrotabs-block.js
r3481381 r3495763 34 34 } 35 35 36 var simpleTabsBlock = document.createElement('div');37 simpleTabsBlock.className = 'simple-tabs-block';36 var metroTabsBlock = document.createElement('div'); 37 metroTabsBlock.className = 'metro-tabs-block'; 38 38 39 39 var tabUl = document.createElement('ul'); 40 tabUl.setAttribute('role', 'tablist'); 41 tabUl.setAttribute('aria-orientation', 'horizontal'); 42 40 43 var wrappersToRemove = []; 41 44 var slugs = []; 45 var tabIds = []; 42 46 43 47 for (var i = 0; i < tabItems.length; i++) { 44 48 var tabItem = tabItems[i]; 45 49 var slug = idPrefix + slugify(tabItem.textContent); 50 var tabId = slug + '-tab'; 46 51 slugs.push(slug); 52 tabIds.push(tabId); 47 53 48 54 var li = document.createElement('li'); 55 li.setAttribute('role', 'presentation'); 56 49 57 var btn = document.createElement('button'); 50 58 btn.type = 'button'; 51 59 btn.className = 'metrotabs-tab-trigger'; 52 btn.setAttribute('data-pane-id', slug); 60 btn.setAttribute('role', 'tab'); 61 btn.setAttribute('id', tabId); 62 btn.setAttribute('aria-selected', 'false'); 63 btn.setAttribute('aria-controls', slug); 64 btn.setAttribute('tabindex', '-1'); 53 65 btn.textContent = tabItem.textContent; 54 66 li.appendChild(btn); … … 59 71 } 60 72 61 simpleTabsBlock.appendChild(tabUl);73 metroTabsBlock.appendChild(tabUl); 62 74 63 75 for (var j = 0; j < tabContents.length; j++) { … … 66 78 div.id = slugs[j]; 67 79 div.className = 'tab-pane'; 80 div.setAttribute('role', 'tabpanel'); 81 div.setAttribute('aria-labelledby', tabIds[j]); 82 div.setAttribute('tabindex', '0'); 68 83 div.innerHTML = tabContent.innerHTML; 69 simpleTabsBlock.appendChild(div);84 metroTabsBlock.appendChild(div); 70 85 tabContent.parentNode.removeChild(tabContent); 71 86 } 72 87 73 container.insertBefore( simpleTabsBlock, container.firstChild);88 container.insertBefore(metroTabsBlock, container.firstChild); 74 89 75 90 wrappersToRemove.forEach(function (w) { … … 79 94 }); 80 95 81 init SimpleTabs(simpleTabsBlock);96 initMetroTabs(metroTabsBlock); 82 97 } 83 98 … … 89 104 } 90 105 91 function initSimpleTabs(container) { 92 var tabs = container.querySelectorAll('ul > li > .metrotabs-tab-trigger'); 93 var tabPanes = container.querySelectorAll('.tab-pane'); 106 function initMetroTabs(container) { 107 var tabs = Array.prototype.slice.call( 108 container.querySelectorAll('[role="tab"]') 109 ); 110 var tabPanes = container.querySelectorAll('[role="tabpanel"]'); 94 111 95 function activateTab(tab ) {112 function activateTab(tab, setFocus) { 96 113 if (!tab) return; 97 114 98 tabs.forEach(function (t) { t.classList.remove('active'); }); 115 // Deactivate all tabs 116 tabs.forEach(function (t) { 117 t.classList.remove('active'); 118 t.setAttribute('aria-selected', 'false'); 119 t.setAttribute('tabindex', '-1'); 120 }); 121 122 // Hide all panels 99 123 tabPanes.forEach(function (p) { p.style.display = 'none'; }); 100 124 125 // Activate the selected tab 101 126 tab.classList.add('active'); 102 var activePaneId = tab.getAttribute('data-pane-id'); 103 var activePane = activePaneId ? container.querySelector('#' + activePaneId) : null; 127 tab.setAttribute('aria-selected', 'true'); 128 tab.setAttribute('tabindex', '0'); 129 130 var activePaneId = tab.getAttribute('aria-controls'); 131 var activePane = activePaneId ? container.querySelector('#' + CSS.escape(activePaneId)) : null; 104 132 if (activePane) { 105 133 activePane.style.display = 'block'; 106 134 } 135 136 if (setFocus) { 137 tab.focus(); 138 } 107 139 } 108 140 141 // Click handler 109 142 tabs.forEach(function (tab) { 110 143 tab.addEventListener('click', function (event) { 111 144 event.preventDefault(); 112 activateTab(tab); 145 activateTab(tab, true); 146 }); 147 }); 148 149 // Keyboard navigation (WAI-ARIA Tabs pattern) 150 tabs.forEach(function (tab) { 151 tab.addEventListener('keydown', function (event) { 152 var key = event.key; 153 var index = tabs.indexOf(tab); 154 var newTab = null; 155 156 if (key === 'ArrowRight') { 157 newTab = tabs[(index + 1) % tabs.length]; 158 } else if (key === 'ArrowLeft') { 159 newTab = tabs[(index - 1 + tabs.length) % tabs.length]; 160 } else if (key === 'Home') { 161 newTab = tabs[0]; 162 } else if (key === 'End') { 163 newTab = tabs[tabs.length - 1]; 164 } 165 166 if (newTab) { 167 event.preventDefault(); 168 activateTab(newTab, true); 169 } 113 170 }); 114 171 }); … … 118 175 if (hash) { 119 176 var paneId = hash.substring(1); 120 var activeTab = container.querySelector(' ul > li > .metrotabs-tab-trigger[data-pane-id="' + paneId+ '"]');121 activateTab(activeTab );177 var activeTab = container.querySelector('[role="tab"][aria-controls="' + CSS.escape(paneId) + '"]'); 178 activateTab(activeTab, false); 122 179 } else if (tabs.length > 0) { 123 activateTab(tabs[0] );180 activateTab(tabs[0], false); 124 181 } 125 182 } -
metro-tabs/trunk/metro-tabs.php
r3481381 r3495763 4 4 * Plugin URI: https://getbutterfly.com/wordpress-plugins/metro-tabs/ 5 5 * Description: Add responsive tab blocks to posts and pages. Group multiple tabs, add any blocks inside each tab. No jQuery, no dependencies. 6 * Version: 1.1. 46 * Version: 1.1.5 7 7 * Author: Ciprian Popescu 8 8 * Author URI: https://getbutterfly.com/ … … 23 23 } 24 24 25 define( 'METRO_TABS_VERSION', '1.1. 4' );25 define( 'METRO_TABS_VERSION', '1.1.5' ); 26 26 define( 'METRO_TABS_PLUGIN_FILE', __FILE__ ); 27 27 define( 'METRO_TABS_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); -
metro-tabs/trunk/readme.txt
r3495739 r3495763 6 6 Requires at least: 6.0 7 7 Tested up to: 7.0 8 Stable tag: 1.1. 48 Stable tag: 1.1.5 9 9 License: GNU General Public License v3 or later 10 10 License URI: https://www.gnu.org/licenses/gpl-3.0.html … … 47 47 4. Back-end block editor with Tab Group and Tab Item blocks 48 48 5. Back-end block editor with Tab Group and Tab Item blocks 49 6. Source code demonstrating ARIA roles and keyboard navigation attributes 49 50 50 51 == Changelog == 52 53 = 1.1.5 = 54 * Add WAI-ARIA Tabs pattern (roles, states, and properties) 55 * Add keyboard navigation (Arrow keys, Home, End) with automatic activation 56 * Add roving tabindex and visible focus styles for keyboard users 57 * Rename .simple-tabs-block CSS class to .metro-tabs-block 51 58 52 59 = 1.1.4 =
Note: See TracChangeset
for help on using the changeset viewer.