active_admin_prism
Prism is a plug-and-play ActiveAdmin theme: a collapsible left sidebar with grouped navigation, card-style panels and tables, and reskinned Formtastic forms — no Sass compiler, no JS bundler, no manual asset manifest edits.
→ See INTEGRATION.md for the full integration guide (configuration reference, feature-by-feature usage, switching/disabling the theme, troubleshooting). This README is a quick start.
Live demo
A live demo dashboard is running at prism-demo.onrender.com/admin:
Username: admin@example.com
Password: password
The demo is hosted on Render's free tier, so the first request after a period of inactivity can take up to a minute while the instance spins up.
Requirements
- ActiveAdmin
>= 3.0, < 4 - Rails
>= 7.0 - Ruby
>= 3.1
Install
Add to your Gemfile:
gem "active_admin_prism"
bundle install
rails g active_admin_prism:install
That's it. The generator:
- adds one line —
ActiveAdminPrism.enable!— to yourconfig/initializers/active_admin.rb. The theme's CSS and JS are registered automatically by the gem's Rails engine; there's no Sass/webpack build step required, regardless of whether your app uses Sprockets or Propshaft. - if you have
app/assets/stylesheets/active_admin.scss(generated byrails g active_admin:assets, i.e. you're on the Sprockets/Sass path, not Webpacker/importmap), adds one@importline there too — see below for what it does.
If you'd rather do either by hand, skip the generator and just add
ActiveAdminPrism.enable! anywhere in your
config/initializers/active_admin.rb (after the ActiveAdmin.setup block
is fine), and @import "active_admin_prism/variable_overrides";
before @import "active_admin/mixins"; in your active_admin.scss.
What it changes, and how
Prism follows a "light touch" philosophy: it changes as little of ActiveAdmin's internals as possible, so it keeps working across ActiveAdmin upgrades.
- Sidebar —
ActiveAdminPrism.enable!swaps ActiveAdmin'sheaderslot (normally the top nav bar) forActiveAdmin::Views::PrismSidebar, using ActiveAdmin's own supported view-registration mechanism (ActiveAdmin::ViewFactory.register). It renders your existingmenu do |m| ... endblocks fromapp/admin/*.rb— nested groups,priority,if:— completely unchanged; only how that menu is drawn is different. No other page structure (title bar, content, footer) is touched. - Panels, tables, buttons, forms — pure CSS reskin of ActiveAdmin's
existing markup (
.panel,.index_table, Formtastic'sli.input.*, etc.). No Ruby view overrides here at all. - Icons — a small curated set of inline SVG icons (no icon font, no
build step). Available anywhere in your admin views as
prism_icon(:cart). - Confirm dialogs — every
data-confirmlink (row-level View/Edit/Delete, and anything else using Rails UJS'sdata-confirm) is routed through ActiveAdmin's own styled jQuery UI dialog — the same one it already uses for Batch Actions — instead of the native, unstylable browserconfirm(). This overrides$.rails.allowAction, jquery-ujs's own documented extension point for custom confirm dialogs; Batch Actions links are left untouched since they already run their own confirm flow. No-ops gracefully if your app doesn't use jquery-rails/jquery-ujs.
Configuration
Every feature is individually toggleable via
ActiveAdminPrism::Configuration — including a sidebar flag that
lets you keep ActiveAdmin's stock top nav while still getting everything
else, and a full off switch for reverting to stock ActiveAdmin without
uninstalling the gem. All default to on:
ActiveAdminPrism.configure do |config|
config. = true # swap AA's top nav for the Prism sidebar
config.colorize_action_icons = true # color-coded View/Edit/Delete icons
config.styled_confirms = true # route data-confirm through the styled dialog
config.collapsible_filters = true # "Filters" panel collapses to an icon until clicked
config. = true # "Powered by Active Admin" moves into the sidebar
config.flash_dismissible = true # dismiss (x) button on flash messages
config.flash_auto_dismiss = true # auto-hide flash messages
config.flash_auto_dismiss_seconds = 5
config.flash_transition_ms = 320 # fade/slide duration when a flash is dismissed
config.login_page = true # centered card + brand mark on the sign-in page
config.login_logo = nil # nil = Prism's built-in mark; or a String/Proc/Symbol
config.login_app_name = nil # nil = falls back to AA's own config.site_title
config.login_tagline = "Sign in to your admin dashboard"
config.language_switcher = true # "Languages" dropdown near the top of the sidebar
config.languages = [ # defaults to these 3 — replace freely
{ label: "English", locale: :en },
{ label: "Español", locale: :es },
{ label: "Français", locale: :fr }
]
end
ActiveAdminPrism.enable!
See INTEGRATION.md for the full reference, and INTEGRATION.md for switching the whole theme on/off (including a full revert to stock ActiveAdmin, gem still installed).
Sidebar navigation: icons & badges
Menu items are still defined the normal ActiveAdmin way. Two extra, optional conventions:
# app/admin/dashboard.rb / any resource file
label: "Orders", priority: 2, html_options: { icon: :box }
label: -> { "Messages #{content_tag(:span, unread_count, class: 'nav-badge')}".html_safe },
html_options: { icon: :message }
icon: (inside html_options: — ActiveAdmin's own MenuItem already passes
this hash through untouched, so no new DSL is introduced) accepts any symbol
from the bundled set: :dashboard, :users,
:cart, :box, :receipt, :credit_card, :message, :settings,
:home, :list, :folder, :bell, :search, :menu, :logout,
:check, :x, :chevron_down, :eye, :pencil, :trash, :filter,
:globe.
Language switcher
A "Languages" dropdown renders near the top of the sidebar (below the
brand, above the Pages nav) — no admin.build_menu :utility_navigation
code needed. Ships with 3 languages; replace the list freely:
ActiveAdminPrism.configure do |config|
config.languages = [
{ label: "English", locale: :en },
{ label: "Deutsch", locale: :de },
{ label: "日本語", locale: :ja }
]
end
By default, each option links to the current page with ?locale=xx
appended (the same convention you'd use by hand with
url_for(locale: ...)) — actually switching I18n.locale based on that
param is your own app's responsibility (typically a before_action), same
as it would be if you'd wired the menu up yourself. Give any entry its own
url: (String/Proc/Symbol) to hit something else instead, e.g. a remote
endpoint that sets the locale server-side:
{ label: "日本語", locale: :ja,
url: -> { "https://example.com/set_locale?locale=ja&return_to=#{request.path}" } }
Turn the whole thing off with config.language_switcher = false.
Toggle switches
Boolean fields render as regular (reskinned) checkboxes by default. Opt in to a switch-style toggle per field:
form do |f|
f.input :active, as: :prism_toggle
end
This only changes the wrapping markup — submitted params are identical to
as: :boolean.
For index columns and show attributes_table rows, use the matching
read-only prism_toggle_tag helper in place of ActiveAdmin's default
status_tag "Yes"/"No" pill:
index do
column :active do |product|
prism_toggle_tag product.active
end
end
show do
attributes_table do
row :active do |product|
prism_toggle_tag product.active
end
end
end
It's an explicit opt-in per column/row (like everything else in Prism) — ActiveAdmin's default boolean rendering is untouched everywhere you don't call it.
Sign-in page
Every Devise auth page (sign in, sign up, forgot/reset password, resend confirmation, resend unlock — whichever of these your app actually uses) gets a centered card, an animated brand mark, a full-width submit button, and a row of pill-shaped links instead of ActiveAdmin's default gradient header box — no setup needed. The icon and app name are shared across all of them; the tagline underneath is sign-in's own configurable line, while every other page just names its own action ("Sign up", "Forgot your password?", etc.):
ActiveAdminPrism.configure do |config|
config.login_logo = -> { image_tag "my_logo.svg", height: 40 }
config.login_app_name = "My Company Admin"
config.login_tagline = "Internal operations console"
end
login_logo/login_app_name/login_tagline each accept a String, Proc
(instance_exec'd in the view — image_tag/prism_icon are both available),
or Symbol (a method name) — the same convention ActiveAdmin itself uses
for config.footer. Turn the whole thing off with config.login_page = false to keep ActiveAdmin's original plain login box. See
INTEGRATION.md for the Gemfile-ordering note
behind how this view override works.
Error pages (404/500)
rails g active_admin_prism:error_pages
Copies Prism-styled public/404.html and public/500.html into your app.
This one is a separate, explicit step (not part of install, and not
config-driven) because static exception pages are read straight off disk by
Rails — there's no engine hook to override them automatically, and a host's
existing public/404.html/500.html are usually already customized, so
silently touching them on every install run would be surprising. The
generator prompts before overwriting either file. Once copied, they're
plain static HTML/CSS with no Ruby behind them — edit them directly for
further changes.
Customizing colors
The shipped prism.css is compiled from Sass variables (see
scss-src/_variables.scss in this gem's source) such as
$prism-color-primary, $prism-bg-page, $prism-sidebar-width. To use
your own palette, fork the SCSS source, adjust the variables, and run
bin/build-css to produce your own prism.css — see
CONTRIBUTING below.
ActiveAdmin's own default colors
Prism reskins ActiveAdmin's existing markup with CSS, but a few things
ActiveAdmin renders itself — the jQuery UI batch-actions confirm dialog, the
dropdown menu popup, any custom page you write with panel/section-background
— are built from ActiveAdmin's own Sass variables
(active_admin/mixins/_variables.scss and _gradients.scss), not from
anything a precompiled CSS file can reach into.
Rather than chase every individual AA component that might leak its default
gray through, the gem ships
variable_overrides.scss —
Prism's values for every one of those variables. Since they're all declared
!default in the activeadmin gem, @import-ing this file before
@import "active_admin/mixins" in your active_admin.scss means everything
ActiveAdmin generates from that point on inherits Prism's palette as its
baseline instead of gray:
// app/assets/stylesheets/active_admin.scss
@import "active_admin_prism/variable_overrides";
@import "active_admin/mixins";
@import "active_admin/base";
The install generator adds this line for you automatically if this file
exists in your app. Every variable in variable_overrides.scss is itself
!default, so you can customize further — set your own value (including
$sidebar-width, which it widens from AA's default 270px to 320px to fit a
"select + input" filter row comfortably) before that @import and it wins.
This only works inside your own app's Sass compilation (where
active_admin/mixins is actually @import-ed) — it's a one-time addition to
your app, not something a precompiled CSS file can do on your behalf.
Development
bin/build-css # compiles + minifies scss-src/*.scss -> app/assets/stylesheets/active_admin_prism/prism.css
bin/build-js # minifies js-src/prism.js -> app/assets/javascripts/active_admin_prism/prism.js
bundle install
bundle exec rspec # or: rake
Never hand-edit the committed prism.css/prism.js — edit
scss-src/*.scss/js-src/prism.js and rebuild. Both committed assets are
shipped pre-minified (not just for Sprockets hosts, who'd normally
re-minify on their own assets:precompile anyway, but for Propshaft hosts
too, since Propshaft has no built-in minification step at all) — all the
documentation comments explaining the theme's various CSS-specificity
fixes etc. live in the scss-src/js-src sources, not the compiled
output.
The test suite boots a minimal Rails + ActiveAdmin + Devise app
(spec/dummy) and exercises the gem against real HTTP requests — no
mocking of ActiveAdmin/Arbre internals. See spec/rails_helper.rb for how
it's wired up.
License
MIT