Flynt is a lightning-fast WordPress Starter Theme for component-based development with ACF Pro.
- WordPress >= 6.1
- Node = 18
- Composer >= 2.4
- Advanced Custom Fields Pro >= 6.0
- Clone this repo to
<your-project>/wp-content/themes
. - Change the domain variable in
flynt/vite.config.js
to match your domain:
const wordpressHost = 'http://your-project.test'
- Navigate to the theme folder and run the following command in your terminal:
# wp-content/themes/flynt
composer install
npm install
npm run build
- Open the WordPress backend and activate the Flynt theme.
To start developing run the following command:
# wp-content/themes/flynt
npm start
All files in assets
and Components
will now be watched for changes and served. Happy coding!
Flynt comes with a ready to use Base Style built according to our best practices for building simple, maintainable components. Go to domain/BaseStyle
to see it in action.
The ./assets
folder contains all global JavaScript, SCSS, images, and font files for the theme. Files inside this folder are watched for changes and compile to ./dist
.
The main.scss
file is compiled to ./dist/assets/main.css
which is enqueued in the front-end.
The admin.scss
file is compiled to ./dist/assets/admin.css
which is enqueued in the administrator back-end of WordPress, so styles added to this file will take effect only in the back-end.
The ./lib
folder includes helper functions and basic setup logic. You will most likely not need to modify any files inside ./lib
. All files in the ./lib
folder are autoloaded via PSR-4.
The ./inc
folder is a more organised version of WordPress' functions.php
and contains all custom theme logic. All files in the ./inc
folder are automatically required.
For organisation, ./inc
has three subfolders. We recommend using these three folders to keep the theme well-structured:
customPostTypes
Use this folder to register custom WordPress post types.customTaxonomies
Use this folder to register custom WordPress taxonomies.fieldGroups
Use this folder to register Advanced Custom Fields field groups. (See Field Groups for more information.)
After the files from ./lib
and ./inc
are loaded, all components from the ./Components
folder are loaded.
Flynt uses Timber to structure its page templates and Twig for rendering them. Timber's documentation is extensive and up to date, so be sure to get familiar with it.
As part of the Twig Extension the theme uses a Twig function in to render components into templates:
renderComponent(componentName, data)
renders a single component. For example, in theindex.twig
template.
Besides the main document structure (in ./templates/_document.twig
), everything else is a component.
A component is a self-contained building-block. Each component contains its own layout, its ACF fields, PHP logic, scripts, and styles.
ExampleComponent/
├── _style.scss
├── functions.php
├── index.twig
├── README.md
├── screenshot.png
├── script.js
The functions.php
file for every component in the ./Components
folder is executed during the WordPress action after_setup_theme
. This is run from the ./functions.php
file of the theme.
To render components into a template, see Page Templates.
Web components provide a standard component model for encapsulation and interoperability HTML elements. Most components are based on an autonomous custom element called flynt-component
.
To define the name of a specific component use the name
attribute, which should match the component’s folder name, to be ensure that its JavaScript is loaded as specified (see JavaScript modules for more details).
For example:
<flynt-component name="BlockWysiwyg" …></flynt-component>
Using a module based approach, allows to breaks JavaScript into separate files and keep them encapsuled inside Components itself.
Different loading strategies can be defined for each component independently when using the custom element flynt-component
:
load:on="idle"
Initialises after full page load, when the browser enters idle state.
Usage example: Elements that don’t need to be interactive immediately.load:on="visible"
Initialises after the element get visible in the viewport.
Usage example: Elements that go “below the fold” or if you want to load it when the user sees it.load:on="load"
(default)
Initialises immediately when the page loads.
Usage example: Elements that need to be interactive as soon as possible.load:on:media="(min-width: 1024px)"
Initialises when the specified media query matches.
Usage example: Elements which may only be visible on certain screen sizes.
Example:
<flynt-component name="BlockWysiwyg" load:on="visible"></flynt-component>
If it makes logical sense, loading strategies can be combined:
<flynt-component name="NavigationMain" load:on="idle" load:on:media="(min-width: 1024px)">
With nested components the loading strategy is waiting for parents. If you have a component with load:on="idle"
nested inside a component with load:on="visible"
, the child component will only be loaded on visible of the parent component.
Defining Advanced Custom Fields (ACF) can be done in functions.php
for each component. As a best practice, we recommend defining your fields inside a function named getACFLayout()
which you can then call in a field group.
For example:
namespace Flynt\Components\BlockWysiwyg;
function getACFLayout()
{
return [
'name' => 'blockWysiwyg',
'label' => __('Block: Wysiwyg', 'flynt'),
'sub_fields' => [
[
'label' => __('Content', 'flynt'),
'name' => 'contentHtml',
'type' => 'wysiwyg',
'delay' => 0,
'media_upload' => 0,
'required' => 1,
],
]
];
}
Field groups are needed to show registered fields in the WordPress back-end. All field groups are created in the ./inc/fieldGroups
folder. Two field groups exist by default: pageComponents.php
and postComponents.php
.
We call the function getACFLayout()
defined in the functions.php
file of each component to load fields into a field group.
For example:
use ACFComposer\ACFComposer;
use Flynt\Components;
add_action('Flynt/afterRegisterComponents', function () {
ACFComposer::registerFieldGroup([
'name' => 'pageComponents',
'title' => 'Page Components',
'style' => 'seamless',
'fields' => [
[
'name' => 'pageComponents',
'label' => __('Page Components', 'flynt'),
'type' => 'flexible_content',
'button_label' => __('Add Component', 'flynt'),
'layouts' => [
Components\BlockWysiwyg\getACFLayout(),
]
]
],
'location' => [
[
[
'param' => 'post_type',
'operator' => '==',
'value' => 'page'
],
[
'param' => 'page_type',
'operator' => '!=',
'value' => 'posts_page'
]
]
]
]);
});
Here we use the ACF Field Group Composer plugin, which provides the advantage that all fields automatically get a unique key.
Flynt includes several utility functions for creating Advanced Custom Fields options pages. Briefly, these are:
Flynt\Utils\Options::addTranslatable
Adds fields into a new group inside the Translatable Options options page. When used with the WPML plugin, these fields will be returned in the current language.Flynt\Utils\Options::addGlobal
Adds fields into a new group inside the Global Options options page. When used with WPML, these fields will always be returned from the primary language. In this way these fields are global and cannot be translated.Flynt\Utils\Options::getTranslatable
Retrieve a translatable option.Flynt\Utils\Options::getGlobal
Retrieve a global option.
Timber provides a resize
filter to resize images on first page load. Resizing many images at the same time can result in a server timeout. That's why Flynt provides a resizeDynamic
filter, that resizes images asynchronously upon first request of the image itself. Resized images are stored in uploads/resized
. To regenerate all image sizes and file versions, delete the folder.
To enable Dynamic Resize, go to Global Options -> Timber Dynamic Resize.
Returns the reading time of a string in minutes.
{{ 'This is a string'|readingTime }}
Example from Components/GridPostsArchive/index.twig
Renders a component. See Page Templates.
{% for component in post.meta('pageComponents') %}
{{ renderComponent(component) }}
{% endfor %}
Example from templates/page.twig
Useful in combination with lazysizes for lazy loading. Returns a "data:image/svg+xml;base64" placeholder image.
{{ placeholderImage(768, (768 / image.aspect)|round, 'rgba(125, 125, 125, 0.1)') }}
Example from Components/BlockImage/index.twig
Resizes an image dynamically. See Timber Dynamic Resize.
{{ post.thumbnail.src|resizeDynamic(1920, (1920 / 3 * 2)|round, 'center') }}
Example from Components/BlockImage/index.twig
In some setups images may not show up, returning a 404 by the server.
The most common reason for this is that you are using nginx and your server is not set up in the the recommended standard. You can see that this is the case, if an image url return a 404 from nginx, not from WordPress itself.
In this case, please add something like
location ~ "^(.*)/wp-content/uploads/(.*)$" {
try_files $uri $uri/ /index.php$is_args$args;
}
to your site config.
Other issues might come from Flynt not being able to determine the relative url of your uploads folder. If you have a non-standard WordPress folder structure, or if you use a plugin that manipulates home_url
(for example, WPML) this can cause problems when using resizeDynamic
.
In this care try to set the relative upload path manually and refresh the permalink settings in the back-end:
add_filter('Flynt/TimberDynamicResize/relativeUploadDir', function () {
return 'app/uploads'; // Example for Bedrock installs.
});
If you want to use https in development, please define the following variables inside a .env
file:
VITE_DEV_SERVER_HOST=https://your-project.test
VITE_DEV_SERVER_KEY=<path-to-ssl-certificate-key>/your-project.test_key.pem
VITE_DEV_SERVER_CERT=<path-to-ssl-certificate-cert>/your-project.test_cert.pem
This project is maintained by Bleech.
The main people in charge of this repo are:
To contribute, please use GitHub issues. Pull requests are accepted. Please also take a moment to read the Contributing Guidelines and Code of Conduct.
If editing the README, please conform to the standard-readme specification.
MIT © Bleech