307 lines
9.7 KiB
PHP
307 lines
9.7 KiB
PHP
<?php
|
|
/**
|
|
* Theme Generator - Builds and caches a CSS file from saved design tokens.
|
|
*
|
|
* Reads theme-mods written by the admin settings page and produces a
|
|
* static CSS file in the uploads directory. The file is enqueued after
|
|
* main.css so that custom values override defaults.
|
|
*
|
|
* @package OTS_Theme
|
|
*/
|
|
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* Return the absolute filesystem path of the generated CSS file.
|
|
*
|
|
* Multi-site aware - each site gets its own file.
|
|
*
|
|
* @return string
|
|
*/
|
|
function oribi_generated_css_path() {
|
|
$upload_dir = wp_upload_dir();
|
|
$blog_id = get_current_blog_id();
|
|
return trailingslashit( $upload_dir['basedir'] ) . "oribi-theme-{$blog_id}-custom.css";
|
|
}
|
|
|
|
/**
|
|
* Return the public URL of the generated CSS file.
|
|
*
|
|
* @return string
|
|
*/
|
|
function oribi_generated_css_url() {
|
|
$upload_dir = wp_upload_dir();
|
|
$blog_id = get_current_blog_id();
|
|
return trailingslashit( $upload_dir['baseurl'] ) . "oribi-theme-{$blog_id}-custom.css";
|
|
}
|
|
|
|
/**
|
|
* Helper: convert a hex colour like #004225 to its "r,g,b" string.
|
|
*
|
|
* @param string $hex Hex colour with or without leading #.
|
|
* @return string e.g. "0,66,37"
|
|
*/
|
|
function oribi_hex_to_rgb( $hex ) {
|
|
$hex = ltrim( $hex, '#' );
|
|
if ( strlen( $hex ) === 3 ) {
|
|
$hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2];
|
|
}
|
|
if ( strlen( $hex ) !== 6 ) {
|
|
return '0,0,0';
|
|
}
|
|
$r = hexdec( substr( $hex, 0, 2 ) );
|
|
$g = hexdec( substr( $hex, 2, 2 ) );
|
|
$b = hexdec( substr( $hex, 4, 2 ) );
|
|
return "{$r},{$g},{$b}";
|
|
}
|
|
|
|
/**
|
|
* Build the CSS string from current theme-mod values.
|
|
*
|
|
* @return string Complete CSS content.
|
|
*/
|
|
function oribi_build_css() {
|
|
|
|
$s = 'oribi_get_setting'; // shorthand
|
|
|
|
// Gather values.
|
|
$primary = $s( 'color_primary' );
|
|
$primary_dk = $s( 'color_primary_dk' );
|
|
$primary_lt = $s( 'color_primary_lt' );
|
|
$accent = $s( 'color_accent' );
|
|
$accent_dk = $s( 'color_accent_dk' );
|
|
$accent_lt = $s( 'color_accent_lt' );
|
|
$dark = $s( 'color_dark' );
|
|
$dark_2 = $s( 'color_dark_2' );
|
|
$text = $s( 'color_text' );
|
|
$text_muted = $s( 'color_text_muted' );
|
|
$border = $s( 'color_border' );
|
|
$bg = $s( 'color_bg' );
|
|
$bg_alt = $s( 'color_bg_alt' );
|
|
|
|
// Dark mode.
|
|
$dk_primary = $s( 'dark_primary' );
|
|
$dk_primary_dk = $s( 'dark_primary_dk' );
|
|
$dk_primary_lt = $s( 'dark_primary_lt' );
|
|
$dk_accent = $s( 'dark_accent' );
|
|
$dk_accent_dk = $s( 'dark_accent_dk' );
|
|
$dk_accent_lt = $s( 'dark_accent_lt' );
|
|
$dk_dark = $s( 'dark_dark' );
|
|
$dk_dark_2 = $s( 'dark_dark_2' );
|
|
$dk_text = $s( 'dark_text' );
|
|
$dk_text_muted = $s( 'dark_text_muted' );
|
|
$dk_border = $s( 'dark_border' );
|
|
$dk_bg = $s( 'dark_bg' );
|
|
$dk_bg_alt = $s( 'dark_bg_alt' );
|
|
$dk_bg_dark = $s( 'dark_bg_dark' );
|
|
$dk_heading = $s( 'dark_heading' );
|
|
$dk_card_bg = $s( 'dark_card_bg' );
|
|
|
|
// Typography.
|
|
$body_font_slug = $s( 'font_family' );
|
|
$heading_font_slug = $s( 'font_heading' );
|
|
$body_font_css = oribi_get_font_family_css( $body_font_slug );
|
|
$heading_font_css = $heading_font_slug ? oribi_get_font_family_css( $heading_font_slug ) : $body_font_css;
|
|
|
|
// Border radii.
|
|
$r_sm = intval( $s( 'radius_sm' ) );
|
|
$r_md = intval( $s( 'radius_md' ) );
|
|
$r_lg = intval( $s( 'radius_lg' ) );
|
|
$r_xl = intval( $s( 'radius_xl' ) );
|
|
|
|
// Layout.
|
|
$c_max = intval( $s( 'container_max' ) );
|
|
$c_pad_min = floatval( $s( 'container_pad_min' ) );
|
|
$c_pad_max = floatval( $s( 'container_pad_max' ) );
|
|
$wide = intval( $s( 'wide_size' ) );
|
|
|
|
$primary_rgb = oribi_hex_to_rgb( $primary );
|
|
$accent_rgb = oribi_hex_to_rgb( $accent );
|
|
$dk_primary_rgb = oribi_hex_to_rgb( $dk_primary );
|
|
|
|
// Build CSS.
|
|
$css = <<<CSS
|
|
/* ================================================================
|
|
OTS Theme - Generated Theme Overrides
|
|
Generated: %s
|
|
================================================================ */
|
|
|
|
/* ── WordPress preset colour overrides ─────────────────────────── */
|
|
:root {
|
|
--wp--preset--color--primary: {$primary};
|
|
--wp--preset--color--primary-dk: {$primary_dk};
|
|
--wp--preset--color--primary-lt: {$primary_lt};
|
|
--wp--preset--color--accent: {$accent};
|
|
--wp--preset--color--accent-dk: {$accent_dk};
|
|
--wp--preset--color--accent-lt: {$accent_lt};
|
|
--wp--preset--color--dark: {$dark};
|
|
--wp--preset--color--dark-2: {$dark_2};
|
|
--wp--preset--color--text: {$text};
|
|
--wp--preset--color--text-muted: {$text_muted};
|
|
--wp--preset--color--border: {$border};
|
|
--wp--preset--color--bg: {$bg};
|
|
--wp--preset--color--bg-alt: {$bg_alt};
|
|
}
|
|
|
|
/* ── Light-mode aliases (consumed by main.css) ─────────────────── */
|
|
:root,
|
|
[data-theme="light"] {
|
|
--color-primary: {$primary};
|
|
--color-primary-dk: {$primary_dk};
|
|
--color-primary-lt: {$primary_lt};
|
|
--color-primary-rgb: {$primary_rgb};
|
|
--color-accent: {$accent};
|
|
--color-accent-dk: {$accent_dk};
|
|
--color-accent-lt: {$accent_lt};
|
|
--color-accent-rgb: {$accent_rgb};
|
|
--color-dark: {$dark};
|
|
--color-dark-2: {$dark_2};
|
|
--color-text: {$text};
|
|
--color-text-muted: {$text_muted};
|
|
--color-border: {$border};
|
|
--color-bg: {$bg};
|
|
--color-bg-alt: {$bg_alt};
|
|
--color-bg-dark: {$dark};
|
|
--color-heading: {$dark};
|
|
--header-scrolled-bg: rgba(255,255,255,.97);
|
|
--header-scrolled-text: {$text};
|
|
--card-bg: {$bg};
|
|
--form-bg: {$bg_alt};
|
|
--form-bg-focus: {$bg};
|
|
|
|
/* ── Typography ── */
|
|
--font-sans: {$body_font_css};
|
|
--font-heading: {$heading_font_css};
|
|
|
|
/* ── Border radii ── */
|
|
--radius-sm: {$r_sm}px;
|
|
--radius-md: {$r_md}px;
|
|
--radius-lg: {$r_lg}px;
|
|
--radius-xl: {$r_xl}px;
|
|
--wp--custom--radius--sm: {$r_sm}px;
|
|
--wp--custom--radius--md: {$r_md}px;
|
|
--wp--custom--radius--lg: {$r_lg}px;
|
|
--wp--custom--radius--xl: {$r_xl}px;
|
|
|
|
/* ── Layout ── */
|
|
--container-max: {$c_max}px;
|
|
--container-pad: clamp({$c_pad_min}rem, 5vw, {$c_pad_max}rem);
|
|
--wp--custom--container--max: {$c_max}px;
|
|
--wp--custom--container--pad: clamp({$c_pad_min}rem, 5vw, {$c_pad_max}rem);
|
|
}
|
|
|
|
/* ── Dark mode overrides ───────────────────────────────────────── */
|
|
[data-theme="dark"] {
|
|
--wp--custom--dark--primary: {$dk_primary};
|
|
--wp--custom--dark--primary-dk: {$dk_primary_dk};
|
|
--wp--custom--dark--primary-lt: {$dk_primary_lt};
|
|
--wp--custom--dark--accent: {$dk_accent};
|
|
--wp--custom--dark--accent-dk: {$dk_accent_dk};
|
|
--wp--custom--dark--accent-lt: {$dk_accent_lt};
|
|
--wp--custom--dark--dark: {$dk_dark};
|
|
--wp--custom--dark--dark-2: {$dk_dark_2};
|
|
--wp--custom--dark--text: {$dk_text};
|
|
--wp--custom--dark--text-muted: {$dk_text_muted};
|
|
--wp--custom--dark--border: {$dk_border};
|
|
--wp--custom--dark--bg: {$dk_bg};
|
|
--wp--custom--dark--bg-alt: {$dk_bg_alt};
|
|
--wp--custom--dark--bg-dark: {$dk_bg_dark};
|
|
--wp--custom--dark--heading: {$dk_heading};
|
|
--wp--custom--dark--card-bg: {$dk_card_bg};
|
|
|
|
--color-primary: {$dk_primary};
|
|
--color-primary-dk: {$dk_primary_dk};
|
|
--color-primary-lt: {$dk_primary_lt};
|
|
--color-primary-rgb: {$dk_primary_rgb};
|
|
--color-accent: {$dk_accent};
|
|
--color-accent-dk: {$dk_accent_dk};
|
|
--color-accent-lt: {$dk_accent_lt};
|
|
--color-dark: {$dk_dark};
|
|
--color-dark-2: {$dk_dark_2};
|
|
--color-text: {$dk_text};
|
|
--color-text-muted: {$dk_text_muted};
|
|
--color-border: {$dk_border};
|
|
--color-bg: {$dk_bg};
|
|
--color-bg-alt: {$dk_bg_alt};
|
|
--color-bg-dark: {$dk_bg_dark};
|
|
--color-heading: {$dk_heading};
|
|
--header-scrolled-bg: rgba(15,23,36,.97);
|
|
--header-scrolled-text: {$dk_text};
|
|
--card-bg: {$dk_card_bg};
|
|
--form-bg: {$dk_card_bg};
|
|
--form-bg-focus: #1A2538;
|
|
}
|
|
|
|
/* ── Typography application ────────────────────────────────────── */
|
|
body {
|
|
font-family: var(--font-sans);
|
|
}
|
|
|
|
h1, h2, h3, h4, h5, h6,
|
|
.wp-block-heading {
|
|
font-family: var(--font-heading, var(--font-sans));
|
|
}
|
|
|
|
/* ── WordPress layout overrides ────────────────────────────────── */
|
|
.wp-site-blocks > .wp-block-group,
|
|
.wp-site-blocks > .alignfull {
|
|
max-width: none;
|
|
}
|
|
|
|
body > .is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)) {
|
|
max-width: {$c_max}px;
|
|
}
|
|
|
|
CSS;
|
|
|
|
return sprintf( $css, gmdate( 'Y-m-d H:i:s' ) );
|
|
}
|
|
|
|
/**
|
|
* Write the generated CSS file to disk.
|
|
*
|
|
* @return bool|WP_Error True on success, WP_Error on failure.
|
|
*/
|
|
function oribi_write_generated_css() {
|
|
|
|
$css = oribi_build_css();
|
|
$path = oribi_generated_css_path();
|
|
|
|
// Ensure directory exists.
|
|
$dir = dirname( $path );
|
|
if ( ! file_exists( $dir ) ) {
|
|
wp_mkdir_p( $dir );
|
|
}
|
|
|
|
// Use WP_Filesystem for safe file writing.
|
|
require_once ABSPATH . 'wp-admin/includes/file.php';
|
|
global $wp_filesystem;
|
|
if ( ! WP_Filesystem() ) {
|
|
return new WP_Error( 'fs', __( 'Could not initialise filesystem.', 'ots-theme' ) );
|
|
}
|
|
|
|
$result = $wp_filesystem->put_contents( $path, $css, FS_CHMOD_FILE );
|
|
if ( ! $result ) {
|
|
return new WP_Error( 'write', __( 'Could not write generated CSS file.', 'ots-theme' ) );
|
|
}
|
|
|
|
// Bump version number so browsers cache-bust.
|
|
$version = intval( get_theme_mod( 'oribi_css_version', 0 ) ) + 1;
|
|
set_theme_mod( 'oribi_css_version', $version );
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Regenerate the CSS file if it doesn't exist yet (e.g. first page load).
|
|
*
|
|
* Hooked early so the file is ready before wp_enqueue_scripts fires.
|
|
*/
|
|
add_action( 'init', function () {
|
|
if ( ! file_exists( oribi_generated_css_path() ) ) {
|
|
oribi_write_generated_css();
|
|
}
|
|
} );
|