mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-09-25 01:53:03 +02:00
fix(toc): add trailing bottom spacing when the TOC scrolls
The desktop TOC is its own scroll container (overflow-y:auto + max-height), but unlike article content — which gets trailing space via .page-content > article::after — it had nothing below the last link. A long TOC scrolled to the bottom left the final link flush against the edge. Add .toc::after as a block pseudo-element inside the desktop media query, using the same technique (and the same Firefox-clips-padding-bottom reason) as the article. Shared component, so public (about/charte/licence) and admin (#admin-toc) both get it. Verified with Playwright: 0px -> ~60px gap; mobile unaffected (TOC is not a scroll container there).
This commit is contained in:
@@ -136,6 +136,15 @@
|
||||
padding-right: var(--space-3xs);
|
||||
}
|
||||
|
||||
/* Trailing bottom spacing when the TOC scrolls (long lists). Pseudo-element
|
||||
(real content in the scroll area) rather than the container's
|
||||
padding-bottom, which Firefox clips in this nested flex layout. */
|
||||
.toc::after {
|
||||
content: "";
|
||||
display: block;
|
||||
height: var(--space-xl);
|
||||
}
|
||||
|
||||
.toc summary {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
+51
-10
@@ -8,8 +8,11 @@ class MarkdownHelper
|
||||
/**
|
||||
* Extract h1–h3 headings from raw markdown content as TOC items.
|
||||
*
|
||||
* Each heading gets an anchor id matching CommonMark's default slugification
|
||||
* (lowercase, spaces → hyphens, punctuation stripped).
|
||||
* The heading is parsed with CommonMark so inline markup (`*em*`,
|
||||
* `**strong**`, `` `code` ``, `[links]()`) is resolved instead of leaking
|
||||
* raw markdown into the TOC. The anchor id is slugified from the parsed
|
||||
* inline text, which is what CommonMark uses to build the rendered heading
|
||||
* ids — so TOC links and heading anchors always match.
|
||||
*
|
||||
* @return array<int, array{label: string, href: string, level: int}>
|
||||
*/
|
||||
@@ -17,19 +20,57 @@ class MarkdownHelper
|
||||
{
|
||||
$items = [];
|
||||
$lines = explode("\n", $content);
|
||||
|
||||
|
||||
// Use CommonMark's own SlugNormalizer so TOC links match the rendered heading IDs exactly.
|
||||
$normalizer = new \League\CommonMark\Normalizer\SlugNormalizer();
|
||||
|
||||
foreach ($lines as $line) {
|
||||
if (preg_match('/^(#{1,3})\s+(.+)$/', $line, $m)) {
|
||||
$level = strlen($m[1]);
|
||||
$label = trim($m[2]);
|
||||
$id = $normalizer->normalize($label);
|
||||
$items[] = ['label' => $label, 'href' => '#' . $id, 'level' => $level];
|
||||
if (!preg_match('/^(#{1,3})\s+(.+)$/', $line, $m)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$headingText = self::parseInlineText(trim($m[2]));
|
||||
|
||||
$items[] = [
|
||||
'label' => $headingText,
|
||||
'href' => '#' . $normalizer->normalize($headingText),
|
||||
'level' => strlen($m[1]),
|
||||
];
|
||||
}
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse markdown and return its plain-text content.
|
||||
*
|
||||
* Walks the inline AST and concatenates the text nodes, so links keep their
|
||||
* label (`[lien](url)` → "lien") and markup is dropped.
|
||||
*/
|
||||
private static function parseInlineText(string $markdown): string
|
||||
{
|
||||
static $parser = null;
|
||||
if ($parser === null) {
|
||||
$environment = new \League\CommonMark\Environment\Environment([
|
||||
'html_input' => 'strip',
|
||||
]);
|
||||
$environment->addExtension(new \League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension());
|
||||
$parser = new \League\CommonMark\Parser\MarkdownParser($environment);
|
||||
}
|
||||
|
||||
$document = $parser->parse($markdown);
|
||||
$walker = $document->walker();
|
||||
$text = '';
|
||||
|
||||
while ($event = $walker->next()) {
|
||||
if (!$event->isEntering()) {
|
||||
continue;
|
||||
}
|
||||
$node = $event->getNode();
|
||||
if ($node instanceof \League\CommonMark\Node\Inline\Text
|
||||
|| $node instanceof \League\CommonMark\Extension\CommonMark\Node\Inline\Code
|
||||
) {
|
||||
$text .= $node->getLiteral();
|
||||
}
|
||||
}
|
||||
|
||||
return trim($text);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user