<div class="switch">
<label class="switch__label" for="temperature-scale">Temperature</label>
<button class="switch__button" type="button" id="temperature-scale" title="Temperature Scale" aria-labelledby="f">
<span class="switch__button-wrap">
<span class="switch__shadow"></span>
<span class="switch__shadow"></span>
<span class="switch__inner">
<span class="switch__options">
<span id="c" class="switch__option-label" aria-hidden="true">°C</span>
<span class="switch__option-sep"></span>
<span id="f" class="switch__option-label" aria-hidden="true">°F</span>
</span>
</span>
</span>
</button>
</div>
HTML
格式化
支持Emmet,输入 p 后按 Tab键试试吧!
<head> ... </head>
<body>
</body>
* {
border: 0;
box-sizing: border-box;
margin: 0;
padding: 0;
}
:root {
--hue: 223;
--bg: hsl(var(--hue),10%,90%);
--fg: hsl(var(--hue),10%,10%);
--trans-dur: 0.3s;
--trans-timing: cubic-bezier(0.65,0,0.35,1);
font-size: calc(60px + (120 - 60) * (100vw - 280px) / (3840 - 280));
}
body,
button {
font: 1em/1.5 "DM Sans", sans-serif;
}
body {
background-color: var(--bg);
color: var(--fg);
display: flex;
height: 100vh;
transition:
background-color var(--trans-dur),
color var(--trans-dur);
}
.switch {
display: flex;
margin: auto;
&__button,
&__label {
-webkit-tap-highlight-color: transparent;
}
&__button {
background-color: transparent;
border-radius: 0.75em;
box-shadow: 0 0 0 0.125em hsla(var(--hue),90%,50%,0);
cursor: pointer;
outline: transparent;
width: 100%;
height: 100%;
-webkit-appearance: none;
appearance: none;
z-index: 1;
transition: box-shadow calc(var(--trans-dur) / 2);
&:focus-visible {
box-shadow: 0 0 0 0.125em hsla(var(--hue),90%,50%,1);
}
&,
&-wrap {
display: block;
position: relative;
}
&-wrap {
box-shadow:
0 0.0625em 0.0625em hsl(var(--hue),10%,65%) inset,
0 -0.0625em 0.0625em hsl(var(--hue),10%,87.5%) inset,
0 0.25em 0.25em hsl(var(--hue),10%,70%) inset;
border-radius: 0.75em;
width: 3.1875em;
height: 1.5em;
transition: box-shadow var(--trans-dur);
}
}
&__label {
overflow: hidden;
margin-inline-end: 0.75em;
position: absolute;
width: 1px;
height: 1px;
}
&__inner,
&__options,
&__options:before,
&__options:after,
&__shadow {
position: absolute;
}
&__inner,
&__options {
background-color: hsl(var(--hue),10%,90%);
}
&__inner {
box-shadow: 0 -0.125em 0.25em hsl(var(--hue),10%,80%) inset;
border-radius: 0.5em;
display: block;
perspective: 20em;
inset: 0.25em;
transition:
background-color var(--trans-dur),
box-shadow var(--trans-dur);
}
&__options {
border-radius: inherit;
box-shadow: 0 0 1px hsl(0,0%,100%) inset;
display: flex;
justify-content: space-around;
font-size: 0.5em;
line-height: 2;
width: 100%;
height: 100%;
transform: translateX(-0.125rem) rotateY(-15deg);
transition:
background-color var(--trans-dur),
box-shadow var(--trans-dur),
transform var(--trans-dur) var(--trans-timing);
&:before,
&:after {
content: "";
display: block;
top: 0;
width: 45%;
height: 100%;
transition: opacity var(--trans-dur) var(--trans-timing);
}
&:before {
border-radius: 0.5rem 0 0 0.5rem;
box-shadow:
0 1px 0.0625rem hsla(var(--hue),10%,10%,0.7) inset,
0.125rem 0.25rem 0.25rem hsla(var(--hue),10%,10%,0.3) inset;
mask-image: linear-gradient(-90deg,hsla(var(--hue),10%,90%,0),hsl(var(--hue),10%,90%));
left: 0;
}
&:after {
border-radius: 0 0.5rem 0.5rem 0;
box-shadow:
0 1px 0.0625rem hsla(var(--hue),10%,10%,0.7) inset,
-0.125rem 0.25rem 0.25rem hsla(var(--hue),10%,10%,0.3) inset;
mask-image: linear-gradient(90deg,hsla(var(--hue),10%,90%,0),hsl(var(--hue),10%,90%));
right: 0;
opacity: 0;
}
}
&__option {
&-label {
color: var(--fg);
font-weight: 500;
text-align: center;
width: 100%;
text-shadow:
0 -1px 0 hsl(var(--hue),10%,40%),
0 1px 0 hsl(0,0%,100%);
transition:
color var(--trans-dur),
text-shadow var(--trans-dur);
&:last-child {
color: hsl(var(--hue),10%,60%);
}
}
&-sep {
background-color: hsl(var(--hue),10%,75%);
box-shadow: 1px 0 0 hsl(0,0%,100%);
width: 1px;
min-width: 1px;
margin: auto 0;
height: 0.75rem;
transition:
background-color var(--trans-dur),
box-shadow var(--trans-dur);
}
}
&__shadow {
border-radius: 0.375em 0.875em 0.375em 0.375em / 0.375em 0.625em 0.125em 0.375em;
box-shadow: 0 0 0 0.125em hsla(var(--hue),10%,10%,0.7);
display: block;
top: 0.375em;
left: 0.375em;
width: 1.25em;
height: 0.75em;
transform: rotate(0);
transform-origin: 1em 0.375em;
transition:
box-shadow var(--trans-dur) var(--trans-timing),
transform var(--trans-dur) var(--trans-timing);
z-index: -1;
}
&__shadow + &__shadow {
border-radius: 0.875em 0.375em 0.375em 0.375em / 0.625em 0.375em 0.375em 0.125em;
box-shadow: 0 0 0.25em 0.125em hsla(var(--hue),10%,10%,0.7);
right: 0.375em;
left: auto;
transform: rotate(7.5deg);
transform-origin: 0.5em 0.375em;
}
&__button[aria-labelledby="f"] &__options {
transform: translateX(0.125rem) rotateY(15deg);
&:before {
opacity: 0;
}
&:after {
opacity: 1;
}
}
&__button[aria-labelledby="f"] &__option-label {
color: hsl(var(--hue),10%,60%);
&:last-child {
color: var(--fg);
}
}
&__button[aria-labelledby="f"] &__shadow {
box-shadow: 0 0 0.25em 0.125em hsla(var(--hue),10%,10%,0.7);
transform: rotate(-7.5deg);
}
&__button[aria-labelledby="f"] &__shadow + &__shadow {
box-shadow: 0 0 0 0.125em hsla(var(--hue),10%,10%,0.7);
transform: rotate(0);
}
}
/* Dark theme */
@media (prefers-color-scheme: dark) {
:root {
--bg: hsl(var(--hue),10%,30%);
--fg: hsl(var(--hue),10%,90%);
}
.switch {
&__button {
box-shadow: 0 0 0 0.125em hsla(var(--hue),90%,70%,0);
&:focus-visible {
box-shadow: 0 0 0 0.125em hsla(var(--hue),90%,70%,1);
}
&-wrap {
box-shadow:
0 0.0625em 0.0625em hsl(var(--hue),10%,5%) inset,
0 -0.0625em 0.0625em hsl(var(--hue),10%,27.5%) inset,
0 0.25em 0.25em hsl(var(--hue),10%,20%) inset;
}
}
&__inner,
&__options {
background-color: hsl(var(--hue),10%,30%);
}
&__inner {
box-shadow: 0 -0.125em 0.25em hsl(var(--hue),10%,20%) inset;
}
&__options {
box-shadow: 0 0 1px hsl(var(--hue),10%,50%) inset;
}
&__option {
&-label {
text-shadow:
0 -1px 0 hsl(var(--hue),10%,10%),
0 1px 0 hsl(var(--hue),10%,50%);
&:last-child {
color: hsl(var(--hue),10%,20%);
}
}
&-sep {
background-color: hsl(var(--hue),10%,20%);
box-shadow: 1px 0 0 hsl(var(--hue),10%,50%);
}
}
&__button[aria-labelledby="f"] &__option-label {
color: hsl(var(--hue),10%,20%);
}
&__shadow
&__button[aria-labelledby="f"] &__shadow + &__shadow {
box-shadow: 0 0 0 0.125em hsla(var(--hue),10%,10%,0.9);
}
&__shadow + &__shadow,
&__button[aria-labelledby="f"] &__shadow {
box-shadow: 0 0 0.25em 0.125em hsla(var(--hue),10%,10%,0.9);
}
}
}
window.addEventListener("DOMContentLoaded",() => {
const rockerSwitch = new RockerSwitch("#temperature-scale");
});
class RockerSwitch {
/** Button used for this switch */
button: HTMLButtonElement | null;
/**
* @param buttonEl CSS selector of the button to use
*/
constructor(buttonEl: string) {
this.button = document.querySelector(buttonEl);
this.button?.addEventListener("click",this.temperatureScaleToggle.bind(this));
}
private _temperatureScale: TemperatureScale = "f";
/** Selected temperature scale */
get temperatureScale(): TemperatureScale {
return this._temperatureScale;
}
set temperatureScale(value: TemperatureScale) {
this._temperatureScale = value;
this.button?.setAttribute("aria-labelledby",this._temperatureScale);
}
/** Set the temperature scale to °C or °F. */
temperatureScaleToggle(): void {
this.temperatureScale = this.temperatureScale === "c" ? "f" : "c";
}
}
type TemperatureScale = "c" | "f";