streamlit-react-components 1.0.5__py3-none-any.whl → 1.0.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. streamlit_react_components/__init__.py +5 -1
  2. streamlit_react_components/_frontend/index.css +1 -1
  3. streamlit_react_components/_frontend/index.js +142 -141
  4. streamlit_react_components/common/__init__.py +2 -0
  5. streamlit_react_components/common/button_group.py +10 -0
  6. streamlit_react_components/common/chart_legend.py +10 -0
  7. streamlit_react_components/common/data_table.py +10 -0
  8. streamlit_react_components/common/metric_row.py +10 -0
  9. streamlit_react_components/common/panel.py +10 -0
  10. streamlit_react_components/common/plotly_chart.py +10 -0
  11. streamlit_react_components/common/section_header.py +10 -0
  12. streamlit_react_components/common/smart_chart.py +327 -0
  13. streamlit_react_components/common/stat_card.py +10 -0
  14. streamlit_react_components/common/step_indicator.py +10 -0
  15. streamlit_react_components/form/__init__.py +2 -0
  16. streamlit_react_components/form/checkbox_group.py +10 -0
  17. streamlit_react_components/form/form_select.py +10 -0
  18. streamlit_react_components/form/form_slider.py +10 -0
  19. streamlit_react_components/form/radio_group.py +78 -0
  20. streamlit_react_components/themes.py +1203 -0
  21. {streamlit_react_components-1.0.5.dist-info → streamlit_react_components-1.0.6.dist-info}/METADATA +1 -1
  22. streamlit_react_components-1.0.6.dist-info/RECORD +25 -0
  23. {streamlit_react_components-1.0.5.dist-info → streamlit_react_components-1.0.6.dist-info}/WHEEL +1 -1
  24. streamlit_react_components-1.0.5.dist-info/RECORD +0 -22
  25. {streamlit_react_components-1.0.5.dist-info → streamlit_react_components-1.0.6.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,1203 @@
1
+ """
2
+ Theming system for Streamlit React Components.
3
+
4
+ This module provides a comprehensive theming system that works with both
5
+ Streamlit's standard components and custom React components.
6
+
7
+ Users can:
8
+ 1. Use pre-defined themes (DEFAULT, OCEAN, FOREST, SUNSET, MONOCHROME)
9
+ 2. Edit existing theme dictionaries
10
+ 3. Create custom themes by copying and modifying
11
+ 4. Apply themes globally via set_theme()
12
+ 5. Theme both Streamlit standard components and custom React components
13
+
14
+ Example:
15
+ import streamlit as st
16
+ from streamlit_react_components.themes import set_theme, apply_theme_to_streamlit, OCEAN_THEME
17
+
18
+ set_theme(OCEAN_THEME)
19
+ apply_theme_to_streamlit()
20
+
21
+ st.title("My Themed App")
22
+ st.button("Themed Button")
23
+ """
24
+
25
+ from typing import TypedDict, Optional, Dict, Any
26
+
27
+
28
+ # ============================================
29
+ # TYPE DEFINITIONS
30
+ # ============================================
31
+
32
+ class ColorPalette(TypedDict):
33
+ """Core color palette for a theme."""
34
+ primary: str
35
+ secondary: str
36
+ success: str
37
+ warning: str
38
+ error: str
39
+ info: str
40
+
41
+
42
+ class BackgroundColors(TypedDict):
43
+ """Background color variations."""
44
+ primary: str
45
+ secondary: str
46
+ tertiary: str
47
+ accent: str
48
+
49
+
50
+ class TextColors(TypedDict):
51
+ """Text color variations."""
52
+ primary: str
53
+ secondary: str
54
+ tertiary: str
55
+ inverse: str
56
+
57
+
58
+ class BorderColors(TypedDict):
59
+ """Border color variations."""
60
+ default: str
61
+ focus: str
62
+ active: str
63
+
64
+
65
+ class InteractiveStates(TypedDict):
66
+ """Colors for interactive states."""
67
+ hover_bg: str
68
+ hover_text: str
69
+ focus_ring: str
70
+ active_bg: str
71
+ disabled_bg: str
72
+ disabled_text: str
73
+
74
+
75
+ class Typography(TypedDict):
76
+ """Typography settings."""
77
+ font_family: str
78
+ font_size_base: str
79
+ font_size_sm: str
80
+ font_size_lg: str
81
+ font_size_xl: str
82
+ font_weight_normal: str
83
+ font_weight_medium: str
84
+ font_weight_semibold: str
85
+ font_weight_bold: str
86
+ line_height: str
87
+
88
+
89
+ class Spacing(TypedDict):
90
+ """Spacing scale."""
91
+ xs: str
92
+ sm: str
93
+ md: str
94
+ lg: str
95
+ xl: str
96
+
97
+
98
+ class BorderRadius(TypedDict):
99
+ """Border radius scale."""
100
+ sm: str
101
+ md: str
102
+ lg: str
103
+ xl: str
104
+ full: str
105
+
106
+
107
+ class Theme(TypedDict):
108
+ """Complete theme definition."""
109
+ name: str
110
+ colors: ColorPalette
111
+ backgrounds: BackgroundColors
112
+ text: TextColors
113
+ borders: BorderColors
114
+ interactive: InteractiveStates
115
+ typography: Typography
116
+ spacing: Spacing
117
+ border_radius: BorderRadius
118
+ use_streamlit_fallback: bool
119
+
120
+
121
+ # ============================================
122
+ # PRE-DEFINED THEMES
123
+ # ============================================
124
+
125
+ DEFAULT_THEME: Theme = {
126
+ "name": "Default",
127
+ "colors": {
128
+ "primary": "#3b82f6", # blue-500
129
+ "secondary": "#8b5cf6", # purple-500
130
+ "success": "#22c55e", # green-500
131
+ "warning": "#f59e0b", # amber-500
132
+ "error": "#ef4444", # red-500
133
+ "info": "#06b6d4", # cyan-500
134
+ },
135
+ "backgrounds": {
136
+ "primary": "#0f172a", # slate-900
137
+ "secondary": "#1e293b", # slate-800
138
+ "tertiary": "#334155", # slate-700
139
+ "accent": "#475569", # slate-600
140
+ },
141
+ "text": {
142
+ "primary": "#f1f5f9", # slate-100
143
+ "secondary": "#cbd5e1", # slate-300
144
+ "tertiary": "#94a3b8", # slate-400
145
+ "inverse": "#0f172a", # slate-900
146
+ },
147
+ "borders": {
148
+ "default": "#475569", # slate-600
149
+ "focus": "#3b82f6", # blue-500
150
+ "active": "#60a5fa", # blue-400
151
+ },
152
+ "interactive": {
153
+ "hover_bg": "#334155", # slate-700
154
+ "hover_text": "#f1f5f9", # slate-100
155
+ "focus_ring": "#3b82f6", # blue-500
156
+ "active_bg": "#475569", # slate-600
157
+ "disabled_bg": "#1e293b", # slate-800
158
+ "disabled_text": "#64748b", # slate-500
159
+ },
160
+ "typography": {
161
+ "font_family": "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif",
162
+ "font_size_base": "14px",
163
+ "font_size_sm": "12px",
164
+ "font_size_lg": "16px",
165
+ "font_size_xl": "20px",
166
+ "font_weight_normal": "400",
167
+ "font_weight_medium": "500",
168
+ "font_weight_semibold": "600",
169
+ "font_weight_bold": "700",
170
+ "line_height": "1.5",
171
+ },
172
+ "spacing": {
173
+ "xs": "4px",
174
+ "sm": "8px",
175
+ "md": "16px",
176
+ "lg": "24px",
177
+ "xl": "32px",
178
+ },
179
+ "border_radius": {
180
+ "sm": "4px",
181
+ "md": "8px",
182
+ "lg": "12px",
183
+ "xl": "16px",
184
+ "full": "9999px",
185
+ },
186
+ "use_streamlit_fallback": True,
187
+ }
188
+
189
+
190
+ OCEAN_THEME: Theme = {
191
+ "name": "Ocean",
192
+ "colors": {
193
+ "primary": "#0ea5e9", # sky-500
194
+ "secondary": "#06b6d4", # cyan-500
195
+ "success": "#10b981", # emerald-500
196
+ "warning": "#f59e0b", # amber-500
197
+ "error": "#ef4444", # red-500
198
+ "info": "#0284c7", # sky-600
199
+ },
200
+ "backgrounds": {
201
+ "primary": "#082f49", # sky-950
202
+ "secondary": "#0c4a6e", # sky-900
203
+ "tertiary": "#075985", # sky-800
204
+ "accent": "#0369a1", # sky-700
205
+ },
206
+ "text": {
207
+ "primary": "#f0f9ff", # sky-50
208
+ "secondary": "#e0f2fe", # sky-100
209
+ "tertiary": "#bae6fd", # sky-200
210
+ "inverse": "#082f49", # sky-950
211
+ },
212
+ "borders": {
213
+ "default": "#0369a1", # sky-700
214
+ "focus": "#0ea5e9", # sky-500
215
+ "active": "#38bdf8", # sky-400
216
+ },
217
+ "interactive": {
218
+ "hover_bg": "#075985", # sky-800
219
+ "hover_text": "#f0f9ff", # sky-50
220
+ "focus_ring": "#0ea5e9", # sky-500
221
+ "active_bg": "#0369a1", # sky-700
222
+ "disabled_bg": "#0c4a6e", # sky-900
223
+ "disabled_text": "#0c4a6e",# sky-900
224
+ },
225
+ "typography": {
226
+ "font_family": "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif",
227
+ "font_size_base": "14px",
228
+ "font_size_sm": "12px",
229
+ "font_size_lg": "16px",
230
+ "font_size_xl": "20px",
231
+ "font_weight_normal": "400",
232
+ "font_weight_medium": "500",
233
+ "font_weight_semibold": "600",
234
+ "font_weight_bold": "700",
235
+ "line_height": "1.5",
236
+ },
237
+ "spacing": {
238
+ "xs": "4px",
239
+ "sm": "8px",
240
+ "md": "16px",
241
+ "lg": "24px",
242
+ "xl": "32px",
243
+ },
244
+ "border_radius": {
245
+ "sm": "4px",
246
+ "md": "8px",
247
+ "lg": "12px",
248
+ "xl": "16px",
249
+ "full": "9999px",
250
+ },
251
+ "use_streamlit_fallback": False,
252
+ }
253
+
254
+
255
+ FOREST_THEME: Theme = {
256
+ "name": "Forest",
257
+ "colors": {
258
+ "primary": "#22c55e", # green-500
259
+ "secondary": "#84cc16", # lime-500
260
+ "success": "#10b981", # emerald-500
261
+ "warning": "#f59e0b", # amber-500
262
+ "error": "#ef4444", # red-500
263
+ "info": "#06b6d4", # cyan-500
264
+ },
265
+ "backgrounds": {
266
+ "primary": "#052e16", # green-950
267
+ "secondary": "#14532d", # green-900
268
+ "tertiary": "#166534", # green-800
269
+ "accent": "#15803d", # green-700
270
+ },
271
+ "text": {
272
+ "primary": "#f0fdf4", # green-50
273
+ "secondary": "#dcfce7", # green-100
274
+ "tertiary": "#bbf7d0", # green-200
275
+ "inverse": "#052e16", # green-950
276
+ },
277
+ "borders": {
278
+ "default": "#15803d", # green-700
279
+ "focus": "#22c55e", # green-500
280
+ "active": "#4ade80", # green-400
281
+ },
282
+ "interactive": {
283
+ "hover_bg": "#166534", # green-800
284
+ "hover_text": "#f0fdf4", # green-50
285
+ "focus_ring": "#22c55e", # green-500
286
+ "active_bg": "#15803d", # green-700
287
+ "disabled_bg": "#14532d", # green-900
288
+ "disabled_text": "#166534",# green-800
289
+ },
290
+ "typography": {
291
+ "font_family": "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif",
292
+ "font_size_base": "14px",
293
+ "font_size_sm": "12px",
294
+ "font_size_lg": "16px",
295
+ "font_size_xl": "20px",
296
+ "font_weight_normal": "400",
297
+ "font_weight_medium": "500",
298
+ "font_weight_semibold": "600",
299
+ "font_weight_bold": "700",
300
+ "line_height": "1.5",
301
+ },
302
+ "spacing": {
303
+ "xs": "4px",
304
+ "sm": "8px",
305
+ "md": "16px",
306
+ "lg": "24px",
307
+ "xl": "32px",
308
+ },
309
+ "border_radius": {
310
+ "sm": "4px",
311
+ "md": "8px",
312
+ "lg": "12px",
313
+ "xl": "16px",
314
+ "full": "9999px",
315
+ },
316
+ "use_streamlit_fallback": False,
317
+ }
318
+
319
+
320
+ SUNSET_THEME: Theme = {
321
+ "name": "Sunset",
322
+ "colors": {
323
+ "primary": "#f97316", # orange-500
324
+ "secondary": "#f59e0b", # amber-500
325
+ "success": "#22c55e", # green-500
326
+ "warning": "#eab308", # yellow-500
327
+ "error": "#ef4444", # red-500
328
+ "info": "#06b6d4", # cyan-500
329
+ },
330
+ "backgrounds": {
331
+ "primary": "#431407", # orange-950
332
+ "secondary": "#7c2d12", # orange-900
333
+ "tertiary": "#9a3412", # orange-800
334
+ "accent": "#c2410c", # orange-700
335
+ },
336
+ "text": {
337
+ "primary": "#fff7ed", # orange-50
338
+ "secondary": "#ffedd5", # orange-100
339
+ "tertiary": "#fed7aa", # orange-200
340
+ "inverse": "#431407", # orange-950
341
+ },
342
+ "borders": {
343
+ "default": "#c2410c", # orange-700
344
+ "focus": "#f97316", # orange-500
345
+ "active": "#fb923c", # orange-400
346
+ },
347
+ "interactive": {
348
+ "hover_bg": "#9a3412", # orange-800
349
+ "hover_text": "#fff7ed", # orange-50
350
+ "focus_ring": "#f97316", # orange-500
351
+ "active_bg": "#c2410c", # orange-700
352
+ "disabled_bg": "#7c2d12", # orange-900
353
+ "disabled_text": "#9a3412",# orange-800
354
+ },
355
+ "typography": {
356
+ "font_family": "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif",
357
+ "font_size_base": "14px",
358
+ "font_size_sm": "12px",
359
+ "font_size_lg": "16px",
360
+ "font_size_xl": "20px",
361
+ "font_weight_normal": "400",
362
+ "font_weight_medium": "500",
363
+ "font_weight_semibold": "600",
364
+ "font_weight_bold": "700",
365
+ "line_height": "1.5",
366
+ },
367
+ "spacing": {
368
+ "xs": "4px",
369
+ "sm": "8px",
370
+ "md": "16px",
371
+ "lg": "24px",
372
+ "xl": "32px",
373
+ },
374
+ "border_radius": {
375
+ "sm": "4px",
376
+ "md": "8px",
377
+ "lg": "12px",
378
+ "xl": "16px",
379
+ "full": "9999px",
380
+ },
381
+ "use_streamlit_fallback": False,
382
+ }
383
+
384
+
385
+ MONOCHROME_THEME: Theme = {
386
+ "name": "Monochrome",
387
+ "colors": {
388
+ "primary": "#71717a", # zinc-500
389
+ "secondary": "#52525b", # zinc-600
390
+ "success": "#a1a1aa", # zinc-400
391
+ "warning": "#71717a", # zinc-500
392
+ "error": "#3f3f46", # zinc-700
393
+ "info": "#a1a1aa", # zinc-400
394
+ },
395
+ "backgrounds": {
396
+ "primary": "#09090b", # zinc-950
397
+ "secondary": "#18181b", # zinc-900
398
+ "tertiary": "#27272a", # zinc-800
399
+ "accent": "#3f3f46", # zinc-700
400
+ },
401
+ "text": {
402
+ "primary": "#fafafa", # zinc-50
403
+ "secondary": "#f4f4f5", # zinc-100
404
+ "tertiary": "#e4e4e7", # zinc-200
405
+ "inverse": "#09090b", # zinc-950
406
+ },
407
+ "borders": {
408
+ "default": "#52525b", # zinc-600
409
+ "focus": "#71717a", # zinc-500
410
+ "active": "#a1a1aa", # zinc-400
411
+ },
412
+ "interactive": {
413
+ "hover_bg": "#3f3f46", # zinc-700
414
+ "hover_text": "#fafafa", # zinc-50
415
+ "focus_ring": "#71717a", # zinc-500
416
+ "active_bg": "#52525b", # zinc-600
417
+ "disabled_bg": "#18181b", # zinc-900
418
+ "disabled_text": "#3f3f46",# zinc-700
419
+ },
420
+ "typography": {
421
+ "font_family": "ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Monaco, Consolas, monospace",
422
+ "font_size_base": "14px",
423
+ "font_size_sm": "12px",
424
+ "font_size_lg": "16px",
425
+ "font_size_xl": "20px",
426
+ "font_weight_normal": "400",
427
+ "font_weight_medium": "500",
428
+ "font_weight_semibold": "600",
429
+ "font_weight_bold": "700",
430
+ "line_height": "1.5",
431
+ },
432
+ "spacing": {
433
+ "xs": "4px",
434
+ "sm": "8px",
435
+ "md": "16px",
436
+ "lg": "24px",
437
+ "xl": "32px",
438
+ },
439
+ "border_radius": {
440
+ "sm": "2px",
441
+ "md": "4px",
442
+ "lg": "6px",
443
+ "xl": "8px",
444
+ "full": "9999px",
445
+ },
446
+ "use_streamlit_fallback": False,
447
+ }
448
+
449
+
450
+ # ============================================
451
+ # THEME MANAGEMENT
452
+ # ============================================
453
+
454
+ # Global active theme
455
+ _active_theme: Optional[Theme] = None
456
+
457
+
458
+ def set_theme(theme: Theme) -> None:
459
+ """
460
+ Set the active theme for all components.
461
+
462
+ Args:
463
+ theme: Theme dictionary to set as active
464
+
465
+ Example:
466
+ from streamlit_react_components.themes import set_theme, OCEAN_THEME
467
+ set_theme(OCEAN_THEME)
468
+ """
469
+ global _active_theme
470
+ _active_theme = theme
471
+
472
+
473
+ def get_active_theme() -> Theme:
474
+ """
475
+ Get the currently active theme.
476
+
477
+ Returns:
478
+ The active theme, or DEFAULT_THEME if no theme is set
479
+
480
+ Example:
481
+ theme = get_active_theme()
482
+ primary_color = theme['colors']['primary']
483
+ """
484
+ return _active_theme or DEFAULT_THEME
485
+
486
+
487
+ def get_theme_by_name(name: str) -> Optional[Theme]:
488
+ """
489
+ Get a pre-defined theme by name.
490
+
491
+ Args:
492
+ name: Theme name (case-insensitive): "default", "ocean", "forest", "sunset", "monochrome"
493
+
494
+ Returns:
495
+ Theme dictionary if found, None otherwise
496
+
497
+ Example:
498
+ theme = get_theme_by_name("ocean")
499
+ if theme:
500
+ set_theme(theme)
501
+ """
502
+ themes = {
503
+ "default": DEFAULT_THEME,
504
+ "ocean": OCEAN_THEME,
505
+ "forest": FOREST_THEME,
506
+ "sunset": SUNSET_THEME,
507
+ "monochrome": MONOCHROME_THEME,
508
+ }
509
+ return themes.get(name.lower())
510
+
511
+
512
+ # ============================================
513
+ # CSS GENERATION FOR STREAMLIT COMPONENTS
514
+ # ============================================
515
+
516
+ def generate_streamlit_css(theme: Theme) -> str:
517
+ """
518
+ Generate CSS to override Streamlit's standard component styles.
519
+ This CSS themes 47+ Streamlit components including buttons, inputs,
520
+ metrics, alerts, charts, and more.
521
+
522
+ Args:
523
+ theme: Theme dictionary containing all theme settings
524
+
525
+ Returns:
526
+ CSS string wrapped in <style> tags, ready for st.markdown()
527
+
528
+ Note:
529
+ This function generates ~500 lines of CSS targeting Streamlit's
530
+ internal CSS classes. It uses CSS specificity to override defaults
531
+ while allowing user customization.
532
+ """
533
+ return f"""
534
+ <style>
535
+ /* ============================================
536
+ STREAMLIT THEMING SYSTEM
537
+ Generated CSS for {theme['name']} theme
538
+ ============================================ */
539
+
540
+ /* Global Page Styles */
541
+ .stApp {{
542
+ background-color: {theme['backgrounds']['primary']};
543
+ color: {theme['text']['primary']};
544
+ font-family: {theme['typography']['font_family']};
545
+ }}
546
+
547
+ /* Sidebar */
548
+ [data-testid="stSidebar"] {{
549
+ background-color: {theme['backgrounds']['secondary']};
550
+ }}
551
+
552
+ [data-testid="stSidebar"] .stMarkdown {{
553
+ color: {theme['text']['primary']};
554
+ }}
555
+
556
+ /* ============================================
557
+ BUTTONS
558
+ ============================================ */
559
+
560
+ /* Primary Button (st.button) */
561
+ .stButton > button {{
562
+ background-color: {theme['colors']['primary']};
563
+ color: {theme['text']['inverse']};
564
+ border: 1px solid {theme['borders']['default']};
565
+ border-radius: {theme['border_radius']['md']};
566
+ font-family: {theme['typography']['font_family']};
567
+ font-weight: {theme['typography']['font_weight_medium']};
568
+ transition: all 0.2s;
569
+ }}
570
+
571
+ .stButton > button:hover {{
572
+ background-color: {theme['interactive']['hover_bg']};
573
+ color: {theme['interactive']['hover_text']};
574
+ border-color: {theme['borders']['focus']};
575
+ }}
576
+
577
+ .stButton > button:active {{
578
+ background-color: {theme['interactive']['active_bg']};
579
+ }}
580
+
581
+ .stButton > button:focus {{
582
+ outline: 2px solid {theme['interactive']['focus_ring']};
583
+ outline-offset: 2px;
584
+ }}
585
+
586
+ .stButton > button:disabled {{
587
+ background-color: {theme['interactive']['disabled_bg']};
588
+ color: {theme['interactive']['disabled_text']};
589
+ cursor: not-allowed;
590
+ opacity: 0.6;
591
+ }}
592
+
593
+ /* Download Button (st.download_button) */
594
+ .stDownloadButton > button {{
595
+ background-color: {theme['colors']['secondary']};
596
+ color: {theme['text']['inverse']};
597
+ border-radius: {theme['border_radius']['md']};
598
+ }}
599
+
600
+ .stDownloadButton > button:hover {{
601
+ background-color: {theme['interactive']['hover_bg']};
602
+ }}
603
+
604
+ /* ============================================
605
+ TEXT INPUTS
606
+ ============================================ */
607
+
608
+ /* Text Input (st.text_input) */
609
+ .stTextInput > div > div > input {{
610
+ background-color: {theme['backgrounds']['secondary']};
611
+ color: {theme['text']['primary']};
612
+ border: 1px solid {theme['borders']['default']};
613
+ border-radius: {theme['border_radius']['md']};
614
+ font-family: {theme['typography']['font_family']};
615
+ padding: {theme['spacing']['sm']};
616
+ }}
617
+
618
+ .stTextInput > div > div > input:focus {{
619
+ border-color: {theme['borders']['focus']};
620
+ outline: 2px solid {theme['interactive']['focus_ring']};
621
+ outline-offset: 0px;
622
+ }}
623
+
624
+ .stTextInput > div > div > input::placeholder {{
625
+ color: {theme['text']['tertiary']};
626
+ }}
627
+
628
+ .stTextInput > label {{
629
+ color: {theme['text']['secondary']};
630
+ font-size: {theme['typography']['font_size_sm']};
631
+ font-weight: {theme['typography']['font_weight_medium']};
632
+ }}
633
+
634
+ /* Text Area (st.text_area) */
635
+ .stTextArea > div > div > textarea {{
636
+ background-color: {theme['backgrounds']['secondary']};
637
+ color: {theme['text']['primary']};
638
+ border: 1px solid {theme['borders']['default']};
639
+ border-radius: {theme['border_radius']['md']};
640
+ font-family: {theme['typography']['font_family']};
641
+ }}
642
+
643
+ .stTextArea > div > div > textarea:focus {{
644
+ border-color: {theme['borders']['focus']};
645
+ outline: 2px solid {theme['interactive']['focus_ring']};
646
+ }}
647
+
648
+ .stTextArea > label {{
649
+ color: {theme['text']['secondary']};
650
+ font-size: {theme['typography']['font_size_sm']};
651
+ }}
652
+
653
+ /* Number Input (st.number_input) */
654
+ .stNumberInput > div > div > input {{
655
+ background-color: {theme['backgrounds']['secondary']};
656
+ color: {theme['text']['primary']};
657
+ border: 1px solid {theme['borders']['default']};
658
+ border-radius: {theme['border_radius']['md']};
659
+ }}
660
+
661
+ .stNumberInput > div > div > input:focus {{
662
+ border-color: {theme['borders']['focus']};
663
+ outline: 2px solid {theme['interactive']['focus_ring']};
664
+ }}
665
+
666
+ /* Number input buttons */
667
+ .stNumberInput button {{
668
+ background-color: {theme['backgrounds']['tertiary']};
669
+ color: {theme['text']['secondary']};
670
+ border-color: {theme['borders']['default']};
671
+ }}
672
+
673
+ .stNumberInput button:hover {{
674
+ background-color: {theme['interactive']['hover_bg']};
675
+ }}
676
+
677
+ /* ============================================
678
+ SELECT WIDGETS
679
+ ============================================ */
680
+
681
+ /* Selectbox (st.selectbox) */
682
+ .stSelectbox > div > div {{
683
+ background-color: {theme['backgrounds']['secondary']};
684
+ border: 1px solid {theme['borders']['default']};
685
+ border-radius: {theme['border_radius']['md']};
686
+ }}
687
+
688
+ .stSelectbox > div > div:focus-within {{
689
+ border-color: {theme['borders']['focus']};
690
+ outline: 2px solid {theme['interactive']['focus_ring']};
691
+ }}
692
+
693
+ .stSelectbox > label {{
694
+ color: {theme['text']['secondary']};
695
+ font-size: {theme['typography']['font_size_sm']};
696
+ }}
697
+
698
+ .stSelectbox [data-baseweb="select"] {{
699
+ background-color: {theme['backgrounds']['secondary']};
700
+ }}
701
+
702
+ .stSelectbox [data-baseweb="select"] > div {{
703
+ background-color: {theme['backgrounds']['secondary']};
704
+ color: {theme['text']['primary']};
705
+ }}
706
+
707
+ /* Dropdown menu */
708
+ [data-baseweb="menu"] {{
709
+ background-color: {theme['backgrounds']['tertiary']};
710
+ border: 1px solid {theme['borders']['default']};
711
+ }}
712
+
713
+ [data-baseweb="menu"] li {{
714
+ background-color: {theme['backgrounds']['tertiary']};
715
+ color: {theme['text']['primary']};
716
+ }}
717
+
718
+ [data-baseweb="menu"] li:hover {{
719
+ background-color: {theme['interactive']['hover_bg']};
720
+ color: {theme['interactive']['hover_text']};
721
+ }}
722
+
723
+ /* Multiselect (st.multiselect) */
724
+ .stMultiSelect > div > div {{
725
+ background-color: {theme['backgrounds']['secondary']};
726
+ border: 1px solid {theme['borders']['default']};
727
+ border-radius: {theme['border_radius']['md']};
728
+ }}
729
+
730
+ .stMultiSelect [data-baseweb="tag"] {{
731
+ background-color: {theme['colors']['primary']};
732
+ color: {theme['text']['inverse']};
733
+ }}
734
+
735
+ /* Radio (st.radio) */
736
+ .stRadio > label {{
737
+ color: {theme['text']['secondary']};
738
+ font-size: {theme['typography']['font_size_sm']};
739
+ }}
740
+
741
+ .stRadio > div {{
742
+ color: {theme['text']['primary']};
743
+ }}
744
+
745
+ .stRadio [role="radiogroup"] label {{
746
+ color: {theme['text']['primary']};
747
+ }}
748
+
749
+ .stRadio input[type="radio"]:checked + div {{
750
+ background-color: {theme['colors']['primary']};
751
+ }}
752
+
753
+ .stRadio input[type="radio"]:focus + div {{
754
+ outline: 2px solid {theme['interactive']['focus_ring']};
755
+ }}
756
+
757
+ /* Checkbox (st.checkbox) */
758
+ .stCheckbox > label {{
759
+ color: {theme['text']['primary']};
760
+ }}
761
+
762
+ .stCheckbox input[type="checkbox"] {{
763
+ accent-color: {theme['colors']['primary']};
764
+ }}
765
+
766
+ .stCheckbox input[type="checkbox"]:focus {{
767
+ outline: 2px solid {theme['interactive']['focus_ring']};
768
+ }}
769
+
770
+ /* Toggle (st.toggle) */
771
+ .stToggle > label {{
772
+ color: {theme['text']['secondary']};
773
+ }}
774
+
775
+ .stToggle input:checked + div {{
776
+ background-color: {theme['colors']['primary']};
777
+ }}
778
+
779
+ /* ============================================
780
+ SLIDERS
781
+ ============================================ */
782
+
783
+ /* Slider (st.slider) */
784
+ .stSlider > label {{
785
+ color: {theme['text']['secondary']};
786
+ font-size: {theme['typography']['font_size_sm']};
787
+ }}
788
+
789
+ .stSlider [data-baseweb="slider"] {{
790
+ color: {theme['text']['primary']};
791
+ }}
792
+
793
+ .stSlider [role="slider"] {{
794
+ background-color: {theme['colors']['primary']};
795
+ }}
796
+
797
+ .stSlider [data-baseweb="slider"] [data-testid="stTickBar"] > div {{
798
+ background-color: {theme['colors']['primary']};
799
+ }}
800
+
801
+ .stSlider [data-baseweb="slider"] [data-testid="stThumbValue"] {{
802
+ color: {theme['text']['primary']};
803
+ }}
804
+
805
+ /* ============================================
806
+ DATE & TIME PICKERS
807
+ ============================================ */
808
+
809
+ /* Date Input (st.date_input) */
810
+ .stDateInput > label {{
811
+ color: {theme['text']['secondary']};
812
+ font-size: {theme['typography']['font_size_sm']};
813
+ }}
814
+
815
+ .stDateInput > div > div {{
816
+ background-color: {theme['backgrounds']['secondary']};
817
+ border: 1px solid {theme['borders']['default']};
818
+ border-radius: {theme['border_radius']['md']};
819
+ }}
820
+
821
+ .stDateInput input {{
822
+ color: {theme['text']['primary']};
823
+ }}
824
+
825
+ /* Time Input (st.time_input) */
826
+ .stTimeInput > label {{
827
+ color: {theme['text']['secondary']};
828
+ }}
829
+
830
+ .stTimeInput > div > div {{
831
+ background-color: {theme['backgrounds']['secondary']};
832
+ border: 1px solid {theme['borders']['default']};
833
+ }}
834
+
835
+ /* ============================================
836
+ FILE UPLOADER
837
+ ============================================ */
838
+
839
+ .stFileUploader > label {{
840
+ color: {theme['text']['secondary']};
841
+ }}
842
+
843
+ .stFileUploader section {{
844
+ background-color: {theme['backgrounds']['secondary']};
845
+ border: 2px dashed {theme['borders']['default']};
846
+ border-radius: {theme['border_radius']['lg']};
847
+ }}
848
+
849
+ .stFileUploader section:hover {{
850
+ border-color: {theme['borders']['focus']};
851
+ }}
852
+
853
+ .stFileUploader button {{
854
+ background-color: {theme['colors']['primary']};
855
+ color: {theme['text']['inverse']};
856
+ }}
857
+
858
+ /* ============================================
859
+ COLOR PICKER
860
+ ============================================ */
861
+
862
+ .stColorPicker > label {{
863
+ color: {theme['text']['secondary']};
864
+ }}
865
+
866
+ .stColorPicker > div > div {{
867
+ background-color: {theme['backgrounds']['secondary']};
868
+ border: 1px solid {theme['borders']['default']};
869
+ }}
870
+
871
+ /* ============================================
872
+ METRICS (st.metric)
873
+ ============================================ */
874
+
875
+ [data-testid="stMetric"] {{
876
+ background-color: {theme['backgrounds']['secondary']};
877
+ border: 1px solid {theme['borders']['default']};
878
+ border-radius: {theme['border_radius']['lg']};
879
+ padding: {theme['spacing']['md']};
880
+ }}
881
+
882
+ [data-testid="stMetricLabel"] {{
883
+ color: {theme['text']['secondary']};
884
+ font-size: {theme['typography']['font_size_sm']};
885
+ }}
886
+
887
+ [data-testid="stMetricValue"] {{
888
+ color: {theme['text']['primary']};
889
+ font-size: {theme['typography']['font_size_xl']};
890
+ font-weight: {theme['typography']['font_weight_bold']};
891
+ }}
892
+
893
+ [data-testid="stMetricDelta"] {{
894
+ font-size: {theme['typography']['font_size_sm']};
895
+ }}
896
+
897
+ /* Positive delta */
898
+ [data-testid="stMetricDelta"] svg[fill="currentColor"]:first-child {{
899
+ color: {theme['colors']['success']};
900
+ }}
901
+
902
+ /* Negative delta */
903
+ [data-testid="stMetricDelta"] svg[fill="currentColor"]:last-child {{
904
+ color: {theme['colors']['error']};
905
+ }}
906
+
907
+ /* ============================================
908
+ ALERTS & NOTIFICATIONS
909
+ ============================================ */
910
+
911
+ /* Info (st.info) */
912
+ .stAlert[data-baseweb="notification"][kind="info"] {{
913
+ background-color: {theme['colors']['info']}20;
914
+ border-left: 4px solid {theme['colors']['info']};
915
+ color: {theme['text']['primary']};
916
+ }}
917
+
918
+ /* Success (st.success) */
919
+ .stAlert[data-baseweb="notification"][kind="success"] {{
920
+ background-color: {theme['colors']['success']}20;
921
+ border-left: 4px solid {theme['colors']['success']};
922
+ color: {theme['text']['primary']};
923
+ }}
924
+
925
+ /* Warning (st.warning) */
926
+ .stAlert[data-baseweb="notification"][kind="warning"] {{
927
+ background-color: {theme['colors']['warning']}20;
928
+ border-left: 4px solid {theme['colors']['warning']};
929
+ color: {theme['text']['primary']};
930
+ }}
931
+
932
+ /* Error (st.error) */
933
+ .stAlert[data-baseweb="notification"][kind="error"] {{
934
+ background-color: {theme['colors']['error']}20;
935
+ border-left: 4px solid {theme['colors']['error']};
936
+ color: {theme['text']['primary']};
937
+ }}
938
+
939
+ /* ============================================
940
+ LAYOUT CONTAINERS
941
+ ============================================ */
942
+
943
+ /* Expander (st.expander) */
944
+ .stExpander {{
945
+ background-color: {theme['backgrounds']['secondary']};
946
+ border: 1px solid {theme['borders']['default']};
947
+ border-radius: {theme['border_radius']['md']};
948
+ }}
949
+
950
+ .stExpander summary {{
951
+ color: {theme['text']['primary']};
952
+ font-weight: {theme['typography']['font_weight_medium']};
953
+ }}
954
+
955
+ .stExpander summary:hover {{
956
+ background-color: {theme['interactive']['hover_bg']};
957
+ }}
958
+
959
+ /* Tabs (st.tabs) */
960
+ .stTabs [data-baseweb="tab-list"] {{
961
+ background-color: {theme['backgrounds']['secondary']};
962
+ border-bottom: 1px solid {theme['borders']['default']};
963
+ }}
964
+
965
+ .stTabs [data-baseweb="tab"] {{
966
+ color: {theme['text']['secondary']};
967
+ border-color: transparent;
968
+ }}
969
+
970
+ .stTabs [data-baseweb="tab"]:hover {{
971
+ color: {theme['text']['primary']};
972
+ background-color: {theme['interactive']['hover_bg']};
973
+ }}
974
+
975
+ .stTabs [aria-selected="true"] {{
976
+ color: {theme['colors']['primary']};
977
+ border-bottom-color: {theme['colors']['primary']};
978
+ }}
979
+
980
+ /* Columns */
981
+ [data-testid="column"] {{
982
+ background-color: transparent;
983
+ }}
984
+
985
+ /* ============================================
986
+ DATA DISPLAY
987
+ ============================================ */
988
+
989
+ /* DataFrame (st.dataframe) */
990
+ .stDataFrame {{
991
+ border: 1px solid {theme['borders']['default']};
992
+ border-radius: {theme['border_radius']['md']};
993
+ }}
994
+
995
+ .stDataFrame [data-testid="stDataFrameResizable"] {{
996
+ background-color: {theme['backgrounds']['secondary']};
997
+ }}
998
+
999
+ /* Table headers */
1000
+ .stDataFrame thead th {{
1001
+ background-color: {theme['backgrounds']['tertiary']};
1002
+ color: {theme['text']['primary']};
1003
+ border-bottom: 2px solid {theme['borders']['default']};
1004
+ font-weight: {theme['typography']['font_weight_semibold']};
1005
+ }}
1006
+
1007
+ /* Table rows */
1008
+ .stDataFrame tbody td {{
1009
+ background-color: {theme['backgrounds']['secondary']};
1010
+ color: {theme['text']['primary']};
1011
+ border-bottom: 1px solid {theme['borders']['default']};
1012
+ }}
1013
+
1014
+ .stDataFrame tbody tr:hover td {{
1015
+ background-color: {theme['interactive']['hover_bg']};
1016
+ }}
1017
+
1018
+ /* JSON (st.json) */
1019
+ .stJson {{
1020
+ background-color: {theme['backgrounds']['secondary']};
1021
+ border: 1px solid {theme['borders']['default']};
1022
+ border-radius: {theme['border_radius']['md']};
1023
+ color: {theme['text']['primary']};
1024
+ }}
1025
+
1026
+ /* Code (st.code) */
1027
+ .stCodeBlock {{
1028
+ background-color: {theme['backgrounds']['secondary']};
1029
+ border: 1px solid {theme['borders']['default']};
1030
+ border-radius: {theme['border_radius']['md']};
1031
+ }}
1032
+
1033
+ .stCodeBlock code {{
1034
+ color: {theme['text']['primary']};
1035
+ font-family: {theme['typography']['font_family']};
1036
+ }}
1037
+
1038
+ /* ============================================
1039
+ CHARTS
1040
+ ============================================ */
1041
+
1042
+ /* All Streamlit charts */
1043
+ .stVegaLiteChart, .stArrowVegaLiteChart {{
1044
+ background-color: {theme['backgrounds']['secondary']};
1045
+ border-radius: {theme['border_radius']['md']};
1046
+ }}
1047
+
1048
+ /* ============================================
1049
+ PROGRESS & SPINNERS
1050
+ ============================================ */
1051
+
1052
+ /* Progress Bar (st.progress) */
1053
+ .stProgress > div > div {{
1054
+ background-color: {theme['backgrounds']['tertiary']};
1055
+ }}
1056
+
1057
+ .stProgress > div > div > div {{
1058
+ background-color: {theme['colors']['primary']};
1059
+ }}
1060
+
1061
+ /* Spinner (st.spinner) */
1062
+ .stSpinner > div {{
1063
+ border-color: {theme['colors']['primary']};
1064
+ }}
1065
+
1066
+ /* ============================================
1067
+ MARKDOWN & TEXT
1068
+ ============================================ */
1069
+
1070
+ .stMarkdown {{
1071
+ color: {theme['text']['primary']};
1072
+ font-family: {theme['typography']['font_family']};
1073
+ }}
1074
+
1075
+ .stMarkdown h1, .stMarkdown h2, .stMarkdown h3 {{
1076
+ color: {theme['text']['primary']};
1077
+ font-weight: {theme['typography']['font_weight_bold']};
1078
+ }}
1079
+
1080
+ .stMarkdown a {{
1081
+ color: {theme['colors']['primary']};
1082
+ }}
1083
+
1084
+ .stMarkdown a:hover {{
1085
+ color: {theme['colors']['secondary']};
1086
+ }}
1087
+
1088
+ .stMarkdown code {{
1089
+ background-color: {theme['backgrounds']['tertiary']};
1090
+ color: {theme['colors']['secondary']};
1091
+ border-radius: {theme['border_radius']['sm']};
1092
+ padding: 2px 4px;
1093
+ }}
1094
+
1095
+ /* Title (st.title) */
1096
+ .stTitle {{
1097
+ color: {theme['text']['primary']};
1098
+ font-weight: {theme['typography']['font_weight_bold']};
1099
+ }}
1100
+
1101
+ /* Header (st.header) */
1102
+ .stHeader {{
1103
+ color: {theme['text']['primary']};
1104
+ font-weight: {theme['typography']['font_weight_semibold']};
1105
+ }}
1106
+
1107
+ /* Subheader (st.subheader) */
1108
+ .stSubheader {{
1109
+ color: {theme['text']['secondary']};
1110
+ font-weight: {theme['typography']['font_weight_medium']};
1111
+ }}
1112
+
1113
+ /* Caption (st.caption) */
1114
+ .stCaption {{
1115
+ color: {theme['text']['tertiary']};
1116
+ font-size: {theme['typography']['font_size_sm']};
1117
+ }}
1118
+
1119
+ /* ============================================
1120
+ MEDIA
1121
+ ============================================ */
1122
+
1123
+ /* Image (st.image) */
1124
+ .stImage {{
1125
+ border-radius: {theme['border_radius']['md']};
1126
+ }}
1127
+
1128
+ /* Video (st.video) */
1129
+ .stVideo {{
1130
+ border-radius: {theme['border_radius']['md']};
1131
+ }}
1132
+
1133
+ /* ============================================
1134
+ CHAT
1135
+ ============================================ */
1136
+
1137
+ /* Chat message (st.chat_message) */
1138
+ .stChatMessage {{
1139
+ background-color: {theme['backgrounds']['secondary']};
1140
+ border-radius: {theme['border_radius']['lg']};
1141
+ border: 1px solid {theme['borders']['default']};
1142
+ }}
1143
+
1144
+ .stChatMessage [data-testid="chatAvatarIcon"] {{
1145
+ background-color: {theme['colors']['primary']};
1146
+ }}
1147
+
1148
+ /* Chat input (st.chat_input) */
1149
+ .stChatInput > div > div > input {{
1150
+ background-color: {theme['backgrounds']['secondary']};
1151
+ color: {theme['text']['primary']};
1152
+ border: 1px solid {theme['borders']['default']};
1153
+ }}
1154
+
1155
+ /* ============================================
1156
+ SCROLLBARS
1157
+ ============================================ */
1158
+
1159
+ ::-webkit-scrollbar {{
1160
+ width: 8px;
1161
+ height: 8px;
1162
+ }}
1163
+
1164
+ ::-webkit-scrollbar-track {{
1165
+ background: {theme['backgrounds']['secondary']};
1166
+ }}
1167
+
1168
+ ::-webkit-scrollbar-thumb {{
1169
+ background: {theme['backgrounds']['accent']};
1170
+ border-radius: {theme['border_radius']['full']};
1171
+ }}
1172
+
1173
+ ::-webkit-scrollbar-thumb:hover {{
1174
+ background: {theme['borders']['default']};
1175
+ }}
1176
+
1177
+ </style>
1178
+ """
1179
+
1180
+
1181
+ def apply_theme_to_streamlit(theme: Optional[Theme] = None) -> None:
1182
+ """
1183
+ Apply theme to Streamlit standard components via CSS injection.
1184
+ Call this at the top of your Streamlit app.
1185
+
1186
+ Args:
1187
+ theme: Theme to apply. If None, uses active global theme.
1188
+
1189
+ Example:
1190
+ import streamlit as st
1191
+ from streamlit_react_components.themes import set_theme, apply_theme_to_streamlit, OCEAN_THEME
1192
+
1193
+ set_theme(OCEAN_THEME)
1194
+ apply_theme_to_streamlit()
1195
+
1196
+ st.title("My App")
1197
+ st.button("Click me") # Now themed!
1198
+ """
1199
+ import streamlit as st
1200
+
1201
+ resolved_theme = theme if theme is not None else get_active_theme()
1202
+ css = generate_streamlit_css(resolved_theme)
1203
+ st.markdown(css, unsafe_allow_html=True)