--- /dev/null
+<?php
+/**
+ * Plugin Name: Custom theme features
+ * Description: Loads all custom functionality such as new post types and taxonomies.
+ * Version: 1.0
+ * Author: Cubedesigners
+ * Author URI: https://www.cubedesigners.com
+ */
+
+namespace Cube;
+
+defined('ABSPATH') or die('Direct access blocked');
+
+// Folder name for all plugin files (needed because MU-plugins can't be in folders so we need to put the loader outside)
+$plugin_dir = 'cube';
+$autoloader = __DIR__ . "/$plugin_dir/vendor/autoload.php";
+
+// Namespaced constants for easier path and URL references
+define(__NAMESPACE__ . '\NS', __NAMESPACE__ . '\\'); // Namespace shortcut: NS
+define(NS . 'PLUGIN_PATH', trailingslashit(plugin_dir_path(__FILE__) . $plugin_dir));
+define(NS . 'PLUGIN_URL', trailingslashit(plugin_dir_url(__FILE__) . $plugin_dir));
+
+// Load Composer libraries
+if (file_exists($autoloader)) {
+ require_once $autoloader;
+}
+
+if (class_exists(NS . 'Init')) {
+ Init::register_classes();
+}
--- /dev/null
+{
+ "require": {
+
+ },
+ "autoload": {
+ "psr-4": {
+ "Cube\\": "./src"
+ }
+ }
+}
--- /dev/null
+<?php
+
+namespace Cube\Common;
+
+class Setup {
+
+ /**
+ * Common setup, will be automatically triggered by Init class if included
+ */
+ public function register() {
+
+ }
+
+}
--- /dev/null
+<?php
+
+namespace Cube\Elementor;
+
+use Elementor\Element_Base;
+use Elementor\Controls_Manager;
+use Elementor\Plugin;
+
+class Setup {
+
+ public function register() {
+
+ // Customise Elementor "section" element to allow layout options (via CSS classes applied to main section element)
+ $this->register_customisations();
+
+ // Register widget category with Elementor
+ add_action('elementor/elements/categories_registered', [$this, 'register_widgets_category']);
+
+ // Register widgets with Elementor
+ add_action('elementor/widgets/widgets_registered', [$this, 'register_widgets']);
+ }
+
+
+ public function register_customisations() {
+ $this->_customise_image_box_widget();
+ }
+
+ // Ref: https://developers.elementor.com/widget-categories/
+ /* @var $manager \Elementor\Elements_Manager */
+ public function register_widgets_category($manager) {
+ $manager->add_category('cube', ['title' => 'Cubedesigners']);
+ }
+
+
+ public function register_widgets() {
+
+ $elementor = Plugin::$instance;
+
+ $elementor->widgets_manager->register_widget_type( new Widgets\SolutionGrid() );
+ }
+
+ protected function _customise_image_box_widget() {
+
+ // Add controls to image box widget in editor
+ add_action( 'elementor/element/image-box/section_image/before_section_end', function( $element, $args ) {
+
+ /** @var \Elementor\Element_Base $element */
+ $element->add_control(
+ 'content_alignment',
+ [
+ 'label' => __( 'Vertical Alignment', 'cube' ),
+ 'type' => Controls_Manager::SELECT,
+ 'options' => [
+ 'flex-start' => __( 'Top', 'cube' ),
+ 'center' => __( 'Center', 'cube' ),
+ 'flex-end' => __( 'Bottom', 'cube' ),
+ ],
+ 'default' => 'center',
+ 'selectors' => [
+ '{{WRAPPER}} .elementor-image-box-wrapper' => 'align-items: {{VALUE}};',
+ ],
+ ]
+ );
+ }, 10, 2);
+
+ }
+
+}
--- /dev/null
+<?php
+
+namespace Cube\Elementor\Widgets;
+
+use Elementor\Controls_Manager;
+use Elementor\Utils;
+
+use function Roots\view;
+
+
+class SolutionGrid extends _Base {
+
+ // Widget name / ID
+ public function get_name() {
+ return 'cube-solution-grid';
+ }
+
+ // Elementor widget title
+ public function get_title() {
+ return __( 'Solution Grid', 'cube' );
+ }
+
+ // Elementor interface icon
+ public function get_icon() {
+ return 'eicon-posts-grid';
+ }
+
+ /**
+ * List of scripts the widget depends on.
+ * Used to set scripts dependencies required to run the widget.
+ *
+ * @since 1.0.0
+ * @access public
+ * @return array Widget scripts dependencies.
+ */
+ public function get_script_depends() {
+ return [];
+ }
+ /**
+ * Register the widget controls.
+ * Adds different input fields to allow the user to change and customize the widget settings.
+ *
+ * @since 1.0.0
+ * @access protected
+ */
+ protected function _register_controls() {
+
+ $this->start_controls_section(
+ 'section_content',
+ [
+ 'label' => __( 'Solution Grid', 'cube' ),
+ ]
+ );
+
+ $this->add_control(
+ 'items',
+ [
+ 'label' => __( 'Enigmas', 'cube' ),
+ 'type' => Controls_Manager::REPEATER,
+ 'fields' => [
+ [
+ 'name' => 'puzzle',
+ 'label' => __('Enigma', 'cube'),
+ 'label_block' => true,
+ 'type' => Controls_Manager::MEDIA,
+ 'default' => [
+ 'url' => Utils::get_placeholder_image_src(),
+ ],
+ ],
+ [
+ 'name' => 'solution',
+ 'label' => __('Solution', 'cube'),
+ 'label_block' => true,
+ 'type' => Controls_Manager::MEDIA,
+ 'default' => [
+ 'url' => Utils::get_placeholder_image_src(),
+ ],
+ ],
+ ],
+ 'title_field' => '<# var pName = puzzle.url.split("/").pop().split(".").shift(); sName = solution.url.split("/").pop().split(".").shift(); #><div style="margin:1.25em 0;display:grid;grid-gap:1em;grid-template-columns:repeat(2, 1fr);text-align:center;"><div><img src="{{{ puzzle.url }}}" style="display:block;margin-bottom:0.5em">{{{ pName }}}</div><div><img src="{{{ solution.url }}}" style="display:block;margin-bottom:0.5em">{{{ sName }}}</div></div>',
+ ]
+ );
+ $this->end_controls_section();
+
+ $this->common_controls();
+ }
+ /**
+ * Render the widget output on the frontend.
+ * Written in PHP and used to generate the final HTML.
+ *
+ * @since 1.0.0
+ * @access protected
+ */
+ protected function render() {
+ $ID = $this->get_id();
+ $items = $this->get_settings('items');
+ echo view('widgets/solution-grid', compact('ID', 'items'));
+ }
+}
--- /dev/null
+<?php
+
+namespace Cube\Elementor\Widgets;
+
+use Elementor\Widget_Base;
+use Elementor\Controls_Manager;
+
+abstract class _Base extends Widget_Base {
+
+ // Where to display the widgets in the Elementor interface
+ // Ref: https://developers.elementor.com/widget-categories/
+ // See Elementor/Setup.php for definition of the category
+ public function get_categories() {
+ return ['cube'];
+ }
+
+ // Wrapper for including all common controls
+ // In the future this might be multiple groups of controls
+ // Most widgets will use this but some might selectively include controls as needed.
+ public function common_controls() {
+ $this->spacing_controls();
+ }
+
+ // Common widget spacing controls
+ public function spacing_controls() {
+
+ $this->start_controls_section(
+ 'section_spacing',
+ [
+ 'label' => __( 'Spacing', 'cube' ),
+ ]
+ );
+
+ $this->add_control(
+ 'margin_top',
+ [
+ 'label' => __('Disable margin top', 'cube'),
+ 'type' => Controls_Manager::SWITCHER,
+ 'default' => '',
+ 'return_value' => 'mt-0!',
+ 'prefix_class' => '',
+ ]
+ );
+
+ $this->add_control(
+ 'margin_bottom',
+ [
+ 'label' => __('Disable margin bottom', 'cube'),
+ 'type' => Controls_Manager::SWITCHER,
+ 'default' => '',
+ 'return_value' => 'mb-0!',
+ 'prefix_class' => '',
+ ]
+ );
+
+ $this->add_control(
+ 'padding_top',
+ [
+ 'label' => __('Top Padding', 'cube'),
+ 'type' => Controls_Manager::SELECT,
+ 'options' => [
+ '' => __( 'Défaut', 'cube' ),
+ 'pt-0' => '0',
+ 'pt-1v' => '2.5%',
+ 'pt-2v' => '5%',
+ 'pt-3v' => '7.5%',
+ 'pt-4v' => '10%',
+ ],
+ 'default' => '',
+ 'prefix_class' => '', // Use the full value as the classname
+ 'separator' => 'before',
+ ]
+ );
+
+ $this->add_control(
+ 'padding_bottom',
+ [
+ 'label' => __('Bottom Padding', 'cube'),
+ 'type' => Controls_Manager::SELECT,
+ 'options' => [
+ '' => __( 'Défaut', 'cube' ),
+ 'pb-0' => '0',
+ 'pb-1v' => '2.5%',
+ 'pb-2v' => '5%',
+ 'pb-3v' => '7.5%',
+ 'pb-4v' => '10%',
+ ],
+ 'default' => '',
+ 'prefix_class' => '', // Use the full value as the classname
+ ]
+ );
+
+ $this->end_controls_section();
+
+ }
+
+}
--- /dev/null
+<?php
+
+// Setup reference: https://www.youtube.com/watch?v=NdDRNiIfYDw
+
+namespace Cube;
+
+
+final class Init { // Marked as final because this class should never be extended (single init only!)
+
+ /**
+ * List of service classes to be used
+ * @return array Array of classes to be instantiated
+ */
+ public static function classes() {
+ return [
+ Common\Setup::class,
+ Elementor\Setup::class,
+ ];
+ }
+
+ /**
+ * Register all services (custom post types, custom fields, shortcodes etc)
+ */
+ public static function register_classes() {
+ foreach (self::classes() as $class) {
+ $service = self::instantiate($class);
+
+ // If the class has a register method, call it.
+ if (method_exists($service, 'register')) {
+ $service->register();
+ }
+ }
+ }
+
+ /**
+ * @param class $class Class from the services array
+ * @return class instance New instance of the class
+ */
+ private static function instantiate($class) {
+ return new $class();
+ }
+}
@import 'common/setup'
@import 'common/mixins'
@import 'common/debug'
+@import 'common/layout'
// Extracted components or custom styles that can't be done with utilities
@import 'common/global'
-//@import 'components/*'
+@import 'components/*'
+@import 'widgets/*'
//@import 'pages/*'
+// Allow spacing classes to override others defined here
+@import 'common/spacing'
+
// Utilities go last in source order so they can
// override other classes with the same specificity
@tailwind utilities
opacity: 0.2 !important
html
- lost-utility: overlay ($content-max-width) 1 10px theme('colors.green.700');
+ lost-utility: overlay ($base-width) 1 10px theme('colors.yellow.500');
body
- lost-utility: overlay ($base-width * 0.8) 1 2px theme('colors.blue.300');
+ lost-utility: overlay ($content-max-width * 0.9) 1 2px theme('colors.blue.500');
// Centre line
&:after
p:not(:last-child)
margin-bottom: 1.5em
-// VueJS styling to hide elements until they're ready
-[v-cloak]
- visibility: hidden
+
+//-- Elementor Lightbox customisations
+.dialog-type-lightbox
+ background-color: rgba(#0c2448, 0.8)
+
+.elementor-lightbox
+ .elementor-swiper-button
+ &-next, &-prev
+ width: 10%
--- /dev/null
+// Make sure widget has height when column flex align-items is set
+// This is for widgets that don't take up height on their own, like
+// the flexible background image. Ideally the parent column shouldn't
+// have an alignment set (so it stretches) but this doesn't seem to do any
+// harm and it's more intuitive behaviour...
+.elementor-widget-wrap
+ height: 100%
+ align-content: center
+
+.container,
+.elementor-section-boxed > .elementor-container
+ center($content-max-width)
+ horizontal-spacing(5vw)
+
+ // Nested containers should have no padding
+ .elementor-container, &.elementor-column-gap-no
+ padding-left: 0
+ padding-right: 0
+
+//.elementor-section-wrap > .elementor-section
+// vertical-spacing()
+
+// Override default spacing between widgets
+.elementor-widget-wrap .elementor-widget:not(:last-child)
+ constrain(margin-bottom, $vertical-gutter) // Set margin bottom to standard gutter
+
+// Get rid of default 10px padding around elements
+.elementor-column-gap-default > .elementor-row > .elementor-column > .elementor-element-populated
+ padding: 0
+
+.elementor-section-boxed > .elementor-column-gap-extended
+ horizontal-spacing(-1.5vw, margin) // Negative margins to pull back horizontal padding added to child columns below
+ > .elementor-row > .elementor-column > .elementor-element-populated
+ horizontal-spacing(1.5vw)
+ padding-top: 0
+ padding-bottom: 0
+
+.elementor-section-boxed > .elementor-column-gap-wide
+ horizontal-spacing(-2.5vw, margin) // Negative margins to pull back horizontal padding added to child columns below
+ > .elementor-row > .elementor-column > .elementor-element-populated
+ horizontal-spacing(2.5vw)
+ padding-top: 0
+ padding-bottom: 0
+
+// Once columns have wrapped, remove padding for all
++below($breakpoint-columns)
+ .elementor-container > .elementor-row > .elementor-column > .elementor-element-populated
+ padding: 0 !important
// Normally fractions are based on the container/screen width but we need to cap the value
// on screens that are wider than the $max-content-width, otherwise value will become
// disproportionally large. This is key to making the max-width layout work and scale.
-constrain(property, value = $horizontal-gutter, max-width = $base-width) {
+constrain(property, value = $horizontal-gutter, max-width = $base-width, min-width = 500px) {
{property}: value
+above(max-width) {
}
}
- // Prevent vw / % units from getting too small
- +below(700px) {
- // Only do this if we are dealing with a percentage or vw unit
- if (unit(value) is '%' || unit(value) is 'vw') {
- $min = 700px * unit(value / 100, '') // Convert percentage to a decimal
- {property}: round($min)
+ // Prevent vw / % units from getting too small if set
+ if (min-width) {
+ +below(min-width) {
+ // Only do this if we are dealing with a percentage or vw unit
+ if (unit(value) is '%' || unit(value) is 'vw') {
+ $min = min-width * unit(value / 100, '') // Convert percentage to a decimal
+ {property}: round($min)
+ }
}
}
}
outlines: false,
}
-$base-width = 1920px // Basis for vw unit calculations on large screens
-$content-max-width = $base-width * 0.9 // Allows 5% either side
+$base-width = 1680px // Basis for vw unit calculations on large screens
+$content-max-width = $base-width // 5% padding on left and right will be added
// Gutters (assumed to always be a vw, vh or % unit)
$horizontal-gutter = 5vw
--- /dev/null
+// Apply spacing between elements but not before first one
+// Ref: https://alistapart.com/article/axiomatic-css-and-lobotomized-owls/
+@responsive
+ .spaced > * + *
+ margin-top: 1.5em
+ .spaced-lg > * + *
+ margin-top: 2em
+ .spaced-none > * + *
+ margin-top: 0
+
+ .spaced-horizontal > * + *
+ margin-left: 0.75em
+
+// Generate utility classes for padding/margin based on vw units
+// This will generate classes like pt-1v, pt-2v, pr-1v etc.
+
+$vw-spacing = {
+ '1': 2.5vw,
+ '2': 5vw,
+ '3': 7.5vw,
+ '4': 10vw,
+ '5': 12.5vw,
+}
+
+$sides = {
+ t: top,
+ r: right,
+ b: bottom,
+ l: left,
+ x: horizontal, // special case for both left & right padding
+ y: vertical, // special case for both top & bottom padding
+}
+
+// Generate classes with responsive variations
+@responsive
+ for counter, vwAmount in $vw-spacing
+
+ // First, generate classes for all sides + grid gaps...
+
+ // Padding (p-1v, p-2v etc)
+ .p-{counter}v
+ constrain(padding, vwAmount)
+
+ // Margins (m-1v, m-2v etc)
+ .m-{counter}v
+ constrain(margin, vwAmount)
+
+ // Negative margins (-m-1v, -m-2v etc)
+ .-m-{counter}v
+ constrain(margin, - vwAmount)
+
+
+ // Next, generate classes for individual sides
+ for sideAbbreviation, side in $sides
+
+ // Padding utilities (pt, pr, pb, pl, px, py)
+ .p{sideAbbreviation}-{counter}v
+ $property = s('padding-%s', side)
+
+ if (side is 'horizontal')
+ horizontal-spacing(vwAmount)
+ else if (side is 'vertical')
+ vertical-spacing(vwAmount)
+ else
+ constrain($property, vwAmount)
+
+ // Margin utilities (mt, mr, mb, ml, mx, my)
+ .m{sideAbbreviation}-{counter}v
+ $property = s('margin-%s', side)
+
+ if (side is 'horizontal')
+ horizontal-spacing(vwAmount, 'margin')
+ else if (side is 'vertical')
+ vertical-spacing(vwAmount, 'margin')
+ else
+ constrain($property, vwAmount)
+
+ // Negative margin utilities (-mt, -mr, -mb, -ml, -mx, -my)
+ .-m{sideAbbreviation}-{counter}v
+ $property = s('margin-%s', side)
+
+ if (side is 'horizontal')
+ horizontal-spacing( - vwAmount, 'margin')
+ else if (side is 'vertical')
+ vertical-spacing( - vwAmount, 'margin')
+ else
+ constrain($property, - vwAmount)
--- /dev/null
+.header-logo
+ @apply block mx-auto
+ constrain(margin-top, 7vw, $base-width, false) // No minimum cap on margin so it to matches body bg scaling
+ constrain(margin-bottom, 10vw, $base-width, false)
+ max-width: percentage(762/1512) // logo width / content width
+
--- /dev/null
+.solution-grid
+ display: grid
+ grid-template-columns: repeat(4, 1fr)
+ grid-column-gap: 4.15%
+ constrain(grid-row-gap, 3vw, $base-width, false)
+
+ +below(768px)
+ grid-template-columns: repeat(3, 1fr)
+ +below(500px)
+ grid-template-columns: repeat(2, 1fr)
+
+ img
+ border-radius: 1vw
+
+ +above($content-max-width)
+ border-radius: 16px
-<footer>
+<footer class="mt-3v mb-1v text-xs text-center">
<div class="container">
@php(dynamic_sidebar('sidebar-footer'))
</div>
<header class="banner">
<div class="container">
<a class="brand" href="{{ home_url('/') }}">
- <img class="block mx-auto mt-10"
- style="max-width: 50%"
- src="@asset('images/logo-STR.png')"
- alt="{{ $siteName }}">
+ <img class="header-logo" src="@asset('images/logo-STR.png')" alt="{{ $siteName }}">
</a>
+ {{--
<nav class="nav-primary">
@if (has_nav_menu('primary_navigation'))
{!! wp_nav_menu(['theme_location' => 'primary_navigation', 'menu_class' => 'nav', 'echo' => false]) !!}
@endif
</nav>
+ --}}
</div>
</header>
+{{--
<div class="page-header">
<h1>{!! $title !!}</h1>
</div>
+--}}
--- /dev/null
+<div class="solution-grid">
+ @foreach($items as $item)
+ <a href="{{ $item['solution']['url'] }}" data-elementor-lightbox-slideshow="{{ $ID }}">
+ <img class="solution-grid-thumbnail" src="{{ $item['puzzle']['url'] }}">
+ </a>
+ @endforeach
+</div>
// Tailwind plugins
const { wordpressUtilities } = require('tailwindcss-wordpress');
+const { PWD } = process.env;
module.exports = {
theme: {
corePlugins: {
container: false,
},
+ purge: {
+ content: [
+ `${PWD}/*.php`,
+ `${PWD}/resources/views/**/*.blade.php`,
+ `${PWD}/resources/assets/scripts/**/*.{js,vue}`,
+ `${PWD}/../../mu-plugins/cube/**/*.php`,
+ ],
+ },
};
mix.stylus('resources/assets/styles/editor.styl', 'styles');
-// Remove unused styles in production
-mix.purgeCss({
- content: [
- './resources/views/**/*.php',
- './resources/assets/**/*.js',
- ],
- defaultExtractor: content => content.match(/[A-Za-z0-9-_:/]+/g) || [],
-
- /*
- | Add classes, IDs, or selectors that which do not exist in your views or
- | scripts. By default, We have whitelisted classes generated by WordPress.
- */
- whitelist: require('purgecss-with-wordpress').whitelist,
- whitelistPatterns: require('purgecss-with-wordpress').whitelistPatterns,
-});
-
mix.js('resources/assets/scripts/app.js', 'scripts')
.js('resources/assets/scripts/customizer.js', 'scripts')
.blocks('resources/assets/scripts/editor.js', 'scripts')