Разработка кастомной темы WordPress с нуля
Кастомная тема — единственный правильный путь, когда дизайн не вписывается в рамки готовых шаблонов, нужна максимальная производительность без балласта сторонних фреймворков, или сайт должен точно соответствовать фирменному стилю. Разработка ведётся на PHP + современный CSS + vanilla JS или сборщик.
Структура темы
my-theme/
├── style.css # Заголовок темы (обязательно)
├── functions.php # Хуки, регистрация ресурсов
├── index.php # Fallback шаблон
├── header.php
├── footer.php
├── single.php # Шаблон записи
├── page.php # Шаблон страницы
├── archive.php
├── search.php
├── 404.php
├── front-page.php # Главная страница
├── template-parts/
│ ├── content-post.php
│ ├── content-card.php
│ └── navigation.php
├── inc/
│ ├── theme-setup.php
│ ├── enqueue.php
│ ├── custom-post-types.php
│ └── acf-fields.php
├── src/
│ ├── scss/
│ └── js/
└── package.json
functions.php: правильная структура
<?php
// Подключаем модульно, не пишем всё в один файл
require get_template_directory() . '/inc/theme-setup.php';
require get_template_directory() . '/inc/enqueue.php';
require get_template_directory() . '/inc/custom-post-types.php';
inc/theme-setup.php:
<?php
function mytheme_setup(): void {
add_theme_support('title-tag');
add_theme_support('post-thumbnails');
add_theme_support('html5', ['search-form', 'comment-form', 'gallery', 'caption', 'style', 'script']);
add_theme_support('custom-logo', [
'height' => 60,
'width' => 200,
'flex-height' => true,
]);
add_theme_support('woocommerce'); // если нужно
load_theme_textdomain('mytheme', get_template_directory() . '/languages');
register_nav_menus([
'primary' => __('Главное меню', 'mytheme'),
'footer' => __('Меню подвала', 'mytheme'),
]);
}
add_action('after_setup_theme', 'mytheme_setup');
Подключение ресурсов
function mytheme_enqueue_assets(): void {
$theme_version = wp_get_theme()->get('Version');
// CSS
wp_enqueue_style(
'mytheme-style',
get_template_directory_uri() . '/dist/css/main.css',
[],
$theme_version
);
// JS (defer для производительности)
wp_enqueue_script(
'mytheme-main',
get_template_directory_uri() . '/dist/js/main.js',
[],
$theme_version,
['strategy' => 'defer', 'in_footer' => true]
);
// Передача данных в JS
wp_localize_script('mytheme-main', 'siteData', [
'ajaxUrl' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('mytheme_nonce'),
'homeUrl' => home_url(),
]);
}
add_action('wp_enqueue_scripts', 'mytheme_enqueue_assets');
Шаблон записи (single.php)
<?php get_header(); ?>
<main id="main" class="site-main">
<?php
while (have_posts()) :
the_post(); ?>
<article id="post-<?php the_ID(); ?>" <?php post_class('entry'); ?>>
<header class="entry-header">
<?php if (has_post_thumbnail()) : ?>
<div class="entry-thumbnail">
<?php the_post_thumbnail('large', ['loading' => 'eager', 'fetchpriority' => 'high']); ?>
</div>
<?php endif; ?>
<h1 class="entry-title"><?php the_title(); ?></h1>
<div class="entry-meta">
<time datetime="<?php echo get_the_date('c'); ?>">
<?php echo get_the_date(); ?>
</time>
<?php
$categories = get_the_category();
if ($categories) {
echo '<span class="entry-categories">';
foreach ($categories as $category) {
echo '<a href="' . esc_url(get_category_link($category->term_id)) . '">'
. esc_html($category->name) . '</a>';
}
echo '</span>';
} ?>
</div>
</header>
<div class="entry-content">
<?php the_content(); ?>
</div>
</article>
<?php the_post_navigation([
'prev_text' => '<span class="nav-label">Предыдущая</span> <span class="nav-title">%title</span>',
'next_text' => '<span class="nav-label">Следующая</span> <span class="nav-title">%title</span>',
]); ?>
<?php endwhile; ?>
</main>
<?php get_sidebar(); ?>
<?php get_footer(); ?>
Сборка с Vite
// vite.config.js
import { defineConfig } from 'vite';
import liveReload from 'vite-plugin-live-reload';
export default defineConfig({
plugins: [liveReload('**/*.php')],
build: {
outDir: 'dist',
rollupOptions: {
input: {
main: 'src/js/main.js',
admin: 'src/js/admin.js',
},
},
},
css: {
preprocessorOptions: {
scss: { additionalData: '@use "src/scss/variables" as *;' },
},
},
});
ACF для кастомных полей
Кастомные поля через ACF Pro — стандарт для сложных тем:
// Регистрация полей через PHP (не через UI — для версионирования)
if (function_exists('acf_add_local_field_group')) {
acf_add_local_field_group([
'key' => 'group_hero_section',
'title' => 'Секция Hero',
'fields' => [
[
'key' => 'field_hero_heading',
'label' => 'Заголовок',
'name' => 'hero_heading',
'type' => 'text',
],
[
'key' => 'field_hero_image',
'label' => 'Фоновое изображение',
'name' => 'hero_image',
'type' => 'image',
'return_format' => 'array',
],
],
'location' => [
[['param' => 'page_template', 'operator' => '==', 'value' => 'template-home.php']],
],
]);
}
Сроки
Базовая тема с шаблонами (главная, блог, страница, 404) без дизайна — 2–3 дня. Тема по готовому дизайну с 10–15 типами страниц, ACF-полями и кастомными типами записей — 2–3 недели.







