@codeforamerica/marcomms-design-system 1.19.0 → 1.19.1

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 (91) hide show
  1. package/package.json +2 -1
  2. package/src/components/accordion.js +141 -0
  3. package/src/components/accordion.stories.js +56 -0
  4. package/src/components/avatar.js +62 -0
  5. package/src/components/avatar.stories.js +27 -0
  6. package/src/components/bar.js +102 -0
  7. package/src/components/bar.stories.js +22 -0
  8. package/src/components/blob.js +128 -0
  9. package/src/components/blob.stories.js +73 -0
  10. package/src/components/box.js +55 -0
  11. package/src/components/box.stories.js +24 -0
  12. package/src/components/breadcrumbs.js +80 -0
  13. package/src/components/breadcrumbs.stories.js +27 -0
  14. package/src/components/button.js +163 -0
  15. package/src/components/button.scss +157 -0
  16. package/src/components/button.stories.js +49 -0
  17. package/src/components/callout.js +62 -0
  18. package/src/components/callout.stories.js +20 -0
  19. package/src/components/card.js +456 -0
  20. package/src/components/card.stories.js +227 -0
  21. package/src/components/carousel.js +662 -0
  22. package/src/components/carousel.stories.js +165 -0
  23. package/src/components/details.scss +71 -0
  24. package/src/components/details.stories.js +27 -0
  25. package/src/components/form-elements.scss +304 -0
  26. package/src/components/form-elements.stories.js +134 -0
  27. package/src/components/icon.js +44 -0
  28. package/src/components/icon.scss +32 -0
  29. package/src/components/icon.stories.js +38 -0
  30. package/src/components/label.js +63 -0
  31. package/src/components/label.stories.js +29 -0
  32. package/src/components/link-list.scss +80 -0
  33. package/src/components/link-list.stories.js +52 -0
  34. package/src/components/loader.scss +24 -0
  35. package/src/components/loader.stories.js +12 -0
  36. package/src/components/logo-card.js +93 -0
  37. package/src/components/logo-card.stories.js +48 -0
  38. package/src/components/nav.js +98 -0
  39. package/src/components/nav.stories.js +40 -0
  40. package/src/components/page-nav.js +171 -0
  41. package/src/components/page-nav.stories.js +112 -0
  42. package/src/components/pager.js +98 -0
  43. package/src/components/pager.stories.js +30 -0
  44. package/src/components/pagination.js +116 -0
  45. package/src/components/pagination.stories.js +30 -0
  46. package/src/components/person-card.js +240 -0
  47. package/src/components/person-card.stories.js +58 -0
  48. package/src/components/pill.js +33 -0
  49. package/src/components/pill.stories.js +25 -0
  50. package/src/components/placeholder.js +25 -0
  51. package/src/components/placeholder.stories.js +10 -0
  52. package/src/components/promo.js +82 -0
  53. package/src/components/promo.stories.js +37 -0
  54. package/src/components/pullquote.js +42 -0
  55. package/src/components/pullquote.stories.js +16 -0
  56. package/src/components/quote.js +111 -0
  57. package/src/components/quote.stories.js +23 -0
  58. package/src/components/reveal.js +83 -0
  59. package/src/components/reveal.stories.js +40 -0
  60. package/src/components/slide.js +122 -0
  61. package/src/components/slide.stories.js +49 -0
  62. package/src/components/social-icon.js +236 -0
  63. package/src/components/social-icon.stories.js +36 -0
  64. package/src/components/stat.js +92 -0
  65. package/src/components/stat.stories.js +28 -0
  66. package/src/components/tab-list.js +114 -0
  67. package/src/components/tab-list.stories.js +18 -0
  68. package/src/components/tab.js +95 -0
  69. package/src/components/tab.stories.js +29 -0
  70. package/src/components/tile.js +149 -0
  71. package/src/components/tile.stories.js +41 -0
  72. package/src/components/transcript.js +44 -0
  73. package/src/components/transcript.stories.js +103 -0
  74. package/src/core/base.scss +86 -0
  75. package/src/core/colors.mdx +100 -0
  76. package/src/core/grid.mdx +244 -0
  77. package/src/core/grid.scss +394 -0
  78. package/src/core/helpers.scss +111 -0
  79. package/src/core/layout.scss +103 -0
  80. package/src/core/layout.stories.js +145 -0
  81. package/src/core/reset.scss +53 -0
  82. package/src/core/shadows.mdx +108 -0
  83. package/src/core/tokens.scss +261 -0
  84. package/src/core/typography.mdx +79 -0
  85. package/src/core/typography.scss +411 -0
  86. package/src/core.js +10 -0
  87. package/src/index.js +43 -0
  88. package/src/shared/common.js +65 -0
  89. package/src/shared/layout.js +14 -0
  90. package/src/shared/typography.js +397 -0
  91. package/src/styles.scss +15 -0
@@ -0,0 +1,394 @@
1
+ @use "sass:math";
2
+ @use "sass:list";
3
+
4
+ // ===========
5
+ // Grid System
6
+ // ===========
7
+
8
+ // -------------
9
+ // Configuration
10
+ // -------------
11
+
12
+ $grid-columns: 12;
13
+ $base-font-size: 20px;
14
+ $gap-width: 1.5 * $base-font-size; // 30px - equivalent to --spacing-layout-1
15
+ $outer-margin: 1.5 * $base-font-size; // 30px - equivalent to --spacing-layout-1
16
+ $max-width: 1200px;
17
+
18
+ // Breakpoints: name, min-width, container-width
19
+ $breakpoints: (
20
+ sm: (480px, 480px),
21
+ md: (768px, 768px),
22
+ lg: (1024px, $max-width)
23
+ );
24
+
25
+ // Calculated values
26
+ $gap-compensation: calc($gap-width * -0.5);
27
+ $half-gap-width: calc($gap-width * 0.5);
28
+
29
+ // ------
30
+ // Mixins
31
+ // ------
32
+
33
+ @mixin flex-container($direction: row, $wrap: wrap) {
34
+ display: flex;
35
+ flex-flow: $direction $wrap;
36
+ }
37
+
38
+ @mixin col-base {
39
+ box-sizing: border-box;
40
+ flex: 0 0 auto;
41
+ }
42
+
43
+ @mixin col-size($size) {
44
+ // Calculate width accounting for gaps between columns
45
+ $gap-count: $grid-columns - 1;
46
+ $total-gap-width: $gap-count * $gap-width;
47
+ $available-width: calc(100% - #{$total-gap-width});
48
+ $width: calc(#{$available-width} / #{$grid-columns} * #{$size});
49
+
50
+ // For single column, add back the gap space
51
+ @if $size == $grid-columns {
52
+ $width: 100%;
53
+ }
54
+
55
+ @else {
56
+ $additional-gaps: $size - 1;
57
+ $width: calc(#{$width} + #{$additional-gaps * $gap-width});
58
+ }
59
+
60
+ flex-basis: $width;
61
+ max-width: $width;
62
+ }
63
+
64
+ @mixin col-offset($size) {
65
+ @if $size == 0 {
66
+ margin-left: 0;
67
+ }
68
+
69
+ @else {
70
+ // Calculate offset accounting for gaps between columns
71
+ $gap-count: $grid-columns - 1;
72
+ $total-gap-width: $gap-count * $gap-width;
73
+ $available-width: calc(100% - #{$total-gap-width});
74
+ $additional-gaps: $size;
75
+
76
+ margin-left: calc(#{$available-width} / #{$grid-columns} * #{$size} + #{$additional-gaps * $gap-width});
77
+ }
78
+ }
79
+
80
+ // ----------
81
+ // Containers
82
+ // ----------
83
+
84
+ .wrapper {
85
+ box-sizing: border-box;
86
+ max-width: $max-width;
87
+ margin: 0 auto;
88
+ padding-inline: $outer-margin;
89
+ }
90
+
91
+ .container-fluid {
92
+ margin-inline: auto;
93
+ display: flex;
94
+ flex-direction: column;
95
+ width: 100%;
96
+ }
97
+
98
+ // No gap variant
99
+ .container-fluid--no-gap {
100
+ row-gap: 0;
101
+ }
102
+
103
+ // Apply gaps only when direct children are rows
104
+ .container-fluid:has(> .row):not(.container-fluid--no-gap) {
105
+ row-gap: var(--row-gap, #{$gap-width});
106
+ }
107
+
108
+ // When wrapper and container-fluid are combined, wrapper handles padding
109
+ .wrapper.container-fluid {
110
+ padding-inline: $outer-margin;
111
+ }
112
+
113
+ .container {
114
+ margin-inline: auto;
115
+ padding-inline: $outer-margin;
116
+ display: flex;
117
+ flex-direction: column;
118
+ width: 100%;
119
+ }
120
+
121
+ // ----
122
+ // Rows
123
+ // ----
124
+
125
+ .row {
126
+ @include flex-container(row, wrap);
127
+
128
+ box-sizing: border-box;
129
+ flex-basis: 100%;
130
+ column-gap: var(--column-gap, #{$gap-width});
131
+ row-gap: var(--row-gap, var(--column-gap, #{$gap-width}));
132
+
133
+ // Row variants
134
+ &.reverse {
135
+ flex-direction: row-reverse;
136
+ }
137
+
138
+ // No gap modifier
139
+ &--no-gap {
140
+ column-gap: 0;
141
+ row-gap: 0;
142
+
143
+ // Override column sizing to use simple percentages
144
+ @for $i from 1 through $grid-columns {
145
+ .col-xs-#{$i} {
146
+ flex-basis: math.percentage(math.div($i, $grid-columns));
147
+ max-width: math.percentage(math.div($i, $grid-columns));
148
+ }
149
+ }
150
+
151
+ // Responsive column overrides
152
+ @each $name, $values in $breakpoints {
153
+ $min-width: list.nth($values, 1);
154
+
155
+ @media only screen and (min-width: $min-width) {
156
+ @for $i from 1 through $grid-columns {
157
+ .col-#{$name}-#{$i} {
158
+ flex-basis: math.percentage(math.div($i, $grid-columns));
159
+ max-width: math.percentage(math.div($i, $grid-columns));
160
+ }
161
+ }
162
+ }
163
+ }
164
+ }
165
+ }
166
+
167
+ .container-fluid > .row {
168
+ flex: 1;
169
+ }
170
+
171
+ // Testing variant - shows grid lines between columns
172
+ .container-fluid--grid-test {
173
+ position: relative;
174
+
175
+ // Fill the outer margins with light grey
176
+ &::before {
177
+ content: '';
178
+ position: absolute;
179
+ top: 0;
180
+ left: 0;
181
+ right: 0;
182
+ bottom: 0;
183
+ pointer-events: none;
184
+ z-index: 0;
185
+ background:
186
+ // Left margin
187
+ linear-gradient(
188
+ to right,
189
+ rgba(200, 200, 200, 0.3) 0,
190
+ rgba(200, 200, 200, 0.3) $outer-margin,
191
+ transparent $outer-margin
192
+ ),
193
+ // Right margin
194
+ linear-gradient(
195
+ to left,
196
+ rgba(200, 200, 200, 0.3) 0,
197
+ rgba(200, 200, 200, 0.3) $outer-margin,
198
+ transparent $outer-margin
199
+ );
200
+ }
201
+
202
+ // Fill the gaps between columns with light grey
203
+ &::after {
204
+ content: '';
205
+ position: absolute;
206
+ top: 0;
207
+ left: $outer-margin;
208
+ right: $outer-margin;
209
+ bottom: 0;
210
+ pointer-events: none;
211
+ z-index: 1;
212
+ background-image: repeating-linear-gradient(
213
+ to right,
214
+ transparent 0,
215
+ transparent calc((100% - #{$gap-width * 11}) / 12), // Column width
216
+ rgba(200, 200, 200, 0.4) calc((100% - #{$gap-width * 11}) / 12), // Start of gap
217
+ rgba(200, 200, 200, 0.4) calc((100% - #{$gap-width * 11}) / 12 + #{$gap-width}), // End of gap
218
+ transparent calc((100% - #{$gap-width * 11}) / 12 + #{$gap-width}) // Next column starts
219
+ );
220
+ }
221
+
222
+ .row {
223
+ position: relative;
224
+ z-index: 2;
225
+ }
226
+ }
227
+
228
+ // -------
229
+ // Columns
230
+ // -------
231
+
232
+ // Auto-width column
233
+ .col-xs {
234
+ @include col-base;
235
+
236
+ flex-grow: 1;
237
+ flex-basis: 0;
238
+ max-width: 100%;
239
+ }
240
+
241
+ // Sized columns (1-12)
242
+ @for $i from 1 through $grid-columns {
243
+ .col-xs-#{$i} {
244
+ @include col-base;
245
+ @include col-size($i);
246
+ }
247
+ }
248
+
249
+ // Offset columns (0-12)
250
+ @for $i from 0 through $grid-columns {
251
+ .col-xs-offset-#{$i} {
252
+ @include col-offset($i);
253
+ }
254
+ }
255
+
256
+ // Column reverse
257
+ .col.reverse {
258
+ flex-direction: column-reverse;
259
+ }
260
+
261
+ // ------------------
262
+ // Responsive columns
263
+ // ------------------
264
+
265
+ @each $name, $values in $breakpoints {
266
+ $min-width: list.nth($values, 1);
267
+ $container-width: list.nth($values, 2);
268
+
269
+ @media only screen and (min-width: $min-width) {
270
+ .container {
271
+ width: $container-width;
272
+ }
273
+
274
+ // Auto-width column
275
+ .col-#{$name} {
276
+ @include col-base;
277
+
278
+ flex-grow: 1;
279
+ flex-basis: 0;
280
+ max-width: 100%;
281
+ }
282
+
283
+ // Sized columns (1-12)
284
+ @for $i from 1 through $grid-columns {
285
+ .col-#{$name}-#{$i} {
286
+ @include col-base;
287
+ @include col-size($i);
288
+ }
289
+ }
290
+
291
+ // Offset columns (0-12)
292
+ @for $i from 0 through $grid-columns {
293
+ .col-#{$name}-offset-#{$i} {
294
+ @include col-offset($i);
295
+ }
296
+ }
297
+
298
+ // Row flex sizing
299
+ @for $i from 1 through 3 {
300
+ .row-#{$name}-#{$i} {
301
+ flex: $i !important;
302
+ }
303
+ }
304
+
305
+ // Row ordering
306
+ .row-first-#{$name} {
307
+ order: -1;
308
+ }
309
+
310
+ .row-last-#{$name} {
311
+ order: 1;
312
+ }
313
+ }
314
+ }
315
+
316
+ // -------------------
317
+ // Alignment Utilities
318
+ // -------------------
319
+
320
+ // Horizontal alignment (justify-content)
321
+ .start-xs { justify-content: flex-start; }
322
+
323
+ .center-xs { justify-content: center; }
324
+
325
+ .end-xs { justify-content: flex-end; }
326
+
327
+ .around-xs { justify-content: space-around; }
328
+
329
+ .between-xs { justify-content: space-between; }
330
+
331
+ // Vertical alignment (align-items)
332
+ .top-xs { align-items: flex-start; }
333
+
334
+ .middle-xs { align-items: center; }
335
+
336
+ .bottom-xs { align-items: flex-end; }
337
+
338
+ .stretch-xs {
339
+ align-items: stretch;
340
+
341
+ > * {
342
+ display: flex;
343
+ }
344
+ }
345
+
346
+ // Ordering
347
+ .first-xs { order: -1; }
348
+
349
+ .last-xs { order: 1; }
350
+
351
+ // Responsive alignment utilities
352
+ @each $name, $values in $breakpoints {
353
+ $min-width: list.nth($values, 1);
354
+
355
+ @media only screen and (min-width: $min-width) {
356
+ // Horizontal alignment
357
+ .start-#{$name} { justify-content: flex-start; }
358
+ .center-#{$name} { justify-content: center; }
359
+ .end-#{$name} { justify-content: flex-end; }
360
+ .around-#{$name} { justify-content: space-around; }
361
+ .between-#{$name} { justify-content: space-between; }
362
+
363
+ // Vertical alignment
364
+ .top-#{$name} { align-items: flex-start; }
365
+ .middle-#{$name} { align-items: center; }
366
+ .bottom-#{$name} { align-items: flex-end; }
367
+ .stretch-#{$name} {
368
+ align-items: stretch;
369
+
370
+ & > * {
371
+ display: flex;
372
+ flex-direction: column;
373
+ }
374
+
375
+ & > * > *{
376
+ height: 100%;
377
+ }
378
+ }
379
+
380
+ // Ordering
381
+ .first-#{$name} { order: -1; }
382
+ .last-#{$name} { order: 1; }
383
+ }
384
+ }
385
+
386
+ // -----------------
387
+ // Utility Functions
388
+ // -----------------
389
+
390
+ @function column-span($columns) {
391
+ $width: math.div($max-width - $outer-margin, $grid-columns) * $columns - $gap-width;
392
+
393
+ @return $width;
394
+ }
@@ -0,0 +1,111 @@
1
+
2
+ // Text alignment
3
+
4
+ .text-left {
5
+ text-align: start !important;
6
+ }
7
+
8
+ .text-center {
9
+ text-align: center !important;
10
+ }
11
+
12
+ .text-right {
13
+ text-align: end !important;
14
+ }
15
+
16
+ // Text color
17
+
18
+ .color-purple {
19
+ color: var(--purple-80);
20
+ }
21
+
22
+ .color-red {
23
+ color: var(--red-80);
24
+ }
25
+
26
+ .color-green {
27
+ color: var(--green-80);
28
+ }
29
+
30
+ // Margins
31
+
32
+ .no-margin {
33
+ margin: 0;
34
+ }
35
+
36
+ // Rounded
37
+ .rounded-corners {
38
+ border-radius: var(--rounded-corners);
39
+ }
40
+
41
+ // Hidden
42
+
43
+ .is-hidden {
44
+ border: 0;
45
+ clip: rect(0, 0, 0, 0);
46
+ height: 1px;
47
+ margin: -1px;
48
+ overflow: hidden;
49
+ padding: 0;
50
+ position: absolute;
51
+ white-space: nowrap;
52
+ width: 1px;
53
+ }
54
+
55
+ // Loading
56
+
57
+ .is-loading {
58
+ position: relative;
59
+ opacity: 0.4;
60
+
61
+ &:after {
62
+ content: '';
63
+ height: 100%;
64
+ left: 0;
65
+ overflow: hidden;
66
+ position: absolute;
67
+ top: 0;
68
+ width: 100%;
69
+ }
70
+ }
71
+
72
+ // Screen-reader-only (for example, for adding screenreader labels to semantic icons)
73
+
74
+ .sr-only,
75
+ .screen-reader-text {
76
+ border: 0;
77
+ clip: rect(0, 0, 0, 0);
78
+ height: 1px;
79
+ margin: -1px;
80
+ overflow: hidden;
81
+ padding: 0;
82
+ position: absolute;
83
+ white-space: nowrap;
84
+ width: 1px;
85
+ }
86
+
87
+ // Skip link
88
+
89
+ a.skip-link {
90
+ background-color: var(--white);
91
+ height: 1px;
92
+ left: -999px;
93
+ overflow: hidden;
94
+ position: absolute;
95
+ text-decoration: none;
96
+ top: auto;
97
+ width: 1px;
98
+ z-index: -999;
99
+
100
+ &:focus,
101
+ &:active {
102
+ height: initial;
103
+ left: var(--spacing-component-2);
104
+ overflow: auto;
105
+ padding: var(--spacing-component-2);
106
+ text-align: center;
107
+ top: var(--spacing-component-2);
108
+ width: initial;
109
+ z-index: 999;
110
+ }
111
+ }
@@ -0,0 +1,103 @@
1
+ @use "grid";
2
+
3
+ // Sections
4
+
5
+ .section {
6
+ --bg-color: var(--white);
7
+ --border: var(--hairline) solid var(--black-10);
8
+ --padding: var(--spacing-layout-2);
9
+ --spacing: var(--spacing-layout-1);
10
+
11
+ background-color: var(--bg-color);
12
+ border-block-start: var(--border);
13
+ padding-block: var(--padding);
14
+ }
15
+
16
+ // ======
17
+
18
+ // Cluster
19
+
20
+ .cluster {
21
+ overflow: hidden;
22
+
23
+ & > * {
24
+ display: flex;
25
+ flex-wrap: wrap;
26
+ margin: var(--spacing, calc(var(--spacing-layout-1) / 2 * -1));
27
+ }
28
+
29
+ & > * > * {
30
+ margin: var(--spacing, calc(var(--spacing-layout-1) / 2));
31
+ }
32
+ }
33
+
34
+ // =====
35
+
36
+ // Stack
37
+
38
+ .stack {
39
+ --spacing: var(--spacing-layout-1);
40
+
41
+ & > * + * {
42
+ margin-block-start: var(--spacing);
43
+ }
44
+ }
45
+
46
+ // Special case: when stack is combined with flex containers, use gap instead of margins
47
+ .container-fluid.stack {
48
+ gap: var(--spacing);
49
+
50
+ // Reset margin-based spacing since gap handles it
51
+ & > * + * {
52
+ margin-block-start: 0;
53
+ }
54
+ }
55
+
56
+ .stack-with-lines {
57
+ & > * + *::before {
58
+ border-block-start: var(--hairline) solid var(--black-20);
59
+ content: "";
60
+ display: block;
61
+ margin-block: var(--spacing, var(--spacing-layout-1));
62
+ width: 100%;
63
+ }
64
+ }
65
+
66
+ // =====
67
+
68
+ // Grid
69
+
70
+ :has(> .grid) {
71
+ container-type: inline-size;
72
+ }
73
+
74
+ .grid {
75
+ --spacing: var(--spacing-layout-1);
76
+
77
+ align-items: stretch;
78
+ column-gap: var(--spacing);
79
+ display: flex;
80
+ flex-flow: row wrap;
81
+ justify-content: flex-start;
82
+ row-gap: var(--spacing);
83
+
84
+ & > * {
85
+ flex: 0 0 auto;
86
+ flex-basis: 100%;
87
+ max-width: 100%;
88
+ }
89
+
90
+ @container (width >= 768px) {
91
+ & > * {
92
+ flex-basis: calc((100% - var(--spacing) * 2) / 3);
93
+ max-width: calc((100% - var(--spacing) * 2) / 3);
94
+ }
95
+
96
+ /* Center align when there are fewer than 3 items */
97
+ &:has(> :nth-child(1):nth-last-child(1)),
98
+ &:has(> :nth-child(2):nth-last-child(1)) {
99
+ justify-content: center;
100
+ }
101
+ }
102
+ }
103
+