about page content + integrate mdx

This commit is contained in:
2026-03-06 20:37:20 -06:00
parent 3071ae41e6
commit 7feb00e33a
14 changed files with 1968 additions and 47 deletions

6
.idea/misc.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptSettings">
<option name="languageLevel" value="FLOW" />
</component>
</project>

1776
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -38,6 +38,7 @@
"@babel/preset-env": "^7.29.0",
"@babel/preset-react": "^7.28.5",
"@babel/preset-typescript": "^7.28.5",
"@mdx-js/loader": "^3.1.1",
"@pmmmwh/react-refresh-webpack-plugin": "^0.6.2",
"@svgr/webpack": "^8.1.0",
"@testing-library/dom": "^10.4.1",
@@ -45,6 +46,7 @@
"@testing-library/react": "^16.3.2",
"@testing-library/user-event": "^14.6.1",
"@types/jest": "^30.0.0",
"@types/mdx": "^2.0.13",
"@types/node": "^25.3.3",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
@@ -71,7 +73,8 @@
"typescript": "^5.9.3",
"webpack": "^5.105.4",
"webpack-cli": "^6.0.1",
"webpack-dev-server": "^5.2.3"
"webpack-dev-server": "^5.2.3",
"webpack-manifest-plugin": "^6.0.1"
},
"babel": {
"presets": [

94
src/about/About.mdx Normal file
View File

@@ -0,0 +1,94 @@
import openingImage from "../img/IMG_20240725_175449_803.jpg";
import rainbow from "../img/IMG-0186.jpg";
import japanese from "../img/IMG_20230531_000224_01.jpg";
import computers from "../img/IMG_3071.jpg";
import mobo from "../img/2025-10-03-02-20-50-077.jpg";
import suit from "../img/military.jpg";
import nutrivend from "../img/nutrivend.jpeg";
<img src={openingImage} alt="Tymur Arsentiev" className="self"/>
# Welcome to my blog and personal website!
For the longest time, I never had a personal website to show my skills.
Project after project, I have over 4 years under my belt, but I've never considered making a portfolio.
Since early 2026, I've begun working on one. My website styles blend together an abstract UI while retaining functional simplicity on UX.
Website development is my favorite hobby, and is naturally my favorite type of software engineering.
My goals are the following:
- Journaling my thoughts (originally done on [Medium](https://medium.com/@tymur.arsent)
and [Substack](https://substack.com/@timsnodocmartins))
- Merging my unique fashion sense with my website design skills
- Cultivating my personal brand by documenting unique coding challenges
## Fashion Rebel
<div className="clothing">
<img src={rainbow} alt="Rainbow colored clothing"/>
<img src={japanese} alt="Purple shirt with Japanese text"/>
</div>
I am a fashion rebel at heart; just like my websites, the goal of my fashion is to stand out.
I'm more of a shirt/jacket person more than anything, and I tend to keep my pants simple.
I'm a sucker for flashy colors and abstract designs, so I'm always out buying, looking for inspiration.
I'm originally from Chicago, but I go to Georgia Tech in Atlanta. But I like Atlanta more for fashion.
Whenever I can, I like to go thrifting near Ponce De Leon or Little Five Points.
Chicago has few boutiques, mostly only name brand clothing. It's gotten so bad recently that I've been doing all my shopping on [Ebay](https://www.ebay.com/usr/tymur999).
I like Ebay because it's like a collector's website. I've been a collector all my life, so you could say that I collect clothing.
I also sell stuff on there, so be sure to check out my listings.
Why do I talk about fashion? That's because my fashion hobby inspired me to start this website.
I'd like to think my taste in wearing outfits has been a huge asset in finding a modern design that works for this site.
## Self-Hosting Champion
<div className="computers">
<img src={computers} alt="Surrounded by computers"/>
<img src={mobo} alt="Surrounded by computers"/>
</div>
As a rebel, I greatly value my independence.
I don't like how big tech companies operate by encroaching on my data,
so I've been self-hosting nearly all the software I use.
This website is hosted on my Kubernetes cluster, run at home.
Here are some other software I self-host on it:
- [Gitea, for VCS operations and CI/CD solution](https://gitea.tymur999.com)
- [Nextcloud, for private cloud and file+calendar+task synchronization](https://nextcloud.tymur999.com)
- Postfix, for private email solution and custom domain
### That alone means I have my own self-hosted version of Google Drive, Gmail, and Github.
I take pride in my Kubernetes cluster as Kubernetes knowledge is invaluable at nearly all software companies.
I like managing the software at every step of the deployment pipeline, besides writing the actual code.
If you couldn't tell, I'm very DIY-ey. I build every computer and server from scratch.
The cluster runs on a 28-core dual Intel Xeon server, which is my most impressive build.
Otherwise, All my computers run on Arch Linux, which is built from scratch.
Not to mention, my Linux experience spans nearly my entire life. I got my first Raspberry Pi in 2013.
And now I daily drive Arch Linux, if that says anything about my Linux experience.
## Savvy Entrepreneur
<div className="clothing">
<img src={suit} alt="Surrounded by computers"/>
<img src={nutrivend} alt="Surrounded by computers"/>
</div>
At heart, I'm always self-employed. Always looking to hustle or solve a challenge to earn money.
I've been a part of two startups, Nutrivend and Kiwoon Learning, both of which I worked as CTO.
At Kiwoon Learning, around 2023, I made a stock market simulator backend in C# and built a frontend in React.
The goal of the app was to teach high school students how to invest in stocks.
Through coding the app, I learned how to stop being so perfectionist.
My perfectionism led to delays in releases, and I learned to value my time much more.
However, the simulator turned out to be one of the most advanced on the market, especially for high school students.
At Nutrivend, I built a full-stack web application using Next.js.
The app featured a quiz system that filtered people into different categories of supplements based on their
fitness goals and health. I helped my friend Woosik integrate Stripe so he could easily accept payments
and manage his supplement orders and supply.
Here, I used my Kubernetes experience to deploy the app on my cluster and keep uptime as close to 24/7 as possible.

View File

@@ -1,40 +1,14 @@
import "./about.scss";
import openingImage from "../img/IMG_20240725_175449_803.jpg";
import rainbow from "../img/IMG-0186.jpg";
import japanese from "../img/IMG_20230531_000224_01.jpg";
import type {PropsWithChildren} from "react";
import {PropsWithChildren} from "react";
import About from "./About.mdx";
export default function AboutPage() {
return <main className="about-page">
<div className="edge"/>
<section className="about-container">
<img src={openingImage} alt="Tymur Arsentiev" className="self"/>
<h1 className="title">Welcome to my blog and personal website!</h1>
<p>
For the longest time, I never had a personal website to show my skills.
I'm a longtime coder, project after project, but have never considered making a place to share my thoughts.
After a while, I was inspired by my <ExternalLink href="https://medium.com/@tymur.arsent">Medium</ExternalLink> and
&nbsp;<ExternalLink href="https://substack.com/@timsnodocmartins">Substack</ExternalLink> account to begin journaling my thoughts on my own.
</p>
<p>
I like to take pride in my uniqueness as a web developer.
I don't go for trends, I consider myself <i>the trendsetter.</i>&nbsp;
My website design was definitely influenced by my fashion sense.
</p>
<h2>Fashion Rebel</h2>
<div className="clothing">
<img src={rainbow} alt="Rainbow colored clothing"/>
<img src={japanese} alt="Purple shirt with Japanese text"/>
</div>
<p>
Clothes shopping is a great knack of mine. I'm skilled in finding all kinds of weird clothes to wear.
I like doing stuff that makes me stand out, as you can see from the gallery.
</p>
<p>
My store of choice is <ExternalLink href="https://www.ebay.com/usr/tymur999">Ebay</ExternalLink>, where I love thrifting out-of-this-world outfits for a low price.
EBay is a place for me to liquidate my assets, which is random items around the house.
My favorite thing about Ebay is that they have almost every clothing brand, typically at reduced prices due to use.
</p>
<About components={{
'a': ExternalLink,
}}/>
</section>
<div className="edge"/>
</main>

View File

@@ -1,4 +1,6 @@
@use "../nav/nav.scss";
$paragraph: 20px;
$max-width: 600px;
.about-page {
color: white;
@@ -24,24 +26,28 @@
flex-direction: column;
align-items: center;
font-family: Neuton;
line-height: 40px;
img:not(.self) {
border: 2px white solid;
}
p {
text-indent: 2 * nav.$padding;
text-indent: 30px;
margin: 10px;
max-width: 600px;
line-height: 40px;
font-size: 20px;
max-width: $max-width;
font-size: $paragraph;
}
h1 {
font-size: 40px;
font-size: $paragraph * 2;
}
h2 {
font-size: 30px;
font-size: $paragraph * 1.5;
}
h3 {
font-size: $paragraph * 1.1;
font-weight: bold;
}
}
@@ -50,7 +56,7 @@
flex-grow: 1;
}
.clothing {
.clothing, .computers {
margin: 15px 0;
display: flex;
flex-direction: row;
@@ -61,6 +67,27 @@
width: 33%;
}
.computers {
// select second
img:not(:first-child) {
width: 20%;
}
img {
width: 30%;
}
}
.external-link {
text-decoration: underline !important;
}
ul {
display: flex;
flex-direction: column;
font-size: $paragraph;
max-width: $max-width;
gap: 10px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 817 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

BIN
src/img/IMG_3071.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

BIN
src/img/IMG_3095.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 MiB

BIN
src/img/military.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

BIN
src/img/nutrivend.jpeg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

14
src/index.d.ts vendored
View File

@@ -5,6 +5,20 @@ declare namespace NodeJS {
}
}
declare module '*.mdx' {
import {MDXProps} from "mdx/types";
let MDXComponent: (props: MDXProps) => JSX.Element;
export default MDXComponent;
}
declare module '*.md' {
import {MDXProps} from "mdx/types";
let MDXComponent: (props: MDXProps) => JSX.Element;
export default MDXComponent;
}
declare module '*.avif' {
const src: string;
export default src;

View File

@@ -4,7 +4,9 @@ import HtmlWebpackPlugin from "html-webpack-plugin";
import MiniCssExtractPlugin from "mini-css-extract-plugin";
import ReactRefreshWebpackPlugin from "@pmmmwh/react-refresh-webpack-plugin";
import ForkTsCheckerWebpackPlugin from "fork-ts-checker-webpack-plugin";
import {WebpackManifestPlugin} from "webpack-manifest-plugin";
import TerserPlugin from "terser-webpack-plugin";
import {Options} from '@mdx-js/loader';
import "webpack-dev-server";
const isProduction = process.env.NODE_ENV === "production";
@@ -34,7 +36,7 @@ const config: webpack.Configuration = {
oneOf: [
// Image assets
{
test: [/\.avif$/, /\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/, '/\.pdf$/'],
test: [/\.(avif|bmp|.gif|jpe?g|png|pdf)$/],
type: "asset",
parser: {
dataUrlCondition: {
@@ -82,10 +84,35 @@ const config: webpack.Configuration = {
],
plugins: [
!isProduction && "react-refresh/babel",
].filter(Boolean),
],
cacheDirectory: true,
},
},
{ // mdx.js integration
test: /\.mdx?$/,
include: path.resolve(import.meta.dirname, "src"),
use: [
{
loader: "babel-loader",
options: {
presets: [
"@babel/preset-env",
["@babel/preset-react", { runtime: "automatic" }],
"@babel/preset-typescript",
],
plugins: [
!isProduction && "react-refresh/babel",
],
cacheDirectory: true,
}
}, {
loader: "@mdx-js/loader",
options: <Options> {
}
}
],
},
// CSS Loader
{
test: /\.css$/,
@@ -114,20 +141,22 @@ const config: webpack.Configuration = {
template: path.resolve(import.meta.dirname, "public/index.html"),
inject: true,
}),
new WebpackManifestPlugin({
fileName: "manifest.json",
publicPath: "/"
}),
new webpack.DefinePlugin({
"process.env": JSON.stringify(process.env),
}),
new ForkTsCheckerWebpackPlugin({
async: !isProduction,
}),
isProduction
&& new MiniCssExtractPlugin({
isProduction && new MiniCssExtractPlugin({
filename: "static/css/[name].[contenthash:8].css",
chunkFilename: "static/css/[name].[contenthash:8].chunk.css",
}),
!isProduction
&& new ReactRefreshWebpackPlugin(),
].filter(Boolean),
!isProduction && new ReactRefreshWebpackPlugin(),
],
devServer: {
historyApiFallback: true,
port: 3000,