maverick-wave 5.27.0 → 5.29.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 (60) hide show
  1. package/.claude/skills/mw-maverick-wave/SKILL.md +4 -2
  2. package/.claude/skills/mw-maverick-wave/references/components.md +212 -0
  3. package/.claude/skills/mw-maverick-wave/references/javascript.md +6 -1
  4. package/.claude/skills/mw-maverick-wave/references/theming.md +53 -51
  5. package/CHANGELOG.md +17 -0
  6. package/CLAUDE.md +4 -4
  7. package/README.md +2 -2
  8. package/index.html +65 -0
  9. package/maverick-wave.min.css +4 -4
  10. package/maverick-wave.min.js +1 -1
  11. package/package.json +2 -2
  12. package/scripts/verify.js +2 -1
  13. package/src/assets/images/photo-balloon.svg +35 -0
  14. package/src/assets/images/photo-forest-path.svg +31 -0
  15. package/src/assets/images/photo-lighthouse.svg +42 -0
  16. package/src/assets/images/photo-palm.svg +27 -0
  17. package/src/assets/images/photo-peak.svg +27 -0
  18. package/src/assets/images/photo-tower.svg +34 -0
  19. package/src/assets/images/photo-waterfall.svg +32 -0
  20. package/src/assets/images/story-aurora.svg +36 -0
  21. package/src/assets/images/story-beach.svg +34 -0
  22. package/src/assets/images/story-stars.svg +35 -0
  23. package/src/assets/media/story-sunrise.jpg +0 -0
  24. package/src/assets/media/story-sunrise.mp4 +0 -0
  25. package/src/js/main.js +411 -8
  26. package/src/partials/avatars-container.html +50 -0
  27. package/src/partials/calendar-container.html +36 -38
  28. package/src/partials/feed-container.html +506 -0
  29. package/src/partials/item-lists-container.html +80 -130
  30. package/src/partials/kanban-container.html +17 -294
  31. package/src/partials/mosaic-container.html +173 -0
  32. package/src/partials/panels-container.html +113 -162
  33. package/src/partials/portrait-gallery-container.html +175 -0
  34. package/src/partials/stories-container.html +182 -0
  35. package/src/partials/tables-container.html +21 -40
  36. package/src/scss/abstracts/_mixins.scss +2 -0
  37. package/src/scss/abstracts/_variables.scss +9 -0
  38. package/src/scss/components/_avatars.scss +52 -0
  39. package/src/scss/components/_cards.scss +7 -3
  40. package/src/scss/components/_feed.scss +203 -0
  41. package/src/scss/components/_hero-badge.scss +48 -0
  42. package/src/scss/components/_index.scss +8 -0
  43. package/src/scss/components/_leader-row.scss +24 -18
  44. package/src/scss/components/_lightbox.scss +178 -0
  45. package/src/scss/components/_mosaic.scss +83 -0
  46. package/src/scss/components/_overlay-btn.scss +47 -0
  47. package/src/scss/components/_portrait-gallery.scss +180 -0
  48. package/src/scss/components/_stories.scss +443 -0
  49. package/src/scss/components/_tiles.scss +4 -1
  50. package/src/scss/components/_tooltip.scss +193 -0
  51. package/src/scss/layout/_footer.scss +46 -84
  52. package/src/scss/layout/_grid.scss +48 -45
  53. package/src/scss/layout/_header-reveal.scss +1 -1
  54. package/src/scss/layout/_header.scss +49 -66
  55. package/src/scss/layout/_home.scss +5 -2
  56. package/src/scss/layout/_main.scss +100 -337
  57. package/src/scss/layout/_parallax.scss +1 -1
  58. package/src/scss/layout/_section.scss +24 -17
  59. package/src/scss/utilities/_fit.scss +29 -0
  60. package/src/scss/utilities/_index.scss +1 -0
@@ -10,7 +10,7 @@
10
10
  // and invoice draws one.
11
11
  //
12
12
  // The leader is the row's own ::before rather than an element in the markup.
13
- // A generated box on a flex container is a flex item, so "order" can place it
13
+ // A generated box on a grid container is a grid item, so "order" can place it
14
14
  // between the two spans - and generated content stays out of the
15
15
  // accessibility tree, where a filler element would need aria-hidden.
16
16
  .mw-leader-row {
@@ -18,20 +18,27 @@
18
18
  --mw-leader-row-width: 2px;
19
19
  --mw-leader-row-color: var(--mw-border);
20
20
 
21
- display: flex;
22
- // The values never shrink, so without a wrap the label is the only thing
23
- // left to give - and it gives until it is one letter per line
24
- flex-wrap: wrap;
25
- align-items: flex-end;
21
+ // Grid, not a wrapping flex row. Flex fills a line greedily and moves the
22
+ // next item down before it shrinks anything, so a label that still fit on
23
+ // its own line kept it and pushed the value underneath - on a phone the
24
+ // row then read as text, dashes, and a number stranded on the line below.
25
+ // A grid column gives instead: the label breaks between its words and the
26
+ // value stays where it belongs.
27
+ //
28
+ // The leader column holds a stub of line when label and value nearly fill
29
+ // the row. Anything past the two declared columns - a second value - takes
30
+ // a column of its own.
31
+ display: grid;
32
+ grid-template-columns: minmax(0, auto) minmax(spacing('6'), 1fr);
33
+ grid-auto-flow: column;
34
+ grid-auto-columns: max-content;
35
+ align-items: end;
26
36
  gap: spacing('3');
27
37
  margin: 0;
28
38
 
29
39
  &::before {
30
40
  content: '';
31
41
  order: 1;
32
- flex: 1 1 auto;
33
- // Keeps a stub of line visible when label and value nearly fill the row
34
- min-width: spacing('6');
35
42
  border-bottom: var(--mw-leader-row-width) var(--mw-leader-row-style)
36
43
  var(--mw-leader-row-color);
37
44
  margin-bottom: $_mw-leader-baseline-lift;
@@ -39,17 +46,16 @@
39
46
 
40
47
  &-label {
41
48
  order: 0;
42
- // No min-width override: the default floor is the longest word, so the
43
- // label wraps between words and stops there instead of going to nothing.
44
- // Past that floor the row wraps rather than the label shrinking further.
45
- //
49
+ // Words first, and the column floor is zero, so a single word wider than
50
+ // what is left of the row breaks inside itself rather than running past
51
+ // the edge - a German compound noun reaches that point on a phone.
52
+ overflow-wrap: break-word;
46
53
  // The row also works as a dl group, and a dd carries 40px of browser margin
47
54
  margin: 0;
48
55
  }
49
56
 
50
57
  &-value {
51
58
  order: 2;
52
- flex-shrink: 0;
53
59
  margin: 0;
54
60
  // A price or a page number must not break, and a column of them only
55
61
  // lines up with tabular figures.
@@ -59,8 +65,8 @@
59
65
 
60
66
  // Two values in one row read as a single long range with only a gap between
61
67
  // them. The mark takes the leader's colour, so retuning a row moves both.
62
- // It hangs off the first of the pair, not the second: once the row wraps, a
63
- // trailing mark reads as "continues below" and a leading one as a slip.
68
+ // It hangs off the first of the pair, not the second, where a trailing mark
69
+ // would read as "and there is more" at the end of the row.
64
70
  &-value:has(+ &-value)::after {
65
71
  content: '|';
66
72
  margin-left: spacing('3');
@@ -77,10 +83,10 @@
77
83
  }
78
84
 
79
85
  &-top {
80
- align-items: flex-start;
86
+ align-items: start;
81
87
 
82
88
  &::before {
83
- // flex-start pins the leader to the top edge, so it drops to the first
89
+ // start pins the leader to the top edge, so it drops to the first
84
90
  // line's baseline itself instead of being lifted off the last one
85
91
  margin: calc(1lh - #{$_mw-leader-baseline-lift}) 0 0;
86
92
  }
@@ -0,0 +1,178 @@
1
+ @use '../abstracts' as *;
2
+ @use 'overlay-btn';
3
+
4
+ @layer mw.components {
5
+ dialog.mw-lightbox {
6
+ position: fixed;
7
+ inset: 0;
8
+ width: auto;
9
+ height: auto;
10
+ max-width: none;
11
+ max-height: none;
12
+ margin: 0;
13
+ padding: 0;
14
+ border: none;
15
+ background: $fixed-stage;
16
+ color: $fixed-surface-text;
17
+ overflow: hidden;
18
+ overscroll-behavior: contain;
19
+ // The brand ink disappears on the stage
20
+ --mw-focus-ring-color: #{$fixed-surface-text};
21
+ opacity: 0;
22
+ transition:
23
+ opacity var(--mw-duration-base) var(--mw-ease-out),
24
+ overlay var(--mw-duration-base) allow-discrete,
25
+ display var(--mw-duration-base) allow-discrete;
26
+
27
+ &[open] {
28
+ opacity: 1;
29
+
30
+ @starting-style {
31
+ opacity: 0;
32
+ }
33
+ }
34
+
35
+ &::backdrop {
36
+ background: transparent;
37
+ }
38
+ }
39
+
40
+ // A <dialog> does not lock the page scroll behind it
41
+ :root:has(dialog.mw-lightbox[open]) {
42
+ overflow: hidden;
43
+ scrollbar-gutter: stable; // Or the page shifts by the hidden scrollbar
44
+ }
45
+
46
+ .mw-lightbox-track {
47
+ display: flex;
48
+ height: 100%;
49
+ overflow-x: auto;
50
+ overflow-y: hidden;
51
+ overscroll-behavior: contain;
52
+ scroll-snap-type: x mandatory;
53
+ scrollbar-width: none;
54
+
55
+ &::-webkit-scrollbar {
56
+ display: none;
57
+ }
58
+
59
+ @media (prefers-reduced-motion: no-preference) {
60
+ scroll-behavior: smooth;
61
+ }
62
+ }
63
+
64
+ .mw-lightbox-slide {
65
+ flex: 0 0 100%;
66
+ min-width: 0;
67
+ display: flex;
68
+ flex-direction: column;
69
+ align-items: center;
70
+ justify-content: center;
71
+ gap: spacing('4');
72
+ margin: spacing('0');
73
+ padding: 4.25rem spacing('3')
74
+ calc(#{spacing('6')} + env(safe-area-inset-bottom, 0px));
75
+ scroll-snap-align: center;
76
+ scroll-snap-stop: always;
77
+
78
+ img {
79
+ display: block;
80
+ flex: 0 1 auto;
81
+ min-height: 0;
82
+ max-width: 100%;
83
+ max-height: 100%;
84
+ object-fit: contain;
85
+ border-radius: radius('xs');
86
+ user-select: none;
87
+ }
88
+
89
+ figcaption {
90
+ flex: none;
91
+ max-width: 60ch;
92
+ max-height: 30%;
93
+ overflow-y: auto;
94
+ padding-inline: spacing('3');
95
+ color: rgb(239 239 239 / 88%);
96
+ font-size: font-size('sm');
97
+ line-height: 1.5;
98
+ text-align: center;
99
+
100
+ > * {
101
+ margin: spacing('0');
102
+ }
103
+
104
+ > * + * {
105
+ margin-top: spacing('2');
106
+ }
107
+
108
+ time,
109
+ small {
110
+ display: block;
111
+ color: rgb(239 239 239 / 60%);
112
+ font-size: font-size('xs');
113
+ }
114
+ }
115
+ }
116
+
117
+ // On a phone the arrows sit in the bar, off the photo
118
+ .mw-lightbox-bar {
119
+ position: absolute;
120
+ inset: 0 0 auto;
121
+ z-index: z-index('lift');
122
+ display: flex;
123
+ align-items: center;
124
+ gap: spacing('1');
125
+ padding: max(#{spacing('3')}, env(safe-area-inset-top, 0px)) spacing('3')
126
+ spacing('5');
127
+ background: linear-gradient(rgb(0 0 0 / 55%), transparent);
128
+
129
+ .mw-lightbox-close {
130
+ margin-left: auto;
131
+ }
132
+ }
133
+
134
+ .mw-lightbox-count {
135
+ min-width: 4.5em;
136
+ font-size: font-size('sm');
137
+ font-variant-numeric: tabular-nums;
138
+ text-align: center;
139
+ }
140
+
141
+ @include media-up('md') {
142
+ .mw-lightbox-slide {
143
+ padding-inline: 5.5rem;
144
+ }
145
+
146
+ .mw-lightbox-bar {
147
+ padding-left: spacing('6');
148
+ }
149
+
150
+ .mw-lightbox-count {
151
+ order: -1;
152
+ text-align: left;
153
+ }
154
+
155
+ // Fixed works because the bar has no transform or filter to trap them
156
+ .mw-lightbox-prev,
157
+ .mw-lightbox-next {
158
+ position: fixed;
159
+ top: 50%;
160
+ translate: 0 -50%;
161
+ width: 3.25rem;
162
+ height: 3.25rem;
163
+ background: rgb(255 255 255 / 10%);
164
+
165
+ &:hover {
166
+ background: rgb(255 255 255 / 20%);
167
+ }
168
+ }
169
+
170
+ .mw-lightbox-prev {
171
+ left: spacing('6');
172
+ }
173
+
174
+ .mw-lightbox-next {
175
+ right: spacing('6');
176
+ }
177
+ }
178
+ }
@@ -0,0 +1,83 @@
1
+ @use '../abstracts' as *;
2
+
3
+ @layer mw.components {
4
+ .mw-mosaic {
5
+ display: grid;
6
+ // Two columns on a phone, four at most, in whatever box it sits
7
+ grid-template-columns: repeat(auto-fill, minmax(max(120px, 22%), 1fr));
8
+ // Equal rows sized by the square tiles' aspect-ratio
9
+ grid-auto-rows: 1fr;
10
+ grid-auto-flow: dense;
11
+ // Dense packing reorders the tiles; Tab follows what the eye sees
12
+ reading-flow: grid-rows;
13
+ gap: spacing('4');
14
+ }
15
+
16
+ .mw-mosaic-gap-sm {
17
+ gap: spacing('1');
18
+ }
19
+
20
+ .mw-mosaic-gap-lg {
21
+ gap: spacing('5');
22
+ }
23
+
24
+ .mw-mosaic-item {
25
+ position: relative;
26
+ margin: spacing('0');
27
+ // Only sizes the rows; stretched both ways, or the ratio sizes the tile
28
+ aspect-ratio: 1;
29
+ place-self: stretch;
30
+ overflow: hidden;
31
+ border-radius: radius('sm');
32
+ background: var(--mw-card-background);
33
+ transition: transform var(--mw-duration-fast) var(--mw-ease-out);
34
+
35
+ > a {
36
+ position: absolute;
37
+ inset: 0;
38
+ display: block;
39
+ @include tappable;
40
+ @include focus-ring-inset(-3px);
41
+ }
42
+
43
+ img {
44
+ display: block;
45
+ width: 100%;
46
+ height: 100%;
47
+ object-fit: cover;
48
+ transition: transform var(--mw-duration-zoom) var(--mw-ease-out);
49
+ }
50
+
51
+ > figcaption {
52
+ display: none;
53
+ }
54
+
55
+ @include hover-move {
56
+ img {
57
+ transform: scale(1.05);
58
+ }
59
+ }
60
+
61
+ &:has(> a:active) {
62
+ transform: scale(0.97);
63
+ transition-duration: var(--mw-duration-instant);
64
+ }
65
+ }
66
+
67
+ // Picked by the script from the photo's shape unless one is set by hand
68
+ .mw-mosaic-wide {
69
+ grid-column: span 2;
70
+ aspect-ratio: 2;
71
+ }
72
+
73
+ .mw-mosaic-tall {
74
+ grid-row: span 2;
75
+ aspect-ratio: 1 / 2;
76
+ }
77
+
78
+ .mw-mosaic-big {
79
+ grid-column: span 2;
80
+ grid-row: span 2;
81
+ aspect-ratio: 1;
82
+ }
83
+ }
@@ -0,0 +1,47 @@
1
+ @use '../abstracts' as *;
2
+
3
+ @layer mw.components {
4
+ .mw-overlay-btn {
5
+ display: inline-flex;
6
+ align-items: center;
7
+ justify-content: center;
8
+ flex: none;
9
+ width: 2.75rem;
10
+ height: 2.75rem;
11
+ padding: spacing('0');
12
+ border: none;
13
+ border-radius: radius('full');
14
+ background: rgb(0 0 0 / 38%);
15
+ color: $fixed-surface-text;
16
+ font-size: font-size('md');
17
+ line-height: 1;
18
+ cursor: pointer;
19
+ transition: var(--mw-transition-fast);
20
+ @include tappable;
21
+ @include focus-ring;
22
+
23
+ svg {
24
+ width: 1.375rem;
25
+ height: 1.375rem;
26
+ fill: none;
27
+ stroke: currentColor;
28
+ stroke-width: 2;
29
+ stroke-linecap: round;
30
+ stroke-linejoin: round;
31
+ }
32
+
33
+ &:hover {
34
+ background: rgb(0 0 0 / 62%);
35
+ }
36
+
37
+ &:active {
38
+ transform: scale(0.9);
39
+ transition-duration: var(--mw-duration-instant);
40
+ }
41
+
42
+ &:disabled {
43
+ opacity: 0;
44
+ pointer-events: none;
45
+ }
46
+ }
47
+ }
@@ -0,0 +1,180 @@
1
+ @use '../abstracts' as *;
2
+ @use 'overlay-btn';
3
+
4
+ @layer mw.components {
5
+ $two-up: 560px;
6
+ $three-up: 880px;
7
+ $gaps: (
8
+ '': spacing('6'),
9
+ '-gap-sm': spacing('3'),
10
+ '-gap-lg': spacing('7'),
11
+ );
12
+
13
+ // On a landscape phone the width follows from this cap
14
+ $max-height: 78svh;
15
+
16
+ .mw-portrait-gallery {
17
+ position: relative;
18
+ // Inline-size containment would collapse the box in a flex or grid slot
19
+ width: 100%;
20
+ container: mw-portrait-gallery / inline-size;
21
+ }
22
+
23
+ .mw-portrait-gallery-track {
24
+ display: flex;
25
+ overflow-x: auto;
26
+ overscroll-behavior-x: contain;
27
+ scroll-snap-type: x mandatory;
28
+ border-radius: radius('lg');
29
+ scrollbar-width: none;
30
+ @include focus-ring;
31
+
32
+ &::-webkit-scrollbar {
33
+ display: none;
34
+ }
35
+
36
+ @media (prefers-reduced-motion: no-preference) {
37
+ scroll-behavior: smooth;
38
+ }
39
+ }
40
+
41
+ .mw-portrait-gallery-slide {
42
+ position: relative;
43
+ flex: none;
44
+ // The next slide peeks in at the edge
45
+ width: min(86cqi, $max-height * 3 / 4);
46
+ aspect-ratio: 3 / 4;
47
+ margin: spacing('0');
48
+ overflow: hidden;
49
+ border-radius: radius('lg');
50
+ background: $fixed-stage;
51
+ scroll-snap-align: start;
52
+
53
+ > img,
54
+ > video {
55
+ position: absolute;
56
+ inset: 0;
57
+ width: 100%;
58
+ height: 100%;
59
+ object-fit: cover;
60
+ display: block;
61
+ }
62
+
63
+ > figcaption {
64
+ position: absolute;
65
+ inset: auto 0 0;
66
+ padding: spacing('10') spacing('5') spacing('5');
67
+ background: linear-gradient(to top, rgb(0 0 0 / 72%), transparent);
68
+ color: $fixed-surface-text;
69
+ font-size: font-size('sm');
70
+ line-height: 1.45;
71
+ text-shadow: 0 1px 3px rgb(0 0 0 / 50%);
72
+
73
+ strong {
74
+ display: block;
75
+ font-size: font-size('md');
76
+ }
77
+ }
78
+ }
79
+
80
+ .mw-portrait-gallery-tall .mw-portrait-gallery-slide {
81
+ width: min(86cqi, $max-height * 9 / 16);
82
+ aspect-ratio: 9 / 16;
83
+ }
84
+
85
+ .mw-portrait-gallery-dots {
86
+ display: flex;
87
+ justify-content: center;
88
+ gap: spacing('4');
89
+ margin-top: spacing('5');
90
+ }
91
+
92
+ .mw-portrait-gallery-dot {
93
+ width: 7px;
94
+ height: 7px;
95
+ padding: spacing('0');
96
+ border: none;
97
+ border-radius: radius('full');
98
+ background: var(--mw-gray-color);
99
+ cursor: pointer;
100
+ transition: var(--mw-transition-fast);
101
+ @include tappable;
102
+ @include hit-area(6px);
103
+ @include focus-ring;
104
+
105
+ &.mw-active {
106
+ background: var(--mw-primary-text-color);
107
+ transform: scale(1.45);
108
+ }
109
+ }
110
+
111
+ @each $suffix, $gap in $gaps {
112
+ .mw-portrait-gallery#{$suffix} .mw-portrait-gallery-track {
113
+ gap: $gap;
114
+ }
115
+ }
116
+
117
+ @container mw-portrait-gallery (width >= #{$two-up}) {
118
+ .mw-portrait-gallery-dots {
119
+ display: none;
120
+ }
121
+ }
122
+
123
+ // The widths subtract the gaps, so each gap restates them - every two-up
124
+ // rule before every three-up one, or a modifier's two-up rule would win
125
+ @each $up, $share, $gaps-in-row in ($two-up 2.25 1, $three-up 3 2) {
126
+ @container mw-portrait-gallery (width >= #{$up}) {
127
+ @each $suffix, $gap in $gaps {
128
+ $room: calc(100cqi - $gaps-in-row * $gap);
129
+
130
+ .mw-portrait-gallery#{$suffix} .mw-portrait-gallery-slide {
131
+ width: min($room / $share, $max-height * 3 / 4);
132
+ }
133
+
134
+ .mw-portrait-gallery#{$suffix}.mw-portrait-gallery-tall
135
+ .mw-portrait-gallery-slide {
136
+ width: min($room / $share, $max-height * 9 / 16);
137
+ }
138
+ }
139
+ }
140
+ }
141
+
142
+ // A finger swipes, so the arrows are for a mouse only
143
+ .mw-portrait-gallery-prev,
144
+ .mw-portrait-gallery-next {
145
+ display: none;
146
+ position: absolute;
147
+ top: 50%;
148
+ translate: 0 -50%;
149
+ z-index: z-index('lift');
150
+
151
+ @media (hover: hover) and (pointer: fine) {
152
+ display: inline-flex;
153
+ }
154
+ }
155
+
156
+ .mw-portrait-gallery-prev {
157
+ left: spacing('4');
158
+ }
159
+
160
+ .mw-portrait-gallery-next {
161
+ right: spacing('4');
162
+ }
163
+
164
+ // Centred on the photos, not on photos plus the dot row
165
+ .mw-portrait-gallery:has(.mw-portrait-gallery-dots) {
166
+ .mw-portrait-gallery-prev,
167
+ .mw-portrait-gallery-next {
168
+ translate: 0 calc(-50% - #{spacing('5')});
169
+ }
170
+ }
171
+
172
+ @container mw-portrait-gallery (width >= #{$two-up}) {
173
+ .mw-portrait-gallery:has(.mw-portrait-gallery-dots) {
174
+ .mw-portrait-gallery-prev,
175
+ .mw-portrait-gallery-next {
176
+ translate: 0 -50%;
177
+ }
178
+ }
179
+ }
180
+ }