diff options
Diffstat (limited to 'apps/web-shared/src/styles/components')
23 files changed, 2595 insertions, 0 deletions
diff --git a/apps/web-shared/src/styles/components/alert.scss b/apps/web-shared/src/styles/components/alert.scss new file mode 100644 index 0000000..9d9008d --- /dev/null +++ b/apps/web-shared/src/styles/components/alert.scss @@ -0,0 +1,69 @@ +@use '../base' as *; + +/* -------------------------------- + +File#: _1_alert +Title: Alert +Descr: Feedback message +Usage: codyhouse.co/license + +-------------------------------- */ + +.alert { + background-color: alpha(var(--color-primary), 0.2); + color: var(--color-contrast-higher); + + // hide element + position: absolute; + clip: rect(1px, 1px, 1px, 1px); + clip-path: inset(50%); +} + +.alert__icon { + color: var(--color-primary); +} + +.alert__close-btn { + display: inline-block; + + .icon { + display: block; + } + + &:hover { + opacity: 0.7; + } +} + +// themes +.alert--success { + background-color: alpha(var(--color-success), 0.2); + + .alert__icon { + color: var(--color-success); + } +} + +.alert--error { + background-color: alpha(var(--color-error), 0.2); + + .alert__icon { + color: var(--color-error); + } +} + +.alert--warning { + background-color: alpha(var(--color-warning), 0.2); + + .alert__icon { + color: var(--color-warning); + } +} + +// toggle visibility +.alert--is-visible { + position: static; + clip: auto; + clip-path: none; +} + diff --git a/apps/web-shared/src/styles/components/autocomplete.scss b/apps/web-shared/src/styles/components/autocomplete.scss new file mode 100644 index 0000000..cde3632 --- /dev/null +++ b/apps/web-shared/src/styles/components/autocomplete.scss @@ -0,0 +1,76 @@ +@use '../base' as *; +@use 'circle-loader.scss' as *; + +/* -------------------------------- + +File#: _2_autocomplete +Title: Autocomplete +Descr: Autocomplete plugin for input elements +Usage: codyhouse.co/license + +-------------------------------- */ + +:root { + --autocomplete-dropdown-vertical-gap: 4px; // gap between input and results list + --autocomplete-dropdown-max-height: 150px; + --autocomplete-dropdown-scrollbar-width: 6px; // custom scrollbar width - webkit browsers +} + +// results dropdown +.autocomplete__results { + position: absolute; + z-index: var(--z-index-popover, 5); + width: 100%; + left: 0; + top: calc(100% + var(--autocomplete-dropdown-vertical-gap)); + background-color: var(--color-bg-light); + box-shadow: var(--inner-glow), var(--shadow-md); + border-radius: var(--radius-md); + opacity: 0; + visibility: hidden; + overflow: hidden; + + .autocomplete--results-visible & { + opacity: 1; + visibility: visible; + } +} + +.autocomplete__list { + max-height: var(--autocomplete-dropdown-max-height); + overflow: auto; + -webkit-overflow-scrolling: touch; + + // custom scrollbar + &::-webkit-scrollbar { // scrollbar width + width: var(--autocomplete-dropdown-scrollbar-width); + } + + &::-webkit-scrollbar-track { // progress bar + background-color: alpha(var(--color-contrast-higher), 0.08); + border-radius: 0; + } + + &::-webkit-scrollbar-thumb { // handle + background-color: alpha(var(--color-contrast-higher), 0.12); + border-radius: 0; + + &:hover { + background-color: alpha(var(--color-contrast-higher), 0.2); + } + } +} + +// single result item +.autocomplete__item { + cursor: pointer; + + &:hover { + background-color: alpha(var(--color-contrast-higher), 0.075); + } + + &:focus { + outline: none; + background-color: alpha(var(--color-primary), 0.15); + } +} diff --git a/apps/web-shared/src/styles/components/btn-states.scss b/apps/web-shared/src/styles/components/btn-states.scss new file mode 100644 index 0000000..a2fc6c5 --- /dev/null +++ b/apps/web-shared/src/styles/components/btn-states.scss @@ -0,0 +1,51 @@ +@use '../base' as *; + +/* -------------------------------- + +File#: _1_btn-states +Title: Buttons states +Descr: Multi-state button elements +Usage: codyhouse.co/license + +-------------------------------- */ + +.btn__content-a { + display: inline-flex; +} + +.btn__content-b { + display: none; +} + +.btn__content-a, .btn__content-b { + align-items: center; +} + +.btn--state-b { + .btn__content-a { + display: none; + } + + .btn__content-b { + display: inline-block; // fallback + display: inline-flex; + } +} + +/* preserve button width when switching from state A to state B */ +.btn--preserve-width { + .btn__content-b { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + justify-content: center; + } + + &.btn--state-b .btn__content-a { + display: inline-block; // fallback + display: inline-flex; + visibility: hidden; + } +} diff --git a/apps/web-shared/src/styles/components/chip.scss b/apps/web-shared/src/styles/components/chip.scss new file mode 100644 index 0000000..1bb93db --- /dev/null +++ b/apps/web-shared/src/styles/components/chip.scss @@ -0,0 +1,117 @@ +@use '../base' as *; + +/* -------------------------------- + +File#: _1_chips +Title: Chips +Descr: A list of compact pieces of information +Usage: codyhouse.co/license + +-------------------------------- */ + +.chip { + /* reset - in case the class is applied to a <button> or an <a> */ + border: 0; + color: inherit; + line-height: 1; + appearance: none; + + display: inline-flex; + align-items: center; + border-radius: var(--radius-sm); + + background-color: alpha(var(--color-contrast-higher), 0.1); + padding: var(--space-xxxs) 0; +} + +.chip--outline { + background-color: transparent; + box-shadow: inset 0 0 0 1px alpha(var(--color-contrast-higher), 0.25); +} + +.chip--error { + background-color: alpha(var(--color-error), 0.2); + color: var(--color-contrast-higher); +} + +.chip--success { + background-color: alpha(var(--color-success), 0.2); + color: var(--color-contrast-higher); +} + +.chip--warning { + background-color: alpha(var(--color-warning), 0.2); + color: var(--color-contrast-higher); +} + +.chip--interactive { + cursor: pointer; + + &:hover { + background-color: alpha(var(--color-contrast-higher), 0.2); + } + + &:focus { + outline: none; + box-shadow: 0 0 0 3px alpha(var(--color-contrast-higher), 0.3); + } + + &:focus:not(:focus-visible) { + box-shadow: none; + } +} + +.chip__label { + padding: 0 var(--space-xxs); +} + +.chip__img { + display: block; + width: 1.5em; + height: 1.5em; + border-radius: 50%; + object-fit: cover; +} + +.chip__icon-wrapper { + display: flex; + width: 1.5em; + height: 1.5em; + border-radius: 50%; + background-color: alpha(var(--color-contrast-higher), 0.95); + color: var(--color-bg); /* icon color */ + + .icon { + display: block; + margin: auto; + } +} + +.chip__btn { + @include reset; + display: flex; + width: 1em; + height: 1em; + background-color: alpha(var(--color-contrast-higher), 0.2); + border-radius: 50%; + cursor: pointer; + margin-right: 7px; + + .icon { + display: block; + margin: 0 auto; + } + + &:hover { + background-color: alpha(var(--color-contrast-higher), 0.3); + } + + &:focus { + outline: none; + box-shadow: 0 0 0 2px alpha(var(--color-contrast-higher), 0.5); + } + + &:focus:not(:focus-visible) { + box-shadow: none; + } +} diff --git a/apps/web-shared/src/styles/components/circle-loader.scss b/apps/web-shared/src/styles/components/circle-loader.scss new file mode 100644 index 0000000..5116d39 --- /dev/null +++ b/apps/web-shared/src/styles/components/circle-loader.scss @@ -0,0 +1,315 @@ +@use '../base' as *; + +/* -------------------------------- + +File#: _1_circle-loader +Title: Circle Loader +Descr: A collection of animated circle loaders +Usage: codyhouse.co/license + +-------------------------------- */ + +:root { + // v1 + --circle-loader-v1-size: 48px; + --circle-loader-v1-stroke-width: 4px; + + // v2 + --circle-loader-v2-size: 64px; + --circle-loader-v2-stroke-width: 2; + + // v3 + --circle-loader-v3-size: 64px; + + // v4 + --circle-loader-v4-size: 48px; + + // v5 + --circle-loader-v5-size: 64px; + + // v6 + --circle-loader-v6-size: 48px; +} + +.circle-loader { + position: relative; + display: inline-block; +} + +@supports (animation-name: this) { + .circle-loader__label { + @include srHide; // show label only to screen readers if animations are supported + } +} + +// loader v1 - rotation +@supports (animation-name: this) { + .circle-loader--v1 { + transform: rotate(45deg); + will-change: transform; + animation: circle-loader-1 0.75s infinite var(--ease-in-out); + + .circle-loader__circle { + width: var(--circle-loader-v1-size); // loader width + height: var(--circle-loader-v1-size); // loader height + border-width: var(--circle-loader-v1-stroke-width); // loader stroke width + border-style: solid; + border-color: alpha(var(--color-primary), 0.2); // loader base color + border-radius: 50%; + + &::after { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + border-width: inherit; + border-style: inherit; + border-color: transparent; + border-top-color: var(--color-primary); // loader fill color + border-radius: inherit; + } + } + } +} + +@keyframes circle-loader-1 { + 0% { + transform: rotate(45deg); + } + + 100% { + transform: rotate(405deg); + } +} + +// loader v2 - filling +@supports (animation-name: this) { + .circle-loader--v2 { + will-change: transform; + animation: circle-loader-spinning-main 1.4s infinite linear; + + .circle-loader__svg { + display: block; + width: var(--circle-loader-v2-size); + height: var(--circle-loader-v2-size); + color: var(--color-primary); // loader color + + > * { + stroke-width: var(--circle-loader-v2-stroke-width); // loader stroke width + } + } + + .circle-loader__base { + opacity: 0.2; + } + + .circle-loader__fill { + stroke-linecap: round; // optional - remove if you prefer butt caps + stroke-dashoffset: 0; + stroke-dasharray: 90 120; + transform-origin: 50% 50%; + transform: rotate(45deg); + animation: circle-loader-dash 1.4s infinite; + } + } +} + +@keyframes circle-loader-dash { + 0%, 20% { + stroke-dashoffset: 0; + transform: rotate(0); + } + + 50%, 70% { + stroke-dashoffset: 80; + transform: rotate(270deg); + } + + 100% { + stroke-dashoffset: 0; + transform: rotate(360deg); + } +} + +@keyframes circle-loader-spinning-main { + to { + transform: rotate(360deg); + } +} + +// loader v3 - drop +@supports (animation-name: this) { + .circle-loader--v3 { + width: var(--circle-loader-v3-size); // loader width + height: var(--circle-loader-v3-size); // loader height + + .circle-loader__circle { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + border-radius: 50%; + background-color: var(--color-primary); // loader color + transform: scale(0); + opacity: 0.8; + will-change: transform, opacity; + animation: circle-loader-3 1.2s infinite; + } + + .circle-loader__circle--2nd { + animation-delay: 0.6s; // this should be half the duration of animation + } + } +} + +@keyframes circle-loader-3 { + to { + transform: scale(1); + opacity: 0; + } +} + +// loader v4 - eclipse +@supports (animation-name: this) { + .circle-loader--v4 { + width: var(--circle-loader-v4-size); // loader width + height: var(--circle-loader-v4-size); // loader height + border-radius: 50%; + overflow: hidden; + + .circle-loader__mask, + .circle-loader__circle { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + border-radius: inherit; + } + + .circle-loader__mask { + clip-path: circle(calc(0.5 * var(--circle-loader-v4-size))); // fix iOS issue - it needs to be = half size of loader + } + + .circle-loader__circle--1st { + background-color: var(--color-contrast-low); // loader base color + } + + .circle-loader__circle--2nd { + background-color: var(--color-primary); // loader fill color + will-change: transform; + transform-origin: 50% 100%; + animation: circle-loader-4 1.2s infinite cubic-bezier(.23, .9, .75, .1); + transform: translateX(-100%); + } + } +} + +@keyframes circle-loader-4 { + to { + transform: translateX(100%); + } +} + +// loader v5 - bounce +@supports (animation-name: this) { + .circle-loader--v5 { + font-size: var(--circle-loader-v5-size); // loader size - if you edit this value all elements scale accordingly + width: 1em; + height: 1em; + + .circle-loader__label { + font-size: 1rem; + } + + .circle-loader__ball { + position: absolute; + top: 0; + left: calc(50% - 0.140625em); + width: 0.28125em; + height: 0.28125em; + background-color: var(--color-primary); + border-radius: 50%; + animation: circle-loader-5-ball 0.8s infinite; + } + + .circle-loader__shadow { + position: absolute; + bottom: 0; + left: calc(50% - 0.15625em); + width: 0.3125em; + height: 0.3125em; + background-color: var(--color-contrast-lower); + border-radius: 50%; + transform: scaleY(0.4) scaleX(1.2); + animation: circle-loader-5-shadow 0.8s infinite; + } + } +} + +@keyframes circle-loader-5-ball { + 0% { + transform: translateY(0); + animation-timing-function: cubic-bezier(.61, .12, .85, .4); + } + + 50% { + transform: translateY(0.5625em); + animation-timing-function: cubic-bezier(.12, .59, .46, .95); + } + + 100% { + transform: translateY(0); + } +} + +@keyframes circle-loader-5-shadow { + 0% { + transform: scaleY(0.4) scaleX(1.2); + background-color: var(--color-contrast-lower); + animation-timing-function: cubic-bezier(.61, .12, .85, .4); + } + + 50% { + transform: scaleY(0.2) scaleX(0.6); + background-color: var(--color-contrast-low); + animation-timing-function: cubic-bezier(.12, .59, .46, .95); + } + + 100% { + transform: scaleY(0.4) scaleX(1.2); + background-color: var(--color-contrast-lower); + } +} + +// loader v6 - worm +@supports (animation-name: this) { + .circle-loader--v6 { + .circle-loader__svg { + display: block; + width: var(--circle-loader-v6-size); + height: var(--circle-loader-v6-size); + color: var(--color-primary); // loader color + } + + .circle-loader__fill { + stroke-width: 8px; // loader stroke width + stroke-dashoffset: 35; + stroke-dasharray: 36 36; + animation: circle-loader-6 1.5s infinite; + } + } +} + +@keyframes circle-loader-6 { + 0%, 100% { + stroke-dashoffset: 35; + } + + 50% { + stroke-dashoffset: -35; + } +} diff --git a/apps/web-shared/src/styles/components/custom-checkbox.scss b/apps/web-shared/src/styles/components/custom-checkbox.scss new file mode 100644 index 0000000..5722ee0 --- /dev/null +++ b/apps/web-shared/src/styles/components/custom-checkbox.scss @@ -0,0 +1,131 @@ +@use '../base' as *; + +/* -------------------------------- + +File#: _1_custom-checkbox +Title: Custom Checkbox +Descr: Replace the native checkbox button with a custom element (e.g., an icon) +Usage: codyhouse.co/license + +-------------------------------- */ + +:root { + --custom-checkbox-size: 20px; + --custom-checkbox-radius: 4px; + --custom-checkbox-border-width: 1px; + --custom-checkbox-marker-size: 18px; +} + +.custom-checkbox { + position: relative; + z-index: 1; + display: inline-block; + font-size: var(--custom-checkbox-size); +} + +.custom-checkbox__input { + position: relative; + /* hide native input */ + margin: 0; + padding: 0; + opacity: 0; + height: 1em; + width: 1em; + display: block; + z-index: 1; +} + +.custom-checkbox__control { + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 0; + z-index: -1; + pointer-events: none; + color: alpha(var(--color-contrast-low), 0.65); /* unchecked color */ + + &::before, &::after { + content: ''; + position: absolute; + } + + &::before { /* focus circle */ + width: 160%; + height: 160%; + background-color: currentColor; + top: 50%; + left: 50%; + transform: translate(-50%, -50%) scale(0); + opacity: 0; + border-radius: 50%; + will-change: transform; + } + + &::after { /* custom checkbox */ + top: 0; + left: 0; + width: 100%; + height: 100%; + + /* custom checkbox style */ + background-color: var(--color-bg); + border-radius: var(--custom-checkbox-radius); + box-shadow: inset 0 0 0 var(--custom-checkbox-border-width) currentColor, var(--shadow-xs); /* border */ + } +} + +.custom-checkbox__input:checked ~ .custom-checkbox__control, +.custom-checkbox__input:indeterminate ~ .custom-checkbox__control { + &::after { + background-color: currentColor; + background-repeat: no-repeat; + background-position: center; + background-size: var(--custom-checkbox-marker-size); + box-shadow: none; + } +} + +.custom-checkbox__input:checked ~ .custom-checkbox__control { + color: var(--color-primary); /* checked color */ + + &::after { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpolyline points='2.5 8 6.5 12 13.5 3' fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5'/%3E%3C/svg%3E"); + } +} + +.custom-checkbox__input:indeterminate ~ .custom-checkbox__control { + &::after { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cline x1='2' y1='8' x2='14' y2='8' fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2'/%3E%3C/svg%3E"); + } +} + +.custom-checkbox__input:active ~ .custom-checkbox__control { + transform: scale(0.9); +} + +.custom-checkbox__input:checked:active ~ .custom-checkbox__control, +.custom-checkbox__input:indeterminate:active ~ .custom-checkbox__control { + transform: scale(1); +} + +.custom-checkbox__input:focus ~ .custom-checkbox__control::before { + opacity: 0.2; + transform: translate(-50%, -50%) scale(1); +} + +/* --icon */ +.custom-checkbox--icon { + --custom-checkbox-size: 32px; + + .custom-checkbox__control::after { + display: none; + } + + .icon { + display: block; + color: inherit; + position: relative; + z-index: 1; + } +} diff --git a/apps/web-shared/src/styles/components/custom-select.scss b/apps/web-shared/src/styles/components/custom-select.scss new file mode 100644 index 0000000..9cd3b5e --- /dev/null +++ b/apps/web-shared/src/styles/components/custom-select.scss @@ -0,0 +1,158 @@ +@use '../base' as *; + +/* -------------------------------- + +File#: _1_custom-select +Title: Custom Select +Descr: Custom Select Control +Usage: codyhouse.co/license + +-------------------------------- */ + +:root { + // --default variation only 👇 + --select-icon-size: 16px; + --select-icon-right-margin: var(--space-sm); // icon margin right + --select-text-icon-gap: var(--space-xxxs); // gap between text and icon +} + +.select { + position: relative; +} + +.select__input { + width: 100%; + height: 100%; + padding-right: calc(var(--select-icon-size) + var(--select-icon-right-margin) + var(--select-text-icon-gap)) !important; +} + +.select__icon { + width: var(--select-icon-size); + height: var(--select-icon-size); + pointer-events: none; + position: absolute; + right: var(--select-icon-right-margin); + top: 50%; + transform: translateY(-50%); +} + +// --custom-dropdown +:root { + --select-dropdown-gap: 4px; // distance between select control and custom dropdown +} + +.select__button { // created in JS - custom select control + width: 100%; +} + +.select__button[aria-expanded="true"] { + // custom select control if dropdown = visible +} + +.select__dropdown { // created in JS - custom select dropdown + position: absolute; + left: 0; + top: 100%; + min-width: 200px; + max-height: 1px; // updated in JS + background-color: var(--color-bg-light); + box-shadow: var(--inner-glow), var(--shadow-md); + padding: var(--space-xxxs) 0; + border-radius: var(--radius-md); + z-index: var(--z-index-popover, 5); + margin-top: var(--select-dropdown-gap); + margin-bottom: var(--select-dropdown-gap); + overflow: auto; + + // use rem units + @include spaceUnit(1rem); + @include textUnit(1rem); + + visibility: hidden; + opacity: 0; +} + +.select__dropdown--right { // change dropdown position based on the available space + right: 0; + left: auto; +} + +.select__dropdown--up { + bottom: 100%; + top: auto; +} + +.select__button[aria-expanded="true"] + .select__dropdown { + visibility: visible; + opacity: 1; +} + +// custom <optgroup> list - include all <option>s if no <optgroup> available +.select__list { + list-style: none !important; +} + +.select__list:not(:first-of-type) { + padding-top: var(--space-xxs); +} + +.select__list:not(:last-of-type) { + border-bottom: 1px solid alpha(var(--color-contrast-higher), 0.1); + padding-bottom: var(--space-xxs); +} + +.select__item { // single item inside .select__list + display: flex; + align-items: center; + padding: var(--space-xxs) var(--space-sm); + color: var(--color-contrast-high); + width: 100%; + text-align: left; + // truncate text + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.select__item--optgroup { // custom <optgroup> label + font-size: var(--text-sm); + color: var(--color-contrast-medium); +} + +.select__item--option { // custom <option> label + cursor: pointer; + + &:hover { + background-color: alpha(var(--color-contrast-higher), 0.075); + } + + &:focus { + outline: none; + background-color: alpha(var(--color-primary), 0.15); + } + + &[aria-selected=true] { // selected option + background-color: var(--color-primary); + color: var(--color-white); + position: relative; + @include fontSmooth; + + &::after { // check icon next to the selected language + content: ''; + display: block; + height: 1em; + width: 1em; + margin-left: auto; + background-color: currentColor; + mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpolyline stroke-width='2' stroke='%23ffffff' fill='none' stroke-linecap='round' stroke-linejoin='round' points='1,9 5,13 15,3 '/%3E%3C/svg%3E"); + } + + &:focus { + box-shadow: inset 0 0 0 2px var(--color-primary-dark); + } + } +} + +html:not(.js ) .select .icon { // hide icon if JS = disabled + display: none; +} diff --git a/apps/web-shared/src/styles/components/details.scss b/apps/web-shared/src/styles/components/details.scss new file mode 100644 index 0000000..b4c122d --- /dev/null +++ b/apps/web-shared/src/styles/components/details.scss @@ -0,0 +1,57 @@ +@use '../base' as *; + +/* -------------------------------- + +File#: _1_details +Title: Details +Descr: A button that toggles the visibility of additional information +Usage: codyhouse.co/license + +-------------------------------- */ + +.details {} + +.details__summary { + display: inline-block; + cursor: pointer; + user-select: none; + + &:hover { + color: var(--color-primary); + } + + &:focus { + outline: 2px solid alpha(var(--color-primary), 0.2); + outline-offset: 4px; + } + + .icon { + flex-shrink: 0; + } +} + +// if JS = enabled +.js { + .details__summary { + list-style: none; // remove summary default icon + } + + .details__summary::-webkit-details-marker { + display: none; // remove default icon in webkit browsers + } + + .details__summary[aria-expanded="true"] .icon { + transform: rotate(90deg); // rotate icon when content is visible + } + + .details__content[aria-hidden="true"] { + display: none; + } +} + +// if JS = disabled +html:not(.js) .details__summary { + .icon { + display: none; + } +} diff --git a/apps/web-shared/src/styles/components/dropdown.scss b/apps/web-shared/src/styles/components/dropdown.scss new file mode 100644 index 0000000..c5ded33 --- /dev/null +++ b/apps/web-shared/src/styles/components/dropdown.scss @@ -0,0 +1,98 @@ +@use '../base' as *; + +/* -------------------------------- + +File#: _2_dropdown +Title: Dropdown +Descr: A hoverable link that toggles the visibility of a dropdown list +Usage: codyhouse.co/license + +-------------------------------- */ + +:root { + --dropdown-item-padding: var(--space-xxs) var(--space-sm); +} + +.dropdown { + position: relative; +} + +.dropdown__menu { + border-radius: var(--radius-md); + background-color: var(--color-bg-light); + box-shadow: var(--inner-glow), var(--shadow-sm); + z-index: var(--z-index-popover, 5); + position: absolute; + left: 0; + top: 100%; + opacity: 0; + visibility: hidden; +} + +.dropdown__wrapper { + max-height: 24px; +} + +@media (pointer: fine) { // user has pointing device (e.g., mouse) + .dropdown__wrapper, + .open-dropdown { + &:hover .dropdown__menu, + &:focus .dropdown__menu { + opacity: 1; + visibility: visible; + } + } + + .dropdown__sub-wrapper:hover > .dropdown__menu { + left: 100%; + } +} + +@media not all and (pointer: fine) { + .dropdown__trigger-icon { + display: none; + } +} + +.dropdown__item { + display: block; + text-decoration: none; + color: var(--color-contrast-high); + padding: var(--dropdown-item-padding); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + + &:hover, &.dropdown__item--hover { + background-color: alpha(var(--color-contrast-higher), 0.075); + } +} + +.dropdown__separator { // h line divider + height: 1px; + background-color: var(--color-contrast-lower); + margin: var(--dropdown-item-padding); +} + +.dropdown__sub-wrapper { + position: relative; + + > .dropdown__item { // item w/ right arrow + position: relative; + padding-right: calc(var(--space-sm) + 12px); + + .icon { // right arrow + position: absolute; + display: block; + width: 12px; + height: 12px; + right: var(--space-xxs); + top: calc(50% - 6px); + } + } + + > .dropdown__menu { // sub menu + top: calc(var(--space-xxs) * -1); + box-shadow: var(--inner-glow), var(--shadow-md); + } +} diff --git a/apps/web-shared/src/styles/components/form-validator.scss b/apps/web-shared/src/styles/components/form-validator.scss new file mode 100644 index 0000000..cc9f9a3 --- /dev/null +++ b/apps/web-shared/src/styles/components/form-validator.scss @@ -0,0 +1,18 @@ +@use '../base' as *; + +/* -------------------------------- + +File#: _1_form-validator +Title: Form Validator +Descr: A plugin to validate form fields +Usage: codyhouse.co/license + +-------------------------------- */ + +.form-validate__error-msg { + display: none; // hide error message by default + + .form-validate__input-wrapper--error & { + display: block; // show error message + } +} diff --git a/apps/web-shared/src/styles/components/interactive-table.scss b/apps/web-shared/src/styles/components/interactive-table.scss new file mode 100644 index 0000000..f239c62 --- /dev/null +++ b/apps/web-shared/src/styles/components/interactive-table.scss @@ -0,0 +1,156 @@ +@use '../base' as *; +@use 'menu.scss' as *; +@use 'menu-bar.scss' as *; + +/* -------------------------------- + +File#: _3_interactive-table +Title: Interactive Table +Descr: Table with the option of sorting data and selecting rows to perform specific actions +Usage: codyhouse.co/license + +-------------------------------- */ + +.int-table { + overflow: hidden; + border-bottom: 2px solid var(--color-contrast-lower); +} + +.int-table__inner { + position: relative; + overflow: auto; + + &::-webkit-scrollbar { // custom scrollbar style + height: 8px; + width: 8px; + } + + &::-webkit-scrollbar-track { // progress bar + background-color: var(--color-contrast-lower); + } + + &::-webkit-scrollbar-thumb { // handle + background-color: alpha(var(--color-contrast-higher), 0.9); + border-radius: 50em; + } + + &::-webkit-scrollbar-thumb:hover { + background-color: var(--color-contrast-higher); + } +} + +.int-table__table { + width: 100%; +} + +.int-table__header { + .int-table__cell { + background-color: var(--color-bg); + box-shadow: 0 2px 0 var(--color-contrast-lower); + } +} + +.int-table__body { + .int-table__row { + border-bottom: 1px solid var(--color-contrast-lower); + + &:last-child { + border-bottom: none; + } + } + + .int-table__row--checked { + background-color: alpha(var(--color-primary), 0.1); + border-color: alpha(var(--color-primary), 0.25); + } +} + +.int-table__cell { // standard cell + padding: var(--space-xxxs); +} + +.int-table__cell--th { // header cell + font-weight: 600; +} + +.int-table__cell--sort { // header cell with sorting option + user-select: none; + + &:hover, &:focus-within { + background-color: alpha(var(--color-contrast-higher), 0.075); + } + + &:hover { + cursor: pointer; + } +} + +.int-table__cell--focus { + background-color: alpha(var(--color-primary), 0.15); +} + +.int-table__sort-icon { // sorting icon indicator + .arrow-up, .arrow-down { + fill: alpha(var(--color-contrast-higher), 0.3); + } +} + +.int-table__cell--asc .int-table__sort-icon .arrow-up, +.int-table__cell--desc .int-table__sort-icon .arrow-down { + fill: var(--color-contrast-higher); +} + +.int-table__checkbox { + --custom-checkbox-size: 18px; + --custom-checkbox-marker-size: 16px; + display: block; + width: var(--custom-checkbox-size); + height: var(--custom-checkbox-size); +} + +.int-table__menu-btn { + display: flex; + align-items: center; + justify-content: center; + width: 2em; + cursor: pointer; + + .icon { + display: block; + width: 16px; + height: 16px; + } +} + +// --sticky-header +.int-table--sticky-header { + position: relative; + z-index: 1; + + .int-table__inner { + max-height: 605px; + } + + .int-table__header { + .int-table__cell { + position: sticky; + top: 0; + z-index: 2; + } + } +} + +// actions +.int-table-actions { + .menu-bar { + --menu-bar-button-size: 38px; // size of the menu buttons + --menu-bar-icon-size: 16px; // size of the icons inside the buttons + --menu-bar-horizontal-gap: var(--space-xxxxs); // horizontal gap between buttons + --menu-bar-vertical-gap: 4px; // vertical gap between buttons and labels (tooltips) + --menu-bar-label-size: var(--text-xs); // label font size + } + + .menu-bar__icon { + color: alpha(var(--color-contrast-higher), 0.5); + } +} diff --git a/apps/web-shared/src/styles/components/list.scss b/apps/web-shared/src/styles/components/list.scss new file mode 100644 index 0000000..df600a3 --- /dev/null +++ b/apps/web-shared/src/styles/components/list.scss @@ -0,0 +1,195 @@ +@use '../base' as *; + +/* -------------------------------- + +File#: _1_list +Title: List +Descr: Custom list component +Usage: codyhouse.co/license + +-------------------------------- */ + +:root { + --list-space-y: 0.375em; // vertical gaps + --list-offset: 1em; // sublist horizontal offset + --list-line-height-multiplier: 1; // line-height multiplier +} + +.list, .text-component .list { + padding-left: 0; + list-style: none; + + ul, ol { + list-style: none; + margin: 0; // reset + margin-top: calc((var(--list-space-y) / 2) * var(--text-space-y-multiplier, 1)); + padding-top: calc((var(--list-space-y) / 2) * var(--text-space-y-multiplier, 1)); + padding-left: var(--list-offset); + } + + li { + padding-bottom: calc((var(--list-space-y) / 2) * var(--text-space-y-multiplier, 1)); + margin-bottom: calc((var(--list-space-y) / 2) * var(--text-space-y-multiplier, 1)); + line-height: calc(var(--body-line-height) * var(--list-line-height-multiplier)); + } + + > li:last-child, ul > li:last-child, ol > li:last-child { + margin-bottom: 0; + } + + &:not(.list--border) > li:last-child, ul > li:last-child, ol > li:last-child { + padding-bottom: 0; + } +} + +/* #region (ul + ol) */ +.list--ul, .text-component .list--ul, +.list--ol, .text-component .list--ol { + --list-offset: calc(var(--list-bullet-size) + var(--list-bullet-margin-right)); + + ul, ol { + padding-left: 0; + } + + li { + @supports (--css: variables) { + padding-left: var(--list-offset) !important; + } + } + + li::before { + display: inline-flex; + justify-content: center; + align-items: center; + vertical-align: middle; + position: relative; + top: -0.1em; + + @supports (--css: variables) { + width: var(--list-bullet-size) !important; + height: var(--list-bullet-size) !important; + margin-left: calc(var(--list-bullet-size) * -1) !important; + left: calc(var(--list-bullet-margin-right) * -1) !important; + } + } +} + +// unordered list +.list--ul, .text-component .list--ul { + --list-bullet-size: 7px; // dot width and height + --list-bullet-margin-right: 12px; // gap between bullet and content + + > li { + padding-left: 19px; // IE fallback + } + + > li::before { // bullet + content: ''; + border-radius: 50%; + color: var(--color-contrast-lower); // bullet color + background-color: currentColor; + + // IE fallback + width: 7px; + height: 7px; + margin-left: -7px; + left: -12px; + // end - IE fallback + } + + ul li::before { + background-color: transparent; + box-shadow: inset 0 0 0 2px currentColor; + } +} + +// ordered list +.list--ol, .text-component .list--ol { + --list-bullet-size: 26px; // ⚠️ use px or rem units - circle width and height + --list-bullet-margin-right: 6px; // ⚠️ use px or rem units - gap between circle and content + --list-bullet-font-size: 14px; // ⚠️ use px or rem units - bullet font size + counter-reset: list-items; + + > li { + counter-increment: list-items; + padding-left: 32px; // IE fallback + } + + ol { + counter-reset: list-items; + } + + > li::before { + content: counter(list-items); + font-size: var(--list-bullet-font-size, 14px); + background-color: var(--color-contrast-lower); + color: var(--color-contrast-high); + line-height: 1; + border-radius: 50%; + + // IE fallback + width: 26px; + height: 26px; + margin-left: -26px; + left: -6px; + // end - IE fallback + } + + ol > li::before { + background-color: transparent; + box-shadow: inset 0 0 0 2px var(--color-contrast-lower); + } +} +/* #endregion */ + +/* #region (border) */ +.list--border, .text-component .list--border { // show border divider among list items + li:not(:last-child) { + border-bottom: 1px solid var(--color-contrast-lower); + } + + ul, ol { + border-top: 1px solid var(--color-contrast-lower); + } +} +/* #endregion */ + +/* #region (icons) */ +.list--icons, .text-component .list--icons { // use icons as bullet points + --list-bullet-size: 24px; + --list-bullet-margin-right: 8px; // gap between icon and text + --list-offset: calc(var(--list-bullet-size) + var(--list-bullet-margin-right)); + + ul, ol { + padding-left: 32px; // IE fallback + + @supports (--css: variables) { + padding-left: var(--list-offset); + } + } +} + +.list__icon { + position: relative; + + // IE fallback + width: 24px; + height: 24px; + margin-right: 8px; + + &:not(.top-0) { + top: calc((1em * var(--body-line-height) - 24px) / 2); + } + // end - IE fallback + + @supports (--css: variables) { + width: var(--list-bullet-size); + height: var(--list-bullet-size); + margin-right: var(--list-bullet-margin-right); + + &:not(.top-0) { + top: calc((1em * var(--body-line-height) * var(--list-line-height-multiplier) - var(--list-bullet-size)) / 2); + } + } +} +/* #endregion */ diff --git a/apps/web-shared/src/styles/components/menu-bar.scss b/apps/web-shared/src/styles/components/menu-bar.scss new file mode 100644 index 0000000..3f70fbe --- /dev/null +++ b/apps/web-shared/src/styles/components/menu-bar.scss @@ -0,0 +1,139 @@ +@use '../base' as *; +@use 'menu.scss' as *; + +/* -------------------------------- + +File#: _2_menu-bar +Title: Menu Bar +Descr: Application menu with a list of common actions that users can perform +Usage: codyhouse.co/license + +-------------------------------- */ + +:root { + --menu-bar-button-size: 2.5em; // size of the menu buttons + --menu-bar-icon-size: 1em; // size of the icons inside the buttons + --menu-bar-horizontal-gap: var(--space-xxs); // horizontal gap between buttons + --menu-bar-vertical-gap: 4px; // vertical gap between buttons and labels (tooltips) + --menu-bar-label-size: var(--text-xs); // label font size +} + +.menu-bar { + list-style: none; + display: inline-flex; + align-items: center; +} + +.menu-bar__item { // menu button + position: relative; + display: inline-block; // flex fallback + display: flex; + align-items: center; + justify-content: center; + height: var(--menu-bar-button-size); + width: var(--menu-bar-button-size); + border-radius: 50%; + cursor: pointer; + + &:not(:last-child) { + margin-right: var(--menu-bar-horizontal-gap); + } + + &:hover, + &.menu-control--active { + background-color: alpha(var(--color-contrast-higher), 0.1); + + > .menu-bar__icon { + color: var(--color-contrast-higher); + } + + > .menu-bar__label { // show label + clip: auto; + clip-path: none; + height: auto; + width: auto; + } + } + + &:focus { + outline: none; + background-color: alpha(var(--color-primary), 0.1); + } + + &:active { + background-color: var(--color-contrast-low); + } + + &:focus:active { + background-color: alpha(var(--color-primary), 0.2); + } +} + +.menu-bar__item--trigger { // button used to show hidden actions - visibile only if menu = collapsed + display: none; +} + +.menu-bar__icon { + display: block; + color: var(--color-contrast-high); + font-size: var(--menu-bar-icon-size); // set icon size +} + +.menu-bar__label { // label visible on :hover + // hide + position: absolute; + z-index: var(--z-index-popover, 5); + clip: rect(1px, 1px, 1px, 1px); + clip-path: inset(50%); + width: 1px; + height: 1px; + overflow: hidden; + white-space: nowrap; + // style + top: 100%; + left: 50%; + transform: translateX(-50%) translateY(var(--menu-bar-vertical-gap)); + padding: var(--space-xxs) var(--space-xs); + color: var(--color-bg); + background-color: alpha(var(--color-contrast-higher), 0.95); + border-radius: var(--radius-md); + font-size: var(--menu-bar-label-size); + @include fontSmooth; + pointer-events: none; + user-select: none; +} + +.menu-bar--collapsed { // mobile layout style + .menu-bar__item--hide { // hide buttons + display: none; + } + + .menu-bar__item--trigger { // show submenu trigger + display: inline-block; // flex fallback + display: flex; + } +} + +// detect when the menu needs to switch from the mobile layout to an expanded one - used in JS +.js { + .menu-bar { + opacity: 0; // hide menu bar while it is initialized in JS + + &::before { + display: none; + content: 'collapsed'; + } + } + + .menu-bar--loaded { + opacity: 1; + } + + @each $breakpoint, $value in $breakpoints { + .menu-bar--expanded\@#{$breakpoint}::before { + @include breakpoint(#{$breakpoint}) { + content: 'expanded'; + } + } + } +} diff --git a/apps/web-shared/src/styles/components/menu.scss b/apps/web-shared/src/styles/components/menu.scss new file mode 100644 index 0000000..8e211a5 --- /dev/null +++ b/apps/web-shared/src/styles/components/menu.scss @@ -0,0 +1,81 @@ +@use '../base' as *; + +/* -------------------------------- + +File#: _1_menu +Title: Menu +Descr: Application menu that provides access to a set of functionalities +Usage: codyhouse.co/license + +-------------------------------- */ + +.menu { + --menu-vertical-gap: 5px; // vertical gap between the Menu element and its control + --menu-item-padding: var(--space-xxxs) var(--space-xs); + list-style: none; + position: fixed; // top/left position set in JS + background-color: var(--color-bg-light); + //padding: var(--space-xxs) 0; + border-radius: var(--radius-md); + z-index: var(--z-index-popover, 5); + user-select: none; + margin-top: var(--menu-vertical-gap); + margin-bottom: var(--menu-vertical-gap); + overflow: auto; + + + // use rem units + @include spaceUnit(1rem); + @include textUnit(1rem); + + visibility: hidden; + opacity: 0; +} + +.menu--is-visible { + visibility: visible; + opacity: 1; +} + +.menu--overlay { + z-index: var(--z-index-overlay, 15); +} + +.menu__content { + display: block; // fallback + display: flex; + align-items: center; + text-decoration: none; // reset link style + padding: var(--menu-item-padding); + color: var(--color-contrast-high); + cursor: pointer; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + + &:hover { + background-color: alpha(var(--color-contrast-higher), 0.075); + } + + &:focus { + outline: none; + background-color: alpha(var(--color-primary), 0.15); + } +} + +.menu__label { + padding: var(--menu-item-padding); + font-size: var(--text-sm); + color: var(--color-contrast-medium); +} + +.menu__separator { + height: 1px; + background-color: var(--color-contrast-lower); + margin: var(--menu-item-padding); +} + +.menu__icon { + color: alpha(var(--color-contrast-higher), 0.5); + margin-right: var(--space-xxs); +} diff --git a/apps/web-shared/src/styles/components/modal.scss b/apps/web-shared/src/styles/components/modal.scss new file mode 100644 index 0000000..1beec76 --- /dev/null +++ b/apps/web-shared/src/styles/components/modal.scss @@ -0,0 +1,105 @@ +@use '../base' as *; + +/* -------------------------------- + +File#: _1_modal-window +Title: Modal Window +Descr: A modal dialog used to display critical information +Usage: codyhouse.co/license + +-------------------------------- */ + +.modal { + position: fixed; + z-index: var(--z-index-overlay, 15); + width: 100%; + height: 100%; + left: 0; + top: 0; + opacity: 0; + visibility: hidden; + + &:not(.modal--is-visible) { + pointer-events: none; + background-color: transparent; + } +} + +.modal--is-visible { + opacity: 1; + visibility: visible; +} + +// close button +.modal__close-btn { + display: flex; + flex-shrink: 0; + border-radius: 50%; + cursor: pointer; + + .icon { + display: block; + margin: auto; + } +} + +.modal__close-btn--outer { // close button - outside the modal__content + width: 48px; + height: 48px; + position: fixed; + top: var(--space-sm); + right: var(--space-sm); + z-index: var(--z-index-fixed-element, 10); + background-color: alpha(var(--color-black), 0.9); + + .icon { + color: var(--color-white); // icon color + } + + &:hover { + background-color: alpha(var(--color-black), 1); + + .icon { + transform: scale(1.1); + } + } +} + +.modal__close-btn--inner { // close button - inside the modal__content + width: 2em; + height: 2em; + background-color: var(--color-bg-light); + box-shadow: var(--inner-glow), var(--shadow-sm); + + .icon { + color: inherit; // icon color + } + + &:hover { + background-color: var(--color-bg-lighter); + box-shadow: var(--inner-glow), var(--shadow-md); + } +} + +// load content - optional +.modal--is-loading { + .modal__content { + visibility: hidden; + } + + .modal__loader { + display: flex; + } +} + +.modal__loader { // loader icon + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + justify-content: center; + align-items: center; + display: none; + pointer-events: none; +} diff --git a/apps/web-shared/src/styles/components/pagination.scss b/apps/web-shared/src/styles/components/pagination.scss new file mode 100644 index 0000000..0a09210 --- /dev/null +++ b/apps/web-shared/src/styles/components/pagination.scss @@ -0,0 +1,77 @@ +@use '../base' as *; + +/* -------------------------------- + +File#: _1_pagination +Title: Pagination +Descr: Component used to navigate through pages of related content +Usage: codyhouse.co/license + +-------------------------------- */ + +.pagination {} + +.pagination__list > li { + display: inline-block; // flex fallback +} + +// --split - push first + last item to sides +.pagination--split { + .pagination__list { + width: 100%; + + > *:first-child { + margin-right: auto; + } + + > *:last-child { + margin-left: auto; + } + } +} + +.pagination__item { + display: inline-block; // flex fallback + display: inline-flex; + height: 100%; + align-items: center; + padding: var(--space-xs) calc(1.355 * var(--space-xs)); + + white-space: nowrap; + line-height: 1; + border-radius: var(--radius-md); + + text-decoration: none; + color: var(--color-contrast-high); + @include fontSmooth; + + will-change: transform; + + &:hover:not(.pagination__item--selected):not(.pagination__item--ellipsis) { + background-color: alpha(var(--color-contrast-higher), 0.1); + } +} + +.pagination__item--selected { + background-color: var(--color-contrast-higher); + color: var(--color-bg); + box-shadow: var(--shadow-sm); +} + +.pagination__item--disabled { + opacity: 0.5; + pointer-events: none; +} + +// --jumper +.pagination__jumper { + .form-control { + width: 3em; + margin-right: var(--space-xs); + } + + em { + flex-shrink: 0; + white-space: nowrap; + } +} diff --git a/apps/web-shared/src/styles/components/popover.scss b/apps/web-shared/src/styles/components/popover.scss new file mode 100644 index 0000000..7f423a0 --- /dev/null +++ b/apps/web-shared/src/styles/components/popover.scss @@ -0,0 +1,38 @@ +@use '../base'as *; + +/* -------------------------------- + +File#: _1_popover +Title: Popover +Descr: A pop-up box controlled by a trigger element +Usage: codyhouse.co/license + +-------------------------------- */ +:root { + --popover-width: 250px; + --popover-control-gap: 4px; // ⚠️ use px units - vertical gap between the popover and its control + --popover-viewport-gap: 20px; // ⚠️ use px units - vertical gap between the popover and the viewport - visible if popover height > viewport height + --popover-transition-duration: 0.2s; +} + +.popover { + position: fixed; // top/left position set in JS + width: var(--popover-width); + z-index: var(--z-index-popover, 5); + margin-top: var(--popover-control-gap); + margin-bottom: var(--popover-control-gap); + overflow: auto; + -webkit-overflow-scrolling: touch; + + visibility: hidden; + opacity: 0; +} + +.popover--is-visible { + visibility: visible; + opacity: 1; +} + +.popover-control--active { + // class added to the trigger when popover is visible +} diff --git a/apps/web-shared/src/styles/components/pre-header.scss b/apps/web-shared/src/styles/components/pre-header.scss new file mode 100644 index 0000000..1e803e7 --- /dev/null +++ b/apps/web-shared/src/styles/components/pre-header.scss @@ -0,0 +1,46 @@ +@use '../base' as *; + +/* -------------------------------- + +File#: _1_pre-header +Title: Pre-header +Descr: Pre-header (top) banner +Usage: codyhouse.co/license + +-------------------------------- */ + +.pre-header { + display: block; + background-color: var(--color-contrast-higher); + color: var(--color-bg); + @include fontSmooth; +} + +.pre-header--is-hidden { + display: none; +} + +.pre-header__close-btn { + position: absolute; + right: 0; + top: calc(50% - 0.5em); + will-change: transform; + + &:hover { + transform: scale(1.1); + } + + .icon { + display: block; + } +} + +// --link +a.pre-header { + text-decoration: none; + + &:hover { + text-decoration: underline; + background-color: var(--color-contrast-high); + } +} diff --git a/apps/web-shared/src/styles/components/radios-checkboxes.scss b/apps/web-shared/src/styles/components/radios-checkboxes.scss new file mode 100644 index 0000000..c4009f9 --- /dev/null +++ b/apps/web-shared/src/styles/components/radios-checkboxes.scss @@ -0,0 +1,134 @@ +@use '../base' as *; + +/* -------------------------------- + +File#: _1_radios-checkboxes +Title: Radios and Checkboxes +Descr: Custom radio and checkbox buttons +Usage: codyhouse.co/license + +-------------------------------- */ + +:root { + // radios and checkboxes + --checkbox-radio-size: 18px; + --checkbox-radio-gap: var(--space-xxs); // gap between button and label + --checkbox-radio-border-width: 1px; + --checkbox-radio-line-height: var(--body-line-height); + + // radio buttons + --radio-marker-size: 8px; + + // checkboxes + --checkbox-marker-size: 12px; + --checkbox-radius: 4px; +} + +// hide native buttons +.radio, +.checkbox { + position: absolute; + padding: 0; + margin: 0; + margin-top: calc((1em * var(--checkbox-radio-line-height) - var(--checkbox-radio-size)) / 2); + opacity: 0; + height: var(--checkbox-radio-size); + width: var(--checkbox-radio-size); + pointer-events: none; +} + +// label +.radio + label, +.checkbox + label { + display: inline-block; + line-height: var(--checkbox-radio-line-height); + user-select: none; + cursor: pointer; + padding-left: calc(var(--checkbox-radio-size) + var(--checkbox-radio-gap)); +} + +// custom inputs - basic style +.radio + label::before, +.checkbox + label::before { + content: ''; + box-sizing: border-box; + display: inline-block; + position: relative; + vertical-align: middle; + top: -0.1em; + margin-left: calc(-1 * (var(--checkbox-radio-size) + var(--checkbox-radio-gap))); + flex-shrink: 0; + width: var(--checkbox-radio-size); + height: var(--checkbox-radio-size); + background-color: var(--color-bg); + border-width: var(--checkbox-radio-border-width); + border-color: alpha(var(--color-contrast-low), 0.65); + border-style: solid; + box-shadow: var(--shadow-xs); + background-repeat: no-repeat; + background-position: center; + margin-right: var(--checkbox-radio-gap); +} + +// :hover +.radio:not(:checked):not(:focus) + label:hover::before, +.checkbox:not(:checked):not(:focus) + label:hover::before { + border-color: alpha(var(--color-contrast-low), 1); +} + +// radio only style +.radio + label::before { + border-radius: 50%; +} + +// checkbox only style +.checkbox + label::before { + border-radius: var(--checkbox-radius); +} + +// :checked +.radio:checked + label::before, +.checkbox:checked + label::before { + background-color: var(--color-primary); + box-shadow: var(--shadow-xs); + border-color: var(--color-primary); +} + +// radio button icon +.radio:checked + label::before { + background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cg class='nc-icon-wrapper' fill='%23ffffff'%3E%3Ccircle cx='8' cy='8' r='8' fill='%23ffffff'%3E%3C/circle%3E%3C/g%3E%3C/svg%3E"); + background-size: var(--radio-marker-size); +} + +// checkbox button icon +.checkbox:checked + label::before { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpolyline points='1 6.5 4 9.5 11 2.5' fill='none' stroke='%23FFFFFF' stroke-linecap='round' stroke-linejoin='round' stroke-width='2'/%3E%3C/svg%3E"); + background-size: var(--checkbox-marker-size); +} + +// :focus +.radio:checked:active + label::before, +.checkbox:checked:active + label::before, +.radio:focus + label::before, +.checkbox:focus + label::before { + border-color: var(--color-primary); + box-shadow: 0 0 0 3px alpha(var(--color-primary), 0.2); +} + +// --radio--bg, --checkbox--bg -> variation with background color +.radio--bg + label, .checkbox--bg + label { + padding: var(--space-xxxxs) var(--space-xxxs); + padding-left: calc(var(--checkbox-radio-size) + var(--checkbox-radio-gap) + var(--space-xxxs)); + border-radius: var(--radius-md); +} + +.radio--bg + label:hover, .checkbox--bg + label:hover { + background-color: alpha(var(--color-contrast-higher), 0.075); +} + +.radio--bg:active + label, +.checkbox--bg:active + label, +.radio--bg:focus + label, +.checkbox--bg:focus + label { + background-color: alpha(var(--color-primary), 0.1); +} diff --git a/apps/web-shared/src/styles/components/select-autocomplete.scss b/apps/web-shared/src/styles/components/select-autocomplete.scss new file mode 100644 index 0000000..78a0fb0 --- /dev/null +++ b/apps/web-shared/src/styles/components/select-autocomplete.scss @@ -0,0 +1,173 @@ +@use '../base' as *; +@use 'autocomplete.scss' as *; + +/* -------------------------------- + +File#: _3_select-autocomplete +Title: Select Autocomplete +Descr: Selection dropdown with autocomplete +Usage: codyhouse.co/license + +-------------------------------- */ + +.select-auto { + &.autocomplete { + --autocomplete-dropdown-vertical-gap: 4px; // gap between input and results list + --autocomplete-dropdown-max-height: 250px; + --autocomplete-dropdown-scrollbar-width: 6px; // custom scrollbar - webkit browsers + } +} + +// input +.select-auto__input-wrapper { + --input-btn-size: 1.25em; // btn/icon size + --input-btn-icon-size: 16px; // btn/icon size + --input-btn-text-gap: var(--space-xxs); // gap between button/icon and text + + position: relative; + background: var(--color-bg-dark); + line-height: 1.2; + box-shadow: inset 0 0 0 1px var(--color-contrast-lower); + + &.multiple { + display: flex; + flex-direction: row; + flex-flow: wrap; + + .chip { + white-space: nowrap; + margin-right: 1px; + } + + input[type="text"] { + width: auto; + } + + @media (max-width: 756px) { + flex-flow: column !important; + + &.has-selection { + input[type="text"] { + margin-top: 5px; + } + + .chip { + justify-content: space-between; + + .chip__btn { + margin-right: 0 !important;; + } + } + } + } + } + + &::placeholder { + opacity: 1; + color: var(--color-contrast-low); + } + + &:focus-within { + background: var(--color-bg); + box-shadow: inset 0 0 0 1px alpha(var(--color-contrast-lower), 0), 0px 0px 0px 1px var(--color-primary); + outline: none; + } + + .form-control { + width: 100%; + height: 100%; + padding-right: calc(var(--form-control-padding-x) + var(--input-btn-size) + var(--input-btn-text-gap)); + } +} + + +.select-auto__input-icon-wrapper { + width: var(--input-btn-size); + height: var(--input-btn-size); + + position: absolute; + bottom: calc(var(--input-btn-size) / 3); + right: var(--form-control-padding-x); + display: flex; + pointer-events: none; + + .icon { + display: block; + margin: auto; + width: var(--input-btn-icon-size, 16px); + height: var(--input-btn-icon-size, 16px); + } +} + +.select-auto__input-btn { + display: none; + justify-content: center; + align-items: center; + width: inherit; + height: inherit; + pointer-events: auto; + cursor: pointer; + color: var(--color-contrast-medium); // icon color + + &:hover { + color: var(--color-contrast-high); + } +} + +.select-auto--selection-done { + .select-auto__input-icon-wrapper > .icon { + display: none; + } + + .select-auto__input-btn { + display: flex; + } +} + +// dropdown +.select-auto__results { + // reset spacing and typography + @include spaceUnit(1rem); + @include textUnit(1rem); +} + +// single result item +.select-auto__option { + position: relative; + cursor: pointer; + + &:hover { + background-color: alpha(var(--color-contrast-higher), 0.05); + } + + &:focus { + outline: none; + background-color: alpha(var(--color-primary), 0.12); + } + + &.select-auto__option--selected { + background-color: var(--color-primary); + color: var(--color-white); + padding-right: calc(1em + var(--space-sm)); + @include fontSmooth; + + &:focus { + background-color: var(--color-primary-dark); + } + + &::after { + content: ''; + position: absolute; + right: var(--space-sm); + top: calc(50% - 0.5em); + height: 1em; + width: 1em; + background-color: currentColor; + mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpolyline stroke-width='2' stroke='%23ffffff' fill='none' stroke-linecap='round' stroke-linejoin='round' points='1,9 5,13 15,3 '/%3E%3C/svg%3E"); + } + } +} + +.select-auto__group-title, .select-auto__no-results-msg { + outline: none; +} diff --git a/apps/web-shared/src/styles/components/tabbed-navigation.scss b/apps/web-shared/src/styles/components/tabbed-navigation.scss new file mode 100644 index 0000000..4090fca --- /dev/null +++ b/apps/web-shared/src/styles/components/tabbed-navigation.scss @@ -0,0 +1,133 @@ +@use '../base' as *; + +/* -------------------------------- + +File#: _1_tabbed-navigation-v2 +Title: Tabbed Navigation v2 +Descr: Tabbed (secondary) navigation +Usage: codyhouse.co/license + +-------------------------------- */ + +.tabs-nav-v2 { + display: flex; + flex-wrap: wrap; + + .tab-v2 { + display: inline-block; // flexbox fallback + display: inline-flex; + align-items: center; + } +} + +.tabs-nav-v2__item { + display: inline-block; + padding: var(--space-xxs) var(--space-sm); + color: inherit; + white-space: nowrap; + text-decoration: none; +} + +.tabs-nav-v2__item--selected, +.tabs-nav-v2__item[aria-selected="true"] { + color: var(--color-bg); + background-color: var(--color-contrast-higher); +} + +@include breakpoint(md) { + .tabs-nav-v2 { + .tab-v2 { + margin: 0; + } + } + + .tabs-nav-v2__item { + background-color: transparent; + margin: var(--space-xxs) var(--space-sm); + padding: var(--space-xxxs) var(--space-xxs) !important; + border-radius: var(--radius-md); + + &:hover { + background-color: alpha(var(--color-primary), 0.035); + color: var(--color-primary); + } + } + + .tabs-nav-v2__item--selected, + .tabs-nav-v2__item[aria-selected="true"] { + + background-color: alpha(var(--color-primary), 0.075); + color: var(--color-primary); + position: relative; + + &::after { + content: ''; + position: absolute; + bottom: calc(var(--tabs-nav-border-width) * -1); + left: 0; + width: 100%; + height: var(--tabs-nav-border-width); + background-color: var(--color-bg); + } + + &:hover { + background-color: alpha(var(--color-primary), 0.075); + } + } +} + +:root { + --s-tabs-border-bottom-width: 1px; + --s-tabs-selected-item-border-bottom-width: 1px; +} + +.s-tabs { + position: relative; + + &::after { /* gradient - truncate text */ + content: ''; + position: absolute; + right: -1px; + top: 0; + height: calc(100% - var(--s-tabs-border-bottom-width)); + width: 2em; + pointer-events: none; + z-index: 1; + } +} + +.s-tabs__list { + display: flex; + overflow: auto; + -webkit-overflow-scrolling: auto; + + &::after { /* border bottom */ + content: ''; + position: absolute; + width: 100%; + height: var(--s-tabs-border-bottom-width); + left: 0; + bottom: 0; + background-color: var(--color-contrast-lower); + } +} + +.s-tabs__link { + color: var(--color-contrast-medium); + text-decoration: none; + display: inline-block; + padding: var(--space-xs) var(--space-sm); + white-space: nowrap; + border-bottom: var(--s-tabs-selected-item-border-bottom-width) solid transparent; + z-index: 1; + + &:hover:not(.s-tabs__link--current) { + color: var(--color-contrast-high); + } +} + +.s-tabs__link--current { + position: relative; + color: var(--color-primary); + border-bottom-color: var(--color-primary); +} diff --git a/apps/web-shared/src/styles/components/table.scss b/apps/web-shared/src/styles/components/table.scss new file mode 100644 index 0000000..af8207f --- /dev/null +++ b/apps/web-shared/src/styles/components/table.scss @@ -0,0 +1,147 @@ +@use '../base'as *; + +/* -------------------------------- + +File#: _1_table +Title: Table +Descr: Data tables used to organize and display information in rows and columns +Usage: codyhouse.co/license + +-------------------------------- */ + +// >>> style affecting all (block + expanded) versions 👇 +.table { + position: relative; + z-index: 1; +} + +// <<< end style affecting all versions + +// >>> block version only (mobile) 👇 +.table:not(.table--expanded) { + border-collapse: separate; + border-spacing: 0 var(--space-md); // row gap + margin-top: calc(-2 * var(--space-md)); // set spacing variable = row gap ☝️ + + .table__header { + // hide table header - but keep it accessible to SR + @include srHide; + } + + .table__row { + .table__cell:first-child { + border-radius: var(--radius-md) var(--radius-md) 0 0; + } + + .table__cell:last-child { + border-radius: 0 0 var(--radius-md) var(--radius-md); + + &::after { + // hide border bottom + display: none; + } + } + } + + .table__cell { + position: relative; + display: flex; + justify-content: space-between; + width: 100%; + text-align: right; + padding: var(--space-md); + background-color: var(--color-bg-light); + + &::after { + // border bottom + content: ''; + position: absolute; + bottom: 0; + left: var(--space-md); + width: calc(100% - (2 * var(--space-md))); + height: 1px; + background-color: var(--color-contrast-lower); + } + } + + .table__label { + // inline labels -> visible when table header is hidden + font-weight: bold; + text-align: left; + color: var(--color-contrast-higher); + margin-right: var(--space-md); + } +} + +// <<< end block version + +// >>> expanded version only (desktop) 👇 -> show standard rows and cols +.table--expanded { + border-bottom: 1px solid var(--color-contrast-lower); // table border bottom + + .table__header { + .table__cell { + // header cell style + position: relative; + z-index: 10; + background-color: var(--color-bg); + border-bottom: 1px solid var(--color-contrast-lower); // header border bottom + font-weight: bold; + color: var(--color-contrast-higher); + } + } + + .table__body { + .table__row { + &:nth-child(odd) { + background-color: alpha(var(--color-bg-dark), 0.85); + } + } + } + + .table__cell { + padding: var(--space-xxxs); + } + + .table__label { + // hide inline labels + display: none; + } + + // --header-sticky + .table__header--sticky { + .table__cell { + // header cell style + position: sticky; + top: 0; + } + } +} + +// <<< end expanded version + +.js { + .table { + opacity: 0; // hide table while it is initialized in JS + } + + .table--loaded { + opacity: 1; + } +} + +// detect when the table needs to switch from the mobile layout to an expanded one - used in JS +[class*="table--expanded"]::before { + display: none; +} + +@each $breakpoint, +$value in $breakpoints { + .table--expanded\@#{$breakpoint}::before { + content: 'collapsed'; + + @include breakpoint(#{$breakpoint}) { + content: 'expanded'; + } + } +} diff --git a/apps/web-shared/src/styles/components/user-menu.scss b/apps/web-shared/src/styles/components/user-menu.scss new file mode 100644 index 0000000..1b5c1d5 --- /dev/null +++ b/apps/web-shared/src/styles/components/user-menu.scss @@ -0,0 +1,81 @@ +@use '../base' as *; +@use 'menu.scss' as *; + +/* -------------------------------- + +File#: _2_user-menu +Title: User Menu +Descr: A menu controlled by the user profile image +Usage: codyhouse.co/license + +-------------------------------- */ + +.user-menu-control { + --profile-figure-size: 40px; + + cursor: pointer; + display: inline-flex; + align-items: center; + text-align: left; + + &:hover { + .user-menu-control__img-wrapper { + opacity: 0.8; + } + + .user-menu__meta-title { + color: var(--color-primary); + } + } + + &:focus, &.menu-control--active { + outline: none; + + .user-menu-control__img-wrapper::after { + opacity: 1; + transform: scale(1); + } + } +} + +.user-menu-control__img-wrapper { + width: var(--profile-figure-size); + height: var(--profile-figure-size); + position: relative; + transition: opacity 0.2s; + + &::after { + content: ''; + position: absolute; + z-index: -1; + left: -4px; + top: -4px; + width: 100%; + height: 100%; + border-radius: inherit; + width: calc(var(--profile-figure-size) + 8px); + height: calc(var(--profile-figure-size) + 8px); + border: 2px solid var(--color-primary); + pointer-events: none; + + opacity: 0; + transform: scale(0.8); + + transition: all 0.2s; + } +} + +.user-menu-control__img { + display: block; + width: 100%; + object-fit: cover; + border-radius: inherit; +} + +.user-menu__meta { + max-width: 100px; +} + +.user-menu__meta-title { + transition: color 0.2s; +} |
