Redesigned work page and added about page
This commit is contained in:
parent
49bde18f27
commit
c01163cecc
7 changed files with 281 additions and 196 deletions
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
<header class="site-header">
|
<header class="site-header">
|
||||||
<div class="header-content">
|
<div class="header-content">
|
||||||
<a href="/" class="header-link" aria-label="@jedmund">
|
<a href="/about" class="header-link" aria-label="@jedmund">
|
||||||
<Avatar />
|
<Avatar />
|
||||||
</a>
|
</a>
|
||||||
<SegmentedController />
|
<SegmentedController />
|
||||||
|
|
|
||||||
|
|
@ -13,14 +13,15 @@
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.page {
|
.page {
|
||||||
background: var(--page-color);
|
background: var(--page-color);
|
||||||
border-radius: 16px;
|
border-radius: $card-corner-radius; // Match universe posts
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: $unit-4x;
|
gap: $unit-4x;
|
||||||
margin: $unit-6x auto $unit-6x;
|
margin: $unit-6x auto $unit-6x;
|
||||||
padding: $unit-5x;
|
padding: $unit-5x;
|
||||||
max-width: 784px;
|
width: 100%;
|
||||||
|
max-width: 700px;
|
||||||
|
|
||||||
&:first-child {
|
&:first-child {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
|
|
|
||||||
|
|
@ -6,50 +6,100 @@
|
||||||
export let backgroundColor: string
|
export let backgroundColor: string
|
||||||
export let name: string
|
export let name: string
|
||||||
export let description: string
|
export let description: string
|
||||||
|
export let highlightColor: string
|
||||||
|
export let index: number = 0
|
||||||
|
|
||||||
|
$: isEven = index % 2 === 0
|
||||||
|
|
||||||
|
// Create highlighted description
|
||||||
|
$: highlightedDescription = description.replace(
|
||||||
|
new RegExp(`(${name})`, 'gi'),
|
||||||
|
`<span style="color: ${highlightColor};">$1</span>`
|
||||||
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="project-item">
|
<div class="project-item {isEven ? 'even' : 'odd'}">
|
||||||
<SVGHoverEffect
|
<div class="project-logo">
|
||||||
{SVGComponent}
|
<SVGHoverEffect
|
||||||
{backgroundColor}
|
{SVGComponent}
|
||||||
maxMovement={10}
|
{backgroundColor}
|
||||||
containerHeight="220px"
|
maxMovement={10}
|
||||||
bounceDamping={0.2}
|
containerHeight="80px"
|
||||||
/>
|
bounceDamping={0.2}
|
||||||
<h3 class="project-name">{name}</h3>
|
/>
|
||||||
<p class="project-description">{description}</p>
|
</div>
|
||||||
|
<div class="project-content">
|
||||||
|
<p class="project-description">{@html highlightedDescription}</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.project-item {
|
.project-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: row;
|
||||||
gap: $unit;
|
align-items: center;
|
||||||
|
gap: $unit-3x;
|
||||||
|
padding: $unit-3x;
|
||||||
|
background: $grey-100;
|
||||||
|
border-radius: $card-corner-radius;
|
||||||
|
|
||||||
|
&.odd {
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-name {
|
.project-logo {
|
||||||
margin: $unit 0 $unit-half;
|
flex-shrink: 0;
|
||||||
font-size: 1rem;
|
width: 80px;
|
||||||
font-weight: bold;
|
height: 80px;
|
||||||
color: $red-60;
|
|
||||||
|
:global(.svg-container) {
|
||||||
|
width: 80px !important;
|
||||||
|
height: 80px !important;
|
||||||
|
border-radius: $unit-2x;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(svg) {
|
||||||
|
width: 48px !important;
|
||||||
|
height: 48px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-content {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-description {
|
.project-description {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 1rem;
|
font-size: 1.125rem; // 18px
|
||||||
line-height: 1.3;
|
line-height: 1.3;
|
||||||
overflow: hidden;
|
color: $grey-00;
|
||||||
text-overflow: ellipsis;
|
|
||||||
display: -webkit-box;
|
|
||||||
-webkit-line-clamp: 3;
|
|
||||||
-webkit-box-orient: vertical;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@include breakpoint('phone') {
|
@include breakpoint('phone') {
|
||||||
.project-description {
|
.project-item {
|
||||||
-webkit-line-clamp: none;
|
flex-direction: column !important;
|
||||||
overflow: visible;
|
gap: $unit-2x;
|
||||||
max-height: fit-content;
|
padding: $unit-2x;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-logo {
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
|
||||||
|
:global(.svg-container) {
|
||||||
|
width: 60px !important;
|
||||||
|
height: 60px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(svg) {
|
||||||
|
width: 36px !important;
|
||||||
|
height: 36px !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
backgroundColor: string
|
backgroundColor: string
|
||||||
name: string
|
name: string
|
||||||
description: string
|
description: string
|
||||||
|
highlightColor: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const projects: Project[] = [
|
const projects: Project[] = [
|
||||||
|
|
@ -19,58 +20,95 @@
|
||||||
SVGComponent: MaitsuLogo,
|
SVGComponent: MaitsuLogo,
|
||||||
backgroundColor: '#FFF7EA',
|
backgroundColor: '#FFF7EA',
|
||||||
name: 'Maitsu',
|
name: 'Maitsu',
|
||||||
description: "I'm building a hobby journal that helps people make something new every week."
|
description: "Maitsu is a hobby journal that helps people make something new every week.",
|
||||||
|
highlightColor: '#F77754'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
SVGComponent: SlackLogo,
|
SVGComponent: SlackLogo,
|
||||||
backgroundColor: '#4a154b',
|
backgroundColor: '#4a154b',
|
||||||
name: 'Slack',
|
name: 'Slack',
|
||||||
description:
|
description:
|
||||||
'I led design for Workflows and other consumer-facing automation features at Slack.'
|
'At Slack, I helped redefine strategy for Workflows and other features in under the automation umbrella.',
|
||||||
|
highlightColor: '#611F69'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
SVGComponent: FigmaLogo,
|
SVGComponent: FigmaLogo,
|
||||||
backgroundColor: '#2c2c2c',
|
backgroundColor: '#2c2c2c',
|
||||||
name: 'Figma',
|
name: 'Figma',
|
||||||
description: 'I helped lead and set the direction for the early prototyping team at Figma.'
|
description: 'At Figma, I designed features and led R&D and strategy for the nascent prototyping team.',
|
||||||
|
highlightColor: '#0ACF83'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
SVGComponent: PinterestLogo,
|
SVGComponent: PinterestLogo,
|
||||||
backgroundColor: '#f7f7f7',
|
backgroundColor: '#f7f7f7',
|
||||||
name: 'Pinterest',
|
name: 'Pinterest',
|
||||||
description:
|
description:
|
||||||
'As the first design hire at Pinterest, I helped define product direction and grow the design team.'
|
'At Pinterest, I was the first product design hired, and touched almost every part of the product.',
|
||||||
|
highlightColor: '#CB1F27'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<section class="projects">
|
<section class="projects">
|
||||||
<ul>
|
<ul>
|
||||||
{#each projects as project}
|
<li>
|
||||||
|
<div class="intro-card">
|
||||||
|
<p class="intro-text">
|
||||||
|
<span class="highlighted">@jedmund</span> is a software designer and strategist based out of San Francisco.
|
||||||
|
</p>
|
||||||
|
<p class="intro-text">
|
||||||
|
In his 15 year career, he's focused his design practice on building tools that help people connect with technology—and their own creativity.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{#each projects as project, index}
|
||||||
<li>
|
<li>
|
||||||
<ProjectItem {...project} />
|
<ProjectItem {...project} {index} />
|
||||||
</li>
|
</li>
|
||||||
{/each}
|
{/each}
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.projects ul {
|
.projects {
|
||||||
display: grid;
|
display: flex;
|
||||||
grid-template-columns: 1fr 1fr;
|
justify-content: center;
|
||||||
list-style: none;
|
|
||||||
padding: 0;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
gap: $unit-4x;
|
|
||||||
margin: 0;
|
ul {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 700px;
|
||||||
|
gap: $unit-3x;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
@include breakpoint('phone') {
|
li {
|
||||||
grid-template-columns: 1fr;
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.intro-card {
|
||||||
|
padding: $unit-3x;
|
||||||
|
background: $grey-100;
|
||||||
|
border-radius: $card-corner-radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
.intro-text {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 1.125rem; // 18px
|
||||||
|
line-height: 1.3;
|
||||||
|
color: $grey-00;
|
||||||
|
|
||||||
|
&:not(:last-child) {
|
||||||
|
margin-bottom: $unit-2x;
|
||||||
}
|
}
|
||||||
|
|
||||||
li {
|
.highlighted {
|
||||||
flex: 0 0 calc(50% - #{$unit-2x}); /* 50% width minus gap */
|
color: #D0290D;
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,152 +1,5 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Album from '$components/Album.svelte'
|
|
||||||
import Game from '$components/Game.svelte'
|
|
||||||
import MentionList from '$components/MentionList.svelte'
|
|
||||||
import Page from '$components/Page.svelte'
|
|
||||||
import ProjectList from '$components/ProjectList.svelte'
|
import ProjectList from '$components/ProjectList.svelte'
|
||||||
import RecentAlbums from '$components/RecentAlbums.svelte'
|
|
||||||
|
|
||||||
import type { PageData } from './$types'
|
|
||||||
|
|
||||||
export let data: PageData
|
|
||||||
|
|
||||||
$: ({ albums, games, error } = data)
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Page>
|
<ProjectList />
|
||||||
<svelte:fragment slot="header">
|
|
||||||
<h2 class="subheader">@jedmund is a software designer</h2>
|
|
||||||
</svelte:fragment>
|
|
||||||
|
|
||||||
<ProjectList />
|
|
||||||
</Page>
|
|
||||||
|
|
||||||
<Page>
|
|
||||||
<svelte:fragment slot="header">
|
|
||||||
<h2>A little about me</h2>
|
|
||||||
</svelte:fragment>
|
|
||||||
|
|
||||||
<section class="bio">
|
|
||||||
<p>
|
|
||||||
Hello! My name is <em>Justin Edmund</em>. I'm a software designer and developer living in San
|
|
||||||
Francisco.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Right now, I'm spending my free time building a hobby journaling app called <a
|
|
||||||
href="https://maitsu.co"
|
|
||||||
target="_blank">Maitsu</a
|
|
||||||
>. I've spent time at several companies over the last 11 years, but you might know me from
|
|
||||||
<a href="https://www.pinterest.com/" target="_blank">Pinterest</a>, where I was the first
|
|
||||||
design hire.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
I was born and raised in New York City and spend a lot of time in Tokyo. I graduated from <a
|
|
||||||
href="http://design.cmu.edu/"
|
|
||||||
target="_blank">Carnegie Mellon University</a
|
|
||||||
> in 2011 with a Bachelors of Arts in Communication Design.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
I occasionally write about design and development on my <a href="/universe">blog</a>.
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
</Page>
|
|
||||||
<Page>
|
|
||||||
<svelte:fragment slot="header">
|
|
||||||
<h2>Notable mentions</h2>
|
|
||||||
</svelte:fragment>
|
|
||||||
|
|
||||||
<MentionList />
|
|
||||||
</Page>
|
|
||||||
<Page noHorizontalPadding={true}>
|
|
||||||
<svelte:fragment slot="header">
|
|
||||||
<h2>Now playing</h2>
|
|
||||||
</svelte:fragment>
|
|
||||||
|
|
||||||
<RecentAlbums {albums} />
|
|
||||||
|
|
||||||
<!-- <section class="latest-games">
|
|
||||||
{#if games && games.length > 0}
|
|
||||||
<ul>
|
|
||||||
{#each games.slice(0, 3) as game}
|
|
||||||
<Game {game} />
|
|
||||||
{/each}
|
|
||||||
</ul>
|
|
||||||
{:else}
|
|
||||||
<p>Loading games...</p>
|
|
||||||
{/if}
|
|
||||||
</section> -->
|
|
||||||
</Page>
|
|
||||||
|
|
||||||
<footer>
|
|
||||||
<p>© 2024 Justin Edmund</p>
|
|
||||||
</footer>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
a,
|
|
||||||
em {
|
|
||||||
color: $red-60;
|
|
||||||
font-weight: 500;
|
|
||||||
font-style: normal;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
text-decoration: underline;
|
|
||||||
text-decoration-style: wavy;
|
|
||||||
}
|
|
||||||
|
|
||||||
header {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: $unit-2x;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1,
|
|
||||||
h2 {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
color: $accent-color;
|
|
||||||
font-size: 1.2rem;
|
|
||||||
font-weight: 500;
|
|
||||||
|
|
||||||
&.subheader {
|
|
||||||
margin-bottom: $unit-2x;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.bio {
|
|
||||||
font-size: 1rem;
|
|
||||||
line-height: 1.3;
|
|
||||||
|
|
||||||
p:first-child {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
p:last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
list-style: none;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.latest-games ul {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
gap: $unit-4x;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
footer {
|
|
||||||
font-size: 0.85rem;
|
|
||||||
color: $grey-40;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
||||||
115
src/routes/about/+page.svelte
Normal file
115
src/routes/about/+page.svelte
Normal file
|
|
@ -0,0 +1,115 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import Album from '$components/Album.svelte'
|
||||||
|
import Game from '$components/Game.svelte'
|
||||||
|
import MentionList from '$components/MentionList.svelte'
|
||||||
|
import Page from '$components/Page.svelte'
|
||||||
|
import RecentAlbums from '$components/RecentAlbums.svelte'
|
||||||
|
|
||||||
|
import type { PageData } from './$types'
|
||||||
|
|
||||||
|
export let data: PageData
|
||||||
|
|
||||||
|
$: ({ albums, games, error } = data)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Page>
|
||||||
|
<svelte:fragment slot="header">
|
||||||
|
<h2>A little about me</h2>
|
||||||
|
</svelte:fragment>
|
||||||
|
|
||||||
|
<section class="bio">
|
||||||
|
<p>
|
||||||
|
Hello! My name is <em>Justin Edmund</em>. I'm a software designer and developer living in San
|
||||||
|
Francisco.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Right now, I'm spending my free time building a hobby journaling app called <a
|
||||||
|
href="https://maitsu.co"
|
||||||
|
target="_blank">Maitsu</a
|
||||||
|
>. I've spent time at several companies over the last 11 years, but you might know me from
|
||||||
|
<a href="https://www.pinterest.com/" target="_blank">Pinterest</a>, where I was the first
|
||||||
|
design hire.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
I was born and raised in New York City and spend a lot of time in Tokyo. I graduated from <a
|
||||||
|
href="http://design.cmu.edu/"
|
||||||
|
target="_blank">Carnegie Mellon University</a
|
||||||
|
> in 2011 with a Bachelors of Arts in Communication Design.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
I occasionally write about design and development on my <a href="/universe">blog</a>.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
</Page>
|
||||||
|
<Page>
|
||||||
|
<svelte:fragment slot="header">
|
||||||
|
<h2>Notable mentions</h2>
|
||||||
|
</svelte:fragment>
|
||||||
|
|
||||||
|
<MentionList />
|
||||||
|
</Page>
|
||||||
|
<Page noHorizontalPadding={true}>
|
||||||
|
<svelte:fragment slot="header">
|
||||||
|
<h2>Now playing</h2>
|
||||||
|
</svelte:fragment>
|
||||||
|
|
||||||
|
<RecentAlbums {albums} />
|
||||||
|
|
||||||
|
<!-- <section class="latest-games">
|
||||||
|
{#if games && games.length > 0}
|
||||||
|
<ul>
|
||||||
|
{#each games.slice(0, 3) as game}
|
||||||
|
<Game {game} />
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
{:else}
|
||||||
|
<p>Loading games...</p>
|
||||||
|
{/if}
|
||||||
|
</section> -->
|
||||||
|
</Page>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<p>© 2024 Justin Edmund</p>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
a,
|
||||||
|
em {
|
||||||
|
color: $red-60;
|
||||||
|
font-weight: 500;
|
||||||
|
font-style: normal;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
text-decoration: underline;
|
||||||
|
text-decoration-style: wavy;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
color: $accent-color;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-weight: 500;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bio {
|
||||||
|
font-size: 1rem;
|
||||||
|
line-height: 1.3;
|
||||||
|
|
||||||
|
p:first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
p:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: $grey-40;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
28
src/routes/about/+page.ts
Normal file
28
src/routes/about/+page.ts
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
import type { PageLoad } from './$types'
|
||||||
|
import type { Album } from '$lib/types/lastfm'
|
||||||
|
|
||||||
|
export const load: PageLoad = async ({ fetch }) => {
|
||||||
|
try {
|
||||||
|
const [albums] = await Promise.all([
|
||||||
|
fetchRecentAlbums(fetch)
|
||||||
|
])
|
||||||
|
|
||||||
|
return {
|
||||||
|
albums
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error fetching data:', err)
|
||||||
|
return {
|
||||||
|
albums: [],
|
||||||
|
games: [],
|
||||||
|
error: err instanceof Error ? err.message : 'An unknown error occurred'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchRecentAlbums(fetch: typeof window.fetch): Promise<Album[]> {
|
||||||
|
const response = await fetch('/api/lastfm')
|
||||||
|
if (!response.ok) throw new Error(`Failed to fetch albums: ${response.status}`)
|
||||||
|
const musicData: { albums: Album[] } = await response.json()
|
||||||
|
return musicData.albums
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue