feature: Initial commit

This commit is contained in:
Keith Solomon
2026-05-25 12:04:18 -05:00
commit 2e5bfaba89
152 changed files with 28391 additions and 0 deletions
+12
View File
@@ -0,0 +1,12 @@
# EditorConfig is awesome: https://EditorConfig.org
# top-most EditorConfig file
root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
+2
View File
@@ -0,0 +1,2 @@
LOCALHOST_URL=http://domain.local
BROWSERSYNC_PORT=5000
+33
View File
@@ -0,0 +1,33 @@
name: PHPCS check
on:
# push:
pull_request:
# Allow manually triggering the workflow.
workflow_dispatch:
# Cancel all previous workflow runs for the same branch that have not yet completed.
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
phpcs:
name: PHPCS
runs-on: ubuntu-22.04
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup PHP
uses: "shivammathur/setup-php@v2"
with:
php-version: "8.2"
ini-values: "memory_limit=1G"
coverage: none
tools: cs2pr
- name: Install Composer dependencies
uses: "ramsey/composer-install@v3"
- name: Run PHPCS checks
continue-on-error: true
run: vendor/bin/phpcs --standard=.phpcs.xml --report-full --report-checkstyle=./phpcs-report.xml .
- name: Show PHPCS results in PR
run: cs2pr ./phpcs-report.xml
+26
View File
@@ -0,0 +1,26 @@
name: Sync TODOs with Issues
on:
workflow_dispatch:
push:
paths-ignore:
- '**.md'
jobs:
sync_todos:
runs-on: ubuntu-latest
permissions:
contents: write
issues: write
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Sync TODOs
uses: Solo-Web-Works/TODO-Sync@v2
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
summary_file: TODO_SUMMARY.md
dry_run: false
commit: false
+64
View File
@@ -0,0 +1,64 @@
name: Deploy to WPEngine
on:
workflow_dispatch
# Remove "workflow_dispatch" above and uncomment the lines below to enable deploy on push
# only do this once you're actually ready to start deploying to Flywheel
# push:
# branches:
# - main
jobs:
build:
runs-on: ubuntu-latest
if: ${{ !contains(github.event.head_commit.message, '#skipGA') }}
steps:
- uses: actions/checkout@v3
- name: Get Composer Cache Directory
id: composer-cache
run: |
echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: |
${{ runner.os }}-composer-
- name: Install PHP Composer
uses: php-actions/composer@v6
with:
php_version: "8.2"
- name: Cache node modules
id: cache-npm
uses: actions/cache@v3
env:
cache-name: cache-node-modules
with:
# npm cache files are stored in `~/.npm` on Linux/macOS
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- name: Install npm dependencies
run: npm install
- name: Run build task
run: npm run build
- name: Clean up node modules (not needed to deploy)
run: rm -rf node_modules
- name: Deploy to WPE
uses: wpengine/github-action-wpe-site-deploy@v3
with:
WPE_SSHG_KEY_PRIVATE: ${{ secrets.WPE_SSHG_KEY_PRIVATE }}
WPE_ENV: vdiv5
FLAGS: '-azvr --inplace --delete --exclude=".*"'
REMOTE_PATH: "wp-content/themes/vdi-v5"
+16
View File
@@ -0,0 +1,16 @@
*~
.DS_Store
vendor
node_modules
static/dist/
.env
bak/
testimonials.class.php
resource-filter/
phpcs-results.txt
# Playwright
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/
+46
View File
@@ -0,0 +1,46 @@
<?xml version="1.0"?>
<ruleset name="Coding Style Checks">
<description>Coding Style Checks.</description>
<ini name="error_reporting" value="E_ALL &#38; ~E_DEPRECATED" />
<arg value="sp"/>
<arg name="colors"/>
<arg name="extensions" value="php,html"/>
<arg name="parallel" value="2048"/>
<exclude-pattern>vendor/</exclude-pattern>
<exclude-pattern>node_modules/</exclude-pattern>
<rule ref="WordPress">
<exclude name="Generic.WhiteSpace.DisallowSpaceIndent.SpacesUsed"/>
<exclude name="Generic.Functions.CallTimePassByReference"/>
<exclude name="Universal.WhiteSpace.PrecisionAlignment.Found"/>
<exclude name="WordPress.WP.I18n.NonSingularStringLiteralText"/>
<exclude name="WordPress.WP.I18n.NonSingularStringLiteralDomain"/>
<exclude name="WordPress.WP.I18n.NoEmptyStrings"/>
<exclude name="WordPress.WP.EnqueuedResourceParameters.MissingVersion"/>
<exclude name="WordPress.PHP.YodaConditions.NotYoda"/>
<exclude name="WordPress.PHP.NoSilencedErrors.Discouraged"/>
<exclude name="WordPress.Files.FileName.NotHyphenatedLowercase"/>
<exclude name="WordPress.Files.FileName.InvalidClassFileName"/>
<exclude name="WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase"/>
<exclude name="WordPress.NamingConventions.ValidVariableName.InterpolatedVariableNotSnakeCase"/>
<exclude name="WordPress.NamingConventions.ValidVariableName.PropertyNotSnakeCase"/>
<exclude name="WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase"/>
<exclude name="WordPress.NamingConventions.ValidHookName.NotLowercase"/>
<exclude name="WordPress.NamingConventions.ValidHookName.UseUnderscores"/>
<exclude name="WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid"/>
<exclude name="WordPress.NamingConventions.ValidFunctionName.FunctionNameInvalid"/>
<exclude name="WordPress.DB.SlowDBQuery.slow_db_query_tax_query"/>
<exclude name="WordPress.DB.SlowDBQuery.slow_db_query_meta_query"/>
<exclude name="WordPress.Security.NonceVerification.Recommended"/>
<exclude name="Squiz.Commenting.FileComment.MissingPackageTag"/>
<exclude name="Squiz.Commenting.FileComment.Missing"/>
<exclude name="Squiz.Commenting.FileComment.WrongStyle"/>
<exclude name="Squiz.Commenting.InlineComment.InvalidEndChar"/>
</rule>
<rule ref="Internal.NoCodeFound">
<severity>0</severity>
</rule>
</ruleset>
+16
View File
@@ -0,0 +1,16 @@
{
"workbench.colorCustomizations": {
"tree.indentGuidesStroke": "#3d92ec",
"activityBar.background": "#213309",
"titleBar.activeBackground": "#2E470C",
"titleBar.activeForeground": "#F6FCEE",
"titleBar.inactiveBackground": "#213209",
"titleBar.inactiveForeground": "#F7FCEF",
"statusBar.background": "#213209",
"statusBar.foreground": "#F7FCEF",
"statusBar.debuggingBackground": "#213209",
"statusBar.debuggingForeground": "#F7FCEF",
"statusBar.noFolderBackground": "#213209",
"statusBar.noFolderForeground": "#F7FCEF"
}
}
+47
View File
@@ -0,0 +1,47 @@
<?php
/**
* Page Not Found
*
* @package BasicWP
* @since 1.0.0
*/
namespace BasicWP;
get_header();
?>
<section class="error-404 container">
<h2>Page Not Found</h2>
<h3>Sorry, the page you're looking for doesn't exist.</h3>
<p>It might have been removed, had its name changed, or is temporarily unavailable.</p>
<p>
<form role="search" method="get" class="search-form" action="<?php echo esc_url( home_url( '/' ) ); ?>">
<label>
<span class="screen-reader-text"><?php echo esc_html__( 'Search for:', 'label' ); ?></span>
Try searching for it: <input type="search" class="search-field border border-secondary px-2 rounded-md" placeholder="<?php echo esc_attr_x( 'Search &hellip;', 'placeholder' ); ?>" value="<?php echo get_search_query(); ?>" name="s" title="<?php echo esc_attr__( 'Search for:', 'label' ); ?>" />
</label>
<x-button
btnClasses="search-submit button text-center"
element="button"
type="submit"
title="<?php echo esc_attr__( 'Search', 'submit button' ); ?>"
color="primary"
variant="default"
size="small"
width="auto"
></x-button>
</form>
</p>
<p>Or check out the links below:</p>
<ul class="list-inside">
<li><a href="<?php echo esc_url( home_url( '/' ) ); ?>">Home</a></li>
<li><a href="<?php echo esc_url( home_url( '/news' ) ); ?>">Posts</a></li>
<li>If all else fails, <a href="<?php echo esc_url( home_url( '/contact' ) ); ?>">Contact Us</a></li>
</ul>
</section>
<?php get_footer(); ?>
+488
View File
@@ -0,0 +1,488 @@
# VDI WordPress Theme Starter v5
VDI WordPress Theme Starter v5 is a minimal WordPress theme designed as a starting point for custom theme development. It focuses on modern development approaches with a lean architecture that avoids the overhead of theme frameworks.
Repo: [https://github.com/Vincent-Design-Inc/VDI-Starter-v5](https://github.com/Vincent-Design-Inc/VDI-Starter-v5)
AC Bug/Issue Tracking: [https://next-app.activecollab.com/119590/projects/4553?modal=Task-98192-4553](https://next-app.activecollab.com/119590/projects/4553?modal=Task-98192-4553)
## Key Features
- Tailwind CSS
- Namespaced PHP for isolation
- Built-in support for ACF blocks
- Fast development workflow with watch and compile functions
## Project Structure
```plaintext
VDI-Starter-v5/
├── acf/ # ACF field group JSON definitions
├── bin/ # Build scripts
├── content/ # Sample page and post content for testing
├── lib/ # PHP library files
│ ├── class-breadcrumbs.php # Breadcrumb generation
│ ├── class-acf.php # ACF integration
│ ├── class-enqueue.php # Assets enqueuing
│ ├── class-menuitems.php # Navigation menu builder
│ ├── class-resources.php # Custom Resources post type
│ ├── extras.php # Miscellaneous theme functions
│ ├── helpers.php # Helper functions
│ ├── hooks.php # WordPress hooks and filters
│ ├── search-features.php # Enhanced search functionality
│ └── show-template.php # Script to show which template is used for the current page
├── static/ # Static assets
│ ├── dist/ # Compiled assets
│ ├── img/ # Static theme images
│ └── js/ # JavaScript files
│ ├── components/ # JS components
│ │ └── button.js # Button custom component
│ ├── modules/ # JS theme modules
│ │ ├── GetHeaderHeight.js # Calculate header height and set it as a CSS variable
│ │ ├── Navigation.js # Script controlling navigation behavior
│ │ └── TagExternalLinks.js # Tags external links with appropriate attributes
│ ├── admin.js # Admin-specific JS
│ └── theme.js # Main theme JS
├── styles/ # CSS styles
│ ├── backend/ # Admin styles
│ ├── base/ # Base styles
│ ├── blocks/ # Block styles
│ ├── components/ # Component styles
│ ├── fonts/ # Font styles
│ └── theme.css # Main CSS entry point
├── tests/ # Automated Playwright tests
│ └── site-a11y.spec.js # Site Accessibility tests
├── views/ # Template views
│ ├── blocks/ # Custom ACF blocks
│ ├── icons/ # SVG icons
│ ├── forms/ # Form templates
│ └── partials/ # Reusable template parts
├── 404.php # 404 error template
├── footer.php # Footer template
├── front-page.php # Front page template
├── functions.php # Main functions file
├── header.php # Header template
├── index.php # Main template
├── page.php # Page template
├── search.php # Search results template
├── sidebar.php # Main sidebar template
├── sidebar-page.php # Page sidebar template
├── single.php # Single post template
├── style.css # Theme metadata
└── whitelist.php # Tailwind class whitelist
```
## Core Files and Templates
### Theme Core Files
- **style.css**: Contains theme metadata and registration information
- **functions.php**: Initializes the theme and loads necessary dependencies
- **class-acf.php**: Handles Advanced Custom Fields (ACF) functionality
- **class-breadcrumbs.php**: Generates breadcrumb navigation for the site
- **class-enqueue.php**: Manages script and style enqueuing for frontend and backend
- **class-menuitems.php**: Builder for the navigation menus
- **class-resources.php**: Custom post type management for "Resources", included as an example of CPT usage
- **extras.php**: Miscellaneous theme functions, including sidebar and page header checks
- **helpers.php**: Utility functions for common tasks
- **hooks.php**: Contains WordPress hooks and filters for theme functionality
- **search-features.php**: Enhances search functionality with custom queries and filters
- **show-template.php**: Outputs the current template file being used for debugging
- **whitelist.php**: Contains Tailwind CSS class whitelist for styles used in WordPress editor
### Template Files
- **header.php**: Defines the site header with primary navigation
- **footer.php**: Defines the site footer with widget areas and copyright
- **front-page.php**: Template for the front page of the site
- **page.php**: Displays individual static pages with optional sidebar
- **index.php**: Main template file that displays the blog posts list
- **single.php**: Displays individual posts with author and category information
- **search.php**: Displays search results with pagination
- **404.php**: Custom "Page Not Found" template with search form
- **sidebar.php**: Primary sidebar template
- **sidebar-page.php**: Page-specific sidebar template
- **views/forms/search.php** - Search form template for use in various locations
### Included Blocks
- **Boilerplate**: Example block for custom development
- **Accordion**: Collapsible content sections
- **Buttons**: Group of buttons
- **Button**: Configurable button element
- **Grid**: Flexible grid layout system
- **Grid Cell**: Individual grid item
- **Homepage Hero**: Hero section typically used on homepage
- **Media Text**: Image or video with accompanying text
- **Media Text with Inner Blocks**: Media-text with nested block support
- **Page Children**: Displays child pages of current page
- **Section**: Container block with background and width (contained and full-screen width) options
## Library Functions and Classes
### helpers.php
```php
getFieldValue($field_path)
```
- Retrieves a nested value from an ACF field using dot notation
- **Parameters**: `$field_path` - The dot-notated path to the value (e.g., `contact_info.phone`)
- **Returns**: The value at the specified path
```php
customMenuOrder($menu_ord)
```
- Customizes the order of admin menu items in WordPress dashboard
- Add other admin menu URLs to the array to change their order
- **Parameters**: `$menu_ord` - The existing menu order array
- **Returns**: Array specifying the custom menu order, or true for default order
```php
blockCategories($categories)
```
- Adds custom block category for theme blocks
- **Parameters**: `$categories` - The existing block categories
- **Returns**: Modified array of categories
```php
consoleLog($data)
```
- Utility function to print a variable to the console for debugging
- **Parameters**: `$data` - The data to print to the console
### search-features.php
```php
pageSearch($query)
```
- Modifies the WordPress query for page search functionality
- **Parameters**: `$query` - The WordPress query object
- **Returns**: void
```php
postSort($key)
```
- Returns a sorting function for WP_Post objects in reverse order
- **Parameters**: `$key` - WP_Post object property used for sorting
- **Returns**: Sorting function
```php
dedupe($posts, $key)
```
- Removes duplicate posts based on a specified key
- **Parameters**:
- `$posts` - Array of posts
- `$key` - Key to check for duplicates
- **Returns**: Filtered array of posts
```php
searchResultFilter($posts, $query)
```
- Filters and processes search results
- **Parameters**:
- `$posts` - Array of posts
- `$query` - The search query
- **Returns**: Filtered array of posts
### extras.php
```php
getChildrenPages()
```
- Gets child pages of the current page
- **Returns**: Array of child pages or empty array
```php
hasSidebar()
```
- Checks if sidebar should be rendered
- **Returns**: Boolean indicating sidebar presence
```php
hasPageHeader()
```
- Checks if the page should render a page header
- **Returns**: Boolean indicating page header presence
```php
createOwnerRole()
```
- Creates a new WordPress role named "Owner" with admin capabilities minus plugin/theme management
- **Returns**: void
```php
getTheTitle()
```
- Gets the title based on the current context (archive, search, etc.)
- **Returns**: Appropriate title string
```php
divWrapper($content)
```
- Wraps iframes and embeds in a div with the class "embed"
- **Parameters**: `$content` - The content to process
- **Returns**: Modified content
### Class: Resources (class-resources.php)
Custom post type and taxonomy management for "Resources"
### Class: Enqueue (class-enqueue.php)
Manages script and style enqueueing
```php
enqFEAssets()
```
- Enqueues frontend CSS and JS files
- **Returns**: void
```php
enqBEAssets()
```
- Enqueues backend (admin/editor) CSS and JS files
- **Returns**: void
### Class: ACF (class-acf.php)
Handles ACF (Advanced Custom Fields) functionality
```php
saveJson($path)
```
- Sets the path for saving ACF field groups as JSON
- **Parameters**: `$path` - The default path
- **Returns**: Modified path
```php
loadJson($paths)
```
- Sets the path for loading ACF field groups from JSON
- **Parameters**: `$paths` - The default paths
- **Returns**: Modified paths array
### Class: Breadcrumbs (class-breadcrumbs.php)
Generates and displays breadcrumb navigation
```php
generate()
```
- Generates breadcrumb data based on the current page
- **Returns**: Array of breadcrumb items
```php
render()
```
- Renders breadcrumb HTML with schema.org markup
- **Returns**: void (echoes HTML)
The class also includes other helper methods for different page types:
- `getHomeBreadcrumb()`
- `getBlogPostsIndexBreadcrumb()`
- `getSinglePostBreadcrumbs()`
- `getCustomPostTypeBreadcrumbs()`
- `getStaticPageBreadcrumbs($post)`
- `getTaxonomyArchiveBreadcrumb()`
- `getPostTypeArchiveBreadcrumb()`
- `getDateArchiveBreadcrumbs()`
- `getMonthArchiveBreadcrumb()`
- `getYearArchiveBreadcrumb()`
- `getAuthorArchiveBreadcrumb()`
- `getSearchBreadcrumb()`
- `get404Breadcrumb()`
## ACF Blocks System
The theme implements a custom block system using Advanced Custom Fields. Blocks are registered automatically in functions.php via the `regACFBlocks()` function.
See `views/blocks/boilerplate/` for a starting point for custom blocks.
### Block Structure
Each block consists of:
- PHP template file (block rendering)
- CSS file (block-specific styles)
- block.json file (block configuration)
See the [ACF Blocks Documentation](https://www.advancedcustomfields.com/resources/#acf-blocks) for more details on creating custom blocks.
## JavaScript Components
The theme utilizes custom web components for enhanced functionality.
### ButtonComponent
A custom HTML element for creating buttons with various configurations:
```javascript
class ButtonComponent extends HTMLElement {
// Attributes:
// - btnClasses: Additional CSS classes
// - element: The element to use (`a` or `button`)
// - type: Button type (for non-anchor elements)
// - url: The URL for links
// - target: Target attribute for links
// - title: Button text
// - color: Button color
// - variant: Button style variant
// - size: Button size
// - width: Button width
}
```
Usage:
```html
<x-button
btnClasses="button text-center"
element="a"
url="https://example.com"
target="_blank"
title="Click Me"
color="primary"
variant="default"
size="medium"
width="auto"
></x-button>
```
## Asset Management
### CSS
The theme uses Tailwind CSS for styling with custom utilities:
- Base styles in `views/styles/base`
- Component styles in `views/styles/components`
- Block-specific styles in individual block folders and `views/styles/blocks`
- Admin/editor styles in `views/styles/backend`
### JavaScript
- **theme.js**: Main frontend JavaScript
- **admin.js**: Admin-specific JavaScript
- **components**: JavaScript components
- **modules**: JavaScript modules for specific functionality (e.g., navigation, focus styling)
### Build System
The theme includes a simple build system in the bin directory:
- **.build.js**: Production build script that compiles Tailwind CSS
- **.watch.js**: Development watch script with BrowserSync for live reloading
- **.utils.js**: Shared utility functions for the build process
## Development Workflow
### Setup
1. Create a new repo using this one as a template.
2. Clone the repo to your local dev folder.
3. Install dependencies (if asked to overwrite, choose "no"):
```bash
# Install theme dependencies
composer install
npm i
# Configure playwright for testing
npm init playwright@latest --yes "--" . '--quiet' '--browser=chromium' '--browser=firefox' '--browser=webkit' '--lang=js'
# Perform initial build
npm run build
```
4. Create .env file from .env.example and set:
- `LOCALHOST_URL`: Local development URL.
- `BROWSERSYNC_PORT`: Port for BrowserSync (default: 5000).
5. Start building your project.
- Run `npm run start` to start the development server with live reloading.
- Activate the theme in your WordPress admin.
- (Optional) Import the sample content from `content`.
### Sart Development Server
- Run `npm run watch` or `npm run start` to start development server with live reloading.
- Changes to PHP, CSS, and JS files will trigger automatic reload.
### Production
- Run `npm run build` to compile and optimize CSS for production (normally handled by GitHub Actions).
### Deployment
- Deployment is handled via GitHub Actions.
- Ensure you update `.github/workflows/wpengine,yml` with the correct WP Engine deployment configuration (environment and theme folder).
## Testing
The theme includes a suite of testing tools to ensure code quality and functionality.
Accessibility tests using Playwright and Axe:
- Test files located in `tests`.
- Run via the [VSCode Playwright extension](https://marketplace.visualstudio.com/items/?itemName=ms-playwright.playwright) or with `npx playwright test --ui` in your terminal for the dedicated Playwright window.
- Tests include:
- `site-a11y.spec.js`: Accessibility tests for the site.
- Generates reports in `playwright-report` and screenshots in `test-results`.
Code linting for modified WordPress coding standards using phpcs:
- Run `composer lint` in your terminal to check PHP files for coding standards violations
- Test results are saved in `phpcs-results.txt`. Review the file for error details.
- Automated fixes noted in the results can be done using `composer fix` in your terminal.
### Files
- `404.php` - Displays a custom "Page Not Found" message when a visitor tries to access a page that doesn't exist.
- `archive.php` - Displays a list of posts from a particular archive, such as a category, date, or tag archive.
- `author.php` - Displays a list of posts by a specific author, along with the author's information.
- `category.php` - Displays a list of posts from a specific category.
- `footer.php` - Contains the footer section of the theme, typically including closing HTML tags and widgets or navigation in the footer area.
- `front-page.php` - The template used for the front page of the site, either static or a blog, depending on the site settings.
- `functions.php` - Imports custom functionality for the theme. **Should not be edited**, all changes/additions should live in the `lib` directory.
- `header.php` - Contains the header section of the theme, typically including the site's title, meta tags, and navigation menu.
- `index.php` - The fallback template for all WordPress pages, used if no other more specific template (like `category.php` or `single.php`) is available.
- `page.php` - Displays individual static pages, such as "About" or "Contact" pages.
- `screenshot.png` - An image of the themes design, shown in the WordPress theme selector to give users a preview of the theme's appearance.
- `search.php` - Displays the results of a search query, showing posts or pages that match the search terms entered by the user.
- `single.php` - Displays individual posts, often used for blog posts or custom post types.
- `tag.php` - Displays a list of posts associated with a specific tag.
### Folders
- `acf` - Storage for ACF field group JSON files. You shouldn't edit anything here manually.
- `bin` - Helper files for the dev and build processes.
- `lib` - Main library for the theme, including the Twig engine and other helper functions.
- `static` - Static assets for the theme, including CSS, JS, and images. The rendered stylesheet is stored here.
- `styles` - The raw CSS files compiled by Tailwind into the final CSS file.
- `views` - Twig view templates for the theme. This is where you will do most of your work.
- `blocks` - ACF block templates.
- `boilerplate` - Example ACF block template. Used to build your own blocks (more on this below).
- `icons` - Icon templates for SVG icons.
- `partials` - Partial templates for the page hero, breadcrumb, and social media sections.
+217
View File
@@ -0,0 +1,217 @@
{
"key": "group_5f7f85a2a3e13",
"title": "Button",
"fields": [
{
"key": "field_67d5cd1108a6e",
"label": "Element",
"name": "element",
"aria-label": "",
"type": "radio",
"instructions": "",
"required": 1,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"choices": {
"a": "&lt;a&gt; Element",
"button": "&lt;button&gt; Element"
},
"default_value": "a",
"return_format": "value",
"allow_null": 0,
"other_choice": 0,
"allow_in_bindings": 0,
"layout": "horizontal",
"save_other_choice": 0,
"wpml_cf_preferences": 1
},
{
"key": "field_5f7f85a71151d",
"label": "Link",
"name": "link",
"aria-label": "",
"type": "link",
"instructions": "",
"required": 1,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"return_format": "array",
"wpml_cf_preferences": 1
},
{
"key": "field_5f7f85bd1151e",
"label": "Color",
"name": "color",
"aria-label": "",
"type": "select",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"choices": {
"primary": "Primary",
"secondary": "Secondary",
"white": "White",
"black": "Black"
},
"default_value": "primary",
"return_format": "value",
"multiple": 0,
"allow_null": 0,
"allow_in_bindings": 1,
"ui": 0,
"ajax": 0,
"placeholder": "",
"create_options": 0,
"save_options": 0,
"wpml_cf_preferences": 1
},
{
"key": "field_5f7f85e41151f",
"label": "Variant",
"name": "variant",
"aria-label": "",
"type": "select",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"choices": {
"default": "Default",
"outline": "Outline"
},
"default_value": "default",
"allow_null": 0,
"multiple": 0,
"ui": 0,
"return_format": "value",
"ajax": 0,
"placeholder": "",
"create_options": 0,
"save_options": 0,
"wpml_cf_preferences": 1
},
{
"key": "field_5f807a47dd2a8",
"label": "Size",
"name": "size",
"aria-label": "",
"type": "select",
"instructions": "Controls the padding of the buttons",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"choices": {
"small": "Small",
"medium": "Medium",
"large": "Large"
},
"default_value": "medium",
"return_format": "value",
"multiple": 0,
"allow_null": 0,
"allow_in_bindings": 1,
"ui": 0,
"ajax": 0,
"placeholder": "",
"create_options": 0,
"save_options": 0,
"wpml_cf_preferences": 1
},
{
"key": "field_605924972a98a",
"label": "Width",
"name": "width",
"aria-label": "",
"type": "select",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"choices": {
"auto": "Auto",
"small": "Small",
"wide": "Wide",
"full": "Full Width"
},
"default_value": "auto",
"return_format": "value",
"multiple": 0,
"allow_null": 0,
"allow_in_bindings": 1,
"ui": 0,
"ajax": 0,
"placeholder": "",
"create_options": 0,
"save_options": 0,
"wpml_cf_preferences": 1
},
{
"key": "field_6924c83e85da9",
"label": "ARIA Label",
"name": "aria_label",
"aria-label": "",
"type": "text",
"instructions": "Used to give the link an accessible name if using \"Read more\" as the title",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"wpml_cf_preferences": 0,
"default_value": "",
"maxlength": "",
"allow_in_bindings": 0,
"placeholder": "",
"prepend": "",
"append": ""
}
],
"location": [
[
{
"param": "block",
"operator": "==",
"value": "acf\/button"
}
]
],
"menu_order": 0,
"position": "normal",
"style": "default",
"label_placement": "top",
"instruction_placement": "label",
"hide_on_screen": "",
"active": true,
"description": "",
"show_in_rest": 0,
"display_title": "",
"acfml_field_group_mode": "translation",
"modified": 1764018311
}
+459
View File
@@ -0,0 +1,459 @@
{
"key": "group_5fd3e006e5da5",
"title": "Global Fields",
"fields": [
{
"key": "field_5fd3e0137bfed",
"label": "Contact Info",
"name": "contact_info",
"aria-label": "",
"type": "group",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"layout": "row",
"sub_fields": [
{
"key": "field_5fd3e0277bfee",
"label": "Email",
"name": "email",
"aria-label": "",
"type": "email",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"placeholder": "",
"prepend": "",
"append": ""
},
{
"key": "field_5fd3e02f7bfef",
"label": "Phone",
"name": "phone",
"aria-label": "",
"type": "text",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"placeholder": "",
"prepend": "",
"append": "",
"maxlength": ""
},
{
"key": "field_684ad36ebaf40",
"label": "Fax",
"name": "fax",
"aria-label": "",
"type": "text",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"maxlength": "",
"allow_in_bindings": 1,
"placeholder": "",
"prepend": "",
"append": ""
},
{
"key": "field_5fd3e0437bff0",
"label": "Address",
"name": "address",
"aria-label": "",
"type": "textarea",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"placeholder": "",
"maxlength": "",
"rows": 3,
"new_lines": "br"
},
{
"key": "field_60381dcb082b5",
"label": "Hours",
"name": "hours",
"aria-label": "",
"type": "textarea",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"placeholder": "",
"maxlength": "",
"rows": 3,
"new_lines": "br"
}
]
},
{
"key": "field_677bf0e692d37",
"label": "Social Media",
"name": "social_media",
"aria-label": "",
"type": "group",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"layout": "row",
"sub_fields": [
{
"key": "field_677bf0e692d3c",
"label": "Facebook",
"name": "facebook",
"aria-label": "",
"type": "url",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"allow_in_bindings": 1,
"placeholder": ""
},
{
"key": "field_677bf0e692d3e",
"label": "Instagram",
"name": "instagram",
"aria-label": "",
"type": "url",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"allow_in_bindings": 1,
"placeholder": ""
},
{
"key": "field_677bf0e692d3d",
"label": "X\/Twitter",
"name": "twitter",
"aria-label": "",
"type": "url",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"allow_in_bindings": 1,
"placeholder": ""
},
{
"key": "field_677bf0e692d41",
"label": "LinkedIn",
"name": "linkedin",
"aria-label": "",
"type": "url",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"allow_in_bindings": 1,
"placeholder": ""
},
{
"key": "field_677bf0e692d40",
"label": "Youtube",
"name": "youtube",
"aria-label": "",
"type": "url",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"allow_in_bindings": 1,
"placeholder": ""
},
{
"key": "field_677bf0e692d3f",
"label": "Pinterest",
"name": "pinterest",
"aria-label": "",
"type": "url",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"allow_in_bindings": 1,
"placeholder": ""
}
]
},
{
"key": "field_60302ebb3604d",
"label": "Header",
"name": "header",
"aria-label": "",
"type": "group",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"layout": "row",
"sub_fields": [
{
"key": "field_60cbbb8fc100a",
"label": "Header Logo",
"name": "header_logo",
"aria-label": "",
"type": "image",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"return_format": "array",
"preview_size": "medium",
"library": "all",
"min_width": "",
"min_height": "",
"min_size": "",
"max_width": "",
"max_height": "",
"max_size": "",
"mime_types": ""
}
]
},
{
"key": "field_61df32ca33bc0",
"label": "Footer",
"name": "footer",
"aria-label": "",
"type": "group",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"layout": "block",
"sub_fields": [
{
"key": "field_61df32e833bc1",
"label": "Footer Logo",
"name": "footer_logo",
"aria-label": "",
"type": "image",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"return_format": "array",
"preview_size": "medium",
"library": "all",
"min_width": "",
"min_height": "",
"min_size": "",
"max_width": "",
"max_height": "",
"max_size": "",
"mime_types": ""
},
{
"key": "field_64761ea8cb0dd",
"label": "Footer Description",
"name": "footer_description",
"aria-label": "",
"type": "wysiwyg",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"tabs": "all",
"toolbar": "full",
"media_upload": 1,
"delay": 0
},
{
"key": "field_61df3348c3795",
"label": "Copyright Text",
"name": "copyright_text",
"aria-label": "",
"type": "text",
"instructions": "Use '%Y' to display the current year.",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "&copy; %Y. All rights reserved.",
"maxlength": "",
"allow_in_bindings": 1,
"placeholder": "",
"prepend": "",
"append": ""
},
{
"key": "field_684af923d5f68",
"label": "Credit Text",
"name": "credit_text",
"aria-label": "",
"type": "text",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "Web Design & Development by <a href=\"https:\/\/vincentdesign.ca\/\"><strong>Vincent Design<\/strong><\/a>",
"maxlength": "",
"allow_in_bindings": 1,
"placeholder": "",
"prepend": "",
"append": ""
}
]
},
{
"key": "field_60cbbba3c100b",
"label": "Admin",
"name": "admin",
"aria-label": "",
"type": "group",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"layout": "row",
"sub_fields": [
{
"key": "field_60cbbbb2c100c",
"label": "404 Page",
"name": "404_page",
"aria-label": "",
"type": "post_object",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"post_type": [
"page"
],
"taxonomy": "",
"allow_null": 0,
"multiple": 0,
"return_format": "object",
"ui": 1,
"bidirectional_target": []
}
]
}
],
"location": [
[
{
"param": "options_page",
"operator": "==",
"value": "global-fields"
}
]
],
"menu_order": 0,
"position": "normal",
"style": "default",
"label_placement": "top",
"instruction_placement": "label",
"hide_on_screen": "",
"active": true,
"description": "",
"show_in_rest": 0,
"modified": 1749744328
}
+362
View File
@@ -0,0 +1,362 @@
{
"key": "group_600f5a9e242c3",
"title": "Grid",
"fields": [
{
"key": "field_6010666abf912",
"label": "Grid Settings",
"name": "",
"aria-label": "",
"type": "accordion",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"open": 1,
"multi_expand": 1,
"endpoint": 0
},
{
"key": "field_600f6ea6251d4",
"label": "Columns",
"name": "columns",
"aria-label": "",
"type": "range",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"min": 1,
"max": 12,
"step": "",
"prepend": "",
"append": ""
},
{
"key": "field_600f6ee1251d6",
"label": "Columns (sm)",
"name": "columns_sm",
"aria-label": "",
"type": "range",
"instructions": "",
"required": 0,
"conditional_logic": [
[
{
"field": "field_60106fa83e9ae",
"operator": "==",
"value": "sm"
}
]
],
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"min": 1,
"max": 12,
"step": "",
"prepend": "",
"append": ""
},
{
"key": "field_600f6f41251d7",
"label": "Columns (md)",
"name": "columns_md",
"aria-label": "",
"type": "range",
"instructions": "",
"required": 0,
"conditional_logic": [
[
{
"field": "field_60106fa83e9ae",
"operator": "==",
"value": "md"
}
]
],
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"min": 1,
"max": 12,
"step": "",
"prepend": "",
"append": ""
},
{
"key": "field_600f6f4d251d8",
"label": "Columns (lg)",
"name": "columns_lg",
"aria-label": "",
"type": "range",
"instructions": "",
"required": 0,
"conditional_logic": [
[
{
"field": "field_60106fa83e9ae",
"operator": "==",
"value": "lg"
}
]
],
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"min": 1,
"max": 12,
"step": "",
"prepend": "",
"append": ""
},
{
"key": "field_600f6f57251d9",
"label": "Columns (xl)",
"name": "columns_xl",
"aria-label": "",
"type": "range",
"instructions": "",
"required": 0,
"conditional_logic": [
[
{
"field": "field_60106fa83e9ae",
"operator": "==",
"value": "xl"
}
]
],
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"min": 1,
"max": 12,
"step": "",
"prepend": "",
"append": ""
},
{
"key": "field_600f6f66251da",
"label": "Columns (2xl)",
"name": "columns_2xl",
"aria-label": "",
"type": "range",
"instructions": "",
"required": 0,
"conditional_logic": [
[
{
"field": "field_60106fa83e9ae",
"operator": "==",
"value": "2xl"
}
]
],
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"min": 1,
"max": 12,
"step": "",
"prepend": "",
"append": ""
},
{
"key": "field_60106fa83e9ae",
"label": "Breakpoints",
"name": "columns_breakpoints",
"aria-label": "",
"type": "checkbox",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"choices": {
"sm": "sm (640px)",
"md": "md (768px)",
"lg": "lg (1024px)",
"xl": "xl (1280px)",
"2xl": "2xl (1536px)"
},
"allow_custom": 0,
"default_value": [],
"layout": "vertical",
"toggle": 0,
"return_format": "value",
"save_custom": 0,
"custom_choice_button_text": "Add new choice"
},
{
"key": "field_600f5d3ec8e30",
"label": "Gap X",
"name": "gap_x",
"aria-label": "",
"type": "select",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"choices": {
"0": "0",
"1": "1",
"2": "2",
"3": "3",
"4": "4",
"5": "5",
"6": "6",
"7": "7",
"8": "8",
"9": "9",
"10": "10",
"11": "11",
"12": "12",
"14": "14",
"16": "16",
"18": "18",
"20": "20",
"24": "24",
"28": "28",
"32": "32",
"36": "36",
"40": "40",
"48": "48",
"56": "56",
"64": "64",
"72": "72",
"80": "80",
"88": "88",
"96": "96"
},
"default_value": 0,
"allow_null": 0,
"multiple": 0,
"ui": 0,
"return_format": "value",
"ajax": 0,
"placeholder": ""
},
{
"key": "field_60dca39d232a1",
"label": "Gap Y",
"name": "gap_y",
"aria-label": "",
"type": "select",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"choices": {
"0": "0",
"1": "1",
"2": "2",
"3": "3",
"4": "4",
"5": "5",
"6": "6",
"7": "7",
"8": "8",
"9": "9",
"10": "10",
"11": "11",
"12": "12",
"14": "14",
"16": "16",
"18": "18",
"20": "20",
"24": "24",
"28": "28",
"32": "32",
"36": "36",
"40": "40",
"48": "48",
"56": "56",
"64": "64",
"72": "72",
"80": "80",
"88": "88",
"96": "96"
},
"default_value": 0,
"allow_null": 0,
"multiple": 0,
"ui": 0,
"return_format": "value",
"ajax": 0,
"placeholder": ""
},
{
"key": "field_60106e64bf91c",
"label": "Grid Settings End",
"name": "",
"aria-label": "",
"type": "accordion",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"open": 0,
"multi_expand": 0,
"endpoint": 1
}
],
"location": [
[
{
"param": "block",
"operator": "==",
"value": "acf\/grid"
}
]
],
"menu_order": 0,
"position": "normal",
"style": "default",
"label_placement": "top",
"instruction_placement": "label",
"hide_on_screen": "",
"active": true,
"description": "",
"show_in_rest": 0,
"modified": 1744581665
}
+256
View File
@@ -0,0 +1,256 @@
{
"key": "group_60106ed700da3",
"title": "Grid Cell",
"fields": [
{
"key": "field_60106ed7117ef",
"label": "Col Span",
"name": "",
"aria-label": "",
"type": "accordion",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"open": 1,
"multi_expand": 1,
"endpoint": 0
},
{
"key": "field_60106ed7117f7",
"label": "Default",
"name": "col_span",
"aria-label": "",
"type": "range",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "hide-top-label",
"id": ""
},
"default_value": "",
"min": 1,
"max": 12,
"step": "",
"prepend": "",
"append": ""
},
{
"key": "field_60106ed711802",
"label": "(sm)",
"name": "col_span_sm",
"aria-label": "",
"type": "range",
"instructions": "",
"required": 0,
"conditional_logic": [
[
{
"field": "field_60106ed711830",
"operator": "==",
"value": "sm"
}
]
],
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"min": 1,
"max": 12,
"step": "",
"prepend": "",
"append": ""
},
{
"key": "field_60106ed71180a",
"label": "(md)",
"name": "col_span_md",
"aria-label": "",
"type": "range",
"instructions": "",
"required": 0,
"conditional_logic": [
[
{
"field": "field_60106ed711830",
"operator": "==",
"value": "md"
}
]
],
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"min": 1,
"max": 12,
"step": "",
"prepend": "",
"append": ""
},
{
"key": "field_60106ed711811",
"label": "(lg)",
"name": "col_span_lg",
"aria-label": "",
"type": "range",
"instructions": "",
"required": 0,
"conditional_logic": [
[
{
"field": "field_60106ed711830",
"operator": "==",
"value": "lg"
}
]
],
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"min": 1,
"max": 12,
"step": "",
"prepend": "",
"append": ""
},
{
"key": "field_60106ed71181d",
"label": "(xl)",
"name": "col_span_xl",
"aria-label": "",
"type": "range",
"instructions": "",
"required": 0,
"conditional_logic": [
[
{
"field": "field_60106ed711830",
"operator": "==",
"value": "xl"
}
]
],
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"min": 1,
"max": 12,
"step": "",
"prepend": "",
"append": ""
},
{
"key": "field_60106ed711825",
"label": "(2xl)",
"name": "col_span_2xl",
"aria-label": "",
"type": "range",
"instructions": "",
"required": 0,
"conditional_logic": [
[
{
"field": "field_60106ed711830",
"operator": "==",
"value": "2xl"
}
]
],
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"min": 1,
"max": 12,
"step": "",
"prepend": "",
"append": ""
},
{
"key": "field_60106ed711830",
"label": "Breakpoints",
"name": "col_span_breakpoints",
"aria-label": "",
"type": "checkbox",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"choices": {
"sm": "sm (640px)",
"md": "md (768px)",
"lg": "lg (1024px)",
"xl": "xl (1280px)",
"2xl": "2xl (1536px)"
},
"allow_custom": 0,
"default_value": [],
"layout": "vertical",
"toggle": 0,
"return_format": "value",
"save_custom": 0,
"custom_choice_button_text": "Add new choice"
},
{
"key": "field_60106ed711838",
"label": "Col Span End",
"name": "",
"aria-label": "",
"type": "accordion",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"open": 0,
"multi_expand": 0,
"endpoint": 1
}
],
"location": [
[
{
"param": "block",
"operator": "==",
"value": "acf\/grid-cell"
}
]
],
"menu_order": 0,
"position": "normal",
"style": "default",
"label_placement": "top",
"instruction_placement": "label",
"hide_on_screen": "",
"active": true,
"description": "",
"show_in_rest": 0,
"modified": 1697125043
}
+192
View File
@@ -0,0 +1,192 @@
{
"key": "group_60bfb84ae973c",
"title": "Page Heading",
"fields": [
{
"key": "field_60bfda53dc0f2",
"label": "Hero Style",
"name": "hero_style",
"aria-label": "",
"type": "select",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"choices": {
"default": "Default",
"none": "None"
},
"default_value": false,
"allow_null": 0,
"multiple": 0,
"ui": 0,
"return_format": "value",
"ajax": 0,
"placeholder": ""
},
{
"key": "field_6478f669004aa",
"label": "Background Color",
"name": "background_color",
"aria-label": "",
"type": "color_picker",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"enable_opacity": 0,
"return_format": "string"
},
{
"key": "field_6478f68c004ab",
"label": "Is Dark?",
"name": "is_dark",
"aria-label": "",
"type": "true_false",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"message": "",
"default_value": 1,
"ui": 0,
"ui_on_text": "",
"ui_off_text": ""
},
{
"key": "field_60bfb8534a41f",
"label": "Heading",
"name": "heading",
"aria-label": "",
"type": "text",
"instructions": "",
"required": 0,
"conditional_logic": [
[
{
"field": "field_60bfda53dc0f2",
"operator": "!=",
"value": "none"
}
]
],
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"placeholder": "",
"prepend": "",
"append": "",
"maxlength": ""
},
{
"key": "field_60bfb85a4a420",
"label": "Intro",
"name": "intro",
"aria-label": "",
"type": "textarea",
"instructions": "",
"required": 0,
"conditional_logic": [
[
{
"field": "field_60bfda53dc0f2",
"operator": "!=",
"value": "none"
}
]
],
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"placeholder": "",
"maxlength": "",
"rows": "",
"new_lines": ""
},
{
"key": "field_60bfb8614a421",
"label": "Call to Actions",
"name": "call_to_actions",
"aria-label": "",
"type": "repeater",
"instructions": "",
"required": 0,
"conditional_logic": false,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"collapsed": "",
"min": 0,
"max": 0,
"layout": "table",
"button_label": "Add Call to Action",
"rows_per_page": 20,
"sub_fields": [
{
"key": "field_60bfb86e4a422",
"label": "Link",
"name": "link",
"aria-label": "",
"type": "link",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"return_format": "array",
"parent_repeater": "field_60bfb8614a421"
}
]
}
],
"location": [
[
{
"param": "post_type",
"operator": "==",
"value": "page"
}
],
[
{
"param": "post_type",
"operator": "==",
"value": "post"
}
]
],
"menu_order": 1,
"position": "side",
"style": "default",
"label_placement": "top",
"instruction_placement": "label",
"hide_on_screen": "",
"active": true,
"description": "",
"show_in_rest": 0,
"modified": 1746895738
}
+106
View File
@@ -0,0 +1,106 @@
{
"key": "group_60bfdb328901d",
"title": "Homepage Hero",
"fields": [
{
"key": "field_60bfdb328cfae",
"label": "Heading",
"name": "heading",
"aria-label": "",
"type": "text",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"placeholder": "",
"prepend": "",
"append": "",
"maxlength": ""
},
{
"key": "field_60bfdb328cfb9",
"label": "Intro",
"name": "intro",
"aria-label": "",
"type": "wysiwyg",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"tabs": "all",
"toolbar": "full",
"media_upload": 1,
"delay": 0
},
{
"key": "field_60bfdb328cfc0",
"label": "Calls to Action",
"name": "calls_to_action",
"aria-label": "",
"type": "repeater",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"layout": "table",
"pagination": 0,
"min": 0,
"max": 0,
"collapsed": "",
"button_label": "Add Call to Action",
"rows_per_page": 20,
"sub_fields": [
{
"key": "field_60bfdb3294667",
"label": "Link",
"name": "link",
"aria-label": "",
"type": "link",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"return_format": "array",
"parent_repeater": "field_60bfdb328cfc0"
}
]
}
],
"location": [
[
{
"param": "block",
"operator": "==",
"value": "acf\/homepage-hero"
}
]
],
"menu_order": 0,
"position": "normal",
"style": "default",
"label_placement": "top",
"instruction_placement": "label",
"hide_on_screen": "",
"active": true,
"description": "",
"show_in_rest": 0,
"modified": 1742332828
}
+556
View File
@@ -0,0 +1,556 @@
{
"key": "group_6261bc658dd80",
"title": "Section",
"fields": [
{
"key": "field_6262e24ce0672",
"label": "Background",
"name": "",
"type": "accordion",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"open": 0,
"multi_expand": 1,
"endpoint": 0
},
{
"key": "field_6262df5194500",
"label": "Background Color",
"name": "background_color",
"type": "color_picker",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"enable_opacity": 0,
"return_format": "string"
},
{
"key": "field_6261bc723d308",
"label": "Background Image",
"name": "background_image",
"type": "image",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"return_format": "array",
"preview_size": "medium",
"library": "all",
"min_width": "",
"min_height": "",
"min_size": "",
"max_width": "",
"max_height": "",
"max_size": "",
"mime_types": ""
},
{
"key": "field_62a9e85035329",
"label": "Background Position",
"name": "background_position",
"type": "group",
"instructions": "",
"required": 0,
"conditional_logic": [
[
{
"field": "field_6261bc723d308",
"operator": "!=empty"
}
]
],
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"layout": "block",
"sub_fields": [
{
"key": "field_62a9e87c3532a",
"label": "Size",
"name": "size",
"type": "select",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"choices": {
"cover": "Cover",
"contain": "Contain",
"custom": "Custom"
},
"default_value": false,
"allow_null": 0,
"multiple": 0,
"ui": 0,
"return_format": "value",
"ajax": 0,
"placeholder": ""
},
{
"key": "field_62a9e8ef3532b",
"label": "Scale",
"name": "scale",
"type": "range",
"instructions": "",
"required": 0,
"conditional_logic": [
[
{
"field": "field_62a9e87c3532a",
"operator": "==",
"value": "custom"
}
]
],
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": 100,
"min": "",
"max": 200,
"step": "",
"prepend": "",
"append": "%"
},
{
"key": "field_62a9e92e3532c",
"label": "X",
"name": "x",
"type": "range",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": 50,
"min": "",
"max": "",
"step": "",
"prepend": "",
"append": "%"
},
{
"key": "field_62a9e98c3532d",
"label": "Y",
"name": "y",
"type": "range",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": 50,
"min": "",
"max": "",
"step": "",
"prepend": "",
"append": "%"
}
]
},
{
"key": "field_6262df6394501",
"label": "Background End",
"name": "",
"type": "accordion",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"open": 0,
"multi_expand": 0,
"endpoint": 1
},
{
"key": "field_6262df8794502",
"label": "Overlay",
"name": "",
"type": "accordion",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"open": 0,
"multi_expand": 1,
"endpoint": 0
},
{
"key": "field_6261bfcb2d09c",
"label": "Overlay Color",
"name": "overlay_color",
"type": "color_picker",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"enable_opacity": 0,
"return_format": "string"
},
{
"key": "field_6262df9994503",
"label": "Overlay Image",
"name": "overlay_image",
"type": "image",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"return_format": "array",
"preview_size": "medium",
"library": "all",
"min_width": "",
"min_height": "",
"min_size": "",
"max_width": "",
"max_height": "",
"max_size": "",
"mime_types": ""
},
{
"key": "field_6261c02e08b7f",
"label": "Overlay Opacity",
"name": "overlay_opacity",
"type": "range",
"instructions": "",
"required": 0,
"conditional_logic": [
[
{
"field": "field_6261bfcb2d09c",
"operator": "!=empty"
}
],
[
{
"field": "field_6262df9994503",
"operator": "!=empty"
}
]
],
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"min": "",
"max": "",
"step": "",
"prepend": "",
"append": "%"
},
{
"key": "field_62a9e9ab3532e",
"label": "Overlay Position",
"name": "overlay_position",
"type": "group",
"instructions": "",
"required": 0,
"conditional_logic": [
[
{
"field": "field_6262df9994503",
"operator": "!=empty"
}
]
],
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"layout": "block",
"sub_fields": [
{
"key": "field_62a9e9ab3532f",
"label": "Size",
"name": "size",
"type": "select",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"choices": {
"cover": "Cover",
"contain": "Contain",
"custom": "Custom"
},
"default_value": false,
"allow_null": 0,
"multiple": 0,
"ui": 0,
"return_format": "value",
"ajax": 0,
"placeholder": ""
},
{
"key": "field_62a9e9ab35330",
"label": "Scale",
"name": "scale",
"type": "range",
"instructions": "",
"required": 0,
"conditional_logic": [
[
{
"field": "field_62a9e9ab3532f",
"operator": "==",
"value": "custom"
}
]
],
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": 100,
"min": "",
"max": 200,
"step": "",
"prepend": "",
"append": ""
},
{
"key": "field_62a9e9ab35331",
"label": "X",
"name": "x",
"type": "range",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": 50,
"min": "",
"max": "",
"step": "",
"prepend": "",
"append": "%"
},
{
"key": "field_62a9e9ab35332",
"label": "Y",
"name": "y",
"type": "range",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": 50,
"min": "",
"max": "",
"step": "",
"prepend": "",
"append": "%"
}
]
},
{
"key": "field_6262e26be0673",
"label": "Overlay End",
"name": "",
"type": "accordion",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"open": 0,
"multi_expand": 0,
"endpoint": 1
},
{
"key": "field_6262dfbb94504",
"label": "Theme",
"name": "",
"type": "accordion",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"open": 0,
"multi_expand": 1,
"endpoint": 0
},
{
"key": "field_6261c14931371",
"label": "Is Dark",
"name": "is_dark",
"type": "true_false",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"message": "",
"default_value": 0,
"ui": 0,
"ui_on_text": "",
"ui_off_text": ""
},
{
"key": "field_6262e2b1e0674",
"label": "Theme End",
"name": "",
"type": "accordion",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"open": 0,
"multi_expand": 0,
"endpoint": 1
},
{
"key": "field_6262eadf25dae",
"label": "Content",
"name": "",
"type": "accordion",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"open": 0,
"multi_expand": 1,
"endpoint": 0
},
{
"key": "field_6262eb0025db0",
"label": "Content Width",
"name": "content_width",
"type": "select",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"choices": {
"default": "Default",
"full": "Full Width"
},
"default_value": "default",
"allow_null": 0,
"multiple": 0,
"ui": 0,
"return_format": "value",
"ajax": 0,
"placeholder": ""
},
{
"key": "field_6262eaeb25daf",
"label": "Content End",
"name": "",
"type": "accordion",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"open": 0,
"multi_expand": 0,
"endpoint": 1
}
],
"location": [
[
{
"param": "block",
"operator": "==",
"value": "acf\/section"
}
]
],
"menu_order": 0,
"position": "normal",
"style": "default",
"label_placement": "top",
"instruction_placement": "label",
"hide_on_screen": "",
"active": true,
"description": "",
"show_in_rest": 0,
"modified": 1655311426
}
+132
View File
@@ -0,0 +1,132 @@
{
"key": "group_645e51f721207",
"title": "Accordion Block",
"fields": [
{
"key": "field_64639b159080f",
"label": "First Item Open?",
"name": "open",
"aria-label": "",
"type": "true_false",
"instructions": "Should the first accordion item be open on page load?",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"message": "",
"default_value": 0,
"ui": 0,
"ui_on_text": "",
"ui_off_text": ""
},
{
"key": "field_67fabc2185ea0",
"label": "Item Group Name",
"name": "group_items",
"aria-label": "",
"type": "text",
"instructions": "Enables single expansion mode closes previously open items when opening a new one.",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"maxlength": "",
"allow_in_bindings": 0,
"placeholder": "",
"prepend": "",
"append": ""
},
{
"key": "field_645e52e3e0ac2",
"label": "Accordion Items",
"name": "accordion_items",
"aria-label": "",
"type": "repeater",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"layout": "row",
"pagination": 0,
"min": 0,
"max": 0,
"collapsed": "",
"button_label": "Add Row",
"rows_per_page": 20,
"sub_fields": [
{
"key": "field_645e51f7eadcb",
"label": "Title",
"name": "title",
"aria-label": "",
"type": "text",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"maxlength": "",
"placeholder": "",
"prepend": "",
"append": "",
"parent_repeater": "field_645e52e3e0ac2"
},
{
"key": "field_645e523feadcc",
"label": "Content",
"name": "content",
"aria-label": "",
"type": "wysiwyg",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"tabs": "all",
"toolbar": "full",
"media_upload": 1,
"delay": 0,
"parent_repeater": "field_645e52e3e0ac2"
}
]
}
],
"location": [
[
{
"param": "block",
"operator": "==",
"value": "acf\/accordion"
}
]
],
"menu_order": 0,
"position": "normal",
"style": "default",
"label_placement": "top",
"instruction_placement": "label",
"hide_on_screen": "",
"active": true,
"description": "",
"show_in_rest": 0,
"modified": 1744493675
}
+389
View File
@@ -0,0 +1,389 @@
{
"key": "group_645e7cf448e66",
"title": "Media With Text Block",
"fields": [
{
"key": "field_645e7cf4c6700",
"label": "Background Color",
"name": "background_color",
"aria-label": "",
"type": "color_picker",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"enable_opacity": 0,
"return_format": "string"
},
{
"key": "field_645e7d22c6701",
"label": "Is Dark",
"name": "is_dark",
"aria-label": "",
"type": "true_false",
"instructions": "If the background is dark, check this so text will remain legible",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"message": "",
"default_value": 0,
"ui": 0,
"ui_on_text": "",
"ui_off_text": ""
},
{
"key": "field_645e7df0c6702",
"label": "Media Type",
"name": "media_type",
"aria-label": "",
"type": "select",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"choices": {
"image": "Image",
"video": "Video"
},
"default_value": false,
"return_format": "value",
"multiple": 0,
"allow_null": 0,
"ui": 0,
"ajax": 0,
"placeholder": ""
},
{
"key": "field_645e7e2cc6703",
"label": "Image",
"name": "image",
"aria-label": "",
"type": "image",
"instructions": "",
"required": 0,
"conditional_logic": [
[
{
"field": "field_645e7df0c6702",
"operator": "==",
"value": "image"
}
]
],
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"return_format": "array",
"library": "all",
"min_width": "",
"min_height": "",
"min_size": "",
"max_width": "",
"max_height": "",
"max_size": "",
"mime_types": "",
"preview_size": "medium"
},
{
"key": "field_645e7e60c6704",
"label": "Video",
"name": "video",
"aria-label": "",
"type": "oembed",
"instructions": "",
"required": 0,
"conditional_logic": [
[
{
"field": "field_645e7df0c6702",
"operator": "==",
"value": "video"
}
]
],
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"width": "",
"height": ""
},
{
"key": "field_645e7f04c6705",
"label": "Media on Left",
"name": "media_on_left",
"aria-label": "",
"type": "true_false",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"message": "",
"default_value": 0,
"ui": 0,
"ui_on_text": "",
"ui_off_text": ""
},
{
"key": "field_645e7f26c6706",
"label": "Heading",
"name": "heading",
"aria-label": "",
"type": "text",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"maxlength": "",
"placeholder": "",
"prepend": "",
"append": ""
},
{
"key": "field_645e7f3dc6707",
"label": "Subheading",
"name": "subheading",
"aria-label": "",
"type": "text",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"maxlength": "",
"placeholder": "",
"prepend": "",
"append": ""
},
{
"key": "field_645e7f49c6708",
"label": "Content",
"name": "content",
"aria-label": "",
"type": "wysiwyg",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"tabs": "all",
"toolbar": "full",
"media_upload": 1,
"delay": 0
},
{
"key": "field_645e7f85c6709",
"label": "Calls To Action",
"name": "calls_to_action",
"aria-label": "",
"type": "repeater",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"layout": "block",
"pagination": 0,
"min": 0,
"max": 0,
"collapsed": "",
"button_label": "Add Row",
"rows_per_page": 20,
"sub_fields": [
{
"key": "field_645e7f93c670a",
"label": "Link",
"name": "link",
"aria-label": "",
"type": "link",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"return_format": "array",
"parent_repeater": "field_645e7f85c6709"
},
{
"key": "field_646500c05f6da",
"label": "Color",
"name": "color",
"aria-label": "",
"type": "select",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"choices": {
"primary": "Primary",
"secondary": "Secondary",
"white": "White",
"gray": "Gray"
},
"default_value": "primary",
"return_format": "value",
"multiple": 0,
"allow_null": 0,
"ui": 0,
"ajax": 0,
"placeholder": "",
"parent_repeater": "field_645e7f85c6709"
},
{
"key": "field_646501305f6db",
"label": "Variant",
"name": "variant",
"aria-label": "",
"type": "select",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"choices": {
"default": "Default",
"outline": "Outline"
},
"default_value": "default",
"return_format": "value",
"multiple": 0,
"allow_null": 0,
"ui": 0,
"ajax": 0,
"placeholder": "",
"parent_repeater": "field_645e7f85c6709"
},
{
"key": "field_646501565f6dc",
"label": "Size",
"name": "size",
"aria-label": "",
"type": "select",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"choices": {
"small": "Small",
"medium": "Medium",
"large": "Large"
},
"default_value": "medium",
"return_format": "value",
"multiple": 0,
"allow_null": 0,
"ui": 0,
"ajax": 0,
"placeholder": "",
"parent_repeater": "field_645e7f85c6709"
},
{
"key": "field_646501735f6dd",
"label": "Width",
"name": "width",
"aria-label": "",
"type": "select",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"choices": {
"auto": "Auto",
"small": "Small",
"wide": "Wide",
"full": "Full Width"
},
"default_value": "auto",
"return_format": "value",
"multiple": 0,
"allow_null": 0,
"allow_in_bindings": 1,
"ui": 0,
"ajax": 0,
"placeholder": "",
"parent_repeater": "field_645e7f85c6709"
}
]
}
],
"location": [
[
{
"param": "block",
"operator": "==",
"value": "acf\/media-text"
}
],
[
{
"param": "block",
"operator": "==",
"value": "acf\/media-text-innerblocks"
}
]
],
"menu_order": 0,
"position": "normal",
"style": "default",
"label_placement": "top",
"instruction_placement": "label",
"hide_on_screen": "",
"active": true,
"description": "",
"show_in_rest": 0,
"modified": 1742680440
}
+24
View File
@@ -0,0 +1,24 @@
require("dotenv").config();
const fs = require("fs");
const path = require("path");
const distDir = path.resolve(`static/dist`);
const { tailwindToCSS } = require(`./.utils`);
const start = performance.now();
// Tailwind to CSS
try {
// Ensure the output directory exists
if (!fs.existsSync(distDir)) {
fs.mkdirSync(distDir, { recursive: true });
}
// Compile Tailwind
tailwindToCSS();
const end = performance.now();
const runtime = (end - start).toFixed(3);
console.log(`Production build successfully run in ${runtime}ms`);
} catch (error) {
console.error("Error processing CSS files:", error);
}
+25
View File
@@ -0,0 +1,25 @@
/**
* Utility functions shared between builds
*/
const fs = require("fs");
const path = require("path");
const distDir = path.resolve(`static/dist`);
const debounce = require("lodash.debounce");
require("dotenv").config();
// Theme root as working directory
const cwd = path.resolve("..");
// Compile Tailwind
module.exports.tailwindToCSS = debounce(() => {
try {
require("child_process").execSync(
`npx @tailwindcss/cli -i styles/theme.css -o static/dist/theme.css`,
{ stdio: "inherit" }
);
} catch (error) {
console.log(`Error compiling Tailwind to CSS:`, error);
}
}, 50);
+48
View File
@@ -0,0 +1,48 @@
require("dotenv").config();
const fs = require("fs");
const path = require("path");
const browserSync = require("browser-sync").create();
const { tailwindToCSS } = require(`./.utils`);
// Theme root as working directory
const cwd = path.resolve("../");
// Initialize BrowserSync
browserSync.init({
proxy: process.env.LOCALHOST_URL,
port: process.env.BROWSERSYNC_PORT,
cors: true,
cwd: cwd,
logLevel: `warn`,
minify: false,
open: false,
ui: false,
ghostMode: false,
reloadOnRestart: true,
notify: false,
watch: true,
});
// Reload JS during development
browserSync.watch(`static/js/*.js`, (event, file) => {
if (event !== "change") return;
const start = performance.now();
try {
browserSync.stream();
const end = performance.now();
const runtime = (end - start).toFixed(3);
console.log(`JS re-injected successfully in ${runtime}ms`);
} catch (error) {
console.log("Error compiling JS:", error);
}
});
// Compile tailwind to css and reload on changes
browserSync.watch(["**/*.css", "**/*.php"], (event, file) => {
if (event !== "change") return;
tailwindToCSS();
browserSync.reload();
});
+32
View File
@@ -0,0 +1,32 @@
{
"name": "vincentdesigninc/basic-wp",
"description": "Minimal custom WordPress theme with modern templating, a lightning fast build process, and no tom foolery.",
"type": "project",
"license": "MIT",
"authors": [
{
"name": "Keith Solomon",
"email": "keith@vincentdesign.ca"
}
],
"require-dev": {
"squizlabs/php_codesniffer": "^3.12",
"wp-coding-standards/wpcs": "^3.1"
},
"config": {
"allow-plugins": {
"dealerdirect/phpcodesniffer-composer-installer": true
}
},
"scripts": {
"lint": [
"phpcs --report-full=./phpcs-results.txt ."
],
"fix": [
"phpcbf --standard=.phpcs.xml ."
],
"phpcs-config": [
"phpcs --config-show"
]
}
}
Generated
+417
View File
@@ -0,0 +1,417 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "2c997c41672ef92cfa6b4d04f9c9097a",
"packages": [],
"packages-dev": [
{
"name": "dealerdirect/phpcodesniffer-composer-installer",
"version": "v1.0.0",
"source": {
"type": "git",
"url": "https://github.com/PHPCSStandards/composer-installer.git",
"reference": "4be43904336affa5c2f70744a348312336afd0da"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/4be43904336affa5c2f70744a348312336afd0da",
"reference": "4be43904336affa5c2f70744a348312336afd0da",
"shasum": ""
},
"require": {
"composer-plugin-api": "^1.0 || ^2.0",
"php": ">=5.4",
"squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0"
},
"require-dev": {
"composer/composer": "*",
"ext-json": "*",
"ext-zip": "*",
"php-parallel-lint/php-parallel-lint": "^1.3.1",
"phpcompatibility/php-compatibility": "^9.0",
"yoast/phpunit-polyfills": "^1.0"
},
"type": "composer-plugin",
"extra": {
"class": "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin"
},
"autoload": {
"psr-4": {
"PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Franck Nijhof",
"email": "franck.nijhof@dealerdirect.com",
"homepage": "http://www.frenck.nl",
"role": "Developer / IT Manager"
},
{
"name": "Contributors",
"homepage": "https://github.com/PHPCSStandards/composer-installer/graphs/contributors"
}
],
"description": "PHP_CodeSniffer Standards Composer Installer Plugin",
"homepage": "http://www.dealerdirect.com",
"keywords": [
"PHPCodeSniffer",
"PHP_CodeSniffer",
"code quality",
"codesniffer",
"composer",
"installer",
"phpcbf",
"phpcs",
"plugin",
"qa",
"quality",
"standard",
"standards",
"style guide",
"stylecheck",
"tests"
],
"support": {
"issues": "https://github.com/PHPCSStandards/composer-installer/issues",
"source": "https://github.com/PHPCSStandards/composer-installer"
},
"time": "2023-01-05T11:28:13+00:00"
},
{
"name": "phpcsstandards/phpcsextra",
"version": "1.3.0",
"source": {
"type": "git",
"url": "https://github.com/PHPCSStandards/PHPCSExtra.git",
"reference": "46d08eb86eec622b96c466adec3063adfed280dd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPCSStandards/PHPCSExtra/zipball/46d08eb86eec622b96c466adec3063adfed280dd",
"reference": "46d08eb86eec622b96c466adec3063adfed280dd",
"shasum": ""
},
"require": {
"php": ">=5.4",
"phpcsstandards/phpcsutils": "^1.0.9",
"squizlabs/php_codesniffer": "^3.12.1"
},
"require-dev": {
"php-parallel-lint/php-console-highlighter": "^1.0",
"php-parallel-lint/php-parallel-lint": "^1.3.2",
"phpcsstandards/phpcsdevcs": "^1.1.6",
"phpcsstandards/phpcsdevtools": "^1.2.1",
"phpunit/phpunit": "^4.5 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0"
},
"type": "phpcodesniffer-standard",
"extra": {
"branch-alias": {
"dev-stable": "1.x-dev",
"dev-develop": "1.x-dev"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-3.0-or-later"
],
"authors": [
{
"name": "Juliette Reinders Folmer",
"homepage": "https://github.com/jrfnl",
"role": "lead"
},
{
"name": "Contributors",
"homepage": "https://github.com/PHPCSStandards/PHPCSExtra/graphs/contributors"
}
],
"description": "A collection of sniffs and standards for use with PHP_CodeSniffer.",
"keywords": [
"PHP_CodeSniffer",
"phpcbf",
"phpcodesniffer-standard",
"phpcs",
"standards",
"static analysis"
],
"support": {
"issues": "https://github.com/PHPCSStandards/PHPCSExtra/issues",
"security": "https://github.com/PHPCSStandards/PHPCSExtra/security/policy",
"source": "https://github.com/PHPCSStandards/PHPCSExtra"
},
"funding": [
{
"url": "https://github.com/PHPCSStandards",
"type": "github"
},
{
"url": "https://github.com/jrfnl",
"type": "github"
},
{
"url": "https://opencollective.com/php_codesniffer",
"type": "open_collective"
},
{
"url": "https://thanks.dev/u/gh/phpcsstandards",
"type": "thanks_dev"
}
],
"time": "2025-04-20T23:35:32+00:00"
},
{
"name": "phpcsstandards/phpcsutils",
"version": "1.0.12",
"source": {
"type": "git",
"url": "https://github.com/PHPCSStandards/PHPCSUtils.git",
"reference": "87b233b00daf83fb70f40c9a28692be017ea7c6c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPCSStandards/PHPCSUtils/zipball/87b233b00daf83fb70f40c9a28692be017ea7c6c",
"reference": "87b233b00daf83fb70f40c9a28692be017ea7c6c",
"shasum": ""
},
"require": {
"dealerdirect/phpcodesniffer-composer-installer": "^0.4.1 || ^0.5 || ^0.6.2 || ^0.7 || ^1.0",
"php": ">=5.4",
"squizlabs/php_codesniffer": "^3.10.0 || 4.0.x-dev@dev"
},
"require-dev": {
"ext-filter": "*",
"php-parallel-lint/php-console-highlighter": "^1.0",
"php-parallel-lint/php-parallel-lint": "^1.3.2",
"phpcsstandards/phpcsdevcs": "^1.1.6",
"yoast/phpunit-polyfills": "^1.1.0 || ^2.0.0"
},
"type": "phpcodesniffer-standard",
"extra": {
"branch-alias": {
"dev-stable": "1.x-dev",
"dev-develop": "1.x-dev"
}
},
"autoload": {
"classmap": [
"PHPCSUtils/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-3.0-or-later"
],
"authors": [
{
"name": "Juliette Reinders Folmer",
"homepage": "https://github.com/jrfnl",
"role": "lead"
},
{
"name": "Contributors",
"homepage": "https://github.com/PHPCSStandards/PHPCSUtils/graphs/contributors"
}
],
"description": "A suite of utility functions for use with PHP_CodeSniffer",
"homepage": "https://phpcsutils.com/",
"keywords": [
"PHP_CodeSniffer",
"phpcbf",
"phpcodesniffer-standard",
"phpcs",
"phpcs3",
"standards",
"static analysis",
"tokens",
"utility"
],
"support": {
"docs": "https://phpcsutils.com/",
"issues": "https://github.com/PHPCSStandards/PHPCSUtils/issues",
"security": "https://github.com/PHPCSStandards/PHPCSUtils/security/policy",
"source": "https://github.com/PHPCSStandards/PHPCSUtils"
},
"funding": [
{
"url": "https://github.com/PHPCSStandards",
"type": "github"
},
{
"url": "https://github.com/jrfnl",
"type": "github"
},
{
"url": "https://opencollective.com/php_codesniffer",
"type": "open_collective"
}
],
"time": "2024-05-20T13:34:27+00:00"
},
{
"name": "squizlabs/php_codesniffer",
"version": "3.12.2",
"source": {
"type": "git",
"url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git",
"reference": "6d4cf6032d4b718f168c90a96e36c7d0eaacb2aa"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/6d4cf6032d4b718f168c90a96e36c7d0eaacb2aa",
"reference": "6d4cf6032d4b718f168c90a96e36c7d0eaacb2aa",
"shasum": ""
},
"require": {
"ext-simplexml": "*",
"ext-tokenizer": "*",
"ext-xmlwriter": "*",
"php": ">=5.4.0"
},
"require-dev": {
"phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4"
},
"bin": [
"bin/phpcbf",
"bin/phpcs"
],
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.x-dev"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Greg Sherwood",
"role": "Former lead"
},
{
"name": "Juliette Reinders Folmer",
"role": "Current lead"
},
{
"name": "Contributors",
"homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors"
}
],
"description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.",
"homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer",
"keywords": [
"phpcs",
"standards",
"static analysis"
],
"support": {
"issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues",
"security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy",
"source": "https://github.com/PHPCSStandards/PHP_CodeSniffer",
"wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki"
},
"funding": [
{
"url": "https://github.com/PHPCSStandards",
"type": "github"
},
{
"url": "https://github.com/jrfnl",
"type": "github"
},
{
"url": "https://opencollective.com/php_codesniffer",
"type": "open_collective"
},
{
"url": "https://thanks.dev/u/gh/phpcsstandards",
"type": "thanks_dev"
}
],
"time": "2025-04-13T04:10:18+00:00"
},
{
"name": "wp-coding-standards/wpcs",
"version": "3.1.0",
"source": {
"type": "git",
"url": "https://github.com/WordPress/WordPress-Coding-Standards.git",
"reference": "9333efcbff231f10dfd9c56bb7b65818b4733ca7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/9333efcbff231f10dfd9c56bb7b65818b4733ca7",
"reference": "9333efcbff231f10dfd9c56bb7b65818b4733ca7",
"shasum": ""
},
"require": {
"ext-filter": "*",
"ext-libxml": "*",
"ext-tokenizer": "*",
"ext-xmlreader": "*",
"php": ">=5.4",
"phpcsstandards/phpcsextra": "^1.2.1",
"phpcsstandards/phpcsutils": "^1.0.10",
"squizlabs/php_codesniffer": "^3.9.0"
},
"require-dev": {
"php-parallel-lint/php-console-highlighter": "^1.0.0",
"php-parallel-lint/php-parallel-lint": "^1.3.2",
"phpcompatibility/php-compatibility": "^9.0",
"phpcsstandards/phpcsdevtools": "^1.2.0",
"phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0"
},
"suggest": {
"ext-iconv": "For improved results",
"ext-mbstring": "For improved results"
},
"type": "phpcodesniffer-standard",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Contributors",
"homepage": "https://github.com/WordPress/WordPress-Coding-Standards/graphs/contributors"
}
],
"description": "PHP_CodeSniffer rules (sniffs) to enforce WordPress coding conventions",
"keywords": [
"phpcs",
"standards",
"static analysis",
"wordpress"
],
"support": {
"issues": "https://github.com/WordPress/WordPress-Coding-Standards/issues",
"source": "https://github.com/WordPress/WordPress-Coding-Standards",
"wiki": "https://github.com/WordPress/WordPress-Coding-Standards/wiki"
},
"funding": [
{
"url": "https://opencollective.com/php_codesniffer",
"type": "custom"
}
],
"time": "2024-03-25T16:39:00+00:00"
}
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {},
"prefer-stable": false,
"prefer-lowest": false,
"platform": {},
"platform-dev": {},
"plugin-api-version": "2.6.0"
}
File diff suppressed because it is too large Load Diff
+111
View File
@@ -0,0 +1,111 @@
<?php
/**
* Theme footer template
*
* @package BasicWP
* @since 1.0.0
*/
namespace BasicWP;
$footerLogo = getFieldValue( 'footer.footer_logo.url' ) ? getFieldValue( 'footer.footer_logo.url' ) : '';
$footerDesc = getFieldValue( 'footer.footer_description' ) ? getFieldValue( 'footer.footer_description' ) : '';
$copyright_text = getFieldValue( 'footer.copyright_text' ) ? getFieldValue( 'footer.copyright_text' ) : 'Copyright &copy; %Y ' . get_bloginfo( 'name' );
$copyright = str_replace( '%Y', gmdate( 'Y' ), $copyright_text );
$locations = get_nav_menu_locations();
$footerNav = ! empty( $locations['footer_navigation'] )
? ( wp_get_nav_menu_items( (int) $locations['footer_navigation'] ) ? wp_get_nav_menu_items( (int) $locations['footer_navigation'] ) : array() )
: array();
?>
</main>
<footer role="contentinfo" class="site-footer bg-gray-800 text-white text-base">
<div class="pt-16 pb-12 text-base">
<div class="container mx-auto">
<div class="grid grid-cols-4 gap-6 md:grid-cols-8 lg:grid-cols-12">
<div class="col-span-4 md:col-span-8 lg:col-span-4 max-w-[40ch] prose-p:text-14px prose-p:mb-4 text-balance" aria-labelledby="footer-header">
<h2 id="footer-header" class="max-w-64 h-auto">
<a href="<?php echo esc_url( home_url( '/' ) ); ?>" class="site-footer__logo-link h-full w-full">
<?php if ( $footerLogo ) { ?>
<img src="<?php echo esc_url( $footerLogo ); ?>" alt="<?php echo esc_attr( get_bloginfo( 'name' ) ); ?>" class="site-footer__logo-image" />
<?php
} else {
echo esc_html( get_bloginfo( 'name' ) );
}
?>
</a>
</h2>
<div id="footer-description" aria-label="Footer description">
<?php echo wp_kses_post( $footerDesc ); ?>
</div>
<div class="social-links mt-8">
<?php
get_template_part(
'views/partials/social-media',
null,
array(
'circle' => false,
'classes' => 'social-icons p-0 mr-2 text-30px text-gray-300! hover:text-info!',
)
);
?>
</div>
</div>
<div id="footRight" class="col-span-4 md:col-span-8 grid md:grid-cols-4 gap-10 lg:justify-end">
<div aria-labelledby="footer-area-1" class="prose-p:text-balance">
<h3 class="mb-4 pb-2 border-b border-b-secondary font-bold text-white" id="footer-area-1">Footer Area 1</h3>
<?php dynamic_sidebar( 'footer-1' ); ?>
</div>
<div aria-labelledby="footer-area-2" class="prose-p:text-balance">
<h3 class="mb-4 pb-2 border-b border-b-secondary font-bold text-white" id="footer-area-2">Footer Area 2</h3>
<?php dynamic_sidebar( 'footer-2' ); ?>
</div>
<div aria-labelledby="footer-area-3" class="prose-p:text-balance">
<h3 class="mb-4 pb-2 border-b border-b-secondary font-bold text-white" id="footer-area-3">Footer Area 3</h3>
<?php dynamic_sidebar( 'footer-3' ); ?>
</div>
<div id="footer-nav" aria-labelledby="footer-navigation">
<h3 class="mb-4 pb-2 border-b border-b-secondary font-bold text-white" id="footer-navigation"><?php echo esc_html__( 'Navigation' ); ?></h3>
<?php if ( $footerNav ) : ?>
<nav class="site-footer__nav" aria-label="Footer navigation">
<ul class="site-footer__nav-list">
<?php foreach ( $footerNav as $item ) : ?>
<li class="site-footer__nav-item list-none text-left <?php echo esc_attr( implode( ' ', $item->classes ) ); ?>">
<a href="<?php echo esc_url( $item->url ); ?>" class="site-footer__nav-link">
<?php echo esc_html( $item->title ); ?>
</a>
</li>
<?php endforeach; ?>
</ul>
</nav>
<?php endif; ?>
</div>
</div>
</div>
</div>
</div>
<div class="copyright bg-primary text-white text-center py-3 text-14px">
<?php
if ( $copyright ) {
echo wp_kses_post( $copyright );
} else {
echo esc_html( '&copy; ' . gmdate( 'Y' ) . ' ' . get_bloginfo( 'name' ) . '.' );
}
?>
</div>
</footer>
<?php wp_footer(); ?>
</body>
</html>
+27
View File
@@ -0,0 +1,27 @@
<?php
/**
* Front Page Template
*
* @package BasicWP
*/
namespace BasicWP;
get_header();
?>
<div class="container my-section lg:my-0 mx-auto">
<div>
<?php
// Content block
if ( have_posts() ) {
while ( have_posts() ) {
the_post();
the_content();
}
}
?>
</div>
</div>
<?php get_footer(); ?>
+38
View File
@@ -0,0 +1,38 @@
<?php
/**
* Functions file for the BasicWP theme.
*
* This file initializes the theme by including necessary dependencies
* and loading additional function files.
*
* @package BasicWP
* @since 1.0.0
*/
namespace BasicWP;
// Load functions.
foreach ( glob( __DIR__ . '/lib/*.php' ) as $filename ) {
include_once $filename;
}
/** Registers custom ACF (Advanced Custom Fields) blocks for use in the WordPress theme.
*
* This function is intended to define and register custom Gutenberg blocks
* using ACF, allowing for dynamic and reusable content blocks within the theme.
*
* @return void
*/
function regACFBlocks() {
define( 'BLOCKS_DIR', get_stylesheet_directory() . '/views/blocks' );
if ( is_dir( BLOCKS_DIR ) ) {
foreach ( scandir( BLOCKS_DIR ) as $folder ) {
if ( ( '.' !== $folder && '..' !== $folder && 'boilerplate' !== $folder ) && is_dir( BLOCKS_DIR . '/' . $folder ) ) {
register_block_type( BLOCKS_DIR . '/' . $folder );
}
}
}
}
add_action( 'init', __NAMESPACE__ . '\\regACFBlocks', 5 );
+62
View File
@@ -0,0 +1,62 @@
<?php
/**
* Theme header template
*
* @package BasicWP
* @since 1.0.0
*/
namespace BasicWP;
global $views;
$headerLogo = getFieldValue( 'header.header_logo.url' ) ? getFieldValue( 'header.header_logo.url' ) : get_theme_file_uri( '/static/img/logo.svg' );
// Check conditions for displaying the hero section
$showHero = in_array(
true,
array(
get_field( 'hero_style' ) === 'default',
is_home(),
is_archive(),
is_single(),
is_search(),
is_404(),
),
true
);
?>
<!DOCTYPE html>
<html <?php language_attributes(); ?>>
<head>
<title><?php wp_title( '' ); ?></title>
<meta charset="<?php bloginfo( 'charset' ); ?>">
<meta name="viewport" content="width=device-width, initial-scale=1">
<?php wp_head(); ?>
</head>
<body <?php echo body_class(); ?>>
<a class="skip-link" href="#maincontent">
<?php echo esc_html__( 'Skip to main content' ); ?>
</a>
<header role="banner" class="site-header bg-secondary flex flex-col items-center justify-start">
<?php get_template_part( 'views/components/nav-aux' ); ?>
<div class="header__nav-main container py-4 items-center grid gap-x-8 gap-y-0 grid-cols-[83px_auto] grid-rows-[1fr] justify-between">
<a href="<?php bloginfo( 'url' ); ?>" class="site-header__logo block size-20">
<img class="size-full" src="<?php echo esc_url( $headerLogo ); ?>" alt="<?php bloginfo( 'name' ); ?> logo"/>
</a>
<?php get_template_part( 'views/components/nav-main' ); ?>
</div>
</header>
<main id="maincontent" class="overflow-hidden min-h-[78dvh]">
<?php if ( $showHero ) : ?>
<?php get_template_part( 'views/partials/page-hero' ); ?>
<?php endif; ?>
+100
View File
@@ -0,0 +1,100 @@
<?php
/**
* Blog posts list
*
* @package BasicWP
* @since 1.0.0
*/
namespace BasicWP;
// Determine classes based on sidebar presence
if ( hasSidebar() ) {
$classes = 'container grid grid-cols-1 lg:grid-cols-4 gap-8 xl:gap-16 my-section mx-auto';
$clsEntry = 'lg:col-span-3';
} else {
$classes = 'container my-section lg:my-0 mx-auto';
}
get_header();
?>
<section class="<?php echo esc_attr( $classes ); ?>">
<?php if ( have_posts() ) : ?>
<div class="blog-posts <?php echo esc_attr( $clsEntry ); ?>">
<div class="post-list">
<div class="post-list__posts grid grid-cols-[repeat(auto-fit,minmax(20rem,1fr))] gap-6">
<?php
while ( have_posts() ) :
the_post();
?>
<div class="post-list__post flex flex-col border border-secondary rounded-md shadow-lg hover:prose-img:scale-110 hover:prose-img:origin-center hover:prose-img:duration-500">
<figure class="post-list__img aspect-video border-b border-secondary rounded-t-md block h-auto w-full overflow-hidden m-0 p-0">
<?php if ( has_post_thumbnail() ) : ?>
<?php
$featImg = get_the_post_thumbnail_url();
$postImg = $featImg ? $featImg : 'https://picsum.photos/600/400?random=' . get_the_ID();
$imgAlt = get_post_meta( get_post_thumbnail_id(), '_wp_attachment_image_alt', true ) ?? get_the_title();
?>
<img class="block h-full object-cover transition-transform duration-300 ease-linear w-full will-change-transform" src="<?php echo esc_url( $postImg ); ?>" alt="<?php echo esc_attr( $imgAlt ); ?>">
<?php endif; ?>
</figure>
<div class="post-list__details px-4 py-8 flex flex-col grow">
<div class="post-list__cats">
Posted in:
<?php
$categories = get_the_category();
foreach ( $categories as $index => $category ) :
$separator = $index < count( $categories ) - 1 ? ', ' : '';
?>
<a href="<?php echo esc_url( get_category_link( $category->term_id ) ); ?>" class="post-list__category text-14px font-semibold leading-none uppercase mt-0 mb-2.5 mx-0 inline-block"><?php echo esc_html( $category->name ); ?></a><?php echo esc_attr( $separator ); ?>
<?php endforeach; ?>
</div>
<a href="<?php the_permalink(); ?>" class="">
<h2 class="post-list__title font-normal text-25px text-balance line-clamp-4 truncate mt-0 mb-5 mx-0"><?php the_title(); ?></h2>
</a>
<div class="post-list__excerpt mb-6">
<?php customExcerpt( get_the_content(), 15 ); ?>
</div>
<div class="post-list__byline mt-auto">
<span class="post-list__author"><?php echo esc_html( ucfirst( get_the_author() ) ); ?> &mdash;</span>
<span class="post-list__date"><?php echo get_the_date(); ?></span>
</div>
</div>
</div>
<?php endwhile; ?>
</div>
<div class="post-list__pagination">
<?php
the_posts_pagination(
array(
'mid_size' => 2,
'prev_text' => '&laquo; Previous',
'next_text' => 'Next &raquo;',
)
);
?>
</div>
</div>
</div>
<?php else : ?>
<div class="blog-posts <?php echo esc_attr( $clsEntry ); ?>">
<div class="no-posts">
<h2 class="no-posts__title">Nothing here yet&hellip;</h2>
<p class="no-posts__desc">No published posts found.</p>
</div>
</div>
<?php endif; ?>
<?php if ( hasSidebar() ) : ?>
<?php get_sidebar(); ?>
<?php endif; ?>
</section>
<?php get_footer(); ?>
+251
View File
@@ -0,0 +1,251 @@
<?php
/*
* On Theme Activation adds Home and News pages, sets up reading options for front page and posts page, and erases sample page and post
*/
// phpcs:ignore
if ( isset( $_GET['activated'] ) && is_admin() ) {
// Set Blog Description to nothing
update_option( 'blogdescription', '' );
// List of pages to create with nested structure
$arrPages = array(
'Home' => array(),
'News' => array(),
'Page Not Found (Error 404)' => array(),
'Contact Us' => array(),
/**
* Sample nested structure
*
* 'Parent Page' => array(
* 'Subpage 1' => array(
* 'Sub-subpage 1',
* 'Sub-subpage 2'
* ),
* 'Subpage 2.2' => array()
* ),
*/
);
foreach ( $arrPages as $pageTitle => $childPages ) {
// phpcs:ignore
$pageExists = get_page_by_title($pageTitle);
if ( ! $pageExists ) {
// Create the parent page
$pageId = wp_insert_post(
array(
'post_title' => $pageTitle,
'post_content' => '',
'post_type' => 'page',
'post_status' => 'publish',
)
);
// If there are child pages, create them under the parent page
foreach ( $childPages as $childPageTitle => $subChildPages ) {
if ( is_array( $subChildPages ) ) {
$subpageId = wp_insert_post(
array(
'post_title' => $childPageTitle,
'post_content' => '',
'post_type' => 'page',
'post_status' => 'publish',
'post_parent' => $pageId,
)
);
foreach ( $subChildPages as $subChildPageTitle ) {
wp_insert_post(
array(
'post_title' => $subChildPageTitle,
'post_content' => '',
'post_type' => 'page',
'post_status' => 'publish',
'post_parent' => $subpageId,
)
);
}
} else {
wp_insert_post(
array(
'post_title' => $childPageTitle,
'post_content' => '',
'post_type' => 'page',
'post_status' => 'publish',
'post_parent' => $pageId,
)
);
}
}
}
}
// Use a static front page
// phpcs:ignore
$home = get_page_by_title('Home');
update_option( 'page_on_front', $home->ID );
update_option( 'show_on_front', 'page' );
// Set the blog/news page
// phpcs:ignore
$news = get_page_by_title('News');
update_option( 'page_for_posts', $news->ID );
// Trash the samples
wp_delete_post( 1, true );
wp_delete_post( 2, true );
// Flush rewrite rules to ensure new pages are recognized
flush_rewrite_rules();
/**
* Install and activate must-use plugins
*/
$muPlugins = array(
array(
'url' => 'https://docs.vincentdevelopment.ca/files/advanced-custom-fields-pro.zip',
'active' => true,
),
array(
'url' => 'https://docs.vincentdevelopment.ca/files/gravity-forms.zip',
'active' => true,
),
array(
'url' => 'https://updraftplus.com/wp-content/uploads/updraftplus.zip',
'active' => false,
),
array(
'url' => 'https://downloads.wordpress.org/plugin/simple-history.5.11.0.zip',
'active' => true,
),
array(
'url' => 'https://downloads.wordpress.org/plugin/autodescription.5.1.2.zip',
'active' => true,
),
array(
'url' => 'https://downloads.wordpress.org/plugin/better-search-replace.1.4.10.zip',
'active' => true,
),
array(
'url' => 'https://downloads.wordpress.org/plugin/google-site-kit.1.153.0.zip',
'active' => false,
),
);
// Custom log file
$logFile = WP_CONTENT_DIR . '/mu-plugin-install.log';
/**
* Simple logging function.
*
* @param string $message The message to log.
*/
function log_message( $message ) {
global $logFile;
$timestamp = gmdate( 'Y-m-d H:i:s' );
// phpcs:ignore
file_put_contents( $logFile, "[$timestamp] $message\n", FILE_APPEND );
}
// Include necessary WordPress files
require_once ABSPATH . 'wp-admin/includes/file.php';
require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
require_once ABSPATH . 'wp-admin/includes/plugin.php';
// Force direct filesystem access
add_filter( 'filesystem_method', fn() => 'direct' );
global $wp_filesystem;
if ( ! WP_Filesystem() ) {
log_message( 'Filesystem initialization failed.' );
return;
}
// Define a silent skin to avoid show_message() errors
// phpcs:disable
class Silent_Upgrader_Skin extends WP_Upgrader_Skin {
public function feedback( $string, ...$args ) {}
public function header() {}
public function footer() {}
public function error( $errors ) {
log_message( 'Upgrader error: ' . print_r( $errors, true ) );
}
public function before() {}
public function after() {}
}
// phpcs:enable
$skin = new Silent_Upgrader_Skin();
$upgrader = new Plugin_Upgrader( $skin );
// Process each plugin
foreach ( $muPlugins as $plug ) {
$plugUrl = $plug['url'];
$shouldActivate = ! empty( $plug['active'] );
// Download plugin
$response = wp_remote_get( $plugUrl );
if ( is_wp_error( $response ) ) {
log_message( "Failed to download plugin from {$plugUrl}: " . $response->get_error_message() );
continue;
}
// Extract filename from URL
$plugFileName = basename( wp_parse_url( $plugUrl, PHP_URL_PATH ) );
// Save the plugin zip
$plugZip = wp_upload_bits( $plugFileName, null, wp_remote_retrieve_body( $response ) );
if ( $plugZip['error'] ) {
log_message( "Failed to save plugin zip {$plugFileName}: " . $plugZip['error'] );
continue;
}
// Install the plugin
$installResult = $upgrader->install( $plugZip['file'] );
if ( is_wp_error( $installResult ) ) {
log_message( "Failed to install plugin {$plugFileName}: " . $installResult->get_error_message() );
// Cleanup temp zip
if ( file_exists( $plugZip['file'] ) ) {
wp_delete_file( $plugZip['file'] );
}
continue;
}
// Get plugin info ( folder/main file )
$plugInfo = $upgrader->plugin_info();
log_message( "plugin_info for {$plugFileName}: {$plugInfo}" );
if ( ! $plugInfo || ! file_exists( WP_PLUGIN_DIR . '/' . $plugInfo ) ) {
log_message( "Could not determine installed plugin file for {$plugFileName}." );
// Cleanup temp zip
if ( file_exists( $plugZip['file'] ) ) {
wp_delete_file( $plugZip['file'] );
}
continue;
}
log_message( "Successfully installed plugin {$plugInfo}." );
// Attempt activation if marked active
if ( $shouldActivate ) {
$activateResult = activate_plugin( $plugInfo );
if ( is_wp_error( $activateResult ) ) {
log_message( "Failed to activate plugin {$plugInfo}: " . $activateResult->get_error_message() );
} else {
log_message( "Successfully activated plugin {$plugInfo}." );
}
} else {
log_message( "Plugin {$plugInfo} installed but not activated ( per configuration )." );
}
// Cleanup temp zip
if ( file_exists( $plugZip['file'] ) ) {
wp_delete_file( $plugZip['file'] );
log_message( "Deleted temporary zip file {$plugZip['file']}." );
}
}
log_message( '=== Plugin installation and activation process completed ===' );
}
+59
View File
@@ -0,0 +1,59 @@
<?php
/**
* ACF (Advanced Custom Fields) support class & functions
*
* @package BasicWP
* @since 1.0.0
*/
namespace BasicWP;
/**
* Class ACF
*
* This class serves as a wrapper or utility for handling Advanced Custom Fields (ACF) functionality.
* It is part of the Basic-WP project and is located in the `lib` directory.
*
* @package Basic-WP
*/
class ACF {
/**
* Variable to hold the file path.
*
* @var string $path The file path associated with the class.
*/
public $path;
/** Constructor.
*
* This constructor initializes the class by setting the file path and
* adding filters for loading and saving JSON files related to ACF.
*/
public function __construct() {
$this->path = get_stylesheet_directory() . '/acf';
add_filter( 'acf/settings/load_json', array( $this, 'loadJson' ) );
add_filter( 'acf/settings/save_json', array( $this, 'saveJson' ) );
}
/** Save JSON.
*
* @param mixed $path The path to save the JSON file.
*/
// phpcs:ignore
public function saveJson( $path ) {
return $this->path;
}
/** Load JSON.
*
* @param mixed $paths The paths to load the JSON file.
*/
// phpcs:ignore
public function loadJson( $paths ) {
return array( $this->path );
}
}
if ( function_exists( 'get_fields' ) ) {
$acfInstance = new ACF();
}
+396
View File
@@ -0,0 +1,396 @@
<?php
namespace BasicWP;
/**
* Class Breadcrumbs
*
* This class is responsible for generating and managing breadcrumb navigation
* for the WordPress theme. Breadcrumbs provide a navigational aid that helps
* users understand their location within the website's hierarchy.
*
* @package Basic-WP
* @subpackage Breadcrumbs
*/
class Breadcrumbs {
/**
* Generates the breadcrumb navigation for the website.
*
* This method is responsible for creating and returning the breadcrumb
* trail, which helps users navigate the website by showing their current
* location within the site's hierarchy.
*
* @return array An array of breadcrumb items, where each item is typically an
* associative array containing 'title' and 'url' keys.
*/
public static function generate() {
global $post;
$breadcrumbs = array();
// Always start with Home
$breadcrumbs[] = self::getHomeBreadcrumb();
if ( is_front_page() ) {
// Front Page - do nothing
return $breadcrumbs;
} elseif ( is_home() ) {
// Blog Index - add the Blog Posts Index page title
$breadcrumbs[] = self::getBlogPostsIndexBreadcrumb();
} elseif ( is_singular( 'post' ) ) {
// Single Post - add the category and post title
$breadcrumbs = array_merge( $breadcrumbs, self::getSinglePostBreadcrumbs() );
} elseif ( is_singular() && ! is_page() ) {
// Single Custom Post Type - add the post type archive and post title
$breadcrumbs = array_merge( $breadcrumbs, self::getCustomPostTypeBreadcrumbs() );
} elseif ( is_page() ) {
// Static Page - add the parent pages if any, and the page title
$breadcrumbs = array_merge( $breadcrumbs, self::getStaticPageBreadcrumbs( $post ) );
} elseif ( is_category() || is_tag() || is_tax() ) {
// Taxonomy Archive - add the taxonomy term name
$breadcrumbs[] = self::getTaxonomyArchiveBreadcrumb();
} elseif ( is_post_type_archive() ) {
// Post Type Archive - add the post type name
$breadcrumbs[] = self::getPostTypeArchiveBreadcrumb();
} elseif ( is_day() ) {
// Daily Archive - add the month and day links
$breadcrumbs = array_merge( $breadcrumbs, self::getDateArchiveBreadcrumbs() );
} elseif ( is_month() ) {
// Monthly Archive - add the month link
$breadcrumbs[] = self::getMonthArchiveBreadcrumb();
} elseif ( is_year() ) {
// Yearly Archive - add the year link
$breadcrumbs[] = self::getYearArchiveBreadcrumb();
} elseif ( is_author() ) {
// Author Archive - add the author name
$breadcrumbs[] = self::getAuthorArchiveBreadcrumb();
} elseif ( is_search() ) {
// Search Results - add the search query
$breadcrumbs[] = self::getSearchBreadcrumb();
} elseif ( is_404() ) {
// 404 Not Found - add a 404 message
$breadcrumbs[] = self::get404Breadcrumb();
}
return $breadcrumbs;
}
/** Generates the breadcrumb for the home page.
*
* This method is responsible for creating the breadcrumb
* link or label that represents the home page in the breadcrumb
* navigation trail.
*
* @return string The HTML or text representation of the home breadcrumb.
*/
private static function getHomeBreadcrumb() {
return array(
'url' => home_url(),
'label' => 'Home',
);
}
/** Generates the breadcrumb for the blog posts index page.
*
* This method is responsible for creating a breadcrumb entry
* that represents the blog posts index page in the breadcrumb trail.
*
* @return array An associative array representing the breadcrumb for the blog posts index page.
*/
private static function getBlogPostsIndexBreadcrumb() {
return array( 'label' => get_the_title( get_option( 'page_for_posts' ) ) );
}
/** Generates the breadcrumb trail for a single post.
*
* This method is responsible for creating the breadcrumb navigation
* specific to single post views. It typically includes links to the
* homepage, category (or categories) the post belongs to, and the
* post title itself.
*
* @return array An array representing the breadcrumb trail, where each
* element is a breadcrumb item (e.g., URL and label).
*/
private static function getSinglePostBreadcrumbs() {
$breadcrumbs = array();
$categories = get_the_category();
$breadcrumbs[] = array(
'url' => get_permalink( get_option( 'page_for_posts' ) ),
'label' => get_the_title( get_option( 'page_for_posts' ) ),
);
if ( ! empty( $categories ) ) {
$cat = $categories[0];
$breadcrumbs[] = array(
'url' => get_category_link( $cat ),
'label' => $cat->name,
);
}
$breadcrumbs[] = array( 'label' => get_the_title() );
return $breadcrumbs;
}
/** Generates breadcrumbs for custom post types.
*
* This method is responsible for creating breadcrumb navigation
* specific to custom post types. It retrieves and formats the
* breadcrumb trail based on the custom post type's hierarchy
* and structure.
*
* @return array An array representing the breadcrumb trail for the custom post type.
*/
private static function getCustomPostTypeBreadcrumbs() {
$breadcrumbs = array();
$postType = get_post_type_object( get_post_type() );
if ( $postType && ! in_array( get_post_type(), array( 'post', 'page' ), true ) ) {
$breadcrumbs[] = array(
'url' => get_post_type_archive_link( $postType->name ),
'label' => $postType->labels->name,
);
}
$breadcrumbs[] = array( 'label' => get_the_title() );
return $breadcrumbs;
}
/** Generates breadcrumbs for a static page.
*
* This method is responsible for creating breadcrumb navigation
* for static pages in a WordPress theme. It utilizes the provided
* post object to determine the breadcrumb structure.
*
* @param WP_Post $post The WordPress post object representing the static page.
* @return array An array of breadcrumb items, where each item is typically
* an associative array containing 'title' and 'url' keys.
*/
private static function getStaticPageBreadcrumbs( $post ) {
$breadcrumbs = array();
if ( $post->post_parent ) {
$ancestors = array_reverse( get_post_ancestors( $post->ID ) );
foreach ( $ancestors as $ancestor ) {
$breadcrumbs[] = array(
'url' => get_permalink( $ancestor ),
'label' => get_the_title( $ancestor ),
);
}
}
$breadcrumbs[] = array( 'label' => get_the_title() );
return $breadcrumbs;
}
/** Generates a breadcrumb for taxonomy archive pages.
*
* This method is responsible for creating breadcrumb navigation
* specific to taxonomy archive pages, providing users with a clear
* path to navigate back to the taxonomy or related sections.
*
* @return array An array representing the breadcrumb trail for the taxonomy archive.
*/
private static function getTaxonomyArchiveBreadcrumb() {
$term = get_queried_object();
return $term && isset( $term->name ) ? array( 'label' => $term->name ) : array();
}
/** Generates a breadcrumb for the archive page of a custom post type.
*
* This method is responsible for creating a breadcrumb link that points
* to the archive page of a specific custom post type. It is typically used
* in breadcrumb navigation to provide users with a way to navigate back
* to the archive page of the post type they are currently viewing.
*
* @return array An array containing the breadcrumb data for the custom post type archive.
*/
private static function getPostTypeArchiveBreadcrumb() {
$postType = get_post_type_object( get_post_type() );
return $postType ? array( 'label' => $postType->labels->name ) : array();
}
/** Generates breadcrumbs for date-based archives.
*
* This method is responsible for creating breadcrumb navigation
* for WordPress date archive pages, such as year, month, or day archives.
*
* @return array An array representing the breadcrumb trail for the date archive.
*/
private static function getDateArchiveBreadcrumbs() {
return array(
array(
'url' => get_month_link( get_query_var( 'year' ), get_query_var( 'monthnum' ) ),
'label' => get_the_date( 'F Y' ),
),
array( 'label' => get_the_date() ),
);
}
/** Generates a breadcrumb for a monthly archive page.
*
* This method is responsible for creating a breadcrumb
* specific to WordPress monthly archive pages. It typically
* includes the year and month as part of the breadcrumb trail.
*
* @return string The HTML markup for the monthly archive breadcrumb.
*/
private static function getMonthArchiveBreadcrumb() {
return array( 'label' => get_the_date( 'F Y' ) );
}
/** Generates a breadcrumb for the year-based archive page.
*
* This method is responsible for creating a breadcrumb link
* specific to the year archive in a WordPress site. It is typically
* used to provide navigation for users when they are viewing posts
* filtered by a specific year.
*
* @return string The HTML markup for the year archive breadcrumb.
*/
private static function getYearArchiveBreadcrumb() {
return array( 'label' => get_the_date( 'Y' ) );
}
/** Generates the breadcrumb for the author archive page.
*
* This method is responsible for creating a breadcrumb trail
* specific to the author archive, providing navigation context
* for pages that display posts by a particular author.
*
* @return string The HTML markup for the author archive breadcrumb.
*/
private static function getAuthorArchiveBreadcrumb() {
$author = get_queried_object();
return array( 'label' => 'Author: ' . $author->display_name );
}
/** Generates the breadcrumb for search results.
*
* This method is responsible for creating a breadcrumb trail
* specific to search result pages. It helps users navigate back
* to the search context or other parts of the site.
*
* @return string The HTML markup for the search breadcrumb.
*/
private static function getSearchBreadcrumb() {
return array( 'label' => 'Search: ' . get_search_query() );
}
/** Generates the breadcrumb trail for a 404 error page.
*
* This method is responsible for creating a breadcrumb structure
* specifically for 404 error pages, providing users with a navigational
* context when a requested page is not found.
*
* @return array An array representing the breadcrumb trail for the 404 page.
*/
private static function get404Breadcrumb() {
return array( 'label' => '404 Not Found' );
}
/** Renders the breadcrumb navigation.
*
* This method generates and outputs the breadcrumb trail for the current page.
*
* @return void
*/
public static function render() {
$items = self::generate();
$metadata = array(
'@context' => 'https://schema.org',
'@type' => 'BreadcrumbList',
'itemListElement' => array(),
);
ob_start();
?>
<nav aria-label="Breadcrumbs" class="flex items-center py-2 -mx-2 leading-none" vocab="https://schema.org/" typeof="BreadcrumbList">
<?php foreach ( $items as $index => $item ) : ?>
<?php
$ldItem = array(
'@type' => 'ListItem',
'position' => $index + 1,
'name' => htmlspecialchars( $item['label'], ENT_QUOTES, 'UTF-8' ),
);
$isActive = ( $index === count( $items ) - 1 );
$activeClass = $isActive ? ' active' : '';
?>
<span class="p-2 breadcrumb-item<?php echo esc_attr( $activeClass ); ?>"
<?php
if ( $isActive ) :
?>
aria-current="page"<?php endif; ?>>
<?php if ( ! empty( $item['url'] ) ) : ?>
<a href="<?php echo esc_url( $item['url'] ); ?>">
<?php if ( $index === 0 ) : ?>
<svg class="flex-shrink-0 w-5 h-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" aria-label="Home">
<path d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z" />
</svg>
<span class="sr-only"><?php echo esc_attr( $item['label'] ); ?></span>
<?php else : ?>
<?php echo esc_attr( $item['label'] ); ?>
<?php endif; ?>
</a>
<?php $ldItem['item'] = $item['url']; ?>
<?php else : ?>
<?php echo esc_attr( $item['label'] ); ?>
<?php endif; ?>
</span>
<?php $metadata['itemListElement'][] = $ldItem; ?>
<?php if ( ! $isActive ) : ?>
<svg class="flex-shrink-0 w-5 h-5 text-light" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd" />
</svg>
<?php endif; ?>
<?php endforeach; ?>
</nav>
<script type="application/ld+json"><?php echo esc_js( wp_json_encode( $metadata ) ); ?></script>
<?php
echo wp_kses(
ob_get_clean(),
array(
'a' => array(
'class' => array(),
'href' => array(),
'title' => array(),
'rel' => array(),
'target' => array(),
'aria-label' => array(),
'aria-current' => array(),
),
'nav' => array(
'aria-label' => array(),
'class' => array(),
'vocab' => array(),
'typeof' => array(),
),
'span' => array(
'class' => array(),
'aria-current' => array(),
),
'svg' => array(
'class' => array(),
'xmlns' => array(),
'viewBox' => array(),
'fill' => array(),
'aria-hidden' => array(),
'aria-label' => array(),
),
'path' => array(
'd' => array(),
'fill-rule' => array(),
'clip-rule' => array(),
'fill' => array(),
'xmlns:xlink' => array(),
),
'script' => array(
'type' => array(),
'src' => array(),
),
)
);
}
}
+112
View File
@@ -0,0 +1,112 @@
<?php
/**
* BasicWP Theme Enqueue Class
*
* @package BasicWP
* @since 1.0.0
*/
namespace BasicWP;
/**
* Class Enqueue
*
* Handles the enqueueing of scripts and styles in a WordPress theme or plugin.
*
* @package Basic-WP
* @since 1.0.0
*/
class Enqueue {
/**
* Initialize hooks to enqueue scripts and styles.
*/
public function __construct() {
add_action( 'wp_enqueue_scripts', array( $this, 'enqFEAssets' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'enqBEAssets' ) );
add_action( 'enqueue_block_editor_assets', array( $this, 'enqEditorAssets' ) );
}
/**
* Enqueue frontend CSS and JS files.
*/
public function enqFEAssets() {
$theme_dir = get_stylesheet_directory();
$theme_uri = get_stylesheet_directory_uri();
/**
* CSS
*/
$css_path = '/static/dist/theme.css';
if ( file_exists( $theme_dir . $css_path ) ) {
$version = filemtime( $theme_dir . $css_path );
wp_enqueue_style( 'basicwp-theme', $theme_uri . $css_path, array(), $version );
}
$font_ver = gmdate( 'U' );
wp_enqueue_style( 'raleway', 'https://fonts.googleapis.com/css2?family=Raleway:wght@100..900&display=swap', false, $font_ver );
/**
* JS
*/
$js_path = '/static/js/theme.js';
if ( file_exists( $theme_dir . $js_path ) ) {
$version = filemtime( $theme_dir . $js_path );
wp_enqueue_script( 'jquery' ); // Needed by downstream scripts; modules can't depend on classic scripts.
wp_enqueue_script_module( 'basicwp-theme', $theme_uri . $js_path, array(), $version );
wp_enqueue_script_module( 'basicwp-button', $theme_uri . '/static/js/components/button.js', array( 'basicwp-theme' ), $version );
}
}
/**
* Enqueue backend (admin/editor) CSS and JS files.
*/
public function enqBEAssets() {
$theme_dir = get_stylesheet_directory();
$theme_uri = get_stylesheet_directory_uri();
$font_ver = gmdate( 'U' );
wp_enqueue_style( 'raleway', 'https://fonts.googleapis.com/css2?family=Raleway:wght@100..900&display=swap', false, $font_ver );
/**
* Admin CSS
*/
$admin_css_path = '/styles/backend/admin.css';
if ( file_exists( $theme_dir . $admin_css_path ) ) {
$version = filemtime( $theme_dir . $admin_css_path );
wp_enqueue_style( 'basicwp-admin', $theme_uri . $admin_css_path, array(), $version );
}
/**
* Admin JS
*/
$admin_js_path = '/static/js/admin.js';
if ( file_exists( $theme_dir . $admin_js_path ) ) {
$version = filemtime( $theme_dir . $admin_js_path );
wp_enqueue_script( 'jquery' ); // Needed by downstream scripts; modules can't depend on classic scripts.
wp_enqueue_script_module( 'basicwp-admin', $theme_uri . $admin_js_path, array(), $version );
wp_enqueue_script_module( 'basicwp-button', $theme_uri . '/static/js/components/button.js', array( 'basicwp-admin' ), $version );
}
}
/**
* Enqueue block editor CSS (scoped to editor to avoid leaking into wp-admin UI).
*/
public function enqEditorAssets() {
$theme_dir = get_stylesheet_directory();
$theme_uri = get_stylesheet_directory_uri();
$editor_css_path = '/styles/backend/editor.css';
$font_ver = gmdate( 'U' );
wp_enqueue_style( 'raleway', 'https://fonts.googleapis.com/css2?family=Raleway:wght@100..900&display=swap', false, $font_ver );
if ( file_exists( $theme_dir . $editor_css_path ) ) {
$version = filemtime( $theme_dir . $editor_css_path );
wp_enqueue_style( 'basicwp-editor', $theme_uri . $editor_css_path, array(), $version );
}
}
}
// Initialize the Enqueue class.
$enqueue = new Enqueue();
+185
View File
@@ -0,0 +1,185 @@
<?php
/**
* BasicWP MenuItems Class
*
* @package BasicWP
* @since 1.0.0
*/
namespace BasicWP;
/**
* Class MenuItems
*
* This class is responsible for managing menu items within the Basic-WP theme.
*
* @package Basic-WP
* @since 1.0.0
*/
class MenuItems {
/**
* Accepts menu name to populate navigation items.
*
* @var string $displayLocation
*/
protected $displayLocation;
/**
* First level menu items (menu_item_parent = 0).
*
* @var array $topLevelNavItems
*/
public $topLevelNavItems;
/**
* True if nav item has children items.
*
* @var bool $hasChildren
*/
public $hasChildren;
/**
* Children navigation items.
*
* @var array $nestedNavItems
*/
public $nestedNavItems;
/**
* True if the current page is the same as the menu item.
*
* @var bool $currentPage
*/
public $currentPage;
/**
* Constructor method for initializing the class with a specific menu name.
*
* @param string $menuName The name of the menu to be used. Defaults to 'main_navigation'.
*/
public function __construct( $menuName = 'main_navigation' ) {
$this->displayLocation = $menuName;
$this->topLevelNavItems = $this->getTopLevelNavItems();
$this->hasChildren = function ( $item ) {
return $this->hasChildren( $item );
};
$this->nestedNavItems = function ( $parentItem ) {
return $this->getNestedNavItems( $parentItem );
};
$this->currentPage = function ( $item ) {
return $this->currentPage( $item );
};
}
/**
* Retrieves the list of menus.
*
* @return array An array containing menu items.
*/
public function getMenus() {
$menus = get_nav_menu_locations();
return empty( $menus ) ? array() : $menus;
}
/**
* Retrieves the navigation items.
*
* @return array An array of navigation items.
*/
public function getNavItems() {
$locations = $this->getMenus();
// If the menu location doesn't exist, return empty array
if ( ! isset( $locations[ $this->displayLocation ] ) ) {
return array();
}
// Get the menu ID for this location
$menuId = $locations[ $this->displayLocation ];
// Direct call to wp_get_nav_menu_items with the menu ID
$items = wp_get_nav_menu_items( $menuId );
// Return empty array if no items
return is_array( $items ) ? $items : array();
}
/**
* Checks if the given parent item has child items.
*
* @param mixed $parentItem The parent item to check for children.
* @return bool Returns true if the parent item has children, false otherwise.
*/
public function hasChildren( $parentItem ) {
foreach ( $this->getNavItems() as $item ) {
if ( $item->menu_item_parent === strval( $parentItem->ID ) ) {
return true;
}
}
return false;
}
/**
* Retrieves the top-level navigation items.
*
* @return array An array of top-level navigation items.
*/
public function getTopLevelNavItems() {
$items = array();
foreach ( $this->getNavItems() as $item ) {
if ( $item->menu_item_parent === '0' ) {
array_push( $items, $item );
}
}
return $items;
}
/**
* Retrieves the nested navigation items for a given parent item.
*
* @param mixed $parentItem The parent navigation item for which nested items are to be retrieved.
* @return array The list of nested navigation items.
*/
public function getNestedNavItems( $parentItem ) {
$childrenItems = array();
foreach ( $this->getNavItems() as $item ) {
if ( $item->menu_item_parent === strval( $parentItem->ID ) ) {
array_push( $childrenItems, $item );
}
}
return $childrenItems;
}
/**
* Determines the current page based on the provided item.
*
* @param mixed $item The item used to determine the current page.
* @return mixed The current page information.
*/
public function currentPage( $item ) {
global $wp;
return ( $item->url === home_url( $wp->request ) . '/' ) ? 'true' : 'false';
}
/**
* Renders the output for the current context.
*
* This method is responsible for generating and returning the
* appropriate output for the current context or request.
*
* @return void
*/
public function render() {
global $views;
// Extract class properties to local variables
$location = $this->displayLocation;
$topLevelNavItems = $this->topLevelNavItems;
$hasChildren = $this->hasChildren;
$nestedNavItems = $this->nestedNavItems;
$currentPage = $this->currentPage;
include $views . '/components/menu-items/index.php';
}
}
+129
View File
@@ -0,0 +1,129 @@
<?php
/**
* Resources custom post type & taxonomies
*
* @package BasicWP
* @since 1.0.0
*/
namespace BasicWP;
/**
* Class Resources
*
* This class is responsible for setting up the Resources post type and taxonomies.
*
* @package Basic-WP
*/
class Resources {
/**
* Constructor for the class.
*
* Initializes the class and sets up any necessary properties or methods.
*/
public function __construct() {
add_action( 'init', array( $this, 'registerPostType' ) );
add_action( 'init', array( $this, 'registerTaxonomy' ) );
add_filter( 'post_type_link', array( $this, 'postTypeLink' ), 10, 2 );
}
/**
* Registers a custom post type.
*
* This method is responsible for defining and registering a custom post type
* with WordPress. It should include all necessary arguments and labels
* required for the post type to function correctly.
*
* @return void
*/
public function registerPostType() {
register_post_type(
'resources',
array(
'labels' => array(
'name' => 'Resources',
'singular_name' => 'Resource',
'menu_name' => 'Resources',
'name_admin_bar' => 'Resource',
'add_new' => 'Add New Resource',
'add_new_item' => 'Add New Resource',
'edit_item' => 'Edit Resource',
'new_item' => 'New Resource',
'view_item' => 'View Resource',
'search_items' => 'Search Resources',
'not_found' => 'No resources found',
'not_found_in_trash' => 'No resources found in Trash',
),
'public' => true,
'has_archive' => true,
'rewrite' => array( 'slug' => 'resources' ),
'supports' => array( 'title', 'editor', 'excerpt', 'thumbnail', 'revisions', 'custom-fields' ),
'menu_position' => 20,
'menu_icon' => 'dashicons-hammer',
'show_in_rest' => true,
)
);
}
/**
* Registers a custom taxonomy.
*
* This method is responsible for defining and registering a custom taxonomy
* within the WordPress environment. It should include the necessary arguments
* and settings for the taxonomy to function as intended.
*
* @return void
*/
public function registerTaxonomy() {
register_taxonomy(
'resource_type',
array( 'resources' ),
array(
'labels' => array(
'name' => 'Resource Types',
'singular_name' => 'Resource Type',
'search_items' => 'Search Resource Types',
'all_items' => 'All Resource Types',
'parent_item' => 'Parent Resource Type',
'parent_item_colon' => 'Parent Resource Type:',
'edit_item' => 'Edit Resource Type',
'update_item' => 'Update Resource Type',
'add_new_item' => 'Add New Resource Type',
'new_item_name' => 'New Resource Type Name',
'menu_name' => 'Resource Types',
),
'public' => true,
'hierarchical' => true,
'show_admin_column' => true,
'rewrite' => array(
'slug' => 'resources',
'with_front' => false,
'hierarchical' => true,
),
'show_in_rest' => true,
)
);
}
/**
* Filters the permalink for a post of a specific post type.
*
* @param string $post_link The post's permalink.
* @param WP_Post $post The post object.
* @return string The filtered post permalink.
*/
public function postTypeLink( $post_link, $post ) {
if ( 'resources' === $post->post_type ) {
$terms = get_the_terms( $post->ID, 'resource_type' );
if ( $terms && ! is_wp_error( $terms ) ) {
$term_slug = $terms[0]->slug;
return home_url( "resources/{$term_slug}/{$post->post_name}" );
}
}
return $post_link;
}
}
new Resources();
+260
View File
@@ -0,0 +1,260 @@
<?php
/**
* Filters. etc
*
* @package BasicWP
* @since 1.0.0
*/
namespace BasicWP;
/** Get child pages of the current page, sorted by menu order.
*
* @return array Array of child page objects, with URL added to each.
*/
function getChildrenPages() {
$children = get_pages(
array(
'child_of' => get_the_ID(),
'sort_order' => 'ASC',
'sort_column' => 'menu_order',
)
);
foreach ( $children as &$child ) {
$child->url = get_page_link( $child->ID );
}
return $children;
}
// Modify which pages should render the sidebar.
add_filter(
'hasSidebar',
function ( $has_sidebar ) {
// Add post types that should never have a sidebar.
if ( is_page() && ! get_field( 'has_sidebar' ) ) {
return false;
}
return $has_sidebar;
}
);
/** Helper to check whether or not the sidebar should be rendered
* (to add/remove a sidebar from a page, edit the filter instead
* of modifying this function).
*/
function hasSidebar() {
return apply_filters( 'hasSidebar', true );
}
// Add extra body classes here.
add_filter(
'body_class',
function ( $classes ) {
if ( hasSidebar() ) {
$classes = array_merge( $classes, array( 'has-sidebar' ) );
}
return $classes;
}
);
/**
* Checks if the page should render a page header.
*
* @return bool true if page header should be rendered, false otherwise
*/
function hasPageHeader() {
global $post;
if ( get_field( 'hero_style' ) !== 'none' ) {
return false;
}
return true;
}
/** Create the Owner role.
*
* This function creates a new role named "Owner" with all the capabilities of the
* Administrator role, except for the following:
*
* - activate_plugins
* - delete_plugins
* - edit_plugins
* - install_plugins
* - update_plugins
* - switch_themes
* - edit_themes
* - delete_themes
* - install_themes
* - update_themes
* - update_core
* - manage_options
*
* This role is meant to be used by a person who should have almost all the same
* capabilities as an Administrator, but should not have the ability to update
* the WordPress core software, manage plugins or themes, or edit other site
* options.
*
* @return void
*/
function createOwnerRole() {
// First, remove the role if it exists.
remove_role( 'owner' );
// Get the administrator role.
$admin_role = get_role( 'administrator' );
$admin_capabilities = $admin_role->capabilities;
// Remove specific capabilities.
$capabilities_to_remove = array(
'activate_plugins',
'delete_plugins',
'edit_plugins',
'install_plugins',
'update_plugins',
'switch_themes',
'edit_themes',
'delete_themes',
'install_themes',
'update_themes',
'update_core',
'manage_options',
);
foreach ( $capabilities_to_remove as $capability ) {
unset( $admin_capabilities[ $capability ] );
}
// Add the Owner role with the modified capabilities.
add_role( 'owner', 'Owner', $admin_capabilities );
}
add_action( 'init', __NAMESPACE__ . '\\createOwnerRole' );
/** Retrieves the appropriate title for the current page context.
*
* The function determines the type of page being viewed and returns
* the corresponding title. It handles different page types, including
* the home page, single posts, archives, search results, and 404 pages.
* If none of these conditions apply, it defaults to fetching the page's title.
*
* @return string The title relevant to the current page context.
*/
function getTheTitle() {
$title = '';
if ( is_home() || is_single() ) {
$title = get_the_title( get_option( 'page_for_posts', true ) );
} elseif ( is_archive() ) {
$title = get_the_archive_title();
} elseif ( is_search() ) {
$title = sprintf(
/* translators: %s is replaced with the search query */
__( 'Search Results for "%s"', 'basicwp' ),
get_search_query()
);
} elseif ( is_404() ) {
$title = 'Page Not Found (error 404)';
} else {
$title = get_the_title();
}
return $title;
}
/** Wraps iframes and embed elements in a div with a specific class.
*
* This function searches for iframe and embed elements within the provided
* content and wraps each found element in a div with the class "embed".
* It is useful for applying consistent styling or responsive behavior
* to embedded media elements.
*
* @param string $content The HTML content containing iframes or embeds.
* @return string The modified content with wrapped iframes and embeds.
*/
function divWrapper( $content ) {
// match any iframes.
$pattern = '~<iframe.*</iframe>|<embed.*</embed>~';
preg_match_all( $pattern, $content, $matches );
foreach ( $matches[0] as $match ) {
// wrap matched iframe with div.
$wrappedframe = '<div class="embed">' . $match . '</div>';
// replace original iframe with new in content.
$content = str_replace( $match, $wrappedframe, $content );
}
return $content;
}
add_filter( 'the_content', __NAMESPACE__ . '\\divWrapper' );
/** Selectively add sidebar to page.
*
* This function adds a custom field group to the WordPress editor for
* pages, allowing users to specify whether a page should have a sidebar.
* Default is no sidebar.
*
* @return void
*/
add_action(
'acf/include_fields',
function () {
if ( ! function_exists( 'acf_add_local_field_group' ) ) {
return;
}
acf_add_local_field_group(
array(
'key' => 'group_6817d79573087',
'title' => 'Page Sidebar',
'fields' => array(
array(
'key' => 'field_6817d7954a168',
'label' => '',
'name' => 'has_sidebar',
'aria-label' => '',
'type' => 'true_false',
'instructions' => '',
'required' => 0,
'conditional_logic' => 0,
'wrapper' => array(
'width' => '',
'class' => '',
'id' => '',
),
'message' => 'Should this page have a sidebar?',
'default_value' => 0,
'allow_in_bindings' => 0,
'ui' => 0,
'ui_on_text' => '',
'ui_off_text' => '',
),
),
'location' => array(
array(
array(
'param' => 'post_type',
'operator' => '==',
'value' => 'page',
),
),
),
'menu_order' => 0,
'position' => 'side',
'style' => 'default',
'label_placement' => 'top',
'instruction_placement' => 'label',
'hide_on_screen' => '',
'active' => true,
'description' => '',
'show_in_rest' => 0,
)
);
}
);
+219
View File
@@ -0,0 +1,219 @@
<?php
/**
* BasicWP Theme Helpers
*
* @package BasicWP
* @since 1.0.0
*/
namespace BasicWP;
// Define global variables for theme and views folder paths.
global $theme, $views;
$theme = get_template_directory();
$views = $theme . '/views';
/** Retrieves a nested value from an ACF field.
*
* @param string $field_path The dot-notated path to the value. For example, 'contact_info.phone'.
*
* @return mixed The value at the specified path.
*/
function getFieldValue( $field_path ) {
$parts = explode( '.', $field_path );
$field = get_field( array_shift( $parts ), 'option' );
foreach ( $parts as $part ) {
$field = $field[ $part ] ?? '';
}
return $field;
}
/**
* Returns wrapper attributes for a block for both preview and front-end contexts.
*
* @param string $classes Space separated class list.
* @param bool $is_preview Whether the block is being previewed in the editor.
*
* @return string Wrapper attributes ready for output.
*/
function blockWrapperAttributes( $classes, $is_preview ) {
if ( $is_preview ) {
return 'class="' . $classes . '"';
}
return get_block_wrapper_attributes(
array(
'class' => $classes,
)
);
}
// Add Global Fields options page.
if ( function_exists( 'acf_add_options_page' ) ) {
add_action(
'init',
function () {
acf_add_options_page(
array(
'page_title' => 'Global Fields',
'menu_title' => 'Global Fields',
'menu_slug' => 'global-fields',
'icon_url' => 'dashicons-admin-site',
)
);
}
);
}
/** Customizes the order of the admin menu items in WordPress.
*
* This function modifies the default menu order in the WordPress admin dashboard
* by specifying a custom sequence for menu items, separators, and additional
* options. If the menu order is not specified, it returns true to allow the
* default order to be used.
*
* @param bool $menu_ord Indicates whether the menu order has been specified.
*
* @return array|bool An array specifying the custom menu order, or true if the
* menu order is not specified.
*/
function customMenuOrder( $menu_ord ) {
if ( ! $menu_ord ) {
return true;
}
return array(
'index.php', // Dashboard.
'global-fields', // Global Theme Fields.
'edit.php?post_type=acf-field-group', // ACF Field Groups.
'separator1', // First separator.
'edit.php', // Posts.
'edit.php?post_type=page', // Pages.
'edit.php?post_type=resources', // Resources.
'upload.php', // Media.
'separator2', // Second separator.
'edit.php?post_type=page-template', // Page Templates.
'edit.php?post_type=wp_block', // Reusable Blocks.
'edit.php?post_type=block-pattern', // Block Patterns.
'edit.php?post_type=element', // Elements.
'separator3', // Third separator.
'link-manager.php', // Links.
'edit-comments.php', // Comments.
'gf_edit_forms', // Gravity Forms.
'themes.php', // Appearance.
'plugins.php', // Plugins.
'separator-last', // Last separator.
'users.php', // Users.
'tools.php', // Tools.
'options-general.php', // Settings.
);
}
add_filter( 'custom_menu_order', __NAMESPACE__ . '\\customMenuOrder', 10, 1 );
add_filter( 'menu_order', __NAMESPACE__ . '\\customMenuOrder', 10, 1 );
/** Add custom block category for our blocks
*
* @param array $categories The existing block categories.
* @return array
*/
function blockCategories( $categories ) {
$vdi_cat = array(
'slug' => 'vdi-blocks',
'title' => 'VDI Custom Blocks',
'icon' => 'dashicons-admin-customizer',
);
array_unshift( $categories, $vdi_cat );
return $categories;
}
add_filter( 'block_categories_all', __NAMESPACE__ . '\\blockCategories', 10, 2 );
/**
* Creates a escaping function to allowed certain HTML for embed content.
* Needed for when echoing the innerblock HTML.
*
* @return array An array of HTML elements allowed.
*/
function escEmbeds() {
/**
* Return the allowed html
* These are the elements in the rendered embed block for youtube and vimeo videos.
* Therefore we need to allow these to keep the same structure.
*/
return array(
'iframe' => array(
'role' => true, // Add role="presentation" to iframes.
'presentation' => true, // Add role="presentation" to iframes.
'src' => true,
'height' => true,
'width' => true,
'frameborder' => true,
'allowfullscreen' => true,
),
'figure' => array(
'class' => true,
),
'div' => array(
'class' => true,
),
);
}
/**
* Finds the numeric position of the first occurrence of a needle in a haystack array.
*
* @param string $haystack The string to search in.
* @param mixed $needles The array of strings to search for.
* @param int $offset (Optional) The position to start the search from.
* @return int|false The numeric position of the first occurrence of a needle in the haystack array.
*/
function strposArray( $haystack, $needles, $offset = 0 ) {
if ( is_array( $needles ) ) {
$positions = array();
foreach ( $needles as $str ) {
$pos = strpos( $haystack, $str, $offset );
if ( $pos !== false ) {
$positions[] = $pos; }
}
return count( $positions ) ? min( $positions ) : false;
} else {
return strpos( $haystack, $needles, $offset );
}
}
/**
* Generates a custom excerpt for the given text.
*
* @param string $text The text to generate the excerpt from.
* @param int $number_of_words The maximum number of words in the excerpt. Default is 55.
* @param string|null $more The string to append to the end of the excerpt if it is truncated. Default is null.
* @return void
*/
function customExcerpt( $text, $number_of_words = 55, $more = null ) {
$allowed_end = array( '.', '!', '?', '...' );
$text_no_html = wp_strip_all_tags( $text );
$trimmed_text = wp_trim_words( $text, $number_of_words, $more );
$trimmed_text_length = strlen( $trimmed_text );
$sentence_end_position = strposArray( $text_no_html, $allowed_end, $trimmed_text_length );
$text_with_html = ( ( $sentence_end_position !== false ) ? substr( $text_no_html, 0, ( $sentence_end_position + 1 ) ) : $trimmed_text );
echo wp_kses_post( wpautop( $text_with_html ) );
}
/** Print a variable to the console for debugging purposes.
*
* @param mixed $data The data to print to the console.
*/
function consoleLog( $data ) {
echo '<script>';
echo 'console.log(' . wp_json_encode( $data ) . ')';
echo '</script>';
}
+301
View File
@@ -0,0 +1,301 @@
<?php
/**
* BasicWP Theme Hooks
*
* @package BasicWP
* @since 1.0.0
*/
namespace BasicWP;
/**
* Add preconnect for Google fonts to head
*
* @return void
*/
add_action(
'wp_head',
function () {
?>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<?php
},
0
);
/**
* Register the navigation menus.
*
* @link https://developer.wordpress.org/reference/functions/register_nav_menus/
*/
register_nav_menus(
array(
'main_navigation' => 'Main Navigation',
'aux_navigation' => 'Auxiliary Navigation',
'footer_navigation' => 'Footer Navigation',
)
);
/**
* Widget Areas
*
* Set up sidebar/widget areas for the theme.
*
* @return void
*/
add_action(
'widgets_init',
function () {
$config = array(
'before_widget' => '<div class="widget %1$s %2$s">',
'after_widget' => '</div>',
'before_title' => '<h2>',
'after_title' => '</h2>',
);
$cfg_foot = array(
'before_widget' => '<div class="widget %1$s %2$s">',
'after_widget' => '</div>',
'before_title' => '<h4>',
'after_title' => '</h4>',
);
register_sidebar(
array(
'name' => 'Primary Sidebar',
'id' => 'sidebar-primary',
) + $config
);
register_sidebar(
array(
'name' => 'Page Sidebar',
'id' => 'sidebar-page',
) + $config
);
register_sidebar(
array(
'name' => 'Footer Area 1',
'id' => 'footer-1',
) + $cfg_foot
);
register_sidebar(
array(
'name' => 'Footer Area 2',
'id' => 'footer-2',
) + $cfg_foot
);
register_sidebar(
array(
'name' => 'Footer Area 3',
'id' => 'footer-3',
) + $cfg_foot
);
}
);
/**
* Basic SEO
*
* {Site URL}: {Title}
*/
add_filter(
'wp_title',
function ( $title ) {
$site_name = get_bloginfo( 'name' );
return "{$site_name}: {$title}";
}
);
/**
* Excerpt
*/
add_filter(
'excerpt_more',
function () {
return '&hellip;';
}
);
/**
* Whether the page hero should hold the main h1 of the page.
*/
add_filter(
'include_page_title_in_hero',
function ( $include_title ) {
// Add post types that should not use the title in the hero.
if ( is_singular( ( 'post' ) ) ) {
return false;
}
return $include_title;
}
);
/**
* WP Cleanup
*/
function init() {
remove_action( 'wp_head', 'print_emoji_detection_script', 7 );
remove_action( 'wp_print_styles', 'print_emoji_styles' );
remove_action( 'admin_print_styles', 'print_emoji_styles' );
remove_action( 'admin_print_scripts', 'print_emoji_detection_script' );
remove_filter( 'the_content_feed', 'wp_staticize_emoji' );
remove_filter( 'comment_text_rss', 'wp_staticize_emoji' );
remove_filter( 'wp_mail', 'wp_staticize_emoji_for_email' );
wp_dequeue_style( 'wp-block-library' ); // Core block styles.
wp_dequeue_style( 'wp-block-library-theme' ); // Block theme styles.
wp_dequeue_style( 'global-styles' ); // Global styles.
wp_dequeue_style( 'core-block-supports' ); // Core block supports.
wp_dequeue_style( 'core-block-styles' ); // Core block styles.
remove_action( 'wp_enqueue_scripts', 'wp_enqueue_global_styles', 1 );
remove_action( 'wp_enqueue_scripts', 'wp_enqueue_classic_theme_styles', 1 );
remove_action( 'wp_head', 'wp_print_head_scripts', 9 );
remove_action( 'wp_head', 'wp_generator' ); // WordPress version.
remove_action( 'wp_head', 'rsd_link' ); // RSD link.
remove_action( 'wp_head', 'wlwmanifest_link' ); // Windows Live Writer.
remove_action( 'wp_head', 'wp_shortlink_wp_head' ); // Shortlink.
remove_action( 'wp_head', 'rest_output_link_wp_head' ); // REST API link.
remove_action( 'wp_head', 'wp_oembed_add_discovery_links' ); // oEmbed discovery links.
remove_action( 'wp_head', 'rel_canonical' ); // Canonical URL.
remove_action( 'wp_head', 'wp_resource_hints', 2 ); // DNS Prefetch.
add_filter( 'wp_img_tag_add_width_and_height_attr', '__return_false' ); // Disable intrinsic image size.
add_filter( 'wp_img_tag_add_auto_sizes', '__return_false' ); // Disable auto sizes.
add_filter( 'xmlrpc_enabled', '__return_false' );
// Add theme support features
add_theme_support( 'post-thumbnails' );
add_theme_support( 'title-tag' );
add_theme_support(
'html5',
array(
'caption',
'comment-form',
'comment-list',
'gallery',
'global-search-form',
'script',
'style',
)
);
add_theme_support( 'align-wide' );
add_theme_support( 'editor-styles' );
add_theme_support( 'responsive-embeds' );
add_theme_support( 'customize-selective-refresh-widgets' );
}
add_action( 'init', __NAMESPACE__ . '\\init', 1 );
/**
* Allow SVG uploads
*/
add_filter(
'wp_check_filetype_and_ext',
function ( $data, $file, $filename, $mimes ) {
global $wp_version;
if ( '4.7.1' !== $wp_version ) {
return $data;
}
$filetype = wp_check_filetype( $filename, $mimes );
return array(
'ext' => $filetype['ext'],
'type' => $filetype['type'],
'proper_filename' => $data['proper_filename'],
);
},
10,
4
);
add_filter(
'upload_mimes',
function ( $mimes ) {
$mimes['svg'] = 'image/svg+xml';
return $mimes;
}
);
/**
* Fix display issues with SVGs in admin
*/
add_action(
'admin_head',
function () {
echo '
<style type="text/css">
.attachment-266x266, .thumbnail img {
width: 100% !important;
height: auto !important;
}
</style>
';
}
);
/**
* Filters the email address used as the sender in outgoing emails.
*
* This function allows you to modify the default "from" email address
* used by WordPress when sending emails.
*
* @param string $old The original email address.
* @return string The new email address to use as the sender.
*/
// phpcs:ignore
function new_mail_from( $old ) {
return get_option( 'admin_email' );
}
/**
* Filters the name used as the sender in outgoing emails.
*
* This function allows you to modify the default "from" name
* used by WordPress when sending emails.
*
* @param string $old The original name.
* @return string The new name to use as the sender.
*/
// phpcs:ignore
function new_mail_from_name( $old ) {
return get_option( 'blogname' );
}
add_filter( 'wp_mail_from', __NAMESPACE__ . '\\new_mail_from' );
add_filter( 'wp_mail_from_name', __NAMESPACE__ . '\\new_mail_from_name' );
/**
* Add child page template
*/
add_filter(
'page_template',
function ( $template ) {
global $post;
if ( $post->post_parent ) {
// get top level parent page
$parent = get_post(
reset( array_reverse( get_post_ancestors( $post->ID ) ) )
);
$child_template = locate_template(
array(
$parent->post_name . '-child.php',
)
);
if ( $child_template ) {
return $child_template;
}
}
return $template;
}
);
+122
View File
@@ -0,0 +1,122 @@
<?php
/**
* Search features for BasicWP theme.
*
* @package BasicWP
* @since 1.0.0
*/
namespace BasicWP;
/**
* Modifies the WordPress query object for page search functionality.
*
* @param WP_Query $query The WordPress query object.
*
* @return void
*/
function pageSearch( $query ) {
if ( ! is_admin() && $query->is_main_query() && $query->is_search ) {
$query->set( 'paged', ( get_query_var( 'paged' ) ) ? get_query_var( 'paged' ) : 1 );
$query->set( 'posts_per_page', -1 );
}
}
add_action( 'pre_get_posts', __NAMESPACE__ . '\\pageSearch' );
/**
* Sort WP_Post objects in reverse order (most recent first).
*
* @param string $key WP_Post object property used for sorting. 'post_date' is assumed.
*
* @return int
*/
function postSort( $key ) {
return function ( $a, $b ) use ( $key ) {
if ( $a->$key < $b->$key ) {
return 1;
} elseif ( $a->$key > $b->$key ) {
return -1;
} else {
// If first comparison is equal, use title as secondary sort key.
return strnatcasecmp( $a->post_title, $b->post_title );
}
};
}
/**
* Remove duplicate posts in combined list from default and secondary queries.
*
* @param array $posts Array of WP_Post objects.
* @param string $key Search key used to identify duplicate objects. Post ID is used.
*
* @return array
*/
function dedupe( $posts, $key ) {
$unique_posts = array();
$ids = array();
foreach ( $posts as $post ) {
if ( ! in_array( $post->$key, $ids, true ) ) {
$ids[] = $post->$key;
$unique_posts[] = $post;
}
}
return $unique_posts;
}
/**
* Posts_results filter hook callback.
*
* @param array $posts Array of WP_Post objects.
* @param object $query WP_Query object from default search.
*
* @return array
*/
function searchResultFilter( $posts, $query ) {
if ( \is_search() && $query->is_search() && ! \is_admin() ) {
$args = array(
'post_type' => array( 'post', 'page' ),
'posts_per_page' => -1,
'tax_query' => array(
'relation' => 'OR', // Include both tags and categories.
array(
'taxonomy' => 'post_tag',
'field' => 'name',
'terms' => $query->get( 's' ),
),
array(
'taxonomy' => 'category',
'field' => 'name',
'terms' => $query->get( 's' ),
),
),
);
// Remove callback to avoid infinite loop.
remove_filter( 'posts_results', __NAMESPACE__ . '\\searchResultFilter', 10 );
$secondary_query = new \WP_Query( $args );
$tagged_posts = $secondary_query->get_posts();
// Combine default search results with secondary query.
$all_posts = array_merge( $posts, $tagged_posts );
// Remove duplicate posts.
$unique_posts = dedupe( $all_posts, 'ID' );
// Sort by reverse post_date order (most recent first).
usort( $unique_posts, postSort( 'post_date' ) );
// Restore posts_results callback.
add_filter( 'posts_results', __NAMESPACE__ . '\\searchResultFilter', 10, 2 );
return $unique_posts;
} else {
return $posts;
}
}
add_filter( 'posts_results', __NAMESPACE__ . '\\searchResultFilter', 10, 2 );
+174
View File
@@ -0,0 +1,174 @@
<?php
/**
* Add a comment to show which template is being used on the current page.
*
* @package BasicWP
* @since 1.0.0
*/
namespace BasicWP;
/**
* Class ShowTemplate
*
* Displays the active WordPress template in the footer for debugging purposes.
* Determines which template WordPress has chosen to use and outputs it as an HTML comment.
*
* @package BasicWP
* @since 1.0.0
*/
class ShowTemplate {
/**
* The template file path that WordPress has chosen to use.
*
* @var string|false
*/
private $template = false;
/**
* Constructor for ShowTemplate class.
*
* Initializes the template checking functionality by hooking into template_redirect action.
* Does nothing if called from admin area.
*
* @since 1.0.0
*/
public function __construct() {
if ( is_admin() ) {
return;
}
add_action( 'template_redirect', array( &$this, 'checkTemplate' ), 0 );
}
/**
* Using the same logic used by WordPress determine the template to be used
*
* @since 1.0.0
*/
public function checkTemplate() {
if ( is_404() ) {
$template = get_404_template();
if ( $template ) {
$this->template = $template;
}
} elseif ( is_search() ) {
$template = get_search_template();
if ( $template ) {
$this->template = $template;
}
} elseif ( is_tax() ) {
$template = get_taxonomy_template();
if ( $template ) {
$this->template = $template;
}
} elseif ( is_home() ) {
$template = get_home_template();
if ( $template ) {
$this->template = $template;
}
} elseif ( is_front_page() ) {
$template = get_front_page_template();
if ( $template ) {
$this->template = $template;
}
} elseif ( is_attachment() ) {
$template = get_attachment_template();
if ( $template ) {
$this->template = $template;
}
} elseif ( is_single() ) {
$template = get_single_template();
if ( $template ) {
$this->template = $template;
}
} elseif ( is_page() ) {
$template = get_page_template();
if ( $template ) {
$this->template = $template;
}
} elseif ( is_category() ) {
$template = get_category_template();
if ( $template ) {
$this->template = $template;
}
} elseif ( is_tag() ) {
$template = get_tag_template();
if ( $template ) {
$this->template = $template;
}
} elseif ( is_author() ) {
$template = get_author_template();
if ( $template ) {
$this->template = $template;
}
} elseif ( is_date() ) {
$template = get_date_template();
if ( $template ) {
$this->template = $template;
}
} elseif ( is_archive() ) {
$template = get_archive_template();
if ( $template ) {
$this->template = $template;
}
} else {
$this->template = function_exists( 'get_index_template' ) ? get_index_template() : get_template_directory() . '/index.php';
}
$this->template = apply_filters( 'template_include', $this->template );
// Hook into the footer so we can echo the active template
add_action( 'wp_footer', array( &$this, 'show_template' ), 100 );
}
/**
* Echo the active template to the footer
* Try to catch when a plugin or otherwise hooks template_redirect to include a different template
*
* @since 1.0.0
*/
public function show_template() {
$fudge = false;
foreach ( debug_backtrace() as $trace ) { // phpcs:ignore
switch ( $trace['function'] ) {
case 'wp_footer':
$wp_footer = $trace['file'];
break;
case 'get_footer':
$get_footer = $trace['file'];
break;
}
}
$fudge = isset( $get_footer ) ? $get_footer : $wp_footer;
if ( $fudge === $this->template || $fudge === false ) {
echo wp_kses_post( "<!-- Active Template: {$this->template} -->\n" );
} else {
echo esc_html( "<!--\n" );
echo esc_html( "The template loader logic has chosen a different template than what was used.\n\n" );
echo esc_html( "Chosen Template: {$this->template}\n" );
echo esc_html( "Actual Template: $fudge\n\n" );
echo esc_html( "This will usually occur if the template file was overriden using an action on template_redirect.\n" );
echo esc_html( "This is a best effort guess to catch such scenarios as mentioned above but can be incorrect.\n" );
echo esc_html( "-->\n" );
}
}
}
$ShowTemplate = new ShowTemplate();
+3021
View File
File diff suppressed because it is too large Load Diff
+42
View File
@@ -0,0 +1,42 @@
{
"name": "vdi-starter-v5",
"version": "5.0",
"description": "VDI-Starter-v5 is a minimal WordPress theme designed as a starting point for custom theme development. It focuses on modern development approaches with a lean architecture that avoids the overhead of theme frameworks.",
"scripts": {
"start": "npm run watch",
"watch": "node bin/.watch.js",
"build": "npx @tailwindcss/cli -i ./styles/theme.css -o ./static/dist/theme.css --optimize"
},
"repository": {
"type": "git",
"url": "git+https://github.com/Vincent-Design-Inc/VDI-Starter-v5.git"
},
"keywords": [
"wordpress",
"minimal",
"basic",
"custom",
"theme"
],
"author": "Keith Solomon <keith@vincentdesign.ca>",
"license": "MIT",
"bugs": {
"url": "https://github.com/Vincent-Design-Inc/VDI-Starter-v5/issues"
},
"homepage": "https://github.com/Vincent-Design-Inc/VDI-Starter-v5#readme",
"devDependencies": {
"@axe-core/playwright": "^4.10.1",
"@playwright/test": "^1.52.0",
"@types/node": "^22.15.2",
"browser-sync": "^3.0.3",
"dotenv": "^16.4.7"
},
"dependencies": {
"@tailwindcss/cli": "^4.0.13",
"@tailwindcss/typography": "^0.5.16",
"glob": "^10.3.10",
"lodash": "^4.17.21",
"lodash.debounce": "^4.0.8",
"tailwindcss": "^4.0.13"
}
}
+41
View File
@@ -0,0 +1,41 @@
<?php
/**
* Single Pages
*
* @package BasicWP
* @since 1.0.0
*/
namespace BasicWP;
get_header();
$clsEntry = '';
// Determine classes based on sidebar presence
if ( hasSidebar() ) {
$classes = 'container grid grid-cols-1 lg:grid-cols-4 gap-8 xl:gap-16 my-section mx-auto';
$clsEntry = 'lg:col-span-3';
} else {
$classes = 'container my-section lg:my-0 mx-auto';
}
?>
<article class="<?php echo esc_attr( $classes ); ?>">
<div class="entry-content <?php echo esc_attr( $clsEntry ); ?>">
<?php
if ( have_posts() ) {
while ( have_posts() ) {
the_post();
the_content();
}
}
?>
</div>
<?php if ( hasSidebar() ) : ?>
<?php get_sidebar( 'page' ); ?>
<?php endif; ?>
</article>
<?php get_footer(); ?>
+82
View File
@@ -0,0 +1,82 @@
// @ts-check
import { defineConfig, devices } from '@playwright/test';
/**
* Read environment variables from file.
* https://github.com/motdotla/dotenv
*/
// import dotenv from 'dotenv';
// import path from 'path';
// dotenv.config({ path: path.resolve(__dirname, '.env') });
/**
* @see https://playwright.dev/docs/test-configuration
*/
export default defineConfig({
testDir: './tests',
/* Run tests in files in parallel */
fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
/* Retry on CI only */
retries: process.env.CI ? 2 : 0,
/* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: 'html',
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like `await page.goto('/')`. */
// baseURL: 'http://127.0.0.1:3000',
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',
ignoreHTTPSErrors: true,
},
/* Configure projects for major browsers */
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
// {
// name: 'firefox',
// use: { ...devices['Desktop Firefox'] },
// },
// {
// name: 'webkit',
// use: { ...devices['Desktop Safari'] },
// },
/* Test against mobile viewports. */
// {
// name: 'Mobile Chrome',
// use: { ...devices['Pixel 5'] },
// },
// {
// name: 'Mobile Safari',
// use: { ...devices['iPhone 12'] },
// },
/* Test against branded browsers. */
// {
// name: 'Microsoft Edge',
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
// },
// {
// name: 'Google Chrome',
// use: { ...devices['Desktop Chrome'], channel: 'chrome' },
// },
],
/* Run your local dev server before starting the tests */
// webServer: {
// command: 'npm run start',
// url: 'http://127.0.0.1:3000',
// reuseExistingServer: !process.env.CI,
// },
});
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

+103
View File
@@ -0,0 +1,103 @@
<?php
/**
* Blog search template
*
* @package BasicWP
* @since 1.0.0
*/
namespace BasicWP;
// Determine classes based on sidebar presence
if ( hasSidebar() ) {
$classes = 'container grid grid-cols-1 lg:grid-cols-4 gap-8 xl:gap-16 my-section mx-auto';
$clsEntry = 'lg:col-span-3';
} else {
$classes = 'container my-section lg:my-0 mx-auto';
}
get_header();
?>
<section class="<?php echo esc_attr( $classes ); ?>">
<?php if ( have_posts() ) : ?>
<div class="blog-posts <?php echo esc_attr( $clsEntry ); ?>">
<div class="post-list">
<div class="post-list__posts grid grid-cols-[repeat(auto-fit,minmax(20rem,1fr))] gap-6">
<?php
while ( have_posts() ) :
the_post();
?>
<div class="post-list__post flex flex-col border border-secondary rounded-md shadow-lg hover:prose-img:scale-110 hover:prose-img:origin-center hover:prose-img:duration-500">
<figure class="post-list__img aspect-video border-b border-secondary rounded-t-md block h-auto w-full overflow-hidden m-0 p-0">
<?php
$featImg = get_the_post_thumbnail_url();
$postImg = $featimg ? $featImg : 'https://picsum.photos/600/400?random=' . get_the_ID();
$imgAlt = get_post_meta( get_post_thumbnail_id(), '_wp_attachment_image_alt', true ) ?? get_the_title();
?>
<img class="block h-full object-cover transition-transform duration-300 ease-linear w-full will-change-transform" src="<?php echo esc_url( $postImg ); ?>" alt="<?php echo esc_attr( $imgAlt ); ?>">
</figure>
<div class="post-list__details px-4 py-8 flex flex-col flex-grow">
<?php
$categories = get_the_category();
if ( ! empty( $categories ) ) {
?>
<div class="post-list__cats">
Posted in:
<?php
foreach ( $categories as $index => $category ) :
$separator = $index < count( $categories ) - 1 ? ', ' : '';
?>
<a href="<?php echo esc_url( get_category_link( $category->term_id ) ); ?>" class="post-list__category text-14px font-semibold leading-none uppercase mt-0 mb-2.5 mx-0 inline-block"><?php echo esc_html( $category->name ); ?></a><?php echo esc_attr( $separator ); ?>
<?php endforeach; ?>
</div>
<?php } else { ?>
<div class="post-list__cats">
<?php $postType = get_post_type(); ?>
Post Type: <span class="post-list__category text-14px font-semibold leading-none uppercase mt-0 mb-2.5 mx-0 inline-block"><?php echo esc_html( $postType ); ?></span>
</div>
<?php } ?>
<a href="<?php the_permalink(); ?>" class="">
<h2 class="post-list__title font-normal text-25px text-balance line-clamp-4 truncate mt-0 mb-5 mx-0"><?php the_title(); ?></h2>
</a>
<div class="post-list__byline mt-auto">
<span class="post-list__author"><?php echo esc_html( ucfirst( get_the_author() ) ); ?> &mdash;</span>
<span class="post-list__date"><?php echo get_the_date(); ?></span>
</div>
</div>
</div>
<?php endwhile; ?>
</div>
<div class="post-list__pagination">
<?php
the_posts_pagination(
array(
'mid_size' => 2,
'prev_text' => '&laquo; Previous',
'next_text' => 'Next &raquo;',
)
);
?>
</div>
</div>
</div>
<?php else : ?>
<div class="blog-posts <?php echo esc_attr( $clsEntry ); ?>">
<div class="no-posts">
<h2 class="no-posts__title">Nothing here yet&hellip;</h2>
<p class="no-posts__desc">No published posts found.</p>
</div>
</div>
<?php endif; ?>
<?php if ( hasSidebar() ) : ?>
<?php get_sidebar(); ?>
<?php endif; ?>
</section>
<?php get_footer(); ?>
+15
View File
@@ -0,0 +1,15 @@
<?php
/**
* Page Sidebar
*
* @package BasicWP
*/
namespace BasicWP;
?>
<aside class="sidebar sidebar-page">
<h2><?php esc_html_e( 'Page Sidebar', 'basicwp' ); ?></h2>
<?php dynamic_sidebar( 'sidebar-page' ); ?>
</aside>
+14
View File
@@ -0,0 +1,14 @@
<?php
/**
* Sidebar
*
* @package BasicWP
*/
namespace BasicWP;
?>
<aside class="sidebar">
<?php dynamic_sidebar( 'sidebar-primary' ); ?>
</aside>
+86
View File
@@ -0,0 +1,86 @@
<?php
/**
* Single Posts
*
* @package BasicWP
* @since 1.0.0
*/
namespace BasicWP;
get_header();
// Determine classes based on sidebar presence
if ( hasSidebar() ) {
$classes = 'container grid grid-cols-1 lg:grid-cols-4 gap-8 xl:gap-16 my-section mx-auto';
$clsEntry = 'lg:col-span-3';
} else {
$classes = 'container my-section lg:my-0 mx-auto';
}
?>
<article class="<?php echo esc_attr( $classes ); ?>">
<div class="entry-content <?php echo esc_attr( $clsEntry ); ?>">
<?php
if ( have_posts() ) {
while ( have_posts() ) {
the_post();
?>
<div class="post-title mb-0">
<h1 class="mb-0"><?php the_title(); ?></h1>
</div>
<div class="post-meta text-14px italic mb-6">
<div class="">
<span class="post-date">Posted: <?php the_date(); ?></span> | <span class="post-author">Posted by: <?php echo esc_html( ucfirst( get_the_author() ) ); ?></span>
</div>
<div class="">
<span class="post-categories">
<?php
$categories = get_the_category();
if ( ! empty( $categories ) ) {
echo '<span>Posted in: ';
foreach ( $categories as $ct ) {
echo '<a href="' . esc_url( get_category_link( $ct->cat_ID ) ) . '">' . esc_html( $ct->name ) . '</a>';
if ( count( $categories ) > 1 && $ct !== end( $categories ) ) {
echo ', ';
}
}
echo '</span>';
}
?>
</span>
|
<span class="post-tags">
<?php
$tags = get_the_tags();
if ( ! empty( $tags ) ) {
echo '<span>Tagged: ';
foreach ( $tags as $tg ) {
echo '<a href="' . esc_url( get_tag_link( $tg->term_id ) ) . '">' . esc_html( $tg->name ) . '</a>';
if ( count( $tags ) > 1 && $tg !== end( $tags ) ) {
echo ', ';
}
}
echo '</span>';
}
?>
</span>
</div>
</div>
<?php
the_content();
}
}
?>
</div>
<?php if ( hasSidebar() ) : ?>
<?php get_sidebar(); ?>
<?php endif; ?>
</article>
<?php get_footer(); ?>
+7
View File
@@ -0,0 +1,7 @@
<svg width="83px" height="83px" viewBox="0 0 83 83" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Copyright (c) 2025 Kevinleary.net, LLC. All rights reserved. -->
<title>kevinleary.net</title>
<circle fill="#FFFFFF" cx="41.5" cy="41.5" r="41.5"></circle>
<path d="M41.4990862,5 C21.3735072,5 5,21.3753349 5,41.5009138 C5,61.6264928 21.3735072,78 41.5009138,78 C61.6264928,78 78,61.6264928 78,41.5009138 C78,21.3753349 61.6264928,5 41.4990862,5 Z M50.341036,59.1902962 L34.0314949,42.9703077 L33.9218386,43.0799639 L33.9218386,59.1902962 L30.0893523,59.1902962 L30.0893523,22.8904188 L33.9218386,22.8904188 L33.9218386,37.9517062 L49.2389906,22.8904188 L54.5006634,22.8904188 L36.7016248,40.400696 L55.5387427,59.1902962 L50.341036,59.1902962 Z" fill="#000000" fill-rule="nonzero"></path>
<path d="M41.5,0 C18.5798958,0 0,18.5798958 0,41.5 C0,64.418375 18.5798958,83 41.5,83 C64.418375,83 83,64.4201042 83,41.5 C83,18.5798958 64.4201042,0 41.5,0 Z M41.5,80.2934783 C20.1078009,80.2934783 2.70652174,62.8907964 2.70652174,41.5009041 C2.70652174,20.1092036 20.1096092,2.70652174 41.5,2.70652174 C62.8903908,2.70652174 80.2934783,20.1092036 80.2934783,41.5009041 C80.2934783,62.8907964 62.8903908,80.2934783 41.5,80.2934783 Z" fill="#000000" fill-rule="nonzero"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 976 B

+12
View File
@@ -0,0 +1,12 @@
/**
* Admin JS
*/
import { registerButtonComponent } from './components/button.js';
const app = () => {
registerButtonComponent();
}
document.addEventListener('DOMContentLoaded', app);
console.log(`admin.js loaded.`);
+33
View File
@@ -0,0 +1,33 @@
// Back to Top Button Component
class BackToTopButton extends HTMLElement {
connectedCallback() {
this.innerHTML = `
<button id="backToTopBtn" aria-label="Back to top" class="back-to-top" style="">
↑ Top
</button>
`;
const btn = this.querySelector('#backToTopBtn');
let previousScrollY = window.scrollY;
window.addEventListener('scroll', () => {
const currentScrollY = window.scrollY;
const isScrollingUp = currentScrollY < previousScrollY;
const shouldShowButton = currentScrollY > 300 && isScrollingUp;
btn.style.display = shouldShowButton ? 'block' : 'none';
previousScrollY = currentScrollY;
});
btn.addEventListener('click', () => {
window.scrollTo({ top: 0, behavior: 'smooth' });
});
}
}
export function registerBackToTopButton() {
if (!customElements.get('back-to-top')) {
customElements.define('back-to-top', BackToTopButton);
}
}
+99
View File
@@ -0,0 +1,99 @@
class ButtonComponent extends HTMLElement {
/**
* Parameters
* - btnClasses: Additional classes to add to the block.
* - element: The element to use for the button. Defaults to 'a'.
* - url: The URL to link to.
* - target: The target for the link.
* - title: The text to display on the button.
* - ariaLabel: The ARIA label for the button.
* - color: The color of the button.
* - variant: The variant of the button.
* - size: The size of the button.
* - width: The width of the button.
*
*/
connectedCallback() {
if (!this.querySelector(this.getAttribute('element'))) {
this.append(document.createElement(this.getAttribute('element')));
}
this.update();
}
static get observedAttributes() {
return [
'btnClasses',
'el',
'element',
'type',
'url',
'target',
'title',
'ariaLabel',
'color',
'variant',
'size',
'width',
]
}
attributeChangedCallback() {
this.update();
}
update() {
const btn = this.querySelector(this.getAttribute('element'));
// console.log('[ButtonComponent] attributes', {
// btnClasses: this.getAttribute('btnClasses'),
// element: this.getAttribute('element'),
// type: this.getAttribute('type'),
// url: this.getAttribute('url'),
// target: this.getAttribute('target'),
// title: this.getAttribute('title'),
// ariaLabel: this.getAttribute('ariaLabel'),
// color: this.getAttribute('color'),
// variant: this.getAttribute('variant'),
// size: this.getAttribute('size'),
// width: this.getAttribute('width'),
// });
if (btn) {
btn.classList = this.getAttribute('btnClasses') || '';
if (this.getAttribute('element') == 'a') {
btn.href = this.getAttribute('url') || '#';
if (btn.target) {
btn.target = 'target="${this.getAttribute(target)}"';
}
}
const type = this.getAttribute('type');
if (type && this.getAttribute('element') !== 'a') {
btn.type = type;
}
btn.title = this.getAttribute('title') || '';
btn.textContent = this.getAttribute('title') || '';
if (!this.getAttribute('ariaLabel') && this.getAttribute('url')) {
btn.setAttribute('aria-label', `Link to ${this.getAttribute('url')}`);
} else {
btn.setAttribute('aria-label', this.getAttribute('ariaLabel'));
}
btn.setAttribute('aria-label', this.getAttribute('ariaLabel'));
btn.setAttribute('data-button-color', this.getAttribute('color'));
btn.setAttribute('data-button-variant', this.getAttribute('variant'));
btn.setAttribute('data-button-size', this.getAttribute('size'));
btn.setAttribute('data-button-width', this.getAttribute('width'));
}
}
}
export const registerButtonComponent = () => {
customElements.define('x-button', ButtonComponent);
}
+12
View File
@@ -0,0 +1,12 @@
/**
* Get Header Height
* -
* Get header height and set CSS variable "--hgtHeader" to the header height.
*/
function getHeaderHeight() {
const headerHeight = document.querySelector('.header__nav-main').getBoundingClientRect().height;
document.documentElement.style.setProperty('--hgtHeader', `${headerHeight}px`);
}
export default getHeaderHeight;
+576
View File
@@ -0,0 +1,576 @@
/**
* VDI Main Nav - All Main Navigation Functionality
*
* Please review documentation upon first use, and, as-needed:
* https://docs.vincentdevelopment.ca/docs/starter-v3-enhancements/navigation/
*/
/**
* Navigation
* Handles navigation logic
*
* @param {string} mobileMenuButtonId
* @param {string} dropDownClass
* @param {string} subMenuLinkClass
*/
class Navigation {
/**
* The main toggle element
*
* @type {HTMLElement}
* @link {https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_properties}
*/
#mobileMenuButton;
/**
* List of sub dropdown buttons
*
* @type {NodeList}
* @link {https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_properties}
*/
#dropdownButtons;
/**
* Class name to identify sub menu items (ul>li>a)
* @type {string}
*/
#subMenuElementIdentifier;
/**
* Current navigation level in sliding viewport
* @type {number}
*/
#currentLevel = 0;
/**
* Navigation stack for breadcrumb functionality
* @type {Array}
*/
#navigationStack = [];
/**
* Whether sliding viewport mode is enabled
* @type {boolean}
*/
#slidingViewportEnabled = false;
constructor(mobileMenuButtonId, dropDownClass, subMenuLinkClass = "sub-menu-item") {
this.#mobileMenuButton = document.getElementById(mobileMenuButtonId);
this.#dropdownButtons = document.querySelectorAll(dropDownClass); // Do not change this to getElementsByClassName, logic is iterating over non-live node list
this.#subMenuElementIdentifier = subMenuLinkClass;
this.handleEscapeKey();
// Initialize sliding viewport immediately if styles are detected
this.initializeSlidingViewport();
}
/**
* Handles main mobile toggling
* Adds an event listener to mobile menu dropdown
* toggle button
*/
mobileMenuToggle() {
this.#mobileMenuButton.addEventListener("click", (event) => {
this.toggleMobileMenu(event, this.#mobileMenuButton); // toggle submenu on mobile
});
this.closeOnBlur(this.#mobileMenuButton, { "navType": "desktop" }); // close submenu when user clicks outside
this.closeOnBlur(this.#mobileMenuButton, { "navType": "mobile" });
}
/**
* Handles dropdowns on desktop navigation
* Loops over list of navigation dropdown buttons
* and adds eventlisteners to toggle view
*/
desktopMenuDropdowns() {
this.#dropdownButtons.forEach((button) => {
button.addEventListener("click", (event) => {
this.toggleDropdown(event, button);
});
this.closeOnBlur(button); // close menu when user clicks outside
this.handleFocus(button); // close dropdown when user finishes tabbing submenu elements
});
}
/**
* Toggles desktop dropdowns
*
* @param {EventTarget} event
* @param {HTMLButtonElement} button
*/
toggleDropdown(event, button) {
this.toggleAriaExpanded(event, button);
}
/**
* Toggles mobile menu
*
* @param {EventTarget} event
* @param {HTMLButtonElement} button
*/
toggleMobileMenu(event, button) {
this.toggleAriaExpanded(event, button);
const isExpanded = button.getAttribute("aria-expanded") === "true";
if (!isExpanded) {
// Reset sliding navigation when menu is closed
if (this.#slidingViewportEnabled) {
this.resetSlidingNavigation();
}
}
}
/**
* Toggles aria-expanded attribute
*
* @param {EventTarget} event
* @param {HTMLButtonElement} button
*/
toggleAriaExpanded(event, button) {
let isExpanded = event.currentTarget.getAttribute("aria-expanded") === "true"; // true is returned as string
// ... but open the targeted secondary nav
if (!isExpanded) {
// close all dropdowns...
this.closeAllDropDowns();
// then toggle targeted dropdown...
button.setAttribute("aria-expanded", true);
} else {
button.setAttribute("aria-expanded", false);
}
}
/**
* Close dropdown when user clicks anywhere else on the screen
*
* @param {HTMLButtonElement} button
* @link {https://developer.mozilla.org/en-US/docs/Web/API/Element/blur_event}
* @link {https://developer.mozilla.org/en-US/docs/Web/API/FocusEvent/relatedTarget}
*/
closeOnBlur(button, args) {
if (args === undefined || args?.navType === "desktop") {
button.addEventListener("blur", (event) => {
if (event.relatedTarget == null) {
event.currentTarget.setAttribute("aria-expanded", false);
}
}, { passive: true });
}
//FIXME: Remove hardcoded literal
if (args?.navType === "mobile") {
document.getElementById("menu-container").addEventListener("blur", (event) => {
// Note: cannot group negation -1*-1*-1 != -(1*1*1)
const isNull = event.relatedTarget == null;
const isNotMenuItem = !isNull && !event.relatedTarget.classList.contains("menu-vdi__toggle")
&& !event.relatedTarget.classList.contains("sub-menu-item")
&& !event.relatedTarget.classList.contains("menu-vdi__link")
&& !event.relatedTarget.classList.contains("menu-vdi__back"); // Don't close on back button click
if (isNull || isNotMenuItem)
button.setAttribute("aria-expanded", false);
}, true)
}
}
/**
* Handles escape behaviour
* Closes all dropdown when user hits
* escape key
*
* @link {https://developer.mozilla.org/en-US/docs/Web/API/Element/keyup_event}
*/
handleEscapeKey() {
window.addEventListener("keyup", (event) => {
if (event.key === "Escape") {
this.#mobileMenuButton.setAttribute("aria-expanded", false);
this.closeAllDropDowns();
// Reset sliding navigation on escape
if (this.#slidingViewportEnabled) {
this.resetSlidingNavigation();
}
}
}, { passive: true });
}
/**
* Close all dropdown menus
* Sets aria expanded property by looping
* over the list of dropdown button elements
*
* @link {https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-expanded}
*/
closeAllDropDowns() {
this.#dropdownButtons.forEach((button) => {
button.setAttribute("aria-expanded", false);
});
}
/**
* Handle focus
* Watches blur event on submenu list container, if the next
* focus element is not a submenu list item, closes the
* dropdown.
*
* Implemented for WCAG 2.2 compliance
*
* @param {htmlButtonElement} button
*/
handleFocus(button) {
const subMenuListElement = button.closest("ul");
/**
* Ducking JavaScript, I am not sure why the blur event on submenu(ul)
* would bubble up to the button since the button and sub menu list element
* are siblings and not nested. This is a mystery to me.
*
* Here we are stopping any bubbling event that may be propagated to the
* top level buttons. This includes the bubbling event from sub menu list element.
*
* If anyone finds a better solution to this or can explain bubbling of focus event
* to next sibling, you time and effort would be much appreciated.
*/
button.addEventListener("focusout", (event) => {
event.stopImmediatePropagation();
})
subMenuListElement.addEventListener("focusout", (event) => {
// blur event triggers when user clicks outside
const nextFocusElement = event.relatedTarget;
let isSubMenuElement;
if (nextFocusElement !== null) {
isSubMenuElement = nextFocusElement.classList.contains(this.#subMenuElementIdentifier);
} else {
isSubMenuElement = false; // close when user clicks outside
}
if (!isSubMenuElement) {
this.closeAllDropDowns();
}
});
}
/**
* Initialize sliding viewport navigation
* Detects if sliding viewport should be enabled and sets up the structure
*/
initializeSlidingViewport() {
// Check if we should enable sliding viewport (could be based on screen size, user preference, etc.)
this.#slidingViewportEnabled = this.shouldEnableSlidingViewport();
if (this.#slidingViewportEnabled) {
console.log('Sliding viewport enabled, setting up structure');
this.setupSlidingViewportStructure();
} else {
console.log('Sliding viewport not enabled');
}
}
/**
* Determine if sliding viewport should be enabled
* @returns {boolean}
*/
shouldEnableSlidingViewport() {
// Check if sliding viewport styles are loaded by testing if the CSS rule exists
// This allows CSS-based switching between navigation styles
const isMobile = window.innerWidth <= 1000; // 62.5rem converted to px
if (!isMobile) return false;
// Check if sliding viewport CSS is loaded by testing a specific rule
// We need to test the element within the proper context (.nav-main)
try {
const navMain = document.querySelector('.nav-main');
if (!navMain) return false;
const testElement = document.createElement('div');
testElement.className = 'menu-vdi--sliding';
testElement.style.position = 'absolute';
testElement.style.visibility = 'hidden';
testElement.style.height = '1px';
testElement.style.width = '1px';
navMain.appendChild(testElement);
// Get computed styles to check if sliding viewport CSS is active
const computedStyle = window.getComputedStyle(testElement);
const hasOverflowHidden = computedStyle.overflow === 'hidden';
navMain.removeChild(testElement);
return hasOverflowHidden;
} catch (error) {
console.warn('Error detecting sliding viewport styles:', error);
return false;
}
}
/**
* Setup the HTML structure for sliding viewport navigation
*/
setupSlidingViewportStructure() {
const menuContainer = document.getElementById('menu-container');
if (!menuContainer) {
console.warn('Menu container not found');
return;
}
// Don't set up if already configured
if (menuContainer.classList.contains('menu-vdi--sliding')) {
console.log('Sliding viewport already configured');
return;
}
console.log('Setting up sliding viewport structure');
// Add sliding class to enable sliding styles
menuContainer.classList.add('menu-vdi--sliding');
// Create viewport container
const viewport = document.createElement('div');
viewport.className = 'menu-vdi__viewport';
viewport.setAttribute('data-current-level', '0');
// Create main level
const mainLevel = document.createElement('div');
mainLevel.className = 'menu-vdi__level menu-vdi__level--main';
mainLevel.setAttribute('data-level', '0');
// Move existing menu items to main level
const existingItems = Array.from(menuContainer.children);
console.log('Moving', existingItems.length, 'existing items to main level');
existingItems.forEach(item => {
mainLevel.appendChild(item);
});
// Setup click handlers for parent items in sliding mode
this.setupSlidingClickHandlers(mainLevel);
viewport.appendChild(mainLevel);
menuContainer.appendChild(viewport);
console.log('Sliding viewport structure complete');
}
/**
* Setup click handlers for parent menu items in sliding mode
* @param {HTMLElement} level
*/
setupSlidingClickHandlers(level) {
const parentButtons = level.querySelectorAll('.menu-vdi__toggle');
parentButtons.forEach(button => {
button.addEventListener('click', (event) => {
if (this.#slidingViewportEnabled) {
event.preventDefault();
event.stopPropagation();
const parentItem = button.closest('.menu-vdi__item--parent');
const submenu = parentItem.querySelector('.menu-vdi__submenu');
if (submenu) {
this.navigateToLevel(button.textContent.trim(), submenu);
}
}
}, true); // Use capture phase to intercept before other handlers
});
}
/**
* Navigate to a specific navigation level
* @param {string} levelTitle
* @param {HTMLElement} submenuElement
*/
navigateToLevel(levelTitle, submenuElement) {
const viewport = document.querySelector('.menu-vdi__viewport');
if (!viewport) return;
this.#currentLevel++;
this.#navigationStack.push({ title: levelTitle, level: this.#currentLevel });
// Create new level
const newLevel = document.createElement('div');
newLevel.className = 'menu-vdi__level';
newLevel.setAttribute('data-level', this.#currentLevel);
// Add back button
const backButton = this.createBackButton();
newLevel.appendChild(backButton);
// Add level title
const levelTitleEl = document.createElement('div');
levelTitleEl.className = 'menu-vdi__level-title';
levelTitleEl.textContent = levelTitle;
newLevel.appendChild(levelTitleEl);
// Clone and add submenu items
const submenuItems = submenuElement.cloneNode(true);
submenuItems.className = 'menu-vdi__level-items';
// Convert submenu items to level items and ensure proper visibility
const items = submenuItems.querySelectorAll('.menu-vdi__item');
items.forEach(item => {
// Remove any nested classes that don't apply to sliding mode
item.classList.remove('menu-vdi__item--child', 'menu-vdi__item--grandchild');
// Handle nested items if they exist
const nestedToggle = item.querySelector('.menu-vdi__toggle');
if (nestedToggle) {
nestedToggle.addEventListener('click', (event) => {
event.preventDefault();
// Could implement deeper nesting here if needed
});
}
});
// Remove any hidden submenu classes from nested elements to ensure visibility
const nestedSubmenus = submenuItems.querySelectorAll('.menu-vdi__submenu');
nestedSubmenus.forEach(nestedSubmenu => {
nestedSubmenu.classList.remove('menu-vdi__submenu');
nestedSubmenu.classList.add('menu-vdi__nested-items');
});
// Ensure all list items are visible in sliding mode
const allListItems = submenuItems.querySelectorAll('li');
allListItems.forEach(li => {
if (!li.classList.contains('menu-vdi__item')) {
li.classList.add('menu-vdi__item');
}
});
newLevel.appendChild(submenuItems);
viewport.appendChild(newLevel);
// Animate to new level
this.animateToLevel(this.#currentLevel);
}
/**
* Create a back button for navigation levels
* @returns {HTMLElement}
*/
createBackButton() {
const backButton = document.createElement('button');
backButton.className = 'menu-vdi__back';
backButton.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 19.5L8.25 12l7.5-7.5" />
</svg>
Back
`;
backButton.addEventListener('click', (event) => {
event.preventDefault();
event.stopPropagation();
this.navigateBack();
});
return backButton;
}
/**
* Navigate back to previous level
*/
navigateBack() {
if (this.#currentLevel > 0) {
this.#currentLevel--;
this.#navigationStack.pop();
// Remove the current level element
const viewport = document.querySelector('.menu-vdi__viewport');
const currentLevelEl = viewport.querySelector(`[data-level="${this.#currentLevel + 1}"]`);
if (currentLevelEl) {
currentLevelEl.remove();
}
// Animate back to previous level
this.animateToLevel(this.#currentLevel);
// Ensure menu stays open after back navigation
setTimeout(() => {
if (this.#mobileMenuButton.getAttribute("aria-expanded") === "false") {
this.#mobileMenuButton.setAttribute("aria-expanded", "true");
}
}, 50);
}
}
/**
* Animate viewport to specific level
* @param {number} level
*/
animateToLevel(level) {
const viewport = document.querySelector('.menu-vdi__viewport');
if (!viewport) return;
const translateX = -level * 100;
viewport.style.transform = `translateX(${translateX}%)`;
viewport.setAttribute('data-current-level', level);
}
/**
* Reset sliding navigation to main level
*/
resetSlidingNavigation() {
const menuContainer = document.getElementById('menu-container');
if (!menuContainer) return;
if (this.#slidingViewportEnabled) {
this.#currentLevel = 0;
this.#navigationStack = [];
const viewport = document.querySelector('.menu-vdi__viewport');
if (viewport) {
// Remove all levels except main
const levels = viewport.querySelectorAll('.menu-vdi__level:not(.menu-vdi__level--main)');
levels.forEach(level => level.remove());
// Reset position
this.animateToLevel(0);
}
} else {
// Clean up sliding structure and restore normal menu
this.cleanupSlidingStructure();
}
}
/**
* Clean up sliding viewport structure and restore normal menu
*/
cleanupSlidingStructure() {
const menuContainer = document.getElementById('menu-container');
if (!menuContainer) return;
// Remove sliding class
menuContainer.classList.remove('menu-vdi--sliding');
// Find viewport and move items back to container
const viewport = menuContainer.querySelector('.menu-vdi__viewport');
if (viewport) {
const mainLevel = viewport.querySelector('.menu-vdi__level--main');
if (mainLevel) {
// Move all items back to menu container
const items = Array.from(mainLevel.children);
items.forEach(item => {
menuContainer.appendChild(item);
});
}
// Remove viewport structure
viewport.remove();
}
console.log('Sliding structure cleaned up');
}
}
export default Navigation;
+39
View File
@@ -0,0 +1,39 @@
/**
* Tags external links in the document with appropriate attributes and styling.
*
* This function identifies all anchor elements with href attributes and determines
* if they point to external domains. External links are enhanced with:
* - Accessibility label indicating they open in a new tab
* - target="_blank" to open in new tab
* - rel="noopener noreferrer" for security
* - Custom CSS class "extLink" for styling
*
* Links are considered external if their host differs from the current page's host.
* Malformed URLs are silently ignored.
*
* @function tagExternalLinks
* @returns {void}
*/
function tagExternalLinks() {
const currentHost = window.location.host;
document.querySelectorAll('a[href]').forEach(link => {
try {
const url = new URL(link.href, window.location.href);
// If the link's host is different from the current host, treat as external
if (url.host !== currentHost) {
link.setAttribute('aria-label', 'External link, opens in a new tab');
link.setAttribute('target', '_blank');
link.setAttribute('rel', 'noopener noreferrer'); // Security best practice
link.classList.add('extLink'); // Add a custom class for icons or other styling
}
} catch (e) {
// Ignore malformed URLs
}
});
}
export default tagExternalLinks;
+76
View File
@@ -0,0 +1,76 @@
/**
* Theme JS
*/
import { registerButtonComponent } from './components/button.js';
import { registerBackToTopButton } from './components/backToTop.js';
import GetHeaderHeight from './modules/GetHeaderHeight.js';
import tagExternalLinks from './modules/TagExternalLinks.js';
import Navigation from './modules/Navigation.js';
// Add passive event listeners
! function (e) {
"function" == typeof define && define.amd ? define(e) : e()
}(function () {
let e;
const t = ["scroll", "wheel", "touchstart", "touchmove", "touchenter", "touchend", "touchleave", "mouseout", "mouseleave", "mouseup", "mousedown", "mousemove", "mouseenter", "mousewheel", "mouseover"];
if (function () {
let e = !1;
try {
const t = Object.defineProperty({}, "passive", {
get: function () {
e = !0
}
});
window.addEventListener("test", null, t);
window.removeEventListener("test", null, t);
} catch (e) { }
return e
}()) {
const n = EventTarget.prototype.addEventListener;
e = n;
EventTarget.prototype.addEventListener = function (n, o, r) {
let i;
const s = "object" == typeof r && null !== r,
u = s ? r.capture : r;
if (s) {
const t = Object.getOwnPropertyDescriptor(r, "passive");
r = t && !0 !== t.writable && void 0 === t.set ? { ...r } : r;
} else {
r = {};
}
r.passive = void 0 !== (i = r.passive) ? i : -1 !== t.indexOf(n) && !0;
r.capture = void 0 !== u && u;
e.call(this, n, o, r);
};
EventTarget.prototype.addEventListener._original = e
}
});
/**
* Application entrypoint
*/
document.addEventListener('DOMContentLoaded', () => {
// Tag external links
tagExternalLinks();
// Register button component
registerButtonComponent();
registerBackToTopButton();
// Initialize Navigation
const navigation = new Navigation('navMainToggle', '.menu-vdi__toggle');
// Initialize Navigation
navigation.desktopMenuDropdowns();
navigation.mobileMenuToggle();
// Initialize Header Height
GetHeaderHeight();
// Add Back to Top button to body
const backToTop = document.createElement('back-to-top');
document.body.appendChild(backToTop);
});
console.log(`theme.js loaded.`);
+7
View File
@@ -0,0 +1,7 @@
/*
* Theme Name:VDI Starter v5
* Description: Custom WordPress theme starter for VDI Projects
* Version: 5.0
* Author: Vincent Design Inc.
* Text Domain: basicwp
*/
+2
View File
@@ -0,0 +1,2 @@
/* Light admin styles; avoid overriding core wp-admin UI */
+55
View File
@@ -0,0 +1,55 @@
/* Theme editor styles */
@import "../../static/dist/theme.css";
:root {
--vdi-editor-gutter: clamp(1rem, 4vw, 3rem);
}
body {
font-family: var(--font-sans);
h1, h2, h3,
h4, h5, h6 {
font-family: var(--font-headings);
font-weight: 700;
margin: 0 0 1rem;
}
}
.editor-styles-wrapper.is-root-container,
.editor-styles-wrapper .block-editor-block-list__layout.is-root-container,
.editor-styles-wrapper .wp-block-post-content,
.editor-visual-editor__post-title-wrapper {
box-sizing: border-box;
margin-inline: auto;
max-width: min(100%, var(--wp--style--global--wide-size, 1536px));
padding-inline: var(--vdi-editor-gutter);
width: 100%;
}
.editor-styles-wrapper .alignfull,
.editor-styles-wrapper .mx-break-out {
margin-left: calc(var(--vdi-editor-gutter) * -1);
margin-right: calc(var(--vdi-editor-gutter) * -1);
max-width: none;
width: calc(100% + (var(--vdi-editor-gutter) * 2));
}
.editor-styles-wrapper .ml-break-out {
margin-left: calc(var(--vdi-editor-gutter) * -1);
max-width: none;
width: calc(100% + var(--vdi-editor-gutter));
}
.editor-styles-wrapper .mr-break-out {
margin-left: 0;
margin-right: calc(var(--vdi-editor-gutter) * -1);
max-width: none;
width: calc(100% + var(--vdi-editor-gutter));
}
.wp-block-buttons .block-editor-block-list__layout {
display: flex;
gap: 1rem;
}
+24
View File
@@ -0,0 +1,24 @@
@theme {
/** Break-out variables
* These are used for the break-out plugin and the responsive utilities.
* The break-out variables are set to match the default plugin settings.
* You can override them if you need to adjust for a particular use case.
*/
--twcb-scrollbar-width: 0px;
}
@utility mx-break-out {
margin-left: calc(50% - 50vw);
margin-right: calc(50% - 50vw);
width: calc(100vw - var(--twcb-scrollbar-width));
}
@utility ml-break-out {
margin-left: calc(50% - 50vw);
width: 100%;
}
@utility mr-break-out {
margin-left: calc(-50% + 50vw);
width: 100%;
}
+41
View File
@@ -0,0 +1,41 @@
/* Theme color definitions */
@theme {
--color-black: oklch(0% 0 0);
--color-white: oklch(100% 0 0);
--color-background: oklch(89.75% 0 0);
--color-text: var(--color-black);
--color-primary: oklch(0.57 0.203362 257.1706);
--color-primary-100: color-mix(in oklch, var(--color-primary) 10%, white);
--color-primary-200: color-mix(in oklch, var(--color-primary) 20%, white);
--color-primary-300: color-mix(in oklch, var(--color-primary) 30%, white);
--color-primary-400: color-mix(in oklch, var(--color-primary) 40%, white);
--color-primary-500: color-mix(in oklch, var(--color-primary) 50%, white);
--color-primary-600: color-mix(in oklch, var(--color-primary) 60%, white);
--color-primary-700: color-mix(in oklch, var(--color-primary) 70%, white);
--color-primary-800: color-mix(in oklch, var(--color-primary) 80%, white);
--color-primary-900: color-mix(in oklch, var(--color-primary) 90%, white);
--color-secondary: oklch(0.56 0.0176 257.23);
--color-secondary-100: color-mix(in oklch, var(--color-secondary) 10%, white);
--color-secondary-200: color-mix(in oklch, var(--color-secondary) 20%, white);
--color-secondary-300: color-mix(in oklch, var(--color-secondary) 30%, white);
--color-secondary-400: color-mix(in oklch, var(--color-secondary) 40%, white);
--color-secondary-500: color-mix(in oklch, var(--color-secondary) 50%, white);
--color-secondary-600: color-mix(in oklch, var(--color-secondary) 60%, white);
--color-secondary-700: color-mix(in oklch, var(--color-secondary) 70%, white);
--color-secondary-800: color-mix(in oklch, var(--color-secondary) 80%, white);
--color-secondary-900: color-mix(in oklch, var(--color-secondary) 90%, white);
--color-bodylinks: oklch(0.48 0.0789 211.58);
--color-footlinks: oklch(0.65 0.1104 212.2);
--color-success: oklch(64.01% 0.1751 146.7);
--color-info: oklch(0.55 0.0922 211.57);
--color-warning: oklch(84.42% 0.1722 84.93);
--color-danger: oklch(0.5126 0.1865 22.61);
--color-light: oklch(98.16% 0.0017 247.8);
--color-dark: oklch(34.51% 0.0133 248.2);
}
+67
View File
@@ -0,0 +1,67 @@
/* Forms */
/* Base styles */
input[type="text"], input[type="email"], input[type="tel"],
input[type="url"], input[type="number"], input[type="password"],
input[type="date"], select, textarea {
@apply px-4 py-2 w-full rounded border-2 border-primary;
@apply text-black bg-white;
@apply focus-visible:border-transparent focus-visible:outline-2 focus-visible:outline-offset-[3px] focus-visible:outline-primary;
font-size: inherit;
line-height: inherit;
}
/* Gravity Forms styles */
.gform_wrapper {
@apply max-w-full mx-auto;
.gform_fields { @apply text-black; }
fieldset.gfield { @apply mb-6; }
legend, label { @apply text-lg font-bold mb-2; }
label.gform-field-label--type-sub{ @apply font-normal text-base; }
.gfield_required { @apply text-danger text-xs font-bold ml-1; }
.dark {
input[type="text"], input[type="email"], input[type="tel"],
input[type="url"], input[type="number"], input[type="password"],
input[type="date"], select, textarea {
@apply text-black bg-white;
@apply focus-visible:border-transparent focus-visible:outline-2 focus-visible:outline-offset-[3px] focus-visible:outline-primary;
}
}
.ginput_complex {
@apply sm:flex;
span {
@apply block grow;
&:not(:first-child) { @apply mt-6 sm:mt-0 sm:ml-4; }
}
}
.gform_footer { @apply mt-8; }
h2.gform_submission_error { @apply text-xl text-danger font-bold my-4; }
.validation_message { @apply italic text-danger; }
.hidden_label > label { @apply hidden; }
}
/* Search block styles */
.wp-block-search__input {
@apply px-4 py-2 w-full rounded border-2 border-transparent focus-visible:bg-secondary-300 focus-visible:border-primary;
appearance: none;
flex-grow: 1;
margin-left: 0;
margin-right: 0;
min-width: 3rem;
text-decoration: unset !important;
}
+85
View File
@@ -0,0 +1,85 @@
/* Miscellaneous custom styles */
@theme {
--spacing-menu-top: calc(100% + .9375rem);
--spacing-section: 2rem;
--shadow-menu-shadow: 0 .25rem .375rem rgba(0,0,0,0.1);
/** Breakpoints
* The breakpoints are set to match the default Tailwind breakpoints.
* You can override them here if you want to use different breakpoints.
*
* @see https://tailwindcss.com/docs/breakpoints
*/
--breakpoint-*: initial;
--breakpoint-xxs: 22.5rem; /* 360px */
--breakpoint-xs: 29.6875rem; /* 475px */
--breakpoint-sm: 40rem; /* 640px */
--breakpoint-md: 48rem; /* 768px */
--breakpoint-lg: 64rem; /* 1024px */
--breakpoint-xl: 70rem; /* 1280px */
--breakpoint-2xl: 96rem; /* 1536px */
}
/* Basic layout styles */
main#maincontent {
background-color: var(--color-background);
color: var(--color-text);
margin: 0;
padding: 0 0 4rem;
}
.container {
margin: 0 auto;
width: 100%;
padding-inline: clamp(1.5rem, 5vw, 3rem);
}
.section {
@apply relative my-section px-section;
&:first-child {
@apply mt-0;
}
&:last-child, p:last-child {
@apply mb-0;
}
&.has-background {
@apply py-section bg-cover bg-no-repeat;
}
}
/** Allows containers inside containers
*
* .container .wp-block-section {
* @apply mx-break-out;
* }
*/
.content-wrapper {
.alignfull {
@apply max-w-full;
}
.alignwide {
@apply max-w-full;
}
.alignleft {
@apply ml-0 mr-auto float-none;
}
.alignright {
@apply ml-auto mr-0 float-none;
}
.aligncenter {
@apply mx-auto;
}
}
/* Responsive embeds */
.embed { position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden; }
.embed iframe, .embed object, .embed embed, .embed video { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
+9
View File
@@ -0,0 +1,9 @@
/* Theme base styles */
@import "./global.css";
@import "./colors.css";
@import "./prose.css";
@import "./typography.css";
@import "./skip-link.css";
@import './misc.css';
@import "./forms.css";
+1
View File
@@ -0,0 +1 @@
/* Miscellaneous extra styles */
+36
View File
@@ -0,0 +1,36 @@
/* Theme prose styles */
@theme {
--tw-prose-body: var(--color-primary);
--tw-prose-headings: var(--color-primary);
--tw-prose-lead: var(--color-primary);
--tw-prose-links: var(--color-info);
--tw-prose-bold: var(--color-primary);
--tw-prose-counters: var(--color-primary);
--tw-prose-bullets: var(--color-secondary);
--tw-prose-hr: var(--color-secondary);
--tw-prose-quotes: var(--color-primary);
--tw-prose-quote-borders: var(--color-primary);
--tw-prose-captions: var(--color-secondary);
--tw-prose-code: var(--color-primary);
--tw-prose-pre-code: var(--color-primary);
--tw-prose-pre-bg: var(--color-secondary);
--tw-prose-th-borders: var(--color-secondary);
--tw-prose-td-borders: var(--color-secondary);
--tw-prose-invert-body: var(--color-primary);
--tw-prose-invert-headings: var(--color-primary);
--tw-prose-invert-lead: var(--color-primary);
--tw-prose-invert-links: var(--color-secondary);
--tw-prose-invert-bold: var(--color-primary);
--tw-prose-invert-counters: var(--color-primary);
--tw-prose-invert-bullets: var(--color-primary);
--tw-prose-invert-hr: var(--color-secondary);
--tw-prose-invert-quotes: var(--color-primary);
--tw-prose-invert-quote-borders: var(--color-primary);
--tw-prose-invert-captions: var(--color-primary);
--tw-prose-invert-code: var(--color-secondary);
--tw-prose-invert-pre-code: var(--color-primary);
--tw-prose-invert-pre-bg: oklch(0% 0 0 / 50%);
--tw-prose-invert-th-borders: var(--color-primary);
--tw-prose-invert-td-borders: var(--color-primary);
}
+25
View File
@@ -0,0 +1,25 @@
.skip-link {
background-color: #f6ff00 !important;
border-color: #f6ff00 !important;
color: #000 !important;
font-size: larger;
font-weight: 600;
left: 0;
margin: 0 auto;
max-width: 90vw;
opacity: 1;
outline-color: #f6ff00 !important;
padding: 1rem;
position: absolute;
right: 0;
text-align: center;
top: 0.5rem;
transition: transform 0.1875s ease-out;
width: 15rem;
z-index: 999;
&:not(:focus):not(:hover) {
opacity: 0;
transform: translateY(-4em);
}
}
+174
View File
@@ -0,0 +1,174 @@
/* Basic typographical styles */
/**
* All font sizes are based on 16px base font size and 1920px wide screen
* Default size is expressed as percentage of screen width.
* text-14px: 12px-27px, default: 14px
* text-16px: 14px-28px, default: 16px
* text-18px: 14px-30px, default: 18px
* text-20px: 16px-32px, default: 20px
* text-22px: 17px-33px, default: 22px
* text-25px: 18px-35px, default: 25px
* text-30px: 19px-37px, default: 30px
* text-35px: 20px-40px, default: 35px
* text-38px: 22px-48px, default: 38px
* text-40px: 24px-56px, default: 40px
* text-45px: 25px-64px, default: 45px
* text-50px: 27px-72px, default: 50px
* text-55px: 28px-76px, default: 55px
* text-60px: 30px-80px, default: 60px
* text-70px: 30px-76px, default: 70px
* text-75px: 32px-80px, default: 75px
*
* Font sizes at standard viewport widths:
* | 360px | 640px | 768px | 1024px | 1280px | 1440px | 1920px
* |-------|-------|-------|--------|--------|--------|-------
* text-14px | 12.00 | 12.36 | 12.52 | 12.85 | 13.18 | 13.38 | 14.00
* text-16px | 14.00 | 14.36 | 14.52 | 14.85 | 15.18 | 15.38 | 16.00
* text-18px | 14.00 | 14.72 | 15.05 | 15.70 | 16.36 | 16.77 | 18.00
* text-20px | 16.00 | 16.72 | 17.05 | 17.70 | 18.36 | 18.77 | 20.00
* text-22px | 17.60 | 18.36 | 18.75 | 19.47 | 20.19 | 20.65 | 22.00
* text-25px | 18.00 | 19.26 | 19.83 | 20.98 | 22.13 | 22.85 | 25.00
* text-30px | 18.96 | 20.89 | 21.85 | 23.66 | 25.47 | 26.60 | 30.00
* text-35px | 20.00 | 22.69 | 23.92 | 26.38 | 28.85 | 30.38 | 35.00
* text-38px | 22.40 | 24.85 | 26.48 | 29.04 | 31.60 | 33.20 | 38.00
* text-40px | 24.00 | 26.87 | 28.18 | 30.81 | 33.44 | 35.08 | 40.00
* text-45px | 25.60 | 29.22 | 30.67 | 33.86 | 37.04 | 39.03 | 45.00
* text-50px | 27.20 | 31.58 | 33.16 | 36.90 | 40.65 | 42.98 | 50.00
* text-70px | 30.40 | 37.01 | 40.76 | 47.26 | 53.75 | 57.82 | 70.00
* text-75px | 32.00 | 39.46 | 43.25 | 50.30 | 57.36 | 61.77 | 75.00
*/
@theme {
--font-sans: "Raleway", sans-serif;
--line-height: 1.6;
--text-base: 1rem;
--text-14px: clamp(0.75rem, calc(0.7212rem + 0.1282vw), 0.875rem);
--text-16px: clamp(0.875rem, calc(0.8462rem + 0.1282vw), 1rem);
--text-18px: clamp(0.875rem, calc(0.8173rem + 0.2564vw), 1.125rem);
--text-20px: clamp(1rem, calc(0.9423rem + 0.2564vw), 1.25rem);
--text-22px: clamp(1.1rem, calc(1.0365rem + 0.2821vw), 1.375rem);
--text-25px: clamp(1.125rem, calc(1.024rem + 0.4487vw), 1.5625rem);
--text-30px: clamp(1.185rem, calc(1.0258rem + 0.7077vw), 1.875rem);
--text-35px: clamp(1.25rem, calc(1.0337rem + 0.9615vw), 2.1875rem);
--text-38px: clamp(1.4rem, calc(1.175rem + 1vw), 2.375rem);
--text-40px: clamp(1.5rem, calc(1.2692rem + 1.0256vw), 2.5rem);
--text-45px: clamp(1.6rem, calc(1.3202rem + 1.2436vw), 2.8125rem);
--text-50px: clamp(1.7rem, calc(1.3712rem + 1.4615vw), 3.125rem);
--text-70px: clamp(1.9rem, calc(1.3288rem + 2.5385vw), 4.375rem);
--text-75px: clamp(2rem, calc(1.3798rem + 2.7564vw), 4.6875rem);
--h1: calc(var(--text-base) * 2.25);
--h2: calc(var(--text-base) * 1.75);
--h3: calc(var(--text-base) * 1.5);
--h4: calc(var(--text-base) * 1.25);
--h5: calc(var(--text-base) * 1.125);
--h6: calc(var(--text-base) * 1.05);
}
body {
background-color: white;
color: black;
font-family: var(--font-sans);
font-size: var(--text-base);
line-height: var(--line-height);
}
::selection { background: var(--color-warning); }
@layer components {
h1, h2, h3,
h4, h5, h6 {
font-family: var(--font-headings);
font-weight: 700;
margin: 0 0 1rem;
}
h1, .h1 {
font-size: var(--h1);
line-height: 1.2;
}
h2, .h2 {
font-size: var(--h2);
line-height: 1.3;
}
h3, .h3 {
font-size: var(--h3);
line-height: 1.4;
}
h4, .h4 {
font-size: var(--h4);
line-height: 1.5;
}
h5, .h5 { font-size: var(--h5); }
h6, .h6 { font-size: var(--h6); }
}
a, .link {
color: var(--color-bodylinks);
text-decoration: none;
transition: color 200ms;
cursor: pointer;
&:hover { color: var(--color-primary); }
}
h1 a, .h1 a,
h2 a, .h2 a,
h3 a, .h3 a {
color: inherit;
text-decoration: underline;
}
p {
margin-top: 0;
margin-bottom: 1rem;
}
ul { list-style-type: disc; }
ol { list-style-type: decimal; }
li ul, li ol { margin: 0 1rem; }
ol ol { list-style: lower-alpha; }
ol ol ol { list-style: lower-roman; }
ol ol ol ol { list-style: lower-alpha; }
pre, code,
samp, style { font-family: monospace; }
pre {
font-size: 0.875rem;
overflow: auto;
padding: 1.5rem;
}
pre code {
background-color: inherit;
border-radius: 0;
color: inherit;
padding: 0;
}
code {
@apply bg-black/30 px-[3px] py-0.5 font-mono text-black text-xs rounded-sm;
}
hr {
background-color: black;
border: none;
display: block;
height: 1px;
margin: 1rem 0;
width: 100%;
}
+176
View File
@@ -0,0 +1,176 @@
/* Button styles */
@theme {
/* Configuration */
/**
* Button component settings
*
* The variables below are used to define the button styles.
* Most of these variables come from the main theme configuration.
* The following variables are not defined, but have fallback values
* in their respective locations and can be added if needed:
*
* --button-font-weight (fallback is 600/semibold)
* --button-font-size (fallback is 1rem)
* --button-outline-width (fallback is --button-border-width)
* --button-outline-style (fallback is --button-border-style)
* --button-outline-color (fallback is --button-border-color)
*/
--button-bg: var(--color-primary);
--button-color: var(--color-white);
--button-hover-bg: var(--color-info);
--button-hover-border-color: var(--color-info);
--button-hover-color: var(--color-white);
--button-border-width: 3px;
--button-border-style: solid;
--button-border-color: var(--button-bg);
--button-radius: 0.5rem;
}
.btn, .button, .acf-block-preview .button {
@apply px-8 py-2 min-w-0;
background: var(--button-bg);
color: var(--button-color);
cursor: pointer;
display: inline-block;
transition: background 200ms, border-color 200ms, color 200ms;
border-width: var(--button-border-width);
border-style: var(--button-border-style);
border-color: var(--button-border-color);
border-radius: var(--button-radius);
text-decoration: none;
font-weight: var(--button-font-weight, 600);
font-size: var(--button-font-size, 1rem);
line-height: 1.1;
&[data-button-variant="outline"] {
background: transparent;
&:hover { background: transparent; }
--button-color: var(--button-outline-color, var(--button-bg));
--button-hover-border-color: var(--button-hover-bg);
--button-hover-color: var(--button-hover-bg);
}
}
/* Hover/focus/active */
.btn:hover, .button:hover, .acf-block-preview .button:hover {
background: var(--button-hover-bg);
border-color: var(--button-hover-border-color);
color: var(--button-hover-color);
}
.btn:focus, .button:focus {
outline-width: var(--button-outline-width, var(--button-border-width));
outline-style: var(--button-outline-style, var(--button-border-style));
outline-color: var(--button-outline-color, var(--button-border-color));
outline-offset: calc(var(--button-border-width) * 2);
}
.btn:active, .button:active { transform: scale(99%); }
/* Back To Top Button */
#backToTopBtn {
display:none;
position:fixed;
bottom:2rem;
right:2rem;
z-index:1000;
padding:0.75em 1.5em;
font-size:1.1rem;
border-radius:2em;
background:var(--color-primary,#3857BC);
color:#fff;
border:none;
box-shadow:0 2px 8px rgba(0,0,0,0.15);
cursor:pointer;
transition:opacity 0.2s;
}
/**
* Variants
*
* The following styles are used to define button variants.
* These styles are applied to the button element using the
* `data-button-*` attributes.
*/
/* Sizes */
.btn[data-button-size="small"], .button[data-button-size="small"] { @apply px-4 py-0.5; }
.btn[data-button-size="medium"], .button[data-button-size="medium"] { @apply px-8 py-2; }
.btn[data-button-size="large"], .button[data-button-size="large"] { @apply px-12 py-3; }
/* Width */
.btn[data-button-width="auto"], .button[data-button-width="auto"] { @apply min-w-0; }
.btn[data-button-width="small"], .button[data-button-width="small"] { @apply px-2; }
.btn[data-button-width="wide"], .button[data-button-width="wide"] { @apply sm:min-w-[20rem]; }
.button[data-button-width="full"],
x-button:has(.button[data-button-width="full"]) { @apply w-full; }
/**
* Colors
*
* We don't need to speicifcally target "primary" buttons
* as the button styling defaults to "primary".
* However, you can, if necessary, by adding a style block
* like the ones below and changing the color values.
*/
.btn[data-button-color="secondary"], .button[data-button-color="secondary"] {
--button-bg: var(--color-secondary);
--button-color: var(--color-white);
--button-border-color: var(--color-secondary);
--button-outline-color: oklch(0.48 0.0136 252.2);
--button-hover-bg: var(--color-dark);
--button-hover-border-color: var(--color-dark);
--button-hover-color: var(--color-white);
}
.btn[data-button-color="light"], .button[data-button-color="light"] {
--button-bg: var(--color-white);
--button-color: var(--color-dark);
--button-border-color: var(--color-white);
--button-hover-bg: var(--color-dark);
--button-hover-border-color: var(--color-dark);
--button-hover-color: var(--color-white);
}
.btn[data-button-color="white"], .button[data-button-color="white"] {
--button-bg: var(--color-white);
--button-color: var(--color-black);
--button-border-color: var(--color-white);
--button-hover-bg: var(--color-secondary-200);
--button-hover-border-color: var(--color-secondary-200);
--button-hover-color: var(--color-black);
}
.btn[data-button-color="black"], .button[data-button-color="black"] {
--button-bg: var(--color-black);
--button-color: var(--color-white);
--button-border-color: var(--color-black);
--button-hover-bg: var(--color-secondary);
--button-hover-border-color: var(--color-secondary);
--button-hover-color: var(--color-dark);
}
.back-to-top {
background: var(--color-primary, #3857BC);
color: #fff;
border: none;
border-radius: 2em;
padding: 0.75em 1.5em;
font-size: 1.1rem;
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
cursor: pointer;
transition: opacity 0.2s, background 0.2s;
opacity: 0.85;
}
.back-to-top:hover, .back-to-top:focus {
background: var(--color-info, #233a7a);
opacity: 1;
outline: 2px solid var(--color-info, #233a7a);
}
+5
View File
@@ -0,0 +1,5 @@
.alignfull {
margin-left: calc(50% - 50vw);
margin-right: calc(50% - 50vw);
width: 100vw;
}
+4
View File
@@ -0,0 +1,4 @@
/* Theme block styles */
@import './buttons.css';
@import './core.css';
+13
View File
@@ -0,0 +1,13 @@
/* Breadcrumb styles */
#breadcrumbs {
@apply text-white;
a {
color: oklch(0.72 0.122 212.25);
&:hover {
color:color-mix(in oklch, oklch(0.72 0.122 212.25) 60%, white);
}
}
}
+8
View File
@@ -0,0 +1,8 @@
/* Theme component styles */
@import "./site-header.css";
@import "./breadcrumbs.css";
@import "./post-list.css";
@import "./sidebar.css";
@import "./pagination.css";
@import "./site-footer.css";
+35
View File
@@ -0,0 +1,35 @@
/* Post index pagination styles */
.pagination {
.nav-links {
@apply hidden md:flex items-center justify-center mt-12;
.page-numbers {
@apply hidden md:flex items-center justify-center h-10 p-4 text-base font-medium transition duration-300 rounded stroke-primary text-primary hover:bg-info hover:text-light hover:stroke-info focus-visible:bg-info focus-visible:text-light focus-visible:stroke-info focus-visible:outline-none;
}
.page-numbers.current {
@apply text-light whitespace-nowrap bg-info ring-offset-2 hover:bg-primary hover:stroke-primary focus-visible:bg-info;
}
.prev, .next {
@apply flex gap-4;
}
}
& a, & span {
border: 1px solid #ddd;
border-radius: 4px;
color: #3857BC !important;
margin: 0 5px;
padding: .25rem .5rem;
text-decoration: none;
}
& .current, & a:hover {
background: #3857BC !important;
border-color: #3857BC !important;
color: #fff !important;
padding: .25rem .5rem;
}
}
+37
View File
@@ -0,0 +1,37 @@
/* Blog/post index listing styles */
.post-list {
/* Original styles, if needed
* .post-list__posts {
* }
* .post-list__h1 {
* }
* .post-list__post {
* &:hover {
* img {
* }
* }
* .post-list__title {
* }
* .post-list__details {
* }
* .post-list__byline {
* }
* .post-list__author {
* }
* .post-list__sep {
* }
* .post-list__date {
* }
* }
*/
}
/* Original styles, if needed
* .no-posts {
* .no-posts__title {
* }
* .no-posts__desc {
* }
* }
*/
+28
View File
@@ -0,0 +1,28 @@
/* Sidebar styles */
.sidebar{
.widget {
/* Widget styles */
@apply mb-8;
h3 {
/* Widget title styles */
@apply m-0 mb-2 font-semibold;
}
ul {
/* List styles */
@apply list-none m-0 p-0 pl-3 border-l-3 border-secondary-400;
li {
/* List item styles */
@apply text-balance;
&:first-of-type {
/* First list item styles */
@apply leading-none mb-1;
}
}
}
}
}
+41
View File
@@ -0,0 +1,41 @@
/* Footer styles */
.site-footer {
#footRight {
div {
@apply col-span-1 md:col-span-4 lg:col-span-1;
h3 {
@apply font-bold text-secondary-300 text-20px mb-4 pb-2 border-b border-b-secondary-300
}
a {
@apply text-footlinks hover:opacity-60;
}
}
.widget {
li {
@apply text-16px my-1 mb-2 leading-4;
}
h4 {
@apply font-bold text-18px text-secondary mb-2;
}
a {
@apply transition-colors duration-300 hover:text-success focus-visible:text-success;
}
}
.menu-footer-menu-container {
@apply grow;
}
}
.copyright {
p { @apply leading-none m-0 p-0; }
a { @apply text-white hover:text-primary-500 underline underline-offset-2; }
}
}
+22
View File
@@ -0,0 +1,22 @@
/* Header styles */
.site-header {
/* Site header styles */
.nav-aux__container {
a {
/* Link styles */
color: var(--color-primary-600);
text-decoration: none;
font-weight: 500;
transition: color 0.3s ease;
&:hover {
color: var(--color-primary-800);
}
}
#globalSearch {
@apply text-14px text-light;
}
}
}
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.
+702
View File
@@ -0,0 +1,702 @@
/*--------------------------------
Lineicons regular icon font
-------------------------------- */
@font-face {
font-family: 'Lineicons';
src: url('../../styles/fonts/Lineicons.woff2') format('woff2'),
url('../../styles/fonts/Lineicons.svg') format('svg');
}
/* base class */
[class^="lni-"], [class*=" lni-"],
[class^="icon-"], [class*=" icon-"],
[class^="lni-"]:before, [class*=" lni-"]:before,
[class^="icon-"]:before, [class*=" icon-"]:before {
font-family: "Lineicons", sans-serif;
font-style: normal;
speak: never;
display: inline-block;
text-decoration: inherit;
vertical-align: middle;
text-align: center;
/* If needed - opacity: .8; */
/* Animation center compensation - margins should be symmetric */
/* if needed - margin-left: .2rem; */
/* if needed - margin-right: .2rem; */
/* For safety - reset parent styles, that can break glyph codes*/
font-variant: normal;
text-transform: none;
font-size: 120%;
/* Font smoothing. That was taken from TWBS */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
/**
* Uncomment for 3D effect
*
* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3);
*/
}
/* Circular Icons */
.circular-icon {
@apply box-content bg-secondary rounded-full text-gray-800 inline-block;
--size: 3rem;
height: var(--size);
text-align: center;
width: var(--size);
i {
display: inline-block;
height: var(--size);
line-height: var(--size);
text-align: center;
vertical-align: middle;
width: var(--size);
}
&i::before, span { line-height: var(--size) !important; }
}
/* rotate the icon infinitely */
.lni-is-spinning { animation: lni-spin 1s infinite linear; }
@keyframes lni-spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* transform */
.lni-rotate-90 { transform: rotate(90deg); }
.lni-rotate-180 { transform: rotate(180deg); }
.lni-rotate-270 { transform: rotate(270deg); }
.lni-flip-y { transform: scaleY(-1); }
.lni-flip-x { transform: scaleX(-1); }
/* icons */
.lni-500px:before, .icon-500px:before { content: "\ea01"; }
.lni-adobe:before, .icon-adobe:before { content: "\ea02"; }
.lni-adonis:before, .icon-adonis:before { content: "\ea03"; }
.lni-aeroplane-1:before, .icon-aeroplane-1:before { content: "\ea04"; }
.lni-agenda:before, .icon-agenda:before { content: "\ea05"; }
.lni-airbnb:before, .icon-airbnb:before { content: "\ea06"; }
.lni-airtable:before, .icon-airtable:before { content: "\ea07"; }
.lni-alarm-1:before, .icon-alarm-1:before { content: "\ea08"; }
.lni-align-text-center:before, .icon-align-text-center:before { content: "\ea09"; }
.lni-align-text-left:before, .icon-align-text-left:before { content: "\ea0a"; }
.lni-align-text-right:before, .icon-align-text-right:before { content: "\ea0b"; }
.lni-alpinejs:before, .icon-alpinejs:before { content: "\ea0c"; }
.lni-amazon:before, .icon-amazon:before { content: "\ea0d"; }
.lni-amazon-original:before, .icon-amazon-original:before { content: "\ea0e"; }
.lni-amazon-pay:before, .icon-amazon-pay:before { content: "\ea0f"; }
.lni-ambulance-1:before, .icon-ambulance-1:before { content: "\ea10"; }
.lni-amd:before, .icon-amd:before { content: "\ea11"; }
.lni-amex:before, .icon-amex:before { content: "\ea12"; }
.lni-anchor:before, .icon-anchor:before { content: "\ea13"; }
.lni-android:before, .icon-android:before { content: "\ea14"; }
.lni-android-old:before, .icon-android-old:before { content: "\ea15"; }
.lni-angellist:before, .icon-angellist:before { content: "\ea16"; }
.lni-angle-double-down:before, .icon-angle-double-down:before { content: "\ea17"; }
.lni-angle-double-left:before, .icon-angle-double-left:before { content: "\ea18"; }
.lni-angle-double-right:before, .icon-angle-double-right:before { content: "\ea19"; }
.lni-angle-double-up:before, .icon-angle-double-up:before { content: "\ea1a"; }
.lni-angular:before, .icon-angular:before { content: "\ea1b"; }
.lni-app-store:before, .icon-app-store:before { content: "\ea1c"; }
.lni-apple-brand:before, .icon-apple-brand:before { content: "\ea1d"; }
.lni-apple-music:before, .icon-apple-music:before { content: "\ea1e"; }
.lni-apple-music-alt:before, .icon-apple-music-alt:before { content: "\ea1f"; }
.lni-apple:before, .icon-apple:before { content: "\ea1d"; }
.lni-apple-music:before, .icon-apple-music:before { content: "\ea1e"; }
.lni-apple-music-alt:before, .icon-apple-music-alt:before { content: "\ea1f"; }
.lni-apple-pay:before, .icon-apple-pay:before { content: "\ea20"; }
.lni-arc-browser:before, .icon-arc-browser:before { content: "\ea21"; }
.lni-arrow-all-direction:before, .icon-arrow-all-direction:before { content: "\ea22"; }
.lni-arrow-angular-top-left:before, .icon-arrow-angular-top-left:before { content: "\ea23"; }
.lni-arrow-angular-top-right:before, .icon-arrow-angular-top-right:before { content: "\ea24"; }
.lni-arrow-both-direction-horizontal-1:before, .icon-arrow-both-direction-horizontal-1:before { content: "\ea25"; }
.lni-arrow-both-direction-vertical-1:before, .icon-arrow-both-direction-vertical-1:before { content: "\ea26"; }
.lni-arrow-downward:before, .icon-arrow-downward:before { content: "\ea27"; }
.lni-arrow-left:before, .icon-arrow-left:before { content: "\ea28"; }
.lni-arrow-left-circle:before, .icon-arrow-left-circle:before { content: "\ea29"; }
.lni-arrow-right:before, .icon-arrow-right:before { content: "\ea2a"; }
.lni-arrow-right-circle:before, .icon-arrow-right-circle:before { content: "\ea2b"; }
.lni-arrow-upward:before, .icon-arrow-upward:before { content: "\ea2c"; }
.lni-asana:before, .icon-asana:before { content: "\ea2d"; }
.lni-astro:before, .icon-astro:before { content: "\ea2e"; }
.lni-atlassian:before, .icon-atlassian:before { content: "\ea2f"; }
.lni-audi:before, .icon-audi:before { content: "\ea30"; }
.lni-audi-alt:before, .icon-audi-alt:before { content: "\ea31"; }
.lni-aws:before, .icon-aws:before { content: "\ea32"; }
.lni-azure:before, .icon-azure:before { content: "\ea33"; }
.lni-badge-decagram-percent:before, .icon-badge-decagram-percent:before { content: "\ea34"; }
.lni-balloons:before, .icon-balloons:before { content: "\ea35"; }
.lni-ban-2:before, .icon-ban-2:before { content: "\ea36"; }
.lni-bar-chart-4:before, .icon-bar-chart-4:before { content: "\ea37"; }
.lni-bar-chart-dollar:before, .icon-bar-chart-dollar:before { content: "\ea38"; }
.lni-basket-shopping-3:before, .icon-basket-shopping-3:before { content: "\ea39"; }
.lni-beat:before, .icon-beat:before { content: "\ea3a"; }
.lni-behance:before, .icon-behance:before { content: "\ea3b"; }
.lni-bell-1:before, .icon-bell-1:before { content: "\ea3c"; }
.lni-bike:before, .icon-bike:before { content: "\ea3d"; }
.lni-bing:before, .icon-bing:before { content: "\ea3e"; }
.lni-bitbucket:before, .icon-bitbucket:before { content: "\ea3f"; }
.lni-bitcoin:before, .icon-bitcoin:before { content: "\ea40"; }
.lni-bittorrent:before, .icon-bittorrent:before { content: "\ea41"; }
.lni-blogger:before, .icon-blogger:before { content: "\ea42"; }
.lni-blogger-alt:before, .icon-blogger-alt:before { content: "\ea43"; }
.lni-bluetooth:before, .icon-bluetooth:before { content: "\ea44"; }
.lni-bluetooth-logo:before, .icon-bluetooth-logo:before { content: "\ea45"; }
.lni-bmw:before, .icon-bmw:before { content: "\ea46"; }
.lni-board-writing-3:before, .icon-board-writing-3:before { content: "\ea47"; }
.lni-bold:before, .icon-bold:before { content: "\ea48"; }
.lni-bolt-2:before, .icon-bolt-2:before { content: "\ea49"; }
.lni-bolt-3:before, .icon-bolt-3:before { content: "\ea4a"; }
.lni-book-1:before, .icon-book-1:before { content: "\ea4b"; }
.lni-bookmark-1:before, .icon-bookmark-1:before { content: "\ea4c"; }
.lni-bookmark-circle:before, .icon-bookmark-circle:before { content: "\ea4d"; }
.lni-books-2:before, .icon-books-2:before { content: "\ea4e"; }
.lni-bootstrap-5:before, .icon-bootstrap-5:before { content: "\ea4f"; }
.lni-bootstrap-5-square:before, .icon-bootstrap-5-square:before { content: "\ea50"; }
.lni-box-archive-1:before, .icon-box-archive-1:before { content: "\ea51"; }
.lni-box-closed:before, .icon-box-closed:before { content: "\ea52"; }
.lni-box-gift-1:before, .icon-box-gift-1:before { content: "\ea53"; }
.lni-brave:before, .icon-brave:before { content: "\ea54"; }
.lni-bricks:before, .icon-bricks:before { content: "\ea55"; }
.lni-bridge-3:before, .icon-bridge-3:before { content: "\ea56"; }
.lni-briefcase-1:before, .icon-briefcase-1:before { content: "\ea57"; }
.lni-briefcase-2:before, .icon-briefcase-2:before { content: "\ea58"; }
.lni-briefcase-plus-1:before, .icon-briefcase-plus-1:before { content: "\ea59"; }
.lni-brush-1-rotated:before, .icon-brush-1-rotated:before { content: "\ea5a"; }
.lni-brush-2:before, .icon-brush-2:before { content: "\ea5b"; }
.lni-btc:before, .icon-btc:before { content: "\ea5c"; }
.lni-bug-1:before, .icon-bug-1:before { content: "\ea5d"; }
.lni-buildings-1:before, .icon-buildings-1:before { content: "\ea5e"; }
.lni-bulb-2:before, .icon-bulb-2:before { content: "\ea5f"; }
.lni-bulb-4:before, .icon-bulb-4:before { content: "\ea60"; }
.lni-burger-1:before, .icon-burger-1:before { content: "\ea61"; }
.lni-burger-drink:before, .icon-burger-drink:before { content: "\ea62"; }
.lni-bus-1:before, .icon-bus-1:before { content: "\ea63"; }
.lni-busket-ball:before, .icon-busket-ball:before { content: "\ea64"; }
.lni-cake-1:before, .icon-cake-1:before { content: "\ea65"; }
.lni-calculator-1:before, .icon-calculator-1:before { content: "\ea66"; }
.lni-calculator-2:before, .icon-calculator-2:before { content: "\ea67"; }
.lni-calendar-days:before, .icon-calendar-days:before { content: "\ea68"; }
.lni-camera-1:before, .icon-camera-1:before { content: "\ea69"; }
.lni-camera-movie-1:before, .icon-camera-movie-1:before { content: "\ea6a"; }
.lni-candy-cane-2:before, .icon-candy-cane-2:before { content: "\ea6b"; }
.lni-candy-round-1:before, .icon-candy-round-1:before { content: "\ea6c"; }
.lni-canva:before, .icon-canva:before { content: "\ea6d"; }
.lni-capsule-1:before, .icon-capsule-1:before { content: "\ea6e"; }
.lni-car-2:before, .icon-car-2:before { content: "\ea6f"; }
.lni-car-4:before, .icon-car-4:before { content: "\ea70"; }
.lni-car-6:before, .icon-car-6:before { content: "\ea71"; }
.lni-caravan-1:before, .icon-caravan-1:before { content: "\ea72"; }
.lni-cart-1:before, .icon-cart-1:before { content: "\ea73"; }
.lni-cart-2:before, .icon-cart-2:before { content: "\ea74"; }
.lni-cash-app:before, .icon-cash-app:before { content: "\ea75"; }
.lni-certificate-badge-1:before, .icon-certificate-badge-1:before { content: "\ea76"; }
.lni-chat-bubble-2:before, .icon-chat-bubble-2:before { content: "\ea77"; }
.lni-check:before, .icon-check:before { content: "\ea78"; }
.lni-check-circle-1:before, .icon-check-circle-1:before { content: "\ea79"; }
.lni-check-square-2:before, .icon-check-square-2:before { content: "\ea7a"; }
.lni-chevron-down:before, .icon-chevron-down:before { content: "\ea7b"; }
.lni-chevron-down-circle:before, .icon-chevron-down-circle:before { content: "\ea7c"; }
.lni-chevron-left:before, .icon-chevron-left:before { content: "\ea7d"; }
.lni-chevron-left-circle:before, .icon-chevron-left-circle:before { content: "\ea7e"; }
.lni-chevron-right:before, .icon-chevron-right:before { content: "\ea7d"; transform: rotate(180deg); }
.lni-chevron-right-circle:before, .icon-chevron-right-circle:before { content: "\ea7f"; }
.lni-chevron-up:before, .icon-chevron-up:before { content: "\ea80"; }
.lni-chevron-up-circle:before, .icon-chevron-up-circle:before { content: "\ea81"; }
.lni-chrome:before, .icon-chrome:before { content: "\ea82"; }
.lni-chromecast:before, .icon-chromecast:before { content: "\ea83"; }
.lni-cisco:before, .icon-cisco:before { content: "\ea84"; }
.lni-claude:before, .icon-claude:before { content: "\ea85"; }
.lni-clickup:before, .icon-clickup:before { content: "\ea86"; }
.lni-clipboard:before, .icon-clipboard:before { content: "\ea87"; }
.lni-cloud-2:before, .icon-cloud-2:before { content: "\ea88"; }
.lni-cloud-bolt-1:before, .icon-cloud-bolt-1:before { content: "\ea89"; }
.lni-cloud-bolt-2:before, .icon-cloud-bolt-2:before { content: "\ea8a"; }
.lni-cloud-check-circle:before, .icon-cloud-check-circle:before { content: "\ea8b"; }
.lni-cloud-download:before, .icon-cloud-download:before { content: "\ea8c"; }
.lni-cloud-iot-2:before, .icon-cloud-iot-2:before { content: "\ea8d"; }
.lni-cloud-rain:before, .icon-cloud-rain:before { content: "\ea8e"; }
.lni-cloud-refresh-clockwise:before, .icon-cloud-refresh-clockwise:before { content: "\ea8f"; }
.lni-cloud-sun:before, .icon-cloud-sun:before { content: "\ea90"; }
.lni-cloud-upload:before, .icon-cloud-upload:before { content: "\ea91"; }
.lni-cloudflare:before, .icon-cloudflare:before { content: "\ea92"; }
.lni-code-1:before, .icon-code-1:before { content: "\ea93"; }
.lni-code-s:before, .icon-code-s:before { content: "\ea94"; }
.lni-codepen:before, .icon-codepen:before { content: "\ea95"; }
.lni-coffee-cup-2:before, .icon-coffee-cup-2:before { content: "\ea96"; }
.lni-coinbase:before, .icon-coinbase:before { content: "\ea97"; }
.lni-colour-palette-3:before, .icon-colour-palette-3:before { content: "\ea98"; }
.lni-comment-1:before, .icon-comment-1:before { content: "\ea99"; }
.lni-comment-1-share:before, .icon-comment-1-share:before { content: "\ea9a"; }
.lni-comment-1-text:before, .icon-comment-1-text:before { content: "\ea9b"; }
.lni-compass-drafting-2:before, .icon-compass-drafting-2:before { content: "\ea9c"; }
.lni-connectdevelop:before, .icon-connectdevelop:before { content: "\ea9d"; }
.lni-copilot:before, .icon-copilot:before { content: "\ea9e"; }
.lni-coral:before, .icon-coral:before { content: "\ea9f"; }
.lni-cpanel:before, .icon-cpanel:before { content: "\eaa0"; }
.lni-crane-4:before, .icon-crane-4:before { content: "\eaa1"; }
.lni-creative-commons:before, .icon-creative-commons:before { content: "\eaa2"; }
.lni-credit-card-multiple:before, .icon-credit-card-multiple:before { content: "\eaa3"; }
.lni-crop-2:before, .icon-crop-2:before { content: "\eaa4"; }
.lni-crown-3:before, .icon-crown-3:before { content: "\eaa5"; }
.lni-css3:before, .icon-css3:before { content: "\eaa6"; }
.lni-dashboard-square-1:before, .icon-dashboard-square-1:before { content: "\eaa7"; }
.lni-database-2:before, .icon-database-2:before { content: "\eaa8"; }
.lni-deno:before, .icon-deno:before { content: "\eaa9"; }
.lni-dev:before, .icon-dev:before { content: "\eaaa"; }
.lni-dialogflow:before, .icon-dialogflow:before { content: "\eaab"; }
.lni-diamonds-1:before, .icon-diamonds-1:before { content: "\eaac"; }
.lni-diamonds-2:before, .icon-diamonds-2:before { content: "\eaad"; }
.lni-digitalocean:before, .icon-digitalocean:before { content: "\eaae"; }
.lni-diners-club:before, .icon-diners-club:before { content: "\eaaf"; }
.lni-direction-ltr:before, .icon-direction-ltr:before { content: "\eab0"; }
.lni-direction-rtl:before, .icon-direction-rtl:before { content: "\eab1"; }
.lni-discord:before, .icon-discord:before { content: "\eab2"; }
.lni-discord-chat:before, .icon-discord-chat:before { content: "\eab3"; }
.lni-discover:before, .icon-discover:before { content: "\eab4"; }
.lni-docker:before, .icon-docker:before { content: "\eab5"; }
.lni-dollar:before, .icon-dollar:before { content: "\eab6"; }
.lni-dollar-circle:before, .icon-dollar-circle:before { content: "\eab7"; }
.lni-double-quotes-end-1:before, .icon-double-quotes-end-1:before { content: "\eab8"; }
.lni-download-1:before, .icon-download-1:before { content: "\eab9"; }
.lni-download-circle-1:before, .icon-download-circle-1:before { content: "\eaba"; }
.lni-dribbble:before, .icon-dribbble:before { content: "\eabb"; }
.lni-dribbble-symbol:before, .icon-dribbble-symbol:before { content: "\eabc"; }
.lni-drizzle:before, .icon-drizzle:before { content: "\eabd"; }
.lni-dropbox:before, .icon-dropbox:before { content: "\eabe"; }
.lni-drupal:before, .icon-drupal:before { content: "\eabf"; }
.lni-dumbbell-1:before, .icon-dumbbell-1:before { content: "\eac0"; }
.lni-edge:before, .icon-edge:before { content: "\eac1"; }
.lni-emoji-expressionless:before, .icon-emoji-expressionless:before { content: "\eac2"; }
.lni-emoji-expressionless-flat-eyes:before, .icon-emoji-expressionless-flat-eyes:before { content: "\eac3"; }
.lni-emoji-grin:before, .icon-emoji-grin:before { content: "\eac4"; }
.lni-emoji-sad:before, .icon-emoji-sad:before { content: "\eac5"; }
.lni-emoji-smile:before, .icon-emoji-smile:before { content: "\eac6"; }
.lni-emoji-smile-side:before, .icon-emoji-smile-side:before { content: "\eac7"; }
.lni-emoji-smile-sunglass:before, .icon-emoji-smile-sunglass:before { content: "\eac8"; }
.lni-emoji-smile-tongue:before, .icon-emoji-smile-tongue:before { content: "\eac9"; }
.lni-enter:before, .icon-enter:before { content: "\eaca"; }
.lni-enter-down:before, .icon-enter-down:before { content: "\eacb"; }
.lni-envato:before, .icon-envato:before { content: "\eacc"; }
.lni-envelope-1:before, .icon-envelope-1:before { content: "\eacd"; }
.lni-eraser-1:before, .icon-eraser-1:before { content: "\eace"; }
.lni-ethereum-logo:before, .icon-ethereum-logo:before { content: "\eacf"; }
.lni-euro:before, .icon-euro:before { content: "\ead0"; }
.lni-exit:before, .icon-exit:before { content: "\ead1"; }
.lni-exit-up:before, .icon-exit-up:before { content: "\ead2"; }
.lni-expand-arrow-1:before, .icon-expand-arrow-1:before { content: "\ead3"; }
.lni-expand-square-4:before, .icon-expand-square-4:before { content: "\ead4"; }
.lni-expressjs:before, .icon-expressjs:before { content: "\ead5"; }
.lni-eye:before, .icon-eye:before { content: "\ead6"; }
.lni-facebook:before, .icon-facebook:before { content: "\ead7"; }
.lni-facebook-messenger:before, .icon-facebook-messenger:before { content: "\ead8"; }
.lni-facebook-rounded:before, .icon-facebook-rounded:before { content: "\ead9"; }
.lni-facebook-square:before, .icon-facebook-square:before { content: "\eada"; }
.lni-facetime:before, .icon-facetime:before { content: "\eadb"; }
.lni-figma:before, .icon-figma:before { content: "\eadc"; }
.lni-file-format-zip:before, .icon-file-format-zip:before { content: "\eadd"; }
.lni-file-multiple:before, .icon-file-multiple:before { content: "\eade"; }
.lni-file-pencil:before, .icon-file-pencil:before { content: "\eadf"; }
.lni-file-plus-circle:before, .icon-file-plus-circle:before { content: "\eae0"; }
.lni-file-question:before, .icon-file-question:before { content: "\eae1"; }
.lni-file-xmark:before, .icon-file-xmark:before { content: "\eae2"; }
.lni-firebase:before, .icon-firebase:before { content: "\eae3"; }
.lni-firefox:before, .icon-firefox:before { content: "\eae4"; }
.lni-firework-rocket-4:before, .icon-firework-rocket-4:before { content: "\eae5"; }
.lni-fitbit:before, .icon-fitbit:before { content: "\eae6"; }
.lni-flag-1:before, .icon-flag-1:before { content: "\eae7"; }
.lni-flag-2:before, .icon-flag-2:before { content: "\eae8"; }
.lni-flickr:before, .icon-flickr:before { content: "\eae9"; }
.lni-floppy-disk-1:before, .icon-floppy-disk-1:before { content: "\eaea"; }
.lni-flower-2:before, .icon-flower-2:before { content: "\eaeb"; }
.lni-flutter:before, .icon-flutter:before { content: "\eaec"; }
.lni-folder-1:before, .icon-folder-1:before { content: "\eaed"; }
.lni-ford:before, .icon-ford:before { content: "\eaee"; }
.lni-framer:before, .icon-framer:before { content: "\eaef"; }
.lni-funnel-1:before, .icon-funnel-1:before { content: "\eaf0"; }
.lni-gallery:before, .icon-gallery:before { content: "\eaf1"; }
.lni-game-pad-modern-1:before, .icon-game-pad-modern-1:before { content: "\eaf2"; }
.lni-gatsby:before, .icon-gatsby:before { content: "\eaf3"; }
.lni-gauge-1:before, .icon-gauge-1:before { content: "\eaf4"; }
.lni-gear-1:before, .icon-gear-1:before { content: "\eaf5"; }
.lni-gears-3:before, .icon-gears-3:before { content: "\eaf6"; }
.lni-gemini:before, .icon-gemini:before { content: "\eaf7"; }
.lni-git:before, .icon-git:before { content: "\eaf8"; }
.lni-github:before, .icon-github:before { content: "\eaf9"; }
.lni-glass-juice-1:before, .icon-glass-juice-1:before { content: "\eafa"; }
.lni-globe-1:before, .icon-globe-1:before { content: "\eafb"; }
.lni-globe-stand:before, .icon-globe-stand:before { content: "\eafc"; }
.lni-go:before, .icon-go:before { content: "\eafd"; }
.lni-goodreads:before, .icon-goodreads:before { content: "\eafe"; }
.lni-google:before, .icon-google:before { content: "\eaff"; }
.lni-google-cloud:before, .icon-google-cloud:before { content: "\eb00"; }
.lni-google-drive:before, .icon-google-drive:before { content: "\eb01"; }
.lni-google-meet:before, .icon-google-meet:before { content: "\eb02"; }
.lni-google-pay:before, .icon-google-pay:before { content: "\eb03"; }
.lni-google-wallet:before, .icon-google-wallet:before { content: "\eb04"; }
.lni-graduation-cap-1:before, .icon-graduation-cap-1:before { content: "\eb05"; }
.lni-grammarly:before, .icon-grammarly:before { content: "\eb06"; }
.lni-hacker-news:before, .icon-hacker-news:before { content: "\eb07"; }
.lni-hammer-1:before, .icon-hammer-1:before { content: "\eb08"; }
.lni-hammer-2:before, .icon-hammer-2:before { content: "\eb09"; }
.lni-hand-mic:before, .icon-hand-mic:before { content: "\eb0a"; }
.lni-hand-shake:before, .icon-hand-shake:before { content: "\eb0b"; }
.lni-hand-stop:before, .icon-hand-stop:before { content: "\eb0c"; }
.lni-hand-taking-dollar:before, .icon-hand-taking-dollar:before { content: "\eb0d"; }
.lni-hand-taking-leaf-1:before, .icon-hand-taking-leaf-1:before { content: "\eb0e"; }
.lni-hand-taking-user:before, .icon-hand-taking-user:before { content: "\eb0f"; }
.lni-hashnode:before, .icon-hashnode:before { content: "\eb10"; }
.lni-hat-chef-3:before, .icon-hat-chef-3:before { content: "\eb11"; }
.lni-headphone-1:before, .icon-headphone-1:before { content: "\eb12"; }
.lni-heart:before, .icon-heart:before { content: "\eb13"; }
.lni-helicopter-2:before, .icon-helicopter-2:before { content: "\eb14"; }
.lni-helmet-safety-1:before, .icon-helmet-safety-1:before { content: "\eb15"; }
.lni-hierarchy-1:before, .icon-hierarchy-1:before { content: "\eb16"; }
.lni-highlighter-1:before, .icon-highlighter-1:before { content: "\eb17"; }
.lni-highlighter-2:before, .icon-highlighter-2:before { content: "\eb18"; }
.lni-home-2:before, .icon-home-2:before { content: "\eb19"; }
.lni-hospital-2:before, .icon-hospital-2:before { content: "\eb1a"; }
.lni-hourglass:before, .icon-hourglass:before { content: "\eb1b"; }
.lni-html5:before, .icon-html5:before { content: "\eb1c"; }
.lni-ibm:before, .icon-ibm:before { content: "\eb1d"; }
.lni-id-card:before, .icon-id-card:before { content: "\eb1e"; }
.lni-imdb:before, .icon-imdb:before { content: "\eb1f"; }
.lni-indent:before, .icon-indent:before { content: "\eb20"; }
.lni-info:before, .icon-info:before { content: "\eb21"; }
.lni-injection-1:before, .icon-injection-1:before { content: "\eb22"; }
.lni-instagram:before, .icon-instagram:before { content: "\eb23"; }
.lni-instagram-logotype:before, .icon-instagram-logotype:before { content: "\eb24"; }
.lni-intel:before, .icon-intel:before { content: "\eb25"; }
.lni-ios:before, .icon-ios:before { content: "\eb26"; }
.lni-island-2:before, .icon-island-2:before { content: "\eb27"; }
.lni-jaguar:before, .icon-jaguar:before { content: "\eb28"; }
.lni-jamstack:before, .icon-jamstack:before { content: "\eb29"; }
.lni-java:before, .icon-java:before { content: "\eb2a"; }
.lni-javascript:before, .icon-javascript:before { content: "\eb2b"; }
.lni-jcb:before, .icon-jcb:before { content: "\eb2c"; }
.lni-joomla:before, .icon-joomla:before { content: "\eb2d"; }
.lni-jsfiddle:before, .icon-jsfiddle:before { content: "\eb2e"; }
.lni-key-1:before, .icon-key-1:before { content: "\eb2f"; }
.lni-keyboard:before, .icon-keyboard:before { content: "\eb30"; }
.lni-knife-fork-1:before, .icon-knife-fork-1:before { content: "\eb31"; }
.lni-kubernetes:before, .icon-kubernetes:before { content: "\eb32"; }
.lni-label-dollar-2:before, .icon-label-dollar-2:before { content: "\eb33"; }
.lni-laptop-2:before, .icon-laptop-2:before { content: "\eb34"; }
.lni-laptop-phone:before, .icon-laptop-phone:before { content: "\eb35"; }
.lni-laravel:before, .icon-laravel:before { content: "\eb36"; }
.lni-layers-1:before, .icon-layers-1:before { content: "\eb37"; }
.lni-layout-26:before, .icon-layout-26:before { content: "\eb38"; }
.lni-layout-9:before, .icon-layout-9:before { content: "\eb39"; }
.lni-leaf-1:before, .icon-leaf-1:before { content: "\eb3a"; }
.lni-leaf-6:before, .icon-leaf-6:before { content: "\eb3b"; }
.lni-lemon-squeezy:before, .icon-lemon-squeezy:before { content: "\eb3c"; }
.lni-life-guard-tube-1:before, .icon-life-guard-tube-1:before { content: "\eb3d"; }
.lni-line:before, .icon-line:before { content: "\eb3e"; }
.lni-line-dashed:before, .icon-line-dashed:before { content: "\eb3f"; }
.lni-line-dotted:before, .icon-line-dotted:before { content: "\eb40"; }
.lni-line-height:before, .icon-line-height:before { content: "\eb41"; }
.lni-lineicons:before, .icon-lineicons:before { content: "\eb42"; }
.lni-link-2-angular-right:before, .icon-link-2-angular-right:before { content: "\eb43"; }
.lni-linkedin:before, .icon-linkedin:before { content: "\eb44"; }
.lni-location-arrow-right:before, .icon-location-arrow-right:before { content: "\eb45"; }
.lni-locked-1:before, .icon-locked-1:before { content: "\eb46"; }
.lni-locked-2:before, .icon-locked-2:before { content: "\eb47"; }
.lni-loom:before, .icon-loom:before { content: "\eb48"; }
.lni-magento:before, .icon-magento:before { content: "\eb49"; }
.lni-magnet:before, .icon-magnet:before { content: "\eb4a"; }
.lni-mailchimp:before, .icon-mailchimp:before { content: "\eb4b"; }
.lni-map-marker-1:before, .icon-map-marker-1:before { content: "\eb4c"; }
.lni-map-marker-5:before, .icon-map-marker-5:before { content: "\eb4d"; }
.lni-map-pin-5:before, .icon-map-pin-5:before { content: "\eb4e"; }
.lni-markdown:before, .icon-markdown:before { content: "\eb4f"; }
.lni-mastercard:before, .icon-mastercard:before { content: "\eb50"; }
.lni-medium:before, .icon-medium:before { content: "\eb51"; }
.lni-medium-alt:before, .icon-medium-alt:before { content: "\eb52"; }
.lni-megaphone-1:before, .icon-megaphone-1:before { content: "\eb53"; }
.lni-menu-cheesburger:before, .icon-menu-cheesburger:before { content: "\eb54"; }
.lni-menu-hamburger-1:before, .icon-menu-hamburger-1:before { content: "\eb55"; }
.lni-menu-meatballs-1:before, .icon-menu-meatballs-1:before { content: "\eb56"; }
.lni-menu-meatballs-2:before, .icon-menu-meatballs-2:before { content: "\eb57"; }
.lni-mercedes:before, .icon-mercedes:before { content: "\eb58"; }
.lni-message-2:before, .icon-message-2:before { content: "\eb59"; }
.lni-message-2-question:before, .icon-message-2-question:before { content: "\eb5a"; }
.lni-message-3-text:before, .icon-message-3-text:before { content: "\eb5b"; }
.lni-meta:before, .icon-meta:before { content: "\eb5c"; }
.lni-meta-alt:before, .icon-meta-alt:before { content: "\eb5d"; }
.lni-microphone-1:before, .icon-microphone-1:before { content: "\eb5e"; }
.lni-microscope:before, .icon-microscope:before { content: "\eb5f"; }
.lni-microsoft:before, .icon-microsoft:before { content: "\eb60"; }
.lni-microsoft-edge:before, .icon-microsoft-edge:before { content: "\eb61"; }
.lni-microsoft-teams:before, .icon-microsoft-teams:before { content: "\eb62"; }
.lni-minus:before, .icon-minus:before { content: "\eb63"; }
.lni-minus-circle:before, .icon-minus-circle:before { content: "\eb64"; }
.lni-mongodb:before, .icon-mongodb:before { content: "\eb65"; }
.lni-monitor:before, .icon-monitor:before { content: "\eb66"; }
.lni-monitor-code:before, .icon-monitor-code:before { content: "\eb67"; }
.lni-monitor-mac:before, .icon-monitor-mac:before { content: "\eb68"; }
.lni-moon-half-right-5:before, .icon-moon-half-right-5:before { content: "\eb69"; }
.lni-mountains-2:before, .icon-mountains-2:before { content: "\eb6a"; }
.lni-mouse-2:before, .icon-mouse-2:before { content: "\eb6b"; }
.lni-mushroom-1:before, .icon-mushroom-1:before { content: "\eb6c"; }
.lni-mushroom-5:before, .icon-mushroom-5:before { content: "\eb6d"; }
.lni-music:before, .icon-music:before { content: "\eb6e"; }
.lni-mysql:before, .icon-mysql:before { content: "\eb6f"; }
.lni-nasa:before, .icon-nasa:before { content: "\eb70"; }
.lni-netflix:before, .icon-netflix:before { content: "\eb71"; }
.lni-netlify:before, .icon-netlify:before { content: "\eb72"; }
.lni-next-step-2:before, .icon-next-step-2:before { content: "\eb73"; }
.lni-nextjs:before, .icon-nextjs:before { content: "\eb74"; }
.lni-nike:before, .icon-nike:before { content: "\eb75"; }
.lni-nissan:before, .icon-nissan:before { content: "\eb76"; }
.lni-nodejs:before, .icon-nodejs:before { content: "\eb77"; }
.lni-nodejs-alt:before, .icon-nodejs-alt:before { content: "\eb78"; }
.lni-notebook-1:before, .icon-notebook-1:before { content: "\eb79"; }
.lni-notion:before, .icon-notion:before { content: "\eb7a"; }
.lni-npm:before, .icon-npm:before { content: "\eb7b"; }
.lni-nuxt:before, .icon-nuxt:before { content: "\eb7c"; }
.lni-nvidia:before, .icon-nvidia:before { content: "\eb7d"; }
.lni-oculus:before, .icon-oculus:before { content: "\eb7e"; }
.lni-open-ai:before, .icon-open-ai:before { content: "\eb7f"; }
.lni-opera-mini:before, .icon-opera-mini:before { content: "\eb80"; }
.lni-oracle:before, .icon-oracle:before { content: "\eb81"; }
.lni-outdent:before, .icon-outdent:before { content: "\eb82"; }
.lni-paddle:before, .icon-paddle:before { content: "\eb83"; }
.lni-page-break-1:before, .icon-page-break-1:before { content: "\eb84"; }
.lni-pagination:before, .icon-pagination:before { content: "\eb85"; }
.lni-paint-bucket:before, .icon-paint-bucket:before { content: "\eb86"; }
.lni-paint-roller-1:before, .icon-paint-roller-1:before { content: "\eb87"; }
.lni-paperclip-1:before, .icon-paperclip-1:before { content: "\eb88"; }
.lni-party-flags:before, .icon-party-flags:before { content: "\eb89"; }
.lni-party-spray:before, .icon-party-spray:before { content: "\eb8a"; }
.lni-patreon:before, .icon-patreon:before { content: "\eb8b"; }
.lni-pause:before, .icon-pause:before { content: "\eb8c"; }
.lni-payoneer:before, .icon-payoneer:before { content: "\eb8d"; }
.lni-paypal:before, .icon-paypal:before { content: "\eb8e"; }
.lni-pen-to-square:before, .icon-pen-to-square:before { content: "\eb8f"; }
.lni-pencil-1:before, .icon-pencil-1:before { content: "\eb90"; }
.lni-pepsi:before, .icon-pepsi:before { content: "\eb91"; }
.lni-phone:before, .icon-phone:before { content: "\eb92"; }
.lni-photos:before, .icon-photos:before { content: "\eb93"; }
.lni-php:before, .icon-php:before { content: "\eb94"; }
.lni-pie-chart-2:before, .icon-pie-chart-2:before { content: "\eb95"; }
.lni-pilcrow:before, .icon-pilcrow:before { content: "\eb96"; }
.lni-pimjo-logo:before, .icon-pimjo-logo:before { content: "\eb97"; }
.lni-pimjo-symbol:before, .icon-pimjo-symbol:before { content: "\eb98"; }
.lni-pinterest:before, .icon-pinterest:before { content: "\eb99"; }
.lni-pizza-2:before, .icon-pizza-2:before { content: "\eb9a"; }
.lni-placeholder-dollar:before, .icon-placeholder-dollar:before { content: "\eb9b"; }
.lni-plantscale:before, .icon-plantscale:before { content: "\eb9c"; }
.lni-play:before, .icon-play:before { content: "\eb9d"; }
.lni-play-store:before, .icon-play-store:before { content: "\eb9e"; }
.lni-playstation:before, .icon-playstation:before { content: "\eb9f"; }
.lni-plug-1:before, .icon-plug-1:before { content: "\eba0"; }
.lni-plus:before, .icon-plus:before { content: "\eba1"; }
.lni-plus-circle:before, .icon-plus-circle:before { content: "\eba2"; }
.lni-pnpm:before, .icon-pnpm:before { content: "\eba3"; }
.lni-postgresql:before, .icon-postgresql:before { content: "\eba4"; }
.lni-postman:before, .icon-postman:before { content: "\eba5"; }
.lni-pound:before, .icon-pound:before { content: "\eba6"; }
.lni-power-button:before, .icon-power-button:before { content: "\eba7"; }
.lni-previous-step-2:before, .icon-previous-step-2:before { content: "\eba8"; }
.lni-printer:before, .icon-printer:before { content: "\eba9"; }
.lni-prisma:before, .icon-prisma:before { content: "\ebaa"; }
.lni-producthunt:before, .icon-producthunt:before { content: "\ebab"; }
.lni-proton-mail-logo:before, .icon-proton-mail-logo:before { content: "\ebac"; }
.lni-proton-mail-symbol:before, .icon-proton-mail-symbol:before { content: "\ebad"; }
.lni-python:before, .icon-python:before { content: "\ebae"; }
.lni-question-mark:before, .icon-question-mark:before { content: "\ebaf"; }
.lni-question-mark-circle:before, .icon-question-mark-circle:before { content: "\ebb0"; }
.lni-quora:before, .icon-quora:before { content: "\ebb1"; }
.lni-radis:before, .icon-radis:before { content: "\ebb2"; }
.lni-react:before, .icon-react:before { content: "\ebb3"; }
.lni-reddit:before, .icon-reddit:before { content: "\ebb4"; }
.lni-refresh-circle-1-clockwise:before, .icon-refresh-circle-1-clockwise:before { content: "\ebb5"; }
.lni-refresh-dollar-1:before, .icon-refresh-dollar-1:before { content: "\ebb6"; }
.lni-refresh-user-1:before, .icon-refresh-user-1:before { content: "\ebb7"; }
.lni-remix-js:before, .icon-remix-js:before { content: "\ebb8"; }
.lni-road-1:before, .icon-road-1:before { content: "\ebb9"; }
.lni-rocket-5:before, .icon-rocket-5:before { content: "\ebba"; }
.lni-route-1:before, .icon-route-1:before { content: "\ebbb"; }
.lni-rss-right:before, .icon-rss-right:before { content: "\ebbc"; }
.lni-ruler-1:before, .icon-ruler-1:before { content: "\ebbd"; }
.lni-ruler-pen:before, .icon-ruler-pen:before { content: "\ebbe"; }
.lni-rupee:before, .icon-rupee:before { content: "\ebbf"; }
.lni-safari:before, .icon-safari:before { content: "\ebc0"; }
.lni-sanity:before, .icon-sanity:before { content: "\ebc1"; }
.lni-school-bench-1:before, .icon-school-bench-1:before { content: "\ebc2"; }
.lni-school-bench-2:before, .icon-school-bench-2:before { content: "\ebc3"; }
.lni-scissors-1-vertical:before, .icon-scissors-1-vertical:before { content: "\ebc4"; }
.lni-scoter:before, .icon-scoter:before { content: "\ebc5"; }
.lni-scroll-down-2:before, .icon-scroll-down-2:before { content: "\ebc6"; }
.lni-search-1:before, .icon-search-1:before { content: "\ebc7"; }
.lni-search-2:before, .icon-search-2:before { content: "\ebc8"; }
.lni-search-minus:before, .icon-search-minus:before { content: "\ebc9"; }
.lni-search-plus:before, .icon-search-plus:before { content: "\ebca"; }
.lni-search-text:before, .icon-search-text:before { content: "\ebcb"; }
.lni-select-cursor-1:before, .icon-select-cursor-1:before { content: "\ebcc"; }
.lni-seo-monitor:before, .icon-seo-monitor:before { content: "\ebcd"; }
.lni-service-bell-1:before, .icon-service-bell-1:before { content: "\ebce"; }
.lni-share-1:before, .icon-share-1:before { content: "\ebcf"; }
.lni-share-1-circle:before, .icon-share-1-circle:before { content: "\ebd0"; }
.lni-share-2:before, .icon-share-2:before { content: "\ebd1"; }
.lni-shield-2:before, .icon-shield-2:before { content: "\ebd2"; }
.lni-shield-2-check:before, .icon-shield-2-check:before { content: "\ebd3"; }
.lni-shield-dollar:before, .icon-shield-dollar:before { content: "\ebd4"; }
.lni-shift-left:before, .icon-shift-left:before { content: "\ebd5"; }
.lni-shift-right:before, .icon-shift-right:before { content: "\ebd6"; }
.lni-ship-1:before, .icon-ship-1:before { content: "\ebd7"; }
.lni-shirt-1:before, .icon-shirt-1:before { content: "\ebd8"; }
.lni-shopify:before, .icon-shopify:before { content: "\ebd9"; }
.lni-shovel:before, .icon-shovel:before { content: "\ebda"; }
.lni-shuffle:before, .icon-shuffle:before { content: "\ebdb"; }
.lni-sign-post-left:before, .icon-sign-post-left:before { content: "\ebdc"; }
.lni-signal-app:before, .icon-signal-app:before { content: "\ebdd"; }
.lni-signs-post-2:before, .icon-signs-post-2:before { content: "\ebde"; }
.lni-sketch:before, .icon-sketch:before { content: "\ebdf"; }
.lni-skype:before, .icon-skype:before { content: "\ebe0"; }
.lni-slack:before, .icon-slack:before { content: "\ebe1"; }
.lni-slice-2:before, .icon-slice-2:before { content: "\ebe2"; }
.lni-sliders-horizontal-square-2:before, .icon-sliders-horizontal-square-2:before { content: "\ebe3"; }
.lni-slideshare:before, .icon-slideshare:before { content: "\ebe4"; }
.lni-snapchat:before, .icon-snapchat:before { content: "\ebe5"; }
.lni-sort-alphabetical:before, .icon-sort-alphabetical:before { content: "\ebe6"; }
.lni-sort-high-to-low:before, .icon-sort-high-to-low:before { content: "\ebe7"; }
.lni-soundcloud:before, .icon-soundcloud:before { content: "\ebe8"; }
.lni-spacex:before, .icon-spacex:before { content: "\ebe9"; }
.lni-spellcheck:before, .icon-spellcheck:before { content: "\ebea"; }
.lni-spinner-2-sacle:before, .icon-spinner-2-sacle:before { content: "\ebeb"; }
.lni-spinner-3:before, .icon-spinner-3:before { content: "\ebec"; }
.lni-sports:before, .icon-sports:before { content: "\ebed"; }
.lni-spotify:before, .icon-spotify:before { content: "\ebee"; }
.lni-spotify-alt:before, .icon-spotify-alt:before { content: "\ebef"; }
.lni-squarespace:before, .icon-squarespace:before { content: "\ebf0"; }
.lni-stackoverflow:before, .icon-stackoverflow:before { content: "\ebf1"; }
.lni-stamp:before, .icon-stamp:before { content: "\ebf2"; }
.lni-star-fat:before, .icon-star-fat:before { content: "\ebf3"; }
.lni-star-fat-half-2:before, .icon-star-fat-half-2:before { content: "\ebf4"; }
.lni-star-sharp-disabled:before, .icon-star-sharp-disabled:before { content: "\ebf5"; }
.lni-statista:before, .icon-statista:before { content: "\ebf6"; }
.lni-steam:before, .icon-steam:before { content: "\ebf7"; }
.lni-stethoscope-1:before, .icon-stethoscope-1:before { content: "\ebf8"; }
.lni-stopwatch:before, .icon-stopwatch:before { content: "\ebf9"; }
.lni-storage-hdd-2:before, .icon-storage-hdd-2:before { content: "\ebfa"; }
.lni-strikethrough-1:before, .icon-strikethrough-1:before { content: "\ebfb"; }
.lni-stripe:before, .icon-stripe:before { content: "\ebfc"; }
.lni-stumbleupon:before, .icon-stumbleupon:before { content: "\ebfd"; }
.lni-sun-1:before, .icon-sun-1:before { content: "\ebfe"; }
.lni-supabase:before, .icon-supabase:before { content: "\ebff"; }
.lni-surfboard-2:before, .icon-surfboard-2:before { content: "\ec00"; }
.lni-svelte:before, .icon-svelte:before { content: "\ec01"; }
.lni-swift:before, .icon-swift:before { content: "\ec02"; }
.lni-tab:before, .icon-tab:before { content: "\ec03"; }
.lni-tailwindcss:before, .icon-tailwindcss:before { content: "\ec04"; }
.lni-target-user:before, .icon-target-user:before { content: "\ec05"; }
.lni-telegram:before, .icon-telegram:before { content: "\ec06"; }
.lni-telephone-1:before, .icon-telephone-1:before { content: "\ec07"; }
.lni-telephone-3:before, .icon-telephone-3:before { content: "\ec08"; }
.lni-tesla:before, .icon-tesla:before { content: "\ec09"; }
.lni-text-format:before, .icon-text-format:before { content: "\ec0a"; }
.lni-text-format-remove:before, .icon-text-format-remove:before { content: "\ec0b"; }
.lni-text-paragraph:before, .icon-text-paragraph:before { content: "\ec0c"; }
.lni-thumbs-down-3:before, .icon-thumbs-down-3:before { content: "\ec0d"; }
.lni-thumbs-up-3:before, .icon-thumbs-up-3:before { content: "\ec0e"; }
.lni-ticket-1:before, .icon-ticket-1:before { content: "\ec0f"; }
.lni-tickets-3:before, .icon-tickets-3:before { content: "\ec10"; }
.lni-tiktok:before, .icon-tiktok:before { content: "\ec11"; }
.lni-tiktok-alt:before, .icon-tiktok-alt:before { content: "\ec12"; }
.lni-tower-broadcast-1:before, .icon-tower-broadcast-1:before { content: "\ec13"; }
.lni-toyota:before, .icon-toyota:before { content: "\ec14"; }
.lni-train-1:before, .icon-train-1:before { content: "\ec15"; }
.lni-train-3:before, .icon-train-3:before { content: "\ec16"; }
.lni-trash-3:before, .icon-trash-3:before { content: "\ec17"; }
.lni-tree-2:before, .icon-tree-2:before { content: "\ec18"; }
.lni-trees-3:before, .icon-trees-3:before { content: "\ec19"; }
.lni-trello:before, .icon-trello:before { content: "\ec1a"; }
.lni-trend-down-1:before, .icon-trend-down-1:before { content: "\ec1b"; }
.lni-trend-up-1:before, .icon-trend-up-1:before { content: "\ec1c"; }
.lni-trophy-1:before, .icon-trophy-1:before { content: "\ec1d"; }
.lni-trowel-1:before, .icon-trowel-1:before { content: "\ec1e"; }
.lni-truck-delivery-1:before, .icon-truck-delivery-1:before { content: "\ec1f"; }
.lni-tumblr:before, .icon-tumblr:before { content: "\ec20"; }
.lni-turborepo:before, .icon-turborepo:before { content: "\ec21"; }
.lni-twitch:before, .icon-twitch:before { content: "\ec22"; }
.lni-twitter-old:before, .icon-twitter-old:before { content: "\ec23"; }
.lni-typescript:before, .icon-typescript:before { content: "\ec24"; }
.lni-uber:before, .icon-uber:before { content: "\ec25"; }
.lni-uber-symbol:before, .icon-uber-symbol:before { content: "\ec26"; }
.lni-ubuntu:before, .icon-ubuntu:before { content: "\ec27"; }
.lni-underline:before, .icon-underline:before { content: "\ec28"; }
.lni-unlink-2-angular-eft:before, .icon-unlink-2-angular-eft:before { content: "\ec29"; }
.lni-unlocked-2:before, .icon-unlocked-2:before { content: "\ec2a"; }
.lni-unsplash:before, .icon-unsplash:before { content: "\ec2b"; }
.lni-upload-1:before, .icon-upload-1:before { content: "\ec2c"; }
.lni-upload-circle-1:before, .icon-upload-circle-1:before { content: "\ec2d"; }
.lni-user-4:before, .icon-user-4:before { content: "\ec2e"; }
.lni-user-multiple-4:before, .icon-user-multiple-4:before { content: "\ec2f"; }
.lni-vector-nodes-6:before, .icon-vector-nodes-6:before { content: "\ec30"; }
.lni-vector-nodes-7:before, .icon-vector-nodes-7:before { content: "\ec31"; }
.lni-vercel:before, .icon-vercel:before { content: "\ec32"; }
.lni-vimeo:before, .icon-vimeo:before { content: "\ec33"; }
.lni-visa:before, .icon-visa:before { content: "\ec34"; }
.lni-vite:before, .icon-vite:before { content: "\ec35"; }
.lni-vk:before, .icon-vk:before { content: "\ec36"; }
.lni-vmware:before, .icon-vmware:before { content: "\ec37"; }
.lni-volkswagen:before, .icon-volkswagen:before { content: "\ec38"; }
.lni-volume-1:before, .icon-volume-1:before { content: "\ec39"; }
.lni-volume-high:before, .icon-volume-high:before { content: "\ec3a"; }
.lni-volume-low:before, .icon-volume-low:before { content: "\ec3b"; }
.lni-volume-mute:before, .icon-volume-mute:before { content: "\ec3c"; }
.lni-volume-off:before, .icon-volume-off:before { content: "\ec3d"; }
.lni-vs-code:before, .icon-vs-code:before { content: "\ec3e"; }
.lni-vuejs:before, .icon-vuejs:before { content: "\ec3f"; }
.lni-wallet-1:before, .icon-wallet-1:before { content: "\ec40"; }
.lni-watch-beat-1:before, .icon-watch-beat-1:before { content: "\ec41"; }
.lni-water-drop-1:before, .icon-water-drop-1:before { content: "\ec42"; }
.lni-webflow:before, .icon-webflow:before { content: "\ec43"; }
.lni-webhooks:before, .icon-webhooks:before { content: "\ec44"; }
.lni-wechat:before, .icon-wechat:before { content: "\ec45"; }
.lni-weight-machine-1:before, .icon-weight-machine-1:before { content: "\ec46"; }
.lni-whatsapp:before, .icon-whatsapp:before { content: "\ec47"; }
.lni-wheelbarrow-empty:before, .icon-wheelbarrow-empty:before { content: "\ec48"; }
.lni-wheelchair-1:before, .icon-wheelchair-1:before { content: "\ec49"; }
.lni-windows:before, .icon-windows:before { content: "\ec4a"; }
.lni-wise:before, .icon-wise:before { content: "\ec4b"; }
.lni-wordpress:before, .icon-wordpress:before { content: "\ec4c"; }
.lni-www:before, .icon-www:before { content: "\ec4d"; }
.lni-www-cursor:before, .icon-www-cursor:before { content: "\ec4e"; }
.lni-x:before, .icon-x:before { content: "\ec4f"; }
.lni-xampp:before, .icon-xampp:before { content: "\ec50"; }
.lni-xbox:before, .icon-xbox:before { content: "\ec51"; }
.lni-xmark:before, .icon-xmark:before { content: "\ec52"; }
.lni-xmark-circle:before, .icon-xmark-circle:before { content: "\ec53"; }
.lni-xrp:before, .icon-xrp:before { content: "\ec54"; }
.lni-yahoo:before, .icon-yahoo:before { content: "\ec55"; }
.lni-yarn:before, .icon-yarn:before { content: "\ec56"; }
.lni-ycombinator:before, .icon-ycombinator:before { content: "\ec57"; }
.lni-yen:before, .icon-yen:before { content: "\ec58"; }
.lni-youtube:before, .icon-youtube:before { content: "\ec59"; }
.lni-youtube-kids:before, .icon-youtube-kids:before { content: "\ec5a"; }
.lni-youtube-music:before, .icon-youtube-music:before { content: "\ec5b"; }
.lni-zapier:before, .icon-zapier:before { content: "\ec5c"; }
.lni-zero-size:before, .icon-zero-size:before { content: "\ec5d"; }
.lni-zoom:before, .icon-zoom:before { content: "\ec5e"; }
.icon-chevron-right-after {
align-items: flex-start;
display: flex;
justify-content: space-between;
line-height: 1;
margin-top: 0;
padding-top: 0;
width: 100%;
&:after {
content: "\ea7d";
margin-top: .25rem;
transform: rotate(180deg);
}
}
+36
View File
@@ -0,0 +1,36 @@
/**
* Navigation component
* A composite component consisting of
* following components:
* - nav-main-mega.php
* - menu-items
* - index.php
* - nav-functional.php
*
* Desktop Navigation Styles:
* Choose one or the other for the main navigation, based on theme needs.
* @import 'nav-main-default'; (standard dropdown)
* @import 'nav-main-mega'; (mega menu style)
*
* Mobile Navigation Styles:
* Choose one for mobile navigation behavior:
* @import 'nav-mobile-accordion'; (traditional dropdown/accordion style)
* @import 'nav-mobile-sliding'; (sliding viewport style)
*/
@import "./nav-functional.css";
@import "./nav-aux.css";
/* Mobile Navigation Style - Choose one of the following: */
/* Traditional dropdown style */
@import "./nav-main-default.css";
/* Mega menu style */
/* @import "./nav-main-mega.css"; */
/* Mobile Navigation Style - Choose one of the following: */
/* Accordion/dropdown style */
/* @import "./nav-mobile-accordion.css"; */
/* Sliding viewport style */
@import "./nav-mobile-sliding.css";
@import "./nav-footer.css";
+36
View File
@@ -0,0 +1,36 @@
/**
* VDI Navs & Menu - Auxiliary Nav Styles
*
* Please review documentation upon first use, and, as-needed:
* https://docs.vincentdevelopment.ca/docs/starter-v3-enhancements/navigation/
*/
.nav-aux {
@apply relative flex flex-wrap gap-2 items-center justify-end p-0 m-0;
.menu-vdi {
@apply relative flex flex-wrap gap-4 items-center gap-y-2 gap-x-4 p-0 m-0;
.menu-vdi__toggle {
/* styles */
@apply flex items-center gap-2;
/* interaction */
@apply focus-visible:underline hover:underline;
}
}
}
/* desktop overrides */
@media screen and (min-width: 62.5rem) {
.nav-aux {
@apply gap-4;
.menu-vdi {
@apply justify-end;
.menu-vdi__toggle {
@apply flex items-center gap-2;
}
}
}
}
+102
View File
@@ -0,0 +1,102 @@
/**
* VDI Navs & Menu - Footer Navigation
*
* Please review documentation upon first use, and, as-needed:
* ___Link_Here___
*/
/* desktop */
@media screen and (min-width: 62.5rem) {
.nav-footer .menu-vdi {
@apply flex flex-col items-start justify-start p-0 m-0;
>li {
>a {
/* text */
@apply font-normal leading-snug text-secondary text-18px;
/* spacing & display */
@apply mx-0 p-0 no-underline;
}
}
}
}
/* mobile */
@media screen and (max-width: 62.5rem) {
.nav-footer {
.nav-footer__toggle {
/* display */
@apply text-primary p-3;
}
.menu-vdi {
@apply flex-col w-[95%] py-6;
.menu-vdi__submenu {
@apply py-2 px-7 flex-col;
}
.menu-vdi__item {
a,
button {
/* text */
@apply font-normal leading-snug text-lg;
/* spacing & display */
@apply block w-full p-4;
/* interaction */
@apply focus-visible:bg-secondary-200 hover:bg-secondary-200;
}
a {
@apply block w-full;
}
button {
@apply flex w-full justify-between;
}
}
}
}
}
@media screen and (min-width: 62.5rem) {
.nav-footer .menu-vdi {
.menu-vdi__toggle {
@apply font-semibold underline text-20px mt-3 pb-1;
>svg {
@apply hidden;
}
}
.menu-vdi__item--parent {
@apply static;
}
.menu-vdi__submenu {
@apply static block m-0 mb-3 p-0 pl-2 shadow-none;
>li {
@apply w-full m-0 p-0;
}
.menu-vdi__item {
/* text */
@apply font-normal leading-snug text-18px;
/* spacing & display */
@apply block w-full;
/* interaction */
@apply focus-visible:bg-secondary-200 hover:bg-secondary-200;
a {
@apply block w-full;
}
}
a.menu-vdi__item,
.menu-vdi__item a {
@apply p-0 text-secondary;
}
}
}
}
+100
View File
@@ -0,0 +1,100 @@
/**
* VDI Navs & Menu - Functional Styles
*
* Please review documentation upon first use, and, as-needed:
* https://docs.vincentdevelopment.ca/docs/starter-v3-enhancements/navigation/
*/
/* all sizes */
.menu-vdi {
@apply hidden;
>li {
@apply list-none;
>a {
@apply focus-visible:underline hover:underline;
}
a[aria-current="true"] {
@apply underline;
}
.menu-vdi__toggle {
&[aria-expanded="true"] {
svg {
@apply rotate-180;
}
+.menu-vdi__submenu {
@apply flex;
}
}
}
}
.menu-vdi__submenu {
@apply hidden container z-10;
/* should also put current page here */
.menu-vdi__item--grandchild a {
@apply no-underline focus-visible:underline hover:underline;
}
}
}
/* Mobile */
@media screen and (max-width: 62.5rem) {
#app:has(.nav-main__toggle[aria-expanded="true"]) { height: calc(100vh - var(--hgtHeader)); }
.nav-main__toggle {
.nav-toggle-hamburger { display: inline-block !important; }
.nav-toggle-x { display: none !important; }
&[aria-expanded="true"] {
.nav-toggle-hamburger { display: none !important; }
.nav-toggle-x { display: inline-block !important; }
}
}
.nav-main {
.nav-main__toggle[aria-expanded="true"] {
+ul {
@apply absolute flex;
}
}
.menu-vdi {
@apply absolute right-0 z-10;
.menu-vdi__toggle {
@apply flex items-center gap-2;
}
}
}
.nav-aux {
.menu-vdi__submenu {
@apply absolute list-none;
}
}
}
/* Desktop */
@media screen and (min-width: 62.5rem) {
.nav-main__toggle {
@apply hidden;
}
.menu-vdi {
@apply flex list-none;
.menu-vdi__toggle {
@apply flex items-center gap-2;
}
.menu-vdi__submenu {
@apply absolute list-none;
}
}
}
+67
View File
@@ -0,0 +1,67 @@
/**
* VDI Navs & Menu - Main Navigation + Default Dropdown Menu Styles (Desktop Only)
*
* This file contains only desktop navigation styles.
* For mobile navigation, choose one of:
* - nav-mobile-accordion.css (traditional dropdown/accordion style)
* - nav-mobile-sliding.css (sliding viewport style)
*
* Please review documentation upon first use, and, as-needed:
* TODO: Add documenation link here
*/
/* desktop */
@media screen and (min-width: 62.5rem) {
.nav-main .menu-vdi {
@apply flex items-center justify-end p-0 m-0;
>li {
>a,
>.menu-vdi__toggle {
/* text*/
@apply font-bold text-20px text-black hover:text-light no-underline leading-snug;
/* spacing & display */
@apply mx-4 p-0 no-underline;
}
}
}
}
@media screen and (min-width: 62.5rem) {
.menu-vdi {
.menu-vdi__toggle {
@apply flex items-center gap-2;
}
.menu-vdi__item--parent {
@apply relative;
}
.menu-vdi__submenu {
@apply bg-white shadow-lg left-4 w-64 flex-col;
top: calc(100% + 1rem);
>li {
@apply w-full;
}
.menu-vdi__item {
/* text */
@apply font-bold text-18px text-black hover:text-light no-underline leading-snug;
/* spacing & display */
@apply block w-full;
/* interaction */
@apply focus-visible:bg-secondary-200 hover:bg-secondary-200;
a {
@apply block w-full;
}
}
a.menu-vdi__item,
.menu-vdi__item a {
@apply p-4;
}
}
}
}
+129
View File
@@ -0,0 +1,129 @@
/**
* VDI Navs & Menu - Main Navigation + Mega Menu Styles
*
* Please review documentation upon first use, and, as-needed:
* https://docs.vincentdevelopment.ca/docs/starter-v3-enhancements/navigation/
*/
/* all sizes */
.nav-main .menu-vdi {
.menu-vdi__toggle {
@apply flex items-center gap-2;
}
}
/* mobile */
@media screen and (max-width: 62.5rem) {
.nav-main {
.nav-main__toggle {
/* display */
@apply text-white p-3;
}
.menu-vdi {
@apply flex-col bg-white w-[95%] right-0 z-10 py-6;
top: var(--hgtHeader);
min-height: calc(100vh - var(--hgtHeader));
.menu-vdi__submenu {
@apply py-2 px-7 flex-col;
}
.menu-vdi__item {
a,
button {
/* text */
@apply font-bold text-20px text-black hover:text-light no-underline leading-snug;
/* spacing & display */
@apply block w-full p-4;
/* interaction */
@apply focus-visible:bg-secondary-200 hover:bg-secondary-200;
}
a {
@apply block w-full;
}
button {
@apply flex w-full justify-between;
}
}
}
}
}
/* desktop */
@media screen and (min-width: 62.5rem) {
.nav-main .menu-vdi {
@apply flex items-center justify-end p-0 m-0;
>li {
>a,
>.menu-vdi__toggle {
/* text */
@apply font-bold text-20px text-black hover:text-light no-underline leading-snug;
/* spacing & display */
@apply w-max mx-4 p-0 no-underline;
}
}
.menu-vdi__toggle {
@apply flex items-center gap-2;
}
.menu-vdi__item--parent {
@apply relative;
/* Mega Menu */
&:has(.menu-vdi__item--grandchild) {
@apply static;
}
}
.menu-vdi__submenu {
@apply bg-white shadow-lg left-4 w-64 flex-col;
top: calc(100% + 1rem);
>li {
@apply w-full;
}
.menu-vdi__item {
/* text */
@apply font-bold text-18px text-black hover:text-light no-underline leading-snug;
/* spacing & display */
@apply block w-full;
/* interaction */
@apply focus-visible:bg-secondary-200 hover:bg-secondary-200;
a {
@apply block w-max;
}
}
a.menu-vdi__item,
.menu-vdi__item a {
@apply p-4;
}
/* Mega Menu */
&:has(.menu-vdi__item--grandchild) {
@apply flex-row gap-x-8 right-0 ml-auto mr-0 p-4 w-max;
span {
@apply font-bold text-xl underline w-max;
}
a.menu-vdi__item,
.menu-vdi__item a {
@apply p-0;
}
.menu-vdi__megaCol {
@apply px-0 w-max;
}
}
}
}
}
@@ -0,0 +1,52 @@
/**
* VDI Navs & Menu - Mobile Accordion Navigation
*
* Traditional mobile accordion/dropdown style navigation
* Include this file for accordion-style mobile navigation
*/
/* Mobile accordion navigation */
@media screen and (max-width: 62.5rem) {
.nav-main {
.nav-main__toggle {
/* display */
@apply text-white p-3;
}
.menu-vdi {
@apply flex-col bg-white w-[95%] right-0 z-10 py-6;
@apply absolute hidden; /* Hidden by default */
top: var(--hgtHeader);
min-height: calc(100vh - var(--hgtHeader));
.menu-vdi__submenu {
@apply py-2 px-7 flex-col;
}
.menu-vdi__item {
a,
button {
/* text */
@apply font-bold text-20px text-black hover:text-light no-underline leading-snug;
/* spacing & display */
@apply block w-full p-4;
/* interaction */
@apply focus-visible:bg-secondary-200 hover:bg-secondary-200;
}
a {
@apply block w-full;
}
button {
@apply flex w-full justify-between;
}
}
}
/* Show menu when toggle button is expanded */
.nav-main__toggle[aria-expanded="true"] ~ .menu-vdi:not(.menu-vdi--sliding) {
@apply !flex; /* Use !important to override hidden */
}
}
}
+130
View File
@@ -0,0 +1,130 @@
/**
* VDI Navs & Menu - Mobile Sliding Viewport Navigation
*
* Sliding viewport style for mobile navigation where
* users can navigate through menu levels by sliding between views
* Include this file for sliding-style mobile navigation
*/
/* Mobile sliding viewport navigation */
@media screen and (max-width: 62.5rem) {
.nav-main {
.nav-main__toggle {
/* display */
@apply text-white p-3;
}
.menu-vdi--sliding {
@apply relative overflow-hidden bg-white w-[95%] right-0 z-10 py-6;
@apply absolute hidden; /* Hidden by default */
top: var(--hgtHeader);
min-height: calc(100vh - var(--hgtHeader));
/* Container for all navigation levels */
.menu-vdi__viewport {
@apply flex transition-transform duration-300 ease-in-out;
width: 100%;
}
/* Each navigation level */
.menu-vdi__level {
@apply w-full flex-shrink-0 flex-col;
min-width: 100%;
}
/* Back button for secondary levels */
.menu-vdi__back {
@apply flex items-center gap-2 p-4 border-b border-gray-200 font-bold text-black hover:bg-secondary-200 focus-visible:bg-secondary-200 cursor-pointer;
svg {
@apply w-5 h-5;
}
}
/* Level indicator for context */
.menu-vdi__level-title {
@apply p-4 border-b border-gray-200 font-bold text-lg text-center bg-gray-50;
}
/* Navigation items in sliding mode */
.menu-vdi__item {
a,
button {
/* text */
@apply font-bold text-20px text-black hover:text-light no-underline leading-snug;
/* spacing & display */
@apply block w-full p-4;
/* interaction */
@apply focus-visible:bg-secondary-200 hover:bg-secondary-200;
}
a {
@apply block w-full;
}
button {
@apply flex w-full justify-between items-center;
/* Arrow indicator for items with children */
svg {
@apply w-5 h-5;
}
}
}
/* Hide submenu toggles in sliding mode as they become navigation buttons */
.menu-vdi__toggle {
/* Override the accordion toggle behavior */
&[aria-expanded="true"] {
svg {
@apply rotate-0; /* Don't rotate arrow */
}
+.menu-vdi__submenu {
@apply hidden; /* Don't show dropdown */
}
}
}
/* Hide traditional submenus in sliding mode */
.menu-vdi__submenu {
@apply hidden;
}
/* Ensure menu items in sliding viewport have proper styling */
.menu-vdi__level-items {
@apply flex-col;
.menu-vdi__item {
@apply w-full;
a {
@apply p-4 block w-full no-underline;
}
span {
@apply p-4 block w-full font-bold;
}
}
/* Style nested items that were converted from submenus */
.menu-vdi__nested-items {
@apply block;
.menu-vdi__item {
@apply w-full;
a {
@apply p-4 pl-8 block w-full no-underline; /* Add indent for nested items */
}
}
}
}
}
/* Show sliding menu when toggle button is expanded and menu has sliding class */
.nav-main__toggle[aria-expanded="true"] + .menu-vdi--sliding {
@apply !block; /* Use !important to override hidden */
}
}
}
+22
View File
@@ -0,0 +1,22 @@
/* Tailwind setup */
@import "tailwindcss";
/* Base styles */
@import "./base/index.css";
@import "./navigation/index.css";
/* Lineicons icon font */
@import "./fonts/lineicons.css";
/* Import *-break-out utilities (replicating the non-functional "tailwind-container-break-out" plugin) */
@import "./base/break-out.css";
/* Components */
@import "./components/index.css";
/* Blocks */
@import "./blocks/index.css";
/* Import Tailwind typography plugin */
@plugin "@tailwindcss/typography";
+60
View File
@@ -0,0 +1,60 @@
const { test, expect } = require('@playwright/test');
const AxeBuilder = require('@axe-core/playwright').default;
const domain = 'http://domain.local/';
test.use({
viewport: { width: 1920, height: 1080 },
});
test.describe('site-test', () => {
test('Homepage Test', async ({ page }, testInfo) => {
await page.goto(domain);
await page.screenshot({ path: 'test-results/homepage.png', fullPage: true });
const accessibilityScanResults = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'wcag22a', 'wcag22aa'])
.analyze();
await testInfo.attach('accessibility-scan-results', {
body: JSON.stringify(accessibilityScanResults, null, 2),
contentType: 'application/json'
});
expect(accessibilityScanResults.violations).toEqual([]);
});
test('Blog Index Page Test', async ({ page }, testInfo) => {
await page.goto(`${domain}news/`);
await page.screenshot({ path: 'test-results/blog-index.png', fullPage: true });
const accessibilityScanResults = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'wcag22a', 'wcag22aa'])
.analyze();
await testInfo.attach('accessibility-scan-results', {
body: JSON.stringify(accessibilityScanResults, null, 2),
contentType: 'application/json'
});
expect(accessibilityScanResults.violations).toEqual([]);
});
test('404 Page Test', async ({ page }, testInfo) => {
await page.goto(`${domain}yaya/`);
await page.screenshot({ path: 'test-results/404.png', fullPage: true });
const accessibilityScanResults = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'wcag22a', 'wcag22aa'])
.analyze();
await testInfo.attach('accessibility-scan-results', {
body: JSON.stringify(accessibilityScanResults, null, 2),
contentType: 'application/json'
});
expect(accessibilityScanResults.violations).toEqual([]);
});
});
+184
View File
@@ -0,0 +1,184 @@
{
"$schema": "https://schemas.wp.org/trunk/theme.json",
"version": 3,
"settings": {
"layout": {
"contentSize": "100%",
"wideSize": "1536px"
},
"color": {
"palette": [
{
"slug": "black",
"color": "#000",
"name": "Black"
},
{
"slug": "white",
"color": "#fff",
"name": "White"
},
{
"slug": "theme-bg",
"color": "var(--color-background)",
"name": "Theme Background"
},
{
"slug": "theme-text",
"color": "var(--color-text)",
"name": "Theme Text"
},
{
"slug": "theme-primary",
"color": "var(--color-primary)",
"name": "Theme Primary"
},
{
"slug": "theme-secondary",
"color": "var(--color-secondary)",
"name": "Theme Secondary"
},
{
"slug": "theme-bodylinks",
"color": "var(--color-bodylinks)",
"name": "Theme Body Links"
},
{
"slug": "theme-footerlinks",
"color": "var(--color-footlinks)",
"name": "Theme Footer Links"
},
{
"slug": "theme-success",
"color": "var(--color-success)",
"name": "Theme Success"
},
{
"slug": "theme-warning",
"color": "var(--color-warning)",
"name": "Theme Warning"
},
{
"slug": "theme-danger",
"color": "var(--color-danger)",
"name": "Theme Danger"
},
{
"slug": "theme-info",
"color": "var(--color-info)",
"name": "Theme Info"
}
]
},
"typography": {
"fontFamilies": [
{
"fontFamily": "var(--font-sans)",
"slug": "theme-sans",
"name": "Theme Sans"
}
],
"fontSizes": [
{
"slug": "base",
"size": "var(--text-base)",
"name": "Base"
},
{
"slug": "text-14px",
"size": "var(--text-14px)",
"name": "Text 14px"
},
{
"slug": "text-16px",
"size": "var(--text-16px)",
"name": "Text 16px"
},
{
"slug": "text-18px",
"size": "var(--text-18px)",
"name": "Text 18px"
},
{
"slug": "text-20px",
"size": "var(--text-20px)",
"name": "Text 20px"
},
{
"slug": "text-22px",
"size": "var(--text-22px)",
"name": "Text 22px"
},
{
"slug": "text-25px",
"size": "var(--text-25px)",
"name": "Text 25px"
},
{
"slug": "text-30px",
"size": "var(--text-30px)",
"name": "Text 30px"
},
{
"slug": "text-35px",
"size": "var(--text-35px)",
"name": "Text 35px"
},
{
"slug": "text-38px",
"size": "var(--text-38px)",
"name": "Text 38px"
},
{
"slug": "text-40px",
"size": "var(--text-40px)",
"name": "Text 40px"
},
{
"slug": "text-45px",
"size": "var(--text-45px)",
"name": "Text 45px"
},
{
"slug": "text-50px",
"size": "var(--text-50px)",
"name": "Text 50px"
},
{
"slug": "text-70px",
"size": "var(--text-70px)",
"name": "Text 70px"
},
{
"slug": "text-75px",
"size": "var(--text-75px)",
"name": "Text 75px"
}
],
"lineHeight": true
},
"spacing": {
"padding": true,
"margin": true,
"units": [ "px", "em", "rem", "vh", "vw", "%" ]
}
},
"styles": {
"color": {
"background": "var(--wp--preset--color--background)",
"text": "var(--wp--preset--color--text)"
},
"elements": {
"link": {
"color": {
"text": "var(--wp--preset--color--theme-bodylinks)"
}
}
},
"typography": {
"fontSize": "var(--wp--preset--font-size--base)",
"fontFamily": "var(--wp--preset--font-family--theme-sans)",
"lineHeight": "1.5"
}
}
}
+68
View File
@@ -0,0 +1,68 @@
/* Accordion block styles */
.accordion {
border-radius: 0.25rem;
border: 1px solid var(--color-dark);
width: 100%;
details.accBody {
border-bottom: 1px solid var(--color-dark);
max-height: 3.7rem;
overflow: hidden;
padding: 1rem;
transition: 0.75s max-height ease-in-out;
transition-behavior: allow-discrete;
&:last-of-type { border: none; }
summary.accHeader {
cursor: pointer;
list-style-type: none;
padding-right: 2rem;
position: relative;
&::-webkit-details-marker { display: none; }
h2 {
color: var(--color-dark);
cursor: pointer;
font-size: 1.25rem;
font-weight: 500;
margin: 0;
}
svg.marker {
color: var(--color-dark);
fill: var(--color-dark);
height: 1rem;
position: absolute;
right: 0;
top: 0.25rem;
width: 1.25rem;
}
}
div.accContent {
margin-top: 1rem;
}
&[open] {
background-color: var(--color-dark);
border-color: var(--color-light);
color: var(--color-light);
max-height: 20rem;
transition: 0.75s max-height ease-in-out;
transition-behavior: allow-discrete;
summary.accHeader {
h2 { color: var(--color-light); }
svg.marker {
color: var(--color-light);
fill: var(--color-light);
transform: rotate(45deg);
}
}
}
}
}
+44
View File
@@ -0,0 +1,44 @@
<?php
/**
* Block Name: Accordion
*
* This is the template that renders the Accordion block.
*
* @package BasicWP
*/
namespace BasicWP;
$open = get_field( 'open' );
$group = get_field( 'group_items' );
$accItems = get_field( 'accordion_items' );
if ( $accItems ) :
$classes = 'accordion';
$wrapper = blockWrapperAttributes( $classes, $is_preview );
?>
<section <?php echo wp_kses_post( $wrapper ); ?>>
<?php foreach ( $accItems as $index => $item ) : ?>
<?php
$itemID = 'accordion-' . ( $index + 1 );
$isOpen = ( $index === 0 && $open ) ? 'open' : '';
?>
<details <?php echo esc_attr( $group ) ? 'name="' . esc_attr( $group ) . '"' : ''; ?> <?php echo esc_attr( $isOpen ); ?> class="accBody">
<summary class="accHeader">
<h2><?php echo esc_html( $item['title'] ); ?></h2>
<svg xmlns="http://www.w3.org/2000/svg" class="marker" fill="none" height="1rem" width="1rem" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5" aria-labelledby="<?php echo esc_attr( $itemID ); ?>-title <?php echo esc_attr( $itemID ); ?>-desc">
<title id="<?php echo esc_attr( $itemID ); ?>-title">Open icon</title>
<desc id="<?php echo esc_attr( $itemID ); ?>-desc">icon that represents the state of the summary</desc>
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4v16m8-8H4" />
</svg>
</summary>
<div id="<?php echo esc_attr( $itemID ); ?>" class="accContent">
<?php echo wp_kses_post( $item['content'] ); ?>
</div>
</details>
<?php endforeach; ?>
</section>
<?php endif; ?>
+27
View File
@@ -0,0 +1,27 @@
{
"name": "acf/accordion",
"title": "Accordion",
"description": "Accordion block built with details and summary elements.",
"style": [
"file:./accordion.css"
],
"category": "vdi-block",
"icon": "block-default",
"keywords": [
"accordion",
"faq"
],
"acf": {
"mode": "preview",
"renderTemplate": "accordion.php"
},
"supports": {
"align": true,
"anchor": false,
"color": true,
"html": true,
"jsx": true,
"mode": true,
"multiple": true
}
}
+26
View File
@@ -0,0 +1,26 @@
{
"name": "acf/boilerplate",
"title": "Block Boilerplate",
"description": "Boilerplate code to create ACF blocks.",
"style": [
"file:./boilerplate.css"
],
"category": "vdi-blocks",
"icon": "block-default",
"keywords": [
"boilerplate"
],
"acf": {
"mode": "preview",
"renderTemplate": "boilerplate.php"
},
"supports": {
"align": true,
"anchor": true,
"color": true,
"html": false,
"jsx": false,
"mode": true,
"multiple": false
}
}

Some files were not shown because too many files have changed in this diff Show More