Added accessibility support
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
'use client';
|
||||
import { jsx as _jsx } from "react/jsx-runtime";
|
||||
import { createContext, use, useCallback, useEffect, useState } from 'react';
|
||||
import { A11Y_COOKIE, A11Y_COOKIE_MAX_AGE, A11Y_DEFAULT, a11yAttributes, parseA11y, serializeA11y } from './state.js';
|
||||
const A11yContext = /*#__PURE__*/ createContext(null);
|
||||
function readCookie(name) {
|
||||
if (typeof document === 'undefined') {
|
||||
return undefined;
|
||||
}
|
||||
const match = document.cookie.match(new RegExp(`(?:^|; )${name}=([^;]*)`));
|
||||
return match ? decodeURIComponent(match[1]) : undefined;
|
||||
}
|
||||
/**
|
||||
* Provides accessibility preferences, persists them in a cookie, and applies them
|
||||
* as data-attributes on <html> so the project's CSS can react. Like
|
||||
* ConsentProvider for cookies — wrap the app once; the widget/button consume it.
|
||||
*
|
||||
* The plugin ships NO styles: it only sets attributes (data-a11y-*). The project
|
||||
* writes CSS for those it supports (see docs/accessibility.md). This keeps the
|
||||
* design in the project's hands.
|
||||
*
|
||||
* // layout.tsx
|
||||
* import { AccessibilityProvider } from '@intecion/ipal-kit/client'
|
||||
* <AccessibilityProvider>{children}</AccessibilityProvider>
|
||||
*
|
||||
* To avoid a flash, the project can read the a11y-prefs cookie server-side and
|
||||
* set the attributes on <html> during SSR (see docs). This provider re-applies
|
||||
* on the client and keeps them in sync.
|
||||
*/ export function AccessibilityProvider({ children }) {
|
||||
const [state, setState] = useState(A11Y_DEFAULT);
|
||||
// Hydrate from cookie on mount.
|
||||
useEffect(()=>{
|
||||
setState(parseA11y(readCookie(A11Y_COOKIE)));
|
||||
}, []);
|
||||
// Apply attributes to <html> whenever state changes.
|
||||
useEffect(()=>{
|
||||
const el = document.documentElement;
|
||||
const attrs = a11yAttributes(state);
|
||||
for (const [attr, value] of Object.entries(attrs)){
|
||||
if (value === null) {
|
||||
el.removeAttribute(attr);
|
||||
} else {
|
||||
el.setAttribute(attr, value);
|
||||
}
|
||||
}
|
||||
}, [
|
||||
state
|
||||
]);
|
||||
const persist = useCallback((next)=>{
|
||||
document.cookie = `${A11Y_COOKIE}=${encodeURIComponent(serializeA11y(next))}; path=/; max-age=${A11Y_COOKIE_MAX_AGE}; samesite=lax`;
|
||||
}, []);
|
||||
const set = useCallback((key, value)=>{
|
||||
setState((prev)=>{
|
||||
const next = {
|
||||
...prev,
|
||||
[key]: value
|
||||
};
|
||||
persist(next);
|
||||
return next;
|
||||
});
|
||||
}, [
|
||||
persist
|
||||
]);
|
||||
const reset = useCallback(()=>{
|
||||
setState(A11Y_DEFAULT);
|
||||
persist(A11Y_DEFAULT);
|
||||
}, [
|
||||
persist
|
||||
]);
|
||||
return /*#__PURE__*/ _jsx(A11yContext, {
|
||||
value: {
|
||||
reset,
|
||||
set,
|
||||
state
|
||||
},
|
||||
children: children
|
||||
});
|
||||
}
|
||||
/** Access accessibility preferences + setters. Use inside AccessibilityProvider. */ export function useAccessibility() {
|
||||
const ctx = use(A11yContext);
|
||||
if (!ctx) {
|
||||
throw new Error('useAccessibility must be used within <AccessibilityProvider>');
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
//# sourceMappingURL=AccessibilityProvider.js.map
|
||||
Reference in New Issue
Block a user