Most demos need a newer browser

They use :has() and container queries. Update your browser, or on an iPhone update iOS to 16.4 or later.

Quick tips

Each tip covers one technique. Most cards put the same element under a Without label and a With label, so the technique is the only thing that changes.

Shows at a glance

The before and after are visible at the same time, with nothing to click.

Without

One word on its last line looks unfinished

With

One word on its last line looks unfinished

Even out headline lines

A single word stranded on the last line of a headline creates an awkward, unfinished look. The balance value distributes words across lines so each line is roughly the same length, which prevents that lonely word from appearing.

Browsers limit balancing to short blocks: six lines in Chrome and Edge, ten in Firefox. Past that line count the browser skips balancing and renders the text normally. Single-line headings see no change either way.

h1 { text-wrap: balance; }
Without

Coffee 2 Bagel 1 Total 3

With

Coffee 2 Bagel 1 Total 3

Keep your spacing and line breaks

HTML collapses runs of spaces into one and converts line breaks to spaces. The pre value tells the browser to honor every space and line break exactly as you typed them, which is what you see in the demo's aligned columns.

Good for ASCII art, formatted data, code snippets and tables. One gotcha: text set to pre will not wrap to fit narrow screens. Use white-space: pre-wrap instead when you need wrapping.

.order { white-space: pre; }
Without
With

Readable text on any background

The contrast-color() function takes a background color and returns either black or white, whichever provides better contrast. In the demo the yellow and lime swatches get black text, while navy and wine get white, with no logic written out by hand.

Current versions of all major browsers support this, but older browsers do not. Compute the text color yourself or use a fixed color with enough contrast on every background as a fallback for older versions.

.chip { color: contrast-color(var(--bg)); }
Without
With

Center anything

Setting the container to display: grid with place-items: center centers its children both horizontally and vertically. The child element needs no centering properties of its own, which keeps the markup simple.

Of the methods that work on multiple children at once, this is the shortest. An alternative is flexbox with justify-content: center and align-items: center, which is slightly more verbose but gives you finer control over spacing between items.

.frame { display: grid; place-items: center; }
Without
onetwothreefourfive
With
onetwothreefourfive

A grid that wraps on its own

The auto-fit keyword tells grid to fit as many columns as possible, and minmax(62px, 1fr) says each column must be at least 62 pixels wide. When the container shrinks below the width needed for all items, they wrap to a new row.

Change the minimum value to control when wrapping happens. Use auto-fill instead of auto-fit when you want empty columns to hold their space rather than collapsing.

.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(62px, 1fr)); }
Without
With

A whole animation in one line

The animation shorthand packs every timing property into one declaration. The name (bounce) links to a @keyframes rule you define separately, and the rest sets duration, easing, iteration count and direction.

The shorthand saves space. The tradeoff is that it can be hard to read when you come back to it later, so you can write each property separately (animation-name, animation-duration, and so on) if that is clearer for your team.

.ball { animation: bounce .6s ease-in-out infinite alternate; }

Try these yourself

None of these shows the full effect until you act. The green bar says what to do, and the dashed ring marks where.

Click in each box and type. Compare the blinking line.

Without
With

Color the text cursor

The caret-color property changes the blinking text cursor without affecting the typed text itself. In the demo the cursor is red while the text keeps its own color, which makes the cursor stand out in a branded form.

A cursor color that blends into the background makes typing difficult, because you lose track of where you are in the field. Pick a color with strong contrast against the input's background, and test it in both light and dark themes if your site supports theme switching.

input { caret-color: #ff6b6b; }

Hover each box to see its pointer.Needs a mouse

Pointer styles
pointerzoom-ingrabhelp waitcrosshairnot-allowedtext

Change the mouse pointer

The cursor property changes the pointer icon when someone hovers over an element. The demo shows eight common values, from pointer (a hand) to not-allowed (a circle with a slash).

Match the pointer to the action. Use grab for draggable items, zoom-in for images that enlarge on click, help for tooltips. The wrong cursor confuses people, so be deliberate about changing it.

.zoomable { cursor: zoom-in; }

Drag across both sentences. Only one highlights.

Without

Drag across this sentence to highlight it.

With

Drag across this sentence to highlight it.

Stop text from being selected

Dragging across text normally highlights it. That selection is useful for copying prose but looks broken on buttons or draggable controls. Setting user-select: none prevents the highlight from appearing.

Safari still requires the -webkit- prefix, so include both lines. Use this sparingly. Preventing selection on body text makes it harder for people to copy passages or look up unfamiliar words, which is hostile to readers.

.label {
  -webkit-user-select: none;
  user-select: none;
}

Try to drag the corner of each box.Best with a mouse

Without
With

Stop text boxes from being resized

Text areas show a draggable corner by default, which lets someone enlarge the box to see more at once. Setting resize: none hides that corner so the box stays the size you designed.

Chat interfaces and single-line comment boxes often disable resizing because a taller box breaks the layout. Someone writing a long message will have to scroll inside a small box. Disable it when your design requires a fixed height.

textarea { resize: none; }

Click in each field. Only one card lights up.

Without
With

React to a focused child

The :focus-within pseudo-class matches a parent when any of its children has focus. In the demo, clicking in the input field focuses it, and the card's border lights up because the card is the input's parent.

Useful for highlighting a form card or an entire table row when someone clicks into a field. One catch: the parent stays highlighted as long as focus is inside, so tabbing through multiple fields in the same parent will keep it lit the whole time.

.field:focus-within { border-color: #b8ff57; }
Without
With

Stop the sideways layout jump

When content overflows and a scrollbar appears, the page jumps sideways because the scrollbar takes up space. Setting scrollbar-gutter: stable reserves that space from the start, so the layout does not shift when the scrollbar shows.

The property only helps where scrollbars are always visible, like Windows and Linux. It has no effect on phones or Macs set to hide scrollbars, because the browser has nothing to reserve space for.

.panel { scrollbar-gutter: stable; }