Better labs cards
This commit is contained in:
parent
2203e050bf
commit
69193db45a
3 changed files with 95 additions and 65 deletions
|
|
@ -1,5 +1,6 @@
|
|||
<script lang="ts">
|
||||
import type { Project } from '$lib/types/project'
|
||||
import Button from './admin/Button.svelte'
|
||||
|
||||
const { project }: { project: Project } = $props()
|
||||
|
||||
|
|
@ -11,29 +12,41 @@
|
|||
{#if isClickable}
|
||||
<a href={projectUrl} class="lab-card clickable">
|
||||
<div class="card-header">
|
||||
<h3 class="project-title">{project.title}</h3>
|
||||
<span class="project-year">{project.year}</span>
|
||||
<div class="project-title-container">
|
||||
<h3 class="project-title">{project.title}</h3>
|
||||
<span class="project-year">{project.year}</span>
|
||||
</div>
|
||||
{#if project.externalUrl}
|
||||
<Button
|
||||
variant="primary"
|
||||
buttonSize="medium"
|
||||
href={project.externalUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
iconPosition="right"
|
||||
onclick={(e) => e.stopPropagation()}
|
||||
>
|
||||
Visit site
|
||||
<svg
|
||||
slot="icon"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M5 12h14" />
|
||||
<path d="m12 5 7 7-7 7" />
|
||||
</svg>
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<p class="project-description">{project.description}</p>
|
||||
|
||||
{#if project.externalUrl}
|
||||
<div class="project-links">
|
||||
<span class="project-link primary external">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none">
|
||||
<path
|
||||
d="M10 6H6a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-4M14 4h6m0 0v6m0-6L10 14"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
Visit Project
|
||||
</span>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Add status indicators for different project states -->
|
||||
{#if project.status === 'password-protected'}
|
||||
<div class="status-indicator password-protected">
|
||||
|
|
@ -58,34 +71,36 @@
|
|||
{:else}
|
||||
<article class="lab-card">
|
||||
<div class="card-header">
|
||||
<h3 class="project-title">{project.title}</h3>
|
||||
<span class="project-year">{project.year}</span>
|
||||
<div class="project-title-container">
|
||||
<h3 class="project-title">{project.title}</h3>
|
||||
<span class="project-year">{project.year}</span>
|
||||
</div>
|
||||
|
||||
{#if project.externalUrl}
|
||||
<div class="project-links">
|
||||
<a
|
||||
href={project.externalUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="project-link primary"
|
||||
>
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none">
|
||||
<path
|
||||
d="M10 6H6a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-4M14 4h6m0 0v6m0-6L10 14"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
Visit Project
|
||||
</a>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<p class="project-description">{project.description}</p>
|
||||
|
||||
{#if project.externalUrl}
|
||||
<div class="project-links">
|
||||
<a
|
||||
href={project.externalUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="project-link primary"
|
||||
>
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none">
|
||||
<path
|
||||
d="M10 6H6a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-4M14 4h6m0 0v6m0-6L10 14"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
Visit Project
|
||||
</a>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Add status indicators for different project states -->
|
||||
{#if project.status === 'list-only'}
|
||||
<div class="status-indicator list-only">
|
||||
|
|
@ -116,12 +131,14 @@
|
|||
background: $grey-100;
|
||||
border-radius: $card-corner-radius;
|
||||
padding: $unit-3x;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $unit-3x;
|
||||
transition:
|
||||
transform 0.2s ease,
|
||||
box-shadow 0.2s ease;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
display: block;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-2px);
|
||||
|
|
@ -135,20 +152,35 @@
|
|||
@include breakpoint('phone') {
|
||||
padding: $unit-2x;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
margin-bottom: $unit-2x;
|
||||
align-items: flex-start;
|
||||
gap: $unit-2x;
|
||||
|
||||
// Style the Button component when used in card header
|
||||
:global(.btn) {
|
||||
flex-shrink: 0;
|
||||
margin-top: 2px; // Align with title baseline
|
||||
}
|
||||
}
|
||||
|
||||
.project-title-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $unit-half;
|
||||
}
|
||||
|
||||
.project-title {
|
||||
margin: 0;
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
font-weight: 400;
|
||||
color: $grey-00;
|
||||
line-height: 1.3;
|
||||
|
||||
|
|
@ -158,9 +190,9 @@
|
|||
}
|
||||
|
||||
.project-year {
|
||||
font-size: 0.875rem;
|
||||
font-size: 1rem;
|
||||
color: $grey-40;
|
||||
font-weight: 500;
|
||||
font-weight: 400;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,9 +16,9 @@
|
|||
|
||||
const navItems: NavItem[] = [
|
||||
{ icon: WorkIcon, text: 'Work', href: '/', variant: 'work' },
|
||||
{ icon: LabsIcon, text: 'Labs', href: '/labs', variant: 'labs' },
|
||||
{ icon: UniverseIcon, text: 'Universe', href: '/universe', variant: 'universe' },
|
||||
{ icon: PhotosIcon, text: 'Photos', href: '/photos', variant: 'photos' },
|
||||
{ icon: UniverseIcon, text: 'Universe', href: '/universe', variant: 'universe' }
|
||||
{ icon: LabsIcon, text: 'Labs', href: '/labs', variant: 'labs' }
|
||||
]
|
||||
|
||||
// Track hover state for each item
|
||||
|
|
@ -28,11 +28,11 @@
|
|||
const activeIndex = $derived(
|
||||
currentPath === '/'
|
||||
? 0
|
||||
: currentPath.startsWith('/labs')
|
||||
: currentPath.startsWith('/universe')
|
||||
? 1
|
||||
: currentPath.startsWith('/photos')
|
||||
? 2
|
||||
: currentPath.startsWith('/universe')
|
||||
: currentPath.startsWith('/labs')
|
||||
? 3
|
||||
: -1
|
||||
)
|
||||
|
|
@ -186,25 +186,17 @@
|
|||
}
|
||||
|
||||
// Different animations for each nav item
|
||||
// First item is Work (index 1)
|
||||
// First item is Work
|
||||
.nav-item:nth-of-type(1) :global(svg.animate) {
|
||||
animation: cursorWiggle 0.6s ease;
|
||||
}
|
||||
|
||||
// Second item is Photos (index 2) - animation handled by individual rect animations
|
||||
|
||||
// Third item is Labs (index 3)
|
||||
// Second item is Universe
|
||||
.nav-item:nth-of-type(2) :global(svg.animate) {
|
||||
animation: tubeRotate 0.6s ease;
|
||||
transform-origin: center bottom;
|
||||
}
|
||||
|
||||
// Fourth item is Universe (index 4)
|
||||
.nav-item:nth-of-type(4) :global(svg.animate) {
|
||||
animation: starSpin 0.6s ease;
|
||||
}
|
||||
|
||||
// Specific animation for photo masonry rectangles
|
||||
// Third item is Photos - animation handled by individual rect animations
|
||||
.nav-item:nth-of-type(3) :global(svg.animate rect:nth-child(1)) {
|
||||
animation: masonryRect1 0.6s ease;
|
||||
}
|
||||
|
|
@ -220,4 +212,10 @@
|
|||
.nav-item:nth-of-type(3) :global(svg.animate rect:nth-child(4)) {
|
||||
animation: masonryRect4 0.6s ease;
|
||||
}
|
||||
|
||||
// Fourth item is Labs
|
||||
.nav-item:nth-of-type(4) :global(svg.animate) {
|
||||
animation: tubeRotate 0.6s ease;
|
||||
transform-origin: center bottom;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@
|
|||
width: 100%;
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
padding: $unit-4x $unit-3x;
|
||||
padding: 0 $unit-3x;
|
||||
|
||||
@include breakpoint('phone') {
|
||||
padding: $unit-3x $unit-2x;
|
||||
|
|
|
|||
Loading…
Reference in a new issue