%
A share of some other value. Which value depends on the property.
Safe to useBaseline: widely available
| Browser | Since |
|---|---|
| Chrome | 1+ |
| Chrome Android | 18+ |
| Edge | 12+ |
| Firefox | 1+ |
| Firefox Android | 4+ |
| Safari | 1+ |
| Safari iOS | 1+ |
Formula N% = N/100 × the reference value of the property
How it works
Put 10% on a child inside a box 400px wide and 300px tall and the answer depends on the property: width gives 40px, height gives 30px, margin-block-start gives 40px, and font-size gives a tenth of the parent’s text size. A percentage means nothing until you know which property it sits on, and every surprise in this unit comes from that.
The surprising ones first. padding and margin on all four sides are measured against the width of the box that contains them (its height in vertical text), so in that box margin-block-start: 10% is 40px and not 30px. line-height takes the font size of the element it sits on, and the result is turned into a length before it is inherited, so a child with larger text keeps the parent’s number, unlike a unitless line height, which is inherited as a ratio. translate uses the element’s own box, so translate: 50% on a 100px-wide element moves it 50px. The plainer ones: font-size uses the parent’s font size, so 120% is 1.2em; width and the horizontal offsets use the containing block’s width; height and the vertical offsets use its height, but only when that height is definite, and if the parent is as tall as its content, height: 50% has nothing to be half of and behaves as auto.
When a percentage misbehaves, find which of these it belongs to before changing the value.
Try it
One percentage, seven references
Change the size of the box. Each row is a different property, and each takes its percentage of a different thing.
| Declaration | Resolved to | Measured against |
|---|---|---|
width: 50% | The width of the containing block | |
height: 50% | The height of the containing block, which has to be set | |
padding-block-start: 10% | The width of the containing block | |
margin-block-start: 10% | The width of the containing block | |
translate: 50% | The width of the element itself (100px here) | |
font-size: 120% | The font size of the parent (16px here) | |
line-height: 150% | The text size set on the element, 20px here |
The same as a table
| Declaration | Resolved to | Measured against |
|---|---|---|
width: 50% | 200px | The width of the containing block |
height: 50% | 150px | The height of the containing block, which has to be set |
padding-block-start: 10% | 40px | The width of the containing block |
margin-block-start: 10% | 40px | The width of the containing block |
translate: 50% | 50px | The width of the element itself (100px here) |
font-size: 120% | 19.2px | The font size of the parent (16px here) |
line-height: 150% | 30px | The text size set on the element, 20px here |
What changes it
- Moves with
- page zoom
- Stays put under
- the reader's text size
- the window size
- the font in use
- the query container
- phone toolbars
- the writing mode
And whatever moves the property's own reference: the parent's width, the element's box, the inherited font size.
Reach for it when
Widths that share a parent, and translate() or border-radius, which take their percentage of the element's own box.
Watch out
In a box 400px wide, padding-block: 10% is 40px whatever the height of the box. In horizontal text, padding and margin take their percentage from the width on all four sides.
.story-head {
padding-block: 5%;
}