feat: adapt portfolio sections for smaller screens

This commit is contained in:
2026-09-05 17:26:06 +08:00
parent f6228f8507
commit 3d12f48f32
9 changed files with 440 additions and 120 deletions

View File

@@ -14,11 +14,9 @@
display: flex; display: flex;
align-items: center; align-items: center;
gap: 36px; gap: 36px;
transition: border-color 0.2s ease; transition:
} border-color var(--motion-duration-fast) ease,
transform var(--motion-duration-default) var(--motion-ease-out);
.card:hover {
border-color: var(--accent-default);
} }
.crestCol { .crestCol {
@@ -87,3 +85,37 @@
[data-theme="dark"] .card { [data-theme="dark"] .card {
background-color: var(--bg-surface); background-color: var(--bg-surface);
} }
@media (max-width: 760px) {
.educationSection {
padding-top: var(--space-8);
}
.card {
align-items: flex-start;
flex-direction: column;
gap: var(--space-4);
padding: var(--space-5);
}
.crestCol,
.crestContainer {
width: 180px;
height: 132px;
}
}
@media (max-width: 480px) {
.crestCol,
.crestContainer {
width: 144px;
height: 108px;
}
}
@media (hover: hover) and (pointer: fine) {
.card:hover {
border-color: var(--accent-default);
transform: translateY(-2px);
}
}

View File

@@ -34,7 +34,9 @@
background-color: var(--color-slate-400); background-color: var(--color-slate-400);
margin-top: 6px; margin-top: 6px;
z-index: 2; z-index: 2;
transition: all 0.2s ease; transition:
background-color var(--motion-duration-fast) ease,
box-shadow var(--motion-duration-default) ease;
} }
.nodeCurrent { .nodeCurrent {
@@ -183,3 +185,50 @@
line-height: var(--line-height-body-small); line-height: var(--line-height-body-small);
color: var(--text-secondary); color: var(--text-secondary);
} }
@media (max-width: 900px) {
.experienceSection {
padding-top: var(--space-8);
padding-bottom: var(--space-9);
}
.timelineItem {
min-height: 0;
}
.itemContent {
flex-direction: column;
gap: var(--space-4);
padding-bottom: var(--space-7);
}
.identityCol,
.detailsCol {
width: 100%;
max-width: none;
}
}
@media (max-width: 640px) {
.spineColumn {
width: 24px;
}
.itemContent {
padding-left: var(--space-3);
}
.metaRow {
flex-wrap: wrap;
}
.logoContainer {
width: 60px;
height: 60px;
}
.roleTitle {
font-size: 18px;
line-height: 26px;
}
}

View File

@@ -65,12 +65,9 @@
gap: 12px; gap: 12px;
color: var(--text-primary); color: var(--text-primary);
text-decoration: none; text-decoration: none;
transition: color 0.2s ease, transform 0.2s ease; transition:
} color var(--motion-duration-fast) ease,
transform var(--motion-duration-default) var(--motion-ease-out);
.socialItem:hover {
color: var(--accent-default);
transform: translateY(-2px);
} }
.socialItem:focus-visible { .socialItem:focus-visible {
@@ -104,3 +101,64 @@
font-weight: 600; font-weight: 600;
color: var(--text-primary); color: var(--text-primary);
} }
@media (max-width: 900px) {
.footerSection {
height: auto;
min-height: 480px;
padding: var(--space-10) var(--layout-page-gutter) var(--space-9);
}
.bgContainer {
height: 100%;
}
.content {
width: 100%;
margin-top: 0;
}
.socialRow {
flex-wrap: wrap;
justify-content: center;
gap: var(--space-6) var(--space-8);
}
}
@media (max-width: 560px) {
.footerSection {
min-height: 560px;
padding-top: var(--space-9);
}
.ctaTitle {
font-size: 36px;
line-height: 46px;
}
.ctaSubtitle {
max-width: 300px;
margin-bottom: var(--space-7);
font-size: var(--font-size-body-default);
line-height: var(--line-height-body-default);
letter-spacing: 0.08em;
}
.socialRow {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
width: 100%;
gap: var(--space-6) var(--space-4);
}
.socialLabel {
overflow-wrap: anywhere;
}
}
@media (hover: hover) and (pointer: fine) {
.socialItem:hover {
color: var(--accent-default);
transform: translateY(-2px);
}
}

View File

@@ -130,22 +130,12 @@
backdrop-filter: none; backdrop-filter: none;
} }
.heroButtonOutline:hover {
border-color: var(--color-blue-700);
background: rgba(255, 255, 255, 0.2);
}
.heroButtonPrimary { .heroButtonPrimary {
border-color: var(--color-blue-600); border-color: var(--color-blue-600);
background: var(--color-blue-600); background: var(--color-blue-600);
color: var(--color-blue-white); color: var(--color-blue-white);
} }
.heroButtonPrimary:hover {
border-color: var(--color-blue-700);
background: var(--color-blue-700);
}
.heroButton:focus-visible { .heroButton:focus-visible {
outline: 3px solid var(--focus-ring); outline: 3px solid var(--focus-ring);
outline-offset: 3px; outline-offset: 3px;
@@ -157,22 +147,12 @@
color: var(--text-primary); color: var(--text-primary);
} }
[data-theme="dark"] .heroButtonOutline:hover {
border-color: var(--color-blue-100);
background: rgba(130, 181, 255, 0.12);
}
[data-theme="dark"] .heroButtonPrimary { [data-theme="dark"] .heroButtonPrimary {
border-color: var(--color-blue-white); border-color: var(--color-blue-white);
background: var(--color-blue-100); background: var(--color-blue-100);
color: var(--color-blue-950); color: var(--color-blue-950);
} }
[data-theme="dark"] .heroButtonPrimary:hover {
border-color: var(--color-blue-white);
background: var(--color-blue-white);
}
.contactBar { .contactBar {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
@@ -336,4 +316,24 @@
[data-theme="dark"] .iconButton:hover { [data-theme="dark"] .iconButton:hover {
color: var(--accent-default); color: var(--accent-default);
} }
.heroButtonOutline:hover {
border-color: var(--color-blue-700);
background: rgba(255, 255, 255, 0.2);
}
.heroButtonPrimary:hover {
border-color: var(--color-blue-700);
background: var(--color-blue-700);
}
[data-theme="dark"] .heroButtonOutline:hover {
border-color: var(--color-blue-100);
background: rgba(130, 181, 255, 0.12);
}
[data-theme="dark"] .heroButtonPrimary:hover {
border-color: var(--color-blue-white);
background: var(--color-blue-white);
}
} }

View File

@@ -19,14 +19,12 @@
padding: 24px 32px; padding: 24px 32px;
display: flex; display: flex;
gap: 36px; gap: 36px;
transition: border-color 0.2s ease; transition:
border-color var(--motion-duration-fast) ease,
transform var(--motion-duration-default) var(--motion-ease-out);
position: relative; position: relative;
} }
.certCard:hover {
border-color: var(--accent-default);
}
.thumbnailContainer { .thumbnailContainer {
position: relative; position: relative;
width: 264px; width: 264px;
@@ -43,10 +41,6 @@
transition: transform 0.3s ease; transition: transform 0.3s ease;
} }
.certCard:hover .certImage {
transform: scale(1.02);
}
.certDetails { .certDetails {
flex-grow: 1; flex-grow: 1;
display: flex; display: flex;
@@ -110,10 +104,44 @@
transition: gap 0.2s ease; transition: gap 0.2s ease;
} }
.viewCertLink:hover {
gap: 10px;
}
[data-theme="dark"] .certCard { [data-theme="dark"] .certCard {
background-color: var(--bg-surface); background-color: var(--bg-surface);
} }
@media (max-width: 760px) {
.milestonesSection {
padding-top: var(--space-8);
padding-bottom: var(--space-9);
}
.certCard {
flex-direction: column;
gap: var(--space-5);
padding: var(--space-5);
}
.thumbnailContainer {
width: 100%;
height: auto;
aspect-ratio: 11 / 8;
}
.actionRow {
justify-content: flex-start;
}
}
@media (hover: hover) and (pointer: fine) {
.certCard:hover {
border-color: var(--accent-default);
transform: translateY(-2px);
}
.certCard:hover .certImage {
transform: scale(1.02);
}
.viewCertLink:hover {
gap: 10px;
}
}

View File

@@ -28,7 +28,11 @@
border: 2px solid var(--color-blue-600); border: 2px solid var(--color-blue-600);
border-radius: 50%; border-radius: 50%;
cursor: pointer; cursor: pointer;
transition: color 0.2s ease, background-color 0.2s ease, transform 0.2s ease; transition:
color var(--motion-duration-fast) ease,
background-color var(--motion-duration-fast) ease,
border-color var(--motion-duration-fast) ease,
transform var(--motion-duration-fast) var(--motion-ease-out);
} }
.navButtonPrevious { .navButtonPrevious {
@@ -41,19 +45,6 @@
color: var(--color-blue-white); color: var(--color-blue-white);
} }
.navButton:hover:not(:disabled) {
transform: translateY(-1px);
}
.navButtonPrevious:hover:not(:disabled) {
background: var(--color-blue-200);
}
.navButtonNext:hover:not(:disabled) {
border-color: var(--color-blue-700);
background: var(--color-blue-700);
}
.navButton:focus-visible { .navButton:focus-visible {
outline: 3px solid var(--focus-ring); outline: 3px solid var(--focus-ring);
outline-offset: 3px; outline-offset: 3px;
@@ -68,20 +59,12 @@
color: var(--color-blue-white); color: var(--color-blue-white);
} }
[data-theme="dark"] .navButtonPrevious:hover:not(:disabled) {
background: rgba(255, 255, 255, 0.12);
}
[data-theme="dark"] .navButtonNext { [data-theme="dark"] .navButtonNext {
border-color: var(--color-blue-white); border-color: var(--color-blue-white);
background: var(--color-blue-white); background: var(--color-blue-white);
color: var(--color-blue-950); color: var(--color-blue-950);
} }
[data-theme="dark"] .navButtonNext:hover:not(:disabled) {
background: var(--color-blue-200);
}
.carouselViewport { .carouselViewport {
position: relative; position: relative;
width: 100%; width: 100%;
@@ -130,12 +113,6 @@
transition: border-color 0.25s ease, box-shadow 0.25s ease, transform 0.25s ease; transition: border-color 0.25s ease, box-shadow 0.25s ease, transform 0.25s ease;
} }
.card:hover {
border-color: var(--border-strong);
box-shadow: 0 10px 24px var(--shadow-color);
transform: translateY(-2px);
}
.mediaContainer { .mediaContainer {
position: relative; position: relative;
width: 100%; width: 100%;
@@ -150,10 +127,6 @@
transition: transform 0.3s ease; transition: transform 0.3s ease;
} }
.card:hover .projectImage {
transform: scale(1.02);
}
.cardBody { .cardBody {
display: flex; display: flex;
flex: 1; flex: 1;
@@ -243,11 +216,6 @@
transition: color 0.2s ease, gap 0.2s ease; transition: color 0.2s ease, gap 0.2s ease;
} }
.caseStudyLink:hover {
gap: 14px;
color: var(--color-blue-700);
}
.caseStudyLink:focus-visible { .caseStudyLink:focus-visible {
border-radius: 2px; border-radius: 2px;
outline: 3px solid var(--focus-ring); outline: 3px solid var(--focus-ring);
@@ -258,10 +226,6 @@
color: var(--accent-default); color: var(--accent-default);
} }
[data-theme="dark"] .caseStudyLink:hover {
color: var(--color-blue-100);
}
.scrollbarWrapper { .scrollbarWrapper {
display: flex; display: flex;
justify-content: center; justify-content: center;
@@ -329,3 +293,106 @@
[data-theme="dark"] .scrollbar::-moz-range-thumb { [data-theme="dark"] .scrollbar::-moz-range-thumb {
background: var(--color-blue-300); background: var(--color-blue-300);
} }
@media (max-width: 1024px) {
.projectsSection {
padding-top: var(--space-8);
padding-bottom: var(--space-9);
}
.carouselTrack {
gap: var(--space-4);
}
.card {
flex-basis: min(340px, calc(100vw - (2 * var(--layout-page-gutter))));
width: min(340px, calc(100vw - (2 * var(--layout-page-gutter))));
}
}
@media (max-width: 640px) {
.headerRow {
align-items: stretch;
flex-direction: column;
gap: var(--space-3);
margin-bottom: var(--space-6);
}
.arrowButtons {
align-self: flex-end;
gap: var(--space-2);
}
.navButton {
width: 40px;
height: 40px;
}
.carouselViewport {
margin-inline: calc(-1 * var(--layout-page-gutter));
width: calc(100% + (2 * var(--layout-page-gutter)));
padding-inline: var(--layout-page-gutter);
scroll-padding-inline: var(--layout-page-gutter);
scroll-snap-type: x mandatory;
}
.card {
flex-basis: min(320px, calc(100vw - 40px));
width: min(320px, calc(100vw - 40px));
min-height: 460px;
}
.mediaContainer {
height: 200px;
}
.cardBody {
padding: var(--space-5);
}
.scrollbarWrapper {
display: none;
}
}
@media (hover: hover) and (pointer: fine) {
.navButton:hover:not(:disabled) {
transform: translateY(-1px);
}
.navButtonPrevious:hover:not(:disabled) {
background: var(--color-blue-200);
}
.navButtonNext:hover:not(:disabled) {
border-color: var(--color-blue-700);
background: var(--color-blue-700);
}
.card:hover {
border-color: var(--border-strong);
box-shadow: 0 10px 24px var(--shadow-color);
transform: translateY(-2px);
}
.card:hover .projectImage {
transform: scale(1.02);
}
.caseStudyLink:hover {
gap: 14px;
color: var(--color-blue-700);
}
[data-theme="dark"] .caseStudyLink:hover {
color: var(--color-blue-100);
}
[data-theme="dark"] .navButtonPrevious:hover:not(:disabled) {
background: rgba(255, 255, 255, 0.12);
}
[data-theme="dark"] .navButtonNext:hover:not(:disabled) {
background: var(--color-blue-200);
}
}

View File

@@ -6,8 +6,6 @@ import { ArrowLeft, ArrowRight, ArrowUpRight } from 'lucide-react';
import { projects } from '@/data/projects'; import { projects } from '@/data/projects';
import styles from './ProjectsSection.module.css'; import styles from './ProjectsSection.module.css';
const CARD_STEP = 384;
export const ProjectsSection: React.FC = () => { export const ProjectsSection: React.FC = () => {
const viewportRef = useRef<HTMLDivElement>(null); const viewportRef = useRef<HTMLDivElement>(null);
const dragState = useRef({ const dragState = useRef({
@@ -40,8 +38,14 @@ export const ProjectsSection: React.FC = () => {
}, []); }, []);
const scrollByCard = (direction: -1 | 1) => { const scrollByCard = (direction: -1 | 1) => {
viewportRef.current?.scrollBy({ const viewport = viewportRef.current;
left: direction * CARD_STEP, const track = viewport?.firstElementChild as HTMLElement | null;
const card = track?.firstElementChild as HTMLElement | null;
if (!viewport || !track || !card) return;
const gap = Number.parseFloat(window.getComputedStyle(track).columnGap) || 0;
viewport.scrollBy({
left: direction * (card.getBoundingClientRect().width + gap),
behavior: 'smooth', behavior: 'smooth',
}); });
}; };

View File

@@ -84,12 +84,9 @@
line-height: var(--line-height-label-small); line-height: var(--line-height-label-small);
color: var(--text-secondary); color: var(--text-secondary);
font-weight: 500; font-weight: 500;
transition: all 0.2s ease; transition:
} color var(--motion-duration-fast) ease,
border-color var(--motion-duration-fast) ease;
.badgePill:hover {
border-color: var(--accent-default);
color: var(--text-primary);
} }
.toolsBlock { .toolsBlock {
@@ -113,16 +110,15 @@
border: 1px solid var(--border-default); border: 1px solid var(--border-default);
border-radius: var(--radius-lg); border-radius: var(--radius-lg);
padding: 16px 8px; padding: 16px 8px;
transition: all 0.25s cubic-bezier(0.2, 0, 0.2, 1); transition:
opacity var(--motion-duration-default) ease,
filter var(--motion-duration-default) ease,
border-color var(--motion-duration-fast) ease,
box-shadow var(--motion-duration-default) ease,
transform var(--motion-duration-default) var(--motion-ease-out);
cursor: default; cursor: default;
} }
.toolTile:hover {
border-color: var(--accent-default);
transform: translateY(-4px);
box-shadow: 0 8px 24px var(--shadow-color);
}
.toolDimmed { .toolDimmed {
opacity: 0.35; opacity: 0.35;
filter: grayscale(0.8); filter: grayscale(0.8);
@@ -168,3 +164,87 @@
[data-theme="dark"] .toolTile { [data-theme="dark"] .toolTile {
background-color: var(--bg-raised); background-color: var(--bg-raised);
} }
@media (max-width: 1180px) {
.toolsGrid {
grid-template-columns: repeat(6, minmax(0, 1fr));
gap: var(--space-4);
}
.toolTile {
width: 100%;
}
}
@media (max-width: 900px) {
.skillsSection {
padding-top: var(--space-8);
padding-bottom: var(--space-9);
}
.mainCard {
padding: var(--space-6);
}
.skillsGrid {
grid-template-columns: repeat(2, minmax(0, 1fr));
column-gap: var(--space-6);
}
.toolsGrid {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
.filterTabsRow {
flex-wrap: wrap;
}
}
@media (max-width: 560px) {
.mainCard {
padding: var(--space-5);
}
.skillsGrid {
grid-template-columns: 1fr;
row-gap: var(--space-5);
}
.toolsGrid {
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: var(--space-3);
}
.toolTile {
height: 124px;
padding: var(--space-3) var(--space-2);
}
.toolIconContainer {
width: 52px;
height: 52px;
}
.filterTabsRow {
align-items: stretch;
flex-direction: column;
gap: var(--space-2);
}
.filterButton {
width: 100%;
}
}
@media (hover: hover) and (pointer: fine) {
.badgePill:hover {
border-color: var(--accent-default);
color: var(--text-primary);
}
.toolTile:hover {
border-color: var(--accent-default);
box-shadow: 0 8px 24px var(--shadow-color);
transform: translateY(-3px);
}
}

View File

@@ -158,35 +158,18 @@ body {
color: var(--text-primary); color: var(--text-primary);
} }
.pill-btn-outline:hover {
background: rgba(255, 255, 255, 0.7);
border-color: var(--accent-default);
transform: translateY(-1px);
}
[data-theme="dark"] .pill-btn-outline { [data-theme="dark"] .pill-btn-outline {
background: rgba(9, 28, 63, 0.6); background: rgba(9, 28, 63, 0.6);
border-color: var(--border-default); border-color: var(--border-default);
color: var(--text-primary); color: var(--text-primary);
} }
[data-theme="dark"] .pill-btn-outline:hover {
background: rgba(16, 63, 127, 0.8);
border-color: var(--accent-default);
}
.pill-btn-primary { .pill-btn-primary {
background-color: var(--action-primary-bg); background-color: var(--action-primary-bg);
color: var(--action-primary-fg); color: var(--action-primary-fg);
border-color: transparent; border-color: transparent;
} }
.pill-btn-primary:hover {
background-color: var(--action-primary-hover);
transform: translateY(-1px);
box-shadow: 0 4px 12px var(--shadow-color);
}
@media (prefers-reduced-motion: reduce) { @media (prefers-reduced-motion: reduce) {
html { html {
scroll-behavior: auto; scroll-behavior: auto;
@@ -204,3 +187,22 @@ a {
color: inherit; color: inherit;
text-decoration: none; text-decoration: none;
} }
@media (hover: hover) and (pointer: fine) {
.pill-btn-outline:hover {
border-color: var(--accent-default);
background: rgba(255, 255, 255, 0.7);
transform: translateY(-1px);
}
[data-theme="dark"] .pill-btn-outline:hover {
border-color: var(--accent-default);
background: rgba(16, 63, 127, 0.8);
}
.pill-btn-primary:hover {
background-color: var(--action-primary-hover);
box-shadow: 0 4px 12px var(--shadow-color);
transform: translateY(-1px);
}
}