Related
#367 tracks the defect that leaves moved headings unsized. This issue is about the scale itself. #367 sets its minimum step and title rule from the convention chosen here.
What happened
Edward Saperia sent these reports on 2026-09-26.
- Labels look like body text. Under a heading "Team 13", the lines "Members:", "Problem Idea:", "Approach:" and "Datasets:" are H3 headings. Edward wrote: "This is an H3 - way too small, I can't tell it apart from normal text!" He added: "I like big fat headings".
- Same level, two sizes. "4. The Constitution" renders large. "5. The Agent" renders much smaller, close to body text. We do not know the levels of these two headings.
What we expect
Every heading stands out from body text at a glance. A higher level is clearly larger than a lower one. Two headings of the same level have the same size. A named typographic convention sets the scale, and we can say why each size is what it is.
Findings
Read from code. Nobody has measured these in a browser yet.
- The smallest rank in a section is always 12pt (
MIN_SIZE). The largest is 20pt (MAX_SIZE).
- We found no rule that sets a size for body paragraphs in the pad. Body text then follows the root size, which is 16px by default, or 12pt. The smallest heading would have the same size as body text, and bold would be the only difference.
- Every heading level has weight 700 and line height 1.15. Size is the only signal between levels.
- Sizes spread evenly between 12pt and 20pt. With three levels, the ratio between neighbours is 1.25 to 1.33. With six levels, it is 1.09 to 1.13.
- The CSS fallbacks, used when a heading has no size, are 20, 16, 14, 12, 11 and 11pt for h1 to h6. With a 12pt body, h4 equals body text, and h5 and h6 are smaller.
Reference points
The review compares at least these, then picks one.
- The HTML default sizes: 2em, 1.5em, 1.17em, 1em, 0.83em and 0.67em for h1 to h6 (Sections and headings). Its h4 to h6 are at or below body size, so this default is a weak target for skimming.
- A modular scale: a fixed ratio between neighbouring levels, such as 1.2 or 1.25, built from the body size.
- The current rule: sizes spread evenly between 12pt and 20pt by rank in a section.
Acceptance criteria
Needs from Edward
The links to the two pads, or the level of each heading in the "4. The Constitution" screenshot. We need them to tell a scale problem from a defect.
Blocked by
None — can start now.
Agent brief
Type: HITL. The maintainer picks the convention, the body ratio and the step ratio. An agent then implements and tests it.
Category: enhancement
Current behavior: See Findings.
Desired behavior: Headings stand out from body text and from each other, and the scale follows one named convention that the spec records.
Where to start: apps/webapp/src/components/TipTap/extensions/heading-scale/heading-scale.ts (MAX_SIZE, MIN_SIZE, buildDecorations). apps/webapp/src/styles/editor/_heading-node.scss (--hd-size and the per-level fallbacks). apps/webapp/src/components/TipTap/CLAUDE.md §HeadingScale. Search by symbol. File names are hints as of 2026-09-29.
Rules that apply: §HeadingScale is a mandatory spec. It ranks levels inside a section, uses decorations only, writes no sizes into the document, and forbids fixed per-level point maps. A convention that sets a size per level changes that spec. Get a maintainer ruling, then update the section in the same change. AGENTS.md §Test Policy allows a test here, because it pins reported behavior. CONTEXT.md §Pad outline holds the names.
Verify: Start make dev-local and confirm the webapp port. Open a pad that has all six levels and body text. In the console, run [...document.querySelectorAll('.ProseMirror h1, .ProseMirror h2, .ProseMirror h3, .ProseMirror p')].map(n => [n.tagName, getComputedStyle(n).fontSize]) and compare the values. Check light and dark themes.
Related
#367 tracks the defect that leaves moved headings unsized. This issue is about the scale itself. #367 sets its minimum step and title rule from the convention chosen here.
What happened
Edward Saperia sent these reports on 2026-09-26.
What we expect
Every heading stands out from body text at a glance. A higher level is clearly larger than a lower one. Two headings of the same level have the same size. A named typographic convention sets the scale, and we can say why each size is what it is.
Findings
Read from code. Nobody has measured these in a browser yet.
MIN_SIZE). The largest is 20pt (MAX_SIZE).Reference points
The review compares at least these, then picks one.
Acceptance criteria
apps/webapp/src/components/TipTap/CLAUDE.md§HeadingScale.Needs from Edward
The links to the two pads, or the level of each heading in the "4. The Constitution" screenshot. We need them to tell a scale problem from a defect.
Blocked by
None — can start now.
Agent brief
Type: HITL. The maintainer picks the convention, the body ratio and the step ratio. An agent then implements and tests it.
Category: enhancement
Current behavior: See Findings.
Desired behavior: Headings stand out from body text and from each other, and the scale follows one named convention that the spec records.
Where to start:
apps/webapp/src/components/TipTap/extensions/heading-scale/heading-scale.ts(MAX_SIZE,MIN_SIZE,buildDecorations).apps/webapp/src/styles/editor/_heading-node.scss(--hd-sizeand the per-level fallbacks).apps/webapp/src/components/TipTap/CLAUDE.md§HeadingScale. Search by symbol. File names are hints as of 2026-09-29.Rules that apply: §HeadingScale is a mandatory spec. It ranks levels inside a section, uses decorations only, writes no sizes into the document, and forbids fixed per-level point maps. A convention that sets a size per level changes that spec. Get a maintainer ruling, then update the section in the same change.
AGENTS.md§Test Policy allows a test here, because it pins reported behavior.CONTEXT.md§Pad outline holds the names.Verify: Start
make dev-localand confirm the webapp port. Open a pad that has all six levels and body text. In the console, run[...document.querySelectorAll('.ProseMirror h1, .ProseMirror h2, .ProseMirror h3, .ProseMirror p')].map(n => [n.tagName, getComputedStyle(n).fontSize])and compare the values. Check light and dark themes.