@getvitops/generator 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/README.md +35 -0
  2. package/assets/bricks/elements/carousel.php +89 -0
  3. package/assets/bricks/elements/centered.php +128 -0
  4. package/assets/bricks/elements/color-scheme-toggle.php +61 -0
  5. package/assets/bricks/elements/copy-button.php +65 -0
  6. package/assets/bricks/elements/dismissable.php +93 -0
  7. package/assets/bricks/elements/entries.php +96 -0
  8. package/assets/bricks/elements/image-compare.php +132 -0
  9. package/assets/bricks/elements/multi-field.php +148 -0
  10. package/assets/bricks/elements/sitenav.php +298 -0
  11. package/assets/bricks/elements/split-link.php +170 -0
  12. package/assets/bricks/elements/split-panel.php +155 -0
  13. package/assets/bricks/elements/split.php +83 -0
  14. package/assets/bricks/load.php +129 -0
  15. package/assets/css/animation.css +526 -0
  16. package/assets/css/global.css +17 -0
  17. package/assets/css/index.css +58 -0
  18. package/assets/css/layout.css +723 -0
  19. package/assets/css/patterns/anchor-link.css +66 -0
  20. package/assets/css/patterns/banner.css +64 -0
  21. package/assets/css/patterns/carousel.css +269 -0
  22. package/assets/css/patterns/cluster.css +64 -0
  23. package/assets/css/patterns/code.css +17 -0
  24. package/assets/css/patterns/combobox.css +97 -0
  25. package/assets/css/patterns/comment.css +88 -0
  26. package/assets/css/patterns/copy.css +82 -0
  27. package/assets/css/patterns/details.css +87 -0
  28. package/assets/css/patterns/dialog.css +99 -0
  29. package/assets/css/patterns/drawer.css +108 -0
  30. package/assets/css/patterns/dropdown.css +45 -0
  31. package/assets/css/patterns/forms.css +335 -0
  32. package/assets/css/patterns/grouped.css +86 -0
  33. package/assets/css/patterns/horizontal-scroll.css +40 -0
  34. package/assets/css/patterns/icon.css +72 -0
  35. package/assets/css/patterns/lightbox.css +156 -0
  36. package/assets/css/patterns/list.css +272 -0
  37. package/assets/css/patterns/marquee.css +68 -0
  38. package/assets/css/patterns/masonry.css +40 -0
  39. package/assets/css/patterns/media.css +70 -0
  40. package/assets/css/patterns/nav.css +241 -0
  41. package/assets/css/patterns/notification.css +114 -0
  42. package/assets/css/patterns/overlay.css +28 -0
  43. package/assets/css/patterns/popover.css +68 -0
  44. package/assets/css/patterns/pull-quote.css +71 -0
  45. package/assets/css/patterns/scroll-based.css +227 -0
  46. package/assets/css/patterns/scroll-target.css +124 -0
  47. package/assets/css/patterns/separator.css +130 -0
  48. package/assets/css/patterns/sitenav.css +582 -0
  49. package/assets/css/patterns/split-link.css +113 -0
  50. package/assets/css/patterns/splitter.css +212 -0
  51. package/assets/css/patterns/stack.css +67 -0
  52. package/assets/css/patterns/sticky.css +127 -0
  53. package/assets/css/patterns/subgrid.css +178 -0
  54. package/assets/css/patterns/svg.css +166 -0
  55. package/assets/css/patterns/table.css +106 -0
  56. package/assets/css/patterns/tag.css +75 -0
  57. package/assets/css/patterns/text-effects.css +317 -0
  58. package/assets/css/patterns/tooltip.css +68 -0
  59. package/assets/css/patterns/tree.css +126 -0
  60. package/assets/css/utilities.css +351 -0
  61. package/assets/js/deferred.js +1 -0
  62. package/assets/js/elements.js +887 -0
  63. package/assets/js/polyfills/adoptedStyleSheets.js +1 -0
  64. package/assets/js/polyfills/css-anchor-positioning-fn.js +17 -0
  65. package/assets/js/polyfills/global.esm.js +1 -0
  66. package/assets/js/polyfills/popover.js +63 -0
  67. package/assets/js/polyfills/template-shadowroot.js +24 -0
  68. package/assets/js/polyfills/webcomponents-bundle.js +27 -0
  69. package/assets/js/polyfills.js +1 -0
  70. package/dist/index.d.mts +1425 -0
  71. package/dist/index.d.mts.map +1 -0
  72. package/dist/index.mjs +2611 -0
  73. package/dist/index.mjs.map +1 -0
  74. package/package.json +33 -0
  75. package/schema.json +508 -0
@@ -0,0 +1,170 @@
1
+ <?php
2
+ /**
3
+ * Vitops — Split Link element (Bricks Builder).
4
+ *
5
+ * A "split button": a primary <a> flush with a <button> that toggles an anchored
6
+ * [popover] holding secondary, nestable content. Pure native platform — the toggle
7
+ * is a Popover API invoker (`popovertarget`, which the browser also wires to
8
+ * `aria-expanded`/`aria-details` for free), and the panel is placed with CSS Anchor
9
+ * Positioning. No Lit component; the framework's split-link.css does the styling.
10
+ *
11
+ * Placement is authored via the "Placement" control, which writes a `position-area`
12
+ * value (logical keywords) to `--split-link-area` on the root; it inherits down to
13
+ * the panel. Each instance gets a unique anchor-name derived from the element id.
14
+ *
15
+ * Nestable: the popover's children are authored in the canvas. A live [popover] is
16
+ * display:none, so in the builder the panel renders WITHOUT the `popover` attribute
17
+ * (as `.split-link__panel--editing`) to stay visible and droppable; on the frontend
18
+ * it is a real top-layer popover.
19
+ *
20
+ * Owned by the framework repo (vitops: bricks/elements/); copied into the theme's
21
+ * dist/bricks/ on build — do not hand-edit in the theme.
22
+ */
23
+
24
+ if ( ! defined( 'ABSPATH' ) ) {
25
+ exit;
26
+ }
27
+
28
+ class Vitops_Element_Split_Link extends \Bricks\Element {
29
+ public $category = 'vitops';
30
+ public $name = 'vitops-split-link';
31
+ public $icon = 'ti-direction-alt';
32
+ public $css_selector = '.split-link';
33
+ public $nestable = true;
34
+ public $vue_component = 'bricks-nestable';
35
+
36
+ public function get_label() {
37
+ return esc_html__( 'Split Link', 'bricks' );
38
+ }
39
+
40
+ public function get_keywords() {
41
+ return array( 'split', 'link', 'button', 'popover', 'dropdown', 'menu', 'vitops' );
42
+ }
43
+
44
+ public function set_controls() {
45
+ $this->controls['linkText'] = array(
46
+ 'tab' => 'content',
47
+ 'label' => esc_html__( 'Link text', 'bricks' ),
48
+ 'type' => 'text',
49
+ 'default' => esc_html__( 'View', 'bricks' ),
50
+ );
51
+
52
+ $this->controls['link'] = array(
53
+ 'tab' => 'content',
54
+ 'label' => esc_html__( 'Link', 'bricks' ),
55
+ 'type' => 'link',
56
+ );
57
+
58
+ $this->controls['toggleLabel'] = array(
59
+ 'tab' => 'content',
60
+ 'label' => esc_html__( 'Toggle label (accessible name)', 'bricks' ),
61
+ 'type' => 'text',
62
+ 'placeholder' => esc_html__( 'More options', 'bricks' ),
63
+ 'description' => esc_html__( 'aria-label for the popover toggle button.', 'bricks' ),
64
+ );
65
+
66
+ $this->controls['caret'] = array(
67
+ 'tab' => 'content',
68
+ 'label' => esc_html__( 'Toggle glyph', 'bricks' ),
69
+ 'type' => 'text',
70
+ 'placeholder' => '▾',
71
+ 'description' => esc_html__( 'Character/HTML inside the toggle. It flips 180° while the panel is open.', 'bricks' ),
72
+ );
73
+
74
+ // Placement writes a position-area value (logical keywords) onto the root,
75
+ // where it inherits down to the panel. TODO: replace this select with a
76
+ // visual grid control — position-area is richer than a 3×3 grid (span-*
77
+ // keywords), so the picker must model spans, not just the 9 corners/edges.
78
+ $this->controls['placement'] = array(
79
+ 'tab' => 'content',
80
+ 'label' => esc_html__( 'Placement', 'bricks' ),
81
+ 'type' => 'select',
82
+ 'inline' => true,
83
+ 'options' => array(
84
+ 'block-start span-inline-end' => esc_html__( 'Top · start-aligned', 'bricks' ),
85
+ 'block-start center' => esc_html__( 'Top · center', 'bricks' ),
86
+ 'block-start span-inline-start' => esc_html__( 'Top · end-aligned', 'bricks' ),
87
+ 'inline-start center' => esc_html__( 'Inline-start (left)', 'bricks' ),
88
+ 'center' => esc_html__( 'Center (over trigger)', 'bricks' ),
89
+ 'inline-end center' => esc_html__( 'Inline-end (right)', 'bricks' ),
90
+ 'block-end span-inline-end' => esc_html__( 'Bottom · start-aligned', 'bricks' ),
91
+ 'block-end center' => esc_html__( 'Bottom · center', 'bricks' ),
92
+ 'block-end span-inline-start' => esc_html__( 'Bottom · end-aligned', 'bricks' ),
93
+ ),
94
+ 'default' => 'block-end span-inline-end',
95
+ 'css' => array( array( 'property' => '--split-link-area', 'selector' => '' ) ),
96
+ );
97
+
98
+ $this->controls['gap'] = array(
99
+ 'tab' => 'content',
100
+ 'label' => esc_html__( 'Panel gap', 'bricks' ),
101
+ 'type' => 'number',
102
+ 'units' => true,
103
+ 'placeholder' => '0.5rem',
104
+ 'css' => array( array( 'property' => '--split-link-gap', 'selector' => '' ) ),
105
+ );
106
+
107
+ $this->controls['_cssClasses']['default'] = 'split-link';
108
+ }
109
+
110
+ // Seed one block so authors have a target to drop popover content into.
111
+ public function get_nestable_children() {
112
+ return array(
113
+ array( 'name' => 'block', 'label' => esc_html__( 'Popover content', 'bricks' ) ),
114
+ );
115
+ }
116
+
117
+ // Builder canvas render() runs in the iframe; a live [popover] would be hidden
118
+ // there, so we reveal the panel while editing. Defensive: any true signal means
119
+ // builder; otherwise treat as frontend (the safe default — a false negative only
120
+ // makes the hidden content harder to reach via the canvas, not broken).
121
+ private function is_in_builder() {
122
+ foreach ( array( 'bricks_is_builder_iframe', 'bricks_is_builder_call', 'bricks_is_builder' ) as $fn ) {
123
+ if ( function_exists( $fn ) && $fn() ) {
124
+ return true;
125
+ }
126
+ }
127
+ return false;
128
+ }
129
+
130
+ public function render() {
131
+ $s = $this->settings;
132
+ $id = $this->id;
133
+ $pop_id = 'sl-' . $id;
134
+ $anchor = '--sl-' . $id;
135
+
136
+ // Unique anchor-name on the wrapper; the panel points at it (top-layer
137
+ // popovers can't be contained by a positioned ancestor).
138
+ $this->set_attribute( '_root', 'style', "anchor-name:{$anchor};" );
139
+
140
+ $link_text = isset( $s['linkText'] ) && '' !== $s['linkText'] ? $s['linkText'] : esc_html__( 'View', 'bricks' );
141
+ $toggle_label = ! empty( $s['toggleLabel'] ) ? $s['toggleLabel'] : esc_html__( 'More options', 'bricks' );
142
+ $caret = isset( $s['caret'] ) && '' !== $s['caret'] ? $s['caret'] : '&#9662;'; // ▾
143
+
144
+ // Primary link — Bricks resolves href/target/rel from the link control.
145
+ if ( ! empty( $s['link'] ) ) {
146
+ $this->set_link_attributes( '_link', $s['link'] );
147
+ }
148
+ $link_attrs = $this->render_attributes( '_link' );
149
+
150
+ // Builder: drop the `popover` attribute so nested content stays visible.
151
+ $editing = $this->is_in_builder();
152
+ $panel_class = 'split-link__panel' . ( $editing ? ' split-link__panel--editing' : '' );
153
+ $popover_attr = $editing ? '' : 'popover';
154
+
155
+ echo "<div {$this->render_attributes( '_root' )}>";
156
+
157
+ echo "<a class=\"split-link__link\" {$link_attrs}>" . esc_html( $link_text ) . '</a>';
158
+
159
+ echo '<button type="button" class="split-link__toggle" popovertarget="' . esc_attr( $pop_id ) . '"'
160
+ . ' aria-label="' . esc_attr( $toggle_label ) . '">'
161
+ . '<span class="split-link__caret" aria-hidden="true">' . $caret . '</span>'
162
+ . '</button>';
163
+
164
+ echo "<div id=\"{$pop_id}\" {$popover_attr} class=\"{$panel_class}\" style=\"position-anchor:{$anchor};\">";
165
+ echo \Bricks\Frontend::render_children( $this );
166
+ echo '</div>';
167
+
168
+ echo '</div>';
169
+ }
170
+ }
@@ -0,0 +1,155 @@
1
+ <?php
2
+ /**
3
+ * Vitops — Split Panel element (Bricks Builder).
4
+ *
5
+ * Renders the <wc-split-panel> Lit component (src/web-components/WCSplitPanel.ts): a
6
+ * resizable two-panel splitter with a draggable handle. Author fills the two seeded
7
+ * columns, which carry slot="start" / slot="end" so the component picks them up.
8
+ *
9
+ * Nestable: the live shadow-DOM handle appears on the frontend; the builder canvas
10
+ * shows the two columns stacked (upgrade happens client-side via dist/elements.js).
11
+ *
12
+ * Owned by the framework repo (vitops: bricks/elements/); copied into the theme's
13
+ * dist/bricks/ on build — do not hand-edit in the theme.
14
+ */
15
+
16
+ if ( ! defined( 'ABSPATH' ) ) {
17
+ exit;
18
+ }
19
+
20
+ class Vitops_Element_Split_Panel extends \Bricks\Element {
21
+ public $category = 'vitops';
22
+ public $name = 'vitops-split-panel';
23
+ public $icon = 'ti-layout-sidebar-2';
24
+ public $css_selector = 'wc-split-panel';
25
+ public $nestable = true;
26
+ public $vue_component = 'bricks-nestable';
27
+
28
+ public function get_label() {
29
+ return esc_html__( 'Split Panel', 'bricks' );
30
+ }
31
+
32
+ public function get_keywords() {
33
+ return array( 'split', 'panel', 'resize', 'splitter', 'drag', 'vitops' );
34
+ }
35
+
36
+ public function set_controls() {
37
+ $this->controls['position'] = array(
38
+ 'tab' => 'content',
39
+ 'label' => esc_html__( 'Initial position (%)', 'bricks' ),
40
+ 'type' => 'number',
41
+ 'min' => 0,
42
+ 'max' => 100,
43
+ 'default' => 50,
44
+ );
45
+
46
+ $this->controls['vertical'] = array(
47
+ 'tab' => 'content',
48
+ 'label' => esc_html__( 'Vertical (stack top/bottom)', 'bricks' ),
49
+ 'type' => 'checkbox',
50
+ );
51
+
52
+ $this->controls['discrete'] = array(
53
+ 'tab' => 'content',
54
+ 'label' => esc_html__( 'Discrete (step) dragging', 'bricks' ),
55
+ 'type' => 'checkbox',
56
+ );
57
+
58
+ $this->controls['minSize'] = array(
59
+ 'tab' => 'content',
60
+ 'label' => esc_html__( 'Min panel size (px)', 'bricks' ),
61
+ 'type' => 'number',
62
+ 'default' => 100,
63
+ );
64
+
65
+ $this->controls['maxSize'] = array(
66
+ 'tab' => 'content',
67
+ 'label' => esc_html__( 'Max panel size (px)', 'bricks' ),
68
+ 'type' => 'number',
69
+ );
70
+
71
+ $this->controls['keyboardStep'] = array(
72
+ 'tab' => 'content',
73
+ 'label' => esc_html__( 'Keyboard step (%)', 'bricks' ),
74
+ 'type' => 'number',
75
+ );
76
+
77
+ $this->controls['collapseThreshold'] = array(
78
+ 'tab' => 'content',
79
+ 'label' => esc_html__( 'Collapse threshold (%)', 'bricks' ),
80
+ 'type' => 'number',
81
+ );
82
+
83
+ $this->controls['snapPoints'] = array(
84
+ 'tab' => 'content',
85
+ 'label' => esc_html__( 'Snap points (%)', 'bricks' ),
86
+ 'type' => 'text',
87
+ 'placeholder' => '25,50,75',
88
+ );
89
+
90
+ $this->controls['snapDistance'] = array(
91
+ 'tab' => 'content',
92
+ 'label' => esc_html__( 'Snap distance (%)', 'bricks' ),
93
+ 'type' => 'number',
94
+ );
95
+ }
96
+
97
+ // Two sides seeded, each tagged with the slot the component reads.
98
+ public function get_nestable_children() {
99
+ return array(
100
+ array(
101
+ 'name' => 'block',
102
+ 'label' => esc_html__( 'Start panel', 'bricks' ),
103
+ 'settings' => array(
104
+ '_attributes' => array(
105
+ array( 'id' => 'vslotstart', 'name' => 'slot', 'value' => 'start' ),
106
+ ),
107
+ ),
108
+ ),
109
+ array(
110
+ 'name' => 'block',
111
+ 'label' => esc_html__( 'End panel', 'bricks' ),
112
+ 'settings' => array(
113
+ '_attributes' => array(
114
+ array( 'id' => 'vslotend', 'name' => 'slot', 'value' => 'end' ),
115
+ ),
116
+ ),
117
+ ),
118
+ );
119
+ }
120
+
121
+ public function render() {
122
+ $s = $this->settings;
123
+ $attrs = array();
124
+
125
+ // number/text attributes → kebab-case component attribute names.
126
+ $num = array(
127
+ 'position' => 'position',
128
+ 'minSize' => 'min-size',
129
+ 'maxSize' => 'max-size',
130
+ 'keyboardStep' => 'keyboard-step',
131
+ 'collapseThreshold' => 'collapse-threshold',
132
+ 'snapDistance' => 'snap-distance',
133
+ 'snapPoints' => 'snap-points',
134
+ );
135
+ foreach ( $num as $key => $attr ) {
136
+ if ( isset( $s[ $key ] ) && '' !== $s[ $key ] ) {
137
+ $attrs[] = $attr . '="' . esc_attr( $s[ $key ] ) . '"';
138
+ }
139
+ }
140
+
141
+ // boolean attributes → present only when true.
142
+ if ( ! empty( $s['vertical'] ) ) {
143
+ $attrs[] = 'vertical';
144
+ }
145
+ if ( ! empty( $s['discrete'] ) ) {
146
+ $attrs[] = 'discrete';
147
+ }
148
+
149
+ $attr_str = implode( ' ', $attrs );
150
+
151
+ echo "<wc-split-panel {$attr_str} {$this->render_attributes( '_root' )}>";
152
+ echo \Bricks\Frontend::render_children( $this );
153
+ echo '</wc-split-panel>';
154
+ }
155
+ }
@@ -0,0 +1,83 @@
1
+ <?php
2
+ /**
3
+ * Vitops — Split layout element (Bricks Builder).
4
+ *
5
+ * A nestable flex row rendering the framework's `.split` (src/css/layout.css) — equal
6
+ * columns by default. Set a ratio by adding a class in the CSS-classes field:
7
+ * `split-1-2` / `2-1` / `1-3` / `3-1` / `1-4` / `4-1` / `2-3` / `3-2`, each with an
8
+ * optional `-sm/-md/-lg/-xl` suffix to engage the ratio from a container breakpoint.
9
+ *
10
+ * The `.split` base rides on the built-in "CSS classes" setting (defaulted below) so it
11
+ * shows in the builder canvas (assembled from settings) and the frontend.
12
+ *
13
+ * Owned by the framework repo (vitops: bricks/elements/); copied into the theme's
14
+ * dist/bricks/ on build — do not hand-edit in the theme.
15
+ */
16
+
17
+ if ( ! defined( 'ABSPATH' ) ) {
18
+ exit;
19
+ }
20
+
21
+ class Vitops_Element_Split extends \Bricks\Element {
22
+ public $category = 'vitops';
23
+ public $name = 'vitops-split';
24
+ public $icon = 'ti-layout-column2';
25
+ public $css_selector = '.split';
26
+ public $nestable = true;
27
+ public $vue_component = 'bricks-nestable';
28
+
29
+ public function get_label() {
30
+ return esc_html__( 'Split', 'bricks' );
31
+ }
32
+
33
+ public function get_keywords() {
34
+ return [ 'split', 'columns', 'ratio', 'flex', 'container', 'layout', 'vitops' ];
35
+ }
36
+
37
+ public function set_controls() {
38
+ $this->controls['tag'] = [
39
+ 'tab' => 'content',
40
+ 'label' => esc_html__( 'HTML tag', 'bricks' ),
41
+ 'type' => 'select',
42
+ 'inline' => true,
43
+ 'options' => [
44
+ 'div' => 'div',
45
+ 'section' => 'section',
46
+ 'article' => 'article',
47
+ 'aside' => 'aside',
48
+ 'main' => 'main',
49
+ 'header' => 'header',
50
+ 'footer' => 'footer',
51
+ 'nav' => 'nav',
52
+ ],
53
+ 'default' => 'div',
54
+ 'lowercase' => true,
55
+ ];
56
+
57
+ $this->controls['ratioInfo'] = [
58
+ 'tab' => 'content',
59
+ 'type' => 'info',
60
+ 'content' => esc_html__( 'Equal columns by default. Add a class in "CSS classes" for a ratio: split-1-2, 2-1, 1-3, 3-1, 1-4, 4-1, 2-3, 3-2 (append -sm/-md/-lg/-xl to engage it from a breakpoint).', 'bricks' ),
61
+ ];
62
+
63
+ // Base flex class, defaulted onto the built-in "CSS classes" setting so it
64
+ // shows in the builder canvas (assembled from settings) and the frontend.
65
+ $this->controls['_cssClasses']['default'] = 'split';
66
+ }
67
+
68
+ // Seed two empty columns — a split is inherently two sides. No opinionated
69
+ // classes; the author fills each column (and can make it a rhythm container).
70
+ public function get_nestable_children() {
71
+ return [
72
+ [ 'name' => 'block', 'label' => esc_html__( 'Column 1', 'bricks' ) ],
73
+ [ 'name' => 'block', 'label' => esc_html__( 'Column 2', 'bricks' ) ],
74
+ ];
75
+ }
76
+
77
+ public function render() {
78
+ // $this->tag is resolved from the `tag` setting by the base (get_tag()).
79
+ echo "<{$this->tag} {$this->render_attributes( '_root' )}>";
80
+ echo \Bricks\Frontend::render_children( $this );
81
+ echo "</{$this->tag}>";
82
+ }
83
+ }
@@ -0,0 +1,129 @@
1
+ <?php
2
+ /**
3
+ * Vitops — Bricks theme bootstrap.
4
+ *
5
+ * One-line integration for the child theme. Add to the theme's functions.php:
6
+ *
7
+ * require_once get_stylesheet_directory() . '/dist/bricks/load.php';
8
+ *
9
+ * It does three things:
10
+ * 1. Registers every repo-owned Bricks element under dist/bricks/elements/.
11
+ * 2. Enqueues the framework CSS + JS bundles — polyfills.js and elements.js as
12
+ * ES modules (so the Lit custom elements upgrade), deferred.js with `defer`.
13
+ * The enqueue runs on `wp_enqueue_scripts`, which the Bricks builder canvas
14
+ * iframe also fires, so the elements upgrade while editing too.
15
+ * 3. Registers a "Vitops" builder category so the elements group together.
16
+ *
17
+ * Owned by the framework repo (vitops: bricks/); copied into the theme's
18
+ * dist/bricks/ on build — do not hand-edit in the theme.
19
+ */
20
+
21
+ if ( ! defined( 'ABSPATH' ) ) {
22
+ exit;
23
+ }
24
+
25
+ /**
26
+ * 1. Register the custom Bricks elements.
27
+ *
28
+ * Bricks exposes `\Bricks\Elements::register_element( $file )`; each file defines
29
+ * a `Vitops_Element_*` class extending `\Bricks\Element`.
30
+ */
31
+ add_action(
32
+ 'init',
33
+ function () {
34
+ if ( ! class_exists( '\Bricks\Elements' ) ) {
35
+ return;
36
+ }
37
+
38
+ foreach ( glob( __DIR__ . '/elements/*.php' ) as $file ) {
39
+ \Bricks\Elements::register_element( $file );
40
+ }
41
+ },
42
+ 11
43
+ );
44
+
45
+ /**
46
+ * 2. Enqueue the framework bundles. Handles listed in $module_handles are rewritten
47
+ * to <script type="module"> below.
48
+ */
49
+ add_action(
50
+ 'wp_enqueue_scripts',
51
+ function () {
52
+ $dir = get_stylesheet_directory_uri() . '/dist';
53
+ $path = get_stylesheet_directory() . '/dist';
54
+
55
+ // Version each asset by its file mtime so a deploy (which rewrites the file,
56
+ // preserving a fresh mtime through rsync) busts any browser/CDN cache. A `null`
57
+ // version leaves the URL query-less and highly cacheable, so post-deploy edits
58
+ // never reach returning visitors. Missing file → null (WP's default behaviour).
59
+ $ver = function ( $file ) use ( $path ) {
60
+ $p = $path . '/' . $file;
61
+ return file_exists( $p ) ? filemtime( $p ) : null;
62
+ };
63
+
64
+ wp_enqueue_style( 'vitops-styles', $dir . '/styles.min.css', array(), $ver( 'styles.min.css' ) );
65
+
66
+ // Feature-detected polyfill loader — first, high, as a module.
67
+ wp_enqueue_script( 'vitops-polyfills', $dir . '/polyfills.js', array(), $ver( 'polyfills.js' ), false );
68
+
69
+ // Custom-element registrations — module; self-registers on load.
70
+ wp_enqueue_script( 'vitops-elements', $dir . '/elements.js', array(), $ver( 'elements.js' ), true );
71
+
72
+ // Non-critical progressive enhancement — plain deferred script.
73
+ wp_enqueue_script( 'vitops-deferred', $dir . '/deferred.js', array(), $ver( 'deferred.js' ), true );
74
+ }
75
+ );
76
+
77
+ /**
78
+ * Rewrite the ES-module bundles to `type="module"` (WP has no first-class flag for
79
+ * this on classic enqueues). deferred.js stays a plain `defer` script.
80
+ */
81
+ add_filter(
82
+ 'script_loader_tag',
83
+ function ( $tag, $handle ) {
84
+ $module_handles = array( 'vitops-polyfills', 'vitops-elements' );
85
+
86
+ if ( in_array( $handle, $module_handles, true ) ) {
87
+ // Ensure a single type="module" attribute (replace any injected type).
88
+ $tag = preg_replace( '/\stype=(["\']).*?\1/', '', $tag );
89
+ $tag = str_replace( ' src=', ' type="module" src=', $tag );
90
+ }
91
+
92
+ return $tag;
93
+ },
94
+ 10,
95
+ 2
96
+ );
97
+
98
+ /**
99
+ * 3. Group the elements under a "Vitops" category, pinned to the TOP of the builder
100
+ * panel. Bricks renders categories in array order, so prepend rather than append.
101
+ */
102
+ add_filter(
103
+ 'bricks/builder/element_categories',
104
+ function ( $categories ) {
105
+ // Drop any pre-existing key so the prepended one wins the ordering.
106
+ unset( $categories['vitops'] );
107
+
108
+ return array( 'vitops' => esc_html__( 'Vitops', 'bricks' ) ) + $categories;
109
+ },
110
+ 5
111
+ );
112
+
113
+ /**
114
+ * 4. Default the design system to dark mode: stamp data-brx-theme="dark" on <html>.
115
+ * The generated semantic colour roles (surface/brand-primary/success/…) remap under
116
+ * `:root[data-brx-theme="dark"]`, and the site's pages are dark-themed, so this keeps
117
+ * the framework's cards/badges/patterns consistent with the page. Server-side via
118
+ * language_attributes so there is no light-mode flash on load.
119
+ */
120
+ add_filter(
121
+ 'language_attributes',
122
+ function ( $output ) {
123
+ // Bricks sets data-brx-theme from its default colour scheme (light); strip any
124
+ // existing value and force dark. High priority so this runs after Bricks.
125
+ $output = preg_replace( '/\s*data-brx-theme=(["\'][^"\']*["\']|\S+)/', '', (string) $output );
126
+ return rtrim( $output ) . ' data-brx-theme="dark"';
127
+ },
128
+ 99
129
+ );