theme: workspaces drift behind the hull instead of over the bar

The hull moves to the top layer, with its transparent opening and empty input
region, so windows sliding out of the opening during a workspace switch pass
behind the frame like things behind a porthole rim rather than drawing over
the bar. A layer rule keeps the bar and the rail above it on that layer.

The animation tree is redone around that: workspaces drift 18% vertically
and fade on a slow, jolt-free "drift" curve; the SUPER+S scratchpad slides in
whole from under the bar, the same gesture as the dashboard and popouts;
windows and layers keep their calm resolve and fade.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
nvrlandClaude Opus 5.5 committed 2026-10-04 21:13:11 +02:00
1 parent 63a910bf14
commit 9af24ccf87
4 files changed
+55 -18

No files matched your search

+25 -8
View File
@@ -4,10 +4,22 @@
-- overshoots, nothing pops. Matches Theme.easeSnap / easeGlide in
-- ~/.config/quickshell/config/Theme.qml.
--
-- Windows fade in from a hair smaller, the way a NieR menu panel resolves
-- rather than arrives. Workspaces slide a short way and fade instead of
-- sweeping the whole screen across, which on a 2560px panel is a lot of
-- motion for "you are somewhere else now". The border angle never animates.
-- The screen is a porthole: the hull (quickshell/bar/Frame.qml) sits on the
-- top layer, so anything that moves past the edge of the opening passes
-- behind the frame instead of over the bar.
--
-- windows resolve in from a hair smaller and fade, the way a NieR menu
-- panel appears rather than arrives; leave the same way, faster.
-- workspaces drift: the old one sinks a little way behind the rim as it
-- fades, the new one rises into its place. A short, slow
-- vertical drift rather than a full-screen sweep, which on a
-- 2560px panel is a lot of motion for "you are somewhere else
-- now". Direction follows workspace order.
-- scratchpad a drawer: SUPER+S lowers it out from under the bar, the same
-- gesture as the dashboard and the popouts, and raises it back.
-- layers only fade; quickshell animates its own surfaces.
--
-- Nothing overshoots and the border angle never animates.
hl.config({
animations = {
@@ -23,6 +35,9 @@ hl.curve("standard", { type = "bezier", points = { { 0.40, 0.00 }, { 0.20, 1.00
-- Leaving: ease in, so the exit gathers pace and is gone.
hl.curve("exit", { type = "bezier", points = { { 0.55, 0.00 }, { 0.90, 0.35 } } })
hl.curve("linear", { type = "bezier", points = { { 0, 0 }, { 1, 1 } } })
-- easeInOutSine-ish with a long tail: starts without a jolt, settles slowly.
-- For motion big enough that a fast start would read as a shove.
hl.curve("drift", { type = "bezier", points = { { 0.35, 0.00 }, { 0.15, 1.00 } } })
-- --------------------------------------------------------------------- tree
-- speed is in ds (1ds = 100ms). Unset leaves inherit their parent.
@@ -48,8 +63,10 @@ hl.animation({ leaf = "layers", enabled = true, speed = 2.6, bezier = "glide" })
hl.animation({ leaf = "layersIn", enabled = true, speed = 2.6, bezier = "glide", style = "fade" })
hl.animation({ leaf = "layersOut", enabled = true, speed = 2, bezier = "exit", style = "fade" })
hl.animation({ leaf = "workspaces", enabled = true, speed = 4, bezier = "glide", style = "slidefadevert 12%" })
hl.animation({ leaf = "workspacesIn", enabled = true, speed = 4, bezier = "glide", style = "slidefadevert 12%" })
hl.animation({ leaf = "workspacesOut", enabled = true, speed = 3, bezier = "exit", style = "slidefadevert 12%" })
hl.animation({ leaf = "workspaces", enabled = true, speed = 5.5, bezier = "drift", style = "slidefadevert 18%" })
hl.animation({ leaf = "workspacesIn", enabled = true, speed = 5.5, bezier = "drift", style = "slidefadevert 18%" })
hl.animation({ leaf = "workspacesOut", enabled = true, speed = 4.5, bezier = "drift", style = "slidefadevert 18%" })
hl.animation({ leaf = "specialWorkspace", enabled = true, speed = 4, bezier = "glide", style = "slidefadevert 20%" })
hl.animation({ leaf = "specialWorkspace", enabled = true, speed = 4.5, bezier = "glide", style = "slidevert" })
hl.animation({ leaf = "specialWorkspaceIn", enabled = true, speed = 4.5, bezier = "glide", style = "slidevert" })
hl.animation({ leaf = "specialWorkspaceOut", enabled = true, speed = 3.5, bezier = "exit", style = "slidevert" })
+10
View File
@@ -11,3 +11,13 @@ for _, namespace in ipairs({ "bottom", "top", "takemi-launcher" }) do
ignore_alpha = 0.5,
})
end
-- The hull shares the top layer with the bar and the rail, which draw on it,
-- so they are ordered above it whatever order they map in. See
-- quickshell/bar/Frame.qml.
for _, namespace in ipairs({ "takemi-bar", "takemi-rail" }) do
hl.layer_rule({
match = { namespace = namespace },
order = -1,
})
end
+12 -5
View File
@@ -310,8 +310,11 @@ network and bluetooth, but keep running as the secret and pairing agents.
borders running from the light into the suit's grey. The shadow is soft and
wide, so windows float in the opening. Group tabs are solid hull slabs, the
current one lifted a step with its title in the light. Motion
(`hypr/lua/animations.lua`) glides and settles with no overshoot; layers only
fade, since quickshell animates its own surfaces.
(`hypr/lua/animations.lua`) glides and settles with no overshoot: windows
resolve in from a hair smaller, workspaces drift a short way vertically and
fade (sliding behind the hull), the `SUPER+S` scratchpad lowers out from
under the bar like the dashboard, and layers only fade, since quickshell
animates its own surfaces.
- **Alacritty** — `alacritty/yorha.toml`, selected by the `import` in
`alacritty.toml`. Muted earth hues on the void; selection inverts.
Alacritty only live-reloads when `alacritty.toml` itself changes, so touch
@@ -337,9 +340,13 @@ still exists as a no-op property so old call sites need not be chased down.
**The hull.** Borrowed from caelestia's screen border: the bar, the rail and a
thin band down the left and along the bottom (`Theme.frame`) are one surface,
drawn by `bar/Frame.qml` on the bottom layer, and the desktop is a rounded
opening cut out of it (`Theme.frameRadius`). Read against the wallpaper, you are
inside the ship and the windows sit in the porthole. The bar and the rail draw
drawn by `bar/Frame.qml`, and the desktop is a rounded opening cut out of it
(`Theme.frameRadius`). Read against the wallpaper, you are inside the ship and
the windows sit in the porthole. The hull is on the top layer, over the
windows, with a transparent opening and no input region, so a window moving
past the opening's edge — a workspace switch, the scratchpad drawer — passes
behind the frame rather than over the bar. `hypr/lua/rules/layers.lua` orders
the bar and the rail above it. The bar and the rail draw
no surface of their own and ignore exclusive zones; Frame reserves all four
zones itself, so every piece is placed from the screen edge.
+8 -5
View File
@@ -9,10 +9,13 @@ import "root:/config"
// The idea is caelestia's screen border; the reading is the wallpaper's — you
// are inside the ship, and the windows sit in the porthole.
//
// The hull lives on the bottom layer, under every window. Hyprland's gaps keep
// tiled windows inside the opening, so nothing ever has to be drawn over a
// window; a floating window dragged onto the frame simply covers it, and a
// fullscreen one hides it, which is what should happen in both cases.
// The hull lives on the top layer, over every window, and its opening is
// transparent with no input region, so it covers nothing inside the opening
// and catches no clicks. Being above the windows is the point: a workspace
// switch slides windows *behind* the frame, the way things pass behind the
// rim of a porthole, instead of drawing over the bar. A fullscreen window
// still covers it, since Hyprland draws fullscreen above the top layer.
// hypr/lua/rules/layers.lua orders it under the bar and the rail.
//
// The bar and the rail draw no surface of their own and ignore exclusive
// zones, so every piece is placed from the screen edge. The four zones are
@@ -29,7 +32,7 @@ Scope {
screen: root.modelData
WlrLayershell.layer: WlrLayer.Bottom
WlrLayershell.layer: WlrLayer.Top
WlrLayershell.namespace: "takemi-frame"
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
exclusionMode: ExclusionMode.Ignore