What Is QSS?
Qt Style Sheets (QSS) is a subset of CSS applied to Qt widgets. You can style colors, borders, fonts, spacing, and pseudo-states (:hover, :pressed, :checked, :disabled).
/* Apply to entire app */
QWidget {
background-color: #1e1e2e;
color: #cdd6f4;
font-family: 'Fira Code', monospace;
font-size: 13px;
}
// Apply globally
qApp->setStyleSheet(qssString);
// Apply to one widget (and its children)
window->setStyleSheet(qssString);
// Apply to one widget only
btn->setStyleSheet("QPushButton { background: #89b4fa; }");
Selectors
/* Type selector */
QPushButton { background: #313244; }
/* Class selector (widget objectName) */
QPushButton#dangerBtn { background: #f38ba8; }
/* Descendant selector */
QDialog QPushButton { border-radius: 4px; }
/* Property selector */
QLabel[status="error"] { color: #f38ba8; }
QLabel[status="ok"] { color: #a6e3a1; }
// Set property for CSS selection
label->setProperty("status", "error");
label->style()->unpolish(label); // force re-style
label->style()->polish(label);
Pseudo-States
QPushButton {
background: #313244;
border: 1px solid #45475a;
border-radius: 8px;
padding: 6px 16px;
color: #cdd6f4;
}
QPushButton:hover { background: #45475a; }
QPushButton:pressed { background: #89b4fa; color: #1e1e2e; }
QPushButton:disabled { background: #181825; color: #585b70; }
QPushButton:checked { background: #89b4fa; color: #1e1e2e; border-color: #89b4fa; }
QPushButton:focus { border-color: #89b4fa; outline: none; }
Common Widget Styles
QLineEdit
QLineEdit {
background: #181825;
border: 1px solid #45475a;
border-radius: 6px;
padding: 4px 8px;
color: #cdd6f4;
selection-background-color: #89b4fa;
}
QLineEdit:focus {
border-color: #89b4fa;
}
QLineEdit:read-only {
background: #1e1e2e;
color: #6c7086;
}
QComboBox
QComboBox {
background: #313244;
border: 1px solid #45475a;
border-radius: 6px;
padding: 4px 8px;
color: #cdd6f4;
min-width: 100px;
}
QComboBox::drop-down {
border: none;
width: 20px;
}
QComboBox::down-arrow {
image: url(:/icons/chevron-down.svg);
width: 12px; height: 12px;
}
QComboBox QAbstractItemView {
background: #313244;
border: 1px solid #45475a;
selection-background-color: #89b4fa;
selection-color: #1e1e2e;
}
QScrollBar
QScrollBar:vertical {
background: #181825;
width: 8px;
border-radius: 4px;
}
QScrollBar::handle:vertical {
background: #45475a;
min-height: 30px;
border-radius: 4px;
}
QScrollBar::handle:vertical:hover {
background: #6c7086;
}
QScrollBar::add-line, QScrollBar::sub-line { height: 0; }
QProgressBar
QProgressBar {
background: #181825;
border: 1px solid #313244;
border-radius: 6px;
text-align: center;
color: #cdd6f4;
height: 12px;
}
QProgressBar::chunk {
background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
stop:0 #89b4fa, stop:1 #a6e3a1);
border-radius: 5px;
}
QTabWidget
QTabWidget::pane {
border: 1px solid #313244;
border-radius: 0 8px 8px 8px;
background: #1e1e2e;
}
QTabBar::tab {
background: #181825;
color: #6c7086;
padding: 6px 16px;
border-radius: 6px 6px 0 0;
}
QTabBar::tab:selected {
background: #1e1e2e;
color: #cdd6f4;
border-bottom: 2px solid #89b4fa;
}
QTabBar::tab:hover { color: #cdd6f4; }
Visual: QSS Box Model
┌──────────────────────────────────────┐
│ margin │
│ ┌────────────────────────────────┐ │
│ │ border │ │
│ │ ┌──────────────────────────┐ │ │
│ │ │ padding │ │ │
│ │ │ ┌────────────────────┐ │ │ │
│ │ │ │ content area │ │ │ │
│ │ │ └────────────────────┘ │ │ │
│ │ └──────────────────────────┘ │ │
│ └────────────────────────────────┘ │
└──────────────────────────────────────┘
Dark & Light Theme System
class ThemeManager : public QObject {
Q_OBJECT
public:
enum Theme { Dark, Light };
static ThemeManager& instance() {
static ThemeManager t; return t;
}
void setTheme(Theme theme) {
m_current = theme;
qApp->setStyleSheet(theme == Dark ? darkQss() : lightQss());
emit themeChanged(theme);
}
Theme current() const { return m_current; }
signals:
void themeChanged(Theme t);
private:
Theme m_current = Dark;
static QString darkQss() {
return R"(
QWidget { background:#1e1e2e; color:#cdd6f4; font-size:13px; }
QPushButton {
background:#313244; border:1px solid #45475a;
border-radius:8px; padding:6px 16px; color:#cdd6f4;
}
QPushButton:hover { background:#45475a; }
QPushButton:pressed { background:#89b4fa; color:#1e1e2e; }
QLineEdit {
background:#181825; border:1px solid #45475a;
border-radius:6px; padding:4px 8px; color:#cdd6f4;
}
QLineEdit:focus { border-color:#89b4fa; }
QLabel { color:#cdd6f4; }
QFrame { border:1px solid #313244; }
)";
}
static QString lightQss() {
return R"(
QWidget { background:#eff1f5; color:#4c4f69; font-size:13px; }
QPushButton {
background:#dce0e8; border:1px solid #ccd0da;
border-radius:8px; padding:6px 16px; color:#4c4f69;
}
QPushButton:hover { background:#ccd0da; }
QPushButton:pressed { background:#1e66f5; color:#eff1f5; }
QLineEdit {
background:#ffffff; border:1px solid #ccd0da;
border-radius:6px; padding:4px 8px; color:#4c4f69;
}
QLineEdit:focus { border-color:#1e66f5; }
)";
}
};
// Toggle in app
connect(themeBtn, &QPushButton::clicked, [](){
auto &tm = ThemeManager::instance();
tm.setTheme(tm.current() == ThemeManager::Dark
? ThemeManager::Light : ThemeManager::Dark);
});
QPalette — System Colors
#include <QPalette>
// Set custom palette (alternative to QSS for system-level theming)
QPalette p = qApp->palette();
p.setColor(QPalette::Window, QColor("#1e1e2e"));
p.setColor(QPalette::Base, QColor("#181825"));
p.setColor(QPalette::Text, QColor("#cdd6f4"));
p.setColor(QPalette::Button, QColor("#313244"));
p.setColor(QPalette::ButtonText,QColor("#cdd6f4"));
p.setColor(QPalette::Highlight, QColor("#89b4fa"));
p.setColor(QPalette::HighlightedText, QColor("#1e1e2e"));
qApp->setPalette(p);
Load QSS from File
// Style from embedded resource
QFile f(":/styles/dark.qss");
if (f.open(QFile::ReadOnly | QFile::Text)) {
QTextStream ts(&f);
qApp->setStyleSheet(ts.readAll());
}
Lab — Themed Settings Panel
class SettingsPanel : public QWidget {
Q_OBJECT
public:
SettingsPanel(QWidget *p = nullptr) : QWidget(p) {
setObjectName("settingsPanel");
auto *lay = new QVBoxLayout(this);
lay->setSpacing(12);
lay->setContentsMargins(20, 20, 20, 20);
// Title
auto *title = new QLabel("Device Settings");
title->setObjectName("panelTitle");
title->setStyleSheet("font-size:18px; font-weight:bold; "
"color:#89b4fa; margin-bottom:8px;");
// Form
auto *form = new QFormLayout;
form->setSpacing(8);
auto *portEdit = new QLineEdit("/dev/ttyUSB0");
auto *baudBox = new QComboBox;
baudBox->addItems({"9600","19200","38400","115200","921600"});
baudBox->setCurrentText("115200");
auto *themeBtn = new QPushButton("Toggle Theme");
themeBtn->setObjectName("themeBtn");
auto *saveBtn = new QPushButton("Save");
saveBtn->setObjectName("primaryBtn");
saveBtn->setStyleSheet(
"QPushButton#primaryBtn { background:#89b4fa; color:#1e1e2e; "
"font-weight:bold; } "
"QPushButton#primaryBtn:hover { background:#b4d0f8; }");
form->addRow("Serial Port:", portEdit);
form->addRow("Baud Rate:", baudBox);
lay->addWidget(title);
lay->addLayout(form);
lay->addWidget(themeBtn);
lay->addStretch();
lay->addWidget(saveBtn);
connect(themeBtn, &QPushButton::clicked, []{
auto &tm = ThemeManager::instance();
tm.setTheme(tm.current() == ThemeManager::Dark
? ThemeManager::Light : ThemeManager::Dark);
});
}
};
Interview Questions
Q1: What is the difference between QSS and QPalette?
QPalettecontrols system-level color roles (text, background, highlight).QSSis more powerful — CSS-like selectors, pseudo-states, box model, borders, images. QSS overrides QPalette for styled widgets. Use QPalette for system integration (e.g., OS dark mode detection); use QSS for custom app themes.
Q2: Why do you need style()->unpolish() and style()->polish() after changing a property used in QSS?
Qt caches style computations. After changing a dynamic property, you must tell Qt to re-evaluate the style for that widget by calling
unpolish()thenpolish()on its style engine.
Q3: How do you apply a style to only one widget without affecting children?
Use the type selector with a specific
objectName:QPushButton#myBtn { ... }. This targets only the widget with that objectName. Or, usesetStyleSheet("QPushButton { ... }")on a child-less widget.
Q4: What are the performance implications of QSS?
QSS parsing and application has overhead — especially on large widget trees. Prefer applying the style sheet at the app level once, rather than per-widget. Avoid wildcard selectors (
* { ... }) on large trees. For embedded/low-power targets, consider usingQPaletteor customQStyleinstead.
Q5: How do you detect the OS dark/light mode and apply the correct theme?
Check
QPalette::window().color().lightness()— if < 128, the system is in dark mode. On Qt 6.5+, useQGuiApplication::styleHints()->colorScheme()which returnsQt::ColorScheme::DarkorLight. Connect toQGuiApplication::styleHints()->colorSchemeChanged()to react dynamically.
Application: Catppuccin-Themed Device Dashboard
class CatppuccinDashboard : public QMainWindow {
Q_OBJECT
public:
CatppuccinDashboard(QWidget *p = nullptr) : QMainWindow(p) {
setWindowTitle("Device Dashboard");
resize(1000, 650);
// Apply Catppuccin Mocha theme
setStyleSheet(R"(
QMainWindow, QWidget {
background:#1e1e2e; color:#cdd6f4;
font-family:'Segoe UI',sans-serif; font-size:13px;
}
QMenuBar { background:#181825; padding:2px; }
QMenuBar::item { padding:4px 12px; border-radius:4px; }
QMenuBar::item:selected { background:#313244; }
QMenu { background:#313244; border:1px solid #45475a; }
QMenu::item:selected { background:#89b4fa; color:#1e1e2e; }
QPushButton {
background:#313244; border:1px solid #45475a;
border-radius:8px; padding:6px 16px;
}
QPushButton:hover { background:#45475a; }
QPushButton:pressed { background:#89b4fa; color:#1e1e2e; }
QGroupBox {
border:1px solid #313244; border-radius:8px;
margin-top:16px; padding:8px;
font-weight:bold; color:#89b4fa;
}
QGroupBox::title {
subcontrol-origin:margin; left:10px; padding:0 4px;
}
QStatusBar { background:#181825; color:#6c7086; }
QStatusBar::item { border:none; }
QTabWidget::pane { border:1px solid #313244; border-radius:0 8px 8px 8px; }
QTabBar::tab { background:#181825; color:#6c7086; padding:6px 20px; }
QTabBar::tab:selected { background:#1e1e2e; color:#cdd6f4; border-bottom:2px solid #89b4fa; }
QProgressBar {
background:#181825; border:1px solid #313244;
border-radius:6px; height:10px;
}
QProgressBar::chunk {
background:qlineargradient(x1:0,y1:0,x2:1,y2:0,
stop:0 #89b4fa, stop:1 #a6e3a1);
border-radius:5px;
}
)");
// Build UI
auto *central = new QWidget;
setCentralWidget(central);
auto *grid = new QGridLayout(central);
grid->setSpacing(16);
grid->setContentsMargins(16,16,16,16);
// Stats group
auto *statsGroup = new QGroupBox("Live Readings");
auto *statsLay = new QHBoxLayout(statsGroup);
auto mkStat = [&](const QString &label, const QString &value, const QString &color) {
auto *frame = new QFrame;
frame->setStyleSheet(QString("QFrame{background:#313244;border-radius:10px;padding:8px;}"
"QLabel{color:%1;}").arg(color));
auto *vl = new QVBoxLayout(frame);
auto *lbl = new QLabel(label); lbl->setAlignment(Qt::AlignCenter);
auto *val = new QLabel(value);
val->setFont(QFont("Segoe UI", 24, QFont::Bold));
val->setAlignment(Qt::AlignCenter);
vl->addWidget(val); vl->addWidget(lbl);
return frame;
};
statsLay->addWidget(mkStat("Temperature", "36.5 °C", "#fab387"));
statsLay->addWidget(mkStat("Voltage", "3.30 V", "#a6e3a1"));
statsLay->addWidget(mkStat("Humidity", "60 %", "#89dceb"));
statsLay->addWidget(mkStat("Status", "Online", "#a6e3a1"));
grid->addWidget(statsGroup, 0, 0, 1, 2);
statusBar()->showMessage("Connected to /dev/ttyUSB0 @ 115200");
}
};
References
| Resource | Link |
|---|---|
| Qt Docs: Style Sheets | https://doc.qt.io/qt-6/stylesheet.html |
| Qt Docs: Style Sheet Reference | https://doc.qt.io/qt-6/stylesheet-reference.html |
| Qt Docs: QPalette | https://doc.qt.io/qt-6/qpalette.html |
| Qt Docs: QStyle | https://doc.qt.io/qt-6/qstyle.html |
Next tutorial → Qt Testing (QTest)