Files
michaelschiemer/resources/css/admin/07-utilities/_accessibility.css
2025-11-24 21:28:25 +01:00

334 lines
8.1 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Accessibility Utilities - Admin Interface
*
* WCAG 2.1 AA Compliance:
* - Enhanced focus indicators (minimum 2px outline, 3:1 contrast)
* - Skip links and screen reader utilities
* - Reduced motion preferences
* - Keyboard navigation enhancements
*/
@layer utilities {
/**
* Focus Visible Enhancement (WCAG 2.1 Level AA)
*
* Requirement: 2.4.7 Focus Visible - All focusable elements
* must have a visible focus indicator with min 3:1 contrast ratio.
*/
:focus-visible {
outline: var(--focus-ring-width, 2px) solid var(--focus-ring);
outline-offset: var(--focus-ring-offset, 2px);
border-radius: var(--radius-sm);
}
/**
* Skip to Content Link (WCAG 2.4.1)
*
* Requirement: Bypass Blocks - Provide a mechanism to bypass
* repeated navigation blocks.
*/
.admin-skip-link {
position: absolute;
top: -9999px;
left: -9999px;
z-index: var(--z-toast);
padding: var(--spacing-md) var(--spacing-lg);
background-color: var(--accent-primary);
color: white;
text-decoration: none;
font-weight: var(--font-weight-semibold);
border-radius: var(--radius-md);
box-shadow: var(--shadow-lg);
&:focus {
top: var(--spacing-md);
left: var(--spacing-md);
}
}
/**
* Screen Reader Only
*
* Visually hidden but accessible to screen readers.
*/
.sr-only,
.admin-sr-only,
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
/**
* Focus Visible for Not Screen Reader Only
*
* Show element when focused (for skip links).
*/
.sr-only-focusable:focus,
.sr-only-focusable:active {
position: static;
width: auto;
height: auto;
overflow: visible;
clip: auto;
white-space: normal;
}
/**
* Reduced Motion (WCAG 2.3.3)
*
* Respect user's motion preferences.
*/
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
/**
* Focus Within Enhancement
*
* Show focus state for container when child is focused.
*/
.admin-nav__item:focus-within {
position: relative;
&::before {
content: '';
position: absolute;
left: -2px;
right: -2px;
top: -2px;
bottom: -2px;
border: 2px solid var(--focus-ring);
border-radius: var(--radius-md);
pointer-events: none;
}
}
/**
* Keyboard Navigation Indicator
*
* Show clear visual feedback for keyboard users.
*/
body.user-is-tabbing *:focus {
outline: 3px solid var(--accent-info);
outline-offset: 3px;
}
/**
* High Contrast Mode Support
*
* Ensure borders are visible in Windows High Contrast Mode.
*/
@media (prefers-contrast: high) {
.admin-card,
.admin-sidebar,
.admin-header {
border: 2px solid currentColor;
}
button,
.admin-action-btn,
.admin-nav__link {
border: 2px solid currentColor !important;
}
}
/**
* Touch Target Size (WCAG 2.5.5 Level AAA, but good practice)
*
* Minimum 44x44px touch targets for mobile.
*/
@media (pointer: coarse) {
button,
a,
input[type="checkbox"],
input[type="radio"],
.admin-action-btn,
.admin-nav__link {
min-width: 44px;
min-height: 44px;
}
}
/**
* Accessible Color Contrast Helpers
*
* WCAG AA requires:
* - Normal text: 4.5:1 contrast ratio
* - Large text (18pt+): 3:1 contrast ratio
* - UI components: 3:1 contrast ratio
*/
.text-contrast-aa {
/* Ensures minimum 4.5:1 contrast */
color: var(--content-text);
}
.text-contrast-large {
/* Large text can use lower contrast */
font-size: 1.125rem;
color: var(--content-text);
opacity: 0.9;
}
/**
* Error Identification (WCAG 3.3.1)
*
* Errors must be communicated with more than just color.
*/
.admin-error,
.admin-form-error {
color: var(--accent-error);
&::before {
content: '⚠ ';
font-weight: bold;
margin-right: 0.25rem;
}
}
[aria-invalid="true"] {
border-color: var(--accent-error) !important;
border-width: 2px !important;
/* Error icon */
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="%23dc2626" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>');
background-repeat: no-repeat;
background-position: right 0.75rem center;
background-size: 1.25rem;
padding-right: 2.5rem;
}
/**
* Success/Warning States with Icons
*
* Not relying solely on color for state communication.
*/
.admin-success::before {
content: '✓ ';
font-weight: bold;
color: var(--accent-success);
margin-right: 0.25rem;
}
.admin-warning::before {
content: '⚠ ';
font-weight: bold;
color: var(--accent-warning);
margin-right: 0.25rem;
}
.admin-info::before {
content: ' ';
font-weight: bold;
color: var(--accent-info);
margin-right: 0.25rem;
}
/**
* Accessible Button States
*/
button:disabled,
[aria-disabled="true"] {
opacity: 0.5;
cursor: not-allowed;
position: relative;
/* Pattern to indicate disabled state (not just opacity) */
&::after {
content: '';
position: absolute;
inset: 0;
background-image: repeating-linear-gradient(
45deg,
transparent,
transparent 5px,
oklch(0% 0 0 / 0.05) 5px,
oklch(0% 0 0 / 0.05) 10px
);
pointer-events: none;
}
}
/**
* Live Region Announcements
*
* For dynamic content updates.
*/
.admin-live-region {
position: absolute;
left: -10000px;
width: 1px;
height: 1px;
overflow: hidden;
}
[aria-live="polite"],
[aria-live="assertive"] {
/* Screen reader will announce these */
}
/**
* Accessible Table Improvements
*/
table {
caption {
font-weight: var(--font-weight-semibold);
text-align: left;
padding: var(--spacing-md);
background-color: var(--bg-secondary);
}
th {
font-weight: var(--font-weight-semibold);
text-align: left;
}
/* Zebra striping for better readability */
tbody tr:nth-child(even) {
background-color: var(--bg-secondary);
}
}
/**
* Print Styles (Accessibility includes print)
*/
@media print {
.admin-sidebar,
.admin-header__actions,
.admin-mobile-overlay,
.admin-sidebar__mobile-toggle {
display: none !important;
}
.admin-content {
max-width: 100% !important;
padding: 0 !important;
}
a[href]::after {
content: " (" attr(href) ")";
font-size: 0.875em;
color: var(--content-text);
}
/* Don't show internal links */
a[href^="#"]::after,
a[href^="javascript:"]::after {
content: "";
}
}
}