Skip to content

Commit

Permalink
Extract GistCard from gist list page
Browse files Browse the repository at this point in the history
  • Loading branch information
MartijnHols committed Dec 8, 2024
1 parent dc95aad commit ca14dca
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 139 deletions.
149 changes: 149 additions & 0 deletions components/GistCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import { css } from '@emotion/react'
import styled from '@emotion/styled'
import AngledContainer from './AngledContainer'
import { GistTag, SerializableGistMeta } from './GistMeta'
import Link from './Link'
import PublicationDate from './PublicationDate'
import Tag from './Tag'

const ArticleTitle = styled.h2(
({ theme }) => css`
color: ${theme.colors.black};
text-decoration: none;
border-bottom: 10px solid ${theme.colors.black};
transition: all 120ms ease-out;
// It is not possible to animate a linear-gradient (yet), so we need to do
// some background-positioning trickery to get the effect we want.
background-image: linear-gradient(
0deg,
${theme.colors.black} 50%,
transparent 50%
);
background-size: 100% 200%;
background-position-y: 0px;
margin-top: 0;
border-top: 0;
transform: none;
// Add some extra space on top so the hover state background's top isn't too
// close to the text.
padding-top: ${theme.spacing.x1}px;
margin-top: -${theme.spacing.x1}px;
margin-bottom: 0;
hyphens: auto;
font-size: 2em;
::before {
content: none;
}
@media (min-width: ${theme.breakpoints.TABLET}px) {
hyphens: manual;
font-size: 3em;
}
`,
)
const ArticleLink = styled(Link, {
shouldForwardProp: (prop) => prop !== 'howTo',
})<{ howTo?: boolean }>(({ theme, howTo }) => [
css`
display: block;
margin-top: ${theme.spacing.x6}px;
margin-bottom: ${theme.spacing.x6}px;
margin-left: auto;
margin-right: auto;
color: ${theme.colors.black};
:hover {
${ArticleTitle} {
color: ${theme.colors.white};
background-position-y: 100%;
}
}
`,
howTo &&
css`
max-width: 90%;
`,
])
const Article = styled(AngledContainer, {
shouldForwardProp: (prop) => prop !== 'howTo',
})<{ howTo?: boolean }>(({ theme, howTo }) => [
css`
--background: ${theme.colors.yellow50};
// Top and bottom margins are not equal since the angle changes the visual
// margin. I believe the left-most column is most important to appear
// visually aligned.
margin-top: ${theme.spacing.x5}px;
margin-bottom: ${theme.spacing.x7}px;
// Offset the padding so the code text aligns with the rest of the text
margin-left: -${theme.spacing.x4}px;
margin-right: -${theme.spacing.x4}px;
padding: ${theme.spacing.x3}px ${theme.spacing.x4}px ${theme.spacing.x2}px;
`,
howTo &&
css`
padding: ${theme.spacing.x2}px;
font-size: 90%;
@media (min-width: ${theme.breakpoints.TABLET}px) {
padding: ${theme.spacing.x2}px ${theme.spacing.x3}px;
${ArticleTitle} {
font-size: 2em;
border-bottom: ${theme.spacing.x1}px solid ${theme.colors.black};
}
}
`,
])
const ArticleMetadata = styled.div(
({ theme }) => css`
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: ${theme.spacing.x2}px;
`,
)
const PublishedAt = styled.div`
opacity: 0.6;
`
const Tags = styled.div(
({ theme }) => css`
display: flex;
gap: ${theme.spacing.x1}px;
flex-wrap: wrap;
`,
)

interface Props {
gist: SerializableGistMeta
}

const GistCard = ({ gist }: Props) => (
<ArticleLink
href={`/gists/${gist.slug}`}
className="plain"
howTo={gist.tags.includes(GistTag.HowTo)}
>
<Article
as="article"
boxShadow={false}
howTo={gist.tags.includes(GistTag.HowTo)}
>
<ArticleTitle>{gist.title}</ArticleTitle>
<p>{gist.description}</p>
<ArticleMetadata>
<PublishedAt>
Published <PublicationDate date={gist.publishedAt} />
</PublishedAt>
<Tags>
{gist.tags.map((tag) => (
<Tag key={tag}>{tag}</Tag>
))}
</Tags>
</ArticleMetadata>
</Article>
</ArticleLink>
)

export default GistCard
4 changes: 4 additions & 0 deletions components/GistMeta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,7 @@ export default interface GistMeta {
tags: GistTag[]
relatedGist?: Promise<{ meta: GistMeta }>
}

export type SerializableGistMeta = Omit<GistMeta, 'titleReact'> & {
publishedAt: PublicationDate
}
142 changes: 3 additions & 139 deletions pages/gists/index.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
import { css } from '@emotion/react'
import styled from '@emotion/styled'
import { GetStaticProps } from 'next'
import { useRouter } from 'next/router'
import Angle from '../../components/Angle'
import AngledContainer from '../../components/AngledContainer'
import BaseHead from '../../components/BaseHead'
import Container from '../../components/Container'
import GistCard from '../../components/GistCard'
import GistMeta, {
GistTag,
PublicationDate as PublicationDateType,
SerializableGistMeta,
} from '../../components/GistMeta'
import Link from '../../components/Link'
import PageWrapper from '../../components/PageWrapper'
import PublicationDate from '../../components/PublicationDate'
import Tag from '../../components/Tag'
import TopBar from '../../components/TopBar'
import absoluteUrl from '../../utils/absoluteUrl'
Expand Down Expand Up @@ -42,10 +41,6 @@ export const filterUnpublished = (
const makeFilterByTag = (tag: GistTag | undefined) => (gist: GistMeta) =>
tag === undefined || gist.tags.includes(tag)

type SerializableGistMeta = Omit<GistMeta, 'titleReact'> & {
publishedAt: PublicationDateType
}

export const getStaticProps: GetStaticProps<Props> = async () => {
await generateRssFeed()

Expand Down Expand Up @@ -79,114 +74,6 @@ const ArticleList = styled.ul`
list-style: none;
padding: 0;
`
const ArticleTitle = styled.h2(
({ theme }) => css`
color: ${theme.colors.black};
text-decoration: none;
border-bottom: 10px solid ${theme.colors.black};
transition: all 120ms ease-out;
// It is not possible to animate a linear-gradient (yet), so we need to do
// some background-positioning trickery to get the effect we want.
background-image: linear-gradient(
0deg,
${theme.colors.black} 50%,
transparent 50%
);
background-size: 100% 200%;
background-position-y: 0px;
margin-top: 0;
border-top: 0;
transform: none;
// Add some extra space on top so the hover state background's top isn't too
// close to the text.
padding-top: ${theme.spacing.x1}px;
margin-top: -${theme.spacing.x1}px;
margin-bottom: 0;
hyphens: auto;
font-size: 2em;
::before {
content: none;
}
@media (min-width: ${theme.breakpoints.TABLET}px) {
hyphens: manual;
font-size: 3em;
}
`,
)
const ArticleLink = styled(Link, {
shouldForwardProp: (prop) => prop !== 'howTo',
})<{ howTo?: boolean }>(({ theme, howTo }) => [
css`
display: block;
margin-top: ${theme.spacing.x6}px;
margin-bottom: ${theme.spacing.x6}px;
margin-left: auto;
margin-right: auto;
color: ${theme.colors.black};
:hover {
${ArticleTitle} {
color: ${theme.colors.white};
background-position-y: 100%;
}
}
`,
howTo &&
css`
max-width: 90%;
`,
])
const Article = styled(AngledContainer, {
shouldForwardProp: (prop) => prop !== 'howTo',
})<{ howTo?: boolean }>(({ theme, howTo }) => [
css`
--background: ${theme.colors.yellow50};
// Top and bottom margins are not equal since the angle changes the visual
// margin. I believe the left-most column is most important to appear
// visually aligned.
margin-top: ${theme.spacing.x5}px;
margin-bottom: ${theme.spacing.x7}px;
// Offset the padding so the code text aligns with the rest of the text
margin-left: -${theme.spacing.x4}px;
margin-right: -${theme.spacing.x4}px;
padding: ${theme.spacing.x3}px ${theme.spacing.x4}px ${theme.spacing.x2}px;
`,
howTo &&
css`
padding: ${theme.spacing.x2}px;
font-size: 90%;
@media (min-width: ${theme.breakpoints.TABLET}px) {
padding: ${theme.spacing.x2}px ${theme.spacing.x3}px;
${ArticleTitle} {
font-size: 2em;
border-bottom: ${theme.spacing.x1}px solid ${theme.colors.black};
}
}
`,
])
const ArticleMetadata = styled.div(
({ theme }) => css`
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: ${theme.spacing.x2}px;
`,
)
const PublishedAt = styled.div`
opacity: 0.6;
`
const Tags = styled.div(
({ theme }) => css`
display: flex;
gap: ${theme.spacing.x1}px;
flex-wrap: wrap;
`,
)

interface Props {
gists: SerializableGistMeta[]
Expand Down Expand Up @@ -217,30 +104,7 @@ const GistsIndex = ({ gists }: Props) => {
<ArticleList>
{filteredGists.map((gist) => (
<li key={gist.slug}>
<ArticleLink
href={`/gists/${gist.slug}`}
className="plain"
howTo={gist.tags.includes(GistTag.HowTo)}
>
<Article
as="article"
boxShadow={false}
howTo={gist.tags.includes(GistTag.HowTo)}
>
<ArticleTitle>{gist.title}</ArticleTitle>
<p>{gist.description}</p>
<ArticleMetadata>
<PublishedAt>
Published <PublicationDate date={gist.publishedAt} />
</PublishedAt>
<Tags>
{gist.tags.map((tag) => (
<Tag key={tag}>{tag}</Tag>
))}
</Tags>
</ArticleMetadata>
</Article>
</ArticleLink>
<GistCard gist={gist} />
</li>
))}
</ArticleList>
Expand Down

0 comments on commit ca14dca

Please sign in to comment.