diff options
Diffstat (limited to 'core/css/inputs.scss')
-rw-r--r-- | core/css/inputs.scss | 896 |
1 files changed, 896 insertions, 0 deletions
diff --git a/core/css/inputs.scss b/core/css/inputs.scss new file mode 100644 index 00000000000..27136b69ad4 --- /dev/null +++ b/core/css/inputs.scss @@ -0,0 +1,896 @@ +/*! + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +@use 'variables'; +@use 'sass:color'; +@use 'functions'; + + /* Specifically override browser styles */ +input, textarea, select, button, div[contenteditable=true], div[contenteditable=false] { + font-family: var(--font-face); +} + +.select2-container-multi .select2-choices .select2-search-field input, .select2-search input, .ui-widget { + font-family: var(--font-face) !important; +} + +.select2-container.select2-drop-above .select2-choice { + background-image: unset !important; +} + +$opacity-disabled: .7; + +/* Simple selector to allow easy overriding */ +select, +button:not( + .button-vue, + /* "vs__" class prefix is used in the vue-select lib */ + [class^="vs__"] +), +input, +textarea, +div[contenteditable=true], +div[contenteditable=false] { + width: 130px; + min-height: var(--default-clickable-area); + box-sizing: border-box; +} + +/** + * color-main-text normal state + * color-main-text active state + * color-text-maxcontrast disabled state + */ + +button:not(.button-vue), +input:not([type='range']), +textarea { + &:disabled { + cursor: default; + color: var(--color-text-maxcontrast); + border-color: var(--color-border-dark); + opacity: $opacity-disabled; + } +} + +input:not([type="range"]) { + outline: none; +} + +/* Default global values */ +div.select2-drop .select2-search input, // TODO: REMOVE WHEN DROPPING SELECT2 +input[type='submit'], +input[type='button'], +input[type='reset'], +button:not( + .button-vue, + [class^="vs__"] +), +.button, +.pager li a { + padding: 7px 14px; + background-color: var(--color-main-background); + color: var(--color-main-text); + border: 1px solid var(--color-border-dark); + font-size: var(--default-font-size); + outline: none; + border-radius: var(--border-radius); + cursor: text; + &:not(.app-navigation-entry-button) { + margin: 3px; + margin-inline-start: 0; + } + &:not( + :disabled, + .primary + ) { + &:not(.app-navigation-entry-button) { + &:hover, + &:focus, + &.active { + /* active class used for multiselect */ + border-color: var(--color-main-text); + outline: none; + } + &:active { + outline: none; + background-color: var(--color-main-background); + color: var(--color-main-text); + } + } + &:focus-visible { + box-shadow: 0 0 0 4px var(--color-main-background) !important; + outline: 2px solid var(--color-main-text) !important; + } + } + &:disabled { + background-color: var(--color-background-dark); + color: var(--color-main-text); + cursor: default; + opacity: 0.5; + } + &:required { + box-shadow: none; + } + &:user-invalid { + box-shadow: 0 0 0 2px var(--color-error) !important; + } + /* Primary action button, use sparingly */ + &.primary { + background-color: var(--color-primary-element); + border-color: var(--color-primary-element); + color: var(--color-primary-element-text); + cursor: pointer; + + /* Apply border to primary button if on log in page (and not in a dark container) or if in header */ + #body-login :not(.body-login-container) &, + #header & { + border-color: var(--color-primary-element-text); + } + + &:not(:disabled) { + &:hover, + &:focus, + &:active { + background-color: var(--color-primary-element-hover); + border-color: var(--color-primary-element-hover); + } + &:focus, + &:focus-visible { + box-shadow: 0 0 0 2px var(--color-main-text); + } + &:active { + color: var(--color-primary-element-text-dark); + } + } + &:disabled { + // opacity is already defined to .5 if disabled + background-color: var(--color-primary-element); + color: var(--color-primary-element-text-dark); + cursor: default; + } + } +} + +div[contenteditable=false] { + margin: 3px; + margin-inline-start: 0; + padding: 7px 6px; + font-size: 13px; + border: 1px solid var(--color-background-darker); + outline: none; + border-radius: var(--border-radius); + + background-color: var(--color-background-dark); + color: var(--color-text-maxcontrast); + cursor: default; + opacity: 0.5; +} + +/* Specific override */ +input { + &:not([type='radio']):not([type='checkbox']):not([type='range']):not([type='submit']):not([type='button']):not([type='reset']):not([type='color']):not([type='file']):not([type='image']) { + -webkit-appearance: textfield; + -moz-appearance: textfield; + appearance: textfield; + // force height for inline elements like inputs (not textarea, contenteditable...) + height: var(--default-clickable-area); + } + &[type='radio'], + &[type='checkbox'], + &[type='file'], + &[type='image'] { + height: auto; + width: auto; + } + /* Color input doesn't respect the initial height + so we need to set a custom one */ + &[type='color'] { + margin: 3px; + padding: 0 2px; + min-height: 30px; + width: 40px; + cursor: pointer; + } + &[type='hidden'] { + height: 0; + width: 0; + } + &[type='time'] { + width: initial; + } +} + +/* 'Click' inputs */ +select, +button:not( + .button-vue, + [class^="vs__"] +), +.button, +input[type='button'], +input[type='submit'], +input[type='reset'] { + padding: calc((var(--default-clickable-area) - 1lh) / 2) calc(3 * var(--default-grid-baseline)); + font-size: var(--default-font-size); + width: auto; + min-height: var(--default-clickable-area); + cursor: pointer; + box-sizing: border-box; + color: var(--color-primary-element-light-text); + background-color: var(--color-primary-element-light); + border: none; + + &:hover, + &:focus { + background-color: var(--color-primary-element-light-hover); + } + + &:disabled { + cursor: default; + } +} + +input:not( + [type='range'], + .input-field__input, + [type='submit'], + [type='button'], + [type='reset'], + .multiselect__input, + .select2-input, + .action-input__input, + [class^="vs__"] +), +select, +div[contenteditable=true], +textarea { + margin: 3px; + margin-inline-start: 0; + padding: 0 12px; + font-size: var(--default-font-size); + background-color: var(--color-main-background); + color: var(--color-main-text); + border: 2px solid var(--color-border-maxcontrast); + height: 36px; + outline: none; + border-radius: var(--border-radius-large); + text-overflow: ellipsis; + cursor: pointer; + &:not(:disabled):hover, &:not(:disabled):focus, &:not(:disabled):active { + border-color: 2px solid var(--color-main-text); + box-shadow: 0 0 0 2px var(--color-main-background); + } + &:not(:disabled):focus { + cursor: text; + } +} + +.multiselect__input, .select2-input { + background-color: var(--color-main-background); + color: var(--color-main-text); +} + +textarea, div[contenteditable=true] { + padding: 12px; + height: auto; +} + +/* Override the ugly select arrow */ +select { + background: var(--icon-triangle-s-dark) no-repeat; + appearance: none; + background-color: var(--color-main-background); + padding-inline-end: 28px !important; +} + +body[dir='ltr'] select { + background-position: right 8px center; +} + +body[dir='rtl'] select { + background-position: left 8px center; +} + +select, +button:not( + .button-vue, + [class^="vs__"] +), +.button { + * { + cursor: pointer; + } + + &:disabled { + * { + cursor: default; + } + } +} + +/* Buttons */ +button:not( + .button-vue, + [class^="vs__"] +), +.button, +input[type='button'], +input[type='submit'], +input[type='reset'] { + font-weight: bold; + border-radius: var(--border-radius-element); + + /* Get rid of the inside dotted line in Firefox */ + &::-moz-focus-inner { + border: 0; + } + + &.error { + background-color: var(--color-error) !important; + border-color: var(--color-error) !important; + color: #fff !important; + &:hover{ + background-color: var(--color-error-hover) !important; + border-color: var(--color-main-text) !important; + } + } +} + +button:not( + .button-vue, + .action-button, + [class^="vs__"] +), +.button { + > span { + /* icon position inside buttons */ + &[class^='icon-'], + &[class*=' icon-'] { + display: inline-block; + vertical-align: text-bottom; + opacity: 0.5; + } + } +} + +/* Confirm inputs */ +input[type='text'], +input[type='password'], +input[type='email'] { + + .icon-confirm { + margin-inline-start: -13px !important; + border-inline-start-color: transparent !important; + border-radius: 0 var(--border-radius-large) var(--border-radius-large) 0 !important; + border-width: 2px; + background-clip: padding-box; + /* Avoid background under border */ + background-color: var(--color-main-background) !important; + opacity: 1; + height: var(--default-clickable-area); + width: var(--default-clickable-area); + padding: 7px 6px; + cursor: pointer; + margin-inline-end: 0; + &:disabled { + cursor: default; + @include functions.icon-color('confirm-fade', 'actions', variables.$color-black, 2, true); + } + } + + /* only show confirm borders if input is not focused */ + &:not(:active):not(:hover):not(:focus){ + &:invalid { + + .icon-confirm { + border-color: var(--color-error); + } + } + + .icon-confirm { + &:active, + &:hover, + &:focus { + border-color: var(--color-primary-element) !important; + border-radius: var(--border-radius) !important; + &:disabled { + border-color: var(--color-background-darker) !important; + } + } + } + } + &:active, + &:hover, + &:focus { + + .icon-confirm { + border-color: var(--color-primary-element) !important; + border-inline-start-color: transparent !important; + /* above previous input */ + z-index: 2; + } + } +} + + +/* Various Fixes */ +button img, +.button img { + cursor: pointer; +} + +select, +.button.multiselect { + font-weight: normal; +} + +/* Radio & Checkboxes */ +$checkbox-radio-size: 14px; +$color-checkbox-radio-white: #fff; + +input[type='checkbox'], +input[type='radio'] { + &.radio, + &.checkbox { + position: absolute; + inset-inline-start: -10000px; + top: auto; + width: 1px; + height: 1px; + overflow: hidden; + + label { + user-select: none; + } + &:disabled + label, + &:disabled + label:before { + cursor: default; + } + + label:before { + content: ''; + display: inline-block; + height: $checkbox-radio-size; + width: $checkbox-radio-size; + vertical-align: middle; + border-radius: 50%; + margin: 0 3px; + margin-inline: 3px 6px; + border: 1px solid var(--color-text-maxcontrast); + } + &:not(:disabled):not(:checked) + label:hover:before, + &:focus + label:before { + border-color: var(--color-primary-element); + } + &:focus-visible + label { + outline-style: solid; + outline-color: var(--color-main-text); + outline-width: 1px; + outline-offset: 2px; + } + &:checked + label:before, + &.checkbox:indeterminate + label:before { + /* ^ :indeterminate have a strange behavior on radio, + so we respecified the checkbox class again to be safe */ + box-shadow: inset 0px 0px 0px 2px var(--color-main-background); + background-color: var(--color-primary-element); + border-color: var(--color-primary-element); + } + &:disabled + label:before { + border: 1px solid var(--color-text-maxcontrast); + background-color: var(--color-text-maxcontrast) !important; /* override other status */ + } + &:checked:disabled + label:before { + background-color: var(--color-text-maxcontrast); + } + + // Detail description below label of checkbox or radio button + & + label ~ em { + display: inline-block; + margin-inline-start: 25px; + } + & + label ~ em:last-of-type { + margin-bottom: $checkbox-radio-size; + } + } + &.checkbox { + + label:before { + border-radius: 1px; + height: $checkbox-radio-size; + width: $checkbox-radio-size; + box-shadow: none !important; + background-position: center; + } + &:checked + label:before { + background-image: url('../img/actions/checkbox-mark.svg'); + } + &:indeterminate + label:before { + background-image: url('../img/actions/checkbox-mixed.svg'); + } + } + + /* We do not use the variables as we keep the colours as white for this variant */ + &.radio--white, + &.checkbox--white { + + label:before, + &:focus + label:before { + border-color: color.adjust($color-checkbox-radio-white, $lightness: -27%, $space: hsl); + } + &:not(:disabled):not(:checked) + label:hover:before { + border-color: $color-checkbox-radio-white; + } + &:checked + label:before { + box-shadow: inset 0px 0px 0px 2px var(--color-main-background); + background-color: color.adjust($color-checkbox-radio-white, $lightness: -14%, $space: hsl); + border-color: color.adjust($color-checkbox-radio-white, $lightness: -14%, $space: hsl); + } + &:disabled + label:before { + background-color: color.adjust($color-checkbox-radio-white, $lightness: -27%, $space: hsl) !important; /* override other status */ + border-color: rgba($color-checkbox-radio-white, 0.4) !important; /* override other status */ + } + &:checked:disabled + label:before { + box-shadow: inset 0px 0px 0px 2px var(--color-main-background); + border-color: rgba($color-checkbox-radio-white, 0.4) !important; /* override other status */ + background-color: color.adjust($color-checkbox-radio-white, $lightness: -27%, $space: hsl); + } + } + &.checkbox--white { + &:checked + label:before, + &:indeterminate + label:before { + background-color: transparent !important; /* Override default checked */ + border-color: $color-checkbox-radio-white !important; /* Override default checked */ + background-image: url('../img/actions/checkbox-mark-white.svg'); + } + &:indeterminate + label:before { + background-image: url('../img/actions/checkbox-mixed-white.svg'); + } + &:disabled + label:before { + opacity: 0.7; /* No other choice for white background image */ + } + } +} + +/* Select2 overriding. Merged to core with vendor stylesheet */ +div.select2-drop { + margin-top: -2px; + background-color: var(--color-main-background); + &.select2-drop-active { + border-color: var(--color-border-dark); + } + .avatar { + display: inline-block; + margin-inline-end: 8px; + vertical-align: middle; + img { + cursor: pointer; + } + } + .select2-search input { + min-height: auto; + background: var(--icon-search-dark) no-repeat !important; + background-origin: content-box !important; + } + .select2-results { + max-height: 250px; + margin: 0; + padding: 0; + .select2-result-label { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + span { + cursor: pointer; + em { + cursor: inherit; + background: unset; + } + } + } + .select2-result, + .select2-no-results, + .select2-searching { + position: relative; + display: list-item; + padding: 12px; + background-color: transparent; + cursor: pointer; + color: var(--color-text-maxcontrast); + } + .select2-result { + &.select2-selected { + background-color: var(--color-background-dark); + } + } + .select2-highlighted { + background-color: var(--color-background-dark); + color: var(--color-main-text); + } + } +} + +body[dir='ltr'] div.select2-drop .select2-search input { + background-position: right center !important; +} + +body[dir='rtl'] div.select2-drop .select2-search input { + background-position: left center !important; +} + +.select2-chosen, +#select2-drop { + .avatar, + .avatar img { + cursor: pointer; + } +} + +div.select2-container-multi { + .select2-choices, + &.select2-container-active .select2-choices { + box-shadow: none; + white-space: nowrap; + text-overflow: ellipsis; + background: var(--color-main-background); + color: var(--color-text-maxcontrast) !important; + box-sizing: content-box; + border-radius: var(--border-radius-large); + border: 2px solid var(--color-border-dark); + margin: 0; + padding: 6px; + min-height: 44px; + &:focus-within { + border-color: var(--color-primary-element) + } + .select2-search-choice { + line-height: 20px; + padding-inline-start: 5px; + &.select2-search-choice-focus, + &:hover, + &:active, + & { + background-image: none; + background-color: var(--color-main-background); + color: var(--color-text-maxcontrast); + border: 1px solid var(--color-border-dark); + } + .select2-search-choice-close { + display: none; + } + } + .select2-search-field input { + line-height: 20px; + min-height: 28px; + max-height: 28px; + color: var(--color-main-text); + &.select2-active { + background: none !important; + } + } + } +} + +div.select2-container { + margin: 3px; + margin-inline-start: 0; + &.select2-container-multi .select2-choices { + display: flex; + flex-wrap: wrap; + li { + float: none; + } + } + a.select2-choice { + box-shadow: none; + white-space: nowrap; + text-overflow: ellipsis; + background: var(--color-main-background); + color: var(--color-text-maxcontrast) !important; + box-sizing: content-box; + border-radius: var(--border-radius-large); + border: 2px solid var(--color-border-dark); + margin: 0; + padding: 6px 12px; + min-height: 44px; + &:focus-within { + border-color: var(--color-primary-element) + } + .select2-search-choice { + line-height: 20px; + padding-inline-start: 5px; + background-image: none; + background-color: var(--color-background-dark); + border-color: var(--color-background-dark); + .select2-search-choice-close { + display: none; + } + &.select2-search-choice-focus, + &:hover { + background-color: var(--color-border); + border-color: var(--color-border); + } + } + .select2-arrow { + background: none; + border-radius: 0; + border: none; + b { + background: var(--icon-triangle-s-dark) no-repeat center !important; + opacity: .5; + } + } + &:hover .select2-arrow b, + &:focus .select2-arrow b, + &:active .select2-arrow b { + opacity: .7; + } + .select2-search-field input { + line-height: 20px; + } + } +} + +/* Vue v-select */ +.v-select { + margin: 3px; + margin-inline-start: 0; + display: inline-block; + .dropdown-toggle { + display: flex !important; + flex-wrap: wrap; + .selected-tag { + line-height: 20px; + padding-inline-start: 5px; + background-image: none; + background-color: var(--color-main-background); + color: var(--color-text-maxcontrast); + border: 1px solid var(--color-border-dark); + display: inline-flex; + align-items: center; + .close { + margin-inline-start: 3px; + } + } + } + .dropdown-menu { + padding: 0; + li { + padding: 5px; + position: relative; + display: list-item; + background-color: transparent; + cursor: pointer; + color: var(--color-text-maxcontrast); + a { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + height: 25px; + padding-block: 3px 4px; + padding-inline: 2px 7px; + margin: 0; + cursor: pointer; + min-height: 1em; + -webkit-touch-callout: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + display: inline-flex; + align-items: center; + background-color: transparent !important; + color: inherit !important; + &::before { + content: ' '; + background-image: var(--icon-checkmark-dark); + background-repeat: no-repeat; + background-position: center; + min-width: 16px; + min-height: 16px; + display: block; + opacity: 0.5; + margin-inline-end: 5px; + visibility: hidden; + } + } + &.highlight { + color: var(--color-main-text); + } + &.active > a { + background-color: var(--color-background-dark); + color: var(--color-main-text); + &::before { + visibility: visible; + } + } + } + } +} + +/* Progressbar */ +progress:not(.vue) { + display: block; + width: 100%; + padding: 0; + border: 0 none; + background-color: var(--color-background-dark); + border-radius: var(--border-radius); + flex-basis: 100%; + height: 5px; + overflow: hidden; + &.warn { + &::-moz-progress-bar { + background: var(--color-error); + } + &::-webkit-progress-value { + background: var(--color-error); + } + } + &::-webkit-progress-bar { + background: transparent; + } + &::-moz-progress-bar { + border-radius: var(--border-radius); + background: var(--color-primary-element); + transition: 250ms all ease-in-out; + } + &::-webkit-progress-value { + border-radius: var(--border-radius); + background: var(--color-primary-element); + transition: 250ms all ease-in-out; + } +} + +/* Animation */ +@keyframes shake { + 10%, + 90% { + transform: translate(-1px); + } + 20%, + 80% { + transform: translate(2px); + } + 30%, + 50%, + 70% { + transform: translate(-4px); + } + 40%, + 60% { + transform: translate(4px); + } +} + +.shake { + animation-name: shake; + animation-duration: .7s; + animation-timing-function: ease-out; +} + +// Keep the labels for screen readers but hide them since we use placeholders +// Same as .hidden-visually +label.infield { + position: absolute; + inset-inline-start: -10000px; + top: -10000px; + width: 1px; + height: 1px; + overflow: hidden; +} + +// when rules are grouped using the comma operator and one selector is invalid / unknown then the whole group is invalidated. +// https://www.w3.org/TR/selectors-3/#grouping +// In this case `::-ms-input-placeholder` is unknown to Firefox and Chrome +@mixin placeholder-style { + color: var(--color-text-maxcontrast); + font-size: var(--default-font-size); +} + +::placeholder { + @include placeholder-style; +} + +::-ms-input-placeholder { + @include placeholder-style; +} + +::-webkit-input-placeholder { + @include placeholder-style; +} |