Skip to content

feat: show hex colors as swatches - #1064

Merged
xHeaven merged 4 commits into
dev-null-sh:productionfrom
chrismessina:feat/hex-colors
Oct 24, 2026
Merged

xHeaven merged 4 commits into
dev-null-sh:productionfrom
chrismessina:feat/hex-colors

Conversation

@chrismessina

@chrismessina chrismessina commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Developers paste colors into posts all the time (#ff8a4c, #fff), and today they read as plain text. This shows a hex color with a small swatch of the color beside it, on the web. Nothing is stored and nothing changes on the server or in the API.

This started as the color part of #1059 (hashtags, now closed) and stands on its own: it doesn't depend on hashtags.

Changes

  • resources/js/lib/colors.ts (colorsIn): # or #, then 3, 4, 6 or 8 hex digits. A 3- or 4-digit value also needs a letter or a leading zero, so #123 and #333 stay issue references while #000 is black; issue numbers never start with a zero or run to 6 digits here, so #000000 and #333333 are colors too. It's a whole word, like a hashtag would be: it starts at the start, after whitespace, or after a quoted line's >, and only punctuation may follow it before the next space. So #fff. is a color, while #fff-x, #ff8a4c.com and (#fff) aren't.
  • LinkifiedText finds colors in the prose (code is blanked out first, with the new withoutCode in code.ts) and draws them in a withColors step between mentions and wordmarks. Wordmark keys now include their offset, so several runs of text in one line don't share React keys.
  • A startsText prop tells LinkifiedText that its text is a whole body, so a >#fff that starts it counts as a quoted line. The Following page's suggestions pass it, since they render the raw body.
  • The composer no longer offers a digit-only color like #333333 or #99999999 as an issue reference or looks it up: issueReferenceBeforeCaret skips a reference that colorsIn recognizes, with code blanked out as when rendering. #123 and (#333333 stay issue references.
  • KNOWLEDGE_BASE.md has a sentence about colors.

Validation

Local, macOS, PHP 8.5.12, PostgreSQL 18 with pgvector, in a worktree with production merged in at 4ff5b70:

  • pest --parallel --ci: 4,115 passed.
  • bun run test:unit: 159 passed (includes tests/Unit/Colors.test.ts). vp test run: 497 passed, and 469 with tests/draft-images.config.ts (includes LinkifiedText and IssueReference).
  • pint --test, rector --dry-run, vp fmt --check, vp lint, and tsc (app and tests): pass. vp lint reports warnings, all from lines this branch doesn't change.
  • Each new test failed before the code it covers was written.
  • Not run locally: the 100% line-coverage gate (no Xdebug or pcov for PHP 8.5 here); hosted CI runs it. This change has no PHP.

Visual evidence

image

The swatch is an inline 0.75em square inside a whitespace-nowrap span, so it can't push the color onto its own line. Layout shift was measured with the same markup on #1059's build: 0 at 1440×900, 1366×650 and 390×844, light and dark. It wasn't measured separately on this branch.

Device verification

Not tested on hardware or a simulator. The change is inline text rendering with no input, focus or scrolling behavior, so the on-screen keyboard, safe areas and touch don't apply.

Independent review

  • Reviewer: Codex (gpt-6.1-sol, high effort), read-only, source and diff only.
  • Revisions: base bb1acc1 (production), head 96a0b8de9566acfa3a819545df0d94a1be6b68c7.
  • CODE: PASS. The first review found one P2: a body that starts with >#fff got no swatch on Following suggestions. It's fixed with the startsText prop above and confirmed fixed in the second review. The reviewer verified the color rule against issue references, precedence with URLs, mentions and wordmarks, offsets across slices, React keys, and the commit, chat and Following callers.
  • Follow-up 36c2cc7 (digit-only colors, escaped pattern): CODE: PASS. The reviewer confirmed #000 and #000000 trigger no issue lookup, and that the escaped zero-width class matches the same characters as before.
  • Merge with production (14bce0b): production's new Fragment around plain text was keyed by index, which repeated across the runs withColors joins; it's now keyed by offset, with a test that failed before. The reviewer confirmed the keys, CertificateText and fix: expand issue references only when selected #1098's issue expansion all hold, and found one P2 that predated the merge: the composer still looked up #333333 as an issue.
  • Follow-up 4f80e51 (no issue lookup for colors): CODE: PASS. The first version missed a color right before inline code; the reviewer found it, and the fix now checks the whole draft with code blanked out. The final review confirmed both cases fixed and that only 6- and 8-digit candidates change.
  • VISUAL: not independently reviewed. The reviewer read the swatch markup but didn't see it rendered; the screenshots were checked by the author only.

Limitations

  • The mobile app doesn't show swatches; it would need its own copy of the rule.
  • Three- and four-digit grays such as #333 and #999 stay plain text, since they read the same as issue numbers.
  • A color right after punctuation, like (#fff), isn't recognized, matching how a hashtag would start.

A hex color written like a hashtag, such as #ff8a4c or #fff, shows with a
small swatch of the color. It needs at least one letter, so dev-null-sh#123 stays an
issue reference, and colors in code are left alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@chrismessina chrismessina mentioned this pull request Oct 23, 2026
#000 and #000000 are black, and #333333 is gray, but they showed as plain
text because a color needed a letter. Issue numbers never start with a
zero and aren't 6 or 8 digits long, so those count as colors now, while
dev-null-sh#123 and dev-null-sh#333 still stay plain. The pattern also spells its zero-width
characters as escapes instead of the invisible characters themselves.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@chrismessina
chrismessina marked this pull request as ready for review October 23, 2026 20:25
@xHeaven

xHeaven commented Oct 23, 2026

Copy link
Copy Markdown
Member

Please fix the conflicts.

chrismessina and others added 2 commits October 23, 2026 19:28
Production's withWordmarks now wraps plain text in a Fragment keyed by
index, which repeats across the runs withColors joins; key it by offset
too, like the wordmarks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The composer offered "#333333" and "#99999999" as issue references and
looked them up, although they render as colors. Skip a reference that the
color rule recognizes, with code blanked out as when rendering.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@xHeaven
xHeaven merged commit c3c4485 into dev-null-sh:production Oct 24, 2026
5 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants