-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
feat(redesign): blog listing page #2207
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+1,861
−31
Merged
Changes from all commits
Commits
Show all changes
33 commits
Select commit
Hold shift + click to select a range
a492321
feat: add Avatar component
g-francesca 7aea4bc
feat: add post cards
g-francesca 9c2f5c5
style: post cards
g-francesca 90e6261
feat: dynamic blog listing page
g-francesca d1076eb
feat: add Pagination component
g-francesca 0f3c0aa
style: refine listing style
g-francesca 86d9089
feat: add Tag component
g-francesca 230b669
feat: review Avatar component to include multiple authors
g-francesca fcec8ae
feat: set Tabs to filter posts
g-francesca 3488f08
style: refine pagination and tag
g-francesca 9490956
fix: remove usless index
g-francesca 9c6e1bf
fix: minor style fixes
g-francesca 20743a0
fix: formatting
g-francesca 091e4c0
fix: clean up blog posts tags
g-francesca 60d9957
fix: use Image astro component
g-francesca 761c537
fix: pagination links for accessibility
g-francesca 5c6b897
fix: color contrast for tags
g-francesca e351421
fix: formatting
g-francesca 00cf88c
fix(theme): disable CSS transitions during theme change to prevent fl…
ShubhamOulkar 2ba4ed6
fix: show authors on blog posts
ShubhamOulkar c57b72e
style: blog page with 2 cols on tablet
g-francesca e176b88
Merge branch 'redesign-blog-listing' of github.com:expressjs/expressj…
g-francesca 85051d8
Merge branch 'redesign' of https://github.com/expressjs/expressjs.com…
bjohansebas 91aa5b6
add recent blog
bjohansebas a15a966
fixup!
bjohansebas 9fbcc8b
fix: sidebar taking the entire heigh when doing screenshots
g-francesca 02b7910
refactor: review listing pagination
g-francesca 0faa05e
fix: always use english posts
g-francesca d4ec28f
fix: improve theme toggle reliability and fix Firefox/safari flashing
ShubhamOulkar bde3b0f
fix focus state PostCard component.
ShubhamOulkar e016880
fix: hover state PostCard component
ShubhamOulkar 9f39652
fix: allow tab focus only on selected Tab to move focus on tabs use l…
ShubhamOulkar 3bae1fc
fix: focus main-content on skip to main content
ShubhamOulkar File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| --- | ||
| import './PageHead.css'; | ||
| import { Flex, H1, Body } from '@components/primitives'; | ||
|
|
||
| interface Props { | ||
| title: string; | ||
| description?: string; | ||
| } | ||
|
|
||
| const { title, description } = Astro.props; | ||
| --- | ||
|
|
||
| <Flex direction="column" gap="6" align="center" class="page-head"> | ||
| <H1 vMargin={false}>{title}</H1> | ||
| { | ||
| description && ( | ||
| <Body vMargin={false} class="page-head__description"> | ||
| {description} | ||
| </Body> | ||
| ) | ||
| } | ||
| </Flex> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| @layer patterns { | ||
| .page-head { | ||
| margin: var(--space-10) 0; | ||
|
|
||
| @media (--md-up) { | ||
| margin: var(--space-16) 0 var(--space-12); | ||
| } | ||
| } | ||
|
|
||
| .page-head__description { | ||
| max-width: 68rem; | ||
| text-align: center; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| /** | ||
| * PageHead Component Index | ||
| * | ||
| * Export PageHead component | ||
| */ | ||
|
|
||
| export { default } from './PageHead.astro'; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| --- | ||
| /** | ||
| * Pagination Pattern Component | ||
| * | ||
| * Renders previous/next navigation with a current page indicator. | ||
| * | ||
| * @example | ||
| * <Pagination | ||
| * prevUrl="/blog" | ||
| * nextUrl="/blog/3" | ||
| * currentPage={2} | ||
| * lastPage={5} | ||
| * /> | ||
| */ | ||
| import './Pagination.css'; | ||
| import { Icon } from 'astro-icon/components'; | ||
| import { Flex, Button, Body } from '@components/primitives'; | ||
| import type { HTMLAttributes } from 'astro/types'; | ||
|
|
||
| interface Props extends HTMLAttributes<'div'> { | ||
| prevUrl?: string; | ||
| nextUrl?: string; | ||
| currentPage: number; | ||
| lastPage: number; | ||
| } | ||
|
|
||
| const { prevUrl, nextUrl, currentPage, lastPage, ...rest } = Astro.props; | ||
| --- | ||
|
|
||
| <Flex justify="between" align="center" gap="4" class="pagination" {...rest}> | ||
| { | ||
| prevUrl ? ( | ||
| <Button as="a" href={prevUrl} variant="secondary" size="md" data-pagination-prev> | ||
| <Icon name="fluent:arrow-left-20-filled" /> | ||
| Previous | ||
| </Button> | ||
| ) : ( | ||
| <Button variant="secondary" size="md" data-pagination-prev> | ||
| <Icon name="fluent:arrow-left-20-filled" /> | ||
| Previous | ||
| </Button> | ||
| ) | ||
| } | ||
| <Body vMargin={false} data-pagination-label> | ||
| Page {currentPage} of {lastPage} | ||
| </Body> | ||
| { | ||
| nextUrl ? ( | ||
| <Button as="a" href={nextUrl} variant="secondary" size="md" data-pagination-next> | ||
| Next | ||
| <Icon name="fluent:arrow-right-20-filled" /> | ||
| </Button> | ||
| ) : ( | ||
| <Button variant="secondary" size="md" data-pagination-next> | ||
| Next | ||
| <Icon name="fluent:arrow-right-20-filled" /> | ||
| </Button> | ||
| ) | ||
| } | ||
| </Flex> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| @layer patterns { | ||
| .pagination { | ||
| margin-top: var(--space-8); | ||
| } | ||
|
|
||
| .pagination a[aria-disabled='true'] { | ||
| opacity: 0.6; | ||
| pointer-events: none; | ||
| cursor: not-allowed; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| --- | ||
| /** | ||
| * PostCard Pattern Component | ||
| * | ||
| * An article card with a label, title, and author avatar(s). | ||
| * | ||
| * @example — Single author | ||
| * <PostCard | ||
| * href="/blog/secure-express-app" | ||
| * labels={["Security"]} | ||
| * title="How to secure your Express app" | ||
| * coverSrc="/images/cover.jpg" | ||
| * authors={[{ src: "/images/author.jpg", name: "John Doe" }]} | ||
| * avatarCaption="Jun 05, 2025" | ||
| * /> | ||
| * | ||
| * @example — Multiple authors | ||
| * <PostCard | ||
| * href="/blog/express-5" | ||
| * labels={["Release"]} | ||
| * title="Express 5.0 is here" | ||
| * coverSrc="/images/cover.jpg" | ||
| * authors={[ | ||
| * { src: "/images/author1.jpg", name: "Jane Smith" }, | ||
| * { src: "/images/author2.jpg", name: "John Doe" }, | ||
| * ]} | ||
| * avatarCaption="Mar 04, 2026" | ||
| * /> | ||
| */ | ||
| import './PostCard.css'; | ||
| import type { HTMLAttributes } from 'astro/types'; | ||
| import { Image } from 'astro:assets'; | ||
| import { H3, Avatar, Tag } from '@components/primitives'; | ||
|
|
||
| interface Author { | ||
| src: string; | ||
| alt?: string; | ||
| name: string; | ||
| } | ||
|
|
||
| interface Props extends HTMLAttributes<'article'> { | ||
| href: string; | ||
| labels: string[]; | ||
| title: string; | ||
| coverSrc?: string; | ||
| coverAlt?: string; | ||
| authors: Author[]; | ||
| avatarCaption?: string; | ||
| } | ||
|
|
||
| const { | ||
| href, | ||
| labels, | ||
| title, | ||
| coverSrc, | ||
| coverAlt = '', | ||
| authors, | ||
| avatarCaption, | ||
| class: className, | ||
| ...rest | ||
| } = Astro.props; | ||
| --- | ||
|
|
||
| <article class:list={['post-card', className]} {...rest}> | ||
| <a href={href} class="post-card__link"> | ||
| <div> | ||
| <!-- TODO: this will replaced by auto-generated OG images --> | ||
| { | ||
| coverSrc && ( | ||
| <Image | ||
| class="post-card__cover" | ||
| src={coverSrc} | ||
| alt={coverAlt || ''} | ||
| width={800} | ||
| height={160} | ||
| loading="eager" | ||
| /> | ||
| ) | ||
| } | ||
| <div class="post-card__labels"> | ||
| {labels.map((label) => <Tag>{label}</Tag>)} | ||
| </div> | ||
| <H3 as="h2" vMargin={false} class="post-card__title"> | ||
| {title} | ||
| </H3> | ||
| </div> | ||
| <Avatar authors={authors} caption={avatarCaption} size="md" /> | ||
| </a> | ||
| </article> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| @layer patterns { | ||
| .post-card { | ||
| display: flex; | ||
| flex-direction: column; | ||
| border: var(--border-width-1) solid var(--color-border-secondary); | ||
| border-radius: var(--radius-base); | ||
| gap: var(--space-4); | ||
| overflow: hidden; | ||
| padding: var(--space-4); | ||
| min-height: var(--size-88); | ||
| height: 100%; | ||
| cursor: pointer; | ||
| transition: ease-in-out; | ||
| transition-property: outline; | ||
|
|
||
| &:hover { | ||
| outline: var(--color-focus-ring) solid var(--border-width-1); | ||
| } | ||
|
|
||
| @media (--md-up) { | ||
| min-height: var(--size-100); | ||
| } | ||
| } | ||
|
|
||
| .post-card__labels { | ||
| display: flex; | ||
| flex-wrap: wrap; | ||
| gap: var(--space-2); | ||
| margin-bottom: var(--space-4); | ||
| } | ||
|
|
||
| .post-card__link { | ||
| display: flex; | ||
| flex-direction: column; | ||
| justify-content: space-between; | ||
| gap: inherit; | ||
| color: inherit; | ||
| text-decoration: none; | ||
| height: 100%; | ||
|
|
||
| &:focus { | ||
| outline: none; | ||
| } | ||
| } | ||
|
|
||
| .post-card:focus-within { | ||
| outline: var(--color-focus-ring) solid var(--border-width-1); | ||
| } | ||
|
|
||
| .post-card__cover { | ||
| margin-bottom: var(--space-3); | ||
| display: block; | ||
| height: var(--size-40); | ||
| width: 100%; | ||
| object-fit: cover; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| --- | ||
| /** | ||
| * Avatar Primitive Component | ||
| * | ||
| * A rounded avatar image (or stacked images for multiple authors) | ||
| * with a name and optional caption displayed to the right. | ||
| * | ||
| * @example — Single author | ||
| * <Avatar | ||
| * authors={[{ src: "/images/author.jpg", name: "John Doe" }]} | ||
| * caption="Jun 05, 2025" | ||
| * /> | ||
| * | ||
| * @example — Multiple authors | ||
| * <Avatar | ||
| * authors={[ | ||
| * { src: "/images/a.jpg", name: "Alex Pit" }, | ||
| * { src: "/images/b.jpg", name: "Tom Cruise" }, | ||
| * ]} | ||
| * caption="Mar 04, 2026" | ||
| * /> | ||
| * | ||
| * @example — Small size | ||
| * <Avatar | ||
| * authors={[{ src: "/images/author.jpg", name: "Jane Smith" }]} | ||
| * caption="Mar 04, 2026" | ||
| * size="sm" | ||
| * /> | ||
| */ | ||
| import './Avatar.css'; | ||
| import type { HTMLAttributes } from 'astro/types'; | ||
| import { Image } from 'astro:assets'; | ||
| import { BodyMd, BodySm, Flex } from '@components/primitives'; | ||
|
|
||
| interface Author { | ||
| src: string; | ||
| alt?: string; | ||
| name: string; | ||
| } | ||
|
|
||
| interface Props extends HTMLAttributes<'div'> { | ||
| authors: Author[]; | ||
| caption?: string; | ||
| size?: 'sm' | 'md'; | ||
| } | ||
|
|
||
| const { authors, caption, size = 'md', class: className, ...rest } = Astro.props; | ||
|
|
||
| const displayName = authors?.map((a) => a.name).join(', ') ?? ''; | ||
| const imgSize = size === 'sm' ? 24 : 32; | ||
| --- | ||
|
|
||
| <Flex align="center" gap="2" class:list={[`avatar--${size}`, className]} {...rest}> | ||
| <div class="avatar__images"> | ||
| { | ||
| authors?.map((a) => ( | ||
| <Image | ||
| class="avatar__image" | ||
| src={a.src} | ||
| alt={a.alt ?? a.name} | ||
| width={imgSize} | ||
| height={imgSize} | ||
| /> | ||
| )) | ||
| } | ||
| </div> | ||
| <div class="avatar__content"> | ||
| <BodyMd as="span" weight="medium" vMargin={false}>{displayName}</BodyMd> | ||
| { | ||
| caption && ( | ||
| <BodySm as="span" color="secondary" vMargin={false}> | ||
| {caption} | ||
| </BodySm> | ||
| ) | ||
| } | ||
| </div> | ||
| </Flex> |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For reference, we’ll have OG images generated automatically, but that will be handled in a separate PR.