From dff9c043945b6a8f535da876b95d76c40f01db03 Mon Sep 17 00:00:00 2001 From: Xinyi Lin Date: Fri, 26 Jun 2026 19:48:35 -0400 Subject: [PATCH] feat(ui): heading-case control, decluttered sidebar, and forge brand mark - Replace the uppercaseHeadings boolean with a headingCase enum (small caps / uppercase / normal) across the editor model, editor + print CSS, and the LaTeX template (\scshape / \MakeUppercase / neither); migrate legacy stored values. - Regroup the Typography panel by element (Headings / Entries / Skills / Contact) with a single 'Jake's defaults' reset; drop the divergent Italic-date control so the date column follows italic subtitles (matches role-fit). - Collapse the 11 fine-grained spacing sliders behind a remembered 'Fine-tune spacing' disclosure; keep Zoom + presets always visible. - Make the Preset and Case segmented controls full-width so they stop wrapping/ overflowing beside their labels in the narrow sidebar; re-measure the pill on option-count changes. - Add a forge anvil favicon + sidebar brand lockup; wire favicon, theme-color, and description into index.html. Refresh README to match. Co-Authored-By: Claude Opus 4.8 --- README.md | 17 ++- index.html | 6 + public/favicon.svg | 17 +++ server/latex/templates/jakes.mjs | 36 +++--- src/App.tsx | 182 +++++++++++++++++++++---------- src/hooks/useDocStyle.ts | 86 +++++++++------ src/styles/index.css | 96 +++++++++++++++- src/styles/resume-document.css | 6 +- src/styles/resume-editor.css | 11 +- 9 files changed, 340 insertions(+), 117 deletions(-) create mode 100644 public/favicon.svg diff --git a/README.md b/README.md index 802daaa..ae41b04 100644 --- a/README.md +++ b/README.md @@ -18,12 +18,15 @@ tailoring, job tracker, and applications stripped out. titles with right-aligned dates, italic subtitles, automatic multi-page pagination that keeps headings with their first entry. - **Layout & spacing controls** — page zoom (also ⌘/Ctrl +/- / 0), - Compact/Normal/Relaxed presets, fine sliders for line height and - section/entry/line gaps. Settings persist to `localStorage`. -- **Typography controls** — toggle uppercase, underline, and bold section - headings; bold titles, italic subtitles/dates, bold skill labels; and a + Compact/Normal/Relaxed presets, and fine sliders for line height and + header/section/entry/list gaps tucked behind a "Fine-tune spacing" disclosure. + Save the current spacing as a reusable **Custom** preset. Settings persist to + `localStorage`. +- **Typography controls** — grouped by element (Headings / Entries / Skills / + Contact): section-heading **case** (small caps / uppercase / normal) plus bold + and underline; bold titles, italic subtitles; bold skill labels; and a configurable contact divider (quick-pick glyphs or custom 1-2 char input). - Includes a one-click reset. + One-click reset to Jake's defaults. - **Exports** (each download opens a rename dialog pre-filled with a resume-derived file name) - **PDF - LaTeX** — the resume rendered through the Jake's template and compiled @@ -79,6 +82,10 @@ src/ editor/ the editable on-page resume (sections, entries, bullets, skills) Resume*.tsx read-only document + off-screen print layer styles/ design tokens, resume document/editor CSS, app shell +public/ + favicon.svg forge brand mark (anvil app-icon, also the sidebar lockup) + fonts/ embedded LM Roman faces for PDF-faithful on-page rendering + CNAME GitHub Pages custom-domain config (jakeforge.xinyiklin.com) server/ latex/ Jake's template renderer, resume parser, Tectonic wrapper docx.mjs DOCX text extractor (zero-dep; shells to unzip) diff --git a/index.html b/index.html index 997f9eb..adb9f0c 100644 --- a/index.html +++ b/index.html @@ -3,6 +3,12 @@ + + + jakeforge — Jake's-style resume editor diff --git a/public/favicon.svg b/public/favicon.svg new file mode 100644 index 0000000..cdda80e --- /dev/null +++ b/public/favicon.svg @@ -0,0 +1,17 @@ + + jakeforge + + + + + + + + + + + + + + + diff --git a/server/latex/templates/jakes.mjs b/server/latex/templates/jakes.mjs index 749d158..91df891 100644 --- a/server/latex/templates/jakes.mjs +++ b/server/latex/templates/jakes.mjs @@ -54,10 +54,10 @@ const DEFAULTS = { boldTitles: true, boldSkillLabels: true, italicSubtitles: true, - italicDates: true, - // Section-heading look — small-caps (\scshape) is the default; the Uppercase - // toggle forces ALL CAPS, and the rule under the heading is the titlerule. - uppercaseHeadings: false, + // Section-heading case — small-caps (\scshape) is Jake's default; "uppercase" + // forces ALL CAPS (\MakeUppercase) and "none" leaves Title Case. The rule under + // the heading is the titlerule. + headingCase: "smallcaps", sectionRule: true }; @@ -143,8 +143,14 @@ function styleToLatex(docStyle) { boldTitles: bool(docStyle.boldTitles, DEFAULTS.boldTitles), boldSkillLabels: bool(docStyle.boldSkillLabels, DEFAULTS.boldSkillLabels), italicSubtitles: bool(docStyle.italicSubtitles, DEFAULTS.italicSubtitles), - italicDates: bool(docStyle.italicDates, DEFAULTS.italicDates), - uppercaseHeadings: bool(docStyle.uppercaseHeadings, DEFAULTS.uppercaseHeadings), + // headingCase replaced the old `uppercaseHeadings` boolean; honor a legacy + // client that still sends `uppercaseHeadings: true`. + headingCase: + docStyle.headingCase === "uppercase" || docStyle.headingCase === "none" + ? docStyle.headingCase + : docStyle.uppercaseHeadings === true + ? "uppercase" + : DEFAULTS.headingCase, sectionRule: bool(docStyle.sectionRule, DEFAULTS.sectionRule) }; } @@ -227,9 +233,10 @@ function buildPreamble(style) { % Section gaps are pure separators: \titlespacing "before" is the WHOLE gap % above the heading (= sectionGap), "after" is the rule→first-row gap % (= sectionEntryGap). titlesec drops the before-glue at the top of a page, so -% the first section's gap is set by the header \vspace instead. \scshape gives -% small-caps; the Uppercase toggle uppercases the heading text in renderSection. -\titleformat{\section}{\scshape\raggedright\large${style.boldHeadings ? "\\bfseries" : ""}}{}{0em}{}[\color{black}${style.sectionRule ? "\\titlerule" : ""}] +% the first section's gap is set by the header \vspace instead. Heading case: +% \scshape (small caps), \MakeUppercase before-code (full caps), or neither +% (plain Title Case as stored). +\titleformat{\section}{${style.headingCase === "smallcaps" ? "\\scshape" : ""}\raggedright\large${style.boldHeadings ? "\\bfseries" : ""}}{}{0em}{${style.headingCase === "uppercase" ? "\\MakeUppercase" : ""}}[\color{black}${style.sectionRule ? "\\titlerule" : ""}] \titlespacing*{\section}{0pt}{${fmtPt(style.sectionGapPt)}}{${fmtPt(style.sectionEntryGapPt)}} \ifdefined\pdfgentounicode\pdfgentounicode=1\fi @@ -258,12 +265,12 @@ function buildPreamble(style) { \item \resumeRow{${bf("#1", style.boldTitles)}}{#2}% \vspace{${fmtPt(style.titleSubVSpacePt)}}% - \resumeRow{${it("\\small#3", style.italicSubtitles)}}{${it("\\small #4", style.italicDates)}}% + \resumeRow{${it("\\small#3", style.italicSubtitles)}}{${it("\\small #4", style.italicSubtitles)}}% } \newcommand{\resumeSubSubheading}[2]{% \item - \resumeRow{${it("\\small#1", style.italicSubtitles)}}{${it("\\small #2", style.italicDates)}}% + \resumeRow{${it("\\small#1", style.italicSubtitles)}}{${it("\\small #2", style.italicSubtitles)}}% } \newcommand{\resumeProjectHeading}[2]{% @@ -367,10 +374,9 @@ ${item.bullets.map((bullet) => ` \\resumeItem{${escapeTex(bullet)}}`).joi function renderSection(section, style) { const hasAnyHeading = section.items.some((item) => item.title || item.subtitle || item.meta || item.location); - // Headings are small-caps (\scshape) by default; the Uppercase toggle forces - // ALL CAPS by uppercasing the text (\scshape is then a no-op on the caps). - const titled = titleCase(section.heading); - const heading = style.uppercaseHeadings ? titled.toUpperCase() : titled; + // Heading case is applied by \titleformat (\\scshape / \\MakeUppercase / none), + // so the text is just stored Title Case here. + const heading = titleCase(section.heading); // Summary-like headings render their rows as plain small paragraphs instead of // the skills block (which draws \\ line breaks and bolds "Label:" prefixes). diff --git a/src/App.tsx b/src/App.tsx index 6d5fecf..83661c7 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,8 +1,8 @@ import { useCallback, useEffect, useLayoutEffect, useRef, useState, type ChangeEvent, type DragEvent } from "react"; -import { Eye, FileCode2, FileDown, RotateCcw, Upload } from "lucide-react"; +import { ChevronRight, Eye, FileCode2, FileDown, RotateCcw, Upload } from "lucide-react"; import { useResumeEditor } from "./hooks/useResumeEditor"; -import { useDocStyle, DOC_ZOOM_OPTIONS, DOC_SPACING_PRESETS, DOC_SPACING_KEYS, type DocSpacingKey, type DocSpacingPreset } from "./hooks/useDocStyle"; +import { useDocStyle, JAKE_STYLE_DEFAULTS, DOC_ZOOM_OPTIONS, DOC_SPACING_PRESETS, DOC_SPACING_KEYS, type DocSpacingKey, type DocSpacingPreset, type HeadingCase } from "./hooks/useDocStyle"; import { useTemplates } from "./hooks/useTemplates"; import { useResumeExport } from "./hooks/useResumeExport"; import { ResumeEditor } from "./sections/editor/ResumeEditor"; @@ -13,6 +13,8 @@ import { fileToText } from "./lib/importResume"; import type { ResumeData } from "./lib/resumeData"; const STORAGE_KEY = "jakeforge.resume.v1"; +// Remembers whether the spacing-sliders disclosure is expanded. +const FINE_TUNE_KEY = "jakeforge.ui.fineTune.v1"; function loadSavedResume(): ResumeData | null { try { @@ -106,11 +108,15 @@ function SpacingSlider({ function AnimatedSeg({ items, activeKey, - onSelect + onSelect, + block = false }: { items: { key: K; label: string }[]; activeKey: K | null; onSelect: (key: K) => void; + // Full-width track with equal-width options — for 3-option controls that don't + // fit beside an inline label in the narrow sidebar. + block?: boolean; }) { const containerRef = useRef(null); const btnRefs = useRef>(new Map()); @@ -123,13 +129,16 @@ function AnimatedSeg({ const cr = containerRef.current.getBoundingClientRect(); const br = btn.getBoundingClientRect(); setPill({ left: br.left - cr.left, width: br.width }); - }, [activeKey]); + // items.length is a dep: in the full-width (block) layout, adding the saved + // "Custom" option reflows every button to a new equal width even when + // activeKey is unchanged, so the pill must re-measure on count changes too. + }, [activeKey, items.length]); useLayoutEffect(measure, [measure]); useEffect(() => { window.addEventListener("resize", measure); return () => window.removeEventListener("resize", measure); }, [measure]); return ( -
+
{pill && ( ({ // via the custom input). const COMMON_DIVIDERS = ["|", "•", "·", "–", "/"]; +// Section-heading letter case — a mutually-exclusive choice, shown as a segmented +// control (small caps is Jake's \scshape default; "Normal" leaves Title Case). +const HEADING_CASES: { key: HeadingCase; label: string }[] = [ + { key: "smallcaps", label: "Small caps" }, + { key: "uppercase", label: "Uppercase" }, + { key: "none", label: "Normal" } +]; + // A pill that toggles a boolean DocStyle flag — filled when active. Reads more // like a formatting toolbar than a column of checkboxes. function ToggleChip({ @@ -184,6 +201,27 @@ export default function App() { const templates = useTemplates(); const [texStatus, setTexStatus] = useState(""); + // The 11 fine-grained spacing sliders live behind a disclosure — most people + // pick a preset and never open them. Closed by default; choice is remembered. + const [showFineTune, setShowFineTune] = useState(() => { + try { + return window.localStorage.getItem(FINE_TUNE_KEY) === "1"; + } catch { + return false; + } + }); + function toggleFineTune() { + setShowFineTune((open) => { + const next = !open; + try { + window.localStorage.setItem(FINE_TUNE_KEY, next ? "1" : "0"); + } catch { + // Storage unavailable (private mode); the toggle still works this session. + } + return next; + }); + } + const selectedTemplate = templates.templates.find((t) => t.id === templates.selectedTemplateId) ?? null; @@ -355,8 +393,11 @@ export default function App() {
diff --git a/src/hooks/useDocStyle.ts b/src/hooks/useDocStyle.ts index 9478083..2ce19f2 100644 --- a/src/hooks/useDocStyle.ts +++ b/src/hooks/useDocStyle.ts @@ -1,5 +1,9 @@ import { useEffect, useMemo, useRef, useState, type CSSProperties } from "react"; +// Section-heading letter case — one mutually-exclusive choice (small caps, as in +// Jake's \scshape; full ALL CAPS; or plain Title Case), so it's a segmented pick. +export type HeadingCase = "smallcaps" | "uppercase" | "none"; + // User-adjustable typography for the HTML resume page (the editor, its read-only // print mirror, and therefore the "PDF · clean" export). The same values are // also sent to the LaTeX renderer so `.tex`, PDF preview, and PDF · LaTeX use the @@ -22,9 +26,8 @@ export type DocStyle = { boldTitles: boolean; // entry title (role / project / school) boldHeadings: boolean; // section headings boldSkillLabels: boolean; // "Languages:" style labels - italicSubtitles: boolean; // subtitle + location row - italicDates: boolean; // the right-aligned date / link slot - uppercaseHeadings: boolean; // force section headings to ALL CAPS (off = small caps, as in the PDF) + italicSubtitles: boolean; // subtitle + location/date rows + headingCase: HeadingCase; // section-heading letter case (small caps / uppercase / normal) sectionRule: boolean; // the horizontal rule under each section heading contactDivider: string; // 1–2 char separator between header contact items }; @@ -46,14 +49,37 @@ export const DOC_STYLE_DEFAULTS: DocStyle = { boldHeadings: false, boldSkillLabels: true, italicSubtitles: true, - italicDates: false, - // Off by default → small-caps headings that match the Jake template's \scshape. - // The toggle forces ALL CAPS for users who prefer it. - uppercaseHeadings: false, + // Small caps by default → matches the Jake template's \scshape headings. + headingCase: "smallcaps", sectionRule: true, contactDivider: "|" }; +// The non-spacing "style" fields the Typography panel owns. Lifted as a type so +// the "Jake's defaults" reset and applyStyle() stay in sync with the controls. +export type DocStyleFields = Pick< + DocStyle, + | "boldTitles" + | "boldHeadings" + | "boldSkillLabels" + | "italicSubtitles" + | "headingCase" + | "sectionRule" + | "contactDivider" +>; + +// The authentic upstream Jake's-Resume look for the style fields — applied by the +// Typography panel's "Jake's defaults" reset. Spacing/zoom are left untouched. +export const JAKE_STYLE_DEFAULTS: DocStyleFields = { + boldTitles: DOC_STYLE_DEFAULTS.boldTitles, + boldHeadings: DOC_STYLE_DEFAULTS.boldHeadings, + boldSkillLabels: DOC_STYLE_DEFAULTS.boldSkillLabels, + italicSubtitles: DOC_STYLE_DEFAULTS.italicSubtitles, + headingCase: DOC_STYLE_DEFAULTS.headingCase, + sectionRule: DOC_STYLE_DEFAULTS.sectionRule, + contactDivider: DOC_STYLE_DEFAULTS.contactDivider +}; + // Single source of truth for the spacing fields that make up a preset (the // sliders, the built-in presets, and the saved Custom snapshot). The key type // and the runtime list derive from this one array so they can't drift. @@ -175,8 +201,15 @@ function coerce(raw: unknown, legacyMode: "current" | "v1" = "current"): DocStyl boldHeadings: r.boldHeadings === true, boldSkillLabels: r.boldSkillLabels !== false, italicSubtitles: r.italicSubtitles !== false, - italicDates: r.italicDates === true, - uppercaseHeadings: r.uppercaseHeadings === true, + // headingCase replaced the old `uppercaseHeadings` boolean: a stored `true` + // migrates to "uppercase"; anything else (including the old `false`) falls to + // the small-caps default. + headingCase: + r.headingCase === "uppercase" || r.headingCase === "none" + ? r.headingCase + : (r as Record).uppercaseHeadings === true + ? "uppercase" + : DOC_STYLE_DEFAULTS.headingCase, sectionRule: r.sectionRule !== false, // Cap at 2 chars; fall back to "|" when missing (not when intentionally blank). contactDivider: typeof r.contactDivider === "string" ? r.contactDivider.slice(0, 2) : DOC_STYLE_DEFAULTS.contactDivider @@ -253,9 +286,10 @@ export function useDocStyle() { "--doc-heading-weight": style.boldHeadings ? "700" : "400", "--doc-skill-label-weight": style.boldSkillLabels ? "700" : "400", "--doc-subtitle-style": style.italicSubtitles ? "italic" : "normal", - "--doc-date-style": style.italicDates ? "italic" : "normal", - // Section headings render small-caps by default; the toggle forces caps. - "--doc-heading-transform": style.uppercaseHeadings ? "uppercase" : "none", + // Heading case: small-caps via font-variant, ALL CAPS via text-transform, + // "none" leaves the stored Title Case untouched. + "--doc-heading-variant": style.headingCase === "smallcaps" ? "small-caps" : "normal", + "--doc-heading-transform": style.headingCase === "uppercase" ? "uppercase" : "none", "--doc-rule-width": style.sectionRule ? "1px" : "0", // Quoted so it's a valid CSS `content` token. "--doc-contact-divider": JSON.stringify(style.contactDivider) @@ -267,22 +301,10 @@ export function useDocStyle() { setStyle((current) => ({ ...current, [key]: value })); } - // Reset only the typography fields (toggles + contact divider). Zoom and the - // spacing values are left as-is — the spacing presets already act as a reset - // for those. - const TYPOGRAPHY_DEFAULTS = { - boldTitles: DOC_STYLE_DEFAULTS.boldTitles, - boldHeadings: DOC_STYLE_DEFAULTS.boldHeadings, - boldSkillLabels: DOC_STYLE_DEFAULTS.boldSkillLabels, - italicSubtitles: DOC_STYLE_DEFAULTS.italicSubtitles, - italicDates: DOC_STYLE_DEFAULTS.italicDates, - uppercaseHeadings: DOC_STYLE_DEFAULTS.uppercaseHeadings, - sectionRule: DOC_STYLE_DEFAULTS.sectionRule, - contactDivider: DOC_STYLE_DEFAULTS.contactDivider - } satisfies Partial; - - function resetTypography() { - setStyle((current) => ({ ...current, ...TYPOGRAPHY_DEFAULTS })); + // Apply a partial set of style fields at once (e.g. the Typography panel's + // "Jake's defaults" reset), leaving spacing and zoom untouched. + function applyStyle(partial: Partial) { + setStyle((current) => ({ ...current, ...partial })); } function applySpacingPreset(preset: DocSpacingPreset) { @@ -300,19 +322,19 @@ export function useDocStyle() { } } - const isTypographyDefault = useMemo( - () => (Object.keys(TYPOGRAPHY_DEFAULTS) as (keyof typeof TYPOGRAPHY_DEFAULTS)[]).every((k) => style[k] === TYPOGRAPHY_DEFAULTS[k]), + const isStyleDefault = useMemo( + () => (Object.keys(JAKE_STYLE_DEFAULTS) as (keyof DocStyleFields)[]).every((k) => style[k] === JAKE_STYLE_DEFAULTS[k]), [style] ); return { style, set, - resetTypography, + applyStyle, applySpacingPreset, saveCustomPreset, customPreset, - isTypographyDefault, + isStyleDefault, cssVars }; } diff --git a/src/styles/index.css b/src/styles/index.css index fac6962..db24eb3 100644 --- a/src/styles/index.css +++ b/src/styles/index.css @@ -30,12 +30,26 @@ .brand { display: flex; - flex-direction: column; - gap: 2px; + align-items: center; + gap: var(--s2); padding-bottom: var(--s3); border-bottom: 1px solid var(--hairline-soft); } +.brand__icon { + flex: 0 0 auto; + width: 34px; + height: 34px; + border-radius: var(--r-md); +} + +.brand__text { + display: flex; + flex-direction: column; + gap: 2px; + min-width: 0; +} + .brand__mark { font-family: var(--font-serif); font-size: 1.4rem; @@ -433,6 +447,58 @@ border-color: var(--accent); } +/* Disclosure that hides the 11 fine-grained spacing sliders behind one row. */ +.ctl-disclosure { + display: flex; + align-items: center; + gap: var(--s2); + width: 100%; + padding: 6px 0; + font-size: 0.82rem; + font-weight: 600; + color: var(--ink-muted); + background: none; + border: 0; + cursor: pointer; + transition: color 0.12s var(--ease); +} + +.ctl-disclosure:hover { + color: var(--ink-strong); +} + +.ctl-disclosure__chev { + flex: 0 0 auto; + color: var(--ink-faint); + transition: transform 0.15s var(--ease); +} + +.ctl-disclosure.is-open .ctl-disclosure__chev { + transform: rotate(90deg); +} + +/* "Edited" tag when the live spacing matches no preset — a hint that there are + custom values tucked inside the collapsed disclosure. */ +.ctl-disclosure__hint { + margin-left: auto; + padding: 1px 7px; + font-size: 0.62rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.06em; + color: var(--ink-faint); + background: var(--card-elev); + border: 1px solid var(--hairline-soft); + border-radius: var(--r-pill); +} + +.ctl-disclosure__body { + display: flex; + flex-direction: column; + gap: var(--s3); + margin-top: var(--s1); +} + .ctl-divider { width: 44px; padding: 4px 6px; @@ -485,6 +551,31 @@ border-radius: var(--r-pill); } +/* Full-width track with equal-width options — used for the 3-option Preset and + Case controls, which don't fit beside an inline label in the narrow sidebar. */ +.seg--full { + display: flex; + width: 100%; +} + +.seg--full .seg__opt { + flex: 1 1 0; + min-width: 0; + text-align: center; +} + +/* Stacked label + full-width segmented control. */ +.ctl-seg-field { + display: flex; + flex-direction: column; + gap: var(--s2); +} + +.ctl-seg-field__label { + font-size: 0.82rem; + color: var(--ink-muted); +} + .seg__pill { position: absolute; top: 2px; @@ -503,6 +594,7 @@ padding: 4px 12px; font-size: 0.78rem; font-weight: 600; + white-space: nowrap; color: var(--ink-soft); border-radius: var(--r-pill); transition: color 0.2s ease; diff --git a/src/styles/resume-document.css b/src/styles/resume-document.css index 30931ce..ba44cfa 100644 --- a/src/styles/resume-document.css +++ b/src/styles/resume-document.css @@ -126,14 +126,16 @@ margin-top: 0.62em; } -/* Section heading: small caps with a rule below, matching the PDF's \scshape. */ +/* Section heading: case-driven (small caps / uppercase / normal), with a rule + below matching the PDF's \scshape + \titlerule. */ .resume-doc__heading { margin: 0 0 0.16em; padding-bottom: 0.05em; border-bottom: 1px solid #000000; font-size: 1.02em; font-weight: 700; - font-variant: small-caps; + font-variant: var(--doc-heading-variant, small-caps); + text-transform: var(--doc-heading-transform, none); letter-spacing: 0.04em; color: #000000; } diff --git a/src/styles/resume-editor.css b/src/styles/resume-editor.css index f0168b9..5cb18f9 100644 --- a/src/styles/resume-editor.css +++ b/src/styles/resume-editor.css @@ -381,14 +381,15 @@ } /* Section heading: small caps with a rule below, matching the PDF's \scshape. - Headings are stored in title case ("Education") so CSS small-caps renders the - first letter as a full capital and the rest as small capitals. The Uppercase - toggle (--doc-heading-transform: uppercase) still forces ALL CAPS on demand. */ + Headings are stored in title case ("Education"), so the Case control drives + appearance: small-caps (--doc-heading-variant) renders the lowercase run as + small capitals, Uppercase (--doc-heading-transform) forces ALL CAPS, and + Normal leaves the stored Title Case untouched. */ .rdx-section__heading { /* \large at the 11pt documentclass = 12pt = 1.096em of \normalsize 10.95pt. */ font-size: 1.1em; font-weight: var(--doc-heading-weight, 400); - font-variant: small-caps; + font-variant: var(--doc-heading-variant, small-caps); text-transform: var(--doc-heading-transform, none); letter-spacing: 0.04em; color: #000000; @@ -530,7 +531,7 @@ min-width: 6ch; text-align: right; font-weight: 400; - font-style: var(--doc-date-style, normal); + font-style: normal; color: #000000; field-sizing: content; }