diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..ad93c14 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,5 @@ +{ + "image": "mcr.microsoft.com/devcontainers/universal:2", + "features": { + } +} diff --git a/.env.sample b/.env.sample new file mode 100644 index 0000000..ea2a679 --- /dev/null +++ b/.env.sample @@ -0,0 +1,71 @@ +# Configuration for the Ideas On Purpose WordPress Docker environment, +# more info here: https://github.com/ideasonpurpose/docker-wordpress-dev + + +SSH_KEY_PATH= +# SSH_KEY_PATH should be set to the local path to your private key. For example, +# if you have public key named `id_rsa_deploy.pub` associated with your managed +# WordPress account, SSH_KEY_PATH should point to the pair's matching private +# key like this: SSH_KEY_PATH="~/.ssh/id_rsa_deploy" + + +SSH_LOGIN= +# SSH_LOGIN should be set to the SSH Login string from WP Engine's or Kinsta's +# admin backend. They should look something like this: +# - wpengine: "iop001@iop001.ssh.wpengine.net" +# - kinsta: "ssh iop001@11.22.33.44 -p 54321" +# In the above examples the elements map like this: +# `${SSH_USER}@${SSH_HOST}` +# Each item can also be entered individually, and individual entries will take +# precedence over components extracted from SSH_LOGIN. +# Values should be quoted for compatibility with Docker Compose v2.x + + +SSH_USER= +# The user account which connects to the server. For WP Engine, this matches the +# environment name. + + +SSH_HOST= +# The server address to connect to. + + +SSH_PORT= +# The server port listening for SSH connections + + +SSH_WP_CONTENT_DIR= +# default: sites/${SSH_USER}/wp-content +# SSH_WP_CONTENT_DIR is the path to the wordpress wp-content directory. This will +# most likely match the `WP_CONTENT_DIR` WordPress constant and does not include +# a trailing slash. +# Path can be relative to the SSH user home folder or an absolute path. +# Examples: +# - wpengine: sites/${SSH_USER}/wp-content +# - kinsta: public/wp-content + + +DATA_MODEL_PLUGIN= +BLOCKS_PLUGIN= +# Additional development plugins can be mounted into the WordPress environment as +# individual volumes. The local relative path to the working plugin directory should +# be pointed to the absolute path inside the container. In the following example, +# a data-model plugin is being developed in a sibling directory to the theme: +# +# DATA_MODEL_PLUGIN="../example-plugin:/var/www/html/wp-content/plugins/example-plugin" + + +WORDPRESS_DEBUG=1 +# This is used to set the WP_DEBUG constant to toggle WordPress debugging. +# .env vars are imported as strings, so true/false will not work correctly. +# PHP does correctly coerce the integers 0 and 1 to their corresponding boolean values. +# PHP interprets the number 0 as a string to boolean false +# Leave this value blank or set it to 0 to disable WP_DEBUG +# Setting this to 1, true or any other non-empty string enables WP_DEBUG + + +WP_ENVIRONMENT_TYPE= +# Blank or one of 'production', 'staging' or 'development'. For IOP's development +# environments, this value defaults to 'development'. Otherwise WordPress defaults +# to 'production'. Documentation: +# https://developer.wordpress.org/reference/functions/wp_get_environment_type/ diff --git a/.gitignore b/.gitignore index 396493b..0a59f50 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,178 @@ -node_modules -logs -wp-content -!wp-content/themes -wp-content/themes/twenty* +# Managed WordPress Hosting .gitignore file for ignoring WordPress files +# +# Most recent revision here: +# https://gist.github.com/joemaller/4f7518e0d04a82a3ca16 +# +# Raw Source (for curl): +# https://gist.githubusercontent.com/joemaller/4f7518e0d04a82a3ca16/raw +# +# Used by these WordPress Development environments: +# https://github.com/ideasonpurpose/docker-wordpress-dev +# https://github.com/ideasonpurpose/basic-wordpress-vagrant (deprecated) +# +# Originally based on WP Engine's gitignore files: +# https://wpengine.com/support/using-git-on-sites-with-legacy-staging/#ignore +# https://wpengine.com/wp-content/uploads/2013/10/recommended-gitignore-no-wp.txt + +# environment vars +.env + +# temp files, OS junk and dotfiles +*.bak +*.swp +*.tmp +*~ +.cvs +.DS_Store +.listing +.svn +Thumbs.db + +# wp core (as of 4.6) +/db-config.php +/index.php +/license.txt +/readme.html +/wp-activate.php +/wp-admin +/wp-app.php +/wp-atom.php +/wp-blog-header.php +/wp-comments-post.php +/wp-commentsrss2.php +/wp-config-sample.php +/wp-content/index.php +/wp-content/themes/index.php +/wp-content/themes/twenty* +/wp-cron.php +/wp-feed.php +/wp-includes +/wp-links-opml.php +/wp-load.php +/wp-login.php +/wp-mail.php +/wp-pass.php +/wp-rdf.php +/wp-register.php +/wp-rss.php +/wp-rss2.php +/wp-settings.php +/wp-signup.php +/wp-trackback.php +/xmlrpc.php + +# wordpress config, user-content and caches +cache/ +sitemap.xml +sitemap.xml.gz +wp-config.php +wp-content/advanced-cache.php +wp-content/backup-db/ +wp-content/backups/ +wp-content/blogs.dir +wp-content/cache/ +wp-content/cache/supercache/ +wp-content/managewp/ +wp-content/upgrade/ +wp-content/uploads/ +wp-content/wp-cache-config.php + +# WordPress debugging files & logs +*.log +debug.log +log.txt +logs/ + +# Third-party files and leftovers +gt-cache/ +ics-importer-cache/ +imagecache.* +pclzip-*.gz +wp-content/w3-.* +wp-content/w3tc.* +gallery/* +album/* + +# wpengine specific +.smushit-status +_wpeprivate +wp-content/advanced-cache.php +wp-content/object-cache.php + +# flywheel specific +wp-content/flywheel-config/ + +# large/disallowed file types +# a CDN should be used for these +*.3gp +*.3gpp +*.asf +*.asx +*.avi +*.bin +*.deb +*.dll +*.dmg +*.exe +*.flv +*.hqx +*.img +*.iso +*.kar +*.m4a +*.m4v +*.mid +*.midi +*.mng +*.mov +*.mp3 +*.mp4 +*.mpeg +*.mpg +*.msi +*.msm +*.msp +*.ogg +*.ra +*.webm +*.wmv + +# don't commit database dumpfiles *.sql +*.sql.gz +*.sql.tgz +*.sql.zip + +# npm and composer dependencies +node_modules/ +vendor/ + +# Let's try ignoring sourcemaps +*.css.map +*.js.map + +# basic-wordpress-vagrant v0.3.0 added links to Kint and XHProf. Ignore them +/kint +/xhprof + +# Moving away from Git-deploys, exclude generated artifacts +/_builds/ +/builds +dist/ + +# Webpack Stats & profiler +/webpack + +# Ignore all plugins +wp-content/plugins +wp-content/mu-plugins + +# I have not yet wanted these files in project history +*.code-workspace + +# Docker experiments +_log/ +_profiler/ + +# WSL 2 Cruft +*:Zone.Identifier diff --git a/.stylelintrc.js b/.stylelintrc.js new file mode 100644 index 0000000..9476165 --- /dev/null +++ b/.stylelintrc.js @@ -0,0 +1,308 @@ +// TODO: Move these into separate files +const special = { + groupName: "Special", + emptyLineBefore: "threshold", + noEmptyLineBetween: true, + properties: ["composes", "@import", "@extend", "@mixin", "@at-root"], +}; + +const content = { + groupName: "Content", + emptyLineBefore: "threshold", + noEmptyLineBetween: true, + properties: ["content"], +}; + +const positioning = { + groupName: "Positioning", + emptyLineBefore: "threshold", + noEmptyLineBetween: true, + properties: ["position", "top", "right", "bottom", "left", "z-index"], +}; + +const boxModel = { + groupName: "Box Model", + emptyLineBefore: "threshold", + noEmptyLineBetween: true, + properties: [ + "display", + "flex", + "flex-basis", + "flex-direction", + "flex-flow", + "flex-grow", + "flex-shrink", + "flex-wrap", + "grid", + "grid-area", + "grid-auto-rows", + "grid-auto-columns", + "grid-auto-flow", + "grid-gap", + "grid-row", + "grid-row-start", + "grid-row-end", + "grid-row-gap", + "grid-column", + "grid-column-start", + "grid-column-end", + "grid-column-gap", + "grid-template", + "grid-template-areas", + "grid-template-rows", + "grid-template-columns", + "gap", + "align-content", + "align-items", + "align-self", + "justify-content", + "justify-items", + "justify-self", + "order", + "float", + "clear", + "box-sizing", + "width", + "min-width", + "max-width", + "height", + "min-height", + "max-height", + "margin", + "margin-top", + "margin-right", + "margin-bottom", + "margin-left", + "padding", + "padding-top", + "padding-right", + "padding-bottom", + "padding-left", + ], +}; + +const visual = { + groupName: "Visual", + emptyLineBefore: "threshold", + noEmptyLineBetween: true, + properties: [ + "list-style", + "list-style-position", + "list-style-type", + "list-style-image", + "table-layout", + "empty-cells", + "caption-side", + "background", + "background-color", + "background-image", + "background-repeat", + "background-position", + "background-position-x", + "background-position-y", + "background-size", + "background-clip", + "background-origin", + "background-attachment", + "background-blend-mode", + "border", + "border-color", + "border-style", + "border-width", + "border-top", + "border-top-color", + "border-top-width", + "border-top-style", + "border-right", + "border-right-color", + "border-right-width", + "border-right-style", + "border-bottom", + "border-bottom-color", + "border-bottom-width", + "border-bottom-style", + "border-left", + "border-left-color", + "border-left-width", + "border-left-style", + "border-radius", + "border-top-left-radius", + "border-top-right-radius", + "border-bottom-right-radius", + "border-bottom-left-radius", + "border-image", + "border-image-source", + "border-image-slice", + "border-image-width", + "border-image-outset", + "border-image-repeat", + "border-collapse", + "border-spacing", + "outline", + "outline-width", + "outline-style", + "outline-color", + "outline-offset", + "box-shadow", + "box-decoration-break", + "transform", + "transform-origin", + "transform-style", + "backface-visibility", + "perspective", + "perspective-origin", + "visibility", + "cursor", + "opacity", + "filter", + "isolation", + "backdrop-filter", + "mix-blend-mode", + ], +}; + +const typography = { + groupName: "Typography", + emptyLineBefore: "threshold", + noEmptyLineBetween: true, + properties: [ + "color", + "font", + "font-weight", + "font-size", + "font-family", + "font-style", + "font-variant", + "font-size-adjust", + "font-stretch", + "font-effect", + "font-emphasize", + "font-emphasize-position", + "font-emphasize-style", + "font-smooth", + "line-height", + "direction", + "letter-spacing", + "white-space", + "text-align", + "text-align-last", + "text-transform", + "text-decoration", + "text-emphasis", + "text-emphasis-color", + "text-emphasis-style", + "text-emphasis-position", + "text-indent", + "text-justify", + "text-outline", + "text-wrap", + "text-overflow", + "text-overflow-ellipsis", + "text-overflow-mode", + "text-orientation", + "text-shadow", + "vertical-align", + "word-wrap", + "word-break", + "word-spacing", + "overflow-wrap", + "tab-size", + "hyphens", + "unicode-bidi", + "columns", + "column-count", + "column-fill", + "column-gap", + "column-rule", + "column-rule-color", + "column-rule-style", + "column-rule-width", + "column-span", + "column-width", + "page-break-after", + "page-break-before", + "page-break-inside", + "src", + ], +}; + +const animation = { + groupName: "Animation", + emptyLineBefore: "threshold", + noEmptyLineBetween: true, + properties: [ + "transition", + "transition-delay", + "transition-timing-function", + "transition-duration", + "transition-property", + "animation", + "animation-name", + "animation-duration", + "animation-play-state", + "animation-timing-function", + "animation-delay", + "animation-iteration-count", + "animation-direction", + "animation-fill-mode", + ], +}; + +const misc = { + groupName: "Misc.", + emptyLineBefore: "threshold", + noEmptyLineBetween: true, + properties: [ + "appearance", + // "content", + "clip", + "clip-path", + "counter-reset", + "counter-increment", + "resize", + "user-select", + "nav-index", + "nav-up", + "nav-right", + "nav-down", + "nav-left", + "pointer-events", + "quotes", + "touch-action", + "will-change", + "zoom", + "fill", + "fill-rule", + "clip-rule", + "stroke", + ], +}; + +module.exports = { + plugins: ["stylelint-order", "stylelint-prettier"], + customSyntax: "postcss-scss", + rules: { + "prettier/prettier": [true, { printWidth: 120 }], + "order/properties-order": [ + [ + special, + content, + positioning, + boxModel, + typography, + visual, + animation, + misc, + ], + { + emptyLineBeforeUnspecified: "threshold", + emptyLineMinimumPropertyThreshold: 7, + }, + ], + "rule-empty-line-before": [ + "always-multi-line", + { except: ["first-nested"], ignore: ["after-comment"] }, + ], + }, + ignoreFiles: ["**/node_modules/**/*", "**/dist/**/*", "**/*.js"], +}; diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..1016c99 --- /dev/null +++ b/composer.json @@ -0,0 +1,40 @@ +{ + "name": "ideasonpurpose/docker-wordpress-dev", + "description": "Docker-based local development environment for WordPress projects", + "authors": [ + { + "name": "Ideas On Purpose", + "homepage": "https://www.ideasonpurpose.com" + } + ], + "config": { + "optimize-autoloader": true, + "sort-packages": true, + "vendor-dir": "wp-content/themes/docker-wordpress-dev/vendor", + "platform": { + "php": "8.0", + "ext-intl": "0" + } + }, + "autoload": { + "psr-4": { + "IdeasOnPurpose\\": [ + "wp-content/themes/docker-wordpress-dev/lib" + ] + } + }, + "require": { + "ideasonpurpose/wp-google-analytics": "^1.1.0", + "ideasonpurpose/wp-svg-lib": "^2.0.1", + "ideasonpurpose/wp-theme-init": "^2.9.0", + "ideasonpurpose/wp-vimeo-embed": "dev-master", + "php-stubs/wordpress-stubs": "dev-master" + }, + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/ideasonpurpose/wp-vimeo-embed", + "no-api": true + } + ] +} diff --git a/docker-compose.yml b/docker-compose.yml index 9f1f66d..9d6ac80 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,28 +1,288 @@ services: - # This service bumps the WordPress verion in the Dockerfile to the latest release. - # See the bin/bump-wordpress.sh script for docs - # - https://github.com/igeclouds/wordpressdevenv - bump-wp: - image: jetbrainsinfra/jq:latest + # Primary database for the local WordPress development environment. + # Image from: https://hub.docker.com/_/mariadb + db: + # image: mysql:5.7 + # NOTE: 10.7.x was corrupting NJHI and IOP imports for unknown reasons + # image: mariadb:10.7.3 + image: mariadb:10.9.3 + restart: always + volumes: + - db_data:/var/lib/mysql + - ./_db:/docker-entrypoint-initdb.d + # - ./_log/mysql:/var/log/mysql + environment: + MYSQL_ALLOW_EMPTY_PASSWORD: 1 + MYSQL_DATABASE: wordpress + MYSQL_USER: wordpress + MYSQL_PASSWORD: wordpress + # command: > + # --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci + + # Ideas On Purpose's local WordPress development environment. + # Update image version with `docker run ideasonpurpose/wordpress init` + # Project info: https://github.com/ideasonpurpose/docker-wordpress-dev + wordpress: + depends_on: + - db + # image: ideasonpurpose/wordpress:dev + image: ideasonpurpose/wordpress:6.1.1 + restart: always + volumes: + - wp_data:/var/www/html + - ./wp-content/themes/${npm_package_name:-ioptheme}:/var/www/html/wp-content/themes/${npm_package_name:-ioptheme} + - ./wp-content/plugins:/var/www/html/wp-content/plugins + - ./wp-content/uploads:/var/www/html/wp-content/uploads + - ${DATA_MODEL_PLUGIN:-/tmp/null:/tmp/DATA_MODEL_PLUGIN} + - ${BLOCKS_PLUGIN:-/tmp/null:/tmp/BLOCKS_PLUGIN} + - ${IOP_DASHBOARD_PLUGIN:-/tmp/null:/tmp/IOP_DASHBOARD_PLUGIN} + - ./webpack/xdebug:/tmp/xdebug + expose: + - 9003 + environment: + DATA_MODEL_PLUGIN: + BLOCKS_PLUGIN: + WORDPRESS_DEBUG: + + # Apache will throw errors for any ulimit value below 8192 + # NOTE THAT THIS MIGHT BE MORE THAN THE SYSTEM OFFERS + ulimits: + nofile: 8192 + + # Ideas On Purpose's development toolchain + # Image from: https://hub.docker.com/r/ideasonpurpose/docker-build + tools: + # image: ideasonpurpose/docker-build:dev + image: ideasonpurpose/docker-build:0.13.2 + user: "${UID:-1000}:${GID:-1000}" + depends_on: + - wordpress + volumes: + - .:/usr/src/site/ + ports: + - "${npm_config_port:-8080}:8080" + environment: + PORT: ${npm_config_port:-8080} + entrypoint: npm run + command: start + + # Utility service which exposes wp-cli. Useful for testing pre-releases + # Update image version with `docker run ideasonpurpose/wordpress init` + # Project info: https://github.com/ideasonpurpose/docker-wordpress-dev + wp-cli: + depends_on: + - db + - wordpress + # image: ideasonpurpose/wordpress:dev + image: ideasonpurpose/wordpress:6.1.1 + profiles: ["utility"] + user: www-data + volumes: + - wp_data:/var/www/html + - ./wp-content/themes/${npm_package_name:-ioptheme}:/var/www/html/wp-content/themes/${npm_package_name:-ioptheme} + - ./wp-content/plugins:/var/www/html/wp-content/plugins + - ./wp-content/uploads:/var/www/html/wp-content/uploads + environment: + - WP_CLI_CACHE_DIR=/tmp/wp-cli + - WP_CLI_DISABLE_AUTO_CHECK_UPDATE=true + command: wp theme activate ${npm_package_name:-ioptheme} + + # Utility service for running composer commands. Useful for adding tools from packagist + # Image from: https://hub.docker.com/_/composer + composer: + image: composer:2.4.2 + profiles: ["utility"] + user: "${UID:-1000}:${GID:-1000}" + environment: + COMPOSER_HOME: /.composer volumes: - ./:/app + - ~/.composer:/.composer + command: install + + # Runs phpMyAdmin on port 8002 + # Image from: https://hub.docker.com/_/phpmyadmin + phpmyadmin: + image: phpmyadmin:5.2.0-apache + profiles: ["utility"] + ports: + - "${npm_config_port:-8002}:80" + depends_on: + - db + environment: + PMA_USER: root + PMA_PASSWORD: ~ + UPLOAD_LIMIT: 1G command: | - sh /app/bin/bump-wordpress.sh + bash -c 'echo && + echo -e "🛠 \033[33mStarting phpMyAdmin at\033[0m \033[36mhttp://localhost:${npm_config_port:-8002}\033[0m" && + echo && + /docker-entrypoint.sh apache2-foreground' - # Wrapper for the shfmt docker image - # Simplifies formatting scripts - # https://hub.docker.com/r/mvdan/shfmt - shfmt: - image: mvdan/shfmt:latest-alpine + # XDebug profile viewer at http://localhost:9001 (change port with --port=xxxx) + # Enable profiling by appending `?XDEBUG_PROFILE=1` to any request + # https://hub.docker.com/r/wodby/webgrind + webgrind: + image: wodby/webgrind:1.9 + profiles: ["utility"] user: "${UID:-1000}:${GID:-1000}" + ports: + - "${npm_config_port:-9004}:8080" + depends_on: + - wordpress volumes: - - ./bin:/mnt + - ./webpack/xdebug:/tmp + environment: + WEBGRIND_DEFAULT_TIMEZONE: America/New_York command: | - sh -c ' - echo && - echo -e "🧙‍ Formatting shell scripts in \033[36m./bin\033[0m with shfmt" && - shfmt -l -i 2 -w /mnt && - echo -e "✨ All done!" && - echo' + bash -c 'echo && + echo -e "🔍 \033[33mStarting WebGrind server at\033[0m \033[36mhttp://localhost:${npm_config_port:-9004}\033[0m" && + echo -e "⏱️ Profile any request by adding \033[1;35m?XDEBUG_PROFILE=1\033[0m to the url" && + echo && + php -S 0.0.0.0:8080 index.php + ' + + # Dumps the current database to _db/theme-YYYY-MM-DDTHHMMSS.sql.gz + # Image from: https://hub.docker.com/_/mariadb + mysqldump: + # image: mysql:5.7 + image: mariadb:10.9.3 + profiles: ["utility"] + depends_on: + - db + volumes: + - ./_db:/usr/src + environment: + MYSQL_DATABASE: wordpress + OWNER_GROUP: "${UID:-1000}:${GID:-1000}" + + # NOTE: The mysqldump file will include these CREATE/USE commands: + # CREATE DATABASE `wordpress`; + # USE `wordpress`; + # to drop those lines, remove "--databases" from the mysqldump command + command: | + bash -c 'for i in {1..10} + do echo -e "⏳ \033[33mWaiting for MySQL server...\033[0m" && + mysql -s -h db -e "exit" && break || sleep 3 + done && + sleep 2 && + echo -e "✔️ \033[32mConnected to MySQL\033[0m" && + export DUMPFILE="/usr/src/'${npm_package_name:-dumpfile}'-$$(date +%FT%H%M%S).sql" && + echo $${DUMPFILE} && + mysqldump -hdb --default-character-set=utf8mb4 --databases $${MYSQL_DATABASE} > "$${DUMPFILE}" && + gzip "$${DUMPFILE}" && + chown -R $${OWNER_GROUP} /usr/src && + echo "Successfully dumped MySQL database to \"$${DUMPFILE}.gz\""' + + # Reloads the database from the first found *.sql dumpfile in _db + # Image from: https://hub.docker.com/_/mariadb + mysql-reload: + # image: mysql:5.7 + image: mariadb:10.9.3 + profiles: ["utility"] + depends_on: + - db + volumes: + - ./_db:/usr/src/dumpfiles + environment: + MYSQL_DATABASE: wordpress + command: | + bash -c 'for i in {1..10} + do echo -e "⏳ \033[33mWaiting for MySQL server...\033[0m" && + mysql -s -h db -e "exit" && break || sleep 3 + done && + sleep 2 && + echo -e "✔️ \033[32mConnected to MySQL\033[0m" && + mysqladmin -hdb -v -f drop $${MYSQL_DATABASE} && + mysqladmin -hdb -v -f create $${MYSQL_DATABASE} && + echo Database \"$${MYSQL_DATABASE}\" created && + echo Reloading database from dumpfile && + mysql -hdb $${MYSQL_DATABASE} < $$(ls /usr/src/dumpfiles/*.sql | tail -n1)' + + # Activates the theme directly in the database + # Image from: https://hub.docker.com/_/mariadb + theme-activate: + # image: mysql:5.7 + image: mariadb:10.9.3 + profiles: ["utility"] + depends_on: + - db + volumes: + - ./_db:/usr/src/dumpfiles + environment: + MYSQL_DATABASE: wordpress + command: | + bash -c 'for i in {1..10} + do echo -e "⏳ \033[33mWaiting for MySQL server...\033[0m" && + mysql -s -h db -e "exit" && break || sleep 3 + done && + sleep 2 && + echo -e "✔️ \033[32mConnected to MySQL\033[0m" && + if [[ $$(mysql -s -h db $${MYSQL_DATABASE} -e "SHOW TABLES LIKE \"wp_options\"") ]] + then + echo -e "🎨 \033[36mActivating theme \033[0m\033[1m${npm_package_name:-ioptheme}\033[0m" + mysql -h db $${MYSQL_DATABASE} \ + -e "UPDATE wp_options \ + SET option_value = \"'${npm_package_name:-ioptheme}'\" \ + WHERE option_name in (\"template\",\"stylesheet\")" + else + echo -e "Unable to activate theme: \033[31m'\'wp_options\'' table does not exist.\033[0m" + echo "To recreate an existing site, copy the site'\''s MySQL dumpfile into _db" + echo If this project is starting from an empty database, you can ignore this messege. + fi' + + # Repairs permissions for known project files and directories + # Update image version with `docker run ideasonpurpose/wordpress init` + # Project info: https://github.com/ideasonpurpose/docker-wordpress-dev + repair-permissions: + # image: ideasonpurpose/wordpress:dev + image: ideasonpurpose/wordpress:6.1.1 + profiles: ["utility"] + volumes: + - .:/usr/src/site + environment: + OWNER_GROUP: "${UID:-1000}:${GID:-1000}" + entrypoint: /usr/local/bin/permissions.sh + + # Locally mirror remote sites by pulling the database, plugins and uploads + # Remote connections are configured in the project's .env file + # Run `docker run ideasonpurpose/wordpress init` to update the image version + # Project info: https://github.com/ideasonpurpose/docker-wordpress-dev + pull: + # image: ideasonpurpose/wordpress:dev + image: ideasonpurpose/wordpress:6.1.1 + profiles: ["utility"] + volumes: + - .:/usr/src/site + entrypoint: | + /usr/local/bin/pull.sh + environment: + OWNER_GROUP: "${UID:-1000}:${GID:-1000}" + SSH_KEY_PATH: + SSH_LOGIN: + SSH_USER: + SSH_PORT: + SSH_WP_CONTENT_DIR: + secrets: + - SSH_KEY + + # Run the init script from ideasonpurpose/wordpress:latest (intentionally "latest") + # to refresh tooling and bring the project inline with our boilerplate + # Image from: https://hub.docker.com/r/ideasonpurpose/docker-build + refresh: + # image: ideasonpurpose/wordpress:dev + image: ideasonpurpose/wordpress:latest + profiles: ["utility"] + volumes: + - .:/usr/src/site + command: init + +secrets: + SSH_KEY: + file: ${SSH_KEY_PATH:-~/.ssh/your_ssh_private_key} +volumes: + db_data: + name: ${npm_package_name:-ioptheme}_db + wp_data: + name: ${npm_package_name:-ioptheme}_wp diff --git a/ideasonpurpose.config.js b/ideasonpurpose.config.js new file mode 100644 index 0000000..75dee95 --- /dev/null +++ b/ideasonpurpose.config.js @@ -0,0 +1,25 @@ +const pkg = require("./package.json"); +const themeName = pkg.name || "ioptheme"; + +module.exports = { + src: `./wp-content/themes/${themeName}/src`, + dist: `./wp-content/themes/${themeName}/dist`, + entry: [ + "./js/main.js", + "./js/admin.js", + "./js/editor.js", + "./sass/main.scss", + ], + publicPath: `/wp-content/themes/${themeName}/dist/`, + + // enable polling on Windows (no iNotify events), does `isWindows` work? + // This property can be a boolean or an integer > 250 + // NOTE: iNotify events sometimes stop working on macOS, restart Docker to get them back + // usePolling: false, + + // OPTIONAL: Specify a Sass implementation, accepts `node-sass` and `sass` (Dart Sass, default) + // sass: "sass", + + // Set the webpack devtool option for sourcemaps + devtool: "source-map", +}; diff --git a/package.json b/package.json index 525fbef..077975c 100644 --- a/package.json +++ b/package.json @@ -4,15 +4,55 @@ "private": true, "description": "Docker-based local development environment for WordPress projects", "repository": "https://github.com/ideasonpurpose/docker-wordpress-dev", - "license": "MIT", "author": "Ideas On Purpose (https://www.ideasonpurpose.com/)", "contributors": [ "Joe Maller " ], "scripts": { + "_bootstrap:composer": "npm run composer:install", + "_bootstrap:npm": "npm ci", + "prebootstrap": "npm run permissions:repair", + "bootstrap": "npm run _bootstrap:npm && npm run _bootstrap:composer && npm run theme:activate", + "postbootstrap": "echo && echo ' 🚀' && echo ' ✨ All set!' && echo '🌏 Run this to get started:' && chalk bold yellow ' npm run start' && echo", + "build": "npm run docker:build", + "build:debug": "cross-env-shell \"docker compose run --rm -p $npm_package_config_port:8080 -p 9229:9229 tools build:debug\"", + "composer": "npm run composer:install", + "composer:install": "docker compose run --rm composer", + "composer:require": "docker compose run --rm composer require", + "composer:update": "docker compose run --rm composer update", + "docker:build": "docker compose run --rm tools build", + "docker:start": "docker compose run --rm --service-ports tools start", + "docker:stop": "docker compose down", + "log:wordpress": "npm run logs:wordpress", + "logs:wordpress": "docker compose exec wordpress tail -f /var/log/wordpress/debug.log", + "mysql": "docker compose exec db mysql wordpress", + "mysql:dump": "docker compose run --rm mysqldump", + "mysql:reload": "cross-env-shell \"docker compose run --rm mysql-reload\"", + "postmysql:reload": "npm run theme:activate", + "mysqldump": "npm run mysql:dump", + "permissions:repair": "docker compose run --rm repair-permissions", + "phpmyadmin": "docker compose run --rm --service-ports phpmyadmin", + "preproject:refresh": "docker compose pull refresh", + "project:refresh": "docker compose run --rm refresh", + "postproject:refresh": "docker compose pull wordpress", + "pull": "npm run pull:db && npm run pull:plugins && npm run pull:uploads", + "prepull:db": "npm run mysql:dump", + "pull:db": "docker compose run --rm pull database", + "postpull:db": "npm run mysql:reload", + "pull:plugins": "docker compose run --rm pull plugins", + "pull:uploads": "docker compose run --rm pull uploads", + "pull:uploads-all": "docker compose run --rm pull uploads all", + "start": "npm run docker:start || exit 0", + "poststart": "npm run stop", + "start:debug": "cross-env-shell \"docker compose run --rm -p $npm_package_config_port:8080 -p 9229:9229 tools start:debug\"", + "stop": "npm run docker:stop", + "theme:activate": "cross-env-shell \"docker compose run --rm theme-activate\"", + "version": "version-everything && git add -u", + "postversion": "npm run build", + "webgrind": "docker compose run --rm --service-ports webgrind", + "wp-cli": "cross-env-shell \"docker compose run --rm wp-cli\"", "bump": "npm run wordpress:bump", "shfmt": "docker compose run --rm shfmt", - "version": "version-everything && auto-changelog && git add -u", "wordpress:bump": "docker-compose run --rm bump-wp" }, "prettier": { @@ -49,24 +89,33 @@ }, "devDependencies": { "@prettier/plugin-php": "^0.19.2", - "auto-changelog": "^2.4.0", + "chalk-cli": "^5.0.0", + "cross-env": "^7.0.3", + "postcss-scss": "^4.0.5", "prettier": "^2.7.1", - "version-everything": "^0.9.1" - }, - "auto-changelog": { - "package": true, - "backfillLimit": 7 + "sort-package-json": "^2.0.0", + "stylelint": "^14.14.0", + "stylelint-order": "^5.0.0", + "stylelint-prettier": "^2.0.0", + "version-everything": "^0.9.1", + "auto-changelog": "^2.4.0" }, "version-everything": { - "prefix": "ideasonpurpose/wordpress:", "files": [ + "Dockerfile", + "README.md", "bin/docker-entrypoint-iop.sh", "bin/getting-started.sh", "bin/permissions.sh", "bin/pull.sh", "bin/wp-init.sh", - "Dockerfile", - "README.md" - ] + "wp-content/themes/docker-wordpress-dev/style.css" + ], + "prefix": "ideasonpurpose/wordpress:" + }, + "license": "MIT", + "auto-changelog": { + "package": true, + "backfillLimit": 7 } } diff --git a/wp-content/themes/docker-wordpress-dev/404.php b/wp-content/themes/docker-wordpress-dev/404.php new file mode 100644 index 0000000..fb81458 --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/404.php @@ -0,0 +1,68 @@ + -1, + 'post_type' => 'page', + 'meta_query' => [ + [ + 'key' => '_wp_page_template', + 'value' => '404.php', + ], + ], +]; +$pageQuery = new \WP_Query($args); + +/** + * Use $pageQuery or a synthetic $post object to populate page title and content + */ +if ($pageQuery->have_posts()) { + /** + * Some hosts including WP Engine disable MySQL's `ORDER BY RAND()` option since it + * can be extremely burdensome with larger tables. + * @link https://wpengine.com/support/about-order-by-rand/ + * + * Instead, we just shuffle the returned collection of posts then use the first one. + */ + shuffle($pageQuery->posts); // some hosts disable MySQL rand() queries + $pageQuery->the_post(); +} else { + $fakePost = new \stdClass(); + $fakePost->ID = 0; + $fakePost->post_title = $pageTitle; + $fakePost->post_content = $pageContent; + $fakePost->filter = 'raw'; + + $post = new WP_Post($fakePost); + setup_postdata($post); +} + +get_header(); + +get_template_part('template-parts/page'); + +get_footer(); diff --git a/wp-content/themes/docker-wordpress-dev/archive.php b/wp-content/themes/docker-wordpress-dev/archive.php new file mode 100644 index 0000000..825dfe8 --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/archive.php @@ -0,0 +1,13 @@ +post_type); + +get_footer(); diff --git a/wp-content/themes/docker-wordpress-dev/footer.php b/wp-content/themes/docker-wordpress-dev/footer.php new file mode 100644 index 0000000..3c44609 --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/footer.php @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/wp-content/themes/docker-wordpress-dev/front-page.php b/wp-content/themes/docker-wordpress-dev/front-page.php new file mode 100644 index 0000000..ea2cbef --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/front-page.php @@ -0,0 +1,20 @@ + + + + +
+
+ +
+
+ + + +get('Version')); +} + +new ThemeInit(); + +/** + * Load Scripts from the webpack generated dist/dependency-manifest.json file + */ +new ThemeInit\Manifest(); + +/** + * Initialize IdeasOnPurpose\WP\GoogleAnalytics + */ +$client_ga_id = 'UA-2565788-3'; +$iop_dev_ga = 'UA-2565788-3'; +new WP\GoogleAnalytics($client_ga_id, $iop_dev_ga); + +/** + * Initialize our SVG Library for all SVGs in ./dist/images/svg + */ +new WP\SVG(__DIR__ . '/dist/images/svg'); + +/** + * Register Custom Widgets + */ +// new Widgets\NAME(); + +/** + * Register Custom Shortcodes + */ +// new Shortcodes\NAME(); + +/** + * Enable TaxonomyCountColumn so post-counts in Taxonomy listings are accurate to the displayed post_type + */ +new WP\TaxonomyCountColumn(); + +/** + * Add Search redirect and length limiter + */ +new WP\Search(); + +/** + * Enable assorted WordPress features + */ +add_action('after_setup_theme', function () { + // Add excerpts to pages + add_post_type_support('page', 'excerpt'); + + // Theme features + add_theme_support('post-thumbnails'); + add_theme_support('title-tag'); + add_theme_support('html5', ['search-form', 'gallery', 'caption']); + + // Gutenberg settings + add_theme_support('editor-styles'); // https://developer.wordpress.org/block-editor/developers/themes/theme-support/#editor-styles + add_theme_support('align-wide'); // https://wordpress.org/gutenberg/handbook/extensibility/theme-support/#wide-alignment + add_theme_support('disable-custom-colors'); // https: //wordpress.org/gutenberg/handbook/extensibility/theme-support/#disabling-custom-colors-in-block-color-palettes +}); + +/** + * Register Sidebars and disable default widgets + */ +add_action('widgets_init', function () { + // register_sidebars(); + + unregister_widget('WP_Nav_Menu_Widget'); + unregister_widget('WP_Widget_Archives'); + unregister_widget('WP_Widget_Calendar'); + unregister_widget('WP_Widget_Categories'); + unregister_widget('WP_Widget_Custom_HTML'); + unregister_widget('WP_Widget_Links'); + unregister_widget('WP_Widget_Media_Audio'); + unregister_widget('WP_Widget_Media_Gallery'); + unregister_widget('WP_Widget_Media_Video'); + unregister_widget('WP_Widget_Meta'); + unregister_widget('WP_Widget_Pages'); + unregister_widget('WP_Widget_Recent_Comments'); + unregister_widget('WP_Widget_Recent_Posts'); + unregister_widget('WP_Widget_RSS'); + unregister_widget('WP_Widget_Search'); + unregister_widget('WP_Widget_Tag_Cloud'); +}); + +/** + * Register Custom Menus + */ +add_action('after_setup_theme', function () { + register_nav_menus([ + 'menu-main' => 'Main Menu', + 'menu-footer' => 'Footer Menu', + ]); +}); + +/** + * Define additional image sizes + * Image sizes are generated from an array of size objects + * Each size maps like this: + * name: (string) Internal image size name (slug) + * dims: (array) Array of two integers: [w, h] + * display: (string) Show in WP Menus using this name + * crop: (array|boolean) if not false, hard-crop the resulting image + * + * If display_name is specified, the image size will appear in authoring menus + */ +$image_sizes = [ + ['name' => '1k', 'dims' => [1024, 1024], 'display' => '1k - 1024px'], + ['name' => '2k', 'dims' => [2048, 2048], 'display' => '2k - 2048px'], + ['name' => '4k', 'dims' => [3840, 3840], 'display' => '4k - 3840px'], +]; +new ImageSize($image_sizes); + +/** + * ACF Options Pages + */ +if (function_exists('acf_add_options_page')) { + $acf_options = acf_add_options_page([ + 'page_title' => 'Global Theme Options', + 'menu_title' => 'Site Options', + 'position' => 35, + 'icon_url' => + 'data:image/svg+xml;base64,' . + base64_encode( + ' + + ', + ), + ]); + + acf_add_options_sub_page([ + 'page_title' => 'Header Options', + 'menu_title' => 'Header', + 'parent_slug' => $acf_options['menu_slug'], + ]); + + + acf_add_options_sub_page([ + 'page_title' => 'Footer Options', + 'menu_title' => 'Footer', + 'parent_slug' => $acf_options['menu_slug'], + ]); +} + diff --git a/wp-content/themes/docker-wordpress-dev/header.php b/wp-content/themes/docker-wordpress-dev/header.php new file mode 100644 index 0000000..0b9e8ee --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/header.php @@ -0,0 +1,14 @@ + + class="no-js"> + + + + + + + + + +> + +post_name); +} + +get_footer(); diff --git a/wp-content/themes/docker-wordpress-dev/lib/ImageSize.php b/wp-content/themes/docker-wordpress-dev/lib/ImageSize.php new file mode 100644 index 0000000..9480490 --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/lib/ImageSize.php @@ -0,0 +1,64 @@ +sizes = $image_sizes; + $this->names = []; + + add_action('after_setup_theme', [$this, 'addSizes']); + add_filter('image_size_names_choose', [$this, 'addToMenu']); + } + + public function addSizes() + { + foreach ($this->sizes as $img) { + $crop = false; + if (isset($img['crop'])) { + $crop = (is_array($img['crop'])) ? $img['crop'] : boolval($img['crop']); + } + add_image_size($img['name'], $img['dims'][0], $img['dims'][1], $crop); + if (isset($img['display'])) { + $this->names[$img['name']] = $img['display']; + } + } + } + + /** + * Adds our custom sizes to the WordPress image size menu + * @param array $sizes Array ofimage sizes passed from the iamge_size_names_choose filter + */ + public function addToMenu($sizes) + { + return array_merge($sizes, $this->names); + } + + /** + * Add a filter to remove $sizes from the WordPress thumbnail generation + * @param array $sizes An associative array of image sizes. + */ + public static function removeSizes($sizes) + { + add_filter('intermediate_image_sizes_advanced', function ($defaultSizes) use ($sizes) { + foreach ($sizes as $size) { + unset($defaultSizes[$size]); + } + return $defaultSizes; + }); + } +} diff --git a/wp-content/themes/docker-wordpress-dev/lib/WP/TaxonomyCountColumn.php b/wp-content/themes/docker-wordpress-dev/lib/WP/TaxonomyCountColumn.php new file mode 100644 index 0000000..f25e810 --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/lib/WP/TaxonomyCountColumn.php @@ -0,0 +1,173 @@ +object_type); + $guid = sha1(json_encode([$termId, $taxName, $post_types])); + + if (get_transient($guid) === false) { + foreach ($post_types as $type) { + $args = [ + 'posts_per_page' => -1, + 'post_type' => $type, + 'tax_query' => [['taxonomy' => $taxName, 'terms' => $termId]], + ]; + $countQuery = new \WP_Query($args); + + update_term_meta($termId, "count_{$type}", $countQuery->found_posts); + } + set_transient($guid, true, 60); + } + } + + /** + * Adds a meta_query exposing the count_{$post_type} field to the Term Query so there are values to + * order by. Since the query_var has the same name as the termmeta field, we can rely on WordPress + * to sanitize the input. + * + * The query returns whether a number or null depending on whether the key exists. + * + * ref: https://core.trac.wordpress.org/ticket/40335 + * ref: https://stackoverflow.com/a/47224730/503463 + */ + public function orderByMeta(\WP_Term_Query $query) + { + $orderby = $query->query_vars['orderby'] ?? false; + + if (preg_match('/^count_[-a-z]+/', $orderby)) { + $meta_query = new \WP_Meta_Query([ + $orderby => [ + 'relation' => 'OR', + ['key' => $orderby, 'type' => 'NUMERIC'], + ['key' => $orderby, 'compare' => 'NOT EXISTS'], + ], + ]); + $query->meta_query = $meta_query; + } + } + + /** + * Adds a sortable Count column and 'Refresh Counts' bulk action to the Taxonomy admin interface + */ + public function setupTaxonomyAdmin() + { + $taxonomies = get_taxonomies(['public' => true], 'names'); + + foreach ($taxonomies as $taxonomy) { + add_filter("manage_edit-{$taxonomy}_columns", [$this, 'addCountColumn'], 100); + add_filter("manage_edit-{$taxonomy}_sortable_columns", [$this, 'makeCountColumnSortable'], 100); + add_action("manage_{$taxonomy}_custom_column", [$this, 'renderCountColumn'], 100, 3); + + add_filter("bulk_actions-edit-{$taxonomy}", [$this, 'addResetBulkAction']); + add_filter("handle_bulk_actions-edit-{$taxonomy}", [$this, 'bulkActionHandler'], 100, 3); + } + add_action('admin_notices', [$this, 'addCountUpdateNotice']); + add_action('admin_enqueue_scripts', [$this, 'adminCountColumnStyles'], 100); + } + + public function addCountColumn($cols) + { + $newCols = $cols; + unset($newCols['posts']); + $newCols['post_type_count'] = 'Count'; + return $newCols; + } + + public function makeCountColumnSortable($cols) + { + global $post_type; + $newCols = $cols; + $newCols['post_type_count'] = "count_$post_type"; + return $newCols; + } + + public function renderCountColumn($content, $name, $id) + { + $output = $content; + if ($name === 'post_type_count') { + $screen = get_current_screen(); + + $term = get_term($id); + $taxonomy = get_taxonomy($term->taxonomy); + $count = get_term_meta($id, "count_{$screen->post_type}", true); + + $viewHref = add_query_arg( + [$taxonomy->query_var => $term->slug, 'post_type' => $screen->post_type], + 'edit.php', + ); + + $output .= strlen($count) ? sprintf('%s', $viewHref, $count) : '--'; + } + return $output; + } + + public function addResetBulkAction($actions) + { + $newActions = ['reset_post_type_counts' => 'Refresh Counts']; + return array_merge($newActions, $actions); + } + + public function bulkActionHandler($redirect, $action, $ids) + { + $screen = get_current_screen(); + + if (strlen($screen->taxonomy)) { + if (count($ids)) { + wp_update_term_count_now($ids, $screen->taxonomy); + $redirect = add_query_arg(['post_type_count_updated' => count($ids)], $redirect); + } + } + return $redirect; + } + /** + * Note: This method outputs an update message into the admin + */ + public function addCountUpdateNotice() + { + if (!empty($_REQUEST['post_type_count_updated'])) { + $screen = get_current_screen(); + $taxonomy = get_taxonomy($screen->taxonomy); + $term = strtolower($taxonomy->labels->singular_name); + $terms = strtolower($taxonomy->labels->name); + $count = intval($_REQUEST['post_type_count_updated']); + + $msg = _n("Updated count for {$count} {$term}.", "Updated counts for {$count} {$terms}.", $count); + printf('

%s

', $msg); + } + } + + public function adminCountColumnStyles() + { + $css = " + .column-post_type_count { + width: 74px; + text-align: center; + } + "; + wp_add_inline_style('wp-admin', $css); + } +} diff --git a/wp-content/themes/docker-wordpress-dev/page.php b/wp-content/themes/docker-wordpress-dev/page.php new file mode 100644 index 0000000..a7933e9 --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/page.php @@ -0,0 +1,18 @@ + true]; + +get_header(); + +while (have_posts()) { + the_post(); + + get_template_part('template-parts/page', $post->post_name, $args); +} + +get_footer(); diff --git a/wp-content/themes/docker-wordpress-dev/screenshot.png b/wp-content/themes/docker-wordpress-dev/screenshot.png new file mode 100644 index 0000000..238df6a Binary files /dev/null and b/wp-content/themes/docker-wordpress-dev/screenshot.png differ diff --git a/wp-content/themes/docker-wordpress-dev/singular.php b/wp-content/themes/docker-wordpress-dev/singular.php new file mode 100644 index 0000000..1692251 --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/singular.php @@ -0,0 +1,17 @@ +post_type); +} + +get_footer(); diff --git a/wp-content/themes/docker-wordpress-dev/src/acf-json/group_637d587898a29.json b/wp-content/themes/docker-wordpress-dev/src/acf-json/group_637d587898a29.json new file mode 100644 index 0000000..ec49e42 --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/src/acf-json/group_637d587898a29.json @@ -0,0 +1,85 @@ +{ + "key": "group_637d587898a29", + "title": "Cookies and Copyright", + "fields": [ + { + "key": "field_60ec673efed85", + "label": "Cookie Notice", + "name": "cookie_notice", + "aria-label": "", + "type": "textarea", + "instructions": "Basic HTML is allowed.", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "75", + "class": "", + "id": "" + }, + "default_value": "This site uses cookies for analytics. By continuing to browse this site, you agree to this use.", + "placeholder": "This site uses cookies for analytics. By continuing to browse this site, you agree to this use.", + "maxlength": "", + "rows": 3, + "new_lines": "wpautop" + }, + { + "key": "field_60ec6964fc0c2", + "label": "Cookie Accept Button", + "name": "cookie_accept_button", + "aria-label": "", + "type": "text", + "instructions": "", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "25", + "class": "", + "id": "" + }, + "default_value": "Agree", + "placeholder": "Agree", + "prepend": "", + "append": "", + "maxlength": "" + }, + { + "key": "field_6082c24c9b58e", + "label": "Copyright Line", + "name": "copyright", + "aria-label": "", + "type": "text", + "instructions": "Text appearing after the copyright symbol. Use the token %YEAR%<\/code> to insert the current year.", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "", + "class": "", + "id": "" + }, + "default_value": "CLIENT NAME - %YEAR% All Rights Reserved", + "placeholder": "CLIENT NAME - %YEAR% All Rights Reserved", + "prepend": "©", + "append": "", + "maxlength": "" + } + ], + "location": [ + [ + { + "param": "options_page", + "operator": "==", + "value": "acf-options-footer" + } + ] + ], + "menu_order": 11, + "position": "normal", + "style": "default", + "label_placement": "top", + "instruction_placement": "label", + "hide_on_screen": "", + "active": true, + "description": "", + "show_in_rest": 0, + "modified": 1669159125 +} \ No newline at end of file diff --git a/wp-content/themes/docker-wordpress-dev/src/favicon/favicon.ico b/wp-content/themes/docker-wordpress-dev/src/favicon/favicon.ico new file mode 100644 index 0000000..49ecd52 Binary files /dev/null and b/wp-content/themes/docker-wordpress-dev/src/favicon/favicon.ico differ diff --git a/wp-content/themes/docker-wordpress-dev/src/images/svg/email.svg b/wp-content/themes/docker-wordpress-dev/src/images/svg/email.svg new file mode 100644 index 0000000..3812a6b --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/src/images/svg/email.svg @@ -0,0 +1 @@ + diff --git a/wp-content/themes/docker-wordpress-dev/src/images/svg/facebook.svg b/wp-content/themes/docker-wordpress-dev/src/images/svg/facebook.svg new file mode 100644 index 0000000..28384d7 --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/src/images/svg/facebook.svg @@ -0,0 +1 @@ + diff --git a/wp-content/themes/docker-wordpress-dev/src/images/svg/instagram.svg b/wp-content/themes/docker-wordpress-dev/src/images/svg/instagram.svg new file mode 100644 index 0000000..48f141e --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/src/images/svg/instagram.svg @@ -0,0 +1 @@ + diff --git a/wp-content/themes/docker-wordpress-dev/src/images/svg/linkedin.svg b/wp-content/themes/docker-wordpress-dev/src/images/svg/linkedin.svg new file mode 100644 index 0000000..555b211 --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/src/images/svg/linkedin.svg @@ -0,0 +1 @@ + diff --git a/wp-content/themes/docker-wordpress-dev/src/images/svg/pinterest.svg b/wp-content/themes/docker-wordpress-dev/src/images/svg/pinterest.svg new file mode 100644 index 0000000..543aad1 --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/src/images/svg/pinterest.svg @@ -0,0 +1 @@ + diff --git a/wp-content/themes/docker-wordpress-dev/src/images/svg/site-logo.svg b/wp-content/themes/docker-wordpress-dev/src/images/svg/site-logo.svg new file mode 100644 index 0000000..22db2a2 --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/src/images/svg/site-logo.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/wp-content/themes/docker-wordpress-dev/src/images/svg/twitter.svg b/wp-content/themes/docker-wordpress-dev/src/images/svg/twitter.svg new file mode 100644 index 0000000..5cce3d4 --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/src/images/svg/twitter.svg @@ -0,0 +1 @@ + diff --git a/wp-content/themes/docker-wordpress-dev/src/images/svg/vimeo.svg b/wp-content/themes/docker-wordpress-dev/src/images/svg/vimeo.svg new file mode 100644 index 0000000..f241af3 --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/src/images/svg/vimeo.svg @@ -0,0 +1 @@ + diff --git a/wp-content/themes/docker-wordpress-dev/src/images/svg/youtube.svg b/wp-content/themes/docker-wordpress-dev/src/images/svg/youtube.svg new file mode 100644 index 0000000..64870a7 --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/src/images/svg/youtube.svg @@ -0,0 +1 @@ + diff --git a/wp-content/themes/docker-wordpress-dev/src/js/admin.js b/wp-content/themes/docker-wordpress-dev/src/js/admin.js new file mode 100644 index 0000000..7edc661 --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/src/js/admin.js @@ -0,0 +1,3 @@ +require('../sass/admin.scss'); + +console.log("admin.js"); diff --git a/wp-content/themes/docker-wordpress-dev/src/js/editor.js b/wp-content/themes/docker-wordpress-dev/src/js/editor.js new file mode 100644 index 0000000..968ab53 --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/src/js/editor.js @@ -0,0 +1,12 @@ +/* global wp:true */ + +require("../sass/editor.scss"); + +console.log("editor.js"); + +/* +wp.blocks.registerBlockStyle("core/paragraph", { + name: "intro", + label: "Intro" +}); +*/ diff --git a/wp-content/themes/docker-wordpress-dev/src/js/main.js b/wp-content/themes/docker-wordpress-dev/src/js/main.js new file mode 100644 index 0000000..fbe8f35 --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/src/js/main.js @@ -0,0 +1,9 @@ +/** + * If we import main.scss here, remove it from the entry array in ideasonpurpose.config.js + */ + +// require('../sass/main.scss') +require('../js/modules/share.js'); +require('../js/modules/menu.js'); + +console.log('JS Loaded'); diff --git a/wp-content/themes/docker-wordpress-dev/src/js/modules/menu.js b/wp-content/themes/docker-wordpress-dev/src/js/modules/menu.js new file mode 100644 index 0000000..d846c9f --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/src/js/modules/menu.js @@ -0,0 +1,48 @@ +import $ from "jquery"; + +const $html = $('html'); +const $body = $('body'); +const $window = $(window); +const menuOpenClass = 'menu-open'; +const menuTriggerClass = '.js-toggle-menu'; + + +/** + * Toggle Menu + * + * Toggles the menuOpenClass on + * + * Prevents from being scrolled + * while the hamburger menu is open + */ + +function toggleMenu() { + var top = $window.scrollTop(); + + // Pin the body and prevent scrolling while the menu is open + if (!$body.is('.' + menuOpenClass)) { + $body.css('top', -1 * top + 'px').attr('data-scroll', top); + } + + $body.toggleClass(menuOpenClass); + + // Scroll the body back to its initial position + if (!$body.is('.' + menuOpenClass)) { + $('body,html').scrollTop($('body').attr('data-scroll')); + } +} + +/** + * Hamburger button class + */ + +$(menuTriggerClass).on('click', toggleMenu); + + +/** + * ESC key toggles the menu + */ + +$html.on('keyup', function (e) { + e.keyCode === 27 && toggleMenu(); +}); diff --git a/wp-content/themes/docker-wordpress-dev/src/js/modules/share.js b/wp-content/themes/docker-wordpress-dev/src/js/modules/share.js new file mode 100644 index 0000000..68b8910 --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/src/js/modules/share.js @@ -0,0 +1,16 @@ +import $ from "jquery"; + +const shareClass = ".js--share"; + +// Share buttons +$("body").on("click", shareClass, function (e) { + e.preventDefault(); + + window + .open( + $(this).attr("href"), + "Share", + "height=500,width=900,top=150,left=150" + ) + .focus(); +}); diff --git a/wp-content/themes/docker-wordpress-dev/src/sass/admin.scss b/wp-content/themes/docker-wordpress-dev/src/sass/admin.scss new file mode 100644 index 0000000..17eb3f1 --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/src/sass/admin.scss @@ -0,0 +1,3 @@ +/** + * Admin styles for everything but the editor + */ diff --git a/wp-content/themes/docker-wordpress-dev/src/sass/base/_base.scss b/wp-content/themes/docker-wordpress-dev/src/sass/base/_base.scss new file mode 100644 index 0000000..0f87339 --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/src/sass/base/_base.scss @@ -0,0 +1,25 @@ +/** + * Base + */ + +html { + scroll-behavior: smooth; // no-js jumplink smooth scroll transition + + // Prevent horizontal scrolling + width: 100%; + overflow-x: hidden; +} + +body { + @extend .sans; + + // Prevent scrolling while the hamburger menu is open + @include mq(lg, "max") { + &.menu-open { + position: fixed; + left: 0; + right: 0; + overflow: hidden; + } + } +} diff --git a/wp-content/themes/docker-wordpress-dev/src/sass/base/_fonts.scss b/wp-content/themes/docker-wordpress-dev/src/sass/base/_fonts.scss new file mode 100644 index 0000000..2000de0 --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/src/sass/base/_fonts.scss @@ -0,0 +1,12 @@ +/** + * Fonts + * + * Should always default to a system font stack + * https://css-tricks.com/snippets/css/system-font-stack/ + */ + +// @import url("https://fonts.googleapis.com/css2?family=Nunito:ital,wght@0,200;0,300;0,400;0,600;0,700;0,800;0,900;1,200;1,300;1,400;1,600;1,700;1,800;1,900&display=swap"); + +.sans { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; +} diff --git a/wp-content/themes/docker-wordpress-dev/src/sass/base/_reset.scss b/wp-content/themes/docker-wordpress-dev/src/sass/base/_reset.scss new file mode 100644 index 0000000..53993ed --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/src/sass/base/_reset.scss @@ -0,0 +1,88 @@ +* { + box-sizing: border-box; +} + +main { + display: block; +} + +html, body, div, span, applet, object, iframe, p, blockquote, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video, button, pre { + margin: 0; + padding: 0; + border: 0; + background: 0; + font-size: 100%; + font-family: inherit; + vertical-align: baseline; +} + +h1, h2, h3, h4, h5, h6 { + margin: 0; +} + +/* HTML5 display-role reset for older browsers */ +article, aside, details, figcaption, figure, +footer, header, hgroup, menu, nav, section { + display: block; +} + +html { + line-height: 1.15; + -webkit-text-size-adjust: 100%; +} + +a { + color: inherit; + text-decoration: none; + background-color: transparent; +} + +svg { + fill: currentColor; +} + +ol, ul { + list-style: none; +} + +img { + border-style: none; + max-width: 100%; +} + +blockquote, q { + quotes: none; +} + +blockquote:before, blockquote:after, +q:before, q:after { + content: ''; + content: none; +} + +table { + border-collapse: collapse; + border-spacing: 0; +} + +button, +[type="button"], +[type="reset"], +[type="submit"] { + -webkit-appearance: button; +} + +[hidden] { + display: none; +} diff --git a/wp-content/themes/docker-wordpress-dev/src/sass/base/_typography.scss b/wp-content/themes/docker-wordpress-dev/src/sass/base/_typography.scss new file mode 100644 index 0000000..bf26faf --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/src/sass/base/_typography.scss @@ -0,0 +1,13 @@ +/** + * Typography + */ + +.type- { + &intro { + font-size: 1em; + } +} + +.is-style-intro { + @extend .type-intro; +} diff --git a/wp-content/themes/docker-wordpress-dev/src/sass/base/_utilities.scss b/wp-content/themes/docker-wordpress-dev/src/sass/base/_utilities.scss new file mode 100644 index 0000000..ad6a97c --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/src/sass/base/_utilities.scss @@ -0,0 +1,40 @@ +/** + * Utility classes + */ + +.a11y { + clip: rect(1px, 1px, 1px, 1px); + clip-path: inset(1px); + border: 0; + padding: 0; + width: 1px; + height: 1px; + margin: -1px; + white-space: nowrap; + overflow: hidden; + display: block; + position: absolute; + background: #000; + color: #fff; +} + +/* + This class can be @extend'ed so that an item + extends its background color to the sides of the screen +*/ + +.extends-bg { + position: relative; + z-index: 1; + + &::before { + content: ""; + position: absolute; + background: inherit; + left: -100vw; + right: -100vw; + top: 0; + bottom: 0; + z-index: -1; + } +} diff --git a/wp-content/themes/docker-wordpress-dev/src/sass/editor.scss b/wp-content/themes/docker-wordpress-dev/src/sass/editor.scss new file mode 100644 index 0000000..0246585 --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/src/sass/editor.scss @@ -0,0 +1,3 @@ +/** + * Styles for the WordPres block editor + */ diff --git a/wp-content/themes/docker-wordpress-dev/src/sass/main.scss b/wp-content/themes/docker-wordpress-dev/src/sass/main.scss new file mode 100644 index 0000000..c509139 --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/src/sass/main.scss @@ -0,0 +1,38 @@ +@charset "utf-8"; + +@import + // Variables + "variables/colors", + "variables/layout", + + // Mixins + "mixins/breakpoint", + "mixins/columns", + "mixins/rows", + + // Base styles + "base/reset", + "base/fonts", + "base/typography", + "base/utilities", + "base/base", + + // WP specific + "wp/wp-logged-in", + + // Modules + "modules/main", + "modules/wrapper", + "modules/alignfull", + "modules/header", + "modules/header__menu", + "modules/header__menu-button", + "modules/sharebar", + "modules/footer", + "modules/editorial" + + // WP Editor colors. Do not move these styles elsewhere + // MUST be placed at the end of the import list. + // "wp/wp-editor-colors" +; + diff --git a/wp-content/themes/docker-wordpress-dev/src/sass/mixins/_breakpoint.scss b/wp-content/themes/docker-wordpress-dev/src/sass/mixins/_breakpoint.scss new file mode 100644 index 0000000..ad677b5 --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/src/sass/mixins/_breakpoint.scss @@ -0,0 +1,27 @@ +/* + * Media query shorthand, to be used with variables from $site-breakpoints + * Defaults to min-width + * + * Usage: @include mq(lg, min/max(optional) ) { ... } + */ + +@function breakpoint-min($name, $breakpoints: $site-breakpoints) { + $min: map-get($breakpoints, $name); + @return max(0, $min); +} + +@mixin mq($name, $type: min) { + $min: breakpoint-min($name); + + @if ($min > 0) { + @if ($type == max) { + $min: $min - 1px; + } + + @media ( #{$type}-width: $min ) { + @content; + } + } @else { + @content; + } +} diff --git a/wp-content/themes/docker-wordpress-dev/src/sass/mixins/_columns.scss b/wp-content/themes/docker-wordpress-dev/src/sass/mixins/_columns.scss new file mode 100644 index 0000000..20a39ac --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/src/sass/mixins/_columns.scss @@ -0,0 +1,46 @@ +@use "sass:math"; + +/* + * Generate grid columns + * + */ + +@mixin grid-col-padding($gutter) { + padding-left: $gutter * 0.5; + padding-right: $gutter * 0.5; + /* + * Default columns align left with smallest width + */ + flex: 0 0 auto; +} + +@mixin grid-col-flex($size) { + width: percentage(math.div($size, $column-count)); + max-width: percentage(math.div($size, $column-count)); + flex: 0 0 percentage(math.div($size, $column-count)); +} + +// generate paddings +@each $breakpoint, $gutter in $column-gutter { + @include mq($breakpoint) { + #{$row-class} > * { + @include grid-col-padding($gutter); + } + } +} + +// generate flex styles +@each $breakpoint, $size in $site-breakpoints { + @include mq($breakpoint) { + $index: index($site-breakpoints, $breakpoint $size); + + @for $i from 1 through $column-count { + // ignore first breakpoint class, e.g. .col instead of .col-sm + $className: if($index > 1, #{$column-class}-#{$breakpoint}-#{$i}, #{$column-class}-#{$i}); + + #{$className} { + @include grid-col-flex($i); + } + } + } +} diff --git a/wp-content/themes/docker-wordpress-dev/src/sass/mixins/_rows.scss b/wp-content/themes/docker-wordpress-dev/src/sass/mixins/_rows.scss new file mode 100644 index 0000000..78d12bb --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/src/sass/mixins/_rows.scss @@ -0,0 +1,27 @@ +/* + * Generate grid rows + * + * $row-class defines the row classname. E.G. .row + * $column-gutter sets the negative margins for each defined breakpoint. + * + */ + +@mixin row-gutters($gutter) { + margin-left: ($gutter * -0.5); + margin-right: ($gutter * -0.5); +} + +@mixin row() { + display: flex; + flex-wrap: wrap; + + @each $breakpoint, $gutter in $column-gutter { + @include mq($breakpoint) { + @include row-gutters($gutter); + } + } +} + +#{$row-class} { + @include row(); +} diff --git a/wp-content/themes/docker-wordpress-dev/src/sass/modules/_alignfull.scss b/wp-content/themes/docker-wordpress-dev/src/sass/modules/_alignfull.scss new file mode 100644 index 0000000..8362796 --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/src/sass/modules/_alignfull.scss @@ -0,0 +1,6 @@ +.alignfull { + width: auto; + margin-left: calc(-50vw + 50%); + margin-right: calc(-50vw + 50%); +} + diff --git a/wp-content/themes/docker-wordpress-dev/src/sass/modules/_editorial.scss b/wp-content/themes/docker-wordpress-dev/src/sass/modules/_editorial.scss new file mode 100644 index 0000000..9ba9e07 --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/src/sass/modules/_editorial.scss @@ -0,0 +1,13 @@ +/** + * Editorial + */ + +.editorial { + > :first-child { + margin-top: 0; + } + + > :last-child { + margin-bottom: 0; + } +} diff --git a/wp-content/themes/docker-wordpress-dev/src/sass/modules/_footer.scss b/wp-content/themes/docker-wordpress-dev/src/sass/modules/_footer.scss new file mode 100644 index 0000000..9c5fe5b --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/src/sass/modules/_footer.scss @@ -0,0 +1,6 @@ +/** + * Footer + */ + +.footer { +} diff --git a/wp-content/themes/docker-wordpress-dev/src/sass/modules/_header.scss b/wp-content/themes/docker-wordpress-dev/src/sass/modules/_header.scss new file mode 100644 index 0000000..fb50f61 --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/src/sass/modules/_header.scss @@ -0,0 +1,47 @@ +/** + * Header + */ + +.header { + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 1000; + background: $white; + + .wrapper, + &__bar { + @each $breakpoint, $height in $header-heights { + @include mq($breakpoint) { + height: $height; + } + } + } + + .wrapper, + &__bar { + display: flex; + align-items: center; + justify-content: space-between; + } + + &__logo { + display: flex; + align-items: center; + height: 100%; + + svg { + max-height: 100%; + } + } + + @include mq(lg, "max") { + // Places the header bar above the opened hamburger menu + &__bar { + width: 100%; + position: relative; + z-index: 10; + } + } +} diff --git a/wp-content/themes/docker-wordpress-dev/src/sass/modules/_header__menu-button.scss b/wp-content/themes/docker-wordpress-dev/src/sass/modules/_header__menu-button.scss new file mode 100644 index 0000000..122088b --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/src/sass/modules/_header__menu-button.scss @@ -0,0 +1,93 @@ +@use "sass:math"; + +/** + * Header Menu button config + */ + +$menuBtnSize: 60px; +$menuBtnLineWidth: 28px; +$menuBtnLineThickness: 3px; +$menuBtnLinesSpacing: 6px; +$menuBtnAnimationDuration: 250ms; + +/** + * Menu button styles + */ + +.header__menu-button { + //color: $indigo; + + position: relative; + width: $menuBtnSize; + height: $menuBtnSize; + vertical-align: middle; + margin-left: 25px; + margin-right: math.div(-($menuBtnSize - $menuBtnLineWidth), 2); + background: transparent; + cursor: pointer; + outline: 0; + + &-lines { + margin: ceil(math.div(-$menuBtnLineThickness, 2)) auto 0; + top: 50%; + left: 0; + right: 0; + transition: background $menuBtnAnimationDuration; + + // Transform to X + .menu-open & { + background: transparent; + + &:before { + top: 0; + left: 0; + right: 0; + transform: rotate(45deg); + transition: top $menuBtnAnimationDuration ease 0s, + transform $menuBtnAnimationDuration ease $menuBtnAnimationDuration; + } + + &:after { + bottom: 0; + left: 0; + right: 0; + transform: rotate(-45deg); + transition: bottom $menuBtnAnimationDuration ease 0s, + transform $menuBtnAnimationDuration ease $menuBtnAnimationDuration; + } + } + } + + &-lines, + &-lines:before, + &-lines:after { + background: currentColor; + width: $menuBtnLineWidth; + height: $menuBtnLineThickness; + position: absolute; + } + + &-lines:before, + &-lines:after { + content: ""; + left: 0; + right: 0; + width: auto; + } + + &-lines:before { + top: -1 * ($menuBtnLineThickness + $menuBtnLinesSpacing); + transition: top $menuBtnAnimationDuration ease $menuBtnAnimationDuration, + transform $menuBtnAnimationDuration ease 0s; + } + + &-lines:after { + bottom: -1 * ($menuBtnLineThickness + $menuBtnLinesSpacing); + transition: bottom $menuBtnAnimationDuration ease $menuBtnAnimationDuration, + transform $menuBtnAnimationDuration ease 0s; + } + + @include mq(lg) { + display: none; + } +} diff --git a/wp-content/themes/docker-wordpress-dev/src/sass/modules/_header__menu.scss b/wp-content/themes/docker-wordpress-dev/src/sass/modules/_header__menu.scss new file mode 100644 index 0000000..617ef41 --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/src/sass/modules/_header__menu.scss @@ -0,0 +1,33 @@ +/** + * Header Menu + */ + +.header__menu { + @include mq(lg, "max") { + position: fixed; + top: map-get($map: $header-heights, $key: "sm"); + right: 0; + bottom: 0; + left: 0; + z-index: 0; + + background: $white; + + transform: translateY(-100%); + opacity: 0; + transition: all 250ms; + + .menu-open & { + transform: translateY(0); + opacity: 1; + z-index: 1; + } + } + + @include mq(lg) { + &-list { + display: flex; + align-items: center; + } + } +} diff --git a/wp-content/themes/docker-wordpress-dev/src/sass/modules/_main.scss b/wp-content/themes/docker-wordpress-dev/src/sass/modules/_main.scss new file mode 100644 index 0000000..4a77f19 --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/src/sass/modules/_main.scss @@ -0,0 +1,12 @@ +/** + *
element + * Pushes content below the fixed header + */ + +main { + @each $breakpoint, $height in $header-heights { + @include mq($breakpoint) { + margin-top: $height; + } + } +} diff --git a/wp-content/themes/docker-wordpress-dev/src/sass/modules/_sharebar.scss b/wp-content/themes/docker-wordpress-dev/src/sass/modules/_sharebar.scss new file mode 100644 index 0000000..557ccdb --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/src/sass/modules/_sharebar.scss @@ -0,0 +1,9 @@ +/** + * Sharebar + */ + +.sharebar { + &__list { + display: flex; + } +} diff --git a/wp-content/themes/docker-wordpress-dev/src/sass/modules/_wrapper.scss b/wp-content/themes/docker-wordpress-dev/src/sass/modules/_wrapper.scss new file mode 100644 index 0000000..b8c5abd --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/src/sass/modules/_wrapper.scss @@ -0,0 +1,18 @@ +/** + * Site wrapper + */ + +.wrapper { + position: relative; + margin-left: auto; + margin-right: auto; + width: 100%; + max-width: $wrapper-max-width; + + @each $name, $size in $wrapper-gutter { + @include mq($name) { + padding-left: $size; + padding-right: $size; + } + } +} diff --git a/wp-content/themes/docker-wordpress-dev/src/sass/variables/_colors.scss b/wp-content/themes/docker-wordpress-dev/src/sass/variables/_colors.scss new file mode 100644 index 0000000..e97091f --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/src/sass/variables/_colors.scss @@ -0,0 +1,8 @@ +/** + * Colors + * + * Add HEX colors variants for use with rgba(color, opacity) syntax + */ + +$white: var(--wp--preset--color--white); +$black: var(--wp--preset--color--black); diff --git a/wp-content/themes/docker-wordpress-dev/src/sass/variables/_layout.scss b/wp-content/themes/docker-wordpress-dev/src/sass/variables/_layout.scss new file mode 100644 index 0000000..ba80342 --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/src/sass/variables/_layout.scss @@ -0,0 +1,41 @@ +// Breakpoints + +$site-breakpoints: ( + sm: 0px, + md: 768px, + lg: 1024px, + xl: 1440px, +); + +// Columns and Rows + +$row-class: ".row"; +$column-class: ".col"; +$column-count: 12; + +$column-gutter: ( + sm: 12px, + md: 16px, + lg: 32px, + xl: 32px, +); + +// .wrapper container + +$wrapper-max-width: 1440px; + +$wrapper-gutter: ( + sm: 24px, + md: 32px, + lg: 48px, + xl: 106px, +); + +// Header heights + +$header-heights: ( + sm: 60px, + md: 80px, + lg: 80px, + xl: 80px, +); diff --git a/wp-content/themes/docker-wordpress-dev/src/sass/wp/_wp-editor-colors.scss b/wp-content/themes/docker-wordpress-dev/src/sass/wp/_wp-editor-colors.scss new file mode 100644 index 0000000..d3b4635 --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/src/sass/wp/_wp-editor-colors.scss @@ -0,0 +1,24 @@ +/** + * Color map for Gutenberg styles + * Matches functions.php color definitions + * + * These are different from colors.scss + * and MUST be placed at the end of the scss imports list. + */ + +$colorPalette: ( + white: $white, + black: $black, +); + +@each $name, $color in $colorPalette { + .has-#{$name}-color, + /* This overrides Gutenberg color:inherit styles */ + .has-#{$name}-color.wp-block-button__link { + color: $color; + } + + .has-#{$name}-background-color { + background-color: $color; + } +} diff --git a/wp-content/themes/docker-wordpress-dev/src/sass/wp/_wp-logged-in.scss b/wp-content/themes/docker-wordpress-dev/src/sass/wp/_wp-logged-in.scss new file mode 100644 index 0000000..baf3e62 --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/src/sass/wp/_wp-logged-in.scss @@ -0,0 +1,24 @@ +/** + * Wordpress logged in state + * Normalizes the Admin Bar and pushes the header & menu down + */ + +#wpadminbar { + position: fixed; +} + +.logged-in { + .header { + top: 46px; + + &__menu { + top: 46px + map-get($map: $header-heights, $key: "sm"); + } + } + + @media (min-width: 783px) { + .header { + top: 32px; + } + } +} diff --git a/wp-content/themes/docker-wordpress-dev/style.css b/wp-content/themes/docker-wordpress-dev/style.css new file mode 100644 index 0000000..d0f9123 --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/style.css @@ -0,0 +1,7 @@ +/* +Theme Name: docker-wordpress-dev - v0.0.0 +Description: Docker-based local development environment for WordPress projects +Version: 0.0.0 +Author: Ideas On Purpose +Author URI: https://www.ideasonpurpose.com/ +*/ diff --git a/wp-content/themes/docker-wordpress-dev/template-parts/archive.php b/wp-content/themes/docker-wordpress-dev/template-parts/archive.php new file mode 100644 index 0000000..55a568d --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/template-parts/archive.php @@ -0,0 +1,20 @@ + + +
+
+

+
+ +
+
+ +
+ + +
+
+ + diff --git a/wp-content/themes/docker-wordpress-dev/template-parts/components/card.php b/wp-content/themes/docker-wordpress-dev/template-parts/components/card.php new file mode 100644 index 0000000..303bb4d --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/template-parts/components/card.php @@ -0,0 +1,41 @@ +{$term->name}"; +}, $categories); + +?> + + + + + + diff --git a/wp-content/themes/docker-wordpress-dev/template-parts/components/cookie-notice.php b/wp-content/themes/docker-wordpress-dev/template-parts/components/cookie-notice.php new file mode 100644 index 0000000..59088ae --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/template-parts/components/cookie-notice.php @@ -0,0 +1,30 @@ + + This site uses cookies for analytics. By continuing to browse this site, you agree + to this use. Read our Privacy Policy to learn more. +

+EOF; +$default_button_text = 'I Agree'; + +$cookie_button = get_field('cookie_accept_button', 'options') ?: $default_button_text; +$cookie_text = get_field('cookie_notice', 'options') ?: $default_cookie_text; +?> + + + + + + diff --git a/wp-content/themes/docker-wordpress-dev/template-parts/components/pagination.php b/wp-content/themes/docker-wordpress-dev/template-parts/components/pagination.php new file mode 100644 index 0000000..c340323 --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/template-parts/components/pagination.php @@ -0,0 +1,105 @@ +max_num_pages; + +if ($page_count <= 1) { + return; +} +/** + * HTML Snippets + */ +$fillerSnippet = '...'; +$edgeSnippet = ''. $SVG->arrowRight .''; +$edgeSnippetDisabled = ''. $SVG->arrowLeft .''; + +$allLinks = paginate_links([ + 'type' => 'array', + 'show_all' => true, + 'prev_next' => false, + 'prev_text' => 'Prev', + 'next_text' => 'Next' +]); + +/** + * $showLinks is mostly here for possible future re-use, this motif doesn't work with less than 7 visible items + */ +$showLinks = 7; + +/** + * $isEvenOffset shifts even-numbered center values towards the left + */ +$isEvenOffset = intval(!($showLinks & 1)); +$displayMid = $showLinks / 2; + +/** + * Calculate how many slices to show in the center. If $current is close to the edges, include the edge (-2), + * otherwise, omit both edges (-4) + */ +$sliceCount = + $current > $displayMid && $current < $page_count - floor($displayMid) - 1 ? $showLinks - 4 : $showLinks - 2; + +$sliceCount = count($allLinks) <= $showLinks ? count($allLinks) : $sliceCount; + +/** + * Calculate $start based on distance from middle and proximity to edges + */ +if ($current < $displayMid) { + $start = 0; +} elseif ($current > $page_count - $displayMid - 1) { + $start = $page_count - $sliceCount; +} else { + $start = $current - floor($sliceCount / 2) + $isEvenOffset; +} + +$centerLinks = array_slice($allLinks, $start, $sliceCount); + +$first = []; +$last = []; + +/** + * Assemble first/last links including $fillerSnippet if count($allLinks) < $showLinks + */ +if (count($allLinks) > $showLinks) { + if (count($centerLinks) < $showLinks - 2 || $current > $page_count / 2) { + $first = array_slice($allLinks, 0, 1); + $first[] = $fillerSnippet; + } + if (count($centerLinks) < $showLinks - 2 || $current < $page_count / 2) { + $last = array_slice($allLinks, -1); + array_unshift($last, $fillerSnippet); + } +} +/** + * Active/Inactive Previous/Next arrows, these are expected to be Arrays + */ +$prev = + $current + 1 > 1 + ? sprintf($edgeSnippet, 'pagination__first', get_pagenum_link($current - 1 + 1)) + : sprintf($edgeSnippetDisabled, 'pagination__first'); + +$next = + $current + 1 < $page_count + ? sprintf($edgeSnippet, 'pagination__last', get_pagenum_link($current + 1 + 1), '') + : sprintf($edgeSnippetDisabled, 'pagination__last'); + +$prev = [$prev]; +$next = [$next]; + +/** + * Finally, merge all the pieces into a single array to be imploded into the HTML + */ +$display = array_merge($prev, $first, $centerLinks, $last, $next); +?> + + + + + + diff --git a/wp-content/themes/docker-wordpress-dev/template-parts/components/sharebar.php b/wp-content/themes/docker-wordpress-dev/template-parts/components/sharebar.php new file mode 100644 index 0000000..4f71019 --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/template-parts/components/sharebar.php @@ -0,0 +1,32 @@ + + + + + diff --git a/wp-content/themes/docker-wordpress-dev/template-parts/favicon.php b/wp-content/themes/docker-wordpress-dev/template-parts/favicon.php new file mode 100644 index 0000000..9ec7929 --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/template-parts/favicon.php @@ -0,0 +1,12 @@ + + +/dist/favicon/` + */ +?> + + + diff --git a/wp-content/themes/docker-wordpress-dev/template-parts/footer.php b/wp-content/themes/docker-wordpress-dev/template-parts/footer.php new file mode 100644 index 0000000..501c36b --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/template-parts/footer.php @@ -0,0 +1,67 @@ + $menu_id, + 'menu_class' => "{$menu_key}__menu", + 'items_wrap' => '' . "\n", + 'container' => '', + 'echo' => false, + ]); + } +} + +$copyright = get_field('copyright', 'options'); +if (is_null($copyright) || empty($copyright)) { + $copyright = + '%YEAR% ' . get_bloginfo('name') . ' | ' . get_bloginfo('tagline');')'; +} +$copyright = str_replace('%YEAR%', date('Y'), $copyright); + + +?> + + + +
+
+
+
+

Footer

+ + +
+
+
+ + +
+ + + + + + + diff --git a/wp-content/themes/docker-wordpress-dev/template-parts/header.php b/wp-content/themes/docker-wordpress-dev/template-parts/header.php new file mode 100644 index 0000000..cae7b5b --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/template-parts/header.php @@ -0,0 +1,52 @@ + $menu_id, + 'menu_class' => "{$menu_key}__menu", + 'items_wrap' => '' . "\n", + 'container' => '', + 'echo' => false, + ]); + } +} +?> + + + +
+ +
+ + diff --git a/wp-content/themes/docker-wordpress-dev/template-parts/page.php b/wp-content/themes/docker-wordpress-dev/template-parts/page.php new file mode 100644 index 0000000..a645359 --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/template-parts/page.php @@ -0,0 +1,17 @@ + + +
+
+ +
+

+
+ +
+ +
+ +
+
+ + diff --git a/wp-content/themes/docker-wordpress-dev/template-parts/single.php b/wp-content/themes/docker-wordpress-dev/template-parts/single.php new file mode 100644 index 0000000..855564c --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/template-parts/single.php @@ -0,0 +1,20 @@ + + +
+
+ +
+ +

+ + +
+ +
+ +
+ +
+
+ + diff --git a/wp-content/themes/docker-wordpress-dev/theme.json b/wp-content/themes/docker-wordpress-dev/theme.json new file mode 100644 index 0000000..3c44113 --- /dev/null +++ b/wp-content/themes/docker-wordpress-dev/theme.json @@ -0,0 +1,47 @@ +{ + "version": 1, + "settings": { + "color": { + "link": false, + "palette": [ + { "name": "White", "slug": "white", "color": "#fff" }, + { "name": "Black", "slug": "black", "color": "#000" } + ], + "gradients": [ + { + "name": "Blue-Navy", + "gradient": "linear-gradient(0deg, #00f 15%, #004 85%)", + "slug": "blue-navy-gradient" + } + ], + "custom": true, + "customGradient": true + }, + "typography": { + "dropCap": false, + "customFontSize": false + }, + "blocks": { + "core/button": { + "border": { + "customRadius": false, + "customColor": false + } + }, + "core/paragraph": { + "typography": { + "fontSizes": [] + } + } + } + }, + "styles": { + "blocks": { + "core/button": { + "border": { + "radius": "0px" + } + } + } + } +}