diff --git a/libs/web/tracker/presenters/src/lib/ui/markdown.spec.ts b/libs/web/tracker/presenters/src/lib/ui/markdown.spec.ts
index 30cb859..e75cb89 100644
--- a/libs/web/tracker/presenters/src/lib/ui/markdown.spec.ts
+++ b/libs/web/tracker/presenters/src/lib/ui/markdown.spec.ts
@@ -43,7 +43,17 @@ describe('renderMarkdown', () => {
it('shows task lists as ticked boxes nobody can click', () => {
const html = renderMarkdown('- [x] Reproducido\n- [ ] Arreglado');
- expect(html).toContain('');
- expect(html).toContain('');
+ const boxes = Array.from(
+ new DOMParser()
+ .parseFromString(html, 'text/html')
+ .querySelectorAll('input[type="checkbox"]'),
+ );
+ expect(boxes.map((box) => box.checked)).toEqual([true, false]);
+ // Not `disabled` (browsers grey those out): out of reach instead.
+ for (const box of boxes) {
+ expect(box.disabled).toBe(false);
+ expect(box.getAttribute('aria-disabled')).toBe('true');
+ expect(box.tabIndex).toBe(-1);
+ }
});
});
diff --git a/libs/web/tracker/presenters/src/lib/ui/markdown.ts b/libs/web/tracker/presenters/src/lib/ui/markdown.ts
index 4f8a80c..13b4fb1 100644
--- a/libs/web/tracker/presenters/src/lib/ui/markdown.ts
+++ b/libs/web/tracker/presenters/src/lib/ui/markdown.ts
@@ -67,9 +67,14 @@ function installHooks(): void {
}
}
if (node.tagName === 'INPUT') {
- // Task lists: `- [x] done` renders as a ticked, read-only box.
+ // Task lists: `- [x] done` renders as a ticked box nobody can change.
+ // Not `disabled`: browsers grey those out; CSS takes the clicks away.
if (node.getAttribute('type') !== 'checkbox') node.remove();
- else node.setAttribute('disabled', '');
+ else {
+ node.removeAttribute('disabled');
+ node.setAttribute('aria-disabled', 'true');
+ node.setAttribute('tabindex', '-1');
+ }
}
const cls = node.getAttribute('class');
if (cls && !/^ts-mention( is-me)?$/.test(cls))
@@ -117,7 +122,8 @@ export function renderMarkdown(text: string, me: string | null = null): string {
'data-user',
'type',
'checked',
- 'disabled',
+ 'aria-disabled',
+ 'tabindex',
],
// DOMPurify's own rule plus `attachment:`: links and images to http(s),
// mail or a pasted file; plain values (`type="checkbox"`) still pass.
@@ -179,10 +185,20 @@ export const MARKDOWN_STYLES = `
margin: 0;
}
}
+ /* Task items: the box takes the bullet's place, so their text lines
+ up with the other items' (as in the editor). */
li:has(> input[type='checkbox']),
li:has(> p:first-child > input[type='checkbox']) {
list-style: none;
- margin-left: -1.375rem;
+ }
+ li > input[type='checkbox'],
+ li > p:first-child > input[type='checkbox'] {
+ width: 0.875rem;
+ height: 0.875rem;
+ margin: 0 0.25rem 0 -1.375rem;
+ vertical-align: -0.125em;
+ accent-color: var(--ts-brand);
+ pointer-events: none;
}
a {
color: var(--ts-brand);