Das Hotlinking von Premium-Schriften verhindern

Vielleicht hab ihr auch eine Website, die eine „Premium-Schriftart“ verwendet. Das Laden von einem CDN, vielleicht sogar mit externem JavaScript, ist für die Performance und den Datenschutz dabei oft nicht ideal. Deshalb möchtet ihr die Schriftarten wahrscheinlich auf eurem Server hosten. Viele Websites, auf denen ihr Schriftarten kaufen könnt, haben vermutlich eine Lizenzvereinbarung, die in etwa so lautet:

Im Rahmen dieser Lizenz muss die Schriftart angemessen gesichert werden, um zu verhindern, dass unbefugte Websites, die sich der direkten Kontrolle des Lizenznehmers entziehen, auf die Schriftartensoftware zur Anzeige von Inhalten zugreifen, einschließlich Hotlinking oder direktem Zugriff über Webserver-Integration.

Aber wie können wir das Hotlinking verhindern? Das hängt stark vom Webserver und der Infrastruktur ab, die ihr verwendet. Werfen wir mal einen Blick auf ein paar unterschiedliche Ansätze.

Verwendung einer Web Application Firewall wie der Cloudflare WAF

In einem Projekt, in dem ich diese Anforderung hatte, war es nicht möglich, die Serverkonfiguration zu ändern. Aber die gesamte Website befand sich hinter Cloudflare. In Cloudflare gibt es eines spezielle Hotlink Protection, die aber nur für einige wenige Bildformate das Hotlinking unterbindet.

Nach einigen Versuchen habe ich eine Regel gefunden, die auch für Webfonts funktioniert. Ihr könnt sie in der Benutzeroberfläche von Cloudflare unter „Sicherheit -> WAF“ konfigurieren:

Screenshot der Benutzeroberfläche zum Bearbeiten einer Cloudflare WAF-Regel mit dem Namen „Hotlink Protection für Schriften“. Die Steuerelemente wurden verwendet, um die Regel zu erstellen, die auch im Code unten gefunden werden kann.

Alteranativ könnt ihr den Link „Ausdruck bearbeiten“ verwenden und den folgenden Code einfügen:

(http.request.full_uri enthält "/fonts/" und nicht http.referer enthält "example.com" und nicht cf.client.bot und nicht http.user_agent enthält "PTST")

Diese Regel verhindert Hotlinking von jedem Pfad, der den Ordner /fonts/ enthält. Dies könnte etwas zu streng sein. Es könnte auch andere (freie) Schriftarten blockieren, oder solche, die in Plugins gespeichert sind. Ihr könntet stattdessen einen spezifischeren Ordner wie wp-content/themes/euer-theme-slug/assets/fonts/ im Feld „Vollständige URI“ verwenden.

Das andere Feld, das ihr anpassen müsst, ist das Feld „Referer“. Hier verwenden ihr eure eigenen Domänennamen. Es ist wichtig, es auf „enthält nicht“ (oder ähnlich) zu setzen, da wir Anfragen blockieren wollen, die „nicht“ von unserer Domain kommen.

Die Einstellungen „Bekannte Bots“ und „User Agent enthält nicht PTST“ verhindern, dass die Regel angewendet wird, wenn eure Website von Suchmaschinen oder dem Performance-Tool webpagetest.org aufgerufen wird. Die Seitenaufrufe dieser Dienste werden dann also nicht beeinträchtigt.

Schließlich wählt ihr unter „Dann folgendermaßen verfahren…“ die Aktion „Blockieren“ aus, die das Laden der Schriftart mit einem Hotlink verhindert.

Verwendung einer nginx-Konfiguration

Wenn ihr nginx als Webserver verwendet, könnt ihr mit dem ngx_http_referer_module ebenfalls das Hotlinking auf die Premium-Schriften blockieren. Hierzu fügt ihr die folgende location zu eurer Konfiguration hinzu:

location /fonts/ {
    valid_referers example.com www.example.com;
    if ($invalid_referer) {
        return 403;
    }
}

Ihr könnt hier, wie in der Dokumentation beschrieben, statische Domänennamen oder einen regulären Ausdruck und einige andere Werte für die valid_referers verwenden.

Verwendung einer Apache-Konfiguration

Wenn ihr Apache verwendt, was viele Shared-Hosting-Anbieter immer noch tun, könnt ihr in der .htaccess-Datei im Hauptverzeichnis der Website folgendes hinzufügen:

<IfModule mod_rewrite.c>
  RewriteCond %{HTTP_REFERER} !^https?://(www\.)?example\.com [NC]
  RewriteCond %{REQUEST_URI} /fonts/
  RewriteRule . - [F,NC]
</IfModule>

Auch hiermit wird das Hotlinking von anderen Domains blockiert. Weitere RewriteCond Werte findet ihr in der Apache-Dokumentation.

Einschränkungen

Sowohl das Beispiel für nginx, als auch das für Apache, beachten keine Suchmaschinen-Bots und Performance-Tools. Es wäre zwar möglich, sie nicht zu blockieren, doch das wäre sehr viel komplexer in der Einrichtung.

Ihr solltet euch auch bewusst sein, dass all diese Lösungen nicht „sicher“ sind, da sie Hotlinking nicht wirklich verhindern können. Wenn andere Premium-Schriftarten, die auf eurem Server gespeichert sind, für deren fremde Websites verwenden möchte, könnten sie einen „Proxy“ schreiben und den HTTP_REFERER Header so manipulieren, dass es so aussieht, als kämen die Anfragen von eurem Server. Aber ich würde sagen, dass eine der in diesem Blogbeitrag vorgestellten Maßnahmen „angemessen sicher“ sein sollte, wie es in der Lizenzvereinbarung heißt. Da ich euch aber keinen juristischen Rat geben kann, fragt besser bei Schriften-Shops nach, ob eure Maßnahmen für diese ausreichend sind.

Fazit

Die Verwendung einer schönen Premium-Schriftart kann eure Website von anderen abheben. Wenn ihr sie auf eurem eigenen Server hostet, verbessert dies die Ladegeschwindigkeit und den Datenschutz. Lest aber immer in der Lizenzvereinbarung nach, ob ihr eine Anforderung für das Unterbinden von Hotlinking findet. Andernfalls könnten ihr in echte Schwierigkeiten geraten.

Anhang-Seiten zu Mediendateien umleiten

Bei der Arbeit an einem Projekt habe ich diese Woche eine interessante Entdeckung gemacht. Die Website hatte nur zwei Kommentare – es ist ein WooCommerce-Shop, also sind Kommentare nicht wirklich wichtig. Diese beiden Kommentare wurden aber auf einer Anhang-Seite abgegeben. Die Seite hatte den Titel „contact-2“ und war ein Header-Bild. Vermutlich hatte jemand nach der „Kontakt“-Seite der Website gesucht, dann diese Seite gefunden und dann das Kommentare-Formular mit einem Kontaktformular verwechselt.

Was ist eine Anhang-Seite?

Für diejenigen von euch, die noch nie auf eine Anhang-Seite gestoßen sind, möchte ich kurz erklären, was diese sind. Einige von euch wissen vielleicht, dass für jede Datei, die man in die Medienbibliothek hochlädt, ein Datenbankeintrag angelegt wird. Diese Einträge befinden sich in der Tabelle wp_posts mit dem Beitragstyp attachment. Jede Mediendatei ist also „ein Beitrag“. Und da der Beitragstyp „Anhang“ auf public eingestellt ist, hat jede Mediendatei auch eine Anhang-Seite.

In dem oben genannten Fall wurde eine Datei mit dem Namen „contact.jpg“ hochgeladen. Da es bereits eine reguläre Seite mit dem Titel „contact“ gab, hat WordPress das Suffix „-2“ angehängt. Ich hatte letztes Jahr einen Blogbeitrag über dieses Suffix geschrieben und wie man es entfernt. Aber was bedeutet das nun? Wenn ihr auf eine URL wie https://example.com/contact-2 zugreift, dann erhaltet ihr eine Seite, die möglicherweise die Vorlage singular.php eures Themes verwendet, es sei denn, das Theme hat eine bessere Vorlage für Anhänge.

Auf dieser Seite seht ihr dann die Mediendatei, im Falle eines Bildes in voller Größe. Auf der Demoseite für das Twenty Nineteen-Theme können ihr eine solche Anhang-Seite sehen. Diese URL verwendet den Parameter attachment_id, der die Post-ID des Anhangs (Mediendatei) angibt. Ihr könnten auch den p Parameter verwenden, der dann eine Weiterleitung macht. Je nachdem wie eure Permalinks eingestellt sind, könntet ihr aber auch eine „schöne URL“ wie die bereits erwähnte „contact-2“ URL sehen.

Warum ihr eine Anhang-Seite (nicht) haben wollt

Wenn ihr einen medienlastigen Blog habt und Kommentare zu euren Mediendateien zulassen möchtet, kann eine Anhang-Seite sinnvoll sein. Aber für die meisten Websites würden sie nur die Anzahl der Seiten auf eurer Website „aufblähen“. Sie könnten auch mit einigen „normalen Seiten“ in Konflikt stehen.

Diese Anhang-Seiten sind auch für Suchmaschinen sichtbar und da sie nur den Dateinamen als Titel verwenden, kann es passieren, dass eine solche Seite (mehr) Besuche hat, die ihr eigentlich auf den normalen Seiten haben wollt. Glücklicherweise tauchen die Anhang-Seiten nicht in den Sitemaps auf.

Wie entfernt man die Anhang-Seiten?

Ihr könnt sie nicht wirklich „entfernen“. Ihr könnt sie „nicht öffentlich“ machen, aber es gibt einen einfacheren Weg. Wenn ihr eine neue WordPress-Seite mit WordPress 6.4 eingerichtet habt, können ihr jetzt aufhören weiterzulesen, denn ihr habt schon eine Lösung.

Wenn eure WordPress-Installation älter ist, können ihr eine neue Option verwenden, die in WordPress 6.4 eingeführt wurde, um die URL einer Anhang-Seite auf die URL einer Mediendatei umzuleiten.

Die Option mit der WP-CLI setzen

Ich verwende für solche Aufgaben normalerweise die WP-CLI. Wenn ihr sie auch einsetzt, können ihr den folgenden Befehl ausführen:

wp option set wp_attachment_pages_enabled 0

Ein Wert von 0 für die Option wp_attachment_pages_enabled deaktiviert die Anhang-Seiten und die Funktion redirect_canonical() würde dann auf die Mediendatei umleiten.

Die options.php Seite verwenden

Wenn ihr die WP-CLI nicht verwendet oder es nicht könnt, dann können ihr alternativ eine „versteckte Funktion“ im WordPress-Dashboard verwenden. Navigiert hierzu zur URL wp-admin/options.php im Dashboard. Dort werden alle Optionen angezeigt und manche können auch bearbeitet werden. Sucht hier nach dem Namen der Option, ändern den Wert von 1 auf 0 und speichern die Optionen über die Button „Änderungen speichern“ am Ende der Optionsseite.

Ein SEO-Plugin verwenden

Einige SEO-Plugins bieten ebenfalls die Möglichkeit, die Seiten mit den Anhängen umzuleiten (oder sie anders zu deaktivieren). Ich verwende Yoast SEO auf den meisten meiner Seiten und hier finden ihr die Einstellung für Anhang-Seiten unter „Yoast SEO > Einstellungen > Erweitert > Medienseiten“. In diesem Abschnitt stellt die Option „Aktiviere medien Seiten“ auf „aus“. Wenn ihr stattdessen die Anhang-Seiten doch verwenden möchtet, können ihr hier einige andere Einstellungen für Anhang-Seiten vornehmen.

Fazit

Es ist leicht zu vergessen, dass es Anhang-Seiten in WordPress gibt. Wahrscheinlich gab es in der Vergangenheit gute Gründe, sie zu haben. Aber auf einer typischen Website will man sie wohl eher nicht haben. Glücklicherweise können ihr sie jetzt ohne zusätzliche Plugins „abschalten“.

Ein Navigationsmenü durch Hinzufügen einer Beschriftung barrierefreier machen

Nach den 24 Beiträgen im Adventskalender vor Weihnachten, habe ich mir im Januar eine Auszeit genommen, um wieder Energie zu tanken und um weitere und andere Themen zu sammeln, über die ich schreiben möchte. Weil ich mich aktuell auf eine Zertifizierung für Barrierefreiheit vorbereite, möchte ich einige Blogbeiträge zu diesen Themen schreiben. Heute gibt es den ersten zu diesem Thema, ich werde zwischendurch aber vermutlich auch über andere Themen bloggen.

Einstellen einer Menü-Beschriftung bei einem Block-Theme

Wenn ihr eine Website mit nur einer Navigation habt, die den <nav> Tag verwendet, dann seid ihr vielleicht noch auf der sicheren Seite. Es ist aber trotzdem eine gute Idee, der Navigation ein (gutes) Label für die Hauptnavigation zu vergeben. Wenn ihr aber zwei Menüs habt, dann müsst ihr auf jeden Fall Labels vergeben.

Viele Screenreader haben die Möglichkeit, zu „Landmarks“ auf eurer Website zu springen. Damit kann man die Navigationsmenüs auf der Seite leichter finden. In der Regel wird dabei aber nur angesagt, wie viele Links die einzelnen Menüs haben, nicht aber, wo sich das Menü befindet oder welchen „Zweck“ sie hat.

Das Standard-Navigationsmenü in Twenty Twenty-Five

Wenn ihr ein neues WordPress-Projekt mit dem Standard-Theme Twenty Twenty-Five erstellt, werdet ihr ein Navigationsmenü nur mit der „Sample Page“ als Eintrag bekommen. Es würde sogar ein aria-label Attribut haben, das aber leer ist (alle anderen Attributwerte habe ich durch ... ersetzt, da sie in diesem Codeschnipsel nicht wichtig sind):

<nav class="..." aria-label="" data-wp-interactive="..." data-wp-context="...">

Das Navigationsmenü hat allerdings einen „Namen“. Es heißt einfach „Navigation“. Wenn ihr zu „Design > Website-Editor > Navigation“ geht, sollten es bei euch ungefähr so aussehen:

Screenshot des Website-Editors mit geöffneter Ansicht „Navigation“, die ein Menü „Navigation“ mit einem Eintrag „Sample Page“ zeigt.

Hier klickt ihr auf die drei Punkte neben dem Namen und dann auf „Umbenennen“. Jetzt könnt ihr z.B. „Hauptnavigation“ eingeben. Wenn wir jetzt die Seite im Frontend aktualisieren, sehen wir … keine Veränderung. Auch nach der Änderung des Namens ist das aria-abel weiterhin leer.

Lösung: Ändern des Namens nach Duplizieren des Menüs

Ich weiß nicht genau, warum die Änderung des Namen keinen Wert für das aria-label hinzufügt, aber ich habe einen Workaround gefunden. Statt „Umbenennen“ zu verwenden, könnt ihr „Duplizieren“ nutzen. Dadurch wird dem duplizierten Menü ein Suffix „(Kopie)“ hinzugefügt. Jetzt müsst ihr dieses neue Navigationsmenü nur noch an der gewünschten Stelle einsetzen. Navigiert dafür zum Beispiel zu „Design > Website-Editor > Vorlagen > Header“. Dort wählt ihr dann die Kopfzeile aus und klicken auf den Navigationsblock. In den Blockeinstellungen klickt ihr auf die drei Punkte rechts von „Menü“ und wählt dann das neue Menü aus, das ihr zuvor dupliziert und umbenannt habt:

Screenshot der Einstellungen des Blocks „Navigation“ mit geöffneter Auswahl der verfügbaren Menüs.

Wenn wir nun die Kopfzeile mit diesem neuen Menü speichern und das Frontend aktualisieren, haben wir endlich unser aria-label (hier im Code „Hauptnavigation“ statt „Main navigation“ wie im Screenshot):

<nav class="..." aria-label="Hauptnavigation" ... >

In der Auswahlliste im Screenshot oben gibt es auch die Option „Create new Menu“ (auf Deutsch: „Neues Menü erstellen“). Wenn ihr hier eine neue Navigation erstellt, würde diese einen Namen wie „Navigation 2“ erhalten. Dieser würde automatisch als Label verwendet werden. Um das zu ändern, müsstet ihr wieder zurück zum Website-Editor gehen, um es dort zu ändern. Es wäre schön, wenn das auch direkt hier in den Blockeinstellungen möglich wäre.

Hinzufügen eines Labels zu einem Classic Theme

Wenn ihr noch ein Classic Theme verwenden, müsst ihr das Label anders verwalten. Obwohl WordPress seit Version 5.5 Labels unterstützt, verwenden viele Themes diese noch nicht. Zumindest nicht mit dem neuen Parameter. So wird das Label z.B. in Twenty Twenty hinzugefügt:

<nav aria-label="<?php esc_attr_e( 'Footer', 'twentytwenty' ); ?>" ... >

Wenn ihr das Label hier ändern wollt, müsstet ihr die Datei selbst bearbeiten – in einem Child Theme. Nicht wirklich ideal, aber dennoch möglich. Wenn ihr also selbst Themes entwickelt, dann verwendet am besten den Parameter container_aria_label der Funktion wp_nav_menu() in eurem Code.

Fazit

Das Hinzufügen eines Labels zu jedem Navigationsmenü kann einen großen Unterschied für die Barrierefreiheit der Website machen. Wenn ihr ein Block-Theme verwendet, können ihr das ganz ohne Code direkt im Website-Editor anpassen. Wie ich euch gezeigt habe, sind die automatischen Namen der Navigationsmenüs nicht wirklich ideal. Ihr sollten also vielleicht mal die Labels eurer Website überprüfen und ihnen eventuell bessere Namen geben.

Erstellen eines Dialogs mit nativem HTML

Hier ist er nun, der letzte Adventskalender-Blogbeitrag für 2024. Das bedeutet auch, dass viele von euch heute oder morgen Weihnachten feiern. Warum lest ihr also an einem solchen Tag Blogbeiträge? 😁

Im letzten Thema dieser Ausgabe geht es um ein weiteres HTML-Element, das ihr vielleicht noch nicht kanntet. Aber alle von euch kennen und hassen Cookie-Popups, richtig? Außer natürlich, ihr seid an der Entwicklung solcher Tools beteiligt. 😅

Das dialog HTML-Element

Wenn wir ein Popup erstellen „wollen“, haben wir in der Vergangenheit oft eine „Modal-Library“ verwendet. Aber jetzt haben wir ein eigenes HTML-Element dafür: das <dialog> Element. Es hat einige Gemeinsamkeiten mit dem details Element, über das ich vor vier Tagen gebloggt habe. Und wenn ihr die weiteren Ressourcen besucht habt, die ich dort verlinkt hatte, dann habt ihr das Element vielleicht schon kennengelernt. So würdet ihr es verwenden:

<dialog>
	Some Text inside the dialog.
</dialog>

Wenn ihr diesen Code in eure Seite einfügt, dann seht ihr … absolut nichts. Das Element ist nicht standardmäßig sichtbar. Ihr könnt das open Attribut hinzufügen, genau wie bei dem details Element, dann solltet ihr das hier sehen:

Chrome with open dev tools showing the properties of the open dialog element, that is centered on the page.

Wie ihr hier sehen könnt, erhalten wir mehr als nur ein leeres Element mit etwas Text. Das Dialogfeld hat einen Rahmen, einen Innenabstand und ist auf der Seite zentriert, wobei eine absolute Position und ein automatischer Außenabstand verwendet werden. Das würde sogar bedeuten, dass er über jedem Inhalt angezeigt wird, der nach ihm kommt.

Öffnen und Schließen des Dialogs

Ein Dialog, der standardmäßig geöffnet ist, ist nicht wirklich nützlich. Zum Glück gibt es einige JavaScript-Funktionen, mit denen wir ihn interaktiv gestalten können. Fügen wir also einen Button zum Öffnen des Dialogs und einen Button zum Schließen des Dialogs hinzu:

Chrome showing an open dialog element. The dev tools are open and at the bottom of the HTML markup tree, a "#top-layer" is shown after the closing html tag. The dialog has a paragraph and an undordered list. The page behind the dialog has an "Open dialog" button and inside the dialog, there is a "Read it" button at the end.

Dieser Screenshot zeigt mehrere Dinge auf einmal. Zuerst einmal haben wir die Webseite mit einem Button „Open Dialog“. Wenn dieser angeklickt wird, öffnet sich der Dialog, der dieses Mal etwas mehr HTML-Inhalt hat. Vielleicht ist euch auch aufgefallen, dass der Hintergrund der Website grau ist. Er ist eigentlich weiß, aber wenn der Dialog geöffnet ist, wird die Seite mit einem transparenten Overlay überlagert. Dies ist auch links mit der ::backdrop Pseudoelement zu sehen. Eine letzte Besonderheit ist der #top-layer nach dem HTML-Baum auf der linken Seite. Dies ist eine spezielle Ebene, die die gesamte Seite überdeckt und die Barrierefreiheit verbessert.

Wenn ihr auf den Button „Read it“ klickt, wird der Dialog geschlossen. Für die beiden Buttons benötigen wir etwas JavaScript, um diese Funktionalität umzusetzen:

const dialog = document.querySelector('dialog');
const openButton = document.querySelector('button#open-dialog');
const closeButton = document.querySelector('button#close-dialog');
openButton.addEventListener('click', () => {
	dialog.showModal();
});
closeButton.addEventListener('click', () => {
	dialog.close();
})

Wenn ihr die Funktion showModal() verwendet, spielt es keine Rolle, wo das Element im HTML-Baum platziert ist. Es wird immer in der Mitte der Seite geöffnet. Passt der Inhalt nicht hinein, wird eine Scrollbar angezeigt.

Alternative Möglichkeit zum Öffnen des Dialogs

Es gibt eine zweite Funktion zum Öffnen eines Dialogs. Sie heißt einfach show() und würde den Dialog „inline“ öffnen und nicht als „Modal“. In diesem Fall macht es einen Unterschied, wo der Dialog platziert wird. Es wird immer noch mit „position: absolute“ über dem Inhalt angezeigt, aber nicht mehr vertikal zentriert. Er wird genau dort angezeigt, wo sein HTML-Code im DOM platziert ist. Es gibt auch kein Hintergrund-Overlay und keinen #top-layer mehr, womit das Verhalten einige wichtige Unterschiede hat.

Schließen des Modals ohne JavaScript

Um den Dialog zu öffnen, benötigen ihr etwas JavaScript mit einer der beiden Funktionen, um es „anzuzeigen“. Es sei denn, ihr fügen das open Attribut hinzu, damit es standardmäßig geöffnet ist – ich frage mich wirklich, warum die Funktionen nicht openModal() und open() heißen, wenn das Element auch das open Attribut verwendet.

Um den Dialog zu schließen, brauchen ihr aber kein JavaScript. Ihr könnt ihn auch selbst schließen lassen, wenn du ein Formular als Inhalt hast:

<dialog open>
	<form method="dialog">
		Some text in the dialog.
		<button>OK</button>
	</form>
</dialog>

Siehst du den besonderen Trick mit dem Formular? Es verwendet den Wert dialog für das Attribut method. Dadurch wird der Dialog geschlossen, wenn das Formular mit einem Button darin abgeschickt wird. Ihr könnt also auch einfach nur ein Eingabefeld haben, dann wird der Dialog mit ENTER geschlossen.

Wenn der Dialog mit showModal() geöffnet wurde, kann dieses Modal auch mit der ESC-Taste geschlossen werden. Es gibt also verschiedene Möglichkeiten, es ohne JavaScript zu schließen.

Styling des Elements

Der Dialog selbst kann wie jeder andere „Container“ gestylt werden. Wie bereits gezeigt, verfügt es standardmäßig über einen Rahmen und einen Innenabstand. Spannender ist aber wahrscheinlich die Gestaltung des „Backdrops“. Hier ist ein Beispiel, welches den Dialog und den „Backdrop“ anpasst:

dialog {
	border-radius: 5px;
	border: 3px solid red;
	top: 30px;

	&::backdrop {
		background: black;
		opacity: 0.7;
	}
}

Wir ändern den Rahmen des Dialogs und auch den Hintergrund des „Backdrops“. Ach, und übrigens, was ihr hier seht, ist keine Sass-Syntax. Es handelt sich um natives verschachteltes CSS. Wenn ihr das noch nicht gesehen habt, lesen einfach den vorherigen Blogbeitrag „Verschachteltes CSS ohne Präprozessoren“ im Adventskalender. So würde es dann aussehen:

An open

Anstelle des rgba() Wertes für den Hintergrund verwenden wir nun eine bcakground-color in Kombination mit einer opactiy.

Noch mehr aus dem Element herausholen

Auf der Dokumentationsseite findet ihr viele weitere Beispiele dafür, wie ihr Formulare innerhalb des Elements verwenden, es gestalten und mit ihm interagieren könnt, und vieles mehr. Das alles wäre aber zu viel für diesen Blogbeitrag. Der CSS-Tricks-Blogbeitrag, der sich mit dem details Element beschäftigt, enthält auch einige Beispiele für das dialog Element.

Fazit

Ich hoffe, ich habe mir das beste Thema für heute aufgehoben. Zumindest fand ich dieses Element wirklich faszinierend und habe es in einem Projekt verwendet. Vielleicht blogge ich demnächst über diesen Anwendungsfall. Aber das ist etwas für das nächste Jahr. Dies war mein letzter Adventskalender-Blogbeitrag und auch der letzte für dieses Jahr, wenn ich meine zweiwöchige Blog-Routine einhalte.

Ich hoffe, ihr habt in den Adventskalender-Blogbeiträgen oder in den anderen Beiträgen, die ich in diesem Jahr geschrieben habe, ein paar neue Dinge gelernt. Es hat Spaß gemacht, aber es war auch viel mehr Arbeit, als ich dachte. Ich wünsche euch ein paar tolle Tage und wir sehen uns alle 2025! 🎇

Ein einfacherer Dark-Mode

Vor vier Tagen habe ich über „Die prefers-color-scheme Media-Query“ geschrieben und darüber, wie man sie verwendet, um einen Dark-Mode für den Browser zu erstellen. Heute werde ich euch zeigen, dass ihr sie vielleicht gar nicht braucht. Hm, aber wie bekommen wir dann einen Dark-Mode, werdet ihr euch jetzt vermutlich fragen? Mit einer einzigen CSS-Eigenschaft anstelle einer Medienabfrage.

Die color-scheme Eigenschaft

Ihr müsst nur eine einzige Eigenschaft verwenden, um dem Browser mitzuteilen, welche Farbschemas ihr habt. Und so können Sie es verwenden:

:root {
  color-scheme: light dark;
}

Das war es auch schon! Mehr als das braucht ihr nicht. Kommt morgen für einen neuen Blogbeitrag wieder. 😁 Nee, natürlich gibt es noch mehr zu sagen. Aber schauen wir doch mal, was das schon alles kann. Ich habe eine Seite mit ein wenig HTML ganz ohne CSS erstellt, abgesehen von dieser einen Zeile. So sieht sie in meinem Chrome-Browser aus, wenn ich einen hellen Modus verwende:

Screenshot einiger Elemente im hellen Modus in Chrome: h1, p, fieldset, input, textarea, select und button.

Und jetzt verwenden wir den Umschalter, den ich euch im Blogbeitrag „Präferenzen im Browser simulieren“ gezeigt habe, um in den dunklen Modus zu wechseln. Das würde dann so aussehen:

Screenshot von einigen Elementen im Dark-Mode in Chrome: h1, p, fieldset, input, textarea, select und button. Formularfelder und Button haben jetzt eine Hintergrundfarbe.

Während im hellen Modus alle Elemente einen weißen Hintergrund hatten, haben die Formularelemente und der Button nun einen grauen Hintergrund. Der Platzhaltertext des Inputs hat nun aber einen zu geringen Kontrast.

All das haben wir „kostenlos“ bekommen. Mit nur einer einzigen Zeile CSS. Aber dieses Design gefällt euch vielleicht nicht, wie können wir es also ändern, ohne die Media-Query zu verwenden? Es gibt einige spezielle „Funktionen“, die ihr verwenden könnt.

Die light-dark() Funktion

Nehmen wir unseren Quellcode aus dem Blogbeitrag „Die prefers-color-scheme Media-Query„, um zu sehen, wie das color-scheme verwendet werden kann, um einen einfachen Dark-Mode zu erstellen. So sah unser alter Code aus:

button {
	padding: 10px 20px;
	font-size: 2rem;
	background-color: #eee;
	border: 3px solid #333;
	border-radius: 10px;
	color: #333;
}
@media (prefers-color-scheme: dark) {
	button {
		background: #333;
		border-color: #333;
		color: #eee;
	}
}

Jetzt nehmen wir diesen Code und verwenden jede Farbe in der neuen light-dark() Funktion, die bereits eine recht gute Basis-Unterstützung hat. Wir definieren zuerst die helle Farbe und dann die dunkle Farbe. Es sollte dann wie folgt aussehen:

button {
	padding: 10px 20px;
	font-size: 2rem;
	background-color: light-dark(#eee, #333);
	border: 3px solid light-dark(#333, #eee);
	border-radius: 10px;
	color: light-dark(#333, #eee);
}

Ist das nicht viel einfacher zu lesen? Wenn wir die Seite mit einem hellen Farbschema betrachten, würden wir dies sehen:

Chrome mit geöffneten Entwicklertools, fokussiert auf einen Button. In den „Styles“ visualisiert die Funktion „light-dark()“ die beiden Farben.

Wenn Sie die Schaltfläche untersuchen, können Sie die Funktion light-dark() in den „Styles“ sehen und dort die Farben sehen/ändern. Betrachten wir nun dieselbe Seite mit der Einstellung für ein dunkles Farbschema:

Chrome mit geöffneten Entwicklertools, fokussiert auf einen Button. In den „Styles“ visualisiert die Funktion „light-dark()“ die beiden Farben. Es wird der Dark-Mode simuliert.

Das ist die grundlegende Verwendung der Eigenschaft color-scheme und der Funktion light-dark(). Aber die Eigenschaft kann verschiedene Werte haben.

Standardmäßig zu einem dunklen Modus wechseln

Wenn ihr den Dark-Mode als Standard für eure Website verwenden und auf einen hellen Modus verzichten möchtet, setzt das color-scheme einfach wie folgt um:

:root {
	color-scheme: dark;
}

Falls ihr aber standardmäßig einen hellen Modus haben wollt, tut genau das Gegenteil und verwendet light als einzigen Wert. Sobald ihr die beiden Werte light und dark habt, die Reihenfolge spielt keine Rolle, werdet ihr das oben gezeigte Verhalten haben.

Den Dark-Mode verhindern

Wie ich im Blogbeitrag „Präferenzen im Browser simulieren“ beschrieben hatte, verfügt Chrome über eine Option für einen „automatischen dunklen Modus“. Dadurch wird ein dunkler Modus verwendet, auch wenn das color-scheme auf light eingestellt ist. Ihr könnt den Dark-Mode aber auch verhindern, indem ihr das Keyword only hinzufügt:

:root {
	color-scheme: only light;
}

Jetzt wird der Browser keinen Dark-Mode verwenden. Und natürlich könnt ihr auch „only dark“ verwenden, um das Gegenteil zu erreichen.

CSS-Eigenschaft nur für einige Elemente setzen

Einige Website-Designs verwenden „unterschiedliche Farbschemas“ für verschiedene Elemente. Zum Beispiel könnte eine Website eine dunkle Kopfzeile, aber eine helle Fußzeile hat. Vielleicht möchtet ihr diese Farben beibehalten, auch wenn der Modus umgeschaltet werden kann. Dann könnt ihr das color-scheme für einige Elemente einschränken, aber für andere Teile beide zulassen:

:root {
	color-scheme: light dark;
}
header {
	color-scheme: only dark;
}
.left {
	color-scheme: light;
}
.middle {
	color-scheme: light dark;
}
.right {
	color-scheme: dark;
}
footer {
	color-scheme: only light;
}

In diesem Codeschnipsel legen wir beide Farbschemas für das Root-Element fest, ändern sie aber dann für bestimmte Teile der Seite. Die Kopf- und Fußzeile sind auf nur ein Farbschema beschränkt. Im Inhaltsbereich gibt es drei Teile. Der linke und der rechte Teil nehmen nur ein Schema an, der mittlere nimmt zwei an (wir könnten die Angaben für den mittleren weglassen, da er das Schema vom Root-Element erben würde, aber der Vollständigkeit halber habe ich ihn hier gelassen).

Ich habe unseren Button in jedem dieser Abschnitte platziert. Schauen wir uns an, wie das in Chrome mit den verschiedenen Farbschema-Simulationen aussieht:

Mit „prefers-color-scheme: light“

Chrome mit „prefers-color-scheme: light“ zeigt dunkle Buttons in der Kopfzeile und auf der rechten Seite und helle Buttons auf der linken Seite, in der Mitte und in der Fußzeile an.

Wir können hier sehen, dass unser „color-scheme: dark“ im „.right“ Container den Button im dunklen Farbschema anzeigt.

Mit „prefers-color-scheme: dark“ (bevorzugtes Farbschema: dunkel)

Chrome mit „prefers-color-scheme: dark“ zeigt dunkle Buttons in der Kopfzeile, in der Mitte und auf der rechten Seite an und helle Buttons auf der linken Seite und in der Fußzeile.

Beim Umschalten auf das dunkle Farbschema hat sich der Button in der Mitte verändert, die anderen sind jedoch gleich geblieben. Auch der Inhaltsbereich hat jetzt einen schwarzen Hintergrund, aber die Hintergrundfarben von Kopf- und Fußzeile sind gleich geblieben.

Mit „Automatischer dunkler Modus“

Chrome mit „Automatischer dunkler Modus“ zeigt dunkle Buttons in der Kopfzeile, in der Mitte und rechts, und helle Buttons in der Fußzeile an. Der button auf der linken Seite hat einen Dark-Mode mit einer anderen Rahmenfarbe (dunkler).

Unsere Kopf- und Fußzeile sind immer gleich geblieben. Aber jetzt wird auch der linke Button in einem Dark-Mode angezeigt, da dieser Teil nicht das Schlüsselwort „only“ verwendet. Die Rahmenfarbe ist allerdings dunkler als in unserem definierten CSS-Design.

Den Dark-Mode weiter stylen

Da die Funktion light-dark() nur für Farben verwendet werden kann, müsst ihr möglicherweise auch weiterhin die Media-Query prefers-color-scheme verwenden, um andere Stile in eurem Dark-Mode zu ändern.

Wenn ihr noch kreativer werden wollt und euch noch mehr mit dem Dark-Mode beschäftigen möchtet, empfehle ich euch den Blogbeitrag „Dark Mode in CSS Guide“ von CSS-Trick sowie die beiden Seiten über die color-scheme Eigenschaft und die light-dark() Funktion.

Fazit

Das Hinzufügen eines Dark-Mode zu eurer Seite und das Testen des Modus war noch nie so einfach wie mit den in den letzten Blogbeiträgen beschriebenen Methoden. Vielleicht könnt ihr also in diesen dunklen Wintertagen auch das Design eurer Website anpassen und versuchen, ein schönes dunkles Farbschema zu erstellen.

CSS-Stile mit JavaScript auslesen

In der Regel sollten man jede der grundlegenden Webtechnologien für ihren Hauptzweck verwenden: HTML für das Markup, CSS für die Stile und JavaScript für zusätzliche Interaktivität. Aber manchmal überschneiden sich diese Dinge. Es kann Fälle geben, in denen ihr die aktuell gerenderten Stile eines Elements kennen müsst. Darum geht es im heutigen Thema.

Ermitteln der berechneten Stile eines Elements

Der Funktionsname dafür könnte nicht einfacher zu merken sein: getComputedStyle(). Sie ist eine Funktion des windows Objekts und bekommt das Element übergeben, für das wir die Stile haben wollen:

const body = document.querySelector('body');
const bodyStyles = window.getComputedStyle(body);
const bodyFontSize = bodyStyles.getPropertyValue('font-size');
console.log(bodyFontSize); // 16px

Um eine einzelne CSS-Eigenschaft abzurufen, verwenden wir die Funktion getPropertyValue() für das CSSStyleDeclaration Objekt (in unserem Fall bodyStyles) und teilen ihm mit, welche Eigenschaft wir abrufen möchten.

Wenn ihr diesen Code in einem völlig leeren HTML-Dokument ausführt, erhaltet ihr wahrscheinlich 16px als Schriftgröße für den Textkörper, was in den meisten Browsern die Standardeinstellung ist, es sei denn, die Größe wurde im Betriebssystem oder im Browser geändert.

Aber schauen wir mal, wie wird das Ganze noch interessanter machen können. Man hat nicht immer eine feste Schriftgröße für ein Element, denn manchmal ist sie relativ zu anderen Elementen:

body {
	font-size: 22px;
}
h1 {
	font-size: 1.6em;
}
h1 small {
	font-size: 75%;
}

Jetzt haben wir eine Website mit einer <h1> Überschrift und einem <small> Element darin. Wie groß wäre die resultierende Schriftgröße? Schnappen wir uns unsere Taschenrechner:

  • <body> font-size: 22px
  • <h1> font-size: 22px * 1,6 = 35,2px
  • <small> innerhalb von <h1> font-size: 35,2px * 0,75 = 26,4px

Wir sollten also eine Schriftgröße von 26,4px für den kleinen Text erhalten. Schauen wir uns an, ob das der Fall ist, indem wir es auch mit den Entwicklertools überprüfen:

Screenshot von Chrome mit den geöffneten Entwicklungswerkzeugen. Die Seite hat ein h1 Element mit einem darin enthaltenen small Element. Dieses Element ist ausgewählt und das Stile-Panel „Berechnet“ zeigt „font-size: 26.4px“.

Hier seht ihr ein vollständiges Beispiel, mit dem geänderten Selektor. Wir haben das <small> Element ausgewählt und Chrome zeigt uns die Schriftgröße von 26,4px im Abschnitt „Berechnet“ an. Wie ich nach dem Aufruf von console.log() geschrieben habe, erhalten wir diesen Wert auch von JavaScript. Ganz nett, nicht wahr?

Abrufen von Stilen aus einem Pseudo-Element

Wenn ein Element über ein Pseudo-Element verfügt, das entweder speziell festgelegt oder standardmäßig verfügbar ist, können wir auch Stile für dieses Element abfragen. Legen wir also einige Stile für das neue ::marker Pseudo-Element eines Listenelements fest:

ul li::marker {
	color: red;
}

Wir können nicht einfach ein Pseudo-Element mit der Methode document.querySelector() auswählen, aber wir können die berechneten Stile wie folgt erhalten:

const element = document.querySelector('ul li');
const color = window.getComputedStyle(element, '::marker').color;
console.log(color); // rgb(255, 0, 0)

Wenn ihr versucht, Stile für ein „älteres Pseudo-Element“ wie das ::before Pseudo-Element zu erhalten, können ihr auch :before schreiben, aber ihr könnt nicht :marker schreiben, da dies ein neueres Pseudo-Element ist, für das die Variante mit nur einem Doppelpunkt nicht existiert.

In diesem Codeschnipsel verwende ich nicht zusätzliche die Funktion getPropertyValue(), da sie nicht unbedingt erforderlich ist. Ihr könnt die meisten Eigenschaften direkt über ihren Namen abrufen. Ich empfehle, einen Blick in die Dokumentation des CSSStyleDeclaration Objekts zu werfen oder das Objekt auf der Konsole auszugeben, um die verfügbaren Eigenschaften zu sehen.

Was ihr in diesem Beispiel auch sehen könnt, ist, dass wir nicht den Wert red erhalten, den wir in unserem CSS verwendet haben. Stattdessen erhalten wir rgb(255, 0, 0) als berechneten Stil. Weitere Informationen hierzu findet ihr in den Notes auf der Dokumentationsseite.

Fazit

Vielleicht habt ihr einen JavaScript-Code, der die Stile eines Elements kennen muss. Sei es die Größe, die Farbe, die Position oder andere Eigenschaften, die eine Auswirkung auf euren Code haben würden. In diesen Fällen ist die Funktion getComputedStyle() sehr hilfreich. Der WordPress Core verwendet sie auch an verschiedenen Stellen, z.B. im Farbwähler, um vor einem zu geringen Farbkontrast zu warnen. Das ist ein sehr gutes Beispiel dafür, wie man die Funktion verwenden kann.

Aufzeichnen und Wiedergeben von Website-Interaktionen für Tests

Dies ist der letzte Blog-Beitrag zum Thema Debugging für diesen Adventskalender. Für den letzten Beitrag habe ich mir etwas Besonderes aufgehoben. Vielleicht gibt es Dinge, die ihr im Frontend (oder Backend) eurer Seite immer wieder testen müsst, nachdem ihr etwas geändert habt. Sei es das Ausfüllen eines Formulars, das Klicken auf einige Elemente oder die Navigation durch die Seite. Es kann sehr mühsam sein, das immer wieder manuell zu tun. Aber wenn ihr Chrome für eure Tests verwendet, gibt es ein nettes eingebautes Tool, das ich durch eine Kollegin kennengelernt habe.

Die Rekorder Funktion in Chrome

Um zu dieser Funktion zu gelangen, haben ihr zwei Möglichkeiten. Ihr können das Panel auf ähnliche Weise öffnen, wie wir das „Rendering“ Panel geöffnet haben. Öffnen dazu die Entwicklertools, klickt auf die drei vertikalen Punkte, wählt „Weitere Tools“ und dann „Rekorder“ aus:

Screenshot, wie man die "Weitere Tools > Rekorder" Einstellungen öffnet.

Die zweite Möglichkeit ist die Verwendung des Dialogs „Befehl ausführen“, den ihr mit „Strg+ Umschalt + P“ (oder „Cmd+ Umschalt + P“ auf einem Mac) öffnen könnt. Dort sucht ihr dann nach „Rekorder“. Sobald das Panel geöffnet ist, klickt auf die blaue Schaltfläche „Neue Aufzeichnung erstellen“:

Screenshot der geöffneten Chrome Rekorder-Funktion mit dem blauen Button „Neue Aufzeichnung erstellen“.

Auf der nächsten Seite erstellt Chrome automatisch einen „Name der Aufzeichnung“ mit dem aktuellen Datum und der Uhrzeit für eure Aufzeichnung, den ihr aber mit einem aussagekräftigeren Titel überschreiben könnt:

Chrome-Recorder-Funktion mit geändertem „Name der Aufzeichnung“, bevor die Aufzeichnung gestartet wird.

Wenn ihr fertig seid, klickt auf den roten Button am Ende der Seite oder drücken Sie „Strg + E“. Dann macht irgendetwas auf der Seite. In diesem Beispiel habe ich oben rechts auf die Suche geklickt, „Adventskalender“ in das Suchfeld eingegeben und auf ENTER gedrückt. Nachdem die Seite neu geladen wurde, habe ich wieder auf den Button unten geklickt, um die Aufzeichnung zu beenden. Ihr solltet dann in etwa so etwas sehen:

Chrome Rekorder nach der Aufzeichnung.

Wenn du nach oben scrollst, findest du oben links einen Button „Code anzeigen“. Wenn ihr dort darauf klickt, können ihr die technische Datei für die Aufzeichnung in verschiedenen Formaten sehen:

Chrome Rekorder mit geöffneter Code-Ansicht.

Der interessanteste Button ist aber der blaue Button „Noch einmal wiedergeben“. Links davon können ihr zuvor noch die Wiedergabegeschwindigkeit auswählen. Wenn ihr nun auf den Button klickt, zeigt Chrome die Meldung „Chrome wird automatisierter Testsoftware gesteuert“ an. Es werden dann alle Schritte abgespielt und einzeln überprüft. Wenn alles erfolgreich war, ist die vertikale Linie für einige Sekunden grün:

Chrome Rekorder direkt nach der erneuten Wiedergabe.

Oben links in der Leiste seht ihr ein Dropdown-Menü. Hier findet ihr alle Aufzeichnungen, die ihr jemals in diesem Chrome-Profil gemacht habt. Mit den Schaltflächen daneben können ihr sie importieren, exportieren und löschen. Wenn ihr eine solche JSON-Datei exportiert und an eine andere Person senden, könnt ihr die wiederholbaren Testschritte mit anderen teilen.

Ihr könnt auch jeden Schritt ausklappen und die Einstellungen des Schritts ändern. Auf der Seite Chrome for Developer findet ihr hierzu ein kurzes Video, in dem der Rekorder etwas genauer erklärt wird.

Fazit

Wenn ihr einen Test auf einer Seite wiederholen möchtet, aber nicht jeden Klick und jeden Tastendruck manuell wiederholen wollt, ist der Chrome Rekorder ein großartiges Tool, das ihr verwenden könnt. Es hat auch eine Exportoption, die ich wahrscheinlich in einem anderen Blog-Beitrag behandeln werde. Da dieses Thema aber nicht ganz in unsere vier Themen für den Adventskalender 2024 passt, müssen ihr bis 2025 warten.

Ich konnte keine ähnliche Funktion in Firefox finden, aber wenn ihr eine Erweiterung kennt, die etwas Ähnliches in anderen Browsern anbietet, hinterlasst bitte einen Kommentar.

Barrierefreie Akkordeons mit dem details HTML-Element erstellen

In meinem vorletzten Blog-Beitrag zum Thema HTML in dieser Adventskalender-Serie möchte ich ein Element vorstellen, das einige von euch vielleicht schon benutzt haben, ohne es zu kennen. Aber die Funktionalität, die es euch bietet, haben viele von euch vielleicht schon in einer Website verwendet.

Toggle- und Akkordeon-Elemente

Wenn wir nur ein Element haben, das beim Anklicken eines Buttons oder Textes zusätzlichen Text einblendet, sprechen wir oft von „Toggle-Elementen“. Gibt es mehrere, von denen immer nur eines gleichzeitig geöffnet sein kann, spricht man von „Akkordeons“.

In der Vergangenheit hätten wir für eine solche Funktionalität eine JavaScript-Bibliothek verwenden und den Inhalt in einigen Container-Elementen platziert, die dann über einen Button oder Link angezeigt werden. Aber jetzt haben wir ein Element für diese Funktion. Wenn wir einen einzelnen Toggle haben wollen, wäre dies unser Markup:

<details open>
	<summary>Erfahre mehr über das details Element</summary>
	Dies ist Text, der nur angezeigt wird, wenn das Element geöffnet wird.
	Ihr können hier auch beliebige andere HTML-Elemente verwenden, ihr müsst 
	also nicht nur einfachen Text verwenden.
</details>

In diesem Beispiel wird der „versteckte Text“ nicht in ein Element eingeschlossen. Wahrscheinlich würdet ihr hier ein <p> oder <div> verwenden, aber eigentlich wird jeder Inhalt, der nach dem <summary> Element kommt, ausgeblendet. Das Element wird standardmäßig „geschlossen“ angezeigt uns es würde so aussehen:

Ein nach rechts zeigendes Dreieck mit dem Text „Erfahre mehr über das details Element“ daneben.

Der nach rechts zeigende Pfeil wird standardmäßig angezeigt. Die Form wird in den verschiedenen Browsern etwas unterschiedlich aussehen. Wenn ihr auf den Pfeil oder den Text klickt, wird das Element geöffnet, und der Pfeil zeigt jetzt nach unten. Damit wird angezeigt, dass das Element geöffnet ist:

Ein nach unten zeigendes Dreieck, mit dem Text des details Elements rechts davon und dem restlichen Inhalt innerhalb des details Elements darunter.

Wie bereits erwähnt, ist das Element standardmäßig geschlossen, ihr können es aber auch öffnen, indem ihr ein leeres open Attribut zum Element hinzufügt:

<details open>
<!--... -->
</details>

Wenn ihr nun auf das <summary> Element klickt, wird das <details> Element geschlossen. Ich kann mir nicht wirklich einen Anwendungsfall vorstellen, in dem man dies für einen einzelnen Toggle verwenden würde. Warum sollte man mit einem Klick etwas ausblenden, das bereits sichtbar war?

Erstellen eines Akkordeons mit mehreren „gruppierten“ details Elementen

Das <details> Element hat nur zwei spezifische Attribute. Das Attribut open haben wir bereits kennengelernt, das zweite ist name. Wenn ihr mehreren <Details> Elementen dieses Attribut mit demselben Wert gebt, werden sie zu einem Akkordeon gruppiert, wobei immer nur eines zur gleichen Zeit geöffnet sein kann:

<h2>Gruppe eins</h2>
<details name="group-one">
	<summary>Erstes Element von Gruppe eins</summary>
	<p>Der Inhalt des ersten Elements von Gruppe eins.</p>
</details>
<details name="group-one">
	<summary>Zweites Element von Gruppe eins</summary>
	<p>Der Inhalt des zweiten Elements von Gruppe eins.</p>
</details>
<h2>Gruppe zwei</h2>
<details name="group-two">
	<summary>Erstes Element von Gruppe zwei</summary>
	<p>Der Inhalt des ersten Elements von Gruppe zwei.</p>
</details>
<blockquote>
	<q>Ein zufälliges Zitat dazwischen</q>
	<p>Eine berühmte Person</p>
</blockquote>
<details name="group-two" open>
	<summary>Zweites Element von Gruppe zwei</summary>
	<ul>
		<li>Punkt 1</li>
		<li>Punkt 2</li>
	</ul>
</details>

Ich habe hier ein etwas komplexeres Beispiel gewählt, um mehrere Dinge auf einmal zu zeigen. Zunächst einmal haben wir zwei Akkordeons, die jeweils die Werte group-one bzw. group-two für das name Attribut verwenden. Das HTML-Markup würde folgendermaßen dargestellt werden:

Zwei details Akkordeongruppen mit einem Zitat zwischen den Elementen der zweiten Gruppe, die eine ungeordnete Liste als Inhalt des zweiten details Elements hat.

In Gruppe eins hat kein Element das open Attribut, also sind beide standardmäßig geschlossen, während in Gruppe zwei das zweite Element das open Attribut hat und seinen Inhalt anzeigt. In diesem Fall ist der Inhalt eine ungeordnete Liste, was zeigt, dass das Element beliebigen HTML-Code und nicht nur statischen Text enthalten kann.

Zwischen dem ersten und dem zweiten Element der zweiten Gruppe befindet sich auch ein „Blockquote“ (Zitatblock). Dies soll verdeutlichen, dass nicht alle <details> Elemente direkt neben-/untereinander stehen müssen. Ihr könnt auch Inhalte dazwischen haben.

Wenn ihr eines der anderen Elemente in derselben Gruppe öffnet, wird automatisch das aktuell geöffnete Element geschlossen. Wenn ihr das geöffnete Element ebenfalls schließt, sind dann alle geschlossen. Es gibt keine (native) Möglichkeit, das Schließen aller Elemente zu verbieten.

Wenn ihr ermöglichen wollt, dass alle Elemente offen sind, verwenden einfach nicht das Attribut name für diese Elemente. Ihr könnt auch ein wenig JavaScript für zwei Buttons „Alle öffnen“ und „Alle schließen“ verwenden, die alle Elements öffnen oder schließen würden. Es gibt sogar ein toggle Event, für das ihr einen Event-Listener registrieren könnet. Mehr dazu finden ihr in der Dokumentation.

Styling des Elements

Die Dokumentation enthält auch einige grundlegende Beispiele für die Gestaltung. Wenn ihr noch kreativere Möglichkeiten zur Gestaltung des Elements sehen möchten, kann ich euch den CSS-Tricks-Blogbeitrag zu diesem Element sehr empfehlen..

Fazit

Wenn ihr wirklich ein Toggle-/Akkordeon-Element auf eurer Website benötigt, kann ich die Verwendung der nativen HTML-Elemente sehr empfehlen. Ich persönlich mag keine Toggles oder Akkordeons. Ich blättere lieber durch eine längere Seite, als auf all diese „Toggle-Buttons“ klicken zu müssen, die die Seite auch noch nach oben/unten verschieben, womit man das nächste Element erst einmal anvisieren muss. Das frustriert mich wirklich.

Wenn ihr WordPress verwendet, benutzt einfach den „Details“ Block, der euch genau dieses Element liefert. Ihr habt dann eine Zeile für die Zusammenfassung und dann einen „Absatz“ Block für den Inhalt. Ihr könnt hier aber jeden beliebigen Block verwenden, sogar mehrere oder Blöcke wie „Gruppe“, „Spalten“ usw., um wirklich kreativ zu werden.

Die prefers-color-scheme Media-Query

Heute möchte ich über etwas sprechen, das viele von euch wahrscheinlich sehr mögen: Dark-Modes! Oder genauer gesagt, wie man das Farbschema auf den dunklen Modus umstellt, diese Präferenz eingestellt ist. Ich verwende den Dark-Mode für meine IDE und für einige Online-Dienste, aber für viele bin ich immer noch auf der hellen Seite.

Erkennung des Dark-Modes und Anpassung der Styles

Die Aktivierung des Dark-Mode kann durch eine Einstellung im Betriebssystem oder Browser vorgenommen werden oder durch das Klicken auf einen Button passieren. Unabhängig davon, wie zum Dark-Mode gewechselt wird, sollte euer CSS-Code die Media-Query wie folgt verwenden:

button {
	padding: 10px 20px;
	font-size: 2rem;
	background-color: #eee;
	border: 3px solid #333;
	border-radius: 10px;
	color: #333;
}
@media (prefers-color-scheme: dark) {
	button {
		background: #333;
		border-color: #333;
		color: #eee;
	}
}

Dieses CSS geht davon aus, dass ihr standardmäßig ein helles Farbschema verwenden. Wenn nun die Präferenz auf dark gesetzt wird, werden alternative Farben für die Buttons verwendet. So würde ein Button in hellen oder dunklen Farben aussehen:

Ein Button mit dem Text „light colors“ auf der linken Seite, mit einer hellgrauen Hintergrundfarbe und einer dunkelgrauen Text- und Rahmenfarbe. Ein zweiter Button auf der rechten Seite mit dem Text „dark colors“, dem Text in hellgrau und dem Hintergrund und Rahmen in dunkelgrau für ein „ausgefülltes“ Design.

Durch die Verwendung der gleichen Farbe für Hintergrund und Rahmen haben wir die dunkle Version des Buttons „ausgefüllt“. Ihr müsst aber nicht immer alle Farben invertieren/vertauschen. Einige generische Plugins tun dies jedoch. Wie ich im Beitrag „Präferenzen im Browser simulieren“ erklärt habe, könnt ihr diese Dark-Mode-Styles einfach testen, ohne die Einstellungen in eurem Betriebssystem ändern zu müssen. Chrome verfügt noch über eine Option „Automatischer dunkler Modus“, aber diese würde nur versuchen zu „erraten“, welche Farben verwendet werden könnten. Wir hätten dann ein etwas helleres Grau für den Hintergrund und ein helleres Grau für den Rahmen:

Zwei Buttons, einer mit der Bezeichnung „light colors“ und einer mit der Bezeichnung „dark colors“. Beide haben die gleichen Farben, mit einem mittelgrauen Rand, einem dunkelgrauen Hintergrund und einer hellgrauen Textfarbe.

Wie ihr sehen könnt, würden sogar unsere definierten Farben für den Dark-Mode eines Buttons nicht mehr verwendet werden. Stattdessen zeigt Chrome jeden Button in diesem „automatischen dunklen Modus“ an. Ich bin mir aber auch nicht sicher, ob viele Leute das wirklich nutzen.

Verwendung des hellen Dark-Modes als Standard

Viele Leute entwerfen eine Website mit einem hellen Modus als Standard und verwenden dann die Medienabfrage wie oben gezeigt, um die Farben für einen dunklen Modus zu ändern. Wenn ihr allerdings eine Seite mit einem Dark-Mode als Standard gestalten möchtet, wäre das eine Menge CSS in dieser Media-Query. Was ihr hier stattdessen tun könnt: ändern die Farben, wenn jemand ausdrücklich einen hellen Modus bevorzugt. So in etwa macht ihr das:

button {
	padding: 10px 20px;
	font-size: 2rem;
	background-color: #333;
	border: 3px solid #333;
	border-radius: 10px;
	color: #eee;
}
@media (prefers-color-scheme: light) {
	button {
		background: #eee;
		border-color: #eee;
		color: #333;
	}
}

Dieser Code-Schnipsel ist im Grunde genommen wieder der Erste, es sind nur (fast) alle Farben vertauscht und wir haben den bedeutenden Unterschied, dass wir jetzt in der Media-Query prefers-color-scheme nach light abfragen. Der Button sieht nun im Standard-Dark-Mode und im hellen Modus wie folgt aus:

Zwei Buttons, der linke mit dem Text „dark colors“, einem dunkelgrauen Hintergrund und hellgrauen Text. Der rechte mit dem Text „dark colors“, einem hellgrauen Hintergrund und dunkelgrauem Text.

Je nach euren eigenen Vorlieben könnt ihr also entscheiden, welches Farbschema ihr als Standard für eure Website verwenden möchtet. Das Hinzufügen des anderen Farbschemas kann allerdings einen großen Unterschied in Bezug auf Lesbarkeit und Barrierefreiheit machen. Vor allem, wenn ihr einen Dark-Mode als Standardeinstellung haben wollte, sollten ihr unbedingt in Erwägung ziehen, auch ein helles Farbschema anzubieten.

Fazit

Das Hinzufügen eines Dark-Mode zu eurem CSS war noch nie so einfach wie mit dieser Media-Query. Wenn ihr die Möglichkeit zur Auswahl der Modus geben wollt, kann ein „Umschalt-Button“ oder eine Profileinstellung sinnvoll sein. Dafür ist eine weitere Eigenschaft sehr nützlich. Aber das wird das Thema unseres letzten Blogbeitrags im Adventskalender 2024 zu CSS sein.

Tagged Template Literals in JavaScript

Im Blogbeitrag vor 8 Tagen mit dem Titel „Dynamische Parameter in JavaScript“ habe ich euch gezeigt, wie Funktionen mit einer variablen Anzahl von Parametern funktionieren. Heute möchte ich über die Funktion „getaggte Template“ („Tagged Template Literals“) in JavaScript sprechen, als eine Art von Funktion, die variable Anzahl von Parametern hat.

Template-Literale

Bevor wir uns dem Thema zuwenden, sollten wir zunächst über „Template Literals“ sprechen. In alten Versionen von JavaScript hätte man Folgendes getan, wenn man eine Variable zwischen statischem Text einfügen möchte:

var name = "Bernhard";
var country = "Finland";
var greet = "Hi, my name is " + name + " and I'm currently in " + country + ".";
console.log(greet);
//Hi, my name is Bernhard and I'm currently in Finland.

Mit modernem JavaScript könnt ihr „Template Literals“ verwenden, um eine Variable (oder sogar Code) in euren Strings zu platzieren, die dann interpretiert werden:

const name = "Bernhard";
const country = "Finland";
const greet = `Hi, my name is ${name} and I'm currently in ${country}.`;
console.log(greet);
//Hi, my name is Bernhard and I'm currently in Finland.

Durch die Verwendung von „Backticks“ anstelle von Anführungszeichen verwandeln wir unseren einfachen String in ein Template Literal. Ist das nicht viel einfacher zu lesen? Ah, und ja, ich bin derzeit in Finnland und nicht in Berlin. Auf den Åland-Inseln, um genau zu sein.

Tagged Template Literals

Nach dieser kurzen Einführung kommen wir nun zu den „getaggten“ Template Literals. Wenn ihr sie zum ersten Mal seht, dann kommen sie euch vielleicht ein bisschen seltsam aus. So könnte ein Beispiel aussehen:

const name = "Bernhard";
const country = "Finland";
const greet = makeItBold`Hi, my name is ${name} and I'm currently in ${country}.`;

function makeItBold(strings, ...values) {
	// strings => ['Hello, my name is ', " and I'm currently in ", '.']
	// values => ['Bernhard', 'Finland']

	let result = "";

	// Loop through the strings and values
	for (let i = 0; i < values.length; i++) {
		result += strings[i] + `<b>${values[i]}</b>`;
	}

	// Append the last string (after the last variable)
	result += strings[values.length];

	return result;
}

console.log(greet);
// Hi, my name is <b>Bernhard</b> and I'm currently in <b>Finland</b>.

Wir definieren eine Funktion(makeItBold) und schreiben dann einfach den Funktionsnamen vor das Template Literal. In der Funktion erhalten wir das „statische Strings-Array“ als ersten strings Parameter und dann eine dynamische Anzahl von Parametern für jede „Variable“ (oder genauer gesagt jeden Ausdruck) im Template Literal als zweiten values Parameter. Wir könnten diese Funktion auch so schreiben:

function makeItBold(strings, name, country) {
	// ...
}

Aber das wäre nicht wirklich flexibel, wenn wir die Variablen vertauschen oder mehr/weniger Variablen in einem anderen String haben.

Eine Möglichkeit, die Funktion etwas kürzer zu machen, ist die Verwendung von modernem JavaScript-Code, der mit Arrays arbeitet (zur besseren Lesbarkeit in mehreren Zeilen geschrieben):

function makeItBold(strings, ...values) {
	return strings.reduce(
		(result, str, i) => result + str + (values[i] ? `<b>${values[i]}</b>` : ""),
		""
	);
}

Diejenigen unter euch, die mit modernem JavaScript vertraut sind, können dies wahrscheinlich leicht lesen. Ich muss immer noch zweimal hinschauen und es sorgfältig lesen.

Ich hatte Schwierigkeiten, ein gutes „Praxisbeispiel“ zu finden, aber nachdem ich das obige Beispiel gefunden hatte, ist mir sogar eines eingefallen, das wirklich nützlich sein könnte. Es arbeitet auch nicht mit skalaren Werten, sondern mit Objekten. Was haltet ihr von diesem Beispiel?

function schema(strings, ...values) {
	return strings.reduce((result, str, i) => {
		const value = values[i];
		if (value) {
			// Check for specific schema.org types and add relevant markup
			if (value.type === "Person") {
				return (
					result +
					str +
					`<span itemscope itemtype="https://schema.org/Person">
						<span itemprop="name">${value.name}</span>
					</span>`
				);
			} else if (value.type === "Place") {
				return (
					result +
					str +
					`<span itemscope itemtype="https://schema.org/Place">
						<span itemprop="name">${value.name}</span>
					</span>`
				);
			}
		}
		return result + str;
	}, "");
}

// Usage
const name = { type: "Person", name: "Bernhard" };
const country = { type: "Place", name: "Finland" };

const greet = schema`Hello, my name is ${name} and I'm currently in ${country}.`;

console.log(greet);
// Hello, my name is <span itemscope itemtype="https://schema.org/Person"><span itemprop="name">Bernhard</span></span> and I'm currently in <span itemscope itemtype="https://schema.org/Place"><span itemprop="name">Finland</span></span>.

Fazit

Wie bereits erwähnt, war es nicht einfach, ein gutes Beispiel zu finden. Man könnte argumentieren, dass man in vielen Fällen auch einfach ein Template Literal und etwas Inline-HTML-Markup verwenden könnte. Aber trotzdem gibt es diese Funktionalität und ihr sollten wissen, wie solcher Code funktioniert.

Wenn ihr ein Beispiel mit Tagged Template Literals aus einem eurer Projekte habt, hinterlasst bitte einen Kommentar.