sustainableweb-ig

ReSpec Tag and Filter

Features

HTML

The below attributes are required to be included within <section> elements:

Note:

<section class="notoc" data-testable="machine" data-materials="medium" data-energy="medium" data-water="medium" data-emissions="medium" data-standard="AFNOR" data-considerations="Accessibility" data-categories="Compatibility, Ideation, KPIs, Patterns, Reporting, Research, Social Equity, UI, Usability">
	<h4 id="identify-and-define">Success Criterion: Identify and define <span class="external"><a class ="machine" href="https://w3c.github.io/sustainableweb-wsg/star.html#UX02-1">Machine-testable</a> <span class="hide">/</span> <a class="urls" href="https://w3c.github.io/sustainableweb-wsg/resources.html#UX02-1">Resources</a></span></h4>
	<p>Primary and secondary target visitors are identified, and their needs are defined through quantitative or qualitative research, testing, or analytics, ensuring your visitors and affected communities remain a close part of the research and testing process.</p>
</section>

CSS

The below must be included within the <head> element:

Note:

/* Role-based Labeling & Filter System: https://github.com/w3c/sustainableweb-wsg/issues/14 */
legend { font-weight: bold; width: fit-content; }
.switch form { display: flex; margin-bottom: 1.5em; margin-top: 1.5em; }
.switch label { box-sizing: border-box; border: medium solid var(--toclink-visited-text); display: flex; padding: 0.5em; justify-content: center; }
.switch label:has(input:checked) { background-color: #d9d9d9; color: #000000; font-weight: bold; }
.switch label:has(input:focus:checked) { outline: 4px solid var(--toclink-underline); }
.switch label:first-child { border-right: 0; border-top-left-radius: 15px; border-bottom-left-radius: 15px; min-width: 100px; }
.switch label:last-child { border-left: 0; border-top-right-radius: 15px; border-bottom-right-radius: 15px; min-width: 100px; }
.filter h2 { position: static !important; }
.filter input { margin-bottom: 0.75em; margin-top: 0.75em; }
.filter label { margin-right: 0.5em; white-space: nowrap; }
.standards label, .considerations label, .categories label { flex-basis: 50%; margin-right: 0; }
.filter fieldset { border: medium solid #d9d9d9; display: flex; flex-wrap: wrap; margin-bottom: 1em; }
.filter fieldset:has(:checked) ~ .c .reset { border-color: var(--toclink-visited-text); color: var(--toclink-text); }
.filter .c, .switch { max-width: fit-content; margin-left: auto; margin-right: auto; }
.filter .c .reset { background-color: var(--tocsidebar-bg); border: medium solid #d9d9d9; border-radius: 15px; color: #d9d9d9; font-size: 1em; font-weight: bold; margin-bottom: 2em; padding: 1.2em 3em; }
/* Hide All Non-SC Related Content Upon Filter Activation */
.hidden, .switch input, .switch:has(#contents:checked) ~ .filter, .switch:has(#filters:checked) ~ #table-of-contents, .switch:has(#filters:checked) ~ .toc, body:has(#filter :checked) #abstract, body:has(#filter :checked) #sotd, #toc:has(.filter :checked) ~ #introduction, #toc:has(.filter :checked) ~ #considerations, #toc:has(.filter :checked) ~ #acknowledgments, #toc:has(.filter :checked) ~ #changelog, #toc:has(.filter :checked) ~ section > p ~ section, #toc:has(.filter :checked) ~ section > p, #toc:has(.filter :checked) ~ section > ul, #toc:has(.filter :checked) ~ section > .summary, #toc:has(.filter :checked) ~ section > p ~ section > section
{ clip: rect(0,0,0,0) !important; content-visibility: hidden; height: 1px; overflow: hidden; position: absolute; width: 1px; }
/* Show Filtered Content */
#toc:has(.filter :checked) ~ section section:has(.mt), #toc:has(.filter :checked) ~ section section:has(.ht),
#toc:has(.filter :checked) ~ section section:has(.mh), #toc:has(.filter :checked) ~ section section:has(.mm), #toc:has(.filter :checked) ~ section section:has(.ml),
#toc:has(.filter :checked) ~ section section:has(.eh), #toc:has(.filter :checked) ~ section section:has(.em), #toc:has(.filter :checked) ~ section section:has(.el),
#toc:has(.filter :checked) ~ section section:has(.wh), #toc:has(.filter :checked) ~ section section:has(.wm), #toc:has(.filter :checked) ~ section section:has(.wl),
#toc:has(.filter :checked) ~ section section:has(.ch), #toc:has(.filter :checked) ~ section section:has(.cm), #toc:has(.filter :checked) ~ section section:has(.cl),
#toc:has(.filter :checked) ~ section section:has(.afnor), #toc:has(.filter :checked) ~ section section:has(.aws), #toc:has(.filter :checked) ~ section section:has(.azure), #toc:has(.filter :checked) ~ section section:has(.gpf), #toc:has(.filter :checked) ~ section section:has(.gr491), #toc:has(.filter :checked) ~ section section:has(.greenit), #toc:has(.filter :checked) ~ section section:has(.opquast), #toc:has(.filter :checked) ~ section section:has(.sdgs), #toc:has(.filter :checked) ~ section section:has(.ca), #toc:has(.filter :checked) ~ section section:has(.cp), #toc:has(.filter :checked) ~ section section:has(.cs), #toc:has(.filter :checked) ~ section section:has(.ai), #toc:has(.filter :checked) ~ section section:has(.assets), #toc:has(.filter :checked) ~ section section:has(.compatibility), #toc:has(.filter :checked) ~ section section:has(.content), #toc:has(.filter :checked) ~ section section:has(.css), #toc:has(.filter :checked) ~ section section:has(.e-waste), #toc:has(.filter :checked) ~ section section:has(.education), #toc:has(.filter :checked) ~ section section:has(.governance), #toc:has(.filter :checked) ~ section section:has(.hardware), #toc:has(.filter :checked) ~ section section:has(.html), #toc:has(.filter :checked) ~ section section:has(.ideation), #toc:has(.filter :checked) ~ section section:has(.javascript), #toc:has(.filter :checked) ~ section section:has(.kpis), #toc:has(.filter :checked) ~ section section:has(.marketing), #toc:has(.filter :checked) ~ section section:has(.networking), #toc:has(.filter :checked) ~ section section:has(.patterns), #toc:has(.filter :checked) ~ section section:has(.performance), #toc:has(.filter :checked) ~ section section:has(.report), #toc:has(.filter :checked) ~ section section:has(.research), #toc:has(.filter :checked) ~ section section:has(.equity), #toc:has(.filter :checked) ~ section section:has(.software), #toc:has(.filter :checked) ~ section section:has(.strategy), #toc:has(.filter :checked) ~ section section:has(.ui), #toc:has(.filter :checked) ~ section section:has(.usability),
.mt, .ht, .mh, .mm, .ml, .eh, .em, .el, .wh, .wm, .wl, .ch, .cm, .cl, .afnor, .aws, .azure, .gpf, .gr491, .greenit, .opquast, .sdgs, .ca, .cp, .cs, .ai, .assets, .compatibility, .content, .css, .e-waste, .education, .governance, .hardware, .html, .ideation, .javascript, .kpis, .marketing, .networking, .patterns, .performance, .report, .research, .equity, .software, .strategy, .ui, .usability,
.mt ~ section:last-child, .ht ~ section:last-child, .mh ~ section:last-child, .mm ~ section:last-child, .ml ~ section:last-child, .eh ~ section:last-child, .em ~ section:last-child, .el ~ section:last-child, .wh ~ section:last-child, .wm ~ section:last-child, .wl ~ section:last-child, .ch ~ section:last-child, .cm ~ section:last-child, .cl ~ section:last-child , .afnor ~ section:last-child, .aws ~ section:last-child, .azure ~ section:last-child, .gpf ~ section:last-child, .gr491 ~ section:last-child, .greenit ~ section:last-child, .opquast ~ section:last-child, .sdgs ~ section:last-child, .ca ~ section:last-child, .cp ~ section:last-child, .cs ~ section:last-child, .ai ~ section:last-child, .assets ~ section:last-child, .compatibility ~ section:last-child, .content ~ section:last-child, .css ~ section:last-child, .e-waste ~ section:last-child, .education ~ section:last-child, .governance ~ section:last-child, .hardware ~ section:last-child, .html ~ section:last-child, .ideation ~ section:last-child, .javascript ~ section:last-child, .kpis ~ section:last-child, .marketing ~ section:last-child, .networking ~ section:last-child, .patterns ~ section:last-child, .performance ~ section:last-child, .report ~ section:last-child, .research ~ section:last-child, .equity ~ section:last-child, .software ~ section:last-child, .strategy ~ section:last-child, .ui ~ section:last-child, .usability ~ section:last-child
{ clip: auto; content-visibility: visible !important; height: auto !important; overflow: unset !important; position: static !important; width: auto !important; }
@media (scripting: none) { .switch, .filter { display: none; } }

JavaScript

The below must be included within the <head> element:

Note:

<script>
	window.addEventListener("click", (event) => { addFilter(); updateQueryString(); });
	window.addEventListener("load", function() { queryFilter(); });
	function addFilter() {
		window.addEventListener('change', filterData); filterData(); }
	function htmlInsert() {
		let toc = document.querySelector('#toc');
		let newItems = `
		<section id="switch" class="switch">
			<form>
				<label for="contents"><input type="radio" id="contents" name="switcher" checked>Contents</label>
				<label for="filters"><input type="radio" id="filters" name="switcher">Filters</label>
			</form>
		</section>`;
		toc.insertAdjacentHTML('afterbegin', newItems);
		let newItems2 = `
		<section id="filter" class="filter">
			<h2 class="introductory">Content Filters</h2>
			<form>
				<fieldset class="testable">
					<legend><a href="#wsg-layers-of-guidance">Testable</a></legend>
					<label for="mt"><input type="checkbox" id="mt" name="test">Machine-testable</label>
					<label for="ht"><input type="checkbox" id="ht" name="test">Human-testable</label>
				</fieldset>
				<fieldset class="materials">
					<legend><a href="#reporting">Materials</a></legend>
					<label for="mh"><input type="checkbox" id="mh" name="materials">High</label>
					<label for="mm"><input type="checkbox" id="mm" name="materials">Medium</label>
					<label for="ml"><input type="checkbox" id="ml" name="materials">Low</label>
				</fieldset>
				<fieldset class="energy">
					<legend><a href="#reporting">Energy</a></legend>
					<label for="eh"><input type="checkbox" id="eh" name="energy">High</label>
					<label for="em"><input type="checkbox" id="em" name="energy">Medium</label>
					<label for="el"><input type="checkbox" id="el" name="energy">Low</label>
				</fieldset>
				<fieldset class="water">
					<legend><a href="#reporting">Water</a></legend>
					<label for="wh"><input type="checkbox" id="wh" name="water">High</label>
					<label for="wm"><input type="checkbox" id="wm" name="water">Medium</label>
					<label for="wl"><input type="checkbox" id="wl" name="water">Low</label>
				</fieldset>
				<fieldset class="emissions">
					<legend><a href="#reporting">Emissions</a></legend>
					<label for="ch"><input type="checkbox" id="ch" name="emissions">High</label>
					<label for="cm"><input type="checkbox" id="cm" name="emissions">Medium</label>
					<label for="cl"><input type="checkbox" id="cl" name="emissions">Low</label>
				</fieldset>
				<fieldset class="standards">
					<legend><a href="#relationships">Standards</a></legend>
					<label for="afnor"><input type="checkbox" id="afnor" name="standard">AFNOR</label>
					<label for="aws"><input type="checkbox" id="aws" name="standard"><abbr title="Amazon Web Services">AWS</abbr> <abbr title="Well-Architected Framework">WAF</abbr></label>
					<label for="azure"><input type="checkbox" id="azure" name="standard"><abbr title="Microsoft Azure">Azure</abbr> WAF</label>
					<label for="gpf"><input type="checkbox" id="gpf" name="standard"><abbr title="General Policy Framework for the Ecodesign of Digital Services">GPF</abbr></label>
					<label for="gr491"><input type="checkbox" id="gr491" name="standard">GR491</label>
					<label for="greenit"><input type="checkbox" id="greenit" name="standard">GreenIT</label>
					<label for="opquast"><input type="checkbox" id="opquast" name="standard">OpQuast</label>
					<label for="sdgs"><input type="checkbox" id="sdgs" name="standard"><abbr title="United Nations">UN</abbr> SDGs</label>
				</fieldset>
				<fieldset class="considerations">
					<legend><a href="#considerations">Considerations</a></legend>
					<label for="ca"><input type="checkbox" id="ca" name="considerations">Accessibility</label>
					<label for="cp"><input type="checkbox" id="cp" name="considerations">Privacy</label>
					<label for="cs"><input type="checkbox" id="cs" name="considerations">Security</label>
				</fieldset>
				<fieldset class="categories">
					<legend>Categories</legend>
					<label for="ai"><input type="checkbox" id="ai" name="category">AI</label>
					<label for="assets"><input type="checkbox" id="assets" name="category">Assets</label>
					<label for="compatibility"><input type="checkbox" id="compatibility" name="category">Compatibility</label>
					<label for="content"><input type="checkbox" id="content" name="category">Content</label>
					<label for="css"><input type="checkbox" id="css" name="category">CSS</label>
					<label for="e-waste"><input type="checkbox" id="e-waste" name="category">E-Waste</label>
					<label for="education"><input type="checkbox" id="education" name="category">Education</label>
					<label for="governance"><input type="checkbox" id="governance" name="category">Governance</label>
					<label for="hardware"><input type="checkbox" id="hardware" name="category">Hardware</label>
					<label for="html"><input type="checkbox" id="html" name="category">HTML</label>
					<label for="ideation"><input type="checkbox" id="ideation" name="category">Ideation</label>
					<label for="javascript"><input type="checkbox" id="javascript" name="category">JavaScript</label>
					<label for="kpis"><input type="checkbox" id="kpis" name="category">KPIs</label>
					<label for="marketing"><input type="checkbox" id="marketing" name="category">Marketing</label>
					<label for="networking"><input type="checkbox" id="networking" name="category">Networking</label>
					<label for="patterns"><input type="checkbox" id="patterns" name="category">Patterns</label>
					<label for="performance"><input type="checkbox" id="performance" name="category">Performance</label>
					<label for="report"><input type="checkbox" id="report" name="category">Reporting</label>
					<label for="research"><input type="checkbox" id="research" name="category">Research</label>
					<label for="equity"><input type="checkbox" id="equity" name="category">Social Equity</label>
					<label for="software"><input type="checkbox" id="software" name="category">Software</label>
					<label for="strategy"><input type="checkbox" id="strategy" name="category">Strategy</label>
					<label for="ui"><input type="checkbox" id="ui" name="category">UI</label>
					<label for="usability"><input type="checkbox" id="usability" name="category">Usability</label>
				</fieldset>
				<div class="c">
					<input class="reset" type="reset" value="Reset the filters">
				</div>
			</form>
		</section>`;
		toc.insertAdjacentHTML('beforeend', newItems2); }
	function autoScroll() {
		document.addEventListener('click', event => {
		if (event.target.matches('.filter input')) {
			document.getElementById('user-experience-design').scrollIntoView({ behavior: 'smooth', block: 'start' });
			const inputs = document.querySelectorAll('.filter input');
			const noneChecked = ![...inputs].some(input => input.checked);
			if (noneChecked) { window.scrollTo({ top: 0, behavior: 'smooth' }); } } }); }
	function filterData() {
		filterNote('mt', '[data-testable="machine"]');
		filterNote('ht', '[data-testable="human"]');
		filterNote('mh', '[data-materials="high"]');
		filterNote('mm', '[data-materials="medium"]');
		filterNote('ml', '[data-materials="low"]');
		filterNote('eh', '[data-energy="high"]');
		filterNote('em', '[data-energy="medium"]');
		filterNote('el', '[data-energy="low"]');
		filterNote('wh', '[data-water="high"]');
		filterNote('wm', '[data-water="medium"]');
		filterNote('wl', '[data-water="low"]');
		filterNote('ch', '[data-emissions="high"]');
		filterNote('cm', '[data-emissions="medium"]');
		filterNote('cl', '[data-emissions="low"]');
		filterNote('afnor', '[data-standard*="AFNOR"]');
		filterNote('aws', '[data-standard*="AWS WAF"]');
		filterNote('azure', '[data-standard*="Azure WAF"]');
		filterNote('gpf', '[data-standard*="GPF"]');
		filterNote('gr491', '[data-standard*="GR491"]');
		filterNote('greenit', '[data-standard*="GreenIT"]');
		filterNote('opquast', '[data-standard*="OpQuast"]');
		filterNote('sdgs', '[data-standard*="SDGs"]');
		filterNote('ca', '[data-considerations*="Accessibility"]');
		filterNote('cp', '[data-considerations*="Privacy"]');
		filterNote('cs', '[data-considerations*="Security"]');
		filterNote('ai', '[data-categories*="AI"]');
		filterNote('assets', '[data-categories*="Assets"]');
		filterNote('compatibility', '[data-categories*="Compatibility"]');
		filterNote('content', '[data-categories*="Content"]');
		filterNote('css', '[data-categories*="CSS"]');
		filterNote('e-waste', '[data-categories*="E-Waste"]');
		filterNote('education', '[data-categories*="Education"]');
		filterNote('governance', '[data-categories*="Governance"]');
		filterNote('hardware', '[data-categories*="Hardware"]');
		filterNote('html', '[data-categories*="HTML"]');
		filterNote('ideation', '[data-categories*="Ideation"]');
		filterNote('javascript', '[data-categories*="JavaScript"]');
		filterNote('kpis', '[data-categories*="KPIs"]');
		filterNote('marketing', '[data-categories*="Marketing"]');
		filterNote('networking', '[data-categories*="Networking"]');
		filterNote('patterns', '[data-categories*="Patterns"]');
		filterNote('performance', '[data-categories*="Performance"]');
		filterNote('report', '[data-categories*="Reporting"]');
		filterNote('research', '[data-categories*="Research"]');
		filterNote('equity', '[data-categories*="Social Equity"]');
		filterNote('software', '[data-categories*="Software"]');
		filterNote('strategy', '[data-categories*="Strategy"]');
		filterNote('ui', '[data-categories*="UI"]');
		filterNote('usability', '[data-categories*="Usability"]');

		// Updates TOC based on filters
		const toc = document.querySelector('#toc > .toc');
		if (toc) { toc.querySelectorAll('a').forEach(link => {
			link.parentElement.classList.remove('hidden');
			link.classList.remove('hidden'); }); }
		document.querySelectorAll('body > section > section').forEach(section => {
		let style = window.getComputedStyle(section);
		if (style.position === 'absolute') {
			const toc = document.querySelector('#toc > .toc');
			if (toc) {
			toc.querySelectorAll('a').forEach(link => {
				const str = link.href;
				const result = str.split('#')[1];
				if (section.id === result || result === "abstract" || result === "sotd" || result === "considerations" || result === "acknowledgments" || result === "changelog") {
					link.classList.add('hidden'); } else if (
				result === "introduction") {
					link.parentElement.classList.add('hidden'); } }); } } });
		document.querySelectorAll('a').forEach(a => {
			if (a.classList.contains('hidden')) {
				a.setAttribute('tabindex', '-1'); } else { a.removeAttribute('tabindex');
			} }); }
	function filterNote(name, attr) {
		let countTotal = " (" + document.querySelectorAll(attr).length + ")";
		if (document.getElementById(name).parentElement.innerHTML.includes(countTotal)) { } else {
			document.getElementById(name).parentElement.innerHTML += countTotal; }
		if (document.getElementById(name).checked == true) {
			document.querySelectorAll(attr).forEach(function (elem) {
				elem.classList.add(name); }); } else {
			document.querySelectorAll(attr).forEach(function (elem) {
				elem.classList.remove(name); }); }
		return(name + attr); }
	function getQueryValue(input) {
		let labelText = input.parentElement.textContent.trim().toLowerCase().toLowerCase().replace(/\s+/g, '-').replace(/\(.*?\)/g, '').replace(/-$/, '');
		if (["high", "medium", "low"].includes(labelText)) {
			return `${input.id}-${labelText}`;
		} else { return `${labelText}`; } }
	function updateQueryString() {
		autoScroll();
		const params = new URLSearchParams(window.location.search);
		params.delete('filter');
		document.querySelectorAll('.filter input:checked').forEach(input => params.append('filter', getQueryValue(input)));
		const query = params.toString();
		const hash = window.location.hash;
		let newUrl = window.location.pathname;
		if (window.location.search || query) { newUrl += '?' + query; }
		history.replaceState(null, '', newUrl += hash); }
	function queryFilter() {
		// Query String Filtering onLoad
		const filters = document.querySelectorAll('.filter');
		if (!filters.length) return;
		document.querySelectorAll('.filter input').forEach(input => {
			input.addEventListener('change', updateQueryString); });
		document.querySelectorAll('form').forEach(form => {
			form.addEventListener('reset', () => setTimeout(updateQueryString, 0)); });
		const queryParams = window.location.search.substring(1).toLowerCase().split('&').filter(Boolean);
		document.querySelectorAll('.filter label').forEach(label => {
			const input = label.querySelector('input');
			if (!input || !input.id) return;
			const rawText = Array.from(label.childNodes).filter(node => node.nodeType === Node.TEXT_NODE).map(node => node.textContent).join(' ').trim().toLowerCase();
			let text = rawText.replace(/\s+/g, '-').replace(/\(.*?\)/g, '').replace(/-$/, '');
			let result = ["high", "medium", "low"].includes(text)
				? `${input.id}-${text}`
				: text;
			result = `filter=${result}`;
			input.checked = queryParams.includes(result); });
		if (typeof filterData === 'function') { filterData(); } }
</script>

The below must be included in the respecConfig section:

var respecConfig = {
	postProcess: [htmlInsert, addFilter, queryFilter] }