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
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 @@
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);');
});
});