diff --git a/app/(core)/components/FunFactSlider.jsx b/app/(core)/components/FunFactSlider.jsx
new file mode 100644
index 00000000..1ae4a08c
--- /dev/null
+++ b/app/(core)/components/FunFactSlider.jsx
@@ -0,0 +1,113 @@
+import { useEffect, useState } from "react";
+import { Lightbulb, X } from "lucide-react";
+
+export default function FunFactSlider({ fact }) {
+ const [visible, setVisible] = useState(false);
+ const [render, setRender] = useState(false);
+
+ useEffect(() => {
+ if (!fact) return;
+
+ const renderTimer = setTimeout(() => {
+ setRender(true);
+ }, 0);
+
+ const showTimer = setTimeout(() => {
+ setVisible(true);
+ }, 50);
+
+ const hideTimer = setTimeout(() => {
+ setVisible(false);
+ }, 5000);
+
+ const removeTimer = setTimeout(() => {
+ setRender(false);
+ }, 5800);
+
+ return () => {
+ clearTimeout(renderTimer);
+ clearTimeout(showTimer);
+ clearTimeout(hideTimer);
+ clearTimeout(removeTimer);
+ };
+ }, [fact]);
+
+ if (!render) return null;
+
+ return (
+
+ {/* Cyber-Grid Background Detail */}
+
+
+ {/* Top Glow/Reflection */}
+
+
+ {/* Corner Accent Decor */}
+
+
+
+ {/* Icon Container with Layered Glow */}
+
+
+ {/* Text Content */}
+
+
+ {/* Close Button */}
+
+
+
+ );
+}
diff --git a/app/(core)/components/TopSim.tsx b/app/(core)/components/TopSim.tsx
index e71f975c..4685a33e 100644
--- a/app/(core)/components/TopSim.tsx
+++ b/app/(core)/components/TopSim.tsx
@@ -2,22 +2,25 @@ import Back from "./Back";
import simulations from "../data/chapters";
import { usePathname } from "next/navigation";
import useMobile from "../hooks/useMobile";
+import { useEffect, useState } from "react";
+import FunFactSlider from "../components/FunFactSlider";
export default function TopSim() {
const location = usePathname();
const idx = simulations.findIndex((sim) => sim.link === location);
const isMobile = useMobile();
+ const [fact, setFact] = useState(null);
+
function getPrevious() {
if (idx === -1) return "/";
- const prevIndex = (idx - 1 + simulations.length) % simulations.length;
- return simulations[prevIndex].link;
+ return simulations[(idx - 1 + simulations.length) % simulations.length]
+ .link;
}
function getNext() {
if (idx === -1) return "/";
- const nextIndex = (idx + 1) % simulations.length;
- return simulations[nextIndex].link;
+ return simulations[(idx + 1) % simulations.length].link;
}
function getCurrentName() {
@@ -25,6 +28,22 @@ export default function TopSim() {
return `${simulations[idx].id} - ${simulations[idx].name}`;
}
+ useEffect(() => {
+ const funFacts = simulations[idx]?.funFacts ?? [];
+
+ const timer = setTimeout(() => {
+ if (funFacts.length === 0) {
+ setFact(null);
+ return;
+ }
+
+ const randomIndex = Math.floor(Math.random() * funFacts.length);
+ setFact(funFacts[randomIndex]);
+ }, 0);
+
+ return () => clearTimeout(timer);
+ }, [idx]);
+
return (
{!isMobile && (
@@ -32,10 +51,8 @@ export default function TopSim() {
)}
-
+
+
- {!isMobile &&
}
+
+ {!isMobile && (
+
+
+
+ )}
);
}
diff --git a/app/(core)/data/chapters.js b/app/(core)/data/chapters.js
index afb525b7..7beb51e3 100644
--- a/app/(core)/data/chapters.js
+++ b/app/(core)/data/chapters.js
@@ -9,16 +9,22 @@ const chapters = [
tags: [TAGS.EASY, TAGS.KINEMATICS, TAGS.COLLISION],
icon: "/icons/bouncingBall.png",
relatedBlogSlug: "physics-bouncing-ball-comprehensive-educational-guide",
+ funFacts: [
+ "No real ball bounces forever because energy is lost as heat and sound.",
+ ],
},
+
{
id: 2,
name: "Vector Operations Calculator",
- desc: "Interactive 2D vector operations tool. Visualize vector addition, subtraction, and dot products in real-time with our physics calculator.",
+ desc: "Interactive 2D vector operations tool.",
link: "/simulations/VectorsOperations",
tags: [TAGS.EASY, TAGS.MATH, TAGS.VECTORS, TAGS.TRIGONOMETRY],
icon: "/icons/vector.png",
relatedBlogSlug: "operations-with-vectors",
+ funFacts: ["Dot product helps determine the angle between two vectors."],
},
+
{
id: 3,
name: "Ball Acceleration",
@@ -27,7 +33,9 @@ const chapters = [
tags: [TAGS.MEDIUM, TAGS.KINEMATICS, TAGS.ACCELERATION, TAGS.INTERACTIVE],
icon: "/icons/acceleration.png",
relatedBlogSlug: "ball-uniformly-accelerated-motion",
+ funFacts: ["Acceleration happens whenever velocity changes."],
},
+
{
id: 4,
name: "Ball Gravity",
@@ -36,34 +44,50 @@ const chapters = [
tags: [TAGS.MEDIUM, TAGS.DYNAMICS, TAGS.GRAVITY, TAGS.COLLISION],
icon: "/icons/gravity.png",
relatedBlogSlug: "ball-free-fall-comprehensive-guide",
+ funFacts: [
+ "All objects fall at the same rate in a vacuum, regardless of mass.",
+ ],
},
+
{
id: 5,
name: "Spring Mass System Simulator",
- desc: "Explore Hooke's Law and Simple Harmonic Motion. Adjust mass and spring constants to visualize oscillations and energy conservation.",
+ desc: "Explore Hooke's Law and Simple Harmonic Motion.",
link: "/simulations/SpringConnection",
tags: [TAGS.ADVANCED, TAGS.DYNAMICS, TAGS.SPRINGS, TAGS.OSCILLATIONS],
icon: "/icons/spring.png",
relatedBlogSlug: "spring-connection",
+ funFacts: [
+ "Hooke’s Law is valid only within the elastic limit of a spring.",
+ ],
},
+
{
id: 6,
name: "Simple Pendulum Simulation",
- desc: "Calculate the period and frequency of a pendulum. Visualize the relationship between length, gravity, and kinetic energy in real-time.",
+ desc: "Calculate the period and frequency of a pendulum.",
link: "/simulations/SimplePendulum",
tags: [TAGS.MEDIUM, TAGS.DYNAMICS, TAGS.OSCILLATIONS, TAGS.ENERGY],
icon: "/icons/pendulam.png",
relatedBlogSlug: "pendulum-motion",
+ funFacts: [
+ "For small angles, a pendulum’s period does not depend on mass.",
+ ],
},
+
{
id: 7,
name: "Projectile & Parabolic Motion",
- desc: "Simulate projectile motion under gravity. Predict trajectories, range, and maximum height with our interactive physics calculator.",
+ desc: "Simulate projectile motion under gravity.",
link: "/simulations/ParabolicMotion",
tags: [TAGS.MEDIUM, TAGS.KINEMATICS, TAGS.GRAVITY, TAGS.MATH],
icon: "/icons/parabola.png",
relatedBlogSlug: "projectile-parabolic-motion",
+ funFacts: [
+ "Maximum range occurs at a 45° launch angle without air resistance.",
+ ],
},
+
{
id: 8,
name: "Inclined Plane",
@@ -71,14 +95,9 @@ const chapters = [
link: "/simulations/InclinedPlane",
tags: [TAGS.MEDIUM, TAGS.DYNAMICS, TAGS.FORCES, TAGS.FRICTION],
icon: "/icons/inclined.png",
- },
- {
- id: 0,
- name: "Test for benchmarks",
- desc: "This is a simulation created specifically for performance testing.",
- link: "/simulations/test",
- tags: [TAGS.EXPERIMENTAL, TAGS.BENCHMARK, TAGS.PERFORMANCE],
- icon: "/icons/test.png",
+ funFacts: [
+ "Inclined planes reduce the force needed to lift heavy objects.",
+ ],
},
];
diff --git a/app/(core)/styles/index.css b/app/(core)/styles/index.css
index 1f58b8d7..b3db8f1a 100644
--- a/app/(core)/styles/index.css
+++ b/app/(core)/styles/index.css
@@ -1586,8 +1586,12 @@ footer {
width: 20%;
}
-.top-nav-sim-filler {
+.fun-fact-slider-wrapper {
width: 20%;
+ justify-content: center;
+ margin-left: auto;
+ margin-top: 20px;
+ margin-right: 55px;
}
/* Wind icon for Ball Gravity Simulation*/
diff --git a/components.json b/components.json
new file mode 100644
index 00000000..3ce9e88d
--- /dev/null
+++ b/components.json
@@ -0,0 +1,23 @@
+{
+ "$schema": "https://ui.shadcn.com/schema.json",
+ "style": "new-york",
+ "rsc": true,
+ "tsx": true,
+ "tailwind": {
+ "config": "tailwind.config.ts",
+ "css": "app/(core)/styles/index.css",
+ "baseColor": "neutral",
+ "cssVariables": true,
+ "prefix": ""
+ },
+ "iconLibrary": "lucide",
+ "rtl": false,
+ "aliases": {
+ "components": "@/components",
+ "utils": "@/lib/utils",
+ "ui": "@/components/ui",
+ "lib": "@/lib",
+ "hooks": "@/hooks"
+ },
+ "registries": {}
+}
diff --git a/components/ui/button.tsx b/components/ui/button.tsx
new file mode 100644
index 00000000..900bdf9e
--- /dev/null
+++ b/components/ui/button.tsx
@@ -0,0 +1,58 @@
+import * as React from "react";
+import { Slot } from "@radix-ui/react-slot";
+import { cva, type VariantProps } from "class-variance-authority";
+
+import { cn } from "@/lib/utils";
+
+const buttonVariants = cva(
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
+ {
+ variants: {
+ variant: {
+ default:
+ "bg-primary text-primary-foreground shadow hover:bg-primary/90",
+ destructive:
+ "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
+ outline:
+ "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
+ secondary:
+ "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
+ ghost: "hover:bg-accent hover:text-accent-foreground",
+ link: "text-primary underline-offset-4 hover:underline",
+ },
+ size: {
+ default: "h-9 px-4 py-2",
+ sm: "h-8 rounded-md px-3 text-xs",
+ lg: "h-10 rounded-md px-8",
+ icon: "h-9 w-9",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ size: "default",
+ },
+ }
+);
+
+export interface ButtonProps
+ extends
+ React.ButtonHTMLAttributes,
+ VariantProps {
+ asChild?: boolean;
+}
+
+const Button = React.forwardRef(
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
+ const Comp = asChild ? Slot : "button";
+ return (
+
+ );
+ }
+);
+Button.displayName = "Button";
+
+export { Button, buttonVariants };
diff --git a/lib/utils.ts b/lib/utils.ts
new file mode 100644
index 00000000..a5ef1935
--- /dev/null
+++ b/lib/utils.ts
@@ -0,0 +1,6 @@
+import { clsx, type ClassValue } from "clsx";
+import { twMerge } from "tailwind-merge";
+
+export function cn(...inputs: ClassValue[]) {
+ return twMerge(clsx(inputs));
+}
diff --git a/next.config.mjs b/next.config.mjs
new file mode 100644
index 00000000..f8a052d5
--- /dev/null
+++ b/next.config.mjs
@@ -0,0 +1,12 @@
+/** @type {import('next').NextConfig} */
+const nextConfig = {
+ output: "export",
+ env: {
+ NEXT_PUBLIC_APP_VERSION: "0.0.0",
+ },
+ images: {
+ unoptimized: true,
+ },
+};
+
+export default nextConfig;
diff --git a/next.config.ts b/next.config.ts
deleted file mode 100644
index 0d9aea6b..00000000
--- a/next.config.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import type { NextConfig } from "next";
-import packageJson from "./package.json" assert { type: "json" };
-
-const nextConfig: NextConfig = {
- output: "export",
- env: {
- NEXT_PUBLIC_APP_VERSION: packageJson.version,
- },
- images: {
- unoptimized: true,
- },
-};
-
-export default nextConfig;
diff --git a/package-lock.json b/package-lock.json
index 92317a98..0420b663 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -16,10 +16,16 @@
"@fortawesome/free-solid-svg-icons": "^7.1.0",
"@fortawesome/react-fontawesome": "^3.1.0",
"@octokit/rest": "^22.0.1",
+ "@radix-ui/react-slot": "^1.2.4",
"ace-builds": "^1.43.5",
+ "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
+ "framer-motion": "^12.34.0",
"katex": "^0.16.25",
"koalaz": "^1.1.3",
+ "lucide-react": "^0.563.0",
+ "motion": "^12.34.0",
+ "next": "16.1.5",
"motion": "^12.23.24",
"next": "16.1.6",
"p5": "^2.1.2",
@@ -30,15 +36,17 @@
"react-katex": "^3.1.0",
"react-router-hash-link": "^2.4.3",
"react-syntax-highlighter": "^16.1.0",
+ "tailwind-merge": "^3.4.0",
+ "tailwindcss-animate": "^1.0.7",
"xml-formatter": "^3.6.7"
},
"devDependencies": {
"@eslint/js": "^10.0.1",
"@semantic-release/exec": "^7.1.0",
"@tailwindcss/postcss": "^4",
- "@types/node": "^25.0.9",
+ "@types/node": "25.2.2",
"@types/p5": "^1.7.6",
- "@types/react": "^19.2.7",
+ "@types/react": "19.2.13",
"@types/react-dom": "^19.2.3",
"@types/react-katex": "^3.0.4",
"@types/react-syntax-highlighter": "^15.5.13",
@@ -61,6 +69,7 @@
"sitemap": "^9.0.1",
"tailwindcss": "^4",
"ts-node": "^10.9.2",
+ "typescript": "5.9.3"
"typescript": "^5.9.3"
},
"engines": {
@@ -1840,6 +1849,39 @@
"node": ">=18"
}
},
+ "node_modules/@radix-ui/react-compose-refs": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
+ "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-slot": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz",
+ "integrity": "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-compose-refs": "1.1.2"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@rtsao/scc": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
@@ -2679,9 +2721,9 @@
"license": "MIT"
},
"node_modules/@types/node": {
- "version": "25.1.0",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-25.1.0.tgz",
- "integrity": "sha512-t7frlewr6+cbx+9Ohpl0NOTKXZNV9xHRmNOvql47BFJKcEG1CxtxlPEEe+gR9uhVWM4DwhnvTF110mIL4yP9RA==",
+ "version": "25.2.2",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.2.tgz",
+ "integrity": "sha512-BkmoP5/FhRYek5izySdkOneRyXYN35I860MFAGupTdebyE66uZaR+bXLHq8k4DirE5DwQi3NuhvRU1jqTVwUrQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2710,10 +2752,10 @@
"license": "MIT"
},
"node_modules/@types/react": {
- "version": "19.2.10",
- "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.10.tgz",
- "integrity": "sha512-WPigyYuGhgZ/cTPRXB2EwUw+XvsRA3GqHlsP4qteqrnnjDrApbS7MxcGr/hke5iUoeB7E/gQtrs9I37zAJ0Vjw==",
- "dev": true,
+ "version": "19.2.13",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.13.tgz",
+ "integrity": "sha512-KkiJeU6VbYbUOp5ITMIc7kBfqlYkKA5KhEHVrGMmUUMt7NeaZg65ojdPk+FtNrBAOXNVM5QM72jnADjM+XVRAQ==",
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"csstype": "^3.2.2"
@@ -4160,6 +4202,18 @@
"url": "https://github.com/sponsors/colinhacks"
}
},
+ "node_modules/class-variance-authority": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz",
+ "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "clsx": "^2.1.1"
+ },
+ "funding": {
+ "url": "https://polar.sh/cva"
+ }
+ },
"node_modules/clean-stack": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
@@ -4841,7 +4895,7 @@
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
- "dev": true,
+ "devOptional": true,
"license": "MIT"
},
"node_modules/damerau-levenshtein": {
@@ -6629,12 +6683,12 @@
}
},
"node_modules/framer-motion": {
- "version": "12.29.2",
- "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.29.2.tgz",
- "integrity": "sha512-lSNRzBJk4wuIy0emYQ/nfZ7eWhqud2umPKw2QAQki6uKhZPKm2hRQHeQoHTG9MIvfobb+A/LbEWPJU794ZUKrg==",
+ "version": "12.34.0",
+ "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.34.0.tgz",
+ "integrity": "sha512-+/H49owhzkzQyxtn7nZeF4kdH++I2FWrESQ184Zbcw5cEqNHYkE5yxWxcTLSj5lNx3NWdbIRy5FHqUvetD8FWg==",
"license": "MIT",
"dependencies": {
- "motion-dom": "^12.29.2",
+ "motion-dom": "^12.34.0",
"motion-utils": "^12.29.2",
"tslib": "^2.4.0"
},
@@ -8766,6 +8820,15 @@
"yallist": "^3.0.2"
}
},
+ "node_modules/lucide-react": {
+ "version": "0.563.0",
+ "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.563.0.tgz",
+ "integrity": "sha512-8dXPB2GI4dI8jV4MgUDGBeLdGk8ekfqVZ0BdLcrRzocGgG75ltNEmWS+gE7uokKF/0oSUuczNDT+g9hFJ23FkA==",
+ "license": "ISC",
+ "peerDependencies": {
+ "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
"node_modules/magic-string": {
"version": "0.30.21",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
@@ -9036,12 +9099,12 @@
"license": "MIT"
},
"node_modules/motion": {
- "version": "12.29.2",
- "resolved": "https://registry.npmjs.org/motion/-/motion-12.29.2.tgz",
- "integrity": "sha512-jMpHdAzEDF1QQ055cB+1lOBLdJ6ialVWl6QQzpJI2OvmHequ7zFVHM2mx0HNAy+Tu4omUlApfC+4vnkX0geEOg==",
+ "version": "12.34.0",
+ "resolved": "https://registry.npmjs.org/motion/-/motion-12.34.0.tgz",
+ "integrity": "sha512-01Sfa/zgsD/di8zA/uFW5Eb7/SPXoGyUfy+uMRMW5Spa8j0z/UbfQewAYvPMYFCXRlyD6e5aLHh76TxeeJD+RA==",
"license": "MIT",
"dependencies": {
- "framer-motion": "^12.29.2",
+ "framer-motion": "^12.34.0",
"tslib": "^2.4.0"
},
"peerDependencies": {
@@ -9062,9 +9125,9 @@
}
},
"node_modules/motion-dom": {
- "version": "12.29.2",
- "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.29.2.tgz",
- "integrity": "sha512-/k+NuycVV8pykxyiTCoFzIVLA95Nb1BFIVvfSu9L50/6K6qNeAYtkxXILy/LRutt7AzaYDc2myj0wkCVVYAPPA==",
+ "version": "12.34.0",
+ "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.34.0.tgz",
+ "integrity": "sha512-Lql3NuEcScRDxTAO6GgUsRHBZOWI/3fnMlkMcH5NftzcN37zJta+bpbMAV9px4Nj057TuvRooMK7QrzMCgtz6Q==",
"license": "MIT",
"dependencies": {
"motion-utils": "^12.29.2"
@@ -14344,13 +14407,31 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/tailwind-merge": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.4.0.tgz",
+ "integrity": "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/dcastil"
+ }
+ },
"node_modules/tailwindcss": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.1.tgz",
- "integrity": "sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw==",
- "dev": true,
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz",
+ "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==",
"license": "MIT"
},
+ "node_modules/tailwindcss-animate": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz",
+ "integrity": "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==",
+ "license": "MIT",
+ "peerDependencies": {
+ "tailwindcss": ">=3.0.0 || insiders"
+ }
+ },
"node_modules/tapable": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz",
diff --git a/package.json b/package.json
index db1bc3d1..92f98a9a 100644
--- a/package.json
+++ b/package.json
@@ -27,10 +27,14 @@
"@fortawesome/free-solid-svg-icons": "^7.1.0",
"@fortawesome/react-fontawesome": "^3.1.0",
"@octokit/rest": "^22.0.1",
+ "@radix-ui/react-slot": "^1.2.4",
"ace-builds": "^1.43.5",
+ "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
+ "framer-motion": "^12.34.0",
"katex": "^0.16.25",
"koalaz": "^1.1.3",
+ "lucide-react": "^0.563.0",
"motion": "^12.23.24",
"next": "16.1.6",
"p5": "^2.1.2",
@@ -41,15 +45,16 @@
"react-katex": "^3.1.0",
"react-router-hash-link": "^2.4.3",
"react-syntax-highlighter": "^16.1.0",
+ "tailwind-merge": "^3.4.0",
+ "tailwindcss-animate": "^1.0.7",
"xml-formatter": "^3.6.7"
},
"devDependencies": {
- "@eslint/js": "^10.0.1",
"@semantic-release/exec": "^7.1.0",
"@tailwindcss/postcss": "^4",
- "@types/node": "^25.0.9",
+ "@types/node": "25.2.2",
"@types/p5": "^1.7.6",
- "@types/react": "^19.2.7",
+ "@types/react": "19.2.13",
"@types/react-dom": "^19.2.3",
"@types/react-katex": "^3.0.4",
"@types/react-syntax-highlighter": "^15.5.13",
@@ -57,7 +62,6 @@
"@typescript-eslint/parser": "^8.53.0",
"axios": "^1.13.2",
"dotenv": "^17.2.3",
- "eslint": "^9.39.1",
"eslint-config-next": "16.1.6",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.24",
@@ -72,7 +76,7 @@
"sitemap": "^9.0.1",
"tailwindcss": "^4",
"ts-node": "^10.9.2",
- "typescript": "^5.9.3"
+ "typescript": "5.9.3"
},
"lint-staged": {
"**/*.{js,jsx,ts,tsx}": [
diff --git a/tailwind.config.ts b/tailwind.config.ts
index bfb53710..95ea5797 100644
--- a/tailwind.config.ts
+++ b/tailwind.config.ts
@@ -8,6 +8,29 @@ const config: Config = {
"./components/**/*.{js,ts,jsx,tsx,mdx}",
"./app/**/*.{js,ts,jsx,tsx,mdx}",
],
+ theme: {
+ extend: {
+ keyframes: {
+ float: {
+ "0%, 100%": { transform: "translateY(0px)" },
+ "50%": { transform: "translateY(-8px)" },
+ },
+ fadeSlide: {
+ "0%": { opacity: "0", transform: "translateY(20px)" },
+ "100%": { opacity: "1", transform: "translateY(0px)" },
+ },
+ glowPulse: {
+ "0%, 100%": { boxShadow: "0 0 15px rgba(0,255,255,0.2)" },
+ "50%": { boxShadow: "0 0 35px rgba(0,255,255,0.5)" },
+ },
+ },
+ animation: {
+ float: "float 6s ease-in-out infinite",
+ fadeSlide: "fadeSlide 5s ease-in-out forwards",
+ glowPulse: "glowPulse 5s ease-in-out infinite",
+ },
+ },
+ },
plugins: [],
};
diff --git a/tsconfig.json b/tsconfig.json
index 3abda2f7..394807fa 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -32,7 +32,8 @@
"**/*.mts",
"app/hooks/useSimInfo.ts",
"app/components/LandingPart.jsx",
- "app/(pages)/blog/[slug]/page.jsx"
+ "app/(pages)/blog/[slug]/page.jsx",
+ "next.config.mjs"
],
"exclude": ["node_modules"]
}