Skip to content

Commit 345b869

Browse files
committed
style: breadcrumb
1 parent 800eb8b commit 345b869

File tree

4 files changed

+322
-207
lines changed

4 files changed

+322
-207
lines changed

src/components/BreadcrumbTitle.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,33 @@ const BreadcrumbTitle = ({
77
gradient = true,
88
sansFont = false,
99
lightStyle = true,
10+
variant = 'default',
1011
}) => {
1112
// Use provided breadcrumbs array, or fallback to default ['fc', 'apps', slug]
1213
const parts = breadcrumbs || (slug ? ['fc', 'apps', slug] : []);
14+
const isBrutalist = variant === 'brutalist';
15+
16+
if (isBrutalist) {
17+
return (
18+
<div className="flex flex-col gap-2 mb-8">
19+
<div className="flex items-center gap-2 font-mono text-[10px] uppercase tracking-[0.3em] text-gray-400">
20+
{parts.map((part, index) => (
21+
<React.Fragment key={index}>
22+
<span className={index === parts.length - 1 ? 'text-emerald-500 font-bold' : ''}>
23+
{part}
24+
</span>
25+
{index < parts.length - 1 && (
26+
<span className="text-gray-600 font-bold "> {'//'}</span>
27+
)}
28+
</React.Fragment>
29+
))}
30+
</div>
31+
<h1 className="text-4xl md:text-6xl font-black tracking-tighter text-white uppercase leading-none">
32+
{title}
33+
</h1>
34+
</div>
35+
);
36+
}
1337

1438
return (
1539
<div

src/pages/apps/BubbleWrapPage.js

Lines changed: 139 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
import React, { useState, useEffect } from 'react';
22
import { Link } from 'react-router-dom';
3-
import { ArrowLeftIcon, CirclesFourIcon } from '@phosphor-icons/react';
4-
import colors from '../../config/colors';
3+
import {
4+
ArrowLeftIcon,
5+
CirclesFourIcon,
6+
ArrowsClockwiseIcon,
7+
InfoIcon,
8+
ShieldCheckIcon,
9+
TrophyIcon
10+
} from '@phosphor-icons/react';
511
import useSeo from '../../hooks/useSeo';
612
import BreadcrumbTitle from '../../components/BreadcrumbTitle';
713
import { useAchievements } from '../../context/AchievementContext';
14+
import GenerativeArt from '../../components/GenerativeArt';
815

9-
const BUBBLE_COUNT = 100; // Number of bubbles
16+
const BUBBLE_COUNT = 100;
1017

1118
const BubbleWrapPage = () => {
1219
useSeo({
@@ -25,8 +32,6 @@ const BubbleWrapPage = () => {
2532
const { unlockAchievement } = useAchievements();
2633
const [bubbles, setBubbles] = useState(Array(BUBBLE_COUNT).fill(false));
2734
const [popCount, setPopCount] = useState(0);
28-
29-
// Audio context for simple pop sound
3035
const [audioContext, setAudioContext] = useState(null);
3136

3237
useEffect(() => {
@@ -48,19 +53,19 @@ const BubbleWrapPage = () => {
4853
oscillator.connect(gainNode);
4954
gainNode.connect(audioContext.destination);
5055

51-
oscillator.type = 'square'; // Change to square wave for a harsher sound
52-
oscillator.frequency.value = 100 + Math.random() * 200; // Lower frequency for more bass
56+
oscillator.type = 'square';
57+
oscillator.frequency.value = 100 + Math.random() * 200;
5358

5459
const now = audioContext.currentTime;
55-
gainNode.gain.setValueAtTime(0.5, now); // Start louder
56-
gainNode.gain.exponentialRampToValueAtTime(0.001, now + 0.08); // Faster and shorter decay
60+
gainNode.gain.setValueAtTime(0.5, now);
61+
gainNode.gain.exponentialRampToValueAtTime(0.001, now + 0.08);
5762

5863
oscillator.start(now);
59-
oscillator.stop(now + 0.08); // Very short duration
64+
oscillator.stop(now + 0.08);
6065
};
6166

6267
const popBubble = (index) => {
63-
if (bubbles[index]) return; // Already popped
68+
if (bubbles[index]) return;
6469

6570
const newBubbles = [...bubbles];
6671
newBubbles[index] = true;
@@ -74,84 +79,138 @@ const BubbleWrapPage = () => {
7479
setPopCount(0);
7580
};
7681

77-
const cardStyle = {
78-
backgroundColor: colors['app-alpha-10'],
79-
borderColor: colors['app-alpha-50'],
80-
color: colors.app,
81-
};
82+
const progress = Math.round((popCount / BUBBLE_COUNT) * 100);
8283

8384
return (
84-
<div className="py-16 sm:py-24">
85-
<div className="mx-auto max-w-7xl px-6 lg:px-8 text-gray-300">
86-
<Link
87-
to="/apps"
88-
className="group text-primary-400 hover:underline flex items-center justify-center gap-2 text-lg mb-4"
89-
>
90-
<ArrowLeftIcon className="text-xl transition-transform group-hover:-translate-x-1" />
91-
Back to Apps
92-
</Link>
93-
<BreadcrumbTitle title="Bubble Wrap" slug="pop" />
94-
<hr className="border-gray-700" />
95-
<div className="flex justify-center items-center mt-16">
96-
<div
97-
className="group bg-transparent border rounded-lg shadow-2xl p-6 flex flex-col justify-between relative transform overflow-hidden h-full w-full max-w-3xl"
98-
style={cardStyle}
99-
>
100-
<div
101-
className="absolute top-0 left-0 w-full h-full opacity-10"
102-
style={{
103-
backgroundImage:
104-
'radial-gradient(circle, white 1px, transparent 1px)',
105-
backgroundSize: '10px 10px',
106-
}}
107-
></div>
108-
<div className="relative z-10 p-1 text-center">
109-
<h1 className="text-3xl font-arvo font-normal mb-4 text-app flex items-center justify-center gap-2">
110-
<CirclesFourIcon size={32} /> Bubble Wrap
111-
</h1>
112-
<hr className="border-gray-700 mb-6" />
113-
114-
<div className="flex justify-between items-center mb-6 px-4">
115-
<div className="text-xl font-bold">Popped: {popCount}</div>
116-
<button
117-
onClick={resetBubbles}
118-
className="px-4 py-2 rounded-md text-sm font-arvo font-normal border transition-colors duration-300 hover:bg-white/10"
119-
style={{
120-
borderColor: cardStyle.color,
121-
color: cardStyle.color,
122-
}}
123-
>
124-
Get a fresh sheet
125-
</button>
126-
</div>
85+
<div className="min-h-screen bg-[#050505] text-white selection:bg-emerald-500/30 font-sans">
86+
<div className="mx-auto max-w-7xl px-6 py-24 md:px-12">
87+
88+
<header className="mb-24">
89+
<Link to="/apps" className="group mb-12 inline-flex items-center gap-2 text-xs font-mono text-gray-500 hover:text-white transition-colors uppercase tracking-[0.3em]">
90+
<ArrowLeftIcon weight="bold" className="transition-transform group-hover:-translate-x-1" />
91+
<span>Applications</span>
92+
</Link>
93+
94+
<div className="flex flex-col md:flex-row md:items-end justify-between gap-12">
95+
<div className="space-y-4">
96+
<BreadcrumbTitle
97+
title="Bubble Wrap"
98+
slug="pop"
99+
variant="brutalist"
100+
/>
101+
<p className="text-xl text-gray-400 max-w-2xl font-light leading-relaxed">
102+
Digital stress relief. Pop virtual cells to satisfy your tactile cravings.
103+
</p>
104+
</div>
105+
</div>
106+
</header>
107+
108+
<div className="grid grid-cols-1 lg:grid-cols-12 gap-12">
109+
110+
{/* Controls & Configuration */}
111+
<div className="lg:col-span-4 space-y-8">
112+
<div className="border border-white/10 bg-white/[0.02] p-8 rounded-sm space-y-10">
113+
<h3 className="font-mono text-[10px] font-bold text-emerald-500 uppercase tracking-widest flex items-center gap-2">
114+
<CirclesFourIcon weight="fill" />
115+
Status
116+
</h3>
117+
118+
<div className="space-y-8">
119+
<div className="space-y-4">
120+
<div className="flex justify-between items-end">
121+
<label className="font-mono text-[10px] text-gray-500 uppercase tracking-widest">Completion</label>
122+
<span className="text-xl font-black text-emerald-500">{progress}%</span>
123+
</div>
124+
<div className="w-full bg-gray-800 h-1 rounded-sm overflow-hidden">
125+
<div
126+
className="bg-emerald-500 h-full transition-all duration-300"
127+
style={{ width: `${progress}%` }}
128+
/>
129+
</div>
130+
</div>
131+
132+
<div className="space-y-4 pt-4 border-t border-white/5">
133+
<div className="flex justify-between items-end">
134+
<label className="font-mono text-[10px] text-gray-500 uppercase">Popped Cells</label>
135+
<span className="text-xl font-black text-white">{popCount} <span className="text-gray-600 text-sm">/ {BUBBLE_COUNT}</span></span>
136+
</div>
137+
</div>
127138

128-
<div className="grid grid-cols-5 sm:grid-cols-10 gap-2 justify-items-center">
129-
{bubbles.map((isPopped, index) => (
130-
<button
131-
key={index}
132-
onClick={() => popBubble(index)}
133-
className={`w-12 h-12 rounded-full border-2 transition-all duration-100 relative overflow-hidden focus:outline-none
134-
${
135-
isPopped
136-
? 'bg-transparent scale-95 opacity-50 border-gray-600'
137-
: 'bg-white/10 hover:bg-white/20 scale-100 cursor-pointer'
138-
}`}
139-
style={{
140-
borderColor: isPopped ? 'gray' : cardStyle.color,
141-
boxShadow: isPopped
142-
? 'inset 0 0 5px rgba(0,0,0,0.5)'
143-
: '0 4px 6px rgba(0,0,0,0.3)',
144-
}}
139+
<button
140+
onClick={resetBubbles}
141+
className="w-full py-3 border border-white/10 hover:border-white text-gray-400 hover:text-white transition-all text-xs font-black uppercase tracking-widest flex items-center justify-center gap-2"
145142
>
146-
{!isPopped && (
147-
<div className="absolute top-2 left-2 w-3 h-3 bg-white rounded-full opacity-30 blur-[1px]"></div>
148-
)}
143+
<ArrowsClockwiseIcon size={16} weight="bold" />
144+
Reset Sheet
149145
</button>
150-
))}
151146
</div>
152147
</div>
148+
149+
<div className="p-8 border border-white/10 bg-white/[0.01] rounded-sm flex items-start gap-4">
150+
<InfoIcon size={24} className="text-gray-700 shrink-0" />
151+
<p className="text-[10px] font-mono uppercase tracking-[0.2em] leading-relaxed text-gray-500">
152+
Popping all cells triggers a completion event. Use this tool to reduce anxiety or procrastinate effectively.
153+
</p>
154+
</div>
155+
</div>
156+
157+
{/* Game Area */}
158+
<div className="lg:col-span-8 space-y-12">
159+
<div className="relative border border-white/10 bg-white/[0.02] p-8 md:p-12 rounded-sm overflow-hidden group min-h-[600px] flex items-center justify-center">
160+
<div className="absolute inset-0 opacity-[0.03] pointer-events-none grayscale">
161+
<GenerativeArt seed="bubblewrap" className="w-full h-full" />
162+
</div>
163+
164+
<div className="relative z-10 w-full max-w-2xl">
165+
<div className="grid grid-cols-10 gap-2 sm:gap-3 md:gap-4 justify-items-center">
166+
{bubbles.map((isPopped, index) => (
167+
<button
168+
key={index}
169+
onClick={() => popBubble(index)}
170+
className={`w-8 h-8 sm:w-10 sm:h-10 rounded-full border-2 transition-all duration-200 relative overflow-hidden focus:outline-none group/bubble
171+
${
172+
isPopped
173+
? 'bg-transparent border-gray-800 scale-90'
174+
: 'bg-emerald-500/10 border-emerald-500/30 hover:bg-emerald-500/20 hover:border-emerald-500 hover:scale-105 cursor-pointer'
175+
}`}
176+
>
177+
{!isPopped && (
178+
<>
179+
<div className="absolute inset-0 opacity-0 group-hover/bubble:opacity-100 transition-opacity bg-emerald-400/10" />
180+
<div className="absolute top-1.5 left-1.5 w-2 h-2 sm:w-2.5 sm:h-2.5 bg-white rounded-full opacity-20 blur-[0.5px] pointer-events-none" />
181+
</>
182+
)}
183+
{isPopped && (
184+
<div className="absolute inset-0 flex items-center justify-center">
185+
<div className="w-1 h-1 bg-gray-800 rounded-full" />
186+
</div>
187+
)}
188+
</button>
189+
))}
190+
</div>
191+
</div>
192+
</div>
193+
194+
<div className="p-8 border border-white/10 bg-white/[0.01] rounded-sm flex items-center justify-between gap-6">
195+
<div className="flex items-center gap-4">
196+
<ShieldCheckIcon size={32} className="text-emerald-500/50" />
197+
<span className="text-[10px] font-mono text-gray-500 uppercase tracking-widest">Audio Engine: Active</span>
198+
</div>
199+
{popCount === BUBBLE_COUNT && (
200+
<div className="flex items-center gap-2 text-emerald-500 animate-pulse">
201+
<TrophyIcon weight="fill" />
202+
<span className="text-[10px] font-mono uppercase tracking-widest font-bold">Sheet Cleared</span>
203+
</div>
204+
)}
205+
</div>
153206
</div>
207+
154208
</div>
209+
210+
<footer className="mt-32 pt-12 border-t border-white/10 flex flex-col md:flex-row justify-between items-center gap-6 text-gray-600 font-mono text-[10px] uppercase tracking-[0.3em]">
211+
<span>Fezcodex_Bubble_Wrap_v1.0.0</span>
212+
<span className="text-gray-800">SIMULATION // ACTIVE</span>
213+
</footer>
155214
</div>
156215
</div>
157216
);

src/pages/apps/CipherStudioPage.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { useToast } from '../../hooks/useToast';
55
import useSeo from '../../hooks/useSeo';
66
import CustomDropdown from '../../components/CustomDropdown';
77
import GenerativeArt from '../../components/GenerativeArt';
8+
import BreadcrumbTitle from '../../components/BreadcrumbTitle';
89

910
const CIPHERS = [
1011
{ label: 'Atbash', value: 'atbash' },
@@ -113,9 +114,11 @@ const CipherStudioPage = () => {
113114

114115
<div className="flex flex-col md:flex-row md:items-end justify-between gap-12">
115116
<div className="space-y-4">
116-
<h1 className="text-6xl md:text-8xl font-black tracking-tighter text-white leading-none uppercase">
117-
{appName}
118-
</h1>
117+
<BreadcrumbTitle
118+
title={appName}
119+
slug="cipher"
120+
variant="brutalist"
121+
/>
119122
<p className="text-xl text-gray-400 max-w-2xl font-light leading-relaxed">
120123
Classic message encryption. Choose an algorithm to transform your messages into secure character sequences.
121124
</p>

0 commit comments

Comments
 (0)