diff --git a/blocks/contrast-checker/index.js b/blocks/contrast-checker/index.js
new file mode 100644
index 0000000000000..885936258d8ff
--- /dev/null
+++ b/blocks/contrast-checker/index.js
@@ -0,0 +1,40 @@
+/**
+ * External dependencies
+ */
+import tinycolor from 'tinycolor2';
+
+/**
+ * WordPress dependencies
+ */
+import { __ } from '@wordpress/i18n';
+import { Notice } from '@wordpress/components';
+
+/**
+ * Internal dependencies
+ */
+import './style.scss';
+
+function ContrastChecker( { backgroundColor, textColor, isLargeText } ) {
+ if ( ! backgroundColor || ! textColor ) {
+ return null;
+ }
+ const tinyBackgroundColor = tinycolor( backgroundColor );
+ const tinyTextColor = tinycolor( textColor );
+ if ( tinycolor.isReadable(
+ tinyBackgroundColor,
+ tinyTextColor,
+ { level: 'AA', size: ( isLargeText ? 'large' : 'small' ) }
+ ) ) {
+ return null;
+ }
+ const msg = tinyBackgroundColor.getBrightness() < tinyTextColor.getBrightness() ?
+ __( 'This color combination may be hard for people to read. Try using a darker background color and/or a brighter text color.' ) :
+ __( 'This color combination may be hard for people to read. Try using a brighter background color and/or a darker text color.' );
+ return (
+
+
+
+ );
+}
+
+export default ContrastChecker;
diff --git a/blocks/contrast-checker/style.scss b/blocks/contrast-checker/style.scss
new file mode 100644
index 0000000000000..2456bd495e4ec
--- /dev/null
+++ b/blocks/contrast-checker/style.scss
@@ -0,0 +1,3 @@
+.blocks-contrast-checker > .notice {
+ margin: 0;
+}
diff --git a/blocks/library/button/index.js b/blocks/library/button/index.js
index 666e244d4e062..e7edef6b8bd19 100644
--- a/blocks/library/button/index.js
+++ b/blocks/library/button/index.js
@@ -2,6 +2,7 @@
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
+import { Component } from '@wordpress/element';
import { Dashicon, IconButton, PanelBody } from '@wordpress/components';
/**
@@ -16,70 +17,100 @@ import BlockControls from '../../block-controls';
import ToggleControl from '../../inspector-controls/toggle-control';
import BlockAlignmentToolbar from '../../block-alignment-toolbar';
import ColorPalette from '../../color-palette';
+import ContrastChecker from '../../contrast-checker';
import InspectorControls from '../../inspector-controls';
import BlockDescription from '../../block-description';
const { attr, children } = source;
+const { getComputedStyle } = window;
-registerBlockType( 'core/button', {
- title: __( 'Button' ),
+class ButtonBlock extends Component {
+ constructor() {
+ super( ...arguments );
- icon: 'button',
+ this.containers = {};
+ this.fallbackColors = {};
- category: 'layout',
+ this.state = {
+ fallbackBackgroundColor: undefined,
+ fallbackTextColor: undefined,
+ };
- attributes: {
- url: {
- type: 'string',
- source: attr( 'a', 'href' ),
- },
- title: {
- type: 'string',
- source: attr( 'a', 'title' ),
- },
- text: {
- type: 'array',
- source: children( 'a' ),
- },
- align: {
- type: 'string',
- default: 'none',
- },
- color: {
- type: 'string',
- },
- textColor: {
- type: 'string',
- },
- },
+ this.updateAlignment = this.updateAlignment.bind( this );
+ this.toggleClear = this.toggleClear.bind( this );
+ this.bindRef = this.bindRef.bind( this );
+ }
- getEditWrapperProps( attributes ) {
- const { align, clear } = attributes;
- const props = {};
+ componentDidMount() {
+ this.grabColors();
+ }
- if ( 'left' === align || 'right' === align || 'center' === align ) {
- props[ 'data-align' ] = align;
+ componentDidUpdate() {
+ this.grabColors();
+ }
+
+ updateAlignment( nextAlign ) {
+ this.props.setAttributes( { align: nextAlign } );
+ }
+
+ toggleClear() {
+ const { attributes, setAttributes } = this.props;
+ setAttributes( { clear: ! attributes.clear } );
+ }
+
+ bindRef( node ) {
+ if ( ! node ) {
+ return;
}
- if ( clear ) {
- props[ 'data-clear' ] = 'true';
+ this.containers.background = node;
+ this.containers.text = node.querySelector( '[contenteditable="true"]' );
+ }
+
+ grabColors() {
+ const { background, text } = this.containers;
+ const { textColor, color } = this.props.attributes;
+ const { fallbackTextColor, fallbackBackgroundColor } = this.state;
+
+ if ( ! color && ! fallbackBackgroundColor && background ) {
+ this.setState( { fallbackBackgroundColor: getComputedStyle( background ).backgroundColor } );
}
- return props;
- },
+ if ( ! textColor && ! fallbackTextColor && text ) {
+ this.setState( { fallbackTextColor: getComputedStyle( text ).color } );
+ }
+ }
+
+ render() {
+ const {
+ attributes,
+ setAttributes,
+ focus,
+ setFocus,
+ className,
+ } = this.props;
- edit( { attributes, setAttributes, focus, setFocus, className } ) {
- const { text, url, title, align, color, textColor, clear } = attributes;
- const updateAlignment = ( nextAlign ) => setAttributes( { align: nextAlign } );
- const toggleClear = () => setAttributes( { clear: ! clear } );
+ const {
+ text,
+ url,
+ title,
+ align,
+ color,
+ textColor,
+ clear,
+ } = attributes;
+ const {
+ fallbackBackgroundColor,
+ fallbackTextColor,
+ } = this.state;
return [
focus && (
-
+
),
-
+
setAttributes( { textColor: colorValue } ) }
/>
+
}
,
@@ -132,6 +168,58 @@ registerBlockType( 'core/button', {
),
];
+ }
+}
+
+registerBlockType( 'core/button', {
+ title: __( 'Button' ),
+
+ icon: 'button',
+
+ category: 'layout',
+
+ attributes: {
+ url: {
+ type: 'string',
+ source: attr( 'a', 'href' ),
+ },
+ title: {
+ type: 'string',
+ source: attr( 'a', 'title' ),
+ },
+ text: {
+ type: 'array',
+ source: children( 'a' ),
+ },
+ align: {
+ type: 'string',
+ default: 'none',
+ },
+ color: {
+ type: 'string',
+ },
+ textColor: {
+ type: 'string',
+ },
+ },
+
+ getEditWrapperProps( attributes ) {
+ const { align, clear } = attributes;
+ const props = {};
+
+ if ( 'left' === align || 'right' === align || 'center' === align ) {
+ props[ 'data-align' ] = align;
+ }
+
+ if ( clear ) {
+ props[ 'data-clear' ] = 'true';
+ }
+
+ return props;
+ },
+
+ edit( props ) {
+ return ;
},
save( { attributes } ) {
diff --git a/package.json b/package.json
index 923bfe17e2dab..d9a050e1aa86f 100644
--- a/package.json
+++ b/package.json
@@ -44,6 +44,7 @@
"rememo": "2.3.3",
"showdown": "1.7.4",
"simple-html-tokenizer": "0.4.1",
+ "tinycolor2": "1.4.1",
"uuid": "3.1.0"
},
"devDependencies": {