feat(rofi): real icons, zebra rows, content-fit menus across all themes

Reworked the menu look, palette-driven so it lands on every theme:

  - Real Papirus icons in place of Nerd Font glyphs: a row() helper emits
    rofi's per-row icon protocol, and the five static menus (root, Tools,
    System, Power, Capture) map each entry to a freedesktop icon name
    (verified present in Papirus). Entry text is now clean ("Apps"), the
    case-matching is unchanged.
  - Larger icons via a new t.ui.iconSize knob (36px, generated theme); the
    summer whole-swaps keep their 40px identity.
  - Rounded, roomy rows with subtle zebra striping: alternate rows lift to
    @surface (a faint bg0 wash on the summer cream/slate windows), the
    selected row to @accent — using rofi's normal/alternate/selected row
    parities.
  - Content-fit windows: fixed-num-lines=false so a 6-entry menu stops
    reserving 8 rows of empty space (the launcher still fills + scrolls).
    Dropped the fixed height:50% from the summer .rasi so they size to
    content too — which also unhid their clipped Power/Keybindings rows.
  - Title-cased the menu prompts.

Verified by building the HM config and rendering the generated theme plus
both summer whole-swaps headlessly (sway + grim): icons resolve, zebra
shows, every menu hugs its rows.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Bernardo Magri
2026-06-17 22:11:25 +01:00
parent b0b2a4d732
commit 994025f0c8
5 changed files with 252 additions and 53 deletions

163
config.rasi Normal file
View File

@@ -0,0 +1,163 @@
/**
* Rofi Everforest Theme
* Based on wofi everforest config
*/
* {
/* Everforest color definitions */
bg-dim: #232a2e;
bg0: #2d353b;
bg1: #343f44;
bg2: #3d484d;
bg3: #475258;
bg4: #4f585e;
bg5: #56635f;
bg-visual: #543a48;
bg-red: #514045;
bg-green: #425047;
bg-blue: #3a515d;
bg-yellow: #4d4c43;
fg: #d3c6aa;
red: #e67e80;
orange: #e69875;
yellow: #dbbc7f;
green: #a7c080;
aqua: #83c092;
blue: #7fbbb3;
purple: #d699b6;
grey0: #7a8478;
grey1: #859289;
grey2: #9da9a0;
/* Font settings */
font: "Iosevka Nerd Font 17";
/* General styling */
background-color: transparent;
text-color: @fg;
}
configuration {
/* Basic settings */
drun-display-format: "{name}";
case-sensitive: false;
location: 0;
disable-history: false;
hide-scrollbar: true;
display-drun: "Search";
/* Image settings */
drun-icon-theme: "Papirus-Dark";
show-icons: true;
icon-size: 40;
}
window {
/* Window styling based on wofi config */
transparency: "real";
background-color: @fg;
text-color: @bg0;
border-bottom: 5px;
border-color: #7d6a40;
border-radius: 15px;
width: 40%;
height: 50%;
location: center;
anchor: center;
x-offset: 0;
y-offset: 0;
}
mainbox {
background-color: transparent;
children: [ inputbar, listview ];
spacing: 0px;
padding: 0px;
}
inputbar {
children: [ prompt, entry ];
background-color: @green;
text-color: @bg0;
expand: false;
border-bottom: 5px;
border-color: #556a35;
border-radius: 10px;
margin: 15px;
padding: 10px;
spacing: 15px;
}
prompt {
enabled: true;
background-color: inherit;
text-color: inherit;
}
entry {
background-color: inherit;
text-color: inherit;
cursor: text;
placeholder: "Type to search...";
placeholder-color: @bg0;
}
listview {
background-color: transparent;
columns: 1;
lines: 8;
spacing: 0px;
cycle: true;
dynamic: true;
layout: vertical;
padding: 20px;
}
element {
background-color: transparent;
text-color: @bg0;
orientation: horizontal;
border-radius: 10px;
padding: 10px;
spacing: 15px;
}
element-icon {
background-color: transparent;
text-color: inherit;
size: 40px;
cursor: inherit;
}
element-text {
background-color: transparent;
text-color: inherit;
highlight: bold;
cursor: inherit;
vertical-align: 0.5;
horizontal-align: 0.0;
}
element selected {
background-color: @bg0;
text-color: @fg;
border-bottom: 5px;
border-color: #161a1d;
border-radius: 10px;
}
element-text selected {
text-color: @fg;
}
element-icon selected {
text-color: @fg;
}
scrollbar {
width: 0px;
background-color: transparent;
handle-width: 0px;
handle-color: transparent;
border: 0px;
}

View File

@@ -41,9 +41,9 @@ let
nomarchy-menu = pkgs.writeShellScriptBin "nomarchy-menu" '' nomarchy-menu = pkgs.writeShellScriptBin "nomarchy-menu" ''
# Nomarchy menu dispatcher thin presentation layer over # Nomarchy menu dispatcher thin presentation layer over
# `rofi -dmenu`; actions delegate to nomarchy-theme-sync, systemctl, # `rofi -dmenu`; actions delegate to nomarchy-theme-sync, systemctl,
# hyprctl and friends. Icons are nf-md glyphs (Pango falls back to a # hyprctl and friends. Menu entries carry real icons from the theme's
# Nerd Font for them). `nomarchy-menu` with no argument shows the # icon set (Papirus) via rofi's per-row icon protocol see row() below.
# module picker. # `nomarchy-menu` with no argument shows the module picker.
urlencode() { urlencode() {
local s="$1" out="" ch i local s="$1" out="" ch i
@@ -58,16 +58,22 @@ let
printf '%s' "$out" printf '%s' "$out"
} }
# Emit one dmenu row with a themed icon: `row "Label" icon-name`. The NUL
# + 0x1f field separator is rofi's per-row icon protocol; printed straight
# to the pipe because bash can't store NUL in a variable. Rendered when
# rofi has show-icons (on globally; passed explicitly on the icon menus).
row() { printf '%s\0icon\x1f%s\n' "$1" "$2"; }
case "''${1:-}" in case "''${1:-}" in
power) power)
choice=$(printf '%s\n' \ choice=$( {
"󰌾 Lock" \ row "Lock" system-lock-screen
"󰍃 Logout" \ row "Logout" system-log-out
"󰤄 Suspend" \ row "Suspend" system-suspend
"󰒲 Hibernate" \ row "Hibernate" system-hibernate
"󰜉 Reboot" \ row "Reboot" system-reboot
"󰐥 Shutdown" \ row "Shutdown" system-shutdown
| rofi -dmenu -p power) || exit 0 } | rofi -dmenu -show-icons -p Power) || exit 0
case "$choice" in case "$choice" in
*Lock) loginctl lock-session ;; *Lock) loginctl lock-session ;;
*Logout) hyprctl dispatch exit ;; *Logout) hyprctl dispatch exit ;;
@@ -134,12 +140,12 @@ let
exec blueman-manager ;; exec blueman-manager ;;
capture) capture)
choice=$(printf '%s\n' \ choice=$( {
"󰆟 Region clipboard" \ row "Region clipboard" applets-screenshooter
"󰆟 Region file" \ row "Region file" applets-screenshooter
"󰍹 Full screen clipboard" \ row "Full screen clipboard" camera-photo
"󰍹 Full screen file" \ row "Full screen file" camera-photo
| rofi -dmenu -p capture) || exit 0 } | rofi -dmenu -show-icons -p Capture) || exit 0
dir="$HOME/Pictures/Screenshots" dir="$HOME/Pictures/Screenshots"
file="$dir/$(date +%Y%m%d-%H%M%S).png" file="$dir/$(date +%Y%m%d-%H%M%S).png"
case "$choice" in case "$choice" in
@@ -183,16 +189,16 @@ let
tools) tools)
# Tools submenu utilities you invoke. Each leaf is also reachable # Tools submenu utilities you invoke. Each leaf is also reachable
# directly (SUPER+CTRL+<mnemonic>); this just keeps the root short. # directly (SUPER+CTRL+<mnemonic>); this just keeps the root short.
choice=$(printf '%s\n' \ choice=$( {
"󰃬 Calculator" \ row "Calculator" accessories-calculator
"󰅌 Clipboard" \ row "Clipboard" edit-paste
"󰒨 Emoji" \ row "Emoji" face-smile-big
"󰈞 Files" \ row "Files" system-file-manager
"󰖟 Web search" \ row "Web search" system-search
"󰄄 Capture" \ row "Capture" applets-screenshooter
"󰭹 Ask Claude" \ row "Ask Claude" internet-chat
"󰁍 Back" \ row "Back" go-previous
| rofi -dmenu -p tools) || exit 0 } | rofi -dmenu -show-icons -p Tools) || exit 0
case "$choice" in case "$choice" in
*Calc*) exec "$0" calc ;; *Calc*) exec "$0" calc ;;
*Clipboard*) exec "$0" clipboard ;; *Clipboard*) exec "$0" clipboard ;;
@@ -208,18 +214,17 @@ let
# System submenu connectivity and machine state. Snapshots and the # System submenu connectivity and machine state. Snapshots and the
# power-profile picker self-gate (BTRFS+snapper / laptop on ppd), so # power-profile picker self-gate (BTRFS+snapper / laptop on ppd), so
# the submenu shrinks to match the hardware, like the Waybar modules. # the submenu shrinks to match the hardware, like the Waybar modules.
entries=(
"󰖩 Network"
"󰂯 Bluetooth"
"󰂛 Do Not Disturb"
)
command -v btrfs-assistant >/dev/null 2>&1 && entries+=("󰋚 Snapshots")
bats=(/sys/class/power_supply/BAT*) bats=(/sys/class/power_supply/BAT*)
choice=$( {
row "Network" network-wireless
row "Bluetooth" bluetooth
row "Do Not Disturb" notification-disabled
command -v btrfs-assistant >/dev/null 2>&1 && row "Snapshots" timeshift
if [ -e "''${bats[0]}" ] && command -v powerprofilesctl >/dev/null 2>&1; then if [ -e "''${bats[0]}" ] && command -v powerprofilesctl >/dev/null 2>&1; then
entries+=("󰓅 Power profile") row "Power profile" preferences-system-power
fi fi
entries+=("󰁍 Back") row "Back" go-previous
choice=$(printf '%s\n' "''${entries[@]}" | rofi -dmenu -p system) || exit 0 } | rofi -dmenu -show-icons -p System) || exit 0
case "$choice" in case "$choice" in
*Network*) exec "$0" network ;; *Network*) exec "$0" network ;;
*Bluetooth*) exec "$0" bluetooth ;; *Bluetooth*) exec "$0" bluetooth ;;
@@ -233,14 +238,14 @@ let
# Root picker grouped into category submenus so the top level stays # Root picker grouped into category submenus so the top level stays
# short and scannable. Apps/Theme/Power/Keybindings are frequent enough # short and scannable. Apps/Theme/Power/Keybindings are frequent enough
# to keep at the top; everything else lives under Tools or System. # to keep at the top; everything else lives under Tools or System.
choice=$(printf '%s\n' \ choice=$( {
"󰀻 Apps" \ row "Apps" applications-all
"󰏘 Theme" \ row "Theme" preferences-desktop-theme
"󰦬 Tools" \ row "Tools" applications-utilities
"󰒓 System" \ row "System" preferences-system
"󰐥 Power" \ row "Power" system-shutdown
"󰋖 Keybindings" \ row "Keybindings" preferences-desktop-keyboard
| rofi -dmenu -p menu) || exit 0 } | rofi -dmenu -show-icons -p Menu) || exit 0
case "$choice" in case "$choice" in
*Apps*) exec rofi -show drun ;; *Apps*) exec rofi -show drun ;;
*Theme*) exec "$0" theme ;; *Theme*) exec "$0" theme ;;
@@ -283,6 +288,10 @@ in
drun-display-format = "{name}"; drun-display-format = "{name}";
display-drun = "apps"; display-drun = "apps";
display-run = "run"; display-run = "run";
# Size the window to the actual number of rows instead of always
# reserving `lines` (8) — so a 6-entry menu doesn't leave empty space,
# while the app launcher still fills + scrolls past `lines`.
fixed-num-lines = false;
}; };
# Whole-swap themes bring their own rofi.rasi; otherwise the theme # Whole-swap themes bring their own rofi.rasi; otherwise the theme
@@ -335,22 +344,34 @@ in
columns = 1; columns = 1;
dynamic = true; dynamic = true;
scrollbar = false; scrollbar = false;
spacing = px 2; spacing = px 4; # a little air so the rounded zebra rows separate
padding = mkLiteral "8px 0px 0px 0px"; padding = mkLiteral "8px 0px 0px 0px";
}; };
# Rounded, roomy rows with subtle zebra striping: alternate rows
# lift to @surface, the selected row to @accent. normal/alternate/
# selected are rofi's row parities (the `.normal` suffix is the
# match-state, untouched by our menus).
"element" = { "element" = {
padding = mkLiteral "8px 12px"; padding = mkLiteral "10px 14px";
border-radius = px t.ui.rounding; border-radius = px t.ui.rounding;
spacing = px 8; spacing = px 12; # gap between the (larger) icon and the label
}; };
"element selected" = { "element normal.normal" = {
background-color = mkLiteral "@bg";
text-color = mkLiteral "@fg";
};
"element alternate.normal" = {
background-color = mkLiteral "@surface";
text-color = mkLiteral "@fg";
};
"element selected.normal" = {
background-color = mkLiteral "@accent"; background-color = mkLiteral "@accent";
text-color = mkLiteral "@bg"; text-color = mkLiteral "@bg";
}; };
"element-icon" = { "element-icon" = {
background-color = mkLiteral "transparent"; background-color = mkLiteral "transparent";
size = mkLiteral "1em"; size = px t.ui.iconSize; # real icons (Papirus), not 1em font glyphs
}; };
"element-text" = { "element-text" = {
background-color = mkLiteral "transparent"; background-color = mkLiteral "transparent";

View File

@@ -38,6 +38,7 @@ let
gapsOut = 12; gapsOut = 12;
borderSize = 2; borderSize = 2;
rounding = 10; rounding = 10;
iconSize = 36; # rofi launcher + menu icon size (px) for the generated theme
activeOpacity = 1.0; activeOpacity = 1.0;
inactiveOpacity = 0.95; inactiveOpacity = 0.95;
terminalOpacity = 0.96; terminalOpacity = 0.96;

View File

@@ -29,7 +29,8 @@ window {
border-color: @yellow; border-color: @yellow;
border-radius: 15px; border-radius: 15px;
width: 40%; width: 40%;
height: 50%; /* No fixed height — size to content (fixed-num-lines:false) so every
* entry shows without scrolling and there's no empty space below. */
location: center; location: center;
anchor: center; anchor: center;
x-offset: 0; x-offset: 0;
@@ -90,6 +91,12 @@ element {
spacing: 15px; spacing: 15px;
} }
/* Zebra striping: every other row lifts with a faint light wash over the
* dark window (bg0 at ~12% alpha). The selected row below still wins. */
element alternate.normal {
background-color: #fdf6e31f;
}
element-icon { element-icon {
background-color: transparent; background-color: transparent;
text-color: inherit; text-color: inherit;

View File

@@ -29,7 +29,8 @@ window {
border-color: @yellow; border-color: @yellow;
border-radius: 15px; border-radius: 15px;
width: 40%; width: 40%;
height: 50%; /* No fixed height — size to content (fixed-num-lines:false) so every
* entry shows without scrolling and there's no empty space below. */
location: center; location: center;
anchor: center; anchor: center;
x-offset: 0; x-offset: 0;
@@ -90,6 +91,12 @@ element {
spacing: 15px; spacing: 15px;
} }
/* Zebra striping: every other row lifts with a faint dark wash over the
* cream window (bg0 at ~12% alpha). The selected row below still wins. */
element alternate.normal {
background-color: #2d353b1f;
}
element-icon { element-icon {
background-color: transparent; background-color: transparent;
text-color: inherit; text-color: inherit;