diff --git a/.architecture-rules/frontend.yaml b/.architecture-rules/frontend.yaml
index ab912b25..3d26158d 100644
--- a/.architecture-rules/frontend.yaml
+++ b/.architecture-rules/frontend.yaml
@@ -561,6 +561,39 @@ template_rules:
exceptions:
- "shared/macros/pagination.html"
+ - id: "TPL-015"
+ name: "Use correct page_header macro API"
+ severity: "error"
+ description: |
+ The page_header macro does not accept a buttons=[] parameter.
+ Use action_label/action_onclick for a single action button,
+ or use page_header_flex with {% call %} for multiple buttons.
+
+ OLD (deprecated - will break):
+ {{ page_header('Title', buttons=[
+ {'text': 'Add', 'icon': 'plus', 'click': 'doSomething()'}
+ ]) }}
+
+ NEW (single action button):
+ {{ page_header('Title', action_label='Add', action_icon='plus', action_onclick='doSomething()') }}
+
+ NEW (multiple buttons - use page_header_flex):
+ {% call page_header_flex(title='Title') %}
+
+
+ {% endcall %}
+
+ Parameters for page_header:
+ - title, subtitle
+ - action_label, action_url, action_icon, action_onclick
+ - back_url, back_label
+ pattern:
+ file_pattern: "app/templates/**/*.html"
+ anti_patterns:
+ - "page_header\\s*\\([^)]*buttons\\s*="
+ exceptions:
+ - "shared/macros/headers.html"
+
- id: "TPL-014"
name: "Use new modal_simple macro API with call block"
severity: "error"
diff --git a/scripts/validate_architecture.py b/scripts/validate_architecture.py
index 92a34c4d..3bf43e62 100755
--- a/scripts/validate_architecture.py
+++ b/scripts/validate_architecture.py
@@ -1306,6 +1306,38 @@ class ArchitectureValidator:
)
break # Only report once per line
+ def _check_page_header_macro_api(
+ self, file_path: Path, content: str, lines: list[str]
+ ):
+ """
+ TPL-015: Check for old page_header macro API.
+
+ The page_header macro does not accept a buttons=[] parameter.
+ Use action_label/action_onclick for single button, or page_header_flex for multiple.
+
+ OLD (deprecated): {{ page_header('Title', buttons=[...]) }}
+ NEW (correct): {{ page_header('Title', action_label='Add', action_onclick='...') }}
+ """
+ # Skip the macro definition file
+ if "shared/macros/headers.html" in str(file_path):
+ return
+
+ if "noqa: tpl-015" in content.lower():
+ return
+
+ for i, line in enumerate(lines, 1):
+ if re.search(r"page_header\s*\([^)]*buttons\s*=", line):
+ self._add_violation(
+ rule_id="TPL-015",
+ rule_name="Use correct page_header macro API",
+ severity=Severity.ERROR,
+ file_path=file_path,
+ line_number=i,
+ message="page_header does not accept 'buttons' parameter",
+ context=line.strip()[:80],
+ suggestion="Use action_label/action_onclick, or page_header_flex with {% call %}",
+ )
+
def _check_alpine_template_vars(
self, file_path: Path, content: str, lines: list[str], js_content: str
):
@@ -3286,6 +3318,9 @@ class ArchitectureValidator:
# TPL-014: Check for old modal_simple macro API
self._check_modal_simple_macro_api(file_path, content, lines)
+ # TPL-015: Check for old page_header macro API
+ self._check_page_header_macro_api(file_path, content, lines)
+
# Skip base/partials for TPL-001 check
if is_base_or_partial:
continue