<section class="wrapper">
<div class="bg"></div>
<div class="edit">
<div class="factor">
<span>Slow mo</span>
<button type="button" data-factor="dec">-</button>
<span data-factor-value></span>
<button type="button" data-factor="inc">+</button>
</div>
</div>
<h1 class="name-wrapper" data-username>
</h1>
<form class="form" data-form>
<div class="collapsable collapsable--label" data-collapsable>
<label class="label" for="name">
Enter a name to get started
</label>
</div>
<input type="text" name="username" id="name" class="input collapsable collapsable--input" placeholder="Name" spellcheck="false" autocomplete="off" required data-collapsable>
<div class="collapsable collapsable--btn" data-collapsable>
<button type="submit" class="btn">
Save
<span>❯</span>
</button>
</div>
</form>
<section class="circle" data-circle>
<div>
<svg class="circ-rotate" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<circle stroke="white" fill="none" cx="50" cy="50" r="49" vectorEffect="non-scaling-stroke" stroke="1" data-circ-svg />
</svg>
<p>Welcome aboard</p>
<button class="btn" data-reset>Reset
<span>❯</span>
</button>
</div>
</section>
<p class="info">Change the slow mo value to control all the animation and transition speeds</p>
</section>
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@100..900&display=swap");
html,
body {
height: 100%;
margin: 0;
line-height: 1.4;
}
button {
cursor: pointer;
border: 0;
background: none;
font-family: Inter, sans-serif;
}
h1,
p {
margin-bottom: 0;
}
input {
max-width: 100%;
font-family: Inter, sans-serif;
}
label {
font-family: Inter, sans-serif;
}
* {
box-sizing: border-box;
}
body {
background: rgb(10, 10, 10);
font-family: Inter, sans-serif;
--primary: #2a1cf7;
--factor: 1;
}
.wrapper {
position: relative;
height: 100%;
display: flex;
padding: 1rem;
}
.btn {
position: relative;
font-size: 0.85rem;
font-weight: 500;
background: var(--primary);
padding: 0.75rem 1.5rem;
border-radius: 5rem;
color: white;
width: fit-content;
margin-left: auto;
margin-right: auto;
letter-spacing: 0.02rem;
transition: padding calc(0.07s * var(--factor)) ease;
& span {
font-size: 0.65rem;
color: #eee;
position: absolute;
top: 36%;
right: 1.5rem;
height: fit-content;
opacity: 0;
transition: opacity calc(0.1s * var(--factor)) ease,
translate calc(0.1s * var(--factor)) ease;
}
&:focus-visible {
outline: none;
padding-right: 2.25rem;
& span {
opacity: 1;
translate: 4px 0;
}
}
}
.bg {
position: absolute;
width: 100%;
height: 100%;
inset: 0;
background: linear-gradient(-45deg, #020024, #090979, #4b3090);
background-size: 400% 400%;
animation: gradientShift 15s ease infinite;
height: 100vh;
}
@keyframes gradientShift {
0% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
100% {
background-position: 0% 50%;
}
}
.form {
overflow: hidden;
position: absolute;
left: 0;
right: 0;
margin: auto;
width: 500px;
top: 60%;
display: flex;
flex-direction: column;
gap: 1.25rem;
background: #fff;
padding: 1.5rem 2rem;
border-radius: 0.5rem;
& .collapsable {
transition: all calc(0.3s * var(--factor)) cubic-bezier(0.25, 1, 0.5, 1);
overflow: hidden;
opacity: 1;
&.closed {
height: 0;
opacity: 0;
}
}
& .collapsable--label {
height: 24px;
}
& .collapsable--input {
height: 37px;
}
& .label {
color: #333;
font-weight: 700;
}
& .input {
max-width: 100%;
font-size: 1rem;
padding: 0.5rem 0.1rem;
border: 0;
border-bottom: 1.5px solid #777;
background: none;
&::placeholder {
color: #777;
}
&:focus {
outline: 0;
border-color: var(--primary);
}
}
& .collapsable--btn {
height: 40px;
margin: auto;
}
}
@keyframes formMoveUp {
0% {
transform: scaleY(1);
translate: 0 0;
background: #ffffff;
}
100% {
transform: scaleY(0.3);
translate: 0 -30vh;
background: #ffffff00;
}
}
.form-move-up {
animation: formMoveUp calc(0.7s * var(--factor)) calc(0.1s * var(--factor))
cubic-bezier(0.85, 0, 0.15, 1) forwards;
}
.name-wrapper {
position: absolute;
top: 25%;
left: 0;
right: 0;
color: #fff;
margin: auto;
width: fit-content;
opacity: 0;
translate: 0 0;
font-size: 1.75rem;
font-weight: 700;
transition: all calc(var(--factor) * 0.2s) calc(var(--factor) * 0.6s) ease;
&.visible {
opacity: 1;
translate: 0 -26px;
}
}
.circle {
overflow: hidden;
position: absolute;
padding: 2px;
left: 0;
right: 0;
margin: auto;
top: 45%;
width: 280px;
aspect-ratio: 1;
border-radius: 50%;
opacity: 0;
visibility: hidden;
transition: opacity calc(0.7s * var(--factor)) ease calc(0.6s * var(--factor));
& div {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 0.85rem;
width: 100%;
height: 100%;
}
& p {
margin-top: 1rem;
color: #fff;
}
&.visible {
visibility: visible;
opacity: 1;
}
svg {
position: absolute;
inset: 0;
margin: auto;
max-width: 100%;
circle {
stroke-dasharray: 0 70;
}
}
}
@keyframes strokeReveal {
from {
opacity: 0;
stroke-dasharray: 0 70;
}
to {
opacity: 1;
stroke-dasharray: 1 10;
}
}
.stroke-reveal {
animation: strokeReveal calc(2s * var(--factor))
cubic-bezier(0.33, 1, 0.68, 1) calc(0.1s * var(--factor)) forwards;
}
@keyframes circRotate {
from {
rotate: 0deg;
}
to {
rotate: -360deg;
}
}
.circ-rotate {
animation: circRotate calc(10s * var(--factor)) linear infinite forwards;
}
.edit {
position: absolute;
top: 1rem;
left: 1rem;
display: flex;
flex-direction: column;
gap: 1rem;
& button {
color: #fff;
width: fit-content;
font-size: 0.85rem;
border-radius: 4px;
padding: 0;
line-height: 1.4;
}
.factor {
display: flex;
align-items: center;
gap: 8px;
span {
color: #fff;
font-size: 0.85rem;
}
button {
padding: 4px 12px;
background: #ffffff44;
&:first-of-type {
border-radius: 4px 0 0 4px;
}
&:last-of-type {
border-radius: 0 4px 4px 0;
}
}
}
}
.info {
position: relative;
color: #fff;
margin-top: auto;
text-align: center;
width: 100%;
font-size: 0.85rem;
}
const userName = (() => {
let name = document.querySelector("[data-username]");
return {
setName: (val) => (name.innerText = `Hello ${val}`),
toggle: () => name.classList.toggle("visible")
};
})();
const circle = (() => {
let el = document.querySelector("[data-circle]");
let circSvg = document.querySelector("[data-circ-svg]");
return {
toggle: () => {
el.classList.toggle("visible");
circSvg.classList.toggle("stroke-reveal");
}
};
})();
const form = (() => {
let formEl = document.querySelector("[data-form]");
let formCollapsables = document.querySelectorAll("[data-collapsable]");
formEl.addEventListener("submit", (event) => {
event.preventDefault();
let val = new FormData(event.target).get("username");
userName.setName(val);
toggle();
userName.toggle();
circle.toggle();
});
function toggle() {
formEl.classList.toggle("form-move-up");
formCollapsables.forEach((item) => {
item.classList.toggle("closed");
});
}
return {
toggle
};
})();
const editor = (() => {
let factor = 1;
let resetBtn = document.querySelectorAll("[data-reset]");
let factorBtn = document.querySelectorAll("[data-factor]");
let factorDisplay = document.querySelector("[data-factor-value]");
resetBtn.forEach((btn) =>
btn.addEventListener("click", () => {
document.body.style.setProperty("--factor", 0);
userName.toggle();
form.toggle();
circle.toggle();
setTimeout(() => document.body.style.setProperty("--factor", factor), 10);
})
);
factorBtn.forEach((btn) =>
btn.addEventListener("click", (event) => {
let action = event.target.getAttribute("data-factor");
factor = Math.max(0, action === "inc" ? factor + 1 : factor - 1);
document.body.style.setProperty("--factor", factor);
updateDisplay();
})
);
function updateDisplay() {
factorDisplay.innerText = getComputedStyle(document.body).getPropertyValue(
"--factor"
);
}
function setFactor(val) {
factor = val;
}
updateDisplay();
return {
setFactor
};
})();
View Compiled
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.