Skip to content

Show hex color dot in inline code, similar to GitHub#14564

Open
antonio-mello-ai wants to merge 2 commits intostreamlit:developfrom
antonio-mello-ai:feat/markdown-hex-color-badge
Open

Show hex color dot in inline code, similar to GitHub#14564
antonio-mello-ai wants to merge 2 commits intostreamlit:developfrom
antonio-mello-ai:feat/markdown-hex-color-badge

Conversation

@antonio-mello-ai
Copy link
Copy Markdown

Summary

When a hex color like #0969DA is used in backtick inline code within st.markdown, a small colored circle is now displayed before the hex text — matching GitHub's Markdown rendering behavior.

Before: #0969DA renders as plain inline code
After: Shows a colored dot followed by the hex text

Changes

  • Modified CustomCodeTag in StreamlitMarkdown.tsx to detect valid hex colors (3 or 6 digit) and render a color dot
  • Added 6 tests covering valid hex colors, invalid values, and text preservation

Closes #11643


Generative AI disclosure: This PR was co-authored with Claude (Anthropic).

When a hex color like `#0969DA` is used in backtick inline code within
st.markdown, a small colored circle is now displayed before the hex
text — matching GitHub's Markdown rendering behavior.

Closes streamlit#11643

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

Thanks for contributing to Streamlit! 🎈

Please make sure you have read our Contributing Guide. You can find additional information about Streamlit development in the wiki.

The review process:

  1. Initial triage: A maintainer will apply labels, approve CI to run, and trigger AI-assisted reviews. Your PR may be flagged with status:needs-product-approval if the feature requires product team sign-off.

  2. Code review: A core maintainer will start reviewing your PR once:

    • It is marked as 'ready for review', not 'draft'
    • It has status:product-approved (or doesn't need it)
    • All CI checks pass
    • All AI review comments are addressed

We're receiving many contributions and have limited review bandwidth — please expect some delay. We appreciate your patience! 🙏

@snyk-io
Copy link
Copy Markdown
Contributor

snyk-io Bot commented Mar 29, 2026

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

This PR adds GitHub-style hex color badges for inline code in st.markdown. When inline backtick text matches a valid 3-digit (#RGB) or 6-digit (#RRGGBB) hex color, a small colored dot is rendered before the code text. The implementation adds an isHexColor validator with a module-level regex constant and a colored <span> inside CustomCodeTag. Six frontend unit tests cover valid colors, invalid values, and text preservation. Closes #11643.

All three reviewers (claude-4.6-opus-high-thinking, gemini-3.1-pro, gpt-5.3-codex-high) completed their reviews. No expected models failed.

Code Quality

The implementation is concise and well-scoped — only two files are changed. The isHexColor function is cleanly extracted with a module-level HEX_COLOR_RE regex constant.

However, all three reviewers flagged the same blocking issue: the colored dot <span> uses an inline style prop with 8 CSS properties, which directly violates the project's frontend guidelines requiring @emotion/styled components over inline styles. This should be refactored into a styled component (e.g. StyledHexColorDot in styled-components.ts) with only backgroundColor as a dynamic prop.

One reviewer also identified that isHexColor is imported in the test file (line 40) but never called directly — the tests exercise it only through component rendering. This unused import will likely cause a lint failure and should either be removed or accompanied by direct unit tests for the function.

Test Coverage

Unit tests: Six component-level tests cover the main positive and negative rendering cases. All three reviewers agreed the basic coverage is adequate but noted gaps:

  • The isHexColor function lacks direct unit tests. The analogous isValidCssColor function has a comprehensive it.each parameterized test suite — isHexColor should follow the same pattern for edge cases (empty string, # only, lowercase, 4/8-digit hex, whitespace).
  • Two assertions use toBeInTheDocument() with getByTestId, which is flagged as redundant per frontend/AGENTS.md. These should use toBeVisible() instead.

E2E tests: No E2E test is included. All three reviewers noted this gap with varying urgency. Per the project's testing strategy ("Most new user-facing features should be covered by both unit tests and E2E tests"), a visual rendering feature like this should have E2E coverage in e2e_playwright/st_markdown_test.py to confirm correct rendering across browsers and themes.

Backwards Compatibility

No breaking changes. All three reviewers agreed. The feature is purely additive — existing inline code rendering is unchanged. The color dot only appears when the full inline code text matches the strict hex color regex, so no existing content is affected.

Security & Risk

No security concerns. All three reviewers agreed. The backgroundColor CSS property is set from user content, but the strict regex /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/ ensures only valid hex characters are accepted, making CSS injection or XSS infeasible through this path. No auth, routing, WebSocket, storage, or runtime behavior is modified.

External test recommendation

  • Recommend external_test: No
  • Triggered categories: None
  • Evidence:
    • frontend/lib/src/components/shared/StreamlitMarkdown/StreamlitMarkdown.tsx: Pure frontend rendering change adding a decorative <span> to inline code. No changes to routing, auth, WebSocket, embedding, asset serving, CORS, CSP, cookies, storage, or runtime behavior.
    • frontend/lib/src/components/shared/StreamlitMarkdown/StreamlitMarkdown.test.tsx: Test additions only.
  • Suggested external_test focus areas: N/A
  • Confidence: High (unanimous across all three reviewers)
  • Assumptions and gaps: None — the change is entirely within the frontend rendering layer.

Accessibility

All three reviewers identified the same issue: the color dot <span> is missing aria-hidden="true". Since the dot is purely decorative (the hex code text is preserved alongside it), screen readers will encounter an empty span with no semantic meaning. Adding aria-hidden="true" is required per accessibility best practices for decorative elements.

Recommendations

  1. Replace inline style with a styled component (blocking — all 3 reviewers agreed): Create a StyledHexColorDot in styled-components.ts with static properties, passing only backgroundColor as a dynamic prop. Consider using a theme color for the border instead of hardcoded rgba(0,0,0,0.1) so it adapts to dark mode.
  2. Add aria-hidden="true" to the color dot span (blocking — all 3 reviewers agreed).
  3. Fix the unused isHexColor import in the test file (blocking — will cause lint failure): Either remove it or add direct it.each unit tests for the function following the isValidCssColor pattern.
  4. Fix redundant test assertions: Replace toBeInTheDocument() with toBeVisible() when using getByTestId (lines 1178 and 1187).
  5. Add E2E test coverage: Add a test case to e2e_playwright/st_markdown_test.py verifying inline hex code renders the color dot correctly in a running Streamlit app.
  6. Consider 4/8-digit hex support (optional follow-up): GitHub also supports #RGBA and #RRGGBBAA. Extending the regex would provide fuller parity with GitHub's rendering.

Verdict

CHANGES REQUESTED: The feature is well-scoped and the concept is sound, but it needs a styled component instead of inline styles (guideline violation), aria-hidden="true" for accessibility, an unused import fix, and improved test coverage before merging.


This is a consolidated AI review by claude-4.6-opus-high-thinking, synthesizing reviews from claude-4.6-opus-high-thinking, gemini-3.1-pro, and gpt-5.3-codex-high. Please verify the feedback and use your judgment.

This review also includes 5 inline comment(s) on specific code lines.

<StyledInlineCode className={className} {...omit(props, "node")}>
{isHexColor(codeText) && (
<span
data-testid="stHexColorDot"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: Inline style prop violates the project's frontend coding guidelines (frontend/AGENTS.md: "Avoid inline style props: Prefer @emotion/styled components over inline style attributes"). Extract the static properties (display, width, height, borderRadius, marginRight, verticalAlign, border) into a styled component (e.g. StyledHexColorDot in styled-components.ts), keeping only backgroundColor as a dynamic inline style. Also consider using a theme color for the border (e.g. theme.colors.borderColor) instead of the hardcoded rgba(0,0,0,0.1) so it adapts to dark mode.

CustomMediaTag,
CustomPreTag,
HeadingWithActionElements,
isHexColor,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: isHexColor is imported but never used in the test file — the tests only exercise the component rendering, not the function directly. Remove the unused import to avoid lint failures, or add direct unit tests for isHexColor (following the it.each pattern used by isValidCssColor) covering edge cases like empty string, # only, lowercase variants, 4/8-digit hex, and whitespace.

</ErrorBoundary>
) : (
<StyledInlineCode className={className} {...omit(props, "node")}>
{isHexColor(codeText) && (
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: This decorative dot should have aria-hidden="true" to prevent screen readers from announcing a meaningless empty span. The hex code text already conveys the color information; the dot is purely visual.

render(<CustomCodeTag {...props} />)

const dot = screen.getByTestId("stHexColorDot")
expect(dot).toBeInTheDocument()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Per frontend/AGENTS.md: "Utilizing any query that throws if not found AND asserting using toBeInTheDocument is redundant and must be avoided." Since getByTestId already throws if the element is not found, replace expect(dot).toBeInTheDocument() with expect(dot).toBeVisible(). Same issue at line 1187.

@@ -665,6 +680,15 @@ export function isValidCssColor(color: string): boolean {
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thought: GitHub also supports 4-digit (#RGBA) and 8-digit (#RRGGBBAA) hex colors. Since the PR description references GitHub parity, consider extending the regex to /^#([0-9A-Fa-f]{3,4}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/. This could also be done as a follow-up.

@github-actions github-actions Bot added the do-not-merge PR is blocked from merging label Mar 31, 2026
- Extract inline style to StyledHexColorDot styled component
- Add aria-hidden="true" to decorative color dot
- Add isHexColor unit tests via it.each (edge cases)
- Use toBeVisible() instead of toBeInTheDocument() for getByTestId
- Extend hex regex to support 4-digit and 8-digit hex colors (#F00F, #0969DA80)
- Add render tests for 4-digit and 8-digit hex colors
@sfc-gh-nbellante sfc-gh-nbellante added area:frontend Related to frontend aspects feature:markdown Related to Markdown rendering change:feature PR contains new feature or enhancement implementation labels Apr 3, 2026
@antonio-mello-ai
Copy link
Copy Markdown
Author

All suggestions from the automated review have been addressed (styled component extraction, aria-hidden, extended hex regex, test improvements). Ready for maintainer review.

@sfc-gh-nbellante sfc-gh-nbellante added the impact:users PR changes affect end users label Apr 6, 2026
@github-actions
Copy link
Copy Markdown
Contributor

This pull request has had no activity for 14 days, so it has been marked as stale. If you still want to continue this work, please leave a comment or push a commit within 7 days. A maintainer can also apply the never-stale label to opt out.

@github-actions github-actions Bot added the stale label Apr 21, 2026
@kmcgrady
Copy link
Copy Markdown
Collaborator

kmcgrady commented Apr 22, 2026

Hey @antonio-mello-ai, thanks for this contribution! The implementation is clean and you did a great job addressing the AI review feedback.

A few things we'd want to see adjusted before this could be considered for merge:

  1. GitHub behavior mismatch: GitHub's docs only support 6-digit hex (#RRGGBB), rgb(R,G,B), and hsl(H,S,L) -- they do NOT support 3-digit (#RGB), 4-digit (#RGBA), or 8-digit (#RRGGBBAA) hex. Since the issue explicitly asks to match GitHub's behavior, the regex should be narrowed to only #RRGGBB for the initial implementation. Supporting additional formats could be a follow-up enhancement.
  2. Dark mode border: The StyledHexColorDot uses a hardcoded border: 1px solid rgba(0,0,0,0.1) which will be nearly invisible in dark mode. This should use a theme color (e.g., theme.colors.borderColor or similar) to adapt properly.
  3. Missing E2E tests: Per our testing strategy, user-facing visual features need E2E coverage in e2e_playwright/st_markdown_test.py to confirm rendering across browsers and themes.

I would double check that this is okay from our product-team before final merge but these would be the things we should address.

@github-actions github-actions Bot removed the stale label Apr 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:frontend Related to frontend aspects change:feature PR contains new feature or enhancement implementation do-not-merge PR is blocked from merging feature:markdown Related to Markdown rendering impact:users PR changes affect end users

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Show hex colors in Markdown, similar to GitHub

3 participants