Why Word markup in a description holds products back
A description is meant to be text wrapped in a few tags. What a word processor pastes in is text buried under markup that no browser needs and no editor wrote — and it stays there for years, because it is invisible in the visual editor and only shows up when something looks slightly off.
-
The theme loses control of the typography. Inline
font-familyandfont-sizedeclarations override your stylesheet, so one product renders in Calibri at 11pt while its neighbours use the store's own type. Dark-mode themes and mobile breakpoints break in the same way. -
Pages get heavier for nothing. A short paragraph
can carry several kilobytes of
mso-declarations,<o:p>tags and comment blocks. That is weight on every product page and every category listing that prints the short description. -
Empty paragraphs push the content down. Runs of
<p> </p>open gaps between the heading, the price and the actual copy — the reader scrolls past white space to find out what the product is. - Feeds and exports carry it along. Product feeds, marketplace listings and CSV exports take the description as it is stored, debris included, and downstream systems are far less forgiving of it than a browser.
How to find HTML debris in descriptions manually
Descriptions live in wp_posts: the long description in
post_content and the short one in
post_excerpt. This WP-CLI query lists published products
where either field carries the commonest Word leftovers:
wp db query "SELECT ID, post_title
FROM wp_posts
WHERE post_type = 'product' AND post_status = 'publish'
AND (
post_content LIKE '%class=\"Mso%' OR post_excerpt LIKE '%class=\"Mso%'
OR post_content LIKE '%<o:p>%' OR post_excerpt LIKE '%<o:p>%'
OR post_content LIKE '%mso-%' OR post_excerpt LIKE '%mso-%'
OR post_content LIKE '%<!--[if%' OR post_excerpt LIKE '%<!--[if%'
OR post_content LIKE '%<p> </p>%' OR post_excerpt LIKE '%<p> </p>%'
)
LIMIT 50;"
Adjust the table prefix if yours isn't wp_. Add
post_status = 'draft' or
'private' to the filter if you want unpublished products
too.
The list is the easy part. The query only knows the patterns you typed
— an empty paragraph written as <p><br></p>
or an <o:p> tag with attributes slips past it — and
it cannot tell you what to remove without breaking the page. Then
someone opens each product, switches the editor to code view, and cuts
by hand, hoping the table width or the store's own class names survive
the pass. Next quarter's supplier import brings the same paste back.
How CatalogLift finds and cleans it
Every scan reads the description and the short description of every
synced product and looks for a mark that only a word processor leaves:
an Mso class name, an Office-only tag such as
<o:p>, a conditional comment, a style declaration
naming an mso- property or Word's tab-stops —
or a paragraph that holds nothing but whitespace, a non-breaking space
or a line break. Fields that are empty are skipped, and shortcodes are
left alone. Each match becomes a low-severity finding for that one
field, with the pattern it saw and a preview of the current value as
evidence, so the description and the short description of one product
are two findings rather than one blur.
The fix is computed, not written. There is no AI in this check: CatalogLift derives the cleaned value from the same rule that raised the finding — Word tags, comments and Mso class tokens removed, empty paragraphs dropped, and inline typography stripped only where a Word marker proves it was a paste rather than your choice. Layout properties and unrecognized styles stay. You see the before and after as a diff, approve it, and nothing reaches WooCommerce before that decision; nothing in your store changes before that. The next scan confirms the debris is gone, and because CatalogLift keeps watching, the next paste from Word becomes a fresh finding instead of a silent regression.