isset( $c['family'] ) && in_array( $c['family'], array( 'gradient', 'dots', 'grid', 'stripes' ), true ) ? $c['family'] : 'gradient', 'background' => sflo_background_color( $c['background'] ?? null, '#eee8ff' ), 'accent' => sflo_background_color( $c['accent'] ?? null, '#7255de' ), 'scale' => sflo_background_number( $c['scale'] ?? null, 32, 8, 120 ), 'angle' => sflo_background_number( $c['angle'] ?? null, 135, 0, 359 ), 'motion' => ( $c['motion'] ?? null ) === true, 'speed' => sflo_background_number( $c['speed'] ?? null, 16, 4, 40 ), ); } function sflo_background_declarations( $input ) { $c = sflo_background_normalize( $input ); if ( $c['family'] === 'dots' ) { $dot = (int) max( 1, min( 6, floor( $c['scale'] / 10 + 0.5 ) ) ); $image = 'radial-gradient(circle,' . $c['accent'] . ' ' . $dot . 'px,transparent ' . ( $dot + 0.5 ) . 'px)'; $size = $c['scale'] . 'px ' . $c['scale'] . 'px'; } elseif ( $c['family'] === 'grid' ) { $image = 'linear-gradient(' . $c['accent'] . ' 1px,transparent 1px),linear-gradient(90deg,' . $c['accent'] . ' 1px,transparent 1px)'; $size = $c['scale'] . 'px ' . $c['scale'] . 'px'; } elseif ( $c['family'] === 'stripes' ) { $half = $c['scale'] / 2; $image = 'repeating-linear-gradient(' . $c['angle'] . 'deg,' . $c['background'] . ' 0px,' . $c['background'] . ' ' . $half . 'px,' . $c['accent'] . ' ' . $half . 'px,' . $c['accent'] . ' ' . $c['scale'] . 'px)'; $size = 'auto'; } else { $image = 'linear-gradient(' . $c['angle'] . 'deg,' . $c['background'] . ',' . $c['accent'] . ')'; $size = '200% 200%'; } return 'background-color:' . $c['background'] . ';background-image:' . $image . ';background-size:' . $size . ';background-position:0 0;'; } /** Same bounded ASCII hash as the JavaScript exporter; no user CSS is accepted. */ function sflo_background_hash( $value ) { $hash = 0; for ( $i = 0, $length = strlen( $value ); $i < $length; $i++ ) { $hash = ( $hash * 31 + ord( $value[$i] ) ) & 0xffffffff; } return base_convert( (string) $hash, 10, 36 ); } function sflo_background_stylesheet( $input, $requested_selector = '.sflo-background' ) { $c = sflo_background_normalize( $input ); $target = is_string( $requested_selector ) && strlen( $requested_selector ) <= 80 && preg_match( '/^\.[a-z][a-z0-9-]*$/iD', $requested_selector ) ? $requested_selector : '.sflo-background'; $css = $target . '{' . sflo_background_declarations( $c ) . '}'; if ( $c['motion'] ) { $name = 'sfloBackgroundMove-' . sflo_background_hash( json_encode( $c ) . $target ); $css .= '@keyframes ' . $name . '{from{background-position:0 0}to{background-position:' . ( $c['family'] === 'gradient' ? '100% 50%' : '64px 64px' ) . '}}' . $target . '{animation:' . $name . ' ' . $c['speed'] . 's ease-in-out infinite alternate}' . '@media(prefers-reduced-motion:reduce){' . $target . '{animation:none}}'; } return $css; } function sflo_background_ink( $hex ) { $channels = array(); foreach ( array( 1, 3, 5 ) as $offset ) { $v = hexdec( substr( $hex, $offset, 2 ) ) / 255; $channels[] = $v <= 0.04045 ? $v / 12.92 : pow( ( $v + 0.055 ) / 1.055, 2.4 ); } return $channels[0] * 0.2126 + $channels[1] * 0.7152 + $channels[2] * 0.0722 > 0.179 ? '#111820' : '#ffffff'; }