Last active
July 18, 2025 14:55
-
-
Save amkisko/af1b2f7dc4f0f941437ea16400277864 to your computer and use it in GitHub Desktop.
ActiveAdmin v4 tailwind v4 compat
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace :active_admin do | |
desc "Build Active Admin Tailwind stylesheets" | |
task build: :environment do | |
command = [ | |
Rails.root.join("bin/tailwindcss").to_s, | |
"-i", Rails.root.join("app/assets/stylesheets/active_admin.tailwind.css").to_s, | |
"-o", Rails.root.join("app/assets/builds/active_admin.css").to_s, | |
"-m" | |
] | |
system(*command, exception: true) | |
end | |
desc "Watch Active Admin Tailwind stylesheets" | |
task watch: :environment do | |
command = [ | |
Rails.root.join("bin/tailwindcss").to_s, | |
"--watch", | |
"-i", Rails.root.join("app/assets/stylesheets/active_admin.tailwind.css").to_s, | |
"-o", Rails.root.join("app/assets/builds/active_admin.css").to_s, | |
"-m" | |
] | |
system(*command) | |
end | |
end | |
Rake::Task["assets:precompile"].enhance(["active_admin:build"]) | |
Rake::Task["test:prepare"].enhance(["active_admin:build"]) if Rake::Task.task_defined?("test:prepare") | |
Rake::Task["spec:prepare"].enhance(["tailwindcss:build"]) if Rake::Task.task_defined?("spec:prepare") | |
Rake::Task["db:test:prepare"].enhance(["tailwindcss:build"]) if Rake::Task.task_defined?("db:test:prepare") |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@import "tailwindcss"; | |
@config "../../../config/tailwind-active_admin.config.js"; | |
@custom-variant dark (&:where(.dark, .dark *)); | |
@utility ring-opacity-5 { | |
--tw-ring-opacity: 0.05; | |
} | |
@layer base { | |
*, | |
::after, | |
::before, | |
::backdrop, | |
::file-selector-button { | |
border-color: var(--color-gray-200, currentColor); | |
} | |
/* Form Inputs */ | |
[type='text'], | |
[type='email'], | |
[type='url'], | |
[type='password'], | |
[type='number'], | |
[type='date'], | |
[type='datetime-local'], | |
[type='month'], | |
[type='search'], | |
[type='tel'], | |
[type='time'], | |
[type='week'], | |
textarea, | |
select { | |
@apply appearance-none bg-gray-50 dark:bg-gray-700 border-gray-300 dark:border-gray-600 border rounded-md px-3 py-2 text-gray-900 dark:text-white w-full; | |
--tw-shadow: 0 0 #0000; | |
} | |
/* Form Input Focus States */ | |
[type='text']:focus, | |
[type='email']:focus, | |
[type='url']:focus, | |
[type='password']:focus, | |
[type='number']:focus, | |
[type='date']:focus, | |
[type='datetime-local']:focus, | |
[type='month']:focus, | |
[type='search']:focus, | |
[type='tel']:focus, | |
[type='time']:focus, | |
[type='week']:focus, | |
textarea:focus, | |
select:focus { | |
@apply outline-none ring-2 ring-blue-500 dark:ring-blue-500 border-blue-500 dark:border-blue-500; | |
} | |
/* Placeholders */ | |
input::placeholder, | |
textarea::placeholder { | |
@apply text-gray-500 dark:text-gray-400; | |
} | |
/* Checkbox and Radio */ | |
[type='checkbox'], | |
[type='radio'] { | |
@apply appearance-none p-0 inline-block align-middle bg-white dark:bg-gray-700 border-gray-300 dark:border-gray-600 border rounded-none h-4 w-4 text-blue-600 dark:text-blue-500; | |
print-color-adjust: exact; | |
} | |
[type='radio'] { | |
@apply rounded-full; | |
} | |
/* Checkbox and Radio Focus States */ | |
[type='checkbox']:focus, | |
[type='radio']:focus { | |
@apply outline-none ring-2 ring-blue-500 dark:ring-blue-500; | |
} | |
/* Checkbox and Radio Checked States */ | |
[type='checkbox']:checked, | |
[type='radio']:checked { | |
@apply border-transparent bg-current bg-no-repeat bg-center; | |
background-size: 0.65rem 0.65rem; | |
} | |
[type='checkbox']:checked { | |
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 12'%3E%3Cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M1 5.917 5.724 10.5 15 1.5'/%3E%3C/svg%3E"); | |
} | |
[type='radio']:checked { | |
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3E%3Ccircle cx='8' cy='8' r='3'/%3E%3C/svg%3E"); | |
background-size: 1rem 1rem; | |
} | |
/* File Input */ | |
[type='file'] { | |
@apply bg-transparent border-0 p-0; | |
} | |
[type='file']:focus { | |
@apply outline-none; | |
} | |
/* File Selector Button */ | |
input[type=file]::file-selector-button { | |
@apply text-white bg-gray-800 dark:bg-gray-600 border-0 font-medium text-sm cursor-pointer px-8 py-2.5 -ml-4 mr-4; | |
} | |
input[type=file]::file-selector-button:hover { | |
@apply bg-gray-700 dark:bg-gray-500; | |
} | |
} | |
@layer components { | |
/* Action Item Button */ | |
.action-item-button { | |
@apply py-2 px-3 text-sm font-medium no-underline text-gray-900 focus:outline-none bg-white rounded-md border border-gray-200 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:ring-gray-200 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700; | |
} | |
/* Index Data Table Toolbar */ | |
.index-data-table-toolbar { | |
@apply flex flex-col lg:flex-row gap-4 mb-4; | |
} | |
/* Scopes */ | |
.scopes { | |
@apply flex flex-wrap gap-1.5; | |
} | |
/* Index Button Group */ | |
.index-button-group { | |
@apply inline-flex flex-wrap items-stretch rounded-md; | |
} | |
.index-button-group > :where(*:not(:first-child)) { | |
@apply -ms-px my-0; | |
} | |
/* Index Button */ | |
.index-button { | |
@apply inline-flex items-center justify-center px-3 py-2 text-sm font-medium no-underline text-gray-900 bg-white border border-gray-200 hover:bg-gray-100 focus:z-10 focus:ring-2 focus:ring-blue-700 focus:text-blue-700 first:rounded-s-md last:rounded-e-md dark:bg-gray-900 dark:border-gray-700 dark:text-gray-100 dark:hover:text-gray-200 dark:hover:bg-gray-800 dark:focus:ring-blue-500 dark:focus:text-white; | |
} | |
.index-button-selected { | |
@apply bg-gray-100 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-800; | |
} | |
/* Scopes Count */ | |
.scopes-count { | |
@apply inline-flex items-center justify-center rounded-full bg-indigo-200/80 text-indigo-800 dark:bg-indigo-800 dark:text-indigo-200 px-1.5 py-1 text-xs font-normal ms-2 leading-none; | |
} | |
/* Paginated Collection */ | |
.paginated-collection { | |
@apply border border-gray-200 dark:border-gray-800 rounded-md shadow-sm overflow-hidden; | |
} | |
.paginated-collection-contents { | |
@apply overflow-x-auto; | |
} | |
.paginated-collection-pagination { | |
@apply p-2 lg:p-3 flex flex-col-reverse lg:flex-row gap-4 items-center justify-between; | |
} | |
.paginated-collection-footer { | |
@apply p-3 flex gap-2 items-center justify-between text-sm border-t border-gray-200 dark:border-gray-800; | |
} | |
/* Data Table */ | |
.data-table { | |
@apply w-full text-sm text-gray-800 dark:text-gray-300; | |
} | |
.data-table :where(thead > tr > th) { | |
@apply px-3 py-3.5 whitespace-nowrap font-semibold text-start text-xs uppercase border-b text-gray-700 bg-gray-50 dark:bg-gray-950/50 dark:border-gray-800 dark:text-white; | |
} | |
.data-table :where(tbody > tr) { | |
@apply border-b dark:border-gray-800; | |
} | |
.data-table :where(td) { | |
@apply px-3 py-4; | |
} | |
/* Data Table Resource Actions */ | |
.data-table-resource-actions { | |
@apply flex gap-2 items-center; | |
} | |
.data-table-resource-actions > a { | |
@apply text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white; | |
} | |
/* Data Sortable */ | |
.data-table :where(thead > tr > th[data-sortable]) { | |
@apply cursor-pointer; | |
} | |
.data-table-sorted-icon { | |
@apply invisible w-2 h-3; | |
} | |
.data-table :where(thead > tr > th[data-sort-direction]) .data-table-sorted-icon { | |
@apply visible; | |
} | |
.data-table :where(thead > tr > th[data-sort-direction="asc"]) .data-table-sorted-icon { | |
@apply rotate-180; | |
} | |
/* Filters Form */ | |
.filters-form { | |
@apply text-sm mb-6; | |
} | |
.filters-form-title { | |
@apply text-gray-700 dark:text-gray-200 font-bold text-lg mb-4; | |
} | |
.filters-form :where(.label) { | |
@apply block mb-1.5 text-sm; | |
} | |
.filters-form-input-group { | |
@apply grid grid-cols-1 sm:grid-cols-2 gap-4; | |
} | |
.filters-form-input-group :where(.input) { | |
@apply w-full; | |
} | |
.filters-form-input-group :where(.label) { | |
@apply block mb-1 text-sm font-medium text-gray-700 dark:text-gray-300; | |
} | |
.filters-form-input-group :where(.hint) { | |
@apply mt-1 text-sm text-gray-500 dark:text-gray-400; | |
} | |
.filters-form-field { | |
@apply mb-4; | |
} | |
.filters-form-buttons { | |
@apply flex gap-2 items-center; | |
} | |
.filters-form-submit { | |
@apply min-w-[6rem] font-bold text-white bg-blue-600 hover:bg-blue-700 focus:ring-4 focus:outline-none focus:ring-blue-300 rounded-md px-3 py-2 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800 cursor-pointer; | |
} | |
.filters-form-clear { | |
@apply rounded-md px-3 py-2 font-semibold text-gray-700 hover:bg-gray-100 no-underline dark:text-gray-400 dark:hover:bg-inherit dark:hover:text-gray-100 dark:focus:ring-blue-800; | |
} | |
/* Panel */ | |
.panel { | |
@apply mb-6 border border-gray-200 rounded-md shadow-sm dark:border-gray-800; | |
} | |
.panel-title { | |
@apply font-bold bg-gray-100 dark:bg-gray-900 rounded-t-md p-3; | |
} | |
.panel-body { | |
@apply py-5 px-3; | |
} | |
/* Attributes Table */ | |
.attributes-table { | |
@apply overflow-hidden mb-6 border border-gray-200 rounded-md shadow-sm dark:border-gray-800; | |
} | |
.attributes-table :where(tbody > tr > th) { | |
@apply w-32 sm:w-40 text-start text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-800/60 dark:text-gray-300; | |
} | |
.attributes-table :where(tbody > tr > th, tbody > tr > td) { | |
@apply p-3; | |
} | |
/* Status Tag */ | |
.status-tag { | |
@apply bg-gray-200 text-gray-600 dark:bg-gray-700 dark:text-gray-400 inline-flex items-center rounded-full text-sm font-medium px-2.5 py-0.5 whitespace-nowrap; | |
} | |
.status-tag:where([data-status=yes]) { | |
@apply bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300; | |
} | |
/* Tabs */ | |
.tabs-nav { | |
@apply flex flex-wrap mb-2 text-sm font-medium text-center border-b border-gray-200 dark:border-gray-700; | |
} | |
.tabs-nav > :where(a) { | |
@apply block p-4 border-b-2 border-transparent rounded-t-md hover:text-gray-600 dark:hover:text-gray-300 no-underline; | |
} | |
.tabs-content { | |
@apply p-4 mb-6; | |
} | |
/* Formtastic */ | |
.formtastic { | |
@apply text-sm; | |
} | |
.formtastic :where(.fieldset-title, .has-many-fields-title) { | |
@apply block w-full mb-3 border-b font-bold text-lg; | |
} | |
.formtastic :where(.label) { | |
@apply block mb-1.5 text-gray-700 dark:text-gray-300; | |
} | |
.formtastic :where(.label abbr) { | |
@apply ms-1 no-underline; | |
} | |
.formtastic :where(.input) { | |
@apply py-3; | |
} | |
.formtastic :where(.choice) { | |
@apply mb-1; | |
} | |
.formtastic :where(.boolean label, .choice label) { | |
@apply flex gap-2 items-center; | |
} | |
.formtastic :where(.fragments-group) { | |
@apply inline-flex flex-wrap gap-1; | |
} | |
.formtastic :where(.fragment label) { | |
@apply sr-only; | |
} | |
.formtastic :where(.inline-hints) { | |
@apply text-gray-500 mt-2; | |
} | |
.formtastic :where(.errors) { | |
@apply p-4 mb-6 rounded-md space-y-2 bg-red-50 text-red-800 dark:bg-red-800 dark:text-red-300; | |
} | |
.formtastic :where(.errors > li) { | |
@apply list-disc ms-4; | |
} | |
.formtastic :where(.inline-errors) { | |
@apply font-bold mt-2 text-red-600 dark:text-red-300; | |
} | |
.formtastic :where(.error [type=email], .error [type=number], .error [type=password], .error [type=tel], .error [type=text], .error [type=url], .error textarea) { | |
@apply border-red-500; | |
} | |
.formtastic :where(.buttons, .actions) { | |
@apply mt-3; | |
} | |
.formtastic :where(.actions > ol) { | |
@apply flex items-center gap-6; | |
} | |
.formtastic :where([type=submit], [type=button], button) { | |
@apply font-bold text-white bg-blue-600 hover:bg-blue-700 focus:ring-4 focus:outline-none focus:ring-blue-300 rounded-lg px-4 py-2 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800 cursor-pointer; | |
} | |
.formtastic :where(.actions .cancel-link) { | |
@apply font-semibold leading-6 text-gray-900 dark:text-white no-underline; | |
} | |
.formtastic :where(.has-many-add) { | |
@apply inline-block py-3; | |
} | |
.formtastic :where(.has-many-container) { | |
@apply space-y-8; | |
} | |
.formtastic :where(.has-many-fields) { | |
@apply ps-3 border-s-4 border-s-gray-200 dark:border-s-gray-700; | |
} | |
/* Select Elements */ | |
select { | |
@apply bg-no-repeat bg-[right_0.75rem_center] bg-[length:0.75em_0.75em] pr-8; | |
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 10 6'%3E%3Cpath stroke='%236B7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m1 1 4 4 4-4'/%3E%3C/svg%3E"); | |
print-color-adjust: exact; | |
} | |
.dark select { | |
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 10 6'%3E%3Cpath stroke='%23D1D5DB' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m1 1 4 4 4-4'/%3E%3C/svg%3E"); | |
} | |
/* Fix for Formtastic fragments (date/time selects) */ | |
.fragments-group, | |
.fragments { | |
@apply flex flex-row gap-2 items-center; | |
} | |
.fragment { | |
@apply flex-shrink-0; | |
} | |
.fragments-group select, | |
.fragments select { | |
@apply w-auto min-w-[5rem]; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const execSync = require("node:child_process").execSync; | |
const activeAdminPath = execSync("bundle show activeadmin", { | |
encoding: "utf-8", | |
}).trim(); | |
module.exports = { | |
content: [ | |
`${activeAdminPath}/vendor/javascript/flowbite.js`, | |
`${activeAdminPath}/plugin.js`, | |
`${activeAdminPath}/app/views/**/*.{arb,erb,html,rb}`, | |
"./app/admin/**/*.{arb,erb,html,rb}", | |
"./app/views/active_admin/**/*.{arb,erb,html,rb}", | |
"./app/views/admin/**/*.{arb,erb,html,rb}", | |
"./app/views/layouts/active_admin*.{erb,html}", | |
"./app/assets/controllers/active_admin/**/*.js", | |
], | |
darkMode: "selector" | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment