diff --git a/app/assets/javascripts/content_editor/extensions/html_marks.js b/app/assets/javascripts/content_editor/extensions/html_marks.js
index 79fc0eea2c788541fe6dcc2efea382464e6db0f1..58fa2655e250ba8ecba29d1c51c4b4b467a2c88e 100644
--- a/app/assets/javascripts/content_editor/extensions/html_marks.js
+++ b/app/assets/javascripts/content_editor/extensions/html_marks.js
@@ -50,7 +50,8 @@ export default marks.map((name) =>
     },
 
     parseHTML() {
-      return [{ tag: name, priority: PARSE_HTML_PRIORITY_LOWEST }];
+      const tag = name === 'span' ? `${name}:not([data-escaped-char])` : name;
+      return [{ tag, priority: PARSE_HTML_PRIORITY_LOWEST }];
     },
 
     renderHTML({ HTMLAttributes }) {
diff --git a/spec/frontend/content_editor/extensions/html_marks_spec.js b/spec/frontend/content_editor/extensions/html_marks_spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..3757962ce5210a2b247fbb4211d573db807f3e5b
--- /dev/null
+++ b/spec/frontend/content_editor/extensions/html_marks_spec.js
@@ -0,0 +1,89 @@
+import HTMLMarks from '~/content_editor/extensions/html_marks';
+import { createTestEditor, createDocBuilder } from '../test_utils';
+
+describe('content_editor/extensions/html_marks', () => {
+  let tiptapEditor;
+  let doc;
+  let ins;
+  let abbr;
+  let bdo;
+  let cite;
+  let dfn;
+  let small;
+  let span;
+  let time;
+  let kbd;
+  let q;
+  let p;
+  let samp;
+  let varMark;
+  let ruby;
+  let rp;
+  let rt;
+
+  beforeEach(() => {
+    tiptapEditor = createTestEditor({ extensions: [...HTMLMarks] });
+
+    ({
+      builders: {
+        doc,
+        ins,
+        abbr,
+        bdo,
+        cite,
+        dfn,
+        small,
+        span,
+        time,
+        kbd,
+        q,
+        samp,
+        var: varMark,
+        ruby,
+        rp,
+        rt,
+        p,
+      },
+    } = createDocBuilder({
+      tiptapEditor,
+      names: {
+        ...HTMLMarks.reduce(
+          (builders, htmlMark) => ({
+            ...builders,
+            [htmlMark.name]: { markType: htmlMark.name },
+          }),
+          {},
+        ),
+      },
+    }));
+  });
+
+  it.each`
+    input                                                    | expectedContent
+    ${'<ins>inserted</ins>'}                                 | ${() => ins('inserted')}
+    ${'<abbr title="abbr">abbreviation</abbr>'}              | ${() => abbr({ title: 'abbr' }, 'abbreviation')}
+    ${'<bdo dir="rtl">bdo</bdo>'}                            | ${() => bdo({ dir: 'rtl' }, 'bdo')}
+    ${'<cite>citation</cite>'}                               | ${() => cite('citation')}
+    ${'<dfn>definition</dfn>'}                               | ${() => dfn('definition')}
+    ${'<small>small text</small>'}                           | ${() => small('small text')}
+    ${'<span dir="rtl">span text</span>'}                    | ${() => span({ dir: 'rtl' }, 'span text')}
+    ${'<time datetime="2023-11-02">November 2, 2023</time>'} | ${() => time({ datetime: '2023-11-02' }, 'November 2, 2023')}
+    ${'<kbd>keyboard</kbd>'}                                 | ${() => kbd('keyboard')}
+    ${'<q>quote</q>'}                                        | ${() => q('quote')}
+    ${'<samp>sample</samp>'}                                 | ${() => samp('sample')}
+    ${'<var>variable</var>'}                                 | ${() => varMark('variable')}
+    ${'<ruby>base<rp>(</rp><rt>ruby</rt><rp>)</rp></ruby>'}  | ${() => ruby('base', rp('('), rt('ruby'), rp(')'))}
+  `('parses and creates marks for $input', ({ input, expectedContent }) => {
+    tiptapEditor.commands.setContent(input);
+    expect(tiptapEditor.getJSON()).toEqual(doc(p(expectedContent())).toJSON());
+    expect(tiptapEditor.getHTML()).toContain(input);
+  });
+
+  it('does not parse an element with a data-escaped-char attribute', () => {
+    const input = '<span data-escaped-char>#</span> not a heading';
+    const expectedDoc = doc(p('# not a heading'));
+    tiptapEditor.commands.setContent(input);
+    expect(tiptapEditor.getJSON()).toEqual(expectedDoc.toJSON());
+    expect(tiptapEditor.getHTML()).not.toContain('<span');
+  });
+});