diff --git a/public/assets/skills/arrow-peel.svg b/public/assets/skills/arrow-peel.svg new file mode 100644 index 0000000..b8cf631 --- /dev/null +++ b/public/assets/skills/arrow-peel.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/assets/skills/icons/Android.svg b/public/assets/skills/icons/Android.svg new file mode 100644 index 0000000..1b37475 --- /dev/null +++ b/public/assets/skills/icons/Android.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/assets/skills/icons/Angular.svg b/public/assets/skills/icons/Angular.svg new file mode 100644 index 0000000..b810959 --- /dev/null +++ b/public/assets/skills/icons/Angular.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/assets/skills/icons/CSS.svg b/public/assets/skills/icons/CSS.svg new file mode 100644 index 0000000..7ca6eb1 --- /dev/null +++ b/public/assets/skills/icons/CSS.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/assets/skills/icons/Figma.svg b/public/assets/skills/icons/Figma.svg new file mode 100644 index 0000000..e0cd0a2 --- /dev/null +++ b/public/assets/skills/icons/Figma.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/assets/skills/icons/Firebase.svg b/public/assets/skills/icons/Firebase.svg new file mode 100644 index 0000000..5b61d3d --- /dev/null +++ b/public/assets/skills/icons/Firebase.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/public/assets/skills/icons/Git.svg b/public/assets/skills/icons/Git.svg new file mode 100644 index 0000000..e8a356e --- /dev/null +++ b/public/assets/skills/icons/Git.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/public/assets/skills/icons/Js.svg b/public/assets/skills/icons/Js.svg new file mode 100644 index 0000000..d28449a --- /dev/null +++ b/public/assets/skills/icons/Js.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/public/assets/skills/icons/Linux.svg b/public/assets/skills/icons/Linux.svg new file mode 100644 index 0000000..1065e2f --- /dev/null +++ b/public/assets/skills/icons/Linux.svg @@ -0,0 +1,334 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/assets/skills/icons/Next.svg b/public/assets/skills/icons/Next.svg new file mode 100644 index 0000000..3a380f8 --- /dev/null +++ b/public/assets/skills/icons/Next.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/public/assets/skills/icons/Node.svg b/public/assets/skills/icons/Node.svg new file mode 100644 index 0000000..c710263 --- /dev/null +++ b/public/assets/skills/icons/Node.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/assets/skills/icons/React.svg b/public/assets/skills/icons/React.svg new file mode 100644 index 0000000..c5df8bb --- /dev/null +++ b/public/assets/skills/icons/React.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/public/assets/skills/icons/Rest-Api.svg b/public/assets/skills/icons/Rest-Api.svg new file mode 100644 index 0000000..345ac6f --- /dev/null +++ b/public/assets/skills/icons/Rest-Api.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/assets/skills/icons/Supabase.svg b/public/assets/skills/icons/Supabase.svg new file mode 100644 index 0000000..95ffb15 --- /dev/null +++ b/public/assets/skills/icons/Supabase.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/public/assets/skills/icons/Tailwind.svg b/public/assets/skills/icons/Tailwind.svg new file mode 100644 index 0000000..87418e6 --- /dev/null +++ b/public/assets/skills/icons/Tailwind.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/assets/skills/icons/Ts.svg b/public/assets/skills/icons/Ts.svg new file mode 100644 index 0000000..54ef3d8 --- /dev/null +++ b/public/assets/skills/icons/Ts.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/public/assets/skills/icons/Vue.Js.svg b/public/assets/skills/icons/Vue.Js.svg new file mode 100644 index 0000000..ea6dd51 --- /dev/null +++ b/public/assets/skills/icons/Vue.Js.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/public/assets/skills/peel-card-click.webp b/public/assets/skills/peel-card-click.webp new file mode 100644 index 0000000..505b255 Binary files /dev/null and b/public/assets/skills/peel-card-click.webp differ diff --git a/public/assets/skills/peel-card-default.webp b/public/assets/skills/peel-card-default.webp new file mode 100644 index 0000000..a4b4270 Binary files /dev/null and b/public/assets/skills/peel-card-default.webp differ diff --git a/public/assets/skills/peel-card-hover.webp b/public/assets/skills/peel-card-hover.webp new file mode 100644 index 0000000..d8ca08d Binary files /dev/null and b/public/assets/skills/peel-card-hover.webp differ diff --git a/public/assets/skills/skills-background.webp b/public/assets/skills/skills-background.webp new file mode 100644 index 0000000..1a6acce Binary files /dev/null and b/public/assets/skills/skills-background.webp differ diff --git a/public/assets/skills/skills-description-paper-mobile.webp b/public/assets/skills/skills-description-paper-mobile.webp new file mode 100644 index 0000000..9b806e0 Binary files /dev/null and b/public/assets/skills/skills-description-paper-mobile.webp differ diff --git a/public/assets/skills/skills-description-paper.webp b/public/assets/skills/skills-description-paper.webp new file mode 100644 index 0000000..0df86bc Binary files /dev/null and b/public/assets/skills/skills-description-paper.webp differ diff --git a/public/assets/skills/skills-title-loop.webp b/public/assets/skills/skills-title-loop.webp new file mode 100644 index 0000000..cf11247 Binary files /dev/null and b/public/assets/skills/skills-title-loop.webp differ diff --git a/src/app/pages/home/home.html b/src/app/pages/home/home.html index dc09206..d53abd5 100644 --- a/src/app/pages/home/home.html +++ b/src/app/pages/home/home.html @@ -1,4 +1,5 @@
+
diff --git a/src/app/pages/home/home.ts b/src/app/pages/home/home.ts index ed32ad5..3869211 100644 --- a/src/app/pages/home/home.ts +++ b/src/app/pages/home/home.ts @@ -1,11 +1,12 @@ import { Component } from '@angular/core'; -import { Hero } from './sections/hero/hero'; import { About } from './sections/about/about'; +import { Hero } from './sections/hero/hero'; +import { Skills } from './sections/skills/skills'; @Component({ selector: 'app-home', - imports: [Hero, About], + imports: [Hero, About, Skills], templateUrl: './home.html', styleUrl: './home.css', }) diff --git a/src/app/pages/home/sections/about/about.css b/src/app/pages/home/sections/about/about.css index be4a7bf..f54cc35 100644 --- a/src/app/pages/home/sections/about/about.css +++ b/src/app/pages/home/sections/about/about.css @@ -2,34 +2,6 @@ display: block; } -.about__title-underline { - clip-path: inset(0 0 0 0); - animation: draw-about-underline 10s cubic-bezier(0.22, 1, 0.36, 1) infinite; -} - -@keyframes draw-about-underline { - 0%, - 2% { - clip-path: inset(0 100% 0 0); - } - - 12%, - 99.99% { - clip-path: inset(0 0 0 0); - } - - 100% { - clip-path: inset(0 100% 0 0); - } -} - -@media (prefers-reduced-motion: reduce) { - .about__title-underline { - clip-path: inset(0 0 0 0); - animation: none; - } -} - .availability-list { container-type: inline-size; display: flex; diff --git a/src/app/pages/home/sections/about/about.html b/src/app/pages/home/sections/about/about.html index 583c867..11c8b37 100644 --- a/src/app/pages/home/sections/about/about.html +++ b/src/app/pages/home/sections/about/about.html @@ -21,7 +21,7 @@ * { + grid-area: 1 / 1; +} + +.skills__title-loop { + width: 100%; + height: 100%; + pointer-events: none; + user-select: none; +} + +.skills__description { + isolation: isolate; +} + +.skills__description > picture { + pointer-events: none; + user-select: none; +} + +.skills__icon { + filter: grayscale(0); + transition: + filter 220ms ease-out, + transform 260ms cubic-bezier(0.16, 1, 0.3, 1); +} + +.skills__peel-card { + isolation: isolate; + transform: rotate(7deg); + transition: transform 320ms cubic-bezier(0.16, 1, 0.3, 1); +} + +.skills__peel-card:focus-visible { + outline: 3px solid var(--color-secondary); + outline-offset: 6px; +} + +.skills__peel-image { + position: absolute; + z-index: 0; + inset: 0; + width: 100%; + height: 100%; + pointer-events: none; + user-select: none; + transition: opacity 240ms ease-out; +} + +.skills__peel-image--hover, +.skills__peel-image--open, +.skills__peel-back { + opacity: 0; +} + +.skills__peel-front, +.skills__peel-back { + transition: + opacity 180ms ease-out, + transform 280ms cubic-bezier(0.16, 1, 0.3, 1); +} + +.skills__peel-back { + transform: scale(0.92) rotate(-2deg); +} + +.skills__peel-card--open { + transform: rotate(-7deg); +} + +.skills__peel-card--open .skills__peel-image--default, +.skills__peel-card--open .skills__peel-image--hover, +.skills__peel-card--open .skills__peel-front { + opacity: 0; +} + +.skills__peel-card--open .skills__peel-image--open, +.skills__peel-card--open .skills__peel-back { + opacity: 1; +} + +.skills__peel-card--open .skills__peel-back { + transform: scale(1) rotate(0); +} + +@media (min-width: 64rem) and (hover: hover) and (pointer: fine) { + .skills__icon { + filter: grayscale(1); + } + + .skills__item:hover .skills__icon { + filter: grayscale(0); + transform: scale(0.7778); + } +} + +@media (hover: hover) and (pointer: fine) { + .skills__peel-card:not(.skills__peel-card--open):hover { + transform: rotate(3deg) scale(1.03); + } + + .skills__peel-card:not(.skills__peel-card--open):hover .skills__peel-image--default { + opacity: 0; + } + + .skills__peel-card:not(.skills__peel-card--open):hover .skills__peel-image--hover { + opacity: 1; + } + + .skills__peel-card:not(.skills__peel-card--open):hover .skills__peel-front { + opacity: 0; + } +} + +@media (prefers-reduced-motion: reduce) { + .skills__peel-card, + .skills__icon, + .skills__peel-image, + .skills__peel-front, + .skills__peel-back { + transition: none; + } +} diff --git a/src/app/pages/home/sections/skills/skills.html b/src/app/pages/home/sections/skills/skills.html new file mode 100644 index 0000000..2a1ac70 --- /dev/null +++ b/src/app/pages/home/sections/skills/skills.html @@ -0,0 +1,141 @@ +
+
+
+

My stack

+ +
+ +

+ Skill set +

+
+
+ +
+ + +

+ I build maintainable web applications with Angular and TypeScript, and I enjoy expanding my + stack through hands-on projects, thoughtful problem-solving, and continuous learning. +

+
+ + + + +
+
diff --git a/src/app/pages/home/sections/skills/skills.model.ts b/src/app/pages/home/sections/skills/skills.model.ts new file mode 100644 index 0000000..58e477a --- /dev/null +++ b/src/app/pages/home/sections/skills/skills.model.ts @@ -0,0 +1,5 @@ +export interface SkillItem { + readonly id: string; + readonly label: string; + readonly iconSrc: string; +} diff --git a/src/app/pages/home/sections/skills/skills.ts b/src/app/pages/home/sections/skills/skills.ts new file mode 100644 index 0000000..d8fcf1c --- /dev/null +++ b/src/app/pages/home/sections/skills/skills.ts @@ -0,0 +1,95 @@ +import { Component, signal } from '@angular/core'; + +import type { SkillItem } from './skills.model'; + +@Component({ + selector: 'app-skills', + imports: [], + templateUrl: './skills.html', + styleUrl: './skills.css', +}) +export class Skills { + protected readonly isPeelCardOpen = signal(false); + + protected readonly skillItems = [ + { + id: 'javascript', + label: 'JavaScript', + iconSrc: '/assets/skills/icons/Js.svg', + }, + { + id: 'typescript', + label: 'TypeScript', + iconSrc: '/assets/skills/icons/Ts.svg', + }, + { + id: 'css', + label: 'CSS', + iconSrc: '/assets/skills/icons/CSS.svg', + }, + { + id: 'tailwind-css', + label: 'Tailwind CSS', + iconSrc: '/assets/skills/icons/Tailwind.svg', + }, + { + id: 'figma', + label: 'Figma', + iconSrc: '/assets/skills/icons/Figma.svg', + }, + { + id: 'angular', + label: 'Angular', + iconSrc: '/assets/skills/icons/Angular.svg', + }, + { + id: 'react', + label: 'React', + iconSrc: '/assets/skills/icons/React.svg', + }, + { + id: 'vuejs', + label: 'Vue.js', + iconSrc: '/assets/skills/icons/Vue.Js.svg', + }, + { + id: 'nextjs', + label: 'Next.js', + iconSrc: '/assets/skills/icons/Next.svg', + }, + { + id: 'nodejs', + label: 'Node.js', + iconSrc: '/assets/skills/icons/Node.svg', + }, + { + id: 'rest-api', + label: 'REST API', + iconSrc: '/assets/skills/icons/Rest-Api.svg', + }, + { + id: 'supabase', + label: 'Supabase', + iconSrc: '/assets/skills/icons/Supabase.svg', + }, + { + id: 'firebase', + label: 'Firebase', + iconSrc: '/assets/skills/icons/Firebase.svg', + }, + { + id: 'git', + label: 'Git', + iconSrc: '/assets/skills/icons/Git.svg', + }, + { + id: 'linux', + label: 'Linux', + iconSrc: '/assets/skills/icons/Linux.svg', + }, + ] as const satisfies readonly SkillItem[]; + + protected togglePeelCard(): void { + this.isPeelCardOpen.update((isOpen) => !isOpen); + } +} diff --git a/src/styles.css b/src/styles.css index 5647eef..2057215 100644 --- a/src/styles.css +++ b/src/styles.css @@ -64,6 +64,9 @@ --palette-yellow-500: #f7c518; --palette-blue-500: #3355ff; --palette-orange-500: #f87a55; + + --motion-decorative-reveal-duration: 10s; + --motion-decorative-reveal-easing: cubic-bezier(0.22, 1, 0.36, 1); } @theme inline { @@ -79,6 +82,35 @@ --font-hand: 'Kalam', cursive; } +.decorative-reveal { + clip-path: inset(0 0 0 0); + animation: decorative-reveal var(--motion-decorative-reveal-duration) + var(--motion-decorative-reveal-easing) infinite; +} + +@keyframes decorative-reveal { + 0%, + 2% { + clip-path: inset(0 100% 0 0); + } + + 12%, + 99.99% { + clip-path: inset(0 0 0 0); + } + + 100% { + clip-path: inset(0 100% 0 0); + } +} + +@media (prefers-reduced-motion: reduce) { + .decorative-reveal { + clip-path: inset(0 0 0 0); + animation: none; + } +} + .hero__socials a { position: relative; overflow: hidden;