├── .browserlistrc ├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .github ├── CODEOWNERS ├── CODE_OF_CONDUCT.md ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml └── workflows │ ├── assertions.yml │ └── codeql-analysis.yml ├── .gitignore ├── .markdownlint.json ├── .markdownlintignore ├── .nvmrc ├── .prettierignore ├── .prettierrc.js ├── .stylelintignore ├── .stylelintrc.json ├── .vscode ├── extensions.json ├── settings.json └── snipsnap.code-snippets ├── 404.php ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── archive.php ├── assets ├── critical.js ├── fonts │ ├── inter │ │ ├── Inter-Black.woff2 │ │ ├── Inter-Bold.woff2 │ │ ├── Inter-Light.woff2 │ │ ├── Inter-Regular.woff2 │ │ └── LICENSE.txt │ └── oxygen │ │ ├── OFL.txt │ │ ├── oxygen-mono-v14-latin-regular.woff2 │ │ ├── oxygen-v15-latin-300.woff2 │ │ ├── oxygen-v15-latin-700.woff2 │ │ └── oxygen-v15-latin-regular.woff2 ├── images │ ├── icons │ │ ├── caret-down.svg │ │ ├── close.svg │ │ ├── facebook-square.svg │ │ ├── hamburger.svg │ │ ├── instagram-square.svg │ │ ├── linkedin-square.svg │ │ ├── pause.svg │ │ ├── play.svg │ │ └── twitter-square.svg │ └── placeholder.png ├── index.js ├── js │ ├── blocks │ │ ├── custom │ │ │ └── .gitkeep │ │ └── index.js │ ├── global │ │ ├── index.js │ │ ├── js-enabled.js │ │ └── window-ready.js │ ├── index.js │ ├── template-tags │ │ ├── index.js │ │ ├── mobile-menu.js │ │ ├── modal.js │ │ ├── navigation-primary.js │ │ ├── off-canvas.js │ │ ├── table.js │ │ └── video.js │ └── templates │ │ └── index.js ├── scss │ ├── blocks │ │ ├── core │ │ │ ├── _navigation.scss │ │ │ └── index.scss │ │ ├── custom │ │ │ ├── .gitkeep │ │ │ └── index.scss │ │ └── index.scss │ ├── critical.scss │ ├── critical │ │ ├── _header.scss │ │ └── index.scss │ ├── global │ │ ├── _editor.scss │ │ ├── _footer.scss │ │ ├── _global.scss │ │ └── index.scss │ ├── index.scss │ ├── tailwind.scss │ ├── template-tags │ │ ├── _comments.scss │ │ ├── _forms.scss │ │ └── index.scss │ └── templates │ │ └── index.scss └── tailwind-preflight.css ├── blocks └── .gitkeep ├── comments.php ├── composer.json ├── composer.lock ├── footer.php ├── front-page.php ├── functions.php ├── header.php ├── home.php ├── inc ├── README.md ├── customizer │ ├── assets │ │ └── scripts │ │ │ ├── livepreview.js │ │ │ └── tinymce.js │ ├── class-text-editor-custom-control.php │ ├── customizer.php │ ├── panels.php │ ├── sections.php │ └── settings.php ├── functions │ ├── get-attachment-id-from-url.php │ ├── get-categorized-blog.php │ ├── get-custom-logo-url.php │ ├── get-svg.php │ ├── get-trimmed-excerpt.php │ ├── get-trimmed-title.php │ └── security.php ├── hooks │ ├── add-og-tags.php │ ├── body-classes.php │ ├── category-transient-flusher.php │ ├── content-more-link.php │ ├── custom-mime-types.php │ ├── disable-wpautop-for-gutenberg.php │ ├── excerpt-more.php │ ├── get-post-content.php │ ├── main-classes.php │ ├── print-customizer-footer-scripts.php │ ├── print-customizer-header-scripts.php │ ├── register-block-categories.php │ ├── register-block-pattern-category.php │ └── remove-archive-title-prefix.php ├── post-types │ └── .gitkeep ├── setup │ ├── critical-css.php │ ├── preload-assets.php │ ├── preload-scripts.php │ ├── scripts.php │ └── setup.php ├── shortcodes │ └── .gitkeep ├── template-tags │ ├── print-comments.php │ ├── print-entry-footer.php │ ├── print-numeric-pagination.php │ ├── print-post-author.php │ ├── print-post-date.php │ └── print-post-taxonomies.php └── wpcli │ ├── block-starter │ ├── block.json │ ├── block.php │ ├── editor.asset.php │ ├── editor.js │ ├── editor.scss │ ├── script.asset.php │ ├── script.js │ └── style.scss │ ├── class-blocks-scaffold.php │ └── wpcli.php ├── index.php ├── lefthook.yml ├── package-lock.json ├── package.json ├── page.php ├── parts ├── .gitkeep ├── footer.html └── header.html ├── patterns ├── footer.php └── header.php ├── phpcs.xml.dist ├── postcss.config.js ├── screenshot.png ├── search.php ├── searchform.php ├── single.php ├── style.css ├── tailwind.config.js ├── template-parts ├── content-none.php ├── content-page.php ├── content-password-protected.php ├── content-search.php └── content.php ├── templates └── index.html ├── theme.json ├── webpack.config.js └── webpack.prod.js /.browserlistrc: -------------------------------------------------------------------------------- 1 | extends @wordpress/browserslist-config 2 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # This file is for unifying the coding style for different editors and IDEs 2 | # editorconfig.org 3 | 4 | # WordPress Coding Standards 5 | # https://make.wordpress.org/core/handbook/coding-standards/ 6 | 7 | root = true 8 | 9 | [*] 10 | charset = utf-8 11 | end_of_line = lf 12 | insert_final_newline = true 13 | trim_trailing_whitespace = true 14 | indent_style = tab 15 | 16 | [*.yml] 17 | indent_style = space 18 | indent_size = 2 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | build 2 | node_modules 3 | vendor 4 | *.min.js 5 | !/.*.js 6 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [ 3 | 'plugin:@wordpress/eslint-plugin/recommended', 4 | 'plugin:eslint-comments/recommended', 5 | ], 6 | parserOptions: { 7 | ecmaVersion: 2021, 8 | }, 9 | root: true, 10 | env: { 11 | browser: true, 12 | es6: true, 13 | jquery: true, 14 | }, 15 | rules: { 16 | '@wordpress/no-global-event-listener': 0, // Disable. We don't use React-based components. 17 | camelcase: 1, 18 | }, 19 | }; 20 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Code Owners 2 | 3 | ## 4 | 5 | * @khleomix 6 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | - Using welcoming and inclusive language 12 | - Being respectful of differing viewpoints and experiences 13 | - Gracefully accepting constructive criticism 14 | - Focusing on what is best for the community 15 | - Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | - The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | - Trolling, insulting/derogatory comments, and personal or political attacks 21 | - Public or private harassment 22 | - Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | - Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at contact@webdevstudios.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Closes 2 | 3 | 7 | 8 | ## What type of PR is this? (put an x to all applicable) 9 | 10 | - [ ] 🍕 Feature 11 | - [ ] 🐛 Bug Fix 12 | - [ ] 📝 Documentation Update 13 | - [ ] 🎨 Style 14 | - [ ] 🧑‍💻 Code Refactor 15 | - [ ] 🔥 Performance Improvements 16 | - [ ] ✅ Test 17 | - [ ] 🤖 Build 18 | - [ ] 🔁 CI 19 | - [ ] 📦 Chore (Release) 20 | - [ ] ⏩ Revert 21 | 22 | ## Link to test 23 | 24 | 28 | 29 | ## Description 30 | 31 | 35 | 36 | ## Related Tickets & Documents 37 | 38 | 42 | 43 | ## Mobile & Desktop Screenshots/Recordings 44 | 45 | 49 | 50 | 51 | ## Added tests? 52 | 53 | - [ ] 👍 yes 54 | - [ ] 🙅 no, because they aren't needed 55 | - [ ] 🙋 no, because I need help 56 | 57 | ## Added to documentation? 58 | 59 | - [ ] 📜 README.md 60 | - [ ] 📓 [Confluence](https://webdevstudios.atlassian.net/wiki/spaces/wds1/pages/2988474566/Feature+Documentation) 61 | - [ ] 🙅 no documentation needed 62 | 63 | ----- 64 | 65 | ## Reviewer's Testing Checklist 66 | 67 | 70 | 71 | As a reviewer, please ensure the following testing criteria are met and validated before approving this Pull Request. 72 | 73 | - [ ] **Visual Regression Testing:** Ensure that existing functionality is not negatively impacted by the changes. 74 | - [ ] **Cross-Browser Compatibility:** Test on major browsers (Chrome, Firefox, Safari) to ensure compatibility. 75 | - [ ] **Mobile Responsiveness:** Confirm that the changes are responsive and functional on various mobile devices. 76 | - [ ] **Accessibility Testing:** Validate that the changes comply with accessibility standards. 77 | - [ ] **Linting:** Check that the code passes all linting checks (PHPCS, ESLint, SassLint). 78 | - [ ] **Theme Compatibility:** Ensure that the changes do not adversely affect the site's theme and styling. 79 | - [ ] **Plugin Compatibility:** Check if the changes are compatible with existing plugins and do not cause conflicts. 80 | - [ ] **Core Functionality:** Verify that the WordPress core functionalities are not disrupted. 81 | - [ ] **Custom Post Types and Taxonomies:** Confirm that any custom post types or taxonomies function as intended. 82 | - [ ] **Security Best Practices:** Ensure that the code follows WordPress security best practices. 83 | - [ ] **Performance:** Check for any performance issues, especially with database queries and page load times. 84 | - [ ] **SEO Considerations:** Where applicable, confirm that the changes do not negatively impact SEO elements. 85 | - [ ] **WordPress Coding Standards:** Ensure that the code adheres to WordPress coding standards. 86 | - [ ] **Documentation:** Ensure that any new features or changes are appropriately documented in the README.md or Confluence. 87 | - [ ] **User Acceptance Testing (UAT):** If applicable, confirm that stakeholders have reviewed and accepted the changes. 88 | - [ ] **Post-Deployment Tasks:** Check if there are any tasks that need to be performed after deployment. 89 | 90 | ## [optional] Additional Reviewer Notes or Considerations? 91 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: 'npm' 4 | directory: '/' 5 | schedule: 6 | interval: 'weekly' 7 | -------------------------------------------------------------------------------- /.github/workflows/assertions.yml: -------------------------------------------------------------------------------- 1 | name: Assertions 2 | 3 | on: 4 | pull_request: 5 | branches: [main] 6 | 7 | workflow_dispatch: 8 | 9 | jobs: 10 | lint: 11 | runs-on: ubuntu-latest 12 | 13 | strategy: 14 | matrix: 15 | node-version: [18.x] 16 | php-versions: ['8.0'] 17 | 18 | steps: 19 | - name: Checkout repository 20 | uses: actions/checkout@v4 21 | with: 22 | token: ${{ github.token }} 23 | 24 | - name: Setup PHP ${{ matrix.php-versions }} 25 | uses: shivammathur/setup-php@v2 26 | with: 27 | php-version: ${{ matrix.php-versions }} 28 | tools: composer:v2, phpcs 29 | 30 | - name: Setup Node ${{ matrix.node-version }} 31 | uses: actions/setup-node@v3 32 | with: 33 | node-version: ${{ matrix.node-version }} 34 | 35 | - name: Use NPM 9 36 | run: npm i -g npm@9 37 | 38 | - name: Cache Node dependencies 39 | uses: actions/cache@v3 40 | with: 41 | path: ~/.npm 42 | key: ${{ runner.OS }}-node-${{ hashFiles('**/package-lock.json') }} 43 | restore-keys: ${{ runner.OS }}-node- 44 | 45 | - name: Cache Composer dependencies 46 | uses: actions/cache@v3 47 | with: 48 | path: /tmp/composer-cache 49 | key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} 50 | restore-keys: ${{ runner.OS }}-composer- 51 | 52 | - name: Authorize Composer with WDS packagist server 53 | run: echo '${{ secrets.COMPOSER_AUTH }}' > $GITHUB_WORKSPACE/auth.json 54 | 55 | - name: Install dependencies 56 | run: | 57 | composer install --quiet 58 | npm ci --legacy-peer-deps --ignore-scripts --no-fund --no-audit --quiet 59 | 60 | - name: Lint PHP 61 | run: npm run lint:php 62 | 63 | - name: Lint scripts 64 | run: npm run lint:js 65 | 66 | - name: Lint styles 67 | run: npm run lint:css 68 | 69 | - name: Lint markdown 70 | run: npm run lint:md 71 | 72 | - name: Lint package.json 73 | run: npm run lint:pkg-json 74 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: 'CodeQL' 13 | 14 | on: 15 | push: 16 | branches: [main] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [main] 20 | schedule: 21 | - cron: '29 23 * * 3' 22 | workflow_dispatch: 23 | 24 | jobs: 25 | analyze: 26 | name: Analyze 27 | runs-on: ubuntu-latest 28 | permissions: 29 | actions: read 30 | contents: read 31 | security-events: write 32 | 33 | strategy: 34 | fail-fast: false 35 | matrix: 36 | language: ['javascript'] 37 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 38 | # Learn more: 39 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 40 | 41 | steps: 42 | - name: Checkout repository 43 | uses: actions/checkout@v4 44 | 45 | # Initializes the CodeQL tools for scanning. 46 | - name: Initialize CodeQL 47 | uses: github/codeql-action/init@v2 48 | with: 49 | languages: ${{ matrix.language }} 50 | # If you wish to specify custom queries, you can do so here or in a config file. 51 | # By default, queries listed here will override any specified in a config file. 52 | # Prefix the list here with "+" to use these queries and those in the config file. 53 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 54 | 55 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 56 | # If this step fails, then you should remove it and run the build manually (see below) 57 | - name: Autobuild 58 | uses: github/codeql-action/autobuild@v2 59 | 60 | # ℹ️ Command-line programs to run using the OS shell. 61 | # 📚 https://git.io/JvXDl 62 | 63 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 64 | # and modify them (or add more) to build your code if your project 65 | # uses a compiled language 66 | 67 | #- run: | 68 | # make bootstrap 69 | # make release 70 | 71 | - name: Perform CodeQL Analysis 72 | uses: github/codeql-action/analyze@v2 73 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # os 2 | *~ 3 | .DS_* 4 | .svn/ 5 | .cvs/ 6 | .hg/ 7 | *.bak 8 | *.swp 9 | Thumbs.db 10 | 11 | # node 12 | .env 13 | .env.local 14 | logs 15 | *.log 16 | npm-debug.log* 17 | yarn-debug.log* 18 | yarn-error.log* 19 | node_modules/ 20 | 21 | # composer 22 | vendor/ 23 | 24 | # theme 25 | build/ 26 | pa11y-ci-report/ 27 | -------------------------------------------------------------------------------- /.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "node_modules/@wordpress/scripts/config/.markdownlint.json", 3 | "MD007": { "indent": 1 } 4 | } 5 | -------------------------------------------------------------------------------- /.markdownlintignore: -------------------------------------------------------------------------------- 1 | build 2 | node_modules 3 | vendor 4 | CHANGELOG.md 5 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 18 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | build/ 2 | node_modules/ 3 | vendor/ 4 | style.css 5 | README.md 6 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | ...require( '@wordpress/prettier-config' ), 3 | overrides: [ 4 | { 5 | files: '*.scss', 6 | options: { 7 | singleQuote: true, 8 | }, 9 | }, 10 | ], 11 | }; 12 | -------------------------------------------------------------------------------- /.stylelintignore: -------------------------------------------------------------------------------- 1 | build/ 2 | vendor/ 3 | !.*.js 4 | -------------------------------------------------------------------------------- /.stylelintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "@wordpress/stylelint-config/scss", 4 | "stylelint-config-prettier" 5 | ], 6 | "rules": { 7 | "at-rule-empty-line-before": null, 8 | "declaration-colon-newline-after": null, 9 | "declaration-no-important": true, 10 | "function-parentheses-space-inside": null, 11 | "no-descending-specificity": null, 12 | "rule-empty-line-before": null, 13 | "selector-pseudo-class-parentheses-space-inside": null, 14 | "scss/at-rule-no-unknown": [ 15 | true, 16 | { 17 | "ignoreAtRules": [ 18 | "tailwind", 19 | "apply", 20 | "layer", 21 | "variants", 22 | "responsive", 23 | "screen" 24 | ] 25 | } 26 | ], 27 | "string-quotes": "single" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "bradlc.vscode-tailwindcss", 4 | "davidanson.vscode-markdownlint", 5 | "dbaeumer.vscode-eslint", 6 | "editorconfig.editorconfig", 7 | "esbenp.prettier-vscode", 8 | "snipsnapdev.snipsnap-vscode", 9 | "stylelint.vscode-stylelint", 10 | "valeryanm.vscode-phpsab" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "phpsab.standard": "WordPress", 3 | "phpsab.executablePathCS": "./vendor/bin/phpcs", 4 | "phpsab.executablePathCBF": "./vendor/bin/phpcbf", 5 | "editor.formatOnSave": true, 6 | "scss.validate": false, 7 | "[javascriptreact]": { 8 | "editor.defaultFormatter": "esbenp.prettier-vscode" 9 | }, 10 | "intelephense.format.enable": false, 11 | "intelephense.stubs": [ "acf", "wordpress", "standard", "Core", "pcre" ], 12 | 13 | "workbench.colorCustomizations": { 14 | "activityBar.background": "#3F4040", 15 | "activityBarBadge.background": "#f3713c", 16 | "activityBarBadge.foreground": "#fff", 17 | "statusBar.background": "#f3713c", 18 | "statusBar.foreground": "#fff" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.vscode/snipsnap.code-snippets: -------------------------------------------------------------------------------- 1 | 2 | 404 Not Found 3 | 4 |

404 Not Found

5 |
nginx
6 | 7 | 8 | -------------------------------------------------------------------------------- /404.php: -------------------------------------------------------------------------------- 1 | 13 | 14 |
15 | 16 |
17 | 20 | 21 |
22 | 23 |

24 | 25 | 26 | 27 |
28 |
29 | 30 |
31 | 32 | 33 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How To Contribute 2 | 3 | WebDevStudios welcomes contributions and bug fixes from third-parties. Here are the steps to get started: 4 | 5 | - Create an [Issue](https://github.com/WebDevStudios/wd_s/issues) so we can all discuss your idea 6 | - Fork wd_s 7 | - Create a feature/hotfix branch off [main](https://github.com/WebDevStudios/wd_s/tree/main) 8 | - Commit code changes to your feature/hotifx branch 9 | - Continue to merge master into your feature/hotifx branch so it stays current 10 | - Test across all major browsers 11 | - Accessibility testing (both WCAG 2.1AA and Section 508) 12 | - Must pass PHPCS, ESLint, and Stylelint assertions 13 | - Submit a [Pull Request](https://github.com/WebDevStudios/wd_s/pulls) and reference your Issue # 14 | - If everything tests well on our end, we may merge in your Pull Request 15 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The GNU General Public License, Version 2, June 1991 (GPLv2) 2 | 3 | > Copyright (C) 1989, 1991 Free Software Foundation, Inc. 4 | > 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 5 | 6 | Everyone is permitted to copy and distribute verbatim copies of this license 7 | document, but changing it is not allowed. 8 | 9 | ## Preamble 10 | 11 | The licenses for most software are designed to take away your freedom to share 12 | and change it. By contrast, the GNU General Public License is intended to 13 | guarantee your freedom to share and change free software--to make sure the 14 | software is free for all its users. This General Public License applies to most 15 | of the Free Software Foundation's software and to any other program whose 16 | authors commit to using it. (Some other Free Software Foundation software is 17 | covered by the GNU Lesser General Public License instead.) You can apply it to 18 | your programs, too. 19 | 20 | When we speak of free software, we are referring to freedom, not price. Our 21 | General Public Licenses are designed to make sure that you have the freedom to 22 | distribute copies of free software (and charge for this service if you wish), 23 | that you receive source code or can get it if you want it, that you can change 24 | the software or use pieces of it in new free programs; and that you know you can 25 | do these things. 26 | 27 | To protect your rights, we need to make restrictions that forbid anyone to deny 28 | you these rights or to ask you to surrender the rights. These restrictions 29 | translate to certain responsibilities for you if you distribute copies of the 30 | software, or if you modify it. 31 | 32 | For example, if you distribute copies of such a program, whether gratis or for a 33 | fee, you must give the recipients all the rights that you have. You must make 34 | sure that they, too, receive or can get the source code. And you must show them 35 | these terms so they know their rights. 36 | 37 | We protect your rights with two steps: (1) copyright the software, and (2) offer 38 | you this license which gives you legal permission to copy, distribute and/or 39 | modify the software. 40 | 41 | Also, for each author's protection and ours, we want to make certain that 42 | everyone understands that there is no warranty for this free software. If the 43 | software is modified by someone else and passed on, we want its recipients to 44 | know that what they have is not the original, so that any problems introduced by 45 | others will not reflect on the original authors' reputations. 46 | 47 | Finally, any free program is threatened constantly by software patents. We wish 48 | to avoid the danger that redistributors of a free program will individually 49 | obtain patent licenses, in effect making the program proprietary. To prevent 50 | this, we have made it clear that any patent must be licensed for everyone's free 51 | use or not licensed at all. 52 | 53 | The precise terms and conditions for copying, distribution and modification 54 | follow. 55 | 56 | ## Terms And Conditions For Copying, Distribution And Modification 57 | 58 | **0.** This License applies to any program or other work which contains a notice 59 | placed by the copyright holder saying it may be distributed under the terms of 60 | this General Public License. The "Program", below, refers to any such program or 61 | work, and a "work based on the Program" means either the Program or any 62 | derivative work under copyright law: that is to say, a work containing the 63 | Program or a portion of it, either verbatim or with modifications and/or 64 | translated into another language. (Hereinafter, translation is included without 65 | limitation in the term "modification".) Each licensee is addressed as "you". 66 | 67 | Activities other than copying, distribution and modification are not covered by 68 | this License; they are outside its scope. The act of running the Program is not 69 | restricted, and the output from the Program is covered only if its contents 70 | constitute a work based on the Program (independent of having been made by 71 | running the Program). Whether that is true depends on what the Program does. 72 | 73 | **1.** You may copy and distribute verbatim copies of the Program's source code 74 | as you receive it, in any medium, provided that you conspicuously and 75 | appropriately publish on each copy an appropriate copyright notice and 76 | disclaimer of warranty; keep intact all the notices that refer to this License 77 | and to the absence of any warranty; and give any other recipients of the Program 78 | a copy of this License along with the Program. 79 | 80 | You may charge a fee for the physical act of transferring a copy, and you may at 81 | your option offer warranty protection in exchange for a fee. 82 | 83 | **2.** You may modify your copy or copies of the Program or any portion of it, 84 | thus forming a work based on the Program, and copy and distribute such 85 | modifications or work under the terms of Section 1 above, provided that you also 86 | meet all of these conditions: 87 | 88 | - **a)** You must cause the modified files to carry prominent notices stating 89 | that you changed the files and the date of any change. 90 | 91 | - **b)** You must cause any work that you distribute or publish, that in whole 92 | or in part contains or is derived from the Program or any part thereof, to 93 | be licensed as a whole at no charge to all third parties under the terms of 94 | this License. 95 | 96 | - **c)** If the modified program normally reads commands interactively when 97 | run, you must cause it, when started running for such interactive use in the 98 | most ordinary way, to print or display an announcement including an 99 | appropriate copyright notice and a notice that there is no warranty (or 100 | else, saying that you provide a warranty) and that users may redistribute 101 | the program under these conditions, and telling the user how to view a copy 102 | of this License. (Exception: if the Program itself is interactive but does 103 | not normally print such an announcement, your work based on the Program is 104 | not required to print an announcement.) 105 | 106 | These requirements apply to the modified work as a whole. If identifiable 107 | sections of that work are not derived from the Program, and can be reasonably 108 | considered independent and separate works in themselves, then this License, and 109 | its terms, do not apply to those sections when you distribute them as separate 110 | works. But when you distribute the same sections as part of a whole which is a 111 | work based on the Program, the distribution of the whole must be on the terms of 112 | this License, whose permissions for other licensees extend to the entire whole, 113 | and thus to each and every part regardless of who wrote it. 114 | 115 | Thus, it is not the intent of this section to claim rights or contest your 116 | rights to work written entirely by you; rather, the intent is to exercise the 117 | right to control the distribution of derivative or collective works based on the 118 | Program. 119 | 120 | In addition, mere aggregation of another work not based on the Program with the 121 | Program (or with a work based on the Program) on a volume of a storage or 122 | distribution medium does not bring the other work under the scope of this 123 | License. 124 | 125 | **3.** You may copy and distribute the Program (or a work based on it, under 126 | Section 2) in object code or executable form under the terms of Sections 1 and 2 127 | above provided that you also do one of the following: 128 | 129 | - **a)** Accompany it with the complete corresponding machine-readable source 130 | code, which must be distributed under the terms of Sections 1 and 2 above on 131 | a medium customarily used for software interchange; or, 132 | 133 | - **b)** Accompany it with a written offer, valid for at least three years, to 134 | give any third party, for a charge no more than your cost of physically 135 | performing source distribution, a complete machine-readable copy of the 136 | corresponding source code, to be distributed under the terms of Sections 1 137 | and 2 above on a medium customarily used for software interchange; or, 138 | 139 | - **c)** Accompany it with the information you received as to the offer to 140 | distribute corresponding source code. (This alternative is allowed only for 141 | noncommercial distribution and only if you received the program in object 142 | code or executable form with such an offer, in accord with Subsection b 143 | above.) 144 | 145 | The source code for a work means the preferred form of the work for making 146 | modifications to it. For an executable work, complete source code means all the 147 | source code for all modules it contains, plus any associated interface 148 | definition files, plus the scripts used to control compilation and installation 149 | of the executable. However, as a special exception, the source code distributed 150 | need not include anything that is normally distributed (in either source or 151 | binary form) with the major components (compiler, kernel, and so on) of the 152 | operating system on which the executable runs, unless that component itself 153 | accompanies the executable. 154 | 155 | If distribution of executable or object code is made by offering access to copy 156 | from a designated place, then offering equivalent access to copy the source code 157 | from the same place counts as distribution of the source code, even though third 158 | parties are not compelled to copy the source along with the object code. 159 | 160 | **4.** You may not copy, modify, sublicense, or distribute the Program except as 161 | expressly provided under this License. Any attempt otherwise to copy, modify, 162 | sublicense or distribute the Program is void, and will automatically terminate 163 | your rights under this License. However, parties who have received copies, or 164 | rights, from you under this License will not have their licenses terminated so 165 | long as such parties remain in full compliance. 166 | 167 | **5.** You are not required to accept this License, since you have not signed 168 | it. However, nothing else grants you permission to modify or distribute the 169 | Program or its derivative works. These actions are prohibited by law if you do 170 | not accept this License. Therefore, by modifying or distributing the Program (or 171 | any work based on the Program), you indicate your acceptance of this License to 172 | do so, and all its terms and conditions for copying, distributing or modifying 173 | the Program or works based on it. 174 | 175 | **6.** Each time you redistribute the Program (or any work based on the 176 | Program), the recipient automatically receives a license from the original 177 | licensor to copy, distribute or modify the Program subject to these terms and 178 | conditions. You may not impose any further restrictions on the recipients' 179 | exercise of the rights granted herein. You are not responsible for enforcing 180 | compliance by third parties to this License. 181 | 182 | **7.** If, as a consequence of a court judgment or allegation of patent 183 | infringement or for any other reason (not limited to patent issues), conditions 184 | are imposed on you (whether by court order, agreement or otherwise) that 185 | contradict the conditions of this License, they do not excuse you from the 186 | conditions of this License. If you cannot distribute so as to satisfy 187 | simultaneously your obligations under this License and any other pertinent 188 | obligations, then as a consequence you may not distribute the Program at all. 189 | For example, if a patent license would not permit royalty-free redistribution of 190 | the Program by all those who receive copies directly or indirectly through you, 191 | then the only way you could satisfy both it and this License would be to refrain 192 | entirely from distribution of the Program. 193 | 194 | If any portion of this section is held invalid or unenforceable under any 195 | particular circumstance, the balance of the section is intended to apply and the 196 | section as a whole is intended to apply in other circumstances. 197 | 198 | It is not the purpose of this section to induce you to infringe any patents or 199 | other property right claims or to contest validity of any such claims; this 200 | section has the sole purpose of protecting the integrity of the free software 201 | distribution system, which is implemented by public license practices. Many 202 | people have made generous contributions to the wide range of software 203 | distributed through that system in reliance on consistent application of that 204 | system; it is up to the author/donor to decide if he or she is willing to 205 | distribute software through any other system and a licensee cannot impose that 206 | choice. 207 | 208 | This section is intended to make thoroughly clear what is believed to be a 209 | consequence of the rest of this License. 210 | 211 | **8.** If the distribution and/or use of the Program is restricted in certain 212 | countries either by patents or by copyrighted interfaces, the original copyright 213 | holder who places the Program under this License may add an explicit 214 | geographical distribution limitation excluding those countries, so that 215 | distribution is permitted only in or among countries not thus excluded. In such 216 | case, this License incorporates the limitation as if written in the body of this 217 | License. 218 | 219 | **9.** The Free Software Foundation may publish revised and/or new versions of 220 | the General Public License from time to time. Such new versions will be similar 221 | in spirit to the present version, but may differ in detail to address new 222 | problems or concerns. 223 | 224 | Each version is given a distinguishing version number. If the Program specifies 225 | a version number of this License which applies to it and "any later version", 226 | you have the option of following the terms and conditions either of that version 227 | or of any later version published by the Free Software Foundation. If the 228 | Program does not specify a version number of this License, you may choose any 229 | version ever published by the Free Software Foundation. 230 | 231 | **10.** If you wish to incorporate parts of the Program into other free programs 232 | whose distribution conditions are different, write to the author to ask for 233 | permission. For software which is copyrighted by the Free Software Foundation, 234 | write to the Free Software Foundation; we sometimes make exceptions for this. 235 | Our decision will be guided by the two goals of preserving the free status of 236 | all derivatives of our free software and of promoting the sharing and reuse of 237 | software generally. 238 | 239 | ## No Warranty 240 | 241 | **11.** BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR 242 | THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE 243 | STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM 244 | "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, 245 | BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 246 | PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 247 | PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 248 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 249 | 250 | **12.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 251 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE 252 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 253 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR 254 | INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA 255 | BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 256 | FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER 257 | OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 258 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wd_s 2 | 3 | [![Build Status](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2Fwebdevstudios%2Fwd_s%2Fbadge%3Fref%3Dmain&style=flat)](https://github.com/WebDevStudios/wd_s/actions) 4 | 5 | A starter theme from WebDevStudios. 6 | 7 | [![WebDevStudios. Your Success is Our Mission.](https://camo.githubusercontent.com/42f2d2ee1fd163a35dfde75884c89f308d0ae014e313bd3050caa1b98bfde9c6/68747470733a2f2f77656264657673747564696f732e636f6d2f77702d636f6e74656e742f75706c6f6164732f323031382f30342f7764732d6769746875622d62616e6e65722e706e67)](https://webdevstudios.com/contact/) 8 | 9 | ## Table of Contents 10 | 11 | - [Introduction](#introduction) 12 | - [Getting Started](#getting-started) 13 | - [Prerequisites](#prerequisites) 14 | - [Quick Start](#quick-start) 15 | - [Advanced](#advanced) 16 | - [Setup](#setup) 17 | - [Development](#development) 18 | - [Contributing and Support](#contributing-and-support) 19 | 20 | ## Introduction 21 | 22 | Hello there! I am a versatile starter theme known as `wd_s`, or `wdunderscores`. My foundation is built primarily on PHP templates, but I offer the flexibility to incorporate custom templates through the Site Editor. It's worth noting that I'm designed for customization, so it's best not to use me as a Parent Theme. Instead, harness my potential to transform me into the most amazing WordPress theme you can envision – that's what I'm here for! 23 | 24 | I come equipped with a host of robust web technologies, including [Tailwind](https://www.tailwindcss.com), [npm](https://www.npmjs.com/), [webpack](https://webpack.js.org/), [Sass](http://sass-lang.com/), and [PostCSS](https://github.com/postcss/postcss). To ensure your code aligns with [WordPress standards](https://make.wordpress.org/core/handbook/best-practices/coding-standards/) and stays clean, I leverage [@wordpress/scripts](https://developer.wordpress.org/block-editor/packages/packages-scripts/) for CSS and JavaScript linting. What's more, I take accessibility seriously and proudly comply with both WCAG 2.1AA and Section 508 standards right from the start. 25 | 26 | I also come with the Site Editor activated, providing you with even more versatility and ease in crafting your WordPress theme. Whether you're customizing templates or utilizing the Site Editor, I'm here to support your creative journey. And remember, I do require at least PHP 8.0 to be activated to unlock my full potential. 27 | 28 | ## Getting Started 29 | 30 | ### Prerequisites 31 | 32 | Because I compile and bundle assets via NPM scripts, basic knowledge of the command line and the following dependencies are required: 33 | 34 | - [Node](https://nodejs.org) (v18+) 35 | - [NPM](https://npmjs.com) (v9+) 36 | - [Composer](https://getcomposer.org/) 37 | 38 | ### Quick Start 39 | 40 | If you want to keep it simple, head over to [https://wdunderscores.com](https://wdunderscores.com) and generate your `wd_s` based theme from there. You just input the name of the theme you want to create, click the "Generate" button, and you get your ready-to-awesomize starter theme. Alternatively, you could download a pre-built [ZIP](http://wdunderscores.com/wp-content/themes/wdunderscores/inc/prototype/wd_s.zip) 41 | 42 | ### Advanced 43 | 44 | If you want to set me up manually: 45 | 46 | 1. [Download](http://wdunderscores.com/wp-content/themes/wdunderscores/inc/prototype/wd_s.zip) and extract the zip into your `wp-content/themes` directory and rename `wd_s` to fit your needs. 47 | 48 | 2. Find & Replace 49 | 50 | You'll need to change all instances of the name: `wd_s`. 51 | 52 | - Search for: `@package wd_s` and replace with: `@package project_name` to capture the package name 53 | - Update `"WebDevStudios\wd_s,wd_s"` to: `"CompanyName\project_name,project_name"` (with double quotes) in phpcs.xml.dist 54 | - Search for: `WebDevStudios\wd_s` and replace with: `CompanyName\project_name` to capture the namespaces 55 | - Update `"webdevstudios/wd_s"` to `"companyname/project_name"` (with double quotes) in composer.json 56 | - Search for: `, 'wd_s'` and replace with: `, 'project_name'` (inside backticks) to capture the text domain 57 | - Update `Text Domain: wd_s` to: `Text Domain: project_name` in style.css 58 | - Update `"wd_s"` to: `"project_name"` (with double quotes) in phpcs.xml.dist and package.json 59 | - Update `'wd_s'` to: `'project_name'` (with single quotes) in inc/setup/setup.php 60 | - Search for: `wd_s_` and replace with: `project_name_` to capture all the function names 61 | - Search for: `'wd_s-` and replace with: `'project_name-` to capture prefixed handles 62 | - Search for `wd_s.pot` and replace with: `project_name.pot` to capture translation files 63 | - Search for `wdunderscores.test` and replace with: `project_name.test` to match your local development URL 64 | - Edit the theme information in the header of style.scss to meet your needs 65 | 66 | ## Setup 67 | 68 | From the command line, change directories to your new theme directory: 69 | 70 | ```bash 71 | cd /wp-content/themes/your-theme 72 | ``` 73 | 74 | Install theme dependencies and trigger an initial build. 75 | 76 | >Note: You will need to have Composer 2 and NPM 9 installed first. 77 | 78 | ```bash 79 | npm i && composer i 80 | ``` 81 | 82 | ### Development 83 | 84 | From the command line, type any of the following to perform an action: 85 | 86 | Command | Action 87 | :- | :- 88 | `npm run watch` | Builds assets and starts Live Reload and Browsersync servers 89 | `npm run start` | Builds assets and starts Live Reload server 90 | `npm run build` | Builds production-ready assets for a deployment 91 | `npm run lint` | Check all CSS, JS, MD, and PHP files for errors 92 | `npm run format` | Fix all CSS, JS, MD, and PHP formatting errors automatically 93 | `npm run report` | Gives detailed information on coding standards violations in PHP code 94 | 95 | ### Building a block with wpcli 96 | 97 | In order to build a block, run the following wpcli script. 98 | 99 | Note: If you're using Local, you can open the shell from within Local to do this. `Sitename -> Open site shell`. 100 | 101 | ```bash 102 | wp wds create_portable_block myblock --title="This is myblock" --desc="This block is used for wds." --keyword="myblock" --icon="table-row-before" --namespace="wds" 103 | ``` 104 | 105 | This will scaffold out a block inside of the `blocks` folder in the theme. Replace `myblock` with the name of your block and update the other items `title`, `desc`, `keyword`, `icon`, `namespace` with the appropriate information. 106 | 107 | Note: If you don't see your new block available under the block listing in the editor after following the above steps, try changing the namespacing in your block's `block.json` to the following format `your-namespace/blockname` and check again. 108 | 109 | ## Contributing and Support 110 | 111 | Your contributions and [support tickets](https://github.com/WebDevStudios/wd_s/issues) are welcome. Please see our [contributing guidelines](https://github.com/WebDevStudios/wd_s/blob/main/CONTRIBUTING.md) before submitting a pull request. 112 | 113 | wd_s is free software, and is released under the terms of the GNU General Public License version 2 or any later version. See [LICENSE.md](https://github.com/WebDevStudios/wd_s/blob/main/LICENSE.md) for complete license. 114 | -------------------------------------------------------------------------------- /archive.php: -------------------------------------------------------------------------------- 1 | 14 | 15 |
16 | 17 | 18 | 19 | 25 | 26 | 41 | 42 |
43 | 44 | 46 | -------------------------------------------------------------------------------- /assets/critical.js: -------------------------------------------------------------------------------- 1 | import './scss/critical.scss'; 2 | -------------------------------------------------------------------------------- /assets/fonts/inter/Inter-Black.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebDevStudios/wd_s/756a4a309c0c241fa2aba2c67d9434a95c02c74a/assets/fonts/inter/Inter-Black.woff2 -------------------------------------------------------------------------------- /assets/fonts/inter/Inter-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebDevStudios/wd_s/756a4a309c0c241fa2aba2c67d9434a95c02c74a/assets/fonts/inter/Inter-Bold.woff2 -------------------------------------------------------------------------------- /assets/fonts/inter/Inter-Light.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebDevStudios/wd_s/756a4a309c0c241fa2aba2c67d9434a95c02c74a/assets/fonts/inter/Inter-Light.woff2 -------------------------------------------------------------------------------- /assets/fonts/inter/Inter-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebDevStudios/wd_s/756a4a309c0c241fa2aba2c67d9434a95c02c74a/assets/fonts/inter/Inter-Regular.woff2 -------------------------------------------------------------------------------- /assets/fonts/inter/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2020 The Inter Project Authors (https://github.com/rsms/inter) 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /assets/fonts/oxygen/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012, vernon adams (vern@newtypography.co.uk), with Reserved Font Names 'Oxygen' 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /assets/fonts/oxygen/oxygen-mono-v14-latin-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebDevStudios/wd_s/756a4a309c0c241fa2aba2c67d9434a95c02c74a/assets/fonts/oxygen/oxygen-mono-v14-latin-regular.woff2 -------------------------------------------------------------------------------- /assets/fonts/oxygen/oxygen-v15-latin-300.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebDevStudios/wd_s/756a4a309c0c241fa2aba2c67d9434a95c02c74a/assets/fonts/oxygen/oxygen-v15-latin-300.woff2 -------------------------------------------------------------------------------- /assets/fonts/oxygen/oxygen-v15-latin-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebDevStudios/wd_s/756a4a309c0c241fa2aba2c67d9434a95c02c74a/assets/fonts/oxygen/oxygen-v15-latin-700.woff2 -------------------------------------------------------------------------------- /assets/fonts/oxygen/oxygen-v15-latin-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebDevStudios/wd_s/756a4a309c0c241fa2aba2c67d9434a95c02c74a/assets/fonts/oxygen/oxygen-v15-latin-regular.woff2 -------------------------------------------------------------------------------- /assets/images/icons/caret-down.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | caret-down 4 | 5 | 6 | -------------------------------------------------------------------------------- /assets/images/icons/close.svg: -------------------------------------------------------------------------------- 1 | close -------------------------------------------------------------------------------- /assets/images/icons/facebook-square.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /assets/images/icons/hamburger.svg: -------------------------------------------------------------------------------- 1 | hamburger 2 | -------------------------------------------------------------------------------- /assets/images/icons/instagram-square.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /assets/images/icons/linkedin-square.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /assets/images/icons/pause.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | pause 4 | 5 | 6 | -------------------------------------------------------------------------------- /assets/images/icons/play.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | play 4 | 5 | 6 | -------------------------------------------------------------------------------- /assets/images/icons/twitter-square.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /assets/images/placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebDevStudios/wd_s/756a4a309c0c241fa2aba2c67d9434a95c02c74a/assets/images/placeholder.png -------------------------------------------------------------------------------- /assets/index.js: -------------------------------------------------------------------------------- 1 | import './scss/tailwind.scss'; 2 | import './scss/index.scss'; 3 | import './js/index'; 4 | -------------------------------------------------------------------------------- /assets/js/blocks/custom/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebDevStudios/wd_s/756a4a309c0c241fa2aba2c67d9434a95c02c74a/assets/js/blocks/custom/.gitkeep -------------------------------------------------------------------------------- /assets/js/blocks/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Gutenberg Block JS 3 | * 4 | * Import JS for Gutenberg blocks. 5 | */ 6 | 7 | // Custom Gutenberg Blocks. 8 | // import './wd'; 9 | // import './acf'; 10 | -------------------------------------------------------------------------------- /assets/js/global/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Global JS 3 | * 4 | * Import JS that applies globally. 5 | */ 6 | 7 | import './js-enabled'; 8 | import './window-ready'; 9 | -------------------------------------------------------------------------------- /assets/js/global/js-enabled.js: -------------------------------------------------------------------------------- 1 | /** 2 | * File js-enabled.js 3 | * 4 | * If Javascript is enabled, replace the class "no-js". 5 | */ 6 | document.body.className = document.body.className.replace( 'no-js', 'js' ); 7 | -------------------------------------------------------------------------------- /assets/js/global/window-ready.js: -------------------------------------------------------------------------------- 1 | /** 2 | * File window-ready.js 3 | * 4 | * Add a "ready" class to when window is ready. 5 | * 6 | * @author Greg Rickaby, Corey Collins 7 | * @since January 31, 2020 8 | */ 9 | function wdsWindowReady() { 10 | document.body.classList.add( 'ready' ); 11 | } 12 | 13 | if ( 14 | ( 'complete' === document.readyState || 15 | 'loading' !== document.readyState ) && 16 | ! document.documentElement.doScroll 17 | ) { 18 | wdsWindowReady(); 19 | } else { 20 | document.addEventListener( 'DOMContentLoaded', wdsWindowReady ); 21 | } 22 | -------------------------------------------------------------------------------- /assets/js/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Site JS 3 | */ 4 | 5 | import './global'; 6 | import './template-tags'; 7 | import './templates'; 8 | import './blocks'; 9 | -------------------------------------------------------------------------------- /assets/js/template-tags/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Template Tag JS 3 | * 4 | * JS for functions in template-tags.php. 5 | */ 6 | 7 | import './mobile-menu'; 8 | import './modal'; 9 | import './navigation-primary'; 10 | import './off-canvas'; 11 | import './table'; 12 | import './video'; 13 | -------------------------------------------------------------------------------- /assets/js/template-tags/mobile-menu.js: -------------------------------------------------------------------------------- 1 | /** 2 | * File: mobile-menu.js 3 | * 4 | * Create an accordion style dropdown. 5 | */ 6 | 7 | // Make sure everything is loaded first. 8 | if ( 9 | ( 'complete' === document.readyState || 10 | 'loading' !== document.readyState ) && 11 | ! document.documentElement.doScroll 12 | ) { 13 | wdsMobileMenu(); 14 | } else { 15 | document.addEventListener( 'DOMContentLoaded', wdsMobileMenu ); 16 | } 17 | 18 | /** 19 | * Handle our mobile menus. 20 | * 21 | * @author Corey Collins 22 | * @since January 31, 2020 23 | */ 24 | function wdsMobileMenu() { 25 | const subMenuParentItem = document.querySelectorAll( 26 | '.mobile-menu li.menu-item-has-children, .utility-navigation li.menu-item-has-children' 27 | ); 28 | 29 | subMenuParentItem.forEach( ( subMenuParent ) => { 30 | const menuItem = subMenuParent.querySelector( 'a' ); 31 | 32 | menuItem.innerHTML += 33 | ''; 34 | 35 | const subMenuTrigger = document.querySelectorAll( '.parent-indicator' ); 36 | 37 | subMenuTrigger.forEach( ( trigger ) => { 38 | trigger.addEventListener( 'click', toggleSubmenu ); 39 | } ); 40 | } ); 41 | 42 | /** 43 | * Open/Close a submenu. 44 | * 45 | * @author Corey Collins 46 | * @since January 31, 2020 47 | * @param {Object} event The triggered event. 48 | */ 49 | function toggleSubmenu( event ) { 50 | event.preventDefault(); 51 | 52 | const targetElement = event.target, 53 | targetParent = targetElement.parentNode.closest( 54 | '.menu-item-has-children' 55 | ), 56 | subMenu = targetParent.querySelector( 'ul.sub-menu' ); 57 | 58 | closeAllSubmenus( targetParent ); 59 | maybeOpenSubmenu( targetParent, subMenu ); 60 | } 61 | 62 | /** 63 | * Open a submenu. 64 | * 65 | * @author Corey Collins 66 | * @since January 31, 2020 67 | * @param {Object} parent The parent menu. 68 | * @param {Object} subMenu The submenu. 69 | */ 70 | function maybeOpenSubmenu( parent, subMenu ) { 71 | if ( parent.classList.contains( 'is-visible' ) ) { 72 | closeSubmenu( parent, subMenu ); 73 | return; 74 | } 75 | 76 | // Expand the list menu item, and set the corresponding button aria to true. 77 | parent.classList.add( 'is-visible' ); 78 | parent 79 | .querySelector( '.parent-indicator' ) 80 | .setAttribute( 'aria-expanded', true ); 81 | 82 | // Slide the menu in. 83 | subMenu.classList.add( 'is-visible', 'animated', 'slideInLeft' ); 84 | } 85 | 86 | /** 87 | * Close a submenu. 88 | * 89 | * @author Corey Collins 90 | * @since January 31, 2020 91 | * @param {Object} parent The parent item. 92 | * @param {Object} subMenu The submenu. 93 | */ 94 | function closeSubmenu( parent, subMenu ) { 95 | parent.classList.remove( 'is-visible' ); 96 | parent 97 | .querySelector( '.parent-indicator' ) 98 | .setAttribute( 'aria-expanded', false ); 99 | subMenu.classList.remove( 'is-visible', 'animated', 'slideInLeft' ); 100 | } 101 | 102 | /** 103 | * Close all open submenus on the same 104 | * level/hierarchys the menu we're trying 105 | * to open. 106 | * 107 | * @author Corey Collins 108 | * @since January 31, 2020 109 | * @param {Object} targetParent The target parent item. 110 | */ 111 | function closeAllSubmenus( targetParent ) { 112 | const submenuSiblings = getSiblings( targetParent ); 113 | 114 | submenuSiblings.forEach( ( sibling ) => { 115 | sibling.classList.remove( 'is-visible' ); 116 | 117 | if ( sibling.querySelector( '.parent-indicator' ) ) { 118 | sibling 119 | .querySelector( '.parent-indicator' ) 120 | .setAttribute( 'aria-expanded', false ); 121 | } 122 | 123 | if ( sibling.querySelector( '.sub-menu' ) ) { 124 | sibling 125 | .querySelector( '.sub-menu' ) 126 | .classList.remove( 127 | 'is-visible', 128 | 'animated', 129 | 'slideInLeft' 130 | ); 131 | } 132 | } ); 133 | } 134 | 135 | /** 136 | * Find siblings of an item. 137 | * 138 | * @author Corey Collins 139 | * @since January 31, 2020 140 | * @param {Object} element The element being opened. 141 | * @return {Array} List of siblings. 142 | */ 143 | const getSiblings = function ( element ) { 144 | const siblings = []; 145 | let sibling = element.parentNode.firstChild; 146 | 147 | while ( sibling ) { 148 | if ( 1 === sibling.nodeType && sibling !== element ) { 149 | siblings.push( sibling ); 150 | } 151 | 152 | sibling = sibling.nextSibling; 153 | } 154 | 155 | return siblings; 156 | }; 157 | } 158 | -------------------------------------------------------------------------------- /assets/js/template-tags/modal.js: -------------------------------------------------------------------------------- 1 | /** 2 | * File modal.js 3 | * 4 | * Deal with multiple modals and their media. 5 | */ 6 | 7 | // Make sure everything is loaded first. 8 | if ( 9 | ( 'complete' === document.readyState || 10 | 'loading' !== document.readyState ) && 11 | ! document.documentElement.doScroll 12 | ) { 13 | wdsModals(); 14 | } else { 15 | document.addEventListener( 'DOMContentLoaded', wdsModals ); 16 | } 17 | 18 | /** 19 | * Fire off our modal functions. 20 | * 21 | * @author Corey Collins 22 | * @since January 31, 2020 23 | */ 24 | function wdsModals() { 25 | const modalTrigger = document.querySelectorAll( '.modal-trigger' ), 26 | modalClose = document.querySelectorAll( '.modal .close' ), 27 | pageBody = document.body; 28 | 29 | // Loop through each modal trigger on the page and add a listener for its header. 30 | modalTrigger.forEach( ( trigger ) => { 31 | trigger.addEventListener( 'click', openModal ); 32 | } ); 33 | 34 | modalClose.forEach( ( trigger ) => { 35 | trigger.addEventListener( 'click', closeModalOnCloseButton ); 36 | } ); 37 | 38 | pageBody.addEventListener( 'keydown', closeOnEscape ); 39 | pageBody.addEventListener( 'click', closeOnClick ); 40 | 41 | /** 42 | * Open a modal when we trigger it. 43 | * 44 | * @author Corey Collins 45 | * @since January 31, 2020 46 | * @param {Object} event The triggered event. 47 | */ 48 | function openModal( event ) { 49 | const thisTarget = event.target, 50 | thisModalTarget = thisTarget.getAttribute( 'data-target' ), 51 | thisModal = document.querySelector( thisModalTarget ), 52 | focusableChildren = 53 | thisModal.querySelectorAll( 'a, input, button' ); 54 | 55 | pageBody.classList.add( 'modal-open' ); 56 | thisModal.classList.add( 'modal-open' ); 57 | thisModal.setAttribute( 'aria-hidden', false ); 58 | 59 | if ( 0 < focusableChildren.length ) { 60 | focusableChildren[ 0 ].focus(); 61 | } 62 | } 63 | 64 | /** 65 | * Close a modal when we hit the close button. 66 | * 67 | * @author Corey Collins 68 | * @since January 31, 2020 69 | * @param {Object} event The triggered event. 70 | */ 71 | function closeModalOnCloseButton( event ) { 72 | const thisTarget = event.target, 73 | thisModalTarget = thisTarget.getAttribute( 'data-target' ), 74 | thisModal = document.querySelector( thisModalTarget ), 75 | modalIframe = thisModal.querySelector( 'iframe' ); 76 | 77 | pageBody.classList.remove( 'modal-open' ); 78 | thisModal.classList.remove( 'modal-open' ); 79 | thisModal.setAttribute( 'aria-hidden', true ); 80 | 81 | if ( modalIframe ) { 82 | const iframeURL = modalIframe.getAttribute( 'src' ); 83 | 84 | modalIframe.setAttribute( 'src', '' ); 85 | modalIframe.setAttribute( 'src', iframeURL ); 86 | } 87 | } 88 | 89 | /** 90 | * Close the modal when we hit the escape key. 91 | * 92 | * @author Corey Collins 93 | * @since January 31, 2020 94 | * @param {Object} event The triggered event. 95 | */ 96 | function closeOnEscape( event ) { 97 | if ( ! pageBody.classList.contains( 'modal-open' ) ) { 98 | return; 99 | } 100 | 101 | const currentlyOpenModal = 102 | document.querySelector( '.modal.modal-open' ), 103 | modalIframe = currentlyOpenModal.querySelector( 'iframe' ); 104 | 105 | if ( 27 === event.keyCode ) { 106 | currentlyOpenModal.setAttribute( 'aria-hidden', true ); 107 | currentlyOpenModal.classList.remove( 'modal-open' ); 108 | pageBody.classList.remove( 'modal-open' ); 109 | 110 | if ( modalIframe ) { 111 | const iframeURL = modalIframe.getAttribute( 'src' ); 112 | modalIframe.setAttribute( 'src', '' ); 113 | modalIframe.setAttribute( 'src', iframeURL ); 114 | } 115 | } 116 | } 117 | 118 | /** 119 | * Close the modal when we hit outside of the modal area. 120 | * 121 | * @author Corey Collins 122 | * @since January 31, 2020 123 | * @param {Object} event The triggered event. 124 | */ 125 | function closeOnClick( event ) { 126 | const clickedElement = event.target; 127 | 128 | if ( pageBody.classList.contains( 'modal-open' ) ) { 129 | if ( clickedElement.classList.contains( 'modal-open' ) ) { 130 | const modalIframe = clickedElement.querySelector( 'iframe' ); 131 | 132 | pageBody.classList.remove( 'modal-open' ); 133 | clickedElement.classList.remove( 'modal-open' ); 134 | clickedElement.setAttribute( 'aria-hidden', true ); 135 | 136 | if ( modalIframe ) { 137 | const iframeURL = modalIframe.getAttribute( 'src' ); 138 | 139 | modalIframe.setAttribute( 'src', '' ); 140 | modalIframe.setAttribute( 'src', iframeURL ); 141 | } 142 | } 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /assets/js/template-tags/navigation-primary.js: -------------------------------------------------------------------------------- 1 | /** 2 | * File: navigation-primary.js 3 | * 4 | * Helpers for the primary navigation. 5 | */ 6 | 7 | ( function () { 8 | const subMenuParentItem = document.querySelectorAll( 9 | '.main-navigation .menu-item-has-children' 10 | ); 11 | 12 | document.addEventListener( 'DOMContentLoaded', addDownArrow ); 13 | document.addEventListener( 'DOMContentLoaded', toggleFocusClass ); 14 | 15 | /** 16 | * Adds the down arrow to parent menu items. 17 | * 18 | * @author Corey Collins 19 | * @since January 31, 2020 20 | */ 21 | function addDownArrow() { 22 | subMenuParentItem.forEach( ( parentItem ) => { 23 | const menuItem = parentItem.querySelector( 'a' ); 24 | menuItem.innerHTML += 25 | ''; 26 | } ); 27 | } 28 | 29 | /** 30 | * Adds event listeners for tabbing in and out of parent items. 31 | * 32 | * @author Corey Collins 33 | * @since January 31, 2020 34 | */ 35 | function toggleFocusClass() { 36 | subMenuParentItem.forEach( ( parentItem ) => { 37 | parentItem.addEventListener( 'focusin', toggleIn ); 38 | parentItem.addEventListener( 'focusout', toggleOut ); 39 | } ); 40 | } 41 | 42 | /** 43 | * Handle toggling a parent menu on. 44 | * 45 | * @author Corey Collins 46 | * @since January 31, 2020 47 | * @param {Object} event The triggered event. 48 | */ 49 | function toggleIn( event ) { 50 | const parentMenuItems = getParents( 51 | event.target.parentNode, 52 | '.menu-item-has-children' 53 | ); 54 | parentMenuItems.forEach( ( parentItem ) => { 55 | parentItem.classList.add( 'focus' ); 56 | } ); 57 | } 58 | 59 | /** 60 | * Handle toggling a parent menu off. 61 | * 62 | * @since January 31, 2020 63 | * @author Corey Collins 64 | * @param {Object} event The triggered event. 65 | */ 66 | function toggleOut( event ) { 67 | const parentMenuItems = getParents( 68 | event.target.parentNode, 69 | '.menu-item-has-children' 70 | ); 71 | parentMenuItems.forEach( ( parentItem ) => { 72 | parentItem.classList.remove( 'focus' ); 73 | } ); 74 | } 75 | 76 | /** 77 | * Get all of the parents for a matching element and selector. 78 | * 79 | * @author Corey Collins 80 | * @since January 31, 2020 81 | * @see https://gomakethings.com/climbing-up-and-down-the-dom-tree-with-vanilla-javascript/#getting-all-matches-up-the-tree 82 | * @param {Object} elem The parent menu item. 83 | * @param {string} selector The CSS class of the element. 84 | * @return {Array} Parents. 85 | */ 86 | const getParents = function ( elem, selector ) { 87 | // Element.matches() polyfill. 88 | if ( ! Element.prototype.matches ) { 89 | Element.prototype.matches = 90 | Element.prototype.matchesSelector || 91 | Element.prototype.mozMatchesSelector || 92 | Element.prototype.msMatchesSelector || 93 | Element.prototype.oMatchesSelector || 94 | Element.prototype.webkitMatchesSelector || 95 | function ( s ) { 96 | const matches = ( 97 | this.document || this.ownerDocument 98 | ).querySelectorAll( s ); 99 | let i = matches.length; 100 | while ( 0 >= --i && matches.item( i ) !== this ) {} 101 | return -1 > i; 102 | }; 103 | } 104 | 105 | // Setup parents array. 106 | const parents = []; 107 | 108 | // Get matching parent elements. 109 | for ( ; elem && elem !== document; elem = elem.parentNode ) { 110 | // Add matching parents to array. 111 | if ( selector ) { 112 | if ( elem.matches( selector ) ) { 113 | parents.push( elem ); 114 | } 115 | } else { 116 | parents.push( elem ); 117 | } 118 | } 119 | 120 | return parents; 121 | }; 122 | } )(); 123 | -------------------------------------------------------------------------------- /assets/js/template-tags/off-canvas.js: -------------------------------------------------------------------------------- 1 | /** 2 | * File: off-canvas.js 3 | * 4 | * Help deal with the off-canvas mobile menu. 5 | */ 6 | 7 | // Make sure everything is loaded first. 8 | if ( 9 | ( 'complete' === document.readyState || 10 | 'loading' !== document.readyState ) && 11 | ! document.documentElement.doScroll 12 | ) { 13 | wdsOffCanvas(); 14 | } else { 15 | document.addEventListener( 'DOMContentLoaded', wdsOffCanvas ); 16 | } 17 | 18 | /** 19 | * Kick off our off canvas functions. 20 | * 21 | * @author Corey Collins 22 | * @since January 31, 2020 23 | */ 24 | function wdsOffCanvas() { 25 | const offCanvasScreen = document.querySelector( '.off-canvas-screen' ); 26 | 27 | if ( ! offCanvasScreen ) { 28 | return; 29 | } 30 | 31 | const offCanvasContainer = document.querySelector( 32 | '.off-canvas-container' 33 | ), 34 | offCanvasOpen = document.querySelector( '.off-canvas-open' ); 35 | 36 | offCanvasOpen.addEventListener( 'click', toggleOffCanvas ); 37 | offCanvasScreen.addEventListener( 'click', closeOffCanvas ); 38 | document.body.addEventListener( 'keydown', closeOnEscape ); 39 | 40 | /** 41 | * Close everything when we hit the escape key. 42 | * 43 | * @author Corey Collins 44 | * @since January 31, 2020 45 | * @param {Object} event The event trigger. 46 | */ 47 | function closeOnEscape( event ) { 48 | if ( 27 === event.keyCode ) { 49 | closeOffCanvas(); 50 | } 51 | } 52 | 53 | /** 54 | * Handle closing the off-canvas overlay. 55 | * 56 | * @author Corey Collins 57 | * @since January 31, 2020 58 | */ 59 | function closeOffCanvas() { 60 | offCanvasContainer.classList.remove( 'is-visible' ); 61 | offCanvasOpen.classList.remove( 'is-visible' ); 62 | offCanvasScreen.classList.remove( 'is-visible' ); 63 | 64 | offCanvasContainer.setAttribute( 'aria-hidden', true ); 65 | offCanvasOpen.setAttribute( 'aria-expanded', false ); 66 | } 67 | 68 | /** 69 | * Toggle the display of the off-canvas overlay. 70 | * 71 | * @author Corey Collins 72 | * @since January 31, 2020 73 | */ 74 | function toggleOffCanvas() { 75 | if ( 'true' === offCanvasOpen.getAttribute( 'aria-expanded' ) ) { 76 | closeOffCanvas(); 77 | } else { 78 | openOffCanvas(); 79 | } 80 | } 81 | 82 | /** 83 | * Handle opening the off-canvas overlay. 84 | * 85 | * @author Corey Collins 86 | * @since January 31, 2020 87 | */ 88 | function openOffCanvas() { 89 | offCanvasContainer.classList.add( 'is-visible' ); 90 | offCanvasOpen.classList.add( 'is-visible' ); 91 | offCanvasScreen.classList.add( 'is-visible' ); 92 | 93 | offCanvasContainer.setAttribute( 'aria-hidden', false ); 94 | offCanvasOpen.setAttribute( 'aria-expanded', true ); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /assets/js/template-tags/table.js: -------------------------------------------------------------------------------- 1 | /** 2 | * File table.js 3 | * 4 | * Make tables responsive. 5 | * 6 | * @author Haris Zulfiqar, Corey Collins 7 | * @since January 31, 2020 8 | */ 9 | 10 | ( function () { 11 | document.querySelectorAll( 'table' ).forEach( ( table ) => { 12 | const tableHeaders = table.querySelectorAll( 'th' ); 13 | 14 | // Bail if our table has no headers set. 15 | if ( 0 === tableHeaders.length ) { 16 | return; 17 | } 18 | 19 | const tableRow = table.querySelectorAll( 'tbody tr' ); 20 | 21 | tableRow.forEach( ( row ) => { 22 | const tableCell = row.querySelectorAll( 'td' ); 23 | 24 | tableCell.forEach( ( cell, index ) => { 25 | if ( tableHeaders[ index ].textContent ) { 26 | cell.setAttribute( 27 | 'data-label', 28 | tableHeaders[ index ].textContent 29 | ); 30 | } 31 | } ); 32 | } ); 33 | } ); 34 | } )(); 35 | -------------------------------------------------------------------------------- /assets/js/template-tags/video.js: -------------------------------------------------------------------------------- 1 | /** 2 | * File video.js 3 | * 4 | * Deal with video playback. 5 | */ 6 | 7 | ( function () { 8 | const videoButtons = document.querySelectorAll( '.video-toggle' ); 9 | 10 | // Toggle playback on background videos. 11 | videoButtons.forEach( ( videoButton ) => { 12 | videoButton.addEventListener( 'click', toggleVideoPlayback ); 13 | } ); 14 | 15 | /** 16 | * Toggle video playback when the button is pressed. 17 | * 18 | * @author Jo Murgel, Corey Collins 19 | * @since January 31, 2020 20 | * @param {Object} event The triggered event. 21 | */ 22 | function toggleVideoPlayback( event ) { 23 | const targetParent = event.target.parentNode, 24 | targetElement = targetParent.querySelector( '.video-background' ); 25 | 26 | targetParent.classList.toggle( 'video-toggled' ); 27 | 28 | if ( targetParent.classList.contains( 'video-toggled' ) ) { 29 | targetElement.pause(); 30 | } else { 31 | targetElement.play(); 32 | } 33 | } 34 | } )(); 35 | -------------------------------------------------------------------------------- /assets/js/templates/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Template JS 3 | * 4 | * JS for WordPress template files. 5 | * See https://developer.wordpress.org/themes/basics/template-files/. 6 | */ 7 | 8 | // import './template-name'; 9 | -------------------------------------------------------------------------------- /assets/scss/blocks/core/_navigation.scss: -------------------------------------------------------------------------------- 1 | /* stylelint-disable selector-class-pattern, declaration-no-important, value-keyword-case */ 2 | 3 | /* Navigation 4 | --------------------------------------------- */ 5 | 6 | .wp-block-navigation__responsive-container.is-menu-open { 7 | padding-left: var( --wp--preset--spacing--50 ) !important; 8 | padding-right: var( --wp--preset--spacing--50 ) !important; 9 | width: 100%; 10 | } 11 | 12 | /* Submenu 13 | --------------------------------------------- */ 14 | 15 | .wp-block-navigation:not( .has-background ) 16 | .wp-block-navigation__submenu-container { 17 | min-width: 180px !important; 18 | padding: var( --wp--preset--spacing--20 ); 19 | } 20 | 21 | .wp-block-navigation 22 | :where( .wp-block-navigation__submenu-container ) 23 | .wp-block-navigation-item__content { 24 | padding: 2px var( --wp--preset--spacing--20 ); 25 | } 26 | 27 | .wp-block-navigation__responsive-container.is-menu-open 28 | .wp-block-navigation__submenu-container { 29 | padding-left: var( --wp--preset--spacing--20 ) !important; 30 | padding-right: var( --wp--preset--spacing--20 ) !important; 31 | } 32 | 33 | /* Media Queries 34 | ---------------------------------------------------------------------------- */ 35 | 36 | @media only screen and ( max-width: 600px ) { 37 | .wp-block-navigation__container.is-responsive.items-justified-right.wp-block-navigation { 38 | --navigation-layout-justification-setting: flex-start; 39 | --navigation-layout-justify: flex-start; 40 | width: 100%; 41 | } 42 | } 43 | 44 | /* stylelint-enable selector-class-pattern, declaration-no-important, value-keyword-case */ 45 | -------------------------------------------------------------------------------- /assets/scss/blocks/core/index.scss: -------------------------------------------------------------------------------- 1 | //---------------------------------------- 2 | // Core Gutenberg Block Styles 3 | // Import supported Core Gutenberg block partials. 4 | //---------------------------------------- 5 | 6 | @import 'navigation'; 7 | -------------------------------------------------------------------------------- /assets/scss/blocks/custom/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebDevStudios/wd_s/756a4a309c0c241fa2aba2c67d9434a95c02c74a/assets/scss/blocks/custom/.gitkeep -------------------------------------------------------------------------------- /assets/scss/blocks/custom/index.scss: -------------------------------------------------------------------------------- 1 | //---------------------------------------- 2 | // Custom Gutenberg Block Styles 3 | // Import Custom Gutenberg block partials. 4 | //---------------------------------------- 5 | -------------------------------------------------------------------------------- /assets/scss/blocks/index.scss: -------------------------------------------------------------------------------- 1 | //---------------------------------------- 2 | // Gutenberg Block Styles 3 | // Import Core and custom Gutenberg block partials. 4 | //---------------------------------------- 5 | 6 | // Core Gutenberg Blocks. 7 | @import 'core/index'; 8 | @import 'custom/index'; 9 | -------------------------------------------------------------------------------- /assets/scss/critical.scss: -------------------------------------------------------------------------------- 1 | //---------------------------------------- 2 | // Critical CSS. 3 | //---------------------------------------- 4 | @import 'critical/index'; 5 | -------------------------------------------------------------------------------- /assets/scss/critical/_header.scss: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------------------- 2 | // Global Site Header 3 | //-------------------------------------------------------------- 4 | .site-header { 5 | .site-header-content { 6 | @apply m-0; 7 | 8 | .wp-block-site-logo { 9 | .site-branding { 10 | @apply flex-auto; 11 | a { 12 | @apply no-underline; 13 | 14 | .custom-logo { 15 | @apply w-auto max-h-14; 16 | } 17 | } 18 | } 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /assets/scss/critical/index.scss: -------------------------------------------------------------------------------- 1 | //---------------------------------------- 2 | // Critical CSS 3 | // Import partials for "above the fold" (barf) styles. 4 | //---------------------------------------- 5 | 6 | @import 'header'; 7 | -------------------------------------------------------------------------------- /assets/scss/global/_editor.scss: -------------------------------------------------------------------------------- 1 | // Edges of blocks weren't obvious. Adds subtle outline. 2 | .wp-block-post-content { 3 | .wp-block { 4 | &.is-selected { 5 | @apply p-4 outline outline-1; 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /assets/scss/global/_footer.scss: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------------------- 2 | // Global Site Footer 3 | //-------------------------------------------------------------- 4 | .site-footer { 5 | @apply flex p-10 flex-nowrap justify-center items-center bottom-0; 6 | 7 | .site-info { 8 | @apply text-center; 9 | 10 | .custom-logo { 11 | @apply max-h-12 w-auto; 12 | } 13 | 14 | p { 15 | @apply text-xs; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /assets/scss/global/_global.scss: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------------------- 2 | // Global Resets/Overrides 3 | //-------------------------------------------------------------- 4 | *, 5 | ::after, 6 | ::before { 7 | @apply box-border break-words; 8 | 9 | word-wrap: break-word; 10 | } 11 | 12 | html { 13 | scroll-behavior: smooth; 14 | } 15 | 16 | pre { 17 | @apply overflow-auto; 18 | } 19 | 20 | .post-container { 21 | @apply mb-6; 22 | } 23 | -------------------------------------------------------------------------------- /assets/scss/global/index.scss: -------------------------------------------------------------------------------- 1 | //---------------------------------------- 2 | // Global site styles 3 | //---------------------------------------- 4 | 5 | @import 'global'; 6 | @import 'editor'; 7 | @import 'footer'; 8 | -------------------------------------------------------------------------------- /assets/scss/index.scss: -------------------------------------------------------------------------------- 1 | // @layer base { 2 | // // Custom base styles if necessary. 3 | // // See https://tailwindcss.com/docs/adding-base-styles. 4 | // } 5 | 6 | //---------------------------------------- 7 | // Theme Styles. 8 | //---------------------------------------- 9 | @import 'blocks/index'; 10 | @import 'global/index'; 11 | @import 'template-tags/index'; 12 | @import 'templates/index'; 13 | -------------------------------------------------------------------------------- /assets/scss/tailwind.scss: -------------------------------------------------------------------------------- 1 | //----------------------------------------- 2 | // Tailwind Files 3 | //----------------------------------------- 4 | @tailwind base; 5 | @tailwind components; 6 | @tailwind utilities; 7 | -------------------------------------------------------------------------------- /assets/scss/template-tags/_comments.scss: -------------------------------------------------------------------------------- 1 | /* Comments 2 | --------------------------------------------- */ 3 | 4 | .comments-area { 5 | .comment-list { 6 | @apply list-none mb-0 p-0; 7 | 8 | .comment { 9 | @apply clear-both; 10 | } 11 | 12 | .children { 13 | @apply list-none my-4; 14 | } 15 | } 16 | 17 | .comment-respond { 18 | .comment-form { 19 | .comment-form-comment { 20 | @apply flex flex-col; 21 | 22 | textarea { 23 | @apply rounded-md; 24 | border: 1px var( --wp--preset--color--primary-300 ) solid; 25 | } 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /assets/scss/template-tags/_forms.scss: -------------------------------------------------------------------------------- 1 | /* Comments 2 | --------------------------------------------- */ 3 | button, 4 | input, 5 | optgroup, 6 | select, 7 | textarea { 8 | @apply rounded-md; 9 | border: 1px var( --wp--preset--color--primary-300 ) solid; 10 | padding-top: 6px; 11 | padding-right: 16px; 12 | padding-bottom: 6px; 13 | padding-left: 16px; 14 | } 15 | 16 | [type='reset'], 17 | [type='submit'] { 18 | background-color: var( --wp--preset--color--tertiary-300 ); 19 | border-radius: 10px; 20 | border-width: 0; 21 | border-style: solid; 22 | color: var( --wp--preset--color--white ); 23 | font-family: inherit; 24 | font-size: var( --wp--preset--font-size--normal ); 25 | line-height: 1.435; 26 | padding-top: 8px; 27 | padding-right: 16px; 28 | padding-bottom: 8px; 29 | padding-left: 16px; 30 | text-decoration: none; 31 | } 32 | -------------------------------------------------------------------------------- /assets/scss/template-tags/index.scss: -------------------------------------------------------------------------------- 1 | //---------------------------------------- 2 | // Template Tag Styles 3 | // Import partials for functions in template-tags.php. 4 | //---------------------------------------- 5 | 6 | @import 'comments'; 7 | @import 'forms'; 8 | -------------------------------------------------------------------------------- /assets/scss/templates/index.scss: -------------------------------------------------------------------------------- 1 | //---------------------------------------- 2 | // Template Styles 3 | // Import partials for WordPress templates and template parts. 4 | // See https://developer.wordpress.org/themes/basics/template-files/. 5 | //---------------------------------------- 6 | -------------------------------------------------------------------------------- /assets/tailwind-preflight.css: -------------------------------------------------------------------------------- 1 | /* 2 | 1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) 3 | 2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) 4 | */ 5 | 6 | *, 7 | ::before, 8 | ::after { 9 | box-sizing: border-box; /* 1 */ 10 | border-width: 0; /* 2 */ 11 | border-style: solid; /* 2 */ 12 | } 13 | 14 | ::before, 15 | ::after { 16 | --tw-content: ''; 17 | } 18 | 19 | /* 20 | 1. Use a consistent sensible line-height in all browsers. 21 | 2. Prevent adjustments of font size after orientation changes in iOS. 22 | 3. Use a more readable tab size. 23 | */ 24 | 25 | html { 26 | line-height: 1.5; /* 1 */ 27 | -webkit-text-size-adjust: 100%; /* 2 */ 28 | -moz-tab-size: 4; /* 3 */ 29 | tab-size: 4; /* 3 */ 30 | } 31 | 32 | /* 33 | 1. Remove the margin in all browsers. 34 | 2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. 35 | */ 36 | 37 | body { 38 | line-height: inherit; /* 2 */ 39 | } 40 | 41 | /* 42 | 1. Add the correct height in Firefox. 43 | 2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) 44 | 3. Ensure horizontal rules are visible by default. 45 | */ 46 | 47 | hr { 48 | height: 0; /* 1 */ 49 | color: inherit; /* 2 */ 50 | border-top-width: 1px; /* 3 */ 51 | } 52 | 53 | /* 54 | Add the correct text decoration in Chrome, Edge, and Safari. 55 | */ 56 | 57 | abbr:where( [title] ) { 58 | text-decoration: underline dotted; 59 | } 60 | 61 | /* 62 | Reset links to optimize for opt-in styling instead of opt-out. 63 | */ 64 | 65 | a { 66 | text-decoration: inherit; 67 | } 68 | 69 | /* 70 | Add the correct font weight in Edge and Safari. 71 | */ 72 | 73 | b, 74 | strong { 75 | font-weight: bolder; 76 | } 77 | 78 | /* 79 | 1. Use the user's configured `mono` font family by default. 80 | 2. Correct the odd `em` font sizing in all browsers. 81 | */ 82 | 83 | code, 84 | kbd, 85 | samp, 86 | pre { 87 | font-family: theme( 88 | 'fontFamily.mono', 89 | ui-monospace, 90 | SFMono-Regular, 91 | Menlo, 92 | Monaco, 93 | Consolas, 94 | 'Liberation Mono', 95 | 'Courier New', 96 | monospace 97 | ); /* 1 */ 98 | font-size: 1em; /* 2 */ 99 | } 100 | 101 | /* 102 | Add the correct font size in all browsers. 103 | */ 104 | 105 | small { 106 | font-size: 80%; 107 | } 108 | 109 | /* 110 | Prevent `sub` and `sup` elements from affecting the line height in all browsers. 111 | */ 112 | 113 | sub, 114 | sup { 115 | font-size: 75%; 116 | line-height: 0; 117 | position: relative; 118 | vertical-align: baseline; 119 | } 120 | 121 | sub { 122 | bottom: -0.25em; 123 | } 124 | 125 | sup { 126 | top: -0.5em; 127 | } 128 | 129 | /* 130 | 1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) 131 | 2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) 132 | 3. Remove gaps between table borders by default. 133 | */ 134 | 135 | table { 136 | text-indent: 0; /* 1 */ 137 | border-color: inherit; /* 2 */ 138 | border-collapse: collapse; /* 3 */ 139 | } 140 | 141 | /* 142 | 1. Change the font styles in all browsers. 143 | */ 144 | 145 | button, 146 | input, 147 | optgroup, 148 | select, 149 | textarea { 150 | font-family: inherit; /* 1 */ 151 | font-size: 100%; /* 1 */ 152 | font-weight: inherit; /* 1 */ 153 | line-height: inherit; /* 1 */ 154 | color: inherit; /* 1 */ 155 | } 156 | 157 | /* 158 | Remove the inheritance of text transform in Edge and Firefox. 159 | */ 160 | 161 | button, 162 | select { 163 | text-transform: none; 164 | } 165 | 166 | /* 167 | 1. Correct the inability to style clickable types in iOS and Safari. 168 | */ 169 | 170 | button, 171 | [type='button'], 172 | [type='reset'], 173 | [type='submit'] { 174 | -webkit-appearance: button; /* 1 */ 175 | } 176 | 177 | /* 178 | Use the modern Firefox focus style for all focusable elements. 179 | */ 180 | 181 | :-moz-focusring { 182 | outline: auto; 183 | } 184 | 185 | /* 186 | Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) 187 | */ 188 | 189 | :-moz-ui-invalid { 190 | box-shadow: none; 191 | } 192 | 193 | /* 194 | Add the correct vertical alignment in Chrome and Firefox. 195 | */ 196 | 197 | progress { 198 | vertical-align: baseline; 199 | } 200 | 201 | /* 202 | Correct the cursor style of increment and decrement buttons in Safari. 203 | */ 204 | 205 | ::-webkit-inner-spin-button, 206 | ::-webkit-outer-spin-button { 207 | height: auto; 208 | } 209 | 210 | /* 211 | 1. Correct the odd appearance in Chrome and Safari. 212 | 2. Correct the outline style in Safari. 213 | */ 214 | 215 | [type='search'] { 216 | -webkit-appearance: textfield; /* 1 */ 217 | outline-offset: -2px; /* 2 */ 218 | } 219 | 220 | /* 221 | Remove the inner padding in Chrome and Safari on macOS. 222 | */ 223 | 224 | ::-webkit-search-decoration { 225 | -webkit-appearance: none; 226 | } 227 | 228 | /* 229 | 1. Correct the inability to style clickable types in iOS and Safari. 230 | 2. Change font properties to `inherit` in Safari. 231 | */ 232 | 233 | ::-webkit-file-upload-button { 234 | -webkit-appearance: button; /* 1 */ 235 | font: inherit; /* 2 */ 236 | } 237 | 238 | /* 239 | Add the correct display in Chrome and Safari. 240 | */ 241 | 242 | summary { 243 | display: list-item; 244 | } 245 | 246 | fieldset { 247 | margin: 0; 248 | padding: 0; 249 | } 250 | 251 | legend { 252 | padding: 0; 253 | } 254 | 255 | /* 256 | Prevent resizing textareas horizontally by default. 257 | */ 258 | 259 | textarea { 260 | resize: vertical; 261 | } 262 | 263 | /* 264 | 1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) 265 | */ 266 | 267 | input::placeholder, 268 | textarea::placeholder { 269 | opacity: 1; /* 1 */ 270 | } 271 | 272 | /* 273 | Set the default cursor for buttons. 274 | */ 275 | 276 | button, 277 | [role='button'] { 278 | cursor: pointer; 279 | } 280 | 281 | /* 282 | Make sure disabled buttons don't get the pointer cursor. 283 | */ 284 | :disabled { 285 | cursor: default; 286 | } 287 | 288 | /* 289 | 1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) 290 | 2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) 291 | This can trigger a poorly considered lint error in some tools but is included by design. 292 | */ 293 | 294 | img, 295 | svg, 296 | video, 297 | canvas, 298 | audio, 299 | iframe, 300 | embed, 301 | object { 302 | display: block; /* 1 */ 303 | vertical-align: middle; /* 2 */ 304 | } 305 | 306 | /* 307 | Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) 308 | */ 309 | 310 | img, 311 | video { 312 | max-width: 100%; 313 | height: auto; 314 | } 315 | 316 | /* Make elements with the HTML hidden attribute stay hidden by default */ 317 | [hidden] { 318 | display: none; 319 | } 320 | -------------------------------------------------------------------------------- /blocks/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebDevStudios/wd_s/756a4a309c0c241fa2aba2c67d9434a95c02c74a/blocks/.gitkeep -------------------------------------------------------------------------------- /comments.php: -------------------------------------------------------------------------------- 1 | 23 | 24 |
25 |

26 | 27 | 31 |

32 | ' . wp_kses_post( get_the_title() ) . '' // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- XSS OK. 38 | ); 39 | ?> 40 |

41 | 42 | 1 && get_option( 'page_comments' ) ) : // Are there comments to navigate through? 44 | ?> 45 | 54 | 55 | 56 |
    57 | 'ol', 61 | 'short_ping' => true, 62 | 'avatar_size' => 66, 63 | ] 64 | ); 65 | ?> 66 |
67 | 68 | 1 && get_option( 'page_comments' ) ) : // Are there comments to navigate through? 70 | ?> 71 | 80 | 81 | 82 | 83 | 84 | 88 |

89 | 90 | 91 | 'button' ] ); 95 | ?> 96 | 97 |
98 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webdevstudios/wd_s", 3 | "description": "A starter theme from WebDevStudios.", 4 | "type": "wordpress-theme", 5 | "license": "GPL-2.0-or-later", 6 | "authors": [ 7 | { 8 | "name": "WebDevStudios", 9 | "email": "contact@webdevstudios.com" 10 | } 11 | ], 12 | "config": { 13 | "platform": { 14 | "php": "8.0" 15 | }, 16 | "allow-plugins": { 17 | "composer/installers": true, 18 | "dealerdirect/phpcodesniffer-composer-installer": true 19 | }, 20 | "sort-packages": true 21 | }, 22 | "require": { 23 | "composer/installers": "^1.5 || ^2.0.0" 24 | }, 25 | "require-dev": { 26 | "dealerdirect/phpcodesniffer-composer-installer": "^1.0.0", 27 | "php-stubs/acf-pro-stubs": "^6.0", 28 | "phpcompatibility/phpcompatibility-wp": "^2.1.4", 29 | "wp-cli/wp-cli-bundle": "^2.7", 30 | "wp-coding-standards/wpcs": "^3.0" 31 | }, 32 | "scripts": { 33 | "format": "phpcbf --report=summary,source", 34 | "lint": "phpcs --report=code", 35 | "report": "phpcs --report=info", 36 | "pot": "wp i18n make-pot . build/languages/wd_s.pot --exclude=node_modules,vendor,build --allow-root" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /footer.php: -------------------------------------------------------------------------------- 1 | 15 |
16 |
17 | 18 |
19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /front-page.php: -------------------------------------------------------------------------------- 1 | 14 |
15 |
16 | 17 | 34 | 35 |
36 |
37 | 38 | 39 | -------------------------------------------------------------------------------- /functions.php: -------------------------------------------------------------------------------- 1 | section and everything up until
6 | * 7 | * @link https://developer.wordpress.org/themes/basics/template-files/#template-partials 8 | * 9 | * @package wd_s 10 | */ 11 | 12 | ?> 13 | 14 | > 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 27 | 28 | 29 | 30 | > 31 | 32 | 33 | 34 |
35 | 36 | 39 | 40 |
41 | -------------------------------------------------------------------------------- /home.php: -------------------------------------------------------------------------------- 1 | 14 |
15 |
16 | 17 | 18 |
19 |

20 |
21 | 22 | 37 | 38 |
39 |
40 | 41 | 42 | -------------------------------------------------------------------------------- /inc/README.md: -------------------------------------------------------------------------------- 1 | # Included `PHP` Files 2 | 3 | Use the `/inc` directory to declare any theme functionality. All files in this directory are imported inside of `functions.php`. 4 | 5 | ## Directories 6 | 7 | The `/inc` directory is organized into sub-directories based on the functionality/purpose of the code. These directories can be modified as needed, but the following structure is recommended: 8 | 9 | ```text 10 | inc/ 11 | └─── customizer/ (functions relating to the theme customizer) 12 | └─── functions/ (general functions that don't fit into any other directory) 13 | └─── hooks/ (theme hooks) 14 | └─── post-types/ (theme post type registrations & functions) 15 | └─── setup/ (functions relating to the theme setup) 16 | └─── shortcodes/ (shortcode registrations) 17 | └─── template-tags/ (functions that render markup for use in theme templates) 18 | └─── README.md 19 | ``` 20 | 21 | ## Filenames 22 | 23 | As a general rule, each `.php` file should contain a single function/action which should match the name of the file (replacing underscores with hyphens in the filename). 24 | 25 | For example, `function demo_function() {...}` would be declared in a file named `demo-function.php` and stored inside an appropriate `/inc` sub-directory. 26 | 27 | -------------------------------------------------------------------------------- /inc/customizer/assets/scripts/livepreview.js: -------------------------------------------------------------------------------- 1 | /** 2 | * File livepreview.js. 3 | * 4 | * Deal with real time changes asynchronously. 5 | */ 6 | 7 | ( function ( $ ) { 8 | // Hook into the API. 9 | const api = wp.customize; 10 | 11 | // Site title. 12 | api( 'blogname', function ( value ) { 13 | value.bind( function ( to ) { 14 | $( '.site-title a' ).text( to ); 15 | } ); 16 | } ); 17 | 18 | // Site description. 19 | api( 'blogdescription', function ( value ) { 20 | value.bind( function ( to ) { 21 | $( '.site-description' ).text( to ); 22 | } ); 23 | } ); 24 | 25 | // Header text color. 26 | api( 'header_textcolor', function ( value ) { 27 | value.bind( function ( to ) { 28 | if ( 'blank' === to ) { 29 | $( '.site-title a, .site-description' ).css( { 30 | clip: 'rect(1px, 1px, 1px, 1px)', 31 | position: 'absolute', 32 | } ); 33 | } else { 34 | $( '.site-title a, .site-description' ).css( { 35 | clip: 'auto', 36 | position: 'relative', 37 | } ); 38 | $( '.site-title a, .site-description' ).css( { 39 | color: to, 40 | } ); 41 | } 42 | } ); 43 | } ); 44 | 45 | // Copyright text. 46 | api( 'wd_s_copyright_text', function ( value ) { 47 | value.bind( function ( to ) { 48 | $( '.site-info' ).text( to ); 49 | } ); 50 | } ); 51 | } )( jQuery ); 52 | -------------------------------------------------------------------------------- /inc/customizer/assets/scripts/tinymce.js: -------------------------------------------------------------------------------- 1 | /* globals tinymce */ 2 | 3 | /** 4 | * File editor.js. 5 | * 6 | * Theme Customizer editor enhancements for a better user experience. 7 | */ 8 | 9 | window.wdsAdditionalTinyMCE = window.wdsAdditionalTinyMCE || {}; 10 | ( function ( window, document, $, app ) { 11 | ( 'use strict' ); 12 | 13 | /** 14 | * Config from WP localization. 15 | */ 16 | app.l10n = window.wdsAdditionalTinyMCE || {}; 17 | 18 | /** 19 | * Caches elements. 20 | */ 21 | app.cache = function () { 22 | app.$ = {}; 23 | 24 | app.api = wp.customize; 25 | }; 26 | 27 | /** 28 | * Initialization function. 29 | */ 30 | app.init = function () { 31 | // Build cached elements. 32 | app.cache(); 33 | 34 | // Bail early if requirements aren't met. 35 | if ( ! app.MeetsRequirements() ) { 36 | return; 37 | } 38 | 39 | // Rebind editors when a control section is clicked. 40 | $( '.control-section' ).on( 'click', app.bindEditors ); 41 | 42 | // Update customizer option when tinymce is changed. 43 | $( '.wds-customize-text-editor' ) 44 | .find( 'textarea' ) 45 | .on( 'change keyup', app.editorUpdated ); 46 | }; 47 | 48 | /** 49 | * Make sure the editor updates the customize option when changed in visual mode. 50 | */ 51 | app.bindEditors = function () { 52 | // Was needed a timeout since RTE is not initialized when this code run. 53 | setTimeout( function () { 54 | for ( let i = 0; i < tinymce.editors.length; i++ ) { 55 | tinymce.editors[ i ].onChange.add( function ( ed ) { 56 | // Update HTML view textarea (that is the one used to send the data to server). 57 | ed.save(); 58 | 59 | // Update the customize option. 60 | wp.customize( ed.id, function ( obj ) { 61 | obj.set( ed.getContent() ); 62 | } ); 63 | } ); 64 | } 65 | }, 1000 ); 66 | }; 67 | 68 | /** 69 | * Fires when the editor is changed. 70 | */ 71 | app.editorUpdated = function () { 72 | const $me = $( this ); 73 | 74 | // Update the customize option. 75 | wp.customize( this.id, function ( obj ) { 76 | obj.set( $me.val() ); 77 | } ); 78 | }; 79 | 80 | /** 81 | * Determine if requirements are met for JS bindings. 82 | * 83 | * @return {boolean} True if requirements are met, false otherwise. 84 | */ 85 | app.MeetsRequirements = function () { 86 | return $( '.wds-customize-text-editor' ).length; 87 | }; 88 | 89 | /** 90 | * Safely log to the console. 91 | * 92 | * @param {string} str var to log 93 | */ 94 | app.log = function ( str ) { 95 | // Bail early if no console. 96 | if ( ! window.console ) { 97 | return; 98 | } 99 | 100 | window.console.log( str ); 101 | }; 102 | 103 | // Fire init on document.ready. 104 | $( document ).ready( app.init ); 105 | 106 | return app; 107 | } )( window, document, jQuery, window.wdsAdditionalTinyMCE ); 108 | -------------------------------------------------------------------------------- /inc/customizer/class-text-editor-custom-control.php: -------------------------------------------------------------------------------- 1 | 33 | 37 |
38 | $this->id, 42 | 'textarea_rows' => 4, 43 | 'media_buttons' => true, 44 | ]; 45 | 46 | // Add the editor. 47 | wp_editor( $this->value(), $this->id, $settings ); 48 | 49 | // Only enqueue scripts once. 50 | if ( ! self::$did_scripts ) { 51 | $this->enqueue_scripts(); 52 | $this->add_footer_scripts(); 53 | 54 | self::$did_scripts = true; 55 | } 56 | ?> 57 |
58 | remove_section( 'custom_css' ); 20 | $wp_customize->remove_section( 'static_front_page' ); 21 | $wp_customize->remove_section( 'background_image' ); 22 | $wp_customize->remove_section( 'colors' ); 23 | } 24 | add_action( 'customize_register', __NAMESPACE__ . '\remove_default_customizer_sections', 15 ); 25 | 26 | /** 27 | * Include other customizer files. 28 | * 29 | * @author WebDevStudios 30 | */ 31 | function include_custom_controls() { 32 | require get_template_directory() . '/inc/customizer/panels.php'; 33 | require get_template_directory() . '/inc/customizer/sections.php'; 34 | require get_template_directory() . '/inc/customizer/settings.php'; 35 | require get_template_directory() . '/inc/customizer/class-text-editor-custom-control.php'; 36 | } 37 | add_action( 'customize_register', __NAMESPACE__ . '\include_custom_controls', -999 ); 38 | 39 | /** 40 | * Enqueue customizer related scripts. 41 | * 42 | * @author WebDevStudios 43 | */ 44 | function customize_scripts() { 45 | wp_enqueue_script( 'wd_s-customize-livepreview', get_template_directory_uri() . '/inc/customizer/assets/scripts/livepreview.js', [ 'jquery', 'customize-preview' ], '1.0.0', true ); 46 | } 47 | add_action( 'customize_preview_init', __NAMESPACE__ . '\customize_scripts' ); 48 | 49 | /** 50 | * Add support for the fancy new edit icons. 51 | * 52 | * @param object $wp_customize Instance of WP_Customize_Class. 53 | * 54 | * @author WebDevStudios 55 | * @link https://make.wordpress.org/core/2016/02/16/selective-refresh-in-the-customizer/. 56 | */ 57 | function selective_refresh_support( $wp_customize ) { 58 | 59 | // The
classname to append edit icon too. 60 | $settings = [ 61 | 'blogname' => '.site-title a', 62 | 'blogdescription' => '.site-description', 63 | 'wd_s_copyright_text' => '.site-info', 64 | ]; 65 | 66 | // Loop through, and add selector partials. 67 | foreach ( (array) $settings as $setting => $selector ) { 68 | $args = [ 'selector' => $selector ]; 69 | $wp_customize->selective_refresh->add_partial( $setting, $args ); 70 | } 71 | } 72 | add_action( 'customize_register', __NAMESPACE__ . '\selective_refresh_support' ); 73 | 74 | /** 75 | * Add live preview support via postMessage. 76 | * 77 | * Note: You will need to hook this up via livepreview.js 78 | * 79 | * @author WebDevStudios 80 | * 81 | * @param object $wp_customize Instance of WP_Customize_Class. 82 | * @link https://codex.wordpress.org/Theme_Customization_API#Part_3:_Configure_Live_Preview_.28Optional.29. 83 | */ 84 | function live_preview_support( $wp_customize ) { 85 | 86 | // Settings to apply live preview to. 87 | $settings = [ 88 | 'blogname', 89 | 'blogdescription', 90 | 'header_textcolor', 91 | 'wd_s_copyright_text', 92 | ]; 93 | 94 | // Loop through and add the live preview to each setting. 95 | foreach ( (array) $settings as $setting_name ) { 96 | 97 | // Try to get the customizer setting. 98 | $setting = $wp_customize->get_setting( $setting_name ); 99 | 100 | // Skip if it is not an object to avoid notices. 101 | if ( ! is_object( $setting ) ) { 102 | continue; 103 | } 104 | 105 | // Set the transport to avoid page refresh. 106 | $setting->transport = 'postMessage'; 107 | } 108 | } 109 | add_action( 'customize_register', __NAMESPACE__ . '\live_preview_support', 999 ); 110 | -------------------------------------------------------------------------------- /inc/customizer/panels.php: -------------------------------------------------------------------------------- 1 | add_panel( 20 | 'site-options', 21 | [ 22 | 'priority' => 10, 23 | 'capability' => 'edit_theme_options', 24 | 'theme_supports' => '', 25 | 'title' => esc_html__( 'Site Options', 'wd_s' ), 26 | 'description' => esc_html__( 'Other theme options.', 'wd_s' ), 27 | ] 28 | ); 29 | } 30 | 31 | add_action( 'customize_register', __NAMESPACE__ . '\customize_panels' ); 32 | -------------------------------------------------------------------------------- /inc/customizer/sections.php: -------------------------------------------------------------------------------- 1 | add_section( 20 | 'wd_s_additional_scripts_section', 21 | [ 22 | 'title' => esc_html__( 'Additional Scripts', 'wd_s' ), 23 | 'priority' => 10, 24 | 'panel' => 'site-options', 25 | ] 26 | ); 27 | 28 | // Register a footer section. 29 | $wp_customize->add_section( 30 | 'wd_s_footer_section', 31 | [ 32 | 'title' => esc_html__( 'Footer Customizations', 'wd_s' ), 33 | 'priority' => 90, 34 | 'panel' => 'site-options', 35 | ] 36 | ); 37 | } 38 | add_action( 'customize_register', __NAMESPACE__ . '\customize_sections' ); 39 | -------------------------------------------------------------------------------- /inc/customizer/settings.php: -------------------------------------------------------------------------------- 1 | add_setting( 20 | 'wd_s_header_scripts', 21 | [ 22 | 'default' => '', 23 | 'sanitize_callback' => 'force_balance_tags', 24 | ] 25 | ); 26 | 27 | // Create the setting field. 28 | $wp_customize->add_control( 29 | 'wd_s_header_scripts', 30 | [ 31 | 'label' => esc_attr__( 'Header Scripts', 'wd_s' ), 32 | 'description' => esc_attr__( 'Additional scripts to add to the header. Basic HTML tags are allowed.', 'wd_s' ), 33 | 'section' => 'wd_s_additional_scripts_section', 34 | 'type' => 'textarea', 35 | ] 36 | ); 37 | 38 | // Register a setting. 39 | $wp_customize->add_setting( 40 | 'wd_s_footer_scripts', 41 | [ 42 | 'default' => '', 43 | 'sanitize_callback' => 'force_balance_tags', 44 | ] 45 | ); 46 | 47 | // Create the setting field. 48 | $wp_customize->add_control( 49 | 'wd_s_footer_scripts', 50 | [ 51 | 'label' => esc_attr__( 'Footer Scripts', 'wd_s' ), 52 | 'description' => esc_attr__( 'Additional scripts to add to the footer. Basic HTML tags are allowed.', 'wd_s' ), 53 | 'section' => 'wd_s_additional_scripts_section', 54 | 'type' => 'textarea', 55 | ] 56 | ); 57 | } 58 | 59 | add_action( 'customize_register', __NAMESPACE__ . '\customize_additional_scripts' ); 60 | 61 | 62 | /** 63 | * Register copyright text setting. 64 | * 65 | * @author WebDevStudios 66 | * 67 | * @param WP_Customize_Manager $wp_customize Instance of WP_Customize_Manager. 68 | */ 69 | function customize_copyright_text( $wp_customize ) { 70 | // Register a setting. 71 | $wp_customize->add_setting( 72 | 'wd_s_copyright_text', 73 | [ 74 | 'default' => '', 75 | 'sanitize_callback' => 'wp_kses_post', 76 | ] 77 | ); 78 | 79 | // Create the setting field. 80 | $wp_customize->add_control( 81 | 'wd_s_copyright_text', 82 | [ 83 | 'label' => esc_attr__( 'Copyright Text', 'wd_s' ), 84 | 'description' => esc_attr__( 'The copyright text will be displayed in the footer. Basic HTML tags allowed.', 'wd_s' ), 85 | 'section' => 'wd_s_footer_section', 86 | 'type' => 'textarea', 87 | ] 88 | ); 89 | } 90 | 91 | add_action( 'customize_register', __NAMESPACE__ . '\customize_copyright_text' ); 92 | -------------------------------------------------------------------------------- /inc/functions/get-attachment-id-from-url.php: -------------------------------------------------------------------------------- 1 | get_var( $wpdb->prepare( "SELECT wposts.ID FROM {$wpdb->posts} wposts, {$wpdb->postmeta} wpostmeta WHERE wposts.ID = wpostmeta.post_id AND wpostmeta.meta_key = '_wp_attached_file' AND wpostmeta.meta_value = %s AND wposts.post_type = 'attachment'", $attachment_url ) ); 44 | } 45 | 46 | return $attachment_id; 47 | } 48 | -------------------------------------------------------------------------------- /inc/functions/get-categorized-blog.php: -------------------------------------------------------------------------------- 1 | 'count' ] ); 22 | 23 | $category_count = isset( $category_count_query[0] ) ? (int) $category_count_query[0] : 0; 24 | 25 | set_transient( 'wd_s_categories', $category_count ); 26 | } 27 | 28 | return $category_count > 1; 29 | } 30 | -------------------------------------------------------------------------------- /inc/functions/get-custom-logo-url.php: -------------------------------------------------------------------------------- 1 | '', 33 | 'icon' => '', 34 | 'title' => '', 35 | 'desc' => '', 36 | 'stroke-width' => '', 37 | 'height' => '', 38 | 'width' => '', 39 | ]; 40 | 41 | // Parse args. 42 | $args = wp_parse_args( $args, $defaults ); 43 | 44 | // Figure out which title to use. 45 | $block_title = ( $args['title'] ) ? $args['title'] : $args['icon']; 46 | 47 | // Generate random IDs for the title and description. 48 | $random_number = wp_rand( 0, 99999 ); 49 | $block_title_id = 'title-' . sanitize_title( $block_title ) . '-' . $random_number; 50 | $desc_id = 'desc-' . sanitize_title( $block_title ) . '-' . $random_number; 51 | 52 | // Set ARIA. 53 | $aria_hidden = ' aria-hidden="true"'; 54 | $aria_labelledby = ''; 55 | 56 | if ( $args['title'] && $args['desc'] ) { 57 | $aria_labelledby = ' aria-labelledby="' . $block_title_id . ' ' . $desc_id . '"'; 58 | $aria_hidden = ''; 59 | } 60 | 61 | // Set SVG parameters. 62 | $color = ( $args['color'] ) ? ' color="' . $args['color'] . '"' : ''; 63 | $stroke_width = ( $args['stroke-width'] ) ? ' stroke-width="' . $args['stroke-width'] . '"' : ''; 64 | $height = ( $args['height'] ) ? ' height="' . $args['height'] . '"' : ''; 65 | $width = ( $args['width'] ) ? ' width="' . $args['width'] . '"' : ''; 66 | 67 | // Start a buffer... 68 | ob_start(); 69 | ?> 70 | 71 | 78 | class="icon " 79 | 83 | role="img"> 84 | 85 | 86 | 87 | 88 | 92 | 93 | 94 | 95 | 96 | 97 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 20, 24 | 'more' => '...', 25 | 'post' => '', 26 | ]; 27 | 28 | // Parse args. 29 | $args = wp_parse_args( $args, $defaults ); 30 | 31 | // Trim the excerpt. 32 | return wp_trim_words( get_the_excerpt( $args['post'] ), absint( $args['length'] ), esc_html( $args['more'] ) ); 33 | } 34 | -------------------------------------------------------------------------------- /inc/functions/get-trimmed-title.php: -------------------------------------------------------------------------------- 1 | 12, 23 | 'more' => '...', 24 | ]; 25 | 26 | // Parse args. 27 | $args = wp_parse_args( $args, $defaults ); 28 | 29 | // Trim the title. 30 | return wp_kses_post( wp_trim_words( get_the_title( get_the_ID() ), $args['length'], $args['more'] ) ); 31 | } 32 | -------------------------------------------------------------------------------- /inc/functions/security.php: -------------------------------------------------------------------------------- 1 | post_content : ''; 30 | 31 | // Strip all tags from the post content we just grabbed. 32 | $default_content = ( $post_content ) ? wp_strip_all_tags( strip_shortcodes( $post_content ) ) : $post_content; 33 | 34 | // Set our default title. 35 | $default_title = get_bloginfo( 'name' ); 36 | 37 | // Set our default URL. 38 | $default_url = get_permalink(); 39 | 40 | // Set our base description. 41 | $default_base_description = ( get_bloginfo( 'description' ) ) ? get_bloginfo( 'description' ) : esc_html__( 'Visit our website to learn more.', 'wd_s' ); 42 | 43 | // Set the card type. 44 | $default_type = 'article'; 45 | 46 | // Get our custom logo URL. We'll use this on archives and when no featured image is found. 47 | $logo_id = get_theme_mod( 'custom_logo' ); 48 | $logo_image = ( $logo_id ) ? wp_get_attachment_image_src( $logo_id, 'full' ) : ''; 49 | $logo_url = ( $logo_id ) ? $logo_image[0] : ''; 50 | 51 | // Set our final defaults. 52 | $card_title = $default_title; 53 | $card_description = $default_base_description; 54 | $card_long_description = $default_base_description; 55 | $card_url = $default_url; 56 | $card_image = $logo_url; 57 | $card_type = $default_type; 58 | 59 | // Let's start overriding! 60 | // All singles. 61 | if ( is_singular() ) { 62 | 63 | if ( has_post_thumbnail() ) { 64 | $card_image = get_the_post_thumbnail_url(); 65 | } 66 | } 67 | 68 | // Single posts/pages that aren't the front page. 69 | if ( is_singular() && ! is_front_page() ) { 70 | 71 | $card_title = get_the_title() . ' - ' . $default_title; 72 | $card_description = ( $default_content ) ? wp_trim_words( $default_content, 53, '...' ) : $default_base_description; 73 | $card_long_description = ( $default_content ) ? wp_trim_words( $default_content, 140, '...' ) : $default_base_description; 74 | } 75 | 76 | // Categories, Tags, and Custom Taxonomies. 77 | if ( is_category() || is_tag() || is_tax() ) { 78 | 79 | $term_name = single_term_title( '', false ); 80 | $card_title = $term_name . ' - ' . $default_title; 81 | $specify = ( is_category() ) ? esc_html__( 'categorized in', 'wd_s' ) : esc_html__( 'tagged with', 'wd_s' ); 82 | $queried_object = get_queried_object(); 83 | $card_url = get_term_link( $queried_object ); 84 | $card_type = 'website'; 85 | 86 | // Translators: get the term name. 87 | $card_long_description = sprintf( esc_html__( 'Posts %1$s %2$s.', 'wd_s' ), $specify, $term_name ); 88 | $card_description = $card_long_description; 89 | } 90 | 91 | // Search results. 92 | if ( is_search() ) { 93 | 94 | $search_term = get_search_query(); 95 | $card_title = $search_term . ' - ' . $default_title; 96 | $card_url = get_search_link( $search_term ); 97 | $card_type = 'website'; 98 | 99 | // Translators: get the search term. 100 | $card_long_description = sprintf( esc_html__( 'Search results for %s.', 'wd_s' ), $search_term ); 101 | $card_description = $card_long_description; 102 | } 103 | 104 | if ( is_home() ) { 105 | 106 | $posts_page = get_option( 'page_for_posts' ); 107 | $card_title = get_the_title( $posts_page ) . ' - ' . $default_title; 108 | $card_url = get_permalink( $posts_page ); 109 | $card_type = 'website'; 110 | } 111 | 112 | // Front page. 113 | if ( is_front_page() ) { 114 | 115 | $front_page = get_option( 'page_on_front' ); 116 | $card_title = ( $front_page ) ? get_the_title( $front_page ) . ' - ' . $default_title : $default_title; 117 | $card_url = get_home_url(); 118 | $card_type = 'website'; 119 | } 120 | 121 | // Post type archives. 122 | if ( is_post_type_archive() ) { 123 | 124 | $post_type_name = get_post_type(); 125 | $card_title = $post_type_name . ' - ' . $default_title; 126 | $card_url = get_post_type_archive_link( $post_type_name ); 127 | $card_type = 'website'; 128 | } 129 | 130 | // Media page. 131 | if ( is_attachment() ) { 132 | $attachment_id = get_the_ID(); 133 | $card_image = ( wp_attachment_is_image( $attachment_id ) ) ? wp_get_attachment_image_url( $attachment_id, 'full' ) : $card_image; 134 | } 135 | 136 | ?> 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | with the_content(); 4 | * 5 | * @package wd_s 6 | */ 7 | 8 | namespace WebDevStudios\wd_s; 9 | 10 | /** 11 | * Customize "Read More" string on with the_content(); 12 | * 13 | * @author WebDevStudios 14 | * 15 | * @return string Read more link. 16 | */ 17 | function content_more_link() { 18 | return ' ' . esc_html__( 'Read More', 'wd_s' ) . '...'; 19 | } 20 | 21 | add_filter( 'the_content_more_link', __NAMESPACE__ . '\content_more_link' ); 22 | -------------------------------------------------------------------------------- /inc/hooks/custom-mime-types.php: -------------------------------------------------------------------------------- 1 | %2$s', get_permalink( get_the_ID() ), esc_html__( 'Read more...', 'wd_s' ) ); 19 | } 20 | 21 | add_filter( 'excerpt_more', __NAMESPACE__ . '\excerpt_more' ); 22 | -------------------------------------------------------------------------------- /inc/hooks/get-post-content.php: -------------------------------------------------------------------------------- 1 | 4 | * 5 | * @package wd_s 6 | */ 7 | 8 | namespace WebDevStudios\wd_s; 9 | 10 | /** 11 | * Adds custom classes to apply to
12 | * 13 | * @author WebDevStudios 14 | * 15 | * @param array $new_classes Classes for the
element. 16 | * 17 | * @return array main classes. 18 | */ 19 | function main_classes( $new_classes ) { 20 | 21 | $classes = [ 'site-main' ]; 22 | 23 | if ( ! empty( $new_classes ) ) { 24 | $classes = array_merge( $classes, $new_classes ); 25 | } 26 | 27 | $classes = apply_filters( 'wd_s_main_classes', $classes ); 28 | 29 | return implode( ' ', $classes ); 30 | } 31 | -------------------------------------------------------------------------------- /inc/hooks/print-customizer-footer-scripts.php: -------------------------------------------------------------------------------- 1 | 16 | * @since 2023-05-30 17 | */ 18 | function register_wds_category( $categories ) { 19 | $custom_block_category = [ 20 | 'slug' => __( 'wds-blocks-category', 'wd_s' ), 21 | 'title' => __( 'WDS Blocks', 'wd_s' ), 22 | ]; 23 | 24 | $categories_sorted = []; 25 | $categories_sorted[0] = $custom_block_category; 26 | 27 | foreach ( $categories as $category ) { 28 | $categories_sorted[] = $category; 29 | } 30 | 31 | return $categories_sorted; 32 | } 33 | 34 | add_filter( 'block_categories_all', __NAMESPACE__ . '\register_wds_category', 10, 1 ); 35 | -------------------------------------------------------------------------------- /inc/hooks/register-block-pattern-category.php: -------------------------------------------------------------------------------- 1 | __( 'WDS Patterns', 'wd_s' ), 19 | ] 20 | ); 21 | 22 | // Remove default patterns. 23 | remove_theme_support( 'core-block-patterns' ); 24 | } 25 | add_action( 'init', __NAMESPACE__ . '\register_custom_block_pattern_category', 9 ); 26 | -------------------------------------------------------------------------------- /inc/hooks/remove-archive-title-prefix.php: -------------------------------------------------------------------------------- 1 | 17 | 20 | 17 | 18 | 19 | 20 | '1.0.0', 23 | 'dependencies' => [ 'wp-polyfill' ], 24 | ]; 25 | } 26 | 27 | ?> 28 | 29 | 30 | '1.0.0', 23 | 'dependencies' => [ 'wp-polyfill' ], 24 | ]; 25 | } 26 | 27 | // Register styles & scripts. 28 | wp_enqueue_style( 'wd_s-styles', get_stylesheet_directory_uri() . '/build/index.css', [], $asset_file['version'] ); 29 | wp_enqueue_style( 'custom-preflight', get_stylesheet_directory_uri() . '/assets/tailwind-preflight.css', [], $asset_file['version'] ); 30 | wp_enqueue_script( 'wd_s-scripts', get_stylesheet_directory_uri() . '/build/index.js', $asset_file['dependencies'], $asset_file['version'], true ); 31 | 32 | if ( is_singular() && comments_open() && get_option( 'thread_comments' ) ) { 33 | wp_enqueue_script( 'comment-reply' ); 34 | } 35 | } 36 | add_action( 'wp_enqueue_scripts', __NAMESPACE__ . '\scripts' ); 37 | -------------------------------------------------------------------------------- /inc/setup/setup.php: -------------------------------------------------------------------------------- 1 | tag in the document head, and expect WordPress to 36 | * provide it for us. 37 | */ 38 | add_theme_support( 'title-tag' ); 39 | 40 | /** 41 | * Enable support for Post Thumbnails on posts and pages. 42 | * 43 | * @link https://developer.wordpress.org/themes/functionality/featured-images-post-thumbnails/ 44 | */ 45 | add_theme_support( 'post-thumbnails' ); 46 | add_image_size( 'full-width', 1920, 1080, false ); 47 | 48 | // Register navigation menus. 49 | register_nav_menus( 50 | [ 51 | 'primary' => esc_html__( 'Primary Menu', 'wd_s' ), 52 | ] 53 | ); 54 | 55 | /** 56 | * Switch default core markup for search form, comment form, and comments 57 | * to output valid HTML5. 58 | */ 59 | add_theme_support( 60 | 'html5', 61 | [ 62 | 'search-form', 63 | 'comment-form', 64 | 'comment-list', 65 | 'gallery', 66 | 'caption', 67 | ] 68 | ); 69 | 70 | // Custom logo support. 71 | add_theme_support( 72 | 'custom-logo', 73 | [ 74 | 'height' => 250, 75 | 'width' => 500, 76 | 'flex-height' => true, 77 | 'flex-width' => true, 78 | 'header-text' => [ 'site-title', 'site-description' ], 79 | ] 80 | ); 81 | 82 | // Add theme support for selective refresh for widgets. 83 | add_theme_support( 'customize-selective-refresh-widgets' ); 84 | 85 | // Gutenberg support for full-width/wide alignment of supported blocks. 86 | add_theme_support( 'align-wide' ); 87 | 88 | // Gutenberg editor styles support. 89 | add_theme_support( 'editor-styles' ); 90 | add_editor_style( 'build/index.css' ); 91 | } 92 | 93 | add_action( 'after_setup_theme', __NAMESPACE__ . '\setup' ); 94 | -------------------------------------------------------------------------------- /inc/shortcodes/.gitkeep: -------------------------------------------------------------------------------- 1 | # Forces git to create the empty post-types directory. This file can be removed after directory is created. 2 | -------------------------------------------------------------------------------- /inc/template-tags/print-comments.php: -------------------------------------------------------------------------------- 1 | ' . esc_attr__( 'Posted in %1$s', 'wd_s' ) . '
', $categories_list ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- XSS OK. 24 | } 25 | 26 | /* translators: used between list items, there is a space after the comma */ 27 | $tags_list = get_the_tag_list( '', esc_attr__( ', ', 'wd_s' ) ); 28 | if ( $tags_list ) { 29 | 30 | /* translators: the post tags */ 31 | printf( '', $tags_list ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- XSS OK. 32 | } 33 | } 34 | 35 | if ( ! is_single() && ! post_password_required() && ( comments_open() || get_comments_number() ) ) { 36 | echo ''; 39 | } 40 | 41 | edit_post_link( 42 | sprintf( 43 | /* translators: %s: Name of current post */ 44 | esc_html__( 'Edit %s', 'wd_s' ), 45 | wp_kses_post( get_the_title( '"', '"', false ) ) 46 | ), 47 | '' 49 | ); 50 | } 51 | -------------------------------------------------------------------------------- /inc/template-tags/print-numeric-pagination.php: -------------------------------------------------------------------------------- 1 | max_num_pages ) ? $query->max_num_pages : 1; 26 | 27 | // Set defaults. 28 | $defaults = [ 29 | 'prev_text' => '«', 30 | 'next_text' => '»', 31 | 'mid_size' => 4, 32 | 'total' => $total_pages, 33 | ]; 34 | 35 | // Parse args. 36 | $args = wp_parse_args( $args, $defaults ); 37 | 38 | if ( null === paginate_links( $args ) ) { 39 | return; 40 | } 41 | ?> 42 | 43 |
44 | 47 |
48 | 49 | esc_html__( 'by', 'wd_s' ), 24 | ]; 25 | 26 | // Parse args. 27 | $args = wp_parse_args( $args, $defaults ); 28 | 29 | ?> 30 | 36 | 37 | esc_html__( 'Posted on', 'wd_s' ), 24 | 'date_format' => get_option( 'date_format' ), 25 | ]; 26 | 27 | // Parse args. 28 | $args = wp_parse_args( $args, $defaults ); 29 | ?> 30 | 31 | 32 | 33 | 34 | '; 21 | foreach ( $post_taxonomies as $taxonomy ) { 22 | $terms = get_the_terms( $post_id, $taxonomy ); 23 | if ( $terms && ! is_wp_error( $terms ) ) { 24 | echo '

' . esc_html( ucfirst( $taxonomy ) ) . ': '; 25 | $term_links = array(); 26 | foreach ( $terms as $term ) { 27 | $term_links[] = '' . esc_html( $term->name ) . ''; 28 | } 29 | $imploded_terms = implode( ', ', $term_links ); 30 | echo wp_kses_post( $imploded_terms ); 31 | echo '

'; 32 | } 33 | } 34 | echo '
'; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /inc/wpcli/block-starter/block.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schemas.wp.org/trunk/block.json", 3 | "apiVersion": 2, 4 | "name": "wds/{{name}}", 5 | "title": "{{title}}", 6 | "description": "{{description}}", 7 | "editorScript": "file:./editor.js", 8 | "editorStyle": "file:../../build/{{name}}.editor.css", 9 | "script": "file:./script.js", 10 | "style": "file:../../build/{{name}}.css", 11 | "category": "wds-blocks-category", 12 | "icon": "{{icon}}", 13 | "keywords": [ "{{name}}", "{{keyword}}", "block" ], 14 | "acf": { 15 | "mode": "auto", 16 | "renderTemplate": "{{name}}.php" 17 | }, 18 | "supports": { 19 | "align": false, 20 | "anchor": true, 21 | "color": {}, 22 | "customClassName": true, 23 | "jsx": true 24 | }, 25 | "example": { 26 | "attributes": { 27 | "mode": "preview", 28 | "data": { 29 | "_is_preview": "true" 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /inc/wpcli/block-starter/block.php: -------------------------------------------------------------------------------- 1 | 20 |
class=""> 21 | 22 |
23 | -------------------------------------------------------------------------------- /inc/wpcli/block-starter/editor.asset.php: -------------------------------------------------------------------------------- 1 | array(), 4 | 'version' => false, 5 | ); 6 | -------------------------------------------------------------------------------- /inc/wpcli/block-starter/editor.js: -------------------------------------------------------------------------------- 1 | // Editor JS here. 2 | -------------------------------------------------------------------------------- /inc/wpcli/block-starter/editor.scss: -------------------------------------------------------------------------------- 1 | // Editor styles. 2 | -------------------------------------------------------------------------------- /inc/wpcli/block-starter/script.asset.php: -------------------------------------------------------------------------------- 1 | array(), 4 | 'version' => false, 5 | ); 6 | -------------------------------------------------------------------------------- /inc/wpcli/block-starter/script.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Block script. 3 | * 4 | * @package 5 | * @since 2.0.0 6 | */ 7 | 8 | // add JS here. 9 | -------------------------------------------------------------------------------- /inc/wpcli/block-starter/style.scss: -------------------------------------------------------------------------------- 1 | // Frontend styles. 2 | -------------------------------------------------------------------------------- /inc/wpcli/class-blocks-scaffold.php: -------------------------------------------------------------------------------- 1 | [--title=] [--desc=] [--keyword=] [--icon=] [--namespace=] 36 | * 37 | * ## OPTIONS 38 | * 39 | * 40 | * : The block name. Must only have alphabetical characters. 41 | * 42 | * [--desc=] 43 | * : The bloc description. 44 | * 45 | * [--keyword=] 46 | * : They keyword for the block. 47 | * 48 | * [--icon=] 49 | * : Block Icon. 50 | * 51 | * [--namespace=] 52 | * : Block Namespace. 53 | * : Default: WebDevStudios\wd_s 54 | * 55 | * ## EXAMPLES 56 | * 57 | * wp wds create_portable_block myblock --title="This is myblock" --desc="This block is used for wds." --keyword="myblock" --icon="table-row-before" --namespace="wds" 58 | * @since 2.0.0 59 | * @param string $name The block name. 60 | * @param array $assoc_args The block args. 61 | */ 62 | public function create_portable_block( $name, $assoc_args ) { 63 | $this->name = esc_html( $name[0] ); 64 | 65 | // validate name. 66 | if ( ! preg_match( '/^[a-zA-Z0-9\-]+$/', $this->name ) ) { 67 | WP_CLI::error( 'Invalid name, Block name must only contain upper and lowercase letters.', true ); 68 | } 69 | 70 | if ( ! isset( $args['namespace'] ) && preg_match( '/[\/\\\\]/', $assoc_args['namespace'] ) ) { 71 | WP_CLI::error( 'Invalid namespace, Block namespace must not contain slashes.', true ); 72 | } 73 | 74 | // Merge with default args. 75 | $args = wp_parse_args( 76 | $assoc_args, 77 | [ 78 | 'title' => ucfirst( $this->name ), 79 | 'desc' => '', 80 | 'keywords' => strtolower( $this->name ), 81 | 'icon' => 'table-row-before', 82 | 'namespace' => 'wds/', 83 | ] 84 | ); 85 | 86 | // create the directory. 87 | $this->create_block_dir(); 88 | 89 | // create block json. 90 | $this->create_block_json( $args ); 91 | 92 | // create block renderer. 93 | $this->create_block_render_php( $args ); 94 | 95 | // create editor assets. 96 | $this->create_block_editor_assets(); 97 | 98 | // create FE assets. 99 | $this->create_block_assets(); 100 | 101 | WP_CLI::success( $this->name . ' block created.' ); 102 | } 103 | 104 | /** 105 | * Init file system. 106 | * 107 | * @since 2.0.0 108 | */ 109 | private function init_filesystem() { 110 | // File system support. 111 | global $wp_filesystem; 112 | require_once ABSPATH . '/wp-admin/includes/file.php'; 113 | WP_Filesystem(); 114 | 115 | return $wp_filesystem; 116 | } 117 | 118 | /** 119 | * Create the block directory. 120 | * 121 | * @author Biplav Subedi 122 | * @since 2.0.0 123 | */ 124 | private function create_block_dir() { 125 | $dir = ROOT_PATH . 'blocks/' . $this->name; 126 | 127 | if ( ! $this->init_filesystem()->exists( $dir ) ) { 128 | $this->init_filesystem()->mkdir( $dir, 0755 ); 129 | } else { 130 | WP_CLI::error( 'Block directory already exists.', true ); 131 | } 132 | } 133 | 134 | /** 135 | * Create the block render file. 136 | * 137 | * @param array $args Block details. 138 | * @since 2.0.0 139 | * @author Biplav Subedi 140 | */ 141 | private function create_block_render_php( $args ) { 142 | $local_file = ROOT_PATH . 'inc/wpcli/block-starter/block.php'; 143 | $content = ''; 144 | 145 | if ( $this->init_filesystem()->exists( $local_file ) ) { 146 | $content = $this->init_filesystem()->get_contents( $local_file ); 147 | $content = str_replace( 'wds', $args['namespace'], $content ); 148 | if ( ! $this->init_filesystem()->put_contents( ROOT_PATH . 'blocks/' . $this->name . '/' . $this->name . '.php', $content ) ) { 149 | WP_CLI::error( 'ERROR :: Could not create a render file.', true ); 150 | } 151 | } else { 152 | WP_CLI::error( 'ERROR :: Could not create a render file.', true ); 153 | } 154 | } 155 | 156 | /** 157 | * Create the block json. 158 | * 159 | * @param array $args Block details. 160 | * @since 2.0.0 161 | * @author Biplav Subedi 162 | */ 163 | private function create_block_json( $args ) { 164 | $local_file = ROOT_PATH . 'inc/wpcli/block-starter/block.json'; 165 | $content = ''; 166 | 167 | if ( $this->init_filesystem()->exists( $local_file ) ) { 168 | $content = $this->init_filesystem()->get_contents( $local_file ); 169 | $content = str_replace( 170 | [ 171 | '{{name}}', 172 | '{{title}}', 173 | '{{description}}', 174 | '{{icon}}', 175 | 'wds/', 176 | '{{keyword}}', 177 | ], 178 | [ 179 | $this->name, 180 | $args['title'], 181 | $args['desc'], 182 | $args['icon'], 183 | trailingslashit( $args['namespace'] ), 184 | $args['keyword'], 185 | ], 186 | $content 187 | ); 188 | } 189 | 190 | if ( ! $this->init_filesystem()->put_contents( ROOT_PATH . 'blocks/' . $this->name . '/block.json', $content ) ) { 191 | WP_CLI::error( 'ERROR :: Could not create a block json file.', true ); 192 | } 193 | } 194 | 195 | /** 196 | * Create the block editor assets. 197 | * 198 | * @since 2.0.0 199 | * @author Biplav Subedi 200 | */ 201 | private function create_block_editor_assets() { 202 | $asset_js = ROOT_PATH . 'inc/wpcli/block-starter/editor.js'; 203 | $asset_php = ROOT_PATH . 'inc/wpcli/block-starter/editor.asset.php'; 204 | 205 | $asset_scss = ROOT_PATH . 'inc/wpcli/block-starter/editor.scss'; 206 | 207 | if ( 208 | ! $this->init_filesystem()->exists( $asset_js ) 209 | || ! $this->init_filesystem()->exists( $asset_php ) 210 | || ! $this->init_filesystem()->exists( $asset_scss ) 211 | ) { 212 | WP_CLI::error( 'ERROR :: Could not find editor assets.', true ); 213 | } 214 | 215 | // copy editor js. 216 | if ( ! $this->init_filesystem()->copy( $asset_js, ROOT_PATH . 'blocks/' . $this->name . '/editor.js' ) ) { 217 | WP_CLI::error( 'ERROR :: Could not create editor js file.', true ); 218 | } 219 | // copy editor.asset.php. 220 | if ( ! $this->init_filesystem()->copy( $asset_php, ROOT_PATH . 'blocks/' . $this->name . '/editor.asset.php' ) ) { 221 | WP_CLI::error( 'ERROR :: Could not create editor asset php file.', true ); 222 | } 223 | 224 | // copy styles. 225 | if ( ! $this->init_filesystem()->copy( $asset_scss, ROOT_PATH . 'assets/scss/blocks/custom/' . $this->name . '.editor.scss' ) ) { 226 | WP_CLI::error( 'ERROR :: Could not create styles file.', true ); 227 | } 228 | 229 | // add js file for build process. 230 | if ( 231 | ! $this->init_filesystem()->put_contents( 232 | ROOT_PATH . 'assets/js/blocks/custom/' . $this->name . '.editor.js', 233 | "import '../../../scss/blocks/custom/" . $this->name . ".editor.scss';\n" 234 | ) 235 | ) { 236 | WP_CLI::error( 'ERROR :: Could not create a block js style file.', true ); 237 | } 238 | } 239 | 240 | /** 241 | * Create the block main styles. 242 | * 243 | * @since 2.0.0 244 | * @author Biplav Subedi 245 | */ 246 | private function create_block_assets() { 247 | $asset_js = ROOT_PATH . 'inc/wpcli/block-starter/script.js'; 248 | $asset_php = ROOT_PATH . 'inc/wpcli/block-starter/script.asset.php'; 249 | 250 | $asset_scss = ROOT_PATH . 'inc/wpcli/block-starter/style.scss'; 251 | 252 | if ( 253 | ! $this->init_filesystem()->exists( $asset_js ) 254 | || ! $this->init_filesystem()->exists( $asset_php ) 255 | || ! $this->init_filesystem()->exists( $asset_scss ) 256 | ) { 257 | WP_CLI::error( 'ERROR :: Could not find block assets.', true ); 258 | } 259 | 260 | // copy editor js. 261 | if ( ! $this->init_filesystem()->copy( $asset_js, ROOT_PATH . 'blocks/' . $this->name . '/script.js' ) ) { 262 | WP_CLI::error( 'ERROR :: Could not create script js file.', true ); 263 | } 264 | 265 | // copy script.asset.php. 266 | if ( ! $this->init_filesystem()->copy( $asset_php, ROOT_PATH . 'blocks/' . $this->name . '/script.asset.php' ) ) { 267 | WP_CLI::error( 'ERROR :: Could not create script asset php file.', true ); 268 | } 269 | 270 | // copy styles. 271 | if ( ! $this->init_filesystem()->copy( $asset_scss, ROOT_PATH . 'assets/scss/blocks/custom/' . $this->name . '.scss' ) ) { 272 | WP_CLI::error( 'ERROR :: Could not create styles file.', true ); 273 | } 274 | 275 | // add js file for build process. 276 | if ( 277 | ! $this->init_filesystem()->put_contents( 278 | ROOT_PATH . 'assets/js/blocks/custom/' . $this->name . '.js', 279 | "import '../../../scss/blocks/custom/" . $this->name . ".scss';\n" 280 | ) 281 | ) { 282 | WP_CLI::error( 'ERROR :: Could not create a block js style file.', true ); 283 | } 284 | } 285 | } 286 | -------------------------------------------------------------------------------- /inc/wpcli/wpcli.php: -------------------------------------------------------------------------------- 1 | 18 | * @return void 19 | */ 20 | function cli_register_commands() { 21 | WP_CLI::add_command( 'wds', __NAMESPACE__ . '\Blocks_Scaffold' ); 22 | } 23 | add_action( 'cli_init', __NAMESPACE__ . '\cli_register_commands' ); 24 | 25 | /** 26 | * Register Blocks 27 | * 28 | * @return void 29 | * @author Jenna Hines 30 | * @since 2.0.0 31 | */ 32 | function acf_register_blocks() { 33 | $wds_acf_blocks = glob( ROOT_PATH . 'blocks/*/block.json' ); 34 | 35 | foreach ( $wds_acf_blocks as $block ) { 36 | register_block_type( $block ); 37 | } 38 | } 39 | add_action( 'init', __NAMESPACE__ . '\acf_register_blocks' ); 40 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | 19 |
20 |
21 | 22 | 39 | 40 |
41 |
42 | 43 | 44 | -------------------------------------------------------------------------------- /lefthook.yml: -------------------------------------------------------------------------------- 1 | # https://github.com/Arkweid/lefthook/blob/master/docs/full_guide.md 2 | 3 | pre-commit: 4 | commands: 5 | formatphp: 6 | glob: '*.php' 7 | run: ./vendor/bin/phpcbf {staged_files} 8 | lintphp: 9 | glob: '*.php' 10 | run: ./vendor/bin/phpcs {staged_files} 11 | formatstyles: 12 | glob: '*.{css,scss}' 13 | run: npx prettier {staged_files} --write 14 | lintstyles: 15 | glob: '*.{css,scss}' 16 | run: npx stylelint {staged_files} --fix 17 | formatjavascript: 18 | glob: '*.{js,ts,tsx}' 19 | run: npx prettier {staged_files} --write 20 | lintjavascript: 21 | glob: '*.{js,ts,tsx}' 22 | run: npx eslint {staged_files} --fix 23 | lintmarkdown: 24 | glob: '*.{md}' 25 | run: npx markdownlint-cli {staged_files} --fix 26 | other: 27 | glob: '*.{yml,json,html}' 28 | exclude: 'group_*.json,CHANGELOG.md' # Exclude specific files, including CHANGELOG.md 29 | run: | 30 | staged_files="$LEFTHOOK_STAGED_FILES" 31 | for file in $staged_files 32 | do 33 | if [[ ! "$file" =~ ^templates/ && "$file" != "package-lock.json" && "$file" != "composer.lock" ]]; then 34 | npx prettier "$file" --write 35 | fi 36 | done 37 | stage_fixed: true 38 | stage: pre-commit 39 | merge-conflict: 40 | run: echo "Resolve merge conflicts before committing" 41 | exit-status: 42 | run: echo "All pre-commit checks passed" 43 | templates-exclude: 44 | glob: 'templates/**' # Match all files and subdirectories within templates folder 45 | run: echo "Skipping templates folder" 46 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wd_s", 3 | "version": "4.0.0", 4 | "private": true, 5 | "description": "A starter theme from WebDevStudios.", 6 | "author": "WebDevStudios ", 7 | "license": "GPL-2.0-or-later", 8 | "keywords": [ 9 | "WordPress", 10 | "Theme" 11 | ], 12 | "homepage": "https://github.com/WebDevStudios/wd_s/#readme", 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/WebDevStudios/wd_s.git" 16 | }, 17 | "bugs": { 18 | "url": "https://github.com/WebDevStudios/wd_s/issues" 19 | }, 20 | "engines": { 21 | "node": ">=18", 22 | "npm": ">=9" 23 | }, 24 | "dependencies": { 25 | "@wordpress/eslint-plugin": "^17.0.0", 26 | "@wordpress/scripts": "^26.13.0", 27 | "clean-webpack-plugin": "^4.0.0", 28 | "copy-webpack-plugin": "^11.0.0", 29 | "cross-env": "^7.0.3", 30 | "css-minimizer-webpack-plugin": "^5.0.0", 31 | "eslint-plugin-eslint-comments": "^3.2.0", 32 | "eslint-webpack-plugin": "^4.0.1", 33 | "imagemin-webpack-plugin": "^2.4.2", 34 | "offline-github-changelog": "^3.0.1", 35 | "postcss-loader": "^7.3.3", 36 | "postcss-move-props-to-bg-image-query": "^4.0.0", 37 | "postcss-preset-env": "^9.1.1", 38 | "sass-loader": "^13.2.2", 39 | "stylelint-config-prettier": "^9.0.5", 40 | "stylelint-webpack-plugin": "^4.1.1", 41 | "svg-spritemap-webpack-plugin": "^4.5.0", 42 | "svg-transform-loader": "^2.0.13", 43 | "tailwindcss": "^3.3.3" 44 | }, 45 | "devDependencies": { 46 | "@evilmartians/lefthook": "^1.4.9", 47 | "@wordpress/prettier-config": "^2.14.0", 48 | "auto-changelog": "^2.4.0", 49 | "autoprefixer": "^10.4.16", 50 | "browser-sync": "^2.29.3", 51 | "css-loader": "^6.7.3", 52 | "eslint-plugin-prettier": "^5.0.0", 53 | "glob": "^10.3.3", 54 | "mini-css-extract-plugin": "^2.7.5", 55 | "npm-run-all": "^4.1.5", 56 | "pa11y-ci": "^3.0.1", 57 | "pa11y-ci-reporter-html": "^6.0.2", 58 | "postcss": "^8.4.31", 59 | "prettier": "npm:wp-prettier@^3.0.3", 60 | "webpack-cli": "^5.0.2", 61 | "webpack-merge": "^5.8.0" 62 | }, 63 | "scripts": { 64 | "accessibility": "pa11y-ci --reporter=pa11y-ci-reporter-html --sitemap $npm_config_url/sitemap.xml", 65 | "build": "cross-env NODE_ENV=production wp-scripts build --config webpack.prod.js", 66 | "build:pot": "composer run-script pot", 67 | "build:all": "composer install --quiet && composer run-script pot && npm run build", 68 | "check-engines": "wp-scripts check-engines", 69 | "check-licenses": "wp-scripts check-licenses", 70 | "dev": "npm run watch", 71 | "format": "run-p format:*", 72 | "format:css": "npx prettier '**/*.scss' --write && npm run lint:css -- --fix", 73 | "format:js": "wp-scripts format && npm run lint:js -- --fix", 74 | "format:md": "npx markdownlint-cli . --fix", 75 | "format:php": "composer run-script format", 76 | "lint": "run-p lint:*", 77 | "lint:css": "wp-scripts lint-style '**/*.scss'", 78 | "lint:js": "wp-scripts lint-js", 79 | "lint:md": "wp-scripts lint-md-docs", 80 | "lint:php": "composer run-script lint", 81 | "lint:pkg-json": "wp-scripts lint-pkg-json", 82 | "report": "composer run-script report", 83 | "packages-update": "wp-scripts packages-update", 84 | "postinstall": "npx lefthook install", 85 | "start": "cross-env NODE_ENV=development wp-scripts start", 86 | "sync": "browser-sync start --https --proxy https://wdunderscores.test --no-open --files 'build/*.*, **/*.html, **/*.php, !node_modules/**/*.html'", 87 | "version": "auto-changelog -p && git add CHANGELOG.md", 88 | "watch": "run-p start sync" 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /page.php: -------------------------------------------------------------------------------- 1 | 19 |
20 |
21 | 22 | 39 | 40 |
41 |
42 | 43 | 44 | -------------------------------------------------------------------------------- /parts/.gitkeep: -------------------------------------------------------------------------------- 1 | # Forces git to create the empty post-types directory. This file can be removed after directory is created. 2 | -------------------------------------------------------------------------------- /parts/footer.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /parts/header.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /patterns/footer.php: -------------------------------------------------------------------------------- 1 | ' : ''; 13 | 14 | // Generate the copyright information. 15 | $wds_copyright_info = esc_html__( ' Copyright © ', 'wd_s' ) . esc_attr( gmdate( 'Y' ) ) . esc_html__( '. All Rights Reserved.', 'wd_s' ); 16 | 17 | // Generate the theme attribution. 18 | $wds_theme_info = esc_html__( ' Proudly powered by WordPress. ', 'wd_s' ) . '' . esc_html__( 'WebDevStudios', 'wd_s' ) . '' . esc_html__( ' is a WD3 Party.', 'wd_s' ); 19 | ?> 20 | 21 | 22 |
23 | 24 | 25 | 26 |

27 | 28 | 29 | 30 |

31 | 32 |
33 | 34 | -------------------------------------------------------------------------------- /patterns/header.php: -------------------------------------------------------------------------------- 1 | ' : ''; 13 | ?> 14 | 15 |
16 | 17 |
18 | 19 |
20 | 21 |
22 | 23 | 24 | 25 |
26 | 27 |
28 | 29 |
30 | 31 |
32 | 33 | -------------------------------------------------------------------------------- /phpcs.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | Apply WordPress Coding Standards 4 | 5 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | ./ 75 | 76 | 77 | /build/* 78 | /node_modules/* 79 | /vendor/* 80 | 81 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | 'postcss-move-props-to-bg-image-query': {}, 5 | 'postcss-preset-env': { 6 | autoprefixer: { 7 | flexbox: 'no-2009', 8 | }, 9 | stage: 3, 10 | features: { 11 | 'nesting-rules': true, 12 | }, 13 | }, 14 | }, 15 | }; 16 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebDevStudios/wd_s/756a4a309c0c241fa2aba2c67d9434a95c02c74a/screenshot.png -------------------------------------------------------------------------------- /search.php: -------------------------------------------------------------------------------- 1 | 14 | 15 |
16 | 17 | 34 | 35 |
36 | 37 | 38 | -------------------------------------------------------------------------------- /searchform.php: -------------------------------------------------------------------------------- 1 | 11 | 12 |
13 | 17 | 18 |
19 | -------------------------------------------------------------------------------- /single.php: -------------------------------------------------------------------------------- 1 | 14 | 15 |
16 | 17 | 'is-layout-constrained', 27 | ] 28 | ) 29 | ); 30 | 31 | print_comments(); 32 | 33 | endwhile; // End of the loop. 34 | ?> 35 | 36 |
37 | 38 | 39 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | /* 2 | Theme Name: wd_s 3 | Theme URI: https://webdevstudios.com/ 4 | Author: WebDevStudios 5 | Author URI: https://webdevstudios.com/ 6 | Description: Hi. I'm a hybrid starter theme called wd_s, or wdunderscores. I'm a theme meant for hacking so don't use me as a Parent Theme. Instead, try turning me into the next, most awesome, WordPress theme out there. That's what I'm here for! 7 | Version: 4.0.0 8 | License: GNU General Public License v2 or later 9 | License URI: http://www.gnu.org/licenses/gpl-2.0.html 10 | Text Domain: wd_s 11 | Requires at least: 5.8 12 | Tested up to: 6.3 13 | Requires PHP: 8.0 14 | Tested up to: 8.2 15 | Tags: WebDevStudios, wds, gutenberg, tailwind, starter-theme, starter, custom, hybrid, hybrid-themne 16 | 17 | This theme, like WordPress, is licensed under the GPL. 18 | 19 | Based on Underscores http://underscores.me/, (C) 2012-2017 Automattic, Inc. 20 | */ 21 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | const fs = require( 'fs' ); 2 | const path = require( 'path' ); 3 | const glob = require( 'glob' ); 4 | 5 | // Get arrays of all of the files. 6 | const topLevelPhpFiles = glob.sync( './*.php' ), 7 | directoryFiles = [ 8 | './inc/*.php', 9 | './template-parts/*.php', 10 | './assets/js/**/*.js', 11 | ]; 12 | 13 | const themeJsonPath = path.join( __dirname, 'theme.json' ); 14 | const themeJson = fs.readFileSync( themeJsonPath ); 15 | const theme = JSON.parse( themeJson ); 16 | 17 | const { palette } = theme.settings.color; 18 | const colors = palette.reduce( ( acc, item ) => { 19 | const [ color, number ] = item.slug.split( '-' ); 20 | 21 | if ( number ) { 22 | // If there is a number identifier, make this an object 23 | if ( ! acc[ color ] ) { 24 | acc[ color ] = {}; 25 | } 26 | acc[ color ][ number ] = item.color; 27 | } else { 28 | acc[ color ] = item.color; 29 | } 30 | 31 | return acc; 32 | }, {} ); 33 | 34 | module.exports = { 35 | corePlugins: { 36 | preflight: false, 37 | }, 38 | content: topLevelPhpFiles.concat( directoryFiles ), 39 | theme: { colors }, 40 | }; 41 | -------------------------------------------------------------------------------- /template-parts/content-none.php: -------------------------------------------------------------------------------- 1 | 11 | 12 |
13 | 14 | 17 | 18 |
19 | 20 |

21 | Get started here.', 'wd_s' ), 26 | [ 27 | 'a' => [ 28 | 'href' => [], 29 | ], 30 | ] 31 | ), 32 | esc_url( admin_url( 'post-new.php' ) ) 33 | ); 34 | ?> 35 |

36 | 37 |

38 | 39 | 40 |

41 | 42 | 43 |
44 | 45 |
46 | -------------------------------------------------------------------------------- /template-parts/content-page.php: -------------------------------------------------------------------------------- 1 | 11 | 12 |
> 13 | 14 |
15 | ', '' ); ?> 16 |
17 | 18 |
19 | '', 26 | ] 27 | ); 28 | ?> 29 |
30 | 31 | 32 |
33 | "', '"', false ) 39 | ), 40 | '' 42 | ); 43 | ?> 44 |
45 | 46 | 47 |
48 | -------------------------------------------------------------------------------- /template-parts/content-password-protected.php: -------------------------------------------------------------------------------- 1 | 11 | 12 |
> 13 | 14 |
15 | ', '' ); ?> 16 |
17 | 18 |
19 | [], 24 | 'label' => [ 25 | 'for' => [], 26 | ], 27 | 'form' => [ 28 | 'action' => [], 29 | 'class' => [], 30 | 'method' => [], 31 | ], 32 | 'input' => [ 33 | 'id' => [], 34 | 'name' => [], 35 | 'size' => [], 36 | 'type' => [], 37 | 'value' => [], 38 | ], 39 | ] 40 | ); 41 | ?> 42 |
43 | 44 |
45 | -------------------------------------------------------------------------------- /template-parts/content-search.php: -------------------------------------------------------------------------------- 1 | 15 | 16 | 38 | -------------------------------------------------------------------------------- /template-parts/content.php: -------------------------------------------------------------------------------- 1 | 15 | 16 |
> 17 | 18 |
19 | ', '' ); 22 | else : 23 | the_title( '

', '

' ); 24 | endif; 25 | 26 | if ( 'post' === get_post_type() ) : 27 | ?> 28 | 43 | 44 |
45 | 46 |
47 | →', 'wd_s' ), 53 | [ 54 | 'span' => [ 55 | 'class' => [], 56 | ], 57 | ] 58 | ), 59 | the_title( '"', '"', false ) 60 | ) 61 | ); 62 | 63 | wp_link_pages( 64 | [ 65 | 'before' => '', 67 | ] 68 | ); 69 | ?> 70 |
71 | 72 |
73 | 74 |
75 | 76 |
77 | -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 |
8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require( 'path' ); 2 | const defaultConfig = require( '@wordpress/scripts/config/webpack.config' ); 3 | const CopyPlugin = require( 'copy-webpack-plugin' ); 4 | const SVGSpritemapPlugin = require( 'svg-spritemap-webpack-plugin' ); 5 | const { CleanWebpackPlugin } = require( 'clean-webpack-plugin' ); 6 | const MiniCssExtractPlugin = require( 'mini-css-extract-plugin' ); 7 | const ESLintPlugin = require( 'eslint-webpack-plugin' ); 8 | const StylelintPlugin = require( 'stylelint-webpack-plugin' ); 9 | 10 | /** 11 | * Webpack config (Development mode) 12 | * 13 | * @see https://developer.wordpress.org/block-editor/packages/packages-scripts/#provide-your-own-webpack-config 14 | */ 15 | module.exports = { 16 | ...defaultConfig, 17 | entry: { 18 | index: './assets/index.js', 19 | critical: './assets/critical.js', 20 | }, 21 | module: { 22 | rules: [ 23 | { 24 | test: /\.(webp|png|jpe?g|gif)$/, 25 | type: 'asset/resource', 26 | generator: { 27 | filename: 'images/[name][ext]', 28 | }, 29 | }, 30 | { 31 | test: /\.(sa|sc|c)ss$/, 32 | exclude: '/node_modules', 33 | use: [ 34 | MiniCssExtractPlugin.loader, 35 | 'css-loader', 36 | 'postcss-loader', 37 | 'svg-transform-loader/encode-query', 38 | 'sass-loader', 39 | ], 40 | }, 41 | { 42 | test: /\.svg$/, 43 | type: 'asset/inline', 44 | use: 'svg-transform-loader', 45 | }, 46 | { 47 | test: /\.(woff|woff2|eot|ttf|otf)$/, 48 | type: 'asset/resource', 49 | generator: { 50 | filename: 'fonts/[name][ext]', 51 | }, 52 | }, 53 | ], 54 | }, 55 | plugins: [ 56 | ...defaultConfig.plugins, 57 | 58 | new MiniCssExtractPlugin(), 59 | 60 | /** 61 | * Copy source files/directories to a build directory. 62 | * 63 | * @see https://www.npmjs.com/package/copy-webpack-plugin 64 | */ 65 | new CopyPlugin( { 66 | patterns: [ 67 | { 68 | from: '**/*.{jpg,jpeg,png,gif,svg}', 69 | to: 'images/[path][name][ext]', 70 | context: path.resolve( process.cwd(), 'assets/images' ), 71 | noErrorOnMissing: true, 72 | }, 73 | { 74 | from: '*.svg', 75 | to: 'images/icons/[name][ext]', 76 | context: path.resolve( 77 | process.cwd(), 78 | 'assets/images/icons' 79 | ), 80 | noErrorOnMissing: true, 81 | }, 82 | { 83 | from: '**/*.{woff,woff2,eot,ttf,otf}', 84 | to: 'fonts/[path][name][ext]', 85 | context: path.resolve( process.cwd(), 'assets/fonts' ), 86 | noErrorOnMissing: true, 87 | }, 88 | ], 89 | } ), 90 | 91 | /** 92 | * Generate an SVG sprite. 93 | * 94 | * @see https://github.com/cascornelissen/svg-spritemap-webpack-plugin 95 | */ 96 | new SVGSpritemapPlugin( 'assets/images/icons/*.svg', { 97 | output: { 98 | filename: 'images/icons/sprite.svg', 99 | }, 100 | sprite: { 101 | prefix: false, 102 | }, 103 | } ), 104 | 105 | /** 106 | * Clean build directory. 107 | * 108 | * @see https://www.npmjs.com/package/clean-webpack-plugin 109 | */ 110 | new CleanWebpackPlugin( { 111 | cleanAfterEveryBuildPatterns: [ '!fonts/**', '!*.woff2' ], 112 | } ), 113 | 114 | /** 115 | * Report JS warnings and errors to the command line. 116 | * 117 | * @see https://www.npmjs.com/package/eslint-webpack-plugin 118 | */ 119 | new ESLintPlugin(), 120 | 121 | /** 122 | * Report css warnings and errors to the command line. 123 | * 124 | * @see https://www.npmjs.com/package/stylelint-webpack-plugin 125 | */ 126 | new StylelintPlugin(), 127 | ], 128 | }; 129 | -------------------------------------------------------------------------------- /webpack.prod.js: -------------------------------------------------------------------------------- 1 | const { merge } = require( 'webpack-merge' ); 2 | const common = require( './webpack.config.js' ); 3 | const CssMinimizerPlugin = require( 'css-minimizer-webpack-plugin' ); 4 | const ImageminPlugin = require( 'imagemin-webpack-plugin' ).default; 5 | 6 | /** 7 | * Webpack config (Production mode) 8 | * 9 | * @see https://webpack.js.org/guides/production/ 10 | */ 11 | module.exports = merge( common, { 12 | plugins: [ 13 | /** 14 | * Uses Imagemin to compress source images. 15 | * 16 | * @see https://www.npmjs.com/package/imagemin-webpack-plugin 17 | */ 18 | new ImageminPlugin( { 19 | disable: false, 20 | test: /\.(jpe?g|png|gif|svg)$/i, 21 | gifsicle: { 22 | interlaced: true, 23 | }, 24 | optipng: { 25 | optimizationLevel: 3, 26 | }, 27 | jpegtran: { 28 | quality: 70, 29 | progressive: true, 30 | }, 31 | svgo: null, 32 | } ), 33 | ], 34 | optimization: { 35 | minimizer: [ 36 | /** 37 | * Minify CSS. 38 | * 39 | * @see https://www.npmjs.com/package/css-minimizer-webpack-plugin 40 | */ 41 | new CssMinimizerPlugin( { 42 | minimizerOptions: { 43 | preset: [ 44 | 'default', 45 | { 46 | discardComments: { removeAll: true }, 47 | }, 48 | ], 49 | }, 50 | } ), 51 | ], 52 | }, 53 | } ); 54 | --------------------------------------------------------------------------------