
طراحی قالب وردپرس فقط ساختن چند فایل PHP و قرار دادن آنها داخل پوشه themes نیست. یک قالب درست باید بداند وردپرس برای هر نوع صفحه سراغ کدام فایل میرود، فایلهای CSS و JavaScript را چگونه بارگذاری کند، منو و تصویر شاخص را چطور فعال کند، خروجی نوشتهها را از حلقه وردپرس بگیرد و در عین حال با ویرایشگر بلوکی، افزونهها و استانداردهای فعلی وردپرس ناسازگار نشود.
در این آموزش یک قالب واقعی و ساده با نام hamyarwp-starter میسازیم. مسیر آموزش از ایجاد پوشه قالب شروع میشود و به ساخت صفحه نوشته، برگه، آرشیو، جستجو، خطای 404، دیدگاهها، منو، تصویر شاخص، فایل theme.json و بررسی نهایی میرسد. اگر هنوز با مدیریت وردپرس آشنا نیستید، بهتر است ابتدا آموزش وردپرس همیار وردپرس را مرور کنید تا مفاهیمی مثل نوشته، برگه، فهرست و پوسته برایتان روشن باشد.
خروجی نهایی این آموزش چیست؟
در پایان یک قالب کلاسیکِ مدرن و قابل توسعه دارید که از ساختار PHP وردپرس استفاده میکند، اما برای هماهنگی بهتر با ویرایشگر بلوکی از قابلیتهای جدیدتر مانند
theme.jsonنیز بهره میبرد. هدف این مقاله ساخت یک قالب تجاری کامل نیست؛ هدف این است که مسیر استاندارد توسعه قالب را واقعاً یاد بگیرید و بعد بتوانید آن را توسعه دهید.
قالب وردپرس چیست و چه کاری انجام میدهد؟
قالب یا Theme لایهای است که محتوای ذخیرهشده در وردپرس را به خروجی قابل مشاهده در مرورگر تبدیل میکند. رنگها، تایپوگرافی، چیدمان، سربرگ، پابرگ، صفحه نوشته، آرشیو، نتایج جستجو و بسیاری از جزئیات ظاهری توسط قالب کنترل میشوند. مستندات رسمی وردپرس نیز تأکید میکند که وظیفه اصلی Theme نمایش و ارائه محتواست؛ قابلیتهای حیاتی سایت که نباید با تغییر قالب از بین بروند، بهتر است در افزونه قرار بگیرند.
به همین دلیل اگر در حال طراحی قالب فروشگاهی، آموزشی یا شرکتی هستید، نباید هر قابلیت کسبوکار را داخل functions.php انباشته کنید. برای مثال ساخت یک نوع محتوای بسیار حیاتی که بعد از تعویض قالب باید همچنان باقی بماند، معمولاً مسئولیت افزونه است، نه Theme. این تفکیک از همان ابتدا باعث میشود قالب قابل نگهداریتر و تعویض آن کمریسکتر باشد.
قالب کلاسیک، قالب بلوکی یا Hybrid؛ کدام را یاد بگیریم؟
وردپرس امروز دو خانواده اصلی قالب دارد: Classic Theme و Block Theme. قالبهای کلاسیک عمدتاً با فایلهای PHP، HTML، CSS و توابع وردپرس ساخته میشوند. قالبهای بلوکی از قالبهای HTML مبتنی بر بلوک و Site Editor استفاده میکنند. مستندات رسمی وردپرس قالبهای بلوکی را مسیر مدرن توسعه Theme معرفی میکند، اما قالبهای کلاسیک همچنان پشتیبانی میشوند و بخش بزرگی از اکوسیستم وردپرس بر پایه آنهاست.
| نوع قالب | ساختار اصلی | ویرایش از Site Editor | مناسب برای |
|---|---|---|---|
| کلاسیک | فایلهای PHP مانند index.php و single.php |
خیر، به شکل کامل | یادگیری توسعه PHP وردپرس، پروژههای قدیمی و بسیاری از قالبهای اختصاصی |
| بلوکی | فایلهای HTML بلوکی در پوشه templates و معمولاً theme.json |
بله | پروژههایی که ویرایش کامل ساختار سایت از ویرایشگر بلوکی اولویت دارد |
| Hybrid | هسته کلاسیک + برخی قابلیتهای مدرن مانند theme.json |
محدودتر از Block Theme | پروژههایی که انعطاف PHP را میخواهند و همزمان از امکانات جدید ویرایشگر استفاده میکنند |
در این مقاله پروژه اصلی را بهصورت قالب کلاسیک با امکانات مدرن میسازیم. دلیل این انتخاب آموزشی روشن است: برای فهمیدن Template Hierarchy، حلقه وردپرس، Template Tagها، هوکها و فایلهای قالب، ساختار کلاسیک دید واضحتری میدهد. در بخشهای بعدی theme.json را هم اضافه میکنیم تا پروژه صرفاً یک نمونه قدیمی نباشد. برای مطالعه جزئیات تفاوت این دو معماری، راهنمای رسمی Theme در WordPress.org مرجع مناسبی است.
پیشنیازهای طراحی قالب وردپرس
برای دنبال کردن این آموزش لازم نیست برنامهنویس حرفهای باشید، اما آشنایی پایه با HTML و CSS و خواندن کد PHP ضروری است. اگر مفهوم متغیر، شرط، حلقه و تابع در PHP برایتان کاملاً ناآشناست، بهتر است ابتدا مبانی را یاد بگیرید و بعد سراغ توسعه Theme بروید. همچنین بهتر است پروژه را روی سایت اصلی انجام ندهید؛ یک نصب محلی یا محیط آزمایشی جدا انتخاب امنتری است.

- یک نصب آزمایشی وردپرس: روی رایانه شخصی یا محیط Staging.
- ویرایشگر کد: هر ویرایشگری که نمایش ساختار پوشه و فایلهای PHP، CSS و JavaScript را راحت کند.
- مرورگر و ابزار توسعهدهنده: برای بررسی خطاهای ظاهری و واکنشگرایی.
- آشنایی پایه با PHP: برای درک توابع، شرطها و حلقه وردپرس.
- نسخه پشتیبان: اگر این مراحل را روی سایتی انجام میدهید که از قبل داده دارد، قبل از تغییرات نسخه پشتیبان بگیرید.
نکته مهم: فایلهای هسته وردپرس مانند پوشههای
wp-adminوwp-includesرا برای ساخت قالب ویرایش نمیکنیم. تمام کار ما داخلwp-content/themesانجام میشود.
مراحل طراحی قالب وردپرس از صفر
برای اینکه آموزش قابل اجرا باشد، از یک پروژه ثابت استفاده میکنیم. نام پوشه قالب ما hamyarwp-starter است. بهتر است شما هم یک بار دقیقاً با همین ساختار جلو بروید و بعد از اینکه منطق را یاد گرفتید، نامها و طراحی را برای پروژه شخصی خودتان تغییر دهید.
مرحله 1: وردپرس آزمایشی را آماده کنید
در محیط آزمایشی خود یک وردپرس تمیز داشته باشید و حداقل چند نوشته، یک برگه، یک دستهبندی و یک منو بسازید. وجود محتوای آزمایشی باعث میشود هنگام ساخت index.php، single.php و archive.php فوراً نتیجه را ببینید. همچنین برای یکی دو نوشته تصویر شاخص تعیین کنید تا بعداً قابلیت Featured Image را آزمایش کنیم.
مرحله 2: پوشه قالب را در wp-content/themes بسازید
به مسیر نصب وردپرس بروید و پوشه wp-content/themes را باز کنید. داخل آن پوشه جدیدی با نام زیر بسازید:
hamyarwp-starter
در تمام مراحل بعدی فایلهای قالب داخل همین پوشه قرار میگیرند. نام پوشه بهتر است کوتاه، انگلیسی، بدون فاصله و یکتا باشد.
مرحله 3: فایل style.css را بسازید و قالب را به وردپرس معرفی کنید
در ریشه پوشه قالب فایل style.css را ایجاد کنید. این فایل فقط برای CSS نیست؛ وردپرس اطلاعات شناسنامهای Theme را از سربرگ همین فایل میخواند. برای پروژه آموزشی ما محتوای اولیه این است:
/*
Theme Name: HamyarWP Starter
Theme URI: https://hamyarwp.com/
Author: HamyarWP
Author URI: https://hamyarwp.com/
Description: A minimal educational classic WordPress theme for the HamyarWP theme development tutorial.
Version: 1.0.0
License: GNU General Public License v2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Text Domain: hamyarwp-starter
*/

مهمترین مقدار، Theme Name است. Text Domain نیز برای ترجمهپذیری کدها اهمیت دارد و بهتر است با slug قالب هماهنگ باشد. اطلاعاتی مانند نسخه و مجوز هم از ابتدا پروژه را منظمتر میکند.

مرحله 4: index.php را ایجاد کنید و قالب را فعال کنید
برای شروع یک قالب کلاسیک ساده، style.css و index.php نقطه پایه هستند. فعلاً داخل index.php یک خروجی بسیار ساده قرار دهید تا فقط مطمئن شویم وردپرس Theme را میبیند:
<?php
echo '<h1>HamyarWP Starter</h1>';
حالا از پیشخوان وردپرس به بخش نمایش و سپس پوستهها بروید. باید قالب HamyarWP Starter را در فهرست ببینید. آن را فعال کنید و صفحه اصلی سایت را باز کنید. اگر عنوان ساده بالا نمایش داده شد، اتصال قالب با وردپرس برقرار است.
مرحله 5: functions.php را بسازید و قابلیتهای قالب را ثبت کنید
فایل functions.php یکی از مهمترین فایلهای قالب کلاسیک است. وردپرس این فایل را هنگام بارگذاری Theme اجرا میکند و از آن برای ثبت قابلیتهای قالب، منوها، هوکها و بارگذاری فایلهای جانبی استفاده میکنیم. در پروژه ما فایل کامل زیر را قرار دهید:
<?php
/**
* Theme setup and assets.
*
* @package HamyarWP_Starter
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
function hamyarwp_starter_setup() {
add_theme_support( 'title-tag' );
add_theme_support( 'post-thumbnails' );
add_theme_support( 'automatic-feed-links' );
add_theme_support( 'responsive-embeds' );
add_theme_support( 'wp-block-styles' );
add_theme_support( 'align-wide' );
add_theme_support(
'html5',
array(
'search-form',
'comment-form',
'comment-list',
'gallery',
'caption',
'style',
'script',
)
);
add_theme_support(
'custom-logo',
array(
'height' => 80,
'width' => 240,
'flex-height' => true,
'flex-width' => true,
)
);
register_nav_menus(
array(
'primary' => __( 'منوی اصلی', 'hamyarwp-starter' ),
)
);
}
add_action( 'after_setup_theme', 'hamyarwp_starter_setup' );
function hamyarwp_starter_assets() {
$theme_version = wp_get_theme()->get( 'Version' );
wp_enqueue_style(
'hamyarwp-starter-style',
get_stylesheet_uri(),
array(),
$theme_version
);
wp_enqueue_style(
'hamyarwp-starter-main',
get_template_directory_uri() . '/assets/css/main.css',
array( 'hamyarwp-starter-style' ),
$theme_version
);
wp_enqueue_script(
'hamyarwp-starter-main',
get_template_directory_uri() . '/assets/js/main.js',
array(),
$theme_version,
true
);
}
add_action( 'wp_enqueue_scripts', 'hamyarwp_starter_assets' );
چند نکته مهم در این کد وجود دارد. قابلیت title-tag اجازه میدهد وردپرس عنوان سند را مدیریت کند؛ post-thumbnails تصویر شاخص را فعال میکند؛ html5 خروجی بخشهایی مثل فرم جستجو و دیدگاه را به ساختار HTML5 نزدیک میکند و register_nav_menus() محل منوی اصلی را به وردپرس معرفی میکند.
همچنین نام توابع با پیشوند hamyarwp_starter_ شروع شده است. این کار احتمال تداخل نام تابع با افزونهها یا کدهای دیگر را کاهش میدهد. مستندات رسمی وردپرس نیز برای Themeها توصیه میکند نام توابع و کلاسهای اختصاصی یکتا باشند.
مرحله 6: فایلهای CSS و JavaScript را به روش صحیح بارگذاری کنید
یکی از اشتباهات رایج در آموزشهای قدیمی این است که فایل CSS یا JavaScript را مستقیم با تگ <link> یا <script> داخل header.php قرار میدهند. وردپرس برای این کار سازوکار استاندارد wp_enqueue_style() و wp_enqueue_script() را دارد. در functions.php بالا همین روش را استفاده کردهایم.
حالا پوشههای زیر را بسازید:
hamyarwp-starter/
└── assets/
├── css/
│ └── main.css
└── js/
└── main.js
فایل style.css همچنان در ریشه Theme میماند؛ چون وردپرس شناسنامه قالب را از همانجا میخواند. استایلهای اصلی رابط کاربری را میتوانیم برای نظم بیشتر در assets/css/main.css قرار دهیم. فایل JavaScript آموزشی ما فعلاً بسیار ساده است:
document.documentElement.classList.add('js');
مزیت enqueue این است که وردپرس وابستگیها، ترتیب و محل بارگذاری منابع را مدیریت میکند و افزونهها نیز میتوانند با این منابع بهتر تعامل داشته باشند. برای جزئیات بیشتر میتوانید بخش Custom Functionality در Theme Handbook را ببینید.
مرحله 7: header.php و footer.php را جدا کنید
اگر تمام HTML صفحه را داخل index.php بنویسیم، خیلی زود تکرار کد شروع میشود. بخش سربرگ را در header.php و پابرگ را در footer.php قرار میدهیم و در Templateها با get_header() و get_footer() فراخوانی میکنیم.
محتوای header.php پروژه:
<!doctype html>
<html <?php language_attributes(); ?>>
<head>
<meta charset="<?php bloginfo( 'charset' ); ?>">
<meta name="viewport" content="width=device-width, initial-scale=1">
<?php wp_head(); ?>
</head>
<body <?php body_class(); ?>>
<?php wp_body_open(); ?>
<a class="skip-link screen-reader-text" href="#primary">
<?php esc_html_e( 'رفتن به محتوای اصلی', 'hamyarwp-starter' ); ?>
</a>
<header class="site-header">
<div class="container header-inner">
<div class="site-branding">
<?php if ( has_custom_logo() ) : ?>
<?php the_custom_logo(); ?>
<?php endif; ?>
<a class="site-title" href="<?php echo esc_url( home_url( '/' ) ); ?>">
<?php echo esc_html( get_bloginfo( 'name' ) ); ?>
</a>
</div>
<?php
wp_nav_menu(
array(
'theme_location' => 'primary',
'container' => 'nav',
'container_class'=> 'primary-navigation',
'menu_class' => 'menu',
'fallback_cb' => false,
)
);
?>
</div>
</header>
سه فراخوانی در این فایل بسیار مهم هستند:
wp_head()قبل از بسته شدن<head>قرار میگیرد تا وردپرس و افزونهها بتوانند منابع و دادههای لازم را به Head اضافه کنند.body_class()کلاسهای زمینهای وردپرس را رویbodyقرار میدهد.wp_body_open()بلافاصله پس از باز شدنbodyاجرا میشود و نقطه اتصال استانداردی برای افزونهها و کدهای دیگر است.
فایل footer.php نیز به این شکل است:
<footer class="site-footer">
<div class="container">
<p>
<?php
printf(
/* translators: %s: site name. */
esc_html__( 'تمام حقوق برای %s محفوظ است.', 'hamyarwp-starter' ),
esc_html( get_bloginfo( 'name' ) )
);
?>
</p>
</div>
</footer>
<?php wp_footer(); ?>
</body>
</html>
wp_footer() باید قبل از بسته شدن body وجود داشته باشد؛ بسیاری از اسکریپتها و افزونهها برای بارگذاری صحیح به این هوک وابستهاند.
مرحله 8: حلقه وردپرس را در index.php پیادهسازی کنید
وردپرس برای نمایش نوشتههای پیدا شده توسط Query اصلی از الگویی به نام The Loop استفاده میکند. تابع have_posts() بررسی میکند آیا نتیجه دیگری وجود دارد و the_post() اشارهگر حلقه را روی نوشته بعدی قرار میدهد. سپس Template Tagهایی مانند the_title()، the_excerpt() و the_post_thumbnail() اطلاعات همان نوشته را نمایش میدهند.
محتوای قبلی index.php را با نسخه کامل زیر جایگزین کنید:
<?php
get_header();
?>
<main id="primary" class="site-main container">
<?php if ( have_posts() ) : ?>
<div class="posts-grid">
<?php while ( have_posts() ) : ?>
<?php the_post(); ?>
<article id="post-<?php the_ID(); ?>" <?php post_class( 'post-card' ); ?>>
<?php if ( has_post_thumbnail() ) : ?>
<a class="post-card__image" href="<?php the_permalink(); ?>" aria-hidden="true" tabindex="-1">
<?php the_post_thumbnail( 'large' ); ?>
</a>
<?php endif; ?>
<h2 class="post-card__title">
<a href="<?php the_permalink(); ?>"><?php the_title(); ?></a>
</h2>
<p class="post-card__meta"><?php echo esc_html( get_the_date() ); ?></p>
<div class="post-card__excerpt">
<?php the_excerpt(); ?>
</div>
</article>
<?php endwhile; ?>
</div>
<?php the_posts_pagination(); ?>
<?php else : ?>
<p><?php esc_html_e( 'محتوایی برای نمایش پیدا نشد.', 'hamyarwp-starter' ); ?></p>
<?php endif; ?>
</main>
<?php
get_footer();
در این نسخه، عنوان هر نوشته به پیوند دائمی خودش متصل است، تصویر شاخص در صورت وجود نمایش داده میشود، تاریخ و خلاصه نوشته در کارت قرار میگیرند و در پایان the_posts_pagination() صفحهبندی آرشیو را میسازد. اگر هیچ نوشتهای وجود نداشته باشد نیز پیام مشخصی نشان داده میشود.
مرحله 9: فایل single.php را برای صفحه نوشته بسازید
اگر single.php وجود نداشته باشد، وردپرس در سلسلهمراتب Templateها سراغ گزینههای عمومیتر میرود و ممکن است نهایتاً از index.php استفاده کند. اما صفحه نوشته معمولاً چیدمان متفاوتی از آرشیو نیاز دارد؛ بنابراین فایل جداگانه میسازیم:
<?php
get_header();
?>
<main id="primary" class="site-main container">
<?php while ( have_posts() ) : ?>
<?php the_post(); ?>
<article id="post-<?php the_ID(); ?>" <?php post_class( 'single-entry' ); ?>>
<header class="entry-header">
<h1><?php the_title(); ?></h1>
<p class="entry-meta"><?php echo esc_html( get_the_date() ); ?></p>
</header>
<?php if ( has_post_thumbnail() ) : ?>
<div class="entry-thumbnail">
<?php the_post_thumbnail( 'large' ); ?>
</div>
<?php endif; ?>
<div class="entry-content">
<?php
the_content();
wp_link_pages(
array(
'before' => '<div class="page-links">' . esc_html__( 'صفحهها:', 'hamyarwp-starter' ),
'after' => '</div>',
)
);
?>
</div>
</article>
<?php
if ( comments_open() || get_comments_number() ) {
comments_template();
}
?>
<?php endwhile; ?>
</main>
<?php
get_footer();
در این فایل عنوان، تاریخ، تصویر شاخص و محتوای کامل نوشته نمایش داده میشود. wp_link_pages() نیز اگر محتوای یک نوشته با Page Break به چند قسمت تقسیم شده باشد، پیوندهای صفحات را ایجاد میکند. در انتها در صورت فعال بودن دیدگاهها، comments_template() فایل دیدگاه را فراخوانی میکند.
مرحله 10: page.php را برای برگهها بسازید
برگهها معمولاً متادیتای کمتری نسبت به نوشتهها نیاز دارند. برای همین یک Template سادهتر با نام page.php میسازیم:
<?php
get_header();
?>
<main id="primary" class="site-main container">
<?php while ( have_posts() ) : ?>
<?php the_post(); ?>
<article id="post-<?php the_ID(); ?>" <?php post_class( 'single-entry' ); ?>>
<header class="entry-header">
<h1><?php the_title(); ?></h1>
</header>
<div class="entry-content">
<?php the_content(); ?>
</div>
</article>
<?php endwhile; ?>
</main>
<?php
get_footer();
بعداً میتوانید برای برگههای خاص Templateهای دقیقتری مثل page-contact.php یا Page Template سفارشی ایجاد کنید، اما برای یادگیری مسیر پایه همین فایل کافی است.
مرحله 11: archive.php را برای دستهبندیها و آرشیوها اضافه کنید
صفحات دستهبندی، برچسب، نویسنده و آرشیوهای زمانی میتوانند از archive.php استفاده کنند. نسخه آموزشی ما عنوان و توضیح آرشیو را نمایش میدهد و سپس نوشتهها را با Loop فهرست میکند:
<?php
get_header();
?>
<main id="primary" class="site-main container">
<header class="archive-header">
<?php the_archive_title( '<h1>', '</h1>' ); ?>
<?php the_archive_description( '<div class="archive-description">', '</div>' ); ?>
</header>
<?php if ( have_posts() ) : ?>
<div class="posts-grid">
<?php while ( have_posts() ) : ?>
<?php the_post(); ?>
<article id="post-<?php the_ID(); ?>" <?php post_class( 'post-card' ); ?>>
<h2 class="post-card__title">
<a href="<?php the_permalink(); ?>"><?php the_title(); ?></a>
</h2>
<div class="post-card__excerpt">
<?php the_excerpt(); ?>
</div>
</article>
<?php endwhile; ?>
</div>
<?php the_posts_pagination(); ?>
<?php else : ?>
<p><?php esc_html_e( 'مطلبی در این آرشیو پیدا نشد.', 'hamyarwp-starter' ); ?></p>
<?php endif; ?>
</main>
<?php
get_footer();
مرحله 12: search.php و 404.php را فراموش نکنید
یک Theme آموزشی که فقط صفحه اصلی را نمایش دهد هنوز برای استفاده واقعی ناقص است. کاربران ممکن است جستجو کنند یا به آدرس اشتباه برسند. فایل search.php نتایج جستجو را مدیریت میکند:
<?php
get_header();
?>
<main id="primary" class="site-main container">
<header class="archive-header">
<h1>
<?php esc_html_e( 'نتایج جستجو برای:', 'hamyarwp-starter' ); ?>
<?php echo esc_html( get_search_query() ); ?>
</h1>
</header>
<?php if ( have_posts() ) : ?>
<div class="posts-grid">
<?php while ( have_posts() ) : ?>
<?php the_post(); ?>
<article id="post-<?php the_ID(); ?>" <?php post_class( 'post-card' ); ?>>
<h2 class="post-card__title">
<a href="<?php the_permalink(); ?>"><?php the_title(); ?></a>
</h2>
<div class="post-card__excerpt">
<?php the_excerpt(); ?>
</div>
</article>
<?php endwhile; ?>
</div>
<?php the_posts_pagination(); ?>
<?php else : ?>
<p><?php esc_html_e( 'نتیجهای پیدا نشد. عبارت دیگری را جستجو کنید.', 'hamyarwp-starter' ); ?></p>
<?php get_search_form(); ?>
<?php endif; ?>
</main>
<?php
get_footer();
و فایل 404.php برای آدرسهایی است که محتوای معتبری ندارند:
<?php
get_header();
?>
<main id="primary" class="site-main container">
<section class="error-404 not-found">
<h1><?php esc_html_e( 'صفحه موردنظر پیدا نشد', 'hamyarwp-starter' ); ?></h1>
<p><?php esc_html_e( 'ممکن است آدرس تغییر کرده باشد. از جستجو استفاده کنید یا به صفحه اصلی برگردید.', 'hamyarwp-starter' ); ?></p>
<?php get_search_form(); ?>
<p>
<a href="<?php echo esc_url( home_url( '/' ) ); ?>">
<?php esc_html_e( 'بازگشت به صفحه اصلی', 'hamyarwp-starter' ); ?>
</a>
</p>
</section>
</main>
<?php
get_footer();
ساخت این دو Template هم تجربه کاربری را بهتر میکند و هم به شما نشان میدهد چگونه WordPress بر اساس نوع درخواست، فایل متفاوتی را انتخاب میکند.
مرحله 13: فایل comments.php را برای دیدگاهها ایجاد کنید
اگر نوشتههای سایت دیدگاه دارند، قالب باید نمایش فهرست نظرات و فرم ارسال دیدگاه را هم مدیریت کند. فایل ساده زیر برای پروژه ما کافی است:
<?php
if ( post_password_required() ) {
return;
}
?>
<section id="comments" class="comments-area">
<?php if ( have_comments() ) : ?>
<h2 class="comments-title">
<?php esc_html_e( 'دیدگاهها', 'hamyarwp-starter' ); ?>
</h2>
<ol class="comment-list">
<?php
wp_list_comments(
array(
'style' => 'ol',
'short_ping' => true,
)
);
?>
</ol>
<?php the_comments_pagination(); ?>
<?php endif; ?>
<?php comment_form(); ?>
</section>
این فایل فقط زمانی توسط single.php فراخوانی میشود که دیدگاهها باز باشند یا نوشته قبلاً دیدگاهی داشته باشد. برای قالب تجاری میتوانید نمایش Avatar، متادیتا، پاسخها و حالتهای مختلف Comment را بیشتر سفارشی کنید.
مرحله 14: منوی وردپرس را فعال و در هدر نمایش دهید
در functions.php محل منویی با شناسه primary ثبت کردهایم و در header.php با wp_nav_menu() آن را نمایش میدهیم. بعد از فعالسازی قالب، یک فهرست در پیشخوان بسازید و آن را به محل «منوی اصلی» اختصاص دهید. از اینجا به بعد مدیر سایت میتواند بدون ویرایش فایل Theme، آیتمهای منو را تغییر دهد.
اگر register_nav_menus() را ننویسید و لینکها را مستقیم داخل HTML هدر قرار دهید، قالب از نظر مدیریت محتوا انعطاف خود را از دست میدهد. این یکی از تفاوتهای مهم بین یک HTML ثابت و Theme واقعی وردپرس است.
مرحله 15: تصویر شاخص را فعال و آزمایش کنید
قابلیت تصویر شاخص با این خط در تابع setup فعال شده است:
add_theme_support( 'post-thumbnails' );
بعد از آن در Template میتوانید با has_post_thumbnail() وجود تصویر را بررسی کنید و با the_post_thumbnail() آن را نمایش دهید. نکته مهم این است که فقط فراخوانی the_post_thumbnail() کافی نیست؛ اگر Theme پشتیبانی از Post Thumbnail را ثبت نکرده باشد، گزینه تصویر شاخص ممکن است در ویرایش نوشته در دسترس نباشد.
مرحله 16: theme.json را اضافه کنید و قالب کلاسیک را مدرنتر کنید
theme.json فقط مخصوص Block Theme نیست. وردپرس اجازه میدهد قالبهای کلاسیک نیز از بخشی از تنظیمات و استایلهای سراسری آن استفاده کنند. برای پروژه آموزشی ما فایل زیر را در ریشه Theme قرار میدهیم:
{
"$schema": "https://schemas.wp.org/trunk/theme.json",
"version": 3,
"settings": {
"appearanceTools": true,
"layout": {
"contentSize": "760px",
"wideSize": "1180px"
},
"color": {
"palette": [
{
"slug": "primary",
"name": "Primary",
"color": "#0f766e"
},
{
"slug": "text",
"name": "Text",
"color": "#1f2937"
},
{
"slug": "surface",
"name": "Surface",
"color": "#ffffff"
}
]
},
"typography": {
"fluid": true
}
},
"styles": {
"spacing": {
"blockGap": "1.25rem"
}
}
در این فایل اندازه محتوای معمولی و عریض، یک پالت رنگ ساده و برخی ابزارهای ظاهری تعریف شدهاند. در زمان بروزرسانی این مقاله، نسخه 3 آخرین نسخه مرجع theme.json در مستندات رسمی است. اگر در آینده WordPress schema را تغییر داد، این بخش باید دوباره با مرجع رسمی theme.json بررسی شود.
مرحله 17: ظاهر قالب را با CSS واکنشگرا کامل کنید
تا اینجا Theme از نظر منطق کار میکند، اما برای تبدیل شدن به یک خروجی قابل استفاده به CSS نیاز داریم. فایل assets/css/main.css پروژه نمونه شامل Container، Header، Grid نوشتهها، صفحه نوشته، صفحهبندی و Breakpointهای موبایل است:
* {
box-sizing: border-box;
}
html {
scroll-behavior: smooth;
}
body {
margin: 0;
background: #f8fafc;
color: #1f2937;
font-family: Tahoma, Arial, sans-serif;
line-height: 1.9;
}
a {
color: #0f766e;
}
img {
max-width: 100%;
height: auto;
}
.container {
width: min(1180px, calc(100% - 32px));
margin-inline: auto;
}
.site-header,
.site-footer {
background: #ffffff;
border-bottom: 1px solid #e5e7eb;
}
.site-footer {
margin-top: 48px;
border-top: 1px solid #e5e7eb;
border-bottom: 0;
padding: 24px 0;
}
.header-inner {
min-height: 76px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 24px;
}
.site-branding {
display: flex;
align-items: center;
gap: 12px;
}
.site-title {
font-size: 1.25rem;
font-weight: 700;
text-decoration: none;
}
.menu {
display: flex;
flex-wrap: wrap;
gap: 18px;
margin: 0;
padding: 0;
list-style: none;
}
.site-main {
padding-top: 40px;
}
.posts-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 24px;
}
.post-card,
.single-entry,
.archive-header,
.error-404,
.comments-area {
background: #ffffff;
border: 1px solid #e5e7eb;
border-radius: 16px;
padding: 24px;
}
.post-card__image img,
.entry-thumbnail img {
display: block;
width: 100%;
border-radius: 12px;
}
.post-card__title {
font-size: 1.25rem;
line-height: 1.6;
margin: 16px 0 8px;
}
.post-card__title a {
color: #111827;
text-decoration: none;
}
.post-card__meta,
.entry-meta {
color: #6b7280;
font-size: .9rem;
}
.single-entry {
max-width: 860px;
margin-inline: auto;
}
.entry-content {
margin-top: 24px;
}
.entry-content > * {
max-width: 760px;
margin-inline: auto;
}
.entry-content > .alignwide {
max-width: 1080px;
}
.entry-content > .alignfull {
max-width: none;
width: 100%;
}
.navigation.pagination {
margin-top: 32px;
}
.nav-links {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.page-numbers {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 42px;
min-height: 42px;
padding: 6px 10px;
background: #ffffff;
border: 1px solid #d1d5db;
border-radius: 8px;
text-decoration: none;
}
.page-numbers.current {
background: #0f766e;
color: #ffffff;
border-color: #0f766e;
}
.screen-reader-text {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
.screen-reader-text:focus {
width: auto;
height: auto;
margin: 12px;
padding: 10px 14px;
clip: auto;
background: #ffffff;
z-index: 9999;
}
@media (max-width: 900px) {
.posts-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@media (max-width: 680px) {
.header-inner {
align-items: flex-start;
flex-direction: column;
padding-block: 18px;
}
.menu {
gap: 12px;
}
.posts-grid {
grid-template-columns: 1fr;
}
.site-main {
padding-top: 24px;
}
}
برای پروژه واقعی بهتر است CSS را بر اساس طراحی رابط کاربری خودتان توسعه دهید. از همان ابتدا نمایش موبایل را آزمایش کنید؛ واکنشگرایی چیزی نیست که در آخر پروژه با چند خط Media Query به قالب اضافه شود. اگر هدف شما بهینهسازی بیشتر عملکرد سایت است، راهنمای افزایش سرعت وردپرس میتواند در مرحله تست نهایی مفید باشد.
مرحله 18: screenshot.png و ساختار نهایی پوشه را آماده کنید
وردپرس میتواند تصویر screenshot.png را در صفحه پوستهها نمایش دهد. برای قالبی که قرار است در دایرکتوری رسمی WordPress.org منتشر شود، الزامات Theme Review اهمیت پیدا میکنند و تصویر Theme نباید بزرگتر از 1200 در 900 پیکسل باشد. برای پروژه شخصی نیز بهتر است تصویر واقعی خروجی نهایی قالب را قرار دهید تا در پیشخوان قابل تشخیص باشد.
ساختار نهایی پروژه ما به این شکل است:
hamyarwp-starter/
├── 404.php
├── archive.php
├── comments.php
├── footer.php
├── functions.php
├── header.php
├── index.php
├── page.php
├── search.php
├── single.php
├── style.css
├── theme.json
├── screenshot.png
└── assets/
├── css/
│ └── main.css
└── js/
└── main.js

وردپرس از کجا میفهمد کدام فایل قالب را نمایش دهد؟
یکی از مهمترین مفاهیم توسعه Theme، Template Hierarchy است. وردپرس نوع درخواست کاربر را تشخیص میدهد و از خاصترین Template موجود به سمت فایلهای عمومیتر حرکت میکند. بنابراین وجود index.php به این معنا نیست که باید همه صفحهها را داخل همان فایل بنویسیم؛ index.php در قالب کلاسیک آخرین گزینه عمومی بسیاری از مسیرهاست.
| صفحهای که کاربر میبیند | Template اصلی پیشنهادی | نمونه مسیر fallback |
|---|---|---|
| یک نوشته | single.php |
single.php → singular.php → index.php |
| یک برگه | page.php |
page-{slug}.php → page-{id}.php → page.php → singular.php → index.php |
| دستهبندی | category.php یا archive.php |
category-{slug}.php → category-{id}.php → category.php → archive.php → index.php |
| نتایج جستجو | search.php |
search.php → index.php |
| صفحه 404 | 404.php |
404.php → index.php |
| آرشیو عمومی | archive.php |
Templateهای دقیقتر آرشیو → archive.php → index.php |
برای پروژههای جدی حتماً نمودار و جزئیات Template Hierarchy در مستندات رسمی وردپرس را کنار دستتان داشته باشید. وقتی این سلسلهمراتب را بفهمید، تصمیمگیری درباره اینکه هر بخش را در چه فایلی بسازید بسیار سادهتر میشود.
اگر یک قالب HTML آماده داشته باشیم، چگونه آن را به وردپرس تبدیل کنیم؟
بسیاری از پروژهها از یک طرح HTML/CSS آماده شروع میشوند. در این حالت بهجای کپی کردن کل فایل HTML در index.php و نگه داشتن مسیرهای ثابت، باید ساختار را به بخشهای منطقی WordPress تبدیل کنید.
- HTML مربوط به سربرگ را به
header.phpمنتقل کنید. - پابرگ را در
footer.phpقرار دهید. - CSS و JavaScript را از طریق enqueue در
functions.phpبارگذاری کنید. - لینکهای ثابت منابع را با توابع مسیر وردپرس جایگزین کنید.
- محتوای تکرارشونده نوشتهها را با Loop و Template Tagها داینامیک کنید.
- منوی HTML ثابت را با
wp_nav_menu()جایگزین کنید. - صفحه نوشته، برگه، آرشیو، جستجو و 404 را به Templateهای جدا منتقل کنید.
- در نهایت خروجی موبایل، فرمها، افزونهها و حالت بدون محتوا را آزمایش کنید.
در مسیر تبدیل HTML به وردپرس، Hard-code کردن URL فایلها یکی از خطاهای رایج است. مثلاً نباید فرض کنید پوشه Theme همیشه در یک URL ثابت قرار دارد؛ برای منابع قالب از توابع وردپرس مانند get_template_directory_uri() و برای پیوند صفحه اصلی از home_url() استفاده کنید.
فایلهای مهم قالب وردپرس و وظیفه هر کدام
| فایل | وظیفه | آیا در پروژه آموزشی استفاده کردیم؟ |
|---|---|---|
style.css |
شناسنامه Theme و استایل پایه | بله |
index.php |
Template عمومی و fallback قالب کلاسیک | بله |
functions.php |
قابلیتها، منوها، هوکها و منابع قالب | بله |
header.php |
Head، شروع Body و سربرگ سایت | بله |
footer.php |
پابرگ، wp_footer() و پایان سند |
بله |
single.php |
صفحه یک نوشته | بله |
page.php |
صفحه یک برگه | بله |
archive.php |
آرشیوها و fallback دسته/برچسب/نویسنده | بله |
search.php |
نتایج جستجو | بله |
404.php |
صفحه خطای پیدا نشدن محتوا | بله |
comments.php |
فهرست و فرم دیدگاه | بله |
front-page.php |
صفحه اول سایت در صورت وجود | در توسعه بعدی |
home.php |
صفحه فهرست نوشتهها | در توسعه بعدی |
theme.json |
تنظیمات و استایلهای سراسری ویرایشگر و Theme | بله |
چند اصل فنی که قالب شما را از یک تمرین ساده جدا میکند
فایلهای CSS و JS را مستقیم Hard-code نکنید
منابع را با API استاندارد وردپرس enqueue کنید. این روش با سیستم وابستگی وردپرس سازگار است و امکان مدیریت نسخه، ترتیب و بارگذاری منابع را فراهم میکند.
wp_head، wp_body_open و wp_footer را حذف نکنید
قالب ممکن است ظاهراً بدون این هوکها هم صفحهای نشان دهد، اما بسیاری از افزونهها و قابلیتهای هسته برای تزریق کدهای لازم به آنها وابستهاند. نبودن این فراخوانیها میتواند مشکلهایی ایجاد کند که در نگاه اول به خود Theme مربوط به نظر نمیرسند.
برای توابع اختصاصی پیشوند یکتا انتخاب کنید
تعریف تابعی عمومی مثل setup() یا load_scripts() احتمال برخورد نام با افزونه یا قالب دیگر را بالا میبرد. پیشوند پروژه را در نام تابع، Handle منابع و شناسههای اختصاصی استفاده کنید.
خروجیهای داینامیک را متناسب با Context امن کنید
در جایی که URL را با echo چاپ میکنید از esc_url() استفاده کنید و برای رشتههای سادهای که باید فقط متن باشند esc_html() مناسب است. اگر قالب ورودی سفارشی دریافت میکند، Sanitize کردن داده هنگام ذخیره و Escape هنگام خروجی بخش مهمی از امنیت Theme است.
قابلیت حیاتی سایت را داخل قالب قفل نکنید
اگر داده یا قابلیتی باید بعد از تعویض Theme همچنان باقی بماند، احتمالاً جای آن در افزونه است. Theme باید تا حد امکان روی Presentation تمرکز کند. این اصل بعدها هنگام مهاجرت یا بازطراحی سایت جلوی وابستگیهای پرهزینه را میگیرد.
اشتباهات رایج در طراحی قالب وردپرس
| اشتباه | نتیجه احتمالی | راه درست |
|---|---|---|
نوشتن همه چیز در index.php |
کد تکراری و نگهداری دشوار | استفاده از Template Hierarchy و فایلهای جدا |
| بارگذاری مستقیم CSS/JS در Header | تداخل و کنترل ضعیف منابع | استفاده از enqueue در functions.php |
حذف wp_head() یا wp_footer() |
اختلال در افزونهها و اسکریپتها | قرار دادن هوکها در محل استاندارد |
| Hard-code کردن URL | خرابی مسیرها بعد از جابهجایی سایت یا تغییر پوشه | استفاده از توابع URL وردپرس |
فراموش کردن post-thumbnails |
نبودن قابلیت تصویر شاخص در Theme | ثبت قابلیت با add_theme_support() |
| استفاده از نام تابع عمومی | تداخل احتمالی با کد دیگر | Prefix کردن نامها |
| تست فقط صفحه اصلی | خرابی Single، Archive، Search یا 404 در زمان انتشار | اجرای چکلیست کامل Templateها |
اگر حین توسعه با خطای سفید، خطای PHP یا رفتار غیرمنتظره روبهرو شدید، راهنمای خطاهای رایج وردپرس میتواند برای شروع عیبیابی مفید باشد؛ اما در پروژه توسعه Theme، لاگ PHP و خطاهای دقیق محیط آزمایشی معمولاً سریعتر علت را مشخص میکنند.
چکلیست تست قالب قبل از استفاده روی سایت اصلی
فعال شدن Theme بدون خطا به معنی آماده بودن آن نیست. قبل از استفاده روی سایت واقعی، حداقل سناریوهای زیر را بررسی کنید:
| بخش تست | چه چیزی بررسی شود؟ |
|---|---|
| فعالسازی | قالب بدون Fatal Error فعال شود و پیشخوان در دسترس بماند. |
| صفحه اصلی و آرشیو | نوشتهها، صفحهبندی، تصاویر و حالت بدون نوشته درست باشند. |
| Single و Page | عنوان، محتوا، تصاویر، بلوکها و لینکهای داخلی بدون شکستگی نمایش داده شوند. |
| منو | منوی اصلی از پیشخوان قابل تغییر باشد و آیتمهای چندسطحی بررسی شوند. |
| دیدگاه | فهرست نظرات، پاسخها و فرم ارسال دیدگاه کار کنند. |
| Search و 404 | نتیجه جستجو و مسیرهای نامعتبر خروجی مناسب داشته باشند. |
| موبایل | در عرضهای مختلف اسکرول افقی ناخواسته، متن بریده یا منوی خراب نداشته باشید. |
| ویرایشگر بلوکی | تصویر، گالری، نقلقول، دکمه، ستون و بلوکهای رایج را آزمایش کنید. |
| افزونهها | حداقل افزونههای ضروری پروژه را در محیط آزمایشی فعال و صفحات کلیدی را بررسی کنید. |
| خطاها | لاگ PHP و Console مرورگر را برای Warning، Notice و خطاهای JavaScript بررسی کنید. |
برای سایت واقعی قالب اختصاصی بهتر است یا قالب آماده؟
یادگیری توسعه Theme به این معنی نیست که هر سایت الزاماً باید قالب اختصاصی داشته باشد. اگر نیازهای پروژه استاندارد است و زمان یا بودجه توسعه محدود دارید، یک Theme آماده باکیفیت میتواند انتخاب منطقیتری باشد. همیار وردپرس مجموعهای از قالبهای رایگان وردپرس را نیز برای سناریوهای مختلف معرفی کرده است.
| سناریو | قالب آماده | قالب اختصاصی |
|---|---|---|
| راهاندازی سریع | مناسبتر | زمان توسعه بیشتری نیاز دارد |
| طراحی کاملاً منحصربهفرد | محدود به امکانات Theme | انعطاف بسیار بیشتر |
| کنترل روی کد | وابسته به توسعهدهنده قالب | در اختیار تیم پروژه |
| هزینه شروع | معمولاً کمتر | معمولاً بیشتر |
| نیازهای بسیار اختصاصی | ممکن است نیازمند افزونه و Override زیاد باشد | قابل طراحی بر اساس نیاز واقعی |
اگر هدف شما یادگیری توسعه وردپرس است، ساختن حداقل یک Theme از صفر تمرین بسیار ارزشمندی است؛ حتی اگر بعداً در پروژههای واقعی از Starter Theme، قالب آماده یا Block Theme استفاده کنید. با این تجربه متوجه میشوید وقتی یک صفحه وردپرس لود میشود، پشت صحنه چه اتفاقی میافتد.
مرحله بعد بعد از ساخت این قالب چیست؟
قالبی که ساختیم یک پایه آموزشی تمیز است، نه محصول نهایی بازار. مسیر توسعه بعدی میتواند شامل front-page.php، home.php، Template Partها، Sidebar و Widget Area، فرم جستجوی سفارشی، Breadcrumb، پشتیبانی دقیقتر از ویرایشگر، ترجمهپذیری کامل، تست دسترسپذیری، WooCommerce Template Support و در پروژههای جدیدتر یادگیری Block Theme باشد.
اگر تصمیم دارید این Theme را برای یک سایت واقعی توسعه دهید، هر قابلیت را فقط زمانی اضافه کنید که نیاز پروژه آن را توجیه میکند. بزرگ شدن بیهدف functions.php یا اضافه کردن دهها Template که هیچوقت استفاده نمیشوند، قالب را حرفهایتر نمیکند؛ ساختار روشن، خروجی قابل پیشبینی و کد قابل نگهداری مهمتر است.
جمعبندی
در این آموزش طراحی قالب وردپرس را از یک پوشه خالی شروع کردیم و مرحلهبهمرحله به Themeی رسیدیم که نوشتهها را با Loop نمایش میدهد، برای نوشته، برگه، آرشیو، جستجو و خطای 404 Template جدا دارد، منو و تصویر شاخص را ثبت میکند، فایلهای CSS و JavaScript را با روش استاندارد وردپرس بارگذاری میکند و از theme.json برای بخشی از قابلیتهای مدرن استفاده میکند.
مهمترین چیزی که باید از این آموزش بماند، حفظ کردن نام توابع نیست؛ باید منطق Theme را بفهمید: وردپرس درخواست را دریافت میکند، طبق Template Hierarchy فایل مناسب را انتخاب میکند، Query و Loop داده را در اختیار Template میگذارند و هوکها و APIهای Theme اجازه میدهند بدون Hard-code کردن همه چیز، خروجی قابل توسعه بسازید. از اینجا به بعد میتوانید همین پروژه را به طراحی واقعی خودتان تبدیل کنید.