0) Mentales Modell
- Arbeite in den HTML‑Quellen (z. B.
front-page.html
,index.html
). - Füge dort WP‑Aktionen (Template Parts, Code/PHP, Loops …) ein.
- Export generiert die PHPs. Exportierte
.php
später nicht per Hand ändern (würde überschrieben).
1) Header & Footer – sauber trennen
Ziel: getheader()
/getfooter()
nutzen und Template‑Parts erzeugen.
Vorgehen in Pinegrow (PG 8.5):
- Header anlegen
- In deiner HTML‑Quelle vom
<!DOCTYPE html>
bis inkl. öffnendem<body …>
markieren. - WordPress → Define template part → Name:
header
→ Datei „. - Inhalt von
header.php
sollte enthalten: <!DOCTYPE html>
<html <?php languageattributes(); ?>>
<head> … <?php wphead(); ?> … </head>
<body … class="<?php echo implode(' ', getbodyclass()); ?>">
<?php if ( functionexists('wpbodyopen') ) wpbodyopen(); ?>
- Footer anlegen
- Footer‑Markup bis `
markieren → Define template part →
`. - Enthält Footer‑HTML +
<?php wpfooter(); ?>
+</body></html>
.
- Auf jeder Seite einbinden
- Ganz oben im
<body>
: WordPress → Code → PHP:
<?php get_header(); ?>
- Ganz unten vor
</body>
: WordPress → Code → PHP:
<?php get_footer(); ?>
> Hinweis: Die alten Template‑Part‑Marker nicht in <main>
liegen lassen (sonst Wandersignale wie _WPENDHEADER
).
2) „Site content“ vs. „The loop“ – nie mischen
- Site content = fertiger Loop für singuläre Seiten (Front‑Page, Page, Single). Enthält
thepost()
+thecontent()
. - The loop + Post content = manuelle Variante.
- Regel: In einem
<main>
immer nur eines von beiden verwenden.
Beispiel (Front‑Page, simpel)
<?php get_header(); ?>
<main>
<?php // Variante A – Site content (empfohlen für Front/Page)
if ( have_posts() ) : while ( have_posts() ) : the_post();
the_content();
endwhile; endif; ?>
</main>
<?php get_footer(); ?>
Beispiel (Blogliste in index.php)
<?php get_header(); ?>
<main class="container py-5">
<?php if ( have_posts() ) : ?>
<div class="row g-4">
<?php while ( have_posts() ) : the_post(); ?>
<article class="col-md-6 col-lg-4">
<h2 class="h5"><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2>
<p><?php the_excerpt(); ?></p>
</article>
<?php endwhile; ?>
</div>
<?php the_posts_pagination(); ?>
<?php else : ?>
<p><?php _e('Keine Beiträge gefunden.', 'wpcgrau'); ?></p>
<?php endif; ?>
</main>
<?php get_footer(); ?>
3) Menü korrekt ausgeben (Bootstrap)
functions.php – Menü registrieren:
register_nav_menus([
'primary' => __('Hauptmenü', 'wpcgrau'),
]);
Template (z. B. im Header bei der <ul>
):
<?php
if ( has_nav_menu('primary') ) {
wp_nav_menu([
'theme_location' => 'primary',
'container' => '',
'menu_class' => 'navbar-nav ms-auto',
'fallback_cb' => '__return_empty_string',
]);
}
?>
> Wichtig: Action auf die ` legen, nicht auf
<nav>. Kein
itemswrap => ‚%3$s‘ (sonst fehlt das
<ul>`).
4) Logo oder Seitentitel – Fallback
Theme‑Support (einmalig):
add_theme_support('custom-logo', [
'height' => 200,
'width' => 200,
'flex-height' => true,
'flex-width' => true,
]);
Template (Navbar‑Brand):
<?php if ( function_exists('the_custom_logo') && has_custom_logo() ) : ?>
<?php the_custom_logo(); ?>
<?php else : ?>
<a class="navbar-brand" href="<?php echo esc_url( home_url('/') ); ?>">
<?php bloginfo('name'); ?>
</a>
<?php endif; ?>
Optional – „ zur Logo‑Ausgabe hinzufügen:
add_filter('get_custom_logo', function($html){
return str_replace('custom-logo-link', 'custom-logo-link navbar-brand', $html);
});
5) Hintergrundbild via Customizer ohne Gradient zu verlieren
CSS („):
.masthead{
background-image:
linear-gradient(to bottom, rgba(0,0,0,.3) 0%, rgba(0,0,0,.7) 75%, #000 100%),
var(--masthead-img, url('../assets/img/bg-masthead.jpg'));
background-position: center;
background-repeat: no-repeat;
background-size: cover;
}
/* (Übergangslösung, falls noch inline background-image existiert) */
.masthead[style]{
background-image:
linear-gradient(to bottom, rgba(0,0,0,.3) 0%, rgba(0,0,0,.7) 75%, #000 100%),
var(--masthead-img, url('../assets/img/bg-masthead.jpg')) !important;
}
PHP („):
// Dev: Stylesheet cache-busten
add_action('wp_enqueue_scripts', function(){
wp_dequeue_style('wpcgrau');
wp_enqueue_style('wpcgrau', get_stylesheet_uri(), [], time());
}, 99);
function codekeks_get_mod_image_url($key, $size='full'){
$v = get_theme_mod($key);
if(!$v) return '';
if(is_numeric($v)) return wp_get_attachment_image_url((int)$v, $size) ?: '';
return esc_url_raw($v);
}
add_action('wp_head', function(){
$url = codekeks_get_mod_image_url('wpc_grau_header_wpc_grau_bgpic', 'full');
echo "\n<!-- codekeks custom.php loaded -->\n";
if($url){
echo '<style id="codekeks-masthead-var">:root{--masthead-img:url(\''.esc_url($url).'\')}</style>'."\n";
}
}, 999);
6) Template‑Hierarchie – Kurzreferenz
- front-page.php – Startseite (höchste Priorität, egal ob Blog oder statische Seite).
- home.php – Blog‑Index (Beiträge‑Übersicht).
- page.php – einzelne Seite (Typ
page
). Varianten:page-{slug}.php
,page-{id}.php
. - single.php – einzelne Beiträge. Spezifisch:
single-post.php
. - singular.php – generisch für alle Singles, falls
single.php
/page.php
fehlen. - index.php – Fallback, muss existieren.
Matrix Startseite:
- Einstellungen → Lesen = „Statische Seite“: nimmt front-page.php, Inhalt kommt von der gewählten Seite.
- Einstellungen → Lesen = „Neueste Beiträge“: nimmt front-page.php (Startseite) + home.php (Blogliste).
7) Häufige Stolperfallen (und Fixes)
- Doppelter Content: Nicht Site content und Loop gleichzeitig im selben
<main>
. - Menü ohne `
: Keine
itemswrap => ‚%3$s‘ohne eigenen
<ul>Wrapper; Action gehört auf die
<ul>`. - Header/Footer im Content: Falsch platzierte Template‑Part‑Marker. Lösung: echte
getheader()
/get_footer()
nutzen. - Cache frisst Änderungen: DevTools → Disable cache aktiv, WP‑Cache leeren, Versioning (
time()
) nutzen. - Export überschreibt Hand‑Änderungen: Änderungen nur in HTML‑Quellen + WP‑Aktionen.
8) Export‑Workflow (sicher)
- HTML‑Quelle(n) bearbeiten, WP‑Aktionen setzen.
- WordPress → Export Theme.
- Im Frontend Quelltext prüfen (z. B.
<!-- codekeks custom.php loaded -->
). - Keine produktiven Hand‑Edits in exportierten PHPs.
9) Nützliche Debug‑Snippets
Schneller Head‑Ping:
add_action('wp_head', fn()=>print("<!-- DEV ".time()." -->\n"), 1000);
Body‑Klassen prüfen:
echo '<!-- '.implode(' ', get_body_class()).' -->';
Menü‑Registrierung sichtbar machen:
if ( ! has_nav_menu('primary') ) echo '<!-- primary menu not assigned -->';
10) Bootstrap‑Nav – Minimalbeispiel
<nav class="navbar navbar-expand-lg navbar-light fixed-top" id="mainNav">
<div class="container px-4 px-lg-5">
<?php if ( function_exists('the_custom_logo') && has_custom_logo() ) : the_custom_logo(); else : ?>
<a class="navbar-brand" href="<?php echo esc_url( home_url('/') ); ?>"><?php bloginfo('name'); ?></a>
<?php endif; ?>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
<?php _e('Menü','wpcgrau'); ?> <i class="fas fa-bars"></i>
</button>
<div class="collapse navbar-collapse" id="navbarResponsive">
<ul class="navbar-nav ms-auto">
<?php
if ( has_nav_menu('primary') ) {
wp_nav_menu([
'theme_location' => 'primary',
'container' => false,
'items_wrap' => '%3$s', // wir benutzen die vorhandene <ul>
'fallback_cb' => '__return_empty_string',
]);
}
?>
</ul>
</div>
</div>
</nav>
Letzter Tipp: Orange PG‑Warnungen („action will be ignored“) ernst nehmen: Die Aktion hängt meist am falschen Element/Ort. Lieber einmal löschen und am richtigen Knoten neu setzen.