Mein Blog wird 16 Jahre alt – der Beginn eines neuen Lebensabschnitts

Für einige von uns war der 16. Geburtstag schon etwas besonders. Endlich konnte man in Filme ab 16 gehen und auch alleine bis 24 Uhr im Kino sein. Andere haben sich den Traum eines Motorrad-Führerscheins erfüllt. Auch mein Blog wird heute 16 Jahre alt und es beginnt ein neues Kapitel.

Schließung von weiteren Plugins in Aussicht

Noch im letzten Jahr hatte ich ja gehofft, dass ich das Plugin Language Fallback endlich schließen kann. Aber leider ist Preferred Languages noch nicht im Core delandet. In die Sache wird aber durch die nun längeren Veröffentlichungszyklen etwas Bewegung kommen, denn das Plugin hat nun gute Chancen, in einer der nächsten Hauptversionen endlich enthalten zu sein. Wichtige Vorarbeiten zur notwendigen Performance-Optimierung sind bereits in WordPress 6.5 umgesetzt worden.

Auf dem WordCamp Leipzig habe ich außerdem erfahren, dass eine Schnittstelle für mein Plugin Embeds for ProvenExpert bald nicht mehr verfügbar sein wird. Ich bin darüber aber nicht so traurig, denn es gibt mittlerweile ein offizielles Plugin von ProvenExpert, das außerdem Blöcke implementiert. Meines bietet aktuell nur Widgets an und ich spare mir damit die Arbeit am Branch, der die Blöcke umsetzt. Ich werde aber natürlich versuchen einen „Migrationspfad“ für alle umzusetzen, die aktuell noch mein Plugin verwenden, damit sie möglichst ohne Probleme umsteigen können.

Die Zahlen für das vergangene Jahr

Die Besuche auf der deutschen Seite sind leider auch in den vergangen 12 Monaten stärker zurückgegangen. Der Rückgang betrug etwa 23,1% und ich gehe stark davon aus, dass dies mit der verstärkten Nutzung von AI zusammenhängt. Denn viele finden Lösungen zu Problemen mittlerweile eher direkt in den diversen AI-Tools, als über klassische Suchmaschinen. In einem Vergleichszeitraum von Februar bis Juni (weiter zurück geht es leider nicht), zeigt die Google Search Console einen Rückgang der Klickzahlen von knapp 46% an. Die Impressionen gingen aber nur um knapp 11% zurück.

Es gibt aber auch eine erfreuliche Zahl: die Aufenthaltsdauer auf der Website ist um 300% gestiegen! Es kommen also weniger auf die Seite, aber diese lesen dann auch viel länger auf meinem Blog. Es ist also mehr „qualitativer Traffic“. Am Ende sind mir aber die Zahlen nicht so super wichtig, denn ich verfolge mit der Seite ja keinen kommerziellen Zweck. Ich freue mich einfach, wenn ich anderen weiterhelfen kann und ganz besonders, wenn mir dann jemand auch einen Kommentar hinterlässt. Es gab aber leider nur 12 Kommentare, 4 davon von mir. Ihr dürft das gerne alle direkt mal unter diesem Beitrag ändern. 😉

Auf dem englischen Blog sind die Besuche übrigens um 12% gestiegen und die Verweildauer ebenfalls um 240%. Für mehr Zahlen müsst ihr aber die englische Version dieses Beitrags lesen.

Top3 im letzten Jahr

  1. Formatierten Quellcode mit Syntaxhervorhebung in Word einfügen
  2. Fehler beim Senden in Contact Form 7 debuggen
  3. SVG Icons per CSS bearbeiten

Auf den ersten beiden Plätzen gab es keine Änderungen zum letzten Jahr. Allerdings ist der drittplatzierte Beitrag auf Rang 5 gerutscht und stattdessen hat es ein Beitrag von 2016 auf das Treppchen geschafft. Es ist schön zu sehen, dass das Thema SVG endlich so langsam die notwendige Bedeutung bekommt.

Für Platz 2 gab es aber kurz vor dem Jahreswechsel ein inhaltliches Update. Da der Beitrag noch immer so erfolgreich ist, er bisher aber noch nicht ins Englische übersetzt war, habe ich das nachgeholt. Bei der Gelegenheit habe ich den Inhalt auch noch einmal mit der aktuellen Version von WordPress und Contact Form 7 getestet und alle Screenshots aktualisiert. Auch eine weitere Neuigkeit betrifft diesen Beitrag, aber dazu gleich mehr.

Classic Themes funktionieren auch 2025 noch

Noch vor 12 Monaten war ich optimistisch, dass mein Projekt zur Neuimplementierung des aktuellen Themes als Block Theme erfolgreich sein würde. Aber knapp 2 Wochen später musste ich mir eingestehen, dass das Ziel nicht in der gewünschten Form erreichbar ist. Im November habe ich das Projekt dann erst einmal abgebrochen. In den vier Monaten zwischen diesen beiden Beiträgen habe ich auch sehr viel Zeit in das Theme investiert und das Bloggen vernachlässigen müssen.

Ich habe noch immer vor, das aktuelle Theme zu ersetzen, aber eben nicht mehr mit der gleichen Zielsetzung. Den neuen Versuch werde ich dann auch wieder mit Beiträgen begleiten, aber vielleicht auch noch anders.

Neues Projekt: ergänzende Videos zum Blog

Schon oft stand ich vor dem Problem, dass ich zu einem Thema einen Blogbeitrag schreiben wollte, es mir aber sehr schwerfiel, diesen in Worten, Codebeispielen und Bildern zu verfassen. Manche Dinge sind einfach zu komplex, um sie gut in dieser Form zu erklären, gerade dann, wenn man viele Schritte benötigt und nicht unbedingt 20 Screenshots von jedem einzelnen dieser Schritte haben möchte. Daher möchte ich mich an das Projekt wagen, solche Themen in einem Video zu erklären. Ich habe bereits einen YouTube Channel und auch schon mal zum Test ein Video produziert. Und nun kommen wir auch zum zweitplatzierten Beitrag zurück, denn dieser eignet sich hier wohl sehr gut, dazu auch noch ein Video aufzuzeichnen. Sobald es fertig ist, werde ich es in einem eigenen Beitrag vorstellen und wohl im Originalbeitrag einbinden.

Fazit

Einige Ziele konnte ich erreichen, andere nicht. Meine 26 Beiträge habe ich nur durch den Adventskalender im Dezember geschafft, aber es macht mir weiterhin viel Freude, neue Beiträge mit euch zu teilen. Wenn das mit den Videos auch gut funktioniert, könnt ihr euch in Zukunft auf ganz neuen Themen freuen, an die ich mich bisher noch nicht gewagt habe.

Am Ende muss natürlich auch noch ein Video kommen. Letztes Jahr habe ich die Ankündigung zum WordCamp Europe mit euch geteilt, aber es macht natürlich mehr Sinn, mein erstes YouTube Video hier zu präsentieren. Vielleicht wollt ihr mir ja einen Gefallen tun und den Channel gleich mal abonnieren. Das könnte mich etwas mehr dazu motivieren, das nächste Video noch schneller aufzuzeichnen 😉

Click here to display content from YouTube.
Erfahre mehr in der Datenschutzerklärung von YouTube.

WordCamp Europe 2025 – Eine Reise in eine schöne Stadt

Einige von euch wissen vielleicht, dass ich in Mannheim aufgewachsen bin, wo ich 26 Jahre lang gelebt habe, bevor ich nach Berlin gezogen bin. Auf dem Weg zum diesjährigen WordCamp Europe habe ich erst einmal einige Tage in Mannheim verbracht. Von dort aus haben wir dann den den Zug nach Basel in der Schweiz genommen. Nach nur 121 Minuten kamen wir – pünktlich – in Basel an. Als wir mit der Straßenbahn zu unserer Unterkunft für die nächsten Tage gefahren sind, haben wir zum ersten Mal gesehen, was für eine absolut schöne Stadt Basel ist! Wie kann es sein, dass ich so viele Jahre so nah an einer solchen Stadt gelebt und sie nie besucht habe?

Sightseeing und Museen

Da wir schon am Dienstag angekommen sind und der Contributor Day erst am Donnerstag war, hatten wir etwas Zeit, um die Stadt zu erkunden. Wenn man eine Unterkunft in Basel bucht, erhält man die „Basel Card“, mit der man kostenlos öffentliche Verkehrsmittel nutzeb kann und Ermäßigungen in vielen Museen und anderen Einrichtungen erhält. Wir haben sie recht intensiv genutzt, aber Basel lässt sich auch ganz gut zu Fuß erkunden.

Unsere Unterkunft lag direkt neben dem Marktplatz, also sind wir dort kurz in den Innenhof des Rathaus gegangen. Anschließend sind wir weiter durch einige alte Straßen spaziert und die Gelegenheit genutzt, ein paar schöne Dinge zu kaufen. Als nächstes ging es zum „Karnevalsbrunnen“ des Schweizer Künstlers Jean Tinguely. Verschiedene Skulpturen bewegen sich dort und verspritzen Wasser. Ein sehr andere und faszinierender Springbrunnen. Er hat uns so gefallen, dass wir am nächsten Tag unbedingt das Tinguely-Museum besuchen wollten. Aber zuerst ging es weiter zum Basler Münster, einer Kathedrale aus dem 11. Jahrhundert. Hinter der Kathedrale haben wir dann ein einzigartiges Transportmittel entdeckt, das wir unbedingt ausprobieren mussten: eine „Holzfähre“, die keinen „Antrieb“ benötigt, um den Fluss zu überqueren. Sie steuert einfach um 45° in die Strömung des Rheins hinein, um auf die andere Seite zu gelangen.

Der nächste Tag war sehr regnerisch, wie die meisten Tage in dieser Woche, und wir haben daher das Tinguely-Museum besucht, das wirklich einige sehr coole Skulpturen zu bieten hatte. Um die Mechanik nicht zu schnell zu verschleißen, konnte man sie nur alle 9 Minuten manuell per Knopfdruck aktivieren. Am Abend hatten wir ein nettes Teamessen mit meinen Kolleginnen und Kollegen von Syde.

Contributor Day

Wie schon bei früheren WordCamp Flaggschiff-Events habe ich mich freiwillig als Table Lead beim Contributor Day für das Meta-Team gemeldet. Obwohl nur ein Contributor an meinen Tisch kam, hatten wir ein tolles Ergebnis. Xaver hat an einer coolen neuen Funktion gearbeitet, die es WordCamp-Organisationsteams ermöglicht, Teilnehmende schneller einzuchecken, indem sie einen QR-Code scannen, um sie in der Liste zu finden und sie als anwesend zu markieren.

Wie immer ist der Contributor Day viel schneller vorbei, als man denkt. Der Abend war dann dem „The Social“ gewidmet, einer Veranstaltung für alle Organizers, Volunteers, Sponsors und alle anderen, die das WordCamp Europe möglich machen.

Der erste Konferenztag

Syde hatte einen Stand im Sponsor-Bereich, aber ich hatte keinen „Standdienst“. Daher konnte ich die Gelegenheit nutzen und mir einige Vorträge ansehen und mit Leuten sprechen.

WordPress without Borders — The Fight for Digital Freedom

Nach den Opening Remarks bin ich noch für den ersten Vortrag geblieben, eine Keynote von Noel Tock. Er hat einige Statistiken über WordPress mit uns geteilt, zum Beispiel, wie die Anzahl der Installationen durch Ereignisse in den letzten Jahren, wie die Pandemie, beeinflusst wurde. Aber dann hat er den Vortrag in eine Präsentation darüber gemacht, wie WordPress Wohltätigkeitsorganisationen bei ihrer Arbeit helfen kann. Da er selbst eine Wohltätigkeitsorganisation hat, die Hunde in der Ukraine rettet, hat er einige andere Wohltätigkeitsorganisationen vorgestellt, die in der Ukraine tätig sind und was diese tun. Ein sehr anderer und emotionaler Start in das WordCamp Europe.

Building Support as core of product

Nach einer Pause, in der ich mich hauptsächlich mit Sponsoren und Teilnehmern unterhalten habe, ging ich zurück zum Track 1 für einige Lightning-Talks. Für den ersten war ich etwas zu spät dran, aber ich konnte trotzdem ein paar coole Tipps von Anna mitnehmen, wie man den Support zu einem wichtigen Teil des Unternehmens macht und nicht nur als Verantwortung eines einzelnen Teams.

3 WordPress Agency F*ckups and What I Learned from Them

Der zweite Talk war ein lustiger. Ich hatte schon auf dem Contributor Day die Gelegenheit kurz mit Jennifer über ihren Vortrag zu sprechen. Sie hat uns von „Albträumen“ erzählt, die sie in verschiedenen Projekten erlebt hat. Mit vielen davon konnte ich mich gut identifizieren, denn ich habe sie selbst in den letzten 10 Jahren, in denen ich für WordPress-Agenturen gearbeitet habe, erlebt.

What we learned from building Site Kit and how you can apply this to your WordPress site

Im letzten Lightning-Talk dieser Runde hat uns Mariya einige Geschichten über Site Kit erzählt, das offizielle WordPress Plugin von Google. Sie hat uns einge Tipps gegeben, wie die Erkenntnisse aus diesem Plugin anderen bei ihrer eigenen Arbeit helfen können. Es ist immer schwierig, wenn nicht sogar unmöglich, ein Plugin zu entwickeln, das alle zufriedenstellt.

Connecting cultures at SWI Swissinfo.ch: The journey to international audience engagement

Das war der erste Vortrag, den einer meiner Kollegen gehalten hat. Unser Commercial Director bei Syde, Ronny Marx, hat diesen Vortrag zusammen mit Isabelle Schrills von Swissinfo gehalten. Beide haben von ihren Einblicke in einem gemeinsamen Projekt erzählt. Sowohl aus Sicht von Swissinfo, also auch von uns als Agentur. Sie haben gezeigt, wie gut ein Projekt laufen kann, wenn beide Seiten gemeinsam an einer neuen Website arbeiten. Da Swissinfo Inhalte in 10 verschiedenen Sprachen veröffentlicht, war der Einsatz einer WordPress-Multisite in Kombination mit der eigenen mehrsprachigen Plugin-Lösung MultilingualPress von Syde perfekt für diese Art von Projekt geeignet, und Isabelle hat berichtete, dass alle mit dem neuen System sehr zufrieden waren und nicht auf das alte zurück zurückschauen.

WordPress Speed Build

Eines meiner Highlights auf dem WordCamp Europe 2024 in Turin im letzten Jahr war die Speed Build Challenge, die von Jamie Marsland moderiert wurde. In dieser Ausgabe bekamen Ellen Bauer und Fabian Kägy eine echte „Herausforderung“. Jamie hat ihnen eine „ausgefallene“ E-Commerce-Seite als Aufgabe gegeben, einschließlich einiger „Laufbänder“, Slidern, verspielter Schriftarten und anderer Dinge, die schwer nachzubauen waren. Fabian hat es geschafft mehr Dinge visuell zu gestalten, aber Ellen konnte in der kurzen Zeit sogar WooCommerce einrichten, so dass ihr Ergebnis ein echter Shop war. Die Aufgabe war allerdings nicht wirklich in 30 Minuten zu schaffen, aber beide hatten großen Spaß und haben das Publikum bei ihrem Versuch, die Seite nachzubauen, gut unterhalten.

Der zweite Konferenztag

Ich hatte mich ursprünglich für meinen allerersten WCEU-Workshop angemeldet, aber irgendwie meine Bestätigung nicht erhalten. OK, ich war auch sehr spät dran mit der Anmeldung, also ist es meine eigene Schuld. Und da wir für die letzte Nacht in ein anderes Hotel umziehen mussten, habe ich es am Morgen langsam angehen lassen.

Multilingual WordPress for developers

Ein weiterer Kollege von Syde, Dennis Plöttner, hat in Track 2 einen Vortrag über mehrsprachiges WordPress gehalten. Ein Thema, das Dennis sehr am Herzen liegt, da er auch ein kostenloses Plugin zur Erstellung einer mehrsprachigen Website entwickelt hat. Auch wenn ich schon Erfahrung damit habe, WordPress-Code übersetzbar zu machen, konnte auch ich aus seinem Vortrag etwas Neues mitnehmen. Ein solches Thema findet auf dem WordCamp Europe immer ein viel Interesse, besonders in einem Land mit vier offiziellen Sprachen wie der Schweiz.

WP Cafe: Five for the Future

Nach einer kleinen Pause bin ich ins „WP Cafe“ gegangen um mit anderen Teilnehmenden über die Initiative „Five for the Future“ zu diskutieren. In Anbetracht der jüngsten Ereignisse gab es viel zu besprechen. Viele Teilnehmende haben großartige Ideen eingebracht, und ich freue mich sehr darauf, diese wichtige Initiative in unserer Community voranzutreiben.

Why Block Themes Make WooCommerce Stores Better

Einige von euch wissen vielleicht, dass ich E-Commerce nicht mag. Das hat nichts mit WooCommerce oder einer anderen Lösung zu tun, ich mag nur nicht die Komplexität, die damit verbunden ist, und die Zeit, die ich brauche, um mich in die Materie einzuarbeiten, da ich nur ganz selten die Gelegenheit habe, an E-Commerce-Projekten zu arbeiten. Aber ich habe mich auf den Vortrag von Ellen gefreut. Der Saal war fast voll, und ich habe einige neue Dinge darüber gelernt, wie Blöcke WooCommerce in Zukunft wirklich zu einer neuen Erfahrung für Shops machen können. Sie hat uns auch erste Teile eines neuen WooCommerce „Standardtheme“ gezeigt, das ich mir unbedingt ansehen möchte, sobald es in etwa einem Monat zusammen mit WooCommerce 10 veröffentlicht wird.

Fireside chat with Mary Hubbard and Matt Mullenweg

Nachdem viele Sponsoren ihre Verlosungen durchgeführt hatten – ich habe nichts gewonnen, aber eine meiner Kolleginnen hat ein Lego-Set mit Braille-Schrift gewonnen – bin ich zur letzten Session gegangen. Die erste Hälfte bestand aus einem Gespräch zwischen Mary – die ich noch nie zuvor getroffen hatte – und Matt, in dem Mary einige Fragen gestellt hat. Die zweite Hälfte bestand aus Fragen aus dem Publikum, und sowohl Mary als auch Matt haben sich etwa 15 Minuten mehr Zeit genommen, um alle Fragen zu beantworten.

Closing Remarks

Es ist immer ein besonderer Teil: Wir können uns bei dem großartigen Organisationsteam und all den Freiwilligen für ihre Arbeit bedanken. Ich habe versucht, den Moment festzuhalten, aber die Linse meines Handys war nicht weit genug, um alle auf das Foto zu bekommen. Das zeigt auch, wie viele Menschen dabei mithelfen, diese Woche zu einem Ereignis für uns alle zu machen!

Click here to display content from Twitter.
Erfahre mehr in der Datenschutzerklärung von X.

Dann der Moment, auf den alle Teilnehmenden gewartet haben. Die aktuellen Global Leads verabschieden sich und das neue Team kommt auf die Bühne, um zu verkünden, wo das nächste WordCamp Europe stattfinden wird. Ich hatte es schon ein wenig geahnt und lag richtig. Aber seht selbst:

Click here to display content from Twitter.
Erfahre mehr in der Datenschutzerklärung von X.

Die After Party

Ja, es gab wie jedes Jahr auch wieder eine After Party. Ich habe dabei die letzte Gelegenheit genutzt, mit einigen Leuten zu sprechen, die ich bisher noch nicht ansprechen konnte. Und trotzdem gab es viele, mit denen ich diese Woche noch nicht gesprochen habe. Auch wenn es dieses Jahr mit „nur“ 1723 Teilnehmern weniger waren, reicht die Zeit einfach nicht aus, um mit allen ins Gespräch zu kommen. Aber ich hoffe, dass ich viele von euch nächstes Jahr in Polen sehe!

Fazit

Ich war bisher auf allen WordCamp Europes, und in Basel habe ich festgestellt, dass es seit Berlin genauso viele WordCamp Europes (einschließlich der Online-Versionen) gegeben hat wie vor Berlin. Ich kann kaum glauben, dass es schon so lange her ist.

Basel war auf seine eigene Art besonders. Das Wetter war vielleicht das regnerischste für ein WCEU, aber die Stadt war einfach fantastisch, und ich bin froh, dass ich wieder dabei sein konnte. Aber Basel war auch das teuerste Event. Nicht nur für das Organisationsteam, das mit einem sehr knappen Budget auskommen musste. Mit nur 50 € pro Ticket haben sie es dennoch geschafft, genügend Sponsoring aufzutreiben, um die Veranstaltung zu einem großartigen Gesamterlebnis zu machen. Aber leider war Basel für viele Community-Mitglieder einfach zu teuer, um die Reise dieses Jahr anzutreten. Ich glaube, dass Polen es viel mehr Teilnehmenden ermöglichen wird, wiederzukommen, was auch zu einer viel diversenden Gruppe von Teilnehmenden führen wird. Also nicht nur mit denjenigen, die es sich leisten können oder von ihren Unternhemen gesponsert werden. Wir sehen uns dann also hoffentlich nächstes Jahr im Juni!

Implementierung einer eigenen Block-Style-Variantion nur mit JSON-Dateien

Für ein kleines Projekt habe ich mein erstes Block-Theme (das ich fertiggestellt habe) mit dem Plugin Create Block Theme implementiert. In der PDF-Datei mit dem Design gab es einen „runden Button“, bei dem ich mir nicht direkt sicher war, wie ich ihn umsetzen könnte.

Auf den ersten Blick sah es recht einfach aus. Ich füge einen Button ein, schreiben etwas Text hinein und setzen den border-radius auf 50%, um den Button rund zu bekommen. Das Problem ist nur, dass das nur funktioniert, wenn der Button exakt die gleiche Höhe und Breite hat. Da wir aber im Voraus nicht wissen, welchen Text wir im Button stehen haben werden. Wenn wir einen längeren Text verwenden, erhöht sich auch nur die Breite. Wir erhalten dann einen Button mit einer ovalen Form:

The Block Editor with a button and a "border-radius" set to "50%", showing an oval button.

Das funktioniert also nicht. Aber wie können wir den Button perfekt rund machen? Wir müssen die aspect-ration (also das „Seitenverhältnis“) des Buttons auf 1 setzen. Die Registrierung von eigenen Seitenverhältnissen wurde zwar in WordPress 6.6 eingeführt, kann aber nicht für jeden Block verwendet werden. Es ist derzeit nur für die Blöcke Bild, Beitragsbild und Cover verfügbar. Wie können wir das Ergebnis also dennoch erreichen? An dieser Stelle kommt die Block-Style-Variation ins Spiel.

Was ist eine Block-Style-Variation?

Wie viele von euch wissen, ist eines der schwierigsten Dinge in der IT die Benennung von Dingen. Es gibt viele Dinge im „Gutenberg-Projekt“ mit ähnlichen Namen, was alles sehr verwirrend macht. Eine Block-Style-Variation – manchmal auch nur als „Block Style“ bezeichnet – ist eine Funktionalität, mit der wir einen alternativen Stil für einen Block erstellen können. Wenn wir an den core/button Block denken, haben wir normalerweise die Stile „Füllen“ und „Kontur“. Der core/table Block hat die Stile „Standard“ und „Streifen“. Der WordPress Core kommt mit vielen dieser Blockstile. Ihr könnt aber auch eure eigenen Stile erstellen.

Erstellen einer eigenen Block-Style-Variation

Auf der Dokumentationsseite über Block-Style-Variations finden wir (derzeit) zwei verschiedene Methoden, um einen eigenen Blockstil zu registrieren: PHP und JavaScript. Diejenigen unter euch, die seit einigen Jahren WordPress-Themes schreiben, sind wahrscheinlich (noch) recht vertraut mit PHP. Sehen wir uns also an, wie man es mit PHP macht.

Registrieren eines Blockstils mit PHP

Wir wollen einen runden Button haben. Hierzu müssen wir nur etwas PHP zu unserer functions.php Datei hinzuzufügen:

function tt5_round_button_register_block_styles() {
	register_block_style(
		'core/button',
		[
			'name' => 'round',
			'label' => esc_attr__( 'Round', 'twentytwentyfive-round-button' ),
			'inline_style' => '.wp-block-button.is-style-round {
				aspect-ratio: 1;
				border-radius; 50%;
			}'
		]
	);
}
add_action( 'init', 'tt5_round_button_register_block_styles' );

Mit diesem Code können wir nun den neuen Block-Style aus den „Stile“ (Styles) Einstellungen in der „Block“ Seitenleiste auswählen:

The Block Editor with a button and the "Round" block style selected, showing the button as a perfect circle.

Das sieht doch schon mal gut aus! Und es war nicht wirklich viel Code notwendig. Aber es fühlt sich nicht richtig an, PHP zur Registrierung eines Blockstils zu verwenden, wenn wir ein Blockt-Theme erstellen wollen. Wie können wir es also stattdessen tun? Nun, wir könnten JavaScript verwenden. Ein Beispiel dafür gibt es auf der Dokumentation-Seite zu finden, aber diese Lösung gefällt mir noch weniger.

Wenn ihr auf dieser Seite nach unten scrollen, werdet ihr diesen Hinweis finden:

It is not currently possible to customize your custom-registered block styles via theme.json. You can only style those registered by core WordPress at the moment.

Es wird also „behauptet“, dass ihr die eigenen Stile nicht über die theme.json Datei ändern könnt, sondern nur Core-Blöcke. Das ist wirklich bedauerlich. Aber es ist auch nicht wahr. Ich habe eine Lösung gefunden, um einen Blockstil zu registrieren und ihn auch über die theme.json-Datei zu gestalten.

Registrieren und Anpassen von Styles für einen Blockstil mit JSON

Die Funktion, eigene Blockstile zu verwenden, wurde ebenfalls in WordPress 6.6 eingeführt. Um sie zu nutzen, müsst ihr eine neue Datei im Ordner styles/blocks eures Themes erstellen. Wir könnten also eine Datei styles/blocks/round.json mit folgendem Inhalt erstellen:

{
  "version": 3,
  "$schema": "https://schemas.wp.org/wp/6.8/theme.json",
  "title": "Round",
  "slug": "round",
  "blockTypes": [
    "core/button"
  ],
  "styles": {
    "border": {
      "radius": "50%"
    },
    "dimensions": {
      "aspectRatio": "1"
    }
  }
}

Das ist wirklich alles, was wir tun müssen. Wir können dieser Datei einen beliebigen Namen geben. Und wir müssen sie nicht „laden“, das wird automatisch von WordPress erledigt. Ihr sollten hier "version: 3" verwenden und auch das $schema so einstellen, dass mindestens „6.8“ oder höher (oder trunk, für die neueste Version) verwendet wird.

Überschreiben von eigenen Block-Styles

Auch wenn der Hinweis behauptet hat, dass ihr eigene Blockstile nicht in der theme.json Datei überschreiben könnt, ist das nicht richtig. Ich habe es in einer lokalen WordPress 6.8-Installation erfolgreich getestet. Es würde in etwa so aussehen:

{
  "version": 3,
  "$schema": "https://schemas.wp.org/wp/6.8/theme.json",
  "styles": {
    "blocks": {
      "core/button": {
        "variations": {
          "round": {
            "border": {
              "color": "currentColor",
              "style": "solid",
              "width": "3px"
            },
            "color": {
              "text": "currentColor",
              "background": "white"
            }
          }
        }
      }
    }
  }
}

Hiermit würde der Rahmen und die Farben überschrieben werden und uns einen „runde Kontur-Button“ geben, der so aussehen würde:

The Block Editor with a button and the "Round" block style selected, showing the button as a perfect circle, that has a 3px "outline" style.

Das alles war nur durch mit der Verwendung von JSON-Dateien möglich. Es war nicht nötig, „echten Code“ in PHP oder JavaScript zu schreiben.

Einschränkungen

Auch wenn wir es geschafft haben, einen runden Button ohne „eigenen Code“ zu erstellen, ist das Ergebnis nicht perfekt. Wir haben die dimensions.aspectRatio Eigenschaft für den Button verwendet, aber da sie noch nicht für den core/Button Block unterstützt wird, können wir sie in den globalen Stilen für das Thema nicht sehen und bearbeiten.

Fazit

Das Hinzufügen einer eigenen Block-Style-Variation zu einem bestehenden Core-Block kann die Erstellung konsistenter Layouts für Inhalte erleichtern. Wenn ihr das Schreiben von „echtem Code“ vermeiden möchten, können ihr sie nur mithilfe von JSON-Dateien hinzufügen.

Wenn ihr mehr über dieses Thema erfahren möchten, kann ich euch die Seite „Block style variations and section styles“ auf fullsiteediting.com empfehlen, die weitere Beispiele enthält.

Ich hoffe wirklich, dass die aspectRatio und andere Optionen auch für andere Blöcke verfügbar gemacht werden und dass wir in Zukunft in der Lage sein werden, Blockstile nur mit dem Site-Editor zu erstellen. Es gibt bereits ein Ticket zu diesem Thema, aber ich verstehe, dass das eine ziemlich große Funktionalität ist.

Ein eigenes Panel zum Query Monitor hinzufügen

Eine der Fragen, die ich in verschiedenen Gelegenheiten häufig höre, ist diese: „Welches Plugin verwendest Du auf jeder Website?“. Darauf habe ich normalerweise keine Antwort, da es meiner Meinung nach kein „Pflicht-Plugin“ gibt. Aber ein Plugin, das ich fast immer einsetze, zumindest während der Entwicklung einer Website, ist das Plugin Query Monitor. Es hilft mir sehr dabei, Probleme mit der Website zu finden. Ich habe es sogar auf einigen Produktionsseiten laufen.

Da ich an einigen Projekten arbeite, die auf WordPress VIP gehostet werden, wurde ich gebeten, an einigen VIP-Trainings teilzunehmen. In einer dieser Trainings ging es um verschiedene Möglichkeiten, wie man eine Website debuggen kann. Und natürlich ist Query Monitor auch auf WordPress-VIP-Websites verfügbar.

Erweiterung des Query Monitor Plugins

Da ich Query Monitor häufig benutze, bin ich mit den meisten Panels davon sehr vertraut. Einige davon benutze ich ständig, andere eher selten. Aber ich wusste bisher nicht, dass man „einfach“ eigene Panels mit eigenen Debugging-Daten zu Query Monitor hinzufügen kann. Einige Plugins verwenden diese Möglichkeit schon, um Informationen hinzuzufügen. Andere stellen zusätzliche „Debugging Plugins“ bereit, um die Panels hinzuzufügen. Auf der Query Monitor Website findet ihr eine Liste dieser „Add-On Plugins“.

In dem VIP-Training gab es ein Beispiel dafür, wie man ein neues Panel mit eigenen Daten hinzufügt. Das hat mich so fasziniert, dass ich es direkt mal ausprobieren wollte. Ich habe auch einen schönen Anwendungsfall für meinen Blog gefunden. Ich verwende MultilingualPress, um meine Blogbeiträge zu übersetzen. Daher fand ich die Idee spannend, eine Liste der übersetzten Inhalte für Seiten oder Beiträge zu sehen. Genau das werde ich hier also demonstrieren.

Sammeln der Daten

Das Query Monitor Plugin ist in OOP (objektorientierte Programmierung) geschrieben und das erste, was wir implementieren müssen, ist eine Klasse, die die QM_DataCollector Klasse von Query Monitor erweitert. Diese sammelt die Daten, die wir später anzeigen wollen.

Die Daten werden in einem „Storage“ gespeichert. Wir müssen keinen eigenen implementieren, aber ich wollte eine vollständige Lösung schreiben, also habe ich zuerst diese Implementierung einer QM_Data-Klasse erstellt:

class QM_Data_MLP_Translations extends QM_Data {
	/**
	 * @var Translation[]
	 */
	public $translations;
}

Die MultilingualPress-API verwendet eine eigene Klasse Translation, von der wir ein Array dieser Objekte in unserer Datenklasse verwenden. Jetzt können wir unseren QM_DataCollector implementieren:

class QM_Collector_MLP_Translations extends QM_DataCollector {

	public $id = 'mlp_translations';

	private $current_locale;

	public function __construct() {
		parent::__construct();
		$this->current_locale = get_locale();
	}

	public function get_storage(): QM_Data {
		return new QM_Data_MLP_Translations();
	}

	public function process(): void {
		$args = TranslationSearchArgs::forContext(
			new WordPressContext()
		)->forSiteId( get_current_blog_id() )->includeBase();

		$translations = resolve( Translations::class )->searchTranslations( $args );

		$this->data->translations = [];
		foreach ( $translations as $translation ) {
			if ( $translation->language()->locale() !== $this->current_locale
				 && $translation->remoteContentId() !== 0 ) {
				$this->data->translations[] = $translation;
			}
		}
	}
}

Jeder QM_DataCollector benötigt eine $id Eigenschaft. Wir verwenden hier den Wert mlp_translations mit einem passenden Vendor-Präfix. Im Konstruktor speichern wir auch die aktuelle „Locale“ ab, da es aus irgendwelchen Gründen später auf die Locale aus dem Profil geändert wurde. In der Funktion get_storage() verwenden wir das QM_Data Objekt, das wir zuerst implementiert haben.

Der wichtigste Teil ist die Funktion process(). Hier sammeln wir die Daten und fügen sie unserem Storage hinzu. Die ersten paar Zeilen müsst ihr nicht verstehen, da dies die spezifischen Funktionen von MultilingualPress sind, um eine Liste aller Übersetzungen zu einem Beitrag oder einer Seite zu erhalten. Die Sammlung eurer Daten kann anders aussehen, aber vermutlich werdet ihr Ende eine Schleife haben, um die Daten, die ihr verwenden wollt, in $this->data einzufügen. In meinem Fall filtere ich die aktuelle Übersetzung heraus und füge auch nur die mit einer remoteContentId hinzu, was mir nur „Einzel-Inhalte“ liefern würde.

Initialisierung des Collectors

Der letzte Schritt besteht darin, das „Collector“-Objekt in einem Filter zu initialisieren. Dies wird mit dem folgenden Filter durchgeführt:

function register_qm_collector_mlp_translations( array $collectors ) {
	$collectors['mlp_translations'] = new QM_Collector_MLP_Translations();

	return $collectors;
}

add_filter( 'qm/collectors', 'register_qm_collector_mlp_translations', 10 );

Nichts wirklich Ausgefallenes hier. Nachdem wir nun unseren „Collector“ mit den Daten haben, die wir anzeigen wollen, können wir uns ansehen, wie wir das umsetzen.

Hinzufügen unserer Daten zum Query Monitor

Query Monitor verfügt über mehrere Panels. Wir können entweder ein eigenes Panel hinzufügen oder wir fügen Daten zu einem bestehenden Panel als „Child Panel“ hinzu. Ich habe mich für den zweiten Ansatz entschieden, da es mir logischer erschien, meine Daten dem Panel „Sprachen“ hinzuzufügen. Hier ist der vollständige Code (ohne Kommentare):

class QM_Output_MLP_Translationsextends QM_Output_Html {

	protected $collector;

	public function __construct( QM_Collector $collector ) {
		parent::__construct( $collector );
		add_filter( 'qm/output/panel_menus', array( $this, 'panel_menu' ), 20 );
	}

	public function name() {
		return __( 'Translations', 'multilingualpress' );
	}

	public function output() {
		$data = $this->collector->get_data();

		if ( empty( $data->translations ) ) {
			$this->before_non_tabular_output();

			$notice = __(
				'No translations for this content.',
				'multilingualpress-qm'
			);
			echo $this->build_notice( $notice );

			$this->after_non_tabular_output();

			return;
		}

		$id = sprintf( 'qm-%s', $this->collector->id );

		$this->before_tabular_output( $id );

		$this->output_translation_table( $data->translations );

		$this->after_tabular_output();
	}

	protected function output_translation_table( array $translations ): void {
		echo '<thead>';
		echo '<tr>';
		printf( '<th>%s</th>', esc_html__( 'Locale', 'multilingualpress-qm' ) );
		printf( '<th>%s</th>', esc_html__( 'Title', 'multilingualpress-qm' ) );
		printf( '<th>%s</th>', esc_html__( 'Post ID', 'multilingualpress-qm' ) );
		printf( '<th>%s</th>', esc_html__( 'Site ID', 'multilingualpress-qm' ) );
		echo '</tr>';
		echo '<tbody>';

		foreach ( $translations as $translation ) {
			echo '<tr>';
			printf(
				'<th scope="row"><code>%s</code></th>',
				esc_html( $translation->language()->locale() )
			);
			printf(
				'<td class="qm-ltr">%s</td>',
				$this->build_link(
					$translation->remoteUrl(),
					$translation->remoteTitle()
				)
			);
			printf(
				'<td class="qm-num">%s</td>',
				esc_html( $translation->remoteContentId() )
			);
			printf(
				'<td class="qm-num">%s</td>',
				esc_html( $translation->remoteSiteId() )
			);
			echo '</tr>';
		}

		echo '</tbody>';
	}

	public function panel_menu( array $menu ) {
		if ( ! isset( $menu['qm-languages'] ) ) {
			return $menu;
		}

		$data = $this->collector->get_data();

		$menu['qm-languages']['children'][] = $this->menu(
			[
				'title' => sprintf(
					esc_html__(
						'Content Translations (%s)',
						'multilingualpress-qm'
					),
					number_format_i18n( count( $data->translations ) )
				)
			]
		);

		return $menu;
	}
}

Lasst mich einige der wichtigsten Funktionen erklären. Im Konstruktor verwenden wir einen Filter, um unsere panel_menu() Funktion aufzurufen, die dann das Panel hinzufügt. Hier könnt ihr sehen, dass ich die Daten zu den children von qm-languages hinzufüge. Außerdem hole ich mir hier die gesammelten Daten, um die Anzahl der Übersetzungen zum Titel des Panels hinzufügen zu können.

In der output() Funktion erzeugen wir die eigentliche Ausgabe für das Panel. Zuerst wird geprüft, ob überhaupt Daten vorhanden sind. Wenn nicht, geben wir nur einen Hinweis aus. Vielleicht sind auch die ganzen Funktionen mit Namen wie $this->before_*_output() und $this->after_*_out() aufgefallen. Diese werden von der übergeordneten Klasse QM_Output_Html geerbt und ersparen uns das Schreiben von HTML-Markup.

Die wichtigste Ausgabe findet in der Funktion output_translation_table() statt. Dies ist eine Hilfsfunktion, die ich hinzugefügt habe. Vielleicht braucht ihr eine solche Funktion nicht. Ihr könnt sie auch beliebig umbenennen, damit sie besser zu eurem Anwendungsfall passt. Ich habe sie einfach ähnlich benannt wie andere Funktionen in Query Monitor selbst.

Nachdem wir den <thead> mit einigen Tabellenspaltennamen erzeugt haben, geben wir die Daten für die Übersetzungen aus. Wir verwenden einige Methoden der Translation Objekte von MultilingualPress, um die Site-ID, die Post-ID, den Beitragstitel und den Permalink für den übersetzten Inhalt zu erhalten.

Initialisierung der Ausgabeklasse

Genau wie bei der Collector-Klasse müssen wir auch unsere HTML-Ausgabeklasse initialisieren. Wir verwenden hier einen weiteren Filter:

function register_qm_output_mlp_translations( array $output ) {
	$collector = QM_Collectors::get( 'mlp_translations' );
	if ( $collector ) {
		$output['mlp_translations'] = new QM_Output_MLP_Translations( $collector );
	}

	return $output;
}

add_filter( 'qm/outputter/html', 'register_qm_output_mlp_translations', 50 );

Mit der Implementierung dieser letzten Klasse haben wir alle Teile zusammen, die wir für unser eigenes Query Monitor Panel brauchen.

Alles zusammenfügen

Jetzt fehlt nur noch ein letzter Schritt. Wir müssen noch alle Dateien laden. Das können wir am einfachsten wie folgt machen:

if ( defined( 'QM_DISABLED' ) && constant( 'QM_DISABLED' ) ) {
	return;
}

add_action(
	'plugins_loaded',
	static function () {
		require_once __DIR__ . '/data/languages_mlp_translations.php';
		require_once __DIR__ . '/collectors/languages_mlp_translations.php';
		require_once __DIR__ . '/output/html/languages_mlp_translations.php';
	}
);

Um zu vermeiden, dass die Klassen geladen werden, wenn Query Monitor deaktiviert wurde, überprüfen wir zuerst die Konstante QM_DISABLED. Dann benötigen wir Dateien mit den drei Klassen, die die Filter enthalten, um sie zu initialisieren. Bei der Implementierung wollte ich mich so eng wie möglich an die Codebasis von Query Monitor orientieren. Normalerweise würde ich Filter zur Initialisierung von Klassen nicht in derselben Datei wie die Klassen selbst unterbringen.

Das Ergebnis

Nachdem wir all das umgesetzt haben, wollt ihr wahrscheinlich alle das Ergebnis unserer Arbeit sehen, richtig? Dann lasst mich das Plugin mal schnell auf meinem Blog installieren und euch die Ausgabe für meinen letzten Blogbeitrag zeigen:

Screenshot with Query Monitor opened, showing the new "Content Translations" panel, listing the German translation of the last blog post.

Ziemlich cool, oder? Da mein Blog nur zwei Sprachen hat und es nur eine Übersetzung anzeigt, ist das vielleicht nicht besonders nützlich. Aber uns fallen sicher auch andere Informationen ein, die in diesem Panel angezeigt werden könnten. Vielleicht die Übersetzung aller im Blogbeitrag verwendeten Taxonomien. Oder einige wichtige Einstellungen von MultilingualPress.

Fazit

Das Hinzufügen eines eigenen Panels zum Query Monitor kann euch helfen, wichtige Informationen einfacher zu debuggen. Vielleicht habt ihr einen ganz anderen Anwendungsfall für ein eigenes Query Monitor Panel. Oder vielleicht habt ihr es in der Vergangenheit sogar selbst schon umgesetzt. Dann teilt gerne einen Link zu eurer Lösung mit uns, falls sie öffentlich ist.

Wenn ihr auch MultilingualPress auf eurer Website einsetzen oder einfach nur den vollständigen Code dieses Beispiels sehen möchtet, findet ihr ihn auf Github. Dort könnt ihr ihn auch als Plugin-ZIP herunterladen und direkt testen.

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.