*/ private const RADIUS_PX = [ 'var(--r-sm)' => '6px', 'var(--r-md)' => '10px', 'var(--r-lg)' => '16px', 'var(--r-full)' => '9999px', ]; /** * Working lime. Brand preset must not inherit admin-shell --bl-accent. */ private const BRAND_HEX = '#c4ce00'; /** * Build CSS custom properties and data attributes for a form theme. * * @param array $theme Theme knobs from BookingForm::theme(). * @return array{properties: array, attributes: array} */ public static function declarations( array $theme ): array { $theme = array_merge( BookingForm::default_theme(), $theme ); $properties = []; $attributes = []; $accent = $theme['accent']; if ( self::is_hex( $accent ) || self::is_css_var( $accent ) ) { $resolved = self::resolve_accent( $accent ); } else { $resolved = self::BRAND_HEX; } $properties['--bl-accent'] = $resolved; $properties['--accent-500'] = 'var(--bl-accent)'; $properties['--color-primary'] = 'var(--bl-accent)'; $properties['--accent-50'] = 'color-mix(in srgb, var(--bl-accent) 12%, var(--paper-0))'; $properties['--accent-300'] = 'color-mix(in srgb, var(--bl-accent) 48%, var(--paper-0))'; $properties['--accent-700'] = 'color-mix(in srgb, var(--bl-accent) 78%, var(--ink-900))'; $properties['--on-accent'] = self::is_hex( $resolved ) ? self::on_accent_for_hex( $resolved ) : '#0e0d0b'; $radius = $theme['radius']; $properties['--bl-radius'] = isset( self::RADIUS_PX[ $radius ] ) ? self::RADIUS_PX[ $radius ] : '10px'; $font = $theme['font']; if ( 'var(--font-mono)' === $font ) { $properties['--bl-font'] = 'var(--font-mono)'; $properties['--font-sans'] = 'var(--font-mono)'; $properties['--font-display'] = 'var(--font-mono)'; } else { $properties['--bl-font'] = 'var(--font-sans)'; } $density = $theme['density']; if ( 'compact' === $density || 'comfortable' === $density ) { $attributes['data-bl-density'] = $density; } $scheme = $theme['scheme']; if ( in_array( $scheme, [ 'light', 'dark', 'auto' ], true ) ) { $attributes['data-bl-scheme'] = $scheme; if ( 'dark' === $scheme ) { $attributes['data-scheme'] = 'dark'; } elseif ( 'light' === $scheme ) { $attributes['data-scheme'] = 'light'; } } $slot = $theme['slot_style']; if ( 'pill' === $slot || 'bordered' === $slot ) { $attributes['data-bl-slot-style'] = $slot; } return [ 'properties' => $properties, 'attributes' => $attributes, ]; } /** * Inline style attribute value (no wrapping quotes). * * @param array $theme Theme knobs. */ public static function style_attribute( array $theme ): string { $properties = self::declarations( $theme )['properties']; $parts = []; foreach ( $properties as $name => $value ) { $parts[] = $name . ': ' . $value; } return implode( '; ', $parts ); } /** * Attribute map including `style` when properties exist. * * @param array $theme Theme knobs. * @return array */ public static function html_attributes( array $theme ): array { $declared = self::declarations( $theme ); $out = $declared['attributes']; $style = self::style_attribute( $theme ); if ( '' !== $style ) { $out['style'] = $style; } return $out; } /** * Whether the value is a 3- or 6-digit hex colour. * * @param string $value Raw accent. */ private static function is_hex( string $value ): bool { return 1 === preg_match( '/^#[0-9A-Fa-f]{3}$/', $value ) || 1 === preg_match( '/^#[0-9A-Fa-f]{6}$/', $value ); } /** * Whether the value is a single CSS custom-property reference. * * @param string $value Raw token. */ private static function is_css_var( string $value ): bool { return 1 === preg_match( '/^var\(--[a-z0-9-]+\)$/', $value ); } /** * Map a stored accent token to a colour that does not depend on wp-admin. * * @param string $accent Raw accent from the form theme. */ private static function resolve_accent( string $accent ): string { if ( self::is_hex( $accent ) ) { return strtolower( $accent ); } $map = [ 'var(--bl-accent)' => self::BRAND_HEX, 'var(--bl-brand)' => self::BRAND_HEX, 'var(--ink-900)' => '#0e0d0b', 'var(--free)' => '#0e7c66', 'var(--held)' => '#b26b00', 'var(--accent-300)' => 'color-mix(in srgb, #c4ce00 42%, #fafaf8)', ]; return $map[ $accent ] ?? self::BRAND_HEX; } /** * Charcoal on light fills, light stone on dark fills. * * @param string $hex Accent including #. */ private static function on_accent_for_hex( string $hex ): string { return self::relative_luminance( $hex ) >= 0.45 ? '#0e0d0b' : '#f2f1ee'; } /** * WCAG relative luminance. * * @param string $hex Accent including #. */ private static function relative_luminance( string $hex ): float { $raw = ltrim( $hex, '#' ); if ( 3 === strlen( $raw ) ) { $raw = $raw[0] . $raw[0] . $raw[1] . $raw[1] . $raw[2] . $raw[2]; } if ( 6 !== strlen( $raw ) ) { return 1.0; } $r = self::linear_channel( (int) hexdec( substr( $raw, 0, 2 ) ) ); $g = self::linear_channel( (int) hexdec( substr( $raw, 2, 2 ) ) ); $b = self::linear_channel( (int) hexdec( substr( $raw, 4, 2 ) ) ); return 0.2126 * $r + 0.7152 * $g + 0.0722 * $b; } /** * Convert an sRGB channel (0โ€“255) to linear light. * * @param int $channel 0โ€“255. */ private static function linear_channel( int $channel ): float { $s = $channel / 255.0; return $s <= 0.04045 ? $s / 12.92 : pow( ( $s + 0.055 ) / 1.055, 2.4 ); } }