Free tutorials & notes in Hindi & English · Clean code examples 🧰 Free PDF & Image Tools →
✨ Days 13–18 – Motion, UI & Professional CSS  ·  Day 15

Day 15: Styling Forms & Tables

⏱ 95 min📶 Intermediate🎯 100% self-study
Day 15 / 20

✅ Today you will learn

  • Consistent inputs: padding, borders, radius, font: inherit
  • Focus rings that are visible but beautiful
  • Validation colours with :user-invalid / :user-valid and :has()
  • Custom select arrow, pill radios, accent-color checkboxes
  • Data tables: zebra rows, sticky header, responsive card layout on phones

🏢 In the real world

Forms are where a business earns money – orders, admissions, enquiries. A default browser form looks different on every phone, shows errors in red before the user has typed anything, and the order table of the admin panel overflows on mobile. Today you fix all of that for the Sharma Sweets order form and the admin orders table.

A complete, styled order form

HTML + CSS
<style>
:root { --brand: #264de4; --ok: #15803d; --err: #b91c1c; --line: #cbd5e1; }
* { box-sizing: border-box; }
.form { max-width: 440px; background: #fff; padding: 20px; border-radius: 16px; box-shadow: 0 10px 30px rgb(0 0 0 / 8%); display: grid; gap: 14px; }
.field label { display: block; font-weight: 600; margin-bottom: 4px; }
.field input, .field select {
  width: 100%; padding: 10px 12px; font: inherit;
  border: 1.5px solid var(--line); border-radius: 10px; background: #fff;
  transition: border-color .2s, box-shadow .2s;
}
.field input:focus, .field select:focus { outline: none; border-color: var(--brand); box-shadow: 0 0 0 4px rgb(38 77 228 / 15%); }
.field input::placeholder { color: #94a3b8; }
/* validation only after the user has typed (not on page load) */
.field input:user-invalid { border-color: var(--err); }
.field input:user-valid   { border-color: var(--ok); }
.hint { font-size: .8rem; color: #64748b; }
.field:has(input:user-invalid) .hint { color: var(--err); }
.field select { appearance: none; background: #fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%23475569' stroke-width='2' fill='none'/%3E%3C/svg%3E") no-repeat right 12px center; padding-right: 34px; }
.choices { display: flex; gap: 10px; flex-wrap: wrap; }
.choice input { position: absolute; opacity: 0; }
.choice span { display: inline-block; padding: 8px 14px; border: 1.5px solid var(--line); border-radius: 99px; cursor: pointer; }
.choice input:checked + span { background: var(--brand); border-color: var(--brand); color: #fff; }
.choice input:focus-visible + span { outline: 3px solid #f59e0b; outline-offset: 2px; }
.check { display: flex; gap: 8px; align-items: center; }
.check input { width: 20px; height: 20px; accent-color: var(--brand); }
.submit { background: var(--brand); color: #fff; border: 0; padding: 12px; border-radius: 10px; font: 600 1rem system-ui; cursor: pointer; }
.submit:disabled { opacity: .5; cursor: not-allowed; }
</style>

<form class="form" onsubmit="alert('Valid! Order sent.'); return false">
  <div class="field">
    <label for="n">Full name</label>
    <input id="n" required minlength="2" placeholder="e.g. Aarti Gupta" autocomplete="name">
  </div>
  <div class="field">
    <label for="m">Mobile</label>
    <input id="m" type="tel" required pattern="[6-9][0-9]{9}" placeholder="10-digit mobile" aria-describedby="mh">
    <div class="hint" id="mh">Starts with 6–9, 10 digits</div>
  </div>
  <div class="field">
    <label for="s">Sweet</label>
    <select id="s" required><option value="">Choose…</option><option>Kaju katli</option><option>Ghewar</option></select>
  </div>
  <fieldset class="choices" style="border:0;padding:0;margin:0">
    <legend style="font-weight:600;margin-bottom:6px">Box size</legend>
    <label class="choice"><input type="radio" name="b" checked><span>500 g</span></label>
    <label class="choice"><input type="radio" name="b"><span>1 kg</span></label>
    <label class="choice"><input type="radio" name="b"><span>2 kg</span></label>
  </fieldset>
  <label class="check"><input type="checkbox" required> Gift wrap with a card</label>
  <button class="submit">Place order</button>
</form>
▶  Type a wrong mobile number and leave the field – it turns red; correct it – green
TechniqueWhy
font: inherit on inputsBrowsers use a small system font in inputs by default
padding ≥ 10px, height ≥ 44pxEasy to tap on phones
:focus with box-shadow ringVisible, on-brand focus for keyboard users
:user-invalid / :user-validShow errors only after the user interacts – not on page load
.field:has(input:user-invalid) .hintTurn the hint red without JavaScript
appearance: none + SVG arrowSame select design on every device
Hidden radio + styled spanPill buttons that stay keyboard-accessible
accent-colorBrand-coloured checkboxes in one line
⚠️ Never remove focus without a replacement

outline: none alone makes the form unusable with a keyboard. Always pair it with a visible alternative such as the box-shadow ring above.

💡 Why not :invalid?

:invalid matches immediately, so an empty required field is red as soon as the page opens – it feels like scolding the user. :user-invalid waits until the user has typed and left the field or tried to submit.

Data tables that stay readable

HTML + CSS
<style>
.tbl-wrap { max-height: 230px; overflow: auto; border: 1px solid #e2e8f0; border-radius: 12px; }
table { width: 100%; border-collapse: collapse; font-size: .92rem; }
th, td { padding: 10px 12px; text-align: left; border-bottom: 1px solid #f1f5f9; }
thead th { position: sticky; top: 0; background: #f8fafc; color: #475569; font-size: .75rem; text-transform: uppercase; letter-spacing: .05em; z-index: 1; }
tbody tr:nth-child(even) { background: #fafafa; }
tbody tr:hover { background: #eef2ff; }
.num { text-align: right; font-variant-numeric: tabular-nums; }
.st { padding: 2px 10px; border-radius: 99px; font-size: .75rem; font-weight: 700; }
.st.done { background: #dcfce7; color: #166534; } .st.pack { background: #fef3c7; color: #92400e; } .st.new { background: #dbeafe; color: #1e40af; }
</style>

<div class="tbl-wrap"><table>
  <thead><tr><th>Order</th><th>Customer</th><th>Items</th><th class="num">Amount</th><th>Status</th></tr></thead>
  <tbody>
    <tr><td>#1042</td><td>Aarti Gupta</td><td>Kaju katli 2 kg</td><td class="num">₹1,800</td><td><span class="st done">Delivered</span></td></tr>
    <tr><td>#1043</td><td>R. Meena</td><td>Ghewar 1 kg</td><td class="num">₹650</td><td><span class="st pack">Packing</span></td></tr>
    <tr><td>#1044</td><td>S. Khan</td><td>Assorted box</td><td class="num">₹720</td><td><span class="st new">New</span></td></tr>
    <tr><td>#1045</td><td>P. Jain</td><td>Rasmalai 3 kg</td><td class="num">₹2,100</td><td><span class="st new">New</span></td></tr>
    <tr><td>#1046</td><td>V. Singh</td><td>Ladoo 5 kg</td><td class="num">₹2,500</td><td><span class="st pack">Packing</span></td></tr>
    <tr><td>#1047</td><td>M. Bansal</td><td>Kaju katli 500 g</td><td class="num">₹450</td><td><span class="st done">Delivered</span></td></tr>
  </tbody>
</table></div>
▶  Sticky header, zebra rows, right-aligned money, status pills (scroll inside)

stack

HTML + CSS
<style>
table { width: 100%; border-collapse: collapse; }
th, td { padding: 8px 10px; text-align: left; border-bottom: 1px solid #e2e8f0; }
@media (max-width: 520px) {
  thead { position: absolute; left: -9999px; }          /* hide visually, keep for screen readers */
  tr { display: block; margin-bottom: 10px; border: 1px solid #e2e8f0; border-radius: 10px; padding: 6px; }
  td { display: flex; justify-content: space-between; border: 0; padding: 4px 6px; }
  td::before { content: attr(data-label); font-weight: 600; color: #64748b; }
}
</style>

<table>
  <thead><tr><th>Order</th><th>Customer</th><th>Amount</th></tr></thead>
  <tbody>
    <tr><td data-label="Order">#1042</td><td data-label="Customer">Aarti Gupta</td><td data-label="Amount">₹1,800</td></tr>
    <tr><td data-label="Order">#1043</td><td data-label="Customer">R. Meena</td><td data-label="Amount">₹650</td></tr>
  </tbody>
</table>
<p style="font:13px system-ui;color:#64748b">Narrow the window below 520px to see the card layout.</p>
▶  data-label + ::before turns each row into a labelled card

Option A: scroll wrapper

  • overflow-x: auto around the table
  • Keeps the table shape
  • Best for many numeric columns (reports)

Option B: stacked cards

  • Rows become labelled cards
  • No sideways scrolling
  • Best for 3–6 columns (orders, students)

🏅 Job skills unlocked today

✔ Style inputs, selects, radios and checkboxes consistently✔ Show validation states at the right time✔ Build sticky-header, zebra, status-pill tables✔ Make tables usable on phones

Practice tasks

🏋️ Practice – do it yourself

Task 1: Style a search box with a magnifier icon inside the input (padding-left + background SVG) and a blue focus ring.
Show solution
▶  Result
Task 2: Create a toggle switch from a checkbox (hidden input + styled span with ::before knob).
Task 3: Make the student marks table of a school stack into cards below 600px using data-label.

Quick quiz

🧠 Quick Quiz – test yourself

1. Why add font: inherit to inputs?
Browser defaults differ.
2. Show red borders only after the user interacts:
Waits for interaction.
3. Brand-coloured checkboxes in one line:
Supported by modern browsers.
4. Sticky table header needs…
Plus a background.
5. Numbers in a table column should be…
Digits line up.

Interview corner

💼 Interview Corner

How do you style a custom radio button accessibly?

Keep the real input in the DOM (visually hidden, not display: none) so it stays focusable and announced; style an adjacent element using :checked and :focus-visible selectors; wrap both in a label for a large click target.

How do you make a wide table responsive?

Either wrap it in an overflow-x: auto container (keeps comparison ability) or, for few columns, switch rows to stacked cards in a media query using data-label attributes with ::before. Keep a real thead for screen readers.

Summary

  • Inputs: font: inherit, generous padding, radius, clear focus ring.
  • :user-invalid / :user-valid + :has() for friendly validation.
  • appearance: none, hidden radios and accent-color for custom controls.
  • Tables: sticky head, zebra, tabular numbers; scroll or stack on phones.
➡️ Next

Day 16: a reusable UI component kit – buttons, badges, cards, alerts, tabs, modal and toast.

Frequently Asked Questions

Is :user-invalid supported?
Yes in current Chrome, Edge, Firefox and Safari. Older browsers ignore it, so the form still works – just without colours.
Can I style the file input button?
Yes, with the ::file-selector-button pseudo-element.
← CSS (20 Days)
🔗

Share this topic with a friend

यह topic किसी दोस्त को भेजें

Found it useful? Send it to a classmate learning the same thing.

अच्छा लगा? जो दोस्त यही सीख रहा है, उसे भेज दीजिए।

Next topicDay 16: UI Components: Buttons, Cards, Navbar, Modal Your progress: 15 / 20 (75%) · Lesson 16 / 20 Continue →

How was this lesson?

Your feedback helps us make the content better.

Write a review or suggestion (optional)

After approval, your name and city may appear publicly with your review. Please do not write your email or phone number.

▶ CodingEasily on YouTube

Learn with video lessons in easy Hindi

Featured video: RDBMS Part 3 – Class XI Commerce IT

▶ Subscribe – it’s free