Custom styles
~/.config/lys-shell/styles/index.css is an escape hatch for CSS tweaks that aren't exposed through config.yaml or the settings GUI. The file is created with a starter comment the first time lys-shell runs. Anything written into it is layered on top of the built-in stylesheet, and saved changes apply to the running shell automatically.
For things the config already covers (palette, fonts, global scale, rounding, bar layout, module visibility), use the normal config or the settings GUI. This file is for the rest: per-instance module styling, animation behavior, padding and spacing tweaks, hover and focus state changes, custom keyframe animations.
Per-instance module styling
Two clock modules on the same bar share every config-level styling option. To style them differently, give one of them a CSS class in the layout entry by writing it as a mapping instead of a bare string:
# config.yaml
bar:
layout:
- monitor: "*"
center:
- {module: clock, class: primary-clock}
- clockThe class is added to the box around that module. The selector to target its button from index.css is .primary-clock button.bar-button (every module button is a button.bar-button inside its module box).
The robust way to change appearance on that one instance is to override the CSS variables it consumes. The built-in rules read from variables like --bar-btn-label-color, --bar-btn-icon-color, --bar-btn-icon-size, and --bar-btn-label-weight. The config sets a button's color variables on the button itself, so redefine them on its content, the box inside it, and set the button's own background directly:
/* ~/.config/lys-shell/styles/index.css */
.primary-clock button.bar-button {
background-color: var(--palette-primary);
}
.primary-clock button.bar-button > box {
--bar-btn-label-color: var(--palette-bg);
--bar-btn-label-weight: 700;
}Other modules continue to use the defaults.
The same class field can be applied to any item in left, center, or right, and to items nested inside groups.
Finding selectors and variables
The built-in stylesheet is the reference for selectors and variables: its sources live in styling/css, one plain CSS file per bar module and dropdown. Every rule there reads its colors, sizes and spacing from CSS variables, and overriding a variable inside a narrower selector is the robust way to change one thing.
To see which widget sits where, open the inspector on the running shell:
lys-shell panel inspectIt draws the widget tree over every bar and dropdown. While it is open, Ctrl+Shift+I toggles the overlay and Ctrl+Shift+D prints the focused surface's tree, with each widget's bounds, role and hover, focus and press state, to the shell's stdout (journalctl --user -u lys-shell when it runs as a service). Starting the shell with LYS_UI_INSPECT=1 arms the inspector from the start.
Inspecting dropdowns
Dropdowns close as soon as focus leaves the bar. To keep one open while inspecting it, disable autohide first. Either toggle Bar → Dropdown → Dropdown Autohide in the settings GUI, or set this in config.yaml:
bar:
dropdown-autohide: falseOpen the dropdown, then run lys-shell panel inspect. Re-enable autohide afterwards.
lys-shell renders with lys-ui, which implements GTK4's CSS dialect: a subset of web CSS. See docs.gtk.org/gtk4/css-overview.html for the reference.
Plain CSS
The file is plain CSS, read as written, with no preprocessor. CSS nesting (&) works; @import does not, so everything goes in index.css. Saving the file applies the change. A styles/index.scss left from earlier versions is no longer read; lys-shell logs an error naming it until its rules move to index.css and the file is deleted.
Disable user styles
Empty the contents of ~/.config/lys-shell/styles/index.css to revert to defaults.