Forms and Tables Styling

Introduction

Forms and tables are where users input data and read structured information. Poor styling here directly harms task completion, especially on mobile and for keyboard users. This chapter covers practical CSS patterns for clear form controls, validation feedback, and readable, responsive tables.

Prerequisites

  • Basic input/form HTML structure (label, input, select, textarea)
  • Basic pseudo-classes (:focus-visible, :invalid, :disabled)
  • Basic spacing and typography styling

Form Styling Goals

Well-styled forms should be:

  • readable
  • easy to scan
  • easy to tap/click
  • clear in validation state
  • keyboard accessible

Avoid styling that makes controls look decorative but less usable.

Base Form Layout

A clean vertical form stack:

css
.form {
  display: grid;
  gap: 1rem;
  max-width: 560px;
}
 
.field {
  display: grid;
  gap: 0.4rem;
}

Code explanation:

  • .form controls spacing between field groups
  • .field keeps label/input/help text visually grouped
  • grid with gap is cleaner than many custom margins

Label Styling

css
label {
  font-weight: 600;
  color: #0f172a;
}

Labels should remain clearly visible and close to controls. Do not rely on placeholder text as the only label.

Text Input Baseline

Code explanation:

  • width: 100% allows responsive filling of container
  • min-height: 44px improves touch ergonomics
  • font: inherit keeps typography consistent with page text

Focus State for Inputs

css
input:focus-visible,
select:focus-visible,
textarea:focus-visible {
  outline: 3px solid #93c5fd;
  outline-offset: 1px;
  border-color: #2563eb;
}

Focus visibility is critical for keyboard users and should never be removed without replacement.

Placeholder and Help Text

css
input::placeholder,
textarea::placeholder {
  color: #94a3b8;
}
 
.help-text {
  color: #475569;
  font-size: 0.9rem;
}

Use placeholders as hints only, not as required instructions.

Validation States

css
input:invalid,
select:invalid,
textarea:invalid {
  border-color: #dc2626;
}
 
.field-error {
  color: #b91c1c;
  font-size: 0.875rem;
}

Code explanation:

  • invalid controls get stronger border cue
  • error text gives explicit message beyond color alone

Pair visual cues with textual feedback for accessibility.

Disabled State

css
input:disabled,
select:disabled,
textarea:disabled,
button:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

Disabled controls should look visibly inactive while still readable.

Checkbox and Radio Group Layout

css
.choice-group {
  display: grid;
  gap: 0.5rem;
}
 
.choice {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

This improves readability and touchability for multiple-choice fields.

Two-Column Form on Wider Screens

css
@media (min-width: 800px) {
  .form-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1rem;
  }
 
  .form-grid .full {
    grid-column: 1 / -1;
  }
}

Use this for compact desktop forms while keeping single-column flow on mobile.

Table Styling Goals

Readable tables need:

  • clear column structure
  • visible row separation
  • consistent alignment
  • responsive fallback strategy

Table Base Style

Code explanation:

  • border-collapse: collapse avoids doubled border spacing
  • consistent cell padding improves scanability
  • header background helps users track columns

Zebra Striping and Hover Rows

css
tbody tr:nth-child(even) {
  background: #f8fafc;
}
 
tbody tr:hover {
  background: #eef2ff;
}

Helpful for dense data tables, but keep hover subtle and maintain contrast.

Numeric Alignment

For numeric columns, align right:

css
.num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

This improves comparison of values in financial or metrics tables.

Responsive Table Pattern: Horizontal Scroll

css
.table-wrap {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
html
<div class="table-wrap">
  <table>...</table>
</div>

Code explanation:

  • wrapper adds horizontal scrolling when table exceeds viewport width
  • avoids crushing columns into unreadable narrow cells

For complex datasets, this is often better than forcing stacked table cards.

Sticky Header for Long Tables

css
thead th {
  position: sticky;
  top: 0;
  background: #f8fafc;
  z-index: 1;
}

Useful in long scrolling data tables. Ensure parent scroll context supports sticky behavior.

Accessibility Notes

  • keep visible labels for form controls
  • preserve focus styles
  • use text + color for error states
  • use <th scope="col"> for table headers
  • avoid tables for non-tabular page layout

Warning

Do Not Hide Critical Form Context

Removing labels or focus indicators may make forms unusable for keyboard and assistive-tech users.

Common Mistakes

MistakeWhy it hurtsBetter approach
Placeholder-only labelingPoor accessibility and clarityUse explicit <label>
Tiny input controlsHard touch interactionKeep minimum touch-friendly control size
Error state by color onlyAmbiguous feedbackAdd explicit error text
Tables squeezed on mobileUnreadable columnsUse horizontal overflow wrapper
Center-aligned all table dataHard numeric comparisonLeft-align text, right-align numbers

Debugging Checklist

When form/table UI looks broken:

  1. verify control states in DevTools (:focus-visible, :invalid, :disabled)
  2. check width constraints and overflow
  3. test keyboard-only navigation
  4. verify contrast in normal and error states
  5. test table readability at narrow viewport widths

Mini Exercise

  1. Build a registration form with name, email, password, and country select
  2. Add focus and invalid styles for controls
  3. Add helper text and error text styles
  4. Build a 6-column table with mixed text and numeric data
  5. Wrap table in a horizontal scroll container for mobile
  6. Test both keyboard navigation and mobile responsiveness

FAQ

Should I remove native form control styles completely?

Not necessarily. Start with light normalization and preserve usability cues unless you fully replace them with accessible alternatives.

Is table horizontal scrolling acceptable on mobile?

Yes, often it is the most practical and readable fallback for wide datasets.

How should I style invalid fields?

Use a clear border/state indicator plus explicit text message so users understand what to fix.

Should all table cells be center-aligned?

Usually no. Left alignment is best for text; right alignment is best for numeric columns.

Can I use CSS alone for full form validation UX?

CSS pseudo-classes help with visual states, but complex validation messaging usually needs HTML constraints and JavaScript logic.