Personal dotfiles + UI setup for my Surface Laptop 4 (AMD) running Hyprland. Also, please check out my calendar app: Evercal
- Screenshots
- Dependencies
- Installation
- Hyprland
- Shaders
- Desktop Layouts
- Quickshell Hub
- Power menu
- Wifi menu
- Firefox Customizations
- SDDM
- GTK/QT Themes
- Utilities
- Credits & acknowledgements
- Media sources
- FAQs
|
|
|
Caution
Some layout geometry is hardcoded for 3:2 high-resolution display. Deviation in aspect ratio or pixel density will result in misalignment or things looking too big or small. Please follow instructions in the FAQs below to reconfigure values accordingl or start an issue if you require further assistance.
Note
I've pulled the installer down because of the significant changes in hyprland 0.55. The only way to install surface-dots at the moment is manually copying files around. I'll try to update the installer as soon as possible.
Keybindings
SUPER + Q→ terminal (kitty)SUPER + E→ file manager (thunar)SUPER + R→ rofiSUPER + B→ firefoxSUPER + D→ reading modeSUPER + N→ night lightSUPER + S→ my custom ocr app
SUPER + SPACE→ toggle hub on or offSUPER + X→ kill active windowSUPER + F→ toggle floating (simple)SUPER + ALT + F→ toggle floating and set size900x600+ centerSUPER + M→ fullscreenSUPER + P→ pseudotileSUPER + UP→ togglesplitSUPER + DOWN→ togglesplit
ALT + F4→ Power menuSUPER + ALT + F4→ exit Hyprland
SUPER + Left/Right→ move focus horizontallySUPER + UP/Down→ move focus vertically
SUPER + 1..0→ workspace1..10SUPER + SHIFT + 1..0→ move active window to workspace1..10SUPER + mouse wheel→ next/prev workspaceSUPER + G→ toggle groupSUPER+CTRL+LEFT/RIGHT→ move across grouped windows
SUPER + H→ toggle special workspacemagicSUPER + SHIFT + S→ move active window tospecial:magic
SUPER + LMB→ move windowSUPER + RMB→ resize window
Print→ Screen snipSUPER + PrintorSUPER + O→ Capture screenSUPER + SHIFT + Print→ Window capture
Shaders are integral part of my setup, I find them fun.
- All shaders are located at
~/.config/hypr/shaders/ - shaders can be accessed and toggled through rofi start menu (only in taskbar mode)
OR:
# activate with:
hyprctl eval 'hl.config({ decoration = { screen_shader = "/<path to shader.glsl>" } })'
# To turn off the screen shader, set the screen_shader value to an empty string.
hyprctl eval 'hl.config({ decoration = { screen_shader = "" } })'
A shader-based reading mode to mimic an e-ink reader.
- Toggle with
SUPER + Dor~/.config/hypr/shaders/reading_mode.sh - Automatically disables animations, shadows, and blur
- Custom GLSL shader with e-ink-like color reproduction
- Warm cream paper tone and soft charcoal blacks for reduced contrast
- Fine paper grain -like texture
main.glsl– main shader to improve my display (activates on startup through hyprland exec)night.glsl– my main night-light mode shader (toggle withSUPER + N)outdoor.gls– for maximum outdoor useabilitycinema.glsl– for media consumptionamano.glsl– simulates Yoshitaka Amano artstyleart_canvas.glsl– smulates physical canvas geometry and pigment densitydither.glsl– Simulates 4-bit graphics.fuji_acros.glsl– simulates fujifilm acroscrt_mode.glsl– simulates a crt monitorvhs.glsl– simulates vhsgameboy.glsl– simulates a gameboy screensmart_invert.glsl– eConverts RGB to HSL, inverts the Lightness channel, and converts backsilent_hill.glsl– Pacific Northwest / Silent Hill Shadergreens.glsl– _Retains only green hues and desaturates all other colors to grayscale. _
Two desktop layouts are available depending on how you want the bar positioned.
Use the top bar layout:
qs -c top-barUse the taskbar layout:
qs -c task-barBoth layouts (mostly) reuse the same core components but behave differently depending on mode.
Taskbar mode has additional desktop components and layout changes:
desktop/ScreenBorders.qmldock/Drawer.qml
When the session starts or when no windows are open:
- ScreenBorders wrap around the display edges.
- The taskbar switches to dock mode.
- The center of the dock contains a quickshell app drawer:
dock/Drawer.qml
As soon as a window opens:
- ScreenBorders hide
- The taskbar switches to workspace mode
- The taskbar in this state behaves similarly to the regular bar used in top-bar mode, except it appears at the bottom of the screen.
- The launcher drawer switches to rofi instead of the Quickshell drawer.
-
The rofi (start) menu is wider and contains
shaders -
The Hub media card derives its background colors from album art palette colors, instead of using blurred album artwork.
-
Upcoming events are no longer displayed inside
CalendarsWeatherCard.qmland have a dedicated cardhub/Events.qml. By default, the next upcoming event is shown until it ends. Multiple events can be added to the list by increasing the loop count in the file. -
Theme switching also differs between layouts:
- Taskbar mode: the Hub header has a theme toggle button (dark/light).
- Topbar mode: right-clicking the Arch glyph launcher icon toggles the theme. Both modes use the same theme script just located at:
# in top bar mode: bash ~/config/quickshell/top-bar/bar/theme-mode.sh dark|light
# in task bar mode: bash ~/config/quickshell/task-bar/utils/theme-mode.sh dark|light
-
Changing colors is generally easier in Taskbar mode, as most styling is handled through the dynamic theme system in
lib/ThemeEngine.qml. Some components still use the oldertheme.jsconfiguration, and a few define their own colors internally, so theme behavior is still not completely unified.
Expand for Topbar/Taskbar components
Clicking a workspace pill runs:
hyprctl dispatch workspace <id>
Updates are hardcoded for archlinux if you are using a different distro please replace:
// replace this snippet
sh(`
if [ -e /var/lib/pacman/db.lck ]; then
cat /tmp/qs_updates_count 2>/dev/null || echo 0
exit 0
fi
n=$(checkupdates 2>/dev/null | wc -l)
echo "$n" | tee /tmp/qs_updates_count
`)with a poller for your distro, for example debian:
// replacement snippet:
sh(`
# apt list --upgradable does not lock the database, so we skip the lock check
n=$(apt list --upgradable 2>/dev/null | grep -v 'Listing...' | wc -l)
echo "$n" | tee /tmp/qs_updates_count
`)Clicking the updates pill runs:
kitty -e bash -lc "sudo pacman -Syu"with this line in Taskbar.qml (line 932) and Bar.qml(line 595):
command: ["kitty", "-e", "bash", "-lc", "sudo pacman -Syu"]please replace this line with your package manager's update/upgrade command
- Pressing the clock emits a
requestHubToggle()signal used to open or close the hub. - Pressing Esc or clicking outside the hub closes it.
The hub is named 'snes-hub` with
// in HubWindow.qml (line 78 in task-bar and line 68 in top-bar):
WlrLayershell.namespace: "snes-hub"This is the main control/notification center. The hub is opened by:
- Clicking the date/clock module in the bar
- Pressing SUPER + SPACE through a Hyprland keybinding
It is rendered as a wlr-layershell overlay designed to stay out of the way and close quickly. The UI is composed of reusable components so cards can be added, removed, or restyled without rewriting the entire hub.
If you want a lightweight fallback, an earlier AGS version is available in .config/ags/.
EXPAND FOR INDIVIDUAL COMPONENTS
- Profile icon and username
- RAM and CPU usage indicators (only in topbar mode)
- Screenshot button (runs capture script and closes the hub)
- Power button
- Theme toggle button (taskbar mode)
A compact power grid expands inside the header.
Open it by:
- Clicking the power button
- Pressing the
pkey
Keyboard navigation:
- Arrow keys / Tab to move
- Enter to activate
- Esc to close
Wi-Fi togglewith SSID readout (right-click opens the Wi-Fi module)Bluetooth togglewith connected device statusPerformance profile button(cycles profiles throughauto-cpufreq, right click toggles battery health card)DND toggle(dunst)Volume and brightness sliders(pactlandbrightnessctl)
The battery health card shows RAM and CPU usage in Taskbar mode.
Polled using:
upower -i /org/freedesktop/UPower/devices/battery_BAT1
Displayed information:
- Health (capacity %)
- Current charge %
- Charge cycles
- Energy (full / design)
- Time remaining (when available)
- Charging state
NOTE
If your battery device is notbattery_BAT1, update the device path inBatteryHealthCard.qml.
The hub includes an MPRIS media card.
- Appears only when media is playing
- Clicking it launches the external Now Playing widget and closes the hub
- Resets its internal state when track metadata changes
Some browser content (like YouTube) can behave inconsistently depending on how the browser exposes MPRIS.
In taskbar mode, the media card uses colors extracted from album art instead of blurred artwork backgrounds.
This is a separate Flutter desktop widget managed through Hyprland window rules.
Behavior:
- Window resizing is disabled (
setResizable(false)) - Esc closes the widget
- Theme colors are generated from album artwork using
palette_generator
NOTE
You may need to make the now_playing binary executable and change the path to it in MediaCard.qml
The hub includes a calendar and weather card using a JSON-based weather script.
Calendar events are synced from Google Calendar using:
vdirsyncerkhal
Events are displayed in a dedicated Events card in Taskbar mode instead of inside CalendarsWeatherCard.qml.
The next upcoming event remains visible until it finishes. Multiple events can be configured inside:
hub/Events.qml
Google Calendar sync (vdirsyncer + khal)
Recommended installation method (avoids system Python packaging issues):
sudo pacman -S --needed python-pipxpipx install "vdirsyncer[google]"If both a system and pipx version of vdirsyncer exist, remove the system package and ensure ~/.local/bin appears earlier in PATH.
Create the required directories:
mkdir -p ~/.config/vdirsyncer/status ~/.config/vdirsyncer/tokens
mkdir -p ~/.local/share/vdirsyncer/calendarsExample configuration values:
token_file = "~/.config/vdirsyncer/tokens/google_calendar"
type = "google_calendar"
client_id / client_secret
Calendar files are stored in:
~/.local/share/vdirsyncer/calendars/*
Khal reads .ics files from this location.
- The CalDAV API must be enabled in Google Cloud.
- If OAuth consent is in testing mode, add yourself as a test user.
- If you receive “token obtained but Not Found”, enable calendars at:
https://calendar.google.com/calendar/syncselect
vdirsyncer discover
vdirsyncer sync
khal list now 7d- Clicking a notification dismisses it
- Uses dunst (
dunstctl) as the backend - Collapsed by default when the media card is active
- Can be expanded with the expand button
wlr-layershell power menu overlay (separate from the hub header menu). Toggled with ALT+F4 Run
# in Topbar mode:
quickshell -p ~/.config/quickshell/top-bar/bar/PowerMenu.qml# in Taskbar mode:
quickshell -p ~/.config/quickshell/task-bar/utils/PowerMenu.qmlStandalone network manager applet located at lib/WifiMenu.qml. With both (light/dark) theme.
- Trigger: Right-click the Wi-Fi button in the Hub.
- or run:
quickshell -p ~/<pathto>lib/Wifimenu.qml
Warning
You cannot connect to enterprise access points (for now), I haven't had the time to fix it yet
Custom on-screen displays for:
- Volume
- Brightness
- Various system modes (Dark, Light, Reading Mode, etc.)
Codex Stellarium is an interactive, customizeable astronomy inspired custom new tab/homepage. Replaces the default new tab with an interactive starfield, planetary system, and comet simulator. Contains:
- Canvas Animations: Interactive comets, planetary orbits, and a parallax starfield.
- Dynamic Theme: Auto-switches between light and dark modes based on local time or weather conditions.
- Live Data: Displays current weather (via Open-Meteo API), lunar phase, and sidereal time.
- Shortcuts: Configurable quick links.
Note
- I have a
.crxfile in the codex-stellarium directory if you want to use it in a chromium-based browser. - I also have other custom home/newtab pages in
.config/firefox/custom_homesthat can be installed with this method
.config/firefox/codex-stellarium
Firefox doesn't really want you to use local html as a new tab page, if you want to isntall codex stellarium manually or use your own html as custom new tab:
- Move
config/firefox/defaults/pref/autoconfig.jsto Firefox defaults/pref/ (e.g. /usr/lib/firefox/defaults/pref/) - Edit
config/firefox/mozilla.cfg(repo path:.config/firefox/mozilla.cfg) and set your file path - Move
mozilla.cfgto the Firefox install directory root (e.g. /usr/lib/firefox/)
/chrome/userChrome.css: A custom stylesheet that overrides the default Firefox interface. (These customizations work in windows or other os as well)
Expand for instructions to install custom usercss:
- Open Firefox and enter
about:configin the URL bar. - Accept the risk warning.
- Search for
toolkit.legacyUserProfileCustomizations.stylesheets. - Double-click to set the value to
true.
- Go to
about:profiles. - Find the profile box that states: "This is the profile in use and it cannot be deleted."
- Copy the path listed under Root Directory
(e.g.,/home/username/.mozilla/firefox/xxxxxxxx.default-release).
- Copy the
userChrome.cssinto thechromefolder
(create it if it doesn't exist, or move thechromefolder from this repo)
I use a modified version of Fausto-Korpsvart's Everforest gtk theme. The installer automatically copies it to the required directory for the theme toggle script to use it.
Kvantum theme files are located in:
.config/Kvantum
Additional related configuration files:
.config/qt6ct.config/color-schemes
Use Kvantum Manager to install and apply the Kvantum theme.
I have two SDDM themes:
- Stellarium SDDM theme (Astronomy inspired)
- Pixel SDDM theme (Google Pixel inspired)
The installer installs the themes and writes to conf.d automatically based on your choice. It will however prompt you for password authorization via pkexec.
- For Manual install:
-
move the contents of sddm/theme folder to
/usr/share/sddm/themes/(create the dir if it doesn't exist yet) and:sudo mkdir -p /etc/sddm.conf.d echo -e "[Theme]\nCurrent=stellarium" | sudo tee /etc/sddm.conf.d/theme.conf
-
Utilities include the following:
crt_gen.pya script to add crt like filters to an image (works best with images that aren't too bright or too dark)figures.pyscript to generate nice fun mathematical illustrationsSR4.icmColor profile for the display of the Surface Laptop 4. Import it in KDE Plasma to get Windows-like color calibration.- Fonts I like and use often
- Everforest-GTK-Theme by Fausto-Korpsvart
- Topbar mode Rofi themes loosely based on @adi1090x's type 7
Pixeldots.qmlin sddm theme based on @mahaveergurjar's Pixeldots- Colors: Modified from https://github.com/sainnhe/everforest
- VScode theme: Modified from Andrei Lucaci's Everforest pro theme
- Hyprshade
- Kvantum theme based on materia-everforest-kvantum
- Dave Hoskins for Hash without Sine
- Most svgs are from https://www.svgrepo.com/ , I made some myself
- linux-surface project: https://github.com/linux-surface/linux-surface
- Thorium: https://thorium.rocks/ for the background visualizations in firefox custom new tab
- My design inspiration comes mainly from : Microsoft design, Material design and calla. The typography and UI design used in the installer are my own original work, which I’ve also used in several of my other projects, including my website and the firefox extension.
- Photo by fffunction studio on Unsplash
- Photo by Brian McGowan on Unsplash
- Photo by Mimicry Hu on Unsplash
- Photo by Bailey Zindel on Unsplash
- Photo by Jay Yu on Unsplash
- Photo by Ben Dutton on Unsplash
- Photo by Richard Rhee on Flickr
- Photo by Cedric Chambaz on Flickr
- Photo by temo Berishvili on Unsplash
- Photo by Lucas Pezeta on Unsplash
- Photo by Andreas Strandman on Unsplash
- Surface default wallpapers are from microsoft
- All Rofi pictures were pulled from Pinterest; I don’t know the original owners.
Feel free to copy/steal whatever you want as long as you cite me and more importantly the listed media sources in the credits/references where applicable.
Q: Will this run on a distro other than Arch Linux?
A: I'm not sure about the installer but as long as you have the dependencies I don't see why it wouldn't.
Q: Can I use this setup with another compositor or desktop environment?
A: Yes. Most features, including Quickshell, will work correctly (as long as you're on wayland). Shaders are the only exception. However, some features exclusively rely on hyprland's ipcs, for best experience please use with hyprland
Q: Why use Flutter for the "Now Playing" widget?
A: It was one of my first projects while learning Flutter, which explains the older dependencies. Behind the Material Design frontend, it is just a standard MPRIS controller.
Q: How does the face unlock animation work?
A: It assumes the authentication was successful by default. You may need to adjust the timer in main.qml to get the timing right for a realistic effect. It does properly recognize authentication failures and timeouts.
Q: Why are there multiple app drawers (including the top-bar Rofi drawers)?
A: I am currently experimenting with different designs and layouts. My plan is to write a custom app drawer from scratch in rust with live tiles similar to Windows 10. I want it to feel distinctly native to Linux rather than acting as a cheap copy of the Windows Metro UI.
Q: Why are the desktop layouts two different shells instead of one unified shell with two options?
A: The project originally started as a simple calendar widget for my Google Calendar events. As more components were added over time, it evolved without a strict overall layout plan. Consolidating everything into a single shell would require significant code edits which I don't want to do atm
Q: How do I enable or disable screen borders?
A: (Only in taskbar mode) follow the instruction in shell.qml
Q: Components are misaligned in the hub. How do I fix them?
A: You can correct alignment by adding padding (left, right, up, down), adjusting spacing, or using the translate function. For example, to move weather in CalendarWeather card to the right:
// Right: Weather
ColumnLayout {
Layout.alignment: Qt.AlignTop | Qt.AlignRight
Layout.preferredWidth: 110
/* increase to move right, decrease (values can be negatives too) to move to the left */
transform: Translate { x: 5 } // <--- add thisQ: The taskbar is covering windows at the bottom of the screen. How do I fix this?
A: Decrease the layer exclusive zone in Taskbar.qml or increase the gaps in Hyprland config.
// desktop/Taskbar.qml
WlrLayershell.exclusiveZone: 47 // <-- change thisQ: Will you make a settings app to configure things instead of requiring file edits?
A: Yes, that is the plan. I do not have a strict timeline yet because there's so much spaghetti code
Q: Can I use the top-bar Rofi on the taskbar, or vice versa?
A: Yes. You just need to edit the path to the launcher script in your Hyprland configuration and update the on-click action within the launcher component for the respective bars.
Q: The theme switcher is not applying my GTK or Qt themes. How do I fix it?
A: First, make sure the script has executable permissions. Next, verify the theme files exist and match the names referenced in the script. Finally, run the script directly from the terminal to check for specific error messages abd fix them one by one.









