Skip to content

Instantly share code, notes, and snippets.

@amkisko
Created April 19, 2025 09:41
Show Gist options
  • Save amkisko/af1b2f7dc4f0f941437ea16400277864 to your computer and use it in GitHub Desktop.
Save amkisko/af1b2f7dc4f0f941437ea16400277864 to your computer and use it in GitHub Desktop.
ActiveAdmin v4 tailwind v4 compat
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")
@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-white dark:bg-gray-700 border-gray-500 dark:border-gray-600 border rounded-md px-3 py-2 text-gray-900 dark:text-white;
--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-600 dark:ring-blue-500 border-blue-600 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-500 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-600 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 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(.input) {
@apply py-3;
}
.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(.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 text-gray-700 hover:text-gray-900 dark:text-gray-300 dark:hover:text-white no-underline;
}
/* 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");
}
}
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