Modern styling and theming framework for Qt/PySide and PyQt applications, inspired by shadcn/ui.
QtShadcn loads a local XML theme file containing <light> and <dark> palettes, resolves the design tokens, renders a QSS stylesheet via Jinja2, and applies it to your QApplication in one call.
- Light & dark palettes — single XML file, both modes
- Auto mode — follows the OS theme via
darkdetect - Binding neutral — works with PySide6, PyQt6, PySide2, or PyQt5 via qtpy
- Custom fonts — drop font files in the package
resources/fonts/directory - Disk cache — theme is re-rendered only when the source file changes
- App-provided Qt runtime — install the Qt binding your app already uses
- Themed icons — SVG check icons generated and cached at runtime
- Python >= 3.11
- One of: PySide6, PyQt6, PySide2, or PyQt5 (provided by your application environment)
# Install QtShadcn from PyPI
pip install qtshadcn
# Or with uv
uv add qtshadcnQtShadcn does not bundle a Qt binding. Install the binding your application already uses and
optionally set QT_API to select one when multiple bindings are present:
# PySide6 (recommended)
pip install PySide6
export QT_API=pyside6
# Or PyQt6
pip install PyQt6
export QT_API=pyqt6
# Or PySide2
pip install PySide2
export QT_API=pyside2
# Or PyQt5
pip install PyQt5
export QT_API=pyqt5import sys
from qtpy import QtWidgets
from qtshadcn import setTheme, setThemeMode, getTheme
app = QtWidgets.QApplication(sys.argv)
setThemeMode("auto", save=False) # "auto" | "light" | "dark"
setTheme("path/to/my_theme.xml", save=False)
tokens = getTheme()
print(tokens.primary) # resolved hex color
label = QtWidgets.QLabel("Hello, QtShadcn!")
label.show()
sys.exit(app.exec())Explore the supported widgets by running the gallery:
make galleryThe gallery includes a sidebar navigator, a light/dark toggle, and pages for every currently styled widget.
QtShadcn currently ships QSS for:
QWidget— base background, foreground, and typography classesQLabel— typography and disabled stateQPushButton— variants, sizes, and disabled statesQToolButton— compact icon/action variantsQCheckBox— toggle controls with themed check icons and disabled statesQRadioButton— radio controls with themed checked iconsQLineEdit— input states including focus, disabled, and invalidQTextEdit— textarea states including focus, disabled, and invalidQComboBoxandQFontComboBox— dropdowns, popups, and invalid statesQProgressBar— determinate, thin, and disabled statesQSlider— horizontal, vertical, tick, and disabled statesQGroupBoxandQFrame— layout containers
See the roadmap for what is planned next.
A QtShadcn theme is a plain XML file with two palette sections:
<theme>
<light>
<background>#ffffff</background>
<foreground>#020617</foreground>
<primary>#0f172a</primary>
<primary_foreground>#f8fafc</primary_foreground>
<secondary>#f1f5f9</secondary>
<secondary_foreground>#0f172a</secondary_foreground>
<accent>#f1f5f9</accent>
<accent_foreground>#0f172a</accent_foreground>
<muted>#f1f5f9</muted>
<muted_foreground>#64748b</muted_foreground>
<destructive>#ef4444</destructive>
<destructive_foreground>#f8fafc</destructive_foreground>
<border>#e2e8f0</border>
<input>#e2e8f0</input>
<ring>#0f172a</ring>
<radius>8px</radius>
<font_family>Open Sans</font_family>
<spacing>4px</spacing>
<card>#ffffff</card>
<card_foreground>#020617</card_foreground>
<popover>#ffffff</popover>
<popover_foreground>#020617</popover_foreground>
</light>
<dark>
<!-- same tokens, dark values -->
</dark>
</theme>Unknown tokens are silently ignored so you can extend the format freely.
QtShadcn exposes a small, composable public API directly from the package root:
from qtshadcn import (
qsettings,
ThemeMode,
setThemeMode,
toggleThemeMode,
themeMode,
isDarkTheme,
setTheme,
getTheme,
setStyleSheet,
getStyleSheet,
SystemThemeWatcher,
)Set the active theme mode ("auto", "light", or "dark") and re-render the stylesheet.
Cycle the theme mode: auto → light → dark → auto.
Return the current ThemeMode.
Return whether the resolved active palette is dark.
Load a QtShadcn .xml or .json theme, apply optional token overrides, and re-render the stylesheet.
Return the resolved tokens for the active mode.
Set an additional stylesheet (inline QSS/Jinja string or .qss/.jinja file path) layered on top of the base QSS.
Return the current additional stylesheet content.
Immutable Pydantic model with one field per design token (background, primary, border, radius, font_family, ...). Every token is required in both XML palettes; missing tokens raise ThemeParseError.
- Docs site: https://qtshadcn.readthedocs.io/
- Widget gallery:
examples/gallery/main.py(runmake gallery)
Contributions are welcome. See CONTRIBUTING.md for setup instructions, coding conventions, and architecture rules.
MIT
