Skip to content

LegalDoc layout

This page documents the layout system used for legal-style documents in this repository.

It focuses on:

  • what the layout supports right now
  • how to use it in content
  • what is coupled to Starlight
  • what it would take to reuse in another Astro project

The LegalDoc layout is intentionally scoped. It only activates when a page sets:

docLayout: legalDoc

The scoped behavior is implemented as a combination of:

  1. content frontmatter (docLayout)
  2. a wrapper class on the rendered content panel (content-panel--legal-doc)
  3. legal-specific CSS in legal-doc.scss
  4. a legal-only remark transform that adds legal-clause classes for paragraph numbering

This keeps the generic markdown/MDX attribute behavior separate from legal layout behavior.

Current support is implemented in astro/src/styles/legal-doc.scss and astro/src/components/overrides/ContentPanel.astro.

When the page has docLayout: legalDoc, the content panel receives:

  • content-panel--legal-doc

All legal CSS selectors are scoped to that class.

Inside legal scope, each top-level h2 wrapper increments a section counter.

Rendered output pattern:

  • 1 Tiltrædelsesdato og arbejdssted
  • 2 Ansvar og kompetencer
  • 3 Løn og lønforhandling

Paragraphs marked with legal-clause are rendered with clause numbers using CSS counters:

  • 1.1 ...
  • 1.2 ...
  • 2.1 ...

The legal-clause class is added by the legal-only remark plugin for ordinary paragraphs under h2 sections.

Any paragraph with skip-number is excluded from auto-tagging and auto-number rendering.

The stylesheet includes optional list styles for structured clauses:

  • ol.legal-clauses > li renders as section.clause
  • nested ol.legal-subclauses > li renders as section.clause.subclause

This enables explicit legal list structures when paragraph auto-numbering is not enough.

Within legal scope, elements using table-no-border remove table borders.

This is used for signature and party-information table layouts.

---
title: Example Legal Document
description: Example
docLayout: legalDoc
---

Step 2: Author headings and paragraphs normally

Section titled “Step 2: Author headings and paragraphs normally”

Use ## headings for section boundaries and plain paragraphs for clauses.

The legal plugin + CSS counters do the numbering.

Step 3: Exclude specific paragraphs when needed

Section titled “Step 3: Exclude specific paragraphs when needed”

Use a trailing remark-attr directive to apply skip-number:

Arbejdsstedet er ... <remark-attr class="skip-number" />

You can combine attributes:

Arbejdsstedet er ... <remark-attr style="text-decoration: underline;" class="skip-number" />

Step 4: Use optional list styles for explicit nested clauses

Section titled “Step 4: Use optional list styles for explicit nested clauses”
<ol class="legal-clauses">
<li>
First clause
<ol class="legal-subclauses">
<li>First subclause</li>
</ol>
</li>
</ol>
Section titled “Step 5: Use borderless legal tables where needed”
<table class="table-no-border">...</table>

These files together define the LegalDoc layout behavior:

  • astro/src/content.config.ts
    • defines docLayout schema values (default, legalDoc)
  • astro/src/components/overrides/ContentPanel.astro
    • applies content-panel--legal-doc only for legal pages
  • astro/src/styles/legal-doc.scss
    • contains all legal-scoped counters and helper styles
  • astro/src/config/remark-legal-clauses.mjs
    • auto-tags eligible paragraphs with legal-clause only for legal pages
  • astro/astro.config.mjs
    • registers the legal remark plugin in markdown pipeline
  1. Counter selectors currently target Starlight DOM wrappers (.sl-markdown-content, .sl-heading-wrapper.level-h2).
  2. Clause auto-tagging currently depends on h2 as the section boundary.
  3. Paragraph auto-tagging is top-level and structure-sensitive by design.
  4. If content manually prefixes numbers (for example **2.3**), those visual numbers can duplicate generated numbering.

Reusing this in another Astro repo is practical, and there are three paths.

Path A: Astro + Starlight target (lowest friction)

Section titled “Path A: Astro + Starlight target (lowest friction)”
  1. Copy remark-legal-clauses.mjs.
  2. Register it in markdown.remarkPlugins after the generic attr plugin.
  3. Add docLayout to your content schema.
  4. Add a content wrapper class toggle equivalent to content-panel--legal-doc.
  5. Copy legal CSS and keep selectors aligned with your rendered Starlight structure.
  6. Mark target pages with docLayout: legalDoc.

Path B: Astro without Starlight (moderate adaptation)

Section titled “Path B: Astro without Starlight (moderate adaptation)”
  1. Keep the same plugin strategy (docLayout-gated legal clause tagging).
  2. Keep the same scoped class strategy.
  3. Rewrite CSS selectors to your renderer DOM (heading wrappers and content container differ from Starlight).
  4. Verify counters with real rendered HTML before rollout.

Path C: Non-Astro markdown pipeline (highest adaptation)

Section titled “Path C: Non-Astro markdown pipeline (highest adaptation)”
  1. Port legal clause tagging logic to your markdown AST toolchain.
  2. Recreate frontmatter gating in that toolchain.
  3. Reapply scoped CSS counters in the host renderer.
  4. Revisit syntax support for trailing directives if MDX/JSX parsing differs.
  1. A frontmatter field to opt pages into legal layout.
  2. A scoped wrapper class that appears only for legal pages.
  3. A transform that adds legal-clause classes only for opted-in pages.
  4. CSS counters scoped to the wrapper class.
  5. Test fixtures with:
    • multiple h2 sections
    • skip-number paragraphs
    • structured clause lists
    • borderless legal tables
  • remark-attr-for-mdx is generic and reusable across all documents.
  • LegalDoc layout is a separate, optional layer.
  • Keeping them separate avoids layout coupling in the generic authoring plugin.