Most people add another field, such as a select list with predefined options, and turn that into a class using a preprocess, or merge in the paragraph twig template. If don't have a predefined list, and you allow user input, you will need to sanitize the input.
How I do it in the twig template is like this:
{# Renders Width field. #}
{# Sets class name from values in database. #}
{% if content.bp_width|render %}
{% set layout_width = content.bp_width['#items'].getString() %}
{% set layout_width_classes = [
'paragraph--width--tiny' == layout_width ? 'paragraph--width--tiny',
'paragraph--width--narrow' == layout_width ? 'paragraph--width--narrow',
'paragraph--width--medium' == layout_width ? 'paragraph--width--medium',
'paragraph--width--wide' == layout_width ? 'paragraph--width--wide',
'paragraph--width--full' == layout_width ? 'paragraph--width--full',
]
%}
{% endif %}
{# The template default set classes. #}
{%
set classes = [
'paragraph',
'paragraph--type--' ~ paragraph.bundle|clean_class,
view_mode ? 'paragraph--view-mode--' ~ view_mode|clean_class,
]
%}
{# Merges Width field with classes. #}
{% set width_field = content.bp_width|render %}
{% if width_field %}
{% set classes = classes|merge(layout_width_classes) %}
{% endif %}
{# Prints div with classes, and content without Width field. #}
<div{{ attributes.addClass(classes) }}>
<div class="paragraph__column">
{{ content|without('bp_width') }}
</div>
</div>
How do the Bootstrap styles get added? Here is an excerpt from the LESS file:
.paragraph {
.make-row();
margin-left: 0;
margin-right: 0;
}
.paragraph > .paragraph__column {
.make-sm-column(12);
.make-md-column(12);
.make-lg-column(12);
padding-bottom: @jumbotron-padding;
}
Which compiles into the following CSS:
.paragraph {
margin-left: -15px;
margin-right: -15px;
margin-left: 0;
margin-right: 0;
}
.paragraph > .paragraph__column {
position: relative;
min-height: 1px;
padding-left: 15px;
padding-right: 15px;
padding-bottom: 30px;
}
You can overwrite that in your theme if needed.