Skip to content

Commit

Permalink
feat(ui): updates for button, density and colors (#1939)
Browse files Browse the repository at this point in the history
* feat: change density's `medium` property with `slim`

* feat: update Button component to match with the newest styles

* feat: sync theme gradients with the new styles

* chore: changeset

* fix: lint
  • Loading branch information
VanishMax authored Dec 10, 2024
1 parent 565d309 commit be8b50c
Show file tree
Hide file tree
Showing 14 changed files with 122 additions and 107 deletions.
5 changes: 5 additions & 0 deletions .changeset/two-hotels-lie.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@penumbra-zone/ui': minor
---

Update Button component to match the latest designs
10 changes: 5 additions & 5 deletions packages/ui/.storybook/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ const DensityWrapper = ({ children, showDensityControl }) => {
<Tabs
options={[
{ label: 'Sparse', value: 'sparse' },
{ label: 'Medium', value: 'medium' },
{ label: 'Compact', value: 'compact' },
{ label: 'Slim', value: 'slim' },
]}
value={density}
onChange={setDensity}
Expand All @@ -36,14 +36,14 @@ const DensityWrapper = ({ children, showDensityControl }) => {
</div>
);

if (density === 'medium') {
return <Density medium>{densityTabs}</Density>;
}

if (density === 'compact') {
return <Density compact>{densityTabs}</Density>;
}

if (density === 'slim') {
return <Density slim>{densityTabs}</Density>;
}

return <Density sparse>{densityTabs}</Density>;
};

Expand Down
46 changes: 19 additions & 27 deletions packages/ui/src/Button/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@ import { FC, forwardRef, MouseEventHandler, ReactNode } from 'react';
import { LucideIcon } from 'lucide-react';
import cn from 'clsx';
import { getOutlineColorByActionType, ActionType } from '../utils/action-type';
import { Priority, buttonBase, getBackground, getFocusOutline, getOverlays } from '../utils/button';
import { button } from '../utils/typography';
import {
Priority,
buttonBase,
getBackground,
getOverlays,
getFont,
getSize,
getIconSize,
} from '../utils/button';
import { useDensity } from '../utils/density';

const iconOnlyAdornment = cn('rounded-full p-1 w-max');
const sparse = (iconOnly?: boolean | 'adornment') =>
cn('rounded-sm h-12', iconOnly ? 'w-12 min-w-12 pl-0 pr-0' : 'w-full pl-4 pr-4');

const compact = (iconOnly?: boolean | 'adornment') =>
cn('rounded-full h-8 min-w-8 w-max', iconOnly ? 'pl-2 pr-2' : 'pl-4 pr-4');

interface BaseButtonProps {
type?: HTMLButtonElement['type'];
/**
Expand Down Expand Up @@ -109,6 +109,7 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
ref,
) => {
const density = useDensity();
const styleAttrs = { actionType, iconOnly, density, priority };

return (
<button
Expand All @@ -121,31 +122,22 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
{...attrs}
className={cn(
buttonBase,
button,
getBackground(actionType, priority, iconOnly),
getFocusOutline({ density, iconOnly, actionType }),
getOverlays({ actionType, iconOnly, density }),
getFont(styleAttrs),
getSize(styleAttrs),
getBackground(styleAttrs),
getOverlays(styleAttrs),

'-outline-offset-1',
priority === 'secondary' && 'outline-1',
'-outline-offset-1 focus:outline-none',
priority === 'secondary' && 'outline outline-1',
priority === 'secondary' && getOutlineColorByActionType(actionType),
'after:-outline-offset-2',

'relative',
'flex gap-2 items-center justify-center',
'text-neutral-contrast',

// eslint-disable-next-line no-nested-ternary -- allow
iconOnly === 'adornment'
? iconOnlyAdornment
: density === 'sparse'
? sparse(iconOnly)
: compact(iconOnly),
'flex items-center justify-center',
density === 'sparse' ? 'gap-2' : 'gap-1',
)}
>
{IconComponent && (
<IconComponent size={density === 'sparse' && iconOnly === true ? 24 : 16} />
)}
{IconComponent && <IconComponent size={getIconSize(density)} />}

{!iconOnly && children}
</button>
Expand Down
10 changes: 5 additions & 5 deletions packages/ui/src/Density/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { ReactNode } from 'react';
import { Density as TDensity, DensityContext } from '../utils/density';

export type DensityPropType =
| { sparse: true; medium?: never; compact?: never }
| { medium: true; sparse?: never; compact?: never }
| { compact: true; sparse?: never; medium?: never };
| { sparse: true; slim?: never; compact?: never }
| { slim: true; sparse?: never; compact?: never }
| { compact: true; sparse?: never; slim?: never };

export type DensityProps = DensityPropType & {
children?: ReactNode;
Expand Down Expand Up @@ -71,9 +71,9 @@ export type DensityProps = DensityPropType & {
* />
* ```
*/
export const Density = ({ children, sparse, medium, compact }: DensityProps) => {
export const Density = ({ children, sparse, slim, compact }: DensityProps) => {
const density: TDensity =
(sparse && 'sparse') ?? (medium && 'medium') ?? (compact && 'compact') ?? 'sparse';
(sparse && 'sparse') ?? (compact && 'compact') ?? (slim && 'slim') ?? 'sparse';

return <DensityContext.Provider value={density}>{children}</DensityContext.Provider>;
};
14 changes: 7 additions & 7 deletions packages/ui/src/Tabs/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ type LimitedActionType = Exclude<ActionType, 'destructive'>;

const getIndicatorColor = (actionType: LimitedActionType): string => {
if (actionType === 'accent') {
return cn('bg-tabAccent');
return cn('bg-accentRadialGradient');
}
if (actionType === 'unshield') {
return cn('bg-tabUnshield');
return cn('bg-unshieldRadialGradient');
}
return cn('bg-tabNeutral');
return cn('bg-neutralRadialGradient');
};

const getBorderColor = (actionType: LimitedActionType): string => {
Expand All @@ -27,20 +27,20 @@ const getBorderColor = (actionType: LimitedActionType): string => {
};

const getDensityClasses = (density: Density): string => {
if (density === 'compact') {
if (density === 'slim') {
return cn('h-7 gap-4');
}
if (density === 'medium') {
if (density === 'compact') {
return cn('h-[44px] gap-2');
}
return cn('h-[44px] gap-4');
};

const getDensityItemClasses = (density: Density): string => {
if (density === 'medium') {
if (density === 'compact') {
return cn(tabMedium, 'p-2');
}
if (density === 'compact') {
if (density === 'slim') {
return cn(tabSmall, 'py-1 px-2');
}
return cn(tab, 'grow shrink basis-0 p-2');
Expand Down
4 changes: 2 additions & 2 deletions packages/ui/src/ValueView/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ const getGap = (density: Density) => {
if (density === 'sparse') {
return 'gap-2';
}
if (density === 'medium') {
if (density === 'compact') {
return 'gap-1.5';
}
return 'gap-1';
Expand All @@ -142,7 +142,7 @@ const getIconSize = (density: Density) => {
if (density === 'sparse') {
return 'lg';
}
if (density === 'medium') {
if (density === 'compact') {
return 'md';
}
return 'sm';
Expand Down
29 changes: 21 additions & 8 deletions packages/ui/src/theme/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,11 @@ const PALETTE = {
},
base: {
black: '#000000',
blackAlt: '#0D0D0D',
white: '#ffffff',
transparent: 'transparent',
},
};
} as const;

/**
* Call `theme.spacing(x)`, where `x` is the number of spacing units (in the
Expand Down Expand Up @@ -192,6 +193,7 @@ export const theme = {
},
base: {
black: PALETTE.base.black,
blackAlt: PALETTE.base.blackAlt,
white: PALETTE.base.white,
transparent: PALETTE.base.transparent,
},
Expand All @@ -210,24 +212,35 @@ export const theme = {
unshieldFocusOutline: PALETTE.purple['400'],
neutralFocusOutline: PALETTE.neutral['400'],
destructiveFocusOutline: PALETTE.red['400'],
successFocusOutline: PALETTE.green['400'],
},
other: {
tonalStroke: PALETTE.neutral['50'] + hexOpacity(0.15),
tonalFill5: PALETTE.neutral['50'] + hexOpacity(0.05),
tonalFill10: PALETTE.neutral['50'] + hexOpacity(0.1),
solidStroke: PALETTE.neutral['700'],
solidStroke: PALETTE.neutral['900'],
dialogBackground: PALETTE.teal['700'] + hexOpacity(0.1),
overlay: PALETTE.base.black + hexOpacity(0.5),
},
},
gradient: {
card: 'linear-gradient(136deg, rgba(250, 250, 250, 0.1) 6.32%, rgba(250, 250, 250, 0.01) 75.55%)',
tabNeutral: 'radial-gradient(at 50% 100%, rgba(163, 163, 163, 0.35) 0%, transparent 50%)',
tabAccent: 'radial-gradient(at 50% 100%, rgba(244, 156, 67, 0.35) 0%, transparent 50%)',
tabUnshield: 'radial-gradient(at 50% 100%, rgba(193, 166, 204, 0.35) 0%, transparent 50%)',
dialogSuccess: `radial-gradient(100% 100% at 0% 0%, rgba(83, 174, 168, 0.20) 0%, rgba(83, 174, 168, 0.02) 100%)`,
dialogCaution: `radial-gradient(100% 100% at 0% 0%, rgba(153, 97, 15, 0.20) 0%, rgba(153, 97, 15, 0.02) 100%)`,
dialogError: `radial-gradient(100% 100% at 0% 0%, rgba(175, 38, 38, 0.20) 0%, rgba(175, 38, 38, 0.02) 100%)`,
neutralRadialGradient:
'radial-gradient(50% 100% at 50% 100%, rgba(163, 163, 163, 0.35) 0%, rgba(163, 163, 163, 0.00) 95%)',
accentRadialGradient:
'radial-gradient(50% 100% at 50% 100%, rgba(186, 77, 20, 0.35) 0%, rgba(186, 77, 20, 0.00) 95%)',
unshieldRadialGradient:
'radial-gradient(50% 100% at 50% 100%, rgba(112, 82, 121, 0.35) 0%, rgba(112, 82, 121, 0.00) 95%)',
accentRadialBackground:
'radial-gradient(100% 100% at 0% 0%, rgba(244, 156, 67, 0.25) 0%, rgba(244, 156, 67, 0.03) 100%)',
unshieldRadialBackground:
'radial-gradient(100% 100% at 0% 0%, rgba(193, 166, 204, 0.25) 0%, rgba(193, 166, 204, 0.03) 100%)',
secondaryRadialBackground:
'radial-gradient(100% 100% at 0% 0%, rgba(83, 174, 168, 0.25) 0%, rgba(83, 174, 168, 0.03) 100%)',
cautionRadialBackground:
'radial-gradient(100% 100% at 0% 0%, rgba(153, 97, 15, 0.25) 0%, rgba(153, 97, 15, 0.03) 100%)',
destructiveRadialBackground:
'radial-gradient(100% 100% at 0% 0%, rgba(175, 38, 38, 0.25) 0%, rgba(175, 38, 38, 0.03) 100%)',
buttonHover:
'linear-gradient(0deg, rgba(83, 174, 168, 0.15) 0%, rgba(83, 174, 168, 0.15) 100%)',
buttonDisabled: 'linear-gradient(0deg, rgba(10, 10, 10, 0.8) 0%, rgba(10, 10, 10, 0.8) 100%)',
Expand Down
22 changes: 9 additions & 13 deletions packages/ui/src/utils/action-type.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import cn from 'clsx';

export type ActionType = 'default' | 'accent' | 'unshield' | 'destructive';
export type ActionType = 'default' | 'accent' | 'unshield' | 'destructive' | 'success';

export const getColorByActionType = (actionType: ActionType): string => {
if (actionType === 'destructive') {
Expand All @@ -9,64 +9,60 @@ export const getColorByActionType = (actionType: ActionType): string => {
return cn('text-text-primary');
};

const AFTER_OUTLINE_COLOR_MAP: Record<ActionType, string> = {
default: cn('focus-within:after:outline-action-neutralFocusOutline'),
accent: cn('focus-within:after:outline-action-primaryFocusOutline'),
unshield: cn('focus-within:after:outline-action-unshieldFocusOutline'),
destructive: cn('focus-within:after:outline-action-destructiveFocusOutline'),
};

const OUTLINE_COLOR_MAP: Record<ActionType, string> = {
default: cn('outline-neutral-main'),
default: cn('outline-other-tonalStroke'),
accent: cn('outline-primary-main'),
unshield: cn('outline-unshield-main'),
destructive: cn('outline-destructive-main'),
success: cn('outline-success-main'),
};

const BEFORE_OUTLINE_COLOR_MAP: Record<ActionType, string> = {
default: cn('focus:before:outline-action-neutralFocusOutline'),
accent: cn('focus:before:outline-action-primaryFocusOutline'),
unshield: cn('focus:before:outline-action-unshieldFocusOutline'),
destructive: cn('focus:before:outline-action-destructiveFocusOutline'),
success: cn('focus:before:outline-action-successFocusOutline'),
};

const FOCUS_OUTLINE_COLOR_MAP: Record<ActionType, string> = {
default: cn('focus:outline-action-neutralFocusOutline'),
accent: cn('focus:outline-action-primaryFocusOutline'),
unshield: cn('focus:outline-action-unshieldFocusOutline'),
destructive: cn('focus:outline-action-destructiveFocusOutline'),
success: cn('focus:outline-action-successFocusOutline'),
};

const FOCUS_WITHIN_OUTLINE_COLOR_MAP: Record<ActionType, string> = {
default: cn('focus-within:outline-action-neutralFocusOutline'),
accent: cn('focus-within:outline-action-primaryFocusOutline'),
unshield: cn('focus-within:outline-action-unshieldFocusOutline'),
destructive: cn('focus-within:outline-action-destructiveFocusOutline'),
success: cn('focus-within:outline-action-successFocusOutline'),
};

const ARIA_CHECKED_OUTLINE_COLOR_MAP: Record<ActionType, string> = {
default: cn('aria-checked:outline-action-neutralFocusOutline'),
accent: cn('aria-checked:outline-action-primaryFocusOutline'),
unshield: cn('aria-checked:outline-action-unshieldFocusOutline'),
destructive: cn('aria-checked:outline-action-destructiveFocusOutline'),
success: cn('aria-checked:outline-action-successFocusOutline'),
};

const BORDER_COLOR_MAP: Record<ActionType, string> = {
default: cn('border-neutral-main'),
accent: cn('border-primary-main'),
unshield: cn('border-unshield-main'),
destructive: cn('border-destructive-main'),
success: cn('border-success-main'),
};

const BACKGROUND_COLOR_MAP: Record<ActionType, string> = {
default: cn('bg-neutral-main'),
accent: cn('bg-primary-main'),
unshield: cn('bg-unshield-main'),
destructive: cn('bg-destructive-main'),
};

export const getAfterOutlineColorByActionType = (actionType: ActionType): string => {
return AFTER_OUTLINE_COLOR_MAP[actionType];
success: cn('bg-success-main'),
};

export const getBeforeOutlineColorByActionType = (actionType: ActionType): string => {
Expand Down
Loading

0 comments on commit be8b50c

Please sign in to comment.