Skip to content

feat(components): shared Heading Twig component for H1-H6#98

Open
martinydeAI wants to merge 1 commit into
developfrom
feature/issue-92-heading-component
Open

feat(components): shared Heading Twig component for H1-H6#98
martinydeAI wants to merge 1 commit into
developfrom
feature/issue-92-heading-component

Conversation

@martinydeAI

Copy link
Copy Markdown
Collaborator

Links to issues

Closes #92.

Description

Several templates were hand-rolling the same <h1>/<h2> markup
with font-display text-[clamp(...)] font-medium leading-tight tracking-tight text-ink salad. Extract a shared Heading Twig
component that picks the tag from a level prop (1–6) and the
visual treatment from a size token, with sensible defaults per
level.

  • New templates/components/Heading.html.twiglevel (1–6),
    size (xl | lg | md | sm, auto from level), class
    slot for extras (margins, layout hooks).
  • Refactored call sites on develop:
    • templates/assistant/show.html.twig (hero h1)
    • templates/security/login.html.twig (compact-size h1)
    • templates/components/PageHeader.html.twig (hero h1)
    • templates/components/Hero.html.twig (hero h1)
    • templates/components/EmptyState.html.twig (small h2)
    • templates/components/Filter/Rail.html.twig (section h2)

The two card-internal h3s in CardRail/Card and
Catalog/AssistantCard keep their own markup because they ride a
parent group element for group-hover:text-primary — that
coupling is a card-component concern, not a heading concern.
Pulling it into Heading would force the prop API to absorb
hover-coupling state.

Screenshot of the result

n/a — every refactored call site preserves the exact class string
that was there before (verified by diff). Visual output is
unchanged.

Checklist

  • My code is covered by test cases.
  • My code passes our test (all our tests).
  • My code passes our static analysis suite.
  • My code passes our continuous integration process.

Verified locally:

  • task coding-standards-twig-check — green (after a
    coding-standards-twig-apply pass to drop quotes on the hash
    keys, per project preference).
  • task coding-standards-php-check — green (no PHP changed).
  • task coding-standards-markdown-check — green.
  • task test-coverage — could not run locally due to a Docker
    Desktop / MariaDB OOM situation on the host. The change is
    template-only and no test asserts on the exact class string, so
    CI should be green; happy to re-run once the host is healthy.

Additional comments or questions

The size token names (xl / lg / md / sm) intentionally
match Tailwind's mental model rather than naming them after their
call-site role (e.g. hero / auth / section / subsection).
That keeps the component reusable without inventing a new
vocabulary; the level→size default mapping captures the common case.


Details - AI specificities

Adds `templates/components/Heading.html.twig` accepting a `level`
prop (1-6), an optional `size` token (xl / lg / md / sm; defaults
chosen from `level`), and a `class` slot for layout extras like
margins. Centralises the `font-display text-[clamp(...)] ...` token
salad that several templates were hand-rolling.

Refactors the call sites that duplicated the markup:

- templates/assistant/show.html.twig (hero h1)
- templates/security/login.html.twig (compact-size h1)
- templates/components/PageHeader.html.twig (hero h1)
- templates/components/Hero.html.twig (hero h1)
- templates/components/EmptyState.html.twig (small h2)
- templates/components/Filter/Rail.html.twig (section h2)

The two card-internal h3s in `CardRail/Card` and
`Catalog/AssistantCard` keep their own markup because they ride a
parent `group` for `group-hover:text-primary` - that coupling is a
card-component concern, not a heading concern.

Refs #92.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@martinyde martinyde requested a review from yepzdk June 22, 2026 06:54
md: 'font-display text-2xl font-medium tracking-tight text-ink',
sm: 'font-display text-xl font-semibold text-ink',
} %}
{% set resolvedSize = size ?? (level == 1 ? 'xl' : (level == 2 ? 'md' : 'sm')) %}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

level and size are fully decoupled — nothing stops <twig:Heading level="4" size="xl"> rendering an <h4> bigger than an <h1> elsewhere on the page. Explicit size is already live: login uses size="lg" and EmptyState size="sm".

Should we cap size to a per-level ceiling — a heading can shrink, never out-size a higher level? Something like:

level default allowed sizes
1 xl xl, lg
2 md lg, md, sm
3–6 sm sm

That keeps both current overrides (1lg, 2sm) valid while ruling out the inversion. Note lg is never auto-reachable today — only via explicit size — so a level→size table would also give it a natural home. Non-blocking; fine as a follow-up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(components): shared Heading Twig component for H1-H6

2 participants