mirror of
https://github.com/tabler/tabler.git
synced 2026-08-29 21:31:28 +04:00
Improve accessibility across layouts, components, and forms (#2799)
This commit is contained in:
@@ -41,6 +41,7 @@
|
||||
@forward 'layout/page';
|
||||
@forward 'layout/footer';
|
||||
@forward 'layout/dark';
|
||||
@forward 'layout/accessibility';
|
||||
|
||||
@forward 'ui/accordion';
|
||||
@forward 'ui/alerts';
|
||||
|
||||
@@ -1014,7 +1014,11 @@ $focus-ring-width: 0.25rem !default;
|
||||
$focus-ring-opacity: 0.25 !default;
|
||||
$focus-ring-color: color-mix(in srgb, var(--#{$prefix}primary) #{math.percentage($focus-ring-opacity)}, transparent) !default;
|
||||
$focus-ring-blur: 0 !default;
|
||||
$focus-ring-box-shadow: 0 0 $focus-ring-blur $focus-ring-width $focus-ring-color !default;
|
||||
// Inner 2px solid ring keeps the indicator above the 3:1 non-text contrast minimum;
|
||||
// the translucent halo preserves the soft look
|
||||
$focus-ring-box-shadow:
|
||||
0 0 0 2px var(--#{$prefix}primary),
|
||||
0 0 $focus-ring-blur $focus-ring-width $focus-ring-color !default;
|
||||
|
||||
// Transitions
|
||||
$transition-base: all 0.2s ease-in-out !default;
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
@use '../config' as *;
|
||||
|
||||
// Forced-colors (Windows High Contrast) support: the UA strips box-shadow there,
|
||||
// and Tabler's focus indicator is shadow-based, so restore an outline-drawn one.
|
||||
@media (forced-colors: active) {
|
||||
:focus-visible {
|
||||
outline: 2px solid Highlight;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
// .btn-close is drawn with a mask + background-color and would disappear
|
||||
.btn-close {
|
||||
forced-color-adjust: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
// Decorative looping animations are dropped entirely
|
||||
.badge-blink,
|
||||
.status-dot-animated::before,
|
||||
.status-indicator-animated .status-indicator-circle,
|
||||
.icon-pulse,
|
||||
.icon-tada,
|
||||
.icon-rotate,
|
||||
.card-gradient-animated,
|
||||
.btn-animate-icon-pulse .icon,
|
||||
.btn-animate-icon-shake .icon,
|
||||
.btn-animate-icon-tada .icon,
|
||||
.wave-1,
|
||||
.wave-2,
|
||||
.wave-3 {
|
||||
animation: none !important;
|
||||
}
|
||||
|
||||
// Loading indicators still convey state, so slow them down instead of stopping
|
||||
.loader::after,
|
||||
.btn-loading::after,
|
||||
.progress-bar-indeterminate::before,
|
||||
.animated-dots::after {
|
||||
animation-duration: 3s !important;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,22 @@
|
||||
@use '../config' as *;
|
||||
|
||||
// Skip-to-content link: hidden via .visually-hidden-focusable until keyboard focus reveals it
|
||||
.skip-link {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
inset-inline-start: 0;
|
||||
z-index: $zindex-tooltip;
|
||||
padding: 0.5rem 1rem;
|
||||
background: var(--#{$prefix}primary);
|
||||
color: var(--#{$prefix}primary-fg, #fff);
|
||||
text-decoration: none;
|
||||
|
||||
&:focus-visible {
|
||||
outline: 2px solid var(--#{$prefix}primary);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
:root,
|
||||
:host {
|
||||
font-size: 16px;
|
||||
// No fixed root font-size: rem sizing must track the user's browser preference (WCAG 1.4.4)
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
|
||||
@@ -67,7 +67,9 @@
|
||||
|
||||
@mixin focus-ring($show-border: false) {
|
||||
outline: 0;
|
||||
box-shadow: 0 0 $focus-ring-blur $focus-ring-width color-transparent(var(--#{$prefix}primary), 0.25);
|
||||
box-shadow:
|
||||
0 0 0 2px var(--#{$prefix}primary),
|
||||
0 0 $focus-ring-blur $focus-ring-width color-transparent(var(--#{$prefix}primary), 0.25);
|
||||
|
||||
@if ($show-border) {
|
||||
border-color: color-transparent(var(--#{$prefix}primary), 0.25);
|
||||
|
||||
@@ -113,7 +113,9 @@
|
||||
@include expect() {
|
||||
.t {
|
||||
outline: 0;
|
||||
box-shadow: 0 0 0 0.25rem color-mix(in srgb, var(--tblr-primary) 25%, transparent);
|
||||
box-shadow:
|
||||
0 0 0 2px var(--tblr-primary),
|
||||
0 0 0 0.25rem color-mix(in srgb, var(--tblr-primary) 25%, transparent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -129,7 +131,9 @@
|
||||
@include expect() {
|
||||
.t {
|
||||
outline: 0;
|
||||
box-shadow: 0 0 0 0.25rem color-mix(in srgb, var(--tblr-primary) 25%, transparent);
|
||||
box-shadow:
|
||||
0 0 0 2px var(--tblr-primary),
|
||||
0 0 0 0.25rem color-mix(in srgb, var(--tblr-primary) 25%, transparent);
|
||||
border-color: color-mix(in srgb, var(--tblr-primary) 25%, transparent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,11 +278,17 @@
|
||||
content: none;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
&:focus:not(:focus-visible) {
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
outline: 2px solid var(--#{$prefix}primary);
|
||||
outline-offset: -2px;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&.show {
|
||||
color: var(--#{$prefix}body-color);
|
||||
|
||||
@@ -59,7 +59,6 @@
|
||||
background: 0 0;
|
||||
border: var(--#{$prefix}border-width) var(--#{$prefix}border-style) transparent;
|
||||
@include border-radius(var(--#{$prefix}border-radius-pill));
|
||||
outline: 0;
|
||||
@include transition(background $transition-time, border $transition-time, box-shadow 0.32s, color $transition-time);
|
||||
|
||||
&:hover {
|
||||
@@ -68,6 +67,11 @@
|
||||
background: #fefeff;
|
||||
border-color: var(--#{$prefix}border-color);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
outline: 2px solid var(--#{$prefix}primary);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.date-today {
|
||||
|
||||
@@ -21,6 +21,18 @@
|
||||
opacity: var(--#{$prefix}btn-close-opacity);
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
position: relative;
|
||||
|
||||
// Invisible overlay extending the hit area to a 24x24px minimum touch target
|
||||
&:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: max(100%, 24px);
|
||||
height: max(100%, 24px);
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--#{$prefix}btn-close-color);
|
||||
|
||||
@@ -17,10 +17,15 @@
|
||||
opacity: $btn-disabled-opacity;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
&:focus:not(:focus-visible) {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
outline: 2px solid var(--#{$prefix}primary);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
svg {
|
||||
display: block;
|
||||
width: 100%;
|
||||
|
||||
@@ -10,7 +10,9 @@
|
||||
|
||||
button[data-bs-dismiss='toast'],
|
||||
button[data-dismiss='toast'] {
|
||||
outline: none;
|
||||
&:focus:not(:focus-visible) {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
@include media-print {
|
||||
|
||||
Reference in New Issue
Block a user