diff --git a/.gitignore b/.gitignore
index 16d54bb..9711bba 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,3 +22,11 @@ pnpm-debug.log*
# jetbrains setting folder
.idea/
+
+# astro setting folder
+.astro/
+
+# Lock file
+package-lock.json
+yarn.lock
+pnpm-lock.yaml
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..dd9bfb5
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,9 @@
+{
+ "workbench.colorCustomizations": {
+ "titleBar.activeForeground": "#283a22",
+ "titleBar.inactiveForeground": "#82bf72",
+ "titleBar.activeBackground": "#77bb65",
+ "titleBar.inactiveBackground": "#49713e"
+ },
+ "editor.tabSize": 4
+}
diff --git a/package.json b/package.json
index fb661f9..8c78426 100644
--- a/package.json
+++ b/package.json
@@ -11,38 +11,38 @@
},
"dependencies": {
"@astrojs/check": "^0.9.4",
- "@astrojs/react": "4.1.1",
- "@astrojs/starlight": "^0.30.3",
- "@astrojs/tailwind": "5.1.3",
+ "@astrojs/react": "4.1.5",
+ "@astrojs/starlight": "^0.31.1",
+ "@astrojs/tailwind": "5.1.4",
"@heroicons/react": "^2.2.0",
"@splinetool/react-spline": "^4.0.0",
- "@splinetool/runtime": "^1.9.48",
+ "@splinetool/runtime": "^1.9.59",
"@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1",
- "astro": "5.1.0",
+ "astro": "5.1.7",
"clsx": "^2.1.1",
- "motion": "^11.12.0",
+ "motion": "^11.18.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
- "tailwindcss": "^3.4.15",
- "typescript": "^5.7.2"
+ "tailwindcss": "^3.4.17",
+ "typescript": "^5.7.3"
},
"devDependencies": {
- "@eslint/js": "^9.15.0",
+ "@eslint/js": "^9.18.0",
"@phosphor-icons/react": "^2.1.7",
- "@typescript-eslint/parser": "^8.16.0",
- "eslint": "^9.15.0",
- "eslint-config-prettier": "^9.1.0",
+ "@typescript-eslint/parser": "^8.20.0",
+ "eslint": "^9.18.0",
+ "eslint-config-prettier": "^10.0.1",
"eslint-plugin-astro": "^1.3.1",
"eslint-plugin-jsx-a11y": "^6.10.2",
- "eslint-plugin-prettier": "^5.2.1",
- "eslint-plugin-react": "^7.37.2",
- "eslint-plugin-react-hooks": "^5.0.0",
- "eslint-plugin-tailwindcss": "^3.17.5",
- "prettier": "^3.4.1",
+ "eslint-plugin-prettier": "^5.2.3",
+ "eslint-plugin-react": "^7.37.4",
+ "eslint-plugin-react-hooks": "^5.1.0",
+ "eslint-plugin-tailwindcss": "^3.18.0",
+ "prettier": "^3.4.2",
"prettier-plugin-astro": "^0.14.1",
- "sass": "^1.81.0",
- "tailwind-merge": "^2.5.5",
- "typescript-eslint": "^8.16.0"
+ "sass": "^1.83.4",
+ "tailwind-merge": "^2.6.0",
+ "typescript-eslint": "^8.20.0"
}
}
diff --git a/src/components/Animations/BackrollCubes/BackrollCubes.module.scss b/src/components/Animations/BackrollCubes/BackrollCubes.module.scss
new file mode 100644
index 0000000..8f85793
--- /dev/null
+++ b/src/components/Animations/BackrollCubes/BackrollCubes.module.scss
@@ -0,0 +1,138 @@
+$grid-size: 5; // sqrt of number of cubes
+$cube-size: 4em; // cube edge length
+$faces: 6; // number of cube faces
+$edges: 4; // number of square edges
+$color: #ddd;
+$brightness-percent: 10%;
+$gap: 0.25 * $cube-size;
+$middle: 0.5 * ($grid-size - 1);
+$distance: $cube-size + $gap;
+$animation-distance: 3 * $distance;
+$edge-angle: 360deg/$edges;
+$duration: 2.8s;
+$breakpoint: 50%/$grid-size;
+
+@mixin background($color, $angle, $percent) {
+ background: darken($color, (1 - cos($angle)) * $percent);
+}
+
+@for $i from 0 through $grid-size {
+ .animate-backrollcubes-cube:nth-child(n) {
+ &[data-movement="#{$i}"] {
+ animation-name: animate-backrollcubes-movement-#{$i};
+
+ div:before {
+ animation-name: animate-backrollcubes-fade-#{$i};
+ }
+ }
+ }
+
+ @keyframes animate-backrollcubes-movement-#{$i} {
+ #{$i * $breakpoint} {
+ transform: none;
+ }
+ #{($i + 2) * $breakpoint}, 100% {
+ transform: translateZ(-$animation-distance) rotateX($edge-angle);
+ }
+ }
+
+ @keyframes animate-backrollcubes-fade-#{$i} {
+ #{$i * $breakpoint} {
+ opacity: 0.999;
+ }
+ #{($i + 2) * $breakpoint}, 100% {
+ opacity: 0.001;
+ }
+ }
+}
+
+.animate-backrollcubes-assembly {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform-style: preserve-3d;
+ animation: animate-backrollcubes-assembly $duration linear infinite;
+}
+
+@keyframes animate-backrollcubes-assembly {
+ to {
+ transform: translateZ($animation-distance);
+ }
+}
+
+.animate-backrollcubes-cube {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform-style: preserve-3d;
+ animation: animate-backrollcubes-phase $duration cubic-bezier(0.65, 0.05, 0.35, 1) infinite;
+
+ @for $i from 0 to $grid-size {
+ $y: ($i - $middle) * $distance;
+ $q1: abs($i - $middle);
+
+ @for $j from 0 to $grid-size {
+ $x: ($j - $middle) * $distance;
+ $idx: $i * $grid-size + $j + 1;
+ $q2: abs($j - $middle);
+ $s: $q1 + $q2 + max(0, max($q1, $q2) - 1);
+
+ &:nth-child(#{$idx}) {
+ margin: $y $x;
+ animation-name: animate-backrollcubes-movement-#{$s};
+
+ div:before {
+ animation-name: animate-backrollcubes-fade-#{$s};
+ }
+ }
+ }
+ }
+}
+
+.animate-backrollcubes-cube__face {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ margin: -0.5 * $cube-size;
+ width: $cube-size;
+ height: $cube-size;
+ backface-visibility: hidden;
+
+ &:nth-child(-n + 4):before {
+ position: absolute;
+ width: inherit;
+ height: inherit;
+ animation: animate-backrollcubes-phase $duration ease-in-out infinite;
+ content: '';
+ }
+
+ &:nth-child(n + 5) {
+ background: darken($color, 3 * $brightness-percent);
+ }
+
+ @for $i from 0 to $faces {
+ $angle: $i * $edge-angle;
+ $prev-angle: ($i - 1) * $edge-angle;
+
+ &:nth-child(#{$i + 1}) {
+ transform: if($i < $edges,
+ rotateX($angle),
+ rotateY(pow(-1, $i) * $edge-angle)
+ ) translateZ(0.5 * $cube-size);
+
+ @if $i < $edges {
+ @include background($color, $angle, $brightness-percent);
+
+ &:before {
+ @include background($color, $prev-angle, $brightness-percent);
+ }
+ }
+ }
+ }
+}
+
+@keyframes animate-backrollcubes-phase {
+ from, to {
+ transform: none;
+ }
+}
diff --git a/src/components/Animations/BackrollCubes/BackrollCubes.tsx b/src/components/Animations/BackrollCubes/BackrollCubes.tsx
new file mode 100644
index 0000000..99bab5e
--- /dev/null
+++ b/src/components/Animations/BackrollCubes/BackrollCubes.tsx
@@ -0,0 +1,15 @@
+import styles from './BackrollCubes.module.scss';
+
+export const BackrollCubes = () => {
+ return (
+
+ {Array.from({ length: 25 }, (_, i) => (
+
+ {Array.from({ length: 6 }, (_, j) => (
+
+ ))}
+
+ ))}
+
+ );
+};
diff --git a/src/components/Animations/CubeAssembly/CubeAssembly.module.scss b/src/components/Animations/CubeAssembly/CubeAssembly.module.scss
new file mode 100644
index 0000000..5c71d1e
--- /dev/null
+++ b/src/components/Animations/CubeAssembly/CubeAssembly.module.scss
@@ -0,0 +1,102 @@
+$palette: #1c6c3e #dd7ca9; // Using our theme colors
+$n-items: 6;
+$edge-length: 3.125em;
+$inradius: .5 * $edge-length;
+$s: .2; // minimum scale factor
+$t: .6s; // animation duration
+$p: 7%; // difference between face shades
+
+@mixin c($c, $p: $p) {
+ color: lighten(saturate($c, $p), $p);
+}
+
+.wrapper {
+ width: 100%;
+ height: 100%;
+ position: relative;
+}
+
+.assembly {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform-style: preserve-3d;
+ transform: rotateX(-35deg) rotateY(-45deg) translateZ($inradius);
+}
+
+.item {
+ position: absolute;
+ margin: -$inradius;
+ width: $edge-length;
+ height: $edge-length;
+ transform-style: preserve-3d;
+ transform-origin: 50% 50% (-1 * $inradius);
+ animation: s $t cubic-bezier(.45, .03, .51, .95) infinite alternate;
+
+ // Front face
+ background: currentColor;
+ box-shadow: 0 0 .125em currentColor;
+ transform-style: preserve-3d;
+
+ // Top face
+ &:before {
+ position: absolute;
+ content: '';
+ width: 100%;
+ height: $edge-length;
+ background: currentColor;
+ transform: rotateX(90deg) translateY(-100%);
+ transform-origin: top;
+ box-shadow: inherit;
+ }
+
+ // Left side face
+ &:after {
+ position: absolute;
+ content: '';
+ width: $edge-length;
+ height: 100%;
+ background: currentColor;
+ transform: rotateY(-90deg) translateX(-100%);
+ transform-origin: left;
+ box-shadow: inherit;
+ }
+
+ // Right side face
+ div {
+ position: absolute;
+ width: $edge-length;
+ height: 100%;
+ background: currentColor;
+ transform: rotateY(90deg) translateX(100%);
+ transform-origin: right;
+ box-shadow: inherit;
+ }
+
+ @for $i from 0 through ($n-items - 1) {
+ $n: $n-items - 1;
+ $c: mix(nth($palette, 1), nth($palette, 2), $i/$n * 100%);
+
+ &:nth-child(#{$i + 1}) {
+ margin-top: (.5 * $n - $i - .5) * $edge-length;
+ color: $c;
+ animation-delay: ($i/$n-items - 1) * 2 * $t;
+
+ &:before {
+ @include c($c, 2 * $p);
+ }
+ &:after {
+ @include c($c);
+ filter: brightness(0.85);
+ }
+ div {
+ @include c($c);
+ filter: brightness(0.7);
+ }
+ }
+ }
+}
+
+@keyframes s {
+ to { transform: scale3d($s, $s, $s); }
+}
diff --git a/src/components/Animations/CubeAssembly/CubeAssembly.tsx b/src/components/Animations/CubeAssembly/CubeAssembly.tsx
new file mode 100644
index 0000000..baac425
--- /dev/null
+++ b/src/components/Animations/CubeAssembly/CubeAssembly.tsx
@@ -0,0 +1,15 @@
+import styles from './CubeAssembly.module.scss';
+
+export const CubeAssembly = () => {
+ return (
+
+
+ {Array.from({ length: 6 }, (_, index) => (
+
+ ))}
+
+
+ );
+};
diff --git a/src/components/Animations/CubeDots/CubeDots.module.scss b/src/components/Animations/CubeDots/CubeDots.module.scss
new file mode 100644
index 0000000..7257051
--- /dev/null
+++ b/src/components/Animations/CubeDots/CubeDots.module.scss
@@ -0,0 +1,173 @@
+.animate-cubedots-wrapper {
+ display: flex;
+ width: 100vw;
+ height: 100vh;
+ margin: 0;
+ padding: 0;
+ align-items: center;
+ justify-content: center;
+
+ &__content {
+ height: 450px;
+ width: 280px;
+ position: relative;
+ }
+}
+
+@keyframes animate-cubedots-rotation {
+ 0% {
+ transform: rotateZ(0deg) rotateX(-15deg) rotateY(0deg);
+ }
+ 100% {
+ transform: rotateZ(0deg) rotateX(-15deg) rotateY(-360deg);
+ }
+}
+
+@keyframes animate-cubedots-color-front {
+ 0%, 100% {
+ opacity: 1;
+ }
+ 25%, 75% {
+ opacity: 0;
+ }
+}
+
+@keyframes animate-cubedots-color-back {
+ 0%, 100%, 25%, 75% {
+ opacity: 0;
+ }
+ 50% {
+ opacity: 1;
+ }
+}
+
+@keyframes animate-cubedots-color-right {
+ 0%, 50% {
+ opacity: 0.25;
+ }
+ 50% {
+ opacity: 0;
+ }
+ 25% {
+ opacity: 1;
+ }
+}
+
+@keyframes animate-cubedots-color-left {
+ 0%, 50%, 100% {
+ opacity: 0;
+ }
+ 75% {
+ opacity: 1;
+ }
+}
+
+.animate-cubedots-cube {
+ --animate-cubedots-z-angle: 100px;
+ --animate-cubedots-color: #06FFFF;
+ --animate-cubedots-time: 5s;
+
+ width: 203px;
+ height: 203px;
+ position: relative;
+ transform-style: preserve-3d;
+ transform-origin: center center;
+ transform: rotateZ(0deg) rotateX(0deg) rotateY(0deg);
+ animation: animate-cubedots-rotation var(--animate-cubedots-time) infinite linear;
+}
+
+.animate-cubedots-side {
+ position: absolute;
+ width: 203px;
+ height: 203px;
+ opacity: 0;
+
+ &:after {
+ content: '';
+ left: 0;
+ top: 0;
+ width: 3px;
+ height: 3px;
+ background: var(--animate-cubedots-color);
+ position: absolute;
+ border-radius: 100%;
+ box-shadow:
+ 0px 40px 0px 0px var(--animate-cubedots-color),
+ 0px 80px 0px 0px var(--animate-cubedots-color),
+ 0px 120px 0px 0px var(--animate-cubedots-color),
+ 0px 160px 0px 0px var(--animate-cubedots-color),
+ 0px 200px 0px 0px var(--animate-cubedots-color),
+ 40px 0px 0px 0px var(--animate-cubedots-color),
+ 80px 0px 0px 0px var(--animate-cubedots-color),
+ 120px 0px 0px 0px var(--animate-cubedots-color),
+ 160px 0px 0px 0px var(--animate-cubedots-color),
+ 200px 0px 0px 0px var(--animate-cubedots-color),
+ 40px 40px 0px 0px var(--animate-cubedots-color),
+ 80px 40px 0px 0px var(--animate-cubedots-color),
+ 120px 40px 0px 0px var(--animate-cubedots-color),
+ 160px 40px 0px 0px var(--animate-cubedots-color),
+ 200px 40px 0px 0px var(--animate-cubedots-color),
+ 40px 80px 0px 0px var(--animate-cubedots-color),
+ 80px 80px 0px 0px var(--animate-cubedots-color),
+ 120px 80px 0px 0px var(--animate-cubedots-color),
+ 160px 80px 0px 0px var(--animate-cubedots-color),
+ 200px 80px 0px 0px var(--animate-cubedots-color),
+ 40px 120px 0px 0px var(--animate-cubedots-color),
+ 80px 120px 0px 0px var(--animate-cubedots-color),
+ 120px 120px 0px 0px var(--animate-cubedots-color),
+ 160px 120px 0px 0px var(--animate-cubedots-color),
+ 200px 120px 0px 0px var(--animate-cubedots-color),
+ 40px 160px 0px 0px var(--animate-cubedots-color),
+ 80px 160px 0px 0px var(--animate-cubedots-color),
+ 120px 160px 0px 0px var(--animate-cubedots-color),
+ 160px 160px 0px 0px var(--animate-cubedots-color),
+ 200px 160px 0px 0px var(--animate-cubedots-color),
+ 40px 200px 0px 0px var(--animate-cubedots-color),
+ 80px 200px 0px 0px var(--animate-cubedots-color),
+ 120px 200px 0px 0px var(--animate-cubedots-color),
+ 160px 200px 0px 0px var(--animate-cubedots-color),
+ 200px 200px 0px 0px var(--animate-cubedots-color);
+ }
+
+ &--front {
+ animation: animate-cubedots-color-front var(--animate-cubedots-time) infinite linear;
+ transform: rotateY(0deg) translateZ(var(--animate-cubedots-z-angle));
+ }
+
+ &--right {
+ transform: rotateY(90deg) translateZ(var(--animate-cubedots-z-angle));
+ animation: animate-cubedots-color-right var(--animate-cubedots-time) infinite linear;
+ }
+
+ &--back {
+ transform: rotateY(180deg) translateZ(var(--animate-cubedots-z-angle));
+ animation: animate-cubedots-color-back var(--animate-cubedots-time) infinite linear;
+ }
+
+ &--left {
+ transform: rotateY(-90deg) translateZ(var(--animate-cubedots-z-angle));
+ animation: animate-cubedots-color-left var(--animate-cubedots-time) infinite linear;
+ }
+
+ &--top {
+ opacity: 1;
+ transform: rotateX(90deg) translateZ(var(--animate-cubedots-z-angle));
+ }
+
+ &--bottom {
+ opacity: 0;
+ }
+}
+
+.animate-cubedots-shadow {
+ position: absolute;
+ top: 258px;
+ width: 203px;
+ height: 203px;
+ transform-style: preserve-3d;
+ transform: rotateX(90deg);
+ filter: blur(8px);
+ opacity: 0.2;
+ background: var(--animate-cubedots-color);
+ animation: animate-cubedots-rotation var(--animate-cubedots-time) infinite linear;
+}
diff --git a/src/components/Animations/CubeDots/CubeDots.tsx b/src/components/Animations/CubeDots/CubeDots.tsx
new file mode 100644
index 0000000..a1134a9
--- /dev/null
+++ b/src/components/Animations/CubeDots/CubeDots.tsx
@@ -0,0 +1,19 @@
+import styles from './CubeDots.module.scss';
+
+export const CubeDots = () => {
+ return (
+
+ );
+};
diff --git a/src/components/Animations/CubeExpand/CubeExpand.module.scss b/src/components/Animations/CubeExpand/CubeExpand.module.scss
new file mode 100644
index 0000000..a183e00
--- /dev/null
+++ b/src/components/Animations/CubeExpand/CubeExpand.module.scss
@@ -0,0 +1,140 @@
+:root {
+ --animate-cubeexpand-size: 2em;
+ --animate-cubeexpand-multiplier: 5;
+ --animate-cubeexpand-duration: 0.5s;
+ --animate-cubeexpand-color1: #ffffff;
+ --animate-cubeexpand-color2: #ff3366;
+ --animate-cubeexpand-color3: #ffcc33;
+ --animate-cubeexpand-color4: #ccff33;
+}
+
+.animate-cubeexpand-wrapper {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform-style: preserve-3d;
+ transform: rotateX(-35deg) rotateY(-45deg);
+}
+
+.animate-cubeexpand-group {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform-style: preserve-3d;
+
+ $rotation-chain: ();
+ $reverse-chain: ();
+
+ @for $i from 0 through 5 {
+ $j: $i % 2;
+
+ &:nth-child(#{$i + 1}) {
+ @if $i > 0 {
+ $rotation: rotate3d(0, #{1 - $j}, $j, #{pow(-1, $j + 1) * 90}deg);
+ $rotation-chain: $rotation-chain $rotation;
+ $reverse-chain: rotate3d(0, #{1 - $j}, $j, #{pow(-1, $j) * 90}deg) $reverse-chain;
+ }
+
+ z-index: $j;
+ transform: $rotation-chain translate3d(
+ 0,
+ calc(-0.5 * (var(--animate-cubeexpand-multiplier) + 1) * var(--animate-cubeexpand-size)),
+ calc(-0.5 * (var(--animate-cubeexpand-multiplier) + 1) * var(--animate-cubeexpand-size))
+ );
+
+ > .animate-cubeexpand-scale3d {
+ transform: translateX(calc(-0.5 * (var(--animate-cubeexpand-multiplier) + 1) * var(--animate-cubeexpand-size)));
+
+ .animate-cubeexpand-face {
+ box-shadow: 0 0 1px currentColor;
+ }
+ }
+ }
+ }
+
+ &:nth-child(3n + 1) .animate-cubeexpand-expand { color: var(--animate-cubeexpand-color2); }
+ &:nth-child(3n + 2) .animate-cubeexpand-expand { color: var(--animate-cubeexpand-color3); }
+ &:nth-child(3n + 3) .animate-cubeexpand-expand { color: var(--animate-cubeexpand-color4); }
+}
+
+.animate-cubeexpand-expand {
+ transform: scaleX(var(--animate-cubeexpand-multiplier));
+ transform-style: preserve-3d;
+}
+
+.animate-cubeexpand-scale3d {
+ position: absolute;
+ transform-style: preserve-3d;
+
+ .animate-cubeexpand-expand & {
+ transform-origin: calc(var(--animate-cubeexpand-size) * -0.5);
+ animation:
+ animate-cubeexpand-scale var(--animate-cubeexpand-duration) ease-in-out infinite alternate,
+ animate-cubeexpand-origin calc(var(--animate-cubeexpand-duration) * 2) steps(1) infinite;
+ }
+
+ .animate-cubeexpand-group:nth-child(2n) .animate-cubeexpand-expand & {
+ animation-delay: calc(var(--animate-cubeexpand-duration) * -1);
+ }
+}
+
+.animate-cubeexpand-reverse {
+ position: absolute;
+ transform-style: preserve-3d;
+}
+
+.animate-cubeexpand-face {
+ position: absolute;
+ margin: calc(var(--animate-cubeexpand-size) * -0.5);
+ width: var(--animate-cubeexpand-size);
+ height: var(--animate-cubeexpand-size);
+ background: currentColor;
+ transform-style: preserve-3d;
+ backface-visibility: visible;
+
+ // All six faces of the cube
+ &:nth-child(1) {
+ transform: translateZ(calc(var(--animate-cubeexpand-size) * 0.5));
+ filter: brightness(1);
+ }
+ &:nth-child(2) {
+ transform: rotateY(180deg) translateZ(calc(var(--animate-cubeexpand-size) * 0.5));
+ filter: brightness(0.8);
+ }
+ &:nth-child(3) {
+ transform: rotateY(90deg) translateZ(calc(var(--animate-cubeexpand-size) * 0.5));
+ filter: brightness(0.9);
+ }
+ &:nth-child(4) {
+ transform: rotateY(-90deg) translateZ(calc(var(--animate-cubeexpand-size) * 0.5));
+ filter: brightness(0.7);
+ }
+ &:nth-child(5) {
+ transform: rotateX(90deg) translateZ(calc(var(--animate-cubeexpand-size) * 0.5));
+ filter: brightness(0.95);
+ }
+ &:nth-child(6) {
+ transform: rotateX(-90deg) translateZ(calc(var(--animate-cubeexpand-size) * 0.5));
+ filter: brightness(0.85);
+ }
+}
+
+@keyframes animate-cubeexpand-scale {
+ 0% {
+ transform: scale3d(0, 1, 1);
+ }
+ 100% {
+ transform: scale3d(1, 1, 1);
+ }
+}
+
+@keyframes animate-cubeexpand-origin {
+ 50% {
+ transform-origin: calc(var(--animate-cubeexpand-size) * 0.5);
+ }
+}
+
+:global(body) {
+ background: #000;
+ overflow: hidden;
+}
diff --git a/src/components/Animations/CubeExpand/CubeExpand.tsx b/src/components/Animations/CubeExpand/CubeExpand.tsx
new file mode 100644
index 0000000..74af7dd
--- /dev/null
+++ b/src/components/Animations/CubeExpand/CubeExpand.tsx
@@ -0,0 +1,26 @@
+import styles from './CubeExpand.module.scss';
+
+const Block = () => (
+
+
+ {Array.from({ length: 6 }, (_, i) => (
+
+ ))}
+
+
+);
+
+export const CubeExpand = () => {
+ return (
+
+ {Array.from({ length: 6 }, (_, i) => (
+
+ ))}
+
+ );
+};
diff --git a/src/components/Animations/CubeFrame/CubeFrame.module.scss b/src/components/Animations/CubeFrame/CubeFrame.module.scss
new file mode 100644
index 0000000..bea3e74
--- /dev/null
+++ b/src/components/Animations/CubeFrame/CubeFrame.module.scss
@@ -0,0 +1,89 @@
+$n: 7;
+$s: 5rem;
+
+.animate-cubeframe-cubes {
+ transform: rotateX(55deg) rotateZ(-45deg);
+ position: absolute;
+ width: $s;
+ height: $s;
+ transform-style: preserve-3d;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.animate-cubeframe-cube {
+ position: absolute;
+ width: $s;
+ height: $s;
+ transform-style: preserve-3d;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.animate-cubeframe-x,
+.animate-cubeframe-y,
+.animate-cubeframe-z {
+ position: absolute;
+ width: $s;
+ height: $s;
+ transform-style: preserve-3d;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+
+ &:before,
+ &:after {
+ content: '';
+ position: absolute;
+ width: $s;
+ height: $s;
+ border: 1px solid white;
+ box-shadow: inset 0 0 3px 0 white;
+ background-color: #272727;
+ }
+}
+
+.animate-cubeframe-x {
+ transform: rotateX(90deg);
+ &:before { transform: translateZ($s / 2); }
+ &:after { transform: translateZ(-$s / 2); }
+}
+
+.animate-cubeframe-y {
+ transform: rotateY(90deg);
+ &:before { transform: translateZ($s / 2); }
+ &:after { transform: translateZ(-$s / 2); }
+}
+
+.animate-cubeframe-z {
+ transform: rotateZ(90deg);
+ &:before { transform: translateZ($s / 2); }
+ &:after { transform: translateZ(-$s / 2); }
+}
+
+@for $i from 0 through $n - 1 {
+ .animate-cubeframe-cube:nth-child(#{$i + 1}) {
+ --animate-cubeframe-delay: #{$i * 0.15}s;
+ animation: animate-cubeframe-spin 3s var(--animate-cubeframe-delay) cubic-bezier(1, -0.50, 0.50, 1.25) infinite;
+
+ .animate-cubeframe-x,
+ .animate-cubeframe-y,
+ .animate-cubeframe-z {
+ &:before {
+ transform: translateZ((($s / 3) * $i) + ($s / 2));
+ }
+
+ &:after {
+ transform: translateZ(((-$s / 3) * $i) - ($s / 2));
+ }
+ }
+ }
+}
+
+@keyframes animate-cubeframe-spin {
+ 50%, 100% {
+ transform: rotateZ(-90deg);
+ }
+}
diff --git a/src/components/Animations/CubeFrame/CubeFrame.tsx b/src/components/Animations/CubeFrame/CubeFrame.tsx
new file mode 100644
index 0000000..4f0622a
--- /dev/null
+++ b/src/components/Animations/CubeFrame/CubeFrame.tsx
@@ -0,0 +1,15 @@
+import styles from './CubeFrame.module.scss';
+
+export const CubeFrame = () => {
+ return (
+
+ {Array.from({ length: 7 }, (_, i) => (
+
+ ))}
+
+ );
+};
diff --git a/src/components/Animations/CubeGradient/CubeGradient.module.scss b/src/components/Animations/CubeGradient/CubeGradient.module.scss
new file mode 100644
index 0000000..b334638
--- /dev/null
+++ b/src/components/Animations/CubeGradient/CubeGradient.module.scss
@@ -0,0 +1,60 @@
+.animate-cubegradient-cube {
+ position: relative;
+ width: 300px;
+ height: 300px;
+ transform-style: preserve-3d;
+ animation: animate-cubegradient-animate 4s linear infinite;
+ margin: 0 auto;
+
+ @keyframes animate-cubegradient-animate {
+ 0% {
+ transform: rotateX(-30deg) rotateY(0deg);
+ }
+ 100% {
+ transform: rotateX(-30deg) rotateY(360deg);
+ }
+ }
+}
+
+.animate-cubegradient-sides {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ transform-style: preserve-3d;
+}
+
+.animate-cubegradient-side {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: linear-gradient(#212121, #1c6c3e);
+ transform: rotateY(calc(90deg * var(--animate-cubegradient-i))) translateZ(150px);
+}
+
+.animate-cubegradient-top {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 300px;
+ height: 300px;
+ background: #222;
+ transform: rotateX(90deg) translateZ(150px);
+}
+
+.animate-cubegradient-bottom {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 300px;
+ height: 300px;
+ background: #1c6c3e;
+ transform: rotateX(-90deg) translateZ(150px);
+ filter: blur(20px);
+ box-shadow: 0 0 120px rgba(28, 108, 62, 0.2),
+ 0 0 200px rgba(28, 108, 62, 0.4),
+ 0 0 300px rgba(28, 108, 62, 0.6);
+}
diff --git a/src/components/Animations/CubeGradient/CubeGradient.tsx b/src/components/Animations/CubeGradient/CubeGradient.tsx
new file mode 100644
index 0000000..46a6b31
--- /dev/null
+++ b/src/components/Animations/CubeGradient/CubeGradient.tsx
@@ -0,0 +1,19 @@
+import styles from './CubeGradient.module.scss';
+
+export const CubeGradient = () => {
+ return (
+
+
+
+
+ {[0, 1, 2, 3].map((index) => (
+
+ ))}
+
+
+ );
+};
diff --git a/src/components/Animations/CubeGrid/CubeGrid.module.scss b/src/components/Animations/CubeGrid/CubeGrid.module.scss
new file mode 100644
index 0000000..0af4a44
--- /dev/null
+++ b/src/components/Animations/CubeGrid/CubeGrid.module.scss
@@ -0,0 +1,156 @@
+// Define size variable first
+$size: 60;
+
+:root {
+ --animate-cubegrid-size: 60px;
+ --animate-cubegrid-color-front: #e7Ac20;
+ --animate-cubegrid-color-left: #d53a33;
+ --animate-cubegrid-color-top: #1d9099;
+}
+
+* {
+ box-sizing: border-box;
+ transition: 0.35s ease;
+}
+
+.animate-cubegrid-container {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ text-align: center;
+ width: 88%;
+ width: var(--animate-cubegrid-size);
+ height: var(--animate-cubegrid-size);
+ perspective: 6000px;
+}
+
+.animate-cubegrid-cube {
+ position: absolute;
+ height: 100%;
+ width: 100%;
+ transform-style: preserve-3d;
+ transform: rotateX(-30deg) rotateY(45deg);
+
+ div {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ }
+
+ .animate-cubegrid-front {
+ transform: rotateY(0deg) translateZ(calc(var(--animate-cubegrid-size) / 2));
+ background-color: var(--animate-cubegrid-color-front);
+ }
+
+ .animate-cubegrid-left {
+ transform: rotateY(-90deg) translateZ(calc(var(--animate-cubegrid-size) / 2));
+ background-color: var(--animate-cubegrid-color-left);
+ }
+
+ .animate-cubegrid-top {
+ transform: rotateX(90deg) translateZ(calc(var(--animate-cubegrid-size) / 2));
+ background-color: var(--animate-cubegrid-color-top);
+ }
+
+ @for $i from 1 through 27 {
+ &:nth-child(#{$i}) {
+ animation: animate-cubegrid-#{$i} 3.5s cubic-bezier(0.75, 0, 0.2, 1) infinite;
+ }
+ }
+
+ // Base cube positions - matching original exactly
+ &.cube1 { transform: rotateX(-30deg) rotateY(45deg) translateX(($size - 1)px) translateY(($size - 1)px) translateZ(-($size - 1)px) }
+ &.cube2 { transform: rotateX(-30deg) rotateY(45deg) translateX(0) translateY(($size - 1)px) translateZ(-($size - 1)px) }
+ &.cube3 { transform: rotateX(-30deg) rotateY(45deg) translateX(($size - 1)px) translateY(($size - 1)px) translateZ(0) }
+ &.cube4 { transform: rotateX(-30deg) rotateY(45deg) translateX(-($size - 1)px) translateY(($size - 1)px) translateZ(-($size - 1)px) }
+ &.cube5 { transform: rotateX(-30deg) rotateY(45deg) translateX(0) translateY(($size - 1)px) translateZ(0) }
+ &.cube6 { transform: rotateX(-30deg) rotateY(45deg) translateX(($size - 1)px) translateY(($size - 1)px) translateZ(($size - 1)px) }
+ &.cube7 { transform: rotateX(-30deg) rotateY(45deg) translateX(-($size - 1)px) translateY(($size - 1)px) translateZ(0) }
+ &.cube8 { transform: rotateX(-30deg) rotateY(45deg) translateX(0) translateY(($size - 1)px) translateZ(($size - 1)px) }
+ &.cube9 { transform: rotateX(-30deg) rotateY(45deg) translateX(-($size - 1)px) translateY(($size - 1)px) translateZ(($size - 1)px) }
+
+ // Middle layer (z = 1)
+ &.cube10 { transform: rotateX(-30deg) rotateY(45deg) translateX(0) translateY(0) translateZ(var(--animate-cubegrid-size)) }
+ &.cube11 { transform: rotateX(-30deg) rotateY(45deg) translateX(var(--animate-cubegrid-size)) translateY(0) translateZ(var(--animate-cubegrid-size)) }
+ &.cube12 { transform: rotateX(-30deg) rotateY(45deg) translateX(calc(2 * var(--animate-cubegrid-size))) translateY(0) translateZ(var(--animate-cubegrid-size)) }
+ &.cube13 { transform: rotateX(-30deg) rotateY(45deg) translateX(0) translateY(var(--animate-cubegrid-size)) translateZ(var(--animate-cubegrid-size)) }
+ &.cube14 { transform: rotateX(-30deg) rotateY(45deg) translateX(var(--animate-cubegrid-size)) translateY(var(--animate-cubegrid-size)) translateZ(var(--animate-cubegrid-size)) }
+ &.cube15 { transform: rotateX(-30deg) rotateY(45deg) translateX(calc(2 * var(--animate-cubegrid-size))) translateY(var(--animate-cubegrid-size)) translateZ(var(--animate-cubegrid-size)) }
+ &.cube16 { transform: rotateX(-30deg) rotateY(45deg) translateX(0) translateY(calc(2 * var(--animate-cubegrid-size))) translateZ(var(--animate-cubegrid-size)) }
+ &.cube17 { transform: rotateX(-30deg) rotateY(45deg) translateX(var(--animate-cubegrid-size)) translateY(calc(2 * var(--animate-cubegrid-size))) translateZ(var(--animate-cubegrid-size)) }
+ &.cube18 { transform: rotateX(-30deg) rotateY(45deg) translateX(calc(2 * var(--animate-cubegrid-size))) translateY(calc(2 * var(--animate-cubegrid-size))) translateZ(var(--animate-cubegrid-size)) }
+
+ // Top layer (z = 2)
+ &.cube19 { transform: rotateX(-30deg) rotateY(45deg) translateX(0) translateY(0) translateZ(calc(2 * var(--animate-cubegrid-size))) }
+ &.cube20 { transform: rotateX(-30deg) rotateY(45deg) translateX(var(--animate-cubegrid-size)) translateY(0) translateZ(calc(2 * var(--animate-cubegrid-size))) }
+ &.cube21 { transform: rotateX(-30deg) rotateY(45deg) translateX(calc(2 * var(--animate-cubegrid-size))) translateY(0) translateZ(calc(2 * var(--animate-cubegrid-size))) }
+ &.cube22 { transform: rotateX(-30deg) rotateY(45deg) translateX(0) translateY(var(--animate-cubegrid-size)) translateZ(calc(2 * var(--animate-cubegrid-size))) }
+ &.cube23 { transform: rotateX(-30deg) rotateY(45deg) translateX(var(--animate-cubegrid-size)) translateY(var(--animate-cubegrid-size)) translateZ(calc(2 * var(--animate-cubegrid-size))) }
+ &.cube24 { transform: rotateX(-30deg) rotateY(45deg) translateX(calc(2 * var(--animate-cubegrid-size))) translateY(var(--animate-cubegrid-size)) translateZ(calc(2 * var(--animate-cubegrid-size))) }
+ &.cube25 { transform: rotateX(-30deg) rotateY(45deg) translateX(0) translateY(calc(2 * var(--animate-cubegrid-size))) translateZ(calc(2 * var(--animate-cubegrid-size))) }
+ &.cube26 { transform: rotateX(-30deg) rotateY(45deg) translateX(var(--animate-cubegrid-size)) translateY(calc(2 * var(--animate-cubegrid-size))) translateZ(calc(2 * var(--animate-cubegrid-size))) }
+ &.cube27 { transform: rotateX(-30deg) rotateY(45deg) translateX(calc(2 * var(--animate-cubegrid-size))) translateY(calc(2 * var(--animate-cubegrid-size))) translateZ(calc(2 * var(--animate-cubegrid-size))) }
+}
+
+// Animation keyframes for each cube
+@for $i from 1 through 27 {
+ $x: ($i - 1) % 3 - 1;
+ $y: floor(($i - 1) / 9) - 1;
+ $z: floor(($i - 1) % 9 / 3) - 1;
+
+ @keyframes animate-cubegrid-#{$i} {
+ 0% {
+ transform: rotateX(-30deg) rotateY(45deg)
+ translateX(calc(#{$x} * (var(--animate-cubegrid-size) - 1px)))
+ translateY(calc(#{$y} * (var(--animate-cubegrid-size) - 1px)))
+ translateZ(calc(#{$z} * (var(--animate-cubegrid-size) - 1px)));
+ }
+ 16.666% {
+ transform: rotateX(-30deg) rotateY(45deg)
+ translateX(calc(#{$x} * (var(--animate-cubegrid-size) - 1px)))
+ translateY(calc(#{$y} * (var(--animate-cubegrid-size) - 1px)))
+ translateZ(calc(#{if($z < 0, -2, if($z > 0, 2, $z))} * var(--animate-cubegrid-size)));
+ }
+ 33.333% {
+ transform: rotateX(-30deg) rotateY(45deg)
+ translateX(calc(#{if($x < 0, -2, if($x > 0, 2, $x))} * var(--animate-cubegrid-size)))
+ translateY(calc(#{$y} * (var(--animate-cubegrid-size) - 1px)))
+ translateZ(calc(#{if($z < 0, -2, if($z > 0, 2, $z))} * var(--animate-cubegrid-size)));
+ }
+ 50% {
+ transform: rotateX(-30deg) rotateY(45deg)
+ translateX(calc(#{if($x < 0, -2, if($x > 0, 2, $x))} * var(--animate-cubegrid-size)))
+ translateY(calc(#{if($y < 0, -1.675, if($y > 0, 1.675, $y))} * var(--animate-cubegrid-size)))
+ translateZ(calc(#{if($z < 0, -2, if($z > 0, 2, $z))} * var(--animate-cubegrid-size)));
+ }
+ 66.666% {
+ transform: rotateX(-30deg) rotateY(45deg)
+ translateX(calc(#{if($x < 0, -2, if($x > 0, 2, $x))} * var(--animate-cubegrid-size)))
+ translateY(calc(#{if($y < 0, -1.675, if($y > 0, 1.675, $y))} * var(--animate-cubegrid-size)))
+ translateZ(calc(#{$z} * (var(--animate-cubegrid-size) - 1px)));
+ }
+ 83.333% {
+ transform: rotateX(-30deg) rotateY(45deg)
+ translateX(calc(#{$x} * (var(--animate-cubegrid-size) - 1px)))
+ translateY(calc(#{if($y < 0, -1.675, if($y > 0, 1.675, $y))} * var(--animate-cubegrid-size)))
+ translateZ(calc(#{$z} * (var(--animate-cubegrid-size) - 1px)));
+ }
+ 100% {
+ transform: rotateX(-30deg) rotateY(45deg)
+ translateX(calc(#{$x} * (var(--animate-cubegrid-size) - 1px)))
+ translateY(calc(#{$y} * (var(--animate-cubegrid-size) - 1px)))
+ translateZ(calc(#{$z} * (var(--animate-cubegrid-size) - 1px)));
+ }
+ }
+}
+
+@media screen and (max-width: 600px) {
+ .animate-cubegrid-container {
+ transform: translate(-50%, -50%) scale(0.55);
+ }
+}
+
+:global(body) {
+ background-color: #262626;
+}
diff --git a/src/components/Animations/CubeGrid/CubeGrid.tsx b/src/components/Animations/CubeGrid/CubeGrid.tsx
new file mode 100644
index 0000000..461b893
--- /dev/null
+++ b/src/components/Animations/CubeGrid/CubeGrid.tsx
@@ -0,0 +1,26 @@
+import styles from './CubeGrid.module.scss';
+
+const SmallCube = () => (
+
+);
+
+const CubeSection = () => (
+
+ {Array.from({ length: 27 }, (_, i) => (
+
+ ))}
+
+);
+
+export const CubeGrid = () => {
+ return (
+ <>
+
+
+ >
+ );
+};
diff --git a/src/components/Animations/CubeMatrix/CubeMatrix.module.scss b/src/components/Animations/CubeMatrix/CubeMatrix.module.scss
new file mode 100644
index 0000000..b59de99
--- /dev/null
+++ b/src/components/Animations/CubeMatrix/CubeMatrix.module.scss
@@ -0,0 +1,63 @@
+$size: 50px;
+$cubeTop: #F6FDF8;
+$cubeLeft: #6C7340;
+$cubeRight: #72A298;
+$defaultSpacing: 30px; // Default gap between cubes
+$expandedSpacing: 60px; // Expanded spacing for animation
+
+.animate-cubematrix-container {
+ position: relative;
+ width: ($size * 3) + ($defaultSpacing * 2);
+ height: ($size * 3) + ($defaultSpacing * 2);
+ transform-style: preserve-3d;
+ transform: rotateX(45deg) rotateZ(45deg);
+}
+
+.animate-cubematrix-cube,
+.animate-cubematrix-cube:before,
+.animate-cubematrix-cube:after {
+ width: $size;
+ height: $size;
+ position: absolute;
+}
+
+.animate-cubematrix-cube {
+ background-color: $cubeTop;
+ transform-style: preserve-3d;
+
+ &:before {
+ content: "";
+ background-color: $cubeLeft;
+ transform-origin: 100% 100%;
+ transform: rotateX(-90deg) translateY($size);
+ }
+
+ &:after {
+ content: "";
+ transform-origin: 100% 0;
+ background-color: $cubeRight;
+ transform: rotateY(90deg) translateX($size);
+ }
+}
+
+@for $i from 0 through 2 {
+ @for $j from 0 through 2 {
+ $index: $i * 3 + $j + 1;
+ .animate-cubematrix-cube:nth-child(#{$index}) {
+ // Default position includes spacing
+ $baseX: ($size + $defaultSpacing) * (2 - $j);
+ $baseY: ($size + $defaultSpacing) * (2 - $i);
+
+ animation: splitCube#{$index} 4s ease-in-out infinite;
+
+ @keyframes splitCube#{$index} {
+ 0%, 100% {
+ transform: translateX($baseX) translateY($baseY);
+ }
+ 50% {
+ transform: translateX($size * (2 - $j)) translateY($size * (2 - $i));
+ }
+ }
+ }
+ }
+}
diff --git a/src/components/Animations/CubeMatrix/CubeMatrix.tsx b/src/components/Animations/CubeMatrix/CubeMatrix.tsx
new file mode 100644
index 0000000..2af7d37
--- /dev/null
+++ b/src/components/Animations/CubeMatrix/CubeMatrix.tsx
@@ -0,0 +1,11 @@
+import styles from './CubeMatrix.module.scss';
+
+export const CubeMatrix = () => {
+ return (
+
+ {Array.from({ length: 9 }, (_, i) => (
+
+ ))}
+
+ );
+};
diff --git a/src/components/Animations/CubeOrbit/CubeOrbit.module.scss b/src/components/Animations/CubeOrbit/CubeOrbit.module.scss
new file mode 100644
index 0000000..05c6993
--- /dev/null
+++ b/src/components/Animations/CubeOrbit/CubeOrbit.module.scss
@@ -0,0 +1,100 @@
+:root {
+ --animate-cubeorbit-size: 80px;
+ --animate-cubeorbit-face-color: 0, 221, 255;
+ --animate-cubeorbit-dark-color: 0, 83, 173;
+}
+
+.animate-cubeorbit-wrapper {
+ position: absolute;
+ width: calc(var(--animate-cubeorbit-size) * 4);
+ height: calc(var(--animate-cubeorbit-size) * 4);
+ top: 50%;
+ left: 50%;
+ margin: calc((var(--animate-cubeorbit-size) * 4) / -2) 0 0 calc((var(--animate-cubeorbit-size) * 4) / -2);
+ animation: animate-cubeorbit-wrapper 15s linear infinite;
+}
+
+.animate-cubeorbit-cube {
+ display: block;
+ width: var(--animate-cubeorbit-size);
+ height: var(--animate-cubeorbit-size);
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ margin: calc(var(--animate-cubeorbit-size) / -2) 0 0 calc(var(--animate-cubeorbit-size) / -2);
+ transform-style: preserve-3d;
+ animation: animate-cubeorbit-cubes 5s linear infinite;
+ list-style: none;
+ padding: 0;
+}
+
+.animate-cubeorbit-face {
+ position: absolute;
+ display: block;
+ width: var(--animate-cubeorbit-size);
+ height: var(--animate-cubeorbit-size);
+ background: rgba(var(--animate-cubeorbit-face-color), 0.5);
+ transform-style: preserve-3d;
+
+ &:nth-child(1) {
+ animation: animate-cubeorbit-pulse 1s alternate infinite;
+ transform: translate3d(0, 0, calc(var(--animate-cubeorbit-size) / 2));
+ }
+
+ &:nth-child(2) {
+ animation: animate-cubeorbit-pulse 1s 1s alternate infinite;
+ transform: rotateY(90deg) translate3d(0, 0, calc(var(--animate-cubeorbit-size) / 2));
+ }
+
+ &:nth-child(3) {
+ animation: animate-cubeorbit-pulse 2s 1s alternate infinite;
+ transform: translate3d(0, 0, calc(var(--animate-cubeorbit-size) / -2));
+ }
+
+ &:nth-child(4) {
+ animation: animate-cubeorbit-pulse 3s 1s alternate infinite;
+ transform: rotateY(-90deg) translate3d(0, 0, calc(var(--animate-cubeorbit-size) / 2));
+ }
+
+ &:nth-child(5) {
+ animation: animate-cubeorbit-pulse 4s 1s alternate infinite;
+ transform: translate3d(0, calc(var(--animate-cubeorbit-size) / -2), 0) rotateX(90deg);
+ }
+
+ &:nth-child(6) {
+ animation: animate-cubeorbit-pulse 5s 1s alternate infinite;
+ transform: translate3d(0, calc(var(--animate-cubeorbit-size) / 2), 0) rotateX(90deg);
+ }
+}
+
+@keyframes animate-cubeorbit-wrapper {
+ 100% {
+ transform: rotate(360deg);
+ }
+}
+
+@keyframes animate-cubeorbit-cubes {
+ 0% {
+ transform: rotateX(45deg) rotateY(0deg);
+ }
+ 100% {
+ transform: rotateX(45deg) rotateY(360deg);
+ }
+}
+
+@keyframes animate-cubeorbit-pulse {
+ 0% {
+ background: rgba(var(--animate-cubeorbit-face-color), 0.5);
+ }
+ 75% {
+ background: rgba(var(--animate-cubeorbit-dark-color), 0.5);
+ }
+ 100% {
+ background: rgba(var(--animate-cubeorbit-face-color), 0.9);
+ }
+}
+
+:global(body) {
+ background: #000;
+ overflow: hidden;
+}
diff --git a/src/components/Animations/CubeOrbit/CubeOrbit.tsx b/src/components/Animations/CubeOrbit/CubeOrbit.tsx
new file mode 100644
index 0000000..133e83e
--- /dev/null
+++ b/src/components/Animations/CubeOrbit/CubeOrbit.tsx
@@ -0,0 +1,35 @@
+import { useEffect, useRef } from 'react';
+import styles from './CubeOrbit.module.scss';
+
+export const CubeOrbit = () => {
+ const wrapperRef = useRef(null);
+
+ useEffect(() => {
+ const container = wrapperRef.current;
+ if (!container) return;
+
+ const cubes = container.querySelectorAll(`.${styles['animate-cubeorbit-cube']}`);
+ const increase = (Math.PI * 2) / cubes.length;
+ let angle = 0;
+
+ cubes.forEach((cube) => {
+ const x = 200 * Math.cos(angle) + 200;
+ const y = 200 * Math.sin(angle) + 200;
+ (cube as HTMLElement).style.left = `${x}px`;
+ (cube as HTMLElement).style.top = `${y}px`;
+ angle += increase;
+ });
+ }, []);
+
+ return (
+
+ {Array.from({ length: 8 }, (_, i) => (
+
+ {Array.from({ length: 6 }, (_, j) => (
+
+ ))}
+
+ ))}
+
+ );
+};
diff --git a/src/components/Animations/CubeShift/CubeShift.module.scss b/src/components/Animations/CubeShift/CubeShift.module.scss
new file mode 100644
index 0000000..bb9f300
--- /dev/null
+++ b/src/components/Animations/CubeShift/CubeShift.module.scss
@@ -0,0 +1,65 @@
+$cube-color: #ccc;
+$cube-size: 100px;
+$animation-duration: 1.2s;
+
+@keyframes animate-cubeshift-color {
+ 0%, 100% {
+ background: $cube-color;
+ }
+ 33% {
+ background: lighten($cube-color, 8%);
+ }
+ 66% {
+ background: lighten($cube-color, 16%);
+ }
+}
+
+.animate-cubeshift-logo {
+ position: relative;
+ height: $cube-size * 2;
+ width: $cube-size * 1.8;
+
+ &.animate-cubeshift-animate span {
+ animation: animate-cubeshift-color $animation-duration infinite;
+
+ &:nth-child(2) {
+ animation-delay: -$animation-duration / 3 * 2;
+ }
+
+ &:nth-child(3) {
+ animation-delay: -$animation-duration / 3;
+ }
+ }
+}
+
+.animate-cubeshift-cube {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+
+ &:nth-child(2) {
+ transform: rotate(180deg) scale(0.5);
+ }
+
+ span {
+ transform-origin: 0 0;
+ position: absolute;
+ height: $cube-size;
+ width: $cube-size;
+
+ &:nth-child(1) {
+ transform: rotate(210deg) skewX(-30deg) scaleY(0.864);
+ background: lighten($cube-color, 16%);
+ }
+
+ &:nth-child(2) {
+ transform: rotate(90deg) skewX(-30deg) scaleY(0.864);
+ background: $cube-color;
+ }
+
+ &:nth-child(3) {
+ transform: rotate(-30deg) skewX(-30deg) scaleY(0.864);
+ background: lighten($cube-color, 8%);
+ }
+ }
+}
diff --git a/src/components/Animations/CubeShift/CubeShift.tsx b/src/components/Animations/CubeShift/CubeShift.tsx
new file mode 100644
index 0000000..32c8e31
--- /dev/null
+++ b/src/components/Animations/CubeShift/CubeShift.tsx
@@ -0,0 +1,18 @@
+import styles from './CubeShift.module.scss';
+
+export const CubeShift = () => {
+ return (
+
+ );
+};
diff --git a/src/components/Animations/CubeSlide/CubeSlide.module.scss b/src/components/Animations/CubeSlide/CubeSlide.module.scss
new file mode 100644
index 0000000..ce457d5
--- /dev/null
+++ b/src/components/Animations/CubeSlide/CubeSlide.module.scss
@@ -0,0 +1,89 @@
+.animate-cubeslide-container {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%) rotateX(-30deg) rotateY(-45deg);
+ transform-style: preserve-3d;
+ perspective: 2000px;
+}
+
+.animate-cubeslide-holder {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%) translate3d(0em, 3em, 1.5em);
+ transform-style: preserve-3d;
+
+ &:last-child {
+ transform: rotateY(-90deg) rotateX(90deg) translate3d(0, 3em, 1.5em);
+ }
+
+ &:first-child {
+ transform: rotateZ(-90deg) rotateX(-90deg) translate3d(0, 3em, 1.5em);
+ }
+
+ &:nth-child(1) .animate-cubeslide-box {
+ background-color: #1FBCD3;
+ &:before { background-color: #126d7a; }
+ &:after { background-color: #1894a7; }
+ }
+
+ &:nth-child(2) .animate-cubeslide-box {
+ background-color: #CBE2B4;
+ &:before { background-color: #98c66a; }
+ &:after { background-color: #b2d48f; }
+ }
+
+ &:nth-child(3) .animate-cubeslide-box {
+ background-color: #F6B6CA;
+ &:before { background-color: #eb5b88; }
+ &:after { background-color: #f089a9; }
+ }
+}
+
+.animate-cubeslide-box {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ transform-style: preserve-3d;
+ animation: animate-cubeslide-box 6s infinite;
+ width: 3em;
+ height: 3em;
+
+ &:before, &:after {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ content: "";
+ }
+
+ &:before {
+ left: 100%;
+ bottom: 0;
+ transform: rotateY(90deg);
+ transform-origin: 0 50%;
+ }
+
+ &:after {
+ left: 0;
+ bottom: 100%;
+ transform: rotateX(90deg);
+ transform-origin: 0 100%;
+ }
+}
+
+@keyframes animate-cubeslide-box {
+ 8.33% { transform: translate3d(-50%, -50%, 0) scaleZ(2); }
+ 16.7% { transform: translate3d(-50%, -50%, -3em) scaleZ(1); }
+ 25% { transform: translate3d(-50%, -100%, -3em) scaleY(2); }
+ 33.3% { transform: translate3d(-50%, -150%, -3em) scaleY(1); }
+ 41.7% { transform: translate3d(-100%, -150%, -3em) scaleX(2); }
+ 50% { transform: translate3d(-150%, -150%, -3em) scaleX(1); }
+ 58.3% { transform: translate3d(-150%, -150%, 0) scaleZ(2); }
+ 66.7% { transform: translate3d(-150%, -150%, 0) scaleZ(1); }
+ 75% { transform: translate3d(-150%, -100%, 0) scaleY(2); }
+ 83.3% { transform: translate3d(-150%, -50%, 0) scaleY(1); }
+ 91.7% { transform: translate3d(-100%, -50%, 0) scaleX(2); }
+ 100% { transform: translate3d(-50%, -50%, 0) scaleX(1); }
+}
diff --git a/src/components/Animations/CubeSlide/CubeSlide.tsx b/src/components/Animations/CubeSlide/CubeSlide.tsx
new file mode 100644
index 0000000..c98ec11
--- /dev/null
+++ b/src/components/Animations/CubeSlide/CubeSlide.tsx
@@ -0,0 +1,13 @@
+import styles from './CubeSlide.module.scss';
+
+export const CubeSlide = () => {
+ return (
+
+ {[1, 2, 3].map((index) => (
+
+ ))}
+
+ );
+};
diff --git a/src/components/Animations/CubeStretch/CubeStretch.module.scss b/src/components/Animations/CubeStretch/CubeStretch.module.scss
new file mode 100644
index 0000000..f3769dc
--- /dev/null
+++ b/src/components/Animations/CubeStretch/CubeStretch.module.scss
@@ -0,0 +1,120 @@
+$n: 10;
+$t: 4.5s;
+$dimension: 1.75em;
+$c_purple_top: #842C8D;
+$c_purple_left: #852C8C;
+$c_purple_right: #7A2882;
+$c_turquoise_top: #3CC6A9;
+$c_turquoise_left: #339E89;
+$c_turquoise_right: #39B097;
+
+.animate-cubestretch-wrapper {
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+ position: relative;
+ transform-style: preserve-3d;
+}
+
+.animate-cubestretch-cube {
+ position: absolute;
+ transform-style: preserve-3d;
+ left: 50%;
+ transform: rotateX(-35deg) rotateY(-45deg);
+
+ @for $i from 0 through $n - 1 {
+ &:nth-child(#{$i + 1}) {
+ top: 26% + ($i * 4);
+ z-index: $n - $i;
+
+ .animate-cubestretch-vis {
+ background: $c_purple_left;
+ animation: animate-cubestretch-p $t ease-in-out infinite;
+
+ &:before {
+ background: $c_purple_right;
+ }
+
+ &:after {
+ background: $c_purple_top;
+ }
+ }
+ }
+
+ @if $i == 0 {
+ &:nth-child(#{$i + 1}) {
+ .animate-cubestretch-vis:after {
+ background: $c_purple_top;
+ }
+ }
+ }
+
+ @if $i % 2 != 0 {
+ &:nth-child(#{$i + 1}) {
+ .animate-cubestretch-vis {
+ background: $c_turquoise_left;
+ animation: animate-cubestretch-t $t ease-in-out infinite;
+
+ &:before {
+ background: $c_turquoise_right;
+ }
+
+ &:after {
+ background: $c_turquoise_top;
+ }
+ }
+ }
+ }
+ }
+}
+
+.animate-cubestretch-vis {
+ position: absolute;
+ transform-style: preserve-3d;
+ transform-origin: right;
+
+ &, &:before, &:after {
+ width: $dimension;
+ height: $dimension;
+ content: '';
+ position: absolute;
+ transform-style: preserve-3d;
+ }
+
+ &:before {
+ transform: rotateY(90deg) translate3d($dimension * 0.5, 0, $dimension * 0.5);
+ filter: brightness(1.3);
+ }
+
+ &:after {
+ transform: rotateX(90deg) translate3d(0, $dimension * -0.5, $dimension * 0.5);
+ filter: brightness(1.6);
+ background: $c_purple_top;
+ }
+}
+
+@keyframes animate-cubestretch-t {
+ 0% { transform: scaleX(1); }
+ 10% { transform: scaleX(4); }
+ 20% { transform: scaleX(1) translateX(-5.275em); }
+ 30% { transform: scaleZ(4) translateX(-5.275em); }
+ 40% { transform: scaleZ(1) translateX(-5.275em) translateZ(-5.275em); }
+ 50% { transform: scaleX(4) translateZ(-5.275em); }
+ 60% { transform: scaleX(1) translateZ(-5.275em); }
+ 70% { transform: scaleZ(4); }
+ 80% { transform: scaleZ(1); }
+ 100% { transform: scaleX(1); }
+}
+
+@keyframes animate-cubestretch-p {
+ 0% { transform: scaleZ(1); }
+ 10% { transform: scaleZ(4); }
+ 20% { transform: scaleZ(1) translateZ(-5.275em); }
+ 30% { transform: scaleX(4) translateZ(-5.275em); }
+ 40% { transform: scaleX(1) translateZ(-5.275em) translateX(-5.275em); }
+ 50% { transform: scaleZ(4) translateX(-5.275em); }
+ 60% { transform: scaleZ(1) translateX(-5.275em); }
+ 70% { transform: scaleX(4); }
+ 80% { transform: scaleX(1); }
+ 100% { transform: scaleZ(1); }
+}
diff --git a/src/components/Animations/CubeStretch/CubeStretch.tsx b/src/components/Animations/CubeStretch/CubeStretch.tsx
new file mode 100644
index 0000000..1241633
--- /dev/null
+++ b/src/components/Animations/CubeStretch/CubeStretch.tsx
@@ -0,0 +1,13 @@
+import styles from './CubeStretch.module.scss';
+
+export const CubeStretch = () => {
+ return (
+
+ {Array.from({ length: 10 }, (_, i) => (
+
+ ))}
+
+ );
+};
diff --git a/src/components/Animations/PenroseTriangle/PenroseTriangle.module.scss b/src/components/Animations/PenroseTriangle/PenroseTriangle.module.scss
new file mode 100644
index 0000000..83532b1
--- /dev/null
+++ b/src/components/Animations/PenroseTriangle/PenroseTriangle.module.scss
@@ -0,0 +1,75 @@
+$size: 100px;
+$half: $size / 2;
+
+.animate-penrosetriangle-container {
+ position: relative;
+ width: 100%;
+ min-height: 100%;
+ overflow: hidden;
+}
+
+.animate-penrosetriangle-platform {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ margin: auto;
+ width: $size * 5;
+ height: $size * 5;
+ transform: rotateX(54.75deg) rotateZ(45deg) translateX($size * 2);
+ transform-style: preserve-3d;
+}
+
+.animate-penrosetriangle-cube {
+ position: absolute;
+ width: $size;
+ height: $size;
+ background: hsl(200, 40%, 40%);
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.3);
+ right: 0;
+ bottom: 0;
+ transform: translateZ($size);
+ transform-style: preserve-3d;
+
+ &:before,
+ &:after {
+ content: '';
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ background: inherit;
+ box-shadow: inherit;
+ }
+
+ &:before {
+ transform-origin: 100% 0;
+ transform: rotateY(-90deg);
+ }
+
+ &:after {
+ transform-origin: 100% 0;
+ transform: rotateX(-90deg) translateZ($size);
+ }
+
+ // Cube positions
+ &:nth-child(2) { right: $size * 1.5; }
+ &:nth-child(3) { right: $size * 3; }
+ &:nth-child(4) { right: $size * 4.5; }
+ &:nth-child(5) { transform: translateZ($size * 2.5); }
+ &:nth-child(6) { transform: translateZ($size * 4); }
+ &:nth-child(7) { transform: translateZ($size * 5.5); }
+ &:nth-child(8) {
+ right: $size * 4.5;
+ bottom: $size * 1.5;
+ }
+ &:nth-child(9) {
+ right: $size * 4.5;
+ bottom: $size * 3;
+ }
+ &:nth-child(10) {
+ bottom: -$size * 1.5;
+ transform: translateZ($size * 5.5);
+ &:after { opacity: 0; }
+ }
+}
diff --git a/src/components/Animations/PenroseTriangle/PenroseTriangle.tsx b/src/components/Animations/PenroseTriangle/PenroseTriangle.tsx
new file mode 100644
index 0000000..5f56b9d
--- /dev/null
+++ b/src/components/Animations/PenroseTriangle/PenroseTriangle.tsx
@@ -0,0 +1,13 @@
+import styles from './PenroseTriangle.module.scss';
+
+export const PenroseTriangle = () => {
+ return (
+
+
+ {Array.from({ length: 10 }, (_, i) => (
+
+ ))}
+
+
+ );
+};
diff --git a/src/components/Animations/SwitchingCubes/SwitchingCubes.module.scss b/src/components/Animations/SwitchingCubes/SwitchingCubes.module.scss
new file mode 100644
index 0000000..9a8bd6a
--- /dev/null
+++ b/src/components/Animations/SwitchingCubes/SwitchingCubes.module.scss
@@ -0,0 +1,102 @@
+:root {
+ --animate-switchingcubes-size: 8em;
+ --animate-switchingcubes-border: 0.125em;
+ --animate-switchingcubes-inset: 1.75em;
+}
+
+.animate-switchingcubes-assembly {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform-style: preserve-3d;
+ transform: rotateX(-45deg) rotateY(-135deg);
+
+ &, * {
+ box-sizing: border-box;
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform-style: preserve-3d;
+ }
+}
+
+.animate-switchingcubes-positioner {
+ @for $i from 0 through 5 {
+ $j: $i % 3;
+ $k: floor($i / 3);
+
+ &:nth-child(#{$i + 1}) {
+ transform:
+ if($j != 2,
+ rotateY(($i + 1 - $k) * 90deg),
+ rotateX(pow(-1, $i + 1) * 90deg)
+ )
+ translateZ(var(--animate-switchingcubes-size))
+ if($j == 0, (), rotate(($i - $k) * 90deg));
+ }
+ }
+
+ &:last-child {
+ transform: scale3d(0.99, 0.99, 0.99);
+ -webkit-transform: none;
+ }
+}
+
+.animate-switchingcubes-cube {
+ .animate-switchingcubes-positioner:not(:last-child) & {
+ transform-origin: calc(var(--animate-switchingcubes-size) / 2) 0 calc(var(--animate-switchingcubes-size) * -0.5);
+ animation: animate-switchingcubes-rotate 1s ease-in-out infinite;
+ }
+}
+
+.animate-switchingcubes-face {
+ margin: calc(var(--animate-switchingcubes-size) * -0.5);
+ border: solid var(--animate-switchingcubes-border);
+ width: var(--animate-switchingcubes-size);
+ height: var(--animate-switchingcubes-size);
+ box-shadow:
+ inset 0 0 0 var(--animate-switchingcubes-inset) grey,
+ inset 0 0 0 calc(2 * var(--animate-switchingcubes-border) + var(--animate-switchingcubes-inset));
+
+ @for $i from 0 through 5 {
+ &:nth-child(#{$i + 1}) {
+ transform: if($i < 4,
+ rotateY($i * 90deg),
+ rotateX(pow(-1, $i) * 90deg)
+ ) translateZ(calc(var(--animate-switchingcubes-size) * 0.5));
+ }
+ }
+}
+
+.animate-switchingcubes-inner {
+ --inner-width: calc(var(--animate-switchingcubes-size) - 2 * (2 * var(--animate-switchingcubes-border) + var(--animate-switchingcubes-inset)));
+ --inner-height: calc(2 * var(--animate-switchingcubes-border) + var(--animate-switchingcubes-inset));
+
+ margin: calc(var(--inner-height) * -0.5) calc(var(--inner-width) * -0.5);
+ border: solid var(--animate-switchingcubes-border);
+ width: var(--inner-width);
+ height: var(--inner-height);
+ backface-visibility: hidden;
+ background: silver;
+
+ @for $i from 0 through 3 {
+ &:nth-child(#{$i + 1}) {
+ transform: rotate($i * 90deg) rotateX(-90deg) translate3d(
+ 0,
+ calc(var(--inner-height) * 0.5),
+ calc(var(--inner-height) * -1)
+ );
+ }
+ }
+}
+
+@keyframes animate-switchingcubes-rotate {
+ to {
+ transform: rotateY(180deg);
+ }
+}
+
+:global(body) {
+ background: black;
+ overflow: hidden;
+}
diff --git a/src/components/Animations/SwitchingCubes/SwitchingCubes.tsx b/src/components/Animations/SwitchingCubes/SwitchingCubes.tsx
new file mode 100644
index 0000000..d10f173
--- /dev/null
+++ b/src/components/Animations/SwitchingCubes/SwitchingCubes.tsx
@@ -0,0 +1,29 @@
+import styles from './SwitchingCubes.module.scss';
+
+const CubeFace = () => (
+
+ {Array.from({ length: 4 }, (_, i) => (
+
+ ))}
+
+);
+
+const Cube = () => (
+
+ {Array.from({ length: 6 }, (_, i) => (
+
+ ))}
+
+);
+
+export const SwitchingCubes = () => {
+ return (
+
+ {Array.from({ length: 7 }, (_, i) => (
+
+
+
+ ))}
+
+ );
+};
diff --git a/src/components/Animations/TricklingCubes/TricklingCubes.module.scss b/src/components/Animations/TricklingCubes/TricklingCubes.module.scss
new file mode 100644
index 0000000..6ca06aa
--- /dev/null
+++ b/src/components/Animations/TricklingCubes/TricklingCubes.module.scss
@@ -0,0 +1,118 @@
+:root {
+ --animate-tricklingcubes-size: 2em;
+ --animate-tricklingcubes-duration: 0.5s;
+ --animate-tricklingcubes-distance: 11.2em;
+ --animate-tricklingcubes-scale: 0.75;
+ --animate-tricklingcubes-color1: #222;
+ --animate-tricklingcubes-color2: #401a2a;
+ --animate-tricklingcubes-color3: #741a38;
+ --animate-tricklingcubes-color4: #9b123c;
+ --animate-tricklingcubes-color5: #c10a40;
+}
+
+.animate-tricklingcubes-assembly {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform-style: preserve-3d;
+ transform: rotateX(-35deg) rotateY(-45deg);
+}
+
+.animate-tricklingcubes-assembly > *,
+[class*='animate-tricklingcubes'] {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform-style: preserve-3d;
+ background: currentColor;
+}
+
+@for $i from 0 through 4 {
+ $j: $i + 1;
+
+ .animate-tricklingcubes-assembly > :nth-child(5n + #{$j}) {
+ --offset: calc((2 - #{$i}) * 2.5 * var(--animate-tricklingcubes-size));
+ top: calc(50% + var(--offset));
+ color: var(--animate-tricklingcubes-color#{$j});
+ }
+
+ .animate-tricklingcubes-switch-out:nth-child(5n + #{$j}) {
+ &, * {
+ animation-delay: calc(#{-$i} * var(--animate-tricklingcubes-duration) / 5);
+ }
+ }
+}
+
+[class*='animate-tricklingcubes-switch'] {
+ animation: animate-tricklingcubes-switch calc(var(--animate-tricklingcubes-duration) * 4) steps(4) infinite;
+}
+
+.animate-tricklingcubes-switch-out {
+ animation-direction: reverse;
+ animation-timing-function: steps(4, start);
+}
+
+.animate-tricklingcubes-mover {
+ animation: animate-tricklingcubes-move var(--animate-tricklingcubes-duration) ease-in-out infinite;
+}
+
+.animate-tricklingcubes-cube {
+ margin: calc(var(--animate-tricklingcubes-size) * -0.5);
+ width: var(--animate-tricklingcubes-size);
+ height: var(--animate-tricklingcubes-size);
+ transform-style: preserve-3d;
+ transform: translateZ(calc(var(--animate-tricklingcubes-size) * 0.5));
+
+ &:before,
+ &:after {
+ content: '';
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ background: inherit;
+ transform-style: preserve-3d;
+ }
+
+ &:before {
+ top: 0;
+ left: 100%;
+ transform-origin: 0 50%;
+ transform: rotateY(90deg);
+ filter: brightness(1.15);
+ }
+
+ &:after {
+ top: -100%;
+ left: 0;
+ transform-origin: 50% 100%;
+ transform: rotateX(90deg);
+ filter: brightness(1.3);
+ }
+}
+
+.animate-tricklingcubes-switch-in,
+.animate-tricklingcubes-switch-out {
+ transform-style: preserve-3d;
+}
+
+@keyframes animate-tricklingcubes-switch {
+ to {
+ transform: rotateY(360deg);
+ }
+}
+
+@keyframes animate-tricklingcubes-move {
+ 75% {
+ transform: translate(calc(0.75 * var(--animate-tricklingcubes-distance)))
+ scale3d(1, 1, 1);
+ }
+ 100% {
+ transform: translate(var(--animate-tricklingcubes-distance))
+ scale3d(0, 0, 0);
+ }
+}
+
+:global(body) {
+ background: #444;
+ overflow: hidden;
+}
diff --git a/src/components/Animations/TricklingCubes/TricklingCubes.tsx b/src/components/Animations/TricklingCubes/TricklingCubes.tsx
new file mode 100644
index 0000000..2dfbfed
--- /dev/null
+++ b/src/components/Animations/TricklingCubes/TricklingCubes.tsx
@@ -0,0 +1,28 @@
+import styles from './TricklingCubes.module.scss';
+
+const Cube = () => (
+
+);
+
+const SwitchingCube = () => (
+
+);
+
+export const TricklingCubes = () => {
+ return (
+
+ {Array.from({ length: 5 }, (_, i) => (
+
+ ))}
+ {Array.from({ length: 5 }, (_, i) => (
+
+ ))}
+
+ );
+};
diff --git a/src/components/Animations/TunnelBall/TunnelBall.module.scss b/src/components/Animations/TunnelBall/TunnelBall.module.scss
new file mode 100644
index 0000000..0aaef0d
--- /dev/null
+++ b/src/components/Animations/TunnelBall/TunnelBall.module.scss
@@ -0,0 +1,220 @@
+@property --animate-tunnelball-angle {
+ syntax: "";
+ inherits: true;
+ initial-value: 0deg;
+}
+
+@property --animate-tunnelball-circle-diameter {
+ syntax: "";
+ inherits: true;
+ initial-value: 0;
+}
+
+$walls: 6;
+$half: $walls / 2;
+$surfaces: $walls * 2;
+
+:root {
+ --animate-tunnelball-c1: #6eccee;
+ --animate-tunnelball-c2: #ffdc99;
+ --animate-tunnelball-c3: #e3a4d0;
+ --animate-tunnelball-c4: #d455ff;
+ --animate-tunnelball-duration: 2.8s;
+ --animate-tunnelball-border: 0.3vmin;
+ --animate-tunnelball-glow: drop-shadow(0 0 3vmin rgba(255, 255, 255, 0.19));
+ --animate-tunnelball-hole-y: 20%;
+ --animate-tunnelball-hole-radius: 11vmin;
+ --animate-tunnelball-surface-offset: calc(360deg / 24);
+}
+
+.animate-tunnelball-container {
+ position: relative;
+ width: 25vmin;
+ aspect-ratio: 1/1.2;
+ --animate-tunnelball-angle: 30deg;
+ animation: animate-tunnelball-rotate var(--animate-tunnelball-duration) linear infinite;
+ transform-style: preserve-3d;
+ transform: rotateX(-45deg) rotateY(45deg);
+}
+
+.animate-tunnelball-wall {
+ position: absolute;
+ inset: 0;
+ --wall-gap: 5vmin;
+ filter: var(--animate-tunnelball-glow);
+
+ @for $i from 1 through $walls {
+ $index: $i - 3;
+ $surfaceIndex: ($i - 1) * 2 + 1;
+
+ &:nth-of-type(#{$i}) {
+ transform: translateZ(calc(var(--wall-gap) * #{$index - 1}));
+ --index: #{$i};
+
+ .animate-tunnelball-surface {
+ --index: #{$surfaceIndex};
+
+ &:nth-child(2) {
+ --index: #{$surfaceIndex + 1};
+ }
+ }
+ }
+ }
+}
+
+.animate-tunnelball-surface {
+ position: absolute;
+ inset: 0;
+ --angle-offset: calc(var(--index) * var(--animate-tunnelball-surface-offset));
+ --circle-diameter: calc(
+ var(--animate-tunnelball-hole-radius) * cos(calc(var(--animate-tunnelball-angle) + var(--angle-offset)))
+ );
+ -webkit-mask: radial-gradient(
+ circle at 50% var(--animate-tunnelball-hole-y),
+ transparent var(--circle-diameter),
+ black var(--circle-diameter)
+ );
+ mask: radial-gradient(
+ circle at 50% var(--animate-tunnelball-hole-y),
+ transparent var(--circle-diameter),
+ black var(--circle-diameter)
+ );
+ background: radial-gradient(
+ circle at 50% var(--animate-tunnelball-hole-y),
+ var(--animate-tunnelball-c4) calc(var(--circle-diameter) + var(--animate-tunnelball-border)),
+ transparent var(--circle-diameter)
+ ),
+ linear-gradient(
+ 45deg,
+ var(--animate-tunnelball-c1),
+ var(--animate-tunnelball-c3),
+ var(--animate-tunnelball-c2),
+ var(--animate-tunnelball-c1),
+ var(--animate-tunnelball-c4),
+ var(--animate-tunnelball-c3),
+ var(--animate-tunnelball-c2)
+ );
+
+ &:nth-child(2) {
+ transform: translate(2vmin, 2.85vmin);
+ }
+}
+
+.animate-tunnelball-left {
+ position: absolute;
+ transform: skewY(55deg) translateY(1.45vmin);
+ inset: 0;
+ width: 2.25vmin;
+ background: linear-gradient(
+ to top,
+ var(--animate-tunnelball-c1),
+ var(--animate-tunnelball-c3),
+ var(--animate-tunnelball-c2),
+ var(--animate-tunnelball-c1)
+ );
+}
+
+.animate-tunnelball-top {
+ position: absolute;
+ transform: skewX(36deg) translateX(1vmin);
+ inset: 0;
+ height: 3vmin;
+ background: linear-gradient(
+ to right,
+ var(--animate-tunnelball-c1),
+ var(--animate-tunnelball-c3),
+ var(--animate-tunnelball-c2),
+ var(--animate-tunnelball-c1)
+ );
+ --angle-offset: calc(var(--index) * var(--animate-tunnelball-surface-offset));
+ --circle-diameter: calc(
+ var(--animate-tunnelball-hole-radius) * cos(calc(var(--animate-tunnelball-angle) + var(--angle-offset)))
+ );
+ -webkit-mask: radial-gradient(
+ calc(var(--circle-diameter) * 0.86) at 50%
+ calc(60% / cos(calc(var(--animate-tunnelball-angle) + var(--angle-offset)))),
+ transparent var(--circle-diameter),
+ black var(--circle-diameter)
+ );
+ mask: radial-gradient(
+ calc(var(--circle-diameter) * 0.86) at 50%
+ calc(60% / cos(calc(var(--animate-tunnelball-angle) + var(--angle-offset)))),
+ transparent var(--circle-diameter),
+ black var(--circle-diameter)
+ );
+ -webkit-mask-repeat: no-repeat;
+ mask-repeat: no-repeat;
+ -webkit-mask-size: 100% 100%;
+ mask-size: 100% 100%;
+ -webkit-mask-position: 0 0;
+ mask-position: 0 0;
+ z-index: 1;
+
+ .animate-tunnelball-wall:nth-last-child(-n+2) & {
+ --circle-diameter: calc(
+ var(--animate-tunnelball-hole-radius) * 1.15 * cos(calc(var(--animate-tunnelball-angle) + var(--angle-offset) - 15deg))
+ );
+ }
+}
+
+.animate-tunnelball-ball-container {
+ z-index: 0;
+ display: grid;
+ place-items: center;
+ position: absolute;
+ inset: 0;
+ transform: translateZ(-35vmin);
+ animation: animate-tunnelball-ball var(--animate-tunnelball-duration) linear infinite;
+}
+
+.animate-tunnelball-ball {
+ width: 21vmin;
+ aspect-ratio: 1;
+ border-radius: 50%;
+ filter: var(--animate-tunnelball-glow);
+ background: radial-gradient(
+ 10.75vmin 10.75vmin at center,
+ black 10vmin,
+ transparent 10vmin
+ ),
+ conic-gradient(
+ var(--animate-tunnelball-c1),
+ var(--animate-tunnelball-c3),
+ var(--animate-tunnelball-c2),
+ var(--animate-tunnelball-c4),
+ var(--animate-tunnelball-c3),
+ var(--animate-tunnelball-c1),
+ var(--animate-tunnelball-c2),
+ var(--animate-tunnelball-c1)
+ );
+ box-shadow: 0 0 5vmin rgba(255, 255, 255, 0.08);
+ transform: rotateX(45deg) rotateY(45deg) translateY(-10vmin);
+}
+
+@keyframes animate-tunnelball-rotate {
+ from {
+ --animate-tunnelball-angle: 360deg;
+ }
+ to {
+ --animate-tunnelball-angle: 0deg;
+ }
+}
+
+@keyframes animate-tunnelball-ball {
+ from {
+ transform: translateZ(-25vmin);
+ opacity: 0;
+ }
+ 10% {
+ transform: translateZ(-15vmin);
+ opacity: 1;
+ }
+ 80% {
+ opacity: 1;
+ transform: translateZ(30vmin);
+ }
+ to {
+ opacity: 0;
+ transform: translateZ(40vmin);
+ }
+}
diff --git a/src/components/Animations/TunnelBall/TunnelBall.tsx b/src/components/Animations/TunnelBall/TunnelBall.tsx
new file mode 100644
index 0000000..1f6662f
--- /dev/null
+++ b/src/components/Animations/TunnelBall/TunnelBall.tsx
@@ -0,0 +1,21 @@
+import styles from './TunnelBall.module.scss';
+
+export const TunnelBall = () => {
+ return (
+
+
+ {Array.from({ length: 6 }, (_, i) => (
+
+ ))}
+
+
+
+ );
+};
diff --git a/src/components/Animations/index.ts b/src/components/Animations/index.ts
new file mode 100644
index 0000000..72691a2
--- /dev/null
+++ b/src/components/Animations/index.ts
@@ -0,0 +1,16 @@
+export * from "./CubeOrbit/CubeOrbit";
+export * from "./CubeDots/CubeDots";
+export * from "./CubeGradient/CubeGradient";
+export * from "./CubeAssembly/CubeAssembly";
+export * from "./CubeMatrix/CubeMatrix";
+export * from './CubeStretch/CubeStretch';
+export * from './CubeFrame/CubeFrame';
+export * from './CubeSlide/CubeSlide';
+export * from './CubeExpand/CubeExpand';
+export * from './CubeGrid/CubeGrid';
+export * from './BackrollCubes/BackrollCubes';
+export * from './CubeShift/CubeShift';
+export * from './TunnelBall/TunnelBall';
+export * from './PenroseTriangle/PenroseTriangle';
+export * from './SwitchingCubes/SwitchingCubes';
+export * from './TricklingCubes/TricklingCubes';
diff --git a/src/components/index.ts b/src/components/index.ts
index 25280ee..a73a929 100644
--- a/src/components/index.ts
+++ b/src/components/index.ts
@@ -11,5 +11,6 @@ export * from "./FixedBottomGradient";
export * from "./RenderOnViewport";
export * from "./SpinnerLoading";
export * from "./AssetsVideoLoader";
+export * from "./Animations";
export type { ModalRef } from "./Modal";
diff --git a/src/pages/animations.astro b/src/pages/animations.astro
new file mode 100644
index 0000000..30ad455
--- /dev/null
+++ b/src/pages/animations.astro
@@ -0,0 +1,147 @@
+---
+import MainLayout from "@src/layouts/MainLayout.astro";
+import { CubeDots, CubeOrbit, CubeGradient, CubeAssembly, CubeMatrix, CubeStretch, CubeFrame, CubeSlide, CubeExpand, CubeGrid, BackrollCubes, CubeShift, PenroseTriangle, SwitchingCubes, TunnelBall, TricklingCubes } from '@src/components/Animations';
+---
+
+
+
+
+
Animation Showcase
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Switching Cubes
+
+
+
+
+
+
+
Trickling Cubes
+
+
+
+
+
+
+
+
+
+
diff --git a/src/sections/home/IntroSection.tsx b/src/sections/home/IntroSection.tsx
index 6162d1f..bf1ef1f 100644
--- a/src/sections/home/IntroSection.tsx
+++ b/src/sections/home/IntroSection.tsx
@@ -1,13 +1,11 @@
-import { AssetsVideoLoader, Button, SectionTitle } from "@src/components";
+import { Button, SectionTitle } from "@src/components";
+import { CubeMatrix } from "@src/components/Animations";
import clsx from "clsx";
export const IntroSection = () => {
return (