From 788c1fa9e631f0a4d0cbb6ab95e47251ca0be46a Mon Sep 17 00:00:00 2001 From: purian23 Date: Thu, 26 Feb 2026 01:09:21 -0500 Subject: [PATCH] feat: Implement Material Design 3 elevation and shadow effects - Added global toggle for enabling M3 elevation & shadow effects - Refactored various components to sync updated specs --- quickshell/Common/SettingsData.qml | 2 ++ quickshell/Common/Theme.qml | 13 +++++++ quickshell/Common/settings/SettingsSpec.js | 1 + quickshell/Modals/Common/DankModal.qml | 26 ++++++++++++++ quickshell/Modules/DankBar/BarCanvas.qml | 2 +- .../Modules/DankBar/Widgets/SystemTrayBar.qml | 18 +++++----- .../Modules/DankDash/MediaDropdownOverlay.qml | 36 +++++++++---------- .../Modules/DankDash/MediaPlayerTab.qml | 12 +++---- quickshell/Modules/DankDash/WeatherTab.qml | 12 +++---- .../Notifications/Popup/NotificationPopup.qml | 23 ++++++------ quickshell/Modules/Settings/AboutTab.qml | 11 +++--- .../Modules/Settings/NotificationsTab.qml | 2 +- .../Modules/Settings/ThemeColorsTab.qml | 10 ++++++ .../Modules/Settings/TimeWeatherTab.qml | 12 +++---- quickshell/Modules/Toast.qml | 10 +++--- .../WorkspaceOverlays/OverviewWidget.qml | 12 +++---- quickshell/Widgets/AppIconRenderer.qml | 2 +- quickshell/Widgets/DankCircularImage.qml | 2 ++ quickshell/Widgets/DankDropdown.qml | 8 ++--- quickshell/Widgets/DankIconPicker.qml | 11 +++--- quickshell/Widgets/DankOSD.qml | 11 +++--- quickshell/Widgets/DankPopout.qml | 17 +++++---- .../matugen/templates/firefox-userchrome.css | 14 +++++--- .../translations/settings_search_index.json | 17 +++++++++ 24 files changed, 181 insertions(+), 103 deletions(-) diff --git a/quickshell/Common/SettingsData.qml b/quickshell/Common/SettingsData.qml index edc3087d..d7341a4c 100644 --- a/quickshell/Common/SettingsData.qml +++ b/quickshell/Common/SettingsData.qml @@ -165,6 +165,8 @@ Singleton { property int modalCustomAnimationDuration: 150 property bool enableRippleEffects: true onEnableRippleEffectsChanged: saveSettings() + property bool m3ElevationEnabled: true + onM3ElevationEnabledChanged: saveSettings() property string wallpaperFillMode: "Fill" property bool blurredWallpaperLayer: false property bool blurWallpaperOnOverview: false diff --git a/quickshell/Common/Theme.qml b/quickshell/Common/Theme.qml index f19ae06b..f9aeac78 100644 --- a/quickshell/Common/Theme.qml +++ b/quickshell/Common/Theme.qml @@ -673,6 +673,19 @@ Singleton { property color shadowMedium: Qt.rgba(0, 0, 0, 0.08) property color shadowStrong: Qt.rgba(0, 0, 0, 0.3) + readonly property bool elevationEnabled: typeof SettingsData !== "undefined" && (SettingsData.m3ElevationEnabled ?? true) + readonly property real elevationBlurMax: 64 + readonly property var elevationLevel1: ({ blurPx: 4, offsetY: 1, spreadPx: 0, alpha: 0.2 }) + readonly property var elevationLevel2: ({ blurPx: 8, offsetY: 4, spreadPx: 0, alpha: 0.25 }) + readonly property var elevationLevel3: ({ blurPx: 12, offsetY: 6, spreadPx: 0, alpha: 0.3 }) + readonly property var elevationLevel4: ({ blurPx: 16, offsetY: 8, spreadPx: 0, alpha: 0.3 }) + readonly property var elevationLevel5: ({ blurPx: 20, offsetY: 10, spreadPx: 0, alpha: 0.3 }) + function elevationShadowColor(level) { + const alpha = (level && level.alpha !== undefined) ? level.alpha : 0.3; + const baseColor = isLightMode ? Qt.rgba(0, 0, 0, 1) : surfaceContainerHighest; + return Theme.withAlpha(baseColor, alpha * (typeof SettingsData !== "undefined" ? SettingsData.popupTransparency : 1)); + } + readonly property var animationDurations: [ { "shorter": 0, diff --git a/quickshell/Common/settings/SettingsSpec.js b/quickshell/Common/settings/SettingsSpec.js index d347e271..d56c4ed1 100644 --- a/quickshell/Common/settings/SettingsSpec.js +++ b/quickshell/Common/settings/SettingsSpec.js @@ -46,6 +46,7 @@ var SPEC = { modalAnimationSpeed: { def: 1 }, modalCustomAnimationDuration: { def: 150 }, enableRippleEffects: { def: true }, + m3ElevationEnabled: { def: true }, wallpaperFillMode: { def: "Fill" }, blurredWallpaperLayer: { def: false }, blurWallpaperOnOverview: { def: false }, diff --git a/quickshell/Modals/Common/DankModal.qml b/quickshell/Modals/Common/DankModal.qml index 3c94d189..4186fb67 100644 --- a/quickshell/Modals/Common/DankModal.qml +++ b/quickshell/Modals/Common/DankModal.qml @@ -1,4 +1,5 @@ import QtQuick +import QtQuick.Effects import Quickshell import Quickshell.Wayland import qs.Common @@ -377,6 +378,31 @@ Item { } } + readonly property var elev: Theme.elevationLevel3 + readonly property real shadowBlurNorm: Math.max(0, Math.min(1, (elev && elev.blurPx !== undefined ? elev.blurPx : 12) / Theme.elevationBlurMax)) + + Rectangle { + id: modalShadowShape + anchors.fill: parent + radius: root.cornerRadius + color: "black" + visible: false + layer.enabled: root.enableShadow && Theme.elevationEnabled && Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1" + layer.smooth: false + + layer.effect: MultiEffect { + autoPaddingEnabled: true + shadowEnabled: true + blurEnabled: false + maskEnabled: false + shadowBlur: modalShadowShape.parent.shadowBlurNorm + shadowScale: 1 + shadowVerticalOffset: modalShadowShape.parent.elev && modalShadowShape.parent.elev.offsetY !== undefined ? modalShadowShape.parent.elev.offsetY : 6 + shadowHorizontalOffset: 0 + shadowColor: Theme.elevationShadowColor(Theme.elevationLevel3) + } + } + Rectangle { anchors.fill: parent color: root.backgroundColor diff --git a/quickshell/Modules/DankBar/BarCanvas.qml b/quickshell/Modules/DankBar/BarCanvas.qml index ab0bdf97..13130fba 100644 --- a/quickshell/Modules/DankBar/BarCanvas.qml +++ b/quickshell/Modules/DankBar/BarCanvas.qml @@ -121,7 +121,7 @@ Item { Loader { id: shadowLoader anchors.fill: parent - active: root.shadowEnabled && mainPathCorrectShape + active: root.shadowEnabled && mainPathCorrectShape && Theme.elevationEnabled asynchronous: false sourceComponent: Item { anchors.fill: parent diff --git a/quickshell/Modules/DankBar/Widgets/SystemTrayBar.qml b/quickshell/Modules/DankBar/Widgets/SystemTrayBar.qml index 750b1d90..5bf10048 100644 --- a/quickshell/Modules/DankBar/Widgets/SystemTrayBar.qml +++ b/quickshell/Modules/DankBar/Widgets/SystemTrayBar.qml @@ -940,9 +940,10 @@ BasePill { } })(), overflowMenu.dpr) - property real shadowBlurPx: 10 - property real shadowSpreadPx: 0 - property real shadowBaseAlpha: 0.60 + readonly property var elev: Theme.elevationLevel2 + property real shadowBlurPx: elev && elev.blurPx !== undefined ? elev.blurPx : 8 + property real shadowSpreadPx: elev && elev.spreadPx !== undefined ? elev.spreadPx : 0 + property real shadowBaseAlpha: elev && elev.alpha !== undefined ? elev.alpha : 0.25 readonly property real popupSurfaceAlpha: Theme.popupTransparency readonly property real effectiveShadowAlpha: Math.max(0, Math.min(1, shadowBaseAlpha * popupSurfaceAlpha)) @@ -966,7 +967,7 @@ BasePill { Item { id: bgShadowLayer anchors.fill: parent - layer.enabled: true + layer.enabled: Theme.elevationEnabled layer.smooth: true layer.textureSize: Qt.size(Math.round(width * overflowMenu.dpr * 2), Math.round(height * overflowMenu.dpr * 2)) layer.textureMirroring: ShaderEffectSource.MirrorVertically @@ -1412,9 +1413,10 @@ BasePill { } })(), menuWindow.dpr) - property real shadowBlurPx: 10 - property real shadowSpreadPx: 0 - property real shadowBaseAlpha: 0.60 + readonly property var elev: Theme.elevationLevel2 + property real shadowBlurPx: elev && elev.blurPx !== undefined ? elev.blurPx : 8 + property real shadowSpreadPx: elev && elev.spreadPx !== undefined ? elev.spreadPx : 0 + property real shadowBaseAlpha: elev && elev.alpha !== undefined ? elev.alpha : 0.25 readonly property real popupSurfaceAlpha: Theme.popupTransparency readonly property real effectiveShadowAlpha: Math.max(0, Math.min(1, shadowBaseAlpha * popupSurfaceAlpha)) @@ -1438,7 +1440,7 @@ BasePill { Item { id: menuBgShadowLayer anchors.fill: parent - layer.enabled: true + layer.enabled: Theme.elevationEnabled layer.smooth: true layer.textureSize: Qt.size(Math.round(width * menuWindow.dpr), Math.round(height * menuWindow.dpr)) layer.textureMirroring: ShaderEffectSource.MirrorVertically diff --git a/quickshell/Modules/DankDash/MediaDropdownOverlay.qml b/quickshell/Modules/DankDash/MediaDropdownOverlay.qml index 9c540f2d..d6bb9ed3 100644 --- a/quickshell/Modules/DankDash/MediaDropdownOverlay.qml +++ b/quickshell/Modules/DankDash/MediaDropdownOverlay.qml @@ -89,14 +89,14 @@ Item { } } - layer.enabled: true + layer.enabled: Theme.elevationEnabled layer.effect: MultiEffect { - shadowEnabled: true + shadowEnabled: Theme.elevationEnabled shadowHorizontalOffset: 0 - shadowVerticalOffset: 8 - shadowBlur: 1.0 - shadowColor: Qt.rgba(0, 0, 0, 0.4) - shadowOpacity: 0.7 + shadowVerticalOffset: Theme.elevationLevel2 && Theme.elevationLevel2.offsetY !== undefined ? Theme.elevationLevel2.offsetY : 4 + shadowBlur: Theme.elevationEnabled ? Math.max(0, Math.min(1, (Theme.elevationLevel2 && Theme.elevationLevel2.blurPx !== undefined ? Theme.elevationLevel2.blurPx : 8) / Theme.elevationBlurMax)) : 0 + shadowColor: Theme.elevationShadowColor(Theme.elevationLevel2) + shadowOpacity: Theme.elevationLevel2 && Theme.elevationLevel2.alpha !== undefined ? Theme.elevationLevel2.alpha : 0.25 } MouseArea { @@ -223,14 +223,14 @@ Item { } } - layer.enabled: true + layer.enabled: Theme.elevationEnabled layer.effect: MultiEffect { - shadowEnabled: true + shadowEnabled: Theme.elevationEnabled shadowHorizontalOffset: 0 - shadowVerticalOffset: 8 - shadowBlur: 1.0 - shadowColor: Qt.rgba(0, 0, 0, 0.4) - shadowOpacity: 0.7 + shadowVerticalOffset: Theme.elevationLevel2 && Theme.elevationLevel2.offsetY !== undefined ? Theme.elevationLevel2.offsetY : 4 + shadowBlur: Theme.elevationEnabled ? Math.max(0, Math.min(1, (Theme.elevationLevel2 && Theme.elevationLevel2.blurPx !== undefined ? Theme.elevationLevel2.blurPx : 8) / Theme.elevationBlurMax)) : 0 + shadowColor: Theme.elevationShadowColor(Theme.elevationLevel2) + shadowOpacity: Theme.elevationLevel2 && Theme.elevationLevel2.alpha !== undefined ? Theme.elevationLevel2.alpha : 0.25 } Column { @@ -373,14 +373,14 @@ Item { } } - layer.enabled: true + layer.enabled: Theme.elevationEnabled layer.effect: MultiEffect { - shadowEnabled: true + shadowEnabled: Theme.elevationEnabled shadowHorizontalOffset: 0 - shadowVerticalOffset: 8 - shadowBlur: 1.0 - shadowColor: Qt.rgba(0, 0, 0, 0.4) - shadowOpacity: 0.7 + shadowVerticalOffset: Theme.elevationLevel2 && Theme.elevationLevel2.offsetY !== undefined ? Theme.elevationLevel2.offsetY : 4 + shadowBlur: Theme.elevationEnabled ? Math.max(0, Math.min(1, (Theme.elevationLevel2 && Theme.elevationLevel2.blurPx !== undefined ? Theme.elevationLevel2.blurPx : 8) / Theme.elevationBlurMax)) : 0 + shadowColor: Theme.elevationShadowColor(Theme.elevationLevel2) + shadowOpacity: Theme.elevationLevel2 && Theme.elevationLevel2.alpha !== undefined ? Theme.elevationLevel2.alpha : 0.25 } Column { diff --git a/quickshell/Modules/DankDash/MediaPlayerTab.qml b/quickshell/Modules/DankDash/MediaPlayerTab.qml index 6a72bb9f..8c9deb65 100644 --- a/quickshell/Modules/DankDash/MediaPlayerTab.qml +++ b/quickshell/Modules/DankDash/MediaPlayerTab.qml @@ -529,14 +529,14 @@ Item { onClicked: activePlayer && activePlayer.togglePlaying() } - layer.enabled: true + layer.enabled: Theme.elevationEnabled layer.effect: MultiEffect { - shadowEnabled: true + shadowEnabled: Theme.elevationEnabled shadowHorizontalOffset: 0 - shadowVerticalOffset: 0 - shadowBlur: 1.0 - shadowColor: Qt.rgba(0, 0, 0, 0.3) - shadowOpacity: 0.3 + shadowVerticalOffset: Theme.elevationLevel1 && Theme.elevationLevel1.offsetY !== undefined ? Theme.elevationLevel1.offsetY : 1 + shadowBlur: Theme.elevationEnabled ? Math.max(0, Math.min(1, (Theme.elevationLevel1 && Theme.elevationLevel1.blurPx !== undefined ? Theme.elevationLevel1.blurPx : 4) / Theme.elevationBlurMax)) : 0 + shadowColor: Theme.elevationShadowColor(Theme.elevationLevel1) + shadowOpacity: Theme.elevationLevel1 && Theme.elevationLevel1.alpha !== undefined ? Theme.elevationLevel1.alpha : 0.2 } } } diff --git a/quickshell/Modules/DankDash/WeatherTab.qml b/quickshell/Modules/DankDash/WeatherTab.qml index c2ee59b3..ce39b985 100644 --- a/quickshell/Modules/DankDash/WeatherTab.qml +++ b/quickshell/Modules/DankDash/WeatherTab.qml @@ -241,14 +241,14 @@ Item { color: Theme.primary anchors.verticalCenter: parent.verticalCenter - layer.enabled: true + layer.enabled: Theme.elevationEnabled layer.effect: MultiEffect { - shadowEnabled: true + shadowEnabled: Theme.elevationEnabled shadowHorizontalOffset: 0 - shadowVerticalOffset: 4 - shadowBlur: 0.8 - shadowColor: Qt.rgba(0, 0, 0, 0.2) - shadowOpacity: 0.2 + shadowVerticalOffset: Theme.elevationLevel1 && Theme.elevationLevel1.offsetY !== undefined ? Theme.elevationLevel1.offsetY : 1 + shadowBlur: Theme.elevationEnabled ? Math.max(0, Math.min(1, (Theme.elevationLevel1 && Theme.elevationLevel1.blurPx !== undefined ? Theme.elevationLevel1.blurPx : 4) / Theme.elevationBlurMax)) : 0 + shadowColor: Theme.elevationShadowColor(Theme.elevationLevel1) + shadowOpacity: Theme.elevationLevel1 && Theme.elevationLevel1.alpha !== undefined ? Theme.elevationLevel1.alpha : 0.2 } } diff --git a/quickshell/Modules/Notifications/Popup/NotificationPopup.qml b/quickshell/Modules/Notifications/Popup/NotificationPopup.qml index cf4f4e75..ea3123bb 100644 --- a/quickshell/Modules/Notifications/Popup/NotificationPopup.qml +++ b/quickshell/Modules/Notifications/Popup/NotificationPopup.qml @@ -313,12 +313,10 @@ PanelWindow { readonly property bool swipeActive: swipeDragHandler.active property bool swipeDismissing: false - readonly property real radiusForShadow: Theme.cornerRadius - property real shadowBlurPx: SettingsData.notificationPopupShadowEnabled ? ((2 + radiusForShadow * 0.2) * (cardHoverHandler.hovered ? 1.2 : 1)) : 0 - property real shadowSpreadPx: SettingsData.notificationPopupShadowEnabled ? (radiusForShadow * (cardHoverHandler.hovered ? 0.06 : 0)) : 0 - property real shadowBaseAlpha: 0.35 - readonly property real popupSurfaceAlpha: SettingsData.popupTransparency - readonly property real effectiveShadowAlpha: Math.max(0, Math.min(1, shadowBaseAlpha * popupSurfaceAlpha)) + readonly property bool shadowsAllowed: Theme.elevationEnabled && SettingsData.notificationPopupShadowEnabled + readonly property var elevLevel: cardHoverHandler.hovered ? Theme.elevationLevel4 : Theme.elevationLevel3 + property real shadowBlurPx: shadowsAllowed ? (elevLevel && elevLevel.blurPx !== undefined ? elevLevel.blurPx : 12) : 0 + property real shadowOffsetY: shadowsAllowed ? (elevLevel && elevLevel.offsetY !== undefined ? elevLevel.offsetY : 6) : 0 Behavior on shadowBlurPx { NumberAnimation { @@ -327,7 +325,7 @@ PanelWindow { } } - Behavior on shadowSpreadPx { + Behavior on shadowOffsetY { NumberAnimation { duration: Theme.shortDuration easing.type: Theme.standardEasing @@ -348,15 +346,14 @@ PanelWindow { layer.effect: MultiEffect { id: shadowFx autoPaddingEnabled: true - shadowEnabled: SettingsData.notificationPopupShadowEnabled + shadowEnabled: content.shadowsAllowed blurEnabled: false maskEnabled: false shadowBlur: Math.max(0, Math.min(1, content.shadowBlurPx / bgShadowLayer.blurMax)) - shadowScale: 1 + (2 * content.shadowSpreadPx) / Math.max(1, Math.min(bgShadowLayer.width, bgShadowLayer.height)) - shadowColor: { - const baseColor = Theme.isLightMode ? Qt.rgba(0, 0, 0, 1) : Theme.surfaceContainerHighest; - return Theme.withAlpha(baseColor, content.effectiveShadowAlpha); - } + shadowScale: 1 + shadowHorizontalOffset: 0 + shadowVerticalOffset: content.shadowOffsetY + shadowColor: content.shadowsAllowed && content.elevLevel ? Theme.elevationShadowColor(content.elevLevel) : "transparent" } Rectangle { diff --git a/quickshell/Modules/Settings/AboutTab.qml b/quickshell/Modules/Settings/AboutTab.qml index d2d9b14a..a80c45de 100644 --- a/quickshell/Modules/Settings/AboutTab.qml +++ b/quickshell/Modules/Settings/AboutTab.qml @@ -878,12 +878,13 @@ Item { x: hoveredButton ? hoveredButton.mapToItem(aboutTab, hoveredButton.width / 2, 0).x - width / 2 : 0 y: hoveredButton ? communityIcons.mapToItem(aboutTab, 0, 0).y - height - 8 : 0 - layer.enabled: true + layer.enabled: Theme.elevationEnabled layer.effect: MultiEffect { - shadowEnabled: true - shadowOpacity: 0.15 - shadowVerticalOffset: 2 - shadowBlur: 0.5 + shadowEnabled: Theme.elevationEnabled + shadowOpacity: Theme.elevationLevel1 && Theme.elevationLevel1.alpha !== undefined ? Theme.elevationLevel1.alpha : 0.2 + shadowVerticalOffset: Theme.elevationLevel1 && Theme.elevationLevel1.offsetY !== undefined ? Theme.elevationLevel1.offsetY : 1 + shadowBlur: Theme.elevationEnabled ? Math.max(0, Math.min(1, (Theme.elevationLevel1 && Theme.elevationLevel1.blurPx !== undefined ? Theme.elevationLevel1.blurPx : 4) / Theme.elevationBlurMax)) : 0 + shadowColor: Theme.elevationShadowColor(Theme.elevationLevel1) } StyledText { diff --git a/quickshell/Modules/Settings/NotificationsTab.qml b/quickshell/Modules/Settings/NotificationsTab.qml index 2c3f9e75..7294e75d 100644 --- a/quickshell/Modules/Settings/NotificationsTab.qml +++ b/quickshell/Modules/Settings/NotificationsTab.qml @@ -274,7 +274,7 @@ Item { settingKey: "notificationPopupShadowEnabled" tags: ["notification", "popup", "shadow", "radius", "rounded"] text: I18n.tr("Popup Shadow") - description: I18n.tr("Show drop shadow on notification popups") + description: I18n.tr("Show drop shadow on notification popups. Requires M3 Elevation to be enabled in Theme & Colors.") checked: SettingsData.notificationPopupShadowEnabled onToggled: checked => SettingsData.set("notificationPopupShadowEnabled", checked) } diff --git a/quickshell/Modules/Settings/ThemeColorsTab.qml b/quickshell/Modules/Settings/ThemeColorsTab.qml index 28b12d55..9764f157 100644 --- a/quickshell/Modules/Settings/ThemeColorsTab.qml +++ b/quickshell/Modules/Settings/ThemeColorsTab.qml @@ -1592,6 +1592,16 @@ Item { defaultValue: 12 onSliderValueChanged: newValue => SettingsData.setCornerRadius(newValue) } + + SettingsToggleRow { + tab: "theme" + tags: ["elevation", "shadow", "lift", "m3", "material"] + settingKey: "m3ElevationEnabled" + text: I18n.tr("M3 Elevation & Shadows") + description: I18n.tr("Material Design 3 shadows and elevation on modals, popouts, and dialogs") + checked: SettingsData.m3ElevationEnabled ?? true + onToggled: checked => SettingsData.set("m3ElevationEnabled", checked) + } } SettingsCard { diff --git a/quickshell/Modules/Settings/TimeWeatherTab.qml b/quickshell/Modules/Settings/TimeWeatherTab.qml index 56afd0c1..6bb52c21 100644 --- a/quickshell/Modules/Settings/TimeWeatherTab.qml +++ b/quickshell/Modules/Settings/TimeWeatherTab.qml @@ -663,14 +663,14 @@ Item { anchors.left: parent.left anchors.verticalCenter: parent.verticalCenter - layer.enabled: true + layer.enabled: Theme.elevationEnabled layer.effect: MultiEffect { - shadowEnabled: true + shadowEnabled: Theme.elevationEnabled shadowHorizontalOffset: 0 - shadowVerticalOffset: 4 - shadowBlur: 0.8 - shadowColor: Qt.rgba(0, 0, 0, 0.2) - shadowOpacity: 0.2 + shadowVerticalOffset: Theme.elevationLevel1 && Theme.elevationLevel1.offsetY !== undefined ? Theme.elevationLevel1.offsetY : 1 + shadowBlur: Theme.elevationEnabled ? Math.max(0, Math.min(1, (Theme.elevationLevel1 && Theme.elevationLevel1.blurPx !== undefined ? Theme.elevationLevel1.blurPx : 4) / Theme.elevationBlurMax)) : 0 + shadowColor: Theme.elevationShadowColor(Theme.elevationLevel1) + shadowOpacity: Theme.elevationLevel1 && Theme.elevationLevel1.alpha !== undefined ? Theme.elevationLevel1.alpha : 0.2 } } diff --git a/quickshell/Modules/Toast.qml b/quickshell/Modules/Toast.qml index ae19efaf..f74a0159 100644 --- a/quickshell/Modules/Toast.qml +++ b/quickshell/Modules/Toast.qml @@ -407,12 +407,12 @@ PanelWindow { } layer.effect: MultiEffect { - shadowEnabled: true + shadowEnabled: Theme.elevationEnabled shadowHorizontalOffset: 0 - shadowVerticalOffset: 4 - shadowBlur: 0.8 - shadowColor: Qt.rgba(0, 0, 0, 0.3) - shadowOpacity: 0.3 + shadowVerticalOffset: Theme.elevationLevel3 && Theme.elevationLevel3.offsetY !== undefined ? Theme.elevationLevel3.offsetY : 6 + shadowBlur: Theme.elevationEnabled ? Math.max(0, Math.min(1, (Theme.elevationLevel3 && Theme.elevationLevel3.blurPx !== undefined ? Theme.elevationLevel3.blurPx : 12) / Theme.elevationBlurMax)) : 0 + shadowColor: Theme.elevationShadowColor(Theme.elevationLevel3) + shadowOpacity: Theme.elevationLevel3 && Theme.elevationLevel3.alpha !== undefined ? Theme.elevationLevel3.alpha : 0.3 } Behavior on opacity { diff --git a/quickshell/Modules/WorkspaceOverlays/OverviewWidget.qml b/quickshell/Modules/WorkspaceOverlays/OverviewWidget.qml index 6c9e8fa2..1028c260 100644 --- a/quickshell/Modules/WorkspaceOverlays/OverviewWidget.qml +++ b/quickshell/Modules/WorkspaceOverlays/OverviewWidget.qml @@ -148,14 +148,14 @@ Item { radius: Theme.cornerRadius color: Theme.surfaceContainer - layer.enabled: true + layer.enabled: Theme.elevationEnabled layer.effect: MultiEffect { - shadowEnabled: true - shadowBlur: 0.5 + shadowEnabled: Theme.elevationEnabled + shadowBlur: Theme.elevationEnabled ? Math.max(0, Math.min(1, (Theme.elevationLevel2 && Theme.elevationLevel2.blurPx !== undefined ? Theme.elevationLevel2.blurPx : 8) / Theme.elevationBlurMax)) : 0 shadowHorizontalOffset: 0 - shadowVerticalOffset: 4 - shadowColor: Theme.shadowStrong - shadowOpacity: 1 + shadowVerticalOffset: Theme.elevationLevel2 && Theme.elevationLevel2.offsetY !== undefined ? Theme.elevationLevel2.offsetY : 4 + shadowColor: Theme.elevationShadowColor(Theme.elevationLevel2) + shadowOpacity: Theme.elevationLevel2 && Theme.elevationLevel2.alpha !== undefined ? Theme.elevationLevel2.alpha : 0.25 blurMax: 32 } diff --git a/quickshell/Widgets/AppIconRenderer.qml b/quickshell/Widgets/AppIconRenderer.qml index bb962ae9..289f92c4 100644 --- a/quickshell/Widgets/AppIconRenderer.qml +++ b/quickshell/Widgets/AppIconRenderer.qml @@ -98,7 +98,7 @@ Item { sourceComponent: IconImage { anchors.fill: parent source: root.iconPath - backer.sourceSize: Qt.size(root.iconSize, root.iconSize) + backer.sourceSize: Qt.size(root.iconSize * 2, root.iconSize * 2) mipmap: true asynchronous: true visible: status === Image.Ready diff --git a/quickshell/Widgets/DankCircularImage.qml b/quickshell/Widgets/DankCircularImage.qml index d7911eac..36cbfe4b 100644 --- a/quickshell/Widgets/DankCircularImage.qml +++ b/quickshell/Widgets/DankCircularImage.qml @@ -81,6 +81,8 @@ Rectangle { mipmap: true cache: true visible: false + sourceSize.width: Math.max(width * 2, 128) + sourceSize.height: Math.max(height * 2, 128) source: !root.shouldProbe ? root.imageSource : "" } diff --git a/quickshell/Widgets/DankDropdown.qml b/quickshell/Widgets/DankDropdown.qml index c1355d45..d9eff353 100644 --- a/quickshell/Widgets/DankDropdown.qml +++ b/quickshell/Widgets/DankDropdown.qml @@ -263,10 +263,10 @@ Item { layer.enabled: true layer.effect: MultiEffect { - shadowEnabled: true - shadowBlur: 0.4 - shadowColor: Theme.shadowStrong - shadowVerticalOffset: 4 + shadowEnabled: Theme.elevationEnabled + shadowBlur: Theme.elevationEnabled ? Math.max(0, Math.min(1, (Theme.elevationLevel2 && Theme.elevationLevel2.blurPx !== undefined ? Theme.elevationLevel2.blurPx : 8) / Theme.elevationBlurMax)) : 0 + shadowColor: Theme.elevationShadowColor(Theme.elevationLevel2) + shadowVerticalOffset: Theme.elevationLevel2 && Theme.elevationLevel2.offsetY !== undefined ? Theme.elevationLevel2.offsetY : 4 } Column { diff --git a/quickshell/Widgets/DankIconPicker.qml b/quickshell/Widgets/DankIconPicker.qml index 5d0386dc..d0d9e841 100644 --- a/quickshell/Widgets/DankIconPicker.qml +++ b/quickshell/Widgets/DankIconPicker.qml @@ -135,13 +135,14 @@ Rectangle { color: Theme.surface radius: Theme.cornerRadius - layer.enabled: true + layer.enabled: Theme.elevationEnabled layer.effect: MultiEffect { - shadowEnabled: true - shadowColor: Theme.shadowStrong - shadowBlur: 0.8 + shadowEnabled: Theme.elevationEnabled + shadowColor: Theme.elevationShadowColor(Theme.elevationLevel2) + shadowBlur: Theme.elevationEnabled ? Math.max(0, Math.min(1, (Theme.elevationLevel2 && Theme.elevationLevel2.blurPx !== undefined ? Theme.elevationLevel2.blurPx : 8) / Theme.elevationBlurMax)) : 0 shadowHorizontalOffset: 0 - shadowVerticalOffset: 4 + shadowVerticalOffset: Theme.elevationLevel2 && Theme.elevationLevel2.offsetY !== undefined ? Theme.elevationLevel2.offsetY : 4 + shadowOpacity: Theme.elevationLevel2 && Theme.elevationLevel2.alpha !== undefined ? Theme.elevationLevel2.alpha : 0.25 } Rectangle { diff --git a/quickshell/Widgets/DankOSD.qml b/quickshell/Widgets/DankOSD.qml index 2811540e..d803b8fd 100644 --- a/quickshell/Widgets/DankOSD.qml +++ b/quickshell/Widgets/DankOSD.qml @@ -257,9 +257,10 @@ PanelWindow { scale: shouldBeVisible ? 1 : 0.9 property bool childHovered: false - property real shadowBlurPx: 10 - property real shadowSpreadPx: 0 - property real shadowBaseAlpha: 0.60 + readonly property var elev: Theme.elevationLevel3 + property real shadowBlurPx: elev && elev.blurPx !== undefined ? elev.blurPx : 12 + property real shadowSpreadPx: elev && elev.spreadPx !== undefined ? elev.spreadPx : 0 + property real shadowBaseAlpha: elev && elev.alpha !== undefined ? elev.alpha : 0.3 readonly property real popupSurfaceAlpha: SettingsData.popupTransparency readonly property real effectiveShadowAlpha: shouldBeVisible ? Math.max(0, Math.min(1, shadowBaseAlpha * popupSurfaceAlpha)) : 0 @@ -277,12 +278,12 @@ PanelWindow { id: bgShadowLayer anchors.fill: parent visible: osdContainer.popupSurfaceAlpha >= 0.95 - layer.enabled: Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1" + layer.enabled: Theme.elevationEnabled && Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1" layer.smooth: false layer.textureSize: Qt.size(Math.round(width * root.dpr), Math.round(height * root.dpr)) layer.textureMirroring: ShaderEffectSource.MirrorVertically - readonly property int blurMax: 64 + readonly property int blurMax: Theme.elevationBlurMax layer.effect: MultiEffect { id: shadowFx diff --git a/quickshell/Widgets/DankPopout.qml b/quickshell/Widgets/DankPopout.qml index 1c9207b6..2576ed29 100644 --- a/quickshell/Widgets/DankPopout.qml +++ b/quickshell/Widgets/DankPopout.qml @@ -493,14 +493,15 @@ Item { x: contentWrapper.x y: contentWrapper.y - property real shadowBlurPx: 10 - property real shadowSpreadPx: 0 - property real shadowBaseAlpha: 0.60 + readonly property var elev: Theme.elevationLevel3 + property real shadowBlurPx: elev && elev.blurPx !== undefined ? elev.blurPx : 12 + property real shadowSpreadPx: elev && elev.spreadPx !== undefined ? elev.spreadPx : 0 + property real shadowBaseAlpha: elev && elev.alpha !== undefined ? elev.alpha : 0.3 readonly property real popupSurfaceAlpha: SettingsData.popupTransparency readonly property real effectiveShadowAlpha: Math.max(0, Math.min(1, shadowBaseAlpha * popupSurfaceAlpha)) - readonly property int blurMax: 64 + readonly property int blurMax: Theme.elevationBlurMax - layer.enabled: Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1" && !(root.suspendShadowWhileResizing && root._resizeActive) + layer.enabled: Theme.elevationEnabled && Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1" && !(root.suspendShadowWhileResizing && root._resizeActive) layer.smooth: false layer.effect: MultiEffect { @@ -511,10 +512,8 @@ Item { maskEnabled: false shadowBlur: Math.max(0, Math.min(1, shadowSource.shadowBlurPx / shadowSource.blurMax)) shadowScale: 1 + (2 * shadowSource.shadowSpreadPx) / Math.max(1, Math.min(shadowSource.width, shadowSource.height)) - shadowColor: { - const baseColor = Theme.isLightMode ? Qt.rgba(0, 0, 0, 1) : Theme.surfaceContainerHighest; - return Theme.withAlpha(baseColor, shadowSource.effectiveShadowAlpha); - } + shadowVerticalOffset: parent.elev && parent.elev.offsetY !== undefined ? parent.elev.offsetY : 6 + shadowColor: Theme.elevationShadowColor(Theme.elevationLevel3) } } diff --git a/quickshell/matugen/templates/firefox-userchrome.css b/quickshell/matugen/templates/firefox-userchrome.css index 2ee23124..07436cc2 100644 --- a/quickshell/matugen/templates/firefox-userchrome.css +++ b/quickshell/matugen/templates/firefox-userchrome.css @@ -53,8 +53,11 @@ --m3-radius: 12px; --m3-radius-sm: 10px; --m3-elev-0: none; - --m3-elev-1: 0 1px 2px rgba(0,0,0,.08), 0 1px 3px rgba(0,0,0,.06); - --m3-elev-2: 0 2px 6px rgba(0,0,0,.10), 0 1px 3px rgba(0,0,0,.06); + --m3-elev-1: 0 1px 2px color-mix(in srgb, var(--md-sys-color-shadow) 8%, transparent), 0 1px 3px color-mix(in srgb, var(--md-sys-color-shadow) 6%, transparent); + --m3-elev-2: 0 2px 6px color-mix(in srgb, var(--md-sys-color-shadow) 10%, transparent), 0 1px 3px color-mix(in srgb, var(--md-sys-color-shadow) 6%, transparent); + --m3-elev-3: 0 11px 7px color-mix(in srgb, var(--md-sys-color-shadow) 19%, transparent), 0 13px 25px color-mix(in srgb, var(--md-sys-color-shadow) 30%, transparent); + --m3-elev-4: 0 14px 12px color-mix(in srgb, var(--md-sys-color-shadow) 17%, transparent), 0 20px 40px color-mix(in srgb, var(--md-sys-color-shadow) 30%, transparent); + --m3-elev-5: 0 17px 17px color-mix(in srgb, var(--md-sys-color-shadow) 15%, transparent), 0 27px 55px color-mix(in srgb, var(--md-sys-color-shadow) 30%, transparent); --tab-height: 34px; --urlbar-height: 38px; @@ -118,8 +121,11 @@ --md-sys-color-surface-container-high: {{colors.surface_container_high.dark.hex}}; --md-sys-color-surface-container-highest: {{colors.surface_container_highest.dark.hex}}; - --m3-elev-1: 0 1px 2px rgba(0,0,0,.50), 0 1px 3px rgba(0,0,0,.35); - --m3-elev-2: 0 4px 10px rgba(0,0,0,.55), 0 1px 3px rgba(0,0,0,.35); + --m3-elev-1: 0 1px 2px color-mix(in srgb, var(--md-sys-color-shadow) 50%, transparent), 0 1px 3px color-mix(in srgb, var(--md-sys-color-shadow) 35%, transparent); + --m3-elev-2: 0 4px 10px color-mix(in srgb, var(--md-sys-color-shadow) 55%, transparent), 0 1px 3px color-mix(in srgb, var(--md-sys-color-shadow) 35%, transparent); + --m3-elev-3: 0 11px 7px color-mix(in srgb, var(--md-sys-color-shadow) 45%, transparent), 0 13px 25px color-mix(in srgb, var(--md-sys-color-shadow) 55%, transparent); + --m3-elev-4: 0 14px 12px color-mix(in srgb, var(--md-sys-color-shadow) 42%, transparent), 0 20px 40px color-mix(in srgb, var(--md-sys-color-shadow) 55%, transparent); + --m3-elev-5: 0 17px 17px color-mix(in srgb, var(--md-sys-color-shadow) 40%, transparent), 0 27px 55px color-mix(in srgb, var(--md-sys-color-shadow) 55%, transparent); --state-hover: color-mix(in srgb, var(--md-sys-color-on-surface) 6%, transparent); --state-press: color-mix(in srgb, var(--md-sys-color-on-surface) 10%, transparent); diff --git a/quickshell/translations/settings_search_index.json b/quickshell/translations/settings_search_index.json index f3820ff4..3e1d2122 100644 --- a/quickshell/translations/settings_search_index.json +++ b/quickshell/translations/settings_search_index.json @@ -2400,6 +2400,23 @@ ], "description": "0 = square corners" }, + { + "section": "m3ElevationEnabled", + "label": "M3 Elevation & Shadows", + "tabIndex": 10, + "category": "Theme & Colors", + "keywords": [ + "appearance", + "elevation", + "lift", + "material", + "m3", + "shadow", + "shadows", + "theme" + ], + "description": "Material Design 3 shadows and elevation on modals, popouts, and dialogs" + }, { "section": "cursorSize", "label": "Cursor Size",