diff --git a/Tests/LibWeb/Text/expected/SVG/svg-viewBox-attribute.txt b/Tests/LibWeb/Text/expected/SVG/svg-viewBox-attribute.txt
new file mode 100644
index 0000000000..e25d6798aa
--- /dev/null
+++ b/Tests/LibWeb/Text/expected/SVG/svg-viewBox-attribute.txt
@@ -0,0 +1,26 @@
+ [object SVGSVGElement]
+[object SVGAnimatedRect]
+null
+null
+[object DOMRect]
+1
+2
+3
+4
+[object DOMRect]
+1
+2
+3
+4
+[object DOMRect]
+5
+6
+7
+8
+[object DOMRect]
+5
+6
+7
+8
+null
+null
diff --git a/Tests/LibWeb/Text/input/SVG/svg-viewBox-attribute.html b/Tests/LibWeb/Text/input/SVG/svg-viewBox-attribute.html
new file mode 100644
index 0000000000..d5b2424d02
--- /dev/null
+++ b/Tests/LibWeb/Text/input/SVG/svg-viewBox-attribute.html
@@ -0,0 +1,39 @@
+
+
+
diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt
index 2164bb6699..039941e81d 100644
--- a/Userland/Libraries/LibWeb/CMakeLists.txt
+++ b/Userland/Libraries/LibWeb/CMakeLists.txt
@@ -559,6 +559,7 @@ set(SOURCES
SVG/AttributeParser.cpp
SVG/SVGAnimatedLength.cpp
SVG/SVGAnimatedNumber.cpp
+ SVG/SVGAnimatedRect.cpp
SVG/SVGAnimatedString.cpp
SVG/SVGClipPathElement.cpp
SVG/SVGDecodedImageData.cpp
diff --git a/Userland/Libraries/LibWeb/Forward.h b/Userland/Libraries/LibWeb/Forward.h
index 7612fb1862..1ebaf204fd 100644
--- a/Userland/Libraries/LibWeb/Forward.h
+++ b/Userland/Libraries/LibWeb/Forward.h
@@ -622,6 +622,7 @@ struct UnderlyingSource;
namespace Web::SVG {
class SVGAnimatedLength;
+class SVGAnimatedRect;
class SVGCircleElement;
class SVGClipPathElement;
class SVGDefsElement;
diff --git a/Userland/Libraries/LibWeb/SVG/SVGAnimatedRect.cpp b/Userland/Libraries/LibWeb/SVG/SVGAnimatedRect.cpp
new file mode 100644
index 0000000000..1735c0b2a0
--- /dev/null
+++ b/Userland/Libraries/LibWeb/SVG/SVGAnimatedRect.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2024, Andreas Kling
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include
+#include
+#include
+#include
+
+namespace Web::SVG {
+
+JS_DEFINE_ALLOCATOR(SVGAnimatedRect);
+
+SVGAnimatedRect::SVGAnimatedRect(JS::Realm& realm)
+ : Bindings::PlatformObject(realm)
+{
+}
+
+SVGAnimatedRect::~SVGAnimatedRect() = default;
+
+void SVGAnimatedRect::initialize(JS::Realm& realm)
+{
+ Base::initialize(realm);
+ set_prototype(&Bindings::ensure_web_prototype(realm, "SVGAnimatedRect"_fly_string));
+ m_base_val = Geometry::DOMRect::create(realm, { 0, 0, 0, 0 });
+ m_anim_val = Geometry::DOMRect::create(realm, { 0, 0, 0, 0 });
+}
+
+void SVGAnimatedRect::visit_edges(Visitor& visitor)
+{
+ Base::visit_edges(visitor);
+ visitor.visit(m_base_val);
+ visitor.visit(m_anim_val);
+}
+
+JS::GCPtr SVGAnimatedRect::base_val() const
+{
+ if (m_nulled)
+ return nullptr;
+ return m_base_val;
+}
+
+JS::GCPtr SVGAnimatedRect::anim_val() const
+{
+ if (m_nulled)
+ return nullptr;
+ return m_anim_val;
+}
+
+void SVGAnimatedRect::set_nulled(bool nulled)
+{
+ m_nulled = nulled;
+}
+
+void SVGAnimatedRect::set_base_val(Gfx::DoubleRect const& rect)
+{
+ m_base_val->set_x(rect.x());
+ m_base_val->set_y(rect.y());
+ m_base_val->set_width(rect.width());
+ m_base_val->set_height(rect.height());
+}
+
+void SVGAnimatedRect::set_anim_val(Gfx::DoubleRect const& rect)
+{
+ m_anim_val->set_x(rect.x());
+ m_anim_val->set_y(rect.y());
+ m_anim_val->set_width(rect.width());
+ m_anim_val->set_height(rect.height());
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/SVG/SVGAnimatedRect.h b/Userland/Libraries/LibWeb/SVG/SVGAnimatedRect.h
new file mode 100644
index 0000000000..d5d9bbad06
--- /dev/null
+++ b/Userland/Libraries/LibWeb/SVG/SVGAnimatedRect.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2024, Andreas Kling
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include
+#include
+
+namespace Web::SVG {
+
+class SVGAnimatedRect final : public Bindings::PlatformObject {
+ WEB_PLATFORM_OBJECT(SVGAnimatedRect, Bindings::PlatformObject);
+ JS_DECLARE_ALLOCATOR(SVGAnimatedRect);
+
+public:
+ virtual ~SVGAnimatedRect();
+
+ JS::GCPtr base_val() const;
+ JS::GCPtr anim_val() const;
+
+ void set_base_val(Gfx::DoubleRect const&);
+ void set_anim_val(Gfx::DoubleRect const&);
+
+ void set_nulled(bool);
+
+private:
+ virtual void initialize(JS::Realm&) override;
+ virtual void visit_edges(Visitor&) override;
+
+ explicit SVGAnimatedRect(JS::Realm&);
+
+ JS::GCPtr m_base_val;
+ JS::GCPtr m_anim_val;
+
+ bool m_nulled { true };
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/SVG/SVGAnimatedRect.idl b/Userland/Libraries/LibWeb/SVG/SVGAnimatedRect.idl
new file mode 100644
index 0000000000..cc2df28358
--- /dev/null
+++ b/Userland/Libraries/LibWeb/SVG/SVGAnimatedRect.idl
@@ -0,0 +1,11 @@
+#import
+
+// https://svgwg.org/svg2-draft/types.html#InterfaceSVGAnimatedRect
+[Exposed=Window]
+interface SVGAnimatedRect {
+ // NOTE: The spec says that baseVal and animVal are not nullable, but they are nullable in some other engines.
+ [SameObject] readonly attribute DOMRect? baseVal;
+
+ // NOTE: animVal is a DOMRectReadOnly in the spec, but other engines expose a DOMRect (sometimes aliased as SVGRect).
+ [SameObject] readonly attribute DOMRect? animVal;
+};
diff --git a/Userland/Libraries/LibWeb/SVG/SVGFitToViewBox.idl b/Userland/Libraries/LibWeb/SVG/SVGFitToViewBox.idl
new file mode 100644
index 0000000000..c92b537d4a
--- /dev/null
+++ b/Userland/Libraries/LibWeb/SVG/SVGFitToViewBox.idl
@@ -0,0 +1,7 @@
+#import