Ich bin vor kurzem auf dieses Pen gestoßen und mein erster Gedanke war, dass alles mit nur drei Elementen gemacht werden könnte: einem Wrapper, einem Bereichs-input und einem output. Auf der CSS-Seite beinhaltet dies die Verwendung eines conic-gradient() mit einem Stopp, der auf eine CSS-Variable gesetzt ist.

Mitte 2015 stellte Lea Verou einen Polyfill für conic-gradient() auf einer Konferenz vor, auf der sie demonstrierte, wie diese zur Erstellung von Tortendiagrammen verwendet werden können. Dieser Polyfill ist großartig für den Einstieg in das Spielen mit conic-gradient(), da er uns ermöglicht, damit Dinge zu bauen, die allgemein funktionieren. Leider funktioniert er nicht mit CSS-Variablen, und CSS-Variablen sind heutzutage zu einer Schlüsselkomponente für effizientes Codieren geworden.
Die gute Nachricht ist, dass sich die Dinge in den letzten zweieinhalb Jahren ein wenig bewegt haben. Chrome und im Allgemeinen Browser, die Blink verwenden und Flags bereitstellen (wie z. B. Opera), unterstützen conic-gradient() jetzt nativ (hurra!), was bedeutet, dass es möglich geworden ist, mit CSS-Variablen als conic-gradient()-Stopps zu experimentieren. Alles, was wir tun müssen, ist das Flag Experimental Web Platform Features in chrome://flags (oder, wenn Sie Opera verwenden, in opera://flags) zu aktivieren.

Okay, jetzt können wir loslegen!
Die anfängliche Struktur
Wir beginnen mit einem Wrapper-Element und einem Bereichs-input.
<div class="wrap">
<input id="r" type="range"/>
</div>
Beachten Sie, dass wir kein output-Element haben. Das liegt daran, dass wir sowieso JavaScript benötigen, um den Wert des output-Elements zu aktualisieren, und wir wollen kein hässliches, nutzloses, nicht aktualisierendes Element sehen, falls JavaScript deaktiviert ist oder aus irgendeinem Grund fehlschlägt. Daher fügen wir dieses Element per JavaScript hinzu und weisen dem Wrapper je nach Unterstützung des aktuellen Browsers für conic-gradient() eine Klasse zu.
Wenn unser Browser conic-gradient() unterstützt, erhält der Wrapper die Klasse .full und wir stylen den output zu einem Diagramm. Andernfalls haben wir nur einen einfachen Schieberegler ohne Diagramm, wobei der output auf dem Schieberegler-Thumb liegt.

conic-gradient() unterstützen (oben) und der Fallback in Browsern, die es nicht unterstützen (unten).Grundlegende Stile
Bevor wir etwas anderes tun, möchten wir in allen Browsern einen schön aussehenden Schieberegler auf dem Bildschirm anzeigen.
Wir beginnen mit dem grundlegendsten Reset und setzen einen background auf das body.
$bg: #3d3d4a;
* { margin: 0 }
body { background: $bg }
Der zweite Schritt ist die Vorbereitung des Schiebereglers für das Styling in WebKit-Browsern, indem -webkit-appearance: none auf ihn und seinen Thumb gesetzt wird (da die Spur aus irgendeinem Grund bereits standardmäßig gesetzt ist) und wir stellen sicher, dass wir gleiche Bedingungen schaffen, indem wir explizit die Eigenschaften festlegen, die browserübergreifend inkonsistent sind, wie z. B. padding, background oder font.
[type='range'] {
&, &::-webkit-slider-thumb { -webkit-appearance: none }
display: block;
padding: 0;
background: transparent;
font: inherit
}
Wenn Sie eine Auffrischung benötigen, wie Schieberegler und ihre Komponenten in verschiedenen Browsern funktionieren, lesen Sie meinen detaillierten Artikel über das Verständnis des Bereichs-Inputs.
Nun können wir uns dem interessanteren Teil widmen. Wir legen die Abmessungen der Spur und des Thumbs fest und setzen diese auf den Schiebereglerkomponenten über die entsprechenden Mixins. Wir werden auch einige background-Werte einbeziehen, damit etwas auf dem Bildschirm sichtbar ist, sowie einen border-radius, um die Dinge zu verschönern. Für beide Komponenten setzen wir auch den border auf none, damit wir konsistente Ergebnisse erzielen.
$k: .1;
$track-w: 25em;
$track-h: .02*$track-w;
$thumb-d: $k*$track-w;
@mixin track() {
border: none;
width: $track-w; height: $track-h;
border-radius: .5*$track-h;
background: #343440
}
@mixin thumb() {
border: none;
width: $thumb-d; height: $thumb-d;
border-radius: 50%;
background: #e6323e
}
[type='range'] {
/* same styles as before */
width: $track-w; height: $thumb-d;
&::-webkit-slider-runnable-track { @include track }
&::-moz-range-track { @include track }
&::-ms-track { @include track }
&::-webkit-slider-thumb {
margin-top: .5*($track-h - $thumb-d);
@include thumb
}
&::-moz-range-thumb { @include thumb }
&::-ms-thumb {
margin-top: 0;
@include thumb
}
}
Wir fügen einige weitere Details hinzu, wie das Setzen eines margin, einer expliziten width und einer font auf dem Wrapper.
.wrap {
margin: 2em auto;
width: $track-w;
font: 2vmin trebuchet ms, arial, sans-serif
}
Wir wollen nicht, dass dies zu klein oder zu groß wird, daher begrenzen wir die font-size.
.wrap {
@media (max-width: 500px), (max-height: 500px) { font-size: 10px }
@media (min-width: 1600px), (min-height: 1600px) { font-size: 32px }
}
Und jetzt haben wir einen schönen, browserübergreifenden Schieberegler.
Siehe das Pen von thebabydino (@thebabydino) auf CodePen.
Das JavaScript
Wir beginnen damit, den Schieberegler und den Wrapper zu holen und das output-Element zu erstellen.
const _R = document.getElementById('r'),
_W = _R.parentNode,
_O = document.createElement('output');
Wir erstellen eine Variable val, in der wir den aktuellen Wert unseres Bereichs-input speichern.
let val = null;
Als nächstes haben wir eine update()-Funktion, die prüft, ob der aktuelle Wert des Schiebereglers gleich dem bereits gespeicherten Wert ist. Wenn das nicht der Fall ist, aktualisieren wir die JavaScript-Variable val, den Textinhalt des output und die CSS-Variable --val auf dem Wrapper.
function update() {
let newval = +_R.value;
if(val !== newval)
_W.style.setProperty('--val', _O.value = val = newval)
};
Bevor wir mit dem JavaScript weitermachen, setzen wir einen conic-gradient() im CSS auf den output.
output {
background: conic-gradient(#e64c65 calc(var(--val)*1%), #41a8ab 0%)
}
Wir setzen die Dinge in Bewegung, indem wir die update()-Funktion aufrufen, den output als Kind des Wrappers in den DOM einfügen und dann testen, ob das berechnete background-image des output der conic-gradient() entspricht, den wir gesetzt haben, oder nicht (beachten Sie, dass wir ihn hinzufügen müssen, bevor wir dies tun).
Wenn das berechnete background-image nicht "none" ist (wie es der Fall ist, wenn wir keine native conic-gradient()-Unterstützung haben), fügen wir dem Wrapper eine Klasse full hinzu. Wir verbinden den output auch über ein for-Attribut mit dem Bereichs-input.
Über Event-Listener stellen wir sicher, dass die update()-Funktion jedes Mal aufgerufen wird, wenn wir den Schieberegler-Thumb bewegen.
_O.setAttribute('for', _R.id);
update();
_W.appendChild(_O);
if(getComputedStyle(_O).backgroundImage !== 'none')
_W.classList.add('full');
_R.addEventListener('input', update, false);
_R.addEventListener('change', update, false);
Wir haben jetzt einen Schieberegler und einen output (der seinen Wert auf einem variablen conic-gradient()-Hintergrund anzeigt, wenn wir ihn in einem Browser mit nativer conic-gradient()-Unterstützung betrachten). Zu diesem Zeitpunkt immer noch hässlich, aber funktionsfähig - der output aktualisiert sich, wenn wir den Schieberegler ziehen.
Siehe das Pen von thebabydino (@thebabydino) auf CodePen.
Wir haben dem output auch einen hellen color-Wert gegeben, damit wir ihn besser sehen können, und über das Pseudo-Element ::after ein % hinzugefügt. Außerdem haben wir den Tooltip (::-ms-tooltip) in Edge ausgeblendet, indem wir dessen display auf none gesetzt haben.
Der Fall ohne Diagramm
Dies ist der Fall, wenn wir keine conic-gradient()-Unterstützung haben und daher kein Diagramm erstellen. Das gewünschte Ergebnis ist unten zu sehen.

Das Ergebnis verschönern
In diesem Fall positionieren wir das output-Element absolut, machen es so groß wie den Thumb und zentrieren seinen Text.
.wrap:not(.full) {
position: relative;
output {
position: absolute;
/* ensure it starts from the top */
top: 0;
/* set dimensions */
width: $thumb-d; height: $thumb-d
}
}
/* we'll be using this for the chart case too */
output {
/* place text in the middle */
display: flex;
align-items: center;
justify-content: center;
}
Wenn Sie eine Auffrischung benötigen, wie align-items und justify-content funktionieren, lesen Sie diesen umfassenden Artikel über CSS-Ausrichtung von Patrick Brosset.
Das Ergebnis ist im folgenden Pen zu sehen, wo wir auch einen outline gesetzt haben, um die Grenzen des output klar erkennen zu können.
Siehe das Pen von thebabydino (@thebabydino) auf CodePen.
Das sieht langsam nach etwas aus, aber unser output bewegt sich nicht mit dem Schieberegler-Thumb.
Den Output bewegen lassen
Um dieses Problem zu beheben, erinnern wir uns zunächst daran, wie die Bewegung eines Schieberegler-Thumbs funktioniert. In Chrome bewegt sich die border-box des Thumbs innerhalb der Grenzen der content-box der Spur, während er in Firefox und Edge die border-box des Thumbs innerhalb der Grenzen der content-box des eigentlichen Schiebereglers bewegt.
Obwohl diese Inkonsistenz in manchen Situationen Probleme verursachen kann, ist unser Anwendungsfall hier ein einfacher. Wir haben keine Ränder, Polsterungen oder Rahmen am Schieberegler oder an seinen Komponenten, sodass die drei Boxen (content-box, padding-box und border-box) sowohl mit dem Schieberegler selbst als auch mit seinen Spur- und Thumb-Komponenten übereinstimmen. Darüber hinaus stimmt die Breite der drei Boxen des eigentlichen input mit der Breite seiner Spur überein.
Das bedeutet, wenn der Schiebereglerwert auf seinem Minimum liegt (den wir nicht explizit gesetzt haben, sodass es der Standard 0 ist), stimmt die linke Kante der Boxen des Thumbs mit der linken Kante des input (und damit der Spur) überein.
Ebenso, wenn der Schiebereglerwert auf seinem Maximum liegt (wiederum nicht explizit gesetzt, sodass er den Standardwert 100 annimmt), stimmt die rechte Kante der Boxen des Thumbs mit der rechten Kante des input (und damit der Spur) überein. Dies platziert die linke Kante des Thumbs eine Thumb-width ($thumb-d) vor (links von) der rechten Kante des Schiebereglers (und der Spur).
Die folgende Abbildung zeigt dies relativ zur width des Inputs ($track-w) - dies wird als 1 dargestellt. Die width des Thumbs ($thumb-d) wird als Bruchteil k der width des Inputs dargestellt (da wir sie als $thumb-d: $k*$track-w gesetzt haben).
Von hier aus erhalten wir, dass sich die linke Kante des Thumbs zwischen Minimum und Maximum um die width des Inputs ($track-w) abzüglich der width des Thumbs (thumb-d) bewegt hat.
Um den output auf die gleiche Weise zu bewegen, verwenden wir eine Translation. In seiner Ausgangsposition befindet sich unser output an der äußersten linken Position des Thumbs, die er einnimmt, wenn der Schiebereglerwert am Minimum liegt, sodass die von uns verwendete transform-Funktion translate(0) ist. Um ihn in die Position zu bewegen, die der Thumb einnimmt, wenn der Schiebereglerwert am Maximum liegt, müssen wir ihn um $track-w - $thumb-d = $track-w*(1 - $k) übersetzen.
output (live).In Ordnung, aber was ist mit den Werten dazwischen?
Nun, denken Sie daran, dass jedes Mal, wenn der Schiebereglerwert aktualisiert wird, wir nicht nur den neuen Wert für den Textinhalt des output setzen, sondern auch für eine CSS-Variable --val auf dem Wrapper. Diese CSS-Variable bewegt sich zwischen 0 am linken Ende (wenn der Schiebereglerwert sein Minimum, in diesem Fall 0, hat) und 100 am anderen Ende (wenn der Schiebereglerwert sein Maximum, in diesem Fall 100, hat).
Wenn wir also unseren output entlang der horizontalen (x-) Achse um calc(var(--val)/100*#{$track-w - $thumb-d}) verschieben, bewegt er sich zusammen mit dem Thumb, ohne dass wir sonst etwas tun müssen.
Siehe das Pen von thebabydino (@thebabydino) auf CodePen.
Beachten Sie, wie das Obige funktioniert, wenn wir woanders auf die Spur klicken, aber *nicht, wenn wir versuchen, den Thumb zu ziehen*. Das liegt daran, dass der output nun auf dem Thumb liegt und unsere Klicks stattdessen abfängt.
Wir beheben dieses Problem, indem wir pointer-events: none auf den output setzen.
Siehe das Pen von thebabydino (@thebabydino) auf CodePen.
In der obigen Demo haben wir auch die hässliche outline auf dem output-Element entfernt, da wir sie nicht mehr benötigen.
Nachdem wir nun einen schönen Fallback für Browser haben, die conic-gradient() nicht nativ unterstützen, können wir uns dem Aufbau des gewünschten Ergebnisses für diejenigen widmen, die es tun (Chrome/Opera mit aktiviertem Flag).
Der Fall mit Diagramm
Das gewünschte Layout zeichnen
Bevor wir mit dem Schreiben von Code beginnen, müssen wir genau wissen, was wir erreichen wollen. Dazu erstellen wir eine Layout-Skizze mit Abmessungen relativ zur width der Spur ($track-w), die auch die width des input und die Kante der content-box des Wrappers ist (Wrapper-padding nicht enthalten).
Das bedeutet, dass die content-box unseres Wrappers ein Quadrat mit der Kantenlänge 1 ist (relativ zur width der Spur), der input ein Rechteck ist, das eine Kante entlang und gleich einer Kante des Wrappers hat, und die andere Kante ein Bruchteil k derselben Kante ist, während sein Thumb ein kxk-Quadrat ist.
Das Diagramm ist ein Quadrat mit der Kantenlänge 1 - 2·k, das an die Kante des Wrappers grenzt, die der des Schiebereglers gegenüberliegt, mit einem k-Abstand zum Schieberegler und in der Mitte entlang der anderen Richtung. Da die Kante des Wrappers 1 und die des Diagramms 1 - 2·k ist, ergeben sich auch in dieser Richtung k Abstände zwischen den Kanten des Wrappers und denen des Diagramms.
Unsere Elemente dimensionieren
Der erste Schritt, um dieses Layout zu erhalten, ist, den Wrapper quadratisch zu machen und die Abmessungen des output auf (1 - 2*$k)*100% zu setzen.
$k: .1;
$track-w: 25em;
$chart-d: (1 - 2*$k)*100%;
.wrap.full {
width: $track-w;
output {
width: $chart-d; height: $chart-d
}
}
Das Ergebnis ist unten zu sehen, wo wir auch einige Umrisse hinzugefügt haben, um die Dinge besser zu erkennen.

conic-gradient()-Unterstützung haben).Dies ist ein guter Anfang, da sich der output bereits an der exakten Stelle befindet, an der wir ihn haben wollen.
Den Schieberegler vertikal machen
Die "offizielle" Methode, dies für WebKit-Browser zu tun, besteht darin, -webkit-appearance: vertical auf den Bereichs-input zu setzen. Dies würde jedoch die benutzerdefinierten Stile brechen, da diese erfordern, dass -webkit-appearance auf none gesetzt ist, und wir können es nicht auf zwei verschiedene Werte gleichzeitig setzen.
Die einzige praktische Lösung, die wir haben, ist die Verwendung einer transform-Funktion. Wie es ist, haben wir das Minimum des Schiebereglers am linken Ende des Wrappers und das Maximum am rechten Ende. Was wir wollen, ist, das Minimum unten im Wrapper und das Maximum oben im Wrapper zu haben.
Dies klingt nach einer 90°-Drehung in negativer Richtung (da die Richtung im Uhrzeigersinn die positive ist) um die obere rechte Ecke (was uns einen transform-origin gibt, der horizontal bei 100% und vertikal bei 0% liegt).
Siehe das Pen von thebabydino (@thebabydino) auf CodePen.
Das ist ein guter Anfang, aber jetzt ist unser Schieberegler außerhalb der Wrapper-Grenze. Um zu entscheiden, was der nächste beste Schritt ist, um ihn an die gewünschte Position zu bringen, müssen wir verstehen, was diese Drehung bewirkt hat. Sie hat nicht nur das eigentliche input-Element gedreht, sondern auch sein lokales Koordinatensystem gedreht. Jetzt zeigt seine x-Achse nach oben und seine y-Achse nach rechts.
Um ihn nach innen zu bringen, entlang der rechten Kante des Wrappers, müssen wir ihn nach der Drehung um seine eigene height in negativer Richtung seiner *y*-Achse verschieben. Das bedeutet, dass die endgültige transform-Kette, die wir anwenden, rotate(-90deg) translatey(-100%) lautet. (Denken Sie daran, dass %-Werte, die in translate()-Funktionen verwendet werden, sich auf die Abmessungen des übersetzten Elements selbst beziehen.)
.wrap.full {
input {
transform-origin: 100% 0;
transform: rotate(-90deg) translatey(-100%)
}
}
Dies ergibt das gewünschte Layout.

conic-gradient()-Unterstützung haben).Das Diagramm stylen
Natürlich ist der erste Schritt, es mit border-radius rund zu machen und die Eigenschaften color, font-size und font-weight anzupassen.
.wrap.full {
output {
border-radius: 50%;
color: #7a7a7a;
font-size: 4.25em;
font-weight: 700
}
}
Sie haben vielleicht bemerkt, dass wir die Abmessungen des Diagramms als (1 - 2*$k)*100% und nicht als (1 - 2*$k)*$track-w gesetzt haben. Das liegt daran, dass $track-w ein em-Wert ist, was bedeutet, dass die berechnete Pixeläquivalenz von der font-size des Elements abhängt, das sie verwendet.
Wir wollten jedoch die font-size hier erhöhen können, ohne eine em-wertige Größe nach unten anpassen zu müssen. Das ist möglich und nicht so kompliziert, aber im Vergleich zum einfachen Setzen der Abmessungen als %-Werte, die nicht von der font-size abhängen, ist es immer noch etwas zusätzliche Arbeit.

conic-gradient()-Unterstützung haben).Vom Kuchen 🥧 zum Donut 🍩
Der einfachste Weg, dieses Loch in der Mitte zu emulieren, in dem sich der Text befindet, ist das Hinzufügen einer weiteren background-Schicht über der conic-gradient()-Schicht. Wir könnten wahrscheinlich einige Mischmodi verwenden, um den Trick zu machen, aber das ist nicht wirklich notwendig, es sei denn, wir haben einen Bild-background. Für einen soliden background wie hier reicht eine einfache Überlagerungsschicht aus.
$p: 39%;
background: radial-gradient($bg $p, transparent $p + .5% /* avoid ugly edge */),
conic-gradient(#e64c65 calc(var(--val)*1%), #41a8ab 0%);
Okay, das war's für das Diagramm selbst!

conic-gradient()-Unterstützung haben).Den Wert auf dem Thumb anzeigen
Dies machen wir mit einem absolut positionierten ::after-Pseudo-Element auf dem wrapper. Wir geben diesem Pseudo-Element die Abmessungen des Thumbs und positionieren es in der unteren rechten Ecke des Wrappers, genau dort, wo sich der Thumb befindet, wenn der Schiebereglerwert am Minimum liegt.
.wrap.full {
position: relative;
&::after {
position: absolute;
right: 0; bottom: 0;
width: $thumb-d; height: $thumb-d;
content: '';
}
}
Wir geben ihm auch einen Umriss, nur damit wir ihn sehen können.

conic-gradient()-Unterstützung haben).Die Bewegung entlang des Thumbs wird genau wie im Fall ohne Diagramm erreicht, nur dass die Translation diesmal entlang der y-Achse in negativer Richtung erfolgt (anstatt entlang der x-Achse in positiver Richtung).
transform: translatey(calc(var(--val)/-100*#{$track-w - $thumb-d}))
Um den Thumb darunter ziehen zu können, müssen wir auch pointer-events: none auf dieses Pseudo-Element setzen. Das Ergebnis ist unten zu sehen - das Ziehen des Thumbs bewegt auch das ::before-Pseudo-Element des Wrappers.

conic-gradient()-Unterstützung haben).Nun, aber was wir hier wirklich wollen, ist, den aktuellen Wert mit diesem Pseudo-Element anzuzeigen. Das Setzen seiner content-Eigenschaft auf var(--val) bewirkt nichts, da --val ein Zahlenwert und keine Zeichenkette ist. Wenn wir es als Zeichenkette setzen würden, könnten wir es als Wert für content verwenden, aber dann könnten wir es nicht mehr für calc() verwenden.
Glücklicherweise können wir dieses Problem mit einem cleveren Trick mit CSS-Zählern umgehen.
counter-reset: val var(--val);
content: counter(val)'%';
Jetzt ist das Ganze funktionsfähig, hurra!

conic-gradient()-Unterstützung haben).Lassen Sie uns also damit beginnen, die Dinge zu verschönern und einige nette Details hinzuzufügen. Wir platzieren den Text in der Mitte des Thumbs, machen ihn white, entfernen alle Umrisse und setzen cursor: pointer auf den input.
.wrap.full {
&::after {
line-height: $thumb-d;
color: #fff;
text-align: center
}
}
[type='range'] {
/* same as before */
cursor: pointer
}
Dies ergibt das folgende schöne Ergebnis.

conic-gradient()-Unterstützung haben).Wiederholungen eliminieren
Eine Sache, die mich stört, ist die Tatsache, dass wir im Fall ohne Diagramm viele gemeinsame Stile auf dem output und im Fall mit Diagramm auf dem .wrap:after haben.

output im Fall ohne Diagramm im Vergleich zu Stilen auf dem .wrap:after im Fall mit Diagramm.Wir können etwas dagegen tun, und das ist die Verwendung einer stillen Klasse, die wir dann erweitern.
%thumb-val {
position: absolute;
width: $thumb-d; height: $thumb-d;
color: #fff;
pointer-events: none
}
.wrap {
&:not(.full) output {
@extend %thumb-val;
/* same other styles */
}
&:after {
@extend %thumb-val;
/* same other styles */
}
}
Schöne Fokus-Stile
Nehmen wir an, wir wollen nicht diesen hässlichen outline auf :focus haben, aber wir wollen diesen Zustand auch visuell deutlich unterscheiden. Was könnten wir also tun? Nun, nehmen wir an, wir machen den Thumb kleiner und ein wenig entsättigt, wenn der input nicht fokussiert ist, und wir blenden auch den Text in diesem Fall aus.
Klingt nach einer coolen Idee... aber da wir keinen Elternelement-Selektor haben, können wir keine Eigenschaftsänderung am ::after des Elternelements des Schiebereglers auslösen, wenn der Schieberegler den Fokus erhält oder verliert. Uff.
Was wir stattdessen tun können, ist, das andere Pseudo-Element des output (das ::before) zu verwenden, um den Wert auf dem Thumb anzuzeigen. Dies bringt zwar eigene Komplikationen mit sich, die wir gleich besprechen werden, aber es ermöglicht uns, Folgendes zu tun.
[type='range']:focus + output:before { /* focus styles */ }
Das Problem bei diesem Ansatz ist, dass wir die font auf dem output selbst aufblähen, aber für sein ::before benötigen wir dieselbe Größe und Dicke wie auf dem Wrapper.
Dies können wir lösen, indem wir eine relative Schriftgröße als Sass-Variable $fsr festlegen und dann diesen Wert verwenden, um die font auf dem tatsächlichen output aufzublähen und sie auf dem output:before Pseudo-Element wieder auf ihre ursprüngliche Größe zu reduzieren.
$fsr: 4;
.wrap {
color: $fg;
&.full {
output {
font-size: $fsr*1em;
&:before {
/* same styles as we had on .wrap:after */
font-size: 1em/$fsr;
font-weight: 200;
}
}
}
}
Abgesehen davon verschieben wir nur die CSS-Deklarationen, die wir zuvor auf dem .wrap:after hatten, auf das output:before.

output-Pseudo-Element.Okay, jetzt können wir zum letzten Schritt übergehen, um zwischen dem normalen und dem fokussierten Look zu unterscheiden.
Wir beginnen damit, die hässliche :focus-Zustands-outline und den Wert auf dem Thumb auszublenden, wenn der Schieberegler nicht fokussiert ist.
%thumb-val {
/* same styles as before */
opacity: 0;
}
[type='range']:focus {
outline: none;
.wrap:not(.full) & + output,
.wrap.full & + output:before { opacity: 1 }
}

conic-gradient()-Unterstützung haben).Als Nächstes legen wir unterschiedliche Stile für die normalen und fokussierten Zustände des Schieberegler-Thumbs fest.
@mixin thumb() {
/* same styles as before */
transform: scale(.7);
filter: saturate(.7)
}
@mixin thumb-focus() {
transform: none;
filter: none
}
[type='range']:focus {
/* same as before */
&::-webkit-slider-thumb { @include thumb-focus }
&::-moz-range-thumb { @include thumb-focus }
&::-ms-thumb { @include thumb-focus }
}

conic-gradient()-Unterstützung haben).Der letzte Schritt ist das Hinzufügen einer Übergangsanimation zwischen diesen Zuständen.
$t: .5s;
@mixin thumb() {
/* same styles as before */
transition: transform $t linear, filter $t
}
%thumb-val {
/* same styles as before */
transition: opacity $t ease-in-out
}

conic-gradient()-Unterstützung haben).Was ist mit Screenreadern?
Da Screenreader heutzutage generierten Inhalt lesen, würden wir in diesem Fall den %-Wert doppelt vorgelesen bekommen. Wir umgehen dies, indem wir role='img' auf unseren output setzen und dann den aktuellen Wert, der vorgelesen werden soll, in ein aria-label-Attribut einfügen.
let conic = false;
function update() {
let newval = +_R.value;
if(val !== newval) {
_W.style.setProperty('--val', _O.value = val = newval);
if(conic) _O.setAttribute('aria-label', `${val}%`)
}
};
update();
_O.setAttribute('for', _R.id);
_W.appendChild(_O);
if(getComputedStyle(_O).backgroundImage !== 'none') {
conic = true;
_W.classList.add('full');
_O.setAttribute('role', 'img');
_O.setAttribute('aria-label', `${val}%`)
}
Die endgültige Demo finden Sie unten. Beachten Sie, dass wir den Fallback nur sehen, wenn Ihr Browser keine native conic-gradient()-Unterstützung hat.
Sehen Sie sich das Pen von thebabydino (@thebabydino) auf CodePen an.
Abschließende Worte
Obwohl die Browserunterstützung hierfür immer noch schlecht ist, wird sich die Situation ändern. Vorerst sind es nur Blink-Browser, die Flags freigeben, aber Safari listet conic-gradient() als in Entwicklung auf, sodass die Dinge bereits besser werden.
Wenn Sie möchten, dass die browserübergreifende Unterstützung eher früher als später Realität wird, können Sie durch Abstimmung für die Implementierung von conic-gradient() in Edge oder durch Hinterlassen eines Kommentars zu diesem Firefox-Bug, warum Sie dies für wichtig halten oder welche Anwendungsfälle Sie im Sinn haben, dazu beitragen. Hier sind meine zur Inspiration.
Großartiger Artikel, Ana. Vielen Dank, dass Sie ihn geschrieben haben!
Ich liebe es, wie wir über benutzerdefinierte Eigenschaften mit JavaScript und CSS interagieren können. Es fühlt sich viel sauberer an als einige der früheren Ansätze, die wir verwendet haben.
Die Zukunft (und die Gegenwart) sieht sehr rosig aus :)
Sie erwähnen, dass CSS-Variablen heutzutage entscheidend sind. Beziehen Sie sich auf native CSS-Variablen oder Preprocessor-Variablen?
Wenn ich "CSS-Variablen" sage, meine ich immer native Variablen. Ansonsten würde ich einfach Preprocessor-Variablen sagen.