theme: strip the remaining Persona punch from Hyprland and the popouts

Hyprland:
- Windows round at 8px, concentric with the porthole's 18px corners at
  the 10px gap, instead of square corners inside a rounded opening.
- Group tabs are solid hull slabs; the titles used to float over the
  wallpaper. The current tab is lifted a step with its title in the light.
- Motion rewritten to glide and settle: no overshoot curve, gentler
  popin, workspaces slide-fade a short way instead of sweeping the whole
  screen, and layers only fade so they never stack on quickshell's own
  slides.

Quickshell:
- Album art in the bar and the media popout is square in a hairline frame,
  not masked to a leaning chamfered shape; the accent slab under the
  popout art is gone, and a track change dips the title instead of
  tilting the art.
- The bar's equaliser is a five-hairline signal meter that rests as dots.
- Segment meters (system, battery, OSD) are 4px rows instead of 10-12px
  blocks; the toast fuse is a hairline; the clock's accent slab is a
  neutral divider.
- No hover pops: rail icons, tray icons, media transport buttons and
  P5Button no longer scale up or lunge sideways; the icon bump is reduced.

Terminal: ANSI bright black drops to #464d44. TUIs use it as a panel fill,
and at the outline grey it read as a flat light slab.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
nvrlandClaude Opus 5.5 committed 2026-10-04 20:45:24 +02:00
1 parent a27dd6f93b
commit ae6539218e
18 files changed
+144 -177

No files matched your search

+14 -6
View File
@@ -296,13 +296,18 @@ network and bluetooth, but keep running as the secret and pairing agents.
## The rest of the desktop
- **Hyprland** — `hypr/lua/look.lua`. No rounding, no blur, both opacities at
`1.0`, 1px borders. The active border runs from the light into the suit's
grey; the inactive border is `overlay`. The shadow is soft and wide, so
windows float in the opening rather than being printed onto it. `gaps_out`
is measured from the hull's opening, since the hull reserves its own zones.
- **Hyprland** — `hypr/lua/look.lua`. Windows round at 8px, concentric with
the opening's 18px corners at a 10px gap (`Theme.frameRadius - gaps_out`);
change one and the others follow. No blur, both opacities at `1.0`, 1px
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.
- **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
it after editing the palette.
The Catppuccin and `takemi.toml` files are left in place, unreferenced.
- **Neovim** — still the `catppuccin` plugin, flavour mocha, configured in
`nvim/lua/plugins/ui.lua`. Not yet moved to the YoRHa palette.
@@ -333,7 +338,10 @@ rectangles; the knobs are kept so a shape can be brought back on purpose. The
porthole's curve is the only one on screen, which is what lets it read.
**Motion glides.** No overshoot anywhere: things ease in on `OutCubic` or
`OutExpo` and settle. NieR's interface is calm and mechanical.
`OutExpo` and settle. Nothing scales up or nudges sideways on hover either;
hover brightens and marks, the way NieR's menus do. Meters are thin segment
rows, album art sits square in a hairline frame, and the bar's player shows a
five-hairline signal meter.
Each surface is **one continuous slab**, with modules divided by hairline rules
rather than by gaps. A row of separate floating cards reads as clutter; that is
+6 -7
View File
@@ -53,16 +53,15 @@ BarPill {
}
}
// Declared with room for its own lean, so the slash sits inside the box
// the Row gave it instead of hanging several px into the date column.
Skew {
// A hairline between the date and the time.
Rectangle {
anchors.verticalCenter: parent.verticalCenter
width: 22 * Theme.skew + 3
height: 22
color: Theme.alpha(Theme.accent, 0.6)
width: 1
height: 18
color: Theme.alpha(Theme.outline, 0.6)
}
// Archivo Black's digits are not all the same width, so a clock sized to
// The display face's digits are not all the same width, so a clock sized to
// its own text changed width as the minutes rolled over — 11:11 is
// several px narrower than 08:48 — and shoved the rest of the bar
// around. Reserve the widest possible reading and centre inside it.
+25 -31
View File
@@ -41,40 +41,27 @@ BarPill {
anchors.left: parent.left
spacing: Theme.padS
// Album art, chopped into the house shape.
Item {
// Album art in a hairline frame.
Rectangle {
id: artFrame
anchors.verticalCenter: parent.verticalCenter
width: 24
height: 24
Shape {
id: artMask
anchors.fill: parent
visible: false
layer.enabled: true
layer.smooth: true
preferredRendererType: Shape.CurveRenderer
ShapePath {
fillColor: "white"
strokeColor: "transparent"
PathPolyline {
path: Geom.chamfer(Geom.parallelogram(24, 24, 0.18), 5, [1, 3])
}
}
}
color: Theme.surface
border.width: 1
border.color: Theme.overlay
Image {
id: art
anchors.fill: parent
anchors.margins: 1
source: Media.artUrl
fillMode: Image.PreserveAspectCrop
cache: true
asynchronous: true
visible: status === Image.Ready
layer.enabled: true
layer.smooth: true
layer.effect: MaskedArt { maskItem: artMask }
sourceSize.width: 48
sourceSize.height: 48
}
// Fallback: the source app's glyph.
@@ -124,8 +111,10 @@ BarPill {
}
}
// Compact play-state equaliser. It moves only while audio is playing
// and freezes into three quiet bars when paused.
// Signal meter: five hairline bars, NieR's audio readout rather than a
// chunky equaliser. It moves only while audio is playing and settles
// into a row of square dots when paused — the channel is open, there
// is just nothing on it.
//
// Sampled from a timer rather than tweened by an infinite
// NumberAnimation. DP-1 runs at 240 Hz, and a frame-synced animation
@@ -144,7 +133,7 @@ BarPill {
Row {
id: eq
anchors.verticalCenter: parent.verticalCenter
spacing: 2
spacing: 3
// Sampling interval — the one knob trading smoothness against cost.
// Frames are the whole expense: a full-bar repaint runs about 0.3% of
@@ -163,8 +152,8 @@ BarPill {
// sine would, is three times the motion and reads far busier than
// this meter is supposed to.
function level(i: int): real {
const lo = 4 + (i * 5) % 13; // old first NumberAnimation `to`
const hi = 14 - (i * 4) % 9; // old second NumberAnimation `to`
const lo = 2 + (i * 3) % 5;
const hi = 14 - (i * 4) % 7;
const d1 = 320 + i * 90; // ...and their durations
const d2 = 280 + i * 70;
@@ -188,12 +177,12 @@ BarPill {
}
Repeater {
model: 3
model: 5
Rectangle {
required property int index
width: 3
height: Media.playing ? eq.level(index) : 6
width: 1
height: Media.playing ? eq.level(index) : 2
anchors.verticalCenter: parent.verticalCenter
color: Media.playing ? Theme.primary : Theme.muted
@@ -212,11 +201,16 @@ BarPill {
function onTrackChanged() { titleGlitch.restart(); }
}
// A new track resolves in, the way NieR text does: a brief dip and back.
SequentialAnimation {
id: titleGlitch
NumberAnimation {
target: artFrame; property: "rotation"
from: -8; to: 0; duration: Theme.durSlow; easing.type: Easing.OutCubic
target: meta; property: "opacity"
to: 0.25; duration: 60
}
NumberAnimation {
target: meta; property: "opacity"
to: 1; duration: Theme.durSlow; easing.type: Easing.OutCubic
}
}
+3 -9
View File
@@ -122,12 +122,10 @@ Item {
}
// Leading-edge wipe. On the rail this runs vertically down the left side,
// mirroring the slash P5Panel puts on horizontal modules.
// mirroring the accent bar P5Panel puts on horizontal modules.
//
// Held off the edge by `wipeInset`: the icon spans the rail's full width, so
// at x = 0 this landed exactly on the slab's own border, and the 1.08 hover
// scale then pushed it out past the silhouette onto the desktop. The inset
// has to survive that scale, which is why it is not simply 1 or 2.
// at x = 0 this landed exactly on the slab's own border.
//
// It is the accent rather than `tint` because it only ever appears on
// hover, and hover is the accent everywhere else in the shell (Theme.glow).
@@ -137,7 +135,7 @@ Item {
anchors.leftMargin: root.wipeInset
anchors.verticalCenter: parent.verticalCenter
width: 2
height: root.hovered ? root.height - 4 : 0
height: root.hovered ? Theme.marker * 2 : 0
color: Theme.glow
Behavior on height {
@@ -145,10 +143,6 @@ Item {
}
}
scale: root.hovered ? 1.08 : 1.0
Behavior on scale {
NumberAnimation { duration: Theme.durBase; easing.type: Easing.OutCubic }
}
onAlarmChanged: if (alarm) glyph.bump()
onLiveChanged: if (live) glyph.bump()
-4
View File
@@ -77,10 +77,6 @@ Column {
}
}
scale: itemHover.hovered ? 1.18 : 1.0
Behavior on scale {
NumberAnimation { duration: Theme.durBase; easing.type: Easing.OutCubic }
}
HoverHandler {
id: itemHover
-1
View File
@@ -76,7 +76,6 @@ Item {
NumberAnimation {
duration: Theme.durSlow
easing.type: Easing.OutCubic
easing.overshoot: 1.6
}
}
+1 -1
View File
@@ -35,7 +35,7 @@ Text {
id: bumpAnim
NumberAnimation {
target: root; property: "scale"
to: 1.35; duration: Theme.durFast; easing.type: Easing.OutExpo
to: 1.12; duration: Theme.durFast; easing.type: Easing.OutExpo
}
NumberAnimation {
target: root; property: "scale"
+3 -3
View File
@@ -2,8 +2,8 @@ import QtQuick
import "root:/config"
import "root:/components"
// One metric in a popout: a label and its reading on a line, with a wide
// sheared segmented meter underneath.
// One metric in a popout: a label and its reading on a line, with a thin
// segmented meter underneath.
//
// This replaces the radial Gauge. A 270° dial was the only curved thing in a
// shell whose whole geometry is sheared slabs and hard chamfers, so it read as
@@ -78,7 +78,7 @@ Item {
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
height: 12
height: 4
segments: root.segments
spacing: root.segmentSpacing
// Fill the row exactly rather than leaving a ragged tail on the right.
+3 -1
View File
@@ -12,7 +12,9 @@ FocusScope {
property bool destructive: false
property bool selected: false
property real lean: Theme.skew
property real lunge: 4
// Sideways nudge on hover. Zero: NieR buttons do not move, the marker
// and the inversion carry the state.
property real lunge: 0
property bool wide: false
signal clicked()
+5 -5
View File
@@ -171,8 +171,8 @@ PanelWindow {
anchors.verticalCenter: parent.verticalCenter
// Natural width, not a fixed one — SplitText offsets its
// ghosts with left/right anchor margins, so pinning the
// width and right-aligning the text would flatten the RGB
// split away. The title's left edge is anchored, so a
// width and right-aligning the text would flatten the
// glitch flicker away. The title's left edge is anchored, so a
// widening readout only moves its own left edge.
text: Math.round(root.level * 100) + "%"
pixelSize: Theme.fsBody
@@ -181,10 +181,10 @@ PanelWindow {
MeterBar {
width: parent.width
height: 10
height: 4
segments: 20
segmentWidth: (width - 19 * 3) / 20
spacing: 3
segmentWidth: (width - 19 * 2) / 20
spacing: 2
value: root.level * 100
activeColor: root.tone
}
-1
View File
@@ -179,7 +179,6 @@ PanelWindow {
selected: root.selectedAction === entry.index
accent: entry.modelData.danger ? Theme.alarm : Theme.primary
lean: Theme.skew
lunge: -10
onClicked: root.invoke(entry.modelData.act)
onHoveredChanged: if (hovered) root.selectedAction = entry.index
onActiveFocusChanged: if (actionButton.activeFocus) root.selectedAction = entry.index
+4 -9
View File
@@ -80,14 +80,9 @@ PanelWindow {
}
}
// Countdown rule that drains along the bottom edge.
//
// It used to be a full-bleed rectangle across the whole toast,
// which meant it ran straight through the card's chopped
// bottom-left corner and out the other side — a stray bar
// hanging off the card at the one place the silhouette is
// supposed to be cut. It lives inside the cut now, and ends
// where the leaning right edge does.
// Countdown hairline that drains along the bottom edge. The
// chop and lean terms are zero under the YoRHa theme; they are
// kept so the fuse still fits if a shape is brought back.
readonly property real fuseSpan: Math.max(0,
width - card.chop - card.implicitHeight * card.lean)
@@ -96,7 +91,7 @@ PanelWindow {
anchors.bottom: parent.bottom
anchors.bottomMargin: 1
x: card.chop
height: 2
height: 1
width: toast.fuseSpan
lean: card.lean
color: Theme.alpha(card.tone, 0.7)
+3 -3
View File
@@ -99,10 +99,10 @@ Popout {
anchors.topMargin: Theme.padM
anchors.left: parent.left
anchors.right: parent.right
height: 12
height: 4
segments: 20
segmentWidth: (width - 19 * 3) / 20
spacing: 3
segmentWidth: (width - 19 * 2) / 20
spacing: 2
value: Battery.percent
activeColor: Battery.tint
}
+6 -52
View File
@@ -32,62 +32,30 @@ Popout {
subtitle: Media.identity
}
// Cover art, chopped.
Item {
// Cover art in a hairline frame.
Rectangle {
id: artFrame
anchors.top: header.bottom
anchors.topMargin: Theme.padM
anchors.left: parent.left
width: 108
height: 108
Shape {
id: artMask
anchors.fill: parent
visible: false
layer.enabled: true
// Without this the mask texture is point-sampled, so the
// antialiased pixels along the cuts get snapped back to hard
// steps before MultiEffect ever sees them — see MaskedArt.
layer.smooth: true
preferredRendererType: Shape.CurveRenderer
ShapePath {
fillColor: "white"
strokeColor: "transparent"
PathPolyline {
path: Geom.chamfer(Geom.parallelogram(108, 108, 0.1), 18, [1, 3])
}
}
}
color: Theme.surface
border.width: 1
border.color: Theme.overlay
Image {
id: art
anchors.fill: parent
anchors.margins: 1
source: Media.artUrl
fillMode: Image.PreserveAspectCrop
asynchronous: true
cache: true
visible: status === Image.Ready
layer.enabled: true
layer.smooth: true
layer.effect: MaskedArt { maskItem: artMask }
}
// Placeholder when the player exposes no artwork.
Shape {
anchors.fill: parent
visible: art.status !== Image.Ready
preferredRendererType: Shape.CurveRenderer
ShapePath {
fillColor: Theme.surface
strokeColor: Theme.surfaceAlt
strokeWidth: 2
PathPolyline {
path: Geom.chamfer(Geom.parallelogram(108, 108, 0.1), 18, [1, 3])
}
}
}
Icon {
anchors.centerIn: parent
visible: art.status !== Image.Ready
@@ -95,16 +63,6 @@ Popout {
font.pixelSize: 42
color: Theme.muted
}
// Spinning rule around the art while playing.
Rectangle {
anchors.right: parent.right
anchors.bottom: parent.bottom
width: 30
height: 3
color: Theme.accent
visible: Media.playing
}
}
Column {
@@ -363,10 +321,6 @@ Popout {
}
}
scale: tbHover.hovered && tb.enabled ? 1.12 : 1.0
Behavior on scale {
NumberAnimation { duration: Theme.durBase; easing.type: Easing.OutCubic }
}
HoverHandler {
id: tbHover
+1 -1
View File
@@ -17,7 +17,7 @@ Item {
Row {
anchors.left: parent.left
anchors.top: parent.top
spacing: Theme.padS
spacing: Theme.padM
Icon {
anchors.verticalCenter: parent.verticalCenter