Skip to content
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

Initial View Transition Support #7511

Merged
merged 57 commits into from
Jul 19, 2023
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
fbb925d
Basic support
matthewp Jun 28, 2023
429be8e
Add the fade transition
matthewp Jun 28, 2023
aa32aaa
Move CSS into a separate file
matthewp Jun 28, 2023
cec93b8
Add transition name
matthewp Jun 28, 2023
6f0cf0b
View Transitions changeset
matthewp Jun 28, 2023
848db48
Replace the boolean transition with 'morph'
matthewp Jun 28, 2023
7d6966c
Update to use `transition:animate`
matthewp Jun 29, 2023
684dce6
Use head propagation
matthewp Jun 30, 2023
8b9764f
Move CSS into a separate file
matthewp Jun 28, 2023
862efda
Add builtin animations and namespaced module
matthewp Jun 30, 2023
3e1d465
Misquote
matthewp Jul 6, 2023
203298e
Remove unused code
matthewp Jul 5, 2023
375726d
Add automatic prefetching to the View Transitions router
matthewp Jul 6, 2023
dfda9c3
Use a data attribute for back nav animations
matthewp Jul 6, 2023
5253ff0
Use [data-astro-transition]
matthewp Jul 6, 2023
8f69981
Add view transitions to examples
matthewp Jul 6, 2023
692083f
Wait on the HTML response before calling startViewTransition
matthewp Jul 7, 2023
6e9be38
Updated stuff
matthewp Jul 11, 2023
c9690ec
Update the compiler
matthewp Jul 11, 2023
370f54a
Fix
matthewp Jul 11, 2023
dbf9398
Fallback support
matthewp Jul 12, 2023
4f6401b
Properly do fallback
matthewp Jul 12, 2023
90ca262
Simplify the selectors
matthewp Jul 13, 2023
1649231
Put viewTransitions support behind a flag
matthewp Jul 13, 2023
454f414
Upgrade the compiler
matthewp Jul 13, 2023
4f78b3c
Remove unused import
matthewp Jul 13, 2023
190695d
Add tests
matthewp Jul 13, 2023
965a56e
Use an explicit import instead of types
matthewp Jul 13, 2023
3df49c0
Fix case where the click comes from within nested content
matthewp Jul 13, 2023
8d4d2a8
Fix linting
matthewp Jul 13, 2023
11cc735
Merge branch 'main' into view-transitions
matthewp Jul 13, 2023
8658ec6
Add a test for the back button
matthewp Jul 13, 2023
7e38b0c
Merge branch 'main' into view-transitions
matthewp Jul 14, 2023
6645b6a
Prevent glitch in fallback
matthewp Jul 17, 2023
cf63e4e
Do not combine selectors
matthewp Jul 17, 2023
034f4c1
Fallback to MPA nav if there is an issue fetching
matthewp Jul 17, 2023
5c606fd
Fallback swap if there are no animations
matthewp Jul 17, 2023
b3c79d4
Update packages/astro/src/@types/astro.ts
matthewp Jul 18, 2023
35abfbb
Update packages/astro/components/ViewTransitions.astro
matthewp Jul 18, 2023
ba8d7f3
Update packages/astro/components/ViewTransitions.astro
matthewp Jul 18, 2023
2b15915
Update the changeset
matthewp Jul 18, 2023
2c9d9dd
PR review changes
matthewp Jul 18, 2023
0067db0
Update more based on review comments.
matthewp Jul 18, 2023
2492264
Update the updateDOM default
matthewp Jul 18, 2023
26525a1
Merge branch 'main' into view-transitions
matthewp Jul 18, 2023
a34e643
Pass in transitions options to the compiler
matthewp Jul 19, 2023
5361a84
Update broken tests
matthewp Jul 19, 2023
ddbeb56
Update .changeset/silly-garlics-live.md
matthewp Jul 19, 2023
3757f6b
Update .changeset/silly-garlics-live.md
matthewp Jul 19, 2023
bbf7d94
Update .changeset/silly-garlics-live.md
matthewp Jul 19, 2023
78e83b1
Update .changeset/silly-garlics-live.md
matthewp Jul 19, 2023
12d4f70
h2 -> h4
matthewp Jul 19, 2023
d4ad84f
Upgrade to stable compiler
matthewp Jul 19, 2023
b17c5d3
Merge branch 'main' into view-transitions
matthewp Jul 19, 2023
fee0145
Remove exp redirects from sitemap
matthewp Jul 19, 2023
d6e96be
Remove usage from examples
matthewp Jul 19, 2023
ecfadd7
Remove example updates
matthewp Jul 19, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/silly-garlics-live.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': minor
---

View Transitions
matthewp marked this conversation as resolved.
Show resolved Hide resolved
5 changes: 4 additions & 1 deletion examples/basics/src/layouts/Layout.astro
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
---
import { ViewTransitions } from 'astro:transitions';

export interface Props {
title: string;
}
Expand All @@ -15,8 +17,9 @@ const { title } = Astro.props;
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="generator" content={Astro.generator} />
<title>{title}</title>
<ViewTransitions />
</head>
<body>
<body transition:animate="fade">
<slot />
</body>
</html>
Expand Down
2 changes: 2 additions & 0 deletions examples/blog/src/components/BaseHead.astro
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Import the global.css file here so that it is included on
// all pages through the use of the <BaseHead /> component.
import '../styles/global.css';
import { ViewTransitions } from 'astro:transitions';

export interface Props {
title: string;
Expand Down Expand Up @@ -41,3 +42,4 @@ const { title, description, image = '/placeholder-social.jpg' } = Astro.props;
<meta property="twitter:title" content={title} />
<meta property="twitter:description" content={description} />
<meta property="twitter:image" content={new URL(image, Astro.url)} />
<ViewTransitions />
3 changes: 2 additions & 1 deletion examples/blog/src/components/Header.astro
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
---
import { slide } from 'astro:transitions';
import HeaderLink from './HeaderLink.astro';
import { SITE_TITLE } from '../consts';
---

<header>
<header transition:animate="morph">
<h2>
{SITE_TITLE}
</h2>
Expand Down
3 changes: 1 addition & 2 deletions examples/blog/src/layouts/BlogPost.astro
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@ const { title, description, pubDate, updatedDate, heroImage } = Astro.props;
}
</style>
</head>

<body>
<body transition:animate="slide">
<Header />
<main>
<article>
Expand Down
2 changes: 1 addition & 1 deletion examples/blog/src/pages/blog/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const posts = (await getCollection('blog')).sort(
}
</style>
</head>
<body>
<body transition:animate="slide">
<Header />
<main>
<section>
Expand Down
2 changes: 1 addition & 1 deletion examples/blog/src/pages/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { SITE_TITLE, SITE_DESCRIPTION } from '../consts';
<head>
<BaseHead title={SITE_TITLE} description={SITE_DESCRIPTION} />
</head>
<body>
<body transition:animate="slide">
<Header title={SITE_TITLE} />
<main>
<h1>🧑‍🚀 Hello, Astronaut!</h1>
Expand Down
4 changes: 3 additions & 1 deletion examples/hackernews/src/layouts/Layout.astro
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
import { ViewTransitions } from 'astro:transitions';
import Nav from '../components/Nav.astro';
---

Expand All @@ -10,8 +11,9 @@ import Nav from '../components/Nav.astro';
<meta name="generator" content={Astro.generator} />
<title>Astro - Hacker News</title>
<meta name="description" content="Hacker News Clone built with Astro" />
<ViewTransitions />
</head>
<body>
<body transition:animate="fade">
<Nav />
<slot />
<style is:global>
Expand Down
3 changes: 2 additions & 1 deletion examples/portfolio/src/components/MainHead.astro
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
import { ViewTransitions } from 'astro:transitions';
import '../styles/global.css';

interface Props {
Expand All @@ -25,7 +26,7 @@ const {
href="https://fonts.googleapis.com/css2?family=Public+Sans:ital,wght@0,400;0,700;1,400&family=Rubik:wght@500;600&display=swap"
rel="stylesheet"
/>

<ViewTransitions />
<script is:inline>
// This code is inlined in the head to make dark mode instant & blocking.
const getThemePreference = () => {
Expand Down
2 changes: 1 addition & 1 deletion examples/portfolio/src/layouts/BaseLayout.astro
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const { title, description } = Astro.props;
<head>
<MainHead title={title} description={description} />
</head>
<body>
<body transition:animate="morph">
<div class="stack backgrounds">
<Nav />
<slot />
Expand Down
4 changes: 3 additions & 1 deletion examples/with-tailwindcss/src/layouts/main.astro
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
import { ViewTransitions } from 'astro:transitions';
const { content } = Astro.props;
---

Expand All @@ -8,8 +9,9 @@ const { content } = Astro.props;
<meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<title>{content.title}</title>
<ViewTransitions />
</head>
<body>
<body transition:animate="morph">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to remove the view transitions added in the examples before merging? These are used for create-astro templates

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking we wanted to promote the usage of this by having it in the examples. Will ask docs what they think.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docs thinks remove! 😄

<slot />
</body>
</html>
5 changes: 3 additions & 2 deletions examples/with-tailwindcss/src/pages/index.astro
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
// Component Imports
import Button from '../components/Button.astro';
import { ViewTransitions } from 'astro:transitions';

// Full Astro Component Syntax:
// https://docs.astro.build/core-concepts/astro-components/
Expand All @@ -13,9 +14,9 @@ import Button from '../components/Button.astro';
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="generator" content={Astro.generator} />
<title>Astro + TailwindCSS</title>
<ViewTransitions />
</head>

<body>
<body transition:animate="morph">
<div class="grid place-items-center h-screen content-center">
<Button>Tailwind Button in Astro!</Button>
<a href="/markdown-page" class="p-4 underline">Markdown is also supported...</a>
Expand Down
9 changes: 9 additions & 0 deletions packages/astro/client-base.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,15 @@ declare module 'astro:assets' {
export const { getImage, getConfiguredImageService, Image }: AstroAssets;
}

declare module 'astro:transitions' {
type TransitionModule = typeof import('./dist/transitions/index.js');
export const slide: TransitionModule['slide'];
export const fade: TransitionModule['fade'];

type ViewTransitionsModule = typeof import('./components/ViewTransitions.astro');
export const ViewTransitions: ViewTransitionsModule['default'];
}

type MD = import('./dist/@types/astro').MarkdownInstance<Record<string, any>>;
interface ExportedMarkdownModuleEntities {
frontmatter: MD['frontmatter'];
Expand Down
89 changes: 89 additions & 0 deletions packages/astro/components/ViewTransitions.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<script>
type Direction = 'forward' | 'back';

let currentIndex = history.state?.index || 0;
matthewp marked this conversation as resolved.
Show resolved Hide resolved
if(!history.state) {
history.replaceState({index: currentIndex}, document.title);
}

async function getHTML(href: string) {
let res = await fetch(href)
let html = await res.text();
matthewp marked this conversation as resolved.
Show resolved Hide resolved
return html;
}

const parser = new DOMParser();

async function updateDOM(dir: Direction, html: string) {
const doc = parser.parseFromString(html, 'text/html');
doc.documentElement.dataset.astroTransition = dir;
document.documentElement.replaceWith(doc.documentElement);
}

async function navigate(dir: Direction, href: string) {
let htmlPromise = getHTML(href);
let finished: Promise<void>;
document.documentElement.dataset.astroTransition = dir;
const html = await htmlPromise;
if(document.startViewTransition) {
finished = document.startViewTransition(() => updateDOM(dir, html)).finished;
} else {
finished = updateDOM(dir, html);
}
try {
await finished;
} finally {
document.documentElement.removeAttribute('data-astro-transition');
}
}

document.addEventListener('click', (ev) => {
let link = ev.target;
if (link instanceof HTMLAnchorElement &&
link.href &&
(!link.target || link.target === '_self') &&
link.origin === location.origin &&
ev.button === 0 && // left clicks only
!ev.metaKey && // new tab (mac)
!ev.ctrlKey && // new tab (windows)
!ev.altKey && // download
!ev.shiftKey &&
!ev.defaultPrevented
) {
ev.preventDefault();
navigate('forward', link.href);
history.pushState({index: currentIndex + 1}, '', link.href);
currentIndex++;
matthewp marked this conversation as resolved.
Show resolved Hide resolved
}
});
window.addEventListener('popstate', ev => {
const index = history.state?.index ?? (currentIndex + 1);
matthewp marked this conversation as resolved.
Show resolved Hide resolved
const direction: Direction = index > currentIndex ? 'forward' : 'back';
navigate(direction, location.href);
currentIndex = index;
});

// Prefetching
function maybePrefetch(pathname: string) {
if(document.querySelector(`link[rel=prefetch][href="${pathname}"]`)) return;
if(navigator.connection){
let conn = navigator.connection;
if(conn.saveData || /(2|3)g/.test(conn.effectiveType || '')) return;
}
let link = document.createElement('link');
link.setAttribute('rel', 'prefetch');
link.setAttribute('href', pathname);
document.head.append(link);
}

['mouseenter', 'touchstart', 'focus'].forEach(evName => {
document.addEventListener(evName, ev => {
if(ev.target instanceof HTMLAnchorElement) {
let el = ev.target;
if(el.origin === location.origin && el.pathname !== location.pathname) {
maybePrefetch(el.pathname);
}
}
}, { passive: true, capture: true });
});
</script>
1 change: 1 addition & 0 deletions packages/astro/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { default as Code } from './Code.astro';
export { default as Debug } from './Debug.astro';
export { default as ViewTransitions } from './ViewTransitions.astro';
32 changes: 32 additions & 0 deletions packages/astro/components/viewtransitions.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
@keyframes astroFadeInOut {
from {
opacity: 1;
}
to {
opacity: 0;
}
}

@keyframes astroFadeIn {
from { opacity: 0; }
}

@keyframes astroFadeOut {
to { opacity: 0; }
}

@keyframes astroSlideFromRight {
from { transform: translateX(100%); }
}

@keyframes astroSlideFromLeft {
from { transform: translateX(-100%); }
}

@keyframes astroSlideToRight {
to { transform: translateX(100%); }
}

@keyframes astroSlideToLeft {
to { transform: translateX(-100%); }
}
7 changes: 5 additions & 2 deletions packages/astro/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@
"./middleware": {
"types": "./dist/core/middleware/index.d.ts",
"default": "./dist/core/middleware/index.js"
}
},
"./transitions": "./dist/transitions/index.js"
},
"imports": {
"#astro/*": "./dist/*.js"
Expand Down Expand Up @@ -113,7 +114,7 @@
"test:e2e:match": "playwright test -g"
},
"dependencies": {
"@astrojs/compiler": "^1.5.3",
"@astrojs/compiler": "1.6.0-pre.10",
"@astrojs/internal-helpers": "^0.1.1",
"@astrojs/language-server": "^1.0.0",
"@astrojs/markdown-remark": "^2.2.1",
Expand All @@ -126,6 +127,7 @@
"@babel/traverse": "^7.22.5",
"@babel/types": "^7.22.5",
"@types/babel__core": "^7.20.1",
"@types/dom-view-transitions": "^1.0.1",
"@types/yargs-parser": "^21.0.0",
"acorn": "^8.9.0",
"boxen": "^6.2.1",
Expand All @@ -149,6 +151,7 @@
"kleur": "^4.1.4",
"magic-string": "^0.27.0",
"mime": "^3.0.0",
"network-information-types": "^0.1.1",
"ora": "^6.3.1",
"p-limit": "^4.0.0",
"path-to-regexp": "^6.2.1",
Expand Down
23 changes: 23 additions & 0 deletions packages/astro/src/@types/astro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,27 @@ export interface AstroBuiltinProps {
'client:only'?: boolean | string;
}

export interface TransitionAnimation {
name: string; // The name of the keyframe
delay?: number | string;
duration?: number | string;
easing?: string;
fillMode?: string;
direction?: string;
}

export interface TransitionAnimationPair {
old: TransitionAnimation | TransitionAnimation[];
new: TransitionAnimation | TransitionAnimation[];
}

export interface TransitionDirectionalAnimations {
forwards: TransitionAnimationPair;
backwards: TransitionAnimationPair;
}

export type TransitionAnimationValue = 'morph' | 'slide' | 'fade' | TransitionDirectionalAnimations;

// Allow users to extend this for astro-jsx.d.ts
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface AstroClientDirectives {}
Expand All @@ -69,6 +90,8 @@ export interface AstroBuiltinAttributes {
'set:html'?: any;
'set:text'?: any;
'is:raw'?: boolean;
'transition:animate'?: 'morph' | 'slide' | 'fade' | TransitionDirectionalAnimations;
'transition:name'?: string;
}

export interface AstroDefineVarsAttribute {
Expand Down
2 changes: 2 additions & 0 deletions packages/astro/src/core/create-vite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import astroScannerPlugin from '../vite-plugin-scanner/index.js';
import astroScriptsPlugin from '../vite-plugin-scripts/index.js';
import astroScriptsPageSSRPlugin from '../vite-plugin-scripts/page-ssr.js';
import { vitePluginSSRManifest } from '../vite-plugin-ssr-manifest/index.js';
import astroTransitions from '../transitions/vite-plugin-transitions.js';
import { joinPaths } from './path.js';

interface CreateViteOptions {
Expand Down Expand Up @@ -132,6 +133,7 @@ export async function createVite(
astroContentAssetPropagationPlugin({ mode, settings }),
vitePluginSSRManifest(),
settings.config.experimental.assets ? [astroAssetsPlugin({ settings, logging, mode })] : [],
astroTransitions(),
],
publicDir: fileURLToPath(settings.config.publicDir),
root: fileURLToPath(settings.config.root),
Expand Down
Loading