A dynamic desktop shell for Hyprland
See the codeA Material 3 Expressive desktop shell for Hyprland, built on Quickshell.
A bar, a dock that morphs into a launcher, desktop widgets, a lock screen, an emoji picker, a screenshot tool and a settings app — themed together from your wallpaper.
v1.20 — Lucid 2. Every surface redrawn in Material You Expressive, a clock that is an app, a launcher that finds everything, sounds of its own, and a bar and settings you can shape, much of it from contributors. It's what I use daily. The shell shows the highlights once after you update; everything that landed in it, and in every version before it, is in the changelog. Rough edges are still possible and bug reports are welcome.
To hear about new versions, press Watch → Custom → Releases at the top of this page. Lucid also checks for itself: once a day it asks GitHub for the newest release and says so once, in a notification. See Updating.
Lucid installs to ~/.config/quickshell and needs Arch Linux and
Hyprland. Three commands:
git clone https://github.com/Sn3akyy1/lucid.git
cd lucid
./install.sh
That's it — the installer does the rest:
paru or yay and uses it, falling back
to pacman for repo packages. It lists everything and asks before touching
your system. Say no and it carries on, telling you which features won't work.
This includes the apps Lucid ships pinned to the dock — several GB, mostly
from the AUR. --no-apps skips them.~/.config/quickshell, moving any existing config to
~/.config/quickshell.backup-<timestamp> first.hyprland.lua and its modules: the keybinds, the
window and layer rules, blur, animations, and an autostart that launches the
shell on login. If you already have a config it is backed up and you are
asked first; --no-hypr keeps yours untouched.matugen/config.toml is appended to, never replaced..bashrc, .zshrc and config.fish), the
VSCode/VSCodium Matugen theme, and the GTK theme: adw-gtk3-dark with the
FairyWren icons, written to gsettings and to both gtk-3.0 and gtk-4.0
settings.ini.
--no-look skips this..desktop files and pins the real
apps, so the dock is never a row of blank letter tiles.Then reload Hyprland so the new binds and rules take effect:
hyprctl reload
Or log out and back in, which picks up the autostart too. To start the shell by
hand in the meantime, run qs.
Set a wallpaper from Settings → General on first run — that's what generates your colour palette.
Pull and re-run. Your settings, pinned apps, reminders, Shazam history and API keys are carried forward into the new install:
git pull
./install.sh
Lucid tells you when there is something to pull. Once a day it asks GitHub's
public API for the newest release, and when that is newer than the version you
installed it posts one notification — once per version, never again for the same
one. The request is a plain curl for
api.github.com/repos/Sn3akyy1/lucid/releases/latest and carries nothing about
you or your machine; Settings → About shows what it found and turns it off.
qs ipc call updates status # what it knows
qs ipc call updates check # ask now
v0.57 beta is a tag, so it stays exactly where it is:
git clone --branch v0.57 https://github.com/Sn3akyy1/lucid.git
It is on the releases page too, as a source archive. Nothing carries over between the two — v1.0.0 moved enough that it is worth installing fresh.
| Flag | What it does |
|---|---|
--no-theming | Skips the palette layer. Leaves ~/.config/lucid and ~/.config/matugen alone — use this if you already have a matugen setup you don't want touched. |
--no-hypr | Keeps your Hyprland config. Lucid's binds, window rules, blur and autostart are not installed. |
--no-apps | Doesn't install the apps the dock ships pinned (Zen, VSCodium, Spotify, Vesktop, Files, Steam, Proton VPN). The dock then pins whatever equivalents you already have. |
--no-look | Doesn't touch kitty.conf, starship.toml, your shell rc files, VSCode settings, or the GTK theme and icons. |
--no-wallpapers | Doesn't copy the bundled wallpapers into ~/Pictures/wallpapers. They are ~180 MB, so this is worth passing on a small disk or a slow link. |
--with-hypr | Reinstalls Lucid's Hyprland config even when one is already in place. |
--skip-deps | Never installs packages, just reports what's missing. |
-y, --yes | Accept every prompt. |
Re-running the installer is how you update. It moves your existing
~/.config/quickshell to ~/.config/quickshell.backup-<timestamp>, installs
the new shell files, then carries your settings, pinned apps, reminders,
Shazam history and API keys forward into it. Nothing is deleted, and the
backup stays where it is until you remove it.
The installer ships these in ~/.config/hypr/modules/binds.lua, along with the
window rules, blur, animations and the autostart. Run hyprctl reload after
installing to pick them up.
| Key | Does |
|---|---|
SUPER (tap) | Launcher |
SUPER + P | Command palette |
SUPER + T | Theme picker |
SUPER + B | Wallpaper picker |
SUPER + SHIFT + V | Clipboard history |
SUPER + S | Settings |
SUPER + . | Emoji picker |
SUPER + K | On-screen keyboard |
SUPER + W | Workspace overview (also: three-finger swipe) |
CTRL + ALT + TAB | Window switcher — every window on every workspace, newest first (+SHIFT steps back) |
SUPER + Esc | Session screen — lock, sleep, log out, restart, shut down |
SUPER + D / Print | Region screenshot |
SUPER + Print | Full screenshot |
SUPER + SHIFT + T | Copy text from a region (OCR) |
SUPER + SHIFT + C | Pick a colour off the screen |
SUPER + E | Files |
SUPER + C | Close window |
SUPER + V | Toggle float |
SUPER + 1–0 | Switch workspace (+SHIFT moves the window) |
SUPER + SHIFT + S | Scratchpad, or put away whichever special workspace is up |
SUPER + ALT + S | Stash the focused window in the scratchpad, or send it back |
SUPER + SHIFT + M | Music workspace |
SUPER + SHIFT + D | Comms workspace |
SUPER + SHIFT + R | To-do workspace |
CTRL + SHIFT + Esc | System monitor workspace |
SUPER + arrows | Move focus |
SUPER + R | Reload Hyprland |
F1–F6 | Volume, mic, brightness |
F9 | Terminal |
F10 | Lock |
F12 | Calculator |
Ran with --no-hypr, or want to bind things yourself? Everything is exposed
over IPC:
hl.bind("SUPER + SPACE", hl.dsp.exec_cmd("qs ipc call -- launcher toggle"))
hl.bind("SUPER + E", hl.dsp.exec_cmd("qs ipc call -- moji toggle"))
hl.bind("SUPER + L", hl.dsp.exec_cmd("qs ipc call -- lock lock"))
hl.bind("SUPER + Escape", hl.dsp.exec_cmd("qs ipc call -- session toggle"))
hl.bind("SUPER + S", hl.dsp.exec_cmd("qs ipc call -- snap toggle"))
hl.bind("SUPER + SHIFT + T", hl.dsp.exec_cmd("qs ipc call -- snap text"))
hl.bind("SUPER + SHIFT + C", hl.dsp.exec_cmd("qs ipc call -- snap color"))
hl.bind("SUPER + comma", hl.dsp.exec_cmd("qs ipc call -- settings open"))
Special workspaces are Hyprland Lua rather than IPC. On a Lua config of your
own, copy support/hypr/modules/specials.lua into ~/.config/hypr/modules/
and bind its functions the way Lucid's modules/binds.lua does:
local specials = require("modules.specials")
hl.bind("SUPER + SHIFT + M", specials.toggle("music"))
Keep the --. It is only strictly required when the call takes an argument —
qs ipc call settings show bar fails with "The following argument was not
expected: bar", while qs ipc call -- settings show bar works. It is harmless
on argument-free calls, so using it everywhere saves you the surprise.
Six modules, each a pill that expands into a panel. Settings lays them out: the bar has a left, a middle and a right zone, and every module can be moved between them and reordered inside them, or taken out of the bar entirely.
songrec)Two shapes, set in Settings: island (floating rounded pills) or notch (flush to the screen edge, with flares that blend into it).
Each module's card under Settings → Bar also picks its look, starting from the one above: the clock in one line or two, or with the time on its accent chip, and how the date reads; media as the turning disc, a still cover or just its bars, with a large-cover panel; workspaces as dots or numbers, how many always show and whether the wheel switches them; notifications as a count, a dot or an accent chip; and the tray collapsed or as every app's icon, in the app's own colours or the palette's, with a switch per app to keep it out.
With more than one display, Settings → Displays → The bar on its own → Every
display puts a bar on each screen, showing that screen's workspaces. One of
them, on the shell's display or the one in use, still turns into notification
popups and answers the bar's qs ipc targets.
Drag a module by its handle to move it within a zone or into another one, or down into Not in the bar to take it out; the arrows and buttons beside it do the same thing. Also in Settings: how close the middle may come to either side before it gives way, and a button that puts every module back where Lucid ships it.
The System pill opens into a control centre — Wi-Fi and Bluetooth with full panels behind them, brightness and volume with the audio devices behind those, two rows of tiles you arrange yourself, what is playing, and CPU, memory, disk and battery.
The small tiles are yours to arrange, the way a phone's quick settings are. The pencil in the panel's header opens Edit tiles: drag a tile to move it, tap its − or drag it below the line to take it out, and tap anything under Tap to add to put it in. Clipboard, Emoji, Wallpaper, Theme, Widgets and Clear wait there to begin with, and Reset brings back the set Lucid ships.
The clock pill opens into the clock app, here on its Today page: the time, the weather through the day and the week ahead, and the next reminder. Around it, a stacked clock, what is playing, the month, the weather, the battery, the system and a to-do list.
A floating M3 toolbar that grows into the launcher rather than opening a second window over it. Pinned apps, running-window indicators, drag to reorder, optional magnification and auto-hide.
The launcher opens with a head that carries the Lucida mark, the modes as one
row of pills, and what the results add up to. It is one search field over eight
modes, each reachable by a pill, by Ctrl and its number, by Tab, or by a
prefix you can type:
| Mode | Prefix | What it does |
|---|---|---|
| Apps | — | Fuzzy search over .desktop entries — word boundaries and initials both hit, so vsc finds Visual Studio Code |
| Commands | > | Shell commands and shell actions |
| Emoji | : | The emoji picker's own list, searched by name and keyword; Return copies one |
| Web | ? | Searches your engine of choice, or opens the address if what you typed is one |
| Run | $ | Runs a command line, in the background or in a terminal that stays open |
| Theme | >theme | Switch between the seven bundled palettes, and any you have imported |
| Wallpaper | >wallpaper | Carousel of your wallpaper folder |
| Clipboard | >clip | What you copied earlier, images included — pick one to put it back on the clipboard |
Type a sum in the search field for a calculator (2^3^2, right-associative).
The answer takes a card above the results, set in display type; Return copies it.
Applications with a window already open carry the dock's own running mark beside their name, and the selected row says what Return will do with it. A search that finds nothing offers the four prefixes as chips you can click.
A typed search ranks more than apps: open windows, by their app or their title (Return switches to the window); apps' own actions such as New Private Window, listed under the app when you type its name; and lock, suspend, log out, restart and shut down from three letters, the last three asking for a second Return. Something that reads as an address gets a row of its own at the top when nothing matches it better. Each of these is a switch on the Launcher settings page, which can also put power buttons at the end of the empty search field.
The application list follows the desktop-entry spec properly: ~/.local/share/ applications overrides /usr/share/applications entry for entry, so an
override marked NoDisplay really does hide the system copy, and OnlyShowIn /
NotShowIn are honoured against XDG_CURRENT_DESKTOP. Whatever is left that
you still do not want, hover it and press the eye on the right of the row — the
Launcher settings page lists everything you hid and brings any of it back.
Hidden applications still launch from the dock and still name their windows.
The Power pill, and the Power row in the command palette, hand over to the
full-screen session screen — the same one SUPER + Esc raises.
Searching apps ends with a web search row, so one field covers "open it" and "look it up". It is a switch on the Launcher settings page, along with the search engine — DuckDuckGo, Google, Brave, Startpage or Kagi.
Clipboard mode keeps what you copy and hands it back: pick an entry to put it
back on the clipboard, Delete (or the button on the row) to drop one, and
Clear history on the Launcher settings page to drop the lot. Images are kept
too and preview in the row. cliphist is the store; the shell owns the
wl-paste watchers that feed it, so history records for as long as the shell
is running. Turn the whole thing off with the switch on the Launcher page.
Wallpaper mode: a carousel that previews as you move through it, and applies on the second press.
Cards you place on the wallpaper yourself. Right-click the desktop and pick Widget Panel, or pick Widgets in the launcher's mode bar, and a sheet slides in with every widget drawn live: drag one straight out to where it should sit, or click it to drop it in the first free spot. Drag it anywhere afterwards, pin it so it stops moving, and it comes back where you left it after a reboot.
The panel searches by name or by what a widget does, filters by kind, lists what is already out on On desktop — hover a row to find its card, click it for the card's sheet, lock or remove it there — and switches layouts on Layouts. While it is open your widgets come up over the windows with a − on each, and a card dragged back onto the sheet is taken off the desktop. Settings → Widgets holds the same gallery and every preset.
The widget panel on its Clock gallery: drag a card onto the desktop, or click it to add it. Out on the desktop, a Coming up card and lucidfetch, each with its − while the panel is open.
Eighteen kinds, sixty-one looks between them — every category ships several variants of the same data:
| Widget | Looks |
|---|---|
| Clock | Digital, stacked, analog with a date window, the time inside an expressive shape, minimal (no card at all), or world clocks |
| Calendar | Full month, this week, today, or your next reminders |
| System | Rings, meters, a two-minute graph, quick-settings tiles, or network throughput — CPU, memory, disk, temperature |
| Battery | Ring, cell, full detail, or every wireless device that reports a charge |
| Media | Artwork card, compact row, cover art with the controls over it, or a record that turns while it plays — and it takes its colours from the artwork |
| Visualiser | Bars, mirrored bands, or one filled wave — live off whatever is playing. Drag any edge to size it, right across the screen if you want |
| Weather | Now, a forecast, an icon and a number, or the next twelve hours as a curve |
| Notes | A sticky square, a ruled sheet that counts your words, or one line in display type |
| To-do | A checklist with real checkboxes, or just the next thing with Done and Later |
| Timer | A countdown with one-tap lengths, a pomodoro with a dot per round, or a stopwatch — the same timers the bar's clock keeps |
| At a Glance | The date and weather, and under them the one thing worth knowing now: a running timer, your next reminder, what is playing, a low battery, sunset, or rain |
| Photo | A frame that turns over your pictures on its own — filling the card, cut to a shape, or as a print with the date under it |
| Fetch | Your machine the way a fetch script prints it, as a card or as terminal output |
| Thermals | The graphics card in full with each fan and the power profile, arc gauges for GPU and CPU heat and the fans, or two temperatures and a fan speed in a row |
| Network | A minute of download and upload drawn as a graph, the connection in detail (address, signal, VPN and whether the internet answers), or down and up as two numbers |
| Games | Your installed Steam games, one click from playing: covers on a shelf, the last one you played over its artwork, or a list |
| Palette | The Material roles the shell is currently built from, click one to copy the hex |
| Phone | Card, compact row, or a remote — the paired phone's battery and signal, with ring, ping, send a file and send the clipboard a click away; the remote drives whatever the phone is playing |
Every widget has its own sheet — right-click it, or use the gear that appears on hover. It draws every style of that widget as a live thumbnail, so you pick the look by seeing it, and holds the card's colour (auto, or any Material container), its size, and its own options: 12- or 24-hour, which metrics to show, °C or °F, a note's tint, and so on. Most take one of four sizes; the visualiser instead grows an outline with handles when you hover it, and you drag any edge or corner to whatever shape you want.
Dragging snaps to the screen edges and centre lines and lines up with the other widgets, with guides while you drag. They can go anywhere on the screen, including the strips the bar and the dock reserve — a full-width visualiser tucked under the dock is the point. Widgets sit below your windows by default so they behave like a desktop, and step aside for fullscreen windows; both are switches on the Widgets page if you would rather they float on top.
Scratchpads that slide over whatever workspace you are on, and hide again with the same keys.
| Keys | Workspace | Opens |
|---|---|---|
SUPER + SHIFT + S | Scratchpad | Nothing of its own — somewhere to park windows |
SUPER + SHIFT + M | Music | Spotify, Feishin, Supersonic, Cider, YouTube Music, TIDAL… |
SUPER + SHIFT + D | Comms | Discord, Vesktop, Telegram, Signal, Element, Slack… |
SUPER + SHIFT + R | To-do | Todoist, Planify, Errands, Endeavour, Obsidian… |
CTRL + SHIFT + Esc | System | btop in a terminal, Mission Center, Resources… |
A key starts its workspace's app if it is not running, and pulls it back in if
you moved it somewhere else; opened from the launcher or the dock, the app lands
in its workspace too. SUPER + ALT + S stashes the focused window in the
scratchpad and, pressed inside a special workspace, sends it back. The
scratchpad key also puts away whichever workspace is up, so one key always
returns you to what you were doing.
Settings → Workspaces picks each one's apps, turns any of them off, and sets
how far the screen behind dims, whether it blurs too, how much margin the windows
keep from the screen's edges, and whether switching workspace puts them away.
New workspace makes one of your own, with a name, a mark for the bar and any
apps; its key goes into keybinds.json like any other, so it shows on the
cheatsheet and is changed on the Keybinds page. Deleting it brings its windows
back to the workspace you are on.
Add an app offers everything installed, not only the catalogue above, and
works out what to match its window by. It writes ~/.config/hypr/lucid-specials.lua, which
modules/specials.lua reads on every key press, so a change applies without a
Hyprland reload.
Settings → Windows changes how Hyprland draws your windows, live, with a preview drawn from the values in force: the gaps between windows and around the screen, border width and colour (none, your palette's accent, or a gradient through three of its colours, which follows the theme as it changes), a faint border on the other windows, how round the corners are and their shape, a shadow, and dimming the windows you are not in. Under Tiling: the layout (dwindle, master or scrolling) and each one's own options — which way dwindle splits and where a new window goes, the master's side and size, the width of a scrolling column — with a preview of the whole screen for one to five windows, and a switch that takes a lone window edge to edge, without gaps, border or rounded corners. Under Behaviour: whether focus follows the cursor or waits for a click, whether an app may take the focus when it asks, the cursor jumping with the focus, resizing a window by its edges, floating windows snapping to each other and to the screen, hiding the cursor while typing or after a while, and window animations.
Only what you change there is Lucid's. It goes to
~/.config/hypr/lucid-settings.lua, which modules/settings.lua applies without
a reload; everything else stays as your Hyprland config has it, and each row's
reset hands its option back to your config. hyprland.lua requires the module
before anything of your own, so a file you add after it — hypr-user.lua,
HyprMod's hyprland-gui — still wins: the row says which file sets the option
and stays shut until it is taken out there.
Settings → Input holds the keyboard side of Hyprland, through the same
lucid-settings.lua as the Windows page and with the same rule that your own
config wins: your layouts in order (the first is the one every keyboard starts
with), each picked by name from the system's list of layouts and variants, the
key that switches between them, what Caps Lock does — the other keyboard options
your config sets are kept — key repeat delay and speed, and Num Lock at sign-in,
with a field to try it all out. Then the mouse — pointer speed, acceleration,
natural scrolling, scroll speed, left-handed buttons — and, when there is one,
the touchpad: tap to click, natural scrolling, scroll speed, off while typing,
two-finger right click, tap and drag, and a middle click from both buttons —
and the swipe between workspaces: how far the fingers travel, which way, how
fast a flick switches anyway, how far along letting go still switches, going
past the neighbour and making a new workspace at the end.
WlSessionLock that checks the password through
PAM, laid out the way Material You lays out a lock screen: an oversized
two-line clock in the wallpaper's own colour, the weather, battery, network
and Bluetooth at a glance, what is playing, the notifications and a power bar
that asks before it logs you out. Start typing and it focuses — the wallpaper
blurs further and everything but the sign-in card steps back. It counts your
remaining tries against faillock, warns about Caps Lock, shows the keyboard
layout, and will not let a notification launch anything while it is upSUPER + Esc, the control centre's Session tile or
the launcher's command list. Lock, sleep, log out, restart and shut down, each
on its own expressive shape that morphs as you move along the row with the
arrow keys. The three that cost you work ask twice: the first press arms the
shape in red with a ring that runs out after four seconds, and only the second
goes throughSUPER + K, the desktop's right-click menu or the
launcher's command list. It never takes the focus off what you are typing
into, and clicks outside it reach the application underneath, so the caret
stays where you put it. A letters layer and a function layer, modifiers that
latch on one press and lock on two, and chords sent as chords. Drag it
anywhere by the strip along its top and it stays therehyprpicker. Choosing it
drops the freeze, the shade and the crosshair and lets clicks through, so the
toolbar is left floating over a live, usable desktop. Pick HEX, RGB or HSL,
then hit the eyedropper: the toolbar stays up over hyprpicker, you click a
pixel, and the value is copied and shown in a toast with the colour beside it.
The output template is set per format, so the zoom lens, the clipboard and the
toast all read the same and all three are valid CSS (#RRGGBB, rgb(r, g, b),
hsl(h, s%, l%)). Cancelling the pick leaves you on the toolbar;
picking a colour ends the session. The toast shows the colour as itself rather than
as an icon. hyprpicker is asked for raw components and the string is built
here, so the swatch, the clipboard and the format all agreeqs ipc call -- toast show game "Game Mode On",
or toast warn alert "..." for the red variant. Named icons are copy,
check, alert, info, text, game and camera; anything else is
taken as a raw SVG pathABC against abc — rather than the words on and offtimedatectl set-timezone behind a polkit prompt, so every application on
the box moves together and Lucid can never drift from the rest of your
desktop. Switch on Set it from my location and a new position brings the
zone with it. A program reads the zone once when it starts, so anything
already open stays on the old one until you restart it — Lucid corrects for
that itself and is right either wayqs ipc call network status | list | rescanpavucontrol and
with whatever else you use. System sounds mark a USB or Bluetooth device
coming and going, the charger, low battery, the camera turning on and off and
a screenshot, each with its own switch and a button to hear it, under one
volume; they keep quiet while you are silenced, apart from the battery
warnings. Tiny ticks answer the volume, brightness, Caps Lock and microphone
keys — once where a drag or a held key starts and once where it stops.
The page also shows what a device is doing: a live meter under the output and
the microphone volume, balance and a Left/Right test for a stereo output, a
way to put an app back on the default device, and a Behaviour card for
WirePlumber's own settings (remembering each app's volume and device,
following the default, pausing when headphones go, headset call mode, mono).
The sounds are synthesised by support/sounds/build-sounds.py into
assets/sounds; the ticks start from Kenney's Interface
Sounds, which are CC0. Scriptable with
qs ipc call settings sound, and qs ipc call sounds play <name> plays any
of thempavucontrolqs ipc call kdeconnect status, list, rescan, and
qs ipc call -- kdeconnect ring <id>smartctl and mounting through udisksctl. It warns
when space runs low and can empty trash after a number of days~/.config/hypr/hypridle.conf and restarts the daemon whenever something on
the page changes, so the ladder — dim, lock, screen off, suspend — is set with
sliders rather than by hand. A rail at the top of the page shows the sequence
in the order it actually fires and warns when two steps are out of turn.
Extras worth knowing: keep awake, a caffeine toggle that holds every step
until you turn it off; never interrupt something playing, which asks
playerctl before each step; a suspend that can be limited to battery only; and
lock-before-sleep plus wake-the-screen-on-resume. Whatever was in your
hypridle.conf first is read into the page once and copied to
hypridle.conf.pre-lucid, so nothing is lost. Scriptable with
qs ipc call idle status | keepawake | on | off | restart~/.local/share/icons/<theme>-noshadow, copying the original's sizes,
hotspots and frame delays exactly, and points everything at that copy.
The point is that one choice reaches everywhere: each change is written
to GTK 2, 3 and 4, to gsettings, to the XCursor fallback theme, to
qt5ct and qt6ct, and to Hyprland's env module, and hyprctl setcursor runs
so the pointer changes under your hand rather than at the next login. The dock
picks a new icon theme up the moment you choose it — Qt only reads the icon
theme when a process starts, so Lucid walks the theme directories itself. Only
the keys Lucid owns are touched — every comment and every other setting in
those files stays where it was, and a toolkit this machine does not use is
skipped rather than conjured. It starts by reading what the machine already
says, so opening the page changes nothing; Re-read from the system picks
those values up again after you have changed appearance with another tool.
Turn GTK, Qt or Hyprland off individually if you would rather keep one of
them by hand. Scriptable with qs ipc call settings environment~/.config/hypr/lucid-monitors.lua, applied without a reload, and a display is
keyed by its description rather than its port, so moving the cable keeps what
you set. Scriptable with qs ipc call settings displays, and the display the
shell sits on with qs ipc call -- displays shell <name|here|next|auto> —
worth a keybind if you move between screensbackground_opacity leaves the text alone, so it can go all the way —
and app windows, a quarter as far, because Hyprland fades a window's text
along with its background. Any installed app can be frosted on its own, and
anything else you have open can be added by its window class. Per-app values
become Hyprland window rules in ~/.config/hypr/lucid-glass.lua, applied
without a reload and pushed onto windows that are already open. Scriptable
with qs ipc call settings glassCtrl+F, or just start typing.
Scriptable with qs ipc call -- settings search <words>. The General page
sets how big the whole shell is (Interface size, 75 to 125 %, without
touching the monitor's scale) and how round (Corner rounding, square to
half as round again). The first start after an update opens a What's new
sheet once; About → What's new brings it back
The lock screen — the clock in the wallpaper's own colour, the weather, battery, network and Bluetooth at a glance, what is playing, the notifications, and the power bar in the corner.
The screenshot toolbar — Photo, Video, Text and Colour, then a region or the whole display — over the desktop it is about to take.
Settings on the Sound page: Lucid's own system sounds, each with a switch and a play button, under one volume for all of them.
Settings on the Storage page — what fills the drive, by kind and as a map you can zoom through — with the control centre and the on-screen keyboard open beside it.
Colours come from one of eight palettes, picked in Settings or via the launcher's Theme mode:
Matugen and Pywal generate a palette from your current wallpaper. Your colour builds one from a single colour you pick, and leaves the wallpaper alone. Catppuccin Mocha, Gruvbox, Nightfox, Nord and Tokyo Night are fixed palettes that don't change with the wallpaper. Anything you import sits alongside them, and the whole list can be dragged into the order you want.
Every palette has a light mode too — Light or dark on the Theme page. Matugen and Pywal re-extract the wallpaper in the mode you pick, Your colour is built again from its colour; the fixed palettes get a light version built from their own colours, so Nord lands on its own Snow Storm and Gruvbox on its own cream. An imported light scheme goes the other way: light mode keeps it exactly as its author made it, and its dark mode is built from its own colours. Light surfaces carry a trace of the accent, and Accent tint on the General page sets how much. The mode is remembered beside the theme, and GTK and Qt applications follow it, switching to the light or dark counterpart of their theme where one is installed.
Generated palettes, at the top of Settings → Colours, shapes what Matugen and Your colour build:
hyprpicker), or one of a handful to start from.Changes apply as you make them, to the shell and every app template.
Light mode, with the palette taken from the wallpaper. The bar, the dock and every widget follow it.
Whichever is active, the shell reads ~/.cache/quickshell/matugen.json — a
flat map of Material 3 colour roles. Changing your wallpaper through Lucid
runs ~/.config/hypr/scripts/wallpaper/set-wallpaper.sh, which sets the
wallpaper and then regenerates that file if the active theme is wallpaper-derived.
Hyprland's window borders follow it as well. modules/decorations.lua reads the
same file, primary for the focused window and surface container for the rest,
and the shell re-applies it every time the palette changes, light mode included.
So does the starship prompt, without Lucid owning it: only the [palettes.colors]
table in ~/.config/starship.toml is repainted, so a prompt you have designed
yourself keeps its shape. Point its styles at color1 to color9 (the comment
above that table says which colour each one carries) and they follow every theme.
Every display gets the same picture, cropped to fill. To treat one differently —
a portrait screen that should letterbox rather than crop, or a second screen
with a picture of its own — put a rule in ~/.config/lucid/wallpaper-outputs.conf,
one output a line:
DP-3 --resize fit --fill-color 000000
HDMI-A-1 ~/Pictures/wallpapers/second.jpg
Any argument that is a file becomes that output's picture; the rest is passed to
awww/swww. Outputs with no rule keep the wallpaper you picked, and colours
are still generated from that one. qs ipc call displays list prints your
outputs, left to right.
If you already use matugen, the installer appends its Quickshell template
to your config.toml and backs up the original — your existing templates are
left alone.
Every template in ~/.config/matugen/config.toml follows whichever palette is
active, not only the wallpaper ones: picking Nord or Catppuccin renders them
with that theme's colours. That makes the config the place to theme any other
application — put a template in ~/.config/matugen/templates/ and add a block
for it:
[templates.myapp]
input_path = '~/.config/matugen/templates/myapp.css'
output_path = '~/.config/myapp/colors.css'
post_hook = 'pkill -USR1 myapp' # optional: tell the app to reload
Templates use matugen's syntax —
{{colors.primary.default.hex}} and so on. A fixed palette fills every role it
defines with its own colour; the few it doesn't (the *_fixed family, the
tonal palettes, base16) come from a scheme matugen derives from its primary.
Each template renders on its own, so one that fails doesn't hold the rest back:
a toast names it, and ~/.cache/lucid/templates.json keeps what happened to
every template on the last change, with matugen's error for the ones that
failed. ~/.config/lucid/render-templates.sh is what renders them, for the
wallpaper and the fixed palettes alike.
Settings → Colours (qs ipc call settings colours) shows the same thing
without opening the file:
https:// link. Try it
shows what it writes with your colours, or why it can't, before it's added.
Below it, every colour role of the current palette: click one to copy the
variable that writes it, as #rrggbb, rrggbb, rgb(), rgba() or hsl().~/.config/lucid/templates.py does the editing, and runs from a terminal too
(templates.py list, set <name> on|off, add, remove, try). The config
is copied to config.toml.lucid-backup before each change.
Settings → Palettes (qs ipc call settings palettes) has a gallery of
the few hundred base16 and base24 schemes that
tinted-theming collects, each drawn
in its own colours, searchable, and split into dark and light. Download fetches
the whole collection once (about half a megabyte, kept in
~/.cache/lucid/schemes); after that, hovering a scheme offers Add, which
makes it one of your themes, and Use, which also switches to it.
The same page takes the URL of any colour-scheme repo, or a scheme file you already have, clones or reads it, and builds a full Material 3 palette out of what it finds. Scheme repos agree on no common format, so detection is tiered: base16 and base24 YAML, name-keyed JSON (Catppuccin and friends) and palettes Lucid exported are read exactly, and anything else falls back to harvesting hex codes and sorting them by tone and chroma. A repo carrying several variants lists them so you can pick one, and wallpapers in the repo come along with it.
Nothing from the repo is ever executed — only text is parsed and only images are copied.
It writes ~/.config/lucid/themes/<id>/{quickshell.json,meta.json} and
~/Pictures/wallpapers/<id>/, which you can also do by hand: a
quickshell.json with the same keys as the bundled palettes is all a theme is.
The importer runs from a terminal too, on a repo URL or a local file or folder:
python3 ~/.config/lucid/add-theme.py <repo-url|file|folder> [--list] [--variant <name>] [--name <label>]
python3 ~/.config/lucid/scheme-gallery.py update # the gallery, downloaded and indexed
Further down the page, the editor works on the palette on screen. Six key colours (background, text, primary, secondary, tertiary, error) each rebuild the whole palette the way an imported scheme is built, and every other role can be set by itself; the shell wears the draft while you work, and Save as theme keeps it (Discard, or closing Settings, puts the palette back). Export writes the palette on screen to a file: a Lucid palette, which keeps every role and imports back exactly, or base16 YAML for tinted-theming's tools and anything else that reads base16.
The exporter runs from a terminal too:
python3 ~/.config/lucid/palette-edit.py export ~/.cache/quickshell/matugen.json <lucid|base16> <file> [<name>]
Arch Linux and Hyprland. The installer handles all of this, listed here so you know what's being pulled in.
Required — the shell won't start without these:
quickshell · qt6-5compat · qt6-declarative · qt6-multimedia
Per feature — a missing one breaks only its own feature:
| Package | Backs |
|---|---|
matugen, jq | Wallpaper-derived colours, theme switching |
git | Importing a theme from a scheme repo on Settings → Theme |
awww | Setting the wallpaper |
python-pywal | The Pywal theme |
networkmanager | Wi-Fi panel |
qrencode | The QR code that shares a saved Wi-Fi network. Without it the password still shows, with no code |
bluez, bluez-utils | Bluetooth panel and the Bluetooth settings page |
kdeconnect, python-gobject | The KDE Connect page. The daemon is the backend and starts itself; python-gobject backs the bridge Lucid talks to it through. Without either the page says so and does nothing else |
sshfs | Browsing a phone's files from the phone widget or the KDE Connect page. Without it KDE Connect cannot mount the phone and the button does nothing |
libpulse, wireplumber | Volume, audio devices, and playing Lucid's sounds |
inotify-tools | The camera on/off toast and sound. Without it the camera is never watched; nothing else changes |
smartmontools, udisks2 | The Storage page's drive health and mounting. Without them those two rows say so; the map and the cleanup work either way |
pipewire-audio, sound-theme-freedesktop | The Sound page's Left/Right test: pw-play, and the voice that names each side. Without them the test plays nothing |
brightnessctl, upower | Brightness, battery |
hyprsunset | Night light. Lucid starts it when night light first comes on and talks to it over hyprctl hyprsunset; one you already run is used as it is, and never reset unless Lucid warmed it |
hypridle | The Idle page: dimming, locking, screen off and suspend when you walk away. Without it the page says so and writes nothing |
grim, wf-recorder, ffmpeg, imagemagick | Screenshots and recording |
tesseract, tesseract-data-eng | The Text mode's OCR. Without them Text mode says so and copies nothing. Add tesseract-data-<lang> and set ocrLang in lucidshot/Screenshot.qml for another language |
python-pillow, python-numpy, python-fonttools | Emoji in copied text. Without them the text still copies, minus the emoji. The glyph atlas is built once and cached in ~/.cache/lucidshot-ocr; lucidshot/emoji-ocr.py --atlas builds it up front so the first copy is not slow |
wl-clipboard, wtype | Emoji and GIF pasting |
cliphist | Clipboard history. Without it the launcher's Clipboard mode says so and the Launcher page's switch is greyed out |
cava | Audio visualisers — the media panel's strip (its config is installed to ~/.config/cava/quickshell.conf; the strip needs that file's raw-ascii output settings) and the desktop widget, which writes its own ~/.cache/quickshell/lucid-cava.conf at whatever band count the widest card asks for |
songrec | Song identification |
curl | Weather, location lookup and GIF search |
polkit | Every administrator prompt. The shell registers itself as the session's authentication agent and drives polkit's own setuid helper, so no separate agent is needed — and no other agent should be started, as only one can hold the session |
accountsservice | The Users and Accounts page. Every change goes through it, so the shell's own polkit dialog asks and nothing runs as root |
libnotify | Notification actions |
swappy | Marking up a screenshot, from its preview card or notification |
hyprpicker | The Colour mode. Without it the mode says so and picks nothing |
xdg-utils | Opening links and files from the shell |
librsvg | Turning the pointer's shadow off — the cursor theme is rendered again from its vector sources |
noto-fonts-emoji | Emoji rendering |
The Hyprland config and the look — installed unless you pass --no-hypr
or --no-look. The binds shell out to these, so a missing one is a dead key:
| Package | Backs |
|---|---|
kitty | Terminal (F9), and the themed terminal colours |
fish | The shell kitty opens |
nautilus | Files (SUPER+E) |
playerctl | The media keys |
gnome-calculator | Calculator (F12) |
starship | The prompt |
ttf-jetbrains-mono-nerd | The glyphs the prompt and kitty are drawn with |
adw-gtk-theme | Provides adw-gtk3-dark, the GTK theme Lucid selects |
papirus-icon-theme | Fallback icons — FairyWren declares Inherits=Papirus |
The dock's default pins — installed unless you pass --no-apps. Several
GB, mostly from the AUR:
zen-browser-bin · vscodium-bin · spotify · vesktop · nautilus ·
steam · proton-vpn-gtk-app
Anything you already have an equivalent of is left alone — vscodium counts
for vscodium-bin, discord for vesktop, and so on. steam is skipped
unless the multilib repo is enabled.
Lucid uses Hyprland-specific APIs for workspaces and window management. It will not work on other compositors.
The default UI font is Google Sans, which is not in the Arch repos. If you don't have it, Qt falls back to your default sans and everything still works — or pick any installed font in Settings → General.
GIF search needs a free Giphy key (email only, no card) from
developers.giphy.com. Put it in
~/.config/quickshell/lucidmoji/config.json:
{ "giphyKey": "your-key-here", "tenorKey": "", "gifDir": "" }
Tenor works too if you'd rather use that. Emoji and kaomoji need no key.
Wallpapers default to ~/Pictures/wallpapers, one folder per theme —
~/Pictures/wallpapers/gruvbox is what the strip shows while you are on
Gruvbox. The installer puts a set in each of them, skipping any file you already
have, and --no-wallpapers leaves the folder alone entirely. Change the folder
in Settings → General, which then applies to every theme.
Every surface is scriptable. qs ipc call -- <target> <function> [arg]:
| Target | Functions |
|---|---|
launcher | toggle open close wallpaper theme power blur command shuffle clipboard emoji search <query> hide <id> unhide <id> hidden |
settings | toggle open close show <page> general users glass bar dock colours palettes environment keybinds input displays widgets windows workspaces notifications sound network bluetooth kdeconnect idle datetime font search <words> reset |
idle | status keepawake awake normal on off restart |
network | status list rescan |
nightlight | toggle on off status |
kdeconnect | status list rescan ring <id> ping <id> clipboard <id> files <id> send <id> <path> |
widgets | panel openPanel <screen> closePanel add <type> <variant> remove <uid> clear toggle lock unlock list catalogue settings resize <uid> <w> <h> |
moji | toggle open close emoji kaomoji gif center |
keyboard | toggle open close letters fnkeys center bigger smaller |
notifs | toggle open close clear toggleDnd expandAll settings count |
lock | lock unlock isLocked status — nothing here bypasses the password; PAM is the only way in |
snap | toggle open close text color video |
session | toggle open close |
switcher | next prev toggle open close status — next/prev open it when it is closed |
timer | start <seconds> stopwatch pomodoro pomodoroSkip pomodoroStop pomodoroAdd <minutes> status |
clock | open <tab> toggle close — tab is today, calendar, timer, stopwatch or world |
toast | show <icon> <label> warn <icon> <label> |
sounds | play <name> list — at the system-sound volume |
screenshot | full text |
media | toggle open close identify playPause next previous |
workspaces | toggle open close |
updates | status check whatsnew (the What's new sheet for this version) |
displays | list settings shell <where> bar <where> dock <where> — where is an output name, left/middle/right, here, next, prev or auto |
polkit | status demo <action-id> fail grant close — demo raises the dialog with no PAM session behind it, for previewing a theme |
debug | toggle on off — draws input and blur region outlines |
./uninstall.sh
Moves ~/.config/quickshell aside rather than deleting it, so your settings
survive. Packages installed by install.sh are left alone. It prints what else
it left in place — the matugen template blocks, ~/.config/hypr,
starship.toml and its init lines in your rc files, kitty's colour and
opacity includes — each with a timestamped backup beside it, so you can undo
them by hand.
The media popup's visualiser sits flat and never moves.
cava is running without ~/.config/cava/quickshell.conf — its own defaults emit
ncurses output rather than the raw ascii frames the strip parses, so there is
nothing to draw. Re-run the installer, or copy support/cava/quickshell.conf
there yourself. Before v1.0.0 this showed as one enormous bar covering the
popup instead.
Another app's notifications show instead of Lucid's.
org.freedesktop.Notifications is a single-owner D-Bus name. Lucid's bar
serves it, but any other notification daemon that is merely installed —
swaync, dunst, mako — gets D-Bus-activated by the first notification and then
keeps the name for the whole session. It does not need to be autostarted to
win. Check who holds it:
busctl --user status org.freedesktop.Notifications | grep PID
If that is not quickshell, mask the daemon and log back in:
systemctl --user mask swaync.service # or dunst.service, mako.service
pkill swaync
The installer offers to do this for you.
GTK apps are light, or their icons are wrong.
Under Hyprland there is no xsettings daemon, so GTK3/GTK4 read
~/.config/gtk-{3,4}.0/settings.ini while GNOME apps and portals read
gsettings. The installer writes both. If only some apps changed, check they
agree:
gsettings get org.gnome.desktop.interface gtk-theme # adw-gtk3-dark
gsettings get org.gnome.desktop.interface icon-theme # FairyWren_Dark
grep -E 'theme-name' ~/.config/gtk-3.0/settings.ini ~/.config/gtk-4.0/settings.ini
The icons live in ~/.local/share/icons/FairyWren_Dark. If that directory is
missing the installer could not reach GitLab — clone it by hand from
https://gitlab.com/FreshDoctor/FairyWren-Icons.
Nothing appears when I run qs. Check qs log for QML errors, and
confirm you're on Hyprland — Lucid needs its Wayland protocols.
Everything is grey / colours look wrong. The palette cache is missing or
empty. Set a wallpaper through Settings → General, or copy a bundled palette:
cp ~/.config/lucid/themes/nord/quickshell.json ~/.cache/quickshell/matugen.json
The blur frosts my windows instead of the desktop. Hyprland's blur samples
whatever is behind the layer. Lucid leaves that on so a panel opened over a
window shows the window; if you would rather have every pill frost the
wallpaper, uncomment the xray line at the end of
~/.config/hypr/modules/layerrules.lua, or add this to hyprland.lua:
hl.layer_rule({ match = { namespace = "quickshell" }, xray = true })
A Lua config cannot source a .conf file, so the old layerrule = xray 1, quickshell line has nowhere to go.
Icons look blurry. A fractional monitor scale factor puts icons on
fractional pixels. Use an integer scale, or adjust the icon size in Settings.
Animations feel choppy on an NVIDIA card, even at 144Hz or higher. Qt's Wayland plugin refuses threaded OpenGL on the proprietary NVIDIA driver — that is its workaround for QTBUG-95817 — so Qt Quick falls back to the basic render loop, whose animation driver is a fixed ~16ms timer with no vsync awareness. Every animation in the shell is then pinned to ~60fps however fast the monitor runs, which is why Hyprland stays smooth while Lucid does not.
Lucid starts through ~/.config/lucid/launch-shell.sh, which moves those
machines to Qt's Vulkan backend — Vulkan carries no such vendor check, so the
threaded loop and its vsync-driven animations come back. Ask it what it decided:
~/.config/lucid/launch-shell.sh --explain
It only switches when NVIDIA is the GPU the compositor actually renders on, the
driver is 555 or newer, and an NVIDIA Vulkan driver is present. Mesa already
gets the threaded loop and is left alone. Override the detection with
LUCID_RHI_BACKEND=vulkan or LUCID_RHI_BACKEND=opengl; an existing
QSG_RHI_BACKEND in your environment always wins.
A keybind with an argument does nothing. Add -- before the target:
qs ipc call -- settings show bar. Without it qs parses the argument as its
own and refuses the call. Argument-free calls work either way.
Issues and PRs welcome. If you're reporting a bug, qs log output and your
Hyprland version help a lot.
The logo is Lucida — the astronomical term for the brightest star in a constellation, sharing its Latin root (lux) with "Lucid". A broken orbit ring, a four-point star at the centre, and one companion dot outside the gap.
Its official colour is coral #FF7F50. In the shell itself the ring and the
companion dot track your accent, so the mark on the dock launcher recolours with
whatever palette you are running — but coral is the canonical brand colour, and
what you see above.
assets/logo.svg is the mark on its dark plate; assets/logo-mark.svg is the
bare coral mark for light and dark backgrounds alike. Both are drawn from the
same 24×24 geometry as LucidaMark.qml.
MIT — see LICENSE.
Built on Quickshell. Colour generation by matugen. Design follows Material 3 Expressive.
12 followers · starred Sep 2026
A dynamic desktop shell for Hyprland
See the codeA Material 3 Expressive desktop shell for Hyprland, built on Quickshell.
A bar, a dock that morphs into a launcher, desktop widgets, a lock screen, an emoji picker, a screenshot tool and a settings app — themed together from your wallpaper.
v1.20 — Lucid 2. Every surface redrawn in Material You Expressive, a clock that is an app, a launcher that finds everything, sounds of its own, and a bar and settings you can shape, much of it from contributors. It's what I use daily. The shell shows the highlights once after you update; everything that landed in it, and in every version before it, is in the changelog. Rough edges are still possible and bug reports are welcome.
To hear about new versions, press Watch → Custom → Releases at the top of this page. Lucid also checks for itself: once a day it asks GitHub for the newest release and says so once, in a notification. See Updating.
Lucid installs to ~/.config/quickshell and needs Arch Linux and
Hyprland. Three commands:
git clone https://github.com/Sn3akyy1/lucid.git
cd lucid
./install.sh
That's it — the installer does the rest:
paru or yay and uses it, falling back
to pacman for repo packages. It lists everything and asks before touching
your system. Say no and it carries on, telling you which features won't work.
This includes the apps Lucid ships pinned to the dock — several GB, mostly
from the AUR. --no-apps skips them.~/.config/quickshell, moving any existing config to
~/.config/quickshell.backup-<timestamp> first.hyprland.lua and its modules: the keybinds, the
window and layer rules, blur, animations, and an autostart that launches the
shell on login. If you already have a config it is backed up and you are
asked first; --no-hypr keeps yours untouched.matugen/config.toml is appended to, never replaced..bashrc, .zshrc and config.fish), the
VSCode/VSCodium Matugen theme, and the GTK theme: adw-gtk3-dark with the
FairyWren icons, written to gsettings and to both gtk-3.0 and gtk-4.0
settings.ini.
--no-look skips this..desktop files and pins the real
apps, so the dock is never a row of blank letter tiles.Then reload Hyprland so the new binds and rules take effect:
hyprctl reload
Or log out and back in, which picks up the autostart too. To start the shell by
hand in the meantime, run qs.
Set a wallpaper from Settings → General on first run — that's what generates your colour palette.
Pull and re-run. Your settings, pinned apps, reminders, Shazam history and API keys are carried forward into the new install:
git pull
./install.sh
Lucid tells you when there is something to pull. Once a day it asks GitHub's
public API for the newest release, and when that is newer than the version you
installed it posts one notification — once per version, never again for the same
one. The request is a plain curl for
api.github.com/repos/Sn3akyy1/lucid/releases/latest and carries nothing about
you or your machine; Settings → About shows what it found and turns it off.
qs ipc call updates status # what it knows
qs ipc call updates check # ask now
v0.57 beta is a tag, so it stays exactly where it is:
git clone --branch v0.57 https://github.com/Sn3akyy1/lucid.git
It is on the releases page too, as a source archive. Nothing carries over between the two — v1.0.0 moved enough that it is worth installing fresh.
| Flag | What it does |
|---|---|
--no-theming | Skips the palette layer. Leaves ~/.config/lucid and ~/.config/matugen alone — use this if you already have a matugen setup you don't want touched. |
--no-hypr | Keeps your Hyprland config. Lucid's binds, window rules, blur and autostart are not installed. |
--no-apps | Doesn't install the apps the dock ships pinned (Zen, VSCodium, Spotify, Vesktop, Files, Steam, Proton VPN). The dock then pins whatever equivalents you already have. |
--no-look | Doesn't touch kitty.conf, starship.toml, your shell rc files, VSCode settings, or the GTK theme and icons. |
--no-wallpapers | Doesn't copy the bundled wallpapers into ~/Pictures/wallpapers. They are ~180 MB, so this is worth passing on a small disk or a slow link. |
--with-hypr | Reinstalls Lucid's Hyprland config even when one is already in place. |
--skip-deps | Never installs packages, just reports what's missing. |
-y, --yes | Accept every prompt. |
Re-running the installer is how you update. It moves your existing
~/.config/quickshell to ~/.config/quickshell.backup-<timestamp>, installs
the new shell files, then carries your settings, pinned apps, reminders,
Shazam history and API keys forward into it. Nothing is deleted, and the
backup stays where it is until you remove it.
The installer ships these in ~/.config/hypr/modules/binds.lua, along with the
window rules, blur, animations and the autostart. Run hyprctl reload after
installing to pick them up.
| Key | Does |
|---|---|
SUPER (tap) | Launcher |
SUPER + P | Command palette |
SUPER + T | Theme picker |
SUPER + B | Wallpaper picker |
SUPER + SHIFT + V | Clipboard history |
SUPER + S | Settings |
SUPER + . | Emoji picker |
SUPER + K | On-screen keyboard |
SUPER + W | Workspace overview (also: three-finger swipe) |
CTRL + ALT + TAB | Window switcher — every window on every workspace, newest first (+SHIFT steps back) |
SUPER + Esc | Session screen — lock, sleep, log out, restart, shut down |
SUPER + D / Print | Region screenshot |
SUPER + Print | Full screenshot |
SUPER + SHIFT + T | Copy text from a region (OCR) |
SUPER + SHIFT + C | Pick a colour off the screen |
SUPER + E | Files |
SUPER + C | Close window |
SUPER + V | Toggle float |
SUPER + 1–0 | Switch workspace (+SHIFT moves the window) |
SUPER + SHIFT + S | Scratchpad, or put away whichever special workspace is up |
SUPER + ALT + S | Stash the focused window in the scratchpad, or send it back |
SUPER + SHIFT + M | Music workspace |
SUPER + SHIFT + D | Comms workspace |
SUPER + SHIFT + R | To-do workspace |
CTRL + SHIFT + Esc | System monitor workspace |
SUPER + arrows | Move focus |
SUPER + R | Reload Hyprland |
F1–F6 | Volume, mic, brightness |
F9 | Terminal |
F10 | Lock |
F12 | Calculator |
Ran with --no-hypr, or want to bind things yourself? Everything is exposed
over IPC:
hl.bind("SUPER + SPACE", hl.dsp.exec_cmd("qs ipc call -- launcher toggle"))
hl.bind("SUPER + E", hl.dsp.exec_cmd("qs ipc call -- moji toggle"))
hl.bind("SUPER + L", hl.dsp.exec_cmd("qs ipc call -- lock lock"))
hl.bind("SUPER + Escape", hl.dsp.exec_cmd("qs ipc call -- session toggle"))
hl.bind("SUPER + S", hl.dsp.exec_cmd("qs ipc call -- snap toggle"))
hl.bind("SUPER + SHIFT + T", hl.dsp.exec_cmd("qs ipc call -- snap text"))
hl.bind("SUPER + SHIFT + C", hl.dsp.exec_cmd("qs ipc call -- snap color"))
hl.bind("SUPER + comma", hl.dsp.exec_cmd("qs ipc call -- settings open"))
Special workspaces are Hyprland Lua rather than IPC. On a Lua config of your
own, copy support/hypr/modules/specials.lua into ~/.config/hypr/modules/
and bind its functions the way Lucid's modules/binds.lua does:
local specials = require("modules.specials")
hl.bind("SUPER + SHIFT + M", specials.toggle("music"))
Keep the --. It is only strictly required when the call takes an argument —
qs ipc call settings show bar fails with "The following argument was not
expected: bar", while qs ipc call -- settings show bar works. It is harmless
on argument-free calls, so using it everywhere saves you the surprise.
Six modules, each a pill that expands into a panel. Settings lays them out: the bar has a left, a middle and a right zone, and every module can be moved between them and reordered inside them, or taken out of the bar entirely.
songrec)Two shapes, set in Settings: island (floating rounded pills) or notch (flush to the screen edge, with flares that blend into it).
Each module's card under Settings → Bar also picks its look, starting from the one above: the clock in one line or two, or with the time on its accent chip, and how the date reads; media as the turning disc, a still cover or just its bars, with a large-cover panel; workspaces as dots or numbers, how many always show and whether the wheel switches them; notifications as a count, a dot or an accent chip; and the tray collapsed or as every app's icon, in the app's own colours or the palette's, with a switch per app to keep it out.
With more than one display, Settings → Displays → The bar on its own → Every
display puts a bar on each screen, showing that screen's workspaces. One of
them, on the shell's display or the one in use, still turns into notification
popups and answers the bar's qs ipc targets.
Drag a module by its handle to move it within a zone or into another one, or down into Not in the bar to take it out; the arrows and buttons beside it do the same thing. Also in Settings: how close the middle may come to either side before it gives way, and a button that puts every module back where Lucid ships it.
The System pill opens into a control centre — Wi-Fi and Bluetooth with full panels behind them, brightness and volume with the audio devices behind those, two rows of tiles you arrange yourself, what is playing, and CPU, memory, disk and battery.
The small tiles are yours to arrange, the way a phone's quick settings are. The pencil in the panel's header opens Edit tiles: drag a tile to move it, tap its − or drag it below the line to take it out, and tap anything under Tap to add to put it in. Clipboard, Emoji, Wallpaper, Theme, Widgets and Clear wait there to begin with, and Reset brings back the set Lucid ships.
The clock pill opens into the clock app, here on its Today page: the time, the weather through the day and the week ahead, and the next reminder. Around it, a stacked clock, what is playing, the month, the weather, the battery, the system and a to-do list.
A floating M3 toolbar that grows into the launcher rather than opening a second window over it. Pinned apps, running-window indicators, drag to reorder, optional magnification and auto-hide.
The launcher opens with a head that carries the Lucida mark, the modes as one
row of pills, and what the results add up to. It is one search field over eight
modes, each reachable by a pill, by Ctrl and its number, by Tab, or by a
prefix you can type:
| Mode | Prefix | What it does |
|---|---|---|
| Apps | — | Fuzzy search over .desktop entries — word boundaries and initials both hit, so vsc finds Visual Studio Code |
| Commands | > | Shell commands and shell actions |
| Emoji | : | The emoji picker's own list, searched by name and keyword; Return copies one |
| Web | ? | Searches your engine of choice, or opens the address if what you typed is one |
| Run | $ | Runs a command line, in the background or in a terminal that stays open |
| Theme | >theme | Switch between the seven bundled palettes, and any you have imported |
| Wallpaper | >wallpaper | Carousel of your wallpaper folder |
| Clipboard | >clip | What you copied earlier, images included — pick one to put it back on the clipboard |
Type a sum in the search field for a calculator (2^3^2, right-associative).
The answer takes a card above the results, set in display type; Return copies it.
Applications with a window already open carry the dock's own running mark beside their name, and the selected row says what Return will do with it. A search that finds nothing offers the four prefixes as chips you can click.
A typed search ranks more than apps: open windows, by their app or their title (Return switches to the window); apps' own actions such as New Private Window, listed under the app when you type its name; and lock, suspend, log out, restart and shut down from three letters, the last three asking for a second Return. Something that reads as an address gets a row of its own at the top when nothing matches it better. Each of these is a switch on the Launcher settings page, which can also put power buttons at the end of the empty search field.
The application list follows the desktop-entry spec properly: ~/.local/share/ applications overrides /usr/share/applications entry for entry, so an
override marked NoDisplay really does hide the system copy, and OnlyShowIn /
NotShowIn are honoured against XDG_CURRENT_DESKTOP. Whatever is left that
you still do not want, hover it and press the eye on the right of the row — the
Launcher settings page lists everything you hid and brings any of it back.
Hidden applications still launch from the dock and still name their windows.
The Power pill, and the Power row in the command palette, hand over to the
full-screen session screen — the same one SUPER + Esc raises.
Searching apps ends with a web search row, so one field covers "open it" and "look it up". It is a switch on the Launcher settings page, along with the search engine — DuckDuckGo, Google, Brave, Startpage or Kagi.
Clipboard mode keeps what you copy and hands it back: pick an entry to put it
back on the clipboard, Delete (or the button on the row) to drop one, and
Clear history on the Launcher settings page to drop the lot. Images are kept
too and preview in the row. cliphist is the store; the shell owns the
wl-paste watchers that feed it, so history records for as long as the shell
is running. Turn the whole thing off with the switch on the Launcher page.
Wallpaper mode: a carousel that previews as you move through it, and applies on the second press.
Cards you place on the wallpaper yourself. Right-click the desktop and pick Widget Panel, or pick Widgets in the launcher's mode bar, and a sheet slides in with every widget drawn live: drag one straight out to where it should sit, or click it to drop it in the first free spot. Drag it anywhere afterwards, pin it so it stops moving, and it comes back where you left it after a reboot.
The panel searches by name or by what a widget does, filters by kind, lists what is already out on On desktop — hover a row to find its card, click it for the card's sheet, lock or remove it there — and switches layouts on Layouts. While it is open your widgets come up over the windows with a − on each, and a card dragged back onto the sheet is taken off the desktop. Settings → Widgets holds the same gallery and every preset.
The widget panel on its Clock gallery: drag a card onto the desktop, or click it to add it. Out on the desktop, a Coming up card and lucidfetch, each with its − while the panel is open.
Eighteen kinds, sixty-one looks between them — every category ships several variants of the same data:
| Widget | Looks |
|---|---|
| Clock | Digital, stacked, analog with a date window, the time inside an expressive shape, minimal (no card at all), or world clocks |
| Calendar | Full month, this week, today, or your next reminders |
| System | Rings, meters, a two-minute graph, quick-settings tiles, or network throughput — CPU, memory, disk, temperature |
| Battery | Ring, cell, full detail, or every wireless device that reports a charge |
| Media | Artwork card, compact row, cover art with the controls over it, or a record that turns while it plays — and it takes its colours from the artwork |
| Visualiser | Bars, mirrored bands, or one filled wave — live off whatever is playing. Drag any edge to size it, right across the screen if you want |
| Weather | Now, a forecast, an icon and a number, or the next twelve hours as a curve |
| Notes | A sticky square, a ruled sheet that counts your words, or one line in display type |
| To-do | A checklist with real checkboxes, or just the next thing with Done and Later |
| Timer | A countdown with one-tap lengths, a pomodoro with a dot per round, or a stopwatch — the same timers the bar's clock keeps |
| At a Glance | The date and weather, and under them the one thing worth knowing now: a running timer, your next reminder, what is playing, a low battery, sunset, or rain |
| Photo | A frame that turns over your pictures on its own — filling the card, cut to a shape, or as a print with the date under it |
| Fetch | Your machine the way a fetch script prints it, as a card or as terminal output |
| Thermals | The graphics card in full with each fan and the power profile, arc gauges for GPU and CPU heat and the fans, or two temperatures and a fan speed in a row |
| Network | A minute of download and upload drawn as a graph, the connection in detail (address, signal, VPN and whether the internet answers), or down and up as two numbers |
| Games | Your installed Steam games, one click from playing: covers on a shelf, the last one you played over its artwork, or a list |
| Palette | The Material roles the shell is currently built from, click one to copy the hex |
| Phone | Card, compact row, or a remote — the paired phone's battery and signal, with ring, ping, send a file and send the clipboard a click away; the remote drives whatever the phone is playing |
Every widget has its own sheet — right-click it, or use the gear that appears on hover. It draws every style of that widget as a live thumbnail, so you pick the look by seeing it, and holds the card's colour (auto, or any Material container), its size, and its own options: 12- or 24-hour, which metrics to show, °C or °F, a note's tint, and so on. Most take one of four sizes; the visualiser instead grows an outline with handles when you hover it, and you drag any edge or corner to whatever shape you want.
Dragging snaps to the screen edges and centre lines and lines up with the other widgets, with guides while you drag. They can go anywhere on the screen, including the strips the bar and the dock reserve — a full-width visualiser tucked under the dock is the point. Widgets sit below your windows by default so they behave like a desktop, and step aside for fullscreen windows; both are switches on the Widgets page if you would rather they float on top.
Scratchpads that slide over whatever workspace you are on, and hide again with the same keys.
| Keys | Workspace | Opens |
|---|---|---|
SUPER + SHIFT + S | Scratchpad | Nothing of its own — somewhere to park windows |
SUPER + SHIFT + M | Music | Spotify, Feishin, Supersonic, Cider, YouTube Music, TIDAL… |
SUPER + SHIFT + D | Comms | Discord, Vesktop, Telegram, Signal, Element, Slack… |
SUPER + SHIFT + R | To-do | Todoist, Planify, Errands, Endeavour, Obsidian… |
CTRL + SHIFT + Esc | System | btop in a terminal, Mission Center, Resources… |
A key starts its workspace's app if it is not running, and pulls it back in if
you moved it somewhere else; opened from the launcher or the dock, the app lands
in its workspace too. SUPER + ALT + S stashes the focused window in the
scratchpad and, pressed inside a special workspace, sends it back. The
scratchpad key also puts away whichever workspace is up, so one key always
returns you to what you were doing.
Settings → Workspaces picks each one's apps, turns any of them off, and sets
how far the screen behind dims, whether it blurs too, how much margin the windows
keep from the screen's edges, and whether switching workspace puts them away.
New workspace makes one of your own, with a name, a mark for the bar and any
apps; its key goes into keybinds.json like any other, so it shows on the
cheatsheet and is changed on the Keybinds page. Deleting it brings its windows
back to the workspace you are on.
Add an app offers everything installed, not only the catalogue above, and
works out what to match its window by. It writes ~/.config/hypr/lucid-specials.lua, which
modules/specials.lua reads on every key press, so a change applies without a
Hyprland reload.
Settings → Windows changes how Hyprland draws your windows, live, with a preview drawn from the values in force: the gaps between windows and around the screen, border width and colour (none, your palette's accent, or a gradient through three of its colours, which follows the theme as it changes), a faint border on the other windows, how round the corners are and their shape, a shadow, and dimming the windows you are not in. Under Tiling: the layout (dwindle, master or scrolling) and each one's own options — which way dwindle splits and where a new window goes, the master's side and size, the width of a scrolling column — with a preview of the whole screen for one to five windows, and a switch that takes a lone window edge to edge, without gaps, border or rounded corners. Under Behaviour: whether focus follows the cursor or waits for a click, whether an app may take the focus when it asks, the cursor jumping with the focus, resizing a window by its edges, floating windows snapping to each other and to the screen, hiding the cursor while typing or after a while, and window animations.
Only what you change there is Lucid's. It goes to
~/.config/hypr/lucid-settings.lua, which modules/settings.lua applies without
a reload; everything else stays as your Hyprland config has it, and each row's
reset hands its option back to your config. hyprland.lua requires the module
before anything of your own, so a file you add after it — hypr-user.lua,
HyprMod's hyprland-gui — still wins: the row says which file sets the option
and stays shut until it is taken out there.
Settings → Input holds the keyboard side of Hyprland, through the same
lucid-settings.lua as the Windows page and with the same rule that your own
config wins: your layouts in order (the first is the one every keyboard starts
with), each picked by name from the system's list of layouts and variants, the
key that switches between them, what Caps Lock does — the other keyboard options
your config sets are kept — key repeat delay and speed, and Num Lock at sign-in,
with a field to try it all out. Then the mouse — pointer speed, acceleration,
natural scrolling, scroll speed, left-handed buttons — and, when there is one,
the touchpad: tap to click, natural scrolling, scroll speed, off while typing,
two-finger right click, tap and drag, and a middle click from both buttons —
and the swipe between workspaces: how far the fingers travel, which way, how
fast a flick switches anyway, how far along letting go still switches, going
past the neighbour and making a new workspace at the end.
WlSessionLock that checks the password through
PAM, laid out the way Material You lays out a lock screen: an oversized
two-line clock in the wallpaper's own colour, the weather, battery, network
and Bluetooth at a glance, what is playing, the notifications and a power bar
that asks before it logs you out. Start typing and it focuses — the wallpaper
blurs further and everything but the sign-in card steps back. It counts your
remaining tries against faillock, warns about Caps Lock, shows the keyboard
layout, and will not let a notification launch anything while it is upSUPER + Esc, the control centre's Session tile or
the launcher's command list. Lock, sleep, log out, restart and shut down, each
on its own expressive shape that morphs as you move along the row with the
arrow keys. The three that cost you work ask twice: the first press arms the
shape in red with a ring that runs out after four seconds, and only the second
goes throughSUPER + K, the desktop's right-click menu or the
launcher's command list. It never takes the focus off what you are typing
into, and clicks outside it reach the application underneath, so the caret
stays where you put it. A letters layer and a function layer, modifiers that
latch on one press and lock on two, and chords sent as chords. Drag it
anywhere by the strip along its top and it stays therehyprpicker. Choosing it
drops the freeze, the shade and the crosshair and lets clicks through, so the
toolbar is left floating over a live, usable desktop. Pick HEX, RGB or HSL,
then hit the eyedropper: the toolbar stays up over hyprpicker, you click a
pixel, and the value is copied and shown in a toast with the colour beside it.
The output template is set per format, so the zoom lens, the clipboard and the
toast all read the same and all three are valid CSS (#RRGGBB, rgb(r, g, b),
hsl(h, s%, l%)). Cancelling the pick leaves you on the toolbar;
picking a colour ends the session. The toast shows the colour as itself rather than
as an icon. hyprpicker is asked for raw components and the string is built
here, so the swatch, the clipboard and the format all agreeqs ipc call -- toast show game "Game Mode On",
or toast warn alert "..." for the red variant. Named icons are copy,
check, alert, info, text, game and camera; anything else is
taken as a raw SVG pathABC against abc — rather than the words on and offtimedatectl set-timezone behind a polkit prompt, so every application on
the box moves together and Lucid can never drift from the rest of your
desktop. Switch on Set it from my location and a new position brings the
zone with it. A program reads the zone once when it starts, so anything
already open stays on the old one until you restart it — Lucid corrects for
that itself and is right either wayqs ipc call network status | list | rescanpavucontrol and
with whatever else you use. System sounds mark a USB or Bluetooth device
coming and going, the charger, low battery, the camera turning on and off and
a screenshot, each with its own switch and a button to hear it, under one
volume; they keep quiet while you are silenced, apart from the battery
warnings. Tiny ticks answer the volume, brightness, Caps Lock and microphone
keys — once where a drag or a held key starts and once where it stops.
The page also shows what a device is doing: a live meter under the output and
the microphone volume, balance and a Left/Right test for a stereo output, a
way to put an app back on the default device, and a Behaviour card for
WirePlumber's own settings (remembering each app's volume and device,
following the default, pausing when headphones go, headset call mode, mono).
The sounds are synthesised by support/sounds/build-sounds.py into
assets/sounds; the ticks start from Kenney's Interface
Sounds, which are CC0. Scriptable with
qs ipc call settings sound, and qs ipc call sounds play <name> plays any
of thempavucontrolqs ipc call kdeconnect status, list, rescan, and
qs ipc call -- kdeconnect ring <id>smartctl and mounting through udisksctl. It warns
when space runs low and can empty trash after a number of days~/.config/hypr/hypridle.conf and restarts the daemon whenever something on
the page changes, so the ladder — dim, lock, screen off, suspend — is set with
sliders rather than by hand. A rail at the top of the page shows the sequence
in the order it actually fires and warns when two steps are out of turn.
Extras worth knowing: keep awake, a caffeine toggle that holds every step
until you turn it off; never interrupt something playing, which asks
playerctl before each step; a suspend that can be limited to battery only; and
lock-before-sleep plus wake-the-screen-on-resume. Whatever was in your
hypridle.conf first is read into the page once and copied to
hypridle.conf.pre-lucid, so nothing is lost. Scriptable with
qs ipc call idle status | keepawake | on | off | restart~/.local/share/icons/<theme>-noshadow, copying the original's sizes,
hotspots and frame delays exactly, and points everything at that copy.
The point is that one choice reaches everywhere: each change is written
to GTK 2, 3 and 4, to gsettings, to the XCursor fallback theme, to
qt5ct and qt6ct, and to Hyprland's env module, and hyprctl setcursor runs
so the pointer changes under your hand rather than at the next login. The dock
picks a new icon theme up the moment you choose it — Qt only reads the icon
theme when a process starts, so Lucid walks the theme directories itself. Only
the keys Lucid owns are touched — every comment and every other setting in
those files stays where it was, and a toolkit this machine does not use is
skipped rather than conjured. It starts by reading what the machine already
says, so opening the page changes nothing; Re-read from the system picks
those values up again after you have changed appearance with another tool.
Turn GTK, Qt or Hyprland off individually if you would rather keep one of
them by hand. Scriptable with qs ipc call settings environment~/.config/hypr/lucid-monitors.lua, applied without a reload, and a display is
keyed by its description rather than its port, so moving the cable keeps what
you set. Scriptable with qs ipc call settings displays, and the display the
shell sits on with qs ipc call -- displays shell <name|here|next|auto> —
worth a keybind if you move between screensbackground_opacity leaves the text alone, so it can go all the way —
and app windows, a quarter as far, because Hyprland fades a window's text
along with its background. Any installed app can be frosted on its own, and
anything else you have open can be added by its window class. Per-app values
become Hyprland window rules in ~/.config/hypr/lucid-glass.lua, applied
without a reload and pushed onto windows that are already open. Scriptable
with qs ipc call settings glassCtrl+F, or just start typing.
Scriptable with qs ipc call -- settings search <words>. The General page
sets how big the whole shell is (Interface size, 75 to 125 %, without
touching the monitor's scale) and how round (Corner rounding, square to
half as round again). The first start after an update opens a What's new
sheet once; About → What's new brings it back
The lock screen — the clock in the wallpaper's own colour, the weather, battery, network and Bluetooth at a glance, what is playing, the notifications, and the power bar in the corner.
The screenshot toolbar — Photo, Video, Text and Colour, then a region or the whole display — over the desktop it is about to take.
Settings on the Sound page: Lucid's own system sounds, each with a switch and a play button, under one volume for all of them.
Settings on the Storage page — what fills the drive, by kind and as a map you can zoom through — with the control centre and the on-screen keyboard open beside it.
Colours come from one of eight palettes, picked in Settings or via the launcher's Theme mode:
Matugen and Pywal generate a palette from your current wallpaper. Your colour builds one from a single colour you pick, and leaves the wallpaper alone. Catppuccin Mocha, Gruvbox, Nightfox, Nord and Tokyo Night are fixed palettes that don't change with the wallpaper. Anything you import sits alongside them, and the whole list can be dragged into the order you want.
Every palette has a light mode too — Light or dark on the Theme page. Matugen and Pywal re-extract the wallpaper in the mode you pick, Your colour is built again from its colour; the fixed palettes get a light version built from their own colours, so Nord lands on its own Snow Storm and Gruvbox on its own cream. An imported light scheme goes the other way: light mode keeps it exactly as its author made it, and its dark mode is built from its own colours. Light surfaces carry a trace of the accent, and Accent tint on the General page sets how much. The mode is remembered beside the theme, and GTK and Qt applications follow it, switching to the light or dark counterpart of their theme where one is installed.
Generated palettes, at the top of Settings → Colours, shapes what Matugen and Your colour build:
hyprpicker), or one of a handful to start from.Changes apply as you make them, to the shell and every app template.
Light mode, with the palette taken from the wallpaper. The bar, the dock and every widget follow it.
Whichever is active, the shell reads ~/.cache/quickshell/matugen.json — a
flat map of Material 3 colour roles. Changing your wallpaper through Lucid
runs ~/.config/hypr/scripts/wallpaper/set-wallpaper.sh, which sets the
wallpaper and then regenerates that file if the active theme is wallpaper-derived.
Hyprland's window borders follow it as well. modules/decorations.lua reads the
same file, primary for the focused window and surface container for the rest,
and the shell re-applies it every time the palette changes, light mode included.
So does the starship prompt, without Lucid owning it: only the [palettes.colors]
table in ~/.config/starship.toml is repainted, so a prompt you have designed
yourself keeps its shape. Point its styles at color1 to color9 (the comment
above that table says which colour each one carries) and they follow every theme.
Every display gets the same picture, cropped to fill. To treat one differently —
a portrait screen that should letterbox rather than crop, or a second screen
with a picture of its own — put a rule in ~/.config/lucid/wallpaper-outputs.conf,
one output a line:
DP-3 --resize fit --fill-color 000000
HDMI-A-1 ~/Pictures/wallpapers/second.jpg
Any argument that is a file becomes that output's picture; the rest is passed to
awww/swww. Outputs with no rule keep the wallpaper you picked, and colours
are still generated from that one. qs ipc call displays list prints your
outputs, left to right.
If you already use matugen, the installer appends its Quickshell template
to your config.toml and backs up the original — your existing templates are
left alone.
Every template in ~/.config/matugen/config.toml follows whichever palette is
active, not only the wallpaper ones: picking Nord or Catppuccin renders them
with that theme's colours. That makes the config the place to theme any other
application — put a template in ~/.config/matugen/templates/ and add a block
for it:
[templates.myapp]
input_path = '~/.config/matugen/templates/myapp.css'
output_path = '~/.config/myapp/colors.css'
post_hook = 'pkill -USR1 myapp' # optional: tell the app to reload
Templates use matugen's syntax —
{{colors.primary.default.hex}} and so on. A fixed palette fills every role it
defines with its own colour; the few it doesn't (the *_fixed family, the
tonal palettes, base16) come from a scheme matugen derives from its primary.
Each template renders on its own, so one that fails doesn't hold the rest back:
a toast names it, and ~/.cache/lucid/templates.json keeps what happened to
every template on the last change, with matugen's error for the ones that
failed. ~/.config/lucid/render-templates.sh is what renders them, for the
wallpaper and the fixed palettes alike.
Settings → Colours (qs ipc call settings colours) shows the same thing
without opening the file:
https:// link. Try it
shows what it writes with your colours, or why it can't, before it's added.
Below it, every colour role of the current palette: click one to copy the
variable that writes it, as #rrggbb, rrggbb, rgb(), rgba() or hsl().~/.config/lucid/templates.py does the editing, and runs from a terminal too
(templates.py list, set <name> on|off, add, remove, try). The config
is copied to config.toml.lucid-backup before each change.
Settings → Palettes (qs ipc call settings palettes) has a gallery of
the few hundred base16 and base24 schemes that
tinted-theming collects, each drawn
in its own colours, searchable, and split into dark and light. Download fetches
the whole collection once (about half a megabyte, kept in
~/.cache/lucid/schemes); after that, hovering a scheme offers Add, which
makes it one of your themes, and Use, which also switches to it.
The same page takes the URL of any colour-scheme repo, or a scheme file you already have, clones or reads it, and builds a full Material 3 palette out of what it finds. Scheme repos agree on no common format, so detection is tiered: base16 and base24 YAML, name-keyed JSON (Catppuccin and friends) and palettes Lucid exported are read exactly, and anything else falls back to harvesting hex codes and sorting them by tone and chroma. A repo carrying several variants lists them so you can pick one, and wallpapers in the repo come along with it.
Nothing from the repo is ever executed — only text is parsed and only images are copied.
It writes ~/.config/lucid/themes/<id>/{quickshell.json,meta.json} and
~/Pictures/wallpapers/<id>/, which you can also do by hand: a
quickshell.json with the same keys as the bundled palettes is all a theme is.
The importer runs from a terminal too, on a repo URL or a local file or folder:
python3 ~/.config/lucid/add-theme.py <repo-url|file|folder> [--list] [--variant <name>] [--name <label>]
python3 ~/.config/lucid/scheme-gallery.py update # the gallery, downloaded and indexed
Further down the page, the editor works on the palette on screen. Six key colours (background, text, primary, secondary, tertiary, error) each rebuild the whole palette the way an imported scheme is built, and every other role can be set by itself; the shell wears the draft while you work, and Save as theme keeps it (Discard, or closing Settings, puts the palette back). Export writes the palette on screen to a file: a Lucid palette, which keeps every role and imports back exactly, or base16 YAML for tinted-theming's tools and anything else that reads base16.
The exporter runs from a terminal too:
python3 ~/.config/lucid/palette-edit.py export ~/.cache/quickshell/matugen.json <lucid|base16> <file> [<name>]
Arch Linux and Hyprland. The installer handles all of this, listed here so you know what's being pulled in.
Required — the shell won't start without these:
quickshell · qt6-5compat · qt6-declarative · qt6-multimedia
Per feature — a missing one breaks only its own feature:
| Package | Backs |
|---|---|
matugen, jq | Wallpaper-derived colours, theme switching |
git | Importing a theme from a scheme repo on Settings → Theme |
awww | Setting the wallpaper |
python-pywal | The Pywal theme |
networkmanager | Wi-Fi panel |
qrencode | The QR code that shares a saved Wi-Fi network. Without it the password still shows, with no code |
bluez, bluez-utils | Bluetooth panel and the Bluetooth settings page |
kdeconnect, python-gobject | The KDE Connect page. The daemon is the backend and starts itself; python-gobject backs the bridge Lucid talks to it through. Without either the page says so and does nothing else |
sshfs | Browsing a phone's files from the phone widget or the KDE Connect page. Without it KDE Connect cannot mount the phone and the button does nothing |
libpulse, wireplumber | Volume, audio devices, and playing Lucid's sounds |
inotify-tools | The camera on/off toast and sound. Without it the camera is never watched; nothing else changes |
smartmontools, udisks2 | The Storage page's drive health and mounting. Without them those two rows say so; the map and the cleanup work either way |
pipewire-audio, sound-theme-freedesktop | The Sound page's Left/Right test: pw-play, and the voice that names each side. Without them the test plays nothing |
brightnessctl, upower | Brightness, battery |
hyprsunset | Night light. Lucid starts it when night light first comes on and talks to it over hyprctl hyprsunset; one you already run is used as it is, and never reset unless Lucid warmed it |
hypridle | The Idle page: dimming, locking, screen off and suspend when you walk away. Without it the page says so and writes nothing |
grim, wf-recorder, ffmpeg, imagemagick | Screenshots and recording |
tesseract, tesseract-data-eng | The Text mode's OCR. Without them Text mode says so and copies nothing. Add tesseract-data-<lang> and set ocrLang in lucidshot/Screenshot.qml for another language |
python-pillow, python-numpy, python-fonttools | Emoji in copied text. Without them the text still copies, minus the emoji. The glyph atlas is built once and cached in ~/.cache/lucidshot-ocr; lucidshot/emoji-ocr.py --atlas builds it up front so the first copy is not slow |
wl-clipboard, wtype | Emoji and GIF pasting |
cliphist | Clipboard history. Without it the launcher's Clipboard mode says so and the Launcher page's switch is greyed out |
cava | Audio visualisers — the media panel's strip (its config is installed to ~/.config/cava/quickshell.conf; the strip needs that file's raw-ascii output settings) and the desktop widget, which writes its own ~/.cache/quickshell/lucid-cava.conf at whatever band count the widest card asks for |
songrec | Song identification |
curl | Weather, location lookup and GIF search |
polkit | Every administrator prompt. The shell registers itself as the session's authentication agent and drives polkit's own setuid helper, so no separate agent is needed — and no other agent should be started, as only one can hold the session |
accountsservice | The Users and Accounts page. Every change goes through it, so the shell's own polkit dialog asks and nothing runs as root |
libnotify | Notification actions |
swappy | Marking up a screenshot, from its preview card or notification |
hyprpicker | The Colour mode. Without it the mode says so and picks nothing |
xdg-utils | Opening links and files from the shell |
librsvg | Turning the pointer's shadow off — the cursor theme is rendered again from its vector sources |
noto-fonts-emoji | Emoji rendering |
The Hyprland config and the look — installed unless you pass --no-hypr
or --no-look. The binds shell out to these, so a missing one is a dead key:
| Package | Backs |
|---|---|
kitty | Terminal (F9), and the themed terminal colours |
fish | The shell kitty opens |
nautilus | Files (SUPER+E) |
playerctl | The media keys |
gnome-calculator | Calculator (F12) |
starship | The prompt |
ttf-jetbrains-mono-nerd | The glyphs the prompt and kitty are drawn with |
adw-gtk-theme | Provides adw-gtk3-dark, the GTK theme Lucid selects |
papirus-icon-theme | Fallback icons — FairyWren declares Inherits=Papirus |
The dock's default pins — installed unless you pass --no-apps. Several
GB, mostly from the AUR:
zen-browser-bin · vscodium-bin · spotify · vesktop · nautilus ·
steam · proton-vpn-gtk-app
Anything you already have an equivalent of is left alone — vscodium counts
for vscodium-bin, discord for vesktop, and so on. steam is skipped
unless the multilib repo is enabled.
Lucid uses Hyprland-specific APIs for workspaces and window management. It will not work on other compositors.
The default UI font is Google Sans, which is not in the Arch repos. If you don't have it, Qt falls back to your default sans and everything still works — or pick any installed font in Settings → General.
GIF search needs a free Giphy key (email only, no card) from
developers.giphy.com. Put it in
~/.config/quickshell/lucidmoji/config.json:
{ "giphyKey": "your-key-here", "tenorKey": "", "gifDir": "" }
Tenor works too if you'd rather use that. Emoji and kaomoji need no key.
Wallpapers default to ~/Pictures/wallpapers, one folder per theme —
~/Pictures/wallpapers/gruvbox is what the strip shows while you are on
Gruvbox. The installer puts a set in each of them, skipping any file you already
have, and --no-wallpapers leaves the folder alone entirely. Change the folder
in Settings → General, which then applies to every theme.
Every surface is scriptable. qs ipc call -- <target> <function> [arg]:
| Target | Functions |
|---|---|
launcher | toggle open close wallpaper theme power blur command shuffle clipboard emoji search <query> hide <id> unhide <id> hidden |
settings | toggle open close show <page> general users glass bar dock colours palettes environment keybinds input displays widgets windows workspaces notifications sound network bluetooth kdeconnect idle datetime font search <words> reset |
idle | status keepawake awake normal on off restart |
network | status list rescan |
nightlight | toggle on off status |
kdeconnect | status list rescan ring <id> ping <id> clipboard <id> files <id> send <id> <path> |
widgets | panel openPanel <screen> closePanel add <type> <variant> remove <uid> clear toggle lock unlock list catalogue settings resize <uid> <w> <h> |
moji | toggle open close emoji kaomoji gif center |
keyboard | toggle open close letters fnkeys center bigger smaller |
notifs | toggle open close clear toggleDnd expandAll settings count |
lock | lock unlock isLocked status — nothing here bypasses the password; PAM is the only way in |
snap | toggle open close text color video |
session | toggle open close |
switcher | next prev toggle open close status — next/prev open it when it is closed |
timer | start <seconds> stopwatch pomodoro pomodoroSkip pomodoroStop pomodoroAdd <minutes> status |
clock | open <tab> toggle close — tab is today, calendar, timer, stopwatch or world |
toast | show <icon> <label> warn <icon> <label> |
sounds | play <name> list — at the system-sound volume |
screenshot | full text |
media | toggle open close identify playPause next previous |
workspaces | toggle open close |
updates | status check whatsnew (the What's new sheet for this version) |
displays | list settings shell <where> bar <where> dock <where> — where is an output name, left/middle/right, here, next, prev or auto |
polkit | status demo <action-id> fail grant close — demo raises the dialog with no PAM session behind it, for previewing a theme |
debug | toggle on off — draws input and blur region outlines |
./uninstall.sh
Moves ~/.config/quickshell aside rather than deleting it, so your settings
survive. Packages installed by install.sh are left alone. It prints what else
it left in place — the matugen template blocks, ~/.config/hypr,
starship.toml and its init lines in your rc files, kitty's colour and
opacity includes — each with a timestamped backup beside it, so you can undo
them by hand.
The media popup's visualiser sits flat and never moves.
cava is running without ~/.config/cava/quickshell.conf — its own defaults emit
ncurses output rather than the raw ascii frames the strip parses, so there is
nothing to draw. Re-run the installer, or copy support/cava/quickshell.conf
there yourself. Before v1.0.0 this showed as one enormous bar covering the
popup instead.
Another app's notifications show instead of Lucid's.
org.freedesktop.Notifications is a single-owner D-Bus name. Lucid's bar
serves it, but any other notification daemon that is merely installed —
swaync, dunst, mako — gets D-Bus-activated by the first notification and then
keeps the name for the whole session. It does not need to be autostarted to
win. Check who holds it:
busctl --user status org.freedesktop.Notifications | grep PID
If that is not quickshell, mask the daemon and log back in:
systemctl --user mask swaync.service # or dunst.service, mako.service
pkill swaync
The installer offers to do this for you.
GTK apps are light, or their icons are wrong.
Under Hyprland there is no xsettings daemon, so GTK3/GTK4 read
~/.config/gtk-{3,4}.0/settings.ini while GNOME apps and portals read
gsettings. The installer writes both. If only some apps changed, check they
agree:
gsettings get org.gnome.desktop.interface gtk-theme # adw-gtk3-dark
gsettings get org.gnome.desktop.interface icon-theme # FairyWren_Dark
grep -E 'theme-name' ~/.config/gtk-3.0/settings.ini ~/.config/gtk-4.0/settings.ini
The icons live in ~/.local/share/icons/FairyWren_Dark. If that directory is
missing the installer could not reach GitLab — clone it by hand from
https://gitlab.com/FreshDoctor/FairyWren-Icons.
Nothing appears when I run qs. Check qs log for QML errors, and
confirm you're on Hyprland — Lucid needs its Wayland protocols.
Everything is grey / colours look wrong. The palette cache is missing or
empty. Set a wallpaper through Settings → General, or copy a bundled palette:
cp ~/.config/lucid/themes/nord/quickshell.json ~/.cache/quickshell/matugen.json
The blur frosts my windows instead of the desktop. Hyprland's blur samples
whatever is behind the layer. Lucid leaves that on so a panel opened over a
window shows the window; if you would rather have every pill frost the
wallpaper, uncomment the xray line at the end of
~/.config/hypr/modules/layerrules.lua, or add this to hyprland.lua:
hl.layer_rule({ match = { namespace = "quickshell" }, xray = true })
A Lua config cannot source a .conf file, so the old layerrule = xray 1, quickshell line has nowhere to go.
Icons look blurry. A fractional monitor scale factor puts icons on
fractional pixels. Use an integer scale, or adjust the icon size in Settings.
Animations feel choppy on an NVIDIA card, even at 144Hz or higher. Qt's Wayland plugin refuses threaded OpenGL on the proprietary NVIDIA driver — that is its workaround for QTBUG-95817 — so Qt Quick falls back to the basic render loop, whose animation driver is a fixed ~16ms timer with no vsync awareness. Every animation in the shell is then pinned to ~60fps however fast the monitor runs, which is why Hyprland stays smooth while Lucid does not.
Lucid starts through ~/.config/lucid/launch-shell.sh, which moves those
machines to Qt's Vulkan backend — Vulkan carries no such vendor check, so the
threaded loop and its vsync-driven animations come back. Ask it what it decided:
~/.config/lucid/launch-shell.sh --explain
It only switches when NVIDIA is the GPU the compositor actually renders on, the
driver is 555 or newer, and an NVIDIA Vulkan driver is present. Mesa already
gets the threaded loop and is left alone. Override the detection with
LUCID_RHI_BACKEND=vulkan or LUCID_RHI_BACKEND=opengl; an existing
QSG_RHI_BACKEND in your environment always wins.
A keybind with an argument does nothing. Add -- before the target:
qs ipc call -- settings show bar. Without it qs parses the argument as its
own and refuses the call. Argument-free calls work either way.
Issues and PRs welcome. If you're reporting a bug, qs log output and your
Hyprland version help a lot.
The logo is Lucida — the astronomical term for the brightest star in a constellation, sharing its Latin root (lux) with "Lucid". A broken orbit ring, a four-point star at the centre, and one companion dot outside the gap.
Its official colour is coral #FF7F50. In the shell itself the ring and the
companion dot track your accent, so the mark on the dock launcher recolours with
whatever palette you are running — but coral is the canonical brand colour, and
what you see above.
assets/logo.svg is the mark on its dark plate; assets/logo-mark.svg is the
bare coral mark for light and dark backgrounds alike. Both are drawn from the
same 24×24 geometry as LucidaMark.qml.
MIT — see LICENSE.
Built on Quickshell. Colour generation by matugen. Design follows Material 3 Expressive.
12 followers · starred Sep 2026