mirror of
https://github.com/Xevion/grain.git
synced 2025-12-06 01:15:10 -06:00
Compare commits
5 Commits
30d4570997
...
d668a21750
| Author | SHA1 | Date | |
|---|---|---|---|
| d668a21750 | |||
| 75913606f4 | |||
| 4d0bdeac7e | |||
| 683e504c9c | |||
| 048701580d |
36
package.json
36
package.json
@@ -9,25 +9,25 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@heroicons/react": "^2.0.13",
|
"@heroicons/react": "^2.2.0",
|
||||||
|
"@mantine/hooks": "^8.2.4",
|
||||||
|
"@tailwindcss/vite": "^4.1.11",
|
||||||
"@use-it/event-listener": "^0.1.7",
|
"@use-it/event-listener": "^0.1.7",
|
||||||
"autoprefixer": "^10.4.13",
|
"cssnano": "^7.1.0",
|
||||||
"chance": "^1.1.9",
|
"random-js": "^2.1.0",
|
||||||
"postcss": "^8.4.19",
|
"react": "^19.1.1",
|
||||||
"react": "^18.2.0",
|
"react-dom": "^19.1.1",
|
||||||
"react-dom": "^18.2.0",
|
"tailwindcss": "^4.1.11"
|
||||||
"sass": "^1.56.1",
|
|
||||||
"tailwindcss": "^3.2.4",
|
|
||||||
"usehooks-ts": "^2.9.1"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/chance": "^1.1.3",
|
"@types/node": "^24.2.1",
|
||||||
"@types/node": "^18.11.9",
|
"@types/react": "^19.1.9",
|
||||||
"@types/react": "^18.0.24",
|
"@types/react-dom": "^19.1.7",
|
||||||
"@types/react-dom": "^18.0.8",
|
"@vitejs/plugin-react": "^5.0.0",
|
||||||
"@vitejs/plugin-react": "^2.2.0",
|
"rollup-plugin-visualizer": "^6.0.3",
|
||||||
"typescript": "^4.6.4",
|
"typescript": "^5.9.2",
|
||||||
"vite": "^3.2.4",
|
"vite": "^7.1.1",
|
||||||
"vite-tsconfig-paths": "^3.6.0"
|
"vite-tsconfig-paths": "^5.1.4"
|
||||||
}
|
},
|
||||||
|
"packageManager": "pnpm@9.15.1+sha512.1acb565e6193efbebda772702950469150cf12bcc764262e7587e71d19dc98a423dff9536e57ea44c49bdf790ff694e83c27be5faa23d67e0c033b583be4bfcf"
|
||||||
}
|
}
|
||||||
|
|||||||
2573
pnpm-lock.yaml
generated
Normal file
2573
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,6 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
plugins: {
|
|
||||||
tailwindcss: {},
|
|
||||||
autoprefixer: {},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
@@ -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 {
|
||||||
@@ -35,7 +29,7 @@ function App() {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={style}
|
style={style}
|
||||||
className="text-zinc-800 gradient max-w-[100vw] max-h-[100vh] overflow-clip"
|
className="text-zinc-800 gradient max-w-screen max-h-screen overflow-clip"
|
||||||
>
|
>
|
||||||
<div className="font-inter w-full h-full bg-zinc-800/50 bg-blend-overlay">
|
<div className="font-inter w-full h-full bg-zinc-800/50 bg-blend-overlay">
|
||||||
<div className="grid grid-cols-12 w-full">
|
<div className="grid grid-cols-12 w-full">
|
||||||
@@ -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,15 +50,15 @@ 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>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className={`h-[100vh] transition-opacity ease-in-out duration-75 ${
|
className={`h-screen transition-opacity ease-in-out duration-75 ${
|
||||||
postHidden ? "opacity-0 pointer-events-none" : ""
|
postHidden ? "opacity-0 pointer-events-none" : ""
|
||||||
} flex col-span-9 sm:col-span-6 md:col-span-5 w-full min-h-[100vh]`}
|
} flex col-span-9 sm:col-span-6 md:col-span-5 w-full min-h-screen`}
|
||||||
|
|
||||||
>
|
>
|
||||||
<div className="bg-white overflow-y-auto">
|
<div className="bg-white overflow-y-auto">
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ const Post = () => {
|
|||||||
</p>
|
</p>
|
||||||
<div className="pt-3">
|
<div className="pt-3">
|
||||||
<a href="https://github.com/Xevion/grain">
|
<a href="https://github.com/Xevion/grain">
|
||||||
<div className="inline text-white text-medium drop-shadow-lg rounded border-2 shadow-xl border-zinc-600/75 m-2 p-2 bg-gradient-to-r from-red-500 via-orange-500 to-orange-700">
|
<div className="inline text-white text-medium drop-shadow-lg rounded border-2 shadow-xl border-zinc-600/75 m-2 p-2 bg-linear-to-r from-red-500 via-orange-500 to-orange-700">
|
||||||
In Progress
|
In Progress
|
||||||
<ShieldExclamationIcon className="inline h-[1.4rem] ml-3 drop-shadow-2xl" />
|
<ShieldExclamationIcon className="inline h-[1.4rem] ml-3 drop-shadow-2xl" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import ReactDOM from "react-dom/client";
|
import ReactDOM from "react-dom/client";
|
||||||
import App from "@/components/App";
|
import App from "@/components/App";
|
||||||
import "@/styles/index.scss";
|
import "@/styles/index.css";
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
|
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
|
|||||||
62
src/styles/index.css
Normal file
62
src/styles/index.css
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
@import url("https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;0,800;1,300;1,400;1,500;1,600;1,700;1,800&family=Raleway:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap");
|
||||||
|
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap");
|
||||||
|
@import url("https://fonts.googleapis.com/css2?family=Roboto+Mono&display=swap");
|
||||||
|
@import "tailwindcss";
|
||||||
|
|
||||||
|
@theme {
|
||||||
|
--font-family-opensans: "Open Sans", sans-serif;
|
||||||
|
--font-family-inter: "Inter", sans-serif;
|
||||||
|
--font-family-mono: "Roboto Mono", monospace;
|
||||||
|
--font-family-raleway: "Raleway", sans-serif;
|
||||||
|
--font-family-roboto: "Roboto";
|
||||||
|
|
||||||
|
--box-shadow-inner-md: inset 1px 4px 6px 0 rgb(0 0 0 / 0.1);
|
||||||
|
--box-shadow-inner-md-2: inset 2px 2px 6px 0 rgb(0 0 0 / 0.15);
|
||||||
|
--box-shadow-inner-md-3: inset 2px 4px 6px 0 rgb(0 0 0 / 0.21);
|
||||||
|
--box-shadow-inner-md-4: inset 2px 4px 10px 0 rgb(0 0 0 / 0.28);
|
||||||
|
--box-shadow-inner-lg: inset 4px 5px 7px 0 rgb(0 0 0 / 0.2);
|
||||||
|
--box-shadow-inner-xl: inset 4px 9px 9px 0 rgb(0 0 0 / 0.3);
|
||||||
|
--box-shadow-inner-2xl: inset 4px 11px 12px 0 rgb(0 0 0 / 0.3);
|
||||||
|
|
||||||
|
--screens-xs: 450px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 24px;
|
||||||
|
font-weight: 400;
|
||||||
|
|
||||||
|
color-scheme: light dark;
|
||||||
|
|
||||||
|
font-synthesis: none;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
-webkit-text-size-adjust: 100%;
|
||||||
|
|
||||||
|
@apply text-zinc-800 bg-zinc-400;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
@apply min-w-screen min-h-screen;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gradient {
|
||||||
|
filter: contrast(150%) brightness(90%);
|
||||||
|
background-blend-mode: overlay;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
@apply border-zinc-50/70 border outline-none focus:outline-2 outline-offset-2 focus:outline-fuchsia-500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.semibold-children {
|
||||||
|
& b {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pre.inline {
|
||||||
|
@apply text-zinc-900;
|
||||||
|
}
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
@import url("https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;0,800;1,300;1,400;1,500;1,600;1,700;1,800&family=Raleway:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap");
|
|
||||||
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap");
|
|
||||||
@import url("https://fonts.googleapis.com/css2?family=Roboto+Mono&display=swap");
|
|
||||||
|
|
||||||
@tailwind base;
|
|
||||||
@tailwind components;
|
|
||||||
@tailwind utilities;
|
|
||||||
|
|
||||||
:root {
|
|
||||||
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
|
|
||||||
font-size: 16px;
|
|
||||||
line-height: 24px;
|
|
||||||
font-weight: 400;
|
|
||||||
|
|
||||||
color-scheme: light dark;
|
|
||||||
|
|
||||||
font-synthesis: none;
|
|
||||||
text-rendering: optimizeLegibility;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
-moz-osx-font-smoothing: grayscale;
|
|
||||||
-webkit-text-size-adjust: 100%;
|
|
||||||
|
|
||||||
@apply text-zinc-800 bg-zinc-400;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
@apply min-w-[100vw] min-h-[100vh];
|
|
||||||
}
|
|
||||||
|
|
||||||
.gradient {
|
|
||||||
filter: contrast(150%) brightness(90%);
|
|
||||||
background-blend-mode: overlay;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button {
|
|
||||||
@apply border-zinc-50/70 border outline-none focus:outline-2 outline-offset-2 focus:outline-fuchsia-500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.semibold-children {
|
|
||||||
& b {
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pre.inline {
|
|
||||||
@apply text-zinc-900;
|
|
||||||
}
|
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
/** @type {import('tailwindcss').Config} */
|
|
||||||
module.exports = {
|
|
||||||
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
|
|
||||||
theme: {
|
|
||||||
extend: {
|
|
||||||
screens: {
|
|
||||||
xs: "450px",
|
|
||||||
},
|
|
||||||
boxShadow: {
|
|
||||||
"inner-md": "inset 1px 4px 6px 0 rgb(0 0 0 / 0.1)",
|
|
||||||
"inner-md-2": "inset 2px 2px 6px 0 rgb(0 0 0 / 0.15)",
|
|
||||||
"inner-md-3": "inset 2px 4px 6px 0 rgb(0 0 0 / 0.21)",
|
|
||||||
"inner-md-4": "inset 2px 4px 10px 0 rgb(0 0 0 / 0.28)",
|
|
||||||
"inner-lg": "inset 4px 5px 7px 0 rgb(0 0 0 / 0.2)",
|
|
||||||
"inner-xl": "inset 4px 9px 9px 0 rgb(0 0 0 / 0.3)",
|
|
||||||
"inner-2xl": "inset 4px 11px 12px 0 rgb(0 0 0 / 0.3)",
|
|
||||||
},
|
|
||||||
fontFamily: {
|
|
||||||
opensans: ['"Open Sans"', "sans-serif"],
|
|
||||||
inter: ['"Inter"', "sans-serif"],
|
|
||||||
mono: ['"Roboto Mono"', "monospace"],
|
|
||||||
raleway: ['"Raleway"', "sans-serif"],
|
|
||||||
roboto: ['"Roboto"'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
plugins: [],
|
|
||||||
};
|
|
||||||
@@ -1,13 +1,41 @@
|
|||||||
import { defineConfig, loadEnv } from 'vite'
|
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 { visualizer } from "rollup-plugin-visualizer";
|
||||||
|
import cssnano from "cssnano";
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default ({mode }) => {
|
export default ({ mode }) => {
|
||||||
process.env = {...process.env, ...loadEnv(mode, process.cwd())};
|
process.env = { ...process.env, ...loadEnv(mode, process.cwd()) };
|
||||||
|
|
||||||
return defineConfig({
|
return defineConfig({
|
||||||
base: '/',
|
base: "/",
|
||||||
plugins: [react(), tsconfigPaths()],
|
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: {
|
||||||
|
rollupOptions: {
|
||||||
|
treeshake: {
|
||||||
|
// Remove unused module exports
|
||||||
|
moduleSideEffects: false,
|
||||||
|
// Optimize property access
|
||||||
|
propertyReadSideEffects: false,
|
||||||
|
// Remove unused imports
|
||||||
|
tryCatchDeoptimization: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user