1// Copyright (C) 2023 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15@import "theme"; 16 17// This checkbox element is expected to contain a checkbox type input followed 18// by an empty span element. 19// The input is completely hidden and an entirely new checkbox is drawn inside 20// the span element. This allows us to style it how we like, and also add some 21// fancy transitions. 22// The box of the checkbox is a fixed sized span element. The tick is also a 23// fixed sized rectange rotated 45 degrees with only the bottom and right 24// borders visible. 25// When unchecked, the tick size and border width is 0, so the tick is 26// completely invsible. When we transition to checked, the border size on the 27// bottom and right sides is immmdiately set to full width, and the tick morphs 28// into view first by expanding along the x axis first, then expanding up the 29// y-axis. This has the effect of making the tick look like it's being drawn 30// onto the page with a pen. 31// When transitioning from checked to unchecked, the animation plays in reverse, 32// and the border width is set to 0 right at the end in order to make the tick 33// completely invisible again. 34.pf-checkbox { 35 $tick-anim-time-width: 100ms; 36 $tick-anim-time-height: 150ms; 37 $tick-anim-time: $tick-anim-time-width + $tick-anim-time-height; 38 $tick-easing: linear; 39 40 $box-size: 18px; 41 $tick-height: 9px; 42 $tick-width: 5px; 43 $box-label-padding: 6px; 44 45 display: inline-block; 46 position: relative; // Turns this container into a positioned element 47 font-family: $pf-font; 48 font-size: inherit; 49 color: $pf-minimal-foreground; 50 user-select: none; 51 cursor: pointer; 52 padding-left: $box-size + $box-label-padding; 53 54 // Hide the default checkbox 55 input { 56 position: absolute; 57 opacity: 0; 58 pointer-events: none; 59 } 60 61 // The span forms the "box" of the checkbox 62 span { 63 position: absolute; 64 left: 0; 65 top: 0; 66 bottom: 0; 67 margin-top: auto; 68 margin-bottom: auto; 69 height: $box-size; 70 width: $box-size; 71 border-radius: $pf-border-radius; 72 border: solid 2px $pf-minimal-foreground; 73 transition: background $pf-anim-timing; 74 background: none; 75 76 // The :after element forms the "tick" of the checkbox 77 &:after { 78 content: ""; 79 display: block; 80 position: absolute; 81 bottom: 7.5px; 82 left: 1px; 83 width: 0px; 84 height: 0px; 85 border-color: $pf-primary-foreground; 86 border-style: solid; 87 border-width: 0; 88 transform-origin: 0% 100%; // Put the origin at the short edge of the tick 89 transform: rotate(45deg); 90 transition: 91 height $tick-anim-time-height $tick-easing, 92 width $tick-anim-time-width $tick-anim-time-height $tick-easing, 93 border-width 0ms $tick-anim-time; 94 } 95 } 96 97 &:hover { 98 span { 99 background: $pf-minimal-background-hover; 100 } 101 } 102 103 input:checked + span { 104 border-color: $pf-primary-background; 105 background: $pf-primary-background; 106 } 107 108 input:focus-visible + span { 109 @include focus; 110 } 111 112 input:checked + span:after { 113 width: $tick-width; 114 height: $tick-height; 115 border-width: 0 2px 2px 0; 116 transition: 117 width $tick-anim-time-height $tick-easing, 118 height $tick-anim-time-height $tick-anim-time-width $tick-easing, 119 border-width 0ms; 120 } 121 122 &.pf-disabled { 123 cursor: not-allowed; 124 color: $pf-minimal-foreground-disabled; 125 126 span { 127 border-color: $pf-minimal-foreground-disabled; 128 background: none; 129 &:after { 130 border-color: $pf-primary-foreground; 131 } 132 } 133 134 input:checked ~ span { 135 border-color: $pf-primary-background-disabled; 136 background: $pf-primary-background-disabled; 137 } 138 } 139} 140