├── index.html
├── script.js
└── styles.css
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Document
10 |
11 |
12 |
13 |
18 |
19 |
--------------------------------------------------------------------------------
/script.js:
--------------------------------------------------------------------------------
1 | const btn = document.querySelector("[data-btn]")
2 | btn.addEventListener("click", () => {
3 | btn.classList.add("animating")
4 | })
5 |
--------------------------------------------------------------------------------
/styles.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: white;
3 | min-height: 100vh;
4 | display: flex;
5 | justify-content: center;
6 | align-items: center;
7 | margin: 0;
8 | }
9 |
10 | :root {
11 | --squish-animation-time: 500ms;
12 | --progress-animation-time: 1000ms;
13 | --circle-animation-time: 300ms;
14 | --checkmark-animation-time: 300ms;
15 | --btn-width: 125px;
16 | --btn-height: 40px;
17 | }
18 |
19 | .btn {
20 | position: relative;
21 | background-color: #2B2D2F;
22 | color: #71DFBE;
23 | border: none;
24 | border-radius: .125em;
25 | width: var(--btn-width);
26 | height: var(--btn-height);
27 | font-weight: bold;
28 | cursor: pointer;
29 | padding: 0;
30 | }
31 |
32 | .btn.animating {
33 | background-color: transparent;
34 | color: transparent;
35 | user-select: none;
36 | cursor: default;
37 | animation: hide 0ms calc(var(--squish-animation-time) + var(--progress-animation-time)) forwards;
38 | }
39 |
40 | .btn::before {
41 | content: "";
42 | display: none;
43 | position: absolute;
44 | background-color: #2B2D2F;
45 | inset: 0;
46 | border-radius: .125em;
47 | animation: squish var(--squish-animation-time) cubic-bezier(0.26, 0.6, 0.46, 1.7);
48 | animation-fill-mode: forwards;
49 | }
50 |
51 |
52 | .btn::after {
53 | content: "";
54 | display: none;
55 | position: absolute;
56 | background-color: #AAA;
57 | left: 51%;
58 | right: 51%;
59 | top: 45%;
60 | bottom: 45%;
61 | border-radius: .25em;
62 | animation: progress var(--progress-animation-time) var(--squish-animation-time);
63 | animation-fill-mode: forwards;
64 | }
65 |
66 | .btn.animating::before,
67 | .btn.animating::after {
68 | display: block;
69 | }
70 |
71 | .btn.animating + .checkmark-container {
72 | background-color: #2B2D2F;
73 | border-radius: .25em;
74 | width: 0;
75 | height: 0;
76 | animation: circle var(--circle-animation-time) calc(var(--squish-animation-time) + var(--progress-animation-time)) forwards cubic-bezier(0.26, 0.6, 0.46, 1.7);
77 | display: flex;
78 | justify-content: center;
79 | align-items: center;
80 | }
81 |
82 | .btn.animating + .checkmark-container .checkmark-svg {
83 | stroke: white;
84 | width: 25px;
85 | stroke-dashoffset: 40.84104919433594;
86 | stroke-dasharray: 40.84104919433594;
87 | stroke-linecap: round;
88 | stroke-linejoin: round;
89 | stroke-width: 3px;
90 | animation: checkmark var(--circle-animation-time) calc(var(--squish-animation-time) + var(--progress-animation-time) + var(--circle-animation-time)) forwards;
91 | }
92 |
93 | @keyframes squish {
94 | 100% {
95 | left: -25%;
96 | right: -25%;
97 | top: 45%;
98 | bottom: 45%;
99 | border-radius: .25em;
100 | }
101 | }
102 |
103 | @keyframes progress {
104 | 100% {
105 | left: -25%;
106 | right: -25%;
107 | }
108 | }
109 |
110 | @keyframes hide {
111 | 100% {
112 | width: 0;
113 | height: 0;
114 | }
115 | }
116 |
117 | @keyframes circle {
118 | 0% {
119 | width: calc(var(--btn-width) * 1.50);
120 | height: calc(var(--btn-height) * .1);
121 | }
122 | 100% {
123 | background-color: #71DFBE;
124 | width: 50px;
125 | height: 50px;
126 | border-radius: 100%;
127 | }
128 | }
129 |
130 | @keyframes checkmark {
131 | 100% {
132 | stroke-dashoffset: 0;
133 | }
134 | }
--------------------------------------------------------------------------------