There isn't anything OOTB that would do this, even with Rendering Variants. The Accordion is probably the closest thing to that.
I think the easiest way to achieve what you want would be to clone the Accordion component and provide your own razor view for it. If you look at the existing razor for the Accordion (Just focusing on the part that builds the accordion section):
<div>
<ul class="items">
@foreach (var composite in Model.CompositeItems)
{
<li class="item">
@using (Html.Sxa().BeginEditFrame(composite.Value.ID.ToString(), "Accordion", Model.IsControlEditable))
{
var headerKey = string.Format("{0}-{1}-{2}", headerBaseKey, composite.Key, Model.Id);
<div class="toggle-header">
<div class="label">
@Html.Sitecore().Placeholder(headerKey)
</div>
</div>
var contentKey = string.Format("{0}-{1}-{2}", contentBaseKey, composite.Key, Model.Id);
<div class="toggle-content">
@Html.Sitecore().Placeholder(contentKey)
</div>
}
</li>
}
</ul>
</div>
Just a very quick alteration to that, adding a second loop would put the accordion content where you need it to be. Then the rendering variant of the content would take care of the H1
for the heading:
<div>
<ul class="items">
@foreach (var composite in Model.CompositeItems)
{
<li class="item">
@using (Html.Sxa().BeginEditFrame(composite.Value.ID.ToString(), "Accordion", Model.IsControlEditable))
{
var headerKey = string.Format("{0}-{1}-{2}", headerBaseKey, composite.Key, Model.Id);
<div class="toggle-header">
<div class="label">
@Html.Sitecore().Placeholder(headerKey)
</div>
</div>
}
</li>
}
</ul>
@foreach (var composite in Model.CompositeItems)
{
<div class="item">
@using (Html.Sxa().BeginEditFrame(composite.Value.ID.ToString(), "Accordion", Model.IsControlEditable))
{
var contentKey = string.Format("{0}-{1}-{2}", contentBaseKey, composite.Key, Model.Id);
<div class="toggle-content">
@Html.Sitecore().Placeholder(contentKey)
</div>
}
</div>
}
</div>
Its not exactly the same markup and uses the Accordion JavaScript to toggle the content vs the anchor links with Id's, but it should give you the same result.
Caveat: The above code is untested, but gives you a starting point :)