Match lock border to Hyprland style

This commit is contained in:
David Heinemeier Hansson
2026-06-03 13:10:48 +02:00
parent a8080d4016
commit e1099e4413
4 changed files with 56 additions and 15 deletions
+17 -10
View File
@@ -16,6 +16,13 @@ scale-with-font = true
size-horizontal = 26
size-vertical = 28
[hyprland]
# Shared Hyprland-derived border tokens. Surface sections reference these so
# lock, notifications, popups, and menu-style cards stay aligned with the
# current Hyprland active-border gradient.
active-border = "{{ shell_gradient hyprland_active_border accent }}"
active-border-foreground = "{{ shell_gradient hyprland_active_border foreground }}"
[controls]
# Shared state tokens for interactive control chrome (buttons, dropdowns,
# tab strips, etc).
@@ -120,7 +127,7 @@ base-size = 12
background = "{{ background }}"
background-alpha = 1.0
text = "{{ foreground }}"
border = "{{ shell_gradient hyprland_active_border accent }}"
border = "hyprland.active-border"
border-alpha = 1.0
# border-width = 2
@@ -130,7 +137,7 @@ border-alpha = 1.0
background = "{{ background }}"
background-alpha = 0.97
text = "{{ foreground }}"
border = "{{ shell_gradient hyprland_active_border foreground }}"
border = "hyprland.active-border-foreground"
border-alpha = 1.0
[notifications]
@@ -139,7 +146,7 @@ background-alpha = 1.0
text = "{{ foreground }}"
# Conventionally matches the Hyprland active-window border. Border accepts
# either a solid color or the full active-border gradient.
border = "{{ shell_gradient hyprland_active_border accent }}"
border = "hyprland.active-border"
border-alpha = 1.0
# border-width = 2
countdown = "{{ accent }}"
@@ -152,14 +159,14 @@ countdown = "{{ accent }}"
background = "{{ background }}"
background-alpha = 0.95
text = "{{ foreground }}"
border = "{{ shell_gradient hyprland_active_border foreground }}"
border = "hyprland.active-border-foreground"
border-alpha = 1.0
scrim = "{{ background }}"
scrim-alpha = 0.5
selected-background = "{{ foreground }}"
selected-background-alpha = 0.08
selected-text = "{{ accent }}"
selected-border = "{{ shell_gradient hyprland_active_border foreground }}"
selected-border = "hyprland.active-border-foreground"
selected-border-alpha = 0.25
[menu]
@@ -169,14 +176,14 @@ selected-border-alpha = 0.25
background = "{{ background }}"
background-alpha = 1.0
text = "{{ foreground }}"
border = "{{ shell_gradient hyprland_active_border foreground }}"
border = "hyprland.active-border-foreground"
border-alpha = 1.0
scrim = "{{ background }}"
scrim-alpha = 0.5
selected-background = "{{ foreground }}"
selected-background-alpha = 0.08
selected-text = "{{ accent }}"
selected-border = "{{ shell_gradient hyprland_active_border foreground }}"
selected-border = "hyprland.active-border-foreground"
selected-border-alpha = 0.25
[polkit]
@@ -189,7 +196,7 @@ background = "{{ background }}"
background-alpha = 1.0
text = "{{ foreground }}"
text-error = "{{ red }}"
border = "{{ shell_gradient hyprland_active_border accent }}"
border = "hyprland.active-border"
border-error = "{{ red }}"
border-alpha = 1.0
scrim = "{{ background }}"
@@ -208,8 +215,8 @@ background-alpha = 0.8
text = "{{ foreground }}"
placeholder = "{{ mix foreground background 34% }}"
text-error = "{{ red }}"
border = "{{ foreground }}"
border-active = "{{ accent }}"
border = "hyprland.active-border"
border-active = "hyprland.active-border"
border-error = "{{ red }}"
border-alpha = 1.0
# selection is the text-selection tint inside the input field.
+33 -1
View File
@@ -34,6 +34,18 @@ QtObject {
return ""
}
function resolveValueRef(raw) {
var s = String(raw || "").replace(/^\s+|\s+$/g, "")
var seen = {}
while (s.match(/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/) && !seen[s]) {
seen[s] = true
var next = Color.shellValues[s]
if (next === undefined || next === null || String(next).length === 0) break
s = String(next).replace(/^\s+|\s+$/g, "")
}
return s
}
function alpha(section, key, fallback) {
var raw = value(section, key)
if (String(raw).length === 0) return fallback
@@ -128,7 +140,7 @@ QtObject {
function surfaceSpec(section, token, fallbackColor, fallbackWidth, alphaKey) {
var opacity = alpha(section, alphaKey || token + "-alpha", 1.0)
var legacyGradientRaw = valueOr(section, token === "border" ? ["border-gradient"] : [token + "-gradient", "border-gradient"])
var resolved = borderValue(value(section, token), fallbackColor, opacity, legacyGradientRaw)
var resolved = borderValue(resolveValueRef(value(section, token)), fallbackColor, opacity, legacyGradientRaw)
return {
color: resolved.color,
@@ -137,6 +149,26 @@ QtObject {
}
}
function hyprlandActiveSpec(fallbackColor, fallbackWidth) {
var raw = value("hyprland", "active-border")
var opacity = alpha("hyprland", "active-border-alpha", 1.0)
// Existing generated themes predate [hyprland] and already keep the active
// border under [notifications]. Use it as the compatibility source until
// the next theme refresh writes the shared token.
if (String(raw).length === 0) {
raw = value("notifications", "border")
opacity = alpha("notifications", "border-alpha", opacity)
}
var resolved = borderValue(resolveValueRef(raw), fallbackColor, opacity, "")
return {
color: resolved.color,
widths: Geometry.parseWidthSpec(value("hyprland", "active-border-width"), fallbackWidth),
gradient: resolved.gradient,
}
}
function controlPrefix(state) {
if (state === "hover" || state === "hot") return "hover-cursor"
return state || "normal"
+1
View File
@@ -47,6 +47,7 @@ QtObject {
function flatColor(value, fallback) {
var token = firstColorToken(value)
var role = String(token || "").replace(/^\s+|\s+$/g, "").toLowerCase()
if (root.shellValues[role] && root.shellValues[role] !== token) return flatColor(root.shellValues[role], fallback)
if (role === "foreground" || role === "text") return root.foreground
if (role === "accent") return root.accent
if (role === "urgent") return root.urgent
+5 -4
View File
@@ -25,9 +25,10 @@ Item {
readonly property int passwordDotFontSize: Math.round(Style.font.heading * 1.33)
readonly property int passwordDotLetterSpacing: Math.round(Style.font.heading * 0.19)
readonly property bool showPasswordCursor: inputEnabled && !authenticatingPassword && failureMessage.length === 0
readonly property string borderToken: failureMessage.length > 0 ? "border-error" : (authenticatingPassword ? "border-active" : "border")
readonly property color borderFallback: failureMessage.length > 0 ? Color.lock.borderError : (authenticatingPassword ? Color.lock.borderActive : Color.lock.border)
readonly property var inputBorderSpec: Border.surfaceSpec("lock", borderToken, borderFallback, root.outlineThickness, "border-alpha")
readonly property bool errorState: failureMessage.length > 0
readonly property var inputBorderSpec: errorState
? Border.surfaceSpec("lock", "border-error", Color.lock.borderError, root.outlineThickness, "border-alpha")
: Border.hyprlandActiveSpec(Color.accent, root.outlineThickness)
signal submitPassword(string password)
signal passwordTextEdited(string password)
@@ -107,7 +108,7 @@ Item {
anchors.centerIn: parent
color: Color.lock.background
borderSpec: root.inputBorderSpec
radius: 0
radius: Style.cornerRadius
clip: true
TextInput {