katalyst-koi 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (206) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +23 -0
  4. data/Upgrade.md +6 -0
  5. data/app/assets/builds/koi/admin.css +1 -0
  6. data/app/assets/builds/koi/nav_items.css +1 -0
  7. data/app/assets/config/koi.js +10 -0
  8. data/app/assets/images/koi/application/chevron-right.svg +10 -0
  9. data/app/assets/images/koi/application/glyphicons-halflings-white.png +0 -0
  10. data/app/assets/images/koi/application/glyphicons-halflings.png +0 -0
  11. data/app/assets/images/koi/application/icon-collapse-down.png +0 -0
  12. data/app/assets/images/koi/application/icon-collapse-up.png +0 -0
  13. data/app/assets/images/koi/application/icon-file-doc.png +0 -0
  14. data/app/assets/images/koi/application/icon-file-img.png +0 -0
  15. data/app/assets/images/koi/application/icon-file-pdf.png +0 -0
  16. data/app/assets/images/koi/application/icon-file-ppt.png +0 -0
  17. data/app/assets/images/koi/application/icon-file-unknown.png +0 -0
  18. data/app/assets/images/koi/application/icon-file-xls.png +0 -0
  19. data/app/assets/images/koi/application/icon-file-zip.png +0 -0
  20. data/app/assets/images/koi/application/icon-form-date-picker.png +0 -0
  21. data/app/assets/images/koi/application/icon-form-error.png +0 -0
  22. data/app/assets/images/koi/application/icon-index-sort-ascending.png +0 -0
  23. data/app/assets/images/koi/application/icon-index-sort-descending.png +0 -0
  24. data/app/assets/images/koi/application/icon-index-sort.png +0 -0
  25. data/app/assets/images/koi/application/icon-index-sortable.png +0 -0
  26. data/app/assets/images/koi/application/icon-menu-cursor.png +0 -0
  27. data/app/assets/images/koi/application/icon-overlay-add.png +0 -0
  28. data/app/assets/images/koi/application/icon-overlay-close.png +0 -0
  29. data/app/assets/images/koi/application/icon-sortable.png +0 -0
  30. data/app/assets/images/koi/application/jcrop.gif +0 -0
  31. data/app/assets/images/koi/application/loading.gif +0 -0
  32. data/app/assets/images/koi/application/select-arrow.svg +3 -0
  33. data/app/assets/images/koi/application/select_arrow.png +0 -0
  34. data/app/assets/images/koi/application/sort-ascending.png +0 -0
  35. data/app/assets/images/koi/application/sort-descending.png +0 -0
  36. data/app/assets/javascripts/koi/admin.js +4 -0
  37. data/app/assets/javascripts/koi/controllers/application.js +11 -0
  38. data/app/assets/javascripts/koi/controllers/document_field_controller.js +26 -0
  39. data/app/assets/javascripts/koi/controllers/file_field_controller.js +143 -0
  40. data/app/assets/javascripts/koi/controllers/flash_controller.js +12 -0
  41. data/app/assets/javascripts/koi/controllers/form_request_submit_controller.js +11 -0
  42. data/app/assets/javascripts/koi/controllers/image_field_controller.js +24 -0
  43. data/app/assets/javascripts/koi/controllers/index.js +6 -0
  44. data/app/assets/javascripts/koi/controllers/index_actions_controller.js +61 -0
  45. data/app/assets/javascripts/koi/controllers/keyboard_controller.js +149 -0
  46. data/app/assets/javascripts/koi/controllers/navigation_controller.js +84 -0
  47. data/app/assets/javascripts/koi/controllers/navigation_toggle_controller.js +7 -0
  48. data/app/assets/javascripts/koi/controllers/show_hide_controller.js +25 -0
  49. data/app/assets/javascripts/koi/controllers/sluggable_controller.js +30 -0
  50. data/app/assets/javascripts/koi/controllers/webauthn_authentication_controller.js +23 -0
  51. data/app/assets/javascripts/koi/controllers/webauthn_registration_controller.js +30 -0
  52. data/app/assets/javascripts/koi/utils/transition.js +220 -0
  53. data/app/assets/stylesheets/koi/admin.scss +27 -0
  54. data/app/assets/stylesheets/koi/base/_button.scss +122 -0
  55. data/app/assets/stylesheets/koi/base/_icon.scss +29 -0
  56. data/app/assets/stylesheets/koi/base/_index.scss +18 -0
  57. data/app/assets/stylesheets/koi/base/_input.scss +13 -0
  58. data/app/assets/stylesheets/koi/base/_link.scss +26 -0
  59. data/app/assets/stylesheets/koi/base/_list.scss +11 -0
  60. data/app/assets/stylesheets/koi/base/_typography.scss +160 -0
  61. data/app/assets/stylesheets/koi/components/_actions-group.scss +7 -0
  62. data/app/assets/stylesheets/koi/components/_image-field.scss +33 -0
  63. data/app/assets/stylesheets/koi/components/_index-actions.scss +69 -0
  64. data/app/assets/stylesheets/koi/components/_index-table.scss +91 -0
  65. data/app/assets/stylesheets/koi/components/_index.scss +6 -0
  66. data/app/assets/stylesheets/koi/components/_item-table.scss +33 -0
  67. data/app/assets/stylesheets/koi/components/_pagy.scss +41 -0
  68. data/app/assets/stylesheets/koi/layouts/_banner.scss +7 -0
  69. data/app/assets/stylesheets/koi/layouts/_content.scss +40 -0
  70. data/app/assets/stylesheets/koi/layouts/_flash.scss +41 -0
  71. data/app/assets/stylesheets/koi/layouts/_header.scss +62 -0
  72. data/app/assets/stylesheets/koi/layouts/_index.scss +48 -0
  73. data/app/assets/stylesheets/koi/layouts/_main.scss +23 -0
  74. data/app/assets/stylesheets/koi/layouts/_navigation.scss +156 -0
  75. data/app/assets/stylesheets/koi/layouts/_stack.scss +13 -0
  76. data/app/assets/stylesheets/koi/pages/_index.scss +1 -0
  77. data/app/assets/stylesheets/koi/pages/_login.scss +40 -0
  78. data/app/assets/stylesheets/koi/themes/_content.scss +5 -0
  79. data/app/assets/stylesheets/koi/themes/_govuk.scss +52 -0
  80. data/app/assets/stylesheets/koi/themes/_index.scss +5 -0
  81. data/app/assets/stylesheets/koi/themes/_kpop.scss +5 -0
  82. data/app/assets/stylesheets/koi/themes/_navigation.scss +5 -0
  83. data/app/assets/stylesheets/koi/themes/_trix.scss +32 -0
  84. data/app/assets/stylesheets/koi/utils/_breakpoints.scss +13 -0
  85. data/app/assets/stylesheets/koi/utils/_hide.scss +11 -0
  86. data/app/assets/stylesheets/koi/utils/_index.scss +2 -0
  87. data/app/assets/stylesheets/koi/utils/_typography.scss +24 -0
  88. data/app/components/koi/header/edit_component.rb +58 -0
  89. data/app/components/koi/header/index_component.rb +23 -0
  90. data/app/components/koi/header/new_component.rb +40 -0
  91. data/app/components/koi/header/show_component.rb +51 -0
  92. data/app/components/koi/header_component.html.erb +16 -0
  93. data/app/components/koi/header_component.rb +28 -0
  94. data/app/components/koi/index_table_component.rb +21 -0
  95. data/app/controllers/admin/admin_users_controller.rb +88 -0
  96. data/app/controllers/admin/application_controller.rb +9 -0
  97. data/app/controllers/admin/caches_controller.rb +11 -0
  98. data/app/controllers/admin/credentials_controller.rb +64 -0
  99. data/app/controllers/admin/dashboards_controller.rb +7 -0
  100. data/app/controllers/admin/sessions_controller.rb +78 -0
  101. data/app/controllers/admin/url_rewrites_controller.rb +87 -0
  102. data/app/controllers/concerns/koi/controller/has_admin_users.rb +49 -0
  103. data/app/controllers/concerns/koi/controller/has_webauthn.rb +45 -0
  104. data/app/controllers/concerns/koi/controller/is_admin_controller.rb +52 -0
  105. data/app/helpers/katalyst/content/editor/errors.rb +21 -0
  106. data/app/helpers/katalyst/navigation/editor/errors.rb +21 -0
  107. data/app/helpers/koi/application_helper.rb +7 -0
  108. data/app/helpers/koi/date_helper.rb +36 -0
  109. data/app/helpers/koi/definition_list_helper.rb +92 -0
  110. data/app/helpers/koi/index_actions_helper.rb +99 -0
  111. data/app/jobs/koi/application_job.rb +6 -0
  112. data/app/mailers/koi/application_mailer.rb +8 -0
  113. data/app/models/admin/credential.rb +14 -0
  114. data/app/models/admin/user.rb +51 -0
  115. data/app/models/application_record.rb +5 -0
  116. data/app/models/concerns/koi/model/archivable.rb +55 -0
  117. data/app/models/url_rewrite.rb +25 -0
  118. data/app/views/admin/admin_users/_admin.html+row.erb +4 -0
  119. data/app/views/admin/admin_users/_authentication.html.erb +15 -0
  120. data/app/views/admin/admin_users/_fields.html.erb +4 -0
  121. data/app/views/admin/admin_users/edit.html.erb +11 -0
  122. data/app/views/admin/admin_users/index.html.erb +9 -0
  123. data/app/views/admin/admin_users/new.html.erb +11 -0
  124. data/app/views/admin/admin_users/show.html.erb +22 -0
  125. data/app/views/admin/credentials/new.html.erb +14 -0
  126. data/app/views/admin/dashboards/show.html.erb +1 -0
  127. data/app/views/admin/sessions/new.html.erb +19 -0
  128. data/app/views/admin/shared/icons/_close.html.erb +8 -0
  129. data/app/views/admin/shared/icons/_cross.html.erb +3 -0
  130. data/app/views/admin/shared/icons/_menu.html.erb +3 -0
  131. data/app/views/admin/shared/icons/_refresh.html.erb +8 -0
  132. data/app/views/admin/url_rewrites/_form_fields.html.erb +3 -0
  133. data/app/views/admin/url_rewrites/_url_rewrite.html+row.erb +7 -0
  134. data/app/views/admin/url_rewrites/edit.html.erb +12 -0
  135. data/app/views/admin/url_rewrites/index.html.erb +10 -0
  136. data/app/views/admin/url_rewrites/new.html.erb +11 -0
  137. data/app/views/admin/url_rewrites/show.html.erb +16 -0
  138. data/app/views/katalyst/content/asides/_aside.html+form.erb +18 -0
  139. data/app/views/katalyst/content/columns/_column.html+form.erb +18 -0
  140. data/app/views/katalyst/content/contents/_content.html+form.erb +20 -0
  141. data/app/views/katalyst/content/figures/_figure.html+form.erb +17 -0
  142. data/app/views/katalyst/content/groups/_group.html+form.erb +18 -0
  143. data/app/views/katalyst/content/items/_item.html+form.erb +18 -0
  144. data/app/views/katalyst/content/sections/_section.html+form.erb +18 -0
  145. data/app/views/katalyst/navigation/items/_button.html.erb +15 -0
  146. data/app/views/katalyst/navigation/items/_heading.html.erb +11 -0
  147. data/app/views/katalyst/navigation/items/_link.html.erb +13 -0
  148. data/app/views/katalyst/navigation/menus/edit.html.erb +12 -0
  149. data/app/views/katalyst/navigation/menus/new.html.erb +9 -0
  150. data/app/views/katalyst/navigation/menus/show.html.erb +18 -0
  151. data/app/views/layouts/koi/_environment.html.erb +4 -0
  152. data/app/views/layouts/koi/_flash.html.erb +8 -0
  153. data/app/views/layouts/koi/_header.html.erb +11 -0
  154. data/app/views/layouts/koi/_navigation.html.erb +13 -0
  155. data/app/views/layouts/koi/_navigation_collapse.html.erb +3 -0
  156. data/app/views/layouts/koi/_navigation_header.html.erb +6 -0
  157. data/app/views/layouts/koi/_navigation_item.html.erb +12 -0
  158. data/app/views/layouts/koi/application.html.erb +59 -0
  159. data/app/views/layouts/koi/login.html.erb +29 -0
  160. data/config/importmap.rb +9 -0
  161. data/config/initializers/flipper.rb +13 -0
  162. data/config/initializers/pagy.rb +1 -0
  163. data/config/initializers/time_formats.rb +5 -0
  164. data/config/locales/koi.en.yml +18 -0
  165. data/config/locales/pagy.en.yml +6 -0
  166. data/config/routes.rb +25 -0
  167. data/db/migrate/20120220130849_devise_create_admins.rb +56 -0
  168. data/db/migrate/20130509235316_add_url_rewriter.rb +13 -0
  169. data/db/migrate/20230213053854_convert_devise_admins_to_rails.rb +7 -0
  170. data/db/migrate/20230412023411_create_admin_user_credentials.rb +20 -0
  171. data/db/migrate/20230531063707_update_admin_users.rb +37 -0
  172. data/db/migrate/20230602033610_add_archived_to_admin_users.rb +7 -0
  173. data/db/seeds.rb +9 -0
  174. data/lib/generators/koi/active_record/active_record_generator.rb +43 -0
  175. data/lib/generators/koi/admin/USAGE +8 -0
  176. data/lib/generators/koi/admin/admin_generator.rb +20 -0
  177. data/lib/generators/koi/admin_controller/USAGE +17 -0
  178. data/lib/generators/koi/admin_controller/admin_controller_generator.rb +51 -0
  179. data/lib/generators/koi/admin_controller/templates/controller.rb.tt +81 -0
  180. data/lib/generators/koi/admin_controller/templates/controller_spec.rb.tt +135 -0
  181. data/lib/generators/koi/admin_route/admin_route_generator.rb +62 -0
  182. data/lib/generators/koi/admin_views/USAGE +12 -0
  183. data/lib/generators/koi/admin_views/admin_views_generator.rb +54 -0
  184. data/lib/generators/koi/admin_views/templates/_fields.html.erb.tt +3 -0
  185. data/lib/generators/koi/admin_views/templates/_record.html+row.erb.tt +10 -0
  186. data/lib/generators/koi/admin_views/templates/edit.html.erb.tt +12 -0
  187. data/lib/generators/koi/admin_views/templates/index.html.erb.tt +7 -0
  188. data/lib/generators/koi/admin_views/templates/new.html.erb.tt +11 -0
  189. data/lib/generators/koi/admin_views/templates/show.html.erb.tt +18 -0
  190. data/lib/govuk_design_system_formbuilder/concerns/file_element.rb +115 -0
  191. data/lib/govuk_design_system_formbuilder/elements/document.rb +59 -0
  192. data/lib/govuk_design_system_formbuilder/elements/image.rb +86 -0
  193. data/lib/katalyst/koi.rb +3 -0
  194. data/lib/koi/caching.rb +15 -0
  195. data/lib/koi/config.rb +11 -0
  196. data/lib/koi/engine.rb +40 -0
  197. data/lib/koi/form_builder.rb +76 -0
  198. data/lib/koi/menu/builder.rb +68 -0
  199. data/lib/koi/menu.rb +46 -0
  200. data/lib/koi/middleware/url_redirect.rb +44 -0
  201. data/lib/koi/release.rb +52 -0
  202. data/lib/koi/version.rb +5 -0
  203. data/lib/koi.rb +37 -0
  204. data/spec/factories/admins.rb +9 -0
  205. data/spec/factories/url_rewrites.rb +9 -0
  206. metadata +430 -0
@@ -0,0 +1,23 @@
1
+ import { Controller } from "@hotwired/stimulus";
2
+
3
+ import {
4
+ get,
5
+ parseRequestOptionsFromJSON,
6
+ } from "@github/webauthn-json/browser-ponyfill";
7
+
8
+ export default class WebauthnAuthenticationController extends Controller {
9
+ static targets = ["response"];
10
+ static values = { options: Object };
11
+
12
+ authenticate() {
13
+ get(this.options).then((response) => {
14
+ this.responseTarget.value = JSON.stringify(response);
15
+
16
+ this.element.requestSubmit();
17
+ });
18
+ }
19
+
20
+ get options() {
21
+ return parseRequestOptionsFromJSON(this.optionsValue);
22
+ }
23
+ }
@@ -0,0 +1,30 @@
1
+ import { Controller } from "@hotwired/stimulus";
2
+
3
+ import {
4
+ create,
5
+ parseCreationOptionsFromJSON,
6
+ } from "@github/webauthn-json/browser-ponyfill";
7
+
8
+ export default class WebauthnRegistrationController extends Controller {
9
+ static values = { options: Object };
10
+ static targets = ["response"];
11
+
12
+ submit(e) {
13
+ if (this.responseTarget.value === "") {
14
+ e.preventDefault();
15
+ this.createCredential();
16
+ }
17
+ }
18
+
19
+ createCredential() {
20
+ create(this.options).then((response) => {
21
+ this.responseTarget.value = JSON.stringify(response);
22
+
23
+ this.element.requestSubmit();
24
+ });
25
+ }
26
+
27
+ get options() {
28
+ return parseCreationOptionsFromJSON(this.optionsValue);
29
+ }
30
+ }
@@ -0,0 +1,220 @@
1
+ const DEFAULT_DELAY = 250;
2
+
3
+ /**
4
+ * A utility class for managing CSS transition animations.
5
+ *
6
+ * Transition uses Javascript timers to track state instead of relying on
7
+ * CSS transition events, which is a more complicated API. Please call `cancel`
8
+ * when the node being animated is detached from the DOM to avoid unexpected
9
+ * errors or animation glitches.
10
+ *
11
+ * Transition assumes that CSS already specifies styles to achieve the expected
12
+ * start and end states. Transition adds temporary overrides and then animates
13
+ * between those values using CSS transitions. For example, to use the collapse
14
+ * transition:
15
+ *
16
+ * @example
17
+ * // CSS:
18
+ * target {
19
+ * max-height: unset;
20
+ * overflow: 0;
21
+ * }
22
+ * target.hidden {
23
+ * max-height: 0;
24
+ * }
25
+ *
26
+ * @example
27
+ * // Javascript
28
+ * target.addClass("hidden");
29
+ * new Transition(target).collapse().start();
30
+ */
31
+ class Transition {
32
+ constructor(target, options) {
33
+ const { delay } = this._setDefaults(options);
34
+
35
+ this.target = target;
36
+ this.runner = new Runner(this, delay);
37
+ this.properties = [];
38
+
39
+ this.startingCallbacks = [];
40
+ this.startedCallbacks = [];
41
+ this.completeCallbacks = [];
42
+ }
43
+
44
+ add(property) {
45
+ this.properties.push(property);
46
+ return this;
47
+ }
48
+
49
+ /** Adds callback for transition events */
50
+ addCallback(type, callback) {
51
+ switch (type) {
52
+ case "starting":
53
+ this.startingCallbacks.push(callback);
54
+ break;
55
+ case "started":
56
+ this.startedCallbacks.push(callback);
57
+ break;
58
+ case "complete":
59
+ this.completeCallbacks.push(callback);
60
+ break;
61
+ }
62
+ return this;
63
+ }
64
+
65
+ /** Collapse an element in place, assumes overflow is set appropriately, margin is not collapsed */
66
+ collapse() {
67
+ return this.add(
68
+ new PropertyTransition(
69
+ "max-height",
70
+ `${this.target.scrollHeight}px`,
71
+ "0px",
72
+ ),
73
+ );
74
+ }
75
+
76
+ /** Restore a collapsed element */
77
+ expand() {
78
+ return this.add(
79
+ new PropertyTransition(
80
+ "max-height",
81
+ "0px",
82
+ `${this.target.scrollHeight}px`,
83
+ ),
84
+ );
85
+ }
86
+
87
+ /** Slide an element left or right by its scroll width, assumes position relative */
88
+ slideOut(direction) {
89
+ return this.add(
90
+ new PropertyTransition(direction, "0px", `-${this.target.scrollWidth}px`),
91
+ );
92
+ }
93
+
94
+ /** Restore an element that has been slid */
95
+ slideIn(direction) {
96
+ return this.add(
97
+ new PropertyTransition(direction, `-${this.target.scrollWidth}px`, "0px"),
98
+ );
99
+ }
100
+
101
+ /** Cause an element to become transparent by transforming opacity */
102
+ fadeOut() {
103
+ return this.add(new PropertyTransition("opacity", "100%", "0%"));
104
+ }
105
+
106
+ /** Cause a transparent element to become visible again */
107
+ fadeIn() {
108
+ return this.add(new PropertyTransition("opacity", "0%", "100%"));
109
+ }
110
+
111
+ start(callback = null) {
112
+ // start the runner on next tick so that any side-effects of the current execution can occur first
113
+ requestAnimationFrame(() => {
114
+ this.runner.start(this.target);
115
+ if (callback) callback();
116
+ });
117
+ return this;
118
+ }
119
+
120
+ cancel() {
121
+ this.runner.stop(this.target);
122
+ return this;
123
+ }
124
+
125
+ _starting() {
126
+ const event = new Event("transition:starting");
127
+ this.startingCallbacks.forEach((cb) => cb(event));
128
+ this.target.dispatchEvent(event);
129
+ }
130
+
131
+ _started() {
132
+ const event = new Event("transition:started");
133
+ this.startedCallbacks.forEach((cb) => cb(event));
134
+ this.target.dispatchEvent(event);
135
+ }
136
+
137
+ _complete() {
138
+ const event = new Event("transition:complete");
139
+ this.completeCallbacks.forEach((cb) => cb(event));
140
+ this.target.dispatchEvent(event);
141
+ }
142
+
143
+ _setDefaults(options) {
144
+ return Object.assign({ delay: DEFAULT_DELAY }, options);
145
+ }
146
+ }
147
+
148
+ /**
149
+ * Encapsulates internal execution and timing functionality for `Transition`
150
+ */
151
+ class Runner {
152
+ constructor(transition, delay) {
153
+ this.transition = transition;
154
+ this.running = null;
155
+ this.delay = delay;
156
+ }
157
+
158
+ start(target) {
159
+ // 1. Set the initial state(s)
160
+ this.transition.properties.forEach((t) => t.onStarting(target));
161
+
162
+ // 2. On next update, set transition and final state(s)
163
+ requestAnimationFrame(() => this.onStarted(target));
164
+
165
+ // 3. After transition has finished, clean up
166
+ this.running = setTimeout(() => this.stop(target, true), this.delay);
167
+
168
+ this.transition._starting();
169
+ }
170
+
171
+ onStarted(target) {
172
+ target.style.transitionProperty = this.transition.properties
173
+ .map((t) => t.property)
174
+ .join(",");
175
+ target.style.transitionDuration = `${this.delay}ms`;
176
+ this.transition.properties.forEach((t) => t.onStarted(target));
177
+
178
+ this.transition._started();
179
+ }
180
+
181
+ stop(target, timeout = false) {
182
+ if (!this.running) return;
183
+ if (!timeout) clearTimeout(this.running);
184
+
185
+ this.running = null;
186
+
187
+ target.style.removeProperty("transition-property");
188
+ target.style.removeProperty("transition-duration");
189
+ this.transition.properties.forEach((t) => t.onComplete(target));
190
+
191
+ this.transition._complete();
192
+ }
193
+ }
194
+
195
+ /**
196
+ * Represents animation of a single CSS property. Currently only CSS animations
197
+ * are supported, but this could be a natural extension point for Javascript
198
+ * animations in the future.
199
+ */
200
+ class PropertyTransition {
201
+ constructor(property, from, to) {
202
+ this.property = property;
203
+ this.from = from;
204
+ this.to = to;
205
+ }
206
+
207
+ onStarting(target) {
208
+ target.style.setProperty(this.property, this.from);
209
+ }
210
+
211
+ onStarted(target) {
212
+ target.style.setProperty(this.property, this.to);
213
+ }
214
+
215
+ onComplete(target) {
216
+ target.style.removeProperty(this.property);
217
+ }
218
+ }
219
+
220
+ export { Transition };
@@ -0,0 +1,27 @@
1
+ // DEPENDENCIES
2
+
3
+ @use "base";
4
+ @use "components";
5
+ @use "layouts";
6
+ @use "pages";
7
+ @use "themes";
8
+ @use "utils";
9
+
10
+ :root {
11
+ /* Color styles */
12
+ --site-primary-light: #5a87c9;
13
+ --site-primary: #266dd3;
14
+ --site-primary-dark: #344055;
15
+ --site-on-primary: #ffffff;
16
+ --site-secondary-light: #5a87c9;
17
+ --site-secondary: #266dd3;
18
+ --site-secondary-dark: #344055;
19
+ --site-on-secondary: #ffffff;
20
+ --site-text-color: #000000;
21
+ --site-success: #129d7f;
22
+ --site-warning: #ffc759;
23
+ --site-alert: #ea3946;
24
+ --site-notice: rgb(37, 99, 235);
25
+ --site-disabled: #9ca3af;
26
+ --site-link: #266dd3;
27
+ }
@@ -0,0 +1,122 @@
1
+ %button-base {
2
+ display: inline-block;
3
+ position: relative;
4
+ padding: 5px 1rem;
5
+ cursor: pointer;
6
+ height: 2.5rem;
7
+
8
+ border: none;
9
+ outline: none;
10
+ transition:
11
+ color 0.2s ease,
12
+ background 0.2s ease,
13
+ border-color 0.2s ease;
14
+ text-decoration: none;
15
+
16
+ font-weight: 500;
17
+ }
18
+
19
+ @mixin button-filled {
20
+ @extend %button-base;
21
+ background: var(--site-primary);
22
+ border: 2px solid var(--site-primary);
23
+ color: var(--site-on-primary);
24
+
25
+ &:hover,
26
+ &:focus {
27
+ background: var(--site-primary-dark);
28
+ border-color: var(--site-primary-dark);
29
+ text-decoration: none;
30
+ }
31
+
32
+ &:active {
33
+ background: var(--site-primary-dark);
34
+ border-color: var(--site-primary-dark);
35
+ text-decoration: none;
36
+ }
37
+
38
+ &[disabled] {
39
+ background: var(--site-primary-light);
40
+ border-color: var(--site-primary-light);
41
+ opacity: 0.45;
42
+
43
+ &:hover,
44
+ &:focus {
45
+ background: var(--site-primary-light);
46
+ border-color: var(--site-primary-light);
47
+ }
48
+
49
+ &:active {
50
+ background: var(--site-primary-light);
51
+ border-color: var(--site-primary-light);
52
+ }
53
+ }
54
+ }
55
+
56
+ @mixin button-outline {
57
+ @extend %button-base;
58
+
59
+ color: var(--site-secondary);
60
+ border: 2px solid var(--site-secondary);
61
+ background: transparent;
62
+
63
+ &:hover,
64
+ &:focus {
65
+ border-color: var(--site-secondary);
66
+ background: var(--site-secondary);
67
+ color: var(--site-on-secondary);
68
+ text-decoration: none;
69
+ }
70
+
71
+ &:active {
72
+ border-color: var(--site-secondary);
73
+ background: var(--site-secondary);
74
+ color: var(--site-on-secondary);
75
+ text-decoration: none;
76
+ }
77
+
78
+ &[disabled] {
79
+ background: none;
80
+ &:hover,
81
+ &:focus {
82
+ background: none;
83
+ }
84
+
85
+ &:active {
86
+ background: none;
87
+ }
88
+ }
89
+ }
90
+
91
+ @mixin button-text {
92
+ @extend %button-base;
93
+
94
+ color: var(--site-primary);
95
+ background: none;
96
+
97
+ &:hover,
98
+ &:focus {
99
+ background: none;
100
+ color: var(--site-text-color);
101
+ }
102
+
103
+ &:active {
104
+ background: none;
105
+ color: var(--site-text-color);
106
+ }
107
+
108
+ &[disabled] {
109
+ background: none;
110
+
111
+ &:hover,
112
+ &:focus {
113
+ color: var(--site-primary-light);
114
+ background: none;
115
+ }
116
+
117
+ &:active {
118
+ color: var(--site-primary-light);
119
+ background: none;
120
+ }
121
+ }
122
+ }
@@ -0,0 +1,29 @@
1
+ .icon {
2
+ width: 1.5em;
3
+ height: 1.5em;
4
+ display: inline-block;
5
+ }
6
+
7
+ .icon-large {
8
+ width: 2em;
9
+ height: 2em;
10
+ display: inline-block;
11
+ }
12
+
13
+ .icon-small {
14
+ width: 0.75em;
15
+ height: 0.75em;
16
+ display: inline-block;
17
+ }
18
+
19
+ .icon-medium {
20
+ width: 1em;
21
+ height: 1em;
22
+ display: inline-block;
23
+ }
24
+
25
+ .leading-icon {
26
+ display: flex;
27
+ gap: 0.25rem;
28
+ align-items: center;
29
+ }
@@ -0,0 +1,18 @@
1
+ @use "button";
2
+ @use "icon";
3
+ @use "input";
4
+ @use "link";
5
+ @use "list";
6
+ @use "typography";
7
+
8
+ .button--primary {
9
+ @include button.button-filled;
10
+ }
11
+
12
+ .button--secondary {
13
+ @include button.button-outline;
14
+ }
15
+
16
+ .button--text {
17
+ @include button.button-text;
18
+ }
@@ -0,0 +1,13 @@
1
+ input,
2
+ select {
3
+ border: 2px solid var(--site-text-color);
4
+ height: 2.5rem;
5
+ line-height: 1;
6
+ padding: 5px;
7
+
8
+ &:focus {
9
+ outline: 3px solid #fd0;
10
+ outline-offset: 0;
11
+ box-shadow: inset 0 0 0 2px;
12
+ }
13
+ }
@@ -0,0 +1,26 @@
1
+ a[href]:not(.button) {
2
+ color: var(--site-link);
3
+ text-decoration: none;
4
+
5
+ &:hover,
6
+ &:focus {
7
+ color: var(--site-link);
8
+ text-decoration: underline;
9
+ outline: none;
10
+ }
11
+
12
+ &.no-hover-link {
13
+ text-decoration: none;
14
+
15
+ &:hover,
16
+ &:focus {
17
+ text-decoration: none;
18
+ }
19
+ }
20
+
21
+ &[disabled] {
22
+ color: var(--site-disabled);
23
+ pointer-events: none;
24
+ opacity: 0.45;
25
+ }
26
+ }
@@ -0,0 +1,11 @@
1
+ ol,
2
+ ul {
3
+ margin: 0;
4
+ padding-left: 3ch;
5
+ }
6
+
7
+ ul.no-bullet {
8
+ padding-left: 0;
9
+ list-style-type: none;
10
+ max-width: none;
11
+ }
@@ -0,0 +1,160 @@
1
+ $font-sizes: (
2
+ h1: 3.3125rem,
3
+ h2: 2rem,
4
+ h3: 1.5rem,
5
+ h4: 1.125rem,
6
+ h5: 1.125rem,
7
+ h6: 1rem,
8
+ paragraph: 1rem,
9
+ small: 0.875rem,
10
+ ) !default;
11
+
12
+ $line-heights: (
13
+ h1: 1.03em,
14
+ h2: 1.09em,
15
+ h3: 1.04em,
16
+ h4: 1.11em,
17
+ h5: 1.11em,
18
+ // used for labels
19
+ h6: 1.5em,
20
+ paragraph: 1.14em,
21
+ small: 1.25em,
22
+ ) !default;
23
+
24
+ @use "../utils/breakpoints";
25
+ @use "../utils/typography" as * with (
26
+ $-font-sizes: $font-sizes,
27
+ $-line-heights: $line-heights
28
+ );
29
+
30
+ :root {
31
+ font-family: "Inter", sans-serif;
32
+ font-size: 100%; // defaults to 16px
33
+ color: var(--site-text-color);
34
+
35
+ /* Text-size styles */
36
+ /* base size: paragraph (16px) */
37
+ --heading--h1: #{font-size(h1)};
38
+ --heading--h2: #{font-size(h2)};
39
+ --heading--h3: #{font-size(h3)};
40
+ --heading--h4: #{font-size(h4)};
41
+ --heading--h5: #{font-size(h5)};
42
+ --heading--h6: #{font-size(h6)};
43
+ --paragraph: #{font-size(paragraph)};
44
+ --paragraph--strong: #{font-size(paragraph)};
45
+ --paragraph--em: #{font-size(paragraph)};
46
+ --paragraph--small: #{font-size(small)};
47
+ --text-width: 70ch;
48
+ }
49
+
50
+ @supports (font-variation-settings: normal) {
51
+ :root {
52
+ font-family: "Inter var", sans-serif;
53
+ }
54
+ }
55
+
56
+ h1,
57
+ .heading-one {
58
+ font-size: var(--heading--h1);
59
+ font-weight: 400;
60
+ line-height: line-height(h1);
61
+ margin-bottom: 0.25em;
62
+ letter-spacing: -0.015em;
63
+ color: var(--site-text-color);
64
+ }
65
+
66
+ h2,
67
+ .heading-two {
68
+ font-size: var(--heading--h2);
69
+ font-weight: 400;
70
+ line-height: line-height(h2);
71
+ margin-bottom: 0.25em;
72
+ letter-spacing: -0.015em;
73
+ color: var(--site-text-color);
74
+ }
75
+
76
+ h3,
77
+ .heading-three {
78
+ font-size: var(--heading--h3);
79
+ font-weight: 400;
80
+ line-height: line-height(h3);
81
+ margin: 0 0 0.25em;
82
+ letter-spacing: -0.015em;
83
+ color: var(--site-text-color);
84
+ }
85
+
86
+ h4,
87
+ .heading-four {
88
+ font-size: var(--heading--h4);
89
+ font-weight: 400;
90
+ line-height: line-height(h4);
91
+ margin: 0 0 0.25em;
92
+ letter-spacing: -0.015em;
93
+ color: var(--site-text-color);
94
+ }
95
+
96
+ h5,
97
+ .heading-five {
98
+ font-size: var(--heading--h5);
99
+ font-weight: 400;
100
+ line-height: line-height(h5);
101
+ margin: 0 0 0.25em;
102
+ letter-spacing: -0.015em;
103
+ color: var(--site-text-color);
104
+ }
105
+
106
+ h6,
107
+ .heading-six {
108
+ font-size: var(--heading--h6);
109
+ font-weight: 400; // Bold
110
+ line-height: line-height(h6);
111
+ margin: 0 0 0.25em;
112
+ letter-spacing: -0.015em;
113
+ color: var(--site-text-color);
114
+ }
115
+
116
+ label {
117
+ font-size: var(--heading-h6);
118
+ font-weight: 500; // Bold
119
+ line-height: line-height(h6);
120
+ margin-bottom: 0.125rem;
121
+ letter-spacing: 0.01em;
122
+ color: var(--site-text-color);
123
+ }
124
+
125
+ p,
126
+ .body-one {
127
+ font-size: var(--paragraph);
128
+ line-height: line-height(paragraph);
129
+ letter-spacing: 0.01em;
130
+ color: var(--site-text-color);
131
+ }
132
+
133
+ small,
134
+ em {
135
+ font-size: var(--paragraph--em);
136
+ line-height: line-height(paragraph);
137
+ letter-spacing: 0.01em;
138
+ font-weight: 300;
139
+ color: var(--site-text-color);
140
+ }
141
+
142
+ strong {
143
+ font-size: var(--paragraph--strong);
144
+ font-weight: 600;
145
+ letter-spacing: 0.01em;
146
+ color: var(--site-text-color);
147
+ }
148
+
149
+ h1,
150
+ h2,
151
+ h3,
152
+ h4,
153
+ p,
154
+ small,
155
+ legend,
156
+ label,
157
+ input,
158
+ textarea {
159
+ max-width: var(--text-width);
160
+ }
@@ -0,0 +1,7 @@
1
+ .actions-group,
2
+ .actions {
3
+ display: flex;
4
+ align-items: stretch;
5
+ flex-direction: row;
6
+ column-gap: 0.5rem;
7
+ }