diff --git a/CHANGELOG.md b/CHANGELOG.md index 44e1bfcfa..197ac1485 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,16 @@ - Badge with Label, added an example showing a text label rendered next to a badge component, to the badge docs. - A new layout component at `atoms/switcher`, that lays out its children in a horizontal row with consistent spacing between children. The layout switches to a vertical stack once the width of the component passes below a threshold, or the number of children goes over a limit. - A new layout component at `atoms/stack`, that lays out its children vertically, with consistent spacing between children. +- Joined-ui now works with inputs — you can now join inputs together with other inputs, or inputs together with buttons. +- Inputs now use the global `:focus` outline, for improved keyboard navigation and visual accessibility. + +### Fixed + +- Default configuration for webfonts uses absolute values instead of CSS Custom Properties, so the font-weights work correctly again. + +### Changed + +- Updated visual appearance of inputs. If you weren’t overriding the default configuration, you don’t need to do anything to update. If you were overriding the configuration, be aware that there are now extra states for radio & checkboxes that you should override the colors for: active, checked-hover, checked-active, invalid, invalid-checked. You can now also set the box-shadow for each state. ## [[6.0.0]](https://github.com/bitcrowd/bitstyles/releases/tag/v6.0.0) - 2023-06-08 diff --git a/scss/bitstyles/atoms/button-colors/_index.scss b/scss/bitstyles/atoms/button-colors/_index.scss index b5a0fd322..93f7a8793 100644 --- a/scss/bitstyles/atoms/button-colors/_index.scss +++ b/scss/bitstyles/atoms/button-colors/_index.scss @@ -7,8 +7,16 @@ @use '../../tools/themes'; @use 'sass:map'; +/* prettier-ignore */ + @each $variant-name, $variant-themes in settings.$variants { $variant-default-theme: map.get($variant-themes, 'default'); + $button-name: ''; + @if $variant-name == '' { + $button-name: 'button--primary'; + } @else { + $button-name: 'button#{$variant-name}'; + } /* stylelint-disable custom-property-pattern, max-nesting-depth */ #{classname.get($classname-items: 'button#{$variant-name}', $layer: 'atom')} { @@ -16,8 +24,8 @@ &, &:visited { @each $property in button-settings.$allowed-color-properties { - #{$property}: var( - design-token.get('button#{$variant-name}', $property) + #{design-token.get('button', $property)}: var( + design-token.get($button-name, $property) ); } } @@ -27,8 +35,8 @@ &:hover, &:focus { @each $property in button-settings.$allowed-color-properties { - #{$property}: var( - design-token.get('button#{$variant-name}', 'hover', $property) + #{design-token.get('button', 'hover', $property)}: var( + design-token.get($button-name, 'hover', $property) ); } } @@ -37,8 +45,8 @@ @if map.has-key($variant-default-theme, 'active') { &:active { @each $property in button-settings.$allowed-color-properties { - #{$property}: var( - design-token.get('button#{$variant-name}', 'active', $property) + #{design-token.get('button', 'active', $property)}: var( + design-token.get($button-name, 'active', $property) ); } } @@ -50,8 +58,8 @@ &[aria-selected='true'], &[aria-current] { @each $property in button-settings.$allowed-color-properties { - #{$property}: var( - design-token.get('button#{$variant-name}', 'pressed', $property) + #{design-token.get('button', 'pressed', $property)}: var( + design-token.get($button-name, 'pressed', $property) ); } } @@ -65,8 +73,8 @@ &:disabled:focus, &[aria-disabled='true']:focus { @each $property in button-settings.$allowed-color-properties { - #{$property}: var( - design-token.get('button#{$variant-name}', 'disabled', $property) + #{design-token.get('button', 'disabled', $property)}: var( + design-token.get($button-name, 'disabled', $property) ); } } @@ -76,7 +84,7 @@ @each $theme-name, $theme in $variant-themes { @include themes.get($theme-name) { - @include button.colors($colors: $theme, $variant-name: $variant-name); + @include button.colors($colors: $theme, $variant-name: $button-name); } } } diff --git a/scss/bitstyles/atoms/button-shapes/_settings.scss b/scss/bitstyles/atoms/button-shapes/_settings.scss index 3a94abaae..839d9bb23 100644 --- a/scss/bitstyles/atoms/button-shapes/_settings.scss +++ b/scss/bitstyles/atoms/button-shapes/_settings.scss @@ -11,6 +11,7 @@ $default: ( 'min-width': 0, 'font-size': var(design-token.get('font-size', '2')), 'font-weight': var(design-token.get('font-weight', 'semibold')), + 'line-height': var(design-token.get('line-height', '4')), ) !default; /* stylelint-enable length-zero-no-unit */ $small: ( diff --git a/scss/bitstyles/atoms/button/_index.scss b/scss/bitstyles/atoms/button/_index.scss index 9a54be28d..e40589be4 100644 --- a/scss/bitstyles/atoms/button/_index.scss +++ b/scss/bitstyles/atoms/button/_index.scss @@ -13,8 +13,6 @@ #{design-token.get('button', 'border-top-left-radius')}: var(design-token.get('button', 'border-radius')); #{design-token.get('button', 'transition')}: settings.$transition; #{design-token.get('button', 'justify-content')}: settings.$justify-content; - #{design-token.get('button', 'transition')}: settings.$transition; - #{design-token.get('button', 'justify-content')}: settings.$justify-content; display: flex; position: relative; flex-shrink: 0; @@ -36,10 +34,15 @@ var(design-token.get('button', 'border-top-right-radius')) var(design-token.get('button', 'border-bottom-right-radius')) var(design-token.get('button', 'border-bottom-left-radius')); + border-color: var(design-token.get('button', 'border-color')); outline-offset: 0; + background-color: var(design-token.get('button', 'background-color')); + box-shadow: var(design-token.get('button', 'box-shadow')); + color: var(design-token.get('button', 'color')); font-family: settings.$font-family; - font-size: var(design-token.get('button', 'font-size')); - font-weight: var(design-token.get('button', 'font-weight')); + font-size: var(design-token.get('button', 'font-size'), inherit); + font-weight: var(design-token.get('button', 'font-weight'), inherit); + line-height: var(design-token.get('button', 'line-height'), inherit); text-align: center; text-decoration: none; white-space: nowrap; @@ -51,7 +54,21 @@ &:hover, &:focus { z-index: 1; + border-color: var(design-token.get('button', 'hover', 'border-color')); + outline-width: 0; + background-color: var(design-token.get('button', 'hover', 'background-color')); + box-shadow: var(design-token.get('button', 'hover', 'box-shadow')); + color: var(design-token.get('button', 'hover', 'color')); + text-decoration: none; + } + + &:active { + z-index: 1; + border-color: var(design-token.get('button', 'active', 'border-color')); outline-width: 0; + background-color: var(design-token.get('button', 'active', 'background-color')); + box-shadow: var(design-token.get('button', 'active', 'box-shadow')); + color: var(design-token.get('button', 'active', 'color')); text-decoration: none; } @@ -66,6 +83,10 @@ &[aria-selected='true'], &[aria-current] { z-index: 2; + border-color: var(design-token.get('button', 'pressed', 'border-color')); + background-color: var(design-token.get('button', 'pressed', 'background-color')); + box-shadow: var(design-token.get('button', 'pressed', 'box-shadow')); + color: var(design-token.get('button', 'pressed', 'color')); } &:disabled, @@ -74,6 +95,10 @@ &[aria-disabled='true']:hover, &:disabled:focus, &[aria-disabled='true']:focus { + border-color: var(design-token.get('button', 'disabled', 'border-color')); + background-color: var(design-token.get('button', 'disabled', 'background-color')); + box-shadow: var(design-token.get('button', 'disabled', 'box-shadow')); + color: var(design-token.get('button', 'disabled', 'color')); cursor: not-allowed; } } diff --git a/scss/bitstyles/atoms/button/_settings.scss b/scss/bitstyles/atoms/button/_settings.scss index e0269509f..d2d94e715 100644 --- a/scss/bitstyles/atoms/button/_settings.scss +++ b/scss/bitstyles/atoms/button/_settings.scss @@ -32,5 +32,6 @@ $allowed-shape-properties: ( 'min-width', 'font-size', 'font-weight', + 'line-height', 'justify-content' ) !default; diff --git a/scss/bitstyles/base/forms/Input.js b/scss/bitstyles/base/forms/Input.js new file mode 100644 index 000000000..26eba0c74 --- /dev/null +++ b/scss/bitstyles/base/forms/Input.js @@ -0,0 +1,19 @@ +export default ({ + value = '', + placeholder = '', + disabled = false, + ariaInvalid = false, + type = 'text', + checked = false, + id, +}) => { + const input = document.createElement('input'); + input.type = type; + input.value = value; + input.placeholder = placeholder; + if (disabled) input.setAttribute('disabled', disabled); + if (ariaInvalid) input.setAttribute('aria-invalid', ariaInvalid); + if (id) input.setAttribute('id', id); + if (checked) input.setAttribute('checked', checked); + return input; +}; diff --git a/scss/bitstyles/base/forms/Label.js b/scss/bitstyles/base/forms/Label.js new file mode 100644 index 000000000..26a0348eb --- /dev/null +++ b/scss/bitstyles/base/forms/Label.js @@ -0,0 +1,17 @@ +export default ({ + children, + htmlFor = null, + ariaInvalid = false, + ariaDisabled = false, + classnames = [], +}) => { + const label = document.createElement('label'); + children.forEach((child) => { + label.append(child); + }); + if (ariaInvalid) label.setAttribute('aria-invalid', ariaInvalid); + if (ariaDisabled) label.setAttribute('aria-disabled', ariaDisabled); + if (htmlFor) label.setAttribute('for', htmlFor); + classnames.forEach((classname) => label.classList.add(classname)); + return label; +}; diff --git a/scss/bitstyles/base/forms/Select.js b/scss/bitstyles/base/forms/Select.js new file mode 100644 index 000000000..78f9d526c --- /dev/null +++ b/scss/bitstyles/base/forms/Select.js @@ -0,0 +1,34 @@ +const defaultOptions = [ + { + value: 'blue', + label: 'Blue', + }, + { + value: 'red', + label: 'Red', + }, + { + value: 'green', + label: 'Green', + }, + { + value: 'pink', + label: 'Pink', + }, +]; + +export default ({ disabled = false, ariaInvalid = false, id, options }) => { + const select = document.createElement('select'); + if (disabled) select.setAttribute('disabled', disabled); + if (ariaInvalid) select.setAttribute('aria-invalid', ariaInvalid); + if (id) select.setAttribute('id', id); + + const opts = options || defaultOptions; + opts.forEach((option) => { + const opt = document.createElement('option'); + opt.value = option.value; + opt.append(option.label); + select.appendChild(opt); + }); + return select; +}; diff --git a/scss/bitstyles/base/forms/Textarea.js b/scss/bitstyles/base/forms/Textarea.js new file mode 100644 index 000000000..a661f7035 --- /dev/null +++ b/scss/bitstyles/base/forms/Textarea.js @@ -0,0 +1,17 @@ +export default ({ + value = '', + placeholder = '', + disabled = false, + ariaInvalid = false, + id, +}) => { + const textarea = document.createElement('textarea'); + textarea.value = value; + textarea.placeholder = placeholder; + textarea.setAttribute('rows', '5'); + textarea.setAttribute('cols', '30'); + if (disabled) textarea.setAttribute('disabled', disabled); + if (ariaInvalid) textarea.setAttribute('aria-invalid', ariaInvalid); + if (id) textarea.setAttribute('id', id); + return textarea; +}; diff --git a/scss/bitstyles/base/forms/_index.scss b/scss/bitstyles/base/forms/_index.scss index 556d95c6f..68a723758 100644 --- a/scss/bitstyles/base/forms/_index.scss +++ b/scss/bitstyles/base/forms/_index.scss @@ -20,9 +20,16 @@ legend { } label { - display: block; - margin-bottom: 0; + display: flex; + gap: settings.$label-gap; + align-items: baseline; + margin-bottom: settings.$label-gap; + font-weight: settings.$label-font-weight; cursor: pointer; + + &[aria-disabled='true'] { + color: settings.$label-disabled-color; + } } [type='color'] { diff --git a/scss/bitstyles/base/forms/_settings.scss b/scss/bitstyles/base/forms/_settings.scss index aada9c136..36f30a00d 100644 --- a/scss/bitstyles/base/forms/_settings.scss +++ b/scss/bitstyles/base/forms/_settings.scss @@ -1,3 +1,5 @@ +@use '../../tools/design-token'; + // // Fieldset //////////////////////////////////////// @@ -14,3 +16,10 @@ $legend-background-color: transparent !default; $legend-border: 0 !default; $legend-border-radius: 0 !default; $legend-color: inherit !default; + +// +// Label //////////////////////////////////////// + +$label-gap: var(design-token.get('size', 's7')) !default; +$label-font-weight: var(design-token.get('font-weight', 'medium')) !default; +$label-disabled-color: var(design-token.get('color', 'grayscale')) !default; diff --git a/scss/bitstyles/base/forms/forms.stories.mdx b/scss/bitstyles/base/forms/forms.stories.mdx index 455a6696e..80c36c2c5 100644 --- a/scss/bitstyles/base/forms/forms.stories.mdx +++ b/scss/bitstyles/base/forms/forms.stories.mdx @@ -1,174 +1,91 @@ import { Canvas, Meta, Story } from '@storybook/addon-docs'; - + -# Forms - -## Fieldset + - - - {` -
Fieldset
- `} -
-
+# Forms ## Inputs and labels Inputs can be siblings or children of their associated label, as you prefer. Different frameworks may favor different styles, but for most inputs it makes little difference in terms of semantics & usability. -The exception is for radios & checkboxes (see below), which should always be a child of their label to make the click/tap target larger. It’s therefore common practice to wrap all inputs this way, for consistsency. - -Take a look at [Base/Inputs](/docs/base-inputs--input-text) for a complete list of inputs. +The exception is for radios & checkboxes (see below), which should always be a child of their label to make the click/tap target larger. It’s therefore common practice to wrap all inputs this way, for consistency. ### Text inputs -This includes all standard `type="text"` inputs, plus all the text-based inputs such as `email`, and inputs which do not have a different representation on the current OS/browser combination. Right now that includes `tel` on desktop environments, but not mobile, and `date` on some desktop browsers but not others (and no mobile). +This includes all standard `type="text"` inputs, plus all the text-based inputs such as `email`, `password`, `tel` — take a look at the other stories in the forms section for examples of the other input types. + + + + + + + + + + + + + +## Textareas + +### No values - - {` - - - `} - - - {` - - `} - - - {` - - `} - - - {` - - `} - + + + + + +### With values + + + + + ## Checkboxes +### Unchecked + - - {` - - - `} - - - {` - - - `} - + + + + + +### Checked + + + + + ## Radios +### Unchecked + - - {` - - - `} - - - {` - - - `} - + + + -## Selects +### Checked - - {` - - - `} - - - {` - - - `} - - - {` - - - `} - + + + -## Textareas +## Selects - - {` - - `} - - - {` - - `} - + + + diff --git a/scss/bitstyles/base/forms/input-checkbox.stories.js b/scss/bitstyles/base/forms/input-checkbox.stories.js new file mode 100644 index 000000000..827214e35 --- /dev/null +++ b/scss/bitstyles/base/forms/input-checkbox.stories.js @@ -0,0 +1,111 @@ +import Input from './Input'; +import Label from './Label'; + +export default { + title: 'Base/Forms/Input checkbox', + component: Input, + subcomponents: { Label }, + argTypes: {}, +}; + +const Template = (args) => { + return Label({ + htmlFor: args.id, + ariaInvalid: args.ariaInvalid, + ariaDisabled: args.disabled, + children: [Input(args), args.label], + classname: 'u-items-baseline', + }); +}; + +// ***** Unchecked radios ****************** // + +export const Base = Template.bind({}); +Base.args = { type: 'checkbox', label: 'Unchecked' }; +Base.parameters = { + zeplinLink: [ + { + name: 'base', + link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fc96111d7d524003d54ff', + }, + { + name: 'hover', + link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fc97298cca5236f0332ce', + }, + { + name: 'active', + link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fc97028fa4823ffce07c8', + }, + ], +}; + +export const Invalid = Template.bind({}); +Invalid.args = { + type: 'checkbox', + ariaInvalid: true, + label: 'Unchecked invalid', +}; +Invalid.parameters = { + zeplinLink: + 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=645a598e4880c213a3197320', +}; + +export const Disabled = Template.bind({}); +Disabled.args = { + type: 'checkbox', + disabled: true, + label: 'Unchecked disabled', +}; +Disabled.parameters = { + zeplinLink: + 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fc971830548239007431f', +}; + +// ***** Checked radios ****************** // + +export const Checked = Template.bind({}); +Checked.args = { + type: 'checkbox', + checked: true, + label: 'Checked', +}; +Checked.parameters = { + zeplinLink: [ + { + name: 'base', + link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fc96362f9d023b1e5d965', + }, + { + name: 'hover', + link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fc964d04e11239e10ece0', + }, + { + name: 'active', + link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fc96842426023b99c98de', + }, + ], +}; + +export const CheckedInvalid = Template.bind({}); +CheckedInvalid.args = { + type: 'checkbox', + checked: true, + ariaInvalid: true, + label: 'Checked invalid', +}; +CheckedInvalid.parameters = { + zeplinLink: + 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=645a59a5d47c930dfb98d064', +}; + +export const CheckedDisabled = Template.bind({}); +CheckedDisabled.args = { + type: 'checkbox', + checked: true, + disabled: true, + label: 'Checked disabled', +}; +CheckedDisabled.parameters = { + zeplinLink: + 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fc96c28fa4823ffce078c', +}; diff --git a/scss/bitstyles/base/forms/input-other.stories.js b/scss/bitstyles/base/forms/input-other.stories.js new file mode 100644 index 000000000..d52fbcc72 --- /dev/null +++ b/scss/bitstyles/base/forms/input-other.stories.js @@ -0,0 +1,105 @@ +import Input from './Input'; + +export default { + title: 'Base/Forms/Input other', + component: Input, + argTypes: {}, +}; + +const Template = (args) => Input(args); + +// ***** Text inputs with values ****************** // + +export const Button = Template.bind({}); +Button.args = { type: 'button', value: 'Button input' }; + +export const ButtonDisabled = Template.bind({}); +ButtonDisabled.args = { + type: 'button', + value: 'Button input disabled', + disabled: true, +}; + +export const Color = Template.bind({}); +Color.args = { type: 'color' }; + +export const ColorDisabled = Template.bind({}); +ColorDisabled.args = { type: 'color', disabled: true }; + +export const Date = Template.bind({}); +Date.args = { type: 'date' }; + +export const DateDisabled = Template.bind({}); +DateDisabled.args = { type: 'date', disabled: true }; + +export const DatetimeLocal = Template.bind({}); +DatetimeLocal.args = { type: 'datetime-local' }; + +export const DatetimeLocalDisabled = Template.bind({}); +DatetimeLocalDisabled.args = { type: 'datetime-local', disabled: true }; + +export const File = Template.bind({}); +File.args = { type: 'file' }; + +export const FileDisabled = Template.bind({}); +FileDisabled.args = { type: 'file', disabled: true }; + +export const Image = Template.bind({}); +Image.args = { type: 'image' }; + +export const ImageDisabled = Template.bind({}); +ImageDisabled.args = { type: 'image', disabled: true }; + +export const Month = Template.bind({}); +Month.args = { type: 'month' }; + +export const MonthDisabled = Template.bind({}); +MonthDisabled.args = { type: 'month', disabled: true }; + +export const Week = Template.bind({}); +Week.args = { type: 'week' }; + +export const WeekDisabled = Template.bind({}); +WeekDisabled.args = { type: 'week', disabled: true }; + +export const Password = Template.bind({}); +Password.args = { type: 'password' }; + +export const PasswordDisabled = Template.bind({}); +PasswordDisabled.args = { type: 'password', disabled: true }; + +export const Reset = Template.bind({}); +Reset.args = { type: 'reset' }; + +export const ResetDisabled = Template.bind({}); +ResetDisabled.args = { type: 'reset', disabled: true }; + +export const Search = Template.bind({}); +Search.args = { type: 'search' }; + +export const SearchDisabled = Template.bind({}); +SearchDisabled.args = { type: 'search', disabled: true }; + +export const Submit = Template.bind({}); +Submit.args = { type: 'submit', value: 'Submit input' }; + +export const SubmitDisabled = Template.bind({}); +SubmitDisabled.args = { type: 'submit', value: 'Submit input', disabled: true }; + +export const Tel = Template.bind({}); +Tel.args = { type: 'tel' }; + +export const TelDisabled = Template.bind({}); +TelDisabled.args = { type: 'tel', disabled: true }; + +export const Time = Template.bind({}); +Time.args = { type: 'time' }; + +export const TimeDisabled = Template.bind({}); +TimeDisabled.args = { type: 'time', disabled: true }; + +export const Url = Template.bind({}); +Url.args = { type: 'url' }; + +export const UrlDisabled = Template.bind({}); +UrlDisabled.args = { type: 'url', disabled: true }; diff --git a/scss/bitstyles/base/forms/input-radio.stories.js b/scss/bitstyles/base/forms/input-radio.stories.js new file mode 100644 index 000000000..570ad8ce8 --- /dev/null +++ b/scss/bitstyles/base/forms/input-radio.stories.js @@ -0,0 +1,99 @@ +import Input from './Input'; +import Label from './Label'; + +export default { + title: 'Base/Forms/Input radio', + component: Input, + subcomponents: { Label }, + argTypes: {}, +}; + +const Template = (args) => { + return Label({ + htmlFor: args.id, + ariaInvalid: args.ariaInvalid, + ariaDisabled: args.disabled, + children: [Input(args), args.label], + classname: 'u-items-baseline', + }); +}; + +// ***** Unchecked radios ****************** // + +export const Base = Template.bind({}); +Base.args = { type: 'radio', label: 'Unchecked' }; +Base.parameters = { + zeplinLink: [ + { + name: 'base', + link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fc2b5d50be32376c68fcd', + }, + { + name: 'hover', + link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fc2b95a65082385f940c1', + }, + { + name: 'active', + link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fc2bedf599923e9f4e22d', + }, + ], +}; + +export const Invalid = Template.bind({}); +Invalid.args = { type: 'radio', ariaInvalid: true, label: 'Unchecked invalid' }; +Invalid.parameters = { + zeplinLink: + 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=645a5888f64b970e42359dbc', +}; + +export const Disabled = Template.bind({}); +Disabled.args = { type: 'radio', disabled: true, label: 'Unchecked disabled' }; +Disabled.parameters = { + zeplinLink: + 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fc2bfc40d7123d9858e06', +}; + +// ***** Checked radios ****************** // + +export const Checked = Template.bind({}); +Checked.args = { type: 'radio', checked: true, label: 'Checked' }; +Checked.parameters = { + zeplinLink: [ + { + name: 'base', + link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fc2bd35ff5623d67134f9', + }, + { + name: 'hover', + link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fc2b76d8d3923c74e7dc1', + }, + { + name: 'active', + link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fc2b628fa4823ffcd6e3f', + }, + ], +}; + +export const CheckedInvalid = Template.bind({}); +CheckedInvalid.args = { + type: 'radio', + checked: true, + ariaInvalid: true, + label: 'Checked invalid', +}; +CheckedInvalid.parameters = { + zeplinLink: + 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=645a58805b9d821337cb7b6c', +}; + +export const CheckedDisabled = Template.bind({}); +CheckedDisabled.args = { + type: 'radio', + checked: true, + disabled: true, + label: 'Checked disabled', +}; +CheckedDisabled.parameters = { + zeplinLink: + 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fc2ba62f9d023b1e53c19', +}; diff --git a/scss/bitstyles/base/forms/input-text.stories.js b/scss/bitstyles/base/forms/input-text.stories.js new file mode 100644 index 000000000..b250c203c --- /dev/null +++ b/scss/bitstyles/base/forms/input-text.stories.js @@ -0,0 +1,281 @@ +import Input from './Input'; +import Label from './Label'; + +export default { + title: 'Base/Forms/Input text', + component: Input, + subcomponents: { Label }, + argTypes: {}, +}; + +const Template = (args) => { + const wrapper = new DocumentFragment(); + const label = Label({ + htmlFor: args.id, + ariaInvalid: args.ariaInvalid, + ariaDisabled: args.disabled, + children: [args.label], + }); + const input = Input(args); + wrapper.append(label); + wrapper.append(input); + return wrapper; +}; + +// ***** Text inputs with values ****************** // + +export const TextWithValue = Template.bind({}); +TextWithValue.args = { + value: 'Input text with value', + placeholder: 'Input text placeholder', + label: 'Input text with value', +}; +TextWithValue.parameters = { + zeplinLink: [ + { + name: 'base', + link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fbb9d8f3dc7239794dccd', + }, + { + name: 'hover', + link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fbba235ff5623d67079eb', + }, + { + name: 'active', + link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fbb9e62f9d023b1e475f2', + }, + ], +}; + +export const TextWithValueInvalid = Template.bind({}); +TextWithValueInvalid.args = { + value: 'Input text with value invalid', + placeholder: 'Input text invalid placeholder', + ariaInvalid: true, + label: 'Input text with value invalid', +}; +TextWithValueInvalid.parameters = { + zeplinLink: + 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fbba098cca5236f016f66', +}; + +export const TextWithValueDisabled = Template.bind({}); +TextWithValueDisabled.args = { + value: 'Input text with value disabled', + placeholder: 'Input text disabled placeholder', + disabled: true, + label: 'Input text with value disabled', +}; +TextWithValueDisabled.parameters = { + zeplinLink: + 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=645259551dc67b3c0c0f8634', +}; + +// ***** Text inputs without values ****************** // + +export const TextEmpty = Template.bind({}); +TextEmpty.args = { + value: '', + placeholder: 'Input text empty placeholder', + label: 'Input text empty', +}; +TextEmpty.parameters = { + zeplinLink: [ + { + name: 'base', + link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fbb9c4fed4a236a1c340c', + }, + { + name: 'hover', + link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fbba7ebc1c823de19d9b4', + }, + { + name: 'active', + link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fbba6122e2e235f235223', + }, + ], +}; + +export const TextEmptyInvalid = Template.bind({}); +TextEmptyInvalid.args = { + value: '', + placeholder: 'Input text empty invalid placeholder', + label: 'Input text empty invalid', + ariaInvalid: true, +}; +TextEmptyInvalid.parameters = { + zeplinLink: + 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fbba425cd9623cef856c0', +}; + +export const TextEmptyDisabled = Template.bind({}); +TextEmptyDisabled.args = { + value: '', + placeholder: 'Input text empty disabled placeholder', + label: 'Input text empty disabled', + disabled: true, +}; +TextEmptyDisabled.parameters = { + zeplinLink: + 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fbb9ff25cd023ae394942', +}; + +// ***** Email inputs with values ****************** // + +export const EmailWithValue = Template.bind({}); +EmailWithValue.args = { + type: 'email', + value: 'Input@email.base', + placeholder: 'Input email placeholder', + label: 'Input email with value', +}; +EmailWithValue.parameters = { + zeplinLink: [ + { + name: 'base', + link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fbb9c4fed4a236a1c340c', + }, + { + name: 'hover', + link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fbba7ebc1c823de19d9b4', + }, + { + name: 'active', + link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fbba6122e2e235f235223', + }, + ], +}; + +export const EmailWithValueInvalid = Template.bind({}); +EmailWithValueInvalid.args = { + type: 'email', + value: 'Input@email.invalid', + placeholder: 'Input email with value invalid placeholder', + label: 'Input email with value invalid', + ariaInvalid: true, +}; +EmailWithValueInvalid.parameters = { + zeplinLink: + 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fbba098cca5236f016f66', +}; + +export const EmailWithValueDisabled = Template.bind({}); +EmailWithValueDisabled.args = { + type: 'email', + value: 'Input@email.disabled', + placeholder: 'Input email with value disabled placeholder', + label: 'Input email with value disabled', + disabled: true, +}; +EmailWithValueDisabled.parameters = { + zeplinLink: + 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=645259551dc67b3c0c0f8634', +}; + +// ***** Email inputs without values ****************** // + +export const EmailEmpty = Template.bind({}); +EmailEmpty.args = { + type: 'email', + value: '', + placeholder: 'Input email empty placeholder', + label: 'Input email empty', +}; +EmailEmpty.parameters = { + zeplinLink: [ + { + name: 'base', + link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fbb9c4fed4a236a1c340c', + }, + { + name: 'hover', + link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fbba7ebc1c823de19d9b4', + }, + { + name: 'active', + link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fbba6122e2e235f235223', + }, + ], +}; + +export const EmailInvalidEmpty = Template.bind({}); +EmailInvalidEmpty.args = { + type: 'email', + value: '', + placeholder: 'Input email empty invalid placeholder', + label: 'Input email empty invalid', + ariaInvalid: true, +}; +EmailInvalidEmpty.parameters = { + zeplinLink: + 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fbba425cd9623cef856c0', +}; + +export const EmailDisabledEmpty = Template.bind({}); +EmailDisabledEmpty.args = { + type: 'email', + value: '', + placeholder: 'Input email empty disabled placeholder', + label: 'Input email empty disabled', + disabled: true, +}; +EmailDisabledEmpty.parameters = { + zeplinLink: + 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fbb9ff25cd023ae394942', +}; + +// ***** Number inputs with values ****************** // + +export const NumberWithValue = Template.bind({}); +NumberWithValue.args = { + type: 'number', + value: '012345', + placeholder: 'Input number placeholder', + label: 'Input number with value', +}; + +export const NumberWithValueInvalid = Template.bind({}); +NumberWithValueInvalid.args = { + type: 'number', + value: 'Input email invalid', + placeholder: 'Input number invalid placeholder', + label: 'Input number with value invalid', + ariaInvalid: true, +}; + +export const NumberWithValueDisabled = Template.bind({}); +NumberWithValueDisabled.args = { + type: 'number', + value: '012345', + placeholder: 'Input number disabled placeholder', + label: 'Input number with value disabled', + disabled: true, +}; + +// ***** Number inputs without values ****************** // + +export const NumberEmpty = Template.bind({}); +NumberEmpty.args = { + type: 'number', + value: '', + placeholder: 'Input number placeholder', + label: 'Input number empty', +}; + +export const NumberEmptyInvalid = Template.bind({}); +NumberEmptyInvalid.args = { + type: 'number', + value: '', + placeholder: 'Input number invalid placeholder', + label: 'Input number empty invalid', + ariaInvalid: true, +}; + +export const NumberEmptyDisabled = Template.bind({}); +NumberEmptyDisabled.args = { + type: 'number', + value: '', + placeholder: 'Input number disabled placeholder', + label: 'Input number empty disabled', + disabled: true, +}; diff --git a/scss/bitstyles/base/forms/inputs.stories.mdx b/scss/bitstyles/base/forms/inputs.stories.mdx deleted file mode 100644 index af09c8afb..000000000 --- a/scss/bitstyles/base/forms/inputs.stories.mdx +++ /dev/null @@ -1,238 +0,0 @@ -import { Canvas, Meta, Story } from '@storybook/addon-docs'; - - - -## Label - - - - {` - - `} - - - -## Input - button - - - - {` - - `} - - - {` - - `} - - - -## Input - color - - - - {` - - `} - - - {` - - `} - - - -## Input - date - - - - {` - - `} - - - {` - - `} - - - -## Input - datetime-local - - - - {` - - `} - - - {` - - `} - - - -## Input - file - - - - {` - - `} - - - {` - - `} - - - -## Input - image - - - - {` - - `} - - - {` - - `} - - - -## Input - month - - - - {` - - `} - - - {` - - `} - - - -## Input - week - - - - {` - - `} - - - {` - - `} - - - -## Input - password - - - - {` - - `} - - - {` - - `} - - - -## Input - reset - - - - {` - - `} - - - {` - - `} - - - -## Input - search - - - - {` - - `} - - - {` - - `} - - - -## Input - submit - - - - {` - - `} - - - {` - - `} - - - -## Input - tel - - - - {` - - `} - - - {` - - `} - - - -## Input - time - - - - {` - - `} - - - {` - - `} - - - -## Input - url - - - - {` - - `} - - - {` - - `} - - diff --git a/scss/bitstyles/base/forms/label.stories.js b/scss/bitstyles/base/forms/label.stories.js new file mode 100644 index 000000000..0c8dfcb40 --- /dev/null +++ b/scss/bitstyles/base/forms/label.stories.js @@ -0,0 +1,39 @@ +import Label from './Label'; + +export default { + title: 'Base/Forms/Label', + component: Label, + argTypes: {}, +}; + +const Template = (args) => Label(args); + +const Link = () => { + const link = document.createElement('a'); + link.href = '#dummy-link'; + link.innerHTML = 'a link'; + return link; +}; + +// ***** Labels ****************** // + +export const Invalid = Template.bind({}); +Invalid.args = { + children: ['Simple text label for an invalid input'], + ariaInvalid: true, +}; + +export const Disabled = Template.bind({}); +Disabled.args = { + children: ['Simple text label for a disabled input'], + ariaDisabled: true, +}; + +export const WithFor = Template.bind({}); +WithFor.args = { + children: ['Simple text label with a for attribute'], + htmlFor: 'input-id', +}; + +export const WithLink = Template.bind({}); +WithLink.args = { children: ['Text label with ', Link(), ' as content.'] }; diff --git a/scss/bitstyles/base/forms/select.stories.js b/scss/bitstyles/base/forms/select.stories.js new file mode 100644 index 000000000..821a2db7e --- /dev/null +++ b/scss/bitstyles/base/forms/select.stories.js @@ -0,0 +1,62 @@ +import Label from './Label'; +import Select from './Select'; + +export default { + title: 'Base/Forms/Select', + component: Select, + argTypes: {}, +}; + +const Template = (args) => { + const wrapper = new DocumentFragment(); + wrapper.append( + Label({ + htmlFor: args.id, + ariaInvalid: args.ariaInvalid, + children: [args.label], + }) + ); + wrapper.append(Select(args)); + return wrapper; +}; + +export const Base = Template.bind({}); +Base.args = { + label: 'Select', +}; +Base.parameters = { + zeplinLink: [ + { + name: 'base', + link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=6447aae722a9183fc59d4449', + }, + { + name: 'hover', + link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=6447aae838a4ce3f997a0b0b', + }, + { + name: 'active', + link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=6447aaeae8e2b0449814cad5', + }, + ], +}; +Base.parameters = { + zeplinLink: + 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=6447aae722a9183fc59d4449', +}; + +export const Invalid = Template.bind({}); +Invalid.args = { + label: 'Select invalid', + ariaInvalid: true, +}; +Invalid.parameters = { + zeplinLink: + 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=646f20300fe8d32205780813', +}; + +export const Disabled = Template.bind({}); +Disabled.args = { + label: 'Select disabled', + disabled: true, +}; diff --git a/scss/bitstyles/base/forms/textarea.stories.js b/scss/bitstyles/base/forms/textarea.stories.js new file mode 100644 index 000000000..d22e80b6b --- /dev/null +++ b/scss/bitstyles/base/forms/textarea.stories.js @@ -0,0 +1,83 @@ +import Label from './Label'; +import Textarea from './Textarea'; + +export default { + title: 'Base/Forms/Textarea', + component: Textarea, + argTypes: {}, +}; + +const Template = (args) => { + const wrapper = new DocumentFragment(); + wrapper.append( + Label({ + htmlFor: args.id, + ariaInvalid: args.ariaInvalid, + children: [args.label], + }) + ); + wrapper.append(Textarea(args)); + return wrapper; +}; + +// ***** Textareas with values ****************** // + +export const WithValue = Template.bind({}); +WithValue.args = { + value: 'Textarea with value', + placeholder: 'Textarea with value placeholder', + label: 'Textarea with value', +}; +WithValue.parameters = { + zeplinLink: + 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=6447b1c78993dd3fe66afe6f', +}; + +export const WithValueInvalid = Template.bind({}); +WithValueInvalid.args = { + value: 'Textarea', + ariaInvalid: true, + placeholder: 'Textarea with value invalid placeholder', + label: 'Textarea with value invalid', +}; +WithValueInvalid.parameters = { + zeplinLink: + 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=645a593bd47c930dfb98ccc0', +}; + +export const WithValueDisabled = Template.bind({}); +WithValueDisabled.args = { + value: 'Textarea with value disabled', + disabled: true, + placeholder: 'Textarea with value disabled placeholder', + label: 'Textarea with value disabled', +}; + +// ***** Textareas without values ****************** // + +export const Empty = Template.bind({}); +Empty.args = { + value: '', + placeholder: 'Textarea empty placeholder', + label: 'Textarea empty', +}; +Empty.parameters = { + zeplinLink: + 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=6447b1c473b2c445226d25da', +}; + +export const EmptyInvalid = Template.bind({}); +EmptyInvalid.args = { + value: '', + ariaInvalid: true, + placeholder: 'Textarea empty invalid placeholder', + label: 'Textarea empty invalid', +}; + +export const EmptyDisabled = Template.bind({}); +EmptyDisabled.args = { + value: '', + disabled: true, + placeholder: 'Textarea empty disabled placeholder', + label: 'Textarea empty disabled', +}; diff --git a/scss/bitstyles/base/input-checkbox/_index.scss b/scss/bitstyles/base/input-checkbox/_index.scss index 16cee3d0c..9464b8bde 100644 --- a/scss/bitstyles/base/input-checkbox/_index.scss +++ b/scss/bitstyles/base/input-checkbox/_index.scss @@ -1,16 +1,17 @@ @forward 'settings'; @use './settings'; +@use '../../design-tokens/focus'; @use '../../tools/design-token'; [type='checkbox'] { - display: inline-block; position: relative; + top: calc(settings.$gap / 2); flex-shrink: 0; width: 1em; height: 1em; - margin-right: settings.$gap; + margin: settings.$gap; + outline-offset: 0; font-size: settings.$size; - vertical-align: middle; cursor: pointer; appearance: none; } @@ -21,16 +22,30 @@ border: settings.$border-width solid settings.$border-color; border-radius: settings.$border-radius; background-color: settings.$background-color; + box-shadow: settings.$box-shadow; color: settings.$color; &:hover, &:focus { border-color: settings.$border-color-hover; - outline: none; + outline-width: 0; background-color: settings.$background-color-hover; + box-shadow: settings.$box-shadow-hover; color: settings.$color-hover; } + &:focus-visible { + outline: focus.$outline-color solid focus.$outline-width; + outline-offset: focus.$outline-offset; + } + + &:active { + border-color: settings.$border-color-active; + background-color: settings.$background-color-active; + box-shadow: settings.$box-shadow-active; + color: settings.$color-active; + } + &::after { content: ''; display: block; @@ -42,6 +57,10 @@ var(design-token.get('transition', 'default', 'duration')) var(design-token.get('transition', 'default', 'easing')), transform + calc(var(design-token.get('transition', 'default', 'duration')) / 2) + var(design-token.get('transition', 'default', 'duration')) + var(design-token.get('transition', 'default', 'easing')), + outline-offset calc(var(design-token.get('transition', 'default', 'duration')) / 2) var(design-token.get('transition', 'default', 'duration')) var(design-token.get('transition', 'default', 'easing')); @@ -55,6 +74,7 @@ &:checked { border-color: settings.$border-color-checked; background-color: settings.$background-color-checked; + box-shadow: settings.$box-shadow-checked; color: settings.$color-checked; &::after { @@ -63,16 +83,51 @@ } } + &:invalid, + &[aria-invalid='true'] { + border-color: settings.$border-color-invalid; + background-color: settings.$background-color-invalid; + box-shadow: settings.$box-shadow-invalid; + color: settings.$color-invalid; + + &:checked { + border-color: settings.$border-color-invalid-checked; + background-color: settings.$background-color-invalid-checked; + box-shadow: settings.$box-shadow-invalid-checked; + color: settings.$color-invalid-checked; + } + } + &[disabled] { border-color: settings.$border-color-disabled; background-color: settings.$background-color-disabled; + box-shadow: settings.$box-shadow-disabled; color: settings.$color-disabled; &:checked { border-color: settings.$border-color-disabled-checked; background-color: settings.$background-color-disabled-checked; + box-shadow: settings.$box-shadow-disabled-checked; color: settings.$color-disabled-checked; } } + + &:checked:hover:not(:invalid), + &:checked:focus:not(:invalid), + &:checked:hover:not([aria-invalid='true']), + &:checked:focus:not([aria-invalid='true']) { + border-color: settings.$border-color-checked-hover; + background-color: settings.$background-color-checked-hover; + box-shadow: settings.$box-shadow-checked-hover; + color: settings.$color-checked-hover; + } + + &:checked:active:not(:invalid), + &:checked:active:not([aria-invalid='true']) { + border-color: settings.$border-color-checked-active; + background-color: settings.$background-color-checked-active; + box-shadow: settings.$box-shadow-checked-active; + color: settings.$color-checked-active; + } } } diff --git a/scss/bitstyles/base/input-checkbox/_settings.scss b/scss/bitstyles/base/input-checkbox/_settings.scss index 1c853a135..f74a82898 100644 --- a/scss/bitstyles/base/input-checkbox/_settings.scss +++ b/scss/bitstyles/base/input-checkbox/_settings.scss @@ -1,12 +1,12 @@ -@use '../../tools/design-token'; @use '../../tools/color'; +@use '../../tools/design-token'; // // Base styles //////////////////////////////////////// $border-radius: var(design-token.get('size', 's6')) !default; $gap: var(design-token.get('size', 's4')) !default; -$border-width: var(design-token.get('size', 's7')) !default; -$size: var(design-token.get('size', 'm')) !default; +$border-width: 0.1875rem !default; +$size: var(design-token.get('size', 'l2')) !default; // // Base colors //////////////////////////////////////// @@ -14,7 +14,8 @@ $color: var(design-token.get('color', 'grayscale')) !default; $background-color: var( design-token.get('color', 'grayscale', 'white') ) !default; -$border-color: var(design-token.get('color', 'grayscale')) !default; +$border-color: var(design-token.get('color', 'grayscale', 'dark-2')) !default; +$box-shadow: none !default; // // Hover colors //////////////////////////////////////// @@ -23,13 +24,84 @@ $background-color-hover: var( design-token.get('color', 'grayscale', 'white') ) !default; $border-color-hover: var(design-token.get('color', 'brand-1')) !default; +$box-shadow-hover: var(design-token.get('shadow', 'brand-1')) !default; + +// +// Active colors //////////////////////////////////////// +$color-active: var(design-token.get('color', 'brand-1', 'dark-1')) !default; +$background-color-active: var( + design-token.get('color', 'grayscale', 'white') +) !default; +$border-color-active: var( + design-token.get('color', 'brand-1', 'dark-1') +) !default; +$box-shadow-active: none !default; // // Checked colors //////////////////////////////////////// $color-checked: var(design-token.get('color', 'grayscale', 'white')) !default; -$background-color-checked: var(design-token.get('color', 'brand-1')) !default; -$border-color-checked: var(design-token.get('color', 'brand-1')) !default; +$background-color-checked: var( + design-token.get('color', 'brand-1', 'dark-1') +) !default; +$border-color-checked: var( + design-token.get('color', 'brand-1', 'dark-1') +) !default; $background-image-checked: url("data:image/svg+xml,%3Csvg width='20' height='20' viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='#{color.url-encode-color(#ffffff)}' d='m83.07 11.71-44.41 44.59-21.73-21.81-15.93 15.99 37.65 37.81 60.35-60.59z'/%3E%3C/svg%3E") !default; +$box-shadow-checked: var(design-token.get('shadow', 'brand-1', 'box')) !default; + +// +// Checked hover colors //////////////////////////////////////// +$color-checked-hover: var( + design-token.get('color', 'grayscale', 'white') +) !default; +$background-color-checked-hover: var( + design-token.get('color', 'brand-1') +) !default; +$border-color-checked-hover: var(design-token.get('color', 'brand-1')) !default; +$background-image-checked-hover: url("data:image/svg+xml,%3Csvg width='20' height='20' viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='#{color.url-encode-color(#ffffff)}' d='m83.07 11.71-44.41 44.59-21.73-21.81-15.93 15.99 37.65 37.81 60.35-60.59z'/%3E%3C/svg%3E") !default; +$box-shadow-checked-hover: none !default; + +// +// Checked active colors //////////////////////////////////////// +$color-checked-active: var( + design-token.get('color', 'grayscale', 'white') +) !default; +$background-color-checked-active: var( + design-token.get('color', 'brand-1') +) !default; +$border-color-checked-active: var( + design-token.get('color', 'brand-1', 'dark-1') +) !default; +$background-image-checked-active: url("data:image/svg+xml,%3Csvg width='20' height='20' viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='var(#{design-token.get('color', 'grayscale', 'white')})' d='m83.07 11.71-44.41 44.59-21.73-21.81-15.93 15.99 37.65 37.81 60.35-60.59z'/%3E%3C/svg%3E") !default; +$box-shadow-checked-active: none !default; + +// +// Invalid colors //////////////////////////////////////// +$color-invalid: var(design-token.get('color', 'grayscale', 'white')) !default; +$background-color-invalid: var( + design-token.get('color', 'grayscale', 'white') +) !default; +$border-color-invalid: var( + design-token.get('color', 'warning', 'dark-1') +) !default; +$background-image-invalid: none !default; +$box-shadow-invalid: none !default; + +// +// Invalid checked colors //////////////////////////////////////// +$color-invalid-checked: var( + design-token.get('color', 'grayscale', 'white') +) !default; +$background-color-invalid-checked: var( + design-token.get('color', 'warning', 'dark-1') +) !default; +$border-color-invalid-checked: var( + design-token.get('color', 'warning', 'dark-1') +) !default; +$background-image-invalid-checked: url("data:image/svg+xml,%3Csvg width='20' height='20' viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='var(#{design-token.get('color', 'grayscale', 'white')})' d='m83.07 11.71-44.41 44.59-21.73-21.81-15.93 15.99 37.65 37.81 60.35-60.59z'/%3E%3C/svg%3E") !default; +$box-shadow-invalid-checked: var( + design-token.get('shadow', 'warning', 'box') +) !default; // // Disabled colors //////////////////////////////////////// @@ -37,20 +109,22 @@ $color-disabled: var( design-token.get('color', 'grayscale', 'light-2') ) !default; $background-color-disabled: var( - design-token.get('color', 'grayscale', 'light-2') + design-token.get('color', 'grayscale', 'white') ) !default; $border-color-disabled: var( - design-token.get('color', 'grayscale', 'light-2') + design-token.get('color', 'grayscale', 'light-1') ) !default; +$box-shadow-disabled: none !default; // // Disabled-checked colors //////////////////////////////////////// $color-disabled-checked: var( - design-token.get('color', 'grayscale', 'light-1') + design-token.get('color', 'grayscale', 'white') ) !default; $background-color-disabled-checked: var( - design-token.get('color', 'grayscale', 'light-2') + design-token.get('color', 'grayscale', 'light-1') ) !default; $border-color-disabled-checked: var( design-token.get('color', 'grayscale', 'light-1') ) !default; +$box-shadow-disabled-checked: none !default; diff --git a/scss/bitstyles/base/input-checkbox/input-checkbox.stories.mdx b/scss/bitstyles/base/input-checkbox/input-checkbox.stories.mdx deleted file mode 100644 index e941b8d7c..000000000 --- a/scss/bitstyles/base/input-checkbox/input-checkbox.stories.mdx +++ /dev/null @@ -1,28 +0,0 @@ -import { Canvas, Meta, Story } from '@storybook/addon-docs'; - - - -# Input - checkbox - - - - {` - - `} - - - {` - - `} - - - {` - - `} - - - {` - - `} - - diff --git a/scss/bitstyles/base/input-radio/_index.scss b/scss/bitstyles/base/input-radio/_index.scss index c8378a8c8..64ad00db9 100644 --- a/scss/bitstyles/base/input-radio/_index.scss +++ b/scss/bitstyles/base/input-radio/_index.scss @@ -1,16 +1,17 @@ @forward 'settings'; @use './settings'; @use '../../tools/design-token'; +@use '../../design-tokens/focus'; [type='radio'] { - display: inline-block; position: relative; + top: calc(settings.$gap / 2); flex-shrink: 0; width: 1em; height: 1em; - margin-right: settings.$gap; + margin: settings.$gap; + outline-offset: 0; font-size: settings.$size; - vertical-align: middle; cursor: pointer; appearance: none; } @@ -21,6 +22,7 @@ border: settings.$border-width solid settings.$border-color; border-radius: settings.$border-radius; background-color: settings.$background-color; + box-shadow: settings.$box-shadow; color: settings.$color; &::after { @@ -33,6 +35,9 @@ var(design-token.get('transition', 'default', 'duration')) var(design-token.get('transition', 'default', 'easing')), transform var(design-token.get('transition', 'default', 'duration')) + var(design-token.get('transition', 'default', 'easing')), + outline-offset + var(design-token.get('transition', 'default', 'duration')) var(design-token.get('transition', 'default', 'easing')); border-radius: settings.$border-radius; opacity: 0; @@ -42,14 +47,28 @@ &:hover, &:focus { border-color: settings.$border-color-hover; - outline: none; + outline-width: 0; background-color: settings.$background-color-hover; + box-shadow: settings.$box-shadow-hover; color: settings.$color-hover; } + &:focus-visible { + outline: focus.$outline-color solid focus.$outline-width; + outline-offset: focus.$outline-offset; + } + + &:active { + border-color: settings.$border-color-active; + background-color: settings.$background-color-active; + box-shadow: settings.$box-shadow-active; + color: settings.$color-active; + } + &:checked { border-color: settings.$border-color-checked; background-color: settings.$background-color-checked; + box-shadow: settings.$box-shadow-checked; color: settings.$color-checked; &::after { @@ -58,14 +77,41 @@ } } + &:invalid, + &[aria-invalid='true'] { + border-color: settings.$border-color-invalid; + background-color: settings.$background-color-invalid; + box-shadow: settings.$box-shadow-invalid; + color: settings.$color-invalid; + + &::after { + background: settings.$color-invalid; + } + + &:checked { + border-color: settings.$border-color-invalid-checked; + background-color: settings.$background-color-invalid-checked; + box-shadow: settings.$box-shadow-invalid-checked; + color: settings.$color-invalid-checked; + + /* stylelint-disable max-nesting-depth */ + &::after { + background: settings.$color-invalid-checked; + } + /* stylelint-enable max-nesting-depth */ + } + } + &[disabled] { border-color: settings.$border-color-disabled; background-color: settings.$background-color-disabled; + box-shadow: settings.$box-shadow-disabled; color: settings.$color-disabled; &:checked { border-color: settings.$border-color-disabled-checked; background-color: settings.$background-color-disabled-checked; + box-shadow: settings.$box-shadow-disabled-checked; color: settings.$color-disabled-checked; } @@ -73,5 +119,35 @@ background: settings.$color-disabled-checked; } } + + &:focus:not(:focus-visible) { + outline-width: 0; + } + + &:checked:hover:not(:invalid), + &:checked:focus:not(:invalid), + &:checked:hover:not([aria-invalid='true']), + &:checked:focus:not([aria-invalid='true']) { + border-color: settings.$border-color-checked-hover; + background-color: settings.$background-color-checked-hover; + box-shadow: settings.$box-shadow-checked-hover; + color: settings.$color-checked-hover; + + &::after { + background: settings.$color-checked-hover; + } + } + + &:checked:active:not(:invalid), + &:checked:active:not([aria-invalid='true']) { + border-color: settings.$border-color-checked-active; + background-color: settings.$background-color-checked-active; + box-shadow: settings.$box-shadow-checked-active; + color: settings.$color-checked-active; + + &::after { + background: settings.$color-checked-active; + } + } } } diff --git a/scss/bitstyles/base/input-radio/_settings.scss b/scss/bitstyles/base/input-radio/_settings.scss index 09dd019da..61794a238 100644 --- a/scss/bitstyles/base/input-radio/_settings.scss +++ b/scss/bitstyles/base/input-radio/_settings.scss @@ -5,8 +5,8 @@ // Base styles //////////////////////////////////////// $border-radius: layout.$border-radius-round !default; $gap: var(design-token.get('size', 's4')) !default; -$border-width: var(design-token.get('size', 's7')) !default; -$size: var(design-token.get('size', 'm')) !default; +$border-width: 0.1875rem !default; +$size: var(design-token.get('size', 'l2')) !default; // // Base colors //////////////////////////////////////// @@ -14,7 +14,8 @@ $color: var(design-token.get('color', 'grayscale', 'light-1')) !default; $background-color: var( design-token.get('color', 'grayscale', 'white') ) !default; -$border-color: var(design-token.get('color', 'grayscale')) !default; +$border-color: var(design-token.get('color', 'grayscale', 'dark-2')) !default; +$box-shadow: var(design-token.get('shadow', 'brand-1', 'box')) !default; // // Hover colors //////////////////////////////////////// @@ -23,14 +24,77 @@ $background-color-hover: var( design-token.get('color', 'grayscale', 'white') ) !default; $border-color-hover: var(design-token.get('color', 'brand-1')) !default; +$box-shadow-hover: none !default; + +// +// Active colors //////////////////////////////////////// +$color-active: var(design-token.get('color', 'brand-1')) !default; +$background-color-active: var( + design-token.get('color', 'grayscale', 'white') +) !default; +$border-color-active: var( + design-token.get('color', 'brand-1', 'dark-1') +) !default; +$box-shadow-active: none !default; // // Checked colors //////////////////////////////////////// -$color-checked: var(design-token.get('color', 'brand-1')) !default; +$color-checked: var(design-token.get('color', 'brand-1', 'dark-1')) !default; $background-color-checked: var( design-token.get('color', 'grayscale', 'white') ) !default; -$border-color-checked: var(design-token.get('color', 'brand-1')) !default; +$border-color-checked: var( + design-token.get('color', 'brand-1', 'dark-1') +) !default; +$box-shadow-checked: var(design-token.get('shadow', 'brand-1', 'box')) !default; + +// +// Checked hover colors //////////////////////////////////////// +$color-checked-hover: var(design-token.get('color', 'brand-1')) !default; +$background-color-checked-hover: var( + design-token.get('color', 'grayscale', 'white') +) !default; +$border-color-checked-hover: var(design-token.get('color', 'brand-1')) !default; +$box-shadow-checked-hover: none !default; + +// +// Checked active colors //////////////////////////////////////// +$color-checked-active: var(design-token.get('color', 'brand-1')) !default; +$background-color-checked-active: var( + design-token.get('color', 'grayscale', 'white') +) !default; +$border-color-checked-active: var( + design-token.get('color', 'brand-1', 'dark-1') +) !default; +$box-shadow-checked-active: none !default; + +// +// Invalid colors //////////////////////////////////////// + +$color-invalid: var(design-token.get('color', 'warning', 'dark-1')) !default; +$background-color-invalid: var( + design-token.get('color', 'grayscale', 'white') +) !default; +$border-color-invalid: var( + design-token.get('color', 'warning', 'dark-1') +) !default; +$box-shadow-invalid: none !default; + +// +// Checked invalid colors //////////////////////////////////////// + +$color-invalid-checked: var( + design-token.get('color', 'warning', 'dark-1') +) !default; +$background-color-invalid-checked: var( + design-token.get('color', 'grayscale', 'white') +) !default; +$border-color-invalid-checked: var( + design-token.get('color', 'warning', 'dark-1') +) !default; +$box-shadow-invalid-checked: var( + design-token.get('shadow', 'warning', 'box') +) !default; // // Disabled colors //////////////////////////////////////// @@ -38,11 +102,12 @@ $color-disabled: var( design-token.get('color', 'grayscale', 'light-1') ) !default; $background-color-disabled: var( - design-token.get('color', 'grayscale', 'light-2') + design-token.get('color', 'grayscale', 'white') ) !default; $border-color-disabled: var( design-token.get('color', 'grayscale', 'light-1') ) !default; +$box-shadow-disabled: none !default; // // Disabled-checked colors //////////////////////////////////////// @@ -50,8 +115,9 @@ $color-disabled-checked: var( design-token.get('color', 'grayscale', 'light-1') ) !default; $background-color-disabled-checked: var( - design-token.get('color', 'grayscale', 'light-2') + design-token.get('color', 'grayscale', 'white') ) !default; $border-color-disabled-checked: var( design-token.get('color', 'grayscale', 'light-1') ) !default; +$box-shadow-disabled-checked: none !default; diff --git a/scss/bitstyles/base/input-radio/input-radio.stories.mdx b/scss/bitstyles/base/input-radio/input-radio.stories.mdx deleted file mode 100644 index fa9b1855e..000000000 --- a/scss/bitstyles/base/input-radio/input-radio.stories.mdx +++ /dev/null @@ -1,28 +0,0 @@ -import { Canvas, Meta, Story } from '@storybook/addon-docs'; - - - -# Input - radio - - - - {` - - `} - - - {` - - `} - - - {` - - `} - - - {` - - `} - - diff --git a/scss/bitstyles/base/input-text/_index.scss b/scss/bitstyles/base/input-text/_index.scss index 576de4f44..adb84e7ea 100644 --- a/scss/bitstyles/base/input-text/_index.scss +++ b/scss/bitstyles/base/input-text/_index.scss @@ -1,6 +1,7 @@ @forward 'settings'; @use './settings'; @use '../../tools/design-token'; +@use '../../design-tokens/focus'; /* stylelint-disable selector-max-type */ textarea, @@ -10,7 +11,10 @@ input { background-color var(design-token.get('transition', 'default', 'duration')) var(design-token.get('transition', 'default', 'easing')), border var(design-token.get('transition', 'default', 'duration')) + var(design-token.get('transition', 'default', 'easing')), + outline-offset var(design-token.get('transition', 'default', 'duration')) var(design-token.get('transition', 'default', 'easing')); + outline-offset: 0; resize: vertical; &::placeholder { @@ -21,9 +25,8 @@ input { } } -/* - * Selects all the text-like input types. We’re avoiding using e.g. `input[type="text"]` as it means we cannot use a single class later on - */ +/* prettier-ignore */ + [type='text'], [type='email'], [type='number'], @@ -37,30 +40,57 @@ input { [type='time'], [type='url'], textarea { - display: block; - width: 100%; - padding: settings.$padding-vertical settings.$padding-horizontal; + #{design-token.get('input-text', 'border-top-right-radius')}: settings.$border-radius; + #{design-token.get('input-text', 'border-bottom-right-radius')}: settings.$border-radius; + #{design-token.get('input-text', 'border-bottom-left-radius')}: settings.$border-radius; + #{design-token.get('input-text', 'border-top-left-radius')}: settings.$border-radius; + #{design-token.get('input-text', 'padding-vertical')}: settings.$padding-vertical; + #{design-token.get('input-text', 'padding-horizontal')}: settings.$padding-horizontal; + #{design-token.get('input-text', 'border-width')}: settings.$border-width; + display: flex; + flex-grow: 1; + padding: + calc(var(design-token.get('input-text', 'padding-vertical')) - var(design-token.get('input-text', 'border-width'), 0rem)) + calc(var(design-token.get('input-text','padding-horizontal')) - var(design-token.get('input-text', 'border-width'), 0rem)); border: settings.$border-width solid settings.$border-color; - border-radius: settings.$border-radius; + border-radius: + var(design-token.get('input-text', 'border-top-left-radius')) + var(design-token.get('input-text', 'border-top-right-radius')) + var(design-token.get('input-text', 'border-bottom-right-radius')) + var(design-token.get('input-text', 'border-bottom-left-radius')); background-color: settings.$background; box-shadow: settings.$box-shadow; color: settings.$color; font: settings.$font; - &:hover { + &:hover, + &:focus { border-color: settings.$border-color-hover; background-color: settings.$background-hover; box-shadow: settings.$box-shadow-hover; color: settings.$color-hover; + + &::placeholder { + color: settings.$placeholder-color-hover; + } } - &:active, - &:focus { + &:focus-visible { + z-index: 3; + outline: focus.$outline-color solid focus.$outline-width; + outline-offset: focus.$outline-offset; + } + + &:active { border-color: settings.$border-color-active; - outline: none; + outline-width: 0; background-color: settings.$background-active; box-shadow: settings.$box-shadow-active; color: settings.$color-active; + + &::placeholder { + color: settings.$placeholder-color-active; + } } &:invalid, @@ -69,6 +99,10 @@ textarea { background-color: settings.$background-color-invalid; box-shadow: settings.$box-shadow-invalid; color: settings.$color-invalid; + + &::placeholder { + color: settings.$placeholder-color-invalid; + } } &:disabled { @@ -77,6 +111,14 @@ textarea { box-shadow: settings.$box-shadow-disabled; color: settings.$color-disabled; cursor: default; + + &::placeholder { + color: settings.$placeholder-color-disabled; + } + } + + &:focus:not(:focus-visible) { + outline-width: 0; } } /* stylelint-enable selector-max-type */ diff --git a/scss/bitstyles/base/input-text/_settings.scss b/scss/bitstyles/base/input-text/_settings.scss index 1b0454d5e..a7aaf147e 100644 --- a/scss/bitstyles/base/input-text/_settings.scss +++ b/scss/bitstyles/base/input-text/_settings.scss @@ -4,90 +4,77 @@ // // Base styles //////////////////////////////////////// -$padding-vertical: var(design-token.get('size', 's5')) !default; -$padding-horizontal: var(design-token.get('size', 's5')) !default; -$border-radius: var(design-token.get('size', 's5')) !default; -$border-width: var(design-token.get('size', 's7')) !default; +$padding-vertical: var(design-token.get('size', 's2')) !default; +$padding-horizontal: var(design-token.get('size', 'm')) !default; +$border-radius: var(design-token.get('size', 's4')) !default; +$border-width: 0.1875rem !default; $font: inherit !default; -$placeholder-font-style: italic !default; +$placeholder-font-style: normal !default; $placeholder-font-weight: var( design-token.get('font-weight', 'normal') ) !default; -$placeholder-color: var( - design-token.get('color', 'grayscale', 'light-1') -) !default; // // Base colors //////////////////////////////////////// -$color: var(design-token.get('color', 'grayscale')) !default; +$color: var(design-token.get('color', 'grayscale', 'dark-4')) !default; $border-color: var(design-token.get('color', 'grayscale')) !default; $background: var(design-token.get('color', 'grayscale', 'white')) !default; -$box-shadow: shadow.to-box-shadow( - shadow.generate( - $color: - rgba(var(design-token.get('color', 'grayscale', 'light-1-rgb')), 0.025), - $blur-radius: var(design-token.get('size', 's5')), - ) -) !default; +$box-shadow: none !default; +$placeholder-color: var(design-token.get('color', 'grayscale')) !default; // // Hover colors //////////////////////////////////////// -$color-hover: var(design-token.get('color', 'grayscale', 'dark-1')) !default; -$background-hover: var(design-token.get('color', 'grayscale')) !default; -$border-color-hover: var( - design-token.get('color', 'grayscale', 'dark-1') +$color-hover: var(design-token.get('color', 'grayscale', 'dark-4')) !default; +$background-hover: var( + design-token.get('color', 'grayscale', 'white') ) !default; -$box-shadow-hover: shadow.to-box-shadow( - shadow.generate( - $color: - rgba(var(design-token.get('color', 'brand-1', 'light-1-rgb')), 0.025), - $blur-radius: var(design-token.get('size', 's2')), - ) +$border-color-hover: var( + design-token.get('color', 'grayscale', 'dark-2') ) !default; +$box-shadow-hover: none !default; +$placeholder-color-hover: var(design-token.get('color', 'grayscale')) !default; // // Active colors //////////////////////////////////////// -$color-active: var(design-token.get('color', 'grayscale', 'dark-1')) !default; +$color-active: var(design-token.get('color', 'grayscale', 'dark-4')) !default; $background-active: var( design-token.get('color', 'grayscale', 'white') ) !default; $border-color-active: var( - design-token.get('color', 'grayscale', 'dark-1') -) !default; -$box-shadow-active: shadow.to-box-shadow( - shadow.generate( - $color: - rgba(var(design-token.get('color', 'brand-1', 'light-1-rgb')), 0.075), - $blur-radius: var(design-token.get('size', 's6')), - ) + design-token.get('color', 'brand-1', 'dark-1') ) !default; +$box-shadow-active: none !default; +$placeholder-color-active: var(design-token.get('color', 'grayscale')) !default; // // Disabled colors //////////////////////////////////////// $color-disabled: var(design-token.get('color', 'grayscale')) !default; $background-disabled: var( - design-token.get('color', 'grayscale', 'light-2') + design-token.get('color', 'grayscale', 'white') ) !default; $border-color-disabled: var( - design-token.get('color', 'grayscale', 'light-2') + design-token.get('color', 'grayscale', 'light-1') ) !default; $box-shadow-disabled: none !default; +$placeholder-color-disabled: var( + design-token.get('color', 'grayscale') +) !default; // // Invalid colors //////////////////////////////////////// -$color-invalid: var(design-token.get('color', 'warning')) !default; +$color-invalid: var(design-token.get('color', 'warning', 'dark-4')) !default; $background-color-invalid: var( design-token.get('color', 'grayscale', 'white') ) !default; -$border-color-invalid: var(design-token.get('color', 'warning')) !default; -$box-shadow-invalid: shadow.to-box-shadow( - shadow.generate( - $color: rgba(var(design-token.get('color', 'warning', 'rgb')), 0.05), - $blur-radius: var(design-token.get('size', 's4')), - ) +$border-color-invalid: var( + design-token.get('color', 'warning', 'dark-1') +) !default; +$box-shadow-invalid: none !default; +$placeholder-color-invalid: var( + design-token.get('color', 'grayscale') ) !default; diff --git a/scss/bitstyles/base/input-text/input-text.stories.mdx b/scss/bitstyles/base/input-text/input-text.stories.mdx deleted file mode 100644 index 8dfe8398d..000000000 --- a/scss/bitstyles/base/input-text/input-text.stories.mdx +++ /dev/null @@ -1,84 +0,0 @@ -import { Canvas, Meta, Story } from '@storybook/addon-docs'; - - - -# Base input types - -Here are the various inputs, textareas & selects. They’re presented here for completeness, and so they’re available for testing purposes. - -You may find [Base/Forms](/docs/base-forms--fieldset) more informative. - -## Input - text - - - - {` - - `} - - - {` - - `} - - - -## Input - email - - - - {` - - `} - - - {` - - `} - - - -## Input - number - - - - {` - - `} - - - {` - - `} - - - -## Input - range - - - - {` - - `} - - - {` - - `} - - - -## Textarea - - - - {` - - `} - - - {` - - `} - - diff --git a/scss/bitstyles/base/select/_index.scss b/scss/bitstyles/base/select/_index.scss index 09321cc54..41e8ba50e 100644 --- a/scss/bitstyles/base/select/_index.scss +++ b/scss/bitstyles/base/select/_index.scss @@ -1,15 +1,25 @@ @forward 'settings'; @use 'settings'; @use '../../tools/design-token'; +@use '../../design-tokens/focus'; +@use '../../tools/custom-property'; +/* prettier-ignore */ @supports (-webkit-appearance: none) or (-moz-appearance: none) or (appearance: none) { /* stylelint-disable selector-max-type */ select { - display: block; - width: 100%; - padding: settings.$padding-vertical calc(settings.$padding-horizontal * 2) - settings.$padding-vertical settings.$padding-horizontal; + #{design-token.get('select', 'padding-vertical')}: settings.$padding-vertical; + #{design-token.get('select', 'padding-horizontal')}: settings.$padding-horizontal; + #{design-token.get('select', 'border-width')}: settings.$border-width; + #{design-token.get('select', 'border-top-right-radius')}: settings.$border-radius; + #{design-token.get('select', 'border-bottom-right-radius')}: settings.$border-radius; + #{design-token.get('select', 'border-bottom-left-radius')}: settings.$border-radius; + #{design-token.get('select', 'border-top-left-radius')}: settings.$border-radius; + display: flex; + padding: + calc(var(design-token.get('select', 'padding-vertical')) - var(design-token.get('select', 'border-width'), 0rem)) + calc(var(design-token.get('select','padding-horizontal')) - var(design-token.get('select', 'border-width'), 0rem)); overflow: hidden; transition: color var(design-token.get('transition', 'default', 'duration')) var(design-token.get('transition', 'default', 'easing')), @@ -19,14 +29,21 @@ border var(design-token.get('transition', 'default', 'duration')) var(design-token.get('transition', 'default', 'easing')); border: settings.$border-width solid settings.$border-color; - border-radius: settings.$border-radius; + border-radius: + var(design-token.get('select', 'border-top-left-radius')) + var(design-token.get('select', 'border-top-right-radius')) + var(design-token.get('select', 'border-bottom-right-radius')) + var(design-token.get('select', 'border-bottom-left-radius')); + outline-offset: 0; background-color: settings.$background-color; background-image: settings.$background-image; background-repeat: no-repeat; background-position: right 0.75em top 50%; background-size: 1em; + box-shadow: settings.$box-shadow; color: settings.$color; font: settings.$font; + font-weight: settings.$font-weight; text-overflow: ellipsis; cursor: pointer; appearance: none; @@ -35,26 +52,35 @@ display: none; } - &:hover { + &:hover, + &:focus { border-color: settings.$border-color-hover; background-color: settings.$background-color-hover; background-image: settings.$background-image-hover; + box-shadow: settings.$box-shadow-hover; color: settings.$color-hover; } - &:focus, + &:focus-visible { + outline: focus.$outline-color solid focus.$outline-width; + outline-offset: focus.$outline-offset; + } + &:active { border-color: settings.$border-color-active; outline: none; background-color: settings.$background-color-active; background-image: settings.$background-image-active; + box-shadow: settings.$box-shadow-active; color: settings.$color-active; } - &:invalid { + &:invalid, + &[aria-invalid='true'] { border-color: settings.$border-color-invalid; background-color: settings.$background-color-invalid; background-image: settings.$background-image-invalid; + box-shadow: settings.$box-shadow-invalid; color: settings.$color-invalid; } @@ -62,9 +88,14 @@ border-color: settings.$border-color-disabled; background-color: settings.$background-color-disabled; background-image: settings.$background-image-disabled; + box-shadow: settings.$box-shadow-disabled; color: settings.$color-disabled; cursor: default; } + + &:focus:not(:focus-visible) { + outline-width: 0; + } } /* stylelint-enable selector-max-type */ } diff --git a/scss/bitstyles/base/select/_settings.scss b/scss/bitstyles/base/select/_settings.scss index 116e83224..54028c943 100644 --- a/scss/bitstyles/base/select/_settings.scss +++ b/scss/bitstyles/base/select/_settings.scss @@ -1,55 +1,65 @@ -@use '../../tools/design-token'; @use '../../tools/color'; +@use '../../tools/design-token'; // // Base styles //////////////////////////////////////// -$padding-vertical: var(design-token.get('size', 's5')) !default; -$padding-horizontal: var(design-token.get('size', 's1')) !default; -$border-radius: var(design-token.get('size', 's5')) !default; -$border-width: var(design-token.get('size', 's7')) !default; +$padding-vertical: var(design-token.get('size', 's2')) !default; +$padding-horizontal: var(design-token.get('size', 'l1')) !default; +$border-radius: var(design-token.get('size', 's4')) !default; +$border-width: 0.1875rem !default; $font: inherit !default; +$font-weight: var(design-token.get('font-weight', 'bold')) !default; // // Base appearance //////////////////////////////////////// -$color: var(design-token.get('color', 'grayscale')) !default; -$border-color: var(design-token.get('color', 'grayscale')) !default; +$color: var(design-token.get('color', 'grayscale', 'dark-1')) !default; +$border-color: var(design-token.get('color', 'grayscale', 'dark-1')) !default; $background-color: var( design-token.get('color', 'grayscale', 'white') ) !default; $background-image: url("data:image/svg+xml,%3Csvg width='20' height='20' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cpath fill='#{color.url-encode-color(var(design-token.get('color', 'grayscale', 'light-1')))}' d='M6.64 34.23a5.57 5.57 0 0 1 7.87-7.89L49.92 61.91 85.49 26.34a5.57 5.57 0 0 1 7.87 7.89L53.94 73.66a5.58 5.58 0 0 1-7.88 0Z'/%3E%3C/svg%3E") !default; +$box-shadow: none !default; // // Hover styles //////////////////////////////////////// $color-hover: var(design-token.get('color', 'grayscale', 'dark-1')) !default; -$border-color-hover: var(design-token.get('color', 'grayscale')) !default; -$background-color-hover: var(design-token.get('color', 'grayscale')) !default; +$border-color-hover: var( + design-token.get('color', 'grayscale', 'dark-1') +) !default; +$background-color-hover: var( + design-token.get('color', 'brand-1', 'light-4') +) !default; $background-image-hover: url("data:image/svg+xml,%3Csvg width='20' height='20' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cpath fill='#{color.url-encode-color($color-hover)}' d='M6.64 34.23a5.57 5.57 0 0 1 7.87-7.89L49.92 61.91 85.49 26.34a5.57 5.57 0 0 1 7.87 7.89L53.94 73.66a5.58 5.58 0 0 1-7.88 0Z'/%3E%3C/svg%3E") !default; +$box-shadow-hover: none !default; // // Active styles //////////////////////////////////////// -$color-active: var(design-token.get('color', 'grayscale', 'dark-1')) !default; +$color-active: var(design-token.get('color', 'brand-1', 'dark-1')) !default; $border-color-active: var( - design-token.get('color', 'grayscale', 'dark-1') + design-token.get('color', 'brand-1', 'dark-1') +) !default; +$background-color-active: var( + design-token.get('color', 'grayscale', 'white') ) !default; -$background-color-active: transparent !default; $background-image-active: url("data:image/svg+xml,%3Csvg width='20' height='20' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cpath fill='#{color.url-encode-color(var(design-token.get('color', 'grayscale', 'light-1')))}' d='M6.64 34.23a5.57 5.57 0 0 1 7.87-7.89L49.92 61.91 85.49 26.34a5.57 5.57 0 0 1 7.87 7.89L53.94 73.66a5.58 5.58 0 0 1-7.88 0Z'/%3E%3C/svg%3E") !default; +$box-shadow-active: var(design-token.get('shadow', 'brand-1')) !default; // // Invalid styles //////////////////////////////////////// -$color-invalid: var(design-token.get('color', 'warning')) !default; +$color-invalid: var(design-token.get('color', 'grayscale', 'dark-1')) !default; $border-color-invalid: var(design-token.get('color', 'warning')) !default; $background-color-invalid: var( design-token.get('color', 'grayscale', 'white') ) !default; $background-image-invalid: url("data:image/svg+xml,%3Csvg width='20' height='20' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cpath fill='#{color.url-encode-color(var(design-token.get('color', 'warning')))}' d='M6.64 34.23a5.57 5.57 0 0 1 7.87-7.89L49.92 61.91 85.49 26.34a5.57 5.57 0 0 1 7.87 7.89L53.94 73.66a5.58 5.58 0 0 1-7.88 0Z'/%3E%3C/svg%3E") !default; +$box-shadow-invalid: none !default; // // Disabled styles //////////////////////////////////////// $color-disabled: var(design-token.get('color', 'grayscale')) !default; -$border-color-disabled: var( - design-token.get('color', 'grayscale', 'light-2') -) !default; +$border-color-disabled: var(design-token.get('color', 'grayscale')) !default; $background-color-disabled: var( - design-token.get('color', 'grayscale', 'light-2') + design-token.get('color', 'grayscale', 'light-3') ) !default; $background-image-disabled: url("data:image/svg+xml,%3Csvg width='20' height='20' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cpath fill='#{color.url-encode-color(var(design-token.get('color', 'grayscale', 'light-1')))}' d='M6.64 34.23a5.57 5.57 0 0 1 7.87-7.89L49.92 61.91 85.49 26.34a5.57 5.57 0 0 1 7.87 7.89L53.94 73.66a5.58 5.58 0 0 1-7.88 0Z'/%3E%3C/svg%3E") !default; +$box-shadow-disabled: none !default; diff --git a/scss/bitstyles/base/select/select.stories.mdx b/scss/bitstyles/base/select/select.stories.mdx deleted file mode 100644 index 49a2b47f7..000000000 --- a/scss/bitstyles/base/select/select.stories.mdx +++ /dev/null @@ -1,35 +0,0 @@ -import { Canvas, Meta, Story } from '@storybook/addon-docs'; - - - -# Select - - - - {` - - `} - - - {` - - `} - - - {` - - `} - - diff --git a/scss/bitstyles/design-tokens/_breakpoints.scss b/scss/bitstyles/design-tokens/_breakpoints.scss index 8ae5e335c..50219eb6d 100644 --- a/scss/bitstyles/design-tokens/_breakpoints.scss +++ b/scss/bitstyles/design-tokens/_breakpoints.scss @@ -2,13 +2,13 @@ $boundary-width: 0.0625em !default; $s-m-boundary: 30em !default; -$m-l2-boundary: 55em !default; -$l-l3-boundary: 95em !default; +$m-l-boundary: 55em !default; +$l-xl-boundary: 95em !default; $breakpoints: ( 's': 'screen and (max-width: #{$s-m-boundary - $boundary-width})', 'm': 'screen and (min-width: #{$s-m-boundary})', - 'l': 'screen and (min-width: #{$m-l2-boundary})', - 'xl': 'screen and (min-width: #{$l-l3-boundary})', + 'l': 'screen and (min-width: #{$m-l-boundary})', + 'xl': 'screen and (min-width: #{$l-xl-boundary})', 'hidpi': '(-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi), (min-resolution: 2dppx)', 'landscape': 'all and (orientation:landscape)', diff --git a/scss/bitstyles/design-tokens/_typography.scss b/scss/bitstyles/design-tokens/_typography.scss index 5ef441058..ed608db39 100644 --- a/scss/bitstyles/design-tokens/_typography.scss +++ b/scss/bitstyles/design-tokens/_typography.scss @@ -26,37 +26,37 @@ $webfont-variants: ( 'normal': ( 'font-family': $webfont-family-name, 'font-style': normal, - 'font-weight': var(custom-property.name('font-weight', 'normal')), + 'font-weight': 400, 'filename': 'poppins-v20-latin-400', ), 'italic': ( 'font-family': $webfont-family-name, 'font-style': italic, - 'font-weight': var(custom-property.name('font-weight', 'normal')), + 'font-weight': 400, 'filename': 'poppins-v20-latin-400italic', ), 'medium': ( 'font-family': $webfont-family-name, 'font-style': normal, - 'font-weight': var(custom-property.name('font-weight', 'medium')), + 'font-weight': 500, 'filename': 'poppins-v20-latin-500', ), 'medium-italic': ( 'font-family': $webfont-family-name, 'font-style': italic, - 'font-weight': var(custom-property.name('font-weight', 'medium')), + 'font-weight': 500, 'filename': 'poppins-v20-latin-500italic', ), 'semibold': ( 'font-family': $webfont-family-name, 'font-style': normal, - 'font-weight': var(custom-property.name('font-weight', 'semibold')), + 'font-weight': 600, 'filename': 'poppins-v20-latin-600', ), 'semibold-italic': ( 'font-family': $webfont-family-name, 'font-style': italic, - 'font-weight': var(custom-property.name('font-weight', 'semibold')), + 'font-weight': 600, 'filename': 'poppins-v20-latin-600italic', ), ) !default; diff --git a/scss/bitstyles/organisms/joined-ui/JoinedUI.js b/scss/bitstyles/organisms/joined-ui/JoinedUI.js index 575cf2867..d525199cf 100644 --- a/scss/bitstyles/organisms/joined-ui/JoinedUI.js +++ b/scss/bitstyles/organisms/joined-ui/JoinedUI.js @@ -1,11 +1,6 @@ -export default ({ children }) => { +export default ({ children, classname }) => { const list = document.createElement('ul'); - list.classList.add( - 'u-list-none', - 'u-inline-flex', - 'u-items-stretch', - 'o-joined-ui' - ); + list.classList.add('u-list-none', 'o-joined-ui', classname); children.forEach((child) => { const listItem = document.createElement('li'); diff --git a/scss/bitstyles/organisms/joined-ui/_index.scss b/scss/bitstyles/organisms/joined-ui/_index.scss index 17ce3f7b2..5861b2082 100644 --- a/scss/bitstyles/organisms/joined-ui/_index.scss +++ b/scss/bitstyles/organisms/joined-ui/_index.scss @@ -3,49 +3,120 @@ @use '../../tools/classname'; @use '../../tools/design-token'; -/* stylelint-disable scss/dollar-variable-default */ +#{classname.get($classname-items: 'joined-ui', $layer: 'organism')} { + /* Buttons */ + $button-classname: classname.get( + $classname-items: 'button', + $layer: 'atom', + ); + $button-border-top-left-radius-name: design-token.get( + 'button', + 'border', + 'top', + 'left', + 'radius' + ); + $button-border-top-right-radius-name: design-token.get( + 'button', + 'border', + 'top', + 'right', + 'radius' + ); + $button-border-bottom-right-radius-name: design-token.get( + 'button', + 'border', + 'bottom', + 'right', + 'radius' + ); + $button-border-bottom-left-radius-name: design-token.get( + 'button', + 'border', + 'bottom', + 'left', + 'radius' + ); + $button-border-width-name: design-token.get('button', 'border', 'width'); + $button-box-shadow-name: design-token.get('button', 'box-shadow'); -$button-classname: classname.get( - $classname-items: 'button', - $layer: 'atom', -); -$button-border-top-left-radius-name: design-token.get( - 'button', - 'border', - 'top', - 'left', - 'radius' -); -$button-border-top-right-radius-name: design-token.get( - 'button', - 'border', - 'top', - 'right', - 'radius' -); -$button-border-bottom-right-radius-name: design-token.get( - 'button', - 'border', - 'bottom', - 'right', - 'radius' -); -$button-border-bottom-left-radius-name: design-token.get( - 'button', - 'border', - 'bottom', - 'left', - 'radius' -); -$button-border-width-name: design-token.get('button', 'border', 'width'); -$button-box-shadow-name: design-token.get('button', 'box-shadow'); + /* Text inputs */ + $input-text-border-top-left-radius-name: design-token.get( + 'input-text', + 'border', + 'top', + 'left', + 'radius' + ); + $input-text-border-top-right-radius-name: design-token.get( + 'input-text', + 'border', + 'top', + 'right', + 'radius' + ); + $input-text-border-bottom-right-radius-name: design-token.get( + 'input-text', + 'border', + 'bottom', + 'right', + 'radius' + ); + $input-text-border-bottom-left-radius-name: design-token.get( + 'input-text', + 'border', + 'bottom', + 'left', + 'radius' + ); + $input-text-border-width-name: design-token.get( + 'input-text', + 'border', + 'width' + ); + $input-text-box-shadow-name: design-token.get('input-text', 'box-shadow'); -/* stylelint-enable scss/dollar-variable-default */ + /* Selects */ + $select-border-top-left-radius-name: design-token.get( + 'select', + 'border', + 'top', + 'left', + 'radius' + ); + $select-border-top-right-radius-name: design-token.get( + 'select', + 'border', + 'top', + 'right', + 'radius' + ); + $select-border-bottom-right-radius-name: design-token.get( + 'select', + 'border', + 'bottom', + 'right', + 'radius' + ); + $select-border-bottom-left-radius-name: design-token.get( + 'select', + 'border', + 'bottom', + 'left', + 'radius' + ); + $select-border-width-name: design-token.get('select', 'border', 'width'); + $select-box-shadow-name: design-token.get('select', 'box-shadow'); -#{classname.get($classname-items: 'joined-ui', $layer: 'organism')} { + display: inline-flex; + align-items: stretch; border-radius: settings.$border-radius; box-shadow: settings.$box-shadow; + > * { + display: flex; + } + > :first-child #{$button-classname}, > #{$button-classname}:first-child { #{$button-box-shadow-name}: none; @@ -54,6 +125,22 @@ $button-box-shadow-name: design-token.get('button', 'box-shadow'); margin-right: calc(var(#{$button-border-width-name}) * -1); } + > :first-child input, + > input:first-child { + #{$input-text-box-shadow-name}: none; + #{$input-text-border-top-right-radius-name}: #{0}; + #{$input-text-border-bottom-right-radius-name}: #{0}; + margin-right: calc(var(#{$input-text-border-width-name}) * -1); + } + + > :first-child select, + > select:first-child { + #{$select-box-shadow-name}: none; + #{$select-border-top-right-radius-name}: #{0}; + #{$select-border-bottom-right-radius-name}: #{0}; + margin-right: calc(var(#{$select-border-width-name}) * -1); + } + > :last-child #{$button-classname}, > #{$button-classname}:last-child { #{$button-box-shadow-name}: none; @@ -62,6 +149,22 @@ $button-box-shadow-name: design-token.get('button', 'box-shadow'); margin-right: 0; } + > :last-child input, + > input:last-child { + #{$input-text-box-shadow-name}: none; + #{$input-text-border-top-left-radius-name}: #{0}; + #{$input-text-border-bottom-left-radius-name}: #{0}; + margin-right: 0; + } + + > :last-child select, + > select:last-child { + #{$select-box-shadow-name}: none; + #{$select-border-top-left-radius-name}: #{0}; + #{$select-border-bottom-left-radius-name}: #{0}; + margin-right: 0; + } + > :not(:first-child):not(:last-child) #{$button-classname}, > #{$button-classname}:not(:first-child):not(:last-child) { #{$button-border-top-left-radius-name}: #{0}; @@ -70,4 +173,22 @@ $button-box-shadow-name: design-token.get('button', 'box-shadow'); #{$button-border-bottom-left-radius-name}: #{0}; margin-right: calc(var(#{$button-border-width-name}) * -1); } + + > :not(:first-child):not(:last-child) input, + > input:not(:first-child):not(:last-child) { + #{$input-text-border-top-left-radius-name}: #{0}; + #{$input-text-border-top-right-radius-name}: #{0}; + #{$input-text-border-bottom-right-radius-name}: #{0}; + #{$input-text-border-bottom-left-radius-name}: #{0}; + margin-right: calc(var(#{$input-text-border-width-name}) * -1); + } + + > :not(:first-child):not(:last-child) select, + > select:not(:first-child):not(:last-child) { + #{$select-border-top-left-radius-name}: #{0}; + #{$select-border-top-right-radius-name}: #{0}; + #{$select-border-bottom-right-radius-name}: #{0}; + #{$select-border-bottom-left-radius-name}: #{0}; + margin-right: calc(var(#{$select-border-width-name}) * -1); + } } diff --git a/scss/bitstyles/tools/_button.scss b/scss/bitstyles/tools/_button.scss index 7610b7a11..346dc3d49 100644 --- a/scss/bitstyles/tools/_button.scss +++ b/scss/bitstyles/tools/_button.scss @@ -6,8 +6,9 @@ @use './design-token'; @use './custom-properties'; +/* prettier-ignore */ + @mixin colors($colors, $variant-name) { - $button-name: 'button#{$variant-name}'; $allow-list: list.join( settings.$allowed-color-properties, settings.$extra-color-properties @@ -17,9 +18,7 @@ @include custom-properties.output-filtered( $properties: map.get($colors, 'default'), $allow-list: $allow-list, - $module-name: ( - $button-name, - ) + $module-name: ($variant-name) ); } @@ -27,10 +26,7 @@ @include custom-properties.output-filtered( $properties: map.get($colors, 'hover'), $allow-list: $allow-list, - $module-name: ( - $button-name, - 'hover', - ) + $module-name: ($variant-name, 'hover') ); } @@ -38,10 +34,7 @@ @include custom-properties.output-filtered( $properties: map.get($colors, 'active'), $allow-list: $allow-list, - $module-name: ( - $button-name, - 'active', - ) + $module-name: ($variant-name, 'active') ); } @@ -49,10 +42,7 @@ @include custom-properties.output-filtered( $properties: map.get($colors, 'pressed'), $allow-list: $allow-list, - $module-name: ( - $button-name, - 'pressed', - ) + $module-name: ($variant-name, 'pressed') ); } @@ -60,19 +50,18 @@ @include custom-properties.output-filtered( $properties: map.get($colors, 'disabled'), $allow-list: $allow-list, - $module-name: ( - $button-name, - 'disabled', - ) + $module-name: ($variant-name, 'disabled') ); } } +/* prettier-ignore */ + @mixin sizes($sizes, $with-square-variant: true) { @include custom-properties.output-filtered( $properties: $sizes, $allow-list: settings.$allowed-shape-properties, - $module-name: 'button' + $module-name: 'button', ); @each $property-name in (settings.$allowed-shape-properties) { @@ -88,18 +77,8 @@ $min-height-property: design-token.get('button', 'min-height'); &#{classname.get($classname-items: 'button--square', $layer: 'atom')} { - #{$min-width-property}: calc( - 1em * - var(#{$line-height-property}) + - 2 * - #{map.get($sizes, 'padding-vertical')} - ); - #{$min-height-property}: calc( - 1em * - var(#{$line-height-property}) + - 2 * - #{map.get($sizes, 'padding-vertical')} - ); + #{$min-width-property}: #{calc(1em * var(#{$line-height-property}) + 2 * #{map.get($sizes, 'padding-vertical')})}; + #{$min-height-property}: #{calc(1em * var(#{$line-height-property}) + 2 * #{map.get($sizes, 'padding-vertical')})}; @if map.has-key($sizes, 'padding-vertical') { #{design-token.get('button', 'padding-horizontal')}: map.get( @@ -110,6 +89,8 @@ $sizes, 'padding-vertical' ); + #{design-token.get('button', 'padding-horizontal')}: #{map.get($sizes, 'padding-vertical')}; + #{design-token.get('button', 'padding-vertical')}: #{map.get($sizes, 'padding-vertical')}; } } } diff --git a/scss/bitstyles/tools/_custom-property.scss b/scss/bitstyles/tools/_custom-property.scss index 8b0ed43a3..bf0dd7eb4 100644 --- a/scss/bitstyles/tools/_custom-property.scss +++ b/scss/bitstyles/tools/_custom-property.scss @@ -3,7 +3,7 @@ @use 'sass:list'; @function name($items...) { - $string-items: list.join((setup.$custom-property-namespace), $items); + $string-items: list.join(setup.$custom-property-namespace, $items); $custom-property-name: string.join-with-dashes( $string-items: $string-items, ); diff --git a/scss/bitstyles/ui/buttons.stories.mdx b/scss/bitstyles/ui/buttons.stories.mdx index f9fbaf2a1..5719cb61d 100644 --- a/scss/bitstyles/ui/buttons.stories.mdx +++ b/scss/bitstyles/ui/buttons.stories.mdx @@ -3,6 +3,63 @@ import icons from '../../../assets/images/icons.svg'; +
+
+ + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + +
+
+ # Buttons See [Atoms/Button](/docs/atoms-button-overview--page) for general information on buttons. diff --git a/scss/bitstyles/ui/forms.stories.js b/scss/bitstyles/ui/forms.stories.js new file mode 100644 index 000000000..2a462897e --- /dev/null +++ b/scss/bitstyles/ui/forms.stories.js @@ -0,0 +1,106 @@ +import Input from '../base/forms/Input'; +import Label from '../base/forms/Label'; +import Select from '../base/forms/Select'; +import Button from '../atoms/button/Button'; +import JoinedUI from '../organisms/joined-ui/JoinedUI'; +import icons from '../../../assets/images/icons.svg'; + +export default { + title: 'UI/Data/Forms', + subcomponents: { Input, Label, Select, Button, JoinedUI }, +}; + +// ***** Default size, each shape & color ****************** // + +export const SearchFormWithButton = () => { + const form = document.createElement('form'); + form.setAttribute('action', ''); + form.setAttribute('role', 'search'); + form.setAttribute('method', 'post'); + form.append(Label({ for: 'text-search', children: ['Search users'] })); + form.append( + JoinedUI({ + children: [ + Input({ + type: 'text', + placeholder: 'Username or email', + }), + Button({ + colorVariant: ['outline'], + children: 'Search', + type: 'submit', + }), + ], + }) + ); + return form; +}; + +export const SearchFormWithIconButton = () => { + const form = document.createElement('form'); + form.setAttribute('action', ''); + form.setAttribute('role', 'search'); + form.setAttribute('method', 'post'); + form.append(Label({ for: 'text-search', children: ['Search users'] })); + form.append( + JoinedUI({ + children: [ + Input({ + type: 'text', + placeholder: 'Username or email', + }), + Button({ + colorVariant: ['outline'], + children: `Search`, + type: 'submit', + }), + ], + }) + ); + return form; +}; + +export const SearchFormWithSelectAndButton = () => { + const form = document.createElement('form'); + form.setAttribute('action', ''); + form.setAttribute('role', 'search'); + form.setAttribute('method', 'post'); + form.append(Label({ for: 'text-search', children: ['Search users'] })); + const wrapper = document.createElement('div'); + wrapper.setAttribute('class', 'u-flex u-gap-s5'); + wrapper.append( + JoinedUI({ + children: [ + Input({ + type: 'text', + placeholder: 'Username or email', + }), + Select({ + options: [ + { + value: 'user', + label: 'User', + }, + { + value: 'admin', + label: 'Admin', + }, + { + value: 'superuser', + label: 'Superuser', + }, + ], + }), + ], + }) + ); + wrapper.append( + Button({ + colorVariant: ['outline'], + children: 'Search', + type: 'submit', + }) + ); + form.append(wrapper); + return form; +}; diff --git a/scss/bitstyles/ui/forms.stories.mdx b/scss/bitstyles/ui/forms.stories.mdx index 84aa812c9..75c22e30b 100644 --- a/scss/bitstyles/ui/forms.stories.mdx +++ b/scss/bitstyles/ui/forms.stories.mdx @@ -1,7 +1,7 @@ import { Canvas, Meta, Story } from '@storybook/addon-docs'; import icons from '../../../assets/images/icons.svg'; - + # Forms @@ -64,7 +64,7 @@ Here are some examples using the grid utility classes to create common form layo

Login