diff --git a/src/Elements/BodyComponents/MjWrapper.php b/src/Elements/BodyComponents/MjWrapper.php
new file mode 100644
index 0000000..b169583
--- /dev/null
+++ b/src/Elements/BodyComponents/MjWrapper.php
@@ -0,0 +1,261 @@
+>
+ */
+ protected array $allowedAttributes = [
+ 'background-color' => [
+ 'unit' => 'color',
+ 'type' => 'color',
+ 'description' => 'wrapper background color',
+ 'default_value' => '',
+ ],
+ 'background-url' => [
+ 'unit' => 'string',
+ 'type' => 'string',
+ 'description' => 'wrapper background image url',
+ 'default_value' => '',
+ ],
+ 'background-repeat' => [
+ 'unit' => 'string',
+ 'type' => 'string',
+ 'description' => 'wrapper background repeat value',
+ 'default_value' => 'repeat',
+ ],
+ 'background-size' => [
+ 'unit' => 'string',
+ 'type' => 'string',
+ 'description' => 'wrapper background size',
+ 'default_value' => 'auto',
+ ],
+ 'background-position' => [
+ 'unit' => 'string',
+ 'type' => 'string',
+ 'description' => 'wrapper background position',
+ 'default_value' => 'top center',
+ ],
+ 'background-position-x' => [
+ 'unit' => 'string',
+ 'type' => 'string',
+ 'description' => 'wrapper background position x value',
+ 'default_value' => '',
+ ],
+ 'background-position-y' => [
+ 'unit' => 'string',
+ 'type' => 'string',
+ 'description' => 'wrapper background position y value',
+ 'default_value' => '',
+ ],
+ 'border' => [
+ 'unit' => 'string',
+ 'type' => 'string',
+ 'description' => 'wrapper border format',
+ 'default_value' => 'none',
+ ],
+ 'border-bottom' => [
+ 'unit' => 'string',
+ 'type' => 'string',
+ 'description' => 'wrapper border bottom',
+ 'default_value' => '',
+ ],
+ 'border-left' => [
+ 'unit' => 'string',
+ 'type' => 'string',
+ 'description' => 'wrapper border left',
+ 'default_value' => '',
+ ],
+ 'border-radius' => [
+ 'unit' => 'px',
+ 'type' => 'measure',
+ 'description' => 'wrapper border radius',
+ 'default_value' => '',
+ ],
+ 'border-right' => [
+ 'unit' => 'string',
+ 'type' => 'string',
+ 'description' => 'wrapper border right',
+ 'default_value' => '',
+ ],
+ 'border-top' => [
+ 'unit' => 'string',
+ 'type' => 'string',
+ 'description' => 'wrapper border top',
+ 'default_value' => '',
+ ],
+ 'css-class' => [
+ 'unit' => 'string',
+ 'type' => 'string',
+ 'description' => 'class name added to root HTML element',
+ 'default_value' => '',
+ ],
+ 'padding' => [
+ 'unit' => 'px',
+ 'type' => 'string',
+ 'description' => 'supports up to 4 parameters',
+ 'default_value' => '20px 0',
+ ],
+ 'padding-bottom' => [
+ 'unit' => 'px',
+ 'type' => 'measure',
+ 'description' => 'bottom offset',
+ 'default_value' => '',
+ ],
+ 'padding-left' => [
+ 'unit' => 'px',
+ 'type' => 'measure',
+ 'description' => 'left offset',
+ 'default_value' => '',
+ ],
+ 'padding-right' => [
+ 'unit' => 'px',
+ 'type' => 'measure',
+ 'description' => 'right offset',
+ 'default_value' => '',
+ ],
+ 'padding-top' => [
+ 'unit' => 'px',
+ 'type' => 'measure',
+ 'description' => 'top offset',
+ 'default_value' => '',
+ ],
+ 'text-align' => [
+ 'unit' => 'string',
+ 'type' => 'alignment',
+ 'description' => 'left/right/center/justify',
+ 'default_value' => 'center',
+ ],
+ ];
+
+ protected array $defaultAttributes = [
+ 'background-repeat' => 'repeat',
+ 'background-size' => 'auto',
+ 'background-position' => 'top center',
+ 'border' => 'none',
+ 'padding' => '20px 0',
+ 'text-align' => 'center',
+ ];
+
+ public function render(): string
+ {
+ $tableAttributes = $this->getHtmlAttributes([
+ 'align' => 'center',
+ 'class' => $this->getAttribute('css-class'),
+ 'background' => $this->getAttribute('background-url'),
+ 'border' => '0',
+ 'cellpadding' => '0',
+ 'cellspacing' => '0',
+ 'role' => 'presentation',
+ 'style' => 'table',
+ ]);
+
+ $tdAttributes = $this->getHtmlAttributes([
+ 'style' => 'td',
+ ]);
+
+ $children = $this->getChildren() ?? [];
+ $content = $this->renderChildren($children, []);
+
+ return "
";
+ }
+
+ /**
+ * @return array>
+ */
+ public function getStyles(): array
+ {
+ $background = $this->getAttribute('background-url') ?
+ [
+ 'background' => $this->getBackground(),
+ 'background-position' => $this->getAttribute('background-position'),
+ 'background-repeat' => $this->getAttribute('background-repeat'),
+ 'background-size' => $this->getAttribute('background-size'),
+ ] :
+ [
+ 'background' => $this->getAttribute('background-color'),
+ 'background-color' => $this->getAttribute('background-color'),
+ ];
+
+ return [
+ 'table' => array_merge($background, [
+ 'width' => '100%',
+ 'border-radius' => $this->getAttribute('border-radius'),
+ ]),
+ 'td' => [
+ 'border' => $this->getAttribute('border'),
+ 'border-bottom' => $this->getAttribute('border-bottom'),
+ 'border-left' => $this->getAttribute('border-left'),
+ 'border-right' => $this->getAttribute('border-right'),
+ 'border-top' => $this->getAttribute('border-top'),
+ 'direction' => 'ltr',
+ 'font-size' => '0px',
+ 'padding' => $this->getAttribute('padding'),
+ 'padding-bottom' => $this->getAttribute('padding-bottom'),
+ 'padding-left' => $this->getAttribute('padding-left'),
+ 'padding-right' => $this->getAttribute('padding-right'),
+ 'padding-top' => $this->getAttribute('padding-top'),
+ 'text-align' => $this->getAttribute('text-align'),
+ ],
+ ];
+ }
+
+ private function getBackground(): string
+ {
+ $bgUrl = $this->getAttribute('background-url');
+ $bgSize = $this->getAttribute('background-size');
+
+ $backgroundParts = [];
+
+ if ($this->getAttribute('background-color')) {
+ $backgroundParts[] = $this->getAttribute('background-color');
+ }
+
+ if ($bgUrl) {
+ $backgroundParts[] = "url('$bgUrl')";
+ $backgroundParts[] = $this->getAttribute('background-position');
+ $backgroundParts[] = "/ $bgSize";
+ $backgroundParts[] = $this->getAttribute('background-repeat');
+ }
+
+ return implode(' ', array_filter($backgroundParts));
+ }
+}
diff --git a/tests/Unit/Elements/BodyComponents/MjWrapperTest.php b/tests/Unit/Elements/BodyComponents/MjWrapperTest.php
new file mode 100644
index 0000000..64ef245
--- /dev/null
+++ b/tests/Unit/Elements/BodyComponents/MjWrapperTest.php
@@ -0,0 +1,205 @@
+element = new MjWrapper();
+});
+
+it('is not ending tag', function () {
+ expect($this->element->isEndingTag())->toBe(false);
+});
+
+it('returns the correct component name', function () {
+ expect($this->element->getTagName())->toBe('mj-wrapper');
+});
+
+it('returns the correct default attributes', function () {
+ $attributes = [
+ 'background-repeat' => 'repeat',
+ 'background-size' => 'auto',
+ 'background-position' => 'top center',
+ 'border' => 'none',
+ 'padding' => '20px 0',
+ 'text-align' => 'center',
+ ];
+
+ foreach ($attributes as $key => $value) {
+ expect($this->element->getAttribute($key))->toBe($value);
+ }
+});
+
+it('will throw out of bounds exception if the allowed attribute is not existing', function () {
+ $this->element->getAllowedAttributeData('invalid-attribute');
+})->throws(OutOfBoundsException::class);
+
+it('will return allowed attribute data', function () {
+ $data = $this->element->getAllowedAttributeData('background-color');
+ expect($data)->toBeArray();
+ expect($data)->toHaveKey('type');
+ expect($data)->toHaveKey('unit');
+});
+
+it('will correctly render a simple wrapper', function () {
+ $wrapperNode = new MjmlNode(
+ 'mj-wrapper',
+ null,
+ null,
+ false,
+ null
+ );
+
+ $factory = new ElementFactory();
+ $mjWrapperElement = $factory->create($wrapperNode);
+
+ expect($mjWrapperElement)->toBeInstanceOf(MjWrapper::class);
+
+ $out = $mjWrapperElement->render();
+
+ expect($out)->toContain('toContain('role="presentation"');
+ expect($out)->not->toBeEmpty();
+});
+
+it('will correctly render a wrapper with background color', function () {
+ $wrapperNode = new MjmlNode(
+ 'mj-wrapper',
+ ['background-color' => '#f0f0f0'],
+ null,
+ false,
+ null
+ );
+
+ $factory = new ElementFactory();
+ $mjWrapperElement = $factory->create($wrapperNode);
+
+ $out = $mjWrapperElement->render();
+
+ expect($out)->toContain('#f0f0f0');
+ expect($out)->not->toBeEmpty();
+});
+
+it('will correctly render a wrapper with background URL', function () {
+ $wrapperNode = new MjmlNode(
+ 'mj-wrapper',
+ ['background-url' => 'https://example.com/bg.jpg'],
+ null,
+ false,
+ null
+ );
+
+ $factory = new ElementFactory();
+ $mjWrapperElement = $factory->create($wrapperNode);
+
+ $out = $mjWrapperElement->render();
+
+ expect($out)->toContain('https://example.com/bg.jpg');
+ expect($out)->not->toBeEmpty();
+});
+
+it('will correctly render a wrapper with padding', function () {
+ $wrapperNode = new MjmlNode(
+ 'mj-wrapper',
+ ['padding' => '30px 10px'],
+ null,
+ false,
+ null
+ );
+
+ $factory = new ElementFactory();
+ $mjWrapperElement = $factory->create($wrapperNode);
+
+ $out = $mjWrapperElement->render();
+
+ expect($out)->toContain('30px 10px');
+ expect($out)->not->toBeEmpty();
+});
+
+it('will correctly render a wrapper with border', function () {
+ $wrapperNode = new MjmlNode(
+ 'mj-wrapper',
+ ['border' => '2px solid #000'],
+ null,
+ false,
+ null
+ );
+
+ $factory = new ElementFactory();
+ $mjWrapperElement = $factory->create($wrapperNode);
+
+ $out = $mjWrapperElement->render();
+
+ expect($out)->toContain('2px solid #000');
+ expect($out)->not->toBeEmpty();
+});
+
+it('will correctly render a wrapper with border-radius', function () {
+ $wrapperNode = new MjmlNode(
+ 'mj-wrapper',
+ ['border-radius' => '10px'],
+ null,
+ false,
+ null
+ );
+
+ $factory = new ElementFactory();
+ $mjWrapperElement = $factory->create($wrapperNode);
+
+ $out = $mjWrapperElement->render();
+
+ expect($out)->toContain('10px');
+ expect($out)->not->toBeEmpty();
+});
+
+it('will correctly render a wrapper with text-align', function () {
+ $wrapperNode = new MjmlNode(
+ 'mj-wrapper',
+ ['text-align' => 'left'],
+ null,
+ false,
+ null
+ );
+
+ $factory = new ElementFactory();
+ $mjWrapperElement = $factory->create($wrapperNode);
+
+ $out = $mjWrapperElement->render();
+
+ expect($out)->toContain('left');
+ expect($out)->not->toBeEmpty();
+});
+
+it('will correctly render a wrapper with all custom properties', function () {
+ $wrapperNode = new MjmlNode(
+ 'mj-wrapper',
+ [
+ 'background-color' => '#e0e0e0',
+ 'background-url' => 'https://example.com/image.jpg',
+ 'padding' => '40px 20px',
+ 'border' => '1px solid #ccc',
+ 'border-radius' => '8px',
+ 'text-align' => 'right',
+ ],
+ null,
+ false,
+ null
+ );
+
+ $factory = new ElementFactory();
+ $mjWrapperElement = $factory->create($wrapperNode);
+
+ $out = $mjWrapperElement->render();
+
+ expect($out)->toContain('#e0e0e0');
+ expect($out)->toContain('https://example.com/image.jpg');
+ expect($out)->toContain('40px 20px');
+ expect($out)->toContain('1px solid #ccc');
+ expect($out)->toContain('8px');
+ expect($out)->toContain('right');
+ expect($out)->not->toBeEmpty();
+});