diff --git a/docs/architecture/tables.md b/docs/architecture/tables.md
index 8bdc4df..ed4b903 100644
--- a/docs/architecture/tables.md
+++ b/docs/architecture/tables.md
@@ -88,3 +88,15 @@ We deliberately keep TipTap's `resizable: false` (so its own columnResizing plug
schematic text line (`previewTextCss`: thickness = bold, slant = italic, colour = the region's):
without it an emphasis-only area such as first/last column or the total row is **invisible** in
a tile, since the tiles hold no real text — only fills and borders would show.
+
+## Cell metrics measured against LibreOffice
+
+- **No space above a cell's first block.** LibreOffice adds none there (its
+ `AddParaTableSpacingAtStart`); Word does, so `editor.css` drops it for the `add` spacing
+ model only. Measured on the Math Guide: the table row grew 1mm per row without it. `pageBreaks.ts`
+ excludes cell leaves from its page-top-drop detection, or that missing padding would read
+ as a decoration to add back.
+- **`
` carries no UA defaults.** A repeating ODF header row (`table:table-header-rows`)
+ becomes a `tableHeader`, and the browser centres and bolds it — neither Word nor LibreOffice
+ does. `editor.css` resets both, so the cell follows its own style (the "header row" preset's
+ bold still comes from `.cell-header`).
diff --git a/src/lib/editor/extensions/pageBreaks.ts b/src/lib/editor/extensions/pageBreaks.ts
index f12a48d..94639e4 100644
--- a/src/lib/editor/extensions/pageBreaks.ts
+++ b/src/lib/editor/extensions/pageBreaks.ts
@@ -807,8 +807,10 @@ export const PageBreaks = Extension.create({
const cs = getComputedStyle(child);
// The block's own space above, and how much of it the last pass took off
// at a page top (the decoration zeroes both padding and margin).
+ // A cell's first block never carries it (LibreOffice adds none there), so
+ // the missing padding is the document, not a decoration to add back.
const spaceAbove = parseFloat(cs.getPropertyValue('--space-before')) || 0;
- const dropped = spaceAbove > 0.5
+ const dropped = spaceAbove > 0.5 && !inTableCell
&& parseFloat(cs.paddingTop) < 0.5 && parseFloat(cs.marginTop) < 0.5
? spaceAbove : 0;
leaves.push({
diff --git a/src/styles/editor.css b/src/styles/editor.css
index 24ad546..3efab67 100644
--- a/src/styles/editor.css
+++ b/src/styles/editor.css
@@ -487,6 +487,12 @@
padding-top: 0;
margin-top: var(--space-before);
}
+/* LibreOffice adds no space above the first block of a table cell (measured; its
+ AddParaTableSpacingAtStart), Word does — so it follows the document's spacing model. */
+.paper:not([data-spacing-model='max']) .tiptap :is(td, th) > :first-child {
+ padding-top: 0;
+ margin-top: 0;
+}
/* The line box: the paragraph's spacing factor times its font's natural line height.
Either half may come from the style rule or, nearer, from the block's own attrs
@@ -636,6 +642,10 @@
border: 1px solid #000;
padding: var(--cell-pad, 0 0.19cm);
vertical-align: top;
+ /* A repeating header row is a | , and no word processor centres or bolds its text
+ for that reason — drop the two UA defaults so the cell follows its own style. */
+ text-align: inherit;
+ font-weight: inherit;
/* Positioning context for the column-resize handle widget (a child div the
resize plugin places at the cell's right edge). */
position: relative;
|