diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index 1427506..78a6e42 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -22,7 +22,7 @@ export const routes: Routes = [ loadComponent: () => import( './features/project-display/components/projects-list/projects-list.component' - ).then((c) => c.ProjectsListComponent), + ).then((c) => c.ProjectListComponent), }, { path: 'experience', diff --git a/src/app/features/project-display/components/project-card/project-card.component.css b/src/app/features/project-display/components/project-card/project-card.component.css new file mode 100644 index 0000000..8ff6aab --- /dev/null +++ b/src/app/features/project-display/components/project-card/project-card.component.css @@ -0,0 +1,706 @@ +/* Base styles with fade-in animations */ +.project-card { + min-height: 300px; + cursor: pointer; + /* Animation - initially hidden */ + opacity: 0; + transform: scale(0.95) translateY(20px); + animation: cardMaterialize 1.2s ease-out forwards; +} + +/* Main card materialization animation */ +@keyframes cardMaterialize { + 0% { + opacity: 0; + transform: scale(0.95) translateY(20px); + filter: blur(2px); + } + 20% { + opacity: 0.3; + transform: scale(0.98) translateY(10px); + filter: blur(1px); + } + 60% { + opacity: 0.8; + transform: scale(1.01) translateY(-2px); + filter: blur(0px); + } + 100% { + opacity: 1; + transform: scale(1) translateY(0); + filter: blur(0px); + } +} + +/* Scanning line effect during load */ +.project-card::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 2px; + background: linear-gradient(90deg, + transparent, + var(--color-nier-accent), + transparent + ); + animation: scanLine 2s ease-out 0.2s forwards; + z-index: 20; +} + +@keyframes scanLine { + 0% { + left: -100%; + opacity: 0; + } + 50% { + opacity: 1; + } + 100% { + left: 100%; + opacity: 0; + } +} + +.project-card:not(.redacted):hover .click-indicator { + opacity: 1; + transition: opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1); +} + +/* Header section animation */ +.project-header { + opacity: 0; + transform: translateX(-30px); + animation: headerSlideIn 0.8s ease-out 0.3s forwards; +} + +@keyframes headerSlideIn { + 0% { + opacity: 0; + transform: translateX(-30px); + border-bottom-color: transparent; + } + 50% { + opacity: 0.6; + transform: translateX(-5px); + } + 100% { + opacity: 1; + transform: translateX(0); + border-bottom-color: var(--color-nier-border); + } +} + +/* Status text fade-in effect (reduced glitch) */ +.project-status { + opacity: 0; + animation: statusFadeIn 0.6s ease-out 0.5s forwards; +} + +@keyframes statusFadeIn { + 0% { + opacity: 0; + transform: translateX(-10px); + } + 100% { + opacity: 1; + transform: translateX(0); + } +} + +/* Title fade and scale */ +.project-title { + opacity: 0; + transform: scale(0.9); + animation: titleExpand 0.7s ease-out 0.7s forwards; +} + +@keyframes titleExpand { + 0% { + opacity: 0; + transform: scale(0.9); + letter-spacing: -0.1em; + } + 70% { + opacity: 0.8; + transform: scale(1.02); + letter-spacing: 0.05em; + } + 100% { + opacity: 1; + transform: scale(1); + letter-spacing: 0.1em; + } +} + +/* Body content staggered animation */ +.project-body { + opacity: 0; + animation: bodyFadeUp 0.8s ease-out 0.9s forwards; +} + +@keyframes bodyFadeUp { + 0% { + opacity: 0; + transform: translateY(15px); + } + 100% { + opacity: 1; + transform: translateY(0); + } +} + +.project-detail { + display: flex; + align-items: flex-start; + font-size: 0.875rem; + line-height: 1.5; + /* Animation */ + opacity: 0; + transform: translateX(-20px); + animation: detailSlideIn 0.5s ease-out forwards; +} + +.project-detail:nth-child(1) { animation-delay: 1.1s; } +.project-detail:nth-child(2) { animation-delay: 1.2s; } +.project-detail:nth-child(3) { animation-delay: 1.3s; } + +@keyframes detailSlideIn { + 0% { + opacity: 0; + transform: translateX(-20px); + } + 100% { + opacity: 1; + transform: translateX(0); + } +} + +.detail-label { + font-weight: 500; + color: var(--color-nier-accent); + min-width: 120px; + margin-right: 1rem; + flex-shrink: 0; +} + +.detail-value { + flex: 1; + word-break: break-word; +} + +.tech-grid { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; +} + +.tech-item { + background-color: var(--color-nier-mid); + color: var(--color-nier-text-dark); + border: 1px solid var(--color-nier-border); + padding: 0.375rem 0.75rem; + font-size: 0.75rem; + font-weight: 500; + letter-spacing: 0.05em; + border-radius: 2px; + /* Animation */ + opacity: 0; + transform: scale(0.8); + animation: techItemPop 0.3s ease-out forwards; + animation-delay: 1.6s; /* Default delay, overridden by Angular */ +} + +@keyframes techItemPop { + 0% { + opacity: 0; + transform: scale(0.8); + border-color: transparent; + } + 50% { + opacity: 0.8; + transform: scale(1.1); + } + 100% { + opacity: 1; + transform: scale(1); + border-color: var(--color-nier-border); + } +} + +/* Tech stack animation */ +.tech-stack { + opacity: 0; + animation: techStackReveal 0.6s ease-out 1.4s forwards; +} + +@keyframes techStackReveal { + 0% { + opacity: 0; + transform: translateY(10px); + border-top-color: transparent; + } + 100% { + opacity: 1; + transform: translateY(0); + border-top-color: var(--color-nier-border); + } +} + +.access-section { + padding: 1rem 1.5rem; + background-color: var(--color-nier-checkered-bg); + background-size: 0.2rem 0.2rem; + background-image: + linear-gradient(to right, var(--color-nier-checkered-grid) 1px, rgba(0, 0, 0, 0) 1px), + linear-gradient(to bottom, var(--color-nier-checkered-grid) 1px, rgba(0, 0, 0, 0) 1px); + border-top: 1px solid var(--color-nier-border); + display: flex; + gap: 1rem; + /* Animation */ + opacity: 0; + transform: translateY(20px); + animation: accessSectionRise 0.7s ease-out 1.8s forwards; +} + +@keyframes accessSectionRise { + 0% { + opacity: 0; + transform: translateY(20px); + border-top-color: transparent; + } + 50% { + opacity: 0.7; + transform: translateY(-2px); + } + 100% { + opacity: 1; + transform: translateY(0); + border-top-color: var(--color-nier-border); + } +} + +.access-btn { + background-color: transparent; + border: 2px solid var(--color-nier-accent); + color: var(--color-nier-accent); + padding: 0.5rem 1rem; + font-family: var(--font-terminal); + font-size: 0.875rem; + font-weight: 500; + cursor: pointer; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + letter-spacing: 0.05em; + flex: 1; + /* Animation */ + opacity: 0; + transform: scale(0.9); + animation: buttonMaterialize 0.4s ease-out forwards; +} + +.access-btn:first-child { animation-delay: 2.0s; } +.access-btn:last-child { animation-delay: 2.1s; } + +@keyframes buttonMaterialize { + 0% { + opacity: 0; + transform: scale(0.9); + border-color: transparent; + box-shadow: none; + } + 30% { + opacity: 0.6; + transform: scale(1.05); + border-color: var(--color-nier-accent); + box-shadow: 0 0 10px rgba(255, 201, 102, 0.3); + } + 100% { + opacity: 1; + transform: scale(1); + border-color: var(--color-nier-accent); + box-shadow: none; + } +} + +.access-btn:hover { + background-color: var(--color-nier-accent); + color: var(--color-nier-text-light); + transform: translateY(-1px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); +} + +.click-indicator { + position: absolute; + bottom: 0.5rem; + right: 1rem; + font-size: 0.7rem; + color: var(--color-nier-mid); + font-family: var(--font-terminal-retro); + opacity: 0; + transition: opacity 0.3s ease; + pointer-events: none; + /* Animation */ + animation: indicatorFade 0.5s ease-out 2.3s forwards; +} + +@keyframes indicatorFade { + 0% { + opacity: 0; + transform: translateY(5px); + } + 100% { + opacity: 0; /* Still hidden until hover */ + transform: translateY(0); + } +} + +/* Status Colors */ +.status-completed { color: var(--color-nier-accent); } +.status-active { color: #4a7c59; } +.status-experimental { color: #7c5a4a; } +.status-archived { color: var(--color-nier-mid); } + +/* Redacted Styles */ +.project-card.redacted { + cursor: not-allowed; + /* Special animation for redacted cards */ + animation: redactedMaterialize 1.5s ease-out forwards; +} + +@keyframes redactedMaterialize { + 0% { + opacity: 0; + transform: scale(0.95); + filter: blur(3px); + } + 30% { + opacity: 0.4; + transform: scale(0.98); + filter: blur(2px); + } + 60% { + opacity: 0.7; + transform: scale(1.01); + filter: blur(1px); + } + 100% { + opacity: 1; + transform: scale(1); + filter: blur(0px); + } +} + +.project-card.redacted:hover { + transform: none; + border-color: var(--color-nier-border); +} + +.redacted-background { + position: relative; + min-height: 300px; + background-color: var(--color-nier-checkered-bg); + background-size: 0.5rem 0.5rem; + background-image: + linear-gradient(45deg, var(--color-nier-mid) 25%, transparent 25%), + linear-gradient(-45deg, var(--color-nier-mid) 25%, transparent 25%), + linear-gradient(45deg, transparent 75%, var(--color-nier-mid) 75%), + linear-gradient(-45deg, transparent 75%, var(--color-nier-mid) 75%); +} + +.redacted-content { + filter: blur(1px); + user-select: none; + opacity: 0.6; +} + +.redacted-overlay { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background-color: var(--color-nier-accent); + color: var(--color-nier-text-light); + padding: 1.5rem 2rem; + border: 2px solid var(--color-nier-text-light); + text-align: center; + z-index: 10; + /* Animation */ + opacity: 0; + animation: redactedOverlaySlam 0.6s ease-out 1.2s forwards; +} + +@keyframes redactedOverlaySlam { + 0% { + opacity: 0; + transform: translate(-50%, -50%) scale(0.8) rotate(-2deg); + } + 50% { + opacity: 0.9; + transform: translate(-50%, -50%) scale(1.1) rotate(1deg); + } + 100% { + opacity: 1; + transform: translate(-50%, -50%) scale(1) rotate(0deg); + } +} + +/* Static overlay animation for redacted cards */ +.static-overlay { + opacity: 0; + animation: staticFadeIn 0.8s ease-out 1.0s forwards; +} + +@keyframes staticFadeIn { + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } +} + +/* Corner accent animation */ +.corner-accent { + opacity: 0; + animation: cornerAccentGlow 0.8s ease-out 2.5s forwards; +} + +@keyframes cornerAccentGlow { + 0% { + opacity: 0; + transform: scale(0.5); + } + 50% { + opacity: 0.8; + transform: scale(1.2); + } + 100% { + opacity: 1; + transform: scale(1); + } +} + +/* Glitch layers animation for added cyberpunk effect */ +.glitch-layer { + opacity: 0; + animation: glitchLayerFlicker 0.1s ease-in-out forwards; +} + +.glitch-layer-1 { animation-delay: 0.8s; } +.glitch-layer-2 { animation-delay: 1.0s; } +.glitch-layer-3 { animation-delay: 1.2s; } + +@keyframes glitchLayerFlicker { + 0%, 100% { opacity: 0; } + 50% { opacity: 0.1; } +} + +/* Responsive Design */ +@media (max-width: 768px) { + .detail-label { + min-width: 100px; + } + + .access-section { + flex-direction: column; + } + + .access-btn { + flex: none; + } + + /* Faster animations on mobile */ + .project-card { + animation-duration: 1s; + } + + .project-header { + animation-duration: 0.6s; + animation-delay: 0.2s; + } + + .project-status { + animation-delay: 0.4s; + } + + .project-title { + animation-delay: 0.5s; + } + + .project-body { + animation-delay: 0.6s; + } +} + +/* Modal Animations */ +.modal-overlay { + opacity: 0; + animation: modalOverlayFadeIn 0.4s ease-out forwards; +} + +@keyframes modalOverlayFadeIn { + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } +} + +.modal-container { + opacity: 0; + transform: scale(0.9) translateY(-20px); + animation: modalContainerSlideIn 0.5s ease-out 0.1s forwards; +} + +@keyframes modalContainerSlideIn { + 0% { + opacity: 0; + transform: scale(0.9) translateY(-20px); + filter: blur(1px); + } + 60% { + opacity: 0.9; + transform: scale(1.02) translateY(-5px); + filter: blur(0px); + } + 100% { + opacity: 1; + transform: scale(1) translateY(0); + filter: blur(0px); + } +} + +.modal-header { + opacity: 0; + transform: translateY(-10px); + animation: modalHeaderFadeIn 0.4s ease-out 0.3s forwards; +} + +@keyframes modalHeaderFadeIn { + 0% { + opacity: 0; + transform: translateY(-10px); + } + 100% { + opacity: 1; + transform: translateY(0); + } +} + +.modal-content { + opacity: 0; + transform: translateY(15px); + animation: modalContentFadeUp 0.5s ease-out 0.4s forwards; +} + +@keyframes modalContentFadeUp { + 0% { + opacity: 0; + transform: translateY(15px); + } + 100% { + opacity: 1; + transform: translateY(0); + } +} + +.modal-section { + opacity: 0; + transform: translateX(-10px); + animation: modalSectionSlideIn 0.3s ease-out forwards; +} + +.modal-section:nth-child(1) { animation-delay: 0.6s; } +.modal-section:nth-child(2) { animation-delay: 0.7s; } +.modal-section:nth-child(3) { animation-delay: 0.8s; } +.modal-section:nth-child(4) { animation-delay: 0.9s; } +.modal-section:nth-child(5) { animation-delay: 1.0s; } + +@keyframes modalSectionSlideIn { + 0% { + opacity: 0; + transform: translateX(-10px); + } + 100% { + opacity: 1; + transform: translateX(0); + } +} + +.modal-close-btn { + opacity: 0; + transform: scale(0.8); + animation: modalCloseBtnPop 0.3s ease-out 0.5s forwards; +} + +@keyframes modalCloseBtnPop { + 0% { + opacity: 0; + transform: scale(0.8); + } + 50% { + opacity: 0.8; + transform: scale(1.1); + } + 100% { + opacity: 1; + transform: scale(1); + } +} + +/* Modal exit animations */ +.modal-overlay.closing { + animation: modalOverlayFadeOut 0.3s ease-in forwards; +} + +@keyframes modalOverlayFadeOut { + 0% { + opacity: 1; + } + 100% { + opacity: 0; + } +} + +.modal-container.closing { + animation: modalContainerSlideOut 0.3s ease-in forwards; +} + +@keyframes modalContainerSlideOut { + 0% { + opacity: 1; + transform: scale(1) translateY(0); + } + 100% { + opacity: 0; + transform: scale(0.95) translateY(-10px); + filter: blur(1px); + } +} +@media (prefers-reduced-motion: reduce) { + .project-card, + .project-header, + .project-status, + .project-title, + .project-body, + .project-detail, + .tech-stack, + .tech-item, + .access-section, + .access-btn, + .click-indicator, + .corner-accent, + .glitch-layer, + .static-overlay, + .redacted-overlay { + animation: none !important; + opacity: 1 !important; + transform: none !important; + } + + .project-card::before { + display: none; + } +} diff --git a/src/app/features/project-display/components/project-card/project-card.component.html b/src/app/features/project-display/components/project-card/project-card.component.html new file mode 100644 index 0000000..a42f277 --- /dev/null +++ b/src/app/features/project-display/components/project-card/project-card.component.html @@ -0,0 +1,144 @@ +
project works!
diff --git a/src/app/features/project-display/components/projects-list/project/project.component.ts b/src/app/features/project-display/components/projects-list/project/project.component.ts deleted file mode 100644 index 2e57d40..0000000 --- a/src/app/features/project-display/components/projects-list/project/project.component.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Component } from '@angular/core'; - -@Component({ - selector: 'app-project', - imports: [], - templateUrl: './project.component.html', - styleUrl: './project.component.css' -}) -export class ProjectComponent { - -} diff --git a/src/app/features/project-display/components/projects-list/projects-list.component.css b/src/app/features/project-display/components/projects-list/projects-list.component.css index e69de29..b14fbf3 100644 --- a/src/app/features/project-display/components/projects-list/projects-list.component.css +++ b/src/app/features/project-display/components/projects-list/projects-list.component.css @@ -0,0 +1,30 @@ +.animate-scan { + background: linear-gradient(90deg, transparent, rgba(0, 0, 0, 0.1), transparent); + animation: scan 3s infinite; +} + +@keyframes scan { + 0% { transform: translateX(-100%); } + 100% { transform: translateX(100%); } +} + +.animate-fade-in-line { + animation: fadeInLine 0.8s forwards; +} + +@keyframes fadeInLine { + to { opacity: 1; } +} + +/* Responsive grid for project cards */ +.grid-project-cards { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(400px, 1fr)); + gap: 1.5rem; +} + +@media (max-width: 768px) { + .grid-project-cards { + grid-template-columns: 1fr; + } +} diff --git a/src/app/features/project-display/components/projects-list/projects-list.component.html b/src/app/features/project-display/components/projects-list/projects-list.component.html index d59212f..e3055e3 100644 --- a/src/app/features/project-display/components/projects-list/projects-list.component.html +++ b/src/app/features/project-display/components/projects-list/projects-list.component.html @@ -1,5 +1,76 @@ -