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,33 @@
1
+ .govuk-image-field {
2
+ .govuk-form-group {
3
+ margin-top: 30px;
4
+ }
5
+ }
6
+
7
+ .preview-image,
8
+ .preview-file {
9
+ display: inline-flex;
10
+ padding: 0.5rem;
11
+
12
+ .file-destroy {
13
+ align-self: center;
14
+ padding-left: 0.5rem;
15
+
16
+ &::after {
17
+ content: "";
18
+ background-image: url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiIGhlaWdodD0iMjEiIHZpZXdCb3g9IjAgMCAyMSAyMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSIjQjJCMkIyIj48cGF0aCBkPSJNMi41OTIuMDQ0bDE4LjM2NCAxOC4zNjQtMi41NDggMi41NDhMLjA0NCAyLjU5MnoiLz48cGF0aCBkPSJNMCAxOC4zNjRMMTguMzY0IDBsMi41NDggMi41NDhMMi41NDggMjAuOTEyeiIvPjwvZz48L3N2Zz4=");
19
+ background-repeat: no-repeat;
20
+ background-size: contain;
21
+ background-position: center;
22
+ display: inline-block;
23
+ width: 12px;
24
+ height: 12px;
25
+ margin-left: 0.5rem;
26
+ }
27
+ }
28
+
29
+ .image-thumbnail {
30
+ max-width: 100%;
31
+ max-height: 80px;
32
+ }
33
+ }
@@ -0,0 +1,69 @@
1
+ .index-actions > form {
2
+ display: flex;
3
+ align-items: stretch;
4
+ justify-content: space-between;
5
+ margin-bottom: 1rem;
6
+
7
+ // checkbox styles based on govuk-frontend
8
+
9
+ input[type="checkbox"] {
10
+ cursor: pointer;
11
+ position: absolute;
12
+ z-index: 1;
13
+ top: 0;
14
+ bottom: 0;
15
+ left: 0;
16
+ width: 2.5rem;
17
+ opacity: 0;
18
+ }
19
+
20
+ label:has(input[type="checkbox"]) {
21
+ display: inline-block;
22
+ position: relative;
23
+ margin-bottom: 0;
24
+ padding: 0 0 0 calc(2.5rem + 5px);
25
+ line-height: 2.5rem;
26
+ cursor: pointer;
27
+ -ms-touch-action: manipulation;
28
+ touch-action: manipulation;
29
+ }
30
+
31
+ label:has(input[type="checkbox"])::before {
32
+ content: "";
33
+ box-sizing: border-box;
34
+ position: absolute;
35
+ top: 0;
36
+ bottom: 0;
37
+ left: 0;
38
+ aspect-ratio: 1/1;
39
+ border: 2px solid currentcolor;
40
+ background: transparent;
41
+ }
42
+
43
+ label:has(input[type="checkbox"]:focus)::before {
44
+ border-width: 4px;
45
+ outline: 3px solid transparent;
46
+ outline-offset: 1px;
47
+ box-shadow: 0 0 0 3px #ffdd00;
48
+ }
49
+
50
+ label:has(input[type="checkbox"])::after {
51
+ content: "";
52
+ box-sizing: border-box;
53
+ position: absolute;
54
+ top: 12px;
55
+ left: 9px;
56
+ width: 23px;
57
+ height: 12px;
58
+ transform: rotate(-45deg);
59
+ border: solid;
60
+ border-width: 0 0 5px 5px;
61
+ border-top-color: transparent;
62
+ opacity: 0;
63
+ background: transparent;
64
+ }
65
+
66
+ label:has(input[type="checkbox"][checked])::after {
67
+ opacity: 1;
68
+ }
69
+ }
@@ -0,0 +1,91 @@
1
+ @mixin sort-icon {
2
+ display: inline-block;
3
+ content: " ";
4
+ position: relative;
5
+ right: -1rem;
6
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 13'%3E%3Cpath d='M.541 0l11.125 12.573a.5.5 0 00.749 0L23.541 0h-23z' fill='%23000' fill-rule='evenodd'/%3E%3C/svg%3E");
7
+ background-size: 14px 14px;
8
+ height: 14px;
9
+ width: 14px;
10
+ }
11
+
12
+ $grey: #f0ecf3 !default;
13
+
14
+ $table-header-color: transparent !default;
15
+ $row-border-color: $grey !default;
16
+ $row-height: 48px !default;
17
+
18
+ .index-table {
19
+ --row-height: #{$row-height};
20
+ --table-header-color: #{$table-header-color};
21
+ --row-border-color: #{$row-border-color};
22
+ }
23
+
24
+ .index-table {
25
+ border: none;
26
+ table-layout: fixed;
27
+ border-collapse: collapse;
28
+ text-align: left;
29
+ width: 100%;
30
+
31
+ thead {
32
+ background: var(--table-header-color);
33
+ }
34
+
35
+ tr {
36
+ height: var(--row-height);
37
+ line-height: var(--row-height);
38
+ }
39
+
40
+ th,
41
+ td {
42
+ border: none;
43
+ box-shadow: inset 0px -1px 0px var(--row-border-color);
44
+ overflow: hidden;
45
+ text-overflow: ellipsis;
46
+ vertical-align: top;
47
+ white-space: nowrap;
48
+
49
+ > a {
50
+ display: block;
51
+ overflow: hidden;
52
+ white-space: nowrap;
53
+ text-overflow: ellipsis;
54
+ text-decoration: none;
55
+ }
56
+
57
+ > img {
58
+ width: 6rem;
59
+ padding: 1rem 0;
60
+ }
61
+
62
+ > a > img {
63
+ padding-top: 1rem;
64
+ }
65
+ }
66
+
67
+ th {
68
+ font-weight: bold;
69
+ }
70
+
71
+ thead a.ascending:after,
72
+ [data-sort="asc"] a::after {
73
+ @include sort-icon;
74
+ top: 0;
75
+ transform: rotate(180deg);
76
+ }
77
+
78
+ thead a.descending:after,
79
+ [data-sort="desc"] a::after {
80
+ @include sort-icon;
81
+ top: 4px;
82
+ }
83
+
84
+ .button_to {
85
+ line-height: 1.5;
86
+ }
87
+
88
+ caption {
89
+ margin: 2rem 0;
90
+ }
91
+ }
@@ -0,0 +1,6 @@
1
+ @use "actions-group";
2
+ @use "image-field";
3
+ @use "index-actions";
4
+ @use "index-table";
5
+ @use "item-table";
6
+ @use "pagy";
@@ -0,0 +1,33 @@
1
+ $grey: #f0ecf3 !default;
2
+
3
+ $table-header-color: transparent !default;
4
+ $row-border-color: $grey !default;
5
+ $row-height: 48px !default;
6
+
7
+ .item-table {
8
+ --row-height: #{$row-height};
9
+ --table-header-color: #{$table-header-color};
10
+ --row-border-color: #{$row-border-color};
11
+ }
12
+
13
+ .item-table {
14
+ display: grid;
15
+ grid-template-columns: 1fr 3fr;
16
+ margin: 1rem 0;
17
+ border-top: 1px solid var(--row-border-color);
18
+
19
+ dt,
20
+ dd {
21
+ display: flex;
22
+ align-items: center;
23
+ border-bottom: 1px solid var(--row-border-color);
24
+ padding: 0 1rem;
25
+ min-height: var(--row-height);
26
+ line-height: 1.5rem;
27
+ }
28
+
29
+ dt {
30
+ font-weight: bold;
31
+ padding-left: 0;
32
+ }
33
+ }
@@ -0,0 +1,41 @@
1
+ @use "../base/button" as *;
2
+
3
+ .pagination {
4
+ .page {
5
+ a {
6
+ @include button-text;
7
+ border: 2px solid transparent;
8
+
9
+ &:hover {
10
+ text-decoration: none;
11
+ }
12
+ }
13
+
14
+ &.prev,
15
+ &.next {
16
+ a {
17
+ border: none;
18
+ padding: 0;
19
+ }
20
+ }
21
+
22
+ &.prev {
23
+ padding-right: 1rem;
24
+ }
25
+
26
+ &.next {
27
+ padding-left: 1rem;
28
+ }
29
+ }
30
+
31
+ .active {
32
+ @include button-outline;
33
+ pointer-events: none;
34
+ }
35
+
36
+ .disabled {
37
+ pointer-events: none;
38
+ color: var(--site-disabled);
39
+ font-weight: 500;
40
+ }
41
+ }
@@ -0,0 +1,7 @@
1
+ %banner {
2
+ line-height: 2rem;
3
+ text-align: center;
4
+ color: #eee;
5
+ background: #555;
6
+ box-shadow: inset 0 -1px 4px #333;
7
+ }
@@ -0,0 +1,40 @@
1
+ @use "header";
2
+ @use "flash";
3
+
4
+ $inset: 1rem !default;
5
+ $space: 1rem !default;
6
+
7
+ %content {
8
+ margin: $space $inset;
9
+ display: grid;
10
+ grid-template-columns: minmax(auto, 72rem);
11
+ grid-template-areas: "main";
12
+ grid-gap: $space;
13
+ max-width: 72rem;
14
+
15
+ > [role="main"] {
16
+ grid-area: main;
17
+ }
18
+
19
+ &.has-sidebar {
20
+ grid-template-columns: minmax(auto, 2fr) minmax(auto, 1fr);
21
+ grid-template-areas: "main sidebar";
22
+ max-width: (72rem + 36rem);
23
+
24
+ > [role="main"] {
25
+ grid-column: 1/2;
26
+ }
27
+ }
28
+
29
+ .page--sidebar {
30
+ grid-area: sidebar;
31
+ > * {
32
+ position: sticky;
33
+ top: $space;
34
+ }
35
+ }
36
+
37
+ #flash {
38
+ @include flash.flash;
39
+ }
40
+ }
@@ -0,0 +1,41 @@
1
+ @mixin flash {
2
+ padding: 0;
3
+
4
+ li {
5
+ display: flex;
6
+ justify-content: space-between;
7
+ min-height: 3rem;
8
+ line-height: 3rem;
9
+ padding: 0 1.5rem;
10
+ list-style: none;
11
+ grid-area: status;
12
+ font-weight: bold;
13
+ border: 1px solid var(--flash-color);
14
+ color: var(--flash-color);
15
+ }
16
+
17
+ button {
18
+ display: inline;
19
+ background: white;
20
+ border: none;
21
+ color: var(--flash-color);
22
+ cursor: pointer;
23
+ }
24
+
25
+ .alert {
26
+ --flash-color: var(--site-alert);
27
+ }
28
+
29
+ .notice {
30
+ --flash-color: var(--site-notice);
31
+ }
32
+
33
+ .success {
34
+ --flash-color: var(--site-success);
35
+ }
36
+
37
+ .warning,
38
+ .error {
39
+ --flash-color: var(--site-warning);
40
+ }
41
+ }
@@ -0,0 +1,62 @@
1
+ @mixin list($separator, $space: 1rem) {
2
+ display: flex;
3
+ list-style-position: outside;
4
+
5
+ > * {
6
+ display: list-item;
7
+ margin-left: $space;
8
+ }
9
+
10
+ > *::marker {
11
+ content: " " + $separator + " ";
12
+ color: rgba(0, 0, 0, 0.4);
13
+ }
14
+
15
+ > *:first-child {
16
+ display: block;
17
+ margin-left: 0;
18
+ }
19
+ }
20
+
21
+ $inset: 2rem !default;
22
+
23
+ $title: 4rem;
24
+ $breadcrumbs: 3rem;
25
+ $actions: 3rem;
26
+ $height: $title + $breadcrumbs + $actions;
27
+
28
+ %header {
29
+ border-bottom: 1px solid #ececec;
30
+ color: #b2b2b2;
31
+ background: #ececec;
32
+
33
+ display: grid;
34
+ grid-template-areas: "breadcrumbs" "title" "actions";
35
+ grid-template-rows: $breadcrumbs $title $actions;
36
+
37
+ .heading {
38
+ grid-area: title;
39
+ display: flex;
40
+ align-items: center;
41
+ margin: 0 $inset;
42
+ }
43
+
44
+ .breadcrumbs {
45
+ grid-area: breadcrumbs;
46
+ display: flex;
47
+ align-items: center;
48
+ margin: 0 $inset;
49
+
50
+ @include list("› ", 1.2rem);
51
+ }
52
+
53
+ .actions {
54
+ grid-area: actions;
55
+ display: flex;
56
+ align-items: center;
57
+ margin: 0 $inset;
58
+
59
+ @include list("|", 0.5rem);
60
+ list-style-type: square;
61
+ }
62
+ }
@@ -0,0 +1,48 @@
1
+ @use "navigation" as *;
2
+ @use "main" as *;
3
+ @use "banner" as *;
4
+ @use "stack";
5
+
6
+ html,
7
+ body {
8
+ width: 100%;
9
+ height: 100%;
10
+ }
11
+
12
+ body {
13
+ display: grid;
14
+ grid-template-areas: "banner banner" "navigation main";
15
+ grid-template-rows: auto 1fr;
16
+ grid-template-columns: auto 3fr;
17
+ }
18
+
19
+ body > header {
20
+ grid-area: banner;
21
+ height: 2rem;
22
+ overflow-y: auto;
23
+ @extend %banner;
24
+ }
25
+
26
+ body > nav {
27
+ grid-area: navigation;
28
+ height: 100%;
29
+ overflow-y: auto;
30
+ width: 2rem;
31
+ transition: width 0.25s;
32
+ @extend %navigation;
33
+
34
+ &[aria-expanded] {
35
+ width: 17rem;
36
+ }
37
+ }
38
+
39
+ .navigation-collapse {
40
+ grid-area: navigation;
41
+ }
42
+
43
+ body > main {
44
+ grid-area: main;
45
+ height: 100%;
46
+ overflow: clip auto;
47
+ @extend %main;
48
+ }
@@ -0,0 +1,23 @@
1
+ @use "header" as * with (
2
+ $inset: 2rem
3
+ );
4
+ @use "content" as * with (
5
+ $inset: 2rem,
6
+ $space: 2rem
7
+ );
8
+
9
+ %main {
10
+ display: grid;
11
+ grid-template-areas: "header" "content";
12
+ grid-template-rows: auto 1fr;
13
+
14
+ > header {
15
+ grid-area: header;
16
+ @extend %header;
17
+ }
18
+
19
+ > .page {
20
+ grid-area: content;
21
+ @extend %content;
22
+ }
23
+ }
@@ -0,0 +1,156 @@
1
+ $heading: rgba(255, 255, 255, 0.6);
2
+ $separator: rgba(255, 255, 255, 0.2);
3
+
4
+ %subheading {
5
+ color: $heading;
6
+ font-size: 0.8rem;
7
+ text-transform: uppercase;
8
+ font-weight: 400;
9
+ margin: 0;
10
+ }
11
+
12
+ @mixin header {
13
+ display: grid;
14
+ grid-template-areas: "icon primary" "icon secondary";
15
+ grid-template-rows: 1fr 1fr;
16
+ grid-template-columns: 3rem 1fr;
17
+ padding: 1rem;
18
+
19
+ .site-name {
20
+ grid-area: primary;
21
+ padding-left: 1rem;
22
+ font-size: 1.25rem;
23
+ font-weight: unset;
24
+ color: var(--site-primary);
25
+ margin: 0;
26
+ white-space: nowrap;
27
+ overflow-x: hidden;
28
+ text-overflow: ellipsis;
29
+ }
30
+
31
+ .admin-name {
32
+ grid-area: secondary;
33
+ color: white;
34
+ padding-left: 1rem;
35
+ @extend %subheading;
36
+ opacity: 0.75;
37
+ white-space: nowrap;
38
+ overflow-x: hidden;
39
+ text-overflow: ellipsis;
40
+ }
41
+
42
+ .site-icon {
43
+ grid-area: icon;
44
+ display: flex;
45
+ background-color: var(--site-primary);
46
+ color: var(--site-on-primary);
47
+ width: 3rem;
48
+ height: 3rem;
49
+ align-items: center;
50
+ justify-content: space-around;
51
+ text-transform: capitalize;
52
+ font-size: 2rem;
53
+ border-radius: 0.25rem;
54
+ }
55
+ }
56
+
57
+ %navigation {
58
+ background: black;
59
+ color: white;
60
+ padding-bottom: 2rem;
61
+ --site-link: white;
62
+
63
+ > * {
64
+ transition: opacity 0.25s;
65
+ }
66
+
67
+ &:not([aria-expanded]) {
68
+ > * {
69
+ opacity: 0;
70
+ }
71
+ }
72
+
73
+ header {
74
+ @include header;
75
+ border-bottom: 1px solid $separator;
76
+ }
77
+
78
+ ul,
79
+ li {
80
+ padding: 0;
81
+ list-style: none;
82
+ font-size: var(--heading--h6);
83
+ }
84
+
85
+ .filter {
86
+ margin: 1rem calc(1rem - 5px) 0;
87
+
88
+ input {
89
+ width: 100%;
90
+ border: none;
91
+ border-radius: 1px;
92
+ }
93
+ }
94
+
95
+ li > span {
96
+ display: block;
97
+ padding: 1.25rem 1rem 0.25rem;
98
+ color: $heading;
99
+ }
100
+
101
+ > ul > li > span {
102
+ border-top: 1px solid $separator;
103
+ margin-top: 1rem;
104
+ padding: 1.25rem 1rem 0.25rem;
105
+ @extend %subheading;
106
+ }
107
+
108
+ li li li > a[href] {
109
+ padding-left: 2.5rem;
110
+ }
111
+
112
+ li > a[href] {
113
+ display: block;
114
+ padding: 0.75rem 1rem;
115
+ white-space: nowrap;
116
+ overflow: hidden;
117
+ text-overflow: ellipsis;
118
+ }
119
+
120
+ li > a[href]:hover,
121
+ li > a[href]:focus {
122
+ background: rgba(255, 255, 255, 0.2);
123
+ text-decoration: none;
124
+ }
125
+ }
126
+
127
+ .navigation-collapse {
128
+ width: 2rem;
129
+ border-radius: 50%;
130
+ margin-top: 1rem;
131
+ justify-self: right;
132
+ align-self: start;
133
+ background-color: white;
134
+ box-shadow: 0px 1px 7px 2px rgba(0, 0, 0, 0.1);
135
+ visibility: hidden;
136
+ opacity: 0;
137
+ transform: translateX(50%);
138
+ transition:
139
+ opacity 0.25s,
140
+ background-color 0.25s;
141
+ cursor: pointer;
142
+
143
+ nav[aria-expanded] + & {
144
+ transform: translateX(50%) rotate(180deg);
145
+ }
146
+
147
+ nav:hover + &,
148
+ &:hover {
149
+ visibility: visible;
150
+ opacity: 1;
151
+ }
152
+
153
+ &:hover {
154
+ background-color: var(--site-primary);
155
+ }
156
+ }
@@ -0,0 +1,13 @@
1
+ .stack {
2
+ display: flex;
3
+ flex-direction: column;
4
+ justify-content: flex-start;
5
+ }
6
+
7
+ .stack > * {
8
+ margin-block: 0;
9
+ }
10
+
11
+ .stack > * + * {
12
+ margin-block-start: var(--space, 1.5rem);
13
+ }
@@ -0,0 +1 @@
1
+ @use "login";
@@ -0,0 +1,40 @@
1
+ @use "../layouts/navigation" as nav;
2
+
3
+ .admin-login {
4
+ display: flex;
5
+ flex-direction: row;
6
+ align-items: center;
7
+ justify-content: center;
8
+ background: var(--site-primary);
9
+ }
10
+
11
+ .admin-login > main {
12
+ flex: 1 1 auto;
13
+ max-width: 20rem;
14
+ background: white;
15
+ border-radius: 0.25rem;
16
+ height: unset;
17
+ }
18
+
19
+ .admin-login header {
20
+ @include nav.header;
21
+ width: 100%;
22
+
23
+ .admin-name {
24
+ color: #555;
25
+ }
26
+ }
27
+
28
+ .admin-login form {
29
+ padding: 0 1rem 1rem;
30
+ }
31
+
32
+ .admin-login .actions-group {
33
+ display: flex;
34
+ flex-direction: row;
35
+ justify-content: stretch;
36
+ }
37
+
38
+ .admin-login .button--primary {
39
+ flex: 1;
40
+ }