diff --git a/demo/js/components/RootPage.js b/demo/js/components/RootPage.js index 379d39a95bbf..3e9140f8b19f 100644 --- a/demo/js/components/RootPage.js +++ b/demo/js/components/RootPage.js @@ -237,11 +237,18 @@ class RootPage extends Component { clearTimeout(this.hTimeoutDetectExperimental); this.hTimeoutDetectExperimental = null; } - // For IE11 - if (link.sheet.cssRules.length === 0) { + let rulesLength = 0; + try { + // https://bugzilla.mozilla.org/show_bug.cgi?id=761236#c1 suggests that `NS_ERROR_DOM_INVALID_ACCESS_ERR` is thrown + // if we try to read `.cssRules` on a stylesheet that's not done being parsed yet + rulesLength = link.sheet.cssRules.length; + } catch (err) {} // eslint-disable-line no-empty + // For IE11/FF + if (rulesLength === 0) { this.hTimeoutDetectExperimental = setTimeout(() => { this._detectComponentsX(link); }, 100); + return; } const isComponentsX = Array.prototype.some.call( link.sheet.cssRules, diff --git a/src/components/accordion/_accordion.scss b/src/components/accordion/_accordion.scss index a8ae34eea4b5..8470570efd66 100644 --- a/src/components/accordion/_accordion.scss +++ b/src/components/accordion/_accordion.scss @@ -4,11 +4,12 @@ @import '../../globals/scss/typography'; @import '../../globals/scss/spacing'; @import '../../globals/scss/helper-mixins'; +@import '../../globals/scss/functions'; @import '../../globals/scss/import-once'; @import '../../globals/scss/css--reset'; @import '../../globals/scss/css--typography'; -@include exports('accordion') { +@mixin accordion { .#{$prefix}--accordion { @include reset; @include font-family; @@ -137,3 +138,148 @@ margin-bottom: 0; } } + +@mixin accordion--x { + .#{$prefix}--accordion { + @include reset; + @include font-family; + list-style: none; + width: 100%; + } + + .#{$prefix}--accordion__item { + transition: all $transition--base $carbon--standard-easing; + border-top: 1px solid $ui-03; + overflow: hidden; + + &:focus { + outline: none; + + .#{$prefix}--accordion__arrow { + @include focus-outline('border'); + overflow: visible; // safari fix + outline-offset: -0.5px; // safari fix + } + } + + &:last-child { + border-bottom: 1px solid $ui-03; + } + } + + .#{$prefix}--accordion__heading { + @include button-reset; + color: $text-01; + display: flex; + align-items: center; + justify-content: $accordion-justify-content; + cursor: pointer; + padding: rem(7px) 0; + flex-direction: $accordion-flex-direction; + width: 100%; + margin: 0; // safari fix + + &:focus { + outline: none; + + .#{$prefix}--accordion__arrow { + @include focus-outline('border'); + overflow: visible; // safari fix + outline-offset: -0.5px; // safari fix + } + } + &:hover { + background-color: $field-01; + } + } + + .#{$prefix}--accordion__arrow { + transition: all $transition--base $carbon--standard-easing; + height: 1.25rem; + width: 1.25rem; + padding: $spacing-2xs $spacing-3xs $spacing-2xs $spacing-2xs; + margin: $accordion-arrow-margin; + fill: $ui-05; + transform: rotate(90deg) /*rtl:rotate(180deg)*/; + } + + .#{$prefix}--accordion__title { + @include typescale('zeta'); + @include line-height('body'); + margin: $accordion-title-margin; + width: 100%; + text-align: left; + font-weight: 400; + } + + .#{$prefix}--accordion__content { + transition: all $transition--expansion $carbon--ease-out; + padding-left: $spacing-md; + height: 0; + visibility: hidden; + opacity: 0; + + p { + @include typescale('zeta'); + padding-right: 20%; + } + } + + .#{$prefix}--accordion__item--active { + overflow: visible; + border-top: 1px solid transparent; + + .#{$prefix}--accordion__content { + padding-bottom: $spacing-lg; + padding-top: $spacing-xs; + height: auto; + visibility: visible; + opacity: 1; + transition: all $transition--expansion $carbon--ease-in; + } + + .#{$prefix}--accordion__arrow { + /*rtl:ignore*/ + transform: rotate(-90deg); + fill: $ui-05; + } + } + + // Skeleton state + .#{$prefix}--accordion.#{$prefix}--skeleton .#{$prefix}--accordion__heading, + .#{$prefix}--accordion.#{$prefix}--skeleton .#{$prefix}--accordion__button { + cursor: default; + } + + .#{$prefix}--accordion.#{$prefix}--skeleton .#{$prefix}--accordion__arrow { + pointer-events: none; + fill: $ui-05; + cursor: default; + + &:hover, + &:focus, + &:active { + border: none; + outline: none; + cursor: default; + } + } + + .#{$prefix}--skeleton .#{$prefix}--accordion__heading:focus .#{$prefix}--accordion__arrow { + border: none; + outline: none; + cursor: default; + } + + .#{$prefix}--accordion__title.#{$prefix}--skeleton__text { + margin-bottom: 0; + } +} + +@include exports('accordion') { + @if feature-flag-enabled('components-x') { + @include accordion--x; + } @else { + @include accordion; + } +} diff --git a/src/components/breadcrumb/_breadcrumb.scss b/src/components/breadcrumb/_breadcrumb.scss index 086fc7ce24c5..038c8834efa6 100644 --- a/src/components/breadcrumb/_breadcrumb.scss +++ b/src/components/breadcrumb/_breadcrumb.scss @@ -9,7 +9,7 @@ @import '../../globals/scss/css--typography'; @import '../link/link'; -@include exports('breadcrumb') { +@mixin breadcrumb { .#{$prefix}--breadcrumb { @include typescale('zeta'); @include font-family; @@ -66,3 +66,60 @@ height: 1rem; } } + +@mixin breadcrumb-experimental { + .#{$prefix}--breadcrumb { + @include typescale('zeta'); + @include font-family; + display: inline; + + @include breakpoint(bp--xs--major) { + display: flex; + flex-wrap: wrap; + } + } + + .#{$prefix}--breadcrumb-item { + margin-right: $spacing-xs; + display: flex; + align-items: center; + line-height: 1.25; //needed for correct spacing for underline hover + } + + .#{$prefix}--breadcrumb-item::after { + content: '/'; + margin-left: $spacing-xs; + color: $text-02; + } + + .#{$prefix}--breadcrumb--no-trailing-slash .#{$prefix}--breadcrumb-item:last-child::after { + content: ''; + } + + .#{$prefix}--breadcrumb-item:last-child { + margin-right: 0; + + &::after { + margin-right: 0; + } + } + + .#{$prefix}--breadcrumb .#{$prefix}--link { + white-space: nowrap; + } + + // Skeleton State + .#{$prefix}--breadcrumb.#{$prefix}--skeleton .#{$prefix}--link { + @include skeleton; + width: rem(100px); + height: 1rem; + } +} + +@include exports('breadcrumb') { + @if feature-flag-enabled('components-x') { + @include breadcrumb-experimental; + } @else { + @include breadcrumb; + } +} diff --git a/src/components/checkbox/README.md b/src/components/checkbox/README.md index 336a2f048e90..7408b63ac3ab 100644 --- a/src/components/checkbox/README.md +++ b/src/components/checkbox/README.md @@ -2,16 +2,22 @@ #### Public Methods -| Name | Params | Description | -|-----------|-------------------------------|-----------------------------------------------------------------------------------------------------------------------| -| setState | state: `String` ['true', 'false', 'mixed'] | Can be used to set the checkbox to `true`(checked), `false`(unchecked) or `mixed` (indeterminate) | -| setDisabled | state: `Boolean` | Can be used to set the checkbox to disabled, needed for the `label > input` | +| Name | Params | Description | +| ----------- | ------------------------------------------ | ------------------------------------------------------------------------------------------------- | +| setState | state: `String` ['true', 'false', 'mixed'] | Can be used to set the checkbox to `true`(checked), `false`(unchecked) or `mixed` (indeterminate) | +| setDisabled | state: `Boolean` | Can be used to set the checkbox to disabled, needed for the `label > input` | #### Options -| Option | Default Selector | Description | -|---------------------|------------------------------------------------|--| -| selectorInit | .bx--checkbox | The CSS selector to find checkbox | +| Option | Default Selector | Description | +| --------------------------------- | ---------------------------------- | -------------------------------------------------------------------------- | +| selectorInit | .bx--checkbox | The CSS selector to find checkbox | +| selectorContainedCheckboxState | [data-contained-checkbox-state] | The CSS selector to find a container of checkbox preserving checked state | +| selectorContainedCheckboxDisabled | [data-contained-checkbox-disabled] | The CSS selector to find a container of checkbox preserving disabled state | +| classLabel | .bx--checkbox-label | The CSS class for the label | +| classLabelFocused | .bx--checkbox-label\_\_focus | The CSS class for the focused label | +| attribContainedCheckboxState | data-contained-checkbox-state | The attribute name for the checked state of contained checkbox | +| attribContainedCheckboxDisabled | data-contained-checkbox-disabled | The attribute name for the disabled state of contained checkbox | ### FAQ @@ -41,14 +47,12 @@ With `label` wrapping `input` ``` -Note: You no longer need to include a SVG for the checkmark to render. +Note: You no longer need to include a SVG for the checkmark to render. #### Fieldset and Legend As a best practice, groups of checkboxes should make use of `
` and `` (see Form for details). This is especially true for forms submitting data. -But, there are exceptions to the rule. For example, Data Tables make use of checkboxes as a way to select rows of data. +But, there are exceptions to the rule. For example, Data Tables make use of checkboxes as a way to select rows of data. Checkboxes in this context would represent an entire row of data in its associated table row. - - diff --git a/src/components/checkbox/_checkbox.scss b/src/components/checkbox/_checkbox.scss index caba22c39d6e..60c72750ee7c 100644 --- a/src/components/checkbox/_checkbox.scss +++ b/src/components/checkbox/_checkbox.scss @@ -59,8 +59,8 @@ height: 5px; background: none; /*rtl:ignore*/ - border-left: 2px solid $inverse-01; - border-bottom: 2px solid $inverse-01; + border-left: 1px solid $inverse-01; + border-bottom: 1px solid $inverse-01; /*rtl:ignore*/ transform: scale(0) rotate(-45deg); position: absolute; @@ -213,11 +213,11 @@ @include font-family; @include font-smoothing; @include typescale('zeta'); - line-height: 1.25rem; + line-height: 1.5rem; position: relative; display: flex; cursor: pointer; - padding-left: rem(24px); + padding-left: rem(26px); //width of checkbox 16px + 10px of padding min-height: rem(24px); user-select: none; } @@ -230,8 +230,8 @@ // According to the spec, we'll want the bounding box for our checkbox to // be 16px. The border size will be what will be updated during the // different checkbox states. - width: rem(12px); - height: rem(12px); + width: rem(16px); + height: rem(16px); margin: rem(2px); // We need to position the pseudo-element absolutely in the space that we've @@ -243,53 +243,25 @@ top: rem(2px); background-color: $ui-02; border: 1px solid $ui-05; - border-radius: 1px; + border-radius: 2px; } // Create the appearance of the check in the `after` pseudo-element .#{$prefix}--checkbox-label::after { content: ''; position: absolute; - left: rem(5px); - top: rem(7px); - width: rem(6px); + left: rem(6px); + top: rem(8px); + width: rem(7px); height: rem(3px); background: none; - border-left: 1px solid $ui-02; - border-bottom: 1px solid $ui-02; + border-left: 2px solid $ui-02; + border-bottom: 2px solid $ui-02; transform: scale(0) rotate(-45deg); transform-origin: bottom right; margin-top: rem(-3px); } - //---------------------------------------------- - // Hover - // --------------------------------------------- - - // Hover color for border - .#{$prefix}--checkbox-wrapper:hover .#{$prefix}--checkbox-label::before { - border-color: $ibm-colors__gray--70; - } - - //---------------------------------------------- - // Focus - // --------------------------------------------- - - // Update border color and width for focus - .#{$prefix}--checkbox:focus + .#{$prefix}--checkbox-label::before, - .#{$prefix}--checkbox-label__focus::before, - .#{$prefix}--checkbox-wrapper:hover .#{$prefix}--checkbox-label__focus::before { - border-color: $brand-01; - border-width: 3px; - } - - // Handle case of checked and focused - .#{$prefix}--checkbox:checked:focus + .#{$prefix}--checkbox-label::before, - .#{$prefix}--checkbox-label[data-contained-checkbox-state='true'].#{$prefix}--checkbox-label__focus::before { - background-color: $brand-01; - border-color: $brand-01; - } - //---------------------------------------------- // Checked // --------------------------------------------- @@ -316,20 +288,44 @@ transform: scale(1) rotate(0deg); border-left: 0 solid $inverse-01; border-bottom: 1.5px solid $inverse-01; - width: rem(6px); - top: rem(9.25px); + width: rem(8px); + top: rem(11px); + } + + //---------------------------------------------- + // Focus + // --------------------------------------------- + + // Unchecked + .#{$prefix}--checkbox:focus + .#{$prefix}--checkbox-label::before, + .#{$prefix}--checkbox-label__focus::before, + // Checked + .#{$prefix}--checkbox:checked:focus + .#{$prefix}--checkbox-label::before, + .#{$prefix}--checkbox-label[data-contained-checkbox-state='true'].#{$prefix}--checkbox-label__focus::before, + // Indeterminate + .#{$prefix}--checkbox:indeterminate:focus + .#{$prefix}--checkbox-label::before, + .#{$prefix}--checkbox-label[data-contained-checkbox-state='mixed'].#{$prefix}--checkbox-label__focus::before { + // We can't use outline here because of the rounded corners so have to increase the width/height to fake an outline. + border-color: $brand-01; + border-width: 3px; + width: rem(20px); + height: rem(20px); + left: -2px; + top: 0; } //---------------------------------------------- // Disabled // --------------------------------------------- - .#{$prefix}--checkbox:disabled + .#{$prefix}--checkbox-label { + .#{$prefix}--checkbox:disabled + .#{$prefix}--checkbox-label, + .#{$prefix}--checkbox-label[data-contained-checkbox-disabled='true'] { cursor: not-allowed; color: $text-03; } - .#{$prefix}--checkbox:disabled + .#{$prefix}--checkbox-label::before { + .#{$prefix}--checkbox:disabled + .#{$prefix}--checkbox-label::before, + .#{$prefix}--checkbox-label[data-contained-checkbox-disabled='true']::before { border-color: $text-03; } } diff --git a/src/components/checkbox/checkbox.hbs b/src/components/checkbox/checkbox.hbs index f8222e7f7d95..dc3a8b60c49c 100644 --- a/src/components/checkbox/checkbox.hbs +++ b/src/components/checkbox/checkbox.hbs @@ -34,8 +34,8 @@
-
diff --git a/src/components/checkbox/checkbox.js b/src/components/checkbox/checkbox.js index 9c01116431c1..0743431eddc7 100644 --- a/src/components/checkbox/checkbox.js +++ b/src/components/checkbox/checkbox.js @@ -1,3 +1,4 @@ +import settings from '../../globals/js/settings'; import mixin from '../../globals/js/misc/mixin'; import createComponent from '../../globals/js/mixins/create-component'; import initComponentBySearch from '../../globals/js/mixins/init-component-by-search'; @@ -48,8 +49,8 @@ class Checkbox extends mixin(createComponent, initComponentBySearch, handles) { this.element.checked = true; // nested checkboxes inside labels - if (this.element.parentElement.classList.contains('bx--checkbox-label')) { - this.element.parentElement.setAttribute('data-contained-checkbox-state', 'true'); + if (this.element.parentElement.classList.contains(this.options.classLabel)) { + this.element.parentElement.setAttribute(this.options.attribContainedCheckboxState, 'true'); } } else if (this.element.checked === false) { this.element.removeAttribute('checked'); @@ -57,21 +58,21 @@ class Checkbox extends mixin(createComponent, initComponentBySearch, handles) { this.element.checked = false; // nested checkboxes inside labels - if (this.element.parentElement.classList.contains('bx--checkbox-label')) { - this.element.parentElement.setAttribute('data-contained-checkbox-state', 'false'); + if (this.element.parentElement.classList.contains(this.options.classLabel)) { + this.element.parentElement.setAttribute(this.options.attribContainedCheckboxState, 'false'); } } } _handleFocus() { - if (this.element.parentElement.classList.contains('bx--checkbox-label')) { - this.element.parentElement.classList.add('bx--checkbox-label__focus'); + if (this.element.parentElement.classList.contains(this.options.classLabel)) { + this.element.parentElement.classList.add(this.options.classLabelFocused); } } _handleBlur() { - if (this.element.parentElement.classList.contains('bx--checkbox-label')) { - this.element.parentElement.classList.remove('bx--checkbox-label__focus'); + if (this.element.parentElement.classList.contains(this.options.classLabel)) { + this.element.parentElement.classList.remove(this.options.classLabelFocused); } } @@ -90,9 +91,9 @@ class Checkbox extends mixin(createComponent, initComponentBySearch, handles) { this.element.indeterminate = state === stateChangeTypes.mixed; this.element.checked = state === stateChangeTypes.true; - const container = this.element.closest('[data-contained-checkbox-state]'); + const container = this.element.closest(this.options.selectorContainedCheckboxState); if (container) { - container.setAttribute('data-contained-checkbox-state', state); + container.setAttribute(this.options.attribContainedCheckboxState, state); } } @@ -105,9 +106,9 @@ class Checkbox extends mixin(createComponent, initComponentBySearch, handles) { } else if (value === false) { this.element.removeAttribute('disabled'); } - const container = this.element.closest('[data-contained-checkbox-disabled]'); + const container = this.element.closest(this.options.selectorContainedCheckboxDisabled); if (container) { - container.setAttribute('data-contained-checkbox-disabled', value); + container.setAttribute(this.options.attribContainedCheckboxDisabled, value); } } @@ -118,8 +119,8 @@ class Checkbox extends mixin(createComponent, initComponentBySearch, handles) { if (this.element.indeterminate === true) { this.element.setAttribute('aria-checked', 'mixed'); } - if (this.element.parentElement.classList.contains('bx--checkbox-label') && this.element.indeterminate === true) { - this.element.parentElement.setAttribute('data-contained-checkbox-state', 'mixed'); + if (this.element.parentElement.classList.contains(this.options.classLabel) && this.element.indeterminate === true) { + this.element.parentElement.setAttribute(this.options.attribContainedCheckboxState, 'mixed'); } } @@ -127,14 +128,14 @@ class Checkbox extends mixin(createComponent, initComponentBySearch, handles) { if (this.element.checked === true) { this.element.setAttribute('aria-checked', 'true'); } - if (this.element.parentElement.classList.contains('bx--checkbox-label') && this.element.checked) { - this.element.parentElement.setAttribute('data-contained-checkbox-state', 'true'); + if (this.element.parentElement.classList.contains(this.options.classLabel) && this.element.checked) { + this.element.parentElement.setAttribute(this.options.attribContainedCheckboxState, 'true'); } - if (this.element.parentElement.classList.contains('bx--checkbox-label')) { - this.element.parentElement.setAttribute('data-contained-checkbox-disabled', 'false'); + if (this.element.parentElement.classList.contains(this.options.classLabel)) { + this.element.parentElement.setAttribute(this.options.attribContainedCheckboxDisabled, 'false'); } - if (this.element.parentElement.classList.contains('bx--checkbox-label') && this.element.disabled) { - this.element.parentElement.setAttribute('data-contained-checkbox-disabled', 'true'); + if (this.element.parentElement.classList.contains(this.options.classLabel) && this.element.disabled) { + this.element.parentElement.setAttribute(this.options.attribContainedCheckboxDisabled, 'true'); } } @@ -152,10 +153,26 @@ class Checkbox extends mixin(createComponent, initComponentBySearch, handles) { * @member Checkbox.options * @type {Object} * @property {string} selectorInit The data attribute to find copy button UIs. + * @property {string} selectorContainedCheckboxState The CSS selector to find a container of checkbox preserving checked state. + * @property {string} selectorContainedCheckboxDisabled + * The CSS selector to find a container of checkbox preserving disabled state. + * @property {string} classLabel The CSS class for the label. + * @property {string} classLabelFocused The CSS class for the focused label. + * @property {string} attribContainedCheckboxState The attribute name for the checked state of contained checkbox. + * @property {string} attribContainedCheckboxDisabled The attribute name for the disabled state of contained checkbox. */ - static options = { - selectorInit: '.bx--checkbox', - }; + static get options() { + const { prefix } = settings; + return { + selectorInit: `.${prefix}--checkbox`, + selectorContainedCheckboxState: '[data-contained-checkbox-state]', + selectorContainedCheckboxDisabled: '[data-contained-checkbox-disabled]', + classLabel: `${prefix}--checkbox-label`, + classLabelFocused: `${prefix}--checkbox-label__focus`, + attribContainedCheckboxState: 'data-contained-checkbox-state', + attribContainedCheckboxDisabled: 'data-contained-checkbox-disabled', + }; + } static stateChangeTypes = stateChangeTypes; } diff --git a/src/components/code-snippet/code-snippet.js b/src/components/code-snippet/code-snippet.js index c3f02943a593..f64ca9cca3a2 100644 --- a/src/components/code-snippet/code-snippet.js +++ b/src/components/code-snippet/code-snippet.js @@ -1,3 +1,4 @@ +import settings from '../../globals/js/settings'; import mixin from '../../globals/js/misc/mixin'; import createComponent from '../../globals/js/mixins/create-component'; import initComponentBySearch from '../../globals/js/mixins/init-component-by-search'; @@ -60,16 +61,19 @@ class CodeSnippet extends mixin(createComponent, initComponentBySearch, handles) * @type {Object} * @property {string} selectorInit The data attribute to find code snippet UIs. */ - static options = { - selectorInit: '[data-code-snippet]', - attribShowMoreText: 'data-show-more-text', - attribShowLessText: 'data-show-less-text', - minHeight: 288, - classExpanded: 'bx--snippet--expand', - classExpandBtn: '.bx--snippet-btn--expand', - classExpandText: '.bx--snippet-btn--text', - classHideExpand: 'bx--snippet-btn--expand--hide', - }; + static get options() { + const { prefix } = settings; + return { + selectorInit: '[data-code-snippet]', + attribShowMoreText: 'data-show-more-text', + attribShowLessText: 'data-show-less-text', + minHeight: 288, + classExpanded: `${prefix}--snippet--expand`, + classExpandBtn: `.${prefix}--snippet-btn--expand`, + classExpandText: `.${prefix}--snippet-btn--text`, + classHideExpand: `${prefix}--snippet-btn--expand--hide`, + }; + } } export default CodeSnippet; diff --git a/src/components/date-picker/date-picker.js b/src/components/date-picker/date-picker.js index 2696c349bdfc..b1f9a8254e76 100644 --- a/src/components/date-picker/date-picker.js +++ b/src/components/date-picker/date-picker.js @@ -335,7 +335,7 @@ class DatePicker extends mixin(createComponent, initComponentBySearch, handles) classWeekday: `${prefix}--date-picker__weekday`, classDay: `${prefix}--date-picker__day`, classFocused: `${prefix}--focused`, - classVisuallyHidden: 'bx--visually-hidden', + classVisuallyHidden: `${prefix}--visually-hidden`, attribType: 'data-date-picker-type', dateFormat: 'm/d/Y', }; diff --git a/src/components/text-input/text-input.config.js b/src/components/text-input/text-input.config.js index a8d1ef37e541..fa578b5053c1 100644 --- a/src/components/text-input/text-input.config.js +++ b/src/components/text-input/text-input.config.js @@ -19,11 +19,19 @@ module.exports = { }, }, { - name: 'toggle password visibility', - label: 'Text Input (toggle password visibility)', + name: 'password', + label: 'Password Input', context: { password: true, }, }, + { + name: 'password--light', + label: 'Password Input (Light)', + context: { + light: true, + password: true, + }, + }, ], }; diff --git a/src/components/text-input/text-input.hbs b/src/components/text-input/text-input.hbs index 3b035646c0e1..56b479b082ce 100644 --- a/src/components/text-input/text-input.hbs +++ b/src/components/text-input/text-input.hbs @@ -9,7 +9,7 @@ - + @@ -29,7 +29,7 @@ - + @@ -51,7 +51,7 @@ - + @@ -73,7 +73,7 @@ - + @@ -96,7 +96,7 @@ - + diff --git a/src/components/ui-shell/_platform-header.scss b/src/components/ui-shell/_platform-header.scss index 22fe1c38f777..8d205f4a5813 100644 --- a/src/components/ui-shell/_platform-header.scss +++ b/src/components/ui-shell/_platform-header.scss @@ -1,6 +1,7 @@ @import '../../globals/scss/functions'; @import '../../globals/scss/helper-mixins'; @import '../../globals/scss/vars'; +@import '../../globals/scss/typography'; @import 'theme'; @import 'functions'; @@ -44,16 +45,16 @@ fill: $shell-header-icon-01; } - .#{$prefix}--platform-header__menu-trigger { - margin-right: units(1); - } - //-------------------------------------------------------------------------- // Platform Header - Name //-------------------------------------------------------------------------- a.#{$prefix}--platform-header__name { + @include typescale('zeta'); + display: flex; + align-items: center; + height: 100%; + padding: 0 units(2); text-decoration: none; - font-size: rem(14px); font-weight: 400; letter-spacing: 0.1px; line-height: 20px; @@ -69,6 +70,11 @@ color: $shell-header-text-01; } + a.#{$prefix}--platform-header__name:focus { + outline: none; + box-shadow: inset 0 0 0 4px $shell-brand-01; + } + //-------------------------------------------------------------------------- // Platform Header - Navigation //-------------------------------------------------------------------------- @@ -78,7 +84,7 @@ .#{$prefix}--platform-header__links { display: flex; - margin-left: units(3); + margin-left: units(1); height: 100%; } @@ -86,39 +92,88 @@ height: 100%; } - .#{$prefix}--platform-header__link-item:hover { - background-color: $shell-ui-02; - } - - a.#{$prefix}--platform-header__link { + .#{$prefix}--platform-header__dropdown-item, + .#{$prefix}--platform-header__link, + .#{$prefix}--dropdown-item__menu-item a { + @include typescale('zeta'); + position: relative; display: flex; align-items: center; height: 100%; - - // TODO: sync type styles - font-size: rem(14px); line-height: 18px; letter-spacing: 0; text-decoration: none; font-weight: 400; - padding: 0 units(2); + cursor: pointer; + } + + .#{$prefix}--platform-header__dropdown-item:hover, + .#{$prefix}--platform-header__link:hover { + background-color: $shell-ui-02; + } + + .#{$prefix}--platform-header__dropdown-item:focus, + .#{$prefix}--dropdown-item__menu-item a:focus { + outline: none; + box-shadow: inset 0 0 0 4px $shell-brand-01; + } + + .#{$prefix}--platform-header__arrow { + fill: $shell-header-text-01; + margin-left: units(2); + } + + .#{$prefix}--platform-header__dropdown-item:focus .#{$prefix}--platform-header__arrow, + .#{$prefix}--platform-header__dropdown-item:hover .#{$prefix}--platform-header__arrow { + transform: rotate(180deg); + } + + .#{$prefix}--platform-header__dropdown-item:hover .#{$prefix}--dropdown-item__menu { + display: block; + } + + .#{$prefix}--dropdown-item__menu { + display: none; + position: absolute; + top: 100%; + left: 0; + background-color: $shell-ui-02; + padding: 0; + box-shadow: 0 8px 16px 0 rgba($shell-ui-03, 0.25); + } + + .#{$prefix}--dropdown-item__menu-item { + width: units(25); + height: units(6); + } + + .#{$prefix}--dropdown-item__menu-item a { + color: $shell-header-text-01; + } + + .#{$prefix}--dropdown-item__menu-item a:hover { + background-color: $shell-header-bg-06; + color: $shell-header-text-01; + } + + .#{$prefix}--platform-header__link { border: units(0.5) solid transparent; } - a.#{$prefix}--platform-header__link, - a.#{$prefix}--platform-header__link:hover { + .#{$prefix}--platform-header__link, + .#{$prefix}--platform-header__link:hover { color: $shell-header-text-01; } - a.#{$prefix}--platform-header__link:active { + .#{$prefix}--platform-header__link:active { background-color: $shell-ui-02; } // Safari: use the active style for links to simulate the focus // styles since links in safari don't display the focus styles. - a.#{$prefix}--platform-header__link:active, - a.#{$prefix}--platform-header__link:focus { + .#{$prefix}--platform-header__link:active, + .#{$prefix}--platform-header__link:focus { border-color: $shell-brand-01; outline: none; } diff --git a/src/components/ui-shell/_product-switcher.scss b/src/components/ui-shell/_product-switcher.scss new file mode 100644 index 000000000000..7eca0d973de9 --- /dev/null +++ b/src/components/ui-shell/_product-switcher.scss @@ -0,0 +1,174 @@ +@import '../../globals/scss/functions'; +@import '../../globals/scss/helper-mixins'; +@import '../../globals/scss/vars'; +@import '../../globals/scss/typography'; +@import 'theme'; +@import 'functions'; + +@mixin product-switcher { + //-------------------------------------------------------------------------- + // Platform Global Panel + //-------------------------------------------------------------------------- + .#{$prefix}--panel--overlay { + position: fixed; + top: units(6); + right: 0; + bottom: 0; + width: units(32); + will-change: transform; + transform: translate3d(100%, 0, 0); + padding: 1rem 0; + overflow-y: auto; + z-index: 1000; + background-color: $shell-header-bg-02; + height: 100%; + overflow-x: hidden; + } + + .#{$prefix}--panel--expanded { + box-shadow: 0 8px 16px 0 rgba($shell-ui-03, 0.25); + transform: translate3d(0, 0, 0); + transition: transform 0.11s cubic-bezier(0.2, 0, 0.38, 0.9); + } + + //-------------------------------------------------------------------------- + // Platform Switcher - Search + //-------------------------------------------------------------------------- + .#{$prefix}--product-switcher__search { + padding: 0 units(2); + margin-bottom: units(3); + } + + .#{$prefix}--search--shell input { + background-color: $shell-header-bg-05; + } + + //-------------------------------------------------------------------------- + // Platform Switcher - Buttons + //-------------------------------------------------------------------------- + .#{$prefix}--product-switcher__subheader, + .#{$prefix}--product-switcher__all-btn { + @include typescale('omega'); + padding: units(1); + color: $shell-header-text-03; + } + + .#{$prefix}--product-switcher__subheader { + padding-left: units(7); + } + + .#{$prefix}--product-switcher__all-btn { + padding-left: units(7); + } + + .#{$prefix}--product-switcher__all-btn, + .#{$prefix}--product-switcher__back-btn { + display: inline-block; + background: transparent; + width: 100%; + border: none; + color: $shell-header-link; + cursor: pointer; + text-align: left; + } + + .#{$prefix}--product-switcher__all-btn:hover, + .#{$prefix}--product-switcher__back-btn:hover { + text-decoration: underline; + } + + .#{$prefix}--product-switcher__all-btn:focus, + .#{$prefix}--product-switcher__back-btn:focus { + outline: none; + box-shadow: inset 0 0 0 3px $shell-header-link; + } + + .#{$prefix}--product-switcher__back-btn { + display: flex; + align-items: center; + @include typescale('omega'); + padding: units(1) units(2); + } + + .#{$prefix}--product-switcher__back-arrow { + fill: $shell-header-link; + margin-right: units(2); + } + + //-------------------------------------------------------------------------- + // Platform Switcher - Product List + //-------------------------------------------------------------------------- + .#{$prefix}--product-list__item { + cursor: pointer; + display: flex; + justify-content: space-between; + } + + .#{$prefix}--product-list__item:hover { + background: $shell-header-bg-03; + } + + .#{$prefix}--product-link { + display: flex; + flex-direction: row; + align-items: center; + width: 100%; + padding: units(1) units(2); + text-decoration: none; + } + + .#{$prefix}--product-link:focus { + outline: none; + box-shadow: inset 0 0 0 3px $shell-header-link; + } + + .#{$prefix}--product-switcher__icon { + margin-right: units(2); + } + + .#{$prefix}--product-link__name { + @include typescale('zeta'); + margin-left: 0.25rem; + font-weight: 400; + color: $shell-header-text-02; + } + + .#{$prefix}--overflow-menu { + display: none; + justify-content: center; + align-items: center; + width: units(5); + } + + .#{$prefix}--overflow-menu > svg { + fill: $shell-header-text-02; + } + + .#{$prefix}--overflow-menu:hover { + background: $shell-header-bg-04; + } + + .#{$prefix}--overflow-menu:hover > svg { + fill: $shell-header-text-02; + } + + .#{$prefix}--overflow-menu:focus { + display: flex; + outline: none; + box-shadow: inset 0 0 0 3px $shell-header-link; + } + + .#{$prefix}--overflow-menu-options__option:hover { + background: $shell-header-bg-03; + } + + .#{$prefix}--product-list__item:hover .#{$prefix}--overflow-menu { + display: flex; + } +} + +@include exports('product-switcher') { + @if feature-flag-enabled('ui-shell') { + @include product-switcher; + } +} diff --git a/src/components/ui-shell/_theme.scss b/src/components/ui-shell/_theme.scss index 634707eb9a59..dde484db48e6 100644 --- a/src/components/ui-shell/_theme.scss +++ b/src/components/ui-shell/_theme.scss @@ -7,14 +7,23 @@ $shell-header-bg-02: #f3f3f3; // Panel item hover $shell-header-bg-03: #dcdcdc; +// Panel overflow hover +$shell-header-bg-04: #bebebe; + +// Panel input background +$shell-header-bg-05: #fff; + +// Header Nav Link hober +$shell-header-bg-06: #4c4c4c; + // Primary text in header-panel, Item text $shell-header-text-01: #f3f3f3; // Secondary text in header-panel, Item text -$shell-header-text-02: #252525; +$shell-header-text-02: #171717; // Secondary text in header-panel, Category label -$shell-header-text-03: #565656; +$shell-header-text-03: #8c8c8c; // Header bar icons $shell-header-icon-01: #f3f3f3; @@ -31,3 +40,4 @@ $shell-header-icon-selected: #0062ff; // Temporary token $shell-brand-01: #0062ff; $shell-ui-02: #3d3d3d; +$shell-ui-03: #000; diff --git a/src/components/ui-shell/_ui-shell.scss b/src/components/ui-shell/_ui-shell.scss index a887f91c0ff2..84998ebcb28d 100644 --- a/src/components/ui-shell/_ui-shell.scss +++ b/src/components/ui-shell/_ui-shell.scss @@ -2,5 +2,6 @@ @import 'variables'; @import 'functions'; @import 'platform-header'; +@import 'product-switcher'; @import 'platform-side-nav'; @import 'platform-nav'; diff --git a/src/components/ui-shell/platform-header-nav.hbs b/src/components/ui-shell/platform-header-nav.hbs index d5fa04abad00..17139847640b 100644 --- a/src/components/ui-shell/platform-header-nav.hbs +++ b/src/components/ui-shell/platform-header-nav.hbs @@ -1,11 +1,25 @@ - + \ No newline at end of file diff --git a/src/components/ui-shell/product-switcher.hbs b/src/components/ui-shell/product-switcher.hbs new file mode 100644 index 000000000000..6457c3bc1b7d --- /dev/null +++ b/src/components/ui-shell/product-switcher.hbs @@ -0,0 +1,103 @@ +{{#if switcher.state.expanded}} +