recipe-planner-ui 1.0.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 (68) hide show
  1. package/LICENSE +21 -0
  2. package/dist/cjs/app-globals-V2Kpy_OQ.js +5 -0
  3. package/dist/cjs/index-B6uIqnrk.js +2043 -0
  4. package/dist/cjs/index.cjs.js +2 -0
  5. package/dist/cjs/loader.cjs.js +13 -0
  6. package/dist/cjs/recipe-planner-ui.cjs.js +25 -0
  7. package/dist/cjs/rp-day-slot.cjs.entry.js +57 -0
  8. package/dist/cjs/rp-filter-chips_5.cjs.entry.js +240 -0
  9. package/dist/collection/collection-manifest.json +18 -0
  10. package/dist/collection/components/rp-day-slot/rp-day-slot.css +131 -0
  11. package/dist/collection/components/rp-day-slot/rp-day-slot.js +201 -0
  12. package/dist/collection/components/rp-filter-chips/rp-filter-chips.css +36 -0
  13. package/dist/collection/components/rp-filter-chips/rp-filter-chips.js +123 -0
  14. package/dist/collection/components/rp-ingredient-list/rp-ingredient-list.css +53 -0
  15. package/dist/collection/components/rp-ingredient-list/rp-ingredient-list.js +57 -0
  16. package/dist/collection/components/rp-modal/rp-modal.css +94 -0
  17. package/dist/collection/components/rp-modal/rp-modal.js +196 -0
  18. package/dist/collection/components/rp-recipe-card/rp-recipe-card.css +106 -0
  19. package/dist/collection/components/rp-recipe-card/rp-recipe-card.js +170 -0
  20. package/dist/collection/components/rp-search-bar/rp-search-bar.css +65 -0
  21. package/dist/collection/components/rp-search-bar/rp-search-bar.js +166 -0
  22. package/dist/collection/index.js +1 -0
  23. package/dist/components/index.d.ts +35 -0
  24. package/dist/components/index.js +1 -0
  25. package/dist/components/rp-day-slot.d.ts +11 -0
  26. package/dist/components/rp-day-slot.js +1 -0
  27. package/dist/components/rp-filter-chips.d.ts +11 -0
  28. package/dist/components/rp-filter-chips.js +1 -0
  29. package/dist/components/rp-ingredient-list.d.ts +11 -0
  30. package/dist/components/rp-ingredient-list.js +1 -0
  31. package/dist/components/rp-modal.d.ts +11 -0
  32. package/dist/components/rp-modal.js +1 -0
  33. package/dist/components/rp-recipe-card.d.ts +11 -0
  34. package/dist/components/rp-recipe-card.js +1 -0
  35. package/dist/components/rp-search-bar.d.ts +11 -0
  36. package/dist/components/rp-search-bar.js +1 -0
  37. package/dist/esm/app-globals-DQuL1Twl.js +3 -0
  38. package/dist/esm/index-B4wAFfci.js +2034 -0
  39. package/dist/esm/index.js +1 -0
  40. package/dist/esm/loader.js +11 -0
  41. package/dist/esm/recipe-planner-ui.js +21 -0
  42. package/dist/esm/rp-day-slot.entry.js +55 -0
  43. package/dist/esm/rp-filter-chips_5.entry.js +234 -0
  44. package/dist/index.cjs.js +1 -0
  45. package/dist/index.js +1 -0
  46. package/dist/recipe-planner-ui/index.esm.js +0 -0
  47. package/dist/recipe-planner-ui/p-66ba2983.entry.js +1 -0
  48. package/dist/recipe-planner-ui/p-B4wAFfci.js +3 -0
  49. package/dist/recipe-planner-ui/p-DQuL1Twl.js +1 -0
  50. package/dist/recipe-planner-ui/p-d54dbd37.entry.js +1 -0
  51. package/dist/recipe-planner-ui/recipe-planner-ui.css +1 -0
  52. package/dist/recipe-planner-ui/recipe-planner-ui.esm.js +1 -0
  53. package/dist/types/components/rp-day-slot/rp-day-slot.d.ts +41 -0
  54. package/dist/types/components/rp-filter-chips/rp-filter-chips.d.ts +24 -0
  55. package/dist/types/components/rp-ingredient-list/rp-ingredient-list.d.ts +14 -0
  56. package/dist/types/components/rp-modal/rp-modal.d.ts +34 -0
  57. package/dist/types/components/rp-recipe-card/rp-recipe-card.d.ts +36 -0
  58. package/dist/types/components/rp-search-bar/rp-search-bar.d.ts +31 -0
  59. package/dist/types/components.d.ts +536 -0
  60. package/dist/types/index.d.ts +7 -0
  61. package/dist/types/stencil-public-runtime.d.ts +1873 -0
  62. package/loader/cdn.js +1 -0
  63. package/loader/index.cjs.js +1 -0
  64. package/loader/index.d.ts +24 -0
  65. package/loader/index.es2017.js +1 -0
  66. package/loader/index.js +2 -0
  67. package/package.json +72 -0
  68. package/readme.md +131 -0
@@ -0,0 +1,196 @@
1
+ import { Host, h } from "@stencil/core";
2
+ const FOCUSABLE = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
3
+ /**
4
+ * A modal dialog with a focus trap.
5
+ *
6
+ * Escape and focus containment are handled here rather than left to consumers, because
7
+ * getting them wrong is the usual reason a hand-rolled modal is inaccessible.
8
+ *
9
+ * @slot - Dialog body.
10
+ * @slot footer - Action row pinned to the bottom, typically confirm and cancel.
11
+ */
12
+ export class Modal {
13
+ el;
14
+ /** Whether the dialog is visible. */
15
+ open = false;
16
+ /** Dialog heading, also used as its accessible name. */
17
+ heading = '';
18
+ /** Fired when the user dismisses the dialog via Escape, the backdrop, or the close button. */
19
+ rpClose;
20
+ previouslyFocused = null;
21
+ onOpenChange(isOpen) {
22
+ if (isOpen) {
23
+ this.previouslyFocused = document.activeElement;
24
+ document.addEventListener('keydown', this.onKeydown);
25
+ // The dialog content renders in the same tick, so defer focus until it exists.
26
+ requestAnimationFrame(() => this.focusFirstField());
27
+ }
28
+ else {
29
+ document.removeEventListener('keydown', this.onKeydown);
30
+ this.previouslyFocused?.focus();
31
+ this.previouslyFocused = null;
32
+ }
33
+ }
34
+ componentDidLoad() {
35
+ // A dialog can be mounted already open, in which case @Watch never fires.
36
+ if (this.open)
37
+ this.onOpenChange(true);
38
+ }
39
+ disconnectedCallback() {
40
+ // Without this, every mount of a page containing a modal leaks a document listener.
41
+ document.removeEventListener('keydown', this.onKeydown);
42
+ }
43
+ /**
44
+ * Moves focus to the first focusable control inside the dialog.
45
+ *
46
+ * Exposed as a method because "move focus now" is a one-shot action with no state to
47
+ * represent; a prop would have to be toggled and reset to fire it twice.
48
+ */
49
+ async focusFirstField() {
50
+ const first = this.el.querySelector(FOCUSABLE);
51
+ first?.focus();
52
+ }
53
+ onKeydown = (event) => {
54
+ if (!this.open)
55
+ return;
56
+ if (event.key === 'Escape') {
57
+ event.preventDefault();
58
+ this.rpClose.emit();
59
+ return;
60
+ }
61
+ if (event.key === 'Tab')
62
+ this.trapFocus(event);
63
+ };
64
+ trapFocus(event) {
65
+ const focusable = Array.from(this.el.querySelectorAll(FOCUSABLE));
66
+ if (focusable.length === 0)
67
+ return;
68
+ const first = focusable[0];
69
+ const last = focusable[focusable.length - 1];
70
+ const active = document.activeElement;
71
+ // Wrap in both directions so Tab never escapes the dialog into the page behind it.
72
+ if (event.shiftKey && active === first) {
73
+ event.preventDefault();
74
+ last.focus();
75
+ }
76
+ else if (!event.shiftKey && active === last) {
77
+ event.preventDefault();
78
+ first.focus();
79
+ }
80
+ }
81
+ onBackdropClick = (event) => {
82
+ if (event.target === event.currentTarget)
83
+ this.rpClose.emit();
84
+ };
85
+ render() {
86
+ if (!this.open)
87
+ return null;
88
+ return (h(Host, null, h("div", { class: "backdrop", onClick: this.onBackdropClick }, h("div", { class: "dialog", role: "dialog", "aria-modal": "true", "aria-label": this.heading }, h("header", { class: "head" }, h("h2", { class: "heading" }, this.heading), h("button", { type: "button", class: "close", onClick: () => this.rpClose.emit(), "aria-label": "Close dialog" }, "\u00D7")), h("div", { class: "body" }, h("slot", null)), h("footer", { class: "foot" }, h("slot", { name: "footer" }))))));
89
+ }
90
+ static get is() { return "rp-modal"; }
91
+ static get encapsulation() { return "scoped"; }
92
+ static get originalStyleUrls() {
93
+ return {
94
+ "$": ["rp-modal.css"]
95
+ };
96
+ }
97
+ static get styleUrls() {
98
+ return {
99
+ "$": ["rp-modal.css"]
100
+ };
101
+ }
102
+ static get properties() {
103
+ return {
104
+ "open": {
105
+ "type": "boolean",
106
+ "mutable": false,
107
+ "complexType": {
108
+ "original": "boolean",
109
+ "resolved": "boolean",
110
+ "references": {}
111
+ },
112
+ "required": false,
113
+ "optional": false,
114
+ "docs": {
115
+ "tags": [],
116
+ "text": "Whether the dialog is visible."
117
+ },
118
+ "getter": false,
119
+ "setter": false,
120
+ "reflect": true,
121
+ "attribute": "open",
122
+ "defaultValue": "false"
123
+ },
124
+ "heading": {
125
+ "type": "string",
126
+ "mutable": false,
127
+ "complexType": {
128
+ "original": "string",
129
+ "resolved": "string",
130
+ "references": {}
131
+ },
132
+ "required": false,
133
+ "optional": false,
134
+ "docs": {
135
+ "tags": [],
136
+ "text": "Dialog heading, also used as its accessible name."
137
+ },
138
+ "getter": false,
139
+ "setter": false,
140
+ "reflect": false,
141
+ "attribute": "heading",
142
+ "defaultValue": "''"
143
+ }
144
+ };
145
+ }
146
+ static get events() {
147
+ return [{
148
+ "method": "rpClose",
149
+ "name": "rpClose",
150
+ "bubbles": true,
151
+ "cancelable": true,
152
+ "composed": true,
153
+ "docs": {
154
+ "tags": [],
155
+ "text": "Fired when the user dismisses the dialog via Escape, the backdrop, or the close button."
156
+ },
157
+ "complexType": {
158
+ "original": "void",
159
+ "resolved": "void",
160
+ "references": {}
161
+ }
162
+ }];
163
+ }
164
+ static get methods() {
165
+ return {
166
+ "focusFirstField": {
167
+ "complexType": {
168
+ "signature": "() => Promise<void>",
169
+ "parameters": [],
170
+ "references": {
171
+ "Promise": {
172
+ "location": "global",
173
+ "id": "global::Promise"
174
+ },
175
+ "HTMLElement": {
176
+ "location": "global",
177
+ "id": "global::HTMLElement"
178
+ }
179
+ },
180
+ "return": "Promise<void>"
181
+ },
182
+ "docs": {
183
+ "text": "Moves focus to the first focusable control inside the dialog.\n\nExposed as a method because \"move focus now\" is a one-shot action with no state to\nrepresent; a prop would have to be toggled and reset to fire it twice.",
184
+ "tags": []
185
+ }
186
+ }
187
+ };
188
+ }
189
+ static get elementRef() { return "el"; }
190
+ static get watchers() {
191
+ return [{
192
+ "propName": "open",
193
+ "methodName": "onOpenChange"
194
+ }];
195
+ }
196
+ }
@@ -0,0 +1,106 @@
1
+ :host {
2
+ display: block;
3
+ font-family: var(--rp-font-sans);
4
+ color: var(--rp-color-text);
5
+ }
6
+
7
+ .card {
8
+ display: flex;
9
+ flex-direction: column;
10
+ height: 100%;
11
+ overflow: hidden;
12
+ background: var(--rp-color-surface);
13
+ border: 1px solid var(--rp-color-border);
14
+ border-radius: var(--rp-radius-lg);
15
+ box-shadow: var(--rp-shadow-sm);
16
+ }
17
+
18
+ .media {
19
+ position: relative;
20
+ aspect-ratio: 4 / 3;
21
+ background: var(--rp-color-surface-sunken);
22
+ }
23
+
24
+ .media img {
25
+ display: block;
26
+ width: 100%;
27
+ height: 100%;
28
+ object-fit: cover;
29
+ }
30
+
31
+ .media-fallback {
32
+ width: 100%;
33
+ height: 100%;
34
+ background: linear-gradient(135deg, var(--rp-color-surface-sunken), var(--rp-color-border));
35
+ }
36
+
37
+ .badges {
38
+ position: absolute;
39
+ inset-block-start: var(--rp-space-2);
40
+ inset-inline-start: var(--rp-space-2);
41
+ display: flex;
42
+ flex-wrap: wrap;
43
+ gap: var(--rp-space-1);
44
+ }
45
+
46
+ .favorite {
47
+ position: absolute;
48
+ inset-block-start: var(--rp-space-2);
49
+ inset-inline-end: var(--rp-space-2);
50
+ display: grid;
51
+ place-items: center;
52
+ width: 36px;
53
+ height: 36px;
54
+ padding: 0;
55
+ cursor: pointer;
56
+ background: var(--rp-color-surface);
57
+ border: 1px solid var(--rp-color-border);
58
+ border-radius: 50%;
59
+ box-shadow: var(--rp-shadow-sm);
60
+ }
61
+
62
+ .favorite svg {
63
+ fill: none;
64
+ stroke: var(--rp-color-text-muted);
65
+ stroke-width: 1.8;
66
+ }
67
+
68
+ :host([favorite]) .favorite svg {
69
+ fill: var(--rp-color-favorite);
70
+ stroke: var(--rp-color-favorite);
71
+ }
72
+
73
+ .favorite:focus-visible {
74
+ outline: var(--rp-focus-ring);
75
+ outline-offset: var(--rp-focus-offset);
76
+ }
77
+
78
+ .body {
79
+ flex: 1;
80
+ padding: var(--rp-space-3) var(--rp-space-4);
81
+ }
82
+
83
+ .title {
84
+ margin: 0;
85
+ font-size: var(--rp-font-size-lg);
86
+ line-height: 1.3;
87
+ }
88
+
89
+ .category {
90
+ margin: var(--rp-space-1) 0 0;
91
+ font-size: var(--rp-font-size-sm);
92
+ color: var(--rp-color-text-muted);
93
+ }
94
+
95
+ .actions {
96
+ display: flex;
97
+ gap: var(--rp-space-2);
98
+ align-items: center;
99
+ padding: 0 var(--rp-space-4) var(--rp-space-4);
100
+ }
101
+
102
+ /* Collapse the footer when the consumer supplies no actions, so empty cards keep
103
+ their proportions instead of leaving a stray gap. */
104
+ .actions:not(:has(*)) {
105
+ display: none;
106
+ }
@@ -0,0 +1,170 @@
1
+ import { Host, h } from "@stencil/core";
2
+ /**
3
+ * A recipe summary tile.
4
+ *
5
+ * Takes only primitives, so it can be driven from plain HTML attributes with no
6
+ * framework present. It has no idea where a recipe comes from or what a route is —
7
+ * consumers inject navigation through the `actions` slot.
8
+ *
9
+ * @slot - Badges rendered over the image, such as a category or source marker.
10
+ * @slot actions - Controls rendered in the footer, typically links to view or edit.
11
+ */
12
+ export class RecipeCard {
13
+ el;
14
+ /** Identifier echoed back in the toggle event so consumers know which card fired. */
15
+ recipeId;
16
+ /**
17
+ * Recipe name, rendered as the card heading.
18
+ *
19
+ * Named `recipeTitle` rather than `title` because every HTMLElement already defines
20
+ * `title` on its prototype; shadowing it produces inconsistent behaviour across browsers.
21
+ */
22
+ recipeTitle;
23
+ /** Absolute URL of the recipe image. */
24
+ image;
25
+ /** Category label shown under the heading. */
26
+ category;
27
+ /** Whether this recipe is currently a favorite. Reflected for CSS hooks. */
28
+ favorite = false;
29
+ /** Fired when the favorite control is activated. */
30
+ rpFavoriteToggle;
31
+ toggleFavorite = () => {
32
+ // Emits the intended next state. The consumer owns the data and decides whether
33
+ // the change is applied, so this component never writes to its own prop.
34
+ this.rpFavoriteToggle.emit({ recipeId: this.recipeId, favorite: !this.favorite });
35
+ };
36
+ render() {
37
+ return (h(Host, { key: '7ac4bd65a2fa94815db81c986d17e3dff768334f' }, h("article", { key: 'b8ab03b01a7305ebac8fa8c7a512cf670afbd7c9', class: "card" }, h("div", { key: 'ddd9892bf42ed7fdd1300d439449ac4c471f11e3', class: "media" }, this.image ? (h("img", { src: this.image, alt: "", loading: "lazy", width: "320", height: "240" })) : (h("div", { class: "media-fallback", "aria-hidden": "true" })), h("div", { key: '211b2f1621e96b2350ff17598094790809c6fe81', class: "badges" }, h("slot", { key: 'ab834b8c4ff5c643c35d4f29c7bc9ddab5886e44' })), h("button", { key: '67d76da1db3b7d867d64042260cb69b30ea9fa2f', type: "button", class: "favorite", onClick: this.toggleFavorite, "aria-pressed": String(this.favorite), "aria-label": this.favorite ? `Remove ${this.recipeTitle} from favorites` : `Add ${this.recipeTitle} to favorites` }, h("svg", { key: 'ad94f74c83aba86af030d380d02ca331bf1edd66', viewBox: "0 0 24 24", width: "20", height: "20", "aria-hidden": "true" }, h("path", { key: 'cec837ac8a9e440730ed83f5746fba9833722d43', d: "M12 21s-7.5-4.7-9.3-9A5.2 5.2 0 0 1 12 6.5 5.2 5.2 0 0 1 21.3 12c-1.8 4.3-9.3 9-9.3 9z" })))), h("div", { key: '57e619602fb97b1197d1391e1bca37059fad4750', class: "body" }, h("h3", { key: '1576688c8d62e883132bb25f7f4f0832e7e102c2', class: "title" }, this.recipeTitle), this.category && h("p", { key: 'ca58ac4811bf94d13761ac0fd3fdd63a4b35c057', class: "category" }, this.category)), h("div", { key: 'fe7531c32eb5b110fe5aea2c4680a020848633bd', class: "actions" }, h("slot", { key: 'daf97a1324f3d2d08d1900d057cab1968be8c9d7', name: "actions" })))));
38
+ }
39
+ static get is() { return "rp-recipe-card"; }
40
+ static get encapsulation() { return "scoped"; }
41
+ static get originalStyleUrls() {
42
+ return {
43
+ "$": ["rp-recipe-card.css"]
44
+ };
45
+ }
46
+ static get styleUrls() {
47
+ return {
48
+ "$": ["rp-recipe-card.css"]
49
+ };
50
+ }
51
+ static get properties() {
52
+ return {
53
+ "recipeId": {
54
+ "type": "string",
55
+ "mutable": false,
56
+ "complexType": {
57
+ "original": "string",
58
+ "resolved": "string",
59
+ "references": {}
60
+ },
61
+ "required": true,
62
+ "optional": false,
63
+ "docs": {
64
+ "tags": [],
65
+ "text": "Identifier echoed back in the toggle event so consumers know which card fired."
66
+ },
67
+ "getter": false,
68
+ "setter": false,
69
+ "reflect": false,
70
+ "attribute": "recipe-id"
71
+ },
72
+ "recipeTitle": {
73
+ "type": "string",
74
+ "mutable": false,
75
+ "complexType": {
76
+ "original": "string",
77
+ "resolved": "string",
78
+ "references": {}
79
+ },
80
+ "required": true,
81
+ "optional": false,
82
+ "docs": {
83
+ "tags": [],
84
+ "text": "Recipe name, rendered as the card heading.\n\nNamed `recipeTitle` rather than `title` because every HTMLElement already defines\n`title` on its prototype; shadowing it produces inconsistent behaviour across browsers."
85
+ },
86
+ "getter": false,
87
+ "setter": false,
88
+ "reflect": false,
89
+ "attribute": "recipe-title"
90
+ },
91
+ "image": {
92
+ "type": "string",
93
+ "mutable": false,
94
+ "complexType": {
95
+ "original": "string",
96
+ "resolved": "string",
97
+ "references": {}
98
+ },
99
+ "required": false,
100
+ "optional": true,
101
+ "docs": {
102
+ "tags": [],
103
+ "text": "Absolute URL of the recipe image."
104
+ },
105
+ "getter": false,
106
+ "setter": false,
107
+ "reflect": false,
108
+ "attribute": "image"
109
+ },
110
+ "category": {
111
+ "type": "string",
112
+ "mutable": false,
113
+ "complexType": {
114
+ "original": "string",
115
+ "resolved": "string",
116
+ "references": {}
117
+ },
118
+ "required": false,
119
+ "optional": true,
120
+ "docs": {
121
+ "tags": [],
122
+ "text": "Category label shown under the heading."
123
+ },
124
+ "getter": false,
125
+ "setter": false,
126
+ "reflect": false,
127
+ "attribute": "category"
128
+ },
129
+ "favorite": {
130
+ "type": "boolean",
131
+ "mutable": false,
132
+ "complexType": {
133
+ "original": "boolean",
134
+ "resolved": "boolean",
135
+ "references": {}
136
+ },
137
+ "required": false,
138
+ "optional": false,
139
+ "docs": {
140
+ "tags": [],
141
+ "text": "Whether this recipe is currently a favorite. Reflected for CSS hooks."
142
+ },
143
+ "getter": false,
144
+ "setter": false,
145
+ "reflect": true,
146
+ "attribute": "favorite",
147
+ "defaultValue": "false"
148
+ }
149
+ };
150
+ }
151
+ static get events() {
152
+ return [{
153
+ "method": "rpFavoriteToggle",
154
+ "name": "rpFavoriteToggle",
155
+ "bubbles": true,
156
+ "cancelable": true,
157
+ "composed": true,
158
+ "docs": {
159
+ "tags": [],
160
+ "text": "Fired when the favorite control is activated."
161
+ },
162
+ "complexType": {
163
+ "original": "{ recipeId: string; favorite: boolean }",
164
+ "resolved": "{ recipeId: string; favorite: boolean; }",
165
+ "references": {}
166
+ }
167
+ }];
168
+ }
169
+ static get elementRef() { return "el"; }
170
+ }
@@ -0,0 +1,65 @@
1
+ :host {
2
+ display: block;
3
+ font-family: var(--rp-font-sans);
4
+ }
5
+
6
+ .bar {
7
+ display: flex;
8
+ gap: var(--rp-space-2);
9
+ align-items: center;
10
+ }
11
+
12
+ .field {
13
+ flex: 1;
14
+ min-width: 0;
15
+ padding: var(--rp-space-2) var(--rp-space-3);
16
+ font: inherit;
17
+ font-size: var(--rp-font-size-md);
18
+ color: var(--rp-color-text);
19
+ background: var(--rp-color-surface);
20
+ border: 1px solid var(--rp-color-border);
21
+ border-radius: var(--rp-radius-md);
22
+ }
23
+
24
+ /* The native clear affordance would sit beside our own button. */
25
+ .field::-webkit-search-cancel-button {
26
+ display: none;
27
+ }
28
+
29
+ .field:focus-visible,
30
+ .submit:focus-visible,
31
+ .clear:focus-visible {
32
+ outline: var(--rp-focus-ring);
33
+ outline-offset: var(--rp-focus-offset);
34
+ }
35
+
36
+ .clear {
37
+ display: grid;
38
+ place-items: center;
39
+ width: 32px;
40
+ height: 32px;
41
+ padding: 0;
42
+ font-size: 1.25rem;
43
+ line-height: 1;
44
+ color: var(--rp-color-text-muted);
45
+ cursor: pointer;
46
+ background: none;
47
+ border: none;
48
+ border-radius: 50%;
49
+ }
50
+
51
+ .clear:hover {
52
+ background: var(--rp-color-surface-sunken);
53
+ }
54
+
55
+ .submit {
56
+ padding: var(--rp-space-2) var(--rp-space-4);
57
+ font: inherit;
58
+ font-size: var(--rp-font-size-md);
59
+ font-weight: 600;
60
+ color: var(--rp-color-accent-contrast);
61
+ cursor: pointer;
62
+ background: var(--rp-color-accent);
63
+ border: none;
64
+ border-radius: var(--rp-radius-md);
65
+ }