Skip to content

Commit e54a442

Browse files
authored
[Brand Refactor] Button updates (#1344)
1 parent 2525c28 commit e54a442

194 files changed

Lines changed: 189 additions & 328 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
'@primer/react-brand': minor
3+
'@primer/brand-primitives': minor
4+
'@primer/brand-css': minor
5+
---
6+
7+
Updated Button appearance and API ergonomics:
8+
9+
- ⚠️ Breaking change: Removed the `accent` Button variant. Use `primary` instead, which now applies the previous `accent` appearance.
10+
- Updated `secondary`, and `subtle` variant colors and state styles.
11+
- Updated medium Button label typography to better match the new Figma treatment.
12+
- Deprecated the `hasArrow` prop and hid Button arrows by default.
13+
- Note: `hasArrow` will be removed entirely in a future release.
-16.4 KB
Binary file not shown.

apps/next-docs/content/components/Button/index.mdx

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ thumbnail_darkMode: '/images/thumbnails/button-thumbnail-dark.png'
99
---
1010

1111
import header from './images/button_header.png'
12-
import buttonAccent from './images/button_accent.png'
1312

1413
<img src={header.src} alt="The different button types" />
1514

@@ -64,12 +63,6 @@ Primary buttons have top priority in visual hierarchy, and using too many of the
6463

6564
![An image showing all primary states.](https://github.com/primer/brand/assets/6951037/dfad0124-4cdc-4230-b442-4ee9c747d0f0)
6665

67-
#### Accent
68-
69-
Accent buttons share a similar hierarchical priority, and should also be used sparingly to avoid diluting their effectiveness. Crucially, accent buttons should only be used in the [hero](/components/Hero) and [CTA banner](/components/CTABanner) components.
70-
71-
<img src={buttonAccent.src} alt="All accent button states." />
72-
7366
#### Secondary
7467

7568
Secondary buttons are the default button type. Use them for the main actions on a page or form. Secondary buttons are the most common button type and should be used in most cases. They can be paired with a primary button to perform a secondary action, or used on their own.
@@ -130,10 +123,6 @@ Small buttons are meant to be used sparingly. Some use cases can be:
130123
- Areas where very low priority information might display a CTA.
131124
- Brand presence inside products where information is compact. For example for in-product banner.
132125

133-
### Arrow
134-
135-
+The arrow is hidden by default for all button variants except subtle. You can optionally show the arrow when you need to draw attention to a specific, singular button on a page. However, use arrows sparingly on primary actions to avoid diluting their impact. Multiple arrows on the same page will reduce their effectiveness as directional cues.
136-
137126
## Accessibility
138127

139128
### Descriptive buttons

apps/next-docs/content/components/Button/react.mdx

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,6 @@ The `primary` variant of `Button` indicates that the action is of high priority,
3636
<Button variant="primary">Primary</Button>
3737
```
3838

39-
### Accent
40-
41-
The `accent` variant is equally high priority, and should be used only in [Hero](../../Hero) or [CTABanner](../../CTABanner) components.
42-
43-
```jsx live
44-
<Button variant="accent">Accent</Button>
45-
```
46-
4739
### Subtle
4840

4941
The `subtle` variant of Button indicates that the action is a low priority one.
@@ -68,14 +60,6 @@ The `subtle` variant of Button indicates that the action is a low priority one.
6860
<Button block>Full-width button</Button>
6961
```
7062

71-
### Removing the arrow
72-
73-
The animated arrow indicator can be removed using the `hasArrow` prop.
74-
75-
```jsx live
76-
<Button hasArrow={false}>No arrow</Button>
77-
```
78-
7963
### Using an icon
8064

8165
You can place an icon inside the `Button` in either the leading or the trailing position to enhance the visual context. It is recommended to use an [Octicon](https://primer.style/octicons) here.
@@ -111,5 +95,5 @@ The `Button` component can render as a `button` or `a` HTML element. By default,
11195
| `variants` | <ButtonVariantsProp /> | `'secondary'` | The styling variations available in Button |
11296
| `size` | <ButtonSizesProp /> | `'medium'` | The size variations available in Button |
11397
| `block` | `boolean` | `false` | The full-width option available in Button |
114-
| `hasArrow` | `boolean` | `true` | A flag to show/hide the arrow icon |
98+
| `hasArrow` | `boolean` | `false` | Deprecated. The `hasArrow` prop will be removed in a future release. |
11599
| `disabled` | `boolean` | `false` | Disables the rendered element using `disabled` or `aria-disabled` attributes based on the element type. |

apps/next-docs/content/components/Tooltip/index.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ Use this with caution. It is generally better to use a visible label for better
4747

4848
```jsx live
4949
<Tooltip text="Go fullscreen" type="label">
50-
<Button hasArrow={false}>
50+
<Button>
5151
<ScreenFullIcon />
5252
</Button>
5353
</Tooltip>

packages/design-tokens/src/tokens/functional/components/button/colors.js

Lines changed: 52 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,18 @@ module.exports = {
33
primary: {
44
bgColor: {
55
rest: {
6-
value: 'var(--base-color-scale-black-0)',
7-
dark: 'var(--base-color-scale-white-0)',
6+
value: 'var(--base-color-scale-green-6)',
7+
dark: 'var(--base-color-scale-green-5)',
88
},
99
hover: {
10-
value: 'var(--base-color-scale-gray-8)',
11-
dark: 'var(--base-color-scale-gray-0)',
10+
value:
11+
'linear-gradient(0deg, rgba(0, 0, 0, 0.16) 0%, rgba(0, 0, 0, 0.16) 100%), var(--base-color-scale-green-6)', // 000000 at 16% opacity
12+
dark: 'linear-gradient(0deg, rgba(0, 0, 0, 0.16) 0%, rgba(0, 0, 0, 0.16) 100%), var(--base-color-scale-green-5)', // 000000 at 16% opacity
1213
},
1314
active: {
14-
value: 'var(--base-color-scale-gray-8)',
15-
dark: 'var(--base-color-scale-gray-0)',
15+
value:
16+
'linear-gradient(0deg, rgba(0, 0, 0, 0.24) 0%, rgba(0, 0, 0, 0.24) 100%), var(--base-color-scale-green-6)', // 000000 at 24% opacity
17+
dark: 'linear-gradient(0deg, rgba(0, 0, 0, 0.24) 0%, rgba(0, 0, 0, 0.24) 100%), var(--base-color-scale-green-5)', // 000000 at 24% opacity
1618
},
1719
disabled: {
1820
value: 'var(--base-color-scale-gray-4)',
@@ -21,26 +23,26 @@ module.exports = {
2123
},
2224
borderColor: {
2325
rest: {
24-
value: 'var(--base-color-scale-black-0)',
25-
dark: 'var(--base-color-scale-white-0)',
26+
value: 'transparent',
27+
dark: 'transparent',
2628
},
2729
hover: {
28-
value: 'var(--base-color-scale-gray-8)',
29-
dark: 'var(--base-color-scale-white-0)',
30+
value: 'transparent',
31+
dark: 'transparent',
3032
},
3133
active: {
32-
value: 'var(--base-color-scale-gray-8)',
33-
dark: 'var(--base-color-scale-white-0)',
34+
value: 'var(--base-color-scale-green-7)',
35+
dark: 'var(--base-color-scale-green-6)',
3436
},
3537
disabled: {
36-
value: 'var(--base-color-scale-gray-4)',
37-
dark: 'var(--base-color-scale-gray-4)',
38+
value: 'transparent',
39+
dark: 'transparent',
3840
},
3941
},
4042
fgColor: {
4143
rest: {
42-
value: 'var(--brand-color-text-onEmphasis)',
43-
dark: 'var(--brand-color-text-onEmphasis)',
44+
value: 'var(--base-color-scale-white-0)',
45+
dark: 'var(--base-color-scale-white-0)',
4446
},
4547
disabled: {
4648
value: 'var(--base-color-scale-gray-2)',
@@ -49,66 +51,38 @@ module.exports = {
4951
},
5052
shadow: {
5153
active: {
52-
value: 'inset 0px 1px 0px 0px var(--brand-button-primary-borderColor-active)',
53-
dark: 'inset 0px 1px 0px 0px var(--brand-button-primary-borderColor-active)',
54+
value: 'none',
55+
dark: 'none',
5456
},
5557
},
5658
},
57-
accent: {
59+
secondary: {
5860
bgColor: {
5961
rest: {
60-
value: 'var(--base-color-scale-green-6)',
61-
dark: 'var(--base-color-scale-green-5)',
62+
value: 'rgba(0, 0, 0, 0.06)', // 000000 at 6% opacity
63+
dark: 'rgba(255, 255, 255, 0.12)', // ffffff at 12% opacity
6264
},
6365
hover: {
64-
value: 'var(--base-color-scale-green-7)',
65-
dark: 'var(--base-color-scale-green-6)',
66+
value: 'linear-gradient(0deg, rgba(0, 0, 0, 0.06) 0%, rgba(0, 0, 0, 0.06) 100%), rgba(0, 0, 0, 0.06)', // 000000 at 6% opacity overlay and base
67+
dark: 'rgba(255, 255, 255, 0.16)', // ffffff at 16% opacity
6668
},
6769
active: {
68-
value: 'var(--base-color-scale-green-6)',
69-
dark: 'var(--base-color-scale-green-5)',
70-
},
71-
disabled: {
72-
value: 'var(--base-color-scale-gray-4)',
73-
dark: 'var(--base-color-scale-gray-4)',
70+
value: 'linear-gradient(0deg, rgba(0, 0, 0, 0.16) 0%, rgba(0, 0, 0, 0.16) 100%), rgba(0, 0, 0, 0.06)', // 000000 at 16% opacity over 000000 at 6% opacity
71+
dark: 'rgba(255, 255, 255, 0.26)', // ffffff at 26% opacity
7472
},
7573
},
7674
borderColor: {
77-
active: {
78-
value: 'var(--base-color-scale-green-7)',
79-
dark: 'var(--base-color-scale-green-6)',
80-
},
81-
},
82-
fgColor: {
8375
rest: {
84-
value: 'var(--base-color-scale-white-0)',
85-
dark: 'var(--base-color-scale-white-0)',
86-
},
87-
disabled: {
8876
value: 'var(--base-color-scale-gray-2)',
89-
dark: 'var(--base-color-scale-gray-2)',
90-
},
91-
},
92-
},
93-
secondary: {
94-
bgColor: {
95-
rest: {
96-
value: 'transparent',
97-
dark: 'transparent',
98-
},
99-
},
100-
borderColor: {
101-
rest: {
102-
value: '#0D111729', // 0D1117 at 16% opacity
103-
dark: '#ffffff29', // ffffff at 16% opacity
77+
dark: 'rgba(255, 255, 255, 0.06)', // ffffff at 6% opacity
10478
},
10579
hover: {
106-
value: '#0D111752', // 0D1117 at 32% opacity
107-
dark: '#ffffff52', // ffffff at 32% opacity
80+
value: 'var(--base-color-scale-gray-2)',
81+
dark: 'rgba(255, 255, 255, 0.12)', // ffffff at 12% opacity
10882
},
10983
active: {
110-
value: '#0D111752', // 0D1117 at 32% opacity
111-
dark: '#ffffff52', // ffffff at 32% opacity
84+
value: 'var(--base-color-scale-gray-2)',
85+
dark: 'rgba(255, 255, 255, 0.12)', // ffffff at 12% opacity
11286
},
11387
},
11488
fgColor: {
@@ -125,16 +99,30 @@ module.exports = {
12599
subtle: {
126100
bgColor: {
127101
rest: {
128-
value: '#0D111712', // 0D1117 at 7% opacity
129-
dark: '#ffffff12', // ffffff at 7% opacity
102+
value: 'rgba(0, 0, 0, 0.01)', // 000000 at 1% opacity
103+
dark: 'rgba(0, 0, 0, 0.01)', // 000000 at 1% opacity
104+
},
105+
hover: {
106+
value: 'linear-gradient(0deg, rgba(0, 0, 0, 0.06) 0%, rgba(0, 0, 0, 0.06) 100%), rgba(0, 0, 0, 0.06)', // 000000 at 6% opacity overlay and base
107+
dark: 'rgba(255, 255, 255, 0.16)', // ffffff at 16% opacity
108+
},
109+
active: {
110+
value: 'linear-gradient(0deg, rgba(0, 0, 0, 0.16) 0%, rgba(0, 0, 0, 0.16) 100%), rgba(0, 0, 0, 0.06)', // 000000 at 16% opacity over 000000 at 6% opacity
111+
dark: 'rgba(255, 255, 255, 0.24)', // ffffff at 24% opacity
112+
},
113+
},
114+
borderColor: {
115+
rest: {
116+
value: 'var(--base-color-scale-gray-2)',
117+
dark: 'var(--base-color-scale-gray-6)',
130118
},
131119
hover: {
132-
value: '#0D11170D', // 0D1117 at 5% opacity
133-
dark: '#ffffff0D', // ffffff at 5% opacity
120+
value: 'var(--base-color-scale-gray-2)',
121+
dark: 'rgba(255, 255, 255, 0.12)', // ffffff at 12% opacity
134122
},
135123
active: {
136-
value: '#0D111712', // 0D1117 at 7% opacity
137-
dark: '#ffffff12', // ffffff at 7% opacity
124+
value: 'var(--base-color-scale-gray-2)',
125+
dark: 'rgba(255, 255, 255, 0.12)', // ffffff at 12% opacity
138126
},
139127
},
140128
fgColor: {

packages/react/src/ActionMenu/ActionMenu.features.stories.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ export const SplitButtonModeAllVariants = () => {
170170
/**
171171
* TODO: Only show examples for action menu button variants that are design approved
172172
*/
173-
const filteredButtonsVariants = ButtonVariants.filter(variant => !['accent', 'secondary'].includes(variant))
173+
const filteredButtonsVariants = ButtonVariants.filter(variant => variant !== 'secondary')
174174

175175
return (
176176
<>

packages/react/src/ActionMenu/ActionMenu.module.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@
7070
padding: var(--brand-control-medium-paddingBlock-condensed) var(--brand-control-medium-paddingInline-normal); /* Override the default button padding */
7171
}
7272

73+
.ActionMenu__inner-button-dropdown-icon {
74+
vertical-align: middle !important;
75+
}
76+
7377
.ActionMenu__innerButton--split-button:last-child {
7478
border-top-left-radius: 0;
7579
border-bottom-left-radius: 0;

packages/react/src/ActionMenu/ActionMenu.module.css.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ declare const styles: {
66
readonly "ActionMenu__button--medium": string;
77
readonly "ActionMenu__button--small": string;
88
readonly "ActionMenu__button--split-button": string;
9+
readonly "ActionMenu__inner-button-dropdown-icon": string;
910
readonly "ActionMenu__innerButton--disabled": string;
1011
readonly "ActionMenu__innerButton--medium": string;
1112
readonly "ActionMenu__innerButton--split-button": string;

packages/react/src/ActionMenu/ActionMenu.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,6 @@ const ActionMenuButton = forwardRef<HTMLButtonElement, ActionMenuButtonProps>(
372372
disabled && styles['ActionMenu__innerButton--disabled'],
373373
)}
374374
variant={variant}
375-
hasArrow={false}
376375
aria-disabled={disabled}
377376
data-testid={testId || testIds.button}
378377
size={size}
@@ -385,7 +384,6 @@ const ActionMenuButton = forwardRef<HTMLButtonElement, ActionMenuButtonProps>(
385384
as="button"
386385
className={styles['ActionMenu__innerButton--split-button']}
387386
variant={variant}
388-
hasArrow={false}
389387
aria-haspopup="true"
390388
aria-label="Menu"
391389
size={size}
@@ -394,7 +392,7 @@ const ActionMenuButton = forwardRef<HTMLButtonElement, ActionMenuButtonProps>(
394392
disabled={disabled}
395393
{...props}
396394
>
397-
<ChevronDownIcon />
395+
<ChevronDownIcon className={styles['ActionMenu__inner-button-dropdown-icon']} />
398396
</Button>
399397
</div>
400398
)
@@ -404,7 +402,6 @@ const ActionMenuButton = forwardRef<HTMLButtonElement, ActionMenuButtonProps>(
404402
<Button
405403
ref={ref}
406404
className={clsx(styles.ActionMenu__button, styles[`ActionMenu__button--${size}`], className)}
407-
hasArrow={false}
408405
aria-haspopup="true"
409406
aria-expanded={menuOpen ? 'true' : 'false'}
410407
disabled={disabled}

0 commit comments

Comments
 (0)