Skip to content

Commit fce2ba5

Browse files
committed
add view transitions for PostCard everywhere
1 parent 3a9e0b0 commit fce2ba5

File tree

14 files changed

+139
-24
lines changed

14 files changed

+139
-24
lines changed

docs/working-notes/todo3.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -437,10 +437,13 @@ open images in new tab and check all sizes
437437
links in md colors
438438
fix table of contents styling
439439
retest vertical spacing, font-size and content width
440+
440441
// maybe
441-
astro transitions
442+
astro transitions, works for PostCard on blog, tags, categories, explore
442443
--------
443444

444445
ask about optimal max-width image width
445446
fix .mdx live reload
447+
firefox has white flash bug on theme change after transitions, https://youtu.be/9MChTVlXbf8?t=363
448+
446449
```

src/components/BaseHead.astro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,5 +106,5 @@ const ogImageUrl = new URL(image, baseUrl);
106106
<script src="set-url-here" crossorigin="anonymous"></script>
107107
-->
108108

109-
<ViewTransitions />
109+
<ViewTransitions fallback="none" />
110110
</head>

src/components/PostCard.astro

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import TagList from '@/components/TagList.astro';
99
import { draftText } from '@/constants/data';
1010
import { IMAGE_SIZES } from '@/constants/image';
1111
import { ROUTES } from '@/constants/routes';
12+
import { setTransitionSlug, TRANSITION_ELEMENT_IDS } from '@/constants/transitions';
1213
import { formatDate, formatDateIso } from '@/utils/datetime';
1314
import { cn } from '@/utils/styles';
1415
@@ -39,6 +40,8 @@ const { lastAccessDate, isUpdatedDate } = getPublishedOrUpdatedDate({
3940
publishDate,
4041
updatedDate,
4142
});
43+
44+
const getTransitionNameFromElementId = setTransitionSlug({ pageSlug: slug });
4245
---
4346

4447
<article
@@ -58,7 +61,9 @@ const { lastAccessDate, isUpdatedDate } = getPublishedOrUpdatedDate({
5861
alt={heroAlt}
5962
itemprop="image"
6063
class="w-full h-[168px] xs:h-[250px] md:h-[168px] md:max-w-[298px] object-cover rounded-box"
61-
transition:name={`hero-image-${slug}`}
64+
transition:name={getTransitionNameFromElementId({
65+
elementId: TRANSITION_ELEMENT_IDS.POST_CARD.HERO_IMAGE,
66+
})}
6267
/>
6368
</div>
6469
)
@@ -67,7 +72,12 @@ const { lastAccessDate, isUpdatedDate } = getPublishedOrUpdatedDate({
6772
{/* right column */}
6873
<div class={cn('flex flex-col', { 'md:basis-2/3': !noHero })}>
6974
{/* category and publishDate row*/}
70-
<div class="flex items-start justify-between mb-4 text-sm leading-none">
75+
<div
76+
class="flex items-start justify-between mb-4 text-sm leading-none"
77+
transition:name={getTransitionNameFromElementId({
78+
elementId: TRANSITION_ELEMENT_IDS.POST_CARD.META,
79+
})}
80+
>
7181
<Link
7282
href={`${ROUTES.CATEGORIES}${category}`}
7383
class="inline-flex items-center gap-2 bg-primary-base-200 font-medium px-2 py-0.5 rounded-button"
@@ -87,14 +97,30 @@ const { lastAccessDate, isUpdatedDate } = getPublishedOrUpdatedDate({
8797
</div>
8898

8999
{/* title */}
90-
<h2 class="text-2xl font-bold break-words md:line-clamp-2 mt-0 mb-2">
100+
<h2
101+
class="text-2xl font-bold break-words md:line-clamp-2 mt-0 mb-2"
102+
transition:name={getTransitionNameFromElementId({
103+
elementId: TRANSITION_ELEMENT_IDS.POST_CARD.TITLE,
104+
})}
105+
>
91106
<Link href={`${ROUTES.BLOG}${slug}`} variant="link-heading">
92107
{title}
93108
{draft && <sup class="text-sm text-red-500 ml-1">{draftText}</sup>}
94109
</Link>
95110
</h2>
96111

97-
{description && <p class="text-base mb-4 line-clamp-2">{description}</p>}
112+
{
113+
description && (
114+
<p
115+
class="text-base mb-4 line-clamp-2"
116+
transition:name={getTransitionNameFromElementId({
117+
elementId: TRANSITION_ELEMENT_IDS.POST_CARD.DESCRIPTION,
118+
})}
119+
>
120+
{description}
121+
</p>
122+
)
123+
}
98124

99125
{/* reading time and read more link */}
100126
<div class="flex items-center xs:items-end justify-between">

src/components/PostCardSmall.astro

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import Link from '@/components/Link.astro';
77
import { draftText } from '@/constants/data';
88
import { IMAGE_SIZES } from '@/constants/image';
99
import { ROUTES } from '@/constants/routes';
10+
import { setTransitionSlug, TRANSITION_ELEMENT_IDS } from '@/constants/transitions';
1011
import { formatDate, formatDateIso } from '@/utils/datetime';
1112
import { cn } from '@/utils/styles';
1213
@@ -25,6 +26,8 @@ const { lastAccessDate, isUpdatedDate } = getPublishedOrUpdatedDate({
2526
publishDate,
2627
updatedDate,
2728
});
29+
30+
const getTransitionNameFromElementId = setTransitionSlug({ pageSlug: slug });
2831
---
2932

3033
<article
@@ -40,16 +43,40 @@ const { lastAccessDate, isUpdatedDate } = getPublishedOrUpdatedDate({
4043
src={heroImage}
4144
alt={heroAlt}
4245
class="object-cover rounded-box hidden xs:block"
46+
transition:name={getTransitionNameFromElementId({
47+
elementId: TRANSITION_ELEMENT_IDS.POST_CARD.HERO_IMAGE,
48+
})}
4349
/>
4450
<div>
45-
<h4 class="b-h4 text-xl leading-none line-clamp-1 mb-1">
51+
<h4
52+
class="b-h4 text-xl leading-none line-clamp-1 mb-1"
53+
transition:name={getTransitionNameFromElementId({
54+
elementId: TRANSITION_ELEMENT_IDS.POST_CARD.TITLE,
55+
})}
56+
>
4657
<Link variant="link-heading" href={`${ROUTES.BLOG}${slug}`}>
4758
{draft && <sup class="text-sm text-red-500 mr-1">{draftText}</sup>}
4859
{title}
4960
</Link>
5061
</h4>
51-
{description && <p class="text-base text-captions line-clamp-1 mb-2">{description}</p>}
52-
<span class="inline-flex items-center gap-1 text-sm text-captions text-nowrap">
62+
{
63+
description && (
64+
<p
65+
class="text-base text-captions line-clamp-1 mb-2"
66+
transition:name={getTransitionNameFromElementId({
67+
elementId: TRANSITION_ELEMENT_IDS.POST_CARD.DESCRIPTION,
68+
})}
69+
>
70+
{description}
71+
</p>
72+
)
73+
}
74+
<span
75+
class="inline-flex items-center gap-1 text-sm text-captions text-nowrap"
76+
transition:name={getTransitionNameFromElementId({
77+
elementId: TRANSITION_ELEMENT_IDS.POST_CARD.META,
78+
})}
79+
>
5380
<Icon name={isUpdatedDate ? 'mdi:edit-outline' : 'mdi:access-time'} class="w-4 h-4" />
5481
<time itemprop="datePublished" datetime={formatDateIso(lastAccessDate)}>
5582
{formatDate(lastAccessDate)}

src/components/ThemeScript.astro

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ import * as themeConstants from '@/constants/themes';
102102
// initial setup
103103
setTheme(getUserPreference());
104104

105+
// fails in firefox
105106
// View Transitions hook to restore theme
106107
document.addEventListener('astro:after-swap', () => setTheme(getUserPreference()));
107108

src/config.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ const configData: ConfigType = {
3232
SITE_URL: process.env.SITE_URL,
3333
SITE_TITLE: 'Nemanja Mitic',
3434
SITE_DESCRIPTION: 'I am Nemanja, full stack developer',
35-
PAGE_SIZE: { POST_CARD: 3, POST_CARD_SMALL: 10 },
35+
PAGE_SIZE_POST_CARD: 3,
36+
PAGE_SIZE_POST_CARD_SMALL: 10,
3637
MORE_POSTS_COUNT: 3,
3738
AUTHOR_NAME: 'Nemanja Mitic',
3839
AUTHOR_EMAIL: '[email protected]',

src/constants/transitions.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
export interface TransitionNameArgs {
2+
elementId: string;
3+
pageSlug: string;
4+
}
5+
6+
export type TransitionElementId = Pick<TransitionNameArgs, 'elementId'>;
7+
export type TransitionPageSlug = Pick<TransitionNameArgs, 'pageSlug'>;
8+
9+
export const getTransitionName = ({ elementId, pageSlug }: TransitionNameArgs): string =>
10+
`${elementId}-${pageSlug}`;
11+
12+
/** apply only slug, returns getTransitionNameFromElementId({elementId}) function */
13+
export const setTransitionSlug =
14+
({ pageSlug }: TransitionPageSlug) =>
15+
({ elementId }: TransitionElementId) =>
16+
getTransitionName({ elementId, pageSlug });
17+
18+
export const TRANSITION_ELEMENT_IDS = {
19+
POST_CARD: {
20+
HERO_IMAGE: 'post-card-hero-image',
21+
TITLE: 'post-card-title',
22+
DESCRIPTION: 'post-card-description',
23+
META: 'post-card-meta',
24+
},
25+
EXPLORE: {
26+
YEAR: 'explore-year',
27+
},
28+
} as const;

src/pages/blog/[...page].astro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import type { GetStaticPathsOptions } from 'astro';
1212
// [page].astro and getStaticPaths because of pagination
1313
export async function getStaticPaths({ paginate }: GetStaticPathsOptions) {
1414
const posts: Post[] = await getAllPostsWithReadingTime();
15-
const pageSize = CONFIG.PAGE_SIZE.POST_CARD; // must take entire config here, interesting
15+
const pageSize = CONFIG.PAGE_SIZE_POST_CARD; // must take entire config here, interesting
1616
1717
const pagination = paginate(posts, { pageSize });
1818
pagination.push({ params: { page: '1' }, props: pagination[0].props });

src/pages/blog/[slug].astro

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import TagList from '@/components/TagList.astro';
1414
import { draftText } from '@/constants/data';
1515
import { IMAGE_SIZES } from '@/constants/image';
1616
import { ROUTES } from '@/constants/routes';
17+
import { setTransitionSlug, TRANSITION_ELEMENT_IDS } from '@/constants/transitions';
1718
import { CONFIG } from '@/config';
1819
import { getOpenGraphImagePath } from '@/libs/api/open-graph/image-path';
1920
@@ -72,6 +73,8 @@ const shareProps = {
7273
url: `${SITE_URL}${ROUTES.BLOG}${slug}`,
7374
};
7475
76+
const getTransitionNameFromElementId = setTransitionSlug({ pageSlug: slug });
77+
7578
// handle all metadata here
7679
const image = getOpenGraphImagePath(pathname);
7780
@@ -91,25 +94,44 @@ const metadata: Metadata = { title, description, image };
9194
loading="eager"
9295
itemprop="image"
9396
class="block max-w-full h-auto aspect-[16/8] object-cover"
94-
transition:name={`hero-image-${slug}`}
97+
transition:name={getTransitionNameFromElementId({
98+
elementId: TRANSITION_ELEMENT_IDS.POST_CARD.HERO_IMAGE,
99+
})}
95100
/>
96101
)
97102
}
98103
</Fragment>
99104

100105
<Fragment slot="hero-text">
101-
<h1 class="b-h1">
106+
<h1
107+
class="b-h1"
108+
transition:name={getTransitionNameFromElementId({
109+
elementId: TRANSITION_ELEMENT_IDS.POST_CARD.TITLE,
110+
})}
111+
>
102112
{title}
103113
{draft && <sup class="text-red-500 ml-1">{draftText}</sup>}
104114
</h1>
105115

106116
{
107117
description && (
108-
<p class="font-normal text-content text-xl md:text-2xl mb-6 md:mb-8">{description}</p>
118+
<p
119+
class="font-normal text-content text-xl md:text-2xl mb-6 md:mb-8"
120+
transition:name={getTransitionNameFromElementId({
121+
elementId: TRANSITION_ELEMENT_IDS.POST_CARD.DESCRIPTION,
122+
})}
123+
>
124+
{description}
125+
</p>
109126
)
110127
}
111128

112-
<PostMeta {...postMetaProps} />
129+
<PostMeta
130+
{...postMetaProps}
131+
transition:name={getTransitionNameFromElementId({
132+
elementId: TRANSITION_ELEMENT_IDS.POST_CARD.META,
133+
})}
134+
/>
113135
</Fragment>
114136

115137
<Fragment slot="content">

src/pages/blog/categories/[category]/[...page].astro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import type { GetStaticPathsOptions } from 'astro';
1616
1717
export async function getStaticPaths({ paginate }: GetStaticPathsOptions) {
1818
const posts: Post[] = await getAllPostsWithReadingTime();
19-
const pageSize = CONFIG.PAGE_SIZE.POST_CARD;
19+
const pageSize = CONFIG.PAGE_SIZE_POST_CARD;
2020
2121
const uniqueCategories = getUniqueCategories(posts);
2222

0 commit comments

Comments
 (0)