diff --git a/REQUIREMENTS.md b/REQUIREMENTS.md
index e79c2ad..a629e56 100644
--- a/REQUIREMENTS.md
+++ b/REQUIREMENTS.md
@@ -121,7 +121,7 @@ slhx competes with React by making frontend frameworks unnecessary for most apps
## page_swap
### req: page_swap/001
-001 Minimal page swapping is a first-class slhx-axum happy path. Authors mark real anchors with `data-slhx-nav`; links keep valid `href` and work without JS.
+001 Minimal page swapping is a first-class slhx-axum happy path. Authors mark real anchors with `data-slhx-nav`; links keep valid `href` and work without JS. Missing `href` on a static `data-slhx-nav` anchor is a build error.
### req: page_swap/002
002 A `data-slhx-nav` click fetches the target URL as a slhx partial request. The response updates the canonical content slot, optionally navigation and title, then applies a `Navigate` effect.
diff --git a/slhx-build/src/lib.rs b/slhx-build/src/lib.rs
index f180266..4001383 100644
--- a/slhx-build/src/lib.rs
+++ b/slhx-build/src/lib.rs
@@ -1067,7 +1067,7 @@ fn reject_invalid_slhx_attr_values(path: &Path, attrs: &[SurfaceAttribute]) -> i
}
fn reject_invalid_slhx_attr_placement(path: &Path, tag: &str, attrs: &[SurfaceAttribute]) -> io::Result<()> {
- if has_attr(attrs, "data-slhx-nav") && tag != "a" {
+ if has_attr(attrs, "data-slhx-nav") && (tag != "a" || !has_attr(attrs, "href")) {
return Err(invalid_slhx_placement(
path,
"data-slhx-nav",
@@ -1652,6 +1652,12 @@ fn main() {{
"data-slhx-nav",
"",
),
+ (
+ "nav-missing-href",
+ r#"Docs"#,
+ "data-slhx-nav",
+ "",
+ ),
(
"sse-child",
r#""#,