Skip to content

Commit 1d044d2

Browse files
committed
feat(button): add button colors in theme
1 parent d8cb01e commit 1d044d2

7 files changed

Lines changed: 239 additions & 323 deletions

File tree

src/components/Button/Button.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import { Box } from '../Box';
2828
getStyles={(props, theme) => ({
2929
buttonStyle: {},
3030
textStyle: {},
31-
focusColor: '',
31+
focusColor: '#5ab9ae',
3232
})}
3333
/>
3434
</Playground>

src/components/Button/Button.styles.ts

Lines changed: 20 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,18 @@
11
import { TextStyle, ViewStyle } from 'react-native';
22

3-
import { Theme } from '../../theme/ThemeInterface';
3+
import { ButtonColor, Theme } from '../../theme/ThemeInterface';
4+
import { darken } from './changeColor';
45

56
export type ButtonColorProps = ViewStyle & {
7+
backgroundColor: string;
68
color: string;
7-
focusColor: string;
89
loadingBackgroundColor?: string;
910
};
1011

11-
export interface ButtonColors {
12-
default: ButtonColorProps;
13-
danger: ButtonColorProps;
14-
primary: ButtonColorProps;
15-
secondary: ButtonColorProps;
16-
}
17-
18-
export type ButtonColor = keyof ButtonColors;
19-
2012
export interface ButtonAppearances {
21-
minimal: ButtonColors;
22-
primary: ButtonColors;
23-
outline: ButtonColors;
13+
minimal: { [size in ButtonColor]: ButtonColorProps };
14+
primary: { [size in ButtonColor]: ButtonColorProps };
15+
outline: { [size in ButtonColor]: ButtonColorProps };
2416
}
2517

2618
export type ButtonAppearance = keyof ButtonAppearances;
@@ -57,65 +49,47 @@ export const getButtonVariables = (theme: Theme): ButtonVariables => {
5749
default: {
5850
backgroundColor: theme.colors.background.content,
5951
color: theme.colors.text.default,
60-
61-
focusColor: theme.colors.background.greyDefault,
6252
},
6353

6454
danger: {
6555
backgroundColor: theme.colors.background.content,
6656
color: theme.colors.text.danger,
67-
68-
focusColor: theme.colors.background.greyDefault,
6957
},
7058
primary: {
7159
backgroundColor: theme.colors.background.content,
7260
color: theme.colors.text.primary,
73-
74-
focusColor: theme.colors.background.greyDefault,
7561
},
7662
secondary: {
7763
backgroundColor: theme.colors.background.content,
78-
color: theme.colors.text.secondary
79-
? theme.colors.text.secondary
80-
: theme.colors.text.default,
81-
82-
focusColor: theme.colors.background.greyDefault,
64+
color: theme.colors.text.secondary,
8365
},
8466
},
8567
primary: {
8668
default: {
87-
backgroundColor: theme.colors.background.greyLight,
88-
borderColor: theme.colors.border.default,
89-
color: theme.colors.text.default,
69+
backgroundColor: theme.colors.button.default,
70+
color: theme.colors.button.defaultText,
9071

91-
focusColor: theme.colors.background.greyDefault,
9272
loadingBackgroundColor: theme.colors.background.overlay,
9373
},
9474

9575
danger: {
96-
backgroundColor: theme.colors.background.dangerDefault,
97-
borderColor: theme.colors.border.danger,
98-
color: theme.colors.text.white,
76+
backgroundColor: theme.colors.button.danger,
77+
color: theme.colors.button.dangerText,
9978

100-
focusColor: theme.colors.background.dangerDark,
10179
loadingBackgroundColor: theme.colors.background.dangerLight,
10280
},
10381

10482
primary: {
105-
backgroundColor: theme.colors.background.primaryDefault,
106-
borderColor: theme.colors.border.primary,
107-
color: theme.colors.text.white,
83+
backgroundColor: theme.colors.button.primary,
84+
color: theme.colors.button.primaryText,
10885

109-
focusColor: theme.colors.background.primaryDark,
11086
loadingBackgroundColor: theme.colors.background.primaryLight,
11187
},
11288

11389
secondary: {
114-
backgroundColor: theme.colors.background.secondaryDefault,
115-
borderColor: theme.colors.border.secondary,
116-
color: theme.colors.text.white,
90+
backgroundColor: theme.colors.button.secondary,
91+
color: theme.colors.button.secondaryText,
11792

118-
focusColor: theme.colors.background.secondaryDark,
11993
loadingBackgroundColor: theme.colors.background.secondaryLight,
12094
},
12195
},
@@ -126,40 +100,32 @@ export const getButtonVariables = (theme: Theme): ButtonVariables => {
126100
borderColor: theme.colors.text.default,
127101
borderWidth: 3,
128102
color: theme.colors.text.default,
129-
130-
focusColor: theme.colors.background.greyDefault,
131103
},
132104

133105
danger: {
134106
backgroundColor: theme.colors.background.content,
135107
borderColor: theme.colors.border.danger,
136108
borderWidth: 3,
137109
color: theme.colors.text.danger,
138-
139-
focusColor: theme.colors.background.dangerLight,
140110
},
141111

142112
primary: {
143113
backgroundColor: theme.colors.background.content,
144114
borderColor: theme.colors.border.primary,
145115
borderWidth: 3,
146116
color: theme.colors.text.primary,
147-
148-
focusColor: theme.colors.background.primaryLight,
149117
},
150118

151119
secondary: {
152120
backgroundColor: theme.colors.background.content,
153121
borderColor: theme.colors.border.secondary,
154122
borderWidth: 3,
155123
color: theme.colors.text.secondary,
156-
157-
focusColor: theme.colors.background.secondaryLight,
158124
},
159125
},
160126
},
161127
disabled: {
162-
backgroundColor: theme.colors.background.greyDark,
128+
backgroundColor: theme.colors.button.disabled,
163129

164130
color: theme.colors.text.muted,
165131
},
@@ -232,7 +198,6 @@ export const getButtonStyles: GetButtonStyles = (buttonStyleProps, theme) => {
232198

233199
const {
234200
color: textColor,
235-
focusColor,
236201
loadingBackgroundColor,
237202
...buttonStyle
238203
} = appearances[appearance][color];
@@ -261,7 +226,10 @@ export const getButtonStyles: GetButtonStyles = (buttonStyleProps, theme) => {
261226
}
262227
: {}),
263228
},
264-
focusColor,
229+
focusColor:
230+
appearance === 'minimal' || appearance === 'outline'
231+
? theme.colors.button.default
232+
: darken(buttonStyle.backgroundColor, 0.05),
265233
textStyle: {
266234
alignItems: 'center',
267235
color: isDisabled ? disabledButtonTextColor : textColor,

src/components/Button/Button.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,12 @@ import {
88
} from 'react-native';
99
import { DeepPartial } from 'ts-essentials';
1010

11-
import { useTheme } from '../../theme';
11+
import { ButtonColor, useTheme } from '../../theme';
1212
import { mergeStyles, ReplaceReturnType } from '../../utils/mergeStyles';
1313
import { LoadingDots } from '../Loading';
1414
import { Text } from '../Typography';
1515
import {
1616
ButtonAppearance,
17-
ButtonColor,
1817
ButtonSize,
1918
ButtonStyles,
2019
GetButtonStyles,
@@ -32,7 +31,7 @@ export interface ButtonProps extends AccessibilityProps {
3231

3332
/**
3433
* The intent of the button.
35-
* @default "default"
34+
* @default "primary"
3635
*/
3736
color?: ButtonColor;
3837

@@ -108,7 +107,7 @@ export const Button = (props: ButtonProps) => {
108107
const {
109108
appearance = 'primary',
110109
title,
111-
color = 'default',
110+
color = 'primary',
112111
getStyles,
113112
icon,
114113
iconAfter,
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
// From https://stackoverflow.com/questions/1507931/generate-lighter-darker-color-in-css-using-javascript
2+
3+
const pad = (num: string, totalChars: number) => {
4+
num = num + '';
5+
while (num.length < totalChars) {
6+
num = '0' + num;
7+
}
8+
return num;
9+
};
10+
11+
// Ratio is between 0 and 1
12+
const changeColor = (color: string, ratio: number, darker: boolean) => {
13+
// Trim trailing/leading whitespace
14+
color = color.replace(/^\s*|\s*$/, '');
15+
16+
// Expand three-digit hex
17+
color = color.replace(/^#?([a-f0-9])([a-f0-9])([a-f0-9])$/i, '#$1$1$2$2$3$3');
18+
19+
// Calculate ratio
20+
const difference = Math.round(ratio * 256) * (darker ? -1 : 1);
21+
// Determine if input is RGB(A)
22+
const rgbValue = '(\\d|[1-9]\\d|1\\d{2}|2[0-4][0-9]|25[0-5])';
23+
const rgb = color.match(
24+
new RegExp(
25+
'^rgba?\\(\\s*' +
26+
rgbValue +
27+
'\\s*,\\s*' +
28+
rgbValue +
29+
'\\s*,\\s*' +
30+
rgbValue +
31+
'(?:\\s*,\\s*' +
32+
'(0|1|0?\\.\\d+))?' +
33+
'\\s*\\)$',
34+
'i',
35+
),
36+
);
37+
38+
const alpha = !!rgb && rgb[4] != null ? rgb[4] : null;
39+
// Convert hex to decimal
40+
const decimal = !!rgb
41+
? [rgb[1], rgb[2], rgb[3]]
42+
: color
43+
.replace(
44+
/^#?([a-f0-9][a-f0-9])([a-f0-9][a-f0-9])([a-f0-9][a-f0-9])/i,
45+
(substring, arg1, arg2, arg3) => {
46+
return (
47+
parseInt(arg1, 16) +
48+
',' +
49+
parseInt(arg2, 16) +
50+
',' +
51+
parseInt(arg3, 16)
52+
);
53+
},
54+
)
55+
.split(/,/);
56+
57+
// Return RGB(A)
58+
return !!rgb
59+
? 'rgb' +
60+
(alpha !== null ? 'a' : '') +
61+
'(' +
62+
Math[darker ? 'max' : 'min'](
63+
parseInt(decimal[0], 10) + difference,
64+
darker ? 0 : 255,
65+
) +
66+
', ' +
67+
Math[darker ? 'max' : 'min'](
68+
parseInt(decimal[1], 10) + difference,
69+
darker ? 0 : 255,
70+
) +
71+
', ' +
72+
Math[darker ? 'max' : 'min'](
73+
parseInt(decimal[2], 10) + difference,
74+
darker ? 0 : 255,
75+
) +
76+
(alpha !== null ? ', ' + alpha : '') +
77+
')'
78+
: // Return hex
79+
[
80+
'#',
81+
pad(
82+
Math[darker ? 'max' : 'min'](
83+
parseInt(decimal[0], 10) + difference,
84+
darker ? 0 : 255,
85+
).toString(16),
86+
2,
87+
),
88+
pad(
89+
Math[darker ? 'max' : 'min'](
90+
parseInt(decimal[1], 10) + difference,
91+
darker ? 0 : 255,
92+
).toString(16),
93+
2,
94+
),
95+
pad(
96+
Math[darker ? 'max' : 'min'](
97+
parseInt(decimal[2], 10) + difference,
98+
darker ? 0 : 255,
99+
).toString(16),
100+
2,
101+
),
102+
].join('');
103+
};
104+
105+
export const darken = (color: string, ratio: number) => {
106+
return changeColor(color, ratio, true);
107+
};
108+
109+
export const lighten = (color: string, ratio: number) => {
110+
return changeColor(color, ratio, false);
111+
};

src/theme/ThemeInterface.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,14 +77,30 @@ export interface TextColors {
7777

7878
export type TextColor = keyof TextColors | string;
7979

80-
export interface BorderColors {
80+
export interface ButtonColors {
81+
danger: string;
8182
default: string;
82-
83+
disabled: string;
8384
primary: string;
8485
secondary: string;
86+
}
87+
88+
export interface ButtonColorsWithText extends ButtonColors {
89+
dangerText: string;
90+
defaultText: string;
91+
disabledText: string;
92+
primaryText: string;
93+
secondaryText: string;
94+
}
8595

96+
export type ButtonColor = keyof ButtonColors | string;
97+
98+
export interface BorderColors {
8699
danger: string;
100+
default: string;
87101
info: string;
102+
primary: string;
103+
secondary: string;
88104
success: string;
89105
warning: string;
90106
}
@@ -130,6 +146,7 @@ export type BackgroundColor = keyof BackgroundColors | string;
130146
export interface Colors {
131147
background: BackgroundColors;
132148
border: BorderColors;
149+
button: ButtonColorsWithText;
133150
text: TextColors;
134151
}
135152

src/theme/default-theme/foundational-styles/colors.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,23 @@ const colors: Colors = {
5050
warning: palette.orange.darkest,
5151
},
5252

53+
button: {
54+
disabled: palette.neutral.light,
55+
disabledText: palette.neutral.darkest,
56+
57+
default: palette.neutral.lightest,
58+
defaultText: palette.neutral.darkest,
59+
60+
primary: palette.teal.base,
61+
primaryText: 'white',
62+
63+
secondary: palette.orange.base,
64+
secondaryText: 'white',
65+
66+
danger: palette.red.base,
67+
dangerText: 'white',
68+
},
69+
5370
text: {
5471
link: palette.neutral.darkest,
5572
default: palette.neutral.darkest,

0 commit comments

Comments
 (0)