A Sass variable is resolved at build time and does not exist in the output. A custom property is a real value in the cascade, which means it inherits, it can be changed per selector, and JavaScript can read and write it.
:root { --gutter: 16px; }
.compact { --gutter: 8px; }
.card { padding: var(--gutter); } // 16px, or 8px inside .compact
// and at runtime
document.documentElement.style.setProperty('--gutter', '24px');
That is the capability no preprocessor has: theming without recompiling, and a value a media query can change. Support is Chrome, Firefox and Safari but not IE or Edge yet, so this year it is additive — declare a static fallback before the var() and browsers that do not understand it use the first declaration.