Preview: he-simple-post-layout.php
Size: 30.10 KB
/home/certprox/template.certproxywizard.com/wp-content/plugins/he-simple-post-layout/he-simple-post-layout.php
<?php
/**
* Plugin Name: HE Simple Post Layout
* Plugin URI: https://example.com/he-simple-post-layout
* Description: A beautiful, modern single blog post layout for Hello Elementor theme. Includes next/previous navigation, suggested posts sidebar, and a 1200px reading experience. Zero configuration required.
* Version: 2.0.0
* Requires at least: 5.8
* Requires PHP: 7.4
* Author: Your Name
* License: GPL v2 or later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
* Text Domain: he-simple-post-layout
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( ! class_exists( 'HE_Simple_Post_Layout' ) ) {
final class HE_Simple_Post_Layout {
const VERSION = '2.0.0';
private static ?HE_Simple_Post_Layout $instance = null;
public static function get_instance(): HE_Simple_Post_Layout {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
private function __construct() {
add_action( 'wp_head', [ $this, 'inject_styles' ] );
add_filter( 'the_content', [ $this, 'wrap_post_content' ], 99 );
}
private function should_apply(): bool {
if ( ! is_singular( 'post' ) || ! is_main_query() ) {
return false;
}
if (
class_exists( '\Elementor\Plugin' ) &&
isset( \Elementor\Plugin::$instance->preview ) &&
method_exists( \Elementor\Plugin::$instance->preview, 'is_preview_mode' ) &&
\Elementor\Plugin::$instance->preview->is_preview_mode()
) {
return false;
}
if (
class_exists( '\Elementor\Plugin' ) &&
isset( \Elementor\Plugin::$instance->editor ) &&
method_exists( \Elementor\Plugin::$instance->editor, 'is_edit_mode' ) &&
\Elementor\Plugin::$instance->editor->is_edit_mode()
) {
return false;
}
return true;
}
public function inject_styles(): void {
if ( ! $this->should_apply() ) return;
?>
<style id="hesl-post-styles">
/* ── Reset & base ─────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }
.hesl-page {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
color: #1e293b;
background: #f8fafc;
margin: 0;
}
/* ── Outer shell ──────────────────────────────────────────── */
.hesl-container {
max-width: 1200px;
margin: 0 auto;
padding: 48px 32px 64px;
}
/* ── Two-column grid: article + sidebar ───────────────────── */
.hesl-grid {
display: grid;
grid-template-columns: 1fr 340px;
gap: 48px;
align-items: start;
}
/* ══════════════════════════════════════════════════════════
ARTICLE COLUMN
══════════════════════════════════════════════════════════ */
/* Category badge above title */
.hesl-category-badge {
display: inline-flex;
align-items: center;
gap: 6px;
background: #1B2F6B;
color: #fff;
font-size: 11px;
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
padding: 5px 14px;
border-radius: 100px;
margin-bottom: 20px;
text-decoration: none;
}
/* Article card */
.hesl-article-card {
background: #ffffff;
border-radius: 16px;
overflow: hidden;
box-shadow: 0 1px 3px rgba(0,0,0,0.06), 0 8px 24px rgba(0,0,0,0.06);
}
/* Featured image */
.hesl-featured-image {
width: 100%;
position: relative;
overflow: hidden;
max-height: 480px;
background: #e2e8f0;
}
.hesl-featured-image img {
width: 100%;
height: 480px;
object-fit: cover;
display: block;
transition: transform 0.4s ease;
}
/* Article inner padding */
.hesl-article-inner {
padding: 40px 48px 48px;
}
/* Title */
.hesl-post-title {
font-size: 2.25rem;
font-weight: 800;
line-height: 1.2;
letter-spacing: -0.03em;
color: #0f172a;
margin: 0 0 20px 0;
}
/* Meta row */
.hesl-meta-row {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 20px;
padding: 16px 0;
border-top: 1px solid #f1f5f9;
border-bottom: 1px solid #f1f5f9;
margin-bottom: 36px;
}
.hesl-meta-item {
display: flex;
align-items: center;
gap: 7px;
font-size: 13.5px;
color: #64748b;
font-weight: 500;
}
.hesl-meta-item svg {
width: 15px;
height: 15px;
flex-shrink: 0;
color: #94a3b8;
}
.hesl-meta-dot {
width: 3px;
height: 3px;
border-radius: 50%;
background: #cbd5e1;
}
/* ── Post body typography ─────────────────────────────────── */
.hesl-post-body {
font-size: 17px;
line-height: 1.8;
letter-spacing: -0.01em;
color: #374151;
}
.hesl-post-body p { margin: 0 0 1.6em; }
.hesl-post-body h2 {
font-size: 1.5rem; font-weight: 700;
letter-spacing: -0.02em; color: #0f172a;
margin: 2.2em 0 0.8em; line-height: 1.3;
}
.hesl-post-body h3 {
font-size: 1.2rem; font-weight: 700;
color: #1e293b; margin: 1.8em 0 0.6em;
}
.hesl-post-body h4, .hesl-post-body h5, .hesl-post-body h6 {
font-weight: 600; color: #1e293b; margin: 1.5em 0 0.5em;
}
.hesl-post-body ul, .hesl-post-body ol {
padding-left: 1.6em; margin: 0 0 1.6em;
}
.hesl-post-body li { margin-bottom: 0.55em; }
.hesl-post-body blockquote {
margin: 2.2em 0;
padding: 1.2em 1.8em;
border-left: 4px solid #1B2F6B;
background: #f0f4ff;
border-radius: 0 10px 10px 0;
font-size: 1.05em;
font-style: italic;
color: #475569;
}
.hesl-post-body a {
color: #1B2F6B;
text-decoration: underline;
text-underline-offset: 3px;
font-weight: 500;
}
.hesl-post-body a:hover { color: #F5A623; }
.hesl-post-body img {
max-width: 100%; height: auto;
border-radius: 10px; display: block; margin: 1.8em auto;
}
.hesl-post-body hr {
border: none; border-top: 1px solid #e2e8f0; margin: 2.5em 0;
}
.hesl-post-body code {
background: #f1f5f9; border-radius: 5px;
padding: 0.15em 0.5em; font-size: 0.875em; color: #1B2F6B;
}
.hesl-post-body pre {
background: #0f172a; color: #e2e8f0;
border-radius: 10px; padding: 1.4em 1.6em;
overflow-x: auto; font-size: 0.875rem;
line-height: 1.65; margin: 0 0 1.8em;
}
.hesl-post-body pre code { background: transparent; padding: 0; color: inherit; }
.hesl-post-body table { width: 100%; border-collapse: collapse; font-size: 0.9rem; margin: 0 0 1.6em; }
.hesl-post-body th, .hesl-post-body td { border: 1px solid #e2e8f0; padding: 0.7em 1em; text-align: left; }
.hesl-post-body th { background: #f8fafc; font-weight: 600; color: #0f172a; }
/* ── Tags strip ───────────────────────────────────────────── */
.hesl-tags {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-top: 40px;
padding-top: 28px;
border-top: 1px solid #f1f5f9;
}
.hesl-tags-label {
font-size: 13px;
font-weight: 600;
color: #94a3b8;
text-transform: uppercase;
letter-spacing: 0.07em;
align-self: center;
margin-right: 4px;
}
.hesl-tag {
display: inline-block;
background: #f1f5f9;
color: #475569;
font-size: 12.5px;
font-weight: 500;
padding: 5px 14px;
border-radius: 100px;
text-decoration: none;
transition: background 0.2s, color 0.2s;
}
.hesl-tag:hover { background: #1B2F6B; color: #fff; }
/* ══════════════════════════════════════════════════════════
PREV / NEXT NAVIGATION
══════════════════════════════════════════════════════════ */
.hesl-post-nav {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
margin-top: 24px;
}
.hesl-nav-link {
display: flex;
flex-direction: column;
gap: 6px;
background: #ffffff;
border: 1.5px solid #e2e8f0;
border-radius: 14px;
padding: 20px 22px;
text-decoration: none;
transition: border-color 0.2s, box-shadow 0.2s, transform 0.2s;
color: inherit;
}
.hesl-nav-link:hover {
border-color: #1B2F6B;
box-shadow: 0 4px 16px rgba(27,47,107,0.1);
transform: translateY(-2px);
color: inherit;
}
.hesl-nav-link.hesl-nav-next { text-align: right; }
.hesl-nav-label {
font-size: 11px;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
color: #94a3b8;
display: flex;
align-items: center;
gap: 5px;
}
.hesl-nav-link.hesl-nav-next .hesl-nav-label { justify-content: flex-end; }
.hesl-nav-title {
font-size: 14.5px;
font-weight: 600;
color: #1e293b;
line-height: 1.4;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.hesl-nav-thumb {
width: 42px;
height: 42px;
border-radius: 8px;
object-fit: cover;
flex-shrink: 0;
}
.hesl-nav-content { display: flex; align-items: center; gap: 12px; }
.hesl-nav-link.hesl-nav-next .hesl-nav-content { flex-direction: row-reverse; }
/* ══════════════════════════════════════════════════════════
SIDEBAR
══════════════════════════════════════════════════════════ */
.hesl-sidebar {
display: flex;
flex-direction: column;
gap: 24px;
position: sticky;
top: 32px;
}
.hesl-widget {
background: #ffffff;
border-radius: 16px;
padding: 24px;
box-shadow: 0 1px 3px rgba(0,0,0,0.06), 0 4px 12px rgba(0,0,0,0.04);
}
.hesl-widget-title {
font-size: 13px;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
color: #94a3b8;
margin: 0 0 18px;
padding-bottom: 14px;
border-bottom: 1px solid #f1f5f9;
}
/* Author card */
.hesl-author-card {
text-align: center;
padding: 8px 0;
}
.hesl-author-avatar {
width: 72px;
height: 72px;
border-radius: 50%;
margin: 0 auto 12px;
display: block;
border: 3px solid #f1f5f9;
object-fit: cover;
}
.hesl-author-avatar-fallback {
width: 72px;
height: 72px;
border-radius: 50%;
background: linear-gradient(135deg, #1B2F6B 0%, #3b5998 100%);
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 12px;
font-size: 28px;
font-weight: 700;
color: #fff;
}
.hesl-author-name {
font-size: 16px;
font-weight: 700;
color: #0f172a;
margin: 0 0 5px;
}
.hesl-author-bio {
font-size: 13px;
color: #64748b;
line-height: 1.6;
margin: 0;
}
/* Suggested posts */
.hesl-suggested-list {
display: flex;
flex-direction: column;
gap: 16px;
}
.hesl-suggested-item {
display: flex;
gap: 14px;
align-items: flex-start;
text-decoration: none;
color: inherit;
transition: opacity 0.2s;
}
.hesl-suggested-item:hover { opacity: 0.75; }
.hesl-suggested-thumb {
width: 72px;
height: 72px;
border-radius: 10px;
object-fit: cover;
flex-shrink: 0;
background: #e2e8f0;
}
.hesl-suggested-thumb-placeholder {
width: 72px;
height: 72px;
border-radius: 10px;
background: #f1f5f9;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
}
.hesl-suggested-thumb-placeholder svg { color: #cbd5e1; }
.hesl-suggested-info { flex: 1; min-width: 0; }
.hesl-suggested-title {
font-size: 13.5px;
font-weight: 600;
color: #1e293b;
line-height: 1.4;
margin: 0 0 5px;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.hesl-suggested-date {
font-size: 11.5px;
color: #94a3b8;
font-weight: 500;
}
.hesl-suggested-divider {
height: 1px;
background: #f1f5f9;
margin: 0;
border: none;
}
/* ══════════════════════════════════════════════════════════
RESPONSIVE
══════════════════════════════════════════════════════════ */
@media (max-width: 1024px) {
.hesl-grid { grid-template-columns: 1fr; }
.hesl-sidebar { position: static; }
.hesl-container { padding: 32px 24px 48px; }
}
@media (max-width: 768px) {
.hesl-article-inner { padding: 24px 20px 32px; }
.hesl-post-title { font-size: 1.65rem; }
.hesl-post-body { font-size: 16px; }
.hesl-post-nav { grid-template-columns: 1fr; }
.hesl-featured-image img { height: 260px; }
.hesl-container { padding: 16px 16px 40px; }
}
@media (max-width: 480px) {
.hesl-post-title { font-size: 1.4rem; }
.hesl-meta-dot { display: none; }
.hesl-meta-row { gap: 10px; }
}
</style>
<?php
}
/* ── Helpers ─────────────────────────────────────────────────── */
private function get_post_thumbnail_html( int $post_id, string $size, string $css_class, string $fallback_text = '' ): string {
$url = get_the_post_thumbnail_url( $post_id, $size );
if ( $url ) {
$alt = esc_attr( get_the_title( $post_id ) );
return sprintf( '<img src="%s" alt="%s" class="%s" loading="lazy" decoding="async" />', esc_url( $url ), $alt, esc_attr( $css_class ) );
}
if ( $fallback_text ) {
return sprintf(
'<div class="hesl-suggested-thumb-placeholder"><svg width="28" height="28" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><path d="M21 15l-5-5L5 21"/></svg></div>'
);
}
return '';
}
private function get_categories_html( int $post_id ): string {
$cats = get_the_category( $post_id );
if ( empty( $cats ) ) return '';
$cat = $cats[0];
return sprintf(
'<a href="%s" class="hesl-category-badge">%s</a>',
esc_url( get_category_link( $cat->term_id ) ),
esc_html( $cat->name )
);
}
private function get_tags_html( int $post_id ): string {
$tags = get_the_tags( $post_id );
if ( empty( $tags ) ) return '';
$out = '<div class="hesl-tags"><span class="hesl-tags-label">Tags</span>';
foreach ( $tags as $tag ) {
$out .= sprintf(
'<a href="%s" class="hesl-tag">%s</a>',
esc_url( get_tag_link( $tag->term_id ) ),
esc_html( $tag->name )
);
}
$out .= '</div>';
return $out;
}
private function get_nav_html(): string {
$prev = get_previous_post();
$next = get_next_post();
if ( ! $prev && ! $next ) return '';
$html = '<nav class="hesl-post-nav" aria-label="Post navigation">';
if ( $prev ) {
$thumb = get_the_post_thumbnail_url( $prev->ID, 'thumbnail' );
$thumb_html = $thumb
? sprintf( '<img src="%s" class="hesl-nav-thumb" alt="" loading="lazy" />', esc_url( $thumb ) )
: '';
$html .= sprintf(
'<a href="%s" class="hesl-nav-link hesl-nav-prev">
<span class="hesl-nav-label"><svg width="13" height="13" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24"><path d="M19 12H5M12 5l-7 7 7 7"/></svg> Previous Post</span>
<div class="hesl-nav-content">%s<span class="hesl-nav-title">%s</span></div>
</a>',
esc_url( get_permalink( $prev->ID ) ),
$thumb_html,
esc_html( get_the_title( $prev->ID ) )
);
} else {
$html .= '<div></div>';
}
if ( $next ) {
$thumb = get_the_post_thumbnail_url( $next->ID, 'thumbnail' );
$thumb_html = $thumb
? sprintf( '<img src="%s" class="hesl-nav-thumb" alt="" loading="lazy" />', esc_url( $thumb ) )
: '';
$html .= sprintf(
'<a href="%s" class="hesl-nav-link hesl-nav-next">
<span class="hesl-nav-label">Next Post <svg width="13" height="13" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24"><path d="M5 12h14M12 5l7 7-7 7"/></svg></span>
<div class="hesl-nav-content">%s<span class="hesl-nav-title">%s</span></div>
</a>',
esc_url( get_permalink( $next->ID ) ),
$thumb_html,
esc_html( get_the_title( $next->ID ) )
);
} else {
$html .= '<div></div>';
}
$html .= '</nav>';
return $html;
}
private function get_author_widget_html( int $post_id ): string {
$author_id = (int) get_post_field( 'post_author', $post_id );
$name = esc_html( get_the_author_meta( 'display_name', $author_id ) );
$bio = esc_html( get_the_author_meta( 'description', $author_id ) );
$avatar_url = esc_url( get_avatar_url( $author_id, [ 'size' => 144 ] ) );
$initial = strtoupper( mb_substr( $name, 0, 1 ) );
$avatar_html = $avatar_url
? sprintf( '<img src="%s" class="hesl-author-avatar" alt="%s" loading="lazy" />', $avatar_url, $name )
: sprintf( '<div class="hesl-author-avatar-fallback">%s</div>', esc_html( $initial ) );
return sprintf(
'<aside class="hesl-widget">
<p class="hesl-widget-title">About the Author</p>
<div class="hesl-author-card">
%s
<p class="hesl-author-name">%s</p>
%s
</div>
</aside>',
$avatar_html,
$name,
$bio ? sprintf( '<p class="hesl-author-bio">%s</p>', $bio ) : ''
);
}
private function get_suggested_posts_html( int $current_id ): string {
$args = [
'post_type' => 'post',
'posts_per_page' => 4,
'post__not_in' => [ $current_id ],
'ignore_sticky_posts' => 1,
'orderby' => 'rand',
];
// Prefer posts from same category
$cats = wp_get_post_categories( $current_id );
if ( ! empty( $cats ) ) {
$args['category__in'] = $cats;
}
$query = new WP_Query( $args );
if ( ! $query->have_posts() ) return '';
$html = '<aside class="hesl-widget">
<p class="hesl-widget-title">You Might Also Like</p>
<div class="hesl-suggested-list">';
$first = true;
while ( $query->have_posts() ) {
$query->the_post();
$pid = get_the_ID();
$thumb = get_the_post_thumbnail_url( $pid, 'thumbnail' );
if ( ! $first ) {
$html .= '<hr class="hesl-suggested-divider" />';
}
$first = false;
$thumb_html = $thumb
? sprintf( '<img src="%s" class="hesl-suggested-thumb" alt="%s" loading="lazy" />', esc_url( $thumb ), esc_attr( get_the_title() ) )
: '<div class="hesl-suggested-thumb-placeholder"><svg width="28" height="28" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><path d="M21 15l-5-5L5 21"/></svg></div>';
$html .= sprintf(
'<a href="%s" class="hesl-suggested-item">
%s
<div class="hesl-suggested-info">
<p class="hesl-suggested-title">%s</p>
<span class="hesl-suggested-date">%s</span>
</div>
</a>',
esc_url( get_permalink() ),
$thumb_html,
esc_html( get_the_title() ),
esc_html( get_the_date() )
);
}
wp_reset_postdata();
$html .= '</div></aside>';
return $html;
}
/* ── Main content wrapper ───────────────────────────────────── */
public function wrap_post_content( string $content ): string {
if ( ! $this->should_apply() ) return $content;
$post_id = get_the_ID();
$title = esc_html( get_the_title( $post_id ) );
$date = esc_html( get_the_date( 'F j, Y', $post_id ) );
$author = esc_html( get_the_author() );
$read_time = max( 1, (int) ceil( str_word_count( wp_strip_all_tags( $content ) ) / 200 ) );
/* Featured image */
$thumb_url = get_the_post_thumbnail_url( $post_id, 'full' );
$thumb_html = '';
if ( $thumb_url ) {
$thumb_html = sprintf(
'<div class="hesl-featured-image">
<img src="%s" alt="%s" loading="eager" decoding="async" />
</div>',
esc_url( $thumb_url ),
esc_attr( $title )
);
}
/* Calendar icon */
$icon_date = '<svg width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/></svg>';
/* Author icon */
$icon_author = '<svg width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><circle cx="12" cy="8" r="4"/><path d="M4 20c0-4 3.6-7 8-7s8 3 8 7"/></svg>';
/* Clock icon */
$icon_clock = '<svg width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 3"/></svg>';
ob_start();
?>
<div class="hesl-page">
<div class="hesl-container">
<div class="hesl-grid">
<!-- ── Article column ── -->
<div class="hesl-article-col">
<?php echo $this->get_categories_html( $post_id ); ?>
<article class="hesl-article-card">
<?php echo $thumb_html; ?>
<div class="hesl-article-inner">
<h1 class="hesl-post-title"><?php echo $title; ?></h1>
<div class="hesl-meta-row">
<span class="hesl-meta-item"><?php echo $icon_date; ?><?php echo $date; ?></span>
<span class="hesl-meta-dot"></span>
<span class="hesl-meta-item"><?php echo $icon_author; ?><?php echo $author; ?></span>
<span class="hesl-meta-dot"></span>
<span class="hesl-meta-item"><?php echo $icon_clock; ?><?php echo $read_time; ?> min read</span>
</div>
<div class="hesl-post-body">
<?php echo $content; ?>
</div>
<?php echo $this->get_tags_html( $post_id ); ?>
</div><!-- /.hesl-article-inner -->
</article><!-- /.hesl-article-card -->
<?php echo $this->get_nav_html(); ?>
</div><!-- /.hesl-article-col -->
<!-- ── Sidebar ── -->
<aside class="hesl-sidebar">
<?php echo $this->get_author_widget_html( $post_id ); ?>
<?php echo $this->get_suggested_posts_html( $post_id ); ?>
</aside>
</div><!-- /.hesl-grid -->
</div><!-- /.hesl-container -->
</div><!-- /.hesl-page -->
<?php
return ob_get_clean();
}
} // end class
} // end if class_exists
add_action( 'plugins_loaded', static function (): void {
HE_Simple_Post_Layout::get_instance();
} );
Directory Contents
Dirs: 0 × Files: 1