mirror of
https://github.com/mastodon/mastodon.git
synced 2026-09-13 02:36:36 -05:00
Redesign: Add new tonal variant button (#40262)
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -16,8 +16,8 @@ export const buttonClasses = classes;
|
||||
|
||||
interface ButtonPropsBase<As extends 'a' | 'button'> {
|
||||
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<ButtonProps> = ({
|
||||
size = 'md',
|
||||
variant = 'solid',
|
||||
color = 'tonal',
|
||||
variant = 'tonal',
|
||||
color = 'neutral',
|
||||
as: asComp = 'button',
|
||||
children,
|
||||
className,
|
||||
|
||||
@@ -54,6 +54,7 @@ export const Card = <As extends React.ElementType>({
|
||||
|
||||
{!hideButton && (
|
||||
<IconButton
|
||||
variant='solid'
|
||||
icon={TrashIcon}
|
||||
onClick={onDelete}
|
||||
color='destructive'
|
||||
|
||||
@@ -1,24 +1,21 @@
|
||||
import type { CSSProperties } from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import type { Merge } from 'type-fest';
|
||||
import type { PolymorphicProps } from '@/types/polymorphic';
|
||||
|
||||
import { Popover } from '../popover';
|
||||
import type { PopoverProps } from '../popover';
|
||||
|
||||
import classes from './styles.module.scss';
|
||||
|
||||
export type MenuCardProps<As extends React.ElementType> = Merge<
|
||||
export type MenuCardProps<As extends React.ElementType> = PolymorphicProps<
|
||||
{
|
||||
as?: As;
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
elevation?: 1 | 2;
|
||||
maxWidth?: number | string;
|
||||
style?: React.CSSProperties;
|
||||
},
|
||||
React.ComponentProps<As>
|
||||
As
|
||||
>;
|
||||
|
||||
export const MenuCard = <As extends React.ElementType>({
|
||||
@@ -41,7 +38,7 @@ export const MenuCard = <As extends React.ElementType>({
|
||||
'--_max-card-width':
|
||||
typeof maxWidth === 'number' ? `${maxWidth}px` : maxWidth,
|
||||
...style,
|
||||
} as CSSProperties
|
||||
} as React.CSSProperties
|
||||
}
|
||||
>
|
||||
{children}
|
||||
@@ -85,7 +82,7 @@ export const PopoverMenuCard = <As extends React.ElementType>({
|
||||
{({ props: popoverChildProps }) => (
|
||||
<MenuCard
|
||||
{...popoverChildProps}
|
||||
{...props}
|
||||
{...(props as React.ComponentPropsWithoutRef<As>)}
|
||||
className={classNames(
|
||||
className,
|
||||
props.maxWidth && classes.popoverCard,
|
||||
|
||||
@@ -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<MenuProps> = ({ type = 'actions', children }) => {
|
||||
return <MenuContext value={contextValue}>{children}</MenuContext>;
|
||||
};
|
||||
|
||||
export type MenuTriggerProps<As extends React.ElementType> = Merge<
|
||||
React.ComponentProps<As>,
|
||||
{
|
||||
as?: As;
|
||||
}
|
||||
>;
|
||||
|
||||
export const MenuTrigger = <As extends React.ElementType>({
|
||||
as: asComp,
|
||||
children,
|
||||
...props
|
||||
}: MenuTriggerProps<As>) => {
|
||||
}: PolymorphicProps<object, As>) => {
|
||||
const Component = asComp ?? Button;
|
||||
const { menuTriggerProps } = useMenuContext();
|
||||
return (
|
||||
@@ -282,7 +274,7 @@ export const MenuList = <As extends React.ElementType>({
|
||||
reference={popover.reference}
|
||||
popoverElement={popover.popover}
|
||||
container={null}
|
||||
{...props}
|
||||
{...(props as React.ComponentPropsWithoutRef<As>)}
|
||||
{...menuListProps}
|
||||
>
|
||||
{type === 'navigation' ? <ul>{children}</ul> : children}
|
||||
|
||||
@@ -38,7 +38,7 @@ const meta = {
|
||||
<ModalActions align={actionAlign}>
|
||||
<Button>Cancel</Button>
|
||||
|
||||
<Button color='neutral'>Save</Button>
|
||||
<Button variant='solid'>Save</Button>
|
||||
</ModalActions>
|
||||
</ModalShell>
|
||||
);
|
||||
|
||||
@@ -83,7 +83,7 @@ export const ComposeFooter: React.FC<{ onEmojiPick: OnEmojiPick }> = ({
|
||||
</span>
|
||||
|
||||
<Button
|
||||
color='neutral'
|
||||
variant='solid'
|
||||
type='submit'
|
||||
disabled={!canSubmit}
|
||||
loading={isSubmitting}
|
||||
|
||||
@@ -52,13 +52,13 @@ const ComposerModalCancelConfirm: React.FC<{ openNew?: boolean }> = ({
|
||||
/>
|
||||
|
||||
<ModalActions>
|
||||
<Button color='destructive' onClick={handleDelete}>
|
||||
<Button variant='solid' color='destructive' onClick={handleDelete}>
|
||||
<FormattedMessage
|
||||
id='compose.cancel_modal.delete'
|
||||
defaultMessage='Delete draft'
|
||||
/>
|
||||
</Button>
|
||||
<Button color='neutral' onClick={handleContinue}>
|
||||
<Button variant='solid' onClick={handleContinue}>
|
||||
<FormattedMessage
|
||||
id='compose.cancel_modal.continue'
|
||||
defaultMessage='Continue draft'
|
||||
|
||||
@@ -127,7 +127,7 @@ const ComposerModalRearrange: React.FC<{ onClose: () => void }> = ({
|
||||
/>
|
||||
</Button>
|
||||
|
||||
<Button color='neutral' onClick={handleSave}>
|
||||
<Button variant='solid' onClick={handleSave}>
|
||||
<FormattedMessage
|
||||
id='compose.rearrange_modal.save'
|
||||
defaultMessage='Save'
|
||||
|
||||
@@ -55,7 +55,7 @@ const ComposerModalSwitch: React.FC = () => {
|
||||
/>
|
||||
</Button>
|
||||
|
||||
<Button size='sm' color='neutral' onClick={handleContinue}>
|
||||
<Button size='sm' variant='solid' onClick={handleContinue}>
|
||||
<FormattedMessage
|
||||
id='compose.switch_modal.continue'
|
||||
defaultMessage='Continue'
|
||||
|
||||
@@ -181,8 +181,8 @@ export const ComposePoll: React.FC = () => {
|
||||
className={classNames(
|
||||
classes.pollDurationSelect,
|
||||
buttonClasses.base,
|
||||
buttonClasses.solid,
|
||||
buttonClasses.tonal,
|
||||
buttonClasses.neutral,
|
||||
buttonClasses.xs,
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -74,7 +74,7 @@ export const ComposeRedesignButton: React.FC = () => {
|
||||
<MenuTrigger
|
||||
as={IconButton}
|
||||
icon={PenNibIcon}
|
||||
color='neutral'
|
||||
variant='solid'
|
||||
className={classes.button}
|
||||
size='lg'
|
||||
>
|
||||
|
||||
@@ -76,7 +76,7 @@ export const ComposeUpload: React.FC<{
|
||||
as={IconButton}
|
||||
icon={DotsThreeIcon}
|
||||
size='sm'
|
||||
color='neutral'
|
||||
variant='solid'
|
||||
className={classes.mediaMenuButton}
|
||||
>
|
||||
<FormattedMessage
|
||||
|
||||
Reference in New Issue
Block a user