namespace WPForms\Frontend;
use WPForms\Integrations\Gutenberg\ThemesData;
use WPForms\Lite\Integrations\Gutenberg\ThemesData as LiteThemesData;
use WPForms\Pro\Integrations\Gutenberg\ThemesData as ProThemesData;
use WPForms\Pro\Integrations\Gutenberg\StockPhotos;
public const ROOT_VARS = [
'field-border-radius' => '3px',
'field-border-style' => 'solid',
'field-border-size' => '1px',
'field-background-color' => self::WHITE,
'field-border-color' => 'rgba( 0, 0, 0, 0.25 )',
'field-text-color' => 'rgba( 0, 0, 0, 0.7 )',
'field-menu-color' => self::WHITE,
'label-color' => 'rgba( 0, 0, 0, 0.85 )',
'label-sublabel-color' => 'rgba( 0, 0, 0, 0.55 )',
'label-error-color' => '#d63637',
'button-border-radius' => '3px',
'button-border-style' => 'none',
'button-border-size' => '1px',
'button-background-color' => '#066aab',
'button-border-color' => '#066aab',
'button-text-color' => self::WHITE,
'page-break-color' => '#066aab',
'background-image' => 'none',
'background-position' => 'center center',
'background-repeat' => 'no-repeat',
'background-size' => 'cover',
'background-width' => '100px',
'background-height' => '100px',
'background-color' => 'rgba( 0, 0, 0, 0 )',
'background-url' => 'url()',
'container-padding' => '0px',
'container-border-style' => 'none',
'container-border-width' => '1px',
'container-border-color' => '#000000',
'container-border-radius' => '3px',
* Container shadow vars and values.
public const CONTAINER_SHADOW_SIZE = [
'box-shadow' => '0px 3px 5px 0px rgba(0, 0, 0, 0.1)',
'box-shadow' => '0px 10px 20px 0px rgba(0, 0, 0, 0.1)',
'box-shadow' => '0px 30px 50px -10px rgba(0, 0, 0, 0.15)',
* Field Size vars and values.
public const FIELD_SIZE = [
'input-height' => '31px',
'input-spacing' => '10px',
'checkbox-size' => '14px',
'sublabel-spacing' => '5px',
'input-height' => '43px',
'input-spacing' => '15px',
'checkbox-size' => '16px',
'sublabel-spacing' => '5px',
'input-height' => '50px',
'input-spacing' => '20px',
'checkbox-size' => '18px',
'sublabel-spacing' => '10px',
* Label Size vars and values.
public const LABEL_SIZE = [
'sublabel-font-size' => '13px',
'sublabel-line-height' => '16px',
'sublabel-font-size' => '14px',
'sublabel-line-height' => '17px',
'sublabel-font-size' => '16px',
'sublabel-line-height' => '19px',
* Button Size vars and values.
public const BUTTON_SIZE = [
private const SPARE_VARS = [ 'field-border-color' ];
private const WHITE = '#ffffff';
* Flag to check if root CSS vars were output.
private $is_root_vars_displayed;
public function init(): void {
private function init_vars(): void {
$vars[':root'] = array_merge(
$this->get_complex_vars( 'field-size', self::FIELD_SIZE['medium'] ),
$this->get_complex_vars( 'label-size', self::LABEL_SIZE['medium'] ),
$this->get_complex_vars( 'button-size', self::BUTTON_SIZE['medium'] ),
$this->get_complex_vars( 'container-shadow-size', self::CONTAINER_SHADOW_SIZE['none'] )
* Allows developers to modify default CSS variables which output on the frontend.
* @param array $vars CSS variables two-dimensional array.
* The first level keys is the CSS selector.
* Second level keys is the variable name without the `--wpforms-` prefix.
$this->css_vars = apply_filters( 'wpforms_frontend_css_vars_init_vars', $vars );
* Get complex CSS variables data.
* @param string $prefix CSS variable prefix.
* @param array $values Values.
public function get_complex_vars( string $prefix, array $values ): array {
foreach ( $values as $key => $value ) {
$vars[ "{$prefix}-{$key}" ] = $value;
* Get CSS variables data by selector.
* @param string $selector Selector.
public function get_vars( string $selector = ':root' ): array {
if ( empty( $this->css_vars[ $selector ] ) ) {
return $this->css_vars[ $selector ];
* Output root CSS variables.
* @since 1.8.1.2 Added $force argument.
* @param bool $force Force output root variables.
* @noinspection PhpMissingParamTypeInspection
public function output_root( $force = false ): void {
_deprecated_function( __METHOD__, '1.9.3 of the WPForms plugin' );
if ( ! empty( $this->is_root_vars_displayed ) && empty( $force ) ) {
$this->output_selector_vars( ':root', $this->css_vars[':root'] );
$this->is_root_vars_displayed = true;
* Get root variables CSS.
public function get_root_vars_css(): string {
return $this->get_selector_vars_css( ':root', $this->css_vars[':root'] );
* Output selector's CSS variables.
* @param string $selector Selector.
* @param array $vars Variables data.
* @param string $style_id Style tag ID attribute. Optional. Default is an empty string.
* @param string|int $form_id Form ID. Optional. Default is an empty string.
public function output_selector_vars( string $selector, array $vars, string $style_id = '', $form_id = '' ): void {
if ( empty( $this->render_engine ) ) {
$this->render_engine = wpforms_get_render_engine();
if ( $this->render_engine === 'classic' ) {
// If this is not full "Base and Form Theme Styling", skip.
if ( (int) wpforms_setting( 'disable-css', '1' ) !== 1 ) {
$style_id = empty( $style_id ) ? 'wpforms-css-vars-' . $selector : $style_id;
sanitize_key( $style_id ),
$this->get_selector_vars_css( $selector, $vars, $form_id ) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
* Output CSS vars for the form added as a shortcode.
* @param array $atts Shortcode attributes.
public function output_css_vars_for_shortcode( array $atts ): void {
if ( empty( $atts['id'] ) ) {
$form_handler = wpforms()->obj( 'form' );
$form_id = (int) $atts['id'];
$form_data = $form_handler->get( $form_id, [ 'content_only' => true ] );
if ( empty( $form_data ) ) {
$attr = isset( $form_data['settings']['themes'] ) ? (array) $form_data['settings']['themes'] : [];
$attr = $this->maybe_override_attributes( $attr );
$css_vars = $this->get_customized_css_vars( $attr );
$css_vars = $this->add_css_vars_units( $css_vars );
$selector = "#wpforms-{$form_id}";
$style_id = "wpforms-css-vars-{$form_id}";
$this->output_selector_vars( $selector, $css_vars, $style_id, $form_id );
$this->output_custom_css( $attr, $selector, $style_id );
* @param array $attr Attributes.
* @param string $selector Selector.
* @param string $style_id Style ID.
* @noinspection PhpMissingParamTypeInspection
private function output_custom_css( array $attr, string $selector, string $style_id ): void {
if ( wpforms_get_render_engine() === 'classic' ) {
$custom_css = trim( $attr['customCss'] ?? '' );
if ( empty( $custom_css ) ) {
'<style id="%1$s-custom-css">
sanitize_key( $style_id ),
$selector, // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
wp_strip_all_tags( $custom_css ) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
* Maybe override attributes with themes.json settings.
* @param array $attr Attributes.
private function maybe_override_attributes( array $attr ): array {
$theme_slug = (string) ( $attr['wpformsTheme'] ?? '' );
if ( empty( $theme_slug ) ) {
$attr = $this->normalize_background_url( $attr );
$theme_data = $this->get_themes_data_object()->get_theme( $theme_slug );
$settings = $theme_data['settings'] ?? [];
return array_merge( $attr, $settings );
* Normalize background URL.
* Check if the background URL is not wrapped in url() and add it if needed.
* @param array $attr Attributes.
private function normalize_background_url( array $attr ): array {
if ( ! isset( $attr['backgroundUrl'] ) ) {
if ( strpos( $attr['backgroundUrl'], 'url(' ) === 0 ) {
$attr['backgroundUrl'] = 'url(' . $attr['backgroundUrl'] . ')';
* Get themes data object.