├── .eslintrc ├── .gitignore ├── .vscode └── tasks.json ├── 404.php ├── README.md ├── acf-json └── .gitkeep ├── acf └── .gitkeep ├── archive.php ├── assets ├── css │ └── app.css ├── fonts │ └── .gitkeep ├── images │ └── cookie.png └── js │ └── app.js ├── author.php ├── footer.php ├── functions.php ├── header.php ├── index.php ├── jsconfig.json ├── lib ├── autoload.php └── src │ ├── Config │ ├── ACFSettings.php │ ├── AdminSettings.php │ ├── CustomPostTypes.php │ ├── CustomTaxonomies.php │ ├── ImageSettings.php │ ├── MenuSettings.php │ ├── PostSettings.php │ ├── ThemeSupport.php │ └── TwigSettings.php │ ├── Core │ ├── Assets.php │ ├── Menu.php │ ├── MenuItem.php │ └── Site.php │ └── PostTypes │ └── CustomPost.php ├── package.json ├── page.php ├── screenshot.png ├── search.php ├── sidebar.php ├── single.php ├── source ├── scripts │ ├── app.js │ ├── components │ │ └── MyComponent.vue │ └── utils │ │ └── polyfills.js └── styles │ ├── app.scss │ ├── common │ └── _base.scss │ ├── components │ └── .gitkeep │ ├── layouts │ └── .gitkeep │ └── utility │ ├── _mixins.scss │ ├── _normalize.scss │ └── _variables.scss ├── static └── no-timber.html ├── style.css ├── views ├── 404.twig ├── archive.twig ├── author.twig ├── base.twig ├── comment.twig ├── footer.twig ├── front-page.twig ├── html-header.twig ├── index.twig ├── page-slug.twig ├── page.twig ├── partials │ ├── nav.twig │ └── pagination.twig ├── search.twig ├── sidebar.twig ├── single-password.twig ├── single.twig └── tease.twig └── webpack.mix.js /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "eslint:recommended", 4 | "plugin:vue/recommended" 5 | ], 6 | "rules": { 7 | "no-console": "off" 8 | } 9 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .sass-cache 3 | node_modules 4 | npm-debug.log -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "watch", 9 | "problemMatcher": [], 10 | "group": { 11 | "kind": "build", 12 | "isDefault": true 13 | } 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /404.php: -------------------------------------------------------------------------------- 1 | ) 84 | 4. Run `npm run watch` to confirm that the source files compile successfully. The site should open up in your browser with a URL of 85 | 86 | Git 87 | 88 | 1. We will initialize a git repo in the wp-content folder, so `cd ../..` and `git init` 89 | 2. `git add *` and `git commit -m "Initial commit"` 90 | 3. If you use Tower: 91 | * Open the repo in Tower with `gittower .` 92 | * Add a remote repo named _origin_ and paste in the link to your remote repo 93 | 4. Make your first push to your remote repo! 94 | 95 | 96 | ## Cloning 97 | 98 | If you are a team member working on a project that someone has already created using the above steps and just need to pull down the project and get started, follow these steps: 99 | 100 | 1. In your command line, go to your project folder (e.g. `~/Sites/`), or wherever you parked Valet above. 101 | 2. Run `wp valet new your-project-name --unsecure` 102 | * Note that you should ideally use the same settings as the rest of the team 103 | * You can leave off the `--unsecure` flag to enabled HTTPS locally 104 | * You can change this option after the project is created using `valet secure your-project-name` / `valet unsecure your-project-name` 105 | * Full list of options available [here](https://github.com/aaemnnosttv/wp-cli-valet-command#wp-valet-new) 106 | 3. Confirm that the site is running by going to 107 | 6. `cd` into the wp-content folder `cd your-project-name/wp-content` and remove everything in this directory. We will clone the project repo into here. 108 | 7. `git clone your.git.repo.url.here . ` 109 | * _Note the trailing "space period space"_ 110 | 8. Open in Tower, if that's your thing: `gittower .` and make sure your remote _origin_ is set up correctly 111 | 9. `wp plugin activate soil` 112 | 10. `wp theme activate your-project-name` 113 | 11. `cd` into the theme folder (e.g. `cd themes/your-project-name`) 114 | 12. Run `npm install` 115 | 13. Run `npm run watch` to confirm that the source files compile successfully. The site should open up in your browser with a URL of 116 | 117 | ## Extras 118 | 119 | 6. Set a static home page in Settings > Reading and choosing "A Static Page". This will automatically act as your home page and will reference the `views/front-page.twig` template. 120 | 121 | -------------------------------------------------------------------------------- /acf-json/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danwill/timber_starter_theme/b78a8562e794fa1e804e842c927e80c97dec6eff/acf-json/.gitkeep -------------------------------------------------------------------------------- /acf/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danwill/timber_starter_theme/b78a8562e794fa1e804e842c927e80c97dec6eff/acf/.gitkeep -------------------------------------------------------------------------------- /archive.php: -------------------------------------------------------------------------------- 1 | query_vars['author'])) { 18 | $author = new Timber\User($wp_query->query_vars['author']); 19 | $context['author'] = $author; 20 | $context['title'] = 'Author Archives: ' . $author->name(); 21 | } 22 | Timber::render(array('author.twig', 'archive.twig'), $context); 23 | -------------------------------------------------------------------------------- /footer.php: -------------------------------------------------------------------------------- 1 |

Timber not activated. Make sure you activate the plugin in ' . esc_url(admin_url('plugins.php')) . '

'; 12 | }); 13 | add_filter('template_include', function ($template) { 14 | return get_stylesheet_directory() . '/static/no-timber.html'; 15 | }); 16 | return; 17 | } 18 | 19 | use Timber\Timber; 20 | use Mechanic\Core\Site; 21 | 22 | // Timber::$cache = true; 23 | Timber::$dirname = [ 24 | 'views', 25 | 'views/partials', 26 | ]; 27 | 28 | /** 29 | * By default, Timber does NOT autoescape values. Want to enable Twig's autoescape? 30 | * No prob! Just set this value to true 31 | */ 32 | Timber::$autoescape = false; 33 | 34 | require_once('lib/autoload.php'); 35 | 36 | new Site(); 37 | -------------------------------------------------------------------------------- /header.php: -------------------------------------------------------------------------------- 1 | '; 51 | print_r($toolbars); 52 | echo '< /pre >'; 53 | die; 54 | */ 55 | 56 | // Add a new toolbar called "Very Simple" 57 | // - this toolbar has only 1 row of buttons 58 | $toolbars['Very Simple'] = array(); 59 | $toolbars['Very Simple'][1] = array('bold', 'italic', 'link'); 60 | 61 | return $toolbars; 62 | } 63 | 64 | public static function addOptionsPage() 65 | { 66 | if (function_exists('acf_add_options_page')) { 67 | acf_add_options_page(array( 68 | 'page_title' => 'Global Settings', 69 | 'menu_title' => 'Global Settings' 70 | )); 71 | } 72 | } 73 | 74 | // Examples of how to modify ACF field values based on certain criteria 75 | public static function configureFields() 76 | { 77 | // Limits relationship field based on value of a custom field 78 | // add_filter('acf/fields/relationship/query/name=videos', function($args) { 79 | // $args['meta_key'] = 'article_type'; 80 | // $args['meta_value'] = 'video'; 81 | // return $args; 82 | // }, 10, 3); 83 | 84 | // add_filter('acf/fields/relationship/query/name=videos', function($args, $field, $post_id) { 85 | // // $post = get_posts($post_id); 86 | // if(self::isTermPost($post_id)) { 87 | // $args['cat'] = $term->id; 88 | // } 89 | 90 | // // error_log($post_id); 91 | // $args['meta_key'] = 'article_type'; 92 | // $args['meta_value'] = 'video'; 93 | // return $args; 94 | // }, 10, 3); 95 | /* 96 | add_filter('acf/load_field/name=category_article', function($field) { 97 | // global $field; 98 | echo '
';
 99 |             print_r($field);
100 |             echo '
'; 101 | return $field; 102 | }); 103 | */ 104 | // Only shows top-level taxonomy terms for specified field names 105 | // https://support.advancedcustomfields.com/forums/topic/taxonomy-field-filter-to-only-show-parents/ 106 | /*add_filter('acf/fields/taxonomy/wp_list_categories', function($args, $field) { 107 | if($field['_name'] == 'person_sector') { 108 | $args['depth'] = 1; 109 | } 110 | return $args; 111 | }, 10, 2);*/ 112 | 113 | // Filter to customize the value shown on Post Object types for custom posts that don't support a title 114 | // @link https://www.advancedcustomfields.com/resources/acf-fields-post_object-result/ 115 | /* 116 | function posttype_post_object_result( $title, $post, $field, $post_id ) { 117 | $title = get_field('first_name', $post->ID) . ' ' . get_field('last_name', $post->ID); 118 | return $title; 119 | } 120 | add_filter('acf/fields/post_object/result/name=acffieldname', 'posttype_post_object_result', 10, 4); 121 | */ } 122 | } 123 | -------------------------------------------------------------------------------- /lib/src/Config/AdminSettings.php: -------------------------------------------------------------------------------- 1 | 39 | 50 | [ 36 | 'name' => 'Custom Post', 37 | 'singular_name' => 'CustomPost' 38 | ], 39 | 'public' => true, 40 | 'menu_icon' => 'dashicons-groups', 41 | 'has_archive' => false, 42 | 'supports' => [ 43 | 'title', 44 | 'revisions' 45 | ], 46 | 'rewrite' => [ 47 | 'slug' => 'team' 48 | ], 49 | 'show_in_nav_menus' => true, 50 | ] 51 | ); 52 | */ } 53 | 54 | // Removes custom taxonomy meta box from custom post type so we can use ACF instead 55 | public static function configMetaBoxes() 56 | { 57 | remove_meta_box('sectiondiv', 'article', 'side'); 58 | } 59 | 60 | /** 61 | * Example showing how to modify the listing view for a custom post type 62 | * 63 | * Replace project with project namespace (optional) and postname with name of custom post type (required) 64 | * 65 | * @link https://codex.wordpress.org/Plugin_API/Action_Reference/manage_$post_type_posts_custom_column 66 | */ 67 | 68 | /* 69 | function project_postname_columns($columns) { 70 | $columns = array( 71 | 'cb' => '', 72 | 'title' => 'Name', 73 | 'logo' => 'Logo', 74 | 'date' => 'Date', 75 | ); 76 | return $columns; 77 | } 78 | 79 | function project_postname_custom_columns($column) 80 | { 81 | global $post; 82 | if($column == 'logo') { 83 | echo wp_get_attachment_image( get_field('logo', $post->ID), array(320,125) ); 84 | } 85 | } 86 | 87 | add_action("manage_postname_posts_custom_column", "project_postname_custom_columns"); 88 | add_filter("manage_postname_posts_columns", "project_postname_columns"); 89 | */ 90 | } 91 | -------------------------------------------------------------------------------- /lib/src/Config/CustomTaxonomies.php: -------------------------------------------------------------------------------- 1 | 'Sectors', 16 | 'singular_name' => 'Sector', 17 | 'menu_name' => 'Sector', 18 | 'all_items' => 'All Sectors', 19 | 'parent_item' => 'Sector Group', 20 | 'parent_item_colon' => 'Sector Group:', 21 | 'new_item_name' => 'New Sector Name', 22 | 'add_new_item' => 'Add New Sector', 23 | 'edit_item' => 'Edit Sector', 24 | 'update_item' => 'Update Sector', 25 | 'view_item' => 'View Item', 26 | ); 27 | $args = array( 28 | 'labels' => $labels, 29 | 'hierarchical' => true, 30 | 'public' => true, 31 | 'show_ui' => true, 32 | 'show_admin_column' => false, 33 | 'show_in_nav_menus' => true, 34 | 'show_tagcloud' => false, 35 | ); 36 | */ 37 | // Can leave the item out of the side nav by leaving this array empty. Can attach via ACF 38 | // register_taxonomy( 'custom_taxonomy', ['custom_post'], $args ); 39 | 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/src/Config/ImageSettings.php: -------------------------------------------------------------------------------- 1 | __('Main Navigation'), 16 | 'footer-nav' => __('Footer Navigation'), 17 | ]); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/src/Config/PostSettings.php: -------------------------------------------------------------------------------- 1 | name = 'Articles'; 18 | $labels->singular_name = 'Article'; 19 | $labels->add_new = 'Add Article'; 20 | $labels->add_new_item = 'Add Article'; 21 | $labels->edit_item = 'Edit Article'; 22 | $labels->new_item = 'New Article'; 23 | $labels->view_item = 'View Article'; 24 | $labels->view_items = 'View Articles'; 25 | $labels->search_items = 'Search Articles'; 26 | $labels->not_found = 'No articles found.'; 27 | $labels->not_found_in_trash = 'No articles found in Trash.'; 28 | $labels->parent_item_colon = 'Parent articles'; // Not for "post" 29 | $labels->archives = 'Articles Archives'; 30 | $labels->attributes = 'Articles Attributes'; 31 | $labels->insert_into_item = 'Insert into articles'; 32 | $labels->uploaded_to_this_item = 'Uploaded to this articles'; 33 | $labels->featured_image = 'Hero Image'; 34 | $labels->set_featured_image = 'Set hero image'; 35 | $labels->remove_featured_image = 'Remove hero image'; 36 | $labels->use_featured_image = 'Use as hero image'; 37 | $labels->filter_items_list = 'Filter article list'; 38 | $labels->items_list_navigation = 'Articles list navigation'; 39 | $labels->items_list = 'Articles list'; 40 | 41 | # Menu 42 | $labels->menu_name = 'Articles'; 43 | $labels->all_items = 'All Articles'; 44 | $labels->name_admin_bar = 'Articles'; 45 | 46 | return $labels; 47 | } 48 | 49 | // Edit columns for post listing page 50 | // @see https://www.smashingmagazine.com/2017/12/customizing-admin-columns-wordpress/ 51 | public static function configureColumns($columns) 52 | { 53 | $columns = [ 54 | 'cb' => $columns['cb'], 55 | 'title' => $columns['title'], 56 | 'categories' => $columns['categories'], 57 | 'tags' => $columns['tags'], 58 | 'date' => $columns['date'], 59 | ]; 60 | return $columns; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib/src/Config/ThemeSupport.php: -------------------------------------------------------------------------------- 1 | tag in the document head, and expect WordPress to 21 | * provide it for us. 22 | */ 23 | // add_theme_support('title-tag'); 24 | 25 | /* 26 | * Enable support for Post Thumbnails on posts and pages. 27 | * 28 | * @link https://developer.wordpress.org/themes/functionality/featured-images-post-thumbnails/ 29 | */ 30 | add_theme_support('post-thumbnails'); 31 | 32 | /* 33 | * Switch default core markup for search form, comment form, and comments 34 | * to output valid HTML5. 35 | */ 36 | add_theme_support( 37 | 'html5', 38 | array( 39 | 'comment-form', 40 | 'comment-list', 41 | 'gallery', 42 | 'caption', 43 | ) 44 | ); 45 | 46 | /* 47 | * Enable support for Post Formats. 48 | * 49 | * See: https://codex.wordpress.org/Post_Formats 50 | */ 51 | add_theme_support( 52 | 'post-formats', 53 | array( 54 | 'aside', 55 | 'image', 56 | 'video', 57 | 'quote', 58 | 'link', 59 | 'gallery', 60 | 'audio', 61 | ) 62 | ); 63 | 64 | add_theme_support('menus'); 65 | 66 | // Roots.io Soil plugin 67 | add_theme_support('soil-clean-up'); 68 | add_theme_support('soil-disable-asset-versioning'); 69 | add_theme_support('soil-disable-trackbacks'); 70 | add_theme_support('soil-js-to-footer'); 71 | add_theme_support('soil-nav-walker'); 72 | add_theme_support('soil-nice-search'); 73 | add_theme_support('soil-relative-urls'); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /lib/src/Config/TwigSettings.php: -------------------------------------------------------------------------------- 1 | addExtension(new \Twig_Extension_StringLoader()); 19 | 20 | // Loads contents of an SVG file inline 21 | $twig->addFunction(new \Twig_SimpleFunction('svg', function ($path) { 22 | if (substr($path, 0, 1) !== '/') { 23 | $path = "/{$path}"; 24 | } 25 | $path = get_template_directory_uri() . $path; 26 | $ch = curl_init($path); 27 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 28 | // curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); 29 | $data = curl_exec($ch); 30 | if (curl_errno($ch)) { 31 | dump(curl_error($ch)); 32 | echo ''; 33 | } else { 34 | echo $data; 35 | } 36 | curl_close($ch); 37 | })); 38 | 39 | // Example of a custom Twig filter 40 | $twig->addFilter(new \Twig_SimpleFilter('no_orphan', function ($text) { 41 | return preg_replace('/\s+(\S+)\s*$/', ' $1', $text); 42 | })); 43 | 44 | $twig->addFilter(new \Twig_SimpleFilter('break_space', function ($text) { 45 | return str_replace(' ', '
', $text); 46 | })); 47 | 48 | $twig->addFilter(new \Twig_SimpleFilter('json_prop', function ($text) { 49 | return htmlentities(json_encode($text, JSON_HEX_QUOT), ENT_QUOTES); 50 | })); 51 | 52 | $twig->addFilter(new \Twig_SimpleFilter('safe_email', function ($str) { 53 | $email = ''; 54 | for ($i = 0, $len = strlen($str); $i < $len; $i++) { 55 | $j = rand(0, 1); 56 | if ($j == 0) { 57 | $email .= '&#' . ord($str[$i]) . ';'; 58 | } elseif ($j == 1) { 59 | $email .= $str[$i]; 60 | } 61 | } 62 | return str_replace('@', '@', $email); 63 | })); 64 | 65 | return $twig; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /lib/src/Core/Assets.php: -------------------------------------------------------------------------------- 1 | current) { 17 | $this->add_class($this->listItemClass.'--current'); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /lib/src/Core/Site.php: -------------------------------------------------------------------------------- 1 | static::$postType, 24 | 'post_status' => 'publish', 25 | 'posts_per_page' => $perPage, 26 | 'orderby' => 'title', // or menu_order, etc 27 | 'order' => 'ASC' 28 | ]; 29 | 30 | return static::posts($args); 31 | } 32 | 33 | /** 34 | * Convenience function that takes a standard set of WP_Query arguments but mixes it with 35 | * arguments that mean we're selecting the right post type 36 | * 37 | * @param array $args standard WP_Query array 38 | * @return array An array of Post objects 39 | */ 40 | public static function query($args = null) 41 | { 42 | $args = is_array($args) ? $args : []; 43 | 44 | // Set the correct post type 45 | $args = array_merge($args, ['post_type' => static::$postType]); 46 | 47 | if (!isset($args['post_status'])) { 48 | $args['post_status'] = 'publish'; 49 | } 50 | 51 | return static::posts($args); 52 | } 53 | 54 | /** 55 | * Raw query function that uses the arguments provided to make a call to Timber::get_posts 56 | * and casts the returning data in instances of ourself. 57 | * 58 | * @param array $args standard WP_Query array 59 | * @return array An array of Post objects 60 | */ 61 | public static function posts($args = null) 62 | { 63 | return Timber::get_posts($args, get_called_class()); 64 | } 65 | 66 | public static function postType() 67 | { 68 | return static::$postType; 69 | } 70 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "dev": "NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", 5 | "watch": "NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", 6 | "hot": "NODE_ENV=development webpack-dev-server --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js", 7 | "production": "NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js" 8 | }, 9 | "devDependencies": { 10 | "laravel-mix": "^2.x" 11 | }, 12 | "dependencies": { 13 | "vue": "^2.x" 14 | } 15 | } -------------------------------------------------------------------------------- /page.php: -------------------------------------------------------------------------------- 1 | post_name . '.twig', 'page.twig'), $context); 30 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danwill/timber_starter_theme/b78a8562e794fa1e804e842c927e80c97dec6eff/screenshot.png -------------------------------------------------------------------------------- /search.php: -------------------------------------------------------------------------------- 1 | ID)) { 18 | Timber::render('single-password.twig', $context); 19 | } else { 20 | Timber::render(array('single-' . $timber_post->ID . '.twig', 'single-' . $timber_post->post_type . '.twig', 'single-' . $timber_post->slug . '.twig', 'single.twig'), $context); 21 | } 22 | -------------------------------------------------------------------------------- /source/scripts/app.js: -------------------------------------------------------------------------------- 1 | console.log('Built by Mechanic'); 2 | 3 | require('./utils/polyfills.js'); 4 | 5 | import Vue from 'vue'; 6 | 7 | Vue.component('my-component', require('./components/MyComponent.vue')); 8 | 9 | new Vue({ 10 | el: '#app', 11 | }); 12 | -------------------------------------------------------------------------------- /source/scripts/components/MyComponent.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /source/scripts/utils/polyfills.js: -------------------------------------------------------------------------------- 1 | if (!Array.prototype.find) { 2 | Array.prototype.find = function(predicate) { 3 | 'use strict'; 4 | if (this == null) { 5 | throw new TypeError('Array.prototype.find called on null or undefined'); 6 | } 7 | if (typeof predicate !== 'function') { 8 | throw new TypeError('predicate must be a function'); 9 | } 10 | var list = Object(this); 11 | var length = list.length >>> 0; 12 | var thisArg = arguments[1]; 13 | var value; 14 | 15 | for (var i = 0; i < length; i++) { 16 | value = list[i]; 17 | if (predicate.call(thisArg, value, i, list)) { 18 | return value; 19 | } 20 | } 21 | return undefined; 22 | }; 23 | } -------------------------------------------------------------------------------- /source/styles/app.scss: -------------------------------------------------------------------------------- 1 | @import "utility/normalize"; 2 | 3 | @import "utility/variables"; 4 | @import "utility/mixins"; 5 | 6 | @import "common/base"; -------------------------------------------------------------------------------- /source/styles/common/_base.scss: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | } 4 | 5 | img { 6 | max-width: 100%; 7 | } 8 | 9 | [v-cloak] { 10 | display: none; 11 | } -------------------------------------------------------------------------------- /source/styles/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danwill/timber_starter_theme/b78a8562e794fa1e804e842c927e80c97dec6eff/source/styles/components/.gitkeep -------------------------------------------------------------------------------- /source/styles/layouts/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danwill/timber_starter_theme/b78a8562e794fa1e804e842c927e80c97dec6eff/source/styles/layouts/.gitkeep -------------------------------------------------------------------------------- /source/styles/utility/_mixins.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danwill/timber_starter_theme/b78a8562e794fa1e804e842c927e80c97dec6eff/source/styles/utility/_mixins.scss -------------------------------------------------------------------------------- /source/styles/utility/_normalize.scss: -------------------------------------------------------------------------------- 1 | /*! normalize.css v3.0.1 | MIT License | git.io/normalize */ 2 | 3 | /** 4 | * 1. Set default font family to sans-serif. 5 | * 2. Prevent iOS text size adjust after orientation change, without disabling 6 | * user zoom. 7 | */ 8 | 9 | html { 10 | font-family: sans-serif; /* 1 */ 11 | -ms-text-size-adjust: 100%; /* 2 */ 12 | -webkit-text-size-adjust: 100%; /* 2 */ 13 | } 14 | 15 | /** 16 | * Remove default margin. 17 | */ 18 | 19 | body { 20 | margin: 0; 21 | } 22 | 23 | /* HTML5 display definitions 24 | ========================================================================== */ 25 | 26 | /** 27 | * Correct `block` display not defined for any HTML5 element in IE 8/9. 28 | * Correct `block` display not defined for `details` or `summary` in IE 10/11 and Firefox. 29 | * Correct `block` display not defined for `main` in IE 11. 30 | */ 31 | 32 | article, 33 | aside, 34 | details, 35 | figcaption, 36 | figure, 37 | footer, 38 | header, 39 | hgroup, 40 | main, 41 | nav, 42 | section, 43 | summary { 44 | display: block; 45 | } 46 | 47 | /** 48 | * 1. Correct `inline-block` display not defined in IE 8/9. 49 | * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. 50 | */ 51 | 52 | audio, 53 | canvas, 54 | progress, 55 | video { 56 | display: inline-block; /* 1 */ 57 | vertical-align: baseline; /* 2 */ 58 | } 59 | 60 | /** 61 | * Prevent modern browsers from displaying `audio` without controls. 62 | * Remove excess height in iOS 5 devices. 63 | */ 64 | 65 | audio:not([controls]) { 66 | display: none; 67 | height: 0; 68 | } 69 | 70 | /** 71 | * Address `[hidden]` styling not present in IE 8/9/10. 72 | * Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22. 73 | */ 74 | 75 | [hidden], 76 | template { 77 | display: none; 78 | } 79 | 80 | /* Links 81 | ========================================================================== */ 82 | 83 | /** 84 | * Remove the gray background color from active links in IE 10. 85 | */ 86 | 87 | a { 88 | background: transparent; 89 | } 90 | 91 | /** 92 | * Improve readability when focused and also mouse hovered in all browsers. 93 | */ 94 | 95 | a:active, 96 | a:hover { 97 | outline: 0; 98 | } 99 | 100 | /* Text-level semantics 101 | ========================================================================== */ 102 | 103 | /** 104 | * Address styling not present in IE 8/9/10/11, Safari, and Chrome. 105 | */ 106 | 107 | abbr[title] { 108 | border-bottom: 1px dotted; 109 | } 110 | 111 | /** 112 | * Address style set to `bolder` in Firefox 4+, Safari, and Chrome. 113 | */ 114 | 115 | b, 116 | strong { 117 | font-weight: bold; 118 | } 119 | 120 | /** 121 | * Address styling not present in Safari and Chrome. 122 | */ 123 | 124 | dfn { 125 | font-style: italic; 126 | } 127 | 128 | /** 129 | * Address variable `h1` font-size and margin within `section` and `article` 130 | * contexts in Firefox 4+, Safari, and Chrome. 131 | */ 132 | 133 | h1 { 134 | font-size: 2em; 135 | margin: 0.67em 0; 136 | } 137 | 138 | /** 139 | * Address styling not present in IE 8/9. 140 | */ 141 | 142 | mark { 143 | background: #ff0; 144 | color: #000; 145 | } 146 | 147 | /** 148 | * Address inconsistent and variable font size in all browsers. 149 | */ 150 | 151 | small { 152 | font-size: 80%; 153 | } 154 | 155 | /** 156 | * Prevent `sub` and `sup` affecting `line-height` in all browsers. 157 | */ 158 | 159 | sub, 160 | sup { 161 | font-size: 75%; 162 | line-height: 0; 163 | position: relative; 164 | vertical-align: baseline; 165 | } 166 | 167 | sup { 168 | top: -0.5em; 169 | } 170 | 171 | sub { 172 | bottom: -0.25em; 173 | } 174 | 175 | /* Embedded content 176 | ========================================================================== */ 177 | 178 | /** 179 | * Remove border when inside `a` element in IE 8/9/10. 180 | */ 181 | 182 | img { 183 | border: 0; 184 | } 185 | 186 | /** 187 | * Correct overflow not hidden in IE 9/10/11. 188 | */ 189 | 190 | svg:not(:root) { 191 | overflow: hidden; 192 | } 193 | 194 | /* Grouping content 195 | ========================================================================== */ 196 | 197 | /** 198 | * Address margin not present in IE 8/9 and Safari. 199 | */ 200 | 201 | figure { 202 | margin: 1em 40px; 203 | } 204 | 205 | /** 206 | * Address differences between Firefox and other browsers. 207 | */ 208 | 209 | hr { 210 | -moz-box-sizing: content-box; 211 | box-sizing: content-box; 212 | height: 0; 213 | } 214 | 215 | /** 216 | * Contain overflow in all browsers. 217 | */ 218 | 219 | pre { 220 | overflow: auto; 221 | } 222 | 223 | /** 224 | * Address odd `em`-unit font size rendering in all browsers. 225 | */ 226 | 227 | code, 228 | kbd, 229 | pre, 230 | samp { 231 | font-family: monospace, monospace; 232 | font-size: 1em; 233 | } 234 | 235 | /* Forms 236 | ========================================================================== */ 237 | 238 | /** 239 | * Known limitation: by default, Chrome and Safari on OS X allow very limited 240 | * styling of `select`, unless a `border` property is set. 241 | */ 242 | 243 | /** 244 | * 1. Correct color not being inherited. 245 | * Known issue: affects color of disabled elements. 246 | * 2. Correct font properties not being inherited. 247 | * 3. Address margins set differently in Firefox 4+, Safari, and Chrome. 248 | */ 249 | 250 | button, 251 | input, 252 | optgroup, 253 | select, 254 | textarea { 255 | color: inherit; /* 1 */ 256 | font: inherit; /* 2 */ 257 | margin: 0; /* 3 */ 258 | } 259 | 260 | /** 261 | * Address `overflow` set to `hidden` in IE 8/9/10/11. 262 | */ 263 | 264 | button { 265 | overflow: visible; 266 | } 267 | 268 | /** 269 | * Address inconsistent `text-transform` inheritance for `button` and `select`. 270 | * All other form control elements do not inherit `text-transform` values. 271 | * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. 272 | * Correct `select` style inheritance in Firefox. 273 | */ 274 | 275 | button, 276 | select { 277 | text-transform: none; 278 | } 279 | 280 | /** 281 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 282 | * and `video` controls. 283 | * 2. Correct inability to style clickable `input` types in iOS. 284 | * 3. Improve usability and consistency of cursor style between image-type 285 | * `input` and others. 286 | */ 287 | 288 | button, 289 | html input[type="button"], /* 1 */ 290 | input[type="reset"], 291 | input[type="submit"] { 292 | -webkit-appearance: button; /* 2 */ 293 | cursor: pointer; /* 3 */ 294 | } 295 | 296 | /** 297 | * Re-set default cursor for disabled elements. 298 | */ 299 | 300 | button[disabled], 301 | html input[disabled] { 302 | cursor: default; 303 | } 304 | 305 | /** 306 | * Remove inner padding and border in Firefox 4+. 307 | */ 308 | 309 | button::-moz-focus-inner, 310 | input::-moz-focus-inner { 311 | border: 0; 312 | padding: 0; 313 | } 314 | 315 | /** 316 | * Address Firefox 4+ setting `line-height` on `input` using `!important` in 317 | * the UA stylesheet. 318 | */ 319 | 320 | input { 321 | line-height: normal; 322 | } 323 | 324 | /** 325 | * It's recommended that you don't attempt to style these elements. 326 | * Firefox's implementation doesn't respect box-sizing, padding, or width. 327 | * 328 | * 1. Address box sizing set to `content-box` in IE 8/9/10. 329 | * 2. Remove excess padding in IE 8/9/10. 330 | */ 331 | 332 | input[type="checkbox"], 333 | input[type="radio"] { 334 | box-sizing: border-box; /* 1 */ 335 | padding: 0; /* 2 */ 336 | } 337 | 338 | /** 339 | * Fix the cursor style for Chrome's increment/decrement buttons. For certain 340 | * `font-size` values of the `input`, it causes the cursor style of the 341 | * decrement button to change from `default` to `text`. 342 | */ 343 | 344 | input[type="number"]::-webkit-inner-spin-button, 345 | input[type="number"]::-webkit-outer-spin-button { 346 | height: auto; 347 | } 348 | 349 | /** 350 | * 1. Address `appearance` set to `searchfield` in Safari and Chrome. 351 | * 2. Address `box-sizing` set to `border-box` in Safari and Chrome 352 | * (include `-moz` to future-proof). 353 | */ 354 | 355 | input[type="search"] { 356 | -webkit-appearance: textfield; /* 1 */ 357 | -moz-box-sizing: content-box; 358 | -webkit-box-sizing: content-box; /* 2 */ 359 | box-sizing: content-box; 360 | } 361 | 362 | /** 363 | * Remove inner padding and search cancel button in Safari and Chrome on OS X. 364 | * Safari (but not Chrome) clips the cancel button when the search input has 365 | * padding (and `textfield` appearance). 366 | */ 367 | 368 | input[type="search"]::-webkit-search-cancel-button, 369 | input[type="search"]::-webkit-search-decoration { 370 | -webkit-appearance: none; 371 | } 372 | 373 | /** 374 | * Define consistent border, margin, and padding. 375 | */ 376 | 377 | fieldset { 378 | border: 1px solid #c0c0c0; 379 | margin: 0 2px; 380 | padding: 0.35em 0.625em 0.75em; 381 | } 382 | 383 | /** 384 | * 1. Correct `color` not being inherited in IE 8/9/10/11. 385 | * 2. Remove padding so people aren't caught out if they zero out fieldsets. 386 | */ 387 | 388 | legend { 389 | border: 0; /* 1 */ 390 | padding: 0; /* 2 */ 391 | } 392 | 393 | /** 394 | * Remove default vertical scrollbar in IE 8/9/10/11. 395 | */ 396 | 397 | textarea { 398 | overflow: auto; 399 | } 400 | 401 | /** 402 | * Don't inherit the `font-weight` (applied by a rule above). 403 | * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. 404 | */ 405 | 406 | optgroup { 407 | font-weight: bold; 408 | } 409 | 410 | /* Tables 411 | ========================================================================== */ 412 | 413 | /** 414 | * Remove most spacing between table cells. 415 | */ 416 | 417 | table { 418 | border-collapse: collapse; 419 | border-spacing: 0; 420 | } 421 | 422 | td, 423 | th { 424 | padding: 0; 425 | } 426 | -------------------------------------------------------------------------------- /source/styles/utility/_variables.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danwill/timber_starter_theme/b78a8562e794fa1e804e842c927e80c97dec6eff/source/styles/utility/_variables.scss -------------------------------------------------------------------------------- /static/no-timber.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Timber not active 6 | 7 | 8 | 9 |

Timber not activated

10 | 11 | 12 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Theme Name: Mechanic 3 | * Description: Custom theme built by Mechanic 4 | * Author: Mechanic (http://builtbymechanic.com) 5 | */ -------------------------------------------------------------------------------- /views/404.twig: -------------------------------------------------------------------------------- 1 | {% extends "base.twig" %} 2 | 3 | {% block content %} 4 | Sorry, we couldn't find what you're looking for. 5 | {% endblock %} -------------------------------------------------------------------------------- /views/archive.twig: -------------------------------------------------------------------------------- 1 | {# This file demonstrates using most of the index.twig template and modifying 2 | just a small part. See `search.twig` for an example of another approach #} 3 | 4 | {% extends "index.twig" %} 5 | 6 | {% block content %} 7 |

This is my archive

8 | {{ parent() }} 9 | {% endblock %} -------------------------------------------------------------------------------- /views/author.twig: -------------------------------------------------------------------------------- 1 | {% extends "base.twig" %} 2 | 3 | {% block content %} 4 | {% for post in posts %} 5 | {% include ["tease-"~post.post_type~".twig", "tease.twig"] %} 6 | {% endfor %} 7 | {% endblock %} -------------------------------------------------------------------------------- /views/base.twig: -------------------------------------------------------------------------------- 1 | {% block html_head %} 2 | {% include 'html-header.twig' %} 3 | {% block head %} 4 | {% endblock %} 5 | 6 | {% endblock %} 7 | 8 | 9 | 10 | 20 | 21 |
22 |
23 | {% block content %} 24 | Sorry, no content 25 | {% endblock %} 26 |
27 | {% if sidebar %} 28 | 31 | {% endif %} 32 |
33 | 34 | {% block footer %} 35 |
36 |
37 | {% include 'footer.twig' %} 38 |
39 |
40 | {{ function('wp_footer') }} 41 | {% endblock %} 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /views/comment.twig: -------------------------------------------------------------------------------- 1 |
2 |
{{comment.author.name}} says
3 |
{{comment.comment_content|wpautop}}
4 |
-------------------------------------------------------------------------------- /views/footer.twig: -------------------------------------------------------------------------------- 1 | Copyright {{"now"|date('Y')}} -------------------------------------------------------------------------------- /views/front-page.twig: -------------------------------------------------------------------------------- 1 | 2 | {# No specific markup for the home page, yet. Add it here inside a content block! #} 3 | 4 | {% extends "page.twig" %} 5 | -------------------------------------------------------------------------------- /views/html-header.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | {{function('wp_head')}} -------------------------------------------------------------------------------- /views/index.twig: -------------------------------------------------------------------------------- 1 | {% extends "base.twig" %} 2 | 3 | {% block content %} 4 | {% for post in posts %} 5 | {% include ['tease-'~post.post_type~'.twig', 'tease.twig'] %} 6 | {% endfor %} 7 | {% include 'partial/pagination.twig' with { pagination: posts.pagination({show_all: false, mid_size: 3, end_size: 2}) } %} 8 | {% endblock %} 9 | -------------------------------------------------------------------------------- /views/page-slug.twig: -------------------------------------------------------------------------------- 1 | {% extends "base.twig" %} 2 | 3 | {% block content %} 4 |
5 | {{content}} 6 |
7 | {% endblock %} 8 | -------------------------------------------------------------------------------- /views/page.twig: -------------------------------------------------------------------------------- 1 | {% extends "base.twig" %} 2 | 3 | {% block content %} 4 |
5 |
6 |
7 |

{{post.title}}

8 |
9 | {{post.content}} 10 |
11 |
12 |
13 |
14 | {% endblock %} -------------------------------------------------------------------------------- /views/partials/nav.twig: -------------------------------------------------------------------------------- 1 | Menu 2 | 11 | -------------------------------------------------------------------------------- /views/partials/pagination.twig: -------------------------------------------------------------------------------- 1 | {% if posts.pagination.pages is not empty %} 2 | 64 | {% endif %} -------------------------------------------------------------------------------- /views/search.twig: -------------------------------------------------------------------------------- 1 | {# see `archive.twig` for an alternative strategy of extending templates #} 2 | {% extends "base.twig" %} 3 | 4 | {% block content %} 5 | {# see `base.twig:27` for where this block's content will be inserted #} 6 |
7 | {% for post in posts %} 8 | {% include ['tease-'~post.post_type~'.twig', 'tease.twig'] %} 9 | {% endfor %} 10 | 11 | {% include 'partial/pagination.twig' with { pagination: posts.pagination({show_all: false, mid_size: 3, end_size: 2}) } %} 12 |
13 | {% endblock %} -------------------------------------------------------------------------------- /views/sidebar.twig: -------------------------------------------------------------------------------- 1 | Sidebar in Timber. Add HTML to your hearts content. -------------------------------------------------------------------------------- /views/single-password.twig: -------------------------------------------------------------------------------- 1 | {% extends "base.twig" %} 2 | 3 | {% block content %} 4 |
5 | 6 | 7 | 8 |
9 | {% endblock %} 10 | -------------------------------------------------------------------------------- /views/single.twig: -------------------------------------------------------------------------------- 1 | {% extends "base.twig" %} 2 | 3 | {% block content %} 4 |
5 |
6 |
7 |

{{post.title}}

8 |

9 | By {{ post.author.name }} {{ post.post_date|date}} 10 |

11 |
12 |
13 |
14 |

Comments

15 | {{ comment_form }} 16 |
17 |
18 | {% for cmt in post.get_comments() %} 19 | {% include "comment.twig" with { comment:cmt } %} 20 | {% endfor %} 21 |
22 |
23 |
24 |
25 | {% endblock %} 26 | -------------------------------------------------------------------------------- /views/tease.twig: -------------------------------------------------------------------------------- 1 |
2 | {% block content %} 3 |

{{post.title}}

4 |

{{post.preview}}

5 | {% if post.get_thumbnail %} 6 | 7 | {% endif %} 8 | {% endblock %} 9 |
-------------------------------------------------------------------------------- /webpack.mix.js: -------------------------------------------------------------------------------- 1 | // Laravel Mix config 2 | // Documentation: https://github.com/JeffreyWay/laravel-mix/tree/master/docs#readme 3 | 4 | const mix = require('laravel-mix'); 5 | 6 | const source = 'source'; 7 | const public = 'assets'; 8 | 9 | mix.setPublicPath(public); 10 | 11 | // Full options: https://browsersync.io/docs/options/ 12 | mix.browserSync({ 13 | proxy: 'project.test', 14 | files: [ 15 | `${public}/js/*.js`, 16 | `${public}/css/*.css`, 17 | 'views/**/*.twig', 18 | '**/*.php' 19 | ], 20 | ghostMode: false 21 | }); 22 | 23 | if (mix.inProduction()) { 24 | mix.version(); 25 | mix.options({ 26 | terser: { 27 | terserOptions: { 28 | compress: { 29 | drop_console: true 30 | } 31 | } 32 | } 33 | }); 34 | } 35 | 36 | // mix.js(`${source}/scripts/app.js`, `${public}/js`).sourceMaps() //to enable sourcemaps 37 | mix.js(`${source}/scripts/app.js`, `${public}/js`) 38 | .sass(`${source}/styles/app.scss`, `${public}/css`, { 39 | outputStyle: mix.inProduction() ? 'compressed' : 'expanded' 40 | }) 41 | .options({ 42 | processCssUrls: false 43 | }); 44 | 45 | 46 | 47 | mix.disableNotifications(); --------------------------------------------------------------------------------