Tailwind CSS

Lesson 01

Utility composition readability

Group Tailwind utilities by purpose so reviewers can understand layout, color, state, and motion without decoding one long string.

Good Code

PrimaryButton.tsx
type PrimaryButtonProps = {
  children: React.ReactNode;
  disabled?: boolean;
};

const baseClasses =
  // Layout and sizing live together.
  "inline-flex h-10 items-center justify-center gap-2 rounded-md px-4 text-sm font-medium";
const toneClasses =
  // Color, shadow, and hover tone live together.
  "bg-sky-600 text-white shadow-sm hover:bg-sky-500";
const stateClasses =
  // Focus and disabled styles live together.
  "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sky-300 disabled:cursor-not-allowed disabled:opacity-50";

export function PrimaryButton({ children, disabled }: PrimaryButtonProps) {
  return (
    <button
      type="button"
      disabled={disabled}
      className={[baseClasses, toneClasses, stateClasses].join(" ")}
    >
      {children}
    </button>
  );
}

Bad Code

PrimaryButton.tsx
export function PrimaryButton({ children, disabled }) {
  // One long className mixes layout, color, state, and a duplicate radius.
  return (
    <button
      disabled={disabled}
      className="px-4 text-white rounded-md h-10 bg-sky-600 text-sm hover:bg-sky-500 inline-flex shadow-sm items-center disabled:opacity-50 justify-center font-medium focus-visible:ring-2 gap-2 rounded-lg disabled:cursor-not-allowed focus-visible:ring-sky-300"
    >
      {children}
    </button>
  );
}

Review Notes

What to review

Good Code

The good version separates base structure, visual tone, and interaction states. A reviewer can scan each concern and spot duplicate or conflicting utilities quickly.

Bad Code

The bad version ships one long class list with mixed concerns and an accidental rounded value conflict. The UI may render, but the intent is harder to review.

Takeaways

  • A utility-first class list still needs structure; readable grouping makes design intent reviewable.