Skip to content

Commit 1a36943

Browse files
committed
article and responsiveness
1 parent 02a30bc commit 1a36943

8 files changed

Lines changed: 149 additions & 13 deletions

File tree

index.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@
2323
<link rel="preconnect" href="https://fonts.googleapis.com">
2424
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
2525
<link href="https://fonts.googleapis.com/css2?family=Kalam:wght@300;400;700&family=Caveat:wght@400;500;600;700&display=swap" rel="stylesheet">
26+
27+
<!-- Preload critical images for better performance -->
28+
<link rel="preload" as="image" href="/assets/raylogo.png" />
29+
<link rel="preload" as="image" href="/assets/ray.png" />
30+
<link rel="preload" as="image" href="/assets/aishift.png" />
31+
2632
<title>Regan Maharjan Portfolio Website</title>
2733
</head>
2834

src/App.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { Accessibility } from './components/pages/Accessibility';
1515
import { Contact } from './components/pages/Contact';
1616
import { Photography } from './components/pages/Photography';
1717
import { CMS } from './components/pages/CMS';
18+
import { preloadCriticalImages } from './utils/preloadImages';
1819

1920
function AppContent() {
2021
const location = useLocation();
@@ -39,6 +40,11 @@ function AppContent() {
3940
};
4041
}, []);
4142

43+
// Preload critical images on app mount
44+
useEffect(() => {
45+
preloadCriticalImages();
46+
}, []);
47+
4248
return (
4349
<div className="min-h-screen bg-background flex flex-col">
4450
{!isHomePage && <Navigation />}

src/components/pages/Contact.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ export function Contact() {
160160
<ul className="space-y-3">
161161
{opportunities.items.map((item) => (
162162
<li key={item} className="flex items-start gap-2">
163-
<span className="text-blue-600 mt-1"></span>
163+
<span className="text-yellow-500 mt-1 flex-shrink-0"></span>
164164
<span className="text-muted-foreground">{item}</span>
165165
</li>
166166
))}

src/components/pages/Experience.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ export function Experience() {
9191
<ul className="grid md:grid-cols-2 gap-3">
9292
{exp.highlights.map((highlight) => (
9393
<li key={highlight} className="flex items-start gap-2">
94-
<span className="text-blue-600 mt-1"></span>
94+
<span className="text-yellow-500 mt-1 flex-shrink-0"></span>
9595
<span className="text-muted-foreground">{highlight}</span>
9696
</li>
9797
))}

src/components/pages/StoryDetail.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import contentData from '../../data/content';
77
import { useState } from 'react';
88
import { useMetaTags } from '../../utils/useMetaTags';
99
import { getImageUrl } from '../../utils/imageUtils';
10+
import { preloadStoryImages } from '../../utils/preloadImages';
1011

1112
const iconMap: Record<string, typeof Globe> = {
1213
'Globe': Globe,
@@ -77,6 +78,12 @@ export function StoryDetail() {
7778
navigate('/impact');
7879
return;
7980
}
81+
82+
// Preload story images for smoother experience
83+
if (storyId) {
84+
preloadStoryImages(storyId);
85+
}
86+
8087
// Scroll to top when story loads
8188
window.scrollTo({ top: 0, behavior: 'instant' });
8289
}, [selectedStory, navigate, storyId]);
@@ -205,7 +212,7 @@ export function StoryDetail() {
205212
<ul className="grid md:grid-cols-2 gap-3">
206213
{selectedStory.content.work.map((item) => (
207214
<li key={item} className="flex items-start gap-2">
208-
<span className="text-blue-600 mt-1"></span>
215+
<span className="text-yellow-500 mt-1 flex-shrink-0"></span>
209216
<span className="text-gray-900">{item}</span>
210217
</li>
211218
))}

src/components/pages/StoryOfAdventureDetail.tsx

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import contentData from '../../data/content';
77
import { getImageUrl } from '../../utils/imageUtils';
88
import { useMetaTags } from '../../utils/useMetaTags';
99
import { Comments } from '../Comments';
10+
import { preloadStoryImages } from '../../utils/preloadImages';
1011

1112
const iconMap: Record<string, typeof Globe> = {
1213
'Globe': Globe,
@@ -90,6 +91,11 @@ export function StoryOfAdventureDetail() {
9091
return;
9192
}
9293

94+
// Preload story images for smoother experience
95+
if (storyId) {
96+
preloadStoryImages(storyId);
97+
}
98+
9399
// Scroll to top when story loads
94100
window.scrollTo({ top: 0, behavior: 'instant' });
95101

@@ -290,9 +296,12 @@ export function StoryOfAdventureDetail() {
290296
// Handle list objects
291297
if (item.type === 'list' && Array.isArray(item.items)) {
292298
return (
293-
<ul key={index} className="list-disc list-outside space-y-3 text-xl text-gray-900 leading-relaxed ml-6 marker:text-gray-600">
299+
<ul key={index} className="space-y-3 text-xl text-gray-900 leading-relaxed ml-6 list-none">
294300
{item.items.map((listItem: string, listIndex: number) => (
295-
<li key={listIndex} className="pl-2">{listItem}</li>
301+
<li key={listIndex} className="pl-2 flex items-start">
302+
<span className="text-yellow-500 mr-3 mt-1 flex-shrink-0"></span>
303+
<span>{listItem}</span>
304+
</li>
296305
))}
297306
</ul>
298307
);
@@ -322,7 +331,7 @@ export function StoryOfAdventureDetail() {
322331
<ul className="grid md:grid-cols-2 gap-3">
323332
{selectedStory.content.work.map((item) => (
324333
<li key={item} className="flex items-start gap-2">
325-
<span className="text-blue-600 mt-1"></span>
334+
<span className="text-yellow-500 mt-1 flex-shrink-0"></span>
326335
<span className="text-gray-900">{item}</span>
327336
</li>
328337
))}

src/styles/globals.css

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -199,8 +199,9 @@ html {
199199
scroll-behavior: smooth;
200200
}
201201

202-
/* Mobile-specific scrollbar hiding */
203-
@media (max-width: 768px) {
202+
/* Mobile-specific scrollbar hiding and navigation spacing */
203+
/* Using max-width: 639px to match Tailwind's sm breakpoint (640px) */
204+
@media (max-width: 639px) {
204205
/* Hide scrollbars on mobile for better UX */
205206
.overflow-x-auto::-webkit-scrollbar {
206207
display: none;
@@ -211,11 +212,29 @@ html {
211212
scrollbar-width: none;
212213
}
213214

214-
/* Ensure content starts below fixed navigation on mobile */
215-
/* Navigation bar is h-16 (64px) + safe area inset + buffer */
216-
/* Apply dynamic padding that accounts for safe area */
217-
.min-h-screen {
218-
padding-top: calc(4rem + env(safe-area-inset-top, 0px) + 2rem) !important;
215+
/* CRITICAL: Ensure content starts below fixed navigation on mobile */
216+
/* Navigation bar total height = env(safe-area-inset-top) + 64px (h-16) */
217+
/* Using EXTREMELY generous padding: minimum 18rem (288px) to ensure content is NEVER covered */
218+
/* With safe area: safe-area + 64px + 160px buffer = safe-area + 224px */
219+
/* This MUST override all Tailwind padding classes with maximum specificity */
220+
div.min-h-screen.pt-48,
221+
div.min-h-screen[class*="pt-"],
222+
.min-h-screen.pt-48,
223+
.min-h-screen[class*="pt-"],
224+
div[class*="min-h-screen"][class*="pt-"],
225+
[class*="min-h-screen"][class*="pt-"] {
226+
padding-top: max(18rem, calc(env(safe-area-inset-top, 0px) + 4rem + 10rem)) !important;
227+
}
228+
229+
/* Fallback for any min-h-screen without explicit padding classes */
230+
.min-h-screen:not([class*="pt-"]) {
231+
padding-top: max(18rem, calc(env(safe-area-inset-top, 0px) + 4rem + 10rem)) !important;
232+
}
233+
234+
/* Extra safety: target story detail pages specifically */
235+
div.min-h-screen > div.max-w-4xl,
236+
div.min-h-screen > div.max-w-3xl {
237+
margin-top: 0 !important;
219238
}
220239
}
221240

src/utils/preloadImages.ts

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/**
2+
* Utility to preload images for better performance
3+
* Preloads critical images that are likely to be viewed soon
4+
*/
5+
6+
import { getImageUrl } from './imageUtils';
7+
import contentData from '../data/content';
8+
9+
/**
10+
* Preload critical images that appear on the homepage and common pages
11+
*/
12+
export function preloadCriticalImages() {
13+
const criticalImages: string[] = [
14+
// Home page image
15+
getImageUrl("src/assets/ray.png"),
16+
// Logo
17+
getImageUrl("/assets/raylogo.png"),
18+
// Common story thumbnails
19+
getImageUrl("/assets/aishift.png"),
20+
getImageUrl("/assets/annapurna.png"),
21+
getImageUrl("/assets/yose.jpg"),
22+
// Profile image
23+
getImageUrl("src/assets/mitregan.png"),
24+
];
25+
26+
criticalImages.forEach((imageUrl) => {
27+
if (imageUrl && !imageUrl.startsWith('http')) {
28+
const link = document.createElement('link');
29+
link.rel = 'preload';
30+
link.as = 'image';
31+
link.href = imageUrl;
32+
document.head.appendChild(link);
33+
} else if (imageUrl && imageUrl.startsWith('http')) {
34+
// For external images, use Image object to preload
35+
const img = new Image();
36+
img.src = imageUrl;
37+
}
38+
});
39+
}
40+
41+
/**
42+
* Preload images for a specific story
43+
*/
44+
export function preloadStoryImages(storyId: string) {
45+
const allStories = [
46+
...contentData.storiesOfAdventure.stories,
47+
...contentData.impact.stories,
48+
];
49+
50+
const story = allStories.find((s) => s.id === storyId);
51+
if (!story || !story.content.images) return;
52+
53+
story.content.images.forEach((image) => {
54+
const imageUrl = typeof image === 'string' ? image : image.url;
55+
const fullUrl = getImageUrl(imageUrl);
56+
57+
if (fullUrl && !fullUrl.startsWith('http')) {
58+
const link = document.createElement('link');
59+
link.rel = 'preload';
60+
link.as = 'image';
61+
link.href = fullUrl;
62+
document.head.appendChild(link);
63+
} else if (fullUrl && fullUrl.startsWith('http')) {
64+
const img = new Image();
65+
img.src = fullUrl;
66+
}
67+
});
68+
}
69+
70+
/**
71+
* Preload all photography images
72+
*/
73+
export function preloadPhotographyImages() {
74+
const images = contentData.photography.images;
75+
// Preload first 10 images (most likely to be viewed)
76+
images.slice(0, 10).forEach((img) => {
77+
const imageUrl = getImageUrl(img.url);
78+
if (imageUrl && !imageUrl.startsWith('http')) {
79+
const link = document.createElement('link');
80+
link.rel = 'preload';
81+
link.as = 'image';
82+
link.href = imageUrl;
83+
document.head.appendChild(link);
84+
} else if (imageUrl && imageUrl.startsWith('http')) {
85+
const imgElement = new Image();
86+
imgElement.src = imageUrl;
87+
}
88+
});
89+
}

0 commit comments

Comments
 (0)