CSS avancé pour Streamlit : Créer des apps professionnelles en 2025

CSS avancé pour Streamlit : Créer des apps professionnelles en 2025
Vous en avez marre du design par défaut de Streamlit ? Vous voulez créer des applications avec votre identité visuelle ? Bonne nouvelle : Streamlit offre des capacités de customisation CSS bien plus poussées qu'on ne le pense.
Dans ce guide complet, je vous montre toutes les techniques CSS avancées pour transformer vos apps Streamlit basiques en produits professionnels avec votre branding personnalisé.
Ce que vous allez apprendre
- ✅ Configurer un thème personnalisé via
config.toml - ✅ Injecter du CSS custom pour un contrôle total
- ✅ Utiliser Google Fonts et polices personnalisées
- ✅ Créer des thèmes clair/sombre professionnels
- ✅ Styliser les composants individuellement
- ✅ Optimiser pour mobile et responsive design
- ✅ Créer un design system cohérent
Prérequis : Connaissances de base en Streamlit et CSS.
Table des matières
- Méthode 1 : Theming via config.toml
- Méthode 2 : CSS Injection avancée
- Méthode 3 : Composants HTML/CSS custom
- Polices personnalisées (Google Fonts)
- Thèmes clair/sombre professionnels
- Design system cohérent
- Responsive design mobile
- Exemples de customisation
Méthode 1 : Theming natif avec config.toml
La méthode officielle et recommandée pour customiser Streamlit : le fichier .streamlit/config.toml.
Structure de base
Créez un fichier .streamlit/config.toml à la racine de votre projet :
mon-app/
├── app.py
├── .streamlit/
│ └── config.toml
└── requirements.txt
Configuration complète d'un thème
[theme]
# Couleur primaire (boutons, liens, sélections)
primaryColor = "#FF4B4B"
# Couleur de fond principale
backgroundColor = "#FFFFFF"
# Couleur de fond secondaire (widgets, code blocks)
secondaryBackgroundColor = "#F0F2F6"
# Couleur du texte
textColor = "#262730"
# Police par défaut
font = "sans serif"
Toutes les propriétés disponibles
Couleurs principales
[theme]
# Couleur d'accent (boutons, hover, focus)
primaryColor = "#FF4B4B"
# Fond principal de l'app
backgroundColor = "#FFFFFF"
# Fond des éléments (inputs, code blocks)
secondaryBackgroundColor = "#F0F2F6"
# Couleur du texte
textColor = "#262730"
# Couleur des liens
linkColor = "#FF4B4B"
# Souligner les liens ?
linkUnderline = true
# Couleur du texte dans le code inline
codeTextColor = "#E83E8C"
# Fond des code blocks
codeBackgroundColor = "#F8F9FA"
Bordures et radius
[theme]
# Couleur des bordures (widgets non focus)
borderColor = "#E6E9EF"
# Couleur bordure dataframes
dataframeBorderColor = "#E1E4E8"
# Afficher bordures des widgets ?
showWidgetBorder = true
# Radius général (boutons, inputs, code)
baseRadius = "0.5rem"
# Radius spécifique boutons
buttonRadius = "0.5rem"
Fonts et tailles
[theme]
# Police par défaut
font = "sans serif"
# Police des titres
headingFont = "sans serif"
# Police du code
codeFont = "monospace"
# Taille de base
baseFontSize = "16px"
# Poids de base
baseFontWeight = 400
# Taille code
codeFontSize = "14px"
Formats de couleurs acceptés
Streamlit accepte 4 formats :
# 1. Noms CSS
primaryColor = "darkBlue"
# 2. HEX
primaryColor = "#483D8B"
# 3. RGB
primaryColor = "rgb(106, 90, 205)"
# 4. HSL
primaryColor = "hsl(248, 53%, 58%)"
Exemple : Thème corporate moderne
[theme]
# Identité visuelle entreprise
primaryColor = "#0066CC" # Bleu corporate
backgroundColor = "#FAFBFC" # Gris très clair
secondaryBackgroundColor = "#FFFFFF" # Blanc pur
textColor = "#1F2937" # Gris foncé
# Design moderne
baseRadius = "0.75rem" # Coins arrondis
buttonRadius = "9999px" # Boutons pilules
showWidgetBorder = false # Sans bordures
# Typographie professionnelle
font = "Inter:https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"
headingFont = "Inter:https://fonts.googleapis.com/css2?family=Inter:wght@700&display=swap"
baseFontSize = "16px"
baseFontWeight = 400
Palette de couleurs étendue
Streamlit supporte une palette de 7 couleurs pour les éléments markdown, métriques et alertes :
[theme]
# Palette complète
red = "#FF4B4B"
redBackground = "#FFF0F0"
redText = "#CC0000"
orange = "#FF8C00"
orangeBackground = "#FFF4E6"
orangeText = "#CC6600"
yellow = "#FFD700"
yellowBackground = "#FFFBEB"
yellowText = "#996600"
green = "#00CC66"
greenBackground = "#F0FFF4"
greenText = "#008844"
blue = "#0066CC"
blueBackground = "#E6F2FF"
blueText = "#004499"
violet = "#9966FF"
violetBackground = "#F5F0FF"
violetText = "#6633CC"
gray = "#6B7280"
grayBackground = "#F9FAFB"
grayText = "#374151"
Ces couleurs affectent :
st.success(),st.error(),st.warning(),st.info()- Colonnes de dataframes colorées
- Avatars et badges
- Markdown avec couleurs
Méthode 2 : CSS Injection avancée
Pour un contrôle total, injectez du CSS personnalisé directement dans votre app.
Injection CSS globale
import streamlit as st
def load_custom_css():
st.markdown("""
<style>
/* Customiser les boutons */
.stButton>button {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border-radius: 25px;
padding: 0.75rem 2rem;
font-weight: 600;
border: none;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
transition: all 0.3s ease;
}
.stButton>button:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.6);
}
/* Customiser les inputs */
.stTextInput>div>div>input {
border: 2px solid #E5E7EB;
border-radius: 0.5rem;
padding: 0.75rem;
font-size: 1rem;
}
.stTextInput>div>div>input:focus {
border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}
/* Masquer le menu Streamlit */
#MainMenu {visibility: hidden;}
/* Masquer le footer */
footer {visibility: hidden;}
/* Customiser les titres */
h1 {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
font-weight: 800;
font-size: 3rem;
}
/* Cards personnalisées */
.custom-card {
background: white;
border-radius: 1rem;
padding: 2rem;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
border: 1px solid #E5E7EB;
}
/* Sidebar personnalisée */
[data-testid="stSidebar"] {
background: linear-gradient(180deg, #667eea 0%, #764ba2 100%);
}
[data-testid="stSidebar"] * {
color: white !important;
}
</style>
""", unsafe_allow_html=True)
# Appeler au début de l'app
load_custom_css()
st.title("Mon App Stylisée")
st.button("Bouton Custom")
Cibler des composants spécifiques
Utilisez les attributs data-testid pour cibler précisément :
st.markdown("""
<style>
/* Métrique */
[data-testid="stMetricValue"] {
font-size: 2.5rem;
font-weight: 700;
color: #667eea;
}
/* Selectbox */
[data-testid="stSelectbox"] {
background: white;
border-radius: 0.5rem;
}
/* Dataframe */
[data-testid="stDataFrame"] {
border: 2px solid #E5E7EB;
border-radius: 0.75rem;
overflow: hidden;
}
/* Tabs */
[data-testid="stTab"] {
font-weight: 600;
font-size: 1.1rem;
}
/* Chat input */
[data-testid="stChatInput"] input {
border-radius: 25px;
border: 2px solid #667eea;
}
</style>
""", unsafe_allow_html=True)
Inspecter les classes CSS Streamlit
Pour trouver les classes CSS à customiser :
- Ouvrez l'app dans Chrome/Firefox
- Clic droit > Inspecter (F12)
- Sélectionnez l'élément à styliser
- Regardez les classes CSS dans l'inspecteur
Principales classes :
.stButton>button- Boutons.stTextInput>div>div>input- Text inputs.stSelectbox- Select dropdowns.stDataFrame- Dataframes.stMetric- Métriques.stTabs- Tabs[data-testid="stSidebar"]- Sidebar
Créer des composants stylisés réutilisables
import streamlit as st
def styled_card(title, content, color="#667eea"):
"""Card stylisée réutilisable"""
st.markdown(f"""
<div style="
background: white;
border-radius: 1rem;
padding: 1.5rem;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
border-left: 4px solid {color};
margin: 1rem 0;
">
<h3 style="margin: 0 0 1rem 0; color: {color};">{title}</h3>
<p style="margin: 0; color: #4B5563;">{content}</p>
</div>
""", unsafe_allow_html=True)
def styled_metric(label, value, delta=None):
"""Métrique stylisée custom"""
delta_html = ""
if delta:
color = "#10B981" if delta > 0 else "#EF4444"
symbol = "↑" if delta > 0 else "↓"
delta_html = f'<span style="color: {color};">{symbol} {abs(delta)}%</span>'
st.markdown(f"""
<div style="
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border-radius: 0.75rem;
padding: 1.5rem;
text-align: center;
">
<div style="font-size: 0.875rem; opacity: 0.9;">{label}</div>
<div style="font-size: 2.5rem; font-weight: 700; margin: 0.5rem 0;">{value}</div>
{delta_html}
</div>
""", unsafe_allow_html=True)
# Utilisation
styled_card("Titre", "Contenu de la card", "#FF4B4B")
styled_metric("Ventes", "42,500€", delta=12.5)
Polices personnalisées (Google Fonts)
Méthode 1 : Google Fonts via config.toml
La méthode la plus simple :
[theme]
# Format : "NomFont:URL_CSS"
font = "Inter:https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"
headingFont = "Poppins:https://fonts.googleapis.com/css2?family=Poppins:wght@700;800&display=swap"
codeFont = "Fira Code:https://fonts.googleapis.com/css2?family=Fira+Code&display=swap"
Polices recommandées :
# Moderne et professionnelle
font = "Inter:https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
# Corporate et élégante
font = "Montserrat:https://fonts.googleapis.com/css2?family=Montserrat:wght@400;600&display=swap"
# Technique et lisible
font = "Roboto:https://fonts.googleapis.com/css2?family=Roboto:wght@400;500&display=swap"
# Créative et moderne
font = "Poppins:https://fonts.googleapis.com/css2?family=Poppins:wght@400;600&display=swap"
Méthode 2 : Google Fonts via CSS injection
import streamlit as st
st.markdown("""
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
html, body, [class*="css"] {
font-family: 'Inter', sans-serif;
}
h1, h2, h3, h4, h5, h6 {
font-family: 'Inter', sans-serif;
font-weight: 700;
}
code {
font-family: 'Fira Code', monospace;
}
</style>
""", unsafe_allow_html=True)
Méthode 3 : Polices locales (self-hosted)
Pour héberger vos propres fichiers de police :
1. Structure du projet :
mon-app/
├── app.py
├── .streamlit/
│ └── config.toml
└── static/
└── fonts/
├── custom-font.woff2
└── custom-font.woff
2. Configuration config.toml :
[server]
# Activer le serveur de fichiers statiques
enableStaticServing = true
[theme]
# Déclarer la police custom
[[theme.fontFaces]]
family = "CustomFont"
url = "app/static/fonts/custom-font.woff2"
weight = 400
style = "normal"
[[theme.fontFaces]]
family = "CustomFont"
url = "app/static/fonts/custom-font-bold.woff2"
weight = 700
style = "normal"
# Utiliser la police
font = "CustomFont, sans-serif"
Note : Formats supportés : OTF, TTF, WOFF, WOFF2
Thèmes clair/sombre professionnels
Configuration thèmes séparés
[theme]
# Paramètres globaux
base = "light" # Thème par défaut
# Thème clair
[theme.light]
primaryColor = "#667eea"
backgroundColor = "#FFFFFF"
secondaryBackgroundColor = "#F9FAFB"
textColor = "#1F2937"
# Thème sombre
[theme.dark]
primaryColor = "#818CF8"
backgroundColor = "#111827"
secondaryBackgroundColor = "#1F2937"
textColor = "#F9FAFB"
Exemple complet : Design moderne dual-theme
[theme]
base = "light"
font = "Inter:https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
baseFontSize = "16px"
baseRadius = "0.75rem"
# === LIGHT THEME ===
[theme.light]
primaryColor = "#667eea"
backgroundColor = "#FAFBFC"
secondaryBackgroundColor = "#FFFFFF"
textColor = "#1F2937"
linkColor = "#667eea"
borderColor = "#E5E7EB"
codeBackgroundColor = "#F3F4F6"
# === DARK THEME ===
[theme.dark]
primaryColor = "#818CF8"
backgroundColor = "#0F172A"
secondaryBackgroundColor = "#1E293B"
textColor = "#F1F5F9"
linkColor = "#A5B4FC"
borderColor = "#334155"
codeBackgroundColor = "#0F172A"
Détection automatique du thème système
Streamlit détecte automatiquement le thème de l'OS si base n'est pas spécifié, ou si l'utilisateur toggle le mode dans l'app.
Design System cohérent
Créez un design system complet pour vos apps Streamlit :
Structure modulaire
# styles.py - Votre design system
import streamlit as st
class DesignSystem:
"""Design system cohérent pour toutes vos apps"""
# Palette de couleurs
COLORS = {
'primary': '#667eea',
'secondary': '#764ba2',
'success': '#10B981',
'danger': '#EF4444',
'warning': '#F59E0B',
'info': '#3B82F6',
'gray': {
'50': '#F9FAFB',
'100': '#F3F4F6',
'500': '#6B7280',
'900': '#111827'
}
}
# Spacing
SPACING = {
'xs': '0.25rem',
'sm': '0.5rem',
'md': '1rem',
'lg': '1.5rem',
'xl': '2rem',
'2xl': '3rem'
}
# Border radius
RADIUS = {
'sm': '0.25rem',
'md': '0.5rem',
'lg': '0.75rem',
'full': '9999px'
}
@staticmethod
def load_css():
"""Charge le CSS global"""
st.markdown(f"""
<style>
:root {{
--color-primary: {DesignSystem.COLORS['primary']};
--color-secondary: {DesignSystem.COLORS['secondary']};
--spacing-md: {DesignSystem.SPACING['md']};
}}
.stButton>button {{
background: linear-gradient(135deg,
{DesignSystem.COLORS['primary']} 0%,
{DesignSystem.COLORS['secondary']} 100%);
color: white;
border-radius: {DesignSystem.RADIUS['full']};
padding: {DesignSystem.SPACING['sm']} {DesignSystem.SPACING['lg']};
font-weight: 600;
border: none;
transition: all 0.3s ease;
}}
.stButton>button:hover {{
transform: translateY(-2px);
box-shadow: 0 10px 25px rgba(102, 126, 234, 0.3);
}}
</style>
""", unsafe_allow_html=True)
@staticmethod
def card(title, content, color='primary'):
"""Composant Card réutilisable"""
color_value = DesignSystem.COLORS.get(color, color)
st.markdown(f"""
<div style="
background: white;
border-radius: {DesignSystem.RADIUS['lg']};
padding: {DesignSystem.SPACING['lg']};
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
border-left: 4px solid {color_value};
margin: {DesignSystem.SPACING['md']} 0;
">
<h3 style="margin: 0 0 {DesignSystem.SPACING['sm']} 0; color: {color_value};">
{title}
</h3>
<p style="margin: 0; color: {DesignSystem.COLORS['gray']['900']};">
{content}
</p>
</div>
""", unsafe_allow_html=True)
Utilisation dans votre app
# app.py
import streamlit as st
from styles import DesignSystem
# Charger le design system
DesignSystem.load_css()
st.title("Mon App avec Design System")
# Utiliser les composants
DesignSystem.card("Succès", "Votre action a réussi !", color='success')
DesignSystem.card("Attention", "Vérifiez ces données", color='warning')
st.button("Action Primaire") # Utilise automatiquement le style défini
Responsive Design mobile
Streamlit est responsive par défaut, mais vous pouvez l'optimiser :
Media queries CSS
st.markdown("""
<style>
/* Desktop */
@media (min-width: 768px) {
.stButton>button {
font-size: 1.125rem;
padding: 1rem 2rem;
}
h1 {
font-size: 3rem;
}
}
/* Mobile */
@media (max-width: 767px) {
.stButton>button {
font-size: 1rem;
padding: 0.75rem 1.5rem;
width: 100%;
}
h1 {
font-size: 2rem;
}
/* Masquer sidebar sur mobile par défaut */
[data-testid="stSidebar"] {
display: none;
}
}
/* Tablet */
@media (min-width: 768px) and (max-width: 1024px) {
.stDataFrame {
font-size: 0.875rem;
}
}
</style>
""", unsafe_allow_html=True)
Colonnes responsives
import streamlit as st
# Desktop : 3 colonnes | Mobile : 1 colonne
col1, col2, col3 = st.columns([1, 1, 1])
with col1:
st.metric("Metric 1", "1,234")
with col2:
st.metric("Metric 2", "5,678")
with col3:
st.metric("Metric 3", "9,012")
# CSS responsive
st.markdown("""
<style>
@media (max-width: 767px) {
[data-testid="column"] {
width: 100% !important;
flex: 100% !important;
}
}
</style>
""", unsafe_allow_html=True)
Exemples de customisation complète
Exemple 1 : App corporate moderne
config.toml :
[theme]
primaryColor = "#0066CC"
backgroundColor = "#FAFBFC"
secondaryBackgroundColor = "#FFFFFF"
textColor = "#1F2937"
font = "Inter:https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap"
baseFontSize = "16px"
baseRadius = "0.75rem"
buttonRadius = "9999px"
showWidgetBorder = false
app.py :
import streamlit as st
st.markdown("""
<style>
.stButton>button {
background: #0066CC;
color: white;
font-weight: 600;
box-shadow: 0 4px 6px rgba(0, 102, 204, 0.2);
}
.stButton>button:hover {
background: #0052A3;
box-shadow: 0 6px 12px rgba(0, 102, 204, 0.3);
}
h1 {
color: #0066CC;
font-weight: 700;
border-bottom: 3px solid #0066CC;
padding-bottom: 0.5rem;
}
</style>
""", unsafe_allow_html=True)
st.title("Dashboard Corporate")
st.button("Action Principale")
Exemple 2 : App SaaS moderne (gradient)
import streamlit as st
st.markdown("""
<style>
/* Fond gradient */
.main {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
/* Container blanc sur gradient */
[data-testid="stAppViewContainer"] > div:first-child {
background: white;
border-radius: 1rem;
padding: 2rem;
margin: 2rem;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
}
/* Boutons glassmorphism */
.stButton>button {
background: rgba(255, 255, 255, 0.2);
backdrop-filter: blur(10px);
color: white;
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 15px;
font-weight: 600;
}
.stButton>button:hover {
background: rgba(255, 255, 255, 0.3);
transform: translateY(-2px);
}
/* Titres gradient */
h1 {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
font-weight: 800;
}
</style>
""", unsafe_allow_html=True)
st.title("SaaS App Design")
st.button("Get Started")
Exemple 3 : App minimaliste (style Apple)
# config.toml
[theme]
primaryColor = "#007AFF"
backgroundColor = "#FFFFFF"
secondaryBackgroundColor = "#F2F2F7"
textColor = "#000000"
font = "SF Pro Display:-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif"
baseFontSize = "17px"
baseRadius = "12px"
showWidgetBorder = false
# app.py
import streamlit as st
st.markdown("""
<style>
/* Typographie Apple */
body {
-webkit-font-smoothing: antialiased;
}
/* Boutons style Apple */
.stButton>button {
background: #007AFF;
color: white;
font-weight: 500;
letter-spacing: -0.015em;
box-shadow: none;
}
/* Cards avec effet depth */
.element-container {
background: white;
border-radius: 12px;
box-shadow: 0 0 0 1px rgba(0,0,0,0.05);
}
/* Spacing Apple-like */
h1, h2, h3 {
font-weight: 600;
letter-spacing: -0.02em;
}
</style>
""", unsafe_allow_html=True)
Outils et ressources
Générateurs de thèmes
- Streamlit Theme Maker - Créateur officiel
- Coolors.co - Générateur de palettes
- Google Fonts - Catalogue de polices
Inspecteur d'éléments
Utilisez les DevTools du navigateur :
- F12 > Inspecteur
- Sélectionnez un élément
- Copiez la classe CSS
- Stylez dans votre code
Tester le responsive
Chrome DevTools > Toggle device toolbar (Ctrl+Shift+M)
Limites et bonnes pratiques
⚠️ Limitations
- Pas d'accès direct au HTML : Streamlit génère le HTML
- Classes CSS changent : Entre versions Streamlit
- Performance : Éviter trop de CSS inline
- Maintenance : Tester après chaque upgrade Streamlit
✅ Bonnes pratiques
- Préférez config.toml pour le theming de base
- CSS injection seulement si nécessaire
- Testez sur mobile avant déploiement
- Versionning : Fixez la version Streamlit en prod
- Design system : Centralisez vos styles
Conclusion
Vous savez maintenant tout sur le CSS avancé dans Streamlit :
✅ Theming natif via config.toml
✅ CSS injection pour contrôle total
✅ Google Fonts et polices custom
✅ Thèmes clair/sombre professionnels
✅ Design system modulaire
✅ Responsive design mobile
Ma recommandation :
- Commencez par
config.toml(80% des besoins) - Ajoutez du CSS custom si nécessaire (20%)
- Créez un design system réutilisable
90% de vos besoins peuvent être satisfaits avec config.toml + quelques lignes de CSS.
Vous voulez maîtriser Streamlit de A à Z ? Découvrez ma formation Streamlit Unleashed : CSS avancé, authentification, architecture professionnelle et 15 projets concrets. De débutant à expert en 20h.
Besoin d'audit de votre app ? Testez gratuitement Roast My Streamlit - analyse de design et recommandations personnalisées en 2 minutes.
Découvrez aussi :

📚 Approfondir avec mon livre
"Business Intelligence avec Python" - Le guide complet pour maîtriser l'analyse de données
Voir sur Amazon →