Enhance accessibility with correct keyboard navigation and focus style

This commit is contained in:
Bubka
2022-09-21 21:38:53 +02:00
parent fb7c0a9c6a
commit 4f3fa4ba75
17 changed files with 120 additions and 79 deletions

View File

@ -15,7 +15,7 @@
<div v-else class="content has-text-centered"> <div v-else class="content has-text-centered">
<router-link id="lnkSettings" :to="{ name: 'settings.options' }" class="has-text-grey">{{ $t('settings.settings') }}</router-link> <router-link id="lnkSettings" :to="{ name: 'settings.options' }" class="has-text-grey">{{ $t('settings.settings') }}</router-link>
<span v-if="!this.$root.appConfig.proxyAuth || (this.$root.appConfig.proxyAuth && this.$root.appConfig.proxyLogoutUrl)"> <span v-if="!this.$root.appConfig.proxyAuth || (this.$root.appConfig.proxyAuth && this.$root.appConfig.proxyLogoutUrl)">
- <a id="lnkSignOut" class="has-text-grey" @click="logout">{{ $t('auth.sign_out') }}</a> - <button id="lnkSignOut" class="button is-text is-like-text has-text-grey" @click="logout">{{ $t('auth.sign_out') }}</button>
</span> </span>
</div> </div>
</footer> </footer>

View File

@ -1,7 +1,7 @@
<template> <template>
<div class="field"> <div class="field">
<input :id="fieldName" type="checkbox" :name="fieldName" class="is-checkradio is-info" v-model="form[fieldName]" v-on:change="$emit(fieldName, form[fieldName])" v-bind="$attrs"> <input :id="fieldName" type="checkbox" :name="fieldName" class="is-checkradio is-info" v-model="form[fieldName]" v-on:change="$emit(fieldName, form[fieldName])" v-bind="$attrs">
<label :for="fieldName" class="label" v-html="label"></label> <label tabindex="0" :for="fieldName" class="label" :class="labelClass" v-html="label" v-on:keypress.space.prevent="setCheckbox"></label>
<p class="help" v-html="help" v-if="help"></p> <p class="help" v-html="help" v-if="help"></p>
</div> </div>
</template> </template>
@ -38,6 +38,15 @@
type: String, type: String,
default: '' default: ''
}, },
},
methods: {
setCheckbox(event) {
if (this.$attrs.disabled == false) {
this.form[this.fieldName] = !this.form[this.fieldName]
this.$emit(this.fieldName, this.form[this.fieldName])
}
}
} }
} }
</script> </script>

View File

@ -13,10 +13,10 @@
v-on:change="$emit('field-changed', form[fieldName])" v-on:change="$emit('field-changed', form[fieldName])"
v-on:keyup="checkCapsLock" v-on:keyup="checkCapsLock"
/> />
<span v-if="currentType == 'password'" class="icon is-small is-right is-clickable" @click="currentType = 'text'" :title="$t('auth.forms.reveal_password')"> <span v-if="currentType == 'password'" role="button" tabindex="0" class="icon is-small is-right is-clickable" @keyup.enter="setFieldType('text')" @click="setFieldType('text')" :title="$t('auth.forms.reveal_password')">
<font-awesome-icon :icon="['fas', 'eye-slash']" /> <font-awesome-icon :icon="['fas', 'eye-slash']" />
</span> </span>
<span v-else class="icon is-small is-right is-clickable" @click="currentType = 'password'" :title="$t('auth.forms.hide_password')"> <span v-else role="button" tabindex="0" class="icon is-small is-right is-clickable" @keyup.enter="setFieldType('password')" @click="setFieldType('password')" :title="$t('auth.forms.hide_password')">
<font-awesome-icon :icon="['fas', 'eye']" /> <font-awesome-icon :icon="['fas', 'eye']" />
</span> </span>
</div> </div>
@ -121,6 +121,12 @@
checkCapsLock(event) { checkCapsLock(event) {
this.hasCapsLockOn = event.getModifierState('CapsLock') ? true : false this.hasCapsLockOn = event.getModifierState('CapsLock') ? true : false
}, },
setFieldType(event) {
if (this.currentType != event) {
this.currentType = event
}
}
}, },
} }
</script> </script>

View File

@ -1,11 +1,29 @@
<template> <template>
<div class="field" :class="{ 'pt-3' : hasOffset }"> <div class="field" :class="{ 'pt-3' : hasOffset }" role="radiogroup" :aria-labelledby="inputId('label', fieldName)">
<label class="label" v-html="label"></label> <label :id="inputId('label', fieldName)" class="label" v-html="label"></label>
<div class="is-toggle buttons"> <div class="is-toggle buttons">
<label class="button is-dark" :disabled="isDisabled" v-for="(choice, index) in choices" :key="index" :class="{ 'is-link' : form[fieldName] === choice.value }"> <button
<input type="radio" class="is-hidden" :checked="form[fieldName] === choice.value" :value="choice.value" v-model="form[fieldName]" v-on:change="$emit(fieldName, form[fieldName])" :disabled="isDisabled" /> role="radio"
type="button"
class="button is-dark"
:aria-checked="form[fieldName] === choice.value"
:disabled="isDisabled"
v-for="(choice, index) in choices"
:key="index"
:class="{ 'is-link' : form[fieldName] === choice.value }"
v-on:click.stop="setRadio(choice.value)"
>
<input
:id="inputId(inputType, choice.value)"
:type="inputType"
class="is-hidden"
:checked="form[fieldName] === choice.value"
:value="choice.value"
v-model="form[fieldName]"
:disabled="isDisabled"
/>
<font-awesome-icon :icon="['fas', choice.icon]" v-if="choice.icon" class="mr-3" /> {{ choice.text }} <font-awesome-icon :icon="['fas', choice.icon]" v-if="choice.icon" class="mr-3" /> {{ choice.text }}
</label> </button>
</div> </div>
<field-error :form="form" :field="fieldName" /> <field-error :form="form" :field="fieldName" />
<p class="help" v-html="help" v-if="help"></p> <p class="help" v-html="help" v-if="help"></p>
@ -18,7 +36,7 @@
data() { data() {
return { return {
inputType: 'radio'
} }
}, },
@ -58,6 +76,13 @@
type: Boolean, type: Boolean,
default: false default: false
} }
},
methods: {
setRadio(event) {
this.form[this.fieldName] = event
this.$emit(this.fieldName, this.form[this.fieldName])
}
} }
} }
</script> </script>

View File

@ -14,9 +14,9 @@
</div> </div>
<div v-if="this.showcloseButton" class="fullscreen-footer"> <div v-if="this.showcloseButton" class="fullscreen-footer">
<!-- Close button --> <!-- Close button -->
<label class="button is-dark is-rounded" @click.stop="closeModal"> <button class="button is-dark is-rounded" @click.stop="closeModal">
{{ $t('commons.close') }} {{ $t('commons.close') }}
</label> </button>
</div> </div>
</div> </div>
</template> </template>

View File

@ -5,7 +5,7 @@
</figure> </figure>
<p class="is-size-4 has-text-grey-light has-ellipsis">{{ internal_service }}</p> <p class="is-size-4 has-text-grey-light has-ellipsis">{{ internal_service }}</p>
<p class="is-size-6 has-text-grey has-ellipsis">{{ internal_account }}</p> <p class="is-size-6 has-text-grey has-ellipsis">{{ internal_account }}</p>
<p class="is-size-1 has-text-white is-clickable" :title="$t('commons.copy_to_clipboard')" v-clipboard="() => internal_password.replace(/ /g, '')" v-clipboard:success="clipboardSuccessHandler">{{ displayedOtp }}</p> <p tabindex="0" class="is-size-1 has-text-white is-clickable" :title="$t('commons.copy_to_clipboard')" v-clipboard="() => internal_password.replace(/ /g, '')" v-clipboard:success="clipboardSuccessHandler">{{ displayedOtp }}</p>
<ul class="dots" v-show="isTimeBased(internal_otp_type)"> <ul class="dots" v-show="isTimeBased(internal_otp_type)">
<li v-for="n in 10" :key="n"></li> <li v-for="n in 10" :key="n"></li>
</ul> </ul>

View File

@ -5,7 +5,7 @@
<div class="tabs is-centered is-fullwidth"> <div class="tabs is-centered is-fullwidth">
<ul> <ul>
<li v-for="tab in tabs" :key="tab.view" :class="{ 'is-active': tab.view === activeTab }"> <li v-for="tab in tabs" :key="tab.view" :class="{ 'is-active': tab.view === activeTab }">
<a :id="tab.id" @click="selectTab(tab.view)">{{ tab.name }}</a> <a :id="tab.id" tabindex="0" @click="selectTab(tab.view)">{{ tab.name }}</a>
</li> </li>
</ul> </ul>
</div> </div>

View File

@ -177,6 +177,12 @@ Vue.mixin({
case 'password': case 'password':
prefix = 'pwd' prefix = 'pwd'
break break
case 'radio':
prefix = 'rdo'
break
case 'label':
prefix = 'lbl'
break
default: default:
prefix = 'txt' prefix = 'txt'
break break

View File

@ -50,9 +50,9 @@
{{ $t('commons.environment') }} {{ $t('commons.environment') }}
</h2> </h2>
<div class="box has-background-black-bis is-family-monospace is-size-7"> <div class="box has-background-black-bis is-family-monospace is-size-7">
<span class="is-pulled-right is-clickable" v-clipboard="() => this.$refs.listInfos.innerText" v-clipboard:success="clipboardSuccessHandler"> <button class="button copy-text is-pulled-right is-small is-text" v-clipboard="() => this.$refs.listInfos.innerText" v-clipboard:success="clipboardSuccessHandler">
<font-awesome-icon :icon="['fas', 'copy']" /> <font-awesome-icon :icon="['fas', 'copy']" />
</span> </button>
<ul ref="listInfos"> <ul ref="listInfos">
<li v-for="(value, key) in infos" :value="value" :key="key"><b>{{key}}</b>: {{value}}</li> <li v-for="(value, key) in infos" :value="value" :key="key"><b>{{key}}</b>: {{value}}</li>
</ul> </ul>
@ -62,9 +62,9 @@
{{ $t('settings.user_options') }} {{ $t('settings.user_options') }}
</h2> </h2>
<div class="box has-background-black-bis is-family-monospace is-size-7"> <div class="box has-background-black-bis is-family-monospace is-size-7">
<span class="is-pulled-right is-clickable" v-clipboard="() => this.$refs.listUserOptions.innerText" v-clipboard:success="clipboardSuccessHandler"> <button class="button copy-text is-pulled-right is-small is-text" v-clipboard="() => this.$refs.listUserOptions.innerText" v-clipboard:success="clipboardSuccessHandler">
<font-awesome-icon :icon="['fas', 'copy']" /> <font-awesome-icon :icon="['fas', 'copy']" />
</span> </button>
<ul ref="listUserOptions"> <ul ref="listUserOptions">
<li v-for="(value, option) in options" :value="value" :key="option"><b>{{option}}</b>: {{value}}</li> <li v-for="(value, option) in options" :value="value" :key="option"><b>{{option}}</b>: {{value}}</li>
</ul> </ul>

View File

@ -19,7 +19,7 @@
<vue-footer :showButtons="true"> <vue-footer :showButtons="true">
<!-- Close Group switch button --> <!-- Close Group switch button -->
<p class="control"> <p class="control">
<a class="button is-dark is-rounded" @click="closeGroupSwitch()">{{ $t('commons.close') }}</a> <button class="button is-dark is-rounded" @click="closeGroupSwitch()">{{ $t('commons.close') }}</button>
</p> </p>
</vue-footer> </vue-footer>
</div> </div>
@ -81,7 +81,7 @@
</div> </div>
</div> </div>
</transition> </transition>
<div class="tfa-cell tfa-content is-size-3 is-size-4-mobile" @click.stop="showAccount(account)"> <div tabindex="0" class="tfa-cell tfa-content is-size-3 is-size-4-mobile" @click="showAccount(account)" @keyup.enter="showAccount(account)" role="button">
<div class="tfa-text has-ellipsis"> <div class="tfa-text has-ellipsis">
<img :src="'/storage/icons/' + account.icon" v-if="account.icon && $root.appSettings.showAccountsIcons" :alt="$t('twofaccounts.icon_for_account_x_at_service_y', {account: account.account, service: account.service})"> <img :src="'/storage/icons/' + account.icon" v-if="account.icon && $root.appSettings.showAccountsIcons" :alt="$t('twofaccounts.icon_for_account_x_at_service_y', {account: account.account, service: account.service})">
{{ displayService(account.service) }}<font-awesome-icon class="has-text-danger is-size-5 ml-2" v-if="$root.appSettings.useEncryption && account.account === $t('errors.indecipherable')" :icon="['fas', 'exclamation-circle']" /> {{ displayService(account.service) }}<font-awesome-icon class="has-text-danger is-size-5 ml-2" v-if="$root.appSettings.useEncryption && account.account === $t('errors.indecipherable')" :icon="['fas', 'exclamation-circle']" />
@ -148,7 +148,7 @@
{{ selectedAccounts.length }}&nbsp;{{ $t('commons.selected') }} {{ selectedAccounts.length }}&nbsp;{{ $t('commons.selected') }}
<button @click="clearSelected" :style="{visibility: selectedAccounts.length > 0 ? 'visible' : 'hidden'}" class="delete" :title="$t('commons.clear_selection')"></button> <button @click="clearSelected" :style="{visibility: selectedAccounts.length > 0 ? 'visible' : 'hidden'}" class="delete" :title="$t('commons.clear_selection')"></button>
</span> </span>
<span @click="selectAll" class="tag is-dark is-clickable has-background-black-ter has-text-grey" :title="$t('commons.select_all')"> <span role="button" @click="selectAll" class="tag is-dark is-clickable has-background-black-ter has-text-grey" :title="$t('commons.select_all')">
{{ $t('commons.all') }} {{ $t('commons.all') }}
<font-awesome-icon class="ml-1" :icon="['fas', 'check-square']" /> <font-awesome-icon class="ml-1" :icon="['fas', 'check-square']" />
</span> </span>
@ -156,10 +156,10 @@
</div> </div>
<div class="control"> <div class="control">
<div class="tags has-addons are-medium"> <div class="tags has-addons are-medium">
<span @click="sortAsc" class="tag is-dark is-clickable has-background-black-ter has-text-grey" :title="$t('commons.sort_ascending')"> <span role="button" @click="sortAsc" class="tag is-dark is-clickable has-background-black-ter has-text-grey" :title="$t('commons.sort_ascending')">
<font-awesome-icon :icon="['fas', 'sort-alpha-down']" /> <font-awesome-icon :icon="['fas', 'sort-alpha-down']" />
</span> </span>
<span @click="sortDesc" class="tag is-dark is-clickable has-background-black-ter has-text-grey" :title="$t('commons.sort_descending')"> <span role="button" @click="sortDesc" class="tag is-dark is-clickable has-background-black-ter has-text-grey" :title="$t('commons.sort_descending')">
<font-awesome-icon :icon="['fas', 'sort-alpha-up']" /> <font-awesome-icon :icon="['fas', 'sort-alpha-up']" />
</span> </span>
</div> </div>
@ -167,7 +167,7 @@
</div> </div>
<div class="field is-grouped is-justify-content-center pt-1"> <div class="field is-grouped is-justify-content-center pt-1">
<div class="control"> <div class="control">
<div class="tags are-medium has-addons is-clickable" v-if="selectedAccounts.length > 0" @click="showGroupSelector = true"> <div role="button" class="tags are-medium has-addons is-clickable" v-if="selectedAccounts.length > 0" @click="showGroupSelector = true">
<span class="tag is-dark"> <span class="tag is-dark">
{{ $t('groups.change_group') }} {{ $t('groups.change_group') }}
</span> </span>
@ -177,7 +177,7 @@
</div> </div>
</div> </div>
<div class="control"> <div class="control">
<div class="tags are-medium has-addons is-clickable" v-if="selectedAccounts.length > 0" @click="destroyAccounts"> <div role="button" class="tags are-medium has-addons is-clickable" v-if="selectedAccounts.length > 0" @click="destroyAccounts">
<span class="tag is-dark"> <span class="tag is-dark">
{{ $t('commons.delete') }} {{ $t('commons.delete') }}
</span> </span>
@ -193,7 +193,7 @@
<!-- search --> <!-- search -->
<div class="field"> <div class="field">
<div class="control has-icons-right"> <div class="control has-icons-right">
<input type="text" :title="$t('commons.search')" class="input is-rounded is-search" v-model="search"> <input id="txtSearch" type="search" tabindex="1" :aria-label="$t('commons.search')" :title="$t('commons.search')" class="input is-rounded is-search" v-model="search">
<span class="icon is-small is-right"> <span class="icon is-small is-right">
<font-awesome-icon :icon="['fas', 'search']" v-if="!search" /> <font-awesome-icon :icon="['fas', 'search']" v-if="!search" />
<a class="delete" v-if="search" @click="search = '' "></a> <a class="delete" v-if="search" @click="search = '' "></a>
@ -201,14 +201,18 @@
</div> </div>
</div> </div>
<!-- group switch toggle --> <!-- group switch toggle -->
<div class="is-clickable has-text-centered"> <div class="has-text-centered">
<div class="columns" @click="toggleGroupSwitch"> <div class="columns">
<div class="column" v-if="!showGroupSwitch"> <div class="column" v-if="!showGroupSwitch">
{{ activeGroupName }} ({{ filteredAccounts.length }}) <button :title="$t('groups.show_group_selector')" tabindex="1" class="button is-text is-like-text" @click.stop="toggleGroupSwitch">
<font-awesome-icon :icon="['fas', 'caret-down']" /> {{ activeGroupName }} ({{ filteredAccounts.length }})&nbsp;
<font-awesome-icon :icon="['fas', 'caret-down']" />
</button>
</div> </div>
<div class="column" v-else> <div class="column" v-else>
{{ $t('groups.select_accounts_to_show') }} <button :title="$t('groups.hide_group_selector')" tabindex="1" class="button is-text is-like-text" @click.stop="toggleGroupSwitch">
{{ $t('groups.select_accounts_to_show') }}
</button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -10,7 +10,9 @@
</div> </div>
<div class="nav-links"> <div class="nav-links">
<p>{{ $t('auth.webauthn.lost_your_device') }}&nbsp;<router-link id="lnkRecoverAccount" :to="{ name: 'webauthn.lost' }" class="is-link">{{ $t('auth.webauthn.recover_your_account') }}</router-link></p> <p>{{ $t('auth.webauthn.lost_your_device') }}&nbsp;<router-link id="lnkRecoverAccount" :to="{ name: 'webauthn.lost' }" class="is-link">{{ $t('auth.webauthn.recover_your_account') }}</router-link></p>
<p v-if="!this.$root.appSettings.useWebauthnOnly">{{ $t('auth.sign_in_using') }}&nbsp;<a id="lnkSignWithLegacy" class="is-link" @click="showWebauthn = false">{{ $t('auth.login_and_password') }}</a></p> <p v-if="!this.$root.appSettings.useWebauthnOnly">{{ $t('auth.sign_in_using') }}&nbsp;
<a id="lnkSignWithLegacy" role="button" class="is-link" @keyup.enter="showWebauthn = false" @click="showWebauthn = false" tabindex="0">{{ $t('auth.login_and_password') }}</a>
</p>
</div> </div>
</form-wrapper> </form-wrapper>
<!-- login/password legacy form --> <!-- login/password legacy form -->
@ -28,7 +30,9 @@
</div> </div>
<div v-else> <div v-else>
<p>{{ $t('auth.forms.forgot_your_password') }}&nbsp;<router-link id="lnkResetPwd" :to="{ name: 'password.request' }" class="is-link">{{ $t('auth.forms.request_password_reset') }}</router-link></p> <p>{{ $t('auth.forms.forgot_your_password') }}&nbsp;<router-link id="lnkResetPwd" :to="{ name: 'password.request' }" class="is-link">{{ $t('auth.forms.request_password_reset') }}</router-link></p>
<p >{{ $t('auth.sign_in_using') }}&nbsp;<a id="lnkSignWithWebauthn" class="is-link" @click="showWebauthn = true">{{ $t('auth.webauthn.security_device') }}</a></p> <p >{{ $t('auth.sign_in_using') }}&nbsp;
<a id="lnkSignWithWebauthn" role="button" class="is-link" @keyup.enter="showWebauthn = true" @click="showWebauthn = true" tabindex="0">{{ $t('auth.webauthn.security_device') }}</a>
</p>
</div> </div>
</div> </div>
</form-wrapper> </form-wrapper>

View File

@ -37,7 +37,7 @@
<vue-footer :showButtons="true"> <vue-footer :showButtons="true">
<!-- Cancel button --> <!-- Cancel button -->
<p class="control"> <p class="control">
<a class="button is-dark is-rounded" @click.stop="exitSettings"> <a role="button" tabindex="0" class="button is-dark is-rounded" @click.stop="exitSettings">
{{ $t('commons.close') }} {{ $t('commons.close') }}
</a> </a>
</p> </p>

View File

@ -9,7 +9,7 @@
{{ $t('settings.token_legend')}} {{ $t('settings.token_legend')}}
</div> </div>
<div class="mt-3"> <div class="mt-3">
<a class="is-link" @click="createToken()"> <a role="button" tabindex="0" class="is-link" @click="createToken()">
<font-awesome-icon :icon="['fas', 'plus-circle']" /> {{ $t('settings.generate_new_token')}} <font-awesome-icon :icon="['fas', 'plus-circle']" /> {{ $t('settings.generate_new_token')}}
</a> </a>
</div> </div>

View File

@ -9,8 +9,8 @@
{{ $t('auth.webauthn.security_devices_legend')}} {{ $t('auth.webauthn.security_devices_legend')}}
</div> </div>
<div class="mt-3"> <div class="mt-3">
<a class="is-link" @click="register()"> <a role="button" tabindex="0" @click="register()">
<font-awesome-icon :icon="['fas', 'plus-circle']" /> {{ $t('auth.webauthn.register_a_new_device')}} <font-awesome-icon :icon="['fas', 'plus-circle']" />&nbsp;{{ $t('auth.webauthn.register_a_new_device')}}
</a> </a>
</div> </div>
<!-- credentials list --> <!-- credentials list -->

View File

@ -22,7 +22,7 @@ return [
'Unable_to_decrypt_uri' => 'Unable to decrypt uri', 'Unable_to_decrypt_uri' => 'Unable to decrypt uri',
'not_a_supported_otp_type' => 'This OTP format is not currently supported', 'not_a_supported_otp_type' => 'This OTP format is not currently supported',
'cannot_create_otp_without_secret' => 'Cannot create an OTP without a secret', 'cannot_create_otp_without_secret' => 'Cannot create an OTP without a secret',
'data_of_qrcode_is_not_valid_URI' => 'The data of this QR code is not a valid OTP Auth URI:', 'data_of_qrcode_is_not_valid_URI' => 'The data of this QR code is not a valid OTP Auth URI. The QR code contains:',
'wrong_current_password' => 'Wrong current password, nothing has changed', 'wrong_current_password' => 'Wrong current password, nothing has changed',
'error_during_encryption' => 'Encryption failed, your database remains unprotected.', 'error_during_encryption' => 'Encryption failed, your database remains unprotected.',
'error_during_decryption' => 'Decryption failed, your database is still protected. This is mainly caused by an integrity issue of encrypted data for one or more accounts.', 'error_during_decryption' => 'Decryption failed, your database is still protected. This is mainly caused by an integrity issue of encrypted data for one or more accounts.',

View File

@ -15,7 +15,9 @@ return [
'groups' => 'Groups', 'groups' => 'Groups',
'create_group' => 'Create new group', 'create_group' => 'Create new group',
'select_accounts_to_show' => 'Select accounts to show', 'show_group_selector' => 'Show group selector',
'hide_group_selector' => 'Hide group selector',
'select_accounts_to_show' => 'Select accounts group to show',
'manage_groups' => 'Manage groups', 'manage_groups' => 'Manage groups',
'active_group' => 'Active group', 'active_group' => 'Active group',
'manage_groups_legend' => 'You can create groups to organize your accounts the way you want. All accounts remain visible in the pseudo group named \'All\', regardless of the group they belong to.', 'manage_groups_legend' => 'You can create groups to organize your accounts the way you want. All accounts remain visible in the pseudo group named \'All\', regardless of the group they belong to.',

View File

@ -205,9 +205,13 @@ a:hover {
flex-grow: 1; flex-grow: 1;
overflow: hidden; overflow: hidden;
} }
.tfa-content:focus, .tfa-content:focus-visible
// .tfa-grid .tfa-content { {
// } outline: 2px solid $grey;
border: none;
outline-offset: 7px;
border-radius: 3px;
}
.tfa-list .tfa-content { .tfa-list .tfa-content {
padding-right: 1rem; padding-right: 1rem;
@ -524,43 +528,24 @@ a.has-text-white-bis:focus, a.has-text-white-bis:focus-visible {
box-shadow: $input-focus-box-shadow-size $input-focus-box-shadow-color; box-shadow: $input-focus-box-shadow-size $input-focus-box-shadow-color;
} }
// .button.is-dark:focus:not(:active), .button.is-dark.is-focused:not(:active), .is-checkradio[type="checkbox"] + label:focus,
// .button.is-link:focus:not(:active), .button.is-link.is-focused:not(:active) { .is-checkradio[type="checkbox"] + label:focus-visible
// box-shadow: 0 0 0 0.125em hsla(0, 0%, 96%, 0.25); {
// } outline: none;
// .button.copy-text:focus:not(:active), .button.copy-text.is-focused:not(:active) { border: none;
// box-shadow: none; }
// color: hsl(0, 0%, 86%); .is-checkradio[type="checkbox"] + label:focus::before,
// } .is-checkradio[type="checkbox"] + label:focus-visible::before
{
// a:focus, outline: none;
// .button:focus, border: 1px solid $input-focus-border-color;
// .control.has-icons-right > span.icon:focus { box-shadow: $input-focus-box-shadow-size $input-focus-box-shadow-color;
// outline: none !important; }
// } .is-checkradio[type="checkbox"] + label::before,
// .button:focus { .is-checkradio[type="checkbox"] + label::before
// box-shadow: none; {
// } border-color: $grey;
// a:focus-visible, }
// .control.has-icons-right > span.icon:focus {
// outline: 2px solid hsl(217, 71%, 53%) !important;
// outline-offset: 3px !important;
// }
// .button:focus-visible {
// box-shadow: none;
// outline: 2px solid hsl(217, 71%, 53%) !important;
// outline-offset: 3px !important;
// }
// @supports not selector(:focus-visible) {
// a:focus,
// button:focus,
// .control.has-icons-right > span.icon:focus {
// outline: 2px solid hsl(217, 71%, 53%);
// outline-offset: 3px;
// }
// }
.label { .label {