Experimente sind eine tolle Ausrede, um die neuesten Tricks zu lernen, neue Ideen zu entwickeln und an seine Grenzen zu gehen. „Pure CSS“-Demos gibt es schon seit einer Weile, aber neue Möglichkeiten eröffnen sich, wenn Browser und CSS selbst sich weiterentwickeln. CSS- und HTML-Präprozessoren haben ebenfalls dazu beigetragen, die Szene voranzubringen. Manchmal werden Präprozessoren zum Hardcoding jedes möglichen Szenarios verwendet, zum Beispiel für lange Zeichenketten von :checked und benachbarten Geschwister-Selektoren.
In diesem Artikel werde ich die Kernideen eines reinen CSS Connect 4-Spiels, das ich erstellt habe, erläutern. Ich habe versucht, so wenig Hardcoding wie möglich in meinem Experiment zu verwenden und ohne Präprozessoren zu arbeiten, um den resultierenden Code kurz zu halten. Den gesamten Code und das Spiel können Sie hier sehen
Siehe den Pen Pure CSS Connect 4 von Bence Szabó (@finnhvman) auf CodePen.
Wesentliche Konzepte
Ich glaube, es gibt einige Konzepte, die im Genre „Pure CSS“ als wesentlich gelten. Typischerweise werden Formularelemente zur Verwaltung des Zustands und zur Erfassung von Benutzeraktionen verwendet. Ich war begeistert, als ich Leute fand, die <button type="reset"> benutzten, um ein Spiel zurückzusetzen oder neu zu starten. Alles, was Sie tun müssen, ist, Ihre Elemente in ein <form>-Tag zu packen und den Button hinzuzufügen. Meiner Meinung nach ist dies eine wesentlich sauberere Lösung, als die Seite neu laden zu müssen.
Mein erster Schritt war die Erstellung eines Formularelements, in das ich dann eine Reihe von Eingaben für die Felder und den Reset-Button einfügte. Hier ist eine sehr grundlegende Demonstration von <button type="reset"> in Aktion
Siehe den Pen Pure HTML Form Reset von Bence Szabó (@finnhvman) auf CodePen.
Ich wollte ein schönes visuelles Design für diese Demo haben, um ein vollständiges Erlebnis zu bieten. Anstatt ein externes Bild für das Spielfeld oder die Scheiben zu verwenden, habe ich radial-gradient() verwendet. Eine großartige Ressource, die ich oft nutze, ist Lea Verous CSS3 Patterns Gallerie. Es ist eine Sammlung von Mustern, die aus Farbverläufen bestehen und auch editierbar sind! Ich habe currentcolor verwendet, was sich für das Scheibenmuster als sehr nützlich erwies. Ich habe eine Kopfzeile hinzugefügt und meinen Pure CSS Ripple Button wiederverwendet.

Scheiben aufs Spielfeld fallen lassen
Als Nächstes habe ich es den Benutzern ermöglicht, ihre Züge zu machen, indem sie Scheiben auf das Connect 4-Spielfeld fallen lassen. In Connect 4 lassen die Spieler (einer rot und einer gelb) abwechselnd Scheiben in Spalten fallen. Es gibt 7 Spalten und 6 Reihen (42 Felder). Jedes Feld kann leer sein oder von einer roten oder gelben Scheibe besetzt sein. Ein Feld kann also drei Zustände haben (leer, rot oder gelb). Scheiben, die in derselben Spalte fallen gelassen werden, stapeln sich übereinander.
Ich habe damit begonnen, zwei Checkboxen für jedes Feld zu platzieren. Wenn beide nicht angekreuzt sind, gilt das Feld als leer, und wenn eine von ihnen angekreuzt ist, hat der entsprechende Spieler seine Scheibe darin.
Der mögliche Zustand, dass beide angekreuzt sind, sollte vermieden werden, indem sie versteckt werden, sobald einer von ihnen angekreuzt ist. Diese Checkboxen sind unmittelbare Geschwister, so dass, wenn die erste eines Paares angekreuzt ist, beide durch die Verwendung der Pseudo-Klasse :checked und des benachbarten Geschwister-Kombinators (+) ausgeblendet werden können. Was ist, wenn die zweite angekreuzt ist? Sie können die zweite ausblenden, aber wie beeinflusst das die erste? Nun, es gibt keinen vorherigen Geschwister-Selektor, so funktionieren CSS-Selektoren einfach nicht. Diese Idee musste ich verwerfen.
Tatsächlich kann eine Checkbox *drei* Zustände von sich aus haben, sie kann im indeterminate-Zustand sein. Das Problem ist, dass man sie nicht allein mit HTML in den indeterminate Zustand versetzen kann. Selbst wenn man es könnte, würde der nächste Klick auf die Checkbox sie immer in den checked-Zustand verwandeln. Den zweiten Spieler zu zwingen, doppelt zu klicken, wenn er seinen Zug macht, ist unzuverlässig und inakzeptabel.
Ich hing in der MDN-Dokumentation für :indeterminate fest und bemerkte, dass Radio-Inputs ebenfalls einen indeterminate Zustand haben. Radio-Buttons mit demselben Namen befinden sich in diesem Zustand, wenn sie alle nicht angekreuzt sind. Wow, das ist ein tatsächlicher Anfangszustand! Was wirklich von Vorteil ist, ist, dass das Ankreuzen des späteren Geschwisters auch Auswirkungen auf das erstere hat! Daher füllte ich das Spielfeld mit 42 Paaren von Radio-Inputs.
Rückblickend hätte eine clevere Anordnung und Verwendung von Labels mit entweder Checkboxen oder Radio-Buttons den Trick gemacht, aber ich habe Labels nicht als Option betrachtet, um den Code einfacher und kürzer zu halten.
Ich wollte große Interaktionsflächen für eine gute UX haben, daher dachte ich, es sei sinnvoll, den Spielern einen Zug zu ermöglichen, indem sie auf eine Spalte klicken. Ich stapelte die Steuerelemente derselben Spalte übereinander, indem ich den entsprechenden Elementen absolute und relative Positionierung hinzufügte. Auf diese Weise konnte nur das unterste leere Feld innerhalb einer Spalte ausgewählt werden. Ich habe die Zeit des Übergangs des Scheibenfalls pro Reihe akribisch eingestellt und die Timing-Funktion nähert sich einer quadratischen Kurve an, um einen realistischen freien Fall zu simulieren. Bisher kamen die Puzzleteile gut zusammen, obwohl die untenstehende Animation deutlich zeigt, dass nur der rote Spieler seine Züge machen konnte.

Die klickbaren Flächen von Radio-Inputs werden mit farbigen, aber halbtransparenten Rechtecken visualisiert. Die gelben und roten Inputs sind jeweils sechsmal (= sechs Reihen) pro Spalte übereinander gestapelt, wobei der rote Input der untersten Reihe oben auf dem Stapel liegt. Die Mischung aus Rot und Gelb erzeugt die orangefarbene Farbe, die zu Beginn auf dem Spielfeld zu sehen ist. Je weniger leere Felder in einer Spalte verfügbar sind, desto weniger intensiv wird diese orangefarbene Farbe, da die Radio-Inputs nicht angezeigt werden, sobald sie nicht mehr :indeterminate sind. Da der rote Input in jedem Feld immer genau über dem gelben Input liegt, kann nur der rote Spieler Züge machen.
Züge verfolgen
Ich hatte nur eine vage Vorstellung und viel Hoffnung, dass ich das Wechseln der Züge zwischen den beiden Spielern irgendwie mit dem allgemeinen Geschwister-Selektor lösen könnte. Das Konzept war, den roten Spieler am Zug zu lassen, wenn die Anzahl der angekreuzten Eingaben gerade war (0, 2, 4 usw.) und den gelben Spieler am Zug zu lassen, wenn diese Zahl ungerade war. Bald erkannte ich, dass der allgemeine Geschwister-Selektor nicht so funktioniert, wie ich es wollte (und auch nicht sollte!).
Dann war die offensichtliche Wahl, mit den nth-Selektoren zu experimentieren. So reizvoll es auch war, die Schlüsselwörter even und odd zu verwenden, stieß ich auf eine Sackgasse. Der :nth-child-Selektor „zählt“ die Kinder innerhalb eines Elternelements, unabhängig von Typ, Klasse, Pseudo-Klasse oder sonstigem. Der :nth-of-type-Selektor „zählt“ die Kinder eines Typs innerhalb eines Elternelements, unabhängig von Klasse oder Pseudo-Klasse. Das Problem ist also, dass sie nicht auf dem :checked-Status basieren können.
Nun, CSS-Zähler zählen auch, also warum nicht versuchen? Eine häufige Verwendung von Zählern ist die Nummerierung von Überschriften (auch auf mehreren Ebenen) in einem Dokument. Sie werden durch CSS-Regeln gesteuert, können jederzeit beliebig zurückgesetzt werden und ihre Inkrementierungs- (oder Dekrementierungs-) Werte können beliebige ganze Zahlen sein. Die Zähler werden mit der Funktion counter() in der content-Eigenschaft angezeigt.
Der einfachste Schritt war die Einrichtung eines Zählers und das Zählen der :checked-Eingaben im Connect 4-Raster. Es gibt nur zwei Schwierigkeiten bei diesem Ansatz. Die erste ist, dass man keine Arithmetik mit einem Zähler durchführen kann, um festzustellen, ob er gerade oder ungerade ist. Die zweite ist, dass man keine CSS-Regeln auf Elemente basierend auf dem Zählerwert anwenden kann.
Die erste Schwierigkeit habe ich durch die Binärisierung des Zählers gelöst. Der Wert des Zählers ist anfangs null. Wenn der rote Spieler seinen Radio-Button ankreuzt, wird der Zähler um eins erhöht. Wenn der gelbe Spieler seinen Radio-Button ankreuzt, wird der Zähler um eins dekrementiert und so weiter. Daher wird der Zählerwert entweder null oder eins sein, gerade oder ungerade.
Die Lösung des zweiten Problems erforderte viel mehr Kreativität (lies: Hack). Wie bereits erwähnt, können Zähler angezeigt werden, aber nur in den Pseudo-Elementen ::before und ::after. Das ist keine große Sache, aber wie können sie andere Elemente beeinflussen? Zumindest kann der Zählerwert die Breite des Pseudo-Elements ändern. Unterschiedliche Zahlen haben unterschiedliche Breiten. Das Zeichen 1 ist typischerweise dünner als 0, aber das ist schwer zu kontrollieren. Wenn sich die Anzahl der Zeichen ändert und nicht das Zeichen selbst, ist die resultierende Breitenänderung besser steuerbar. Es ist nicht ungewöhnlich, römische Ziffern mit CSS-Zählern zu verwenden. Eins und zwei, in römischen Ziffern dargestellt, sind das gleiche Zeichen, einmal und zweimal, und ihre Breiten in Pixeln auch.
Meine Idee war, die Radio-Buttons eines Spielers (gelb) links und die Radio-Buttons des anderen Spielers (rot) rechts von ihrem gemeinsamen Elternelement zu befestigen. Anfangs liegen die roten Buttons über den gelben Buttons, dann bewirkt die Breitenänderung des Containers, dass die roten Buttons „verschwinden“ und die gelben Buttons zum Vorschein kommen. Ein ähnliches realweltliches Konzept ist das Schiebefenster mit zwei Scheiben, eine Scheibe ist fest (gelbe Buttons), die andere ist verschiebbar (rote Buttons) über der anderen. Der Unterschied ist, dass im Spiel nur die Hälfte des Fensters sichtbar ist.
Bisher so gut, aber ich war mit font-size (und den anderen font-Eigenschaften) immer noch nicht zufrieden, die indirekt die Breite steuern. Ich dachte, letter-spacing würde hier gut passen, da es die Größe nur in einer Dimension erhöht. Unerwarteterweise hat selbst ein Buchstabe einen Buchstabenabstand (der nach dem Buchstaben gerendert wird), und zwei Buchstaben rendern den Buchstabenabstand zweimal. Vorhersehbare Breiten sind entscheidend, um dies zuverlässig zu machen. Nullbreiten-Zeichen zusammen mit einfachem und doppeltem Buchstabenabstand würden funktionieren, aber es ist gefährlich, die font-size auf null zu setzen. Die Definition eines großen letter-spacing (in Pixeln) und einer winzigen (1px) font-size machte es über alle Browser hinweg fast konsistent, ja, ich spreche von Subpixeln.
Ich brauchte die Containerbreite, um zwischen der Anfangsgröße (=w) und mindestens der doppelten Anfangsgröße (>=2w) zu wechseln, um die gelben Buttons vollständig ausblenden und anzeigen zu können. Nehmen wir an, v ist die gerenderte Breite des Zeichens ‚i‘ (niedrigere römische Darstellung, variiert je nach Browser) und c ist die gerenderte Breite (konstant) des letter-spacing. Ich brauchte v + c = w, aber das konnte nicht sein, weil c und w ganze Zahlen sind, v aber keine ganze Zahl ist. Ich habe min-width und max-width Eigenschaften verwendet, um die möglichen Breitenwerte einzuschränken, daher habe ich auch die möglichen Zählerwerte auf ‚i‘ und ‚iii‘ geändert, um sicherzustellen, dass die Textbreiten die Einschränkungen unter- und überschreiten. In Gleichungen sah das so aus: v + c < w, 3v + 3c > 2w und v << c, was 2/3w < c < w ergibt. Die Schlussfolgerung ist, dass der letter-spacing etwas kleiner als die Anfangsbreite sein muss.
Ich habe bisher so argumentiert, als ob das Pseudo-Element, das den Zählerwert anzeigt, das Elternelement der Radio-Buttons wäre, was aber nicht der Fall ist. Ich habe jedoch bemerkt, dass die Breite des Pseudo-Elements die Breite seines Elternelements verändert und in diesem Fall ist das Elternelement der Container der Radio-Buttons.
Wenn Sie denken, könnte das nicht mit arabischen Ziffern gelöst werden? Sie haben Recht, das Wechseln des Zählerwerts zwischen etwas wie ‚1‘ und ‚111‘ würde auch funktionieren. Nichtsdestotrotz gab mir die Idee der römischen Ziffern überhaupt erst die Anregung und sie waren auch eine gute Entschuldigung für den clickbaityen Titel, also habe ich sie beibehalten.

Die Anwendung der beschriebenen Technik verdoppelt die Containerbreite der Radio-Inputs, wenn ein roter Input angekreuzt ist, und stellt die ursprüngliche Breite wieder her, wenn ein gelber Input angekreuzt ist. Im Container der ursprünglichen Breite liegen die roten Inputs über den gelben, aber im Container der doppelten Breite sind die roten Inputs verschoben.
Mustererkennung
Im echten Leben sagt Ihnen das Connect 4-Spielfeld nicht, ob Sie gewonnen oder verloren haben, aber die Bereitstellung von richtigem Feedback ist Teil einer guten Benutzererfahrung in jeder Software. Das nächste Ziel ist es, zu erkennen, ob ein Spieler das Spiel gewonnen hat. Um das Spiel zu gewinnen, muss ein Spieler vier seiner Scheiben in einer Spalte, Reihe oder diagonalen Linie haben. Dies ist in vielen Programmiersprachen eine sehr einfache Aufgabe, aber in der reinen CSS-Welt ist dies eine gewaltige Herausforderung. Das Zerlegen in Unteraufgaben ist der Weg, um dies systematisch anzugehen.
Ich habe einen Flex-Container als Elternteil der Radio-Buttons und Scheiben verwendet. Ein gelber Radio-Button, ein roter Radio-Button und ein Div für die Scheibe gehören zu einem Feld. Ein solches Feld wird 42 Mal wiederholt und in Spalten angeordnet, die umbrechen. Folglich sind die Felder in einer Spalte nebeneinander, was die Erkennung von vier in einer Spalte mit dem benachbarten Selektor zum einfachsten Teil macht
<div class="grid">
<input type="radio" name="slot11">
<input type="radio" name="slot11">
<div class="disc"></div>
<input type="radio" name="slot12">
<input type="radio" name="slot12">
<div class="disc"></div>
...
<input type="radio" name="slot16">
<input type="radio" name="slot16">
<div class="disc"></div>
<input type="radio" name="slot21">
<input type="radio" name="slot21">
<div class="disc"></div>
...
</div>
/* Red four in a column selector */
input:checked + .disc + input + input:checked + .disc + input + input:checked + .disc + input + input:checked ~ .outcome
/* Yellow four in a column selector */
input:checked + input + .disc + input:checked + input + .disc + input:checked + input + .disc + input:checked ~ .outcome
Dies ist eine einfache, aber hässliche Lösung. Es gibt 11 Typ- und Klassenselektoren, die pro Spieler aneinandergereiht sind, um den Fall von vier in einer Spalte abzudecken. Das Hinzufügen eines div mit der Klasse .outcome nach den Elementen der Felder ermöglicht die bedingte Anzeige der Ergebnisnachricht. Es gibt auch ein Problem mit der fälschlichen Erkennung von vier in einer Spalte, wo die Spalte umgebrochen ist, aber lassen wir dieses Problem beiseite.
Ein ähnlicher Ansatz zur Erkennung von vier in einer Reihe wäre eine wirklich schreckliche Idee. Es gäbe 56 Selektoren, die pro Spieler aneinandergereiht sind (wenn ich richtig gerechnet habe), ganz zu schweigen davon, dass sie einen ähnlichen Fehler der falschen Erkennung hätten. Dies ist eine Situation, in der die :nth-child(An+B [of S]) oder die Spalten-Kombinatoren in Zukunft nützlich sein werden.
Für eine bessere Semantik könnte man für jede Spalte ein neues div hinzufügen und die Feldelemente darin anordnen. Diese Modifikation würde auch die Möglichkeit der oben genannten falschen Erkennung beseitigen. Dann könnte die Erkennung von vier in einer Reihe so aussehen: Wähle eine Spalte, in der der erste rote Radio-Input angekreuzt ist, und wähle die benachbarte Spalte, in der der erste rote Radio-Input angekreuzt ist, und so weiter noch zweimal. Das klingt sehr umständlich und würde den „Eltern“-Selektor erfordern.
Die Auswahl des Elternteils ist nicht machbar, aber die Auswahl des Kindes ist es. Wie würde die Erkennung von vier in einer Reihe mit verfügbaren Kombinatoren und Selektoren ablaufen? Wähle eine Spalte, wähle dann ihren ersten roten Radio-Input, wenn er angekreuzt ist, und wähle die benachbarte Spalte, dann wähle ihren ersten roten Radio-Input, wenn er angekreuzt ist, und so weiter noch zweimal. Das klingt immer noch umständlich, ist aber möglich. Der Trick liegt nicht nur im CSS, sondern auch im HTML, die nächste Spalte muss das Geschwister der Radio-Buttons in der vorherigen Spalte sein, was eine verschachtelte Struktur erzeugt.
<div class="grid column">
<input type="radio" name="slot11">
<input type="radio" name="slot11">
<div class="disc"></div>
...
<input type="radio" name="slot16">
<input type="radio" name="slot16">
<div class="disc"></div>
<div class="column">
<input type="radio" name="slot21">
<input type="radio" name="slot21">
<div class="disc"></div>
...
<input type="radio" name="slot26">
<input type="radio" name="slot26">
<div class="disc"></div>
<div class="column">
...
</div>
</div>
</div>
/* Red four in a row selectors */
input:nth-of-type(2):checked ~ .column > input:nth-of-type(2):checked ~ .column > input:nth-of-type(2):checked ~ .column > input:nth-of-type(2):checked ~ .column::after,
input:nth-of-type(4):checked ~ .column > input:nth-of-type(4):checked ~ .column > input:nth-of-type(4):checked ~ .column > input:nth-of-type(4):checked ~ .column::after,
...
input:nth-of-type(12):checked ~ .column > input:nth-of-type(12):checked ~ .column > input:nth-of-type(12):checked ~ .column > input:nth-of-type(12):checked ~ .column::after
Nun, die Semantik ist durcheinander und diese Selektoren sind nur für den roten Spieler (eine weitere Runde geht für den gelben Spieler), andererseits funktioniert es. Ein kleiner Vorteil ist, dass es keine falsch erkannten Spalten oder Reihen geben wird. Der Anzeigemechanismus des Ergebnisses musste ebenfalls angepasst werden, die Verwendung des ::after-Pseudo-Elements einer passenden Spalte ist eine konsistente Lösung, wenn die richtige Formatierung angewendet wird. Infolgedessen muss nach dem letzten Feld eine gefälschte achte Spalte hinzugefügt werden.
Wie im obigen Codeausschnitt zu sehen ist, werden bestimmte Positionen innerhalb einer Spalte abgeglichen, um vier in einer Reihe zu erkennen. Die gleiche Technik kann zur Erkennung von vier in einer Diagonalen verwendet werden, indem diese Positionen angepasst werden. Beachten Sie, dass die Diagonalen in zwei Richtungen verlaufen können.
input:nth-of-type(2):checked ~ .column > input:nth-of-type(4):checked ~ .column > input:nth-of-type(6):checked ~ .column > input:nth-of-type(8):checked ~ .column::after,
input:nth-of-type(4):checked ~ .column > input:nth-of-type(6):checked ~ .column > input:nth-of-type(8):checked ~ .column > input:nth-of-type(10):checked ~ .column::after,
...
input:nth-of-type(12):checked ~ .column > input:nth-of-type(10):checked ~ .column > input:nth-of-type(8):checked ~ .column > input:nth-of-type(6):checked ~ .column::after
Die Anzahl der Selektoren hat im Endeffekt stark zugenommen, und dies ist definitiv ein Bereich, in dem CSS-Präprozessoren die Länge der Deklaration reduzieren könnten. Trotzdem denke ich, dass die Demo moderat kurz ist. Sie sollte irgendwo in der Mitte auf der Skala liegen, von der Festlegung eines Selektors für jedes mögliche Gewinnmuster bis zur Verwendung von 4 magischen Selektoren (Spalte, Reihe, zwei Diagonalen).

Schwachstellen schließen
Jede Software hat Randfälle, und diese müssen behandelt werden. Die möglichen Ergebnisse eines Connect 4-Spiels sind nicht nur der Gewinn des roten oder gelben Spielers, sondern auch, dass kein Spieler gewinnt und das Spielfeld gefüllt wird, bekannt als Unentschieden. Technisch gesehen bricht dieser Fall das Spiel nicht oder produziert keine Fehler, was fehlt, ist das Feedback an die Spieler.
Das Ziel ist es zu erkennen, wenn 42 :checked Radio-Buttons auf dem Spielfeld sind. Das bedeutet auch, dass keiner von ihnen im :indeterminate-Zustand ist. Das erfordert eine Auswahl für jede Radio-Gruppe. Radio-Buttons sind ungültig, wenn sie :indeterminate sind, andernfalls sind sie gültig. Also habe ich das required-Attribut für jeden Input hinzugefügt und dann die :valid-Pseudo-Klasse auf dem Formular verwendet, um ein Unentschieden zu erkennen.

Die Abdeckung des Unentschieden-Ergebnisses führte einen Fehler ein. In dem sehr seltenen Fall, dass der gelbe Spieler im letzten Zug gewinnt, werden sowohl die Gewinn- als auch die Unentschieden-Nachrichten angezeigt. Das liegt daran, dass die Erkennungs- und Anzeigemethode dieser Ergebnisse orthogonal ist. Ich habe das Problem umgangen, indem ich sichergestellt habe, dass die Gewinnnachricht einen weißen Hintergrund hat und über der Unentschieden-Nachricht liegt. Ich musste auch den Fade-In-Übergang der Unentschieden-Nachricht verzögern, damit sie sich nicht mit dem Übergang der Gewinnnachricht vermischt.

Obwohl viele Radio-Buttons durch absolute Positionierung hintereinander versteckt sind, sind alle, die sich im indeterminate Zustand befinden, immer noch durch Tabbing durch die Steuerelemente zugänglich. Dies ermöglicht es den Spielern, ihre Scheiben in beliebige Felder fallen zu lassen. Eine Möglichkeit, dies zu handhaben, ist, die Tastaturinteraktionen einfach durch das tabindex-Attribut zu verbieten: das Setzen auf -1 bedeutet, dass es nicht über die sequentielle Tastaturnavigation erreichbar sein sollte. Ich musste jeden Radio-Input mit diesem Attribut erweitern, um diese Lücke zu schließen.
<input type="radio" name="slot11" tabindex="-1" required>
<input type="radio" name="slot11" tabindex="-1" required>
<div class="disc"></div>
...
Einschränkungen
Der größte Nachteil ist, dass das Spielfeld nicht responsiv ist und auf kleinen Ansichtsfenstern aufgrund der unzuverlässigen Lösung zur Nachverfolgung von Zügen fehlerhaft sein kann. Ich wagte es nicht, das Risiko einer Refaktorierung zu einer responsiven Lösung einzugehen, da die Implementierung aufgrund ihrer Natur mit festen Dimensionen sicherer erscheint.
Ein weiteres Problem ist der Sticky-Hover auf Touch-Geräten. Das Hinzufügen einiger Interaktions-Media-Queries an den richtigen Stellen ist die einfachste Möglichkeit, dies zu beheben, obwohl dies die Freifall-Animation eliminieren würde.
Man könnte meinen, dass die Pseudo-Klasse :indeterminate bereits weit verbreitet unterstützt wird, und das stimmt auch. Das Problem ist, dass sie in einigen Browsern nur teilweise unterstützt wird. Beachten Sie Note 1 in der Kompatibilitätstabelle: MS IE und Edge unterstützen sie nicht für Radio-Buttons. Wenn Sie die Demo in diesen Browsern betrachten, wird Ihr Cursor auf dem Spielfeld zum not-allowed-Cursor, dies ist eine unbeabsichtigte, aber einigermaßen anmutige Degradation.

Fazit
Danke, dass Sie bis zum letzten Abschnitt durchgehalten haben! Lassen Sie uns einige Zahlen betrachten
- 140 HTML-Elemente
- 350 (angemessene) Zeilen CSS
- 0 JavaScript
- 0 externe Ressourcen
Insgesamt bin ich mit dem Ergebnis zufrieden und das Feedback war großartig. Ich habe auf jeden Fall viel gelernt, als ich diese Demo erstellt habe, und ich hoffe, ich konnte beim Schreiben dieses Artikels viel weitergeben!
Ich habe alles durchgelesen. Sieht nach ehrlicher Selbstfolter aus, aber ich lobe Sie dafür, dass Sie es getan haben. Es ist großartig geworden und Sie mussten viele Probleme lösen, an die ich nicht gedacht hätte.
Ich ziehe wirklich meinen Hut vor Ihnen. Tolle Arbeit! Zugegebenermaßen hätte ich ein solches Projekt nicht unternommen, aber Sie haben es getan und Erfolg gehabt. Was kommt als Nächstes?
Wirklich beeindruckende Arbeit.
Danke, ich lerne viel, indem ich studiere, wie Sie das machen!
Schön! Ich habe zweimal hingesehen, als der Titel dieses Artikels in meinem RSS-Feed auftauchte. Hatte keine Ahnung, dass das möglich ist
Gehirn geblasen!
Danke für den tollen Beitrag!
Der „römische Ziffern“-Hack ist ziemlich interessant, und Ihre Erklärungen sind großartig!
Es gibt nur eine Sache, die ich nicht verstehen konnte: Warum verwenden Sie nicht :invalid anstelle von :indeterminate? Würde das nicht das Problem der „eingeschränkten :indeterminate-Unterstützung“ lösen?
Wow, absolut richtiger Punkt! Er kam mir nie in den Sinn, aber Sie haben Recht, das wird viel besser unterstützt und würde auch unter IE und Edge funktionieren.
Sehr schöner Beitrag und was für eine kreative Lösung!
Das ist großartig, sehr beeindruckend!
Das ist wirklich großartig! :D