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;
align-items: center;
gap: 36px;
transition: border-color 0.2s ease;
}
.card:hover {
border-color: var(--accent-default);
transition:
border-color var(--motion-duration-fast) ease,
transform var(--motion-duration-default) var(--motion-ease-out);
}
.crestCol {
@@ -87,3 +85,37 @@
[data-theme="dark"] .card {
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);
margin-top: 6px;
z-index: 2;
transition: all 0.2s ease;
transition:
background-color var(--motion-duration-fast) ease,
box-shadow var(--motion-duration-default) ease;
}
.nodeCurrent {
@@ -183,3 +185,50 @@
line-height: var(--line-height-body-small);
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;
color: var(--text-primary);
text-decoration: none;
transition: color 0.2s ease, transform 0.2s ease;
}
.socialItem:hover {
color: var(--accent-default);
transform: translateY(-2px);
transition:
color var(--motion-duration-fast) ease,
transform var(--motion-duration-default) var(--motion-ease-out);
}
.socialItem:focus-visible {
@@ -104,3 +101,64 @@
font-weight: 600;
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;
}
.heroButtonOutline:hover {
border-color: var(--color-blue-700);
background: rgba(255, 255, 255, 0.2);
}
.heroButtonPrimary {
border-color: var(--color-blue-600);
background: var(--color-blue-600);
color: var(--color-blue-white);
}
.heroButtonPrimary:hover {
border-color: var(--color-blue-700);
background: var(--color-blue-700);
}
.heroButton:focus-visible {
outline: 3px solid var(--focus-ring);
outline-offset: 3px;
@@ -157,22 +147,12 @@
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 {
border-color: var(--color-blue-white);
background: var(--color-blue-100);
color: var(--color-blue-950);
}
[data-theme="dark"] .heroButtonPrimary:hover {
border-color: var(--color-blue-white);
background: var(--color-blue-white);
}
.contactBar {
display: inline-flex;
align-items: center;
@@ -336,4 +316,24 @@
[data-theme="dark"] .iconButton:hover {
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;
display: flex;
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;
}
.certCard:hover {
border-color: var(--accent-default);
}
.thumbnailContainer {
position: relative;
width: 264px;
@@ -43,10 +41,6 @@
transition: transform 0.3s ease;
}
.certCard:hover .certImage {
transform: scale(1.02);
}
.certDetails {
flex-grow: 1;
display: flex;
@@ -110,10 +104,44 @@
transition: gap 0.2s ease;
}
.viewCertLink:hover {
gap: 10px;
}
[data-theme="dark"] .certCard {
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-radius: 50%;
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 {
@@ -41,19 +45,6 @@
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 {
outline: 3px solid var(--focus-ring);
outline-offset: 3px;
@@ -68,20 +59,12 @@
color: var(--color-blue-white);
}
[data-theme="dark"] .navButtonPrevious:hover:not(:disabled) {
background: rgba(255, 255, 255, 0.12);
}
[data-theme="dark"] .navButtonNext {
border-color: var(--color-blue-white);
background: var(--color-blue-white);
color: var(--color-blue-950);
}
[data-theme="dark"] .navButtonNext:hover:not(:disabled) {
background: var(--color-blue-200);
}
.carouselViewport {
position: relative;
width: 100%;
@@ -130,12 +113,6 @@
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 {
position: relative;
width: 100%;
@@ -150,10 +127,6 @@
transition: transform 0.3s ease;
}
.card:hover .projectImage {
transform: scale(1.02);
}
.cardBody {
display: flex;
flex: 1;
@@ -243,11 +216,6 @@
transition: color 0.2s ease, gap 0.2s ease;
}
.caseStudyLink:hover {
gap: 14px;
color: var(--color-blue-700);
}
.caseStudyLink:focus-visible {
border-radius: 2px;
outline: 3px solid var(--focus-ring);
@@ -258,10 +226,6 @@
color: var(--accent-default);
}
[data-theme="dark"] .caseStudyLink:hover {
color: var(--color-blue-100);
}
.scrollbarWrapper {
display: flex;
justify-content: center;
@@ -329,3 +293,106 @@
[data-theme="dark"] .scrollbar::-moz-range-thumb {
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 styles from './ProjectsSection.module.css';
const CARD_STEP = 384;
export const ProjectsSection: React.FC = () => {
const viewportRef = useRef<HTMLDivElement>(null);
const dragState = useRef({
@@ -40,8 +38,14 @@ export const ProjectsSection: React.FC = () => {
}, []);
const scrollByCard = (direction: -1 | 1) => {
viewportRef.current?.scrollBy({
left: direction * CARD_STEP,
const viewport = viewportRef.current;
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',
});
};

View File

@@ -84,12 +84,9 @@
line-height: var(--line-height-label-small);
color: var(--text-secondary);
font-weight: 500;
transition: all 0.2s ease;
}
.badgePill:hover {
border-color: var(--accent-default);
color: var(--text-primary);
transition:
color var(--motion-duration-fast) ease,
border-color var(--motion-duration-fast) ease;
}
.toolsBlock {
@@ -113,16 +110,15 @@
border: 1px solid var(--border-default);
border-radius: var(--radius-lg);
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;
}
.toolTile:hover {
border-color: var(--accent-default);
transform: translateY(-4px);
box-shadow: 0 8px 24px var(--shadow-color);
}
.toolDimmed {
opacity: 0.35;
filter: grayscale(0.8);
@@ -168,3 +164,87 @@
[data-theme="dark"] .toolTile {
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);
}
}