自定义主题开发
本章节通过实战案例,手把手教你从零创建一个完整的 WordPress 主题。
🎯 项目规划
主题功能清单
| 功能 | 说明 |
|---|---|
| 响应式布局 | 移动端友好 |
| 深色模式 | 自动/手动切换 |
| 自定义 Logo | 支持上传 |
| 自定义菜单 | 多菜单位置 |
| 侧边栏 | 首页/文章页独立 |
| 评论区 | 自定义评论区样式 |
| SEO 优化 | 结构化数据支持 |
| 无障碍 | WCAG 2.1 支持 |
📁 创建主题目录
bash
wp-content/themes/
└── my-awesome-theme/
├── style.css # 主题信息
├── functions.php # 主题功能
├── index.php # 主模板
├── header.php # 页头
├── footer.php # 页脚
├── sidebar.php # 侧边栏
├── comments.php # 评论区
├── screenshot.png # 主题截图
├── 404.php # 404页面
├── search.php # 搜索结果
├── archive.php # 归档页
├── single.php # 文章页
├── page.php # 页面模板
├── front-page.php # 首页
├── template-parts/ # 模板部件
│ ├── content.php
│ ├── content-single.php
│ └── content-none.php
├── template-fullwidth.php # 全宽模板
├── css/
│ └── custom.css # 自定义样式
├── js/
│ └── theme.js # 主题脚本
├── inc/
│ ├── template-tags.php # 模板标签
│ └── customizer.php # 自定义器
└── languages/ # 国际化
└── my-awesome-theme.pot1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
🎨 创建 style.css
css
/*
Theme Name: My Awesome Theme
Theme URI: https://example.com/my-awesome-theme
Author: Your Name
Author URI: https://example.com
Description: 一个现代、简洁、响应式的 WordPress 主题
Version: 1.0.0
Requires at least: 6.0
Tested up to: 6.4
Requires PHP: 7.4
License: GNU General Public License v2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Text Domain: my-awesome-theme
Tags: blog, custom-logo, custom-menu, editor-style, featured-images, footer-widgets, full-width-template, post-formats, rtl-language-support, theme-options, threaded-comments, translation-ready, block-styles, wide-blocks, accessibility-ready
My Awesome Theme is distributed under the terms of the GNU GPL.
*/
/* ================================
CSS Custom Properties
================================ */
:root {
/* Colors */
--color-primary: #21759b;
--color-primary-dark: #185a7a;
--color-secondary: #f0f0f0;
--color-text: #333333;
--color-text-light: #666666;
--color-background: #ffffff;
--color-border: #e0e0e0;
/* Typography */
--font-primary: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, sans-serif;
--font-heading: inherit;
--font-size-base: 16px;
--line-height-base: 1.6;
/* Spacing */
--spacing-unit: 1rem;
--container-max: 1200px;
/* Transitions */
--transition-fast: 0.15s ease;
--transition-normal: 0.3s ease;
}
/* Dark Mode */
@media (prefers-color-scheme: dark) {
:root {
--color-text: #e0e0e0;
--color-text-light: #999999;
--color-background: #1a1a1a;
--color-border: #333333;
}
}
/* ================================
Reset & Base Styles
================================ */
*,
*::before,
*::after {
box-sizing: border-box;
}
html {
font-size: var(--font-size-base);
scroll-behavior: smooth;
}
body {
margin: 0;
padding: 0;
font-family: var(--font-primary);
line-height: var(--line-height-base);
color: var(--color-text);
background-color: var(--color-background);
}
/* ================================
Layout
================================ */
.site {
display: flex;
flex-direction: column;
min-height: 100vh;
}
.site-content {
flex: 1;
max-width: var(--container-max);
margin: 0 auto;
padding: calc(var(--spacing-unit) * 2);
width: 100%;
}
.container {
max-width: var(--container-max);
margin: 0 auto;
padding: 0 var(--spacing-unit);
}
/* ================================
Header
================================ */
.site-header {
background: var(--color-background);
border-bottom: 1px solid var(--color-border);
padding: var(--spacing-unit) 0;
}
.site-branding {
display: flex;
align-items: center;
}
.site-title {
margin: 0;
font-size: 1.75rem;
font-weight: 700;
}
.site-title a {
color: var(--color-primary);
text-decoration: none;
}
.site-description {
margin: 0.25rem 0 0;
color: var(--color-text-light);
font-size: 0.875rem;
}
/* Navigation */
.main-navigation {
margin-top: var(--spacing-unit);
}
.nav-menu {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-wrap: wrap;
gap: var(--spacing-unit);
}
.nav-menu li {
position: relative;
}
.nav-menu a {
display: block;
padding: 0.5rem 0;
color: var(--color-text);
text-decoration: none;
}
.nav-menu a:hover,
.nav-menu .current-menu-item a {
color: var(--color-primary);
}
/* ================================
Content
================================ */
.entry-header {
margin-bottom: var(--spacing-unit);
}
.entry-title {
font-size: 2rem;
margin: 0 0 var(--spacing-unit);
line-height: 1.2;
}
.entry-title a {
color: var(--color-text);
text-decoration: none;
}
.entry-meta {
color: var(--color-text-light);
font-size: 0.875rem;
}
.entry-content {
margin: var(--spacing-unit) 0;
}
.entry-footer {
padding-top: var(--spacing-unit);
border-top: 1px solid var(--color-border);
color: var(--color-text-light);
font-size: 0.875rem;
}
/* ================================
Footer
================================ */
.site-footer {
background: var(--color-background);
border-top: 1px solid var(--color-border);
padding: calc(var(--spacing-unit) * 2) 0;
text-align: center;
color: var(--color-text-light);
font-size: 0.875rem;
}
/* ================================
Components
================================ */
.button {
display: inline-block;
padding: 0.75rem 1.5rem;
background: var(--color-primary);
color: #fff;
text-decoration: none;
border-radius: 4px;
transition: background var(--transition-fast);
}
.button:hover {
background: var(--color-primary-dark);
}
/* ================================
Responsive
================================ */
@media (min-width: 768px) {
.site-content {
display: grid;
gap: calc(var(--spacing-unit) * 2);
}
.has-sidebar .site-main {
display: grid;
grid-template-columns: 1fr 300px;
gap: calc(var(--spacing-unit) * 2);
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
🔧 创建 functions.php
php
<?php
/**
* My Awesome Theme Functions
*
* @package My_Awesome_Theme
* @version 1.0.0
*/
if (!defined('ABSPATH')) {
exit;
}
// 定义常量
define('MY_AWESOME_THEME_VERSION', '1.0.0');
define('MY_AWESOME_THEME_DIR', get_template_directory());
define('MY_AWESOME_THEME_URL', get_template_directory_uri());
/**
* 主题设置
*/
function my_awesome_theme_setup() {
// 语言支持
load_theme_textdomain('my-awesome-theme', MY_AWESOME_THEME_DIR . '/languages');
// 自动加载 feed 链接
add_theme_support('automatic-feed-links');
// 文章缩略图
add_theme_support('post-thumbnails');
add_image_size('featured-image', 1200, 600, true);
add_image_size('thumbnail-medium', 600, 400, true);
// 自定义 Logo
add_theme_support('custom-logo', array(
'height' => 100,
'width' => 400,
'flex-height' => true,
'flex-width' => true,
));
// 注册菜单位置
register_nav_menus(array(
'primary' => __('Primary Menu', 'my-awesome-theme'),
'footer' => __('Footer Menu', 'my-awesome-theme'),
'social' => __('Social Links Menu', 'my-awesome-theme'),
));
// HTML5 支持
add_theme_support('html5', array(
'search-form',
'comment-form',
'comment-list',
'gallery',
'caption',
'style',
'script',
));
// 自定义背景
add_theme_support('custom-background', array(
'default-color' => 'ffffff',
));
// 标题标签支持
add_theme_support('title-tag');
// 编辑器样式
add_theme_support('editor-styles');
add_editor_style('css/editor-style.css');
// 块样式
add_theme_support('wp-block-styles');
// 宽对齐
add_theme_support('align-wide');
// 深色模式
add_theme_support('dark-editor-style');
}
add_action('after_setup_theme', 'my_awesome_theme_setup');
/**
* 设置内容宽度
*/
function my_awesome_theme_content_width() {
$GLOBALS['content_width'] = apply_filters('my_awesome_theme_content_width', 800);
}
add_action('after_setup_theme', 'my_awesome_theme_content_width', 0);
/**
* 注册侧边栏
*/
function my_awesome_theme_widgets_init() {
register_sidebar(array(
'name' => __('Primary Sidebar', 'my-awesome-theme'),
'id' => 'sidebar-1',
'description' => __('Add widgets here.', 'my-awesome-theme'),
'before_widget' => '<section id="%1$s" class="widget %2$s">',
'after_widget' => '</section>',
'before_title' => '<h2 class="widget-title">',
'after_title' => '</h2>',
));
register_sidebar(array(
'name' => __('Footer Widgets', 'my-awesome-theme'),
'id' => 'footer-widgets',
'description' => __('Footer widgets area.', 'my-awesome-theme'),
'before_widget' => '<div id="%1$s" class="footer-widget %2$s">',
'after_widget' => '</div>',
'before_title' => '<h3 class="widget-title">',
'after_title' => '</h3>',
));
}
add_action('widgets_init', 'my_awesome_theme_widgets_init');
/**
* 加载脚本和样式
*/
function my_awesome_theme_scripts() {
// 主样式
wp_enqueue_style(
'my-awesome-theme-style',
get_stylesheet_uri(),
array(),
MY_AWESOME_THEME_VERSION
);
// 主题脚本
wp_enqueue_script(
'my-awesome-theme-navigation',
MY_AWESOME_THEME_URL . '/js/navigation.js',
array(),
MY_AWESOME_THEME_VERSION,
true
);
// 评论回复脚本
if (is_singular() && comments_open() && get_option('thread_comments')) {
wp_enqueue_script('comment-reply');
}
}
add_action('wp_enqueue_scripts', 'my_awesome_theme_scripts');
/**
* 自定义文章类型支持
*/
function my_awesome_theme_custom_post_types() {
// 作品集
register_post_type('portfolio', array(
'labels' => array(
'name' => __('Portfolio', 'my-awesome-theme'),
'singular_name' => __('Portfolio Item', 'my-awesome-theme'),
),
'public' => true,
'has_archive' => true,
'show_in_rest' => true,
'supports' => array('title', 'editor', 'thumbnail', 'excerpt'),
'menu_icon' => 'dashicons-portfolio',
));
}
add_action('init', 'my_awesome_theme_custom_post_types');
/**
* 包含辅助函数
*/
require MY_AWESOME_THEME_DIR . '/inc/template-tags.php';
require MY_AWESOME_THEME_DIR . '/inc/customizer.php';1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
📄 创建 header.php
php
<?php
/**
* Header template
*
* @package My_Awesome_Theme
*/
?>
<!DOCTYPE html>
<html <?php language_attributes(); ?>>
<head>
<meta charset="<?php bloginfo('charset'); ?>">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="profile" href="https://gmpg.org/xfn/11">
<?php wp_head(); ?>
</head>
<body <?php body_class(); ?>>
<?php wp_body_open(); ?>
<div id="page" class="site">
<header id="masthead" class="site-header">
<div class="container">
<?php if (has_custom_logo()) : ?>
<?php the_custom_logo(); ?>
<?php else : ?>
<div class="site-branding">
<?php if (is_front_page() && is_home()) : ?>
<h1 class="site-title">
<a href="<?php echo esc_url(home_url('/')); ?>" rel="home">
<?php bloginfo('name'); ?>
</a>
</h1>
<?php else : ?>
<p class="site-title">
<a href="<?php echo esc_url(home_url('/')); ?>" rel="home">
<?php bloginfo('name'); ?>
</a>
</p>
<?php endif; ?>
<?php
$description = get_bloginfo('description', 'display');
if ($description || is_customize_preview()) :
?>
<p class="site-description"><?php echo $description; ?></p>
<?php endif; ?>
</div>
<?php endif; ?>
<nav id="site-navigation" class="main-navigation">
<?php
wp_nav_menu(array(
'theme_location' => 'primary',
'container' => false,
'menu_class' => 'nav-menu',
'fallback_cb' => function() {
echo '<ul class="nav-menu"><li><a href="' . admin_url('nav-menus.php') . '">Add Menu</a></li></ul>';
},
));
?>
</nav>
</div>
</header>
<main id="main" class="site-content">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
📄 创建 footer.php
php
<?php
/**
* Footer template
*
* @package My_Awesome_Theme
*/
?>
</main><!-- #main -->
<footer id="colophon" class="site-footer">
<div class="container">
<?php if (is_active_sidebar('footer-widgets')) : ?>
<div class="footer-widgets-area">
<?php dynamic_sidebar('footer-widgets'); ?>
</div>
<?php endif; ?>
<div class="site-info">
<p>
© <?php echo date('Y'); ?> <?php bloginfo('name'); ?>.
<?php printf(
esc_html__('Powered by %1$s and %2$s.', 'my-awesome-theme'),
'<a href="https://wordpress.org/">WordPress</a>',
'<a href="https://example.com/">My Awesome Theme</a>'
); ?>
</p>
</div>
</div>
</footer>
</div><!-- #page -->
<?php wp_footer(); ?>
</body>
</html>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
📄 创建 index.php (主模板)
php
<?php
/**
* Main template file
*
* @package My_Awesome_Theme
*/
get_header();
?>
<div class="site-main">
<?php if (have_posts()) : ?>
<?php if (is_home() && !is_front_page()) : ?>
<header class="page-header">
<h1 class="page-title"><?php single_post_title(); ?></h1>
</header>
<?php endif; ?>
<div class="posts-list">
<?php while (have_posts()) : the_post(); ?>
<?php get_template_part('template-parts/content', get_post_type()); ?>
<?php endwhile; ?>
</div>
<?php
the_posts_pagination(array(
'mid_size' => 2,
'prev_text' => __('Previous', 'my-awesome-theme'),
'next_text' => __('Next', 'my-awesome-theme'),
));
?>
<?php else : ?>
<?php get_template_part('template-parts/content', 'none'); ?>
<?php endif; ?>
</div>
<?php get_sidebar(); ?>
<?php get_footer(); ?>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
📄 创建模板部件
php
<?php
/**
* Template part for displaying posts
*
* @package My_Awesome_Theme
*/
?>
<article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
<header class="entry-header">
<?php if (is_singular()) : ?>
<?php the_title('<h1 class="entry-title">', '</h1>'); ?>
<?php else : ?>
<?php the_title('<h2 class="entry-title"><a href="' . esc_url(get_permalink()) . '" rel="bookmark">', '</a></h2>'); ?>
<?php endif; ?>
<div class="entry-meta">
<span class="posted-on">
<time datetime="<?php the_time('c'); ?>">
<?php echo get_the_date(); ?>
</time>
</span>
<span class="byline">
<?php esc_html_e('by', 'my-awesome-theme'); ?>
<a href="<?php echo get_author_posts_url(get_the_author_meta('ID')); ?>">
<?php the_author(); ?>
</a>
</span>
</div>
</header>
<?php if (has_post_thumbnail()) : ?>
<div class="post-thumbnail">
<?php the_post_thumbnail('featured-image'); ?>
</div>
<?php endif; ?>
<div class="entry-content">
<?php
the_content(sprintf(
wp_kses(
__('Continue reading<span class="screen-reader-text"> "%s"</span>', 'my-awesome-theme'),
array('span' => array('class' => array()))
),
get_the_title()
));
wp_link_pages(array(
'before' => '<div class="page-links">' . esc_html__('Pages:', 'my-awesome-theme'),
'after' => '</div>',
));
?>
</div>
<footer class="entry-footer">
<?php my_awesome_theme_entry_footer(); ?>
</footer>
</article>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
🧪 测试主题
- 将主题文件夹复制到
wp-content/themes/ - 在 WordPress 后台 → 外观 → 主题中激活
- 检查各项功能是否正常
