agentsclimarketplace

Tailwind

Skill MARUCIE/openclaw-foundry/web/public/packs/spellbook-frontend-engineer/skills/tailwind

The curated AI Agent skill marketplace — 37K+ vetted skills, S/A/B/C ratings, deploy anywhere

Install
npx -y skills add MARUCIE/openclaw-foundry --skill tailwind

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Use when composing Tailwind utilities, building responsive or dark-mode layouts, defining component variants with cva, resolving conflicting classes with tailwind-merge, or configuring a custom theme.

SKILL.md

13.6 KB, as published. Nobody here has run it

是什么

Tailwind CSS 是 utility-first(工具类优先)的 CSS 框架,把样式写在标签上而不是另起一个 .css 文件。 用它的效果是:开发与设计的距离被拉到最近,重构样式时不再担心牵一发动全身。

怎么用

  1. 先把设计语言(颜色、间距、字号)配置进 tailwind.config,让设计 token 由配置而非约定承载。
  2. 用响应式前缀(sm: md: lg:)一行写完多端布局,让媒体查询不再四处散落。
  3. 通过 dark: 变体处理深色模式,让主题切换的成本接近为零。
  4. 用 cva(class-variance-authority)封装组件变体,让重复的类名组合变成可命名的 API。
  5. 用 tailwind-merge 解决类名冲突,让覆盖与合并的优先级可预期。

架构图

flowchart LR
  设计token --> 配置文件
  配置文件 --> 工具类
  工具类 --> 响应式变体
  响应式变体 --> 深色模式
  深色模式 --> 组件变体

Tailwind CSS Patterns

Utility-first CSS with Tailwind v3/v4, component variants, and the cn() pattern.

When to Activate

  • Composing utilities for layout (flex, grid, spacing, sizing)
  • Responsive design with breakpoint prefixes
  • Dark mode with dark: variants
  • Building component variants with cva (class-variance-authority)
  • Deduplicating conflicting classes with tailwind-merge
  • Configuring custom colors, fonts, or spacing in tailwind.config
  • Animating with transition, animate-*, or custom keyframes

Core Utility Patterns

Spacing and sizing

// Padding / margin — p-{n}, m-{n}, px-{n}, py-{n}, pt/pr/pb/pl
<div className="p-4 mt-2 px-6 py-3">          {/* p=16px, mt=8px, px=24px, py=12px */}

// Width / height
<div className="w-full max-w-lg h-screen min-h-0">
<div className="w-[340px] h-[calc(100vh-64px)]">  {/* arbitrary values */}

// Margin auto (centering)
<div className="mx-auto max-w-2xl">

Flexbox

<div className="flex items-center justify-between gap-4">
<div className="flex flex-col gap-2">
<div className="flex flex-wrap gap-2">
<div className="flex items-start justify-center">

// Flex children
<div className="flex-1 min-w-0">          {/* grow, allow shrinking below content size */}
<div className="flex-none w-16">          {/* fixed width */}
<div className="flex-shrink-0">           {/* don't shrink */}

Grid

<div className="grid grid-cols-3 gap-6">
<div className="grid grid-cols-[1fr_2fr_1fr] gap-4">    {/* arbitrary template */}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">

{/* Spanning */}
<div className="col-span-2">
<div className="col-start-2 col-end-4">

Typography

<h1 className="text-3xl font-bold tracking-tight text-gray-900">
<p  className="text-base text-gray-600 leading-relaxed">
<span className="text-sm font-medium text-blue-600 uppercase tracking-wide">

// Truncate long text
<p className="truncate">                    {/* single line ellipsis */}
<p className="line-clamp-3">               {/* clamp to 3 lines */}

Responsive Design

Tailwind is mobile-first — unprefixed utilities apply to all sizes, prefixes override at that breakpoint and up.

// Breakpoints: sm(640px), md(768px), lg(1024px), xl(1280px), 2xl(1536px)
<div className="
  grid
  grid-cols-1           {/* mobile: 1 column */}
  sm:grid-cols-2        {/* sm+: 2 columns */}
  lg:grid-cols-3        {/* lg+: 3 columns */}
">

// Hide/show
<div className="hidden md:block">      {/* hidden on mobile, visible md+ */}
<div className="block md:hidden">      {/* visible on mobile, hidden md+ */}

// Text size changes per breakpoint
<h1 className="text-xl md:text-3xl lg:text-5xl font-bold">

Dark Mode

// tailwind.config — use 'class' strategy (toggle via class on <html>)
darkMode: 'class'

// Next.js — use next-themes
<html className={theme === 'dark' ? 'dark' : ''}>

// In components
<div className="bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100">
<button className="bg-blue-600 dark:bg-blue-500 hover:bg-blue-700 dark:hover:bg-blue-400">

State Variants

// Hover, focus, active
<button className="
  bg-blue-600
  hover:bg-blue-700
  focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2
  active:bg-blue-800
  disabled:opacity-50 disabled:cursor-not-allowed
">

// Group hover — parent hover affects children
<div className="group">
  <div className="opacity-0 group-hover:opacity-100 transition-opacity">
    Shows on parent hover
  </div>
</div>

// Peer — sibling state
<input className="peer" />
<p className="hidden peer-focus:block">Focused hint</p>
<p className="hidden peer-invalid:block text-red-500">Invalid input</p>

The cn() Helper

cn() merges class names and resolves Tailwind conflicts (last one wins with tailwind-merge).

npm install clsx tailwind-merge
// lib/utils.ts
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs))
}

// Usage — conditional classes, no conflicts
<div className={cn(
  "rounded-lg border p-4",
  isActive && "border-blue-500 bg-blue-50",
  isError && "border-red-500 bg-red-50",
  className,    // external overrides win
)}>

// Conflict resolution — twMerge picks the last conflicting utility
cn("px-4 px-6")         // → "px-6"
cn("text-red-500", "text-blue-500")  // → "text-blue-500"

Component Variants with cva

cva (class-variance-authority) defines components with typed variant props.

npm install class-variance-authority
// components/ui/button.tsx
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"

const buttonVariants = cva(
  // Base styles — always applied
  "inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
  {
    variants: {
      variant: {
        default:     "bg-blue-600 text-white hover:bg-blue-700",
        destructive: "bg-red-600 text-white hover:bg-red-700",
        outline:     "border border-gray-300 bg-white hover:bg-gray-50",
        ghost:       "hover:bg-gray-100 hover:text-gray-900",
        link:        "text-blue-600 underline-offset-4 hover:underline",
      },
      size: {
        sm:      "h-8 px-3 text-sm",
        default: "h-10 px-4 py-2",
        lg:      "h-12 px-8 text-lg",
        icon:    "h-10 w-10",
      },
    },
    defaultVariants: {
      variant: "default",
      size: "default",
    },
  },
)

interface ButtonProps
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
    VariantProps<typeof buttonVariants> {
  asChild?: boolean
}

export function Button({ className, variant, size, ...props }: ButtonProps) {
  return (
    <button
      className={cn(buttonVariants({ variant, size, className }))}
      {...props}
    />
  )
}

// Usage
<Button>Default</Button>
<Button variant="destructive" size="lg">Delete</Button>
<Button variant="outline" size="sm">Cancel</Button>

Custom Theme Config

// tailwind.config.ts
import type { Config } from "tailwindcss"

export default {
  content: ["./src/**/*.{ts,tsx}"],
  darkMode: "class",
  theme: {
    extend: {
      colors: {
        brand: {
          50:  "#eff6ff",
          500: "#3b82f6",
          900: "#1e3a5f",
        },
        // CSS variable-based (works with shadcn/ui)
        background: "hsl(var(--background))",
        foreground: "hsl(var(--foreground))",
        primary: {
          DEFAULT: "hsl(var(--primary))",
          foreground: "hsl(var(--primary-foreground))",
        },
      },
      fontFamily: {
        sans: ["var(--font-inter)", "sans-serif"],
        mono: ["var(--font-mono)", "monospace"],
      },
      borderRadius: {
        lg: "var(--radius)",
        md: "calc(var(--radius) - 2px)",
      },
      keyframes: {
        "fade-in": {
          from: { opacity: "0", transform: "translateY(4px)" },
          to:   { opacity: "1", transform: "translateY(0)" },
        },
        "spin-slow": {
          to: { transform: "rotate(360deg)" },
        },
      },
      animation: {
        "fade-in":   "fade-in 0.2s ease-out",
        "spin-slow": "spin-slow 3s linear infinite",
      },
    },
  },
} satisfies Config

Animations and Transitions

// Transitions — apply to base element, variants add what changes
<button className="
  transition-colors duration-200 ease-in-out
  bg-blue-600 hover:bg-blue-700
">

// All properties
<div className="transition-all duration-300">

// Transform
<div className="transition-transform hover:scale-105 hover:-translate-y-1">
<div className="hover:rotate-3 transition-transform duration-200">

// Opacity fade
<div className={cn(
  "transition-opacity duration-300",
  isVisible ? "opacity-100" : "opacity-0",
)}>

// Built-in animations
<div className="animate-spin">       {/* loading spinner */}
<div className="animate-pulse">      {/* skeleton loading */}
<div className="animate-bounce">
<div className="animate-fade-in">    {/* custom from config */}

Common Component Patterns

Card

<div className="rounded-xl border border-gray-200 bg-white shadow-sm dark:border-gray-800 dark:bg-gray-950">
  <div className="p-6">
    <h3 className="text-lg font-semibold text-gray-900 dark:text-gray-100">Title</h3>
    <p className="mt-2 text-sm text-gray-500 dark:text-gray-400">Description</p>
  </div>
  <div className="border-t border-gray-200 px-6 py-4 dark:border-gray-800">
    <Button>Action</Button>
  </div>
</div>

Input

<input className="
  w-full rounded-md border border-gray-300 bg-white px-3 py-2 text-sm
  placeholder:text-gray-400
  focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500
  disabled:cursor-not-allowed disabled:bg-gray-50 disabled:text-gray-500
  dark:border-gray-700 dark:bg-gray-900 dark:text-gray-100
" />

Badge

const badgeVariants = cva(
  "inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium",
  {
    variants: {
      variant: {
        default: "bg-gray-100 text-gray-800",
        success: "bg-green-100 text-green-800",
        warning: "bg-yellow-100 text-yellow-800",
        error:   "bg-red-100 text-red-800",
        info:    "bg-blue-100 text-blue-800",
      },
    },
    defaultVariants: { variant: "default" },
  },
)

Modal overlay

{/* Backdrop */}
<div className="fixed inset-0 z-50 bg-black/50 backdrop-blur-sm" onClick={onClose} />

{/* Panel */}
<div className="fixed left-1/2 top-1/2 z-50 w-full max-w-md -translate-x-1/2 -translate-y-1/2
  rounded-xl bg-white p-6 shadow-xl dark:bg-gray-900">
  ...
</div>

Tailwind v4 (New in 2025)

/* No tailwind.config — configure in CSS */
@import "tailwindcss";

@theme {
  --color-brand-500: #3b82f6;
  --font-sans: "Inter", sans-serif;
  --radius-lg: 0.75rem;
}

/* Dark mode via media query by default (no class strategy needed) */
/* Use @variant dark { ... } for custom dark styles */

v4 changes: config moves to CSS @theme, faster build, no PostCSS required, native cascade layers.


Red Flags

  • String template literals for conditional classes`bg-${color} p-4` generates class names at runtime that Tailwind's static scanner never sees and therefore never includes in the output CSS; use only complete class names from the source, with cn() for conditions
  • Conflicting utilities without tailwind-mergeclassName="px-4 px-6" applies both; the last rule in the stylesheet wins (not the last in the string), which is unpredictable; twMerge resolves conflicts correctly by keeping only the last conflicting utility
  • Repeating variant logic in if/else stringsclassName={isActive ? "bg-blue-600 text-white rounded-lg px-4" : "bg-gray-100 text-gray-900 rounded-lg px-4"} duplicates base classes; use cva to declare base + variant styles separately
  • Arbitrary values that are repeatedw-[340px] appearing in five components belongs in theme.extend as a named token; arbitrary values are for one-offs only
  • Missing dark: variants on color utilities — adding bg-white text-gray-900 without corresponding dark:bg-gray-900 dark:text-gray-100 makes dark mode look broken; pair every color with its dark-mode variant at the same time
  • No focus-visible:ring on interactive elements — keyboard users navigating with Tab have no visible indicator when focus styles are absent; every button and link needs a focus-visible:ring-* class for WCAG AA compliance
  • Importing Tailwind classes from JS variables — storing class names in a variable and spreading them defeats the static content scanner; Tailwind purges any class it cannot find as a complete string in the source at build time

Checklist

  • cn() used instead of string concatenation for conditional classes
  • cva used for components with multiple variants (not if/else strings)
  • Responsive classes ordered mobile-first (base → sm: → md: → lg:)
  • group / peer used for parent/sibling state instead of JS state
  • dark: variants added alongside every color utility
  • transition-colors / transition-all on interactive elements
  • focus-visible:ring on all interactive elements for keyboard accessibility
  • Arbitrary values (w-[340px]) used sparingly — extend theme for repeated values
  • disabled: variants on form elements to style disabled state

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.