DEPRECATED: We have stopped developing this, check out our new boilerplate stackhaus.
Vuehaus is a boilerplate used to build fast, responsive WordPress templates with Vue.js. Built by Funkhaus.
Head over to the tutorial to learn how to build Vue.js + WordPress sites with Vuehaus!
themes/
directory: git clone https://github.com/funkhaus/vuehaus my-theme
cd my-theme
npm install
npm run dev
npm run build
npm run deploy
Paste these anywhere in your <script>
tags to use them in your own templates. This assumes you've included
import _get from 'lodash/get'
All results from The Loop:
_get(this.$store, 'state.loop')
The first result from The Loop:
this.$store.getters.post
The featured image from the first result in The Loop:
_get(this.$store.getters.post, 'featured_attachment')
The children of the first result of The Loop:
_get(this.$store.getters.post, 'related.children')
Vuehaus includes the Google/Typekit Web Font Loader in parts/font-loader.php
. Follow the instructions on that repo to load fonts from Google, Typekit, or your own uploads.
// font loader example
WebFontConfig = {
google: {
// your google fonts families here
families: []
},
typekit: {
id: ''
},
custom: {
// your custom font families here
families: [],
urls: [
'<?php echo get_template_directory_uri(); ?>/static/fonts/fonts.css'
]
}
}
Place the .svg file in src/svgs/
.
In the script
section of the template where you want to use the SVG, add:
import exampleSvg from 'src/svgs/example.svg'
export default {
data() {
return {
exampleSvg
}
}
}
In the location where you want to use the SVG:
<div v-html="exampleSvg"></div>
That's it!
Vuehaus comes with a component called responsive-image
that provides some built-in image handling, including fading in images as they load. You can pass an image object from Rest-Easy's attachment serializer and it will build itself automatically:
<!-- Build a responsive image component from the featured image of the first post in The Loop -->
<responsive-image :object="$store.state.loop[0].featured_attachment"/>
You can also include any of the following attributes:
<responsive-image
src="https://github.com/funkhaus/vuehaus/raw/master/source-url"
height="height in px"
width="width in px"
aspect="aspect ratio, as percent (ie '56' for 56%)"
size="WordPress-defined size slug"
color="background color of pre-loaded space"
/>
You must include either an object
or a src
parameter on a responsive-image
element; all other values are optional.
All images in Vuehaus have an associated video URL (saved as a metafield called custom_video_url
). You can access this in a serialized image in Vuex with image.videoUrl
.
Vuehaus supports SCSS out of the box, and comes with a style vars file in src/styles/_vars.scss
and the base styling for the entire site in src/styles/_base.scss
.
You can import the vars file in any Vue template like this:
<style lang="scss">
@import 'src/styles/vars';
// Now you have access to the $vars!
</style>
Vuehaus also comes with a few suggested breakpoints in that same vars file - you can use them in a media query like this:
@media #{ $lt-phone } {
// your styling
}
The default breakpoints (with lt
for "less than" and gt
for "greater than") are:
gt-cinema
- only screen and (min-width: 1800px)
lt-desktop
- only screen and (max-width: 1100px)
lt-phone
- only screen and (max-width: 750px)
lt-phone-landscape
- only screen and (max-width: 750px) and (orientation: landscape)
Since URLs can easily change in the WordPress backend, Vuehaus includes a new WP role, Developer, that has access to a set of controls that other roles (even Administrator) can't see. One of these controls is for a page's "Developer ID" - an arbitrary value that can reliably identify a page.
The Developer ID is accessible via a post object's custom_developer_id
property - for example, $post->custom_developer_id
.
If we set the About page's Developer ID to about
, then rewrite the relevant line in add_routes_to_json
like the following:
...
// path_from_dev_id is a Vuehaus function that retrieves a page's relative path from its Developer ID
path_from_dev_id('about') => 'About'
...
This will guarantee that the path to this page will always render the About template, even if the user changes that path later on.
The Developer role in Vuehaus has a few extra capabilities available:
Any missing page in the add_routes_to_json
function (for example, if get_page_by_dev_id('about')
didn't find any pages) would break the given route; a Developer can lock pages to prevent this type of bug. Check the "Prevent non-dev deletion" box in the Developer Meta screen to prevent other users from placing that page in the Trash accidentally.
Check the "Hide Rich Editor" box to prevent non-Developer users from using WordPress's rich editor. This can be helpful to maintain stricter controls over the template and class names in a page's content.
Take a look at the path-to-regexp documentation for examples of routing using regex capabilities.
The routing table in Vuehaus automatically converts a string-string key-value pair to a Vue route object:
array(
path_from_dev_id('my-developer-id') => 'MyComponentName'
)
...turns to:
const router = new VueRouter({
routes: [{ path: '/my-developer-id', component: 'MyComponentName.vue' }]
})
You can take advantage of the Vue router's more advanced capabilities, like redirect/alias, naming, and more by setting the value to an object:
array(
path_from_dev_id('your-developer-id') => array(
// Redirect to a path - in this case, to the path of the first child
'redirect' => get_child_of_dev_id_path('work')
),
path_from_dev_id('your-developer0id', '/:medium*') => array(
// Define a component and a name for the route
'component' => 'WorkGrid',
'name' => 'work-grid'
),
path_from_dev_id('your-developer-id') => array(
// Redirect to a named route
'redirect' => array(
'name': => 'work-grid'
)
)
)
This isn't the limit of the routing table's capabilities - anything the Vue router can do, you can build in the add_routes_to_json
function.
When trying to get the children of the front page, you'll need to use slug_from_dev_id
instead of path_from_dev_id
:
'/' . slug_from_dev_id('front-page') . '/:detail' => 'FrontPageChild',
path_from_dev_id
would return a slash as the relative path to the front page, but slug_from_dev_id
ensures that you're getting the front page's name.
Vuehaus defines a few utility functions to make building the routing table easier:
get_child_of_dev_id($dev_id, $nth_child = 0)
- Get the post object of the nth child (zero-based, default 0
) of a page with the given Developer ID.get_child_of_dev_id_path($dev_id, $nth_child = 0, $after = '')
- Get the relative path of the nth child of a page with the given Developer ID. Adds $after
to the retrieved path.get_children_of_dev_id($dev_id)
- Get the children of a page with the given Developer ID. Returns false
if no page with Developer ID exists or an empty array if no children found.get_page_by_dev_id($dev_id)
- Get the first page with a given Developer ID. Returns the complete WP Post object or false
if none found.path_from_dev_id($dev_id, $after = '')
- Get the relative path of a page with a given Developer ID. Adds $after
to the retrieved path. Returns '#404'
if no page with the given Developer ID is found.slug_from_dev_id($dev_id)
- Get the slug of a page with a given Developer ID.user_is_developer()
- Boolean indicating whether the current logged-in user is a Developer or not.If you need to upgrade your version of Rest-Easy, change the $latest_rest_easy
variable in functions/vuehaus-plugins.php
to match the latest Rest Easy version. You'll be prompted to upgrade the next time you load any page on the WordPress backend.
Vuehaus uses Vuex to handle a site's state. The default store in src/utils/store.js
is set up like this:
{
// From Rest-Easy
site: jsonData.site,
meta: jsonData.meta,
loop: jsonData.loop,
// Vuehaus-specific
transitioning_in: false,
transitioning_out: false,
loaded: true
}
See the Rest-Easy documentation for more information on jsonData
and its properties, as well as the Vuex documentation for Vuex terms like store, state, mutation, etc.
You can commit a mutation from any Vue component by using:
this.$store.commit('MUTATION_NAME', payload)
Default Vuehaus mutations:
'REPLACE_QUERYDATA', { site, meta, loop }
- Replaces the store
's site
, meta
, and loop
properties with the site
, meta
, and loop
properties of the payload.'SET_TRANSITIONING_IN, true | false'
- Sets state.transitioning_in
to the given value.'SET_TRANSITIONING_OUT, true | false'
- Sets state.transitioning_out
to the given value.'SET_LOADED', true | false
- Sets state.loaded
to the given value.'OPEN_MENU'
- Sets state.menuOpened
to true
.'CLOSE_MENU'
- Sets state.menuOpened
to false
.'UPDATE_REFERRAL_ROUTE'
- Sets state.referral
to given referral object.Where mutations are synchronous, actions are asynchronous:
this.$store.dispatch('ACTION_NAME', payload)
Default Vuehaus actions:
'LOAD_AND_REPLACE_QUERYDATA, { path: 'url string' }'
- Runs the following process:
state.loaded
to false
.src/utils/cache.js
(which is a global cache that can be import
ed into any other file) for the given path
key.
If none is found:
'SET_LOADED', false
'REPLACE_QUERYDATA'
with the data from the cache.'SET_LOADED', true
Getters are shortcuts to dynamic state properties:
$store.state.getters.desiredGetter
Default Vuehaus getters include:
loading
- Returns the opposite of $store.state.loaded
.post
- Returns either the first post in $store.state.loop
or, if none, an empty object.referralPath
- Returns either the fullPath
of the current value of $store.state.referral
or, if none, an empty string.Throttled resize and scroll events are available to any child of the App component:
this.$root.$on('throttled.resize', () => {
// your throttled resize event here...
// default: 1 per 10ms
})
this.$root.$on('throttled.scroll', () => {
// your throttled scroll event here...
// default: 1 per 10ms
})
Both events are fired after the $root
element saves updated window dimensions/scroll positions for resize/scroll events.
Vuehaus comes with a few SCSS partials to make writing CSS easier. In a Vue template file:
<style lang="scss">
@import 'src/styles/desired-partial';
</style>
Default partials include:
base
- Style applied in App.vue
, affecting every page on the site.
easings
- Several common easing functions. Includes:
transitions
- Common transitions applied in App.vue
, affecting every page on the site. Includes:
fade
slide-left
slide-right
Usable with:
<transition name="transition-name"><your-code-here/></transition>
vars
- Variables to use across the site. Import in any given template to make global CSS changes much easier to manage. Defaults include:
$white: #ffffff;
$black: #000000;
$font-family: 'Helvetica';
$desktop-padding: 50px;
$mobile-padding: 20px;
$header-height: 80px;
The following are breakpoints that can be used with @media #{$size} { /* your rules here */ }
:
$gt-cinema: "only screen and (min-width: 1800px)";
$lt-desktop: "only screen and (max-width: 1100px)";
$lt-phone: "only screen and (max-width: 750px)";
$lt-phone-landscape: "only screen and (max-width: 750px) and (orientation: landscape)";
vars
includes the following mixins:
fill
- position: absolute
with top
, right
, bottom
, and left
set to 0
.cover
- Centered, no-repeat, background-size: cover
.contain
- Same as cover
, but with background-size: contain
.Vuehaus comes with Gutenberg support out of the box, including a simple way to create and use your own custom blocks.
TL;DR:
To add a new block:
buildBlock
in blocks/src/index.js
npm run block-dev
for development and npm run block-build
for production.The simplest example is creating a new text block:
buildBlock({
// The unique slug of your block (namespaced to your theme) (required)
slug: 'text-block',
// Class applied to the block (required)
class: 'text-block',
// The human-readable name of your block (optional)
name: 'Text Block',
// A short block description (optional)
description: 'A block of text.',
// WP Dash Icon (https://developer.wordpress.org/resource/dashicons/) (optional)
icon: 'format-gallery',
// The block content (required)
content: [
{
// Unique name for this item
name: 'myContent',
// Item type - 'text'
type: 'text'
}
]
})
When you've added a new block, it will appear under your theme name in the block menu when editing a Gutenberg post.
content
is an array that contains 1 or more objects with a name and a type:
content: [
{ name: 'firstWord', type: 'text' },
{ name: 'middleWord', type: 'text' },
{ name: 'lastWord', type: 'text' }
]
You can also include JSX in your content:
content: [
<h2>Enter Your Headline Here:</h2>,
{ name: 'headline', type: 'text' },
<h2>Enter Your Content Here:</h2>,
{ name: 'body', type: 'text' }
]
You can use any of the following as the type
parameter in block content:
text
image
To add a new content type:
blocks/src/prebuilt
.attributes
, edit
, and save
from this file.
blocks/src/prebuilt/text.js
as a model for each property, all of which are required to save and render content correctly.blocks/src/prebuilt/index.js
. Export this value with the name of the type as the key.
text.js
is imported to the index file, then exported with the key text
. Blocks wanting to use this type can do so by setting their type
to 'text'
.fh-components comes with a wp-content
component designed for rendering WordPress content, but we recommend using the built-in gutenberg-content
component for Gutenberg pages.
It accepts all the same arguments as wp-content
, but comes with extra replace
values to handle images in content.
If you don't want to support custom Gutenberg blocks and want to simplify your theme, you can:
Delete the blocks/
directory
Delete functions/blocks.php
Remove this line from functions.php
:
include_once get_template_directory() . '/functions/blocks.php';
Remove this line from package.json
's files
array:
"blocks/**/*.*",
Vuehaus comes with fh-deploy to make deploying your site as easy as possible.
Run npm run deploy
to generate a config file based on a few user inputs. The queue of files to upload is in the package.json files
property. npm run deploy
automatically uploads whatever you have in your directory at the time to your server, so we recommend running npm run build
first - a full deploy command might look like this:
npm run build && npm run deploy
Important note from fh-deploy readme:
Running fh-deploy will automatically overwrite any files of the same name on your server WITHOUT prompting. Assume your remote files are going to be overwritten as soon as you run fh-deploy and make sure you keep up-to-date backups!
Thanks for your interest in working on Vuehaus! You can start any way you'd like, but here's how the Funkhaus team does it:
Install Local by Flywheel to quickly spin up new local WordPress sites.
Create a new WordPress site in Local. We'll call ours "Vuehaus" as an example.
Clone this repo into the wp-content/themes
folder on the new site. On OSX, that'd look like this:
cd ~/Local\ Sites/vuehaus/app/public/wp-content/themes
git clone https://github.com/funkhaus/vuehaus
Activate the Vuehaus theme in the WordPress backend.
Open the repo in your editor of choice and run (optionally using LiveReload):
npm install
# watch development build, or...
npm run dev
# ...use livereload for a more seamless experience
livereload . & npm run dev
When you're ready to submit, npm run build
to make sure the build compiles correctly, then submit your pull request!
fh-components
If you're developing for fh-components as well, we recommend using npm link
:
# Clone the fh-components repo in a shared location
cd ~/Desktop
git clone https://github.com/funkhaus/fh-components
# Make this new fh-components clone the definitive version
cd fh-components
npm link
# Head to the Vuehaus theme installation
cd ~/Local\ Sites/vuehaus/app/public/wp-content/themes/vuehaus
# Uninstall the local fh-components instance and instead use the cloned version above
npm uninstall fh-components
npm link fh-components
From here, running npm run dev
on both fh-components
and the Vuehaus theme will reflect changes in fh-components
. (You can edit the src/views/Default.vue
template to test out new components.)
When you're done making changes and have published the new fh-components
:
cd ~/Local\ Sites/vuehaus/app/public/wp-content/themes/vuehaus
# unlink the local version
npm uninstall fh-components
# make sure fh-components is installed in package.json
npm install fh-components
Not Vuehaus-specific reading material, but rather good practices and articles.
Vuehaus