@cfpb/cfpb-design-system 4.2.4 → 4.3.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 (124) hide show
  1. package/CHANGELOG.md +166 -1
  2. package/dist/components/cfpb-expandables/index.js +1 -1
  3. package/dist/components/cfpb-expandables/index.js.map +3 -3
  4. package/dist/components/cfpb-forms/index.js +1 -1
  5. package/dist/components/cfpb-forms/index.js.map +2 -2
  6. package/dist/elements/cfpb-button/index.js +4 -4
  7. package/dist/elements/cfpb-button/index.js.map +3 -3
  8. package/dist/elements/cfpb-checkbox-icon/index.js +29 -0
  9. package/dist/elements/{cfpb-checkbox → cfpb-checkbox-icon}/index.js.map +4 -4
  10. package/dist/elements/cfpb-expandable/index.css +2 -0
  11. package/dist/elements/cfpb-expandable/index.css.map +7 -0
  12. package/dist/elements/cfpb-expandable/index.js +33 -0
  13. package/dist/elements/cfpb-expandable/index.js.map +7 -0
  14. package/dist/elements/cfpb-file-upload/index.js +4 -4
  15. package/dist/elements/cfpb-file-upload/index.js.map +3 -3
  16. package/dist/elements/cfpb-form-alert/index.js +32 -0
  17. package/dist/elements/cfpb-form-alert/index.js.map +7 -0
  18. package/dist/elements/cfpb-form-choice/index.js +12 -3
  19. package/dist/elements/cfpb-form-choice/index.js.map +4 -4
  20. package/dist/elements/cfpb-form-search/index.js +41 -0
  21. package/dist/elements/cfpb-form-search/index.js.map +7 -0
  22. package/dist/elements/cfpb-form-search-input/index.js +41 -0
  23. package/dist/elements/cfpb-form-search-input/index.js.map +7 -0
  24. package/dist/elements/cfpb-icon-text/index.js +3 -3
  25. package/dist/elements/cfpb-icon-text/index.js.map +3 -3
  26. package/dist/elements/cfpb-label/index.js +3 -3
  27. package/dist/elements/cfpb-label/index.js.map +2 -2
  28. package/dist/elements/cfpb-list/index.js +39 -0
  29. package/dist/elements/cfpb-list/index.js.map +7 -0
  30. package/dist/elements/cfpb-list-item/index.js +39 -0
  31. package/dist/elements/cfpb-list-item/index.js.map +7 -0
  32. package/dist/elements/cfpb-multiselect/index.js +13 -4
  33. package/dist/elements/cfpb-multiselect/index.js.map +4 -4
  34. package/dist/elements/cfpb-pagination/index.js +3 -3
  35. package/dist/elements/cfpb-pagination/index.js.map +2 -2
  36. package/dist/elements/cfpb-select/index.css +2 -0
  37. package/dist/elements/cfpb-select/index.css.map +7 -0
  38. package/dist/elements/cfpb-select/index.js +42 -0
  39. package/dist/elements/cfpb-select/index.js.map +7 -0
  40. package/dist/elements/cfpb-select-list/index.js +39 -0
  41. package/dist/elements/cfpb-select-list/index.js.map +7 -0
  42. package/dist/elements/cfpb-tag-filter/index.js +3 -3
  43. package/dist/elements/cfpb-tag-filter/index.js.map +3 -3
  44. package/dist/elements/cfpb-tag-group/index.js +3 -3
  45. package/dist/elements/cfpb-tag-group/index.js.map +4 -4
  46. package/dist/elements/cfpb-tag-topic/index.js +4 -4
  47. package/dist/elements/cfpb-tag-topic/index.js.map +1 -1
  48. package/dist/elements/index.css +2 -0
  49. package/dist/elements/index.css.map +7 -0
  50. package/dist/elements/index.js +7 -6
  51. package/dist/elements/index.js.map +4 -4
  52. package/dist/index.js +7 -6
  53. package/dist/index.js.map +4 -4
  54. package/dist/utilities/index.js +1 -1
  55. package/dist/utilities/index.js.map +3 -3
  56. package/package.json +1 -1
  57. package/src/components/cfpb-expandables/expandable.js +3 -0
  58. package/src/components/cfpb-forms/multiselect.js +1 -1
  59. package/src/elements/abstracts/custom-props.css +123 -0
  60. package/src/elements/abstracts/grid-mixins.scss +83 -0
  61. package/src/elements/abstracts/heading-mixins.scss +346 -0
  62. package/src/elements/abstracts/index.scss +7 -0
  63. package/src/elements/abstracts/media-queries.scss +35 -0
  64. package/src/elements/abstracts/sizing-vars.scss +65 -0
  65. package/src/elements/abstracts/vars-breakpoints.scss +16 -0
  66. package/src/elements/abstracts/vars.css +79 -0
  67. package/src/elements/base/base.scss +375 -0
  68. package/src/elements/base/font.scss +27 -0
  69. package/src/elements/base/index.scss +3 -0
  70. package/src/elements/base/normalize.scss +290 -0
  71. package/src/elements/cfpb-button/cfpb-button-group.scss +10 -0
  72. package/src/elements/cfpb-button/cfpb-button-link.scss +96 -0
  73. package/src/elements/cfpb-button/cfpb-button.component.scss +11 -4
  74. package/src/elements/cfpb-button/cfpb-button.scss +222 -0
  75. package/src/elements/cfpb-button/index.js +28 -29
  76. package/src/elements/cfpb-button/vars.css +30 -0
  77. package/src/elements/cfpb-checkbox-icon/cfpb-checkbox-icon.component.scss +88 -0
  78. package/src/elements/cfpb-checkbox-icon/index.js +104 -0
  79. package/src/elements/cfpb-expandable/cfpb-expandable.component.scss +218 -0
  80. package/src/elements/cfpb-expandable/index.js +127 -0
  81. package/src/elements/cfpb-file-upload/cfpb-file-upload.component.scss +2 -2
  82. package/src/elements/cfpb-file-upload/index.js +16 -18
  83. package/src/elements/cfpb-form-alert/cfpb-form-alert.component.scss +36 -0
  84. package/src/elements/cfpb-form-alert/index.js +55 -0
  85. package/src/elements/cfpb-form-choice/cfpb-form-choice.component.scss +42 -81
  86. package/src/elements/cfpb-form-choice/index.js +58 -18
  87. package/src/elements/cfpb-form-search/cfpb-form-search.component.scss +54 -0
  88. package/src/elements/cfpb-form-search/index.js +194 -0
  89. package/src/elements/cfpb-form-search-input/cfpb-form-search-input.component.scss +217 -0
  90. package/src/elements/cfpb-form-search-input/index.js +136 -0
  91. package/src/elements/cfpb-icon-text/cfpb-icon-text.component.scss +32 -39
  92. package/src/elements/cfpb-icon-text/index.js +32 -104
  93. package/src/elements/cfpb-label/cfpb-label.component.scss +2 -2
  94. package/src/elements/cfpb-label/index.js +6 -9
  95. package/src/elements/cfpb-list/cfpb-list.component.scss +23 -0
  96. package/src/elements/cfpb-list/index.js +357 -0
  97. package/src/elements/cfpb-list/index.spec.js +169 -0
  98. package/src/elements/cfpb-list-item/cfpb-list-item.component.scss +69 -0
  99. package/src/elements/cfpb-list-item/index.js +215 -0
  100. package/src/elements/cfpb-pagination/cfpb-pagination.component.scss +2 -7
  101. package/src/elements/cfpb-pagination/index.js +6 -8
  102. package/src/elements/cfpb-select/cfpb-select.component.scss +241 -0
  103. package/src/elements/cfpb-select/index.js +381 -0
  104. package/src/elements/cfpb-tag-filter/cfpb-tag-filter.component.scss +6 -3
  105. package/src/elements/cfpb-tag-filter/index.js +15 -7
  106. package/src/elements/cfpb-tag-group/cfpb-tag-group.component.scss +2 -2
  107. package/src/elements/cfpb-tag-group/index.js +53 -6
  108. package/src/elements/cfpb-tag-topic/index.js +5 -7
  109. package/src/elements/cfpb-utilities/parse-child-data.js +50 -0
  110. package/src/elements/cfpb-utilities/parse-child-data.spec.js +56 -0
  111. package/src/elements/cfpb-utilities/search-service.js +46 -0
  112. package/src/elements/cfpb-utilities/search-service.spec.js +138 -0
  113. package/src/elements/cfpb-utilities/transition/transition.scss +98 -0
  114. package/src/elements/index.js +7 -1
  115. package/src/index.scss +11 -0
  116. package/src/tokens/abstracts/custom-props.json +1642 -0
  117. package/src/tokens/abstracts/vars.json +1319 -0
  118. package/src/tokens/cfpb-button/vars.json +436 -0
  119. package/src/utilities/transition/max-height-transition.js +74 -0
  120. package/dist/elements/cfpb-checkbox/index.js +0 -29
  121. package/src/elements/cfpb-multiselect/cfpb-multiselect.component.scss +0 -225
  122. package/src/elements/cfpb-multiselect/index.js +0 -444
  123. package/src/elements/cfpb-multiselect/multiselect-model.js +0 -288
  124. package/src/elements/cfpb-multiselect/multiselect-model.spec.js +0 -236
@@ -0,0 +1,136 @@
1
+ import { html, LitElement, css, unsafeCSS } from 'lit';
2
+ import { ref, createRef } from 'lit/directives/ref.js';
3
+ import styles from './cfpb-form-search-input.component.scss';
4
+ import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
5
+
6
+ import searchIcon from '../../components/cfpb-icons/icons/search.svg';
7
+ import clearIcon from '../../components/cfpb-icons/icons/error.svg';
8
+
9
+ /**
10
+ * @element cfpb-form-search-input
11
+ */
12
+ export class CfpbFormSearchInput extends LitElement {
13
+ static styles = css`
14
+ ${unsafeCSS(styles)}
15
+ `;
16
+
17
+ /**
18
+ * @property {boolean} disabled - Whether the input is disabled or not.
19
+ * @property {string} validation - Validation style: error, warning, success.
20
+ * @property {string} label - The aria-label for the input.
21
+ * @property {string} name - The name within a form.
22
+ * @property {string} value - The value within the input.
23
+ * @property {number} maxlength - The maximum characters allowed in the input.
24
+ * @property {string} placeholder - The placeholder value.
25
+ * @property {string} ariaLabelInput - aria-label for input.
26
+ * @property {string} ariaLabelButton - aria-label for button.
27
+ * @property {boolean} borderless - Whether the input has a border or not.
28
+ * @returns {object} The map of properties.
29
+ */
30
+ static properties = {
31
+ disabled: { type: Boolean, reflect: true },
32
+ validation: { type: String, reflect: true },
33
+ label: { type: String },
34
+ name: { type: String },
35
+ title: { type: String, attribute: true },
36
+ value: { type: String },
37
+ maxlength: { type: Number, reflect: true },
38
+ placeholder: { type: String },
39
+ ariaLabelInput: { type: String, attribute: 'aria-label-input' },
40
+ ariaLabelButton: { type: String, attribute: 'aria-label-button' },
41
+ borderless: { type: Boolean, reflect: true },
42
+ };
43
+
44
+ #searchInput;
45
+
46
+ constructor() {
47
+ super();
48
+
49
+ this.label = 'Search';
50
+ this.name = '';
51
+ this.title = 'Search';
52
+ this.value = '';
53
+ this.maxlength = 75;
54
+ this.placeholder = '';
55
+ this.ariaLabelInput = 'Search input';
56
+ this.ariaLabelButton = 'Clear search';
57
+ this.disabled = false;
58
+ this.#searchInput = createRef();
59
+
60
+ // Borderless is only used for CSS.
61
+ this.borderless = false;
62
+ }
63
+
64
+ #onInput(evt) {
65
+ this.value = evt.target.value;
66
+ }
67
+
68
+ #onBlur() {
69
+ this.dispatchEvent(
70
+ new Event('blur', {
71
+ bubbles: true,
72
+ composed: true,
73
+ }),
74
+ );
75
+ }
76
+
77
+ #onClickClear(evt) {
78
+ evt.preventDefault();
79
+ if (this.disabled) return;
80
+ this.value = '';
81
+ this.#searchInput.value?.focus();
82
+ this.dispatchEvent(
83
+ new CustomEvent('clear', { detail: '', bubbles: true, composed: true }),
84
+ );
85
+ }
86
+
87
+ render() {
88
+ return html`
89
+ <div
90
+ class="o-search-input ${this.validation
91
+ ? `o-search-input--${this.validation}`
92
+ : ''}"
93
+ >
94
+ <label
95
+ for="search-text"
96
+ class="o-search-input__input-label"
97
+ aria-label=${this.label}
98
+ >
99
+ ${unsafeSVG(searchIcon)}
100
+ </label>
101
+ <input
102
+ id="search-text"
103
+ type="search"
104
+ name=${this.name}
105
+ .value=${this.value}
106
+ ?disabled=${this.disabled}
107
+ class="a-text-input a-text-input__full"
108
+ placeholder=${this.placeholder}
109
+ title=${this.title}
110
+ autocomplete="off"
111
+ maxlength=${this.maxlength}
112
+ aria-label=${this.ariaLabelInput}
113
+ ${ref(this.#searchInput)}
114
+ @input=${this.#onInput}
115
+ @blur=${this.#onBlur}
116
+ />
117
+ <button
118
+ type="reset"
119
+ aria-label=${this.ariaLabelButton}
120
+ title=${this.ariaLabelButton}
121
+ @click=${this.#onClickClear}
122
+ >
123
+ ${unsafeSVG(clearIcon)}
124
+ </button>
125
+ </div>
126
+ `;
127
+ }
128
+
129
+ static init() {
130
+ window.customElements.get('cfpb-form-search-input') ||
131
+ window.customElements.define(
132
+ 'cfpb-form-search-input',
133
+ CfpbFormSearchInput,
134
+ );
135
+ }
136
+ }
@@ -1,60 +1,53 @@
1
1
  @use 'sass:math';
2
- @use '@cfpb/cfpb-design-system/src/abstracts' as *;
2
+ @use '@cfpb/cfpb-design-system/src/elements/abstracts' as *;
3
3
 
4
4
  @mixin u-btn-divider() {
5
5
  content: '';
6
6
  border-left: 1px solid var(--icon-text-divider);
7
- order: 2;
8
7
  place-self: normal;
9
8
  }
10
9
 
11
10
  :host {
12
11
  // Theme variables.
13
- --icon-text-divider: var(--pacific-60);
12
+ --icon-text-divider-default: var(--pacific-60);
13
+ --icon-text-divider-disabled: var(--gray-60);
14
14
 
15
- div {
15
+ // Private variables.
16
+ --icon-text-divider: var(--icon-text-divider-default);
17
+
18
+ .wrapper {
16
19
  // This prevents the child button from having an empty gap after the button.
17
20
  display: flex;
18
21
  width: fit-content;
19
22
  align-items: center;
20
23
 
21
- // Hide SVG by default.
22
- & ::slotted(svg) {
23
- display: none;
24
- }
25
-
26
- &.u-has-icon {
27
- gap: math.div(10px, $btn-font-size) + rem;
28
- & slot::before {
29
- @include u-btn-divider;
30
- }
31
-
32
- // Show SVG.
33
- & ::slotted(svg) {
34
- display: initial;
35
- }
36
- }
37
-
38
- &.u-has-icon--left {
39
- & ::slotted(svg) {
40
- order: 1;
41
- }
42
- & ::slotted(span) {
43
- order: 3;
44
- }
45
- }
46
-
47
- &.u-has-icon--right {
48
- & ::slotted(svg) {
49
- order: 3;
50
- }
51
- & ::slotted(span) {
52
- order: 1;
53
- }
54
- }
24
+ gap: math.div(10px, $btn-font-size) + rem;
25
+ }
26
+
27
+ .left-divider::before,
28
+ .right-divider::after {
29
+ @include u-btn-divider;
30
+ }
31
+
32
+ // Shine the slot contents through to the wrapper's flexbox items.
33
+ slot {
34
+ display: contents;
55
35
  }
56
36
  }
57
37
 
38
+ .left-divider ::slotted(svg:first-of-type) {
39
+ order: -1;
40
+ }
41
+
42
+ .right-divider ::slotted(svg:last-of-type) {
43
+ order: 1;
44
+ }
45
+
58
46
  :host([disabled]) {
59
- --icon-text-divider: var(--gray-60);
47
+ --icon-text-divider: var(--icon-text-divider-disabled);
48
+ }
49
+
50
+ // Hide SVG if user calls hideIcon()
51
+ :host([icon-hidden]) ::slotted(svg) {
52
+ display: none !important;
60
53
  }
@@ -2,7 +2,6 @@ import { html, LitElement, css, unsafeCSS } from 'lit';
2
2
  import styles from './cfpb-icon-text.component.scss';
3
3
 
4
4
  /**
5
- *
6
5
  * @element cfpb-icon-text
7
6
  * @slot - The main content for the text and icon.
8
7
  */
@@ -15,132 +14,61 @@ export class CfpbIconText extends LitElement {
15
14
  * @property {boolean} disabled - Apply disabled styles or not.
16
15
  * @returns {object} The map of properties.
17
16
  */
18
- static get properties() {
19
- return {
20
- disabled: { type: Boolean, reflect: true },
21
- };
22
- }
23
-
24
- // DOM references.
25
- #svgObserver;
26
- #iconClasses;
17
+ static properties = {
18
+ disabled: { type: Boolean, reflect: true },
19
+ iconHidden: { type: Boolean, reflect: true, attribute: 'icon-hidden' },
20
+ };
27
21
 
28
22
  constructor() {
29
23
  super();
30
- this.#iconClasses = '';
24
+ this.disabled = false;
25
+ this.iconHidden = false;
31
26
  }
32
27
 
33
- connectedCallback() {
34
- super.connectedCallback();
35
-
36
- this.#svgObserver = new MutationObserver(() => {
37
- this.#processLightDom();
38
- });
28
+ firstUpdated() {
29
+ const slot = this.shadowRoot.querySelector('slot');
30
+ this.#updateDividers();
39
31
 
40
- this.#svgObserver.observe(this, {
41
- childList: true,
42
- subtree: false,
43
- });
32
+ // Handle dynamically added/removed nodes.
33
+ slot.addEventListener('slotchange', () => this.#updateDividers());
44
34
  }
45
35
 
46
- disconnectedCallback() {
47
- super.disconnectedCallback();
48
- if (this.#svgObserver) {
49
- this.#svgObserver.disconnect();
50
- this.#svgObserver = null;
36
+ updated(changedProps) {
37
+ if (changedProps.has('iconHidden')) {
38
+ this.#updateDividers();
51
39
  }
52
40
  }
53
41
 
54
- #processLightDom() {
55
- const div = this.shadowRoot.querySelector('div');
42
+ #updateDividers() {
43
+ const wrapper = this.shadowRoot.querySelector('.wrapper');
56
44
  const slot = this.shadowRoot.querySelector('slot');
57
- const nodes = slot.assignedNodes({ flatten: true });
58
-
59
- let svgEl = null;
60
- let spanEl = null;
45
+ const nodes = slot.assignedNodes({ flatten: true }).filter((node) => {
46
+ return (
47
+ node.nodeType === Node.ELEMENT_NODE ||
48
+ (node.nodeType === Node.TEXT_NODE && node.textContent.trim())
49
+ );
50
+ });
61
51
 
62
- for (const node of nodes) {
63
- if (
64
- node.nodeType === Node.TEXT_NODE &&
65
- node.textContent.trim().length > 0
66
- ) {
67
- const span = document.createElement('span');
68
- span.textContent = node.textContent;
69
- node.replaceWith(span);
70
- if (!spanEl) spanEl = span;
71
- } else if (node.nodeType === Node.ELEMENT_NODE) {
72
- const tag = node.tagName.toLowerCase();
73
- if (tag === 'svg' && !svgEl) {
74
- svgEl = node;
75
- } else if (tag === 'span' && !spanEl) {
76
- spanEl = node;
77
- }
78
- }
79
- }
52
+ const showLeft =
53
+ !this.iconHidden && nodes[0]?.tagName?.toLowerCase() === 'svg';
54
+ const showRight =
55
+ !this.iconHidden &&
56
+ nodes[nodes.length - 1]?.tagName?.toLowerCase() === 'svg';
80
57
 
81
- if (svgEl && spanEl) {
82
- div.classList.add('u-has-icon');
83
- if (
84
- svgEl.compareDocumentPosition(spanEl) & Node.DOCUMENT_POSITION_FOLLOWING
85
- ) {
86
- div.classList.add('u-has-icon--left');
87
- } else {
88
- div.classList.add('u-has-icon--right');
89
- }
90
- }
58
+ wrapper.classList.toggle('left-divider', showLeft);
59
+ wrapper.classList.toggle('right-divider', showRight);
91
60
  }
92
61
 
93
- firstUpdated() {
94
- this.#processLightDom();
95
- }
96
-
97
- /**
98
- * Hide any icon in the slot.
99
- */
100
62
  hideIcon() {
101
- const icon = this.#findIconInSlot();
102
- const div = this.shadowRoot.querySelector('div');
103
- if (icon) {
104
- this.#iconClasses = div.className;
105
- div.className = '';
106
- }
63
+ this.iconHidden = true;
107
64
  }
108
65
 
109
- /**
110
- * Show any icon in the slot, if it was hidden.
111
- */
112
66
  showIcon() {
113
- const icon = this.#findIconInSlot();
114
- const div = this.shadowRoot.querySelector('div');
115
- if (icon) div.className = this.#iconClasses;
116
- }
117
-
118
- /**
119
- * @returns {boolean} True if it has an icon, false otherwise.
120
- */
121
- hasIcon() {
122
- const icon = this.#findIconInSlot();
123
- if (icon) return true;
124
- return false;
125
- }
126
-
127
- /**
128
- * Find the icon SVG in the slot.
129
- * @returns {Node} The icon SVG node.
130
- */
131
- #findIconInSlot() {
132
- const slot = this.shadowRoot.querySelector('slot');
133
- const nodes = slot.assignedNodes({ flatten: true });
134
-
135
- for (const node of nodes) {
136
- if (node.tagName && node.tagName.toLowerCase() === 'svg') {
137
- return node;
138
- }
139
- }
67
+ this.iconHidden = false;
140
68
  }
141
69
 
142
70
  render() {
143
- return html`<div><slot></slot></div>`;
71
+ return html`<span class="wrapper"><slot></slot></span>`;
144
72
  }
145
73
 
146
74
  static init() {
@@ -1,5 +1,5 @@
1
1
  @use 'sass:math';
2
- @use '@cfpb/cfpb-design-system/src/abstracts' as *;
2
+ @use '@cfpb/cfpb-design-system/src/elements/abstracts' as *;
3
3
 
4
4
  :host {
5
5
  .a-label {
@@ -8,7 +8,7 @@
8
8
  }
9
9
 
10
10
  &__helper {
11
- color: $label-helper;
11
+ color: var(--label-helper);
12
12
  font-size: math.div(16px, $base-font-size-px) + rem;
13
13
  font-weight: normal;
14
14
 
@@ -14,17 +14,14 @@ export class CfpbLabel extends LitElement {
14
14
  `;
15
15
 
16
16
  /**
17
- * @property {string} for - Associate the label with an ID elsewhere.
18
17
  * @property {boolean} block - Whether this has block or inline helper text.
18
+ * @property {string} for - Associate the label with an ID elsewhere.
19
19
  * @returns {object} The map of properties.
20
20
  */
21
- static get properties() {
22
- return {
23
- // Other properties.
24
- block: { type: Boolean, reflect: true },
25
- for: { type: String },
26
- };
27
- }
21
+ static properties = {
22
+ block: { type: Boolean, reflect: true },
23
+ for: { type: String },
24
+ };
28
25
 
29
26
  constructor() {
30
27
  super();
@@ -48,7 +45,7 @@ export class CfpbLabel extends LitElement {
48
45
  for=${ifDefined(this.for && this.for.trim() ? this.for : undefined)}
49
46
  >
50
47
  <slot name="label"></slot>
51
- <small class="${this.#helperClass}">
48
+ <small class=${this.#helperClass}>
52
49
  <slot name="helper"></slot>
53
50
  </small>
54
51
  </label>
@@ -0,0 +1,23 @@
1
+ @use 'sass:math';
2
+ @use '@cfpb/cfpb-design-system/src/elements/abstracts' as *;
3
+ @use '@cfpb/cfpb-design-system/src/components/cfpb-icons/icon';
4
+
5
+ :host {
6
+ ::slotted(cfpb-list-item) {
7
+ border-bottom: 1px solid var(--gray-20);
8
+
9
+ // Overlap with the border of prior item.
10
+ margin-top: -1px;
11
+ }
12
+
13
+ ::slotted(cfpb-list-item:first-of-type) {
14
+ border-top: 1px solid var(--gray-20);
15
+
16
+ // Don't shift the first item.
17
+ margin-top: 0;
18
+ }
19
+
20
+ :focus {
21
+ outline: 1px dotted var(--pacific);
22
+ }
23
+ }