@codeforamerica/marcomms-design-system 1.0.0 → 1.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 (92) hide show
  1. package/dist/styles.css +1 -1
  2. package/package.json +2 -1
  3. package/src/components/accordion.js +141 -0
  4. package/src/components/accordion.stories.js +56 -0
  5. package/src/components/avatar.js +62 -0
  6. package/src/components/avatar.stories.js +27 -0
  7. package/src/components/banner.js +152 -0
  8. package/src/components/banner.stories.js +115 -0
  9. package/src/components/bar.js +102 -0
  10. package/src/components/bar.stories.js +22 -0
  11. package/src/components/blob.js +119 -0
  12. package/src/components/blob.stories.js +64 -0
  13. package/src/components/box.js +55 -0
  14. package/src/components/box.stories.js +24 -0
  15. package/src/components/breadcrumbs.js +80 -0
  16. package/src/components/breadcrumbs.stories.js +27 -0
  17. package/src/components/button.js +167 -0
  18. package/src/components/button.scss +162 -0
  19. package/src/components/button.stories.js +49 -0
  20. package/src/components/callout.js +62 -0
  21. package/src/components/callout.stories.js +20 -0
  22. package/src/components/card.js +403 -0
  23. package/src/components/card.stories.js +170 -0
  24. package/src/components/carousel.js +182 -0
  25. package/src/components/carousel.stories.js +61 -0
  26. package/src/components/cta.js +99 -0
  27. package/src/components/cta.stories.js +22 -0
  28. package/src/components/details.scss +71 -0
  29. package/src/components/details.stories.js +27 -0
  30. package/src/components/flexible-layout.js +126 -0
  31. package/src/components/flexible-layout.stories.js +48 -0
  32. package/src/components/form-elements.scss +305 -0
  33. package/src/components/form-elements.stories.js +134 -0
  34. package/src/components/icon.js +41 -0
  35. package/src/components/icon.scss +31 -0
  36. package/src/components/icon.stories.js +16 -0
  37. package/src/components/label.js +63 -0
  38. package/src/components/label.stories.js +29 -0
  39. package/src/components/link-list.scss +80 -0
  40. package/src/components/link-list.stories.js +52 -0
  41. package/src/components/loader.scss +24 -0
  42. package/src/components/loader.stories.js +12 -0
  43. package/src/components/logo-card.js +93 -0
  44. package/src/components/logo-card.stories.js +48 -0
  45. package/src/components/nav.js +99 -0
  46. package/src/components/nav.stories.js +40 -0
  47. package/src/components/page-nav.js +171 -0
  48. package/src/components/page-nav.stories.js +112 -0
  49. package/src/components/pager.js +98 -0
  50. package/src/components/pager.stories.js +30 -0
  51. package/src/components/pagination.js +116 -0
  52. package/src/components/pagination.stories.js +30 -0
  53. package/src/components/person-card.js +240 -0
  54. package/src/components/person-card.stories.js +58 -0
  55. package/src/components/pill.js +33 -0
  56. package/src/components/pill.stories.js +23 -0
  57. package/src/components/promo.js +83 -0
  58. package/src/components/promo.stories.js +37 -0
  59. package/src/components/pullquote.js +42 -0
  60. package/src/components/pullquote.stories.js +16 -0
  61. package/src/components/quote.js +84 -0
  62. package/src/components/quote.stories.js +23 -0
  63. package/src/components/reveal.js +83 -0
  64. package/src/components/reveal.stories.js +40 -0
  65. package/src/components/slide.js +121 -0
  66. package/src/components/slide.stories.js +53 -0
  67. package/src/components/social-icon.js +233 -0
  68. package/src/components/social-icon.stories.js +36 -0
  69. package/src/components/stat.js +92 -0
  70. package/src/components/stat.stories.js +28 -0
  71. package/src/components/tab-list.js +114 -0
  72. package/src/components/tab-list.stories.js +18 -0
  73. package/src/components/tab.js +95 -0
  74. package/src/components/tab.stories.js +29 -0
  75. package/src/components/tile.js +150 -0
  76. package/src/components/tile.stories.js +41 -0
  77. package/src/components/transcript.js +44 -0
  78. package/src/components/transcript.stories.js +167 -0
  79. package/src/core/base.scss +86 -0
  80. package/src/core/colors.mdx +100 -0
  81. package/src/core/grid.mdx +14 -0
  82. package/src/core/grid.scss +295 -0
  83. package/src/core/helpers.scss +111 -0
  84. package/src/core/layout.scss +79 -0
  85. package/src/core/reset.scss +53 -0
  86. package/src/core/tokens.scss +251 -0
  87. package/src/core/typography.mdx +66 -0
  88. package/src/core/typography.scss +426 -0
  89. package/src/shared/common.js +15 -0
  90. package/src/shared/layout.js +14 -0
  91. package/src/shared/typography.js +111 -0
  92. package/src/styles.scss +15 -0
@@ -0,0 +1,98 @@
1
+ import { LitElement, html, css } from "lit";
2
+ import { commonStyles } from "../shared/common.js";
3
+ import { typographyStyles } from "../shared/typography.js";
4
+ import { buttonStyles } from "./button";
5
+ import "./icon";
6
+
7
+ class Pager extends LitElement {
8
+ static properties = {
9
+ previousTitle: {},
10
+ previousUrl: {},
11
+ nextTitle: {},
12
+ nextUrl: {},
13
+ };
14
+
15
+ static styles = [
16
+ commonStyles,
17
+ typographyStyles,
18
+ buttonStyles,
19
+ css`
20
+ :host {
21
+ display: block;
22
+ }
23
+
24
+ * {
25
+ box-sizing: border-box;
26
+ text-wrap: comfortable;
27
+ }
28
+
29
+ nav {
30
+ display: flex;
31
+ flex-direction: column;
32
+ column-gap: var(--gutter-width);
33
+ row-gap: var(--spacing-layout-half);
34
+ }
35
+
36
+ nav > * > * {
37
+ width: 100%;
38
+ }
39
+
40
+ @media (min-width: 768px) {
41
+ nav {
42
+ flex-direction: row-reverse;
43
+ justify-content: space-between;
44
+ }
45
+
46
+ a {
47
+ min-width: var(--column-span-4);
48
+ }
49
+
50
+ .next a {
51
+ text-align: end;
52
+ }
53
+
54
+ .previous a {
55
+ text-align: start;
56
+ }
57
+ }
58
+ `,
59
+ ];
60
+
61
+ render() {
62
+ return html`
63
+ <nav>
64
+ <div class="next">
65
+ ${this.nextUrl
66
+ ? html`
67
+ <a href="${this.nextUrl}" class="cfa-button">
68
+ <span class="small label"
69
+ >Next <cfa-icon>arrow_forward</cfa-icon></span
70
+ ><br />
71
+ <span class="title normal">${this.nextTitle}</span>
72
+ </a>
73
+ `
74
+ : ""}
75
+ </div>
76
+ <div class="previous">
77
+ ${this.previousUrl
78
+ ? html`
79
+ <a
80
+ href="${this.previousUrl}"
81
+ class="cfa-button cfa-button--outline"
82
+ >
83
+ <span class="small label"
84
+ ><cfa-icon>arrow_back</cfa-icon> Previous</span
85
+ ><br />
86
+ <span class="title normal">${this.previousTitle}</span>
87
+ </a>
88
+ `
89
+ : ""}
90
+ </div>
91
+ </nav>
92
+ `;
93
+ }
94
+ }
95
+
96
+ if (!customElements.get("cfa-pager")) {
97
+ customElements.define("cfa-pager", Pager);
98
+ }
@@ -0,0 +1,30 @@
1
+ import { html } from "lit-html";
2
+ import "./pager";
3
+
4
+ export default {
5
+ title: "Molecules/Pager",
6
+ argTypes: {
7
+ previousTitle: { type: "string" },
8
+ previousUrl: { type: "string" },
9
+ nextTitle: { type: "string" },
10
+ nextUrl: { type: "string" },
11
+ },
12
+ };
13
+
14
+ const Template = ({ previousTitle, previousUrl, nextTitle, nextUrl }) => html`
15
+ <cfa-pager
16
+ previousTitle="${previousTitle}"
17
+ previousUrl="${previousUrl}"
18
+ nextTitle="${nextTitle}"
19
+ nextUrl="${nextUrl}"
20
+ >
21
+ </cfa-pager>
22
+ `;
23
+
24
+ export const Default = Template.bind({});
25
+ Default.args = {
26
+ previousTitle: "How should we measure Safety Net delivery?",
27
+ previousUrl: "#",
28
+ nextTitle: "The National Safety Net Scorecard",
29
+ nextUrl: "#",
30
+ };
@@ -0,0 +1,116 @@
1
+ import { LitElement, html, css } from "lit";
2
+ import { commonStyles } from "../shared/common";
3
+ import { buttonStyles } from "./button";
4
+ import "./icon";
5
+
6
+ class Pagination extends LitElement {
7
+ static properties = {
8
+ page: { type: Number },
9
+ totalPages: { type: Number },
10
+ previousLabel: {},
11
+ nextLabel: {},
12
+ };
13
+ static styles = [
14
+ commonStyles,
15
+ buttonStyles,
16
+ css`
17
+ :host {
18
+ display: block;
19
+ }
20
+
21
+ .pagination {
22
+ align-items: center;
23
+ display: flex;
24
+ flex-direction: row;
25
+ gap: var(--gutter-width);
26
+ justify-content: space-between;
27
+ }
28
+
29
+ .pagination > * {
30
+ flex: 1;
31
+ }
32
+
33
+ .status {
34
+ font-size: var(--font-size-small);
35
+ line-height: var(-line-height-small);
36
+ color: var(--gray-60);
37
+ text-align: center;
38
+ }
39
+
40
+ @media (max-width: 768px) {
41
+ .status {
42
+ display: none;
43
+ }
44
+ }
45
+
46
+ .previous {
47
+ text-align: left;
48
+ }
49
+
50
+ .next {
51
+ text-align: right;
52
+ }
53
+ `,
54
+ ];
55
+ constructor() {
56
+ super();
57
+ this.previousLabel = "Previous";
58
+ this.nextLabel = "Next";
59
+ this.page = 1;
60
+ this.totalPages = 1;
61
+ }
62
+
63
+ render() {
64
+ return html`
65
+ <div class="pagination">
66
+ <div class="previous">
67
+ ${this.page > 1
68
+ ? html`
69
+ <button class="cfa-button" @click="${this.handlePreviousPage}">
70
+ <cfa-icon>arrow_back</cfa-icon>&nbsp;${this.previousLabel}
71
+ </button>
72
+ `
73
+ : ""}
74
+ </div>
75
+ <div class="status">Page ${this.page} of ${this.totalPages}</div>
76
+ <div class="next">
77
+ ${this.page < this.totalPages
78
+ ? html`
79
+ <button class="cfa-button" @click="${this.handleNextPage}">
80
+ ${this.nextLabel}&nbsp;<cfa-icon>arrow_forward</cfa-icon>
81
+ </button>
82
+ `
83
+ : ""}
84
+ </div>
85
+ </div>
86
+ `;
87
+ }
88
+
89
+ handleNextPage() {
90
+ if (this.page < this.totalPages) {
91
+ this.page++;
92
+ const pageChangeEvent = new CustomEvent("page-change", {
93
+ bubbles: true,
94
+ cancelable: true,
95
+ detail: this.page,
96
+ });
97
+ this.dispatchEvent(pageChangeEvent);
98
+ }
99
+ }
100
+
101
+ handlePreviousPage() {
102
+ if (this.page > 1) {
103
+ this.page--;
104
+ const pageChangeEvent = new CustomEvent("page-change", {
105
+ bubbles: true,
106
+ cancelable: true,
107
+ detail: this.page,
108
+ });
109
+ this.dispatchEvent(pageChangeEvent);
110
+ }
111
+ }
112
+ }
113
+
114
+ if (!customElements.get("cfa-pagination")) {
115
+ customElements.define("cfa-pagination", Pagination);
116
+ }
@@ -0,0 +1,30 @@
1
+ import { html } from "lit-html";
2
+ import "./pagination.js";
3
+
4
+ export default {
5
+ title: "Molecules/Pagination",
6
+ argTypes: {
7
+ page: { type: "number", min: 1, max: 100 },
8
+ totalPages: { type: "number", min: 1, max: 100 },
9
+ previousLabel: { type: "string" },
10
+ nextLabel: { type: "string" },
11
+ },
12
+ };
13
+
14
+ const Template = ({ page, totalPages, previousLabel, nextLabel }) => html`
15
+ <cfa-pagination
16
+ page="${page}"
17
+ totalPages="${totalPages}"
18
+ previousLabel="${previousLabel}"
19
+ nextLabel="${nextLabel}"
20
+ >
21
+ </cfa-pagination>
22
+ `;
23
+
24
+ export const Default = Template.bind({});
25
+ Default.args = {
26
+ page: 1,
27
+ totalPages: 3,
28
+ previousLabel: "Previous",
29
+ nextLabel: "Next",
30
+ };
@@ -0,0 +1,240 @@
1
+ import { LitElement, html, css, nothing } from "lit";
2
+ import { commonStyles } from "../shared/common";
3
+ import { typographyStyles } from "../shared/typography";
4
+ import "./avatar";
5
+ import "./social-icon";
6
+
7
+ class PersonCard extends LitElement {
8
+ static properties = {
9
+ name: { type: "string" },
10
+ title: { type: "string" },
11
+ company: { type: "string" },
12
+ pronouns: { type: "string" },
13
+ imageUrl: { type: "string" },
14
+ imageAltText: { type: "string" },
15
+ linkUrl: { type: "string" },
16
+ linkedInUrl: { type: "string" },
17
+ twitterHandle: { type: "string" },
18
+ };
19
+ static styles = [
20
+ commonStyles,
21
+ typographyStyles,
22
+ css`
23
+ :host {
24
+ display: block;
25
+ }
26
+
27
+ .card {
28
+ border-radius: var(--rounded-corners);
29
+ position: relative;
30
+ display: flex;
31
+ flex-direction: row;
32
+ margin-inline: auto;
33
+ max-width: 100%;
34
+ text-align: start;
35
+ width: var(--column-span-4);
36
+ transform: translate(0, 0); // hack to ensure pseudo element is stacked above parent background
37
+ }
38
+
39
+ .card.has-link::before {
40
+ border-radius: var(--rounded-corners);
41
+ bottom: calc(-1 * var(--thick));
42
+ content: "";
43
+ height: calc(100% + 2 * var(--thick)));
44
+ left: calc(-1 * var(--thick));
45
+ position: absolute;
46
+ right: calc(-1 * var(--thick));
47
+ top: calc(-1 * var(--thick));
48
+ width: calc(100% + 2 * var(--thick)));
49
+ z-index: -1;
50
+ transition:
51
+ background-color 0.5s ease-in-out,
52
+ box-shadow 0.5s ease-in-out;
53
+ }
54
+
55
+ .card.has-link:hover::before,
56
+ .card.has-link:focus-visible::before {
57
+ background-color: var(--white);
58
+ box-shadow: var(--shadow-medium);
59
+ }
60
+
61
+ .card.has-link .name a {
62
+ box-shadow: 0;
63
+ color: var(--purple-80);
64
+ transition: box-shadow 0.5s ease-in-out;
65
+ }
66
+
67
+ .card.has-link:hover .name a {
68
+ box-shadow: inset 0 calc(-1 * var(--thick)) 0 0 var(--purple-20);
69
+ }
70
+
71
+ a {
72
+ position: relative;
73
+ text-decoration: none;
74
+ z-index: 1;
75
+ }
76
+
77
+ a.link-overlay {
78
+ position: static;
79
+ }
80
+
81
+ a.link-overlay::before {
82
+ content: "";
83
+ position: absolute;
84
+ z-index: 0;
85
+ top: 0;
86
+ right: 0;
87
+ bottom: 0;
88
+ left: 0;
89
+ }
90
+
91
+ .avatar {
92
+ flex-grow: 0;
93
+ }
94
+
95
+ .name {
96
+ display: inline-block;
97
+ }
98
+
99
+ .pronouns {
100
+ color: var(--purple-60);
101
+ }
102
+
103
+ .details {
104
+ color: var(--black);
105
+ flex-grow: 1;
106
+ padding-block-start: var(--spacing-component-2);
107
+ padding-inline-start: var(--spacing-component-2);
108
+ }
109
+
110
+ .social {
111
+ font-size: 0.9rem;
112
+ margin-block-start: var(--spacing-component-1);
113
+ }
114
+
115
+ .social a {
116
+ align-items: center;
117
+ border-radius: 10000px;
118
+ color: var(--purple-80);
119
+ display: inline-flex;
120
+ height: var(--spacing-component-4);
121
+ justify-content: center;
122
+ margin: calc(-1 * var(--spacing-component-2));
123
+ width: var(--spacing-component-4);
124
+ }
125
+
126
+ .social a:hover {
127
+ background-color: var(--purple-20);
128
+ }
129
+
130
+ .social > a + a {
131
+ margin-inline-start: var(--spacing-component-2);
132
+ }
133
+ `,
134
+ ];
135
+ render() {
136
+ return html`
137
+ <div class="card ${this.linkUrl ? "has-link" : ""}">
138
+ <!-- Link overlay pseudo-element so we can do nested links within the card -->
139
+ ${this.linkUrl
140
+ ? html`
141
+ <a
142
+ href="${this.linkUrl}"
143
+ target="${this.linkTarget || "_self"}"
144
+ rel="${this.linkTarget == "_blank" ? "noopener" : nothing}"
145
+ class="link-overlay"
146
+ tabindex="-1"
147
+ >
148
+ </a>
149
+ `
150
+ : ""}
151
+ <div class="avatar">
152
+ ${this.linkUrl
153
+ ? html`
154
+ <a
155
+ href="${this.linkUrl}"
156
+ target="${this.linkTarget || "_self"}"
157
+ rel="${this.linkTarget == "_blank" ? "noopener" : nothing}"
158
+ >
159
+ <cfa-avatar
160
+ imageUrl="${this.imageUrl}"
161
+ altText="Avatar photo of ${this.name}"
162
+ size="3"
163
+ >
164
+ </cfa-avatar>
165
+ </a>
166
+ `
167
+ : html`
168
+ <cfa-avatar
169
+ imageUrl="${this.imageUrl}"
170
+ altText="Avatar photo of ${this.name}"
171
+ size="3"
172
+ >
173
+ </cfa-avatar>
174
+ `}
175
+ </div>
176
+ <div class="details">
177
+ <div>
178
+ <div class="name h4">
179
+ ${this.linkUrl
180
+ ? html`
181
+ <a
182
+ href="${this.linkUrl}"
183
+ target="${this.linkTarget || "_self"}"
184
+ rel="${this.linkTarget == "_blank"
185
+ ? "noopener"
186
+ : nothing}"
187
+ >
188
+ ${this.name.trim()}
189
+ </a>
190
+ `
191
+ : html` ${this.name.trim()} `}
192
+ </div>
193
+ ${this.pronouns
194
+ ? html` <span class="pronouns small">(${this.pronouns})</span> `
195
+ : ""}
196
+ </div>
197
+ <div class="title small">
198
+ ${[this.title?.trim(), this.company?.trim()]
199
+ .filter(Boolean)
200
+ .join(", ")}
201
+ </div>
202
+ ${this.linkedInUrl || this.twitterHandle
203
+ ? html`
204
+ <div class="social">
205
+ ${this.linkedInUrl
206
+ ? html`
207
+ <a
208
+ href="${this.linkedInUrl}"
209
+ target="_blank"
210
+ rel="noopener"
211
+ title="View ${this.name}'s profile on LinkedIn"
212
+ >
213
+ <cfa-social-icon icon="linkedin" />
214
+ </a>
215
+ `
216
+ : nothing}
217
+ ${this.twitterHandle
218
+ ? html`
219
+ <a
220
+ href="https://twitter.com/${this.twitterHandle}"
221
+ target="_blank"
222
+ rel="noopener"
223
+ title="View ${this.name}'s profile on X (Twitter)"
224
+ >
225
+ <cfa-social-icon icon="twitter" />
226
+ </a>
227
+ `
228
+ : nothing}
229
+ </div>
230
+ `
231
+ : nothing}
232
+ </div>
233
+ </div>
234
+ `;
235
+ }
236
+ }
237
+
238
+ if (!customElements.get("cfa-person-card")) {
239
+ customElements.define("cfa-person-card", PersonCard);
240
+ }
@@ -0,0 +1,58 @@
1
+ import { html } from "lit-html";
2
+ import "./person-card";
3
+
4
+ export default {
5
+ title: "Molecules/PersonCard",
6
+ argTypes: {
7
+ name: { type: "string" },
8
+ title: { type: "string" },
9
+ company: { type: "string" },
10
+ pronouns: { type: "string" },
11
+ imageUrl: { type: "string" },
12
+ imageAltText: { type: "string" },
13
+ linkUrl: { type: "string" },
14
+ linkTarget: { type: "string" },
15
+ linkedInUrl: { type: "string" },
16
+ twitterHandle: { type: "string" },
17
+ },
18
+ };
19
+
20
+ const Template = ({
21
+ name,
22
+ title,
23
+ company,
24
+ pronouns,
25
+ imageUrl,
26
+ imageAltText,
27
+ linkUrl,
28
+ linkedInUrl,
29
+ twitterHandle,
30
+ }) => html`
31
+ <cfa-person-card
32
+ name="${name}"
33
+ title="${title}"
34
+ company="${company}"
35
+ pronouns="${pronouns}"
36
+ imageUrl="${imageUrl}"
37
+ imageAltText="${imageAltText}"
38
+ linkUrl="${linkUrl}"
39
+ linkedInUrl="${linkedInUrl}"
40
+ twitterHandle="${twitterHandle}"
41
+ >
42
+ </cfa-person-card>
43
+ `;
44
+
45
+ export const Default = Template.bind({});
46
+ Default.args = {
47
+ name: "Amanda Renteria",
48
+ title: "Chief Executive Officer",
49
+ company: "Code for America",
50
+ pronouns: "she/her",
51
+ imageUrl:
52
+ "https://files.codeforamerica.org/2021/02/13130320/Renteria.Turq_.Standing-scaled-e1678737908242-198x300.jpg",
53
+ imageAltText: "Headshot photo of Amanda Renteria, Code for America CEO",
54
+ linkUrl: "https://www.codeforamerica.org/about/people/amanda-renteria",
55
+ linkTarget: "_blank",
56
+ linkedInUrl: "https://www.linkedin.com/in/amanda-renteria",
57
+ twitterHandle: "amandarenteria",
58
+ };
@@ -0,0 +1,33 @@
1
+ import { LitElement, html, css } from "lit";
2
+ import { commonStyles } from "../shared/common";
3
+
4
+ class Pill extends LitElement {
5
+ static styles = [
6
+ commonStyles,
7
+ css`
8
+ :host {
9
+ --bg-color: var(--gray-20);
10
+ --text-color: var(--gray-80);
11
+
12
+ background-color: var(--bg-color);
13
+ border-radius: var(--spacing-component-3);
14
+ color: var(--text-color);
15
+ display: inline-block;
16
+ font-size: var(--font-size-small);
17
+ font-weight: bold;
18
+ line-height: 1;
19
+ margin-right: var(--spacing-component-1);
20
+ padding: var(--spacing-component-1) var(--spacing-component-2);
21
+ vertical-align: middle;
22
+ }
23
+ `,
24
+ ];
25
+
26
+ render() {
27
+ return html` <slot /> `;
28
+ }
29
+ }
30
+
31
+ if (!customElements.get("cfa-pill")) {
32
+ customElements.define("cfa-pill", Pill);
33
+ }
@@ -0,0 +1,23 @@
1
+ import { html } from "lit-html";
2
+ import "./pill";
3
+
4
+ export default {
5
+ title: "Atoms/Pill",
6
+ argTypes: {
7
+ text: { type: "string" },
8
+ },
9
+ parameters: {
10
+ backgrounds: { default: "white" },
11
+ },
12
+ };
13
+
14
+ const Template = ({ text }) => html`
15
+ <cfa-pill> ${text} </cfa-pill>
16
+ <cfa-pill> ${text} </cfa-pill>
17
+ <cfa-pill> ${text} </cfa-pill>
18
+ `;
19
+
20
+ export const Default = Template.bind({});
21
+ Default.args = {
22
+ text: "Qualitative Research",
23
+ };