Block style wrapper#

Added in version 16.0.0.

The block style wrapper is part of a block’s anatomy. It wraps both the edit and view components, and applies styles declared in the block schema in two ways:

The wrapper is always present. Enable styling by adding a styles object field to your block schema. The wrapper reads values from the styles object and injects them into the edit and view components.

Tip

Prefer custom CSS properties. They are flexible, cascade-friendly, and pragmatic for theming and per-block overrides. Use generated class names for discrete, pre-defined variants.

Quick start#

Add a styles object to your block schema. Instead of doing it manually every time, use the addStyling helper:

import { addStyling } from '@plone/volto/helpers/Extensions/withBlockSchemaEnhancer';

export const MyBlockSchema = ({ intl }) => {
  const schema = { /* your block schema */ };

  // Adds an empty `styles` object field under a "Styling" fieldset
  addStyling({ schema, intl });

  // Add your styling controls
  schema.properties.styles.schema.properties.align = {
    widget: 'align',
    title: 'Alignment',
    actions: ['left', 'center', 'right'],
  };
  schema.properties.styles.schema.fieldsets[0].fields.push('align');

  return schema;
};

You can compose reusable styling controls using composeSchema:

import { composeSchema } from '@plone/volto/helpers';

config.blocks.blocksConfig.listing.schemaEnhancer = composeSchema(
  config.blocks.blocksConfig.listing.schemaEnhancer,
  addStyling,
  addMyCustomColorStylingField,

Note

The styles field is not added automatically. Use addStyling, or add it manually as an object widget. Values defined in styles are injected by the wrapper in edit and view components.

Generated class names#

The wrapper can generate class names from the styles object. This is useful when mapping discrete, curated style tokens to theme classes.

Class names are built by concatenating key–value pairs with -- and prefixing with has--. One level of nesting is supported.

Given:

{
  "styles": {
    "backgroundColor": "#ee22ee",
    "myCustomStyleField": "red",
    "myCustom2StyleField": {
      "color": "black",
      "gradient": "MyGradient"
    }
  }
}

Resulting classes:

<div class="has--backgroundColor--ee22ee has--myCustomStyleField--red has--myCustom2StyleField--color--black has--myCustom2StyleField--gradient--MyGradient">

Pass the injected className to the root element in your view component:

const BlockView = (props) => (
  <div className={props.className}>
    {/* Block's view component code */}
  </div>
);

Customize generated class names#

You can customize class generation with converters in config.settings.styleClassNameConverters by suffixing field names with the converter name. For example:

{
  "styles": {
    "theme:noprefix": "primary",
    "inverted:bool": true
  }
}

Generates:

primary inverted

Error class for blocks#

When a user submits data and the block has a validation error (for example, via blocksErrors), the wrapper automatically adds the error class. This allows you to customize the appearance of errors via CSS:

.block.error {
  border: 1px solid red;
  background: #fff0f0;
}

The class is added automatically by the block engine.

Align class injection#

While injecting style wrapper classes, the engine also injects data.align as a class for layout backwards compatibility. Each block editor wrapper has block-editor-<block_id> <block_align>, for example:

<div data-rbd-draggable-context-id="0" data-rbd-draggable-id="9949a5fa-5d57-4e0c-a150-71149a31096c" class="block-editor-listing center">
  ...
</div>

Use this to help position blocks within legacy layouts if needed.

Style object builder enhancer#

The style wrapper exposes a helper that can programmatically build or modify a style object (inline variables) from block data. Register a utility of type styleWrapperStyleObjectEnhancer:

config.registerUtility({
  name: 'blockThemesEnhancer',
  type: 'styleWrapperStyleObjectEnhancer',
  method: blockThemesEnhancer,
});

The registered utility has the following signature.

type blockThemesEnhancerType = ({
  data,
  container,
}: {
  data: BlocksFormData;
  container: BlocksFormData;
}) => Record<`--${string}`, string>;

data is the current block, and container is its parent block, when applicable. The utility returns a record of CSS custom properties to inject.

Best practices#

  • Prefer custom CSS properties for most styling needs. They compose well and avoid class name bloat.

  • Reserve generated classes for semantic variants that map to predefined theme classes.

  • Keep styles values human-friendly in the UI, but map to robust tokens in CSS.

  • Avoid mixing both mechanisms for the same concern. Pick variables or classes per concern.

  • Always pass className or style to your block's root element in the view component.