├── .env.example ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── Dockerfile ├── README.md ├── cli └── create-cert.sh ├── config ├── php.ini └── phpmyadmin.ini ├── docker-compose.yml ├── nginx └── default.conf.conf └── src ├── .env.example ├── .github └── workflows │ └── issues.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── composer.json ├── composer.lock ├── config ├── application.php └── environments │ ├── development.php │ └── staging.php ├── phpcs.xml ├── web ├── app │ ├── mu-plugins │ │ ├── bedrock-autoloader.php │ │ ├── disallow-indexing.php │ │ └── register-theme-directory.php │ ├── plugins │ │ └── .gitkeep │ ├── themes │ │ ├── .gitkeep │ │ └── twentytwentyone │ │ │ ├── 404.php │ │ │ ├── archive.php │ │ │ ├── assets │ │ │ ├── css │ │ │ │ ├── custom-color-overrides.css │ │ │ │ ├── ie-editor.css │ │ │ │ ├── ie-editor.css.map │ │ │ │ ├── ie.css │ │ │ │ ├── ie.css.map │ │ │ │ ├── print.css │ │ │ │ ├── print.css.map │ │ │ │ ├── style-dark-mode-rtl.css │ │ │ │ ├── style-dark-mode.css │ │ │ │ ├── style-dark-mode.css.map │ │ │ │ ├── style-editor-customizer.css │ │ │ │ ├── style-editor.css │ │ │ │ └── style-editor.css.map │ │ │ ├── images │ │ │ │ ├── Daffodils.jpg │ │ │ │ ├── Reading.jpg │ │ │ │ ├── in-the-bois-de-boulogne.jpg │ │ │ │ ├── playing-in-the-sand.jpg │ │ │ │ ├── roses-tremieres-hollyhocks-1884.jpg │ │ │ │ ├── self-portrait-1885.jpg │ │ │ │ ├── the-garden-at-bougival-1884.jpg │ │ │ │ ├── villa-with-orange-trees-nice.jpg │ │ │ │ └── young-woman-in-mauve.jpg │ │ │ ├── js │ │ │ │ ├── customize-helpers.js │ │ │ │ ├── customize-preview.js │ │ │ │ ├── customize.js │ │ │ │ ├── dark-mode-toggler.js │ │ │ │ ├── editor-dark-mode-support.js │ │ │ │ ├── editor.js │ │ │ │ ├── palette-colorpicker.js │ │ │ │ ├── polyfills.js │ │ │ │ ├── primary-navigation.js │ │ │ │ ├── responsive-embeds.js │ │ │ │ └── skip-link-focus-fix.js │ │ │ └── sass │ │ │ │ ├── 01-settings │ │ │ │ ├── file-header.scss │ │ │ │ ├── fonts.scss │ │ │ │ └── global.scss │ │ │ │ ├── 02-tools │ │ │ │ ├── functions.scss │ │ │ │ └── mixins.scss │ │ │ │ ├── 03-generic │ │ │ │ ├── breakpoints.scss │ │ │ │ ├── clearings.scss │ │ │ │ ├── normalize.scss │ │ │ │ ├── reset.scss │ │ │ │ └── vertical-margins.scss │ │ │ │ ├── 04-elements │ │ │ │ ├── blockquote.scss │ │ │ │ ├── forms-editor.scss │ │ │ │ ├── forms.scss │ │ │ │ ├── links.scss │ │ │ │ ├── media.scss │ │ │ │ └── misc.scss │ │ │ │ ├── 05-blocks │ │ │ │ ├── _config.scss │ │ │ │ ├── audio │ │ │ │ │ └── _style.scss │ │ │ │ ├── blocks-editor.scss │ │ │ │ ├── blocks.scss │ │ │ │ ├── button │ │ │ │ │ ├── _editor.scss │ │ │ │ │ └── _style.scss │ │ │ │ ├── code │ │ │ │ │ ├── _editor.scss │ │ │ │ │ └── _style.scss │ │ │ │ ├── columns │ │ │ │ │ ├── _editor.scss │ │ │ │ │ └── _style.scss │ │ │ │ ├── cover │ │ │ │ │ ├── _editor.scss │ │ │ │ │ └── _style.scss │ │ │ │ ├── file │ │ │ │ │ ├── _editor.scss │ │ │ │ │ └── _style.scss │ │ │ │ ├── gallery │ │ │ │ │ ├── _editor.scss │ │ │ │ │ └── _style.scss │ │ │ │ ├── group │ │ │ │ │ ├── _editor.scss │ │ │ │ │ └── _style.scss │ │ │ │ ├── heading │ │ │ │ │ ├── _editor.scss │ │ │ │ │ └── _style.scss │ │ │ │ ├── html │ │ │ │ │ └── _editor.scss │ │ │ │ ├── image │ │ │ │ │ ├── _editor.scss │ │ │ │ │ └── _style.scss │ │ │ │ ├── latest-comments │ │ │ │ │ ├── _editor.scss │ │ │ │ │ └── _style.scss │ │ │ │ ├── latest-posts │ │ │ │ │ ├── _editor.scss │ │ │ │ │ └── _style.scss │ │ │ │ ├── legacy │ │ │ │ │ ├── _editor.scss │ │ │ │ │ └── _style.scss │ │ │ │ ├── list │ │ │ │ │ ├── _editor.scss │ │ │ │ │ └── _style.scss │ │ │ │ ├── media-text │ │ │ │ │ ├── _editor.scss │ │ │ │ │ └── _style.scss │ │ │ │ ├── navigation │ │ │ │ │ ├── _editor.scss │ │ │ │ │ └── _style.scss │ │ │ │ ├── paragraph │ │ │ │ │ ├── _editor.scss │ │ │ │ │ └── _style.scss │ │ │ │ ├── preformatted │ │ │ │ │ ├── _editor.scss │ │ │ │ │ └── _style.scss │ │ │ │ ├── pullquote │ │ │ │ │ ├── _editor.scss │ │ │ │ │ └── _style.scss │ │ │ │ ├── query-loop │ │ │ │ │ ├── _editor.scss │ │ │ │ │ └── _style.scss │ │ │ │ ├── quote │ │ │ │ │ ├── _editor.scss │ │ │ │ │ └── _style.scss │ │ │ │ ├── rss │ │ │ │ │ ├── _editor.scss │ │ │ │ │ └── _style.scss │ │ │ │ ├── search │ │ │ │ │ ├── _editor.scss │ │ │ │ │ └── _style.scss │ │ │ │ ├── separator │ │ │ │ │ ├── _editor.scss │ │ │ │ │ └── _style.scss │ │ │ │ ├── social-icons │ │ │ │ │ ├── _editor.scss │ │ │ │ │ └── _style.scss │ │ │ │ ├── table │ │ │ │ │ ├── _editor.scss │ │ │ │ │ └── _style.scss │ │ │ │ ├── tag-clould │ │ │ │ │ ├── _editor.scss │ │ │ │ │ └── _style.scss │ │ │ │ ├── utilities │ │ │ │ │ ├── _editor.scss │ │ │ │ │ ├── _font-sizes.scss │ │ │ │ │ └── _style.scss │ │ │ │ ├── verse │ │ │ │ │ ├── _editor.scss │ │ │ │ │ └── _style.scss │ │ │ │ └── video │ │ │ │ │ └── _style.scss │ │ │ │ ├── 06-components │ │ │ │ ├── 404.scss │ │ │ │ ├── archives.scss │ │ │ │ ├── comments.scss │ │ │ │ ├── editor.scss │ │ │ │ ├── entry.scss │ │ │ │ ├── footer-navigation.scss │ │ │ │ ├── footer.scss │ │ │ │ ├── header.scss │ │ │ │ ├── navigation.scss │ │ │ │ ├── pagination.scss │ │ │ │ ├── posts-and-pages.scss │ │ │ │ ├── search.scss │ │ │ │ ├── single.scss │ │ │ │ └── widgets.scss │ │ │ │ ├── 07-utilities │ │ │ │ ├── a11y.scss │ │ │ │ ├── color-palette.scss │ │ │ │ ├── ie.scss │ │ │ │ ├── measure.scss │ │ │ │ └── print.scss │ │ │ │ ├── style-dark-mode.scss │ │ │ │ ├── style-editor.scss │ │ │ │ └── style.scss │ │ │ ├── classes │ │ │ ├── class-twenty-twenty-one-custom-colors.php │ │ │ ├── class-twenty-twenty-one-customize-color-control.php │ │ │ ├── class-twenty-twenty-one-customize-notice-control.php │ │ │ ├── class-twenty-twenty-one-customize.php │ │ │ ├── class-twenty-twenty-one-dark-mode.php │ │ │ └── class-twenty-twenty-one-svg-icons.php │ │ │ ├── comments.php │ │ │ ├── footer.php │ │ │ ├── functions.php │ │ │ ├── header.php │ │ │ ├── image.php │ │ │ ├── inc │ │ │ ├── back-compat.php │ │ │ ├── block-patterns.php │ │ │ ├── block-styles.php │ │ │ ├── custom-css.php │ │ │ ├── menu-functions.php │ │ │ ├── starter-content.php │ │ │ ├── template-functions.php │ │ │ └── template-tags.php │ │ │ ├── index.php │ │ │ ├── package-lock.json │ │ │ ├── package.json │ │ │ ├── page.php │ │ │ ├── postcss.config.js │ │ │ ├── readme.txt │ │ │ ├── screenshot.png │ │ │ ├── search.php │ │ │ ├── searchform.php │ │ │ ├── single.php │ │ │ ├── style-rtl.css │ │ │ ├── style.css │ │ │ ├── style.css.map │ │ │ └── template-parts │ │ │ ├── content │ │ │ ├── content-excerpt.php │ │ │ ├── content-none.php │ │ │ ├── content-page.php │ │ │ ├── content-single.php │ │ │ └── content.php │ │ │ ├── excerpt │ │ │ ├── excerpt-aside.php │ │ │ ├── excerpt-audio.php │ │ │ ├── excerpt-chat.php │ │ │ ├── excerpt-gallery.php │ │ │ ├── excerpt-image.php │ │ │ ├── excerpt-link.php │ │ │ ├── excerpt-quote.php │ │ │ ├── excerpt-status.php │ │ │ ├── excerpt-video.php │ │ │ └── excerpt.php │ │ │ ├── footer │ │ │ └── footer-widgets.php │ │ │ ├── header │ │ │ ├── entry-header.php │ │ │ ├── excerpt-header.php │ │ │ ├── site-branding.php │ │ │ ├── site-header.php │ │ │ └── site-nav.php │ │ │ └── post │ │ │ └── author-bio.php │ └── uploads │ │ └── .gitkeep ├── index.php └── wp-config.php └── wp-cli.yml /.env.example: -------------------------------------------------------------------------------- 1 | IP=127.0.0.1 2 | APP_NAME=myapp 3 | DOMAIN=myapp.local 4 | DB_HOST=mysql 5 | DB_NAME=myapp 6 | DB_ROOT_PASSWORD=password 7 | DB_TABLE_PREFIX=wp_ 8 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI Build 2 | on: [push] 3 | 4 | jobs: 5 | build: 6 | name: Build 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Checkout Repository 10 | uses: actions/checkout@v1 11 | 12 | - name: Run composer install 13 | working-directory: src 14 | run: | 15 | composer install --prefer-dist 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Application 2 | src/web/app/plugins/* 3 | !src/web/app/plugins/.gitkeep 4 | src/web/app/mu-plugins/*/ 5 | src/web/app/upgrade 6 | src/web/app/uploads/* 7 | !src/web/app/uploads/.gitkeep 8 | 9 | # WordPress 10 | src/web/wp 11 | src/web/.htaccess 12 | 13 | # WP-CLI 14 | db-sync 15 | sql-dump-*.sql 16 | 17 | # Dotenv 18 | .env 19 | .env.* 20 | !.env.example 21 | 22 | # Vendor (e.g. Composer) 23 | src/vendor/* 24 | !src/vendor/.gitkeep 25 | 26 | # Database volume 27 | data 28 | 29 | # Node Package Manager 30 | node_modules 31 | 32 | # Certs 33 | certs/ 34 | 35 | # Logs 36 | logs/ 37 | 38 | # Vagrant 39 | bin 40 | .vagrant 41 | 42 | # Custom 43 | .DS_Store 44 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM urre/wordpress-nginx-docker-compose-image:1.4.0 2 | 3 | # Install wp-cli 4 | RUN apt-get update && apt-get install -y sudo less mariadb-client 5 | RUN curl -o /bin/wp-cli.phar https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar 6 | RUN chmod +x /bin/wp-cli.phar 7 | RUN cd /bin && mv wp-cli.phar wp 8 | RUN mkdir -p /var/www/.wp-cli/cache && chown www-data:www-data /var/www/.wp-cli/cache 9 | 10 | # Forward Message to mailhog 11 | RUN curl --location --output /usr/local/bin/mhsendmail https://github.com/mailhog/mhsendmail/releases/download/v0.2.0/mhsendmail_linux_amd64 && \ 12 | chmod +x /usr/local/bin/mhsendmail 13 | RUN echo 'sendmail_path="/usr/local/bin/mhsendmail --smtp-addr=mailhog:1025 --from=no-reply@gbp.lo"' > /usr/local/etc/php/conf.d/mailhog.ini 14 | 15 | # Note: Use docker-compose up -d --force-recreate --build when Dockerfile has changed. 16 | -------------------------------------------------------------------------------- /cli/create-cert.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | source "../.env" 6 | 7 | DOMAIN=$(echo "$DOMAIN") 8 | 9 | mkcert -install "${DOMAIN}" 10 | 11 | mkdir -p ../certs 12 | 13 | find . -type f -name "*.pem" -exec mv {} ../certs \; 14 | -------------------------------------------------------------------------------- /config/php.ini: -------------------------------------------------------------------------------- 1 | file_uploads = On 2 | memory_limit = 512M 3 | upload_max_filesize = 128M 4 | post_max_size = 128M 5 | max_execution_time = 600 6 | client_max_body_size = 128M 7 | -------------------------------------------------------------------------------- /config/phpmyadmin.ini: -------------------------------------------------------------------------------- 1 | upload_max_filesize = 128M 2 | post_max_size = 128M 3 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.6' 2 | services: 3 | nginx: 4 | image: nginx:latest 5 | container_name: ${APP_NAME}-nginx 6 | ports: 7 | - '80:80' 8 | - '443:443' 9 | volumes: 10 | - "./nginx/:/etc/nginx/templates/" 11 | - ./src:/var/www/html:rw,cached 12 | - ./certs:/etc/certs 13 | environment: 14 | - "NGINX_ENVSUBST_TEMPLATE_SUFFIX=.conf" 15 | - "DOMAIN=${DOMAIN}" 16 | depends_on: 17 | - wordpress 18 | restart: always 19 | 20 | mysql: 21 | image: mariadb:10.7 22 | container_name: ${APP_NAME}-mysql 23 | command: --lower_case_table_names=2 24 | volumes: 25 | - './data/db:/var/lib/mysql:delegated' 26 | environment: 27 | - MYSQL_ROOT_PASSWORD=${DB_ROOT_PASSWORD} 28 | - MYSQL_DATABASE=${DB_NAME} 29 | restart: always 30 | ports: 31 | - '3306:3306' 32 | 33 | wordpress: 34 | build: 35 | context: . 36 | dockerfile: Dockerfile 37 | container_name: ${APP_NAME}-wordpress 38 | volumes: 39 | - ./src:/var/www/html:rw,cached 40 | - ./config/php.ini:/usr/local/etc/php/conf.d/php.ini 41 | depends_on: 42 | - mysql 43 | restart: always 44 | 45 | phpmyadmin: 46 | image: phpmyadmin/phpmyadmin 47 | container_name: ${APP_NAME}-phpmyadmin 48 | volumes: 49 | - ./config/phpmyadmin.ini:/usr/local/etc/php/conf.d/phpmyadmin.ini 50 | environment: 51 | PMA_HOST: "${DB_HOST}" 52 | PMA_PORT: 3306 53 | MYSQL_ROOT_PASSWORD: "${DB_ROOT_PASSWORD}" 54 | ports: 55 | - '8082:80' 56 | links: 57 | - mysql:mysql 58 | 59 | mailhog: 60 | container_name: ${APP_NAME}-mailhog 61 | image: mailhog/mailhog 62 | ports: 63 | - "8025:8025" 64 | - "1025:1025" 65 | 66 | composer: 67 | image: composer 68 | container_name: ${APP_NAME}-composer 69 | working_dir: /var/www/html 70 | restart: 'no' 71 | volumes: 72 | - ./src:/var/www/html:rw,cached 73 | -------------------------------------------------------------------------------- /nginx/default.conf.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | server_name ${DOMAIN}; 4 | return 301 https://$host$request_uri; 5 | } 6 | 7 | server { 8 | listen 443 ssl http2; 9 | server_name ${DOMAIN} www.${DOMAIN}; 10 | 11 | ssl_certificate /etc/certs/${DOMAIN}.pem; 12 | ssl_certificate_key /etc/certs/${DOMAIN}-key.pem; 13 | 14 | add_header Strict-Transport-Security "max-age=31536000" always; 15 | 16 | ssl_session_cache shared:SSL:20m; 17 | ssl_session_timeout 10m; 18 | 19 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2; 20 | ssl_prefer_server_ciphers on; 21 | ssl_ciphers "ECDH+AESGCM:ECDH+AES256:ECDH+AES128:!ADH:!AECDH:!MD5;"; 22 | 23 | root /var/www/html/web; 24 | index index.php; 25 | 26 | access_log /var/log/nginx/access.log; 27 | error_log /var/log/nginx/error.log; 28 | 29 | gzip on; 30 | gzip_disable "msie6"; 31 | 32 | gzip_vary on; 33 | gzip_proxied any; 34 | gzip_comp_level 6; 35 | gzip_buffers 16 8k; 36 | gzip_http_version 1.1; 37 | gzip_min_length 0; 38 | gzip_types text/plain application/javascript text/css text/xml application/xml application/xml+rss text/javascript application/vnd.ms-fontobject application/x-font-ttf font/opentype; 39 | 40 | client_max_body_size 100M; 41 | 42 | location ~ /.well-known/acme-challenge { 43 | allow all; 44 | root /var/www/html; 45 | } 46 | 47 | location / { 48 | try_files $uri $uri/ /index.php$is_args$args; 49 | } 50 | 51 | location ~ \.php$ { 52 | try_files $uri =404; 53 | fastcgi_buffers 8 16k; 54 | fastcgi_buffer_size 32k; 55 | fastcgi_connect_timeout 60; 56 | fastcgi_read_timeout 300; 57 | fastcgi_split_path_info ^(.+\.php)(/.+)$; 58 | fastcgi_pass wordpress:9000; 59 | fastcgi_index index.php; 60 | include fastcgi_params; 61 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 62 | fastcgi_param PATH_INFO $fastcgi_path_info; 63 | } 64 | 65 | location ~ /\.ht { 66 | deny all; 67 | } 68 | 69 | location = /favicon.ico { 70 | log_not_found off; access_log off; 71 | } 72 | location = /robots.txt { 73 | log_not_found off; access_log off; allow all; 74 | } 75 | location ~* \.(css|gif|ico|jpeg|jpg|js|png)$ { 76 | expires max; 77 | log_not_found off; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/.env.example: -------------------------------------------------------------------------------- 1 | DB_NAME='myapp' 2 | DB_USER='root' 3 | DB_PASSWORD='password' 4 | 5 | # Optionally, you can use a data source name (DSN) 6 | # When using a DSN, you can remove the DB_NAME, DB_USER, DB_PASSWORD, and DB_HOST variables 7 | # DATABASE_URL='mysql://database_user:database_password@database_host:database_port/database_name' 8 | 9 | # Optional variables 10 | DB_HOST='mysql' 11 | # DB_PREFIX='wp_' 12 | 13 | WP_ENV='development' 14 | WP_HOME='https://myapp.local' 15 | WP_SITEURL="${WP_HOME}/wp" 16 | WP_DEBUG_LOG=/path/to/debug.log 17 | 18 | # Generate your keys here: https://roots.io/salts.html 19 | AUTH_KEY='generateme' 20 | SECURE_AUTH_KEY='generateme' 21 | LOGGED_IN_KEY='generateme' 22 | NONCE_KEY='generateme' 23 | AUTH_SALT='generateme' 24 | SECURE_AUTH_SALT='generateme' 25 | LOGGED_IN_SALT='generateme' 26 | NONCE_SALT='generateme' 27 | -------------------------------------------------------------------------------- /src/.github/workflows/issues.yml: -------------------------------------------------------------------------------- 1 | name: Issue closer 2 | on: [issues] 3 | jobs: 4 | autoclose: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - name: Autoclose issues that did not follow issue template 8 | uses: roots/issue-closer@v1.1 9 | with: 10 | repo-token: ${{ secrets.GITHUB_TOKEN }} 11 | issue-close-message: "Hi @${issue.user.login}, 12 | It looks like the issue template is missing from this issue. Please take a look at the [Contribution Guidelines](https://github.com/roots/guidelines/blob/master/CONTRIBUTING.md), which will tell you **exactly** what your ticket has to contain in order to be processable. 13 | Please **do not** use the issue tracker for personal support requests. Use [Roots Discourse](https://discourse.roots.io/) to ask the Roots community for help, or [hire someone from the community](https://discourse.roots.io/c/jobs)." 14 | issue-pattern: ".*guidelines for Contributing.*" 15 | -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | # Application 2 | web/app/plugins/* 3 | !web/app/plugins/.gitkeep 4 | web/app/mu-plugins/*/ 5 | web/app/upgrade 6 | web/app/uploads/* 7 | !web/app/uploads/.gitkeep 8 | 9 | # WordPress 10 | web/wp 11 | web/.htaccess 12 | 13 | # Logs 14 | *.log 15 | 16 | # Dotenv 17 | .env 18 | .env.* 19 | !.env.example 20 | 21 | # Composer 22 | /vendor 23 | 24 | # WP-CLI 25 | wp-cli.local.yml 26 | -------------------------------------------------------------------------------- /src/LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) Roots 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /src/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "roots/bedrock", 3 | "type": "project", 4 | "license": "MIT", 5 | "description": "WordPress boilerplate with modern development tools, easier configuration, and an improved folder structure", 6 | "homepage": "https://roots.io/bedrock/", 7 | "authors": [ 8 | { 9 | "name": "Scott Walkinshaw", 10 | "email": "scott.walkinshaw@gmail.com", 11 | "homepage": "https://github.com/swalkinshaw" 12 | }, 13 | { 14 | "name": "Ben Word", 15 | "email": "ben@benword.com", 16 | "homepage": "https://github.com/retlehs" 17 | } 18 | ], 19 | "keywords": [ 20 | "bedrock", "composer", "roots", "wordpress", "wp", "wp-config" 21 | ], 22 | "support": { 23 | "issues": "https://github.com/roots/bedrock/issues", 24 | "forum": "https://discourse.roots.io/category/bedrock" 25 | }, 26 | "repositories": [ 27 | { 28 | "type": "composer", 29 | "url": "https://wpackagist.org", 30 | "only": ["wpackagist-plugin/*", "wpackagist-theme/*"] 31 | } 32 | ], 33 | "require": { 34 | "php": ">=8.1", 35 | "composer/installers": "^1.8", 36 | "vlucas/phpdotenv": "^5.4.1", 37 | "oscarotero/env": "^2.1", 38 | "roots/bedrock-autoloader": "^1.0", 39 | "roots/wordpress": "^6.0.0", 40 | "roots/wp-config": "1.0.0", 41 | "roots/wp-password-bcrypt": "1.0.0", 42 | "wpackagist-theme/twentytwentyone": "^1.6" 43 | }, 44 | "require-dev": { 45 | "squizlabs/php_codesniffer": "^3.5.6", 46 | "roave/security-advisories": "dev-master" 47 | }, 48 | "config": { 49 | "optimize-autoloader": true, 50 | "preferred-install": "dist", 51 | "allow-plugins": { 52 | "composer/installers": true, 53 | "roots/wordpress-core-installer": true 54 | } 55 | }, 56 | "minimum-stability": "dev", 57 | "prefer-stable": true, 58 | "extra": { 59 | "installer-paths": { 60 | "web/app/mu-plugins/{$name}/": ["type:wordpress-muplugin"], 61 | "web/app/plugins/{$name}/": ["type:wordpress-plugin"], 62 | "web/app/themes/{$name}/": ["type:wordpress-theme"] 63 | }, 64 | "wordpress-install-dir": "web/wp" 65 | }, 66 | "scripts": { 67 | "post-root-package-install": [ 68 | "php -r \"copy('.env.example', '.env');\"" 69 | ], 70 | "test": [ 71 | "phpcs" 72 | ] 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/config/environments/development.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | Roots Coding Standards 4 | 5 | 6 | . 7 | 8 | 9 | 10 | 11 | 12 | web/wp 13 | vendor/ 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/web/app/mu-plugins/bedrock-autoloader.php: -------------------------------------------------------------------------------- 1 | 14 | 15 | 18 | 19 |
20 |
21 |

22 | 23 |
24 |
25 | 26 | 16 | 17 | 18 | 19 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | >> TABLE OF CONTENTS: 8 | ---------------------------------------------------------------- 9 | # Margins & paddings 10 | # Typography 11 | # Page breaks 12 | # Links 13 | # Visibility 14 | --------------------------------------------------------------*/ 15 | @media print { 16 | 17 | /* Margins & paddings */ 18 | @page { 19 | margin: 2cm; 20 | } 21 | 22 | .entry .entry-header, 23 | .entry, 24 | .single .site-main > article > .entry-footer { 25 | margin-top: 0; 26 | margin-bottom: 0; 27 | } 28 | 29 | .site-footer .site-info { 30 | margin: 0; 31 | } 32 | 33 | .site-header { 34 | padding: 0; 35 | } 36 | 37 | /* Fonts */ 38 | body { 39 | font: 13pt Georgia, "Times New Roman", Times, serif; 40 | font: 13pt var(--global--font-secondary, Georgia, "Times New Roman", Times, serif); 41 | line-height: 1.3; 42 | background: #fff !important; 43 | color: #000; 44 | } 45 | 46 | .has-background-dark * { 47 | color: #000 !important; 48 | } 49 | 50 | h1, 51 | .entry-title, 52 | .singular .entry-title, 53 | .page-title { 54 | font-size: 22pt; 55 | font-weight: bold; 56 | } 57 | 58 | h2, 59 | h3, 60 | h4, 61 | .has-regular-font-size, 62 | .has-large-font-size, 63 | h2.author-title, 64 | p.author-bio, 65 | .comments-title, 66 | .archive-description { 67 | font-size: 14pt; 68 | margin-top: 25px; 69 | } 70 | 71 | .comment-meta, 72 | .comment-meta .comment-author .fn { 73 | font-size: 13pt; 74 | } 75 | 76 | /* Page breaks */ 77 | a { 78 | page-break-inside: avoid; 79 | } 80 | 81 | blockquote { 82 | page-break-inside: avoid; 83 | } 84 | 85 | h1, 86 | h2, 87 | h3, 88 | h4, 89 | h5, 90 | h6 { 91 | page-break-after: avoid; 92 | page-break-inside: avoid; 93 | } 94 | 95 | img { 96 | page-break-inside: avoid; 97 | page-break-after: avoid; 98 | } 99 | 100 | table, 101 | pre, 102 | figure { 103 | page-break-inside: avoid; 104 | } 105 | 106 | ul, 107 | ol, 108 | dl { 109 | page-break-before: avoid; 110 | } 111 | 112 | /* Links */ 113 | a:link, 114 | a:visited, 115 | a { 116 | background: transparent; 117 | font-weight: bold; 118 | text-decoration: underline; 119 | text-align: left; 120 | } 121 | 122 | a[href^=http]:after { 123 | content: " < " attr(href) "> "; 124 | } 125 | 126 | a:after > img { 127 | content: ""; 128 | } 129 | 130 | article a[href^="#"]:after { 131 | content: ""; 132 | } 133 | 134 | a:not(:local-link):after { 135 | content: " < " attr(href) "> "; 136 | } 137 | 138 | .entry-title a:after { 139 | content: "\a< " attr(href) "> "; 140 | white-space: pre; 141 | font-size: 14pt; 142 | } 143 | 144 | .cat-links a:after, 145 | .tags-links a:after, 146 | .byline a:after, 147 | .comment-metadata a:after, 148 | .wp-block-calendar a:after, 149 | .wp-block-tag-cloud a:after, 150 | .page-links a:after { 151 | content: ""; 152 | } 153 | 154 | /* Visibility */ 155 | .primary-navigation, 156 | .site-title + .primary-navigation, 157 | .footer-navigation, 158 | .entry-footer, 159 | .post-navigation, 160 | .navigation.pagination, 161 | .widget-area, 162 | .edit-link, 163 | .more-link, 164 | .comment-reply, 165 | .reply, 166 | .comment .comment-metadata .edit-link, 167 | .comment-respond, 168 | #dark-mode-toggler { 169 | display: none !important; 170 | } 171 | 172 | .entry .entry-content .wp-block-button .wp-block-button__link, 173 | .entry .entry-content .button, 174 | .entry .entry-content .wp-block-file__button { 175 | color: #000; 176 | background: none; 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /src/web/app/themes/twentytwentyone/assets/css/print.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sourceRoot":"","sources":["../sass/07-utilities/print.scss"],"names":[],"mappings":"AAAA;AAAA;AAAA;AAAA;AAKA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAUA;AAEC;EAEA;IACC;;EAGD;AAAA;AAAA;IAGC;IACA;;EAGD;IACC;;EAGD;IACC;;AAGD;EAEA;IACC;IACA;IACA;IACA;IACA;;EAID;IACC;;EAGD;AAAA;AAAA;AAAA;IAIC;IACA;;EAGD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;IASC;IACA;;EAGD;AAAA;IAEC;;AAGD;EAEA;IACC;;EAGD;IACC;;EAGD;AAAA;AAAA;AAAA;AAAA;AAAA;IAMC;IACA;;EAGD;IACC;IACA;;EAGD;AAAA;AAAA;IAGC;;EAGD;AAAA;AAAA;IAGC;;AAGD;EAEA;AAAA;AAAA;IAGC;IACA;IACA;IACA;;EAGD;IACC;;EAGD;IACC;;EAGD;IACC;;EAGD;IACC;;EAGD;IACC;IACA;IACA;;EAGD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;IAOC;;AAGD;EACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;IAcC;;EAGD;AAAA;AAAA;IAGC;IACA","file":"print.css"} -------------------------------------------------------------------------------- /src/web/app/themes/twentytwentyone/assets/css/style-dark-mode-rtl.css: -------------------------------------------------------------------------------- 1 | /* OS dark theme preference */ 2 | @media only screen { 3 | 4 | .is-dark-theme.is-dark-theme { 5 | --global--color-background: var(--global--color-dark-gray); 6 | --global--color-primary: var(--global--color-light-gray); 7 | --global--color-secondary: var(--global--color-light-gray); 8 | --button--color-text: var(--global--color-background); 9 | --button--color-text-hover: var(--global--color-secondary); 10 | --button--color-text-active: var(--global--color-secondary); 11 | --button--color-background: var(--global--color-secondary); 12 | --button--color-background-active: var(--global--color-background); 13 | --global--color-border: #9ea1a7; 14 | 15 | /* Block: Table */ 16 | --table--stripes-border-color: rgba(240, 240, 240, 0.15); 17 | --table--stripes-background-color: rgba(240, 240, 240, 0.15); 18 | } 19 | 20 | .is-dark-theme img { 21 | filter: brightness(0.85) contrast(1.1); 22 | } 23 | 24 | .respect-color-scheme-preference.is-dark-theme body { 25 | background-color: var(--global--color-background); 26 | } 27 | 28 | #dark-mode-toggler { 29 | cursor: pointer; 30 | display: flex; 31 | align-items: center; 32 | justify-content: center; 33 | font-size: var(--global--font-size-xs); 34 | padding: 0.5em; 35 | min-height: 44px; 36 | min-width: max-content; 37 | border: 2px solid currentColor; 38 | box-shadow: none; 39 | background: var(--button--color-text); 40 | color: var(--button--color-background); 41 | z-index: 9998; 42 | } 43 | 44 | .no-js #dark-mode-toggler { 45 | display: none; 46 | } 47 | 48 | #dark-mode-toggler.fixed-bottom { 49 | position: fixed; 50 | bottom: 5px; 51 | left: 5px; 52 | } 53 | 54 | #dark-mode-toggler.fixed-bottom.hide:not(:focus) { 55 | bottom: -80px; 56 | } 57 | 58 | #dark-mode-toggler.relative { 59 | position: absolute; 60 | height: 44px; 61 | top: calc(2.4 * var(--global--spacing-vertical) - 44px); 62 | left: calc(50vw - var(--responsive--alignwide-width) / 2 - 0.5em); 63 | } 64 | 65 | .admin-bar #dark-mode-toggler.relative { 66 | top: calc(2.4 * var(--global--spacing-vertical) - 44px + 32px); 67 | } 68 | } 69 | @media only screen and (max-width: 782px) { 70 | 71 | .admin-bar #dark-mode-toggler.relative { 72 | top: calc(2.4 * var(--global--spacing-vertical) - 44px + 46px); 73 | } 74 | } 75 | @media only screen and (max-width: 481px) { 76 | 77 | .admin-bar #dark-mode-toggler.relative { 78 | top: calc(2.4 * var(--global--spacing-vertical) - 44px + 26px); 79 | } 80 | } 81 | @media only screen and (max-width: 481px) { 82 | 83 | body:not(.primary-navigation-open) #dark-mode-toggler.relative ~ nav { 84 | top: 88px; 85 | } 86 | } 87 | @media only screen { 88 | 89 | .primary-navigation-open #dark-mode-toggler { 90 | display: none; 91 | } 92 | } 93 | @media only screen { 94 | 95 | #dark-mode-toggler:hover, 96 | #dark-mode-toggler:focus { 97 | color: var(--button--color-background-active); 98 | border: 2px solid var(--button--color-text-active); 99 | background-color: var(--button--color-text-active); 100 | } 101 | } 102 | @media only screen { 103 | 104 | .is-IE #dark-mode-toggler { 105 | display: none; 106 | } 107 | } 108 | @media only screen and (prefers-reduced-motion: no-preference) { 109 | 110 | #dark-mode-toggler.fixed-bottom { 111 | transition: bottom 0.5s; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/web/app/themes/twentytwentyone/assets/css/style-dark-mode.css: -------------------------------------------------------------------------------- 1 | /* OS dark theme preference */ 2 | @media only screen { 3 | 4 | .is-dark-theme.is-dark-theme { 5 | --global--color-background: var(--global--color-dark-gray); 6 | --global--color-primary: var(--global--color-light-gray); 7 | --global--color-secondary: var(--global--color-light-gray); 8 | --button--color-text: var(--global--color-background); 9 | --button--color-text-hover: var(--global--color-secondary); 10 | --button--color-text-active: var(--global--color-secondary); 11 | --button--color-background: var(--global--color-secondary); 12 | --button--color-background-active: var(--global--color-background); 13 | --global--color-border: #9ea1a7; 14 | 15 | /* Block: Table */ 16 | --table--stripes-border-color: rgba(240, 240, 240, 0.15); 17 | --table--stripes-background-color: rgba(240, 240, 240, 0.15); 18 | } 19 | 20 | .is-dark-theme img { 21 | filter: brightness(0.85) contrast(1.1); 22 | } 23 | 24 | .respect-color-scheme-preference.is-dark-theme body { 25 | background-color: var(--global--color-background); 26 | } 27 | 28 | #dark-mode-toggler { 29 | cursor: pointer; 30 | display: flex; 31 | align-items: center; 32 | justify-content: center; 33 | font-size: var(--global--font-size-xs); 34 | padding: 0.5em; 35 | min-height: 44px; 36 | min-width: max-content; 37 | border: 2px solid currentColor; 38 | box-shadow: none; 39 | background: var(--button--color-text); 40 | color: var(--button--color-background); 41 | z-index: 9998; 42 | } 43 | 44 | .no-js #dark-mode-toggler { 45 | display: none; 46 | } 47 | 48 | #dark-mode-toggler.fixed-bottom { 49 | position: fixed; 50 | bottom: 5px; 51 | right: 5px; 52 | } 53 | 54 | #dark-mode-toggler.fixed-bottom.hide:not(:focus) { 55 | bottom: -80px; 56 | } 57 | 58 | #dark-mode-toggler.relative { 59 | position: absolute; 60 | height: 44px; 61 | top: calc(2.4 * var(--global--spacing-vertical) - 44px); 62 | right: calc(50vw - var(--responsive--alignwide-width) / 2 - 0.5em); 63 | } 64 | 65 | .admin-bar #dark-mode-toggler.relative { 66 | top: calc(2.4 * var(--global--spacing-vertical) - 44px + 32px); 67 | } 68 | } 69 | @media only screen and (max-width: 782px) { 70 | 71 | .admin-bar #dark-mode-toggler.relative { 72 | top: calc(2.4 * var(--global--spacing-vertical) - 44px + 46px); 73 | } 74 | } 75 | @media only screen and (max-width: 481px) { 76 | 77 | .admin-bar #dark-mode-toggler.relative { 78 | top: calc(2.4 * var(--global--spacing-vertical) - 44px + 26px); 79 | } 80 | } 81 | @media only screen and (max-width: 481px) { 82 | 83 | body:not(.primary-navigation-open) #dark-mode-toggler.relative ~ nav { 84 | top: 88px; 85 | } 86 | } 87 | @media only screen { 88 | 89 | .primary-navigation-open #dark-mode-toggler { 90 | display: none; 91 | } 92 | } 93 | @media only screen { 94 | 95 | #dark-mode-toggler:hover, 96 | #dark-mode-toggler:focus { 97 | color: var(--button--color-background-active); 98 | border: 2px solid var(--button--color-text-active); 99 | background-color: var(--button--color-text-active); 100 | } 101 | } 102 | @media only screen { 103 | 104 | .is-IE #dark-mode-toggler { 105 | display: none; 106 | } 107 | } 108 | @media only screen and (prefers-reduced-motion: no-preference) { 109 | 110 | #dark-mode-toggler.fixed-bottom { 111 | transition: bottom 0.5s; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/web/app/themes/twentytwentyone/assets/css/style-dark-mode.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sourceRoot":"","sources":["../sass/style-dark-mode.scss"],"names":[],"mappings":"AAAA;AACA;EAEC;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;AAEA;IACA;IACA;;EAGD;IACC;;EAGD;IACC;;EAGD;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;EAEA;IACC;;EAGD;IACC;IACA;IACA;;EAEA;IACC;;EAIF;IACC;IACA;IACA;IACA;;EAEA;IACC;;;AAEA;EAHD;IAIE;;;AAGD;EAPD;IAQE;;;AAOA;EADD;IAEE;;;AA7EN;EAmFE;IACC;;;AApFH;EAuFE;IAEC;IACA;IACA;;;AA3FH;EA8FE;IACC;;;AAIA;EADD;IAEE","file":"style-dark-mode.css"} -------------------------------------------------------------------------------- /src/web/app/themes/twentytwentyone/assets/css/style-editor-customizer.css: -------------------------------------------------------------------------------- 1 | /** 2 | * These styles are generated by the Customizer and only loaded when a custom color scheme is active. 3 | */ 4 | -------------------------------------------------------------------------------- /src/web/app/themes/twentytwentyone/assets/images/Daffodils.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/urre/wordpress-nginx-docker-compose/45b5f6a55ee7e8fe83fd4c365024af5fdc7c4cc1/src/web/app/themes/twentytwentyone/assets/images/Daffodils.jpg -------------------------------------------------------------------------------- /src/web/app/themes/twentytwentyone/assets/images/Reading.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/urre/wordpress-nginx-docker-compose/45b5f6a55ee7e8fe83fd4c365024af5fdc7c4cc1/src/web/app/themes/twentytwentyone/assets/images/Reading.jpg -------------------------------------------------------------------------------- /src/web/app/themes/twentytwentyone/assets/images/in-the-bois-de-boulogne.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/urre/wordpress-nginx-docker-compose/45b5f6a55ee7e8fe83fd4c365024af5fdc7c4cc1/src/web/app/themes/twentytwentyone/assets/images/in-the-bois-de-boulogne.jpg -------------------------------------------------------------------------------- /src/web/app/themes/twentytwentyone/assets/images/playing-in-the-sand.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/urre/wordpress-nginx-docker-compose/45b5f6a55ee7e8fe83fd4c365024af5fdc7c4cc1/src/web/app/themes/twentytwentyone/assets/images/playing-in-the-sand.jpg -------------------------------------------------------------------------------- /src/web/app/themes/twentytwentyone/assets/images/roses-tremieres-hollyhocks-1884.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/urre/wordpress-nginx-docker-compose/45b5f6a55ee7e8fe83fd4c365024af5fdc7c4cc1/src/web/app/themes/twentytwentyone/assets/images/roses-tremieres-hollyhocks-1884.jpg -------------------------------------------------------------------------------- /src/web/app/themes/twentytwentyone/assets/images/self-portrait-1885.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/urre/wordpress-nginx-docker-compose/45b5f6a55ee7e8fe83fd4c365024af5fdc7c4cc1/src/web/app/themes/twentytwentyone/assets/images/self-portrait-1885.jpg -------------------------------------------------------------------------------- /src/web/app/themes/twentytwentyone/assets/images/the-garden-at-bougival-1884.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/urre/wordpress-nginx-docker-compose/45b5f6a55ee7e8fe83fd4c365024af5fdc7c4cc1/src/web/app/themes/twentytwentyone/assets/images/the-garden-at-bougival-1884.jpg -------------------------------------------------------------------------------- /src/web/app/themes/twentytwentyone/assets/images/villa-with-orange-trees-nice.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/urre/wordpress-nginx-docker-compose/45b5f6a55ee7e8fe83fd4c365024af5fdc7c4cc1/src/web/app/themes/twentytwentyone/assets/images/villa-with-orange-trees-nice.jpg -------------------------------------------------------------------------------- /src/web/app/themes/twentytwentyone/assets/images/young-woman-in-mauve.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/urre/wordpress-nginx-docker-compose/45b5f6a55ee7e8fe83fd4c365024af5fdc7c4cc1/src/web/app/themes/twentytwentyone/assets/images/young-woman-in-mauve.jpg -------------------------------------------------------------------------------- /src/web/app/themes/twentytwentyone/assets/js/customize-helpers.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Get luminance from a HEX color. 3 | * 4 | * @since Twenty Twenty-One 1.0 5 | * 6 | * @param {string} hex - The hex color. 7 | * 8 | * @return {number} - Returns the luminance, number between 0 and 255. 9 | */ 10 | function twentytwentyoneGetHexLum( hex ) { // jshint ignore:line 11 | var rgb = twentytwentyoneGetRgbFromHex( hex ); 12 | return Math.round( ( 0.2126 * rgb.r ) + ( 0.7152 * rgb.g ) + ( 0.0722 * rgb.b ) ); 13 | } 14 | 15 | /** 16 | * Get RGB from HEX. 17 | * 18 | * @since Twenty Twenty-One 1.0 19 | * 20 | * @param {string} hex - The hex color. 21 | * 22 | * @return {Object} - Returns an object {r, g, b} 23 | */ 24 | function twentytwentyoneGetRgbFromHex( hex ) { 25 | var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i, 26 | result; 27 | 28 | // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF"). 29 | hex = hex.replace( shorthandRegex, function( m, r, g, b ) { 30 | return r.toString() + r.toString() + g.toString() + g.toString() + b.toString() + b.toString(); 31 | } ); 32 | 33 | result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec( hex ); 34 | return result ? { 35 | r: parseInt( result[1], 16 ), 36 | g: parseInt( result[2], 16 ), 37 | b: parseInt( result[3], 16 ) 38 | } : null; 39 | } 40 | -------------------------------------------------------------------------------- /src/web/app/themes/twentytwentyone/assets/js/customize-preview.js: -------------------------------------------------------------------------------- 1 | /* global twentytwentyoneGetHexLum, jQuery */ 2 | ( function() { 3 | // Add listener for the "background_color" control. 4 | wp.customize( 'background_color', function( value ) { 5 | value.bind( function( to ) { 6 | var lum = twentytwentyoneGetHexLum( to ), 7 | isDark = 127 > lum, 8 | textColor = ! isDark ? 'var(--global--color-dark-gray)' : 'var(--global--color-light-gray)', 9 | tableColor = ! isDark ? 'var(--global--color-light-gray)' : 'var(--global--color-dark-gray)', 10 | stylesheetID = 'twentytwentyone-customizer-inline-styles', 11 | stylesheet, 12 | styles; 13 | 14 | // Modify the html & body classes depending on whether this is a dark background or not. 15 | if ( isDark ) { 16 | document.body.classList.add( 'is-dark-theme' ); 17 | document.documentElement.classList.add( 'is-dark-theme' ); 18 | document.body.classList.remove( 'is-light-theme' ); 19 | document.documentElement.classList.remove( 'is-light-theme' ); 20 | document.documentElement.classList.remove( 'respect-color-scheme-preference' ); 21 | } else { 22 | document.body.classList.remove( 'is-dark-theme' ); 23 | document.documentElement.classList.remove( 'is-dark-theme' ); 24 | document.body.classList.add( 'is-light-theme' ); 25 | document.documentElement.classList.add( 'is-light-theme' ); 26 | if ( wp.customize( 'respect_user_color_preference' ).get() ) { 27 | document.documentElement.classList.add( 'respect-color-scheme-preference' ); 28 | } 29 | } 30 | 31 | // Toggle the white background class. 32 | if ( 225 <= lum ) { 33 | document.body.classList.add( 'has-background-white' ); 34 | } else { 35 | document.body.classList.remove( 'has-background-white' ); 36 | } 37 | 38 | stylesheet = jQuery( '#' + stylesheetID ); 39 | styles = ''; 40 | // If the stylesheet doesn't exist, create it and append it to . 41 | if ( ! stylesheet.length ) { 42 | jQuery( '#twenty-twenty-one-style-inline-css' ).after( '' ); 43 | stylesheet = jQuery( '#' + stylesheetID ); 44 | } 45 | 46 | // Generate the styles. 47 | styles += '--global--color-primary:' + textColor + ';'; 48 | styles += '--global--color-secondary:' + textColor + ';'; 49 | styles += '--global--color-background:' + to + ';'; 50 | 51 | styles += '--button--color-background:' + textColor + ';'; 52 | styles += '--button--color-text:' + to + ';'; 53 | styles += '--button--color-text-hover:' + textColor + ';'; 54 | 55 | styles += '--table--stripes-border-color:' + tableColor + ';'; 56 | styles += '--table--stripes-background-color:' + tableColor + ';'; 57 | 58 | // Add the styles. 59 | stylesheet.html( ':root{' + styles + '}' ); 60 | } ); 61 | } ); 62 | }() ); 63 | -------------------------------------------------------------------------------- /src/web/app/themes/twentytwentyone/assets/js/customize.js: -------------------------------------------------------------------------------- 1 | /* global twentytwentyoneGetHexLum */ 2 | 3 | ( function() { 4 | // Wait until the customizer has finished loading. 5 | wp.customize.bind( 'ready', function() { 6 | // Hide the "respect_user_color_preference" setting if the background-color is dark. 7 | if ( 127 > twentytwentyoneGetHexLum( wp.customize( 'background_color' ).get() ) ) { 8 | wp.customize.control( 'respect_user_color_preference' ).deactivate(); 9 | wp.customize.control( 'respect_user_color_preference_notice' ).deactivate(); 10 | } 11 | 12 | // Handle changes to the background-color. 13 | wp.customize( 'background_color', function( setting ) { 14 | setting.bind( function( value ) { 15 | if ( 127 > twentytwentyoneGetHexLum( value ) ) { 16 | wp.customize.control( 'respect_user_color_preference' ).deactivate(); 17 | wp.customize.control( 'respect_user_color_preference_notice' ).activate(); 18 | } else { 19 | wp.customize.control( 'respect_user_color_preference' ).activate(); 20 | wp.customize.control( 'respect_user_color_preference_notice' ).deactivate(); 21 | } 22 | } ); 23 | } ); 24 | } ); 25 | }() ); 26 | -------------------------------------------------------------------------------- /src/web/app/themes/twentytwentyone/assets/js/dark-mode-toggler.js: -------------------------------------------------------------------------------- 1 | function toggleDarkMode() { // jshint ignore:line 2 | var toggler = document.getElementById( 'dark-mode-toggler' ); 3 | 4 | if ( 'false' === toggler.getAttribute( 'aria-pressed' ) ) { 5 | toggler.setAttribute( 'aria-pressed', 'true' ); 6 | document.documentElement.classList.add( 'is-dark-theme' ); 7 | document.body.classList.add( 'is-dark-theme' ); 8 | window.localStorage.setItem( 'twentytwentyoneDarkMode', 'yes' ); 9 | } else { 10 | toggler.setAttribute( 'aria-pressed', 'false' ); 11 | document.documentElement.classList.remove( 'is-dark-theme' ); 12 | document.body.classList.remove( 'is-dark-theme' ); 13 | window.localStorage.setItem( 'twentytwentyoneDarkMode', 'no' ); 14 | } 15 | } 16 | 17 | function twentytwentyoneIsDarkMode() { 18 | var isDarkMode = window.matchMedia( '(prefers-color-scheme: dark)' ).matches; 19 | 20 | if ( 'yes' === window.localStorage.getItem( 'twentytwentyoneDarkMode' ) ) { 21 | isDarkMode = true; 22 | } else if ( 'no' === window.localStorage.getItem( 'twentytwentyoneDarkMode' ) ) { 23 | isDarkMode = false; 24 | } 25 | 26 | return isDarkMode; 27 | } 28 | 29 | function darkModeInitialLoad() { 30 | var toggler = document.getElementById( 'dark-mode-toggler' ), 31 | isDarkMode = twentytwentyoneIsDarkMode(); 32 | 33 | if ( isDarkMode ) { 34 | document.documentElement.classList.add( 'is-dark-theme' ); 35 | document.body.classList.add( 'is-dark-theme' ); 36 | } else { 37 | document.documentElement.classList.remove( 'is-dark-theme' ); 38 | document.body.classList.remove( 'is-dark-theme' ); 39 | } 40 | 41 | if ( toggler && isDarkMode ) { 42 | toggler.setAttribute( 'aria-pressed', 'true' ); 43 | } 44 | } 45 | 46 | function darkModeRepositionTogglerOnScroll() { 47 | 48 | var toggler = document.getElementById( 'dark-mode-toggler' ), 49 | prevScroll = window.scrollY || document.documentElement.scrollTop, 50 | currentScroll, 51 | 52 | checkScroll = function() { 53 | currentScroll = window.scrollY || document.documentElement.scrollTop; 54 | if ( 55 | currentScroll + ( window.innerHeight * 1.5 ) > document.body.clientHeight || 56 | currentScroll < prevScroll 57 | ) { 58 | toggler.classList.remove( 'hide' ); 59 | } else if ( currentScroll > prevScroll && 250 < currentScroll ) { 60 | toggler.classList.add( 'hide' ); 61 | } 62 | prevScroll = currentScroll; 63 | }; 64 | 65 | if ( toggler ) { 66 | window.addEventListener( 'scroll', checkScroll ); 67 | } 68 | } 69 | 70 | darkModeInitialLoad(); 71 | darkModeRepositionTogglerOnScroll(); 72 | -------------------------------------------------------------------------------- /src/web/app/themes/twentytwentyone/assets/js/editor-dark-mode-support.js: -------------------------------------------------------------------------------- 1 | /* global twentytwentyoneIsDarkMode, setTimeout */ 2 | 3 | // Check the color scheme preference and inject the classes if necessary. 4 | if ( document.body.classList.contains( 'twentytwentyone-supports-dark-theme' ) ) { 5 | twentytwentyoneDarkModeEditorInit(); 6 | } 7 | 8 | /** 9 | * Once the editor loads, add the dark mode class. 10 | * 11 | * Wait for the editor to load by periodically checking for an element, then we add the classes. 12 | * 13 | * @since Twenty Twenty-One 1.0 14 | * 15 | * @param {number} attempt Track the number of tries 16 | * @return {void} 17 | */ 18 | function twentytwentyoneDarkModeEditorInit( attempt ) { 19 | var container = document.querySelector( '.block-editor__typewriter' ), 20 | maxAttempts = 8; 21 | 22 | // Set the initial attempt if it's undefined. 23 | attempt = attempt || 0; 24 | 25 | if ( twentytwentyoneIsDarkMode() ) { 26 | if ( null === container ) { 27 | // Try again. 28 | if ( attempt < maxAttempts ) { 29 | setTimeout( 30 | function() { 31 | twentytwentyoneDarkModeEditorInit( attempt + 1 ); 32 | }, 33 | // Double the delay, give the server some time to breathe. 34 | 25 * Math.pow( 2, attempt ) 35 | ); 36 | } 37 | return; 38 | } 39 | 40 | document.body.classList.add( 'is-dark-theme' ); 41 | document.documentElement.classList.add( 'is-dark-theme' ); 42 | container.classList.add( 'is-dark-theme' ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/web/app/themes/twentytwentyone/assets/js/editor.js: -------------------------------------------------------------------------------- 1 | /* global setTimeout */ 2 | wp.domReady( function() { 3 | // Unregister "Wide" Separator Style. 4 | wp.blocks.unregisterBlockStyle( 'core/separator', 'wide' ); 5 | 6 | // Add to ".block-editor__typewriter" the "is-dark-theme" class if needed. 7 | function twentytwentyoneCopyDarkThemeClass() { 8 | var editor, 9 | attemptDelay = 25, 10 | attempt = 0, 11 | maxAttempts = 10; 12 | 13 | if ( ! document.body.classList.contains( 'is-dark-theme' ) ) { 14 | return; 15 | } 16 | 17 | editor = document.querySelector( '.block-editor__typewriter' ); 18 | if ( null === editor ) { 19 | // Try again. 20 | if ( attempt < maxAttempts ) { 21 | setTimeout( function() { 22 | twentytwentyoneCopyDarkThemeClass(); 23 | }, attemptDelay ); 24 | 25 | // Increment the attempts counter. 26 | attempt++; 27 | 28 | // Double the delay, give the server some time to breathe. 29 | attemptDelay *= 2; 30 | } 31 | return; 32 | } 33 | 34 | editor.classList.add( 'is-dark-theme' ); 35 | } 36 | 37 | twentytwentyoneCopyDarkThemeClass(); 38 | } ); 39 | -------------------------------------------------------------------------------- /src/web/app/themes/twentytwentyone/assets/js/palette-colorpicker.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Script for our custom colorpicker control. 3 | * 4 | * This is copied from wp-admin/js/customize-controls.js 5 | * with a few tweaks: 6 | * Removed the hue picker script because we don't use it here 7 | * Added the "palettes" argument in wpColorPicker(). 8 | * 9 | * @since Twenty Twenty-One 1.0 10 | */ 11 | wp.customize.controlConstructor['twenty-twenty-one-color'] = wp.customize.Control.extend( { 12 | ready: function() { 13 | var control = this, 14 | updating = false, 15 | picker; 16 | 17 | picker = this.container.find( '.color-picker-hex' ); 18 | picker.val( control.setting() ).wpColorPicker( { 19 | palettes: control.params.palette, 20 | change: function() { 21 | updating = true; 22 | control.setting.set( picker.wpColorPicker( 'color' ) ); 23 | updating = false; 24 | }, 25 | clear: function() { 26 | updating = true; 27 | control.setting.set( '' ); 28 | updating = false; 29 | } 30 | } ); 31 | 32 | control.setting.bind( function( value ) { 33 | // Bail if the update came from the control itself. 34 | if ( updating ) { 35 | return; 36 | } 37 | picker.val( value ); 38 | picker.wpColorPicker( 'color', value ); 39 | } ); 40 | 41 | // Collapse color picker when hitting Esc instead of collapsing the current section. 42 | control.container.on( 'keydown', function( event ) { 43 | var pickerContainer; 44 | if ( 27 !== event.which ) { // Esc. 45 | return; 46 | } 47 | pickerContainer = control.container.find( '.wp-picker-container' ); 48 | if ( pickerContainer.hasClass( 'wp-picker-active' ) ) { 49 | picker.wpColorPicker( 'close' ); 50 | control.container.find( '.wp-color-result' ).focus(); 51 | event.stopPropagation(); // Prevent section from being collapsed. 52 | } 53 | } ); 54 | } 55 | } ); 56 | -------------------------------------------------------------------------------- /src/web/app/themes/twentytwentyone/assets/js/polyfills.js: -------------------------------------------------------------------------------- 1 | /** 2 | * File polyfills.js. 3 | * 4 | * Polyfills for IE11. 5 | */ 6 | 7 | /** 8 | * Polyfill for Element.closest() because we need to support IE11. 9 | * 10 | * @since Twenty Twenty-One 1.0 11 | * 12 | * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/closest 13 | */ 14 | if ( ! Element.prototype.matches ) { 15 | Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector; 16 | } 17 | 18 | if ( ! Element.prototype.closest ) { 19 | Element.prototype.closest = function( s ) { 20 | var el = this; 21 | do { 22 | if ( Element.prototype.matches.call( el, s ) ) { 23 | return el; 24 | } 25 | el = el.parentElement || el.parentNode; 26 | } while ( el !== null && el.nodeType === 1 ); 27 | return null; 28 | }; 29 | } 30 | 31 | /** 32 | * Polyfill for NodeList.foreach() because we need to support IE11. 33 | * 34 | * @since Twenty Twenty-One 1.0 35 | * 36 | * @see https://developer.mozilla.org/en-US/docs/Web/API/NodeList/forEach 37 | */ 38 | if ( window.NodeList && ! NodeList.prototype.forEach ) { 39 | NodeList.prototype.forEach = function( callback, thisArg ) { 40 | var i; 41 | thisArg = thisArg || window; 42 | for ( i = 0; i < this.length; i++ ) { 43 | callback.call( thisArg, this[i], i, this ); 44 | } 45 | }; 46 | } 47 | -------------------------------------------------------------------------------- /src/web/app/themes/twentytwentyone/assets/js/responsive-embeds.js: -------------------------------------------------------------------------------- 1 | /** 2 | * File responsive-embeds.js. 3 | * 4 | * Make embeds responsive so they don't overflow their container. 5 | */ 6 | 7 | /** 8 | * Add max-width & max-height to