diff --git a/.stylelintrc.yaml b/.stylelintrc.yaml new file mode 100644 index 000000000..0613b192f --- /dev/null +++ b/.stylelintrc.yaml @@ -0,0 +1,256 @@ +extends: stylelint-config-standard +ignoreFiles: + - node_modules/**/* + - packages/**/node_modules/**/* +plugins: + - stylelint-selector-bem-pattern + - stylelint-scss +rules: + # Follow best practices + font-family-name-quotes: always-where-recommended + # http://stackoverflow.com/questions/2168855/is-quoting-the-value-of-url-really-necessary + function-url-quotes: never + # https://www.w3.org/TR/selectors/#attribute-selectors + # http://stackoverflow.com/q/3851091 + selector-attribute-quotes: always + # Single-quotes are a convention throughout our codebase + string-quotes: single + # https://github.com/sasstools/sass-lint/blob/develop/lib/config/property-sort-orders/smacss.yml + declaration-block-properties-order: + - display + - position + - top + - right + - bottom + - left + + - flex + - flex-basis + - flex-direction + - flex-flow + - flex-grow + - flex-shrink + - flex-wrap + - align-content + - align-items + - align-self + - justify-content + - order + + - width + - min-width + - max-width + + - height + - min-height + - max-height + + - margin + - margin-top + - margin-right + - margin-bottom + - margin-left + + - padding + - padding-top + - padding-right + - padding-bottom + - padding-left + + - float + - clear + + - columns + - column-gap + - column-fill + - column-rule + - column-span + - column-count + - column-width + + - transform + - transform-box + - transform-origin + - transform-style + + - transition + - transition-delay + - transition-duration + - transition-property + - transition-timing-function + + # Border + + - border + - border-top + - border-right + - border-bottom + - border-left + - border-width + - border-top-width + - border-right-width + - border-bottom-width + - border-left-width + + - border-style + - border-top-style + - border-right-style + - border-bottom-style + - border-left-style + + - border-radius + - border-top-left-radius + - border-top-right-radius + - border-bottom-left-radius + - border-bottom-right-radius + + - border-color + - border-top-color + - border-right-color + - border-bottom-color + - border-left-color + + - outline + - outline-color + - outline-offset + - outline-style + - outline-width + + # Background + + - background + - background-attachment + - background-clip + - background-color + - background-image + - background-repeat + - background-position + - background-size + + # Text + + - color + + - font + - font-family + - font-size + - font-smoothing + - font-style + - font-variant + - font-weight + + - letter-spacing + - line-height + - list-style + + - text-align + - text-decoration + - text-indent + - text-overflow + - text-rendering + - text-shadow + - text-transform + - text-wrap + + - white-space + - word-spacing + + # Other + + - border-collapse + - border-spacing + - box-shadow + - caption-side + - content + - cursor + - empty-cells + - opacity + - overflow + - quotes + - speak + - table-layout + - vertical-align + - visibility + - z-index + # The following prefix rules are enabled since we use autoprefixer + at-rule-no-vendor-prefix: true + media-feature-name-no-vendor-prefix: true + selector-no-vendor-prefix: true + value-no-vendor-prefix: true + # Usually if you're nesting past 3 levels deep there's a problem + max-nesting-depth: 3 + # Because we adhere to BEM we can limit the amount of necessary compound selectors. Most should + # just be 1 level selector. However, modifiers can introduce an additional compound selector. + # Futhermore, generic qualifying selectors (e.g. `[dir="rtl"]`) can introduce yet another level. + selector-max-compound-selectors: 3 + # For specificity: disallow IDs (0). Allow for complex combinations of classes, pseudo-classes, + # attribute modifiers based on selector-max-compound-selectors, plus an addition for + # pseudo-classes (4). Allow for pseudo-elements (1). + selector-max-specificity: 0,4,1 + # Disallow "@extend" in scss. + # http://csswizardry.com/2016/02/mixins-better-for-performance/ + # http://vanseodesign.com/css/sass-mixin-or-extend/ + # Besides performance, @extend actually *changes* the selector precedence by creating a compound + # selector, which can lead to ambiguous results. + at-rule-blacklist: + - extend + # Extremely useful for typos, and anything emergent can be ignored by this rule + property-no-unknown: true + # There is no reason that a specific ID would be needed for UI components + selector-no-id: true + # Qualifying types are not needed when using a naming system like BEM + selector-no-qualifying-type: true + # In general, we should *never* be modifying elements within our components, since we can't + # predict the use cases in which users would add a certain type of element into a component. + # An exception to this may be in packages/material-design-lite, in which case this rule could be + # disabled for that file, with an explanation. + selector-no-type: true + # Styles for components should never need the universal selector. + selector-no-universal: true + # Ensure any defined symbols are prefixed with "mdl-" + # TODO: Remove "md-" pattern. + custom-media-pattern: ^mdl?-.+ + custom-property-pattern: ^mdl?-.+ + selector-class-pattern: + - ^mdl?-.+ + - + resolveNestedSelectors: true + selector-id-pattern: ^mdl?-.+ + # Names are more semantic than numbers + font-weight-notation: named-where-possible + # :root is an important enough selector that it always stands on it's own. Also, there is no + # reason to make root a compound selector (?) + selector-root-no-composition: true + # http://www.paulirish.com/2010/the-protocol-relative-url/ + function-url-no-scheme-relative: true + # TODO: and FIXME: warnings are super useful because they remind us that we should address these + # within our codebase + comment-word-blacklist: + - + - /^TODO:/ + - /^FIXME:/ + - + severity: warning + # Part of google's style guide + number-leading-zero: never + + # We use Harry Roberts' BEM dialect as our preferred way to format classes. + # See: http://csswizardry.com/2013/01/mindbemding-getting-your-head-round-bem-syntax/ + # See: https://github.com/postcss/postcss-bem-linter/blob/c59db3f/lib/preset-patterns.js#L39 + plugin/selector-bem-pattern: + componentName: ^[a-z]+(?:-[a-z]+)*$ + # -__*--*[]* + componentSelectors: ^\.mdl?-{componentName}(?:__[a-z]+(?:-[a-z]+)*)*(?:--[a-z]+(?:-[a-z]+)*)*(?:\[.+\])*$ + + # SCSS naming patterns, just like our CSS conventions above. + # (note for $-vars we use a leading underscore for "private" variables) + scss/dollar-variable-pattern: ^_?mdl?-.+ + scss/at-function-pattern: ^mdl?-.+ + scss/at-mixin-pattern: ^mdl?-.+ + # Prevents unneeded nesting selectors + scss/selector-no-redundant-nesting-selector: true + # Since leading underscores are not needed, they can be omitted + scss/at-import-no-partial-leading-underscore: true + # Since mixins are explicit (`@include`) and parens are unnecessary for argumentless mixins, they + # can be omitted. + scss/at-mixin-no-argumentless-call-parentheses: true diff --git a/package.json b/package.json index 111e4fa0d..83b474baf 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "dist": "npm run clean && npm run build && npm run build:min", "dev": "npm run clean && MDL_ENV=development webpack-dev-server --content-base demos --inline --hot", "lint:js": "eslint --fix packages webpack.config.js", + "lint:css": "stylefmt -R packages && stylelint packages/**/*.scss", "postinstall": "lerna bootstrap" }, "devDependencies": { @@ -27,6 +28,11 @@ "raw-loader": "^0.5.1", "sass-loader": "^3.2.0", "style-loader": "^0.13.1", + "stylefmt": "^4.1.1", + "stylelint": "^7.0.3", + "stylelint-config-standard": "^11.0.0", + "stylelint-scss": "^1.2.1", + "stylelint-selector-bem-pattern": "^1.0.0", "webpack": "^1.13.1", "webpack-dev-server": "^1.14.1" }, @@ -34,6 +40,8 @@ "presets": [ "es2015" ], - "plugins": ["transform-object-assign"] + "plugins": [ + "transform-object-assign" + ] } } diff --git a/packages/material-design-lite/material-design-lite-theme.scss b/packages/material-design-lite/material-design-lite-theme.scss deleted file mode 100644 index ba358b358..000000000 --- a/packages/material-design-lite/material-design-lite-theme.scss +++ /dev/null @@ -1,3 +0,0 @@ -// Theme variable overrides could go here for custom builds, etc. - -@import 'mdl-checkbox/mdl-checkbox-theme'; diff --git a/packages/mdl-animation/_variables.scss b/packages/mdl-animation/_variables.scss index 8e388219e..4ccd06805 100644 --- a/packages/mdl-animation/_variables.scss +++ b/packages/mdl-animation/_variables.scss @@ -1,3 +1,3 @@ -$mdl-animation-linear-out-slow-in-timing-function: cubic-bezier(0.0, 0.0, 0.2, 0.1) !default; -$mdl-animation-fast-out-slow-in-timing-function: cubic-bezier(0.4, 0.0, 0.2, 1) !default; -$mdl-animation-fast-out-linear-in-timing-function: cubic-bezier(0.4, 0.0, 1, 1) !default +$mdl-animation-linear-out-slow-in-timing-function: cubic-bezier(0, 0, .2, .1) !default; +$mdl-animation-fast-out-slow-in-timing-function: cubic-bezier(.4, 0, .2, 1) !default; +$mdl-animation-fast-out-linear-in-timing-function: cubic-bezier(.4, 0, 1, 1) !default; diff --git a/packages/mdl-checkbox/_keyframes.scss b/packages/mdl-checkbox/_keyframes.scss index 13995ffaa..c0d2c5a71 100644 --- a/packages/mdl-checkbox/_keyframes.scss +++ b/packages/mdl-checkbox/_keyframes.scss @@ -11,7 +11,8 @@ } @keyframes md-checkbox-fade-out-background { - 0%, 50% { + 0%, + 50% { opacity: 1; } @@ -21,7 +22,8 @@ } @keyframes md-checkbox-unchecked-checked-checkmark-path { - 0%, 50% { + 0%, + 50% { stroke-dashoffset: $_md-checkbox-mark-path-length; } @@ -35,7 +37,8 @@ } @keyframes md-checkbox-unchecked-indeterminate-mixedmark { - 0%, 68.2% { + 0%, + 68.2% { transform: scaleX(0); } @@ -61,65 +64,68 @@ @keyframes md-checkbox-checked-indeterminate-checkmark { from { - @include mdl-animation-linear-out-slow-in; - opacity: 1; transform: rotate(0deg); + opacity: 1; + + @include mdl-animation-linear-out-slow-in; } to { - opacity: 0; transform: rotate(45deg); + opacity: 0; } } @keyframes md-checkbox-indeterminate-checked-checkmark { from { - animation-timing-function: $_md-checkbox-indeterminate-checked-easing-function; - opacity: 0; transform: rotate(45deg); + opacity: 0; + animation-timing-function: $_md-checkbox-indeterminate-checked-easing-function; } to { - opacity: 1; transform: rotate(360deg); + opacity: 1; } } @keyframes md-checkbox-checked-indeterminate-mixedmark { from { - @include mdl-animation-linear-out-slow-in; - opacity: 0; transform: rotate(-45deg); + opacity: 0; + + @include mdl-animation-linear-out-slow-in; } to { - opacity: 1; transform: rotate(0deg); + opacity: 1; } } @keyframes md-checkbox-indeterminate-checked-mixedmark { from { - animation-timing-function: $_md-checkbox-indeterminate-checked-easing-function; - opacity: 1; transform: rotate(0deg); + opacity: 1; + animation-timing-function: $_md-checkbox-indeterminate-checked-easing-function; } to { - opacity: 0; transform: rotate(315deg); + opacity: 0; } } @keyframes md-checkbox-indeterminate-unchecked-mixedmark { 0% { - animation-timing-function: linear; - opacity: 1; transform: scaleX(1); + opacity: 1; + animation-timing-function: linear; } - 32.8%, 100% { - opacity: 0; + 32.8%, + 100% { transform: scaleX(0); + opacity: 0; } } diff --git a/packages/mdl-checkbox/_variables.scss b/packages/mdl-checkbox/_variables.scss index 5c507ec4a..c31d0b031 100644 --- a/packages/mdl-checkbox/_variables.scss +++ b/packages/mdl-checkbox/_variables.scss @@ -2,8 +2,8 @@ // Theme variables // $md-checkbox-background-color: #212121 !default; -$md-checkbox-mark-color: #ffffff !default; -$md-checkbox-border-color: rgba(black, 0.54) !default; +$md-checkbox-mark-color: #fff !default; +$md-checkbox-border-color: rgba(black, .54) !default; // NOTE(traviskaufman): While the spec calls for translucent blacks/whites for disabled colors, // this does not work well with elements layered on top of one another. To get around this we // blend the colors together based on the base color and the theme background. @@ -20,4 +20,4 @@ $md-checkbox-transition-duration: 90ms; $md-checkbox-item-spacing: 4px; // Manual calculation done on SVG $_md-checkbox-mark-path-length: 22.910259; -$_md-checkbox-indeterminate-checked-easing-function: cubic-bezier(0.14, 0, 0, 1); +$_md-checkbox-indeterminate-checked-easing-function: cubic-bezier(.14, 0, 0, 1); diff --git a/packages/mdl-checkbox/mdl-checkbox-theme.scss b/packages/mdl-checkbox/mdl-checkbox-theme.scss deleted file mode 100644 index 53e9362af..000000000 --- a/packages/mdl-checkbox/mdl-checkbox-theme.scss +++ /dev/null @@ -1,4 +0,0 @@ -@import './variables'; - -// TODO: This is where all of the theme-specific CSS would be moved to. This way, users who were not -// making use of sass could import the base stylesheet and then theme themselves. diff --git a/packages/mdl-checkbox/mdl-checkbox.scss b/packages/mdl-checkbox/mdl-checkbox.scss index 7448b1dae..865d2b620 100644 --- a/packages/mdl-checkbox/mdl-checkbox.scss +++ b/packages/mdl-checkbox/mdl-checkbox.scss @@ -5,146 +5,141 @@ /** * Applied to elements that cover the checkbox's entire inner container. */ -%md-checkbox-cover-element { - bottom: 0; - left: 0; +@mixin md-checkbox-cover-element { position: absolute; - right: 0; top: 0; + right: 0; + bottom: 0; + left: 0; } /** * Applied to elements that are considered "marks" within the checkbox, e.g. the checkmark and * the mixedmark. */ -%md-checkbox-mark { - $width-padding-inset: 2 * $md-checkbox-border-width; - width: calc(100% - #{$width-padding-inset}); +@mixin md-checkbox-mark { + width: calc(100% - #{2 * $md-checkbox-border-width}); } /** * Applied to elements that appear to make up the outer box of the checkmark, such as the frame * that contains the border and the actual background element that contains the marks. */ -%md-checkbox-outer-box { - @extend %md-checkbox-cover-element; +@mixin md-checkbox-outer-box { border-radius: 2px; + + @include md-checkbox-cover-element; box-sizing: border-box; pointer-events: none; } +// postcss-bem-linter: define checkbox-wrapper .md-checkbox-wrapper { display: inline-block; &__layout { - align-items: baseline; display: inline-flex; + align-items: baseline; vertical-align: middle; } - + // postcss-bem-linter: ignore .md-checkbox { + order: 0; margin: auto; margin-right: $md-checkbox-item-spacing; - order: 0; - [dir="rtl"] & { - margin:{ - left: $md-checkbox-item-spacing; - right: auto; - } + // postcss-bem-linter: ignore + [dir='rtl'] & { + margin-right: auto; + margin-left: $md-checkbox-item-spacing; } } } .md-checkbox-wrapper--align-end { + // postcss-bem-linter: ignore .md-checkbox { order: 1; - margin: { - left: $md-checkbox-item-spacing; - right: auto; - } + margin-right: auto; + margin-left: $md-checkbox-item-spacing; - [dir="rtl"] & { - margin: { - left: auto; - right: $md-checkbox-item-spacing; - } + // postcss-bem-linter: ignore + [dir='rtl'] & { + margin-right: $md-checkbox-item-spacing; + margin-left: auto; } } } +// postcss-bem-linter: end +// postcss-bem-linter: define checkbox .md-checkbox { - cursor: pointer; display: inline-block; + position: relative; + width: $md-checkbox-size; height: $md-checkbox-size; line-height: 0; - position: relative; - vertical-align: bottom; white-space: nowrap; - width: $md-checkbox-size; + cursor: pointer; + vertical-align: bottom; &__frame { - @extend %md-checkbox-outer-box; - - background-color: transparent; - border: $md-checkbox-border-width solid $md-checkbox-border-color; transition: border-color $md-checkbox-transition-duration $mdl-animation-linear-out-slow-in-timing-function; + border: $md-checkbox-border-width solid $md-checkbox-border-color; + background-color: transparent; + + @include md-checkbox-outer-box; will-change: border-color; } &__background { - @extend %md-checkbox-outer-box; - + display: inline-flex; align-items: center; + justify-content: center; + transition: background-color $md-checkbox-transition-duration $mdl-animation-linear-out-slow-in-timing-function; background-color: $md-checkbox-background-color; opacity: 0; - display: inline-flex; - justify-content: center; - transition: background-color $md-checkbox-transition-duration - $mdl-animation-linear-out-slow-in-timing-function; + + @include md-checkbox-outer-box; will-change: background-color, opacity; } &__native-control { position: absolute; - left: 0; top: 0; - + left: 0; width: 100%; height: 100%; margin: 0; padding: 0; - - opacity: 0; cursor: inherit; + opacity: 0; } &__checkmark { - @extend %md-checkbox-cover-element; - @extend %md-checkbox-mark; + @include md-checkbox-cover-element; + @include md-checkbox-mark; - fill: $md-checkbox-mark-color; width: 100%; + fill: $md-checkbox-mark-color; &__path { // !important is needed here because a stroke must be set as an attribute on the SVG in order // for line animation to work properly. stroke: $md-checkbox-mark-color !important; - stroke: { - dashoffset: $_md-checkbox-mark-path-length; - dasharray: $_md-checkbox-mark-path-length; - width: $md-checkbox-mark-stroke-size; - } + stroke-width: $md-checkbox-mark-stroke-size; + stroke-dashoffset: $_md-checkbox-mark-path-length; + stroke-dasharray: $_md-checkbox-mark-path-length; } } &__mixedmark { - @extend %md-checkbox-mark; - - background-color: $md-checkbox-mark-color; height: floor($md-checkbox-mark-stroke-size); - opacity: 0; transform: scaleX(0) rotate(0deg); + background-color: $md-checkbox-mark-color; + opacity: 0; + + @include md-checkbox-mark; } } @@ -175,8 +170,8 @@ opacity: 1; .md-checkbox__checkmark { - opacity: 0; transform: rotate(45deg); + opacity: 0; &__path { stroke-dashoffset: 0; @@ -184,8 +179,8 @@ } .md-checkbox__mixedmark { - opacity: 1; transform: scaleX(1) rotate(0deg); + opacity: 1; } } } @@ -208,7 +203,7 @@ } .md-checkbox--anim { - $indeterminate-change-duration: 500ms; + $_md-checkbox-indeterminate-change-duration: 500ms; &-unchecked-checked { .md-checkbox__background { @@ -219,8 +214,7 @@ // Instead of delaying the animation, we simply multiply its length by 2 and begin the // animation at 50% in order to prevent a flash of styles applied to a checked checkmark // as the background is fading in before the animation begins. - animation: - $md-checkbox-transition-duration * 2 linear 0s md-checkbox-unchecked-checked-checkmark-path; + animation: $md-checkbox-transition-duration * 2 linear 0s md-checkbox-unchecked-checked-checkmark-path; } } @@ -230,8 +224,7 @@ } .md-checkbox__mixedmark { - animation: - $md-checkbox-transition-duration linear 0s md-checkbox-unchecked-indeterminate-mixedmark; + animation: $md-checkbox-transition-duration linear 0s md-checkbox-unchecked-indeterminate-mixedmark; } } @@ -241,32 +234,27 @@ } .md-checkbox__checkmark__path { - animation: - $md-checkbox-transition-duration linear 0s md-checkbox-checked-unchecked-checkmark-path; + animation: $md-checkbox-transition-duration linear 0s md-checkbox-checked-unchecked-checkmark-path; } } &-checked-indeterminate { .md-checkbox__checkmark { - animation: - $md-checkbox-transition-duration linear 0s md-checkbox-checked-indeterminate-checkmark; + animation: $md-checkbox-transition-duration linear 0s md-checkbox-checked-indeterminate-checkmark; } .md-checkbox__mixedmark { - animation: - $md-checkbox-transition-duration linear 0s md-checkbox-checked-indeterminate-mixedmark; + animation: $md-checkbox-transition-duration linear 0s md-checkbox-checked-indeterminate-mixedmark; } } &-indeterminate-checked { .md-checkbox__checkmark { - animation: - $indeterminate-change-duration linear 0s md-checkbox-indeterminate-checked-checkmark; + animation: $_md-checkbox-indeterminate-change-duration linear 0s md-checkbox-indeterminate-checked-checkmark; } .md-checkbox__mixedmark { - animation: - $indeterminate-change-duration linear 0s md-checkbox-indeterminate-checked-mixedmark; + animation: $_md-checkbox-indeterminate-change-duration linear 0s md-checkbox-indeterminate-checked-mixedmark; } } @@ -276,9 +264,8 @@ } .md-checkbox__mixedmark { - animation: - $indeterminate-change-duration * 0.6 linear 0s - md-checkbox-indeterminate-unchecked-mixedmark; + animation: $_md-checkbox-indeterminate-change-duration * .6 linear 0s md-checkbox-indeterminate-unchecked-mixedmark; } } } +// postcss-bem-linter: end diff --git a/packages/mdl-ripple/mdl-ripple.scss b/packages/mdl-ripple/mdl-ripple.scss index 987c0b801..31e1438ae 100644 --- a/packages/mdl-ripple/mdl-ripple.scss +++ b/packages/mdl-ripple/mdl-ripple.scss @@ -1,8 +1,9 @@ @keyframes radius-in { from { - animation-timing-function: cubic-bezier(0.157, 0.72, 0.386, 0.987); transform: scale(0); + animation-timing-function: cubic-bezier(.157, .72, .386, .987); } + to { // This differs slightly from spec. transform: scale(1.5); @@ -11,14 +12,16 @@ @keyframes opacity-out { from { - animation-timing-function: linear; opacity: 1; + animation-timing-function: linear; } + to { opacity: 0; } } +// postcss-bem-linter: define ripple .mdl-ripple { visibility: hidden; pointer-events: none; @@ -28,21 +31,21 @@ &__foreground-circle, &__background { position: absolute; - height: 100%; width: 100%; + height: 100%; } } .mdl-ripple__foreground { &-circle { - background-color: rgba(0,0,0,.06); - border-radius: 50%; transform: scale(0); + border-radius: 50%; + background-color: rgba(0, 0, 0, .06); opacity: 0; } &--bounded-active { - transition: transform 300ms cubic-bezier(0.157, 0.72, 0.386, 0.987); + transition: transform 300ms cubic-bezier(.157, .72, .386, .987); } &--bounded-circle-active { @@ -52,21 +55,22 @@ } .mdl-ripple__background { - background-color: rgba(0,0,0,.06); - border-radius: 50%; - opacity: 0; transform: scale(1); transition: opacity 480ms linear; + border-radius: 50%; + background-color: rgba(0, 0, 0, .06); + opacity: 0; &--active { transition: opacity 600ms linear; // In order for the fill class's duration to be taken into account, the target // opacity value must be different between the two classes. - opacity: 0.99999; + opacity: .99999; } &--bounded-active-fill { transition-duration: 120ms; opacity: 1; } -} \ No newline at end of file +} +// postcss-bem-linter: end diff --git a/webpack.config.js b/webpack.config.js index c9c1083e8..bef9c9517 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -55,11 +55,8 @@ module.exports = [{ entry: { 'material-design-lite': path.resolve( './packages/material-design-lite/material-design-lite.scss'), - 'material-design-lite-theme': path.resolve( - './packages/material-design-lite/material-design-lite-theme.scss'), 'mdl-animation': path.resolve('./packages/mdl-animation/mdl-animation.scss'), 'mdl-checkbox': path.resolve('./packages/mdl-checkbox/mdl-checkbox.scss'), - 'mdl-checkbox-theme': path.resolve('./packages/mdl-checkbox/mdl-checkbox-theme.scss'), 'mdl-ripple': path.resolve('./packages/mdl-ripple/mdl-ripple.scss') }, output: {