From 0f454a39c5f53d5e5d8813c4b870b86e21d045c4 Mon Sep 17 00:00:00 2001 From: Creeperkatze Date: Sat, 8 Aug 2026 12:35:07 +0200 Subject: [PATCH 1/4] Fix close button --- apps/app-frontend/src/components/ui/WindowControls.vue | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/app-frontend/src/components/ui/WindowControls.vue b/apps/app-frontend/src/components/ui/WindowControls.vue index 38f00cd1fb..7431465ad6 100644 --- a/apps/app-frontend/src/components/ui/WindowControls.vue +++ b/apps/app-frontend/src/components/ui/WindowControls.vue @@ -24,8 +24,9 @@ From 70803423b9b7b5bcc80dc01da60f55d131da91fc Mon Sep 17 00:00:00 2001 From: Creeperkatze Date: Sat, 8 Aug 2026 22:49:20 +0200 Subject: [PATCH 2/4] Add hover-only interaction --- .../src/components/ui/WindowControls.vue | 2 +- .../src/components/base/buttons/ButtonFrame.vue | 12 ++++++++++-- .../ui/src/components/base/buttons/types.ts | 2 +- .../ui/src/stories/buttons/Button.stories.ts | 17 +++++++++++++++++ standards/frontend/BUTTONS.md | 9 ++++++++- 5 files changed, 37 insertions(+), 5 deletions(-) diff --git a/apps/app-frontend/src/components/ui/WindowControls.vue b/apps/app-frontend/src/components/ui/WindowControls.vue index 7431465ad6..67aceabc66 100644 --- a/apps/app-frontend/src/components/ui/WindowControls.vue +++ b/apps/app-frontend/src/components/ui/WindowControls.vue @@ -24,7 +24,7 @@ = { quiet: 'button-frame--quiet bg-transparent [&>svg]:text-inherit', } +const filledOnInteractionClasses = + '[&:not(:disabled):not([aria-disabled=true]):hover]:!bg-[--button-color] [&:not(:disabled):not([aria-disabled=true]):focus-visible]:!bg-[--button-color] [&:not(:disabled):not([aria-disabled=true]):hover]:!text-[var(--color-accent-contrast)] [&:not(:disabled):not([aria-disabled=true]):focus-visible]:!text-[var(--color-accent-contrast)]' + const interactionClasses: Record = { surface: '[&:not(:disabled):not([aria-disabled=true]):hover]:bg-surface-4 [&:not(:disabled):not([aria-disabled=true]):focus-visible]:bg-surface-4', - filled: - '[&:not(:disabled):not([aria-disabled=true]):hover]:!bg-[--button-color] [&:not(:disabled):not([aria-disabled=true]):focus-visible]:!bg-[--button-color] [&:not(:disabled):not([aria-disabled=true]):hover]:!text-[var(--color-accent-contrast)] [&:not(:disabled):not([aria-disabled=true]):focus-visible]:!text-[var(--color-accent-contrast)]', + filled: filledOnInteractionClasses, + hover: `button-frame--quiet-hover ${filledOnInteractionClasses}`, none: '[&:not(:disabled):not([aria-disabled=true]):hover]:!brightness-100 [&:not(:disabled):not([aria-disabled=true]):focus-visible]:!brightness-100', } @@ -162,4 +165,9 @@ defineExpose({ element }) .button-frame--quiet { color: var(--button-color, var(--color-base)); } + +/* interaction="hover": ignore --button-color at rest, only tint on hover/focus. */ +.button-frame--quiet-hover { + color: var(--color-base); +} diff --git a/packages/ui/src/components/base/buttons/types.ts b/packages/ui/src/components/base/buttons/types.ts index f50eb35dcd..5afea398c0 100644 --- a/packages/ui/src/components/base/buttons/types.ts +++ b/packages/ui/src/components/base/buttons/types.ts @@ -7,7 +7,7 @@ export type ButtonType = 'base' | 'colored' | 'outlined' | 'quiet' export type ButtonSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl' -export type ButtonInteraction = 'surface' | 'filled' | 'none' +export type ButtonInteraction = 'surface' | 'filled' | 'hover' | 'none' // TODO: Standardized color string enum props across @modrinth/ui export type ButtonColor = diff --git a/packages/ui/src/stories/buttons/Button.stories.ts b/packages/ui/src/stories/buttons/Button.stories.ts index 85bfe95332..dc5c935357 100644 --- a/packages/ui/src/stories/buttons/Button.stories.ts +++ b/packages/ui/src/stories/buttons/Button.stories.ts @@ -128,6 +128,23 @@ export const Quiet: Story = { }), } +export const QuietInteraction: Story = { + render: () => ({ + components: { Button, DownloadIcon, IconButton }, + template: /*html*/ ` +
+ + + + + + + +
+ `, + }), +} + export const Sizes: Story = { render: () => ({ components: { Button, DownloadIcon, IconButton }, diff --git a/standards/frontend/BUTTONS.md b/standards/frontend/BUTTONS.md index b1f71db07c..43f52e381e 100644 --- a/standards/frontend/BUTTONS.md +++ b/standards/frontend/BUTTONS.md @@ -110,7 +110,8 @@ treatment needs to differ from the default surface fill: | Interaction | Treatment | | ----------- | --------- | | `surface` | Uses the standard neutral hover/focus surface. This is the default. | -| `filled` | Fills with the button's `color` and uses contrast text. | +| `filled` | Tints text/icon with the button's `color` at rest, and fills with it on hover/focus using contrast text. | +| `hover` | Like `filled`, but untinted at rest — `color` is only applied as the hover/focus fill. | | `none` | Keeps the background transparent while retaining the focus ring. | ```vue @@ -118,8 +119,14 @@ treatment needs to differ from the default surface fill: + + ``` +Use `hover` for icons that should only signal danger or emphasis on interaction, such as a +window close button, without tinting the icon at rest. + Use `interaction` to describe behavior rather than passing arbitrary hover colors. Resting selected-state backgrounds, such as a current pagination page, remain the responsibility of the owning component. From 99245394e552dae6fe8195614fb9d58d4310be29 Mon Sep 17 00:00:00 2001 From: Creeperkatze Date: Sat, 8 Aug 2026 22:52:10 +0200 Subject: [PATCH 3/4] Cleanup --- packages/ui/src/components/base/buttons/ButtonFrame.vue | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/ui/src/components/base/buttons/ButtonFrame.vue b/packages/ui/src/components/base/buttons/ButtonFrame.vue index 9f997335b2..08d7b11de5 100644 --- a/packages/ui/src/components/base/buttons/ButtonFrame.vue +++ b/packages/ui/src/components/base/buttons/ButtonFrame.vue @@ -166,7 +166,6 @@ defineExpose({ element }) color: var(--button-color, var(--color-base)); } -/* interaction="hover": ignore --button-color at rest, only tint on hover/focus. */ .button-frame--quiet-hover { color: var(--color-base); } From aba157e57421a193169292f483ca0948f98f3dd5 Mon Sep 17 00:00:00 2001 From: Creeperkatze Date: Sat, 8 Aug 2026 22:55:42 +0200 Subject: [PATCH 4/4] Cleanup standards --- standards/frontend/BUTTONS.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/standards/frontend/BUTTONS.md b/standards/frontend/BUTTONS.md index 43f52e381e..2e9747bcf6 100644 --- a/standards/frontend/BUTTONS.md +++ b/standards/frontend/BUTTONS.md @@ -111,7 +111,7 @@ treatment needs to differ from the default surface fill: | ----------- | --------- | | `surface` | Uses the standard neutral hover/focus surface. This is the default. | | `filled` | Tints text/icon with the button's `color` at rest, and fills with it on hover/focus using contrast text. | -| `hover` | Like `filled`, but untinted at rest — `color` is only applied as the hover/focus fill. | +| `hover` | Like `filled`, but untinted at rest. `color` is only applied as the hover/focus fill. | | `none` | Keeps the background transparent while retaining the focus ring. | ```vue @@ -119,9 +119,6 @@ treatment needs to differ from the default surface fill: - - ``` Use `hover` for icons that should only signal danger or emphasis on interaction, such as a