em assumes the parent's font size equals the base. 1pt = 1/72 inch = 1.333px.
| px | rem | pt |
|---|
Convert px to rem one value at a time, look up
common sizes in the table, or paste a stylesheet and convert every
pixel value in it at once. The converter skips comments, strings
and url() paths, so icon-24px.svg stays a
filename.
em assumes the parent's font size equals the base. 1pt = 1/72 inch = 1.333px.
| px | rem | pt |
|---|
Base 16, Keep 1px on, Skip @media on. The border stays a 1px hairline and the breakpoint stays in pixels. Every other px value becomes rem.
.card {
padding: 16px 24px;
margin-bottom: -8px;
border: 1px solid #e2e8f0;
font: 14px/1.5 system-ui;
}
@media (min-width: 768px) {
.card { padding: 32px; }
} .card {
padding: 1rem 1.5rem;
margin-bottom: -0.5rem;
border: 1px solid #e2e8f0;
font: 0.875rem/1.5 system-ui;
}
@media (min-width: 768px) {
.card { padding: 2rem; }
} A rem is the font size of the root element. Say a visitor sets their browser's default text size to 20px because 16px is too small for them. Everything sized in rem grows with it, and everything sized in px stays the same. Browser zoom scales both units, so the difference only shows for people who changed the text size setting, and those are the readers who need it.
Set Base to the root font size your CSS uses. That is 16px unless your CSS sets html { font-size }. The older 62.5% trick makes 1rem equal 10px so the math is easy. If your stylesheet uses it, set Base to 10.
Keep 1px borders in px. As 0.0625rem, a border becomes 0.75px for someone whose default text size is 12px, and browsers round fractional borders inconsistently. Keep 1px leaves both 1px and -1px unchanged.
rem in a media query always uses the browser default, never your html font-size. Converting breakpoints makes them follow the user's text setting. Turn on Skip @media if you want the layout to switch at fixed widths instead.
Choose em output only for spacing that should scale with the element's own text, such as button padding. Nested em font sizes compound, so 1.2em inside 1.2em is 1.44 times the base.
The converter leaves zero values alone, along with anything inside comments, quoted strings and url(), and px inside identifiers such as a class named .w-16px. It rounds results to four decimal places.
const pxToRem = (px, base = 16) => +(px / base).toFixed(4) + "rem";
console.log(pxToRem(24)); // "1.5rem"
console.log(pxToRem(14, 10)); // "1.4rem"
// Convert every px value in a CSS string, leaving 0 and 1px alone.
// (No handling of comments or url() here; the tool on this page does that.)
function convertCss(css, base = 16) {
return css.replace(/(-?\d*\.?\d+)px\b/g, (m, n) => {
const v = Math.abs(parseFloat(n));
return v === 0 || v === 1 ? m : pxToRem(parseFloat(n), base);
});
}
console.log(convertCss(".card { padding: 16px 24px; border: 1px solid; }"));
// .card { padding: 1rem 1.5rem; border: 1px solid; } In a build pipeline, the postcss-pxtorem plugin does this on every compile, so you can keep writing px in source if you prefer.
@use "sass:math";
@function rem($px, $base: 16px) {
@return math.div($px, $base) * 1rem;
}
.card {
padding: rem(16px) rem(24px); // 1rem 1.5rem
font-size: rem(14px); // 0.875rem
border: 1px solid #e2e8f0; // hairline stays in px
} math.div replaced the / operator for division in Dart Sass. The old slash form prints a deprecation warning and will stop working in a future release.
import re
def px_to_rem(css: str, base: float = 16) -> str:
def repl(m):
value = float(m.group(1))
if abs(value) in (0, 1):
return m.group(0)
return f"{round(value / base, 4):g}rem"
return re.sub(r"(-?\d*\.?\d+)px\b", repl, css)
print(px_to_rem(".card { padding: 16px 24px; margin: -8px; border: 1px solid; }"))
# .card { padding: 1rem 1.5rem; margin: -0.5rem; border: 1px solid; } The rem unit arrived with CSS3 and has been supported by every browser since IE9. Before it, em was the only relative option, and em values compound through nested elements, which made layout sizing hard to predict. rem avoids that by always measuring from the html element.
Convert to rem so your layout follows the browser's default font size setting. Page zoom scales px and rem equally, so zoom is unaffected. What px ignores is the separate text size setting, which many people with low vision change. WCAG 1.4.4 asks that text can be resized to 200% without loss of content, and rem-based layouts make that easier to meet.
This tool follows the specifications below. Read them when you need the exact normative wording, for example to settle an edge case between two implementations.
Divide the pixel value by the root font size. With the default 16px root, 24px is 24 / 16 = 1.5rem and 12px is 0.75rem. If your CSS sets html { font-size: 62.5% }, the root is 10px and you divide by 10 instead.
rem is relative to the root element's font size, so it means the same thing everywhere on the page. em is relative to the font size of the element itself (or its parent, when used for font-size), so nested em values compound.
Only by default. 1rem equals the html element's font size, which is 16px in every major browser unless the user changed their default text size or your CSS overrides it. That user setting is the main reason to use rem.
Usually not. A hairline should stay 1px, and 0.0625rem turns into a fractional width when a user's text size is not 16px. The Keep 1px option leaves 1px and -1px untouched.
In CSS, 1pt is 1/72 of an inch and 1px is 1/96 of an inch, so 1pt = 1.333px and 12pt = 16px. Points are for print stylesheets; on screen, use px or rem.
Once the sizes are in rem, lay them out with the CSS grid generator or the flexbox generator. To tidy the converted stylesheet, run it through the CSS formatter.