/* * ContentEditor typography — ProseMirror styles using shadcn design tokens. * * 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. */ .rich-text-editor.ProseMirror { color: var(--foreground); caret-color: var(--foreground); min-height: 100%; } .rich-text-editor.ProseMirror:focus { outline: none; } /* Placeholder */ .rich-text-editor .is-editor-empty:first-child::before { content: attr(data-placeholder); float: left; color: var(--muted-foreground); pointer-events: none; height: 0; } /* 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; } /* 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; } /* Inline code */ .rich-text-editor code { font-family: var(--font-mono, ui-monospace, monospace); font-size: 0.875rem; background: color-mix(in srgb, var(--foreground) 3%, transparent); border: 1px solid color-mix(in srgb, var(--foreground) 5%, transparent); color: color-mix(in srgb, var(--foreground) 75%, transparent); padding: 0.125rem 0.375rem; border-radius: var(--radius-sm); box-decoration-break: clone; -webkit-box-decoration-break: clone; line-height: 2; } /* Code blocks */ .rich-text-editor pre { font-family: var(--font-mono, ui-monospace, monospace); background: var(--muted); border-radius: var(--radius); padding: 0.75rem 1rem; margin: 0.75rem 0; overflow-x: auto; } .rich-text-editor pre code { background: none; border: none; color: var(--foreground); padding: 0; font-size: 0.8125rem; line-height: 1.6; } /* Syntax highlighting — lowlight (hljs) */ .rich-text-editor .hljs-keyword, .rich-text-editor .hljs-selector-tag, .rich-text-editor .hljs-built_in { color: oklch(0.55 0.16 255); } .rich-text-editor .hljs-string, .rich-text-editor .hljs-addition { color: oklch(0.55 0.14 155); } .rich-text-editor .hljs-comment, .rich-text-editor .hljs-quote { color: var(--muted-foreground); font-style: italic; } .rich-text-editor .hljs-number, .rich-text-editor .hljs-literal { color: oklch(0.58 0.16 30); } .rich-text-editor .hljs-title, .rich-text-editor .hljs-section, .rich-text-editor .hljs-title\.function_ { color: oklch(0.55 0.14 280); } .rich-text-editor .hljs-attr, .rich-text-editor .hljs-attribute { color: oklch(0.58 0.12 60); } .rich-text-editor .hljs-variable, .rich-text-editor .hljs-template-variable { color: oklch(0.58 0.14 20); } .rich-text-editor .hljs-type, .rich-text-editor .hljs-title\.class_ { color: oklch(0.55 0.14 200); } .rich-text-editor .hljs-deletion { color: oklch(0.55 0.2 25); } .rich-text-editor .hljs-meta { color: var(--muted-foreground); } /* Dark mode overrides */ .dark .rich-text-editor .hljs-keyword, .dark .rich-text-editor .hljs-selector-tag, .dark .rich-text-editor .hljs-built_in { color: oklch(0.7 0.14 255); } .dark .rich-text-editor .hljs-string, .dark .rich-text-editor .hljs-addition { color: oklch(0.7 0.14 155); } .dark .rich-text-editor .hljs-number, .dark .rich-text-editor .hljs-literal { color: oklch(0.72 0.14 30); } .dark .rich-text-editor .hljs-title, .dark .rich-text-editor .hljs-section, .dark .rich-text-editor .hljs-title\.function_ { color: oklch(0.72 0.12 280); } .dark .rich-text-editor .hljs-attr, .dark .rich-text-editor .hljs-attribute { color: oklch(0.72 0.1 60); } .dark .rich-text-editor .hljs-variable, .dark .rich-text-editor .hljs-template-variable { color: oklch(0.72 0.12 20); } .dark .rich-text-editor .hljs-type, .dark .rich-text-editor .hljs-title\.class_ { color: oklch(0.72 0.12 200); } .dark .rich-text-editor .hljs-deletion { color: oklch(0.7 0.18 25); } /* Tables */ .rich-text-editor .tableWrapper { overflow-x: auto; margin: 1rem 0; border: 1px solid var(--border); border-radius: var(--radius); } .rich-text-editor table { min-width: 100%; border-collapse: collapse; } .rich-text-editor colgroup { display: none; } .rich-text-editor thead { background: color-mix(in srgb, var(--muted) 50%, transparent); } .rich-text-editor tbody tr { border-top: 1px solid var(--border); } .rich-text-editor tr:hover td { background: color-mix(in srgb, var(--muted) 30%, transparent); transition: background 0.15s; } .rich-text-editor th, .rich-text-editor td { text-align: left; padding: 0.625rem 1rem; font-size: 0.875rem; } .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; } /* 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); } /* Readonly mode overrides */ .rich-text-editor.readonly.ProseMirror { caret-color: transparent; cursor: default; } /* Mention NodeView inline layout fix */ .rich-text-editor [data-node-view-wrapper] { display: inline; vertical-align: middle; } /* Block-level NodeViews (fileCard) need to override the inline default above */ .rich-text-editor .file-card-node { display: block !important; } /* Images — generic fallback (non-NodeView contexts) */ .rich-text-editor img { max-width: 100%; height: auto; border-radius: var(--radius); margin: 0.5rem 0; } /* Image NodeView — centered block with max-width cap */ .rich-text-editor .image-node { display: block !important; text-align: center; } .rich-text-editor .image-figure { position: relative; display: inline-block; max-width: min(100%, 640px); margin: 0.75rem 0; } .rich-text-editor .image-figure.image-selected .image-content { outline: 2px solid var(--brand); outline-offset: 2px; } .rich-text-editor .image-content { display: block; width: 100%; height: auto; border-radius: var(--radius); } .rich-text-editor .image-uploading { opacity: 0.5; animation: rte-upload-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; } @keyframes rte-upload-pulse { 0%, 100% { opacity: 0.5; } 50% { opacity: 0.3; } } /* Readonly — zoom cursor on clickable images */ .rich-text-editor.readonly .image-figure { cursor: zoom-in; } /* Image toolbar — dark pill, top-right corner, appears on hover */ .rich-text-editor .image-toolbar { position: absolute; top: 0.5rem; right: 0.5rem; display: flex; gap: 1px; padding: 0.25rem; background: color-mix(in srgb, black 75%, transparent); backdrop-filter: blur(8px); border-radius: var(--radius); opacity: 0; transition: opacity 0.15s; z-index: 1; } .image-figure:hover .image-toolbar { opacity: 1; } .rich-text-editor .image-toolbar button { display: flex; align-items: center; justify-content: center; width: 1.75rem; height: 1.75rem; border-radius: calc(var(--radius) - 2px); color: white; transition: background 0.15s; } .rich-text-editor .image-toolbar button:hover { background: color-mix(in srgb, white 15%, transparent); } /* Bubble menu — floating toolbar pill */ .bubble-menu { display: flex; align-items: center; gap: 1px; padding: 0.25rem; background: var(--popover); border: 1px solid color-mix(in srgb, var(--foreground) 10%, transparent); border-radius: var(--radius); box-shadow: 0 4px 12px color-mix(in srgb, black 12%, transparent), 0 0 0 1px color-mix(in srgb, black 4%, transparent); } /* Link edit mode — inline URL input */ .bubble-menu-link-edit { display: flex; align-items: center; gap: 0.25rem; padding: 0.25rem; background: var(--popover); border: 1px solid color-mix(in srgb, var(--foreground) 10%, transparent); border-radius: var(--radius); box-shadow: 0 4px 12px color-mix(in srgb, black 12%, transparent), 0 0 0 1px color-mix(in srgb, black 4%, transparent); min-width: 300px; } /* Link hover card — shows URL + actions on link hover */ .link-hover-card { display: flex; align-items: center; gap: 0.25rem; padding: 0.25rem 0.25rem 0.25rem 0.5rem; background: var(--popover); border: 1px solid color-mix(in srgb, var(--foreground) 10%, transparent); border-radius: var(--radius); box-shadow: 0 4px 12px color-mix(in srgb, black 12%, transparent), 0 0 0 1px color-mix(in srgb, black 4%, transparent); max-width: min(360px, calc(100vw - 2rem)); white-space: nowrap; }