diff --git a/app/assets/stylesheets/components/buttons.scss b/app/assets/stylesheets/components/buttons.scss
index 1898074e8..73f53803c 100644
--- a/app/assets/stylesheets/components/buttons.scss
+++ b/app/assets/stylesheets/components/buttons.scss
@@ -513,6 +513,13 @@ label[class*='crayons-btn']:focus-within // label for input[type="file"] made to
}
}
+.c-btn--secondary {
+ --bg: var(--btn-secondary-bg);
+ --bg-hover: var(--btn-secondary-bg-hover);
+ --color: var(--btn-secondary-color);
+ --color-hover: var(--btn-secondary-color-hover);
+}
+
.c-btn--icon-alone {
padding: var(--su-2);
}
diff --git a/app/assets/stylesheets/components/links.scss b/app/assets/stylesheets/components/links.scss
index 2c36111dc..0ae04af92 100644
--- a/app/assets/stylesheets/components/links.scss
+++ b/app/assets/stylesheets/components/links.scss
@@ -92,7 +92,13 @@
.c-link--icon-left {
.c-link__icon {
- margin-right: var(--su-2);
- margin-left: calc(var(--su-1) * -1);
+ margin-right: var(--su-1);
+ }
+
+ &.c-link--block {
+ .c-link__icon {
+ margin-right: var(--su-2);
+ margin-left: calc(var(--su-1) * -1);
+ }
}
}
diff --git a/app/assets/stylesheets/config/_colors.scss b/app/assets/stylesheets/config/_colors.scss
index 068b27276..4d11e85b8 100644
--- a/app/assets/stylesheets/config/_colors.scss
+++ b/app/assets/stylesheets/config/_colors.scss
@@ -257,19 +257,25 @@
--btn-current-bg: var(--base-20);
--btn-current-color: var(--base-100);
- // Buttons: Default Destructive
- --btn-destructive-bg: transparent;
- --btn-destructive-bg-hover: var(--accent-danger-a10);
- --btn-destructive-color: var(--accent-danger);
- --btn-destructive-color-hover: var(--accent-danger-darker);
-
// Buttons: Primary
--btn-primary-bg: var(--accent-brand);
--btn-primary-bg-hover: var(--accent-brand-darker);
--btn-primary-color: var(--base-inverted);
--btn-primary-color-hover: var(--base-inverted);
- // Buttons: Primary Destructive
+ // Buttons: Secondary
+ --btn-secondary-bg: var(--accent-brand-a10);
+ --btn-secondary-bg-hover: var(--accent-brand);
+ --btn-secondary-color: var(--accent-brand);
+ --btn-secondary-color-hover: var(--base-inverted);
+
+ // Buttons: Destructive Default
+ --btn-destructive-bg: transparent;
+ --btn-destructive-bg-hover: var(--accent-danger-a10);
+ --btn-destructive-color: var(--accent-danger);
+ --btn-destructive-color-hover: var(--accent-danger-darker);
+
+ // Buttons: Destructive Primary
--btn-primary-destructive-bg: var(--accent-danger);
--btn-primary-destructive-bg-hover: var(--accent-danger-darker);
--btn-primary-destructive-color: var(--base-inverted);
diff --git a/app/javascript/crayons/Buttons/Button.jsx b/app/javascript/crayons/Buttons/Button.jsx
index 111f94482..0e7aa40d9 100644
--- a/app/javascript/crayons/Buttons/Button.jsx
+++ b/app/javascript/crayons/Buttons/Button.jsx
@@ -8,7 +8,7 @@ import { Icon } from '@crayons';
export const ButtonNew = (props) => {
const {
children,
- primary,
+ variant = 'default',
icon,
rounded,
destructive,
@@ -30,8 +30,8 @@ export const ButtonNew = (props) => {
};
const classes = classNames('c-btn', {
- 'c-btn--primary': primary,
- 'c-btn--destructive': destructive,
+ [`c-btn--${variant}`]: variant && variant !== 'default',
+ 'c-btn--destructive': destructive && variant !== 'secondary',
'c-btn--icon-left': icon && children,
'c-btn--icon-alone': icon && !children,
'crayons-tooltip__activator': tooltip,
@@ -73,7 +73,7 @@ ButtonNew.displayName = 'ButtonNew';
ButtonNew.propTypes = {
children: defaultChildrenPropTypes,
- primary: PropTypes.bool,
+ variant: PropTypes.oneOf(['default', 'primary', 'secondary']),
rounded: PropTypes.bool,
destructive: PropTypes.bool,
type: PropTypes.oneOf(['button', 'submit']),
diff --git a/app/javascript/crayons/Buttons/__stories__/Buttons.mdx b/app/javascript/crayons/Buttons/__stories__/Buttons.mdx
index ca2fb29a2..54046696b 100644
--- a/app/javascript/crayons/Buttons/__stories__/Buttons.mdx
+++ b/app/javascript/crayons/Buttons/__stories__/Buttons.mdx
@@ -14,35 +14,50 @@ That is: `` or `` should be used where navigation is elicited, an
For example, a 'Submit' control on 'Sign in' view (form) would suit a `` because its job is to submit the form.
However, 'Register' call-to-action—taking the user to a different form, in a different page or screen—would benefit from `` or `` semantics.
-Do not use `s` as navigational elements. Instead, use Links (or CTAs) when the desired action is to take the user to a new page.
+Do not use `s` as navigational elements. Instead, use `s` (or `s`) when the desired action is to take the user to a new page.
## Variants
-### Primary
-
-Primary button has special look: high contrast (against rest of the view/component) and it's filled with accent color.
-
-Ideally there should be only one Primary button per entire component, or sometimes even per entire view.
-Primary buttons should be used for the principal call to action on the page.
-
### Default
-Default variant is less prominent, and therefore can be used for less prominent actions.
-Default buttons are visually light (greyish) and you can have as many of them as you want (within common sense :D).
-They can (but don't have to) be used in conjunction with a Primary button. In that case, default variant should be used for any remaining actions.
+The _Default_ variant is not very prominent, and therefore can be used for less prominent actions.
+The _Default_ buttons are visually light (greyish) and you can have as many of them as you want (within common sense :D).
+They can (but don't have to) be used in conjunction with a _Primary_ button and less often with _Secondary_ buttons (use your common sense again or ping your designer).
+When combined with the _Primary_ or the _Secondary_, the _Default_ variant should be used for any remaining actions.
+
+Whenever used, make sure it has sufficient color contrast against the background it's used on.
+
+### Primary
+
+The _Primary_ variant has a special look: high contrast (against rest of the view/component) and it's filled with accent color.
+
+Ideally there should be only one _Primary_ button per entire component, or sometimes even per entire view.
+Primary buttons should be used for the principal action on the page.
+
+### Secondary
+
+The _Secondary_ variant is visually in between the _Primary_ and the _Default_ buttons. It's not as loud as the _Primary_ button but definitely stands out more than the _Default_ button.
+It's especially useful when you want your button to be more distinguishable from its surroundings without making it super loud (like _Primary_).
+A good example would be a component with lots of text. The _Default_ button could be potentially missed there (as it uses similar colors as regular texts).
+
+Whenever you have doubts between using the _Secondary_ vs the _Default_ variants, the rule of thumb should be:
+
+> Use the _Default_ variant by default :D unless it's not distinguishable enough from its surroundings.
+
+When combined with the _Primary_ button, the _Secondary_ variant should be used for any remaining or alternative actions.
## Configuration
### Destructive actions
For actions that could have destructive effects on the user’s data (for example, *delete* or *remove*) we have a special style.
-This style can be applied by using `destructive` prop and it will work for both: primary and default variants. Take care when using the default variant to ensure that the red text has sufficient color contrast against the background where you are using it.
+This style can be applied by using `destructive` prop and it will work with two variants: _Primary_ and _Default_ (as it doesn't really make sense to have _Secondary_ variant for destructive buttons).
### Icons
-Icon can be placed in front of a label to both clarify an action and call attention to a button.
+Icon can be placed in front of a label to both clarify an action but also to make button stand out more.
By passing an object in `icon` prop, there will be automatically included `` component.
-By default, the icon itself won't be customizable. Icons within buttons are hidden from assistive technology, so make sure your button has descriptive enough text, or is otherwise well-labelled (e.g. with `aria-label`).
+By default, the icon itself within `` is **not** customizable. Icons within buttons are hidden from assistive technology, so make sure your button has descriptive enough text, or is otherwise well-labelled (e.g. with `aria-label`).
#### Icon-only
diff --git a/app/javascript/crayons/Buttons/__stories__/Buttons.stories.jsx b/app/javascript/crayons/Buttons/__stories__/Buttons.stories.jsx
index ea64f34c9..653a0df14 100644
--- a/app/javascript/crayons/Buttons/__stories__/Buttons.stories.jsx
+++ b/app/javascript/crayons/Buttons/__stories__/Buttons.stories.jsx
@@ -12,11 +12,19 @@ export default {
},
},
argTypes: {
- primary: {
+ variant: {
+ control: {
+ type: 'select',
+ options: {
+ default: undefined,
+ primary: 'primary',
+ secondary: 'secondary',
+ },
+ },
description:
- 'This prop defines whether or not your button will have *primary* style (in practice: high contrast, filled with accent color). Keep in mind ideally there should be only one primary button per entire component, or sometimes even per entire view.',
+ 'There are three available variants (styles) to pick from: _default_, _primary_ and _secondary_. Please refer to the documentation to better understand the differences in usage.',
table: {
- defaultValue: { summary: false },
+ defaultValue: { summary: 'default' },
},
},
rounded: {
@@ -28,7 +36,7 @@ export default {
},
destructive: {
description:
- 'For various destructive actions we can have special styling for button which will add red-ish coloring.',
+ 'For various destructive actions we can have special styling for button which will add red-ish coloring. Keep in mind we only have two variants available for destructive button: _default_ and _primary_',
table: {
defaultValue: { summary: false },
},
@@ -56,7 +64,6 @@ export default {
export const Default = (args) => ;
Default.args = {
- primary: false,
destructive: false,
children: 'Button label',
tooltip: undefined,
@@ -66,7 +73,25 @@ Default.args = {
export const Primary = (args) => ;
Primary.args = {
...Default.args,
- primary: true,
+ variant: 'primary',
+};
+
+export const Secondary = (args) => ;
+Secondary.args = {
+ ...Default.args,
+ variant: 'secondary',
+};
+
+export const Destructive = (args) => ;
+Destructive.args = {
+ ...Default.args,
+ destructive: true,
+};
+
+export const WithTooltip = (args) => ;
+WithTooltip.args = {
+ ...Default.args,
+ tooltip: 'Hello world',
};
export const WithIcon = (args) => ;
@@ -74,3 +99,11 @@ WithIcon.args = {
...Default.args,
icon: CogIcon,
};
+
+export const IconOnly = (args) => ;
+IconOnly.args = {
+ ...Default.args,
+ icon: CogIcon,
+ tooltip: 'Button label',
+ children: undefined,
+};
diff --git a/app/javascript/crayons/Buttons/__tests__/Buttons.test.jsx b/app/javascript/crayons/Buttons/__tests__/Buttons.test.jsx
index 000bab7ab..9288739dc 100644
--- a/app/javascript/crayons/Buttons/__tests__/Buttons.test.jsx
+++ b/app/javascript/crayons/Buttons/__tests__/Buttons.test.jsx
@@ -13,9 +13,33 @@ describe('', () => {
expect(results).toHaveNoViolations();
});
+ it('has no accessibility errors in primary variant', async () => {
+ const { container } = render(
+ ,
+ );
+ const results = await axe(container);
+
+ expect(results).toHaveNoViolations();
+ });
+
+ it('has no accessibility errors in secondary variant', async () => {
+ const { container } = render(
+ ,
+ );
+ const results = await axe(container);
+
+ expect(results).toHaveNoViolations();
+ });
+
it('has no accessibility errors when props provided', async () => {
const { container } = render(
-