A Sass variable is substituted at compile time and does not exist in the output. A custom property is in the cascade, inherits, and can be changed at runtime — which makes them useful for different things.
:root {
--gap: 1rem;
--brand: #2b6cb0;
}
.card { padding: var(--gap); border-color: var(--brand); }
@media (min-width: 60em) {
:root { --gap: 2rem; } /* one line, everything follows */
}
[data-theme="dark"] { --brand: #90cdf4; }
Redefining a property on a selector rather than restating every rule is the thing Sass cannot do, and it is what makes theming a matter of one attribute on the root element. They cannot be used in media query conditions, which is the limitation people hit first. The fallback syntax — var(--gap, 1rem) — covers the property being undefined, not the browser lacking support, and the difference matters for IE11 where nothing here works at all.