# retoor <retoor@molodetz.nl>
from __future__ import annotations
from .base import (
MEDIUM,
LOW,
WARN,
Check,
PageContext,
SiteContext,
ok_check,
page_check,
)
CATEGORY = "content"
THIN_CONTENT_WORDS = 250
@page_check
def single_h1(page: PageContext, site: SiteContext) -> list:
h1s = page.dom.get("h1", []) or []
results = [
ok_check(
"content.h1_present",
CATEGORY,
"H1 heading",
len(h1s) >= 1,
severity=MEDIUM,
value=(h1s[0][:80] if h1s else "missing"),
recommendation="Add a single descriptive H1 heading.",
url=page.url,
)
]
if len(h1s) > 1:
results.append(
Check(
"content.h1_single",
CATEGORY,
"Single H1",
WARN,
LOW,
value=f"{len(h1s)} H1s",
recommendation="Use exactly one H1 per page.",
url=page.url,
)
)
return results
@page_check
def heading_order(page: PageContext, site: SiteContext) -> Check:
headings = page.dom.get("headings", []) or []
levels = [int(h.get("level", 0)) for h in headings if h.get("level")]
skipped = False
prev = 0
for level in levels:
if prev and level > prev + 1:
skipped = True
break
prev = level
return ok_check(
"content.heading_order",
CATEGORY,
"Heading hierarchy",
not skipped,
severity=LOW,
value="ordered" if not skipped else "skips a level",
recommendation="Do not skip heading levels (e.g. H2 to H4).",
warn=True,
url=page.url,
)
@page_check
def word_count(page: PageContext, site: SiteContext) -> Check:
words = int(page.dom.get("wordCount", 0) or 0)
passed = words >= THIN_CONTENT_WORDS
return ok_check(
"content.word_count",
CATEGORY,
"Content depth",
passed,
severity=MEDIUM,
value=f"{words} words",
recommendation=f"Thin content; aim for more than {THIN_CONTENT_WORDS} meaningful words.",
warn=True,
url=page.url,
)