diff --git a/Tests/LibWeb/Layout/expected/css-appearance-none.txt b/Tests/LibWeb/Layout/expected/css-appearance-none.txt
new file mode 100644
index 00000000000..3281c072510
--- /dev/null
+++ b/Tests/LibWeb/Layout/expected/css-appearance-none.txt
@@ -0,0 +1,77 @@
+Viewport <#document> at (0,0) content-size 800x600 children: not-inline
+ BlockContainer at (0,0) content-size 800x126 [BFC] children: not-inline
+ BlockContainer
at (8,8) content-size 784x110 children: inline
+ frag 0 from BlockContainer start: 0, length: 0, rect: [13,10 0x17] baseline: 15.296875
+ frag 1 from TextNode start: 0, length: 13, rect: [18,10 116.171875x17] baseline: 13.296875
+ "Normal button"
+ frag 2 from BlockContainer start: 0, length: 0, rect: [13,31 0x17] baseline: 15.296875
+ frag 3 from TextNode start: 0, length: 20, rect: [18,31 181.203125x17] baseline: 13.296875
+ "No appearance button"
+ frag 4 from CheckBox start: 0, length: 0, rect: [8,50 13x13] baseline: 13
+ frag 5 from TextNode start: 0, length: 15, rect: [21,50 136.296875x17] baseline: 13.296875
+ "Normal checkbox"
+ frag 6 from BlockContainer start: 0, length: 0, rect: [8,80 0x0] baseline: 0
+ frag 7 from TextNode start: 0, length: 22, rect: [8,67 201.328125x17] baseline: 13.296875
+ "No appearance checkbox"
+ frag 8 from RadioButton start: 0, length: 0, rect: [8,85 12x12] baseline: 12
+ frag 9 from TextNode start: 0, length: 12, rect: [20,84 104.203125x17] baseline: 13.296875
+ "Normal radio"
+ frag 10 from BlockContainer start: 0, length: 0, rect: [8,114 0x0] baseline: 0
+ frag 11 from TextNode start: 0, length: 19, rect: [8,101 169.234375x17] baseline: 13.296875
+ "No appearance radio"
+ BlockContainer at (13,10) content-size 0x17 inline-block [BFC] children: not-inline
+ BlockContainer <(anonymous)> at (13,10) content-size 0x17 flex-container(column) [FFC] children: not-inline
+ BlockContainer <(anonymous)> at (13,10) content-size 0x17 flex-item [BFC] children: inline
+ frag 0 from BlockContainer start: 0, length: 0, rect: [13,23 0x0] baseline: 0
+ BlockContainer at (13,23) content-size 0x0 inline-block [BFC] children: inline
+ TextNode <#text>
+ TextNode <#text>
+ BreakNode
+ TextNode <#text>
+ BlockContainer at (13,31) content-size 0x17 inline-block [BFC] children: not-inline
+ BlockContainer <(anonymous)> at (13,31) content-size 0x17 flex-container(column) [FFC] children: not-inline
+ BlockContainer <(anonymous)> at (13,31) content-size 0x17 flex-item [BFC] children: inline
+ frag 0 from BlockContainer start: 0, length: 0, rect: [13,44 0x0] baseline: 0
+ BlockContainer at (13,44) content-size 0x0 inline-block [BFC] children: inline
+ TextNode <#text>
+ TextNode <#text>
+ BreakNode
+ TextNode <#text>
+ CheckBox at (8,50) content-size 13x13 inline-block children: not-inline
+ TextNode <#text>
+ BreakNode
+ TextNode <#text>
+ BlockContainer at (8,80) content-size 0x0 inline-block [BFC] children: not-inline
+ TextNode <#text>
+ BreakNode
+ TextNode <#text>
+ RadioButton at (8,85) content-size 12x12 inline-block children: not-inline
+ TextNode <#text>
+ BreakNode
+ TextNode <#text>
+ BlockContainer at (8,114) content-size 0x0 inline-block [BFC] children: not-inline
+ TextNode <#text>
+ BreakNode
+ TextNode <#text>
+
+ViewportPaintable (Viewport<#document>) [0,0 800x600]
+ PaintableWithLines (BlockContainer) [0,0 800x126]
+ PaintableWithLines (BlockContainer) [8,8 784x110]
+ PaintableWithLines (BlockContainer) [8,8 10x21]
+ PaintableWithLines (BlockContainer(anonymous)) [13,10 0x17]
+ PaintableWithLines (BlockContainer(anonymous)) [13,10 0x17]
+ PaintableWithLines (BlockContainer) [13,23 0x0]
+ TextPaintable (TextNode<#text>)
+ PaintableWithLines (BlockContainer) [8,29 10x21]
+ PaintableWithLines (BlockContainer(anonymous)) [13,31 0x17]
+ PaintableWithLines (BlockContainer(anonymous)) [13,31 0x17]
+ PaintableWithLines (BlockContainer) [13,44 0x0]
+ TextPaintable (TextNode<#text>)
+ CheckBoxPaintable (CheckBox) [8,50 13x13]
+ TextPaintable (TextNode<#text>)
+ PaintableWithLines (BlockContainer) [8,80 0x0]
+ TextPaintable (TextNode<#text>)
+ RadioButtonPaintable (RadioButton) [8,85 12x12]
+ TextPaintable (TextNode<#text>)
+ PaintableWithLines (BlockContainer) [8,114 0x0]
+ TextPaintable (TextNode<#text>)
diff --git a/Tests/LibWeb/Layout/input/css-appearance-none.html b/Tests/LibWeb/Layout/input/css-appearance-none.html
new file mode 100644
index 00000000000..5cd87d66f2b
--- /dev/null
+++ b/Tests/LibWeb/Layout/input/css-appearance-none.html
@@ -0,0 +1,7 @@
+
+Normal button
+No appearance button
+Normal checkbox
+No appearance checkbox
+Normal radio
+No appearance radio
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp
index d73b0598443..807e584f01d 100644
--- a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp
+++ b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp
@@ -103,12 +103,23 @@ JS::GCPtr HTMLInputElement::create_layout_node(NonnullRefPtr(document(), *this, move(style), *this);
+ }
+
+ // https://drafts.csswg.org/css-ui/#appearance-switching
+ // This specification introduces the appearance property to provide some control over this behavior.
+ // In particular, using appearance: none allows authors to suppress the native appearance of widgets,
+ // giving them a primitive appearance where CSS can be used to restyle them.
+ if (style->appearance() == CSS::Appearance::None) {
+ return Element::create_layout_node_for_display_type(document(), style->display(), style, this);
+ }
+
if (type_state() == TypeAttributeState::SubmitButton || type_state() == TypeAttributeState::Button || type_state() == TypeAttributeState::ResetButton)
return heap().allocate_without_realm(document(), this, move(style));
- if (type_state() == TypeAttributeState::ImageButton)
- return heap().allocate_without_realm(document(), *this, move(style), *this);
-
if (type_state() == TypeAttributeState::Checkbox)
return heap().allocate_without_realm(document(), *this, move(style));