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: height $tick-anim-time-height $tick-easing, 91 width $tick-anim-time-width $tick-anim-time-height $tick-easing, 92 border-width 0ms $tick-anim-time; 93 } 94 } 95 96 &:hover { 97 span { 98 background: $pf-minimal-background-hover; 99 } 100 } 101 102 input:checked + span { 103 border-color: $pf-primary-background; 104 background: $pf-primary-background; 105 } 106 107 input:focus-visible + span { 108 @include focus; 109 } 110 111 input:checked + span:after { 112 width: $tick-width; 113 height: $tick-height; 114 border-width: 0 2px 2px 0; 115 transition: width $tick-anim-time-height $tick-easing, 116 height $tick-anim-time-height $tick-anim-time-width $tick-easing, 117 border-width 0ms; 118 } 119 120 &.pf-disabled { 121 cursor: not-allowed; 122 color: $pf-minimal-foreground-disabled; 123 124 span { 125 border-color: $pf-minimal-foreground-disabled; 126 background: none; 127 &:after { 128 border-color: $pf-primary-foreground; 129 } 130 } 131 132 input:checked ~ span { 133 border-color: $pf-primary-background-disabled; 134 background: $pf-primary-background-disabled; 135 } 136 } 137} 138