Compare commits

...

3 Commits

Author SHA1 Message Date
0713ad7521 canvas init 2026-03-01 22:07:33 -06:00
62346321d7 nav menu wave 2026-03-01 22:06:17 -06:00
375b7d7b47 rearchitecture 2026-03-01 22:06:04 -06:00
17 changed files with 283 additions and 70 deletions

2
.gitignore vendored
View File

@@ -21,4 +21,4 @@
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.tar
*.tar

7
.idea/compiler.xml generated Normal file
View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="TypeScriptCompiler">
<option name="memoryAutoIncrease" value="true" />
<option name="useServicePoweredTypesEnabledManually" value="true" />
</component>
</project>

76
package-lock.json generated
View File

@@ -21,9 +21,11 @@
"@types/node": "^16.18.126",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"motion": "^12.34.3",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"react-scripts": "5.0.1",
"three": "^0.183.2",
"typescript": "^4.9.5",
"web-vitals": "^2.1.4"
},
@@ -8699,6 +8701,33 @@
"url": "https://github.com/sponsors/rawify"
}
},
"node_modules/framer-motion": {
"version": "12.34.3",
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.34.3.tgz",
"integrity": "sha512-v81ecyZKYO/DfpTwHivqkxSUBzvceOpoI+wLfgCgoUIKxlFKEXdg0oR9imxwXumT4SFy8vRk9xzJ5l3/Du/55Q==",
"license": "MIT",
"dependencies": {
"motion-dom": "^12.34.3",
"motion-utils": "^12.29.2",
"tslib": "^2.4.0"
},
"peerDependencies": {
"@emotion/is-prop-valid": "*",
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@emotion/is-prop-valid": {
"optional": true
},
"react": {
"optional": true
},
"react-dom": {
"optional": true
}
}
},
"node_modules/fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
@@ -11800,6 +11829,47 @@
"mkdirp": "bin/cmd.js"
}
},
"node_modules/motion": {
"version": "12.34.3",
"resolved": "https://registry.npmjs.org/motion/-/motion-12.34.3.tgz",
"integrity": "sha512-xZIkBGO7v/Uvm+EyaqYd+9IpXu0sZqLywVlGdCFrrMiaO9JI4Kx51mO9KlHSWwll+gZUVY5OJsWgYI5FywJ/tw==",
"license": "MIT",
"dependencies": {
"framer-motion": "^12.34.3",
"tslib": "^2.4.0"
},
"peerDependencies": {
"@emotion/is-prop-valid": "*",
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@emotion/is-prop-valid": {
"optional": true
},
"react": {
"optional": true
},
"react-dom": {
"optional": true
}
}
},
"node_modules/motion-dom": {
"version": "12.34.3",
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.34.3.tgz",
"integrity": "sha512-sYgFe+pR9aIM7o4fhs2aXtOI+oqlUd33N9Yoxcgo1Fv7M20sRkHtCmzE/VRNIcq7uNJ+qio+Xubt1FXH3pQ+eQ==",
"license": "MIT",
"dependencies": {
"motion-utils": "^12.29.2"
}
},
"node_modules/motion-utils": {
"version": "12.29.2",
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.29.2.tgz",
"integrity": "sha512-G3kc34H2cX2gI63RqU+cZq+zWRRPSsNIOjpdl9TN4AQwC4sgwYPl/Q/Obf/d53nOm569T0fYK+tcoSV50BWx8A==",
"license": "MIT"
},
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@@ -16242,6 +16312,12 @@
"node": ">=0.8"
}
},
"node_modules/three": {
"version": "0.183.2",
"resolved": "https://registry.npmjs.org/three/-/three-0.183.2.tgz",
"integrity": "sha512-di3BsL2FEQ1PA7Hcvn4fyJOlxRRgFYBpMTcyOgkwJIaDOdJMebEFPA+t98EvjuljDx4hNulAGwF6KIjtwI5jgQ==",
"license": "MIT"
},
"node_modules/throat": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/throat/-/throat-6.0.2.tgz",

View File

@@ -16,9 +16,11 @@
"@types/node": "^16.18.126",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"motion": "^12.34.3",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"react-scripts": "5.0.1",
"three": "^0.183.2",
"typescript": "^4.9.5",
"web-vitals": "^2.1.4"
},

View File

@@ -1,34 +0,0 @@
$padding: 15px;
.header {
background-color: #282c34;
padding: $padding;
display: flex;
justify-items: center;
font-size: calc(10px + 2vmin);
color: white;
}
.menu {
position: absolute;
background: none;
border: none;
color: white;
text-decoration: underline;
cursor: pointer;
}
button {
}
.name {
margin: auto;
}
.main {
display: flex;
flex-direction: column;
align-items: center;
}

View File

@@ -1,15 +0,0 @@
import React from 'react';
import './App.scss';
import {Canvas} from "./index/Canvas";
import {Nav} from "./Nav";
function App() {
return (
<div className="App">
<Nav/>
<Canvas/>
</div>
);
}
export default App;

View File

@@ -1,13 +0,0 @@
import React from "react";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {faBars} from "@fortawesome/free-solid-svg-icons/faBars";
export function Nav() {
return <nav className="header">
<button className="menu">
<FontAwesomeIcon icon={faBars}/>
</button>
<div className="name">Tymur Arsentiev</div>
</nav>;
}

1
src/img/menu-wave.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1440 320"><path fill="#282c34" fill-opacity="1" d="M0,32L60,37.3C120,43,240,53,360,64C480,75,600,85,720,106.7C840,128,960,160,1080,160C1200,160,1320,128,1380,112L1440,96L1440,0L1380,0C1320,0,1200,0,1080,0C960,0,840,0,720,0C600,0,480,0,360,0C240,0,120,0,60,0L0,0Z"></path></svg>

After

Width:  |  Height:  |  Size: 330 B

View File

@@ -1,5 +1,8 @@
body {
margin: 0;
width: 100%;
height: 100%;
overflow: hidden;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
@@ -11,3 +14,10 @@ code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
button {
background: none;
border: none;
color: white;
cursor: pointer;
}

View File

@@ -1,15 +1,18 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import {Canvas} from "./index/Canvas";
import {Nav} from "./nav/Nav";
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<React.StrictMode>
<App />
<Nav/>
<Canvas/>
</React.StrictMode>
);

View File

@@ -1,9 +1,15 @@
import {motion} from "framer-motion";
import "./canvas.scss";
export function Canvas() {
return (
<>
<main className="main">
<h1>Welcome to my blog</h1>
</main>
</>
<main>
<motion.h1 className="welcome" animate={{ rotate: 360 }}>
Welcome to my blog
</motion.h1>
<canvas>
</canvas>
</main>
)
}

16
src/index/canvas.scss Normal file
View File

@@ -0,0 +1,16 @@
main {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
padding-top: 10%;
background: black;
color: white;
}
.welcome {
text-align: center;
top: 25%;
position: relative;
}

30
src/nav/Menu.tsx Normal file
View File

@@ -0,0 +1,30 @@
import "./menu.scss";
import {motion, animate} from "framer-motion";
import {useEffect} from "react";
const articles = [
"Test 123",
"Hello",
"Title of Album"
];
const hidden = -999;
export function Menu({active} : {active: boolean}) {
useEffect(() => {
if(active) {
animate(".menu", {left : 0})
} else {
animate(".menu", {left : hidden})
}
}, [active]);
return (
<motion.section className="menu">
{
articles.map(title => <div>
{title}
</div>)
}
</motion.section>
)
}

42
src/nav/Nav.tsx Normal file
View File

@@ -0,0 +1,42 @@
import React, {useState} from "react";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {faBars} from "@fortawesome/free-solid-svg-icons/faBars";
import {Menu} from "./Menu";
import wave from "../img/menu-wave.svg";
import "./nav.scss";
import {motion} from "framer-motion";
export function Nav() {
const [active, setActive] = useState(false);
return <>
<nav className="header">
<MenuButton active={active} setActive={setActive}/>
<Title/>
</nav>
<Menu active={active}/>
<img className="wave" alt="wave" src={wave}/>
</>;
}
function MenuButton(props : { active: boolean, setActive: (_:boolean) => void }) {
const {active, setActive} = props;
return (
<div className="menu-btn click">
<button>
<FontAwesomeIcon icon={faBars} onClick={function() {
setActive(!active);
}} />
</button>
</div>
);
}
function Title() {
return (
<a href="/" className="name click">
<motion.h1>Tymur Arsentiev</motion.h1>
</a>
);
}

16
src/nav/menu.scss Normal file
View File

@@ -0,0 +1,16 @@
@import "./nav";
.menu {
position: absolute;
display: flex;
flex-direction: column;
background: $menu;
z-index: 10;
color: white;
width: 25%;
height: 100vh;
div {
padding: $padding;
}
}

52
src/nav/nav.scss Normal file
View File

@@ -0,0 +1,52 @@
$padding: 15px;
$menu: #282c34;
.click:hover {
opacity: 90%;
}
.click:active {
opacity: 80%;
}
.menu-btn {
justify-self: center;
display: flex;
flex-direction: column;
align-items: center;
button {
font-size: 30px;
}
}
.name {
position: absolute;
font-weight: bold;
left: 50%;
transform: translateX(-50%);
color: white;
}
.header {
background-color: $menu;
padding: $padding;
display: flex;
font-size: calc(10px + 2vmin);
color: white;
min-height: 3 * $padding;
}
.name {
align-self: center;
flex-grow: 1;
text-decoration: underline;
}
.wave {
position: absolute;
z-index: 999;
user-select: none;
pointer-events: none;
left: -10%;
width: 110%;
}

View File

@@ -0,0 +1,14 @@
import {createContext, PropsWithChildren, useContext, useSyncExternalStore} from "react";
const RouterContext = createContext<{}>({});
export function useRouter() {
return useContext(RouterContext);
}
export function RouterProvider(props : PropsWithChildren) {
return <RouterContext.Provider value={{}}>
{props.children}
</RouterContext.Provider>
}