feat(ui): simplify skills display and fix contact heading and static labels
This commit is contained in:
1 parent
5f2593db2a
commit
b2a5ef1a31
4 files changed
+228
-159
No files matched your search
@@ -1,36 +1,43 @@
|
||||
<!-- - testimonial section stars add in sanity studio
|
||||
- accessibility dropdown background color in light mode it has transparent
|
||||
- add a photo gallery in gallery page -> uploads from sanity title(optional), photo upload/url, description (optional), category(optional) -> hover overlay blur + show title + show description + click show full image in a modal with cross button in top right -> add filter by category in top right -> display all the photos in a grid layout. -> responsive grid layout for mobile, tablet and desktop -> animation for the photo grid when loaded from the bottom
|
||||
- optimize the low end mobile color theme -> dark mode, light mode -> colors, background, buttons, text, links, icons, everything minimize the blur effect
|
||||
- chat bot remove from navigation bar and its page -> turn the chat bot into FAB -> keyword-matching search assistant for chat -> dont use api, dont label it as ai chat bot label it as Portfolio Assistant
|
||||
- footer make the spotify like on messenger/instagram chat music disk spinning when playing if not its not sppining disk -> layout the socials that is not blocked by bring to top button
|
||||
- link page -> optimize the theme colors, layout, animation for low end mobile -> make it linktree style that no header and footer
|
||||
- navigation bar in mobile -> fix the background colors theme -> fix not working minimize or bring back icon
|
||||
-->
|
||||
|
||||
<!-- - fix certifications page title and numbering it should be 3, tech stack is 2
|
||||
- redesign the card of certificates large picture, badge/cert code, issuing org, and title only -> hover and when click show all the information of the certificate in a modal -> dont make verify button only on credly or other platform
|
||||
- in about page it has certifications section -> remove it because it is already in certifications page -->
|
||||
|
||||
<!-- - in about section bento cell 2 make the image optimize load [About.tsx#L99-103](textBlock;file:///f%3A/WebDevelopment/Portfolio/src/components/About.tsx#L99-103)
|
||||
- link page cv url /CV-Cris-Charles-Garcia.pdf -> available work chip fetch from sanity same from home page
|
||||
- in cards of certificate/badges can be previewed if the picture is a certificate not a badge so make it large and can be previewed
|
||||
- certifications page title remove the ```03 // Credentials & Badges
|
||||
|
||||
Certifications & Badges
|
||||
Verified digital credentials from Cisco and industry partners. Each badge represents completed coursework, assessments, and demonstrated competency
|
||||
<!-- - background shapes in landscape the right side shapes collides
|
||||
- in portrait make the different approach of parallex and layout of the shapes -->
|
||||
<!-- - Lite mode default the toggle is disabled
|
||||
- hide the magnifier lens settings if detected mobile
|
||||
- limit portfolio assistant pre selections, add feature that shows their ip and location when prompts like a using AI chatbot or if they try to prompt in the portfolio assistant like its a AI -->
|
||||
|
||||
<!-- - fix the lite mode only hero section loads when i reload that enabled lite mode
|
||||
- make the UI in mobile same as on pc but when lagging notify to turn on the lite mode -->
|
||||
|
||||
<!-- - remove title description per sections
|
||||
- in project section remove the screenshot picture frame like its a browser in localhost, just make it simple
|
||||
- do not use emoji
|
||||
- certifications section -> all categories use simple word and short for title category
|
||||
- only fetch spotify widget when modal is open, fix recent play its pausing
|
||||
- in tech stack technology add icon fontawesome and update the sanity io -->␍
|
||||
<!-- - testimonial section stars add in sanity studio
|
||||
- accessibility dropdown background color in light mode it has transparent
|
||||
- add a photo gallery in gallery page -> uploads from sanity title(optional), photo upload/url, description (optional), category(optional) -> hover overlay blur + show title + show description + click show full image in a modal with cross button in top right -> add filter by category in top right -> display all the photos in a grid layout. -> responsive grid layout for mobile, tablet and desktop -> animation for the photo grid when loaded from the bottom
|
||||
- optimize the low end mobile color theme -> dark mode, light mode -> colors, background, buttons, text, links, icons, everything minimize the blur effect
|
||||
- chat bot remove from navigation bar and its page -> turn the chat bot into FAB -> keyword-matching search assistant for chat -> dont use api, dont label it as ai chat bot label it as Portfolio Assistant
|
||||
- footer make the spotify like on messenger/instagram chat music disk spinning when playing if not its not sppining disk -> layout the socials that is not blocked by bring to top button
|
||||
- link page -> optimize the theme colors, layout, animation for low end mobile -> make it linktree style that no header and footer
|
||||
- navigation bar in mobile -> fix the background colors theme -> fix not working minimize or bring back icon
|
||||
-->
|
||||
|
||||
<!-- - fix certifications page title and numbering it should be 3, tech stack is 2
|
||||
- redesign the card of certificates large picture, badge/cert code, issuing org, and title only -> hover and when click show all the information of the certificate in a modal -> dont make verify button only on credly or other platform
|
||||
- in about page it has certifications section -> remove it because it is already in certifications page -->
|
||||
|
||||
<!-- - in about section bento cell 2 make the image optimize load [About.tsx#L99-103](textBlock;file:///f%3A/WebDevelopment/Portfolio/src/components/About.tsx#L99-103)
|
||||
- link page cv url /CV-Cris-Charles-Garcia.pdf -> available work chip fetch from sanity same from home page
|
||||
- in cards of certificate/badges can be previewed if the picture is a certificate not a badge so make it large and can be previewed
|
||||
- certifications page title remove the ```03 // Credentials & Badges
|
||||
|
||||
Certifications & Badges
|
||||
Verified digital credentials from Cisco and industry partners. Each badge represents completed coursework, assessments, and demonstrated competency
|
||||
<!-- - background shapes in landscape the right side shapes collides
|
||||
- in portrait make the different approach of parallex and layout of the shapes -->
|
||||
<!-- - Lite mode default the toggle is disabled
|
||||
- hide the magnifier lens settings if detected mobile
|
||||
- limit portfolio assistant pre selections, add feature that shows their ip and location when prompts like a using AI chatbot or if they try to prompt in the portfolio assistant like its a AI -->
|
||||
|
||||
<!-- - fix the lite mode only hero section loads when i reload that enabled lite mode
|
||||
- make the UI in mobile same as on pc but when lagging notify to turn on the lite mode -->
|
||||
|
||||
<!-- - remove title description per sections
|
||||
- in project section remove the screenshot picture frame like its a browser in localhost, just make it simple
|
||||
- do not use emoji
|
||||
- certifications section -> all categories use simple word and short for title category
|
||||
- only fetch spotify widget when modal is open, fix recent play its pausing
|
||||
- in tech stack technology add icon fontawesome and update the sanity io -->
|
||||
|
||||
<!-- # TECHNOLOGIES & TOOLS
|
||||
- make the per categories simple and easy to read, remove some uneccessary components, remove the per skills has its box
|
||||
|
||||
# CONTACT SECTION
|
||||
- fix the "Let's create something amazing." is under lapping to paragraph element
|
||||
- in ultra fast mode the form still has label animation, fix the label to be static and dont move or animate, always show the label above the input field -->
|
||||
+154
-73
@@ -56,6 +56,21 @@ export default function ContactForm({
|
||||
const [formData, setFormData] = useState({ name: '', email: '', message: '' });
|
||||
const [status, setStatus] = useState<'idle' | 'sending' | 'success' | 'error'>('idle');
|
||||
const [errorMessage, setErrorMessage] = useState('');
|
||||
const [isLowTier, setIsLowTier] = useState(() => {
|
||||
if (typeof document !== 'undefined') {
|
||||
return document.documentElement.dataset.tier === 'low' || localStorage.getItem('liteMode') === 'true';
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
const checkTier = () => {
|
||||
setIsLowTier(document.documentElement.dataset.tier === 'low' || localStorage.getItem('liteMode') === 'true');
|
||||
};
|
||||
checkTier();
|
||||
window.addEventListener('tier-change', checkTier);
|
||||
return () => window.removeEventListener('tier-change', checkTier);
|
||||
}, []);
|
||||
|
||||
const linksToRender = socialLinks.length > 0
|
||||
? socialLinks.filter(l => l.url && l.url !== '#' && !l.url.includes('/N/A'))
|
||||
@@ -112,7 +127,7 @@ export default function ContactForm({
|
||||
{/* Left Side: Info */}
|
||||
<div className="lg:col-span-2 flex flex-col justify-center h-full">
|
||||
<ScrollReveal variant="fade-right">
|
||||
<h2 className="text-4xl md:text-5xl font-black font-heading mb-6 text-glow bg-clip-text text-transparent bg-gradient-to-r from-foreground-custom to-primary-custom/80">
|
||||
<h2 className="text-3xl sm:text-4xl md:text-5xl font-black font-heading mb-5 leading-tight tracking-tight text-foreground-custom block">
|
||||
{contactHeading}
|
||||
</h2>
|
||||
<p className="text-muted-foreground-custom text-base md:text-lg mb-8 leading-relaxed">
|
||||
@@ -148,9 +163,9 @@ export default function ContactForm({
|
||||
{status === 'success' ? (
|
||||
<motion.div
|
||||
key="success"
|
||||
initial={{ opacity: 0, scale: 0.95 }}
|
||||
initial={isLowTier ? false : { opacity: 0, scale: 0.95 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
exit={{ opacity: 0, scale: 1.05 }}
|
||||
exit={isLowTier ? undefined : { opacity: 0, scale: 1.05 }}
|
||||
className="text-center py-12 flex flex-col items-center justify-center min-h-[400px]"
|
||||
>
|
||||
<div className="w-20 h-20 rounded-full bg-emerald-500/10 text-emerald-500 flex items-center justify-center mb-6 shadow-[0_0_30px_rgba(16,185,129,0.2)]">
|
||||
@@ -166,7 +181,7 @@ export default function ContactForm({
|
||||
</p>
|
||||
<button
|
||||
onClick={() => setStatus('idle')}
|
||||
className="px-8 py-3.5 rounded-full bg-foreground-custom/5 hover:bg-foreground-custom/10 text-foreground-custom text-sm font-bold shadow-sm hover:scale-[1.02] active:scale-[0.98] transition-all"
|
||||
className="px-8 py-3.5 rounded-full bg-foreground-custom/5 hover:bg-foreground-custom/10 text-foreground-custom text-sm font-bold shadow-sm hover:scale-[1.02] active:scale-[0.98] transition-all cursor-pointer"
|
||||
>
|
||||
Send Another
|
||||
</button>
|
||||
@@ -174,88 +189,153 @@ export default function ContactForm({
|
||||
) : (
|
||||
<motion.form
|
||||
key="form"
|
||||
initial={{ opacity: 0 }}
|
||||
initial={isLowTier ? false : { opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
exit={isLowTier ? undefined : { opacity: 0 }}
|
||||
onSubmit={handleSubmit}
|
||||
className="flex flex-col gap-8"
|
||||
className="flex flex-col gap-6"
|
||||
>
|
||||
{status === 'error' && (
|
||||
<div className="p-4 rounded-2xl bg-rose-500/10 border border-rose-500/20 text-rose-500 text-sm font-medium">
|
||||
<div className="p-4 rounded-xl bg-rose-500/10 border border-rose-500/20 text-rose-500 text-sm font-medium">
|
||||
{errorMessage}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
{/* Floating Name */}
|
||||
<div className="relative z-0 w-full group">
|
||||
<input
|
||||
type="text"
|
||||
name="name"
|
||||
id="name"
|
||||
className="block py-3 px-0 w-full text-base text-foreground-custom bg-transparent border-0 border-b-2 border-border-hover/30 appearance-none focus:outline-none focus:ring-0 focus:border-primary-custom peer transition-colors"
|
||||
placeholder=" "
|
||||
required
|
||||
value={formData.name}
|
||||
onChange={handleChange}
|
||||
disabled={status === 'sending'}
|
||||
/>
|
||||
<label
|
||||
htmlFor="name"
|
||||
className="peer-focus:font-medium absolute text-sm text-muted-foreground-custom duration-300 transform -translate-y-6 scale-75 top-3 -z-10 origin-[0] peer-focus:left-0 peer-focus:text-primary-custom peer-placeholder-shown:scale-100 peer-placeholder-shown:translate-y-0 peer-focus:scale-75 peer-focus:-translate-y-6"
|
||||
>
|
||||
Full Name
|
||||
</label>
|
||||
</div>
|
||||
{/* Low-tier / Ultra-fast mode: Always Static Labels Above Inputs (No animation) */}
|
||||
{isLowTier ? (
|
||||
<div className="flex flex-col gap-5">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-5">
|
||||
{/* Static Full Name */}
|
||||
<div className="flex flex-col gap-1.5 w-full">
|
||||
<label htmlFor="name" className="text-xs font-semibold text-foreground-custom">
|
||||
Full Name
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="name"
|
||||
id="name"
|
||||
className="block py-2.5 px-3 w-full text-sm text-foreground-custom bg-background-custom border border-border-custom rounded-md focus:outline-none focus:border-primary-custom"
|
||||
placeholder="Your full name"
|
||||
required
|
||||
value={formData.name}
|
||||
onChange={handleChange}
|
||||
disabled={status === 'sending'}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Floating Email */}
|
||||
<div className="relative z-0 w-full group">
|
||||
<input
|
||||
type="email"
|
||||
name="email"
|
||||
id="email"
|
||||
className="block py-3 px-0 w-full text-base text-foreground-custom bg-transparent border-0 border-b-2 border-border-hover/30 appearance-none focus:outline-none focus:ring-0 focus:border-primary-custom peer transition-colors"
|
||||
placeholder=" "
|
||||
required
|
||||
value={formData.email}
|
||||
onChange={handleChange}
|
||||
disabled={status === 'sending'}
|
||||
/>
|
||||
<label
|
||||
htmlFor="email"
|
||||
className="peer-focus:font-medium absolute text-sm text-muted-foreground-custom duration-300 transform -translate-y-6 scale-75 top-3 -z-10 origin-[0] peer-focus:left-0 peer-focus:text-primary-custom peer-placeholder-shown:scale-100 peer-placeholder-shown:translate-y-0 peer-focus:scale-75 peer-focus:-translate-y-6"
|
||||
>
|
||||
Email Address
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
{/* Static Email */}
|
||||
<div className="flex flex-col gap-1.5 w-full">
|
||||
<label htmlFor="email" className="text-xs font-semibold text-foreground-custom">
|
||||
Email Address
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
name="email"
|
||||
id="email"
|
||||
className="block py-2.5 px-3 w-full text-sm text-foreground-custom bg-background-custom border border-border-custom rounded-md focus:outline-none focus:border-primary-custom"
|
||||
placeholder="you@example.com"
|
||||
required
|
||||
value={formData.email}
|
||||
onChange={handleChange}
|
||||
disabled={status === 'sending'}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Floating Message */}
|
||||
<div className="relative z-0 w-full group mt-2">
|
||||
<textarea
|
||||
name="message"
|
||||
id="message"
|
||||
rows={4}
|
||||
className="block py-3 px-0 w-full text-base text-foreground-custom bg-transparent border-0 border-b-2 border-border-hover/30 appearance-none focus:outline-none focus:ring-0 focus:border-primary-custom peer transition-colors resize-none"
|
||||
placeholder=" "
|
||||
required
|
||||
value={formData.message}
|
||||
onChange={handleChange}
|
||||
disabled={status === 'sending'}
|
||||
/>
|
||||
<label
|
||||
htmlFor="message"
|
||||
className="peer-focus:font-medium absolute text-sm text-muted-foreground-custom duration-300 transform -translate-y-6 scale-75 top-3 -z-10 origin-[0] peer-focus:left-0 peer-focus:text-primary-custom peer-placeholder-shown:scale-100 peer-placeholder-shown:translate-y-0 peer-focus:scale-75 peer-focus:-translate-y-6"
|
||||
>
|
||||
How can I help you?
|
||||
</label>
|
||||
</div>
|
||||
{/* Static Message */}
|
||||
<div className="flex flex-col gap-1.5 w-full">
|
||||
<label htmlFor="message" className="text-xs font-semibold text-foreground-custom">
|
||||
How can I help you?
|
||||
</label>
|
||||
<textarea
|
||||
name="message"
|
||||
id="message"
|
||||
rows={4}
|
||||
className="block py-2.5 px-3 w-full text-sm text-foreground-custom bg-background-custom border border-border-custom rounded-md focus:outline-none focus:border-primary-custom resize-none"
|
||||
placeholder="Your message or project inquiry..."
|
||||
required
|
||||
value={formData.message}
|
||||
onChange={handleChange}
|
||||
disabled={status === 'sending'}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-col gap-8">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
{/* Floating Name */}
|
||||
<div className="relative z-0 w-full group">
|
||||
<input
|
||||
type="text"
|
||||
name="name"
|
||||
id="name"
|
||||
className="block py-3 px-0 w-full text-base text-foreground-custom bg-transparent border-0 border-b-2 border-border-hover/30 appearance-none focus:outline-none focus:ring-0 focus:border-primary-custom peer transition-colors"
|
||||
placeholder=" "
|
||||
required
|
||||
value={formData.name}
|
||||
onChange={handleChange}
|
||||
disabled={status === 'sending'}
|
||||
/>
|
||||
<label
|
||||
htmlFor="name"
|
||||
className="peer-focus:font-medium absolute text-sm text-muted-foreground-custom duration-300 transform -translate-y-6 scale-75 top-3 -z-10 origin-[0] peer-focus:left-0 peer-focus:text-primary-custom peer-placeholder-shown:scale-100 peer-placeholder-shown:translate-y-0 peer-focus:scale-75 peer-focus:-translate-y-6"
|
||||
>
|
||||
Full Name
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{/* Floating Email */}
|
||||
<div className="relative z-0 w-full group">
|
||||
<input
|
||||
type="email"
|
||||
name="email"
|
||||
id="email"
|
||||
className="block py-3 px-0 w-full text-base text-foreground-custom bg-transparent border-0 border-b-2 border-border-hover/30 appearance-none focus:outline-none focus:ring-0 focus:border-primary-custom peer transition-colors"
|
||||
placeholder=" "
|
||||
required
|
||||
value={formData.email}
|
||||
onChange={handleChange}
|
||||
disabled={status === 'sending'}
|
||||
/>
|
||||
<label
|
||||
htmlFor="email"
|
||||
className="peer-focus:font-medium absolute text-sm text-muted-foreground-custom duration-300 transform -translate-y-6 scale-75 top-3 -z-10 origin-[0] peer-focus:left-0 peer-focus:text-primary-custom peer-placeholder-shown:scale-100 peer-placeholder-shown:translate-y-0 peer-focus:scale-75 peer-focus:-translate-y-6"
|
||||
>
|
||||
Email Address
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Floating Message */}
|
||||
<div className="relative z-0 w-full group mt-2">
|
||||
<textarea
|
||||
name="message"
|
||||
id="message"
|
||||
rows={4}
|
||||
className="block py-3 px-0 w-full text-base text-foreground-custom bg-transparent border-0 border-b-2 border-border-hover/30 appearance-none focus:outline-none focus:ring-0 focus:border-primary-custom peer transition-colors resize-none"
|
||||
placeholder=" "
|
||||
required
|
||||
value={formData.message}
|
||||
onChange={handleChange}
|
||||
disabled={status === 'sending'}
|
||||
/>
|
||||
<label
|
||||
htmlFor="message"
|
||||
className="peer-focus:font-medium absolute text-sm text-muted-foreground-custom duration-300 transform -translate-y-6 scale-75 top-3 -z-10 origin-[0] peer-focus:left-0 peer-focus:text-primary-custom peer-placeholder-shown:scale-100 peer-placeholder-shown:translate-y-0 peer-focus:scale-75 peer-focus:-translate-y-6"
|
||||
>
|
||||
How can I help you?
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Submit Button */}
|
||||
<button
|
||||
type="submit"
|
||||
disabled={status === 'sending'}
|
||||
className="group relative w-full sm:w-auto self-end mt-4 px-8 py-4 rounded-full bg-primary-custom text-white font-bold text-sm shadow-xl shadow-primary-custom/20 hover:shadow-primary-custom/40 hover:scale-[1.02] active:scale-[0.98] transition-all duration-300 overflow-hidden disabled:opacity-70 disabled:cursor-not-allowed flex items-center justify-center gap-3"
|
||||
className={`group relative w-full sm:w-auto self-end mt-2 px-8 py-3.5 rounded-full bg-primary-custom text-white font-bold text-sm shadow-md hover:shadow-lg transition-all duration-200 overflow-hidden disabled:opacity-70 disabled:cursor-not-allowed flex items-center justify-center gap-3 cursor-pointer ${
|
||||
isLowTier ? 'hover:scale-100 active:scale-100 rounded-md' : 'hover:scale-[1.02] active:scale-[0.98]'
|
||||
}`}
|
||||
>
|
||||
<span className="relative z-10 flex items-center gap-2">
|
||||
{status === 'sending' ? 'Sending...' : 'Send Message'}
|
||||
@@ -271,8 +351,9 @@ export default function ContactForm({
|
||||
</svg>
|
||||
)}
|
||||
</span>
|
||||
{/* Hover flare effect */}
|
||||
<div className="absolute inset-0 -translate-x-full bg-gradient-to-r from-transparent via-white/20 to-transparent group-hover:animate-[shimmer_1.5s_infinite]" />
|
||||
{!isLowTier && (
|
||||
<div className="absolute inset-0 -translate-x-full bg-gradient-to-r from-transparent via-white/20 to-transparent group-hover:animate-[shimmer_1.5s_infinite]" />
|
||||
)}
|
||||
</button>
|
||||
</motion.form>
|
||||
)}
|
||||
|
||||
+11
-49
@@ -101,30 +101,6 @@ export default function Skills({ skills, sectionTag = '03 // Tech Stack' }: Skil
|
||||
|
||||
const categories = Array.from(new Set(skills.map((s) => s.category || 'Core')));
|
||||
|
||||
// Bento span calculation to produce aesthetic varying card proportions
|
||||
const getBentoSpan = (index: number, total: number) => {
|
||||
if (total === 1) return 'lg:col-span-12';
|
||||
if (total === 2) return 'lg:col-span-6';
|
||||
if (total === 3) {
|
||||
if (index === 0) return 'lg:col-span-12';
|
||||
return 'lg:col-span-6';
|
||||
}
|
||||
if (total === 4) {
|
||||
if (index === 0) return 'lg:col-span-7';
|
||||
if (index === 1) return 'lg:col-span-5';
|
||||
return 'lg:col-span-6';
|
||||
}
|
||||
if (total >= 5) {
|
||||
if (index === 0) return 'lg:col-span-7 md:col-span-12';
|
||||
if (index === 1) return 'lg:col-span-5 md:col-span-12';
|
||||
if (index === 2) return 'lg:col-span-4 md:col-span-6';
|
||||
if (index === 3) return 'lg:col-span-4 md:col-span-6';
|
||||
if (index === 4) return 'lg:col-span-4 md:col-span-12';
|
||||
return 'lg:col-span-6';
|
||||
}
|
||||
return 'lg:col-span-4';
|
||||
};
|
||||
|
||||
return (
|
||||
<section id="skills" className="py-20 relative">
|
||||
<div className="max-w-6xl mx-auto px-4 sm:px-6 relative z-10">
|
||||
@@ -144,18 +120,17 @@ export default function Skills({ skills, sectionTag = '03 // Tech Stack' }: Skil
|
||||
</div>
|
||||
</ScrollReveal>
|
||||
|
||||
{/* ─── Bento Box Grid for Tech Stack ─── */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-12 gap-5">
|
||||
{/* Clean, Easy-to-Read Categories Grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5">
|
||||
{categories.map((category, idx) => {
|
||||
const categorySkills = skills.filter((s) => (s.category || 'Core') === category);
|
||||
const spanClass = getBentoSpan(idx, categories.length);
|
||||
|
||||
return (
|
||||
<div key={category} className={`${spanClass} col-span-12`}>
|
||||
<ScrollReveal variant="fade-up" delay={idx * 60} className="h-full">
|
||||
<div className="bento-card p-6 sm:p-7 flex flex-col justify-between h-full group">
|
||||
<div key={category} className="col-span-1">
|
||||
<ScrollReveal variant="fade-up" delay={idx * 50} className="h-full">
|
||||
<div className="bento-card p-5 sm:p-6 flex flex-col justify-between h-full group">
|
||||
<div>
|
||||
{/* Header */}
|
||||
{/* Category Header */}
|
||||
<div className="flex items-center justify-between pb-3 mb-4 border-b border-border-custom">
|
||||
<div className="flex items-center gap-2.5">
|
||||
<span className="p-1.5 rounded-lg bg-primary-custom/10 text-primary-custom">
|
||||
@@ -170,32 +145,19 @@ export default function Skills({ skills, sectionTag = '03 // Tech Stack' }: Skil
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Skill Items List / Pills */}
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-2.5 my-2">
|
||||
{/* Clean Skill Items without individual boxes */}
|
||||
<div className="flex flex-wrap gap-x-5 gap-y-3 py-1">
|
||||
{categorySkills.map((skill) => (
|
||||
<div
|
||||
key={skill.name}
|
||||
className="p-3 rounded-xl border border-border-custom/80 bg-foreground-custom/[0.02] hover:bg-foreground-custom/[0.05] hover:border-primary-custom/40 transition-all flex items-center justify-between group/item"
|
||||
className="flex items-center gap-2 text-xs font-medium text-foreground-custom/90 hover:text-primary-custom transition-colors"
|
||||
>
|
||||
<div className="flex items-center gap-2.5 min-w-0">
|
||||
<span className="w-7 h-7 rounded-lg bg-primary-custom/10 text-primary-custom flex items-center justify-center text-sm shrink-0 group-hover/item:bg-primary-custom group-hover/item:text-white transition-all shadow-xs">
|
||||
<i className={getSkillFaClass(skill)} />
|
||||
</span>
|
||||
<span className="text-xs font-semibold text-foreground-custom group-hover/item:text-primary-custom transition-colors truncate">
|
||||
{skill.name}
|
||||
</span>
|
||||
</div>
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-primary-custom/40 group-hover/item:bg-primary-custom transition-colors shrink-0 ml-2" />
|
||||
<i className={`${getSkillFaClass(skill)} text-primary-custom text-sm shrink-0`} />
|
||||
<span>{skill.name}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bento Cell Foot */}
|
||||
<div className="pt-3 mt-4 border-t border-border-custom flex items-center justify-between text-[10px] font-mono text-muted-foreground-custom">
|
||||
<span>CATEGORY // {category.toUpperCase()}</span>
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-primary-custom/60" />
|
||||
</div>
|
||||
</div>
|
||||
</ScrollReveal>
|
||||
</div>
|
||||
|
||||
+20
-1
@@ -373,7 +373,7 @@ html[data-tier='low'].dark .impact-pill {
|
||||
color: #e5e7eb !important;
|
||||
}
|
||||
|
||||
/* 7. Classic Form Inputs */
|
||||
/* 7. Classic Form Inputs & Static Labels (No floating animations in low tier) */
|
||||
html[data-tier='low'] input,
|
||||
html[data-tier='low'] textarea,
|
||||
html[data-tier='low'] select {
|
||||
@@ -381,6 +381,25 @@ html[data-tier='low'] select {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
html[data-tier='low'] #contact label {
|
||||
position: static !important;
|
||||
transform: none !important;
|
||||
display: block !important;
|
||||
margin-bottom: 6px !important;
|
||||
font-size: 12px !important;
|
||||
font-weight: 600 !important;
|
||||
color: var(--fg-color) !important;
|
||||
opacity: 1 !important;
|
||||
z-index: auto !important;
|
||||
animation: none !important;
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
html[data-tier='low'] #contact .peer-focus\:-translate-y-6,
|
||||
html[data-tier='low'] #contact [class*="translate-y"] {
|
||||
transform: none !important;
|
||||
}
|
||||
|
||||
html[data-tier='low']:not(.dark) input,
|
||||
html[data-tier='low']:not(.dark) textarea,
|
||||
html[data-tier='low']:not(.dark) select {
|
||||
|
||||
Reference in new issue
Block a user