@kwtv/components 0.2.0 → 0.4.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 (56) hide show
  1. package/README.md +52 -9
  2. package/package.json +2 -2
  3. package/src/astro/Accordion.astro +10 -6
  4. package/src/astro/AccordionItem.astro +55 -45
  5. package/src/astro/ActiveFilterTags.astro +41 -28
  6. package/src/astro/Alert.astro +45 -41
  7. package/src/astro/Badge.astro +80 -76
  8. package/src/astro/Band.astro +104 -58
  9. package/src/astro/Breadcrumb.astro +38 -30
  10. package/src/astro/Button.astro +135 -129
  11. package/src/astro/Card.astro +72 -62
  12. package/src/astro/CartLine.astro +72 -58
  13. package/src/astro/Checkbox.astro +36 -32
  14. package/src/astro/Chip.astro +68 -64
  15. package/src/astro/ChipRail.astro +6 -1
  16. package/src/astro/DotGrid.astro +55 -42
  17. package/src/astro/EmptyState.astro +29 -23
  18. package/src/astro/FilterGroup.astro +29 -26
  19. package/src/astro/Filters.astro +57 -47
  20. package/src/astro/Footer.astro +195 -178
  21. package/src/astro/FooterColumn.astro +39 -35
  22. package/src/astro/Header.astro +80 -67
  23. package/src/astro/Icon.astro +4 -2
  24. package/src/astro/IconButton.astro +67 -61
  25. package/src/astro/Marquee.astro +43 -41
  26. package/src/astro/NavDropdown.astro +169 -149
  27. package/src/astro/NavLink.astro +59 -56
  28. package/src/astro/Newsletter.astro +64 -56
  29. package/src/astro/Pager.astro +71 -46
  30. package/src/astro/PickCard.astro +72 -68
  31. package/src/astro/ProductCard.astro +134 -119
  32. package/src/astro/Radio.astro +36 -32
  33. package/src/astro/Skeleton.astro +60 -42
  34. package/src/astro/Social.astro +26 -21
  35. package/src/astro/StepIndicator.astro +70 -54
  36. package/src/astro/Sticker.astro +74 -49
  37. package/src/astro/Swatch.astro +44 -40
  38. package/src/astro/SwipeCarousel.astro +5 -1
  39. package/src/astro/Table.astro +66 -56
  40. package/src/astro/TableCard.astro +45 -37
  41. package/src/astro/TextField.astro +104 -91
  42. package/src/astro/Totals.astro +39 -35
  43. package/src/react/Drawer.css +60 -56
  44. package/src/react/Drawer.tsx +1 -5
  45. package/src/react/Modal.css +97 -93
  46. package/src/react/Modal.tsx +4 -6
  47. package/src/react/NewsCarousel.css +108 -104
  48. package/src/react/NewsCarousel.tsx +7 -1
  49. package/src/react/QuantityStepper.css +84 -80
  50. package/src/react/Select.css +159 -155
  51. package/src/react/Select.tsx +8 -2
  52. package/src/react/Toast.css +128 -124
  53. package/src/react/Toast.tsx +38 -4
  54. package/src/styles/base.css +30 -26
  55. package/src/styles/controls.css +183 -179
  56. package/src/styles/motion.css +203 -199
@@ -27,9 +27,15 @@ const classes = ["ui-navgroup", open && "is-open", className]
27
27
  ---
28
28
 
29
29
  <div class={classes} data-navdropdown {...rest}>
30
- <button class="ui-navlink" type="button" aria-expanded={open ? "true" : "false"}>
30
+ <button
31
+ class="ui-navlink"
32
+ type="button"
33
+ aria-expanded={open ? "true" : "false"}
34
+ >
31
35
  {label}
32
- <span aria-hidden="true"><Icon name="chevron-down" size={11} stroke={2.4} /></span>
36
+ <span aria-hidden="true">
37
+ <Icon name="chevron-down" size={11} stroke={2.4} />
38
+ </span>
33
39
  </button>
34
40
  <div class="ui-navpanel">
35
41
  <slot />
@@ -40,172 +46,186 @@ const classes = ["ui-navgroup", open && "is-open", className]
40
46
  // Click-to-toggle, on top of the CSS hover/focus-within path: the only way
41
47
  // a touchscreen tap, or a Safari click (which does not focus a <button>),
42
48
  // can open the panel at all.
43
- document.querySelectorAll<HTMLElement>("[data-navdropdown]").forEach((group) => {
44
- const trigger = group.querySelector<HTMLButtonElement>(".ui-navlink");
45
- if (!trigger) return;
46
-
47
- const close = () => {
48
- group.classList.remove("is-open");
49
- trigger.setAttribute("aria-expanded", "false");
50
- };
51
-
52
- trigger.addEventListener("click", (event) => {
53
- const willOpen = !group.classList.contains("is-open");
54
- document.querySelectorAll<HTMLElement>("[data-navdropdown].is-open").forEach((other) => {
55
- if (other === group) return;
56
- other.classList.remove("is-open");
57
- other.querySelector<HTMLButtonElement>(".ui-navlink")?.setAttribute("aria-expanded", "false");
49
+ document
50
+ .querySelectorAll<HTMLElement>("[data-navdropdown]")
51
+ .forEach((group) => {
52
+ const trigger = group.querySelector<HTMLButtonElement>(".ui-navlink");
53
+ if (!trigger) return;
54
+
55
+ const close = () => {
56
+ group.classList.remove("is-open");
57
+ trigger.setAttribute("aria-expanded", "false");
58
+ };
59
+
60
+ trigger.addEventListener("click", (event) => {
61
+ const willOpen = !group.classList.contains("is-open");
62
+ document
63
+ .querySelectorAll<HTMLElement>("[data-navdropdown].is-open")
64
+ .forEach((other) => {
65
+ if (other === group) return;
66
+ other.classList.remove("is-open");
67
+ other
68
+ .querySelector<HTMLButtonElement>(".ui-navlink")
69
+ ?.setAttribute("aria-expanded", "false");
70
+ });
71
+ group.classList.toggle("is-open", willOpen);
72
+ trigger.setAttribute("aria-expanded", String(willOpen));
73
+ event.stopPropagation();
58
74
  });
59
- group.classList.toggle("is-open", willOpen);
60
- trigger.setAttribute("aria-expanded", String(willOpen));
61
- event.stopPropagation();
62
- });
63
75
 
64
- group.addEventListener("keydown", (event) => {
65
- if (event.key === "Escape") {
66
- close();
67
- trigger.focus();
68
- }
76
+ group.addEventListener("keydown", (event) => {
77
+ if (event.key === "Escape") {
78
+ close();
79
+ trigger.focus();
80
+ }
81
+ });
69
82
  });
70
- });
71
83
 
72
84
  document.addEventListener("click", (event) => {
73
- document.querySelectorAll<HTMLElement>("[data-navdropdown].is-open").forEach((group) => {
74
- if (!group.contains(event.target as Node)) {
75
- group.classList.remove("is-open");
76
- group.querySelector<HTMLButtonElement>(".ui-navlink")?.setAttribute("aria-expanded", "false");
77
- }
78
- });
85
+ document
86
+ .querySelectorAll<HTMLElement>("[data-navdropdown].is-open")
87
+ .forEach((group) => {
88
+ if (!group.contains(event.target as Node)) {
89
+ group.classList.remove("is-open");
90
+ group
91
+ .querySelector<HTMLButtonElement>(".ui-navlink")
92
+ ?.setAttribute("aria-expanded", "false");
93
+ }
94
+ });
79
95
  });
80
96
  </script>
81
97
 
82
98
  <style>
83
- .ui-navgroup {
84
- position: relative;
85
- display: inline-block;
86
- }
99
+ @layer reset, tokens, base, components, motion, utilities;
87
100
 
88
- /* The trigger carries .ui-navlink, but that class is styled in NavLink.astro
89
- and Astro scopes styles per component: NavLink's rules only match NavLink's
90
- own markup. Without this block the button falls back to the browser default
91
- chrome, a grey fill and a border. Restated here so the trigger matches the
92
- sibling links it sits next to. */
93
- .ui-navlink {
94
- position: relative;
95
- display: inline-flex;
96
- align-items: center;
97
- gap: var(--space-1-5);
98
- padding: 7px 10px;
99
- margin: -7px -10px;
100
- border-radius: var(--radius-sm);
101
- font-family: var(--font-sans);
102
- font-size: var(--text-sm);
103
- font-weight: var(--weight-medium);
104
- line-height: 1;
105
- color: var(--text);
106
- text-decoration: none;
107
- background: none;
108
- border: 0;
109
- cursor: pointer;
110
- transition: color var(--duration-fast) var(--ease-standard);
111
- }
101
+ @layer components {
102
+ .ui-navgroup {
103
+ position: relative;
104
+ display: inline-block;
105
+ }
112
106
 
113
- .ui-navlink::after {
114
- content: "";
115
- position: absolute;
116
- left: 10px;
117
- right: 10px;
118
- bottom: 2px;
119
- height: 2px;
120
- background: currentColor;
121
- opacity: 0;
122
- transition: opacity var(--duration-fast) var(--ease-standard);
123
- }
107
+ /* The trigger carries .ui-navlink, but that class is styled in NavLink.astro */
108
+ /* and Astro scopes styles per component: NavLink's rules only match NavLink's */
109
+ /* own markup. Without this block the button falls back to the browser default */
110
+ /* chrome, a grey fill and a border. Restated here so the trigger matches the */
111
+ /* sibling links it sits next to. */
112
+ .ui-navlink {
113
+ position: relative;
114
+ display: inline-flex;
115
+ align-items: center;
116
+ gap: var(--space-1-5);
117
+ padding: 7px 10px;
118
+ margin: -7px -10px;
119
+ border-radius: var(--radius-sm);
120
+ font-family: var(--font-sans);
121
+ font-size: var(--text-sm);
122
+ font-weight: var(--weight-medium);
123
+ line-height: 1;
124
+ color: var(--text);
125
+ text-decoration: none;
126
+ background: none;
127
+ border: 0;
128
+ cursor: pointer;
129
+ transition: color var(--duration-fast) var(--ease-standard);
130
+ }
124
131
 
125
- .ui-navlink:hover,
126
- .ui-navlink.is-hover {
127
- color: var(--link-hover);
128
- }
129
- .ui-navlink:hover::after,
130
- .ui-navlink.is-hover::after {
131
- opacity: 1;
132
- }
132
+ .ui-navlink::after {
133
+ content: "";
134
+ position: absolute;
135
+ left: 10px;
136
+ right: 10px;
137
+ bottom: 2px;
138
+ height: 2px;
139
+ background: currentColor;
140
+ opacity: 0;
141
+ transition: opacity var(--duration-fast) var(--ease-standard);
142
+ }
133
143
 
134
- /* While the panel is open the trigger reads as active, the same way the
135
- current page does. */
136
- .ui-navgroup:hover > .ui-navlink::after,
137
- .ui-navgroup:focus-within > .ui-navlink::after,
138
- .ui-navgroup.is-open > .ui-navlink::after {
139
- opacity: 1;
140
- }
144
+ .ui-navlink:hover,
145
+ .ui-navlink.is-hover {
146
+ color: var(--link-hover);
147
+ }
148
+ .ui-navlink:hover::after,
149
+ .ui-navlink.is-hover::after {
150
+ opacity: 1;
151
+ }
141
152
 
142
- .ui-navlink:focus-visible,
143
- .ui-navlink.is-focus {
144
- outline: 2px solid var(--focus);
145
- outline-offset: 2px;
146
- box-shadow: 0 0 0 4px var(--focus-outer);
147
- }
153
+ /* While the panel is open the trigger reads as active, the same way the */
154
+ /* current page does. */
155
+ .ui-navgroup:hover > .ui-navlink::after,
156
+ .ui-navgroup:focus-within > .ui-navlink::after,
157
+ .ui-navgroup.is-open > .ui-navlink::after {
158
+ opacity: 1;
159
+ }
148
160
 
149
- /* The chevron turns over when the panel is open. */
150
- .ui-navlink > span {
151
- display: inline-flex;
152
- line-height: 0;
153
- transition: transform var(--duration-fast) var(--ease-standard);
154
- }
155
- .ui-navgroup:hover > .ui-navlink > span,
156
- .ui-navgroup:focus-within > .ui-navlink > span,
157
- .ui-navgroup.is-open > .ui-navlink > span {
158
- transform: rotate(180deg);
159
- }
161
+ .ui-navlink:focus-visible,
162
+ .ui-navlink.is-focus {
163
+ outline: 2px solid var(--focus);
164
+ outline-offset: 2px;
165
+ box-shadow: 0 0 0 4px var(--focus-outer);
166
+ }
160
167
 
161
- .ui-navpanel {
162
- position: absolute;
163
- top: 100%;
164
- left: -10px;
165
- z-index: var(--z-dropdown);
166
- min-width: 216px;
167
- margin-top: var(--space-2);
168
- padding: var(--space-2);
169
- display: none;
170
- flex-direction: column;
171
- gap: var(--space-px);
172
- background: var(--surface);
173
- border: 1px solid var(--border);
174
- border-radius: var(--radius-lg);
175
- box-shadow: var(--shadow-lg);
176
- }
168
+ /* The chevron turns over when the panel is open. */
169
+ .ui-navlink > span {
170
+ display: inline-flex;
171
+ line-height: 0;
172
+ transition: transform var(--duration-fast) var(--ease-standard);
173
+ }
174
+ .ui-navgroup:hover > .ui-navlink > span,
175
+ .ui-navgroup:focus-within > .ui-navlink > span,
176
+ .ui-navgroup.is-open > .ui-navlink > span {
177
+ transform: rotate(180deg);
178
+ }
177
179
 
178
- /* Opens on hover and when any child holds focus. */
179
- .ui-navgroup:hover .ui-navpanel,
180
- .ui-navgroup:focus-within .ui-navpanel,
181
- .ui-navgroup.is-open .ui-navpanel {
182
- display: flex;
183
- }
180
+ .ui-navpanel {
181
+ position: absolute;
182
+ top: 100%;
183
+ left: -10px;
184
+ z-index: var(--z-dropdown);
185
+ min-width: 216px;
186
+ margin-top: var(--space-2);
187
+ padding: var(--space-2);
188
+ display: none;
189
+ flex-direction: column;
190
+ gap: var(--space-px);
191
+ background: var(--surface);
192
+ border: 1px solid var(--border);
193
+ border-radius: var(--radius-lg);
194
+ box-shadow: var(--shadow-lg);
195
+ }
184
196
 
185
- .ui-navpanel :global(a) {
186
- display: block;
187
- padding: var(--space-2) var(--space-3);
188
- border-radius: var(--radius-sm);
189
- font-size: var(--text-sm);
190
- color: var(--text);
191
- text-decoration: none;
192
- transition: background-color var(--duration-fast) var(--ease-standard);
193
- }
194
- .ui-navpanel :global(a:hover) {
195
- background: var(--surface-hover);
196
- color: var(--link-hover);
197
- }
198
- .ui-navpanel :global(a:focus-visible) {
199
- outline: 2px solid var(--focus);
200
- outline-offset: -2px;
201
- }
197
+ /* Opens on hover and when any child holds focus. */
198
+ .ui-navgroup:hover .ui-navpanel,
199
+ .ui-navgroup:focus-within .ui-navpanel,
200
+ .ui-navgroup.is-open .ui-navpanel {
201
+ display: flex;
202
+ }
202
203
 
203
- @media (prefers-reduced-motion: reduce) {
204
- .ui-navpanel :global(a),
205
- .ui-navlink,
206
- .ui-navlink::after,
207
- .ui-navlink > span {
208
- transition: none;
204
+ .ui-navpanel :global(a) {
205
+ display: block;
206
+ padding: var(--space-2) var(--space-3);
207
+ border-radius: var(--radius-sm);
208
+ font-size: var(--text-sm);
209
+ color: var(--text);
210
+ text-decoration: none;
211
+ transition: background-color var(--duration-fast) var(--ease-standard);
212
+ }
213
+ .ui-navpanel :global(a:hover) {
214
+ background: var(--surface-hover);
215
+ color: var(--link-hover);
216
+ }
217
+ .ui-navpanel :global(a:focus-visible) {
218
+ outline: 2px solid var(--focus);
219
+ outline-offset: -2px;
220
+ }
221
+
222
+ @media (prefers-reduced-motion: reduce) {
223
+ .ui-navpanel :global(a),
224
+ .ui-navlink,
225
+ .ui-navlink::after,
226
+ .ui-navlink > span {
227
+ transition: none;
228
+ }
209
229
  }
210
230
  }
211
231
  </style>
@@ -28,67 +28,70 @@ const classes = ["ui-navlink", current && "is-current", className]
28
28
  </a>
29
29
 
30
30
  <style>
31
- .ui-navlink {
32
- position: relative;
33
- display: inline-flex;
34
- align-items: center;
35
- gap: var(--space-1-5);
36
- /* 7 px block / 10 px inline padding with an equal negative margin, so the
37
- focus ring has room but the header rhythm does not shift. */
38
- padding: 7px 10px;
39
- margin: -7px -10px;
40
- border-radius: var(--radius-sm);
41
- font-family: var(--font-sans);
42
- font-size: var(--text-sm);
43
- font-weight: var(--weight-medium);
44
- line-height: 1;
45
- color: var(--text);
46
- text-decoration: none;
47
- background: none;
48
- border: 0;
49
- cursor: pointer;
50
- transition: color var(--duration-fast) var(--ease-standard);
51
- }
31
+ @layer reset, tokens, base, components, motion, utilities;
52
32
 
53
- .ui-navlink::after {
54
- content: "";
55
- position: absolute;
56
- left: 10px;
57
- right: 10px;
58
- bottom: 2px;
59
- height: 2px;
60
- background: currentColor;
61
- opacity: 0;
62
- transition: opacity var(--duration-fast) var(--ease-standard);
63
- }
33
+ @layer components {
34
+ .ui-navlink {
35
+ position: relative;
36
+ display: inline-flex;
37
+ align-items: center;
38
+ gap: var(--space-1-5);
39
+ /* 7 px block / 10 px inline padding with an equal negative margin, so the focus ring has room but the header rhythm does not shift. */
40
+ padding: 7px 10px;
41
+ margin: -7px -10px;
42
+ border-radius: var(--radius-sm);
43
+ font-family: var(--font-sans);
44
+ font-size: var(--text-sm);
45
+ font-weight: var(--weight-medium);
46
+ line-height: 1;
47
+ color: var(--text);
48
+ text-decoration: none;
49
+ background: none;
50
+ border: 0;
51
+ cursor: pointer;
52
+ transition: color var(--duration-fast) var(--ease-standard);
53
+ }
64
54
 
65
- .ui-navlink:hover,
66
- .ui-navlink.is-hover {
67
- color: var(--link-hover);
68
- }
69
- .ui-navlink:hover::after,
70
- .ui-navlink.is-hover::after {
71
- opacity: 1;
72
- }
55
+ .ui-navlink::after {
56
+ content: "";
57
+ position: absolute;
58
+ left: 10px;
59
+ right: 10px;
60
+ bottom: 2px;
61
+ height: 2px;
62
+ background: currentColor;
63
+ opacity: 0;
64
+ transition: opacity var(--duration-fast) var(--ease-standard);
65
+ }
73
66
 
74
- .ui-navlink.is-current {
75
- font-weight: var(--weight-semibold);
76
- }
77
- .ui-navlink.is-current::after {
78
- opacity: 1;
79
- }
67
+ .ui-navlink:hover,
68
+ .ui-navlink.is-hover {
69
+ color: var(--link-hover);
70
+ }
71
+ .ui-navlink:hover::after,
72
+ .ui-navlink.is-hover::after {
73
+ opacity: 1;
74
+ }
80
75
 
81
- .ui-navlink:focus-visible,
82
- .ui-navlink.is-focus {
83
- outline: 2px solid var(--focus);
84
- outline-offset: 2px;
85
- box-shadow: 0 0 0 4px var(--focus-outer);
86
- }
76
+ .ui-navlink.is-current {
77
+ font-weight: var(--weight-semibold);
78
+ }
79
+ .ui-navlink.is-current::after {
80
+ opacity: 1;
81
+ }
87
82
 
88
- @media (prefers-reduced-motion: reduce) {
89
- .ui-navlink,
90
- .ui-navlink::after {
91
- transition: none;
83
+ .ui-navlink:focus-visible,
84
+ .ui-navlink.is-focus {
85
+ outline: 2px solid var(--focus);
86
+ outline-offset: 2px;
87
+ box-shadow: 0 0 0 4px var(--focus-outer);
88
+ }
89
+
90
+ @media (prefers-reduced-motion: reduce) {
91
+ .ui-navlink,
92
+ .ui-navlink::after {
93
+ transition: none;
94
+ }
92
95
  }
93
96
  }
94
97
  </style>
@@ -40,7 +40,9 @@ const classes = ["ui-news", stacked && "ui-news--stacked", className]
40
40
  ---
41
41
 
42
42
  <form class={classes} action={action} method={method} {...rest}>
43
- <label class="ui-sr-only" for={id}>{label}</label>
43
+ <label class="ui-sr-only" for={id}>
44
+ {label}
45
+ </label>
44
46
  <input
45
47
  class="ui-news__input"
46
48
  id={id}
@@ -50,69 +52,75 @@ const classes = ["ui-news", stacked && "ui-news--stacked", className]
50
52
  placeholder={placeholder}
51
53
  autocomplete="email"
52
54
  />
53
- <button class="ui-news__btn" type="submit">{buttonText}</button>
55
+ <button class="ui-news__btn" type="submit">
56
+ {buttonText}
57
+ </button>
54
58
  </form>
55
59
 
56
60
  <style>
57
- .ui-news {
58
- display: flex;
59
- gap: var(--space-2);
60
- max-width: 420px;
61
- }
61
+ @layer reset, tokens, base, components, motion, utilities;
62
62
 
63
- .ui-news--stacked {
64
- flex-direction: column;
65
- }
63
+ @layer components {
64
+ .ui-news {
65
+ display: flex;
66
+ gap: var(--space-2);
67
+ max-width: 420px;
68
+ }
66
69
 
67
- .ui-news__input {
68
- flex: 1;
69
- min-width: 0;
70
- min-height: 48px;
71
- padding: 0 var(--space-4);
72
- background: var(--surface-light);
73
- border: 1px solid var(--border-strong-light);
74
- border-radius: var(--radius-md);
75
- font-family: var(--font-sans);
76
- font-size: var(--text-sm);
77
- color: var(--text-light);
78
- }
79
- .ui-news__input::placeholder {
80
- color: var(--text-muted-light);
81
- }
82
- .ui-news__input:focus-visible,
83
- .ui-news__input.is-focus {
84
- outline: 2px solid var(--focus);
85
- outline-offset: 2px;
86
- box-shadow: 0 0 0 4px var(--focus-outer);
87
- }
70
+ .ui-news--stacked {
71
+ flex-direction: column;
72
+ }
88
73
 
89
- .ui-news__btn {
90
- flex: none;
91
- min-height: 48px;
92
- padding: 0 var(--space-5);
93
- background: var(--sage-light);
94
- color: var(--sage-xdark);
95
- border: 0;
96
- border-radius: var(--radius-md);
97
- font-family: var(--font-sans);
98
- font-size: var(--text-sm);
99
- font-weight: var(--weight-semibold);
100
- cursor: pointer;
101
- transition: background-color var(--duration-fast) var(--ease-standard);
102
- }
103
- .ui-news__btn:hover,
104
- .ui-news__btn.is-hover {
105
- background: var(--sage-xlight);
106
- }
107
- .ui-news__btn:focus-visible {
108
- outline: 2px solid var(--focus);
109
- outline-offset: 2px;
110
- box-shadow: 0 0 0 4px var(--focus-outer);
111
- }
74
+ .ui-news__input {
75
+ flex: 1;
76
+ min-width: 0;
77
+ min-height: 48px;
78
+ padding: 0 var(--space-4);
79
+ background: var(--surface-light);
80
+ border: 1px solid var(--border-strong-light);
81
+ border-radius: var(--radius-md);
82
+ font-family: var(--font-sans);
83
+ font-size: var(--text-sm);
84
+ color: var(--text-light);
85
+ }
86
+ .ui-news__input::placeholder {
87
+ color: var(--text-muted-light);
88
+ }
89
+ .ui-news__input:focus-visible,
90
+ .ui-news__input.is-focus {
91
+ outline: 2px solid var(--focus);
92
+ outline-offset: 2px;
93
+ box-shadow: 0 0 0 4px var(--focus-outer);
94
+ }
112
95
 
113
- @media (prefers-reduced-motion: reduce) {
114
96
  .ui-news__btn {
115
- transition: none;
97
+ flex: none;
98
+ min-height: 48px;
99
+ padding: 0 var(--space-5);
100
+ background: var(--sage-light);
101
+ color: var(--sage-xdark);
102
+ border: 0;
103
+ border-radius: var(--radius-md);
104
+ font-family: var(--font-sans);
105
+ font-size: var(--text-sm);
106
+ font-weight: var(--weight-semibold);
107
+ cursor: pointer;
108
+ transition: background-color var(--duration-fast) var(--ease-standard);
109
+ }
110
+ .ui-news__btn:hover,
111
+ .ui-news__btn.is-hover {
112
+ background: var(--sage-xlight);
113
+ }
114
+ .ui-news__btn:focus-visible {
115
+ outline: 2px solid var(--focus);
116
+ outline-offset: 2px;
117
+ box-shadow: 0 0 0 4px var(--focus-outer);
118
+ }
119
+
120
+ @media (prefers-reduced-motion: reduce) {
121
+ .ui-news__btn {
122
+ transition: none;
123
+ }
116
124
  }
117
125
  }
118
126
  </style>