Skip to content

Commit

Permalink
Visual updates to Button & IconButton components (#502)
Browse files Browse the repository at this point in the history
  • Loading branch information
robphoenix authored Sep 26, 2024
1 parent a3245c5 commit 9c3597b
Show file tree
Hide file tree
Showing 15 changed files with 590 additions and 204 deletions.
5 changes: 5 additions & 0 deletions .changeset/strong-spoons-protect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@utilitywarehouse/web-ui': patch
---

`Button` & `IconButton` visual updates
5 changes: 4 additions & 1 deletion packages/web-ui/src/BaseButton/BaseButton.props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,7 @@ export type BaseButtonProps = UnstyledButtonProps &
variant?: 'outline' | 'ghost';
colorScheme?: 'cyan' | 'red' | 'green' | 'gold' | 'grey';
}
);
) & {
/** Inverts the component colours, for use on darker backgrounds. */
inverted?: boolean;
};
274 changes: 182 additions & 92 deletions packages/web-ui/src/BaseButton/BaseButton.tsx

Large diffs are not rendered by default.

74 changes: 21 additions & 53 deletions packages/web-ui/src/Button/Button.docs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ import { DocsHeader } from '../storybook-utils';

- [Accessibility](#accessibility)
- [Variant](#variant)
- [Color schemes](#color-schemes)
- [Colour schemes](#color-schemes)
- [Inverted colours](#inverted-colours)
- [Size](#size)
- [With icons](#with-icons)
- [Semantic HTML](#semantic-html)
Expand All @@ -28,6 +29,10 @@ import { DocsHeader } from '../storybook-utils';

Uses a native `<button>` element under the hood, with support for the <kbd>Space</kbd> and <kbd>Enter</kbd> keys.

Disabled buttons use `aria-disabled` so that they are still focusable when
using the keyboard. This means that screen readers are still able to find the
button, however the `onClick` handler will be disabled.

### Known issues

We are aware that the `cyan` background colour for the `solid` variant does not
Expand Down Expand Up @@ -76,7 +81,7 @@ The ghost button can be combined with all colour schemes.

<Canvas of={Stories.GhostVariant} />

## Color schemes
## Colour schemes

The `colorScheme` prop will change the button colours. Be aware there are some
combinations of `colorScheme` & `variant` that do not work together (ie. `solid` &
Expand Down Expand Up @@ -117,6 +122,15 @@ to have very low prominence. It's especially useful in designs that are
colour-heavy, as it allows the button to blend in with the overall colour scheme,
making it less visually prominent.

## Inverted colours

When inside a `Box` component that specifies a `background` which is the value
of either `colorsCommon.brandMidnight` or `colorsCommon.brandPrimaryPurple`,
the `Button` component colours will be changed to an inverted colour set. This
can also be achieved using the `inverted` prop.

<Canvas of={Stories.InvertedColour} />

## Size

The size prop controls the size of the `Button`. This is a responsive prop and
Expand All @@ -138,18 +152,15 @@ can be used to display different sizes at different breakpoints.
You can nest icons directly inside the `Button`. An appropriate gap is provided
automatically, and the icon will inherit the appropriate colours.

This button is intended to be used with the [UW Icons](https://uw-icons.vercel.app/) packages. Small buttons
should use 16px icons (small), while medium buttons should use 24px icons
(medium).

Using other icons may result in an unexpected appearance.
This button is intended to be used with the [UW Icons](https://uw-icons.vercel.app/) packages.
All buttons should use the small (16px) icons.

<Canvas of={Stories.SimpleExample} />

```tsx
<Button variant="solid" size="medium">
<SettingsMediumIcon />
Edit account
Continue
<ChevronRight01SmallIcon />
</Button>
```

Expand All @@ -168,47 +179,6 @@ appropriate styles.
</Button>
```

### Responsive icon sizes

If you need to render different sized icons in conjunction with the responsive size prop, there are a couple of ways you can do this.

1. Using JS, with the `useMediaQuery` hook.

```tsx
import { Button, useMediaQuery } from '@utilitywarehouse/web-ui';
import { ChevronRight01MediumIcon, ChevronRight01SmallIcon } from '@utilitywarehouse/react-icons';

const MyComponent = () => {
const isDesktop = useMediaQuery(theme => theme.breakpoints.up('desktop'));

return (
<Button size={{ mobile: 'small', desktop: 'medium' }}>
Next page
{isDesktop ? <ChevronRight01MediumIcon /> : <ChevronRight01SmallIcon />}
</Button>
);
};
```

2. Using CSS, with `Box` and style props. This can also be achieved with the `sx` prop.

```tsx
import { Box, Button } from '@utilitywarehouse/web-ui';
import { ChevronRight01MediumIcon, ChevronRight01SmallIcon } from '@utilitywarehouse/react-icons';

const MyComponent = () => {
const isDesktop = useMediaQuery(theme => theme.breakpoints.up('desktop'));

return (
<IconButton size={{ mobile: 'small', desktop: 'medium' }}>
Next page
<Box component={ChevronRight01MediumIcon} display={{ mobile: 'none', desktop: 'block' }} />
<Box component={ChevronRight01SmallIcon} display={{ desktop: 'none' }} />
</IconButton>
);
};
```

## Semantic HTML

> If it goes somewhere it's a link, if it does something it's a button.
Expand All @@ -226,9 +196,7 @@ Read more about this idea in the [Radix UI composition docs](https://www.radix-u

```tsx
<Button asChild>
<a href="https://uw.co.uk/services">
<OpenMediumIcon />
</a>
<a href="https://uw.co.uk/services">View UW services</a>
</Button>
```

Expand Down
121 changes: 87 additions & 34 deletions packages/web-ui/src/Button/Button.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
import type { Meta, StoryObj } from '@storybook/react';
import {
ChevronLeft01MediumIcon,
ChevronLeft01SmallIcon,
ChevronRight01MediumIcon,
ChevronRight01SmallIcon,
OpenMediumIcon,
} from '@utilitywarehouse/react-icons';
import { ChevronLeft01SmallIcon, ChevronRight01SmallIcon } from '@utilitywarehouse/react-icons';
import * as React from 'react';
import { colorsCommon } from '@utilitywarehouse/colour-system';
import { Box } from '../Box';
import { Flex } from '../Flex';
import { Heading } from '../Heading';
Expand All @@ -27,13 +22,15 @@ const meta: Meta<typeof Button> = {
colorScheme: { options: colorSchemes, control: { type: 'radio' } },
size: { control: { type: 'radio' }, options: sizes },
disabled: { control: { type: 'boolean' } },
inverted: { control: { type: 'boolean' } },
},
args: {
children: 'Button',
variant: 'solid',
colorScheme: 'cyan',
size: 'medium',
disabled: false,
inverted: false,
},
};

Expand All @@ -53,13 +50,7 @@ export const KitchenSink: Story = {
{sizes.map(size => (
<Flex key={size} gap={1}>
{solidColorSchemes.map(colorScheme => (
<Button
key={colorScheme}
variant="solid"
colorScheme={colorScheme}
size={size}
onClick={() => alert('hello, world!')}
>
<Button key={colorScheme} variant="solid" colorScheme={colorScheme} size={size}>
Button
</Button>
))}
Expand All @@ -76,7 +67,6 @@ export const KitchenSink: Story = {
variant="solid"
colorScheme={colorScheme}
size={size}
onClick={() => alert('hello, world!')}
>
Button
</Button>
Expand All @@ -99,7 +89,6 @@ export const KitchenSink: Story = {
variant={variant}
colorScheme={colorScheme}
size={size}
onClick={() => alert('hello, world!')}
>
Button
</Button>
Expand All @@ -117,7 +106,6 @@ export const KitchenSink: Story = {
variant={variant}
colorScheme={colorScheme}
size={size}
onClick={() => alert('hello, world!')}
>
Button
</Button>
Expand All @@ -139,7 +127,7 @@ export const AsLink: Story = {
<Button asChild>
<a href="https://uw.co.uk/services">
View UW services
<OpenMediumIcon />
<ChevronRight01SmallIcon />
</a>
</Button>
),
Expand All @@ -157,7 +145,7 @@ export const WithIcons: Story = {
<Flex gap={2} align="center">
{solidColorSchemes.map(colorScheme => (
<Button key={colorScheme} variant="solid" colorScheme={colorScheme} size="medium">
<ChevronLeft01MediumIcon /> Button
<ChevronLeft01SmallIcon /> Button
</Button>
))}
{solidColorSchemes.map(colorScheme => (
Expand All @@ -169,7 +157,7 @@ export const WithIcons: Story = {
<Flex gap={2} align="center">
{solidColorSchemes.map(colorScheme => (
<Button key={colorScheme} variant="solid" colorScheme={colorScheme} size="medium">
Button <ChevronRight01MediumIcon />
Button <ChevronRight01SmallIcon />
</Button>
))}
{solidColorSchemes.map(colorScheme => (
Expand All @@ -194,7 +182,7 @@ export const WithIcons: Story = {
colorScheme={colorScheme}
size="medium"
>
<ChevronLeft01MediumIcon /> Button
<ChevronLeft01SmallIcon /> Button
</Button>
))}
{colorSchemes.map(colorScheme => (
Expand All @@ -216,7 +204,7 @@ export const WithIcons: Story = {
colorScheme={colorScheme}
size="medium"
>
Button <ChevronRight01MediumIcon />
Button <ChevronRight01SmallIcon />
</Button>
))}
{colorSchemes.map(colorScheme => (
Expand All @@ -240,15 +228,23 @@ export const WithIcons: Story = {

export const SimpleExample: Story = {
render: () => (
<Flex gap={2}>
{variants.map(variant => (
<Button key={variant} variant={variant} onClick={() => alert('Hello world!')}>
Next page <ChevronRight01MediumIcon />
</Button>
))}
<Button disabled variant="outline" onClick={() => alert('Hello world!')}>
Next page <ChevronRight01MediumIcon />
</Button>
<Flex direction="column" gap={2}>
<Flex gap={2}>
{variants.map(variant => (
<Button key={variant} variant={variant}>
Next
<ChevronRight01SmallIcon />
</Button>
))}
</Flex>
<Flex gap={2}>
{variants.map(variant => (
<Button key={variant} variant={variant}>
<ChevronLeft01SmallIcon />
Back
</Button>
))}
</Flex>
</Flex>
),
};
Expand Down Expand Up @@ -305,19 +301,76 @@ export const ResponsiveSize: Story = {
render: args => (
<Button {...args} size={{ mobile: 'small', desktop: 'medium' }}>
Responsive size button
<Box component={ChevronRight01MediumIcon} display={{ mobile: 'none', desktop: 'block' }} />
<Box component={ChevronRight01SmallIcon} display={{ mobile: 'block', desktop: 'none' }} />
<ChevronRight01SmallIcon />
</Button>
),
};

export const InvertedColour: Story = {
name: 'Inverted Colour',
render: () => {
return (
<Flex direction="column">
{[colorsCommon.brandPrimaryPurple, colorsCommon.brandMidnight].map(bgColor => (
<Box key={bgColor} padding={2} background={bgColor}>
<Flex direction="column" gap={2}>
<Flex gap={2}>
{solidColorSchemes.map(color => (
<Button key={color} variant="solid" colorScheme={color}>
Button
</Button>
))}
</Flex>
<Flex gap={2}>
{solidColorSchemes.map(color => (
<Button key={color} variant="solid" colorScheme={color} disabled>
Button
</Button>
))}
</Flex>
<Flex gap={2}>
{colorSchemes.map(color => (
<Button key={color} variant="outline" colorScheme={color}>
Button
</Button>
))}
</Flex>
<Flex gap={2}>
{colorSchemes.map(color => (
<Button key={color} variant="outline" colorScheme={color} disabled>
Button
</Button>
))}
</Flex>
<Flex gap={2}>
{colorSchemes.map(color => (
<Button key={color} variant="ghost" colorScheme={color}>
Button
</Button>
))}
</Flex>
<Flex gap={2}>
{colorSchemes.map(color => (
<Button key={color} variant="ghost" colorScheme={color} disabled>
Button
</Button>
))}
</Flex>
</Flex>
</Box>
))}
</Flex>
);
},
};

export const FullWidth: Story = {
render: args => (
<Flex direction="column" align={{ mobile: 'stretch', desktop: 'start' }}>
<Text>This Button is full width for screen widths below the desktop breakpoint.</Text>
<Button {...args}>
{args.children}
<ChevronRight01MediumIcon />
<ChevronRight01SmallIcon />
</Button>
</Flex>
),
Expand Down
Loading

0 comments on commit 9c3597b

Please sign in to comment.