Frontend Design and UI Library Learning Notes

Frontend Design and UI Library Learning Notes

Frontend design is the practical skill of turning product intent into screens that are clear, usable, accessible, responsive, and maintainable.

Product goal
  -> content and user workflow
  -> layout, hierarchy, typography, color, spacing, states
  -> accessible components
  -> implementation with CSS and UI libraries
  -> testing, review, and iteration

Learn design foundations before collecting libraries. A component library can make a good interface faster, but it cannot decide the information hierarchy, empty states, loading states, keyboard behavior, or the right density for your product.

Read react.md before this note. Read color.md for palette, contrast, dark mode, and theme-token guidance. Read tailwind.md for Tailwind-specific styling notes and shadcn.md for shadcn/ui component workflow. Read ui-patterns.md when turning foundations into real product screens. Read state-management.md when UI state starts spreading across components. Read nextjs.md when the design crosses server/client, routing, metadata, images, and deployment boundaries.

1. What Good Frontend Design Optimizes For

Good UI is not decoration. It helps a user understand what is happening and what to do next.

Clarity       The screen has an obvious job.
Hierarchy    Important things look important.
Density      Information fits the task without feeling cramped.
Feedback     Loading, success, error, disabled, and empty states are visible.
Consistency  Similar actions and data use similar patterns.
Access       Keyboard, screen reader, contrast, touch, and motion needs are handled.
Speed        The interface feels responsive and avoids unnecessary client work.
Maintenance  Components, tokens, and styles can evolve without chaos.

Before choosing a library, answer:

Who uses this screen?
What are they trying to finish?
What information must they compare?
What action is most important?
What can go wrong?
What should be visible on a small screen?
What state belongs in the URL, server, form, or component?

2. Foundations Before Libraries

Layout

Use semantic HTML first, then CSS layout.

Normal flow       documents, prose, simple forms
Flexbox           one-dimensional alignment: nav bars, button rows, toolbars
Grid              two-dimensional layout: pages, galleries, dashboards
Container queries component layout based on available space
Positioning       overlays, sticky headers, badges, anchored UI

Avoid absolute positioning for normal page layout. It often breaks as soon as content length, localization, zoom, or viewport size changes.

Responsive Design

Responsive design means the layout adapts to available space, not just to device names.

.shell {
  display: grid;
  gap: 1rem;
  grid-template-columns: minmax(0, 1fr);
}

@media (width >= 64rem) {
  .shell {
    grid-template-columns: 16rem minmax(0, 1fr);
  }
}

Use fluid containers and content-based breakpoints. Check long labels, empty data, many rows, zoomed text, and narrow screens.

Typography

Typography is interface structure.

Body text       16px is a safe default.
Line height     about 1.4-1.7 for prose; tighter for dense UI.
Headings        show hierarchy, not just size.
Labels          short, close to their controls, and easy to scan.
Numbers         align and format consistently in dashboards.

Do not use huge headings inside dense dashboards, cards, tables, or sidebars. Match type size to the container.

Spacing

Use a small spacing scale instead of random values.

4px   tiny icon/text gap
8px   compact control gap
12px  form label/control rhythm
16px  normal group spacing
24px  section spacing
32px  large section spacing

Related things should be closer together than unrelated things. If a screen feels messy, fix spacing and alignment before adding colors.

Color

Use color to encode meaning, not just mood.

Neutral colors     text, borders, surfaces, dividers
Brand color        primary action and selected state
Semantic colors    success, warning, danger, info
Data colors        charts, maps, categories

Never rely on color alone. Pair color with text, icons, position, or shape.

For a deeper UI color guide, including contrast, semantic colors, data colors, dark mode, Tailwind tokens, and shadcn/ui tokens, see color.md.

:root {
  --color-bg: #ffffff;
  --color-text: #111827;
  --color-muted: #6b7280;
  --color-border: #d1d5db;
  --color-primary: #2563eb;
  --space-2: 0.5rem;
  --space-4: 1rem;
}

Accessibility

Accessibility is part of the component contract.

Use real buttons for actions.
Use links for navigation.
Associate labels with inputs.
Keep visible focus styles.
Support keyboard interaction.
Respect reduced motion.
Keep text contrast readable.
Do not hide errors only in color.
Test with zoom and keyboard navigation.

Accessible primitives help, but you still own labels, descriptions, error messages, focus order, contrast, and product-specific behavior.

Design Tokens

Design tokens are named decisions.

--color-primary
--color-danger
--radius-control
--space-3
--font-body
--shadow-popover

Use tokens for values that should stay consistent across the app. Do not tokenize every one-off value too early.

Figma-to-Code Workflow

Use Figma as a source of decisions, not a pixel prison.

Identify components, variants, spacing, states, and responsive behavior.
Map Figma colors and type styles to tokens.
Confirm interactive states that are not visible in the static frame.
Ask about empty, loading, error, permission, and long-content states.
Build semantic markup first.
Compare at common breakpoints and browser zoom.

3. Styling Tools

Styling choices are tradeoffs between speed, control, runtime cost, team familiarity, and design-system maturity.

ToolSolvesUse WhenAvoid WhenProduction FitInstall
Plain CSSBrowser-native stylingYou need fundamentals, global styles, simple componentsYou need scoped class names at scaleAlways usefulnone
CSS ModulesScoped component stylesYou want CSS files without class collisionsYou need shared variants across many componentsStrong defaultframework-supported
Tailwind CSSUtility-first styling; see tailwind.mdYou want fast iteration and consistent tokens in markupThe team dislikes utility classes or has a heavy existing CSS systemVery popular for SaaS and Next.js appsnpm install tailwindcss @tailwindcss/vite for Vite
SassCSS with variables, nesting, mixinsYou maintain an existing Sass codebaseNative CSS or Tailwind already covers the needMature, but less essential than beforenpm install -D sass
vanilla-extractType-safe CSS-in-TS with static CSS outputYou want typed tokens and zero runtime CSS-in-JSYou need the simplest setupStrong for design systemsnpm install @vanilla-extract/css plus bundler plugin
Emotion / styled-componentsRuntime CSS-in-JSYou need component-scoped dynamic styling or use a library built on itServer rendering/runtime cost is a concernCommon in existing appsnpm install @emotion/react @emotion/styled
clsxConditional class stringsClass names depend on props/stateYou only concatenate one stringTiny utilitynpm install clsx
tailwind-mergeResolve conflicting Tailwind classesComponents accept class overridesYou are not using TailwindCommon with Tailwind component APIsnpm install tailwind-merge
class-variance-authorityTyped class variantsButtons, badges, alerts need size/tone variantsOne-off componentsCommon with shadcn-style systemsnpm install class-variance-authority

Minimal CSS Modules example:

import styles from "./Button.module.css";

type ButtonProps = {
  children: React.ReactNode;
};

export function Button({ children }: ButtonProps) {
  return <button className={styles.button}>{children}</button>;
}
.button {
  border: 1px solid var(--color-border);
  border-radius: 0.5rem;
  padding: 0.5rem 0.75rem;
}

Minimal Tailwind example:

export function SaveButton() {
  return (
    <button className="rounded-md bg-blue-600 px-3 py-2 text-sm font-medium text-white hover:bg-blue-700">
      Save
    </button>
  );
}

Minimal variant utility:

import { cva, type VariantProps } from "class-variance-authority";
import clsx from "clsx";
import { twMerge } from "tailwind-merge";

const button = cva("rounded-md px-3 py-2 text-sm font-medium", {
  variants: {
    tone: {
      primary: "bg-blue-600 text-white hover:bg-blue-700",
      subtle: "border border-gray-300 bg-white text-gray-900",
    },
  },
  defaultVariants: {
    tone: "primary",
  },
});

type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> &
  VariantProps<typeof button>;

export function Button({ className, tone, ...props }: ButtonProps) {
  return <button className={twMerge(clsx(button({ tone }), className))} {...props} />;
}

4. Headless UI and Accessibility Primitives

Headless libraries provide behavior and accessibility for hard UI patterns while leaving styling mostly to you.

LibrarySolvesUse WhenAvoid WhenProduction FitInstall
Radix UIAccessible primitives for dialogs, popovers, dropdowns, tooltips, tabs, and moreYou want strong React primitives and custom stylingYou want predesigned componentsExcellent base for custom systemsnpm install radix-ui or individual packages
Base UIUnstyled accessible React componentsYou want modern primitives from the MUI/Radix/Floating UI lineageYou need a fully styled library todayPromising for custom systemsnpm install @base-ui/react
React AriaAccessible components and hooks with internationalization focusAccessibility-heavy apps or design systemsYou want the shortest learning curveVery strong for serious accessibilitynpm install react-aria-components
Headless UIUnstyled accessible components designed around Tailwind workflowsYou use Tailwind and need common primitivesYou need a very broad primitive catalogMature and simplenpm install @headlessui/react
Ark UIHeadless components across React, Solid, Vue, and SvelteYou want cross-framework design-system primitivesYou only need a tiny React appGood for multi-framework teamsnpm install @ark-ui/react
AriakitLower-level accessible React components and hooksYou want fine control over behaviorYou want prebuilt visual designStrong for custom accessible UInpm install @ariakit/react

Radix Popover shape:

import { Popover } from "radix-ui";

export function HelpPopover() {
  return (
    <Popover.Root>
      <Popover.Trigger>Help</Popover.Trigger>
      <Popover.Portal>
        <Popover.Content sideOffset={8}>
          Save changes before leaving the page.
          <Popover.Arrow />
        </Popover.Content>
      </Popover.Portal>
    </Popover.Root>
  );
}

React Aria Components shape:

import { Button, Dialog, DialogTrigger, Heading, Modal } from "react-aria-components";

export function ConfirmDialog() {
  return (
    <DialogTrigger>
      <Button>Delete</Button>
      <Modal>
        <Dialog>
          <Heading slot="title">Delete item?</Heading>
          <p>This action cannot be undone.</p>
        </Dialog>
      </Modal>
    </DialogTrigger>
  );
}

5. Component Libraries

Component libraries provide ready-made visual components. They are fastest when your product can accept their design language.

LibrarySolvesUse WhenAvoid WhenProduction FitInstall
shadcn/uiCopyable component patterns built around Tailwind, primitives, and local ownership; see shadcn.mdYou want editable components in your repoYou expect normal package-style black-box componentsVery popular for modern SaaSnpx shadcn@latest init
MUIComplete Material Design React systemEnterprise apps, admin panels, data-heavy productsYou need a highly custom non-Material brand with minimal overridesVery maturenpm install @mui/material @emotion/react @emotion/styled
Ant DesignEnterprise React component systemDense business software, tables, forms, internal toolsYou need a lightweight bespoke marketing UIVery mature for enterprisenpm install antd --save
MantineBroad React component and hooks libraryYou want many components with good DX and themingYou need strict adherence to another design languageStrong app-building librarynpm install @mantine/core @mantine/hooks
Chakra UIAccessible component primitives with style propsYou like style props and themingYou want zero runtime stylingCommon and approachablenpm i @chakra-ui/react @emotion/react
HeroUI / NextUIStyled React components built on Tailwind and React AriaYou want polished accessible defaultsYour app cannot adopt its styling stackProduction-ready but check version requirementsnpx heroui-cli@latest or npm install @heroui/react @heroui/styles
DaisyUITailwind component class names and themesYou want quick prototypes or simple Tailwind themesYou need deep React behavior or custom accessibility primitivesUseful for speednpm i -D daisyui@latest
Flowbite ReactTailwind-based React componentsYou want ready Tailwind components and blocksYou already have a custom design systemGood for fast product UInpx flowbite-react@latest init
Bootstrap / React BootstrapClassic responsive component systemYou maintain Bootstrap apps or need familiar defaultsYou want a modern custom UI languageStable and widely knownnpm install react-bootstrap bootstrap
Fluent UIMicrosoft-style enterprise componentsMicrosoft ecosystem, Teams-like or Office-like appsYou do not want Fluent design languageStrong for enterprisenpm install @fluentui/react-components

MUI minimal example:

import Button from "@mui/material/Button";

export function PrimaryAction() {
  return <Button variant="contained">Save changes</Button>;
}

Ant Design minimal example:

import { Button, DatePicker } from "antd";

export function ScheduleForm() {
  return (
    <>
      <DatePicker />
      <Button type="primary">Schedule</Button>
    </>
  );
}

shadcn/ui mental model:

CLI adds component source into your app.
You own the component code after installation.
Radix, Base UI, or another primitive layer handles hard interaction behavior.
Tailwind handles styling.

6. Icons

Icons should clarify actions, not replace unclear labels. Use aria-hidden for decorative icons and accessible labels for icon-only buttons.

LibraryBest ForInstall
lucide-reactGeneral app icons, clean stroke style, great default for SaaSnpm install lucide-react
HeroiconsTailwind ecosystem, solid/outline iconsnpm install @heroicons/react
Tabler IconsLarge consistent line-icon setnpm install @tabler/icons-react
Phosphor IconsMultiple weights and flexible visual tonenpm i @phosphor-icons/react
React IconsMany icon packs through one packagenpm install react-icons --save
import { Save } from "lucide-react";

export function SaveIconButton() {
  return (
    <button type="button" aria-label="Save">
      <Save aria-hidden="true" size={18} />
    </button>
  );
}

Prefer one icon family per product. Mixing icon sets casually makes the UI feel uneven.

7. Animation

Animation should explain change. It should not delay the user.

LibrarySolvesUse WhenAvoid WhenInstall
MotionReact animation, gestures, layout animationProduct UI transitions and microinteractionsA simple CSS transition is enoughnpm install motion
GSAPTimelines and complex animation controlRich marketing pages, advanced sequences, SVG/canvas animationNormal app UI needs small transitionsnpm install gsap and optionally npm install @gsap/react
React SpringSpring physics and data-driven motionGesture-like or physics-like UIYou need simple enter/exit transitions onlynpm install @react-spring/web
AutoAnimateZero-config list and layout transitionsLists where items are added, removed, or sortedYou need precise timeline controlnpm install @formkit/auto-animate
Lottie / dotLottieDesigner-exported animationsBrand illustrations and empty statesCritical interaction feedback that must be tiny and instantpackage varies by player

Motion example:

"use client";

import { motion } from "motion/react";

export function FadeInPanel({ children }: { children: React.ReactNode }) {
  return (
    <motion.section initial={{ opacity: 0, y: 8 }} animate={{ opacity: 1, y: 0 }}>
      {children}
    </motion.section>
  );
}

Reduced motion guard:

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    scroll-behavior: auto !important;
    transition-duration: 0.01ms !important;
  }
}

8. Tables, Charts, and Dashboards

Dashboards should prioritize scanning, comparison, filtering, exporting, and error recovery.

Tables and Data Grids

ToolSolvesUse WhenAvoid WhenInstall
TanStack TableHeadless table state: sorting, filtering, pagination, selectionYou want custom markup and behaviorYou want a complete styled gridnpm install @tanstack/react-table
AG GridFull-featured data gridEnterprise grids with grouping, editing, virtualization, Excel-like featuresYou need a simple tablenpm install ag-grid-react
MUI X Data GridData grid integrated with MUIYour app already uses MUIYou are not using MUI and want headless controlnpm install @mui/x-data-grid

TanStack Table owns table logic; you still render accessible markup.

import {
  flexRender,
  getCoreRowModel,
  useReactTable,
  type ColumnDef,
} from "@tanstack/react-table";

type User = {
  name: string;
  email: string;
};

const columns: Array<ColumnDef<User>> = [
  { accessorKey: "name", header: "Name" },
  { accessorKey: "email", header: "Email" },
];

export function UserTable({ data }: { data: User[] }) {
  const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel() });

  return (
    <table>
      <thead>
        {table.getHeaderGroups().map((headerGroup) => (
          <tr key={headerGroup.id}>
            {headerGroup.headers.map((header) => (
              <th key={header.id}>
                {flexRender(header.column.columnDef.header, header.getContext())}
              </th>
            ))}
          </tr>
        ))}
      </thead>
      <tbody>
        {table.getRowModel().rows.map((row) => (
          <tr key={row.id}>
            {row.getVisibleCells().map((cell) => (
              <td key={cell.id}>
                {flexRender(cell.column.columnDef.cell, cell.getContext())}
              </td>
            ))}
          </tr>
        ))}
      </tbody>
    </table>
  );
}

Charts

LibraryBest ForUse WhenAvoid WhenInstall
RechartsCommon React chartsProduct dashboards and simple analyticsYou need custom statistical graphicsnpm install recharts
EChartsMany chart types and large data optionsDense analytics, complex interactions, mapsYou need tiny bundle sizenpm install echarts echarts-for-react
Chart.jsCanvas chartsSimple, responsive charts outside heavy React compositionYou need SVG-level custom compositionnpm install chart.js
D3Low-level visualization primitivesCustom visualizations and data transformsYou just need a normal bar chartnpm install d3
NivoPolished React chart components built on D3You want attractive charts quicklyBundle size or exact custom behavior matters mostnpm install @nivo/core @nivo/bar
visxLow-level React visualization componentsYou want D3 power with React renderingYou want out-of-box dashboardsnpm install @visx/scale @visx/shape @visx/group

Recharts example:

import { Bar, BarChart, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts";

const data = [
  { month: "Jan", signups: 42 },
  { month: "Feb", signups: 57 },
];

export function SignupsChart() {
  return (
    <ResponsiveContainer width="100%" height={240}>
      <BarChart data={data}>
        <XAxis dataKey="month" />
        <YAxis />
        <Tooltip />
        <Bar dataKey="signups" fill="#2563eb" />
      </BarChart>
    </ResponsiveContainer>
  );
}

Chart cautions:

Label axes and units.
Start bars at zero unless there is a strong reason.
Do not use pie charts for precise comparison.
Use colorblind-safe palettes.
Show loading, empty, and error states.
For financial or operational data, define timezone and rounding rules.

9. Drag, Drop, Resize, and Uploads

Drag and drop must still work for keyboard and touch users where the workflow requires it.

LibrarySolvesUse WhenAvoid WhenInstall
dnd-kitAccessible drag/drop primitives for ReactSortable lists, boards, custom DnDYou need native file drops onlynpm install @dnd-kit/core
Pragmatic Drag and DropFast low-level drag/drop from AtlassianComplex DnD across frameworks or performance-sensitive UIYou want high-level React componentsnpm install @atlaskit/pragmatic-drag-and-drop
React DnDDrag/drop architecture with pluggable backendsLegacy or complex DnD appsYou want modern sortable-list ergonomicsnpm install react-dnd react-dnd-html5-backend
react-dropzoneFile drop zonesFile selection and drag-to-uploadYou need general component reorderingnpm install react-dropzone
React RndResizable and draggable boxesInternal tools, editors, panelsA normal responsive layout is enoughnpm install react-rnd

File drop example:

import { useCallback } from "react";
import { useDropzone } from "react-dropzone";

export function UploadDropzone() {
  const onDrop = useCallback((files: File[]) => {
    for (const file of files) {
      console.log(file.name);
    }
  }, []);

  const { getInputProps, getRootProps, isDragActive } = useDropzone({
    accept: { "image/*": [] },
    maxFiles: 3,
    onDrop,
  });

  return (
    <div {...getRootProps()}>
      <input {...getInputProps()} />
      {isDragActive ? "Drop files here" : "Choose files"}
    </div>
  );
}

Uploads are security-sensitive. Validate file type, size, dimensions, and authorization on the server. Client checks improve UX but do not protect the backend.

10. Production Stack Recommendations

Use these as starting points, not rules.

Modern SaaS

Next.js or Vite React
Tailwind CSS
shadcn/ui
Radix UI or Base UI
lucide-react
Motion
React Hook Form + Zod
TanStack Query
TanStack Table
Recharts
Storybook
Playwright

Best when you want high control, modern styling, and components your team can own.

Enterprise Dashboard

Next.js or Vite React
MUI or Ant Design
TanStack Table, AG Grid, or MUI X Data Grid
ECharts or Recharts
React Hook Form + Zod
Storybook
Playwright
axe-core checks

Best when the product needs dense forms, tables, filters, permissions, bulk actions, and predictable admin UI.

Accessibility-Heavy App

Semantic HTML
React Aria or Radix UI
Plain CSS, CSS Modules, or Tailwind CSS
Testing Library
Playwright keyboard flows
axe-core
Manual screen reader checks

Best when forms, dialogs, focus, keyboard interaction, and assistive technology support are central to the product.

Fast Prototype

Vite React
Tailwind CSS
daisyUI, Flowbite React, Mantine, or HeroUI
lucide-react
Recharts

Best when the goal is learning, validation, or an internal proof of concept.

11. When To Use What

Pick libraries from the problem, not from popularity. The strongest frontend engineers can explain why a tool fits the product, team, accessibility needs, customization needs, and maintenance cost.

Fast Decision Map

Custom SaaS/product UI       Tailwind + shadcn/ui + Radix/Base UI
Enterprise dashboard         MUI or Ant Design + AG Grid/MUI X/ECharts
Accessibility-heavy app      Semantic HTML + React Aria/Radix + axe + Playwright
Fast prototype               Mantine, HeroUI, Flowbite, daisyUI, or shadcn/ui
Long-lived design system     Tokens + Radix/Base UI/React Aria + Storybook
Marketing/creative site      Tailwind/CSS Modules + Motion or GSAP
Content-heavy site           Plain CSS/CSS Modules + strong typography + simple components
Data-heavy internal tool     Ant Design/MUI + AG Grid/TanStack Table + ECharts
Small learning app           CSS Modules or Tailwind + a few hand-built components

By Project Type

ProjectBest Starting StackWhyBe Careful With
Modern SaaSTailwind CSS, shadcn/ui, Radix UI or Base UI, lucide-react, MotionHigh control, modern defaults, local component ownershipToo many copied components without product-level cleanup
Enterprise adminMUI or Ant Design, MUI X Data Grid or AG Grid, EChartsDense forms, tables, filters, permissions, and predictable patternsFighting the library's design language for a custom brand
Accessibility-heavy appSemantic HTML, React Aria, Radix UI, Testing Library, axe-core, PlaywrightStrong keyboard, focus, ARIA, and screen-reader foundationsAssuming primitives solve labels, copy, validation, and flow
Fast prototypeMantine, HeroUI, Flowbite React, daisyUI, or shadcn/uiQuick screens with usable defaultsLetting prototype shortcuts become permanent architecture
Custom design systemDesign tokens, CSS Modules/Tailwind/vanilla-extract, Radix/Base UI/React Aria, StorybookReusable primitives and documented component statesBuilding a design system before the product has stable patterns
Marketing pageTailwind or CSS Modules, Motion, GSAP for complex sequencesVisual control and animation flexibilityHeavy app component libraries and slow decorative animation
Dashboard with chartsMUI/Ant/shadcn, TanStack Table/AG Grid, Recharts/EChartsTables and charts are core workflows, not decorationCharts without labels, units, empty states, or colorblind support

Styling Choice

Use ThisWhen To Use ItWhen Not To Use It
Plain CSSLearning CSS, content sites, simple global styling, browser-native controlLarge apps where class collisions become painful
CSS ModulesScoped component CSS, Next.js apps, teams that prefer normal CSS filesYou need a utility workflow or shared variant system everywhere
Tailwind CSSCustom product UI, fast iteration, consistent spacing/color/type scale, shadcn/uiA team strongly dislikes utility classes or already has a mature CSS system
SassExisting Sass codebase, nested legacy styles, mature older teamsNew app where CSS variables, modules, or Tailwind are enough
vanilla-extractType-safe tokens, design-system packages, zero-runtime CSS-in-TSSimple apps where setup cost is not worth it
Emotion/styled-componentsExisting CSS-in-JS app, MUI customization, highly dynamic component stylingServer-rendered apps where runtime styling cost or compatibility is a concern
clsx + tailwind-mergeAny React app with conditional Tailwind classesTiny components with no conditional classes
class-variance-authorityButtons, badges, alerts, inputs, and components with stable variantsOne-off UI where variants are not reused

UI Primitives and Component Kits

Use ThisBest ForChoose It WhenAvoid It When
shadcn/uiModern custom SaaS/product UIYou want editable component source in your repo and Tailwind is acceptableYou want automatic package-style updates
Radix UIAccessible low-level primitivesYou need dialogs, dropdowns, popovers, tabs, tooltips, and custom stylingYou want predesigned visual components
Base UIUnstyled accessible primitivesYou want modern primitive APIs and full styling controlYour team needs a large ready-made visual kit today
React AriaAccessibility-heavy custom componentsKeyboard, focus, ARIA, internationalization, and complex widgets matter deeplyYou want the shortest setup and simplest examples
Headless UITailwind-friendly common primitivesYou want simple unstyled components with Tailwind ergonomicsYou need the broadest primitive catalog
MUIEnterprise apps and dashboardsMaterial-like defaults, many components, theming, and MUI X are usefulYou need a highly custom brand with minimal override work
Ant DesignAdmin panels and internal toolsDense business UI, tables, forms, and predictable enterprise patterns matterYou are building a lightweight consumer or marketing UI
MantineFast full-featured React appsYou want many components and hooks with good developer experienceYour org already committed to another design language
Chakra UIAccessible style-prop workflowYour team likes style props and fast component compositionYou want zero-runtime styling or Tailwind-first components
HeroUI / Flowbite / daisyUIPolished prototypes and Tailwind shortcutsSpeed matters more than deep design-system ownershipYou need strict custom behavior across a large product
Fluent UIMicrosoft ecosystem appsThe product should feel native to Microsoft/Office/Teams-style workflowsYour product has a different visual language

Icons

Default app icons              lucide-react
Tailwind ecosystem             Heroicons
Large line-icon catalog        Tabler Icons
Multiple weights/styles        Phosphor Icons
Many icon packs in one place   React Icons

Use icon-only buttons only when the icon is truly recognizable and the button has an accessible label. Otherwise, use icon plus text.

Animation

Use ThisWhen To Use ItWhen Not To Use It
CSS transitionsHover, focus, simple show/hide, small state changesComplex choreography or gesture-driven UI
MotionReact UI animation, layout animation, gestures, polished product transitionsSimple color/opacity changes that CSS can handle
GSAPTimeline-heavy marketing pages, scroll sequences, SVG/canvas animationNormal dashboards and forms
React SpringPhysics-like interaction and gesture feelStraightforward enter/exit UI
AutoAnimateQuick list add/remove/reorder transitionsYou need exact timeline control
LottieDesigner-exported illustrations and empty statesCore product feedback that should be lightweight and instant

Default rule: use CSS first, Motion second, GSAP only when the animation itself is a major feature.

Tables and Data Grids

Simple static table                  semantic table + CSS/Tailwind
Custom React table logic             TanStack Table
Enterprise grid with heavy features  AG Grid
MUI app needing a grid               MUI X Data Grid
Ant Design admin table               Ant Design Table
Huge lists without table semantics   TanStack Virtual or react-virtuoso

Choose TanStack Table when you want control over markup and styling. Choose AG Grid when users expect spreadsheet-like power: grouping, pinned columns, editing, export, virtualization, and complex filtering.

Charts and Data Visualization

Normal dashboard charts       Recharts
Complex dashboard visuals     ECharts
Simple canvas charts          Chart.js
Polished React chart kit      Nivo
Custom visualization          D3 or visx
Maps                          Mapbox GL or MapLibre
3D                            Three.js or React Three Fiber

Use Recharts first for typical React dashboards. Use ECharts when the chart is more complex than the rest of the page. Use D3 or visx when you are designing a custom visualization, not just rendering a chart.

Forms and Validation

Simple form                  native form + controlled/uncontrolled inputs
Production React forms       React Hook Form
Schema validation            Zod or Valibot
Existing legacy app          Formik/Yup maintenance
Next.js server mutation      native form + Server Function + server validation

React Hook Form is usually the best default for complex client forms. Keep validation close to the boundary: client validation for UX, server validation for trust.

Drag, Drop, Resize, and Uploads

Sortable React lists/boards       dnd-kit
Complex cross-framework DnD       Pragmatic Drag and Drop
Legacy/advanced DnD architecture  React DnD
File drop zones                   react-dropzone
Resizable/draggable panels        React Rnd

Do not make drag and drop the only way to complete an important workflow. Provide buttons, menus, or keyboard paths when the action matters.

Testing and Documentation

Component behavior             Testing Library
Unit tests                     Vitest
End-to-end user journeys       Playwright
API mocking                    MSW
Accessibility checks           axe-core or jest-axe
Component examples/docs        Storybook
Visual regression              Storybook/Chromatic or Playwright screenshots

Use Storybook when components have meaningful variants, states, and stakeholders. Use Playwright for flows users actually depend on: signup, checkout, dashboard filters, auth redirects, uploads, and destructive actions.

For a deeper frontend testing workflow, see frontend-testing.md.

Observability and Product Feedback

Frontend error tracking       Sentry
Session replay/debugging      LogRocket or Sentry Replay
Product analytics             PostHog
Performance metrics           browser APIs, framework analytics, Sentry
Feature flags/experiments     PostHog or dedicated flag tools

Use these after the app has real users or real QA workflows. Do not record secrets, tokens, passwords, private messages, or sensitive form fields.

Default Choices If You Are Unsure

Styling                 Tailwind CSS or CSS Modules
Component strategy      shadcn/ui for custom SaaS, MUI for enterprise
Primitives              Radix UI first, React Aria for accessibility-heavy UI
Icons                   lucide-react
Forms                   React Hook Form + Zod
Tables                  TanStack Table, AG Grid for enterprise
Charts                  Recharts, ECharts for complexity
Animation               CSS first, Motion for React transitions
Drag/drop               dnd-kit
Testing                 Vitest + Testing Library + Playwright
Docs                    Storybook when components repeat

12. Production UI Checklist

Layout works at mobile, tablet, desktop, and browser zoom.
Text does not overflow buttons, cards, nav, tables, or modals.
Keyboard navigation reaches every interactive element.
Focus is visible and logical.
Dialogs trap focus and restore focus on close.
Forms have labels, errors, disabled states, and pending states.
Loading, empty, error, and permission states are designed.
Color contrast is checked.
Motion respects prefers-reduced-motion.
Images have useful alt text or are marked decorative.
Icon-only buttons have aria-label.
Tables have headers, sorting labels, pagination, and empty states.
Charts have labels, units, accessible summaries, and non-color cues.
Client validation is repeated on the server.
Component APIs do not expose unsafe class or HTML escape hatches casually.
Storybook or examples cover key variants and edge states.
Playwright covers critical user journeys.

13. Learning Path

  1. HTML semantics and forms.
  2. CSS fundamentals, selectors, cascade, box model, flexbox, grid, responsive design.
  3. Typography, spacing, color, contrast, and design tokens.
  4. React component composition and props.
  5. Tailwind CSS or CSS Modules.
  6. clsx, tailwind-merge, and class-variance-authority.
  7. lucide-react.
  8. Radix UI or React Aria.
  9. shadcn/ui if using Tailwind.
  10. React Hook Form + Zod for real forms.
  11. TanStack Table for custom tables.
  12. Recharts for dashboards.
  13. Storybook for component examples.
  14. Playwright for user-flow testing.
  15. MUI and Ant Design for enterprise patterns.

14. Practice Projects

  1. Build a settings page with tabs, forms, validation, loading, save success, and field errors.
  2. Build a billing table with sorting, filtering, pagination, empty state, and row actions.
  3. Build a dashboard with KPI cards, one chart, one table, and a date-range filter.
  4. Build a command menu, dialog, dropdown, tooltip, and popover with keyboard testing.

For reusable product screen recipes, see ui-patterns.md. 5. Build a kanban board with drag sorting and a non-drag fallback action. 6. Recreate one Figma screen with semantic HTML, responsive layout, and tokenized styles.

15. Common Mistakes

Starting With a Library Before the UI Problem

Do not choose MUI, shadcn/ui, Tailwind, or Radix before you understand the workflow, density, states, and constraints. Tools should follow the product shape.

Styling Clickable divs

Use real controls. Buttons and links include keyboard and accessibility behavior that generic elements do not.

Treating Client Validation as Security

Client validation helps users. Server validation protects the app.

Overusing Cards

Cards are useful for repeated items and grouped controls. Entire pages made of nested cards often become harder to scan.

Hiding State

Every async interaction needs pending, success, failure, retry, and disabled behavior where applicable.

Copying Enterprise UI Into Small Apps

Enterprise libraries are powerful, but their density and design language can overwhelm a small consumer app.

Copying Marketing UI Into Tools

Operational tools need scanability, compact controls, predictable navigation, and low-friction repeated actions.

16. Commands To Remember

# Tailwind with Vite: changes dependencies
npm install tailwindcss @tailwindcss/vite

# shadcn/ui setup: writes files and changes dependencies
npx shadcn@latest init

# Radix primitives: changes dependencies
npm install radix-ui

# React Aria Components: changes dependencies
npm install react-aria-components

# MUI: changes dependencies
npm install @mui/material @emotion/react @emotion/styled

# Ant Design: changes dependencies
npm install antd --save

# Mantine: changes dependencies
npm install @mantine/core @mantine/hooks

# Motion: changes dependencies
npm install motion

# Tables: changes dependencies
npm install @tanstack/react-table

# Charts: changes dependencies
npm install recharts

# Storybook: writes files and changes dependencies
npm create storybook@latest

Resources