UI Components#
YASB includes a built-in UI component system based on WinUI3 design tokens. These components handle theming, animations, and accessibility automatically. Use them when building widgets that need dialogs, buttons, toggles, or other interactive elements.
All components live in src/core/ui/components/ and share the same token-based theming from src/core/ui/tokens.py.
Components#
| Component | Import | Description |
|---|---|---|
| Button | core.ui.components.button |
Button with default, accent and subtle variants. |
| DropDownButton | core.ui.components.drop_down_button |
Button that opens a menu of actions. |
| Link | core.ui.components.link |
Hyperlink-style button. |
| TextBlock | core.ui.components.text_block |
Label with preset typography variants. |
| TextBox | core.ui.components.text_box |
Single-line text input with optional icon. |
| ToggleSwitch | core.ui.components.toggle_switch |
On/off switch, with or without a text label. |
| Slider | core.ui.components.slider |
Horizontal slider with a value label. |
| DropDown | core.ui.components.dropdown |
Selector with a popup list. |
| Card | core.ui.components.card |
Container with hover and selection states. |
| InfoBar | core.ui.components.info_bar |
Inline notification with a severity icon. |
| ContentDialog | core.ui.components.content_dialog |
Modal dialog with up to three buttons. |
| InputDialog | core.ui.components.input_dialog |
Modal dialog with a text field. |
| StepIndicator | core.ui.components.indicator |
Dash-style step indicator. |
| Loader | core.ui.components.loader |
Spinner and LoaderLine loading indicators. |
Every constructor takes an optional parent argument. Signatures below list the arguments in order, with their defaults.
Button#
Button(
text="",
variant="default",
padding=None,
font_family=None,
font_size=None,
font_weight=None,
parent=None,
)
variant:"default","accent"or"subtle".padding:"l,t,r,b","h,v"or"all". Default"11,5,11,6".font_family: comma-separated family names.font_size: pixel size. Default14.font_weight:"thin","light","normal","medium","demibold"or"bold".- Methods:
set_variant(variant), plus the usualsetIcon()andsetIconSize().
save_btn = Button("Save", variant="accent", parent=self)
save_btn.clicked.connect(self._on_save)
DropDownButton#
A Button that opens a menu of actions.
DropDownButton(
text="",
icon_svg=None,
items=None,
variant="default",
chevron=True,
parent=None,
)
icon_svg: SVG markup drawn in the button's text color.items: a list of(key, label)or(key, label, icon_svg)tuples. UseNonefor a separator.chevron: draw the down arrow that marks the button as a menu.- Signal:
triggered(str)with thekeyof the chosen item.
menu = DropDownButton(
"Actions",
items=[("copy", "Copy"), ("paste", "Paste"), None, ("clear", "Clear")],
parent=self,
)
menu.triggered.connect(lambda key: print(key))
Link#
Link(
text="",
padding=None,
font_family=None,
font_size=None,
font_weight=None,
parent=None,
)
padding: same format asButton. Default"8,4,8,4".font_sizedefaults to14andfont_weightto"normal".- It is a
QPushButton, so connect toclicked.
TextBlock#
TextBlock(text="", variant="body", parent=None)
| Variant | Size | Weight |
|---|---|---|
"title-large" |
40px | DemiBold |
"title" |
28px | DemiBold |
"subtitle" |
20px | DemiBold |
"body" |
14px | Normal |
"body-strong" |
14px | DemiBold |
"body-secondary" |
14px | Normal, secondary color |
"caption" |
12px | DemiBold, secondary color |
"caption-strong" |
12px | DemiBold |
Methods: set_color_override(color) and reset_color().
TextBox#
TextBox(
text="",
placeholder="",
icon_svg=None,
icon_position="left",
height=32,
parent=None,
)
icon_position:"left"or"right".- It is a
QLineEdit, sotext(),textChangedand the otherQLineEditAPI work as usual.
ToggleSwitch#
ToggleSwitch(checked=False, label=None, parent=None)
ToggleSwitchWithLabel(
text="",
checked=False,
on_text=None,
off_text=None,
parent=None,
)
ToggleSwitchis aQAbstractButton: useisChecked()and thetoggled(bool)signal.ToggleSwitchWithLabelshows a text next to the switch.on_textandoff_textreplacetextdepending on the state. It hasisChecked(),setChecked(value)and thetoggledsignal.
toggle = ToggleSwitchWithLabel(
text="Dark Mode",
on_text="Enabled",
off_text="Disabled",
checked=True,
parent=self,
)
toggle.toggled.connect(lambda on: print(on))
Slider#
Slider(minimum=0, maximum=100, value=50, suffix="%", step=1, parent=None)
- Signals:
valueChanged(int), andlabelClicked()when the value label is clicked. - Methods:
value()andset_value(v).
DropDown#
DropDown(items=None, parent=None, *, align_selected=True)
items: a list of(key, label)tuples.align_selected: align the popup so the selected item sits over the button.- Signal:
currentChanged(str)with thekeyof the new selection. - Methods:
set_current(key)andcurrent().
dd = DropDown(items=[("en", "English"), ("de", "German")], parent=self)
dd.set_current("en")
dd.currentChanged.connect(lambda key: print(key))
Card#
Card(parent=None, hover=True)
hover: light up under the cursor. Leave it on for a card the user can click, turn it off for a plain container.- Methods:
set_selected(selected)andis_selected().
InfoBar#
InfoBar(
title="",
message="",
severity=InfoBarSeverity.INFORMATIONAL,
parent=None,
)
severity:InfoBarSeverity.INFORMATIONAL,SUCCESS,WARNINGorERROR. ImportInfoBarSeverityfromcore.ui.components.info_bar.- Methods:
set_severity(severity),set_title(title)andset_message(message).
ContentDialog#
Modal dialog that centers on its parent behind a smoke layer.
ContentDialog(
parent,
title="",
content="",
primary_button_text="",
secondary_button_text="",
close_button_text="",
default_button=ContentDialogButton.NONE,
)
- A button with empty text is hidden.
default_button:ContentDialogButton.NONE,PRIMARY,SECONDARYorCLOSE.- Signals:
primary_button_click,secondary_button_click,close_button_click,openedandclosed(ContentDialogResult). The result isContentDialogResult.NONE,PRIMARYorSECONDARY. - Methods:
show_dialog(),hide_dialog(),set_title(text),set_content(text),set_content_widget(widget),result(), andprimary_button(),secondary_button(),close_button()to reach the buttons.
dlg = ContentDialog(
parent=self,
title="Delete Item?",
content="This action cannot be undone.",
primary_button_text="Delete",
close_button_text="Cancel",
default_button=ContentDialogButton.PRIMARY,
)
dlg.primary_button_click.connect(self._delete_item)
dlg.show_dialog()
InputDialog#
Modal dialog with a text field. It centers on its parent window behind a smoke layer and uses the same open and close animation as ContentDialog. A parent is required.
InputDialog(
title="",
content="",
text="",
placeholder="",
primary_button_text="OK",
close_button_text="Cancel",
parent=None,
)
- Signals:
accepted(str)with the entered text, andrejected(). - Methods:
show_dialog(),hide_dialog(),text()andinput_widget().
dlg = InputDialog(
parent=self,
title="Rename",
content="Enter a new name.",
text="Desktop 1",
primary_button_text="Rename",
)
dlg.accepted.connect(lambda name: print(name))
dlg.show_dialog()
StepIndicator#
StepIndicator(count=1, parent=None)
Method: set_current(index).
Loader#
Spinner(size=24, color="#FFFFFF", pen_width=None, parent=None)
LoaderLine(parent=None, color=None)
Spinneris a circular indeterminate spinner. Method:set_color(color).LoaderLineis a sliding line at the bottom edge of a widget:attach_to_widget(widget)anddetach_from_widget().start()andstop().set_color(color).configure(class_name=None, duration_ms=None, easing=None, segment_ratio=None, height=None, color=None).
loader = LoaderLine(parent=self)
loader.attach_to_widget(target_widget)
loader.start()
# ...
loader.stop()
Design Tokens#
The token system provides 89 color tokens per theme (dark and light). Values are sourced from the WinUI3 Common_themeresources_any.xaml.
from core.ui.theme import get_tokens, theme_key, is_dark, FONT_FAMILIES
tokens = get_tokens() # Returns dict for current OS theme
tokens["text_primary"] # "#ffffff" (dark) or "#e3000000" (light)
tokens["accent_fill_default"] # "#4cc2ff" (dark) or "#0078d4" (light)
| Category | Common Tokens | Usage |
|---|---|---|
| Text Fill | text_primary, text_secondary, text_tertiary, text_disabled |
Label and body text |
| Accent Fill | accent_fill_default, accent_fill_secondary, accent_fill_tertiary |
Primary actions, highlights |
| Control Fill | control_fill_default, control_fill_secondary, control_fill_input_active |
Input backgrounds, buttons |
| Control Stroke | control_stroke_default, control_stroke_secondary, divider_stroke_default |
Borders, dividers |
| Card | card_bg_default, card_stroke_default |
Card backgrounds and borders |
| Subtle Fill | subtle_fill_secondary, subtle_fill_tertiary |
Hover/pressed for subtle buttons |
| Solid Background | solid_bg_base, solid_bg_secondary, solid_bg_tertiary |
Window and panel backgrounds |
| System | system_success, system_caution, system_critical |
Status indicators |
| Layer | layer_default, layer_alt |
Section backgrounds |
Theme Reactivity#
All components respond to OS theme changes automatically. To make your own widget theme-reactive:
from core.ui.theme import get_tokens, theme_key
class MyWidget(QWidget):
def __init__(self):
super().__init__()
self._theme_key = theme_key()
self._apply_styles()
QApplication.instance().paletteChanged.connect(self._on_theme_changed)
def _on_theme_changed(self):
key = theme_key()
if key == self._theme_key:
return
self._theme_key = key
self._apply_styles()
def _apply_styles(self):
tokens = get_tokens()
self.setStyleSheet(f"color: {tokens['text_primary']};")
Tips for Contributors#
- Always use tokens - never hardcode colors. Use
get_tokens()to look up the current theme values. - React to theme changes - connect to
QApplication.instance().paletteChangedand re-apply styles when the OS theme switches. - Use
FONT_FAMILIES- import fromcore.ui.themeinstead of hardcoding"Segoe UI". - Prefer existing components - check if a component already exists before creating a new one. The system is designed to be reusable.