summaryrefslogtreecommitdiffstats
path: root/apps/web-shared/src/styles/components
diff options
context:
space:
mode:
Diffstat (limited to 'apps/web-shared/src/styles/components')
-rw-r--r--apps/web-shared/src/styles/components/alert.scss69
-rw-r--r--apps/web-shared/src/styles/components/autocomplete.scss76
-rw-r--r--apps/web-shared/src/styles/components/btn-states.scss51
-rw-r--r--apps/web-shared/src/styles/components/chip.scss117
-rw-r--r--apps/web-shared/src/styles/components/circle-loader.scss315
-rw-r--r--apps/web-shared/src/styles/components/custom-checkbox.scss131
-rw-r--r--apps/web-shared/src/styles/components/custom-select.scss158
-rw-r--r--apps/web-shared/src/styles/components/details.scss57
-rw-r--r--apps/web-shared/src/styles/components/dropdown.scss98
-rw-r--r--apps/web-shared/src/styles/components/form-validator.scss18
-rw-r--r--apps/web-shared/src/styles/components/interactive-table.scss156
-rw-r--r--apps/web-shared/src/styles/components/list.scss195
-rw-r--r--apps/web-shared/src/styles/components/menu-bar.scss139
-rw-r--r--apps/web-shared/src/styles/components/menu.scss81
-rw-r--r--apps/web-shared/src/styles/components/modal.scss105
-rw-r--r--apps/web-shared/src/styles/components/pagination.scss77
-rw-r--r--apps/web-shared/src/styles/components/popover.scss38
-rw-r--r--apps/web-shared/src/styles/components/pre-header.scss46
-rw-r--r--apps/web-shared/src/styles/components/radios-checkboxes.scss134
-rw-r--r--apps/web-shared/src/styles/components/select-autocomplete.scss173
-rw-r--r--apps/web-shared/src/styles/components/tabbed-navigation.scss133
-rw-r--r--apps/web-shared/src/styles/components/table.scss147
-rw-r--r--apps/web-shared/src/styles/components/user-menu.scss81
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;
+}