├── .editorconfig
├── .env.example
├── .github
└── workflows
│ └── php.yml
├── .gitignore
├── .htaccess
├── README.md
├── composer.json
├── config
├── application.php
└── environments
│ ├── development.php
│ └── staging.php
├── dev
├── cpt.txt
└── taxonomy.txt
├── github
└── composer.json
├── start.sh
├── web
├── .htaccess
├── app
│ ├── languages
│ │ └── .gitkeep
│ ├── mu-plugins
│ │ ├── bedrock-autoloader.php
│ │ ├── juniper-checklist.php
│ │ ├── juniper-commands.php
│ │ └── register-theme-directory.php
│ ├── plugins
│ │ └── .gitkeep
│ ├── themes
│ │ ├── .gitkeep
│ │ └── juniper-theme
│ │ │ ├── .bootstraprc
│ │ │ ├── .eslintrc.json
│ │ │ ├── .gitignore
│ │ │ ├── .posthtmlrc
│ │ │ ├── README.md
│ │ │ ├── acf-json
│ │ │ └── .gitkeep
│ │ │ ├── blocks
│ │ │ ├── .gitkeep
│ │ │ ├── cta
│ │ │ │ ├── ajax.js
│ │ │ │ ├── functions.php
│ │ │ │ ├── script.js
│ │ │ │ └── style.scss
│ │ │ └── filteringposts
│ │ │ │ ├── ajax.js
│ │ │ │ ├── functions.php
│ │ │ │ ├── script.js
│ │ │ │ └── style.scss
│ │ │ ├── class-startersite.php
│ │ │ ├── composer.json
│ │ │ ├── cypress.config.js
│ │ │ ├── cypress
│ │ │ ├── e2e
│ │ │ │ └── blockRestAPI.cy.js
│ │ │ ├── fixtures
│ │ │ │ └── example.json
│ │ │ └── support
│ │ │ │ ├── commands.js
│ │ │ │ └── e2e.js
│ │ │ ├── functions.php
│ │ │ ├── inc
│ │ │ ├── Ajax
│ │ │ │ └── JuniperAjaxFilteringposts.php
│ │ │ ├── Blocks
│ │ │ │ └── JuniperBlocks.php
│ │ │ ├── Cpt
│ │ │ │ └── .gitkeep
│ │ │ ├── Taxonomies
│ │ │ │ └── .gitkeep
│ │ │ ├── gutenberg-styles.php
│ │ │ ├── include.php
│ │ │ └── pattern-categories.php
│ │ │ ├── jsconfig.json
│ │ │ ├── package.json
│ │ │ ├── parts
│ │ │ ├── footer.html
│ │ │ ├── header.html
│ │ │ └── top-bar.html
│ │ │ ├── patterns
│ │ │ └── cta.php
│ │ │ ├── phpunit.xml
│ │ │ ├── screenshot.png
│ │ │ ├── src
│ │ │ ├── css
│ │ │ │ ├── _app.scss
│ │ │ │ ├── base
│ │ │ │ │ ├── _common.scss
│ │ │ │ │ ├── _index.scss
│ │ │ │ │ ├── _mixins.scss
│ │ │ │ │ ├── _reset.scss
│ │ │ │ │ ├── _typography.scss
│ │ │ │ │ └── _variables.scss
│ │ │ │ ├── components
│ │ │ │ │ ├── _buttons.scss
│ │ │ │ │ └── _forms.scss
│ │ │ │ └── vendor
│ │ │ │ │ └── swiper.scss
│ │ │ ├── img
│ │ │ │ └── sample.jpg
│ │ │ └── js
│ │ │ │ ├── _app.js
│ │ │ │ ├── animations
│ │ │ │ └── transitions.js
│ │ │ │ └── nav.js
│ │ │ ├── static
│ │ │ ├── no-timber.html
│ │ │ └── site.js
│ │ │ ├── style.css
│ │ │ ├── templates
│ │ │ ├── 404.html
│ │ │ ├── index.html
│ │ │ └── my-custom-template.html
│ │ │ ├── theme.json
│ │ │ └── views
│ │ │ └── blocks
│ │ │ ├── .gitkeep
│ │ │ └── cta.twig
│ └── uploads
│ │ └── .gitkeep
├── index.php
└── wp-config.php
├── work.sh
└── wp-cli.yml
/.editorconfig:
--------------------------------------------------------------------------------
1 | # This file is for unifying the coding style for different editors and IDEs
2 | # editorconfig.org
3 |
4 | # WordPress Coding Standards
5 | # https://make.wordpress.org/core/handbook/coding-standards/
6 |
7 | root = true
8 |
9 | [*]
10 | charset = utf-8
11 | end_of_line = lf
12 | indent_style = tab
13 | insert_final_newline = true
14 | trim_trailing_whitespace = true
15 |
16 | [*.css]
17 | indent_style = tab
18 | indent_size = 4
19 |
20 | [*.scss]
21 | indent_style = tab
22 | indent_size = 4
23 |
24 | [*.json]
25 | indent_style = space
26 | indent_size = 4
27 |
28 | [*.txt]
29 | end_of_line = crlf
30 |
31 | [*.{yml,yaml}]
32 | indent_style = space
33 | indent_size = 2
34 |
35 | [*.md]
36 | trim_trailing_whitespace = false
37 |
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
1 | DB_NAME=database_name
2 | DB_USER=database_user
3 | DB_PASSWORD=database_password
4 |
5 | # Optional variables
6 | # DB_HOST=localhost
7 | # DB_PREFIX=wp_
8 |
9 | WP_ENV=development
10 | WP_HOME=http://example.com
11 | WP_SITEURL=${WP_HOME}/wp
12 |
13 | # Generate your keys here: https://roots.io/salts.html
14 | AUTH_KEY='generateme'
15 | SECURE_AUTH_KEY='generateme'
16 | LOGGED_IN_KEY='generateme'
17 | NONCE_KEY='generateme'
18 | AUTH_SALT='generateme'
19 | SECURE_AUTH_SALT='generateme'
20 | LOGGED_IN_SALT='generateme'
21 | NONCE_SALT='generateme'
22 |
--------------------------------------------------------------------------------
/.github/workflows/php.yml:
--------------------------------------------------------------------------------
1 | name: PHP Composer
2 |
3 | on:
4 | push:
5 | branches:
6 | - '**'
7 | pull_request:
8 | branches:
9 | - '**'
10 |
11 | permissions:
12 | contents: read
13 |
14 | jobs:
15 | build:
16 |
17 | runs-on: ubuntu-latest
18 |
19 | steps:
20 | - uses: actions/checkout@v3
21 |
22 | - name: Install PHP
23 | uses: shivammathur/setup-php@2.7.0
24 | with:
25 | php-version: '7.4'
26 | coverage: none
27 | tools: composer:v1
28 |
29 | - name: Install dependencies
30 | working-directory: ./github
31 | run: composer install --prefer-dist --no-progress
32 |
33 | # Add a test script to composer.json, for instance: "test": "vendor/bin/phpunit"
34 | # Docs: https://getcomposer.org/doc/articles/scripts.md
35 |
36 | - name: Run set phpcs suite
37 | working-directory: ./github
38 | run: composer run set
39 |
40 | - name: Run test phpcs
41 | working-directory: ./github
42 | run: composer run test
43 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Application
2 | web/app/plugins/*
3 | !web/app/plugins/.gitkeep
4 | web/app/mu-plugins/*/
5 | web/app/upgrade
6 | web/app/uploads/*
7 | !web/app/uploads/.gitkeep
8 |
9 | # WordPress
10 | web/wp
11 |
12 | # Logs
13 | *.log
14 |
15 | # Dotenv
16 | .env
17 | .env.*
18 | !.env.example
19 |
20 | # Composer
21 | /vendor
22 |
23 | # WP-CLI
24 | wp-cli.local.yml
25 |
26 | .DS_Store
27 | .idea
28 |
29 | *.lock
30 | *-lock.json
31 | vendor/
32 | node_modules
33 |
34 | web/app/advanced-cache.php
35 | web/app/cache
36 | web/app/w3tc-config
37 | web-exppress
38 |
39 | .htaccess
40 |
--------------------------------------------------------------------------------
/.htaccess:
--------------------------------------------------------------------------------
1 | RewriteEngine on
2 | RewriteCond %{HTTP_HOST} ^(www.)?juniper.local$
3 | RewriteCond %{REQUEST_URI} !^/web/
4 | RewriteCond %{REQUEST_FILENAME} !-f
5 | RewriteCond %{REQUEST_FILENAME} !-d
6 | RewriteRule ^(.*)$ /web/$1
7 | RewriteCond %{HTTP_HOST} ^(www.)?juniper.local$
8 | RewriteRule ^(/)?$ web/index.php [L]
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Juniper - WordPress starter boilerplate + theme
10 |
11 |
12 | ## Overview
13 |
14 | Juniper is symbiosis of Bedrock boilerplate and Timber.
15 |
16 | Bedrock is a modern WordPress stack that helps you get started with the best development tools and project structure.
17 | Timber allows you to use twig templating system in your WP project.
18 | With this approach you can create theme code with logic files separated from frontend.
19 |
20 | ## Features
21 |
22 | - Easy WordPress configuration with environment specific files
23 | - File structure which makes keeping and maintaining your project on a Git a lot easier
24 | - Dependency management with [Composer](https://getcomposer.org)
25 | - Twig templating system
26 | - Bash console scripts to make creating project from the scratch much easier
27 |
28 | ## Requirements
29 |
30 | - PHP >= 7.4
31 | - Composer - [Install](https://getcomposer.org/doc/00-intro.md#installation-linux-unix-osx)
32 | - PHPCS & PHPCBF with WordPress-Extra standard installed
33 |
34 | ## Installation
35 |
36 | 1. Create local database as you would do for normal WP instance
37 | 2. Map project main catalogue to domain on your localhost
38 | 3. Start a new project:
39 | ```sh
40 | $ bash start.sh
41 | ```
42 | and follow the instructions in the console.
43 | Type in details from step 1 and 2. .env file will
44 | be crated for you (all DB and site details sits there)
45 | 4. Fill correct domain details in .htaccess in main catalogue.
46 | 5. Check if /web/ directory has .htaccess file with default WP entries.
47 | 6. Run
48 | ```sh
49 | $ bash work.sh
50 | ```
51 | in main project directory
52 | 7. Start coding your theme in /web/app/themes/juniper-theme/ :)
53 |
54 | ## Composer dependencies
55 |
56 | To maintain project correctly we insist you to use composer.json in main catalogue.
57 |
58 | If you want to add new plugin to your project you can use [WPackagist](https://wpackagist.org/) -
59 | the only thing you need is plugin slug from the main WP repository.
60 |
61 | You can update them with the same easy way by changing version in composer.json.
62 |
63 | ## Bash scripts
64 |
65 | The main operations that we automate have been handled by below scripts: :
66 |
67 | 1) start.sh - used for the initial configuration of the project. Through this process, we areable to enter the basic data to the database, define the main URL and ACF key. After providing those information, the installer will generate an .env file, which in our case will contain all configuration data (as in wp-config.php in a vanilla WordPress installation).
68 |
69 | 2) work.sh - used every time you work on a project. It compiles the styles in real time by calling a parcel script to listen for file changes.
70 |
71 | ## WP-CLI Commands
72 |
73 | 1. Adding Custom Post Types - After getting the name, a CPT will be created. Its editing will of course be possible later, because this command will generate a file in the theme directory.
74 | ```sh
75 | $ wp add cpt --name="Product"
76 | ```
77 | 2. Adding Taxonomies - Add a name of the taxonomy you want and the slug name of the posts you want it to be attached to and this command will take care of the rest
78 | ```sh
79 | $ wp add taxonomy --name="Category" --post="product"
80 | ```
81 | 3. Adding Gutenberg Blocks - This creates a custom Gutenberg block for the user utilizin the ACF Timber Blocks solution which allows us to use one .twig file with the appropriate comment to create a block. Keywords and description fields are optional
82 | ```sh
83 | $ wp add block --name="Reviews" --keywords="quote,stars" --description="Show three newest reviews"
84 | ```
85 |
86 |
87 | ##
88 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "osomstudio/juniper",
3 | "type": "project",
4 | "license": "MIT",
5 | "homepage": "https://github.com/osomstudio/juniper",
6 | "authors": [
7 | {
8 | "name": "OsomStudio",
9 | "homepage": "https://osomstudio.com"
10 | }
11 | ],
12 | "keywords": [
13 | "juniper",
14 | "timber",
15 | "bedrock",
16 | "composer",
17 | "roots",
18 | "wordpress",
19 | "wp",
20 | "wp-config"
21 | ],
22 | "support": {
23 | "issues": "https://github.com/osomstudio/juniper/issues"
24 | },
25 | "repositories": [
26 | {
27 | "type": "composer",
28 | "url": "https://wpackagist.org",
29 | "only": [
30 | "wpackagist-plugin/*",
31 | "wpackagist-theme/*"
32 | ]
33 | },
34 | {
35 | "type": "composer",
36 | "url": "https://connect.advancedcustomfields.com"
37 | }
38 | ],
39 | "require": {
40 | "php": ">=8.1",
41 | "composer/installers": "^2.2",
42 | "vlucas/phpdotenv": "^5.5",
43 | "oscarotero/env": "^2.1",
44 | "roots/bedrock-autoloader": "^1.0",
45 | "roots/bedrock-disallow-indexing": "^2.0",
46 | "roots/wordpress": "^6.7.2",
47 | "roots/wp-config": "1.0.0",
48 | "roots/wp-password-bcrypt": "^1.1",
49 | "wpackagist-plugin/contact-form-7": "6.0.4",
50 | "wpackagist-plugin/wordpress-seo": "24.6",
51 | "wpackagist-plugin/w3-total-cache": "2.8.6",
52 | "wpackagist-plugin/contact-form-7-honeypot": "2.1.7",
53 | "wpackagist-plugin/webp-express": "0.25.9",
54 | "wpengine/advanced-custom-fields-pro": "^6.2"
55 | },
56 | "require-dev": {
57 | "roave/security-advisories": "dev-latest",
58 | "squizlabs/php_codesniffer": "^3.6.0",
59 | "wp-coding-standards/wpcs": "^3.1"
60 | },
61 | "extra": {
62 | "installer-paths": {
63 | "web/app/mu-plugins/{$name}/": [
64 | "type:wordpress-muplugin"
65 | ],
66 | "web/app/plugins/{$name}/": [
67 | "type:wordpress-plugin"
68 | ],
69 | "web/app/themes/{$name}/": [
70 | "type:wordpress-theme"
71 | ]
72 | },
73 | "wordpress-install-dir": "web/wp"
74 | },
75 | "scripts": {
76 | "post-root-package-install": [
77 | "php -r \"copy('.env.example', '.env');\""
78 | ],
79 | "test": [
80 | "phpcs --standard=WordPress-Extra -snp --basepath=. --ignore=vendor,node_modules --extensions=php --exclude=WordPress.Files.FileName ./web/app/themes"
81 | ],
82 | "start": [
83 | "composer install",
84 | "cd web/app/themes/* && npm install && composer install"
85 | ]
86 | },
87 | "config": {
88 | "optimize-autoloader": true,
89 | "preferred-install": "dist",
90 | "allow-plugins": {
91 | "composer/installers": true,
92 | "roots/wordpress-core-installer": true,
93 | "dealerdirect/phpcodesniffer-composer-installer": true
94 | },
95 | "sort-packages": true,
96 | "platform": {
97 | "php": "8.1"
98 | }
99 | },
100 | "minimum-stability": "dev",
101 | "prefer-stable": true
102 | }
103 |
--------------------------------------------------------------------------------
/config/application.php:
--------------------------------------------------------------------------------
1 | load();
39 | $dotenv->required(['WP_HOME', 'WP_SITEURL']);
40 | if (!env('DATABASE_URL')) {
41 | $dotenv->required(['DB_NAME', 'DB_USER', 'DB_PASSWORD']);
42 | }
43 | }
44 |
45 | /**
46 | * Set up our global environment constant and load its config first
47 | * Default: production
48 | */
49 | define('WP_ENV', env('WP_ENV') ?: 'production');
50 |
51 | /**
52 | * URLs
53 | */
54 | Config::define('WP_HOME', env('WP_HOME'));
55 | Config::define('WP_SITEURL', env('WP_SITEURL'));
56 |
57 | /**
58 | * Custom Content Directory
59 | */
60 | Config::define('CONTENT_DIR', '/app');
61 | Config::define('WP_CONTENT_DIR', $webroot_dir . Config::get('CONTENT_DIR'));
62 | Config::define('WP_CONTENT_URL', Config::get('WP_HOME') . Config::get('CONTENT_DIR'));
63 |
64 | /**
65 | * DB settings
66 | */
67 | Config::define('DB_NAME', env('DB_NAME'));
68 | Config::define('DB_USER', env('DB_USER'));
69 | Config::define('DB_PASSWORD', env('DB_PASSWORD'));
70 | Config::define('DB_HOST', env('DB_HOST') ?: 'localhost');
71 | Config::define('DB_CHARSET', 'utf8mb4');
72 | Config::define('DB_COLLATE', '');
73 | $table_prefix = env('DB_PREFIX') ?: 'wp_';
74 |
75 | if (env('DATABASE_URL')) {
76 | $dsn = (object) parse_url(env('DATABASE_URL'));
77 |
78 | Config::define('DB_NAME', substr($dsn->path, 1));
79 | Config::define('DB_USER', $dsn->user);
80 | Config::define('DB_PASSWORD', isset($dsn->pass) ? $dsn->pass : null);
81 | Config::define('DB_HOST', isset($dsn->port) ? "{$dsn->host}:{$dsn->port}" : $dsn->host);
82 | }
83 |
84 | /**
85 | * Authentication Unique Keys and Salts
86 | */
87 | Config::define('AUTH_KEY', env('AUTH_KEY'));
88 | Config::define('SECURE_AUTH_KEY', env('SECURE_AUTH_KEY'));
89 | Config::define('LOGGED_IN_KEY', env('LOGGED_IN_KEY'));
90 | Config::define('NONCE_KEY', env('NONCE_KEY'));
91 | Config::define('AUTH_SALT', env('AUTH_SALT'));
92 | Config::define('SECURE_AUTH_SALT', env('SECURE_AUTH_SALT'));
93 | Config::define('LOGGED_IN_SALT', env('LOGGED_IN_SALT'));
94 | Config::define('NONCE_SALT', env('NONCE_SALT'));
95 |
96 | /**
97 | * Custom Settings
98 | */
99 | Config::define('AUTOMATIC_UPDATER_DISABLED', true);
100 | Config::define('DISABLE_WP_CRON', env('DISABLE_WP_CRON') ?: false);
101 | // Disable the plugin and theme file editor in the admin
102 | Config::define('DISALLOW_FILE_EDIT', true);
103 | // Disable plugin and theme updates and installation from the admin
104 | Config::define('DISALLOW_FILE_MODS', true);
105 | // Limit the number of post revisions that Wordpress stores (true (default WP): store every revision)
106 | Config::define('WP_POST_REVISIONS', env('WP_POST_REVISIONS') ?: true);
107 |
108 | /**
109 | * Debugging Settings
110 | */
111 | Config::define('WP_DEBUG_DISPLAY', false);
112 | Config::define('WP_DEBUG_LOG', false);
113 | Config::define('SCRIPT_DEBUG', false);
114 | ini_set('display_errors', '0');
115 |
116 | /**
117 | * Allow WordPress to detect HTTPS when used behind a reverse proxy or a load balancer
118 | * See https://codex.wordpress.org/Function_Reference/is_ssl#Notes
119 | */
120 | if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
121 | $_SERVER['HTTPS'] = 'on';
122 | }
123 |
124 | $env_config = __DIR__ . '/environments/' . WP_ENV . '.php';
125 |
126 | if (file_exists($env_config)) {
127 | require_once $env_config;
128 | }
129 |
130 | Config::apply();
131 |
132 | /**
133 | * Bootstrap WordPress
134 | */
135 | if (!defined('ABSPATH')) {
136 | define('ABSPATH', $webroot_dir . '/wp/');
137 | }
138 |
--------------------------------------------------------------------------------
/config/environments/development.php:
--------------------------------------------------------------------------------
1 | cpt_slug = substr( __( 'replace_rewrite_name' ), 0, 20 );
11 | $this->cpt_name = substr( __( 'replace_cpt_name' ), 0, 20 );
12 |
13 | add_action( 'init', array( $this, 'register_custom_cpt' ) );
14 | }
15 |
16 | public function register_custom_cpt() {
17 | register_post_type(
18 | $this->cpt_slug,
19 | array(
20 | 'labels' => array(
21 | 'name' => $this->cpt_name,
22 | 'singular_name' => $this->cpt_name,
23 | ),
24 | 'public' => true,
25 | 'has_archive' => true,
26 | 'rewrite' => array( 'slug' => $this->cpt_slug ),
27 | )
28 | );
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/dev/taxonomy.txt:
--------------------------------------------------------------------------------
1 | taxonomy_slug = 'replace_rewrite_name';
11 | $this->taxonomy_name = 'replace_taxonomy_name';
12 |
13 | add_action( 'init', array( $this, 'register_custom_taxonomy' ) );
14 | }
15 |
16 | public function register_custom_taxonomy() {
17 | $args = array(
18 | 'label' => $this->taxonomy_name,
19 | 'public' => true,
20 | 'rewrite' => false,
21 | 'hierarchical' => true
22 | );
23 |
24 | register_taxonomy( $this->taxonomy_slug, 'selected_post_type', $args );
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/github/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bartosznowak/github",
3 | "authors": [
4 | {
5 | "name": "bartnovak",
6 | "email": "b.nowak@osomstudio.com"
7 | }
8 | ],
9 | "require": {
10 | "squizlabs/php_codesniffer": "*",
11 | "wp-coding-standards/wpcs": "^2.0"
12 | },
13 | "scripts": {
14 | "test": "phpcs --standard=WordPress-Extra --extensions=php ../web/app/themes --exclude=WordPress.Files.FileName -s",
15 | "set" : "phpcs --config-set installed_paths vendor/wp-coding-standards/wpcs"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/start.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | clear
4 |
5 | echo "Checking if you have required packages."
6 | sleep 2
7 |
8 | phpcs_not_installed=$(which phpcs)
9 | if [[ $phpcs_not_installed == "" ]]; then
10 | echo "PHPCS is not installed"
11 | exit
12 | fi
13 |
14 | phpcbf_not_installed=$(which phpcbf)
15 | if [[ $phpcbf_not_installed == "" ]]; then
16 | echo "PHPCBF is not installed"
17 | exit
18 | fi
19 |
20 | standard_not_installed=$(phpcbf -i | grep "WordPress-Extra")
21 | if [[ -z $standard_not_installed ]]; then
22 | echo "WordPress-Extra standard is not installed"
23 | exit
24 | fi
25 |
26 | echo "Checking if it's fresh install of project."
27 | sleep 2
28 |
29 | FILE=composer.json
30 |
31 | if [ ! -f "$FILE" ]; then
32 | echo "No composer.json found."
33 | exit
34 | fi
35 |
36 | echo "I found composer.json. Let's begin downloading required libraries."
37 | sleep 2
38 |
39 | echo "# DATABASE" >> .env;
40 | read -p "Enter db name: " dbname
41 | echo "DB_NAME=$dbname" >> .env;
42 | read -p "Enter db username: " dbuser
43 | echo "DB_USER=$dbuser" >> .env;
44 | read -p "Enter db password: " dbpwd
45 | echo "DB_PASSWORD=$dbpwd" >> .env;
46 | read -e -p "Enter db host (default: localhost): " -i "localhost" dbhost
47 | echo "DB_HOST=$dbhost" >> .env;
48 | read -e -p "Enter db prefix (default: wp_): " -i "wp_" dbprefix
49 | echo "DB_PREFIX=$dbprefix" >> .env;
50 | echo "#/ DATABASE" >> .env;
51 | echo >> .env;
52 | echo >> .env;
53 | echo "# ENV" >> .env;
54 | echo "WP_ENV=development" >> .env
55 | read -p "Enter local domain name (eg. wordpress.local): " domain
56 | echo "WP_HOME=http://$domain" >> .env;
57 | echo 'WP_SITEURL=${WP_HOME}/wp' >> .env;
58 | read -p "Enter ACF PRO KEY: " acfprokey
59 | echo "ACF_PRO_KEY=$acfprokey" >> .env
60 | echo "#/ ENV" >> .env;
61 | echo >> .env;
62 | echo >> .env;
63 |
64 | htaccess="$(cat '.htaccess')"
65 | > '.htaccess'
66 | echo "$htaccess" | sed -E "s/juniper\\.local/${domain}/g" >> '.htaccess'
67 |
68 | echo "# SALTS" >> .env;
69 | wget -qO - https://api.wordpress.org/secret-key/1.1/salt/ \
70 | | tr "'" '"' \
71 | | sed -n -e 's#^define("\([A-Z_]\+\)", \+\("[^"]\+"\));$#\1=\2#p' >> .env
72 | echo "#/ SALTS" >> .env;
73 |
74 | composer install --ignore-platform-reqs
75 |
76 | cd web/app/themes/juniper-theme
77 | npm install
78 |
79 | composer install --ignore-platform-reqs
80 |
81 | echo "That's all. If you set up your DB and htaccess correctly environment should be ready."
82 |
--------------------------------------------------------------------------------
/web/.htaccess:
--------------------------------------------------------------------------------
1 | # BEGIN WordPress
2 | # The directives (lines) between "BEGIN WordPress" and "END WordPress" are
3 | # dynamically generated, and should only be modified via WordPress filters.
4 | # Any changes to the directives between these markers will be overwritten.
5 |
6 | RewriteEngine On
7 | RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
8 | RewriteBase /
9 | RewriteRule ^index\.php$ - [L]
10 | RewriteCond %{REQUEST_FILENAME} !-f
11 | RewriteCond %{REQUEST_FILENAME} !-d
12 | RewriteRule . /index.php [L]
13 |
14 |
15 | # END WordPress
--------------------------------------------------------------------------------
/web/app/languages/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/osomstudio/juniper/0002b3a545cf0ba73ab7f6041224144605e670c4/web/app/languages/.gitkeep
--------------------------------------------------------------------------------
/web/app/mu-plugins/bedrock-autoloader.php:
--------------------------------------------------------------------------------
1 | mu_plugins = dirname( __FILE__ );
19 | }
20 |
21 | private function get_name( $assoc_args ) {
22 | if ( ! key_exists( 'name', $assoc_args ) ) {
23 | WP_CLI::error( 'I need to know the name' );
24 | }
25 |
26 | return $assoc_args['name'];
27 | }
28 |
29 | private function validate_name( $name ) {
30 | if ( strlen( $name ) > 20 ) {
31 | WP_CLI::error( 'The name is too long - Max 20 characters' );
32 | }
33 |
34 | if ( ! preg_match( '/^[A-Za-z0-9-_ ]+$/i', $name ) ) {
35 | WP_CLI::error( 'Name can only have: letters, spaces, dashes, floors' );
36 | }
37 | }
38 |
39 | private function get_needed_names( $name ) {
40 | $lowercase_name = strtolower( $name );
41 |
42 | return array(
43 | 'lowercase_name' => $lowercase_name,
44 | 'slug_name' => str_replace( ' ', '-', $lowercase_name ),
45 | 'rewrite_name' => str_replace( ' ', '-', $lowercase_name ),
46 | );
47 | }
48 |
49 | /**
50 | * Adds a new custom post type.
51 | *
52 | * ## OPTIONS
53 | *
54 | * [--name]
55 | * : The name of custom post type you want
56 | *
57 | * ## EXAMPLES
58 | *
59 | * wp add cpt --name="Products"
60 | *
61 | * @when before_wp_load
62 | */
63 | public function cpt( $args, $assoc_args ) {
64 | $og_name = $this->get_name( $assoc_args );
65 |
66 | $this->validate_name( $og_name );
67 |
68 | extract( $this->get_needed_names( $og_name ) ); // phpcs:ignore
69 |
70 | $class_name = ucfirst( $slug_name );
71 |
72 | if ( file_exists( $this->mu_plugins . "/../themes/juniper-theme/inc/Cpt/$class_name.php" ) ) {
73 | WP_CLI::error( 'Custom post type already exists' );
74 | }
75 |
76 | $replace_array = array(
77 | array( 'replace_cpt_slug', $class_name ),
78 | array( 'replace_cpt_name', $og_name ),
79 | array( 'replace_rewrite_name', $rewrite_name ),
80 | );
81 |
82 | $file_contents = file_get_contents( $this->mu_plugins . '/../../../dev/cpt.txt' ); // phpcs:ignore
83 | foreach ( $replace_array as $search_replace ) {
84 | $file_contents = str_replace( $search_replace[0], $search_replace[1], $file_contents );
85 | }
86 |
87 | file_put_contents( $this->mu_plugins . "/../themes/juniper-theme/inc/Cpt/$class_name.php", $file_contents );
88 |
89 | $new_class = "\$juniper_$slug_name = new \Juniper\Cpt\\$class_name();" . PHP_EOL;
90 | file_put_contents( $this->mu_plugins . '/../themes/juniper-theme/inc/include.php', $new_class, FILE_APPEND );
91 |
92 | shell_exec( 'phpcbf --standard=WordPress-Extra ' . $this->mu_plugins . "/../themes/juniper-theme/inc/Cpt/$slug_name.php" );
93 | shell_exec( 'phpcbf --standard=WordPress-Extra ' . $this->mu_plugins . '/../themes/juniper-theme/inc/include.php' );
94 | }
95 |
96 | /**
97 | * Adds a new taxonomy and attaches it to a post type.
98 | *
99 | * ## OPTIONS
100 | *
101 | * [--name]
102 | * : The name of custom taxonomy you want to add
103 | *
104 | * [--post]
105 | * : The name of the custom post type that this taxonomy should be attached to
106 | *
107 | * ## EXAMPLES
108 | *
109 | * wp add taxonomy --name="Categories" --post="products"
110 | *
111 | * @when before_wp_load
112 | */
113 | public function taxonomy( $args, $assoc_args ) {
114 | $og_name = $this->get_name( $assoc_args );
115 |
116 | if ( ! key_exists( 'post', $assoc_args ) ) {
117 | WP_CLI::error( 'I need to know the name of the custom post type' );
118 | }
119 |
120 | $post_cpt = $assoc_args['post'];
121 |
122 | $this->validate_name( $og_name );
123 | $this->validate_name( $post_cpt );
124 |
125 | extract( $this->get_needed_names( $og_name ) );
126 |
127 | if ( file_exists( $this->mu_plugins . "/../themes/juniper-theme/inc/Taxonomies/$slug_name.php" ) ) {
128 | WP_CLI::error( 'Taxonomy already exists' );
129 | }
130 |
131 | $replace_array = array(
132 | array( 'replace_taxonomy_slug', $slug_name ),
133 | array( 'replace_taxonomy_name', $og_name ),
134 | array( 'replace_rewrite_name', $rewrite_name ),
135 | array( 'selected_post_type', $post_cpt ),
136 | );
137 |
138 | $file_contents = file_get_contents( $this->mu_plugins . '/../../../dev/taxonomy.txt' );
139 | foreach ( $replace_array as $search_replace ) {
140 | $file_contents = str_replace( $search_replace[0], $search_replace[1], $file_contents );
141 | }
142 |
143 | file_put_contents( $this->mu_plugins . "/../themes/juniper-theme/inc/Taxonomies/$slug_name.php", $file_contents );
144 |
145 | $new_class = "\$juniper_$slug_name = new \Juniper\Taxonomies\\$slug_name();" . PHP_EOL;
146 | file_put_contents( $this->mu_plugins . '/../themes/juniper-theme/inc/include.php', $new_class, FILE_APPEND );
147 |
148 | shell_exec( 'phpcbf --standard=WordPress-Extra ' . $this->mu_plugins . '/../themes/juniper-theme/inc/include.php' );
149 | }
150 |
151 |
152 | /**
153 | * Adds a Gutenberg block.
154 | *
155 | * ## OPTIONS
156 | *
157 | * [--name]
158 | * : The name of the new Gutenberg block
159 | *
160 | * [--keywords]
161 | * : Optional keywords of the Gutenberg block
162 | *
163 | * [--description]
164 | * : Optional description of the Gutenberg block
165 | *
166 | * ## EXAMPLES
167 | * wp add block --name="Reviews"
168 | * wp add block --name="Reviews" --keywords="people,stars,quotes"
169 | * wp add block --name="Reviews" --keywords="people,stars,quotes" --description="This section shows the three newest reviews"
170 | *
171 | * @when before_wp_load
172 | */
173 | public function block( $args, $assoc_args ) {
174 | $og_name = $this->get_name( $assoc_args );
175 |
176 | if ( ! preg_match( '/^[A-Za-z0-9-_ ]+$/i', $og_name ) ) {
177 | WP_CLI::error( 'Name can only have: letters, spaces, dashes, floors' );
178 | }
179 |
180 | $lowercase_name = strtolower( $og_name );
181 | $slug_name = str_replace( ' ', '-', $lowercase_name );
182 |
183 | if ( file_exists( "../themes/juniper-theme/blocks/$slug_name/" ) ) {
184 | WP_CLI::error( 'Block already exists' );
185 | }
186 |
187 | mkdir( $this->mu_plugins . "/../themes/juniper-theme/blocks/$slug_name/", 0755 );
188 |
189 | $keywords = '';
190 | if ( key_exists( 'keywords', $assoc_args ) ) {
191 | $keywords = $assoc_args['keywords'];
192 | }
193 |
194 | $description = '';
195 | if ( key_exists( 'description', $assoc_args ) ) {
196 | $description = $assoc_args['description'];
197 | }
198 |
199 | file_put_contents( $this->mu_plugins . "/../themes/juniper-theme/blocks/$slug_name/script.js", '' );
200 | file_put_contents( $this->mu_plugins . "/../themes/juniper-theme/blocks/$slug_name/ajax.js", '' );
201 |
202 | $css = ".$slug_name {}\n\n" .
203 | "body.wp-admin {\n" .
204 | "\t.$slug_name {}\n" .
205 | '}';
206 | file_put_contents( $this->mu_plugins . "/../themes/juniper-theme/blocks/$slug_name/style.scss", $css );
207 |
208 | $php = "mu_plugins . "/../themes/juniper-theme/blocks/$slug_name/functions.php", $php );
226 |
227 | $html = "{#\n" .
228 | "\tTitle: $og_name\n" .
229 | "\tDescription: $description\n" .
230 | "\tCategory: formatting\n" .
231 | "\tIcon: admin-comments\n" .
232 | "\tKeywords: $keywords\n" .
233 | "\tMode: edit\n" .
234 | "\tAlign: full\n" .
235 | "\tSupportsAlign: left right full\n" .
236 | "\tSupportsMode: true\n" .
237 | "\tSupportsMultiple: true\n" .
238 | '#}';
239 | file_put_contents( $this->mu_plugins . "/../themes/juniper-theme/views/blocks/$slug_name.twig", $html );
240 |
241 | shell_exec( 'phpcbf -d error_reporting="E_ALL&~E_DEPRECATED" --standard="WordPress-Extra" ' . $this->mu_plugins . "/../themes/juniper-theme/Blocks/$slug_name/functions.php" );
242 | }
243 | }
244 |
245 | WP_CLI::add_command( 'add', 'Juniper_Commands' );
246 | }
247 | }
248 |
--------------------------------------------------------------------------------
/web/app/mu-plugins/register-theme-directory.php:
--------------------------------------------------------------------------------
1 | ` block.
109 | Template parts in Gutenberg can be registered in the theme.json file, allowing you to define reusable sections such as headers, footers, or sidebars. This helps structure the theme and makes it easier to manage global components.
110 | ```json
111 | {
112 | "templateParts": [
113 | {
114 | "area": "footer",
115 | "name": "footer",
116 | "title": "Footer"
117 | },
118 | {
119 | "area": "header",
120 | "name": "header",
121 | "title": "Header"
122 | },
123 | {
124 | "area": "header",
125 | "name": "top-bar",
126 | "title": "Top bar"
127 | }
128 | ]
129 | }
130 | ```
131 |
132 | ## Block patterns
133 | Block patterns are predefined layouts of blocks that can be inserted into the editor with a single click. They are stored in the patterns directory.
134 | You can see an exmaple in the `juniper-theme` in the `patterns` directory in `cta.php` file.
135 |
136 | ### Pattern categories
137 | Example:
138 | ```php
139 | register_block_pattern_category(
140 | 'cta',
141 | array( 'label' => __( 'CTA', 'juniper-theme' ) )
142 | );
143 | ```
144 |
145 | ## Block styles
146 | Block styles are predefined styles for blocks that can be applied with a single click. An example of block style registration:
147 | ```php
148 | add_action( 'init', 'juniper_register_blocks_styles' );
149 | function juniper_register_blocks_styles() : void {
150 | register_block_style(
151 | 'core/button',
152 | array(
153 | 'name' => 'arrowed',
154 | 'label' => __( 'Arrowed', 'juniper' ),
155 | )
156 | );
157 | }
158 | ```
159 |
160 | ## ACF Blocks
161 | ACF Blocks are good for creating template parts, such us Header, Navigation, Footer and other reusable blocks.
162 | Example of adding ACF block:
163 | ``
164 |
165 | ## Development workflow
166 | 1. Start from configuration in `theme.json` file.
167 | 2. Register block styles and pattern categories.
168 | 3. Create Design System template in the `templates` directory.
169 | 4. Add blocks to the Design System template.
170 | 5. Create template parts in the `parts` directory.
171 | 6. Start creating the rest blocks with use of Gutenberg and ACF.
172 | 7. Register created blocks in the `patterns` directory.
173 |
174 | Source: https://fullsiteediting.com/
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/acf-json/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/osomstudio/juniper/0002b3a545cf0ba73ab7f6041224144605e670c4/web/app/themes/juniper-theme/acf-json/.gitkeep
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/blocks/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/osomstudio/juniper/0002b3a545cf0ba73ab7f6041224144605e670c4/web/app/themes/juniper-theme/blocks/.gitkeep
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/blocks/cta/ajax.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/osomstudio/juniper/0002b3a545cf0ba73ab7f6041224144605e670c4/web/app/themes/juniper-theme/blocks/cta/ajax.js
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/blocks/cta/functions.php:
--------------------------------------------------------------------------------
1 | {
3 | event.preventDefault();
4 |
5 | const ajax_data = {
6 | action: 'filteringposts',
7 | nonce: ajax.nonce,
8 | // custom fields below.
9 | inputajax1: jQuery('input[name="inputajax1"]').val(),
10 | inputajax2: jQuery('input[name="inputajax2"]').val(),
11 | inputajax3: jQuery('input[name="inputajax3"]').val(),
12 | };
13 |
14 | jQuery.ajax(
15 | {
16 | type: 'POST',
17 | url: ajax.ajax_url,
18 | data: ajax_data,
19 | success(data) {
20 | console.log(data);
21 | },
22 | error(xhr, status, errorThrown) {
23 | console.log('Custom ajax error');
24 | },
25 | },
26 | );
27 | },
28 | );
29 |
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/blocks/filteringposts/functions.php:
--------------------------------------------------------------------------------
1 | tag in the document head, and expect WordPress to
34 | * provide it for us.
35 | */
36 | add_theme_support( 'title-tag' );
37 |
38 | /*
39 | * Enable support for Post Thumbnails on posts and pages.
40 | *
41 | * @link https://developer.wordpress.org/themes/functionality/featured-images-post-thumbnails/
42 | */
43 | add_theme_support( 'post-thumbnails' );
44 |
45 | /*
46 | * Switch default core markup for search form, comment form, and comments
47 | * to output valid HTML5.
48 | */
49 | add_theme_support(
50 | 'html5',
51 | array(
52 | 'comment-form',
53 | 'comment-list',
54 | 'gallery',
55 | 'caption',
56 | )
57 | );
58 |
59 | /*
60 | * Enable support for Post Formats.
61 | *
62 | * See: https://codex.wordpress.org/Post_Formats
63 | */
64 | add_theme_support(
65 | 'post-formats',
66 | array(
67 | 'aside',
68 | 'image',
69 | 'video',
70 | 'quote',
71 | 'link',
72 | 'gallery',
73 | 'audio',
74 | )
75 | );
76 |
77 | add_theme_support( 'menus' );
78 | }
79 |
80 | /** This Would return 'foo bar!'.
81 | *
82 | * @param string $text being 'foo', then returned 'foo bar!'.
83 | */
84 | public function myfoo( $text ) {
85 | $text .= ' bar!';
86 | return $text;
87 | }
88 |
89 | /** This is where you can add your own functions to twig.
90 | *
91 | * @param string $twig get extension.
92 | */
93 | public function add_to_twig( $twig ) {
94 | $twig->addExtension( new Twig\Extension\StringLoaderExtension() );
95 | $twig->addFilter( new Twig\TwigFilter( 'myfoo', array( $this, 'myfoo' ) ) );
96 | return $twig;
97 | }
98 |
99 | }
100 |
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "junipertheme/junipertheme",
3 | "type": "library",
4 | "authors": [
5 | {
6 | "name": "bartnovak",
7 | "email": "b.nowak@osomstudio.com"
8 | }
9 | ],
10 | "autoload": {
11 | "psr-4": {
12 | "Juniper\\": "inc"
13 | }
14 | },
15 | "require": {
16 | "php": ">=8.1",
17 | "timber/timber": "^2.0",
18 | "bartnovak/timber-acf-wp-blocks": "^0.1.0"
19 | },
20 | "config": {
21 | "optimize-autoloader": true,
22 | "preferred-install": "dist",
23 | "allow-plugins": {
24 | "composer/installers": true
25 | },
26 | "sort-packages": true,
27 | "platform": {
28 | "php": "8.1"
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/cypress.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require("cypress");
2 |
3 | module.exports = defineConfig({
4 | e2e: {
5 | setupNodeEvents(on, config) {
6 | // implement node event listeners here
7 | },
8 | },
9 | });
10 |
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/cypress/e2e/blockRestAPI.cy.js:
--------------------------------------------------------------------------------
1 | describe('BlockRestAPI', () => {
2 | it('passes', () => {
3 | var homeURL = '';
4 | var request = '/wp-json/wp/v2/';
5 |
6 | if ( homeURL === '' ) {
7 | alert('Fill homeURL in this test .cy.js file.');
8 | }
9 |
10 | cy.request({
11 | method: 'GET',
12 | url: homeURL + request,
13 | body: {name: "test"},
14 | failOnStatusCode: false
15 | })
16 | .then(response => {
17 | expect(response.status).to.equal(401)
18 | })
19 |
20 | })
21 | })
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/cypress/fixtures/example.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Using fixtures to represent data",
3 | "email": "hello@cypress.io",
4 | "body": "Fixtures are a great way to mock data for responses to routes"
5 | }
6 |
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/cypress/support/commands.js:
--------------------------------------------------------------------------------
1 | // ***********************************************
2 | // This example commands.js shows you how to
3 | // create various custom commands and overwrite
4 | // existing commands.
5 | //
6 | // For more comprehensive examples of custom
7 | // commands please read more here:
8 | // https://on.cypress.io/custom-commands
9 | // ***********************************************
10 | //
11 | //
12 | // -- This is a parent command --
13 | // Cypress.Commands.add('login', (email, password) => { ... })
14 | //
15 | //
16 | // -- This is a child command --
17 | // Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
18 | //
19 | //
20 | // -- This is a dual command --
21 | // Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
22 | //
23 | //
24 | // -- This will overwrite an existing command --
25 | // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/cypress/support/e2e.js:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example support/e2e.js is processed and
3 | // loaded automatically before your test files.
4 | //
5 | // This is a great place to put global configuration and
6 | // behavior that modifies Cypress.
7 | //
8 | // You can change the location of this file or turn off
9 | // automatically serving support files with the
10 | // 'supportFile' configuration option.
11 | //
12 | // You can read more here:
13 | // https://on.cypress.io/configuration
14 | // ***********************************************************
15 |
16 | // Import commands.js using ES2015 syntax:
17 | import './commands'
18 |
19 | // Alternatively you can use CommonJS syntax:
20 | // require('./commands')
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/functions.php:
--------------------------------------------------------------------------------
1 | Timber not activated. Make sure you activate the plugin in ' . esc_url( admin_url( 'plugins.php' ) ) . '
';
51 | }
52 | );
53 |
54 | add_filter(
55 | 'template_include',
56 | function ( $template ) {
57 | return get_stylesheet_directory() . '/static/no-timber.html';
58 | }
59 | );
60 | return;
61 | }
62 |
63 | /**
64 | * Sets the directories (inside your theme) to find .twig files
65 | */
66 | Timber::$dirname = array( 'templates', 'views' );
67 |
68 | /**
69 | * By default, Timber does NOT autoescape values. Want to enable Twig's autoescape?
70 | * No prob! Just set this value to true
71 | */
72 | Timber::$autoescape = false;
73 |
74 |
75 | //StarterSite class
76 | require_once 'class-startersite.php';
77 | new StarterSite();
78 |
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/inc/Ajax/JuniperAjaxFilteringposts.php:
--------------------------------------------------------------------------------
1 | self::TEXT_FIELD,
34 | 'inputajax2' => self::TEXT_FIELD,
35 | 'inputajax3' => self::TEXT_FIELD,
36 | );
37 |
38 | /**
39 | * Array with input values after sanitization
40 | *
41 | * @var array
42 | */
43 | private array $ajax_fields_sanitized = array();
44 |
45 | /**
46 | * Ajax class constructor. Basic properties setting, ajax necessary actions registered, enqueue ajax script with localize.
47 | *
48 | * @param string $action_name Ajax action name.
49 | * @param string $js_directory string Directory with js file to be used with this ajax module.
50 | * @param string $block_name Block name
51 | */
52 | public function __construct( $action_name, $js_directory, $block_name ) {
53 | $this->action_name = $action_name;
54 | $this->js_directory = $js_directory;
55 | $this->block_name = $block_name;
56 |
57 | add_action( 'wp_ajax_' . $this->action_name, array( $this, 'ajax_action_handler' ) );
58 | add_action( 'wp_ajax_nopriv_' . $this->action_name, array( $this, 'ajax_action_handler' ) );
59 | add_action( 'wp_enqueue_scripts', array( $this, 'register_script' ) );
60 | }
61 |
62 | /**
63 | * Main ajax handler method. Do not make spaghetti code - if you want to parse data create separate method.
64 | *
65 | * @return void wp_send_json always ends with wp_die
66 | */
67 | public function ajax_action_handler() {
68 | // We are setting up input names and their type in $ajax_fields_types first to sanitize them automatically.
69 | $this->parse_ajax_fields();
70 |
71 | // Custom methods to parse data.
72 |
73 | /**
74 | * Method needs to end with data sent to JS. If error occurs during the processing of
75 | * method, we need to send $this->send_ajax_error().
76 | */
77 | $this->send_ajax_success( $this->ajax_fields_sanitized );
78 | }
79 |
80 | /**
81 | * Methods used to register necessary scripts and localize them.
82 | *
83 | * @return void
84 | */
85 | public function register_script() {
86 | if ( has_block( 'acf/' . $this->block_name ) ) {
87 | wp_enqueue_script( 'ajax-' . $this->action_name, $this->js_directory, array( 'jquery' ), time(), true );
88 | wp_localize_script(
89 | 'ajax-' . $this->action_name,
90 | 'ajax',
91 | array(
92 | 'ajax_url' => admin_url( 'admin-ajax.php' ),
93 | 'action_name' => $this->action_name,
94 | 'nonce' => wp_create_nonce( $this->action_name ),
95 | )
96 | );
97 | }
98 | }
99 |
100 | /**
101 | * Validation method for fields used in ajax request - $this->ajax_fields_sanitized
102 | *
103 | *
104 | * @return void
105 | */
106 | private function parse_ajax_fields() {
107 | if ( ! check_ajax_referer( $this->action_name, 'nonce' ) ) {
108 | $this->send_ajax_error( __( 'Unauthorized request. Try again later.' ) );
109 | }
110 |
111 | foreach ( $this->ajax_fields_types as $field_name => $sanitization_type ) {
112 | if ( ! isset( $_POST[ $field_name ] ) ) {
113 | continue;
114 | }
115 |
116 | switch ( $sanitization_type ) {
117 | case self::TEXT_FIELD:
118 | $this->ajax_fields_sanitized[ $field_name ] = sanitize_text_field( wp_unslash( $_POST[ $field_name ] ) );
119 | break;
120 | case self::TEXTAREA_FIELD:
121 | $this->ajax_fields_sanitized[ $field_name ] = sanitize_textarea_field( wp_unslash( $_POST[ $field_name ] ) );
122 | break;
123 | case self::EMAIL_FIELD:
124 | $this->ajax_fields_sanitized[ $field_name ] = sanitize_email( wp_unslash( $_POST[ $field_name ] ) );
125 | break;
126 | default:
127 | $this->ajax_fields_sanitized[ $field_name ] = sanitize_text_field( wp_unslash( $_POST[ $field_name ] ) );
128 | }
129 | }
130 | }
131 |
132 | /**
133 | * Universal method for returning request error data.
134 | *
135 | * @param string $error_msg Error message, remember to use __() function.
136 | * @return void wp_send_json ends with wp_die.
137 | */
138 | private function send_ajax_error( string $error_msg ) {
139 | wp_send_json(
140 | array(
141 | 'result' => false,
142 | 'msg' => $error_msg,
143 | )
144 | );
145 | }
146 |
147 | /**
148 | * @param array $data Array with response data.
149 | * @return void wp_send_json ends with wp_die.
150 | */
151 | private function send_ajax_success( array $data ) {
152 | wp_send_json(
153 | array(
154 | 'result' => true,
155 | 'data' => $data,
156 | )
157 | );
158 | }
159 |
160 | }
161 |
162 |
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/inc/Blocks/JuniperBlocks.php:
--------------------------------------------------------------------------------
1 | blocks_dir = get_template_directory() . '/blocks';
11 | $this->blocks_list = $this->get_all_blocks();
12 |
13 | }
14 |
15 | private function get_all_blocks() {
16 | return array_values( array_diff( scandir( $this->blocks_dir ), array( '..', '.', '.gitkeep' ) ) );
17 | }
18 |
19 | public function include_blocks_functions() {
20 | foreach ( $this->blocks_list as $single_block ) {
21 | require_once get_template_directory() . '/blocks/' . $single_block . '/functions.php';
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/inc/Cpt/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/osomstudio/juniper/0002b3a545cf0ba73ab7f6041224144605e670c4/web/app/themes/juniper-theme/inc/Cpt/.gitkeep
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/inc/Taxonomies/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/osomstudio/juniper/0002b3a545cf0ba73ab7f6041224144605e670c4/web/app/themes/juniper-theme/inc/Taxonomies/.gitkeep
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/inc/gutenberg-styles.php:
--------------------------------------------------------------------------------
1 | 'arrowed',
8 | 'label' => __( 'Arrowed', 'juniper' ),
9 | )
10 | );
11 | }
12 |
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/inc/include.php:
--------------------------------------------------------------------------------
1 | include_blocks_functions();
8 |
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/inc/pattern-categories.php:
--------------------------------------------------------------------------------
1 | __( 'CTA', 'juniper-theme' ) )
11 | );
12 |
13 |
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "exclude": [
3 | "**/node_modules",
4 | "wp-admin",
5 | "wp-includes"
6 | ]
7 | }
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "parcel-juniper-theme",
3 | "version": "1.0.0",
4 | "dependencies": {
5 | "@barba/core": "^2.9",
6 | "@barba/prefetch": "^2.1",
7 | "bootstrap": "^5.3",
8 | "cypress": "^13.9",
9 | "gsap": "^3.12.5",
10 | "imagesloaded": "^5.0",
11 | "jquery": "^3.7",
12 | "postcss": "^8.4"
13 | },
14 | "devDependencies": {
15 | "@parcel/transformer-sass": "^2.12",
16 | "browserslist": "^4.23",
17 | "eslint": "^8.56",
18 | "eslint-config-airbnb-base": "^15.0",
19 | "husky": "^9.0",
20 | "litepicker": "^2.0",
21 | "parcel": "^2.12",
22 | "sass": "^1.77"
23 | },
24 | "scripts": {
25 | "test": "echo \"Error: no test specified\" && exit 1",
26 | "dev": "parcel watch src/css/_app.scss src/js/_app.js blocks/**/*.js blocks/**/*.scss --public-url ./ --no-source-maps",
27 | "build": "parcel build src/js/_app.js src/css/_app.scss blocks/**/*.js blocks/**/*.scss --public-url ./",
28 | "cypress": "./node_modules/.bin/cypress open",
29 | "flint": "eslint --fix ./src/js/**/*",
30 | "pretest": "eslint --ignore-path .gitignore ./src/js/**/* --fix && eslint --ignore-path .gitignore ./blocks/**/*.js --fix"
31 | },
32 | "husky": {
33 | "hooks": {
34 | "pre-commit": "npm run pretest"
35 | }
36 | },
37 | "author": "osom",
38 | "license": "ISC",
39 | "browserslist": [
40 | "last 2 versions"
41 | ]
42 | }
43 |
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/parts/footer.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
parts/footer.html
6 |
7 |
8 |
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/parts/header.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
parts/header.html
5 |
6 |
7 |
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/parts/top-bar.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Test
4 |
5 |
6 |
7 |
Paragraph
8 |
9 |
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/patterns/cta.php:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
Testing 123123
14 |
15 |
16 |
17 |
Paragraph
18 |
19 |
20 |
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/phpunit.xml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 | ./tests/
12 |
13 |
16 |
17 | ./tests/
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/osomstudio/juniper/0002b3a545cf0ba73ab7f6041224144605e670c4/web/app/themes/juniper-theme/screenshot.png
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/src/css/_app.scss:
--------------------------------------------------------------------------------
1 | @use 'base';
2 | @use 'vendor/swiper';
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/src/css/base/_common.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/osomstudio/juniper/0002b3a545cf0ba73ab7f6041224144605e670c4/web/app/themes/juniper-theme/src/css/base/_common.scss
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/src/css/base/_index.scss:
--------------------------------------------------------------------------------
1 | @forward 'reset';
2 | @forward 'variables';
3 | @forward 'mixins';
4 | @forward 'typography';
5 | @forward 'common';
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/src/css/base/_mixins.scss:
--------------------------------------------------------------------------------
1 | @mixin grid {
2 | margin: 0 auto;
3 | width: 90%;
4 | position: relative;
5 |
6 | @include media($gridWidth + 100, min) {
7 | width: $gridWidth;
8 | }
9 | }
10 |
11 | /*
12 | * Centers element by type
13 | */
14 | @mixin center($type: flex) {
15 | @if $type=='flex' {
16 | display: flex;
17 | align-items: center;
18 | justify-content: center;
19 | }
20 |
21 | @else {
22 | position: absolute;
23 | top: 50%;
24 | left: 50%;
25 | transform: translate(-50%, -50%);
26 | }
27 | }
28 |
29 |
30 | /*
31 | * Sets element media query
32 | */
33 | @mixin media($breakpoint, $type: max) {
34 | @media screen and (#{$type}-width: #{$breakpoint}) {
35 | @content;
36 | }
37 | }
38 |
39 | /*
40 | * Flexbox row system
41 | */
42 | @mixin flex($align:'start', $space:'start') {
43 | display: flex;
44 | flex-wrap: wrap;
45 |
46 | @if $align=='center' {
47 | align-items: center;
48 | }
49 |
50 | @if $align=='start' {
51 | align-items: flex-start;
52 | }
53 |
54 | @if $align=='end' {
55 | align-items: flex-end;
56 | }
57 |
58 | @if $align=='strech' {
59 | align-items: stretch;
60 | }
61 |
62 | @if $space=='center' {
63 | justify-content: center;
64 | }
65 |
66 | @if $space=='space' {
67 | justify-content: space-between;
68 | }
69 |
70 | @if $space=='end' {
71 | justify-content: flex-end;
72 | }
73 | }
74 |
75 | /*
76 | * Column width system
77 | */
78 | @mixin col($items) {
79 | @include media($mobile, min) {
80 | width: calc(8.3333333% * #{$items});
81 | }
82 | }
83 |
84 | /*
85 | * Styles input placeholder
86 | * This is getting prefixed by gulp
87 | */
88 | @mixin input-placeholder {
89 | &::placeholder {
90 | @content;
91 | }
92 | }
93 |
94 | /*
95 | * Fills relative parent width and height
96 | */
97 | @mixin cover {
98 | position: absolute;
99 | top: 0;
100 | left: 0;
101 | width: 100%;
102 | height: 100%;
103 | display: block;
104 | }
105 |
106 | /*
107 | * Includes pseudo element if param is true
108 | * Wrapper for @cover
109 | */
110 | @mixin fw {
111 | position: relative;
112 |
113 | &::after {
114 | content: '';
115 | @include cover();
116 | }
117 | }
118 |
119 |
120 | /*
121 | * Sets item width and height
122 | */
123 | @mixin box($width, $height:$width, $display:inline-flex) {
124 | width: $width;
125 | height: $height;
126 | display: $display;
127 | }
128 |
129 | /*
130 | * Includes animation with params
131 | */
132 | @mixin keyframes($name, $params) {
133 | animation: #{$name} #{$params};
134 |
135 | @keyframes #{$name} {
136 | @content;
137 | }
138 | }
139 |
140 | /*
141 | * Keeps item aspect ratio
142 | * Item height is based on padding-top
143 | */
144 | @mixin ratio($x, $y) {
145 | $padding: unquote(($y / $x) * 100 + '%');
146 | padding-top: $padding;
147 | }
148 |
149 | /*
150 | * Px to vw calculator
151 | * Remember to define your laytout width in _app.scss
152 | */
153 | @function vw($px) {
154 | $vw: ($projectWidth * 0.01) * 1px;
155 | @return ($px / $vw) * 1vw;
156 | }
157 |
158 | @mixin fixed {
159 | position: fixed;
160 | top: 0;
161 | bottom: 0;
162 | right: 0;
163 | left: 0;
164 | display: flex;
165 | justify-content: center;
166 | align-items: center;
167 | }
168 |
169 | @mixin pushup($i:2, $pos:relative) {
170 | z-index: $i;
171 | position: $pos;
172 | }
173 |
174 | @function randomNum($min, $max) {
175 | $rand: random();
176 | $randomNum: $min + floor($rand * (($max - $min) + 1));
177 |
178 | @return $randomNum;
179 | }
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/src/css/base/_reset.scss:
--------------------------------------------------------------------------------
1 | html {
2 | box-sizing: border-box;
3 | vertical-align: baseline;
4 | scroll-behavior: smooth;
5 | }
6 |
7 | *,
8 | *:before,
9 | *:after {
10 | margin: 0;
11 | padding: 0;
12 | box-sizing: inherit;
13 | }
14 |
15 | img,
16 | body,
17 | article,
18 | main,
19 | aside,
20 | address,
21 | details,
22 | figcaption,
23 | figure,
24 | footer,
25 | header,
26 | nav {
27 | display: block;
28 | }
29 |
30 | img {
31 | max-width: 100%;
32 | }
33 |
34 | ol,
35 | ul {
36 | list-style: none
37 | }
38 |
39 | li:empty,
40 | p:empty {
41 | display: none;
42 | }
43 |
44 | textarea,
45 | select,
46 | input,
47 | button {
48 | -webkit-appearance: none;
49 | -moz-appearance: none;
50 | appearance: none;
51 | color: inherit;
52 | background: transparent;
53 | border: none;
54 | }
55 |
56 | strong {
57 | font-weight: bold;
58 | }
59 |
60 | a {
61 | text-decoration: none;
62 | color: inherit;
63 |
64 | &:hover {
65 | text-decoration: none;
66 | }
67 | }
68 |
69 | :focus,
70 | :active {
71 | outline: none;
72 | }
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/src/css/base/_typography.scss:
--------------------------------------------------------------------------------
1 | html {
2 | line-height: 1.15;
3 | -webkit-font-smoothing: antialiased;
4 | -webkit-text-size-adjust: 100%;
5 | font-size: 1rem;
6 | }
7 |
8 | body {
9 | font-family: myriad-pro, myriad-pro-condensed, myriad-pro-semiextended, myriad-pro-semi-condensed, Roboto, -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica Neue, Arial, Noto Sans, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;
10 | font-weight: inherit;
11 | }
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/src/css/base/_variables.scss:
--------------------------------------------------------------------------------
1 | /* Assign variables with values from theme.json */
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/src/css/components/_buttons.scss:
--------------------------------------------------------------------------------
1 | a,
2 | button,
3 | .button {
4 | transition: 300;
5 | cursor: pointer;
6 | }
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/src/css/components/_forms.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/osomstudio/juniper/0002b3a545cf0ba73ab7f6041224144605e670c4/web/app/themes/juniper-theme/src/css/components/_forms.scss
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/src/css/vendor/swiper.scss:
--------------------------------------------------------------------------------
1 | :root {
2 | --swiper-theme-color: #333;
3 | }
4 |
5 | .swiper-container {
6 | margin-left: auto;
7 | margin-right: auto;
8 | position: relative;
9 | overflow: hidden;
10 | list-style: none;
11 | padding: 0;
12 | /* Fix of Webkit flickering */
13 | z-index: 1;
14 | width: 100%;
15 | }
16 |
17 | .swiper-container-vertical>.swiper-wrapper {
18 | flex-direction: column;
19 | }
20 |
21 | .swiper-wrapper {
22 | position: relative;
23 | width: 100%;
24 | height: 100%;
25 | z-index: 1;
26 | display: flex;
27 | transition-property: transform;
28 | box-sizing: content-box;
29 | }
30 |
31 | .swiper-container-android .swiper-slide,
32 | .swiper-wrapper {
33 | transform: translate3d(0px, 0, 0);
34 | }
35 |
36 | .swiper-container-multirow>.swiper-wrapper {
37 | flex-wrap: wrap;
38 | }
39 |
40 | .swiper-container-multirow-column>.swiper-wrapper {
41 | flex-wrap: wrap;
42 | flex-direction: column;
43 | }
44 |
45 | .swiper-container-free-mode>.swiper-wrapper {
46 | transition-timing-function: ease-out;
47 | margin: 0 auto;
48 | }
49 |
50 | .swiper-slide {
51 | flex-shrink: 0;
52 | width: 100%;
53 | height: 100%;
54 | position: relative;
55 | transition-property: transform;
56 | }
57 |
58 | .swiper-slide-invisible-blank {
59 | visibility: hidden;
60 | }
61 |
62 | /* Auto Height */
63 | .swiper-container-autoheight {
64 |
65 | &,
66 | .swiper-slide {
67 | height: auto;
68 | }
69 |
70 | .swiper-wrapper {
71 | align-items: flex-start;
72 | transition-property: transform, height;
73 | }
74 | }
75 |
76 | /* 3D Effects */
77 | .swiper-container-3d {
78 | perspective: 1200px;
79 |
80 | .swiper-wrapper,
81 | .swiper-slide,
82 | .swiper-slide-shadow-left,
83 | .swiper-slide-shadow-right,
84 | .swiper-slide-shadow-top,
85 | .swiper-slide-shadow-bottom,
86 | .swiper-cube-shadow {
87 | transform-style: preserve-3d;
88 | }
89 |
90 | .swiper-slide-shadow-left,
91 | .swiper-slide-shadow-right,
92 | .swiper-slide-shadow-top,
93 | .swiper-slide-shadow-bottom {
94 | position: absolute;
95 | left: 0;
96 | top: 0;
97 | width: 100%;
98 | height: 100%;
99 | pointer-events: none;
100 | z-index: 10;
101 | }
102 |
103 | .swiper-slide-shadow-left {
104 | background-image: linear-gradient(to left, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
105 | }
106 |
107 | .swiper-slide-shadow-right {
108 | background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
109 | }
110 |
111 | .swiper-slide-shadow-top {
112 | background-image: linear-gradient(to top, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
113 | }
114 |
115 | .swiper-slide-shadow-bottom {
116 | background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
117 | }
118 | }
119 |
120 | /* CSS Mode */
121 | .swiper-container-css-mode {
122 | >.swiper-wrapper {
123 | overflow: auto;
124 | scrollbar-width: none;
125 | /* For Firefox */
126 | -ms-overflow-style: none;
127 |
128 | /* For Internet Explorer and Edge */
129 | &::-webkit-scrollbar {
130 | display: none;
131 | }
132 | }
133 |
134 | >.swiper-wrapper>.swiper-slide {
135 | scroll-snap-align: start start;
136 | }
137 | }
138 |
139 | .swiper-container-horizontal.swiper-container-css-mode {
140 | >.swiper-wrapper {
141 | scroll-snap-type: x mandatory;
142 | }
143 | }
144 |
145 | .swiper-container-vertical.swiper-container-css-mode {
146 | >.swiper-wrapper {
147 | scroll-snap-type: y mandatory;
148 | }
149 | }
150 |
151 | .swiper-button {
152 | height: 10px;
153 |
154 | &-next,
155 | &-prev {
156 | width: 30px;
157 | height: 30px;
158 | background: #ccc;
159 | position: absolute;
160 | top: 50%;
161 | transform: translateY(-50%);
162 | z-index: 2;
163 | cursor: pointer;
164 | }
165 |
166 | &-prev {
167 | left: 5%;
168 | }
169 |
170 | &-next {
171 | right: 5%;
172 | }
173 | }
174 |
175 | .swiper-pagination {
176 | position: absolute;
177 | bottom: 20px;
178 | left: 20px;
179 | z-index: 2;
180 | display: flex;
181 |
182 | &-bullet {
183 | display: block;
184 | width: 10px;
185 | height: 10px;
186 | background: #ccc;
187 | margin-right: 5px;
188 |
189 | &-active {
190 | background: red;
191 | }
192 | }
193 | }
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/src/img/sample.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/osomstudio/juniper/0002b3a545cf0ba73ab7f6041224144605e670c4/web/app/themes/juniper-theme/src/img/sample.jpg
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/src/js/_app.js:
--------------------------------------------------------------------------------
1 | import nav from './nav';
2 |
3 | export default class App {
4 | initCore() {
5 | nav.hamburger();
6 | }
7 | }
8 |
9 | function LoadApp() {
10 | const app = new App();
11 | app.initCore();
12 | }
13 |
14 | if (document.readyState === 'loading') {
15 | document.addEventListener('DOMContentLoaded', LoadApp);
16 | } else {
17 | LoadApp();
18 | }
19 |
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/src/js/animations/transitions.js:
--------------------------------------------------------------------------------
1 | import barba from '@barba/core';
2 | import barbaPrefetch from '@barba/prefetch';
3 | import App from '../_app';
4 |
5 | export default function () {
6 | barba.use(barbaPrefetch);
7 | barba.init({
8 | transitions: [{
9 | leave(data) { // eslint-disable-line no-unused-vars
10 | const done = this.async();
11 | done();
12 | document.body.classList.remove('nav--toggled');
13 | document.querySelector('header').scrollTo({ behavior: 'smooth' });
14 | },
15 | enter(data) { // eslint-disable-line no-unused-vars
16 | const done = this.async();
17 | done(); // call on transition end
18 |
19 | App(); // reinit everything inside .main
20 | },
21 | }],
22 | });
23 | }
24 |
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/src/js/nav.js:
--------------------------------------------------------------------------------
1 | const nav = {
2 | hamburger: () => {
3 | const hamburger = document.querySelector('.nav-toggle');
4 | if (!hamburger) return;
5 |
6 | const container = document.querySelector('body');
7 | hamburger.addEventListener('click', () => {
8 | hamburger.classList.toggle('nav--toggled');
9 | container.classList.toggle('nav--toggled');
10 | });
11 | },
12 | };
13 |
14 | export default nav;
15 |
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/static/no-timber.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Timber not active
5 |
6 |
7 |
8 | Timber not activated
9 |
10 |
11 |
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/static/site.js:
--------------------------------------------------------------------------------
1 | jQuery( document ).ready( function( $ ) {
2 |
3 | // Your JavaScript goes here
4 |
5 | });
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/style.css:
--------------------------------------------------------------------------------
1 | /*
2 | * Theme Name: Juniper Theme
3 | * Description: OsomStudio
4 | * Author: OsomStudio
5 | * Author URI: http://osomstudio.com
6 | * Version: 2.1
7 | */
8 |
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/templates/404.html:
--------------------------------------------------------------------------------
1 | ERROR 404
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/templates/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/templates/my-custom-template.html:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/osomstudio/juniper/0002b3a545cf0ba73ab7f6041224144605e670c4/web/app/themes/juniper-theme/templates/my-custom-template.html
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/theme.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schemas.wp.org/trunk/theme.json",
3 | "version": 2,
4 | "settings": {
5 | "typography": {
6 | "fontSizes": [
7 | {
8 | "name": "Mini",
9 | "size": "13px",
10 | "slug": "mini"
11 | },
12 | {
13 | "name": "X Small",
14 | "size": "16px",
15 | "slug": "x-small"
16 | },
17 | {
18 | "name": "Small",
19 | "size": "18px",
20 | "slug": "small"
21 | },
22 | {
23 | "name": "Medium",
24 | "size": "20px",
25 | "slug": "medium"
26 | },
27 | {
28 | "name": "Large",
29 | "size": "24px",
30 | "slug": "large"
31 | },
32 | {
33 | "name": "X Large",
34 | "size": "32px",
35 | "slug": "x-large"
36 | },
37 | {
38 | "name": "XX Large",
39 | "size": "40px",
40 | "slug": "xx-large"
41 | },
42 | {
43 | "name": "XXX Large",
44 | "size": "48px",
45 | "slug": "xxx-large"
46 | }
47 | ],
48 | "fontFamilies": [
49 | {
50 | "name": "Default",
51 | "slug": "default",
52 | "fallbacks": [
53 | "system-ui",
54 | "sans-serif"
55 | ]
56 | }
57 | ],
58 | "lineHeights": [
59 | {
60 | "name": "Default",
61 | "size": 1.5,
62 | "slug": "default"
63 | }
64 | ]
65 | },
66 | "appearanceTools": true,
67 | "color": {
68 | "defaultDuotone": false,
69 | "defaultPalette": false,
70 | "defaultGradients": false,
71 | "palette": [
72 | {
73 | "color": "#164194",
74 | "name": "Primary",
75 | "slug": "primary"
76 | },
77 | {
78 | "color": "#e6ebf5",
79 | "name": "Blueish",
80 | "slug": "blueish"
81 | },
82 | {
83 | "color": "#d0d9ea",
84 | "name": "Blueish:hover",
85 | "slug": "blueish-hover"
86 | },
87 | {
88 | "color": "rgba(0, 0, 0, .5)",
89 | "name": "Gray",
90 | "slug": "gray"
91 | },
92 | {
93 | "color": "#f7f9fc",
94 | "name": "Gray 100",
95 | "slug": "gray-100"
96 | },
97 | {
98 | "color": "#9e9e9e",
99 | "name": "Gray 500",
100 | "slug": "gray-500"
101 | },
102 | {
103 | "color": "#ffffff",
104 | "name": "White",
105 | "slug": "white"
106 | },
107 | {
108 | "color": "#000000",
109 | "name": "Black",
110 | "slug": "black"
111 | },
112 | {
113 | "color": "#e45454",
114 | "name": "Red",
115 | "slug": "red"
116 | },
117 | {
118 | "color": "#87ab3f",
119 | "name": "Green",
120 | "slug": "green"
121 | }
122 | ]
123 | },
124 | "layout": {
125 | "contentSize": "1140px",
126 | "wideSize": "1280px"
127 | },
128 | "spacing": {
129 | "spacingScale": {
130 | "steps": 0
131 | },
132 | "spacingSizes": [
133 | {
134 | "name": "1",
135 | "size": "1rem",
136 | "slug": "10"
137 | },
138 | {
139 | "name": "2",
140 | "size": "min(1.5rem, 2vw)",
141 | "slug": "20"
142 | },
143 | {
144 | "name": "3",
145 | "size": "min(2.5rem, 3vw)",
146 | "slug": "30"
147 | },
148 | {
149 | "name": "4",
150 | "size": "min(4rem, 5vw)",
151 | "slug": "40"
152 | },
153 | {
154 | "name": "5",
155 | "size": "min(6.5rem, 8vw)",
156 | "slug": "50"
157 | },
158 | {
159 | "name": "6",
160 | "size": "min(10.5rem, 13vw)",
161 | "slug": "60"
162 | }
163 | ],
164 | "units": ["%", "px", "em", "rem", "vh", "vw"]
165 | },
166 | "useRootPaddingAwareAlignments": false
167 | },
168 | "styles": {
169 | "blocks": {
170 | "core/group": {
171 | "spacing": {
172 | "padding": {
173 | "top": "var(--wp--preset--spacing--40)",
174 | "bottom": "var(--wp--preset--spacing--40)"
175 | },
176 | "margin": {
177 | "top": "0",
178 | "bottom": "0"
179 | }
180 | }
181 | }
182 | },
183 | "typography": {
184 | "fontSize": "var(--wp--preset--font-size--mini)"
185 | },
186 | "elements": {
187 | "h1": {
188 | "typography": {
189 | "fontSize": "var(--wp--preset--font-size--x-large)"
190 | }
191 | },
192 | "h2": {
193 | "typography": {
194 | "fontSize": "var(--wp--preset--font-size--large)"
195 | }
196 | },
197 | "h3": {
198 | "typography": {
199 | "fontSize": "var(--wp--preset--font-size--medium)"
200 | }
201 | },
202 | "h4": {
203 | "typography": {
204 | "fontSize": "var(--wp--preset--font-size--small)"
205 | }
206 | }
207 | }
208 | },
209 | "templateParts": [
210 | {
211 | "area": "footer",
212 | "name": "footer",
213 | "title": "Footer"
214 | },
215 | {
216 | "area": "header",
217 | "name": "header",
218 | "title": "Header"
219 | },
220 | {
221 | "area": "header",
222 | "name": "top-bar",
223 | "title": "Top bar"
224 | }
225 | ],
226 | "customTemplates": [
227 | {
228 | "name": "my-custom-template",
229 | "title": "The template title",
230 | "postTypes": [
231 | "page",
232 | "post",
233 | "my-cpt"
234 | ]
235 | }
236 | ]
237 | }
238 |
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/views/blocks/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/osomstudio/juniper/0002b3a545cf0ba73ab7f6041224144605e670c4/web/app/themes/juniper-theme/views/blocks/.gitkeep
--------------------------------------------------------------------------------
/web/app/themes/juniper-theme/views/blocks/cta.twig:
--------------------------------------------------------------------------------
1 | {#
2 | Title: CTA
3 | Description:
4 | Category: formatting
5 | Icon: admin-comments
6 | Keywords:
7 | Mode: edit
8 | Align: full
9 | SupportsAlign: left right full
10 | SupportsMode: true
11 | SupportsMultiple: true
12 | #}
--------------------------------------------------------------------------------
/web/app/uploads/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/osomstudio/juniper/0002b3a545cf0ba73ab7f6041224144605e670c4/web/app/uploads/.gitkeep
--------------------------------------------------------------------------------
/web/index.php:
--------------------------------------------------------------------------------
1 |