Programmatischer Zoom in Webapplikationen
2 Okt 2019
Der möglichst barrierefreie Zugriff auf Webseiten für Menschen mit Behinderungen ist seit jeher wichtig und erstrebenswert. Heutzutage, da sich auch die Regierungsbehörden der meisten Länder mit diesem Thema beschäftigen und in weiterer Folge eindeutige gesetzliche Standards und Regelungen einführen, ist es für Entwickler (im Sinne der Website-Kunden und Endkonsumenten) umso wichtiger, diese Standards vollständig und sinnvoll umzusetzen, damit Website-Projekte so zugänglich und barrierefrei wie nur möglich werden.
Die Empfehlung des World Wide Web Consortiums (= W3C; das Gremium zur Standardisierung der Techniken im World Wide Web) kennt drei verschiedene Konformitätsstufen: A, AA und AAA. Um die Stufe AA zu erreichen, muss der Entwickler unter anderem eine Möglichkeit zur Verfügung stellen, mit der die Schriftgröße einer Website vergrößert werden kann:
Textgröße anpassen: Grundsätzlich können Textinhalte auf Webseiten (mit der Ausnahme von Untertiteln und Bildern von Texten ohne assistierende Techniken zu bis zu 200 Prozent vergrößert werden, ohne dass dabei Inhalte oder die Funktionalität verloren gehen (Level AA).
Sehen wir uns aus diesem Grund die verschiedenen Lösungsmöglichkeiten für diese spezielle Anforderung an eine Website an und überlegen uns, welche davon ganz objektiv betrachtet das beste und eleganteste Ergebnis abliefert.
# Unzufriedenstellende Lösung Nummer 1: CSS zoom
Das erste Schlagwort, das meistens in Zusammenhang mit der Vergrößerung der Schriftgröße in den Ring geworfen wird, ist zoom. CSS verfügt über eine zoom-Eigenschaft, die genau das erledigt, was wir brauchen: Die Schrift vergrößern.
Werfen wir hierzu einen prüfenden Blick auf das gängige Designmuster (das wir für den Rest dieses Artikels beibehalten werden): Eine horizontale Navigationsleiste, die bei einem spezifischen Breakpoint zu einem Menüsymbol wird:
Hier passiert genau das, was wir uns erhofft haben. Kein Umbruch und das gesamte Menü wird bei einem spezifischen Breakpoint zu einem Menüsymbol.
Das untenstehende GIF visualisiert, was passiert, wenn wir den zoom-Ansatz auf das Menü-Element anwenden. Konkret haben wir hier einen Schalter erstellt, mit dem wir unterschiedliche Schriftgrößen auswählen und ein angemessenes zoom-Level anwenden können:
Für alle Experimentierfreudigen gibt es den dazugehörigen Pen hier..
Das Menü überschreitet den sichtbaren Bereich, da wir mit zoom weder programmatisch die Breite des Viewports erhöhen, noch einen Umbruch beim Menü vornehmen können. Das Menüsymbol erscheint ebenfalls nicht, da die Größe des Bildschirms in Wahrheit nicht verändert wurde – diese ist immer noch genauso groß wie vor dem Klick auf den Schalter.
Alle diese Probleme machen zoom zu einer ziemlich unattraktiven Lösung – mal ganz davon abgesehen, dass Firefox diese Anwendung überhaupt nicht unterstützt.
# Unzufriedenstellende Lösung Nummer 2: Scale Transforms
Wenn wir transform: scale anstatt von zoom anwenden, erhalten wir mehr oder weniger das gleiche Ergebnis. Der einzig positive Unterschied ist, dass transform: scale von mehr Browsern unterstützt wird – was letzten Endes aber auch keinen Unterschied macht, wenn die Lösung nicht dazu geeignet ist, das Problem aus der Welt zu schaffen. Auch hier passt das Menü nicht in den sichtbaren Bereich und – was noch schlimmer ist – überschreitet sogar den vertikalen Sichtbarkeitsbereich, da das Seitenlayout auf Basis eines anfänglichen Skalierungsfaktors von -1 berechnet wird.
# Unzufriedenstellende Lösung Nummer 3: rem und htmlfont-size
Anstatt zu zoomen oder zu skalieren, können wir rem als Einheit zur Größenbestimmung für alle Seitenelemente anwenden. Wir können dann die jeweiligen Größen ändern, indem wir die font-size Eigenschaften der html-Elemente ändern, da 1rem per definitionem dem Schriftgrößen-Wert des htmls entspricht.
Diese Lösung ist schon ziemlich gut und definitiv raffinierter, aber noch nicht perfekt. Wie im folgenden Demo-GIF sichtbar wird, bleiben auch hier dieselben Probleme bestehen, die uns schon bei den zwei ersten Lösungsvarianten das Leben schwergemacht haben: Mit zunehmender Vergrößerung der Schrift passen die Textelemente aufgrund der zusätzlichen Platzanforderungen nicht mehr in den horizontalen Sichtbarkeitsbereich, wobei die Viewport-Breite allerdings intakt bleibt.
Das eigentliche Problem besteht hier darin, dass die Media Querys nicht automatisch an die Größenänderung angepasst werden. Wenn wir die Schrift vergrößern, sollten die Media Querys ebenfalls dementsprechend angepasst werden, und zwar vor der eigentlichen Größenanpassung und relativ zum Content.
Die tatsächlich funktionierende Lösung: Mit einem Sass mixin den Browser Zoom nachahmen
Nach den ersten drei fehlgeschlagenen Versuchen ist guter Rat teuer – deshalb nutzen wir den native Browserzoom als Inspirationsquelle und sehen uns an, wie dieser auf das Problem reagiert:
Wow! Chrome hat kapiert, dass durch das Zoomen der Viewport tatsächlich verändert wird. Je größer der Zoom, desto schmaler wird der Viewport. Das bedeutet, dass unsere Media Querys tatsächlich so arbeiten, so wie wir das von ihnen erwarten und für die Endnutzer der Website brauchen.
Um das zu erreichen, müssen wir irgendwie schaffen, dass die Werte der Media Querys jedes Mal automatisch upgedatet werden, sobald wir die Schriftgröße verändern (auf den native zoom können wir uns hierbei nicht verlassen, da wir keine Möglichkeit haben, diesen wie von AA eingefordert für die Bedienelemente auf unserer Seite zu nutzen).
Nehmen wir folgendes Szenario an: Wir haben einen Media Query Breakpoint bei 1024px und wir führen einen 200% Zoom durch. In weiterer Folgen müssten wir den Breakpoint auf 2048px updaten, um einen Ausgleich zur neuen Größe zu schaffen.
Das klingt erstmal logisch und die Annahme, dass es zu genau diesem Zweck eine simple und selbsterklärende Lösung geben muss, liegt nahe. Eine Idee wäre beispielsweise, die Media Querys mit rem Einheiten festzulegen, damit die Media Querys automatisch angepasst werden, sobald die font-size vergrößert wird. Hier müssen wir allerdings viele enttäuschen, denn dieser Ansatz funktioniert in der Realität nicht. Wie in diesem Pen klar wird, passiert genau nichts, wenn man ein Media Query von px zu rem abändert. Die Breakpoints im Layout ändern sich auch nach der Vergrößerung der Schriftgröße nicht. Wie in den W3C Standards erläutert wird, ist der Grund dafür, dass sowohl rem als auch em -Einheiten in Media Querys basierend auf dem anfänglichen Wert der font-size des html-Elements berechnet werden, der für gewöhnlich 16px ist (aber variieren kann).
Die relativen Einheiten in Media Querys basieren auf den anfänglich festgelegten Werten, was bedeutet, dass Einheiten niemals auf den Ergebnissen von Deklarationen basieren. Ein Beispiel: In HTML ist die Einheit em relativ zum anfänglichen Wert der ‘font-size.
Um dieses Problem zu umgehen, können wir glücklicherweise auf die Macht von Sass mixins zurückgreifen! Und so wird das konkret gemacht:
- Wir wenden auf jede Größe des html -Elements eine spezielle Klasse an (font-size–s, font-size – m, font-size–l, font-size – xl etc.)
- Wir nutzen einen speziellen mixin, der eine Media Query Regel für jede Kombination von Breakpoint und Größe erzeugt und der einerseits die Bildschirmbreite berücksichtigt und andererseits miteinbezieht, welche Modifierklasse auf das html –Element angewendet wurde.
- Überall dort, wo wir eine Media Query anwenden wollen, verpacken wir den Code mit diesem mixin.
Und so sieht dieser mixin dann aus:
$desktop: 640px;
$m: 1.5;
$l: 2;
$xl: 4;
// Der wirkliche Trick an der Sache findet sich hier. Wir erhöhen die min-width (also minimale Breite), wenn wir die font-size erhöhen
@mixin media-desktop {
html.font-size--s & {
@media (min-width: $desktop) {
@content;
}
}
html.font-size--m & {
@media (min-width: $desktop * $m) {
@content;
}
}
html.font-size--l & {
@media (min-width: $desktop * $l) {
@content;
}
}
html.font-size--xl & {
@media (min-width: $desktop * $xl) {
@content;
}
}
}
.menu {
@include media-desktop {
&__mobile {
display: none;
}
}
}
Hier ein Beispiel für das CSS, das dabei generiert wird:
@media (min-width: 640px) {
html.font-size--s .menu__mobile {
display: none;
}
}
@media (min-width: 960px) {
html.font-size--m .menu__mobile {
display: none;
}
}
@media (min-width: 1280px) {
html.font-size--l .menu__mobile {
display: none;
}
}
@media (min-width: 2560px) {
html.font-size--xl .menu__mobile {
display: none;
}
}
Wenn wir also n Breakpoints und m Größen haben, generieren wir n mal viele m Media Query Rules. Dadurch werden alle möglichen Fälle abgedeckt, wodurch uns die Lösung dieses allgegenwärtigen Problems doch zufriedenstellend und halbwegs elegant geglückt ist: Wir sind nun in der Lage, vergrößerte Media Querys zu nutzen, wenn die Schriftgröße erhöht wurde.
Im Pen unterhalb wird deutlich, wie diese Lösung genau funktioniert:
# Die Nachteile
Wie fast immer im Leben müssen wir zugeben, dass auch diese Lösungen nicht ohne kleine Makel daherkommt. Davon lassen wir uns aber nicht unterkriegen – lieber überlegen wir uns, wie wir diese Makel in den Griff bekommen können.
# Eine erhöhte Spezifität für Media Query Selektoren
Der gesamte Code innerhalb einer Media Query wird um ein zusätzliches Spezifitätslevel erweitert, da dieser innerhalb des html.font-size — x Selektors landet. Wenn wir uns an den „Mobile First“-Ansatz halten und beispielsweise den .no-margin -Modifier auf ein Element anwenden, kann der Normalstil des Desktops den Modifier übertrumpfen, wodurch die Desktop-spezifischen Ränder angewendet werden.
Um dies zu vermeiden, können wir denselben mixin für mobile Anwendungen erzeugen und nicht nur CSS Code für den Desktop, sondern auch mobilen CSS Code mit unserem mixin verpacken. So können wir das drohende Problem mit der Spezifität relativ gut ausgleichen.
Eine alternative Lösung wäre, wenn man alle möglichen Fälle mit einem individuellen Ansatz abdeckt, indem man künstlich die Spezifizität erhöht. Oder man könnte einen mixin mit der gewünschten Funktionalität erzeugen (in unserem Fall ist das „kein Rand“), der nicht nur für „mobile only“ angewendet wird, sondern in jeden Breakpoint Code kommt.
# Große Mengen an generierter CSS
Die Menge an generierter CSS wird erheblich höher sein, da wir denselben CSS-Code für jede mögliche Größe erzeugen müssen.
Das sollte allerdings kein Problem sein, wenn die Dateien in einem gzip verpackt sind (was für gewöhnlich der Fall ist), da dieser sich wiederholenden Code ziemlich gut handhaben kann.
# Diverse Front-End Frameworks wie Zurb Foundation nutzen bereits integrierte Breakpoints in JavaScript Dienstprogrammen und CSS Media Querys
Dieser Punkt ist eine harte Nuss. Unser persönlicher Standpunkt dazu ist, dass wir versuchen, Framework-Merkmale, die von der Bildschirmbreite abhängen, weitestgehend zu vermeiden. Jenes Framework, dessen Verlust hier bislang am meisten schmerzte, ist ein Grid System, also ein Gestaltungsraster. Seit dem Aufstieg von flexbox und grid ist das unserer Meinung nach allerdings nicht länger ein gravierendes Problem. In diesem großartigen Artikel wird im Detail erklärt, wie man sich sein eigenes Grid System bauen kann.
Falls ein Projekt aber unbedingt von diesen oder ähnlichen Frameworks abhängt oder falls wir uns dem Kampf gegen das Spezifizitäts-Problem nicht stellen wollen, die Stufe AA auf der Website aber trotzdem erreicht werden soll, würde unser Kompromiss-Vorschlag lauten, fixierte Höhenelemente ganz auszulassen. Stattdessen könnte eine Kombination aus rems und einer Anpassung des html- Elements font-size zum Einsatz kommen, um das Layout und die Textdimensionen dementsprechend anzupassen.
Wir bei buzzwoo sind immer darauf bedacht, unsere Expertise unter Beweis zu stellen und an Interessierte weiterzugeben. Gerne stehen wir für allgemeine und spezifische Fragen zur Verfügung, was die Umsetzung der Richtlinien für barrierefreie Webinhalte auf Ihrem Websiten-Projekt betrifft! Schreiben Sie uns hier!
Haben Sie Fragen oder benötigen eine individuelle Beratung zu dem, was Sie gerade gelesen haben? Zögern Sie nicht, uns zu kontaktieren! Besuchen Sie unsere Kontaktseite und lassen Sie uns ein Gespräch darüber beginnen, wie wir Ihnen helfen können, Ihre Ziele zu erreichen.