diff --git a/Sources/AsciiDocRender/DocumentRender.swift b/Sources/AsciiDocRender/DocumentRender.swift index 199381a..e3f4a3d 100644 --- a/Sources/AsciiDocRender/DocumentRender.swift +++ b/Sources/AsciiDocRender/DocumentRender.swift @@ -195,6 +195,9 @@ public final class DocumentRenderer { if let pageCSS = pageCSS(from: templateDocument) { templateContext["pageCSS"] = pageCSS } + if let styleCSS = styleCSS(from: templateDocument) { + templateContext["styleCSS"] = styleCSS + } xadContext["template"] = templateContext } @@ -941,6 +944,38 @@ public final class DocumentRenderer { return lines.joined(separator: "\n") } + private func styleCSS(from template: XADTemplateDocument) -> String? { + guard let styleValue = template.typedAttributes["style"] else { return nil } + guard case .dictionary(let style) = styleValue else { return nil } + + var lines: [String] = [":root {"] + + if case .dictionary(let font)? = style["font"] { + if let body = stringValue(font["body"]) { lines.append(" --xad-font-body: \"\(body)\";") } + if let heading = stringValue(font["heading"]) { lines.append(" --xad-font-heading: \"\(heading)\";") } + if let mono = stringValue(font["mono"]) { lines.append(" --xad-font-mono: \"\(mono)\";") } + } + + if case .dictionary(let size)? = style["size"] { + if let body = stringValue(size["body"]) { lines.append(" --xad-size-body: \(body);") } + if let h1 = stringValue(size["h1"]) { lines.append(" --xad-size-h1: \(h1);") } + if let h2 = stringValue(size["h2"]) { lines.append(" --xad-size-h2: \(h2);") } + } + + if case .dictionary(let line)? = style["line"] { + if let leading = stringValue(line["leading"]) { lines.append(" --xad-line-leading: \(leading);") } + } + + if case .dictionary(let color)? = style["color"] { + if let text = stringValue(color["text"]) { lines.append(" --xad-color-text: \(text);") } + if let muted = stringValue(color["muted"]) { lines.append(" --xad-color-muted: \(muted);") } + } + + guard lines.count > 1 else { return nil } + lines.append("}") + return lines.joined(separator: "\n") + } + private func formatAuthor(_ author: AdocAuthor) -> String { if let fullname = author.fullname, !fullname.isEmpty { return appendAddress(fullname, address: author.address) diff --git a/Templates/html5/document.stencil b/Templates/html5/document.stencil index e941ded..f73ab85 100644 --- a/Templates/html5/document.stencil +++ b/Templates/html5/document.stencil @@ -15,6 +15,9 @@ {% if xad and xad.template and xad.template.pageCSS %} {{ xad.template.pageCSS }} {% endif %} + {% if xad and xad.template and xad.template.styleCSS %} + {{ xad.template.styleCSS }} + {% endif %} :root { --surface: #ffffff; @@ -27,6 +30,15 @@ --accent-soft: rgba(37, 99, 235, 0.1); --shadow-soft: 0 10px 25px rgba(15, 23, 42, 0.08); --monospace: "SFMono-Regular", "Roboto Mono", ui-monospace, Menlo, Monaco, "Liberation Mono", monospace; + --xad-font-body: "Times New Roman"; + --xad-font-heading: "Times New Roman"; + --xad-font-mono: "SFMono-Regular", "Roboto Mono", ui-monospace, Menlo, Monaco, "Liberation Mono", monospace; + --xad-size-body: 11pt; + --xad-size-h1: 20pt; + --xad-size-h2: 14pt; + --xad-line-leading: 1.35; + --xad-color-text: #111827; + --xad-color-muted: #4b5563; } @media (prefers-color-scheme: dark) { @@ -57,6 +69,20 @@ line-height: 1.65; } + .xad-doc { + font-family: var(--xad-font-body, "Times New Roman"); + font-size: var(--xad-size-body, 11pt); + line-height: var(--xad-line-leading, 1.35); + color: var(--xad-color-text, #111827); + } + + .xad-doc h1, .xad-doc h2, .xad-doc h3, .xad-doc h4, .xad-doc h5, .xad-doc h6 { + font-family: var(--xad-font-heading, "Times New Roman"); + } + + .xad-doc h1 { font-size: var(--xad-size-h1, 20pt); } + .xad-doc h2 { font-size: var(--xad-size-h2, 14pt); } + a { color: var(--accent); text-decoration: none; @@ -107,6 +133,30 @@ min-width: 0; } + .xad-flow[data-slot="title"] .doc-title p { + font-family: var(--xad-font-heading, "Times New Roman"); + font-size: var(--xad-size-h1, 2.2rem); + font-weight: 700; + line-height: 1.1; + margin: 0; + } + + .xad-flow[data-slot="authors"] .author p { + margin: 0.25rem 0; + color: var(--xad-color-muted, #4b5563); + } + + .xad-flow[data-slot="abstract"] .block.paragraph { + background: var(--surface-muted); + border: 1px solid var(--border-soft); + border-radius: 12px; + padding: 1rem 1.25rem; + } + + .xad-flow[data-slot="abstract"] .block.paragraph p { + margin: 0; + } + .xad-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); diff --git a/Templates/xad/default/template.adoc b/Templates/xad/default/template.adoc index 8618b7f..d0c742f 100644 --- a/Templates/xad/default/template.adoc +++ b/Templates/xad/default/template.adoc @@ -1,5 +1,6 @@ = Default Template :page.masters: {"default": {"size": "A4", "margins": {"top": "18mm", "right": "18mm", "bottom": "20mm", "left": "18mm"}, "duplex": false}} +:style: {"font": {"body": "Libertinus Serif", "heading": "Libertinus Sans", "mono": "Iosevka"}, "size": {"body": "11pt", "h1": "20pt", "h2": "14pt"}, "line": {"leading": 1.35}, "color": {"text": "#111827", "muted": "#4b5563"}} [layout] ---- diff --git a/Templates/xad/double-column-explicit/template.adoc b/Templates/xad/double-column-explicit/template.adoc index 653404d..6f565cd 100644 --- a/Templates/xad/double-column-explicit/template.adoc +++ b/Templates/xad/double-column-explicit/template.adoc @@ -1,5 +1,6 @@ = Double Column Explicit Template :page.masters: {"default": {"size": "A4", "margins": {"top": "18mm", "right": "18mm", "bottom": "20mm", "left": "18mm"}, "duplex": false}} +:style: {"font": {"body": "Libertinus Serif", "heading": "Libertinus Sans", "mono": "Iosevka"}, "size": {"body": "11pt", "h1": "20pt", "h2": "14pt"}, "line": {"leading": 1.35}, "color": {"text": "#111827", "muted": "#4b5563"}} [layout] ---- diff --git a/Templates/xad/double-column-flow/template.adoc b/Templates/xad/double-column-flow/template.adoc index 75ef252..5e25c74 100644 --- a/Templates/xad/double-column-flow/template.adoc +++ b/Templates/xad/double-column-flow/template.adoc @@ -1,5 +1,6 @@ = Double Column Flow Template :page.masters: {"default": {"size": "A4", "margins": {"top": "18mm", "right": "18mm", "bottom": "20mm", "left": "18mm"}, "duplex": false}} +:style: {"font": {"body": "Libertinus Serif", "heading": "Libertinus Sans", "mono": "Iosevka"}, "size": {"body": "11pt", "h1": "20pt", "h2": "14pt"}, "line": {"leading": 1.35}, "color": {"text": "#111827", "muted": "#4b5563"}} [layout] ---- diff --git a/Templates/xad/trivial/template.adoc b/Templates/xad/trivial/template.adoc index afb95cb..cf8846d 100644 --- a/Templates/xad/trivial/template.adoc +++ b/Templates/xad/trivial/template.adoc @@ -1,5 +1,12 @@ = Trivial Template -:page.masters: {"default": {"size": "A4", "margins": {"top": "18mm", "right": "18mm", "bottom": "20mm", "left": "18mm"}, "duplex": false}} +:page.masters: { + default: { + size: "A4", + margins: { top: "18mm", right: "18mm", bottom": "20mm", left: "18mm"}, + duplex: false + } +} +:style: {"font": {"body": "Libertinus Serif", "heading": "Libertinus Sans", "mono": "Iosevka"}, "size": {"body": "11pt", "h1": "20pt", "h2": "14pt"}, "line": {"leading": 1.35}, "color": {"text": "#111827", "muted": "#4b5563"}} [layout] ----