|

Smart Card Lists – warum ich dieses Plugin gebaut habe (und was es alles kann)

Als Webagentur baue ich regelmäßig WordPress-Seiten, auf denen Inhalte nicht einfach “als Blog” funktionieren, sondern als übersichtliche Karten, filterbar, performant, und oft CPT-/JetEngine-basiert (Projekte, Referenzen, Veranstaltungen, Partner, …). Und immer wieder kam derselbe Punkt:

  • Ich möchte eine einzige, wiederverwendbare Karten-Komponente
  • die auf Posts oder Taxonomy-Terms laufen kann,
  • Filter kann (ohne schweres JS-Framework),
  • Events sauber als “upcoming / past” listet (mit JetEngine/ACF-Datum),
  • archiv-aware ist (Kategorie-/Tax-Archive sollen “einfach so” funktionieren),
  • und bei großen Datenmengen Lazy Load beherrscht – ohne gleich ein Monster-Plugin zu installieren.

Genau daraus ist Smart Card Lists entstanden.


Die Grundidee

Smart Card Lists ist ein Shortcode-basiertes “Listing-System”, das überall eingebettet werden kann:

  • als Grid (Cards),
  • optional mit Filter-UI (Checkbox-Gruppen pro Taxonomie),
  • optional als Slider,
  • optional als Lazy-Loading-List,
  • und mit einem Event-Datum, das aus Meta-Feldern (JetEngine/ACF) gezogen wird.

Alles ist so gedacht, dass ich als Entwickler eine solide Basis habe, aber im Alltag einfach nur Shortcodes einsetzen muss.


Installation & Settings

Das Plugin bringt eine eigene Einstellungsseite mit:

WordPress → Einstellungen → Smart Card Lists

Dort kann ich globale Defaults setzen, die ich dann pro Shortcode überschreiben kann:

  • event_meta_key (Standard: date_of_event)
  • event_prefix (Standard: Event: )
  • upcoming_post_types (Standard: event)
  • thumb_ratio (Standard: 3/2)
  • thumb_fit (Standard: cover)

Technisch hole ich mir diese Defaults über:

function scl_get_options() {
  $defaults = [
    'event_meta_key'      => 'date_of_event',
    'event_prefix'        => 'Event: ',
    'upcoming_post_types' => 'event',
    'thumb_ratio'         => '3/2',
    'thumb_fit'           => 'cover',
  ];

  $stored = get_option('smart_card_lists_options', []);
  return wp_parse_args(is_array($stored) ? $stored : [], $defaults);
}

Wichtig: Shortcode-Attribute schlagen immer die Defaults. Das macht das System flexibel.


Was kann das Plugin? (Features im Überblick)

1) smart_list – der Haupt-Shortcode

Der zentrale Shortcode ist:

[smart_list]

Damit kann ich:

  • Posts aus beliebigen Post Types listen (post_types="post,projekt,event")
  • Kategorien ein-/ausschließen
  • Taxonomy-Filter (serverseitig) setzen
  • Taxonomy-Filter (clientseitig) als UI anzeigen
  • Archive übernehmen (oder auto-detect)
  • Excerpt / Autor / Datum steuern
  • Event-Datum aus Meta ziehen
  • Lazy Load aktivieren
  • Slider aktivieren
  • Cards komplett klickbar machen

2) mode="terms" – Taxonomie-Terms als Cards

Ich kann statt Posts auch Terms als Cards darstellen:

[smart_list mode="terms" tax="branche" cols_desktop="4"]

Das ist super für “Kategorien-Übersichten” oder “Branchen-Kacheln”.

3) Event-Logik: upcoming / past / all

Über event_scope kann ich Events trennen:

[smart_list post_types="event" event_scope="upcoming" prefer_event_date="true"]

oder:

[smart_list post_types="event" event_scope="past" order="DESC"]

Die Datumsquelle kommt aus event_date_meta_key (komma-separierte Fallback-Liste möglich).

4) upcoming_events – der Komfort-Shortcode

Für den Alltag wollte ich etwas, das “einfach geht”:

[upcoming_events]

Optional mit “Alle Events”-Link:

[upcoming_events limit="5" all_url="/events" all_label="Alle Events anzeigen"]

Intern generiert der Shortcode automatisch einen passenden smart_list … mit:

  • event_scope="upcoming"
  • 1-Spalten-Layout
  • card_link="true"
  • keine Filter
  • Excerpt aus

So sieht der Wrapper im Code aus:

$sc = sprintf(
  '[smart_list post_types="%s" per_page="%d" event_scope="upcoming" event_date_meta_key="%s" prefer_event_date="true" ...]',
  esc_attr($atts['post_types']),
  intval($atts['limit']),
  esc_attr($atts['meta_key'])
);

$out = '<div class="'.esc_attr($atts['wrapper_class']).'">';
$out .= do_shortcode($sc);
...
$out .= '</div>';

Die wichtigsten Shortcode-Beispiele (Copy & Paste)

Standard-Grid (Posts)

[smart_list post_types="post,projekt" per_page="12" cols_mobile="1" cols_tablet="2" cols_desktop="3"]

Manuell kuratierte Liste (IDs) + Reihenfolge behalten

[smart_list include_ids="12,7,45" ids_order="keep" per_page="3"]

Related Posts: gleiche Kategorie + aktuelles Exemplar ausblenden

[smart_list same_category="true" exclude_current="true" per_page="3"]

Taxonomie serverseitig filtern + Filter-UI anzeigen

[smart_list
  post_types="projekt"
  tax="branche:agentur,industrie;technologie:wordpress,react"
  tax_filters="branche,technologie"
  show_filters="true"
  per_page="24"
]

Lazy Load bei großen Listen

[smart_list per_page="240" lazy="scroll" initial="12" batch="12"]

Slider statt Grid

[smart_list post_types="projekt" per_page="12" cols_desktop="4" slider="true" slider_step="auto"]

Oder immer nur “eine Karte weiterschieben”:

[smart_list post_types="projekt" per_page="12" cols_desktop="4" slider="true" slider_step="1"]

Terms als Cards

[smart_list mode="terms" tax="branche" hide_empty="true" cols_desktop="4" meta_bild="tax_bild"]

Deep Dive: Was passiert “an wichtigen Stellen”?

A) Event-Datum robust parsen (JetEngine/ACF freundlich)

Eines der Kernprobleme: Datum kann kommen als…

  • UNIX Timestamp (10-stellig)
  • Milliseconds (13-stellig)
  • Ymd (20251224)
  • ISO-String
  • array/object mit date, timestamp, etc.

Darum ist hw_parse_date_value() bewusst “defensiv” gebaut:

function hw_parse_date_value($raw) {
  if (is_string($raw)) {
    $raw = trim($raw);

    // 10/13-digit timestamps
    if (ctype_digit($raw)) {
      if (strlen($raw) === 10) return (int)$raw;
      if (strlen($raw) === 13) return (int) floor(((int)$raw)/1000);
    }

    // Ymd
    if (preg_match('/^\d{8}$/', $raw)) {
      $dt = DateTime::createFromFormat('Ymd', $raw, wp_timezone());
      if ($dt) return $dt->getTimestamp();
    }

    // fallback
    $ts = strtotime($raw);
    return $ts ?: 0;
  }
  ...
}

Das ist die Basis für event_scope="upcoming|past" und für die Anzeige im Card-Meta.


B) Event-Datum vs Post-Datum (und CSS-Klassen)

Beim Rendern der Card entscheide ich:

  • Ist ein Event-Datum vorhanden?
  • Ist prefer_event_date="true"?

Dann bekommt die Zeile eine Klasse:

  • .card__meta-date--event
  • oder .card__meta-date--post
$use_event = $prefer_event_date && $event_ts > 0;
$label     = $use_event ? $event_prefix : $date_prefix;
$ts        = $use_event ? $event_ts : $post_ts;

echo '<div class="card__meta-date'
   . ($use_event ? ' card__meta-date--event' : ' card__meta-date--post')
   . '">'
   . esc_html($label . wp_date($date_format, $ts))
   . '</div>';

Praktisch: Ich kann Event-Daten im CSS anders highlighten, ohne Zusatzlogik.


C) Aspect Ratio ohne neue Bildgrößen

Ich wollte keine extra Thumbnail-Sizes registrieren. Stattdessen nutze ich CSS aspect-ratio + object-fit.

Das Plugin injiziert CSS in wp_head:

.cards .card__thumb{
  aspect-ratio: var(--sl-thumb-ratio, 3 / 2);
}
.cards .card__thumb img{
  object-fit: var(--sl-thumb-fit, cover);
}

Und pro Shortcode setze ich:

  • --sl-thumb-ratio
  • --sl-thumb-fit

Zusätzlich normalisiere ich Eingaben wie 3/2, 3:2, 1:

function hw_smartlist_normalize_ratio($raw){
  $raw = str_replace(':', '/', trim((string)$raw));
  if (preg_match('~^(\d+(\.\d+)?)\s*/\s*(\d+(\.\d+)?)$~', $raw, $m)) {
    return ((float)$m[1]).' / '.((float)$m[3]);
  }
  if (is_numeric($raw)) return ((float)$raw).' / 1';
  return '3 / 2';
}

D) Filter-UI: bewusst clientseitig (schnell & simpel)

Die Filter bauen auf data- Attributes pro Card:

$data .= ' data-tax-'.esc_attr($tx).'="'.esc_attr(wp_json_encode($slugs)).'"';

Im Frontend filtere ich dann rein über JS (Checkboxen → Set → Intersection):

function matches(card, selections){
  for (var tax in selections){
    var picked = selections[tax];
    var cardSetTx = parseAttr(card, 'data-tax-' + tax);
    if (picked.size && !intersects(cardSetTx, picked)) return false;
  }
  return true;
}

Warum so?

  • Keine Reloads
  • Keine Ajax-Abhängigkeit
  • Minimaler Code
  • Für typische “Portfolio/Projekte/Partner”-Seiten völlig ausreichend

E) Lazy Load: Token + Transient + Nonce (sauber & sicher)

Bei großen Listen will ich initial nicht 240 Cards ausgeben. Darum:

  • initial initial="12"
  • dann per Scroll batch="12"

Das Plugin speichert die Query-Args serverseitig in einem Transient:

$pack = [
  'args' => $args,
  'opts' => [ ... ],
];
set_transient('hw_sl_'.$token, $pack, MINUTE_IN_SECONDS * 30);

Frontend ruft dann Ajax:

fd.append('action','hw_smart_list_next');
fd.append('nonce', nonce);
fd.append('token', token);
fd.append('offset', offset);
fd.append('limit', batch);

Server prüft:

check_ajax_referer('hw_smart_list_nonce', 'nonce');

$pack = get_transient('hw_sl_'.$token);
if (!$pack) wp_send_json_error(['msg'=>'Expired token']);

Das ist mir wichtig, weil ich keine kompletten Query-Args im Frontend herumschicken will.


F) Slider: “Peek-Effekt” + Pixel-Offset gegen Drift

Der Slider ist optional (slider="true") und nutzt flexbox + Track-Transform.

Desktop “Peek”-Effekt: die erste & letzte Karte sind halb sichtbar:

@media (min-width: 1024px){
  .smartlist-slider{ --sl-peek: calc(50% / var(--sl-cols, 1)); }
  .smartlist-slider__track{
    margin-left:  calc(-1 * var(--sl-peek));
    margin-right: calc(-1 * var(--sl-peek));
    transform: translateX(calc(var(--sl-translate) - var(--sl-peek)));
  }
}

Und ich rechne pixelgenau, damit gap berücksichtigt wird:

var cardW = cards[0].getBoundingClientRect().width || 0;
var stepPx = cardW + gapPx;
var offsetPx = stepPx * currentIndex;
list.style.setProperty('--sl-translate', '-' + offsetPx + 'px');

Zusätzlich gibt’s:

  • Buttons (prev/next)
  • Bullets
  • Swipe/Drag via Pointer Events (Touch + Maus)

Spezial-Features

1) “External Link”-Modus pro Post oder Term

Wenn ein Post/Term ein Meta-Feld has_external_link hat, dann:

  • card verlinkt auf extern
  • mit target="_blank"
  • rel="nofollow noopener noreferrer"
  • und der Single bekommt automatisch noindex,nofollow
if (is_singular()) {
  if (hw_get_external_link_for_post($pid)) {
    echo '<meta name="robots" content="noindex,nofollow" />';
  }
}

Für mich ist das perfekt für:

  • Linklisten
  • Ressourcen
  • Partner, die extern laufen
  • “Weiterleitung”-Posts

2) Titel & Bild pro Card überschreiben

Optional per Meta:

  • listing_text → ersetzt Card-Titel
  • listing_bild → ersetzt Featured Image

Das ist Gold wert, wenn der eigentliche Post-Titel zu lang ist oder wenn ich pro Liste ein anderes Vorschaubild brauche.


Mini-FAQ – häufige Fragen zu Smart Card Lists

Kann ich mehrere Post Types gleichzeitig anzeigen?

Ja, absolut.
Der Shortcode akzeptiert eine kommagetrennte Liste von Post Types. Das ist besonders praktisch, wenn Inhalte aus unterschiedlichen Quellen (z. B. Blogposts, Projekte und Events) gemeinsam dargestellt werden sollen.

[smart_list post_types="post,projekt,event"]

Alle angegebenen Post Types werden in einer gemeinsamen Card-Liste ausgegeben und gleich behandelt. Sortierung, Filter, Lazy Load usw. greifen übergreifend.

Kann ich Taxonomien aus verschiedenen Post Types filtern?

Ja – solange die Taxonomien existieren.
Das Plugin prüft nicht, zu welchem Post Type eine Taxonomie „gehört“, sondern filtert sauber über WordPress-Tax-Queries.

[smart_list
  post_types="projekt,event"
  tax="branche:webdesign;typ:messe"
  tax_filters="branche,typ"
]

👉 Wichtig:

  • Die Filter-UI zeigt nur Taxonomien an, die tatsächlich vorhanden sind
  • Cards ohne passende Terms werden korrekt ausgeblendet

Kann ich die Liste automatisch an Kategorie- oder Taxonomie-Archive anpassen?

Ja.
Wenn der Shortcode auf einem Taxonomie-Archiv verwendet wird, kann er den Kontext automatisch übernehmen:

[smart_list archive_aware="true"]

Beispiel:

  • Du bist auf /branche/webdesign/
  • Der Shortcode filtert automatisch auf branche = webdesign
  • Kein manuelles Setzen von tax="..." nötig

Ideal für:

  • Portfolio-Archive
  • Themen-Landingpages
  • dynamische Übersichtsseiten

Wie funktioniert die Event-Logik genau?

Events werden über ein Meta-Feld mit Datum gesteuert (z. B. JetEngine oder ACF).

[smart_list
  post_types="event"
  event_scope="upcoming"
  event_date_meta_key="date_of_event,start_date"
  prefer_event_date="true"
]

Das Plugin:

  • erkennt unterschiedliche Datumsformate automatisch
  • unterscheidet vergangene / zukünftige Events
  • kann Event-Datum oder Post-Datum bevorzugen
  • sortiert Events korrekt nach dem Event-Datum

Unterstützte Formate u. a.:

  • UNIX Timestamp (10- & 13-stellig)
  • Ymd (JetEngine-Standard)
  • ISO-Strings
  • PHP-Date-Strings

Kann ich Events und normale Posts mischen?

Ja – und das ist ausdrücklich vorgesehen.

[smart_list post_types="post,event" prefer_event_date="true"]

Ergebnis:

  • Events zeigen ihr Event-Datum
  • normale Posts zeigen ihr Veröffentlichungsdatum
  • beide erscheinen in einer gemeinsamen Liste

CSS-Klassen helfen beim Styling:

  • .card__meta-date--event
  • .card__meta-date--post

Funktionieren Lazy Load und Slider gleichzeitig?

Nein – und das ist Absicht.

Warum?

  • Slider benötigen alle Cards gleichzeitig im DOM
  • Lazy Load lädt Inhalte stückweise nach

Beides gleichzeitig würde:

  • Layout-Fehler verursachen
  • Slider-Berechnung zerstören
  • unnötig komplexen JS-Code erfordern

👉 Empfehlung:

  • Slider → kleinere, kuratierte Inhalte
  • Lazy Load → große Listen, Archive, Verzeichnisse

Kann ich Lazy Load auch ohne Scroll auslösen?

Ja – Lazy Load unterstützt Button-basiertes Nachladen:

[smart_list lazy="button" initial="9" batch="9"]

Ergebnis:

  • initial 9 Cards
  • Button „Mehr laden“
  • weitere 9 Cards pro Klick

Ideal für:

  • Performance-kritische Seiten
  • bessere UX auf Mobilgeräten

Kann ich einzelne Cards auf externe Links umleiten?

Ja, sehr elegant.

Wenn ein Post (oder Term) ein Meta-Feld für externe URLs besitzt:

  • Card verlinkt direkt extern
  • öffnet in neuem Tab
  • bekommt nofollow noopener noreferrer
  • Single-Seite wird automatisch noindex,nofollow

Perfekt für:

  • Linklisten
  • Ressourcen-Sammlungen
  • Partner- oder Empfehlungsseiten

Kann ich Titel und Bild pro Liste überschreiben?

Ja.

Über optionale Meta-Felder:

  • listing_text → ersetzt Card-Titel
  • listing_bild → ersetzt Featured Image

Das ist extrem hilfreich, wenn:

  • der eigentliche Post-Titel zu lang ist
  • du für verschiedene Listen unterschiedliche Vorschaubilder brauchst
  • Inhalte mehrfach, aber unterschiedlich präsentiert werden

Muss ich ein bestimmtes Theme oder Page-Builder nutzen?

Nein.

Smart Card Lists ist:

  • Shortcode-basiert
  • Gutenberg-kompatibel
  • Elementor-tauglich
  • Theme-agnostisch

Es bringt kein schweres Styling mit, sondern nur:

  • saubere HTML-Struktur
  • minimale Funktions-CSS
  • klare Klassen für eigenes Design

Ist das Plugin für große Datenmengen geeignet?

Ja – genau dafür wurde es gebaut.

Features dafür:

  • Lazy Load mit Transient-Token
  • serverseitige Query-Kapselung
  • kein Query-Leak ins Frontend
  • keine unnötigen Re-Queries

Ich setze es problemlos für:

  • Verzeichnisse mit mehreren hundert Einträgen
  • Event-Archive
  • Partner- & Projekt-Datenbanken

Ähnliche Posts

halloplugin vault

Wissen, Plugin-Entwicklung
Zentrales Plugin-Management für WordPress mit Versionierung, ZIP-Analyse und Download-Freigaben Wer eigene WordPress-Plugins entwickelt, Kundenprojekte betreut oder interne Erweiterungen für verschiedene Installationen pflegt, steht früher oder später vor demselben organisatorischen Problem:…
Posted on 19. März 2026

WordPress Plugin für Snippet Bibliothek

Wissen, Plugin-Entwicklung
Warum eine eigene Snippet Bibliothek für WordPress sinnvoll ist In der täglichen Arbeit einer Webagentur entstehen immer wieder kleine Code Lösungen. Manchmal handelt es sich um ein CSS Snippet für…
Posted on 12. März 2026

Der große E-Mail-Leitfaden 2026

Wissen, Blog
E-Mail gehört für uns alle ganz selbstverständlich zum Alltag. Gleichzeitig tauchen bei technischen Änderungen oder Neueinrichtungen Fragen auf – besonders dann, wenn etwas nicht wie gewohnt funktioniert: Genau deshalb habe…
Posted on 26. Februar 2026

Automatisierte WordPress-Installation mit dem Script „wpnew“

Wissen, Server & Hosting
Warum ich dieses Script entwickelt habe und wie es funktioniert Wer regelmäßig WordPress-Webseiten für Kunden aufsetzt, kennt das Problem: Die Installationsschritte wiederholen sich jedes Mal. WordPress herunterladen, Datenbank verbinden, Grundeinstellungen…
Posted on 18. Februar 2026

Mailcow Backup- & Wiederherstellungsstrategie

Wissen, Server & Hosting
Diese Seite dokumentiert die vollständige Backup- und Wiederherstellungsstrategie für den Mailserver (Mailcow) sowie die externe Sicherung auf TrueNAS. Ziel der Backup-Strategie Systemübersicht Backup-Architektur VPS (Mailcow Server) TrueNAS VPS-Backup Speicherort der…
Posted on 18. Februar 2026

DNS einfach & ausführlich erklärt (2026)

Wissen, Blog, Server & Hosting
Was DNS ist, wie es funktioniert – und warum es für Domain, Hosting, Mail & Sicherheit entscheidend ist Wenn du eine Website betreibst oder betreiben willst, kommst du an einem…
Posted on 11. Februar 2026

JetForm & Polylang

Wissen, Plugin-Entwicklung
Wer mit JetFormBuilder arbeitet und seine Website mit Polylang mehrsprachig betreibt, stößt an eine Grenze:Formular-Labels, Checkbox-Texte, Radio-Optionen oder der Submit-Button lassen sich nicht sauber übersetzen, ohne Formulare zu duplizieren oder…
Posted on 15. Januar 2026

Kadence Palette RGB Helper

Wissen, Plugin-Entwicklung
Kadence liefert mit seinen globalen Farbpaletten eine richtig gute Basis: --global-palette1 bis --global-palette9 stehen als CSS-Variablen zur Verfügung und lassen sich in Kadence-Blöcken sowie im Theme vielseitig einsetzen. In der…
Posted on 24. Dezember 2025

Wie ich CloudPanel auf einem Netcup VPS sicher eingerichtet habe

Wissen, Server & Hosting
Vom leeren Server bis zum professionellen WordPress-Hosting Als ich mein eigenes Webhosting auf Basis eines Netcup VPS aufgebaut habe, war mir eines besonders wichtig:Ich wollte jeden einzelnen Schritt selbst kontrollieren…
Posted on 14. Dezember 2025

Der ultimative Leitfaden zur Bildoptimierung in WordPress

Wissen, Blog
Von der Komprimierung bis zum SEO Bilder sind ein wesentlicher Bestandteil jeder WordPress-Website. Sie ziehen die Aufmerksamkeit der Besucher auf sich, unterstützen die Botschaft Ihres Contents und können die Verweildauer…
Posted on 14. Mai 2025

Optimal vorbereitet: So planen Sie Ihre neue Webseite

Wissen, Blog
Die Planung einer neuen Webseite ist nicht nur entscheidend für deren Erfolg, sondern kann auch dazu beitragen, die Kosten so gering wie möglich zu halten. Eine gründliche Vorbereitung hilft Ihnen,…
Posted on 24. Juni 2024

Google Snippets richtig verstehen und nutzen

Wissen, Blog
Google Snippets sind kurze, prägnante Informationsausschnitte, die direkt in den Suchergebnissen angezeigt werden, um Benutzern schnelle Antworten auf ihre Suchanfragen zu liefern. Diese informativen Blöcke sollen den Nutzern eine Vorschau…
Posted on 4. Januar 2024

Was sind WordPress Plugins?

Wissen, Blog
WordPress-Plugins sind kleine oder große Zusatzprogramme, die ich zu ihrer WordPress-Webseite hinzufüge, um die Funktionalität der Website zu erweitern oder zu verbessern. Sie werden sowohl von mir selbst sowie von…
Posted on 15. Dezember 2023

Was versteht man unter Usability?

Wissen, Blog
Usability ist im Grunde das Qualitätsmerkmal Ihrer Webseite. Der Anwender sollte möglichst wenig nachdenken beim Benutzen und Navigieren. Er möchte nicht darüber nachdenken, wo sich die Hauptnavigation befindet und was…
Posted on 11. Dezember 2023