diff --git a/.stylelintrc.json b/.stylelintrc.json new file mode 100644 index 0000000000..9fb2c5fe19 --- /dev/null +++ b/.stylelintrc.json @@ -0,0 +1,30 @@ +{ + "extends": [ + "stylelint-config-standard", + "stylelint-config-rational-order" + ], + "plugins": [ + "stylelint-order", + "stylelint-scss" + ], + "rules": { + "indentation": "tab", + "order/order": [ + "dollar-variables", + "custom-properties", + "declarations", + "at-variables", + "rules", + "at-rules" + ], + "at-rule-no-unknown": null, + "scss/at-rule-no-unknown": true, + "selector-pseudo-element-no-unknown": [ + true, + { + "ignorePseudoElements": ["v-deep"] + } + ], + "string-quotes": "single" + } +} diff --git a/package.json b/package.json index 02ce474533..5f54f64291 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,9 @@ "build": "vue-cli-service build", "test": "vue-cli-service test:unit", "lint": "vue-cli-service lint", + "lint:styles": "stylelint \"**/*.{vue,scss}\"", "fix": "prettier --write \"src/**/*.{js,vue,ts}\"", + "fix:styles": "stylelint --fix \"**/*.{vue,scss}\"", "storybook": "start-storybook -p 6006", "build-storybook": "build-storybook" }, @@ -69,6 +71,11 @@ "sass": "^1.23.7", "sass-loader": "^8.0.0", "storybook-addon-themes": "^5.3.0", + "stylelint": "^13.1.0", + "stylelint-config-rational-order": "^0.1.2", + "stylelint-config-standard": "^20.0.0", + "stylelint-order": "^4.0.0", + "stylelint-scss": "^3.14.2", "typescript": "^3.7.4", "vue-cli-plugin-storybook": "^1.1.0", "vue-loader": "^15.8.3", @@ -81,9 +88,18 @@ "pre-commit": "lint-staged" }, "lint-staged": { - "*.{js,vue,ts}": [ + "*.{js,ts}": [ "vue-cli-service lint", "git add" + ], + "*.{scss}": [ + "stylelint --fix", + "git add" + ], + "*.vue": [ + "vue-cli-service lint", + "stylelint --fix", + "git add" ] } } diff --git a/src/components/v-avatar/v-avatar.vue b/src/components/v-avatar/v-avatar.vue index 3be0c198c1..38fa358337 100644 --- a/src/components/v-avatar/v-avatar.vue +++ b/src/components/v-avatar/v-avatar.vue @@ -33,18 +33,18 @@ export default createComponent({ --v-avatar-color: var(--teal); --v-avatar-size: 48px; - background-color: var(--v-avatar-color); - display: flex; - justify-content: center; - align-items: center; position: relative; + display: flex; + align-items: center; + justify-content: center; width: var(--v-avatar-size); height: var(--v-avatar-size); - border-radius: 50%; overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; color: var(--white); + white-space: nowrap; + text-overflow: ellipsis; + background-color: var(--v-avatar-color); + border-radius: 50%; &.tile { border-radius: 0; diff --git a/src/components/v-button/v-button.vue b/src/components/v-button/v-button.vue index 47b6eb652e..17941ebe00 100644 --- a/src/components/v-button/v-button.vue +++ b/src/components/v-button/v-button.vue @@ -69,28 +69,32 @@ export default createComponent({ --v-button-hover-background-color: var(--button-primary-background-color-hover); --v-button-font-size: 16px; - color: var(--v-button-color); - background-color: var(--v-button-background-color); - border-radius: var(--border-radius); - font-weight: var(--weight-bold); - cursor: pointer; - border: var(--input-border-width) solid var(--v-button-background-color); - - font-size: var(--v-button-font-size); - padding: 0 19px; - min-width: 78px; + position: relative; width: var(--v-button-width); + min-width: 78px; height: var(--v-button-height); - + padding: 0 19px; + color: var(--v-button-color); + font-weight: var(--weight-bold); + font-size: var(--v-button-font-size); + background-color: var(--v-button-background-color); + border: var(--input-border-width) solid var(--v-button-background-color); + border-radius: var(--border-radius); + cursor: pointer; transition: var(--fast) var(--transition); transition-property: background-color border; - position: relative; - &:focus { outline: 0; } + &:disabled { + color: var(--button-primary-text-color-disabled); + background-color: var(--button-primary-background-color-disabled); + border: var(--input-border-width) solid var(--button-primary-background-color-disabled); + cursor: not-allowed; + } + &:not(.loading):not(:disabled):hover { color: var(--v-button-hover-color); background-color: var(--v-button-hover-background-color); @@ -110,52 +114,49 @@ export default createComponent({ background-color: transparent; } - &:disabled { - background-color: var(--button-primary-background-color-disabled); - border: var(--input-border-width) solid var(--button-primary-background-color-disabled); - color: var(--button-primary-text-color-disabled); - cursor: not-allowed; - } - &.x-small { --v-button-height: 28px; --v-button-font-size: 12px; - padding: 0 12px; + min-width: 48px; + padding: 0 12px; } &.small { --v-button-height: 36px; --v-button-font-size: 14px; - padding: 0 16px; + min-width: 64px; + padding: 0 16px; } &.large { --v-button-height: 52px; - padding: 0 23px; + min-width: 92px; + padding: 0 23px; } &.x-large { --v-button-height: 58px; --v-button-font-size: 18px; - padding: 0 32px; + min-width: 120px; + padding: 0 32px; } &.icon { + width: var(--v-button-height); min-width: 0; padding: 0; - width: var(--v-button-height); } .content, .spinner { + max-width: 100%; + overflow: hidden; white-space: nowrap; text-overflow: ellipsis; - overflow: hidden; - max-width: 100%; } .content { diff --git a/src/components/v-checkbox/v-checkbox.vue b/src/components/v-checkbox/v-checkbox.vue index a0779a3640..0e1422547b 100644 --- a/src/components/v-checkbox/v-checkbox.vue +++ b/src/components/v-checkbox/v-checkbox.vue @@ -89,22 +89,22 @@ export default createComponent({ .v-checkbox { --v-checkbox-color: var(--input-background-color-active); - font-size: 0; - appearance: none; - background-color: transparent; - border-radius: 0; - border: none; display: flex; align-items: center; + font-size: 0; text-align: left; + background-color: transparent; + border: none; + border-radius: 0; + appearance: none; .label:not(:empty) { - font-size: var(--input-font-size); margin-left: 8px; - vertical-align: middle; + overflow: hidden; + font-size: var(--input-font-size); white-space: nowrap; text-overflow: ellipsis; - overflow: hidden; + vertical-align: middle; } & .v-icon { diff --git a/src/components/v-chip/v-chip.vue b/src/components/v-chip/v-chip.vue index e53c32bb8a..d3706dcecf 100644 --- a/src/components/v-chip/v-chip.vue +++ b/src/components/v-chip/v-chip.vue @@ -92,14 +92,13 @@ export default createComponent({ --v-chip-hover-background-color: var(--chip-primary-background-color-hover); display: inline-flex; + align-items: center; height: 32px; padding: 0 12px; - align-items: center; - color: var(--v-chip-color); + font-weight: var(--weight-normal); background-color: var(--v-chip-background-color); border-radius: 16px; - font-weight: var(--weight-normal); &:hover { color: var(--v-chip-hover-color); @@ -121,26 +120,26 @@ export default createComponent({ } &.x-small { - font-size: 12px; height: 20px; + font-size: 12px; border-radius: 10px; } &.small { - font-size: 14px; height: 24px; + font-size: 14px; border-radius: 12px; } &.large { - font-size: 16px; height: 44px; + font-size: 16px; border-radius: 22px; } &.x-large { - font-size: 18px; height: 48px; + font-size: 18px; border-radius: 24px; } @@ -150,16 +149,15 @@ export default createComponent({ .close-outline { position: relative; + right: -4px; display: inline-flex; - justify-content: center; align-items: center; - + justify-content: center; + width: 14px; + height: 14px; + margin-left: 4px; background-color: var(--chip-primary-close-color); border-radius: 10px; - height: 14px; - width: 14px; - right: -4px; - margin-left: 4px; .close { --v-icon-color: var(--v-chip-background-color); diff --git a/src/components/v-icon/v-icon.vue b/src/components/v-icon/v-icon.vue index 66d79a0b76..bd476a59dd 100644 --- a/src/components/v-icon/v-icon.vue +++ b/src/components/v-icon/v-icon.vue @@ -76,29 +76,31 @@ export default createComponent({ --v-icon-color: currentColor; --v-icon-size: 24px; - color: var(--v-icon-color); position: relative; display: inline-block; - font-size: 0; width: var(--v-icon-size); height: var(--v-icon-size); + color: var(--v-icon-color); + font-size: 0; vertical-align: middle; i { - font-size: var(--v-icon-size); - font-family: 'Material Icons'; - font-weight: normal; - font-style: normal; display: inline-block; + font-weight: normal; + font-size: var(--v-icon-size); + /* stylelint-disable-next-line font-family-no-missing-generic-family-keyword */ + font-family: 'Material Icons'; + font-style: normal; line-height: 1; - text-transform: none; letter-spacing: normal; - word-wrap: normal; white-space: nowrap; - font-feature-settings: 'liga'; + text-transform: none; vertical-align: middle; + word-wrap: normal; + font-feature-settings: 'liga'; &.outline { + /* stylelint-disable-next-line font-family-no-missing-generic-family-keyword */ font-family: 'Material Icons Outline'; } } @@ -119,7 +121,8 @@ export default createComponent({ &.sup { --v-icon-size: 8px; - vertical-align: 0px; + + vertical-align: 0; i { vertical-align: 5px; @@ -148,8 +151,8 @@ export default createComponent({ } &.right { - margin-left: 8px; margin-right: -4px; + margin-left: 8px; } } diff --git a/src/components/v-input/v-input.vue b/src/components/v-input/v-input.vue index fe2ff96c4e..a7371e5450 100644 --- a/src/components/v-input/v-input.vue +++ b/src/components/v-input/v-input.vue @@ -87,13 +87,13 @@ export default createComponent({ } .input { - height: 100%; display: flex; align-items: center; - border: var(--input-border-width) solid var(--input-border-color); - background-color: var(--input-background-color); - border-radius: var(--border-radius); + height: 100%; padding: var(--input-padding); + background-color: var(--input-background-color); + border: var(--input-border-width) solid var(--input-border-color); + border-radius: var(--border-radius); transition: border-color var(--fast) var(--transition); .prepend { @@ -116,20 +116,20 @@ export default createComponent({ width: 100%; } + input { + flex-grow: 1; + height: 100%; + background-color: transparent; + border: none; + appearance: none; + } + &.monospace { input { font-family: var(--family-monospace); } } - input { - appearance: none; - flex-grow: 1; - height: 100%; - border: none; - background-color: transparent; - } - .prefix, .suffix { color: var(--input-border-color-focus); diff --git a/src/components/v-overlay/v-overlay.vue b/src/components/v-overlay/v-overlay.vue index 5fcd22ac15..245adf2246 100644 --- a/src/components/v-overlay/v-overlay.vue +++ b/src/components/v-overlay/v-overlay.vue @@ -41,13 +41,13 @@ export default createComponent({ position: fixed; top: 0; left: 0; + z-index: var(--v-overlay-z-index); + display: flex; + align-items: center; + justify-content: center; width: 100%; height: 100%; pointer-events: none; - display: flex; - justify-content: center; - align-items: center; - z-index: var(--v-overlay-z-index); &.has-click { cursor: pointer; diff --git a/src/components/v-progress/linear/v-progress-linear.vue b/src/components/v-progress/linear/v-progress-linear.vue index 144ed3ac19..544fe9d49b 100644 --- a/src/components/v-progress/linear/v-progress-linear.vue +++ b/src/components/v-progress/linear/v-progress-linear.vue @@ -65,19 +65,19 @@ export default createComponent({ --v-progress-linear-color: var(--progress-background-color-accent); --v-progress-linear-background-color: var(--progress-background-color); - background-color: var(--v-progress-linear-background-color); - height: var(--v-progress-linear-height); position: relative; - width: 100%; display: flex; - justify-content: center; align-items: center; + justify-content: center; + width: 100%; + height: var(--v-progress-linear-height); overflow: hidden; + background-color: var(--v-progress-linear-background-color); .inner { + position: absolute; top: 0; left: 0; - position: absolute; height: 100%; background-color: var(--v-progress-linear-color); } @@ -87,7 +87,7 @@ export default createComponent({ } &.bottom { - bottom: 0px; + bottom: 0; } &.fixed { @@ -95,11 +95,11 @@ export default createComponent({ } &.indeterminate .inner { + position: relative; width: 100% !important; transform-origin: left; - will-change: transform; animation: indeterminate 2s infinite; - position: relative; + will-change: transform; } &.rounded, @@ -108,7 +108,7 @@ export default createComponent({ } &.top { - top: 0px; + top: 0; } } @@ -117,10 +117,12 @@ export default createComponent({ transform: scaleX(0); animation-timing-function: cubic-bezier(0.1, 0.6, 0.9, 0.5); } + 50% { transform: scaleX(1) translateX(25%); animation-timing-function: cubic-bezier(0.4, 0.1, 0.2, 0.9); } + 100% { transform: scaleX(1) translateX(100%); animation-timing-function: cubic-bezier(0.1, 0.6, 0.9, 0.5); diff --git a/src/components/v-sheet/v-sheet.vue b/src/components/v-sheet/v-sheet.vue index ddf03cfb70..15cfb7814c 100644 --- a/src/components/v-sheet/v-sheet.vue +++ b/src/components/v-sheet/v-sheet.vue @@ -13,26 +13,22 @@ export default createComponent({}); diff --git a/src/components/v-slider/v-slider.vue b/src/components/v-slider/v-slider.vue index 937a844f49..f1fd90b40c 100644 --- a/src/components/v-slider/v-slider.vue +++ b/src/components/v-slider/v-slider.vue @@ -101,96 +101,98 @@ export default createComponent({ } .slider { - flex-grow: 1; position: relative; top: -3px; + flex-grow: 1; input { - -webkit-appearance: none; - appearance: none; width: 100%; height: 2px; - - /* - * The vendor specific styling for the tracks needs to be separate into individual - * statements. In browsers, if one of the statements is unknown, the whole selector is - * invalidated. We're using these 'local' mixins to facilitate that. - */ - - @mixin v-slider-track { - height: 2px; - background: var(--v-slider-color); - box-shadow: none; - border: none; - border-radius: 2px; - } + -webkit-appearance: none; + appearance: none; &::-webkit-slider-runnable-track { - @include v-slider-track; + height: 2px; + background: var(--v-slider-color); + border: none; + border-radius: 2px; + box-shadow: none; } &::-moz-range-track { - @include v-slider-track; - } - - @mixin v-slider-thumb { - -webkit-appearance: none; - appearance: none; - box-shadow: none; + height: 2px; + background: var(--v-slider-color); border: none; - height: 14px; - width: 14px; - border-radius: 50%; - background: var(--v-slider-thumb-color); - margin-top: -6px; - cursor: ew-resize; - box-shadow: 0 0 0 4px var(--input-background-color); - z-index: 3; - position: relative; + border-radius: 2px; + box-shadow: none; } &::-webkit-slider-thumb { - @include v-slider-thumb; + position: relative; + z-index: 3; + width: 14px; + height: 14px; + margin-top: -6px; + background: var(--v-slider-thumb-color); + border: none; + border-radius: 50%; + box-shadow: none; + box-shadow: 0 0 0 4px var(--input-background-color); + cursor: ew-resize; + -webkit-appearance: none; + appearance: none; } &::-moz-range-thumb { - @include v-slider-thumb; + position: relative; + z-index: 3; + width: 14px; + height: 14px; + margin-top: -6px; + background: var(--v-slider-thumb-color); + border: none; + border-radius: 50%; + box-shadow: none; + box-shadow: 0 0 0 4px var(--input-background-color); + cursor: ew-resize; + -webkit-appearance: none; + appearance: none; } } .fill { position: absolute; - left: 0; - right: 0; top: 50%; - transform: translateY(2px) scaleX(calc(var(--_v-slider-percentage) / 100)); - transform-origin: left; + right: 0; + left: 0; + z-index: 2; height: 2px; background-color: var(--v-slider-fill-color); + transform: translateY(2px) scaleX(calc(var(--_v-slider-percentage) / 100)); + transform-origin: left; pointer-events: none; - z-index: 2; } .ticks { - opacity: 0; position: absolute; top: 11px; left: 0; + display: flex; + align-items: center; + justify-content: space-between; width: 100%; height: 4px; - pointer-events: none; - display: flex; - justify-content: space-between; - align-items: center; padding: 0 7px; + opacity: 0; transition: opacity var(--fast) var(--transition); + pointer-events: none; .tick { display: inline-block; width: 4px; height: 4px; - border-radius: 50%; background-color: var(--v-slider-fill-color); + border-radius: 50%; } } @@ -203,29 +205,29 @@ export default createComponent({ } .thumb-label { - opacity: 0; position: absolute; - width: max-content; - left: calc(var(--_v-slider-percentage) * 1%); - transform: translateX(-50%); top: 10px; + left: calc(var(--_v-slider-percentage) * 1%); + width: max-content; + padding: 4px 8px; color: var(--input-text-color); + font-size: var(--input-font-size); background-color: var(--input-background-color-alt); border-radius: var(--border-radius); - font-size: var(--input-font-size); - padding: 4px 8px; + transform: translateX(-50%); + opacity: 0; transition: opacity var(--fast) var(--transition); - &:before { - content: ''; + &::before { position: absolute; top: -4px; left: calc(50%); width: 10px; height: 10px; + background-color: var(--input-background-color-alt); border-radius: var(--border-radius); transform: translateX(-50%) rotate(45deg); - background-color: var(--input-background-color-alt); + content: ''; } } diff --git a/src/components/v-spinner/v-spinner.vue b/src/components/v-spinner/v-spinner.vue index ea72e0e981..30d0fe36c2 100644 --- a/src/components/v-spinner/v-spinner.vue +++ b/src/components/v-spinner/v-spinner.vue @@ -20,20 +20,17 @@ export default createComponent({ .v-spinner { --v-spinner-color: var(--loading-background-color-accent); --v-spinner-background-color: var(--loading-background-color); - --v-spinner-speed: 1s; --v-spinner-size: 28px; --v-spinner-line-size: 3px; + position: relative; width: var(--v-spinner-size); height: var(--v-spinner-size); - position: relative; - - border-radius: 100%; + background-color: transparent; border: var(--v-spinner-line-size) solid var(--v-spinner-background-color); border-top: var(--v-spinner-line-size) solid var(--v-spinner-color); - background-color: transparent; - + border-radius: 100%; animation: rotate var(--v-spinner-speed) infinite linear; &.x-small { diff --git a/src/components/v-switch/v-switch.vue b/src/components/v-switch/v-switch.vue index f8f2a7ee37..ccb68c214b 100644 --- a/src/components/v-switch/v-switch.vue +++ b/src/components/v-switch/v-switch.vue @@ -75,67 +75,60 @@ export default createComponent({ .v-switch { --v-switch-color: var(--input-background-color-active); - font-size: 0; - appearance: none; - background-color: transparent; - border-radius: 0; - border: none; display: flex; align-items: center; + font-size: 0; + background-color: transparent; + border: none; + border-radius: 0; + appearance: none; .switch { - display: inline-block; - height: 24px; - width: 44px; - border-radius: 12px; - border: var(--input-border-width) solid var(--input-border-color); position: relative; + display: inline-block; + width: 44px; + height: 24px; + vertical-align: middle; + border: var(--input-border-width) solid var(--input-border-color); + border-radius: 12px; transition: var(--fast) var(--transition); transition-property: background-color border; - vertical-align: middle; - - &:not(:disabled)hover { - border-color: var(--input-border-color-hover); - } &:focus { outline: 0; } &::after { - content: ''; - width: 16px; - height: 16px; position: absolute; top: 2px; left: 2px; display: block; + width: 16px; + height: 16px; background-color: var(--input-border-color); border-radius: 8px; transition: transform var(--fast) var(--transition); + content: ''; + } + + &:hover { + border-color: var(--input-border-color-hover); } } - &[aria-pressed='true'] { - &:not(:disabled) { - .switch { - background-color: var(--v-switch-color); - border-color: var(--v-switch-color); + &[aria-pressed='true'] .switch { + background-color: var(--v-switch-color); + border-color: var(--v-switch-color); - &::after { - background-color: var(--input-text-color-active); - } - } - } - - .switch::after { + &::after { + background-color: var(--input-text-color-active); transform: translateX(20px); } } .label:not(:empty) { - font-size: var(--input-font-size); margin-left: 8px; + font-size: var(--input-font-size); vertical-align: middle; } @@ -144,6 +137,15 @@ export default createComponent({ .switch { background-color: var(--input-background-color-disabled); + border-color: var(--input-border-color); + + &::after { + background-color: var(--input-border-color); + } + + &:hover { + border-color: var(--input-border-color); + } } .label { diff --git a/src/components/v-table/_table-header.vue b/src/components/v-table/_table-header.vue index d63975f61f..c3f2c0407a 100644 --- a/src/components/v-table/_table-header.vue +++ b/src/components/v-table/_table-header.vue @@ -222,21 +222,21 @@ export default createComponent({