Skip to content

Commit 5fe3dd2

Browse files
committed
style: brutalist overhaul for TCG Maker, CodeModal, and TextTransformer and SEO for apps
1 parent bbdb11d commit 5fe3dd2

23 files changed

+158
-161
lines changed

public/timeline/timeline.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
"items": [
1+
[
22
{
33
"date": "2025-12-21",
44
"title": "Neural Net Online",
5-
"description": "Deployed the 3D Knowledge Graph Visualization Protocol (/graph). Now users can navigate the site via an interactive neural network.",
5+
"description": "Deployed the 3D Knowledge Graph Visualization Protocol. Now users can navigate the site via an interactive neural network.",
66
"type": "feature",
7-
"icon": "Graph"
7+
"icon": "Graph",
8+
"link": "/graph"
89
},
910
{
1011
"date": "2025-12-21",

src/components/CodeModal.js

Lines changed: 63 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import React, { useEffect } from 'react';
2-
import { X } from '@phosphor-icons/react';
2+
import { X, Code } from '@phosphor-icons/react';
33
import { motion, AnimatePresence } from 'framer-motion';
44
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
55
import { customTheme } from '../utils/customTheme';
6+
import GenerativeArt from './GenerativeArt';
67

78
const CodeModal = ({ isOpen, onClose, children, language }) => {
89
useEffect(() => {
@@ -20,37 +21,75 @@ const CodeModal = ({ isOpen, onClose, children, language }) => {
2021
<AnimatePresence>
2122
{isOpen && (
2223
<motion.div
23-
className="fixed inset-0 bg-black bg-opacity-75 flex justify-center items-center z-50 p-4"
24+
className="fixed inset-0 bg-black/90 backdrop-blur-sm flex justify-center items-center z-50 p-4 md:p-8"
2425
onClick={onClose}
2526
initial={{ opacity: 0 }}
2627
animate={{ opacity: 1 }}
2728
exit={{ opacity: 0 }}
2829
>
2930
<motion.div
30-
className="relative bg-gray-800 rounded-lg shadow-lg p-6 w-3/4 h-3/4 prose prose-xl prose-dark max-w-none"
31+
className="relative bg-[#050505] border border-white/10 rounded-sm shadow-2xl w-full max-w-5xl h-[85vh] flex flex-col overflow-hidden group"
3132
onClick={(e) => e.stopPropagation()}
32-
initial={{ scale: 0.8, opacity: 0 }}
33-
animate={{ scale: 1, opacity: 1 }}
34-
exit={{ scale: 0.8, opacity: 0 }}
35-
transition={{ duration: 0.2 }}
33+
initial={{ scale: 0.95, opacity: 0, y: 20 }}
34+
animate={{ scale: 1, opacity: 1, y: 0 }}
35+
exit={{ scale: 0.95, opacity: 0, y: 20 }}
36+
transition={{ duration: 0.3, ease: 'circOut' }}
3637
>
37-
<button
38-
onClick={onClose}
39-
className="absolute top-2 right-2 text-white text-2xl bg-gray-800 rounded-full p-2 hover:bg-gray-700 focus:outline-none"
40-
>
41-
<X size={24} weight="bold" />
42-
</button>
43-
<SyntaxHighlighter
44-
style={customTheme}
45-
language={language}
46-
PreTag="pre"
47-
className="overflow-auto h-full"
48-
codeTagProps={{
49-
style: { fontFamily: "'JetBrains Mono', monospace" },
50-
}}
51-
>
52-
{children}
53-
</SyntaxHighlighter>
38+
{/* Background Art */}
39+
<div className="absolute inset-0 opacity-[0.03] pointer-events-none">
40+
<GenerativeArt seed="CODE_MODAL" className="w-full h-full" />
41+
</div>
42+
{/* Header */}
43+
<div className="flex items-center justify-between px-6 py-4 border-b border-white/10 bg-white/[0.02] z-10">
44+
<div className="flex items-center gap-3">
45+
<Code size={20} className="text-emerald-500" weight="fill" />
46+
<span className="font-mono text-xs font-bold uppercase tracking-[0.2em] text-gray-400">
47+
Code_Viewer
48+
<span className="text-gray-600 mx-2">::</span>
49+
<span className="text-emerald-500">{language || 'TEXT'}</span>
50+
</span>
51+
</div>
52+
<button
53+
onClick={onClose}
54+
className="group p-2 hover:bg-red-500/10 border border-transparent hover:border-red-500/50 transition-all rounded-sm"
55+
>
56+
<X size={20} className="text-gray-500 group-hover:text-red-500 transition-colors" />
57+
</button>
58+
</div>
59+
60+
{/* Content */}
61+
<div className="flex-1 overflow-auto relative z-10 custom-scrollbar bg-black/40">
62+
<SyntaxHighlighter
63+
style={customTheme}
64+
language={language}
65+
PreTag="div"
66+
className="!bg-transparent !p-6 !m-0 h-full !font-mono !text-sm md:!text-base"
67+
showLineNumbers={true}
68+
lineNumberStyle={{
69+
minWidth: '2.5em',
70+
paddingRight: '1.5em',
71+
color: '#333',
72+
textAlign: 'right',
73+
fontFamily: '"JetBrains Mono", monospace'
74+
}}
75+
codeTagProps={{
76+
style: { fontFamily: "'JetBrains Mono', monospace" },
77+
}}
78+
>
79+
{children}
80+
</SyntaxHighlighter>
81+
</div>
82+
83+
{/* Footer Status Bar */}
84+
<div className="px-6 py-2 border-t border-white/10 bg-white/[0.02] flex justify-between items-center z-10">
85+
<span className="font-mono text-[10px] text-gray-600 uppercase tracking-widest">
86+
ReadOnly Mode
87+
</span>
88+
<span className="font-mono text-[10px] text-emerald-500/50 uppercase tracking-widest">
89+
• Connected
90+
</span>
91+
</div>
92+
5493
</motion.div>
5594
</motion.div>
5695
)}

src/hooks/useSeo.js

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { useEffect } from 'react';
2+
import { useLocation } from 'react-router-dom';
23

34
function useSeo({
45
title,
@@ -12,6 +13,8 @@ function useSeo({
1213
twitterDescription,
1314
twitterImage,
1415
}) {
16+
const location = useLocation();
17+
1518
useEffect(() => {
1619
// Set document title
1720
if (title) {
@@ -46,6 +49,13 @@ function useSeo({
4649
}
4750
}
4851

52+
// Determine default images for apps
53+
const isAppPath = location.pathname.startsWith('/apps');
54+
const defaultAppImage = '/images/asset/ogtitle-apps.png';
55+
const finalOgImage = ogImage || (isAppPath ? defaultAppImage : null);
56+
const finalTwitterImage =
57+
twitterImage || (isAppPath ? defaultAppImage : null);
58+
4959
// Set Open Graph meta tags
5060
if (ogTitle) {
5161
const metaOgTitle = document.querySelector('meta[property="og:title"]');
@@ -73,14 +83,14 @@ function useSeo({
7383
}
7484
}
7585

76-
if (ogImage) {
86+
if (finalOgImage) {
7787
const metaOgImage = document.querySelector('meta[property="og:image"]');
7888
if (metaOgImage) {
79-
metaOgImage.setAttribute('content', ogImage);
89+
metaOgImage.setAttribute('content', finalOgImage);
8090
} else {
8191
const newMeta = document.createElement('meta');
8292
newMeta.setAttribute('property', 'og:image');
83-
newMeta.setAttribute('content', ogImage);
93+
newMeta.setAttribute('content', finalOgImage);
8494
document.head.appendChild(newMeta);
8595
}
8696
}
@@ -128,20 +138,21 @@ function useSeo({
128138
}
129139
}
130140

131-
if (twitterImage) {
141+
if (finalTwitterImage) {
132142
const metaTwitterImage = document.querySelector(
133143
'meta[name="twitter:image"]',
134144
);
135145
if (metaTwitterImage) {
136-
metaTwitterImage.setAttribute('content', twitterImage);
146+
metaTwitterImage.setAttribute('content', finalTwitterImage);
137147
} else {
138148
const newMeta = document.createElement('meta');
139149
newMeta.setAttribute('name', 'twitter:image');
140-
newMeta.setAttribute('content', twitterImage);
150+
newMeta.setAttribute('content', finalTwitterImage);
141151
document.head.appendChild(newMeta);
142152
}
143153
}
144154
}, [
155+
location.pathname,
145156
title,
146157
description,
147158
keywords,

src/pages/apps/BananaConverterPage.js

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,11 @@ const BananaConverterPage = () => {
3232
'fun',
3333
'measurement',
3434
],
35-
ogTitle: 'Banana for Scale Converter | Fezcodex',
36-
ogDescription:
37-
'Convert measuring units into the internet standard: Bananas.',
38-
ogImage: '/images/asset/ogtitle.png',
35+
ogTitle: 'Banana Converter | Fezcodex',
36+
ogDescription: 'Convert any unit of measurement into bananas.',
3937
twitterCard: 'summary_large_image',
40-
twitterTitle: 'Banana for Scale Converter | Fezcodex',
41-
twitterDescription:
42-
'Convert measuring units into the internet standard: Bananas.',
43-
twitterImage: '/images/asset/ogtitle.png',
38+
twitterTitle: 'Banana Converter | Fezcodex',
39+
twitterDescription: 'Convert any unit of measurement into bananas.',
4440
});
4541

4642
const [inputValue, setInputValue] = useState('');

src/pages/apps/BubbleWrapPage.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,10 @@ const BubbleWrapPage = () => {
2121
description: 'Pop some virtual bubble wrap to relieve stress.',
2222
keywords: ['Fezcodex', 'bubble wrap', 'stress relief', 'pop', 'game'],
2323
ogTitle: 'Bubble Wrap | Fezcodex',
24-
ogDescription: 'Pop some virtual bubble wrap to relieve stress.',
25-
ogImage: '/images/asset/ogtitle.png',
24+
ogDescription: 'Satisfy your popping urges with virtual bubble wrap.',
2625
twitterCard: 'summary_large_image',
2726
twitterTitle: 'Bubble Wrap | Fezcodex',
28-
twitterDescription: 'Pop some virtual bubble wrap to relieve stress.',
29-
twitterImage: '/images/asset/ogtitle.png',
27+
twitterDescription: 'Satisfy your popping urges with virtual bubble wrap.',
3028
});
3129

3230
const { unlockAchievement } = useAchievements();

src/pages/apps/CardGamePage.js

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,15 +55,11 @@ const CardGamePage = () => {
5555
'game',
5656
'react game',
5757
],
58-
ogTitle: 'Higher or Lower Card Game | Fezcodex',
59-
ogDescription:
60-
'Play a simple Higher or Lower card game. Guess if the next card will be higher or lower than the current one.',
61-
ogImage: '/images/asset/ogtitle.png',
58+
ogTitle: 'Card Game | Fezcodex',
59+
ogDescription: 'Play a simple card game in your browser.',
6260
twitterCard: 'summary_large_image',
63-
twitterTitle: 'Higher or Lower Card Game | Fezcodex',
64-
twitterDescription:
65-
'Play a simple Higher or Lower card game. Guess if the next card will be higher or lower than the current one.',
66-
twitterImage: '/images/asset/ogtitle.png',
61+
twitterTitle: 'Card Game | Fezcodex',
62+
twitterDescription: 'Play a simple card game in your browser.',
6763
});
6864

6965
const { addToast } = useToast();

src/pages/apps/CodeSeancePage.js

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,8 @@ import { useAchievements } from '../../context/AchievementContext';
77
const CodeSeancePage = () => {
88
useSeo({
99
title: 'Code Seance | Fezcodex',
10-
description: 'Communicate with the ghosts of deprecated code.',
11-
keywords: ['Fezcodex', 'seance', 'game', 'terminal', 'horror', 'coding'],
12-
ogTitle: 'Code Seance | Fezcodex',
13-
ogDescription: 'Communicate with the ghosts of deprecated code.',
14-
ogImage: '/images/asset/ogtitle.png',
15-
twitterCard: 'summary_large_image',
16-
twitterTitle: 'Code Seance | Fezcodex',
17-
twitterDescription: 'Communicate with the ghosts of deprecated code.',
18-
twitterImage: '/images/asset/ogtitle.png',
10+
description: 'An interactive coding experience with a mystical twist.',
11+
keywords: ['coding', 'seance', 'interactive', 'terminal'],
1912
});
2013

2114
const { unlockAchievement } = useAchievements();

src/pages/apps/ConnectFourPage.js

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,8 @@ import BreadcrumbTitle from '../../components/BreadcrumbTitle';
99
const ConnectFourPage = () => {
1010
useSeo({
1111
title: 'Connect Four | Fezcodex',
12-
description:
13-
'Play the classic game of Connect Four against another player or AI.',
14-
keywords: ['Fezcodex', 'connect four', 'game', 'fun app'],
15-
ogTitle: 'Connect Four | Fezcodex',
16-
ogDescription:
17-
'Play the classic game of Connect Four against another player or AI.',
18-
ogImage: '/images/asset/ogtitle.png',
19-
twitterCard: 'summary_large_image',
20-
twitterTitle: 'Connect Four | Fezcodex',
21-
twitterDescription:
22-
'Play the classic game of Connect Four against another player or AI.',
23-
twitterImage: '/images/asset/ogtitle.png',
12+
description: 'Play the classic Connect Four game in your browser.',
13+
keywords: ['Connect Four', 'game', 'React', 'strategy'],
2414
});
2515

2616
const { addToast } = useToast();

src/pages/apps/DiceRollerPage.js

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,10 @@ const DiceRollerPage = () => {
2828
'rpg',
2929
],
3030
ogTitle: 'Dice Roller | Fezcodex',
31-
ogDescription:
32-
'Roll various types of dice (d4, d6, d8, d10, d12, d20, d100) for your games and simulations.',
33-
ogImage: '/images/asset/ogtitle.png',
31+
ogDescription: 'Roll virtual dice for games and tabletop adventures.',
3432
twitterCard: 'summary_large_image',
3533
twitterTitle: 'Dice Roller | Fezcodex',
36-
twitterDescription:
37-
'Roll various types of dice (d4, d6, d8, d10, d12, d20, d100) for your games and simulations.',
38-
twitterImage: '/images/asset/ogtitle.png',
34+
twitterDescription: 'Roll virtual dice for games and tabletop adventures.',
3935
});
4036

4137
const { addToast } = useToast();

src/pages/apps/ExcuseGeneratorPage.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,12 +115,12 @@ const ExcuseGeneratorPage = () => {
115115
'random generator',
116116
],
117117
ogTitle: 'Excuse Generator | Fezcodex',
118-
ogDescription: 'Generate funny and absurd excuses for any situation.',
119-
ogImage: '/images/asset/ogtitle.png',
118+
ogDescription:
119+
'Generate creative and humorous excuses for any situation.',
120120
twitterCard: 'summary_large_image',
121121
twitterTitle: 'Excuse Generator | Fezcodex',
122-
twitterDescription: 'Generate funny and absurd excuses for any situation.',
123-
twitterImage: '/images/asset/ogtitle.png',
122+
twitterDescription:
123+
'Generate creative and humorous excuses for any situation.',
124124
});
125125

126126
const [currentExcuse, setCurrentExcuse] = useState(

0 commit comments

Comments
 (0)