diff --git a/Cargo.lock b/Cargo.lock
index dccf1bd5a8c3..9d8e77e134b2 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -184,6 +184,7 @@ dependencies = [
"biome_graphql_analyze",
"biome_graphql_syntax",
"biome_grit_patterns",
+ "biome_html_analyze",
"biome_html_formatter",
"biome_js_analyze",
"biome_js_formatter",
@@ -749,6 +750,29 @@ dependencies = [
"serde",
]
+[[package]]
+name = "biome_html_analyze"
+version = "0.5.7"
+dependencies = [
+ "biome_analyze",
+ "biome_console",
+ "biome_deserialize",
+ "biome_deserialize_macros",
+ "biome_diagnostics",
+ "biome_html_factory",
+ "biome_html_parser",
+ "biome_html_syntax",
+ "biome_rowan",
+ "biome_string_case",
+ "biome_suppression",
+ "biome_test_utils",
+ "camino",
+ "insta",
+ "schemars",
+ "serde",
+ "tests_macros",
+]
+
[[package]]
name = "biome_html_factory"
version = "0.5.7"
@@ -1496,6 +1520,7 @@ dependencies = [
"biome_grit_parser",
"biome_grit_patterns",
"biome_grit_syntax",
+ "biome_html_analyze",
"biome_html_factory",
"biome_html_formatter",
"biome_html_parser",
@@ -4047,6 +4072,9 @@ dependencies = [
"biome_graphql_analyze",
"biome_graphql_parser",
"biome_graphql_syntax",
+ "biome_html_analyze",
+ "biome_html_parser",
+ "biome_html_syntax",
"biome_js_analyze",
"biome_js_parser",
"biome_js_syntax",
@@ -5752,6 +5780,9 @@ dependencies = [
"biome_graphql_analyze",
"biome_graphql_parser",
"biome_graphql_syntax",
+ "biome_html_analyze",
+ "biome_html_parser",
+ "biome_html_syntax",
"biome_js_analyze",
"biome_js_factory",
"biome_js_formatter",
diff --git a/Cargo.toml b/Cargo.toml
index 1e64773aee33..e8d8df4e265a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -128,6 +128,7 @@ biome_grit_formatter = { version = "0.0.0", path = "./crates/biome_grit_
biome_grit_parser = { version = "0.1.0", path = "./crates/biome_grit_parser" }
biome_grit_patterns = { version = "0.0.1", path = "./crates/biome_grit_patterns" }
biome_grit_syntax = { version = "0.5.7", path = "./crates/biome_grit_syntax" }
+biome_html_analyze = { version = "0.5.7", path = "./crates/biome_html_analyze" }
biome_html_factory = { version = "0.5.7", path = "./crates/biome_html_factory" }
biome_html_formatter = { version = "0.0.0", path = "./crates/biome_html_formatter" }
biome_html_parser = { version = "0.0.1", path = "./crates/biome_html_parser" }
diff --git a/crates/biome_cli/Cargo.toml b/crates/biome_cli/Cargo.toml
index dfa50d2aa018..32abc9ac33a1 100644
--- a/crates/biome_cli/Cargo.toml
+++ b/crates/biome_cli/Cargo.toml
@@ -34,6 +34,7 @@ biome_glob = { workspace = true }
biome_graphql_analyze = { workspace = true }
biome_graphql_syntax = { workspace = true }
biome_grit_patterns = { workspace = true }
+biome_html_analyze = { workspace = true }
biome_html_formatter = { workspace = true }
biome_js_analyze = { workspace = true }
biome_js_formatter = { workspace = true }
diff --git a/crates/biome_cli/tests/cases/html.rs b/crates/biome_cli/tests/cases/html.rs
index 7b1d98b38f6f..2dd7cddb5ddc 100644
--- a/crates/biome_cli/tests/cases/html.rs
+++ b/crates/biome_cli/tests/cases/html.rs
@@ -331,3 +331,45 @@ fn should_apply_fixes_to_embedded_languages() {
result,
));
}
+
+#[test]
+fn should_lint_a_html_file() {
+ let fs = MemoryFileSystem::default();
+ let mut console = BufferConsole::default();
+
+ let html_file = Utf8Path::new("file.html");
+ fs.insert(
+ html_file.into(),
+ r#"
+"#
+ .as_bytes(),
+ );
+
+ fs.insert(
+ Utf8Path::new("biome.json").into(),
+ r#"{
+ "html": {
+ "linter": {
+ "enabled": true
+ }
+ }
+}"#
+ .as_bytes(),
+ );
+
+ let (fs, result) = run_cli(
+ fs,
+ &mut console,
+ Args::from(["lint", html_file.as_str()].as_slice()),
+ );
+
+ assert!(result.is_err(), "run_cli returned {result:?}");
+
+ assert_cli_snapshot(SnapshotPayload::new(
+ module_path!(),
+ "should_lint_a_html_file",
+ fs,
+ console,
+ result,
+ ));
+}
diff --git a/crates/biome_cli/tests/snapshots/main_cases_html/should_lint_a_html_file.snap b/crates/biome_cli/tests/snapshots/main_cases_html/should_lint_a_html_file.snap
new file mode 100644
index 000000000000..5506bda346f4
--- /dev/null
+++ b/crates/biome_cli/tests/snapshots/main_cases_html/should_lint_a_html_file.snap
@@ -0,0 +1,63 @@
+---
+source: crates/biome_cli/tests/snap_test.rs
+expression: redactor(content)
+---
+## `biome.json`
+
+```json
+{
+ "html": {
+ "linter": {
+ "enabled": true
+ }
+ }
+}
+```
+
+## `file.html`
+
+```html
+
+
+```
+
+# Termination Message
+
+```block
+lint ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ × Some errors were emitted while running checks.
+
+
+
+```
+
+# Emitted Messages
+
+```block
+file.html:1:6 lint/a11y/noHeaderScope FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ × Avoid using the scope attribute on elements other than th elements.
+
+ > 1 │
+ │ ^^^^^^^^^^^
+ 2 │
+
+ i The scope attribute is used to associate a data cell with its corresponding header cell in a data table,
+ so it should be placed on th elements to provide accessibility to screen readers.
+
+ i Follow the links for more information,
+ WCAG 1.3.1
+ WCAG 4.1.1
+
+ i Unsafe fix: Remove the scope attribute.
+
+ 1 │
+ │ -----------
+
+```
+
+```block
+Checked 1 file in