A variation is a different starting state for the same block; a style is a class name applied to it. They appear in different places in the interface.
// variation: appears in the inserter as its own entry
registerBlockVariation('core/group', {
name: 'callout',
title: 'Callout',
attributes: { backgroundColor: 'brand', layout: { type: 'constrained' } },
scope: ['inserter'],
})
// style: appears in the block sidebar, adds is-style-*
registerBlockStyle('core/quote', { name: 'pull', label: 'Pull quote' })
The scope field decides where a variation appears — inserter, block for the transform menu, or transform — and omitting it makes the variation invisible, which is the most common reason one appears not to have registered. A style is cheaper and covers anything that is purely visual; a variation is right when the block needs different attributes as a starting point.