Related
No open issue asks for this change. The contrast rule is WCAG 2.2 Understanding SC 1.4.3 Contrast (Minimum).
What happened
Edward Saperia asked for two changes in chat on 2026-09-26.
He wrote: "issue: can you remove the last "> Write here" from this". The phrase "this" is the empty-block hint.
He also wrote: "also make it a bit lighter grey".
The line in his screenshot is House London #2: 26... > Submissions > Submission Title > Write here.
A pin emoji at the start belongs to the heading text.
A grey bar above the hint reads 100-250 word abstract goes here.
That bar is document text. This change leaves it alone.
Please drag the screenshot from that chat onto this issue.
What we expect
A top-level empty paragraph shows the heading chain only.
The example line becomes House London #2: 26... > Submissions > Submission Title.
The grey stays readable under WCAG 2.2 SC 1.4.3.
A lighter grey than today fails 4.5:1 on the light themes.
The maintainer picks the shade. The passing range is in Findings.
Steps to reproduce
- Open a pad you can edit.
- Add a few headings, then an empty paragraph under the last one.
- Put the caret in that empty paragraph.
- Read the grey empty-block hint. It ends with
> Write here.
Findings
Who owns the text. Read from code.
The webapp builds the trailing segment.
resolveTailSegment returns Write here from the constant PLACEHOLDER_TEXT.paragraph.
The pad calls buildBreadcrumbPlaceholder with scope: 'top-level' in TipTap.tsx.
The placeholder extension paints the string the callback returns.
It stores that string on data-placeholder. Its default is Write something ….
The pad replaces that default. The published package stays unchanged.
What the heading chain is for. Read from code.
The editor schema is flat (heading block*).
buildAncestorChain keeps the headings that sit above the caret.
That chain names the section the empty paragraph belongs to.
The trailing segment only names the block as a paragraph.
Keep the heading chain. Drop the trailing segment.
Heading N, Subtitle, and Enter document name use other branches. Leave them.
List, To-do, Quote, and Write code are the nested labels. Leave them.
The pad wires only top-level. all-blocks appears only in the unit test.
If a paragraph has no heading before it, dropping the tail leaves an empty string.
The extension then draws no hint. A normal pad starts with a heading, so a later paragraph still has a chain.
Where else the words appear. Read from code.
buildBreadcrumbPlaceholder.test.ts asserts Write here on several lines.
The chat composer uses Write a message... and Add a comment… in useTiptapEditor.ts.
No Cypress spec, README, or GIF asserts Write here.
Colour. Measured in headless Chromium with Playwright. Each theme used the hex values in globals.scss.
The page used color-mix(in oklch, var(--color-base-content) <mix>, transparent).
getComputedStyle supplied the colour. A 1px canvas composited it onto the sheet.
The ratio is the WCAG luminance of that pixel against --color-base-100.
Pad rule, read from code: --prose-placeholder-mix: 50% on .editorWrapper .ProseMirror in _blocks.scss.
The paint rule is .is-empty::before in _prose-mirror-body.scss.
The chat composer hardcodes a 40% mix on p.is-empty::before in _chat-editor.scss.
One pad variable colours every empty-block hint in the pad.
A change to it also changes Heading, Subtitle, List, Quote, and Write code.
The title hint uses --muted-foreground, a separate 50% mix in globals.scss.
Leave that token alone unless the maintainer says otherwise.
| Theme |
Resolved colour at 50% |
Composited on the sheet |
Ratio at 50% |
Ratio at 40% |
Lowest mix at 4.5:1 |
| docsplus |
oklch(0.207678 0.0398189 265.767 / 0.5) |
#878b94 on #ffffff |
3.414 |
2.554 |
60% (4.687) |
| docsplus-dark |
oklch(0.927576 0.00579101 none / 0.5) |
#7b7c84 on #0b1220 |
4.510 |
3.274 |
50% (4.510) |
| docsplus-dark-hc |
oklch(0.968254 0.00681902 none / 0.5) |
#84858f on #0f172a |
4.874 |
3.542 |
48% (4.541) |
| docsplus-graphite |
oklch(0.190957 0.0174849 none / 0.5) |
#8a8688 on #f9fafc |
3.439 |
2.572 |
59% (4.571) |
| docsplus-graphite-dark |
oklch(0.930605 0.00578615 none / 0.5) |
#7c797c on #0c0d10 |
4.518 |
3.266 |
50% (4.518) |
| docsplus-paper |
oklch(0.185189 0.0142021 none / 0.5) |
#898483 on #fbf8f4 |
3.485 |
2.595 |
58% (4.528) |
| docsplus-paper-dark |
oklch(0.92493 0.0104246 none / 0.5) |
#7f7978 on #110e0b |
4.497 |
3.263 |
51% (4.579) |
The Understanding page says SC 1.4.3 covers placeholder text.
It requires 4.5:1 for normal text and 3:1 for large text.
Large text means 18 point, or 14 point bold.
The empty paragraph has no font-size rule, so this hint needs 4.5:1.
The live font size in the app was read from code, not measured.
A lighter grey is a lower mix. More of the sheet shows through.
On the light themes, the current 50% mix is already under 4.5:1.
The 40% column is the lighter example. It is also the chat composer's mix.
No mix under 50% meets 4.5:1 on the light themes.
One mix that passes 4.5:1 on all seven themes is 60% or higher.
That mix is darker than the current 50%.
On docsplus, 45% is 2.956:1, so it also misses the 3:1 large-text bar.
New colours become root tokens in globals.scss. That is the design-system change protocol.
Do not write a new color-mix on a component.
Duplicates. Read from GitHub and from the repo.
Issue search and a discussion search found no matching request.
Issue #45 is a closed 2023 report about heading hint font size. It is a different change.
No changelog entry explains the tail.
The comment on resolveTailSegment says a heading tail shows the level the reader is about to write.
The paragraph tail sits in the same map. No ruling says that tail must stay.
Next step: drop the trailing segment in the webapp, and wait for a shade that still passes 4.5:1.
Acceptance criteria
Blocked by
None. The trailing-segment edit can start. The grey waits for a maintainer pick.
Agent brief
Type: AFK for the trailing segment and its test. HITL for the grey. The maintainer picks the shade before any colour edit.
Category: enhancement
Current behavior: A top-level empty paragraph shows the heading chain, then > Write here. The pad paints that hint at a 50% mix of --color-base-content. On the light themes that mix is under 4.5:1.
Desired behavior: The heading chain stays. The trailing segment is gone. The grey meets 4.5:1 in every theme. The maintainer chooses the shade inside the passing range.
Where to start: apps/webapp/src/components/TipTap/placeholders/buildBreadcrumbPlaceholder.ts (PLACEHOLDER_TEXT, resolveTailSegment, buildBreadcrumbPlaceholder). apps/webapp/src/components/TipTap/TipTap.tsx (Placeholder.configure). apps/webapp/src/styles/_blocks.scss (--prose-placeholder-mix). apps/webapp/src/styles/_prose-mirror-body.scss (.is-empty::before). apps/webapp/src/styles/_chat-editor.scss (leave the 40% rule). apps/webapp/src/styles/globals.scss (a new token, and --muted-foreground). extensions/extension-placeholder/src/placeholder.ts (Placeholder) is read only. apps/webapp/src/components/TipTap/placeholders/buildBreadcrumbPlaceholder.test.ts. Search by symbol. File names are hints as of 2026-09-29.
Rules that apply: .cursor/docs/design-system.md Change protocol. New colours become root tokens in globals.scss. apps/webapp/src/components/TipTap/CLAUDE.md §Editor Performance. Keep @docs.plus/extension-placeholder. Do not switch to the built-in placeholder. AGENTS.md §Test Policy allows this test because it pins a reported request. Update the prose row in the design-system catalog if the mix changes.
Verify: Open a pad and put the caret in an empty paragraph under headings. The hint has no Write here. The heading chain is unchanged. If the grey changes, check all seven themes against the table. Run only buildBreadcrumbPlaceholder.test.ts. Do not run the whole webapp Jest suite.
Related
No open issue asks for this change. The contrast rule is WCAG 2.2 Understanding SC 1.4.3 Contrast (Minimum).
What happened
Edward Saperia asked for two changes in chat on 2026-09-26.
He wrote: "issue: can you remove the last "> Write here" from this". The phrase "this" is the empty-block hint.
He also wrote: "also make it a bit lighter grey".
The line in his screenshot is
House London #2: 26... > Submissions > Submission Title > Write here.A pin emoji at the start belongs to the heading text.
A grey bar above the hint reads
100-250 word abstract goes here.That bar is document text. This change leaves it alone.
Please drag the screenshot from that chat onto this issue.
What we expect
A top-level empty paragraph shows the heading chain only.
The example line becomes
House London #2: 26... > Submissions > Submission Title.The grey stays readable under WCAG 2.2 SC 1.4.3.
A lighter grey than today fails 4.5:1 on the light themes.
The maintainer picks the shade. The passing range is in Findings.
Steps to reproduce
> Write here.Findings
Who owns the text. Read from code.
The webapp builds the trailing segment.
resolveTailSegmentreturnsWrite herefrom the constantPLACEHOLDER_TEXT.paragraph.The pad calls
buildBreadcrumbPlaceholderwithscope: 'top-level'inTipTap.tsx.The placeholder extension paints the string the callback returns.
It stores that string on
data-placeholder. Its default isWrite something ….The pad replaces that default. The published package stays unchanged.
What the heading chain is for. Read from code.
The editor schema is flat (
heading block*).buildAncestorChainkeeps the headings that sit above the caret.That chain names the section the empty paragraph belongs to.
The trailing segment only names the block as a paragraph.
Keep the heading chain. Drop the trailing segment.
Heading N,Subtitle, andEnter document nameuse other branches. Leave them.List,To-do,Quote, andWrite codeare the nested labels. Leave them.The pad wires only
top-level.all-blocksappears only in the unit test.If a paragraph has no heading before it, dropping the tail leaves an empty string.
The extension then draws no hint. A normal pad starts with a heading, so a later paragraph still has a chain.
Where else the words appear. Read from code.
buildBreadcrumbPlaceholder.test.tsassertsWrite hereon several lines.The chat composer uses
Write a message...andAdd a comment…inuseTiptapEditor.ts.No Cypress spec, README, or GIF asserts
Write here.Colour. Measured in headless Chromium with Playwright. Each theme used the hex values in
globals.scss.The page used
color-mix(in oklch, var(--color-base-content) <mix>, transparent).getComputedStylesupplied the colour. A 1px canvas composited it onto the sheet.The ratio is the WCAG luminance of that pixel against
--color-base-100.Pad rule, read from code:
--prose-placeholder-mix: 50%on.editorWrapper .ProseMirrorin_blocks.scss.The paint rule is
.is-empty::beforein_prose-mirror-body.scss.The chat composer hardcodes a 40% mix on
p.is-empty::beforein_chat-editor.scss.One pad variable colours every empty-block hint in the pad.
A change to it also changes Heading, Subtitle, List, Quote, and Write code.
The title hint uses
--muted-foreground, a separate 50% mix inglobals.scss.Leave that token alone unless the maintainer says otherwise.
oklch(0.207678 0.0398189 265.767 / 0.5)#878b94on#ffffffoklch(0.927576 0.00579101 none / 0.5)#7b7c84on#0b1220oklch(0.968254 0.00681902 none / 0.5)#84858fon#0f172aoklch(0.190957 0.0174849 none / 0.5)#8a8688on#f9fafcoklch(0.930605 0.00578615 none / 0.5)#7c797con#0c0d10oklch(0.185189 0.0142021 none / 0.5)#898483on#fbf8f4oklch(0.92493 0.0104246 none / 0.5)#7f7978on#110e0bThe Understanding page says SC 1.4.3 covers placeholder text.
It requires 4.5:1 for normal text and 3:1 for large text.
Large text means 18 point, or 14 point bold.
The empty paragraph has no font-size rule, so this hint needs 4.5:1.
The live font size in the app was read from code, not measured.
A lighter grey is a lower mix. More of the sheet shows through.
On the light themes, the current 50% mix is already under 4.5:1.
The 40% column is the lighter example. It is also the chat composer's mix.
No mix under 50% meets 4.5:1 on the light themes.
One mix that passes 4.5:1 on all seven themes is 60% or higher.
That mix is darker than the current 50%.
On docsplus, 45% is 2.956:1, so it also misses the 3:1 large-text bar.
New colours become root tokens in
globals.scss. That is the design-system change protocol.Do not write a new
color-mixon a component.Duplicates. Read from GitHub and from the repo.
Issue search and a discussion search found no matching request.
Issue #45 is a closed 2023 report about heading hint font size. It is a different change.
No changelog entry explains the tail.
The comment on
resolveTailSegmentsays a heading tail shows the level the reader is about to write.The paragraph tail sits in the same map. No ruling says that tail must stay.
Next step: drop the trailing segment in the webapp, and wait for a shade that still passes 4.5:1.
Acceptance criteria
Write hereis gone.>separators and the same truncation.Heading N,Subtitle,Enter document name,List,To-do,Quote, andWrite codestay.--color-base-100.globals.scss. It is not a newcolor-mixin a component.buildBreadcrumbPlaceholder.test.tsfails on the old tail before the edit and passes after it.Blocked by
None. The trailing-segment edit can start. The grey waits for a maintainer pick.
Agent brief
Type: AFK for the trailing segment and its test. HITL for the grey. The maintainer picks the shade before any colour edit.
Category: enhancement
Current behavior: A top-level empty paragraph shows the heading chain, then
> Write here. The pad paints that hint at a 50% mix of--color-base-content. On the light themes that mix is under 4.5:1.Desired behavior: The heading chain stays. The trailing segment is gone. The grey meets 4.5:1 in every theme. The maintainer chooses the shade inside the passing range.
Where to start:
apps/webapp/src/components/TipTap/placeholders/buildBreadcrumbPlaceholder.ts(PLACEHOLDER_TEXT,resolveTailSegment,buildBreadcrumbPlaceholder).apps/webapp/src/components/TipTap/TipTap.tsx(Placeholder.configure).apps/webapp/src/styles/_blocks.scss(--prose-placeholder-mix).apps/webapp/src/styles/_prose-mirror-body.scss(.is-empty::before).apps/webapp/src/styles/_chat-editor.scss(leave the 40% rule).apps/webapp/src/styles/globals.scss(a new token, and--muted-foreground).extensions/extension-placeholder/src/placeholder.ts(Placeholder) is read only.apps/webapp/src/components/TipTap/placeholders/buildBreadcrumbPlaceholder.test.ts. Search by symbol. File names are hints as of 2026-09-29.Rules that apply:
.cursor/docs/design-system.mdChange protocol. New colours become root tokens inglobals.scss.apps/webapp/src/components/TipTap/CLAUDE.md§Editor Performance. Keep@docs.plus/extension-placeholder. Do not switch to the built-in placeholder.AGENTS.md§Test Policy allows this test because it pins a reported request. Update the prose row in the design-system catalog if the mix changes.Verify: Open a pad and put the caret in an empty paragraph under headings. The hint has no
Write here. The heading chain is unchanged. If the grey changes, check all seven themes against the table. Run onlybuildBreadcrumbPlaceholder.test.ts. Do not run the whole webapp Jest suite.