Skip to content

Instantly share code, notes, and snippets.

@lopezjurip
Last active September 25, 2023 16:52
Show Gist options
  • Save lopezjurip/4e41475f386175d464fb0965e7bcc479 to your computer and use it in GitHub Desktop.
Save lopezjurip/4e41475f386175d464fb0965e7bcc479 to your computer and use it in GitHub Desktop.
Tailwind buttons and input plugins (btn-*, input-*)
import { clsx } from "clsx"
export function UserFieldset({ disabled, className, ...props } : React.ComponentProps<fieldset>) {
// fieldset has built-in disabled management for inputs and buttons inside of it.
return (
<fieldset className={clsx("grid grid-cols-1 gap-4", className)} disabled={disabled} {...props}>
<div className="col-span-1">
<label htmlFor="name">Name</label/>
<input id="name" name="name" className={clsx("input input-4 sm:input-3 input-blue", "w-full mt-1")} />
</div>
<div className="col-span-1">
<label htmlFor="email">Email</label/>
<input id="email" name="email" className={clsx("input input-4 sm:input-3 input-blue", "w-full mt-1")} />
</div>
<button type="submit" className={clsx("btn btn-shadow btn-rounded btn-green btn-4 sm:btn-3", "col-span-full")}>
Save
</button/>
<button type="reset" className={clsx("btn btn-shadow btn-rounded btn-red-soft btn-4 sm:btn-3", "col-span-full")}>
Reset
</button/>
</fieldset>
);
}
import type { Config } from "tailwindcss";
import * as plugins from "./tailwind.plugins";
const config: Config = {
content: [
// ...
],
plugins: [
require("@tailwindcss/forms"),
plugins.buttons(),
plugins.inputs(),
],
};
export default config;
import { withOptions } from "tailwindcss/plugin";
import type { PluginAPI } from "tailwindcss/types/config";
function COLORS(theme: PluginAPI["theme"]) {
const SKIP = new Set(["inherit", "current", "white", "black", "transparent"]);
return Object.keys(theme("colors")).filter((color) => !SKIP.has(color));
}
export const buttons = withOptions<void>(() => {
return function ({ addComponents, theme, e }) {
addComponents({
".btn": {
"@apply relative": {},
"@apply inline-flex flex-row items-center justify-center": {},
"@apply border border-transparent": {},
"@apply transition": {},
"@apply ring-offset-white dark:ring-offset-gray-900": {},
// Interactions
"@apply hover:outline-none hover:ring-2 hover:ring-offset-2": {},
"@apply focus:outline-none focus:ring-2 focus:ring-offset-2": {},
// States
"@apply disabled:opacity-75 disabled:cursor-default disabled:pointer-events-none": {},
"@apply aria-disabled:opacity-75 aria-disabled:cursor-default aria-disabled:pointer-events-none": {}, // for <a aria-disabled="true" /> because "disabled" is not valid.
},
".btn-rounded": {
"@apply rounded": {},
},
".btn-shadow": {
"@apply shadow-sm": {},
},
});
addComponents({
".btn-1": {
"@apply px-2.5 py-1.5 text-xs font-medium": {},
},
".btn-2": {
"@apply px-3 py-2 text-sm leading-4 font-medium": {},
},
".btn-3": {
"@apply px-4 py-2 text-sm font-medium": {},
},
".btn-4": {
"@apply px-4 py-2 text-base font-medium": {},
},
".btn-5": {
"@apply px-6 py-3 text-base font-medium": {},
},
});
addComponents({
[".btn-white"]: {
["@apply text-skin-500 dark:text-skin-500"]: {},
["@apply bg-white hover:bg-gray-50"]: {},
["@apply focus:ring-white"]: {},
},
[".btn-white-outline"]: {
["@apply text-white dark:text-white"]: {},
["@apply bg-transparent hover:bg-white dark:hover:bg-white"]: {},
["@apply border border-white"]: {},
["@apply focus:ring-white"]: {},
},
[".btn-gray"]: {
["@apply text-white dark:text-white"]: {},
["@apply bg-gray-600 hover:bg-gray-700"]: {},
["@apply focus:ring-gray-500"]: {},
},
[".btn-gray-outline"]: {
["@apply text-gray-700 dark:text-gray-300"]: {},
["@apply bg-white hover:bg-gray-50 dark:bg-gray-900 dark:hover:bg-gray-800"]: {},
["@apply border border-gray-300 dark:border-gray-600"]: {},
["@apply focus:ring-skin-500"]: {},
},
});
for (const color of COLORS(theme)) {
addComponents({
[`.btn-${color}`]: {
["@apply text-white dark:text-white"]: {},
[`@apply bg-${color}-600`]: {},
[`@apply hover:bg-${color}-700`]: {},
[`@apply focus:ring-${color}-500`]: {},
},
[`.btn-${color}-outline`]: {
[`@apply text-${color}-500 dark:text-${color}-500`]: {},
["@apply bg-transparent"]: {},
[`@apply border border-${color}-500`]: {},
[`@apply hover:bg-${color}-50 dark:hover:bg-${color}-900`]: {},
[`@apply focus:ring-${color}-500`]: {},
},
[`.btn-${color}-soft`]: {
[`@apply text-${color}-900`]: {},
[`@apply bg-${color}-100`]: {},
[`@apply hover:bg-${color}-200`]: {},
[`@apply focus:ring-${color}-500`]: {},
},
});
}
};
});
export const inputs = withOptions<void>(() => {
return function ({ addComponents, theme, e }) {
addComponents({
[".input"]: {
"@apply block": {},
"@apply border border-gray-300 dark:border-gray-600": {},
"@apply bg-white dark:bg-gray-900": {},
"@apply transition": {},
"@apply ring-offset-white dark:ring-offset-gray-900": {},
"@apply text-gray-900 dark:text-gray-100 placeholder-gray-400 dark:placeholder-gray-600": {},
"@apply shadow-sm rounded": {},
"@apply disabled:opacity-75": {},
"@apply disabled:!ring-0": {}, // ! is "!important" to force disable rings.
},
});
addComponents({
[".input-1"]: {
"@apply px-2.5 py-1.5 text-xs font-medium": {},
},
[".input-2"]: {
"@apply px-3 py-2 text-sm leading-4": {},
},
[".input-3"]: {
"@apply px-3 py-2 text-sm": {},
},
[".input-4"]: {
"@apply px-3 py-2 text-base": {},
},
[".input-5"]: {
"@apply px-5 py-3 text-base": {},
},
});
for (const color of iterColors(theme)) {
addComponents({
[`.input-${color}`]: {
["@apply hover:outline-none hover:ring-2 hover:ring-offset-2"]: {},
[`@apply focus:outline-none focus:ring-0 focus:border-${color}-500 focus:ring-${color}-500`]: {},
},
});
}
};
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment