Your First Page - Step 4

Build your first Thorm page and learn the basic workflow: structure, state, and rendering mode.

/Components/Hero.php file defines a Hero component class (App\Components\Hero) with a single static get() method that returns a Thorm Node. It composes a hero section by calling Section::get(...) with two column blocks:

  • Left block: a pill label (“Rapid-response rabbit hole”), a headline (h1 ), a short descriptive paragraph (p), and three styled tags (“Boom!”, “Zap!”, “Contact!”).
  • Right block: a centered mascot image (img) with alt text and a circular style.

Styling is applied via Thorm helpers like cls() and attrs(), producing Tailwind-like utility class strings. The overall section uses a card-like grid layout with spacing and responsive column sizing.

PHP
<?php declare(strict_types=1); namespace App\Components; use Thorm\IR\Node\Node; use function Thorm\attrs; use function Thorm\cls; use function Thorm\div; use function Thorm\h1; use function Thorm\img; use function Thorm\p; use function Thorm\span; use function Thorm ext; final class Hero { public static function get(): Node { return Section::get([ div([], [ div([ cls('inline-flex items-center gap-2 rounded-full border-2 border-acme-ink bg-acme-cream px-4 py-2 text-sm font-semibold') ], [ span([cls('h-2.5 w-2.5 rounded-full bg-acme-orange'),]), text('Rapid-response rabbit hole'), ]), h1([cls('title-font mt-4 text-4xl md:text-5xl')], [ text('Need a stunt? We have a plan.'), ]), p([cls('mt-3 text-lg font-semibold text-acme-muted')], [ text('From product demos to moonshot ideas, drop the details and we will whip up something delightfully over-engineered.'), ]), div([cls('mt-5 flex flex-wrap gap-2')],[ span([cls('rounded-full border-2 border-acme-ink bg-acme-orange px-3 py-1 text-xs font-semibold uppercase text-white')], [text('Boom!')]), span([cls('rounded-full border-2 border-acme-ink bg-acme-blue px-3 py-1 text-xs font-semibold uppercase text-white')], [text('Zap!')]), span([cls('rounded-full border-2 border-acme-ink bg-acme-yellow px-3 py-1 text-xs font-semibold uppercase')], [text('Contact!')]), ]) ]), div([cls('burst grid place-items-center p-6')], [ div([cls('text-center')], [ img([ cls('h-256 w-256 rounded-full object-cover'), attrs([ 'src' => '/images/zany-mascot_256x256.webp', 'alt' => 'Contact Acme HQ: Looney vibes, serious replies', ]), ]), ]), ]), ], [ 'style' => 'card mb-8 grid gap-6 px-6 py-8 md:grid-cols-[1.3fr_1fr] md:px-10', ]); } }

We need to mention here the Section class, a small helper to encapsulate the <section> tag and make the code a bit more readable.

PHP
<?php declare(strict_types=1); namespace App\Components; use InvalidArgumentException; use Thorm\IR\Node\Node; use function Thorm\cls; use function Thorm\el; final class Section { public static function get(array $children, array $opts = []): Node { if(empty($children)) { throw new InvalidArgumentException('Section: $children cannot be empty.'); } $node = el('section', [ cls($opts['style']) ], $children ); return $node; } }
Status: Developer Preview
Things may change, things might break. If something feels awkward, it's probably a design edge we're still smoothing out.