Skip to content

Commit 9bad760

Browse files
committed
init
1 parent a947d86 commit 9bad760

15 files changed

Lines changed: 456 additions & 106 deletions

File tree

css/style.css

Lines changed: 117 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,25 @@ body {
3333
color: var(--text-primary);
3434
line-height: 1.6;
3535
font-size: 16px;
36+
min-height: 100vh;
37+
display: flex;
38+
flex-direction: column;
39+
}
40+
41+
#root {
42+
flex: 1;
43+
display: flex;
44+
flex-direction: column;
45+
}
46+
47+
.app {
48+
flex: 1;
49+
display: flex;
50+
flex-direction: column;
51+
}
52+
53+
main {
54+
flex: 1;
3655
}
3756

3857
.container {
@@ -50,6 +69,52 @@ header {
5069
z-index: 100;
5170
}
5271

72+
.dev-panel {
73+
position: fixed;
74+
bottom: 20px;
75+
right: 20px;
76+
display: flex;
77+
align-items: center;
78+
gap: 1rem;
79+
background-color: var(--bg-white);
80+
padding: 0.75rem 1rem;
81+
border-radius: var(--radius-md);
82+
box-shadow: var(--shadow-lg);
83+
z-index: 1000;
84+
}
85+
86+
.dev-badge {
87+
background-color: #ef4444;
88+
color: white;
89+
padding: 0.25rem 0.75rem;
90+
border-radius: var(--radius-sm);
91+
font-size: 0.75rem;
92+
font-weight: 600;
93+
}
94+
95+
.dev-toggle {
96+
background-color: var(--primary-color);
97+
color: white;
98+
border: none;
99+
padding: 0.5rem 1rem;
100+
border-radius: var(--radius-sm);
101+
font-size: 0.875rem;
102+
cursor: pointer;
103+
font-weight: 500;
104+
}
105+
106+
.dev-toggle:hover {
107+
background-color: var(--primary-dark);
108+
}
109+
110+
.loading {
111+
display: flex;
112+
justify-content: center;
113+
align-items: center;
114+
min-height: 200px;
115+
color: var(--text-secondary);
116+
}
117+
53118
nav {
54119
display: flex;
55120
justify-content: space-between;
@@ -213,12 +278,63 @@ nav {
213278
color: var(--text-primary);
214279
}
215280

281+
/* Empty State */
282+
.empty-state {
283+
display: flex;
284+
flex-direction: column;
285+
align-items: center;
286+
justify-content: center;
287+
padding: 4rem 2rem;
288+
text-align: center;
289+
background-color: var(--bg-white);
290+
border-radius: var(--radius-lg);
291+
box-shadow: var(--shadow-sm);
292+
max-width: 400px;
293+
margin: 0 auto;
294+
}
295+
296+
.empty-icon {
297+
margin-bottom: 1.5rem;
298+
}
299+
300+
.empty-state h3 {
301+
font-size: 1.5rem;
302+
font-weight: 600;
303+
color: var(--text-primary);
304+
margin-bottom: 0.75rem;
305+
}
306+
307+
.empty-state p {
308+
color: var(--text-secondary);
309+
margin-bottom: 2rem;
310+
}
311+
312+
.empty-spinner {
313+
display: flex;
314+
justify-content: center;
315+
}
316+
317+
.spinner {
318+
width: 40px;
319+
height: 40px;
320+
border: 3px solid var(--border-color);
321+
border-top-color: var(--primary-color);
322+
border-radius: 50%;
323+
animation: spin 1s linear infinite;
324+
}
325+
326+
@keyframes spin {
327+
to {
328+
transform: rotate(360deg);
329+
}
330+
}
331+
216332
/* Footer */
217333
footer {
218334
background-color: var(--bg-white);
219335
border-top: 1px solid var(--border-color);
220336
padding: 2rem 0;
221-
margin-top: 4rem;
337+
margin-top: auto;
222338
}
223339

224340
footer p {

index.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,17 @@
1414
</head>
1515
<body>
1616
<div id="root"></div>
17+
<script type="text/babel" src="js/data/config.js"></script>
18+
<script type="text/babel" src="js/data/mock.js"></script>
19+
<script type="text/babel" src="js/data/articles.js"></script>
20+
<script type="text/babel" src="js/data/works.js"></script>
21+
<script type="text/babel" src="js/data/profile.js"></script>
1722
<script type="text/babel" src="js/components/Header.js"></script>
1823
<script type="text/babel" src="js/components/Footer.js"></script>
1924
<script type="text/babel" src="js/components/Hero.js"></script>
2025
<script type="text/babel" src="js/components/Card.js"></script>
2126
<script type="text/babel" src="js/components/WorkItem.js"></script>
27+
<script type="text/babel" src="js/components/EmptyState.js"></script>
2228
<script type="text/babel" src="js/pages/HomePage.js"></script>
2329
<script type="text/babel" src="js/pages/WorksPage.js"></script>
2430
<script type="text/babel" src="js/pages/ArticlesPage.js"></script>

js/app.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
const { useState, useEffect } = React;
22

3+
const DevPanel = ({ onToggle }) => {
4+
return (
5+
<div className="dev-panel">
6+
<span className="dev-badge">Dev Mode</span>
7+
<button className="dev-toggle" onClick={onToggle}>
8+
{Config.dataSource === 'mock' ? 'Exit Dev Mode' : 'Switch to Mock'}
9+
</button>
10+
</div>
11+
);
12+
};
13+
314
const App = () => {
415
const [currentPage, setCurrentPage] = useState('home');
516

@@ -22,6 +33,10 @@ const App = () => {
2233
window.scrollTo({ top: 0, behavior: 'smooth' });
2334
};
2435

36+
const handleToggleMode = () => {
37+
Config.setMode(!Config.isDevelopment);
38+
};
39+
2540
const renderPage = () => {
2641
switch (currentPage) {
2742
case 'works':
@@ -37,6 +52,7 @@ const App = () => {
3752

3853
return (
3954
<div className="app">
55+
{Config.isDevelopment && <DevPanel onToggle={handleToggleMode} />}
4056
<Header currentPage={currentPage} onNavigate={handleNavigate} />
4157
{renderPage()}
4258
<Footer />

js/components/EmptyState.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
const EmptyState = ({ title, description }) => {
2+
return (
3+
<div className="empty-state">
4+
<div className="empty-icon">
5+
<i className="fa-solid fa-inbox" style={{ fontSize: '3rem', color: '#94a3b8' }}></i>
6+
</div>
7+
<h3>{title || 'Coming Soon'}</h3>
8+
<p>{description || 'Content is being prepared and will be available soon.'}</p>
9+
<div className="empty-spinner">
10+
<div className="spinner"></div>
11+
</div>
12+
</div>
13+
);
14+
};

js/data/articles.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
const ArticleData = {
2+
getAll: async () => {
3+
if (Config.dataSource === 'mock') {
4+
return MockData.articles;
5+
}
6+
7+
try {
8+
const response = await fetch('js/data/articles.json');
9+
const data = await response.json();
10+
return data.filter(article => article.published);
11+
} catch (error) {
12+
console.error('Failed to load articles:', error);
13+
return [];
14+
}
15+
},
16+
17+
getBySlug: async (slug) => {
18+
const articles = await ArticleData.getAll();
19+
return articles.find(article => article.slug === slug);
20+
},
21+
22+
getFeatured: async () => {
23+
const articles = await ArticleData.getAll();
24+
return articles.find(article => article.featured && article.published);
25+
}
26+
};

js/data/articles.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[
2+
3+
]

js/data/config.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
const Config = {
2+
isDevelopment: false,
3+
dataSource: 'production',
4+
5+
init: () => {
6+
const urlParams = new URLSearchParams(window.location.search);
7+
const isDev = urlParams.get('dev') === 'true';
8+
9+
Config.isDevelopment = isDev;
10+
Config.dataSource = isDev ? 'mock' : 'production';
11+
},
12+
13+
setMode: (isDev) => {
14+
Config.isDevelopment = isDev;
15+
Config.dataSource = isDev ? 'mock' : 'production';
16+
const url = new URL(window.location.href);
17+
if (isDev) {
18+
url.searchParams.set('dev', 'true');
19+
} else {
20+
url.searchParams.delete('dev');
21+
}
22+
window.location.href = url.toString();
23+
}
24+
};
25+
26+
Config.init();

js/data/mock.js

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
const MockData = {
2+
profile: {
3+
name: 'ZhaoJinhao',
4+
title: 'Software Developer → Component Designer',
5+
bio: 'Hi, I\'m ZhaoJinhao.\n\nWith 5 years of experience as a software developer, I\'ve recently embarked on an exciting transition into component design.\n\nMy focus lies at the intersection of user experience, accessibility, and graceful degradation—crafting digital experiences that work beautifully for everyone.',
6+
avatar: 'https://github.com/user-attachments/assets/dfaf3d94-5a06-49fd-983d-be76a4979251',
7+
email: 'murisanzhao@gmail.com',
8+
dribbble: 'https://dribbble.com/muri-chiu'
9+
},
10+
11+
articles: [
12+
{
13+
id: 1,
14+
slug: 'designing-accessible-components',
15+
title: 'Designing Accessible Components',
16+
tag: 'Accessibility',
17+
description: 'Creating UI components that are usable by everyone, including users with disabilities.',
18+
date: 'May 10, 2024',
19+
featured: true,
20+
published: true,
21+
content: `
22+
<p>Accessibility is not an afterthought; it's a fundamental aspect of good design.</p>
23+
<h3>Key Principles</h3>
24+
<ul>
25+
<li><strong>Perceivable:</strong> Information must be presentable to users in ways they can perceive.</li>
26+
<li><strong>Operable:</strong> User interface components and navigation must be operable.</li>
27+
<li><strong>Understandable:</strong> Information and operation must be understandable.</li>
28+
<li><strong>Robust:</strong> Content must be interpretable by a wide variety of user agents.</li>
29+
</ul>
30+
<blockquote>"The power of the Web is in its universality." — Tim Berners-Lee</blockquote>
31+
`
32+
},
33+
{
34+
id: 2,
35+
slug: 'graceful-degradation',
36+
title: 'Graceful Degradation Strategies',
37+
tag: 'Performance',
38+
description: 'Building resilient applications that work well even when conditions aren\'t ideal.',
39+
date: 'April 25, 2024',
40+
featured: false,
41+
published: false,
42+
content: '<p>Content coming soon...</p>'
43+
},
44+
{
45+
id: 3,
46+
slug: 'user-centered-design',
47+
title: 'User-Centered Design Process',
48+
tag: 'Product Design',
49+
description: 'How to put users at the center of your design workflow.',
50+
date: 'April 10, 2024',
51+
featured: false,
52+
published: false,
53+
content: '<p>Content coming soon...</p>'
54+
}
55+
],
56+
57+
works: [
58+
{
59+
id: 1,
60+
title: 'Accessible Form Components Library',
61+
description: 'A comprehensive library of WCAG 2.1 compliant form components.',
62+
techStack: ['React', 'TypeScript', 'WCAG 2.1', 'ARIA'],
63+
featured: true,
64+
published: true
65+
},
66+
{
67+
id: 2,
68+
title: 'Design System Foundation',
69+
description: 'Foundational design tokens and patterns for consistent product design.',
70+
techStack: ['CSS Variables', 'Design Tokens', 'Figma'],
71+
featured: true,
72+
published: true
73+
},
74+
{
75+
id: 3,
76+
title: 'Progressive Image Loading',
77+
description: 'Image optimization with graceful degradation strategies.',
78+
techStack: ['JavaScript', 'Web Performance'],
79+
featured: false,
80+
published: false
81+
}
82+
]
83+
};

js/data/profile.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
const ProfileData = {
2+
get: () => {
3+
if (Config.dataSource === 'mock') {
4+
return MockData.profile;
5+
}
6+
return MockData.profile;
7+
}
8+
};

js/data/works.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
const WorkData = {
2+
getAll: async () => {
3+
if (Config.dataSource === 'mock') {
4+
return MockData.works;
5+
}
6+
7+
try {
8+
const response = await fetch('js/data/works.json');
9+
const data = await response.json();
10+
return data.filter(work => work.published);
11+
} catch (error) {
12+
console.error('Failed to load works:', error);
13+
return [];
14+
}
15+
},
16+
17+
getFeatured: async () => {
18+
const works = await WorkData.getAll();
19+
return works.filter(work => work.featured && work.published);
20+
}
21+
};

0 commit comments

Comments
 (0)