This commit is contained in:
@ -101,9 +101,14 @@ export default function NavigationBar({ currentPath }: NavigationBarProps) {
|
||||
|
||||
return (
|
||||
<li key={item.id} class="mx-0.5 sm:mx-1">
|
||||
<a href={item.path} class={isActive ? "menu-active" : ""}>
|
||||
<div class="tooltip" data-tip={item.tooltip}>
|
||||
<a
|
||||
href={item.path}
|
||||
class={`min-h-[44px] min-w-[44px] inline-flex items-center justify-center ${isActive ? "menu-active" : ""}`}
|
||||
aria-label={item.tooltip}
|
||||
>
|
||||
<div class="tooltip md:before:-translate-x-1/2 md:before:-translate-y-full md:before:top-auto md:before:bottom-full md:after:-translate-x-1/2 md:after:-translate-y-full md:after:top-auto md:after:bottom-full" data-tip={item.tooltip}>
|
||||
<Icon size={18} class="sm:w-5 sm:h-5" />
|
||||
<span class="sr-only">{item.name}</span>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
|
@ -11,7 +11,7 @@ const { slug } = post;
|
||||
---
|
||||
|
||||
<div class="card bg-accent shadow-lg w-full sm:w-[calc(50%-1rem)] md:w-96 min-w-[280px] max-w-sm shrink">
|
||||
<div class="card-body p-3">
|
||||
<div class="card-body p-3 break-words">
|
||||
<h2 class="card-title text-base-100 justify-center text-center break-words font-bold text-lg mb-2">
|
||||
{title}
|
||||
</h2>
|
||||
|
@ -10,7 +10,7 @@ const { project } = Astro.props;
|
||||
---
|
||||
|
||||
<div class="card bg-accent shadow-lg w-full sm:w-[calc(50%-1rem)] md:w-96 min-w-[280px] max-w-sm shrink">
|
||||
<div class="card-body p-6">
|
||||
<div class="card-body p-6 break-words">
|
||||
<h2 class="card-title text-xl md:text-2xl font-bold justify-center text-center break-words text-base-100">
|
||||
{project.name}
|
||||
</h2>
|
||||
|
@ -21,7 +21,6 @@ export default function ResumeSkills({ skills }: ResumeSkillsProps) {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting && !hasAnimated.value) {
|
||||
hasAnimated.value = true;
|
||||
// Start animation for all skills
|
||||
skills.forEach((skill) => {
|
||||
animateSkill(skill.id, skill.level);
|
||||
});
|
||||
@ -67,19 +66,23 @@ export default function ResumeSkills({ skills }: ResumeSkillsProps) {
|
||||
<div id="skills-section" class="grid grid-cols-1 md:grid-cols-2 gap-3 sm:gap-4">
|
||||
{skills.map((skill) => {
|
||||
const currentLevel = animatedLevels.value[skill.id] || 0;
|
||||
const progressValue = currentLevel * 20; // Convert 1-5 scale to 0-100
|
||||
const progressValue = currentLevel * 20;
|
||||
|
||||
return (
|
||||
<div key={skill.id}>
|
||||
<label class="label p-1 sm:p-2">
|
||||
<span class="label-text text-sm sm:text-base">
|
||||
<div class="flex justify-between items-center p-1 sm:p-2">
|
||||
<span class="text-sm sm:text-base font-medium">
|
||||
{skill.name}
|
||||
</span>
|
||||
</label>
|
||||
<span class="text-xs sm:text-sm text-base-content/70">
|
||||
{Math.round(currentLevel)}/5
|
||||
</span>
|
||||
</div>
|
||||
<progress
|
||||
class="progress progress-primary w-full h-2 sm:h-3 transition-all duration-100 ease-out"
|
||||
class="progress progress-primary w-full h-2 sm:h-3 min-h-2 transition-all duration-100 ease-out"
|
||||
value={progressValue}
|
||||
max="100"
|
||||
aria-label={`${skill.name} skill level: ${Math.round(currentLevel)} out of 5`}
|
||||
></progress>
|
||||
</div>
|
||||
);
|
||||
|
@ -31,7 +31,7 @@ export default function ScrollUpButton() {
|
||||
type="button"
|
||||
onClick={scrollToTop}
|
||||
class={`fixed bottom-20 right-4 z-20 bg-secondary hover:bg-primary
|
||||
p-3 rounded-full shadow-lg transition-all duration-300
|
||||
p-3 rounded-full shadow-lg transition-all duration-300 min-h-[44px] min-w-[44px] inline-flex items-center justify-center
|
||||
${
|
||||
isVisible.value
|
||||
? "opacity-70 translate-y-0"
|
||||
|
@ -12,7 +12,7 @@ const { talk } = Astro.props;
|
||||
<div
|
||||
class="card bg-accent shadow-lg w-full sm:w-[calc(50%-1rem)] md:w-96 min-w-[280px] max-w-sm shrink"
|
||||
>
|
||||
<div class="card-body p-6">
|
||||
<div class="card-body p-6 break-words">
|
||||
<h2
|
||||
class="card-title text-xl md:text-2xl font-bold justify-center text-center break-words text-base-100"
|
||||
>
|
||||
|
@ -36,12 +36,6 @@ const Terminal = () => {
|
||||
}
|
||||
}, [commandHistory]);
|
||||
|
||||
useEffect(() => {
|
||||
if (inputRef.current) {
|
||||
inputRef.current.focus();
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Load command history from localStorage
|
||||
useEffect(() => {
|
||||
const history = loadCommandHistory();
|
||||
@ -219,7 +213,6 @@ __/ =| o |=-O=====O=====O=====O \\ ____Y___________|__|_________________________
|
||||
}
|
||||
onKeyDown={handleKeyDown}
|
||||
className="flex-1 bg-transparent border-none outline-none text-accent ml-1"
|
||||
autoFocus
|
||||
spellcheck={false}
|
||||
/>
|
||||
</form>
|
||||
|
@ -289,7 +289,7 @@ export const navigationItems: NavigationItem[] = [
|
||||
path: "/resume",
|
||||
tooltip: "Resume",
|
||||
icon: BriefcaseBusiness,
|
||||
enabled: true
|
||||
enabled: !!(resumeConfig.jsonFile && resumeConfig.jsonFile.trim())
|
||||
},
|
||||
{
|
||||
id: "projects",
|
||||
|
@ -29,7 +29,7 @@ const pageDescription = description || siteConfig.meta.description;
|
||||
<title>{pageTitle}</title>
|
||||
<ClientRouter />
|
||||
</head>
|
||||
<body class="flex flex-col min-h-screen">
|
||||
<body class="flex flex-col min-h-screen overflow-x-hidden">
|
||||
<main class="flex-grow flex flex-col gap-4 items-center justify-center pb-24 sm:pb-20">
|
||||
<slot />
|
||||
</main>
|
||||
|
@ -12,6 +12,7 @@ import { personalInfo, homepageSections } from "../config/data";
|
||||
alt={personalInfo.profileImage.alt}
|
||||
height={personalInfo.profileImage.height}
|
||||
width={personalInfo.profileImage.width}
|
||||
loading="eager"
|
||||
/>
|
||||
|
||||
<h1
|
||||
|
@ -63,11 +63,16 @@ interface ResumeData {
|
||||
let resumeData: ResumeData | undefined = undefined;
|
||||
let fetchError: string | null = null;
|
||||
|
||||
// Check if resume JSON file is configured before attempting to fetch
|
||||
if (!siteConfig.resume.jsonFile || !siteConfig.resume.jsonFile.trim()) {
|
||||
return Astro.redirect('/');
|
||||
}
|
||||
|
||||
try {
|
||||
// Get the base URL for the current request
|
||||
// Get the base URL
|
||||
const baseUrl = Astro.url.origin;
|
||||
|
||||
// Fetch the JSON file from the public directory using config
|
||||
// Fetch the JSON file from the public directory
|
||||
const response = await fetch(`${baseUrl}${siteConfig.resume.jsonFile}`);
|
||||
|
||||
if (!response.ok) {
|
||||
@ -91,31 +96,20 @@ try {
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading resume data:", error);
|
||||
fetchError =
|
||||
"Unable to load resume data. Please make sure the resume.json file exists in /public/files/";
|
||||
resumeData = undefined;
|
||||
// Return to home page when resume data cannot be loaded
|
||||
return Astro.redirect('/');
|
||||
}
|
||||
|
||||
const data = resumeData;
|
||||
const resumeConfig = siteConfig.resume;
|
||||
|
||||
// At this point, data is guaranteed to exist since we redirect on error
|
||||
if (!data) {
|
||||
return Astro.redirect('/');
|
||||
}
|
||||
---
|
||||
|
||||
{
|
||||
(!data || fetchError) && (
|
||||
<Layout title="Resume">
|
||||
<div class="container mx-auto p-4 sm:p-6 lg:p-8 max-w-4xl text-center w-full">
|
||||
<h1 class="text-2xl font-bold text-red-600">
|
||||
Error loading resume data.
|
||||
</h1>
|
||||
<p>{fetchError || "Please try refreshing the page."}</p>
|
||||
</div>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
data && !fetchError && (
|
||||
<Layout title="Resume">
|
||||
<Layout title="Resume">
|
||||
<div class="container mx-auto p-4 sm:p-6 lg:p-8 max-w-4xl w-full">
|
||||
<h1 class="text-3xl sm:text-4xl font-bold mb-4 sm:mb-6 text-center">
|
||||
{data.basics.name}
|
||||
@ -164,6 +158,7 @@ const resumeConfig = siteConfig.resume;
|
||||
)}
|
||||
</div>
|
||||
|
||||
{resumeConfig.pdfFile?.path && (
|
||||
<div class="text-center mb-6 sm:mb-8">
|
||||
<a
|
||||
href={resumeConfig.pdfFile.path}
|
||||
@ -173,10 +168,11 @@ const resumeConfig = siteConfig.resume;
|
||||
<Icon name="mdi:download" /> {resumeConfig.pdfFile.displayText}
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{data.sections.summary && resumeConfig.sections.summary?.enabled && (
|
||||
<div class="card bg-base-200 shadow-xl mb-4 sm:mb-6">
|
||||
<div class="card-body p-4 sm:p-6">
|
||||
<div class="card-body p-4 sm:p-6 break-words">
|
||||
<h2 class="card-title text-xl sm:text-2xl">
|
||||
{resumeConfig.sections.summary.title || data.sections.summary.name || "Summary"}
|
||||
</h2>
|
||||
@ -190,7 +186,7 @@ const resumeConfig = siteConfig.resume;
|
||||
data.sections.profiles.items.length > 0 &&
|
||||
resumeConfig.sections.profiles?.enabled && (
|
||||
<div class="card bg-base-200 shadow-xl mb-4 sm:mb-6">
|
||||
<div class="card-body p-4 sm:p-6">
|
||||
<div class="card-body p-4 sm:p-6 break-words">
|
||||
<h2 class="card-title text-xl sm:text-2xl">
|
||||
{resumeConfig.sections.profiles.title || data.sections.profiles.name || "Profiles"}
|
||||
</h2>
|
||||
@ -241,7 +237,7 @@ const resumeConfig = siteConfig.resume;
|
||||
data.sections.skills.items.length > 0 &&
|
||||
resumeConfig.sections.skills?.enabled && (
|
||||
<div class="card bg-base-200 shadow-xl mb-4 sm:mb-6">
|
||||
<div class="card-body p-4 sm:p-6">
|
||||
<div class="card-body p-4 sm:p-6 break-words">
|
||||
<h2 class="card-title text-xl sm:text-2xl">
|
||||
{resumeConfig.sections.skills.title || data.sections.skills.name || "Skills"}
|
||||
</h2>
|
||||
@ -258,7 +254,7 @@ const resumeConfig = siteConfig.resume;
|
||||
data.sections.experience.items.length > 0 &&
|
||||
resumeConfig.sections.experience?.enabled && (
|
||||
<div class="card bg-base-200 shadow-xl mb-4 sm:mb-6">
|
||||
<div class="card-body p-4 sm:p-6">
|
||||
<div class="card-body p-4 sm:p-6 break-words">
|
||||
<h2 class="card-title text-xl sm:text-2xl">
|
||||
{resumeConfig.sections.experience.title || data.sections.experience.name || "Experience"}
|
||||
</h2>
|
||||
@ -306,7 +302,7 @@ const resumeConfig = siteConfig.resume;
|
||||
data.sections.education.items.length > 0 &&
|
||||
resumeConfig.sections.education?.enabled && (
|
||||
<div class="card bg-base-200 shadow-xl mb-4 sm:mb-6">
|
||||
<div class="card-body p-4 sm:p-6">
|
||||
<div class="card-body p-4 sm:p-6 break-words">
|
||||
<h2 class="card-title text-xl sm:text-2xl">
|
||||
{resumeConfig.sections.education.title || data.sections.education.name || "Education"}
|
||||
</h2>
|
||||
@ -345,7 +341,7 @@ const resumeConfig = siteConfig.resume;
|
||||
data.sections.volunteer.items.length > 0 &&
|
||||
resumeConfig.sections.volunteer?.enabled && (
|
||||
<div class="card bg-base-200 shadow-xl mb-4 sm:mb-6">
|
||||
<div class="card-body p-4 sm:p-6">
|
||||
<div class="card-body p-4 sm:p-6 break-words">
|
||||
<h2 class="card-title text-xl sm:text-2xl">
|
||||
{resumeConfig.sections.volunteer.title || data.sections.volunteer.name || "Volunteer Work"}
|
||||
</h2>
|
||||
@ -372,6 +368,4 @@ const resumeConfig = siteConfig.resume;
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
</Layout>
|
||||
|
@ -35,51 +35,3 @@
|
||||
--depth: 1;
|
||||
--noise: 1;
|
||||
}
|
||||
|
||||
/* Ensure better text scaling and overflow handling */
|
||||
* {
|
||||
/* Allow text to scale with user preferences */
|
||||
text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
/* Prevent horizontal overflow on smaller screens or when zoomed */
|
||||
body {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* Ensure links and buttons remain accessible at all zoom levels */
|
||||
a, button {
|
||||
min-height: 44px;
|
||||
min-width: 44px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* Make sure card content doesn't overflow */
|
||||
.card-body {
|
||||
overflow-wrap: break-word;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
/* Ensure progress bars scale properly */
|
||||
.progress {
|
||||
min-height: 0.5rem;
|
||||
}
|
||||
|
||||
/* Responsive navigation improvements */
|
||||
@media (max-width: 640px) {
|
||||
.menu-horizontal .menu li {
|
||||
margin: 0 0.125rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Better tooltip positioning for mobile */
|
||||
@media (max-width: 768px) {
|
||||
.tooltip:before,
|
||||
.tooltip:after {
|
||||
transform: translateX(-50%) translateY(-100%);
|
||||
top: auto;
|
||||
bottom: 100%;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user