Beispiel:Menue mit popover.html

Aus SELFHTML-Wiki
Wechseln zu: Navigation, Suche
<!DOCTYPE html>
	<html lang='de'>
	<head>
		<meta charset='UTF-8'>
		<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<!--		<link rel='stylesheet' type='text/css' media='screen' href='./Beispiel:SELFHTML-Beispiel-Grundlayout.css'>  -->
<!--		<link rel='stylesheet' type='text/css' media='screen' href='https://wiki.selfhtml.org/extensions/Selfhtml/example.php/Beispiel:SELFHTML-Beispiel-Grundlayout.css'> -->
		<title>Beispiel: Seitennavigation mit popover</title>

		<style>
body {
	margin: 0em; padding: 0;  
	margin-top: 2em;
	max-width: 55em;
}
main {
	padding: 1em;
}
pre {
	border: thin solid black;
	tab-size: 2;
	padding: 1em;
	width: fit-content;
}

/* menu.css */
#sitenav {
/* Allgemeines zum Aussehen */
	position: absolute;
	box-sizing: border-box;
	top: 1em;
	left: 2em;
	& *, & a { 
		background-color: lightgray; 
		color: black;
	}
	& ul, & li, & a, & button { 
		margin: 0;
		padding: 0;
	}
	& button, & a {
		font-size: 1em;
		margin: .2em;
		padding-left: .4em;
		padding-right: .4em;
		height: 1.6em;
		line-height: 1.6em; 
		display: inline-block;
	}
	& button {
		border: none;
		cursor: pointer;
		font: inherit;
		text-align: inherit;
	}
	& a { 
		text-decoration: none; 
	}
	& li { 
		list-style-type: none; 
	}
	& *::before,
	& *::after { 
		font-family: arial_unicode_ms; 
		font-size: .9em;
		display: inline-block;
	}
	& *::after {
		text-align: end;
		width: 1.5em;
	}
	& > button {
		height: 2.5em;
		line-height: 2em;
		border: thin solid black;
		border-radius: .2em;
		&::after {
			font-size: 1.2em;
		}
	}
	& .langselect {
		& button {
			&::after {
				padding-top: .2em;
				vertical-align: top;
			}
		}
		& img { 
			height: 1.4em;
			margin-top: .1em;
		}
	}
	& [popover] {
		border: none;
		border-radius: 0 .5em .5em .5em;
	}
/* Positionierung der Untermenüs */
	& [popover] {
		/* position-anchor: auto; Funktioniert noch nicht, daher siehe weiter unten "Zuordnung ..." */
		position: absolute;
		top: anchor(bottom);
		left: anchor(left);
	}
	& [popover] [popover] {
		top: anchor(top);
		left: calc(anchor(right) + .4em);
	}
/* Die Symbole hinter den Schaltflächen */
	& button::after {
		content: '☰' / '';
	}
	& button:has(+:popover-open)::after {
		content: '×' / '';
	}
	& [popover] button::after {
		content: '►' / ''; 
	}
	& [popover] button:has(+:popover-open)::after {
		content: '◄' / ''; 
	}
/* Link zur aktuellen Seite */
	& li[aria-current] a[tabindex]::before { 
		content: '►' / 'Aktuelle Seite';
		text-align: start;
		width: 1.5em;
	}
	& [popover] button:has(+ [popover] > li[aria-current]) {
		border-left: 2px solid black; 
		margin-left: 0;
	}
/* Änderungen für beite Bildschirme */
	@media (min-width: 45em) {
		top: 0;
		left: 0;
		& > button {
			display: none;
		}
		& > [popover] {
			display: flex;
			text-align: center;
			padding-left: 5em;
			left: 0;
			top: 0;
			width: 100vw;
			border-radius: 0;
		}
		& [popover] [popover] {
			text-align: left;
			top: anchor(bottom);
			left: anchor(left);
		}
		& [popover] [popover] [popover] {
			top: anchor(top);
			left: calc(anchor(right) + .4em);
		}
		& > [popover] > li > button::after {
			content: '▼' / ''; 
		}
		& > [popover] > li > button:has(+:popover-open)::after {
			content: '▲' / ''; 
		}
		& > [popover] > li > button:has(+ [popover] > li[aria-current]) {
			border-left: none; 
		}
		& > [popover] > li:has(li[aria-current]) { 
			border-bottom: 2px solid black; 
		}
	}
}

/* Zuordnung der popover zu ihrem anchor */
	#level1_toggle {
		anchor-name: --level1_toggle
	}
	#level1  {
		position-anchor: --level1_toggle;
	}
	#level11_toggle {
		anchor-name: --level11_toggle
	}
	#level11  {
		position-anchor: --level11_toggle;
	}
	#level12_toggle {
		anchor-name: --level12_toggle
	}
	#level12  {
		position-anchor: --level12_toggle;
	}
	#level24_toggle {
		anchor-name: --level24_toggle
	}
	#level24  {
		position-anchor: --level24_toggle;
	}
	#level25_toggle {
		anchor-name: --level25_toggle
	}
	#level25  {
		position-anchor: --level25_toggle;
	}
	#langselect_toggle {
		anchor-name: --langselect_toggle
	}
	#langselect  {
		position-anchor: --langselect_toggle;
	}

/* Skip-Link s. auch https://wiki.selfhtml.org/wiki/Skip-Link */
#skip-link { 
	position: absolute; 
	left: 0; top: 0em; 
	background: black; 
	color: white; 
	padding: 0.25em; 
	transform: translateY(-100%);
	transition: 0.2s transform; 
	z-index: 1001; 
	&:focus { 
		transform: translateY(0); 
	}			
} 

/* Für AT */
.visually-hidden { /* Siehe auch https://wiki.selfhtml.org/wiki/Visually-hidden */
	clip: rect(0 0 0 0);
	clip-path: inset(50%);
	height: 1px;
	overflow: hidden;
	position: absolute;
	white-space: nowrap;
	width: 1px;
}
/* Ende menu.css */
		</style>
	
	<script type="module">
		// Polyfill für anchor: https://github.com/oddbird/css-anchor-positioning?tab=readme-ov-file
		if (!("anchorName" in document.documentElement.style)) {
			let erlaubnis = confirm("Diese Seite benötigt den anchor-Polyfill von oddbird, der von der Domain unpkg.com geladen wird. Ist das OK?");
			if(erlaubnis) {
				import("https://unpkg.com/@oddbird/css-anchor-positioning");
				console.log('Polyfill wird geladen.');
			}
		}
	</script>				

	</head>

	<body>

		<a id='skip-link' href='#main'>zum Hauptinhalt</a>
		<nav id='sitenav' aria-labelledby='sitenav-label'>
			<h2 id='sitenav-label' class='visually-hidden'>Site-Navigation</h2>
			<button id="level1_toggle" popovertarget="level1" popovertargetaction="toggle">Menü</button>
			<ul id="level1" popover anchor="level1-toggle">
				<li><a href='/'>Startseite</a></li>
				<li>
					<button id="level11_toggle" popovertarget="level11" popovertargetaction="toggle">Bereich 1</button>
					<ul id="level11" popover>
						<li><a href=''>Seite 1.1</a></li>
						<li><a href=''>Seite 1.2</a></li>
						<li><a href=''>Seite 1.3</a></li>
					</ul>						
				</li>
				<li aria-current='page'>
					<button id="level12_toggle" popovertarget="level12" popovertargetaction="toggle">Bereich 2</button>
					<ul id="level12" popover>
						<li><a href=''>Seite 2.1</a></li>
						<li><a href=''>Seite 2.2</a></li>
						<li><a href=''>Seite 2.3</a></li>
						<li aria-current='page'>
							<button id="level24_toggle" popovertarget="level24" popovertargetaction="toggle">Bereich 2.4</button>
							<ul id="level24" popover>
								<li><a href=''>Seite 2.4.1</a></li>
								<li><a href=''>Seite 2.4.2</a></li>
								<li aria-current='page'><a tabindex=0>aktuelle Seite</a></li>
								<li><a href=''>Seite 2.4.4</a></li>
							</ul>
						</li>
						<li>
							<button id="level25_toggle" popovertarget="level25" popovertargetaction="toggle">Bereich 2.5</button>
							<ul id="level25" popover>
								<li><a href=''>Seite 2.5.1</a></li>
								<li><a href=''>Seite 2.5.2</a></li>
								<li><a href=''>Seite 2.5.3</a></li>
							</ul>
						</li>
						<li><a href=''>Seite 2.6</a></li>
					</ul>
				</li>
				<li><a href=''>Seite 3</a></li>
				<li><a href=''>Seite 4</a></li>
				<li><a href=''>Impressum</a></li>
				<li class='langselect'>
					<button id="langselect_toggle" popovertarget="langselect" popovertargetaction="toggle"><img src='https://wiki.selfhtml.org/images/8/87/Language-Icon.svg' alt='Sprachwahl, Language selection, Sélection de la langue, Selección de idioma'></button>
					<ul id="langselect" popover>
						<li><a href=''>Deutsch</a></li>
						<li><a href=''>English</a></li>
						<li><a href=''>Francais</a></li>
						<li><a href=''>Español</a></li>
					</ul>
				</li>
			</ul>
		</nav>

		<main id='main'>
			<h1>Beispiel: Seitennavigation mit popover</h1>
			<section>
				<h2>Was?</h2>
				<p>Die Navigation soll folgende Eigenschaften haben:</p>
				<ul>
					<li>Bis zu drei Ebenen.</li>
					<li>Auf schmalen Viewports vertikal angeordnet.</li>
					<li>Auf genügend breiten Viewports horizontal angeordnet.</li>
					<li>Bedienung mit Maus, Touch und Tastatur.</li>
				</ul>
			</section>
			<section>
				<h2>Wie?</h2>
				<p>Basis der Navigation ist eine verschachtelte Liste. Das Öffnen des Menüs bzw. der Untermenüs erfolgt über einen Button direkt vor den UL mit der popover-Technik. Die Positionierung der Untermenüs erfolgt über die anchor-Technik.</p>
				<p>Vor die Navigation wird noch ein Skip-Link gesetzt, um Tastatur-Benutzern sowie Besuchern mit assistiven Techniken zu ermöglichen, direkt zum Inhalt zu springen. Per css wird dieser Link aus dem Viewport geschoben und nur bei Focus sichtbar.</p>
				<p>Um Besucher mit assistiven Techniken zu informieren, erhält die Navigation noch eine visuell versteckte Überschrift.</p>
				<p>Per css wird die Navigationsliste je nach Seitenbreite im Quer- oder Hochformat angeordnet.</p>
				<p>Auf schmalen Viewports wird die Navigation hinter einem Menü-Button versteckt. Bei Klick öffnet sich die erste Ebene nach unten und die zweite und dritte nach rechts. Auf genügend breiten Viewports ist der Menü-Button ausgeblendet und die erste Ebene ist immer sichtbar und waagrecht angeordnet. Die zweite Ebene öffnet nach unten, die dritte nach rechts.</p>
				<p>In die Navigation wurde auch ein Untermenü für die Sprachwahl eingebaut. Damit das Sprachwahlmenü unabhängig von der Sprache erkannt wird, wurde hier das durch die „Language Icon Initiative” vorgeschlagene Standard-Icon für Sprachwahlmenüs gewählt.</p>
				<p>In der Beispieldatei sind HTML und CSS in einer Datei zusammengefasst. Im Einsatz sollten die Teile in getrennten Dateien untergebracht werden.</p>
		  </section>
			<section>
				<h2>Probleme</h2>
				<p>Stand Mai 2025 unterstützten nur Chrome und Edge die anchor-Technik, daher wird in den Browsern, die die anchor-Technik nicht unterstützen, der Polyfill von <a href="https://github.com/oddbird/css-anchor-positioning?tab=readme-ov-file">OddBird</a> geladen.</p>
				<p>Leider unterstützt dieser Polyfill nicht die automatische Verknüpfung zwischen dem Button und dem popover, daher müssen die Button eine id haben und die Veknüpfung erfolgt über diese id für jedes button-popover-Paar getrennt.</p>
			</section>
			<secion>
				<h2>HTML</h2>
				<p>Das HTML für ein (Unter-)Menü besteht aus einer Liste mit den Links und einem Button, der diese Liste "öffnet". Für die popover-Technik muss die Liste eine id und das Attribut popover haben. Der Button benötigt die Attribute popovertarget und popovertargetaction. Zusätzlich benötigt der Button z.Zt. noch eine id für die anchor-Technik.</p>
				<pre><code>&lt;button id="level11_toggle" popovertarget="level11" popovertargetaction="toggle">Bereich 1&lt;/button>
&lt;ul id="level11" popover>						
	&lt;li>&lt;a href=''>Seite 1.1&lt;/a>&lt;/li>
	&lt;li>&lt;a href=''>Seite 1.2&lt;/a>&lt;/li>
	&lt;li>&lt;a href=''>Seite 1.3&lt;/a>&lt;/li>
&lt;/ul></code></pre>
				<p>Der link zur aktuellen Seite hat kein href-Attribut, statt dessen aber das Attribut <code>tabindex=0</code>. Die Listenelemente, in denen sich dieser Link befindet, erhalten das Attribut <code>aria-current='page'</code>.</p>
				<pre><code>&lt;li aria-current='page'>&lt;a tabindex=0>aktuelle Seite&lt;/a>&lt;/li></code></pre>
			</secion>
			<section>
				<h2>CSS</h2>
				<p>Die Positionierung der (Unter-)Menüs erfolgt relativ zu den button, die sie öffnen. Dazu muss eine Verbindung zwischen der Liste und dem Button hergestellt werden. In Browsern, die die anchor-Technik unterstützen, erfolgt das über die css-Regel <code>position-anchor: auto;</code>. Da aber der Polyfill diese Regel nicht unterstützt, erfolgt die Verbindung über folgenden CSS-Code:</p>
				<pre><code>#level11_toggle {
	anchor-name: --level11_toggle
}
#level11  {
	position-anchor: --level11_toggle;
}</code></pre>
				<p>Die Positionierung erfolgt dann über:</p>
				<pre><code>position: absolute;
top: anchor(bottom);
left: anchor(left);</code></pre>
				<p>bzw.</p>
				<pre><code>top: anchor(top);
left: calc(anchor(right) + .4em);</code></pre>
				<p>Bei den button, die die Untermenüs öffnen, soll angezeigt werden, ob das Untermenü geöffnet ist. Dieses erfolgt über folgenes CSS, bei dem mit der Pseudoklasse <code>has</code> und den Nachbatrselektor <code>+</code> ermitelt wird, welcher button ein geöffnetes Popover als Nachbarn hat:</p>
				<pre><code>& button::after {
	content: '☰' / '';
}
& button:has(+:popover-open)::after {
	content: '×' / '';
}</code></pre>
				<p>Der Link zur aktuellen Seite witd über die im HTML gesetzten Attribute mit folgendem CSS gekennzeichnet :</p>
				<pre><code>& li[aria-current] a[tabindex]::before { 
	content: '►' / 'Aktuelle Seite';
	text-align: start;
	width: 1.5em;
}
& [popover] button:has(+ [popover] > li[aria-current]) {
	border-left: 2px solid black; 
	padding-left: .2em;
	margin-left: 0;
}</code></pre>
			</section>
		</main>
		<footer>
			<p>Stand: 28. 5. 2025</p>
		</footer>
	</body>
</html>