diff --git a/lib/MetaCPAN/Web/Test/HTML5/Element.pm b/lib/MetaCPAN/Web/Test/HTML5/Element.pm
new file mode 100644
index 0000000000..fa64c8d0d7
--- /dev/null
+++ b/lib/MetaCPAN/Web/Test/HTML5/Element.pm
@@ -0,0 +1,26 @@
+package MetaCPAN::Web::Test::HTML5::Element;
+use strict;
+use warnings;
+
+use parent 'HTML::Element';
+
+use MetaCPAN::Web::Test::HTML5::Element::SVG;
+use constant SVG_CLASS => 'MetaCPAN::Web::Test::HTML5::Element::SVG';
+
+sub insert_element {
+    my ($self, $tag, @more) = @_;
+    local $self->{_element_class} = SVG_CLASS
+        if $tag eq 'svg';
+    $self->SUPER::insert_element($tag, @more);
+}
+
+sub _valid_name {
+    my ( $self, $attr ) = @_;
+    # HTML::Element doesn't allow single character attributes, so fake them
+    # being longer
+    $attr .= 'a'
+        if length($attr) == 1
+    $self->SUPER::_valid_name($attr);
+}
+
+1;
diff --git a/lib/MetaCPAN/Web/Test/HTML5/Element/SVG.pm b/lib/MetaCPAN/Web/Test/HTML5/Element/SVG.pm
index 55cafbacba..7021a22ed1 100644
--- a/lib/MetaCPAN/Web/Test/HTML5/Element/SVG.pm
+++ b/lib/MetaCPAN/Web/Test/HTML5/Element/SVG.pm
@@ -2,16 +2,22 @@ package MetaCPAN::Web::Test::HTML5::Element::SVG;
 use strict;
 use warnings;
 
-use parent 'HTML::Element';
+use parent 'MetaCPAN::Web::Test::HTML5::Element';
 
 sub starttag_XML {
     my $self   = shift;
-    my $is_svg = $self->{_tag} eq 'svg';
-    local $self->{xmlns} = "http://www.w3.org/2000/svg" if $is_svg;
-    local $self->{'xmlns:xlink'} = "http://www.w3.org/1999/xlink"
-        if $is_svg;
+
+    local $self->{xmlns} = "http://www.w3.org/2000/svg"
+        if $self->{_tag} eq 'svg';
+    my @ns = grep /^xmlns:/, keys %$self;
+    local @{$self}{@ns};
+    delete @{$self}{@ns};
 
     return $self->SUPER::starttag_XML(@_);
 }
 
+sub element_class {
+    $_[0]->{_element_class} || __PACKAGE__;
+}
+
 1;
diff --git a/lib/MetaCPAN/Web/Test/HTML5/TreeBuilder.pm b/lib/MetaCPAN/Web/Test/HTML5/TreeBuilder.pm
index ad643382cf..b61afb3bd4 100644
--- a/lib/MetaCPAN/Web/Test/HTML5/TreeBuilder.pm
+++ b/lib/MetaCPAN/Web/Test/HTML5/TreeBuilder.pm
@@ -7,6 +7,15 @@ use parent 'HTML::TreeBuilder';
 use MetaCPAN::Web::Test::HTML5::Element::SVG;
 use constant SVG_CLASS => 'MetaCPAN::Web::Test::HTML5::Element::SVG';
 
+use MetaCPAN::Web::Test::HTML5::Element::MathML;
+use constant MATHML_CLASS => 'MetaCPAN::Web::Test::HTML5::Element::MathML';
+
+my @html5_elements = qw(
+    article audio aside bdi datalist canvas details dialog embed figcaption
+    figure footer header main mark menuitem meter nav output progress rp rt
+    ruby section source summary svg time track video wbr
+);
+
 my @svg_elements = qw(
     a altGlyph altGlyphDef altGlyphItem animate animateColor animateMotion
     animateTransform audio canvas circle clipPath color-profile cursor defs desc
@@ -22,64 +31,58 @@ my @svg_elements = qw(
     switch symbol text textPath title tref tspan unknown use video view vkern
 );
 
+my @mathml_elements = qw(
+    annotation-xml annotation maction maligngroup malignmark math menclose
+    merror mfenced mfrac mglyph mi mlabeledtr mlongdiv mmultiscripts mn mo
+    mover mpadded mphantom mroot mrow ms mscarries mscarry msgroup msline
+    mspace msqrt msrow mstack mstyle msub msubsup msup mtable mtd mtext mtr
+    munder munderover semantics
+);
+
 sub start {
     my $self = shift;
     my $e;
-    my $pos = $self->{'_pos'} || $self;
-    if ( !$pos->isa(SVG_CLASS) ) {
-        local $HTML::TreeBuilder::isBodyElement{article}    = 1;
-        local $HTML::TreeBuilder::isBodyElement{audio}      = 1;
-        local $HTML::TreeBuilder::isBodyElement{aside}      = 1;
-        local $HTML::TreeBuilder::isBodyElement{bdi}        = 1;
-        local $HTML::TreeBuilder::isBodyElement{datalist}   = 1;
-        local $HTML::TreeBuilder::isBodyElement{canvas}     = 1;
-        local $HTML::TreeBuilder::isBodyElement{details}    = 1;
-        local $HTML::TreeBuilder::isBodyElement{dialog}     = 1;
-        local $HTML::TreeBuilder::isBodyElement{embed}      = 1;
-        local $HTML::TreeBuilder::isBodyElement{figcaption} = 1;
-        local $HTML::TreeBuilder::isBodyElement{figure}     = 1;
-        local $HTML::TreeBuilder::isBodyElement{footer}     = 1;
-        local $HTML::TreeBuilder::isBodyElement{header}     = 1;
-        local $HTML::TreeBuilder::isBodyElement{main}       = 1;
-        local $HTML::TreeBuilder::isBodyElement{mark}       = 1;
-        local $HTML::TreeBuilder::isBodyElement{menuitem}   = 1;
-        local $HTML::TreeBuilder::isBodyElement{meter}      = 1;
-        local $HTML::TreeBuilder::isBodyElement{nav}        = 1;
-        local $HTML::TreeBuilder::isBodyElement{output}     = 1;
-        local $HTML::TreeBuilder::isBodyElement{progress}   = 1;
-        local $HTML::TreeBuilder::isBodyElement{rp}         = 1;
-        local $HTML::TreeBuilder::isBodyElement{rt}         = 1;
-        local $HTML::TreeBuilder::isBodyElement{ruby}       = 1;
-        local $HTML::TreeBuilder::isBodyElement{section}    = 1;
-        local $HTML::TreeBuilder::isBodyElement{source}     = 1;
-        local $HTML::TreeBuilder::isBodyElement{summary}    = 1;
-        local $HTML::TreeBuilder::isBodyElement{svg}        = 1;
-        local $HTML::TreeBuilder::isBodyElement{time}       = 1;
-        local $HTML::TreeBuilder::isBodyElement{track}      = 1;
-        local $HTML::TreeBuilder::isBodyElement{video}      = 1;
-        local $HTML::TreeBuilder::isBodyElement{wbr}        = 1;
-        $e = $self->SUPER::start(@_);
+    my $pos = $self->pos;
+
+    my $type = 'html';
 
-        if ( $e->tag eq 'svg' ) {
-            bless $e, SVG_CLASS;
+    if ( $pos->isa(SVG_CLASS) ) {
+        $type = 'svg';
+    }
+    elsif ( $pos->isa(MATHML_CLASS) ) {
+        $type = 'mathml';
+        if ( $pos->tag eq 'annotation-xml') {
+            if (my $encoding = $pos->attr('encoding')) {
+                if ($encoding eq 'SVG1.1' || $encoding eq 'image/svg+xml') {
+                    $type = 'svg';
+                }
+                elsif ($encoding eq 'text/html') {
+                    $type = 'html';
+                }
+            }
         }
     }
-    else {
-        local %HTML::TreeBuilder::isHeadElement       = ();
-        local %HTML::TreeBuilder::isHeadOrBodyElement = ();
-        local %HTML::TreeBuilder::isBodyElement       = map +( $_ => 1 ),
-            @svg_elements;
-        $e = $self->SUPER::start(@_);
-        bless $e, SVG_CLASS;
+
+    local %HTML::TreeBuilder::isHeadElement
+        = %HTML::TreeBuilder::isHeadElement;
+    local %HTML::TreeBuilder::isHeadOrBodyElement
+        = %HTML::TreeBuilder::isHeadOrBodyElement;
+    local %HTML::TreeBuilder::isBodyElement
+        = %HTML::TreeBuilder::isBodyElement,
+            map +( $_ => 1 ), @html_elements;
+
+    if ($type eq 'svg') {
+        %HTML::TreeBuilder::isHeadElement       = ();
+        %HTML::TreeBuilder::isHeadOrBodyElement = ();
+        %HTML::TreeBuilder::isBodyElement       = map +( $_ => 1 ), @svg_elements;
+    }
+    elsif ($type eq 'mathml') {
+        %HTML::TreeBuilder::isHeadElement       = ();
+        %HTML::TreeBuilder::isHeadOrBodyElement = ();
+        %HTML::TreeBuilder::isBodyElement       = map +( $_ => 1 ), @mathml_elements;
     }
-    return $e;
-}
 
-sub _valid_name {
-    my ( $self, $attr ) = @_;
-    $attr =~ s/^xlink://;
-    return 1 if $attr =~ /\A[krxyz]\z/;
-    $self->SUPER::_valid_name($attr);
+    return $self->SUPER::start(@_);
 }
 
 1;
