react-accessible-headings
Version:
Accessible dynamic H1, H2, that will adjust for accessibility reasons! WCAG ARIA
79 lines • 3.51 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.checkHeadingLevels = exports.useHClassName = exports.useLevel = exports.H = exports.Level = exports.HeadingsContext = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = require("react");
exports.HeadingsContext = (0, react_1.createContext)({ level: 1 });
function Level({ children, value, hClassName }) {
const context = (0, react_1.useContext)(exports.HeadingsContext);
const level = levelRange(value !== undefined ? value : context.level + 1);
return ((0, jsx_runtime_1.jsx)(exports.HeadingsContext.Provider, { value: {
level,
hClassName: hClassName || context.hClassName,
}, children: children }));
}
exports.Level = Level;
function H({ children, offset, className, ...otherProps }) {
const context = (0, react_1.useContext)(exports.HeadingsContext);
const proposedLevel = context.level + (offset !== undefined ? offset : 0);
const level = levelRange(proposedLevel);
// merge and trim unneeded spaces between classNames
const mergedClassName = [context.hClassName, className]
.filter(Boolean)
.join(' ');
setTimeout(checkHeadingLevelsDom, CHECK_AFTER_MS);
// couldn't have JSX syntax, because ts throws
// "Property 'children' does not exist on type 'IntrinsicAttributes'"
return (0, react_1.createElement)(`h${level}`, { className: mergedClassName, ...otherProps }, children);
}
exports.H = H;
function levelRange(level) {
if (level > 0 && level <= MAXIMUM_LEVEL) {
return level;
}
const errorMessage = `Heading level "${level}" is not valid HTML5 which only allows levels 1-${MAXIMUM_LEVEL}`;
console.error(errorMessage);
return Math.min(Math.max(1, level), MAXIMUM_LEVEL);
}
function useLevel() {
const context = (0, react_1.useContext)(exports.HeadingsContext);
setTimeout(checkHeadingLevelsDom, CHECK_AFTER_MS);
return levelRange(context.level);
}
exports.useLevel = useLevel;
function useHClassName() {
const context = (0, react_1.useContext)(exports.HeadingsContext);
return context.hClassName || '';
}
exports.useHClassName = useHClassName;
function checkHeadingLevelsDom() {
if (typeof window === 'undefined')
return; // No need to run during SSR
checkHeadingLevels(Array.from(document.querySelectorAll('h1,h2,h3,h4,h5,h6')).map((elm) => parseFloat(elm.tagName.substring(1))));
}
function checkHeadingLevels(headings) {
const badHeadings = getBadHeadings(headings);
if (badHeadings.length > 0) {
const errorMessage = `WCAG accessibility issue detected: skipped heading levels ${badHeadings.map((num) => `h${num}`)}. See https://www.npmjs.com/package/react-accessible-headings#why`;
console.error(errorMessage);
}
return badHeadings;
}
exports.checkHeadingLevels = checkHeadingLevels;
function getBadHeadings(headings) {
return headings.filter(
// multiple H1s are not recommended. See docs.
(heading) => heading === 1).length >= 2 ||
headings.some((heading, index, arr) => {
// detect skipped levels
const precedingHeading = arr[index - 1];
if (!precedingHeading)
return false;
return heading > precedingHeading + 1;
})
? headings
: [];
}
const MAXIMUM_LEVEL = 6;
const CHECK_AFTER_MS = 1; // used in setTimeout and browsers will typically clamp this to ~5ms
//# sourceMappingURL=index.js.map