/* * Rich text prose typography. * * Design tier: "Compact" (same tier as Linear, Slack). Optimized for * short-form content (issue descriptions, comments) that users scan, not * long-form reading. * * Typography values benchmarked against (April 2026): * - github-markdown-css (GitHub's markdown renderer) * - @tailwindcss/typography prose-sm preset * - Linear's editor (Tiptap-based, 14px body) * * Key decisions: * Body: 14px (text-sm), line-height 1.625 (between GitHub 1.5 and Tailwind 1.714) * Headings: h1=22px (1.57x), h2=18px (1.29x), h3=15px (1.07x) — compact but * with clear hierarchy. Previous h3 was 14px (same as body = no differentiation). * Paragraph spacing: 10px (was 8px; GitHub uses 10px, Tailwind prose-sm uses 16px) * List indent: 20px for ul (was 16px; standard is 22-32px) * Code block margin: 12px (was 8px; gives breathing room between code and prose) * Blockquote border: 3px (was 2px; GitHub/Tailwind both use 4px) * Links: var(--brand) blue with 40% opacity underline (was var(--primary) near-black) * * Inline elements (mention cards, inline code) that exceed line-height: * The browser auto-expands the line box for lines containing taller inline * elements. Controlled via vertical-align on [data-node-view-wrapper] and * box-decoration-break: clone on inline code. */ /* Long unbreakable tokens (paths, URLs, identifiers) wrap mid-word instead of overflowing. Tiptap injects the legacy alias `word-wrap: break-word` on .ProseMirror, so the editable render already behaves this way; without this rule the readonly render keeps such tokens on one line and the two renders wrap the same text differently (machine-diffed: inline code and code blocks gained 1-2 lines in the editor only). */ .rich-text-editor { overflow-wrap: break-word; } /* Headings — compact but with clear visual hierarchy */ .rich-text-editor h1 { font-size: 1.375rem; font-weight: 700; margin-top: 1.5rem; margin-bottom: 0.5rem; line-height: 1.3; letter-spacing: -0.01em; } .rich-text-editor h2 { font-size: 1.125rem; font-weight: 600; margin-top: 1.5rem; margin-bottom: 0.5rem; line-height: 1.35; } .rich-text-editor h3 { font-size: 0.9375rem; font-weight: 600; margin-top: 1rem; margin-bottom: 0.5rem; line-height: 1.4; } /* Paragraphs */ .rich-text-editor p { margin-top: 0.625rem; margin-bottom: 0.625rem; line-height: 1.625; } /* First child should not have top margin */ .rich-text-editor > *:first-child { margin-top: 0; } /* Last child should not have bottom margin */ .rich-text-editor > *:last-child { margin-bottom: 0; } /* Lists */ .rich-text-editor ul { list-style-type: disc; padding-inline-start: 1.25rem; padding-inline-end: 0.5rem; margin: 0.5rem 0; } .rich-text-editor ol { list-style-type: decimal; padding-inline-start: 1.5rem; margin: 0.5rem 0; } .rich-text-editor li { margin: 0.25rem 0; line-height: 1.625; } .rich-text-editor li + li { margin-top: 0.25rem; } .rich-text-editor li::marker { color: var(--muted-foreground); } /* Remove paragraph margins inside list items (Tiptap wraps li content in
) */ .rich-text-editor li > p { margin: 0; } .rich-text-editor li > p + p { margin-top: 0.25rem; } .rich-text-editor .math-node { display: inline-flex; max-width: 100%; vertical-align: middle; } .rich-text-editor .math-node.inline { align-items: center; } .rich-text-editor .math-node.block { display: block; margin: 0.75rem 0; overflow-x: auto; overflow-y: hidden; } .rich-text-editor .math-node.block .katex-display { margin: 0; } .rich-text-editor .math-node .katex { max-width: 100%; } /* Nested lists — bullet style progression and tighter spacing */ .rich-text-editor ul ul { list-style-type: circle; margin: 0.25rem 0; } .rich-text-editor ul ul ul { list-style-type: square; } .rich-text-editor ol ol { list-style-type: lower-alpha; margin: 0.25rem 0; } .rich-text-editor ol ol ol { list-style-type: lower-roman; } /* * Task lists (checkboxes) — `- [ ]` / `- [x]`. * * The editor and readonly views emit DIFFERENT DOM, so they are styled * separately rather than sharing a flex rule: * * - Editor (Tiptap NodeView): * ul[data-type="taskList"] > li[data-checked] * > label(contenteditable) > input + span * > div(content) > p, [nested ul] * The item body is wrapped in a
(which carries 1.625); the readonly render keeps bare text in the cell, which otherwise inherits the host's text-sm line-height (~1.43) and makes every readonly cell 2.5px shorter per line. */ line-height: 1.625; } .rich-text-editor th { font-weight: 600; } /* Remove paragraph margin inside table cells */ .rich-text-editor th p, .rich-text-editor td p { margin: 0; } /* Blockquotes */ .rich-text-editor blockquote { border-left: 3px solid color-mix(in srgb, var(--muted-foreground) 30%, transparent); padding-left: 0.75rem; margin: 0.625rem 0; color: var(--muted-foreground); font-style: italic; } .rich-text-editor blockquote p { margin-top: 0.25rem; margin-bottom: 0.25rem; } .rich-text-editor blockquote > *:first-child { margin-top: 0; } .rich-text-editor blockquote > *:last-child { margin-bottom: 0; } .rich-text-editor blockquote blockquote { margin-top: 0.25rem; margin-bottom: 0.25rem; border-left-color: color-mix(in srgb, var(--muted-foreground) 15%, transparent); } /* Horizontal rules */ .rich-text-editor hr { border: none; border-top: 1px solid var(--border); margin: 1rem 0; } /* Links */ .rich-text-editor a { color: var(--brand); text-decoration: underline; text-decoration-color: color-mix(in srgb, var(--brand) 40%, transparent); text-underline-offset: 2px; cursor: pointer; } .rich-text-editor a:hover { text-decoration-color: var(--brand); } /* Issue mention cards — inline cards that sit within text flow */ .rich-text-editor a.issue-mention { color: inherit; text-decoration: none; } .rich-text-editor a.issue-mention:hover { text-decoration: none; } /* Mentions */ .rich-text-editor .mention { color: var(--primary); font-weight: 600; text-decoration: none; margin: 0 0.125rem; } /* Slash command pills */ .rich-text-editor .slash-command { color: var(--primary); font-weight: 600; text-decoration: none; margin: 0 0.125rem; } /* Strong / emphasis */ .rich-text-editor strong { font-weight: 600; } .rich-text-editor em { font-style: italic; } .rich-text-editor s, .rich-text-editor del { text-decoration: line-through; color: var(--muted-foreground); } /* Text highlight (`==text==` → ). Single yellow tint that stays legible in both light and dark themes; `color: inherit` keeps the underlying text color so highlighted links/code remain readable. box-decoration-break clones the background across wrapped lines (same approach as inline code above). */ .rich-text-editor mark { background-color: color-mix(in srgb, #facc15 45%, transparent); color: inherit; border-radius: 0.2em; padding: 0 0.15em; box-decoration-break: clone; -webkit-box-decoration-break: clone; }