TocTable of Contents

Css baseline 2017

Overview of 2017 baseline css features

All the features are Widely available because they all passed 30 months since being newly available.

Border images

The border-image property replaces an element’s standard border with a specified image, slice of an image, or CSS gradient. It tiles, stretches, or repeats a source image to fill the border area, allowing for decorative borders that cannot be achieved with border-style alone.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
.ornate {
  border: 20px solid transparent;
  border-image: url('ornate-border.png') 30 round;
}

.gradient-border {
  border: 4px solid transparent;
  border-image: linear-gradient(135deg, #f093fb, #f5576c) 1;
}

.badge {
  border: 8px solid transparent;
  border-image: url('badge-frame.png') 40 stretch;
}

border-image is a shorthand for border-image-source, border-image-slice, border-image-width, border-image-outset, and border-image-repeat.

border-image-slice divides the source image into nine regions using four inset values — four corners, four edges, and the centre. The number values correspond to pixels in raster images or coordinate units in SVG. The corners are placed at the corners of the border; the edges tile or stretch along the sides.

border-image-repeat controls how edge regions fill the space: stretch (default), repeat, round (scales to fit a whole number of tiles), or space (distributes tiles with spacing).

Using a gradient as the border-image-source with a slice value of 1 is a clean technique for gradient borders — the background-clip: padding-box pattern achieves a similar result but border-image is more direct for this specific use case.

column-fill

The column-fill property controls how content is distributed across columns in a multi-column layout. It determines whether columns are filled sequentially (one after another) or balanced (approximately equal height across all columns).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
.article {
  columns: 3;
  column-fill: balance; /* Default: distribute content evenly */
}

.sequential-columns {
  columns: 3;
  height: 400px;
  column-fill: auto; /* Fill first column, then second, etc. */
}

.balanced-fixed {
  columns: 3;
  height: 600px;
  column-fill: balance-all; /* Balance across all columns, even with height set */
}

balance (the default when no height is set) distributes content as evenly as possible across all columns, so they all end at approximately the same point. This is the typical typographic behaviour for flowing text articles.

auto fills columns sequentially from the first — the first column fills to the container’s height, then the second, and so on. This requires a height to be set on the container; without one, auto behaves like balance.

balance-all is similar to balance but forces balancing even when a height is explicitly set — in balance mode, a fixed height can cause sequential overflow into subsequent columns.

column-fill interacts with column-span — a spanning element always breaks the column balance at its position, creating separate balanced groups above and below it.

Cursor styles

The cursor property sets the type of cursor shown when the mouse pointer is over an element. It communicates affordance — what the user can do with the element — and is an important part of interactive UI design.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
.button {
  cursor: pointer; /* Hand cursor, signals clickability */
}

.disabled {
  cursor: not-allowed;
}

.draggable {
  cursor: grab;
}

.draggable:active {
  cursor: grabbing;
}

.resizable {
  cursor: ew-resize; /* East-west resize arrow */
}

.text-area {
  cursor: text;
}

The most commonly used keyword values include default (the standard arrow), pointer (pointing hand for links and buttons), text (I-beam for text), move (four-directional arrow), grab and grabbing (for draggable elements), not-allowed (blocked action), wait (loading), crosshair, and a full set of directional resize cursors (n-resize, ew-resize, nwse-resize, etc.).

Custom cursors can be specified using url(), with fallback keywords:

1
2
3
.custom {
  cursor: url('cursor.png') 8 8, pointer;
}

The two numbers after the URL are the cursor’s hotspot coordinates — the pixel offset of the active click point from the top-left of the image. A fallback keyword is required after any url() value.

cursor: none hides the cursor entirely, which is occasionally used in games or immersive experiences that render their own custom cursor with JavaScript.

Custom properties

Custom properties — also called CSS variables — are author-defined properties that store values for reuse throughout a stylesheet. They are declared with a -- prefix and accessed using the var() function. Unlike preprocessor variables, they are live in the browser: they participate in the cascade, can be inherited, and can be changed at runtime with JavaScript.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
:root {
  --color-primary: oklch(55% 0.2 250);
  --color-text: #1a1a1a;
  --spacing-md: 1rem;
  --border-radius: 6px;
}

.button {
  background: var(--color-primary);
  padding: var(--spacing-md) calc(var(--spacing-md) * 2);
  border-radius: var(--border-radius);
}

var() accepts a fallback as a second argument, used when the custom property is not defined:

1
2
3
.element {
  color: var(--color-accent, var(--color-primary, blue));
}

Custom properties are scoped to the element they are declared on and inherited by descendants — re-declaring a property on a child element overrides it for that subtree:

1
2
3
.card {
  --color-primary: oklch(60% 0.2 30); /* Overrides for this card */
}

They can be set and read in JavaScript:

1
2
element.style.setProperty('--color-primary', 'red');
const value = getComputedStyle(element).getPropertyValue('--color-primary');

@property enables typed custom properties with defined syntax, initial values, and inheritance behaviour — allowing them to be animated and providing better tooling support.

display: table

The display: table value and its related table display values allow non-table HTML elements to be styled with the same layout behaviour as HTML table elements. This gives access to table formatting — automatic column width distribution, row height equalisation, and cell alignment — without requiring <table> markup.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
.table {
  display: table;
  width: 100%;
}

.row {
  display: table-row;
}

.cell {
  display: table-cell;
  padding: 0.5rem 1rem;
  vertical-align: middle;
}

.col-group {
  display: table-column-group;
}

.caption {
  display: table-caption;
  caption-side: bottom;
}

The full set of table display values mirrors the HTML table element hierarchy: table, table-row, table-cell, table-row-group, table-header-group, table-footer-group, table-column, table-column-group, and table-caption.

A key feature of table layout is that cells in the same column automatically share the same width, and cells in the same row share the same height — behaviour that is difficult to replicate with other layout models without JavaScript. vertical-align on display: table-cell elements controls the vertical alignment of content within the cell, making it one of the older techniques for vertical centring.

Table display values were commonly used for layout before flexbox and grid were available, and remain useful in specific scenarios — particularly equal-height columns and vertical alignment — though flexbox and grid are now generally preferred for new layouts.

font-feature-settings

The font-feature-settings property provides low-level control over OpenType font features using four-character feature tags. It is the escape hatch for accessing any OpenType feature that does not have a dedicated CSS property — though where a higher-level property exists (font-variant-*, font-kerning, etc.), that should be preferred.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
.stylistic-set {
  font-feature-settings: 'ss01' 1, 'ss02' 1;
}

.oldstyle-nums {
  font-feature-settings: 'onum' 1;
}

.no-ligatures {
  font-feature-settings: 'liga' 0, 'clig' 0;
}

.small-caps {
  font-feature-settings: 'smcp' 1;
}

.tabular {
  font-feature-settings: 'tnum' 1;
}

Each feature is identified by its four-character OpenType tag followed by a value — 1 to enable, 0 to disable, or an integer for features that accept a selection index (like cv01 through cv99 character variants). Multiple features are comma-separated.

Common tags include kern (kerning), liga (standard ligatures), clig (contextual ligatures), onum (oldstyle numerals), tnum (tabular numerals), smcp (small capitals), sups (superscripts), subs (subscripts), frac (fractions), zero (slashed zero), and ss01ss20 (stylistic sets).

Because font-feature-settings operates at the lowest level of font feature control, it overrides settings made by higher-level properties when both target the same feature. It is best used specifically for features that have no CSS property equivalent, leaving the rest to font-variant-* properties.

Grid

CSS Grid Layout is a two-dimensional layout system that allows elements to be placed into rows and columns simultaneously. A grid container is created with display: grid, and its children become grid items that can be precisely placed within the defined grid structure.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
.layout {
  display: grid;
  grid-template-columns: 240px 1fr;
  grid-template-rows: auto 1fr auto;
  gap: 1.5rem;
  min-height: 100vh;
}

.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 1.5rem;
}

grid-template-columns and grid-template-rows define the track structure. The fr unit distributes remaining space proportionally. repeat() creates repeated track patterns, and auto-fill or auto-fit with minmax() creates responsive grids that reflow without media queries.

Items can be explicitly placed using grid-column and grid-row with line numbers or named lines:

1
2
3
4
5
6
7
.header {
  grid-column: 1 / -1; /* Span all columns */
}

.sidebar {
  grid-row: 2 / 4;
}

grid-template-areas provides a visual syntax for layout:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
.page {
  display: grid;
  grid-template-areas:
    'header header'
    'sidebar main'
    'footer footer';
  grid-template-columns: 240px 1fr;
}

.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main    { grid-area: main; }
.footer  { grid-area: footer; }

Grid and flexbox complement each other — grid excels at two-dimensional layouts where both axes matter simultaneously, while flexbox is better suited to one-dimensional flow along a single axis.

Multi-column layout

CSS multi-column layout flows content through multiple vertical columns, similar to a newspaper or magazine layout. It is established with columns, column-count, or column-width on a container element, and content flows automatically from one column to the next.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
.article {
  columns: 3; /* Three equal-width columns */
  gap: 2rem;
}

.narrow-text {
  column-width: 20rem; /* As many columns as fit at this width */
}

.max-columns {
  column-count: 4; /* Exactly four columns */
}

columns is a shorthand for column-count and column-width. When both are specified, column-width acts as a minimum width and column-count as a maximum — the browser creates as many columns of at least that width as will fit, up to the specified count.

Column gaps and rules are controlled with gap (or column-gap) and column-rule:

1
2
3
4
5
.article {
  columns: 3;
  column-gap: 3rem;
  column-rule: 1px solid #e0e0e0; /* Vertical divider between columns */
}

column-rule is a shorthand for column-rule-width, column-rule-style, and column-rule-color, and works like border but draws a line between columns without affecting layout.

Content can span all columns with column-span: all, and column breaking can be controlled with break-before, break-after, and break-inside. Multi-column layout is well suited for editorial content, card grids with fixed widths, and any context where a newspaper-style flow is appropriate.

Text stroke and fill (compatibility prefixes)

Text stroke and fill effects on HTML text elements are achieved using -webkit-text-stroke and -webkit-text-fill-color — properties that originated as WebKit extensions and have never been standardised, but are now supported across all major browsers due to their widespread adoption.

-webkit-text-stroke adds an outline to text glyphs. It is a shorthand for -webkit-text-stroke-width and -webkit-text-stroke-color. -webkit-text-fill-color sets the fill color of text independently of the color property, allowing the two to differ.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
.outlined-text {
  -webkit-text-stroke: 2px #1a1a1a;
  -webkit-text-fill-color: transparent; /* Outline only, no fill */
}

.gradient-text {
  background: linear-gradient(135deg, #f093fb, #f5576c);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
}

.thick-stroke {
  -webkit-text-stroke-width: 3px;
  -webkit-text-stroke-color: navy;
  -webkit-text-fill-color: white;
}

The gradient text technique combines -webkit-background-clip: text with -webkit-text-fill-color: transparent to let a background gradient show through the text shape. The unprefixed background-clip: text is now also supported in modern browsers, so both forms are often included for completeness.

Because these properties remain prefixed and are not part of any CSS standard, they should be treated as progressive enhancements. The paint-order property on SVG text offers a more standards-aligned approach to stroke and fill ordering, though it does not directly replicate all of these effects on HTML elements.

text-justify

The text-justify property controls the justification algorithm used when text-align: justify is set. It determines how the browser distributes the extra space needed to make lines fill their container — whether by adjusting word spacing, character spacing, or using language-appropriate methods.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
.article {
  text-align: justify;
  text-justify: auto; /* Default: browser chooses the best method */
}

.inter-word {
  text-align: justify;
  text-justify: inter-word; /* Space added between words only */
}

.inter-character {
  text-align: justify;
  text-justify: inter-character; /* Space distributed between characters */
}

.none {
  text-align: justify;
  text-justify: none; /* Disables justification, same as text-align: start */
}

auto allows the browser to choose the most appropriate method based on the content’s language and script. For Latin text this is typically inter-word; for CJK text it is inter-character, which distributes space between individual characters — the typographically appropriate approach for ideographic scripts.

inter-word is the standard approach for Latin text — only the spaces between words are stretched, maintaining normal character spacing within words. inter-character (equivalent to the older distribute value) spreads space across all character boundaries, which can look unusual for Latin text but is conventional for CJK.

Browser support for values other than auto and none is inconsistent, making auto the most reliable choice in practice.

writing-mode

The writing-mode property sets whether text runs horizontally or vertically, and the direction of the block flow. It is the fundamental property for supporting vertical writing systems used in East Asian typography, and is also used for creative typographic effects with Latin text.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
.horizontal {
  writing-mode: horizontal-tb; /* Default: left to right, top to bottom */
}

.vertical-right {
  writing-mode: vertical-rl; /* Top to bottom, right to left column flow */
}

.vertical-left {
  writing-mode: vertical-lr; /* Top to bottom, left to right column flow */
}

.sideways {
  writing-mode: sideways-rl; /* Rotates the entire line sideways */
}

horizontal-tb is the default for most languages — text runs left to right (or right to left for Arabic/Hebrew, controlled by direction) and lines stack top to bottom.

vertical-rl is the standard mode for traditional Japanese and Chinese vertical text — characters flow top to bottom, and columns progress from right to left. vertical-lr is less common but used in some contexts where columns progress left to right.

Changing writing-mode affects the entire box model — the block axis and inline axis swap, so width controls what was previously height, and height controls what was previously width. Logical properties (block-size, inline-size, margin-block, padding-inline, etc.) automatically adapt to the writing mode, making them the preferred choice for components that may be used in multiple writing directions.

writing-mode also affects form controls, making it possible to create vertical range sliders and progress bars.

writing-mode SVG 1.1 values

SVG 1.1 defined its own set of writing-mode values — lr, lr-tb, rl, rl-tb, tb, and tb-rl — which predate the CSS Writing Modes specification and used different naming conventions. These values were adopted into CSS for SVG compatibility but are now considered legacy and deprecated in favour of the standard CSS values.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
/* SVG 1.1 legacy values (deprecated) */
text {
  writing-mode: tb-rl; /* Top to bottom, right to left — now: vertical-rl */
}

text {
  writing-mode: lr-tb; /* Left to right, top to bottom — now: horizontal-tb */
}

/* Modern CSS equivalents */
text {
  writing-mode: vertical-rl;
}

text {
  writing-mode: horizontal-tb;
}

The mapping between legacy and modern values is straightforward: lr and lr-tb map to horizontal-tb; rl and rl-tb also map to horizontal-tb (with direction: rtl for right-to-left); tb and tb-rl map to vertical-rl.

Modern browsers continue to support the legacy SVG values for backwards compatibility, but they should not be used in new code. The CSS Writing Modes values (horizontal-tb, vertical-rl, vertical-lr, sideways-rl, sideways-lr) are the standard and work consistently in both HTML and SVG contexts.

When authoring inline SVG within HTML documents, the CSS writing mode properties apply and the modern values should be used.