Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion apps/app-frontend/src/components/ui/WindowControls.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@
<IconButton
type="quiet"
color="red"
interaction="hover"
label="Close window"
class="relative expanded-button close-button hover:!bg-red focus-visible:!bg-red"
class="relative expanded-button close-button"
@click="handleClose"
>
<XIcon />
Expand Down
11 changes: 9 additions & 2 deletions packages/ui/src/components/base/buttons/ButtonFrame.vue
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,14 @@ const typeClasses: Record<ButtonType, string> = {
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<ButtonInteraction, string> = {
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',
}
Expand Down Expand Up @@ -162,4 +165,8 @@ defineExpose({ element })
.button-frame--quiet {
color: var(--button-color, var(--color-base));
}
.button-frame--quiet-hover {
color: var(--color-base);
}
</style>
2 changes: 1 addition & 1 deletion packages/ui/src/components/base/buttons/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down
17 changes: 17 additions & 0 deletions packages/ui/src/stories/buttons/Button.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,23 @@ export const Quiet: Story = {
}),
}

export const QuietInteraction: Story = {
render: () => ({
components: { Button, DownloadIcon, IconButton },
template: /*html*/ `
<div class="flex flex-wrap items-center gap-4">
<Button type="quiet" color="red" interaction="surface">Surface (default)</Button>
<Button type="quiet" color="red" interaction="filled">Filled</Button>
<Button type="quiet" color="red" interaction="hover">Hover only</Button>
<Button type="quiet" color="red" interaction="none">None</Button>
<IconButton type="quiet" color="red" interaction="hover" label="Close window">
<DownloadIcon />
</IconButton>
</div>
`,
}),
}

export const Sizes: Story = {
render: () => ({
components: { Button, DownloadIcon, IconButton },
Expand Down
6 changes: 5 additions & 1 deletion standards/frontend/BUTTONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -120,6 +121,9 @@ treatment needs to differ from the default surface fill:
</IconButton>
```

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.
Expand Down