diff --git a/.gitattributes b/.gitattributes index 4e293fc2..cb17614d 100644 --- a/.gitattributes +++ b/.gitattributes @@ -5,3 +5,8 @@ images/memberlite-banner.jpg export-ignore README.md export-ignore languages export-ignore +docs export-ignore +.rtlcssrc.json export-ignore +package.json export-ignore +src export-ignore +webpack.config.js export-ignore \ No newline at end of file diff --git a/.github/workflows/build-theme-files.yml b/.github/workflows/build-theme-files.yml new file mode 100644 index 00000000..3baacb42 --- /dev/null +++ b/.github/workflows/build-theme-files.yml @@ -0,0 +1,37 @@ +name: Build Stylesheets and Scripts + +on: + push: + branches: + - master # Branch we want to run this + workflow_dispatch: + +jobs: + build-stylesheets: + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v2 + + - name: Set up Node.js + uses: actions/setup-node@v3 + with: + node-version: '18' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Build stylesheets and scripts + run: npm run build + + - name: Commit build folder to master + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add build/ # Replace with your actual build output folder + git diff --staged --quiet || git commit -m "chore: build stylesheets and scripts [skip ci]" + git push origin HEAD:master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 4681dcf3..683d93b3 100644 --- a/.gitignore +++ b/.gitignore @@ -17,13 +17,15 @@ $RECYCLE.BIN/ # npm node_modules -/build # composer vendor composer.js composer.lock +# compiled +build + # Temporary files *~ @@ -33,5 +35,6 @@ log/*.txt tests/_build js/blocks.build.asset.php js/blocks.build.js.map -.vscode/settings.json +.claude +.vscode /tests/_output diff --git a/.rtlcssrc.json b/.rtlcssrc.json new file mode 100644 index 00000000..3268fab4 --- /dev/null +++ b/.rtlcssrc.json @@ -0,0 +1,23 @@ +{ + "autoRename": false, + "autoRenameStrict": false, + "blacklist":{}, + "clean": true, + "greedy": false, + "processUrls": false, + "stringMap": [ + { + "name" : "left-right", + "priority": 100, + "search" : ["left", "Left", "LEFT"], + "replace" : ["right", "Right", "RIGHT"], + "options" : { + "scope" : "value", + "ignoreCase" : false + } + } + ], + "useCalc": false, + "aliases": {}, + "processEnv": true +} diff --git a/adminpages/admin_footer.php b/adminpages/admin_footer.php index 444df67b..5c4be186 100644 --- a/adminpages/admin_footer.php +++ b/adminpages/admin_footer.php @@ -5,5 +5,4 @@ * @since 6.1 */ ?> -
diff --git a/adminpages/admin_header.php b/adminpages/admin_header.php index 69375ad2..8bf72a1c 100644 --- a/adminpages/admin_header.php +++ b/adminpages/admin_header.php @@ -16,6 +16,7 @@ diff --git a/adminpages/menus.php b/adminpages/menus.php new file mode 100644 index 00000000..0e31bbf5 --- /dev/null +++ b/adminpages/menus.php @@ -0,0 +1,477 @@ + 0 ) { + $new_menu_id = memberlite_duplicate_menu( $source_menu_id, $new_menu_name ); + } else { + $new_menu_id = wp_create_nav_menu( $new_menu_name ); + } + + if ( $new_menu_id && ! is_wp_error( $new_menu_id ) ) { + // Redirect back to the custom menus page with success message. + wp_safe_redirect( admin_url( 'admin.php?page=memberlite-custom-menus&created=' . $new_menu_id ) ); + exit; + } else { + wp_safe_redirect( admin_url( 'admin.php?page=memberlite-custom-menus&error=create' ) ); + exit; + } + } + + // Check for duplicate action. + $action = isset( $_GET['action'] ) ? sanitize_key( wp_unslash( $_GET['action'] ) ) : ''; + if ( $action === 'duplicate' && isset( $_GET['menu'] ) ) { + // Verify nonce. + if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( $_GET['_wpnonce'], 'memberlite_duplicate_menu_' . intval( $_GET['menu'] ) ) ) { + wp_die( esc_html__( 'Security check failed.', 'memberlite' ) ); + } + + // Check capabilities. + if ( ! current_user_can( 'edit_theme_options' ) ) { + wp_die( esc_html__( 'You do not have permission to do this.', 'memberlite' ) ); + } + + $menu_id = intval( $_GET['menu'] ); + $source_menu = wp_get_nav_menu_object( $menu_id ); + + if ( $source_menu ) { + /* translators: %s: original menu name */ + $new_menu_name = sprintf( __( '%s (Copy)', 'memberlite' ), $source_menu->name ); + $new_menu_id = memberlite_duplicate_menu( $menu_id, $new_menu_name ); + + if ( $new_menu_id ) { + wp_safe_redirect( admin_url( 'admin.php?page=memberlite-custom-menus&duplicated=' . $new_menu_id ) ); + exit; + } + } + + wp_safe_redirect( admin_url( 'admin.php?page=memberlite-custom-menus&error=duplicate' ) ); + exit; + } + + // Check for delete action. + if ( $action === 'delete' && isset( $_GET['menu'] ) ) { + // Verify nonce. + if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( $_GET['_wpnonce'], 'memberlite_delete_menu_' . intval( $_GET['menu'] ) ) ) { + wp_die( esc_html__( 'Security check failed.', 'memberlite' ) ); + } + + // Check capabilities. + if ( ! current_user_can( 'edit_theme_options' ) ) { + wp_die( esc_html__( 'You do not have permission to do this.', 'memberlite' ) ); + } + + $menu_id = intval( $_GET['menu'] ); + $result = wp_delete_nav_menu( $menu_id ); + + if ( $result && ! is_wp_error( $result ) ) { + wp_safe_redirect( admin_url( 'admin.php?page=memberlite-custom-menus&deleted=1' ) ); + } else { + wp_safe_redirect( admin_url( 'admin.php?page=memberlite-custom-menus&error=delete' ) ); + } + exit; + } +} +add_action( 'admin_init', 'memberlite_process_menu_actions' ); + +/** + * Display admin notices for menu actions on the nav-menus.php page. + * + * @since 7.0 + */ +function memberlite_menu_admin_notices() { + $screen = get_current_screen(); + if ( ! $screen || $screen->base !== 'nav-menus' ) { + return; + } + + if ( isset( $_GET['memberlite_duplicated'] ) ) { + ?> +
+

+
+ +
+

+
+ $menu_id ) { + if ( $menu_id && isset( $locations[ $location ] ) ) { + if ( ! isset( $menu_to_locations[ $menu_id ] ) ) { + $menu_to_locations[ $menu_id ] = array(); + } + $menu_to_locations[ $menu_id ][] = $locations[ $location ]; + } + } + + // Load the Memberlite dashboard-area header. + require_once __DIR__ . '/admin_header.php'; + ?> + +
+ + ', + esc_html( $created_menu->name ), + '' + ); + $class = 'notice-success'; + } + } elseif ( isset( $_GET['duplicated'] ) ) { + $duplicated_menu_id = intval( $_GET['duplicated'] ); + $duplicated_menu = wp_get_nav_menu_object( $duplicated_menu_id ); + if ( $duplicated_menu ) { + $edit_url = admin_url( 'nav-menus.php?action=edit&menu=' . $duplicated_menu_id ); + $message = sprintf( + /* translators: %1$s: opening link tag, %2$s: menu name, %3$s: closing link tag */ + __( 'Menu duplicated successfully. %1$sEdit "%2$s" menu%3$s', 'memberlite' ), + '', + esc_html( $duplicated_menu->name ), + '' + ); + $class = 'notice-success'; + } + } elseif ( isset( $_GET['deleted'] ) ) { + $message = __( 'Menu deleted successfully.', 'memberlite' ); + $class = 'notice-success'; + } elseif ( isset( $_GET['error'] ) ) { + $error = sanitize_key( wp_unslash( $_GET['error'] ) ); + switch ( $error ) { + case 'duplicate': + $message = __( 'There was an error duplicating the menu.', 'memberlite' ); + break; + case 'delete': + $message = __( 'There was an error deleting the menu.', 'memberlite' ); + break; + case 'empty_name': + $message = __( 'Please enter a valid menu name.', 'memberlite' ); + break; + case 'name_exists': + $message = __( 'A menu with that name already exists. Please choose another name.', 'memberlite' ); + break; + case 'create': + $message = __( 'There was an error creating the menu.', 'memberlite' ); + break; + } + } + + // Output notice if needed. + if ( $message ) { + printf( + '

%2$s

', + esc_attr( $class ), + wp_kses( $message, array( 'a' => array( 'href' => array() ) ) ) + ); + } + ?> + +

+

+ + ' . esc_html__( 'Learn more about menus in Memberlite', 'memberlite' ) . ''; + ?> +

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

+
+ + + +

+
+

+ + +

+
+
+
+ +
+
+ +
+
+ +

+ + + + + + + + + + + + term_id ); + $item_count = $item_count ? count( $item_count ) : 0; + $assigned = isset( $menu_to_locations[ $menu->term_id ] ) ? implode( ', ', $menu_to_locations[ $menu->term_id ] ) : '—'; + + $edit_url = admin_url( 'nav-menus.php?action=edit&menu=' . $menu->term_id ); + $duplicate_url = wp_nonce_url( + admin_url( 'admin.php?page=memberlite-custom-menus&action=duplicate&menu=' . $menu->term_id ), + 'memberlite_duplicate_menu_' . $menu->term_id + ); + $delete_url = wp_nonce_url( + admin_url( 'admin.php?page=memberlite-custom-menus&action=delete&menu=' . $menu->term_id ), + 'memberlite_delete_menu_' . $menu->term_id + ); + + $confirm_text = sprintf( + /* translators: %s: menu name */ + esc_js( __( 'Are you sure you want to delete the "%s" menu? This cannot be undone.', 'memberlite' ) ), + esc_js( $menu->name ) + ); + ?> + + + + + + + + +
+ name ); ?> + + name ) ); ?>"> + + + name ) ); ?>"> + + + name ) ); ?>"> + + +
+ +
+
+ + + + $menu_item ) { + $args = array( + 'post_title' => $menu_item->title, + 'post_content' => $menu_item->description, + 'post_excerpt' => $menu_item->attr_title, + 'post_status' => $menu_item->post_status, + 'post_type' => 'nav_menu_item', + 'menu_order' => $index, + 'comment_status' => 'closed', + 'ping_status' => 'closed', + 'tax_input' => array( + 'nav_menu' => array( $new_menu_id ), + ), + ); + + // Copy menu item meta. + $meta_input = array(); + $meta_keys = get_post_meta( $menu_item->db_id ); + + foreach ( $meta_keys as $meta_key => $meta_values ) { + if ( $meta_key === '_menu_item_menu_item_parent' ) { + continue; // Handle parent separately. + } + $meta_value = $meta_values[0]; + if ( is_serialized( $meta_value ) ) { + $meta_value = maybe_unserialize( $meta_value ); + } + $meta_input[ $meta_key ] = $meta_value; + } + + $args['meta_input'] = $meta_input; + + // Insert the new menu item. + $new_item_id = wp_insert_post( $args ); + + // Skip this item if insertion failed. + if ( is_wp_error( $new_item_id ) ) { + continue; + } + + // Store the ID mapping. + $id_map[ $menu_item->db_id ] = $new_item_id; + + // Update parent relationship using the mapped ID. + if ( ! empty( $menu_item->menu_item_parent ) && isset( $id_map[ $menu_item->menu_item_parent ] ) ) { + update_post_meta( $new_item_id, '_menu_item_menu_item_parent', $id_map[ $menu_item->menu_item_parent ] ); + } + } + + return $new_menu_id; +} diff --git a/adminpages/sidebars.php b/adminpages/sidebars.php index fbc30acf..904e3db7 100644 --- a/adminpages/sidebars.php +++ b/adminpages/sidebars.php @@ -229,7 +229,7 @@ function memberlite_custom_sidebars() { 'memberlite_delete_custom_sidebar' ); ?> - + "> $template, ); - $options = array(); + // Export theme mods if selected. + if ( ! empty( $_POST['memberlite_export_theme_mods'] ) ) { + // All theme mods = all Customizer settings for the active theme. + $mods = get_theme_mods(); + if ( ! is_array( $mods ) ) { + $mods = array(); + } + $data['mods'] = $mods; + + /** + * Filter the option keys to export when exporting Memberlite theme settings. + * By default, we export the site icon, custom sidebars, and sidebar assignments for custom post types. + * + * Note: This same filter is used for resetting options in memberlite_reset_theme_settings(). + * + * @since 6.1 + * @param array $option_keys Array of option keys to export. + */ + $option_keys = apply_filters( + 'memberlite_export_option_keys', + array( + 'site_icon', + 'memberlite_cpt_sidebars', + 'memberlite_custom_sidebars', + ) + ); + + $options = array(); + + foreach ( $option_keys as $key ) { + $value = get_option( $key, null ); + if ( null !== $value ) { + $options[ $key ] = $value; + } + } + + $data['options'] = $options; - foreach ( $option_keys as $key ) { - $value = get_option( $key, null ); - if ( null !== $value ) { - $options[ $key ] = $value; + if ( function_exists( 'wp_get_custom_css' ) ) { + $data['wp_css'] = wp_get_custom_css(); } } - $data = array( - 'template' => $template, - 'mods' => $mods, - 'options' => $options, - ); + // Export menus if selected. + if ( ! empty( $_POST['memberlite_export_menus'] ) && ! empty( $_POST['memberlite_export_menu_ids'] ) ) { + $menu_ids = array_map( 'intval', $_POST['memberlite_export_menu_ids'] ); + $menus_data = array(); + + // Get current menu location assignments to export with menus. + $menu_locations = get_nav_menu_locations(); + $menu_id_to_locations = array(); + foreach ( $menu_locations as $location => $assigned_menu_id ) { + if ( ! empty( $assigned_menu_id ) ) { + if ( ! isset( $menu_id_to_locations[ $assigned_menu_id ] ) ) { + $menu_id_to_locations[ $assigned_menu_id ] = array(); + } + $menu_id_to_locations[ $assigned_menu_id ][] = $location; + } + } + + foreach ( $menu_ids as $menu_id ) { + $menu = wp_get_nav_menu_object( $menu_id ); + if ( ! $menu ) { + continue; + } + + $menu_items = wp_get_nav_menu_items( $menu_id ); + $items_data = array(); + + if ( ! empty( $menu_items ) ) { + // Build index map for parent references. + $id_to_index = array(); + foreach ( $menu_items as $index => $item ) { + $id_to_index[ $item->db_id ] = $index; + } + + foreach ( $menu_items as $index => $item ) { + // Determine parent index (null if top-level). + $parent_index = null; + if ( ! empty( $item->menu_item_parent ) && isset( $id_to_index[ $item->menu_item_parent ] ) ) { + $parent_index = $id_to_index[ $item->menu_item_parent ]; + } + + // Make internal URLs relative for portability across environments. + $item_url = $item->url; + $site_url = home_url(); + if ( strpos( $item_url, $site_url ) === 0 ) { + $item_url = substr( $item_url, strlen( $site_url ) ); + // Ensure empty path becomes root slash. + if ( empty( $item_url ) ) { + $item_url = '/'; + } + } + + $items_data[] = array( + 'label' => $item->title, + 'url' => $item_url, + 'target' => $item->target, + 'attr_title' => $item->attr_title, + 'description' => $item->description, + 'classes' => array_filter( (array) $item->classes ), + 'xfn' => $item->xfn, + 'parent' => $parent_index, + ); + } + } + + $menu_export_data = array( + 'name' => $menu->name, + 'items' => $items_data, + ); + + // Include location assignments for this menu. + if ( isset( $menu_id_to_locations[ $menu_id ] ) ) { + $menu_export_data['locations'] = $menu_id_to_locations[ $menu_id ]; + } - if ( function_exists( 'wp_get_custom_css' ) ) { - $data['wp_css'] = wp_get_custom_css(); + $menus_data[] = $menu_export_data; + } + + if ( ! empty( $menus_data ) ) { + $data['menus'] = $menus_data; + } } - $json = wp_json_encode( $data ); + $json = wp_json_encode( $data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES ); if ( false === $json ) { wp_die( esc_html__( 'Error encoding export data.', 'memberlite' ) ); @@ -170,6 +261,7 @@ function memberlite_export_theme_settings() { * Handle Memberlite theme settings import. * * @since 6.1 + * @since 7.0 Added navigation menu import support. */ function memberlite_import_theme_settings() { if ( ! current_user_can( 'edit_theme_options' ) ) { @@ -196,25 +288,70 @@ function memberlite_import_theme_settings() { // Decode JSON created by the export tool. $data = json_decode( $raw, true ); - if ( ! is_array( $data ) || empty( $data['template'] ) || ! isset( $data['mods'] ) ) { + // Validate file structure - must have template and either mods or menus. + if ( ! is_array( $data ) || empty( $data['template'] ) || ( ! isset( $data['mods'] ) && ! isset( $data['menus'] ) ) ) { memberlite_import_settings_redirect( 'invalid_file' ); } - $current_template = get_option( 'stylesheet' ); + $current_template = get_option( 'template' ); + $current_stylesheet = get_option( 'stylesheet' ); - // Ensure the file is for this theme. - if ( $data['template'] !== $current_template ) { + // Ensure the file is for this theme (or a child of the same parent theme). + // Check against both template and stylesheet for backwards compatibility with older exports from parent theme installations. + if ( $data['template'] !== $current_template && $data['template'] !== $current_stylesheet ) { memberlite_import_settings_redirect( 'wrong_theme' ); } - // Overwrite current theme mods. + // Overwrite current theme mods if present. if ( isset( $data['mods'] ) && is_array( $data['mods'] ) ) { // Clear existing mods so we don't leave stale ones behind. remove_theme_mods(); + // Get all color setting keys. + $color_keys = memberlite_get_color_setting_keys(); + + // Deprecated mods to skip on import. + $deprecated_mods = array( 'memberlite_darkcss' ); + foreach ( $data['mods'] as $key => $value ) { + // Skip deprecated settings. + if ( in_array( $key, $deprecated_mods, true ) ) { + continue; + } + + // Sanitize color values to remove # prefix + if ( in_array( $key, $color_keys, true ) && is_string( $value ) ) { + $value = sanitize_hex_color_no_hash( $value ); + + // Skip if sanitization failed (returns null for invalid colors) + if ( $value === null ) { + continue; + } + + // Lowercase for consistency + $value = strtolower( $value ); + } + set_theme_mod( $key, $value ); } + + // Now detect if the imported color scheme is legacy or modern + $imported_scheme = isset( $data['mods']['memberlite_color_scheme'] ) ? $data['mods']['memberlite_color_scheme'] : ''; + $final_scheme = 'custom'; // Default to custom if we can't determine the color scheme + + // Type safety: ensure it's a string before using as array key + if ( is_string( $imported_scheme ) && ! empty( $imported_scheme ) && $imported_scheme !== 'custom' ) { + // Check if it's a modern scheme + $modern_schemes = memberlite_get_color_schemes(); + + if ( isset( $modern_schemes[ $imported_scheme ] ) ) { + // It's a valid modern scheme, keep it as-is + $final_scheme = $imported_scheme; + } + } + + // Anything legacy or a malformed color scheme will default to "custom" + set_theme_mod( 'memberlite_color_scheme', $final_scheme ); } // Restore extra options (site_icon, sidebars, etc.), if present. @@ -229,6 +366,148 @@ function memberlite_import_theme_settings() { wp_update_custom_css_post( $data['wp_css'] ); } + // Import navigation menus if present. + if ( ! empty( $data['menus'] ) && is_array( $data['menus'] ) ) { + // Track location assignments to apply after all menus are created. + $location_assignments = array(); + + // Check if user wants to replace existing menus. + $replace_existing = ! empty( $_POST['memberlite_replace_existing_menus'] ); + + foreach ( $data['menus'] as $menu_data ) { + if ( empty( $menu_data['name'] ) ) { + continue; + } + + $menu_name = sanitize_text_field( $menu_data['name'] ); + + // Check if a menu with this exact name already exists. + $existing_menu = wp_get_nav_menu_object( $menu_name ); + + if ( $existing_menu ) { + if ( $replace_existing ) { + // Replace mode: Delete existing menu items and import new ones into existing menu. + $menu_id = $existing_menu->term_id; + $location_menu_id = $menu_id; + + $existing_items = wp_get_nav_menu_items( $menu_id ); + if ( ! empty( $existing_items ) ) { + foreach ( $existing_items as $item ) { + wp_delete_post( $item->db_id, true ); + } + } + } else { + // Non-replace mode: Create duplicate menu, but assign original to locations. + $location_menu_id = $existing_menu->term_id; + + // Create duplicate with numbered name. + $duplicate_name = $menu_name; + $counter = 1; + while ( wp_get_nav_menu_object( $duplicate_name ) ) { + $counter++; + $duplicate_name = $menu_name . ' (' . $counter . ')'; + } + + $menu_id = wp_create_nav_menu( $duplicate_name ); + + if ( is_wp_error( $menu_id ) ) { + continue; + } + } + } else { + // Menu doesn't exist - create it and use it for locations. + $menu_id = wp_create_nav_menu( $menu_name ); + + if ( is_wp_error( $menu_id ) ) { + continue; + } + + $location_menu_id = $menu_id; + } + + // Track location assignments using the appropriate menu ID. + if ( ! empty( $menu_data['locations'] ) && is_array( $menu_data['locations'] ) ) { + foreach ( $menu_data['locations'] as $location ) { + $location_assignments[ sanitize_key( $location ) ] = $location_menu_id; + } + } + + // Import menu items. + if ( ! empty( $menu_data['items'] ) && is_array( $menu_data['items'] ) ) { + // Map of old index to new menu item ID for parent relationships. + $index_to_new_id = array(); + + foreach ( $menu_data['items'] as $index => $item_data ) { + $label = isset( $item_data['label'] ) ? sanitize_text_field( $item_data['label'] ) : ''; + $url = isset( $item_data['url'] ) ? $item_data['url'] : ''; + + // Convert relative URLs to absolute using the current site URL. + if ( ! empty( $url ) && strpos( $url, '/' ) === 0 && strpos( $url, '//' ) !== 0 ) { + $url = home_url( $url ); + } + + $url = esc_url_raw( $url ); + + if ( empty( $label ) || empty( $url ) ) { + continue; + } + + // Determine parent menu item ID. + $parent_id = 0; + if ( isset( $item_data['parent'] ) && is_int( $item_data['parent'] ) && isset( $index_to_new_id[ $item_data['parent'] ] ) ) { + $parent_id = $index_to_new_id[ $item_data['parent'] ]; + } + + // Prepare menu item data. + $menu_item_data = array( + 'menu-item-title' => $label, + 'menu-item-url' => $url, + 'menu-item-status' => 'publish', + 'menu-item-type' => 'custom', + 'menu-item-parent-id' => $parent_id, + ); + + // Add optional fields if present. + if ( ! empty( $item_data['target'] ) ) { + $menu_item_data['menu-item-target'] = sanitize_text_field( $item_data['target'] ); + } + + if ( ! empty( $item_data['attr_title'] ) ) { + $menu_item_data['menu-item-attr-title'] = sanitize_text_field( $item_data['attr_title'] ); + } + + if ( ! empty( $item_data['description'] ) ) { + $menu_item_data['menu-item-description'] = sanitize_textarea_field( $item_data['description'] ); + } + + if ( ! empty( $item_data['classes'] ) && is_array( $item_data['classes'] ) ) { + $menu_item_data['menu-item-classes'] = implode( ' ', array_map( 'sanitize_html_class', $item_data['classes'] ) ); + } + + if ( ! empty( $item_data['xfn'] ) ) { + $menu_item_data['menu-item-xfn'] = sanitize_text_field( $item_data['xfn'] ); + } + + // Insert the menu item. + $new_item_id = wp_update_nav_menu_item( $menu_id, 0, $menu_item_data ); + + if ( ! is_wp_error( $new_item_id ) ) { + $index_to_new_id[ $index ] = $new_item_id; + } + } + } + } + + // Apply menu location assignments after all menus are created. + if ( ! empty( $location_assignments ) ) { + $current_locations = get_nav_menu_locations(); + foreach ( $location_assignments as $location => $menu_id ) { + $current_locations[ $location ] = $menu_id; + } + set_theme_mod( 'nav_menu_locations', $current_locations ); + } + } + memberlite_import_settings_redirect( 'import_ok' ); } add_action( 'admin_post_memberlite_import_theme_settings', 'memberlite_import_theme_settings' ); diff --git a/adminpages/tools/export-settings.php b/adminpages/tools/export-settings.php index e32bbbcb..d9d2b9ed 100644 --- a/adminpages/tools/export-settings.php +++ b/adminpages/tools/export-settings.php @@ -8,6 +8,8 @@ if ( ! defined( 'ABSPATH' ) ) { exit; } + +$menus = wp_get_nav_menus(); ?>
@@ -18,11 +20,110 @@

-

- - - -

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

+
+
+ +

+ +
+ +

+
+ 5 ) { + $classes[] = 'memberlite_scrollable'; + } + $class = implode( ' ', $classes ); + ?> + + + +
+

+ +

+

Export in the WordPress admin.', 'memberlite' ); ?>

diff --git a/adminpages/tools/import-settings.php b/adminpages/tools/import-settings.php index dfa754e9..862681ea 100644 --- a/adminpages/tools/import-settings.php +++ b/adminpages/tools/import-settings.php @@ -18,12 +18,12 @@

-

+

- + + + +
+ @@ -33,6 +33,20 @@

+
+ +

+ +

+
+

diff --git a/archive.php b/archive.php index 28a0c337..25aa392e 100644 --- a/archive.php +++ b/archive.php @@ -6,7 +6,7 @@ */ get_header(); ?> - +

@@ -31,7 +31,7 @@ if ( $content_archives === 'grid' ) { get_template_part( 'components/post/content', 'grid' ); } else { - get_template_part( 'components/post/content', get_post_format() ); + get_template_part( 'components/post/content' ); } ?> @@ -40,13 +40,14 @@ - +
- + +
diff --git a/assets/images/pmpro-icon.svg b/assets/images/pmpro-icon.svg new file mode 100644 index 00000000..b8a92d51 --- /dev/null +++ b/assets/images/pmpro-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/components/footer/footer-back-to-top.php b/components/footer/footer-back-to-top.php new file mode 100644 index 00000000..81e7d852 --- /dev/null +++ b/components/footer/footer-back-to-top.php @@ -0,0 +1,24 @@ + array( + 'class' => array(), + ), + ); + $back_to_top = apply_filters( 'memberlite_back_to_top', ' ' . esc_html__( 'Back to Top', 'memberlite' ) ); + if ( ! empty( $back_to_top ) ) { + echo ''; + } +} diff --git a/components/footer/footer-navigation.php b/components/footer/footer-navigation.php new file mode 100644 index 00000000..b586b9ed --- /dev/null +++ b/components/footer/footer-navigation.php @@ -0,0 +1,25 @@ + + + ' . Memberlite_Customize::sanitize_text_with_links( $copyright_textbox ) . '

'; // WPCS: xss ok. +} diff --git a/components/footer/footer-widgets.php b/components/footer/footer-widgets.php index 45a91e2d..475b68fd 100644 --- a/components/footer/footer-widgets.php +++ b/components/footer/footer-widgets.php @@ -1,20 +1,18 @@ - - - - +if ( is_active_sidebar( 'sidebar-4' ) ) { ?> - - - + - - - -
- -
- ' . Memberlite_Customize::sanitize_text_with_links( $copyright_textbox ) . '

'; // WPCS: xss ok. - } - ?> -
- -
- array( - 'class' => array(), - ), - ); - $back_to_top = apply_filters( 'memberlite_back_to_top', ' ' . esc_html__( 'Back to Top', 'memberlite' ) ); - if ( ! empty( $back_to_top ) ) { - echo ''; - } - ?> -
- -
- - diff --git a/components/footer/variation-default.php b/components/footer/variation-default.php new file mode 100644 index 00000000..8977067f --- /dev/null +++ b/components/footer/variation-default.php @@ -0,0 +1,38 @@ + + + + + + + + + + + + +
+ + + + + +
+ + diff --git a/components/header/header-masthead.php b/components/header/header-masthead.php new file mode 100644 index 00000000..8c0d7279 --- /dev/null +++ b/components/header/header-masthead.php @@ -0,0 +1,108 @@ + + + + + + + + +
+ + +
+ +
+
+ + + + + + + + + + + + + + + + + +
+
+ +
+ +
+ + + + + + diff --git a/components/header/header-member-info.php b/components/header/header-member-info.php new file mode 100644 index 00000000..5cf3da2e --- /dev/null +++ b/components/header/header-member-info.php @@ -0,0 +1,59 @@ + + +
+
+ ID ) { + $get_account_url = ! empty( $pmpro_pages ) ? pmpro_url( 'account' ) : admin_url( 'profile.php' ); + $user_account_link = '' . esc_html( preg_replace( '/\@.*/', '', $current_user->display_name ) ) . ''; + ?> + + + + + 'member', + 'container' => 'nav', + 'container_id' => 'member-navigation', + 'container_class' => 'member-navigation', + 'fallback_cb' => 'memberlite_member_menu_cb', + 'items_wrap' => '
    %3$s
', + 'walker' => new Memberlite_Aria_Walker_Nav_Menu(), + 'depth' => 2, + ) + ); + } else { + wp_nav_menu( + array( + 'theme_location' => 'member-logged-out', + 'container' => 'nav', + 'container_id' => 'member-navigation', + 'container_class' => 'member-navigation', + 'fallback_cb' => 'memberlite_member_menu_cb', + 'items_wrap' => '
    %3$s
', + 'walker' => new Memberlite_Aria_Walker_Nav_Menu(), + ) + ); + } ?> +
+
diff --git a/components/header/header-mobile-menu.php b/components/header/header-mobile-menu.php new file mode 100644 index 00000000..71139e6d --- /dev/null +++ b/components/header/header-mobile-menu.php @@ -0,0 +1,44 @@ + + + + + + + + diff --git a/components/header/header-site-navigation.php b/components/header/header-site-navigation.php new file mode 100644 index 00000000..02e2d73d --- /dev/null +++ b/components/header/header-site-navigation.php @@ -0,0 +1,34 @@ + + - - - - - - - -
-
-
- - - - - - - - - - - - - -
-
-
- - - - - - diff --git a/components/header/meta-member.php b/components/header/meta-member.php deleted file mode 100644 index dfb19c95..00000000 --- a/components/header/meta-member.php +++ /dev/null @@ -1,54 +0,0 @@ - - -
-
- ' . esc_html( preg_replace( '/\@.*/', '', $current_user->display_name ) ) . ''; - } else { - $user_account_link = '' . esc_html( preg_replace( '/\@.*/', '', $current_user->display_name ) ) . ''; - } - ?> - - - - 'member', - 'container' => 'nav', - 'container_id' => 'member-navigation', - 'container_class' => 'member-navigation', - 'fallback_cb' => 'memberlite_member_menu_cb', - 'items_wrap' => '
    %3$s
', - ) - ); - } else { - wp_nav_menu( - array( - 'theme_location' => 'member-logged-out', - 'container' => 'nav', - 'container_id' => 'member-navigation', - 'container_class' => 'member-navigation', - 'fallback_cb' => 'memberlite_member_menu_cb', - 'items_wrap' => '
    %3$s
', - ) - ); - } - ?> -
-
diff --git a/components/header/mobile-menu.php b/components/header/mobile-menu.php deleted file mode 100644 index f9688066..00000000 --- a/components/header/mobile-menu.php +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - diff --git a/components/header/variation-default.php b/components/header/variation-default.php new file mode 100644 index 00000000..40e2cf4a --- /dev/null +++ b/components/header/variation-default.php @@ -0,0 +1,61 @@ + + +
+ +
+ + + +
+ + +
+ +
+ + +
+ + +
+ + + +
+ +
+ +
+ + + + diff --git a/components/page/content-interstitial.php b/components/page/content-interstitial.php deleted file mode 100644 index 72d6904f..00000000 --- a/components/page/content-interstitial.php +++ /dev/null @@ -1,33 +0,0 @@ - - -
> -
- - - '', - ) - ); - ?> -
- ID ) ) { - ?> -
- ', '' ); ?> -
- -
diff --git a/components/page/content-landing.php b/components/page/content-landing.php index ff91f889..42eba12c 100644 --- a/components/page/content-landing.php +++ b/components/page/content-landing.php @@ -1,12 +1,15 @@ -
> +
>
@@ -18,15 +21,13 @@ ) ); ?> - + + + +
- ID ) ) { ?> +
', '' ); ?>
diff --git a/components/page/content-page.php b/components/page/content-page.php index e8453271..e2d82817 100644 --- a/components/page/content-page.php +++ b/components/page/content-page.php @@ -1,12 +1,15 @@ -
> +
aria-labelledby="page-title">
@@ -19,16 +22,11 @@ ); ?> - +
- post_type ) && current_user_can( 'edit_post', $post->ID ) ) { ?> +
', '' ); ?>
diff --git a/components/post/content-audio.php b/components/post/content-audio.php deleted file mode 100644 index b417afb9..00000000 --- a/components/post/content-audio.php +++ /dev/null @@ -1,51 +0,0 @@ - - - - -
> - -
- - - '', - ) - ); - ?> -
- -
- ID ) ) { - ?> -
- - ', '' ); ?> -
- -
diff --git a/components/post/content-grid.php b/components/post/content-grid.php index b6243dcc..78d0a5d9 100644 --- a/components/post/content-grid.php +++ b/components/post/content-grid.php @@ -1,23 +1,25 @@ - +global $memberlite_defaults; ?>
> 'aligncenter' ), get_the_ID() ); - if ( ! empty( $memberlite_get_banner_image ) ) { - // NOTE: The HTML is generated and escaped by the wp_get_attachment_image() function /inc/extras.php. - echo $memberlite_get_banner_image; // WPCS: xss ok. + // Use the dedicated banner image if available, otherwise fall back to the featured image. + $grid_image = memberlite_get_banner_image( get_the_ID(), 'large', false, array( 'class' => 'aligncenter' ) ); + if ( empty( $grid_image ) ) { + $grid_image = get_the_post_thumbnail( get_the_ID(), 'large', array( 'class' => 'aligncenter' ) ); + } + if ( ! empty( $grid_image ) ) { + echo $grid_image; // WPCS: xss ok. } else { ?>
- -
> - -
- - 'aligncenter', - ) - ); ?> -
- -
- ID ) ) { - ?> -
- - ', '' ); ?> -
- -
diff --git a/components/post/content-link.php b/components/post/content-link.php deleted file mode 100644 index 7cde5669..00000000 --- a/components/post/content-link.php +++ /dev/null @@ -1,28 +0,0 @@ - - -
> - - ID ) ) { - ?> -
- - ', '' ); ?> -
- -
diff --git a/components/post/content-none.php b/components/post/content-none.php index f456a621..ea5a6f77 100644 --- a/components/post/content-none.php +++ b/components/post/content-none.php @@ -1,8 +1,9 @@
diff --git a/components/post/content-quote.php b/components/post/content-quote.php deleted file mode 100644 index 38a20d3c..00000000 --- a/components/post/content-quote.php +++ /dev/null @@ -1,57 +0,0 @@ - - - - - diff --git a/components/post/content-single.php b/components/post/content-single.php deleted file mode 100644 index 08fa4bde..00000000 --- a/components/post/content-single.php +++ /dev/null @@ -1,57 +0,0 @@ - - - - -
> -
- - - 'aligncenter', - ) - ); - } - ?> - - - '', - ) - ); - ?> - - -
- ID ) ) { ?> -
- - ', '' ); ?> -
- -
diff --git a/components/post/content-status.php b/components/post/content-status.php deleted file mode 100644 index 622ba82d..00000000 --- a/components/post/content-status.php +++ /dev/null @@ -1,37 +0,0 @@ - - -
> -
- - post_author; ?> - - -
- -
- - ID ) ) { - ?> -
- -
- -
diff --git a/components/post/content-video.php b/components/post/content-video.php deleted file mode 100644 index 35b31bc7..00000000 --- a/components/post/content-video.php +++ /dev/null @@ -1,53 +0,0 @@ - - - - -
> - -
- - - '', - ) - ); - ?> -
- -
- ID ) ) { - ?> -
- -
- -
diff --git a/components/post/content.php b/components/post/content.php index 82297f79..bdce1798 100644 --- a/components/post/content.php +++ b/components/post/content.php @@ -1,64 +1,79 @@ - +
> -
> - - +
- - 'alignright', - ) - ); + ID ); + if ( $thumbnail_id ) { + $alt = get_post_meta( $thumbnail_id, '_wp_attachment_image_alt', true ); } + the_post_thumbnail( + 'thumbnail', + array( + 'class' => 'alignright', + 'alt' => esc_attr( $alt ), + ) + ); + } + + if ( is_archive() || ( is_home() && get_post_type() === 'post' ) ) { + // If we're on an archive or the blog posts list that isn't an archive. $content_archives = get_theme_mod( 'content_archives', $memberlite_defaults['content_archives'] ); - if ( $content_archives == 'excerpt' ) { + if ( $content_archives === 'excerpt' ) { memberlite_the_excerpt(); } else { memberlite_more_content(); - $author_block = get_theme_mod( 'author_block', $memberlite_defaults['author_block'] ); - if ( ! empty( $author_block ) ) { - get_template_part( 'components/post/entry', 'author-block' ); - } + get_template_part( 'components/post/post', 'author-block' ); } + } else { + the_content(); - wp_link_pages( - array( - 'before' => '', - ) - ); - ?> -
- -
+ if ( is_single() ) { + // Only show author blocks on single posts if enabled + get_template_part( 'components/post/post', 'author-block' ); + } + } + + wp_link_pages( + array( + 'before' => '', + ) + ); -
- - - + if ( is_single() ) { + do_action( 'memberlite_after_content_single' ); + } else { + do_action( 'memberlite_after_content_post' ); + } ?> +
- ', '' ); ?> - +
diff --git a/components/post/entry-author-block.php b/components/post/entry-author-block.php deleted file mode 100644 index 30e8a054..00000000 --- a/components/post/entry-author-block.php +++ /dev/null @@ -1,25 +0,0 @@ - - diff --git a/components/post/entry-footer.php b/components/post/entry-footer.php new file mode 100644 index 00000000..d36d43fa --- /dev/null +++ b/components/post/entry-footer.php @@ -0,0 +1,23 @@ +ID ) ) { ?> +
+ + ', '' ); ?> +
+ diff --git a/components/post/entry-header.php b/components/post/entry-header.php index 9b55f3bb..38ce1245 100644 --- a/components/post/entry-header.php +++ b/components/post/entry-header.php @@ -1,72 +1,59 @@ -ID, 'banner' ); - } +if ( $content_archives != 'grid' || is_search() ) { + if ( memberlite_should_show_banner_image() ) { + $memberlite_get_banner_image_src = memberlite_get_banner_image_src( $post->ID, 'banner' ); + } - if ( ! empty( $memberlite_get_banner_image_src ) ) { ?> -
- + if ( ! empty( $memberlite_get_banner_image_src ) ) { ?> +
+ + - 'alignright', - ) - ); - } - ?> + - +
+ array( + 'class' => array(), + ), + 'img' => array( + 'alt' => array(), + 'class' => array(), + 'height' => array(), + 'loading' => array(), + 'src' => array(), + 'title' => array(), + 'width' => array() + ), + 'noscript' => array() + ); + $author_avatar = memberlite_get_author_avatar( $post->post_author ); + echo empty( $author_avatar ) ? '' : wp_kses( $author_avatar, $author_avatar_allowed_html ); + ?> +
+ ', esc_url( get_permalink() ) ), '' ); ?> -
- array( - 'class' => array(), - ), - 'img' => array( - 'alt' => array(), - 'class' => array(), - 'height' => array(), - 'loading' => array(), - 'src' => array(), - 'title' => array(), - 'width' => array() - ), - 'noscript' => array() - ); - $author_avatar = memberlite_get_author_avatar( $post->post_author ); - echo empty( $author_avatar ) ? '' : wp_kses( $author_avatar, $author_avatar_allowed_html ); - ?> -
- - ', esc_url( get_permalink() ) ), '' ); ?> - - ', esc_url( get_permalink() ) ), '' ); ?> - - - - -
-
+ + + +
+
- -
- + +
+ diff --git a/components/post/post-author-block.php b/components/post/post-author-block.php new file mode 100644 index 00000000..7ac403fe --- /dev/null +++ b/components/post/post-author-block.php @@ -0,0 +1,35 @@ + + + diff --git a/components/post/post-comments.php b/components/post/post-comments.php new file mode 100644 index 00000000..963b3e3f --- /dev/null +++ b/components/post/post-comments.php @@ -0,0 +1,14 @@ + a, -.meta-navigation ul.sub-menu li:hover > a, -.header-right .widget_nav_menu ul.sub-menu li:hover > a { - background: #FFFFFF11; -} -.grid-list > * { - background-color: #FFFFFF11; - border-color: #000000CC; -} -.grid-list h1 a, -.grid-list h2 a { - color: #FFFFFFCC; -} -.grid-list h1 a:hover, -.grid-list h2 a:hover { - color: #FFF; -} -.grid-list a.post-thumbnail-link .post-thumbnail-empty { - background-color: #00000033; -} -.comment, -.comment.depth-1.thread-alt, -.comment .comment.alt, -.comment.odd .child-comments .comment { - background: none; -} -mark, -ins { - color: #000; -} -.search-form input[type=search], -#mobile-navigation .search-form input[type=search] { - background: #FFFFFF11; - color: #FFFFFFCC; -} -.content-area input[type=text], -.content-area input[type=email], -.content-area input[type=url], -.content-area input[type=password], -.content-area input[type=search], -.content-area textarea, -#secondary input[type=text], -#secondary input[type=email], -#secondary input[type=url], -#secondary input[type=password], -#secondary input[type=search], -#secondary textarea, -.footer-widgets input[type=text], -.footer-widgets input[type=email], -.footer-widgets input[type=url], -.footer-widgets input[type=password], -.footer-widgets input[type=search], -.footer-widgets textarea, -#mobile-navigation input[type=text], -#mobile-navigation input[type=password] { - color: #FFF; - background: none; - border-color: #FFFFFF80; -} - -/* Comments */ -.comment, -.pingback, -.trackback { - border-color: #FFFFFF19; -} -.comment, -.pingback.even, -.trackback.event { - background: none; -} -.comment.depth-1.thread-alt, -.comment .comment.alt { - background: #FFFFFF19; -} -.comment.even .child-comments .comment { - background: none; -} -.comment.odd .child-comments .comment { - background: #FFFFFF19; -} -.comment-meta .comment-author.vcard { - color: #FFF; -} -.comment-meta .comment-metadata .edit-link { - border-left-color: #FFFFFF4C; -} -.comment-respond .comment-notes, .comment-respond .form-allowed-tags { - color: #FFFFFFCC; -} - -/*-------------------------------------------------------------- -bbPress specific CSS ---------------------------------------------------------------*/ -.bbp-pagination { - color: #AAA; -} -#bbpress-forums div.even, -#bbpress-forums ul.odd, -#bbpress-forums div.even, -#bbpress-forums ul.even { - background: none; -} diff --git a/css/editor.css b/css/editor.css index a868ccbb..8838530f 100644 --- a/css/editor.css +++ b/css/editor.css @@ -1 +1,7 @@ /** Block Editor Custom Styles **/ + +/* Custom Settings Panel Icons */ +.memberlite-custom-settings .components-panel__body-title button svg { + order: -1; + margin-right: 5px; +} diff --git a/css/grid.css b/css/grid.css deleted file mode 100644 index e28ac65b..00000000 --- a/css/grid.css +++ /dev/null @@ -1,157 +0,0 @@ -/* Modern Flexbox Grid System */ -.row { - display: flex; - flex-wrap: wrap; - width: 100%; - margin: 0 auto; - max-width: 71em; -} - -/* Collapse Rows (removes padding between columns) */ -.row.collapse > .column, -.row.collapse > .columns { - padding-left: 0; - padding-right: 0; -} - -/* Nested Rows */ -.row .row { - width: auto; - margin-left: -2.35rem; - margin-right: -2.35rem; - max-width: none; - display: flex; - flex-wrap: wrap; -} - -/* Columns */ -.column, -.columns { - padding-left: 2.35rem; - padding-right: 2.35rem; - width: 100%; - flex: 0 0 100%; - max-width: 100%; -} - -/* Force stacking on small screens */ -@media only screen and (max-width: 40.062em) { - .row { - display: block; /* Stacks columns by default */ - } - .column, .columns { - width: 100%; - max-width: 100%; - } -} - -/* Responsive Grid Sizes */ -@media only screen and (min-width: 40.063em) { - .medium-1 { flex: 0 0 8.33333%; max-width: 8.33333%; } - .medium-2 { flex: 0 0 16.66667%; max-width: 16.66667%; } - .medium-3 { flex: 0 0 25%; max-width: 25%; } - .medium-4 { flex: 0 0 33.33333%; max-width: 33.33333%; } - .medium-5 { flex: 0 0 41.66667%; max-width: 41.66667%; } - .medium-6 { flex: 0 0 50%; max-width: 50%; } - .medium-7 { flex: 0 0 58.33333%; max-width: 58.33333%; } - .medium-8 { flex: 0 0 66.66667%; max-width: 66.66667%; } - .medium-9 { flex: 0 0 75%; max-width: 75%; } - .medium-10 { flex: 0 0 83.33333%; max-width: 83.33333%; } - .medium-11 { flex: 0 0 91.66667%; max-width: 91.66667%; } - .medium-12 { flex: 0 0 100%; max-width: 100%; } -} - -@media only screen and (min-width: 64.063em) { - .large-1 { flex: 0 0 8.33333%; max-width: 8.33333%; } - .large-2 { flex: 0 0 16.66667%; max-width: 16.66667%; } - .large-3 { flex: 0 0 25%; max-width: 25%; } - .large-4 { flex: 0 0 33.33333%; max-width: 33.33333%; } - .large-5 { flex: 0 0 41.66667%; max-width: 41.66667%; } - .large-6 { flex: 0 0 50%; max-width: 50%; } - .large-7 { flex: 0 0 58.33333%; max-width: 58.33333%; } - .large-8 { flex: 0 0 66.66667%; max-width: 66.66667%; } - .large-9 { flex: 0 0 75%; max-width: 75%; } - .large-10 { flex: 0 0 83.33333%; max-width: 83.33333%; } - .large-11 { flex: 0 0 91.66667%; max-width: 91.66667%; } - .large-12 { flex: 0 0 100%; max-width: 100%; } -} - -/* Offsets (Restored for all breakpoints) */ -.small-offset-0 { margin-left: 0%; } -.small-offset-1 { margin-left: 8.33333%; } -.small-offset-2 { margin-left: 16.66667%; } -.small-offset-3 { margin-left: 25%; } -.small-offset-4 { margin-left: 33.33333%; } -.small-offset-5 { margin-left: 41.66667%; } -.small-offset-6 { margin-left: 50%; } -.small-offset-7 { margin-left: 58.33333%; } -.small-offset-8 { margin-left: 66.66667%; } -.small-offset-9 { margin-left: 75%; } -.small-offset-10 { margin-left: 83.33333%; } -.small-offset-11 { margin-left: 91.66667%; } - -@media only screen and (min-width: 40.063em) { - .medium-offset-0 { margin-left: 0%; } - .medium-offset-1 { margin-left: 8.33333%; } - .medium-offset-2 { margin-left: 16.66667%; } - .medium-offset-3 { margin-left: 25%; } - .medium-offset-4 { margin-left: 33.33333%; } - .medium-offset-5 { margin-left: 41.66667%; } - .medium-offset-6 { margin-left: 50%; } - .medium-offset-7 { margin-left: 58.33333%; } - .medium-offset-8 { margin-left: 66.66667%; } - .medium-offset-9 { margin-left: 75%; } - .medium-offset-10 { margin-left: 83.33333%; } - .medium-offset-11 { margin-left: 91.66667%; } -} - -@media only screen and (min-width: 64.063em) { - .large-offset-0 { margin-left: 0%; } - .large-offset-1 { margin-left: 8.33333%; } - .large-offset-2 { margin-left: 16.66667%; } - .large-offset-3 { margin-left: 25%; } - .large-offset-4 { margin-left: 33.33333%; } - .large-offset-5 { margin-left: 41.66667%; } - .large-offset-6 { margin-left: 50%; } - .large-offset-7 { margin-left: 58.33333%; } - .large-offset-8 { margin-left: 66.66667%; } - .large-offset-9 { margin-left: 75%; } - .large-offset-10 { margin-left: 83.33333%; } - .large-offset-11 { margin-left: 91.66667%; } -} - -/* Push/Pull System (Using Flexbox `order`) */ -.push-1 { order: 1; } -.push-2 { order: 2; } -.push-3 { order: 3; } -.push-4 { order: 4; } -.push-5 { order: 5; } -.push-6 { order: 6; } -.push-7 { order: 7; } -.push-8 { order: 8; } -.push-9 { order: 9; } -.push-10 { order: 10; } -.push-11 { order: 11; } - -.pull-1 { order: -1; } -.pull-2 { order: -2; } -.pull-3 { order: -3; } -.pull-4 { order: -4; } -.pull-5 { order: -5; } -.pull-6 { order: -6; } -.pull-7 { order: -7; } -.pull-8 { order: -8; } -.pull-9 { order: -9; } -.pull-10 { order: -10; } -.pull-11 { order: -11; } - -/* Utility Classes */ -.row.align-center { justify-content: center; } -.row.align-right { justify-content: flex-end; } -.row.align-justify { justify-content: space-between; } -.row.align-spaced { justify-content: space-around; } - -.column.align-center { align-self: center; } -.column.align-right { align-self: flex-end; } -.column.align-top { align-self: flex-start; } -.column.align-bottom { align-self: flex-end; } diff --git a/css/lifterlms.css b/css/lifterlms.css index 64c92149..85be65b5 100644 --- a/css/lifterlms.css +++ b/css/lifterlms.css @@ -289,10 +289,10 @@ a.llms-button-secondary { /* notices */ .llms-notice { - background: #EBF5FF; - border: 1px solid #1E429F11; + background: #ebf5ff; + border: 1px solid #1e429f11; border-radius: 8px; - color: #1E429F; + color: #1e429f; font-size: 1.8rem; font-weight: 400; line-height: 2.9rem; @@ -305,15 +305,15 @@ a.llms-button-secondary { } .llms-notice.llms-error { - background: #FDF2F2; - border-color: #9B1C1C11; - color: #9B1C1C; + background: #fdf2f2; + border-color: #9b1c1c11; + color: #9b1c1c; } .llms-notice.llms-success { - background: #F3FAF7; - border-color: #03543F11; - color: #03543F; + background: #f3faf7; + border-color: #03543f11; + color: #03543f; } /* notifications */ @@ -562,27 +562,27 @@ a.llms-button-secondary { } .single-llms_quiz .llms-quiz-buttons form { - align-items: top; - display: flex; - flex-direction: row; + align-items: top; + display: flex; + flex-direction: row; gap: 2.9rem; - margin-top: 2.9rem; + margin-top: 2.9rem; } .single-llms_quiz .llms-quiz-results .llms-donut.passing { - color: #03543F; + color: #03543f; } .single-llms_quiz .llms-quiz-results .llms-donut.passing svg path { - stroke: #03543F; + stroke: #03543f; } .single-llms_quiz .llms-quiz-results .llms-donut.failing { - color: #9B1C1C; + color: #9b1c1c; } .single-llms_quiz .llms-quiz-results .llms-donut.failing svg path { - stroke: #9B1C1C; + stroke: #9b1c1c; } .single-llms_quiz #llms-quiz-wrapper .llms-quiz-attempt-results .llms-quiz-attempt-question .llms-question-title { @@ -593,20 +593,20 @@ a.llms-button-secondary { } .single-llms_quiz #llms-quiz-wrapper .llms-quiz-attempt-results .llms-quiz-attempt-question.status--graded.correct { - background: #F3FAF7; + background: #f3faf7; } .single-llms_quiz #llms-quiz-wrapper .llms-quiz-attempt-results .llms-quiz-attempt-question.status--graded.correct .llms-status-icon { - background-color: #03543F; + background-color: #03543f; color: var(--memberlite-color-white); } .single-llms_quiz #llms-quiz-wrapper .llms-quiz-attempt-results .llms-quiz-attempt-question.status--graded.incorrect { - background: #FDF2F2; + background: #fdf2f2; } .single-llms_quiz #llms-quiz-wrapper .llms-quiz-attempt-results .llms-quiz-attempt-question.status--graded.incorrect .llms-status-icon { - background-color: #9B1C1C; + background-color: #9b1c1c; color: var(--memberlite-color-white); } @@ -692,9 +692,9 @@ a.llms-button-secondary { .llms-student-dashboard .llms-status.llms-completed, .llms-student-dashboard .llms-status.llms-pass, .llms-student-dashboard .llms-status.llms-txn-succeeded { - background: #F3FAF7; - border: 1px solid #03543F11; - color: #03543F; + background: #f3faf7; + border: 1px solid #03543f11; + color: #03543f; } .llms-student-dashboard .llms-login, diff --git a/css/memberlite-shortcodes.css b/css/memberlite-shortcodes.css deleted file mode 100755 index f444a2f9..00000000 --- a/css/memberlite-shortcodes.css +++ /dev/null @@ -1,115 +0,0 @@ -@charset "UTF-8"; -/* CSS Document */ - -/* [memberlite_banner] Shortcode */ -.banner { - padding: 5.8rem 0; -} -.banner .text-center { - text-align: center; -} -.banner .text-right { - text-align: right; -} -.banner .text-left { - text-align: left; -} -.banner.banner_background-image { - background-repeat: no-repeat; - background-size: cover; -} - -/* [memberlite_accordion] Shortcode */ -.memberlite_accordion {} -.memberlite_accordion h2, -.entry-content .memberlite_accordion h2 { - border-top: 1px solid #CCC; - cursor: pointer; - margin: 0; - padding: 1.45rem; -} -.memberlite_accordion h2:before { - content: "\f078"; - float: right; - font-family: 'Font Awesome 5 Free'; - font-weight: 700; - margin: 0 1.45rem; -} -.memberlite_accordion-item.memberlite_accordion-active h2:before { - content: "\f077"; -} -.memberlite_accordion .memberlite_accordion-item-content { - display: none; - margin: 0; - padding: 0 1.45rem 1.45rem 1.45rem; -} -.memberlite_accordion-item:first-child .memberlite_accordion-item-content { - display: block; -} - -/* [memberlite_recent_posts] Shortcode */ -#widget_memberlite_recent_posts .post.hentry { - margin-bottom: 0; -} -#widget_memberlite_recent_posts .widget_has_thumbnail .post.hentry .entry-header { - display: grid; - grid-template-columns: 80px 1.45rem auto; - grid-template-areas: - "thumbnail . title" - "thumbnail . date"; - margin-bottom: 1.45rem; -} -#widget_memberlite_recent_posts .widget_no_thumbnail .post.hentry .entry-header { - margin-bottom: 1.45rem; -} -#widget_memberlite_recent_posts .post.hentry .entry-header .entry-title { - margin: 0; -} -#widget_memberlite_recent_posts .widget_has_thumbnail .post.hentry .entry-header .entry-title { - align-self: end; - grid-area: title; -} -#widget_memberlite_recent_posts .post.hentry .entry-header .post-date { - color: #AAA; -} -.banner #widget_memberlite_recent_posts .widget_has_thumbnail .post.hentry .entry-header .post-date { - color: #EEE; -} -#widget_memberlite_recent_posts .widget_has_thumbnail .post.hentry .entry-header .post-date { - align-self: start; - grid-area: date; -} -#widget_memberlite_recent_posts .widget_has_thumbnail .post.hentry .entry-header .widget_post_thumbnail { - border-radius: 50%; - grid-area: thumbnail; - height: auto; - width: 100%; -} -#widget_memberlite_recent_posts .widget_has_thumbnail .post.hentry .entry-header .widget_post_thumbnail img { - border-radius: 50%; - margin: 0; -} -#widget_memberlite_recent_posts .hentry.format-audio h4.entry-title:before, -#widget_memberlite_recent_posts .hentry.format-image h4.entry-title:before, -#widget_memberlite_recent_posts .hentry.format-link h4.entry-title:before, -#widget_memberlite_recent_posts .hentry.format-video h4.entry-title:before { - font-family: 'Font Awesome 5 Free'; - padding-right: 10px; -} -#widget_memberlite_recent_posts .hentry.format-audio h4.entry-title:before { - content: "\f025"; -} -#widget_memberlite_recent_posts .hentry.format-image h4.entry-title:before { - content: "\f1c5"; -} -#widget_memberlite_recent_posts .hentry.format-link h4.entry-title:before { - content: "\f08e"; -} -#widget_memberlite_recent_posts .hentry.format-video h4.entry-title:before { - content: "\f03d"; -} - -/* [memberlite_subpagelist] Shortcode */ -.page .entry-content .memberlite_subpagelist_item .entry-content { - border-bottom: none; -} diff --git a/css/rtl.css b/css/rtl.css deleted file mode 100644 index 853983b7..00000000 --- a/css/rtl.css +++ /dev/null @@ -1,398 +0,0 @@ -/* Adding support for languages written in a Right To Left (RTL) direction. */ -body { - direction: rtl; - unicode-bidi: embed; -} -caption, th, td{ - text-align: right; -} -blockquote{ - padding: 2.9rem 2.9rem 1px 2.9rem ; - border-right: 5px solid #CCC; - border-left: none; -} -blockquote.quote, .testimonials-widget blockquote{ - padding: 0 3.7rem 0 0 ; -} -blockquote.quote:before{ - right: 0rem; - left: auto; -} -blockquote cite, .testimonials-widget blockquote .credit{ - text-align: left; -} -.testimonials-widget-testimonial .open-quote:before{ - padding-left: 1rem; - padding-right: 0; -} -.testimonials-widget-testimonial .close-quote:after{ - padding-right: 1rem; - padding-left: 0; -} -details > summary:before{ - float: left; -} -ul, ol{ - margin: 0 3.2rem 2.9rem 0 ; -} -ul li, ol li{ - margin: 0 0 1rem 0 ; -} -dt{ - padding: 2.9rem 0 0 0 ; -} -textarea{ - padding-right: 1.45rem; - padding-left: 0; -} -.pmpro_content_message a{ - margin: 5px 0 0 5px ; -} -.main-navigation ul{ - margin-right: 0; -} -.main-navigation li{ - float: right; -} -.main-navigation a{ - border-right: 1px dotted #CCC; - border-left: none; -} -.main-navigation li:first-child a{ - border-right: none; - border-left: none; - padding-left: 1.45rem; -} -.meta-navigation ul, .header-right .widget_nav_menu ul, .footer-navigation ul{ - margin: 0 0 1.45rem 0 ; - padding-right: 0; -} -.meta-navigation ul li, .header-right .widget_nav_menu ul li, .footer-navigation ul li{ - border-left: 1px solid #CCC; - border-right: none; -} -.meta-navigation li:last-child, .header-right .widget_nav_menu li:last-child, .footer-navigation li:last-child{ - border-left: none; - border-right: none; - padding-left: 0; -} -.main-navigation ul.sub-menu, .meta-navigation ul.sub-menu, .header-right .widget_nav_menu ul.sub-menu{ - float: right; - right: -9999em; - left: auto; - text-align: right; -} -.meta-navigation ul.sub-menu ul.sub-menu, .header-right .widget_nav_menu ul.sub-menu ul.sub-menu{ - right: -9999em; - left: auto; -} -.main-navigation ul.sub-menu a, .meta-navigation ul.sub-menu a, .header-right .widget_nav_menu ul.sub-menu a{ - border-right: none; - border-left: none; -} -.main-navigation ul li:hover > ul{ - right: auto; - left: auto; -} -.main-navigation ul li:first-child:hover > ul{ - right: -1.5rem; - left: auto; -} -.main-navigation ul li:hover > ul li:hover > ul{ - right: 100%; - left: auto; -} - -.meta-navigation ul li:hover > ul, .header-right .widget_nav_menu ul li:hover > ul{ - right: 0; - left: auto; -} -.main-navigation ul ul li:hover > ul, .meta-navigation ul ul li:hover > ul, .header-right .widget_nav_menu ul ul li:hover > ul{ - right: 100%; - left: auto; -} -.main-navigation ul ul li:first-child a, .main-navigation ul li:first-child ul li a, .meta-navigation ul ul li:first-child a, .header-right .widget_nav_menu ul ul li:first-child a{ - padding-right: 1.5rem; - padding-left: 0; -} -#site-navigation .search-form{ - float: left; -} -#mobile-navigation{ - right: -100%; - left: auto; -} -#mobile-navigation-height-col{ - right: -100%; - left: auto; -} -#mobile-navigation ul.menu ul.sub-menu{ - margin-right: 0; -} -#mobile-navigation ul.menu ul.sub-menu li a{ - padding-right: 1.5rem; - padding-left: 0; -} -.comment-navigation .nav-previous, .paging-navigation .nav-previous, .post-navigation .nav-previous, .page-navigation .nav-previous{ - float: right; - text-align: right; -} -.comment-navigation .nav-next, .paging-navigation .nav-next, .post-navigation .nav-next, .page-navigation .nav-next{ - float: left; - text-align: left; -} -.screen-reader-text:hover, .screen-reader-text:active, .screen-reader-text:focus{ - right: 5px; - left: auto; -} -.alignleft{ - float: right; - margin-left: 1.5em; - margin-right: auto; -} -.alignright{ - float: left; - margin-right: 1.5em; - margin-left: auto; -} -.text-left{ - text-align: right; -} -.text-right{ - text-align: left; -} -.site-header .widget_search{ - border-right: 1px solid #CCC; - border-left: none; - margin: 0 1.45rem 0 0 ; - padding: 0 1.45rem 0 0 ; -} -#secondary .widget_calendar #wp-calendar tfoot td#next{ - text-align: left; -} -.sidebar-content #primary, .sidebar-left #primary, .blog.sidebar-blog-left #primary, .archive.sidebar-blog-left #primary, .search.sidebar-blog-left #primary, .single.sidebar-blog-left #primary{ - float: left; -} -.content-sidebar #primary, .blog.sidebar-blog-right #primary, .archive.sidebar-blog-right #primary, .search.sidebar-blog-right #primary, .single.sidebar-blog-right #primary{ - float: right; -} -.site-branding { - float: right; -} -.site-branding a.custom-logo-link{ - float: right; -} -.header-right{ - text-align: left; -} -#meta-member .meta-member-inner{ - border-bottom-left-radius: 3px; - border-bottom-right-radius: 3px; - margin: 0 0 1.45rem 0 ; -} -#meta-member .user{ - border-left: 1px solid #DDD; - border-right: none; -} -#meta-member .member-navigation, #meta-member .member-navigation ul { - float: left; -} -#meta-member .member-navigation li{ - border-left: 1px solid #DDD; - border-right: none; -} -#meta-member .member-navigation .sub-menu{ - right: -9999em; - left: auto; - text-align: right; -} -#meta-member .member-navigation ul li:hover > ul{ - right: auto; - left: auto; -} -.masthead{ - padding: 2.9rem 0 5.8rem 0 ; -} -.masthead-post-byline{ - margin: 1.45rem 0 0 0 ; -} -.masthead-banner .masthead-post-byline{ - margin: 16rem 0 0 0 ; -} -.masthead h1{ - margin: 2.9rem 0 0 0 ; -} -.masthead a.pmpro_btn{ - margin: 2rem 0 0 0 ; -} -.home .home-content p.subtitle{ - margin: 0 0 3rem 0 ; -} -.interstitial .masthead .btn{ - left: 0; - right: auto; -} -#primary .hentry .entry-header .entry-title{ - margin: 0 0 .7rem 0 ; -} -.hentry.format-audio h1.entry-title:before, .hentry.format-image h1.entry-title:before, .hentry.format-link h1.entry-title:before, .hentry.format-video h1.entry-title:before, .single-format-audio .masthead-post-byline h1.entry-title:before, .single-format-image .masthead-post-byline h1.entry-title:before, .single-format-link .masthead-post-byline h1.entry-title:before, .single-format-video .masthead-post-byline h1.entry-title:before{ - padding-left: 10px; - padding-right: 0; -} -.hentry.pmpro-level-required h1.entry-title:before, .pmpro-body-level-required .masthead-post-byline h1.entry-title:before{ - padding-left: 10px; - padding-right: 0; -} -.pmpro-level-required.pmpro-has-access h1.entry-title:before, .pmpro-body-level-required.pmpro-body-has-access h1.entry-title:before{ - padding-left: 0; -} -.format-status .entry-content .post_author_avatar{ - float: right; - margin: 0 0 2.9rem 2.9rem ; -} -.format-image .wp-post-image{ - padding-left: 0; - padding-right: 0; -} -.error-404 .entry-content{ - padding: 0 0 5.8rem 0 ; -} -.comment{ - margin: 0 2.9rem 2.9rem 2.9rem ; -} -.bypostauthor{ - border-right-width: 10px; - border-left-width: unset; -} -.bypostauthor .comment-meta .comment-author.vcard .fn:after{ - margin-right: 5px; - margin-left: auto; -} -.comment-meta .comment-metadata, .comment-awaiting-moderation{ - margin: 0 0 1.45rem 0 ; -} -.comment .comment-meta .comment-metadata{ - margin-right: 50px; - margin-left: auto; -} -.comment-meta .comment-metadata .edit-link{ - border-right: 1px solid #CCC; - border-left: none; - margin: 0 .7rem 0 0 ; - padding: 0 .7rem 0 0 ; -} -.comment-respond label{ - text-align: left; - margin: 0 0 0 .7rem ; -} -.comment-respond p.comment-form-cookies-consent label{ - text-align: right; - margin: 0 .7rem 0 0 ; -} -.comment-repond input[type="checkbox"], .comment-repond input[type="radio"]{ - margin-left: .5em; - margin-right: auto; -} -.comment-respond .comment-form-author span.required, .comment-respond .comment-form-email span.required{ - left: 0; - right: auto; -} -.comment-respond .form-allowed-tags, .comment-respond .form-submit, .comment-respond .comment-subscription-form{ - margin-right: 13%; - margin-left: auto; -} -.footer-widgets h3{ - margin: 0 0 1.45rem 0 ; -} -.footer-widgets p{ - margin: .7rem 0 0 0 ; -} -.footer-widgets .post_author_avatar{ - float: right; - margin: 0 0 0 20px ; -} -.footer-widgets ul{ - margin: .7rem 0 0 0 ; -} -#banner_bottom h1, #banner_bottom h2, #banner_bottom h3, #banner_bottom h4, #banner_bottom h5{ - margin: 0 0 1.6rem 0 ; -} -.memberlite_tabbable ul.memberlite_tabs li{ - float: right; - margin: 0 0 0 .5rem ; -} -.memberlite_tabbable ul.memberlite_tabs li > a{ - border-top-right-radius: 3px; - border-top-left-radius: 3px; -} -#pmpro_levels .post h2, .pmpro_signup_form h2{ - margin: 0 0 1rem 0 ; -} -#pmpro_levels.pmpro_levels-table.pmpro_level-highlight, #pmpro_levels.pmpro_levels-div .pmpro_level-highlight, #pmpro_levels.pmpro_levels-2col .pmpro_level-highlight, #pmpro_levels.pmpro_levels-3col .pmpro_level-highlight, #pmpro_levels.pmpro_levels-4col .pmpro_level-highlight { - border-right: 1px solid #CCC; - border-left: 1px solid #CCC; -} -#pmpro_levels.pmpro_levels-table .pmpro_level-highlight td:first-child{ - border-right: 15px solid var(--memberlite-color-secondary); - border-left: none; -} -#pmpro_levels.pmpro_levels-table .pmpro_level-highlight td:last-child{ - border-left: 15px solid var(--memberlite-color-secondary); - border-right: none; -} -#pmpro_levels.pmpro_advanced_levels-compare_table tbody td:first-child{ - text-align: left; -} -#pmpro_levels.pmpro_advanced_levels-compare_table thead th.pmpro_level-highlight, #pmpro_levels.pmpro_advanced_levels-compare_table tbody td.pmpro_level-highlight, #pmpro_levels.pmpro_advanced_levels-compare_table tfoot td.pmpro_level-highlight{ - border-right: 1rem solid var(--memberlite-color-secondary); - border-left: 1rem solid var(--memberlite-color-secondary); -} -#pmpro_levels.pmpro_advanced_levels-compare_table_responsive .pmpro_level-highlight{ - border-right: 1px solid #CCC; - border-left: 1px solid #CCC; -} -#pmpro_levels.pmpro_advanced_levels-compare_table tbody td:hover::after, #pmpro_levels.pmpro_advanced_levels-compare_table tbody tr:nth-child(even) td:hover::after{ - right: 0; - left: auto; -} -.pmpro_levels-div .pmpro_btn-select, .pmpro_levels-2col .pmpro_btn-select{ - margin-right: 3rem; - margin-left: auto; -} -form.pmpro_form hr{ - margin: 0 0 2.9rem 0 ; -} -form#pmpro_form .pmpro_checkout h3 span.pmpro_checkout-h3-msg{ - float: left; -} -form#pmpro_form .pmpro_sslseal{ - padding: 1.45rem 0 1.45rem 1.45rem ; -} -form.pmpro_form .pmpro_checkout #other_discount_code_p, form.pmpro_form .pmpro_checkout #other_discount_code_div{ - margin: 1.45rem 0 0 0 ; - padding: 1.45rem 0 0 0 ; -} -#pmpro_account #pmpro_account-membership table .pmpro_actionlinks{ - text-align: right; -} -.testimonials-widget-testimonial span.image{ - margin: 0 1rem 0 2rem ; -} -@media only screen and (max-width: 767px){ - .menu-toggle{ - left: .5rem; - right: auto; - } - .comment-respond label{ - text-align: right; - } - .comment-respond .form-allowed-tags, .comment-respond .form-submit, .comment-respond .comment-subscription-form{ - margin-right: 0; - } - form#pmpro_form .pmpro_checkout-fields{ - padding: 1rem 0 0 0 ; - } - .pmpro_levels-div .entry-footer .alignright, .pmpro_levels-2col .entry-footer .alignright{ - margin: 0 0 1.5rem 0 ; - } -} diff --git a/css/theme-my-login.css b/css/theme-my-login.css deleted file mode 100644 index cb89fe91..00000000 --- a/css/theme-my-login.css +++ /dev/null @@ -1,196 +0,0 @@ -/** - * Theme My Login specific CSS - * - * @package Memberlite - */ - -/* =Theme My Login Style --------------------------------------------------------------- */ - - -/*-------------------------------------------------------------- -5.2 Menus ---------------------------------------------------------------*/ -#mobile-navigation .widget_theme_my_login p { - margin: 0 0 1rem 0; -} -#mobile-navigation .widget_theme_my_login input[type=text], -#mobile-navigation .widget_theme_my_login input[type=password], -#mobile-navigation .widget_theme_my_login input[type=submit] { - width: 100%; -} -#mobile-navigation .widget_theme_my_login .tml-links li { - display: block; -} -#mobile-navigation .widget_theme_my_login .tml-links li:after { - content: ""; - margin: 0; - padding: 0; -} -#mobile-navigation .widget_theme_my_login .tml-user-avatar { - margin-left: 1rem; -} -#mobile-navigation .widget_theme_my_login ul.tml-user-links { - margin-bottom: 2rem; -} -#mobile-navigation .widget_theme_my_login ul.tml-action-links li a { - font-size: .8em; -} -#mobile-navigation .widget_theme_my_login ul.tml-action-links li a { - display: block; - padding: .5rem 1rem; -} -#mobile-navigation .widget_theme_my_login ul.tml-action-links li a:hover { - background: rgba(0,0,0,0.2); -} -/*-------------------------------------------------------------- -11.2 Theme My Login: https://wordpress.org/plugins/theme-my-login/ ---------------------------------------------------------------*/ -.tml { - max-width: 100%; -} -.tml p { - margin: 0 0 1.45rem 0; -} -.tml-profile h3 { - background: #FAFAFA; - border-top: 1px solid #CCC; - border-bottom: 1px dotted #CCC; - margin-bottom: 0; - margin-top: 0; - padding: .7rem 1.45rem; -} -.tml-profile .tml-field-wrap h3 { - margin-left: -1.45rem; - margin-right: -1.45rem; -} -.tml-profile .tml-field-wrap { - padding: 0 1.45rem; -} -.tml-profile .tml-label, -.tml-profile .tml-description { - margin: 0; -} -.tml-profile table { - display: block; - margin: 1em 0; - padding: 0 1.45rem; -} -.tml-profile tr, -.tml-profile tr:nth-child(even) th, -.tml-profile tr:nth-child(even) td, -.tml-profile th, -.tml-profile td { - background: none; - border: none; - display: block; - padding: 0; -} -.tml-profile td, -.tml-profile tr:nth-child(even) td { - padding-bottom: 1.45rem; -} -.tml-profile .description { - display: block; - font-size: .85em; - font-style: italic; - margin: 0; -} -.tml-profile p.tml-submit-wrap { - text-align: right; -} -#secondary .widget_theme_my_login { - background: #FAFAFA; - border: 1px solid #CCC; - padding: 1.45rem; -} -#secondary .widget_theme_my_login h3.widget-title { - background: none; - border: none; - margin-bottom: .9rem; - padding: 0; -} -#secondary .widget.widget_theme_my_login ul li, -#secondary .widget.widget_theme_my_login ul.menu li { - border: none; - margin: 0; - padding: 0; -} -.tml.tml-user-panel { - display: grid; - grid-template-columns: 80px auto; - grid-template-areas: - "avatar user-links" - "avatar nav"; -} -.tml.tml-user-panel .tml-user-avatar { - grid-area: avatar; -} -.tml.tml-user-panel .tml-user-links { - grid-area: user-links; - list-style: none; - margin-left: 0; -} -.tml.tml-user-panel nav { - grid-area: nav; -} -#primary .tml input, -#primary .tml select { - width: auto; -} -#primary .tml input[type="text"], -#primary .tml input[type="email"], -#primary .tml input[type="url"], -#primary .tml input[type="password"], -#primary .tml textarea, -#primary .tml select { - min-width: 320px; -} -#primary .pmpro_message.pmpro_default .tml input, -#primary .pmpro_message.pmpro_default .tml textarea, -#primary .pmpro_message.pmpro_default .tml select { - width: 100%; -} -#primary .pmpro_message.pmpro_default .tml input[type="checkbox"] { - width: auto; -} -@media screen and (min-width: 768px) { - #primary .tml-login .tml-rememberme-wrap, - #primary .tml-login .tml-submit-wrap { - display: block; - text-align: left; - } -} -#secondary .tml input, -#secondary .tml textarea, -#secondary .tml select { - margin: 0; -} -.tml .tml-links { - border-top: 1px dotted #CCC; - color: #CCC; - display: block; - margin: 0; - padding-top: 1.45rem; -} -.tml .tml-links li { - display: inline-block; -} -.tml .tml-links li:after { - content: "|"; - margin-right: .7rem; - padding-left: .7rem; -} -.tml .tml-links li:last-child:after { - content: ""; - margin: 0; - padding: 0; -} -.site-main .tml .tml-links li a { - color: #777; - text-decoration: none; -} -#secondary .tml .tml-links li a, -#secondary .tml .tml-links li a:hover { - border: none; -} diff --git a/css/woocommerce.css b/css/woocommerce.css index cdc75c16..5253d3c0 100644 --- a/css/woocommerce.css +++ b/css/woocommerce.css @@ -2,209 +2,12 @@ * WooCommerce specific CSS * */ -.woocommerce #content input.button, .woocommerce #respond input#submit, .woocommerce a.button, .woocommerce button.button, .woocommerce input.button, .woocommerce-page #content input.button, .woocommerce-page #respond input#submit, .woocommerce-page a.button, .woocommerce-page button.button, .woocommerce-page input.button, .woocommerce #content input.button.alt, .woocommerce #respond input#submit.alt, .woocommerce a.button.alt, .woocommerce button.button.alt, .woocommerce input.button.alt, .woocommerce-page #content input.button.alt, .woocommerce-page #respond input#submit.alt, .woocommerce-page a.button.alt, .woocommerce-page button.button.alt, .woocommerce-page input.button.alt { - color: #404040; - font-weight: 400; - font-size: 16px; - font-size: 1.6rem; - line-height: 2.6rem; -} -.woocommerce #main ul.products li.product h3, .widget_calendar #wp-calendar caption { - font-size: 20px; - font-size: 2rem; - line-height: 3rem; -} -.woocommerce #content input.button, .woocommerce #respond input#submit, .woocommerce a.button, .woocommerce button.button, .woocommerce input.button, .woocommerce-page #content input.button, .woocommerce-page #respond input#submit, .woocommerce-page a.button, .woocommerce-page button.button, .woocommerce-page input.button, -.woocommerce #content input.button.alt, .woocommerce #respond input#submit.alt, .woocommerce a.button.alt, .woocommerce button.button.alt, .woocommerce input.button.alt, .woocommerce-page #content input.button.alt, .woocommerce-page #respond input#submit.alt, .woocommerce-page a.button.alt, .woocommerce-page button.button.alt, .woocommerce-page input.button.alt { - font-size: 100%; /* Corrects font size not being inherited in all browsers */ - margin: 0; /* Addresses margins set differently in IE6/7, F3/4, S5, Chrome */ - vertical-align: baseline; /* Improves appearance and consistency in all browsers */ -} -.woocommerce #content input.button, .woocommerce #respond input#submit, .woocommerce a.button, .woocommerce button.button, .woocommerce input.button, .woocommerce-page #content input.button, .woocommerce-page #respond input#submit, .woocommerce-page a.button, .woocommerce-page button.button, .woocommerce-page input.button, -.woocommerce #content input.button.alt, .woocommerce #respond input#submit.alt, .woocommerce a.button.alt, .woocommerce button.button.alt, .woocommerce input.button.alt, .woocommerce-page #content input.button.alt, .woocommerce-page #respond input#submit.alt, .woocommerce-page a.button.alt, .woocommerce-page button.button.alt, .woocommerce-page input.button.alt { - border-radius: 3px; - background: var(--memberlite-color-button); - border: none; - box-shadow: none; - color: var(--memberlite-color-white) !important; - cursor: pointer; /* Improves usability and consistency of cursor style between image-type 'input' and others */ - /* -webkit-appearance: button; Corrects inability to style clickable 'input' types in iOS */ - font-size: 16px; - font-size: 1.6rem; - font-style: normal; - font-weight: normal; - display: inline-block; - padding: 1rem 1.5rem; - text-align: center; - text-decoration: none; - text-shadow: none; - -webkit-transform: translateZ(0); - transform: translateZ(0); - -webkit-backface-visibility: hidden; - backface-visibility: hidden; - -moz-osx-font-smoothing: grayscale; - overflow: hidden; - -webkit-transition-duration: 0.3s; - transition-duration: 0.3s; - -webkit-transition-property: color, background-color; - transition-property: color, background-color; -} -.woocommerce #content input.button, .woocommerce #respond input#submit, .woocommerce a.button, .woocommerce button.button, .woocommerce input.button, .woocommerce-page #content input.button, .woocommerce-page #respond input#submit, .woocommerce-page a.button, .woocommerce-page button.button, .woocommerce-page input.button, -.woocommerce #content input.button.alt, .woocommerce #respond input#submit.alt, .woocommerce a.button.alt, .woocommerce button.button.alt, .woocommerce input.button.alt, .woocommerce-page #content input.button.alt, .woocommerce-page #respond input#submit.alt, .woocommerce-page a.button.alt, .woocommerce-page button.button.alt, .woocommerce-page input.button.alt { - padding: .3rem 1.5rem; -} -.woocommerce #content input.button.alt, .woocommerce #respond input#submit.alt, .woocommerce a.button.alt, .woocommerce button.button.alt, .woocommerce input.button.alt, .woocommerce-page #content input.button.alt, .woocommerce-page #respond input#submit.alt, .woocommerce-page a.button.alt, .woocommerce-page button.button.alt, .woocommerce-page input.button.alt { - background: var(--memberlite-color-action); - border: none; - color: var(--memberlite-color-white) !important; - font-size: 18px; - font-size: 1.8rem; - padding: 1.4rem 2rem; - text-shadow: none; -} -.woocommerce #content input.button:hover, .woocommerce #respond input#submit:hover, -.woocommerce a.button:hover, .woocommerce button.button:hover, .woocommerce input.button:hover, -.woocommerce-page #content input.button:hover, .woocommerce-page #respond input#submit:hover, -.woocommerce-page a.button:hover, .woocommerce-page button.button:hover, -.woocommerce-page input.button:hover { - background: var(--memberlite-color-button); - color: var(--memberlite-color-white) !important; -} -.woocommerce #content input.button.alt:hover, .woocommerce #respond input#submit.alt:hover, .woocommerce a.button.alt:hover, .woocommerce button.button.alt:hover, .woocommerce input.button.alt:hover, .woocommerce-page #content input.button.alt:hover, .woocommerce-page #respond input#submit.alt:hover, .woocommerce-page a.button.alt:hover, .woocommerce-page button.button.alt:hover, .woocommerce-page input.button.alt:hover { - background: var(--memberlite-color-button); - border: none; - text-shadow: none; -} -.woocommerce .woocommerce-breadcrumb, .woocommerce-page .woocommerce-breadcrumb { - color: var(--memberlite-color-white) !important; - font-size: 1.2rem; - line-height: 2.2rem; - margin: 0; - padding: 0; -} -.woocommerce .masthead .woocommerce-breadcrumb, .woocommerce-page .masthead .woocommerce-breadcrumb { - color: var(--memberlite-color-white) !important; -} -.woocommerce .masthead .woocommerce-breadcrumb a, .woocommerce-page .masthead .woocommerce-breadcrumb a { - color: var(--memberlite-color-white) !important; - text-decoration: none; - border-bottom: 1px dotted var(--memberlite-color-white) !important; - filter: alpha(opacity=80); - opacity: 0.8; -} -.woocommerce .masthead .woocommerce-breadcrumb a:hover, .woocommerce-page .masthead .woocommerce-breadcrumb a:hover { - filter: alpha(opacity=100); - opacity: 1.0; -} -.woocommerce .entry-summary { - border: none; -} -.woocommerce.single .product_meta { - padding: 1rem; - border-bottom: 1px dotted #CCC; - background: #FAFAFA; - font-style: italic; - color: #AAA; -} -.woocommerce.single .product_meta { - border-top: 1px dotted #CCC; -} -.woocommerce.single .product_meta a { - color: #777; - text-decoration: none; -} -.woocommerce.single .product_meta a:hover { - border-bottom: 1px dotted #777; -} -.woocommerce #main ul.products li.product a img {margin-bottom: .5em; } -.woocommerce #main ul.products li.product h3 {padding: 0; } -.woocommerce #main ul.products li.product .button {margin: 0; } -.woocommerce #content div.product div.images img, .woocommerce div.product div.images img, .woocommerce-page #content div.product div.images img, .woocommerce-page div.product div.images img, .woocommerce ul.products li.product a img, .woocommerce-page ul.products li.product a img {box-shadow: none; } - -.woocommerce #content div.product div.thumbnails a, .woocommerce div.product div.thumbnails a, .woocommerce-page #content div.product div.thumbnails a, .woocommerce-page div.product div.thumbnails a { - width:auto; - background: #FAFAFA; - padding: .25rem; - border-top: 1px dotted #CCC; - border-bottom: 1px dotted #CCC; - margin-bottom: 1.5rem; -} -.woocommerce .related ul li.product, .woocommerce .related ul.products li.product, .woocommerce .upsells.products ul li.product, .woocommerce .upsells.products ul.products li.product, .woocommerce-page .related ul li.product, .woocommerce-page .related ul.products li.product, .woocommerce-page .upsells.products ul li.product, .woocommerce-page .upsells.products ul.products li.product { - width: 22.05%; +.woocommerce:where(body:not(.woocommerce-uses-block-theme)) .woocommerce-breadcrumb { + color: var(--memberlite-color-page-masthead); + font-size: var(--wp--preset--font-size--14); } -.woocommerce #content div.product form.cart div.quantity, .woocommerce div.product form.cart div.quantity, .woocommerce-page #content div.product form.cart div.quantity, .woocommerce-page div.product form.cart div.quantity {float: none; margin: 0 0 1rem 0; } - -.woocommerce p.stars {display: inline-block; } -.woocommerce #review_form #respond textarea, .woocommerce-page #review_form #respond textarea {width: 84%; } -.woocommerce #review_form #respond p.form-submit, .woocommerce #review_form #respond p.comment-subscription-form {margin-left: 13%; } -.woocommerce #content div.product .woocommerce-tabs ul.tabs, .woocommerce div.product .woocommerce-tabs ul.tabs, -.woocommerce-page #content div.product .woocommerce-tabs ul.tabs, -.woocommerce-page div.product .woocommerce-tabs ul.tabs { - margin-left: 0; -} -.woocommerce #content div.product .woocommerce-tabs ul.tabs li, .woocommerce div.product .woocommerce-tabs ul.tabs li, .woocommerce-page #content div.product .woocommerce-tabs ul.tabs li, .woocommerce-page div.product .woocommerce-tabs ul.tabs li { - background: #95a5a6; - box-shadow: none; - margin: 0; - left: 0; -} -.woocommerce #content div.product .woocommerce-tabs ul.tabs li a, .woocommerce div.product .woocommerce-tabs ul.tabs li a, .woocommerce-page #content div.product .woocommerce-tabs ul.tabs li a, .woocommerce-page div.product .woocommerce-tabs ul.tabs li a { - text-shadow: none; - color: var(--memberlite-color-white) !important; -} -.woocommerce #content div.product .woocommerce-tabs ul.tabs li a:hover, .woocommerce div.product .woocommerce-tabs ul.tabs li a:hover, .woocommerce-page #content div.product .woocommerce-tabs ul.tabs li a:hover, .woocommerce-page div.product .woocommerce-tabs ul.tabs li a:hover { - color: var(--memberlite-color-white) !important; -} -.woocommerce #content div.product .woocommerce-tabs ul.tabs li:hover, .woocommerce div.product .woocommerce-tabs ul.tabs li:hover, .woocommerce-page #content div.product .woocommerce-tabs ul.tabs li:hover, .woocommerce-page div.product .woocommerce-tabs ul.tabs li:hover { - background: #798d8f; -} -.woocommerce #content div.product .woocommerce-tabs ul.tabs li.active:hover, .woocommerce div.product .woocommerce-tabs ul.tabs li.active:hover, .woocommerce-page #content div.product .woocommerce-tabs ul.tabs li.active:hover, .woocommerce-page div.product .woocommerce-tabs ul.tabs li.active:hover { - background: var(--memberlite-color-white) !important; -} -.woocommerce #content div.product .woocommerce-tabs ul.tabs li.active a:hover, .woocommerce div.product .woocommerce-tabs ul.tabs li.active a:hover, .woocommerce-page #content div.product .woocommerce-tabs ul.tabs li.active a:hover, .woocommerce-page div.product .woocommerce-tabs ul.tabs li.active a:hover { - color: #404040; +.woocommerce:where(body:not(.woocommerce-uses-block-theme)) .woocommerce-breadcrumb a { + color: var(--memberlite-color-page-masthead); } - -.woocommerce #content div.product .woocommerce-tabs ul.tabs li:after, -.woocommerce #content div.product .woocommerce-tabs ul.tabs li:before, -.woocommerce div.product .woocommerce-tabs ul.tabs li:after, .woocommerce div.product .woocommerce-tabs ul.tabs li:before, -.woocommerce-page #content div.product .woocommerce-tabs ul.tabs li:after, -.woocommerce-page #content div.product .woocommerce-tabs ul.tabs li:before, -.woocommerce-page div.product .woocommerce-tabs ul.tabs li:after, -.woocommerce-page div.product .woocommerce-tabs ul.tabs li:before { - display: none; -} -.woocommerce-cart table.cart img, .woocommerce ul.cart_list li img, #secondary .woocommerce ul.product_list_widget li img { - border-radius:50%; - float: left; - height: 80px; - margin-bottom: 0; - width: 80px; -} -#secondary .woocommerce ul.product_list_widget li img { - margin: -.5em .5em 0 -.5em; -} -#secondary .woocommerce ul.product_list_widget li a { - border-bottom: none; -} -.woocommerce table.cart td.actions #coupon_code, .woocommerce-page #content table.cart td.actions #coupon_code, .woocommerce-page table.cart td.actions #coupon_code {width: 160px; } -.woocommerce-shipping-calculator p {margin: 0; } - -.woocommerce ul.products li.product .price, -.woocommerce-page ul.products li.product .price, -.woocommerce #content div.product p.price, -.woocommerce #content div.product span.price, -.woocommerce div.product p.price, -.woocommerce div.product span.price, -.woocommerce-page #content div.product p.price, -.woocommerce-page #content div.product span.price, -.woocommerce-page div.product p.price, -.woocommerce-page div.product span.price { - color: var(--memberlite-color-secondary); -} - -.woocommerce #content input.button.alt, .woocommerce #respond input#submit.alt, .woocommerce a.button.alt, .woocommerce button.button.alt, .woocommerce input.button.alt, .woocommerce-page #content input.button.alt, .woocommerce-page #respond input#submit.alt, .woocommerce-page a.button.alt, .woocommerce-page button.button.alt, .woocommerce-page input.button.alt { - background: var(--memberlite-color-action); -} \ No newline at end of file diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..9532251f --- /dev/null +++ b/docs/README.md @@ -0,0 +1,14 @@ +# Welcome to Memberlite's Docs! + +## What is this? + +The `docs` folder contains developer-focused documentation for Memberlite, including architecture, historical context, build process, hooks, and so on. +Whenever possible, we try to keep this documentation up-to-date with the latest version of Memberlite. + +## Table of Contents + +1. [Build Process](build-process.md) +2. [CSS Guidelines](css-guidelines.md) +3. [Customizer](customizer.md) +4. [Patterns](patterns.md) +5. [Project Structure](project-structure.md) diff --git a/docs/build-process.md b/docs/build-process.md new file mode 100644 index 00000000..d1de2209 --- /dev/null +++ b/docs/build-process.md @@ -0,0 +1,68 @@ +# Build Process + +## Overview + +Memberlite uses a hybrid development approach: +- Most front-end styles are authored in Sass (`src/scss/`) and compiled to CSS in `build/css/`. +- The compiled CSS includes a table of contents and is not compressed by default. +- Some legacy vanilla CSS files remain in the theme's `css/` directory and are committed directly. +- The theme's JavaScript is edited directly in the `js/` directory. +- Block editor enhancements are compiled using `@wordpress/scripts`. + +## How it Works + +- Sass source files for front-end styles are located in `src/scss/`. +- Run the build process to compile these Sass files into CSS, outputting to `build/css/`. The compiled CSS includes a table of contents and is not minified by default. +- Some vanilla CSS files in the `css/` directory are still used and maintained directly. +- JavaScript for the theme is authored and committed directly in the `js/` directory. +- For block development and customizations to the WordPress block editor, source code is in the `src/` directory. We use `@wordpress/scripts` (wp-scripts), which leverages Webpack under the hood, to build custom blocks and editor settings. A custom `webpack.config.js` file defines additional source and destination paths. +- Built block/editor assets are output to the `build/` directory (or as defined in `webpack.config.js`). + +## Getting Started + +1. Clone the Memberlite repository and navigate to the theme directory. + - For more detailed instructions, see the root `README.md`. +2. Run `npm install` to install dependencies. +3. Run `npm run build:blocks` to build blocks and editor settings. +4. Run `npm run build:css` to compile Sass to CSS. +5. Run `npm run build:rtl` to compile a RTL version of the CSS. If you've made Sass changes, you'll need to compile the CSS for `main.scss` first. +6. Run `npm run build` to build blocks, editor settings, CSS, and the RTL stylesheet all at once. +7. You can also run `npm run watch:css` or `npm run watch:blocks` to watch for changes and rebuild automatically. + +## How the RTL CSS is Built + +We're using the [RTLCSS](https://github.com/MohammadYounes/rtlcss) library to build RTL versions of the CSS. It works by parsing the CSS +and reversing the direction of all CSS properties that have a directional value. For example, `margin-left` becomes `margin-right` and `padding-right` +becomes `padding-left`. You can make adjustments for what strings it replaces in the `.rtlcssrc.json` file. + +**Note:** Keep in mind that the RTLCSS compiler only scans the CSS for strings to replace. It does not change markup from our PHP templates or from within the block editor. + +### A Note About Swapping Icon Fonts + +If you're trying to swap an icon font, for example, Font Awesome, you can leave a comment in the CSS with the +RTL version of the icon font. For example: + +```aiignore +content: "\f105"; /*! rtl:raw:content: "\f104"; */ +``` + +The RTLCSS compiler will swap the icon font for the RTL version. In the example above, it switches the angle right icon to the angle left icon. + +## Adding New Paths to the Webpack Config + +1. Create new blocks in the `src/blocks/` directory. + - You can create new subdirectories within `blocks/` to organize your blocks as needed. For example, you might have `src/blocks/my-new-block/` for a new block called "My New Block". +2. Add the new block to the `entry` object in `webpack.config.js`. + - Format: `'blocks/my-new-block/index': path.resolve( process.cwd(), 'src/blocks/my-new-block/index.js' ),` +4. For new editor settings, you can use the existing `src/editor/custom-settings.js` file. You do not need to add a new path to the Webpack config. + +**Note:** Developers should not commit build files manually; they are generated automatically on merge to `master` via the workflow. + +## Further Reading + +- [WordPress Scripts Documentation](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-scripts/) +- [How Webpack and WordPress packages interact](https://developer.wordpress.org/news/2023/04/how-webpack-and-wordpress-packages-interact/) + +--- + +**Last Updated**: 2026-03-05 diff --git a/docs/css-guidelines.md b/docs/css-guidelines.md new file mode 100644 index 00000000..369c7dcb --- /dev/null +++ b/docs/css-guidelines.md @@ -0,0 +1,62 @@ +# CSS Guidelines + +For an overview of how the theme is structured, see [Project Structure](project-structure.md). +For an overview of how to build the theme's Sass files, see [Build Process](build-process.md). + +--- + +# General Guidelines + +These are guidelines for styling the theme with CSS. + +Before writing any styles, ask yourself whether they should apply globally or only to a specific page or component. If you're styling an element, also check whether there's a block that needs matching styles. + +We write styles **mobile first** using a `breakpoint` mixin to handle responsiveness. Avoid hardcoded values — use either `--memberlite-` prefixed variables or `--wp--preset` prefixed variables instead. Avoid `!important` unless absolutely necessary, and keep Sass nesting no deeper than 3 levels. + +Color schemes are set via the Customizer, which applies a `color-scheme` of `light` or `dark` to the `:root` element. Use the [`light-dark()` function](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/color_value/light-dark) whenever a color needs to differ between schemes, and use [`color-mix()`](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/color_value/color-mix) to create tints and shades at runtime rather than hardcoding separate hex values. + +## Where Do I Put My Styles? + +Stylesheets are organized into focused directories: + +- **`abstracts/`** — Not compiled on their own. `_variables.scss` holds Sass variables (primarily for alert/status colors and button shadows). `_mixins.scss` holds reusable mixins. Both are imported with `@use` into every partial that needs them. +- **`base/`** — Global, element-level styles with no component context (resets, typography, buttons, forms, grid, utilities, alerts). +- **`blocks/`** — Styles targeting WordPress block classes (`.wp-block-*`). +- **`components/`** — Reusable UI patterns that appear in multiple contexts (breadcrumbs, comments, widgets, pagination, etc.). +- **`structure/`** — Major layout regions: header, masthead, footer, and content area. +- **`pages/`** — Styles scoped to specific page types or templates (blog archive, 404, page templates). +- **`plugins/`** — Third-party plugin integration styles, currently only PMPro (`_pmpro.scss`). + +Create a new file when styles grow beyond roughly 50–100 lines, or when they don't logically fit an existing file. New files should be registered in `main.scss`. + +--- + +# Deeper Overview + +## The Breakpoint Mixin + +The mixin supports four size tokens — `mobile`, `tablet`, `desktop`, and `wide`. `mobile` is the one exception to the mobile-first rule: it uses a `max-width` query for small-screen-only overrides. The remaining three use `min-width` queries following the standard mobile-first approach. + +## Sass Variables vs. CSS Custom Properties + +Sass variables (`$variable`) in `_variables.scss` are for values only needed at compile time — primarily alert/status color sets and button shadows — and typically wrap a CSS custom property with a fallback. CSS custom properties (`--memberlite-*` and `--wp--preset--*`) handle anything that needs to be themeable at runtime, including colors, fonts, spacing, and border radius. The `--memberlite-*` variables are set by the [Customizer](customizer.md); the `--wp--preset--*` variables come from `theme.json`. Hardcoded values should be avoided in favor of one of these two systems. + +## Font Awesome Usage + +Use the Sass variable `$font-family-awesome` from `_variables.scss` when rendering Font Awesome icons via `::before` or `::after`. The project uses Font Awesome Free 6.6.0 — refer to [Font Awesome's documentation](https://fontawesome.com/) for icon references. + +## Specificity & Selector Guidelines + +Prefer class selectors over element selectors for components. The project uses the `@use` module system rather than `@import`, so each partial imports only what it needs from `abstracts/`. Some selectors use `:is()` intentionally to group cross-component rules without duplication (see the note in `_shortcodes.scss`). Using an ID selector for specificity is acceptable when PMPro or another plugin requires it. + +## RTL Support + +The project includes `.rtlcssrc.json`, so styles are automatically mirrored for right-to-left languages via `rtlcss` during the build. No manual RTL overrides are needed in most cases. + +## Child Theme Guidelines + +Custom styles for a child theme belong in the child theme's `style.css`, not in the SCSS files compiled into `build/css/main.css` in the parent theme. + +--- + +**Last Updated**: 2026-03-11 diff --git a/docs/customizer.md b/docs/customizer.md new file mode 100644 index 00000000..dfd1b912 --- /dev/null +++ b/docs/customizer.md @@ -0,0 +1,154 @@ +# Customizer Documentation + +## TL;DR + +Memberlite 7.0 changed how we save colors. We added new color schemes, layout settings, and theme variations. We also built tools to support users upgrading from older versions. + +The Customizer is the single source of truth for colors and fonts. We sync those values to `theme.json`. + +--- + +## Overview + +Memberlite is a hybrid theme. It supports both the Classic Customizer and the block editor. + +In 7.0, we built a new system to manage color schemes. This system works across both the Customizer and the block editor. + +We also added new layout settings for: +- Header +- Footer +- Single posts + +We introduced theme variations. A variation applies multiple settings at once. This gives users a quick way to change the look of their site. + +The Customizer is the source of truth for colors and fonts. + +The `header_output()` function in `inc/customizer.php` converts those values into CSS variables in `:root`. + +We sync colors to `theme.json` using the `wp_theme_json_data_theme` filter. + +--- + +## Customizer Settings & Controls + +Most Customizer logic lives in: + +`inc/customizer.php` + +Start with the `register()` function. + +--- + +## Color Schemes + +### Color Scheme vs. Variation + +Both are presets. + +A color scheme controls colors only. + +A variation controls colors, layout, typography, and other settings. + +A variation can include a color scheme. + +--- + +## Color Scheme History + +Before 7.0, we stored hex colors with a leading `#`. + +We did not sync Customizer colors with `theme.json`. + +In 7.0, we: + +- Standardized how we store hex values +- Synced Customizer colors to `theme.json` +- Created one source of truth for default colors + +We kept the existing `memberlite_color_schemes` filter. + +We added: +- New schemes in `inc/colors.php` +- Legacy schemes in `inc/deprecated.php` + +We added migration logic in: `inc/updates/update_7_0.php` + +We also updated the Import Theme Settings tool to convert legacy schemes. + +When a user upgrades to 7.0 (or imports old settings), we set the color scheme to: `custom` + +--- + +## Related Functions + +This is not a full list. Explore the codebase for more. + +### `functions.php` + +- `memberlite_filter_theme_json()` - Syncs customizer colors to `theme.json` + +- `memberlite_dedupe_editor_color_palette()` - Removes duplicate colors from the editor palette + +--- + +### `inc/defaults.php` + +- `memberlite_get_defaults()` - Returns default theme settings + +--- + +### `inc/colors.php` + +- `memberlite_get_color_preset_map()` - Maps theme_mod keys to: + - CSS variables + - Editor colors + - Customizer labels + +- `memberlite_get_default_colors()` - Returns default colors + +- `memberlite_get_active_colors()` - Returns all 17 saved color values + +- `memberlite_is_dark_color()` - Returns true if a hex color is "dark" based on WCAG relative luminance + - This is key in setting up hover colors for button and links styled like buttons across the site. + +--- + +### `inc/customizer.php` + +- `register()` - Registers customizer settings and controls + +- `header_output()` - Outputs CSS variables in `:root` + +- `customizer_controls_js()` - Loads JS for customizer controls. + - This is where we update color pickers based on the selected color scheme. + +- `live_preview()` - Loads JS for live preview. + - This is where we define CSS variables and apply inline CSS for previewing setting changes. + +--- + +### `js/customizer.js` + +Defines CSS variables and applies inline CSS during live preview. + +--- + +### `js/customizer-controls.js` + +Updates color pickers when a scheme changes. + +If a user edits one color, this script sets the scheme to: `custom` + +--- + +## Adding a New Customizer Setting + +In `inc/customizer.php`, use one of these helpers: + +- `add_memberlite_setting_control()`: Use for standard settings + +- `add_memberlite_color_control()`: Use for color picker settings + +--- + +**Last Updated**: 2026-03-11 diff --git a/docs/patterns.md b/docs/patterns.md new file mode 100644 index 00000000..f26cdc9b --- /dev/null +++ b/docs/patterns.md @@ -0,0 +1,406 @@ +# Memberlite Block Patterns + +This document explains how block patterns are set up in the Memberlite theme and provides guidelines for contributors who want to add new patterns or fix existing ones. + +## Table of Contents + +1. [Overview](#overview) +2. [Directory Structure](#directory-structure) +3. [How Patterns Work](#how-patterns-work) +4. [Adding a New Pattern](#adding-a-new-pattern) +5. [Fixing Existing Patterns](#fixing-existing-patterns) +6. [Image Guidelines](#image-guidelines) +7. [Pattern Categories](#pattern-categories) + +--- + +## Overview + +Memberlite uses WordPress's native block pattern registration system. Patterns are pre-designed block layouts that users can insert into their pages/posts via the block inserter in the WordPress editor. + +**Key Features:** +- **Auto-registration**: Patterns are automatically registered by WordPress when placed in the `patterns/` folder +- **Custom categories**: 8 Memberlite-specific categories organize patterns by use case +- **Reusable images**: Dedicated image library specifically for pattern placeholder content + +--- + +## Directory Structure + +``` +wp-content/themes/memberlite/ +├── patterns/ # Pattern PHP files (auto-registered by WordPress) +│ ├── about-the-founder.php +│ ├── board-of-directors.php +│ ├── [29 other pattern files] +│ └── images/ # Pattern-specific images +│ ├── experts/ # Instructor photos, podcast hosts, expert headshots +│ │ ├── cathryn-lavery-fMD_Cru6OTk-unsplash-md.jpg +│ │ └── [25 other images] +│ ├── landscapes/ # Background images, scenery, hero images +│ │ ├── joshua-earle-9idqIGrLuTE-unsplash-md.jpg +│ │ └── [19 other images] +│ └── people/ # Team members, testimonials, general people photos +│ ├── alex-starnes-WYE2UhXsU1Y-unsplash-sm.jpg +│ └── [25 other images] +│ +├── inc/ +│ └── patterns.php # Registers custom pattern categories +│ +└── assets/images/ # Theme-wide images (NOT for patterns) + └── [theme assets] +``` + +--- + +## How Patterns Work + +### Automatic Registration + +WordPress automatically registers any PHP file in the `patterns/` directory as a block pattern. No manual registration code is needed. + +### Pattern File Format + +Each pattern file must follow this structure: + +```php + + + +
+ +
+ +``` + +**Metadata Fields:** +- **Title**: Human-readable name shown in block inserter (plain text, no translation functions) +- **Slug**: Unique identifier in format `memberlite/pattern-name` +- **Description**: Short explanation of the pattern's purpose +- **Categories**: Comma-separated list of category slugs (see [Pattern Categories](#pattern-categories)) +- **Keywords**: Search terms to help users find the pattern + +### Category Registration + +Custom categories are registered in `inc/patterns.php`: + +```php +function memberlite_register_pattern_categories() { + $categories = array( + 'memberlite-about' => __( 'Memberlite - About', 'memberlite' ), + 'memberlite-community' => __( 'Memberlite - Community', 'memberlite' ), + // ... more categories + ); + + foreach ( $categories as $category_id => $category_name ) { + register_block_pattern_category( + $category_id, + array( 'label' => $category_name ) + ); + } +} +add_action( 'init', 'memberlite_register_pattern_categories' ); +``` + +--- + +## Adding a New Pattern + +### Method 1: Create Pattern in Block Editor (Recommended) + +1. **Build your pattern** in the WordPress block editor on a test page +2. **Copy the block markup**: + - Select all blocks in your pattern + - Click the three dots menu → Copy + - This copies the HTML comment markup +3. **Create a new PHP file** in `patterns/` directory (e.g., `my-new-pattern.php`) +4. **Add the pattern structure**: + +```php + + +``` + +5. **Fix image paths** (if your pattern includes images): + - Replace any image URLs with the proper PHP format (see [Image Path Rules](#image-path-rules)) +6. **Test the pattern**: + - Refresh the block editor + - Search for your pattern by name or keyword + - Insert it and verify it displays correctly + +### Method 2: Duplicate and Modify Existing Pattern + +1. **Find a similar pattern** in the `patterns/` directory +2. **Copy the file** and rename it (e.g., `cp testimonials-grid-of-two.php testimonials-grid-of-three.php`) +3. **Update the metadata** at the top of the file +4. **Modify the block markup** as needed +5. **Update image paths** if necessary +6. **Test the pattern** + +--- + +## Fixing Existing Patterns + +### Common Issues and Fixes + +#### 1. Broken Image URLs + +**Problem**: Images not loading in the pattern. + +**Cause**: Incorrect image path format. + +**Fix**: Ensure all image URLs use this exact format: + +```html + +Description + + +Description + + +Description +``` + +#### 2. Unclosed Quotation Marks + +**Problem**: Pattern breaks or displays oddly. + +**Cause**: Missing closing quotes on `src` or `alt` attributes. + +**Fix**: Check all `` tags have properly closed attributes: + +```html + +Description + + + + + +Description" +``` + +#### 3. Pattern Not Appearing in Block Inserter + +**Possible causes:** +- Missing or incorrect metadata in DocBlock comment +- Invalid category slug in `Categories:` field +- PHP syntax error in the file +- File isn't saved with `.php` extension + +**Fix**: Verify the pattern file structure matches the format shown in [Pattern File Format](#pattern-file-format). + +--- + +## Image Guidelines + +### Image Path Rules + +All images used in patterns **must** be stored in the `patterns/images/` directory structure and referenced using this format: + +```php +/patterns/images/SUBDIRECTORY/filename.jpg +``` + +**Subdirectory Guidelines:** + +| Subdirectory | Use For | Examples | +|--------------|---------|----------| +| `experts/` | Instructor photos, podcast hosts, expert headshots, individual professional portraits | about-the-founder, instructor-profile, video-feature-section | +| `people/` | Team members, board members, testimonials, directory photos, general people | meet-the-team, board-of-directors, testimonials-grid | +| `landscapes/` | Background images, scenery, hero images, abstract backgrounds | call-to-action-with-image, hero sections | + +**Image Size Conventions:** +- `-sm.jpg` = Small (typically under 400px wide) +- `-md.jpg` = Medium (typically 400-1200px wide) +- Use `-sm` for thumbnails, avatars, small decorative images +- Use `-md` for hero images, featured images, large displays + +### Why patterns/images/ Instead of assets/images/? + +**Reason 1: Portability** +Patterns are designed to be copy-paste reusable. When patterns reference images in their own directory, the images travel with the patterns if they're ever extracted, shared, or moved to another theme. + +**Reason 2: Organization** +Pattern placeholder images serve a different purpose than theme assets: +- **Pattern images** = Placeholder/demo content meant to be replaced by users +- **Theme assets** = Structural images (logos, icons, backgrounds) that are part of the theme's design + +**Reason 3: Licensing Clarity** +Keeping pattern images separate makes it clear which images are: +- Demo content (can be replaced without affecting theme functionality) +- Core theme assets (required for the theme to function properly) + +### Image Sources and Copyright + +**Current Source**: [Unsplash](https://unsplash.com/) + +**License Requirements**: +Only use images that meet ONE of these criteria: +- ✅ **Public domain** (CC0, no attribution required) +- ✅ **Open source license** without attribution requirements +- ✅ **Unsplash License** (free to use, no attribution required but appreciated) + +**Do NOT use images that:** +- ❌ Require attribution (unless you add proper attribution in the pattern) +- ❌ Are copyrighted without permission +- ❌ Have restrictive commercial use clauses +- ❌ Require link-backs or credit + +**Best Practice**: When downloading from Unsplash: +1. Copy the photo credit from Unsplash +2. Include photographer name in the image filename (we already do this: `photographer-name-photoId-unsplash-sm.jpg`) +3. Add detailed `alt` text describing the image +4. Consider adding a credit comment in the pattern file: + +```php + +... +``` + +### Adding New Images + +1. **Download image** from Unsplash (or other approved source) +2. **Rename following convention**: `photographer-name-photoId-unsplash-size.jpg` + - Example: `alex-starnes-WYE2UhXsU1Y-unsplash-sm.jpg` +3. **Save to appropriate subdirectory**: + - `patterns/images/experts/` for individual professional portraits + - `patterns/images/people/` for team/group photos + - `patterns/images/landscapes/` for backgrounds/scenery +4. **Optimize image** before committing: + - Compress to reasonable file size (use tools like ImageOptim, TinyPNG) + - Aim for under 200KB for `-sm`, under 500KB for `-md` +5. **Use in pattern** with proper path format + +--- + +## Pattern Categories + +### Available Categories + +| Category Slug | Label | Use For | +|---------------|-------|---------| +| `memberlite-about` | Memberlite - About | About pages, founder bios, organization info | +| `memberlite-community` | Memberlite - Community | Member spotlights, community features, directory sections | +| `memberlite-content` | Memberlite - Content | Content upgrades, post grids, newsletter sections | +| `memberlite-courses` | Memberlite - Courses | Course layouts, curriculum, instructor profiles | +| `memberlite-cta` | Memberlite - Call to Action | Marketing, promotions, membership CTAs | +| `memberlite-media` | Memberlite - Media | Video, podcast, media features | +| `memberlite-team` | Memberlite - Team | Team grids, staff listings, board members | +| `memberlite-testimonials` | Memberlite - Testimonials | Reviews, social proof, user feedback | + +### Using Multiple Categories + +Patterns can belong to multiple categories. Separate them with commas: + +```php +/** + * Categories: memberlite-about, memberlite-team + */ +``` + +### Adding a New Category + +If you need to add a new pattern category: + +1. **Edit** `inc/patterns.php` +2. **Add to the `$categories` array**: + +```php +$categories = array( + // ... existing categories ... + 'memberlite-yourcategory' => __( 'Memberlite - Your Category', 'memberlite' ), +); +``` + +3. **Use the new category** in pattern files: + +```php +/** + * Categories: memberlite-yourcategory + */ +``` + +--- + +## Testing Patterns + +### Manual Testing Checklist + +- [ ] Pattern appears in block inserter +- [ ] Pattern is in the correct category +- [ ] All images load correctly +- [ ] Pattern is responsive (test mobile, tablet, desktop) +- [ ] Pattern respects theme colors/fonts +- [ ] No PHP errors in debug.log +- [ ] No JavaScript console errors +- [ ] Pattern works with different color schemes (if applicable) + +### Quick Test in Browser + +1. Go to **Pages → Add New** in WordPress admin +2. Click the **Pattern icon** (grid) in the block inserter +3. Search for your pattern by name or browse categories +4. Insert the pattern and verify it looks correct +5. Test editing the pattern content (change text, swap images) + +--- + +## Troubleshooting + +### Pattern Not Showing Up + +1. **Check file location**: Must be in `patterns/` directory +2. **Check file extension**: Must be `.php` +3. **Check metadata format**: DocBlock must be formatted correctly +4. **Check for PHP errors**: Look in `wp-content/debug.log` +5. **Clear cache**: If using caching plugins, clear cache. Also, check your environment's PHP cache settings. + +### Images Not Loading + +1. **Verify image exists** at the specified path +2. **Check path format**: Must use `/patterns/images/...` +3. **Check file permissions**: Images must be readable by the web server +4. **Check subdirectory**: Make sure the image is in the correct folder (experts/people/landscapes) + +### Pattern Breaks Layout + +1. **Validate HTML**: Check for unclosed tags, mismatched quotes +2. **Check for missing closing quotes** on attributes +3. **Test in isolation**: Try your pattern on a blank page to isolate issues +4. **Compare with working pattern**: Look at similar patterns that work correctly + +--- + +## Additional Resources + +- [WordPress Block Pattern Documentation](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-patterns/) +- [Unsplash License](https://unsplash.com/license) +- [WordPress Block Markup Reference](https://developer.wordpress.org/block-editor/reference-guides/) + +--- + +**Last Updated**: 2026-03-10 diff --git a/docs/project-structure.md b/docs/project-structure.md new file mode 100644 index 00000000..cb77c518 --- /dev/null +++ b/docs/project-structure.md @@ -0,0 +1,163 @@ +# Project Structure + +This document describes the **top-level structure** of the Memberlite theme repository and explains the responsibility of each directory. It is intended to help developers quickly understand where code lives and where changes should be made. + +## General Guidelines + +* **Business back-end logic → `/inc`** +* **Reusable markup → `/components`** +* **Static assets → `/assets`** +* **Front-end CSS source → `/src/scss`** (compiled to `/build/css/main.css`) +* **CSS for the editor, admin, print, plugin integrations, etc. → `/css`** (manually maintained, not part of the build) +* **JS for the front or back-end (including customizer) → `/js`** + +When adding new functionality, prefer extending existing directories rather than creating new top-level folders unless there is a clear architectural need. + +--- + +## Top-Level Directories + +### `/assets` + +Static front-end assets for the theme. + +Typically contains: + +* Fonts used to customize Memberlite via the Customizer settings or the Block Editor options +* Images (mostly icons) + +--- + +### `/bbpress` + +bbPress specific template override for single user content. + +--- + +### `/build` + +Compiled output from the build process. **Do not edit files here directly.** + +Contains: + +* `css/main.css` — compiled from `src/scss/main.scss` +* `css/main.rtl.css` — auto-generated RTL version of `main.css` +* Block and editor assets (future — built from `src/blocks/` and `src/editor/`) + +See [Build Process](build-process.md) for details. + +--- + +### `/components` + +Reusable theme components and partials for the header, footer, page and post templates. + +--- + +### `/css` + +Vanilla CSS files that are **not** part of the Sass build process. These are committed and edited directly. + +Typically contains: + +* Integration stylesheets for third-party plugins (`bbpress.css`, `lifterlms.css`, `woocommerce.css`) +* Utility stylesheets (`admin.css`, `editor.css`, `print.css`, `customizer.css`) + +See [CSS Guidelines](css-guidelines.md) for guidance on when styles belong here vs. in `/src/scss`. + +--- + +### `/docs` + +Where Memberlite's documentation files live. Ideally they are written and pushed with related code in PRs to ensure the docs are always up to date. + +--- + +### `/font-awesome` + +Icon fonts for the Memberlite theme. + +--- + +### `/inc` + +Theme PHP logic and internal APIs. + +Typically contains: + +* Theme setup and configuration +* Customizer logic +* Hooks, filters, and helper functions +* Integration logic with WordPress core and plugins + +**Notes:** + +* This is the primary "back-end" directory for the theme. +* Most extension points (filters/actions) live here. + +--- + +### `/js` + +JavaScript for the front end and back end, including the Customizer preview. Files are authored and committed directly — no build step. + +--- + +### `/languages` + +Translation files for internationalization. + +Typically contains: + +* `.pot`, `.po`, and `.mo` files + +**Notes:** + +* Used for theme localization. +* Strings throughout the theme should be wrapped in translation functions. + +--- + +### `/shortcodes` + +Where the theme's custom shortcodes are defined. This was previously part of Memberlite's Shortcode/Elements Add Ons. + +--- + +### `/src` + +Source files that require a build step before use. + +Contains: + +* `scss/` — Sass source files, compiled to `build/css/main.css`. See [CSS Guidelines](css-guidelines.md) for directory structure and conventions. +* `blocks/` — Custom Gutenberg block source (future use). Built with `@wordpress/scripts` to `build/`. +* `editor/` — Block editor settings and customizations (future use). + +See [Build Process](build-process.md) for details. + +--- + +### `/templates` + +Top-level page templates. + +Typically contains: + +* Custom page templates registered with WordPress +* Specialized templates that don't fit cleanly into the default hierarchy + +--- + +## Root-Level PHP Files + +In addition to the folders above, the theme root contains key PHP files such as: + +* `functions.php` – Bootstraps the theme and loads files from `/inc` +* Template hierarchy files (`index.php`, `single.php`, `page.php`, etc.) +* `style.css` – Theme metadata header and a small base stylesheet. The primary front-end stylesheet is `build/css/main.css`. +* `theme.json` – Block editor configuration. Memberlite is a hybrid WordPress theme, so the theme.json is minimal and syncs colors and fonts with the Customizer settings. + +--- + +**Last Updated**: 2026-03-12 diff --git a/footer.php b/footer.php index ed9fd05f..2baaef17 100644 --- a/footer.php +++ b/footer.php @@ -8,51 +8,28 @@ */ ?> - -
- - - - - - - - - -