Erweiterte Formulargestaltung
In diesem Artikel werden wir sehen, was mit CSS getan werden kann, um die Arten von Formularelementen zu stylen, die schwieriger zu stylen sind – die Kategorien der "schlechten" und "hässlichen" Elemente. Wie wir im vorherigen Artikel gesehen haben, sind Textfelder und Buttons sehr einfach zu stylen; nun werden wir uns dem Styling der problematischeren Teile widmen.
| Voraussetzungen: | Grundlegendes Verständnis von HTML und CSS. | 
|---|---|
| Ziel: | Verstehen, welche Teile von Formularen schwer zu stylen sind und warum; lernen, was getan werden kann, um sie anzupassen. | 
Um zu rekapitulieren, was wir im vorherigen Artikel gesagt haben, hier haben wir:
Die schlechten: Einige Elemente sind schwieriger zu stylen und erfordern komplexeres CSS oder einige spezifische Tricks:
- Kontrollkästchen und Optionsfelder
 <input type="search">
Die hässlichen: Einige Elemente können nicht umfassend mit CSS gestylt werden. Dazu gehören:
- Elemente, die an der Erstellung von Dropdown-Widgets beteiligt sind, einschließlich 
<select>,<option>,<optgroup>und<datalist>.Hinweis: Einige Browser unterstützen jetzt anpassbare Auswahl-Elemente, ein Satz von HTML- und CSS-Funktionen, die eine vollständige Anpassung von
<select>-Elementen und deren Inhalt wie bei regulären DOM-Elementen ermöglichen. <input type="color">- Datumsbezogene Steuerelemente wie 
<input type="datetime-local"> <input type="range"><input type="file"><progress>und<meter>
Beginnen wir zunächst mit der appearance Eigenschaft, die nützlich ist, um all die oben genannten Elemente besser stylbar zu machen.
appearance: Steuerung des OS-Level-Stylings
    Im vorherigen Artikel haben wir erwähnt, dass das Styling von Web-Formularelementen historisch weitgehend vom darunterliegenden Betriebssystem abgeleitet wurde, was ein Grund für die Schwierigkeit bei der Anpassung des Aussehens dieser Steuerelemente ist.
Die appearance-Eigenschaft wurde erstellt, um zu steuern, welches OS- oder System-Level-Styling auf Web-Formularelemente angewendet wird. Der mit Abstand hilfreichste Wert, und wahrscheinlich der einzige, den Sie verwenden werden, ist none. Dies verhindert, soweit möglich, dass ein Steuerelement, auf das Sie es anwenden, systemeigene Styles verwendet, und lässt Sie die Styles selbst mit CSS aufbauen.
Lassen Sie uns beispielsweise diese Kontrollen nehmen:
<form>
  <p>
    <label for="search">search: </label>
    <input id="search" name="search" type="search" />
  </p>
  <p>
    <label for="text">text: </label>
    <input id="text" name="text" type="text" />
  </p>
  <p>
    <label for="date">date: </label>
    <input id="date" name="date" type="datetime-local" />
  </p>
  <p>
    <label for="radio">radio: </label>
    <input id="radio" name="radio" type="radio" />
  </p>
  <p>
    <label for="checkbox">checkbox: </label>
    <input id="checkbox" name="checkbox" type="checkbox" />
  </p>
  <p><input type="submit" value="submit" /></p>
  <p><input type="button" value="button" /></p>
</form>
Durch Anwenden des folgenden CSS wird das systemeigene Styling entfernt.
input {
  appearance: none;
}
Das folgende Live-Beispiel zeigt Ihnen, wie sie in Ihrem System aussehen – standardmäßig auf der linken Seite und mit dem obigen CSS angewendet auf der rechten Seite.
In den meisten Fällen ist der Effekt, den gestalteten Rahmen zu entfernen, was das CSS-Styling ein wenig einfacher macht, aber nicht essenziell ist. In einigen Fällen, wie bei Optionsfeldern und Kontrollkästchen, wird es wesentlich nützlicher. Darauf werden wir jetzt näher eingehen.
Suchboxen und appearance
    Der appearance: none;-Wert war früher besonders nützlich, um <input type="search">-Elemente konsistent zu stylen. Ohne ihn erlaubte Safari es nicht, height- oder font-size-Werte für sie zu setzen. Dies ist jedoch in Safari 16 und höher nicht mehr der Fall. Sie möchten möglicherweise immer noch input[type="search"] explizit mit appearance: none; ansprechen, wenn Ihre Browser-Support-Matrix Safari-Versionen älter als 16 umfasst.
Bei Suchfeldern verschwindet der "x"-Löschen-Button, der erscheint, wenn der Wert nicht null ist, in Edge und Chrome, wenn das Eingabefeld den Fokus verliert, bleibt jedoch in Safari bestehen. Um ihn per CSS zu entfernen, können Sie die folgende Regel verwenden:
input[type="search"]:not(:focus, :active)::-webkit-search-cancel-button {
  display: none;
}
Styling von Kontrollkästchen und Optionsfeldern mit appearance
    Das Styling eines Kontrollkästchens oder eines Optionsfelds ist standardmäßig schwierig. Die Größen der Standardstile von Kontrollkästchen und Optionsfeldern sind nicht zur Änderung vorgesehen, und Browser reagieren sehr unterschiedlich, wenn Sie es versuchen. Einige vergrößern die Größe des Steuerelements, und einige halten das Steuerelement in derselben Größe und fügen zusätzlichen Platz darum herum hinzu.
Ein viel besserer Ansatz ist, das Standarderscheinungsbild von Kontrollkästchen und Optionsfeldern vollständig mit appearance: none; zu entfernen und dann Ihre eigenen Styles für deren verschiedene Zustände hinzuzufügen.
Nehmen wir dieses Beispiel-HTML:
<form>
  <fieldset>
    <legend>Fruit preferences</legend>
    <p>
      <label>
        <input type="checkbox" name="fruit" value="cherry" />
        I like cherry
      </label>
    </p>
    <p>
      <label>
        <input type="checkbox" name="fruit" value="banana" disabled />
        I can't like banana
      </label>
    </p>
    <p>
      <label>
        <input type="checkbox" name="fruit" value="strawberry" />
        I like strawberry
      </label>
    </p>
  </fieldset>
</form>
Lassen Sie uns diese mit einem benutzerdefinierten Kontrollkästchen-Design stylen. Wir beginnen damit, die ursprünglichen Kontrollkästchen-Stile zu entfernen:
input[type="checkbox"] {
  appearance: none;
}
Dann können wir die :checked- und :disabled-Pseudoklassen verwenden, um das Erscheinungsbild unserer benutzerdefinierten Kontrollkästchen zu ändern, wenn sich deren Zustand ändert:
input[type="checkbox"] {
  position: relative;
  width: 1em;
  height: 1em;
  border: 1px solid gray;
  /* Adjusts the position of the checkboxes on the text baseline */
  vertical-align: -2px;
  /* Set here so that Windows' High-Contrast Mode can override */
  color: green;
}
input[type="checkbox"]::before {
  content: "✔";
  position: absolute;
  font-size: 1.2em;
  right: -1px;
  top: -0.3em;
  visibility: hidden;
}
input[type="checkbox"]:checked::before {
  /* Use `visibility` instead of `display` to avoid recalculating layout */
  visibility: visible;
}
input[type="checkbox"]:disabled {
  border-color: black;
  background: #dddddd;
  color: gray;
}
Sie erfahren mehr über solche Pseudoklassen und mehr im nächsten Artikel; die obigen tun folgendes:
:checked— das Kontrollkästchen (oder Optionsfeld) befindet sich in einem ausgewählten Zustand — der Benutzer hat darauf geklickt/aktiviert.:disabled— das Kontrollkästchen (oder Optionsfeld) befindet sich in einem deaktivierten Zustand — es kann nicht interagiert werden.
Sie können das Live-Ergebnis sehen:
Wir haben auch ein paar andere Beispiele erstellt, um Ihnen mehr Ideen zu geben:
- Gestaltete Optionsfelder: Benutzerdefiniertes Styling von Optionsfeldern.
 - Umschalter-Beispiel: Ein als Umschalter gestaltetes Kontrollkästchen.
 
Was kann man über die "hässlichen" Elemente tun?
Wenden wir uns nun den "hässlichen" Steuerelementen zu — denjenigen, die wirklich schwer umfassend zu stylen sind. Kurz gesagt, dies sind Dropdown-Boxen, komplexe Steuerelementtypen wie color und datetime-local, und rückmeldeorientierte Steuerelemente wie <progress> und <meter>.
Das Problem ist, dass diese Elemente sehr unterschiedliche Standardaussehen in den Browsern haben, und während Sie sie in gewisser Weise stylen können, sind einige Teile ihrer Interna nicht zu stylen.
Wenn Sie bereit sind, einige Unterschiede im Aussehen und Verhalten zu akzeptieren, können Sie einige einfache Stile verwenden, um die Dinge erheblich zu verbessern. Dazu gehört die konsistente Größenveränderung und das Styling von Eigenschaften wie background-color und die Verwendung von appearance, um einige systemeigene Stile zu entfernen.
Nehmen Sie das folgende Beispiel, das eine Reihe der "hässlichen" Formularfunktionen in Aktion zeigt:
Sie können auch die Play-Taste drücken, um das Beispiel im MDN Playground auszuführen und den Quellcode zu bearbeiten.
Dieses Beispiel hat das folgende CSS zugewiesen bekommen:
body {
  font-family: "Josefin Sans", sans-serif;
  margin: 20px auto;
  max-width: 400px;
}
form > div {
  margin-bottom: 20px;
}
select {
  appearance: none;
  width: 100%;
  height: 100%;
}
.select-wrapper {
  position: relative;
}
.select-wrapper::after {
  content: "▼";
  font-size: 1rem;
  top: 3px;
  right: 10px;
  position: absolute;
}
button,
label,
input,
select,
progress,
meter {
  display: block;
  font-family: inherit;
  font-size: 100%;
  margin: 0;
  box-sizing: border-box;
  width: 100%;
  padding: 5px;
  height: 30px;
}
input[type="text"],
input[type="datetime-local"],
input[type="color"],
select {
  box-shadow: inset 1px 1px 3px #cccccc;
  border-radius: 5px;
}
label {
  margin-bottom: 5px;
}
button {
  width: 60%;
  margin: 0 auto;
}
Wir haben ein wenig JavaScript auf die Seite hinzugefügt, das die von der Dateiauswahl ausgewählten Dateien unterhalb des Steuerelements auflistet. Dies ist eine vereinfachte Version des Beispiels, das auf der <input type="file"> Referenzseite zu finden ist:
const fileInput = document.querySelector("#file");
const fileList = document.querySelector("#file-list");
fileInput.addEventListener("change", updateFileList);
function updateFileList() {
  while (fileList.firstChild) {
    fileList.removeChild(fileList.firstChild);
  }
  const curFiles = fileInput.files;
  if (!(curFiles.length === 0)) {
    for (const file of curFiles) {
      const listItem = document.createElement("li");
      listItem.textContent = `File name: ${file.name}; file size: ${returnFileSize(file.size)}.`;
      fileList.appendChild(listItem);
    }
  }
}
function returnFileSize(number) {
  if (number < 1e3) {
    return `${number} bytes`;
  } else if (number >= 1e3 && number < 1e6) {
    return `${(number / 1e3).toFixed(1)} KB`;
  }
  return `${(number / 1e6).toFixed(1)} MB`;
}
"Globale" Stile
Im vorherigen Beispiel haben wir es ziemlich gut geschafft, unsere hässlichen Steuerelemente in modernen Browsern einheitlich aussehen zu lassen.
Wir haben einige globale Normalisierungs-CSS auf alle Steuerelemente und deren Beschriftungen angewendet, um sie auf die gleiche Weise zu dimensionieren, ihre Elternschrift zu übernehmen usw., wie im vorherigen Artikel erwähnt:
button,
label,
input,
select,
progress,
meter {
  display: block;
  font-family: inherit;
  font-size: 100%;
  margin: 0;
  box-sizing: border-box;
  width: 100%;
  padding: 5px;
  height: 30px;
}
Wir haben auch einige einheitliche Schatten und abgerundete Ecken zu den Steuerelementen hinzugefügt, wo es sinnvoll ist:
input[type="text"],
input[type="datetime-local"],
input[type="color"],
select {
  box-shadow: inset 1px 1px 3px #cccccc;
  border-radius: 5px;
}
Bei anderen Steuerelementen wie Bereichstypen, Fortschrittsbalken und Messgeräten fügen sie nur einen hässlichen Kasten um den Steuerbereich hinzu, sodass es keinen Sinn ergibt.
Lassen Sie uns über einige Besonderheiten jedes dieser Steuerelementtypen sprechen und dabei die Schwierigkeiten hervorheben.
Selects und Datalists
Einige Browser unterstützen jetzt anpassbare Auswahl-Elemente, ein Satz von HTML- und CSS-Funktionen, die eine vollständige Anpassung von <select>-Elementen und deren Inhalt wie bei regulären DOM-Elementen ermöglichen. In unterstützten Browsern und Codebasen müssen Sie sich nicht mehr um die unten beschriebenen Legacy-Techniken für <select>-Elemente kümmern.
Das Styling von Datalists und Selects (in Browsern, die keine anpassbaren Selects unterstützen) ermöglicht ein akzeptables Maß an Anpassung, vorausgesetzt, Sie möchten das Aussehen und Verhalten nicht allzu sehr von den Standards abweichen. Wir haben es geschafft, die Boxen ziemlich einheitlich und konsistent aussehen zu lassen. Das datalist-auslösende Steuerungselement ist ohnehin ein <input type="text">, daher wussten wir, dass dies kein Problem darstellen würde.
Zwei Dinge sind etwas problematischer. Erstens, das Pfeilsymbol des Selects, das anzeigt, dass es sich um ein Dropdown handelt, unterscheidet sich zwischen den Browsern. Es neigt auch dazu, sich zu ändern, wenn Sie die Größe der Select-Box vergrößern oder sie auf unschöne Weise neu dimensionieren. Um dies in unserem Beispiel zu beheben, verwendeten wir zunächst unseren alten Freund appearance: none, um das Symbol vollständig zu entfernen:
select {
  appearance: none;
}
Dann erstellten wir unser eigenes Symbol mit generiertem Inhalt. Wir setzten eine zusätzliche Umhüllung um das Steuerelement, da ::before/::after nicht auf <select>-Elementen funktionieren (ihr Inhalt wird vollständig vom Browser kontrolliert):
<label for="select">Select a fruit</label>
<div class="select-wrapper">
  <select id="select" name="select">
    <option>Banana</option>
    <option>Cherry</option>
    <option>Lemon</option>
  </select>
</div>
Wir verwenden dann generierten Inhalt, um einen kleinen Abwärtspfeil zu erzeugen, und setzen ihn mithilfe der Positionierung an die richtige Stelle:
.select-wrapper {
  position: relative;
}
.select-wrapper::after {
  content: "▼";
  font-size: 1rem;
  top: 6px;
  right: 10px;
  position: absolute;
}
Das zweite, etwas wichtigere Problem ist, dass Sie keine Kontrolle über das Feld haben, das erscheint, wenn Sie auf die <select>-Box klicken, um es zu öffnen. Sie können die Schriftart des übergeordneten Elements übernehmen, aber Sie können keine Dinge wie Abstände und Farben festlegen. Dasselbe gilt für die Autovervollständigungsliste, die mit <datalist> erscheint.
Wenn Sie wirklich die vollständige Kontrolle über das Styling der Optionen benötigen, müssen Sie entweder eine Bibliothek verwenden, um ein benutzerdefiniertes Steuerelement zu erstellen, oder Ihr eigenes erstellen. Im Fall von <select> könnten Sie auch das multiple-Attribut verwenden, das alle Optionen auf der Seite anzeigt und dieses spezielle Problem umgeht:
<label for="select">Select fruits</label>
<select id="select" name="select" multiple>
  …
</select>
Natürlich könnte dies auch nicht zum von Ihnen angestrebten Design passen, aber es lohnt sich, dies zu beachten!
Datums-Eingabetypen
Die Eingabetypen für Datum/Uhrzeit (datetime-local, time, week, month) haben alle dasselbe große damit verbundene Problem. Das eigentliche umschließende Feld ist so einfach zu stylen wie jedes Texteingabefeld, und was wir in diesem Demo haben, sieht gut aus.
Allerdings sind die internen Teile des Steuerelements (z. B. der Popup-Kalender, den Sie verwenden, um ein Datum auszuwählen, die Spinnfunktion, die Sie verwenden können, um Werte zu erhöhen/verringern) überhaupt nicht stylbar, und Sie können sie nicht mit appearance: none; entfernen. Wenn Sie wirklich die vollständige Kontrolle über das Styling benötigen, müssen Sie entweder eine Bibliothek verwenden, um ein benutzerdefiniertes Steuerungselement zu erstellen oder Ihr eigenes erstellen.
Hinweis:
Es ist erwähnenswert, dass hier auch <input type="number"> angegeben wird — dieser hat ebenfalls eine Spinnfunktion, die Sie verwenden können, um Werte zu erhöhen oder zu verringern, und leidet möglicherweise unter demselben Problem. Im Fall des number-Typs sind die gesammelten Daten jedoch einfacher, und es ist einfach, stattdessen einen tel-Eingabetyp zu verwenden, der das Aussehen von text hat, aber die numerische Tastatur auf Geräten mit Touch-Tastaturen anzeigt.
Bereichs-Eingabetypen
<input type="range"> ist ärgerlich zu stylen. Sie können etwas wie das Folgende verwenden, um den Standardslider-Track vollständig zu entfernen und ihn durch einen benutzerdefinierten Stil zu ersetzen (einen dünnen roten Track in diesem Fall):
input[type="range"] {
  appearance: none;
  background: red;
  height: 2px;
  padding: 0;
  outline: 1px solid transparent;
}
Es ist jedoch sehr schwierig, den Stil des Ziehgriffs des Bereichs-Steuerelements anzupassen — um die volle Kontrolle über das Styling des Bereichs zu erhalten, müssen Sie einige komplexe CSS-Codes verwenden, einschließlich mehrerer nicht-standardmäßiger, browserspezifischer Pseudo-Elemente. Schauen Sie sich Styling Cross-Browser Compatible Range Inputs with CSS auf CSS-Tricks für einen detaillierten Bericht darüber an, was erforderlich ist.
Farbeingabetypen
Eingabesteuerungstypen für Farbe sind nicht allzu schlecht. In unterstützten Browsern neigen sie dazu, Ihnen einen Block in Vollfarbe mit einem kleinen Rand zu geben.
Sie können den Rand entfernen und einfach den Farbblock übriglassen, indem Sie so etwas verwenden:
input[type="color"] {
  border: 0;
  padding: 0;
}
Eine kundenspezifische Lösung ist jedoch die einzige Möglichkeit, etwas wesentlich anderes zu erreichen.
Dateieingabetypen
Eingaben vom Typ Datei sind im Allgemeinen in Ordnung — wie Sie in unserem Beispiel gesehen haben, ist es ziemlich einfach, etwas zu erstellen, das gut mit dem Rest der Seite harmoniert — die Ausgabereihe, die Teil des Steuerelements ist, übernimmt die Schriftart des Elternteils, wenn Sie das Eingabefeld dazu anweisen, und Sie können die benutzerdefinierte Liste von Dateinamen und Größen in jeder gewünschten Weise gestalten; wir haben sie schließlich erstellt.
Das einzige Problem bei Dateiauswahlen ist, dass der Knopf, den Sie drücken, um die Dateiauswahl zu öffnen, nicht stylbar ist — er kann nicht in der Größe oder Farbe geändert werden, und er akzeptiert nicht einmal eine andere Schriftart.
Ein Weg, dieses Problem zu umgehen, besteht darin, die Tatsache zu nutzen, dass, wenn Sie ein Label mit einem Formularelement verbunden haben, das Klicken auf das Label das Steuerelement aktiviert. Sie könnten also das eigentliche Formularelement mit so etwas verstecken:
input[type="file"] {
  height: 0;
  padding: 0;
  opacity: 0;
}
Und dann das Label so stylen, dass es wie ein Button funktioniert, der, wenn er gedrückt wird, die Dateiauswahl wie erwartet öffnet:
label[for="file"] {
  box-shadow: 1px 1px 3px #cccccc;
  background: linear-gradient(to bottom, #eeeeee, #cccccc);
  border: 1px solid darkgrey;
  border-radius: 5px;
  text-align: center;
  line-height: 1.5;
}
label[for="file"]:hover {
  background: linear-gradient(to bottom, white, #dddddd);
}
label[for="file"]:active {
  box-shadow: inset 1px 1px 3px #cccccc;
}
Das Ergebnis des obigen CSS-Stylings können Sie im folgenden Live-Beispiel sehen.
Sie können auch die Play-Taste drücken, um das Beispiel im MDN Playground auszuführen und den Quellcode zu bearbeiten.
Messgeräte und Fortschrittsbalken
<meter> und <progress> sind möglicherweise die schlimmsten von allen. Wie Sie im vorherigen Beispiel gesehen haben, können wir sie relativ genau auf die gewünschte Breite einstellen. Aber darüber hinaus sind sie wirklich schwer zu stylen. Sie verarbeiten Höheneinstellungen nicht konsistent, weder untereinander noch zwischen Browsern, Sie können den Hintergrund, aber nicht die Vordergrundleiste färben, und das Setzen von appearance: none auf ihnen macht die Dinge schlechter, nicht besser.
Es ist einfacher, Ihre eigene benutzerdefinierte Lösung für diese Funktionen zu erstellen, wenn Sie das Styling kontrollieren möchten, oder eine Drittanbieterlösung wie progressbar.js zu verwenden.
Zusammenfassung
Obwohl es immer noch Schwierigkeiten bei der Verwendung von CSS mit HTML-Formularen gibt, gibt es Möglichkeiten, viele der Probleme zu umgehen. Es gibt keine sauberen, universellen Lösungen, aber moderne Browser bieten neue Möglichkeiten. Für den Moment ist die beste Lösung, mehr über die Art und Weise zu erfahren, wie die verschiedenen Browser CSS unterstützen, wenn es auf HTML-Formularelemente angewendet wird.
Im nächsten Artikel dieses Moduls werden wir die Erstellung von vollständig angepassten <select>-Elementen mit den dedizierten, modernen HTML- und CSS-Funktionen erkunden, die zu diesem Zweck verfügbar sind.