CSS User Interface
Optional Advanced just painted a stencil on one photo. This session is a different optional toolkit: user interface. You change how the pointer, caret, and native chrome behave on a control. You are not punching holes in a hero image, and you are not splitting an article into newspaper columns.
Direct answer
CSS user-interface properties hint at interaction. cursor chooses the mouse shape (pointer, wait, help, not-allowed…). resize lets the user drag a corner — usually on a textarea. outline with outline-offset draws a focus ring that does not steal layout space (you already met outline as a box-model ring; here it is the keyboard habit). user-select can stop accidental highlighting. caret-color tints the blinking insertion mark. appearance (and -webkit-appearance) can flatten a native checkbox or search field so you restyle it. Keep focus visible. Do not turn the whole page into an unselectable poster.
Why this is not a mask or a column
A mask decides which pixels of one box stay visible. Multi-column fragments a text stream. UI properties do neither. Layout width and height stay unless resize is in play. Filters and mask-image still belong to art. Here the job is: “Does this look clickable? Can I type? Can I grow this box? Can a keyboard user see where they are?”
Cursor — tell the truth
cursor is a promise. pointer means “this is clickable.” Use it on real buttons and links. Do not put a hand on a heading that does nothing.
a.nav-link,
button.save {
cursor: pointer;
}
button[disabled] {
cursor: not-allowed;
}
.help-hint {
cursor: help;
}
.busy-panel {
cursor: wait;
}
default is the arrow. text is the I-beam over selectable copy. grab / grabbing fit drag handles. Custom url(...) cursors exist; keep them tiny and always list a fallback keyword. A lying cursor is worse than none: people click a slogan and nothing happens.
Resize — grow a box, not a layout
resize works on overflow-capable boxes. Everyday use is a notes field:
textarea.notes {
min-height: 6rem;
min-width: 16rem;
max-width: 100%;
resize: vertical;
overflow: auto;
}
Values: none, both, horizontal, vertical. Pair with overflow other than visible, or the handle may never appear. Do not sprinkle resize: both on every card: the page shell should not become a pile of drag corners. min-height / max-width keep the field from collapsing or exploding.
Outline as a focus ring
The Box Model session already taught that outline sits outside the border and does not take space. In the UI toolkit you keep that ring for keyboard focus — often with :focus-visible so mouse users are not boxed on every click:
button.save:focus-visible {
outline: 3px solid #2563eb;
outline-offset: 3px;
}
button.save:focus:not(:focus-visible) {
outline: none;
}
Never ship a global *:focus { outline: none; } unless every control has an equally visible replacement (border, shadow, or ring). outline-offset pushes the ring off the edge without growing neighbors. This is still not border (which changes size) and not a mask fade on the button label.
user-select — stop accidental highlight
Selection is a feature. Turn it down only where a double-click should not paint a blue wash — a toolbar label, a drag chip — not an article.
.toolbar-label {
user-select: none;
}
article.body {
user-select: text;
}
none blocks highlighting. text is the usual readable default. all selects the whole element on one click — handy for a short code token, noisy on a paragraph. Prefixes (-webkit-user-select) still show up in old WebKit; if you need them, pair them with the standard property. Do not disable select on legal text, prices, or error messages people must copy.
caret-color — tint the insertion mark
The caret is the blinking bar in an input. Match it to the brand without hiding it:
input.email,
textarea.notes {
caret-color: #0f766e;
color: #0f172a;
}
caret-color: auto follows the text color. A matching caret is polish. caret-color: transparent as decoration is a trap: people cannot see where they type. This is not color on the whole page and not a mask-image on the glyph.
appearance — restyle native widgets
Browsers paint checkboxes, search fields, and date pickers with native chrome. appearance: none (plus -webkit-appearance: none where needed) strips that skin so you can draw your own:
input.search {
appearance: none;
-webkit-appearance: none;
border: 1px solid #94a3b8;
border-radius: 0.5rem;
padding: 0.5rem 0.75rem;
}
input[type="checkbox"].tick {
appearance: none;
-webkit-appearance: none;
width: 1.15rem;
height: 1.15rem;
border: 2px solid #334155;
border-radius: 0.25rem;
}
After none, you owe a border, a checked state, and a focus ring. An invisible checkbox is not a custom checkbox. Support varies by control type; test the real widget, not only a text input. Do not use appearance to “fix” a mask that ate a photo or to replace multi-column layout.
UI vs mask vs columns vs outline-as-box
📊 UI vs mask vs columns vs outline
| Tool | Job | Poor fit |
|---|---|---|
cursor / resize / caret-color / appearance |
Interaction chrome on controls | Stenciling a photo; newspaper columns |
user-select |
Limit highlight on chrome, not articles | Hiding copy people must paste |
outline + :focus-visible |
Keyboard focus ring (no layout space) | Replacing border math; fading labels with a mask |
mask-image |
Soft holes on one element’s pixels | Pointer shape; growing a textarea |
Multi-column (column-*) |
Fragment flowing text | Cursors, carets, native widget skin |
Optional Advanced already used columns for prose and masks for pictures. User interface is the control layer. Mixing column-count onto a settings form, or mask-image onto a submit button, does not make the form “more UI.”
What not to restyle away
Do not remove every native hint. Scrollbars, carets, and focus rings exist because people need them. Touch targets still need size (you styled buttons earlier in the course). If a control is disabled, cursor: not-allowed plus a real disabled attribute is calmer than a mask that hides the label.
.ok-ui {
cursor: pointer;
outline-offset: 2px;
}
.not-a-stencil {
/* don't: mask-image on the submit label to “soften” the click */
}
.not-a-newspaper {
/* don't: column-count on a login form to look advanced */
}
If the problem is a photo edge, go back to masking. If the problem is a long article, go back to columns — or to ordinary block flow. If the problem is “this does not feel clickable,” start with cursor and a real control, not a decorative filter.
Try it yourself
- Put
cursor: pointeron a real<button>andnot-allowedon[disabled]. - Give a
textarearesize: vertical,min-height, andoverflow: auto. - Add
:focus-visibleoutline +outline-offset— tab to it with the keyboard. - Set
user-select: noneon a toolbar chip only; leave the article selectable. - Tint
caret-colorin an input. Do not mask the field or setcolumn-counton it.
Common mistakes
cursor: pointeron text that is not a link or buttonresize: bothon layout cards and nav bars- Global
outline: nonewith no:focus-visiblereplacement user-select: noneon whole pages, prices, or error copycaret-color: transparent“for a clean look”appearance: nonethen no border, no checked style, no focus ring- Treating a mask or multi-column as user-interface polish
Summary
- User interface CSS hints at interaction: pointer, resize, caret, native chrome
cursormust tell the truth;resizebelongs on fields, not the page shell- Keep a visible focus outline;
user-selectandcaret-colorstay light-touch appearance: noneonly when you restyle the widget completely- Not a mask stencil and not newspaper columns
- Optional Advanced continues next with 3D transforms (
transforms-3d)
🧠 Test Your Knowledge
Test Your Knowledge
Challenge yourself with this interactive quiz and see how well you understand the topic
📝 Instructions
- Read each question carefully
- Select the best answer for each question
- You can retake the quiz as many times as you want
- Your progress will be shown at the top