fuji_admin 0.1.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 (61) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +143 -0
  4. data/app/assets/javascripts/fuji_admin/base.js +6 -0
  5. data/app/assets/javascripts/fuji_admin/filters.js +282 -0
  6. data/app/assets/javascripts/fuji_admin/floats.js +73 -0
  7. data/app/assets/javascripts/fuji_admin/menu.js +112 -0
  8. data/app/assets/javascripts/fuji_admin/palettes.js +237 -0
  9. data/app/assets/javascripts/fuji_admin/row_actions.js +123 -0
  10. data/app/assets/stylesheets/fuji_admin/_base.scss +23 -0
  11. data/app/assets/stylesheets/fuji_admin/_base_typography.scss +56 -0
  12. data/app/assets/stylesheets/fuji_admin/_grid.scss +111 -0
  13. data/app/assets/stylesheets/fuji_admin/_reset.scss +48 -0
  14. data/app/assets/stylesheets/fuji_admin/components/_buttons.scss +106 -0
  15. data/app/assets/stylesheets/fuji_admin/components/_comments.scss +44 -0
  16. data/app/assets/stylesheets/fuji_admin/components/_components.scss +22 -0
  17. data/app/assets/stylesheets/fuji_admin/components/_date_picker.scss +147 -0
  18. data/app/assets/stylesheets/fuji_admin/components/_dropdown_menu.scss +76 -0
  19. data/app/assets/stylesheets/fuji_admin/components/_filter_chips.scss +71 -0
  20. data/app/assets/stylesheets/fuji_admin/components/_filter_drawer.scss +224 -0
  21. data/app/assets/stylesheets/fuji_admin/components/_filter_form.scss +85 -0
  22. data/app/assets/stylesheets/fuji_admin/components/_flash.scss +55 -0
  23. data/app/assets/stylesheets/fuji_admin/components/_float_labels.scss +77 -0
  24. data/app/assets/stylesheets/fuji_admin/components/_inputs.scss +237 -0
  25. data/app/assets/stylesheets/fuji_admin/components/_menu_toggle.scss +61 -0
  26. data/app/assets/stylesheets/fuji_admin/components/_pagination.scss +70 -0
  27. data/app/assets/stylesheets/fuji_admin/components/_palette_switcher.scss +600 -0
  28. data/app/assets/stylesheets/fuji_admin/components/_panel.scss +44 -0
  29. data/app/assets/stylesheets/fuji_admin/components/_row_actions.scss +110 -0
  30. data/app/assets/stylesheets/fuji_admin/components/_scopes.scss +58 -0
  31. data/app/assets/stylesheets/fuji_admin/components/_select2.scss +194 -0
  32. data/app/assets/stylesheets/fuji_admin/components/_status_tag.scss +59 -0
  33. data/app/assets/stylesheets/fuji_admin/components/_table_tools.scss +14 -0
  34. data/app/assets/stylesheets/fuji_admin/components/_tables.scss +262 -0
  35. data/app/assets/stylesheets/fuji_admin/components/_watchlist_bar.scss +119 -0
  36. data/app/assets/stylesheets/fuji_admin/layouts/_footer.scss +21 -0
  37. data/app/assets/stylesheets/fuji_admin/layouts/_header.scss +80 -0
  38. data/app/assets/stylesheets/fuji_admin/layouts/_layouts.scss +7 -0
  39. data/app/assets/stylesheets/fuji_admin/layouts/_main_content.scss +118 -0
  40. data/app/assets/stylesheets/fuji_admin/layouts/_sidebar.scss +124 -0
  41. data/app/assets/stylesheets/fuji_admin/layouts/_sizes.scss +12 -0
  42. data/app/assets/stylesheets/fuji_admin/layouts/_wrapper.scss +28 -0
  43. data/app/assets/stylesheets/fuji_admin/mixins/_media.scss +30 -0
  44. data/app/assets/stylesheets/fuji_admin/mixins/_mixins.scss +2 -0
  45. data/app/assets/stylesheets/fuji_admin/pages/_form.scss +61 -0
  46. data/app/assets/stylesheets/fuji_admin/pages/_index.scss +77 -0
  47. data/app/assets/stylesheets/fuji_admin/pages/_login.scss +77 -0
  48. data/app/assets/stylesheets/fuji_admin/pages/_pages.scss +5 -0
  49. data/app/assets/stylesheets/fuji_admin/pages/_show.scss +19 -0
  50. data/app/assets/stylesheets/fuji_admin/variables/_breakpoints.scss +25 -0
  51. data/app/assets/stylesheets/fuji_admin/variables/_colors.scss +51 -0
  52. data/app/assets/stylesheets/fuji_admin/variables/_radii.scss +13 -0
  53. data/app/assets/stylesheets/fuji_admin/variables/_shadows.scss +10 -0
  54. data/app/assets/stylesheets/fuji_admin/variables/_spacing.scss +20 -0
  55. data/app/assets/stylesheets/fuji_admin/variables/_typography.scss +25 -0
  56. data/app/assets/stylesheets/fuji_admin/variables/_variables.scss +9 -0
  57. data/lib/fuji_admin/active_admin_patch.rb +19 -0
  58. data/lib/fuji_admin/configuration.rb +29 -0
  59. data/lib/fuji_admin/version.rb +3 -0
  60. data/lib/fuji_admin.rb +24 -0
  61. metadata +124 -0
@@ -0,0 +1,110 @@
1
+ // Row-action dropdown — injected by fuji_admin/row_actions.js into
2
+ // `table.index_table td.col-actions` when the row has 2+ actions. Keeps the
3
+ // column compact regardless of action count.
4
+
5
+ .fuji-row-actions {
6
+ position: relative;
7
+ display: inline-flex;
8
+
9
+ &__trigger {
10
+ display: inline-flex;
11
+ align-items: center;
12
+ justify-content: center;
13
+ width: 28px;
14
+ height: 28px;
15
+ padding: 0;
16
+ background: transparent;
17
+ border: 1px solid $surface-border;
18
+ border-radius: $radius-sm;
19
+ color: $text-color-secondary;
20
+ cursor: pointer;
21
+ transition:
22
+ background-color 0.15s ease,
23
+ color 0.15s ease,
24
+ border-color 0.15s ease;
25
+
26
+ &:hover {
27
+ background-color: $primary-50;
28
+ color: $primary-700;
29
+ border-color: $primary-200;
30
+ }
31
+
32
+ &:focus-visible {
33
+ outline: none;
34
+ box-shadow: $shadow-focus;
35
+ }
36
+
37
+ &[aria-expanded="true"] {
38
+ background-color: $primary-50;
39
+ color: $primary-700;
40
+ border-color: $primary-200;
41
+ }
42
+
43
+ svg {
44
+ display: block;
45
+ }
46
+ }
47
+
48
+ &__menu {
49
+ // Fixed positioning — anchored to the trigger by JS using getBoundingClientRect.
50
+ // Sits above the filter drawer (z:30) and header (z:20); `.paginated_collection`
51
+ // has `overflow: hidden` so absolute positioning would clip us. Fixed sidesteps
52
+ // that entirely.
53
+ position: fixed;
54
+ min-width: 180px;
55
+ padding: $space-1;
56
+ background-color: $surface-0;
57
+ border: 1px solid $surface-border;
58
+ border-radius: $border-radius;
59
+ box-shadow: $shadow-md;
60
+ z-index: 50;
61
+ text-align: left;
62
+ white-space: normal;
63
+
64
+ &[hidden] {
65
+ display: none;
66
+ }
67
+ }
68
+
69
+ // Links in the menu — override the outlined-pill fallback styles so they
70
+ // render as menu rows instead.
71
+ .col-actions &__item,
72
+ &__item {
73
+ display: block;
74
+ width: 100%;
75
+ padding: $space-2 $space-3;
76
+ margin: 0;
77
+ border: 0;
78
+ border-radius: $radius-sm;
79
+ background-color: transparent;
80
+ color: $text-color;
81
+ font-size: $font-size-sm;
82
+ font-weight: $font-weight-regular;
83
+ line-height: 1.4;
84
+ text-align: left;
85
+ text-decoration: none;
86
+ white-space: nowrap;
87
+ transition: background-color 0.15s ease, color 0.15s ease;
88
+
89
+ &:hover {
90
+ background-color: $surface-100;
91
+ color: $primary-700;
92
+ border: 0;
93
+ text-decoration: none;
94
+ }
95
+
96
+ &:focus-visible {
97
+ outline: none;
98
+ background-color: $surface-100;
99
+ }
100
+
101
+ &--danger {
102
+ color: $color-danger;
103
+
104
+ &:hover {
105
+ background-color: rgba($color-danger, 0.08);
106
+ color: $color-danger;
107
+ }
108
+ }
109
+ }
110
+ }
@@ -0,0 +1,58 @@
1
+ // Scope bar — index-page tabs like "All · Active · Archived".
2
+ // AA emits as `ul.scopes > li > a` or `div.scopes > ul > li > a`, with
3
+ // `li.selected` for the current one.
4
+
5
+ .scopes,
6
+ .table_tools .scopes {
7
+ display: inline-flex;
8
+ flex-wrap: wrap;
9
+ gap: 0;
10
+ padding: $space-1;
11
+ background-color: $surface-100;
12
+ border-radius: $border-radius;
13
+ list-style: none;
14
+ margin: 0;
15
+
16
+ > ul {
17
+ display: inline-flex;
18
+ list-style: none;
19
+ padding: 0;
20
+ margin: 0;
21
+ }
22
+
23
+ li {
24
+ display: inline-flex;
25
+
26
+ a {
27
+ display: inline-flex;
28
+ align-items: center;
29
+ padding: $space-1 $space-4;
30
+ font-size: $font-size-sm;
31
+ font-weight: $font-weight-medium;
32
+ color: $text-color-secondary;
33
+ border-radius: $radius-sm;
34
+ transition: background-color 0.15s ease, color 0.15s ease;
35
+
36
+ &:hover {
37
+ color: $text-color;
38
+ text-decoration: none;
39
+ }
40
+
41
+ .count {
42
+ margin-left: $space-2;
43
+ font-size: $font-size-xs;
44
+ color: $text-color-muted;
45
+ }
46
+ }
47
+
48
+ &.selected a {
49
+ background-color: $surface-0;
50
+ color: $text-color;
51
+ box-shadow: $shadow-xs;
52
+
53
+ .count {
54
+ color: $primary-color;
55
+ }
56
+ }
57
+ }
58
+ }
@@ -0,0 +1,194 @@
1
+ // Select2 — AA uses this for relation selectors and enum dropdowns. The
2
+ // library renders its own DOM; we match the native input aesthetic so
3
+ // single/multi selects feel like first-class members of a form.
4
+
5
+ .select2-container--default {
6
+ width: 100% !important;
7
+
8
+ // --- Trigger (closed state) -------------------------------------------
9
+ .select2-selection--single,
10
+ .select2-selection--multiple {
11
+ min-height: 38px;
12
+ padding: 0;
13
+ background-color: $surface-0;
14
+ border: 1px solid $surface-border;
15
+ border-radius: $border-radius;
16
+ transition: border-color 0.15s ease, box-shadow 0.15s ease;
17
+
18
+ &:hover {
19
+ border-color: $surface-400;
20
+ }
21
+ }
22
+
23
+ &.select2-container--focus .select2-selection--single,
24
+ &.select2-container--focus .select2-selection--multiple,
25
+ &.select2-container--open .select2-selection--single,
26
+ &.select2-container--open .select2-selection--multiple {
27
+ border-color: $primary-color;
28
+ box-shadow: $shadow-focus;
29
+ }
30
+
31
+ // Single
32
+ .select2-selection--single {
33
+ display: flex;
34
+ align-items: center;
35
+
36
+ .select2-selection__rendered {
37
+ padding: 0 $space-7 0 $space-3;
38
+ font-size: $font-size-sm;
39
+ color: $text-color;
40
+ line-height: 1.5;
41
+ }
42
+
43
+ .select2-selection__placeholder {
44
+ color: $text-color-muted;
45
+ }
46
+
47
+ .select2-selection__arrow {
48
+ top: 50%;
49
+ right: $space-3;
50
+ height: 0;
51
+ width: 0;
52
+ transform: translateY(-50%);
53
+ background: none;
54
+ border: 0;
55
+
56
+ b {
57
+ margin: 0;
58
+ border: 0;
59
+ width: 0;
60
+ height: 0;
61
+ border-left: 4px solid transparent;
62
+ border-right: 4px solid transparent;
63
+ border-top: 5px solid $text-color-secondary;
64
+ }
65
+ }
66
+
67
+ .select2-selection__clear {
68
+ margin-right: $space-2;
69
+ color: $text-color-muted;
70
+ font-weight: $font-weight-regular;
71
+
72
+ &:hover {
73
+ color: $color-danger;
74
+ }
75
+ }
76
+ }
77
+
78
+ // Multi
79
+ .select2-selection--multiple {
80
+ padding: $space-1 $space-2;
81
+
82
+ .select2-selection__rendered {
83
+ padding: 0;
84
+ display: flex;
85
+ flex-wrap: wrap;
86
+ gap: $space-1;
87
+ }
88
+
89
+ .select2-selection__choice {
90
+ display: inline-flex;
91
+ align-items: center;
92
+ gap: $space-1;
93
+ margin: 2px 0;
94
+ padding: 2px $space-2;
95
+ background-color: $primary-50;
96
+ border: 1px solid $primary-100;
97
+ border-radius: $radius-sm;
98
+ color: $primary-700;
99
+ font-size: $font-size-xs;
100
+ font-weight: $font-weight-medium;
101
+ }
102
+
103
+ .select2-selection__choice__remove {
104
+ order: 2;
105
+ margin: 0 0 0 $space-1;
106
+ color: $primary-700;
107
+ cursor: pointer;
108
+ font-weight: $font-weight-bold;
109
+
110
+ &:hover {
111
+ color: $color-danger;
112
+ }
113
+ }
114
+
115
+ .select2-search--inline .select2-search__field {
116
+ margin: 0;
117
+ padding: 2px 0;
118
+ font-family: inherit;
119
+ font-size: $font-size-sm;
120
+ color: $text-color;
121
+ }
122
+ }
123
+ }
124
+
125
+ // --- Dropdown (open state) ----------------------------------------------
126
+ // Select2 appends .select2-container--open / .select2-dropdown to <body>,
127
+ // so these selectors are global.
128
+ .select2-dropdown {
129
+ background-color: $surface-0;
130
+ border: 1px solid $surface-border;
131
+ border-radius: $border-radius;
132
+ box-shadow: $shadow-md;
133
+ overflow: hidden;
134
+
135
+ .select2-search--dropdown {
136
+ padding: $space-2;
137
+
138
+ .select2-search__field {
139
+ @include fuji-input-base;
140
+ font-size: $font-size-sm;
141
+ }
142
+ }
143
+
144
+ .select2-results__options {
145
+ max-height: 280px;
146
+ padding: $space-1;
147
+ overflow-y: auto;
148
+ }
149
+
150
+ .select2-results__option {
151
+ padding: $space-2 $space-3;
152
+ font-size: $font-size-sm;
153
+ color: $text-color;
154
+ border-radius: $radius-sm;
155
+ cursor: pointer;
156
+
157
+ &[aria-selected="true"] {
158
+ background-color: $primary-50;
159
+ color: $primary-700;
160
+ font-weight: $font-weight-medium;
161
+ }
162
+
163
+ &--highlighted {
164
+ background-color: $surface-100;
165
+ color: $text-color;
166
+
167
+ &[aria-selected="true"] {
168
+ background-color: $primary-100;
169
+ color: $primary-700;
170
+ }
171
+ }
172
+
173
+ &[aria-disabled="true"] {
174
+ color: $text-color-muted;
175
+ cursor: not-allowed;
176
+ }
177
+ }
178
+
179
+ .select2-results__group {
180
+ padding: $space-2 $space-3;
181
+ font-size: $font-size-xs;
182
+ font-weight: $font-weight-semibold;
183
+ color: $text-color-muted;
184
+ text-transform: uppercase;
185
+ letter-spacing: 0.04em;
186
+ }
187
+
188
+ .select2-results__message {
189
+ padding: $space-3;
190
+ font-size: $font-size-sm;
191
+ color: $text-color-muted;
192
+ text-align: center;
193
+ }
194
+ }
@@ -0,0 +1,59 @@
1
+ // Status tags — AA renders as `<span class="status_tag <value>">` where
2
+ // `<value>` is the dasherised string (e.g. "yes", "published", "draft").
3
+ // We style a neutral default plus conventional semantic variants.
4
+
5
+ .status_tag {
6
+ display: inline-flex;
7
+ align-items: center;
8
+ padding: $space-1 $space-3;
9
+ font-size: $font-size-xs;
10
+ font-weight: $font-weight-medium;
11
+ letter-spacing: 0.02em;
12
+ text-transform: uppercase;
13
+ border-radius: $radius-full;
14
+ background-color: $surface-200;
15
+ color: $text-color-secondary;
16
+ line-height: 1.4;
17
+ white-space: nowrap;
18
+
19
+ // Semantic variants (AA dasherises the value into the class name).
20
+ &.yes,
21
+ &.ok,
22
+ &.active,
23
+ &.published,
24
+ &.completed,
25
+ &.approved,
26
+ &.success {
27
+ background-color: rgba($color-success, 0.12);
28
+ color: darken($color-success, 8%);
29
+ }
30
+
31
+ &.no,
32
+ &.error,
33
+ &.failed,
34
+ &.rejected,
35
+ &.danger {
36
+ background-color: rgba($color-danger, 0.12);
37
+ color: $color-danger;
38
+ }
39
+
40
+ &.warning,
41
+ &.pending,
42
+ &.draft {
43
+ background-color: rgba($color-warning, 0.14);
44
+ color: darken($color-warning, 10%);
45
+ }
46
+
47
+ &.info,
48
+ &.processing,
49
+ &.running {
50
+ background-color: rgba($color-info, 0.12);
51
+ color: darken($color-info, 8%);
52
+ }
53
+
54
+ &.empty {
55
+ background-color: transparent;
56
+ color: $text-color-muted;
57
+ font-style: italic;
58
+ }
59
+ }
@@ -0,0 +1,14 @@
1
+ // Table tools — the toolbar above index_table containing scopes, batch
2
+ // actions selector, and download formats dropdown.
3
+
4
+ .table_tools {
5
+ display: flex;
6
+ flex-wrap: wrap;
7
+ gap: $space-3;
8
+ align-items: center;
9
+ padding: 0 0 $space-4;
10
+
11
+ > * {
12
+ display: inline-flex;
13
+ }
14
+ }
@@ -0,0 +1,262 @@
1
+ // Tables — primarily `table.index_table` (AA resource index pages) and
2
+ // `.attributes_table` (AA resource show pages).
3
+
4
+ table.index_table {
5
+ width: 100%;
6
+ background-color: $surface-0;
7
+ border-collapse: separate;
8
+ border-spacing: 0;
9
+ border: 1px solid $surface-border;
10
+ border-radius: $border-radius-card;
11
+ overflow: hidden;
12
+ font-size: $font-size-sm;
13
+
14
+ thead {
15
+ background-color: $surface-50;
16
+
17
+ th {
18
+ padding: $space-3 $space-4;
19
+ font-weight: $font-weight-semibold;
20
+ color: $text-color-secondary;
21
+ text-align: left;
22
+ border-bottom: 1px solid $surface-200;
23
+ white-space: nowrap;
24
+
25
+ a {
26
+ color: inherit;
27
+
28
+ &:hover {
29
+ color: $primary-color;
30
+ text-decoration: none;
31
+ }
32
+ }
33
+
34
+ &.sortable.asc a::after,
35
+ &.sorted-asc a::after {
36
+ content: " ↑";
37
+ color: $primary-color;
38
+ }
39
+
40
+ &.sortable.desc a::after,
41
+ &.sorted-desc a::after {
42
+ content: " ↓";
43
+ color: $primary-color;
44
+ }
45
+
46
+ // Bulk-select column
47
+ &.col-selectable {
48
+ width: 32px;
49
+ padding-right: 0;
50
+ }
51
+ }
52
+ }
53
+
54
+ tbody {
55
+ tr {
56
+ transition: background-color 0.15s ease;
57
+
58
+ &:hover {
59
+ background-color: $surface-50;
60
+ }
61
+
62
+ &.selected {
63
+ background-color: $primary-50;
64
+ }
65
+
66
+ td {
67
+ padding: $space-3 $space-4;
68
+ border-bottom: 1px solid $surface-200;
69
+ color: $text-color;
70
+ vertical-align: middle;
71
+ }
72
+
73
+ &:last-child td {
74
+ border-bottom: 0;
75
+ }
76
+ }
77
+ }
78
+
79
+ // Per-row actions column — the dropdown trigger and menu live in
80
+ // components/_row_actions.scss. When JS hasn't wrapped the actions (no-JS
81
+ // fallback, or only one action), links inside `.table_actions` still get
82
+ // a small outlined-button style so the cell doesn't look broken.
83
+ .col-actions,
84
+ td.col-actions {
85
+ text-align: right;
86
+ white-space: nowrap;
87
+ width: 1%; // shrink-to-fit so the actions column doesn't steal width
88
+
89
+ .table_actions {
90
+ display: inline-flex;
91
+ gap: $space-1;
92
+ align-items: center;
93
+ justify-content: flex-end;
94
+ flex-wrap: wrap;
95
+
96
+ > a {
97
+ display: inline-flex;
98
+ align-items: center;
99
+ padding: $space-1 $space-2;
100
+ margin: 0;
101
+ font-size: $font-size-xs;
102
+ font-weight: $font-weight-medium;
103
+ line-height: 1.4;
104
+ color: $text-color-secondary;
105
+ background-color: transparent;
106
+ border: 1px solid $surface-border;
107
+ border-radius: $radius-sm;
108
+ text-decoration: none;
109
+ transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease;
110
+
111
+ &:hover {
112
+ background-color: $primary-50;
113
+ color: $primary-700;
114
+ border-color: $primary-200;
115
+ text-decoration: none;
116
+ }
117
+
118
+ &:focus-visible {
119
+ outline: none;
120
+ box-shadow: $shadow-focus;
121
+ }
122
+
123
+ &.delete_link:hover {
124
+ background-color: rgba($color-danger, 0.08);
125
+ color: $color-danger;
126
+ border-color: rgba($color-danger, 0.3);
127
+ }
128
+ }
129
+ }
130
+ }
131
+ }
132
+
133
+ // Show-page attributes table — two-column label/value rows.
134
+ .attributes_table {
135
+ table {
136
+ width: 100%;
137
+ font-size: $font-size-sm;
138
+ border-collapse: separate;
139
+ border-spacing: 0;
140
+
141
+ th,
142
+ td {
143
+ padding: $space-3 $space-4;
144
+ border-bottom: 1px solid $surface-200;
145
+ vertical-align: top;
146
+ }
147
+
148
+ th {
149
+ width: 30%;
150
+ min-width: 180px;
151
+ font-size: $font-size-xs;
152
+ font-weight: $font-weight-semibold;
153
+ text-transform: uppercase;
154
+ letter-spacing: 0.04em;
155
+ color: $text-color-muted;
156
+ text-align: left;
157
+ padding-right: $space-5;
158
+ }
159
+
160
+ td {
161
+ color: $text-color;
162
+ word-break: break-word;
163
+ }
164
+
165
+ tr:hover {
166
+ background-color: $surface-50;
167
+ }
168
+
169
+ tr:last-child th,
170
+ tr:last-child td {
171
+ border-bottom: 0;
172
+ }
173
+ }
174
+ }
175
+
176
+ // Generic `table_for` / bare `<table>` output. Matches:
177
+ // * direct children of #main_content (custom index pages like /admin/favorites)
178
+ // * tables inside `.panel .panel_contents` (show-page entities, related docs)
179
+ // * tables with a `.summary-table` class (reporting dashboards)
180
+ //
181
+ // Using `> table` in the first selector avoids matching `.attributes_table`'s
182
+ // inner table (which lives deeper than a direct child of #main_content).
183
+ #main_content > table:not(.index_table):not(.ui-datepicker-calendar),
184
+ #main_content table.summary-table,
185
+ .panel .panel_contents table:not(.index_table):not(.ui-datepicker-calendar) {
186
+ width: 100%;
187
+ background-color: $surface-0;
188
+ border: 1px solid $surface-border;
189
+ border-radius: $border-radius;
190
+ border-collapse: separate;
191
+ border-spacing: 0;
192
+ overflow: hidden;
193
+ font-size: $font-size-sm;
194
+ margin-bottom: $space-3;
195
+
196
+ thead {
197
+ background-color: $surface-50;
198
+
199
+ th {
200
+ padding: $space-2 $space-3;
201
+ font-size: $font-size-xs;
202
+ font-weight: $font-weight-semibold;
203
+ text-transform: uppercase;
204
+ letter-spacing: 0.04em;
205
+ color: $text-color-muted;
206
+ text-align: left;
207
+ border-bottom: 1px solid $surface-200;
208
+ white-space: nowrap;
209
+ }
210
+ }
211
+
212
+ tbody {
213
+ tr {
214
+ transition: background-color 0.15s ease;
215
+
216
+ &:hover {
217
+ background-color: $surface-50;
218
+ }
219
+
220
+ &.odd {
221
+ background-color: transparent; // we handle alternation via hover
222
+ }
223
+
224
+ td {
225
+ padding: $space-2 $space-3;
226
+ border-bottom: 1px solid $surface-200;
227
+ color: $text-color;
228
+ vertical-align: top;
229
+ }
230
+
231
+ &:last-child td {
232
+ border-bottom: 0;
233
+ }
234
+ }
235
+ }
236
+ }
237
+
238
+ // AA's `columns` helper — two (or more) tables laid out side-by-side inside
239
+ // a single panel. Stack below md.
240
+ .columns {
241
+ display: flex;
242
+ flex-direction: column;
243
+ gap: $space-4;
244
+
245
+ @include media-up(md) {
246
+ flex-direction: row;
247
+ gap: $space-5;
248
+ }
249
+
250
+ > .column {
251
+ flex: 1 1 0;
252
+ min-width: 0; // allow shrink for wide columns
253
+ }
254
+ }
255
+
256
+ // Empty state inside table panels
257
+ .blank_slate_container {
258
+ padding: $space-9 $space-5;
259
+ text-align: center;
260
+ color: $text-color-muted;
261
+ font-size: $font-size-sm;
262
+ }