xtendui
Version:
Xtend UI is a powerful frontend library of Tailwind CSS components enhanced by vanilla js. It helps you build interfaces with advanced interactions and animations.
117 lines (105 loc) • 4.04 kB
JavaScript
/*!
* Xtend UI (https://xtendui.github.io/xtendui/)
* @copyright (c) 2017-2026 Riccardo Caroli
* @license MIT (https://github.com/xtendui/xtendui/blob/master/LICENSE.txt)
*/
import { Xt } from './xt.mjs'
if (typeof window !== 'undefined') {
Xt.usabilityIgnore = '.sf-toolbar, .gm-style, .crt-widget, .g-recaptcha-response, [hidden]'
Xt.usabilityHostnames = '127.0.0.1 localhost'
Xt.frame({
func: () => {
if (
Xt.usabilityHostnames !== false &&
(Xt.usabilityHostnames === true || Xt.usabilityHostnames.split(' ').includes(location.hostname))
) {
// eslint-disable-next-line no-console
console.debug(
'%cXtend UI usability log activated, to deactivate remove xtendui/src/usability.js',
'font-weight:bold; color: white; background-color: #0067DD; padding: 6px 10px;',
)
// h1
Xt.mount({
matches: 'body',
mount: ({ ref }) => {
if (ref.closest(Xt.usabilityIgnore)) return
// h1
const h1 = ref.querySelectorAll('h1')
if (!h1.length) {
console.warn('Xtend UI usability detected no "h1" in the page', ref)
} else if (h1.length > 1) {
console.warn('Xtend UI usability detected multiple "h1" in the page', ref)
}
},
})
// images
Xt.mount({
matches: 'img[src]:not([src^="data:"])',
mount: ({ ref }) => {
if (ref.closest(Xt.usabilityIgnore)) return
// loading
const loading = ref.getAttribute('loading')
if (!loading) {
console.warn('Xtend UI usability detected an "image" without "loading" attribute', ref)
}
// alt
const alt = ref.getAttribute('alt')
if (!alt && alt !== '') {
console.warn('Xtend UI usability detected an "image" without "alt" attribute', ref)
}
},
})
// input
Xt.mount({
matches: 'input:not([type="hidden"]), select, textarea',
mount: ({ ref }) => {
if (ref.closest(Xt.usabilityIgnore)) return
// label
const labels = ref.labels
const label = ref.getAttribute('aria-label') || ref.getAttribute('aria-labelledby')
if (!labels.length && (!label || label === '')) {
console.warn(
'Xtend UI usability detected an "input" without "label" or "aria-label" or "aria-labelledby"',
ref,
)
}
},
})
// links
Xt.mount({
matches: 'a[href]',
mount: ({ ref }) => {
if (ref.closest(Xt.usabilityIgnore)) return
// title
const text = ref.textContent.trim()
const title = ref.title
const label = ref.getAttribute('aria-label') || ref.getAttribute('aria-labelledby')
if (!text.length && (!title || title === '') && (!label || label === '')) {
console.warn(
'Xtend UI usability detected a "link" without "textContent" or "title" or "aria-label" or "aria-labelledby"',
ref,
)
}
// target
const target = ref.getAttribute('target')
if (target && target.toLowerCase() === '_blank') {
if (ref.hostname.length && location.hostname !== ref.hostname) {
const rel = ref.getAttribute('rel')
if (
!rel ||
rel === '' ||
(!rel.toLowerCase().indexOf('noopener') && !rel.toLowerCase().indexOf('noreferrer'))
) {
console.warn(
'Xtend UI usability detected a "link" with target="_blank" without rel="noopener" or rel="noreferrer"',
ref,
)
}
}
}
},
})
}
},
})
}