*/ public static function presets() { $presets = array( 'clean' => array( 'label' => __( 'Clean', 'fillaro' ), 'primary' => '#4f46e5', 'background' => '#ffffff', 'field' => '#ffffff', 'text' => '#111827', 'border' => '#d1d5db', 'radius' => 8, 'inputStyle' => 'outlined', 'buttonStyle' => 'solid', 'font' => 'inherit', 'container' => 'card', ), 'soft' => array( 'label' => __( 'Soft', 'fillaro' ), 'primary' => '#db2777', 'background' => '#fff1f5', 'field' => '#ffffff', 'text' => '#3f3f46', 'border' => '#fbcfe8', 'radius' => 16, 'inputStyle' => 'filled', 'buttonStyle' => 'pill', 'font' => 'rounded', 'container' => 'card', ), 'forest' => array( 'label' => __( 'Forest', 'fillaro' ), 'primary' => '#15803d', 'background' => '#f3faf5', 'field' => '#ffffff', 'text' => '#14281d', 'border' => '#bfdcc8', 'radius' => 10, 'inputStyle' => 'outlined', 'buttonStyle' => 'solid', 'font' => 'serif', 'container' => 'card', ), 'minimal' => array( 'label' => __( 'Minimal', 'fillaro' ), 'primary' => '#111111', 'background' => '#ffffff', 'field' => '#ffffff', 'text' => '#111111', 'border' => '#bdbdbd', 'radius' => 0, 'inputStyle' => 'underline', 'buttonStyle' => 'solid', 'font' => 'inherit', 'container' => 'plain', ), 'sunset' => array( 'label' => __( 'Sunset', 'fillaro' ), 'primary' => '#ea580c', 'background' => '#fff8f1', 'field' => '#fffdfa', 'text' => '#431407', 'border' => '#fed7aa', 'radius' => 12, 'inputStyle' => 'outlined', 'buttonStyle' => 'pill', 'font' => 'system', 'container' => 'card', ), 'midnight' => array( 'label' => __( 'Midnight', 'fillaro' ), 'primary' => '#38bdf8', 'background' => '#0f172a', 'field' => '#1e293b', 'text' => '#e2e8f0', 'border' => '#334155', 'radius' => 10, 'inputStyle' => 'filled', 'buttonStyle' => 'solid', 'font' => 'system', 'container' => 'card', ), ); /** * Filters the design presets. * * @param array $presets Presets keyed by id. */ return apply_filters( 'fillaro/theme_presets', $presets ); } /** * Default theme tokens. * * @return array */ public static function defaults() { $clean = self::presets()['clean']; unset( $clean['label'] ); return array_merge( array( 'preset' => 'clean', 'fontSize' => 16, 'spacing' => 'comfortable', 'maxWidth' => 640, ), $clean ); } /** * Sanitize theme tokens. * * @param mixed $raw Raw tokens. * @return array */ public static function sanitize( $raw ) { $raw = is_array( $raw ) ? $raw : array(); $d = self::defaults(); $pick = static function ( $key, array $allowed ) use ( $raw, $d ) { return ( isset( $raw[ $key ] ) && in_array( $raw[ $key ], $allowed, true ) ) ? $raw[ $key ] : $d[ $key ]; }; $color = static function ( $key ) use ( $raw, $d ) { $c = isset( $raw[ $key ] ) ? sanitize_hex_color( (string) $raw[ $key ] ) : ''; return $c ? $c : $d[ $key ]; }; $int = static function ( $key, $min, $max ) use ( $raw, $d ) { return isset( $raw[ $key ] ) && is_numeric( $raw[ $key ] ) ? max( $min, min( $max, (int) $raw[ $key ] ) ) : $d[ $key ]; }; return array( 'preset' => ( isset( $raw['preset'] ) && is_string( $raw['preset'] ) ) ? sanitize_key( $raw['preset'] ) : $d['preset'], 'primary' => $color( 'primary' ), 'background' => $color( 'background' ), 'field' => $color( 'field' ), 'text' => $color( 'text' ), 'border' => $color( 'border' ), 'radius' => $int( 'radius', 0, 32 ), 'fontSize' => $int( 'fontSize', 13, 22 ), 'maxWidth' => $int( 'maxWidth', 320, 1200 ), 'spacing' => $pick( 'spacing', array( 'compact', 'comfortable', 'spacious' ) ), 'inputStyle' => $pick( 'inputStyle', array( 'outlined', 'filled', 'underline' ) ), 'buttonStyle' => $pick( 'buttonStyle', array( 'solid', 'outline', 'pill' ) ), 'font' => $pick( 'font', array( 'inherit', 'system', 'serif', 'rounded', 'mono' ) ), 'container' => $pick( 'container', array( 'card', 'plain' ) ), ); } /** * CSS custom properties for a theme. * * @param array $theme Clean theme. * @return string Inline style value. */ public static function to_style( array $theme ) { $fonts = array( 'inherit' => 'inherit', 'system' => 'system-ui, -apple-system, "Segoe UI", Roboto, sans-serif', 'serif' => 'Georgia, "Iowan Old Style", "Times New Roman", serif', 'rounded' => 'ui-rounded, "SF Pro Rounded", "Nunito", system-ui, sans-serif', 'mono' => 'ui-monospace, "SF Mono", Menlo, Consolas, monospace', ); $gaps = array( 'compact' => '14px', 'comfortable' => '20px', 'spacious' => '28px', ); $vars = array( '--pf-primary' => $theme['primary'], '--pf-on-primary' => self::readable_on( $theme['primary'] ), '--pf-bg' => $theme['background'], '--pf-field' => $theme['field'], '--pf-text' => $theme['text'], '--pf-border' => $theme['border'], '--pf-radius' => $theme['radius'] . 'px', '--pf-font' => $fonts[ $theme['font'] ], '--pf-font-size' => $theme['fontSize'] . 'px', '--pf-gap' => $gaps[ $theme['spacing'] ], '--pf-max-width' => $theme['maxWidth'] . 'px', ); $style = ''; foreach ( $vars as $name => $value ) { $style .= $name . ':' . $value . ';'; } return $style; } /** * Black or white, whichever reads better on a background color. * * @param string $hex Hex color. * @return string */ public static function readable_on( $hex ) { $hex = ltrim( $hex, '#' ); if ( 3 === strlen( $hex ) ) { $hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2]; } $channels = array(); foreach ( array( 0, 2, 4 ) as $offset ) { $c = hexdec( substr( $hex, $offset, 2 ) ) / 255; $channels[] = $c <= 0.03928 ? $c / 12.92 : pow( ( $c + 0.055 ) / 1.055, 2.4 ); } $lum = 0.2126 * $channels[0] + 0.7152 * $channels[1] + 0.0722 * $channels[2]; // White text reads well on saturated brand colors; switch to dark text // only when white falls below 3:1 (the WCAG ratio for bold UI text). return ( 1.05 / ( $lum + 0.05 ) ) >= 3 ? '#ffffff' : '#111111'; } }