tailwind-expert — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited tailwind-expert (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
CSS-first configuration, theme customization, and utility-first best practices.
Tailwind v4 moves configuration from tailwind.config.js into CSS using @theme.
/* app/globals.css */
@import "tailwindcss";
@theme {
/* Colors */
--color-brand-50: oklch(0.97 0.02 260);
--color-brand-500: oklch(0.55 0.22 260);
--color-brand-900: oklch(0.25 0.12 260);
/* Typography */
--font-sans: "Inter Variable", system-ui, sans-serif;
--font-display: "Cal Sans", sans-serif;
/* Spacing base (multiplied by Tailwind's scale) */
--spacing: 0.25rem;
/* Border radius */
--radius-card: 0.75rem;
--radius-button: 0.5rem;
/* Shadows */
--shadow-card: 0 4px 24px oklch(0 0 0 / 0.08);
--shadow-elevated: 0 8px 32px oklch(0 0 0 / 0.12);
/* Custom breakpoints */
--breakpoint-3xl: 1920px;
/* Animations */
--animate-fade-in: fade-in 0.2s ease-out;
--animate-slide-up: slide-up 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes slide-up {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}// All these are auto-generated from @theme above
<div className="bg-brand-500 text-white rounded-card shadow-card animate-fade-in">
<h1 className="font-display text-3xl">Welcome</h1>
</div>
<div className="3xl:grid-cols-6 grid grid-cols-2">
{/* Custom breakpoint just works */}
</div>- // tailwind.config.js (v3)
- module.exports = {
- theme: {
- extend: {
- colors: { brand: { 500: '#3b82f6' } }
- }
- }
- }
+ /* globals.css (v4) */
+ @import "tailwindcss";
+ @theme {
+ --color-brand-500: #3b82f6;
+ }- <div className="bg-opacity-50 bg-black">
+ <div className="bg-black/50">- <div className="ring-2 ring-offset-2 ring-offset-white">
+ <div className="outline outline-2 outline-offset-2">- // PostCSS config (v3) needed autoprefixer, postcss-import explicitly
- module.exports = { plugins: { tailwindcss: {}, autoprefixer: {} } }
+ // v4 — single plugin, no autoprefixer needed (built-in)
+ export default { plugins: { '@tailwindcss/postcss': {} } }npx @tailwindcss/upgrade/* v4 — dark mode variant works out of the box */
@import "tailwindcss";
/* Custom dark mode selector (default uses media query) */
@custom-variant dark (&:where(.dark, .dark *));// Component using dark: variant
<div className="bg-white dark:bg-zinc-900 text-zinc-900 dark:text-zinc-50">
Content adapts automatically
</div>
// Theme toggle (class-based)
function ThemeToggle() {
const [isDark, setIsDark] = useState(false)
useEffect(() => {
document.documentElement.classList.toggle('dark', isDark)
}, [isDark])
return <button onClick={() => setIsDark(!isDark)}>Toggle theme</button>
}import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const badgeVariants = cva(
"inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium",
{
variants: {
variant: {
default: "bg-brand-100 text-brand-900",
success: "bg-green-100 text-green-900",
warning: "bg-amber-100 text-amber-900",
danger: "bg-red-100 text-red-900",
},
},
defaultVariants: { variant: "default" },
}
)
interface BadgeProps
extends React.HTMLAttributes<HTMLSpanElement>,
VariantProps<typeof badgeVariants> {}
export function Badge({ className, variant, ...props }: BadgeProps) {
return <span className={cn(badgeVariants({ variant }), className)} {...props} />
}cn() Utility (Required for Every Project)// lib/utils.ts
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
// Why both clsx AND twMerge:
// clsx handles conditional classes
// twMerge resolves Tailwind class CONFLICTS (e.g. "p-2 p-4" → "p-4")
cn("p-2", isActive && "p-4", className) // correctly resolves to p-4 if both present// Mobile-first — base styles apply to smallest screens
<div className="
grid grid-cols-1 gap-4
sm:grid-cols-2
lg:grid-cols-3
xl:grid-cols-4
">
// Container queries (v4 supports natively)
<div className="@container">
<div className="@sm:flex @lg:grid @lg:grid-cols-2">
{/* Responds to PARENT container size, not viewport */}
</div>
</div>// ❌ Arbitrary value — not reusable, not in design system
<div className="text-[#3b82f6] p-[18px]">
// ✅ Theme token — consistent, reusable, themeable
<div className="text-brand-500 p-4">@apply Sparingly/* Use @apply only for truly repeated, complex patterns */
.btn-base {
@apply inline-flex items-center justify-center rounded-lg font-medium
transition-colors focus-visible:outline-none focus-visible:ring-2
disabled:pointer-events-none disabled:opacity-50;
}
/* Prefer component-level composition (CVA) over @apply for variants *//* v4 auto-detects content — no manual content[] config needed in most cases */
@import "tailwindcss";
/* Only needed for non-standard file locations */
@source "../node_modules/@acme/ui/**/*.{js,jsx}";// Truncate text with ellipsis
<p className="truncate">Long text that gets cut off...</p>
// Multi-line truncate
<p className="line-clamp-3">Long text across multiple lines...</p>
// Aspect ratio containers
<div className="aspect-video bg-zinc-100 rounded-lg overflow-hidden">
<img className="h-full w-full object-cover" />
</div>
// Sticky header with backdrop blur
<header className="sticky top-0 z-50 bg-white/80 backdrop-blur-md border-b">
// Scroll snap carousel
<div className="flex overflow-x-auto snap-x snap-mandatory gap-4">
<div className="snap-center shrink-0 w-80">Card 1</div>
</div>
// Custom scrollbar (v4 supports natively)
<div className="overflow-y-auto [&::-webkit-scrollbar]:w-2
[&::-webkit-scrollbar-thumb]:bg-zinc-300 [&::-webkit-scrollbar-thumb]:rounded-full">tailwind.config.js (v4 paradigm)bg-opacity-50 (removed in v4)text-brand-500 not text-[#3b82f6]sm:, md:, lg:~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.