Wie wir Google Fonts getaggt und goofonts.com erstellt haben

Avatar of Paulina Hetman
Paulina Hetman am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

GooFonts ist ein Nebenprojekt einer Entwickler-Ehefrau und eines Designer-Ehemanns, die beide große Fans der Typografie sind. Wir haben Google Fonts getaggt und eine Website erstellt, die das Suchen und Finden der richtigen Schriftart erleichtert.

GooFonts nutzt WordPress im Backend und NuxtJS (ein Vue.js-Framework) im Frontend. Gerne erzähle ich Ihnen die Geschichte hinter goofonts.com und teile einige technische Details zu den von uns gewählten Technologien und wie wir sie für dieses Projekt angepasst und eingesetzt haben.

Warum wir GooFonts entwickelt haben

Zum Zeitpunkt der Erstellung dieses Artikels bietet Google Fonts 977 Schriftarten an. Die genaue Anzahl können Sie jederzeit über die Google Fonts Developer API. abrufen. Sie können die dynamische Liste aller Schriftarten abrufen, einschließlich einer Liste der verfügbaren Stile und Schriftsysteme für jede Familie.

Die Google Fonts-Website bietet eine schöne Oberfläche, auf der Sie alle Schriftarten in der Vorschau anzeigen und nach Trending, Beliebtheit, Datum oder Namen sortieren können. 

Aber wie sieht es mit der Suchfunktion aus? 

Sie können Schriftarten nach fünf Kategorien einbeziehen und ausschließen: Serif, Sans-Serif, Display, Handwriting und Monospace.

Sie können innerhalb von Schriftsystemen suchen (wie Latin Extended, Cyrillic oder Devanagari (diese werden bei Google Fonts als Subsets bezeichnet). Aber Sie können nicht innerhalb mehrerer Subsets gleichzeitig suchen.

Sie können nach vier Eigenschaften suchen: Dicke, Neigung, Breite und „Anzahl der Stile“. Ein Stil, auch Variante genannt, bezieht sich sowohl auf den Stil (kursiv oder normal) als auch auf die Schriftschnitte (100, 200, bis 900). Oft benötigt die Hauptschriftart drei Varianten: normal, fett und kursiv. Die Eigenschaft „Anzahl der Stile“ sortiert Schriftarten mit vielen Varianten aus, erlaubt aber nicht die Auswahl von Schriftarten, die in der „Normal-, Fett-, Kursiv“-Kombination erhältlich sind.

Es gibt auch ein benutzerdefiniertes Suchfeld, in das Sie Ihre Abfrage eingeben können. Leider wird die Suche ausschließlich über die Namen der Schriftarten durchgeführt. Daher enthalten die Ergebnisse oft Schriftfamilien, die ausschließlich von anderen Diensten als Google Fonts stammen. 

Nehmen wir als Beispiel die Abfrage „cartoon“. Das Ergebnis ist „Cartoon Script“ von einer externen Foundry, Linotype.

Ich erinnere mich an die Arbeit an einem Projekt, das zwei hochgradig stilisierte Schriftarten erforderte — eine, die den alten Wilden Westen beschwor, und die andere, die ein Drehbuch imitierte. Das war der Moment, als ich beschloss, Google Fonts zu taggen. :)

GooFonts in Aktion

Lassen Sie mich Ihnen zeigen, wie GooFonts funktioniert. Die dunkle Seitenleiste auf der rechten Seite ist Ihr „Suchbereich“. Sie können Ihre Schlüsselwörter in das Suchfeld eingeben — dies führt eine „UND“-Suche durch. Sie können zum Beispiel nach Schriftarten suchen, die gleichzeitig cartoon und slab sind. 

Wir haben eine Reihe von Schlüsselwörtern handverlesen — klicken Sie auf eines davon! Wenn Ihr Projekt bestimmte Subsets erfordert, überprüfen Sie diese in den Subset-Bereichen.  Sie können auch alle Varianten auswählen, die Sie für Ihre Schriftart benötigen.

Wenn Ihnen eine Schriftart gefällt, klicken Sie auf das Herzsymbol, und sie wird im localStorage Ihres Browsers gespeichert. Ihre favorisierten Schriftarten finden Sie auf der Seite goofonts.com/bookmarks. Zusammen mit dem Code, den Sie möglicherweise zum Einbetten benötigen.

Wie wir es gebaut haben: Der WordPress-Teil

Zuerst benötigten wir eine Art Schnittstelle, auf der wir jede Schriftart in der Vorschau anzeigen und taggen konnten. Wir brauchten auch eine Datenbank, um diese Tags zu speichern. 

Ich hatte bereits Erfahrung mit WordPress. Außerdem verfügt WordPress über eine REST API, die vielfältige Möglichkeiten für den Umgang mit Daten im Frontend eröffnet. Die Wahl fiel schnell. 

Ich entschied mich für die einfachste mögliche anfängliche Einrichtung. Jede Schriftart ist ein Beitrag und wir verwenden Beitrags-Tags für Schlüsselwörter. Ein benutzerdefinierter Beitragstyp hätte auch funktionieren können, aber da wir WordPress nur für die Daten verwenden, funktioniert der Standard-Inhaltstyp perfekt.

Offensichtlich mussten wir alle Schriftarten programmatisch hinzufügen. Wir mussten auch die Schriftarten programmatisch aktualisieren können, einschließlich dem Hinzufügen neuer oder dem Hinzufügen neuer verfügbarer Varianten und Subsets. 

Der unten beschriebene Ansatz kann mit beliebigen anderen Daten, die über eine externe API verfügbar sind, nützlich sein. In einem benutzerdefinierten WordPress-Plugin registrieren wir eine Menüseite, von der aus wir auf Updates von der API prüfen können. Der Einfachheit halber zeigt die Seite einen Titel, eine Schaltfläche zur Aktivierung des Updates und eine Fortschrittsanzeige für visuelles Feedback.

/**
 * Register a custom menu page. 
 */
function register_custom_menu_page() {
  add_menu_page( 
    'Google Fonts to WordPress', 
    'WP GooFonts', 
    'manage_options', 
    'wp-goofonts-menu', 
  function() { ?>        
    <h1>Google Fonts API</h1>
    <button type="button" id="wp-goofonts-button">Run</button>
    <p id="info"></p>        
    <progress id="progress" max="100" value="0"></progress>
  <?php }
  );
}
add_action( 'admin_menu', 'register_custom_menu_page' );


Beginnen wir mit dem JavaScript-Teil. Während die meisten Beispiele für die Verwendung von Ajax mit WordPress jQuery und der jQuery.ajax-Methode implementieren, kann dasselbe auch ohne jQuery mit axios und einem kleinen Helfer Qs.js für die Daten-Serialisierung erreicht werden.

Wir möchten unser benutzerdefiniertes Skript im Footer laden, nachdem axios und qs geladen wurden:

add_action( 'admin_enqueue_scripts' function() {
  wp__script( 'axios', 'https://unpkg.com/axios/dist/axios.min.js' );
  wp_enqueue_script( 'qs', 'https://unpkg.com/qs/dist/qs.js' );
  wp_enqueue_script( 'wp-goofonts-admin-script', plugin_dir_url( __FILE__ ) . 'js/wp-goofonts.js', array( 'axios', 'qs' ), '1.0.0', true );
});

Schauen wir uns an, wie das JavaScript aussehen könnte.

const BUTTON = document.getElementById('wp-goofonts-button')
const INFO = document.getElementById('info')
const PROGRESS = document.getElementById('progress')
const updater = {
  totalCount: 0,
  totalChecked: 0,
  updated: [],
  init: async function() {
    try {
      const allFonts = await axios.get('https://www.googleapis.com/webfonts/v1/webfonts?key=API_KEY&sort=date')
      this.totalCount = allFonts.data.items.length
      INFO.textContent = `Fetched ${this.totalCount} fonts.`
      this.updatePost(allFonts.data.items, 0)
    } catch (e) {
      console.error(e)
    }
  },
  updatePost: async function(els, index) {
    if (index === this.totalCount) {
      return
    }                
    const data = {
      action: 'goofonts_update_post',
      font: els[index],
    }
    try {
       const apiRequest = await axios.post(ajaxurl, Qs.stringify(data))
       this.totalChecked++
       PROGRESS.setAttribute('value', Math.round(100*this.totalChecked/this.totalCount))
       this.updatePost(els, index+1)
    } catch (e) {
       console.error(e)
      }
   }
}

BUTTON.addEventListener('click', () => {
  updater.init()
})

Die init-Methode sendet eine Anfrage an die Google Fonts API. Sobald die Daten von der API verfügbar sind, rufen wir die rekursive asynchrone Methode updatePost auf, die eine einzelne Schriftart in der POST-Anfrage an den WordPress-Server sendet.

Nun ist es wichtig zu bedenken, dass WordPress Ajax auf seine spezifische Weise implementiert. Zunächst muss jede Anfrage an wp-admin/admin-ajax.php gesendet werden. Diese URL ist im Verwaltungsbereich als globale JavaScript-Variable ajaxurl verfügbar. 

Zweitens müssen alle WordPress-Ajax-Anfragen ein action-Argument in den Daten enthalten. Der Wert der Aktion bestimmt, welcher Hook-Tag auf der Serverseite verwendet wird.

In unserem Fall ist der Aktionswert goofonts_update_post. Das bedeutet, dass das, was auf der Serverseite passiert, durch den Hook wp_ajax_goofonts_update_post bestimmt wird.

add_action( 'wp_ajax_goofonts_update_post', function() {
  if ( isset( $_POST['font'] ) ) {
    /* the post tile is the name of the font */
    $title = wp_strip_all_tags( $_POST['font']['family'] );
    $variants = $_POST['font']['variants'];
    $subsets = $_POST['font']['subsets'];
    $category = $_POST['font']['category'];
    /* check if the post already exists */
    $object = get_page_by_title( $title, 'OBJECT', 'post' );
    if ( NULL === $object ) {
      /* create a new post and set category, variants and subsets as tags */
      goofonts_new_post( $title, $category, $variants, $subsets );
    } else {
      /* check if $variants or $subsets changed */
      goofonts_update_post( $object, $variants, $subsets );
    }
  }
});

function goofonts_new_post( $title, $category, $variants, $subsets ) {
  $post_id =  wp_insert_post( array(
    'post_author'  =>  1,
    'post_name'    =>  sanitize_title( $title ),
    'post_title'   =>  $title,
    'post_type'    =>  'post',
    'post_status'  => 'draft',
    )
  );
  if ( $post_id > 0 ) {
    /* the easy part of tagging ;) append the font category, variants and subsets (these three come from the Google Fonts API) as tags */
    wp_set_object_terms( $post_id, $category, 'post_tag', true );
    wp_set_object_terms( $post_id, $variants, 'post_tag', true );
    wp_set_object_terms( $post_id, $subsets, 'post_tag', true );
  }
}

Auf diese Weise haben wir in weniger als einer Minute fast tausend Beitragsentwürfe im Dashboard — alle mit einigen bereits vorhandenen Tags. Und das ist der Moment, in dem der entscheidende, zeitaufwändigste Teil des Projekts beginnt. Wir müssen beginnen, manuell Tags für jede Schriftart einzeln hinzuzufügen.
Der Standard-WordPress-Editor ist in diesem Fall nicht sehr sinnvoll. Was wir brauchten, war eine Vorschau der Schriftart. Ein Link zur Schriftartenseite auf fonts.google.com ist ebenfalls nützlich.

Eine benutzerdefinierte Meta-Box erfüllt diese Aufgabe sehr gut. In den meisten Fällen verwenden Sie Meta-Boxen für benutzerdefinierte Formularelemente, um einige benutzerdefinierte Daten im Zusammenhang mit dem Beitrag zu speichern. Tatsächlich kann der Inhalt einer Meta-Box praktisch jedes HTML sein.

function display_font_preview( $post ) {
  /* font name, for example Abril Fatface */
  $font = $post->post_title;
  /* font as in url, for example Abril+Fatface */
  $font_url_part = implode( '+', explode( ' ', $font ));
  ?>
  <div class="font-preview"> 
    <link href="<?php echo 'https://fonts.googleapis.com/css?family=' . $font_url_part . '&display=swap'; ?>" rel="stylesheet">
    <header>
      <h2><?php echo $font; ?></h2>
      <a href="<?php echo 'https://fonts.google.com/specimen/' . $font_url_part; ?>" target="_blank" rel="noopener">Specimen on Google Fonts</a>
    </header>
    <div contenteditable="true" style="font-family: <?php echo $font; ?>">
      <p>The quick brown fox jumps over a lazy dog.</p>
      <p style="text-transform: uppercase;">The quick brown fox jumps over a lazy dog.</p>
      <p>1 2 3 4 5 6 7 8 9 0</p>
      <p>& ! ; ? {}[]</p>
    </div>
  </div>
<?php }

add_action( 'add_meta_boxes', function() {
  add_meta_box(
    'font_preview', /* metabox id */
    'Font Preview', /* metabox title */
    'display_font_preview', /* content callback */
    'post' /* where to display */
  );
});

Das Taggen von Schriftarten ist eine Langzeitaufgabe mit viel Wiederholung. Es erfordert auch eine große Portion Konsistenz. Deshalb haben wir mit der Definition einer Reihe von Tag- „Presets“ begonnen. Das könnten zum Beispiel sein

{
  /* ... */
  comic: {
    tags: 'comic, casual, informal, cartoon'
  },
  cursive: {
    tags: 'cursive, calligraphy, script, manuscript, signature'
  },
  /* ... */
}

Als Nächstes haben wir mit etwas benutzerdefiniertem CSS und JavaScript den WordPress-Editor und das Tag-Formular „gehackt“, indem wir es mit einer Reihe von Preset-Schaltflächen erweitert haben. 

Wie wir es gebaut haben: Der Frontend-Teil (mit NuxtJS)

Die Benutzeroberfläche von goofonts.com wurde von Sylvain Guizard, einem französischen Grafik- und Webdesigner (der auch mein Ehemann ist), entworfen. Wir wollten etwas Einfaches mit einem ausgeprägten „Suchbereich“. Sylvain hat bewusst Farben gewählt, die nicht zu weit von der Google Fonts-Identität entfernt sind. Wir suchten nach einem Gleichgewicht zwischen dem Bau von etwas Einzigartigem und Originellem und gleichzeitig der Vermeidung von Verwirrung für den Benutzer.

Während ich bei der Wahl von WordPress für das Backend nicht gezögert habe, wollte ich es nicht im Frontend verwenden. Wir strebten nach einer App-ähnlichen Erfahrung, und ich persönlich wollte in JavaScript programmieren, insbesondere mit Vue.js. 

Ich stieß auf ein Beispiel einer Website, die NuxtJS mit WordPress verwendete, und beschloss, es auszuprobieren. Die Wahl war sofort getroffen. NuxtJS ist ein sehr beliebtes Vue.js-Framework, und ich genieße seine Einfachheit und Flexibilität sehr. 
Ich habe mit verschiedenen NuxtJS-Einstellungen experimentiert, um eine 100% statische Website zu erhalten. Die vollständig statische Lösung schien die performanteste zu sein; die Gesamterfahrung schien am flüssigsten. Das bedeutet auch, dass meine WordPress-Seite nur während des Build-Prozesses verwendet wird. Somit kann sie auf meinem lokalen Rechner laufen. Das ist nicht unerheblich, da es die Hosting-Kosten eliminiert und vor allem die sicherheitsrelevanten WordPress-Konfigurationen überspringt und mich von sicherheitsrelevantem Stress befreit.  ;)

Wenn Sie mit NuxtJS vertraut sind, wissen Sie wahrscheinlich, dass die vollständige statische Generierung noch nicht (oder noch nicht) Teil von NuxtJS ist. Die vorgerenderten Seiten versuchen beim Navigieren erneut, Daten abzurufen.

Deshalb müssen wir die 100% statische Generierung irgendwie „hacken“. In diesem Fall speichern wir die nützlichen Teile der abgerufenen Daten vor jedem Build-Prozess in einer JSON-Datei. Dies ist möglich dank Nuxt Hooks, insbesondere seinen Builder Hooks.

Hooks werden typischerweise in Nuxt-Modulen verwendet.

/* modules/beforebuild.js */

const fs = require('fs')
const axios = require('axios')

const sourcePath = 'http://wpgoofonts.local/wp-json/wp/v2/'
const path = 'static/allfonts.json'

module.exports = () => {
  /* write data to the file, replacing the file if it already exists */
  const storeData = (data, path) => {
    try {
      fs.writeFileSync(path, JSON.stringify(data))
    } catch (err) {
      console.error(err)
    }
  }
  async function getData() {    
    const fetchedTags = await axios.get(`${sourcePath}tags?per_page=500`)
      .catch(e => { console.log(e); return false })
    
  /* build an object of tag_id: tag_slug */
    const tags = fetchedTags.data.reduce((acc, cur) => {
      acc[cur.id] = cur.slug
      return acc
    }, {})
    
  /* we want to know the total number or pages */
    const mhead = await axios.head(`${sourcePath}posts?per_page=100`)
      .catch(e => { console.log(e); return false })
    const totalPages = mhead.headers['x-wp-totalpages']

  /* let's fetch all fonts */
    let fonts = []
    let i = 0
    while (i < totalPages) {
      i++
      const response = await axios.get(`${sourcePath}posts?per_page=100&page=${i}`)
      fonts.push.apply(fonts, response.data)
    }
  
  /* and reduce them to an object with entries like: {roboto: {name: Roboto, tags: ["clean","contemporary", ...]}} */
    fonts = (fonts).reduce((acc, el) => {
      acc[el.slug] = {
        name: el.title.rendered,
        tags: el.tags.map(i => tags[i]),
      }
      return acc
    }, {})

  /* save the fonts object to a .json file */
    storeData(fonts, path)
  }

  /* make sure this happens before each build */
  this.nuxt.hook('build:before', getData)
}
/* nuxt.config.js */
module.exports = {
  // ...
  buildModules: [
    ['~modules/beforebuild']
  ],
// ...
}

Wie Sie sehen können, fordern wir nur eine Liste von Tags und eine Liste von Beiträgen an. Das bedeutet, wir verwenden nur die Standard-WordPress-REST-API-Endpunkte, und es ist keine Konfiguration erforderlich.

Abschließende Gedanken

Die Arbeit an GooFonts war ein langes Abenteuer. Es ist auch diese Art von Projekten, die aktiv gepflegt werden müssen. Wir überprüfen regelmäßig Google Fonts auf neue Schriftarten, Subsets oder Varianten. Wir taggen neue Elemente und aktualisieren unsere Datenbank. Kürzlich war ich wirklich begeistert zu entdecken, dass Bebas Neue der Familie beigetreten ist. Wir haben auch unsere persönlichen Favoriten unter den viel weniger bekannten Exemplaren.

Als Trainer, der regelmäßig Workshops gibt, kann ich echte Benutzer beim Spielen mit GooFonts beobachten. In dieser Phase des Projekts möchten wir so viel Feedback wie möglich erhalten. Wir möchten, dass GooFonts ein nützliches, handliches und intuitives Werkzeug für Webdesigner ist. Eine der zu erledigenden Funktionen ist die Suche nach einer Schriftart anhand ihres Namens. Wir würden auch gerne die Möglichkeit hinzufügen, favorisierte Sets zu teilen und mehrere „Sammlungen“ von Schriftarten zu erstellen.

Als Entwickler habe ich den multidisziplinären Aspekt dieses Projekts sehr genossen. Es war das erste Mal, dass ich mit der WordPress REST API gearbeitet habe, es war mein erstes großes Projekt in Vue.js, und ich habe so viel über Typografie gelernt. 

Würden wir etwas anders machen, wenn wir könnten? Absolut. Es war ein Lernprozess. Andererseits glaube ich nicht, dass wir die Hauptwerkzeuge ändern würden. Die Flexibilität von WordPress und Nuxt.js erwies sich als die richtige Wahl. Würde ich heute von vorne anfangen, würde ich mir auf jeden Fall Zeit nehmen, GraphQL zu erkunden, und ich werde es wahrscheinlich in Zukunft implementieren.

Ich hoffe, dass Sie einige der diskutierten Methoden nützlich finden. Wie ich bereits sagte, ist Ihr Feedback sehr wertvoll. Wenn Sie Fragen oder Anmerkungen haben, lassen Sie es mich bitte in den Kommentaren wissen!