diff --git a/docs/guides/THEMES_IMPL_NOTES.md b/docs/guides/THEMES_IMPL_NOTES.md index 32dacf266ef..5552c797612 100644 --- a/docs/guides/THEMES_IMPL_NOTES.md +++ b/docs/guides/THEMES_IMPL_NOTES.md @@ -1,38 +1,45 @@ -# Notes on ng-material's theme implementation +# Notes on AngularJS Material's theme implementation #### TL;DR -Themes are configured by users with `$mdThemingProvider`. The CSS is then generated at run-time by -the `$mdTheming` service and tacked into the document head. +You configure themes with `$mdThemingProvider`. The CSS is then generated at run-time by +the `$mdTheming` service and appended to the document's ``. -## Actual explanation +## Explanation ### At build time -* All of the styles associated with **color** are defined in a separate scss file of the form +* All the styles associated with **color** are defined in a separate SCSS file of the form `xxx-theme.scss`. -* Instead of using hard-coded color or a SCSS variable, the colors are defined with a mini-DSL -(described deblow). -* The build process takes all of those `-theme.scss` files and globs them up into one enourmous -string. -* The build process wraps that string with code to set it an angular module constant: - ``` angular.module('material.core').constant('$MD_THEME_CSS', 'HUGE_THEME_STRING'); ``` -* That code gets dumped at the end of `angular-material.js` +* Instead of using a hard-coded color or a SCSS variable, you define the colors with a mini-DSL + (described below). +* The build process takes all of those `-theme.scss` files and globs them up into one enormous +string. +* The build process wraps that string with code to set it as an AngularJS module constant: + ``` + angular.module('material.core').constant('$MD_THEME_CSS', 'HUGE_THEME_STRING'); + ``` +* That code gets dumped at the end of `angular-material.js`. ### At run time * The user defines some themes with `$mdThemingProvider` during the module config phase. * At module run time, the theming service takes `$MD_THEME_CSS` and, for each theme, evaluates the mini-DSL, applies the colors for the theme, and appends the resulting CSS into the document head. - ### The mini-DSL -* Each color is written in the form `'{{palette-hue-opacity}}'`, where opacity is optional. -* For example, `'{{primary-500}}'` -* Palettes are `primary`, `accent`, `warn`, `background`, `foreground` -* The hues for each type except `foreground` use the Material Design hues. -* The `forground` palette is a number from one to four: - * `foreground-1`: text - * `foreground-2`: secondary text, icons - * `foreground-3`: disabled text, hint text - * `foreground-4`: dividers -* There is also a special hue called `contrast` that will give a contrast color (for text). -For example, `accent-contrast` will be a contrast color for the accent color, for use as a text -color on an accent-colored background. +* You write each color in the form `'{{palette-hue-contrast-opacity}}'`, where `hue`, `contrast`, +and opacity are optional. +* For example, `'{{primary-500}}'`. +* Palettes are `primary`, `accent`, `warn`, `background`. +* The hues for each type use the Material Design hues. When not specified, each palette defaults +`hue` to `500` except for `background`. +* The `opacity` value can be a decimal between 0 and 1 or one of the following values based on the +hue's contrast type (dark, light, or strongLight): + * `icon`: icon (0.54 / 0.87 / 1.0) + * `secondary`: secondary text (0.54 / 0.87) + * `disabled`: disabled text or icon (0.38 / 0.54) + * `hint`: hint text (0.38 / 0.50) + * `divider`: divider (0.12) +* `contrast` will give a contrast color (for text) and can be mixed with `opacity`. +For example, `accent-contrast` will be a contrast color for the accent color, for use as a text +color on an accent-colored background. Adding an `opacity` value as in `accent-contrast-icon` will +apply the Material Design icon opacity. Using a decimal opacity value as in `accent-contrast-0.25` +will apply the contrast color for the accent color at 25% opacity. diff --git a/src/components/colors/demoBasicUsage/index.html b/src/components/colors/demoBasicUsage/index.html index ce35ca783d1..20011e0e3ac 100644 --- a/src/components/colors/demoBasicUsage/index.html +++ b/src/components/colors/demoBasicUsage/index.html @@ -1,25 +1,25 @@

- Custom component implementations using Material elements often want to easily apply Theme colors + Custom component implementations using Material elements often want to easily apply theme colors to their custom components. Consider the custom <user-card> component below where the md-colors attribute is used to color the background and text colors - using either the current or specified Theme palette colors. + using either the current or specified theme palette colors.

- <user-card> without md-color features +

<user-card> without md-color features

- <user-card> coloring using the 'default' Theme +

<user-card> coloring using the 'default' theme

- <user-card> coloring using the 'forest' Theme +

<user-card> coloring using the 'forest' theme

- Note: The md-colors directive allows pre-defined theme colors to be easily applied - as CSS style properties. md-colors uses the Palettes defined at - Material Design Colors - and the Themes defined using $mdThemingProvider. This feature is simply an - extension of the $mdTheming features. + Note: The md-colors directive allows pre-defined theme colors to be applied + as CSS style properties. md-colors uses the palettes defined in the + + Material Design Colors and the themes defined using $mdThemingProvider. + This directive is an extension of the $mdTheming features.

diff --git a/src/components/colors/demoBasicUsage/regularCard.tmpl.html b/src/components/colors/demoBasicUsage/regularCard.tmpl.html index dd464ef9571..a72cbd8a030 100644 --- a/src/components/colors/demoBasicUsage/regularCard.tmpl.html +++ b/src/components/colors/demoBasicUsage/regularCard.tmpl.html @@ -2,7 +2,7 @@
- +
diff --git a/src/components/colors/demoBasicUsage/script.js b/src/components/colors/demoBasicUsage/script.js index 1e1dd6aaf1e..f9e7785c277 100644 --- a/src/components/colors/demoBasicUsage/script.js +++ b/src/components/colors/demoBasicUsage/script.js @@ -5,7 +5,7 @@ angular.module('colorsDemo', ['ngMaterial']) .accentPalette('green'); $mdIconProvider - .defaultIconSet('img/icons/sets/social-icons.svg', 24); + .iconSet('social', 'img/icons/sets/social-icons.svg', 24); }) .directive('regularCard', function () { return { diff --git a/src/components/colors/demoBasicUsage/style.css b/src/components/colors/demoBasicUsage/style.css index 9e83affab2c..9dae076df2a 100644 --- a/src/components/colors/demoBasicUsage/style.css +++ b/src/components/colors/demoBasicUsage/style.css @@ -14,9 +14,8 @@ color: rgba(255, 255, 255, 0.87); } -span.card-title { - padding-left: 15px; - margin-top: 20px; +h4.card-title { + margin: 24px 8px 0; } code.css { @@ -24,12 +23,12 @@ code.css { } p.footnote code { - font-size:0.85em; + font-size: 0.85em; } p.footnote { - font-size:0.85em; - margin: 30px; - padding:5px; - background-color: rgba(205,205,205,0.45); + font-size: 0.85em; + margin: 30px 8px; + padding: 16px; + background-color: rgba(205, 205, 205, 0.45); } diff --git a/src/components/colors/demoBasicUsage/userCard.tmpl.html b/src/components/colors/demoBasicUsage/userCard.tmpl.html index 461f8f4c76a..d1bb6ebee3e 100644 --- a/src/components/colors/demoBasicUsage/userCard.tmpl.html +++ b/src/components/colors/demoBasicUsage/userCard.tmpl.html @@ -2,7 +2,7 @@
- +
diff --git a/src/components/colors/demoThemePicker/index.html b/src/components/colors/demoThemePicker/index.html index a5e43546fa4..a61b006fcb5 100644 --- a/src/components/colors/demoThemePicker/index.html +++ b/src/components/colors/demoThemePicker/index.html @@ -1,6 +1,6 @@

- Select a color from the Theme Color Palettes + Select two of the Material Palettes below:

@@ -26,8 +26,8 @@

Notice that the foreground colors are automatically determined (from the theme configurations) - based on the specified background palette selection. Of course, you could easily override the - foreground color also... + based on the specified background palette selection. You can also override the foreground color, + if desired.

diff --git a/src/components/colors/demoThemePicker/script.js b/src/components/colors/demoThemePicker/script.js index 527ad874809..f2354c20f2e 100644 --- a/src/components/colors/demoThemePicker/script.js +++ b/src/components/colors/demoThemePicker/script.js @@ -3,7 +3,7 @@ angular .controller('ThemeDemoCtrl', function ($scope, $mdColorPalette) { $scope.colors = Object.keys($mdColorPalette); - $scope.mdURL = 'https://material.google.com/style/color.html#color-color-palette'; + $scope.mdURL = 'https://material.io/archive/guidelines/style/color.html#color-color-palette'; $scope.primary = 'purple'; $scope.accent = 'green'; diff --git a/src/components/colors/demoThemePicker/style.css b/src/components/colors/demoThemePicker/style.css index d07c6d4f3d2..0e5980138c4 100644 --- a/src/components/colors/demoThemePicker/style.css +++ b/src/components/colors/demoThemePicker/style.css @@ -18,10 +18,10 @@ } p.footnote { - font-size:0.85em; - margin: 30px; - padding:5px; - background-color: rgba(205,205,205,0.45); + font-size: 0.85em; + margin: 30px 8px; + padding: 16px; + background-color: rgba(205, 205, 205, 0.45); } .componentTag { diff --git a/src/core/services/theming/theming.js b/src/core/services/theming/theming.js index c3694e7dbd6..789ca0f9bfd 100644 --- a/src/core/services/theming/theming.js +++ b/src/core/services/theming/theming.js @@ -133,13 +133,27 @@ function detectDisabledThemes($mdThemingProvider) { * {{primary-color-0.7}} - Apply 0.7 opacity to each of the above rules * {{primary-contrast}} - Generates .md-hue-1, .md-hue-2, .md-hue-3 with configured contrast (ie. text) color shades set for each hue * {{primary-contrast-0.7}} - Apply 0.7 opacity to each of the above rules + * {{primary-contrast-divider}} - Apply divider opacity to contrast color * * Foreground expansion: Applies rgba to black/white foreground text * + * Old Foreground Expressions: * {{foreground-1}} - used for primary text * {{foreground-2}} - used for secondary text/divider * {{foreground-3}} - used for disabled text * {{foreground-4}} - used for dividers + * + * New Foreground Expressions: + * + * Apply primary text color for contrasting with default background + * {{background-default-contrast}} - default opacity + * {{background-default-contrast-secondary}} - opacity for secondary text + * {{background-default-contrast-hint}} - opacity for hints and placeholders + * {{background-default-contrast-disabled}} - opacity for disabled text + * {{background-default-contrast-divider}} - opacity for dividers + * + * Apply contrast color for specific shades + * {{background-50-contrast-icon}} - Apply contrast color for icon on background's shade 50 hue */ // In memory generated CSS rules; registered by theme.name @@ -148,21 +162,14 @@ var GENERATED = { }; // In memory storage of defined themes and color palettes (both loaded by CSS, and user specified) var PALETTES; -// Text Colors on light and dark backgrounds +// Text colors are automatically generated based on background color when not specified +// Custom palettes can provide override colors // @see https://material.io/archive/guidelines/style/color.html#color-usability var DARK_FOREGROUND = { name: 'dark', - '1': 'rgba(0,0,0,0.87)', - '2': 'rgba(0,0,0,0.54)', - '3': 'rgba(0,0,0,0.38)', - '4': 'rgba(0,0,0,0.12)' }; var LIGHT_FOREGROUND = { name: 'light', - '1': 'rgba(255,255,255,1.0)', - '2': 'rgba(255,255,255,0.7)', - '3': 'rgba(255,255,255,0.5)', - '4': 'rgba(255,255,255,0.12)' }; var DARK_SHADOW = '1px 1px 0px rgba(0,0,0,0.4), -1px -1px 0px rgba(0,0,0,0.4)'; @@ -199,6 +206,35 @@ var DARK_DEFAULT_HUES = { 'hue-3': 'A200' } }; + +// Icon opacity values (active/inactive) from +// https://material.io/archive/guidelines/style/color.html#color-usability +var DARK_CONTRAST_OPACITY = { + 'icon': 0.54, + 'secondary': 0.54, + 'disabled': 0.38, + 'hint': 0.38, + 'divider': 0.12, +}; + +var LIGHT_CONTRAST_OPACITY = { + 'icon': 0.87, + 'secondary': 0.7, + 'disabled': 0.5, + 'hint': 0.5, + 'divider': 0.12 +}; + +// Icon opacity values (active/inactive) from +// https://material.io/archive/guidelines/style/color.html#color-usability +var STRONG_LIGHT_CONTRAST_OPACITY = { + 'icon': 1.0, + 'secondary': 0.7, + 'disabled': 0.5, + 'hint': 0.5, + 'divider': 0.12 +}; + THEME_COLOR_TYPES.forEach(function(colorType) { // Color types with unspecified default hues will use these default hue values var defaultDefaultHues = { @@ -343,7 +379,8 @@ function ThemingProvider($mdColorPalette, $$mdMetaProvider) { /** * @ngdoc method * @name $mdThemingProvider#setDefaultTheme - * @param {string} theme Default theme name to be applied to elements. Default value is `default`. + * @param {string} theme Default theme name to be applied to elements. + * Default value is `default`. */ setDefaultTheme: function(theme) { defaultTheme = theme; @@ -547,7 +584,7 @@ function ThemingProvider($mdColorPalette, $$mdMetaProvider) { Object.keys(color.hues).map(function(key) { return color.hues[key]; }).forEach(function(hueValue) { - if (VALID_HUE_VALUES.indexOf(hueValue) == -1) { + if (VALID_HUE_VALUES.indexOf(hueValue) === -1) { throw new Error("Invalid hue value '%1' in theme %2's %3 color %4. Available hue values: %5" .replace('%1', hueValue) .replace('%2', self.name) @@ -974,18 +1011,44 @@ function parseRules(theme, colorType, rules) { rules = rules.replace(/THEME_NAME/g, theme.name); var themeNameRegex = new RegExp('\\.md-' + theme.name + '-theme', 'g'); - var simpleVariableRegex = /'?"?\{\{\s*([a-zA-Z]+)-(A?\d+|hue-[0-3]|shadow|default)-?(\d\.?\d*)?(contrast)?\s*\}\}'?"?/g; + // Matches '{{ primary-color }}', etc + var hueRegex = new RegExp('([\'"])?{{\\s*([a-zA-Z]+)-?(color|default)?-?(contrast)?-?((?:\\d\\.?\\d*)|(?:[a-zA-Z]+))?\\s*}}(["\'])?','g'); + var simpleVariableRegex = /'?"?{{\s*([a-zA-Z]+)-(A?\d+|hue-[0-3]|shadow|default)-?(contrast)?-?((?:\d\.?\d*)|(?:[a-zA-Z]+))?\s*}}'?"?/g; + var defaultBgHue = theme.colors['background'].hues['default']; + var defaultBgContrastType = PALETTES[theme.colors['background'].name][defaultBgHue].contrastType; // find and replace simple variables where we use a specific hue, not an entire palette // eg. "{{primary-100}}" // \(' + THEME_COLOR_TYPES.join('\|') + '\)' - rules = rules.replace(simpleVariableRegex, function(match, colorType, hue, opacity, contrast) { + rules = rules.replace(simpleVariableRegex, function(match, colorType, hue, contrast, opacity) { + var regexColorType = colorType; if (colorType === 'foreground') { - if (hue == 'shadow') { + if (hue === 'shadow') { return theme.foregroundShadow; - } else { - return theme.foregroundPalette[hue] || theme.foregroundPalette['1']; + } else if (theme.foregroundPalette[hue]) { + // Use user defined palette number (ie: foreground-2) + return rgba(colorToRgbaArray(theme.foregroundPalette[hue])); + } else if (theme.foregroundPalette['1']){ + return rgba(colorToRgbaArray(theme.foregroundPalette['1'])); + } + // Default to background-default-contrast-{opacity} + colorType = 'background'; + contrast = 'contrast'; + if (!opacity && hue) { + // Convert references to legacy hues to opacities (i.e. foreground-4 to *-divider) + switch (hue) { + // hue-1 uses default opacity + case '2': + opacity = 'secondary'; + break; + case '3': + opacity = 'disabled'; + break; + case '4': + opacity = 'divider'; + } } + hue = 'default'; } // `default` is also accepted as a hue-value, because the background palettes are @@ -994,20 +1057,57 @@ function parseRules(theme, colorType, rules) { hue = theme.colors[colorType].hues[hue]; } - return rgba((PALETTES[ theme.colors[colorType].name ][hue] || '')[contrast ? 'contrast' : 'value'], opacity); + var colorDetails = (PALETTES[ theme.colors[colorType].name ][hue] || ''); + + // If user has specified a foreground color, use those + if (colorType === 'background' && contrast && regexColorType !== 'foreground' && + colorDetails.contrastType === defaultBgContrastType) { + // Don't process if colorType was changed + switch (opacity) { + case 'secondary': + case 'icon': + if (theme.foregroundPalette['2']) { + return rgba(colorToRgbaArray(theme.foregroundPalette['2'])); + } + break; + case 'disabled': + case 'hint': + if (theme.foregroundPalette['3']) { + return rgba(colorToRgbaArray(theme.foregroundPalette['3'])); + } + break; + case 'divider': + if (theme.foregroundPalette['4']) { + return rgba(colorToRgbaArray(theme.foregroundPalette['4'])); + } + break; + default: + if (theme.foregroundPalette['1']) { + return rgba(colorToRgbaArray(theme.foregroundPalette['1'])); + } + break; + } + } + + if (contrast && opacity) { + opacity = colorDetails.opacity[opacity] || opacity; + } + + return rgba(colorDetails[contrast ? 'contrast' : 'value'], opacity); }); - // Matches '{{ primary-color }}', etc - var hueRegex = new RegExp('(\'|")?{{\\s*([a-zA-Z]+)-(color|contrast)-?(\\d\\.?\\d*)?\\s*}}("|\')?','g'); var generatedRules = []; // For each type, generate rules for each hue (ie. default, md-hue-1, md-hue-2, md-hue-3) angular.forEach(['default', 'hue-1', 'hue-2', 'hue-3'], function(hueName) { var newRule = rules - .replace(hueRegex, function(match, _, matchedColorType, hueType, opacity) { + .replace(hueRegex, function(match, _, matchedColorType, hueType, contrast, opacity) { var color = theme.colors[matchedColorType]; var palette = PALETTES[color.name]; var hueValue = color.hues[hueName]; + if (contrast && opacity) { + opacity = palette[hueValue].opacity[opacity] || opacity; + } return rgba(palette[hueValue][hueType === 'color' ? 'value' : 'contrast'], opacity); }); if (hueName !== 'default') { @@ -1016,8 +1116,8 @@ function parseRules(theme, colorType, rules) { // Don't apply a selector rule to the default theme, making it easier to override // styles of the base-component - if (theme.name == 'default') { - var themeRuleRegex = /((?:\s|>|\.|\w|-|:|\(|\)|\[|\]|"|'|=)*)\.md-default-theme((?:\s|>|\.|\w|-|:|\(|\)|\[|\]|"|'|=)*)/g; + if (theme.name === 'default') { + var themeRuleRegex = /((?:\s|>|\.|\w|-|:|\(|\)|\[|]|"|'|=)*)\.md-default-theme((?:\s|>|\.|\w|-|:|\(|\)|\[|]|"|'|=)*)/g; newRule = newRule.replace(themeRuleRegex, function(match, start, end) { return match + ', ' + start + end; @@ -1051,7 +1151,7 @@ function generateAllThemes($injector, $mdTheming) { // Break the CSS into individual rules var rules = themeCss - .split(/\}(?!(\}|'|"|;))/) + .split(/}(?!([}'";]))/) .filter(function(rule) { return rule && rule.trim().length; }) .map(function(rule) { return rule.trim() + '}'; }); @@ -1095,9 +1195,12 @@ function generateAllThemes($injector, $mdTheming) { // Internal functions // ************************* - // The user specifies a 'default' contrast color as either light or dark, - // then explicitly lists which hues are the opposite contrast (eg. A100 has dark, A200 has light) - function sanitizePalette(palette, name) { + /** + * The user specifies a 'default' contrast color as either light or dark, then explicitly lists + * which hues are the opposite contrast (eg. A100 has dark, A200 has light). + * @param {!object} palette to sanitize + */ + function sanitizePalette(palette) { var defaultContrast = palette.contrastDefaultColor; var lightColors = palette.contrastLightColors || []; var strongLightColors = palette.contrastStrongLightColors || []; @@ -1114,6 +1217,37 @@ function generateAllThemes($injector, $mdTheming) { delete palette.contrastStrongLightColors; delete palette.contrastDarkColors; + function getContrastType(hueName) { + if (defaultContrast === 'light' ? darkColors.indexOf(hueName) !== -1 : lightColors.indexOf(hueName) === -1) { + return 'dark'; + } + if (strongLightColors.indexOf(hueName) !== -1) { + return 'strongLight'; + } + return 'light'; + } + function getContrastColor(contrastType) { + switch (contrastType) { + default: + case 'strongLight': + return STRONG_LIGHT_CONTRAST_COLOR; + case 'light': + return LIGHT_CONTRAST_COLOR; + case 'dark': + return DARK_CONTRAST_COLOR; + } + } + function getOpacityValues(contrastType) { + switch (contrastType) { + default: + case 'strongLight': + return STRONG_LIGHT_CONTRAST_OPACITY; + case 'light': + return LIGHT_CONTRAST_OPACITY; + case 'dark': + return DARK_CONTRAST_OPACITY; + } + } // Change { 'A100': '#fffeee' } to { 'A100': { value: '#fffeee', contrast:DARK_CONTRAST_COLOR } angular.forEach(palette, function(hueValue, hueName) { if (angular.isObject(hueValue)) return; // Already converted @@ -1126,28 +1260,14 @@ function generateAllThemes($injector, $mdTheming) { .replace('%3', hueName)); } + var contrastType = getContrastType(hueName); palette[hueName] = { hex: palette[hueName], value: rgbValue, - contrast: getContrastColor() + contrastType: contrastType, + contrast: getContrastColor(contrastType), + opacity: getOpacityValues(contrastType) }; - function getContrastColor() { - if (defaultContrast === 'light') { - if (darkColors.indexOf(hueName) > -1) { - return DARK_CONTRAST_COLOR; - } else { - return strongLightColors.indexOf(hueName) > -1 ? STRONG_LIGHT_CONTRAST_COLOR - : LIGHT_CONTRAST_COLOR; - } - } else { - if (lightColors.indexOf(hueName) > -1) { - return strongLightColors.indexOf(hueName) > -1 ? STRONG_LIGHT_CONTRAST_COLOR - : LIGHT_CONTRAST_COLOR; - } else { - return DARK_CONTRAST_COLOR; - } - } - } }); } } @@ -1194,13 +1314,13 @@ function checkValidPalette(theme, colorType) { } function colorToRgbaArray(clr) { - if (angular.isArray(clr) && clr.length == 3) return clr; + if (angular.isArray(clr) && clr.length === 3) return clr; if (/^rgb/.test(clr)) { return clr.replace(/(^\s*rgba?\(|\)\s*$)/g, '').split(',').map(function(value, i) { - return i == 3 ? parseFloat(value, 10) : parseInt(value, 10); + return i === 3 ? parseFloat(value) : parseInt(value, 10); }); } - if (clr.charAt(0) == '#') clr = clr.substring(1); + if (clr.charAt(0) === '#') clr = clr.substring(1); if (!/^([a-fA-F0-9]{3}){1,2}$/g.test(clr)) return; var dig = clr.length / 3; @@ -1218,7 +1338,7 @@ function colorToRgbaArray(clr) { function rgba(rgbArray, opacity) { if (!rgbArray) return "rgb('0,0,0')"; - if (rgbArray.length == 4) { + if (rgbArray.length === 4) { rgbArray = angular.copy(rgbArray); opacity ? rgbArray.pop() : opacity = rgbArray.pop(); } diff --git a/src/core/services/theming/theming.spec.js b/src/core/services/theming/theming.spec.js index 4b79c9d3295..24cf9da4025 100644 --- a/src/core/services/theming/theming.spec.js +++ b/src/core/services/theming/theming.spec.js @@ -171,12 +171,12 @@ describe('$mdThemingProvider', function() { function parse(str) { return themingProvider._parseRules(testTheme, 'primary', str) .join('') - .split(/\}(?!(\}|'|"|;))/) + .split(/}(?!(}|'|"|;))/) .filter(function(val) { return !!val; }) .map(function(rule) { rule += '}'; return { - content: (rule.match(/\{\s*(.*?)\s*\}/) || [])[1] || null, + content: (rule.match(/{\s*(.*?)\s*}/) || [])[1] || null, hue: (rule.match(/md-(hue-\d)/) || [])[1] || null, type: (rule.match(/(primary)/) || [])[1] || null }; @@ -249,12 +249,30 @@ describe('$mdThemingProvider', function() { .toEqual('color: rgba(0,0,0,0.12);'); expect(parse('.md-THEME_NAME-theme { color: "{{foreground-shadow}}"; }')[0].content) .toEqual('color: ;'); + expect(parse('.md-THEME_NAME-theme { color: "{{background-default-contrast}}"; }')[0].content) + .toEqual('color: rgba(0,0,0,0.87);'); + expect(parse('.md-THEME_NAME-theme { color: "{{background-default-contrast-icon}}"; }')[0].content) + .toEqual('color: rgba(0,0,0,0.54);'); + expect(parse('.md-THEME_NAME-theme { color: "{{background-default-contrast-secondary}}"; }')[0].content) + .toEqual('color: rgba(0,0,0,0.54);'); + expect(parse('.md-THEME_NAME-theme { color: "{{background-default-contrast-disabled}}"; }')[0].content) + .toEqual('color: rgba(0,0,0,0.38);'); + expect(parse('.md-THEME_NAME-theme { color: "{{background-default-contrast-hint}}"; }')[0].content) + .toEqual('color: rgba(0,0,0,0.38);'); + expect(parse('.md-THEME_NAME-theme { color: "{{background-default-contrast-divider}}"; }')[0].content) + .toEqual('color: rgba(0,0,0,0.12);'); + expect(parse('.md-THEME_NAME-theme { color: "{{background-default-contrast-0.05}}"; }')[0].content) + .toEqual('color: rgba(0,0,0,0.05);'); + expect(parse('.md-THEME_NAME-theme { color: "{{primary-contrast}}"; }')[0].content) + .toEqual('color: rgba(255,255,255,0.87);'); + expect(parse('.md-THEME_NAME-theme { color: "{{primary-contrast-secondary}}"; }')[0].content) + .toEqual('color: rgba(255,255,255,0.7);'); }); it('for a dark theme', function() { testTheme.dark(); expect(parse('.md-THEME_NAME-theme { color: "{{foreground-1}}"; }')[0].content) - .toEqual('color: rgba(255,255,255,1.0);'); + .toEqual('color: rgba(255,255,255,0.87);'); expect(parse('.md-THEME_NAME-theme { color: "{{foreground-2}}"; }')[0].content) .toEqual('color: rgba(255,255,255,0.7);'); expect(parse('.md-THEME_NAME-theme { color: "{{foreground-3}}"; }')[0].content) @@ -263,6 +281,77 @@ describe('$mdThemingProvider', function() { .toEqual('color: rgba(255,255,255,0.12);'); expect(parse('.md-THEME_NAME-theme { color: "{{foreground-shadow}}"; }')[0].content) .toEqual('color: 1px 1px 0px rgba(0,0,0,0.4), -1px -1px 0px rgba(0,0,0,0.4);'); + expect(parse('.md-THEME_NAME-theme { color: "{{background-default-contrast}}"; }')[0].content) + .toEqual('color: rgba(255,255,255,0.87);'); + expect(parse('.md-THEME_NAME-theme { color: "{{background-default-contrast-icon}}"; }')[0].content) + .toEqual('color: rgba(255,255,255,0.87);'); + expect(parse('.md-THEME_NAME-theme { color: "{{background-default-contrast-secondary}}"; }')[0].content) + .toEqual('color: rgba(255,255,255,0.7);'); + expect(parse('.md-THEME_NAME-theme { color: "{{background-default-contrast-disabled}}"; }')[0].content) + .toEqual('color: rgba(255,255,255,0.5);'); + expect(parse('.md-THEME_NAME-theme { color: "{{background-default-contrast-hint}}"; }')[0].content) + .toEqual('color: rgba(255,255,255,0.5);'); + expect(parse('.md-THEME_NAME-theme { color: "{{background-default-contrast-divider}}"; }')[0].content) + .toEqual('color: rgba(255,255,255,0.12);'); + expect(parse('.md-THEME_NAME-theme { color: "{{background-default-contrast-0.05}}"; }')[0].content) + .toEqual('color: rgba(255,255,255,0.05);'); + expect(parse('.md-THEME_NAME-theme { color: "{{background-900-contrast}}"; }')[0].content) + .toEqual('color: rgb(255,255,255);'); + expect(parse('.md-THEME_NAME-theme { color: "{{background-900-contrast-icon}}"; }')[0].content) + .toEqual('color: rgba(255,255,255,1);'); + expect(parse('.md-THEME_NAME-theme { color: "{{primary-contrast}}"; }')[0].content) + .toEqual('color: rgba(255,255,255,0.87);'); + expect(parse('.md-THEME_NAME-theme { color: "{{primary-contrast-icon}}"; }')[0].content) + .toEqual('color: rgba(255,255,255,0.87);'); + expect(parse('.md-THEME_NAME-theme { color: "{{primary-contrast-secondary}}"; }')[0].content) + .toEqual('color: rgba(255,255,255,0.7);'); + }); + it('override foreground color', function() { + testTheme.dark(false); + testTheme.foregroundPalette = { + '1': 'ff0000', + '2': '00ff00', + '3': '0000ff', + '4': 'ffff00' + }; + expect(parse('.md-THEME_NAME-theme { color: "{{foreground-1}}"; }')[0].content) + .toEqual('color: rgb(255,0,0);'); + expect(parse('.md-THEME_NAME-theme { color: "{{foreground-2}}"; }')[0].content) + .toEqual('color: rgb(0,255,0);'); + expect(parse('.md-THEME_NAME-theme { color: "{{foreground-3}}"; }')[0].content) + .toEqual('color: rgb(0,0,255);'); + expect(parse('.md-THEME_NAME-theme { color: "{{foreground-4}}"; }')[0].content) + .toEqual('color: rgb(255,255,0);'); + expect(parse('.md-THEME_NAME-theme { color: "{{foreground-shadow}}"; }')[0].content) + .toEqual('color: ;'); + expect(parse('.md-THEME_NAME-theme { color: "{{background-default-contrast}}"; }')[0].content) + .toEqual('color: rgb(255,0,0);'); + expect(parse('.md-THEME_NAME-theme { color: "{{background-default-contrast-icon}}"; }')[0].content) + .toEqual('color: rgb(0,255,0);'); + expect(parse('.md-THEME_NAME-theme { color: "{{background-default-contrast-secondary}}"; }')[0].content) + .toEqual('color: rgb(0,255,0);'); + expect(parse('.md-THEME_NAME-theme { color: "{{background-default-contrast-disabled}}"; }')[0].content) + .toEqual('color: rgb(0,0,255);'); + expect(parse('.md-THEME_NAME-theme { color: "{{background-default-contrast-hint}}"; }')[0].content) + .toEqual('color: rgb(0,0,255);'); + expect(parse('.md-THEME_NAME-theme { color: "{{background-default-contrast-divider}}"; }')[0].content) + .toEqual('color: rgb(255,255,0);'); + + // override colors of same contrast type + expect(parse('.md-THEME_NAME-theme { color: "{{background-50-contrast}}"; }')[0].content) + .toEqual('color: rgb(255,0,0);'); + expect(parse('.md-THEME_NAME-theme { color: "{{background-100-contrast}}"; }')[0].content) + .toEqual('color: rgb(255,0,0);'); + expect(parse('.md-THEME_NAME-theme { color: "{{background-200-contrast}}"; }')[0].content) + .toEqual('color: rgb(255,0,0);'); + + // should not override the following + expect(parse('.md-THEME_NAME-theme { color: "{{background-900-contrast}}"; }')[0].content) + .toEqual('color: rgb(255,255,255);'); + expect(parse('.md-THEME_NAME-theme { color: "{{primary-contrast}}"; }')[0].content) + .toEqual('color: rgba(255,255,255,0.87);'); + expect(parse('.md-THEME_NAME-theme { color: "{{primary-contrast-secondary}}"; }')[0].content) + .toEqual('color: rgba(255,255,255,0.7);'); }); });