ui: single rounding knob, fix balloon-shaped cards

New settings/ui.nix holds `radius` as the one source of truth. Theme
derives radiusCard/Small/Tiny from it and hyprland's decoration.rounding
reads the same value, so one number rescales the whole desktop —
previously the shell and the window corners were unrelated constants,
which is why doubling Theme.radius left windows at 8.

Cards were using the SAME radius as the panel containing them. Nested
corners need outer − gap; matching them makes the inner corner
over-round for its size, which is the inflated/balloon look. All nine
inner surfaces now use radiusCard (radius − panelGap). Invisible at
r=8, obvious at r=32.

Radius 32 -> 24.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
rope 2026-07-28 13:58:28 +01:00
parent a36a3f35b7
commit b2e4e9863b
3 changed files with 42 additions and 13 deletions

View file

@ -3,6 +3,7 @@
let let
isMacbook = config.networking.hostName == "FredOS-Macbook"; isMacbook = config.networking.hostName == "FredOS-Macbook";
isGaming = !isMacbook; isGaming = !isMacbook;
ui = import ./ui.nix; # shared corner-rounding knob
in in
{ {
@ -114,7 +115,8 @@ in
}; };
decoration = { decoration = {
rounding = 8; # tracks Theme.radius via settings/ui.nix
rounding = ui.radius;
blur = { blur = {
enabled = true; enabled = true;
size = 1; size = 1;

View file

@ -4,6 +4,7 @@
{ config, pkgs, lib, ... }: { config, pkgs, lib, ... }:
let let
isMacbook = config.networking.hostName == "FredOS-Macbook"; isMacbook = config.networking.hostName == "FredOS-Macbook";
ui = import ./ui.nix; # shared corner-rounding knob
in in
{ {
config = lib.mkIf (lib.elem config.networking.hostName [ "FredOS-Gaming" "FredOS-Macbook" ]) { config = lib.mkIf (lib.elem config.networking.hostName [ "FredOS-Gaming" "FredOS-Macbook" ]) {
@ -180,9 +181,19 @@ in
// here, not in ~15 places. Wide enough for the stacked HH/mm // here, not in ~15 places. Wide enough for the stacked HH/mm
// clock, which is what sets the floor. // clock, which is what sets the floor.
readonly property int barWidth: 46 readonly property int barWidth: 46
readonly property int radius: 32 // cards, panels, dropdowns // Rounding. ONE knob: settings/ui.nix `radius`. Everything
readonly property int radiusSmall: 24 // workspace dots, pill buttons // below derives from it, and hyprland's window rounding reads
readonly property int radiusTiny: 16 // hover rows, action chips // the same value, so changing that one number rescales the
// roundness of the entire desktop.
readonly property int radius: ${toString ui.radius} // panels, dropdowns, screen cutout
// Concentric, NOT a factor: a card inside a panel needs the
// panel's radius MINUS the gap between them. Give it the same
// radius as its container and the inner corner is over-round
// for its size the card reads as inflated/balloon-like,
// invisible at r=8 and obvious by r=32.
readonly property int radiusCard: radius - panelGap
readonly property int radiusSmall: Math.round(radius * 0.75) // pill buttons, workspace dots
readonly property int radiusTiny: Math.round(radius * 0.5) // hover rows, action chips
readonly property int cardPad: 8 // card inner padding (inset = 2×) readonly property int cardPad: 8 // card inner padding (inset = 2×)
readonly property int panelGap: 8 // gap between panel border and cards (all dropdowns/menus/toast) readonly property int panelGap: 8 // gap between panel border and cards (all dropdowns/menus/toast)
@ -311,7 +322,7 @@ in
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
width: 280 width: 280
height: 44 height: 44
radius: Theme.radius radius: Theme.radiusCard
color: Theme.cardBg color: Theme.cardBg
border.width: Theme.borderWidth border.width: Theme.borderWidth
border.color: pam.active ? Theme.base0D border.color: pam.active ? Theme.base0D
@ -744,7 +755,7 @@ in
component Card: Rectangle { component Card: Rectangle {
default property alias cardData: _cardCol.data default property alias cardData: _cardCol.data
property alias cardSpacing: _cardCol.spacing property alias cardSpacing: _cardCol.spacing
radius: Theme.radius radius: Theme.radiusCard
color: Theme.cardBg color: Theme.cardBg
implicitHeight: _cardCol.height + 2 * Theme.cardPad implicitHeight: _cardCol.height + 2 * Theme.cardPad
Column { Column {
@ -1110,7 +1121,7 @@ in
Rectangle { Rectangle {
width: parent.width width: parent.width
height: launcherList.contentHeight + 2 * Theme.cardPad height: launcherList.contentHeight + 2 * Theme.cardPad
radius: Theme.radius radius: Theme.radiusCard
color: Theme.cardBg color: Theme.cardBg
visible: launcherList.count > 0 visible: launcherList.count > 0
@ -1173,7 +1184,7 @@ in
Rectangle { Rectangle {
width: parent.width width: parent.width
height: 36 height: 36
radius: Theme.radius radius: Theme.radiusCard
color: Theme.cardBg color: Theme.cardBg
SIcon { SIcon {
@ -2201,7 +2212,7 @@ in
anchors.centerIn: parent anchors.centerIn: parent
width: 48 width: 48
height: sessionCol.height + 8 height: sessionCol.height + 8
radius: Theme.radius radius: Theme.radiusCard
color: Theme.cardBg color: Theme.cardBg
// Sliding selection pill same tech as the power // Sliding selection pill same tech as the power
@ -2209,7 +2220,7 @@ in
Rectangle { Rectangle {
width: 40 width: 40
height: 40 height: 40
radius: Theme.radius radius: Theme.radiusCard
color: Theme.base02 color: Theme.base02
border.width: 1 border.width: 1
border.color: Theme.base03 border.color: Theme.base03
@ -2984,7 +2995,7 @@ in
Rectangle { Rectangle {
width: parent.width width: parent.width
height: weatherRow.height + 16 height: weatherRow.height + 16
radius: Theme.radius radius: Theme.radiusCard
color: Theme.cardBg color: Theme.cardBg
visible: calPopup.weatherDays.length > 0 visible: calPopup.weatherDays.length > 0
@ -3046,7 +3057,7 @@ in
property var pwNode: calPopup.streamFor(modelData) property var pwNode: calPopup.streamFor(modelData)
width: parent.width width: parent.width
height: mediaCol.height + 16 height: mediaCol.height + 16
radius: Theme.radius radius: Theme.radiusCard
color: Theme.cardBg color: Theme.cardBg
PwObjectTracker { objects: mediaCard.pwNode ? [mediaCard.pwNode] : [] } PwObjectTracker { objects: mediaCard.pwNode ? [mediaCard.pwNode] : [] }
@ -3497,7 +3508,7 @@ in
anchors.margins: Theme.panelGap anchors.margins: Theme.panelGap
width: 320 - 2 * Theme.panelGap width: 320 - 2 * Theme.panelGap
height: Math.max(toastPreview.visible ? 48 : 0, toastCol.height) + 16 height: Math.max(toastPreview.visible ? 48 : 0, toastCol.height) + 16
radius: Theme.radius radius: Theme.radiusCard
color: Theme.cardBg color: Theme.cardBg
property string previewSource: bar.notifPreviewSource(toastItem.currentNotif) property string previewSource: bar.notifPreviewSource(toastItem.currentNotif)

16
settings/ui.nix Normal file
View file

@ -0,0 +1,16 @@
# settings/ui.nix — shared UI metrics.
#
# Single source of truth for corner rounding. Both the quickshell shell
# (settings/quickshell.nix -> Theme.qml) and hyprland's window decoration
# (settings/hyprland.nix) read `radius` from here, and every other rounding
# value is derived from it, so this one number rescales the roundness of the
# whole desktop — panels, cards, pills, hover rows and window corners.
#
# Derived values live next to their consumers:
# Theme.radiusCard = radius - panelGap (concentric nesting, see Theme.qml)
# Theme.radiusSmall = radius * 0.75
# Theme.radiusTiny = radius * 0.5
# hyprland decoration.rounding = radius
{
radius = 24;
}