feat: switch to mantine-hooks, random-js, add cssnano & aggressive bundle optimizations, remove chance/usehooks/lodash

This commit is contained in:
2025-08-09 13:40:04 -05:00
parent 75913606f4
commit d668a21750
5 changed files with 820 additions and 56 deletions

View File

@@ -10,20 +10,21 @@
}, },
"dependencies": { "dependencies": {
"@heroicons/react": "^2.2.0", "@heroicons/react": "^2.2.0",
"@mantine/hooks": "^8.2.4",
"@tailwindcss/vite": "^4.1.11", "@tailwindcss/vite": "^4.1.11",
"@use-it/event-listener": "^0.1.7", "@use-it/event-listener": "^0.1.7",
"chance": "^1.1.13", "cssnano": "^7.1.0",
"random-js": "^2.1.0",
"react": "^19.1.1", "react": "^19.1.1",
"react-dom": "^19.1.1", "react-dom": "^19.1.1",
"tailwindcss": "^4.1.11", "tailwindcss": "^4.1.11"
"usehooks-ts": "^3.1.1"
}, },
"devDependencies": { "devDependencies": {
"@types/chance": "^1.1.7",
"@types/node": "^24.2.1", "@types/node": "^24.2.1",
"@types/react": "^19.1.9", "@types/react": "^19.1.9",
"@types/react-dom": "^19.1.7", "@types/react-dom": "^19.1.7",
"@vitejs/plugin-react": "^5.0.0", "@vitejs/plugin-react": "^5.0.0",
"rollup-plugin-visualizer": "^6.0.3",
"typescript": "^5.9.2", "typescript": "^5.9.2",
"vite": "^7.1.1", "vite": "^7.1.1",
"vite-tsconfig-paths": "^5.1.4" "vite-tsconfig-paths": "^5.1.4"

806
pnpm-lock.yaml generated
View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
import { useInterval, useWindowSize, useToggle } from "usehooks-ts"; import { useViewportSize, useToggle } from "@mantine/hooks";
import useBackground from "@/utils/useBackground"; import useBackground from "@/utils/useBackground";
import Post from "@/components/Post"; import Post from "@/components/Post";
@@ -7,24 +7,18 @@ import {
EyeIcon, EyeIcon,
EyeSlashIcon, EyeSlashIcon,
} from "@heroicons/react/24/solid"; } from "@heroicons/react/24/solid";
import { useMemo } from "react"; import { useMemo, useState } from "react";
function App() { function App() {
const { width, height } = useWindowSize(); const { width, height } = useViewportSize();
const { svg, backgrounds, regenerate } = useBackground({ const { svg, backgrounds, regenerate } = useBackground({
width, width,
height, height,
ratio: 0.4, ratio: 0.4,
}); });
const [postHidden, toggleHidden] = useToggle(false); const [postHidden, toggleHidden] = useToggle([false, true]);
const [iconSpinning, , setIconSpinning] = useToggle(false); const [iconSpinning, toggleIconSpinning] = useToggle([false, true]);
useInterval(
() => {
setIconSpinning(false);
},
iconSpinning ? 200 : null
);
const style = useMemo(() => { const style = useMemo(() => {
return { return {
@@ -43,8 +37,9 @@ function App() {
<button <button
className="block p-2 w-10 h-10 rounded mx-auto xs:mx-0 xs:ml-5 mt-5 shadow-inner-md bg-zinc-700 text-zinc-100 button" className="block p-2 w-10 h-10 rounded mx-auto xs:mx-0 xs:ml-5 mt-5 shadow-inner-md bg-zinc-700 text-zinc-100 button"
onClick={() => { onClick={() => {
setIconSpinning(true); toggleIconSpinning(true);
regenerate(); regenerate();
setTimeout(() => toggleIconSpinning(false), 200);
}} }}
> >
<ArrowPathIcon <ArrowPathIcon
@@ -55,7 +50,7 @@ function App() {
</button> </button>
<button <button
className="block p-2 w-10 h-10 rounded mx-auto xs:mx-0 xs:ml-5 mt-5 shadow-inner-md bg-zinc-700 text-zinc-100 button" className="block p-2 w-10 h-10 rounded mx-auto xs:mx-0 xs:ml-5 mt-5 shadow-inner-md bg-zinc-700 text-zinc-100 button"
onClick={toggleHidden} onClick={() => toggleHidden()}
> >
{postHidden ? <EyeIcon /> : <EyeSlashIcon />} {postHidden ? <EyeIcon /> : <EyeSlashIcon />}
</button> </button>

View File

@@ -1,4 +1,4 @@
import { Chance } from "chance"; import { Random } from "random-js";
import { useMemo, useState } from "react"; import { useMemo, useState } from "react";
import ReactDOMServer from "react-dom/server"; import ReactDOMServer from "react-dom/server";
import { getEdgePoint } from "@/utils/helpers"; import { getEdgePoint } from "@/utils/helpers";
@@ -15,20 +15,20 @@ interface useBackgroundReturn {
svg: string; svg: string;
} }
const chance = Chance(); const random = new Random();
const palettes = [ const palettes = [
// ["#5e1e1e", "#141414", "#400000", "#7a0000", "#2b0059", "#000c59", "#850082", "#850052"], // ["#5e1e1e", "#141414", "#400000", "#7a0000", "#2b0059", "#000c59", "#850082", "#850052"],
["#ed625d", "#42b6c6", "#f79f88", "#446ba6", "#4b95f0", "#d16ba5"], ["#ed625d", "#42b6c6", "#f79f88", "#446ba6", "#4b95f0", "#d16ba5"],
]; ];
const generateBackground = (): string[] => { const generateBackground = (): string[] => {
const palette = chance.pick(palettes); const palette = random.pick(palettes);
return Array(5) return Array(5)
.fill(null) .fill(null)
.map(() => chance.pickone(palette)) .map(() => random.pick(palette))
.map((color) => { .map((color) => {
const [x, y] = getEdgePoint( const [x, y] = getEdgePoint(
chance.integer({ min: 0, max: 400 }), random.integer(0, 400),
100, 100,
100 100
); );
@@ -60,7 +60,7 @@ const useBackground = ({
type="fractalNoise" type="fractalNoise"
baseFrequency="2.1" baseFrequency="2.1"
numOctaves="2" numOctaves="2"
seed={chance.natural()} seed={random.integer(0, 1000000)}
stitchTiles="stitch" stitchTiles="stitch"
/> />
</filter> </filter>

View File

@@ -2,6 +2,8 @@ import { defineConfig, loadEnv } from "vite";
import react from "@vitejs/plugin-react"; import react from "@vitejs/plugin-react";
import tsconfigPaths from "vite-tsconfig-paths"; import tsconfigPaths from "vite-tsconfig-paths";
import tailwindcss from "@tailwindcss/vite"; import tailwindcss from "@tailwindcss/vite";
import { visualizer } from "rollup-plugin-visualizer";
import cssnano from "cssnano";
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default ({ mode }) => { export default ({ mode }) => {
@@ -9,9 +11,31 @@ export default ({ mode }) => {
return defineConfig({ return defineConfig({
base: "/", base: "/",
plugins: [react(), tsconfigPaths(), tailwindcss()], plugins: [
react(),
tsconfigPaths(),
tailwindcss(),
cssnano(),
visualizer({
template: "treemap",
open: true, // Automatically open the report in your browser after build
filename: "stats.html", // Output file name
gzipSize: true, // Show gzip size
brotliSize: true, // Show brotli size
}),
],
build: { build: {
chunkSizeWarningLimit: 650, rollupOptions: {
treeshake: {
// Remove unused module exports
moduleSideEffects: false,
// Optimize property access
propertyReadSideEffects: false,
// Remove unused imports
tryCatchDeoptimization: false,
},
},
}, },
}); });
}; };