Show helper lines when cells have border none
π Provide a description of the improvement
Currently helper lines are displayed only on the whole table:
This is a request to add them on cells as well.
If you'd like to see this improvement implemented, add a π reaction to this post.
I pushed a PoC to https://github.com/ckeditor/ckeditor5/compare/ck/19039-show-helper-lines?expand=1
To me, it does the job (note, I used red dashed border as a helper line for this PoC):
@niegowski @pszczesniak Do you know why we went with outline instead of a border? And covered only the table? Border seems to work fine.
Also, why is there no layout tables in the all features test? Is it on purpose?
@niegowski pointed out to me that those helper lines would then don't display if a table has a border but with a width set to 0px π
However, AFAICS, that's the only scenario where the border-based solution falls short. It's actually good that the helper border is not displayed in this case, because that border would affect the layout.
One more scenario where I was worried we may break the layout is border-style: none; border-width: 5px. CKEditor would not produce such incoherent output, but it may be loaded or pasted into the editor. Fortunately, when border-style is set to none, the width does not have any effect:
And that looks correct in the editor too:
Proposal
So... I wonder if we should not create a combined solution:
- use border (as in my PoC) for most cases β especially for the "set table border to none via table's UI" β it gives best results IMO
- use outline as backup for a weirdly styled tables
We were experimenting with outlines (https://github.com/ckeditor/ckeditor5/commit/7f2893641c7316366a9b87781d75d43c3f7d2264), but it is not possible to set outline only on selected sides, so it overlapped existing borders. We did not want to use borders as those take space in the content, and the editing view would differ from the data view, which affects pagination.
Another proposal
Make the table cell position: relative, then add a pseudo-element that is positioned absolute and fills the entire cell. Set pointer-events: none on this element. With this approach, we can easily apply borders to each side of the cell without affecting the layout.
I'm testing the direction proposed by @pszczesniak. It looks promising. Though, I'm unsure about some things.
β οΈ Note: I started with the table border first.
So, the positioned :before pseudoelement renders on top of the cells. Meaning that it covers the cells' borders. And similarly, cells without borders will affect cells with borders this way.
Unfortunately, I don't think it can be safely pushed back, to render below the table and its cells. It would also be susceptible to table and cells having a background color.
This is how it looks now (red is the help guide; I used 3px for text to see if things position well relative to each other):
π This is Chrome (Firefox looks good too):
π And this is Safari π:
How does it change with more realistic border? I tested a semi-transparent one so even when it renders above the cell's border it does not fully cover it.
π Chrome
π Firefox
π Safari
Not bad, but unfortunately this border is visible over the styled cells. Pity, because it may be confusing. But just for the reference, this is how it looks on master:
So, perhaps it's still an improvement?
I added a border for cells too and unfortunately it surfaces a positioning problem I was afraid of:
π Chrome
π Firefox
π Safari
It looks good only in Safari. In Chrome and Firefox the cells that have different border-width are positioned on non-zero offset from the table. I don't see a way to account for this.
This is the implementation, for reference: https://github.com/ckeditor/ckeditor5/commit/6efb0680ddddb1a29ebd9134da1ed2fa59ac60de
& table[style*="border:none"]:before,
& table[style*="border-style:none"]:before,
& td[style*="border:none"]:before,
& td[style*="border-style:none"]:before,
& th[style*="border:none"]:before,
& th[style*="border-style:none"]:before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
pointer-events: none;
box-sizing: border-box;
border: dashed 1px rgba(0, 0, 0, 0.1) !important;
}
So, for now:
- The first approach: Use
borderof the table/cells in question:- Pros:
- The borders will be positioned and combined between cells/table exactly where and how they'd normally be. No issues like https://github.com/ckeditor/ckeditor5/issues/19039#issuecomment-3311363633
- Minimal impact on potential 3rd party / integrator's stylesheet. Lowest chance for a conflict.
- Cons:
- Might affect Pagination. I'd say it's the same impact as bookmarks have β both (guide borders and bookmarks) can be hidden in pagination. Or, we could even hide guide borders completely when Pagination is turned on.
- Might slightly change the look of a table. I was curious to see impact on layouts such as in the Email editing demos, but I realized that layourt tables have their own outlines
- Pros:
- The second approach: Use pseudoelement:
- Pros:
- No impact on the layout.
- Cons:
- Might conflict with someone's (even our's in the future) use of
td/table:before. - Non-native to table layouts so may not position correctly.
- Might conflict with someone's (even our's in the future) use of
- Pros:
I'd lean towards having this behaviour (showing guide borders) as an option. Probably, turned on by default. And having pagination turn it off or at least ignore when calculating pages.
The whole idea of using a pseudo-element was that it does not affect the layout in the same way as outline, and it can be set on specific sides of the box, not on all sides like outline.
I wanted to check one more time the approach with outline directly on table/td, but:
So, I'd go with the borders.
Initially, with a config option to disable them.
In the future, we could have a user-facing option for toggling visual guides in the menu bar.
The whole idea of using a pseudo-element was that it does not affect the layout in the same way as outline, and it can be set on specific sides of the box, not on all sides like outline.
How would you set it on specific sides? I can't imagine preventing issues like these ones: https://private-user-images.githubusercontent.com/156149/491488112-e6c865b0-3dfe-4b40-a9ff-84356b1408b8.png?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NTgyNzk0MzAsIm5iZiI6MTc1ODI3OTEzMCwicGF0aCI6Ii8xNTYxNDkvNDkxNDg4MTEyLWU2Yzg2NWIwLTNkZmUtNGI0MC1hOWZmLTg0MzU2YjE0MDhiOC5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjUwOTE5JTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI1MDkxOVQxMDUyMTBaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT04NzVlNTRiZTYzMzlkNjFkZDNiNzY1NGEyODU3ZmQ0ZTY5ZGUwYWNlNGE2MWZhYWU1MDUxNzJiYTExMDVlOTg3JlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCJ9.m8QUr5itKOovehnOKUzXYSNkHQvfjRjx7ilLTolYJYA
Might be worth looking into when styling such helper UI elements: https://developer.mozilla.org/en-US/docs/Web/CSS/:where
Might be worth looking into when styling such helper UI elements: https://developer.mozilla.org/en-US/docs/Web/CSS/:where
Turned out that CSS ecosystem is not yet ready to handle :where correctly. We've run into issues with PurgeCSS's lack of support for this selector which was breaking the output styles.
Technical Summary
Based on the research conducted around CKEditor 5βs table border behaviour, the most reliable method for visualizing βhiddenβ borders (border: none) is to reapply a dashed border via CSS whenever the editor detects table elements whose inline styles explicitly remove borders.
When the initial data contains tables or cells styled with border:none or border-*-style:none, CKEditor keeps these styles in the editing view. Because these inline styles originate from the source data, they override normal CSS rules. This creates a challenge when attempting to visually indicate hidden borders inside the editor UI.
To achieve consistent overriding with minimal side effects, the implementation uses the :where() pseudo-class. This keeps all selectors at zero specificity, preventing conflicts with CKEditorβs own UI styles and ensuring that the editorβs widget system continues to function correctly.
Even with :where(), inline styles still carry the highest priority. Therefore, to display dashed preview borders in the editing view, the custom CSS must use !important. This makes it possible for the editor to visually mark elements that originally declared border:none or any side-specific border-*-style:none, without altering the underlying document data.
The CSS relies on attribute selectors (e.g., [style*="border:none"]) to detect both:
- general border removal (
border:none), and - side-specific removal (
border-top-style:none,border-left-style:none, etc.).
This ensures that the visual preview in the editor is consistent with the actual border configuration defined in the source HTML.
To make the solution more flexible for different workflows, this functionality should be wrapped in a configuration option (e.g., table: { showHiddenBorders: true }). This allows integrators to disable the dashed-border visualization for use cases such as:
- strict WYSIWYG scenarios where hidden borders should not be visually indicated,
- custom themes or CSS frameworks that provide their own border-visibility logic,
- minimalistic or distraction-free editing modes.
With this option, developers can tailor the editor to either provide visual guidance (dashed borders for hidden edges) or remain completely faithful to the original HTML styling.
The proposed CSS implementation uses dashed borders with the custom color variable --ck-table-border-none-helper-line-color, offering a clear editing aid without modifying the real HTML output.
Conclusion
Using pseudo-elements to visualize hidden borders in CKEditor 5 tables:
β does not align with table layout rules β cannot integrate with border collapsing β is fragile and hard to maintain β requires heavy CSS hacks
Using real CSS borders (overriding inline styles with !important) is:
β layout-correct β robust even with complex tables β consistent with browser rendering β maintainable
This is why the dashed-border override is the preferred solution.
PoC Implementation
Here is the commit from my research π https://github.com/ckeditor/ckeditor5/commit/7662f1329a40ff9809ccd9e6c91f7d331737bdfa
We need to check how it will work with layout tables.
Build systen
Also i've checked the PurgeCSS with top level :where and it works like it should. Styles are preserved during splitting to editor and content styles without any hacks.