/home/techb158/primomovers.ca/wp-content/plugins/everest-forms/addons/StyleCustomizer/V2
NameSizeModeActions
assets/-0755rm
BuilderPanel.php74580644editdlrm
Compiler.php109390644editdlrm
Engine.php24110644editdlrm
FrontendEnqueue.php90990644editdlrm
Migrator.php276600644editdlrm
Palettes.php53830644editdlrm
PreviewDraft.php155030644editdlrm
RestController.php339750644editdlrm
Sanitizer.php200860644editdlrm
Schema.php477200644editdlrm
Templates.php134540644editdlrm
Edit: /home/techb158/primomovers.ca/wp-content/plugins/everest-forms/addons/StyleCustomizer/V2/Schema.php (47720B)
token index. * * @var array|null */ protected static $index = null; /** * Current schema version. * * @return int */ public static function version() { return self::VERSION; } /** * Reset the memoized cache (tests / after a runtime filter change). */ public static function flush() { self::$tokens = null; self::$index = null; } /* --------------------------------------------------------------------- * * Public accessors * --------------------------------------------------------------------- */ /** * Section metadata — drives the drill-down list order, titles and variant/state tabs. * * @return array */ public static function sections() { $sections = array( 'form' => array( 'title' => __( 'Form', 'everest-forms' ), 'hint' => __( 'The canvas everything sits on — plus the form-wide font.', 'everest-forms' ), ), 'text' => array( 'title' => __( 'Text', 'everest-forms' ), 'hint' => __( 'One typography system, four roles.', 'everest-forms' ), 'variants' => array( 'label', 'sublabel', 'desc', 'title' ), ), 'fields' => array( 'title' => __( 'Inputs', 'everest-forms' ), 'hint' => __( 'Text boxes, dropdowns, textareas, date pickers — and the upload area.', 'everest-forms' ), 'states' => array( 'normal', 'focus' ), ), 'choice' => array( 'title' => __( 'Choices', 'everest-forms' ), 'hint' => __( 'One style set for every choice field: radios, checkboxes, Yes/No, image choices, ratings, Likert rows, and payment pickers.', 'everest-forms' ), ), 'button' => array( 'title' => __( 'Button', 'everest-forms' ), 'hint' => __( 'Styles Submit — Multi-Part and file-upload buttons follow automatically.', 'everest-forms' ), 'states' => array( 'normal', 'hover' ), ), 'messages' => array( 'title' => __( 'Messages', 'everest-forms' ), 'hint' => __( 'The success, error and validation messages shown to visitors.', 'everest-forms' ), 'states' => array( 'success', 'error', 'validation' ), ), ); // Product policy: every design section is Pro; only the palette picker + Custom CSS are free. foreach ( $sections as &$section ) { $section['tier'] = 'pro'; } unset( $section ); /** * Filter the style-customizer section metadata (order, titles, variant/state tabs, tier). * Pro/add-ons can add sections or adjust an existing section's tier here. * * @param array $sections Section definitions keyed by section id. */ return apply_filters( 'evf_style_sections', $sections ); } /** * The full, normalized, filterable token list. Free/Pro tiers and any add-on sections are * layered in via the `evf_style_schema` filter. * * @return array List of token definition arrays. */ public static function tokens() { if ( null === self::$tokens ) { /** * Filter the v2 style token registry. * * @param array $tokens Raw token definitions. * @param int $version Schema version. */ $tokens = apply_filters( 'evf_style_schema', self::raw_tokens(), self::VERSION ); $tokens = array_map( array( __CLASS__, 'normalize' ), $tokens ); self::$tokens = self::apply_tier_policy( $tokens ); } return self::$tokens; } /** * Stamp each token's tier: only the palette-driven tokens ({@see free_token_keys()}) are * tier=free, everything else (including Pro/add-on contributions) is tier=pro. The sanitizer * and compiler refuse to persist/emit a pro token unless {@see Engine::pro_active()}. * * @param array $tokens Normalized tokens. * @return array */ protected static function apply_tier_policy( $tokens ) { $free = self::free_token_keys(); foreach ( $tokens as &$token ) { $token['tier'] = isset( $free[ $token['key'] ] ) ? 'free' : 'pro'; } unset( $token ); return $tokens; } /** * Token keys that stay FREE — the palette-driven colours plus the derived button-hover shade. * * @return array Map of free token key => true. */ protected static function free_token_keys() { $keys = array(); foreach ( self::palette_map() as $token_keys ) { foreach ( (array) $token_keys as $key ) { $keys[ $key ] = true; } } $keys['btn.bgHover'] = true; /** * Filter the token keys that stay in the FREE tier (all others are Pro). * * @param array $keys Map of free token key => true. */ return apply_filters( 'evf_style_free_token_keys', $keys ); } /** * Look up a single token by key. * * @param string $key Token key (e.g. `wrap.pad`). * @return array|null */ public static function get( $key ) { if ( null === self::$index ) { self::$index = array(); foreach ( self::tokens() as $token ) { self::$index[ $token['key'] ] = $token; } } return isset( self::$index[ $key ] ) ? self::$index[ $key ] : null; } /** * key => default value, for building an unstyled (default) record and for * `array_replace_recursive`-style merges in the compiler. * * @return array */ public static function defaults() { $out = array(); foreach ( self::tokens() as $token ) { $out[ $token['key'] ] = $token['default']; } return $out; } /** * key => CSS custom property, for the compiler (skips meta-only tokens with no var). * * @return array */ public static function css_var_map() { $out = array(); foreach ( self::tokens() as $token ) { if ( ! empty( $token['var'] ) ) { $out[ $token['key'] ] = $token['var']; } } return $out; } /** * All 11 named colour palettes (2 free + 9 Pro), each with the old customizer's 6 colours. * The 9 Pro palettes' preview metadata ships here too (unlike other pro tokens) so the panel * can render a real swatch + name for all 11 and lock the Pro ones instead of hiding them; * applying one is still gated by {@see Sanitizer::sanitize_palette_id()}. * * @return array */ public static function palettes() { $palettes = array( array( 'id' => 'classic', 'name' => __( 'Classic', 'everest-forms' ), 'is_pro' => false, 'colors' => array( 'form_background' => '#ffffff', 'field_background' => '#f6f6f6', 'field_label' => '#081d2b', 'field_sublabel' => '#0f3a57', 'button_text' => '#ffffff', 'button_background' => '#3951a5' ) ), array( 'id' => 'mono', 'name' => __( 'Monochrome', 'everest-forms' ), 'is_pro' => false, 'colors' => array( 'form_background' => '#ffffff', 'field_background' => '#f7f7f7', 'field_label' => '#262626', 'field_sublabel' => '#666666', 'button_text' => '#ffffff', 'button_background' => '#1a1a1a' ) ), array( 'id' => 'autumn', 'name' => __( 'Autumn Blaze', 'everest-forms' ), 'is_pro' => true, 'colors' => array( 'form_background' => '#fffafa', 'field_background' => '#fff5f5', 'field_label' => '#330300', 'field_sublabel' => '#4d0500', 'button_text' => '#ffffff', 'button_background' => '#ff5d52' ) ), array( 'id' => 'sunset', 'name' => __( 'Sunset Glow', 'everest-forms' ), 'is_pro' => true, 'colors' => array( 'form_background' => '#fffdfa', 'field_background' => '#fff9f0', 'field_label' => '#664000', 'field_sublabel' => '#805100', 'button_text' => '#ffffff', 'button_background' => '#ffa305' ) ), array( 'id' => 'majestic', 'name' => __( 'Majestic', 'everest-forms' ), 'is_pro' => true, 'colors' => array( 'form_background' => '#fcfbfe', 'field_background' => '#f7f4fb', 'field_label' => '#3a225d', 'field_sublabel' => '#5d3795', 'button_text' => '#ffffff', 'button_background' => '#7545bb' ) ), array( 'id' => 'greenery', 'name' => __( 'Fresh Greenery', 'everest-forms' ), 'is_pro' => true, 'colors' => array( 'form_background' => '#f9fdf6', 'field_background' => '#e9f6ea', 'field_label' => '#334745', 'field_sublabel' => '#557773', 'button_text' => '#ffffff', 'button_background' => '#405956' ) ), array( 'id' => 'cloudy', 'name' => __( 'Cloudy Sky', 'everest-forms' ), 'is_pro' => true, 'colors' => array( 'form_background' => '#f2f3f8', 'field_background' => '#e4e7f1', 'field_label' => '#252b41', 'field_sublabel' => '#2e3651', 'button_text' => '#ffffff', 'button_background' => '#445079' ) ), array( 'id' => 'earthy', 'name' => __( 'Earthy Warm', 'everest-forms' ), 'is_pro' => true, 'colors' => array( 'form_background' => '#f7f6f0', 'field_background' => '#f1efe4', 'field_label' => '#474648', 'field_sublabel' => '#616062', 'button_text' => '#ffffff', 'button_background' => '#463700' ) ), array( 'id' => 'blossom', 'name' => __( 'Blushing Blossom', 'everest-forms' ), 'is_pro' => true, 'colors' => array( 'form_background' => '#fdf7fa', 'field_background' => '#fbeff5', 'field_label' => '#532f42', 'field_sublabel' => '#824a68', 'button_text' => '#ffffff', 'button_background' => '#46102c' ) ), array( 'id' => 'thunder', 'name' => __( 'Thunder', 'everest-forms' ), 'is_pro' => true, 'colors' => array( 'form_background' => '#ededed', 'field_background' => '#f7f7f7', 'field_label' => '#333333', 'field_sublabel' => '#595959', 'button_text' => '#ffffff', 'button_background' => '#1a1a1a' ) ), array( 'id' => 'midnight', 'name' => __( 'Midnight Charm', 'everest-forms' ), 'is_pro' => true, 'colors' => array( 'form_background' => '#363636', 'field_background' => '#3d3d3d', 'field_label' => '#ffffff', 'field_sublabel' => '#f2f2f2', 'button_text' => '#1a1a1a', 'button_background' => '#ffffff' ) ), ); /** * Filter the available colour palettes. * * @param array $palettes Palette definitions. */ return apply_filters( 'evf_style_palettes', $palettes ); } /** * Palette slot => token key(s) it drives. `button_background` also derives `btn.bgHover`. * * @return array */ public static function palette_map() { return array( 'form_background' => array( 'wrap.bg' ), 'field_background' => array( 'input.bg' ), 'field_label' => array( 'label.color', 'title.color' ), 'field_sublabel' => array( 'sub.color', 'desc.color' ), 'button_text' => array( 'btn.color' ), 'button_background' => array( 'btn.bg', 'input.focusBorder', 'choice.checked', 'file.icon' ), ); } /** * The free-tier token VALUES a registered palette id implies (the six palette-driven slots, * plus the derived hover shade) — the single source of truth {@see Sanitizer::sanitize_record()} * uses to DERIVE (never trust raw from the client) a non-Pro site's palette-driven tokens. * * Without this, a non-Pro site's "free" token keys only exist so the 2-swatch palette picker * has something to write to — the sanitizer only ever validated their VALUE TYPE (a well-formed * colour), not that the colour actually came from picking one of the 2 free palettes. A crafted * request (an AI response, a hand-built REST call) could set any hex it liked on these exact * keys and reconstruct a full Pro-palette look with no Pro required (see EVF-2708). * * @param string $palette_id Palette id (see {@see self::palettes()}). * @return array Token key => hex value (bare, not a device bag); empty if the id is unknown. */ public static function palette_token_values( $palette_id ) { foreach ( self::palettes() as $palette ) { if ( $palette['id'] !== $palette_id ) { continue; } $colors = isset( $palette['colors'] ) && is_array( $palette['colors'] ) ? $palette['colors'] : array(); $out = array(); foreach ( self::palette_map() as $slot => $keys ) { if ( ! isset( $colors[ $slot ] ) ) { continue; } foreach ( (array) $keys as $key ) { $out[ $key ] = $colors[ $slot ]; } } if ( ! empty( $colors['button_background'] ) ) { $out['btn.bgHover'] = self::mix_hex( $colors['button_background'], '#000000', 0.14 ); } return $out; } return array(); } /** * Linear-interpolate two hex colours — PHP port of `mixHex()` in constants.ts (kept local * rather than reusing {@see Migrator}'s copy, so this class has no cross-class coupling). * * @param string $a First hex colour (`#rgb` or `#rrggbb`). * @param string $b Second hex colour. * @param float $t Mix factor, 0 = $a, 1 = $b. * @return string `#rrggbb`. */ protected static function mix_hex( $a, $b, $t ) { $parse = static function ( $hex ) { $s = ltrim( (string) $hex, '#' ); if ( 3 === strlen( $s ) ) { $s = $s[0] . $s[0] . $s[1] . $s[1] . $s[2] . $s[2]; } return array( hexdec( substr( $s, 0, 2 ) ), hexdec( substr( $s, 2, 2 ) ), hexdec( substr( $s, 4, 2 ) ) ); }; $from = $parse( $a ); $to = $parse( $b ); $out = '#'; foreach ( array( 0, 1, 2 ) as $i ) { $out .= str_pad( dechex( (int) round( $from[ $i ] + ( $to[ $i ] - $from[ $i ] ) * $t ) ), 2, '0', STR_PAD_LEFT ); } return $out; } /* --------------------------------------------------------------------- * * Token assembly * --------------------------------------------------------------------- */ /** * Raw (un-normalized) token list, in panel display order. * * @return array */ protected static function raw_tokens() { $border = self::border_options(); $form = array( array( 'key' => 'fonts.theme', 'section' => 'form', 'group' => 'Typography', 'label' => __( 'Use theme fonts', 'everest-forms' ), 'type' => 'toggle', 'default' => false, 'hidden' => true, 'keywords' => array( 'inherit', 'typeface', 'theme font' ) ), array( 'key' => 'fonts.family', 'section' => 'form', 'group' => 'Typography', 'label' => __( 'Font family', 'everest-forms' ), 'type' => 'select', 'var' => '--evf-font', 'default' => '', 'source' => 'google_fonts', 'options' => self::font_options(), 'keywords' => array( 'typeface', 'typography', 'google font' ) ), // Palette-driven by default (form_background slot); editing it directly here unlinks // this form from the palette for just this token — see store.ts's paletteMap unlink. array( 'key' => 'wrap.bg', 'section' => 'form', 'group' => 'Background', 'label' => __( 'Background color', 'everest-forms' ), 'type' => 'color', 'var' => '--evf-wrap-bg', 'default' => '#ffffff', 'gradientable' => true, 'keywords' => array( 'fill', 'color', 'gradient' ) ), array( 'key' => 'wrap.bgImage', 'section' => 'form', 'group' => 'Background', 'label' => __( 'Background image', 'everest-forms' ), 'type' => 'media', 'var' => '--evf-wrap-image', 'default' => '', 'advanced' => true, 'keywords' => array( 'image', 'photo', 'picture' ) ), array( 'key' => 'wrap.bgPreset', 'section' => 'form', 'group' => 'Background', 'label' => __( 'Background preset', 'everest-forms' ), 'type' => 'select', 'default' => 'custom', 'options' => self::opts( array( 'custom' => __( 'Custom', 'everest-forms' ), 'fill' => __( 'Fill', 'everest-forms' ), 'fit' => __( 'Fit', 'everest-forms' ), 'repeat' => __( 'Repeat', 'everest-forms' ), 'center' => __( 'Center', 'everest-forms' ) ) ), 'advanced' => true, 'show_when_image' => true, 'keywords' => array( 'image', 'background' ) ), array( 'key' => 'wrap.bgSize', 'section' => 'form', 'group' => 'Background', 'label' => __( 'Image size', 'everest-forms' ), 'type' => 'select', 'var' => '--evf-wrap-bg-size', 'default' => 'cover', 'options' => self::opts( array( 'cover' => __( 'Cover', 'everest-forms' ), 'contain' => __( 'Contain', 'everest-forms' ), 'auto' => __( 'Auto', 'everest-forms' ) ) ), 'advanced' => true, 'show_when_image' => true, 'keywords' => array( 'image', 'background' ) ), array( 'key' => 'wrap.bgPosition', 'section' => 'form', 'group' => 'Background', 'label' => __( 'Image position', 'everest-forms' ), 'type' => 'select', 'var' => '--evf-wrap-bg-position', 'default' => 'center', 'options' => self::opts( array( 'center' => __( 'Center', 'everest-forms' ), 'top' => __( 'Top', 'everest-forms' ), 'bottom' => __( 'Bottom', 'everest-forms' ), 'left' => __( 'Left', 'everest-forms' ), 'right' => __( 'Right', 'everest-forms' ) ) ), 'advanced' => true, 'show_when_image' => true, 'keywords' => array( 'image', 'background' ) ), array( 'key' => 'wrap.bgRepeat', 'section' => 'form', 'group' => 'Background', 'label' => __( 'Repeat background image', 'everest-forms' ), 'type' => 'select', 'var' => '--evf-wrap-bg-repeat', 'default' => 'no-repeat', 'options' => self::opts( array( 'no-repeat' => __( 'No repeat', 'everest-forms' ), 'repeat' => __( 'Repeat', 'everest-forms' ), 'repeat-x' => __( 'Repeat X', 'everest-forms' ), 'repeat-y' => __( 'Repeat Y', 'everest-forms' ) ) ), 'advanced' => true, 'show_when_image' => true, 'keywords' => array( 'image', 'tile' ) ), array( 'key' => 'wrap.bgAttachment', 'section' => 'form', 'group' => 'Background', 'label' => __( 'Scroll with page', 'everest-forms' ), 'type' => 'select', 'var' => '--evf-wrap-bg-attachment', 'default' => 'scroll', 'options' => self::opts( array( 'scroll' => __( 'Scroll', 'everest-forms' ), 'fixed' => __( 'Fixed', 'everest-forms' ) ) ), 'advanced' => true, 'show_when_image' => true, 'keywords' => array( 'parallax', 'image' ) ), array( 'key' => 'wrap.bgOpacity', 'section' => 'form', 'group' => 'Background', 'label' => __( 'Image opacity', 'everest-forms' ), 'type' => 'slider', 'var' => '--evf-wrap-bg-opacity', 'default' => 1, 'min' => 0, 'max' => 1, 'step' => 0.1, 'unit' => '', 'advanced' => true, 'show_when_image' => true, 'keywords' => array( 'image', 'transparency' ) ), array( 'key' => 'wrap.width', 'section' => 'form', 'group' => 'Layout', 'label' => __( 'Width', 'everest-forms' ), 'type' => 'slider', 'var' => '--evf-wrap-width', 'default' => 100, 'min' => 40, 'max' => 100, 'step' => 1, 'unit' => '%', 'advanced' => true, 'keywords' => array( 'size', 'narrow' ) ), self::box( 'wrap.margin', __( 'Form margin', 'everest-forms' ), '--evf-wrap-margin', array( 0, 0, 0, 0 ), 'form', null, -100, 100 ), self::box( 'wrap.pad', __( 'Form padding', 'everest-forms' ), '--evf-wrap-pad', array( 32, 32, 32, 32 ), 'form', null, 0, 100 ), array( 'key' => 'wrap.borderStyle', 'section' => 'form', 'group' => 'Border', 'label' => __( 'Border type', 'everest-forms' ), 'type' => 'select', 'var' => '--evf-wrap-border-style', 'default' => 'solid', 'options' => $border, 'deps' => array( 'wrap.bw', 'wrap.borderC' ), 'advanced' => true, 'keywords' => array( 'outline' ) ), self::bwidth( 'wrap.bw', '--evf-wrap-bw', 1, 'form', null, 'Border', true ), array( 'key' => 'wrap.borderC', 'section' => 'form', 'group' => 'Border', 'label' => __( 'Border color', 'everest-forms' ), 'type' => 'color', 'var' => '--evf-wrap-border-c', 'default' => '#ececf1', 'advanced' => true ), self::radius( 'wrap.radius', '--evf-wrap-radius', 14, 'form', null, 'Border', false ), ); $fields = array( // Palette-driven by default (field_background slot); editing it directly here unlinks // this form from the palette for just this token — see store.ts's paletteMap unlink. array( 'key' => 'input.bg', 'section' => 'fields', 'group' => 'Surface', 'label' => __( 'Background', 'everest-forms' ), 'type' => 'color', 'var' => '--evf-input-bg', 'default' => '#ffffff', 'gradientable' => true, 'keywords' => array( 'fill', 'color', 'gradient' ) ), array( 'key' => 'input.borderC', 'section' => 'fields', 'group' => 'Surface', 'label' => __( 'Border color', 'everest-forms' ), 'type' => 'color', 'var' => '--evf-input-border-c', 'default' => '#969696' ), self::radius( 'input.radius', '--evf-input-radius', 3, 'fields', null, 'Surface', false ), array( 'key' => 'input.size', 'section' => 'fields', 'group' => 'Typography', 'label' => __( 'Font size', 'everest-forms' ), 'type' => 'slider', 'var' => '--evf-input-size', 'default' => 14, 'min' => 11, 'max' => 22, 'step' => 1, 'unit' => 'px', 'advanced' => true, 'keywords' => array( 'text' ) ), // Not gradientable: this rule's element already spends its one `background` on // input.bg — background-clip:text for the text colour would collide with it. array( 'key' => 'input.color', 'section' => 'fields', 'group' => 'Typography', 'label' => __( 'Text color', 'everest-forms' ), 'type' => 'color', 'var' => '--evf-input-color', 'default' => '#575757', 'advanced' => true ), array( 'key' => 'input.ph', 'section' => 'fields', 'group' => 'Typography', 'label' => __( 'Placeholder color', 'everest-forms' ), 'type' => 'color', 'var' => '--evf-input-ph', 'default' => '#c6ccd7', 'advanced' => true, 'keywords' => array( 'hint' ) ), self::fstyle( 'input.fstyle', 'input', 'fields', null, '400' ), self::letter_spacing( 'input.ls', '--evf-input-ls', 'fields', null ), self::talign( 'input.align', '--evf-input-align', 'fields', null ), array( 'key' => 'input.borderStyle', 'section' => 'fields', 'group' => 'Border', 'label' => __( 'Border type', 'everest-forms' ), 'type' => 'select', 'var' => '--evf-input-border-style', 'default' => 'solid', 'options' => $border, 'deps' => array( 'input.bw', 'input.borderC' ), 'advanced' => true ), self::bwidth( 'input.bw', '--evf-input-bw', 1, 'fields', null, 'Border', true ), array( 'key' => 'input.pad', 'section' => 'fields', 'group' => 'Spacing', 'label' => __( 'Field padding', 'everest-forms' ), 'type' => 'box4', 'var' => '--evf-input-pad', 'default' => array( 6, 12, 6, 12 ), 'responsive' => true, 'advanced' => true, 'keywords' => array( 'inner' ) ), array( 'key' => 'field.margin', 'section' => 'fields', 'group' => 'Spacing', 'label' => __( 'Field margin', 'everest-forms' ), 'type' => 'box4', 'var' => '--evf-field-margin', 'default' => array( 0, 0, 10, 0 ), 'responsive' => true, 'advanced' => true, 'keywords' => array( 'gap', 'between' ) ), array( 'key' => 'file.size', 'section' => 'fields', 'group' => 'Upload area', 'label' => __( 'Font size', 'everest-forms' ), 'type' => 'slider', 'var' => '--evf-file-size', 'default' => 14, 'min' => 10, 'max' => 22, 'step' => 1, 'unit' => 'px', 'advanced' => true, 'keywords' => array( 'upload', 'file' ) ), array( 'key' => 'file.color', 'section' => 'fields', 'group' => 'Upload area', 'label' => __( 'Font color', 'everest-forms' ), 'type' => 'color', 'var' => '--evf-file-color', 'default' => '#494d50', 'advanced' => true, 'gradientable' => true, 'keywords' => array( 'upload', 'file', 'text', 'gradient' ) ), array( 'key' => 'file.bg', 'section' => 'fields', 'group' => 'Upload area', 'label' => __( 'Background', 'everest-forms' ), 'type' => 'color', 'var' => '--evf-file-bg', 'default' => 'rgba(255,255,255,0.99)', 'advanced' => true, 'gradientable' => true, 'keywords' => array( 'upload', 'dropzone', 'file', 'gradient' ) ), array( 'key' => 'file.iconBg', 'section' => 'fields', 'group' => 'Upload area', 'label' => __( 'Icon background', 'everest-forms' ), 'type' => 'color', 'var' => '--evf-file-iconbg', 'default' => 'rgba(255,255,255,0.99)', 'advanced' => true, 'gradientable' => true, 'keywords' => array( 'upload', 'file', 'gradient' ) ), array( 'key' => 'file.icon', 'section' => 'fields', 'group' => 'Upload area', 'label' => __( 'Icon color', 'everest-forms' ), 'type' => 'color', 'var' => '--evf-file-icon', 'default' => '#494d50', 'advanced' => true, 'keywords' => array( 'upload', 'file' ) ), array( 'key' => 'file.borderStyle', 'section' => 'fields', 'group' => 'Upload area', 'label' => __( 'Border type', 'everest-forms' ), 'type' => 'select', 'var' => '--evf-file-border-style', 'default' => 'dashed', 'options' => $border, 'advanced' => true, 'keywords' => array( 'upload', 'file' ) ), self::bwidth( 'file.bw', '--evf-file-bw', 1, 'fields', null, 'Upload area', true ), array( 'key' => 'file.border', 'section' => 'fields', 'group' => 'Upload area', 'label' => __( 'Border color', 'everest-forms' ), 'type' => 'color', 'var' => '--evf-file-border', 'default' => '#8e98a2', 'advanced' => true, 'keywords' => array( 'upload', 'file' ) ), self::radius( 'file.radius', '--evf-file-radius', 0, 'fields', null, 'Upload area', true ), array( 'key' => 'file.margin', 'section' => 'fields', 'group' => 'Upload area', 'label' => __( 'Margin', 'everest-forms' ), 'type' => 'box4', 'var' => '--evf-file-margin', 'default' => array( 0, 0, 10, 0 ), 'responsive' => true, 'advanced' => true, 'keywords' => array( 'upload', 'file', 'spacing' ) ), array( 'key' => 'file.pad', 'section' => 'fields', 'group' => 'Upload area', 'label' => __( 'Padding', 'everest-forms' ), 'type' => 'box4', 'var' => '--evf-file-pad', 'default' => array( 6, 12, 6, 12 ), 'responsive' => true, 'advanced' => true, 'keywords' => array( 'upload', 'file', 'spacing', 'inner' ) ), // Applies only when no palette is set — a palette still derives this per palette_map(). array( 'key' => 'input.focusBorder', 'section' => 'fields', 'group' => '', 'label' => __( 'Focus border color', 'everest-forms' ), 'type' => 'color', 'var' => '--evf-input-focus-border', 'default' => '#7ca8eb', 'state' => 'focus', 'keywords' => array( 'outline', 'ring', 'active' ) ), ); $choice = array( array( 'key' => 'choice.checked', 'section' => 'choice', 'group' => '', 'label' => __( 'Selected color', 'everest-forms' ), 'type' => 'color', 'var' => '--evf-choice-checked', 'default' => '#575757', 'keywords' => array( 'checked', 'radio', 'checkbox', 'rating', 'scale', 'likert', 'payment', 'yes no', 'image choice', 'star' ) ), array( 'key' => 'choice.size', 'section' => 'choice', 'group' => '', 'label' => __( 'Mark size', 'everest-forms' ), 'type' => 'slider', 'var' => '--evf-choice-size', 'default' => 16, 'min' => 12, 'max' => 28, 'step' => 1, 'unit' => 'px', 'keywords' => array( 'radio', 'checkbox' ) ), array( 'key' => 'choice.border', 'section' => 'choice', 'group' => '', 'label' => __( 'Unselected border', 'everest-forms' ), 'type' => 'color', 'var' => '--evf-choice-border', 'default' => '#d8dae2', 'keywords' => array( 'radio', 'checkbox', 'unchecked' ) ), array( 'key' => 'choice.variation', 'section' => 'choice', 'group' => '', 'label' => __( 'Style variation', 'everest-forms' ), 'type' => 'select', 'default' => 'default', 'options' => self::opts( array( 'default' => __( 'Default', 'everest-forms' ), 'filled' => __( 'Filled', 'everest-forms' ), 'outline' => __( 'Outline', 'everest-forms' ) ) ), 'advanced' => true, 'special' => 'cvar', 'keywords' => array( 'modern', 'inline' ) ), array( 'key' => 'choice.fsize', 'section' => 'choice', 'group' => '', 'label' => __( 'Option font size', 'everest-forms' ), 'type' => 'slider', 'var' => '--evf-choice-fsize', 'default' => 14, 'min' => 11, 'max' => 22, 'step' => 1, 'unit' => 'px', 'advanced' => true, 'keywords' => array( 'text' ) ), array( 'key' => 'choice.color', 'section' => 'choice', 'group' => '', 'label' => __( 'Option text color', 'everest-forms' ), 'type' => 'color', 'var' => '--evf-choice-color', 'default' => '#575757', 'advanced' => true, 'gradientable' => true, 'keywords' => array( 'text', 'gradient' ) ), self::fstyle( 'choice.fstyle', 'choice', 'choice', null, '400' ), self::letter_spacing( 'choice.ls', '--evf-choice-ls', 'choice', null ), self::talign( 'choice.align', '--evf-choice-align', 'choice', null ), self::box( 'choice.margin', __( 'Margin', 'everest-forms' ), '--evf-choice-margin', array( 0, 0, 5, 0 ), 'choice', null ), ); $button = array( // Palette-driven by default (button_background/button_text slots); editing either // directly here unlinks this form from the palette for just that one token. array( 'key' => 'btn.bg', 'section' => 'button', 'group' => '', 'label' => __( 'Button color', 'everest-forms' ), 'type' => 'color', 'var' => '--evf-btn-bg', 'default' => '#0073aa', 'gradientable' => true, 'keywords' => array( 'submit', 'background', 'color', 'gradient' ) ), // Not gradientable: shares its rule's `background` with btn.bg (see input.color). array( 'key' => 'btn.color', 'section' => 'button', 'group' => '', 'label' => __( 'Text color', 'everest-forms' ), 'type' => 'color', 'var' => '--evf-btn-color', 'default' => '#ffffff', 'keywords' => array( 'submit', 'color' ) ), self::radius( 'btn.radius', '--evf-btn-radius', 3, 'button', null, '', false ), // No 'var': like choice.variation, this drives a wrapper class (`evf-btn-width-fill`) // instead of a CSS variable — see FrontendEnqueue::container_class() (real frontend) // and PreviewBridge.ts (live preview). New property, no v1 equivalent, so old records // simply get the default ('fit') — today's exact behavior — no migration needed. array( 'key' => 'btn.widthMode', 'section' => 'button', 'group' => '', 'label' => __( 'Button width', 'everest-forms' ), 'type' => 'select', 'default' => 'fit', 'options' => self::opts( array( 'fit' => __( 'Fit content', 'everest-forms' ), 'fill' => __( 'Fill width', 'everest-forms' ) ) ), 'keywords' => array( 'full width', 'block', 'stretch', 'size' ) ), array( 'key' => 'btn.size', 'section' => 'button', 'group' => '', 'label' => __( 'Font size', 'everest-forms' ), 'type' => 'slider', 'var' => '--evf-btn-size', 'default' => 14, 'min' => 11, 'max' => 24, 'step' => 1, 'unit' => 'px', 'advanced' => true, 'keywords' => array( 'submit' ) ), self::fstyle( 'btn.fstyle', 'btn', 'button', null, '400' ), self::letter_spacing( 'btn.ls', '--evf-btn-ls', 'button', null ), // 'left', not 'center': matches both v1's own implicit default and the built-in // "Default" template's explicit value — a legacy form that never touched button // alignment has no token for it at all (see Migrator), so this default IS its // rendered alignment; 'center' silently flipped it on migration (EVF-2732). self::talign( 'btn.align', '--evf-btn-align', 'button', null, 'left' ), self::line( 'btn.line', '--evf-btn-lh', 1.5, 'button', null ), self::box( 'btn.margin', __( 'Margin', 'everest-forms' ), '--evf-btn-margin', array( 0, 0, 0, 0 ), 'button', null ), array( 'key' => 'btn.pad', 'section' => 'button', 'group' => '', 'label' => __( 'Padding', 'everest-forms' ), 'type' => 'box4', 'var' => '--evf-btn-pad', 'default' => array( 10, 15, 10, 15 ), 'responsive' => true, 'advanced' => true, 'keywords' => array( 'size' ) ), array( 'key' => 'btn.borderStyle', 'section' => 'button', 'group' => '', 'label' => __( 'Border type', 'everest-forms' ), 'type' => 'select', 'var' => '--evf-btn-border-style', 'default' => 'solid', 'options' => $border, 'deps' => array( 'btn.bw', 'btn.borderC' ), 'advanced' => true, 'keywords' => array( 'outline' ) ), self::bwidth( 'btn.bw', '--evf-btn-bw', 1, 'button', null, '', true ), array( 'key' => 'btn.borderC', 'section' => 'button', 'group' => '', 'label' => __( 'Border color', 'everest-forms' ), 'type' => 'color', 'var' => '--evf-btn-border-c', 'default' => '#cccccc', 'advanced' => true ), // Palette-driven; the default only shows on a no-palette form. array( 'key' => 'btn.bgHover', 'section' => 'button', 'group' => '', 'label' => __( 'Button color', 'everest-forms' ), 'type' => 'color', 'var' => '--evf-btn-bg-hover', 'default' => '#eeeeee', 'state' => 'hover', 'gradientable' => true, 'keywords' => array( 'mouse over', 'gradient' ) ), array( 'key' => 'btn.colorHover', 'section' => 'button', 'group' => '', 'label' => __( 'Text color', 'everest-forms' ), 'type' => 'color', 'var' => '--evf-btn-color-hover', 'default' => '#23282d', 'state' => 'hover', 'keywords' => array( 'mouse over', 'hover font' ) ), array( 'key' => 'btn.borderCHover', 'section' => 'button', 'group' => '', 'label' => __( 'Border color', 'everest-forms' ), 'type' => 'color', 'var' => '--evf-btn-border-c-hover', 'default' => '#cccccc', 'state' => 'hover', 'keywords' => array( 'mouse over', 'hover border' ) ), ); // Messages (tier=pro) live in the Pro plugin, injected via `evf_style_schema` only when // Pro is active — a free schema physically has no pro tokens to sanitize or compile. return array_merge( $form, self::text_role_tokens(), $fields, $choice, $button ); } /** * The four Text roles (Label / Sublabel / Description / Section Title), 7 controls each. * Label & sublabel colour are palette-driven (hidden). Margin & padding are responsive. * * @return array */ protected static function text_role_tokens() { $roles = array( // state key => [ var-prefix, size, min, max, color, neutral-weight, margin, line, pad, color-hidden ]. // label/sublabel color: palette-driven by default (field_label/field_sublabel slots); // editing directly here unlinks this form from the palette for just that one token — // same as title/desc below, which have always worked this way. 'label' => array( 'sub' => 'label', 'size' => 14, 'min' => 10, 'max' => 24, 'color' => '#333333', 'nw' => '600', 'margin' => array( 0, 0, 10, 0 ), 'line' => 1.7, 'pad' => array( 0, 0, 0, 0 ), 'color_hidden' => false ), 'sublabel' => array( 'sub' => 'sub', 'size' => 12, 'min' => 9, 'max' => 20, 'color' => '#666666', 'nw' => '400', 'margin' => array( 0, 0, 10, 0 ), 'line' => 1.5, 'pad' => array( 0, 0, 0, 0 ), 'color_hidden' => false ), 'desc' => array( 'sub' => 'desc', 'size' => 14, 'min' => 9, 'max' => 20, 'color' => '#575757', 'nw' => '400', 'margin' => array( 0, 0, 10, 0 ), 'line' => 1.7, 'pad' => array( 0, 0, 0, 0 ), 'color_hidden' => false ), 'title' => array( 'sub' => 'title', 'size' => 16, 'min' => 12, 'max' => 34, 'color' => '#575757', 'nw' => '700', 'margin' => array( 25, 0, 25, 0 ), 'line' => 1.5, 'pad' => array( 0, 0, 0, 0 ), 'color_hidden' => false ), ); $out = array(); foreach ( $roles as $state => $r ) { $vp = $r['sub']; $out[] = array( 'key' => "{$vp}.size", 'section' => 'text', 'state' => $state, 'group' => '', 'label' => __( 'Font size', 'everest-forms' ), 'type' => 'slider', 'var' => "--evf-{$vp}-size", 'default' => $r['size'], 'min' => $r['min'], 'max' => $r['max'], 'step' => 1, 'unit' => 'px', 'keywords' => array( 'text', $state ) ); $out[] = array( 'key' => "{$vp}.color", 'section' => 'text', 'state' => $state, 'group' => '', 'label' => __( 'Color', 'everest-forms' ), 'type' => 'color', 'var' => "--evf-{$vp}-color", 'default' => $r['color'], 'hidden' => $r['color_hidden'], 'gradientable' => true, 'keywords' => array( 'text', $state, 'gradient' ) ); $out[] = self::fstyle( "{$vp}.fstyle", $vp, 'text', $state, $r['nw'] ); $out[] = self::letter_spacing( "{$vp}.ls", "--evf-{$vp}-ls", 'text', $state ); $out[] = self::talign( "{$vp}.align", "--evf-{$vp}-align", 'text', $state ); $out[] = self::line( "{$vp}.line", "--evf-{$vp}-lh", $r['line'], 'text', $state ); $out[] = self::box( "{$vp}.margin", __( 'Margin', 'everest-forms' ), "--evf-{$vp}-margin", $r['margin'], 'text', $state ); $out[] = self::box( "{$vp}.pad", __( 'Padding', 'everest-forms' ), "--evf-{$vp}-pad", $r['pad'], 'text', $state ); } return $out; } /* --------------------------------------------------------------------- * * Token builders (mixins) — shared shape factory, also used by Pro/add-ons * contributing tier=pro tokens through the `evf_style_schema` filter. * --------------------------------------------------------------------- */ /** * Font-style control (Bold / Italic / Underline / Uppercase, plus an optional Font weight). * * `weight` (an explicit numeric weight, e.g. '600') is an ADDITIVE sub-property alongside the * original four flags — old stored values simply don't have this key, `!empty($v['weight'])` * is false for them, and font-weight keeps deriving from `bold`/`neutral_weight` exactly as * before (see Compiler::declarations() and cssVars.ts's tokenDeclarations()). No migration * step needed: a missing key is indistinguishable from '' (the "Auto" default here), by design. * * @param string $key Token key. * @param string $base CSS var base (e.g. `label`, `input`, `msg-success`). * @param string $section Section key. * @param string $state Variant/state (nullable). * @param string $nw Neutral (non-bold) font-weight. * @return array */ public static function fstyle( $key, $base, $section, $state, $nw ) { return array( 'key' => $key, 'section' => $section, 'state' => $state, 'group' => '', 'label' => __( 'Font style', 'everest-forms' ), 'type' => 'fontstyle', 'vars' => array( 'weight' => "--evf-{$base}-weight", 'style' => "--evf-{$base}-fs", 'decoration' => "--evf-{$base}-td", 'transform' => "--evf-{$base}-tt", ), 'neutral_weight' => $nw, 'weight_options' => self::weight_options(), 'default' => array( 'bold' => false, 'italic' => false, 'underline' => false, 'uppercase' => false, 'weight' => '', ), 'advanced' => true, 'keywords' => array( 'bold', 'italic', 'underline', 'uppercase', 'font style', 'font weight', 'weight' ), ); } /** * Selectable font-weight values for the `fstyle` control's Font weight dropdown. * '' ("Auto") keeps the original Bold-toggle-only behavior. * * @return array value => label. */ protected static function weight_options() { // EVF-2721: this used to offer all 9 standard CSS weight steps (100-900), but a form's // font — a specific Google Font, or "Theme default" falling back to the visitor's system // font stack — only actually HAS a handful of real weight faces. A numeric weight with no // matching face just gets browser-synthesized (inconsistently, sometimes identically to a // neighboring value), so half the dropdown silently did nothing. Trimmed to the 3 steps // virtually every font (Google or system) truly ships, and {@see evfsc_enqueue_fonts}/ // PreviewBridge now request exactly these weights so a chosen Google Font's real files // load instead of relying on synthesis. // // Built directly (not via self::opts(), which takes an associative array — PHP would // coerce these numeric-string keys to ints) so every value is guaranteed a string. return array( array( 'value' => '', 'label' => __( 'Auto', 'everest-forms' ) ), array( 'value' => '300', 'label' => __( 'Light', 'everest-forms' ) ), array( 'value' => '400', 'label' => __( 'Regular', 'everest-forms' ) ), array( 'value' => '700', 'label' => __( 'Bold', 'everest-forms' ) ), ); } /** * Line-height slider (1–3, single value). * * @param string $key Token key. * @param string $var CSS var. * @param float $default Default line-height. * @param string $section Section key. * @param string $state Variant/state (nullable). * @return array */ protected static function line( $key, $var, $default, $section, $state ) { return array( 'key' => $key, 'section' => $section, 'state' => $state, 'group' => '', 'label' => __( 'Line height', 'everest-forms' ), 'type' => 'slider', 'var' => $var, 'default' => $default, 'min' => 1, 'max' => 3, 'step' => 0.1, 'unit' => '', 'advanced' => true, 'keywords' => array( 'leading', 'line height' ), ); } /** * Letter-spacing slider (-2px to 10px, single value). New, additive sibling of `line()` — * every caller of `self::fstyle()` gets one alongside it, so it's added wherever font-weight * was. No v1 equivalent ever existed, so a missing value here just means "not customized yet" * — the schema default (0, i.e. `normal`) — same as any other brand-new token; no migration * step needed. * * @param string $key Token key. * @param string $var CSS var. * @param string $section Section key. * @param string $state Variant/state (nullable). * @return array */ protected static function letter_spacing( $key, $var, $section, $state ) { return array( 'key' => $key, 'section' => $section, 'state' => $state, 'group' => '', 'label' => __( 'Letter spacing', 'everest-forms' ), 'type' => 'slider', 'var' => $var, 'default' => 0, 'min' => -2, 'max' => 10, 'step' => 0.1, 'unit' => 'px', 'advanced' => true, 'keywords' => array( 'tracking', 'letter spacing', 'kerning' ), ); } /** * Alignment control (left/center/right). Single value — never responsive. * * @param string $key Token key. * @param string $var CSS var. * @param string $section Section key. * @param string $state Variant/state (nullable). * @param string $default Default alignment. * @return array */ public static function talign( $key, $var, $section, $state, $default = 'left' ) { return array( 'key' => $key, 'section' => $section, 'state' => $state, 'group' => '', 'label' => __( 'Alignment', 'everest-forms' ), 'type' => 'align', 'var' => $var, 'default' => $default, 'advanced' => true, 'keywords' => array( 'left', 'center', 'right', 'alignment' ), ); } /** * Margin / padding box (4-side). THE responsive control type — Desktop/Tablet/Mobile. * * @param string $key Token key. * @param string $label Control label. * @param string $var CSS var. * @param array $default [top,right,bottom,left]. * @param string $section Section key. * @param string $state Variant/state (nullable). * @param int $min Optional per-side lower bound (null = type default). * @param int $max Optional per-side upper bound (null = type default). * @return array */ protected static function box( $key, $label, $var, $default, $section, $state, $min = null, $max = null ) { $out = array( 'key' => $key, 'section' => $section, 'state' => $state, 'group' => '', 'label' => $label, 'type' => 'box4', 'var' => $var, 'default' => $default, 'responsive' => true, 'advanced' => true, 'keywords' => array( 'spacing' ), ); if ( null !== $min ) { $out['min'] = $min; } if ( null !== $max ) { $out['max'] = $max; } return $out; } /** * Border-width box (4-side, single value — border width was never responsive). * * @param string $key Token key. * @param string $var CSS var. * @param int $d Default (applied to all four sides). * @param string $section Section key. * @param string $state Variant/state (nullable). * @param string $group Group label. * @param bool $advanced Whether behind the "advanced" reveal. * @return array */ public static function bwidth( $key, $var, $d, $section, $state, $group, $advanced = true ) { return array( 'key' => $key, 'section' => $section, 'state' => $state, 'group' => $group, 'label' => __( 'Border width', 'everest-forms' ), 'type' => 'box4', 'var' => $var, 'default' => array( $d, $d, $d, $d ), 'max' => 12, 'advanced' => $advanced, 'keywords' => array( 'thickness', 'border' ), ); } /** * Border-radius box (4-corner + px/% unit, single value). * * @param string $key Token key. * @param string $var CSS var. * @param int $d Default (applied to all four corners). * @param string $section Section key. * @param string $state Variant/state (nullable). * @param string $group Group label. * @param bool $advanced Whether behind the "advanced" reveal. * @return array */ public static function radius( $key, $var, $d, $section, $state, $group, $advanced ) { return array( 'key' => $key, 'section' => $section, 'state' => $state, 'group' => $group, 'label' => __( 'Border radius', 'everest-forms' ), 'type' => 'box4', 'var' => $var, 'default' => array( $d, $d, $d, $d ), 'max' => 60, 'units' => array( 'px', '%' ), 'corners' => true, 'advanced' => $advanced, 'keywords' => array( 'corner', 'rounded' ), ); } /* --------------------------------------------------------------------- * * Helpers * --------------------------------------------------------------------- */ /** * Shared border-type options — the full set the legacy customizer offered. * * @return array */ public static function border_options() { return self::opts( array( 'none' => __( 'None', 'everest-forms' ), 'hidden' => __( 'Hidden', 'everest-forms' ), 'dotted' => __( 'Dotted', 'everest-forms' ), 'dashed' => __( 'Dashed', 'everest-forms' ), 'solid' => __( 'Solid', 'everest-forms' ), 'double' => __( 'Double', 'everest-forms' ), 'groove' => __( 'Groove', 'everest-forms' ), 'ridge' => __( 'Ridge', 'everest-forms' ), 'inset' => __( 'Inset', 'everest-forms' ), 'outset' => __( 'Outset', 'everest-forms' ), 'initial' => __( 'Initial', 'everest-forms' ), 'inherit' => __( 'Inherit', 'everest-forms' ), ) ); } /** * Font-family options. Only "Theme default" is baked in; the full Google Fonts list is * supplied to the panel at runtime via the REST payload's `google_fonts`. * * @return array */ protected static function font_options() { return self::opts( array( '' => __( 'Theme default', 'everest-forms' ), ) ); } /** * Convert a value=>label map into the panel's [{value,label}] option shape. * * @param array $map value => label. * @return array */ protected static function opts( $map ) { $out = array(); foreach ( $map as $value => $label ) { $out[] = array( 'value' => $value, 'label' => $label, ); } return $out; } /** * Fill every token with its default keys so consumers can rely on the full shape. * * @param array $token Raw token. * @return array */ protected static function normalize( $token ) { $token = array_merge( array( 'key' => '', 'section' => '', 'group' => '', 'label' => '', 'type' => '', 'var' => null, 'default' => null, 'state' => null, 'responsive' => false, 'advanced' => false, 'hidden' => false, 'tier' => 'free', 'keywords' => array(), ), $token ); // Box defaults are authored compactly as [t,r,b,l]; store/emit them as the associative // {top,right,bottom,left} shape the legacy engine uses, so migration is an identity copy. if ( 'box4' === $token['type'] && is_array( $token['default'] ) && isset( $token['default'][0] ) ) { $token['default'] = self::to_dim( $token['default'], ! empty( $token['units'] ) ? $token['units'][0] : null ); } return $token; } /** * [t,r,b,l] → { top, right, bottom, left [, unit ] }. * * @param array $box Indexed sides. * @param string|null $unit Optional unit (radius). * @return array */ protected static function to_dim( $box, $unit = null ) { $dim = array( 'top' => isset( $box[0] ) ? $box[0] : 0, 'right' => isset( $box[1] ) ? $box[1] : 0, 'bottom' => isset( $box[2] ) ? $box[2] : 0, 'left' => isset( $box[3] ) ? $box[3] : 0, ); if ( null !== $unit ) { $dim['unit'] = $unit; } return $dim; } }