API version 2 moves the block wrapper from the editor into the block, which means the editor markup and the front-end markup can finally match.
// apiVersion 2
export default function Edit({ attributes }) {
const blockProps = useBlockProps({
className: `is-tone-${attributes.tone}`,
})
return <div {...blockProps}>...</div>
}
// and in save, the matching call
const blockProps = useBlockProps.save({ className: ... })
Forgetting the call produces a block that renders without its alignment, its colour or its spacing in the editor while working correctly on the front end, which is a confusing failure to diagnose. The save variant must be used in the save function specifically — using the editor one there produces markup containing editor-only attributes, which then fails validation.