Add Display settings: article font, font size, table borders
New "Display" admin section with three settings: - Article Font (7 choices + theme default, Google Fonts loaded on demand) - Article Font Size (px, 0 = theme default) - Table Borders (toggle, on by default — adds borders, padding, striping) Inline CSS injected via wp_head only on singular posts with _prautoblogger_generated meta — zero impact on other pages. Extracted Image_Model_Registry to keep settings-fields-extended under 300 lines. Bump 0.6.3 → 0.7.0. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
196
includes/frontend/class-article-typography.php
Normal file
196
includes/frontend/class-article-typography.php
Normal file
@@ -0,0 +1,196 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Injects frontend typography and table styles for PRAutoBlogger-generated posts.
|
||||
*
|
||||
* What: Outputs inline CSS on single post pages that have the
|
||||
* _prautoblogger_generated meta flag, applying user-configured
|
||||
* font family, font size, and table border styles.
|
||||
* Who calls it: PRAutoBlogger::register_frontend_hooks() hooks this
|
||||
* into wp_head.
|
||||
* Dependencies: Reads wp_options for typography settings.
|
||||
*
|
||||
* @see class-prautoblogger.php — Registers the wp_head hook.
|
||||
* @see class-settings-fields.php — Defines the Display settings section.
|
||||
* @see admin/class-admin-page.php — Renders the settings form.
|
||||
*/
|
||||
class PRAutoBlogger_Article_Typography {
|
||||
|
||||
/** Option key for article font family. */
|
||||
private const OPT_FONT_FAMILY = 'prautoblogger_article_font_family';
|
||||
|
||||
/** Option key for article body font size in pixels. */
|
||||
private const OPT_FONT_SIZE = 'prautoblogger_article_font_size';
|
||||
|
||||
/** Option key for table border toggle. */
|
||||
private const OPT_TABLE_BORDERS = 'prautoblogger_table_borders';
|
||||
|
||||
/**
|
||||
* Output inline CSS in <head> for PRAutoBlogger posts.
|
||||
*
|
||||
* Only outputs on singular post pages generated by PRAutoBlogger.
|
||||
* The CSS targets .post-content (PR Theme's article body class)
|
||||
* and overrides the theme's CSS custom properties so the cascade
|
||||
* works naturally with headings and sub-elements.
|
||||
*
|
||||
* Side effects: echoes a <style> block.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function on_wp_head(): void {
|
||||
if ( ! is_singular( 'post' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$post_id = get_queried_object_id();
|
||||
if ( '1' !== get_post_meta( $post_id, '_prautoblogger_generated', true ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$css = $this->build_css();
|
||||
if ( '' === $css ) {
|
||||
return;
|
||||
}
|
||||
|
||||
printf(
|
||||
"\n<style id=\"prautoblogger-typography\">\n%s\n</style>\n",
|
||||
$css // Already escaped — contains only sanitized option values.
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the CSS string from current settings.
|
||||
*
|
||||
* Reads each setting at call time (never cached) so changes
|
||||
* take effect immediately without cache invalidation.
|
||||
*
|
||||
* @return string CSS rules, or empty string if all defaults.
|
||||
*/
|
||||
private function build_css(): string {
|
||||
$rules = [];
|
||||
|
||||
$font_family = $this->get_font_family_stack();
|
||||
if ( '' !== $font_family ) {
|
||||
$rules[] = sprintf(
|
||||
".post-content,\n.post-content p,\n.post-content li {\n\tfont-family: %s;\n}",
|
||||
$font_family
|
||||
);
|
||||
}
|
||||
|
||||
$font_size = absint( get_option( self::OPT_FONT_SIZE, 0 ) );
|
||||
if ( $font_size > 0 ) {
|
||||
$rules[] = sprintf(
|
||||
".post-content,\n.post-content p,\n.post-content li {\n\tfont-size: %dpx;\n\tline-height: 1.7;\n}",
|
||||
$font_size
|
||||
);
|
||||
}
|
||||
|
||||
$table_borders = get_option( self::OPT_TABLE_BORDERS, '1' );
|
||||
if ( '1' === $table_borders ) {
|
||||
$rules[] = implode( "\n", [
|
||||
'.post-content table {',
|
||||
"\tborder-collapse: collapse;",
|
||||
"\twidth: 100%;",
|
||||
"\tmargin: 1.5em 0;",
|
||||
'}',
|
||||
'.post-content th,',
|
||||
'.post-content td {',
|
||||
"\tborder: 1px solid #d1d5db;",
|
||||
"\tpadding: 0.6em 1em;",
|
||||
"\ttext-align: left;",
|
||||
'}',
|
||||
'.post-content thead th {',
|
||||
"\tbackground: #f3f4f6;",
|
||||
"\tfont-weight: 600;",
|
||||
'}',
|
||||
'.post-content tbody tr:nth-child(even) {',
|
||||
"\tbackground: #f9fafb;",
|
||||
'}',
|
||||
] );
|
||||
}
|
||||
|
||||
return implode( "\n\n", $rules );
|
||||
}
|
||||
|
||||
/**
|
||||
* Map the stored font-family key to a full CSS font stack.
|
||||
*
|
||||
* Returns empty string for 'default' (let the theme decide).
|
||||
*
|
||||
* @return string CSS font-family value, or empty for theme default.
|
||||
*/
|
||||
private function get_font_family_stack(): string {
|
||||
$key = sanitize_text_field( get_option( self::OPT_FONT_FAMILY, 'default' ) );
|
||||
|
||||
$stacks = [
|
||||
'default' => '',
|
||||
'inter' => '"Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
|
||||
'georgia' => '"Georgia", "Times New Roman", Times, serif',
|
||||
'merriweather' => '"Merriweather", Georgia, "Times New Roman", serif',
|
||||
'lora' => '"Lora", Georgia, "Times New Roman", serif',
|
||||
'open_sans' => '"Open Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
|
||||
'roboto' => '"Roboto", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
|
||||
'system' => '-apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", sans-serif',
|
||||
];
|
||||
|
||||
return $stacks[ $key ] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get available font choices for the settings UI.
|
||||
*
|
||||
* @return array<string, string> Map of key => display label.
|
||||
*/
|
||||
public static function get_font_choices(): array {
|
||||
return [
|
||||
'default' => __( 'Theme Default (Inter)', 'prautoblogger' ),
|
||||
'inter' => __( 'Inter (Sans-serif)', 'prautoblogger' ),
|
||||
'georgia' => __( 'Georgia (Serif)', 'prautoblogger' ),
|
||||
'merriweather' => __( 'Merriweather (Serif)', 'prautoblogger' ),
|
||||
'lora' => __( 'Lora (Serif)', 'prautoblogger' ),
|
||||
'open_sans' => __( 'Open Sans (Sans-serif)', 'prautoblogger' ),
|
||||
'roboto' => __( 'Roboto (Sans-serif)', 'prautoblogger' ),
|
||||
'system' => __( 'System Default', 'prautoblogger' ),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue Google Fonts stylesheet when a web font is selected.
|
||||
*
|
||||
* Only loads on singular PRAutoBlogger posts that use a Google-hosted font.
|
||||
*
|
||||
* Side effects: enqueues a remote stylesheet.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function on_enqueue_fonts(): void {
|
||||
if ( ! is_singular( 'post' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$post_id = get_queried_object_id();
|
||||
if ( '1' !== get_post_meta( $post_id, '_prautoblogger_generated', true ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$font = sanitize_text_field( get_option( self::OPT_FONT_FAMILY, 'default' ) );
|
||||
|
||||
// Map font keys to Google Fonts URLs. Inter is already loaded by the theme.
|
||||
$google_fonts = [
|
||||
'merriweather' => 'https://fonts.googleapis.com/css2?family=Merriweather:wght@400;700&display=swap',
|
||||
'lora' => 'https://fonts.googleapis.com/css2?family=Lora:wght@400;700&display=swap',
|
||||
'open_sans' => 'https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600;700&display=swap',
|
||||
'roboto' => 'https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap',
|
||||
];
|
||||
|
||||
if ( isset( $google_fonts[ $font ] ) ) {
|
||||
wp_enqueue_style(
|
||||
'prautoblogger-google-font',
|
||||
$google_fonts[ $font ],
|
||||
[],
|
||||
null // No version for external CDN URLs.
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user