|

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

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

Pages Nav Shortcode – warum ich dieses Plugin gebaut habe und wofür ich es heute einsetze

12. Dezember 2025

In fast jedem WordPress-Projekt gibt es einen Punkt, an dem die Standard-Navigation nicht mehr ausreicht. Seiten wachsen, Inhalte werden komplexer, Kategorien verzweigen sich – und plötzlich braucht man kontextbezogene Navigationen,…

Warum ich eine eigene, hochsichere Mailserver-Infrastruktur aufgebaut habe

10. Dezember 2025

E-Mail ist eines der wichtigsten Kommunikationsmittel im Geschäftsalltag. Viele Unternehmen verlassen sich täglich darauf, dass Nachrichten zuverlässig zugestellt werden, geschützt sind und nicht in falsche Hände geraten. Gleichzeitig steigen sowohl…

Seitenfarbe als Designsystem: Mein eigenes WordPress-Plugin für dynamische Farbschemata

8. Dezember 2025

Mit meinem kleinen WordPress-Plugin für die Seitenfarbe habe ich mir ein flexibles Farbsystem gebaut, das direkt aus dem Custom Field farbe gespeist wird. Egal ob Beitrag, Seite, Custom Post Type…

Maßanfertigung im WooCommerce-Shop: Wie ich einen Plattenkonfigurator mit smarter Preislogik gebaut habe

1. Dezember 2025

Wer Platten, Zuschnitte oder andere Maßanfertigungen online verkaufen möchte, kennt das Problem: Standard-WooCommerce-Produkte reichen dafür einfach nicht aus. Kunden wollen Länge, Breite und Stärke frei wählen, Zuschnitte und Bohrungen hinzufügen…

Der ultimative Leitfaden zur Bildoptimierung in WordPress

14. Mai 2025

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…

Kommentare in WordPress deaktivieren – Methoden, Plugins und Code-Lösungen – Der ultimative Leitfaden

21. April 2025

Einleitung: Warum Kommentare deaktivieren? WordPress wurde ursprünglich als Blogging-Plattform entwickelt, und Kommentare waren ein zentrales Feature, um Interaktion und Community-Building zu fördern. Doch im Laufe der Jahre haben sich die…

Die 25 besten WordPress-Plugins für 2025: Unverzichtbare Tools für jede Website

16. April 2025

Entdecken Sie die ultimative Sammlung der 25 leistungsstärksten WordPress-Plugins für 2025. Von SEO-Giganten wie Yoast SEO bis zu innovativen Neuheiten wie AltText.ai – dieser umfassende Guide präsentiert die Tools, die…

URL-Strukturierung für SEO: <br>Der ultimative Guide für bessere Rankings

16. April 2025

Entdecken Sie, warum die URL-Struktur Ihrer Webseite ein entscheidender Erfolgsfaktor für Ihr SEO-Ranking ist. In diesem umfassenden Guide erfahren Sie, wie Sie URLs optimal gestalten, Landingpages für spezifische Keywords optimieren…

Optimal vorbereitet: So planen Sie Ihre neue Webseite

24. Juni 2024

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,…

Google Snippets richtig verstehen und nutzen

4. Januar 2024

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…

Was sind WordPress Plugins?

15. Dezember 2023

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…

Was versteht man unter Usability?

11. Dezember 2023

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…