Skip to content

Match descendant rules naming a table or cell against the content of the cell (#223) - #507

Open
jakejackson1 wants to merge 1 commit into
gravitypdffrom
fix/223-descendant-selectors-in-table-cells
Open

jakejackson1 wants to merge 1 commit into
gravitypdffrom
fix/223-descendant-selectors-in-table-cells

Conversation

@jakejackson1

@jakejackson1 jakejackson1 commented Sep 24, 2026 •

Copy link
Copy Markdown
Member

What was wrong

A descendant rule that names a table, row or cell never matched anything inside a cell. td img { max-width: 20mm }, table tr td img { … }, td span { color: red }, td p { … } and .cell .thumb { … } were all ignored, while img { … } and div img { … } (with the div outside the table) applied. This is the second half of #223 and mpdf#420. It is broader than images: every element in a cell was affected.

Cause

Cell content pushes no block level, so when CssMerger::mergeDescendantSelectors() merged an inline element, or a block element in a cell (which is merged with an empty $inherit), it looked in blk[blklvl]['cascadeCSS']. That is the block outside the table, and it only holds the rules left to match from there. The rules left to match inside the cell are in CssManager::$tablecascadeCSS[$tbCSSlvl], which <table>, <tr> and <td> build and which nothing inside the cell read.

Fix

Inside a table, non-table elements take their descendant rules from the current cell's cascade. That cascade is seeded from the enclosing block when the top-level table opens, so rules like div img, with the div outside the table, still apply.

Chains inside the cell itself, such as td p span, still don't match, because a block in a cell pushes no cascade level of its own. That limit applies to inline chains everywhere in mPDF, not just in cells, and is out of scope here.

Snapshot

image-border-radius changes. Its stylesheet has table.grid td p { margin: 3mm 0 0 0; font-size: 8pt; color: #606060; } for the captions under each picture, and that rule now applies: the captions are 8pt grey with a 3mm top margin, as the fixture intended. The fixture was regenerated with composer snapshot:update image-border-radius. No other snapshot changed.

Tests

tests/Mpdf/TableCellDescendantSelectorTest.php checks that tag chains, class chains, a cell class and an outer block all reach an image in a cell, and that a rule for a cell of another class does not. It also covers a table nested in the cell, and td span and td p colouring text. Seven of the nine cases fail on gravitypdf.

Split from #505, which fixes what a percentage on such an image is resolved against. Together they let td img { max-width: 100% } keep pictures inside their cells.

Closes #223 (together with #505).

🤖 Generated with Claude Code

…the cell (mpdf#420)

Content of a table cell pushes no block level, so its elements looked for
descendant rules in the block outside the table, and a rule such as
"td img", "table tr td span" or ".cell p" never reached them. They now take
their descendant rules from the cell's cascade, which already carries the
rules of the blocks outside the table as well.

The image-border-radius snapshot changes because its "table.grid td p" caption
rule now applies.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A percentage width or max-width on an image in a table cell is resolved against the block containing the table, not the cell

1 participant