active_admin_prism 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 (43) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +133 -0
  3. data/INTEGRATION.md +1012 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +319 -0
  6. data/app/assets/javascripts/active_admin_prism/prism.js +1 -0
  7. data/app/assets/stylesheets/active_admin_prism/_variable_overrides.scss +43 -0
  8. data/app/assets/stylesheets/active_admin_prism/prism.css +1 -0
  9. data/app/views/active_admin/devise/confirmations/new.html.erb +26 -0
  10. data/app/views/active_admin/devise/passwords/edit.html.erb +29 -0
  11. data/app/views/active_admin/devise/passwords/new.html.erb +26 -0
  12. data/app/views/active_admin/devise/registrations/new.html.erb +32 -0
  13. data/app/views/active_admin/devise/sessions/new.html.erb +37 -0
  14. data/app/views/active_admin/devise/shared/_brand.html.erb +34 -0
  15. data/app/views/active_admin/devise/unlocks/new.html.erb +25 -0
  16. data/js-src/prism.js +270 -0
  17. data/lib/active_admin/views/filters_sidebar.rb +69 -0
  18. data/lib/active_admin/views/flash_messages.rb +77 -0
  19. data/lib/active_admin/views/index_table_actions.rb +46 -0
  20. data/lib/active_admin/views/prism_sidebar.rb +212 -0
  21. data/lib/active_admin_prism/configuration.rb +180 -0
  22. data/lib/active_admin_prism/engine.rb +18 -0
  23. data/lib/active_admin_prism/version.rb +5 -0
  24. data/lib/active_admin_prism.rb +33 -0
  25. data/lib/formtastic/inputs/prism_toggle_input.rb +31 -0
  26. data/lib/generators/active_admin_prism/error_pages/error_pages_generator.rb +49 -0
  27. data/lib/generators/active_admin_prism/error_pages/templates/404.html +164 -0
  28. data/lib/generators/active_admin_prism/error_pages/templates/500.html +173 -0
  29. data/lib/generators/active_admin_prism/install/install_generator.rb +69 -0
  30. data/lib/prism_icons.rb +82 -0
  31. data/lib/prism_login_helper.rb +31 -0
  32. data/lib/prism_toggle_tag.rb +35 -0
  33. data/scss-src/_base.scss +281 -0
  34. data/scss-src/_buttons.scss +134 -0
  35. data/scss-src/_forms.scss +272 -0
  36. data/scss-src/_icons.scss +27 -0
  37. data/scss-src/_login.scss +306 -0
  38. data/scss-src/_panels.scss +249 -0
  39. data/scss-src/_sidebar.scss +479 -0
  40. data/scss-src/_tables.scss +142 -0
  41. data/scss-src/_variables.scss +101 -0
  42. data/scss-src/prism.scss +15 -0
  43. metadata +157 -0
data/INTEGRATION.md ADDED
@@ -0,0 +1,1012 @@
1
+ # Prism integration guide
2
+
3
+ A complete walkthrough of installing, configuring, and — if you ever need
4
+ to — switching off `active_admin_prism` in a Rails app. For a quick
5
+ start, see [README.md](README.md); this document goes deeper on every
6
+ option and the reasoning behind it.
7
+
8
+ ## Contents
9
+
10
+ - [Requirements](#requirements)
11
+ - [Install](#install)
12
+ - [Manual install](#manual-install)
13
+ - [Verifying the install](#verifying-the-install)
14
+ - [Configuration reference](#configuration-reference)
15
+ - [Plug-and-play: switching the theme on/off](#plug-and-play-switching-the-theme-onoff)
16
+ - [Feature guide](#feature-guide)
17
+ - [Error pages (404/500)](#error-pages-404500)
18
+ - [Markup & CSS class reference](#markup--css-class-reference)
19
+ - [Customizing colors](#customizing-colors)
20
+ - [Troubleshooting](#troubleshooting)
21
+ - [Upgrading](#upgrading)
22
+ - [Uninstalling](#uninstalling)
23
+
24
+ ## Requirements
25
+
26
+ - ActiveAdmin `>= 3.0, < 4`
27
+ - Rails `>= 7.0`
28
+ - Ruby `>= 3.1`
29
+ - Sprockets or Propshaft asset pipeline (the theme ships precompiled CSS/JS;
30
+ no Sass compiler or JS bundler is required to install it). If your app
31
+ has its own `app/assets/stylesheets/active_admin.scss` (i.e. you ran
32
+ `rails g active_admin:assets`, the Sprockets/Sass path — not
33
+ Webpacker/importmap), you get one extra, automatic benefit: see
34
+ [Customizing colors](#customizing-colors).
35
+
36
+ ## Install
37
+
38
+ ```ruby
39
+ # Gemfile
40
+ gem "active_admin_prism"
41
+ ```
42
+
43
+ ```sh
44
+ bundle install
45
+ rails g active_admin_prism:install
46
+ ```
47
+
48
+ The generator makes two changes, both idempotent (safe to run more than
49
+ once):
50
+
51
+ 1. Appends `ActiveAdminPrism.enable!` to
52
+ `config/initializers/active_admin.rb`. This is what swaps ActiveAdmin's
53
+ top nav bar for the Prism sidebar (unless you've disabled that — see
54
+ [Configuration reference](#configuration-reference)).
55
+ 2. If `app/assets/stylesheets/active_admin.scss` exists, injects
56
+ `@import "active_admin_prism/variable_overrides";` immediately
57
+ before `@import "active_admin/mixins";`. This brings ActiveAdmin's own
58
+ default gray palette onto Prism's colors at the source — see
59
+ [Customizing colors](#customizing-colors) for why this matters.
60
+
61
+ Nothing else is required. The theme's CSS/JS are registered automatically
62
+ by the gem's `Rails::Engine` — there's no manifest to edit and no build
63
+ step, regardless of whether your app uses Sprockets or Propshaft.
64
+
65
+ ## Manual install
66
+
67
+ If you'd rather not run the generator (or it skipped step 2 because you
68
+ don't have an `active_admin.scss` yet):
69
+
70
+ ```ruby
71
+ # config/initializers/active_admin.rb
72
+ ActiveAdmin.setup do |config|
73
+ # ... your existing config ...
74
+ end
75
+
76
+ ActiveAdminPrism.enable!
77
+ ```
78
+
79
+ ```scss
80
+ // app/assets/stylesheets/active_admin.scss (only if this file exists)
81
+ @import "active_admin_prism/variable_overrides";
82
+ @import "active_admin/mixins";
83
+ @import "active_admin/base";
84
+ ```
85
+
86
+ ## Verifying the install
87
+
88
+ 1. Start your server and sign in to `/admin`.
89
+ 2. You should see a left sidebar (brand/logo, "PAGES" section, your
90
+ resources) instead of ActiveAdmin's default top bar.
91
+ 3. View source (or DevTools → Network) and confirm two stylesheets load in
92
+ this order: `active_admin.css` (or `.scss` output) **then**
93
+ `active_admin_prism/prism.css`. Order matters — Prism's CSS must
94
+ load second to correctly override ActiveAdmin's own styles.
95
+ 4. Click a row's Delete icon — you should see a purple/white styled confirm
96
+ dialog, not your browser's native popup.
97
+
98
+ If any of this doesn't match, see [Troubleshooting](#troubleshooting) before
99
+ assuming something's broken — asset caching is the most common cause.
100
+
101
+ ## Configuration reference
102
+
103
+ `ActiveAdminPrism::Configuration` holds one flag per independently
104
+ toggleable feature. Every flag defaults to fully on; an app that never
105
+ touches this is unaffected.
106
+
107
+ ```ruby
108
+ # config/initializers/active_admin.rb, anywhere before `enable!`
109
+ ActiveAdminPrism.configure do |config|
110
+ config.sidebar = true # default: true
111
+ config.colorize_action_icons = true # default: true
112
+ config.styled_confirms = true # default: true
113
+ config.collapsible_filters = true # default: true
114
+ config.sidebar_footer = true # default: true
115
+ config.flash_dismissible = true # default: true
116
+ config.flash_auto_dismiss = true # default: true
117
+ config.flash_auto_dismiss_seconds = 5 # default: 5
118
+ config.flash_transition_ms = 320 # default: 320
119
+ config.login_page = true # default: true
120
+ config.login_logo = nil # default: nil (Prism's built-in mark)
121
+ config.login_app_name = nil # default: nil (falls back to AA's site_title)
122
+ config.login_tagline = "Sign in to your admin dashboard" # default shown
123
+ config.language_switcher = true # default: true
124
+ config.languages = [ # default: these 3
125
+ { label: "English", locale: :en },
126
+ { label: "Español", locale: :es },
127
+ { label: "Français", locale: :fr }
128
+ ]
129
+ end
130
+
131
+ ActiveAdminPrism.enable!
132
+ ```
133
+
134
+ | Option | Default | Effect when `false` (or, for the last two, when changed) |
135
+ | -------------------------------- | ------- | ---------------------------------------------------------------- |
136
+ | `sidebar` | `true` | `enable!` no-ops the header swap; ActiveAdmin's stock top nav renders instead. Everything else (panels, tables, forms, buttons, icons, flash/confirm styling) is unaffected. |
137
+ | `colorize_action_icons` | `true` | Index-table View/Edit/Delete render as ActiveAdmin's plain text links instead of color-coded icon buttons. |
138
+ | `styled_confirms` | `true` | Row-level `data-confirm` links (View/Edit/Delete, or anything else using Rails UJS's `data-confirm`) fall back to the browser's native `confirm()`. Batch Actions confirms are unaffected either way — they always use ActiveAdmin's own dialog. |
139
+ | `collapsible_filters` | `true` | The "Filters" sidebar panel always renders fully expanded (ActiveAdmin's own default) instead of collapsing to a single icon button that expands on click. |
140
+ | `sidebar_footer` | `true` | "Powered by Active Admin" (or your own `config.footer`) stays in ActiveAdmin's original page-level `#footer` instead of moving into the sidebar. |
141
+ | `flash_dismissible` | `true` | Flash messages render as ActiveAdmin's original plain `<div>` (no dismiss button). |
142
+ | `flash_auto_dismiss` | `true` | Flash messages stay on screen until the next page load (ActiveAdmin's default) instead of disappearing on their own. |
143
+ | `flash_auto_dismiss_seconds` | `5` | How long a flash stays visible before auto-dismissing (only relevant while `flash_auto_dismiss` is `true`). |
144
+ | `flash_transition_ms` | `320` | How long the fade/slide-out transition takes when a flash is dismissed (manually or automatically). |
145
+ | `login_page` | `true` | Every Devise auth page (sign in, sign up, forgot/reset password, resend confirmation, resend unlock) renders ActiveAdmin's original plain gradient-header box instead of Prism's centered card/brand mark. |
146
+ | `login_logo` | `nil` | `nil` renders Prism's built-in mark. A `String`/`Proc`/`Symbol` overrides it — see [Sign-in page](#sign-in-page). |
147
+ | `login_app_name` | `nil` | `nil` falls back to ActiveAdmin's own `config.site_title`. A `String`/`Proc`/`Symbol` overrides it. |
148
+ | `login_tagline` | `"Sign in to your admin dashboard"` | `nil` renders no tagline at all. |
149
+ | `language_switcher` | `true` | No "Languages" dropdown renders in the sidebar at all — see [Language switcher](#language-switcher). |
150
+ | `languages` | 3 entries (English/Español/Français) | The list the dropdown renders; an empty array (`[]`) has the same effect as `language_switcher = false`. |
151
+
152
+ **How this crosses the server/client boundary.** `sidebar`,
153
+ `colorize_action_icons`, and the `login_*` flags are pure server-side
154
+ decisions — Ruby renders different markup depending on the flag, full stop
155
+ (see [Sign-in page](#sign-in-page) for why `login_page` needs a Gemfile
156
+ ordering note despite being server-side). `styled_confirms`,
157
+ `collapsible_filters`, `sidebar_footer`, and the flash-related flags are
158
+ read by client-side JS/CSS (`prism.js` / `scss-src`), which can't see Ruby
159
+ config directly — so the relevant Ruby view code renders the *current*
160
+ value into the page on every request (a `prism-styled-confirms-disabled` /
161
+ `prism-filters-collapsible-disabled` / `prism-sidebar-footer-disabled`
162
+ class on `<body>`, and `data-prism-auto-dismiss-ms` /
163
+ `data-prism-transition-ms` attributes on the flash wrapper). That means all
164
+ of these are safe to change at runtime (e.g. from a feature-flag service)
165
+ without restarting your server — the very next page render picks up the
166
+ new value.
167
+
168
+ `flash_transition_ms` specifically is applied by `prism.js` as an inline
169
+ `transition-duration` on each flash (overriding the CSS default of 320ms),
170
+ and reused as the delay before the element is actually removed from the
171
+ DOM — so the fade-out animation and the removal always stay in sync
172
+ regardless of the configured duration.
173
+
174
+ `reset_configuration!` (mainly for test suites) resets every flag back to
175
+ its default:
176
+
177
+ ```ruby
178
+ ActiveAdminPrism.reset_configuration!
179
+ ```
180
+
181
+ ## Plug-and-play: switching the theme on/off
182
+
183
+ Three levels, from "turn off one thing" to "fully revert to stock
184
+ ActiveAdmin":
185
+
186
+ **1. Turn off one feature.** Use the [Configuration reference](#configuration-reference)
187
+ above — e.g. `config.sidebar = false` to keep your existing top nav while
188
+ still getting the panel/table/form/button reskin and the flash/confirm
189
+ polish.
190
+
191
+ **2. Fully disable Prism, temporarily, without uninstalling.** Since every
192
+ Ruby-level override checks `ActiveAdminPrism.configuration` at render
193
+ time, setting every flag to `false` and skipping the sidebar swap gets you
194
+ back to (almost) stock ActiveAdmin behavior while the gem stays installed:
195
+
196
+ ```ruby
197
+ ActiveAdminPrism.configure do |config|
198
+ config.sidebar = false
199
+ config.colorize_action_icons = false
200
+ config.styled_confirms = false
201
+ config.collapsible_filters = false
202
+ config.sidebar_footer = false
203
+ config.flash_dismissible = false
204
+ config.flash_auto_dismiss = false
205
+ config.login_page = false
206
+ config.language_switcher = false
207
+ end
208
+ # Note: still calling enable! here would be a no-op for the sidebar since
209
+ # config.sidebar is false, but there's no need to call it at all in this case.
210
+ ```
211
+
212
+ This is useful for a gradual rollout or A/B test — gate the whole block
213
+ behind your own condition:
214
+
215
+ ```ruby
216
+ if Rails.env.production? && !FeatureFlag.enabled?(:active_admin_prism)
217
+ ActiveAdminPrism.configure do |c|
218
+ c.sidebar = false
219
+ c.colorize_action_icons = false
220
+ c.styled_confirms = false
221
+ c.collapsible_filters = false
222
+ c.sidebar_footer = false
223
+ c.flash_dismissible = false
224
+ c.flash_auto_dismiss = false
225
+ c.login_page = false
226
+ c.language_switcher = false
227
+ end
228
+ end
229
+ ActiveAdminPrism.enable!
230
+ ```
231
+
232
+ Note this doesn't touch the **CSS reskin** of panels/tables/forms/buttons —
233
+ those are unconditional (pure CSS, not gated by config) since they only
234
+ restyle existing ActiveAdmin markup and have no interactive behavior to
235
+ turn off. If you need the *visual* theme fully gone too (not just the
236
+ interactive bits), see level 3.
237
+
238
+ **3. Fully revert to stock ActiveAdmin, including CSS.** Remove (or
239
+ comment out) the two things the generator added:
240
+
241
+ ```ruby
242
+ # config/initializers/active_admin.rb
243
+ # ActiveAdminPrism.enable! <- comment out or delete
244
+ ```
245
+
246
+ ```scss
247
+ // app/assets/stylesheets/active_admin.scss
248
+ // @import "active_admin_prism/variable_overrides"; <- comment out or delete
249
+ @import "active_admin/mixins";
250
+ @import "active_admin/base";
251
+ ```
252
+
253
+ The gem's CSS/JS are still *registered* (the engine does that
254
+ unconditionally at boot), but with nothing in the page referencing Prism's
255
+ classes (no `prism-sidebar`, no `prism-nav-*`, etc.) and ActiveAdmin's own
256
+ Sass variables back to their stock defaults, the visual result is stock
257
+ ActiveAdmin. You don't need to remove the gem from your Gemfile to do this
258
+ — it's a two-line, fully reversible toggle.
259
+
260
+ ## Feature guide
261
+
262
+ ### Sidebar navigation: icons & badges
263
+
264
+ Menu items are still defined the normal ActiveAdmin way — nested groups,
265
+ `priority`, `if:` all work unchanged. Two extra, optional conventions:
266
+
267
+ ```ruby
268
+ # app/admin/orders.rb
269
+ menu label: "Orders", parent: "E-Commerce", priority: 2,
270
+ html_options: { icon: :receipt }
271
+
272
+ menu label: -> { "Messages #{content_tag(:span, unread_count, class: 'nav-badge')}".html_safe },
273
+ html_options: { icon: :message }
274
+ ```
275
+
276
+ `icon:` (inside `html_options:` — ActiveAdmin's own `MenuItem` already
277
+ passes this hash through untouched) accepts: `:dashboard`, `:users`,
278
+ `:cart`, `:box`, `:receipt`, `:credit_card`, `:message`, `:settings`,
279
+ `:home`, `:list`, `:folder`, `:bell`, `:search`, `:menu`, `:logout`,
280
+ `:check`, `:x`, `:chevron_down`, `:eye`, `:pencil`, `:trash`, `:filter`.
281
+
282
+ ### Parent groups & submenus (nested navigation)
283
+
284
+ Nested groups (e.g. an "E-Commerce" group containing "Products" and
285
+ "Orders") are plain ActiveAdmin `menu` DSL — `PrismSidebar` only changes how
286
+ the existing `ActiveAdmin::Menu`/`MenuItem` tree is drawn, not how it's
287
+ built, so this works exactly like it would with the stock top nav.
288
+
289
+ **Create a child under a parent** with `parent:` (a string/symbol — it's
290
+ matched case-insensitively and normalized the same way ActiveAdmin
291
+ normalizes any menu id):
292
+
293
+ ```ruby
294
+ # app/admin/products.rb
295
+ ActiveAdmin.register Product do
296
+ menu label: "Products", parent: "E-Commerce", priority: 1,
297
+ html_options: { icon: :box }
298
+ end
299
+
300
+ # app/admin/orders.rb
301
+ ActiveAdmin.register Order do
302
+ menu label: "Orders", parent: "E-Commerce", priority: 2,
303
+ html_options: { icon: :receipt }
304
+ end
305
+ ```
306
+
307
+ The first time either file registers, ActiveAdmin auto-creates an
308
+ "E-Commerce" parent item for you (non-clickable — its url defaults to `"#"`,
309
+ which is exactly what `PrismSidebar` checks to decide whether to render a
310
+ group toggle instead of a link). Both children then attach to that same
311
+ auto-created parent, sorted by `priority` within the group.
312
+
313
+ **Give the parent its own icon/priority** by declaring it explicitly, once,
314
+ with no `parent:` of its own — anywhere it'll load before (or alongside)
315
+ its children, e.g. in one of the resource files or in a dedicated
316
+ `app/admin/dashboard.rb`-style file:
317
+
318
+ ```ruby
319
+ menu label: "E-Commerce", priority: 2, html_options: { icon: :cart }
320
+ ```
321
+
322
+ ActiveAdmin de-dupes by the normalized menu id (`"e-commerce"`), so this
323
+ becomes the *same* parent item `parent: "E-Commerce"` above attaches to —
324
+ whichever file happens to register first, the icon/priority you set here
325
+ wins for the group itself.
326
+
327
+ **Deeper nesting** — `parent:` also accepts an array for a multi-level
328
+ chain, auto-creating every intermediate level that doesn't already exist:
329
+
330
+ ```ruby
331
+ menu label: "Conversion", parent: ["Analytics", "Reports"], priority: 1
332
+ # => Analytics > Reports > Conversion
333
+ ```
334
+
335
+ **Active/expanded state** is automatic, not something you configure:
336
+ `PrismSidebar` marks a group `.active` (and starts it `.open`) whenever the
337
+ current page is that group or one of its descendants
338
+ (`ActiveAdmin::MenuItem#current?`); `prism.js` then persists whichever
339
+ groups a visitor has manually opened/closed to `localStorage`, keyed per
340
+ item, so their choice survives navigating to an unrelated page instead of
341
+ resetting to that server-rendered default on every request.
342
+
343
+ There's no Prism-specific config for nesting itself — the only lever that
344
+ affects it at all is the top-level `config.sidebar` flag (see
345
+ [Configuration reference](#configuration-reference)): turn it off and this
346
+ same `menu`/`parent:` tree renders through ActiveAdmin's own stock
347
+ top-nav dropdown behavior instead of the Prism sidebar.
348
+
349
+ ### Language switcher
350
+
351
+ A "Languages" dropdown renders near the top of the sidebar, between the
352
+ brand and the Pages nav — no `admin.build_menu :utility_navigation do |menu|
353
+ ... end` code required in your own initializer, unlike the equivalent
354
+ hand-rolled setup in stock ActiveAdmin. Ships with 3 languages by default;
355
+ replace the whole list to add, remove, reorder, or fully localize it:
356
+
357
+ ```ruby
358
+ ActiveAdminPrism.configure do |config|
359
+ config.languages = [
360
+ { label: "English", locale: :en },
361
+ { label: "Deutsch", locale: :de },
362
+ { label: "日本語", locale: :ja }
363
+ ]
364
+ end
365
+ ```
366
+
367
+ Each entry is a `{ label:, locale:, url: }` hash (`url:` optional) —
368
+ `label` is the text shown (and matched against `I18n.locale` to decide
369
+ which option gets `.active` and shows as the dropdown's current
370
+ selection). With no `url:`, `locale` builds the link via
371
+ `url_for(locale: ...)`, appending `?locale=xx` to the *current* page
372
+ (preserving whatever path/params you're already on) — the same
373
+ query-param convention an ActiveAdmin app already reaches for by hand for
374
+ this.
375
+
376
+ Give an entry its own `url:` when switching locale needs to do more than
377
+ change a query param on the current page — hitting a remote/dedicated
378
+ endpoint that sets a cookie, session key, or subdomain before redirecting
379
+ back, for instance:
380
+
381
+ ```ruby
382
+ config.languages = [
383
+ { label: "English", locale: :en },
384
+ { label: "日本語", locale: :ja,
385
+ url: -> { "https://example.com/set_locale?locale=ja&return_to=#{request.path}" } }
386
+ ]
387
+ ```
388
+
389
+ `url:` accepts a `String`, `Proc`, or `Symbol` — same
390
+ `nil`/`String`/`Proc`/`Symbol` convention as `login_logo`. A `Proc` is
391
+ evaluated the exact same way an `ActiveAdmin::MenuItem`'s own `url:` proc
392
+ already is elsewhere in ActiveAdmin (bare `url_for`/`request`/any other
393
+ view helper is available inside it via Arbre's own delegation to the view
394
+ context — no `helpers.` prefix needed — matching the
395
+ `url: proc { url_for(locale: l.locale) }` pattern an ActiveAdmin app
396
+ already reaches for by hand for this).
397
+
398
+ This gem only renders the dropdown and its links; actually switching
399
+ `I18n.locale` based on the param your link lands on (typically a
400
+ `before_action { I18n.locale = params[:locale] || I18n.default_locale }`
401
+ in your own `ApplicationController` or `Admin::BaseController`) is your
402
+ app's own responsibility, same as it would be with a hand-rolled
403
+ utility-nav menu — this gem doesn't touch your controllers to avoid
404
+ guessing at a locale strategy (query param vs. subdomain vs. session vs.
405
+ path prefix) that varies widely between apps.
406
+
407
+ Set `config.languages = []` or `config.language_switcher = false` to
408
+ remove the dropdown entirely — an empty list and the flag have the exact
409
+ same effect, so use whichever reads more clearly for your case.
410
+
411
+ Unlike the rest of the sidebar (built once from your `menu do |m| ... end`
412
+ blocks each request re-renders, same as everything else here), this
413
+ dropdown is constructed fresh on every render directly from
414
+ `ActiveAdminPrism.configuration.languages` rather than through
415
+ ActiveAdmin's own `Menu`/`MenuItem`/`build_menu` machinery — see
416
+ [Language switcher markup](#language-switcher-markup) for why, and for the
417
+ full class/id reference.
418
+
419
+ ### Icon helper
420
+
421
+ Available anywhere in your admin views (not just the sidebar):
422
+
423
+ ```ruby
424
+ prism_icon(:cart, css_class: "prism-icon", size: 18)
425
+ ```
426
+
427
+ ### Toggle switch — form input
428
+
429
+ ```ruby
430
+ form do |f|
431
+ f.input :active, as: :prism_toggle
432
+ end
433
+ ```
434
+
435
+ Only the wrapping markup changes — submitted params are identical to
436
+ `as: :boolean`.
437
+
438
+ ### Toggle switch — read-only index/show
439
+
440
+ ```ruby
441
+ index do
442
+ column :active do |product|
443
+ prism_toggle_tag product.active
444
+ end
445
+ end
446
+
447
+ show do
448
+ attributes_table do
449
+ row :active do |product|
450
+ prism_toggle_tag product.active
451
+ end
452
+ end
453
+ end
454
+ ```
455
+
456
+ Explicit opt-in per column/row — ActiveAdmin's default `status_tag`
457
+ "Yes"/"No" pill is untouched everywhere you don't call it.
458
+
459
+ ### Row-level action icons
460
+
461
+ View/Edit/Delete on index pages render as color-coded icon buttons (blue
462
+ eye, purple pencil, red trash) automatically — no setup needed, and no
463
+ change to your `actions`/`index` blocks. Turn it off entirely with
464
+ `config.colorize_action_icons = false` if you'd rather keep plain text
465
+ links.
466
+
467
+ ### Confirm dialogs
468
+
469
+ Every `data-confirm` link — row-level actions or your own custom ones —
470
+ routes through ActiveAdmin's own styled jQuery UI dialog instead of the
471
+ native browser `confirm()`. No-ops gracefully if your app doesn't use
472
+ jquery-rails/jquery-ujs. Turn off with `config.styled_confirms = false`.
473
+
474
+ ### Collapsible "Filters" sidebar
475
+
476
+ The "Filters" panel (ActiveAdmin's `filters_sidebar_section`) starts
477
+ collapsed to a single filter-icon button; clicking it expands the full form
478
+ in place (also keyboard-accessible — Enter/Space toggle it, and it exposes
479
+ `aria-expanded`). The index table/main content area reflows to reclaim the
480
+ freed-up width while collapsed, and gives it back when expanded. No setup
481
+ needed, and it only targets that specific panel — any other sidebar section
482
+ (dashboard panels, custom `sidebar :title do ... end` blocks) is untouched.
483
+ Turn it off with `config.collapsible_filters = false` to always show it
484
+ fully expanded, matching ActiveAdmin's own default (and the table/content
485
+ area at its normal, non-reflowing width).
486
+
487
+ The reflow relies on the CSS `:has()` selector (broadly supported in
488
+ current browsers); without it, the Filters panel still collapses/expands
489
+ correctly, it just won't reclaim the extra width.
490
+
491
+ ### Flash messages
492
+
493
+ Flash messages get a dismiss button, auto-hide after a configurable delay,
494
+ and fade/slide out with a smooth, configurable transition — see
495
+ [Configuration reference](#configuration-reference) for
496
+ `flash_dismissible`, `flash_auto_dismiss(_seconds)`, and
497
+ `flash_transition_ms`.
498
+
499
+ ### Sidebar footer ("Powered by Active Admin")
500
+
501
+ This is moved from ActiveAdmin's own page-level `#footer` into the sidebar
502
+ itself, pinned below the account/logout area — a `flex: 0 0 auto` box that
503
+ doesn't get pushed down as the nav menu grows. Any custom
504
+ `config.footer = "..."` text you've set (a string, symbol, or proc) is used
505
+ as-is. Turn it off with `config.sidebar_footer = false` to leave the
506
+ original `#footer` visible in its normal place instead, matching
507
+ ActiveAdmin's own default.
508
+
509
+ ### Sign-in page
510
+
511
+ Every Devise auth page ActiveAdmin renders — sign in (`sessions#new`,
512
+ usually `/admin/login`), sign up (`registrations#new`), forgot/reset
513
+ password (`passwords#new`/`#edit`), resend confirmation
514
+ (`confirmations#new`), and resend unlock (`unlocks#new`) — gets a centered
515
+ card, animated brand mark, full-width submit button, and a tidied-up row
516
+ of pill-shaped links instead of ActiveAdmin's default gradient header box.
517
+ No setup needed; whichever of these your app actually uses (most apps
518
+ don't enable Devise's `:confirmable`/`:lockable` modules, so those two
519
+ routes may not exist at all — that's fine, unrouted pages just never
520
+ render).
521
+
522
+ ```ruby
523
+ ActiveAdminPrism.configure do |config|
524
+ config.login_logo = -> { image_tag "my_logo.svg", height: 40 }
525
+ config.login_app_name = "My Company Admin"
526
+ config.login_tagline = "Internal operations console"
527
+ end
528
+ ```
529
+
530
+ The brand mark and app name are shared across *all* of these pages (one
531
+ consistent identity, wherever a visitor lands); only the tagline differs
532
+ per page — `login_tagline` is sign-in's own, independently configurable
533
+ line, while every other page shows its own action's own translated title
534
+ underneath instead (e.g. "Sign up", "Forgot your password?"), which isn't
535
+ a separate config option since it's just naming what the page in front of
536
+ you already does.
537
+
538
+ `login_logo`, `login_app_name`, and `login_tagline` each accept:
539
+
540
+ - `nil` — the built-in default (Prism's own animated prism mark for
541
+ `login_logo`; ActiveAdmin's own `config.site_title` for `login_app_name`;
542
+ a plain default sentence for `login_tagline`, or no tagline at all if you
543
+ explicitly set it to `nil`).
544
+ - A `String` — used as-is (for `login_logo` this must be trusted, already
545
+ html-safe markup, e.g. the result of `image_tag`, not raw untrusted
546
+ input).
547
+ - A `Proc` — `instance_exec`'d in the page's own view context, so ordinary
548
+ view helpers are available, including this gem's own `prism_icon`:
549
+ `-> { prism_icon(:cart, size: 40) }`.
550
+ - A `Symbol` — called as a method on the view context.
551
+
552
+ This is the same `nil`/`String`/`Proc`/`Symbol` convention ActiveAdmin
553
+ itself already uses for `config.footer` and `config.site_title_image`.
554
+
555
+ Turn the whole thing off with `config.login_page = false` to render
556
+ ActiveAdmin's original plain login box on every one of these pages instead
557
+ (see [Configuration reference](#configuration-reference)).
558
+
559
+ **How this actually overrides ActiveAdmin's pages.** Unlike most of Prism
560
+ (which reopens an existing Ruby class or targets existing CSS selectors),
561
+ these pages are customized by the gem shipping its own copies of
562
+ `app/views/active_admin/devise/{sessions/new,registrations/new,
563
+ passwords/new,passwords/edit,confirmations/new,unlocks/new}.html.erb` (plus
564
+ a shared `devise/shared/_brand` partial all six render, so they redesign
565
+ together instead of drifting out of sync) — Rails resolves an engine's
566
+ `app/views` directory as a view path, and engines initialized later end up
567
+ higher priority than ones initialized earlier (each engine
568
+ `prepend_view_path`s its own directory in turn). Since a Gemfile naturally
569
+ lists `gem "active_admin_prism"` after `gem "activeadmin"` (the theme
570
+ depends on it), this gem's copies of those views are what Rails renders,
571
+ without you doing anything — this has been verified against the real
572
+ Bundler/Rails load order in this gem's own demo app. If your Gemfile
573
+ somehow requires `active_admin_prism` *before* `activeadmin` (unusual,
574
+ but possible with certain `group`/`require:` combinations), ActiveAdmin's
575
+ own plain views would win instead — the rest of the theme (CSS reskin,
576
+ sidebar, etc.) is unaffected either way, since none of that depends on view
577
+ path ordering. The actual authentication forms and field lists inside
578
+ every one of these views are untouched, byte-for-byte identical to
579
+ ActiveAdmin's own templates — only the surrounding brand markup is new.
580
+
581
+ ## Error pages (404/500)
582
+
583
+ ```sh
584
+ rails g active_admin_prism:error_pages
585
+ ```
586
+
587
+ Copies Prism-styled `public/404.html` and `public/500.html` (matching the
588
+ card/blob visual language of the sign-in page) into your app.
589
+
590
+ This is deliberately **not** part of `rails g active_admin_prism:install`,
591
+ and **not** gated by `ActiveAdminPrism::Configuration`, unlike every
592
+ other piece of this theme:
593
+
594
+ - **Static exception pages can't be "registered" or overridden by a gem at
595
+ all.** `config.exceptions_app`'s default
596
+ (`ActionDispatch::PublicExceptions`) reads `public/404.html`/`500.html`
597
+ straight off disk from `Rails.public_path`, completely outside the asset
598
+ pipeline and outside Rails' view-resolution/engine-view-path mechanism —
599
+ the same mechanism the [sign-in page](#sign-in-page) override relies on
600
+ doesn't exist here. Copying the files into the host app (the same way
601
+ `rails new` scaffolds its own default `public/404.html`) is the only way
602
+ a gem can deliver this at all.
603
+ - **A host's `public/404.html`/`500.html` are usually already
604
+ customized/committed.** Silently overwriting them on every `install` run
605
+ — or worse, ambushing an unrelated `install` with `copy_file`'s
606
+ interactive overwrite prompt — would be surprising. This generator is a
607
+ separate, explicit opt-in you run (and re-run, if you want a newer
608
+ version of the gem's default templates) whenever you actually want them.
609
+
610
+ Once copied, these are plain static HTML with an inline `<style>` block —
611
+ no ERB, no `@import "prism.css"`, no `ActiveAdminPrism::Configuration`
612
+ read at request time (a truly static file has no request to read config
613
+ during). The colors are hand-copied from `scss-src/_variables.scss`'s
614
+ defaults as of this gem's current version rather than referencing them, so
615
+ they won't automatically track a host's own color customization
616
+ ([Customizing colors](#customizing-colors)) — re-run the generator, or
617
+ hand-edit the copied files, if you want them to match. The action button
618
+ `href`s (`Back to Dashboard`, `Report Issue`) are placeholders — a static
619
+ file has no Rails route helpers available to point them at your actual
620
+ admin path automatically, so edit those after copying.
621
+
622
+ ## Markup & CSS class reference
623
+
624
+ Everything below is either existing ActiveAdmin markup Prism reskins in
625
+ place (no new classes — just a CSS rule targeting AA's own selector), or a
626
+ `prism-*`-prefixed class/id Prism itself renders. Use this as the map for
627
+ writing your own CSS on top of the theme, or for understanding what a given
628
+ `prism-*` class in DevTools corresponds to. Source of truth for all of this
629
+ is `scss-src/*.scss` (edit + `bin/build-css` if you fork the gem) and the
630
+ Ruby view files under `lib/active_admin/views/`.
631
+
632
+ ### Sidebar (`ActiveAdmin::Views::PrismSidebar`)
633
+
634
+ ```
635
+ #header.prism-sidebar <- drop-in replacement for AA's header slot
636
+ span.prism-sidebar-mobile-toggle <- hamburger, ≤900px viewports only
637
+ div.prism-sidebar-inner
638
+ div.prism-sidebar-brand
639
+ #site_title <- AA's own site_title verb, unchanged
640
+ div.prism-sidebar-lang <- only if config.language_switcher and #languages is non-empty
641
+ div.prism-sidebar-scroll
642
+ div.prism-nav-section-label <- "PAGES"
643
+ ul.prism-nav
644
+ li.prism-nav-item[#prism_nav_<id>]
645
+ [.has-children] <- item.items.any?
646
+ [.active] <- item.current?(current_tab)
647
+ [.open] <- has-children && active, or user-toggled
648
+ span.prism-nav-group-toggle <- only when .has-children
649
+ svg.prism-nav-icon
650
+ span.prism-nav-label
651
+ svg.prism-chevron
652
+ a.prism-nav-link <- leaf items only (real url? see below)
653
+ svg.prism-nav-icon
654
+ span.prism-nav-label
655
+ ul.prism-nav-submenu <- only when .has-children, nested li.prism-nav-item...
656
+ div.prism-sidebar-utility <- only if the utility menu has items
657
+ ul.prism-nav.prism-nav-utility
658
+ li#prism_nav_current_user
659
+ li#prism_nav_logout
660
+ div.prism-sidebar-footer <- only if config.sidebar_footer
661
+ ```
662
+
663
+ Notes:
664
+ - A leaf item renders as `a.prism-nav-link` only when its url is a "real"
665
+ url (present and not `"#"`); otherwise it renders as a plain
666
+ `span.prism-nav-link` (see `real_url?` in `prism_sidebar.rb`) — this is
667
+ also exactly how ActiveAdmin auto-created parent groups end up as
668
+ non-clickable toggles (see [Parent groups & submenus](#parent-groups--submenus-nested-navigation)).
669
+ - `#prism_nav_current_user` / `#prism_nav_logout` are ActiveAdmin's own
670
+ stable ids for the default utility nav (`Namespace#build_default_utility_nav`)
671
+ — style hooks, not Prism inventions.
672
+ - `.nav-badge` (not `prism-`-prefixed — it's a convention *you* opt into via
673
+ `label:`, see [Sidebar navigation: icons & badges](#sidebar-navigation-icons--badges)) is the
674
+ class used in the README/INTEGRATION examples for badge counts; it has no
675
+ built-in styling of its own beyond what you see in `_sidebar.scss` — it's
676
+ just an example class name, feel free to use your own.
677
+ - Body-level state: `body.prism-sidebar-open` (mobile off-canvas open,
678
+ toggled by `prism.js`), `body.prism-sidebar-footer-disabled` (set when
679
+ `config.sidebar_footer` is `false` — see [Configuration reference](#configuration-reference)).
680
+
681
+ ### Language switcher markup
682
+
683
+ ```
684
+ div.prism-sidebar-lang[.open] <- ".open" toggled by prism.js, not server-rendered
685
+ span.prism-lang-toggle[aria-expanded]
686
+ svg.prism-nav-icon <- the :globe icon
687
+ span.prism-lang-current <- the option whose locale matches I18n.locale (or the first one)
688
+ svg.prism-chevron
689
+ ul.prism-lang-menu
690
+ li
691
+ a.prism-lang-option[.active] <- href="<current path>?locale=<locale>", or the entry's own :url if set
692
+ ```
693
+
694
+ Unlike every other piece of navigation in this gem (which walks
695
+ ActiveAdmin's own `Menu`/`MenuItem` tree, built via `menu do |m| ... end`
696
+ DSL calls in `app/admin/*.rb`), this one is constructed directly from
697
+ `ActiveAdminPrism.configuration.languages` at render time — there's no
698
+ `ActiveAdmin::Menu` object backing it, no `admin.build_menu
699
+ :utility_navigation` call needed, and no per-namespace setup. That also
700
+ means it re-renders from the *current* config on every request, the same
701
+ as `sidebar_footer`/`collapsible_filters` elsewhere in this gem, rather
702
+ than being fixed at boot the way the sidebar's own header-swap
703
+ registration is (see [Sign-in page](#sign-in-page) for the other example
704
+ of that "built once at boot vs. re-read every request" distinction).
705
+
706
+ ### Panels & cards
707
+
708
+ Pure CSS reskin of AA's existing `Panel`/`Component` markup — no new
709
+ classes:
710
+
711
+ ```
712
+ .panel <- white card, border, shadow, border-radius
713
+ > h3 <- panel title bar
714
+ .panel_contents <- body padding
715
+ .header_action <- float:right slot (AA's own convention)
716
+ .columns > .column <- flex card grid (dashboard layout)
717
+ ```
718
+
719
+ The "Filters" sidebar section specifically (`#filters_sidebar_section`) gets
720
+ extra markup and classes — see
721
+ [Filters sidebar panel](#filters-sidebar-panel-filters_sidebar_section)
722
+ below.
723
+
724
+ ### Index tables & row actions
725
+
726
+ ```
727
+ table.index_table
728
+ th[.sorted-asc/.sorted-desc]
729
+ tr[.even][.selected]
730
+ td.col-actions
731
+ .table_actions <- wraps View/Edit/Delete for a row
732
+ a.view_link <- icon: eye, color: info-blue
733
+ svg.prism-action-icon
734
+ span.prism-visually-hidden <- original text label, screen-reader only
735
+ a.edit_link <- icon: pencil, color: primary
736
+ a.delete_link <- icon: trash, color: danger
737
+ .status_tag[.yes/.no] <- AA's default boolean pill (untouched markup, reskinned colors)
738
+ ```
739
+
740
+ `view_link` / `edit_link` / `delete_link` are ActiveAdmin's **own** classes
741
+ (`IndexTableFor#defaults`) — Prism's `index_table_actions.rb` override only
742
+ swaps the link's *content* (icon + hidden label instead of plain text) when
743
+ `config.colorize_action_icons` is `true`; with it `false`, `item(...)`
744
+ falls back to AA's plain `link_to` untouched, and none of the
745
+ `prism-action-icon`/`prism-visually-hidden` markup is added.
746
+
747
+ ### Forms & toggle switches
748
+
749
+ ```
750
+ form fieldset.inputs
751
+ li[.error] <- p.inline-errors, red border on the field
752
+ ul.errors <- error summary box
753
+
754
+ // as: :boolean (AA default) — reskinned checkbox, no new classes:
755
+ input[type="checkbox"] / input[type="radio"]
756
+
757
+ // as: :prism_toggle (opt-in, PrismToggleInput):
758
+ label.prism-toggle
759
+ input.prism-toggle-input <- visually hidden (width/height:0), still focusable
760
+ span.prism-toggle-track <- the visible pill+thumb, :checked~ sibling selector drives it
761
+
762
+ // prism_toggle_tag(value) (opt-in, read-only index/show helper):
763
+ span.prism-toggle-tag[.on/.off]
764
+ span.prism-toggle-track
765
+ ```
766
+
767
+ `.prism-toggle-track` is shared between the live form input and the
768
+ read-only tag — same pill/thumb CSS, driven by a sibling `:checked`
769
+ selector in the form case and by the `.on`/`.off` class in the read-only
770
+ case (there's no real `<input>` on an index/show page).
771
+
772
+ ### Filters sidebar panel (`#filters_sidebar_section`)
773
+
774
+ Only this specific `SidebarSection` (AA's built-in id for the Filters
775
+ panel) gets the extra icon/collapse markup — any other sidebar section
776
+ (dashboard panels, a host's own `sidebar :title do ... end`) is untouched:
777
+
778
+ ```
779
+ #sidebar <- AA's own right-hand content sidebar (not Prism's left nav)
780
+ .panel#filters_sidebar_section.prism-collapsible-panel[.open]
781
+ > h3 <- the icon-only button when collapsed
782
+ svg.prism-filter-icon
783
+ span.prism-filter-label <- visually hidden (max-width:0) while collapsed
784
+ .panel_contents <- max-height:0 while collapsed, animates open
785
+ form.filter_form
786
+ .filter_form_field[.select_and_search][.filter_date_range]
787
+ ```
788
+
789
+ `.prism-collapsible-panel` and the icon/label markup are only added when
790
+ `config.collapsible_filters` is `true`; `prism.js` mirrors the panel's
791
+ `.open` state onto `body.prism-filters-open`, which is what
792
+ `#active_admin_content`/`#sidebar`'s reflow rules key off (`:has()` — see
793
+ [Collapsible "Filters" sidebar](#collapsible-filters-sidebar)). With the config `false`,
794
+ `body.prism-filters-collapsible-disabled` is the backstop class that forces
795
+ the panel back to always-expanded.
796
+
797
+ ### Flash messages markup
798
+
799
+ ```
800
+ .flashes[data-prism-transition-ms][data-prism-auto-dismiss-ms]
801
+ .flash.flash_notice / .flash_error / .flash_alert [.prism-flash-hide]
802
+ span.prism-flash-message
803
+ button.prism-flash-dismiss[data-prism-flash-dismiss] <- only if config.flash_dismissible
804
+ svg.prism-flash-dismiss-icon
805
+ ```
806
+
807
+ `flash_notice`/`flash_error`/`flash_alert` are ActiveAdmin's own type
808
+ classes (from `flash_messages.each { |type, ...| }`); Prism just colors
809
+ each one distinctly. `.prism-flash-hide` is added by `prism.js` right
810
+ before removal (opacity/transform transition), timed against the
811
+ `data-prism-transition-ms` attribute — see
812
+ [Flash messages](#flash-messages) / [Configuration reference](#configuration-reference).
813
+
814
+ ### Confirm dialog (jQuery UI)
815
+
816
+ Not Prism markup at all — this is ActiveAdmin's own `$.fn.dialog` output
817
+ (`active_admin/base.js`), reskinned by targeting jQuery UI's own classes:
818
+
819
+ ```
820
+ .ui-widget-overlay
821
+ .ui-dialog
822
+ .ui-dialog-titlebar
823
+ form > ul > li <- batch-action inputs, if any (empty <ul> for a plain confirm)
824
+ .ui-dialog-buttonpane
825
+ .ui-dialog-buttonset
826
+ button:first-child <- OK/Confirm — prism-button-primary
827
+ button:last-child <- Cancel — prism-button-secondary
828
+ ```
829
+
830
+ ### Sign-in page markup
831
+
832
+ This same structure is shared by all six auth pages (see
833
+ [Sign-in page](#sign-in-page)) — `form#session_new` below is whichever
834
+ form that particular action renders (`#registration_new` for sign up,
835
+ etc.), byte-for-byte ActiveAdmin's own template either way:
836
+
837
+ ```
838
+ body.logged_out
839
+ #wrapper
840
+ #content_wrapper <- centered 400px column when .prism-login-brand is present
841
+ .flashes
842
+ #active_admin_content <- the white card itself
843
+ #login.prism-login
844
+ [devise/shared/_brand partial:]
845
+ div.prism-login-brand <- only when config.login_page is true
846
+ div.prism-login-logo
847
+ svg.prism-login-mark <- default mark; absent if login_logo renders something else
848
+ .prism-login-tri <- the triangle (animated glow)
849
+ .prism-login-beam <- each dispersed line (animated dash-flow)
850
+ h2.prism-login-title
851
+ p.prism-login-tagline <- login_tagline on sessions#new; the page's own action title everywhere else
852
+ [h2 (plain, no wrapper)] <- rendered instead of .prism-login-brand when config.login_page is false
853
+ #error_explanation <- ActiveAdmin's own shared error-messages partial (every page but sessions#new, which uses the flash instead)
854
+ form#session_new <- untouched Devise/Formtastic form markup
855
+ fieldset.inputs <- border/radius suppressed here (see below) — redundant inside the card
856
+ fieldset.actions <- submit button stretched full-width (see below)
857
+ div.prism-login-links <- wraps active_admin/devise/shared/_links.erb's sign-up/forgot-password/etc links, now pill-chip styled
858
+ #footer
859
+ ```
860
+
861
+ All of the card/blob/centering CSS is scoped under
862
+ `body.logged_out:has(.prism-login-brand)` (see `scss-src/_login.scss`) —
863
+ it only ever applies on these actual auth pages, and never when
864
+ `config.login_page` is `false` (since `.prism-login-brand` then never
865
+ renders at all). `#wrapper` itself gets its `display: block` layout back
866
+ via a `body:not(.logged_out)` guard on the sidebar's own `#wrapper` grid
867
+ rule in `scss-src/_sidebar.scss`, since that id is reused (with a totally
868
+ different two-child structure) by the logged-out layout.
869
+
870
+ Two more fixes live in that same scoped block: `fieldset.inputs`'s own
871
+ card border/radius (right everywhere else a fieldset sits directly on the
872
+ gray page background) is suppressed here specifically, since it otherwise
873
+ draws a redundant second box inside the already-bordered auth-page card;
874
+ and the submit button (`fieldset.actions input[type="submit"]`) is
875
+ stretched to the full card width rather than left-aligned, so it's never
876
+ ambiguously off-center against the centered brand block above it or the
877
+ centered link chips below it.
878
+
879
+ ### Body-level state classes
880
+
881
+ All of these are added/removed by `body_classes` in
882
+ `lib/active_admin/views/flash_messages.rb` (server-rendered, current on
883
+ every request) or toggled by `prism.js` at runtime — see
884
+ [How this crosses the server/client boundary](#configuration-reference):
885
+
886
+ | Class | Meaning |
887
+ | --- | --- |
888
+ | `prism-sidebar-open` | Mobile off-canvas sidebar is open (≤900px viewports). Runtime only, via `prism.js`. |
889
+ | `prism-filters-open` | Filters panel is currently expanded — drives the content-area reflow. Runtime only, mirrors `#filters_sidebar_section.open`. |
890
+ | `prism-styled-confirms-disabled` | `config.styled_confirms` is `false` — `prism.js`'s `$.rails.allowAction` override no-ops. |
891
+ | `prism-filters-collapsible-disabled` | `config.collapsible_filters` is `false` — Filters panel forced always-expanded. |
892
+ | `prism-sidebar-footer-disabled` | `config.sidebar_footer` is `false` — original `#footer` stays visible; the sidebar's own footer div isn't rendered at all. |
893
+
894
+ ### Design tokens (Sass variables)
895
+
896
+ Defined in `scss-src/_variables.scss`, all `!default`. These compile into
897
+ the shipped, precompiled `prism.css` — they are **not** overridable from a
898
+ host app (see [Customizing colors](#customizing-colors) for why, and for
899
+ the separate, host-overridable set of ActiveAdmin's own variables via
900
+ `variable_overrides.scss`):
901
+
902
+ | Variable | Default | Used for |
903
+ | --- | --- | --- |
904
+ | `$prism-color-primary` | `#6d5bd0` | Active nav item, primary buttons, links, focus rings |
905
+ | `$prism-color-primary-dark` | `#5949b8` | Hover/active state of the above |
906
+ | `$prism-color-primary-light` | `#f2ecfd` | Active nav background, selected table row, hover backgrounds |
907
+ | `$prism-color-success` / `-light` | `#22c55e` / `#e8f9ee` | Notice flash, "yes" status tag |
908
+ | `$prism-color-danger` / `-light` | `#ef4444` / `#fdecec` | Error/alert flash, "no" status tag, delete action, field errors |
909
+ | `$prism-color-warning` | `#f59e0b` | Reserved (no default component uses this yet) |
910
+ | `$prism-color-info` | `#3b82f6` | View action icon |
911
+ | `$prism-bg-page` | `#f4f5f7` | Page background, table header background |
912
+ | `$prism-bg-card` | `#ffffff` | Panel/card/dialog background |
913
+ | `$prism-bg-sidebar` | `#ffffff` | Sidebar background |
914
+ | `$prism-text-heading` / `-body` / `-muted` / `-inverse` | `#1a1f36` / `#384250` / `#6b7280` / `#ffffff` | Text color scale |
915
+ | `$prism-border-color` / `-strong` | `#e6e8ec` / `#d6d9e0` | Hairline borders / stronger input borders |
916
+ | `$prism-sidebar-width` | `264px` | **Prism's own** sidebar column width (CSS Grid) — separate from AA's own `$sidebar-width` (320px, set in `variable_overrides.scss`, which sizes the *Filters* sidebar and is host-overridable) |
917
+ | `$prism-radius` / `-sm` / `-pill` | `10px` / `6px` / `999px` | Border-radius scale (cards / buttons+inputs / pills+toggles) |
918
+ | `$prism-shadow-card` / `-popover` | see source | Box-shadow scale |
919
+ | `$prism-font-family` | system font stack | Body font |
920
+ | `$prism-transition-fast` | `120ms ease` | Hover/focus transitions |
921
+ | `$prism-filters-collapsed-gutter` (`_panels.scss`) | `72px` | Width the content area reflows to when the Filters panel is collapsed |
922
+
923
+ ## Customizing colors
924
+
925
+ There are two independent color surfaces, because ActiveAdmin's own CSS
926
+ and Prism's CSS are two separately-compiled stylesheets:
927
+
928
+ **1. Prism's own palette** (`scss-src/_variables.scss` in this gem's
929
+ source — `$prism-color-primary`, `$prism-bg-page`, `$prism-sidebar-width`,
930
+ etc.). To change these you need to fork the gem's SCSS source, edit the
931
+ variables, and run `bin/build-css` to produce your own `prism.css` — there's
932
+ no way to override these from a host app, since `prism.css` ships
933
+ precompiled specifically so installing the gem doesn't require a Sass
934
+ compiler.
935
+
936
+ **2. ActiveAdmin's own default palette**
937
+ (`active_admin/mixins/_variables.scss` and `_gradients.scss` in the
938
+ `activeadmin` gem — `$primary-color`, `$text-color`, etc.), which drives
939
+ things Prism doesn't have Ruby-level control over: the jQuery UI
940
+ batch-actions dialog, the dropdown menu popup, anything you build yourself
941
+ with `panel`/`section-background`. The gem ships
942
+ [`variable_overrides.scss`](app/assets/stylesheets/active_admin_prism/_variable_overrides.scss) —
943
+ Prism's values for every one of these — which the install generator wires
944
+ up automatically (see [Install](#install)). Every variable in that file is
945
+ itself `!default`, so to customize further, set your own value **before**
946
+ that `@import` in your `active_admin.scss` and it wins:
947
+
948
+ ```scss
949
+ // app/assets/stylesheets/active_admin.scss
950
+ $primary-color: #your-brand-color;
951
+ @import "active_admin_prism/variable_overrides";
952
+ @import "active_admin/mixins";
953
+ @import "active_admin/base";
954
+ ```
955
+
956
+ ## Troubleshooting
957
+
958
+ **"I made a change and nothing looks different."** By far the most common
959
+ cause in practice: browser caching. The gem fingerprints its assets by
960
+ content hash, so a genuinely new `prism.css`/`prism.js` always gets a new
961
+ URL — but the *HTML page* referencing the old URL can still be served from
962
+ your browser's cache. Hard-refresh; if that doesn't help, open a private/
963
+ incognito window, which guarantees zero cached anything.
964
+
965
+ **"A button/link still shows ActiveAdmin's default gray, not Prism's
966
+ colors."** ActiveAdmin's own CSS uses some ID-scoped selectors (e.g.
967
+ `#header a`) and wraps hover/active states in `:not(.disabled)` — both add
968
+ specificity that a naive class-based override can lose to regardless of
969
+ load order. If you're overriding something yourself (custom CSS on top of
970
+ Prism), match ActiveAdmin's actual selector path rather than a flatter one,
971
+ or reach for `!important` on the specific property that's losing.
972
+
973
+ **"The confirm dialog / dismiss button isn't behaving."** These are
974
+ client-side JS (`prism.js`) — if `jquery-rails`/`jquery-ujs` isn't loaded
975
+ in your app, the confirm-dialog override no-ops silently by design (falls
976
+ back to the native browser confirm rather than erroring).
977
+
978
+ **"Sass compile error mentioning `$primary-color` or a missing mixin."**
979
+ Make sure `@import "active_admin_prism/variable_overrides"` comes
980
+ **before** `@import "active_admin/mixins"`, and that you haven't
981
+ accidentally commented out `active_admin/mixins` while leaving
982
+ `active_admin/base` active — `base` depends on `mixins` being loaded
983
+ first.
984
+
985
+ **"The sign-in page still looks like stock ActiveAdmin even though
986
+ `config.login_page` is `true`."** This one specific piece of Prism (see
987
+ [Sign-in page](#sign-in-page)) depends on Gemfile order: `gem
988
+ "active_admin_prism"` needs to be declared **after** `gem
989
+ "activeadmin"` so this gem's view overrides ActiveAdmin's own. This is the
990
+ natural, expected order already (the theme depends on ActiveAdmin), so it's
991
+ rarely an issue in practice — but if your Gemfile has an unusual `group`/
992
+ `require:` setup that changes load order, reordering those two lines fixes
993
+ it. Nothing else in the theme (CSS reskin, sidebar, tables, forms) depends
994
+ on this ordering — only the sign-in page's brand markup does.
995
+
996
+ ## Upgrading
997
+
998
+ Since Prism is CSS/JS/light Ruby overrides on top of ActiveAdmin's public
999
+ extension points (`ViewFactory.register`, documented `item()` hooks,
1000
+ `!default` Sass variables) rather than a fork, upgrading `activeadmin` or
1001
+ this gem independently is expected to keep working across minor versions.
1002
+ After upgrading either, re-run through
1003
+ [Verifying the install](#verifying-the-install) once.
1004
+
1005
+ ## Uninstalling
1006
+
1007
+ 1. Remove `ActiveAdminPrism.enable!` from
1008
+ `config/initializers/active_admin.rb`.
1009
+ 2. Remove `@import "active_admin_prism/variable_overrides";` from
1010
+ `active_admin.scss`, if present.
1011
+ 3. Remove `gem "active_admin_prism"` from your Gemfile and
1012
+ `bundle install`.