maverick-wave 5.3.0 → 5.5.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 (36) hide show
  1. package/.claude/skills/mw-maverick-wave/SKILL.md +13 -4
  2. package/.claude/skills/mw-maverick-wave/examples/angular-services.md +91 -0
  3. package/.claude/skills/mw-maverick-wave/examples/static-landing-page.md +5 -4
  4. package/.claude/skills/mw-maverick-wave/references/components.md +7 -0
  5. package/.claude/skills/mw-maverick-wave/references/javascript.md +20 -2
  6. package/.claude/skills/mw-maverick-wave/references/layout.md +81 -10
  7. package/.claude/skills/mw-maverick-wave/references/theming.md +10 -0
  8. package/CHANGELOG.md +18 -0
  9. package/CLAUDE.md +1 -1
  10. package/README.md +3 -4
  11. package/index.html +291 -3
  12. package/maverick-wave.min.css +6 -4
  13. package/maverick-wave.min.js +1 -1
  14. package/package.json +2 -2
  15. package/scripts/verify.js +4 -7
  16. package/src/js/main.js +237 -11
  17. package/src/partials/documentation-container.html +4 -0
  18. package/src/partials/footer-container.html +3 -0
  19. package/src/partials/history-container.html +67 -0
  20. package/src/partials/home-container.html +2 -0
  21. package/src/partials/palette-container.html +142 -0
  22. package/src/partials/parallax-container.html +114 -0
  23. package/src/scss/abstracts/_mixins.scss +6 -5
  24. package/src/scss/abstracts/_variables.scss +4 -0
  25. package/src/scss/base/_base.scss +1 -0
  26. package/src/scss/base/_reset.scss +4 -1
  27. package/src/scss/components/_accordions.scss +31 -7
  28. package/src/scss/components/_toasts.scss +1 -1
  29. package/src/scss/form-elements/_slider.scss +5 -23
  30. package/src/scss/layout/_header-reveal.scss +133 -0
  31. package/src/scss/layout/_index.scss +4 -0
  32. package/src/scss/layout/_main.scss +17 -2
  33. package/src/scss/layout/_parallax.scss +178 -0
  34. package/src/scss/utilities/_reveal.scss +22 -0
  35. package/.impeccable/config.local.json +0 -5
  36. package/.impeccable/hook.cache.json +0 -75
package/index.html CHANGED
@@ -69,6 +69,31 @@
69
69
  border: 1px solid var(--mw-border);
70
70
  }
71
71
 
72
+ /* The parallax pattern demo: a gradient has no edge across the travel,
73
+ the second layer brings one */
74
+ .parallax-demo-ground {
75
+ background-image: linear-gradient(
76
+ 158deg,
77
+ #3c4f78 0%,
78
+ #2a3550 45%,
79
+ #1b2334 100%
80
+ );
81
+ }
82
+
83
+ .parallax-demo-lines {
84
+ --mw-parallax-pattern-image:
85
+ repeating-linear-gradient(
86
+ to bottom,
87
+ rgb(255 255 255 / 24%) 0 1px,
88
+ transparent 1px 34px
89
+ ),
90
+ repeating-linear-gradient(
91
+ to right,
92
+ rgb(255 255 255 / 14%) 0 1px,
93
+ transparent 1px 34px
94
+ );
95
+ }
96
+
72
97
  /* A calendar fills its container; across the full page that stretches the
73
98
  cells to 150px, so the demos get a realistic column instead */
74
99
  .calendar-demo {
@@ -93,6 +118,144 @@
93
118
  height: 50px;
94
119
  }
95
120
  }
121
+
122
+ /* Palette switcher, showcase only. The framework derives every tone from
123
+ the root colors, so the panel writes nothing but those. */
124
+ .palette-fab {
125
+ position: fixed;
126
+ right: 18px;
127
+ bottom: 18px;
128
+ z-index: 400;
129
+ width: 52px;
130
+ height: 52px;
131
+ border-radius: 50%;
132
+ border: 1px solid var(--mw-border-accent);
133
+ background: var(--mw-primary-color);
134
+ color: var(--mw-primary-accent-text-color);
135
+ box-shadow: var(--mw-elevation-4);
136
+ font-size: 1.25rem;
137
+ cursor: pointer;
138
+ transition: var(--mw-transition);
139
+ }
140
+
141
+ .palette-fab:hover {
142
+ background: var(--mw-primary-color-hover);
143
+ }
144
+
145
+ .palette-fab:active {
146
+ transform: scale(0.92);
147
+ }
148
+
149
+ .palette-panel {
150
+ position: fixed;
151
+ right: 18px;
152
+ bottom: 82px;
153
+ z-index: 400;
154
+ width: 300px;
155
+ padding: 18px;
156
+ border-radius: 12px;
157
+ border: 1px solid var(--mw-border);
158
+ background: var(--mw-card-background);
159
+ box-shadow: var(--mw-elevation-5);
160
+ }
161
+
162
+ .palette-heading {
163
+ font-size: 1.1rem;
164
+ margin-bottom: 4px;
165
+ }
166
+
167
+ .palette-hint {
168
+ font-size: 0.8rem;
169
+ line-height: 1.35;
170
+ color: var(--mw-text-muted-color);
171
+ margin-bottom: 14px;
172
+ }
173
+
174
+ .palette-grid {
175
+ display: grid;
176
+ grid-template-columns: 1fr 1fr;
177
+ gap: 6px;
178
+ }
179
+
180
+ .palette-item {
181
+ display: flex;
182
+ align-items: center;
183
+ gap: 8px;
184
+ padding: 6px 8px;
185
+ border-radius: 8px;
186
+ border: 1px solid var(--mw-border);
187
+ background: transparent;
188
+ color: var(--mw-text-color);
189
+ font-size: 0.8rem;
190
+ text-align: left;
191
+ cursor: pointer;
192
+ transition: var(--mw-transition);
193
+ }
194
+
195
+ .palette-item::before {
196
+ content: '';
197
+ width: 16px;
198
+ height: 16px;
199
+ flex-shrink: 0;
200
+ border-radius: 50%;
201
+ background: linear-gradient(
202
+ 135deg,
203
+ var(--swatch-primary) 50%,
204
+ var(--swatch-secondary) 50%
205
+ );
206
+ }
207
+
208
+ .palette-item:hover,
209
+ .palette-item.mw-active {
210
+ border-color: var(--mw-primary-text-color);
211
+ }
212
+
213
+ .palette-item.mw-active {
214
+ background: var(--mw-primary-background);
215
+ }
216
+
217
+ .palette-custom {
218
+ display: flex;
219
+ gap: 10px;
220
+ margin-top: 14px;
221
+ padding-top: 12px;
222
+ border-top: 1px solid var(--mw-border);
223
+ }
224
+
225
+ .palette-field {
226
+ flex: 1;
227
+ display: flex;
228
+ flex-direction: column;
229
+ gap: 4px;
230
+ font-size: 0.75rem;
231
+ color: var(--mw-text-muted-color);
232
+ }
233
+
234
+ .palette-field input {
235
+ width: 100%;
236
+ height: 30px;
237
+ padding: 2px;
238
+ border: 1px solid var(--mw-border);
239
+ border-radius: 6px;
240
+ background: var(--mw-card-background);
241
+ cursor: pointer;
242
+ }
243
+
244
+ @media (max-width: 576px) {
245
+ .palette-fab {
246
+ right: 12px;
247
+ bottom: 12px;
248
+ width: 46px;
249
+ height: 46px;
250
+ }
251
+
252
+ .palette-panel {
253
+ right: 12px;
254
+ left: 12px;
255
+ bottom: 68px;
256
+ width: auto;
257
+ }
258
+ }
96
259
  </style>
97
260
 
98
261
  <script src="maverick-wave.min.js"></script>
@@ -102,7 +265,7 @@
102
265
  <a class="mw-skip-link" href="#main">Skip to content</a>
103
266
 
104
267
  <!-- Header -->
105
- <header class="mw-header mw-localhost-indicator-activated">
268
+ <header class="mw-header mw-header-reveal mw-localhost-indicator-activated">
106
269
  <div class="mw-container">
107
270
  @@include('./src/partials/header-container.html')
108
271
  </div>
@@ -110,8 +273,8 @@
110
273
 
111
274
  <main class="mw-main" id="main">
112
275
  <!-- Home section -->
113
- <section id="home" class="mw-section">
114
- <div class="mw-container">
276
+ <section id="home" class="mw-section mw-parallax-sticky mw-parallax-rise">
277
+ <div class="mw-container mw-parallax">
115
278
  @@include('./src/partials/home-container.html')
116
279
  </div>
117
280
  </section>
@@ -210,6 +373,7 @@
210
373
  <a href="#html-lists" class="mw-section-btn">HTML Lists</a>
211
374
  <a href="#item-lists" class="mw-section-btn">Item Lists</a>
212
375
  <a href="#stepper" class="mw-section-btn">Stepper</a>
376
+ <a href="#parallax" class="mw-section-btn">Parallax</a>
213
377
  </nav>
214
378
 
215
379
  <!-- Grid container -->
@@ -258,6 +422,11 @@
258
422
  <div id="stepper">
259
423
  @@include('./src/partials/stepper-container.html')
260
424
  </div>
425
+
426
+ <!-- Parallax container -->
427
+ <div id="parallax">
428
+ @@include('./src/partials/parallax-container.html')
429
+ </div>
261
430
  </div>
262
431
  </section>
263
432
 
@@ -507,6 +676,17 @@
507
676
  </div>
508
677
  </div>
509
678
  </section>
679
+ <!-- History section -->
680
+ <section id="history" class="mw-section mw-section-alternate">
681
+ <div class="mw-container">
682
+ <h2 class="mw-section-title">History</h2>
683
+ <p class="mw-section-intro">
684
+ The major releases so far. Everything in between - the minors and
685
+ the patch releases - sits in the changelog.
686
+ </p>
687
+ @@include('./src/partials/history-container.html')
688
+ </div>
689
+ </section>
510
690
  </main>
511
691
 
512
692
  <!-- Footer -->
@@ -515,6 +695,9 @@
515
695
  @@include('./src/partials/footer-container.html')
516
696
  </div>
517
697
  </footer>
698
+
699
+ <!-- Palette switcher -->
700
+ @@include('./src/partials/palette-container.html')
518
701
  <script>
519
702
  // just for the showcase (login button triggers login error)
520
703
  document.addEventListener('DOMContentLoaded', () => {
@@ -598,6 +781,111 @@
598
781
  apply(cycleSwitch.querySelector('.mw-active'));
599
782
  });
600
783
 
784
+ // just for the showcase: the palette switcher. Every tone in the framework
785
+ // is derived from the root colors with color-mix(), so writing those on the
786
+ // root element repaints the page - bar, ink, borders and tints included.
787
+ // Nothing is persisted - a palette is for looking at, a reload is the way back.
788
+ document.addEventListener('DOMContentLoaded', () => {
789
+ const fab = document.getElementById('palette-fab');
790
+ const panel = document.getElementById('palette-panel');
791
+ const primaryInput = document.getElementById('palette-primary');
792
+ const secondaryInput = document.getElementById('palette-secondary');
793
+ const items = [...document.querySelectorAll('.palette-item')];
794
+ if (!fab || !panel) return;
795
+
796
+ const root = document.documentElement;
797
+
798
+ const DARK_INK = '#131925';
799
+ const LIGHT_INK = '#f2f6fc';
800
+
801
+ const luminance = (hex) => {
802
+ const [r, g, b] = [1, 3, 5].map((i) => {
803
+ const channel = parseInt(hex.slice(i, i + 2), 16) / 255;
804
+ return channel <= 0.04045
805
+ ? channel / 12.92
806
+ : ((channel + 0.055) / 1.055) ** 2.4;
807
+ });
808
+ return 0.2126 * r + 0.7152 * g + 0.0722 * b;
809
+ };
810
+
811
+ const contrast = (a, b) => {
812
+ const [high, low] = [luminance(a), luminance(b)].sort(
813
+ (x, y) => y - x
814
+ );
815
+ return (high + 0.05) / (low + 0.05);
816
+ };
817
+
818
+ // Measured, not thresholded - a lightness cutoff gets a saturated mid
819
+ // tone wrong
820
+ const ink = (fill) =>
821
+ contrast(fill, DARK_INK) > contrast(fill, LIGHT_INK)
822
+ ? DARK_INK
823
+ : LIGHT_INK;
824
+
825
+ // Cuts the transition the new colours started: the whole page
826
+ // interpolating at once is the expensive part, and the hex labels in the
827
+ // Colors section read computed values, so they need the landed ones
828
+ const settle = () => {
829
+ root.classList.add('mw-theme-switching');
830
+ void root.offsetHeight;
831
+ root.classList.remove('mw-theme-switching');
832
+ window.mwRefreshColorSwatches?.();
833
+ };
834
+
835
+ const paint = (name, color) => {
836
+ root.style.setProperty(`--mw-${name}-color`, color);
837
+ root.style.setProperty(`--mw-${name}-accent-text-color`, ink(color));
838
+ };
839
+
840
+ items.forEach((item) => {
841
+ const { primary, secondary, dark, light } = item.dataset;
842
+ item.style.setProperty('--swatch-primary', primary);
843
+ item.style.setProperty('--swatch-secondary', secondary);
844
+
845
+ item.addEventListener('click', () => {
846
+ items.forEach((other) =>
847
+ other.classList.toggle('mw-active', other === item)
848
+ );
849
+ paint('primary', primary);
850
+ paint('secondary', secondary);
851
+ root.style.setProperty('--mw-dark-page-background', dark);
852
+ root.style.setProperty('--mw-light-page-background', light);
853
+ primaryInput.value = primary;
854
+ secondaryInput.value = secondary;
855
+ settle();
856
+ });
857
+ });
858
+
859
+ // A hand-picked color belongs to no preset - the page backgrounds stay put
860
+ [
861
+ ['primary', primaryInput],
862
+ ['secondary', secondaryInput],
863
+ ].forEach(([name, input]) => {
864
+ input.addEventListener('input', () => {
865
+ items.forEach((item) => item.classList.remove('mw-active'));
866
+ paint(name, input.value);
867
+ });
868
+ // Dragging the picker fires `input` per pixel - the rest waits for the end
869
+ input.addEventListener('change', settle);
870
+ });
871
+
872
+ const toggle = (open) => {
873
+ panel.hidden = !open;
874
+ fab.setAttribute('aria-expanded', String(open));
875
+ };
876
+
877
+ fab.addEventListener('click', (event) => {
878
+ event.stopPropagation();
879
+ toggle(panel.hidden);
880
+ });
881
+ document.addEventListener('click', (event) => {
882
+ if (!panel.hidden && !panel.contains(event.target)) toggle(false);
883
+ });
884
+ document.addEventListener('keydown', (event) => {
885
+ if (event.key === 'Escape') toggle(false);
886
+ });
887
+ });
888
+
601
889
  // for the modals - the framework handles both shapes, div and <dialog>
602
890
  const openModal = window.mwOpenModal;
603
891
  const closeModal = window.mwCloseModal;