Skip to content

Instantly share code, notes, and snippets.

@matthewmcvickar
Last active November 25, 2019 21:20
Show Gist options
  • Save matthewmcvickar/62280b6d589ac1f7b7a0cc1c697c1e76 to your computer and use it in GitHub Desktop.
Save matthewmcvickar/62280b6d589ac1f7b7a0cc1c697c1e76 to your computer and use it in GitHub Desktop.
Config files
# Browsers that we support.
# https://github.com/browserslist/browserslist
last 1 version and > .1% and not dead
not OperaMini all
# https://EditorConfig.org
# NOTE:
# For this configuration file to work in your editor, you must install the
# appropriate EditorConfig plugin. Check out the https://EditorConfig.org site
# for instructions.
# This is the top-most EditorConfig file.
root = true
# All files:
[*]
# Unix-style newlines.
end_of_line = lf
# Add a newline at end of file on save.
insert_final_newline = true
# Trim trailing whitespace on save.
trim_trailing_whitespace = true
# Indent with spaces.
indent_style = space
# Use two spaces per indentation level.
indent_size = 2
# The environment WordPress is currently running in ('dev', 'staging', 'production', etc.)
ENVIRONMENT="dev"
# The development site URL.
DEV_SITE_URL="https://TODO.local"
{
"parser": "babel-eslint",
"parserOptions": {
"sourceType": "module"
},
"env": {
"browser": true,
"commonjs": true,
"jquery": true
},
"extends": [
"eslint:recommended"
],
"rules": {
"strict": 0,
"camelcase": 0,
"quotes": [1, "double", "avoid-escape"],
"no-console": 0
}
}
# OS
/.env
/.idea
.DS_Store
# SSL certificates
*.pem
# Local by Flywheel
/logs
# Development
.sass-cache
*.css.map
node_modules
*.LICENSE
# Don't keep track of generated non-minified development files.
/assets/css/*.css
/assets/js/*.js
# _Do_ keep track of minified files, since they are only generated when building
# for a deploy and should be included in the repo so that, when the repo is
# deployed, it includes these built files.
!/assets/css/*.min.css
!/assets/js/*.min.js
{
"plugins": [
"stylelint-declaration-block-no-ignored-properties",
"stylelint-order",
"stylelint-scss"
],
"extends": [
"stylelint-a11y/recommended"
],
"rules":
"block-no-empty": null,
"block-opening-brace-space-before": "always-multi-line",
"block-opening-brace-newline-after": "always-multi-line",
"color-no-invalid-hex": true,
"comment-empty-line-before": [ "always", {
"ignore": [
"stylelint-commands",
"after-comment"
]
} ],
"declaration-block-trailing-semicolon": "always",
"declaration-colon-space-after": "always-single-line",
"indentation": [ 2, {
"ignore": [
"value"
]
} ],
"font-family-no-missing-generic-family-keyword": true,
"function-comma-space-before": "never",
"function-comma-space-after": "always",
"function-max-empty-lines": 1,
"max-empty-lines": 2,
"order/order": [
"custom-properties",
"declarations"
],
"order/properties-alphabetical-order": true,
"plugin/declaration-block-no-ignored-properties": true,
"property-case": "lower",
"rule-empty-line-before": [ "always-multi-line", {
"except": ["first-nested"],
"ignore": ["after-comment"]
} ],
"scss/at-function-named-arguments": [ "always", {
"ignore": [
"single-argument"
]
} ],
"scss/at-function-parentheses-space-before": "never",
"scss/at-mixin-named-arguments": [ "always", {
"ignore": [
"single-argument"
]
} ],
"scss/at-mixin-parentheses-space-before": "never",
"scss/dollar-variable-colon-space-after": "at-least-one-space",
"scss/dollar-variable-colon-newline-after": "always-multi-line",
"scss/double-slash-comment-empty-line-before": [ "always", {
"except": [
"first-nested"
],
"ignore": [
"between-comments"
]
} ],
"scss/double-slash-comment-whitespace-inside": "always",
"selector-list-comma-newline-after": "always",
"selector-nested-pattern": "^&"
}
}

TODO WordPress Theme

The WordPress theme for the TODO website.

Table of Contents

Setup

Here's how to get your local copy of the site up and running.

Quick Overview

We create a local multi-site WordPress installation using Local by Flywheel, download this repository that contains the theme files, symlink the theme into the right place in the WordPress folder hierarchy, enable the theme in WordPress, install plugins, grab the latest staging/production database, and then get to work.

Setup, Part 1: Get an Account on the Staging Server

  1. Ask your development team leader to make you an administrator account on the staging server and save the login details in your 1Password account.

Setup, Part 2: Install WordPress Locally

  1. Download and install Local by Flywheel.

  2. Open Local by Flywheel. Create a new site called 'TODO.' Keep the defaults, choose the 'Preferred' environment, and make it a WordPress Multisite (with subdomains).

    Detailed settings:

    • Page 1: What's your site's name?
      • Name: TODO
      • Advanced Options:
        • Local site domain: TODO.local
        • Local site path: ~/Local Sites/TODO/
        • Create site from Blueprint: No
    • Page 2: Choose your environment: Preferred
    • Page 3: Setup WordPress:
      • WordPress Username: admin
      • WordPress Password: admin
      • WordPress Email: [keep the default]
      • Avanced Options:
        • TODO: Is this a WordPress multisite? Yes — Subdomain
        • TODO: Is this a WordPress multisite? No
  3. Once the site is created in Local by Flywheel, click on the 'SSL' tab and click 'Trust' to enable SSL on the local installation.

  4. Open the Local by Flywheel preferences. Click on the 'Advanced' tab and turn on 'IPv6 Loopback.' Close the preferences (‘×’ icon in the top right corner.)

  5. Open your terminal app.

  6. Install Homebrew if you haven't before.

    Install mkcert to generate your own SSL certificate for localhost.

    brew install mkcert
    brew install nss
    mkcert -install
    mkcert localhost 127.0.0.1 ::1

    Note: If you use Firefox, restart it now for the SSL certificate to work.

  7. Change directory to the site installation:

    cd ~/Local\ Sites/TODO
  8. Clone this repository into it.

    git clone [email protected]:owen-jones/TODO.git
  9. Go back to Local by Flywheel. Click on the 'Add-ons' button in the sidebar (it looks like a puzzle piece). Click on the 'Volumes' add-on, install it, and then click the 'Enable & Relaunch' button.

  10. Once Local by Flywheel relaunches, select the newly created site in the sidebar and click on the 'More' drop-down menu and click 'Volumes.'

  11. Click 'Add Volume.' Settings are as follows:

    • Click 'Browse,' browse to the TODO theme directory you just cloned, and click 'Open.'
    • In the 'Container Destination' column, enter this path: /app/public/wp-content/themes/TODO
    • Click 'Remap Volumes' and click OK in the confirmation popup.
  12. Make a copy of the .env.example file and call it .env.

    Customize your .env file to match your installation. Below are the properties you should verify for your install. You may not need to change anything.

    • ENVIRONMENT: For all local dev environments, this should be set to dev.
    • DEV_SITE_URL: This should match your local URL (which will be https://TODO.local unless you changed it in Local by Flywheel during setup.)
  13. Enable the theme. Go to the WordPress network admin theme settings and log in (username admin and password admin unless you changed that during setup). Under 'TODO' in the theme list, click 'Network Enable.'

  14. Now go to the main site WP Admin. In the admin sidebar, hover over the 'Appearance' menu and click 'Themes.' Click 'Activate' on the TODO theme. (You may delete the other default themes at this point, since we won't be using them.)

  15. Once you activate the theme, you'll get a warning that some plugins are required. Click the 'Begin Installing Plugins' link in that warning. Select all of the plugins in the list, install them, then activate them.

Setup, Part 3: Sync the Staging Database to Local

[TODO: Once we deploy to the production for the first time, these instructions should be changed to point to the production endpoints.]

  1. Add WP DB Migrate Pro license key on the settings page. The key is available in your Owen Jones 'Dev Team' vault in 1Password.

  2. Still on the Migrate DB screen, click on the 'Migrate' tab. From the list of options, select 'Pull.'

  3. In a new tab, copy the Connection Info key from the staging server WP DB Migrate Pro settings.

  4. Go back to your local Migrate DB screen and paste the connection key.

  5. Once connected, do an initial sync.

    Detailed settings:

    • Pull into a specific subsite: TODO.local
    • Leave the 'Find & Replace' settings as is.
    • Tables: Select All
    • Exclude post types: leave unchecked
    • Advanced Options: check all checkboxes
    • Backup the local database before replacing it: leave unchecked
    • Media Files: checked
      • Compare, download then remove
    • Save Migration Profile: name it Pull from staging
  6. Click 'Pull & Save' to sync for the first time.

  7. You will be logged off after this initial sync. From now on, you'll log in using credentials that were created for you at the beginning of this process.


Development

How to Get Development Build Tools Running

  1. Make sure you have Node and NPM installed.

  2. Install dependencies:

    npm install

Where Things Are (CSS, JS, and Images)

  • Source files are kept in the /_src/ folder.
  • JavaScript is compiled by Webpack, which transpiles the code from ES6 syntax to browser-ready ES5 and bundles files from /_src/js/ and into /assets/js/main.js.
  • CSS is written in SASS, using SCSS syntax. It is located in /_src/css/ and compiled to /assets/css/main.css.
  • Images are optimized from /_src/images/ and copied to /assets/images/.
  • ⚠️ NOTE: The /assets/images/ folder is the destination for optimized images. It will be deleted every time development scripts are run in order to clear out old files. Don't put files directly into the /assets/images/ folder!

How to Watch Files and Compile Assets While Developing the Website

Gulp is used to handle development tasks.

To start the asset compilation, run the script that builds and watches for changes in the CSS/JS/images folders:

npm run dev

OR

npm start

This script also "lints" (checks) your JS and CSS for code style and syntax errors. These errors will be reported in the terminal.

This script also starts a BrowserSync server at https://localhost:3000 to live-reload the browser when assets are updated. This is convenient for automatic reloading and for checking the website on other devices on the local network.

Webpack

Gulp compiles CSS and images, but Webpack is used for compiling JS. (Gulp launches Webpack when you run npm run dev, but Webpack takes over after that.)

Webpack will generate a JS file for every file listed in the entry array defined at the top of the webpack.common.js file. To generate a separate file (e.g., a foo.js file that will only be loaded on a certain page), add a file to that list and then add the script to the template you want.

Modernizr

This site uses some features that are only found in newer browsers, and we provide fallbacks using Modernizr. The modernizr.js file is automatically generated by searching JS and CSS files for JS test names and CSS class names that match the Modernizr ones (see examples here).

Because generating the Modernizr file can be a little time-consuming and generate a lot of console ouput, we only run it on the initial npm start.

⚠️ NOTE: If you add a new Modernizr detection feature to JS or CSS, you must cancel the running Gulp task and re-run npm start to regenerate the Modernizr script.


Deployment

TODO: How will deployment actually work?

How to Deploy to Staging:

  1. Do a minified build.

    npm run build
    git commit -a -m "Staging build."
    git push
    
  2. SSH to OJP Projects server and update the site:

    ssh ojp-projects
    cd /var/www/projects/TODO/
    git pull

How to Deploy to Production:

  1. Run the deploy command:

    npm run deploy

Important Notes

TODO

const autoprefixer = require('autoprefixer');
const browserSync = require('browser-sync');
const del = require('del');
const gulp = require('gulp');
const gulpif = require('gulp-if');
const imagemin = require('gulp-imagemin');
const modernizr = require('gulp-modernizr');
const notify = require('gulp-notify');
const postcss = require('gulp-postcss');
const replace = require('gulp-replace');
const rename = require('gulp-rename');
const sass = require('gulp-sass');
const stylelint = require('gulp-stylelint');
const sourcemaps = require('gulp-sourcemaps');
const uglify = require('gulp-uglify');
const webpack = require('webpack-stream');
/*
Configuration variables.
*/
// Gather environment variables from .env file.
require('dotenv').config();
// Determine if we're running in the dev environment. This is set by the script
// in `package.json` that calls this gulpfile so that we can force the script to
// generate production-ready files.
const isDev = process.env.NODE_ENV === 'development';
// Site information.
const siteURL = process.env.DEV_SITE_URL;
// Paths.
const srcDir = './_src';
const destDir = './assets';
const paths = {
js: {
srcDir: srcDir + '/js/',
src: srcDir + '/js/**/*.js',
dest: destDir + '/js'
},
css: {
src: srcDir + '/css/**/*.{sass,scss,css}',
dest: destDir + '/css'
},
images: {
src: srcDir + '/images/**/*',
dest: destDir + '/images'
},
templates: './**/*.php',
functionsFile: './functions.php'
};
// Export to allow other scripts (i.e. Webpack) to access these paths.
exports.paths = paths;
/*
Task configuration.
*/
// Build JS.
const webpackConfig = require('./webpack.' + process.env.NODE_ENV + '.js');
const compileJS = () => {
return gulp.src(paths.js.src)
.pipe(webpack(webpackConfig))
.pipe(gulp.dest(paths.js.dest))
}
// Build Modernizr file.
const compileModernizr = () => {
return gulp.src([paths.js.src, paths.css.src])
.pipe(modernizr({
excludeTests: [
'svg'
]
}))
.pipe(uglify())
.pipe(gulp.dest(paths.js.dest))
}
// Build CSS.
const compileCSS = () => {
const sassOptions = {
includePaths: ['node_modules']
}
const stylelintOptions = {
fix: true,
reporters: [
{
formatter: 'string',
console: true
}
]
}
if (!isDev) {
sassOptions.outputStyle = 'compressed';
}
return gulp.src(paths.css.src)
.pipe(stylelint(stylelintOptions))
.pipe(gulpif(isDev, sourcemaps.init()))
.pipe(sass(sassOptions))
.on('error', notify.onError((error) => {
return error.message;
}))
.pipe(postcss([ autoprefixer() ]))
.pipe(gulpif(isDev, sourcemaps.write()))
.pipe(gulpif(!isDev, rename({ suffix: '.min' })))
.pipe(gulp.dest(paths.css.dest))
.pipe(browserSyncServer.stream())
}
// Images.
const optimizeImages = () => {
return gulp.src(paths.images.src)
.pipe(imagemin([
imagemin.gifsicle({interlaced: true}),
imagemin.jpegtran({progressive: true}),
imagemin.optipng({optimizationLevel: 5}),
imagemin.svgo({
plugins: [
{removeViewBox: false},
{cleanupIDs: false}
]
})
]))
.pipe(gulp.dest(paths.images.dest))
}
// Browsersync auto-refreshing.
const browserSyncServer = browserSync.create();
const browsersyncServe = (done) => {
browserSyncServer.init({
proxy: siteURL,
open: false,
notify: false,
https: {
key: "localhost+2-key.pem",
cert: "localhost+2.pem",
},
});
done();
}
const reloadBrowser = (done) => {
browserSyncServer.reload();
done();
}
// Watch for changes.
const watch = () => {
// CSS: Watch for source files to change, recompile CSS, and reload.
gulp.watch(paths.css.src, compileCSS);
// Images: Watch for images to change, re-optimize them, and reload.
gulp.watch(paths.images.src, gulp.series(optimizeImages, reloadBrowser));
// JS: Watch for Webpack's builds and reload.
gulp.watch(paths.js.dest, reloadBrowser);
// Files: Watch for template files to change and reload.
gulp.watch(paths.templates, reloadBrowser);
}
// Clean up.
const clean = () => {
return del([
paths.images.dest
])
}
// Update the timestamp used for cache-busting.
const bustCache = () => {
return gulp.src(paths.functionsFile)
.pipe(
replace(/'CACHEBUST', '\d+'/g, function() {
return "'CACHEBUST', '" + new Date().getTime() + "'";
})
)
.pipe(gulp.dest('./'));
}
// Build assets.
const compileAssets = gulp.parallel(
compileCSS,
compileJS,
compileModernizr,
optimizeImages
);
/*
Exports.
*/
// Define tasks.
exports.dev = gulp.series(clean, gulp.parallel(compileAssets, browsersyncServe, watch));
exports.build = gulp.series(clean, compileAssets, bustCache);
{
"name": "ojp-TODO",
"private": true,
"version": "0.1.0",
"repository": {
"type": "git",
"url": "https://github.com/owen-jones/TODO"
},
"ignore": [
"node_modules"
],
"devDependencies": {
"@babel/core": "^7.7.2",
"@babel/plugin-transform-runtime": "^7.6.2",
"@babel/preset-env": "^7.7.1",
"@babel/runtime": "^7.7.2",
"autoprefixer": "^9.7.1",
"babel-eslint": "^10.0.3",
"babel-loader": "^8.0.6",
"browser-sync": "^2.26.7",
"del": "^5.1.0",
"dotenv": "^8.2.0",
"eslint": "^6.6.0",
"eslint-loader": "^3.0.2",
"gulp": "^4.0.2",
"gulp-if": "^3.0.0",
"gulp-imagemin": "^6.2.0",
"gulp-modernizr": "^3.4.0",
"gulp-notify": "^3.2.0",
"gulp-postcss": "^8.0.0",
"gulp-rename": "^1.4.0",
"gulp-replace": "^1.0.0",
"gulp-sass": "^4.0.2",
"gulp-sourcemaps": "^2.6.5",
"gulp-stylelint": "^10.0.0",
"gulp-uglify": "^3.0.2",
"lodash": "^4.17.15",
"normalize.css": "^8.0.1",
"sass": "^1.23.3",
"stylelint": "^11.1.1",
"stylelint-a11y": "^1.2.2",
"stylelint-declaration-block-no-ignored-properties": "^2.1.0",
"stylelint-no-unsupported-browser-features": "^4.0.0",
"stylelint-order": "^3.1.1",
"stylelint-scss": "^3.12.1",
"webpack": "^4.41.2",
"webpack-cli": "^3.3.10",
"webpack-merge": "^4.2.2",
"webpack-notifier": "^1.8.0",
"webpack-stream": "^5.2.1"
},
"scripts": {
"start": "npm run dev",
"dev": "NODE_ENV=development gulp dev",
"build": "NODE_ENV=production gulp build",
"deploy": "sh ./deploy.sh"
}
}
@matthewmcvickar
Copy link
Author

I tested but ultimately decided against using the stylelint-no-unsupported-browser-features StyleLint plugin, because it has no way of seeing that you're using a fallback and clogs your terminal with warnings.

The settings would be, in .stylelintrc:

{
  "plugins": [
    "stylelint-no-unsupported-browser-features"
  ]
  "rules": {
      "plugin/no-unsupported-browser-features": [ true, {
        "severity": "warning"
      } ],
  } 
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment