class-seedlet-custom-colors.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. <?php
  2. /**
  3. * Seedlet Theme: Custom Colors Class
  4. *
  5. * This class is in charge of color customization via the Customizer.
  6. *
  7. * Each variable that needs to be updated is defined in the $seedlet_custom_color_variables array below.
  8. * A customizer setting is created for each color, and custom CSS-variables are enqueued in the front and back end.
  9. *
  10. * @package WordPress
  11. * @subpackage Seedlet
  12. * @since 1.0.0
  13. */
  14. class Seedlet_Custom_Colors {
  15. private $seedlet_custom_color_variables = array();
  16. function __construct() {
  17. /**
  18. * Define color variables
  19. */
  20. $this->seedlet_custom_color_variables = array(
  21. array( '--global--color-background', '#FFFFFF', __( 'Background Color', 'seedlet' ) ),
  22. array( '--global--color-foreground', '#333333', __( 'Foreground Color', 'seedlet' ) ),
  23. array( '--global--color-primary', '#000000', __( 'Primary Color', 'seedlet' ) ),
  24. array( '--global--color-secondary', '#3C8067', __( 'Secondary Color', 'seedlet' ) ),
  25. array( '--global--color-tertiary', '#FAFBF6', __( 'Tertiary Color', 'seedlet' ) ),
  26. array( '--global--color-border', '#EFEFEF', __( 'Borders Color', 'seedlet' ) )
  27. );
  28. /**
  29. * Register Customizer actions
  30. */
  31. add_action( 'customize_register', array( $this, 'seedlet_customize_custom_colors_register' ) );
  32. /**
  33. * Enqueue color variables for customizer & frontend.
  34. */
  35. add_action( 'wp_enqueue_scripts', array( $this, 'seedlet_custom_color_variables' ) );
  36. /**
  37. * Enqueue color variables for editor.
  38. */
  39. add_action( 'enqueue_block_editor_assets', array( $this, 'seedlet_editor_custom_color_variables' ) );
  40. /**
  41. * Enqueue contrast checking.
  42. */
  43. add_action( 'customize_controls_enqueue_scripts', array( $this, 'on_customize_controls_enqueue_scripts' ) );
  44. }
  45. /**
  46. * Find the resulting colour by blending 2 colours
  47. * and setting an opacity level for the foreground colour.
  48. *
  49. * @author J de Silva
  50. * @link http://www.gidnetwork.com/b-135.html
  51. * @param string $foreground Hexadecimal colour value of the foreground colour.
  52. * @param integer $opacity Opacity percentage (of foreground colour). A number between 0 and 100.
  53. * @param string $background Optional. Hexadecimal colour value of the background colour. Default is: <code>FFFFFF</code> aka white.
  54. * @return string Hexadecimal colour value. <code>false</code> on errors.
  55. */
  56. function seedlet_color_blend_by_opacity( $foreground, $opacity, $background=null ) {
  57. static $colors_rgb = array(); // stores colour values already passed through the hexdec() functions below.
  58. if ( ! is_null( $foreground ) ) {
  59. $foreground = '000000'; // default primary.
  60. } else {
  61. $foreground = preg_replace( '/[^0-9a-f]/i', '', $foreground ); //str_replace( '#', '', $foreground );
  62. }
  63. if ( ! is_null( $background ) ) {
  64. $background = 'FFFFFF'; // default background.
  65. } else {
  66. $background = preg_replace( '/[^0-9a-f]/i', '', $background );
  67. }
  68. $pattern = '~^[a-f0-9]{6,6}$~i'; // accept only valid hexadecimal colour values.
  69. if ( !@preg_match($pattern, $foreground) or !@preg_match($pattern, $background) ) {
  70. echo $foreground;
  71. trigger_error( "Invalid hexadecimal color value(s) found", E_USER_WARNING );
  72. return false;
  73. }
  74. $opacity = intval( $opacity ); // validate opacity data/number.
  75. if ( $opacity > 100 || $opacity < 0 ) {
  76. trigger_error( "Opacity percentage error, valid numbers are between 0 - 100", E_USER_WARNING );
  77. return false;
  78. }
  79. if ( $opacity == 100 ) // $transparency == 0
  80. return strtoupper( $foreground );
  81. if ( $opacity == 0 ) // $transparency == 100
  82. return strtoupper( $background );
  83. // calculate $transparency value.
  84. $transparency = 100-$opacity;
  85. if ( !isset($colors_rgb[$foreground]) ) { // do this only ONCE per script, for each unique colour.
  86. $f = array(
  87. 'r'=>hexdec($foreground[0].$foreground[1]),
  88. 'g'=>hexdec($foreground[2].$foreground[3]),
  89. 'b'=>hexdec($foreground[4].$foreground[5])
  90. );
  91. $colors_rgb[$foreground] = $f;
  92. } else { // if this function is used 100 times in a script, this block is run 99 times. Efficient.
  93. $f = $colors_rgb[$foreground];
  94. }
  95. if ( !isset($colors_rgb[$background]) ) { // do this only ONCE per script, for each unique colour.
  96. $b = array(
  97. 'r'=>hexdec($background[0].$background[1]),
  98. 'g'=>hexdec($background[2].$background[3]),
  99. 'b'=>hexdec($background[4].$background[5])
  100. );
  101. $colors_rgb[$background] = $b;
  102. } else { // if this FUNCTION is used 100 times in a SCRIPT, this block will run 99 times. Efficient.
  103. $b = $colors_rgb[$background];
  104. }
  105. $add = array(
  106. 'r'=>( $b['r']-$f['r'] ) / 100,
  107. 'g'=>( $b['g']-$f['g'] ) / 100,
  108. 'b'=>( $b['b']-$f['b'] ) / 100
  109. );
  110. $f['r'] += intval( $add['r'] * $transparency );
  111. $f['g'] += intval( $add['g'] * $transparency );
  112. $f['b'] += intval( $add['b'] * $transparency );
  113. return sprintf( '#%02X%02X%02X', $f['r'], $f['g'], $f['b'] );
  114. }
  115. /**
  116. * Add Theme Options for the Customizer.
  117. *
  118. * @param WP_Customize_Manager $wp_customize Theme Customizer object.
  119. */
  120. function seedlet_customize_custom_colors_register( $wp_customize ) {
  121. /**
  122. * Create color options panel.
  123. */
  124. $wp_customize->add_section( 'seedlet_options', array(
  125. 'capability' => 'edit_theme_options',
  126. 'title' => esc_html__( 'Colors', 'seedlet' ),
  127. ) );
  128. /**
  129. * Create toggle between default and custom colors.
  130. */
  131. $wp_customize->add_setting(
  132. 'custom_colors_active',
  133. array(
  134. 'capability' => 'edit_theme_options',
  135. 'sanitize_callback' => array( $this, 'sanitize_select' ),
  136. 'transport' => 'refresh',
  137. 'default' => 'default',
  138. )
  139. );
  140. $wp_customize->add_control(
  141. 'custom_colors_active',
  142. array(
  143. 'type' => 'radio',
  144. 'section' => 'seedlet_options',
  145. 'label' => __( 'Colors', 'seedlet' ),
  146. 'choices' => array(
  147. 'default' => __( 'Theme Default', 'seedlet' ),
  148. 'custom' => __( 'Custom', 'seedlet' ),
  149. ),
  150. )
  151. );
  152. /**
  153. * Create customizer color controls.
  154. */
  155. foreach ( $this->seedlet_custom_color_variables as $variable ) {
  156. $wp_customize->add_setting(
  157. "seedlet_$variable[0]",
  158. array(
  159. 'default' => esc_html( $variable[1] ),
  160. 'sanitize_callback' => 'sanitize_hex_color'
  161. )
  162. );
  163. $wp_customize->add_control( new WP_Customize_Color_Control(
  164. $wp_customize,
  165. "seedlet_$variable[0]",
  166. array(
  167. 'section' => 'seedlet_options',
  168. 'label' => $variable[2],
  169. 'active_callback' => function() use ( $wp_customize ) {
  170. return ( 'custom' === $wp_customize->get_setting( 'custom_colors_active' )->value() );
  171. },
  172. )
  173. ) );
  174. }
  175. }
  176. /**
  177. * Generate color variables.
  178. */
  179. function seedlet_generate_custom_color_variables( $context = null ) {
  180. if ( $context === 'editor' ) {
  181. $theme_css = ':root .editor-styles-wrapper {';
  182. } else {
  183. $theme_css = ':root {';
  184. }
  185. // Check to see if a custom background color has been set that is needed for our color calculation
  186. // If this check isn't present, the color calculation generates a warning that an invalid color has been supplied
  187. $theme_mod_bg_color = empty( get_theme_mod( "seedlet_--global--color-background" ) ) ? '#FFFFFF' : get_theme_mod( "seedlet_--global--color-background" );
  188. foreach ( $this->seedlet_custom_color_variables as $variable ) {
  189. if ( ! empty ( get_theme_mod( "seedlet_$variable[0]" ) ) ) {
  190. $theme_mod_variable_name = $variable[0];
  191. $theme_mod_default_color = $variable[1];
  192. $theme_mod_custom_color = get_theme_mod( "seedlet_$variable[0]" );
  193. $opacity_integer = 70;
  194. $adjusted_color = $this->seedlet_color_blend_by_opacity( $theme_mod_custom_color, $opacity_integer, $theme_mod_bg_color );
  195. $theme_css .= $theme_mod_variable_name . ":" . $theme_mod_custom_color . ";";
  196. if ( $theme_mod_variable_name === '--global--color-primary' && $theme_mod_default_color !== $theme_mod_custom_color ) {
  197. $theme_css .= "--global--color-primary-hover: " . $adjusted_color . ";";
  198. }
  199. if ( $theme_mod_variable_name === '--global--color-secondary' && $theme_mod_default_color !== $theme_mod_custom_color ) {
  200. $theme_css .= "--global--color-secondary-hover: " . $adjusted_color . ";";
  201. }
  202. if ( $theme_mod_variable_name === '--global--color-foreground' && $theme_mod_default_color !== $theme_mod_custom_color ) {
  203. $theme_css .= "--global--color-foreground-light: " . $adjusted_color . ";";
  204. }
  205. }
  206. }
  207. $theme_css .= "}";
  208. // Text selection colors
  209. $selection_background = $this->seedlet_color_blend_by_opacity( get_theme_mod( "seedlet_--global--color-primary" ), 5, $theme_mod_bg_color ) . ";";
  210. $theme_css .= "::selection { background-color: " . $selection_background . "}";
  211. $theme_css .= "::-moz-selection { background-color: ". $selection_background . "}";
  212. return $theme_css;
  213. }
  214. /**
  215. * Customizer & frontend custom color variables.
  216. */
  217. function seedlet_custom_color_variables() {
  218. if ( 'default' !== get_theme_mod( 'custom_colors_active' ) ) {
  219. wp_add_inline_style( 'seedlet-style', $this->seedlet_generate_custom_color_variables() );
  220. }
  221. }
  222. /**
  223. * Editor custom color variables.
  224. */
  225. function seedlet_editor_custom_color_variables() {
  226. wp_enqueue_style( 'seedlet-custom-color-overrides', get_template_directory_uri() . '/assets/css/custom-color-overrides.css', array(), wp_get_theme()->get( 'Version' ) );
  227. if ( 'default' !== get_theme_mod( 'custom_colors_active' ) ) {
  228. wp_add_inline_style( 'seedlet-custom-color-overrides', $this->seedlet_generate_custom_color_variables( 'editor' ) );
  229. }
  230. }
  231. /**
  232. * Customizer contrast validation warnings.
  233. *
  234. * @author Per Soderlind
  235. * @link https://github.com/soderlind/2016-customizer-demo
  236. */
  237. function on_customize_controls_enqueue_scripts() {
  238. $handle = 'wcag-validate-customizer-color-contrast';
  239. $src = get_template_directory_uri() . '/assets/js/customizer-validate-wcag-color-contrast.js';
  240. $deps = [ 'customize-controls' ];
  241. $exports = [
  242. 'validate_color_contrast' => [
  243. // key = current color control , values = array with color controls to check color contrast against
  244. 'seedlet_--global--color-primary' => [ "seedlet_--global--color-background" ],
  245. 'seedlet_--global--color-secondary' => [ "seedlet_--global--color-background" ],
  246. 'seedlet_--global--color-foreground' => [ "seedlet_--global--color-background" ],
  247. 'seedlet_--global--color-background' => [ "seedlet_--global--color-foreground" ],
  248. ],
  249. ];
  250. wp_enqueue_script( $handle, $src, $deps );
  251. wp_script_add_data( $handle, 'data', sprintf( 'var _validateWCAGColorContrastExports = %s;', wp_json_encode( $exports ) ) );
  252. // Custom color contrast validation text
  253. wp_localize_script( $handle, 'validateContrastText', esc_html__( 'This color combination may be hard for people to read. Try using a brighter background color and/or a darker foreground color.', 'seedlet' ));
  254. }
  255. /**
  256. * Sanitize select.
  257. *
  258. * @param string $input The input from the setting.
  259. * @param object $setting The selected setting.
  260. *
  261. * @return string $input|$setting->default The input from the setting or the default setting.
  262. */
  263. public static function sanitize_select( $input, $setting ) {
  264. $input = sanitize_key( $input );
  265. $choices = $setting->manager->get_control( $setting->id )->choices;
  266. return ( array_key_exists( $input, $choices ) ? $input : $setting->default );
  267. }
  268. }
  269. new Seedlet_Custom_Colors;