33 |
--------------------------------------------------------------------------------
/assets/js/customize.js:
--------------------------------------------------------------------------------
1 | /* global twentytwentyoneGetHexLum */
2 |
3 | ( function() {
4 | // Wait until the customizer has finished loading.
5 | wp.customize.bind( 'ready', function() {
6 | // Hide the "respect_user_color_preference" setting if the background-color is dark.
7 | if ( 127 > twentytwentyoneGetHexLum( wp.customize( 'background_color' ).get() ) ) {
8 | wp.customize.control( 'respect_user_color_preference' ).deactivate();
9 | wp.customize.control( 'respect_user_color_preference_notice' ).deactivate();
10 | }
11 |
12 | // Handle changes to the background-color.
13 | wp.customize( 'background_color', function( setting ) {
14 | setting.bind( function( value ) {
15 | if ( 127 > twentytwentyoneGetHexLum( value ) ) {
16 | wp.customize.control( 'respect_user_color_preference' ).deactivate();
17 | wp.customize.control( 'respect_user_color_preference_notice' ).activate();
18 | } else {
19 | wp.customize.control( 'respect_user_color_preference' ).activate();
20 | wp.customize.control( 'respect_user_color_preference_notice' ).deactivate();
21 | }
22 | } );
23 | } );
24 | } );
25 | }() );
26 |
--------------------------------------------------------------------------------
/archive.php:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/assets/js/customize-helpers.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Get luminance from a HEX color.
3 | *
4 | * @param {string} hex - The hex color.
5 | *
6 | * @return {number} - Returns the luminance, number between 0 and 255.
7 | */
8 | function twentytwentyoneGetHexLum( hex ) { // eslint-disable-line no-unused-vars
9 | var rgb = twentytwentyoneGetRgbFromHex( hex );
10 | return Math.round( ( 0.2126 * rgb.r ) + ( 0.7152 * rgb.g ) + ( 0.0722 * rgb.b ) );
11 | }
12 |
13 | /**
14 | * Get RGB from HEX.
15 | *
16 | * @param {string} hex - The hex color.
17 | *
18 | * @return {Object} - Returns an object {r, g, b}
19 | */
20 | function twentytwentyoneGetRgbFromHex( hex ) {
21 | var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i,
22 | result;
23 |
24 | // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF").
25 | hex = hex.replace( shorthandRegex, function( m, r, g, b ) {
26 | return r.toString() + r.toString() + g.toString() + g.toString() + b.toString() + b.toString();
27 | } );
28 |
29 | result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec( hex );
30 | return result ? {
31 | r: parseInt( result[1], 16 ),
32 | g: parseInt( result[2], 16 ),
33 | b: parseInt( result[3], 16 )
34 | } : null;
35 | }
36 |
--------------------------------------------------------------------------------
/template-parts/post/author-bio.php:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | ' . esc_html__( 'View all of %2$s\'s posts.', 'twentytwentyone' ) . '',
21 | esc_url( get_author_posts_url( get_the_author_meta( 'ID' ) ) ),
22 | get_the_author()
23 | );
24 | ?>
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/assets/js/polyfills.js:
--------------------------------------------------------------------------------
1 | /**
2 | * File polyfills.js.
3 | *
4 | * Polyfills for IE11.
5 | */
6 |
7 | /**
8 | * Polyfill for Element.closest() because we need to support IE11.
9 | *
10 | * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/closest
11 | */
12 | if ( ! Element.prototype.matches ) {
13 | Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector;
14 | }
15 |
16 | if ( ! Element.prototype.closest ) {
17 | Element.prototype.closest = function( s ) {
18 | var el = this;
19 | do {
20 | if ( Element.prototype.matches.call( el, s ) ) {
21 | return el;
22 | }
23 | el = el.parentElement || el.parentNode;
24 | } while ( el !== null && el.nodeType === 1 );
25 | return null;
26 | };
27 | }
28 |
29 | /**
30 | * Polyfill for NodeList.foreach() because we need to support IE11.
31 | *
32 | * @see https://developer.mozilla.org/en-US/docs/Web/API/NodeList/forEach
33 | */
34 | if ( window.NodeList && ! NodeList.prototype.forEach ) {
35 | NodeList.prototype.forEach = function( callback, thisArg ) {
36 | var i;
37 | thisArg = thisArg || window;
38 | for ( i = 0; i < this.length; i++ ) {
39 | callback.call( thisArg, this[i], i, this );
40 | }
41 | };
42 | }
43 |
--------------------------------------------------------------------------------
/classes/class-twenty-twenty-one-customize-notice-control.php:
--------------------------------------------------------------------------------
1 |
40 |
46 | elements, depending on their width & height props.
9 | *
10 | * @since 1.0.0
11 | *
12 | * @return {void}
13 | */
14 | function twentytwentyoneResponsiveEmbeds() {
15 | var proportion, parentWidth;
16 |
17 | // Loop iframe elements.
18 | document.querySelectorAll( 'iframe' ).forEach( function( iframe ) {
19 | // Only continue if the iframe has a width & height defined.
20 | if ( iframe.width && iframe.height ) {
21 | // Calculate the proportion/ratio based on the width & height.
22 | proportion = parseFloat( iframe.width ) / parseFloat( iframe.height );
23 | // Get the parent element's width.
24 | parentWidth = parseFloat( window.getComputedStyle( iframe.parentElement, null ).width.replace( 'px', '' ) );
25 | // Set the max-width & height.
26 | iframe.style.maxWidth = '100%';
27 | iframe.style.maxHeight = Math.round( parentWidth / proportion ).toString() + 'px';
28 | }
29 | } );
30 | }
31 |
32 | // Run on initial load.
33 | twentytwentyoneResponsiveEmbeds();
34 |
35 | // Run on resize.
36 | window.onresize = twentytwentyoneResponsiveEmbeds;
37 |
--------------------------------------------------------------------------------
/template-parts/content/content-single.php:
--------------------------------------------------------------------------------
1 |
13 |
14 | >
15 |
16 |
20 |
21 |
22 | '',
28 | 'after' => ' ',
29 | /* translators: %: page number. */
30 | 'pagelink' => esc_html__( 'Page %', 'twentytwentyone' ),
31 | )
32 | );
33 | ?>
34 |
35 |
36 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/inc/custom-css.php:
--------------------------------------------------------------------------------
1 | tags and can only be interpreted as CSS on the browser.
37 | * Using wp_strip_all_tags() here is sufficient escaping to avoid
38 | * malicious attempts to close and open a ';
391 | }
392 |
393 | /**
394 | * Adds information to the privacy policy.
395 | *
396 | * @access public
397 | *
398 | * @since 1.0.0
399 | *
400 | * @return void
401 | */
402 | public function add_privacy_policy_content() {
403 | if ( ! function_exists( 'wp_add_privacy_policy_content' ) ) {
404 | return;
405 | }
406 | $content = '' . __( 'Twenty Twenty-One uses LocalStorage when Dark Mode support is enabled.', 'twentytwentyone' ) . '
'
407 | . '' . __( 'Suggested text:', 'twentytwentyone' ) . ' '
408 | . __( 'This website uses LocalStorage to save the setting when Dark Mode support is turned on or off. LocalStorage is necessary for the setting to work and is only used when a user clicks on the Dark Mode button. No data is saved in the database or transferred.', 'twentytwentyone' );
409 | wp_add_privacy_policy_content( 'Twenty Twenty-One', wp_kses_post( wpautop( $content, false ) ) );
410 | }
411 |
412 | }
413 |
--------------------------------------------------------------------------------
/inc/template-functions.php:
--------------------------------------------------------------------------------
1 | ';
67 | }
68 | }
69 | add_action( 'wp_head', 'twenty_twenty_one_pingback_header' );
70 |
71 | /**
72 | * Remove the `no-js` class from body if JS is supported.
73 | *
74 | * @since 1.0.0
75 | *
76 | * @return void
77 | */
78 | function twenty_twenty_one_supports_js() {
79 | echo '';
80 | }
81 | add_action( 'wp_footer', 'twenty_twenty_one_supports_js' );
82 |
83 | /**
84 | * Changes comment form default fields.
85 | *
86 | * @since 1.0.0
87 | *
88 | * @param array $defaults The form defaults.
89 | *
90 | * @return array
91 | */
92 | function twenty_twenty_one_comment_form_defaults( $defaults ) {
93 |
94 | // Adjust height of comment form.
95 | $defaults['comment_field'] = preg_replace( '/rows="\d+"/', 'rows="5"', $defaults['comment_field'] );
96 |
97 | return $defaults;
98 | }
99 | add_filter( 'comment_form_defaults', 'twenty_twenty_one_comment_form_defaults' );
100 |
101 | /**
102 | * Determines if post thumbnail can be displayed.
103 | *
104 | * @since 1.0.0
105 | *
106 | * @return bool
107 | */
108 | function twenty_twenty_one_can_show_post_thumbnail() {
109 | return apply_filters(
110 | 'twenty_twenty_one_can_show_post_thumbnail',
111 | ! post_password_required() && ! is_attachment() && has_post_thumbnail()
112 | );
113 | }
114 |
115 | /**
116 | * Returns the size for avatars used in the theme.
117 | *
118 | * @since 1.0.0
119 | *
120 | * @return int
121 | */
122 | function twenty_twenty_one_get_avatar_size() {
123 | return 60;
124 | }
125 |
126 | /**
127 | * Creates continue reading text
128 | */
129 | function twenty_twenty_one_continue_reading_text() {
130 | $continue_reading = sprintf(
131 | /* translators: %s: Name of current post. */
132 | esc_html__( 'Continue reading %s', 'twentytwentyone' ),
133 | the_title( '', ' ', false )
134 | );
135 |
136 | return $continue_reading;
137 | }
138 |
139 | /**
140 | * Create the continue reading link for excerpt.
141 | */
142 | function twenty_twenty_one_continue_reading_link_excerpt() {
143 | if ( ! is_admin() ) {
144 | return '… ' . twenty_twenty_one_continue_reading_text() . ' ';
145 | }
146 | }
147 |
148 | // Filter the excerpt more link.
149 | add_filter( 'excerpt_more', 'twenty_twenty_one_continue_reading_link_excerpt' );
150 |
151 | /**
152 | * Create the continue reading link.
153 | */
154 | function twenty_twenty_one_continue_reading_link() {
155 | if ( ! is_admin() ) {
156 | return '';
157 | }
158 | }
159 |
160 | // Filter the excerpt more link.
161 | add_filter( 'the_content_more_link', 'twenty_twenty_one_continue_reading_link' );
162 |
163 | if ( ! function_exists( 'twenty_twenty_one_post_title' ) ) {
164 | /**
165 | * Add a title to posts and pages that are missing titles.
166 | *
167 | * @since 1.0.0
168 | *
169 | * @param string $title The title.
170 | *
171 | * @return string
172 | */
173 | function twenty_twenty_one_post_title( $title ) {
174 | return '' === $title ? esc_html_x( 'Untitled', 'Added to posts and pages that are missing titles', 'twentytwentyone' ) : $title;
175 | }
176 | }
177 | add_filter( 'the_title', 'twenty_twenty_one_post_title' );
178 |
179 | /**
180 | * Gets the SVG code for a given icon.
181 | *
182 | * @since 1.0.0
183 | *
184 | * @param string $group The icon group.
185 | * @param string $icon The icon.
186 | * @param int $size The icon size in pixels.
187 | *
188 | * @return string
189 | */
190 | function twenty_twenty_one_get_icon_svg( $group, $icon, $size = 24 ) {
191 | return Twenty_Twenty_One_SVG_Icons::get_svg( $group, $icon, $size );
192 | }
193 |
194 | /**
195 | * Changes the default navigation arrows to svg icons
196 | *
197 | * @param string $calendar_output The generated HTML of the calendar.
198 | *
199 | * @return string
200 | */
201 | function twenty_twenty_one_change_calendar_nav_arrows( $calendar_output ) {
202 | $calendar_output = str_replace( '« ', is_rtl() ? twenty_twenty_one_get_icon_svg( 'ui', 'arrow_right' ) : twenty_twenty_one_get_icon_svg( 'ui', 'arrow_left' ), $calendar_output );
203 | $calendar_output = str_replace( ' »', is_rtl() ? twenty_twenty_one_get_icon_svg( 'ui', 'arrow_left' ) : twenty_twenty_one_get_icon_svg( 'ui', 'arrow_right' ), $calendar_output );
204 | return $calendar_output;
205 | }
206 | add_filter( 'get_calendar', 'twenty_twenty_one_change_calendar_nav_arrows' );
207 |
208 | /**
209 | * Get custom CSS.
210 | *
211 | * Return CSS for non-latin language, if available, or null
212 | *
213 | * @param string $type Whether to return CSS for the "front-end", "block-editor" or "classic-editor".
214 | *
215 | * @return string
216 | */
217 | function twenty_twenty_one_get_non_latin_css( $type = 'front-end' ) {
218 |
219 | // Fetch site locale.
220 | $locale = get_bloginfo( 'language' );
221 |
222 | // Define fallback fonts for non-latin languages.
223 | $font_family = apply_filters(
224 | 'twenty_twenty_one_get_localized_font_family_types',
225 | array(
226 |
227 | // Arabic.
228 | 'ar' => array( 'Tahoma', 'Arial', 'sans-serif' ),
229 | 'ary' => array( 'Tahoma', 'Arial', 'sans-serif' ),
230 | 'azb' => array( 'Tahoma', 'Arial', 'sans-serif' ),
231 | 'ckb' => array( 'Tahoma', 'Arial', 'sans-serif' ),
232 | 'fa-IR' => array( 'Tahoma', 'Arial', 'sans-serif' ),
233 | 'haz' => array( 'Tahoma', 'Arial', 'sans-serif' ),
234 | 'ps' => array( 'Tahoma', 'Arial', 'sans-serif' ),
235 |
236 | // Chinese Simplified (China) - Noto Sans SC.
237 | 'zh-CN' => array( '\'PingFang SC\'', '\'Helvetica Neue\'', '\'Microsoft YaHei New\'', '\'STHeiti Light\'', 'sans-serif' ),
238 |
239 | // Chinese Traditional (Taiwan) - Noto Sans TC.
240 | 'zh-TW' => array( '\'PingFang TC\'', '\'Helvetica Neue\'', '\'Microsoft YaHei New\'', '\'STHeiti Light\'', 'sans-serif' ),
241 |
242 | // Chinese (Hong Kong) - Noto Sans HK.
243 | 'zh-HK' => array( '\'PingFang HK\'', '\'Helvetica Neue\'', '\'Microsoft YaHei New\'', '\'STHeiti Light\'', 'sans-serif' ),
244 |
245 | // Cyrillic.
246 | 'bel' => array( '\'Helvetica Neue\'', 'Helvetica', '\'Segoe UI\'', 'Arial', 'sans-serif' ),
247 | 'bg-BG' => array( '\'Helvetica Neue\'', 'Helvetica', '\'Segoe UI\'', 'Arial', 'sans-serif' ),
248 | 'kk' => array( '\'Helvetica Neue\'', 'Helvetica', '\'Segoe UI\'', 'Arial', 'sans-serif' ),
249 | 'mk-MK' => array( '\'Helvetica Neue\'', 'Helvetica', '\'Segoe UI\'', 'Arial', 'sans-serif' ),
250 | 'mn' => array( '\'Helvetica Neue\'', 'Helvetica', '\'Segoe UI\'', 'Arial', 'sans-serif' ),
251 | 'ru-RU' => array( '\'Helvetica Neue\'', 'Helvetica', '\'Segoe UI\'', 'Arial', 'sans-serif' ),
252 | 'sah' => array( '\'Helvetica Neue\'', 'Helvetica', '\'Segoe UI\'', 'Arial', 'sans-serif' ),
253 | 'sr-RS' => array( '\'Helvetica Neue\'', 'Helvetica', '\'Segoe UI\'', 'Arial', 'sans-serif' ),
254 | 'tt-RU' => array( '\'Helvetica Neue\'', 'Helvetica', '\'Segoe UI\'', 'Arial', 'sans-serif' ),
255 | 'uk' => array( '\'Helvetica Neue\'', 'Helvetica', '\'Segoe UI\'', 'Arial', 'sans-serif' ),
256 |
257 | // Devanagari.
258 | 'bn-BD' => array( 'Arial', 'sans-serif' ),
259 | 'hi-IN' => array( 'Arial', 'sans-serif' ),
260 | 'mr' => array( 'Arial', 'sans-serif' ),
261 | 'ne-NP' => array( 'Arial', 'sans-serif' ),
262 |
263 | // Greek.
264 | 'el' => array( '\'Helvetica Neue\', Helvetica, Arial, sans-serif' ),
265 |
266 | // Gujarati.
267 | 'gu' => array( 'Arial', 'sans-serif' ),
268 |
269 | // Hebrew.
270 | 'he-IL' => array( '\'Arial Hebrew\'', 'Arial', 'sans-serif' ),
271 |
272 | // Japanese.
273 | 'ja' => array( 'sans-serif' ),
274 |
275 | // Korean.
276 | 'ko-KR' => array( '\'Apple SD Gothic Neo\'', '\'Malgun Gothic\'', '\'Nanum Gothic\'', 'Dotum', 'sans-serif' ),
277 |
278 | // Thai.
279 | 'th' => array( '\'Sukhumvit Set\'', '\'Helvetica Neue\'', 'Helvetica', 'Arial', 'sans-serif' ),
280 |
281 | // Vietnamese.
282 | 'vi' => array( '\'Libre Franklin\'', 'sans-serif' ),
283 |
284 | )
285 | );
286 |
287 | // Return if the selected language has no fallback fonts.
288 | if ( empty( $font_family[ $locale ] ) ) {
289 | return '';
290 | }
291 |
292 | // Define elements to apply fallback fonts to.
293 | $elements = apply_filters(
294 | 'twenty_twenty_one_get_localized_font_family_elements',
295 | array(
296 | 'front-end' => array( 'body', 'input', 'textarea', 'button', '.button', '.faux-button', '.wp-block-button__link', '.wp-block-file__button', '.has-drop-cap:not(:focus)::first-letter', '.has-drop-cap:not(:focus)::first-letter', '.entry-content .wp-block-archives', '.entry-content .wp-block-categories', '.entry-content .wp-block-cover-image', '.entry-content .wp-block-latest-comments', '.entry-content .wp-block-latest-posts', '.entry-content .wp-block-pullquote', '.entry-content .wp-block-quote.is-large', '.entry-content .wp-block-quote.is-style-large', '.entry-content .wp-block-archives *', '.entry-content .wp-block-categories *', '.entry-content .wp-block-latest-posts *', '.entry-content .wp-block-latest-comments *', '.entry-content p', '.entry-content ol', '.entry-content ul', '.entry-content dl', '.entry-content dt', '.entry-content cite', '.entry-content figcaption', '.entry-content .wp-caption-text', '.comment-content p', '.comment-content ol', '.comment-content ul', '.comment-content dl', '.comment-content dt', '.comment-content cite', '.comment-content figcaption', '.comment-content .wp-caption-text', '.widget_text p', '.widget_text ol', '.widget_text ul', '.widget_text dl', '.widget_text dt', '.widget-content .rssSummary', '.widget-content cite', '.widget-content figcaption', '.widget-content .wp-caption-text' ),
297 | 'block-editor' => array( '.editor-styles-wrapper > *', '.editor-styles-wrapper p', '.editor-styles-wrapper ol', '.editor-styles-wrapper ul', '.editor-styles-wrapper dl', '.editor-styles-wrapper dt', '.editor-post-title__block .editor-post-title__input', '.editor-styles-wrapper .wp-block h1', '.editor-styles-wrapper .wp-block h2', '.editor-styles-wrapper .wp-block h3', '.editor-styles-wrapper .wp-block h4', '.editor-styles-wrapper .wp-block h5', '.editor-styles-wrapper .wp-block h6', '.editor-styles-wrapper .has-drop-cap:not(:focus)::first-letter', '.editor-styles-wrapper cite', '.editor-styles-wrapper figcaption', '.editor-styles-wrapper .wp-caption-text' ),
298 | 'classic-editor' => array( 'body#tinymce.wp-editor', 'body#tinymce.wp-editor p', 'body#tinymce.wp-editor ol', 'body#tinymce.wp-editor ul', 'body#tinymce.wp-editor dl', 'body#tinymce.wp-editor dt', 'body#tinymce.wp-editor figcaption', 'body#tinymce.wp-editor .wp-caption-text', 'body#tinymce.wp-editor .wp-caption-dd', 'body#tinymce.wp-editor cite', 'body#tinymce.wp-editor table' ),
299 | )
300 | );
301 |
302 | // Return if the specified type doesn't exist.
303 | if ( empty( $elements[ $type ] ) ) {
304 | return '';
305 | }
306 |
307 | // Include file if function doesn't exist.
308 | if ( ! function_exists( 'twenty_twenty_one_generate_css' ) ) {
309 | require_once get_theme_file_path( 'inc/custom-css.php' ); // phpcs:ignore WPThemeReview.CoreFunctionality.FileInclude.FileIncludeFound
310 | }
311 |
312 | // Return the specified styles.
313 | return twenty_twenty_one_generate_css( // @phpstan-ignore-line.
314 | implode( ',', $elements[ $type ] ),
315 | 'font-family',
316 | implode( ',', $font_family[ $locale ] ),
317 | null,
318 | null,
319 | false
320 | );
321 | }
322 |
323 | /**
324 | * Print the first instance of a block in the content, and then break away.
325 | *
326 | * @since 1.0.0
327 | *
328 | * @param string $block_name The full block type name, or a partial match.
329 | * Example: `core/image`, `core-embed/*`.
330 | * @param string|null $content The content to search in. Use null for get_the_content().
331 | * @param int $instances How many instances of the block will be printed (max). Defaults to 1.
332 | *
333 | * @return bool Returns true if a block was located & printed, otherwise false.
334 | */
335 | function twenty_twenty_one_print_first_instance_of_block( $block_name, $content = null, $instances = 1 ) {
336 | $instances_count = 0;
337 | $blocks_content = '';
338 |
339 | if ( ! $content ) {
340 | $content = get_the_content();
341 | }
342 |
343 | // Parse blocks in the content.
344 | $blocks = parse_blocks( $content );
345 |
346 | // Loop blocks.
347 | foreach ( $blocks as $block ) {
348 |
349 | // Sanity check.
350 | if ( ! isset( $block['blockName'] ) ) {
351 | continue;
352 | }
353 |
354 | // Check if this the block matches the $block_name.
355 | $is_matching_block = false;
356 |
357 | // If the block ends with *, try to match the first portion.
358 | if ( '*' === $block_name[-1] ) {
359 | $is_matching_block = 0 === strpos( $block['blockName'], rtrim( $block_name, '*' ) );
360 | } else {
361 | $is_matching_block = $block_name === $block['blockName'];
362 | }
363 |
364 | if ( $is_matching_block ) {
365 | // Increment count.
366 | $instances_count++;
367 |
368 | // Add the block HTML.
369 | $blocks_content .= render_block( $block );
370 |
371 | // Break the loop if the $instances count was reached.
372 | if ( $instances_count >= $instances ) {
373 | break;
374 | }
375 | }
376 | }
377 |
378 | if ( $blocks_content ) {
379 | echo apply_filters( 'the_content', $blocks_content ); // phpcs:ignore WordPress.Security.EscapeOutput
380 | return true;
381 | }
382 |
383 | return false;
384 | }
385 |
386 | /**
387 | * Retrieve protected post password form content.
388 | *
389 | * @since 1.0.0
390 | *
391 | * @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global $post.
392 | * @return string HTML content for password form for password protected post.
393 | */
394 | function twenty_twenty_one_password_form( $post = 0 ) {
395 | $post = get_post( $post );
396 | $label = 'pwbox-' . ( empty( $post->ID ) ? wp_rand() : $post->ID );
397 | $output = '' . esc_html__( 'This content is password protected. Please enter a password to view.', 'twentytwentyone' ) . '
398 |
400 | ';
401 | return $output;
402 | }
403 | add_filter( 'the_password_form', 'twenty_twenty_one_password_form' );
404 |
405 | /**
406 | * Filters the list of attachment image attributes.
407 | *
408 | * @since 1.0.0
409 | *
410 | * @param array $attr Array of attribute values for the image markup, keyed by attribute name.
411 | * See wp_get_attachment_image().
412 | * @param WP_Post $attachment Image attachment post.
413 | * @param string|array $size Requested size. Image size or array of width and height values
414 | * (in that order). Default 'thumbnail'.
415 | *
416 | * @return array
417 | */
418 | function twenty_twenty_one_get_attachment_image_attributes( $attr, $attachment, $size ) {
419 |
420 | if ( isset( $attr['class'] ) && false !== strpos( $attr['class'], 'custom-logo' ) ) {
421 | return $attr;
422 | }
423 |
424 | $width = false;
425 | $height = false;
426 |
427 | if ( is_array( $size ) ) {
428 | $width = (int) $size[0];
429 | $height = (int) $size[1];
430 | } elseif ( $attachment && is_object( $attachment ) && $attachment->ID ) {
431 | $meta = wp_get_attachment_metadata( $attachment->ID );
432 | if ( $meta['width'] && $meta['height'] ) {
433 | $width = (int) $meta['width'];
434 | $height = (int) $meta['height'];
435 | }
436 | }
437 |
438 | if ( $width && $height ) {
439 |
440 | // Add style.
441 | $attr['style'] = isset( $attr['style'] ) ? $attr['style'] : '';
442 | $attr['style'] = 'width:100%;height:' . round( 100 * $height / $width, 2 ) . '%;max-width:' . $width . 'px;' . $attr['style'];
443 | }
444 |
445 | return $attr;
446 | }
447 | add_filter( 'wp_get_attachment_image_attributes', 'twenty_twenty_one_get_attachment_image_attributes', 10, 3 );
448 |
--------------------------------------------------------------------------------
/inc/block-patterns.php:
--------------------------------------------------------------------------------
1 | esc_html__( 'Twenty Twenty-One', 'twentytwentyone' ) )
21 | );
22 | }
23 |
24 | /**
25 | * Register Block Patterns.
26 | */
27 | if ( function_exists( 'register_block_pattern' ) ) {
28 |
29 | // Large Text.
30 | register_block_pattern(
31 | 'twentytwentyone/large-text',
32 | array(
33 | 'title' => esc_html__( 'Large text', 'twentytwentyone' ),
34 | 'categories' => array( 'twentytwentyone' ),
35 | 'viewportWidth' => 1440,
36 | 'content' => '' . esc_html__( 'A new portfolio default theme for WordPress', 'twentytwentyone' ) . ' ',
37 | )
38 | );
39 |
40 | // Links Area.
41 | register_block_pattern(
42 | 'twentytwentyone/links-area',
43 | array(
44 | 'title' => esc_html__( 'Links area', 'twentytwentyone' ),
45 | 'categories' => array( 'twentytwentyone' ),
46 | 'viewportWidth' => 1440,
47 | 'description' => esc_html_x( 'A huge text followed by social networks and email address links.', 'Block pattern description', 'twentytwentyone' ),
48 | 'content' => '
' . wp_kses_post( __( 'Let’s Connect.', 'twentytwentyone' ) ) . '
',
49 | )
50 | );
51 |
52 | // Media & Text Article Title.
53 | register_block_pattern(
54 | 'twentytwentyone/media-text-article-title',
55 | array(
56 | 'title' => esc_html__( 'Media and text article title', 'twentytwentyone' ),
57 | 'categories' => array( 'twentytwentyone' ),
58 | 'viewportWidth' => 1440,
59 | 'description' => esc_html_x( 'A Media & Text block with a big image on the left and a heading on the right. The heading is followed by a separator and a description paragraph.', 'Block pattern description', 'twentytwentyone' ),
60 | 'content' => '' . esc_html__( 'Playing in the Sand', 'twentytwentyone' ) . ' ' . wp_kses_post( __( 'Berthe Morisot (French, 1841-1895)', 'twentytwentyone' ) ) . '
',
61 | )
62 | );
63 |
64 | // Overlapping Images.
65 | register_block_pattern(
66 | 'twentytwentyone/overlapping-images',
67 | array(
68 | 'title' => esc_html__( 'Overlapping images', 'twentytwentyone' ),
69 | 'categories' => array( 'twentytwentyone' ),
70 | 'viewportWidth' => 1024,
71 | 'description' => esc_html_x( 'Three images inside an overlapping columns block.', 'Block pattern description', 'twentytwentyone' ),
72 | 'content' => '',
73 | )
74 | );
75 |
76 | // Two Images Showcase.
77 | register_block_pattern(
78 | 'twentytwentyone/two-images-showcase',
79 | array(
80 | 'title' => esc_html__( 'Two images showcase', 'twentytwentyone' ),
81 | 'categories' => array( 'twentytwentyone' ),
82 | 'viewportWidth' => 1440,
83 | 'description' => esc_html_x( 'A media & text block with a big image on the left and a smaller one with bordered frame on the right.', 'Block pattern description', 'twentytwentyone' ),
84 | 'content' => '',
85 | )
86 | );
87 |
88 | // Overlapping Images and Text.
89 | register_block_pattern(
90 | 'twentytwentyone/overlapping-images-and-text',
91 | array(
92 | 'title' => esc_html__( 'Overlapping images and text', 'twentytwentyone' ),
93 | 'categories' => array( 'twentytwentyone' ),
94 | 'viewportWidth' => 1440,
95 | 'description' => esc_html_x( 'An overlapping columns block with two images and a text description.', 'Block pattern description', 'twentytwentyone' ),
96 | 'content' => ' ',
97 | )
98 | );
99 |
100 | // Portfolio List.
101 | register_block_pattern(
102 | 'twentytwentyone/portfolio-list',
103 | array(
104 | 'title' => esc_html__( 'Portfolio list', 'twentytwentyone' ),
105 | 'categories' => array( 'twentytwentyone' ),
106 | 'description' => esc_html_x( 'A list of projects with thumbnail images.', 'Block pattern description', 'twentytwentyone' ),
107 | 'content' => ' ',
108 | )
109 | );
110 |
111 | register_block_pattern(
112 | 'twentytwentyone/contact-information',
113 | array(
114 | 'title' => esc_html__( 'Contact information', 'twentytwentyone' ),
115 | 'categories' => array( 'twentytwentyone' ),
116 | 'description' => esc_html_x( 'A block with 3 columns that display contact information and social media links.', 'Block pattern description', 'twentytwentyone' ),
117 | 'content' => '' . esc_html_x( '123 Main Street', 'Block pattern sample content', 'twentytwentyone' ) . ' ' . esc_html_x( 'Cambridge, MA, 02139', 'Block pattern sample content', 'twentytwentyone' ) . '
',
118 | )
119 | );
120 | }
121 |
--------------------------------------------------------------------------------
/functions.php:
--------------------------------------------------------------------------------
1 | tag in the document head,
44 | * WordPress will provide it for us.
45 | */
46 | add_theme_support( 'title-tag' );
47 |
48 | /**
49 | * Add post-formats support.
50 | */
51 | add_theme_support(
52 | 'post-formats',
53 | array(
54 | 'link',
55 | 'aside',
56 | 'gallery',
57 | 'image',
58 | 'quote',
59 | 'status',
60 | 'video',
61 | 'audio',
62 | 'chat',
63 | )
64 | );
65 |
66 | /*
67 | * Enable support for Post Thumbnails on posts and pages.
68 | *
69 | * @link https://developer.wordpress.org/themes/functionality/featured-images-post-thumbnails/
70 | */
71 | add_theme_support( 'post-thumbnails' );
72 | set_post_thumbnail_size( 1568, 9999 );
73 |
74 | register_nav_menus(
75 | array(
76 | 'primary' => esc_html__( 'Primary menu', 'twentytwentyone' ),
77 | 'footer' => __( 'Secondary menu', 'twentytwentyone' ),
78 | )
79 | );
80 |
81 | /*
82 | * Switch default core markup for search form, comment form, and comments
83 | * to output valid HTML5.
84 | */
85 | add_theme_support(
86 | 'html5',
87 | array(
88 | 'comment-form',
89 | 'comment-list',
90 | 'gallery',
91 | 'caption',
92 | 'style',
93 | 'script',
94 | 'navigation-widgets',
95 | )
96 | );
97 |
98 | /**
99 | * Add support for core custom logo.
100 | *
101 | * @link https://codex.wordpress.org/Theme_Logo
102 | */
103 | $logo_width = 300;
104 | $logo_height = 100;
105 |
106 | add_theme_support(
107 | 'custom-logo',
108 | array(
109 | 'height' => $logo_height,
110 | 'width' => $logo_width,
111 | 'flex-width' => true,
112 | 'flex-height' => true,
113 | 'unlink-homepage-logo' => true,
114 | )
115 | );
116 |
117 | // Add theme support for selective refresh for widgets.
118 | add_theme_support( 'customize-selective-refresh-widgets' );
119 |
120 | // Add support for Block Styles.
121 | add_theme_support( 'wp-block-styles' );
122 |
123 | // Add support for full and wide align images.
124 | add_theme_support( 'align-wide' );
125 |
126 | // Add support for editor styles.
127 | add_theme_support( 'editor-styles' );
128 | $background_color = get_theme_mod( 'background_color', 'D1E4DD' );
129 | if ( 127 > Twenty_Twenty_One_Custom_Colors::get_relative_luminance_from_hex( $background_color ) ) {
130 | add_theme_support( 'dark-editor-style' );
131 | }
132 |
133 | $editor_stylesheet_path = './assets/css/style-editor.css';
134 |
135 | // Note, the is_IE global variable is defined by WordPress and is used
136 | // to detect if the current browser is internet explorer.
137 | global $is_IE;
138 | if ( $is_IE ) {
139 | $editor_stylesheet_path = './assets/css/ie-editor.css';
140 | }
141 |
142 | // Enqueue editor styles.
143 | add_editor_style( $editor_stylesheet_path );
144 |
145 | // Add custom editor font sizes.
146 | add_theme_support(
147 | 'editor-font-sizes',
148 | array(
149 | array(
150 | 'name' => esc_html__( 'Extra small', 'twentytwentyone' ),
151 | 'shortName' => esc_html_x( 'XS', 'Font size', 'twentytwentyone' ),
152 | 'size' => 16,
153 | 'slug' => 'extra-small',
154 | ),
155 | array(
156 | 'name' => esc_html__( 'Small', 'twentytwentyone' ),
157 | 'shortName' => esc_html_x( 'S', 'Font size', 'twentytwentyone' ),
158 | 'size' => 18,
159 | 'slug' => 'small',
160 | ),
161 | array(
162 | 'name' => esc_html__( 'Normal', 'twentytwentyone' ),
163 | 'shortName' => esc_html_x( 'M', 'Font size', 'twentytwentyone' ),
164 | 'size' => 20,
165 | 'slug' => 'normal',
166 | ),
167 | array(
168 | 'name' => esc_html__( 'Large', 'twentytwentyone' ),
169 | 'shortName' => esc_html_x( 'L', 'Font size', 'twentytwentyone' ),
170 | 'size' => 24,
171 | 'slug' => 'large',
172 | ),
173 | array(
174 | 'name' => esc_html__( 'Extra large', 'twentytwentyone' ),
175 | 'shortName' => esc_html_x( 'XL', 'Font size', 'twentytwentyone' ),
176 | 'size' => 40,
177 | 'slug' => 'extra-large',
178 | ),
179 | array(
180 | 'name' => esc_html__( 'Huge', 'twentytwentyone' ),
181 | 'shortName' => esc_html_x( 'XXL', 'Font size', 'twentytwentyone' ),
182 | 'size' => 96,
183 | 'slug' => 'huge',
184 | ),
185 | array(
186 | 'name' => esc_html__( 'Gigantic', 'twentytwentyone' ),
187 | 'shortName' => esc_html_x( 'XXXL', 'Font size', 'twentytwentyone' ),
188 | 'size' => 144,
189 | 'slug' => 'gigantic',
190 | ),
191 | )
192 | );
193 |
194 | // Custom background color.
195 | add_theme_support(
196 | 'custom-background',
197 | array(
198 | 'default-color' => 'd1e4dd',
199 | )
200 | );
201 |
202 | // Editor color palette.
203 | $black = '#000000';
204 | $dark_gray = '#28303D';
205 | $gray = '#39414D';
206 | $green = '#D1E4DD';
207 | $blue = '#D1DFE4';
208 | $purple = '#D1D1E4';
209 | $red = '#E4D1D1';
210 | $orange = '#E4DAD1';
211 | $yellow = '#EEEADD';
212 | $white = '#FFFFFF';
213 |
214 | add_theme_support(
215 | 'editor-color-palette',
216 | array(
217 | array(
218 | 'name' => esc_html__( 'Black', 'twentytwentyone' ),
219 | 'slug' => 'black',
220 | 'color' => $black,
221 | ),
222 | array(
223 | 'name' => esc_html__( 'Dark gray', 'twentytwentyone' ),
224 | 'slug' => 'dark-gray',
225 | 'color' => $dark_gray,
226 | ),
227 | array(
228 | 'name' => esc_html__( 'Gray', 'twentytwentyone' ),
229 | 'slug' => 'gray',
230 | 'color' => $gray,
231 | ),
232 | array(
233 | 'name' => esc_html__( 'Green', 'twentytwentyone' ),
234 | 'slug' => 'green',
235 | 'color' => $green,
236 | ),
237 | array(
238 | 'name' => esc_html__( 'Blue', 'twentytwentyone' ),
239 | 'slug' => 'blue',
240 | 'color' => $blue,
241 | ),
242 | array(
243 | 'name' => esc_html__( 'Purple', 'twentytwentyone' ),
244 | 'slug' => 'purple',
245 | 'color' => $purple,
246 | ),
247 | array(
248 | 'name' => esc_html__( 'Red', 'twentytwentyone' ),
249 | 'slug' => 'red',
250 | 'color' => $red,
251 | ),
252 | array(
253 | 'name' => esc_html__( 'Orange', 'twentytwentyone' ),
254 | 'slug' => 'orange',
255 | 'color' => $orange,
256 | ),
257 | array(
258 | 'name' => esc_html__( 'Yellow', 'twentytwentyone' ),
259 | 'slug' => 'yellow',
260 | 'color' => $yellow,
261 | ),
262 | array(
263 | 'name' => esc_html__( 'White', 'twentytwentyone' ),
264 | 'slug' => 'white',
265 | 'color' => $white,
266 | ),
267 | )
268 | );
269 |
270 | add_theme_support(
271 | 'editor-gradient-presets',
272 | array(
273 | array(
274 | 'name' => esc_html__( 'Purple to yellow', 'twentytwentyone' ),
275 | 'gradient' => 'linear-gradient(160deg, ' . $purple . ' 0%, ' . $yellow . ' 100%)',
276 | 'slug' => 'purple-to-yellow',
277 | ),
278 | array(
279 | 'name' => esc_html__( 'Yellow to purple', 'twentytwentyone' ),
280 | 'gradient' => 'linear-gradient(160deg, ' . $yellow . ' 0%, ' . $purple . ' 100%)',
281 | 'slug' => 'yellow-to-purple',
282 | ),
283 | array(
284 | 'name' => esc_html__( 'Green to yellow', 'twentytwentyone' ),
285 | 'gradient' => 'linear-gradient(160deg, ' . $green . ' 0%, ' . $yellow . ' 100%)',
286 | 'slug' => 'green-to-yellow',
287 | ),
288 | array(
289 | 'name' => esc_html__( 'Yellow to green', 'twentytwentyone' ),
290 | 'gradient' => 'linear-gradient(160deg, ' . $yellow . ' 0%, ' . $green . ' 100%)',
291 | 'slug' => 'yellow-to-green',
292 | ),
293 | array(
294 | 'name' => esc_html__( 'Red to yellow', 'twentytwentyone' ),
295 | 'gradient' => 'linear-gradient(160deg, ' . $red . ' 0%, ' . $yellow . ' 100%)',
296 | 'slug' => 'red-to-yellow',
297 | ),
298 | array(
299 | 'name' => esc_html__( 'Yellow to red', 'twentytwentyone' ),
300 | 'gradient' => 'linear-gradient(160deg, ' . $yellow . ' 0%, ' . $red . ' 100%)',
301 | 'slug' => 'yellow-to-red',
302 | ),
303 | array(
304 | 'name' => esc_html__( 'Purple to red', 'twentytwentyone' ),
305 | 'gradient' => 'linear-gradient(160deg, ' . $purple . ' 0%, ' . $red . ' 100%)',
306 | 'slug' => 'purple-to-red',
307 | ),
308 | array(
309 | 'name' => esc_html__( 'Red to purple', 'twentytwentyone' ),
310 | 'gradient' => 'linear-gradient(160deg, ' . $red . ' 0%, ' . $purple . ' 100%)',
311 | 'slug' => 'red-to-purple',
312 | ),
313 | )
314 | );
315 |
316 | /*
317 | * Adds starter content to highlight the theme on fresh sites.
318 | * This is done conditionally to avoid loading the starter content on every
319 | * page load, as it is a one-off operation only needed once in the customizer.
320 | */
321 | if ( is_customize_preview() ) {
322 | require get_template_directory() . '/inc/starter-content.php';
323 | add_theme_support( 'starter-content', twenty_twenty_one_get_starter_content() );
324 | }
325 |
326 | // Add support for responsive embedded content.
327 | add_theme_support( 'responsive-embeds' );
328 |
329 | // Add support for custom line height controls.
330 | add_theme_support( 'custom-line-height' );
331 |
332 | // Add support for experimental link color control.
333 | add_theme_support( 'experimental-link-color' );
334 |
335 | // Add support for experimental cover block spacing.
336 | add_theme_support( 'custom-spacing' );
337 |
338 | // Add support for custom units.
339 | // This was removed in WordPress 5.6 but is still required to properly support WP 5.5.
340 | add_theme_support( 'custom-units' );
341 | }
342 | }
343 | add_action( 'after_setup_theme', 'twenty_twenty_one_setup' );
344 |
345 | /**
346 | * Register widget area.
347 | *
348 | * @since 1.0.0
349 | *
350 | * @link https://developer.wordpress.org/themes/functionality/sidebars/#registering-a-sidebar
351 | *
352 | * @return void
353 | */
354 | function twenty_twenty_one_widgets_init() {
355 |
356 | register_sidebar(
357 | array(
358 | 'name' => esc_html__( 'Footer', 'twentytwentyone' ),
359 | 'id' => 'sidebar-1',
360 | 'description' => esc_html__( 'Add widgets here to appear in your footer.', 'twentytwentyone' ),
361 | 'before_widget' => '',
363 | 'before_title' => '',
365 | )
366 | );
367 | }
368 | add_action( 'widgets_init', 'twenty_twenty_one_widgets_init' );
369 |
370 | /**
371 | * Set the content width in pixels, based on the theme's design and stylesheet.
372 | *
373 | * Priority 0 to make it available to lower priority callbacks.
374 | *
375 | * @since 1.0.0
376 | *
377 | * @global int $content_width Content width.
378 | *
379 | * @return void
380 | */
381 | function twenty_twenty_one_content_width() {
382 | // This variable is intended to be overruled from themes.
383 | // Open WPCS issue: {@link https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards/issues/1043}.
384 | // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound
385 | $GLOBALS['content_width'] = apply_filters( 'twenty_twenty_one_content_width', 750 );
386 | }
387 | add_action( 'after_setup_theme', 'twenty_twenty_one_content_width', 0 );
388 |
389 | /**
390 | * Enqueue scripts and styles.
391 | *
392 | * @since 1.0.0
393 | *
394 | * @return void
395 | */
396 | function twenty_twenty_one_scripts() {
397 | // Note, the is_IE global variable is defined by WordPress and is used
398 | // to detect if the current browser is internet explorer.
399 | global $is_IE;
400 | if ( $is_IE ) {
401 | // If IE 11 or below, use a flattened stylesheet with static values replacing CSS Variables.
402 | wp_enqueue_style( 'twenty-twenty-one-style', get_template_directory_uri() . '/assets/css/ie.css', array(), wp_get_theme()->get( 'Version' ) );
403 | } else {
404 | // If not IE, use the standard stylesheet.
405 | wp_enqueue_style( 'twenty-twenty-one-style', get_template_directory_uri() . '/style.css', array(), wp_get_theme()->get( 'Version' ) );
406 | }
407 |
408 | // RTL styles.
409 | wp_style_add_data( 'twenty-twenty-one-style', 'rtl', 'replace' );
410 |
411 | // Print styles.
412 | wp_enqueue_style( 'twenty-twenty-one-print-style', get_template_directory_uri() . '/assets/css/print.css', array(), wp_get_theme()->get( 'Version' ), 'print' );
413 |
414 | // Threaded comment reply styles.
415 | if ( is_singular() && comments_open() && get_option( 'thread_comments' ) ) {
416 | wp_enqueue_script( 'comment-reply' );
417 | }
418 |
419 | wp_register_script(
420 | 'twenty-twenty-one-ie11-polyfills',
421 | get_template_directory_uri() . '/assets/js/polyfills.js',
422 | array(),
423 | wp_get_theme()->get( 'Version' ),
424 | true
425 | );
426 |
427 | // Main navigation scripts.
428 | if ( has_nav_menu( 'primary' ) ) {
429 | wp_enqueue_script(
430 | 'twenty-twenty-one-primary-navigation-script',
431 | get_template_directory_uri() . '/assets/js/primary-navigation.js',
432 | array( 'twenty-twenty-one-ie11-polyfills' ),
433 | wp_get_theme()->get( 'Version' ),
434 | true
435 | );
436 | }
437 |
438 | // Responsive embeds script.
439 | wp_enqueue_script(
440 | 'twenty-twenty-one-responsive-embeds-script',
441 | get_template_directory_uri() . '/assets/js/responsive-embeds.js',
442 | array( 'twenty-twenty-one-ie11-polyfills' ),
443 | wp_get_theme()->get( 'Version' ),
444 | true
445 | );
446 | }
447 | add_action( 'wp_enqueue_scripts', 'twenty_twenty_one_scripts' );
448 |
449 | /**
450 | * Enqueue block editor script.
451 | *
452 | * @since 1.0.0
453 | *
454 | * @return void
455 | */
456 | function twentytwentyone_block_editor_script() {
457 |
458 | wp_enqueue_script( 'twentytwentyone-editor', get_theme_file_uri( '/assets/js/editor.js' ), array( 'wp-blocks', 'wp-dom' ), wp_get_theme()->get( 'Version' ), true );
459 | }
460 |
461 | add_action( 'enqueue_block_editor_assets', 'twentytwentyone_block_editor_script' );
462 |
463 | /**
464 | * Fix skip link focus in IE11.
465 | *
466 | * This does not enqueue the script because it is tiny and because it is only for IE11,
467 | * thus it does not warrant having an entire dedicated blocking script being loaded.
468 | *
469 | * @link https://git.io/vWdr2
470 | */
471 | function twenty_twenty_one_skip_link_focus_fix() {
472 |
473 | // If SCRIPT_DEBUG is defined and true, print the unminified file.
474 | if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) {
475 | echo '';
478 | }
479 |
480 | // The following is minified via `npx terser --compress --mangle -- assets/js/skip-link-focus-fix.js`.
481 | ?>
482 |
485 | get( 'Version' ),
547 | true
548 | );
549 |
550 | wp_enqueue_script(
551 | 'twentytwentyone-customize-preview',
552 | get_theme_file_uri( '/assets/js/customize-preview.js' ),
553 | array( 'customize-preview', 'customize-selective-refresh', 'jquery', 'twentytwentyone-customize-helpers' ),
554 | wp_get_theme()->get( 'Version' ),
555 | true
556 | );
557 | }
558 | add_action( 'customize_preview_init', 'twentytwentyone_customize_preview_init' );
559 |
560 | /**
561 | * Enqueue scripts for the customizer.
562 | *
563 | * @since 1.0.0
564 | *
565 | * @return void
566 | */
567 | function twentytwentyone_customize_controls_enqueue_scripts() {
568 |
569 | wp_enqueue_script(
570 | 'twentytwentyone-customize-helpers',
571 | get_theme_file_uri( '/assets/js/customize-helpers.js' ),
572 | array(),
573 | wp_get_theme()->get( 'Version' ),
574 | true
575 | );
576 | }
577 | add_action( 'customize_controls_enqueue_scripts', 'twentytwentyone_customize_controls_enqueue_scripts' );
578 |
579 | /**
580 | * Calculate classes for the main element.
581 | *
582 | * @since 1.0.0
583 | *
584 | * @return void
585 | */
586 | function twentytwentyone_the_html_classes() {
587 | $classes = apply_filters( 'twentytwentyone_html_classes', '' );
588 | if ( ! $classes ) {
589 | return;
590 | }
591 | echo 'class="' . esc_attr( $classes ) . '"';
592 | }
593 |
594 | /**
595 | * Add "is-IE" class to body if the user is on Internet Explorer.
596 | *
597 | * @since 1.0.0
598 | *
599 | * @return void
600 | */
601 | function twentytwentyone_add_ie_class() {
602 | ?>
603 |
608 |