diff --git a/build/plotcss.js b/build/plotcss.js index 13c5570ae91..9f3de014eb4 100644 --- a/build/plotcss.js +++ b/build/plotcss.js @@ -34,9 +34,12 @@ var rules = { "X .ease-bg": "-webkit-transition:background-color .3s ease 0s;-moz-transition:background-color .3s ease 0s;-ms-transition:background-color .3s ease 0s;-o-transition:background-color .3s ease 0s;transition:background-color .3s ease 0s;", "X .modebar--hover>:not(.watermark)": "opacity:0;-webkit-transition:opacity .3s ease 0s;-moz-transition:opacity .3s ease 0s;-ms-transition:opacity .3s ease 0s;-o-transition:opacity .3s ease 0s;transition:opacity .3s ease 0s;", "X:hover .modebar--hover .modebar-group": "opacity:1;", + "X:focus-within .modebar--hover .modebar-group": "opacity:1;", "X .modebar-group": "float:left;display:inline-block;box-sizing:border-box;padding-left:8px;position:relative;vertical-align:middle;white-space:nowrap;", - "X .modebar-btn": "position:relative;font-size:16px;padding:3px 4px;height:22px;cursor:pointer;line-height:normal;box-sizing:border-box;", - "X .modebar-btn svg": "position:relative;top:2px;", + "X .modebar-group a": "display:grid;place-content:center;", + "X .modebar-btn": "position:relative;font-size:16px;padding:3px 4px;height:22px;cursor:pointer;line-height:normal;box-sizing:border-box;border:none;background:rgba(0,0,0,0);", + "X .modebar-btn svg": "position:relative;", + "X .modebar-btn:focus-visible": "outline:1px solid #000;outline-offset:1px;border-radius:3px;", "X .modebar.vertical": "display:flex;flex-direction:column;flex-wrap:wrap;align-content:flex-end;max-height:100%;", "X .modebar.vertical svg": "top:-1px;", "X .modebar.vertical .modebar-group": "display:block;float:none;padding-left:0px;padding-bottom:8px;", diff --git a/draftlogs/7492_add.md b/draftlogs/7492_add.md new file mode 100644 index 00000000000..73c2540552a --- /dev/null +++ b/draftlogs/7492_add.md @@ -0,0 +1 @@ +- Make modebar keyboard-accessible via tabbing [[#7492](https://github.com/plotly/plotly.js/pull/7492)], with thanks to @davidangarita1 for the contribution! diff --git a/src/components/modebar/modebar.js b/src/components/modebar/modebar.js index f51bcd40667..4a2ce19ffcc 100644 --- a/src/components/modebar/modebar.js +++ b/src/components/modebar/modebar.js @@ -43,9 +43,10 @@ proto.update = function(graphInfo, buttons) { var modeBarId = 'modebar-' + fullLayout._uid; this.element.setAttribute('id', modeBarId); - this._uid = modeBarId; + this.element.setAttribute('role', 'toolbar'); - this.element.className = 'modebar'; + this._uid = modeBarId; + this.element.className = 'modebar modebar--custom'; if(context.displayModeBar === 'hover') this.element.className += ' modebar--hover ease-bg'; if(fullLayout.modebar.orientation === 'v') { @@ -145,8 +146,9 @@ proto.createGroup = function() { */ proto.createButton = function(config) { var _this = this; - var button = document.createElement('a'); + var button = document.createElement('button'); + button.setAttribute('type', 'button'); button.setAttribute('rel', 'tooltip'); button.className = 'modebar-btn'; @@ -155,7 +157,10 @@ proto.createButton = function(config) { // for localization: allow title to be a callable that takes gd as arg else if(typeof title === 'function') title = title(this.graphInfo); - if(title || title === 0) button.setAttribute('data-title', title); + if(title || title === 0) { + button.setAttribute('data-title', title) + button.setAttribute("aria-label", title) + }; if(config.attr !== undefined) button.setAttribute('data-attr', config.attr); diff --git a/src/css/_modebar.scss b/src/css/_modebar.scss index b9c7f797a2c..b4a897672f6 100644 --- a/src/css/_modebar.scss +++ b/src/css/_modebar.scss @@ -8,7 +8,7 @@ @include vendor('transition', background-color 0.3s ease 0s); } -.modebar--hover > :not(.watermark) { +.modebar--hover> :not(.watermark) { opacity: 0; @include vendor('transition', opacity 0.3s ease 0s); } @@ -17,6 +17,10 @@ opacity: 1; } +&:focus-within .modebar--hover .modebar-group { + opacity: 1; +} + .modebar-group { float: left; display: inline-block; @@ -25,6 +29,11 @@ position: relative; vertical-align: middle; white-space: nowrap; + + a { + display: grid; + place-content: center; + } } .modebar-btn { @@ -36,15 +45,20 @@ cursor: pointer; line-height: normal; box-sizing: border-box; + border: none; + background: transparent; svg { position: relative; - top: 2px; } - &.modebar-btn--logo { - + &:focus-visible { + outline: 1px solid black; + outline-offset: 1px; + border-radius: 3px; } + + &.modebar-btn--logo {} } .modebar.vertical { @@ -53,9 +67,11 @@ flex-wrap: wrap; align-content: flex-end; max-height: 100%; + svg { - top: -1px; + top: -1px; } + .modebar-group { display: block; float: none; @@ -67,4 +83,4 @@ text-align: center; } } -} +} \ No newline at end of file diff --git a/test/jasmine/tests/modebar_test.js b/test/jasmine/tests/modebar_test.js index fac02c52fad..4d14c87d700 100644 --- a/test/jasmine/tests/modebar_test.js +++ b/test/jasmine/tests/modebar_test.js @@ -69,7 +69,7 @@ describe('ModeBar', function() { } function countButtons(modeBar) { - return d3Select(modeBar.element).selectAll('a.modebar-btn').size(); + return d3Select(modeBar.element).selectAll('button.modebar-btn, a.modebar-btn').size(); } function countLogo(modeBar) { @@ -77,7 +77,7 @@ describe('ModeBar', function() { } function checkBtnAttr(modeBar, index, attr) { - var buttons = d3Select(modeBar.element).selectAll('a.modebar-btn'); + var buttons = d3Select(modeBar.element).selectAll('button.modebar-btn, a.modebar-btn'); return d3Select(buttons[0][index]).attr(attr); } @@ -1676,7 +1676,7 @@ describe('ModeBar', function() { it('add predefined shape drawing and hover buttons via layout.modebar.add', function(done) { function countButtons() { var modeBarEl = gd._fullLayout._modeBar.element; - return d3Select(modeBarEl).selectAll('a.modebar-btn').size(); + return d3Select(modeBarEl).selectAll('button.modebar-btn, a.modebar-btn').size(); } var initial = 10; @@ -1761,7 +1761,7 @@ describe('ModeBar', function() { it('remove buttons using exact (camel case) and short (lower case) names via layout.modebar.remove and template', function(done) { function countButtons() { var modeBarEl = gd._fullLayout._modeBar.element; - return d3Select(modeBarEl).selectAll('a.modebar-btn').size(); + return d3Select(modeBarEl).selectAll('button.modebar-btn, a.modebar-btn').size(); } var initial = 10; @@ -1842,7 +1842,7 @@ describe('ModeBar', function() { it('add buttons using template', function(done) { function countButtons() { var modeBarEl = gd._fullLayout._modeBar.element; - return d3Select(modeBarEl).selectAll('a.modebar-btn').size(); + return d3Select(modeBarEl).selectAll('button.modebar-btn, a.modebar-btn').size(); } var initial = 10; @@ -1865,7 +1865,7 @@ describe('ModeBar', function() { it('add ' + t + ' button if removed by layout and added by config', function(done) { function countButtons() { var modeBarEl = gd._fullLayout._modeBar.element; - return d3Select(modeBarEl).selectAll('a.modebar-btn').size(); + return d3Select(modeBarEl).selectAll('button.modebar-btn, a.modebar-btn').size(); } var initial = 10; @@ -1886,7 +1886,7 @@ describe('ModeBar', function() { it('remove button if added by layout and removed by config', function(done) { function countButtons() { var modeBarEl = gd._fullLayout._modeBar.element; - return d3Select(modeBarEl).selectAll('a.modebar-btn').size(); + return d3Select(modeBarEl).selectAll('button.modebar-btn, a.modebar-btn').size(); } var initial = 10; pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy