Superior WordPress starter theme with modern build tools by Aucor. 200+ hours of development over 2,5 years to make the greatest starting point for WordPress site.
Demo: starter.aucor.fi
Required plugin: Aucor Core
For who: Developers building superb WordPress sites
A few buzz-words: Gutenberg, Gulp, Yarn, SVG, SASS, Browsersync, a11y, l18n, Polylang, Schema.org, Lazyload, BEM, Babel, Responsive images
- Directory structure
- Setup
- Components
- Styles
- Scripts
- SVG and Images
- Includes
- Gutenberg and Classic Editor
- Menus
- Editorconfig
- Site specific plugin
Directory structure was once based a mixture between _underscores(template structure) and Sage (Gulp and assets) It has evolved from there.
/assets/
includes all JS, SASS, images, SVG and fonts
/blocks/
custom Gutenberg blocks
/dist/
has processed, combined and optimized assets ready to be included to theme
/inc/
has all php files that are not part of template structure
Do these theme installation steps before modifying anything.
- Download this repository
- Extract into /wp-content/themes/ and rename for project as
sitename
- Run setup wizard in theme root with bash
sh setup.sh
- Site name (Default: "Aucor Starter")
- Unique id for your theme. Use only a-z and _. The shorter the better. Recommended length is 3-4 characters. (Default: aucor_starter)
- Local development url is used by Browsersync and can be changed in
/assets/manifest.js
(Default: https://aucor-starter.local) - Author name is shown in default style.css (Default: Aucor Oy)
- Author URL is shown in default style.css (Default: https://www.aucor.fi)
Some of the functionality of Aucor Starter require plugin Aucor Core. The plugin is centrally updated so that sites using starter will be easier to maintain and there will be less duplicate code from project to project. Aucor Starter won't have fatal errors without it, but for example localization won't work without it.
Aucor Core is open source so you can take parts of it and add them to your theme or create your own plugin. You shouldn't make modifications to Aucor Core directly as your changes will be overridden with future updates.
Download Aucor Core from WordPress.org or Github and activate.
Protip: If you are using composer: composer require wpackagist-plugin/aucor-core
or WP-CLI: wp plugin install aucor-core
.
Every developer does this before first time working with the project.
- Open terminal and navigate to
/wp-content/themes/sitename
- Run
yarn install
(fetches all node packages for build tools) (no yarn? download: https://yarnpkg.com/en/docs/install) - Go to "Start working"
Protip: If you don't have Gulp installed locally run npm install --global gulp-cli
.
Do this everythime you start to work with the theme.
- Open terminal and navigate to
/wp-content/themes/sitename
- Run
gulp watch
to activate build process in background. You'll get development proxy at http://localhost:3000 where changes to code will be updated automatically to browser (no need to reload). - To quit press
ctrl
+c
Protip: You can also run just gulp
to build all the resources or just some resources with gulp styles
or gulp scripts
.
Protip: Want to start the watch task but not open the Browsersync? Start watch with quiet mode qulp watch -q
.
Components are independent components that can be used in many contexts (forms, menu, teaser etc).
Styles of components should be in /assets/styles/elements
and named as _component-name.scss
and will be compiled to dist/styles/main.css
.
But why? WordPress doesn't offer by default a way to make partials where you can pass arguments. This becomes a problem when same partials are used with various contexts and with multiple variations. You can of course make functions but it easily leads you having some partials as functions and others as template-parts. This component structure makes it possible to have partials with and without arguments in a way that is easy to use from project to project.
Abstract Class Component that keeps in the structure and required functionality for each component. Every component should inherit this class.
There are two basic ways to use component:
Aucor_Teaser::render();
Aucor_Teaser::get();
The render function prints out the HTML markup of the component and get will return it. You can pass arguments in an array like so:
Aucor_Teaser::render(['id' => 123]);
Aucor_Teaser::render([
'id' => 123
'hide_image' => true,
]);
All components can be interacted with the same way. It is up to the component to validate the given args and pick what to do with what argument.
Create components-name.php
to inc/components/
<?php
/**
* Component: Componets name
*
* @example
* Aucor_Components_Name::render([
* 'title' => 'Title',
* 'description' => 'Descriptive text'
* 'image' => 123
* ]);
*
* @package aucor_starter
*/
class Aucor_Components_Name extends Aucor_Component {
public static function frontend($data) {
?>
<div <?php parent::render_attributes($data['attr']); ?>>
<?php if (!empty($data['image'])) : ?>
<div class="components-name__image">
Aucor_Image::render([
'id' => $data['image'],
'size' => 'large',
]);
</div>
<?php endif; ?>
<h3 class="components-name__title">
<?php echo $data['title'] ?>
</h3>
<p class="components-name__description">
<?php echo $data['description'] ?>
</p>
</div>
<?php
}
public static function backend($args = []) {
$default = [
// required
'title' => '',
'description' => '',
// optional
'attr' => [],
'image' => '',
];
// overrides defaults with given args
$args = wp_parse_args($args, $default);
// in example you can make some data validation
if (empty($args['title']) || empty($args['description'])) {
return parent::error('Missing title or/and description');
}
// or set attributes like classes
$args['attr']['class'][] = 'components-name';
// or make conditioning
if (!empty($args['image'])) {
$args['attr']['class'][] = 'components-name--has-image';
}
// $args should be so pre-chewed that frontend doesn't have to think anything
return $args;
}
}
Require it in functions.php.
/**
* Components
*/
require_once 'components/components-name.php';
Create _components-name.scss
to /assets/styles/components/
.
After creating _components-name.scss
you can import it to main.scss so it compiles with other .scss files. Make sure gulp watch
is running or run it manually with gulp styles
.
/* Components
----------------------------------------------- */
@import "components/components-name";
If your component will need .js create components-name.js to /assets/scripts/components/
.
After creating components-name.js
add it to /assets/manifest.js
under "project specific js" or anywhere else. Make sure gulp watch
is running or run it manually with gulp scripts
.
// project specific js
"scripts/lib/components-name.js",
Styles are written in SASS in /assets/styles
. There's five separate stylesheets that are compiled automatically to dist/styles
.
main.scss
front-end styles of websiteadmin.scss
back-end styles for admin viewseditor-classic.scss
back-end styles for old TinyMCE based editoreditor-gutenberg.scss
back-end styles for new Gutenberg editor
/base/
universal styles and utilities_variables.scss
colors, fonts, breakpoints_mixins.scss
all SASS mixins_normalize.scss
set unifiend base styles to html elements for all browsers_typography.scss
base styles for typography elements (h1, h2, h3, p, ul, ol, blockquote etc.)_media.scss
base styles for media elements (img, iframe, svg etc.)_forms.scss
base styles for forms_print.scss
styles for printing out the page
/blocks/
Gutenberg block styles for front-end and back-end_core-
supported core blocks_settings-
utilities for blocks (alignment, width, color)
/elements/
small components that can be used in many contexts (forms, menu, teaser etc)/layout/
layouts and page templates/vendor/
styles for external libraries or WP plugins@node_modules
vendor packagesbreakpoint-sass
helper mixins for breakpoints
- When you begin working, start the gulp with
gulp watch
- You get Browsersync URL at
https://localhost:3000
(check yours in console). Here the styles will automatically reload when you save your changes. - If you make a syntax error, a console will send you a notification and tell where the error is. If Gulp stops running, start it again with
gulp watch
. - The Gulp updates
/assets/last-edited.json
timestamps for styles and WP assets use it. This means that cache busting is done automatically. - There's Autoprefixer that adds prefixes automatically. (If you have to support very old browsers, set browsers in gulpfile.js)
- In browser developer tools shows what styles is located in which SASS partial file (SASS Maps)
- Make a new file like
/assets/styles/elements/_card.scss
- Go edit
main.scss
- Import the new file with
@import "elements/card";
Protip: If those styles are needed in Gutenberg editor as well, include the file in editor-gutenberg.scss
as well.
Theme uses BEM methodology to organize class names. Quick example:
.teaser <-- normal element (Block)
.teaser__title <-- sub-element (Element)
.teaser--big <-- variant (Modifier)
BEM in HTML:
<div class="teaser">
<h2 class="teaser__title">Lorem ipsum</h2>
</div>
BEM in SASS:
.teaser {
&__title {
font-size: 1.25rem;
}
}
Protip: Use your own judgement on how deep you should go. There is no right or wrong way. For example .entry__footer__categories__item__label
might be better as just .categories__label
.
- Setup responsive font sizes by setting fonts in percentages in
html
and change html font size with media queries. All elements userem
for font sizes so all the font size changes happen in html. - Don't hesitate to create variables if you have repeating values. Put all variable definitions in
/base/variables/
or at the beginning of the file. - Build mobile-first: more
min-width
, lessmax-width
.
By default, you get external SVG polyfill svgxuse, jQuery-free fitvids and our framework for navigation. There is a11y-dialog for accessible overlay menu. Also we synchronize image markup from Classic Editor to Gutenberg in front-end to make styling easier (not critical or some cases even needed).
Protip: If you are using jQuery, take into account that Aucor Core moves jQuery to the bottom of the document as a speed optimization. If it causes a problem for you, add a filter add_filter('aucor_core_speed_move_jquery', '__return_false');
.
The script have very simple structure.
/lib/
directory for small componentsmenu-primary.js
primary menu functionalitymarkup-enhancements.js
sync old image markup to Gutenberg style, responsive tablesmobile-menu.js
logic for mobile menu
main.js
main js file that is run in footercritical.js
scripts that should be run in headeditor-gutenberg.js
modifies gutenberg editor
- When you begin working, start the gulp with
gulp watch
- You get Browsersync URL at
https://localhost:3000
(check yours in console). Here the styles will automatically reload when you save your changes. - If you make a syntax error, a console will send you a notification and tell where the error is. If Gulp stops running, start it again with
gulp watch
. - The Gulp updates
/assets/last-edited.json
timestamps for styles and WP assets use it. This means that cache busting is done automatically.
Put file in /assets/scripts/lib/my_script.js
. Add script to main.js (or some other file) in /assets/manifest.js
:
"main.js": [
"scripts/main.js"
],
Combine scripts in one file:
"main.js": [
"scripts/lib/menu-primary.js",
"scripts/main.js"
],
First, go to npmjs.com and find if your library is avaialble. Add your library in package.json
devDependencies. Run yarn upgrade
.
Include the script in /assets/manifest.js
.
Add project libraries in dependencies
and Gulp libraries in devDependencies
. Both will go to same node_modules
but it's much easier to later figure out what goes where.
"main.js": [
"../node_modules/fitvids/dist/fitvids.js",
"scripts/lib/menu-primary.js",
"scripts/main.js"
],
Protip: Gulp uses Babel to compile ES6 and React syntax to be compatible to older browsers. In some cases this may mess up older scripts from node_modules. If you have problems with the script, add it to babel exclusions at gulpfile.js
.
We use Babel to compile the JS. You can use ES6 or React syntax with theme's or Gutenberg's code and it will be compiled to be compatible with older browsers.
Gulp will automatically minify images and combine SVG images in one sprite.
Put all icons to /assets/sprite/
and Gulp will combine and minify them into /dist/sprite/sprite.svg
.
In PHP you can get these images with (more exmples in Template tags):
<?php Aucor_SVG::render([
'name' => 'facebook'
]); ?>
Theme includes one big SVG sprite /assets/images/icons.svg
that has by default a caret (dropdown arrow) and a few social icons. Add your own svg icons in /assets/sprite/
and Gulp will add them to this sprite with id from filename.
Example: Print out SVG /assets/sprite/facebook.svg
<?php Aucor_SVG::render([
'name' => 'facebook'
]); ?>
You can also place more complex (multi-colored) SVGs in /assets/images/
and Gulp will compress them. They can be found in /dist/images/
Put your static images in /assets/images/
and Gulp will compress them a bit. Refer images in /dist/images/
.
Image sizes are defined in /inc/_conf/register-images.php
. Tips for creating image sizes:
- Base images on commonly used aspect ratios (16:9, 1:1)
- Make "medium" half of the text columns and "large" full text column
- Add additional sizes for responsive images (for example teaser_lg, teaser_md, teaser_sm)
<?php Aucor_Image::render([
'id' => 123,
'size' => 'large',
]); ?>
Theme has its own function for getting image markup that gives the developer control of which responsive sizes should be used and include lazy loading.
After you have setup WordPress image sizes go to /inc/_conf/register-image-sizes.php
and setup your "human-sizes" (the sizes you will use as arguments). These human-sizes hold info of the primary image size, supporting sizes and what size the image is displayed.
switch ($human_size) {
case 'hero':
return array(
'primary' => 'hero_md',
'supporting' => ['full', 'hero_xl', 'hero_md', 'hero_sm'],
'sizes' => '100vw'
);
case 'thumbnail':
return array(
'primary' => 'thumbnail',
'supporting' => ['full', 'thumbnail'],
'sizes' => '250px'
);
default:
aucor_starter_debug('Image size error - Missing human readable size {' . $human_size . '}', array('aucor_starter_get_image'));
}
Notice that many "human-sizes" can use same WordPress image sizes. This is useful for example when same image might be displayed different size on different devices so you can pass different "sizes" attributes for browser. Read more about sizes attribute.
Protip: If you use CSS property object-fit
it needs special handling to work in IE11 and older. Theme has object-fit-polyfill installed and all you need to do is add special data attribute for img tag like
Aucor_Image::render([
'id' => 123,
'size' => 'medium',
'attr' => ['data-object-fit' => 'cover']
])
By default the image function has lazy loading on. Lazy loading uses lazysizes library. When emedding image there's three possibilities:
- Lazyload from transparent to visible (default):
Aucor_Image::render([
'id' => 123,
'size' => 'medium'
])
- Lazyload from blurry pre-load image to visible:
Aucor_Image::render([
'id' => 123,
'size' => 'medium',
'attr' => [
'lazyload' => 'animated'
]
])
- No lazyload:
Aucor_Image::render([
'id' => 123,
'size' => 'medium',
'attr' => [
'lazyload' => 'false'
]
])
Lazy loading is SEO friendly and function automatically displays <noscript>
versions for users without JS.
Add all favicon files to /assets/favicon/
where they will be moved (and images optimized) to /dist/favicon/
. Use for example Real Favicon Generator to make favicon. Add favicon embeds to function aucor_starter_favicons
in /inc/_conf/register-assets.php
.
Includes is place for all PHP files that are not part of templates. So all filters, actions and functions.
All the functions, hooks and setup should be on their own filer organized at /inc/. The names of files should describe what the file does as following:
register-
configures new settings and assetssetup-
configures existing settings and assetsfunction-
adds functions to be used in templates
The /functions.php
is the place to require all PHP files that are not part of the current template. This file should only ever have requires and nothing else.
The /inc/_conf/
directory has some essential settings for theme that you basically want to review in every project and probably will return many times during development.
register-assets.php
has all CSS and JS includes to templates as well as any arbitary code added to header or footer.register-blocks.php
defines which Gutenberg blocks are allowed. Gutenberg will have UI in future to select these so this will be removed in some future version. Notice that all blocks that are not defined here are not allowed.register-image-sizes.php
register all image sizes and responsive image sizes.register-localization.php
add all translatable strings for Polylang.register-menus.php
define all menu positions.
Directory /inc/helpers/
.
Function: aucor_starter_get_posted_on()
Get published date.
Display categories and tags of single post.
Function: aucor_starter_get_hardcoded_id($key)
Save all harcoded ids in one place in refer them through this function. Example:
/**
* Get hardcoded ID by key
*
* @param string $key a name of hardcoded id
*
* @return int harcoded id
*/
function aucor_starter_get_hardcoded_id($key = '') {
switch ($key) {
case 'some-id':
return 123;
default:
return 0;
}
}
Used in template:
aucor_starter_get_hardcoded_id('some-id')
Protip: Avoid hardcoding ids if possible. If you really need to do it, centralize them into this function.
Function: aucor_starter_last_edited($asset)
Get last edited timestamp from asset files. Timestamps are saved in /assets/last-edited.json
.
Include fallback function in case critical plugin is not active.
Directory /inc/components/
has various components for menus and navigation things.
Function:
Aucor_Share_Buttons::render([
'section_title' => ask__('Social share: Title')
])
Displays share buttons (Facebook, Twitter, LinkedIn, WhatsApp) with link to their sharer.
<?php Aucor_Share_Buttons::render([
'section_title' => ask__('Social share: Title')
]); ?>
Function: aucor_starter_numeric_posts_nav($custom_query = null, $custom_paged_var = null)
Displays numeric navigation instead of normal "next page, last page" navigation. Can be used with a custom query. You can even change the pagination variable if you need to.
Main query:
if(have_posts())
while (have_posts()) : the_post();
...
endwhile;
Aucor_Posts_Nav_Numeric::render();
endif;
Custom query:
$paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
$args = array(
'post_type' => 'post',
'posts_per_page' => 10,
'paged' => $paged,
);
$loop = new WP_Query($args);
if($loop->have_posts())
while ($loop->have_posts()) : $loop->the_post();
...
endwhile;
Aucor_Posts_Nav_Numeric::render(['wp_query' => $loop]);
endif;
Custom query with your own pagination variable "current_page"
$paged = (isset($_GET['current_page']) && !empty($_GET['current_page'])) ? absint($_GET['current_page']) : 1;
$args = array(
'post_type' => 'post',
'posts_per_page' => 10,
'paged' => $paged,
);
$loop = new WP_Query($args);
if ($loop->have_posts())
while ($loop->have_posts() ) : $loop->the_post();
...
endwhile;
aucor_starter_numeric_posts_nav([
'wp_query' => $loop,
'paged_var' => 'current_page',
]);
endif;
Function:
Aucor_Menu_Sub_Pages::render()
Displays sub-pages for current page in list. If you need to pretend that single post is somewhere in the hierarchy, use global variable pretend_id to display current view to be in certain place in hierarchy
Usage:
Aucor_Menu_Sub_Pages::render([
'id' => $id
]);
Pretend to be someone else (active_id is the id of someone who to pretend):
// single-post.php etc.
Aucor_Menu_Sub_Pages::render([
'id' => $id,
'active_id' => 321,
]);
Function:
Aucor_Menu_Toggle::render([
'id' => $id,
'attr' => $args
])
Display hamburger button for menu.
By default we add small SVG carets for menu items that have children in primary menu.
We add social icons SVG to menu items in social menu. There's a few most used supported already (like Twitter, Facebook, Youtube, LinkedIn) but you can add your very easily there.
You can add icons to primary menu by adding class icon-{name-of-the-icon}
like icon-facebook
.
In depth about file: /inc/_conf/register-localization.php
(and /inc/helpers/setup-fallbacks.php
)
Do you have a minute to talk about localization? Good.
We don't really like .po files as they are confusing for customers, their build process is weird and are prone to errors, it's hard to change the "original" text later on and they are slowish to load. What we do like is Polylang. If we are running a multi-lingual site, we want to use Polylang's String Translation to translate the strings in theme.
But we don't stop at using just Polylang. It's generally a little pain to have to register all the strings for your theme and it's very easy to forget to add something. We created our own wrapper function to give you error messages for missing string in WP_DEBUG mode.
But what if I'm only making site in one language? Well you can be a lazy developer and hardcode things but is that a way to live a life? You can prepare for multiple languages from the start by using these functions and registering your strings already. These functions (and bunch of Polylang's) will work even if you don't use Polylang.
Start off by registering some strings.
- All your strings are registered at
/inc/_conf/register-localization.php
(static pieces of text in theme) - You give a unique context (key) and the default text (value) for string. Example
"Header: Greeting" => "Hello"
By default you have a few strings there. They are in Finnish by default. You can change them to English by copying and pasting following (we should make this into setup process...)
// titles
'Title: Home' => 'Blog',
'Title: Archives' => 'Archives',
'Title: Search' => 'Search',
'Title: 404' => 'Page not found',
// menu
'Menu: Button label' => 'Menu',
'Menu: Primary Menu' => 'Primary menu',
'Menu: Social Menu' => 'Social media channels',
'Menu: Upper Menu' => 'Additional menu',
'Menu: Open' => 'Open menu',
'Menu: Close' => 'Close menu',
'Menu: Open Sub-menu' => 'Open submenu',
'Menu: Close Sub-menu' => 'Close submenu',
// 404
'404: Page not found description' => 'The page might have been deleted or moved to different location. Use the search below to find what you are looking for.',
// search
'Search: Title' => 'Search: ',
'Search: Nothing found' => 'No search results',
'Search: Nothing found description' => 'No search results found. Try different words.',
'Search: Placeholder' => 'Search...',
'Search: Screen reader label' => 'Search from site',
'Search: Submit' => 'Search',
// accessibility
'Accessibility: Skip to content' => 'Skip to content',
// navigation
'Navigation: Previous' => 'Previous',
'Navigation: Next' => 'Next',
// social
'Social share: Title' => 'Share on social media',
'Social share: Facebook' => 'Share on Facebook',
'Social share: Twitter' => 'Share on Twitter',
'Social share: LinkedIn' => 'Share on LinkedIn',
'Social share: WhatsApp' => 'Share on WhatsApp',
// taxonomies
'Taxonomies: Keywords' => 'Keywords',
'Taxonomies: Categories' => 'Categories',
// colors
'Color: White' => 'White',
'Color: Black' => 'Black',
'Color: Primary' => 'Primary color',
// script localization
'Tobi: Prev' => 'Previous',
'Tobi: Next' => 'Next',
'Tobi: Close' => 'Close',
'Tobi: Loading' => 'Loading',
You can change these default texts (values) right here and they will update on templates. If you have Polylang installed, these strings will appear automatically on String Translations.
There are two ways to get translated string
- By key (Social share: Title)
- By value (Share on social media)
All the default strings are fetched by key. In that way you can go on and replace the default values on /inc/_conf/register-localization.php
without having to search and replace anything anywhere.
Return string by key:
Function: ask__($key, $lang = null)
Example: ask__('Social share: Title')
=> 'Share on social media' (or translated version)
Echo string by key:
Function: ask_e($key, $lang = null)
Example: ask_e('Social share: Title')
=> 'Share on social media' (or translated version)
Protip: If you are unsure of what the final text may really be, it's smart to use strings by key. If it makes sense, you can use values by key for everything. Polylang doesn't have it's own "return string by key" but we got your back.
Return string by value:
Function: asv__($key, $lang = null)
Example: asv__('Share on social media')
=> 'Share on social media' (or translated version)
Echo string by value:
Function: asv_e($key, $lang = null)
Example: asv_e('Share on social media')
=> 'Share on social media' (or translated version)
Protip: This is the "normal" Polylang way of getting your translated strings. The downside is that if the default text you propose will be radically changed in String Translation the code might be hard to read (it will work nevertheless).
Debugging is the greatest benefit of using our string localization functions instead of Polylang defaults. If you have WP_DEBUG
set to true
in wp-config.php
(which you should while developing), you will get error messages if you forget things. So example:
- You add to header.php
ask_e('Header: Greeting')
- You forget to add this string to
/inc/_conf/register-localization.php
- You get error message and PHP error log entry that you tried to use
ask_e('Header: Greeting')
in this file on that line without registering the string
So there's really no excuse to forget to register your strings no more.
Aucor Starter supports both Gutenberg and Classic Editor though Gutenberg is preferred.
Aucor Starter includes default Gutenberg styles on front-end and overrides them with from theme. This makes developing both easier and harder:
- 👍 Makes site more future-proof as Gutenberg will have breaking changes in future where some features will not work properly without correct styles (and default styles will take care of them to some degree).
- 👎 You may have to override some opinionated defaults.
In Gutenberg editor, there are still lots of default styles so there might be a few inconsistensies between front-end and back-end. This will get better in future versions of Gutenberg and Aucor Starter.
Gutenberg block styles are in /assets/styles/blocks/
. Each block should have their own file. Also there should be separation for front-end and back-end styles as you'll need to style both. Both styles are defined in same file that is the most convinient way to define them. It does add a bit of unused code to front-end.
All Gutenberg and Classic Editor content should be inside container with class .wysiwyg
. You should scope the styles for this to focus on right elements.
In Gutenberg world, each block will define its own width as there can be wide blocks and full width blocks. There are variables to keep widths consistent.
Protip: Avoid resetting left and right margins if you are not sure what you are doing. You can easily make an element stick to left side of screen by adding margin: 0
instead of margin-top: 0
.
Protip 2: Use the mixins @include spacing-s(margin-top)
, @include spacing-m(margin-top)
or @include spacing-l(margin-top)
to have responsive and unified margins.
Many blocks support alignment and different widths. Generic styles for these are located in /assets/styles/_settings-*
and will be enough for most cases. If you have similar features that many blocks use, add them to settings to keep your code clean.
Set allowed blocks in /inc/_conf/register-blocks.php
.
Aucor Starter supports these blocks by default:
// common blocks
'core/paragraph'
'core/image'
'core/heading'
'core/gallery'
'core/list'
'core/quote'
'core/file'
// formatting
'core/table'
'core/freeform' // classic editor
// layout
'core/button'
'core/media-text'
'core/columns'
'core/group'
'core/separator'
// widgets
'core/shortcode'
// embeds
'core/embed'
'core-embed/twitter'
'core-embed/youtube'
'core-embed/facebook'
'core-embed/instagram'
'core-embed/soundcloud'
'core-embed/spotify'
'core-embed/flickr'
'core-embed/vimeo'
'core-embed/issuu'
'core-embed/slideshare'
// reusable blocks
'core/block'
Notice: All blocks that are not explicitly allowed are disabled. This means that if you install a plugin that has new blocks, those blocks are not shown before you allow them. Gutenberg will provide an UI for this in future and we will drop this feature then.
Gutenberg has still many improvements and bugfixes on the way. These are some issues at the moment:
- You cannot disable Inline Image block because it comes from Paragraph block #12680
- Many features can't be disabled like paragraph drop caps.
- Colors can't be scoped to specific blocks. If you register colors for Gutenberg, they will become available to every block that supports colors.
Gutenberg development is moving fast and there are a lot of people working hard to improve Gutenberg.
You can still use Classic Editor in some post types or all if you like. If you want to completely disable Gutenberg, you might want to re-organize the styles a bit as some styles are shared in /assets/styles/blocks/
.
By default the starter theme has three menu locations: Primary menu, Upper menu and Social menu.
Theme location: primary
Theme's main navigation in header that is build to handle multiple levels.
Theme location: upper
Additional navigation items that are not as important as in primary menu. By default, it will only show 1st level and dropdown menus are not supported. If you will need them, take a look st primary menu styles and main.js
.
Theme location: social
Optional menu for organization's social media accounts.
How to use:
- Include template part somewhere
<?php get_template_part('partials/menu-social'); ?>
- Create a new menu in WP admin
- Add custom link items with url to account and title like "Facebook"
- Menu item gets SVG icon that is based on url
- Style menu as needed on
/assets/styles/elements/_menu-social.scss
Starter includes rough navigation skeleton that is working out of box for 3 levels (or infinite amount if you put a little bit more CSS into it). Skeleton includes /assets/scripts/components/dropdown-menu.js
and /assets/styles/components/_menu-primary.scss
. This menu works with mouse, touch and tabs. Accessibility is built-in!
Inside main.js
there is the menu init and a few arguments:
component_dropdown_menu({
desktop_min_width: 890,
menu: document.querySelector('.primary-navigation'),
});
Protip: The desktop_min_width
option will disable or enable some ways to interact with the menu. For example the hover stuff is disabled on "mobile mode".
Shows and hides the mobile menu. The mobile menu component is essentially a wrapper for a11y-dialog that makes the mobile menu more accessible by trapping the focus inside the menu when it's opened.
component_mobile_menu({
menu: document.querySelector('.js-mobile-menu'),
site: document.querySelector('.js-page'),
toggles: document.querySelectorAll('.js-menu-toggle')
});
Theme has an .editorconfig
file that sets your code editor settings accordingly. Never used Editorconfig? Download the extension to your editor. The settings will automatically be applied when you edit code when you have the extension.
Our settings:
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
It is recommended to create also site specific plugin to store custom post types, custom taxonomies, shortcodes, ACF fields etc information architecture. There is nothing stopping you from defining them into theme, but we find it better to isolate information architecture outside of theme as those post types and taxonomies will remain when theme gets rebuild.