Tag: Theme

Making dark theme switcher with PostCSS.

You have noticed that there is a new design trend that is floating around web design since 2019, the dark mode. Facebook, Apple, and Google both introduced the dark version of their software.

Why a dark theme

Most of you probably think this is just a trend that will disappear after some years, well, let me say that this is not like many other trends, dark UI provide different advantages and they are not something just related to the “designer mood”. Let’s see why a dark mode on your applications and websites are something useful.

Better for batteries

Pixels on a screen consume more energy to display light colors rather than dark ones. Consequently, devices’ batteries can save energy and improve their daily duration while using dark UI.

Better for dark environments

Most of us use their smartphone and laptops while at home. Such environments are typically not so bright. The dark mode can help the use of the application while indoor, without causing visual disturbances.

Better for people

Some people with — or without — visual diseases, like epilepsy, can have unfortunate events by being flashed by bright applications. Having a dark mode means being more accessible.

Preparing styles

A very simple theme switcher should offer at least 3 options:

  • Dark theme
  • Light theme
  • Automatic theme (should be on by default)

Wait, what’s the automatic theme? Well, modern operating systems allow users to change the global visual appearance by setting os-wide options that enable the dark or light mode. The automatic option make sure to respect the OS preference if the user has not specified any theme.

To make this even more simple, we’ll use PostCSS and a simple but useful plugin called postcss-dark-theme-class.

yarn add postcss-dark-theme-class

This plugin will do 70% of the work, once installed, add it to your PostCSS config and configure the selectors you want to use to activate the correct theme, which will be used by the plugin to generate the correct CSS:

module.exports = {   plugins: [     /* ...other plugins */      require('postcss-dark-theme-class')({       darkSelector: '[data-theme="dark"]',       lightSelector: '[data-theme="light"]'     })   ] }

Once the plugin is up and running, we can start defining our dark and light themes using a CSS specific media query prefers-color-scheme. This special media query will handle the automatic part of our themes by applying the correct theme based on the user’s OS preferences:

:root {   --accent-color: hsl(226deg 100% 50%);   --global-background: hsl(0 0% 100%);   --global-foreground: hsl(0 0% 0%); }  @media (prefers-color-scheme: dark) {   :root {     --accent-color: hsl(226deg 100% 50%);     --global-background: hsl(0 0% 0%);     --global-foreground: hsl(0 0% 100%);   } }

If the user is using a dark version of his OS, the set inside the media query will apply, overwriting others, otherwhise the set of properties outside the media query is used. Since it’s pure CSS, this behaviour is on by default.

Browsers will now adapt the color scheme automatically based on the users’ OS preferences. Nice done! 🚀 Now it’s time to make the theme switcher allow users to specify what theme to use, overriding the OS preference.

Making the theme switcher

s we said, our switcher should have three options, we can use a simple select element, or build a set of buttons:

<button aria-current="true" data-set-theme="auto">Auto</button> <button aria-current="false" data-set-theme="dark">Dark</button> <button aria-current="false" data-set-theme="light">Light</button>

We’ll build the switcher using vanilla JS, but you can do it with any framework you want, the concept is the same: we have to add the selectors we defined inside the PostCSS plugin to the root element, based on the clicked button.

const html = document.documentElement const themeButtons = document.querySelectorAll('[data-set-theme]');  themeButtons.forEach((button) => { 	const theme = button.dataset.setTheme;  	button.addEventListener('click', () => { 		html.dataset.theme = theme; 	}) })

Each time we click on a theme button, the value set as data-set-theme is applied as value of the data-theme attribute on the document root element.

Check it live:

Where is the magic?

The magic is made by postcss-dark-theme-class — which will add our [data-theme] custom attribute to the :root selectors we wrote — during the CSS transpilation. Here what it generates from our code:

/* Our automatic and user specified light theme */ :root {   --accent-color: hsl(226deg, 100%, 50%);   --global-background: hsl(0, 0%, 100%);   --global-foreground: hsl(0, 0%, 0%); }  /* Our automatic dark theme */ @media (prefers-color-scheme: dark) {   :root:not([data-theme="light"]) {     --accent-color: hsl(226deg, 100%, 50%);     --global-background: hsl(0, 0%, 0%);     --global-foreground: hsl(0, 0%, 100%);   } }  /* Our dark theme specified by the user */ :root[data-theme="dark"] {   --accent-color: hsl(226deg, 100%, 50%);   --global-background: hsl(0, 0%, 0%);   --global-foreground: hsl(0, 0%, 100%); }

Bonus tip

You may notice that the --accent-color custom property defined inside themes doesn’t change. If you have colors that will not change based on the theme, you can remove them from the prefers-color-scheme at-rule.

In this way, they will not be duplicated and the one defined outside the media query will always apply.

:root {   --accent-color: hsl(226deg 100% 50%);   --global-background: hsl(0 0% 100%);   --global-foreground: hsl(0 0% 0%); }  @media (prefers-color-scheme: dark) {   :root {     --global-background: hsl(0 0% 0%);     --global-foreground: hsl(0 0% 100%);   } }

The post Making dark theme switcher with PostCSS. appeared first on CSS-Tricks.

CSS-Tricks

, , , ,

Where to Learn WordPress Theme Development

Over a decade ago, I did a little three-part video series on Designing for WordPress. Then I did other series with the same spirit, like videocasting the whole v10 redesign, a friend’s website, and even writing a book. Those are getting a little long in the tooth though. You might still learn from watching them if you’re getting into WordPress theme development, but there will be moments that feel very aged (old UI’s and old versions of software). All the code still works though, because WordPress is great at backward compatibility. I still hear from people who found those videos very helpful for them.

But since time has pressed on, and I was recently asked what resources I would suggest now, I figured I’d have a look around and see what looks good to me.

Do you like how I plopped the WordPress logo over some stock art I bought that features both a computer and a chalkboard, by which to evoke a feeling of “learning”? So good. I know.

Who are we talking to?

There’s a spectrum of WordPress developers, from people who don’t know any code at all or barely touch it, to hardcore programming nerds building custom everything.

  1. Pick out a theme that looks good, use it.
  2. 🤷‍♂️
  3. 🤷‍♂️
  4. 🤷‍♂️
  5. 🤷‍♂️
  6. Hardcore programmer nerd.

I can’t speak to anybody on either edge of that spectrum. There is this whole world of people in the middle. They can code, but they aren’t computer science people. They are get the job done people. Maybe it’s something like this:

  1. Pick out a theme that will work, use it.
  2. Start with a theme, customize it a bit using built-in tools.
  3. Start with a theme, hack it up with code to do what you need it to do.
  4. Start from scratch, build out what you need.
  5. Start from scratch, build a highly customized site.
  6. Hardcore programmer nerd.

I’ve always been somewhere around #4, and I think that’s a nice sweet spot. I try to let off-the-shelf WordPress and big popular plugins do the heavy lifting, but I’ll bring-my-own front-end (HTML, CSS, and JavaScript) and customize what I have to. I’m making templates. I’m writing queries. I’m building blocks. I’m modularizing where I can.

I feel powerful in that zone. I can build a lot of sites that way, almost by myself. So where are the resources today that help you learn this kind of WordPress theme development? Lemme see what I can find.

Wing it, old school

There is something to be said for learning by doing. Trial by fire. I’ve learned a lot under these circumstances in my life.

The trick here is to get WordPress installed on a live server and then play with the settings, plugins, customizer, and edit the theme files themselves to make the site do things. You’ll find HTML in those theme files — hack it up! You’ll see PHP code spitting out content. Can you tell what and how to manipulate it? You’ll find a CSS file in the theme — edit that sucker!

Editing a WordPress theme and seeing what happens

The official documentation can help you somewhat here:

To some degree, I’m a fan of doing it live (on a production website) because it lends a sense of realness to what you are doing when you are a beginner. The stakes are high there, giving you a sense of the power you have. When I make these changes, they are for anyone in the world with an internet connection to see.

I did this in my formative years by buying a domain name and hosting, installing WordPress on that hosting, logging into it with SFTP credentials, and literally working on the live files. I used Coda, which is still a popular app, and is being actively developed into a new version of itself as I write.

This is Nova, a MacOS code editor from Panic that has SFTP built-in.

Hopefully, the stakes are real but low. Like you’re working on a pet project or your personal site. At some point, hacking on production sites becomes too dangerous of an idea. One line of misplaced PHP syntax can take down the entire site.

If you’re working on something like a client site, you’ll need to upgrade that workflow.

Modern winging it

The modern, healthy, standard way for working on websites is:

  1. Work on them locally.
  2. Use version control (Git), where new work is done in branches of the master branch.
  3. Deployment to the production website is done when code is pushed to the master branch, like your development branch is merged in.

I’ve done a recent video on this whole workflow as I do it today. My toolset is:

  • Work locally with Local by Flywheel.
  • My web hosting is also Flywheel, but that isn’t required. It could be anything that gives you SFTP access and runs what WordPress needs: Apache, PHP, and MySQL. Disclosure, Flywheel is a sponsor here, but because I like them and their service :).
  • Code is hosted on a private repo on GitHub.
  • Deployment to the Flywheel hosting is done by Buddy. Buddy watches for pushes to the master branch and moves the files over SFTP to the production site.
Local by Flywheel

Now that you have a local setup, you can go nuts. Do whatever you want. You can’t break anything on the live site, so you’re freer to make experimental changes and just see what happens.

When working locally, it’s likely you’ll be editing files with a code editor. I’d say the most popular choice these days is the free VS Code, but there is also Atom and Sublime, and fancier editors like PhpStorm.

The freedom of hacking on files is especially apparent once you’ve pushed your code up to a Git repo. Once you’ve done that, you have the freedom of reverting files back to the state of the last push.

I use the Git software Tower, and that lets me can see what files have changed since I last committed code. If I’ve made a mistake, caused a problem, or done something I don’t like — even if I don’t remember exactly what I changed — I can discard those changes back to their last state. That’s a nice level of freedom.

When I do commit code, to master or by merging a branch into master, that’s when Buddy kicks in and deploys the changes to the production site.

CSS-Tricks itself is a WordPress site, which has continuously evolved over 13 years.

But like, where do you start?

We’re talking about WordPress theme development here, so you start with a theme. Themes are literally folders of files in your WordPress installation.

root   - /wp-content/     - /themes/        - /theme-name/

WordPress comes with some themes right out of the box. As I write, the Twenty Twenty theme ships with WordPress, and it’s a nice one! You could absolutely start your theme hackin’ on that.

Themes tend to have some opinions about how they organize themselves and do things, and Twenty Twenty is no different. I’d say, perhaps controversially, that there is no one true way to organize your theme, so long as it’s valid code and does things the “WordPress” way. This is just something you’ll have to get a feel for as you make themes.

Starter themes

Starter themes were a very popular way to start building a theme from scratch in my day. I don’t have a good sense if that’s still true, but the big idea was a theme with all the basic theme templates you’ll need (single blog post pages, a homepage, a 404 page, search results page, etc.) with very little markup and no styling at all. That way you have an empty canvas from which to build out all your HTML, CSS, and JavaScript yourself to your liking. Sorta like you’re building any other site from scratch with these core technologies, only with some PHP in there spitting out content.

There was a theme called Starkers that was popular, but it’s dead now. I made one called BLANK myself but haven’t touched that in a long time. In looking around a bit, I found some newer themes with this same spirit. Here’s the best three I found:

I can’t personally vouch for them, but they’ve all been updated somewhat recently and look like pretty good starting points to me. I’d give them a shot in the case that I was starting from absolute scratch on a project. I’d be tempted to download one and then spruce it up exactly how I like it and save that as my own starter in case I needed to do it again.

It feels worth mentioning that a lot of web development isn’t starting from scratch, but rather working on existing projects. In that case, the process is still getting a local environment set up; you just aren’t starting from scratch, but with the existing theme. I’d suggest duplicating the theme and changing the name while you hack on it, so even if you deploy it, it doesn’t affect the live theme. Others might suggest using the starter as a “parent” theme, then branching off into a “child” theme.

To get your local development environment all synced up with exactly what the production website is like, I think the best tool is WP DB Migrate Pro, which can yank down the production database to your local site and all the media files (paid product and a paid add-on, worth every penny).

Fancier Starter Themes

Rather than starting from absolute scratch, there are themes that come with sensible defaults and even modern build processes for you start with. The idea is that building a site with essentially raw HTML, CSS, and JavaScript, while entirely doable, just doesn’t have enough modern conveniences to be comfortable.

Here are some.

  • Morten Rand-Hendriksen has a project called WP Rig that has all sorts of developer tools built into it. A Gulp-based build process spins up a BrowserSync server for auto updating. JavaScript gets processed in Babel. CSS gets processed in PostCSS, and code is linted. He teaches WordPress with it.
  • Roots makes a theme called Sage that comes with a templating engine, your CSS framework of choice, and fancy build process stuff.
  • Ignition has a build process and all sorts of helpers.
  • Timber comes with a templating engine and a bunch of code helpers.

I think all these are pretty cool, but are also probably not for just-starting-out beginner developers.

Books

This is tough because of how many there are. In a quick Google search, I found one site selling fifteen WordPress books as a bundle for $ 9.99. How would you even know where to start? How good can they be for that rock bottom price? I dunno.

I wrote a book with Jeff Starr ages ago called Digging Into WordPress. After all these years, Jeff still keeps the book up to date, so I’d say that’s a decent choice! Jeff has other books like The Tao of WordPress and WordPress Themes In Depth.

A lot of other books specifically about WordPress theme development are just fairly old. 2008-2015 stuff. Again, not that there isn’t anything to be learned there, especially as WordPress doesn’t change that rapidly, but still, I’d want to read a book more recent that half a decade old. Seems like a big opportunity for a target audience as large as WordPress users and developers. Or if there is already stuff that I’m just not finding, lemme know in the comments.

Perhaps learning is shifting so much toward online that people don’t write books as much…

Online learning courses

Our official learning partner Frontend Masters has one course on WordPress focused on JavaScript and WordPress, so that might not be quite perfect for learning the basics of theme development. Still, fascinating stuff.

Here’s some others that looked good to me while looking around:

Zac’s course looks like the most updated and perhaps the best option there.

A totally different direction for theme Development

One way to build a site with WordPress is not to use WordPress themes at all! Instead, you can use the WordPress API to suck data out of WordPress and build a site however the heck you please.

This idea of decoupling the CMS and the front end you build is pretty neat. It’s often referred to as using a “headless” CMS. It’s not for everyone. (One big reason is that, in a way, it doubles your technical debt.). But it can bring a freedom to both the CMS and the front end to evolve independently.

The post Where to Learn WordPress Theme Development appeared first on CSS-Tricks.

CSS-Tricks

, , ,
[Top]

How to Build Vue Components in a WordPress Theme

Intrigued by the title and just wanna see some code? Skip ahead.

A few months ago, I was building a WordPress website that required a form with a bunch of fancy conditional fields. Different options and info were required for different choices you could make on the form, and our client needed complete control over all fields 1. In addition, the form needed to appear in multiple places in each page, with slightly different configs.

And the header instance of the form needed to be mutually exclusive with the hamburger menu, so that opening one closes the other.

And the form had text content that was relevant to SEO.

And we wanted the server response to present some cute animated feedback.

(Phew.)

The whole thing felt complex enough that I didn’t want to handle all that state manually. I remembered reading Sarah Drasner’s article “Replacing jQuery With Vue.js: No Build Step Necessary” which shows how to replace classic jQuery patterns with simple Vue micro-apps. That seemed like a good place to start, but I quickly realized that things would get messy on the PHP side of WordPress.

What I really needed were reusable components

PHP → JavaScript

I love the static-first approach of Jamstack tools, like Nuxt, and was looking to do something similar here — send the full content from the server, and progressively enhance on the client side.

But PHP doesn’t have a built-in way to work with components. It does, however, support require-ing files inside other files 2. WordPress has an abstraction of require called get_template_part, that runs relative to the theme folder and is easier to work with. Dividing code into template parts is about the closest thing to components that WordPress provides 3.

Vue, on the other hand, is all about components — but it can only do its thing after the page has loaded and JavaScript is running.

The secret to this marriage of paradigms turns out to be the lesser-known Vue directive inline-template. Its great and wonderful powers allow us to define a Vue component using the markup we already have. It’s the perfect middle ground between getting static HTML from the server, and mounting dynamic DOM elements in the client.

First, the browser gets the HTML, then Vue makes it do stuff. Since the markup is built by WordPress, rather than by Vue in the browser, components can easily use any information that site administrators can edit. And, as opposed to .vue files (which are great for building more app-y things), we can keep the same separation of concerns we use for the whole site — structure and content in PHP, style in CSS, and functionality in JavaScript.

To show how this all fits together, we’re going to build a few features for a recipe blog. First, we’ll add a way for users to rate recipes. Then we’ll build a feedback form based on that rating. Finally, we’ll allow users to filter recipes, based on tags and rating.

We’ll build a few components that share state and live on the same page. To get them to play nicely together — and to make it easy to add additional components in the future — we’ll make the whole page our Vue app, and register components inside it.

Each component will live in its own PHP file and be included in the theme using get_template_part.

Laying the groundwork

There are a few special considerations to take into account when applying Vue to existing pages. The first is that Vue doesn’t want you loading scripts inside it — it will send ominous errors to the console if you do. The easiest way to avoid this is to add a wrapper element around the content for every page, then load scripts outside of it (which is already a common pattern for all kinds of reasons). Something like this:

<?php /* header.php */ ?>  <body <?php body_class(); ?>> <div id="site-wrapper">
<?php /* footer.php */ ?>   </div> <!-- #site-wrapper --> <?php wp_footer(); ?>

The second consideration is that Vue has to be called at the end of body element so that it will load after the rest of the DOM is available to parse. We’ll pass true as the fifth argument  (in_footer) for the wp_enqueue_script  function. Also, to make sure Vue is loaded first, we’ll register it as a dependency of the main script.

<?php // functions.php  add_action( 'wp_enqueue_scripts', function() {   wp_enqueue_script('vue', get_template_directory_uri() . '/assets/js/lib/vue.js', null, null, true); // change to vue.min.js for production   wp_enqueue_script('main', get_template_directory_uri() . '/assets/js/main.js', 'vue', null, true);

Finally, in the main script, we’ll initialize Vue on the site-wrapper element.

// main.js  new Vue({   el: document.getElementById('site-wrapper') })

The star rating component

Our single post template currently looks like this:

<?php /* single-post.php */ ?>  <article class="recipe">   <?php /* ... post content */ ?>    <!-- star rating component goes here --> </article>

We’ll register the star rating component and add some logic to manage it:

// main.js  Vue.component('star-rating', {   data () {     return {       rating: 0     }   },   methods: {     rate (i) { this.rating = i }   },   watch: {     rating (val) {       // prevent rating from going out of bounds by checking it to on every change       if (val < 0)          this.rating = 0       else if (val > 5)          this.rating = 5        // ... some logic to save to localStorage or somewhere else     }   } })  // make sure to initialize Vue after registering all components new Vue({   el: document.getElementById('site-wrapper') })

We’ll write the component template in a separate PHP file. The component will comprise six buttons (one for unrated, 5 with stars). Each button will contain an SVG with either a black or transparent fill.

<?php /* components/star-rating.php */ ?>  <star-rating inline-template>   <div class="star-rating">     <p>Rate recipe:</p>     <button @click="rate(0)">       <svg><path d="..." :fill="rating === 0 ? 'black' : 'transparent'"></svg>     </button>     <button v-for="(i in 5) @click="rate(i)">       <svg><path d="..." :fill="rating >= i ? 'black' : 'transparent'"></svg>     </button>   </div> </star-rating>

As a rule of thumb, I like to give a component’s top element a class name that is identical to that of the component itself. This makes it easy to reason between markup and CSS (e.g. <star-rating> can be thought of as .star-rating).

And now we’ll include it in our page template.

<?php /* single-post.php */ ?>  <article class="recipe">   <?php /* post content */ ?>    <?php get_template_part('components/star-rating'); ?> </article>

All the HTML inside the template is valid and understood by the browser, except for <star-rating>. We can go the extra mile to fix that by using Vue’s is directive:

<div is="star-rating" inline-template>...</div>

Now let’s say that the maximum rating isn’t necessarily 5, but is controllable by the website’s editor using Advanced Custom Fields, a popular WordPress plugin that adds custom fields for pages, posts and other WordPress content. All we need to do is inject that value as a prop of the component that we’ll call maxRating:

<?php // components/star-rating.php  // max_rating is the name of the ACF field $  max_rating = get_field('max_rating'); ?> <div is="star-rating" inline-template :max-rating="<?= $  max_rating ?>">   <div class="star-rating">     <p>Rate recipe:</p>     <button @click="rate(0)">       <svg><path d="..." :fill="rating === 0 ? 'black' : 'transparent'"></svg>     </button>     <button v-for="(i in maxRating) @click="rate(i)">       <svg><path d="..." :fill="rating >= i ? 'black' : 'transparent'"></svg>     </button>   </div> </div>

And in our script, let’s register the prop and replace the magic number 5:

// main.js  Vue.component('star-rating', {   props: {     maxRating: {       type: Number,       default: 5 // highlight     }   },   data () {     return {       rating: 0     }   },   methods: {     rate (i) { this.rating = i }   },   watch: {     rating (val) {       // prevent rating from going out of bounds by checking it to on every change       if (val < 0)          this.rating = 0       else if (val > maxRating)          this.rating = maxRating        // ... some logic to save to localStorage or somewhere else     }   } })

In order to save the rating of the specific recipe, we’ll need to pass in the ID of the post. Again, same idea:

<?php // components/star-rating.php  $  max_rating = get_field('max_rating'); $  recipe_id = get_the_ID(); ?> <div is="star-rating" inline-template :max-rating="<?= $  max_rating ?>" recipe-id="<?= $  recipe_id ?>">   <div class="star-rating">     <p>Rate recipe:</p>     <button @click="rate(0)">       <svg><path d="..." :fill="rating === 0 ? 'black' : 'transparent'"></svg>     </button>     <button v-for="(i in maxRating) @click="rate(i)">       <svg><path d="..." :fill="rating >= i ? 'black' : 'transparent'"></svg>     </button>   </div> </div>
// main.js  Vue.component('star-rating', {   props: {     maxRating: {        // Same as before     },     recipeId: {       type: String,       required: true     }   },   // ...   watch: {     rating (val) {       // Same as before        // on every change, save to some storage       // e.g. localStorage or posting to a WP comments endpoint       someKindOfStorageDefinedElsewhere.save(this.recipeId, this.rating)     }   },   mounted () {     this.rating = someKindOfStorageDefinedElsewhere.load(this.recipeId)       } })

Now we can include the same component file in the archive page (a loop of posts), without any additional setup:

<?php // archive.php  if (have_posts()): while ( have_posts()): the_post(); ?> <article class="recipe">   <?php // Excerpt, featured image, etc. then:   get_template_part('components/star-rating'); ?> </article> <?php endwhile; endif; ?>

The feedback form

The moment a user rates a recipe is a great opportunity to ask for more feedback, so let’s add a little form that appears right after the rating is set.

// main.js  Vue.component('feedback-form', {   props: {     recipeId: {       type: String,       required: true     },     show: { type: Boolean, default: false }   },   data () {     return {       name: '',       subject: ''       // ... other form fields     }   } })
<?php // components/feedback-form.php  $  recipe_id = get_the_ID(); ?> <div is="feedback-form" inline-template recipe-id="<?= $  recipe_id ?>" v-if="showForm(recipe-id)">   <form class="recipe-feedback-form" id="feedback-form-<?= $  recipe_id ?>">     <input type="text" :id="first-name-<?= $  recipe_id ?>" v-model="name">     <label for="first-name-<?= $  recipe_id ?>">Your name</label>     <?php /* ... */ ?>   </form> </div>

Notice that we’re appending a unique string (in this case, recipe-id) to each form element’s ID. This is to make sure they all have unique IDs, even if there are multiple copies of the form on the page.

So, where do we want this form to live? It needs to know the recipe’s rating so it knows it needs to open. We’re just building good ol’ components, so let’s use composition to place the form inside the <star-rating>:

<?php // components/star-rating.php  $  max_rating = get_field('max_rating'); $  recipe_id = get_the_ID(); ?> <div is="star-rating" inline-template :max-rating="<?= $  max_rating ?>" recipe-id="<?= $  recipe_id ?>">   <div class="star-rating">     <p>Rate recipe:</p>     <button @click="rate(0)">       <svg><path d="..." :fill="rating === 0 ? 'black' : 'transparent'"></svg>     </button>     <button v-for="(i in maxRating) @click="rate(i)">       <svg><path d="..." :fill="rating >= i ? 'black' : 'transparent'"></svg>     </button>     <?php get_template_part('components/feedback-form'); ?>   </div> </div>

If at this point you’re thinking, “We really should be composing both components into a single parent component that handles the rating state,” then please give yourself 10 points and wait patiently.

A small progressive enhancement we can add to make the form usable without JavaScript, is to give it the traditional PHP action and then override it in Vue. We’ll use @submit.prevent to prevent the original action, then run a submit method to send the form data in JavaScript.

<?php // components/feedback-form.php  $  recipe_id = get_the_ID(); ?> <div is="feedback-form" inline-template recipe-id="<?= $  recipe_id ?>">   <form action="path/to/feedback-form-handler.php"        @submit.prevent="submit"       class="recipe-feedback-form"        id="feedback-form-<?= $  recipe_id ?>">     <input type="text" :id="first-name-<?= $  recipe_id ?>" v-model="name">     <label for="first-name-<?= $  recipe_id ?>">Your name</label>    <!-- ... -->   </form> </div>

Then, assuming we want to use fetch, our submit method can be something like this:

// main.js  Vue.component('feedback-form', {   // Same as before    methods: {     submit () {       const form = this.$  el.querySelector('form')       const URL = form.action       const formData = new FormData(form)       fetch(URL, {method: 'POST', body: formData})         .then(result => { ... })         .catch(error => { ... })     }   } })

OK, so what do we want to do in .then and .catch? Let’s add a component that will show real-time feedback for the form’s submit status. First let’s add the state to track sending, success, and failure, and a computed property telling us if we’re pending results.

// main.js  Vue.component('feedback-form', {   // Same as before    data () {     return {       name: '',       subject: ''       // ... other form fields       sent: false,       success: false, ​​      error: null     }   },   methods: {     submit () {       const form = this.$  el.querySelector('form')       const URL = form.action       const formData = new FormData(form)       fetch(URL, {method: 'POST', body: formData})         .then(result => {            this.success = true          })         .catch(error => {            this.error = error          })       this.sent = true     }   } })

To add the markup for each message type (success, failure, pending), we could make another component like the others we’ve built so far. But since these messages are meaningless when the server renders the page, we’re better off rendering them only when necessary. To do this we’re going to place our markup in a native HTML <template> tag, which doesn’t render anything in the browser. Then we’ll reference it by id as our component’s template.

<?php /* components/form-status.php */ ?>  <template id="form-status-component" v-if="false">   <div class="form-message-wrapper">     <div class="pending-message" v-if="pending">       <img src="<?= get_template_directory_uri() ?>/spinner.gif">       <p>Patience, young one.</p>     </div>     <div class="success-message" v-else-if="success">       <img src="<?= get_template_directory_uri() ?>/beer.gif">       <p>Huzzah!</p>     </div>     <div class="success-message" v-else-if="error">       <img src="<?= get_template_directory_uri() ?>/broken.gif">       <p>Ooh, boy. It would appear that: {{ error.text }}</p>     </div>   </div </template>

Why add v-if="false" at the top, you ask? It’s a tricky little thing. Once Vue picks up the HTML <template>, it will immediately think of it as a Vue <template> and render it. Unless, you guessed it, we tell Vue not to render it. A bit of a hack, but there you have it.

Since we only need this markup once on the page, we’ll include the PHP component in the footer.

<?php /* footer.php */ ?>  </div> <!-- #site-wrapper --> <?php get_template_part('components/form-status'); ?> <?php wp_footer(); ?>

Now we’ll register the component with Vue…

// main.js  Vue.component('form-status', {   template: '#form-status-component'   props: {     pending: { type: Boolean, required: true },     success: { type: Boolean, required: true },     error: { type: [Object, null], required: true },   } })

…and call it inside our form component:

<?php // components/feedback-form.php  $  recipe_id = get_the_ID(); ?> <div is="feedback-form" inline-template recipe-id="<?= $  recipe_id ?>">   <form action="path/to/feedback-form-handler.php"          @submit.prevent="submit"         class="recipe-feedback-form"          id="feedback-form-<?= $  recipe_id ?>">     <input type="text" :id="first-name-<?= $  recipe_id ?>" v-model="name">     <label for="first-name-<?= $  recipe_id ?>">Your name</label>     <?php // ... ?>   </form>   <form-status v-if="sent" :pending="pending" :success="success" :error="error" /> </div>

Since we registered <form-status> using Vue.component, it’s available globally, without specifically including it in the parent’s components: { }.

Filtering recipes

Now that users can personalize some bits of their experience on our blog, we can add all kinds of useful functionality. Specifically, let’s allow users to set a minimum rating they want to see, using an input at the top of the page.
The first thing we need is some global state to track the minimum rating set by the user. Since we started off by initializing a Vue app on the whole page, global state will just be data on the Vue instance:

// main.js // Same as before  new Vue({   el: document.getElementById('site-wrapper'),   data: {     minimumRating: 0   } })

And where can we put the controls to change this? Since the whole page is the app, the answer is almost anywhere. For instance, at the top of the archive page:

<?php /* archive.php */ ?>  <label for="minimum-rating-input">Only show me recipes I've rated at or above:</label> <input type="number" id="minimum-rating-input" v-model="minimumRating">  <?php if (have_posts()): while ( have_posts()): the_post(); ?> <article class="recipe">   <?php /* Post excerpt, featured image, etc. */ ?>    <?php get_template_part('components/star-rating'); ?> </article> <?php endwhile; endif; ?>

As long as it’s inside our site-wrapper and not inside another component, it’ll just work. If we want, we could also build a filtering component that would change the global state. And if we wanted to get all fancy, we could even add Vuex to the mix (since Vuex can’t persist state between pages by default, we could add something like vuex-persist to use localStorage).

So, now we need to hide or show a recipe based on the filter. To do this, we’ll need to wrap the recipe content in its own component, with a v-show directive. It’s probably best to use the same component for both the single page and the archive page. Unfortunately, neither require nor get_template_part can pass parameters into the called file — but we can use global variables:

<?php /* archive.php */ ?>  <label for="minimum-rating-input">Only show me recipes I've rated at or above:</label> <input type="number" id="minimum-rating-input" v-model="minimumRating">  <?php  $  is_archive_item = true; if (have_posts()): while ( have_posts()): the_post();   get_template_part('components/recipe-content'); endwhile; endif; ?>

We can then use $ is_archive_item as a global variable inside the PHP component file to check if it is set and true. Since we won’t need to hide the content on the single post page, we’ll conditionally add the v-show directive.

<?php  // components/recipe-content.php  global $  is_archive_item; ?> <div is="recipe-content">   <article class="recipe"      <?php if ($  is_archive_item): ?>        v-show="show"     <?php endif; ?>   >     <?php     if ($  is_archive_item):       the_excerpt();     else       the_content();     endif;          get_template_part('components/star-rating');     ?>   </article> </div>

In this specific example, we could have also tested with  is_archive() inside the component, but in most cases we’ll need to set explicit props.

We’ll need to move the rating state and logic up into the <recipe-content> component so it can know if it needs to hide itself. Inside <star-rating>, we’ll make a custom v-model by replacing rating with value, and this.rating = i with  $ emit('input', i) as well . So our component registration will now look like this:

// main.js  Vue.component('recipe-content', {   data () {     rating: 0   },   watch: {     rating (val) {       // ...     }   },   mounted () {     this.rating = someKindOfStorageDefinedElsewhere.load(this.recipeId)       } })  Vue.component('star-rating', {   props: {     maxRating: { /* ... */ },     recipeId: { /* ... */ },     value: { type: Number, required: true }   },   methods: {     rate (i) { this.$  emit('input', i) }   }, })

We’ll add v-model in star-rating.php and change rating to value. In addition, we can now move the <feedback-form> up into <recipe-content>:

<?php // components/star-rating.php  $  max_rating = get_field('max_rating'); $  recipe_id = get_the_ID(); ?> <div is="star-rating"    inline-template    :max-rating="<?= $   max_rating ?>"    recipe-id="<?= $  recipe_id ?>"    v-model="value" >   <div class="star-rating">     <p>Rate recipe:</p>     <button @click="rate(0)">       <svg><path d="..." :fill="value === 0 ? 'black' : 'transparent'"></svg>     </button>     <button v-for="(i in maxRating) @click="rate(i)">       <svg><path d="..." :fill="value >= i ? 'black' : 'transparent'"></svg>     </button>   </div> </div>
<?php // components/recipe-content.php  global $  is_archive_item; ?> <div is="recipe-content">   <article class="recipe"      <?php if ($  is_archive_item): ?>        v-show="show"     <?php endif; ?>   >          <?php     if ($  is_archive_item):       the_excerpt();     else       the_content();     endif;          get_template_part('components/star-rating');     get_template_part('components/feedback-form');     ?>   </article> </div>

Now everything is set up so the initial render shows all recipes, and then the user can filter them based on their rating. Moving forward, we could add all kinds of parameters to filter content. And it doesn’t have to be based on user input — we can allow filtering based on the content itself (e.g. number of ingredients or cooking time) by passing the data from PHP to Vue.

Conclusion

Well, that was a bit of a long ride, but look at what we’ve built: independent, composable, maintainable, interactive, progressively enhanced components in our WordPress theme. We brought together the best of all worlds!

I’ve been using this approach in production for a while now, and I love the way it allows me to reason about the different parts of my themes. I hope I’ve inspired you to try it out too.


  1. Of course, two days before launch, the client’s legal department decided they don’t want to collect all that info. Currently the live form is but a shadow of its development self.
  2. Fun fact: Rasmus Lerdorf said that his original intent was for PHP to be templating only, with all business logic handled in C. Let that sink in for a moment. Then clear an hour from your schedule and watch the whole talk.
  3. There are third-party WordPress templating engines that can compile down to optimized PHP. Twig, for example, comes to mind. We’re trying to go the reverse route and send vanilla PHP to be handled by JavaScript.

The post How to Build Vue Components in a WordPress Theme appeared first on CSS-Tricks.

CSS-Tricks

, , ,
[Top]

Variations on Theme: Reinventing Type on the Web

If anyone knows anything about me, it’s usually one of two things: that I have two outrageously fluffy dogs, or that I like fonts and typography. Like, really really like them. So while I am super excited about how well Tristan is doing with his hydrotherapy —we’re walking 50% further than he was able just a couple months ago, without having to take breaks in the middle—I’m even more riled up about variable fonts.

Image of Tristan and Tillie explaining variable fonts

I know, you’re probably all really shocked.

I’ve been pretty single-minded about them since their introduction three years ago, and think they are going to be a massively Big Thing. But it just hasn’t gotten to that tipping point—yet. But a few things are coming together that make me think this just might be the year. Let me step back a bit and explain.

See the Pen
Variable font, outlined
by Jason Pamental (@jpamental)
on CodePen.

Plex Sans Variable, with outlines showing the range of weights and widths possible with a single file

The future is variable

Variable fonts are an evolution of the OpenType font specification that allows a single file to contain all of the variations of width, weight, slant, italics, and virtually any other permutation of a typeface the type designer want’s to expose—all in a single file. That one file is highly efficient, so it’s far smaller than all of the individual files. For the web, that means we can in many cases save considerable data download, reduce the number of HTTP requests, and in general vastly increase the design flexibility at our disposal. A pretty big change from those plain ‘ol static web fonts we’ve been using for the past 10 years. There’s loads of good stuff out there about them (I’ve written a few here, here, here, and here), but you can also browse the tag right here on CSS Tricks.

This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.

Desktop

Chrome Opera Firefox IE Edge Safari
66 53 62 No 17 11

Mobile / Tablet

iOS Safari Opera Mobile Opera Mini Android Android Chrome Android Firefox
11.0-11.2 No No 76 78 68
Variable fonts are viewable by 87% of devices on the web according to caniuse.com.

Now that browser support is pretty complete (for the most part everything but IE11 and a couple of Android browsers like Baidu and UC), availability of variable fonts is improving (check out v-fonts.com), and Google is launching support for them via their new API—it feels like this might finally be the breakout year for them.

What excites me the most about that is what we’ll see people create. I’m sure at first it will be lots of “rip and replace”, similar to what we saw on the Nielson/Norman group site with their inclusion of Source Sans Variable on their site last year, or what Google has been testing with Oswald Variable on sites 148 million times a day for the past several months. Basically just using that instead of a few static instances to reap the benefits of faster page loads and less code.

Which is great, but only just a beginning.

What I’m looking forward to seeing is people embracing the full range of what these fonts can do. Going form ultra-light to super-heavy, super-condensed to extra-wide. Whatever the fonts support is there for us to explore. I’m hoping to see designers dive in and explore the power of great typography. Type that speaks more and varied volumes to help guide us around our sites and apps. Type set in motion that can lead us from here to there.

See the Pen
Layout variations, part deux
by Jason Pamental (@jpamental)
on CodePen.

This is one of the explorations I did for my newsletter and talks, playing with notions of legibility and the tension between reading fast and slow.

Will some of it be awful?

Probably. But so were a lot of early responsive websites. And lots of sites with static web fonts when they first launched. Or Flash-based sites. All of these have been evolutions of what we can design and make on the web. It’s up to us to do it well instead of poorly. I’m excited to learn together.

After all, if type is the voice of our words—with variable fonts—that voice has become a chorus.

The post Variations on Theme: Reinventing Type on the Web appeared first on CSS-Tricks.

CSS-Tricks

, , ,
[Top]