feat: add metricValueFontSize and metricValueFontWeight theme options#13550
Conversation
…me options Add unit tests for new metric value font styling theme configuration. Tests cover: - Theme utils: font size/weight when configured, not configured, and invalid values - Metric component: font-weight and font-size styling applied correctly - Python config: theme option parsing and validation
Allow customizing st.metric value text appearance through theme config: - metricValueFontSize: font size in pixels - metricValueFontWeight: font weight (100-900) Closes streamlit#12300
✅ 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. |
|
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! 🙏 |
threeXL is 2.25rem which equals approximately 36px (not 28px)
There was a problem hiding this comment.
Pull request overview
This PR adds two new theme configuration options (metricValueFontSize and metricValueFontWeight) to allow customization of the font size and weight for st.metric value text. The implementation spans the full stack from protobuf definitions to frontend rendering.
Changes:
- Adds protobuf fields for metric value font styling configuration
- Adds Python config options with appropriate documentation
- Implements frontend theme validation and application logic
- Includes comprehensive unit tests for validation and styling behavior
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| proto/streamlit/proto/NewSession.proto | Adds optional int32 fields for metric_value_font_size (62) and metric_value_font_weight (63) |
| lib/streamlit/config.py | Defines theme configuration options with descriptions and type validation |
| lib/tests/streamlit/config_test.py | Adds test coverage for new config options |
| frontend/lib/src/theme/types.ts | Extends EmotionTheme interface with optional metric value font properties |
| frontend/lib/src/theme/utils.ts | Implements validation logic (size > 0, weight 100-900) and theme creation |
| frontend/lib/src/theme/utils.test.ts | Comprehensive unit tests covering valid/invalid values and edge cases |
| frontend/lib/src/components/widgets/BidiComponent/utils/theme.test.ts | Updates type guard to include new theme fields |
| frontend/lib/src/components/elements/Metric/styled-components.ts | Applies theme values to metric value text styling |
| frontend/lib/src/components/elements/Metric/Metric.test.tsx | Tests default behavior when theme values are not specified |
lib/streamlit/config.py
Outdated
| _create_theme_options( | ||
| "metricValueFontSize", | ||
| categories=["theme"], | ||
| description=""" | ||
| The font size (in pixels) for st.metric value text. | ||
|
|
||
| If this isn't set, the font size will be threeXL (2.25rem, approximately 36px). | ||
| """, | ||
| type_=int, |
There was a problem hiding this comment.
The description for metricValueFontSize should clarify that it expects a positive integer value, similar to how baseFontSize is documented. Consider adding "This is a positive integer." to the description for consistency with other font size configuration options.
There was a problem hiding this comment.
The "(in pixels)" wording implies a positive value, and invalid values (≤0) are silently ignored (consistent with baseFontSize).
Happy to add explicit "Must be a positive integer." if a reviewer prefers.
There was a problem hiding this comment.
I think this is OK, the other config has another descriptive section along with this. But also defer to @sfc-gh-dmatthews , she will likely make a change before the release if she thinks the wording should be updated.
- Add properties to StreamlitTheme interface (component-v2-lib) - Add extraction in extractComponentsV2Theme for Components V2 support - Add mock values to theme.test.ts - Add negative value tests for metricValueFontSize validation
Update in latest commit (e1e539f)Fixed CI failures by adding missing
Also added negative value tests for Ready for CI approval when you have a chance. Thanks! |
|
Fixed Python |
|
@kagawa0710 since this is a visual change, it would be good if you could add some screenshots to help with the product review 🙏 |
Add E2E test to verify that custom metric value font size and weight are applied correctly via environment variables. The test sets STREAMLIT_THEME_METRIC_VALUE_FONT_SIZE=48 and STREAMLIT_THEME_METRIC_VALUE_FONT_WEIGHT=300, then verifies the CSS properties are applied to the metric value element.
|
@sfc-gh-lwilby Sorry for the late reply due to timezone differences! |
|
Sounds good, thanks for adding this! Just two quick questions:
|
If we want to support rem and px, it probably needs to be string not int |
|
@jrieke @lukasmasuch 1. Default behavior: The screenshot difference is likely just due to my local environment. 2. rem/px support: If string support (rem/px like
Let me know your preference! |
|
Hi @kagawa0710 , Checked with the others on this and we will need to support REM and pixels for this config. Can you update this PR to support that? We don't want to change the code later, particularly the protobuf files, and also we don't want to change how the feature works between releases. Thank you! |
Resolve conflicts: - proto ID conflict: metric_value_font_size/weight moved to 64/65 (chart_diverging_colors uses 62) - types.ts: add shadows alongside metric value props - utils.ts: add chartDivergingColors and shadows handling
- Change metricValueFontSize from int to string type - Support px, rem, and plain number formats (e.g., '48px', '3rem', '48') - Add validation: reject zero/negative values with warning - Update proto, Python config, and frontend types - Add comprehensive tests (17 test cases)]
…tSize - Change test value from "48" to "3rem" to verify string/rem parsing - Add negative assertion to ensure it's NOT the default 36px - Add snapshot test for visual verification
Update: Added rem/px string support for
|
| Format | Example | Result |
|---|---|---|
| rem | "3rem" |
3rem (48px with 16px base) |
| px | "48px" |
48px |
| number (string) | "48" |
48px |
Invalid values log a warning and fallback to default (2.25rem).
Changes
- Proto:
int32→string - Config:
type_=int→type_=str - Frontend: Added
parseFontSizevalidation - Tests: 17 unit test cases + E2E test with rem parsing
Note
CI will fail initially due to missing Linux snapshots for the new E2E snapshot test. I'll add them after this run.
|
@jrieke @lukasmasuch @sfc-gh-lwilby Updated to support rem/px string values as requested. Please see the updated PR description for details:
Ready for review! |
|
Regarding the I think this is a CI permissions issue rather than a code problem:
Could you confirm if this is the case, or let me know if there's something I need to fix? |
|
@kagawa0710 thanks for making these updates! I haven't forgotten about your PR, I will try to take a look at the latest changes tomorrow. I agree the CI failure is probably not related to your changes, I will rerun it. |
.playwright-mcp/metric_after.png
Outdated
There was a problem hiding this comment.
Are these part of your dev pipeline?
There was a problem hiding this comment.
Yes, these are from my local dev pipeline. I've removed them now. Thanks for pointing this out!
| genericFonts: fontsOverride, | ||
| ...conditionalOverrides, | ||
| shadows, | ||
| ...(() => { |
There was a problem hiding this comment.
@kagawa0710 two comments/questions here.
-
I'm wondering if it would be better to pull this into a named function, maybe even add some tests for that function (although it looks like we have coverage in utils.test.ts).
-
More importantly, I'm wondering if there will ever be overrides in
conditionalOverridesthat we would want to override this config. Have you looked at what is set there? I am not an SME on the theming features and may need to pull in a colleague that is more familiar with this feature.
There was a problem hiding this comment.
Thanks for the thoughtful review!
1
Named function extraction: I considered this, but the current IIFE felt appropriate here since it's handling a single value rather than an array like setHeadingFontSizes (which processes h1-h6). Tests in utils.test.ts already cover this logic via createTheme. That said, happy to extract it if you think it improves readability—let me know.
2
conditionalOverrides: I checked and there are no overrides that would affect metricValueFontSize. It's a top-level EmotionTheme property (string-typed like "3rem") rather than part of the fontSizes object (which contains numeric multipliers). Additionally, the current spread order (...conditionalOverrides → metricValueFontSize IIFE) ensures user config takes precedence, so even if conditionalOverrides were extended in the future, this property would still be correctly applied.
There was a problem hiding this comment.
Great, I was thinking more the opposite, that there might be something in conditionalOverrides that we would want to take precedence over the user config. But, it makes sense to me that user config should have first precedence, making it otherwise could be confusing for users.
SummaryThis PR adds two new theme configuration options: Code QualityStrengths:
Issues:
...(metricValueFontWeight &&
metricValueFontWeight >= 100 &&
metricValueFontWeight <= 900
? { metricValueFontWeight }
: {}),
Test CoverageStrengths:
Issues:
@pytest.mark.usefixtures("configure_metric_value_style")
def test_metric_value_style_snapshot(app: Page, assert_snapshot: ImageCompareFunction):
"""Visual snapshot test for custom metric value styling."""
expect_no_skeletons(app, timeout=25000)
# Wait for fonts to load to reduce flakiness
app.wait_for_timeout(5000)
metric = get_metric(app, "Revenue")
assert_snapshot(metric, name="metric_value_custom_style")Consider using
Backwards CompatibilityThe changes are fully backwards compatible:
Security & RiskNo security concerns identified:
Recommendations
...(metricValueFontWeight
? metricValueFontWeight >= 100 && metricValueFontWeight <= 900
? { metricValueFontWeight }
: (LOG.warn(`Invalid metricValueFontWeight: ${metricValueFontWeight}. Must be between 100 and 900.`), {})
: {}),
# Wait for fonts using document.fonts.ready
app.evaluate("() => document.fonts.ready")
VerdictAPPROVED: This is a well-implemented feature that follows existing patterns, includes comprehensive test coverage, and maintains backwards compatibility. The issues identified are minor and don't block the merge:
This is an automated AI review using |
frontend/lib/src/theme/utils.ts
Outdated
| })(), | ||
| ...(metricValueFontWeight && | ||
| metricValueFontWeight >= 100 && | ||
| metricValueFontWeight <= 900 |
There was a problem hiding this comment.
I think there is a good suggestion in the AI review, to make this add a log statement similar to metricValueFontSize above.
...(metricValueFontWeight
? metricValueFontWeight >= 100 && metricValueFontWeight <= 900
? { metricValueFontWeight }
: (LOG.warn(`Invalid metricValueFontWeight: ${metricValueFontWeight}. Must be between 100 and 900.`), {})
: {}),
sfc-gh-lwilby
left a comment
There was a problem hiding this comment.
It would be great to add the warning log as suggested by the AI review, but otherwise looks good! The other AI suggestions about the e2e tests are not really relevant since this is a pattern we have for these particular tests even though it doesn't follow our ideal test writing best practices.
lib/tests/streamlit/config_test.py
Outdated
| config._set_option("theme.codeFontWeight", 300, "test") | ||
| config._set_option("theme.baseFontSize", 14, "test") | ||
| config._set_option("theme.baseFontWeight", 300, "test") | ||
| config._set_option("theme.metricValueFontSize", 32, "test") |
There was a problem hiding this comment.
Type mismatch: Setting metricValueFontSize to integer 32 instead of string. The config option is defined as type_=str in lib/streamlit/config.py line 1964, and the proto field is defined as string in proto/streamlit/proto/NewSession.proto line 218. According to the PR description, valid formats are strings like "48px", "3rem", or "48". This test should use:
config._set_option("theme.metricValueFontSize", "32", "test")Or with units:
config._set_option("theme.metricValueFontSize", "32px", "test")| config._set_option("theme.metricValueFontSize", 32, "test") | |
| config._set_option("theme.metricValueFontSize", "32px", "test") |
Spotted by Graphite Agent
Is this helpful? React 👍 or 👎 to let us know.
metricValueFontWeight
…om:kagawa0710/streamlit into feat/12300-metric-theming-font-size-weight # Please enter a commit message to explain why this merge is necessary, # especially if it merges an updated upstream into a topic branch. # # Lines starting with '#' will be ignored, and an empty message aborts # the commit.
use string value for metricValueFontSize in config test
|
Thanks for getting this merged @kagawa0710, awesome addition!! |
feat: add metricValueFontSize and metricValueFontWeight theme options
Add
metricValueFontSizeandmetricValueFontWeighttheme configuration options to customize st.metric value text appearance.Example configuration
Supported formats for
metricValueFontSize"3rem""48px""48"Invalid values (negative, zero, invalid format) log a warning and fallback to the default (2.25rem ≈ 36px).
Screenshots
Before (Default)
metricValueFontSizenot set → uses default (2.25rem ≈ 36px)Supported formats
Shows valid values (
"48px","3rem","36","24px") and invalid values that fallback to default ("-10px","0","aaa").GitHub Issue Link (if applicable)
Closes #12300
Testing Plan
e2e_playwright/theming/theme_metric_value_style_test.py)Notes
metricValueFontSizeaccepts string values with rem/px units (likeheadingFontSizes).