A block variation preselects attributes; a style adds a class

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.