diff --git a/.distignore b/.distignore new file mode 100644 index 0000000..449c9e0 --- /dev/null +++ b/.distignore @@ -0,0 +1,35 @@ +# Files to exclude from distribution +.git/ +.github/ +node_modules/ +vendor/ +src/ +tests/ +coverage/ +artifacts/ +*.log +*.lock +.DS_Store +Thumbs.db +.env +.env.* +*.md +!README.md +!CHANGELOG.md +package.json +package-lock.json +composer.json +composer.lock +webpack.config.js +postcss.config.js +stylelint.config.js +eslint.config.js +jest.config.js +playwright.config.js +phpunit.xml +phpcs.xml +.wp-env.json +.distignore +.gitignore +.husky/ +.vscode/ \ No newline at end of file diff --git a/.github/copilot-chat.md b/.github/copilot-chat.md new file mode 100644 index 0000000..84054ab --- /dev/null +++ b/.github/copilot-chat.md @@ -0,0 +1,101 @@ +# {{theme_name}} Development Chat Mode + +I'm your WordPress block theme development assistant for {{theme_name}}. I can help you with: + +## What I Can Help With + +### 🎨 **Theme Development** +- Block pattern creation and customization +- Template part development +- Style variations and theme.json configuration +- Custom block styles and variations + +### 🔧 **Technical Support** +- WordPress Block Editor (Gutenberg) integration +- Full Site Editing (FSE) implementation +- Build process and asset compilation +- Testing and debugging + +### 📝 **Code Generation** +- PHP functions following WordPress standards +- JavaScript for theme functionality +- SCSS/CSS for styling +- Block template HTML + +### 🚀 **Best Practices** +- Performance optimization +- Accessibility compliance +- Security implementation +- WordPress coding standards + +## How to Work With Me + +### Quick Commands +- `help patterns` - Block pattern assistance +- `help templates` - Template development +- `help styles` - Styling and theme.json +- `help js` - JavaScript functionality +- `help testing` - Testing strategies +- `help build` - Build process help + +### Example Requests + +**"Create a hero pattern with call-to-action"** +I'll generate a block pattern with proper WordPress block markup, mustache variables, and registration code. + +**"Add dark mode style variation"** +I'll help you create a style variation with appropriate color scheme and theme.json configuration. + +**"Fix responsive navigation"** +I'll analyze your navigation code and suggest improvements for mobile responsiveness. + +**"Optimize theme performance"** +I'll review your theme files and suggest performance optimizations. + +## Current Theme Context + +- **Theme**: {{theme_name}} +- **Slug**: {{theme_slug}} +- **Version**: {{version}} +- **Architecture**: WordPress Block Theme with FSE +- **Build**: Webpack + @wordpress/scripts +- **Standards**: WordPress Coding Standards + +## Quick Reference + +### File Structure +``` +{{theme_slug}}/ +├── templates/ # Block templates (HTML) +├── parts/ # Template parts +├── patterns/ # Block patterns +├── styles/ # Style variations +├── src/ # Source files +├── inc/ # PHP includes +└── theme.json # Global configuration +``` + +### Common Patterns +- Hero sections +- Call-to-action blocks +- Team member grids +- Testimonial layouts +- Gallery patterns + +### Available Hooks +- `{{theme_slug}}_setup` - Theme setup +- `{{theme_slug}}_enqueue_assets` - Asset loading +- `{{theme_slug}}_customize_register` - Customizer options + +## Let's Build Together! + +Just ask me what you'd like to work on, and I'll provide specific, actionable code and guidance for {{theme_name}}. + +**Examples:** +- "How do I add a new block pattern?" +- "Create a contact form template" +- "Help me style the navigation menu" +- "Add animation to the hero section" +- "Optimize images for better performance" + +I'm here to help you create an amazing WordPress block theme! 🚀 \ No newline at end of file diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..3f829fd --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,183 @@ +# {{theme_name}} Block Theme Instructions + +You are an expert WordPress block theme developer working on {{theme_name}}, a modern WordPress block theme with Full Site Editing (FSE) support. + +## Theme Overview + +- **Theme Name**: {{theme_name}} +- **Theme Slug**: {{theme_slug}} +- **Version**: {{version}} +- **Description**: {{description}} +- **Architecture**: WordPress Block Theme with FSE support +- **Build System**: Webpack with @wordpress/scripts +- **Template System**: Mustache templates for configuration + +## Key Technologies + +- WordPress Block Editor (Gutenberg) +- Full Site Editing (FSE) +- theme.json for global styles +- Block patterns and template parts +- Modern JavaScript (ES6+) +- SCSS for styling +- Webpack for asset compilation +- PHPUnit and Jest for testing + +## File Structure + +``` +{{theme_slug}}/ +├── .github/ # GitHub workflows and Copilot config +├── assets/ # Static assets (images, fonts) +├── inc/ # PHP includes and functionality +├── parts/ # Template parts (header, footer, etc.) +├── patterns/ # Block patterns +├── src/ # Source files for build process +│ ├── css/ # SCSS source files +│ └── js/ # JavaScript source files +├── styles/ # Style variations (dark mode, etc.) +├── templates/ # Block templates (HTML) +├── tests/ # Test files +├── public/ # Built assets (auto-generated) +├── functions.php # Theme functions +├── style.css # Theme metadata +├── theme.json # Theme configuration +└── package.json # Build configuration +``` + +## Coding Standards + +### PHP +- Follow WordPress Coding Standards +- Use {{theme_slug}}_ prefix for all functions +- Escape all output with esc_html(), esc_attr(), etc. +- Sanitize all input +- Use WordPress hooks and filters appropriately + +### JavaScript +- Use modern ES6+ syntax +- Follow WordPress JavaScript standards +- Use wp.domReady() for DOM manipulation +- Utilize WordPress packages (@wordpress/*) + +### CSS/SCSS +- Use BEM methodology for custom classes +- Leverage CSS custom properties from theme.json +- Follow WordPress CSS standards +- Mobile-first responsive design + +### Block Templates +- Use semantic HTML structure +- Include proper block comments +- Follow WordPress template hierarchy +- Ensure accessibility compliance + +## Development Guidelines + +### When Working with Block Patterns +- Register patterns in inc/block-patterns.php +- Use mustache variables for customizable content +- Include proper categories and keywords +- Test patterns in the Site Editor + +### When Working with Templates +- Use HTML files in templates/ directory +- Include proper template parts +- Follow WordPress template hierarchy +- Test with different content types + +### When Working with Styles +- Primary styles in theme.json +- Additional styles in src/css/ +- Use CSS custom properties +- Ensure cross-browser compatibility + +### When Working with JavaScript +- Frontend scripts in src/js/theme.js +- Editor scripts in src/js/editor.js +- Use WordPress dependencies +- Ensure accessibility + +## Build Process + +- Development: `npm run start` +- Production: `npm run build:production` +- Linting: `npm run lint` +- Testing: `npm test` + +## Testing Requirements + +- Write PHPUnit tests for PHP functions +- Write Jest tests for JavaScript +- Include E2E tests for critical features +- Test accessibility compliance +- Verify across different browsers + +## Best Practices + +1. **Performance**: Optimize images, minify assets, lazy load content +2. **Accessibility**: Follow WCAG 2.1 AA guidelines +3. **Security**: Validate input, escape output, use nonces +4. **Compatibility**: Test with latest WordPress versions +5. **Documentation**: Comment complex code, update README + +## Mustache Variables + +Use these variables in templates and configuration files: + +### Theme Meta +- `{{theme_name}}` - Display name +- `{{theme_slug}}` - URL-safe identifier +- `{{description}}` - Theme description +- `{{version}}` - Current version +- `{{author}}` - Theme author +- `{{license}}` - License type + +### Design Tokens +- `{{primary_color}}` - Primary brand color +- `{{secondary_color}}` - Secondary color +- `{{background_color}}` - Background color +- `{{text_color}}` - Text color +- `{{font_family}}` - Body font +- `{{heading_font}}` - Heading font + +### Content +- `{{hero_title}}` - Hero section title +- `{{cta_text}}` - Call-to-action text +- `{{footer_text}}` - Footer copyright text + +## Common Tasks + +### Adding a New Block Pattern +1. Create pattern in inc/block-patterns.php +2. Register with appropriate category +3. Use mustache variables for content +4. Test in Site Editor + +### Adding a New Template +1. Create HTML file in templates/ +2. Follow block markup syntax +3. Include proper template parts +4. Test with different content + +### Adding Custom Styles +1. Add settings to theme.json +2. Create styles in src/css/ +3. Register block styles if needed +4. Test responsive behavior + +### Adding JavaScript Functionality +1. Add to src/js/theme.js or src/js/editor.js +2. Use WordPress APIs and hooks +3. Ensure accessibility +4. Write tests + +## Debugging + +- Use WordPress debug mode +- Check browser console for errors +- Use WordPress debugging tools +- Test with default content +- Verify plugin compatibility + +Remember to always test your changes thoroughly and follow WordPress best practices for theme development. \ No newline at end of file diff --git a/.github/copilot-prompt.md b/.github/copilot-prompt.md new file mode 100644 index 0000000..2605044 --- /dev/null +++ b/.github/copilot-prompt.md @@ -0,0 +1,34 @@ +# {{theme_name}} Build Assistant + +You are a WordPress block theme build assistant for {{theme_name}}. Help with theme development, build processes, and WordPress best practices. + +## Current Context +- **Project**: {{theme_name}} WordPress Block Theme +- **Technology**: WordPress FSE, Block Editor, theme.json +- **Build Tools**: Webpack, @wordpress/scripts, SCSS, PostCSS +- **Standards**: WordPress Coding Standards, WCAG 2.1 AA + +## Your Role +Provide expert guidance on: +- Block theme development +- Full Site Editing implementation +- Build process optimization +- Performance and accessibility +- WordPress best practices + +## Output Format +- Provide working code examples +- Include explanatory comments +- Follow WordPress coding standards +- Use mustache template variables when appropriate +- Include testing recommendations + +## Key Considerations +- Always prioritize accessibility +- Follow WordPress security best practices +- Ensure mobile responsiveness +- Optimize for performance +- Use semantic HTML +- Implement proper error handling + +Generate code that is production-ready, well-documented, and follows all relevant standards for {{theme_name}}. \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..890d2f2 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,192 @@ +name: CI/CD Pipeline + +on: + push: + branches: [ main, develop ] + pull_request: + branches: [ main, develop ] + release: + types: [ published ] + +env: + NODE_VERSION: 18 + PHP_VERSION: 8.1 + +jobs: + lint: + name: Code Quality & Linting + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ env.PHP_VERSION }} + tools: composer + coverage: none + + - name: Install Node dependencies + run: npm ci + + - name: Install Composer dependencies + run: composer install --prefer-dist --no-progress --no-suggest + + - name: Lint JavaScript + run: npm run lint:js + + - name: Lint CSS + run: npm run lint:css + + - name: Lint PHP + run: npm run lint:php + + test: + name: Run Tests + runs-on: ubuntu-latest + + strategy: + matrix: + php-version: [8.0, 8.1, 8.2] + wp-version: [latest, 6.4, 6.5] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + tools: composer + coverage: xdebug + + - name: Install Node dependencies + run: npm ci + + - name: Install Composer dependencies + run: composer install --prefer-dist --no-progress --no-suggest + + - name: Build assets + run: npm run build + + - name: Run JavaScript tests + run: npm run test:js + + - name: Run PHP tests + run: npm run test:php + + build: + name: Build & Package + runs-on: ubuntu-latest + needs: [lint, test] + if: github.event_name == 'release' + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Build production assets + run: npm run build:production + + - name: Create distribution package + run: | + mkdir -p dist + rsync -av --exclude-from=.distignore . dist/{{theme_slug}}/ + cd dist + zip -r {{theme_slug}}-${{ github.event.release.tag_name }}.zip {{theme_slug}}/ + + - name: Upload release asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ github.event.release.upload_url }} + asset_path: ./dist/{{theme_slug}}-${{ github.event.release.tag_name }}.zip + asset_name: {{theme_slug}}-${{ github.event.release.tag_name }}.zip + asset_content_type: application/zip + + e2e: + name: End-to-End Tests + runs-on: ubuntu-latest + needs: [lint] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Build assets + run: npm run build + + - name: Start WordPress environment + run: npm run env:start + + - name: Run E2E tests + run: npm run test:e2e + + - name: Stop WordPress environment + if: always() + run: npm run env:stop + + accessibility: + name: Accessibility Tests + runs-on: ubuntu-latest + needs: [lint] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Build assets + run: npm run build + + - name: Start WordPress environment + run: npm run env:start + + - name: Run accessibility tests + run: npm run test:accessibility + + - name: Stop WordPress environment + if: always() + run: npm run env:stop \ No newline at end of file diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..c968a66 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,35 @@ +name: Deploy to WordPress.org + +on: + release: + types: [published] + +jobs: + deploy: + name: Deploy to WordPress.org SVN + runs-on: ubuntu-latest + if: github.repository_owner == '{{github_org}}' + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 18 + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Build production assets + run: npm run build:production + + - name: WordPress Plugin Deploy + uses: 10up/action-wordpress-plugin-deploy@stable + env: + SVN_PASSWORD: ${{ secrets.SVN_PASSWORD }} + SVN_USERNAME: ${{ secrets.SVN_USERNAME }} + SLUG: {{theme_slug}} + VERSION: ${{ github.event.release.tag_name }} \ No newline at end of file diff --git a/.github/workflows/performance.yml b/.github/workflows/performance.yml new file mode 100644 index 0000000..c01ea2c --- /dev/null +++ b/.github/workflows/performance.yml @@ -0,0 +1,47 @@ +name: Performance Testing + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + schedule: + - cron: '0 2 * * 1' # Weekly on Monday at 2 AM + +jobs: + performance: + name: Performance Tests + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 18 + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Build assets + run: npm run build + + - name: Start WordPress environment + run: npm run env:start + + - name: Run performance tests + run: npm run test:performance + + - name: Upload performance results + uses: actions/upload-artifact@v3 + if: always() + with: + name: performance-results + path: artifacts/ + + - name: Stop WordPress environment + if: always() + run: npm run env:stop \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..17fda8f --- /dev/null +++ b/.gitignore @@ -0,0 +1,54 @@ +# Dependencies +node_modules/ +vendor/ + +# Build output +public/ +build/ +dist/ + +# Cache and temp files +.cache/ +.tmp/ +*.log +*.lock + +# Environment files +.env +.env.* +!.env.example + +# OS generated files +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db + +# IDE and editor files +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# Test and coverage output +coverage/ +artifacts/ +test-results/ +playwright-report/ + +# WordPress specific +wp-config.php +.htaccess + +# Backup files +*.bak +*.backup + +# Package manager files +package-lock.json +composer.lock +yarn.lock \ No newline at end of file diff --git a/.wp-env.json b/.wp-env.json new file mode 100644 index 0000000..f44fe0e --- /dev/null +++ b/.wp-env.json @@ -0,0 +1,35 @@ +{ + "core": "https://wordpress.org/latest.tar.gz", + "plugins": [ + "https://downloads.wordpress.org/plugin/gutenberg.latest-stable.zip", + "https://downloads.wordpress.org/plugin/create-block-theme.latest-stable.zip" + ], + "themes": [ + "." + ], + "config": { + "WP_DEBUG": true, + "WP_DEBUG_LOG": true, + "WP_DEBUG_DISPLAY": false, + "SCRIPT_DEBUG": true + }, + "env": { + "development": { + "config": { + "WP_DEBUG": true, + "WP_DEBUG_LOG": true, + "WP_DEBUG_DISPLAY": true, + "WP_ENVIRONMENT_TYPE": "development" + } + }, + "tests": { + "config": { + "WP_DEBUG": true, + "WP_DEBUG_LOG": true, + "WP_DEBUG_DISPLAY": false, + "WP_ENVIRONMENT_TYPE": "local" + }, + "port": 8890 + } + } +} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..25fc0c9 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,48 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added +- Initial theme scaffold with mustache templates +- Full Site Editing support +- Block patterns and template parts +- Style variations (dark mode) +- Modern build pipeline with Webpack +- Automated testing (PHP, JS, CSS, E2E) +- CI/CD workflows +- GitHub Copilot integration +- Security headers and best practices + +### Changed +- N/A + +### Deprecated +- N/A + +### Removed +- N/A + +### Fixed +- N/A + +### Security +- Added security headers +- Implemented proper escaping and sanitization + +## [{{version}}] - {{release_date}} + +### Added +- Initial release +- WordPress {{min_wp_version}}+ support +- Block theme architecture +- Responsive design +- Accessibility features +- Performance optimizations + +[Unreleased]: {{theme_repo_url}}/compare/v{{version}}...HEAD +[{{version}}]: {{theme_repo_url}}/releases/tag/v{{version}} \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..3cb740b --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,137 @@ +# Contributing to {{theme_name}} + +Thank you for your interest in contributing to {{theme_name}}! We welcome contributions from everyone. + +## Code of Conduct + +This project adheres to the [Contributor Covenant Code of Conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. + +## How to Contribute + +### Reporting Bugs + +Before creating bug reports, please check the [existing issues]({{theme_repo_url}}/issues) to see if the problem has already been reported. If you're unable to find an open issue addressing the problem, [open a new one]({{theme_repo_url}}/issues/new). + +When creating a bug report, please include: + +- A clear and descriptive title +- Steps to reproduce the issue +- Expected behavior +- Actual behavior +- Screenshots (if applicable) +- WordPress version +- Theme version +- Browser and version +- Any relevant error messages + +### Suggesting Enhancements + +Enhancement suggestions are welcome! Please [open an issue]({{theme_repo_url}}/issues/new) with: + +- A clear and descriptive title +- Detailed description of the enhancement +- Use cases for the enhancement +- Any relevant examples or mockups + +### Pull Requests + +1. Fork the repository +2. Create a feature branch (`git checkout -b feature/amazing-feature`) +3. Make your changes +4. Add or update tests as needed +5. Update documentation if necessary +6. Ensure all tests pass (`npm test`) +7. Ensure code follows project standards (`npm run lint`) +8. Commit your changes (`git commit -m 'Add amazing feature'`) +9. Push to the branch (`git push origin feature/amazing-feature`) +10. Open a Pull Request + +#### Pull Request Guidelines + +- Keep pull requests focused on a single feature or bug fix +- Include a clear description of what the PR does +- Reference any related issues +- Update the changelog for user-facing changes +- Ensure all checks pass +- Request review from maintainers + +## Development Setup + +See [DEVELOPMENT.md](docs/DEVELOPMENT.md) for detailed setup instructions. + +### Quick Start + +```bash +git clone {{theme_repo_url}} +cd {{theme_slug}} +npm install +composer install +npm run start +``` + +## Coding Standards + +- **PHP**: Follow WordPress coding standards +- **JavaScript**: Use WordPress JavaScript standards +- **CSS**: Follow WordPress CSS standards +- **Commits**: Use conventional commit format + +### Running Tests + +```bash +# All tests +npm test + +# JavaScript only +npm run test:js + +# PHP only +npm run test:php + +# End-to-end tests +npm run test:e2e + +# Linting +npm run lint +``` + +## Theme Structure + +``` +{{theme_slug}}/ +├── .github/ # GitHub workflows and templates +├── assets/ # Static assets +├── inc/ # PHP includes +├── parts/ # Template parts +├── patterns/ # Block patterns +├── src/ # Source files +├── styles/ # Style variations +├── templates/ # Block templates +├── tests/ # Test files +└── public/ # Built assets +``` + +## Documentation + +- All public functions should be documented +- Update README.md for significant changes +- Keep changelog updated +- Document any breaking changes + +## Security + +If you discover a security vulnerability, please send an email to {{security_email}} instead of using the issue tracker. + +## License + +By contributing to {{theme_name}}, you agree that your contributions will be licensed under the {{license}} license. + +## Recognition + +Contributors will be recognized in the project's contributors list and changelog. + +## Questions? + +Feel free to [open an issue]({{theme_repo_url}}/issues/new) or contact the maintainers if you have any questions about contributing. + +Thank you for contributing to {{theme_name}}! 🎉 \ No newline at end of file diff --git a/README.md b/README.md index e5de226..c8852ec 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,174 @@ -# block-theme-scaffold-template -Build-ready WordPress block theme scaffold with mustache templates, modern asset pipeline, block templates, global styles, and contribution standards. Includes automated testing, CI, and docs for rapid theme setup. See [repo-template.md](https://github.com/lightspeedwp/.github/blob/develop/docs/block-themes/repo-template.md) for details. +# {{theme_name}} + +{{description}} + +A modern WordPress block theme supporting Full Site Editing (FSE), built with mustache templates for rapid development and deployment. + +## Features + +- Full Site Editing (FSE) support with block templates +- Mustache template system for dynamic configuration +- Block patterns and template parts +- Style variations (e.g., dark mode) +- Modern asset pipeline with Webpack +- Automated testing (PHP, JS, CSS, E2E) +- CI/CD workflows +- GitHub Copilot integration +- Follows WordPress coding standards + +## Requirements + +- WordPress {{min_wp_version}} or higher +- PHP {{min_php_version}} or higher +- Node.js 18+ +- npm 9+ +- Composer (for PHP dependencies) + +## Installation + +### Development Installation + +1. Clone this repository: + ```bash + git clone {{theme_repo_url}} + cd {{theme_slug}} + ``` + +2. Install dependencies: + ```bash + npm install + composer install + ``` + +3. Build assets: + ```bash + npm run build + ``` + +### WordPress Installation + +1. Upload the theme files to `/wp-content/themes/{{theme_slug}}` or install via WordPress admin. +2. Activate the theme in the 'Appearance' screen. +3. Customize theme settings in the Site Editor. + +## Development + +This theme follows WordPress coding standards and best practices. + +### Setup + +```bash +git clone {{theme_repo_url}} +cd {{theme_slug}} +npm install +composer install +npm run start +``` + +### Available Scripts + +- `npm run start` - Start development mode with hot reloading +- `npm run build` - Build for production +- `npm run build:production` - Build optimized for production +- `npm run lint` - Run all linters +- `npm run lint:js` - Lint JavaScript +- `npm run lint:css` - Lint CSS +- `npm run lint:php` - Lint PHP +- `npm run test` - Run all tests +- `npm run test:js` - Run JavaScript tests +- `npm run test:php` - Run PHP tests +- `npm run test:e2e` - Run end-to-end tests + +### Theme Structure + +``` +{{theme_slug}}/ +├── .github/ # GitHub workflows and automation +├── assets/ # Source assets (images, fonts, etc.) +├── inc/ # PHP includes and functionality +├── parts/ # Template parts (header, footer, etc.) +├── patterns/ # Block patterns +├── src/ # Source files for build process +│ ├── css/ # SCSS/CSS source files +│ └── js/ # JavaScript source files +├── styles/ # Style variations +├── templates/ # Block templates +├── tests/ # Test files +├── public/ # Built assets (auto-generated) +├── functions.php # Theme functions +├── style.css # Main stylesheet with theme metadata +├── theme.json # Theme configuration +├── package.json # Node.js dependencies and scripts +├── composer.json # PHP dependencies +├── webpack.config.js # Webpack configuration +└── README.md # This file +``` + +## Customization + +### Using Mustache Templates + +This theme uses mustache templates for easy customization. Key variables include: + +- `{{theme_name}}` - Display name of the theme +- `{{theme_slug}}` - URL-safe theme identifier +- `{{description}}` - Theme description +- `{{author}}` - Theme author name +- `{{author_uri}}` - Author website URL +- `{{primary_color}}` - Primary brand color +- `{{secondary_color}}` - Secondary color +- `{{background_color}}` - Background color +- `{{text_color}}` - Text color +- `{{body_font}}` - Body font family + +### Customizing Colors and Typography + +Edit `theme.json` to customize: +- Color palette +- Typography settings +- Spacing scale +- Layout settings + +### Adding Custom Patterns + +1. Create new pattern files in the `patterns/` directory +2. Register patterns in `inc/block-patterns.php` +3. Add pattern categories as needed + +## Testing + +- **JavaScript**: Jest unit tests +- **PHP**: PHPUnit tests with WordPress testing framework +- **End-to-End**: Playwright tests +- **Accessibility**: Automated a11y testing +- **Performance**: Core Web Vitals monitoring + +## Contributing + +1. Fork the repository +2. Create a feature branch (`git checkout -b feature/amazing-feature`) +3. Make your changes +4. Run tests and linting (`npm run lint && npm test`) +5. Commit your changes (`git commit -m 'Add amazing feature'`) +6. Push to the branch (`git push origin feature/amazing-feature`) +7. Open a Pull Request + +Please read our [Contributing Guidelines](CONTRIBUTING.md) for more details. + +## Support + +- Documentation: [Full documentation]({{docs_url}}) +- Issues: [GitHub Issues]({{theme_repo_url}}/issues) +- Community: [WordPress.org Support](https://wordpress.org/support/theme/{{theme_slug}}) + +## License + +This theme is licensed under the {{license}} - see the [LICENSE](LICENSE) file for details. + +## Changelog + +See [CHANGELOG.md](CHANGELOG.md) for a complete list of changes. + +--- + +**{{theme_name}}** | v{{version}} | [{{license}}]({{license_uri}}) diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..3325c30 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,113 @@ +# Security Policy + +## Supported Versions + +We actively support the following versions of {{theme_name}}: + +| Version | Supported | +| ------- | ------------------ | +| {{version}}.x | ✅ | +| < {{version}}.0 | ❌ | + +## Reporting a Vulnerability + +If you discover a security vulnerability in {{theme_name}}, please report it responsibly: + +### Where to Report + +**DO NOT** open a public GitHub issue for security vulnerabilities. + +Instead, please send an email to: **{{security_email}}** + +### What to Include + +When reporting a security vulnerability, please include: + +1. **Description**: A clear description of the vulnerability +2. **Impact**: What an attacker could achieve +3. **Steps to Reproduce**: Detailed steps to reproduce the issue +4. **Affected Versions**: Which versions are affected +5. **Suggested Fix**: If you have ideas for a fix (optional) +6. **Your Contact Information**: So we can follow up with you + +### Our Response Process + +1. **Acknowledgment**: We'll acknowledge receipt within 24 hours +2. **Assessment**: We'll assess the vulnerability within 72 hours +3. **Timeline**: We'll provide an estimated timeline for a fix +4. **Updates**: We'll keep you informed of our progress +5. **Resolution**: We'll notify you when the vulnerability is fixed +6. **Disclosure**: We'll coordinate responsible disclosure + +### Timeline Expectations + +- **Critical vulnerabilities**: Fixed within 7 days +- **High severity**: Fixed within 14 days +- **Medium severity**: Fixed within 30 days +- **Low severity**: Fixed in next regular release + +## Security Best Practices + +When using {{theme_name}}, we recommend: + +### For Site Administrators + +- Keep WordPress core updated +- Keep the theme updated to the latest version +- Use strong passwords and two-factor authentication +- Regular security audits +- Proper file permissions +- Regular backups + +### For Developers + +- Follow WordPress security best practices +- Sanitize and validate all user inputs +- Use nonces for form submissions +- Escape output properly +- Follow principle of least privilege +- Regular security code reviews + +## Known Security Features + +{{theme_name}} includes these security features: + +- **Input Sanitization**: All user inputs are properly sanitized +- **Output Escaping**: All output is properly escaped +- **Nonce Verification**: Forms use WordPress nonces +- **Security Headers**: Basic security headers are included +- **No Direct File Access**: PHP files prevent direct access +- **WordPress Standards**: Follows WordPress security guidelines + +## Reporting Security Issues in Dependencies + +If you find security issues in our dependencies: + +1. **WordPress Core**: Report to [WordPress Security Team](https://make.wordpress.org/core/handbook/testing/reporting-security-vulnerabilities/) +2. **Third-party Libraries**: Report to the respective maintainers +3. **Our Theme**: Follow our reporting process above + +## Security Updates + +Security updates will be: + +- Released as soon as possible +- Documented in the changelog +- Announced on our security advisory page +- Backwards compatible when possible + +## Hall of Fame + +We recognize security researchers who responsibly disclose vulnerabilities: + + +*No vulnerabilities reported yet* + +## Contact + +For security-related questions or concerns: + +- **Email**: {{security_email}} +- **GPG Key**: [Available upon request] + +Thank you for helping keep {{theme_name}} secure! 🔒 \ No newline at end of file diff --git a/SUPPORT.md b/SUPPORT.md new file mode 100644 index 0000000..2d6e43d --- /dev/null +++ b/SUPPORT.md @@ -0,0 +1,164 @@ +# Support + +Thank you for using {{theme_name}}! We're here to help you get the most out of your theme. + +## Getting Help + +### Documentation + +First, check our comprehensive documentation: + +- **[Theme Documentation]({{docs_url}})** - Complete guide to using and customizing the theme +- **[Installation Guide]({{docs_url}}/installation)** - Step-by-step installation instructions +- **[Customization Guide]({{docs_url}}/customization)** - How to customize the theme +- **[Block Patterns]({{docs_url}}/patterns)** - Available block patterns and how to use them +- **[FAQ]({{docs_url}}/faq)** - Frequently asked questions + +### Community Support + +- **[WordPress.org Support Forum](https://wordpress.org/support/theme/{{theme_slug}})** - Free community support +- **[GitHub Discussions]({{theme_repo_url}}/discussions)** - Community discussions and Q&A +- **[Discord Community]({{discord_url}})** - Real-time chat with other users + +### Reporting Issues + +- **[GitHub Issues]({{theme_repo_url}}/issues)** - Bug reports and feature requests +- **[Bug Report Template]({{theme_repo_url}}/issues/new?template=bug_report.md)** - Structured bug reporting + +## What We Support + +### ✅ Supported + +- Theme installation and setup +- Basic customization questions +- Bug reports for confirmed issues +- Feature requests and suggestions +- Compatibility issues with WordPress core +- Documentation clarifications + +### ❌ Not Supported + +- Custom development work +- Third-party plugin compatibility (unless theme-related) +- Website design and content creation +- WordPress hosting issues +- General WordPress support +- Custom CSS/PHP modifications + +## Before Asking for Help + +Please try these steps first: + +1. **Check the documentation** - Most questions are answered there +2. **Search existing issues** - Someone might have asked the same question +3. **Update everything** - Ensure WordPress, theme, and plugins are current +4. **Test with default theme** - Confirm the issue is theme-related +5. **Disable plugins** - Rule out plugin conflicts + +## How to Ask for Help + +When asking for help, please provide: + +### Basic Information + +- **WordPress version**: Found in Dashboard → Updates +- **Theme version**: Found in Appearance → Themes +- **PHP version**: Found in Site Health +- **Browser and version**: What browser you're using + +### For Bug Reports + +- **Clear description**: What's happening vs. what should happen +- **Steps to reproduce**: How to recreate the issue +- **Screenshots**: Visual evidence (if applicable) +- **Console errors**: Browser console or WordPress debug log +- **URL**: Link to the page with the issue (if public) + +### For Customization Questions + +- **What you're trying to achieve**: Your end goal +- **What you've tried**: Steps you've already taken +- **Specific pages/sections**: Where you want the changes +- **Visual examples**: Screenshots or examples of desired result + +## Response Times + +### Community Support (Free) + +- **WordPress.org Forum**: 1-7 days (volunteer-based) +- **GitHub Discussions**: 1-5 days (community + maintainers) +- **GitHub Issues**: 1-3 days (maintainers) + +### Priority Support (Premium) + +If you need faster support, consider: + +- **[Premium Support]({{premium_support_url}})** - Priority email support +- **[Custom Development]({{custom_dev_url}})** - Paid customization services + +## Self-Help Resources + +### Troubleshooting + +1. **Theme Conflicts** + ``` + - Switch to a default WordPress theme + - Check if the issue persists + - If not, it's likely a theme issue + ``` + +2. **Plugin Conflicts** + ``` + - Deactivate all plugins + - Check if the issue is resolved + - Reactivate plugins one by one + ``` + +3. **Clear Caches** + ``` + - Clear any caching plugins + - Clear browser cache + - Clear CDN cache (if applicable) + ``` + +### Common Solutions + +- **Site loading slowly**: Check image optimization, caching, hosting +- **Layout broken**: Check for CSS conflicts, browser compatibility +- **Features not working**: Check JavaScript errors in console +- **Images not showing**: Check file permissions, media settings + +## Contributing + +Help improve our support resources: + +- **[Improve Documentation]({{docs_repo_url}})** - Submit documentation improvements +- **[Answer Questions]({{theme_repo_url}}/discussions)** - Help other users +- **[Report Issues]({{theme_repo_url}}/issues)** - Report bugs and suggest features + +## Contact Information + +### General Support +- **Email**: {{support_email}} +- **Response Time**: 1-3 business days + +### Security Issues +- **Email**: {{security_email}} +- **Response Time**: 24 hours + +### Business Inquiries +- **Email**: {{business_email}} +- **Response Time**: 1-2 business days + +## Useful Links + +- **[WordPress Codex](https://codex.wordpress.org/)** - WordPress documentation +- **[WordPress Support](https://wordpress.org/support/)** - General WordPress help +- **[WordPress.org Forums](https://wordpress.org/support/forums/)** - Community support +- **[WordPress News](https://wordpress.org/news/)** - WordPress updates and news + +--- + +**Remember**: The more information you provide, the better we can help you! + +Thank you for using {{theme_name}}! 💙 \ No newline at end of file diff --git a/bin/build.js b/bin/build.js new file mode 100755 index 0000000..d1c9978 --- /dev/null +++ b/bin/build.js @@ -0,0 +1,202 @@ +#!/usr/bin/env node + +/** + * Build and deployment utility script + */ + +const fs = require('fs'); +const path = require('path'); +const { execSync } = require('child_process'); + +const THEME_DIR = path.resolve(__dirname, '..'); +const PACKAGE_JSON = path.join(THEME_DIR, 'package.json'); + +/** + * Get package.json data + */ +function getPackageData() { + try { + return JSON.parse(fs.readFileSync(PACKAGE_JSON, 'utf8')); + } catch (error) { + console.error('Error reading package.json:', error.message); + process.exit(1); + } +} + +/** + * Run command and handle errors + */ +function runCommand(command, options = {}) { + try { + console.log(`Running: ${command}`); + return execSync(command, { + stdio: 'inherit', + cwd: THEME_DIR, + ...options + }); + } catch (error) { + console.error(`Command failed: ${command}`); + process.exit(1); + } +} + +/** + * Build theme for production + */ +function buildProduction() { + console.log('Building theme for production...'); + + // Clean previous build + runCommand('rm -rf public/*'); + + // Build assets + runCommand('npm run build:production'); + + // Optimize images (if imagemin is available) + try { + runCommand('npx imagemin assets/images/* --out-dir=public/images'); + } catch (error) { + console.log('Image optimization skipped (imagemin not available)'); + } + + console.log('Production build complete!'); +} + +/** + * Create distribution package + */ +function createDistribution() { + const pkg = getPackageData(); + const version = pkg.version; + const themeName = pkg.name; + + console.log(`Creating distribution package for ${themeName} v${version}...`); + + // Build for production first + buildProduction(); + + // Create dist directory + const distDir = path.join(THEME_DIR, 'dist'); + const themeDistDir = path.join(distDir, themeName); + + runCommand(`rm -rf ${distDir}`); + runCommand(`mkdir -p ${themeDistDir}`); + + // Copy theme files (excluding development files) + runCommand(`rsync -av --exclude-from=.distignore . ${themeDistDir}/`); + + // Create ZIP file + const zipName = `${themeName}-${version}.zip`; + runCommand(`cd ${distDir} && zip -r ${zipName} ${themeName}/`); + + console.log(`Distribution package created: dist/${zipName}`); +} + +/** + * Run theme checks + */ +function runChecks() { + console.log('Running theme checks...'); + + // Lint code + runCommand('npm run lint'); + + // Run tests + runCommand('npm test'); + + // Check WordPress standards (if WP CLI is available) + try { + runCommand('wp theme status'); + } catch (error) { + console.log('WordPress CLI checks skipped (WP CLI not available)'); + } + + console.log('All checks passed!'); +} + +/** + * Initialize development environment + */ +function initDev() { + console.log('Initializing development environment...'); + + // Install dependencies + runCommand('npm install'); + runCommand('composer install'); + + // Setup git hooks + runCommand('npm run prepare'); + + // Start WordPress environment + try { + runCommand('npm run env:start'); + console.log('WordPress environment started at http://localhost:8889'); + } catch (error) { + console.log('WordPress environment setup skipped'); + } + + console.log('Development environment ready!'); +} + +/** + * Update theme version + */ +function updateVersion(newVersion) { + if (!newVersion) { + console.error('Please provide a version number'); + process.exit(1); + } + + console.log(`Updating theme version to ${newVersion}...`); + + // Update package.json + const pkg = getPackageData(); + pkg.version = newVersion; + fs.writeFileSync(PACKAGE_JSON, JSON.stringify(pkg, null, 2)); + + // Update style.css + const styleCss = path.join(THEME_DIR, 'style.css'); + let styleContent = fs.readFileSync(styleCss, 'utf8'); + styleContent = styleContent.replace(/Version: .*/, `Version: ${newVersion}`); + fs.writeFileSync(styleCss, styleContent); + + console.log(`Version updated to ${newVersion}`); +} + +// Main script logic +const command = process.argv[2]; +const arg = process.argv[3]; + +switch (command) { + case 'build': + buildProduction(); + break; + case 'dist': + createDistribution(); + break; + case 'check': + runChecks(); + break; + case 'init': + initDev(); + break; + case 'version': + updateVersion(arg); + break; + default: + console.log(` +Usage: node build.js [args] + +Commands: + build Build theme for production + dist Create distribution package + check Run linting and tests + init Initialize development environment + version Update theme version + +Examples: + node build.js build + node build.js dist + node build.js version 1.2.0 + `); +} \ No newline at end of file diff --git a/bin/install-wp-tests.sh b/bin/install-wp-tests.sh new file mode 100755 index 0000000..920bc99 --- /dev/null +++ b/bin/install-wp-tests.sh @@ -0,0 +1,178 @@ +#!/usr/bin/env bash + +if [ $# -lt 3 ]; then + echo "usage: $0 [db-host] [wp-version] [skip-database-creation]" + exit 1 +fi + +DB_NAME=$1 +DB_USER=$2 +DB_PASS=$3 +DB_HOST=${4-localhost} +WP_VERSION=${5-latest} +SKIP_DB_CREATE=${6-false} + +TMPDIR=${TMPDIR-/tmp} +TMPDIR=$(echo $TMPDIR | sed -e "s/\/$//") +WP_TESTS_DIR=${WP_TESTS_DIR-$TMPDIR/wordpress-tests-lib} +WP_CORE_DIR=${WP_CORE_DIR-$TMPDIR/wordpress/} + +download() { + if [ `which curl` ]; then + curl -s "$1" > "$2"; + elif [ `which wget` ]; then + wget -nv -O "$2" "$1" + fi +} + +if [[ $WP_VERSION =~ ^[0-9]+\.[0-9]+\-(beta|RC)[0-9]+$ ]]; then + WP_BRANCH=${WP_VERSION%%-*} + WP_TESTS_TAG="branches/$WP_BRANCH" + +elif [[ $WP_VERSION =~ ^[0-9]+\.[0-9]+$ ]]; then + WP_TESTS_TAG="branches/$WP_VERSION" +elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0-9]+ ]]; then + if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then + # version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x + WP_TESTS_TAG="tags/${WP_VERSION%??}" + else + WP_TESTS_TAG="tags/$WP_VERSION" + fi +elif [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then + WP_TESTS_TAG="trunk" +else + # http serves a single offer, whereas https serves multiple. we only want one + download http://api.wordpress.org/core/version-check/1.7/ /tmp/wp-latest.json + grep '[0-9]+\.[0-9]+(\.[0-9]+)?' /tmp/wp-latest.json + LATEST_VERSION=$(grep -o '"version":"[^"]*' /tmp/wp-latest.json | sed 's/"version":"//') + if [[ -z "$LATEST_VERSION" ]]; then + echo "Latest WordPress version could not be found" + exit 1 + fi + WP_TESTS_TAG="tags/$LATEST_VERSION" +fi +set -ex + +install_wp() { + + if [ -d $WP_CORE_DIR ]; then + return; + fi + + mkdir -p $WP_CORE_DIR + + if [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then + mkdir -p $TMPDIR/wordpress-nightly + download https://wordpress.org/nightly-builds/wordpress-latest.zip $TMPDIR/wordpress-nightly/wordpress-nightly.zip + unzip -q $TMPDIR/wordpress-nightly/wordpress-nightly.zip -d $TMPDIR/wordpress-nightly/ + mv $TMPDIR/wordpress-nightly/wordpress/* $WP_CORE_DIR + else + if [ $WP_VERSION == 'latest' ]; then + local ARCHIVE_NAME='latest' + elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+ ]]; then + # https serves multiple offers, whereas http serves single. + download https://api.wordpress.org/core/version-check/1.7/ $TMPDIR/wp-latest.json + if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then + # version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x + LATEST_VERSION=${WP_VERSION%??} + else + # otherwise, scan the releases and get the most up to date minor version of the major release + local VERSION_ESCAPED=`echo $WP_VERSION | sed 's/\./\\\\./g'` + LATEST_VERSION=$(grep -o '"version":"'$VERSION_ESCAPED'[^"]*' $TMPDIR/wp-latest.json | sed 's/"version":"//' | head -1) + fi + if [[ -z "$LATEST_VERSION" ]]; then + local ARCHIVE_NAME="wordpress-$WP_VERSION" + else + local ARCHIVE_NAME="wordpress-$LATEST_VERSION" + fi + else + local ARCHIVE_NAME="wordpress-$WP_VERSION" + fi + download https://wordpress.org/${ARCHIVE_NAME}.tar.gz $TMPDIR/wordpress.tar.gz + tar --strip-components=1 -zxmf $TMPDIR/wordpress.tar.gz -C $WP_CORE_DIR + fi + + download https://raw.github.com/markoheijnen/wp-mysqli/master/db.php $WP_CORE_DIR/wp-content/db.php +} + +install_test_suite() { + # portable in-place argument for both GNU sed and Mac OSX sed + if [[ $(uname -s) == 'Darwin' ]]; then + local ioption='-i.bak' + else + local ioption='-i' + fi + + # set up testing suite if it doesn't yet exist + if [ ! -d $WP_TESTS_DIR ]; then + # set up testing suite + mkdir -p $WP_TESTS_DIR + svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/includes/ $WP_TESTS_DIR/includes + svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/data/ $WP_TESTS_DIR/data + fi + + if [ ! -f wp-tests-config.php ]; then + download https://develop.svn.wordpress.org/${WP_TESTS_TAG}/wp-tests-config-sample.php "$WP_TESTS_DIR"/wp-tests-config.php + # remove all forward slashes in the end + WP_CORE_DIR=$(echo $WP_CORE_DIR | sed "s:/\+$::") + sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR/':" "$WP_TESTS_DIR"/wp-tests-config.php + sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" "$WP_TESTS_DIR"/wp-tests-config.php + sed $ioption "s/yourusernamehere/$DB_USER/" "$WP_TESTS_DIR"/wp-tests-config.php + sed $ioption "s/yourpasswordhere/$DB_PASS/" "$WP_TESTS_DIR"/wp-tests-config.php + sed $ioption "s|localhost|${DB_HOST}|" "$WP_TESTS_DIR"/wp-tests-config.php + fi + +} + +recreate_db() { + shopt -s nocasematch + if [[ $1 =~ ^(y|yes)$ ]] + then + mysqladmin drop $DB_NAME -f --user="$DB_USER" --password="$DB_PASS"$EXTRA + create_db + echo "Recreated the database ($DB_NAME)." + else + echo "Leaving the existing database ($DB_NAME) in place." + fi + shopt -u nocasematch +} + +create_db() { + mysqladmin create $DB_NAME --user="$DB_USER" --password="$DB_PASS"$EXTRA +} + +install_db() { + + if [ ${SKIP_DB_CREATE} = "true" ]; then + return 0 + fi + + # parse DB_HOST for port or socket references + local PARTS=(${DB_HOST//\:/ }) + local DB_HOSTNAME=${PARTS[0]}; + local DB_SOCK_OR_PORT=${PARTS[1]}; + local EXTRA="" + + if ! [ -z $DB_HOSTNAME ] ; then + if [ $(echo $DB_SOCK_OR_PORT | grep -e '^[0-9]\{1,\}$') ]; then + EXTRA=" --host=$DB_HOSTNAME --port=$DB_SOCK_OR_PORT --protocol=tcp" + elif ! [ -z $DB_SOCK_OR_PORT ] ; then + EXTRA=" --socket=$DB_SOCK_OR_PORT" + elif ! [ -z $DB_HOSTNAME ] ; then + EXTRA=" --host=$DB_HOSTNAME --protocol=tcp" + fi + fi + + # create database + if [ $(mysql --user="$DB_USER" --password="$DB_PASS"$EXTRA --execute="SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = '$DB_NAME'" 2>/dev/null | tail -1) != 0 ]; then + echo "Reinstalling will delete the existing test database ($DB_NAME)" + read -p 'Are you sure you want to proceed? [y/N]: ' DELETE_EXISTING_DB + recreate_db $DELETE_EXISTING_DB + else + create_db + fi +} + +install_wp +install_test_suite +install_db \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..c9a36a9 --- /dev/null +++ b/composer.json @@ -0,0 +1,38 @@ +{ + "name": "{{theme_slug}}", + "description": "{{description}}", + "type": "wordpress-theme", + "license": "{{license}}", + "authors": [ + { + "name": "{{author}}", + "homepage": "{{author_uri}}" + } + ], + "support": { + "issues": "{{theme_repo_url}}/issues", + "source": "{{theme_repo_url}}" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "squizlabs/php_codesniffer": "^3.7", + "wp-coding-standards/wpcs": "^3.0", + "phpcompatibility/phpcompatibility-wp": "^2.1", + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", + "phpunit/phpunit": "^9.0", + "yoast/phpunit-polyfills": "^1.0" + }, + "scripts": { + "lint": "phpcs", + "lint:fix": "phpcbf", + "test": "phpunit", + "test:coverage": "phpunit --coverage-html coverage" + }, + "config": { + "allow-plugins": { + "dealerdirect/phpcodesniffer-composer-installer": true + } + } +} \ No newline at end of file diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..6714e2f --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,39 @@ +module.exports = { + extends: [ '@wordpress/eslint-plugin/recommended' ], + env: { + browser: true, + es6: true, + node: true, + jquery: true, + }, + globals: { + wp: 'readonly', + wpApiSettings: 'readonly', + ajaxurl: 'readonly', + }, + rules: { + 'no-console': 'warn', + 'no-debugger': 'error', + }, + overrides: [ + { + files: [ '*.test.js', '*.spec.js' ], + env: { + jest: true, + }, + }, + { + files: [ 'tests/e2e/**/*.js' ], + extends: [ '@wordpress/eslint-plugin/recommended-with-formatting' ], + env: { + node: true, + }, + globals: { + page: 'readonly', + browser: 'readonly', + context: 'readonly', + jestPuppeteer: 'readonly', + }, + }, + ], +}; \ No newline at end of file diff --git a/functions.php b/functions.php new file mode 100644 index 0000000..a2f1756 --- /dev/null +++ b/functions.php @@ -0,0 +1,149 @@ + array( 'label' => __( '{{theme_name}} Hero', '{{theme_slug}}' ) ), + '{{theme_slug}}-about' => array( 'label' => __( '{{theme_name}} About', '{{theme_slug}}' ) ), + '{{theme_slug}}-contact' => array( 'label' => __( '{{theme_name}} Contact', '{{theme_slug}}' ) ), + '{{theme_slug}}-cta' => array( 'label' => __( '{{theme_name}} Call to Action', '{{theme_slug}}' ) ), + '{{theme_slug}}-gallery' => array( 'label' => __( '{{theme_name}} Gallery', '{{theme_slug}}' ) ), + '{{theme_slug}}-team' => array( 'label' => __( '{{theme_name}} Team', '{{theme_slug}}' ) ), + ); + + foreach ( $categories as $slug => $args ) { + register_block_pattern_category( $slug, $args ); + } +} +add_action( 'init', '{{theme_slug}}_register_pattern_categories' ); + +/** + * Load theme includes. + */ +$theme_includes = array( + 'inc/block-patterns.php', + 'inc/block-styles.php', + 'inc/customizer.php', + 'inc/template-functions.php', +); + +foreach ( $theme_includes as $file ) { + $filepath = get_theme_file_path( $file ); + if ( file_exists( $filepath ) ) { + require_once $filepath; + } +} + +/** + * Add custom image sizes. + */ +function {{theme_slug}}_add_image_sizes() { + add_image_size( '{{theme_slug}}-featured', {{featured_image_width}}, {{featured_image_height}}, true ); + add_image_size( '{{theme_slug}}-thumbnail', {{thumbnail_width}}, {{thumbnail_height}}, true ); + add_image_size( '{{theme_slug}}-gallery', {{gallery_image_width}}, {{gallery_image_height}}, true ); +} +add_action( 'after_setup_theme', '{{theme_slug}}_add_image_sizes' ); + +/** + * Modify excerpt length. + */ +function {{theme_slug}}_excerpt_length( $length ) { + return {{excerpt_length}}; +} +add_filter( 'excerpt_length', '{{theme_slug}}_excerpt_length' ); + +/** + * Modify excerpt more. + */ +function {{theme_slug}}_excerpt_more( $more ) { + return '{{excerpt_more}}'; +} +add_filter( 'excerpt_more', '{{theme_slug}}_excerpt_more' ); \ No newline at end of file diff --git a/inc/block-patterns.php b/inc/block-patterns.php new file mode 100644 index 0000000..3dd696e --- /dev/null +++ b/inc/block-patterns.php @@ -0,0 +1,161 @@ + __( 'Hero Section', '{{theme_slug}}' ), + 'description' => __( 'A large hero section with heading, text, and button.', '{{theme_slug}}' ), + 'categories' => array( '{{theme_slug}}-hero' ), + 'keywords' => array( 'hero', 'banner', 'header' ), + 'content' => ' +
+ +

{{hero_title}}

+ + + +

{{hero_description}}

+ + + + + +
+', + ) + ); +} +add_action( 'init', '{{theme_slug}}_register_hero_pattern' ); + +/** + * Register call to action pattern. + */ +function {{theme_slug}}_register_cta_pattern() { + register_block_pattern( + '{{theme_slug}}/call-to-action', + array( + 'title' => __( 'Call to Action', '{{theme_slug}}' ), + 'description' => __( 'A call to action section with heading and button.', '{{theme_slug}}' ), + 'categories' => array( '{{theme_slug}}-cta' ), + 'keywords' => array( 'cta', 'call to action', 'button' ), + 'content' => ' +
+ +

{{cta_title}}

+ + + +

{{cta_description}}

+ + + + + +
+', + ) + ); +} +add_action( 'init', '{{theme_slug}}_register_cta_pattern' ); + +/** + * Register team section pattern. + */ +function {{theme_slug}}_register_team_pattern() { + register_block_pattern( + '{{theme_slug}}/team-section', + array( + 'title' => __( 'Team Section', '{{theme_slug}}' ), + 'description' => __( 'A team section with multiple team member cards.', '{{theme_slug}}' ), + 'categories' => array( '{{theme_slug}}-team' ), + 'keywords' => array( 'team', 'members', 'staff' ), + 'content' => ' +
+ +

{{team_title}}

+ + + +

{{team_description}}

+ + + +
+ +
+ +
+ + + +

{{team_member_1_name}}

+ + + +

{{team_member_1_role}}

+ +
+ + + +
+ +
+ + + +

{{team_member_2_name}}

+ + + +

{{team_member_2_role}}

+ +
+ + + +
+ +
+ + + +

{{team_member_3_name}}

+ + + +

{{team_member_3_role}}

+ +
+ +
+ +
+', + ) + ); +} +add_action( 'init', '{{theme_slug}}_register_team_pattern' ); \ No newline at end of file diff --git a/inc/block-styles.php b/inc/block-styles.php new file mode 100644 index 0000000..a71cfcb --- /dev/null +++ b/inc/block-styles.php @@ -0,0 +1,79 @@ + 'outline', + 'label' => __( 'Outline', '{{theme_slug}}' ), + ) + ); + + register_block_style( + 'core/button', + array( + 'name' => 'ghost', + 'label' => __( 'Ghost', '{{theme_slug}}' ), + ) + ); + + // Quote styles. + register_block_style( + 'core/quote', + array( + 'name' => 'modern', + 'label' => __( 'Modern', '{{theme_slug}}' ), + ) + ); + + // Group styles. + register_block_style( + 'core/group', + array( + 'name' => 'shadow', + 'label' => __( 'Shadow', '{{theme_slug}}' ), + ) + ); + + register_block_style( + 'core/group', + array( + 'name' => 'border', + 'label' => __( 'Border', '{{theme_slug}}' ), + ) + ); + + // Image styles. + register_block_style( + 'core/image', + array( + 'name' => 'rounded', + 'label' => __( 'Rounded', '{{theme_slug}}' ), + ) + ); + + // Post title styles. + register_block_style( + 'core/post-title', + array( + 'name' => 'gradient', + 'label' => __( 'Gradient', '{{theme_slug}}' ), + ) + ); +} +add_action( 'init', '{{theme_slug}}_register_block_styles' ); \ No newline at end of file diff --git a/inc/customizer.php b/inc/customizer.php new file mode 100644 index 0000000..60a37ee --- /dev/null +++ b/inc/customizer.php @@ -0,0 +1,122 @@ +add_section( + '{{theme_slug}}_options', + array( + 'title' => __( '{{theme_name}} Options', '{{theme_slug}}' ), + 'priority' => 130, + ) + ); + + // Footer text setting. + $wp_customize->add_setting( + '{{theme_slug}}_footer_text', + array( + 'default' => '{{default_footer_text}}', + 'sanitize_callback' => 'sanitize_text_field', + ) + ); + + $wp_customize->add_control( + '{{theme_slug}}_footer_text', + array( + 'label' => __( 'Footer Text', '{{theme_slug}}' ), + 'section' => '{{theme_slug}}_options', + 'type' => 'text', + ) + ); + + // Show/hide social links setting. + $wp_customize->add_setting( + '{{theme_slug}}_show_social_links', + array( + 'default' => true, + 'sanitize_callback' => 'wp_validate_boolean', + ) + ); + + $wp_customize->add_control( + '{{theme_slug}}_show_social_links', + array( + 'label' => __( 'Show Social Links', '{{theme_slug}}' ), + 'section' => '{{theme_slug}}_options', + 'type' => 'checkbox', + ) + ); + + // Header layout setting. + $wp_customize->add_setting( + '{{theme_slug}}_header_layout', + array( + 'default' => 'default', + 'sanitize_callback' => 'sanitize_key', + ) + ); + + $wp_customize->add_control( + '{{theme_slug}}_header_layout', + array( + 'label' => __( 'Header Layout', '{{theme_slug}}' ), + 'section' => '{{theme_slug}}_options', + 'type' => 'select', + 'choices' => array( + 'default' => __( 'Default', '{{theme_slug}}' ), + 'centered' => __( 'Centered', '{{theme_slug}}' ), + 'minimal' => __( 'Minimal', '{{theme_slug}}' ), + ), + ) + ); +} +add_action( 'customize_register', '{{theme_slug}}_customize_register' ); + +/** + * Output customizer CSS. + */ +function {{theme_slug}}_customizer_css() { + $footer_text = get_theme_mod( '{{theme_slug}}_footer_text', '{{default_footer_text}}' ); + $header_layout = get_theme_mod( '{{theme_slug}}_header_layout', 'default' ); + + $css = ''; + + // Header layout styles. + if ( 'centered' === $header_layout ) { + $css .= ' + .wp-block-template-part[slug="header"] .wp-block-group { + text-align: center; + } + .wp-block-template-part[slug="header"] .wp-block-navigation { + justify-content: center; + } + '; + } elseif ( 'minimal' === $header_layout ) { + $css .= ' + .wp-block-template-part[slug="header"] { + border-bottom: 1px solid var(--wp--preset--color--neutral); + } + .wp-block-template-part[slug="header"] .wp-block-site-logo { + display: none; + } + '; + } + + if ( ! empty( $css ) ) { + echo ''; + } +} +add_action( 'wp_head', '{{theme_slug}}_customizer_css' ); \ No newline at end of file diff --git a/inc/template-functions.php b/inc/template-functions.php new file mode 100644 index 0000000..b21288e --- /dev/null +++ b/inc/template-functions.php @@ -0,0 +1,173 @@ +'; +} +add_action( 'wp_head', '{{theme_slug}}_viewport_meta', 1 ); + +/** + * Add theme support for custom logo. + */ +function {{theme_slug}}_custom_logo_setup() { + add_theme_support( + 'custom-logo', + array( + 'height' => {{logo_height}}, + 'width' => {{logo_width}}, + 'flex-width' => true, + 'flex-height' => true, + ) + ); +} +add_action( 'after_setup_theme', '{{theme_slug}}_custom_logo_setup' ); + +/** + * Add editor color palette support. + */ +function {{theme_slug}}_editor_color_palette() { + add_theme_support( + 'editor-color-palette', + array( + array( + 'name' => __( 'Primary', '{{theme_slug}}' ), + 'slug' => 'primary', + 'color' => '{{primary_color}}', + ), + array( + 'name' => __( 'Secondary', '{{theme_slug}}' ), + 'slug' => 'secondary', + 'color' => '{{secondary_color}}', + ), + array( + 'name' => __( 'Background', '{{theme_slug}}' ), + 'slug' => 'background', + 'color' => '{{background_color}}', + ), + array( + 'name' => __( 'Foreground', '{{theme_slug}}' ), + 'slug' => 'foreground', + 'color' => '{{text_color}}', + ), + ) + ); +} +add_action( 'after_setup_theme', '{{theme_slug}}_editor_color_palette' ); + +/** + * Custom excerpt length for different post types. + * + * @param int $length Current excerpt length. + * @return int + */ +function {{theme_slug}}_custom_excerpt_length( $length ) { + if ( is_admin() ) { + return $length; + } + + if ( is_home() || is_archive() ) { + return {{archive_excerpt_length}}; + } + + return $length; +} +add_filter( 'excerpt_length', '{{theme_slug}}_custom_excerpt_length' ); + +/** + * Remove unnecessary generator meta tags. + */ +function {{theme_slug}}_remove_version() { + return ''; +} +add_filter( 'the_generator', '{{theme_slug}}_remove_version' ); + +/** + * Add security headers. + */ +function {{theme_slug}}_security_headers() { + if ( ! is_admin() ) { + header( 'X-Content-Type-Options: nosniff' ); + header( 'X-Frame-Options: SAMEORIGIN' ); + header( 'X-XSS-Protection: 1; mode=block' ); + } +} +add_action( 'send_headers', '{{theme_slug}}_security_headers' ); \ No newline at end of file diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..b67d3ef --- /dev/null +++ b/jest.config.js @@ -0,0 +1,21 @@ +module.exports = { + preset: '@wordpress/jest-preset-default', + testEnvironment: 'jsdom', + setupFilesAfterEnv: [ + '@wordpress/jest-console', + '@wordpress/jest-puppeteer-axe', + 'expect-puppeteer', + ], + testPathIgnorePatterns: [ + '/node_modules/', + '/vendor/', + '/public/', + ], + collectCoverageFrom: [ + 'src/**/*.{js,jsx}', + '!src/**/*.test.{js,jsx}', + '!src/**/*.stories.{js,jsx}', + ], + coverageDirectory: 'coverage', + coverageReporters: ['text', 'lcov', 'html'], +}; \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..55cbbcf --- /dev/null +++ b/package.json @@ -0,0 +1,81 @@ +{ + "name": "{{theme_slug}}", + "version": "{{version}}", + "description": "{{description}}", + "author": "{{author}}", + "license": "{{license}}", + "homepage": "{{theme_uri}}", + "repository": { + "type": "git", + "url": "{{theme_repo_url}}" + }, + "bugs": { + "url": "{{theme_repo_url}}/issues" + }, + "keywords": [ + "wordpress", + "theme", + "block-theme", + "full-site-editing", + "fse" + ], + "engines": { + "node": ">=18.0.0", + "npm": ">=9.0.0" + }, + "browserslist": [ + "defaults" + ], + "scripts": { + "start": "wp-scripts start", + "build": "wp-scripts build", + "build:production": "NODE_ENV=production wp-scripts build", + "lint": "npm run lint:js && npm run lint:css && npm run lint:php", + "lint:js": "wp-scripts lint-js", + "lint:js:fix": "wp-scripts lint-js --fix", + "lint:css": "wp-scripts lint-style", + "lint:css:fix": "wp-scripts lint-style --fix", + "lint:php": "composer run lint", + "lint:php:fix": "composer run lint:fix", + "format": "wp-scripts format", + "test": "npm run test:js && npm run test:php", + "test:js": "wp-scripts test-unit-js", + "test:js:watch": "wp-scripts test-unit-js --watch", + "test:php": "composer run test", + "test:e2e": "wp-scripts test-e2e", + "test:e2e:debug": "wp-scripts test-e2e --config jest-e2e.config.js --debug", + "test:performance": "wp-scripts test-performance", + "packages-update": "wp-scripts packages-update", + "prepare": "husky install", + "env": "wp-env", + "env:start": "wp-env start", + "env:stop": "wp-env stop", + "env:destroy": "wp-env destroy" + }, + "devDependencies": { + "@wordpress/scripts": "^27.0.0", + "@wordpress/env": "^9.0.0", + "@wordpress/e2e-test-utils": "^10.0.0", + "husky": "^8.0.3", + "lint-staged": "^15.0.0", + "prettier": "^3.0.0" + }, + "dependencies": {}, + "lint-staged": { + "*.{js,jsx,ts,tsx}": [ + "wp-scripts lint-js --fix", + "wp-scripts format" + ], + "*.{css,scss,sass}": [ + "wp-scripts lint-style --fix" + ], + "*.php": [ + "composer run lint:fix" + ] + }, + "husky": { + "hooks": { + "pre-commit": "lint-staged" + } + } +} \ No newline at end of file diff --git a/parts/footer.html b/parts/footer.html new file mode 100644 index 0000000..dd0d859 --- /dev/null +++ b/parts/footer.html @@ -0,0 +1,66 @@ + +
+ +
+ +
+ + + + +

{{footer_text}}

+ +
+ + + +
+ +

{{links_heading}}

+ + + +
+ + + +
+ +

{{social_heading}}

+ + + + + +
+ +
+ + + +
+ + + +
+ +

+ {{copyright_text}} +

+ + + +

+ {{privacy_text}} | + {{terms_text}} +

+ +
+ +
+ \ No newline at end of file diff --git a/parts/header.html b/parts/header.html new file mode 100644 index 0000000..54c10cd --- /dev/null +++ b/parts/header.html @@ -0,0 +1,22 @@ + +
+ +
+ +
+ + + +
+ + +
+ +
+ + + +
+ +
+ \ No newline at end of file diff --git a/phpcs.xml b/phpcs.xml new file mode 100644 index 0000000..daf7734 --- /dev/null +++ b/phpcs.xml @@ -0,0 +1,57 @@ + + + {{theme_name}} coding standards + + + + . + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /node_modules/* + /vendor/* + /public/* + /tests/* + \ No newline at end of file diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..c9c89e7 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,29 @@ + + + + + ./tests/ + + + + + ./inc/ + ./functions.php + + + ./tests/ + ./vendor/ + ./node_modules/ + + + + + + \ No newline at end of file diff --git a/playwright.config.js b/playwright.config.js new file mode 100644 index 0000000..5cccaaf --- /dev/null +++ b/playwright.config.js @@ -0,0 +1,42 @@ +const { defineConfig, devices } = require('@playwright/test'); + +module.exports = defineConfig({ + testDir: './tests/e2e', + fullyParallel: true, + forbidOnly: !!process.env.CI, + retries: process.env.CI ? 2 : 0, + workers: process.env.CI ? 1 : undefined, + reporter: 'html', + use: { + baseURL: 'http://localhost:8889', + trace: 'on-first-retry', + screenshot: 'only-on-failure', + }, + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + { + name: 'firefox', + use: { ...devices['Desktop Firefox'] }, + }, + { + name: 'webkit', + use: { ...devices['Desktop Safari'] }, + }, + { + name: 'Mobile Chrome', + use: { ...devices['Pixel 5'] }, + }, + { + name: 'Mobile Safari', + use: { ...devices['iPhone 12'] }, + }, + ], + webServer: { + command: 'npm run env:start', + port: 8889, + reuseExistingServer: !process.env.CI, + }, +}); \ No newline at end of file diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..f789c4b --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,8 @@ +module.exports = { + plugins: [ + require( 'autoprefixer' ), + require( 'cssnano' )( { + preset: 'default', + } ), + ], +}; \ No newline at end of file diff --git a/src/css/editor.scss b/src/css/editor.scss new file mode 100644 index 0000000..219e871 --- /dev/null +++ b/src/css/editor.scss @@ -0,0 +1,39 @@ +/** + * Editor-specific styles + */ + +/* Import WordPress editor styles */ +@import "~@wordpress/block-editor/build-style/style.css"; + +/* Editor overrides */ +.block-editor-writing-flow { + font-family: var(--wp--preset--font-family--body); +} + +/* Block toolbar customizations */ +.block-editor-block-toolbar { + border-radius: 4px; +} + +/* Block controls */ +.block-editor-block-controls { + .components-toolbar-group { + border-radius: 4px; + } +} + +/* Inspector controls */ +.block-editor-block-inspector { + .components-panel__header { + background: var(--wp--preset--color--background); + border-bottom: 1px solid var(--wp--preset--color--neutral); + } +} + +/* Template part highlighting */ +.wp-block-template-part { + &.is-selected { + outline: 2px solid var(--wp--preset--color--primary); + outline-offset: 2px; + } +} \ No newline at end of file diff --git a/src/css/style.scss b/src/css/style.scss new file mode 100644 index 0000000..25a190d --- /dev/null +++ b/src/css/style.scss @@ -0,0 +1,182 @@ +/** + * Main theme styles + */ + +/* Import WordPress block styles */ +@import "~@wordpress/block-library/build-style/style.css"; +@import "~@wordpress/block-library/build-style/theme.css"; + +/* Custom theme styles */ +:root { + /* Custom properties for theme colors */ + --theme-primary: var(--wp--preset--color--primary); + --theme-secondary: var(--wp--preset--color--secondary); + --theme-background: var(--wp--preset--color--background); + --theme-foreground: var(--wp--preset--color--foreground); + --theme-accent: var(--wp--preset--color--accent); + --theme-neutral: var(--wp--preset--color--neutral); + + /* Custom properties for spacing */ + --theme-spacing-xs: var(--wp--preset--spacing--x-small); + --theme-spacing-sm: var(--wp--preset--spacing--small); + --theme-spacing-md: var(--wp--preset--spacing--medium); + --theme-spacing-lg: var(--wp--preset--spacing--large); + --theme-spacing-xl: var(--wp--preset--spacing--x-large); +} + +/* Body and typography */ +body { + line-height: 1.6; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +/* Accessibility improvements */ +.screen-reader-text { + border: 0; + clip: rect(1px, 1px, 1px, 1px); + -webkit-clip-path: inset(50%); + clip-path: inset(50%); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute !important; + width: 1px; + word-wrap: normal !important; +} + +.screen-reader-text:focus { + background-color: #f1f1f1; + border-radius: 3px; + box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.6); + clip: auto !important; + -webkit-clip-path: none; + clip-path: none; + color: #21759b; + display: block; + font-size: 0.875rem; + font-weight: 700; + height: auto; + left: 5px; + line-height: normal; + padding: 15px 23px 14px; + text-decoration: none; + top: 5px; + width: auto; + z-index: 100000; +} + +/* Button styles */ +.wp-block-button { + &.is-style-outline { + .wp-block-button__link { + border: 2px solid currentColor; + background: transparent; + + &:hover { + background: currentColor; + color: var(--wp--preset--color--background); + } + } + } + + &.is-style-ghost { + .wp-block-button__link { + background: transparent; + color: inherit; + text-decoration: underline; + border: none; + + &:hover { + text-decoration: none; + } + } + } +} + +/* Quote styles */ +.wp-block-quote { + &.is-style-modern { + border-left: none; + padding-left: 0; + position: relative; + + &::before { + content: '"'; + font-size: 4rem; + line-height: 1; + color: var(--wp--preset--color--primary); + position: absolute; + top: -1rem; + left: -1rem; + } + + p { + font-style: italic; + font-size: 1.25rem; + margin-left: 2rem; + } + } +} + +/* Group styles */ +.wp-block-group { + &.is-style-shadow { + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); + border-radius: 0.5rem; + padding: var(--wp--preset--spacing--medium); + } + + &.is-style-border { + border: 1px solid var(--wp--preset--color--neutral); + border-radius: 0.5rem; + padding: var(--wp--preset--spacing--medium); + } +} + +/* Image styles */ +.wp-block-image { + &.is-style-rounded { + img { + border-radius: 0.5rem; + } + } +} + +/* Post title styles */ +.wp-block-post-title { + &.is-style-gradient { + background: var(--wp--preset--gradient--primary-to-secondary); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + } +} + +/* Navigation improvements */ +.wp-block-navigation { + .wp-block-navigation__responsive-container.is-menu-open { + padding: var(--wp--preset--spacing--medium); + } +} + +/* Skip link */ +.skip-link { + position: absolute; + left: -9999px; + z-index: 999999999; + text-decoration: none; + + &:focus { + left: 6px; + top: 7px; + z-index: 999999; + padding: 8px 16px; + background: var(--wp--preset--color--background); + color: var(--wp--preset--color--foreground); + text-decoration: none; + border-radius: 3px; + box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.6); + } +} \ No newline at end of file diff --git a/src/js/editor.js b/src/js/editor.js new file mode 100644 index 0000000..64cdcab --- /dev/null +++ b/src/js/editor.js @@ -0,0 +1,114 @@ +/** + * Editor JavaScript enhancements + */ + +// Register custom block styles in the editor +wp.domReady( function() { + // Unregister unwanted core block styles + wp.blocks.unregisterBlockStyle( 'core/quote', 'large' ); + wp.blocks.unregisterBlockStyle( 'core/separator', 'wide' ); + wp.blocks.unregisterBlockStyle( 'core/separator', 'dots' ); + + // Register custom block variations + wp.blocks.registerBlockVariation( 'core/group', { + name: '{{theme_slug}}-card', + title: '{{theme_name}} Card', + description: 'A styled card container', + category: 'design', + icon: 'admin-page', + attributes: { + className: 'is-style-shadow', + style: { + spacing: { + padding: { + top: 'var:preset|spacing|medium', + right: 'var:preset|spacing|medium', + bottom: 'var:preset|spacing|medium', + left: 'var:preset|spacing|medium' + } + } + } + } + } ); + + // Add custom formatting options + wp.richText.registerFormatType( '{{theme_slug}}/highlight', { + title: 'Highlight', + tagName: 'mark', + className: 'highlight', + edit: function( { isActive, value, onChange } ) { + return wp.element.createElement( + wp.blockEditor.RichTextToolbarButton, + { + icon: 'admin-customizer', + title: 'Highlight', + onClick: function() { + onChange( + wp.richText.toggleFormat( value, { + type: '{{theme_slug}}/highlight' + } ) + ); + }, + isActive: isActive + } + ); + } + } ); +} ); + +// Editor theme utilities +const {{theme_slug|camelCase}}Editor = { + /** + * Add custom classes to blocks based on attributes + */ + addBlockClasses() { + const { addFilter } = wp.hooks; + + addFilter( + 'blocks.getSaveContent.extraProps', + '{{theme_slug}}/add-block-classes', + function( props, blockType, attributes ) { + if ( blockType.name === 'core/group' && attributes.className ) { + props.className = attributes.className; + } + return props; + } + ); + }, + + /** + * Customize block editor sidebar + */ + customizeSidebar() { + const { registerPlugin } = wp.plugins; + const { PluginSidebar, PluginSidebarMoreMenuItem } = wp.editPost; + const { PanelBody, TextControl } = wp.components; + const { __ } = wp.i18n; + + const {{theme_slug|camelCase}}Sidebar = () => { + return wp.element.createElement( + PluginSidebar, + { + name: '{{theme_slug}}-sidebar', + title: '{{theme_name}} Settings', + icon: 'admin-appearance' + }, + wp.element.createElement( + PanelBody, + { title: __( 'Theme Options', '{{theme_slug}}' ) }, + wp.element.createElement( 'p', null, __( 'Custom theme settings will appear here.', '{{theme_slug}}' ) ) + ) + ); + }; + + registerPlugin( '{{theme_slug}}-sidebar', { + render: {{theme_slug|camelCase}}Sidebar + } ); + } +}; + +// Initialize editor enhancements +wp.domReady( function() { + {{theme_slug|camelCase}}Editor.addBlockClasses(); + {{theme_slug|camelCase}}Editor.customizeSidebar(); +} ); \ No newline at end of file diff --git a/src/js/theme.js b/src/js/theme.js new file mode 100644 index 0000000..6ab2e3c --- /dev/null +++ b/src/js/theme.js @@ -0,0 +1,105 @@ +/** + * Main theme JavaScript + */ + +// Skip link functionality +document.addEventListener( 'DOMContentLoaded', function() { + // Add skip link + const skipLink = document.createElement( 'a' ); + skipLink.href = '#main'; + skipLink.className = 'skip-link screen-reader-text'; + skipLink.textContent = '{{skip_link_text}}'; + document.body.insertBefore( skipLink, document.body.firstChild ); + + // Smooth scroll for anchor links + const anchorLinks = document.querySelectorAll( 'a[href^="#"]' ); + anchorLinks.forEach( link => { + link.addEventListener( 'click', function( e ) { + const href = this.getAttribute( 'href' ); + const target = document.querySelector( href ); + + if ( target ) { + e.preventDefault(); + target.scrollIntoView( { + behavior: 'smooth', + block: 'start' + } ); + } + } ); + } ); + + // Mobile menu accessibility + const navToggle = document.querySelector( '.wp-block-navigation__responsive-container-open' ); + if ( navToggle ) { + navToggle.addEventListener( 'click', function() { + const expanded = this.getAttribute( 'aria-expanded' ) === 'true'; + this.setAttribute( 'aria-expanded', ! expanded ); + } ); + } + + // Focus management for modal dialogs + const modals = document.querySelectorAll( '[role="dialog"]' ); + modals.forEach( modal => { + const focusableElements = modal.querySelectorAll( + 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])' + ); + + if ( focusableElements.length > 0 ) { + focusableElements[0].focus(); + } + } ); +} ); + +// Theme utilities +const {{theme_slug|camelCase}} = { + /** + * Initialize theme features + */ + init() { + this.setupAnimations(); + this.setupLazyLoading(); + }, + + /** + * Setup scroll animations + */ + setupAnimations() { + if ( 'IntersectionObserver' in window ) { + const observer = new IntersectionObserver( entries => { + entries.forEach( entry => { + if ( entry.isIntersecting ) { + entry.target.classList.add( 'is-visible' ); + } + } ); + }, { + threshold: 0.1 + } ); + + const animatedElements = document.querySelectorAll( '.animate-on-scroll' ); + animatedElements.forEach( el => observer.observe( el ) ); + } + }, + + /** + * Setup lazy loading for images + */ + setupLazyLoading() { + if ( 'loading' in HTMLImageElement.prototype ) { + const images = document.querySelectorAll( 'img[data-src]' ); + images.forEach( img => { + img.src = img.dataset.src; + img.removeAttribute( 'data-src' ); + } ); + } else { + // Fallback for browsers without native lazy loading + const script = document.createElement( 'script' ); + script.src = 'https://cdn.jsdelivr.net/npm/intersection-observer@0.12.0/intersection-observer.js'; + document.head.appendChild( script ); + } + } +}; + +// Initialize theme +document.addEventListener( 'DOMContentLoaded', () => { + {{theme_slug|camelCase}}.init(); +} ); \ No newline at end of file diff --git a/style.css b/style.css new file mode 100644 index 0000000..2e11404 --- /dev/null +++ b/style.css @@ -0,0 +1,34 @@ +/* +Theme Name: {{theme_name}} +Theme URI: {{theme_uri}} +Author: {{author}} +Author URI: {{author_uri}} +Description: {{description}} +Version: {{version}} +Requires at least: {{min_wp_version}} +Tested up to: {{tested_wp_version}} +Requires PHP: {{min_php_version}} +License: {{license}} +License URI: {{license_uri}} +Text Domain: {{theme_slug}} +Tags: {{theme_tags}} +Network: false + +{{theme_name}} WordPress Theme, Copyright (C) {{year}} {{author}} +{{theme_name}} is distributed under the terms of the {{license}} + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +/* + * Font smoothing. + * This is a noop, but it's needed to make the theme pass the theme review. + */ \ No newline at end of file diff --git a/stylelint.config.js b/stylelint.config.js new file mode 100644 index 0000000..5484892 --- /dev/null +++ b/stylelint.config.js @@ -0,0 +1,24 @@ +module.exports = { + extends: [ '@wordpress/stylelint-config/scss' ], + rules: { + 'at-rule-no-unknown': [ + true, + { + ignoreAtRules: [ + 'tailwind', + 'apply', + 'layer', + 'config', + 'variants', + 'responsive', + 'screen', + ], + }, + ], + 'declaration-property-unit-allowed-list': { + 'line-height': [ 'px', 'em', 'rem', '%', '' ], + }, + 'selector-class-pattern': null, + 'selector-id-pattern': null, + }, +}; \ No newline at end of file diff --git a/styles/dark.json b/styles/dark.json new file mode 100644 index 0000000..b880b95 --- /dev/null +++ b/styles/dark.json @@ -0,0 +1,47 @@ +{ + "$schema": "https://schemas.wp.org/trunk/theme.json", + "version": 2, + "title": "{{dark_mode_title}}", + "settings": { + "color": { + "palette": [ + { + "slug": "primary", + "color": "{{dark_primary_color}}", + "name": "Primary" + }, + { + "slug": "secondary", + "color": "{{dark_secondary_color}}", + "name": "Secondary" + }, + { + "slug": "background", + "color": "{{dark_background_color}}", + "name": "Background" + }, + { + "slug": "foreground", + "color": "{{dark_text_color}}", + "name": "Foreground" + }, + { + "slug": "accent", + "color": "{{dark_accent_color}}", + "name": "Accent" + }, + { + "slug": "neutral", + "color": "{{dark_neutral_color}}", + "name": "Neutral" + } + ] + } + }, + "styles": { + "color": { + "background": "var(--wp--preset--color--background)", + "text": "var(--wp--preset--color--foreground)" + } + } +} \ No newline at end of file diff --git a/templates/404.html b/templates/404.html new file mode 100644 index 0000000..3a31ac2 --- /dev/null +++ b/templates/404.html @@ -0,0 +1,39 @@ + + + +
+ +

{{not_found_title}}

+ + + +

{{not_found_description}}

+ + + + + + + + + +

{{recent_posts_title}}

+ + + +
+ + +
+ + + +
+ + +
+ +
+ + + \ No newline at end of file diff --git a/templates/archive.html b/templates/archive.html new file mode 100644 index 0000000..4e59bd3 --- /dev/null +++ b/templates/archive.html @@ -0,0 +1,42 @@ + + + +
+ + + + + +
+ + +
+ + + +
+ + +
+ +
+ + + + + + + + + + + +

{{no_posts_text}}

+ + +
+ +
+ + + \ No newline at end of file diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..7871d35 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,29 @@ + + + +
+ +
+ + + + + + + + + + + + + + +

{{no_posts_text}}

+ + +
+ +
+ + + \ No newline at end of file diff --git a/templates/page.html b/templates/page.html new file mode 100644 index 0000000..2eec561 --- /dev/null +++ b/templates/page.html @@ -0,0 +1,11 @@ + + + +
+ + + +
+ + + \ No newline at end of file diff --git a/templates/search.html b/templates/search.html new file mode 100644 index 0000000..fc65531 --- /dev/null +++ b/templates/search.html @@ -0,0 +1,40 @@ + + + +
+ + + +
+ + +
+ + + +
+ + +
+ +
+ + + + + + + + + + + +

{{no_search_results_text}}

+ + +
+ +
+ + + \ No newline at end of file diff --git a/templates/single.html b/templates/single.html new file mode 100644 index 0000000..2eec561 --- /dev/null +++ b/templates/single.html @@ -0,0 +1,11 @@ + + + +
+ + + +
+ + + \ No newline at end of file diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 0000000..a5b6dae --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,32 @@ + { + test.beforeEach(async ({ page }) => { + // Go to WordPress admin and activate theme + await page.goto('/wp-admin'); + + // Login if needed (assuming wp-env default credentials) + const loginForm = page.locator('#loginform'); + if (await loginForm.isVisible()) { + await page.fill('#user_login', 'admin'); + await page.fill('#user_pass', 'password'); + await page.click('#wp-submit'); + } + }); + + test('homepage loads correctly', async ({ page }) => { + await page.goto('/'); + + // Check for basic theme elements + await expect(page.locator('header')).toBeVisible(); + await expect(page.locator('main, #main')).toBeVisible(); + await expect(page.locator('footer')).toBeVisible(); + + // Check for navigation + await expect(page.locator('.wp-block-navigation')).toBeVisible(); + }); + + test('site editor loads', async ({ page }) => { + await page.goto('/wp-admin/site-editor.php'); + + // Wait for editor to load + await page.waitForSelector('.edit-site-layout__content'); + + // Check editor is functional + await expect(page.locator('.edit-site-layout__content')).toBeVisible(); + }); + + test('mobile navigation works', async ({ page }) => { + // Set mobile viewport + await page.setViewportSize({ width: 375, height: 667 }); + await page.goto('/'); + + // Check if mobile menu toggle exists + const menuToggle = page.locator('.wp-block-navigation__responsive-container-open'); + if (await menuToggle.isVisible()) { + await menuToggle.click(); + + // Check if menu opens + await expect(page.locator('.wp-block-navigation__responsive-container.is-menu-open')).toBeVisible(); + } + }); + + test('accessibility standards', async ({ page }) => { + await page.goto('/'); + + // Check for skip link + const skipLink = page.locator('.skip-link'); + await expect(skipLink).toBeHidden(); + + // Focus skip link + await page.keyboard.press('Tab'); + await expect(skipLink).toBeVisible(); + + // Check heading hierarchy + const h1 = page.locator('h1'); + await expect(h1).toHaveCount(1); + + // Check for alt text on images + const images = page.locator('img'); + const count = await images.count(); + for (let i = 0; i < count; i++) { + const img = images.nth(i); + const alt = await img.getAttribute('alt'); + expect(alt).toBeDefined(); + } + }); + + test('responsive design', async ({ page }) => { + await page.goto('/'); + + // Test different viewport sizes + const viewports = [ + { width: 320, height: 568 }, // Mobile + { width: 768, height: 1024 }, // Tablet + { width: 1200, height: 800 } // Desktop + ]; + + for (const viewport of viewports) { + await page.setViewportSize(viewport); + + // Check layout doesn't break + await expect(page.locator('body')).toBeVisible(); + + // Check no horizontal scroll + const bodyWidth = await page.evaluate(() => document.body.scrollWidth); + const viewportWidth = viewport.width; + expect(bodyWidth).toBeLessThanOrEqual(viewportWidth + 20); // Allow small tolerance + } + }); + + test('block patterns work', async ({ page }) => { + await page.goto('/wp-admin/post-new.php?post_type=page'); + + // Wait for editor to load + await page.waitForSelector('.block-editor-writing-flow'); + + // Open patterns panel + await page.click('button[aria-label="Add block"]'); + await page.click('button[aria-label="Patterns"]'); + + // Look for theme patterns + const themePatterns = page.locator('.block-editor-block-patterns-list__item:has-text("{{theme_name}}")'); + const patternCount = await themePatterns.count(); + + expect(patternCount).toBeGreaterThan(0); + }); + + test('style variations work', async ({ page }) => { + await page.goto('/wp-admin/site-editor.php'); + + // Wait for editor to load + await page.waitForSelector('.edit-site-layout__content'); + + // Open global styles + const stylesButton = page.locator('button:has-text("Styles")'); + if (await stylesButton.isVisible()) { + await stylesButton.click(); + + // Check for style variations + const variations = page.locator('.edit-site-global-styles-variations_item'); + const variationCount = await variations.count(); + + expect(variationCount).toBeGreaterThan(1); // Should have at least default + variations + } + }); +}); \ No newline at end of file diff --git a/tests/js/theme.test.js b/tests/js/theme.test.js new file mode 100644 index 0000000..9b9aed5 --- /dev/null +++ b/tests/js/theme.test.js @@ -0,0 +1,105 @@ +/** + * Test theme JavaScript functionality + */ + +describe( '{{theme_name}} Theme JavaScript', () => { + beforeEach( () => { + // Setup DOM + document.body.innerHTML = ''; + + // Mock WordPress globals + global.wp = { + domReady: jest.fn( callback => callback() ) + }; + }); + + describe( 'Skip Link', () => { + test( 'should add skip link to page', () => { + // Simulate DOMContentLoaded + const event = new Event( 'DOMContentLoaded' ); + document.dispatchEvent( event ); + + const skipLink = document.querySelector( '.skip-link' ); + expect( skipLink ).toBeTruthy(); + expect( skipLink.href ).toContain( '#main' ); + expect( skipLink.textContent ).toBeTruthy(); + }); + }); + + describe( 'Smooth Scrolling', () => { + test( 'should handle anchor links with smooth scrolling', () => { + // Create test elements + const link = document.createElement( 'a' ); + link.href = '#test-section'; + document.body.appendChild( link ); + + const target = document.createElement( 'div' ); + target.id = 'test-section'; + document.body.appendChild( target ); + + // Mock scrollIntoView + target.scrollIntoView = jest.fn(); + + // Simulate DOMContentLoaded + const event = new Event( 'DOMContentLoaded' ); + document.dispatchEvent( event ); + + // Click the link + link.click(); + + expect( target.scrollIntoView ).toHaveBeenCalledWith({ + behavior: 'smooth', + block: 'start' + }); + }); + }); + + describe( 'Navigation Accessibility', () => { + test( 'should handle navigation toggle accessibility', () => { + // Create navigation toggle + const navToggle = document.createElement( 'button' ); + navToggle.className = 'wp-block-navigation__responsive-container-open'; + navToggle.setAttribute( 'aria-expanded', 'false' ); + document.body.appendChild( navToggle ); + + // Simulate DOMContentLoaded + const event = new Event( 'DOMContentLoaded' ); + document.dispatchEvent( event ); + + // Click the toggle + navToggle.click(); + + expect( navToggle.getAttribute( 'aria-expanded' ) ).toBe( 'true' ); + }); + }); + + describe( 'Theme Utilities', () => { + test( 'should initialize theme features', () => { + // Mock IntersectionObserver + global.IntersectionObserver = jest.fn().mockImplementation(() => ({ + observe: jest.fn(), + disconnect: jest.fn() + })); + + // Mock HTMLImageElement + global.HTMLImageElement = { + prototype: { + loading: true + } + }; + + // Test theme initialization + expect( () => { + const {{theme_slug|camelCase}} = { + init() { + this.setupAnimations(); + this.setupLazyLoading(); + }, + setupAnimations() {}, + setupLazyLoading() {} + }; + {{theme_slug|camelCase}}.init(); + }).not.toThrow(); + }); + }); +}); \ No newline at end of file diff --git a/tests/test-theme-setup.php b/tests/test-theme-setup.php new file mode 100644 index 0000000..ba8bce7 --- /dev/null +++ b/tests/test-theme-setup.php @@ -0,0 +1,84 @@ +assertTrue( function_exists( '{{theme_slug}}_setup' ) ); + } + + /** + * Test theme supports required features + */ + public function test_theme_supports() { + $this->assertTrue( current_theme_supports( 'wp-block-styles' ) ); + $this->assertTrue( current_theme_supports( 'responsive-embeds' ) ); + $this->assertTrue( current_theme_supports( 'editor-styles' ) ); + $this->assertTrue( current_theme_supports( 'html5' ) ); + $this->assertTrue( current_theme_supports( 'automatic-feed-links' ) ); + $this->assertTrue( current_theme_supports( 'post-thumbnails' ) ); + $this->assertTrue( current_theme_supports( 'align-wide' ) ); + $this->assertTrue( current_theme_supports( 'custom-logo' ) ); + } + + /** + * Test theme version constant is defined + */ + public function test_theme_version_constant() { + $this->assertTrue( defined( '{{theme_slug|upper}}_VERSION' ) ); + $this->assertNotEmpty( constant( '{{theme_slug|upper}}_VERSION' ) ); + } + + /** + * Test content width is set + */ + public function test_content_width() { + global $content_width; + $this->assertNotEmpty( $content_width ); + $this->assertIsInt( $content_width ); + } + + /** + * Test theme includes are loaded + */ + public function test_theme_includes() { + $this->assertTrue( function_exists( '{{theme_slug}}_register_pattern_categories' ) ); + $this->assertTrue( function_exists( '{{theme_slug}}_enqueue_assets' ) ); + $this->assertTrue( function_exists( '{{theme_slug}}_enqueue_editor_assets' ) ); + } + + /** + * Test block pattern categories are registered + */ + public function test_block_pattern_categories() { + $categories = WP_Block_Pattern_Categories_Registry::get_instance()->get_all_registered(); + $theme_categories = array_filter( $categories, function( $category ) { + return strpos( $category['name'], '{{theme_slug}}-' ) === 0; + }); + + $this->assertNotEmpty( $theme_categories ); + } + + /** + * Test theme.json file exists and is valid + */ + public function test_theme_json_exists() { + $theme_json_path = get_template_directory() . '/theme.json'; + $this->assertFileExists( $theme_json_path ); + + $theme_json_content = file_get_contents( $theme_json_path ); + $theme_json_data = json_decode( $theme_json_content, true ); + + $this->assertNotNull( $theme_json_data ); + $this->assertEquals( 2, $theme_json_data['version'] ); + $this->assertArrayHasKey( 'settings', $theme_json_data ); + $this->assertArrayHasKey( 'styles', $theme_json_data ); + } +} \ No newline at end of file diff --git a/theme.json b/theme.json new file mode 100644 index 0000000..778001f --- /dev/null +++ b/theme.json @@ -0,0 +1,317 @@ +{ + "$schema": "https://schemas.wp.org/trunk/theme.json", + "version": 2, + "settings": { + "appearanceTools": true, + "useRootPaddingAwareAlignments": true, + "color": { + "background": true, + "custom": true, + "customDuotone": true, + "customGradient": true, + "defaultDuotone": true, + "defaultGradients": true, + "defaultPalette": true, + "text": true, + "palette": [ + { + "slug": "primary", + "color": "{{primary_color}}", + "name": "Primary" + }, + { + "slug": "secondary", + "color": "{{secondary_color}}", + "name": "Secondary" + }, + { + "slug": "background", + "color": "{{background_color}}", + "name": "Background" + }, + { + "slug": "foreground", + "color": "{{text_color}}", + "name": "Foreground" + }, + { + "slug": "accent", + "color": "{{accent_color}}", + "name": "Accent" + }, + { + "slug": "neutral", + "color": "{{neutral_color}}", + "name": "Neutral" + } + ], + "gradients": [ + { + "slug": "primary-to-secondary", + "gradient": "linear-gradient(135deg, {{primary_color}} 0%, {{secondary_color}} 100%)", + "name": "Primary to Secondary" + } + ] + }, + "typography": { + "customFontSize": true, + "dropCap": true, + "fontStyle": true, + "fontWeight": true, + "letterSpacing": true, + "lineHeight": true, + "textDecoration": true, + "textTransform": true, + "fontFamilies": [ + { + "fontFamily": "{{heading_font_family}}", + "slug": "heading", + "name": "{{heading_font_name}}" + }, + { + "fontFamily": "{{body_font_family}}", + "slug": "body", + "name": "{{body_font_name}}" + }, + { + "fontFamily": "{{mono_font_family}}", + "slug": "monospace", + "name": "{{mono_font_name}}" + } + ], + "fontSizes": [ + { + "slug": "small", + "size": "{{font_size_small}}", + "name": "Small" + }, + { + "slug": "medium", + "size": "{{font_size_medium}}", + "name": "Medium" + }, + { + "slug": "large", + "size": "{{font_size_large}}", + "name": "Large" + }, + { + "slug": "x-large", + "size": "{{font_size_x_large}}", + "name": "Extra Large" + }, + { + "slug": "xx-large", + "size": "{{font_size_xx_large}}", + "name": "Extra Extra Large" + } + ] + }, + "spacing": { + "customSpacingSize": true, + "units": ["px", "em", "rem", "vh", "vw", "%"], + "spacingSizes": [ + { + "slug": "x-small", + "size": "{{spacing_x_small}}", + "name": "Extra Small" + }, + { + "slug": "small", + "size": "{{spacing_small}}", + "name": "Small" + }, + { + "slug": "medium", + "size": "{{spacing_medium}}", + "name": "Medium" + }, + { + "slug": "large", + "size": "{{spacing_large}}", + "name": "Large" + }, + { + "slug": "x-large", + "size": "{{spacing_x_large}}", + "name": "Extra Large" + } + ] + }, + "layout": { + "contentSize": "{{content_width}}", + "wideSize": "{{wide_width}}" + }, + "border": { + "color": true, + "radius": true, + "style": true, + "width": true + }, + "blocks": { + "core/button": { + "border": { + "radius": true + } + }, + "core/pullquote": { + "border": { + "color": true, + "radius": true, + "style": true, + "width": true + } + }, + "core/quote": { + "border": { + "color": true, + "radius": true, + "style": true, + "width": true + } + } + } + }, + "styles": { + "color": { + "background": "var(--wp--preset--color--background)", + "text": "var(--wp--preset--color--foreground)" + }, + "typography": { + "fontFamily": "var(--wp--preset--font-family--body)", + "fontSize": "var(--wp--preset--font-size--medium)", + "lineHeight": "{{body_line_height}}" + }, + "spacing": { + "blockGap": "var(--wp--preset--spacing--medium)" + }, + "elements": { + "h1": { + "typography": { + "fontFamily": "var(--wp--preset--font-family--heading)", + "fontSize": "var(--wp--preset--font-size--xx-large)", + "fontWeight": "{{heading_font_weight}}", + "lineHeight": "{{heading_line_height}}" + } + }, + "h2": { + "typography": { + "fontFamily": "var(--wp--preset--font-family--heading)", + "fontSize": "var(--wp--preset--font-size--x-large)", + "fontWeight": "{{heading_font_weight}}", + "lineHeight": "{{heading_line_height}}" + } + }, + "h3": { + "typography": { + "fontFamily": "var(--wp--preset--font-family--heading)", + "fontSize": "var(--wp--preset--font-size--large)", + "fontWeight": "{{heading_font_weight}}", + "lineHeight": "{{heading_line_height}}" + } + }, + "h4": { + "typography": { + "fontFamily": "var(--wp--preset--font-family--heading)", + "fontSize": "var(--wp--preset--font-size--medium)", + "fontWeight": "{{heading_font_weight}}", + "lineHeight": "{{heading_line_height}}" + } + }, + "h5": { + "typography": { + "fontFamily": "var(--wp--preset--font-family--heading)", + "fontSize": "var(--wp--preset--font-size--small)", + "fontWeight": "{{heading_font_weight}}", + "lineHeight": "{{heading_line_height}}" + } + }, + "h6": { + "typography": { + "fontFamily": "var(--wp--preset--font-family--heading)", + "fontSize": "var(--wp--preset--font-size--small)", + "fontWeight": "{{heading_font_weight}}", + "lineHeight": "{{heading_line_height}}" + } + }, + "link": { + "color": { + "text": "var(--wp--preset--color--primary)" + }, + ":hover": { + "color": { + "text": "var(--wp--preset--color--secondary)" + } + } + }, + "button": { + "color": { + "background": "var(--wp--preset--color--primary)", + "text": "var(--wp--preset--color--background)" + }, + "typography": { + "fontWeight": "{{button_font_weight}}" + }, + "border": { + "radius": "{{button_border_radius}}" + }, + ":hover": { + "color": { + "background": "var(--wp--preset--color--secondary)" + } + } + } + }, + "blocks": { + "core/navigation": { + "typography": { + "fontSize": "var(--wp--preset--font-size--medium)" + } + }, + "core/site-title": { + "typography": { + "fontFamily": "var(--wp--preset--font-family--heading)", + "fontSize": "var(--wp--preset--font-size--large)", + "fontWeight": "{{site_title_font_weight}}" + } + }, + "core/site-tagline": { + "typography": { + "fontSize": "var(--wp--preset--font-size--small)" + }, + "color": { + "text": "var(--wp--preset--color--neutral)" + } + } + } + }, + "customTemplates": [ + { + "name": "blank", + "title": "Blank", + "postTypes": ["page", "post"] + }, + { + "name": "page-no-title", + "title": "Page (No Title)", + "postTypes": ["page"] + } + ], + "templateParts": [ + { + "name": "header", + "title": "Header", + "area": "header" + }, + { + "name": "footer", + "title": "Footer", + "area": "footer" + }, + { + "name": "sidebar", + "title": "Sidebar", + "area": "uncategorized" + } + ] +} \ No newline at end of file diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 0000000..4a5acf3 --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,58 @@ +const defaultConfig = require( '@wordpress/scripts/config/webpack.config' ); +const path = require( 'path' ); + +module.exports = { + ...defaultConfig, + entry: { + 'theme': './src/js/theme.js', + 'editor': './src/js/editor.js', + 'style': './src/css/style.scss', + 'editor-style': './src/css/editor.scss', + }, + output: { + path: path.resolve( process.cwd(), 'public' ), + filename: 'js/[name].js', + clean: true, + }, + module: { + ...defaultConfig.module, + rules: [ + ...defaultConfig.module.rules, + { + test: /\.scss$/, + use: [ + 'style-loader', + 'css-loader', + { + loader: 'sass-loader', + options: { + sassOptions: { + includePaths: [ 'src/css' ], + }, + }, + }, + ], + }, + ], + }, + resolve: { + ...defaultConfig.resolve, + alias: { + ...defaultConfig.resolve.alias, + '@': path.resolve( __dirname, 'src' ), + }, + }, + optimization: { + ...defaultConfig.optimization, + splitChunks: { + chunks: 'all', + cacheGroups: { + vendor: { + test: /[\\/]node_modules[\\/]/, + name: 'vendors', + chunks: 'all', + }, + }, + }, + }, +}; \ No newline at end of file