Monday, March 25, 2024

Build a WordPress Theme Options Page With Carbon Fields

Build a WordPress Theme Options Page With Carbon Fields

In this new tutorial, I’ll explain how to use Carbon Fields, a WordPress custom fields library, to create a Theme Options/Settings page. 

What is Carbon Fields?

Carbon Fields is a free WordPress library developed by 2create. It helps WordPress theme and plugin developers create additional fields for post types, navigation menus, widgets, etc., or even register custom Gutenberg blocks without touching JavaScript. 

carbon fields home pagecarbon fields home pagecarbon fields home page

As we’ll see, along the way, it comes with various fields like checkboxes, repeaters (complex), texts, etc. 

An important note is that we don’t install it as a WordPress plugin but through the Composer PHP dependency manager—if you aren’t familiar with it, think of it like the npm Javascript dependency manager.

So, at this point, if you want to follow this tutorial, first install Composer, then install Carbon Fields by running this command:

1
composer require htmlburger/carbon-fields

What is a WordPress Theme Options Page?

A theme options page is an admin page, located outside the default WordPress pages, where developers store global settings which affect the functionality of their WordPress site or plugin.

For example, there might be options for managing things like:

  • Header behavior—whether it should be sticky or not.
  • Typography settings—the global font size, the global font, the colors of the headings, etc.  

Carbon Fields Underscore

The data are usually stored in the *_options table and available on any site page.

It’s worth noting that the Carbon Fields library automatically prefixes all fields with an underscore when stored in the database (e.g. the footer_text field becomes _footer_text).

How the Carbon Fields library stores the fields in the databaseHow the Carbon Fields library stores the fields in the databaseHow the Carbon Fields library stores the fields in the database

Implementation

For this demonstration, we’ll create an options page that will hold information for the header layout, footer layout, contact info, and social channels of a demo site. 

Please note that the main focus will be to create the page structure in the admin and only show the fields on the front end without adding any styles. All the theme files will be available on this GitHub repo.

As usual, I’ll work with my custom Playground theme—I’ll leave it as barebone as possible and only add the required functionality for the Carbon Fields.

With that in mind, following the docs, I’ll add this code inside the functions.php:

1
use Carbon_Fields\Container;
2
use Carbon_Fields\Field;
3
4
function crb_attach_theme_options() {
5
    Container::make( 'theme_options', __( 'Theme Settings', 'playground' ) )
6
    ->set_page_menu_position( 4 )
7
    ->set_icon( 'dashicons-admin-settings' )
8
    ->add_tab(
9
        __( 'Header', 'playground' ),
10
        array(...),
11
    )
12
    ->add_tab(
13
        __( 'Contact', 'playground' ),
14
        array(...)
15
    )
16
    ->add_tab(
17
        __( 'Socials', 'playground' ),
18
        array(...)
19
    )
20
    ->add_tab(
21
        __( 'Footer', 'playground' ),
22
        array(...)
23
    );
24
}
25
add_action( 'carbon_fields_register_fields', 'crb_attach_theme_options' );
26
27
function crb_load() {
28
    require_once( 'vendor/autoload.php' );
29
    \Carbon_Fields\Carbon_Fields::boot();
30
}
31
add_action( 'after_setup_theme', 'crb_load' );

This code will wrap all the fields inside a container and create a new menu item called Theme Settings underneath the Dashboard menu item. Of course, you can customize the item’s location and the rendered icon.

The new theme options page as created by the Carbon Fields libraryThe new theme options page as created by the Carbon Fields libraryThe new theme options page as created by the Carbon Fields library

Plus, the add_tab method will give a better organization, as it’ll group all our options page fields into four tabbed sections

Our tabbed sectionsOur tabbed sectionsOur tabbed sections

Let’s now have a closer look at these tabs!

Header Tab

The header tab will include fields related to the header functionality. More specifically:

  • A checkbox field will manage whether a ribbon will appear above the main header.
  • Three fields (one rich text and two color fields) will set the ribbon’s appearance. However, these will appear and be functional only if the previous checkbox field is selected.
  • A header scripts field will allow us to embed the Font Awesome 6 icon library in the head tag. We’ll use that library for printing the social iconswe’ll get back to it in an upcoming section.
The fields inside the header tabThe fields inside the header tabThe fields inside the header tab

We’ll create all the fields through the make method like this:

1
Field::make( 'header_scripts', 'header_scripts', __( 'Header Scripts', 'playground' ) ),
2
Field::make( 'rich_text', 'header_ribbon', __( 'Ribbon', 'playground' ) )
3
->set_conditional_logic(
4
    array(
5
        array(
6
            'field' => 'show_header_ribbon',
7
            'value' => true,
8
        ),
9
    )
10
),
11
Field::make( 'color', 'header_ribbon_text_color', __( 'Ribbon Text Color', 'playground' ) )
12
->set_conditional_logic(
13
    array(
14
        array(
15
            'field' => 'show_header_ribbon',
16
            'value' => true,
17
        ),
18
    )
19
),
20
Field::make( 'color', 'header_ribbon_bg_color', __( 'Ribbon Background Color', 'playground' ) )
21
->set_conditional_logic(
22
    array(
23
        array(
24
            'field' => 'show_header_ribbon',
25
            'value' => true,
26
        ),
27
    )
28
),
29
Field::make( 'checkbox', 'show_header_ribbon', __( 'Show Ribbon (Top Header)?', 'playground' ) )->set_option_value( 'yes' ),

Then, wherever we want to display them, we’ll use the carbon_get_theme_option method and pass as an argument the field name like this:

1
<?php 
2
$show_header_ribbon = carbon_get_theme_option( 'show_header_ribbon' );
3
?>
4
5
<header class="site-header">
6
    <nav>
7
        <?php 
8
        if ( $show_header_ribbon ) : 
9
            $header_ribbon            = carbon_get_theme_option( 'header_ribbon' );
10
            $header_ribbon_text_color = carbon_get_theme_option( 'header_ribbon_text_color' ) ? carbon_get_theme_option( 'header_ribbon_text_color' ) : '#000';
11
            $header_ribbon_bg_color   = carbon_get_theme_option( 'header_ribbon_bg_color' ) ? carbon_get_theme_option( 'header_ribbon_bg_color' ) : 'transparent';
12
            ?>
13
            <div class="header-top" style="color: <?php echo esc_attr( $header_ribbon_text_color ); ?>; background: <?php echo esc_attr( $header_ribbon_bg_color ); ?>">
14
                <?php echo wp_kses_post( $header_ribbon ); ?>
15
            </div>
16
        <?php endif; ?>
17
        <div class="header-bottom">A nice header here</div>
18
    </nav>
19
</header>

Contact Tab

The contact tab will include contact-related fields that will be present across different page sections (e.g. on the header, footer, contact pages, sidebars, etc.). More specifically, there will be:

  • A textarea field for adding the company address. 
  • A text field for linking to a Google Maps location.
  • A text field for adding the company phone.
  • A text field for adding the company mail.
  • A select field with dynamic options for allowing users to pick the appropriate Contact Form 7 (CF7) form as a newsletter form.

The fields inside the contact tabThe fields inside the contact tabThe fields inside the contact tab

Imagine that our site contains the following forms:

We should make them all available to the admin users. But here’s the challenge: we won’t hardcode them but instead make all the options of the select field dynamic.

To accomplish this, we’ll define the custom get_all_forms function and pass it as a parameter of the add_options method of our select field. This function will return all the site forms in an array format. 

Here’s how we create the fields of this tab:

1
Field::make( 'textarea', 'address', __( 'Address', 'playground' ) ),
2
Field::make( 'text', 'address_directions', __( 'Address Directions', 'playground' ) )->set_attribute( 'type', 'url' ),
3
Field::make( 'text', 'phone', __( 'Phone', 'playground' ) )->set_attribute( 'type', 'tel' ),
4
Field::make( 'text', 'email', __( 'Email', 'playground' ) )->set_attribute( 'type', 'email' ),
5
Field::make( 'select', 'newsletter_form', __( 'Select Newsletter Form', 'playground' ) )
6
->add_options( 'get_all_forms' ), // or set

And here’s the get_all_forms() declaration:

1
function get_all_forms() {
2
    $all_forms_array = array();
3
    $all_forms       = get_posts(
4
        array(
5
            'post_type'      => 'wpcf7_contact_form',
6
            'posts_per_page' => -1,
7
        )
8
    );
9
    
10
    foreach ( $all_forms as $form ) :
11
        $all_forms_array[ $form->ID ] = esc_html( $form->post_title );
12
    endforeach;
13
    return $all_forms_array;
14
}

Next, we output the fields in the location we want like this:

1
<?php 
2
$address         = carbon_get_theme_option( 'address' );
3
$directions      = carbon_get_theme_option( 'address_directions' );
4
$phone           = carbon_get_theme_option( 'phone' );
5
$email           = carbon_get_theme_option( 'email' );
6
$newsletter_form = carbon_get_theme_option( 'newsletter_form' );
7
?>
8
9
<address>
10
    <?php echo wp_kses_post( $address ); ?>
11
    <a href="<?php echo esc_url( $directions ); ?>" target="_blank">
12
        <?php esc_html_e( 'See Map', 'playground' ); ?>
13
    </a>
14
    <a href="<?php echo esc_url( 'tel:' . $phone ); ?>">
15
        <?php echo esc_html( $phone ); ?>
16
    </a>
17
    <a href="<?php echo esc_url( 'mailto:' . $email ); ?>">
18
        <?php echo esc_html( $email ); ?>
19
    </a>
20
</address>
21
22
<div>
23
    <?php echo do_shortcode( '[contact-form-7 id="' . esc_attr( $newsletter_form ) . '"]' ); ?>
24
</div>

Socials Tab

The socials tab will include a complex field for adding an infinite number of socials. Each social will have three text fields:

  • A text field for the social title (e.g. Facebook).
  • A text field for the social URL.
  • A text field for placing Font Awesome classes (e.g. Facebook classes). 

For those of you coming from the ACF world, the complex field is equivalent to ACF’s repeater field.

The fields inside the socials tabThe fields inside the socials tabThe fields inside the socials tab

Here’s how we create the complex field in terms of code:

1
<?php 
2
Field::make( 'complex', 'socials' )
3
->set_layout( 'tabbed-horizontal' )
4
->add_fields(
5
    array(
6
        Field::make( 'text', 'social_title', __( 'Social Title', 'playground' ) ),
7
        Field::make( 'text', 'social_url', __( 'Social URL', 'playground' ) )->set_attribute( 'type', 'url' ),
8
        Field::make( 'text', 'social_icon', __( 'Social Icon', 'playground' ) )
9
        ->set_attribute( 'placeholder', 'Add a class from the Font Awesome library' ),
10
    )
11
),

And here’s how we print their data wherever we want:

1
<?php 
2
$socials = carbon_get_theme_option( 'socials' );
3
?>
4
5
<ul>
6
    <?php
7
    foreach ( $socials as $social ) :
8
        //https://github.com/WordPress/WordPress-Coding-Standards/issues/1029

9
        /* translators: %s: social channel */
10
        $title = sprintf( __( 'Find us on %s', 'playground' ), $social['social_title'] );
11
        ?>
12
        <li>
13
            <a href="<?php echo esc_url( $social['social_url'] ); ?>" aria-label="<?php echo esc_attr( $title ); ?>" title="<?php echo esc_attr( $title ); ?>" target="_blank">
14
                <i class="<?php echo esc_attr( $social['social_icon'] ); ?>" aria-hidden="true"></i>
15
            </a>
16
        </li>
17
    <?php endforeach; ?>
18
</ul>

Similar to the header tab, the footer tab will include fields related to the footer functionality. More specifically:

  • A text field will output the footer text.
  • Two color fields will set the footer’s foreground and background colors.

The fields inside the footer tabThe fields inside the footer tabThe fields inside the footer tab

As usual, we create them with the make method:

1
Field::make( 'rich_text', 'footer_text', __( 'Footer Text', 'playground' ) ),
2
Field::make( 'color', 'footer_text_color', __( 'Footer Text Color', 'playground' ) ),
3
Field::make( 'color', 'footer_bg_color', __( 'Footer Background Color', 'playground' ) ),

And we display them in the target file:

1
<?php 
2
$footer_text       = carbon_get_theme_option( 'footer_text' );
3
$footer_text_color = carbon_get_theme_option( 'footer_text_color' ) ? carbon_get_theme_option( 'footer_text_color' ) : '#000';
4
$footer_bg_color   = carbon_get_theme_option( 'footer_bg_color' ) ? carbon_get_theme_option( 'footer_bg_color' ) : 'transparent';
5
?>
6
7
<footer class="site-footer" style="color: <?php echo esc_attr( $footer_text_color ); ?>; background: <?php echo esc_attr( $footer_bg_color ); ?>">
8
    <div class="container">
9
        <?php echo wp_kses_post( $footer_text ); ?>
10
    </div>
11
</footer>

Conclusion

In this tutorial, we went through the Carbon Fields WordPress custom fields toolkit and explored many of its field types by building a Theme Options page. I hope you now have enough material to decide whether this library can suit your needs for your next WordPress project. 

In short, go with it if:

  • You want something free and aren’t a big fan of extending the WordPress Customizer.
  • You can work without an administration interface.
  • You build a plugin/theme that requires a small number of fields.

On the other hand, you might have second thoughts of using it if:

  • You don’t have a problem paying for a WordPress plugin that provides more type options and layout styling like ACF PRO.
  • You prefer to manage your fields through a UI and feel overwhelmed managing them through code.
  • Besides not mandatory, if you’re building a multilingual site/plugin, you might want to pick solutions that are officially supported by the translation tool you choose. For example, although Carbon Fields seems to work fine with WPML according to their docs and my local tests, this tool isn’t officially compatible with WPML.  

But without any doubt, this is a handy tool that every WordPress developer should try at least once. What about you? Have you ever used that? Let us know on X!

As always, thanks a lot for reading!


No comments:

Post a Comment