Skip to content

Commit 607c506

Browse files
authored
Update portfolio (#26)
1 parent 6aa0c5d commit 607c506

File tree

9 files changed

+19323
-21859
lines changed

9 files changed

+19323
-21859
lines changed

content/portfolio/harbor.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
title: "Harbor: modern feed reader"
3+
date: 2021-05-25
4+
screenshot: "harbor"
5+
---
6+
7+
<MobileFeature screenshot="harbor">
8+
9+
# Harbor: modern feed reader
10+
11+
With [Harbor](https://harbor.page), I built the RSS reader I always wanted: one with a beautiful interface, smart unified timeline, and no reliance on any third-party services for fetching or syncing feeds. I wanted a feed reader that I could browse casually like I do my Twitter timeline, with the ability to aggregate from hundreds of sources without feeling too overwhelming.
12+
13+
Harbor keeps its unified timeline manageable with a unique feed rating system. When you subscribe to a feed, you can tell Harbor you’re a “casual reader” or a “completionist” for that feed. Harbor then tries to ensure that you won’t miss a post from that blog you love that only posts once a month, while still allowing you to subscribe to feeds that post dozens of times per day.
14+
15+
16+
</MobileFeature>

package-lock.json

Lines changed: 19200 additions & 21814 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/components/DeviceScreenshot.tsx

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import React from 'react'
2+
import styled from 'styled-components'
3+
import useImage from './Hooks/useImage'
4+
import { GatsbyImage, StaticImage } from 'gatsby-plugin-image'
5+
6+
const Aside = styled.aside`
7+
&.screenshot {
8+
justify-self: center;
9+
max-width: 550px;
10+
display: grid;
11+
.screen {
12+
max-width: 71.6%;
13+
margin: -17.5% 0 0 4.5%;
14+
img {
15+
object-fit: contain !important;
16+
}
17+
}
18+
.screen, .frame {
19+
grid-area: 1 / 1;
20+
}
21+
.frame {
22+
pointer-events: none;
23+
z-index: 10;
24+
}
25+
}
26+
`
27+
28+
export default function DeviceScreenshot(
29+
props: {
30+
screenshot: string;
31+
alt?: string;
32+
}
33+
) {
34+
const screenshot = useImage(props.screenshot)?.gatsbyImageData
35+
if (!screenshot) {
36+
return null
37+
}
38+
return (
39+
<Aside className={'screenshot'}>
40+
<GatsbyImage image={screenshot} alt="" className="screen" />
41+
<StaticImage src="../images/12-pro-frame.png" placeholder="none" alt={props.alt ?? ''} className="frame" />
42+
</Aside>
43+
)
44+
}

src/components/Hooks/usePages.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export default function usePages() {
1313
date
1414
description
1515
featuredImage
16+
screenshot
1617
}
1718
}
1819
}

src/components/MobileFeature.tsx

Lines changed: 3 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,13 @@
11
import React from 'react'
22
import styled from 'styled-components'
3+
import DeviceScreenshot from './DeviceScreenshot'
34
import useImage from './Hooks/useImage'
4-
import { GatsbyImage, StaticImage } from 'gatsby-plugin-image'
5+
import { GatsbyImage } from 'gatsby-plugin-image'
56
import { constants, fullWidth } from './Styles'
67

78
const Article = styled.article`
89
display: grid;
910
aside {
10-
&.screenshot {
11-
justify-self: center;
12-
max-width: 550px;
13-
display: grid;
14-
.screen {
15-
max-width: 71.2%;
16-
margin: -17% 0 0 4.7%;
17-
img {
18-
object-fit: contain !important;
19-
}
20-
}
21-
.screen, .frame {
22-
grid-area: 1 / 1;
23-
}
24-
.frame {
25-
pointer-events: none;
26-
z-index: 10;
27-
}
28-
}
2911
&.legacy {
3012
max-width: 630px;
3113
margin-top: max(-4%, -2vw);
@@ -60,17 +42,10 @@ export default function MobileFeature(
6042
}
6143
) {
6244
if (props.screenshot) {
63-
const screenshot = useImage(props.screenshot)?.gatsbyImageData
64-
if (!screenshot) {
65-
return null
66-
}
6745
return (
6846
<Article>
6947
<section>{props.children}</section>
70-
<aside className={'screenshot'}>
71-
<GatsbyImage image={screenshot} alt="" className="screen" />
72-
<StaticImage src="../images/12-mini-frame.png" placeholder="none" alt="" className="frame" />
73-
</aside>
48+
<DeviceScreenshot screenshot={props.screenshot} />
7449
</Article>
7550
)
7651
} else if (props.legacyPhoneImage) {

src/components/Portfolio.tsx

Lines changed: 59 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import useImage from './Hooks/useImage'
44
import usePages from './Hooks/usePages'
55
import withMasonryGridLayout from './Hooks/withMasonryGridLayout'
66
import usePastelColor from './Hooks/usePastelColor'
7+
import DeviceScreenshot from './DeviceScreenshot'
78
import { GatsbyImage } from 'gatsby-plugin-image'
89
import { Link } from 'gatsby'
910
import { constants, fullWidth, popAnimation } from './Styles'
@@ -51,7 +52,7 @@ const Article = styled.article`
5152
@media (max-width: ${constants.mobile}) {
5253
max-height: 90vw;
5354
}
54-
.gatsby-image-wrapper {
55+
& > .gatsby-image-wrapper {
5556
margin: -2em -4em -9em -1em;
5657
z-index: 101;
5758
@media (min-width: ${constants.mobile}) {
@@ -62,6 +63,27 @@ const Article = styled.article`
6263
}
6364
}
6465
}
66+
& > div.screenshot {
67+
transform: scale3d(0.9,0.9,1);
68+
@media (min-width: ${constants.mobile}) {
69+
margin: -3em -4em 0 0;
70+
max-height: min(70vw, 700px);
71+
}
72+
z-index: 101;
73+
& > aside {
74+
transform: rotate(15deg);
75+
@media (max-width: ${constants.mobile}) {
76+
transform: rotate(15deg) scale(0.95) translateX(-1em) translateY(-7em);
77+
}
78+
}
79+
@media (min-width: ${constants.mobile}) {
80+
${popAnimation}
81+
&:hover {
82+
z-index: 1001;
83+
transform: scale3d(0.92,0.92,1);
84+
}
85+
}
86+
}
6587
}
6688
}
6789
}
@@ -81,23 +103,43 @@ export default function Portfolio(props: { children?: JSX.Element }) {
81103
<Article>
82104
<ul className="grid">{props.children && (<li className="intro"><div>{props.children}</div></li>)}{
83105
pages?.map(page => {
84-
const image = useImage(page.context?.frontmatter?.featuredImage)?.gatsbyImageData
85-
if (!image) {
86-
return null
87-
}
88106
const color = usePastelColor(page.context?.frontmatter?.title)
89-
return (
90-
<li
91-
key={page.path}
92-
style={{
93-
'--project-background-color': color.sRGB,
94-
'--project-background-color-p3': color.p3,
95-
}}>
96-
<Link to={page.path}>
97-
<GatsbyImage image={image} alt={page.context?.frontmatter?.title} />
98-
</Link>
99-
</li>
100-
)
107+
if (page.context?.frontmatter?.screenshot) {
108+
return (
109+
<li
110+
key={page.path}
111+
style={{
112+
'--project-background-color': color.sRGB,
113+
'--project-background-color-p3': color.p3,
114+
}}>
115+
<Link to={page.path}>
116+
<div className="screenshot">
117+
<DeviceScreenshot
118+
screenshot={page.context.frontmatter.screenshot}
119+
alt={page.context?.frontmatter?.title}
120+
/>
121+
</div>
122+
</Link>
123+
</li>
124+
)
125+
} else {
126+
const image = useImage(page.context?.frontmatter?.featuredImage)?.gatsbyImageData
127+
if (!image) {
128+
return null
129+
}
130+
return (
131+
<li
132+
key={page.path}
133+
style={{
134+
'--project-background-color': color.sRGB,
135+
'--project-background-color-p3': color.p3,
136+
}}>
137+
<Link to={page.path}>
138+
<GatsbyImage image={image} alt={page.context?.frontmatter?.title} />
139+
</Link>
140+
</li>
141+
)
142+
}
101143
})
102144
}</ul>
103145
</Article>

src/images/12-mini-frame.png

-476 KB
Binary file not shown.

src/images/12-pro-frame.png

473 KB
Loading

src/images/screenshots/harbor.png

2.53 MB
Loading

0 commit comments

Comments
 (0)