diff --git a/ci/release/changelogs/next.md b/ci/release/changelogs/next.md
index f4616390ed..d49c001ae2 100644
--- a/ci/release/changelogs/next.md
+++ b/ci/release/changelogs/next.md
@@ -19,6 +19,7 @@
- exports: pptx follows standards more closely, addressing warnings from some Powerpoint software [#2645](https://github.com/terrastruct/d2/pull/2645)
- d2sequence: fix edge case of invalid sequence diagrams [#2660](https://github.com/terrastruct/d2/pull/2660)
- d2svg: Text may overflow legend bounds when monospace font is used [#2674](https://github.com/terrastruct/d2/pull/2674)
+- d2svg: animated connections honor reduced motion preferences [#2778](https://github.com/terrastruct/d2/pull/2778)
---
diff --git a/d2renderers/d2sketch/testdata/animated/sketch.exp.svg b/d2renderers/d2sketch/testdata/animated/sketch.exp.svg
index abb47d6069..53346e2747 100644
--- a/d2renderers/d2sketch/testdata/animated/sketch.exp.svg
+++ b/d2renderers/d2sketch/testdata/animated/sketch.exp.svg
@@ -12,6 +12,12 @@
}
}
+@media (prefers-reduced-motion: reduce) {
+ .animated-connection {
+ animation: none !important;
+ }
+}
+
.d2-1719270827 .text-bold {
font-family: "d2-1719270827-font-bold";
}
diff --git a/d2renderers/d2sketch/testdata/animated_dark/sketch.exp.svg b/d2renderers/d2sketch/testdata/animated_dark/sketch.exp.svg
index e31831e5be..99f67ad365 100644
--- a/d2renderers/d2sketch/testdata/animated_dark/sketch.exp.svg
+++ b/d2renderers/d2sketch/testdata/animated_dark/sketch.exp.svg
@@ -12,6 +12,12 @@
}
}
+@media (prefers-reduced-motion: reduce) {
+ .animated-connection {
+ animation: none !important;
+ }
+}
+
.d2-1408075173 .text-bold {
font-family: "d2-1408075173-font-bold";
}
diff --git a/d2renderers/d2svg/d2svg.go b/d2renderers/d2svg/d2svg.go
index 4001a853bd..a2197980f8 100644
--- a/d2renderers/d2svg/d2svg.go
+++ b/d2renderers/d2svg/d2svg.go
@@ -2519,6 +2519,12 @@ func EmbedFonts(buf *bytes.Buffer, diagramHash, source string, fontFamily *d2fon
stroke-dashoffset: 0;
}
}
+
+@media (prefers-reduced-motion: reduce) {
+ .animated-connection {
+ animation: none !important;
+ }
+}
`,
)
diff --git a/d2renderers/d2svg/d2svg_test.go b/d2renderers/d2svg/d2svg_test.go
index 2d99dfe321..6de5a3d631 100644
--- a/d2renderers/d2svg/d2svg_test.go
+++ b/d2renderers/d2svg/d2svg_test.go
@@ -1,11 +1,35 @@
package d2svg
import (
+ "bytes"
+ "strings"
"testing"
+ "oss.terrastruct.com/d2/d2renderers/d2fonts"
"oss.terrastruct.com/d2/d2target"
)
+func TestEmbedFontsReducedMotion(t *testing.T) {
+ var buf bytes.Buffer
+ fontFamily := d2fonts.SourceSansPro
+ monoFontFamily := d2fonts.SourceCodePro
+ EmbedFonts(&buf, "diagram", ``, &fontFamily, &monoFontFamily, "")
+
+ css := buf.String()
+ if !strings.Contains(css, "@media (prefers-reduced-motion: reduce)") {
+ t.Fatal("animated connections should honor the reduced-motion preference")
+ }
+ if !strings.Contains(css, "animation: none !important;") {
+ t.Fatal("reduced-motion rule should override the inline connection animation")
+ }
+
+ buf.Reset()
+ EmbedFonts(&buf, "diagram", ``, &fontFamily, &monoFontFamily, "")
+ if strings.Contains(buf.String(), "prefers-reduced-motion") {
+ t.Fatal("static diagrams should not include the reduced-motion rule")
+ }
+}
+
func TestSortObjects(t *testing.T) {
allObjects := []DiagramObject{
// same zIndex and level, should keep in this order
diff --git a/d2renderers/d2svg/dark_theme/testdata/animated/dark_theme.exp.svg b/d2renderers/d2svg/dark_theme/testdata/animated/dark_theme.exp.svg
index 88c18100bf..63fe459aa9 100644
--- a/d2renderers/d2svg/dark_theme/testdata/animated/dark_theme.exp.svg
+++ b/d2renderers/d2svg/dark_theme/testdata/animated/dark_theme.exp.svg
@@ -12,6 +12,12 @@
}
}
+@media (prefers-reduced-motion: reduce) {
+ .animated-connection {
+ animation: none !important;
+ }
+}
+
.d2-4155204744 .text-bold {
font-family: "d2-4155204744-font-bold";
}
diff --git a/e2etests/testdata/stable/animated/dagre/sketch.exp.svg b/e2etests/testdata/stable/animated/dagre/sketch.exp.svg
index f801d2fb8c..bef3a661ed 100644
--- a/e2etests/testdata/stable/animated/dagre/sketch.exp.svg
+++ b/e2etests/testdata/stable/animated/dagre/sketch.exp.svg
@@ -5,6 +5,12 @@
}
}
+@media (prefers-reduced-motion: reduce) {
+ .animated-connection {
+ animation: none !important;
+ }
+}
+
.d2-2154070564 .text-bold {
font-family: "d2-2154070564-font-bold";
}
diff --git a/e2etests/testdata/stable/animated/elk/sketch.exp.svg b/e2etests/testdata/stable/animated/elk/sketch.exp.svg
index dcfe9b1067..3a9ccfb1c1 100644
--- a/e2etests/testdata/stable/animated/elk/sketch.exp.svg
+++ b/e2etests/testdata/stable/animated/elk/sketch.exp.svg
@@ -5,6 +5,12 @@
}
}
+@media (prefers-reduced-motion: reduce) {
+ .animated-connection {
+ animation: none !important;
+ }
+}
+
.d2-4012398488 .text-bold {
font-family: "d2-4012398488-font-bold";
}
diff --git a/e2etests/testdata/stable/sql_table_tooltip_animated/dagre/sketch.exp.svg b/e2etests/testdata/stable/sql_table_tooltip_animated/dagre/sketch.exp.svg
index e846117d2f..a02bcd0c6f 100644
--- a/e2etests/testdata/stable/sql_table_tooltip_animated/dagre/sketch.exp.svg
+++ b/e2etests/testdata/stable/sql_table_tooltip_animated/dagre/sketch.exp.svg
@@ -12,6 +12,12 @@
}
}
+@media (prefers-reduced-motion: reduce) {
+ .animated-connection {
+ animation: none !important;
+ }
+}
+
.appendix-icon {
filter: drop-shadow(0px 0px 32px rgba(31, 36, 58, 0.1));
}]]>