See the Pen iOS toggle button by Shubo Chao (@shubochao) on CodePen.

這篇教學要介紹如何用純CSS把checkbox做成iOS風格的開關(toggle)。最近在CSS Secrets裡面看到這樣的技巧,覺得還滿酷的!決定分享給大家。

Checkbox Hack

如果我們想要用CSS自訂核取方塊的樣式,該怎麼做呢?input可以自訂的CSS樣式並不多。作者提到的技巧是:直接把input藏起來,用虛擬元件來做一個假的方塊。

checkbox通常是由一個核取方塊(input)加一段文字(label)構成:

<input type="checkbox" id="chkbox" />
<label for="chkbox">Some description</label>

因為input和label通常會一起搭配使用,所以可以用label::before來做假的方塊。最巧妙的地方在於,利用CSS的相鄰兄弟選擇器(Adjacent sibling selectors),可以分別對沒勾和勾的狀態,也就是input+label::before和input:checked+label::before分別設定樣式。

See the Pen customized-checkbox by Shubo Chao (@shubochao) on CodePen.

⬆︎ 自訂的核取方塊:

input[type="checkbox"] + label::before {
  content: '\a0';
  display: inline-block;
  border-radius: 0.2em;
  background: #FFF;
  border: 1px solid #CCC;
}

⬆︎ 用pseudo element做一個白底的方形,替代原本的方塊。

input[type="checkbox"]:checked + label::before {
  content: '\2713';
  background: yellowgreen;
}

⬆︎ 打勾的時候顯示✓,背景變成黃綠色。

input[type="checkbox"]:disabled + label::before {
  background: #DDD;
  color: #AAA;
}

⬆︎ 也可以設定disabled時的樣式喔。

input[type="checkbox"] {
  position: absolute;
  clip: rect(0, 0, 0, 0);
}

⬆︎ 把原本的方框藏起來。

iOS Toggle Button

Positioning

iOS的toggle button,其實沒啥學問,就只是一個橢圓形加一個圓形的組合XD。正好虛擬元素剛也有兩個可以用,這邊拿label::before做橢圓形,另外一個label::after做圓形。

.list-item input[type="checkbox"] + label {
  position: relative;
  width: 100%;
}

.list-item input[type="checkbox"] + label::after {
  position: absolute;
  right: 24px;
}

.list-item input[type="checkbox"]:checked + label::after {
  position: absolute;
  right: 0;
}

⬆︎ 圓形和橢圓形的定位,是用position: absolute將label定位在相對母元素(label)右側的位置。分別在checked和unchecked的時候調整圓形的位置。

Change Color: Using Box-Shadow

.list-item input[type="checkbox"] + label::before {
  box-shadow: 0 0 0 gray inset;
  transition: all 0.25s ease-in-out 0s;
}

.list-item input[type="checkbox"]:checked + label::before {
  box-shadow: 0 0 100px limegreen inset;
}

⬆︎ 橢圓型變色的部分,是用不同顏色和範圍的內凹陰影,加上CSS transition做的。光靠設定背景的顏色,不知道要怎麼做出從中心開始變色的效果…

Vertical Alignment

置中效果最後是用flexbox完成的。一開始試了很多方法,例如:

  • 調整line-height = height (似乎只對單行的純文字有效,加進img span 虛擬元素等等就歪了)
  • 不死心,試著調整label * { vertical-align: middle;},還是會上下飄。加進虛擬元素似乎會改變一個元素的高度,讓他垂直對不齊。如果是img之類的東西也沒辦法用這方法跟文字同時置中對齊。放棄。
.list-item {
  display: flex;
  align-items: center;
}

.list-item input[type="checkbox"] + label {
  flex: 1;
}

⬆︎ 最後還是用flexbox的align-items屬性,瞬間把所有事情都搞定XD。然後設定label的flex: 1將寬度延展到最長,按鈕才能定位在最右邊。

@mixin vertical-align {
  top: 50%;
  transform: translateY(-50%);
}

圓形和橢圓形的對齊,只要垂直對齊母元素就可以了,因為母元素已經用flexbox垂直置中了。子元素已經用了absolute絕對定位(因為要對齊右邊),直接從母元素最上面往下移50%,再往上移回自己的50%,就水平對齊母元素了。

我自己覺得這個對齊的技巧對虛擬元素還挺實用的,因為虛擬元素通常需要相對母元素定位,特別是水平或垂直置中。(這邊也有用到。)

Conclusion

今天用了虛擬元素來自訂checkbox。最棒的地方在於,HTML還是和原本一樣簡潔,也不用額外的JS!

<input type="checkbox" id="chkbox" />
<label for="chkbox">Some description</label>

今天又多學會了一個技巧,但是想想,做這個也是挺無用的呀~不過至少在做的過程中得到了一些小小樂趣和成就感,也是挺不錯的~

Reference

覺得這篇文章對你有幫助的話,歡迎分享👉