From 1bb1aba44ddd5bed8a9c2e6e9982f64385d4f918 Mon Sep 17 00:00:00 2001 From: Echo Date: Mon, 24 Aug 2026 15:04:35 +0000 Subject: [PATCH] Redesign: Add new tonal variant button (#40262) --- .../components/button/redesign.module.scss | 81 +++++++++++++------ .../components/button/redesign.stories.tsx | 4 +- .../mastodon/components/button/redesign.tsx | 8 +- .../mastodon/components/card/index.tsx | 1 + .../mastodon/components/menu/card.tsx | 13 ++- .../mastodon/components/menu/index.tsx | 14 +--- .../modal_shell/redesign.stories.tsx | 2 +- .../features/compose/redesign/footer.tsx | 2 +- .../compose/redesign/modal_cancel.tsx | 4 +- .../compose/redesign/modal_rearrange.tsx | 2 +- .../compose/redesign/modal_switch.tsx | 2 +- .../features/compose/redesign/poll.tsx | 2 +- .../features/compose/redesign/trigger.tsx | 2 +- .../features/compose/redesign/upload.tsx | 2 +- 14 files changed, 82 insertions(+), 57 deletions(-) diff --git a/app/javascript/mastodon/components/button/redesign.module.scss b/app/javascript/mastodon/components/button/redesign.module.scss index b0b887732c5..857fca0f6a1 100644 --- a/app/javascript/mastodon/components/button/redesign.module.scss +++ b/app/javascript/mastodon/components/button/redesign.module.scss @@ -54,6 +54,14 @@ a.base { } } +@mixin active { + &:active, + &[aria-pressed='true'], + &[aria-expanded='true'] { + @content; + } +} + // Sizes .xs { @@ -85,33 +93,64 @@ a.base { &.accent { --bg: var(--color-bg-brand-base); - --bg-hover: var(--color-bg-brand-base-hover); - } - - .neutral { - // This technically is a duplicate, but it's added so TS realizes this class is an option. - --bg: var(--color-bg-inverted); - --fg: var(--color-text-inverted); - } - - &.tonal:not(:active, [aria-pressed='true'], [aria-expanded='true']) { - --bg: var(--color-bg-highlight); - --fg: var(--color-text-primary); + --fg: var(--color-text-on-brand-base); } &.destructive { --bg: var(--color-bg-error-base); - --bg-hover: var(--color-bg-error-base-hover); --fg: var(--color-text-on-error-base); } } +// Tonal + +.tonal { + --bg-color: var(--color-text-primary); + --bg-opacity: 4%; + --bg: rgb(from var(--bg-color) r g b / var(--bg-opacity)); + --bg-hover-opacity: 10%; + --bg-hover: rgb(from var(--bg-color) r g b / var(--bg-hover-opacity)); + + &.neutral { + @include active { + --bg: var(--color-bg-inverted); + --fg: var(--color-text-inverted); + } + } + + &.accent, + &.destructive { + --bg-opacity: 10%; + --bg-hover-opacity: 20%; + + @include active { + --bg-opacity: 100%; + } + } + + &.accent { + --bg-color: var(--color-bg-brand-base); + --fg: var(--color-text-brand); + + @include active { + --fg: var(--color-text-on-brand-base); + } + } + + &.destructive { + --bg-color: var(--color-bg-error-base); + --fg: var(--color-text-error); + + @include active { + --fg: var(--color-text-on-error-base); + } + } +} + // Ghost .ghost { - &:active, - &[aria-pressed='true'], - &[aria-expanded='true'] { + @include active { --bg: var(--color-bg-inverted); --fg: var(--color-text-inverted); } @@ -119,9 +158,7 @@ a.base { &.accent { --fg: var(--color-text-brand); - &:active, - &[aria-pressed='true'], - &[aria-expanded='true'] { + @include active { --bg: var(--color-bg-brand-base); --fg: var(--color-text-on-brand-base); } @@ -130,11 +167,9 @@ a.base { &.destructive { --fg: var(--color-text-error); - &:active, - &[aria-pressed='true'], - &[aria-expanded='true'] { - --fg: var(--color-text-on-error-base); + @include active { --bg: var(--color-bg-error-base); + --fg: var(--color-text-on-error-base); } } } diff --git a/app/javascript/mastodon/components/button/redesign.stories.tsx b/app/javascript/mastodon/components/button/redesign.stories.tsx index 4c53745e7ab..1175b759d41 100644 --- a/app/javascript/mastodon/components/button/redesign.stories.tsx +++ b/app/javascript/mastodon/components/button/redesign.stories.tsx @@ -44,11 +44,11 @@ const meta = { }, variant: { control: 'inline-radio', - options: ['solid', 'ghost'], + options: ['solid', 'tonal', 'ghost'], }, color: { control: 'inline-radio', - options: ['neutral', 'accent', 'tonal', 'destructive'], + options: ['neutral', 'accent', 'destructive'], }, leadingIcon: iconArgType, trailingIcon: iconArgType, diff --git a/app/javascript/mastodon/components/button/redesign.tsx b/app/javascript/mastodon/components/button/redesign.tsx index 887fb24f2f8..d9fc4caec69 100644 --- a/app/javascript/mastodon/components/button/redesign.tsx +++ b/app/javascript/mastodon/components/button/redesign.tsx @@ -16,8 +16,8 @@ export const buttonClasses = classes; interface ButtonPropsBase { size?: 'lg' | 'md' | 'sm' | 'xs'; - variant?: 'solid' | 'ghost'; - color?: 'accent' | 'neutral' | 'tonal' | 'destructive'; + variant?: 'solid' | 'tonal' | 'ghost'; + color?: 'accent' | 'neutral' | 'destructive'; onClick?: React.MouseEventHandler< As extends 'button' ? HTMLButtonElement : HTMLAnchorElement >; @@ -36,8 +36,8 @@ type ButtonProps = ButtonButtonProps | ButtonAnchorProps | ButtonLinkProps; const BaseButton: React.FC = ({ size = 'md', - variant = 'solid', - color = 'tonal', + variant = 'tonal', + color = 'neutral', as: asComp = 'button', children, className, diff --git a/app/javascript/mastodon/components/card/index.tsx b/app/javascript/mastodon/components/card/index.tsx index ea93334cfc6..0219c3860c9 100644 --- a/app/javascript/mastodon/components/card/index.tsx +++ b/app/javascript/mastodon/components/card/index.tsx @@ -54,6 +54,7 @@ export const Card = ({ {!hideButton && ( = Merge< +export type MenuCardProps = PolymorphicProps< { - as?: As; children: React.ReactNode; className?: string; elevation?: 1 | 2; maxWidth?: number | string; style?: React.CSSProperties; }, - React.ComponentProps + As >; export const MenuCard = ({ @@ -41,7 +38,7 @@ export const MenuCard = ({ '--_max-card-width': typeof maxWidth === 'number' ? `${maxWidth}px` : maxWidth, ...style, - } as CSSProperties + } as React.CSSProperties } > {children} @@ -85,7 +82,7 @@ export const PopoverMenuCard = ({ {({ props: popoverChildProps }) => ( )} className={classNames( className, props.maxWidth && classes.popoverCard, diff --git a/app/javascript/mastodon/components/menu/index.tsx b/app/javascript/mastodon/components/menu/index.tsx index 37cc77f91cc..6295254067d 100644 --- a/app/javascript/mastodon/components/menu/index.tsx +++ b/app/javascript/mastodon/components/menu/index.tsx @@ -1,4 +1,3 @@ -import type React from 'react'; import { createContext, use, @@ -8,7 +7,7 @@ import { useState, } from 'react'; -import type { Merge } from 'type-fest'; +import type { PolymorphicProps } from '@/types/polymorphic'; import { Button } from '../button/redesign'; @@ -243,18 +242,11 @@ export const Menu: React.FC = ({ type = 'actions', children }) => { return {children}; }; -export type MenuTriggerProps = Merge< - React.ComponentProps, - { - as?: As; - } ->; - export const MenuTrigger = ({ as: asComp, children, ...props -}: MenuTriggerProps) => { +}: PolymorphicProps) => { const Component = asComp ?? Button; const { menuTriggerProps } = useMenuContext(); return ( @@ -282,7 +274,7 @@ export const MenuList = ({ reference={popover.reference} popoverElement={popover.popover} container={null} - {...props} + {...(props as React.ComponentPropsWithoutRef)} {...menuListProps} > {type === 'navigation' ?
    {children}
: children} diff --git a/app/javascript/mastodon/components/modal_shell/redesign.stories.tsx b/app/javascript/mastodon/components/modal_shell/redesign.stories.tsx index ee17187fb1c..ee278c393c3 100644 --- a/app/javascript/mastodon/components/modal_shell/redesign.stories.tsx +++ b/app/javascript/mastodon/components/modal_shell/redesign.stories.tsx @@ -38,7 +38,7 @@ const meta = { - + ); diff --git a/app/javascript/mastodon/features/compose/redesign/footer.tsx b/app/javascript/mastodon/features/compose/redesign/footer.tsx index 5037ae3ee50..035bec8ce56 100644 --- a/app/javascript/mastodon/features/compose/redesign/footer.tsx +++ b/app/javascript/mastodon/features/compose/redesign/footer.tsx @@ -83,7 +83,7 @@ export const ComposeFooter: React.FC<{ onEmojiPick: OnEmojiPick }> = ({ - - -