A component based Wordpress starter theme based on Timber.
Node
Composer
A Local Wordpress Instance (since this is only the theme)
- Make sure you have Node installed.
- If you are using NVM (Node package manager), run
nvm use
to get and/or install the right Node Version for this project. The initial setup was done with Nodev14.17.1 (with npm 6.14.13)
- Then install the PHP dependencies using
composer install
. - Then install the JS dependencies and packages using
npm install
. - Then run
npm run dev
to start the dev server. - To run a build locally or on CI/CD run
npm run build
.
base
– All the PHP classes, actions and hooks that are talking with Wordpress.blocks
– PHP based Gutenberg Blocks that dont't make sense to migrate to React. (Dependency is the plugin Genesis Blocks.)languages
— i18n files for translationssource
— The source folder with all the shared and component based scripts, styles and views.gulp
– Everything related to the frontend buildtool.gutenberg
– React-based Gutenberg blocksscripts
– shared / general purpose scripts like helpers, utilities and so on.styles
– shared / general purpose scripts like helpers, utilities and so on.views
– Twig based components, partials and templates
static
– static assets like svgs, fonts and so on.build
— The generated and minified output/assets after running through Gulp.vendor
– Composer based dependencies like Timber. Since this theme canno't work at all without Timber, it has been installed this way rather than relying on the plugin.
Let's face it. Plain PHP sucks as a template language. So this theme uses Twig as a template language thanks to the help of Timber. The basic concept is, that you still have your well known PHP files but when it comes to rendering Markup, you just pass the data to a Twig file, instead of doing that in PHP. To see examples, check some of the PHP files on root level of this theme and make sure you checkout the Tibmer documentation.
In order to use Emmet in your Twig files create a .vscode
folder in the root directory of this theme. Then create a settings.json
in it and put the following content in that file.
{
"files.associations": {
"*.twig": "twig"
},
"emmet.includeLanguages": {
"twig": "html"
}
}
This setup let's you encapsulate Code into components. So Markup/Twig, Styles and Scripts live in one directory related to that component. As an example check source/components/QuickNews
. There you have:
- QuickNews.twig
- QuickNews.scss
- QuickNews.js
scss
file will be automatically imported by Gulp into the entry file styles/ui.scss
. However, if your component has a js
file as well, you need to manually import it into the entry file in scripts/index.js
.
If your component is getting complex, make sure you split things into subcomponents or, as named here, into partials. Thanks to Twig, you can include and pass data from and to partials.
No one likes big bundle sizes. That's why this setup comes with dynamic imports thanks to Webpack. This will create JS chunks and load it on the fly if your component appears on a certain page. This should be the preferred approach if you have to load a big third party library like Mapbox etc. only on certain pages. Check source/partials/CitySearch/Citysearch.js
to see an example.
This theme uses SASS/SCSS together with the BEM naming convention. All source/assets/styles/*.scss
files will be converted to inside assets/styles/{$name}.css
files.
The base/Package
Class Assets
enqueues them directly.
- admin-editor.min.css is loaded in the backend. This file is generated from source/styles/admin-editor.scss.
- admin.min.css is loaded in the backend. This file is generated from source/styles/admin.scss.
- ui.min.css is loaded in the backend. This file is generated from source/styles/ui.scss.
Gutenberg blocks can be written in two ways. Either you do it "the React way" as shown in the examples in /source/gutenberg/blocks/
or you do it "the PHP way" using a plugin like Genesis Blocks. Whenever possible, i'd recommend to do it with React because of it's component based architecture and reactivity features. However, there are cases where the PHP way is just more straight forward and does the job.
The Theme is provided with built-in SCSS support for Gutenberg blocks. There is a specific Gutenberg
Package (base/Package/Gutenberg.php
) for some functionality.
The SCSS variable $context
is defined in admin-editor.scss (value edit
) and ui.scss (value view
) appropriate to each context, so that the mixins context-view
and context-edit
can generate the CSS appropriately for the current context. For example:
.wp-block-image {
vertical-align: middle;
}
@include context-view() {
.wp-block-image {
margin-top: 1rem;
margin-bottom: 1rem;
}
}
Note that the call to @include context-view()
may not be defined within the .wp-block-image
definition, but must be included as a separate section. This is due to the CSS namespacing which occurs in the gulp-editor-styles
process.
Gulp uses the gulp-editor-styles Node Module to automatically parse and wrap the CSS in an appropriate scope (.editor-styles-wrapper
) for the Gutenberg editor.
This theme uses ES6 modules bundled or as dynamic imports using Webpack. The base/Package
Class Assets
enqueues the entry files. For example: all source/scripts/ui/*.js
files will be bundled to build/scripts/ui.js
. There will also be a minified version build/scripts/ui.min.js
.
There is a built in Font loading process using base64 encoded woff/woff2 fonts, which are stored inside the local storage of the browser. This avoids the FOUT problem.
Assuming that the fonts you're using are licensed for use in this way, convert the fonts to base64-encoded WOFF and WOFF2 CSS files using Transfonter and then add the code to the files in the assets/fonts folder. Generate the WOFF and WOFF2 versions separately, as you'll need individual CSS files.
These files are then loaded by JavaScript and stored in the browser's LocalStorage. The script checks the asset version number to respect new versions.
As for now, this Theme uses Browsersnc to refresh your browser on every change you make to php
or twig
files. For js
and scss
there is even some injection without full reloads.