diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..fe8a69b --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,29 @@ +name: Deploy to SiteGround +on: + push: + branches: [stg, main] + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + deploy-prod: + name: Deploy to Production + if: github.ref == 'refs/heads/main' + uses: macrosbysara/shared-github-actions/.github/workflows/deploy-theme.yml@main + with: + REMOTE: "macrosbysara.com" # Remote folder to deploy to + theme_name: "macros-by-sara" + flags: "-azr --delete" + secrets: inherit + + deploy-stg: + name: Deploy to Staging + if: github.ref == 'refs/heads/stg' + uses: macrosbysara/shared-github-actions/.github/workflows/deploy-theme.yml@main + with: + REMOTE: staging6.macrosbysara.com + theme_name: "macros-by-sara" + flags: "-azvr --delete" + secrets: inherit diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..986ffb4 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,49 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Listen for Xdebug 3.0 (Local)", + "type": "php", + "request": "launch", + "port": 9003, + "xdebugSettings": { + "max_children": 128, + "max_data": 1024, + "max_depth": 3, + "show_hidden": 1 + }, + "pathMappings": { + "/Users/kjroelke/Local Sites/macros-by-sara/app/public/wp-content/themes/macros-by-sara": "${workspaceFolder}" + } + }, + { + "name": "Listen for Xdebug (Local)", + "type": "php", + "request": "launch", + "port": 9000, + "xdebugSettings": { + "max_children": 128, + "max_data": 1024, + "max_depth": 3, + "show_hidden": 1 + }, + "pathMappings": { + "/Users/kjroelke/Local Sites/macros-by-sara/app/public/wp-content/themes/macros-by-sara": "${workspaceFolder}" + } + }, + { + "name": "Launch currently open script", + "type": "php", + "request": "launch", + "program": "${file}", + "cwd": "${fileDirname}", + "port": 9000, + "xdebugSettings": { + "max_children": 128, + "max_data": 1024, + "max_depth": 3, + "show_hidden": 1 + } + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json index a3145ce..15f9023 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -35,7 +35,21 @@ "editor.useTabStops": true, "prettier.useTabs": false }, - "cSpell.words": [ "cpts", "Linktree", "macrosbysara", "wpcf" ], + "cSpell.words": [ + "alignfull", + "alignwide", + "atts", + "azvr", + "bloginfo", + "choctawnationofoklahoma", + "cpts", + "Linktree", + "macrosbysara", + "remoteip", + "trackbacks", + "wght", + "wpcf" + ], "css.format.spaceAroundSelectorSeparator": true, "editor.codeActionsOnSave": { "source.fixAll.eslint": "always", diff --git a/changelog.md b/changelog.md index a786783..1a97ce2 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 2.0.0 - [November 1, 2025] + +- New Theme! + ## 1.3.2 - [October 29, 2025] - Chore: Add lints and configs diff --git a/eslint.config.mjs b/eslint.config.mjs index 95ed3cc..691448f 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,10 +1,7 @@ import globals from 'globals'; import { fixupConfigRules, includeIgnoreFile } from '@eslint/compat'; import wordpressConfig from '@wordpress/eslint-plugin'; - -// eslint-disable-next-line import/no-unresolved import { globalIgnores, defineConfig } from 'eslint/config'; - import { FlatCompat } from '@eslint/eslintrc'; import path from 'path'; import { fileURLToPath, URL } from 'url'; @@ -29,7 +26,7 @@ export default defineConfig( [ ) ), { - files: [ 'wp-content/themes/**/src/js/**/*.{js,ts,jsx,tsx}' ], + files: [ 'src/js/**/*.{js,ts,jsx,tsx}' ], languageOptions: { globals: globals.browser, }, diff --git a/inc/theme/class-gutenberg-handler.php b/inc/theme/class-gutenberg-handler.php index ace6708..f75503c 100644 --- a/inc/theme/class-gutenberg-handler.php +++ b/inc/theme/class-gutenberg-handler.php @@ -18,7 +18,39 @@ class Gutenberg_Handler { * Constructor */ public function __construct() { + add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_block_assets' ) ); add_action( 'after_setup_theme', array( $this, 'theme_supports' ) ); + add_action( 'init', array( $this, 'register_theme_blocks' ) ); + } + + /** + * Enqueue the block editor assets that control the layout of the Block Editor. + */ + public function enqueue_block_assets() { + $files = array( + 'editDefaultBlocks' => 'script', + 'editor' => 'style', + ); + foreach ( $files as $handle => $type ) { + $assets = require_once get_stylesheet_directory() . "/build/admin/{$handle}.asset.php"; + if ( 'style' === $type || 'both' === $type ) { + wp_enqueue_style( + $handle, + get_stylesheet_directory_uri() . "/build/admin/{$handle}.css", + $assets['dependencies'], + $assets['version'] + ); + } + if ( 'script' === $type || 'both' === $type ) { + wp_enqueue_script( + $handle, + get_stylesheet_directory_uri() . "/build/admin/{$handle}.js", + $assets['dependencies'], + $assets['version'], + array( 'strategy' => 'defer' ) + ); + } + } } /** @@ -26,14 +58,26 @@ public function __construct() { */ public function theme_supports() { $opt_in_features = array( - 'disable-custom-colors', 'responsive-embeds', - 'disable-custom-gradients', - 'disable-custom-font-sizes', ); - foreach ( $opt_in_features as $feature ) { add_theme_support( $feature ); } + + $opt_out_features = array( + 'core-block-patterns', + ); + foreach ( $opt_out_features as $feature ) { + remove_theme_support( $feature ); + } + } + + /** + * Register any theme-specific blocks + */ + public function register_theme_blocks() { + // Load blocks + $blocks_path = get_template_directory() . '/build'; + wp_register_block_types_from_metadata_collection( $blocks_path . '/js/blocks', $blocks_path . '/blocks-manifest.php' ); } } diff --git a/inc/theme/class-rest-router.php b/inc/theme/class-rest-router.php new file mode 100644 index 0000000..bcf10e0 --- /dev/null +++ b/inc/theme/class-rest-router.php @@ -0,0 +1,182 @@ +namespace = 'mbs/v1'; + add_action( 'rest_api_init', array( $this, 'register_routes' ) ); + add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_interest_form_script' ), 100 ); + } + + /** + * Register routes. + */ + public function register_routes() { + register_rest_route( + $this->namespace . '/forms', + '/interest-form', + array( + 'methods' => WP_REST_Server::CREATABLE, + 'callback' => array( $this, 'handle_interest_form' ), + 'permission_callback' => array( $this, 'allow_public_access' ), + 'args' => array( + 'firstName' => array( + 'required' => true, + 'sanitize_callback' => 'sanitize_text_field', + ), + 'lastName' => array( + 'required' => true, + 'sanitize_callback' => 'sanitize_text_field', + ), + 'email' => array( + 'required' => true, + 'sanitize_callback' => 'sanitize_email', + 'validate_callback' => 'is_email', + ), + 'interest' => array( + 'required' => true, + 'sanitize_callback' => 'sanitize_text_field', + 'validate_callback' => function ( $param ) { + $valid_options = array( + 'macros', + 'habits', + 'one-time-macros', + 'fitness', + ); + return in_array( $param, $valid_options, true ); + }, + ), + ), + ) + ); + } + + /** + * Example endpoint callback. + * + * @param WP_REST_Request $request The REST request. + * @return WP_REST_Response The REST response. + */ + public function handle_interest_form( WP_REST_Request $request ): WP_REST_Response { + $first_name = $request->get_param( 'firstName' ); + $last_name = $request->get_param( 'lastName' ); + $email = $request->get_param( 'email' ); + $interest = $request->get_param( 'interest' ); + $data = array( + 'code' => 'success', + 'message' => 'Interest form submitted successfully!', + 'data' => array( + 'status' => 200, + 'firstName' => $first_name, + 'lastName' => $last_name, + 'email' => $email, + 'interest' => $interest, + ), + ); + return rest_ensure_response( $data ); + } + + /** + * Enqueue interest form script with localized REST API data. + */ + public function enqueue_interest_form_script() { + wp_localize_script( + 'global', + 'mbsRestApi', + array( + 'root' => esc_url_raw( rest_url() . $this->namespace ), + 'nonce' => wp_create_nonce( 'wp_rest' ), + ) + ); + wp_enqueue_script( + 'cloudflare', + 'https://challenges.cloudflare.com/turnstile/v0/api.js', + array( 'global' ), + null, // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion + array( + 'strategy' => 'async', + ) + ); + } + + /** + * Allow public access to the endpoint. + * + * @param WP_REST_Request $request The REST request. + * @return bool + */ + public function allow_public_access( WP_REST_Request $request ): bool { + if ( ! defined( 'CF_TURNSTILE_SECRET' ) || empty( CF_TURNSTILE_SECRET ) ) { + return false; + } + $nonce = null; + $headers = $request->get_headers(); + if ( isset( $headers['x_wp_nonce'] ) ) { + $nonce = $headers['x_wp_nonce']; + } + $verified = wp_verify_nonce( $nonce[0], 'wp_rest' ); + if ( ! $verified ) { + return false; + } + if ( ! isset( $_POST['cf-turnstile-response'] ) ) { + return false; + } + return $this->cloudflare_validation( + sanitize_text_field( $_POST['cf-turnstile-response'] ?? '' ) + ); + } + + + /** + * Validate Cloudflare Turnstile response. + * + * @param string $token The Turnstile token from the client. + * @param string|null $remoteip Optional. The user's IP address. + * @return bool True if validation is successful, false otherwise. + */ + private function cloudflare_validation( string $token, ?string $remoteip = null ): bool { + $url = 'https://challenges.cloudflare.com/turnstile/v0/siteverify'; + + $data = array( + 'secret' => CF_TURNSTILE_SECRET, + 'response' => $token, + ); + + if ( $remoteip ) { + $data['remoteip'] = $remoteip; + } + + $response = wp_remote_post( + $url, + array( + 'headers' => array( 'Content-Type' => 'application/x-www-form-urlencoded' ), + 'body' => $data, + 'timeout' => 10, + ) + ); + if ( is_wp_error( $response ) ) { + return false; + } + + $response = wp_remote_retrieve_body( $response ); + $response_data = json_decode( $response, true ); + return $response_data['success'] ?? false; + } +} diff --git a/inc/theme/class-theme-init.php b/inc/theme/class-theme-init.php index 165493a..951cf4b 100644 --- a/inc/theme/class-theme-init.php +++ b/inc/theme/class-theme-init.php @@ -13,68 +13,110 @@ * Class: Theme Init */ class Theme_Init { + /** + * Utils Loader + * + * @var Utils_Loader $loader + */ + private Utils_Loader $loader; /** * Constructor */ public function __construct() { - $this->load_files(); - add_filter( 'x_enqueue_parent_stylesheet', '__return_true' ); + require_once get_stylesheet_directory() . '/inc/theme/class-utils-loader.php'; + $this->loader = new Utils_Loader(); + $this->loader->load_files(); + $this->disable_discussion(); + add_action( 'after_setup_theme', array( $this, 'configure_theme_support' ) ); + add_action( 'init', array( $this, 'alter_post_types' ) ); add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) ); add_filter( 'wp_speculation_rules_configuration', array( $this, 'handle_speculative_loading' ) ); } - /** - * Load Required Files - */ - private function load_files() { - $base_path = get_stylesheet_directory() . '/inc'; - $theme_files = array( - 'gutenberg-handler' => 'Gutenberg_Handler', - ); - foreach ( $theme_files as $file => $class ) { - require_once $base_path . "/theme/class-{$file}.php"; - if ( $class ) { - $class = __NAMESPACE__ . "\\{$class}"; - new $class(); + /** Remove comments, pings and trackbacks support from posts types. */ + private function disable_discussion() { + // Close comments on the front-end + add_filter( 'comments_open', '__return_false', 20, 2 ); + add_filter( 'pings_open', '__return_false', 20, 2 ); + + // Hide existing comments. + add_filter( 'comments_array', '__return_empty_array', 10, 2 ); + // Remove comments page in menu. + add_action( + 'admin_menu', + function () { + remove_menu_page( 'edit-comments.php' ); } - } - $plugin_files = array( - 'acf-handler' => array( - 'class' => 'ACF_Handler', - 'dir' => 'acf', - ), ); - foreach ( $plugin_files as $file => $data ) { - require_once $base_path . "/plugins/{$data['dir']}/class-{$file}.php"; - if ( $data['class'] ) { - $class = __NAMESPACE__ . "\\{$data['class']}"; - new $class(); + // Remove comments links from admin bar. + add_action( + 'init', + function () { + if ( is_admin_bar_showing() ) { + remove_action( 'admin_bar_menu', 'wp_admin_bar_comments_menu', 60 ); + } } - } + ); } + /** Registers Theme Supports */ + public function configure_theme_support() { + add_theme_support( 'post-thumbnails' ); + add_theme_support( 'title-tag' ); + + register_nav_menus( + array( + 'primary_menu' => 'Primary Menu', + 'footer_menu' => 'Footer Menu', + ) + ); + } + + /** Alter Post Types. */ + public function alter_post_types() { + add_post_type_support( 'page', 'excerpt' ); + } + + /** * Enqueue scripts and styles. */ public function enqueue_scripts(): void { - $global_assets = require_once get_stylesheet_directory() . '/build/index.asset.php'; - - wp_enqueue_script( - 'global', - get_stylesheet_directory_uri() . '/build/index.js', - $global_assets['dependencies'], - $global_assets['version'], - array( 'strategy' => 'defer' ) - ); - wp_enqueue_style( - 'global', - get_stylesheet_directory_uri() . '/build/index.css', - $global_assets['dependencies'], - $global_assets['version'], + $files = array( + 'bootstrap' => array( + 'js' => 'vendors/bootstrap', + 'css' => 'vendors/bootstrap', + ), + 'global' => array( + 'js' => 'global', + 'css' => 'global', + ), ); + foreach ( $files as $handle => $paths ) { + $assets = require_once get_stylesheet_directory() . "/build/{$paths['js']}.asset.php"; + + $deps = $assets['dependencies']; + if ( 'bootstrap' !== $handle ) { + // Ensure assets load after bootstrap for proper overrides + $deps = array_merge( $deps, array( 'bootstrap' ) ); + } + wp_enqueue_script( + $handle, + get_stylesheet_directory_uri() . "/build/{$paths['js']}.js", + $deps, + $assets['version'], + array( 'strategy' => 'defer' ) + ); + wp_enqueue_style( + $handle, + get_stylesheet_directory_uri() . "/build/{$paths['css']}.css", + $deps, + $assets['version'], + ); + } } /** diff --git a/inc/theme/class-utils-loader.php b/inc/theme/class-utils-loader.php new file mode 100644 index 0000000..d8d3628 --- /dev/null +++ b/inc/theme/class-utils-loader.php @@ -0,0 +1,80 @@ +base_path = get_stylesheet_directory() . '/inc'; + } + + /** + * Load Required Files + */ + public function load_files() { + + // Require Navwalker + require_once $this->base_path . '/theme/navwalkers/class-navwalker.php'; + + // Theme Utils + $theme_files = array( + 'gutenberg-handler' => 'Gutenberg_Handler', + 'rest-router' => 'Rest_Router', + ); + $this->load_utils( '/theme', $theme_files ); + $this->load_acf_utils(); + } + + /** + * Load Utils + * + * @param string $path Path to utils. + * @param array $files Array of files to load. + */ + private function load_utils( string $path, array $files ) { + foreach ( $files as $file => $class ) { + require_once $this->base_path . "{$path}/class-{$file}.php"; + if ( $class ) { + $class = __NAMESPACE__ . "\\{$class}"; + new $class(); + } + } + } + + /** + * Load ACF Utils + */ + private function load_acf_utils() { + $plugin_files = array( + 'acf-handler' => array( + 'class' => 'ACF_Handler', + 'dir' => 'acf', + ), + ); + foreach ( $plugin_files as $file => $data ) { + require_once $this->base_path . "/plugins/{$data['dir']}/class-{$file}.php"; + if ( $data['class'] ) { + $class = __NAMESPACE__ . "\\{$data['class']}"; + new $class(); + + } + } + } +} diff --git a/inc/theme/navwalkers/class-navwalker.php b/inc/theme/navwalkers/class-navwalker.php new file mode 100644 index 0000000..1809e3c --- /dev/null +++ b/inc/theme/navwalkers/class-navwalker.php @@ -0,0 +1,245 @@ + + *
Optional second line
+ +in
+ + +$249
+ + + +/mo
+This is for you if you are: new to tracking macros or want extra support with access to ask questions anytime
+ + + +$249
+ + + +/mo
+This is for you if you are: new to tracking macros or want extra support with access to ask questions anytime
+ + + +Everything in the 1:1 coaching plan plus...
+Weekly phone or Zoom calls
+Anytime messaging for questions
+$219
+ + + +/mo
+This is for you if you are: familiar with tracking macros but like the option to gain clairty and ask questions from time to time
+ + + +Everything in the 1:1 coaching plan plus...
+1x/month phone or Zoom call
+Anytime messaging for questions
+© ${ thisYear } ${ brandName } All Rights Reserved.
Site built by ${ builderLink }
Button Color
+Button Background Color
+${ responseData.message }
`; + } + // form.reset(); + } else if ( responseArea ) { + responseArea.innerHTML = ``; + responseArea.innerHTML = 'There was an error submitting the form. Please try again later.
'; + } else { + // eslint-disable-next-line no-alert + alert( 'There was an error submitting the form. Please try again later.' ); + } + } catch ( error ) { + // eslint-disable-next-line no-alert + alert( 'There was an error submitting the form. Please try again later.' ); + } finally { + setIsLoading( false, form ); + } + } ); + } ); +} + +/** + * Enable or disable form controls during loading state + * @param isLoading loading state + * @param form the form + */ +function setIsLoading( isLoading: boolean, form:HTMLFormElement ) { + const controls = form.querySelectorAll)
+$default-type-base-min: 16px;
+$default-type-base-max: 20px;
+
+// Type Scale steps
+$default-type-steps: (
+ small: "sm",
+ p: "base",
+ h6: "md",
+ h5: "lg",
+ h4: "xl",
+ h3: "xxl",
+ h2: "xxxl",
+ h1: "xxxxl",
+);
+$display-steps: (6, 5, 4, 3, 2, 1);
+
+// Default Type Scale
+$default-type-scale: map.get($scales, minor-third);
+
+//////////////
+// FUNCTIONS
+////////////
+
+@function _rnd($number, $places: 3) {
+ $n: 1;
+
+ @if $places > 0 {
+ @for $i from 1 through $places {
+ $n: $n * 10;
+ }
+ }
+
+ @return math.div(math.round($number * $n), $n);
+}
+
+@function _to-rems($px) {
+ $rems: math.div($px, 16px) * 1rem;
+
+ @return $rems;
+}
+
+/**
+ * Conditionally writes a clamped fluid type value based on a minimum and maximum pixel size.
+*/
+@function clamped(
+ $min-px,
+ $max-px,
+ $min-bp: $default-min-bp,
+ $max-bp: $default-max-bp
+) {
+ $slope: math.div($max-px - $min-px, $max-bp - $min-bp);
+ $slope-vw: _rnd($slope * 100);
+ $intercept-rems: _rnd(_to-rems($min-px - $slope * $min-bp));
+ $min-rems: _rnd(_to-rems($min-px));
+ $max-rems: _rnd(_to-rems($max-px));
+ $preferred: "calc(#{$slope-vw}vw + #{$intercept-rems})";
+
+ @return clamp(#{$min-rems}, #{$preferred}, #{$max-rems});
+}
+
+///////////
+// MIXINS
+///////////
+
+@mixin generate-type-scale(
+ $type-scale: $default-type-scale,
+ $prefix: "font-size",
+ $type-steps: $default-type-steps,
+ $type-base-index: list.index(map.keys($default-type-steps), "p")
+) {
+ @if meta.type-of($type-scale) == "string" {
+ $type-scale: map.get($scales, $type-scale);
+ }
+
+ $steps-keys: ();
+
+ @if meta.type-of($type-steps) == "map" {
+ $steps-keys: map.keys($type-steps);
+ } @else {
+ $steps-keys: $type-steps;
+ }
+
+ @for $i from 1 through list.length($steps-keys) {
+ $step-key: list.nth($steps-keys, $i);
+ $step-value: 0;
+
+ @if meta.type-of($type-steps) == "map" {
+ $step-value: map.get($type-steps, $step-key);
+ } @else {
+ $step-value: $step-key;
+ }
+
+ // stylelint-disable-next-line scss/operator-no-newline-after
+ $min: $default-type-base-min *
+ math.pow($type-scale, $i - $type-base-index);
+ // stylelint-disable-next-line scss/operator-no-newline-after
+ $max: $default-type-base-max *
+ math.pow($type-scale, $i - $type-base-index);
+
+ --#{$prefix}-#{$step-value}: #{clamped($min, $max)};
+ }
+}
diff --git a/src/styles/abstracts/_functions.scss b/src/styles/abstracts/_functions.scss
new file mode 100644
index 0000000..48621c6
--- /dev/null
+++ b/src/styles/abstracts/_functions.scss
@@ -0,0 +1,16 @@
+@use "sass:list";
+@use "../abstracts/variables" as v;
+
+@function transition($properties...) {
+ $new-list: ();
+
+ @each $property in $properties {
+ $new-list: list.append(
+ $new-list,
+ #{$property} var(--wp--custom--transition--standard),
+ comma
+ );
+ }
+
+ @return $new-list;
+}
diff --git a/src/styles/abstracts/_mixins.scss b/src/styles/abstracts/_mixins.scss
new file mode 100644
index 0000000..42c5a2f
--- /dev/null
+++ b/src/styles/abstracts/_mixins.scss
@@ -0,0 +1,21 @@
+@forward "./bs_breakpoints";
+@use "./functions" as fn;
+
+@mixin interaction() {
+ &:hover,
+ &:active,
+ &:focus {
+ @content;
+ }
+}
+
+@mixin btn-interaction($color, $bg-color) {
+ transition: fn.transition(color, background-color, box-shadow, translate);
+
+ @include interaction {
+ color: $color;
+ background-color: $bg-color;
+ box-shadow: var(--btn-hover-box-shadow);
+ translate: var(--btn-hover-translate-x) var(--btn-hover-translate-y);
+ }
+}
diff --git a/src/styles/abstracts/_variables.scss b/src/styles/abstracts/_variables.scss
new file mode 100644
index 0000000..e43cc82
--- /dev/null
+++ b/src/styles/abstracts/_variables.scss
@@ -0,0 +1,27 @@
+$transition-standard: 0.15s ease-in-out;
+
+/* ////////////
+// COLORS
+///////////// */
+
+$primary: rgb(60 28 18);
+$secondary: rgb(135 82 66);
+$custom-colors: (
+ "primary-light": rgb(116 60 24),
+ "secondary-light": rgb(209 188 181),
+ "muted-orange": rgb(193 128 98),
+ "sage": rgb(33 79 76),
+ "gray": rgb(90 88 92),
+ "black": black,
+ "white": white,
+);
+
+/* ////////////
+// TYPOGRAPHY
+///////////// */
+
+$font-headings: cormorant, serif;
+$font-body: mulish, "Helvetica Neue", "Noto Sans", "Liberation Sans", arial,
+ sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol",
+ "Noto Color Emoji";
+$font-serif: adobe-garamond-pro, serif;
diff --git a/src/styles/base/_base.scss b/src/styles/base/_base.scss
index 507f7a3..330d765 100644
--- a/src/styles/base/_base.scss
+++ b/src/styles/base/_base.scss
@@ -1,13 +1,6 @@
:root {
- --color-primary: var(--wp--preset--color--primary, rgb(61 28 17));
- --color-primary--light: var(
- --wp--preset--color--primary-light,
- rgb(116 60 24)
- );
- --color-secondary--light: var(
- --wp--preset--color--secondary-light,
- rgb(210 189 182)
- );
- --color-secondary: var(--wp--preset--color--secondary, rgb(140 101 72));
- --color-grey--dark: var(--wp-preset--color--gray, rgb(90 88 92));
+ --btn-hover-box-shadow: 2px 4px 10px 2px rgb(0 0 0 / 35%);
+ --btn-hover-translate-y: -4px;
+ --btn-hover-translate-x: 0px;
+ --transition-standard: var(--wp--custom--transition--standard);
}
diff --git a/src/styles/base/_typography.scss b/src/styles/base/_typography.scss
new file mode 100644
index 0000000..f05d86e
--- /dev/null
+++ b/src/styles/base/_typography.scss
@@ -0,0 +1,108 @@
+@use "../abstracts/fluid-type-utils" as fluid-type;
+@use "../abstracts/variables" as v;
+
+@font-face {
+ font-family: Mulish;
+ src: url("/assets/fonts/Mulish/Mulish-VariableFont_wght.ttf")
+ format("truetype");
+ font-weight: 100 900; /* Variable font weight range */
+ font-style: normal;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: Mulish;
+ src: url("/assets/fonts/Mulish/Mulish-Italic-VariableFont_wght.ttf")
+ format("truetype");
+ font-weight: 100 900;
+ font-style: italic;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: Cormorant;
+ src: url("/assets/fonts/Cormorant/Cormorant-VariableFont_wght.ttf")
+ format("truetype");
+ font-weight: 100 900; /* Variable font weight range */
+ font-style: normal;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: Cormorant;
+ src: url("/assets/fonts/Cormorant/Cormorant-Italic-VariableFont_wght.ttf")
+ format("truetype");
+ font-weight: 100 900;
+ font-style: italic;
+ font-display: swap;
+}
+
+:root {
+ @include fluid-type.generate-type-scale;
+ @include fluid-type.generate-type-scale(
+ perfect-fourth,
+ "fs-display",
+ fluid-type.$display-steps,
+ 0
+ );
+}
+
+body {
+ --bs-body-font-size: var(--wp--preset--font-size--root);
+
+ text-wrap: pretty;
+ word-break: normal;
+}
+
+// HEADINGS
+
+$headings: (
+ 1: "xxxl",
+ 2: "xxl",
+ 3: "xl",
+ 4: "lg",
+ 5: "md",
+ 6: "sm",
+);
+
+/**
+* Reset Headings and utils to use WP sizes instead of bootstrap's
+*/
+@each $heading, $slug in $headings {
+ h#{$heading} {
+ font-size: var(--wp--preset--font-size--#{$slug});
+ }
+ .h#{$heading} {
+ text-wrap: pretty;
+ font-size: var(--wp--preset--font-size--#{$slug});
+ font-family: var(
+ --wp--preset--font-family--headings,
+ #{v.$font-headings}
+ );
+ }
+ .fs-#{$heading} {
+ font-size: var(--wp--preset--font-size--#{$slug}) !important;
+ }
+}
+
+/**
+* Reset Display Font Sizes to use clamp instead of Bootstrap's RFS
+*/
+@each $step in fluid-type.$display-steps {
+ .display-#{$step} {
+ font-size: var(--fs-display-#{$step}) !important;
+ }
+}
+
+small,
+.small {
+ font-size: var(--font-size-sm);
+}
+
+[class*="fs-"] > p {
+ font-size: inherit;
+}
+
+a {
+ transition: color var(--transition-standard);
+}
diff --git a/src/styles/base/_utilities.scss b/src/styles/base/_utilities.scss
index a24ae77..46f43ee 100644
--- a/src/styles/base/_utilities.scss
+++ b/src/styles/base/_utilities.scss
@@ -1,13 +1,23 @@
-/* stylelint-disable selector-class-pattern */
+@use "../abstracts/mixins" as m;
-.highlight--color-primary {
- color: var(--color-primary);
+.inset-0 {
+ inset: 0;
}
-.highlight--color-secondary {
- color: var(--color-secondary);
-}
+.linktree-link {
+ @include m.btn-interaction(var(--wp--preset--color--primary), white);
+
+ a {
+ position: relative;
+ width: 100%;
+ display: inline-block;
+ text-decoration: none;
-.highlight--color-secondary--light {
- color: var(--color-secondary--light);
+ &::after {
+ content: "";
+ position: absolute;
+ inset: 0;
+ z-index: 1;
+ }
+ }
}
diff --git a/src/styles/block-overrides/_button.scss b/src/styles/block-overrides/_button.scss
new file mode 100644
index 0000000..4fceda0
--- /dev/null
+++ b/src/styles/block-overrides/_button.scss
@@ -0,0 +1,19 @@
+@use "../abstracts/mixins" as m;
+@use "../abstracts/functions" as fn;
+
+.wp-block-button {
+ &__link {
+ border: 2px solid var(--wp--preset--color--primary);
+
+ @include m.btn-interaction(var(--wp--preset--color--primary), white);
+ }
+
+ &.is-style-outline &__link {
+ color: var(--wp--preset--color--primary);
+
+ @include m.interaction {
+ box-shadow: var(--btn-hover-box-shadow);
+ translate: var(--btn-hover-translate-x) var(--btn-hover-translate-y);
+ }
+ }
+}
diff --git a/src/styles/block-overrides/_post-excerpt.scss b/src/styles/block-overrides/_post-excerpt.scss
new file mode 100644
index 0000000..2482ad5
--- /dev/null
+++ b/src/styles/block-overrides/_post-excerpt.scss
@@ -0,0 +1,36 @@
+@use "../abstracts/mixins" as m;
+@use "../abstracts/functions" as fn;
+
+:root .wp-block-post-excerpt {
+ display: flex;
+ flex-direction: column;
+ gap: var(--wp--preset--spacing--base);
+
+ &__more-text {
+ margin-top: auto;
+ margin-bottom: 0;
+ }
+
+ &__more-link {
+ text-decoration: none;
+ padding: var(--wp--preset--spacing--base) var(--wp--preset--spacing--lg);
+ background-color: var(--wp--preset--color--primary);
+ border: 2px solid var(--wp--preset--color--primary);
+ font-size: var(--wp--preset--font-size--root);
+ color: white;
+ border-radius: 50rem;
+ transition: fn.transition(
+ color,
+ background-color,
+ box-shadow,
+ translate
+ );
+
+ @include m.interaction {
+ color: var(--wp--preset--color--primary);
+ background-color: white;
+ box-shadow: var(--btn-hover-box-shadow);
+ translate: var(--btn-hover-translate-x) var(--btn-hover-translate-y);
+ }
+ }
+}
diff --git a/src/styles/block-overrides/_query.scss b/src/styles/block-overrides/_query.scss
new file mode 100644
index 0000000..aae764d
--- /dev/null
+++ b/src/styles/block-overrides/_query.scss
@@ -0,0 +1,11 @@
+.wp-block-query {
+ .wp-block-post-featured-image {
+ a {
+ height: 100% !important;
+
+ img {
+ aspect-ratio: 1;
+ }
+ }
+ }
+}
diff --git a/src/styles/components/_footer.scss b/src/styles/components/_footer.scss
new file mode 100644
index 0000000..8648691
--- /dev/null
+++ b/src/styles/components/_footer.scss
@@ -0,0 +1,9 @@
+footer {
+ nav a {
+ font-size: var(--wp--preset--font-size--xs);
+ }
+
+ #copyright {
+ font-size: var(--wp--preset--font-size--xs);
+ }
+}
diff --git a/src/styles/editor.scss b/src/styles/editor.scss
new file mode 100644
index 0000000..66f2b45
--- /dev/null
+++ b/src/styles/editor.scss
@@ -0,0 +1,8 @@
+// Base Styles
+@use "./base/base";
+@use "./base/utilities";
+@use "./components/footer";
+
+// Block Overrides
+@use "./block-overrides/post-excerpt";
+@use "./block-overrides/query";
diff --git a/src/styles/main.scss b/src/styles/main.scss
index 7942a87..d313fc6 100644
--- a/src/styles/main.scss
+++ b/src/styles/main.scss
@@ -1,4 +1,14 @@
@use "./base/base";
@use "./base/utilities";
-@use "./themeco-overrides";
+@use "./base/typography";
+
+// Block Overrides
+@use "./block-overrides/button";
+@use "./block-overrides/post-excerpt";
+@use "./block-overrides/query";
+
+// Plugin Overrides
@use "./plugin-overrides/contact-form-seven";
+
+// Custom Styles
+@use "./components/footer";
diff --git a/src/styles/plugin-overrides/_contact-form-seven.scss b/src/styles/plugin-overrides/_contact-form-seven.scss
index 9b693f9..33ee816 100644
--- a/src/styles/plugin-overrides/_contact-form-seven.scss
+++ b/src/styles/plugin-overrides/_contact-form-seven.scss
@@ -1,36 +1,51 @@
-.wpcf7 * {
- color: white;
-}
+@use "../abstracts/mixins" as m;
+@use "../abstracts/functions" as fn;
-.wpcf7 input,
-.wpcf7 select {
- color: var(--color-primary);
- border-radius: 10px;
- border: 2px solid var(--color-primary);
-}
+.wpcf7 {
+ * {
+ color: white;
+ }
-.wpcf7 input.wpcf7-submit {
- border: 2px solid var(--color-primary);
- border-radius: 100em;
- color: white;
- background-color: var(--color-primary);
- font-size: 1em;
- text-transform: unset;
- padding: 0.575em 0.85em;
- line-height: initial;
-}
+ label {
+ color: var(--wp--preset--color--primary);
+ }
-.wpcf7 input.wpcf7-submit:hover {
- background-color: white;
- transform: translateY(-4px);
- box-shadow: 2px 4px 10px 2px rgb(0 0 0 / 35%);
- transition: all 0.2s ease;
- color: var(--color-primary);
+ input,
+ select {
+ color: var(--wp--preset--color--primary);
+ border-radius: 10px;
+ border: 2px solid var(--wp--preset--color--primary);
+ }
+
+ input.wpcf7-submit {
+ border: 2px solid var(--wp--preset--color--primary);
+ border-radius: 50rem;
+ color: white;
+ background-color: var(--wp--preset--color--primary);
+ font-size: var(--wp--preset--font-size--root);
+ text-transform: unset;
+ padding: var(--wp--preset--spacing--base) var(--wp--preset--spacing--lg);
+ line-height: initial;
+ transition: fn.transition(
+ color,
+ background-color,
+ box-shadow,
+ translate
+ );
+
+ @include m.interaction {
+ background-color: white;
+ box-shadow: var(--btn-hover-box-shadow);
+ translate: var(--btn-hover-translate-x) var(--btn-hover-translate-y);
+ transition: var(--wp--custom--transition--standard);
+ color: var(--wp--preset--color--primary);
+ }
+ }
}
.wpcf7-response-output {
- color: var(--color-primary);
- border-left: 5px solid var(--color-primary);
+ color: var(--wp--preset--color--primary);
+ border-left: 5px solid var(--wp--preset--color--primary);
background-color: white;
- font-size: 1.6rem;
+ font-size: var(--wp--preset--font-size--root);
}
diff --git a/src/styles/vendors/bootstrap.scss b/src/styles/vendors/bootstrap.scss
new file mode 100644
index 0000000..8e2865b
--- /dev/null
+++ b/src/styles/vendors/bootstrap.scss
@@ -0,0 +1,196 @@
+/* stylelint-disable no-invalid-position-at-import-rule */
+@use "sass:map";
+@use "sass:list";
+@use "../abstracts/variables" as v;
+
+// Required
+@import "bootstrap/scss/functions";
+
+/**
+* Colors
+* If you want, you can override the $primary and $secondary here.
+* Also, you can add custom colors with the sass `map` below
+*/
+
+$primary: v.$primary;
+$secondary: v.$secondary;
+
+// ================================================
+
+/**
+* Typography
+*/
+$font-size-base: 1rem;
+$font-family-sans-serif: v.$font-body;
+$font-family-monospace:
+ monaspace neon,
+ "Courier New",
+ monospace;
+$body-color: $primary;
+
+// LINKS
+$link-color: $primary;
+$link-hover-color: rgba($primary, 0.8);
+
+// HEADINGS
+$headings-font-family: v.$font-headings;
+$headings-font-weight: 700;
+$headings-color: $primary;
+
+/**
+* Minor Third Type Scale
+* @see https://typescale.com/
+*/
+
+$h1-font-size: $font-size-base * 2.986;
+$h2-font-size: $font-size-base * 2.488;
+$h3-font-size: $font-size-base * 2.074;
+$h4-font-size: $font-size-base * 1.728;
+$h5-font-size: $font-size-base * 1.44;
+$h6-font-size: $font-size-base * 1.2;
+
+/**
+* Display Type Scale
+* Major Third Type scale
+* @see https://typescale.com
+*/
+$display-font-sizes: (
+ 1: 3.815 * $font-size-base,
+ 2: 3.052 * $font-size-base,
+ 3: 2.441 * $font-size-base,
+ 4: 1.953 * $font-size-base,
+ 5: 1.563 * $font-size-base,
+ 6: 1.25 * $font-size-base,
+);
+$display-font-family: v.$font-headings;
+$display-font-weight: 700;
+$display-line-height: calc(2ex + 4px);
+
+// ================================================
+
+/**
+* Buttons
+*/
+$btn-transition: all v.$transition-standard;
+$input-btn-padding-x: 0.75rem;
+$input-btn-padding-y: 0.375rem;
+$input-btn-padding-x-lg: 1rem;
+$input-btn-padding-y-lg: 0.5rem;
+$input-btn-padding-x-sm: 0.5rem;
+$input-btn-padding-y-sm: 0.25rem;
+
+// ================================================
+
+/**
+* Navbar
+*/
+
+$navbar-light-active-color: $secondary;
+$navbar-light-hover-color: $secondary;
+$navbar-light-color: white;
+$navbar-light-icon-color: white;
+$navbar-light-toggler-border-color: white;
+$navbar-light-brand-color: white;
+$navbar-light-brand-hover-color: $secondary;
+
+// ================================================
+
+/**
+* Offcanvas
+*/
+
+// $offcanvas-bg-color: $primary;
+$offcanvas-color: white;
+
+// ================================================
+
+/**
+* Options
+* @see https://getbootstrap.com/docs/5.3/customize/options/
+*/
+$enable-shadows: false;
+$enable-dark-mode: false;
+
+// ================================================
+
+@import "bootstrap/scss/variables";
+
+// @import "bootstrap/scss/variables-dark";
+
+// Map Overrides Here (Optional)
+
+// Custom Colors to extend the theme with
+$theme-colors: map.merge($theme-colors, v.$custom-colors);
+
+// Required Bootstrap Modules
+@import "bootstrap/scss/maps";
+@import "bootstrap/scss/mixins";
+@import "bootstrap/scss/utilities";
+@import "bootstrap/scss/root";
+@import "bootstrap/scss/reboot";
+
+// Useful Bootstrap Utilities (Optional)
+@import "bootstrap/scss/buttons";
+
+// Override Bootstrap Buttons
+@each $color, $value in $theme-colors {
+ .btn-#{$color},
+ .is-style-btn-#{$color} {
+ @if $color == "primary" {
+ /**
+ * Override the styles with the `button-variant` mixin
+ * @see https://getbootstrap.com/docs/5.3/components/buttons/#sass-loops
+ */
+ @include button-variant($value, $value);
+ } @else {
+ @include button-variant($value, $value);
+ }
+ }
+}
+
+@each $color, $value in $theme-colors {
+ .btn-outline-#{$color},
+ .is-style-btn-outline-#{$color} {
+ @include button-outline-variant($value);
+ }
+}
+
+@import "bootstrap/scss/transitions";
+@import "bootstrap/scss/close";
+@import "bootstrap/scss/containers";
+@import "bootstrap/scss/grid";
+
+// Optional Bootstrap Modules (w/ Dependencies)
+@import "bootstrap/scss/offcanvas";
+@import "bootstrap/scss/type";
+@import "bootstrap/scss/dropdown";
+
+// @import "bootstrap/scss/images";
+// @import "bootstrap/scss/tables";
+// @import "bootstrap/scss/forms";
+
+// @import "bootstrap/scss/button-group";
+@import "bootstrap/scss/nav";
+@import "bootstrap/scss/navbar"; // Requires nav
+@import "bootstrap/scss/card";
+
+// @import "bootstrap/scss/breadcrumb";
+// @import "bootstrap/scss/accordion";
+// @import "bootstrap/scss/pagination";
+// @import "bootstrap/scss/badge";
+// @import "bootstrap/scss/alert";
+// @import "bootstrap/scss/progress";
+// @import "bootstrap/scss/list-group";
+// @import "bootstrap/scss/toasts";
+// @import "bootstrap/scss/modal"; // Requires transitions
+// @import "bootstrap/scss/tooltip";
+// @import "bootstrap/scss/popover";
+// @import "bootstrap/scss/carousel";
+// @import "bootstrap/scss/spinners";
+// @import "bootstrap/scss/placeholders";
+
+// Helpers
+@import "bootstrap/scss/helpers";
+
+// Utilities
+@import "bootstrap/scss/utilities/api";
diff --git a/style.css b/style.css
index d132305..e0f56ab 100644
--- a/style.css
+++ b/style.css
@@ -3,12 +3,11 @@ Theme Name: Macros By Sara WP Theme
Theme URI: https://github.com/macrosbysara/wordpress-theme
Author: KJ Roelke
Author URI: https://kjroelke.online
-Description: A Child Theme of Pro, built for Macros By Sara WordPress site
-Version: 1.3.2
+Description: A WordPress Theme built for Macros By Sara site
+Version: 2.0.0
Requires at least: 6.7.0
Tested up to: 6.8.3
Requires PHP: 8.2
-Template: pro
License: GPL-2.0-or-later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
*/
diff --git a/templates/category.html b/templates/category.html
new file mode 100644
index 0000000..962e0d8
--- /dev/null
+++ b/templates/category.html
@@ -0,0 +1,94 @@
+
+
+
+ in Tips, Tricks & More in Tips, Tricks & More in
+ What I've Written
+
+
+
+
+
+
+
+ What I've Written
+
+
+
+
+
+
+