input#start(type="checkbox", name="start")
input#playPause(type="checkbox", name="playPause")
p.CTA
span All pause and no PLAY makes
span Jack-o-Lantern a dull pen.
p
- var ngon = 24
.scene
.pivot
- for (var s = 0; s < ngon; ++s) {
.slice
- for (var f = 0; f < ngon/2; ++f) {
.facet
if s == 6 || s == 7
if f == 3 || f == 8
.corner.outerCorner
else if f == 4 || f == 7
.corner.innerCorner
else if s == 9 || s == 12
if f > 0 && f < ngon/2 - 1
.lefTooth
.righTooth
else if s == 20
.crookeTooth
.left
.right
.top
.behind
- }
- }
.partition.vertical
.partition.coverEyes
.partition.loGums
.partition.crooked
.partition.coverPeepers
.partition.lazyEye
- for (var l = 0; l < ngon/2; ++l) {
div
- }
.partition.lazyEye
- for (var l = 0; l < ngon/2; ++l) {
div
- }
.lilPump
.segments
- for (var seg = 0; seg < ngon/2; ++seg) {
div
- }
.holes
- for (var hol = 0; hol < ngon/2; ++hol) {
div
- }
label.startBtn(for="start")
label.playBtn(for="playPause")
div
div
View Compiled
@use "sass:math";
$ngon: 24;
$angle: 360deg/$ngon;
$sliceW: 100em*math.sin($angle/2);
body {
margin: 0;
background: #000;
font-size: .66vh;
display: grid;
height: 100vh;
color: #fff;
}
.CTA {
font-family: courier;
font-weight: bold;
font-size: 2vw;
line-height: 2em;
color: #f80;
margin: 1em;
justify-self: center;
text-shadow: .1em .1em .2em #0b0;
}
span {
display: inline-block;
white-space: nowrap;
overflow: hidden;
justify-self: center;
}
span:nth-child(1) {
animation: tippaTappa 8s steps(27,end) infinite;
}
span:nth-child(2) {
animation: clicketyClack 8s steps(27,end) infinite;
}
@keyframes tippaTappa {
0% {
width: 0ch;
}
25% {
width: 27ch;
}
}
@keyframes clicketyClack {
0%, 25% {
width: 0ch;
}
50% {
width: 27ch;
}
}
.scene {
width: 100em;
aspect-ratio: 1;
position: absolute;
display: grid;
place-self: center;
filter: contrast(1.1);
}
.scene, .pivot, .slice, .facet, .facet > div, .partition {
transform-style: preserve-3d;
}
.pivot {
position: absolute;
inset: 0;
display: grid;
justify-items: center;
animation: spin 12s linear infinite;
--offset: #{$angle};
}
/* PARTITIONS: dividers rotated around the y axis that either separate/obscure different sections or serve as rims for the carved openings */
.partition {
content: '';
position: absolute;
inset: 0%;
margin: 1%; /* just so that the partition doesn't poke through the sphere */
border-radius: 50%;
}
.vertical {
background: #000;
rotate: y calc(2*var(--offset));
}
.coverEyes {
background-image: linear-gradient(90deg, black 50%, transparent 50%); /* a half-circle covering only the evil's eyes */
rotate: y calc(-2.5*var(--offset));
}
.loGums {
background-image: linear-gradient(-20deg, green,orangered);
mask-image: radial-gradient(transparent calc(#{math.sqrt(2)}*45%), black calc(#{math.sqrt(2)}*45%));
-webkit-mask-image: radial-gradient(transparent calc(#{math.sqrt(2)}*45%), black calc(#{math.sqrt(2)}*45%));
clip-path: inset(0 50% 0 0);
rotate: y calc(3.5*var(--offset));
margin: .5%; /* either that or a ngon clip-path - in which case border-radius would also be redundant */
backface-visibility: hidden;
animation: glow 12s linear infinite;
animation-delay: -8s;
}
.crooked {
mask-image: conic-gradient(#000 45deg, #0000 45deg 60deg,#000 60deg 90deg,#0000 90deg 105deg, #000 105deg 135deg,#0000 135deg 150deg,#000 150deg 180deg, #0000 180deg);
-webkit-mask-image: conic-gradient(#000 45deg, #0000 45deg 60deg,#000 60deg 90deg,#0000 90deg 105deg, #000 105deg 135deg,#0000 135deg 150deg,#000 150deg 180deg, #0000 180deg);
rotate: y calc(21.5*var(--offset));
margin: 0; /* either that or a ngon clip-path - in which case border-radius would also be redundant */
backface-visibility: hidden;
animation: glow 12s linear infinite;
animation-delay: 1s;
animation-play-state: paused;
}
.crooked::before {
content: '';
position: absolute;
inset: 0;
background-image: linear-gradient(orange,lime);
mask-image: radial-gradient(transparent calc(#{math.sqrt(2)}*45%), black calc(#{math.sqrt(2)}*45%));
-webkit-mask-image: radial-gradient(transparent calc(#{math.sqrt(2)}*45%), black calc(#{math.sqrt(2)}*45%));
border-radius: 50%;
}
.coverPeepers {
background-image: radial-gradient(circle, #0000 30%, #000 30% 65%, #0000 65%);
mask-image: linear-gradient(90deg, black 49%, transparent 49%);
-webkit-mask-image: linear-gradient(90deg, black 49%, transparent 49%); /* a half-circle covering only the evil's eyes */
rotate: y calc(-13.5*var(--offset));
}
.lazyEye {
margin: 0; /* toggling off the standard partition margin, don't need it here */
border-radius: 0;
display: flex;
align-items: center;
transform: rotateY(calc(12.5*var(--offset))) rotate(calc(var(--dir)*30deg));
}
.lazyEye::before {
content: '';
position: absolute;
width: 45em;
aspect-ratio: 1;
border-radius: 50%;
animation: eyeRoll 1s linear infinite;
}
.lazyEye:nth-child(even) {
--dir: 1;
}
.lazyEye:nth-child(odd) {
--dir: -1;
}
.lazyEye:nth-child(even)::before {
background-image: radial-gradient(at 66.6% 50%, #fff 2%, #0f0 2%, #000 5%);
/* background-image: linear-gradient(blue 49%, pink 49% 51%, blue 51%); */
}
.lazyEye:nth-child(odd)::before {
background-image: radial-gradient(at 33.3% 50%, #ff0 2%, #f80 2%, #000 5%);
/* background-image: linear-gradient(blue 49%, lime 49% 51%, blue 51%); */
}
.lazyEye div {
left: 2.8em;
position: absolute;
width: 7em;
--lazyH: calc(2.2*#{$sliceW}*#{math.sin($angle)});
height: calc(var(--lazyH) + 1px);
transform: rotateX(calc(var(--lazyStep)*var(--offset)*2)) translateZ(calc(#{$sliceW}*-1.1*#{math.cos($angle)})); /* rotation is multiplied by two since the number of facets is half of the ngon */
background-size: 100% calc(12*var(--lazyH));
backface-visibility: hidden;
transform-style: preserve-3d;
animation: rollGlow 1s linear infinite;
}
.lazyEye:nth-child(even) div {
background-image: linear-gradient(#4f4, #280 45% 55%, #280, #4f4 100%);
/* background-image: linear-gradient(#0f0, #022, #100 15% 85%, #022, #0f0 100%); */
/* background-image: linear-gradient(blue 49%, red 49% 51%, blue 51%); */
}
.lazyEye:nth-child(odd) div {
background-image: linear-gradient(#f80 5%, #ff4, #f80 95%);
/* background-image: linear-gradient(#010 35%, #840, #fb0, #840, #010 65%); */
/* background-image: linear-gradient(blue 49%, yellow 49% 51%, blue 51%); */
}
@for $i from 1 through $ngon/2 {
.lazyEye div:nth-child(#{$i}) {
--lazyStep: #{$i - 1};
}
}
.crookeTooth {
position: absolute;
width: inherit;
aspect-ratio: 1;
transform-origin: 100% 0;
transform-style: preserve-3d;
--thickness: 6em;
animation: looseTeeth 1.5s ease-in-out alternate infinite, flatColor 12s linear infinite;
animation-delay: calc(var(--looseStep)*-.7s), -10s;
}
@for $i from 1 through $ngon/2 {
$crookDelta: (math.sin($angle*($i - 1)) - math.sin($angle*$i))/2;
$crookTurn: math.asin($crookDelta);
.facet:nth-child(#{$i}) .crookeTooth {
right: calc(50%*#{(1+math.sin($angle*($i - 1)))});
--turnTooth: calc(-1*#{$crookTurn});
--looseStep: #{$i - 1};
}
}
.crookeTooth, .crookeTooth div {
backface-visibility: hidden;
}
.crookeTooth .behind {
position: absolute;
inset: 0;
background: #000;
transform: translateZ(calc(-1*var(--thickness))) rotateY(180deg);
}
.crookeTooth .left, .crookeTooth .right, .crookeTooth .top {
position: absolute;
width: 100%;
height: var(--thickness);
animation: glow 12s linear infinite;
animation-delay: -1s;
}
.crookeTooth .left {
background: darkgreen;
bottom: 100%;
transform-origin: 0 100%;
rotate: x 90deg;
}
.crookeTooth .right {
background: maroon;
top: 100%;
transform-origin: 0 0;
rotate: x -90deg;
}
.crookeTooth .top {
background-image: linear-gradient(orange,lime);
top: 0;
transform-origin: 0 0%;
transform: rotate(90deg) rotateX(-90deg);
}
.slice {
position: absolute;
width: calc(#{$sliceW} + 1px);
height: 100%;
transform: rotateY(calc(var(--nthSlice)*-1*#{$angle}));
display: grid;
}
@for $i from 1 through $ngon {
.slice:nth-child(#{$i}) {
--nthSlice: #{$i - 1};
}
}
.slice:nth-of-type(2n+1) {
--latter: 0;
}
.slice:nth-of-type(2n) {
--latter: 1;
}
.facet {
position: absolute;
width: 100%;
transform-origin: 50% 0;
padding-block: 1px;
backface-visibility: hidden;
}
.facet:empty, .facet:not(:empty)::before {
background-image: linear-gradient(90deg,#050,#080400,#f60,#080400,#050);
background-size: calc(.5*100em*#{math.sin($angle/2)}*#{$ngon}) 100%; /* .5 being the stretch factor */
background-position-x: calc(.5*(var(--nth)*100em*#{math.sin($angle/2)} - 100em*#{math.sin($angle/2)}*var(--latter)));
animation: lighting 12s linear infinite;
}
.facet:before {
content: '';
position: absolute;
inset: 0;
}
.slice:nth-child(2) .facet::after, .slice:nth-child(3) .facet::after { /* might be redundant, since it's black on black but might come in handy if I adda background */
content: '';
position: absolute;
inset: 0;
background: #000;
rotate: y 180deg;
backface-visibility: hidden;
}
@for $i from 1 through $ngon/2 {
$y1: 50em*(1 - math.cos($angle*($i - 1)));
$y2: 50em*(1 - math.cos($angle*$i));
$deltaY: $y2 - $y1;
$z1: 50em*math.sin($angle*($i - 1));
$z2: 50em*math.sin($angle*$i);
$deltaZ: $z2 - $z1;
$facetH: math.hypot($deltaY,$deltaZ);
$facetAngle: math.atan($deltaZ/$deltaY);
.facet:nth-child(#{$i}) {
top: #{$y1};
height: #{$facetH};
transform: translateZ(#{$z1}) rotateX(#{$facetAngle});
}
.facet:nth-child(#{$i}), .gapSides > div > div:nth-child(#{$i}) {
--nth: #{$i - 1};
}
.gapSides > div > div:nth-child(#{$i}) {
--inclineX: calc(-1*var(--offset) * #{math.cos($angle/2)});
}
$TLtip: 50%*(1 - math.sin($angle*($i - 1)));
$TRtip: 50%*(1 + math.sin($angle*($i - 1)));
$BLtip: 50%*(1 - math.sin($angle*$i));
$BRtip: 50%*(1 + math.sin($angle*$i));
.facet:nth-child(#{$i}):empty, .facet:nth-child(#{$i}):not(:empty)::before, .slice:nth-child(2) .facet:nth-child(#{$i})::after, .slice:nth-child(3) .facet:nth-child(#{$i})::after {
clip-path: polygon(#{$TLtip} 0, #{$TRtip} 0, #{$BRtip} 100%, #{$BLtip} 100%);
}
@if $i == 4 or $i == 8 {
.slice:nth-child(7) .facet:nth-child(#{$i})::before {
clip-path: polygon(#{$TLtip} 0, #{$TRtip} 0, #{$BRtip} 100%);
/* the last one is left out; notice the 1-sin/1+sin strings in each coordinate */
}
.slice:nth-child(8) .facet:nth-child(#{$i})::before {
clip-path: polygon(#{$TLtip} 0, #{$TRtip} 0, #{$BLtip} 100%);
/* the third one is left out */
}
}
@if $i == 5 or $i == 9 {
.slice:nth-child(7) .facet:nth-child(#{$i})::before {
clip-path: polygon(#{$TRtip} 0, #{$BRtip} 100%, #{$BLtip} 100%);
/* the last one is left out; notice the 1-sin/1+sin strings in each coordinate */
}
.slice:nth-child(8) .facet:nth-child(#{$i})::before {
clip-path: polygon(#{$TLtip} 0, #{$BRtip} 100%, #{$BLtip} 100%);
/* the third one is left out */
}
}
@if $i == 4 {
$lCw: (math.sin($angle*($i - 1)) + math.sin($angle*$i))/2*$sliceW;
$lCh: math.hypot($deltaY,$deltaZ); // #{$facetH};
$hypoLC: math.hypot($lCw,$lCh);
$angleLC: math.atan($lCw/$lCh);
.outerCorner { /* this is the same for outer and inner eye corner */
position: absolute;
height: #{$hypoLC};
--corner: #{math.sin($angle*($i - 1))};
width: 5em;
transform: rotate(calc(-1*#{$angleLC}*var(--tdir))) rotateY(-90deg);
}
}
@if $i == 5 {
$lCw: (math.sin($angle*($i - 1)) + math.sin($angle*$i))/2*$sliceW;
$lCh: math.hypot($deltaY,$deltaZ); // #{$facetH};
$hypoLC: math.hypot($lCw,$lCh);
$angleLC: math.atan($lCw/$lCh);
.innerCorner { /* this is the same for outer and inner eye corner */
position: absolute;
height: #{$hypoLC};
--corner: #{math.sin($angle*($i - 1))};
width: 5em;
background: fuchsia;
transform: rotate(calc(-1*#{$angleLC}*var(--tdir))) rotateY(-90deg);
}
}
/* the scary one's teeth*/
@if $i>1 and $i<($ngon/2) {
.slice:nth-child(10) .facet:nth-child(#{$i})::before {
clip-path: polygon(50%*(1 - math.sin($angle*($i - .5))) 50%, 50%*(1+math.sin($angle*($i - 1))) 0, 50%*(1+math.sin($angle*$i)) 100%);
}
.slice:nth-child(13) .facet:nth-child(#{$i})::before {
clip-path: polygon(50%*(1 + math.sin($angle*($i - .5))) 50%, 50%*(1 - math.sin($angle*($i - 1))) 0, 50%*(1 - math.sin($angle*$i)) 100%);
}
:is(.slice:nth-child(10), .slice:nth-child(13)) .facet:nth-child(#{$i}) :is(.lefTooth,.righTooth) {
$lTw: (math.sin($angle*($i - .5)) + math.sin($angle*$i))/2*$sliceW;
$lTh: .5*$facetH;
$hypoLT: math.hypot($lTw,$lTh);
$angleLT: math.atan($lTw/$lTh);
height: #{$hypoLT};
transform: rotateY(-90deg) rotateX(calc(var(--tdir)*#{$angleLT}));
}
.slice:nth-child(10) .facet:nth-child(#{$i}) :is(.lefTooth, .righTooth) {
right: 50%*(1 + math.sin($angle*($i - .5)));
}
.slice:nth-child(13) .facet:nth-child(#{$i}) :is(.lefTooth, .righTooth) {
right: 50%*(1 - math.sin($angle*($i - .5)));
}
}
}
:is(.slice:nth-child(11),.slice:nth-child(12)) .facet:not(:first-child):not(:last-child), /* the evil's mouth */
:is(.slice:nth-child(22), .slice:nth-child(23), .slice:nth-child(24)) .facet,
.slice:nth-child(1), .slice:nth-child(2), .slice:nth-child(3),
.facet:not(:nth-child(3n+4)) .crookeTooth { /* the goof's mouth*/
display: none;
}
.lefTooth, .righTooth {
position: absolute;
width: 5em; /* the thickness of the pumpkin's skull, you can set it to anything within reason */
height: 6em;
filter: brightness(.2);
animation: glow 12s linear infinite;
}
.slice:nth-child(10) .facet div { /* the cutout surfaces on the upper teeth */
animation-delay: -6s;
}
.lefTooth {
background: linear-gradient(#f50,#f90);
bottom: 50%;
transform-origin: bottom right;
}
.righTooth {
background: linear-gradient(#bf0,#0a0);
top: 50%;
transform-origin: top right;
}
/* general styling and shared patterns for the outer corners of the evil one's eyes*/
.outerCorner, .innerCorner {
right: calc(50%*(1 + var(--corner)*var(--anchorDir)));
animation: glow 12s linear infinite;
}
.slice:nth-child(7) .facet div { /* the upper eyelid */
animation-delay: -4.5s; /* .5s per slice, the difference from teeth is 3 slices, ergo 1.5s subtracted from 6s */
}
.slice:nth-child(8) .facet div { /* the upper eyelid */
animation-delay: -7.5s; /* .5s per slice, the difference from teeth is 3 slices, ergo 1.5s subtracted from 6s */
}
.facet:nth-child(4) .outerCorner,
.facet:nth-child(5) .innerCorner {
transform-origin: top right;
}
.facet:nth-child(9) .outerCorner,
.facet:nth-child(8) .innerCorner {
bottom: 0;
transform-origin: bottom right;
}
.slice:nth-child(7) .outerCorner,
.slice:nth-child(8) .innerCorner {
--anchorDir: 1;
}
.slice:nth-child(8) .outerCorner,
.slice:nth-child(7) .innerCorner {
--anchorDir: -1;
}
.slice:nth-child(7) :is(.facet:nth-child(4), .facet:nth-child(8)) > div, /* the top left and bottom right outer corners*/
.slice:nth-child(8) :is(.facet:nth-child(5), .facet:nth-child(9)) > div /* the bottom left and top right inner corners */ {
--tdir: 1;
background: linear-gradient(#bf0,#0a0);
}
.slice:nth-child(8) :is(.facet:nth-child(4), .facet:nth-child(8)) > div, /* the bottom left and top right outer corners*/
.slice:nth-child(7) :is(.facet:nth-child(5), .facet:nth-child(9)) > div /* the top left and bottom right inner corners*/ {
--tdir: -1;
background: linear-gradient(#f50,#f90);
}
/* and last but definitely not least: turn directions for the evil chompers */
.slice:nth-child(10) .lefTooth, .slice:nth-child(13) .righTooth {
--tdir: 1;
}
.slice:nth-child(10) .righTooth, .slice:nth-child(13) .lefTooth {
--tdir: -1;
}
/* mask for the goof's eyeholes */
:is(.slice:nth-child(18),.slice:nth-child(19)) :is(.facet:nth-child(4),.facet:nth-child(5), .facet:nth-child(8), .facet:nth-child(9)) {
mask-image: radial-gradient(circle at var(--hAnchor) var(--vAnchor), transparent #{$sliceW}, black #{$sliceW});
-webkit-mask-image: radial-gradient(circle at var(--hAnchor) var(--vAnchor), transparent #{$sliceW}, black #{$sliceW});
}
.slice:nth-child(18) :is(.facet:nth-child(4),.facet:nth-child(5),.facet:nth-child(8),.facet:nth-child(9)) {
--hAnchor: left;
}
:is(.slice:nth-child(18),.slice:nth-child(19)) :is(.facet:nth-child(4),.facet:nth-child(8)) {
--vAnchor: bottom;
}
.slice:nth-child(19) :is(.facet:nth-child(4),.facet:nth-child(5),.facet:nth-child(8),.facet:nth-child(9)) {
--hAnchor: right;
}
:is(.slice:nth-child(18),.slice:nth-child(19)) :is(.facet:nth-child(5),.facet:nth-child(9)) {
--vAnchor: top;
}
@keyframes lighting {
0% {
background-position-x: calc(.5*(var(--nthSlice)*100em*#{math.sin($angle/2)} - 100em*#{math.sin($angle/2)}*var(--latter)));
}
100% {
background-position-x: calc(.5*(100em*#{math.sin($angle/2)}*#{$ngon} + var(--nthSlice)*100em*#{math.sin($angle/2)} - 100em*#{math.sin($angle/2)}*var(--latter)));
}
}
@keyframes spin {
0% {
transform: rotate(-90deg) rotateY(130deg); /* 130 */
}
100% {
transform: rotate(-90deg) rotateY(-230deg);
}
}
@keyframes glow {
0%, 66.66%, 100% {
filter: brightness(.2);
}
33.33% {
filter: brightness(1);
}
}
@keyframes counterspin {
0% {
transform: translateZ(15em) rotateY(-130deg) scale(.4) rotate(0);
}
100% {
transform: translateZ(15em) rotateY(230deg) scale(.4) rotate(1440deg);
}
}
@keyframes flatColor {
0%, 100% {
background-color: #050;
}
25%, 75% {
background-color: #080400;
}
50% {
background-color: #f60;
}
}
@keyframes looseTeeth {
0% {
transform: skewY(15deg) rotate(var(--turnTooth));
}
100% {
transform: skewY(-15deg) rotate(var(--turnTooth));
}
}
@keyframes eyeRoll {
0% {
transform: rotateY(90deg) translateZ(-15em) rotate(0deg);
}
100% {
transform: rotateY(90deg) translateZ(-15em) rotate(360deg);
}
}
@keyframes rollGlow {
0% {
background-position-y: calc(-1*var(--lazyStep)*var(--lazyH) + .5*var(--lazyH));
}
100% {
background-position-y: calc(-1*var(--lazyStep)*var(--lazyH) + .5*var(--lazyH) + 12*var(--lazyH));
}
}
/* the small pumpkin inside the goof's mouth */
.lilPump {
position: absolute;
display: flex;
place-self: center;
justify-content: center;
width: 100%;
aspect-ratio: 1;
perspective: 10em;
border-radius: 50%;
overflow: hidden;
background: radial-gradient(#000 8%,#0000 8%), conic-gradient(#110800,#630,#110800,#040,#110800);
animation: counterspin 12s linear infinite;
}
.lilPump:after {
content: '';
position: absolute;
inset: 0;
border-radius: 50%;
/* box-shadow: inset -15em -15em 15em #000d; */
}
.lilPump > div {
position: absolute;
--ngon: #{$ngon/2};
--lilW: calc(#{math.tan($angle*2)}*50em);
width: var(--lilW);
height: 50em;
bottom: 50%;
transform-style: preserve-3d;
}
@for $i from 1 through $ngon/2 {
.lilPump > div > div:nth-child(#{$i}) {
--lilStep: #{$i + 1};
}
}
.lilPump > div > div {
content: '';
position: absolute;
inset: 0;
transform-origin: 50% 100%;
transform: rotate(calc(var(--lilStep)*-2*var(--offset)));
}
.lilPump .segments div {
transform: rotate(calc(var(--lilStep)*-2*var(--offset))) rotateX(90deg) translateZ(50em);
background-image: repeating-linear-gradient(90deg, #050,#080400,#f60,#080400,#050);
background-size: calc(var(--lilW)*var(--ngon)/2) 100%;
animation: whee 4s linear infinite;
}
.lilPump .holes div:nth-child(2), .lilPump .holes div:nth-child(4) {
background-image: radial-gradient(at 52% 57%, black 5em, transparent 4em), repeating-conic-gradient(#630,#010,#630 50%);
mask-image: radial-gradient(at 52% 52%, black 6em, transparent 4em);
-webkit-mask-image: radial-gradient(at 52% 52%, black 6em, transparent 4em);
}
.lilPump .holes div:nth-child(n+6) {
clip-path: polygon(0 0,100% 0, 50% 100%);
background-image: radial-gradient(circle at 52% 37%, black 9em, transparent 9em), conic-gradient(from -85deg at 52% 35%, #040 ,#210 170deg 210deg,#010 300deg, #040);
mask-image: radial-gradient(circle at 52% 35%, black 11em, transparent 11em);
-webkit-mask-image: radial-gradient(circle at 52% 35%, black 11em, transparent 11em);
}
.lilPump .holes div:nth-child(6) {
clip-path: polygon(0 0,100% 0, 100% 100%, 50% 100%);
}
.lilPump .holes div:nth-child(12) {
clip-path: polygon(0 0,100% 0, 50% 100%, 0 100%);
}
@keyframes rotfl {
0% {
rotate: 0;
}
100% {
rotate: 360deg;
}
}
@keyframes whee {
0% {
background-position-x: calc(var(--lilW)*var(--lilStep)/2);
}
100% {
background-position-x: calc((var(--lilW)*var(--lilStep) - var(--lilW)*var(--ngon))/2);
}
}
input {
display: none;
}
.startBtn {
display: flex;
height: 15em;
aspect-ratio: 1;
justify-self: center;
z-index: 1;
position: absolute;
bottom: calc(50% + 5em);
background-image: conic-gradient(from 90deg, #210 calc(180deg - #{math.atan(1)}), #bf0 calc(180deg - #{math.atan(1)}), #0a0 calc(180deg + #{math.atan(1)}), #f50 calc(180deg - #{math.atan(1)}), #f90);
mask-image: conic-gradient(from calc(270deg - #{math.atan(.5)}) at 100% 50%, black calc(2*#{math.atan(1)}), transparent calc(2*#{math.atan(1)}));
-webkit-mask-image: conic-gradient(from calc(270deg - #{math.atan(.5)}) at 100% 50%, black calc(2*#{math.atan(.5)}), transparent calc(2*#{math.atan(.5)}));
animation: wiggle 5s linear infinite;
transition: all .3s ease-in-out;
}
@keyframes wiggle {
0%, 5%, 15%, 100% {
transform: skewY(0);
}
2.5%, 12.5% {
transform: skewY(-10deg);
}
7.5% {
transform: skewY(10deg);
}
}
.startBtn::before {
content: '';
position: absolute;
inset: 0;
background: #000;
scale: .666;
mask-image: conic-gradient(from calc(270deg - #{math.atan(.5)}) at 100% 50%, black calc(2*#{math.atan(.5)}), transparent calc(2*#{math.atan(.5)}) );
-webkit-mask-image: conic-gradient(from calc(270deg - #{math.atan(.5)}) at 100% 50%, black calc(2*#{math.atan(.5)}), transparent calc(2*#{math.atan(.5)}) );
transition: inherit;
}
.startBtn:hover::before {
transform: scale(.666);
}
#start:checked ~ :is(.startBtn,.CTA) {
display: none;
}
#start:checked ~ .playBtn {
display: flex;
}
.playBtn {
display: none;
width: 20em;
aspect-ratio: 1;
margin: 5em;
background: linear-gradient(#f60,#050);
position: absolute;
transition: .3s ease-in-out;
/* justify-content:space-around; */ /* probably redundant, scaling doesn't look at layout, transforms from the middle by default, so it will be centered regardless*/
}
.playBtn:hover {
filter: hue-rotate(150deg) brightness(1.5);
}
.playBtn div {
position: relative;
width: 50%;
height: 100%;
padding: 1px;
margin: -1px;
perspective: 0;
perspective-origin: calc(50% + 0%*var(--dir)) 0;
transform: scale(.5);
transition: .3s ease-in-out;
transform-origin: calc(50% - 31%*var(--dir)) 50%;
}
.playBtn div:nth-child(1) {
--dir: -1;
}
.playBtn div:nth-child(2) {
--dir: 1;
}
.playBtn div:before {
content: '';
position: absolute;
width: 100%;
height: 100%;
background: #000;
rotate: x 0deg;
transform-origin: 50% 100%;
transition: .3s ease-in-out;
}
#playPause:checked ~ .playBtn {
rotate: -90deg;
}
#playPause:checked ~ .playBtn div {
perspective-origin: calc(50% + 50%*var(--dir)) 0;
transform: scale(1);
}
#playPause:checked ~ .playBtn div:before {
rotate: x 6deg;
}
#start:not(:checked) ~ .scene *,
#start:not(:checked) ~ .scene *::before,
#start:not(:checked) ~ .scene *::after,
#playPause:checked ~ .scene *,
#playPause:checked ~ .scene *::before,
#playPause:checked ~ .scene *::after {
animation-play-state: paused;
}
@media (orientation: portrait) {
body {
font-size: .66vw;
}
.CTA {
width: 100%;
font-size: 4.9vw;
}
.CTA span {
display: block;
margin-inline: 1em;
}
.playBtn {
margin: 10em;
width: 30em;
}
@keyframes wiggle {
0%, 5%, 15%, 100% {
transform: skewY(0);
}
2.5%, 12.5% {
transform: skewY(-20deg);
}
7.5% {
transform: skewY(20deg);
}
}
}
View Compiled
/*
So there it is: this new pumpkin I've been slowly growing and carving throughout the year. Didn't make it for Halloween, got slowed down by some failed experiments in faux shading. I had some crazy ideas, but they were either too CPU-heavy or glitchy, so I went back to my original concept of linear-gradient based shading.
New things I learned: the :is pseudo-class - which I finally figured out, after some failed attempts in the past. Also, the :empty pseudo-class - which KP did a tutorial about just as I was looking for some way to target empty and non-empty divs. And last but not least - some more experiments with masks, which I used instead of clip-paths on the whole goofy face.
Things I need to work on: naming classes and variables. I like camel-case, but out of fear of going into full JS-style frenzy I often mix in acronyms. Idea to try out next time: concatenating several classes.
And while I still try to avoid JS - purely out of spite - I do love Pug and SCSS very much.
I discuss the construction of the sphere for this pumpkin on my blog at:
https://dev.to/mackfitz/approximating-a-sphere-using-css-and-pumpkins-1dle
https://mackfitz.hashnode.dev/approximating-a-sphere-using-css-and-pumpkins
*/
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.