diff --git a/.changeset/quiet-rockets-juggle.md b/.changeset/quiet-rockets-juggle.md
new file mode 100644
index 000000000..36d36746c
--- /dev/null
+++ b/.changeset/quiet-rockets-juggle.md
@@ -0,0 +1,5 @@
+---
+'astro': patch
+---
+
+Respect the download attribute in links when using view transitions
diff --git a/packages/astro/components/ViewTransitions.astro b/packages/astro/components/ViewTransitions.astro
index 29fe5dd1f..6cf0a5ccd 100644
--- a/packages/astro/components/ViewTransitions.astro
+++ b/packages/astro/components/ViewTransitions.astro
@@ -300,6 +300,7 @@ const { fallback = 'animate' } = Astro.props as Props;
!link ||
!(link instanceof HTMLAnchorElement) ||
link.dataset.astroReload !== undefined ||
+ link.hasAttribute('download') ||
!link.href ||
(link.target && link.target !== '_self') ||
link.origin !== location.origin ||
diff --git a/packages/astro/e2e/fixtures/view-transitions/public/logo.svg b/packages/astro/e2e/fixtures/view-transitions/public/logo.svg
new file mode 100644
index 000000000..e9c63b295
--- /dev/null
+++ b/packages/astro/e2e/fixtures/view-transitions/public/logo.svg
@@ -0,0 +1,13 @@
+
diff --git a/packages/astro/e2e/fixtures/view-transitions/src/pages/four.astro b/packages/astro/e2e/fixtures/view-transitions/src/pages/four.astro
index 915b9814f..c6547dc20 100644
--- a/packages/astro/e2e/fixtures/view-transitions/src/pages/four.astro
+++ b/packages/astro/e2e/fixtures/view-transitions/src/pages/four.astro
@@ -10,4 +10,5 @@ import Layout from '../components/Layout.astro';
load page / no navigation
+ load page / no navigation
diff --git a/packages/astro/e2e/view-transitions.test.js b/packages/astro/e2e/view-transitions.test.js
index 11ff97e40..944d40343 100644
--- a/packages/astro/e2e/view-transitions.test.js
+++ b/packages/astro/e2e/view-transitions.test.js
@@ -443,47 +443,62 @@ test.describe('View Transitions', () => {
'There should be only 1 page load. No additional loads for going back on same page'
).toEqual(1);
});
-});
-test('Navigation also swaps the attributes of the document root', async ({ page, astro }) => {
- await page.goto(astro.resolveUrl('/some-attributes'));
- let p = page.locator('#heading');
- await expect(p, 'should have content').toHaveText('Page with some attributes');
+ test('Navigation also swaps the attributes of the document root', async ({ page, astro }) => {
+ await page.goto(astro.resolveUrl('/some-attributes'));
+ let p = page.locator('#heading');
+ await expect(p, 'should have content').toHaveText('Page with some attributes');
- let h = page.locator('html');
- await expect(h, 'should have content').toHaveAttribute('lang', 'en');
+ let h = page.locator('html');
+ await expect(h, 'should have content').toHaveAttribute('lang', 'en');
- await page.click('#click-other-attributes');
- p = page.locator('#heading');
- await expect(p, 'should have content').toHaveText('Page with other attributes');
+ await page.click('#click-other-attributes');
+ p = page.locator('#heading');
+ await expect(p, 'should have content').toHaveText('Page with other attributes');
- h = page.locator('html');
- await expect(h, 'should have content').toHaveAttribute('lang', 'es');
- await expect(h, 'should have content').toHaveAttribute('style', 'background-color: green');
- await expect(h, 'should have content').toHaveAttribute('data-other-name', 'value');
- await expect(h, 'should have content').toHaveAttribute('data-astro-fake', 'value');
- await expect(h, 'should have content').toHaveAttribute('data-astro-transition', 'forward');
- await expect(h, 'should be absent').not.toHaveAttribute('class', /.*/);
-});
-
-test('Link with data-astro-reload attribute should trigger page load, no tranistion', async ({
- page,
- astro,
-}) => {
- const loads = [];
- page.addListener('load', (p) => {
- loads.push(p.title());
+ h = page.locator('html');
+ await expect(h, 'should have content').toHaveAttribute('lang', 'es');
+ await expect(h, 'should have content').toHaveAttribute('style', 'background-color: green');
+ await expect(h, 'should have content').toHaveAttribute('data-other-name', 'value');
+ await expect(h, 'should have content').toHaveAttribute('data-astro-fake', 'value');
+ await expect(h, 'should have content').toHaveAttribute('data-astro-transition', 'forward');
+ await expect(h, 'should be absent').not.toHaveAttribute('class', /.*/);
});
- // Go to page 4
- await page.goto(astro.resolveUrl('/four'));
- let p = page.locator('#four');
- await expect(p, 'should have content').toHaveText('Page 4');
+ test('Link with data-astro-reload attribute should trigger page load, no tranistion', async ({
+ page,
+ astro,
+ }) => {
+ const loads = [];
+ page.addListener('load', (p) => {
+ loads.push(p.title());
+ });
- // go to page 2
- await page.click('#click-two');
- p = page.locator('#two');
- await expect(p, 'should have content').toHaveText('Page 2');
+ // Go to page 4
+ await page.goto(astro.resolveUrl('/four'));
+ let p = page.locator('#four');
+ await expect(p, 'should have content').toHaveText('Page 4');
- expect(loads.length, 'There should be 2 page load').toEqual(2);
+ // go to page 2
+ await page.click('#click-two');
+ p = page.locator('#two');
+ await expect(p, 'should have content').toHaveText('Page 2');
+
+ expect(loads.length, 'There should be 2 page load').toEqual(2);
+ });
+
+ test('Link with download attribute should trigger download, no transition', async ({
+ page,
+ astro,
+ }) => {
+ // Go to page 4
+ await page.goto(astro.resolveUrl('/four'));
+ let p = page.locator('#four');
+ await expect(p, 'should have content').toHaveText('Page 4');
+
+ // Start waiting for download before clicking. Note no await.
+ const downloadPromise = page.waitForEvent('download', { timeout: 4000 });
+ await page.click('#click-logo');
+ await downloadPromise;
+ });
});