diff --git a/src/Elements/BodyComponents/MjHero.php b/src/Elements/BodyComponents/MjHero.php
new file mode 100644
index 0000000..9281630
--- /dev/null
+++ b/src/Elements/BodyComponents/MjHero.php
@@ -0,0 +1,214 @@
+>
+ */
+ protected array $allowedAttributes = [
+ 'background-color' => [
+ 'unit' => 'color',
+ 'type' => 'color',
+ 'description' => 'hero background color',
+ 'default_value' => '#ffffff',
+ ],
+ 'background-height' => [
+ 'unit' => 'px',
+ 'type' => 'string',
+ 'description' => 'height of the image used',
+ 'default_value' => '',
+ ],
+ 'background-position' => [
+ 'unit' => 'string',
+ 'type' => 'string',
+ 'description' => 'background image position',
+ 'default_value' => 'center center',
+ ],
+ 'background-url' => [
+ 'unit' => 'string',
+ 'type' => 'string',
+ 'description' => 'background image url',
+ 'default_value' => '',
+ ],
+ 'background-width' => [
+ 'unit' => 'px',
+ 'type' => 'string',
+ 'description' => 'width of the image used',
+ 'default_value' => '',
+ ],
+ 'border-radius' => [
+ 'unit' => 'px',
+ 'type' => 'string',
+ 'description' => 'border radius',
+ 'default_value' => '',
+ ],
+ 'height' => [
+ 'unit' => 'px',
+ 'type' => 'string',
+ 'description' => 'hero section height',
+ 'default_value' => '',
+ ],
+ 'mode' => [
+ 'unit' => 'string',
+ 'type' => 'string',
+ 'description' => 'fluid-height or fixed-height',
+ 'default_value' => 'fluid-height',
+ ],
+ 'padding' => [
+ 'unit' => 'px',
+ 'type' => 'string',
+ 'description' => 'supports up to 4 parameters',
+ 'default_value' => '0px',
+ ],
+ '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' => '',
+ ],
+ 'vertical-align' => [
+ 'unit' => 'string',
+ 'type' => 'string',
+ 'description' => 'content vertical alignment (top/middle/bottom)',
+ 'default_value' => 'top',
+ ],
+ 'width' => [
+ 'unit' => 'px',
+ 'type' => 'string',
+ 'description' => 'hero container width',
+ 'default_value' => '',
+ ],
+ ];
+
+ protected array $defaultAttributes = [
+ 'background-color' => '#ffffff',
+ 'background-position' => 'center center',
+ 'mode' => 'fluid-height',
+ 'padding' => '0px',
+ 'vertical-align' => 'top',
+ ];
+
+ public function render(): string
+ {
+ $divAttributes = $this->getHtmlAttributes([
+ 'style' => 'div',
+ ]);
+
+ $tableAttributes = $this->getHtmlAttributes([
+ 'background' => $this->getAttribute('background-url'),
+ 'border' => '0',
+ 'cellpadding' => '0',
+ 'cellspacing' => '0',
+ 'role' => 'presentation',
+ 'style' => 'table',
+ ]);
+
+ $trAttributes = $this->getHtmlAttributes([
+ 'style' => 'tr',
+ ]);
+
+ $tdAttributes = $this->getHtmlAttributes([
+ 'style' => 'td',
+ ]);
+
+ $children = $this->getChildren() ?? [];
+ $content = $this->renderChildren($children, []);
+
+ return "
";
+ }
+
+ /**
+ * @return array>
+ */
+ public function getStyles(): array
+ {
+ $isFixedHeight = $this->getAttribute('mode') === 'fixed-height';
+ $height = $this->getAttribute('height');
+
+ return [
+ 'div' => [
+ 'margin' => '0 auto',
+ 'max-width' => $this->getAttribute('width'),
+ 'border-radius' => $this->getAttribute('border-radius'),
+ ],
+ 'table' => [
+ 'width' => '100%',
+ 'background-color' => $this->getAttribute('background-color'),
+ 'background-position' => $this->getAttribute('background-position'),
+ 'background-repeat' => 'no-repeat',
+ 'background-size' => 'cover',
+ 'border-radius' => $this->getAttribute('border-radius'),
+ ],
+ 'tr' => [
+ 'vertical-align' => $this->getAttribute('vertical-align'),
+ ],
+ 'td' => array_merge(
+ [
+ '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'),
+ ],
+ $isFixedHeight && $height ? ['height' => $height] : []
+ ),
+ ];
+ }
+}
diff --git a/tests/Unit/Elements/BodyComponents/MjHeroTest.php b/tests/Unit/Elements/BodyComponents/MjHeroTest.php
new file mode 100644
index 0000000..a2fe01a
--- /dev/null
+++ b/tests/Unit/Elements/BodyComponents/MjHeroTest.php
@@ -0,0 +1,210 @@
+element = new MjHero();
+});
+
+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-hero');
+});
+
+it('returns the correct default attributes', function () {
+ $attributes = [
+ 'background-color' => '#ffffff',
+ 'background-position' => 'center center',
+ 'mode' => 'fluid-height',
+ 'padding' => '0px',
+ 'vertical-align' => 'top',
+ ];
+
+ 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 hero', function () {
+ $heroNode = new MjmlNode(
+ 'mj-hero',
+ null,
+ null,
+ false,
+ null
+ );
+
+ $factory = new ElementFactory();
+ $mjHeroElement = $factory->create($heroNode);
+
+ expect($mjHeroElement)->toBeInstanceOf(MjHero::class);
+
+ $out = $mjHeroElement->render();
+
+ expect($out)->toContain('toContain('
toContain('role="presentation"');
+ expect($out)->not->toBeEmpty();
+});
+
+it('will correctly set background color', function () {
+ $heroNode = new MjmlNode(
+ 'mj-hero',
+ ['background-color' => '#ff0000'],
+ null,
+ false,
+ null
+ );
+
+ $factory = new ElementFactory();
+ $mjHeroElement = $factory->create($heroNode);
+
+ expect($mjHeroElement->getAttribute('background-color'))->toBe('#ff0000');
+
+ $styles = $mjHeroElement->getStyles();
+ expect($styles['table']['background-color'])->toBe('#ff0000');
+});
+
+it('will correctly set background URL', function () {
+ $heroNode = new MjmlNode(
+ 'mj-hero',
+ ['background-url' => 'https://example.com/hero.jpg'],
+ null,
+ false,
+ null
+ );
+
+ $factory = new ElementFactory();
+ $mjHeroElement = $factory->create($heroNode);
+
+ expect($mjHeroElement->getAttribute('background-url'))->toBe('https://example.com/hero.jpg');
+
+ $out = $mjHeroElement->render();
+ expect($out)->toContain('https://example.com/hero.jpg');
+});
+
+it('will correctly set height in fixed-height mode', function () {
+ $heroNode = new MjmlNode(
+ 'mj-hero',
+ [
+ 'mode' => 'fixed-height',
+ 'height' => '500px',
+ ],
+ null,
+ false,
+ null
+ );
+
+ $factory = new ElementFactory();
+ $mjHeroElement = $factory->create($heroNode);
+
+ expect($mjHeroElement->getAttribute('mode'))->toBe('fixed-height');
+ expect($mjHeroElement->getAttribute('height'))->toBe('500px');
+
+ $styles = $mjHeroElement->getStyles();
+ expect($styles['td']['height'])->toBe('500px');
+});
+
+it('will not set height in fluid-height mode', function () {
+ $heroNode = new MjmlNode(
+ 'mj-hero',
+ [
+ 'mode' => 'fluid-height',
+ 'height' => '500px',
+ ],
+ null,
+ false,
+ null
+ );
+
+ $factory = new ElementFactory();
+ $mjHeroElement = $factory->create($heroNode);
+
+ $styles = $mjHeroElement->getStyles();
+ expect($styles['td'])->not->toHaveKey('height');
+});
+
+it('will correctly set vertical alignment', function () {
+ $heroNode = new MjmlNode(
+ 'mj-hero',
+ ['vertical-align' => 'middle'],
+ null,
+ false,
+ null
+ );
+
+ $factory = new ElementFactory();
+ $mjHeroElement = $factory->create($heroNode);
+
+ $styles = $mjHeroElement->getStyles();
+ expect($styles['tr']['vertical-align'])->toBe('middle');
+});
+
+it('will correctly set padding', function () {
+ $heroNode = new MjmlNode(
+ 'mj-hero',
+ ['padding' => '20px 10px'],
+ null,
+ false,
+ null
+ );
+
+ $factory = new ElementFactory();
+ $mjHeroElement = $factory->create($heroNode);
+
+ $styles = $mjHeroElement->getStyles();
+ expect($styles['td']['padding'])->toBe('20px 10px');
+});
+
+it('will correctly set all custom properties', function () {
+ $heroNode = new MjmlNode(
+ 'mj-hero',
+ [
+ 'background-color' => '#333333',
+ 'background-url' => 'https://example.com/bg.jpg',
+ 'background-position' => 'top left',
+ 'mode' => 'fixed-height',
+ 'height' => '400px',
+ 'padding' => '30px',
+ 'vertical-align' => 'bottom',
+ 'width' => '600px',
+ ],
+ null,
+ false,
+ null
+ );
+
+ $factory = new ElementFactory();
+ $mjHeroElement = $factory->create($heroNode);
+
+ expect($mjHeroElement->getAttribute('background-color'))->toBe('#333333');
+ expect($mjHeroElement->getAttribute('background-url'))->toBe('https://example.com/bg.jpg');
+ expect($mjHeroElement->getAttribute('mode'))->toBe('fixed-height');
+
+ $styles = $mjHeroElement->getStyles();
+ expect($styles['table']['background-color'])->toBe('#333333');
+ expect($styles['table']['background-position'])->toBe('top left');
+ expect($styles['tr']['vertical-align'])->toBe('bottom');
+ expect($styles['td']['height'])->toBe('400px');
+ expect($styles['td']['padding'])->toBe('30px');
+ expect($styles['div']['max-width'])->toBe('600px');
+});