wp-customize-fonts.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  1. <?php
  2. require_once 'wp-customize-global-styles-setting.php';
  3. require_once 'wp-customize-utils.php';
  4. class GlobalStylesFontsCustomizer {
  5. private $section_key = 'customize-global-styles-fonts';
  6. private $font_settings;
  7. private $font_control_default_body;
  8. private $font_control_default_heading;
  9. private $fonts = array(
  10. 'system-font' => array(
  11. 'fontFamily' => '-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif',
  12. 'slug' => 'system-font',
  13. 'name' => 'System Font',
  14. ),
  15. 'arvo' => array(
  16. 'fontFamily' => '"Arvo", serif',
  17. 'slug' => 'arvo',
  18. 'name' => 'Arvo',
  19. 'google' => 'family=Arvo:ital,wght@0,400;0,700;1,400;1,700',
  20. ),
  21. 'bodoni-moda' => array(
  22. 'fontFamily' => '"Bodoni Moda", serif',
  23. 'slug' => 'bodoni-moda',
  24. 'name' => 'Bodoni Moda',
  25. 'google' => 'family=Bodoni+Moda:ital,wght@0,400..900;1,400..900',
  26. ),
  27. 'cabin' => array(
  28. 'fontFamily' => '"Cabin", sans-serif',
  29. 'slug' => 'cabin',
  30. 'name' => 'Cabin',
  31. 'google' => 'family=Cabin:ital,wght@0,400..700;1,400..700',
  32. ),
  33. 'chivo' => array(
  34. 'fontFamily' => '"Chivo", sans-serif',
  35. 'slug' => 'chivo',
  36. 'name' => 'Chivo',
  37. 'google' => 'family=Chivo:ital,wght@0,300;0,400;0,700;0,900;1,300;1,400;1,700;1,900',
  38. ),
  39. 'courier-prime' => array(
  40. 'fontFamily' => '"Courier Prime", serif',
  41. 'slug' => 'courier-prime',
  42. 'name' => 'Courier Prime',
  43. 'google' => 'family=Courier+Prime:ital,wght@0,400;0,700;1,400;1,700',
  44. ),
  45. 'dm-sans' => array(
  46. 'fontFamily' => '"DM Sans", sans-serif',
  47. 'slug' => 'dm-sans',
  48. 'name' => 'DM Sans',
  49. 'google' => 'family=DM+Sans:ital,wght@0,400;0,500;0,700;1,400;1,500;1,700"',
  50. ),
  51. 'domine' => array(
  52. 'fontFamily' => '"Domine", serif',
  53. 'slug' => 'domine',
  54. 'name' => 'Domine',
  55. 'google' => 'family=Domine:wght@400..700',
  56. ),
  57. 'eb-garamond' => array(
  58. 'fontFamily' => '"EB Garamond", serif',
  59. 'slug' => 'eb-garamond',
  60. 'name' => 'EB Garamond',
  61. 'google' => 'family=EB+Garamond:ital,wght@0,400..800;1,400..800',
  62. ),
  63. 'fira-sans' => array(
  64. 'fontFamily' => '"Fira Sans", sans-serif',
  65. 'slug' => 'fira-sans',
  66. 'name' => 'Fira Sans',
  67. 'google' => 'family=Fira+Sans:ital,wght@0,100..900;1,100..900',
  68. ),
  69. 'inter' => array(
  70. 'fontFamily' => '"Inter", sans-serif',
  71. 'slug' => 'inter',
  72. 'name' => 'Inter',
  73. 'google' => 'family=Inter:wght@100..900',
  74. ),
  75. 'josefin-sans' => array(
  76. 'fontFamily' => '"Josefin Sans", sans-serif',
  77. 'slug' => 'josefin-sans',
  78. 'name' => 'Josefin Sans',
  79. 'google' => 'family=Josefin+Sans:ital,wght@0,100..700;1,100..700',
  80. ),
  81. 'libre-baskerville' => array(
  82. 'fontFamily' => '"Libre Baskerville", serif',
  83. 'slug' => 'libre-baskerville',
  84. 'name' => 'Libre Baskerville',
  85. 'google' => 'family=Libre+Baskerville:ital,wght@0,400;0,700;1,400',
  86. ),
  87. 'libre-franklin' => array(
  88. 'fontFamily' => '"Libre Franklin", sans-serif',
  89. 'slug' => 'libre-franklin',
  90. 'name' => 'Libre Franklin',
  91. 'google' => 'family=Libre+Franklin:ital,wght@0,100..900;1,100..900',
  92. ),
  93. 'lora' => array(
  94. 'fontFamily' => '"Lora", serif',
  95. 'slug' => 'lora',
  96. 'name' => 'Lora',
  97. 'google' => 'family=Lora:ital,wght@0,400..700;1,400..700',
  98. ),
  99. 'merriweather' => array(
  100. 'fontFamily' => '"Merriweather", serif',
  101. 'slug' => 'merriweather',
  102. 'name' => 'Merriweather',
  103. 'google' => 'family=Merriweather:ital,wght@0,300;0,400;0,700;0,900;1,300;1,400;1,700;1,900',
  104. ),
  105. 'montserrat' => array(
  106. 'fontFamily' => '"Montserrat", sans-serif',
  107. 'slug' => 'montserrat',
  108. 'name' => 'Montserrat',
  109. 'google' => 'family=Montserrat:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900',
  110. ),
  111. 'nunito' => array(
  112. 'fontFamily' => '"Nunito", sans-serif',
  113. 'slug' => 'nunito',
  114. 'name' => 'Nunito',
  115. 'google' => 'family=Nunito:ital,wght@0,200;0,300;0,400;0,600;0,700;0,800;0,900;1,200;1,300;1,400;1,600;1,700;1,800;1,900',
  116. ),
  117. 'open-sans' => array(
  118. 'fontFamily' => '"Open Sans", sans-serif',
  119. 'slug' => 'open-sans',
  120. 'name' => 'Open Sans',
  121. 'google' => 'family=Open+Sans:ital,wght@0,300;0,400;0,600;0,700;0,800;1,300;1,400;1,600;1,700;1,800',
  122. ),
  123. 'overpass' => array(
  124. 'fontFamily' => '"Overpass", sans-serif',
  125. 'slug' => 'overpass',
  126. 'name' => 'Overpass',
  127. 'google' => 'family=Overpass:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900',
  128. ),
  129. 'playfair-display' => array(
  130. 'fontFamily' => '"Playfair Display", serif',
  131. 'slug' => 'playfair-display',
  132. 'name' => 'Playfair Display',
  133. 'google' => 'family=Playfair+Display:ital,wght@0,400..900;1,400..900',
  134. ),
  135. 'poppins' => array(
  136. 'fontFamily' => '"Poppins", sans-serif',
  137. 'slug' => 'poppins',
  138. 'name' => 'Poppins',
  139. 'google' => 'family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900',
  140. ),
  141. 'raleway' => array(
  142. 'fontFamily' => '"Raleway", sans-serif',
  143. 'slug' => 'raleway',
  144. 'name' => 'Raleway',
  145. 'google' => 'family=Raleway:ital,wght@0,100..900;1,100..900',
  146. ),
  147. 'roboto' => array(
  148. 'fontFamily' => '"Roboto", sans-serif',
  149. 'slug' => 'roboto',
  150. 'name' => 'Roboto',
  151. 'google' => 'family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,800;1,900',
  152. ),
  153. 'roboto-slab' => array(
  154. 'fontFamily' => '"Roboto Slab", sans-serif',
  155. 'slug' => 'roboto-slab',
  156. 'name' => 'Roboto Slab',
  157. 'google' => 'family=Roboto+Slab:wght@100..900',
  158. ),
  159. 'rubik' => array(
  160. 'fontFamily' => '"Rubik", sans-serif',
  161. 'slug' => 'rubik',
  162. 'name' => 'Rubik',
  163. 'google' => 'family=Rubik:ital,wght@0,300..900;1,300..900',
  164. ),
  165. 'source-sans-pro' => array(
  166. 'fontFamily' => '"Source Sans Pro", sans-serif',
  167. 'slug' => 'source-sans-pro',
  168. 'name' => 'Source Sans Pro',
  169. 'google' => 'family=Source+Sans+Pro:ital,wght@0,200;0,300;0,400;0,600;0,700;0,900;1,200;1,300;1,400;1,600;1,700;1,900',
  170. ),
  171. 'source-serif-pro' => array(
  172. 'fontFamily' => '"Source Serif Pro", serif',
  173. 'slug' => 'source-serif-pro',
  174. 'name' => 'Source Serif Pro',
  175. 'google' => 'family=Source+Serif+Pro:ital,wght@0,200;0,300;0,400;0,600;0,700;0,900;1,200;1,300;1,400;1,600;1,700;1,900',
  176. ),
  177. 'space-mono' => array(
  178. 'fontFamily' => '"Space Mono", sans-serif',
  179. 'slug' => 'space-mono',
  180. 'name' => 'Space Mono',
  181. 'google' => 'family=Space+Mono:ital,wght@0,400;0,700;1,400;1,700',
  182. ),
  183. 'work-sans' => array(
  184. 'fontFamily' => '"Work Sans", sans-serif',
  185. 'slug' => 'work-sans',
  186. 'name' => 'Work Sans',
  187. 'google' => 'family=Work+Sans:ital,wght@0,100..900;1,100..900',
  188. ),
  189. );
  190. function __construct() {
  191. add_action( 'customize_register', array( $this, 'initialize' ) );
  192. add_action( 'customize_preview_init', array( $this, 'handle_customize_preview_init' ) );
  193. add_action( 'customize_register', array( $this, 'enqueue_google_fonts' ) );
  194. add_action( 'customize_save_after', array( $this, 'handle_customize_save_after' ) );
  195. add_action( 'customize_controls_enqueue_scripts', array( $this, 'customize_control_js' ) );
  196. }
  197. function handle_customize_preview_init( $wp_customize ) {
  198. $this->update_font_settings( $wp_customize );
  199. $this->customize_preview_js( $wp_customize );
  200. $this->create_customization_style_element( $wp_customize );
  201. }
  202. function customize_preview_js( $wp_customize ) {
  203. wp_enqueue_script( 'customizer-preview-fonts', get_template_directory_uri() . '/inc/customizer/wp-customize-fonts-preview.js', array( 'customize-preview' ) );
  204. wp_localize_script( 'customizer-preview-fonts', 'googleFonts', $this->fonts );
  205. wp_localize_script( 'customizer-preview-fonts', 'fontSettings', $this->font_settings );
  206. }
  207. function customize_control_js() {
  208. wp_enqueue_script( 'customizer-font-control', get_template_directory_uri() . '/inc/customizer/wp-customize-fonts-control.js', array( 'customize-controls' ), null, true );
  209. wp_localize_script( 'customizer-font-control', 'fontControlDefaultBody', array( $this->font_control_default_body ) );
  210. wp_localize_script( 'customizer-font-control', 'fontControlDefaultHeading', array( $this->font_control_default_heading ) );
  211. }
  212. function enqueue_google_fonts() {
  213. wp_enqueue_style( 'blockbase-google-fonts', $this->google_fonts_url(), array(), null );
  214. }
  215. function create_customization_style_element( $wp_customize ) {
  216. wp_enqueue_style( 'global-styles-fonts-customizations', ' ', array( 'global-styles' ) ); // This needs to load after global_styles, hence the dependency
  217. $css = ':root,body{';
  218. $css .= '--wp--custom--body--typography--font-family:' . $this->font_settings['body'] . ';';
  219. $css .= '--wp--custom--heading--typography--font-family: ' . $this->font_settings['heading'] . '}';
  220. $css .= '}';
  221. wp_add_inline_style( 'global-styles-fonts-customizations', $css );
  222. }
  223. function update_font_settings( $wp_customize ) {
  224. $body_value = $wp_customize->get_setting( $this->section_key . 'body' )->post_value();
  225. if ( $body_value ) {
  226. $body_font_setting = $this->fonts[ $body_value ];
  227. $this->font_settings['body'] = $body_font_setting['fontFamily'];
  228. }
  229. $heading_value = $wp_customize->get_setting( $this->section_key . 'heading' )->post_value();
  230. if ( $heading_value ) {
  231. $heading_font_setting = $this->fonts[ $heading_value ];
  232. $this->font_settings['heading'] = $heading_font_setting['fontFamily'];
  233. }
  234. }
  235. function google_fonts_url() {
  236. $font_families = array();
  237. foreach ( $this->fonts as $font ) {
  238. if ( ! empty( $font['google'] ) ) {
  239. $font_families[] = $font['google'];
  240. }
  241. }
  242. $font_families[] = 'display=swap';
  243. // Make a single request for the theme fonts.
  244. return esc_url_raw( 'https://fonts.googleapis.com/css2?' . implode( '&', $font_families ) );
  245. }
  246. function initialize( $wp_customize ) {
  247. $theme = wp_get_theme();
  248. $merged_json = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data()->get_raw_data();
  249. $theme_json = WP_Theme_JSON_Resolver_Gutenberg::get_theme_data()->get_raw_data();
  250. $body_font_default = $this->get_font_family( 'body', $theme_json );
  251. $heading_font_default = $this->get_font_family( 'heading', $theme_json );
  252. $body_font_selected = $this->get_font_family( 'body', $merged_json );
  253. $heading_font_selected = $this->get_font_family( 'heading', $merged_json );
  254. $this->font_settings = array(
  255. 'body' => $body_font_selected['fontFamily'],
  256. 'heading' => $heading_font_selected['fontFamily'],
  257. );
  258. //Add a Section to the Customizer for these bits
  259. $wp_customize->add_section(
  260. $this->section_key,
  261. array(
  262. 'capability' => 'edit_theme_options',
  263. 'description' => sprintf( __( 'Font Customization for %1$s', 'blockbase' ), $theme->name ),
  264. 'title' => __( 'Fonts', 'blockbase' ),
  265. )
  266. );
  267. // Add a reset button
  268. $this->font_control_default_body = $body_font_default['slug'];
  269. $this->font_control_default_heading = $heading_font_default['slug'];
  270. $wp_customize->add_control(
  271. $this->section_key . '-reset-button',
  272. array(
  273. 'type' => 'button',
  274. 'settings' => array(),
  275. 'section' => $this->section_key,
  276. 'input_attrs' => array(
  277. 'value' => __( 'Reset to Default', 'blockbase' ),
  278. 'class' => 'button button-link',
  279. ),
  280. )
  281. );
  282. $this->add_setting_and_control( $wp_customize, 'body', __( 'Body font', 'blockbase' ), $body_font_default['slug'], $body_font_selected['slug'] );
  283. $this->add_setting_and_control( $wp_customize, 'heading', __( 'Heading font', 'blockbase' ), $heading_font_default['slug'], $heading_font_selected['slug'] );
  284. }
  285. function get_font_family( $location, $configuration ) {
  286. $variable = $configuration['settings']['custom'][ $location ]['typography']['fontFamily'];
  287. $slug = preg_replace( '/var\(--wp--preset--font-family--(.*)\)/', '$1', $variable );
  288. return $this->fonts[ $slug ];
  289. }
  290. function add_setting_and_control( $wp_customize, $name, $label, $default, $user_value ) {
  291. $setting_name = $this->section_key . $name;
  292. $global_styles_setting = new WP_Customize_Global_Styles_Setting(
  293. $wp_customize,
  294. $setting_name,
  295. array(
  296. 'default' => $default,
  297. 'user_value' => $user_value,
  298. )
  299. );
  300. $wp_customize->add_setting( $global_styles_setting );
  301. $choices = array();
  302. foreach ( $this->fonts as $font_slug => $font_setting ) {
  303. $choices[ $font_slug ] = $font_setting['name'];
  304. }
  305. $wp_customize->add_control(
  306. $setting_name,
  307. array(
  308. 'section' => $this->section_key,
  309. 'label' => $label,
  310. 'type' => 'select',
  311. 'choices' => $choices,
  312. )
  313. );
  314. // Update the setting to the dirty value.
  315. // This is needed to preserve the settings when other Customizer settings change.
  316. $dirty_value = $wp_customize->get_setting( $setting_name )->post_value();
  317. if ( ! empty( $dirty_value ) ) {
  318. $wp_customize->get_setting( $setting_name )->user_value = $dirty_value;
  319. }
  320. }
  321. function handle_customize_save_after( $wp_customize ) {
  322. $body_value = $wp_customize->get_setting( $this->section_key . 'body' )->post_value();
  323. $heading_value = $wp_customize->get_setting( $this->section_key . 'heading' )->post_value();
  324. if ( ! isset( $body_value ) && ! isset( $heading_value ) ) {
  325. return;
  326. }
  327. $body_default = $wp_customize->get_setting( $this->section_key . 'body' )->default;
  328. $heading_default = $wp_customize->get_setting( $this->section_key . 'heading' )->default;
  329. if ( ! isset( $body_value ) ) {
  330. $body_value = $body_default;
  331. }
  332. if ( ! isset( $heading_value ) ) {
  333. $heading_value = $heading_default;
  334. }
  335. $body_setting = $this->fonts[ $body_value ];
  336. $heading_setting = $this->fonts[ $heading_value ];
  337. // Set up variables for the theme.json.
  338. $font_families = array(
  339. $body_setting,
  340. $heading_setting,
  341. );
  342. $body_font_family_variable = 'var(--wp--preset--font-family--' . $body_setting['slug'] . ')';
  343. $heading_font_family_variable = 'var(--wp--preset--font-family--' . $heading_setting['slug'] . ')';
  344. $google_font_array = array();
  345. if ( isset( $body_setting['google'] ) ) {
  346. $google_font_array[] = $body_setting['google'];
  347. }
  348. if ( isset( $heading_setting['google'] ) ) {
  349. $google_font_array[] = $heading_setting['google'];
  350. }
  351. // Get the user's theme.json from the CPT.
  352. $user_custom_post_type_id = WP_Theme_JSON_Resolver_Gutenberg::get_user_custom_post_type_id();
  353. $user_theme_json_post = get_post( $user_custom_post_type_id );
  354. $user_theme_json_post_content = json_decode( $user_theme_json_post->post_content );
  355. // Set the typography settings.
  356. $user_theme_json_post_content = set_settings_array(
  357. $user_theme_json_post_content,
  358. array( 'settings', 'typography', 'fontFamilies' ),
  359. $font_families
  360. );
  361. // Set the custom body settings.
  362. $user_theme_json_post_content = set_settings_array(
  363. $user_theme_json_post_content,
  364. array( 'settings', 'custom', 'body', 'typography', 'fontFamily' ),
  365. $body_font_family_variable
  366. );
  367. // Set the custom heading settings.
  368. $user_theme_json_post_content = set_settings_array(
  369. $user_theme_json_post_content,
  370. array( 'settings', 'custom', 'heading', 'typography', 'fontFamily' ),
  371. $heading_font_family_variable
  372. );
  373. // Set the custom google fonts settings.
  374. $user_theme_json_post_content = set_settings_array(
  375. $user_theme_json_post_content,
  376. array( 'settings', 'custom', 'fontsToLoadFromGoogle' ),
  377. $google_font_array
  378. );
  379. //If the typeface choices === the default then we remove it instead
  380. if ( $body_value === $body_default && $heading_value === $heading_default ) {
  381. unset( $user_theme_json_post_content->settings->typography->fontFamilies );
  382. unset( $user_theme_json_post_content->settings->custom->body->typography->fontFamily );
  383. unset( $user_theme_json_post_content->settings->custom->heading->typography->fontFamily );
  384. unset( $user_theme_json_post_content->settings->custom->fontsToLoadFromGoogle );
  385. }
  386. // Update the theme.json with the new settings.
  387. $user_theme_json_post->post_content = json_encode( $user_theme_json_post_content );
  388. return wp_update_post( $user_theme_json_post );
  389. }
  390. }
  391. new GlobalStylesFontsCustomizer;