Show hex color dot in inline code, similar to GitHub#14564
Show hex color dot in inline code, similar to GitHub#14564antonio-mello-ai wants to merge 2 commits intostreamlit:developfrom
Conversation
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>
|
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:
We're receiving many contributions and have limited review bandwidth — please expect some delay. We appreciate your patience! 🙏 |
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
There was a problem hiding this comment.
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
isHexColorfunction lacks direct unit tests. The analogousisValidCssColorfunction has a comprehensiveit.eachparameterized test suite —isHexColorshould follow the same pattern for edge cases (empty string,#only, lowercase, 4/8-digit hex, whitespace). - Two assertions use
toBeInTheDocument()withgetByTestId, which is flagged as redundant perfrontend/AGENTS.md. These should usetoBeVisible()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
- Replace inline
stylewith a styled component (blocking — all 3 reviewers agreed): Create aStyledHexColorDotinstyled-components.tswith static properties, passing onlybackgroundColoras a dynamic prop. Consider using a theme color for the border instead of hardcodedrgba(0,0,0,0.1)so it adapts to dark mode. - Add
aria-hidden="true"to the color dot span (blocking — all 3 reviewers agreed). - Fix the unused
isHexColorimport in the test file (blocking — will cause lint failure): Either remove it or add directit.eachunit tests for the function following theisValidCssColorpattern. - Fix redundant test assertions: Replace
toBeInTheDocument()withtoBeVisible()when usinggetByTestId(lines 1178 and 1187). - Add E2E test coverage: Add a test case to
e2e_playwright/st_markdown_test.pyverifying inline hex code renders the color dot correctly in a running Streamlit app. - Consider 4/8-digit hex support (optional follow-up): GitHub also supports
#RGBAand#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" |
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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) && ( |
There was a problem hiding this comment.
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() |
There was a problem hiding this comment.
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 { | |||
| } | |||
| } | |||
|
|
|||
There was a problem hiding this comment.
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.
- 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
|
All suggestions from the automated review have been addressed (styled component extraction, aria-hidden, extended hex regex, test improvements). Ready for maintainer review. |
|
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 |
|
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:
I would double check that this is okay from our product-team before final merge but these would be the things we should address. |
Summary
When a hex color like
#0969DAis used in backtick inline code withinst.markdown, a small colored circle is now displayed before the hex text — matching GitHub's Markdown rendering behavior.Before:
#0969DArenders as plain inline codeAfter: Shows a colored dot followed by the hex text
Changes
CustomCodeTaginStreamlitMarkdown.tsxto detect valid hex colors (3 or 6 digit) and render a color dotCloses #11643
Generative AI disclosure: This PR was co-authored with Claude (Anthropic).