Pages Hard
UX/UI Designer Portfolio
Warm editorial UX/UI designer portfolio with GSAP SplitText hero, rotating role words, View Transitions case study overlay, and scroll-driven section reveals.
Open in Lab
MCP
gsap lenis scrolltrigger splittext view-transitions-api
Targets: JS HTML
Code
/* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
31 โ UX/UI DESIGNER PORTFOLIO
Warm cream editorial theme ยท Georgia/Playfair serifs
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */
/* โโ Tokens โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */
:root {
--bg: #faf8f4;
--text: #1a1008;
--muted: #9e8e7e;
--accent: #c17b3c;
--accent-light: #e8a96a;
--accent-pale: #f5e8d8;
--panel: #f0ebe2;
--border: #e5ddd0;
--white: #ffffff;
--font-serif: Georgia, "Times New Roman", Times, serif;
--font-sans: "Helvetica Neue", Helvetica, Arial, sans-serif;
--radius-sm: 8px;
--radius-md: 16px;
--radius-lg: 24px;
--radius-xl: 36px;
--shadow-sm: 0 2px 8px rgba(26, 16, 8, 0.06);
--shadow-md: 0 8px 32px rgba(26, 16, 8, 0.1);
--shadow-lg: 0 20px 60px rgba(26, 16, 8, 0.14);
--shadow-hover: 0 24px 72px rgba(26, 16, 8, 0.18);
--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
--max-w: 1200px;
--gutter: clamp(1.5rem, 4vw, 4rem);
}
/* โโ Reset โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html {
scroll-behavior: smooth;
font-size: 16px;
}
body {
background: var(--bg);
color: var(--text);
font-family: var(--font-sans);
font-size: 1rem;
line-height: 1.6;
overflow-x: hidden;
-webkit-font-smoothing: antialiased;
}
a {
color: inherit;
text-decoration: none;
}
img,
svg {
display: block;
}
/* Override demo-shell for light theme */
#demo-shell-bar {
background: rgba(250, 248, 244, 0.9) !important;
border-bottom: 1px solid var(--border) !important;
color: var(--text) !important;
}
#demo-shell-bar a,
#demo-shell-bar button {
color: var(--text) !important;
}
/* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
HERO
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */
.hero {
position: relative;
min-height: 100vh;
display: flex;
align-items: center;
padding: 8rem var(--gutter) 6rem;
overflow: hidden;
}
.hero__inner {
position: relative;
z-index: 2;
max-width: var(--max-w);
margin: 0 auto;
width: 100%;
}
.hero__eyebrow {
display: flex;
align-items: center;
gap: 1.5rem;
margin-bottom: 2.5rem;
opacity: 0; /* animated in */
}
.year-badge {
display: inline-flex;
align-items: center;
padding: 0.35rem 0.9rem;
background: var(--panel);
border: 1px solid var(--border);
border-radius: 999px;
font-size: 0.75rem;
font-weight: 600;
letter-spacing: 0.08em;
color: var(--muted);
text-transform: uppercase;
}
.hero__location {
font-size: 0.85rem;
color: var(--muted);
letter-spacing: 0.04em;
}
.hero__name {
font-family: var(--font-serif);
font-size: clamp(4.5rem, 11vw, 11rem);
font-weight: 400;
line-height: 0.92;
letter-spacing: -0.02em;
color: var(--text);
margin-bottom: 2rem;
}
/* SplitText chars will be wrapped in spans by GSAP */
.hero__name .char {
display: inline-block;
will-change: transform, opacity;
}
.hero__role {
display: flex;
align-items: baseline;
flex-wrap: wrap;
gap: 0.5rem;
font-family: var(--font-sans);
font-size: clamp(1rem, 2.2vw, 1.5rem);
font-weight: 300;
color: var(--muted);
margin-bottom: 1.5rem;
letter-spacing: 0.01em;
opacity: 0;
}
.role__word {
color: var(--accent);
font-style: italic;
font-weight: 500;
min-width: 6ch;
display: inline-block;
will-change: opacity;
}
.hero__tagline {
font-family: var(--font-serif);
font-size: clamp(1rem, 2vw, 1.35rem);
color: var(--muted);
line-height: 1.55;
margin-bottom: 3rem;
max-width: 42ch;
opacity: 0;
}
.hero__cta {
display: flex;
gap: 1rem;
flex-wrap: wrap;
opacity: 0;
}
/* Buttons */
.btn {
display: inline-flex;
align-items: center;
padding: 0.85rem 2rem;
border-radius: 999px;
font-size: 0.9rem;
font-weight: 600;
letter-spacing: 0.02em;
transition: all 0.25s var(--ease-out-expo);
cursor: pointer;
border: none;
}
.btn--primary {
background: var(--text);
color: var(--bg);
}
.btn--primary:hover {
background: var(--accent);
transform: translateY(-2px);
box-shadow: 0 8px 24px rgba(193, 123, 60, 0.35);
}
.btn--ghost {
background: transparent;
color: var(--text);
border: 1.5px solid var(--border);
}
.btn--ghost:hover {
border-color: var(--accent);
color: var(--accent);
transform: translateY(-2px);
}
/* Scroll hint */
.hero__scroll-hint {
position: absolute;
bottom: 2.5rem;
left: var(--gutter);
display: flex;
flex-direction: column;
align-items: center;
gap: 0.5rem;
z-index: 2;
opacity: 0;
}
.scroll-line {
display: block;
width: 1px;
height: 48px;
background: linear-gradient(to bottom, var(--accent), transparent);
animation: scrollPulse 2s ease-in-out infinite;
}
.scroll-label {
font-size: 0.65rem;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--muted);
writing-mode: vertical-rl;
}
@keyframes scrollPulse {
0%,
100% {
opacity: 0.4;
transform: scaleY(1);
}
50% {
opacity: 1;
transform: scaleY(1.1);
}
}
/* Background decoration */
.hero__bg-deco {
position: absolute;
inset: 0;
pointer-events: none;
z-index: 1;
overflow: hidden;
}
.deco-circle {
position: absolute;
border-radius: 50%;
opacity: 0.35;
}
.deco-circle--1 {
width: clamp(300px, 45vw, 680px);
height: clamp(300px, 45vw, 680px);
background: radial-gradient(circle, var(--accent-pale) 0%, transparent 70%);
top: -15%;
right: -8%;
}
.deco-circle--2 {
width: clamp(200px, 28vw, 420px);
height: clamp(200px, 28vw, 420px);
background: radial-gradient(circle, #e8d8c4 0%, transparent 70%);
bottom: 5%;
left: 20%;
}
/* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
SECTION SHARED
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */
.section {
padding: clamp(5rem, 10vh, 9rem) var(--gutter);
}
.section__inner {
max-width: var(--max-w);
margin: 0 auto;
}
.section__header {
text-align: center;
margin-bottom: clamp(3rem, 6vw, 5rem);
}
.section__header--left {
text-align: left;
}
.section__label {
display: inline-block;
font-size: 0.72rem;
font-weight: 700;
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--accent);
margin-bottom: 0.75rem;
}
.section__title {
font-family: var(--font-serif);
font-size: clamp(2.5rem, 5vw, 4.5rem);
font-weight: 400;
line-height: 1.05;
letter-spacing: -0.02em;
color: var(--text);
}
.reveal-heading {
opacity: 0;
transform: translateY(30px);
}
/* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
WORK GRID
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */
.section--work {
background: var(--panel);
}
.work-grid {
display: grid;
grid-template-columns: repeat(12, 1fr);
gap: 1.5rem;
}
/* Large cards: span 7 cols */
.case-card--large {
grid-column: span 7;
}
/* Small cards: span 5 cols */
.case-card--small {
grid-column: span 5;
}
/* Alternating layout: row 1 โ large left, small right
row 2 โ small left, large right */
.case-card:nth-child(3) {
order: 3;
}
.case-card:nth-child(4) {
order: 4;
}
/* On row 2, flip order visually */
.work-grid .case-card:nth-child(3).case-card--small {
grid-column: 1 / span 5;
}
.work-grid .case-card:nth-child(4).case-card--large {
grid-column: 6 / span 7;
}
/* Case Card */
.case-card {
background: var(--white);
border-radius: var(--radius-lg);
overflow: hidden;
cursor: pointer;
box-shadow: var(--shadow-sm);
transition: transform 0.4s var(--ease-out-expo), box-shadow 0.4s var(--ease-out-expo);
opacity: 0;
transform: translateY(40px);
}
.case-card:hover {
transform: translateY(-6px);
box-shadow: var(--shadow-hover);
}
.case-card__image {
position: relative;
overflow: hidden;
background: var(--card-color, #c17b3c);
}
.case-card--large .case-card__image {
aspect-ratio: 16 / 9;
}
.case-card--small .case-card__image {
aspect-ratio: 4 / 3;
}
.case-card__image-inner {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
padding: 1.5rem;
}
.case-card__body {
padding: 1.75rem;
}
.case-card__meta {
display: flex;
align-items: center;
gap: 0.75rem;
margin-bottom: 0.75rem;
}
.badge {
display: inline-flex;
align-items: center;
padding: 0.25rem 0.75rem;
background: var(--accent-pale);
color: var(--accent);
border-radius: 999px;
font-size: 0.72rem;
font-weight: 700;
letter-spacing: 0.06em;
text-transform: uppercase;
}
.case-card__year {
font-size: 0.8rem;
color: var(--muted);
}
.case-card__title {
font-family: var(--font-serif);
font-size: clamp(1.5rem, 2.5vw, 2rem);
font-weight: 400;
color: var(--text);
margin-bottom: 0.6rem;
line-height: 1.15;
}
.case-card__desc {
font-size: 0.9rem;
color: var(--muted);
line-height: 1.6;
margin-bottom: 1.25rem;
max-width: 48ch;
}
.case-card__cta {
display: inline-flex;
align-items: center;
font-size: 0.85rem;
font-weight: 600;
color: var(--accent);
letter-spacing: 0.02em;
transition: gap 0.2s ease;
}
.case-card:hover .case-card__cta {
text-decoration: underline;
text-underline-offset: 3px;
}
/* โโ Mockup Illustrations โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */
.mockup {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
gap: 6px;
opacity: 0.92;
}
/* Dashboard mockup */
.mockup--dashboard {
background: rgba(255, 255, 255, 0.15);
border-radius: 10px;
padding: 14px;
max-width: 85%;
}
.mockup__bar {
height: 10px;
border-radius: 5px;
background: rgba(255, 255, 255, 0.5);
width: 60%;
}
.mockup__row {
height: 8px;
border-radius: 4px;
background: rgba(255, 255, 255, 0.3);
}
.mockup__row--short {
width: 40%;
}
.mockup__chart {
flex: 1;
border-radius: 6px;
background: rgba(255, 255, 255, 0.2);
margin-top: 4px;
position: relative;
overflow: hidden;
}
.mockup__chart::after {
content: "";
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 60%;
background: linear-gradient(to top, rgba(255, 255, 255, 0.4), transparent);
clip-path: polygon(0 60%, 25% 30%, 50% 50%, 75% 10%, 100% 40%, 100% 100%, 0 100%);
}
/* Mobile mockup */
.mockup--mobile {
max-width: 120px;
background: rgba(255, 255, 255, 0.15);
border-radius: 20px;
padding: 10px 8px;
border: 2px solid rgba(255, 255, 255, 0.3);
}
.mockup__screen {
display: flex;
flex-direction: column;
gap: 8px;
}
.mockup__app-row {
height: 8px;
border-radius: 4px;
background: rgba(255, 255, 255, 0.5);
width: 70%;
margin: 0 auto;
}
.mockup__app-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 5px;
}
.mockup__app-tile {
aspect-ratio: 1;
border-radius: 8px;
background: rgba(255, 255, 255, 0.25);
}
/* Web mockup */
.mockup--web {
background: rgba(255, 255, 255, 0.12);
border-radius: 10px;
padding: 10px;
max-width: 88%;
gap: 8px;
}
.mockup__nav-bar {
height: 10px;
border-radius: 5px;
background: rgba(255, 255, 255, 0.4);
}
.mockup__hero-block {
height: 50px;
border-radius: 6px;
background: rgba(255, 255, 255, 0.2);
}
.mockup__content-cols {
display: flex;
gap: 6px;
flex: 1;
}
.mockup__col {
flex: 1;
border-radius: 5px;
background: rgba(255, 255, 255, 0.15);
min-height: 40px;
}
/* Map mockup */
.mockup--map {
position: relative;
max-width: 88%;
}
.mockup__map-bg {
position: relative;
height: 100%;
min-height: 120px;
border-radius: 12px;
background: rgba(255, 255, 255, 0.15);
overflow: hidden;
}
.mockup__pin {
position: absolute;
width: 12px;
height: 12px;
border-radius: 50% 50% 50% 0;
transform: rotate(-45deg);
background: rgba(255, 255, 255, 0.8);
}
.mockup__pin--1 {
top: 25%;
left: 30%;
}
.mockup__pin--2 {
top: 50%;
left: 60%;
}
.mockup__pin--3 {
top: 70%;
left: 45%;
}
.mockup__route {
position: absolute;
top: 30%;
left: 35%;
width: 30%;
height: 2px;
background: rgba(255, 255, 255, 0.5);
transform: rotate(15deg);
}
.mockup__map-card {
position: absolute;
bottom: 8px;
left: 8px;
right: 8px;
height: 32px;
border-radius: 8px;
background: rgba(255, 255, 255, 0.25);
}
/* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
PROCESS
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */
.section--process {
background: var(--bg);
}
.process-row {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 2rem;
position: relative;
}
/* Connecting line behind steps */
.process-row::before {
content: "";
position: absolute;
top: 2.2rem;
left: calc(12.5% + 1rem);
right: calc(12.5% + 1rem);
height: 1px;
background: var(--border);
z-index: 0;
}
.process-step {
position: relative;
text-align: center;
opacity: 0;
transform: translateY(30px);
}
.process-step__number {
position: relative;
z-index: 1;
display: inline-flex;
align-items: center;
justify-content: center;
width: 4rem;
height: 4rem;
border-radius: 50%;
background: var(--bg);
border: 2px solid var(--border);
font-family: var(--font-serif);
font-size: 1.1rem;
font-weight: 400;
color: var(--accent);
margin-bottom: 1.5rem;
transition: border-color 0.3s ease, background 0.3s ease;
}
.process-step:hover .process-step__number {
border-color: var(--accent);
background: var(--accent-pale);
}
.process-step__connector {
display: none; /* decorative, handled by ::before on parent */
}
.process-step__title {
font-family: var(--font-serif);
font-size: 1.3rem;
font-weight: 400;
color: var(--text);
margin-bottom: 0.6rem;
}
.process-step__desc {
font-size: 0.875rem;
color: var(--muted);
line-height: 1.65;
}
/* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
ABOUT
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */
.section--about {
background: var(--panel);
}
.about-layout {
display: grid;
grid-template-columns: 1fr 1.4fr;
gap: clamp(3rem, 6vw, 6rem);
align-items: start;
}
/* Image */
.about-image {
opacity: 0;
transform: translateX(-30px);
}
.about-image__frame {
position: relative;
border-radius: var(--radius-xl);
overflow: hidden;
aspect-ratio: 4 / 5;
background: var(--accent-pale);
}
.about-image__placeholder {
position: absolute;
inset: 0;
overflow: hidden;
}
.portrait-bg {
position: absolute;
inset: 0;
background: linear-gradient(160deg, #e8c9a4 0%, #d4a876 50%, #c17b3c 100%);
}
.portrait-shape--head {
position: absolute;
width: 55%;
height: 45%;
background: #e8d0b8;
border-radius: 50%;
top: 12%;
left: 50%;
transform: translateX(-50%);
}
.portrait-shape--body {
position: absolute;
width: 80%;
height: 55%;
background: #3d2a1a;
border-radius: 50% 50% 0 0;
bottom: 0;
left: 50%;
transform: translateX(-50%);
}
.about-image__tag {
position: absolute;
bottom: 1.5rem;
left: 1.5rem;
display: inline-flex;
align-items: center;
gap: 0.5rem;
background: rgba(250, 248, 244, 0.92);
backdrop-filter: blur(8px);
border: 1px solid var(--border);
border-radius: 999px;
padding: 0.5rem 1rem;
font-size: 0.8rem;
font-weight: 600;
color: var(--text);
}
.tag-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: #4caf7d;
flex-shrink: 0;
animation: tagPulse 2s ease-in-out infinite;
}
@keyframes tagPulse {
0%,
100% {
box-shadow: 0 0 0 0 rgba(76, 175, 125, 0.4);
}
50% {
box-shadow: 0 0 0 6px rgba(76, 175, 125, 0);
}
}
/* About content */
.about-content {
padding-top: 0.5rem;
opacity: 0;
transform: translateX(30px);
}
.about-bio {
font-size: 1.05rem;
line-height: 1.75;
color: var(--text);
margin-bottom: 1rem;
max-width: 52ch;
}
.about-bio--secondary {
color: var(--muted);
}
.about-skills {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin: 2rem 0;
}
.skill-pill {
padding: 0.4rem 1rem;
background: var(--white);
border: 1.5px solid var(--border);
border-radius: 999px;
font-size: 0.8rem;
font-weight: 500;
color: var(--text);
transition: border-color 0.2s ease, color 0.2s ease;
}
.skill-pill:hover {
border-color: var(--accent);
color: var(--accent);
}
.about-stats {
display: flex;
gap: 2.5rem;
padding-top: 1.5rem;
border-top: 1px solid var(--border);
}
.stat {
display: flex;
flex-direction: column;
}
.stat__number {
font-family: var(--font-serif);
font-size: 2.5rem;
font-weight: 400;
color: var(--text);
line-height: 1;
margin-bottom: 0.25rem;
}
.stat__label {
font-size: 0.78rem;
color: var(--muted);
letter-spacing: 0.04em;
}
/* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
FOOTER
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */
.footer {
background: var(--text);
color: var(--bg);
padding: clamp(5rem, 10vh, 8rem) var(--gutter) 3rem;
}
.footer__inner {
max-width: var(--max-w);
margin: 0 auto;
}
.footer__availability {
display: inline-flex;
align-items: center;
gap: 0.6rem;
font-size: 0.8rem;
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
color: rgba(250, 248, 244, 0.5);
margin-bottom: 2rem;
}
.avail-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: #4caf7d;
animation: tagPulse 2s ease-in-out infinite;
}
.footer__headline {
font-family: var(--font-serif);
font-size: clamp(2.5rem, 6vw, 5.5rem);
font-weight: 400;
line-height: 1.05;
letter-spacing: -0.02em;
color: var(--bg);
margin-bottom: 2.5rem;
opacity: 0;
transform: translateY(30px);
}
.footer__headline em {
font-style: italic;
color: var(--accent-light);
}
.footer__email {
display: inline-block;
font-size: clamp(1rem, 2vw, 1.4rem);
font-weight: 300;
color: rgba(250, 248, 244, 0.7);
border-bottom: 1px solid rgba(250, 248, 244, 0.2);
padding-bottom: 0.2rem;
margin-bottom: 3rem;
transition: color 0.25s ease, border-color 0.25s ease;
}
.footer__email:hover {
color: var(--accent-light);
border-color: var(--accent-light);
}
.footer__links {
display: flex;
gap: 2rem;
margin-bottom: 5rem;
}
.footer__link {
font-size: 0.9rem;
font-weight: 500;
color: rgba(250, 248, 244, 0.5);
letter-spacing: 0.04em;
transition: color 0.2s ease;
}
.footer__link:hover {
color: var(--bg);
}
.footer__bottom {
display: flex;
gap: 2rem;
font-size: 0.78rem;
color: rgba(250, 248, 244, 0.3);
border-top: 1px solid rgba(250, 248, 244, 0.1);
padding-top: 2rem;
}
/* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
CASE STUDY DETAIL OVERLAY
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */
.detail-overlay {
position: fixed;
inset: 0;
z-index: 1000;
background: var(--bg);
overflow-y: auto;
opacity: 0;
pointer-events: none;
transition: opacity 0.3s ease;
}
.detail-overlay.is-open {
opacity: 1;
pointer-events: all;
}
.detail-overlay__close {
position: fixed;
top: 1.5rem;
right: 1.5rem;
z-index: 10;
width: 44px;
height: 44px;
border-radius: 50%;
background: var(--white);
border: 1.5px solid var(--border);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
color: var(--text);
transition: all 0.2s ease;
box-shadow: var(--shadow-sm);
}
.detail-overlay__close:hover {
background: var(--text);
color: var(--bg);
border-color: var(--text);
}
.detail-overlay__inner {
max-width: var(--max-w);
margin: 0 auto;
padding: 5rem var(--gutter) 4rem;
}
.detail-hero {
display: grid;
grid-template-columns: 1.2fr 1fr;
gap: 4rem;
align-items: center;
margin-bottom: 4rem;
padding-bottom: 4rem;
border-bottom: 1px solid var(--border);
}
.detail-hero__image {
border-radius: var(--radius-lg);
aspect-ratio: 4 / 3;
overflow: hidden;
view-transition-name: card-image;
contain: layout;
}
.detail-title {
font-family: var(--font-serif);
font-size: clamp(2.5rem, 5vw, 4rem);
font-weight: 400;
line-height: 1.1;
margin: 1rem 0;
letter-spacing: -0.02em;
}
.detail-summary {
font-size: 1.05rem;
color: var(--muted);
line-height: 1.7;
max-width: 44ch;
}
.detail-body {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 4rem;
margin-bottom: 3rem;
}
.detail-section__title {
font-family: var(--font-serif);
font-size: 1.5rem;
font-weight: 400;
margin-bottom: 1rem;
color: var(--text);
}
.detail-section__text {
font-size: 1rem;
color: var(--muted);
line-height: 1.75;
}
.detail-outcomes {
display: flex;
gap: 2rem;
grid-column: 1 / -1;
margin-top: 1rem;
}
.outcome {
flex: 1;
background: var(--panel);
border-radius: var(--radius-md);
padding: 1.5rem;
border: 1px solid var(--border);
font-size: 0.9rem;
color: var(--text);
line-height: 1.6;
}
/* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
VIEW TRANSITIONS
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */
@keyframes out-to-left {
from {
transform: translateX(0);
opacity: 1;
}
to {
transform: translateX(-30px);
opacity: 0;
}
}
@keyframes in-from-right {
from {
transform: translateX(30px);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
::view-transition-old(card-image) {
animation: 300ms ease out-to-left;
}
::view-transition-new(card-image) {
animation: 300ms ease in-from-right;
}
/* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
REDUCED MOTION
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */
.reduced-motion * {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
.reduced-motion .hero__name .char,
.reduced-motion .reveal-heading,
.reduced-motion .case-card,
.reduced-motion .process-step,
.reduced-motion .about-image,
.reduced-motion .about-content,
.reduced-motion .footer__headline {
opacity: 1 !important;
transform: none !important;
}
.reduced-motion .hero__eyebrow,
.reduced-motion .hero__role,
.reduced-motion .hero__tagline,
.reduced-motion .hero__cta,
.reduced-motion .hero__scroll-hint {
opacity: 1 !important;
}
/* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
RESPONSIVE
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */
@media (max-width: 900px) {
.work-grid {
grid-template-columns: 1fr;
}
.case-card--large,
.case-card--small {
grid-column: 1 / -1;
}
.work-grid .case-card:nth-child(3).case-card--small,
.work-grid .case-card:nth-child(4).case-card--large {
grid-column: 1 / -1;
}
.process-row {
grid-template-columns: 1fr 1fr;
gap: 2rem;
}
.process-row::before {
display: none;
}
.about-layout {
grid-template-columns: 1fr;
}
.about-image {
max-width: 360px;
}
.detail-hero,
.detail-body {
grid-template-columns: 1fr;
}
.detail-outcomes {
flex-direction: column;
}
}
@media (max-width: 600px) {
.process-row {
grid-template-columns: 1fr;
}
.hero__cta {
flex-direction: column;
}
.about-stats {
gap: 1.5rem;
}
.footer__bottom {
flex-direction: column;
gap: 0.5rem;
}
}if (!window.MotionPreference) {
const __mql = window.matchMedia("(prefers-reduced-motion: reduce)");
const __listeners = new Set();
const MotionPreference = {
prefersReducedMotion() {
return __mql.matches;
},
setOverride(value) {
const reduced = Boolean(value);
document.documentElement.classList.toggle("reduced-motion", reduced);
window.dispatchEvent(new CustomEvent("motion-preference", { detail: { reduced } }));
for (const listener of __listeners) {
try {
listener({ reduced, override: reduced, systemReduced: __mql.matches });
} catch {}
}
},
onChange(listener) {
__listeners.add(listener);
try {
listener({
reduced: __mql.matches,
override: null,
systemReduced: __mql.matches,
});
} catch {}
return () => __listeners.delete(listener);
},
getState() {
return { reduced: __mql.matches, override: null, systemReduced: __mql.matches };
},
};
window.MotionPreference = MotionPreference;
}
function prefersReducedMotion() {
return window.MotionPreference.prefersReducedMotion();
}
function initDemoShell() {
// No-op shim in imported standalone snippets.
}
import gsap from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
import { SplitText } from "gsap/SplitText";
import Lenis from "lenis";
// โโ Plugin registration โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
gsap.registerPlugin(ScrollTrigger, SplitText);
// โโ Demo Shell โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
initDemoShell({
title: "UX/UI Designer Portfolio",
category: "pages",
tech: ["gsap", "lenis", "scrolltrigger", "splittext", "view-transitions-api"],
});
// โโ Reduced motion โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
let reduced = prefersReducedMotion();
if (reduced) document.documentElement.classList.add("reduced-motion");
window.addEventListener("motion-preference", (e) => {
reduced = e.detail.reduced;
document.documentElement.classList.toggle("reduced-motion", reduced);
ScrollTrigger.refresh();
});
const dur = (d) => (reduced ? 0 : d);
// โโ Lenis smooth scroll โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
const lenis = new Lenis({ lerp: 0.1, smoothWheel: true });
lenis.on("scroll", ScrollTrigger.update);
gsap.ticker.add((time) => lenis.raf(time * 1000));
gsap.ticker.lagSmoothing(0);
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// HERO ANIMATIONS
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
function initHero() {
const nameEl = document.querySelector(".hero__name");
if (!nameEl) return;
// SplitText โ char by char animation
const split = new SplitText(nameEl, { type: "chars", charsClass: "char" });
const tl = gsap.timeline({ defaults: { ease: "power3.out" } });
tl.from(split.chars, {
y: 60,
opacity: 0,
duration: dur(1),
stagger: 0.03,
})
.to(
".hero__eyebrow",
{
opacity: 1,
duration: dur(0.6),
},
"-=0.5"
)
.to(
".hero__role",
{
opacity: 1,
y: 0,
duration: dur(0.5),
},
"-=0.3"
)
.to(
".hero__tagline",
{
opacity: 1,
duration: dur(0.5),
},
"-=0.2"
)
.to(
".hero__cta",
{
opacity: 1,
duration: dur(0.4),
},
"-=0.1"
)
.to(
".hero__scroll-hint",
{
opacity: 1,
duration: dur(0.4),
},
"-=0.1"
);
// Parallax on background deco circles
if (!reduced) {
gsap.to(".deco-circle--1", {
y: -80,
ease: "none",
scrollTrigger: {
trigger: ".hero",
start: "top top",
end: "bottom top",
scrub: 1.5,
},
});
gsap.to(".deco-circle--2", {
y: -40,
ease: "none",
scrollTrigger: {
trigger: ".hero",
start: "top top",
end: "bottom top",
scrub: 2,
},
});
}
}
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// ROTATING WORDS
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
function initRotatingWords() {
const wordEl = document.getElementById("rotating-word");
if (!wordEl) return;
const words = ["product", "interaction", "visual", "systems"];
let currentIndex = 0;
function rotateWord() {
currentIndex = (currentIndex + 1) % words.length;
if (reduced) {
wordEl.textContent = words[currentIndex];
return;
}
gsap
.timeline()
.to(wordEl, {
opacity: 0,
y: -10,
duration: 0.3,
ease: "power2.in",
onComplete: () => {
wordEl.textContent = words[currentIndex];
gsap.set(wordEl, { y: 12 });
},
})
.to(wordEl, {
opacity: 1,
y: 0,
duration: 0.4,
ease: "power3.out",
});
}
// Start rotating after hero animation finishes
setTimeout(
() => {
setInterval(rotateWord, 2500);
},
reduced ? 0 : 2000
);
}
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// SECTION HEADING REVEALS
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
function initHeadingReveals() {
const headings = document.querySelectorAll(".reveal-heading");
headings.forEach((heading) => {
gsap.to(heading, {
opacity: 1,
y: 0,
duration: dur(0.8),
ease: "power3.out",
scrollTrigger: {
trigger: heading,
start: "top 85%",
toggleActions: "play none none none",
},
});
});
}
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// WORK CARDS
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
function initWorkCards() {
const cards = document.querySelectorAll(".case-card");
cards.forEach((card, i) => {
gsap.to(card, {
opacity: 1,
y: 0,
duration: dur(0.7),
ease: "power3.out",
delay: reduced ? 0 : (i % 2) * 0.12,
scrollTrigger: {
trigger: card,
start: "top 88%",
toggleActions: "play none none none",
},
});
});
}
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// PROCESS STEPS
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
function initProcessSteps() {
const steps = document.querySelectorAll(".process-step");
if (!steps.length) return;
gsap.to(steps, {
opacity: 1,
y: 0,
duration: dur(0.7),
stagger: reduced ? 0 : 0.12,
ease: "power3.out",
scrollTrigger: {
trigger: ".process-row",
start: "top 80%",
toggleActions: "play none none none",
},
});
}
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// ABOUT SECTION
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
function initAbout() {
const image = document.querySelector(".about-image");
const content = document.querySelector(".about-content");
if (image) {
gsap.to(image, {
opacity: 1,
x: 0,
duration: dur(0.9),
ease: "power3.out",
scrollTrigger: {
trigger: ".about-layout",
start: "top 80%",
toggleActions: "play none none none",
},
});
}
if (content) {
gsap.to(content, {
opacity: 1,
x: 0,
duration: dur(0.9),
ease: "power3.out",
delay: reduced ? 0 : 0.15,
scrollTrigger: {
trigger: ".about-layout",
start: "top 80%",
toggleActions: "play none none none",
},
});
}
// Skill pills stagger
const pills = document.querySelectorAll(".skill-pill");
gsap.from(pills, {
opacity: 0,
scale: 0.85,
duration: dur(0.4),
stagger: reduced ? 0 : 0.04,
ease: "back.out(1.5)",
scrollTrigger: {
trigger: ".about-skills",
start: "top 85%",
toggleActions: "play none none none",
},
});
}
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// FOOTER ANIMATION
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
function initFooter() {
const headline = document.querySelector(".footer__headline");
if (!headline) return;
gsap.to(headline, {
opacity: 1,
y: 0,
duration: dur(1),
ease: "power3.out",
scrollTrigger: {
trigger: ".footer",
start: "top 85%",
toggleActions: "play none none none",
},
});
}
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// CASE STUDY PROJECT DATA
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
const projectData = {
finvault: {
title: "FinVault",
badge: "Fintech",
year: "2024",
color: "#d4956a",
summary:
"A personal finance dashboard redesign focused on reducing cognitive load and building financial confidence for everyday users.",
challenge:
"FinVault's existing dashboard overwhelmed users with dense data visualizations and unclear information hierarchy, leading to a 68% feature abandonment rate and low DAU retention.",
approach:
'I led a 3-month discovery sprint: 24 user interviews, diary studies with 8 participants, and a competitive audit of 14 fintech apps. The redesign introduced progressive disclosure, a "financial health score" to anchor the experience, and contextual micro-goals that guided users toward positive behaviors.',
outcomes: [
{
label: "Engagement",
value: "+47% DAU",
desc: "Daily active users within 90 days of launch",
},
{ label: "Task Success", value: "91%", desc: "Up from 54% on core financial planning tasks" },
{ label: "Satisfaction", value: "4.7โ
", desc: "App Store rating after redesign (from 3.2)" },
],
},
luma: {
title: "Luma",
badge: "Mobile App",
year: "2024",
color: "#9b8fbe",
summary:
"AI-powered mood journaling app that adapts its interface โ colors, typography, content โ to the user's emotional state in real time.",
challenge:
"Most journaling apps feel clinical or one-size-fits-all. The brief was to design an experience that felt genuinely empathetic: one that acknowledged where users were emotionally and met them there without being intrusive.",
approach:
'I prototyped 6 different adaptive UI models and ran moderated usability sessions with 18 users across diverse emotional states. The final design uses a gentle color temperature shift, typographic weight changes, and prompt cadence adjustment to create a sense of co-presence without AI "performance."',
outcomes: [
{ label: "Retention", value: "+61%", desc: "30-day retention vs. category benchmark" },
{ label: "Sessions/week", value: "5.2", desc: "Average weekly journal sessions per user" },
{ label: "NPS", value: "72", desc: "Net Promoter Score at 60-day mark" },
],
},
forma: {
title: "Forma DS",
badge: "Design System",
year: "2023",
color: "#6a9e91",
summary:
"A comprehensive design system built for a B2B SaaS platform serving 40k+ users across 12 distinct product areas with 3 engineering teams.",
challenge:
"The platform had accumulated 7 years of UI debt: 4 inconsistent component libraries, 200+ one-off colors, and 3 separate icon sets. Engineers were re-building the same components independently and design reviews were bottlenecks.",
approach:
"I audited all existing UI, ran a component inventory workshop with both design and engineering leads, and established token architecture from scratch. The system was built in Figma with full Storybook parity, documented for both designers and developers, with WCAG AA as a baseline requirement for every component.",
outcomes: [
{ label: "Build Speed", value: "3ร faster", desc: "Feature UI implementation time reduced" },
{ label: "Components", value: "180+", desc: "Fully documented, accessible components" },
{ label: "Adoption", value: "100%", desc: "All 3 engineering teams on Forma DS by Q4" },
],
},
wayfound: {
title: "Wayfound",
badge: "Travel & Navigation",
year: "2023",
color: "#b8956e",
summary:
"Reimagining travel discovery for solo adventurers โ from inspiration to itinerary in a single, fluid, AI-assisted experience.",
challenge:
'Planning a solo trip involves 8โ12 different apps and websites on average. Wayfound wanted to collapse this fragmented experience into one coherent product, without losing the serendipitous "discovery" quality that makes travel planning enjoyable.',
approach:
'Through guerrilla research at hostels and solo travel forums, I mapped 5 distinct traveler personas and their planning rituals. The UX centers on a "wanderboard" that feels like a digital pin-board โ expansive and non-linear โ that progressively structures into a day-by-day itinerary as the user\'s intent becomes more defined.',
outcomes: [
{ label: "Time Saved", value: "4.2 hrs", desc: "Average planning time reduction per trip" },
{ label: "Bookings", value: "+38%", desc: "Conversion to actual bookings vs. control" },
{ label: "Users", value: "15k", desc: "Beta waitlist signups before launch" },
],
},
};
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// VIEW TRANSITIONS + CASE STUDY OVERLAY
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
function initCaseStudyOverlay() {
const overlay = document.getElementById("detailOverlay");
const closeBtn = document.getElementById("detailClose");
const cards = document.querySelectorAll(".case-card");
if (!overlay || !closeBtn) return;
const supportsVT = typeof document.startViewTransition === "function";
function populateOverlay(project) {
const data = projectData[project];
if (!data) return;
// Hero image
const detailImage = document.getElementById("detailImage");
detailImage.style.background = data.color;
detailImage.innerHTML = `
<div style="width:100%;height:100%;display:flex;align-items:center;justify-content:center;opacity:0.3;">
<svg width="80" height="80" viewBox="0 0 80 80" fill="none">
<rect x="10" y="20" width="60" height="40" rx="6" fill="white"/>
<rect x="20" y="30" width="40" height="6" rx="3" fill="${data.color}"/>
<rect x="20" y="42" width="24" height="4" rx="2" fill="${data.color}80"/>
</svg>
</div>
`;
// Text content
document.getElementById("detailMeta").innerHTML = `
<span class="badge">${data.badge}</span>
<span class="case-card__year">${data.year}</span>
`;
document.getElementById("detailTitle").textContent = data.title;
document.getElementById("detailSummary").textContent = data.summary;
document.getElementById("detailChallenge").textContent = data.challenge;
document.getElementById("detailApproach").textContent = data.approach;
if (data.outcomes) {
["detailOutcome1", "detailOutcome2", "detailOutcome3"].forEach((id, i) => {
const el = document.getElementById(id);
if (el && data.outcomes[i]) {
el.innerHTML = `
<div style="font-size:0.72rem;font-weight:700;letter-spacing:0.1em;text-transform:uppercase;color:var(--muted);margin-bottom:0.4rem;">${data.outcomes[i].label}</div>
<div style="font-family:var(--font-serif);font-size:1.6rem;color:var(--accent);margin-bottom:0.3rem;">${data.outcomes[i].value}</div>
<div style="font-size:0.85rem;color:var(--muted);">${data.outcomes[i].desc}</div>
`;
}
});
}
}
function openOverlay(project) {
populateOverlay(project);
const doOpen = () => {
document.body.classList.add("detail-open");
overlay.classList.add("is-open");
overlay.setAttribute("aria-hidden", "false");
overlay.scrollTop = 0;
document.body.style.overflow = "hidden";
lenis.stop();
closeBtn.focus();
};
if (supportsVT && !reduced) {
document.startViewTransition(doOpen);
} else {
doOpen();
}
}
function closeOverlay() {
const doClose = () => {
document.body.classList.remove("detail-open");
overlay.classList.remove("is-open");
overlay.setAttribute("aria-hidden", "true");
document.body.style.overflow = "";
lenis.start();
};
if (supportsVT && !reduced) {
document.startViewTransition(doClose);
} else {
doClose();
}
}
// Attach card click handlers
cards.forEach((card) => {
card.addEventListener("click", () => {
const project = card.dataset.project;
if (project) openOverlay(project);
});
// Keyboard accessibility
card.setAttribute("tabindex", "0");
card.setAttribute("role", "button");
card.addEventListener("keydown", (e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
const project = card.dataset.project;
if (project) openOverlay(project);
}
});
});
// Close button
closeBtn.addEventListener("click", closeOverlay);
// ESC key
document.addEventListener("keydown", (e) => {
if (e.key === "Escape" && overlay.classList.contains("is-open")) {
closeOverlay();
}
});
// Click outside inner content to close
overlay.addEventListener("click", (e) => {
if (e.target === overlay) closeOverlay();
});
}
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// INIT
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
function init() {
initHero();
initRotatingWords();
initHeadingReveals();
initWorkCards();
initProcessSteps();
initAbout();
initFooter();
initCaseStudyOverlay();
// Refresh ScrollTrigger after all setup
ScrollTrigger.refresh();
}
// Run after DOM is ready
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", init);
} else {
init();
}<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Sofia Reyes โ UX/UI Designer Portfolio</title>
<link rel="stylesheet" href="style.css" />
<script type="importmap">{"imports":{"gsap":"https://esm.sh/gsap@3.13.0","gsap/ScrollTrigger":"https://esm.sh/gsap@3.13.0/ScrollTrigger","gsap/SplitText":"https://esm.sh/gsap@3.13.0/SplitText","gsap/Flip":"https://esm.sh/gsap@3.13.0/Flip","gsap/ScrambleTextPlugin":"https://esm.sh/gsap@3.13.0/ScrambleTextPlugin","gsap/TextPlugin":"https://esm.sh/gsap@3.13.0/TextPlugin","gsap/all":"https://esm.sh/gsap@3.13.0/all","gsap/":"https://esm.sh/gsap@3.13.0/","lenis":"https://esm.sh/lenis@1.1.13/dist/lenis.mjs","three":"https://esm.sh/three@0.171.0","three/addons/":"https://esm.sh/three@0.171.0/examples/jsm/"}}</script>
</head>
<body>
<!-- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
HERO
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ -->
<section class="hero" id="hero">
<div class="hero__inner">
<div class="hero__eyebrow">
<span class="year-badge">ยฉ 2024</span>
<span class="hero__location">Based in Barcelona</span>
</div>
<h1 class="hero__name" aria-label="Sofia Reyes">
Sofia<br/>Reyes
</h1>
<div class="hero__role">
<span class="role__prefix">UX/UI &</span>
<span class="role__word" id="rotating-word" aria-live="polite">product</span>
<span class="role__suffix">designer</span>
</div>
<p class="hero__tagline">
Crafting interfaces that feel inevitable โ<br/>
where clarity meets craft.
</p>
<div class="hero__cta">
<a href="#work" class="btn btn--primary">View Selected Work</a>
<a href="#about" class="btn btn--ghost">About Me</a>
</div>
</div>
<div class="hero__scroll-hint" aria-hidden="true">
<span class="scroll-line"></span>
<span class="scroll-label">Scroll</span>
</div>
<div class="hero__bg-deco" aria-hidden="true">
<div class="deco-circle deco-circle--1"></div>
<div class="deco-circle deco-circle--2"></div>
</div>
</section>
<!-- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
SELECTED WORK
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ -->
<section class="section section--work" id="work">
<div class="section__inner">
<header class="section__header">
<span class="section__label">Selected Work</span>
<h2 class="section__title reveal-heading">Case Studies</h2>
</header>
<div class="work-grid">
<!-- Card 1 โ large -->
<article class="case-card case-card--large" data-project="finvault" data-color="#c17b3c">
<div class="case-card__image" style="--card-color: #d4956a;">
<div class="case-card__image-inner">
<div class="mockup mockup--dashboard">
<div class="mockup__bar"></div>
<div class="mockup__row"></div>
<div class="mockup__row mockup__row--short"></div>
<div class="mockup__chart"></div>
</div>
</div>
</div>
<div class="case-card__body">
<div class="case-card__meta">
<span class="badge">Fintech</span>
<span class="case-card__year">2024</span>
</div>
<h3 class="case-card__title">FinVault</h3>
<p class="case-card__desc">A personal finance dashboard redesign focused on reducing cognitive load and building financial confidence.</p>
<span class="case-card__cta">View Case Study โ</span>
</div>
</article>
<!-- Card 2 โ small -->
<article class="case-card case-card--small" data-project="luma" data-color="#7a6b9e">
<div class="case-card__image" style="--card-color: #9b8fbe;">
<div class="case-card__image-inner">
<div class="mockup mockup--mobile">
<div class="mockup__screen">
<div class="mockup__app-row"></div>
<div class="mockup__app-grid">
<div class="mockup__app-tile"></div>
<div class="mockup__app-tile"></div>
<div class="mockup__app-tile"></div>
<div class="mockup__app-tile"></div>
</div>
</div>
</div>
</div>
</div>
<div class="case-card__body">
<div class="case-card__meta">
<span class="badge">Mobile App</span>
<span class="case-card__year">2024</span>
</div>
<h3 class="case-card__title">Luma</h3>
<p class="case-card__desc">AI-powered mood journaling app that adapts its interface to the user's emotional state.</p>
<span class="case-card__cta">View Case Study โ</span>
</div>
</article>
<!-- Card 3 โ small -->
<article class="case-card case-card--small" data-project="forma" data-color="#4a7b6e">
<div class="case-card__image" style="--card-color: #6a9e91;">
<div class="case-card__image-inner">
<div class="mockup mockup--web">
<div class="mockup__nav-bar"></div>
<div class="mockup__hero-block"></div>
<div class="mockup__content-cols">
<div class="mockup__col"></div>
<div class="mockup__col"></div>
<div class="mockup__col"></div>
</div>
</div>
</div>
</div>
<div class="case-card__body">
<div class="case-card__meta">
<span class="badge">Design System</span>
<span class="case-card__year">2023</span>
</div>
<h3 class="case-card__title">Forma DS</h3>
<p class="case-card__desc">A comprehensive design system built for a B2B SaaS platform serving 40k+ users across 12 product areas.</p>
<span class="case-card__cta">View Case Study โ</span>
</div>
</article>
<!-- Card 4 โ large -->
<article class="case-card case-card--large" data-project="wayfound" data-color="#c17b3c">
<div class="case-card__image" style="--card-color: #b8956e;">
<div class="case-card__image-inner">
<div class="mockup mockup--map">
<div class="mockup__map-bg">
<div class="mockup__pin mockup__pin--1"></div>
<div class="mockup__pin mockup__pin--2"></div>
<div class="mockup__pin mockup__pin--3"></div>
<div class="mockup__route"></div>
</div>
<div class="mockup__map-card"></div>
</div>
</div>
</div>
<div class="case-card__body">
<div class="case-card__meta">
<span class="badge">Travel & Navigation</span>
<span class="case-card__year">2023</span>
</div>
<h3 class="case-card__title">Wayfound</h3>
<p class="case-card__desc">Reimagining travel discovery for solo adventurers โ from inspiration to itinerary in a single, fluid experience.</p>
<span class="case-card__cta">View Case Study โ</span>
</div>
</article>
</div>
</div>
</section>
<!-- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
PROCESS
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ -->
<section class="section section--process" id="process">
<div class="section__inner">
<header class="section__header">
<span class="section__label">How I Work</span>
<h2 class="section__title reveal-heading">The Process</h2>
</header>
<div class="process-row">
<div class="process-step" data-step="01">
<div class="process-step__number">01</div>
<div class="process-step__connector" aria-hidden="true"></div>
<h3 class="process-step__title">Research</h3>
<p class="process-step__desc">Interviews, competitive audits, analytics deep-dives. I build empathy before pixels.</p>
</div>
<div class="process-step" data-step="02">
<div class="process-step__number">02</div>
<div class="process-step__connector" aria-hidden="true"></div>
<h3 class="process-step__title">Define</h3>
<p class="process-step__desc">Synthesizing insights into problem statements, journey maps, and success metrics.</p>
</div>
<div class="process-step" data-step="03">
<div class="process-step__number">03</div>
<div class="process-step__connector" aria-hidden="true"></div>
<h3 class="process-step__title">Design</h3>
<p class="process-step__desc">Wireframes to high-fidelity prototypes. Iterating with users and engineering partners.</p>
</div>
<div class="process-step" data-step="04">
<div class="process-step__number">04</div>
<div class="process-step__connector" aria-hidden="true"></div>
<h3 class="process-step__title">Deliver</h3>
<p class="process-step__desc">Spec'd handoffs, design systems, and post-launch measurement to close the loop.</p>
</div>
</div>
</div>
</section>
<!-- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
ABOUT
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ -->
<section class="section section--about" id="about">
<div class="section__inner about-layout">
<div class="about-image">
<div class="about-image__frame">
<div class="about-image__placeholder">
<!-- Decorative portrait placeholder -->
<div class="portrait-bg"></div>
<div class="portrait-shape portrait-shape--head"></div>
<div class="portrait-shape portrait-shape--body"></div>
</div>
<div class="about-image__tag">
<span class="tag-dot"></span>
Available for work
</div>
</div>
</div>
<div class="about-content">
<header class="section__header section__header--left">
<span class="section__label">About</span>
<h2 class="section__title reveal-heading">The Designer</h2>
</header>
<p class="about-bio">
I'm Sofia โ a UX/UI designer with 7 years of experience turning complex systems into clear, delightful products. I've worked with early-stage startups and enterprise teams across fintech, health, and productivity.
</p>
<p class="about-bio about-bio--secondary">
My work lives at the intersection of research and craft. I believe the best interfaces are the ones users never have to think about โ they just work.
</p>
<div class="about-skills">
<span class="skill-pill">UX Research</span>
<span class="skill-pill">Interaction Design</span>
<span class="skill-pill">Design Systems</span>
<span class="skill-pill">Figma</span>
<span class="skill-pill">Prototyping</span>
<span class="skill-pill">User Testing</span>
<span class="skill-pill">Accessibility</span>
<span class="skill-pill">Motion Design</span>
<span class="skill-pill">Visual Design</span>
<span class="skill-pill">Product Strategy</span>
</div>
<div class="about-stats">
<div class="stat">
<span class="stat__number">7+</span>
<span class="stat__label">Years Experience</span>
</div>
<div class="stat">
<span class="stat__number">40+</span>
<span class="stat__label">Projects Shipped</span>
</div>
<div class="stat">
<span class="stat__number">12</span>
<span class="stat__label">Happy Clients</span>
</div>
</div>
</div>
</div>
</section>
<!-- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
FOOTER / CONTACT
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ -->
<footer class="footer" id="contact">
<div class="footer__inner">
<div class="footer__availability">
<span class="avail-dot"></span>
Open for projects
</div>
<h2 class="footer__headline">
Let's make<br/>
something<br/>
<em>remarkable.</em>
</h2>
<a href="mailto:sofia@example.com" class="footer__email">
sofia@example.com
</a>
<div class="footer__links">
<a href="#" class="footer__link">LinkedIn</a>
<a href="#" class="footer__link">Dribbble</a>
<a href="#" class="footer__link">Read.cv</a>
</div>
<div class="footer__bottom">
<span>Sofia Reyes</span>
<span>Barcelona, Spain</span>
<span>2024</span>
</div>
</div>
</footer>
<!-- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
CASE STUDY DETAIL OVERLAY
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ -->
<div class="detail-overlay" id="detailOverlay" aria-hidden="true" role="dialog" aria-modal="true" aria-label="Case Study Detail">
<button class="detail-overlay__close" id="detailClose" aria-label="Close case study">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15 5L5 15M5 5l10 10" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>
</button>
<div class="detail-overlay__inner">
<div class="detail-hero">
<div class="detail-hero__image" id="detailImage">
<!-- Filled dynamically -->
</div>
<div class="detail-hero__content">
<div class="case-card__meta" id="detailMeta"></div>
<h2 class="detail-title" id="detailTitle"></h2>
<p class="detail-summary" id="detailSummary"></p>
</div>
</div>
<div class="detail-body">
<div class="detail-section">
<h3 class="detail-section__title">The Challenge</h3>
<p class="detail-section__text" id="detailChallenge"></p>
</div>
<div class="detail-section">
<h3 class="detail-section__title">My Approach</h3>
<p class="detail-section__text" id="detailApproach"></p>
</div>
<div class="detail-outcomes">
<div class="outcome" id="detailOutcome1"></div>
<div class="outcome" id="detailOutcome2"></div>
<div class="outcome" id="detailOutcome3"></div>
</div>
</div>
</div>
</div>
<script type="module" src="script.js"></script>
</body>
</html>UX/UI Designer Portfolio
Warm editorial UX/UI designer portfolio with GSAP SplitText hero, rotating role words, View Transitions case study overlay, and scroll-driven section reveals.
Source
- Repository:
libs-genclaude - Original demo id:
31-uxui-portfolio
Notes
Warm editorial UX/UI designer portfolio with GSAP SplitText hero, rotating role words, View Transitions case study overlay, and scroll-driven section reveals.