maverick-wave 5.22.0 → 5.24.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.
@@ -0,0 +1,74 @@
1
+ <h3 class="mw-section-subtitle">Section Head</h3>
2
+
3
+ <p class="mw-text-muted mw-mb-6">
4
+ The opening of a section: a hairline rule, a mark sitting on its left end, the
5
+ title, and the line that says what follows. Everything starts at one edge -
6
+ centred body copy moves its own left edge with every line break, so the eye
7
+ has to find the start of each line instead of jumping to a fixed position.
8
+ Fine for a heading, tiring from the third line on.
9
+ </p>
10
+
11
+ <div class="mw-grid-1">
12
+ <div>
13
+ <h3 class="mw-mb-4">Title, Lead and Mark</h3>
14
+ <div class="mw-card mw-card-simple">
15
+ <div class="mw-section-head">
16
+ <h2 class="mw-section-head-title">Foundations</h2>
17
+ <p class="mw-section-head-intro">
18
+ The raw material - type, color, spacing and the small utilities
19
+ everything else is built from. The lead stops at 65 characters, or at
20
+ four fifths of the container, whichever comes first.
21
+ </p>
22
+ </div>
23
+ <p class="mw-mb-0">The content of the section starts here.</p>
24
+ </div>
25
+ </div>
26
+
27
+ <div>
28
+ <h3 class="mw-mb-4">Numbered</h3>
29
+ <p class="mw-text-muted mw-mb-4">
30
+ <code>mw-section-head-numbered</code> counts in the browser, so no page
31
+ keeps its own running order in the markup. The number closes the rule the
32
+ mark opens, and the counter restarts per <code>mw-main</code>. Opt-in: a
33
+ single section on a legal page numbered "01" is noise.
34
+ </p>
35
+ <div class="mw-card mw-card-simple">
36
+ <div class="mw-section-head mw-section-head-numbered">
37
+ <h2 class="mw-section-head-title">About me</h2>
38
+ <p class="mw-section-head-intro">
39
+ A section head without a lead works the same way - the next one below
40
+ shows it.
41
+ </p>
42
+ </div>
43
+
44
+ <div class="mw-section-head mw-section-head-numbered">
45
+ <h2 class="mw-section-head-title">Contact</h2>
46
+ </div>
47
+ </div>
48
+ </div>
49
+
50
+ <div>
51
+ <h3 class="mw-mb-4">Colored Mark</h3>
52
+ <p class="mw-text-muted mw-mb-4">
53
+ The mark is the one colored thing in the head, so it is where a section
54
+ picks up a tone: <code>mw-section-head-secondary</code>,
55
+ <code>-success</code>, <code>-warning</code>, <code>-danger</code> and
56
+ <code>-info</code>. Its length is
57
+ <code>--mw-section-head-mark-width</code> (80px), the gap to the content
58
+ below <code>--mw-section-head-gap</code>.
59
+ </p>
60
+ <div class="mw-card mw-card-simple">
61
+ <div class="mw-section-head mw-section-head-secondary">
62
+ <h2 class="mw-section-head-title">Secondary</h2>
63
+ </div>
64
+
65
+ <div class="mw-section-head mw-section-head-danger">
66
+ <h2 class="mw-section-head-title">Danger</h2>
67
+ </div>
68
+
69
+ <div class="mw-section-head mw-section-head-success">
70
+ <h2 class="mw-section-head-title">Success</h2>
71
+ </div>
72
+ </div>
73
+ </div>
74
+ </div>
@@ -23,17 +23,48 @@
23
23
  // Only the slope is in vw: a size in vw alone ignores the reader's font size and
24
24
  // stops responding to zoom, the rem term is what keeps that alive.
25
25
  @function fluid($min, $max, $from: 'xs', $to: 'md') {
26
- $min-rem: _to-rem($min, 'min');
27
- $max-rem: _to-rem($max, 'max');
28
- $vw-from: math.div(map.get(v.$breakpoints, $from), 16px);
29
- $vw-to: math.div(map.get(v.$breakpoints, $to), 16px);
26
+ @return _ramp(
27
+ _to-rem($min, 'min'),
28
+ _to-rem($max, 'max'),
29
+ math.div(map.get(v.$breakpoints, $from), 16px),
30
+ math.div(map.get(v.$breakpoints, $to), 16px),
31
+ vw
32
+ );
33
+ }
34
+
35
+ // The same ramp against the container instead of the viewport. For anything
36
+ // inside a `@container` block: sizing there off vw means a 440px card on a
37
+ // 1400px screen takes the width a 900px card was measured for, which is the
38
+ // mismatch container queries exist to remove. The endpoints are container
39
+ // widths rather than breakpoint names, because a container has no breakpoints.
40
+ @function fluid-container($min, $max, $from, $to) {
41
+ $from-rem: _to-rem($from, 'from');
42
+ $to-rem: _to-rem($to, 'to');
43
+
44
+ @if $from-rem == $to-rem {
45
+ @error "fluid-container(): $from and $to must differ, both are #{$from}.";
46
+ }
47
+
48
+ @return _ramp(
49
+ _to-rem($min, 'min'),
50
+ _to-rem($max, 'max'),
51
+ $from-rem,
52
+ $to-rem,
53
+ cqi
54
+ );
55
+ }
30
56
 
31
- $slope: math.div($max-rem - $min-rem, $vw-to - $vw-from);
32
- $intercept: $min-rem - $slope * $vw-from;
57
+ // Both of the above draw the same line through (from, min) and (to, max) and
58
+ // disagree only on what measures the axis. Shared so a change to the rem term -
59
+ // the part that keeps the size responding to zoom - cannot reach one and miss
60
+ // the other.
61
+ @function _ramp($min-rem, $max-rem, $from-rem, $to-rem, $unit) {
62
+ $slope: math.div($max-rem - $min-rem, $to-rem - $from-rem);
63
+ $intercept: $min-rem - $slope * $from-rem;
33
64
 
34
65
  @return clamp(
35
66
  #{$min-rem}rem,
36
- #{_round4($intercept)}rem + #{_round4($slope * 100)}vw,
67
+ #{_round4($intercept)}rem + #{_round4($slope * 100)}#{$unit},
37
68
  #{$max-rem}rem
38
69
  );
39
70
  }
@@ -159,7 +159,12 @@
159
159
  // Point `--mw-section-padding-block` at it to switch a page over - a choice
160
160
  // like the presets are, not the default.
161
161
  --mw-section-padding-fluid: #{fluid(spacing('7'), spacing('12'), $to: 'xl')};
162
- --mw-section-title-gap: #{spacing('7')};
162
+ // The section head: the gap it leaves to the content below, the space
163
+ // between its rule and the title, and the mark sitting on that rule.
164
+ --mw-section-head-gap: #{spacing('10')};
165
+ --mw-section-head-pad: #{spacing('6')};
166
+ --mw-section-head-mark-width: 80px;
167
+ --mw-section-head-mark-color: var(--mw-primary-color);
163
168
  --mw-scroll-hint-offset: #{spacing('9')};
164
169
  --mw-scroll-hint-inline: var(--mw-container-gutter);
165
170
  --mw-scroll-hint-reserve: #{spacing('10')};
@@ -0,0 +1,29 @@
1
+ @use '../abstracts' as *;
2
+
3
+ @layer mw.components {
4
+ // The block that says how to reach someone - postal address, phone, mail.
5
+ // Belongs on an <address>, which is what the one font-style is for: the
6
+ // element is italic by default, and nobody sets an address in italics.
7
+ .mw-contact {
8
+ font-style: normal;
9
+ display: grid;
10
+ gap: spacing('3');
11
+
12
+ > * {
13
+ margin: 0;
14
+ }
15
+
16
+ &-row {
17
+ display: flex;
18
+ flex-wrap: wrap;
19
+ gap: spacing('1') spacing('4');
20
+ }
21
+
22
+ // Sized rather than auto, so the values line up under each other however
23
+ // long the words in front of them are
24
+ &-label {
25
+ flex: 0 0 5rem;
26
+ color: var(--mw-text-muted-color);
27
+ }
28
+ }
29
+ }
@@ -0,0 +1,141 @@
1
+ @use '../abstracts' as *;
2
+
3
+ @layer mw.components {
4
+ // Every fluid value here runs the same ramp: from the width the two-column
5
+ // layout starts at to the width a day card stops growing at. Against the
6
+ // container, not the viewport - the layout switch below is a container query,
7
+ // and sizing off vw would hand a narrow card in a wide window the
8
+ // measurements of a wide one.
9
+ @function _stamp-ramp($min, $max) {
10
+ @return fluid-container($min, $max, $mw-card-wide, 900px);
11
+ }
12
+
13
+ // Named because the today fill cancels the paddings to reach the card border,
14
+ // and the price variant repeats the track list - two copies would drift apart.
15
+ $_mw-date-stamp-pad-top: _stamp-ramp(spacing('4'), spacing('6'));
16
+ $_mw-date-stamp-pad-bottom: _stamp-ramp(spacing('3'), spacing('5'));
17
+ $_mw-date-stamp-day-col: _stamp-ramp(4.25rem, 7rem);
18
+ $_mw-date-stamp-gap: _stamp-ramp(spacing('4'), spacing('7'));
19
+ $_mw-date-stamp-day-size: _stamp-ramp('xl', '3xl');
20
+
21
+ // The date on a dated card - an event, a changelog entry, a day on a menu.
22
+ // Narrow, it is a line above the title. Wide, it turns into a column beside
23
+ // the content, which is the only thing that fills the right half of a card
24
+ // that has a whole row to itself.
25
+ .mw-date-stamp {
26
+ display: flex;
27
+ align-items: baseline;
28
+ gap: spacing('3');
29
+ margin-bottom: spacing('4');
30
+ color: var(--mw-text-muted-color);
31
+
32
+ &-day {
33
+ font-size: font-size('sm');
34
+ font-weight: font-weight('bold');
35
+ text-transform: uppercase;
36
+ letter-spacing: 0.08em;
37
+ }
38
+
39
+ &-date {
40
+ font-size: font-size('sm');
41
+ font-variant-numeric: tabular-nums;
42
+ }
43
+
44
+ // A fill, not a text colour: the price on the same card is already primary,
45
+ // and two blues compete where one would point. The padding is pulled back
46
+ // out again so the fill bleeds around the line instead of indenting it.
47
+ &-today {
48
+ padding: spacing('2') spacing('3');
49
+ margin-inline: spacing('-3');
50
+ margin-block-start: spacing('-2');
51
+ border-radius: radius('sm') radius('lg') radius('sm') radius('lg');
52
+ background: var(--mw-primary-color);
53
+ color: var(--mw-primary-accent-text-color);
54
+ }
55
+ }
56
+
57
+ // No box of its own until there are two columns to be one of, so the card body
58
+ // keeps seeing the title, the text and the price as its own children - which
59
+ // is what mw-offer and mw-card-body's last-child rule both select on
60
+ .mw-date-stamp-body {
61
+ display: contents;
62
+ }
63
+
64
+ @container mw-card (width >= #{$mw-card-wide}) {
65
+ // The rule sits here and not in _cards.scss because the stamp is the guest -
66
+ // the card body's own single column is right for everything that is not one
67
+ .mw-card-body:has(> .mw-date-stamp) {
68
+ display: grid;
69
+ grid-template-columns: $_mw-date-stamp-day-col minmax(0, 1fr);
70
+ // On the same curve as the column, so the dish keeps the width the day
71
+ // and the price give up rather than losing it again to the gutters
72
+ column-gap: $_mw-date-stamp-gap;
73
+ padding-block: $_mw-date-stamp-pad-top $_mw-date-stamp-pad-bottom;
74
+ }
75
+
76
+ // A third track, for the one price that covers the whole day - it is what the
77
+ // right half of a wide card is otherwise spent on. Only when there is a
78
+ // price: with a dish per row it travels with its dish, and an empty track
79
+ // would still charge the card a gap.
80
+ .mw-card-body:has(> .mw-date-stamp):has(> .mw-price) {
81
+ grid-template-columns: $_mw-date-stamp-day-col minmax(0, 1fr) auto;
82
+ }
83
+
84
+ // The row is left to stretch to the body, so a today fill can span the whole
85
+ // card - which means each column has to centre its own content instead
86
+ .mw-date-stamp-body {
87
+ display: flex;
88
+ flex-direction: column;
89
+ justify-content: center;
90
+ }
91
+
92
+ // The card body zeroes its own last child, but the wrapper hides ours from
93
+ // that rule - without this a trailing tag row adds a paragraph margin
94
+ .mw-date-stamp-body > *:last-child {
95
+ margin-bottom: spacing('0');
96
+ }
97
+
98
+ // Beside the dish it prices, not under the description that follows it. The
99
+ // second selector only exists to outrank mw-offer, which pushes a price to
100
+ // the bottom of the card and ties with the first on specificity alone.
101
+ .mw-card-body:has(> .mw-date-stamp) > .mw-price,
102
+ .mw-offer > .mw-card-body:has(> .mw-date-stamp) > .mw-price {
103
+ align-self: center;
104
+ justify-self: end;
105
+ margin-top: spacing('0');
106
+ padding-top: spacing('0');
107
+ text-align: right;
108
+ }
109
+
110
+ .mw-date-stamp {
111
+ flex-direction: column;
112
+ align-items: flex-start;
113
+ // Stretched to the row, so the day and the date centre against the title
114
+ // and description beside them rather than hanging off the first line
115
+ justify-content: center;
116
+ gap: spacing('2');
117
+ margin-bottom: spacing('0');
118
+ }
119
+
120
+ // Top edge of the card to its bottom one: the body's padding is handed back
121
+ // so the fill reaches the border. Only the two left corners are rounded, and
122
+ // to the card's own arc less the 1px it sits inside - the right edge is an
123
+ // inside edge and a cut is what reads as one.
124
+ .mw-date-stamp-today {
125
+ margin-block: calc(-1 * #{$_mw-date-stamp-pad-top})
126
+ calc(-1 * #{$_mw-date-stamp-pad-bottom});
127
+ margin-inline: calc(-1 * #{spacing('5')}) 0;
128
+ padding-block: $_mw-date-stamp-pad-top $_mw-date-stamp-pad-bottom;
129
+ padding-inline: spacing('5');
130
+ border-radius: calc(#{radius-sharp()} - 1px) 0 0
131
+ calc(#{radius('xl')} - 1px);
132
+ }
133
+
134
+ .mw-date-stamp-day {
135
+ // Tracks the column it sits in, or it would outgrow it on the way down
136
+ font-size: $_mw-date-stamp-day-size;
137
+ line-height: 1;
138
+ letter-spacing: 0.02em;
139
+ }
140
+ }
141
+ }
@@ -11,7 +11,9 @@
11
11
  @forward 'cards';
12
12
  @forward 'code';
13
13
  @forward 'coming-soon';
14
+ @forward 'contact';
14
15
  @forward 'content-slider';
16
+ @forward 'date-stamp';
15
17
  @forward 'divider';
16
18
  @forward 'dropdown';
17
19
  @forward 'empty-state';
@@ -21,6 +23,7 @@
21
23
  @forward 'kanban';
22
24
  @forward 'kbd';
23
25
  @forward 'lang-switch';
26
+ @forward 'leader-row';
24
27
  @forward 'links';
25
28
  @forward 'lists';
26
29
  @forward 'localhost-indicator';
@@ -0,0 +1,89 @@
1
+ @use '../abstracts' as *;
2
+
3
+ @layer mw.components {
4
+ // Dots on the baseline, not under the descenders; em so they follow font-size
5
+ $_mw-leader-baseline-lift: 0.34em;
6
+
7
+ // A label on the left, its value on the right, a dotted line bridging the
8
+ // gap. The line is the whole point: over a wide row the eye loses track of
9
+ // which value belongs to which label, which is why every printed menu, index
10
+ // and invoice draws one.
11
+ //
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
14
+ // between the two spans - and generated content stays out of the
15
+ // accessibility tree, where a filler element would need aria-hidden.
16
+ .mw-leader-row {
17
+ --mw-leader-row-style: dotted;
18
+ --mw-leader-row-width: 2px;
19
+ --mw-leader-row-color: var(--mw-border);
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;
26
+ gap: spacing('3');
27
+ margin: 0;
28
+
29
+ &::before {
30
+ content: '';
31
+ 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
+ border-bottom: var(--mw-leader-row-width) var(--mw-leader-row-style)
36
+ var(--mw-leader-row-color);
37
+ margin-bottom: $_mw-leader-baseline-lift;
38
+ }
39
+
40
+ &-label {
41
+ 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
+ //
46
+ // The row also works as a dl group, and a dd carries 40px of browser margin
47
+ margin: 0;
48
+ }
49
+
50
+ &-value {
51
+ order: 2;
52
+ flex-shrink: 0;
53
+ margin: 0;
54
+ // A price or a page number must not break, and a column of them only
55
+ // lines up with tabular figures.
56
+ white-space: nowrap;
57
+ font-variant-numeric: tabular-nums;
58
+ }
59
+
60
+ // Two values in one row read as a single long range with only a gap between
61
+ // 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.
64
+ &-value:has(+ &-value)::after {
65
+ content: '|';
66
+ margin-left: spacing('3');
67
+ color: var(--mw-leader-row-color);
68
+ }
69
+
70
+ &-dashed {
71
+ --mw-leader-row-style: dashed;
72
+ }
73
+
74
+ &-solid {
75
+ --mw-leader-row-style: solid;
76
+ --mw-leader-row-width: 1px;
77
+ }
78
+
79
+ &-top {
80
+ align-items: flex-start;
81
+
82
+ &::before {
83
+ // flex-start pins the leader to the top edge, so it drops to the first
84
+ // line's baseline itself instead of being lifted off the last one
85
+ margin: calc(1lh - #{$_mw-leader-baseline-lift}) 0 0;
86
+ }
87
+ }
88
+ }
89
+ }
@@ -91,6 +91,10 @@
91
91
  }
92
92
 
93
93
  // Size. Only the token moves - see the `em` above.
94
+ .mw-price-xs {
95
+ --mw-price-size: #{font-size('lg')};
96
+ }
97
+
94
98
  .mw-price-sm {
95
99
  --mw-price-size: #{font-size('xl')};
96
100
  }
@@ -159,6 +159,12 @@ $_column-steps-lg: (
159
159
  grid-template-columns: repeat(auto-fill, minmax(min(350px, 100%), 1fr));
160
160
  }
161
161
 
162
+ // Every row as tall as the tallest rather than as tall as its own contents.
163
+ // A modifier, not a grid of its own - it needs one of the above to size.
164
+ .mw-grid-even {
165
+ grid-auto-rows: 1fr;
166
+ }
167
+
162
168
  .mw-grid-lg {
163
169
  @extend %grid-base-lg;
164
170
  }
@@ -8,6 +8,25 @@
8
8
  min-height: 100dvh;
9
9
  display: flex;
10
10
  flex-direction: column;
11
+ // Numbered section heads count per page body, so a second main on the same
12
+ // document starts at 01 again instead of continuing the first one.
13
+ counter-reset: mw-section-head;
14
+ }
15
+
16
+ // Set in from the container it sits in, and giving that back a step at a time
17
+ // as the screen shrinks. For a column of full-width cards, which at 1200px
18
+ // leaves more empty card than content.
19
+ .mw-container-narrow {
20
+ width: 80%;
21
+ margin-inline: auto;
22
+
23
+ @include media-down('lg') {
24
+ width: 90%;
25
+ }
26
+
27
+ @include media-down('md') {
28
+ width: 100%;
29
+ }
11
30
  }
12
31
 
13
32
  .mw-container {
@@ -1,65 +1,135 @@
1
1
  @use '../abstracts' as *;
2
2
 
3
3
  @layer mw.layout {
4
- // The rule under a section heading. Shared, so the two levels cannot drift
5
- // apart in width and weight - the subtitle carried the wider mark, which reads
6
- // as the louder one under the smaller heading. Colour is where they do part.
7
- @mixin _section-rule(
8
- $width,
9
- $height,
10
- $offset,
11
- $color: var(--mw-primary-text-color)
12
- ) {
13
- &::after {
4
+ // The head of a section: a rule, a title, and the line that says what the
5
+ // section is. All three left-aligned, and that is the whole point of the
6
+ // component. Centred running text moves its own left edge with every line
7
+ // break, so the eye has to find the start of each line instead of jumping
8
+ // to a fixed position - fine for a heading, tiring from the third line of
9
+ // body copy on. Aligned, title, lead and content below share one edge, and
10
+ // that edge is what holds a section together.
11
+ .mw-section-head {
12
+ position: relative;
13
+ margin-bottom: var(--mw-section-head-gap);
14
+ padding-top: var(--mw-section-head-pad);
15
+ // Two pixels and not one: the diagonal pattern of mw-section-alternate
16
+ // swallows a hairline, and the rule is the thing that has to survive on
17
+ // every background the framework paints.
18
+ border-top: 2px solid var(--mw-border);
19
+
20
+ // The rule stays quiet so it reads as a boundary and not as a border, and
21
+ // quiet is still hard to see on a patterned band. Hence the mark:
22
+ // a short, thicker piece of the accent colour sitting on the line at the
23
+ // edge everything else starts from. It carries the weight the full-width
24
+ // rule deliberately does not have.
25
+ &::before {
14
26
  content: '';
15
27
  position: absolute;
16
- bottom: $offset;
17
- left: 50%;
18
- transform: translateX(-50%);
19
- width: $width;
20
- height: $height;
28
+ top: 0;
29
+ left: 0;
30
+ transform: translateY(-50%);
31
+ width: var(--mw-section-head-mark-width);
32
+ height: 4px;
21
33
  border-radius: radius('xs');
22
- background: $color;
34
+ background: var(--mw-section-head-mark-color);
23
35
  }
24
36
  }
25
37
 
26
- .mw-section-title {
27
- font-size: fluid('2xl', '3xl', $to: 'sm');
28
- margin-bottom: var(--mw-section-title-gap);
29
- text-align: center;
30
- position: relative;
31
-
32
- @include _section-rule(56px, 3px, spacing('-3'));
38
+ .mw-section-head-title {
39
+ margin: spacing('0');
40
+ font-size: fluid('2xl', '4xl', $to: 'lg');
41
+ line-height: 1;
42
+ letter-spacing: -0.02em;
43
+ text-wrap: balance;
33
44
  }
34
45
 
35
- // The lead line under a section title. Every landing page writes this
36
- // paragraph, and it always wants the same four things: centred, muted,
37
- // narrower than the container so it reads as a line rather than a block, and
38
- // pulled up toward the title it belongs to - the title's gap is sized for the
39
- // content below, not for its own subtitle.
40
- .mw-section-intro {
41
- max-width: 46rem;
42
- margin: spacing('-3') auto spacing('9');
46
+ // Two limits, and the narrower one wins: 65 characters is where a line stops
47
+ // being comfortable, four fifths is what keeps the paragraph from filling the
48
+ // container edge to edge on a wide screen. Below md only the character count
49
+ // is left - four fifths of a phone is a column nothing fits in.
50
+ .mw-section-head-intro {
51
+ max-width: 75ch;
52
+ margin: spacing('4') spacing('0') spacing('0');
43
53
  font-size: font-size('md');
54
+ line-height: 1.6;
44
55
  color: var(--mw-text-muted-color);
45
- text-align: center;
46
- text-wrap: balance;
56
+ text-wrap: pretty;
57
+
58
+ @include media-up('md') {
59
+ max-width: min(80%, 70ch);
60
+ }
61
+
62
+ p {
63
+ margin: spacing('0');
64
+ }
65
+
66
+ p + p {
67
+ margin-top: spacing('4');
68
+ }
69
+ }
70
+
71
+ // Opt-in, because a single section on a legal page numbered "01" is noise.
72
+ // The browser counts, so no page keeps its own running order in the markup -
73
+ // and the number sits at the far end of the rule rather than above the title:
74
+ // the mark opens the line, the number closes it, and the title keeps the
75
+ // first thing read on the left.
76
+ .mw-section-head-numbered {
77
+ counter-increment: mw-section-head;
78
+
79
+ &::after {
80
+ content: counter(mw-section-head, decimal-leading-zero);
81
+ position: absolute;
82
+ top: var(--mw-section-head-pad);
83
+ right: 7px;
84
+ color: var(--mw-text-muted-color);
85
+ font-size: font-size('sm');
86
+ font-weight: font-weight('medium');
87
+ line-height: 1;
88
+ // The tracking sits after the last digit too, so it has to come back off
89
+ // the right or the number stands a space short of the edge it aligns to.
90
+ letter-spacing: 0.2em;
91
+ }
92
+
93
+ // The number owns the right end of the first line - a long title runs into
94
+ // it otherwise, and on a phone that is one word, not a rare case.
95
+ .mw-section-head-title {
96
+ padding-right: spacing('10');
97
+ }
47
98
  }
48
99
 
100
+ // The mark is the one coloured thing in the head, so it is where a section
101
+ // picks up a tone - a danger notice, a success story, a section that belongs
102
+ // to a product colour.
103
+ $_mw-section-head-colors: (secondary, success, warning, danger, info);
104
+
105
+ @each $name in $_mw-section-head-colors {
106
+ .mw-section-head-#{$name} {
107
+ --mw-section-head-mark-color: var(--mw-#{$name}-color);
108
+ }
109
+ }
110
+
111
+ // The level below the head: the boundary inside a section, not around it.
112
+ // Same left edge, and the rule shrinks to a mark of its own so the two
113
+ // levels read as a hierarchy rather than as two of the same thing.
49
114
  .mw-section-subtitle {
50
115
  font-size: fluid('xl', '2xl', $to: 'sm');
51
- // The step above a demo gap of spacing('6') - at spacing('8') the boundary
52
- // between two components read like the one between two demos
53
- margin: spacing('10') spacing('0') spacing('6') spacing('0');
54
- text-align: center;
116
+ // One step above the demo gap of spacing('6'), and that step is the whole
117
+ // margin the boundary between two components has over the one between two
118
+ // demos - the heading leans toward what it names, not toward what came
119
+ // before it.
120
+ margin: spacing('8') spacing('0') spacing('4') spacing('0');
55
121
  position: relative;
56
122
 
57
- @include _section-rule(
58
- 32px,
59
- 2px,
60
- spacing('-2'),
61
- var(--mw-secondary-text-color)
62
- );
123
+ &::after {
124
+ content: '';
125
+ position: absolute;
126
+ bottom: spacing('-2');
127
+ left: 0;
128
+ width: 32px;
129
+ height: 2px;
130
+ border-radius: radius('xs');
131
+ background: var(--mw-secondary-text-color);
132
+ }
63
133
  }
64
134
 
65
135
  // Sticky, because the strip is the only way back: inside a long section the
@@ -135,7 +205,7 @@
135
205
  // The section boundary steps down to 56px on a phone, so the component
136
206
  // boundary steps with it or the two stop being distinguishable
137
207
  .mw-section-subtitle {
138
- margin-top: spacing('7');
208
+ margin-top: spacing('6');
139
209
  }
140
210
  }
141
211